Html Toolbar yetenekli hale getirdik
This commit is contained in:
parent
c102114a3a
commit
9ceacf5d64
17 changed files with 185 additions and 249 deletions
4
.github/instructions/lowcode.instructions.md
vendored
4
.github/instructions/lowcode.instructions.md
vendored
|
|
@ -921,7 +921,7 @@ anahtarları listeler; ortak anahtarlar (§4.3.3) her editörde geçerlidir.
|
||||||
| `dxColorBox` | Renk | String (16) | `editAlphaChannel`, `keyStep` |
|
| `dxColorBox` | Renk | String (16) | `editAlphaChannel`, `keyStep` |
|
||||||
| `dxSlider` | Tek değerli kaydırıcı | Sayı | `min`, `max`, `tooltip.showMode` |
|
| `dxSlider` | Tek değerli kaydırıcı | Sayı | `min`, `max`, `tooltip.showMode` |
|
||||||
| `dxRangeSlider` | Aralık kaydırıcı | Sayı | `min`, `max`, `showRange` |
|
| `dxRangeSlider` | Aralık kaydırıcı | Sayı | `min`, `max`, `showRange` |
|
||||||
| `dxHtmlEditor` | Zengin metin | String (16) | `toolbar.items`, `toolbar.multiline`, `valueType` (`html`/`markdown`), `mediaResizing.enabled`, `imageUpload.uploadUrl`, `imageUpload.fileUploadMode` |
|
| `dxHtmlEditor` | Zengin metin | String (16) | `toolbar.items`, `toolbar.multiline`, `valueType` (`html`/`markdown`), `mediaResizing.enabled`, `imageUpload.tabs`, `imageUpload.uploadUrl`, `imageUpload.fileUploadMode`, `tableResizing.enabled`, `tableContextMenu.enabled`. Boş `editorOptions` da tam araç çubuğu, dosyadan/URL'den resim, resim boyutlandırma ve tablo araçlarıyla gelir (`ui/src/views/shared/htmlEditorOptions.ts`); yazılan değer varsayılanın üstüne tek seviye birleştirilir, yalnızca farkı yaz. |
|
||||||
| `dxImageUpload` | Görsel yükleme (platform) | String (16) | `uploadUrl`\*, `accept`\*, `capture`\*, `multiple`\*, `maxFileSize`\*, `width`\*, `height`\* |
|
| `dxImageUpload` | Görsel yükleme (platform) | String (16) | `uploadUrl`\*, `accept`\*, `capture`\*, `multiple`\*, `maxFileSize`\*, `width`\*, `height`\* |
|
||||||
| `dxImageViewer` | Görsel gösterimi (platform) | String (16) | `width`, `height` |
|
| `dxImageViewer` | Görsel gösterimi (platform) | String (16) | `width`, `height` |
|
||||||
|
|
||||||
|
|
@ -2086,7 +2086,7 @@ Dizi elemanları yaprak sayılmaz; `toolbar.items` tek bir `json` alanıdır.
|
||||||
| Görsel | `uploadUrl`\*, `fileFieldName`, `accept`\*, `capture`\*, `multiple`\*, `maxFileSize`\* | `dxImageUpload`, `dxImageViewer` |
|
| Görsel | `uploadUrl`\*, `fileFieldName`, `accept`\*, `capture`\*, `multiple`\*, `maxFileSize`\* | `dxImageUpload`, `dxImageViewer` |
|
||||||
| Seçim | `text` (`dxCheckBox`), `switchedOnText`/`switchedOffText` (`dxSwitch`), `layout` (`dxRadioGroup`), `editAlphaChannel`/`keyStep` (`dxColorBox`) | ilgili editör |
|
| Seçim | `text` (`dxCheckBox`), `switchedOnText`/`switchedOffText` (`dxSwitch`), `layout` (`dxRadioGroup`), `editAlphaChannel`/`keyStep` (`dxColorBox`) | ilgili editör |
|
||||||
| Kaydırıcı | `min`, `max`, `step`, `tooltip.enabled`, `tooltip.showMode`, `showRange` | `dxSlider`, `dxRangeSlider` |
|
| Kaydırıcı | `min`, `max`, `step`, `tooltip.enabled`, `tooltip.showMode`, `showRange` | `dxSlider`, `dxRangeSlider` |
|
||||||
| HTML | `valueType`, `toolbar.multiline`, `toolbar.items`, `mediaResizing.enabled`, `imageUpload.fileUploadMode`, `imageUpload.uploadUrl` | `dxHtmlEditor` |
|
| HTML | `valueType`, `toolbar.multiline`, `toolbar.items`, `mediaResizing.enabled`, `imageUpload.tabs`, `imageUpload.fileUploadMode`, `imageUpload.uploadUrl`, `tableResizing.enabled`, `tableContextMenu.enabled` | `dxHtmlEditor` |
|
||||||
| Liste sütunu | `format` (metin hâli), `encodeHtml`, `buttons` | hepsi — bu üçü **grid sütununu** da etkiler |
|
| Liste sütunu | `format` (metin hâli), `encodeHtml`, `buttons` | hepsi — bu üçü **grid sütununu** da etkiler |
|
||||||
|
|
||||||
`\*` işaretli anahtarlar backend'de tipli DTO'ya (`GridBoxOptionsDto`, `TagBoxOptionsDto`,
|
`\*` işaretli anahtarlar backend'de tipli DTO'ya (`GridBoxOptionsDto`, `TagBoxOptionsDto`,
|
||||||
|
|
|
||||||
|
|
@ -567,7 +567,7 @@ public class PlatformDbContext :
|
||||||
|
|
||||||
b.Property(x => x.Type).IsRequired().HasMaxLength(64);
|
b.Property(x => x.Type).IsRequired().HasMaxLength(64);
|
||||||
b.Property(x => x.Subject).IsRequired().HasMaxLength(256);
|
b.Property(x => x.Subject).IsRequired().HasMaxLength(256);
|
||||||
b.Property(x => x.Content).IsRequired().HasMaxLength(2048);
|
b.Property(x => x.Content).IsRequired();
|
||||||
b.Property(x => x.EntityName).IsRequired().HasMaxLength(128);
|
b.Property(x => x.EntityName).IsRequired().HasMaxLength(128);
|
||||||
b.Property(x => x.EntityId).IsRequired().HasMaxLength(64);
|
b.Property(x => x.EntityId).IsRequired().HasMaxLength(64);
|
||||||
});
|
});
|
||||||
|
|
@ -1186,7 +1186,7 @@ public class PlatformDbContext :
|
||||||
|
|
||||||
b.Property(x => x.Title).IsRequired().HasMaxLength(256);
|
b.Property(x => x.Title).IsRequired().HasMaxLength(256);
|
||||||
b.Property(x => x.Excerpt).IsRequired().HasMaxLength(512);
|
b.Property(x => x.Excerpt).IsRequired().HasMaxLength(512);
|
||||||
b.Property(x => x.Content).IsRequired().HasMaxLength(4096);
|
b.Property(x => x.Content).IsRequired();
|
||||||
b.Property(x => x.Category).IsRequired().HasMaxLength(64);
|
b.Property(x => x.Category).IsRequired().HasMaxLength(64);
|
||||||
b.Property(x => x.PublishDate).IsRequired();
|
b.Property(x => x.PublishDate).IsRequired();
|
||||||
b.Property(x => x.Attachments).HasMaxLength(2048);
|
b.Property(x => x.Attachments).HasMaxLength(2048);
|
||||||
|
|
@ -1302,7 +1302,7 @@ public class PlatformDbContext :
|
||||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SocialPost)), Prefix.DbSchema);
|
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SocialPost)), Prefix.DbSchema);
|
||||||
b.ConfigureByConvention();
|
b.ConfigureByConvention();
|
||||||
|
|
||||||
b.Property(x => x.Content).IsRequired().HasMaxLength(4096);
|
b.Property(x => x.Content).IsRequired();
|
||||||
b.Property(x => x.LikeCount).HasDefaultValue(0);
|
b.Property(x => x.LikeCount).HasDefaultValue(0);
|
||||||
b.Property(x => x.IsOwnPost).HasDefaultValue(false);
|
b.Property(x => x.IsOwnPost).HasDefaultValue(false);
|
||||||
b.Property(x => x.IsLiked).HasDefaultValue(false);
|
b.Property(x => x.IsLiked).HasDefaultValue(false);
|
||||||
|
|
|
||||||
|
|
@ -855,7 +855,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("Content")
|
b.Property<string>("Content")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(4096)
|
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
b.Property<DateTime>("CreationTime")
|
||||||
|
|
@ -4305,8 +4304,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("Content")
|
b.Property<string>("Content")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(2048)
|
.HasColumnType("nvarchar(max)");
|
||||||
.HasColumnType("nvarchar(2048)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
b.Property<DateTime>("CreationTime")
|
||||||
.HasColumnType("datetime2")
|
.HasColumnType("datetime2")
|
||||||
|
|
@ -5624,7 +5622,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("Content")
|
b.Property<string>("Content")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(4096)
|
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
b.Property<DateTime>("CreationTime")
|
||||||
|
|
|
||||||
|
|
@ -519,7 +519,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||||
Excerpt = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
Excerpt = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
||||||
Content = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: false),
|
Content = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||||
ImageUrl = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
ImageUrl = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
Category = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
Category = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||||
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
|
@ -662,7 +662,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
EntityId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
EntityId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||||
Type = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
Type = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||||
Subject = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
Subject = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||||
Content = table.Column<string>(type: "nvarchar(2048)", maxLength: 2048, nullable: false),
|
Content = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||||
FilesJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
FilesJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
|
@ -802,7 +802,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
Content = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: false),
|
Content = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||||
LikeCount = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
|
LikeCount = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
|
||||||
IsLiked = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
IsLiked = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||||
IsOwnPost = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
IsOwnPost = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||||
|
|
|
||||||
|
|
@ -852,7 +852,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("Content")
|
b.Property<string>("Content")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(4096)
|
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
b.Property<DateTime>("CreationTime")
|
||||||
|
|
@ -4302,8 +4301,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("Content")
|
b.Property<string>("Content")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(2048)
|
.HasColumnType("nvarchar(max)");
|
||||||
.HasColumnType("nvarchar(2048)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
b.Property<DateTime>("CreationTime")
|
||||||
.HasColumnType("datetime2")
|
.HasColumnType("datetime2")
|
||||||
|
|
@ -5621,7 +5619,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("Content")
|
b.Property<string>("Content")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(4096)
|
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
b.Property<DateTime>("CreationTime")
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
export const sizeValues = ['8pt', '10pt', '12pt', '14pt', '18pt', '24pt', '36pt']
|
|
||||||
export const fontValues = [
|
|
||||||
'Arial',
|
|
||||||
'Courier New',
|
|
||||||
'Georgia',
|
|
||||||
'Impact',
|
|
||||||
'Lucida Console',
|
|
||||||
'Tahoma',
|
|
||||||
'Times New Roman',
|
|
||||||
'Verdana',
|
|
||||||
]
|
|
||||||
export const headerValues = [false, 1, 2, 3, 4, 5]
|
|
||||||
export const fontSizeOptions = {
|
|
||||||
inputAttr: {
|
|
||||||
'aria-label': 'Font size',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
export const fontFamilyOptions = {
|
|
||||||
inputAttr: {
|
|
||||||
'aria-label': 'Font family',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
export const headerOptions = {
|
|
||||||
inputAttr: {
|
|
||||||
'aria-label': 'Font family',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
@ -939,6 +939,27 @@ export const optionSpecs: OptionSpec[] = [
|
||||||
group: 'html',
|
group: 'html',
|
||||||
editors: ['dxHtmlEditor'],
|
editors: ['dxHtmlEditor'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'imageUpload.tabs',
|
||||||
|
label: 'imageUpload.tabs',
|
||||||
|
type: 'json',
|
||||||
|
group: 'html',
|
||||||
|
editors: ['dxHtmlEditor'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tableResizing.enabled',
|
||||||
|
label: 'tableResizing.enabled',
|
||||||
|
type: 'boolean',
|
||||||
|
group: 'html',
|
||||||
|
editors: ['dxHtmlEditor'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tableContextMenu.enabled',
|
||||||
|
label: 'tableContextMenu.enabled',
|
||||||
|
type: 'boolean',
|
||||||
|
group: 'html',
|
||||||
|
editors: ['dxHtmlEditor'],
|
||||||
|
},
|
||||||
|
|
||||||
// ── Liste sütununu da etkileyenler ───────────────────────────────
|
// ── Liste sütununu da etkileyenler ───────────────────────────────
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
* Hazır editorOptions kalıpları. Her kalıp mevcut JSON ile birleştirilir,
|
* Hazır editorOptions kalıpları. Her kalıp mevcut JSON ile birleştirilir,
|
||||||
* diğer ayarları silmez.
|
* diğer ayarları silmez.
|
||||||
*/
|
*/
|
||||||
|
import { buildHtmlEditorDefaults } from '@/views/shared/htmlEditorOptions'
|
||||||
import type { JsonObject } from './jsonUtils'
|
import type { JsonObject } from './jsonUtils'
|
||||||
|
|
||||||
export type EditorOptionsPreset = {
|
export type EditorOptionsPreset = {
|
||||||
|
|
@ -13,84 +14,8 @@ export type EditorOptionsPreset = {
|
||||||
value: JsonObject
|
value: JsonObject
|
||||||
}
|
}
|
||||||
|
|
||||||
const htmlToolbarItems = [
|
const buildHtmlEditorOptions = (): JsonObject =>
|
||||||
'undo',
|
JSON.parse(JSON.stringify(buildHtmlEditorDefaults())) as JsonObject
|
||||||
'redo',
|
|
||||||
'separator',
|
|
||||||
'size',
|
|
||||||
'font',
|
|
||||||
'separator',
|
|
||||||
'bold',
|
|
||||||
'italic',
|
|
||||||
'strike',
|
|
||||||
'underline',
|
|
||||||
'separator',
|
|
||||||
'alignLeft',
|
|
||||||
'alignCenter',
|
|
||||||
'alignRight',
|
|
||||||
'alignJustify',
|
|
||||||
'separator',
|
|
||||||
'orderedList',
|
|
||||||
'bulletList',
|
|
||||||
'separator',
|
|
||||||
'header',
|
|
||||||
'separator',
|
|
||||||
'color',
|
|
||||||
'background',
|
|
||||||
'separator',
|
|
||||||
'link',
|
|
||||||
'image',
|
|
||||||
'separator',
|
|
||||||
'clear',
|
|
||||||
'codeBlock',
|
|
||||||
'blockquote',
|
|
||||||
'separator',
|
|
||||||
'insertTable',
|
|
||||||
'deleteTable',
|
|
||||||
'insertRowAbove',
|
|
||||||
'insertRowBelow',
|
|
||||||
'deleteRow',
|
|
||||||
'insertColumnLeft',
|
|
||||||
'insertColumnRight',
|
|
||||||
'deleteColumn',
|
|
||||||
'cellProperties',
|
|
||||||
'tableProperties',
|
|
||||||
]
|
|
||||||
|
|
||||||
const buildHtmlEditorOptions = (): JsonObject => ({
|
|
||||||
mediaResizing: { enabled: true },
|
|
||||||
imageUpload: { tabs: ['file', 'url'], fileUploadMode: 'base64' },
|
|
||||||
toolbar: {
|
|
||||||
multiline: true,
|
|
||||||
items: htmlToolbarItems.map((name) => {
|
|
||||||
if (name === 'size') {
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
acceptedValues: ['8pt', '10pt', '12pt', '14pt', '18pt', '24pt', '36pt'],
|
|
||||||
options: { inputAttr: { 'aria-label': 'Font size' } },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (name === 'font') {
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
acceptedValues: [
|
|
||||||
'Arial',
|
|
||||||
'Courier New',
|
|
||||||
'Georgia',
|
|
||||||
'Impact',
|
|
||||||
'Lucida Console',
|
|
||||||
'Tahoma',
|
|
||||||
'Times New Roman',
|
|
||||||
'Verdana',
|
|
||||||
],
|
|
||||||
options: { inputAttr: { 'aria-label': 'Font family' } },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (name === 'header') return { name, acceptedValues: [false, 1, 2, 3, 4, 5] }
|
|
||||||
return { name }
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const editorOptionsPresets: EditorOptionsPreset[] = [
|
export const editorOptionsPresets: EditorOptionsPreset[] = [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import { FaFileAlt, FaFileUpload, FaPlus, FaTrash } from 'react-icons/fa'
|
||||||
import { Field, FieldProps, Form, Formik } from 'formik'
|
import { Field, FieldProps, Form, Formik } from 'formik'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import { noteService } from '@/services/note.service'
|
import { noteService } from '@/services/note.service'
|
||||||
import { HtmlEditor, ImageUpload, MediaResizing } from 'devextreme-react/html-editor'
|
import { HtmlEditor } from 'devextreme-react/html-editor'
|
||||||
import { HtmlEditorToolbar } from '@/views/shared/HtmlEditorToolbarItems'
|
import { htmlEditorDefaults } from '@/views/shared/htmlEditorOptions'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
|
||||||
const validationSchema = Yup.object({
|
const validationSchema = Yup.object({
|
||||||
|
|
@ -147,15 +147,12 @@ function NoteModalContent({
|
||||||
<Field name="content">
|
<Field name="content">
|
||||||
{({ field }: FieldProps) => (
|
{({ field }: FieldProps) => (
|
||||||
<HtmlEditor
|
<HtmlEditor
|
||||||
|
{...htmlEditorDefaults}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
onValueChanged={(e) => setFieldValue('content', e.value)}
|
onValueChanged={(e) => setFieldValue('content', e.value)}
|
||||||
height={isMaximized ? '50vh' : 220}
|
height={isMaximized ? '50vh' : 220}
|
||||||
placeholder={translate('::App.ListFormNoteModal.NoteModalContent')}
|
placeholder={translate('::App.ListFormNoteModal.NoteModalContent')}
|
||||||
>
|
/>
|
||||||
<MediaResizing enabled={true} />
|
|
||||||
<ImageUpload fileUploadMode="base64" />
|
|
||||||
<HtmlEditorToolbar />
|
|
||||||
</HtmlEditor>
|
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,11 @@ import { captionize } from 'devextreme/core/utils/inflector'
|
||||||
import type { GroupItem } from 'devextreme/ui/form'
|
import type { GroupItem } from 'devextreme/ui/form'
|
||||||
import type { ColumnFormatDto, GridDto } from '@/proxy/form/models'
|
import type { ColumnFormatDto, GridDto } from '@/proxy/form/models'
|
||||||
import { getEditingFormGroups } from '../../list/Utils'
|
import { getEditingFormGroups } from '../../list/Utils'
|
||||||
import { isFormItemVisible, resolveEditorType } from '../../list/shared/editingForm'
|
import {
|
||||||
|
isFormItemVisible,
|
||||||
|
resolveEditorType,
|
||||||
|
withEditorTypeDefaults,
|
||||||
|
} from '../../list/shared/editingForm'
|
||||||
import type { GridColumnData } from '../../list/GridColumnData'
|
import type { GridColumnData } from '../../list/GridColumnData'
|
||||||
import type { RowMode, SimpleItemWithColData } from '../types'
|
import type { RowMode, SimpleItemWithColData } from '../types'
|
||||||
|
|
||||||
|
|
@ -71,7 +75,10 @@ const toFormItem = (
|
||||||
editorType2: colFormat.editorType2,
|
editorType2: colFormat.editorType2,
|
||||||
editorType: resolveEditorType(colFormat.editorType2),
|
editorType: resolveEditorType(colFormat.editorType2),
|
||||||
colSpan: colFormat.colSpan,
|
colSpan: colFormat.colSpan,
|
||||||
editorOptions: { ...lookupEditorOptions, ...parsedEditorOptions },
|
editorOptions: withEditorTypeDefaults(colFormat.editorType2, {
|
||||||
|
...lookupEditorOptions,
|
||||||
|
...parsedEditorOptions,
|
||||||
|
}),
|
||||||
colData: colFormat,
|
colData: colFormat,
|
||||||
tagBoxOptions: colFormat.tagBoxOptions,
|
tagBoxOptions: colFormat.tagBoxOptions,
|
||||||
gridBoxOptions: colFormat.gridBoxOptions,
|
gridBoxOptions: colFormat.gridBoxOptions,
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,12 @@
|
||||||
import { FaTimes } from 'react-icons/fa'
|
import { FaTimes } from 'react-icons/fa'
|
||||||
import { Formik, Form, Field, FieldProps } from 'formik'
|
import { Formik, Form, Field, FieldProps } from 'formik'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import { HtmlEditor, ImageUpload, Item, MediaResizing, Toolbar } from 'devextreme-react/html-editor'
|
import { HtmlEditor } from 'devextreme-react/html-editor'
|
||||||
|
import { htmlEditorDefaults } from '@/views/shared/htmlEditorOptions'
|
||||||
import { useStoreState } from '@/store/store'
|
import { useStoreState } from '@/store/store'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { Button, FormContainer, FormItem } from '@/components/ui'
|
import { Button, FormContainer, FormItem } from '@/components/ui'
|
||||||
import { stripHtml } from './utils'
|
import { stripHtml } from './utils'
|
||||||
import {
|
|
||||||
fontFamilyOptions,
|
|
||||||
fontSizeOptions,
|
|
||||||
fontValues,
|
|
||||||
headerOptions,
|
|
||||||
headerValues,
|
|
||||||
sizeValues,
|
|
||||||
} from '@/proxy/reports/data'
|
|
||||||
|
|
||||||
interface PostFormValues {
|
interface PostFormValues {
|
||||||
content: string
|
content: string
|
||||||
|
|
@ -112,6 +105,7 @@ export function CreatePostModal({ onClose, onSubmit, parentPostId }: CreatePostM
|
||||||
<Field name="content">
|
<Field name="content">
|
||||||
{({ field }: FieldProps) => (
|
{({ field }: FieldProps) => (
|
||||||
<HtmlEditor
|
<HtmlEditor
|
||||||
|
{...htmlEditorDefaults}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
height={300}
|
height={300}
|
||||||
placeholder={
|
placeholder={
|
||||||
|
|
@ -120,59 +114,7 @@ export function CreatePostModal({ onClose, onSubmit, parentPostId }: CreatePostM
|
||||||
: translate('::App.ForumPostManagement.MessageNew')
|
: translate('::App.ForumPostManagement.MessageNew')
|
||||||
}
|
}
|
||||||
onValueChanged={(e) => setFieldValue('content', e.value)}
|
onValueChanged={(e) => setFieldValue('content', e.value)}
|
||||||
>
|
/>
|
||||||
<MediaResizing enabled={true} />
|
|
||||||
<ImageUpload fileUploadMode="base64" />
|
|
||||||
<Toolbar multiline={true}>
|
|
||||||
<Item name="undo" />
|
|
||||||
<Item name="redo" />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="size" acceptedValues={sizeValues} options={fontSizeOptions} />
|
|
||||||
<Item
|
|
||||||
name="font"
|
|
||||||
acceptedValues={fontValues}
|
|
||||||
options={fontFamilyOptions}
|
|
||||||
/>
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="bold" />
|
|
||||||
<Item name="italic" />
|
|
||||||
<Item name="strike" />
|
|
||||||
<Item name="underline" />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="alignLeft" />
|
|
||||||
<Item name="alignCenter" />
|
|
||||||
<Item name="alignRight" />
|
|
||||||
<Item name="alignJustify" />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="orderedList" />
|
|
||||||
<Item name="bulletList" />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item
|
|
||||||
name="header"
|
|
||||||
acceptedValues={headerValues}
|
|
||||||
options={headerOptions}
|
|
||||||
/>
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="color" />
|
|
||||||
<Item name="background" />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="link" />
|
|
||||||
<Item name="image" />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="clear" />
|
|
||||||
<Item name="codeBlock" />
|
|
||||||
<Item name="blockquote" />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="insertTable" />
|
|
||||||
<Item name="deleteTable" />
|
|
||||||
<Item name="insertRowAbove" />
|
|
||||||
<Item name="insertRowBelow" />
|
|
||||||
<Item name="deleteRow" />
|
|
||||||
<Item name="insertColumnLeft" />
|
|
||||||
<Item name="insertColumnRight" />
|
|
||||||
<Item name="deleteColumn" />
|
|
||||||
</Toolbar>
|
|
||||||
</HtmlEditor>
|
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ import {
|
||||||
getMobilePopupPosition,
|
getMobilePopupPosition,
|
||||||
shouldUseMobileEditPopup,
|
shouldUseMobileEditPopup,
|
||||||
} from './shared/editPopup'
|
} from './shared/editPopup'
|
||||||
import { getValueByField, resolveEditorType } from './shared/editingForm'
|
import { getValueByField, resolveEditorType, withEditorTypeDefaults } from './shared/editingForm'
|
||||||
import { createEditingFormHandlers } from './shared/editingFormScripts'
|
import { createEditingFormHandlers } from './shared/editingFormScripts'
|
||||||
import { flattenColumns } from './shared/columns'
|
import { flattenColumns } from './shared/columns'
|
||||||
import { getInitialExtraFilters, mergeFilterTriplets } from './shared/hooks'
|
import { getInitialExtraFilters, mergeFilterTriplets } from './shared/hooks'
|
||||||
|
|
@ -811,6 +811,7 @@ const CardView = (props: CardViewProps) => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('EditorOptions parse error:', item.fieldName, error)
|
console.error('EditorOptions parse error:', item.fieldName, error)
|
||||||
}
|
}
|
||||||
|
editorOptions = withEditorTypeDefaults(item.editorType2, editorOptions)
|
||||||
|
|
||||||
if (column.sourceDbType === DbTypeEnum.Date) {
|
if (column.sourceDbType === DbTypeEnum.Date) {
|
||||||
editorOptions = {
|
editorOptions = {
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ import {
|
||||||
getMobilePopupPosition,
|
getMobilePopupPosition,
|
||||||
shouldUseMobileEditPopup,
|
shouldUseMobileEditPopup,
|
||||||
} from './shared/editPopup'
|
} from './shared/editPopup'
|
||||||
import { getValueByField, resolveEditorType } from './shared/editingForm'
|
import { getValueByField, resolveEditorType, withEditorTypeDefaults } from './shared/editingForm'
|
||||||
import { flattenGridColumns } from './shared/columns'
|
import { flattenGridColumns } from './shared/columns'
|
||||||
import { useListFormCustomSources, useListFormGridDto, useWidgetGroupHeight } from './shared/hooks'
|
import { useListFormCustomSources, useListFormGridDto, useWidgetGroupHeight } from './shared/hooks'
|
||||||
|
|
||||||
|
|
@ -567,6 +567,7 @@ const SchedulerView = (props: SchedulerViewProps) => {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
}
|
}
|
||||||
|
editorOptions = withEditorTypeDefaults(i.editorType2, editorOptions)
|
||||||
|
|
||||||
const listFormField = i
|
const listFormField = i
|
||||||
const listFormColumn = listFormColumns.find(
|
const listFormColumn = listFormColumns.find(
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import type { GridDto, TodoOptionDto } from '@/proxy/form/models'
|
||||||
import type { IdentityUserDto } from '@/proxy/admin/models'
|
import type { IdentityUserDto } from '@/proxy/admin/models'
|
||||||
import type { DataGridRef } from 'devextreme-react/data-grid'
|
import type { DataGridRef } from 'devextreme-react/data-grid'
|
||||||
import type CustomStore from 'devextreme/data/custom_store'
|
import type CustomStore from 'devextreme/data/custom_store'
|
||||||
import { HtmlEditor, ImageUpload, MediaResizing } from 'devextreme-react/html-editor'
|
import { HtmlEditor } from 'devextreme-react/html-editor'
|
||||||
import { HtmlEditorToolbar } from '@/views/shared/HtmlEditorToolbarItems'
|
import { htmlEditorDefaults } from '@/views/shared/htmlEditorOptions'
|
||||||
import Toolbar, { Item as ToolbarItem } from 'devextreme-react/toolbar'
|
import Toolbar, { Item as ToolbarItem } from 'devextreme-react/toolbar'
|
||||||
import TagBox from 'devextreme-react/tag-box'
|
import TagBox from 'devextreme-react/tag-box'
|
||||||
import { AVATAR_URL } from '@/constants/app.constant'
|
import { AVATAR_URL } from '@/constants/app.constant'
|
||||||
|
|
@ -1089,6 +1089,7 @@ const TodoBoardContent = ({
|
||||||
<div className="flex min-h-[260px] flex-1 flex-col pt-4">
|
<div className="flex min-h-[260px] flex-1 flex-col pt-4">
|
||||||
{activeTab === 'description' && options.descriptionExpr && (
|
{activeTab === 'description' && options.descriptionExpr && (
|
||||||
<HtmlEditor
|
<HtmlEditor
|
||||||
|
{...htmlEditorDefaults}
|
||||||
height="100%"
|
height="100%"
|
||||||
placeholder={translate('::App.ListFormTodoBoard.DescriptionPlaceholder')}
|
placeholder={translate('::App.ListFormTodoBoard.DescriptionPlaceholder')}
|
||||||
readOnly={!canUpdate}
|
readOnly={!canUpdate}
|
||||||
|
|
@ -1099,11 +1100,7 @@ const TodoBoardContent = ({
|
||||||
[options.descriptionExpr!]: event.value,
|
[options.descriptionExpr!]: event.value,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
>
|
/>
|
||||||
<MediaResizing enabled={true} />
|
|
||||||
<ImageUpload fileUploadMode="base64" />
|
|
||||||
<HtmlEditorToolbar />
|
|
||||||
</HtmlEditor>
|
|
||||||
)}
|
)}
|
||||||
{activeTab === 'timesheet' && options.timesheetExpr && (
|
{activeTab === 'timesheet' && options.timesheetExpr && (
|
||||||
<div className="flex min-h-0 flex-1 flex-col gap-2">
|
<div className="flex min-h-0 flex-1 flex-col gap-2">
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import {
|
||||||
} from '@/proxy/form/models'
|
} from '@/proxy/form/models'
|
||||||
import { scanBarcode } from '@/components/shared/BarcodeScanner/scanBarcode'
|
import { scanBarcode } from '@/components/shared/BarcodeScanner/scanBarcode'
|
||||||
import { getNextSequenceValue } from '@/services/form.service'
|
import { getNextSequenceValue } from '@/services/form.service'
|
||||||
|
import { mergeHtmlEditorOptions } from '@/views/shared/htmlEditorOptions'
|
||||||
import type { EditorType2, RowMode, SimpleItemWithColData } from '../../form/types'
|
import type { EditorType2, RowMode, SimpleItemWithColData } from '../../form/types'
|
||||||
import {
|
import {
|
||||||
autoNumber,
|
autoNumber,
|
||||||
|
|
@ -423,6 +424,15 @@ export const resolveEditorType = (editorType2?: EditorType2) => {
|
||||||
return editorType2
|
return editorType2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Editör tipinin platform varsayılanlarını yapılandırmadan gelen editorOptions'ın altına serer. */
|
||||||
|
export const withEditorTypeDefaults = <T extends Record<string, any>>(
|
||||||
|
editorType2: EditorType2 | undefined,
|
||||||
|
editorOptions: T,
|
||||||
|
): T =>
|
||||||
|
editorType2 === 'dxHtmlEditor'
|
||||||
|
? mergeHtmlEditorOptions(editorOptions)
|
||||||
|
: editorOptions
|
||||||
|
|
||||||
type FormItemMapperParams = {
|
type FormItemMapperParams = {
|
||||||
mode: RowMode
|
mode: RowMode
|
||||||
translate: (key: string) => string
|
translate: (key: string) => string
|
||||||
|
|
@ -484,11 +494,11 @@ export const createFormItemMapper = ({
|
||||||
? { readOnly: true }
|
? { readOnly: true }
|
||||||
: {}
|
: {}
|
||||||
|
|
||||||
const editorOptions: EditorOptionsWithButtons = {
|
const editorOptions: EditorOptionsWithButtons = withEditorTypeDefaults(colFormat.editorType2, {
|
||||||
...defaultEditorOptions,
|
...defaultEditorOptions,
|
||||||
...parsedEditorOptions,
|
...parsedEditorOptions,
|
||||||
...forcedEditorOptions,
|
...forcedEditorOptions,
|
||||||
}
|
})
|
||||||
|
|
||||||
if (editorOptions.buttons) {
|
if (editorOptions.buttons) {
|
||||||
editorOptions.buttons = editorOptions.buttons.map((btn: any) => {
|
editorOptions.buttons = editorOptions.buttons.map((btn: any) => {
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
/**
|
|
||||||
* DevExtreme HtmlEditor için standart araç çubuğu.
|
|
||||||
* NoteModal ve TodoBoard içinde birebir aynı ~40 satırlık tanım tekrar ediyordu.
|
|
||||||
*/
|
|
||||||
import { Item, Toolbar } from 'devextreme-react/html-editor'
|
|
||||||
import {
|
|
||||||
fontFamilyOptions,
|
|
||||||
fontSizeOptions,
|
|
||||||
fontValues,
|
|
||||||
headerOptions,
|
|
||||||
headerValues,
|
|
||||||
sizeValues,
|
|
||||||
} from '@/proxy/reports/data'
|
|
||||||
|
|
||||||
export const HtmlEditorToolbar = () => (
|
|
||||||
<Toolbar multiline>
|
|
||||||
<Item name="undo" />
|
|
||||||
<Item name="redo" />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="size" acceptedValues={sizeValues} options={fontSizeOptions} />
|
|
||||||
<Item name="font" acceptedValues={fontValues} options={fontFamilyOptions} />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="bold" />
|
|
||||||
<Item name="italic" />
|
|
||||||
<Item name="underline" />
|
|
||||||
<Item name="strike" />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="orderedList" />
|
|
||||||
<Item name="bulletList" />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="header" acceptedValues={headerValues} options={headerOptions} />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="color" />
|
|
||||||
<Item name="background" />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="alignLeft" />
|
|
||||||
<Item name="alignCenter" />
|
|
||||||
<Item name="alignRight" />
|
|
||||||
<Item name="alignJustify" />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="link" />
|
|
||||||
<Item name="image" />
|
|
||||||
<Item name="separator" />
|
|
||||||
<Item name="clear" />
|
|
||||||
</Toolbar>
|
|
||||||
)
|
|
||||||
114
ui/src/views/shared/htmlEditorOptions.ts
Normal file
114
ui/src/views/shared/htmlEditorOptions.ts
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
/**
|
||||||
|
* Uygulamadaki bütün HtmlEditor'ların ortak varsayılanı. Sabit ekranlar (not, todo, forum)
|
||||||
|
* `<HtmlEditor {...htmlEditorDefaults}>` ile, dinamik formlar `mergeHtmlEditorOptions` ile
|
||||||
|
* aynı yetenek setini alır; ListForm'daki "HTML toolbar" kalıbı da buradan beslenir.
|
||||||
|
* Değerler JSON'a yazılabilir kalmalıdır — kalıp editorOptions kolonuna olduğu gibi kaydedilir.
|
||||||
|
*/
|
||||||
|
import type { Properties as HtmlEditorProperties } from 'devextreme/ui/html_editor'
|
||||||
|
|
||||||
|
const sizeValues = ['8pt', '10pt', '12pt', '14pt', '18pt', '24pt', '36pt']
|
||||||
|
const fontValues = [
|
||||||
|
'Arial',
|
||||||
|
'Courier New',
|
||||||
|
'Georgia',
|
||||||
|
'Impact',
|
||||||
|
'Lucida Console',
|
||||||
|
'Segoe UI',
|
||||||
|
'Tahoma',
|
||||||
|
'Times New Roman',
|
||||||
|
'Verdana',
|
||||||
|
]
|
||||||
|
const headerValues = [false, 1, 2, 3, 4, 5, 6]
|
||||||
|
|
||||||
|
type ToolbarItem = NonNullable<NonNullable<HtmlEditorProperties['toolbar']>['items']>[number]
|
||||||
|
|
||||||
|
export const htmlToolbarItemNames = [
|
||||||
|
'undo',
|
||||||
|
'redo',
|
||||||
|
'separator',
|
||||||
|
'header',
|
||||||
|
'size',
|
||||||
|
'font',
|
||||||
|
'separator',
|
||||||
|
'bold',
|
||||||
|
'italic',
|
||||||
|
'underline',
|
||||||
|
'strike',
|
||||||
|
'superscript',
|
||||||
|
'subscript',
|
||||||
|
'separator',
|
||||||
|
'color',
|
||||||
|
'background',
|
||||||
|
'separator',
|
||||||
|
'alignLeft',
|
||||||
|
'alignCenter',
|
||||||
|
'alignRight',
|
||||||
|
'alignJustify',
|
||||||
|
'separator',
|
||||||
|
'orderedList',
|
||||||
|
'bulletList',
|
||||||
|
'decreaseIndent',
|
||||||
|
'increaseIndent',
|
||||||
|
'separator',
|
||||||
|
'blockquote',
|
||||||
|
'codeBlock',
|
||||||
|
'link',
|
||||||
|
'image',
|
||||||
|
'separator',
|
||||||
|
'insertTable',
|
||||||
|
'insertHeaderRow',
|
||||||
|
'insertRowAbove',
|
||||||
|
'insertRowBelow',
|
||||||
|
'deleteRow',
|
||||||
|
'insertColumnLeft',
|
||||||
|
'insertColumnRight',
|
||||||
|
'deleteColumn',
|
||||||
|
'cellProperties',
|
||||||
|
'tableProperties',
|
||||||
|
'deleteTable',
|
||||||
|
'separator',
|
||||||
|
'clear',
|
||||||
|
] as const
|
||||||
|
|
||||||
|
const toToolbarItem = (name: (typeof htmlToolbarItemNames)[number]): ToolbarItem => {
|
||||||
|
if (name === 'size') {
|
||||||
|
return { name, acceptedValues: sizeValues, options: { inputAttr: { 'aria-label': 'Font size' } } }
|
||||||
|
}
|
||||||
|
if (name === 'font') {
|
||||||
|
return { name, acceptedValues: fontValues, options: { inputAttr: { 'aria-label': 'Font family' } } }
|
||||||
|
}
|
||||||
|
if (name === 'header') {
|
||||||
|
return { name, acceptedValues: headerValues, options: { inputAttr: { 'aria-label': 'Header' } } }
|
||||||
|
}
|
||||||
|
return { name }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const buildHtmlEditorDefaults = () =>
|
||||||
|
({
|
||||||
|
toolbar: { multiline: true, items: htmlToolbarItemNames.map(toToolbarItem) },
|
||||||
|
// Varsayılan sekme yalnızca 'url'dir; bilgisayardan resim seçmek için 'file' açıkça verilmeli.
|
||||||
|
imageUpload: { tabs: ['file', 'url'], fileUploadMode: 'base64' },
|
||||||
|
mediaResizing: { enabled: true },
|
||||||
|
tableResizing: { enabled: true },
|
||||||
|
tableContextMenu: { enabled: true },
|
||||||
|
}) satisfies HtmlEditorProperties
|
||||||
|
|
||||||
|
export const htmlEditorDefaults = buildHtmlEditorDefaults()
|
||||||
|
|
||||||
|
const nestedKeys = ['toolbar', 'imageUpload', 'mediaResizing', 'tableResizing', 'tableContextMenu'] as const
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yapılandırmadan gelen editorOptions'ı varsayılanın üstüne yazar. İç içe bloklar tek seviye
|
||||||
|
* birleştirilir; aksi hâlde yalnızca `imageUpload.fileUploadMode` yazan eski bir kayıt
|
||||||
|
* dosya sekmesini ve toolbar'ı sessizce kaybederdi.
|
||||||
|
*/
|
||||||
|
export const mergeHtmlEditorOptions = <T extends Record<string, any>>(options: T): T => {
|
||||||
|
const merged: Record<string, any> = { ...htmlEditorDefaults, ...options }
|
||||||
|
for (const key of nestedKeys) {
|
||||||
|
const own = options[key]
|
||||||
|
if (own && typeof own === 'object' && !Array.isArray(own)) {
|
||||||
|
merged[key] = { ...htmlEditorDefaults[key], ...own }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return merged as T
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue