@procore/text-editor 0.5.1 → 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.
- package/.jest/ckeditorMock.js +47 -23
- package/CHANGELOG.md +19 -0
- package/README.md +96 -7
- package/dist/TextEditor/CKTextEditor.d.ts +3 -0
- package/dist/TextEditor/CKTextEditor.js +312 -0
- package/dist/TextEditor/CKTextEditor.js.map +1 -0
- package/dist/TextEditor/Providers/FeatureFlagProvider.d.ts +5 -0
- package/dist/TextEditor/Providers/FeatureFlagProvider.js +34 -0
- package/dist/TextEditor/Providers/FeatureFlagProvider.js.map +1 -0
- package/dist/TextEditor/TextEditor.d.ts +1 -1
- package/dist/TextEditor/TextEditor.js +16 -232
- package/dist/TextEditor/TextEditor.js.map +1 -1
- package/dist/TextEditor/TextEditor.styles.d.ts +14 -1
- package/dist/TextEditor/TextEditor.styles.js +18 -5
- package/dist/TextEditor/TextEditor.styles.js.map +1 -1
- package/dist/TextEditor/TextEditor.types.d.ts +82 -1
- package/dist/TextEditor/TextEditor.types.js.map +1 -1
- package/dist/TextEditor/TextEditorI18nProvider.d.ts +2 -0
- package/dist/TextEditor/TextEditorI18nProvider.js +27 -0
- package/dist/TextEditor/TextEditorI18nProvider.js.map +1 -0
- package/dist/TextEditor/TinyMCETextEditor.d.ts +3 -0
- package/dist/TextEditor/TinyMCETextEditor.js +63 -0
- package/dist/TextEditor/TinyMCETextEditor.js.map +1 -0
- package/dist/TextEditor/plugins/ProcoreImageUploadPlugin/ProcoreImageUploadPlugin.d.ts +48 -0
- package/dist/TextEditor/plugins/ProcoreImageUploadPlugin/ProcoreImageUploadPlugin.js +441 -0
- package/dist/TextEditor/plugins/ProcoreImageUploadPlugin/ProcoreImageUploadPlugin.js.map +1 -0
- package/dist/TextEditor/plugins/ProcoreImageUploadPlugin/index.d.ts +1 -0
- package/dist/TextEditor/plugins/ProcoreImageUploadPlugin/index.js +2 -0
- package/dist/TextEditor/plugins/ProcoreImageUploadPlugin/index.js.map +1 -0
- package/dist/TextEditor/utils/config.d.ts +6 -1
- package/dist/TextEditor/utils/config.js +23 -9
- package/dist/TextEditor/utils/config.js.map +1 -1
- package/dist/TextEditor/utils/imageAttributes.d.ts +6 -0
- package/dist/TextEditor/utils/imageAttributes.js +17 -0
- package/dist/TextEditor/utils/imageAttributes.js.map +1 -0
- package/dist/TextEditor/utils/imageUrls.d.ts +2 -0
- package/dist/TextEditor/utils/imageUrls.js +36 -0
- package/dist/TextEditor/utils/imageUrls.js.map +1 -0
- package/dist/TextEditor/utils/richTextEditorHandlers.d.ts +9 -0
- package/dist/TextEditor/utils/richTextEditorHandlers.js +234 -0
- package/dist/TextEditor/utils/richTextEditorHandlers.js.map +1 -0
- package/dist/TextEditorOutput/TextEditorOutput.styles.js +2 -2
- package/dist/TextEditorOutput/TextEditorOutput.styles.js.map +1 -1
- package/dist/TextEditorOutput/TextEditorOutput.utils.d.ts +1 -1
- package/dist/TextEditorOutput/TextEditorOutput.utils.js +150 -6
- package/dist/TextEditorOutput/TextEditorOutput.utils.js.map +1 -1
- package/dist/_typedoc/TextEditor/TextEditor.types.json +215 -17
- package/dist/_typedoc/TextEditor/TextEditorProvider.types.json +2 -2
- package/dist/_typedoc/TextEditorOutput/TextEditorOutput.types.json +3 -3
- package/dist/ckeditor5-premium-features.d.js +2 -0
- package/dist/ckeditor5-premium-features.d.js.map +1 -0
- package/dist/locales/en.json +17 -0
- package/dist/locales/pseudo.json +17 -0
- package/package.json +8 -3
package/.jest/ckeditorMock.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
63
|
+
MultiLevelList: class extends MockPlugin {},
|
|
47
64
|
PageBreak: class extends MockPlugin {},
|
|
48
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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,24 @@
|
|
|
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
|
+
|
|
3
22
|
## 0.5.1
|
|
4
23
|
|
|
5
24
|
### Patch 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,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,34 @@
|
|
|
1
|
+
import { getProcoreZone, getProviderParams } from '@procore/web-sdk-env-utils';
|
|
2
|
+
import { FeatureToggleProvider } from '@procore/web-sdk-feature-toggle';
|
|
3
|
+
import React, { useId, useMemo, useRef } from 'react';
|
|
4
|
+
var LAUNCH_DARKLY_KEYS = {
|
|
5
|
+
development: '570f37b4eee8b907140070c5',
|
|
6
|
+
staging: '570f2f6e348a2806d7006b6f',
|
|
7
|
+
production: '570e9423348a2806d7004354'
|
|
8
|
+
};
|
|
9
|
+
var getEnvironment = function getEnvironment() {
|
|
10
|
+
var _getProcoreZone;
|
|
11
|
+
return (_getProcoreZone = getProcoreZone(globalThis.location.hostname)) === null || _getProcoreZone === void 0 ? void 0 : _getProcoreZone.environment;
|
|
12
|
+
};
|
|
13
|
+
export function FeatureFlagProvider(_ref) {
|
|
14
|
+
var children = _ref.children;
|
|
15
|
+
var _getProviderParams = getProviderParams(),
|
|
16
|
+
companyId = _getProviderParams.companyId,
|
|
17
|
+
projectId = _getProviderParams.projectId;
|
|
18
|
+
var environment = getEnvironment();
|
|
19
|
+
var idRef = useRef(useId());
|
|
20
|
+
var context = useMemo(function () {
|
|
21
|
+
return {
|
|
22
|
+
user: {
|
|
23
|
+
email: idRef.current,
|
|
24
|
+
projectId: projectId,
|
|
25
|
+
companyId: companyId
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}, [projectId, companyId]);
|
|
29
|
+
return /*#__PURE__*/React.createElement(FeatureToggleProvider, {
|
|
30
|
+
apiKey: LAUNCH_DARKLY_KEYS[environment],
|
|
31
|
+
context: context
|
|
32
|
+
}, children);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=FeatureFlagProvider.js.map
|