cabloy 5.1.141 → 5.1.142
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cabloy-version +1 -1
- package/.github/workflows/playwright-e2e.yml +2 -2
- package/CHANGELOG.md +21 -0
- package/cabloy-docs/frontend/form-guide.md +47 -0
- package/cabloy-docs/frontend/markdown-guide.md +50 -17
- package/cabloy-docs/frontend/route-alias-guide.md +20 -0
- package/cabloy-docs/frontend/scripts.md +9 -11
- package/cabloy-docs/frontend/zova-form-under-the-hood.md +25 -0
- package/cabloy-docs/fullstack/parallel-worktree-environment.md +1 -1
- package/cabloy-docs/fullstack/quickstart.md +5 -4
- package/cabloy-docs/reference/repo-scripts.md +29 -42
- package/e2e/config/playwright.config.ts +34 -0
- package/e2e/scripts/e2e.ts +2 -27
- package/e2e/scripts/runE2e.ts +62 -0
- package/e2e/scripts/runE2eArgs.test.ts +75 -0
- package/e2e/scripts/runE2eArgs.ts +158 -0
- package/e2e/specs/{a-commerce/commerce.spec.ts → a-commerce.spec.ts} +361 -30
- package/e2e/specs/{cabloy-basic/account.spec.ts → account.spec.ts} +1 -2
- package/oxlint.config.ts +3 -0
- package/package.json +3 -9
- package/scripts/upgrade.ts +63 -68
- package/vona/packages-utils/lint/package.json +1 -1
- package/vona/packages-utils/lint/src/oxc/lint.ts +1 -1
- package/vona/packages-utils/lint/src/oxc/lintVue.ts +1 -1
- package/vona/pnpm-lock.yaml +41 -41
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/refundRecoveryView.tsx +7 -2
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/order.ts +31 -15
- package/vona/src/suite/a-home/modules/home-user/package.json +1 -0
- package/vona/src/suite/a-home/modules/home-user/src/service/account.ts +15 -2
- package/vona/src/suite/a-home/modules/home-user/test/accountActivation.test.ts +34 -4
- package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/package.json +2 -1
- package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/src/.metadata/index.ts +26 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/src/bean/imageScene.markdown.ts +13 -0
- package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
- package/vona/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/providerOperation.ts +23 -0
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/refundOperation.ts +7 -0
- package/vona/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/modules/pay-paypal/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/modules/pay-stripe/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/package.json +5 -5
- package/zova/packages-utils/zova-jsx/package.json +2 -2
- package/zova/packages-zova/zova/package.json +3 -3
- package/zova/packages-zova/zova-core/package.json +1 -1
- package/zova/packages-zova/zova-core/src/bean/resource/locale/type.ts +1 -0
- package/zova/pnpm-lock.yaml +63 -18
- package/zova/src/front/config/config/config.cabloyBasicWeb.ts +3 -1
- package/zova/src/front/config/config/config.cabloyCommerce.ts +4 -2
- package/zova/src/suite/a-commerce/modules/commerce-member/src/page/address/controller.tsx +64 -63
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/types.ts +6 -1
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/component/tableCellActionRefund/controller.tsx +413 -134
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/en-us.ts +76 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/zh-cn.ts +67 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/order.ts +1 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/cart/controller.tsx +1 -1
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/checkout/controller.tsx +7 -7
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/order/controller.tsx +46 -9
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/payment/controller.tsx +50 -30
- package/zova/src/suite/cabloy-basic/modules/basic-captcha/src/component/formFieldCaptcha/controller.tsx +6 -2
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/component/formFieldImage/controller.tsx +9 -15
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/package.json +10 -2
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/.metadata/index.ts +11 -2
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/.metadata/locales.ts +7 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/component/formFieldMarkdown/controller.tsx +723 -5
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/component/formFieldMarkdown/render.tsx +461 -2
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/config/locale/en-us.ts +59 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/config/locale/zh-cn.ts +59 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/index.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/lib/codeBlockLanguages.ts +42 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/lib/richTextContentStyle.ts +20 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/test/lib/codeBlockLanguages.test.ts +25 -0
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +1 -0
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
- package/zova/src/suite-vendor/a-pay/modules/a-pay/src/component/paymentNextAction/controller.tsx +26 -12
- package/zova/src/suite-vendor/a-pay/modules/pay-mock/cli/openapi.config.ts +1 -1
- package/zova/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
- package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/payMockMockPayment.ts +26 -0
- package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/apiSchema/payMockMockPayment.ts +8 -1
- package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/model/payMockPayment.ts +13 -1
- package/zova/src/suite-vendor/a-pay/package.json +3 -3
- package/zova/src/suite-vendor/a-zova/modules/a-form/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/component/formField/controller.tsx +2 -0
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/types/formField.ts +7 -6
- package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/package.json +3 -3
- package/e2e/config/playwright.basic.config.ts +0 -3
- package/e2e/config/playwright.commerce.config.ts +0 -3
- package/e2e/config/playwright.shared.config.ts +0 -38
- package/e2e/scripts/testE2eClean.ts +0 -56
- /package/e2e/specs/{cabloy-basic/basic.spec.ts → cabloy-basic.spec.ts} +0 -0
|
@@ -4,8 +4,11 @@ import type {
|
|
|
4
4
|
IFormFieldComponentOptions,
|
|
5
5
|
IFormFieldRenderContextPropsBucket,
|
|
6
6
|
} from 'zova-module-a-form';
|
|
7
|
-
import type { IResourceFormFieldOptionsBase } from 'zova-module-a-openapi';
|
|
7
|
+
import type { IResourceFormFieldOptionsBase, IImageSceneRecord } from 'zova-module-a-openapi';
|
|
8
|
+
import type { IImageUploaderResult } from 'zova-module-basic-image';
|
|
8
9
|
|
|
10
|
+
import { findParentNodeClosestToPos } from '@tiptap/core';
|
|
11
|
+
import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight';
|
|
9
12
|
import { Highlight } from '@tiptap/extension-highlight';
|
|
10
13
|
import { Image } from '@tiptap/extension-image';
|
|
11
14
|
import { TaskItem, TaskList } from '@tiptap/extension-list';
|
|
@@ -13,21 +16,84 @@ import { TableKit } from '@tiptap/extension-table';
|
|
|
13
16
|
import { Markdown } from '@tiptap/markdown';
|
|
14
17
|
import StarterKit from '@tiptap/starter-kit';
|
|
15
18
|
import { Editor } from '@tiptap/vue-3';
|
|
19
|
+
import { common, createLowlight } from 'lowlight';
|
|
16
20
|
import { BeanControllerBase } from 'zova';
|
|
17
21
|
import { Controller } from 'zova-module-a-bean';
|
|
18
22
|
|
|
23
|
+
import { getCodeBlockLanguage, isCodeBlockLanguage } from '../../lib/codeBlockLanguages.js';
|
|
24
|
+
|
|
25
|
+
const markdownImageScene = 'a-markdown:markdown';
|
|
26
|
+
const lowlight = createLowlight(common);
|
|
27
|
+
|
|
19
28
|
declare module 'zova-module-a-openapi' {
|
|
20
29
|
export interface IResourceFormFieldRecord {
|
|
21
30
|
'basic-markdown:formFieldMarkdown'?: IResourceFormFieldMarkdownOptions;
|
|
22
31
|
}
|
|
23
32
|
}
|
|
24
33
|
|
|
25
|
-
export interface IResourceFormFieldMarkdownOptions extends IResourceFormFieldOptionsBase {
|
|
34
|
+
export interface IResourceFormFieldMarkdownOptions extends IResourceFormFieldOptionsBase {
|
|
35
|
+
imageScene?: keyof IImageSceneRecord | string;
|
|
36
|
+
}
|
|
26
37
|
|
|
27
38
|
export interface ControllerFormFieldMarkdownProps extends IFormFieldComponentOptions {
|
|
28
39
|
options?: IResourceFormFieldMarkdownOptions;
|
|
29
40
|
}
|
|
30
41
|
|
|
42
|
+
type IMarkdownHeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
43
|
+
|
|
44
|
+
interface IMarkdownToolbarState {
|
|
45
|
+
bold: boolean;
|
|
46
|
+
italic: boolean;
|
|
47
|
+
strike: boolean;
|
|
48
|
+
code: boolean;
|
|
49
|
+
highlight: boolean;
|
|
50
|
+
bulletList: boolean;
|
|
51
|
+
orderedList: boolean;
|
|
52
|
+
taskList: boolean;
|
|
53
|
+
blockquote: boolean;
|
|
54
|
+
codeBlock: boolean;
|
|
55
|
+
codeBlockLanguage?: string;
|
|
56
|
+
link: boolean;
|
|
57
|
+
headingLevel: IMarkdownHeadingLevel | undefined;
|
|
58
|
+
canUndo: boolean;
|
|
59
|
+
canRedo: boolean;
|
|
60
|
+
canBold: boolean;
|
|
61
|
+
canItalic: boolean;
|
|
62
|
+
canStrike: boolean;
|
|
63
|
+
canCode: boolean;
|
|
64
|
+
canHighlight: boolean;
|
|
65
|
+
canBulletList: boolean;
|
|
66
|
+
canOrderedList: boolean;
|
|
67
|
+
canTaskList: boolean;
|
|
68
|
+
canBlockquote: boolean;
|
|
69
|
+
canCodeBlock: boolean;
|
|
70
|
+
canLink: boolean;
|
|
71
|
+
canHorizontalRule: boolean;
|
|
72
|
+
canTable: boolean;
|
|
73
|
+
canAddTableRowBefore: boolean;
|
|
74
|
+
canAddTableRowAfter: boolean;
|
|
75
|
+
canDeleteTableRow: boolean;
|
|
76
|
+
canAddTableColumnBefore: boolean;
|
|
77
|
+
canAddTableColumnAfter: boolean;
|
|
78
|
+
canDeleteTableColumn: boolean;
|
|
79
|
+
canDeleteTable: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface IMarkdownContextualToolbarPosition {
|
|
83
|
+
left: number;
|
|
84
|
+
top: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface IMarkdownTableDimensions {
|
|
88
|
+
rows: number;
|
|
89
|
+
cols: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface IMarkdownSelection {
|
|
93
|
+
from: number;
|
|
94
|
+
to: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
31
97
|
@Controller()
|
|
32
98
|
export class ControllerFormFieldMarkdown extends BeanControllerBase {
|
|
33
99
|
static $propsDefault = {};
|
|
@@ -36,16 +102,632 @@ export class ControllerFormFieldMarkdown extends BeanControllerBase {
|
|
|
36
102
|
editor?: Editor;
|
|
37
103
|
value = '';
|
|
38
104
|
readonly = false;
|
|
105
|
+
toolbarState: IMarkdownToolbarState = this._emptyToolbarState();
|
|
106
|
+
tableToolbarPosition?: IMarkdownContextualToolbarPosition;
|
|
107
|
+
codeBlockToolbarPosition?: IMarkdownContextualToolbarPosition;
|
|
108
|
+
readonly tablePickerMaxSize = 8;
|
|
109
|
+
tablePickerOpen = false;
|
|
110
|
+
tablePickerPreview: IMarkdownTableDimensions = { rows: 1, cols: 1 };
|
|
111
|
+
tablePickerActive: IMarkdownTableDimensions = { rows: 1, cols: 1 };
|
|
112
|
+
imageScene = markdownImageScene;
|
|
113
|
+
imageUploadError?: string;
|
|
114
|
+
private _imageUploadSelection?: IMarkdownSelection;
|
|
39
115
|
private _syncing = false;
|
|
40
116
|
private _setValue?: (value: string, disableNotifyChanged?: boolean) => void;
|
|
41
117
|
private _handleBlur?: () => void;
|
|
118
|
+
private _tableToolbarHost?: HTMLElement;
|
|
119
|
+
private _tableToolbarFocused = false;
|
|
120
|
+
private _codeBlockToolbarHost?: HTMLElement;
|
|
121
|
+
private _codeBlockToolbarFocused = false;
|
|
122
|
+
private _codeBlockToolbarHovered = false;
|
|
123
|
+
private _codeBlockToolbarInteracting = false;
|
|
124
|
+
private _contextualToolbarUpdateFrame?: number;
|
|
125
|
+
private _contextualToolbarViewportHandler?: () => void;
|
|
126
|
+
private _tablePickerTrigger?: HTMLButtonElement;
|
|
127
|
+
private _tablePickerRoot?: HTMLElement;
|
|
128
|
+
private _tablePickerPointerHandler?: (event: PointerEvent) => void;
|
|
129
|
+
private _tablePickerKeyHandler?: (event: KeyboardEvent) => void;
|
|
130
|
+
|
|
131
|
+
private _emptyToolbarState(): IMarkdownToolbarState {
|
|
132
|
+
return {
|
|
133
|
+
bold: false,
|
|
134
|
+
italic: false,
|
|
135
|
+
strike: false,
|
|
136
|
+
code: false,
|
|
137
|
+
highlight: false,
|
|
138
|
+
bulletList: false,
|
|
139
|
+
orderedList: false,
|
|
140
|
+
taskList: false,
|
|
141
|
+
blockquote: false,
|
|
142
|
+
codeBlock: false,
|
|
143
|
+
codeBlockLanguage: undefined,
|
|
144
|
+
link: false,
|
|
145
|
+
headingLevel: undefined,
|
|
146
|
+
canUndo: false,
|
|
147
|
+
canRedo: false,
|
|
148
|
+
canBold: false,
|
|
149
|
+
canItalic: false,
|
|
150
|
+
canStrike: false,
|
|
151
|
+
canCode: false,
|
|
152
|
+
canHighlight: false,
|
|
153
|
+
canBulletList: false,
|
|
154
|
+
canOrderedList: false,
|
|
155
|
+
canTaskList: false,
|
|
156
|
+
canBlockquote: false,
|
|
157
|
+
canCodeBlock: false,
|
|
158
|
+
canLink: false,
|
|
159
|
+
canHorizontalRule: false,
|
|
160
|
+
canTable: false,
|
|
161
|
+
canAddTableRowBefore: false,
|
|
162
|
+
canAddTableRowAfter: false,
|
|
163
|
+
canDeleteTableRow: false,
|
|
164
|
+
canAddTableColumnBefore: false,
|
|
165
|
+
canAddTableColumnAfter: false,
|
|
166
|
+
canDeleteTableColumn: false,
|
|
167
|
+
canDeleteTable: false,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private _refreshToolbarState() {
|
|
172
|
+
const editor = this.editor;
|
|
173
|
+
if (!editor) {
|
|
174
|
+
this.toolbarState = this._emptyToolbarState();
|
|
175
|
+
this._scheduleContextualToolbarPosition();
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const can = editor.can();
|
|
179
|
+
const heading = editor.getAttributes('heading').level;
|
|
180
|
+
const codeBlockActive = editor.isActive('codeBlock');
|
|
181
|
+
const codeBlockLanguage = editor.getAttributes('codeBlock').language;
|
|
182
|
+
const tableActive = editor.isActive('table');
|
|
183
|
+
this.toolbarState = {
|
|
184
|
+
bold: editor.isActive('bold'),
|
|
185
|
+
italic: editor.isActive('italic'),
|
|
186
|
+
strike: editor.isActive('strike'),
|
|
187
|
+
code: editor.isActive('code'),
|
|
188
|
+
highlight: editor.isActive('highlight'),
|
|
189
|
+
bulletList: editor.isActive('bulletList'),
|
|
190
|
+
orderedList: editor.isActive('orderedList'),
|
|
191
|
+
taskList: editor.isActive('taskList'),
|
|
192
|
+
blockquote: editor.isActive('blockquote'),
|
|
193
|
+
codeBlock: codeBlockActive,
|
|
194
|
+
codeBlockLanguage: codeBlockActive
|
|
195
|
+
? (getCodeBlockLanguage(codeBlockLanguage) ?? codeBlockLanguage ?? undefined)
|
|
196
|
+
: undefined,
|
|
197
|
+
link: editor.isActive('link'),
|
|
198
|
+
headingLevel:
|
|
199
|
+
heading === 1 ||
|
|
200
|
+
heading === 2 ||
|
|
201
|
+
heading === 3 ||
|
|
202
|
+
heading === 4 ||
|
|
203
|
+
heading === 5 ||
|
|
204
|
+
heading === 6
|
|
205
|
+
? heading
|
|
206
|
+
: undefined,
|
|
207
|
+
canUndo: can.undo(),
|
|
208
|
+
canRedo: can.redo(),
|
|
209
|
+
canBold: can.toggleBold(),
|
|
210
|
+
canItalic: can.toggleItalic(),
|
|
211
|
+
canStrike: can.toggleStrike(),
|
|
212
|
+
canCode: can.toggleCode(),
|
|
213
|
+
canHighlight: can.toggleHighlight(),
|
|
214
|
+
canBulletList: can.toggleBulletList(),
|
|
215
|
+
canOrderedList: can.toggleOrderedList(),
|
|
216
|
+
canTaskList: can.toggleTaskList(),
|
|
217
|
+
canBlockquote: can.toggleBlockquote(),
|
|
218
|
+
canCodeBlock: can.toggleCodeBlock(),
|
|
219
|
+
canLink:
|
|
220
|
+
editor.isActive('link') ||
|
|
221
|
+
(!editor.state.selection.empty && can.setLink({ href: 'https://example.com' })),
|
|
222
|
+
canHorizontalRule: can.setHorizontalRule(),
|
|
223
|
+
canTable: can.insertTable({ rows: 3, cols: 3, withHeaderRow: true }),
|
|
224
|
+
canAddTableRowBefore: tableActive ? can.addRowBefore() : false,
|
|
225
|
+
canAddTableRowAfter: tableActive ? can.addRowAfter() : false,
|
|
226
|
+
canDeleteTableRow: tableActive ? can.deleteRow() : false,
|
|
227
|
+
canAddTableColumnBefore: tableActive ? can.addColumnBefore() : false,
|
|
228
|
+
canAddTableColumnAfter: tableActive ? can.addColumnAfter() : false,
|
|
229
|
+
canDeleteTableColumn: tableActive ? can.deleteColumn() : false,
|
|
230
|
+
canDeleteTable: tableActive ? can.deleteTable() : false,
|
|
231
|
+
};
|
|
232
|
+
this._scheduleContextualToolbarPosition();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
private _runCommand(command: (editor: Editor) => boolean) {
|
|
236
|
+
if (this.readonly || !this.editor) return;
|
|
237
|
+
command(this.editor);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private _shouldShowTableToolbar() {
|
|
241
|
+
const editor = this.editor;
|
|
242
|
+
return !!(
|
|
243
|
+
editor &&
|
|
244
|
+
!this.readonly &&
|
|
245
|
+
editor.isEditable &&
|
|
246
|
+
editor.isActive('table') &&
|
|
247
|
+
(editor.view.hasFocus() || this._tableToolbarFocused)
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private _setTableToolbarPosition(position?: IMarkdownContextualToolbarPosition) {
|
|
252
|
+
if (
|
|
253
|
+
this.tableToolbarPosition?.left === position?.left &&
|
|
254
|
+
this.tableToolbarPosition?.top === position?.top
|
|
255
|
+
) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
this.tableToolbarPosition = position;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private _scheduleContextualToolbarPosition() {
|
|
262
|
+
if (!process.env.CLIENT || this._contextualToolbarUpdateFrame !== undefined) return;
|
|
263
|
+
this._contextualToolbarUpdateFrame = window.requestAnimationFrame(() => {
|
|
264
|
+
this._contextualToolbarUpdateFrame = undefined;
|
|
265
|
+
this._updateTableToolbarPosition();
|
|
266
|
+
this._updateCodeBlockToolbarPosition();
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private _shouldShowCodeBlockToolbar() {
|
|
271
|
+
const editor = this.editor;
|
|
272
|
+
const toolbarInteraction =
|
|
273
|
+
this._codeBlockToolbarFocused ||
|
|
274
|
+
this._codeBlockToolbarHovered ||
|
|
275
|
+
this._codeBlockToolbarInteracting;
|
|
276
|
+
return !!(
|
|
277
|
+
editor &&
|
|
278
|
+
!this.readonly &&
|
|
279
|
+
editor.isEditable &&
|
|
280
|
+
(editor.isActive('codeBlock') || (this.toolbarState.codeBlock && toolbarInteraction)) &&
|
|
281
|
+
(editor.view.hasFocus() || toolbarInteraction)
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private _setCodeBlockToolbarPosition(position?: IMarkdownContextualToolbarPosition) {
|
|
286
|
+
if (
|
|
287
|
+
this.codeBlockToolbarPosition?.left === position?.left &&
|
|
288
|
+
this.codeBlockToolbarPosition?.top === position?.top
|
|
289
|
+
) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
this.codeBlockToolbarPosition = position;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private _updateCodeBlockToolbarPosition() {
|
|
296
|
+
const editor = this.editor;
|
|
297
|
+
const host = this._codeBlockToolbarHost;
|
|
298
|
+
if (!editor || !host || !this._shouldShowCodeBlockToolbar()) {
|
|
299
|
+
this._setCodeBlockToolbarPosition();
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const codeBlock = findParentNodeClosestToPos(
|
|
303
|
+
editor.state.selection.$from,
|
|
304
|
+
node => node.type.name === 'codeBlock',
|
|
305
|
+
);
|
|
306
|
+
if (!codeBlock) {
|
|
307
|
+
this._setCodeBlockToolbarPosition();
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const dom = editor.view.nodeDOM(codeBlock.pos);
|
|
311
|
+
if (!(dom instanceof HTMLElement)) {
|
|
312
|
+
this._setCodeBlockToolbarPosition();
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const codeBlockElement = dom.matches('pre') ? dom : dom.querySelector('pre');
|
|
316
|
+
if (!codeBlockElement) {
|
|
317
|
+
this._setCodeBlockToolbarPosition();
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const codeBlockRect = codeBlockElement.getBoundingClientRect();
|
|
321
|
+
const hostRect = host.getBoundingClientRect();
|
|
322
|
+
this._setCodeBlockToolbarPosition({
|
|
323
|
+
left: codeBlockRect.left - hostRect.left + codeBlockRect.width / 2,
|
|
324
|
+
top: codeBlockRect.top - hostRect.top,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
public setCodeBlockToolbarHost(host: HTMLElement | null) {
|
|
329
|
+
if (this._codeBlockToolbarHost === host) return;
|
|
330
|
+
this._codeBlockToolbarHost = host ?? undefined;
|
|
331
|
+
this._scheduleContextualToolbarPosition();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
public setCodeBlockToolbarFocused(focused: boolean) {
|
|
335
|
+
this._codeBlockToolbarFocused = focused;
|
|
336
|
+
this._scheduleContextualToolbarPosition();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
public setCodeBlockToolbarHovered(hovered: boolean) {
|
|
340
|
+
this._codeBlockToolbarHovered = hovered;
|
|
341
|
+
this._scheduleContextualToolbarPosition();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
public setCodeBlockToolbarInteracting(interacting: boolean) {
|
|
345
|
+
this._codeBlockToolbarInteracting = interacting;
|
|
346
|
+
this._scheduleContextualToolbarPosition();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
private _updateTableToolbarPosition() {
|
|
350
|
+
const editor = this.editor;
|
|
351
|
+
const host = this._tableToolbarHost;
|
|
352
|
+
if (!editor || !host || !this._shouldShowTableToolbar()) {
|
|
353
|
+
this._setTableToolbarPosition();
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const table = findParentNodeClosestToPos(
|
|
357
|
+
editor.state.selection.$from,
|
|
358
|
+
node => node.type.name === 'table',
|
|
359
|
+
);
|
|
360
|
+
if (!table) {
|
|
361
|
+
this._setTableToolbarPosition();
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const dom = editor.view.nodeDOM(table.pos);
|
|
365
|
+
if (!(dom instanceof HTMLElement)) {
|
|
366
|
+
this._setTableToolbarPosition();
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const tableElement = dom.querySelector('table') ?? dom;
|
|
370
|
+
const tableRect = tableElement.getBoundingClientRect();
|
|
371
|
+
const hostRect = host.getBoundingClientRect();
|
|
372
|
+
this._setTableToolbarPosition({
|
|
373
|
+
left: tableRect.left - hostRect.left + tableRect.width / 2,
|
|
374
|
+
top: tableRect.top - hostRect.top - 8,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
public setTableToolbarHost(host: HTMLElement | null) {
|
|
379
|
+
if (this._tableToolbarHost === host) return;
|
|
380
|
+
this._tableToolbarHost = host ?? undefined;
|
|
381
|
+
this._scheduleContextualToolbarPosition();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
public setTableToolbarFocused(focused: boolean) {
|
|
385
|
+
this._tableToolbarFocused = focused;
|
|
386
|
+
this._scheduleContextualToolbarPosition();
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
private _isValidTablePickerDimensions(rows: number, cols: number) {
|
|
390
|
+
return (
|
|
391
|
+
Number.isInteger(rows) &&
|
|
392
|
+
Number.isInteger(cols) &&
|
|
393
|
+
rows >= 1 &&
|
|
394
|
+
rows <= this.tablePickerMaxSize &&
|
|
395
|
+
cols >= 1 &&
|
|
396
|
+
cols <= this.tablePickerMaxSize
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private _setTablePickerActive(rows: number, cols: number, focus = false) {
|
|
401
|
+
if (!this._isValidTablePickerDimensions(rows, cols)) return;
|
|
402
|
+
this.tablePickerActive = { rows, cols };
|
|
403
|
+
this.tablePickerPreview = { rows, cols };
|
|
404
|
+
if (focus && this._tablePickerRoot) {
|
|
405
|
+
const cell = this._tablePickerRoot.querySelector<HTMLButtonElement>(
|
|
406
|
+
`[data-table-picker-cell="${rows}-${cols}"]`,
|
|
407
|
+
);
|
|
408
|
+
cell?.focus();
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
public setTablePickerTrigger(trigger: HTMLButtonElement | null) {
|
|
413
|
+
this._tablePickerTrigger = trigger ?? undefined;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
public setTablePickerRoot(root: HTMLElement | null) {
|
|
417
|
+
this._tablePickerRoot = root ?? undefined;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
public openTablePicker() {
|
|
421
|
+
if (this.readonly || !this.toolbarState.canTable) return;
|
|
422
|
+
this.tablePickerOpen = true;
|
|
423
|
+
this._setTablePickerActive(1, 1);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
public closeTablePicker(options: { restoreFocus?: boolean } = {}) {
|
|
427
|
+
if (!this.tablePickerOpen) return;
|
|
428
|
+
this.tablePickerOpen = false;
|
|
429
|
+
this._setTablePickerActive(1, 1);
|
|
430
|
+
if (options.restoreFocus) {
|
|
431
|
+
this._tablePickerTrigger?.focus();
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
public toggleTablePicker() {
|
|
436
|
+
if (this.tablePickerOpen) {
|
|
437
|
+
this.closeTablePicker({ restoreFocus: true });
|
|
438
|
+
} else {
|
|
439
|
+
this.openTablePicker();
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
public setTablePickerPreview(rows: number, cols: number) {
|
|
444
|
+
this._setTablePickerActive(rows, cols);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
public selectTablePickerSize(rows: number, cols: number) {
|
|
448
|
+
if (
|
|
449
|
+
!this.tablePickerOpen ||
|
|
450
|
+
this.readonly ||
|
|
451
|
+
!this.toolbarState.canTable ||
|
|
452
|
+
!this._isValidTablePickerDimensions(rows, cols)
|
|
453
|
+
) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
this.closeTablePicker();
|
|
457
|
+
this.insertTable(rows, cols);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
public handleTablePickerKeydown(event: KeyboardEvent) {
|
|
461
|
+
if (!this.tablePickerOpen) return;
|
|
462
|
+
const { rows, cols } = this.tablePickerActive;
|
|
463
|
+
let nextRows = rows;
|
|
464
|
+
let nextCols = cols;
|
|
465
|
+
switch (event.key) {
|
|
466
|
+
case 'ArrowUp':
|
|
467
|
+
nextRows = Math.max(1, rows - 1);
|
|
468
|
+
break;
|
|
469
|
+
case 'ArrowDown':
|
|
470
|
+
nextRows = Math.min(this.tablePickerMaxSize, rows + 1);
|
|
471
|
+
break;
|
|
472
|
+
case 'ArrowLeft':
|
|
473
|
+
nextCols = Math.max(1, cols - 1);
|
|
474
|
+
break;
|
|
475
|
+
case 'ArrowRight':
|
|
476
|
+
nextCols = Math.min(this.tablePickerMaxSize, cols + 1);
|
|
477
|
+
break;
|
|
478
|
+
case 'Home':
|
|
479
|
+
nextCols = 1;
|
|
480
|
+
break;
|
|
481
|
+
case 'End':
|
|
482
|
+
nextCols = this.tablePickerMaxSize;
|
|
483
|
+
break;
|
|
484
|
+
case 'Enter':
|
|
485
|
+
case ' ': {
|
|
486
|
+
event.preventDefault();
|
|
487
|
+
this.selectTablePickerSize(rows, cols);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
case 'Escape':
|
|
491
|
+
event.preventDefault();
|
|
492
|
+
this.closeTablePicker({ restoreFocus: true });
|
|
493
|
+
return;
|
|
494
|
+
case 'Tab':
|
|
495
|
+
this.closeTablePicker();
|
|
496
|
+
return;
|
|
497
|
+
default:
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
event.preventDefault();
|
|
501
|
+
this._setTablePickerActive(nextRows, nextCols, true);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
public undo() {
|
|
505
|
+
this._runCommand(editor => editor.chain().focus().undo().run());
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
public redo() {
|
|
509
|
+
this._runCommand(editor => editor.chain().focus().redo().run());
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
public setParagraph() {
|
|
513
|
+
this._runCommand(editor => editor.chain().focus().setParagraph().run());
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
public setHeading(level: IMarkdownHeadingLevel) {
|
|
517
|
+
this._runCommand(editor => editor.chain().focus().toggleHeading({ level }).run());
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
public toggleBold() {
|
|
521
|
+
this._runCommand(editor => editor.chain().focus().toggleBold().run());
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
public toggleItalic() {
|
|
525
|
+
this._runCommand(editor => editor.chain().focus().toggleItalic().run());
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
public toggleStrike() {
|
|
529
|
+
this._runCommand(editor => editor.chain().focus().toggleStrike().run());
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
public toggleCode() {
|
|
533
|
+
this._runCommand(editor => editor.chain().focus().toggleCode().run());
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
public async editLink() {
|
|
537
|
+
const editor = this.editor;
|
|
538
|
+
if (this.readonly || !editor) return;
|
|
539
|
+
const selection = editor.state.selection;
|
|
540
|
+
const linkActive = editor.isActive('link');
|
|
541
|
+
const canEditLink = linkActive
|
|
542
|
+
? editor.can().unsetLink()
|
|
543
|
+
: !selection.empty && editor.can().setLink({ href: 'https://example.com' });
|
|
544
|
+
if (!canEditLink) return;
|
|
545
|
+
|
|
546
|
+
const href = editor.getAttributes('link').href ?? '';
|
|
547
|
+
const result = await this.$appModal.prompt({
|
|
548
|
+
icon: ':editor:insert-link-outline',
|
|
549
|
+
title: this.scope.locale.Link(),
|
|
550
|
+
text: this.scope.locale.LinkUrl(),
|
|
551
|
+
defaultValue: href,
|
|
552
|
+
});
|
|
553
|
+
if (this.readonly || this.editor !== editor || result === undefined) return;
|
|
554
|
+
|
|
555
|
+
const value = result.trim();
|
|
556
|
+
const chain = editor.chain().focus().setTextSelection({
|
|
557
|
+
from: selection.from,
|
|
558
|
+
to: selection.to,
|
|
559
|
+
});
|
|
560
|
+
if (linkActive) {
|
|
561
|
+
chain.extendMarkRange('link');
|
|
562
|
+
}
|
|
563
|
+
if (!value) {
|
|
564
|
+
if (linkActive) chain.unsetLink().run();
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
chain.setLink({ href: value }).run();
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
public toggleHighlight() {
|
|
571
|
+
this._runCommand(editor => editor.chain().focus().toggleHighlight().run());
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
public toggleBulletList() {
|
|
575
|
+
this._runCommand(editor => editor.chain().focus().toggleBulletList().run());
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
public toggleOrderedList() {
|
|
579
|
+
this._runCommand(editor => editor.chain().focus().toggleOrderedList().run());
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
public toggleTaskList() {
|
|
583
|
+
this._runCommand(editor => editor.chain().focus().toggleTaskList().run());
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
public toggleBlockquote() {
|
|
587
|
+
this._runCommand(editor => editor.chain().focus().toggleBlockquote().run());
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
public toggleCodeBlock() {
|
|
591
|
+
this._runCommand(editor => editor.chain().focus().toggleCodeBlock().run());
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
public setCodeBlockLanguage(language?: string) {
|
|
595
|
+
if (language && !isCodeBlockLanguage(language)) return;
|
|
596
|
+
this._runCommand(editor => {
|
|
597
|
+
if (!editor.isActive('codeBlock')) return false;
|
|
598
|
+
return editor
|
|
599
|
+
.chain()
|
|
600
|
+
.focus()
|
|
601
|
+
.updateAttributes('codeBlock', { language: language || null })
|
|
602
|
+
.run();
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
public setHorizontalRule() {
|
|
607
|
+
this._runCommand(editor => editor.chain().focus().setHorizontalRule().run());
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
public insertTable(rows: number, cols: number) {
|
|
611
|
+
if (!this._isValidTablePickerDimensions(rows, cols)) return;
|
|
612
|
+
this._runCommand(editor =>
|
|
613
|
+
editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run(),
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
public addTableRowBefore() {
|
|
618
|
+
this._runCommand(editor => editor.chain().focus().addRowBefore().run());
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
public addTableRowAfter() {
|
|
622
|
+
this._runCommand(editor => editor.chain().focus().addRowAfter().run());
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
public deleteTableRow() {
|
|
626
|
+
this._runCommand(editor => editor.chain().focus().deleteRow().run());
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
public addTableColumnBefore() {
|
|
630
|
+
this._runCommand(editor => editor.chain().focus().addColumnBefore().run());
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
public addTableColumnAfter() {
|
|
634
|
+
this._runCommand(editor => editor.chain().focus().addColumnAfter().run());
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
public deleteTableColumn() {
|
|
638
|
+
this._runCommand(editor => editor.chain().focus().deleteColumn().run());
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
public deleteTable() {
|
|
642
|
+
this._runCommand(editor => editor.chain().focus().deleteTable().run());
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
public beginImageUpload(chooseFiles: () => void) {
|
|
646
|
+
const editor = this.editor;
|
|
647
|
+
if (this.readonly || !editor || !editor.isEditable) return;
|
|
648
|
+
this._imageUploadSelection = {
|
|
649
|
+
from: editor.state.selection.from,
|
|
650
|
+
to: editor.state.selection.to,
|
|
651
|
+
};
|
|
652
|
+
this.imageUploadError = undefined;
|
|
653
|
+
chooseFiles();
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
public handleImageUploaded(result: IImageUploaderResult) {
|
|
657
|
+
const editor = this.editor;
|
|
658
|
+
const selection = this._imageUploadSelection;
|
|
659
|
+
const src = this._resolveImageUrl(result.url);
|
|
660
|
+
if (!editor || this.readonly || !editor.isEditable || !selection || !src) {
|
|
661
|
+
this.imageUploadError = this.scope.locale.ImageUploadInvalidUrl();
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
const { from, to } = selection;
|
|
665
|
+
if (from < 0 || to < from || to > editor.state.doc.content.size) {
|
|
666
|
+
this.imageUploadError = this.scope.locale.ImageUploadInvalidUrl();
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
const chain = editor.chain().focus().setTextSelection({ from, to });
|
|
670
|
+
if (!chain.setImage({ src, ...(result.filename ? { alt: result.filename } : {}) }).run()) {
|
|
671
|
+
this.imageUploadError = this.scope.locale.ImageUploadFailed();
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
this._imageUploadSelection = undefined;
|
|
675
|
+
this.imageUploadError = undefined;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
public handleImageUploadError(error: Error) {
|
|
679
|
+
this.imageUploadError = error.message || this.scope.locale.ImageUploadFailed();
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
private _resolveImageUrl(url: string | undefined) {
|
|
683
|
+
if (!url) return undefined;
|
|
684
|
+
const resolved =
|
|
685
|
+
url.startsWith('/api/') && this.sys.config.api.baseURL
|
|
686
|
+
? `${this.sys.config.api.baseURL.replace(/\/$/, '')}${url}`
|
|
687
|
+
: url;
|
|
688
|
+
try {
|
|
689
|
+
const parsed = new URL(resolved);
|
|
690
|
+
return parsed.protocol === 'http:' || parsed.protocol === 'https:'
|
|
691
|
+
? parsed.toString()
|
|
692
|
+
: undefined;
|
|
693
|
+
} catch {
|
|
694
|
+
return undefined;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
42
697
|
|
|
43
698
|
protected async __init__() {
|
|
44
699
|
this.$controllerMounted(() => {
|
|
700
|
+
this._contextualToolbarViewportHandler = () => {
|
|
701
|
+
if (
|
|
702
|
+
this._shouldShowTableToolbar() ||
|
|
703
|
+
this._shouldShowCodeBlockToolbar() ||
|
|
704
|
+
this._codeBlockToolbarHovered
|
|
705
|
+
) {
|
|
706
|
+
this._scheduleContextualToolbarPosition();
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
this._tablePickerPointerHandler = event => {
|
|
710
|
+
const target = event.target;
|
|
711
|
+
if (
|
|
712
|
+
target instanceof Node &&
|
|
713
|
+
(this._tablePickerTrigger?.contains(target) || this._tablePickerRoot?.contains(target))
|
|
714
|
+
) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
this.closeTablePicker();
|
|
718
|
+
};
|
|
719
|
+
this._tablePickerKeyHandler = event => {
|
|
720
|
+
this.handleTablePickerKeydown(event);
|
|
721
|
+
};
|
|
722
|
+
window.addEventListener('resize', this._contextualToolbarViewportHandler);
|
|
723
|
+
window.addEventListener('scroll', this._contextualToolbarViewportHandler, true);
|
|
724
|
+
document.addEventListener('pointerdown', this._tablePickerPointerHandler);
|
|
725
|
+
document.addEventListener('keydown', this._tablePickerKeyHandler);
|
|
45
726
|
this.editor = new Editor({
|
|
46
727
|
extensions: [
|
|
47
728
|
Markdown,
|
|
48
|
-
StarterKit,
|
|
729
|
+
StarterKit.configure({ codeBlock: false }),
|
|
730
|
+
CodeBlockLowlight.configure({ lowlight }),
|
|
49
731
|
TaskList,
|
|
50
732
|
TaskItem.configure({ nested: true }),
|
|
51
733
|
Image,
|
|
@@ -61,13 +743,25 @@ export class ControllerFormFieldMarkdown extends BeanControllerBase {
|
|
|
61
743
|
},
|
|
62
744
|
},
|
|
63
745
|
onUpdate: ({ editor }) => {
|
|
64
|
-
if (this._syncing)
|
|
65
|
-
|
|
746
|
+
if (!this._syncing) {
|
|
747
|
+
this._setValue?.(editor.getMarkdown());
|
|
748
|
+
}
|
|
749
|
+
},
|
|
750
|
+
onSelectionUpdate: () => {
|
|
751
|
+
this._refreshToolbarState();
|
|
752
|
+
},
|
|
753
|
+
onTransaction: () => {
|
|
754
|
+
this._refreshToolbarState();
|
|
755
|
+
},
|
|
756
|
+
onFocus: () => {
|
|
757
|
+
this._scheduleContextualToolbarPosition();
|
|
66
758
|
},
|
|
67
759
|
onBlur: () => {
|
|
68
760
|
this._handleBlur?.();
|
|
761
|
+
this._scheduleContextualToolbarPosition();
|
|
69
762
|
},
|
|
70
763
|
});
|
|
764
|
+
this._refreshToolbarState();
|
|
71
765
|
this.$watch(
|
|
72
766
|
() => this.value,
|
|
73
767
|
value => {
|
|
@@ -81,12 +775,33 @@ export class ControllerFormFieldMarkdown extends BeanControllerBase {
|
|
|
81
775
|
() => this.readonly,
|
|
82
776
|
readonly => {
|
|
83
777
|
this.editor?.setEditable(!readonly);
|
|
778
|
+
if (readonly) this.closeTablePicker();
|
|
779
|
+
this._refreshToolbarState();
|
|
84
780
|
},
|
|
85
781
|
);
|
|
86
782
|
});
|
|
87
783
|
}
|
|
88
784
|
|
|
89
785
|
protected __dispose__() {
|
|
786
|
+
if (this._contextualToolbarUpdateFrame !== undefined) {
|
|
787
|
+
window.cancelAnimationFrame(this._contextualToolbarUpdateFrame);
|
|
788
|
+
}
|
|
789
|
+
if (this._contextualToolbarViewportHandler) {
|
|
790
|
+
window.removeEventListener('resize', this._contextualToolbarViewportHandler);
|
|
791
|
+
window.removeEventListener('scroll', this._contextualToolbarViewportHandler, true);
|
|
792
|
+
}
|
|
793
|
+
if (this._tablePickerPointerHandler) {
|
|
794
|
+
document.removeEventListener('pointerdown', this._tablePickerPointerHandler);
|
|
795
|
+
}
|
|
796
|
+
if (this._tablePickerKeyHandler) {
|
|
797
|
+
document.removeEventListener('keydown', this._tablePickerKeyHandler);
|
|
798
|
+
}
|
|
799
|
+
this._tablePickerTrigger = undefined;
|
|
800
|
+
this._tablePickerRoot = undefined;
|
|
801
|
+
this._tableToolbarHost = undefined;
|
|
802
|
+
this._codeBlockToolbarHost = undefined;
|
|
803
|
+
this._setTableToolbarPosition();
|
|
804
|
+
this._setCodeBlockToolbarPosition();
|
|
90
805
|
this.editor?.destroy();
|
|
91
806
|
}
|
|
92
807
|
|
|
@@ -96,8 +811,11 @@ export class ControllerFormFieldMarkdown extends BeanControllerBase {
|
|
|
96
811
|
) {
|
|
97
812
|
const value = propsBucket.value ?? '';
|
|
98
813
|
const readonly = propsBucket.readonly ?? false;
|
|
814
|
+
const options = propsBucket.options ?? {};
|
|
815
|
+
const imageScene = options.imageScene ?? markdownImageScene;
|
|
99
816
|
if (this.value !== value) this.value = value;
|
|
100
817
|
if (this.readonly !== readonly) this.readonly = readonly;
|
|
818
|
+
if (this.imageScene !== imageScene) this.imageScene = imageScene;
|
|
101
819
|
if (this._setValue && this._handleBlur) return;
|
|
102
820
|
this._setValue = (value, disableNotifyChanged) => {
|
|
103
821
|
if (this.readonly) return;
|