@datarobot/design-system 28.3.4 → 28.5.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/cjs/chat/chat-message-body.js +9 -4
- package/cjs/chat/hocs.d.ts +1 -1
- package/cjs/chat/hocs.js +21 -22
- package/cjs/chat/index.d.ts +2 -0
- package/cjs/chat/index.js +14 -0
- package/cjs/text-editor/inline-text-editor.d.ts +3 -1
- package/cjs/text-editor/inline-text-editor.js +6 -2
- package/cjs/text-editor/text-editor-constants.d.ts +0 -2
- package/cjs/text-editor/text-editor-constants.js +0 -8
- package/cjs/text-editor/text-editor-content.d.ts +2 -1
- package/cjs/text-editor/text-editor-content.js +22 -2
- package/cjs/text-editor/text-editor-header.js +2 -9
- package/cjs/text-editor/text-editor-helpers.d.ts +1 -0
- package/cjs/text-editor/text-editor-helpers.js +22 -17
- package/cjs/text-editor/text-editor.d.ts +5 -1
- package/cjs/text-editor/text-editor.js +31 -6
- package/esm/chat/chat-message-body.js +9 -4
- package/esm/chat/hocs.d.ts +1 -1
- package/esm/chat/hocs.js +21 -22
- package/esm/chat/index.d.ts +2 -0
- package/esm/chat/index.js +2 -0
- package/esm/text-editor/inline-text-editor.d.ts +3 -1
- package/esm/text-editor/inline-text-editor.js +6 -2
- package/esm/text-editor/text-editor-constants.d.ts +0 -2
- package/esm/text-editor/text-editor-constants.js +0 -6
- package/esm/text-editor/text-editor-content.d.ts +2 -1
- package/esm/text-editor/text-editor-content.js +22 -2
- package/esm/text-editor/text-editor-header.js +3 -10
- package/esm/text-editor/text-editor-helpers.d.ts +1 -0
- package/esm/text-editor/text-editor-helpers.js +22 -18
- package/esm/text-editor/text-editor.d.ts +5 -1
- package/esm/text-editor/text-editor.js +33 -8
- package/js/bundle/bundle.js +175 -127
- package/js/bundle/bundle.min.js +1 -1
- package/js/bundle/index.d.ts +22 -5
- package/package.json +1 -1
- package/styles/index.css +16 -0
- package/styles/index.min.css +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useContext } from 'react';
|
|
2
|
+
import { Editor } from 'slate';
|
|
2
3
|
import classNames from 'classnames';
|
|
3
4
|
import TextEditorContext from './text-editor-context';
|
|
4
5
|
import { BLOCK_TYPES, MARK_TYPES } from './text-editor-constants';
|
|
@@ -146,14 +147,17 @@ export default function TextEditorContent({
|
|
|
146
147
|
elementRender = Element,
|
|
147
148
|
leafRender = Leaf,
|
|
148
149
|
onBlur = () => {},
|
|
149
|
-
onFocus = () => {}
|
|
150
|
+
onFocus = () => {},
|
|
151
|
+
maxLength
|
|
150
152
|
}) {
|
|
151
153
|
const {
|
|
152
154
|
slateReact
|
|
153
155
|
} = useContext(TextEditorContext);
|
|
154
156
|
const {
|
|
155
|
-
Editable
|
|
157
|
+
Editable,
|
|
158
|
+
useSlate
|
|
156
159
|
} = slateReact;
|
|
160
|
+
const editor = useSlate();
|
|
157
161
|
/* eslint-disable-next-line testing-library/render-result-naming-convention */
|
|
158
162
|
const ElementNode = elementRender;
|
|
159
163
|
/* eslint-disable-next-line testing-library/render-result-naming-convention */
|
|
@@ -190,6 +194,22 @@ export default function TextEditorContent({
|
|
|
190
194
|
renderLeaf: renderLeaf,
|
|
191
195
|
onBlur: onBlur,
|
|
192
196
|
onFocus: onFocus,
|
|
197
|
+
onDOMBeforeInput: event => {
|
|
198
|
+
// NOTE: copy-paste events handled in withMaxLength() plugin because Slate under some
|
|
199
|
+
// circumstances stops triggering onDOMBeforeInput event for copy-paste ¯\_(ツ)_/¯
|
|
200
|
+
|
|
201
|
+
// no need to do any calculations if maxLength is not set or if it's a delete event
|
|
202
|
+
if (maxLength && !event.inputType.startsWith('delete')) {
|
|
203
|
+
const currentTextLength = Editor.string(editor, []).length;
|
|
204
|
+
let newText = '';
|
|
205
|
+
if (!!event.data) {
|
|
206
|
+
newText = event.data;
|
|
207
|
+
}
|
|
208
|
+
if (currentTextLength + newText.length > maxLength) {
|
|
209
|
+
event.preventDefault();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
},
|
|
193
213
|
...ariaAttrs
|
|
194
214
|
})
|
|
195
215
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
2
|
import { faTrashCan } from '@fortawesome/free-solid-svg-icons/faTrashCan';
|
|
3
3
|
import { useTranslation } from '../hooks/use-translation';
|
|
4
4
|
import { ACCENT_TYPES, Button } from '../button';
|
|
@@ -21,7 +21,6 @@ export default function TextEditorHeader({
|
|
|
21
21
|
const {
|
|
22
22
|
t
|
|
23
23
|
} = useTranslation();
|
|
24
|
-
const [isDelete, setIsDelete] = useState(false);
|
|
25
24
|
const {
|
|
26
25
|
slate,
|
|
27
26
|
slateReact
|
|
@@ -33,13 +32,6 @@ export default function TextEditorHeader({
|
|
|
33
32
|
const editor = slateReact.useSlate();
|
|
34
33
|
const hasReadOnlyTitle = title && !isEditable;
|
|
35
34
|
const hasEditableTitle = isEditable && onChangeTitle;
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
if (isDelete) {
|
|
38
|
-
onChangeTitle('');
|
|
39
|
-
onDelete();
|
|
40
|
-
setIsDelete(false);
|
|
41
|
-
}
|
|
42
|
-
}, [isDelete]);
|
|
43
35
|
return /*#__PURE__*/_jsx("header", {
|
|
44
36
|
className: "text-editor-header",
|
|
45
37
|
"test-id": "text-editor-header",
|
|
@@ -63,7 +55,8 @@ export default function TextEditorHeader({
|
|
|
63
55
|
onClick: () => {
|
|
64
56
|
textEditorService.deleteContent(editor);
|
|
65
57
|
ReactEditor.focus(editor);
|
|
66
|
-
|
|
58
|
+
onChangeTitle('');
|
|
59
|
+
onDelete();
|
|
67
60
|
},
|
|
68
61
|
accentType: ACCENT_TYPES.ROUND_ICON,
|
|
69
62
|
tooltipText: t('Delete'),
|
|
@@ -10,6 +10,7 @@ export declare function isEmptyTextEditorValue(value: TextEditorValue[]): boolea
|
|
|
10
10
|
* @returns {boolean}
|
|
11
11
|
*/
|
|
12
12
|
export declare function isLongerTextEditorValueThan(value: TextEditorValue[] | TextEditorValueChild[], maxLength?: number): boolean;
|
|
13
|
+
export declare function getTextEditorValueLength(value: TextEditorValue[] | TextEditorValueChild[]): number;
|
|
13
14
|
/**
|
|
14
15
|
* @param {Array} value - the type of value is defined with TextEditorValue
|
|
15
16
|
* @param {number} maxTextLines
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import isEqual from 'lodash/isEqual';
|
|
2
|
-
import { BLOCK_TYPES, TEXT_EDITOR_DEFAULT_VALUE
|
|
2
|
+
import { BLOCK_TYPES, TEXT_EDITOR_DEFAULT_VALUE } from './text-editor-constants';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @param {Array} value - the type of value is defined with TextEditorValue
|
|
@@ -17,29 +17,33 @@ export function isEmptyTextEditorValue(value) {
|
|
|
17
17
|
export function isLongerTextEditorValueThan(value, maxLength = 120) {
|
|
18
18
|
let length = 0;
|
|
19
19
|
for (const record of value) {
|
|
20
|
-
if
|
|
21
|
-
length += record.text.length;
|
|
22
|
-
}
|
|
23
|
-
if (instanceOfContent(record) && !!record.children) {
|
|
24
|
-
for (const childRecord of record.children) {
|
|
25
|
-
if (!!childRecord.text) {
|
|
26
|
-
length += childRecord.text.length;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// We already know the result
|
|
30
|
-
if (length > maxLength) {
|
|
31
|
-
break;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// We already know the result
|
|
20
|
+
// if we reached maxLength - no need to continue
|
|
37
21
|
if (length > maxLength) {
|
|
38
22
|
break;
|
|
39
23
|
}
|
|
24
|
+
// if this is leaf node that has text - sum it up
|
|
25
|
+
if (record.hasOwnProperty('text') && !!record.text) {
|
|
26
|
+
length += record?.text?.length ?? 0;
|
|
27
|
+
} else if (record.hasOwnProperty('children') && !!record.children) {
|
|
28
|
+
// if this is a node that has children - calculate length of children
|
|
29
|
+
length += getTextEditorValueLength(record.children);
|
|
30
|
+
}
|
|
40
31
|
}
|
|
41
32
|
return length > maxLength;
|
|
42
33
|
}
|
|
34
|
+
export function getTextEditorValueLength(value) {
|
|
35
|
+
let length = 0;
|
|
36
|
+
for (const record of value) {
|
|
37
|
+
// if this is leaf node that has text - sum it up
|
|
38
|
+
if (record.hasOwnProperty('text') && !!record.text) {
|
|
39
|
+
length += record?.text?.length ?? 0;
|
|
40
|
+
} else if (record.hasOwnProperty('children') && !!record.children) {
|
|
41
|
+
// if this is a node that has children - iterate through them via recursion
|
|
42
|
+
length += getTextEditorValueLength(record.children);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return length;
|
|
46
|
+
}
|
|
43
47
|
|
|
44
48
|
/**
|
|
45
49
|
* @param {Array} value - the type of value is defined with TextEditorValue
|
|
@@ -38,6 +38,8 @@ export type TextEditorComponentProps = {
|
|
|
38
38
|
onBlur?: () => void;
|
|
39
39
|
deleteEditorContentButtonAriaLabel: string;
|
|
40
40
|
id: string;
|
|
41
|
+
maxLength?: number;
|
|
42
|
+
getCharactersCounterString?: (count: number) => string;
|
|
41
43
|
};
|
|
42
44
|
export type TextEditorProps = {
|
|
43
45
|
name: string;
|
|
@@ -94,9 +96,11 @@ export type TextEditorProps = {
|
|
|
94
96
|
linkInputLabelText?: string;
|
|
95
97
|
deleteEditorContentButtonAriaLabel: string;
|
|
96
98
|
id?: string;
|
|
99
|
+
maxLength?: number;
|
|
100
|
+
getCharactersCounterString?: (count: number) => string;
|
|
97
101
|
};
|
|
98
102
|
/**
|
|
99
103
|
* @midnight-gray-supported
|
|
100
104
|
* @alpine-light-supported
|
|
101
105
|
* */
|
|
102
|
-
export default function TextEditor({ name, header, type, value, initialValue, validity, label, placeholder, autoFocus, readOnly, testId, contentTestId, contentAriaLabel, className, height, enabledTools, toolbarPosition, elementRender, leafRender, onChange, onCancel, onSave, tooltipTextBold, tooltipTextItalic, tooltipTextUnderline, tooltipTextSuperscript, tooltipTextLink, tooltipTextRemoveLink, tooltipTextBulletedList, tooltipTextNumberedList, tooltipTextClearFormatting, tooltipTextCode, urlIsRequiredMessage, enterValidUrlMessage, saveButtonText, headingLargeText, headingMediumText, headingSmallText, regularText, validationErrorWarningMessage, linkInputLabelText, deleteEditorContentButtonAriaLabel, id, }: TextEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
106
|
+
export default function TextEditor({ name, header, type, value, initialValue, validity, label, placeholder, autoFocus, readOnly, testId, contentTestId, contentAriaLabel, className, height, enabledTools, toolbarPosition, elementRender, leafRender, onChange, onCancel, onSave, tooltipTextBold, tooltipTextItalic, tooltipTextUnderline, tooltipTextSuperscript, tooltipTextLink, tooltipTextRemoveLink, tooltipTextBulletedList, tooltipTextNumberedList, tooltipTextClearFormatting, tooltipTextCode, urlIsRequiredMessage, enterValidUrlMessage, saveButtonText, headingLargeText, headingMediumText, headingSmallText, regularText, validationErrorWarningMessage, linkInputLabelText, deleteEditorContentButtonAriaLabel, id, maxLength, getCharactersCounterString, }: TextEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import React, { useMemo, useRef, useContext, useState, useEffect } from 'react';
|
|
1
|
+
import React, { useMemo, useRef, useContext, useState, useEffect, useCallback } from 'react';
|
|
2
2
|
import classnames from 'classnames';
|
|
3
3
|
import isEqual from 'lodash-es/isEqual';
|
|
4
4
|
import { withHistory } from 'slate-history';
|
|
5
5
|
import { ValidityMessages, getErrorAriaAttributes, useDefaultValidationValues } from '../form-field';
|
|
6
|
-
import { resetNodes } from '../chat';
|
|
6
|
+
import { resetNodes, CharactersCounter, withMaxLength } from '../chat';
|
|
7
7
|
import { TextEditorToolbar } from './text-editor-toolbar';
|
|
8
8
|
import TextEditorContext from './text-editor-context';
|
|
9
9
|
import TextEditorHeader from './text-editor-header';
|
|
@@ -12,6 +12,8 @@ import withLinks from './hooks/with-links';
|
|
|
12
12
|
import TextEditorLoader from './text-editor-loader';
|
|
13
13
|
import { TEXT_EDITOR_DEFAULT_VALUE, TEXT_EDITOR_DEFAULT_TOOLS, TOOLBAR_POSITION, INLINE_EDITOR_TYPES, HEADER_HEIGHT } from './text-editor-constants';
|
|
14
14
|
import { useTextEditorDefaultTranslations } from './text-editor-hooks';
|
|
15
|
+
import { getTextEditorValueLength } from './text-editor-helpers';
|
|
16
|
+
import { useTranslation } from '../hooks/use-translation';
|
|
15
17
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
16
18
|
function TextEditorComponent({
|
|
17
19
|
name,
|
|
@@ -38,8 +40,13 @@ function TextEditorComponent({
|
|
|
38
40
|
leafRender = ContentLeaf,
|
|
39
41
|
onChange = () => {},
|
|
40
42
|
deleteEditorContentButtonAriaLabel,
|
|
41
|
-
id = ''
|
|
43
|
+
id = '',
|
|
44
|
+
maxLength,
|
|
45
|
+
getCharactersCounterString
|
|
42
46
|
}) {
|
|
47
|
+
const {
|
|
48
|
+
t
|
|
49
|
+
} = useTranslation();
|
|
43
50
|
const {
|
|
44
51
|
slate,
|
|
45
52
|
slateReact
|
|
@@ -54,7 +61,15 @@ function TextEditorComponent({
|
|
|
54
61
|
withReact,
|
|
55
62
|
Slate
|
|
56
63
|
} = slateReact;
|
|
57
|
-
const
|
|
64
|
+
const getCharactersCounterStringText = useCallback(count => {
|
|
65
|
+
if (getCharactersCounterString) {
|
|
66
|
+
return getCharactersCounterString(count);
|
|
67
|
+
}
|
|
68
|
+
return t('{{count}} characters remaining', {
|
|
69
|
+
count
|
|
70
|
+
});
|
|
71
|
+
}, [getCharactersCounterString]);
|
|
72
|
+
const editor = useMemo(() => withMaxLength(maxLength)(withLinks(withHistory(withReact(createEditor())))), [maxLength]);
|
|
58
73
|
const selection = useRef(null);
|
|
59
74
|
const slateValue = value || initialValue || TEXT_EDITOR_DEFAULT_VALUE;
|
|
60
75
|
useEffect(() => {
|
|
@@ -119,7 +134,8 @@ function TextEditorComponent({
|
|
|
119
134
|
hasDeleteButton: header.hasDeleteButton,
|
|
120
135
|
onDelete: header.onDelete,
|
|
121
136
|
deleteEditorContentButtonAriaLabel: deleteEditorContentButtonAriaLabel,
|
|
122
|
-
inputAriaLabel: header.inputAriaLabel
|
|
137
|
+
inputAriaLabel: header.inputAriaLabel,
|
|
138
|
+
maxLength: header.maxLength
|
|
123
139
|
}), toolbarPosition === TOOLBAR_POSITION.TOP && toolbarMemo, /*#__PURE__*/_jsx(TextEditorContent, {
|
|
124
140
|
placeholder: placeholder,
|
|
125
141
|
readOnly: readOnly,
|
|
@@ -129,9 +145,14 @@ function TextEditorComponent({
|
|
|
129
145
|
leafRender: leafRender,
|
|
130
146
|
onBlur: saveSelection,
|
|
131
147
|
onFocus: recoverSelection,
|
|
132
|
-
ariaAttrs: ariaAttrs
|
|
148
|
+
ariaAttrs: ariaAttrs,
|
|
149
|
+
maxLength: maxLength
|
|
133
150
|
}), toolbarPosition === TOOLBAR_POSITION.BOTTOM && toolbarMemo]
|
|
134
151
|
})
|
|
152
|
+
}), maxLength && /*#__PURE__*/_jsx(CharactersCounter, {
|
|
153
|
+
currLength: getTextEditorValueLength(slateValue),
|
|
154
|
+
maxLength: maxLength,
|
|
155
|
+
getCharactersCounterString: getCharactersCounterStringText
|
|
135
156
|
}), validity && /*#__PURE__*/_jsx(ValidityMessages, {
|
|
136
157
|
validity: validity,
|
|
137
158
|
validationTestId: `${name}-validity`,
|
|
@@ -186,7 +207,9 @@ export default function TextEditor({
|
|
|
186
207
|
validationErrorWarningMessage,
|
|
187
208
|
linkInputLabelText,
|
|
188
209
|
deleteEditorContentButtonAriaLabel,
|
|
189
|
-
id = ''
|
|
210
|
+
id = '',
|
|
211
|
+
maxLength,
|
|
212
|
+
getCharactersCounterString
|
|
190
213
|
}) {
|
|
191
214
|
const [isFocused, setIsFocused] = useState(false);
|
|
192
215
|
const defaultTranslations = useTextEditorDefaultTranslations();
|
|
@@ -244,7 +267,9 @@ export default function TextEditor({
|
|
|
244
267
|
leafRender: leafRender,
|
|
245
268
|
onChange: onChange,
|
|
246
269
|
deleteEditorContentButtonAriaLabel: deleteEditorContentButtonAriaLabel,
|
|
247
|
-
id: id
|
|
270
|
+
id: id,
|
|
271
|
+
maxLength: maxLength,
|
|
272
|
+
getCharactersCounterString: getCharactersCounterString
|
|
248
273
|
})
|
|
249
274
|
})
|
|
250
275
|
});
|