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,
|
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||||
EditorOptions = EncodeHtmlJson
|
EditorOptions = EncodeHtmlJson(false)
|
||||||
},
|
},
|
||||||
new() {
|
new() {
|
||||||
ListFormCode = listForm.ListFormCode,
|
ListFormCode = listForm.ListFormCode,
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ public static class ListFormSeeder_DefaultJsons
|
||||||
});
|
});
|
||||||
|
|
||||||
public static readonly string DefaultFilterJson = "\"IsDeleted\" = 'false'";
|
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 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 DefaultHeaderFilterJson(bool visible = true) => JsonSerializer.Serialize(new { Visible = visible });
|
||||||
public static string DefaultSearchPanelJson(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;
|
@tailwind utilities;
|
||||||
|
|
||||||
@layer base {
|
@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 {
|
body {
|
||||||
@apply text-gray-500 dark:text-gray-400 text-xs bg-gray-100 dark:bg-gray-900 leading-normal;
|
@apply text-gray-500 dark:text-gray-400 text-xs bg-gray-100 dark:bg-gray-900 leading-normal;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,14 @@ export interface ScrollbarProps extends ReactCustomScrollbarProps {
|
||||||
export type ScrollbarRef = Scrollbars
|
export type ScrollbarRef = Scrollbars
|
||||||
|
|
||||||
const ScrollBar = forwardRef<ScrollbarRef, ScrollbarProps>((props, ref) => {
|
const ScrollBar = forwardRef<ScrollbarRef, ScrollbarProps>((props, ref) => {
|
||||||
const { direction = 'ltr', ...rest } = props
|
const {
|
||||||
|
direction = 'ltr',
|
||||||
|
renderThumbVertical,
|
||||||
|
renderThumbHorizontal,
|
||||||
|
renderTrackVertical,
|
||||||
|
renderTrackHorizontal,
|
||||||
|
...rest
|
||||||
|
} = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Scrollbars
|
<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}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { Container } from '@/components/shared'
|
import { Button, Card, FormItem, Input, Select } from '@/components/ui'
|
||||||
import { Button, Card, FormContainer, FormItem, Input, Select } from '@/components/ui'
|
|
||||||
import { ColumnFormatEditDto, ListFormFieldEditTabs } from '@/proxy/admin/list-form-field/models'
|
import { ColumnFormatEditDto, ListFormFieldEditTabs } from '@/proxy/admin/list-form-field/models'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { Field, FieldProps, Form, Formik } from 'formik'
|
import { Field, FieldProps, Form, Formik } from 'formik'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { FaSlidersH } from 'react-icons/fa'
|
||||||
import { number, object, string } from 'yup'
|
import { number, object, string } from 'yup'
|
||||||
import { dbSourceTypeOptions, listFormAlignmentOptions } from '../options'
|
import { dbSourceTypeOptions, listFormAlignmentOptions } from '../options'
|
||||||
|
import EditorOptionsBuilderDialog from '../json-row-operations/EditorOptionsBuilderDialog'
|
||||||
import { FormFieldEditProps } from './FormFields'
|
import { FormFieldEditProps } from './FormFields'
|
||||||
import { tooltipFormatListOptions } from '@/proxy/admin/list-form/options'
|
import { tooltipFormatListOptions } from '@/proxy/admin/list-form/options'
|
||||||
|
|
||||||
|
|
@ -15,6 +17,9 @@ const schema = object().shape({
|
||||||
placeHolder: string(),
|
placeHolder: string(),
|
||||||
bandName: string(),
|
bandName: string(),
|
||||||
sourceDbType: number().required(),
|
sourceDbType: number().required(),
|
||||||
|
alignment: string(),
|
||||||
|
format: string(),
|
||||||
|
editorOptions: string().max(1000),
|
||||||
})
|
})
|
||||||
|
|
||||||
function FormFieldTabDetails({
|
function FormFieldTabDetails({
|
||||||
|
|
@ -26,6 +31,7 @@ function FormFieldTabDetails({
|
||||||
initialValues: ColumnFormatEditDto
|
initialValues: ColumnFormatEditDto
|
||||||
} & FormFieldEditProps) {
|
} & FormFieldEditProps) {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
const [isEditorOptionsDialogOpen, setIsEditorOptionsDialogOpen] = useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Formik
|
<Formik
|
||||||
|
|
@ -36,7 +42,7 @@ function FormFieldTabDetails({
|
||||||
await onSubmit(ListFormFieldEditTabs.DetailsForm, values, formikHelpers)
|
await onSubmit(ListFormFieldEditTabs.DetailsForm, values, formikHelpers)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{({ touched, errors, isSubmitting, values }) => (
|
{({ touched, errors, isSubmitting, values, setFieldValue }) => (
|
||||||
<Form>
|
<Form>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
<Card>
|
<Card>
|
||||||
|
|
@ -123,6 +129,7 @@ function FormFieldTabDetails({
|
||||||
invalid={errors.editorOptions && touched.editorOptions}
|
invalid={errors.editorOptions && touched.editorOptions}
|
||||||
errorMessage={errors.editorOptions}
|
errorMessage={errors.editorOptions}
|
||||||
>
|
>
|
||||||
|
<div className="flex gap-2 items-start">
|
||||||
<Field
|
<Field
|
||||||
type="text"
|
type="text"
|
||||||
name="editorOptions"
|
name="editorOptions"
|
||||||
|
|
@ -130,9 +137,25 @@ function FormFieldTabDetails({
|
||||||
rows={4}
|
rows={4}
|
||||||
textArea={true}
|
textArea={true}
|
||||||
/>
|
/>
|
||||||
|
<Button
|
||||||
|
shape="circle"
|
||||||
|
variant="plain"
|
||||||
|
type="button"
|
||||||
|
size="sm"
|
||||||
|
title="Build editor options"
|
||||||
|
icon={<FaSlidersH />}
|
||||||
|
onClick={() => setIsEditorOptionsDialogOpen(true)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</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">
|
<Button size="sm" className="mt-1" block variant="solid" loading={isSubmitting} type="submit">
|
||||||
{isSubmitting ? translate('::Saving') : translate('::Save')}
|
{isSubmitting ? translate('::Saving') : translate('::Save')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ type EditorOptionsBuilderDialogProps = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseInputClass =
|
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 = [
|
const boolOptions = [
|
||||||
{ key: 'showClearButton', label: 'showClearButton' },
|
{ key: 'showClearButton', label: 'showClearButton' },
|
||||||
|
|
@ -145,6 +145,18 @@ function toSelectValue(value: unknown): string {
|
||||||
return typeof value === 'string' ? value : ''
|
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) {
|
function readCustomValue(option: CustomOption) {
|
||||||
if (option.type === 'boolean') return option.value === 'true'
|
if (option.type === 'boolean') return option.value === 'true'
|
||||||
if (option.type === 'number') return Number(option.value || 0)
|
if (option.type === 'number') return Number(option.value || 0)
|
||||||
|
|
@ -278,23 +290,182 @@ function EditorOptionsBuilderDialog({
|
||||||
return isEmptyObject(next) ? '' : JSON.stringify(next, null, 2)
|
return isEmptyObject(next) ? '' : JSON.stringify(next, null, 2)
|
||||||
}, [customOptions, options])
|
}, [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 (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
width={980}
|
width={1320}
|
||||||
height="82vh"
|
height="88vh"
|
||||||
contentClassName="flex flex-col"
|
contentClassName="flex flex-col"
|
||||||
style={{ overlay: { zIndex: 1300 } }}
|
style={{ overlay: { zIndex: 1300 } }}
|
||||||
preventScroll
|
preventScroll
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
onRequestClose={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">
|
<div className="flex items-center justify-between gap-3">
|
||||||
<h5
|
<h5
|
||||||
title={`${
|
title={`${
|
||||||
editorType || 'DevExpress editor'
|
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
|
Editor Options Builder
|
||||||
</h5>
|
</h5>
|
||||||
|
|
@ -306,8 +477,18 @@ function EditorOptionsBuilderDialog({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="grid grid-cols-2 min-h-0 gap-4 overflow-y-auto pr-1 ">
|
<section className="rounded border border-gray-200 dark:border-gray-700 bg-gray-50/70 dark:bg-gray-900/30 p-3">
|
||||||
<section className="flex flex-col gap-4">
|
<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="rounded border border-gray-200 dark:border-gray-700 p-3">
|
||||||
<div
|
<div
|
||||||
className="mb-3 text-sm font-semibold"
|
className="mb-3 text-sm font-semibold"
|
||||||
|
|
@ -315,21 +496,39 @@ function EditorOptionsBuilderDialog({
|
||||||
>
|
>
|
||||||
1. Hızlı Anahtarlar
|
1. Hızlı Anahtarlar
|
||||||
</div>
|
</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) => (
|
{boolOptions.map((option) => (
|
||||||
<label
|
<label
|
||||||
key={option.key}
|
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.`}
|
title={`${option.key}: true/false olarak editorOptions içine yazılır.`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
className="shrink-0"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={options[option.key] === true}
|
checked={options[option.key] === true}
|
||||||
onChange={(event) => toggleOption(option.key, event.target.checked)}
|
onChange={(event) => toggleOption(option.key, event.target.checked)}
|
||||||
/>
|
/>
|
||||||
<span>{option.label}</span>
|
<span className="min-w-0 truncate">{option.label}</span>
|
||||||
</label>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -340,7 +539,7 @@ function EditorOptionsBuilderDialog({
|
||||||
>
|
>
|
||||||
2. Boyut ve Görünüm
|
2. Boyut ve Görünüm
|
||||||
</div>
|
</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 className="text-xs text-gray-500">
|
||||||
{translate('::ListForms.ListFormEdit.DetailsWidth')}
|
{translate('::ListForms.ListFormEdit.DetailsWidth')}
|
||||||
<input
|
<input
|
||||||
|
|
@ -387,7 +586,7 @@ function EditorOptionsBuilderDialog({
|
||||||
>
|
>
|
||||||
3. Genel Editor Davranışı
|
3. Genel Editor Davranışı
|
||||||
</div>
|
</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 className="text-xs text-gray-500">
|
||||||
label
|
label
|
||||||
<input
|
<input
|
||||||
|
|
@ -469,7 +668,7 @@ function EditorOptionsBuilderDialog({
|
||||||
>
|
>
|
||||||
4. TextBox / Mask
|
4. TextBox / Mask
|
||||||
</div>
|
</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 className="text-xs text-gray-500">
|
||||||
mode
|
mode
|
||||||
<select
|
<select
|
||||||
|
|
@ -557,7 +756,7 @@ function EditorOptionsBuilderDialog({
|
||||||
>
|
>
|
||||||
5. NumberBox
|
5. NumberBox
|
||||||
</div>
|
</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) => (
|
{['min', 'max', 'step'].map((key) => (
|
||||||
<label key={key} className="text-xs text-gray-500">
|
<label key={key} className="text-xs text-gray-500">
|
||||||
{key}
|
{key}
|
||||||
|
|
@ -617,7 +816,7 @@ function EditorOptionsBuilderDialog({
|
||||||
>
|
>
|
||||||
6. DateBox
|
6. DateBox
|
||||||
</div>
|
</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 className="text-xs text-gray-500">
|
||||||
type
|
type
|
||||||
<select
|
<select
|
||||||
|
|
@ -791,195 +990,20 @@ function EditorOptionsBuilderDialog({
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="rounded border border-gray-200 dark:border-gray-700 p-3 flex flex-col min-h-[420px]">
|
<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="rounded border border-gray-200 dark:border-gray-700 p-3">
|
|
||||||
<div
|
<div
|
||||||
className="mb-3 text-sm font-semibold"
|
className="mb-3 flex items-center justify-between gap-2 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>
|
|
||||||
|
|
||||||
<div
|
|
||||||
className="flex items-center gap-2 text-sm font-semibold mt-3"
|
|
||||||
title="Kaydet/Uygula sonrası editorOptions alanına yazılacak net JSON budur."
|
title="Kaydet/Uygula sonrası editorOptions alanına yazılacak net JSON budur."
|
||||||
>
|
>
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
<FaCode />
|
<FaCode />
|
||||||
JSON Önizleme
|
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>
|
</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 || '{}'}
|
{preview || '{}'}
|
||||||
</pre>
|
</pre>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue