@procore/text-editor 0.5.0 → 1.0.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.
Files changed (62) hide show
  1. package/.jest/ckeditorMock.js +47 -23
  2. package/CHANGELOG.md +30 -0
  3. package/README.md +96 -7
  4. package/dist/TextEditor/CKTextEditor.d.ts +3 -0
  5. package/dist/TextEditor/CKTextEditor.js +312 -0
  6. package/dist/TextEditor/CKTextEditor.js.map +1 -0
  7. package/dist/TextEditor/Providers/FeatureFlagProvider.d.ts +5 -0
  8. package/dist/TextEditor/Providers/FeatureFlagProvider.js +34 -0
  9. package/dist/TextEditor/Providers/FeatureFlagProvider.js.map +1 -0
  10. package/dist/TextEditor/TextEditor.d.ts +1 -1
  11. package/dist/TextEditor/TextEditor.js +16 -232
  12. package/dist/TextEditor/TextEditor.js.map +1 -1
  13. package/dist/TextEditor/TextEditor.styles.d.ts +14 -1
  14. package/dist/TextEditor/TextEditor.styles.js +18 -5
  15. package/dist/TextEditor/TextEditor.styles.js.map +1 -1
  16. package/dist/TextEditor/TextEditor.types.d.ts +82 -1
  17. package/dist/TextEditor/TextEditor.types.js.map +1 -1
  18. package/dist/TextEditor/TextEditorI18nProvider.d.ts +2 -0
  19. package/dist/TextEditor/TextEditorI18nProvider.js +27 -0
  20. package/dist/TextEditor/TextEditorI18nProvider.js.map +1 -0
  21. package/dist/TextEditor/TinyMCETextEditor.d.ts +3 -0
  22. package/dist/TextEditor/TinyMCETextEditor.js +63 -0
  23. package/dist/TextEditor/TinyMCETextEditor.js.map +1 -0
  24. package/dist/TextEditor/plugins/NormalizeTableWidthPlugin/NormalizeTableWidthPlugin.d.ts +15 -0
  25. package/dist/TextEditor/plugins/NormalizeTableWidthPlugin/NormalizeTableWidthPlugin.js +72 -0
  26. package/dist/TextEditor/plugins/NormalizeTableWidthPlugin/NormalizeTableWidthPlugin.js.map +1 -0
  27. package/dist/TextEditor/plugins/NormalizeTableWidthPlugin/index.d.ts +1 -0
  28. package/dist/TextEditor/plugins/NormalizeTableWidthPlugin/index.js +2 -0
  29. package/dist/TextEditor/plugins/NormalizeTableWidthPlugin/index.js.map +1 -0
  30. package/dist/TextEditor/plugins/ProcoreImageUploadPlugin/ProcoreImageUploadPlugin.d.ts +48 -0
  31. package/dist/TextEditor/plugins/ProcoreImageUploadPlugin/ProcoreImageUploadPlugin.js +441 -0
  32. package/dist/TextEditor/plugins/ProcoreImageUploadPlugin/ProcoreImageUploadPlugin.js.map +1 -0
  33. package/dist/TextEditor/plugins/ProcoreImageUploadPlugin/index.d.ts +1 -0
  34. package/dist/TextEditor/plugins/ProcoreImageUploadPlugin/index.js +2 -0
  35. package/dist/TextEditor/plugins/ProcoreImageUploadPlugin/index.js.map +1 -0
  36. package/dist/TextEditor/utils/config.d.ts +6 -1
  37. package/dist/TextEditor/utils/config.js +25 -10
  38. package/dist/TextEditor/utils/config.js.map +1 -1
  39. package/dist/TextEditor/utils/imageAttributes.d.ts +6 -0
  40. package/dist/TextEditor/utils/imageAttributes.js +17 -0
  41. package/dist/TextEditor/utils/imageAttributes.js.map +1 -0
  42. package/dist/TextEditor/utils/imageUrls.d.ts +2 -0
  43. package/dist/TextEditor/utils/imageUrls.js +36 -0
  44. package/dist/TextEditor/utils/imageUrls.js.map +1 -0
  45. package/dist/TextEditor/utils/richTextEditorHandlers.d.ts +9 -0
  46. package/dist/TextEditor/utils/richTextEditorHandlers.js +234 -0
  47. package/dist/TextEditor/utils/richTextEditorHandlers.js.map +1 -0
  48. package/dist/TextEditorOutput/TextEditorOutput.styles.js +2 -2
  49. package/dist/TextEditorOutput/TextEditorOutput.styles.js.map +1 -1
  50. package/dist/TextEditorOutput/TextEditorOutput.utils.d.ts +1 -1
  51. package/dist/TextEditorOutput/TextEditorOutput.utils.js +150 -6
  52. package/dist/TextEditorOutput/TextEditorOutput.utils.js.map +1 -1
  53. package/dist/_typedoc/TextEditor/TextEditor.types.json +215 -17
  54. package/dist/_typedoc/TextEditor/TextEditorProvider.types.json +2 -2
  55. package/dist/_typedoc/TextEditorOutput/TextEditorOutput.types.json +3 -3
  56. package/dist/ckeditor5-premium-features.d.js +2 -0
  57. package/dist/ckeditor5-premium-features.d.js.map +1 -0
  58. package/dist/locales/en.json +17 -0
  59. package/dist/locales/pseudo.json +17 -0
  60. package/dist/utils/debounce.js +4 -4
  61. package/dist/utils/debounce.js.map +1 -1
  62. package/package.json +8 -3
