@blocklet/editor 2.5.1 → 2.5.3

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.
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
3
- import { CharacterLimitPlugin as LexicalCharacterLimitPlugin } from '@lexical/react/LexicalCharacterLimitPlugin';
4
3
  import { Box } from '@mui/material';
4
+ import { CharacterLimitPlugin as LexicalCharacterLimitPlugin } from '../../vendor/LexicalCharacterLimitPlugin';
5
5
  import { MaxLengthPlugin } from '../../main/plugins/MaxLengthPlugin';
6
6
  export function CharacterLimitPlugin({ maxLength, charLimitIndicatorStyle, alignLeft = false, }) {
7
7
  const [editor] = useLexicalComposerContext();
@@ -136,11 +136,9 @@ export default function Field({ name, type, default: defaultVal, required, depre
136
136
  return (_jsxs(Stack, { className: "x-field", sx: {
137
137
  borderBottom: '1px solid',
138
138
  borderColor: 'divider',
139
- pt: 1.25,
140
- pb: 2.5,
141
139
  my: 1.25,
142
140
  '& &:last-child': {
143
141
  borderBottom: 'none',
144
142
  },
145
- }, children: [_jsxs(Bar, { children: [name && _jsx(Box, { sx: { color: 'primary.main', fontWeight: 500 }, children: name }), !isMobile && metaInfo] }), isMobile && _jsx(Bar, { sx: { mt: 1.25 }, children: metaInfo }), desc && _jsx(Box, { sx: { color: 'text.secondary', mt: 2 }, children: desc }), childDesc && _jsx(FieldDesc, { ...childDesc.properties }), renderChildren()] }));
143
+ }, children: [_jsxs(Bar, { children: [name && _jsx(Box, { sx: { color: 'primary.main', fontWeight: 500 }, children: name }), !isMobile && metaInfo] }), isMobile && _jsx(Bar, { sx: { mt: 1.25 }, children: metaInfo }), desc && _jsx(Box, { sx: { color: 'text.secondary', mt: 1, mb: 1.25 }, children: desc }), childDesc && _jsx(FieldDesc, { ...childDesc.properties }), renderChildren()] }));
146
144
  }
@@ -2,12 +2,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import StaticEditor from './StaticEditor';
3
3
  export default function FieldDesc({ childNodes }) {
4
4
  return (_jsx(StaticEditor, { childNodes: childNodes, sx: {
5
+ mt: 1,
6
+ mb: 1.25,
5
7
  '&>p:first-child': {
6
8
  color: 'text.secondary',
7
- marginTop: (theme) => `${theme.spacing(2)} !important`,
8
- marginLeft: 0,
9
- marginRight: 0,
10
- marginBottom: 0,
9
+ m: 0,
11
10
  },
12
11
  } }));
13
12
  }
@@ -6,8 +6,12 @@ export default function FieldGroup({ children = [] }) {
6
6
  return null;
7
7
  }
