ScrollBar ve Fields EditorOptions
This commit is contained in:
parent
e8295fd449
commit
f3a516eb92
6 changed files with 391 additions and 213 deletions
|
|
@ -4104,7 +4104,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
EditorOptions = EncodeHtmlJson
|
||||
EditorOptions = EncodeHtmlJson(false)
|
||||
},
|
||||
new() {
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public static class ListFormSeeder_DefaultJsons
|
|||
});
|
||||
|
||||
public static readonly string DefaultFilterJson = "\"IsDeleted\" = 'false'";
|
||||
public static readonly string EncodeHtmlJson = "{ \"encodeHtml\" = false }";
|
||||
public static string EncodeHtmlJson(bool value) => JsonSerializer.Serialize(new { encodeHtml = value });
|
||||
public static string DefaultFilterRowJson(bool visible = true) => JsonSerializer.Serialize(new GridFilterRowDto { Visible = visible });
|
||||
public static string DefaultHeaderFilterJson(bool visible = true) => JsonSerializer.Serialize(new { Visible = visible });
|
||||
public static string DefaultSearchPanelJson(bool visible = true) => JsonSerializer.Serialize(new { Visible = visible });
|
||||
|
|
|
|||
|
|
@ -3,6 +3,71 @@
|
|||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root,
|
||||
.light {
|
||||
--app-scrollbar-size: 8px;
|
||||
--app-scrollbar-thumb: #9ca3af;
|
||||
--app-scrollbar-thumb-hover: #6b7280;
|
||||
--app-scrollbar-track: transparent;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--app-scrollbar-thumb: #4b5563;
|
||||
--app-scrollbar-thumb-hover: #6b7280;
|
||||
}
|
||||
|
||||
* {
|
||||
scrollbar-color: var(--app-scrollbar-thumb) var(--app-scrollbar-track);
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: var(--app-scrollbar-size);
|
||||
height: var(--app-scrollbar-size);
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: var(--app-scrollbar-track);
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
min-height: 40px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 999px;
|
||||
background-color: var(--app-scrollbar-thumb);
|
||||
background-clip: content-box;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb:hover {
|
||||
background-color: var(--app-scrollbar-thumb-hover);
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.dx-scrollable-scrollbar.dx-scrollbar-vertical,
|
||||
.dx-scrollable-scrollbar.dx-scrollbar-vertical .dx-scrollable-scroll {
|
||||
width: var(--app-scrollbar-size) !important;
|
||||
}
|
||||
|
||||
.dx-scrollable-scrollbar.dx-scrollbar-horizontal,
|
||||
.dx-scrollable-scrollbar.dx-scrollbar-horizontal .dx-scrollable-scroll {
|
||||
height: var(--app-scrollbar-size) !important;
|
||||
}
|
||||
|
||||
.dx-scrollable-scroll {
|
||||
border-radius: 999px !important;
|
||||
background-color: var(--app-scrollbar-thumb) !important;
|
||||
}
|
||||
|
||||
.dx-scrollable-scroll:hover,
|
||||
.dx-scrollable-scrollbar.dx-state-hover .dx-scrollable-scroll,
|
||||
.dx-scrollable-scrollbar.dx-scrollbar-hoverable.dx-scrollable-scrollbar-active
|
||||
.dx-scrollable-scroll {
|
||||
background-color: var(--app-scrollbar-thumb-hover) !important;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply text-gray-500 dark:text-gray-400 text-xs bg-gray-100 dark:bg-gray-900 leading-normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,14 @@ export interface ScrollbarProps extends ReactCustomScrollbarProps {
|
|||
export type ScrollbarRef = Scrollbars
|
||||
|
||||
const ScrollBar = forwardRef<ScrollbarRef, ScrollbarProps>((props, ref) => {
|
||||
const { direction = 'ltr', ...rest } = props
|
||||
const {
|
||||
direction = 'ltr',
|
||||
renderThumbVertical,
|
||||
renderThumbHorizontal,
|
||||
renderTrackVertical,
|
||||
renderTrackHorizontal,
|
||||
...rest
|
||||
} = props
|
||||
|
||||
return (
|
||||
<Scrollbars
|
||||
|
|
@ -27,6 +34,65 @@ const ScrollBar = forwardRef<ScrollbarRef, ScrollbarProps>((props, ref) => {
|
|||
}}
|
||||
/>
|
||||
)}
|
||||
renderTrackVertical={
|
||||
renderTrackVertical ||
|
||||
((props) => (
|
||||
<div
|
||||
{...props}
|
||||
style={{
|
||||
...props.style,
|
||||
top: 0,
|
||||
right: direction === 'rtl' ? 'auto' : 0,
|
||||
left: direction === 'rtl' ? 0 : 'auto',
|
||||
bottom: 0,
|
||||
width: 'var(--app-scrollbar-size)',
|
||||
backgroundColor: 'var(--app-scrollbar-track)',
|
||||
}}
|
||||
/>
|
||||
))
|
||||
}
|
||||
renderTrackHorizontal={
|
||||
renderTrackHorizontal ||
|
||||
((props) => (
|
||||
<div
|
||||
{...props}
|
||||
style={{
|
||||
...props.style,
|
||||
right: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
height: 'var(--app-scrollbar-size)',
|
||||
backgroundColor: 'var(--app-scrollbar-track)',
|
||||
}}
|
||||
/>
|
||||
))
|
||||
}
|
||||
renderThumbVertical={
|
||||
renderThumbVertical ||
|
||||
((props) => (
|
||||
<div
|
||||
{...props}
|
||||
style={{
|
||||
...props.style,
|
||||
borderRadius: 999,
|
||||
backgroundColor: 'var(--app-scrollbar-thumb)',
|
||||
}}
|
||||
/>
|
||||
))
|
||||
}
|
||||
renderThumbHorizontal={
|
||||
renderThumbHorizontal ||
|
||||
((props) => (
|
||||
<div
|
||||
{...props}
|
||||
style={{
|
||||
...props.style,
|
||||
borderRadius: 999,
|
||||
backgroundColor: 'var(--app-scrollbar-thumb)',
|
||||
}}
|
||||
/>
|
||||
))
|
||||
}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { Container } from '@/components/shared'
|
||||
import { Button, Card, FormContainer, FormItem, Input, Select } from '@/components/ui'
|
||||
import { Button, Card, FormItem, Input, Select } from '@/components/ui'
|
||||
import { ColumnFormatEditDto, ListFormFieldEditTabs } from '@/proxy/admin/list-form-field/models'
|
||||
import { SelectBoxOption } from '@/types/shared'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Field, FieldProps, Form, Formik } from 'formik'
|
||||
import { useState } from 'react'
|
||||
import { FaSlidersH } from 'react-icons/fa'
|
||||
import { number, object, string } from 'yup'
|
||||
import { dbSourceTypeOptions, listFormAlignmentOptions } from '../options'
|
||||
import EditorOptionsBuilderDialog from '../json-row-operations/EditorOptionsBuilderDialog'
|
||||
import { FormFieldEditProps } from './FormFields'
|
||||
import { tooltipFormatListOptions } from '@/proxy/admin/list-form/options'
|
||||
|
||||
|
|
@ -15,6 +17,9 @@ const schema = object().shape({
|
|||
placeHolder: string(),
|
||||
bandName: string(),
|
||||
sourceDbType: number().required(),
|
||||
alignment: string(),
|
||||
format: string(),
|
||||
editorOptions: string().max(1000),
|
||||
})
|
||||
|
||||
function FormFieldTabDetails({
|
||||
|
|
@ -26,6 +31,7 @@ function FormFieldTabDetails({
|
|||
initialValues: ColumnFormatEditDto
|
||||
} & FormFieldEditProps) {
|
||||
const { translate } = useLocalization()
|
||||
const [isEditorOptionsDialogOpen, setIsEditorOptionsDialogOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<Formik
|
||||
|
|
@ -36,7 +42,7 @@ function FormFieldTabDetails({
|
|||
await onSubmit(ListFormFieldEditTabs.DetailsForm, values, formikHelpers)
|
||||
}}
|
||||
>
|
||||
{({ touched, errors, isSubmitting, values }) => (
|
||||
{({ touched, errors, isSubmitting, values, setFieldValue }) => (
|
||||
<Form>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<Card>
|
||||
|
|
@ -123,16 +129,33 @@ function FormFieldTabDetails({
|
|||
invalid={errors.editorOptions && touched.editorOptions}
|
||||
errorMessage={errors.editorOptions}
|
||||
>
|
||||
<Field
|
||||
type="text"
|
||||
name="editorOptions"
|
||||
component={Input}
|
||||
rows={4}
|
||||
textArea={true}
|
||||
/>
|
||||
<div className="flex gap-2 items-start">
|
||||
<Field
|
||||
type="text"
|
||||
name="editorOptions"
|
||||
component={Input}
|
||||
rows={4}
|
||||
textArea={true}
|
||||
/>
|
||||
<Button
|
||||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="sm"
|
||||
title="Build editor options"
|
||||
icon={<FaSlidersH />}
|
||||
onClick={() => setIsEditorOptionsDialogOpen(true)}
|
||||
/>
|
||||
</div>
|
||||
</FormItem>
|
||||
</Card>
|
||||
</div>
|
||||
<EditorOptionsBuilderDialog
|
||||
isOpen={isEditorOptionsDialogOpen}
|
||||
value={values.editorOptions}
|
||||
onClose={() => setIsEditorOptionsDialogOpen(false)}
|
||||
onApply={(val) => setFieldValue('editorOptions', val)}
|
||||
/>
|
||||
<Button size="sm" className="mt-1" block variant="solid" loading={isSubmitting} type="submit">
|
||||
{isSubmitting ? translate('::Saving') : translate('::Save')}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ type EditorOptionsBuilderDialogProps = {
|
|||
}
|
||||
|
||||
const baseInputClass =
|
||||
'w-full h-9 px-2 rounded border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800 text-sm text-gray-700 dark:text-gray-100 focus:outline-none focus:border-indigo-400'
|
||||
'w-full min-w-0 h-9 px-2 rounded border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800 text-sm text-gray-700 dark:text-gray-100 focus:outline-none focus:border-indigo-400'
|
||||
|
||||
const boolOptions = [
|
||||
{ key: 'showClearButton', label: 'showClearButton' },
|
||||
|
|
@ -145,6 +145,18 @@ function toSelectValue(value: unknown): string {
|
|||
return typeof value === 'string' ? value : ''
|
||||
}
|
||||
|
||||
function toEncodeHtmlSelectValue(value: unknown): string {
|
||||
if (value === true) return 'true'
|
||||
if (value === false) return 'false'
|
||||
return value === 'auto' ? 'auto' : ''
|
||||
}
|
||||
|
||||
function readEncodeHtmlSelectValue(value: string) {
|
||||
if (value === 'true') return true
|
||||
if (value === 'false') return false
|
||||
return undefined
|
||||
}
|
||||
|
||||
function readCustomValue(option: CustomOption) {
|
||||
if (option.type === 'boolean') return option.value === 'true'
|
||||
if (option.type === 'number') return Number(option.value || 0)
|
||||
|
|
@ -278,23 +290,182 @@ function EditorOptionsBuilderDialog({
|
|||
return isEmptyObject(next) ? '' : JSON.stringify(next, null, 2)
|
||||
}, [customOptions, options])
|
||||
|
||||
const presetButtons = (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={clearOptions}
|
||||
title="editorOptions alanını boşaltır. Backend'e boş string kaydedilir."
|
||||
>
|
||||
NULL / Empty
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset({ disabled: true })}
|
||||
title='{"disabled": true} ekler.'
|
||||
>
|
||||
disabled
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset({ tooltip: { enabled: true } })}
|
||||
title='{"tooltip": {"enabled": true}} ekler.'
|
||||
>
|
||||
Tooltip
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset({ format: { type: 'fixedPoint', precision: 2 } })}
|
||||
title="Ondalık format precision 2 ekler."
|
||||
>
|
||||
fixedPoint precision 2
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
addPreset({
|
||||
disabled: true,
|
||||
format: { type: 'fixedPoint', precision: 2 },
|
||||
})
|
||||
}
|
||||
title="Pasif ve precision 2 fixedPoint formatını birlikte ekler."
|
||||
>
|
||||
disabled fixedPoint 2
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
addPreset({
|
||||
format: { type: 'fixedPoint', precision: 1 },
|
||||
useMaskBehavior: true,
|
||||
showSpinButtons: true,
|
||||
disabled: true,
|
||||
})
|
||||
}
|
||||
title="Precision 1, mask behavior, spin buttons ve pasif ayarını ekler."
|
||||
>
|
||||
disabled fixedPoint 1
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
addPreset({
|
||||
format: { type: 'fixedPoint', precision: 2 },
|
||||
useMaskBehavior: true,
|
||||
showSpinButtons: true,
|
||||
})
|
||||
}
|
||||
title="NumberBox için precision 2, mask behavior ve spin buttons ekler."
|
||||
>
|
||||
numberSpin precision 2
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset({ format: 'dd/MM/yyyy', displayFormat: 'dd/MM/yyyy' })}
|
||||
title="DateBox için gün/ay/yıl formatını ekler."
|
||||
>
|
||||
date dd/MM/yyyy
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
addPreset({
|
||||
format: 'dd/MM/yyyy HH:mm',
|
||||
displayFormat: 'dd/MM/yyyy HH:mm',
|
||||
})
|
||||
}
|
||||
title="DateBox için gün/ay/yıl saat:dakika formatını ekler."
|
||||
>
|
||||
dateTime dd/MM/yyyy HH:mm
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
addPreset({
|
||||
format: 'phoneGlobal',
|
||||
mask: '(000) 000-0000',
|
||||
maskInvalidMessage: 'Lütfen geçerli bir telefon numarası girin',
|
||||
useMaskedValue: false,
|
||||
maskRules: { X: '[0-9]' },
|
||||
placeholder: '(555) 123-4567',
|
||||
})
|
||||
}
|
||||
title="Telefon maskesi, yer tutucu ve Türkçe hata mesajı ekler."
|
||||
>
|
||||
phoneGlobal mask
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
addPreset({
|
||||
type: 'time',
|
||||
pickerType: 'list',
|
||||
displayFormat: 'HH:mm',
|
||||
dateSerializationFormat: 'yyyy-MM-ddTHH:mm:ss',
|
||||
interval: 5,
|
||||
width: '100%',
|
||||
})
|
||||
}
|
||||
title="Saat seçimi için dxDateBox time picker ayarları ekler."
|
||||
>
|
||||
time picker
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset({ width: 80, height: 80, multiple: true })}
|
||||
title="ImageUpload için 80x80 ve multiple ayarlarını ekler."
|
||||
>
|
||||
multi image
|
||||
</Button>
|
||||
<Button type="button" size="sm" onClick={() => addPreset({ height: 60 })} title='{"height": 60} ekler.'>
|
||||
Height 60
|
||||
</Button>
|
||||
<Button type="button" size="sm" onClick={() => addPreset({ height: 100 })} title='{"height": 100} ekler.'>
|
||||
Height 100
|
||||
</Button>
|
||||
<Button type="button" size="sm" onClick={() => addPreset({ height: 200 })} title='{"height": 200} ekler.'>
|
||||
Height 200
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset(buildHtmlEditorOptions())}
|
||||
title="HtmlEditor toolbar, image upload ve media resizing ayarlarını ekler."
|
||||
>
|
||||
htmlEditor toolbar
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
isOpen={isOpen}
|
||||
width={980}
|
||||
height="82vh"
|
||||
width={1320}
|
||||
height="88vh"
|
||||
contentClassName="flex flex-col"
|
||||
style={{ overlay: { zIndex: 1300 } }}
|
||||
preventScroll
|
||||
onClose={onClose}
|
||||
onRequestClose={onClose}
|
||||
>
|
||||
<Dialog.Body className="flex min-h-0 flex-1 flex-col gap-4 overflow-hidden">
|
||||
<Dialog.Body className="flex min-h-0 flex-1 flex-col gap-3 overflow-hidden">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<h5
|
||||
title={`${
|
||||
editorType || 'DevExpress editor'
|
||||
} için DevExtreme editorOptions JSON'u oluşturur. Soldan hazır ayarları seç, sağdaki JSON önizleme alanından sonucu kontrol et.`}
|
||||
} için DevExtreme editorOptions JSON'u oluşturur. Üstten hazır ayarları seç, sağdaki JSON önizleme alanından sonucu kontrol et.`}
|
||||
>
|
||||
Editor Options Builder
|
||||
</h5>
|
||||
|
|
@ -306,8 +477,18 @@ function EditorOptionsBuilderDialog({
|
|||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-2 min-h-0 gap-4 overflow-y-auto pr-1 ">
|
||||
<section className="flex flex-col gap-4">
|
||||
<section className="rounded border border-gray-200 dark:border-gray-700 bg-gray-50/70 dark:bg-gray-900/30 p-3">
|
||||
<div
|
||||
className="mb-3 text-sm font-semibold"
|
||||
title="Daha önce kullandığın hazır editorOptions örnekleri. Tıkladığın preset mevcut JSON ile birleştirilir, yani başka seçenekleri silmeden üzerine ekler."
|
||||
>
|
||||
Hazır Ayarlar
|
||||
</div>
|
||||
{presetButtons}
|
||||
</section>
|
||||
|
||||
<div className="grid grid-cols-12 min-h-0 gap-4 overflow-hidden">
|
||||
<section className="col-span-7 flex min-h-0 flex-col gap-3 overflow-y-auto pr-1">
|
||||
<div className="rounded border border-gray-200 dark:border-gray-700 p-3">
|
||||
<div
|
||||
className="mb-3 text-sm font-semibold"
|
||||
|
|
@ -315,21 +496,39 @@ function EditorOptionsBuilderDialog({
|
|||
>
|
||||
1. Hızlı Anahtarlar
|
||||
</div>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-2">
|
||||
<div className="grid grid-cols-2 gap-x-3 gap-y-2">
|
||||
{boolOptions.map((option) => (
|
||||
<label
|
||||
key={option.key}
|
||||
className="flex items-center gap-2 rounded px-1 py-1 text-sm"
|
||||
className="flex min-w-0 items-center gap-2 rounded px-1 py-1 text-xs"
|
||||
title={`${option.key}: true/false olarak editorOptions içine yazılır.`}
|
||||
>
|
||||
<input
|
||||
className="shrink-0"
|
||||
type="checkbox"
|
||||
checked={options[option.key] === true}
|
||||
onChange={(event) => toggleOption(option.key, event.target.checked)}
|
||||
/>
|
||||
<span>{option.label}</span>
|
||||
<span className="min-w-0 truncate">{option.label}</span>
|
||||
</label>
|
||||
))}
|
||||
<label
|
||||
className="col-span-2 grid min-w-0 grid-cols-[110px_1fr] items-center gap-2 rounded px-1 py-1 text-xs"
|
||||
title="encodeHtml değerini true, false veya auto olarak editorOptions içine yazar."
|
||||
>
|
||||
<span className="min-w-0 truncate">encodeHtml</span>
|
||||
<select
|
||||
className={baseInputClass}
|
||||
value={toEncodeHtmlSelectValue(options.encodeHtml)}
|
||||
onChange={(event) =>
|
||||
setOptionValue('encodeHtml', readEncodeHtmlSelectValue(event.target.value))
|
||||
}
|
||||
>
|
||||
<option value="">Seç</option>
|
||||
<option value="true">true</option>
|
||||
<option value="false">false</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -340,7 +539,7 @@ function EditorOptionsBuilderDialog({
|
|||
>
|
||||
2. Boyut ve Görünüm
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 [&>label]:min-w-0">
|
||||
<label className="text-xs text-gray-500">
|
||||
{translate('::ListForms.ListFormEdit.DetailsWidth')}
|
||||
<input
|
||||
|
|
@ -387,7 +586,7 @@ function EditorOptionsBuilderDialog({
|
|||
>
|
||||
3. Genel Editor Davranışı
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 [&>label]:min-w-0">
|
||||
<label className="text-xs text-gray-500">
|
||||
label
|
||||
<input
|
||||
|
|
@ -469,7 +668,7 @@ function EditorOptionsBuilderDialog({
|
|||
>
|
||||
4. TextBox / Mask
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 [&>label]:min-w-0">
|
||||
<label className="text-xs text-gray-500">
|
||||
mode
|
||||
<select
|
||||
|
|
@ -557,7 +756,7 @@ function EditorOptionsBuilderDialog({
|
|||
>
|
||||
5. NumberBox
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-5 gap-3">
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-3 [&>label]:min-w-0">
|
||||
{['min', 'max', 'step'].map((key) => (
|
||||
<label key={key} className="text-xs text-gray-500">
|
||||
{key}
|
||||
|
|
@ -617,7 +816,7 @@ function EditorOptionsBuilderDialog({
|
|||
>
|
||||
6. DateBox
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 [&>label]:min-w-0">
|
||||
<label className="text-xs text-gray-500">
|
||||
type
|
||||
<select
|
||||
|
|
@ -791,195 +990,20 @@ function EditorOptionsBuilderDialog({
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<section className="rounded border border-gray-200 dark:border-gray-700 p-3 flex flex-col min-h-[420px]">
|
||||
<div className="rounded border border-gray-200 dark:border-gray-700 p-3">
|
||||
<div
|
||||
className="mb-3 text-sm font-semibold"
|
||||
title="Daha önce kullandığın hazır editorOptions örnekleri. Tıkladığın preset mevcut JSON ile birleştirilir, yani başka seçenekleri silmeden üzerine ekler."
|
||||
>
|
||||
Hazır Ayarlar
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={clearOptions}
|
||||
title="editorOptions alanını boşaltır. Backend'e boş string kaydedilir."
|
||||
>
|
||||
NULL / Empty
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset({ disabled: true })}
|
||||
title='{"disabled": true} ekler.'
|
||||
>
|
||||
disabled
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset({ tooltip: { enabled: true } })}
|
||||
title='{"tooltip": {"enabled": true}} ekler.'
|
||||
>
|
||||
Tooltip
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset({ format: { type: 'fixedPoint', precision: 2 } })}
|
||||
title="Ondalık format precision 2 ekler."
|
||||
>
|
||||
fixedPoint precision 2
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
addPreset({
|
||||
disabled: true,
|
||||
format: { type: 'fixedPoint', precision: 2 },
|
||||
})
|
||||
}
|
||||
title="Pasif ve precision 2 fixedPoint formatını birlikte ekler."
|
||||
>
|
||||
disabled fixedPoint 2
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
addPreset({
|
||||
format: { type: 'fixedPoint', precision: 1 },
|
||||
useMaskBehavior: true,
|
||||
showSpinButtons: true,
|
||||
disabled: true,
|
||||
})
|
||||
}
|
||||
title="Precision 1, mask behavior, spin buttons ve pasif ayarını ekler."
|
||||
>
|
||||
disabled fixedPoint 1
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
addPreset({
|
||||
format: { type: 'fixedPoint', precision: 2 },
|
||||
useMaskBehavior: true,
|
||||
showSpinButtons: true,
|
||||
})
|
||||
}
|
||||
title="NumberBox için precision 2, mask behavior ve spin buttons ekler."
|
||||
>
|
||||
numberSpin precision 2
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset({ format: 'dd/MM/yyyy', displayFormat: 'dd/MM/yyyy' })}
|
||||
title="DateBox için gün/ay/yıl formatını ekler."
|
||||
>
|
||||
date dd/MM/yyyy
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
addPreset({
|
||||
format: 'dd/MM/yyyy HH:mm',
|
||||
displayFormat: 'dd/MM/yyyy HH:mm',
|
||||
})
|
||||
}
|
||||
title="DateBox için gün/ay/yıl saat:dakika formatını ekler."
|
||||
>
|
||||
dateTime dd/MM/yyyy HH:mm
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
addPreset({
|
||||
format: 'phoneGlobal',
|
||||
mask: '(000) 000-0000',
|
||||
maskInvalidMessage: 'Lütfen geçerli bir telefon numarası girin',
|
||||
useMaskedValue: false,
|
||||
maskRules: { X: '[0-9]' },
|
||||
placeholder: '(555) 123-4567',
|
||||
})
|
||||
}
|
||||
title="Telefon maskesi, yer tutucu ve Türkçe hata mesajı ekler."
|
||||
>
|
||||
phoneGlobal mask
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
addPreset({
|
||||
type: 'time',
|
||||
pickerType: 'list',
|
||||
displayFormat: 'HH:mm',
|
||||
dateSerializationFormat: 'yyyy-MM-ddTHH:mm:ss',
|
||||
interval: 5,
|
||||
width: '100%',
|
||||
})
|
||||
}
|
||||
title="Saat seçimi için dxDateBox time picker ayarları ekler."
|
||||
>
|
||||
time picker
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset({ width: 80, height: 80, multiple: true })}
|
||||
title="ImageUpload için 80x80 ve multiple ayarlarını ekler."
|
||||
>
|
||||
multi image
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset({ height: 60 })}
|
||||
title='{"height": 60} ekler.'
|
||||
>
|
||||
Height 60
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset({ height: 100 })}
|
||||
title='{"height": 100} ekler.'
|
||||
>
|
||||
Height 100
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset({ height: 200 })}
|
||||
title='{"height": 200} ekler.'
|
||||
>
|
||||
Height 200
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() => addPreset(buildHtmlEditorOptions())}
|
||||
title="HtmlEditor toolbar, image upload ve media resizing ayarlarını ekler."
|
||||
>
|
||||
htmlEditor toolbar
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section className="col-span-5 flex min-h-0 flex-col rounded border border-gray-200 bg-white p-3 shadow-sm dark:border-gray-700 dark:bg-gray-900">
|
||||
<div
|
||||
className="flex items-center gap-2 text-sm font-semibold mt-3"
|
||||
className="mb-3 flex items-center justify-between gap-2 text-sm font-semibold"
|
||||
title="Kaydet/Uygula sonrası editorOptions alanına yazılacak net JSON budur."
|
||||
>
|
||||
<FaCode />
|
||||
JSON Önizleme
|
||||
<span className="flex items-center gap-2">
|
||||
<FaCode />
|
||||
JSON Önizleme
|
||||
</span>
|
||||
<span className="rounded bg-gray-100 px-2 py-1 text-[11px] font-normal text-gray-500 dark:bg-gray-800 dark:text-gray-300">
|
||||
{preview ? `${preview.length} karakter` : 'boş'}
|
||||
</span>
|
||||
</div>
|
||||
<pre className="flex overflow-auto rounded bg-gray-50 dark:bg-gray-900 p-3 text-xs whitespace-pre-wrap">
|
||||
<pre className="min-h-0 flex-1 overflow-auto rounded border border-gray-200 bg-gray-950 p-4 font-mono text-xs leading-5 text-emerald-100 shadow-inner dark:border-gray-700 whitespace-pre-wrap">
|
||||
{preview || '{}'}
|
||||
</pre>
|
||||
</section>
|
||||
|
|
|
|||
Loading…
Reference in a new issue