@datarobot/design-system 28.4.0 → 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 +10 -0
- package/styles/index.min.css +1 -1
|
@@ -152,10 +152,15 @@ function ChatMessageBodyBase({
|
|
|
152
152
|
// but there we prevent it only from updating slate inner state, html input still
|
|
153
153
|
// adds entered text to the DOM input field causing buggy behavior: https://datarobot.atlassian.net/browse/TESTLIO-2963
|
|
154
154
|
// preventing default from the onDOMBeforeInput won't add new character to the DOM input field.
|
|
155
|
-
// we still
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
// we still want to leave logic in hocs.ts because it covers copy+paste case, as for some reason
|
|
156
|
+
// under certain circumstances onDOMBeforeInput stops being triggering for copy+paste so having there is safer
|
|
157
|
+
|
|
158
|
+
// we want to always allow deleting text
|
|
159
|
+
if (!event.inputType.startsWith('delete')) {
|
|
160
|
+
const existingMentionsCount = (0, _utils.getMentionsCount)(editor.children);
|
|
161
|
+
if (!!event.data && _slate.Editor.string(editor, []).length + existingMentionsCount + (event?.data?.length ?? 0) > maxLength) {
|
|
162
|
+
event.preventDefault();
|
|
163
|
+
}
|
|
159
164
|
}
|
|
160
165
|
}
|
|
161
166
|
})
|
package/cjs/chat/hocs.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ReactEditor } from 'slate-react';
|
|
2
|
-
export declare const withMaxLength: (maxLength: number) => (editor: ReactEditor) => ReactEditor;
|
|
2
|
+
export declare const withMaxLength: (maxLength: number | undefined) => (editor: ReactEditor) => ReactEditor;
|
|
3
3
|
export declare const withMentions: (editor: any) => any;
|
package/cjs/chat/hocs.js
CHANGED
|
@@ -8,31 +8,30 @@ var _slate = require("slate");
|
|
|
8
8
|
var _utils = require("./utils");
|
|
9
9
|
const withMaxLength = maxLength => function Plugin(editor) {
|
|
10
10
|
const {
|
|
11
|
-
insertText,
|
|
12
11
|
insertData
|
|
13
12
|
} = editor;
|
|
14
13
|
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
}
|
|
14
|
+
// usual text insertion prevention is handled in onDOMBeforeInput event on the Editable component
|
|
15
|
+
// this case prevents copy-paste of text that would exceed the maxLength
|
|
16
|
+
// copy-paste could have been also handled in onDOMBeforeInput, but for
|
|
17
|
+
// some reason sometimes Slate stops triggering onDOMBeforeInput for copy-paste ¯\_(ツ)_/¯
|
|
18
|
+
if (maxLength) {
|
|
19
|
+
editor.insertData = data => {
|
|
20
|
+
const existingMentionsCount = (0, _utils.getMentionsCount)(editor.children);
|
|
21
|
+
// other methods for inserting text does not take into account new lines, so when copy-pasting
|
|
22
|
+
// we should also remove them, otherwise there is a bug when entered text cannot be cut and pasted
|
|
23
|
+
// because on paste text length is considered longer due to \n characters
|
|
24
|
+
const text = data.getData('text/plain')?.replace(/\n/g, '');
|
|
25
|
+
const html = data.getData('text/html');
|
|
26
|
+
const parsedHtml = new DOMParser().parseFromString(html, 'text/html');
|
|
27
|
+
const mentionNodes = parsedHtml.querySelectorAll('.mention');
|
|
28
|
+
const mentionsText = Array.from(mentionNodes).map(node => node.innerText).join('');
|
|
29
|
+
const itemsLengthToBeInserted = mentionNodes.length ? text.length - mentionsText.length : text.length;
|
|
30
|
+
if (_slate.Editor.string(editor, []).length + itemsLengthToBeInserted + existingMentionsCount <= maxLength) {
|
|
31
|
+
insertData(data);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
36
35
|
return editor;
|
|
37
36
|
};
|
|
38
37
|
exports.withMaxLength = withMaxLength;
|
package/cjs/chat/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { Chat } from './chat';
|
|
2
|
+
export { CharactersCounter } from './characters-counter';
|
|
2
3
|
export { isMentionElement, resetNodes } from './utils';
|
|
4
|
+
export { withMaxLength } from './hocs';
|
|
3
5
|
export { DEFAULT_VALUE, NEW_ITEM_KEY } from './constants';
|
|
4
6
|
export type { ChatMessageItem, ActiveItems } from './types';
|
|
5
7
|
export type { ChatProps } from './chat';
|
package/cjs/chat/index.js
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
Object.defineProperty(exports, "CharactersCounter", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _charactersCounter.CharactersCounter;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
6
12
|
Object.defineProperty(exports, "Chat", {
|
|
7
13
|
enumerable: true,
|
|
8
14
|
get: function () {
|
|
@@ -33,6 +39,14 @@ Object.defineProperty(exports, "resetNodes", {
|
|
|
33
39
|
return _utils.resetNodes;
|
|
34
40
|
}
|
|
35
41
|
});
|
|
42
|
+
Object.defineProperty(exports, "withMaxLength", {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: function () {
|
|
45
|
+
return _hocs.withMaxLength;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
36
48
|
var _chat = require("./chat");
|
|
49
|
+
var _charactersCounter = require("./characters-counter");
|
|
37
50
|
var _utils = require("./utils");
|
|
51
|
+
var _hocs = require("./hocs");
|
|
38
52
|
var _constants = require("./constants");
|
|
@@ -24,5 +24,7 @@ export type InlineTextEditorProps = {
|
|
|
24
24
|
readOnly?: boolean;
|
|
25
25
|
deleteEditorContentButtonAriaLabel?: string;
|
|
26
26
|
autoFocus?: boolean;
|
|
27
|
+
maxLength?: number;
|
|
28
|
+
getCharactersCounterString?: (count: number) => string;
|
|
27
29
|
};
|
|
28
|
-
export default function InlineTextEditor({ name, type, readOnly, initialValue, placeholder, ariaLabel, className, enabledTools, header, toolbarPosition, height, onCancel, onSave, autoFocus, deleteEditorContentButtonAriaLabel, }: InlineTextEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
30
|
+
export default function InlineTextEditor({ name, type, readOnly, initialValue, placeholder, ariaLabel, className, enabledTools, header, toolbarPosition, height, onCancel, onSave, autoFocus, deleteEditorContentButtonAriaLabel, maxLength, getCharactersCounterString, }: InlineTextEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -30,7 +30,9 @@ function InlineTextEditor({
|
|
|
30
30
|
onCancel,
|
|
31
31
|
onSave,
|
|
32
32
|
autoFocus = false,
|
|
33
|
-
deleteEditorContentButtonAriaLabel
|
|
33
|
+
deleteEditorContentButtonAriaLabel,
|
|
34
|
+
maxLength,
|
|
35
|
+
getCharactersCounterString
|
|
34
36
|
}) {
|
|
35
37
|
const {
|
|
36
38
|
t
|
|
@@ -102,7 +104,9 @@ function InlineTextEditor({
|
|
|
102
104
|
height: height,
|
|
103
105
|
testId: "inline-text-editor-edit",
|
|
104
106
|
contentAriaLabel: ariaLabel || titleValue,
|
|
105
|
-
deleteEditorContentButtonAriaLabel: deleteEditorContentButtonAriaLabel ?? t('Delete Editor Content')
|
|
107
|
+
deleteEditorContentButtonAriaLabel: deleteEditorContentButtonAriaLabel ?? t('Delete Editor Content'),
|
|
108
|
+
maxLength: maxLength,
|
|
109
|
+
getCharactersCounterString: getCharactersCounterString
|
|
106
110
|
}) : /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
107
111
|
"test-id": "inline-text-editor-view-container",
|
|
108
112
|
className: (0, _classnames.default)('inline-text-editor-view-container', isFlex && 'is-flex'),
|
|
@@ -74,8 +74,6 @@ export interface SlateReact {
|
|
|
74
74
|
Slate: FC<SlateComponent>;
|
|
75
75
|
withReact: typeof withReact;
|
|
76
76
|
}
|
|
77
|
-
export declare function instanceOfContentChild(obj: TextEditorValue | TextEditorValueChild): obj is TextEditorValueChild;
|
|
78
|
-
export declare function instanceOfContent(obj: TextEditorValue | TextEditorValueChild): obj is TextEditorValue;
|
|
79
77
|
export declare const TEXT_EDITOR_DEFAULT_VALUE: TextEditorValue[];
|
|
80
78
|
export declare const TEXT_EDITOR_DEFAULT_TOOLS: TextEditorTool[];
|
|
81
79
|
export interface TextEditorToolbarLabels {
|
|
@@ -4,8 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.TOOLBAR_POSITION = exports.TEXT_EDITOR_TOOLS = exports.TEXT_EDITOR_DEFAULT_VALUE = exports.TEXT_EDITOR_DEFAULT_TOOLS = exports.MARK_TYPES = exports.INLINE_EDITOR_TYPES = exports.HEADER_TITLE_MAX_LENGTH = exports.HEADER_HEIGHT = exports.BLOCK_TYPES = void 0;
|
|
7
|
-
exports.instanceOfContent = instanceOfContent;
|
|
8
|
-
exports.instanceOfContentChild = instanceOfContentChild;
|
|
9
7
|
const MARK_TYPES = exports.MARK_TYPES = {
|
|
10
8
|
BOLD: 'bold',
|
|
11
9
|
ITALIC: 'italic',
|
|
@@ -39,12 +37,6 @@ const TEXT_EDITOR_TOOLS = exports.TEXT_EDITOR_TOOLS = {
|
|
|
39
37
|
CODE: 'code',
|
|
40
38
|
CLEAR_FORMATTING: 'clear-formatting'
|
|
41
39
|
};
|
|
42
|
-
function instanceOfContentChild(obj) {
|
|
43
|
-
return 'text' in obj;
|
|
44
|
-
}
|
|
45
|
-
function instanceOfContent(obj) {
|
|
46
|
-
return !('text' in obj);
|
|
47
|
-
}
|
|
48
40
|
const TEXT_EDITOR_DEFAULT_VALUE = exports.TEXT_EDITOR_DEFAULT_VALUE = [{
|
|
49
41
|
type: BLOCK_TYPES.REGULAR_TEXT,
|
|
50
42
|
children: [{
|
|
@@ -35,5 +35,6 @@ export type TextEditorContentProps = {
|
|
|
35
35
|
leafRender?: FC<LeafProps>;
|
|
36
36
|
onBlur?: () => void;
|
|
37
37
|
onFocus?: () => void;
|
|
38
|
+
maxLength?: number;
|
|
38
39
|
};
|
|
39
|
-
export default function TextEditorContent({ ariaAttrs, testId, placeholder, readOnly, autoFocus, isScrollable, elementRender, leafRender, onBlur, onFocus, }: TextEditorContentProps): import("react/jsx-runtime").JSX.Element;
|
|
40
|
+
export default function TextEditorContent({ ariaAttrs, testId, placeholder, readOnly, autoFocus, isScrollable, elementRender, leafRender, onBlur, onFocus, maxLength, }: TextEditorContentProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.Placeholder = exports.Leaf = exports.Element = void 0;
|
|
7
7
|
exports.default = TextEditorContent;
|
|
8
8
|
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
+
var _slate = require("slate");
|
|
9
10
|
var _classnames = _interopRequireDefault(require("classnames"));
|
|
10
11
|
var _textEditorContext = _interopRequireDefault(require("./text-editor-context"));
|
|
11
12
|
var _textEditorConstants = require("./text-editor-constants");
|
|
@@ -158,14 +159,17 @@ function TextEditorContent({
|
|
|
158
159
|
elementRender = Element,
|
|
159
160
|
leafRender = Leaf,
|
|
160
161
|
onBlur = () => {},
|
|
161
|
-
onFocus = () => {}
|
|
162
|
+
onFocus = () => {},
|
|
163
|
+
maxLength
|
|
162
164
|
}) {
|
|
163
165
|
const {
|
|
164
166
|
slateReact
|
|
165
167
|
} = (0, _react.useContext)(_textEditorContext.default);
|
|
166
168
|
const {
|
|
167
|
-
Editable
|
|
169
|
+
Editable,
|
|
170
|
+
useSlate
|
|
168
171
|
} = slateReact;
|
|
172
|
+
const editor = useSlate();
|
|
169
173
|
/* eslint-disable-next-line testing-library/render-result-naming-convention */
|
|
170
174
|
const ElementNode = elementRender;
|
|
171
175
|
/* eslint-disable-next-line testing-library/render-result-naming-convention */
|
|
@@ -202,6 +206,22 @@ function TextEditorContent({
|
|
|
202
206
|
renderLeaf: renderLeaf,
|
|
203
207
|
onBlur: onBlur,
|
|
204
208
|
onFocus: onFocus,
|
|
209
|
+
onDOMBeforeInput: event => {
|
|
210
|
+
// NOTE: copy-paste events handled in withMaxLength() plugin because Slate under some
|
|
211
|
+
// circumstances stops triggering onDOMBeforeInput event for copy-paste ¯\_(ツ)_/¯
|
|
212
|
+
|
|
213
|
+
// no need to do any calculations if maxLength is not set or if it's a delete event
|
|
214
|
+
if (maxLength && !event.inputType.startsWith('delete')) {
|
|
215
|
+
const currentTextLength = _slate.Editor.string(editor, []).length;
|
|
216
|
+
let newText = '';
|
|
217
|
+
if (!!event.data) {
|
|
218
|
+
newText = event.data;
|
|
219
|
+
}
|
|
220
|
+
if (currentTextLength + newText.length > maxLength) {
|
|
221
|
+
event.preventDefault();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
},
|
|
205
225
|
...ariaAttrs
|
|
206
226
|
})
|
|
207
227
|
});
|
|
@@ -29,7 +29,6 @@ function TextEditorHeader({
|
|
|
29
29
|
const {
|
|
30
30
|
t
|
|
31
31
|
} = (0, _useTranslation.useTranslation)();
|
|
32
|
-
const [isDelete, setIsDelete] = (0, _react.useState)(false);
|
|
33
32
|
const {
|
|
34
33
|
slate,
|
|
35
34
|
slateReact
|
|
@@ -41,13 +40,6 @@ function TextEditorHeader({
|
|
|
41
40
|
const editor = slateReact.useSlate();
|
|
42
41
|
const hasReadOnlyTitle = title && !isEditable;
|
|
43
42
|
const hasEditableTitle = isEditable && onChangeTitle;
|
|
44
|
-
(0, _react.useEffect)(() => {
|
|
45
|
-
if (isDelete) {
|
|
46
|
-
onChangeTitle('');
|
|
47
|
-
onDelete();
|
|
48
|
-
setIsDelete(false);
|
|
49
|
-
}
|
|
50
|
-
}, [isDelete]);
|
|
51
43
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)("header", {
|
|
52
44
|
className: "text-editor-header",
|
|
53
45
|
"test-id": "text-editor-header",
|
|
@@ -71,7 +63,8 @@ function TextEditorHeader({
|
|
|
71
63
|
onClick: () => {
|
|
72
64
|
textEditorService.deleteContent(editor);
|
|
73
65
|
ReactEditor.focus(editor);
|
|
74
|
-
|
|
66
|
+
onChangeTitle('');
|
|
67
|
+
onDelete();
|
|
75
68
|
},
|
|
76
69
|
accentType: _button.ACCENT_TYPES.ROUND_ICON,
|
|
77
70
|
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
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.convertTextToTextEditorValue = convertTextToTextEditorValue;
|
|
7
|
+
exports.getTextEditorValueLength = getTextEditorValueLength;
|
|
7
8
|
exports.hasGreaterTextEditorLinesThan = hasGreaterTextEditorLinesThan;
|
|
8
9
|
exports.isEmptyTextEditorValue = isEmptyTextEditorValue;
|
|
9
10
|
exports.isLongerTextEditorValueThan = isLongerTextEditorValueThan;
|
|
@@ -26,29 +27,33 @@ function isEmptyTextEditorValue(value) {
|
|
|
26
27
|
function isLongerTextEditorValueThan(value, maxLength = 120) {
|
|
27
28
|
let length = 0;
|
|
28
29
|
for (const record of value) {
|
|
29
|
-
if
|
|
30
|
-
length += record.text.length;
|
|
31
|
-
}
|
|
32
|
-
if ((0, _textEditorConstants.instanceOfContent)(record) && !!record.children) {
|
|
33
|
-
for (const childRecord of record.children) {
|
|
34
|
-
if (!!childRecord.text) {
|
|
35
|
-
length += childRecord.text.length;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// We already know the result
|
|
39
|
-
if (length > maxLength) {
|
|
40
|
-
break;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// We already know the result
|
|
30
|
+
// if we reached maxLength - no need to continue
|
|
46
31
|
if (length > maxLength) {
|
|
47
32
|
break;
|
|
48
33
|
}
|
|
34
|
+
// if this is leaf node that has text - sum it up
|
|
35
|
+
if (record.hasOwnProperty('text') && !!record.text) {
|
|
36
|
+
length += record?.text?.length ?? 0;
|
|
37
|
+
} else if (record.hasOwnProperty('children') && !!record.children) {
|
|
38
|
+
// if this is a node that has children - calculate length of children
|
|
39
|
+
length += getTextEditorValueLength(record.children);
|
|
40
|
+
}
|
|
49
41
|
}
|
|
50
42
|
return length > maxLength;
|
|
51
43
|
}
|
|
44
|
+
function getTextEditorValueLength(value) {
|
|
45
|
+
let length = 0;
|
|
46
|
+
for (const record of value) {
|
|
47
|
+
// if this is leaf node that has text - sum it up
|
|
48
|
+
if (record.hasOwnProperty('text') && !!record.text) {
|
|
49
|
+
length += record?.text?.length ?? 0;
|
|
50
|
+
} else if (record.hasOwnProperty('children') && !!record.children) {
|
|
51
|
+
// if this is a node that has children - iterate through them via recursion
|
|
52
|
+
length += getTextEditorValueLength(record.children);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return length;
|
|
56
|
+
}
|
|
52
57
|
|
|
53
58
|
/**
|
|
54
59
|
* @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;
|
|
@@ -18,6 +18,8 @@ var _withLinks = _interopRequireDefault(require("./hooks/with-links"));
|
|
|
18
18
|
var _textEditorLoader = _interopRequireDefault(require("./text-editor-loader"));
|
|
19
19
|
var _textEditorConstants = require("./text-editor-constants");
|
|
20
20
|
var _textEditorHooks = require("./text-editor-hooks");
|
|
21
|
+
var _textEditorHelpers = require("./text-editor-helpers");
|
|
22
|
+
var _useTranslation = require("../hooks/use-translation");
|
|
21
23
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
22
24
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
23
25
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
@@ -46,8 +48,13 @@ function TextEditorComponent({
|
|
|
46
48
|
leafRender = _textEditorContent.Leaf,
|
|
47
49
|
onChange = () => {},
|
|
48
50
|
deleteEditorContentButtonAriaLabel,
|
|
49
|
-
id = ''
|
|
51
|
+
id = '',
|
|
52
|
+
maxLength,
|
|
53
|
+
getCharactersCounterString
|
|
50
54
|
}) {
|
|
55
|
+
const {
|
|
56
|
+
t
|
|
57
|
+
} = (0, _useTranslation.useTranslation)();
|
|
51
58
|
const {
|
|
52
59
|
slate,
|
|
53
60
|
slateReact
|
|
@@ -62,7 +69,15 @@ function TextEditorComponent({
|
|
|
62
69
|
withReact,
|
|
63
70
|
Slate
|
|
64
71
|
} = slateReact;
|
|
65
|
-
const
|
|
72
|
+
const getCharactersCounterStringText = (0, _react.useCallback)(count => {
|
|
73
|
+
if (getCharactersCounterString) {
|
|
74
|
+
return getCharactersCounterString(count);
|
|
75
|
+
}
|
|
76
|
+
return t('{{count}} characters remaining', {
|
|
77
|
+
count
|
|
78
|
+
});
|
|
79
|
+
}, [getCharactersCounterString]);
|
|
80
|
+
const editor = (0, _react.useMemo)(() => (0, _chat.withMaxLength)(maxLength)((0, _withLinks.default)((0, _slateHistory.withHistory)(withReact(createEditor())))), [maxLength]);
|
|
66
81
|
const selection = (0, _react.useRef)(null);
|
|
67
82
|
const slateValue = value || initialValue || _textEditorConstants.TEXT_EDITOR_DEFAULT_VALUE;
|
|
68
83
|
(0, _react.useEffect)(() => {
|
|
@@ -127,7 +142,8 @@ function TextEditorComponent({
|
|
|
127
142
|
hasDeleteButton: header.hasDeleteButton,
|
|
128
143
|
onDelete: header.onDelete,
|
|
129
144
|
deleteEditorContentButtonAriaLabel: deleteEditorContentButtonAriaLabel,
|
|
130
|
-
inputAriaLabel: header.inputAriaLabel
|
|
145
|
+
inputAriaLabel: header.inputAriaLabel,
|
|
146
|
+
maxLength: header.maxLength
|
|
131
147
|
}), toolbarPosition === _textEditorConstants.TOOLBAR_POSITION.TOP && toolbarMemo, /*#__PURE__*/(0, _jsxRuntime.jsx)(_textEditorContent.default, {
|
|
132
148
|
placeholder: placeholder,
|
|
133
149
|
readOnly: readOnly,
|
|
@@ -137,9 +153,14 @@ function TextEditorComponent({
|
|
|
137
153
|
leafRender: leafRender,
|
|
138
154
|
onBlur: saveSelection,
|
|
139
155
|
onFocus: recoverSelection,
|
|
140
|
-
ariaAttrs: ariaAttrs
|
|
156
|
+
ariaAttrs: ariaAttrs,
|
|
157
|
+
maxLength: maxLength
|
|
141
158
|
}), toolbarPosition === _textEditorConstants.TOOLBAR_POSITION.BOTTOM && toolbarMemo]
|
|
142
159
|
})
|
|
160
|
+
}), maxLength && /*#__PURE__*/(0, _jsxRuntime.jsx)(_chat.CharactersCounter, {
|
|
161
|
+
currLength: (0, _textEditorHelpers.getTextEditorValueLength)(slateValue),
|
|
162
|
+
maxLength: maxLength,
|
|
163
|
+
getCharactersCounterString: getCharactersCounterStringText
|
|
143
164
|
}), validity && /*#__PURE__*/(0, _jsxRuntime.jsx)(_formField.ValidityMessages, {
|
|
144
165
|
validity: validity,
|
|
145
166
|
validationTestId: `${name}-validity`,
|
|
@@ -194,7 +215,9 @@ function TextEditor({
|
|
|
194
215
|
validationErrorWarningMessage,
|
|
195
216
|
linkInputLabelText,
|
|
196
217
|
deleteEditorContentButtonAriaLabel,
|
|
197
|
-
id = ''
|
|
218
|
+
id = '',
|
|
219
|
+
maxLength,
|
|
220
|
+
getCharactersCounterString
|
|
198
221
|
}) {
|
|
199
222
|
const [isFocused, setIsFocused] = (0, _react.useState)(false);
|
|
200
223
|
const defaultTranslations = (0, _textEditorHooks.useTextEditorDefaultTranslations)();
|
|
@@ -252,7 +275,9 @@ function TextEditor({
|
|
|
252
275
|
leafRender: leafRender,
|
|
253
276
|
onChange: onChange,
|
|
254
277
|
deleteEditorContentButtonAriaLabel: deleteEditorContentButtonAriaLabel,
|
|
255
|
-
id: id
|
|
278
|
+
id: id,
|
|
279
|
+
maxLength: maxLength,
|
|
280
|
+
getCharactersCounterString: getCharactersCounterString
|
|
256
281
|
})
|
|
257
282
|
})
|
|
258
283
|
});
|
|
@@ -144,10 +144,15 @@ function ChatMessageBodyBase({
|
|
|
144
144
|
// but there we prevent it only from updating slate inner state, html input still
|
|
145
145
|
// adds entered text to the DOM input field causing buggy behavior: https://datarobot.atlassian.net/browse/TESTLIO-2963
|
|
146
146
|
// preventing default from the onDOMBeforeInput won't add new character to the DOM input field.
|
|
147
|
-
// we still
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
// we still want to leave logic in hocs.ts because it covers copy+paste case, as for some reason
|
|
148
|
+
// under certain circumstances onDOMBeforeInput stops being triggering for copy+paste so having there is safer
|
|
149
|
+
|
|
150
|
+
// we want to always allow deleting text
|
|
151
|
+
if (!event.inputType.startsWith('delete')) {
|
|
152
|
+
const existingMentionsCount = getMentionsCount(editor.children);
|
|
153
|
+
if (!!event.data && Editor.string(editor, []).length + existingMentionsCount + (event?.data?.length ?? 0) > maxLength) {
|
|
154
|
+
event.preventDefault();
|
|
155
|
+
}
|
|
151
156
|
}
|
|
152
157
|
}
|
|
153
158
|
})
|
package/esm/chat/hocs.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ReactEditor } from 'slate-react';
|
|
2
|
-
export declare const withMaxLength: (maxLength: number) => (editor: ReactEditor) => ReactEditor;
|
|
2
|
+
export declare const withMaxLength: (maxLength: number | undefined) => (editor: ReactEditor) => ReactEditor;
|
|
3
3
|
export declare const withMentions: (editor: any) => any;
|
package/esm/chat/hocs.js
CHANGED
|
@@ -2,31 +2,30 @@ import { Editor } from 'slate';
|
|
|
2
2
|
import { getMentionsCount, isMentionElement } from './utils';
|
|
3
3
|
export const withMaxLength = maxLength => function Plugin(editor) {
|
|
4
4
|
const {
|
|
5
|
-
insertText,
|
|
6
5
|
insertData
|
|
7
6
|
} = editor;
|
|
8
7
|
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
}
|
|
8
|
+
// usual text insertion prevention is handled in onDOMBeforeInput event on the Editable component
|
|
9
|
+
// this case prevents copy-paste of text that would exceed the maxLength
|
|
10
|
+
// copy-paste could have been also handled in onDOMBeforeInput, but for
|
|
11
|
+
// some reason sometimes Slate stops triggering onDOMBeforeInput for copy-paste ¯\_(ツ)_/¯
|
|
12
|
+
if (maxLength) {
|
|
13
|
+
editor.insertData = data => {
|
|
14
|
+
const existingMentionsCount = getMentionsCount(editor.children);
|
|
15
|
+
// other methods for inserting text does not take into account new lines, so when copy-pasting
|
|
16
|
+
// we should also remove them, otherwise there is a bug when entered text cannot be cut and pasted
|
|
17
|
+
// because on paste text length is considered longer due to \n characters
|
|
18
|
+
const text = data.getData('text/plain')?.replace(/\n/g, '');
|
|
19
|
+
const html = data.getData('text/html');
|
|
20
|
+
const parsedHtml = new DOMParser().parseFromString(html, 'text/html');
|
|
21
|
+
const mentionNodes = parsedHtml.querySelectorAll('.mention');
|
|
22
|
+
const mentionsText = Array.from(mentionNodes).map(node => node.innerText).join('');
|
|
23
|
+
const itemsLengthToBeInserted = mentionNodes.length ? text.length - mentionsText.length : text.length;
|
|
24
|
+
if (Editor.string(editor, []).length + itemsLengthToBeInserted + existingMentionsCount <= maxLength) {
|
|
25
|
+
insertData(data);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
30
29
|
return editor;
|
|
31
30
|
};
|
|
32
31
|
export const withMentions = editor => {
|
package/esm/chat/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { Chat } from './chat';
|
|
2
|
+
export { CharactersCounter } from './characters-counter';
|
|
2
3
|
export { isMentionElement, resetNodes } from './utils';
|
|
4
|
+
export { withMaxLength } from './hocs';
|
|
3
5
|
export { DEFAULT_VALUE, NEW_ITEM_KEY } from './constants';
|
|
4
6
|
export type { ChatMessageItem, ActiveItems } from './types';
|
|
5
7
|
export type { ChatProps } from './chat';
|
package/esm/chat/index.js
CHANGED
|
@@ -24,5 +24,7 @@ export type InlineTextEditorProps = {
|
|
|
24
24
|
readOnly?: boolean;
|
|
25
25
|
deleteEditorContentButtonAriaLabel?: string;
|
|
26
26
|
autoFocus?: boolean;
|
|
27
|
+
maxLength?: number;
|
|
28
|
+
getCharactersCounterString?: (count: number) => string;
|
|
27
29
|
};
|
|
28
|
-
export default function InlineTextEditor({ name, type, readOnly, initialValue, placeholder, ariaLabel, className, enabledTools, header, toolbarPosition, height, onCancel, onSave, autoFocus, deleteEditorContentButtonAriaLabel, }: InlineTextEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
30
|
+
export default function InlineTextEditor({ name, type, readOnly, initialValue, placeholder, ariaLabel, className, enabledTools, header, toolbarPosition, height, onCancel, onSave, autoFocus, deleteEditorContentButtonAriaLabel, maxLength, getCharactersCounterString, }: InlineTextEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -22,7 +22,9 @@ export default function InlineTextEditor({
|
|
|
22
22
|
onCancel,
|
|
23
23
|
onSave,
|
|
24
24
|
autoFocus = false,
|
|
25
|
-
deleteEditorContentButtonAriaLabel
|
|
25
|
+
deleteEditorContentButtonAriaLabel,
|
|
26
|
+
maxLength,
|
|
27
|
+
getCharactersCounterString
|
|
26
28
|
}) {
|
|
27
29
|
const {
|
|
28
30
|
t
|
|
@@ -94,7 +96,9 @@ export default function InlineTextEditor({
|
|
|
94
96
|
height: height,
|
|
95
97
|
testId: "inline-text-editor-edit",
|
|
96
98
|
contentAriaLabel: ariaLabel || titleValue,
|
|
97
|
-
deleteEditorContentButtonAriaLabel: deleteEditorContentButtonAriaLabel ?? t('Delete Editor Content')
|
|
99
|
+
deleteEditorContentButtonAriaLabel: deleteEditorContentButtonAriaLabel ?? t('Delete Editor Content'),
|
|
100
|
+
maxLength: maxLength,
|
|
101
|
+
getCharactersCounterString: getCharactersCounterString
|
|
98
102
|
}) : /*#__PURE__*/_jsxs("div", {
|
|
99
103
|
"test-id": "inline-text-editor-view-container",
|
|
100
104
|
className: classnames('inline-text-editor-view-container', isFlex && 'is-flex'),
|
|
@@ -74,8 +74,6 @@ export interface SlateReact {
|
|
|
74
74
|
Slate: FC<SlateComponent>;
|
|
75
75
|
withReact: typeof withReact;
|
|
76
76
|
}
|
|
77
|
-
export declare function instanceOfContentChild(obj: TextEditorValue | TextEditorValueChild): obj is TextEditorValueChild;
|
|
78
|
-
export declare function instanceOfContent(obj: TextEditorValue | TextEditorValueChild): obj is TextEditorValue;
|
|
79
77
|
export declare const TEXT_EDITOR_DEFAULT_VALUE: TextEditorValue[];
|
|
80
78
|
export declare const TEXT_EDITOR_DEFAULT_TOOLS: TextEditorTool[];
|
|
81
79
|
export interface TextEditorToolbarLabels {
|
|
@@ -31,12 +31,6 @@ export const TEXT_EDITOR_TOOLS = {
|
|
|
31
31
|
CODE: 'code',
|
|
32
32
|
CLEAR_FORMATTING: 'clear-formatting'
|
|
33
33
|
};
|
|
34
|
-
export function instanceOfContentChild(obj) {
|
|
35
|
-
return 'text' in obj;
|
|
36
|
-
}
|
|
37
|
-
export function instanceOfContent(obj) {
|
|
38
|
-
return !('text' in obj);
|
|
39
|
-
}
|
|
40
34
|
export const TEXT_EDITOR_DEFAULT_VALUE = [{
|
|
41
35
|
type: BLOCK_TYPES.REGULAR_TEXT,
|
|
42
36
|
children: [{
|
|
@@ -35,5 +35,6 @@ export type TextEditorContentProps = {
|
|
|
35
35
|
leafRender?: FC<LeafProps>;
|
|
36
36
|
onBlur?: () => void;
|
|
37
37
|
onFocus?: () => void;
|
|
38
|
+
maxLength?: number;
|
|
38
39
|
};
|
|
39
|
-
export default function TextEditorContent({ ariaAttrs, testId, placeholder, readOnly, autoFocus, isScrollable, elementRender, leafRender, onBlur, onFocus, }: TextEditorContentProps): import("react/jsx-runtime").JSX.Element;
|
|
40
|
+
export default function TextEditorContent({ ariaAttrs, testId, placeholder, readOnly, autoFocus, isScrollable, elementRender, leafRender, onBlur, onFocus, maxLength, }: TextEditorContentProps): import("react/jsx-runtime").JSX.Element;
|