@mgdis/mg-components 6.20.0 → 6.22.0
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/dist/cjs/{index-TUElr4Wv.js → index-BxB20_Vw.js} +54 -0
- package/dist/cjs/{index-Bp6wZzau.js → index-C2viYwrR.js} +1761 -80
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/mg-action-more_37.cjs.entry.js +761 -371
- package/dist/cjs/mg-alert.cjs.entry.js +17 -9
- package/dist/cjs/mg-breadcrumb.cjs.entry.js +63 -0
- package/dist/cjs/mg-components.cjs.js +3 -3
- package/dist/cjs/mg-fieldset.cjs.entry.js +24 -7
- package/dist/cjs/mg-input-combobox.cjs.entry.js +49 -19
- package/dist/cjs/mg-input-file.cjs.entry.js +384 -0
- package/dist/cjs/mg-input-rich-text-editor.cjs.entry.js +40096 -15821
- package/dist/cjs/{mg-input.conf-D7jmj_lh.js → mg-input.conf-BNpeATnq.js} +7 -0
- package/dist/cjs/mg-loader.cjs.entry.js +8 -6
- package/dist/cjs/mg-progress.cjs.entry.js +19 -9
- package/dist/collection/assets/icons/index.js +1 -1
- package/dist/collection/collection-manifest.json +5 -3
- package/dist/collection/components/atoms/internals/mg-character-left/mg-character-left.js +1 -1
- package/dist/collection/components/atoms/internals/mg-input-title/mg-input-title.js +1 -1
- package/dist/collection/components/atoms/mg-card/mg-card.js +1 -1
- package/dist/collection/components/atoms/mg-divider/mg-divider.js +1 -1
- package/dist/collection/components/atoms/mg-icon/mg-icon.js +2 -2
- package/dist/collection/components/atoms/mg-tag/mg-tag.js +1 -1
- package/dist/collection/components/atoms/mg-tooltip/mg-tooltip-content/mg-tooltip-content.js +1 -1
- package/dist/collection/components/atoms/mg-tooltip/mg-tooltip.js +1 -1
- package/dist/collection/components/molecules/inputs/mg-input/mg-input.conf.js +6 -0
- package/dist/collection/components/molecules/inputs/mg-input/mg-input.js +1 -1
- package/dist/collection/components/molecules/inputs/mg-input-checkbox/MgInputCheckboxList.js +2 -2
- package/dist/collection/components/molecules/inputs/mg-input-checkbox/mg-input-checkbox-paginated/mg-input-checkbox-paginated.js +2 -2
- package/dist/collection/components/molecules/inputs/mg-input-checkbox/mg-input-checkbox.js +1 -1
- package/dist/collection/components/molecules/inputs/mg-input-combobox/mg-input-combobox.js +1 -1
- package/dist/collection/components/molecules/inputs/mg-input-date/mg-input-date.js +1 -1
- package/dist/collection/components/molecules/inputs/mg-input-file/mg-input-file.js +877 -0
- package/dist/collection/components/molecules/inputs/mg-input-numeric/mg-input-numeric.js +1 -1
- package/dist/collection/components/molecules/inputs/mg-input-password/mg-input-password.js +2 -2
- package/dist/collection/components/molecules/inputs/mg-input-radio/mg-input-radio.js +1 -1
- package/dist/collection/components/molecules/inputs/mg-input-rich-text-editor/editor/custom-properties.scss +212 -0
- package/dist/collection/components/molecules/inputs/mg-input-rich-text-editor/editor/editor.conf.js +1 -0
- package/dist/collection/components/molecules/inputs/mg-input-rich-text-editor/editor/index.js +300 -94
- package/dist/collection/components/molecules/inputs/mg-input-rich-text-editor/mg-input-rich-text-editor.conf.js +29 -0
- package/dist/collection/components/molecules/inputs/mg-input-rich-text-editor/mg-input-rich-text-editor.js +163 -53
- package/dist/collection/components/molecules/inputs/mg-input-select/mg-input-select.js +1 -1
- package/dist/collection/components/molecules/inputs/mg-input-text/mg-input-text.js +2 -2
- package/dist/collection/components/molecules/inputs/mg-input-textarea/mg-input-textarea.js +1 -1
- package/dist/collection/components/molecules/inputs/mg-input-toggle/mg-input-toggle.js +1 -1
- package/dist/collection/components/molecules/internals/mg-item-more/mg-item-more.js +3 -36
- package/dist/collection/components/molecules/menus/mg-menu/mg-menu.conf.js +0 -4
- package/dist/collection/components/molecules/menus/mg-menu/mg-menu.js +21 -43
- package/dist/collection/components/molecules/menus/mg-menu-item/mg-menu-item.js +65 -38
- package/dist/collection/components/molecules/mg-action-more/mg-action-more.js +2 -2
- package/dist/collection/components/molecules/mg-alert/mg-alert.js +1 -1
- package/dist/collection/components/molecules/mg-breadcrumb/mg-breadcrumb.conf.js +1 -0
- package/dist/collection/components/molecules/mg-breadcrumb/mg-breadcrumb.js +92 -0
- package/dist/collection/components/molecules/mg-details/mg-details.js +1 -1
- package/dist/collection/components/molecules/mg-fieldset/mg-fieldset.js +12 -3
- package/dist/collection/components/molecules/mg-form/mg-form.js +12 -2
- package/dist/collection/components/molecules/mg-illustrated-message/mg-illustrated-message.js +1 -1
- package/dist/collection/components/molecules/mg-loader/mg-loader.js +1 -1
- package/dist/collection/components/molecules/mg-message/mg-message.js +2 -2
- package/dist/collection/components/molecules/mg-modal/mg-modal.js +2 -2
- package/dist/collection/components/molecules/mg-panel/mg-panel.js +1 -1
- package/dist/collection/components/molecules/mg-popover/mg-popover-content/mg-popover-content.js +1 -1
- package/dist/collection/components/molecules/mg-popover/mg-popover.js +1 -1
- package/dist/collection/components/molecules/mg-progress/mg-progress.js +1 -1
- package/dist/collection/components/molecules/mg-skip-links/mg-skip-links.js +1 -1
- package/dist/collection/components/molecules/mg-table/mg-table.js +37 -0
- package/dist/collection/components/molecules/mg-tabs/mg-tabs.conf.js +0 -4
- package/dist/collection/components/molecules/mg-tabs/mg-tabs.js +13 -33
- package/dist/collection/locales/en/messages.json +14 -0
- package/dist/collection/locales/fr/messages.json +14 -0
- package/dist/components/events.utils.js +1 -23
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +1 -1
- package/dist/components/index2.js +1 -408
- package/dist/components/index3.js +1 -476
- package/dist/components/mg-action-more.js +1 -254
- package/dist/components/mg-alert.js +1 -224
- package/dist/components/mg-badge.js +1 -6
- package/dist/components/mg-badge2.js +1 -113
- package/dist/components/mg-breadcrumb.d.ts +11 -0
- package/dist/components/mg-breadcrumb.js +1 -0
- package/dist/components/mg-button.js +1 -6
- package/dist/components/mg-button2.js +1 -275
- package/dist/components/mg-card.js +1 -6
- package/dist/components/mg-card2.js +1 -92
- package/dist/components/mg-character-left.js +1 -6
- package/dist/components/mg-character-left2.js +1 -75
- package/dist/components/mg-details.js +1 -104
- package/dist/components/mg-divider.js +1 -6
- package/dist/components/mg-divider2.js +1 -49
- package/dist/components/mg-fieldset.js +1 -209
- package/dist/components/mg-form.js +1 -302
- package/dist/components/mg-icon.js +1 -6
- package/dist/components/mg-icon2.js +1 -188
- package/dist/components/mg-illustrated-message.js +1 -129
- package/dist/components/mg-input-checkbox-paginated.js +1 -6
- package/dist/components/mg-input-checkbox-paginated2.js +1 -135
- package/dist/components/mg-input-checkbox.js +1 -762
- package/dist/components/mg-input-combobox.js +1 -940
- package/dist/components/mg-input-date.js +1 -463
- package/dist/components/mg-input-file.d.ts +11 -0
- package/dist/components/mg-input-file.js +1 -0
- package/dist/components/mg-input-numeric.js +1 -592
- package/dist/components/mg-input-password.js +1 -360
- package/dist/components/mg-input-radio.js +1 -392
- package/dist/components/mg-input-rich-text-editor.js +1663 -16797
- package/dist/components/mg-input-select.js +1 -6
- package/dist/components/mg-input-select2.js +1 -481
- package/dist/components/mg-input-text.js +1 -6
- package/dist/components/mg-input-text2.js +1 -514
- package/dist/components/mg-input-textarea.js +1 -412
- package/dist/components/mg-input-title.js +1 -6
- package/dist/components/mg-input-title2.js +1 -81
- package/dist/components/mg-input-toggle.js +1 -371
- package/dist/components/mg-input.conf.js +1 -0
- package/dist/components/mg-input.js +1 -6
- package/dist/components/mg-input2.js +1 -459
- package/dist/components/mg-item-more.js +1 -6
- package/dist/components/mg-item-more2.js +1 -505
- package/dist/components/mg-loader.js +1 -6
- package/dist/components/mg-loader2.js +1 -74
- package/dist/components/mg-menu-item.js +1 -6
- package/dist/components/mg-menu-item2.js +1 -485
- package/dist/components/mg-menu.js +1 -6
- package/dist/components/mg-message.js +1 -6
- package/dist/components/mg-message2.js +1 -146
- package/dist/components/mg-modal.js +1 -184
- package/dist/components/mg-pagination.js +1 -234
- package/dist/components/mg-panel.js +1 -350
- package/dist/components/mg-popover-content.js +1 -6
- package/dist/components/mg-popover-content2.js +1 -96
- package/dist/components/mg-popover.js +1 -6
- package/dist/components/mg-popover2.js +1 -312
- package/dist/components/mg-progress.js +1 -123
- package/dist/components/mg-skip-links.js +1 -78
- package/dist/components/mg-table.js +1 -319
- package/dist/components/mg-tabs.js +1 -6
- package/dist/components/mg-tabs2.js +1 -315
- package/dist/components/mg-tag.js +1 -133
- package/dist/components/mg-tooltip-content.js +1 -6
- package/dist/components/mg-tooltip-content2.js +1 -42
- package/dist/components/mg-tooltip.js +1 -6
- package/dist/components/mg-tooltip2.js +1 -421
- package/dist/components/popper.js +1 -1790
- package/dist/esm/{index-BluNKQ3L.js → index-DFwaH2hS.js} +1761 -80
- package/dist/esm/{index-CNX6lq0x.js → index-DSEloqPn.js} +54 -1
- package/dist/esm/loader.js +3 -3
- package/dist/esm/mg-action-more_37.entry.js +761 -371
- package/dist/esm/mg-alert.entry.js +17 -9
- package/dist/esm/mg-breadcrumb.entry.js +61 -0
- package/dist/esm/mg-components.js +4 -4
- package/dist/esm/mg-fieldset.entry.js +24 -7
- package/dist/esm/mg-input-combobox.entry.js +49 -19
- package/dist/esm/mg-input-file.entry.js +382 -0
- package/dist/esm/mg-input-rich-text-editor.entry.js +40083 -15808
- package/dist/esm/{mg-input.conf-DDnrE1hD.js → mg-input.conf-C9rmNQIL.js} +7 -1
- package/dist/esm/mg-loader.entry.js +8 -6
- package/dist/esm/mg-progress.entry.js +19 -9
- package/dist/mg-components/locales/en/messages.json +14 -0
- package/dist/mg-components/locales/fr/messages.json +14 -0
- package/dist/mg-components/mg-components.esm.js +1 -1
- package/dist/mg-components/p-0482eb9f.entry.js +1 -0
- package/dist/mg-components/{p-7132bca1.entry.js → p-2ee92f38.entry.js} +1 -1
- package/dist/mg-components/{p-916dfb6d.entry.js → p-3b653226.entry.js} +1 -1
- package/dist/mg-components/p-5745fbea.entry.js +1 -0
- package/dist/mg-components/p-C9rmNQIL.js +1 -0
- package/dist/mg-components/p-DFwaH2hS.js +2 -0
- package/dist/mg-components/p-DSEloqPn.js +1 -0
- package/dist/mg-components/p-adec7fcc.entry.js +1 -0
- package/dist/mg-components/p-c63f97f0.entry.js +1658 -0
- package/dist/mg-components/p-c8c8cfc1.entry.js +1 -0
- package/dist/mg-components/p-cc793586.entry.js +1 -0
- package/dist/mg-components/p-d5e69c95.entry.js +1 -0
- package/dist/types/assets/icons/index.d.ts +2 -0
- package/dist/types/components/molecules/inputs/mg-input/mg-input.conf.d.ts +7 -1
- package/dist/types/components/molecules/inputs/mg-input-file/mg-input-file.d.ts +210 -0
- package/dist/types/components/molecules/inputs/mg-input-rich-text-editor/editor/editor.conf.d.ts +17 -0
- package/dist/types/components/molecules/inputs/mg-input-rich-text-editor/editor/index.d.ts +27 -19
- package/dist/types/components/molecules/inputs/mg-input-rich-text-editor/mg-input-rich-text-editor.conf.d.ts +3 -0
- package/dist/types/components/molecules/inputs/mg-input-rich-text-editor/mg-input-rich-text-editor.d.ts +50 -16
- package/dist/types/components/molecules/internals/mg-item-more/mg-item-more.conf.d.ts +0 -5
- package/dist/types/components/molecules/internals/mg-item-more/mg-item-more.d.ts +1 -6
- package/dist/types/components/molecules/menus/mg-menu/mg-menu.conf.d.ts +2 -10
- package/dist/types/components/molecules/menus/mg-menu/mg-menu.d.ts +7 -7
- package/dist/types/components/molecules/menus/mg-menu-item/mg-menu-item.d.ts +12 -8
- package/dist/types/components/molecules/mg-breadcrumb/mg-breadcrumb.conf.d.ts +18 -0
- package/dist/types/components/molecules/mg-breadcrumb/mg-breadcrumb.d.ts +34 -0
- package/dist/types/components/molecules/mg-table/mg-table.d.ts +6 -0
- package/dist/types/components/molecules/mg-tabs/mg-tabs.conf.d.ts +1 -9
- package/dist/types/components/molecules/mg-tabs/mg-tabs.d.ts +4 -4
- package/dist/types/components.d.ts +302 -46
- package/dist/types/stencil-public-runtime.d.ts +76 -12
- package/ide/intellij/web-types.json +483 -51
- package/ide/vscode/css-custom-data.json +10 -2
- package/ide/vscode/html-custom-data.json +445 -104
- package/package.json +24 -23
- package/dist/collection/components/molecules/inputs/mg-input-rich-text-editor/editor/editor.scss +0 -1
- package/dist/mg-components/p-016bd291.entry.js +0 -1
- package/dist/mg-components/p-186a6139.entry.js +0 -1
- package/dist/mg-components/p-BluNKQ3L.js +0 -2
- package/dist/mg-components/p-CNX6lq0x.js +0 -1
- package/dist/mg-components/p-DDnrE1hD.js +0 -1
- package/dist/mg-components/p-cd061b38.entry.js +0 -1
- package/dist/mg-components/p-d2c72f09.entry.js +0 -1
- package/dist/mg-components/p-d5a276eb.entry.js +0 -1
- package/ide/vscode/custom.css-data.json +0 -425
package/dist/collection/components/molecules/inputs/mg-input-rich-text-editor/editor/index.js
CHANGED
|
@@ -1,109 +1,315 @@
|
|
|
1
1
|
import { isValidString } from "@mgdis/core-ui-helpers/dist/utils";
|
|
2
|
-
import
|
|
2
|
+
import { Jodit } from "jodit";
|
|
3
|
+
// Import Jodit plugins
|
|
4
|
+
import "jodit/esm/plugins/select/select.js";
|
|
5
|
+
import "jodit/esm/plugins/resizer/resizer.js";
|
|
6
|
+
import "jodit/esm/plugins/table/table.js";
|
|
7
|
+
import "jodit/esm/plugins/select-cells/select-cells.js";
|
|
8
|
+
import "jodit/esm/plugins/resize-cells/resize-cells.js";
|
|
9
|
+
import "jodit/esm/plugins/table-keyboard-navigation/table-keyboard-navigation.js";
|
|
10
|
+
import "jodit/esm/plugins/inline-popup/inline-popup.js";
|
|
11
|
+
import "jodit/esm/plugins/clean-html/clean-html.js";
|
|
12
|
+
import "jodit/esm/plugins/delete/delete.js";
|
|
13
|
+
import "jodit/esm/plugins/file/file.js";
|
|
14
|
+
import "jodit/esm/plugins/print/print.js";
|
|
15
|
+
import "jodit/esm/plugins/source/source.js";
|
|
16
|
+
const DEFAULT_ROWS = 5;
|
|
3
17
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @
|
|
18
|
+
* Converts rows count to Jodit editor min-height CSS value.
|
|
19
|
+
* Uses CSS variables --mg-b-font-size and --mg-b-line-height to compute height.
|
|
20
|
+
* @param rows - Number of rows
|
|
21
|
+
* @returns Height as CSS value in pixels (wysiwyg area + toolbar height of 40px + 2px borders)
|
|
22
|
+
* @throws If required CSS variables (--mg-b-font-size, --mg-b-line-height) are missing
|
|
7
23
|
*/
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
if (typeof document.createElement('div').attachShadow({ mode: 'open' }).getSelection === 'function') {
|
|
17
|
-
// In Chromium, the shadow root has a getSelection function which returns the range
|
|
18
|
-
return rootNode.getSelection().getRangeAt(0);
|
|
19
|
-
}
|
|
20
|
-
const selection = window.getSelection();
|
|
21
|
-
// Webkit range retrieval is done with getComposedRanges (see: https://bugs.webkit.org/show_bug.cgi?id=163921)
|
|
22
|
-
if (typeof selection.getComposedRanges === 'function') {
|
|
23
|
-
return selection.getComposedRanges(rootNode)[0];
|
|
24
|
-
}
|
|
25
|
-
// Gecko implements the range API properly in Native Shadow: https://developer.mozilla.org/en-US/docs/Web/API/Selection/getRangeAt
|
|
26
|
-
return selection.getRangeAt(0);
|
|
24
|
+
export const rowsToEditorHeight = (rows) => {
|
|
25
|
+
const root = document.documentElement;
|
|
26
|
+
const computedStyle = getComputedStyle(root);
|
|
27
|
+
const fontSizeValue = computedStyle.getPropertyValue('--mg-b-font-size').trim();
|
|
28
|
+
const lineHeightValue = computedStyle.getPropertyValue('--mg-b-line-height').trim();
|
|
29
|
+
if (fontSizeValue === '') {
|
|
30
|
+
throw new Error('mg-input-rich-text-editor: CSS variable --mg-b-font-size is not defined.');
|
|
27
31
|
}
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
if (lineHeightValue === '') {
|
|
33
|
+
throw new Error('mg-input-rich-text-editor: CSS variable --mg-b-line-height is not defined.');
|
|
30
34
|
}
|
|
35
|
+
const fontSizeRem = parseFloat(fontSizeValue.replace('rem', ''));
|
|
36
|
+
const lineHeight = parseFloat(lineHeightValue);
|
|
37
|
+
const htmlFontSize = parseFloat(getComputedStyle(root).fontSize);
|
|
38
|
+
const fontSizePx = fontSizeRem * htmlFontSize;
|
|
39
|
+
const lineHeightPx = fontSizePx * lineHeight;
|
|
40
|
+
const paddingVertical = 0.8 * htmlFontSize;
|
|
41
|
+
const contentHeight = lineHeightPx * rows;
|
|
42
|
+
const wysiwygHeight = contentHeight + paddingVertical * 2;
|
|
43
|
+
const toolbarHeight = 40;
|
|
44
|
+
const borderHeight = 2;
|
|
45
|
+
const totalHeight = wysiwygHeight + toolbarHeight + borderHeight;
|
|
46
|
+
return `${Math.round(totalHeight)}px`;
|
|
31
47
|
};
|
|
32
48
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* @param
|
|
49
|
+
* Configures Jodit editor
|
|
50
|
+
* @param element - Component element (HTMLMgInputRichTextEditorElement)
|
|
51
|
+
* @param editorElement - Textarea element for the editor
|
|
52
|
+
* @param config - Editor configuration options
|
|
53
|
+
* @returns Configured Jodit editor instance
|
|
36
54
|
*/
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
55
|
+
export const defineEditor = (element, editorElement, config) => {
|
|
56
|
+
const { value, modules, readOnly, disabled, placeholder, rows = DEFAULT_ROWS, handleTextChange, handleFocus, handleBlur } = config;
|
|
57
|
+
// Configure Jodit
|
|
58
|
+
// Note: Using Record<string, unknown> to avoid TypeScript complexity issues with Jodit's deeply nested Config type
|
|
59
|
+
// The actual configuration follows Jodit's Config interface from jodit/esm/types
|
|
60
|
+
const joditConfig = {
|
|
61
|
+
readonly: readOnly === true,
|
|
62
|
+
// When readOnly (readonly or disabled), disable ALL toolbar buttons including plugin buttons (print, source).
|
|
63
|
+
// By default Jodit keeps source, print, fullsize, about, dots active in read-only mode.
|
|
64
|
+
// Set to [] so that when setReadOnly(true) is called (now or later), no toolbar buttons stay active.
|
|
65
|
+
activeButtonsInReadOnly: [],
|
|
66
|
+
placeholder: placeholder || '', // Avoid `Type something...` placeholder by default
|
|
67
|
+
// Set tabIndex to 0 to enable keyboard navigation with Tab key when enabled
|
|
68
|
+
// When disabled, use -1 to exclude the editor from Tab navigation
|
|
69
|
+
tabIndex: disabled === true ? -1 : 0,
|
|
70
|
+
buttons: modules,
|
|
71
|
+
// Disable features we don't want
|
|
72
|
+
showXPathInStatusbar: false,
|
|
73
|
+
showCharsCounter: false,
|
|
74
|
+
showWordsCounter: false,
|
|
75
|
+
toolbarAdaptive: false,
|
|
76
|
+
// Configure source editor mode
|
|
77
|
+
sourceEditor: 'area',
|
|
78
|
+
// This ensures tooltips and popups are created inside the component's shadow root
|
|
79
|
+
shadowRoot: element.shadowRoot,
|
|
80
|
+
// Provide ownerDocument and ownerWindow to ensure Jodit uses the correct context
|
|
81
|
+
ownerDocument: element.shadowRoot.ownerDocument,
|
|
82
|
+
ownerWindow: window,
|
|
83
|
+
// Prevent fullscreen from breaking component's shadow root isolation
|
|
84
|
+
globalFullSize: false,
|
|
85
|
+
// Set min-height from rows (wysiwyg + toolbar + borders)
|
|
86
|
+
minHeight: rowsToEditorHeight(rows),
|
|
87
|
+
// Resizer configuration
|
|
88
|
+
allowResizeTags: new Set(['img', 'table']),
|
|
89
|
+
resizer: {
|
|
90
|
+
showSize: true,
|
|
91
|
+
hideSizeTimeout: 1000,
|
|
92
|
+
useAspectRatio: new Set(['img']), // Preserve aspect ratio for images only
|
|
93
|
+
forImageChangeAttributes: true,
|
|
94
|
+
min_width: 10,
|
|
95
|
+
min_height: 10,
|
|
96
|
+
},
|
|
97
|
+
// Inline toolbar configuration
|
|
98
|
+
toolbarInline: true,
|
|
99
|
+
tableAllowCellSelection: true,
|
|
100
|
+
popup: {
|
|
101
|
+
a: Jodit.atom(['link', 'unlink', 'delete']),
|
|
102
|
+
},
|
|
46
103
|
};
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
104
|
+
// Initialize Jodit editor
|
|
105
|
+
const joditInstance = Jodit.make(editorElement, joditConfig);
|
|
106
|
+
// Add custom classes
|
|
107
|
+
// These classes are added to Jodit's DOM elements for styling purposes
|
|
108
|
+
const wrapperElement = editorElement.parentElement;
|
|
109
|
+
const addCustomClass = (selector, className) => {
|
|
110
|
+
wrapperElement.querySelector(selector).classList.add(className);
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Helpers for Jodit toolbar accessibility.
|
|
114
|
+
* @returns Toolbar element or null if not found
|
|
115
|
+
*/
|
|
116
|
+
const getJoditToolbarElement = () => wrapperElement.querySelector('.jodit-toolbar__box');
|
|
117
|
+
/**
|
|
118
|
+
* Get the WYSIWYG editor element.
|
|
119
|
+
* @returns WYSIWYG element or null if not found
|
|
120
|
+
*/
|
|
121
|
+
const getJoditWysiwygElement = () => wrapperElement.querySelector('.jodit-wysiwyg');
|
|
122
|
+
/**
|
|
123
|
+
* Get focusable toolbar buttons.
|
|
124
|
+
* @returns Array of focusable toolbar buttons
|
|
125
|
+
*/
|
|
126
|
+
const getFocusableToolbarButtons = (toolbarEl) => {
|
|
127
|
+
if (toolbarEl === null)
|
|
128
|
+
return null;
|
|
129
|
+
// Find all buttons within the toolbar, excluding disabled ones
|
|
130
|
+
const buttons = Array.from(toolbarEl.querySelectorAll('button:not([disabled])'));
|
|
131
|
+
// Filter out buttons that are not visible or are in popups/dropdowns
|
|
132
|
+
return buttons.filter(button => {
|
|
133
|
+
const isVisible = button.offsetParent !== null;
|
|
134
|
+
const isInToolbar = toolbarEl.contains(button);
|
|
135
|
+
// Exclude buttons in popups or dropdowns (they have their own navigation)
|
|
136
|
+
const isInPopup = button.closest('.jodit-popup, .jodit-dropdown') !== null;
|
|
137
|
+
return isVisible && isInToolbar && !isInPopup;
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
addCustomClass('.jodit-container', 'mg-c-input__container');
|
|
141
|
+
addCustomClass('.jodit-toolbar__box', 'mg-c-input__toolbar-box');
|
|
142
|
+
addCustomClass('.jodit-workplace', 'mg-c-input__workplace');
|
|
143
|
+
addCustomClass('.jodit-wysiwyg', 'mg-c-input__wysiwyg');
|
|
144
|
+
// Improve toolbar accessibility
|
|
145
|
+
// Add ARIA attributes and keyboard navigation to the toolbar container for better screen reader support
|
|
146
|
+
const toolbarElement = getJoditToolbarElement();
|
|
147
|
+
if (toolbarElement !== null) {
|
|
148
|
+
// Set role="toolbar" to identify this as a toolbar widget
|
|
149
|
+
// This helps screen readers understand the toolbar's purpose and enables proper keyboard navigation
|
|
150
|
+
toolbarElement.setAttribute('role', 'toolbar');
|
|
151
|
+
toolbarElement.setAttribute('aria-label', 'Text formatting toolbar');
|
|
152
|
+
/**
|
|
153
|
+
* Handle keyboard navigation within the toolbar
|
|
154
|
+
* Implements ARIA toolbar pattern: ArrowLeft/Right for navigation, Home/End for first/last
|
|
155
|
+
*/
|
|
156
|
+
const handleToolbarKeyDown = (event) => {
|
|
157
|
+
// Only handle keys when focus is within the toolbar
|
|
158
|
+
const target = event.target;
|
|
159
|
+
if (!toolbarElement.contains(target) || target.tagName !== 'BUTTON') {
|
|
160
|
+
return;
|
|
65
161
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
162
|
+
const buttons = getFocusableToolbarButtons(toolbarElement);
|
|
163
|
+
if (buttons === null || buttons.length === 0)
|
|
164
|
+
return;
|
|
165
|
+
const currentIndex = buttons.indexOf(target);
|
|
166
|
+
if (currentIndex === -1)
|
|
167
|
+
return;
|
|
168
|
+
let targetIndex = currentIndex;
|
|
169
|
+
switch (event.key) {
|
|
170
|
+
case 'ArrowLeft':
|
|
171
|
+
event.preventDefault();
|
|
172
|
+
event.stopPropagation();
|
|
173
|
+
// Move to previous button, wrap to last if at first
|
|
174
|
+
targetIndex = currentIndex > 0 ? currentIndex - 1 : buttons.length - 1;
|
|
175
|
+
buttons[targetIndex].focus();
|
|
176
|
+
break;
|
|
177
|
+
case 'ArrowRight':
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
event.stopPropagation();
|
|
180
|
+
// Move to next button, wrap to first if at last
|
|
181
|
+
targetIndex = currentIndex < buttons.length - 1 ? currentIndex + 1 : 0;
|
|
182
|
+
buttons[targetIndex].focus();
|
|
183
|
+
break;
|
|
184
|
+
case 'Home':
|
|
185
|
+
event.preventDefault();
|
|
186
|
+
event.stopPropagation();
|
|
187
|
+
// Move to first button
|
|
188
|
+
buttons[0].focus();
|
|
189
|
+
break;
|
|
190
|
+
case 'End':
|
|
191
|
+
event.preventDefault();
|
|
192
|
+
event.stopPropagation();
|
|
193
|
+
// Move to last button
|
|
194
|
+
buttons[buttons.length - 1].focus();
|
|
195
|
+
break;
|
|
196
|
+
case 'Tab':
|
|
197
|
+
// Allow Tab to work normally (exit toolbar)
|
|
198
|
+
break;
|
|
199
|
+
default:
|
|
200
|
+
// Let other keys work normally (Enter, Space, etc.)
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Hide SVG icons from screen readers in toolbar buttons.
|
|
206
|
+
* Toolbar buttons already have aria-label, so SVG icons are purely decorative.
|
|
207
|
+
* Without aria-hidden, VoiceOver announces them as images.
|
|
208
|
+
*/
|
|
209
|
+
const hideToolbarSvgsFromScreenReaders = () => {
|
|
210
|
+
toolbarElement.querySelectorAll('svg').forEach(svg => {
|
|
211
|
+
svg.setAttribute('aria-hidden', 'true');
|
|
212
|
+
});
|
|
213
|
+
};
|
|
214
|
+
// Hide SVG icons from screen readers on first render
|
|
215
|
+
hideToolbarSvgsFromScreenReaders();
|
|
216
|
+
// Set up event listeners for keyboard navigation
|
|
217
|
+
toolbarElement.addEventListener('keydown', handleToolbarKeyDown, true);
|
|
218
|
+
// Hide SVGs when toolbar content changes (buttons can be added/removed dynamically)
|
|
219
|
+
const toolbarObserver = new MutationObserver(() => {
|
|
220
|
+
hideToolbarSvgsFromScreenReaders();
|
|
221
|
+
});
|
|
222
|
+
toolbarObserver.observe(toolbarElement, {
|
|
223
|
+
childList: true,
|
|
224
|
+
subtree: true,
|
|
225
|
+
attributes: true,
|
|
226
|
+
attributeFilter: ['disabled'],
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
// Transfer accessibility attributes from the hidden textarea to the WYSIWYG editor
|
|
230
|
+
// The textarea is hidden by Jodit (display: none), so its aria-describedby is not accessible.
|
|
231
|
+
// We need to transfer it to the actual editable element (.jodit-wysiwyg) so screen readers
|
|
232
|
+
// can properly announce help text and error messages.
|
|
233
|
+
if (getJoditWysiwygElement() !== null) {
|
|
234
|
+
const wysiwygElement = getJoditWysiwygElement();
|
|
235
|
+
if (wysiwygElement !== null) {
|
|
236
|
+
// mg-input sets aria-describedby on the textarea in its componentDidLoad (child runs before parent)
|
|
237
|
+
const ariaDescribedBy = editorElement.getAttribute('aria-describedby');
|
|
238
|
+
if (ariaDescribedBy !== null) {
|
|
239
|
+
wysiwygElement.setAttribute('aria-describedby', ariaDescribedBy);
|
|
69
240
|
}
|
|
70
|
-
|
|
241
|
+
// Also set role="textbox" and aria-multiline="true" for proper screen reader support
|
|
242
|
+
wysiwygElement.setAttribute('role', 'textbox');
|
|
243
|
+
wysiwygElement.setAttribute('aria-multiline', 'true');
|
|
244
|
+
// Sync disabled state for screen readers (aria-disabled only when true, omitted otherwise)
|
|
245
|
+
if (disabled === true) {
|
|
246
|
+
wysiwygElement.setAttribute('aria-disabled', 'true');
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
wysiwygElement.removeAttribute('aria-disabled');
|
|
250
|
+
}
|
|
251
|
+
// Associate the editor with the label for screen readers and label click
|
|
252
|
+
const identifier = element.identifier;
|
|
253
|
+
if (identifier !== '') {
|
|
254
|
+
wrapperElement.removeAttribute('id');
|
|
255
|
+
wysiwygElement.id = identifier;
|
|
256
|
+
// Make label click focus the editor like a native input.
|
|
257
|
+
const titleId = `${identifier}-title`;
|
|
258
|
+
const titleElement = element.shadowRoot.querySelector('mg-input-title');
|
|
259
|
+
if (titleElement !== null && titleElement !== undefined) {
|
|
260
|
+
if (titleElement.id !== titleId) {
|
|
261
|
+
titleElement.id = titleId;
|
|
262
|
+
}
|
|
263
|
+
titleElement.addEventListener('click', () => {
|
|
264
|
+
joditInstance.focus();
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
// Link to the label for screen readers
|
|
268
|
+
wysiwygElement.setAttribute('aria-labelledby', titleId);
|
|
269
|
+
}
|
|
270
|
+
// Set up a MutationObserver to sync aria-describedby changes from textarea to wysiwyg
|
|
271
|
+
// This handles dynamic updates when error messages appear/disappear
|
|
272
|
+
const observer = new MutationObserver(() => {
|
|
273
|
+
const updatedAriaDescribedBy = editorElement.getAttribute('aria-describedby');
|
|
274
|
+
if (updatedAriaDescribedBy !== null) {
|
|
275
|
+
wysiwygElement.setAttribute('aria-describedby', updatedAriaDescribedBy);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
wysiwygElement.removeAttribute('aria-describedby');
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
observer.observe(editorElement, {
|
|
282
|
+
attributes: true,
|
|
283
|
+
attributeFilter: ['aria-describedby'],
|
|
284
|
+
});
|
|
285
|
+
// Set up Shift+Tab interception on the wysiwyg element (contenteditable)
|
|
286
|
+
// This intercepts Shift+Tab from the editor to redirect to the first toolbar button
|
|
287
|
+
const handleWysiwygShiftTab = (event) => {
|
|
288
|
+
// Only handle Shift+Tab (backward navigation)
|
|
289
|
+
if (event.key !== 'Tab' || !event.shiftKey) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const buttons = getFocusableToolbarButtons(getJoditToolbarElement());
|
|
293
|
+
if (buttons !== null && buttons.length > 0) {
|
|
294
|
+
// Prevent default Tab behavior to intercept navigation
|
|
295
|
+
event.preventDefault();
|
|
296
|
+
event.stopPropagation();
|
|
297
|
+
// Focus first toolbar button directly
|
|
298
|
+
buttons[0].focus();
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
// Listen for keydown events directly on the wysiwyg element
|
|
302
|
+
wysiwygElement.addEventListener('keydown', handleWysiwygShiftTab, true);
|
|
71
303
|
}
|
|
72
|
-
};
|
|
73
|
-
// Subscribe to selection change separately, because emitter in Quill doesn't catch this event in Shadow DOM
|
|
74
|
-
document.addEventListener('selectionchange', () => {
|
|
75
|
-
editor.selection.update();
|
|
76
|
-
});
|
|
77
|
-
};
|
|
78
|
-
/**
|
|
79
|
-
* Fixes for handling text selection in Quill when used within the Shadow DOM.
|
|
80
|
-
* - Fixes focus detection
|
|
81
|
-
* - Adapts native range handling to work with the Shadow DOM
|
|
82
|
-
* - Correctly manages text selection across Shadow DOM boundaries
|
|
83
|
-
* - Sets up selection change events
|
|
84
|
-
* @returns Quill instance
|
|
85
|
-
*/
|
|
86
|
-
export const defineEditor = (wrapperElement, { value, modules, readOnly, placeholder, handleTextChange, handleFocus, handleBlur }) => {
|
|
87
|
-
const toolbarOptions = {
|
|
88
|
-
toolbar: [['bold', 'italic', 'underline'], [{ list: 'ordered' }, { list: 'bullet' }], ['link'], ['clean']],
|
|
89
|
-
};
|
|
90
|
-
const editor = new Quill(wrapperElement.querySelector('div'), {
|
|
91
|
-
theme: 'snow',
|
|
92
|
-
modules: modules || toolbarOptions,
|
|
93
|
-
readOnly,
|
|
94
|
-
placeholder,
|
|
95
|
-
});
|
|
96
|
-
if (isValidString(value) && /<[a-z][\s\S]*>/i.test(value)) {
|
|
97
|
-
editor.clipboard.dangerouslyPasteHTML(value);
|
|
98
304
|
}
|
|
99
|
-
|
|
100
|
-
|
|
305
|
+
// Set initial value
|
|
306
|
+
if (isValidString(value)) {
|
|
307
|
+
joditInstance.value = value;
|
|
101
308
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return editor;
|
|
309
|
+
// Set up event listeners
|
|
310
|
+
joditInstance.events.on('change', handleTextChange);
|
|
311
|
+
joditInstance.events.on('focus', handleFocus);
|
|
312
|
+
joditInstance.events.on('blur', handleBlur);
|
|
313
|
+
// Return the Jodit instance directly
|
|
314
|
+
return joditInstance;
|
|
109
315
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Default toolbar modules when the modules prop is not set. */
|
|
2
|
+
export const DEFAULT_MODULES = [
|
|
3
|
+
'bold',
|
|
4
|
+
'italic',
|
|
5
|
+
'underline',
|
|
6
|
+
'strikethrough',
|
|
7
|
+
'eraser',
|
|
8
|
+
'|',
|
|
9
|
+
'ul',
|
|
10
|
+
'ol',
|
|
11
|
+
'|',
|
|
12
|
+
'superscript',
|
|
13
|
+
'subscript',
|
|
14
|
+
'|',
|
|
15
|
+
'brush',
|
|
16
|
+
'|',
|
|
17
|
+
'link',
|
|
18
|
+
'image',
|
|
19
|
+
'file',
|
|
20
|
+
'|',
|
|
21
|
+
'table',
|
|
22
|
+
'|',
|
|
23
|
+
'undo',
|
|
24
|
+
'redo',
|
|
25
|
+
'|',
|
|
26
|
+
'print',
|
|
27
|
+
'|',
|
|
28
|
+
'source',
|
|
29
|
+
];
|