@@ -28,40 +28,64 @@ module.exports = {
28
28
  CKEditor: MockCKEditor,
29
29
  Command: MockCommand,
30
30
  Plugin: class extends MockPlugin {},
31
+ Alignment: class extends MockPlugin {},
32
+ Autoformat: class extends MockPlugin {},
33
+ AutoLink: class extends MockPlugin {},
34
+ BlockQuote: class extends MockPlugin {},
31
35
  Bold: class extends MockPlugin {},
32
- Italic: class extends MockPlugin {},
33
- Underline: class extends MockPlugin {},
34
- Link: class extends MockPlugin {},
35
- List: class extends MockPlugin {},
36
- Table: class extends MockPlugin {},
37
- Strikethrough: class extends MockPlugin {},
36
+ Clipboard: class extends MockPlugin {},
38
37
  Code: class extends MockPlugin {},
39
- Subscript: class extends MockPlugin {},
40
- Superscript: class extends MockPlugin {},
41
- Highlight: class extends MockPlugin {},
42
- BlockQuote: class extends MockPlugin {},
38
+ CodeBlock: class extends MockPlugin {},
39
+ Essentials: class extends MockPlugin {},
40
+ FileRepository: class extends MockPlugin {},
41
+ FontBackgroundColor: class extends MockPlugin {},
42
+ FontColor: class extends MockPlugin {},
43
+ FontSize: class extends MockPlugin {},
44
+ GeneralHtmlSupport: class extends MockPlugin {},
43
45
  Heading: class extends MockPlugin {},
46
+ Highlight: class extends MockPlugin {},
47
+ HorizontalLine: class extends MockPlugin {},
44
48
  Image: class extends MockPlugin {},
49
+ ImageCaption: class extends MockPlugin {},
50
+ ImageInsert: class extends MockPlugin {},
51
+ ImageResize: class extends MockPlugin {},
52
+ ImageTextAlternative: class extends MockPlugin {},
53
+ ImageToolbar: class extends MockPlugin {},
54
+ ImageUpload: class extends MockPlugin {},
55
+ ImageUploadEditing: class extends MockPlugin {},
56
+ Indent: class extends MockPlugin {},
57
+ IndentBlock: class extends MockPlugin {},
58
+ Italic: class extends MockPlugin {},
59
+ Link: class extends MockPlugin {},
60
+ List: class extends MockPlugin {},
61
+ ListProperties: class extends MockPlugin {},
45
62
  MediaEmbed: class extends MockPlugin {},
46
- HorizontalLine: class extends MockPlugin {},
63
+ MultiLevelList: class extends MockPlugin {},
47
64
  PageBreak: class extends MockPlugin {},
48
- Alignment: class extends MockPlugin {},
49
- Font: class extends MockPlugin {},
50
- Indent: class extends MockPlugin {},
65
+ Paragraph: class extends MockPlugin {},
51
66
  RemoveFormat: class extends MockPlugin {},
52
67
  SpecialCharacters: class extends MockPlugin {},
53
- WordCount: class extends MockPlugin {},
54
- FindAndReplace: class extends MockPlugin {},
55
- SourceEditing: class extends MockPlugin {},
56
- RestrictedEditing: class extends MockPlugin {},
57
- Style: class extends MockPlugin {},
58
- ShowBlocks: class extends MockPlugin {},
59
- CodeBlock: class extends MockPlugin {},
60
- AutoFormat: class extends MockPlugin {},
61
- Autolink: class extends MockPlugin {},
68
+ SpecialCharactersEssentials: class extends MockPlugin {},
69
+ Strikethrough: class extends MockPlugin {},
70
+ Subscript: class extends MockPlugin {},
71
+ Superscript: class extends MockPlugin {},
72
+ Table: class extends MockPlugin {},
73
+ TableCaption: class extends MockPlugin {},
74
+ TableCellProperties: class extends MockPlugin {},
75
+ TableColumnResize: class extends MockPlugin {},
76
+ TableProperties: class extends MockPlugin {},
77
+ TableToolbar: class extends MockPlugin {},
78
+ Underline: class extends MockPlugin {},
62
79
  Autosave: class extends MockPlugin {},
63
80
  Bookmark: class extends MockPlugin {},
64
81
  Emoji: class extends MockPlugin {},
82
+ FindAndReplace: class extends MockPlugin {},
83
+ Font: class extends MockPlugin {},
65
84
  Fullscreen: class extends MockPlugin {},
66
85
  Language: class extends MockPlugin {},
86
+ RestrictedEditing: class extends MockPlugin {},
87
+ ShowBlocks: class extends MockPlugin {},
88
+ SourceEditing: class extends MockPlugin {},
89
+ Style: class extends MockPlugin {},
90
+ WordCount: class extends MockPlugin {},
67
91
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @procore/text-editor
2
2
 
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - de1d5b3: - **Breaking**: Default renderer changed from CKEditor to TinyMCE. CKEditor activates via LaunchDarkly flag `pmqs-ck-editor-enabled` or `enforceCKEditor` prop.
8
+
9
+ ### New Props
10
+
11
+ | Prop | Description |
12
+ | ----------------- | -------------------------------------------------------------------------------------------------------------- |
13
+ | `enforceCKEditor` | Force CKEditor regardless of flag |
14
+ | `rows` | Fixed height in rows; omit for auto-grow |
15
+ | `placeholder` | Placeholder text for empty editor |
16
+ | `init` | TinyMCE-only init config merged with internal defaults; consumer values win. No effect when CKEditor is active |
17
+
18
+ ### Minor Changes
19
+
20
+ - 0a850ec: Add image upload support backed by standard [Uploads API](https://developers.procore.com/reference/rest/uploads?version=1.1) staging and standalone Prostore files. Uploaded images embed a permanent, publicly fetchable URL plus a `data-rich-text-editor-file-id` marker directly into the saved HTML so content renders as-is in the app, emails, and exports. Images are clamped to their container width (`max-width: 100%`) and limited to 20 per editor value to protect email and PDF layouts.
21
+
22
+ ## 0.5.1
23
+
24
+ ### Patch Changes
25
+
26
+ - ffcbad8: chore: don't assign `this` to a variable sonar lint failure
27
+ - bee0d43: Fix tables pasted from spreadsheets (Excel / Google Sheets) rendering collapsed by normalizing invalid zero/NaN figure widths during the clipboard paste pipeline.
28
+ - Updated dependencies [f0bb16b]
29
+ - Updated dependencies [195f0ba]
30
+ - Updated dependencies [0b792e4]
31
+ - @procore/core-react@12.50.1
32
+
3
33
  ## 0.5.0
4
34
 
5
35
  ### Minor Changes
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @procore/text-editor
2
2
 
3
- Rich text editor component
3
+ Rich text editor component. Renders CKEditor or TinyMCE based on a LaunchDarkly feature flag or with a hard opt-in available via `enforceCKEditor`.
4
4
 
5
5
  ## Installation
6
6
 
@@ -44,11 +44,8 @@ function MyComponent() {
44
44
  }
45
45
  ```
46
46
 
47
-
48
47
  ### Displaying Read-Only Content
49
48
 
50
- You can display formatted text in two ways:
51
-
52
49
  **Option 1:** Use `TextEditor` with `readonly` prop:
53
50
 
54
51
  ```tsx
@@ -69,12 +66,104 @@ function MyComponent({ htmlContent }) {
69
66
  }
70
67
  ```
71
68
 
72
- ## Features
69
+ ### Image uploads
73
70
 
71
+ Pass `companyId` and `toolName` to enable the rich text image upload endpoint.
72
+ When editing project-scoped content, also pass `projectId`:
74
73
 
75
- ### Toolbar Features
74
+ ```tsx
75
+ import { TextEditor } from '@procore/text-editor'
76
+
77
+ function MyComponent() {
78
+ return (
79
+ <TextEditor
80
+ companyId={companyId}
81
+ projectId={projectId}
82
+ toolName="rfis"
83
+ />
84
+ )
85
+ }
86
+ ```
87
+
88
+ The editor first creates a standard Procore Upload, sends the image directly to
89
+ storage, then finalizes it through
90
+ `POST /rest/v2.0/companies/:company_id/projects/:project_id/rich_text_editor/images`.
91
+ Rails stores the finalized upload as a standalone Prostore file and returns its
92
+ `prostore_file_id` plus a permanent, publicly fetchable URL.
93
+
94
+ The `toolName` value is sent as `tool_name` with the finalize request and is
95
+ used by Rails for file attribution and audit metadata. It must match a known
96
+ Procore tool name.
97
+
98
+ The editor embeds that permanent URL (and a `data-rich-text-editor-file-id`
99
+ attribute) directly into the saved HTML, so the content renders as-is in the
100
+ app, emails, and exports without any further URL generation. Consuming forms
101
+ save the rich-text HTML normally — no per-form wiring is needed.
102
+
103
+ Removing an image from the editor only removes it from the content; the editor
104
+ does not delete the underlying Prostore file. Unreferenced files become orphans
105
+ handled by the platform's File Access cleanup, so editing existing content can
106
+ never make an image disappear for other viewers.
107
+
108
+ Up to 20 images are allowed per editor value, and images are clamped to their
109
+ container width (`max-width: 100%`) so high-resolution uploads do not break the
110
+ app, email, or PDF layouts.
111
+
112
+ ## Props
113
+
114
+ | Prop | Type | Default | Description |
115
+ |------|------|---------|-------------|
116
+ | `value` | `string` | — | Current HTML content of the editor |
117
+ | `initialValue` | `string` | — | ⚠️ Deprecated. Use `value` instead |
118
+ | `onChange` | `(value: string, isDirty?: boolean) => void` | — | Fired on content change |
119
+ | `onInit` | `(editor: ClassicEditor) => void` | — | Fired when editor is ready |
120
+ | `onBlur` | `(event: EventInfo, editor: ClassicEditor) => void` | — | Fired on blur |
121
+ | `onFocus` | `(event: EventInfo, editor: ClassicEditor) => void` | — | Fired on focus |
122
+ | `onKeyDown` | `(event: KeyboardEvent, editor: ClassicEditor) => void` | — | Fired on keydown |
123
+ | `onDirty` | `() => void` | — | Fired once when content first differs from initial value |
124
+ | `onError` | `(error, details) => void` | — | Fired on editor error |
125
+ | `onAfterDestroy` | `() => void` | — | Fired after editor is destroyed |
126
+ | `placeholder` | `string` | — | Placeholder text shown when editor is empty |
127
+ | `rows` | `number` | — | Fixed height in rows. Omit for auto-grow |
128
+ | `disabled` | `boolean` | `false` | Disables the editor |
129
+ | `readonly` | `boolean` | `false` | Renders content as read-only output |
130
+ | `error` | `boolean` | `false` | Applies error styling |
131
+ | `locale` | `Locale` | — | Editor locale. Falls back to I18n context |
132
+ | `aria-label` | `string` | — | Accessible label for the editable area |
133
+ | `aria-description` | `string` | — | Accessible description for the editable area |
134
+ | `companyId` | `number \| string` | — | Required with `toolName` to enable image uploads |
135
+ | `projectId` | `number \| string` | — | Project context for project-scoped image uploads |
136
+ | `toolName` | `string` | — | Tool attribution sent with image uploads |
137
+ | `init` | `object` | — | TinyMCE-only init config. Merged with internal defaults; your values win. No effect when CKEditor is active |
138
+ | `enforceCKEditor` | `true` | — | Force CKEditor regardless of feature flag |
139
+
140
+ ### `rows` — Controlling Height
141
+
142
+ Use `rows` to set a fixed editor height. Omit it for auto-grow (default).
76
143
 
77
- The editor includes a comprehensive toolbar with the following capabilities:
144
+ ```tsx
145
+ // Fixed height — 3 rows tall, scrolls when content overflows
146
+ <TextEditor rows={3} value={value} onChange={setValue} />
147
+
148
+ // Auto-grow — expands with content
149
+ <TextEditor value={value} onChange={setValue} />
150
+ ```
151
+
152
+ Both CKEditor and TinyMCE use the same height calculation so they look identical.
153
+
154
+ ## Editor Selection
155
+
156
+ The component renders **TinyMCE by default** and switches to **CKEditor** via a LaunchDarkly feature flag (`pmqs-ck-editor-enabled`). CKEditor will eventually replace TinyMCE fully.
157
+
158
+ To opt in ahead of the rollout:
159
+
160
+ ```tsx
161
+ <TextEditor enforceCKEditor value={value} onChange={setValue} />
162
+ ```
163
+
164
+ ## Features
165
+
166
+ ### Toolbar Features
78
167
 
79
168
  | Feature | Description |
80
169
  |---------|-------------|
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import type { TextEditorProps } from './TextEditor.types';
3
+ export declare const CKTextEditor: (props: Readonly<TextEditorProps>) => React.JSX.Element | null;
@@ -0,0 +1,312 @@
1
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ var _excluded = ["disabled", "error", "readonly", "value", "initialValue", "onChange", "onInit", "onBlur", "onKeyDown", "locale", "onDirty", "companyId", "projectId", "toolName", "placeholder", "rows"];
3
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
6
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
7
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
8
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
9
+ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
10
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
11
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
12
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
13
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
14
+ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
15
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
16
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
17
+ import { CKEditor } from '@ckeditor/ckeditor5-react';
18
+ import { useToastAlertContext } from '@procore/toast-alert';
19
+ import { ClassicEditor, FileRepository } from 'ckeditor5';
20
+ import React from 'react';
21
+ import { Toast, useI18nContext, useZIndexContext } from '@procore/core-react';
22
+ import { TextEditorOutput } from '../TextEditorOutput';
23
+ import { debounce } from '../utils/debounce';
24
+ import { useStickyToolbar } from './StickyToolbar';
25
+ import { GlobalEditorStyles, StyledImageUploadWarningToast, StyledImageUploadWarningToastIcon, StyledTextEditor } from './TextEditor.styles';
26
+ import { TextEditorContext } from './TextEditorProvider';
27
+ import { TextEditorTheme } from './textEditorTheming';
28
+ import { useCKEditorCss } from './useCKEditorCss';
29
+ import { useCKEditorFocusScope } from './useCKEditorFocusScope';
30
+ import { useTabAsNavigation } from './useTabAsNavigation';
31
+ import { addButtonDataAttributes, getDefaultConfig } from './utils';
32
+ import { richTextEditorUploadHandler } from './utils/richTextEditorHandlers';
33
+ var DEBOUNCE_MS = 300;
34
+ export var CKTextEditor = function CKTextEditor(props) {
35
+ var disabled = props.disabled,
36
+ error = props.error,
37
+ readonly = props.readonly,
38
+ value = props.value,
39
+ initialValue = props.initialValue,
40
+ onChange = props.onChange,
41
+ onInit = props.onInit,
42
+ onBlur = props.onBlur,
43
+ onKeyDown = props.onKeyDown,
44
+ propLocale = props.locale,
45
+ onDirty = props.onDirty,
46
+ companyId = props.companyId,
47
+ projectId = props.projectId,
48
+ toolName = props.toolName,
49
+ placeholder = props.placeholder,
50
+ rows = props.rows,
51
+ restProps = _objectWithoutProperties(props, _excluded);
52
+ var ariaLabel = props['aria-label'];
53
+ var ariaDescription = props['aria-description'];
54
+ var _useCKEditorCss = useCKEditorCss(),
55
+ cssLoading = _useCKEditorCss.isLoading;
56
+ useCKEditorFocusScope();
57
+ var editorRef = React.useRef(null);
58
+ var editorInstanceRef = React.useRef(null);
59
+ var initialValueRef = React.useRef(value || initialValue || '');
60
+ var lastExternalValueRef = React.useRef(value || initialValue || '');
61
+ var keyDownListenerRef = React.useRef(null);
62
+ var _React$useState = React.useState(false),
63
+ _React$useState2 = _slicedToArray(_React$useState, 2),
64
+ isDirtyState = _React$useState2[0],
65
+ setIsDirtyState = _React$useState2[1];
66
+ var _React$useState3 = React.useState(false),
67
+ _React$useState4 = _slicedToArray(_React$useState3, 2),
68
+ isEditorReady = _React$useState4[0],
69
+ setIsEditorReady = _React$useState4[1];
70
+ var i18n = useI18nContext();
71
+ var _useToastAlertContext = useToastAlertContext(),
72
+ showToast = _useToastAlertContext.showToast;
73
+ var contextLocale = i18n.locale;
74
+ var locale = propLocale || contextLocale;
75
+ var showImageUploadWarning = React.useCallback(function (message) {
76
+ showToast.custom(function (_ref) {
77
+ var toastDismissProps = _ref.toastDismissProps;
78
+ return /*#__PURE__*/React.createElement(StyledImageUploadWarningToast, {
79
+ alignItems: "center",
80
+ padding: "md xl"
81
+ }, /*#__PURE__*/React.createElement(StyledImageUploadWarningToastIcon, {
82
+ size: "lg"
83
+ }), /*#__PURE__*/React.createElement(Toast.Text, null, message), /*#__PURE__*/React.createElement(Toast.Dismiss, _extends({
84
+ "aria-label": i18n.t('textEditor.imageUpload.dismissWarning')
85
+ }, toastDismissProps)));
86
+ });
87
+ }, [i18n, showToast]);
88
+ React.useEffect(function () {
89
+ if (value !== undefined) {
90
+ lastExternalValueRef.current = value;
91
+ }
92
+ }, [value]);
93
+ React.useEffect(function () {
94
+ var editor = editorInstanceRef.current;
95
+ if (!isEditorReady || !editor || value === undefined) return;
96
+ if (editor.editing.view.document.isFocused) return;
97
+ if (value === editor.getData()) return;
98
+ editor.data.set(value);
99
+ }, [value, isEditorReady]);
100
+
101
+ // Cleanup keydown listener on unmount
102
+ React.useEffect(function () {
103
+ return function () {
104
+ if (keyDownListenerRef.current && editorInstanceRef.current) {
105
+ editorInstanceRef.current.editing.view.document.off('keydown', keyDownListenerRef.current);
106
+ keyDownListenerRef.current = null;
107
+ }
108
+ };
109
+ }, []);
110
+
111
+ // Update aria-label when ariaLabel prop changes
112
+ React.useEffect(function () {
113
+ if (!isEditorReady || !editorInstanceRef.current) {
114
+ return;
115
+ }
116
+ var editor = editorInstanceRef.current;
117
+ editor.editing.view.change(function (writer) {
118
+ var viewEditableRoot = editor.editing.view.document.getRoot();
119
+ if (viewEditableRoot) {
120
+ if (ariaLabel) {
121
+ writer.setAttribute('aria-label', ariaLabel, viewEditableRoot);
122
+ } else {
123
+ writer.removeAttribute('aria-label', viewEditableRoot);
124
+ }
125
+ }
126
+ });
127
+ }, [ariaLabel, isEditorReady]);
128
+
129
+ // Update aria-description when ariaDescription prop changes
130
+ React.useEffect(function () {
131
+ if (!isEditorReady || !editorInstanceRef.current) {
132
+ return;
133
+ }
134
+ var editor = editorInstanceRef.current;
135
+ editor.editing.view.change(function (writer) {
136
+ var viewEditableRoot = editor.editing.view.document.getRoot();
137
+ if (viewEditableRoot) {
138
+ if (ariaDescription) {
139
+ writer.setAttribute('aria-description', ariaDescription, viewEditableRoot);
140
+ } else {
141
+ writer.removeAttribute('aria-description', viewEditableRoot);
142
+ }
143
+ }
144
+ });
145
+ }, [ariaDescription, isEditorReady]);
146
+ var _useZIndexContext = useZIndexContext(),
147
+ zIndex = _useZIndexContext.value;
148
+ var _React$useContext = React.useContext(TextEditorContext),
149
+ features = _React$useContext.features;
150
+ var enableStickyToolbar = !!(features !== null && features !== void 0 && features.stickyToolbar);
151
+ var enableTabAsNavigation = !!(features !== null && features !== void 0 && features.tabAsNavigation);
152
+ var imageUploadHandlers = React.useMemo(function () {
153
+ if (companyId === undefined || toolName === undefined) {
154
+ return undefined;
155
+ }
156
+ var handlerOptions = {
157
+ companyId: companyId,
158
+ projectId: projectId,
159
+ toolName: toolName
160
+ };
161
+ var upload = richTextEditorUploadHandler(handlerOptions);
162
+ return {
163
+ upload: upload,
164
+ i18n: i18n,
165
+ onError: showToast.error,
166
+ onWarning: showImageUploadWarning
167
+ };
168
+ }, [companyId, i18n, projectId, showImageUploadWarning, showToast.error, toolName]);
169
+ var mergedConfig = React.useMemo(function () {
170
+ var defaultConfig = getDefaultConfig(locale, {
171
+ imageUpload: imageUploadHandlers
172
+ });
173
+
174
+ // Set accessible label for the editable area if provided
175
+ // This is used by screen readers to announce the editor's purpose
176
+ if (ariaLabel) {
177
+ defaultConfig.label = ariaLabel;
178
+ }
179
+ if (placeholder) {
180
+ defaultConfig.root = _objectSpread(_objectSpread({}, defaultConfig.root), {}, {
181
+ placeholder: placeholder
182
+ });
183
+ }
184
+ return defaultConfig;
185
+ }, [ariaLabel, imageUploadHandlers, locale, placeholder]);
186
+ var bindKeyDownHandler = React.useCallback(function (editor) {
187
+ var editorElement = editor.editing.view.document.getRoot();
188
+ if (!editorElement) {
189
+ return;
190
+ }
191
+
192
+ // Remove existing keydown listener
193
+ if (keyDownListenerRef.current) {
194
+ editor.editing.view.document.off('keydown', keyDownListenerRef.current);
195
+ }
196
+
197
+ // Create and store the new listener
198
+ var keyDownListener = function keyDownListener(_event, data) {
199
+ var domEvent = data.domEvent;
200
+ // Prevent Enter from propagating to parent forms so the
201
+ // editor can handle newlines without accidentally submitting.
202
+ if (domEvent.key === 'Enter') {
203
+ domEvent.stopPropagation();
204
+ }
205
+ onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(domEvent, editor);
206
+ };
207
+ keyDownListenerRef.current = keyDownListener;
208
+
209
+ // Add the keydown listener
210
+ editor.editing.view.document.on('keydown', keyDownListener);
211
+ }, [onKeyDown]);
212
+ var debouncedChange = React.useRef(debounce(function (_event, editor) {
213
+ var data = editor.getData();
214
+ var isDirty = data !== lastExternalValueRef.current.trim();
215
+ var hasPendingUploads = hasPendingImageUploads(editor);
216
+ if (isDirty && !isDirtyState) {
217
+ setIsDirtyState(true);
218
+ onDirty === null || onDirty === void 0 ? void 0 : onDirty();
219
+ }
220
+ if (hasPendingUploads) {
221
+ return;
222
+ }
223
+ onChange === null || onChange === void 0 ? void 0 : onChange(data, isDirty);
224
+ }, DEBOUNCE_MS)).current;
225
+ var handleChange = debouncedChange;
226
+ var handleReady = function handleReady(editor) {
227
+ addButtonDataAttributes(editor);
228
+ editorInstanceRef.current = editor;
229
+ setIsEditorReady(true);
230
+
231
+ // Set aria-label and aria-description on the editable area if provided
232
+ // These provide accessible context to screen reader users
233
+ editor.editing.view.change(function (writer) {
234
+ var viewEditableRoot = editor.editing.view.document.getRoot();
235
+ if (viewEditableRoot) {
236
+ if (ariaLabel) {
237
+ writer.setAttribute('aria-label', ariaLabel, viewEditableRoot);
238
+ }
239
+ if (ariaDescription) {
240
+ writer.setAttribute('aria-description', ariaDescription, viewEditableRoot);
241
+ }
242
+ if (rows !== undefined) {
243
+ var baseHeight = 35;
244
+ var rowHeight = 22;
245
+ var editorHeight = rows * rowHeight + baseHeight;
246
+ writer.setStyle('min-height', "".concat(editorHeight, "px"), viewEditableRoot);
247
+ writer.setStyle('max-height', "".concat(editorHeight, "px"), viewEditableRoot);
248
+ }
249
+ }
250
+ });
251
+
252
+ // Bind keydown handler when editor is ready
253
+ bindKeyDownHandler(editor);
254
+ onInit === null || onInit === void 0 ? void 0 : onInit(editor);
255
+ };
256
+ var handleBlur = function handleBlur(event, editor) {
257
+ debouncedChange.flush();
258
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(event, editor);
259
+ };
260
+ useStickyToolbar({
261
+ enabled: enableStickyToolbar,
262
+ editor: isEditorReady ? editorInstanceRef.current : null,
263
+ editorRef: editorRef,
264
+ cssLoading: cssLoading
265
+ });
266
+ var _useTabAsNavigation = useTabAsNavigation({
267
+ config: mergedConfig,
268
+ enabled: enableTabAsNavigation,
269
+ editor: isEditorReady ? editorInstanceRef.current : null
270
+ }),
271
+ config = _useTabAsNavigation.config;
272
+ var editorKey = "".concat(locale, "-").concat(enableTabAsNavigation);
273
+ if (cssLoading) {
274
+ return null;
275
+ }
276
+ if (readonly) {
277
+ return /*#__PURE__*/React.createElement(TextEditorOutput, {
278
+ value: value,
279
+ "aria-label": ariaLabel,
280
+ "aria-description": ariaDescription
281
+ });
282
+ }
283
+ return /*#__PURE__*/React.createElement(StyledTextEditor, {
284
+ ref: editorRef,
285
+ key: editorKey,
286
+ error: error,
287
+ $ready: isEditorReady,
288
+ "aria-invalid": error ? 'true' : 'false'
289
+ }, /*#__PURE__*/React.createElement(GlobalEditorStyles, {
290
+ zIndex: zIndex + 1
291
+ }), /*#__PURE__*/React.createElement(TextEditorTheme, null), /*#__PURE__*/React.createElement(CKEditor, _extends({}, restProps, {
292
+ editor: ClassicEditor,
293
+ config: config,
294
+ disabled: disabled,
295
+ data: initialValueRef.current,
296
+ onChange: handleChange,
297
+ onReady: handleReady,
298
+ onBlur: handleBlur
299
+ })));
300
+ };
301
+ function hasPendingImageUploads(editor) {
302
+ try {
303
+ var _fileRepository$loade;
304
+ var fileRepository = editor.plugins.get(FileRepository);
305
+ return Array.from((_fileRepository$loade = fileRepository.loaders) !== null && _fileRepository$loade !== void 0 ? _fileRepository$loade : []).some(function (loader) {
306
+ return ['reading', 'uploading'].includes(String(loader.status));
307
+ });
308
+ } catch (_error) {
309
+ return false;
310
+ }
311
+ }
312
+ //# sourceMappingURL=CKTextEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CKTextEditor.js","names":["CKEditor","useToastAlertContext","ClassicEditor","FileRepository","React","Toast","useI18nContext","useZIndexContext","TextEditorOutput","debounce","useStickyToolbar","GlobalEditorStyles","StyledImageUploadWarningToast","StyledImageUploadWarningToastIcon","StyledTextEditor","TextEditorContext","TextEditorTheme","useCKEditorCss","useCKEditorFocusScope","useTabAsNavigation","addButtonDataAttributes","getDefaultConfig","richTextEditorUploadHandler","DEBOUNCE_MS","CKTextEditor","props","disabled","error","readonly","value","initialValue","onChange","onInit","onBlur","onKeyDown","propLocale","locale","onDirty","companyId","projectId","toolName","placeholder","rows","restProps","_objectWithoutProperties","_excluded","ariaLabel","ariaDescription","_useCKEditorCss","cssLoading","isLoading","editorRef","useRef","editorInstanceRef","initialValueRef","lastExternalValueRef","keyDownListenerRef","_React$useState","useState","_React$useState2","_slicedToArray","isDirtyState","setIsDirtyState","_React$useState3","_React$useState4","isEditorReady","setIsEditorReady","i18n","_useToastAlertContext","showToast","contextLocale","showImageUploadWarning","useCallback","message","custom","_ref","toastDismissProps","createElement","alignItems","padding","size","Text","Dismiss","_extends","t","useEffect","undefined","current","editor","editing","view","document","isFocused","getData","data","set","off","change","writer","viewEditableRoot","getRoot","setAttribute","removeAttribute","_useZIndexContext","zIndex","_React$useContext","useContext","features","enableStickyToolbar","stickyToolbar","enableTabAsNavigation","tabAsNavigation","imageUploadHandlers","useMemo","handlerOptions","upload","onError","onWarning","mergedConfig","defaultConfig","imageUpload","label","root","_objectSpread","bindKeyDownHandler","editorElement","keyDownListener","_event","domEvent","key","stopPropagation","on","debouncedChange","isDirty","trim","hasPendingUploads","hasPendingImageUploads","handleChange","handleReady","baseHeight","rowHeight","editorHeight","setStyle","concat","handleBlur","event","flush","enabled","_useTabAsNavigation","config","editorKey","ref","$ready","onReady","_fileRepository$loade","fileRepository","plugins","get","Array","from","loaders","some","loader","includes","String","status","_error"],"sources":["../../src/TextEditor/CKTextEditor.tsx"],"sourcesContent":["import { CKEditor } from '@ckeditor/ckeditor5-react'\nimport { useToastAlertContext } from '@procore/toast-alert'\nimport { ClassicEditor, FileRepository, type EventInfo } from 'ckeditor5'\nimport React from 'react'\n\nimport { Toast, useI18nContext, useZIndexContext } from '@procore/core-react'\nimport { TextEditorOutput } from '../TextEditorOutput'\nimport { debounce } from '../utils/debounce'\nimport { useStickyToolbar } from './StickyToolbar'\nimport {\n GlobalEditorStyles,\n StyledImageUploadWarningToast,\n StyledImageUploadWarningToastIcon,\n StyledTextEditor,\n} from './TextEditor.styles'\nimport type { KeyDownListener, TextEditorProps } from './TextEditor.types'\nimport { TextEditorContext } from './TextEditorProvider'\nimport { TextEditorTheme } from './textEditorTheming'\nimport { useCKEditorCss } from './useCKEditorCss'\nimport { useCKEditorFocusScope } from './useCKEditorFocusScope'\nimport { useTabAsNavigation } from './useTabAsNavigation'\nimport { addButtonDataAttributes, getDefaultConfig } from './utils'\nimport { richTextEditorUploadHandler } from './utils/richTextEditorHandlers'\n\nconst DEBOUNCE_MS = 300\n\nexport const CKTextEditor = (props: Readonly<TextEditorProps>) => {\n const {\n disabled,\n error,\n readonly,\n value,\n initialValue,\n onChange,\n onInit,\n onBlur,\n onKeyDown,\n locale: propLocale,\n onDirty,\n companyId,\n projectId,\n toolName,\n placeholder,\n rows,\n ...restProps\n } = props\n const ariaLabel = props['aria-label']\n const ariaDescription = props['aria-description']\n const { isLoading: cssLoading } = useCKEditorCss()\n useCKEditorFocusScope()\n\n const editorRef = React.useRef<HTMLDivElement>(null)\n const editorInstanceRef = React.useRef<ClassicEditor | null>(null)\n const initialValueRef = React.useRef<string>(value || initialValue || '')\n const lastExternalValueRef = React.useRef<string>(value || initialValue || '')\n const keyDownListenerRef = React.useRef<KeyDownListener | null>(null)\n const [isDirtyState, setIsDirtyState] = React.useState(false)\n const [isEditorReady, setIsEditorReady] = React.useState(false)\n const i18n = useI18nContext()\n const { showToast } = useToastAlertContext()\n const { locale: contextLocale } = i18n\n const locale = propLocale || contextLocale\n\n const showImageUploadWarning = React.useCallback(\n (message: string) => {\n showToast.custom(({ toastDismissProps }) => (\n <StyledImageUploadWarningToast alignItems=\"center\" padding=\"md xl\">\n <StyledImageUploadWarningToastIcon size=\"lg\" />\n <Toast.Text>{message}</Toast.Text>\n <Toast.Dismiss\n aria-label={i18n.t('textEditor.imageUpload.dismissWarning')}\n {...toastDismissProps}\n />\n </StyledImageUploadWarningToast>\n ))\n },\n [i18n, showToast]\n )\n\n React.useEffect(() => {\n if (value !== undefined) {\n lastExternalValueRef.current = value\n }\n }, [value])\n\n React.useEffect(() => {\n const editor = editorInstanceRef.current\n if (!isEditorReady || !editor || value === undefined) return\n if (editor.editing.view.document.isFocused) return\n if (value === editor.getData()) return\n\n editor.data.set(value)\n }, [value, isEditorReady])\n\n // Cleanup keydown listener on unmount\n React.useEffect(() => {\n return () => {\n if (keyDownListenerRef.current && editorInstanceRef.current) {\n editorInstanceRef.current.editing.view.document.off(\n 'keydown',\n keyDownListenerRef.current\n )\n keyDownListenerRef.current = null\n }\n }\n }, [])\n\n // Update aria-label when ariaLabel prop changes\n React.useEffect(() => {\n if (!isEditorReady || !editorInstanceRef.current) {\n return\n }\n\n const editor = editorInstanceRef.current\n editor.editing.view.change((writer) => {\n const viewEditableRoot = editor.editing.view.document.getRoot()\n if (viewEditableRoot) {\n if (ariaLabel) {\n writer.setAttribute('aria-label', ariaLabel, viewEditableRoot)\n } else {\n writer.removeAttribute('aria-label', viewEditableRoot)\n }\n }\n })\n }, [ariaLabel, isEditorReady])\n\n // Update aria-description when ariaDescription prop changes\n React.useEffect(() => {\n if (!isEditorReady || !editorInstanceRef.current) {\n return\n }\n\n const editor = editorInstanceRef.current\n editor.editing.view.change((writer) => {\n const viewEditableRoot = editor.editing.view.document.getRoot()\n if (viewEditableRoot) {\n if (ariaDescription) {\n writer.setAttribute(\n 'aria-description',\n ariaDescription,\n viewEditableRoot\n )\n } else {\n writer.removeAttribute('aria-description', viewEditableRoot)\n }\n }\n })\n }, [ariaDescription, isEditorReady])\n\n const { value: zIndex } = useZIndexContext()\n\n const { features } = React.useContext(TextEditorContext)\n const enableStickyToolbar = !!features?.stickyToolbar\n const enableTabAsNavigation = !!features?.tabAsNavigation\n\n const imageUploadHandlers = React.useMemo(() => {\n if (companyId === undefined || toolName === undefined) {\n return undefined\n }\n\n const handlerOptions = {\n companyId,\n projectId,\n toolName,\n }\n\n const upload = richTextEditorUploadHandler(handlerOptions)\n\n return {\n upload,\n i18n,\n onError: showToast.error,\n onWarning: showImageUploadWarning,\n }\n }, [\n companyId,\n i18n,\n projectId,\n showImageUploadWarning,\n showToast.error,\n toolName,\n ])\n\n const mergedConfig = React.useMemo(() => {\n const defaultConfig = getDefaultConfig(locale, {\n imageUpload: imageUploadHandlers,\n })\n\n // Set accessible label for the editable area if provided\n // This is used by screen readers to announce the editor's purpose\n if (ariaLabel) {\n defaultConfig.label = ariaLabel\n }\n\n if (placeholder) {\n defaultConfig.root = { ...defaultConfig.root, placeholder }\n }\n\n return defaultConfig\n }, [ariaLabel, imageUploadHandlers, locale, placeholder])\n\n const bindKeyDownHandler = React.useCallback(\n (editor: ClassicEditor) => {\n const editorElement = editor.editing.view.document.getRoot()\n if (!editorElement) {\n return\n }\n\n // Remove existing keydown listener\n if (keyDownListenerRef.current) {\n editor.editing.view.document.off('keydown', keyDownListenerRef.current)\n }\n\n // Create and store the new listener\n const keyDownListener = (\n _event: EventInfo,\n data: { domEvent: Event }\n ) => {\n const domEvent = data.domEvent as KeyboardEvent\n // Prevent Enter from propagating to parent forms so the\n // editor can handle newlines without accidentally submitting.\n if (domEvent.key === 'Enter') {\n domEvent.stopPropagation()\n }\n\n onKeyDown?.(domEvent, editor)\n }\n\n keyDownListenerRef.current = keyDownListener\n\n // Add the keydown listener\n editor.editing.view.document.on('keydown', keyDownListener)\n },\n [onKeyDown]\n )\n\n const debouncedChange = React.useRef(\n debounce((_event: EventInfo<string, unknown>, editor: ClassicEditor) => {\n const data = editor.getData()\n const isDirty = data !== lastExternalValueRef.current.trim()\n const hasPendingUploads = hasPendingImageUploads(editor)\n\n if (isDirty && !isDirtyState) {\n setIsDirtyState(true)\n onDirty?.()\n }\n\n if (hasPendingUploads) {\n return\n }\n\n onChange?.(data, isDirty)\n }, DEBOUNCE_MS)\n ).current\n\n const handleChange = debouncedChange\n\n const handleReady = (editor: ClassicEditor) => {\n addButtonDataAttributes(editor)\n editorInstanceRef.current = editor\n setIsEditorReady(true)\n\n // Set aria-label and aria-description on the editable area if provided\n // These provide accessible context to screen reader users\n editor.editing.view.change((writer) => {\n const viewEditableRoot = editor.editing.view.document.getRoot()\n if (viewEditableRoot) {\n if (ariaLabel) {\n writer.setAttribute('aria-label', ariaLabel, viewEditableRoot)\n }\n if (ariaDescription) {\n writer.setAttribute(\n 'aria-description',\n ariaDescription,\n viewEditableRoot\n )\n }\n if (rows !== undefined) {\n const baseHeight = 35\n const rowHeight = 22\n const editorHeight = rows * rowHeight + baseHeight\n writer.setStyle('min-height', `${editorHeight}px`, viewEditableRoot)\n writer.setStyle('max-height', `${editorHeight}px`, viewEditableRoot)\n }\n }\n })\n\n // Bind keydown handler when editor is ready\n bindKeyDownHandler(editor)\n\n onInit?.(editor)\n }\n\n const handleBlur = (\n event: EventInfo<string, unknown>,\n editor: ClassicEditor\n ) => {\n debouncedChange.flush()\n onBlur?.(event, editor)\n }\n\n useStickyToolbar({\n enabled: enableStickyToolbar,\n editor: isEditorReady ? editorInstanceRef.current : null,\n editorRef,\n cssLoading,\n })\n\n const { config } = useTabAsNavigation({\n config: mergedConfig,\n enabled: enableTabAsNavigation,\n editor: isEditorReady ? editorInstanceRef.current : null,\n })\n\n const editorKey = `${locale}-${enableTabAsNavigation}`\n\n if (cssLoading) {\n return null\n }\n\n if (readonly) {\n return (\n <TextEditorOutput\n value={value}\n aria-label={ariaLabel}\n aria-description={ariaDescription}\n />\n )\n }\n\n return (\n <StyledTextEditor\n ref={editorRef}\n key={editorKey}\n error={error}\n $ready={isEditorReady}\n aria-invalid={error ? 'true' : 'false'}\n >\n <GlobalEditorStyles zIndex={zIndex + 1} />\n <TextEditorTheme />\n <CKEditor\n {...restProps}\n editor={ClassicEditor}\n config={config}\n disabled={disabled}\n data={initialValueRef.current}\n onChange={handleChange}\n onReady={handleReady}\n onBlur={handleBlur}\n />\n </StyledTextEditor>\n )\n}\n\nfunction hasPendingImageUploads(editor: ClassicEditor) {\n try {\n const fileRepository = editor.plugins.get(FileRepository) as {\n loaders?: Iterable<{ status?: string }>\n }\n\n return Array.from(fileRepository.loaders ?? []).some((loader) =>\n ['reading', 'uploading'].includes(String(loader.status))\n )\n } catch (_error) {\n return false\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { ReactElement } from 'react';
2
+ import React from 'react';
3
+ export declare function FeatureFlagProvider({ children, }: Readonly<{
4
+ children: ReactElement;
5
+ }>): React.JSX.Element;