8
8
  return (_jsx(Box, { className: "x-field-group", sx: {
9
+ '& > .x-field:first-child': {
10
+ marginTop: 0,
11
+ },
9
12
  '& > .x-field:last-child': {
10
13
  borderBottom: 'none',
14
+ marginBottom: 0,
11
15
  },
12
16
  }, children: children.map((child) => {
13
17
  const { properties } = child;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import type { JSX } from 'react';
9
+ export declare function CharacterLimitPlugin({ charset, maxLength, renderer, }: {
10
+ charset: 'UTF-8' | 'UTF-16';
11
+ maxLength: number;
12
+ renderer?: ({ remainingCharacters }: {
13
+ remainingCharacters: number;
14
+ }) => JSX.Element;
15
+ }): JSX.Element;
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
3
+ import { useMemo, useState } from 'react';
4
+ import { useCharacterLimit } from './useCharacterLimit';
5
+ const CHARACTER_LIMIT = 5;
6
+ let textEncoderInstance = null;
7
+ function textEncoder() {
8
+ if (window.TextEncoder === undefined) {
9
+ return null;
10
+ }
11
+ if (textEncoderInstance === null) {
12
+ textEncoderInstance = new window.TextEncoder();
13
+ }
14
+ return textEncoderInstance;
15
+ }
16
+ function utf8Length(text) {
17
+ const currentTextEncoder = textEncoder();
18
+ if (currentTextEncoder === null) {
19
+ // http://stackoverflow.com/a/5515960/210370
20
+ const m = encodeURIComponent(text).match(/%[89ABab]/g);
21
+ return text.length + (m ? m.length : 0);
22
+ }
23
+ return currentTextEncoder.encode(text).length;
24
+ }
25
+ function DefaultRenderer({ remainingCharacters }) {
26
+ return (_jsx("span", { className: `characters-limit ${remainingCharacters < 0 ? 'characters-limit-exceeded' : ''}`, children: remainingCharacters }));
27
+ }
28
+ export function CharacterLimitPlugin({ charset = 'UTF-16', maxLength = CHARACTER_LIMIT, renderer = DefaultRenderer, }) {
29
+ const [editor] = useLexicalComposerContext();
30
+ const [remainingCharacters, setRemainingCharacters] = useState(maxLength);
31
+ const characterLimitProps = useMemo(() => ({
32
+ remainingCharacters: setRemainingCharacters,
33
+ strlen: (text) => {
34
+ if (charset === 'UTF-8') {
35
+ return utf8Length(text);
36
+ }
37
+ if (charset === 'UTF-16') {
38
+ return text.length;
39
+ }
40
+ throw new Error('Unrecognized charset');
41
+ },
42
+ }), [charset]);
43
+ useCharacterLimit(editor, maxLength, characterLimitProps);
44
+ return renderer({ remainingCharacters });
45
+ }
@@ -0,0 +1 @@
1
+ export * from './LexicalCharacterLimitPlugin';
@@ -0,0 +1 @@
1
+ export * from './LexicalCharacterLimitPlugin';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import type { LexicalEditor } from 'lexical';
9
+ import { OverflowNode } from '@lexical/overflow';
10
+ type OptionalProps = {
11
+ remainingCharacters?: (characters: number) => void;
12
+ strlen?: (input: string) => number;
13
+ };
14
+ export declare function useCharacterLimit(editor: LexicalEditor, maxCharacters: number, optional?: OptionalProps): void;
15
+ export declare function $mergePrevious(overflowNode: OverflowNode): void;
16
+ export {};
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import { $createOverflowNode, $isOverflowNode, OverflowNode } from '@lexical/overflow';
9
+ import { $rootTextContent } from '@lexical/text';
10
+ import { $dfs, $unwrapNode, mergeRegister } from '@lexical/utils';
11
+ import { $getSelection, $isElementNode, $isLeafNode, $isRangeSelection, $isTextNode, $setSelection, COMMAND_PRIORITY_LOW, DELETE_CHARACTER_COMMAND, } from 'lexical';
12
+ import { useEffect } from 'react';
13
+ import invariant from '../../shared/invariant';
14
+ export function useCharacterLimit(editor, maxCharacters, optional = Object.freeze({})) {
15
+ const { strlen = (input) => input.length,
16
+ // UTF-16
17
+ remainingCharacters = () => { }, } = optional;
18
+ useEffect(() => {
19
+ if (!editor.hasNodes([OverflowNode])) {
20
+ invariant(false, 'useCharacterLimit: OverflowNode not registered on editor');
21
+ }
22
+ }, [editor]);
23
+ useEffect(() => {
24
+ let text = editor.getEditorState().read($rootTextContent);
25
+ let lastComputedTextLength = 0;
26
+ return mergeRegister(editor.registerTextContentListener((currentText) => {
27
+ text = currentText;
28
+ }), editor.registerUpdateListener((payload) => {
29
+ if (!payload)
30
+ return;
31
+ const { dirtyLeaves, dirtyElements } = payload;
32
+ const isComposing = editor.isComposing();
33
+ const hasContentChanges = dirtyLeaves.size > 0 || dirtyElements.size > 0;
34
+ if (isComposing || !hasContentChanges) {
35
+ return;
36
+ }
37
+ const textLength = strlen(text);
38
+ const textLengthAboveThreshold = textLength > maxCharacters || (lastComputedTextLength !== null && lastComputedTextLength > maxCharacters);
39
+ const diff = maxCharacters - textLength;
40
+ remainingCharacters(diff);
41
+ if (lastComputedTextLength === null || textLengthAboveThreshold) {
42
+ const offset = findOffset(text, maxCharacters, strlen);
43
+ editor.update(() => {
44
+ $wrapOverflowedNodes(offset);
45
+ }, {
46
+ tag: 'history-merge',
47
+ });
48
+ }
49
+ lastComputedTextLength = textLength;
50
+ }), editor.registerCommand(DELETE_CHARACTER_COMMAND, (isBackward) => {
51
+ const selection = $getSelection();
52
+ if (!$isRangeSelection(selection)) {
53
+ return false;
54
+ }
55
+ const anchorNode = selection.anchor.getNode();
56
+ const overflow = anchorNode.getParent();
57
+ const overflowParent = overflow ? overflow.getParent() : null;
58
+ const parentNext = overflowParent ? overflowParent.getNextSibling() : null;
59
+ selection.deleteCharacter(isBackward);
60
+ if (overflowParent && overflowParent.isEmpty()) {
61
+ overflowParent.remove();
62
+ }
63
+ else if ($isElementNode(parentNext) && parentNext.isEmpty()) {
64
+ parentNext.remove();
65
+ }
66
+ return true;
67
+ }, COMMAND_PRIORITY_LOW));
68
+ }, [editor, maxCharacters, remainingCharacters, strlen]);
69
+ }
70
+ function findOffset(text, maxCharacters, strlen) {
71
+ const { Segmenter } = Intl;
72
+ let offsetUtf16 = 0;
73
+ let offset = 0;
74
+ if (typeof Segmenter === 'function') {
75
+ const segmenter = new Segmenter();
76
+ const graphemes = segmenter.segment(text);
77
+ for (const { segment: grapheme } of graphemes) {
78
+ const nextOffset = offset + strlen(grapheme);
79
+ if (nextOffset > maxCharacters) {
80
+ break;
81
+ }
82
+ offset = nextOffset;
83
+ offsetUtf16 += grapheme.length;
84
+ }
85
+ }
86
+ else {
87
+ const codepoints = Array.from(text);
88
+ const codepointsLength = codepoints.length;
89
+ for (let i = 0; i < codepointsLength; i++) {
90
+ const codepoint = codepoints[i];
91
+ const nextOffset = offset + strlen(codepoint);
92
+ if (nextOffset > maxCharacters) {
93
+ break;
94
+ }
95
+ offset = nextOffset;
96
+ offsetUtf16 += codepoint.length;
97
+ }
98
+ }
99
+ return offsetUtf16;
100
+ }
101
+ function $wrapOverflowedNodes(offset) {
102
+ const dfsNodes = $dfs().filter(Boolean);
103
+ const dfsNodesLength = dfsNodes.length;
104
+ let accumulatedLength = 0;
105
+ for (let i = 0; i < dfsNodesLength; i += 1) {
106
+ const { node } = dfsNodes[i];
107
+ if ($isOverflowNode(node)) {
108
+ const previousLength = accumulatedLength;
109
+ const nextLength = accumulatedLength + node.getTextContentSize();
110
+ if (nextLength <= offset) {
111
+ const parent = node.getParent();
112
+ const previousSibling = node.getPreviousSibling();
113
+ const nextSibling = node.getNextSibling();
114
+ $unwrapNode(node);
115
+ const selection = $getSelection();
116
+ // Restore selection when the overflow children are removed
117
+ if ($isRangeSelection(selection) &&
118
+ (!selection.anchor.getNode().isAttached() || !selection.focus.getNode().isAttached())) {
119
+ if ($isTextNode(previousSibling)) {
120
+ previousSibling.select();
121
+ }
122
+ else if ($isTextNode(nextSibling)) {
123
+ nextSibling.select();
124
+ }
125
+ else if (parent !== null) {
126
+ parent.select();
127
+ }
128
+ }
129
+ }
130
+ else if (previousLength < offset) {
131
+ const descendant = node.getFirstDescendant();
132
+ const descendantLength = descendant !== null ? descendant.getTextContentSize() : 0;
133
+ const previousPlusDescendantLength = previousLength + descendantLength;
134
+ // For simple text we can redimension the overflow into a smaller and more accurate
135
+ // container
136
+ const firstDescendantIsSimpleText = $isTextNode(descendant) && descendant.isSimpleText();
137
+ const firstDescendantDoesNotOverflow = previousPlusDescendantLength <= offset;
138
+ if (firstDescendantIsSimpleText || firstDescendantDoesNotOverflow) {
139
+ $unwrapNode(node);
140
+ }
141
+ }
142
+ }
143
+ else if ($isLeafNode(node)) {
144
+ const previousAccumulatedLength = accumulatedLength;
145
+ accumulatedLength += node.getTextContentSize();
146
+ if (accumulatedLength > offset && !$isOverflowNode(node.getParent())) {
147
+ const previousSelection = $getSelection();
148
+ let overflowNode;
149
+ // For simple text we can improve the limit accuracy by splitting the TextNode
150
+ // on the split point
151
+ if (previousAccumulatedLength < offset && $isTextNode(node) && node.isSimpleText()) {
152
+ const [, overflowedText] = node.splitText(offset - previousAccumulatedLength);
153
+ overflowNode = $wrapNode(overflowedText);
154
+ }
155
+ else {
156
+ overflowNode = $wrapNode(node);
157
+ }
158
+ if (previousSelection !== null) {
159
+ $setSelection(previousSelection);
160
+ }
161
+ $mergePrevious(overflowNode);
162
+ }
163
+ }
164
+ }
165
+ }
166
+ function $wrapNode(node) {
167
+ const overflowNode = $createOverflowNode();
168
+ node.replace(overflowNode);
169
+ overflowNode.append(node);
170
+ return overflowNode;
171
+ }
172
+ export function $mergePrevious(overflowNode) {
173
+ const previousNode = overflowNode.getPreviousSibling();
174
+ if (!$isOverflowNode(previousNode)) {
175
+ return;
176
+ }
177
+ const firstChild = overflowNode.getFirstChild();
178
+ const previousNodeChildren = previousNode.getChildren();
179
+ const previousNodeChildrenLength = previousNodeChildren.length;
180
+ if (firstChild === null) {
181
+ overflowNode.append(...previousNodeChildren);
182
+ }
183
+ else {
184
+ for (let i = 0; i < previousNodeChildrenLength; i++) {
185
+ firstChild.insertBefore(previousNodeChildren[i]);
186
+ }
187
+ }
188
+ const selection = $getSelection();
189
+ if ($isRangeSelection(selection)) {
190
+ const { anchor } = selection;
191
+ const anchorNode = anchor.getNode();
192
+ const { focus } = selection;
193
+ const focusNode = anchor.getNode();
194
+ if (anchorNode.is(previousNode)) {
195
+ anchor.set(overflowNode.getKey(), anchor.offset, 'element');
196
+ }
197
+ else if (anchorNode.is(overflowNode)) {
198
+ anchor.set(overflowNode.getKey(), previousNodeChildrenLength + anchor.offset, 'element');
199
+ }
200
+ if (focusNode.is(previousNode)) {
201
+ focus.set(overflowNode.getKey(), focus.offset, 'element');
202
+ }
203
+ else if (focusNode.is(overflowNode)) {
204
+ focus.set(overflowNode.getKey(), previousNodeChildrenLength + focus.offset, 'element');
205
+ }
206
+ }
207
+ previousNode.remove();
208
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/editor",
3
- "version": "2.5.1",
3
+ "version": "2.5.3",
4
4
  "main": "lib/index.js",
5
5
  "sideEffects": false,
6
6
  "publishConfig": {
@@ -73,7 +73,7 @@
73
73
  "ufo": "^1.5.4",
74
74
  "url-join": "^4.0.1",
75
75
  "zustand": "^4.5.5",
76
- "@blocklet/pdf": "2.5.1"
76
+ "@blocklet/pdf": "2.5.3"
77
77
  },
78
78
  "devDependencies": {
79
79
  "@babel/core": "^7.25.2",