@lofcz/platejs-cursor 52.0.11
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/LICENSE +24 -0
- package/README.md +12 -0
- package/dist/index.d.ts +130 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +358 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) Ziad Beyens, Dylan Schiemann, Joe Anderson, Felix Feng
|
|
4
|
+
|
|
5
|
+
Unless otherwise specified in a LICENSE file within an individual package directory,
|
|
6
|
+
this license applies to all files in this repository outside of those package directories.
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
in the Software without restriction, including without limitation the rights
|
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
furnished to do so, subject to the following conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be included in
|
|
16
|
+
all copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
24
|
+
THE SOFTWARE.
|
package/README.md
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Editor, TRange, UnknownObject } from "platejs";
|
|
3
|
+
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
type CaretPosition = {
|
|
6
|
+
height: number;
|
|
7
|
+
left: number;
|
|
8
|
+
top: number;
|
|
9
|
+
};
|
|
10
|
+
type CursorData = {
|
|
11
|
+
selectionStyle?: React.CSSProperties;
|
|
12
|
+
style?: React.CSSProperties;
|
|
13
|
+
};
|
|
14
|
+
interface CursorOverlayState<TCursorData extends Record<string, unknown>> extends CursorState<TCursorData> {
|
|
15
|
+
caretPosition: CaretPosition | null;
|
|
16
|
+
selectionRects: SelectionRect[];
|
|
17
|
+
}
|
|
18
|
+
type CursorState<TCursorData extends UnknownObject = UnknownObject> = {
|
|
19
|
+
selection: TRange | null;
|
|
20
|
+
key?: any;
|
|
21
|
+
data?: TCursorData;
|
|
22
|
+
};
|
|
23
|
+
type SelectionRect = {
|
|
24
|
+
height: number;
|
|
25
|
+
left: number;
|
|
26
|
+
top: number;
|
|
27
|
+
width: number;
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/components/CursorOverlay.d.ts
|
|
31
|
+
interface CursorOverlayProps<TCursorData extends UnknownObject = UnknownObject> extends Pick<CursorProps<CursorData>, 'classNames' | 'disableCaret' | 'disableSelection' | 'onRenderCaret' | 'onRenderSelectionRect'> {
|
|
32
|
+
/**
|
|
33
|
+
* Container the overlay will be rendered in. If set, all returned overlay
|
|
34
|
+
* positions will be relative to this container.
|
|
35
|
+
*/
|
|
36
|
+
containerRef?: React.RefObject<HTMLElement | null>;
|
|
37
|
+
/** Cursor states to use for calculating the overlay positions, by key. */
|
|
38
|
+
cursors?: Record<string, CursorState<TCursorData>>;
|
|
39
|
+
/** Overrides `Cursor` component. */
|
|
40
|
+
onRenderCursor?: React.FC<CursorProps>;
|
|
41
|
+
/**
|
|
42
|
+
* Whether to refresh the cursor overlay positions on container resize.
|
|
43
|
+
*
|
|
44
|
+
* @default true
|
|
45
|
+
*/
|
|
46
|
+
refreshOnResize?: boolean;
|
|
47
|
+
}
|
|
48
|
+
type CursorProps<TCursorData extends UnknownObject = UnknownObject> = CursorOverlayState<TCursorData> & {
|
|
49
|
+
id: string;
|
|
50
|
+
classNames?: Partial<{
|
|
51
|
+
caret: string;
|
|
52
|
+
selectionRect: string;
|
|
53
|
+
}>;
|
|
54
|
+
/** Whether to disable the caret. */
|
|
55
|
+
disableCaret?: boolean;
|
|
56
|
+
/** Whether to disable the selection rects. */
|
|
57
|
+
disableSelection?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Custom caret component. For example, you could display a label next to
|
|
60
|
+
* the caret.
|
|
61
|
+
*
|
|
62
|
+
* @default styled div
|
|
63
|
+
*/
|
|
64
|
+
onRenderCaret?: React.FC<Pick<CursorProps<TCursorData>, 'caretPosition' | 'data'>>;
|
|
65
|
+
/** Overrides `Caret` component */
|
|
66
|
+
onRenderSelectionRect?: React.FC<{
|
|
67
|
+
selectionRect: SelectionRect;
|
|
68
|
+
} & Pick<CursorProps<TCursorData>, 'data'>>;
|
|
69
|
+
};
|
|
70
|
+
declare function CursorOverlayContent<TCursorData extends UnknownObject = UnknownObject>({
|
|
71
|
+
classNames,
|
|
72
|
+
onRenderCaret,
|
|
73
|
+
onRenderCursor: CursorComponent,
|
|
74
|
+
onRenderSelectionRect,
|
|
75
|
+
...props
|
|
76
|
+
}: CursorOverlayProps<TCursorData>): React.JSX.Element | null;
|
|
77
|
+
declare function CursorOverlay<TCursorData extends UnknownObject = UnknownObject>(props: CursorOverlayProps<TCursorData>): React.JSX.Element | null;
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/hooks/useCursorOverlayPositions.d.ts
|
|
80
|
+
declare const FROZEN_EMPTY_ARRAY: SelectionRect[];
|
|
81
|
+
declare const useCursorOverlayPositions: <TCursorData extends UnknownObject>({
|
|
82
|
+
containerRef,
|
|
83
|
+
cursors: cursorStates,
|
|
84
|
+
refreshOnResize
|
|
85
|
+
}?: CursorOverlayProps<TCursorData>) => {
|
|
86
|
+
cursors: CursorOverlayState<TCursorData>[];
|
|
87
|
+
refresh: (sync?: boolean) => void;
|
|
88
|
+
};
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/hooks/useRefreshOnResize.d.ts
|
|
91
|
+
interface useRefreshOnResizeOptions extends Pick<CursorOverlayProps, 'containerRef' | 'refreshOnResize'> {
|
|
92
|
+
selectionRectCache: React.MutableRefObject<WeakMap<TRange, SelectionRect[]>>;
|
|
93
|
+
}
|
|
94
|
+
declare const useRefreshOnResize: ({
|
|
95
|
+
containerRef,
|
|
96
|
+
refreshOnResize,
|
|
97
|
+
selectionRectCache: selectionRectCacheRef
|
|
98
|
+
}: useRefreshOnResizeOptions) => {
|
|
99
|
+
refresh: (sync?: boolean) => void;
|
|
100
|
+
};
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region src/hooks/useRequestReRender.d.ts
|
|
103
|
+
declare const useRequestReRender: () => (immediate?: boolean) => void;
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/queries/getCaretPosition.d.ts
|
|
106
|
+
/** Get the caret position of a range from selectionRects. */
|
|
107
|
+
declare const getCaretPosition: (selectionRects: SelectionRect[], range: TRange) => CaretPosition | null;
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/queries/getCursorOverlayState.d.ts
|
|
110
|
+
/** Get cursor overlay state from selection rects. */
|
|
111
|
+
declare const getCursorOverlayState: <TCursorData extends UnknownObject = UnknownObject>({
|
|
112
|
+
cursors: cursorStates,
|
|
113
|
+
selectionRects
|
|
114
|
+
}: {
|
|
115
|
+
selectionRects: Record<string, SelectionRect[]>;
|
|
116
|
+
} & Pick<CursorOverlayProps<TCursorData>, "cursors">) => CursorOverlayState<TCursorData>[];
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/queries/getSelectionRects.d.ts
|
|
119
|
+
declare const getSelectionRects: (editor: Editor, {
|
|
120
|
+
range,
|
|
121
|
+
xOffset,
|
|
122
|
+
yOffset
|
|
123
|
+
}: {
|
|
124
|
+
range: TRange;
|
|
125
|
+
xOffset: number;
|
|
126
|
+
yOffset: number;
|
|
127
|
+
}) => SelectionRect[];
|
|
128
|
+
//#endregion
|
|
129
|
+
export { CaretPosition, CursorData, CursorOverlay, CursorOverlayContent, CursorOverlayProps, CursorOverlayState, CursorProps, CursorState, FROZEN_EMPTY_ARRAY, SelectionRect, getCaretPosition, getCursorOverlayState, getSelectionRects, useCursorOverlayPositions, useRefreshOnResize, useRefreshOnResizeOptions, useRequestReRender };
|
|
130
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/components/CursorOverlay.tsx","../src/hooks/useCursorOverlayPositions.ts","../src/hooks/useRefreshOnResize.ts","../src/hooks/useRequestReRender.ts","../src/queries/getCaretPosition.ts","../src/queries/getCursorOverlayState.ts","../src/queries/getSelectionRects.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { c } from "react-compiler-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { useEditorMounted, useEditorRef, useIsomorphicLayoutEffect } from "platejs/react";
|
|
4
|
+
import { PathApi, RangeApi, TextApi } from "platejs";
|
|
5
|
+
|
|
6
|
+
//#region src/hooks/useRequestReRender.ts
|
|
7
|
+
const useRequestReRender = () => {
|
|
8
|
+
const [, setUpdateCounter] = React.useState(0);
|
|
9
|
+
const animationFrameRef = React.useRef(null);
|
|
10
|
+
const requestReRender = React.useCallback((immediate = false) => {
|
|
11
|
+
if (animationFrameRef.current && !immediate) return;
|
|
12
|
+
if (!immediate) {
|
|
13
|
+
animationFrameRef.current = requestAnimationFrame(() => {
|
|
14
|
+
setUpdateCounter((state) => state + 1);
|
|
15
|
+
animationFrameRef.current = null;
|
|
16
|
+
});
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (animationFrameRef.current) {
|
|
20
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
21
|
+
animationFrameRef.current = null;
|
|
22
|
+
}
|
|
23
|
+
setUpdateCounter((state_0) => state_0 + 1);
|
|
24
|
+
}, []);
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
if (animationFrameRef.current) {
|
|
27
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
28
|
+
animationFrameRef.current = null;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
React.useEffect(() => () => {
|
|
32
|
+
if (animationFrameRef.current) cancelAnimationFrame(animationFrameRef.current);
|
|
33
|
+
}, []);
|
|
34
|
+
return requestReRender;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/hooks/useRefreshOnResize.ts
|
|
39
|
+
const useRefreshOnResize = (t0) => {
|
|
40
|
+
const $ = c(10);
|
|
41
|
+
const { containerRef, refreshOnResize, selectionRectCache: selectionRectCacheRef } = t0;
|
|
42
|
+
const requestReRender = useRequestReRender();
|
|
43
|
+
let t1;
|
|
44
|
+
if ($[0] !== requestReRender || $[1] !== selectionRectCacheRef) {
|
|
45
|
+
t1 = (t2$1) => {
|
|
46
|
+
const sync = t2$1 === void 0 ? false : t2$1;
|
|
47
|
+
selectionRectCacheRef.current = /* @__PURE__ */ new WeakMap();
|
|
48
|
+
requestReRender(sync);
|
|
49
|
+
};
|
|
50
|
+
$[0] = requestReRender;
|
|
51
|
+
$[1] = selectionRectCacheRef;
|
|
52
|
+
$[2] = t1;
|
|
53
|
+
} else t1 = $[2];
|
|
54
|
+
const refresh = t1;
|
|
55
|
+
let t2;
|
|
56
|
+
let t3;
|
|
57
|
+
if ($[3] !== containerRef || $[4] !== refresh || $[5] !== refreshOnResize) {
|
|
58
|
+
t2 = () => {
|
|
59
|
+
if (!refreshOnResize || !containerRef?.current) return;
|
|
60
|
+
const resizeObserver = new ResizeObserver(() => refresh());
|
|
61
|
+
resizeObserver.observe(containerRef.current);
|
|
62
|
+
return () => resizeObserver.disconnect();
|
|
63
|
+
};
|
|
64
|
+
t3 = [
|
|
65
|
+
containerRef,
|
|
66
|
+
refresh,
|
|
67
|
+
refreshOnResize
|
|
68
|
+
];
|
|
69
|
+
$[3] = containerRef;
|
|
70
|
+
$[4] = refresh;
|
|
71
|
+
$[5] = refreshOnResize;
|
|
72
|
+
$[6] = t2;
|
|
73
|
+
$[7] = t3;
|
|
74
|
+
} else {
|
|
75
|
+
t2 = $[6];
|
|
76
|
+
t3 = $[7];
|
|
77
|
+
}
|
|
78
|
+
React.useEffect(t2, t3);
|
|
79
|
+
let t4;
|
|
80
|
+
if ($[8] !== refresh) {
|
|
81
|
+
t4 = { refresh };
|
|
82
|
+
$[8] = refresh;
|
|
83
|
+
$[9] = t4;
|
|
84
|
+
} else t4 = $[9];
|
|
85
|
+
return t4;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/queries/getCaretPosition.ts
|
|
90
|
+
/** Get the caret position of a range from selectionRects. */
|
|
91
|
+
const getCaretPosition = (selectionRects, range) => {
|
|
92
|
+
const isCollapsed = range && RangeApi.isCollapsed(range);
|
|
93
|
+
const isBackward = range && RangeApi.isBackward(range);
|
|
94
|
+
const anchorRect = selectionRects[isBackward ? 0 : selectionRects.length - 1];
|
|
95
|
+
if (!anchorRect) return null;
|
|
96
|
+
return {
|
|
97
|
+
height: anchorRect.height,
|
|
98
|
+
left: anchorRect.left + (isBackward || isCollapsed ? 0 : anchorRect.width),
|
|
99
|
+
top: anchorRect.top
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/queries/getCursorOverlayState.ts
|
|
105
|
+
/** Get cursor overlay state from selection rects. */
|
|
106
|
+
const getCursorOverlayState = ({ cursors: cursorStates, selectionRects }) => {
|
|
107
|
+
if (!cursorStates) return [];
|
|
108
|
+
return Object.entries(cursorStates).map(([key, cursorState]) => {
|
|
109
|
+
const selection = cursorState?.selection ?? null;
|
|
110
|
+
const rects = selectionRects[key] ?? FROZEN_EMPTY_ARRAY;
|
|
111
|
+
const caretPosition = selection ? getCaretPosition(rects, selection) : null;
|
|
112
|
+
return {
|
|
113
|
+
...cursorState,
|
|
114
|
+
caretPosition,
|
|
115
|
+
selection,
|
|
116
|
+
selectionRects: rects
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/queries/getSelectionRects.ts
|
|
123
|
+
const getSelectionRects = (editor, { range, xOffset, yOffset }) => {
|
|
124
|
+
const [start, end] = RangeApi.edges(range);
|
|
125
|
+
const domRange = editor.api.toDOMRange(range);
|
|
126
|
+
if (!domRange) return [];
|
|
127
|
+
const selectionRects = [];
|
|
128
|
+
const textEntries = editor.api.nodes({
|
|
129
|
+
at: range,
|
|
130
|
+
match: TextApi.isText
|
|
131
|
+
});
|
|
132
|
+
for (const [textNode, textPath] of textEntries) {
|
|
133
|
+
const domNode = editor.api.toDOMNode(textNode);
|
|
134
|
+
if (!domNode?.parentElement) return [];
|
|
135
|
+
const isStartNode = PathApi.equals(textPath, start.path);
|
|
136
|
+
const isEndNode = PathApi.equals(textPath, end.path);
|
|
137
|
+
let clientRects = null;
|
|
138
|
+
if (isStartNode || isEndNode) {
|
|
139
|
+
const nodeRange = document.createRange();
|
|
140
|
+
nodeRange.selectNode(domNode);
|
|
141
|
+
if (isStartNode) nodeRange.setStart(domRange.startContainer, domRange.startOffset);
|
|
142
|
+
if (isEndNode) nodeRange.setEnd(domRange.endContainer, domRange.endOffset);
|
|
143
|
+
clientRects = nodeRange.getClientRects();
|
|
144
|
+
} else clientRects = domNode.getClientRects();
|
|
145
|
+
for (let i = 0; i < clientRects.length; i++) {
|
|
146
|
+
const clientRect = clientRects.item(i);
|
|
147
|
+
if (!clientRect) continue;
|
|
148
|
+
selectionRects.push({
|
|
149
|
+
height: clientRect.height,
|
|
150
|
+
left: clientRect.left - xOffset,
|
|
151
|
+
top: clientRect.top - yOffset,
|
|
152
|
+
width: clientRect.width
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return selectionRects;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/hooks/useCursorOverlayPositions.ts
|
|
161
|
+
const FROZEN_EMPTY_ARRAY = Object.freeze([]);
|
|
162
|
+
const useCursorOverlayPositions = (t0) => {
|
|
163
|
+
const $ = c(20);
|
|
164
|
+
let t1;
|
|
165
|
+
if ($[0] !== t0) {
|
|
166
|
+
t1 = t0 === void 0 ? {} : t0;
|
|
167
|
+
$[0] = t0;
|
|
168
|
+
$[1] = t1;
|
|
169
|
+
} else t1 = $[1];
|
|
170
|
+
const { containerRef, cursors: cursorStates, refreshOnResize: t2 } = t1;
|
|
171
|
+
const refreshOnResize = t2 === void 0 ? true : t2;
|
|
172
|
+
const editor = useEditorRef();
|
|
173
|
+
let t3;
|
|
174
|
+
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
|
175
|
+
t3 = /* @__PURE__ */ new WeakMap();
|
|
176
|
+
$[2] = t3;
|
|
177
|
+
} else t3 = $[2];
|
|
178
|
+
const selectionRectCache = React.useRef(t3);
|
|
179
|
+
let t4;
|
|
180
|
+
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
|
181
|
+
t4 = {};
|
|
182
|
+
$[3] = t4;
|
|
183
|
+
} else t4 = $[3];
|
|
184
|
+
const [selectionRects, setSelectionRects] = React.useState(t4);
|
|
185
|
+
let t5;
|
|
186
|
+
if ($[4] !== containerRef || $[5] !== cursorStates || $[6] !== editor || $[7] !== selectionRects) {
|
|
187
|
+
t5 = () => {
|
|
188
|
+
if (!containerRef?.current) return;
|
|
189
|
+
if (!cursorStates) return;
|
|
190
|
+
let xOffset = 0;
|
|
191
|
+
let yOffset = 0;
|
|
192
|
+
if (containerRef) {
|
|
193
|
+
const contentRect = containerRef.current.getBoundingClientRect();
|
|
194
|
+
xOffset = contentRect.x;
|
|
195
|
+
yOffset = contentRect.y;
|
|
196
|
+
yOffset = yOffset - containerRef.current.scrollTop;
|
|
197
|
+
}
|
|
198
|
+
let selectionRectsChanged = Object.keys(selectionRects).length !== Object.keys(cursorStates).length;
|
|
199
|
+
const getCachedSelectionRects = (t6$1) => {
|
|
200
|
+
const { cursor } = t6$1;
|
|
201
|
+
const range = cursor.selection;
|
|
202
|
+
if (!range) return FROZEN_EMPTY_ARRAY;
|
|
203
|
+
const cached = selectionRectCache.current.get(range);
|
|
204
|
+
if (cached) return cached;
|
|
205
|
+
const rects = getSelectionRects(editor, {
|
|
206
|
+
range,
|
|
207
|
+
xOffset,
|
|
208
|
+
yOffset
|
|
209
|
+
});
|
|
210
|
+
selectionRectsChanged = true;
|
|
211
|
+
selectionRectCache.current.set(range, rects);
|
|
212
|
+
return rects;
|
|
213
|
+
};
|
|
214
|
+
const updated = Object.fromEntries(Object.entries(cursorStates).map((t7$1) => {
|
|
215
|
+
const [key, cursor_0] = t7$1;
|
|
216
|
+
return [key, getCachedSelectionRects({ cursor: cursor_0 })];
|
|
217
|
+
}));
|
|
218
|
+
if (selectionRectsChanged) setSelectionRects(updated);
|
|
219
|
+
};
|
|
220
|
+
$[4] = containerRef;
|
|
221
|
+
$[5] = cursorStates;
|
|
222
|
+
$[6] = editor;
|
|
223
|
+
$[7] = selectionRects;
|
|
224
|
+
$[8] = t5;
|
|
225
|
+
} else t5 = $[8];
|
|
226
|
+
const updateSelectionRects = t5;
|
|
227
|
+
let t6;
|
|
228
|
+
if ($[9] !== updateSelectionRects) {
|
|
229
|
+
t6 = () => {
|
|
230
|
+
updateSelectionRects();
|
|
231
|
+
};
|
|
232
|
+
$[9] = updateSelectionRects;
|
|
233
|
+
$[10] = t6;
|
|
234
|
+
} else t6 = $[10];
|
|
235
|
+
useIsomorphicLayoutEffect(t6);
|
|
236
|
+
let t7;
|
|
237
|
+
if ($[11] !== cursorStates || $[12] !== selectionRects) {
|
|
238
|
+
t7 = getCursorOverlayState({
|
|
239
|
+
cursors: cursorStates,
|
|
240
|
+
selectionRects
|
|
241
|
+
});
|
|
242
|
+
$[11] = cursorStates;
|
|
243
|
+
$[12] = selectionRects;
|
|
244
|
+
$[13] = t7;
|
|
245
|
+
} else t7 = $[13];
|
|
246
|
+
const cursors = t7;
|
|
247
|
+
let t8;
|
|
248
|
+
if ($[14] !== containerRef || $[15] !== refreshOnResize) {
|
|
249
|
+
t8 = {
|
|
250
|
+
containerRef,
|
|
251
|
+
refreshOnResize,
|
|
252
|
+
selectionRectCache
|
|
253
|
+
};
|
|
254
|
+
$[14] = containerRef;
|
|
255
|
+
$[15] = refreshOnResize;
|
|
256
|
+
$[16] = t8;
|
|
257
|
+
} else t8 = $[16];
|
|
258
|
+
const { refresh } = useRefreshOnResize(t8);
|
|
259
|
+
let t9;
|
|
260
|
+
if ($[17] !== cursors || $[18] !== refresh) {
|
|
261
|
+
t9 = {
|
|
262
|
+
cursors,
|
|
263
|
+
refresh
|
|
264
|
+
};
|
|
265
|
+
$[17] = cursors;
|
|
266
|
+
$[18] = refresh;
|
|
267
|
+
$[19] = t9;
|
|
268
|
+
} else t9 = $[19];
|
|
269
|
+
return t9;
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
//#endregion
|
|
273
|
+
//#region src/components/CursorOverlay.tsx
|
|
274
|
+
function CursorOverlayContent(t0) {
|
|
275
|
+
const $ = c(21);
|
|
276
|
+
let CursorComponent;
|
|
277
|
+
let classNames;
|
|
278
|
+
let onRenderCaret;
|
|
279
|
+
let onRenderSelectionRect;
|
|
280
|
+
let props;
|
|
281
|
+
if ($[0] !== t0) {
|
|
282
|
+
({classNames, onRenderCaret, onRenderCursor: CursorComponent, onRenderSelectionRect, ...props} = t0);
|
|
283
|
+
$[0] = t0;
|
|
284
|
+
$[1] = CursorComponent;
|
|
285
|
+
$[2] = classNames;
|
|
286
|
+
$[3] = onRenderCaret;
|
|
287
|
+
$[4] = onRenderSelectionRect;
|
|
288
|
+
$[5] = props;
|
|
289
|
+
} else {
|
|
290
|
+
CursorComponent = $[1];
|
|
291
|
+
classNames = $[2];
|
|
292
|
+
onRenderCaret = $[3];
|
|
293
|
+
onRenderSelectionRect = $[4];
|
|
294
|
+
props = $[5];
|
|
295
|
+
}
|
|
296
|
+
const { disableCaret, disableSelection } = props;
|
|
297
|
+
const { cursors } = useCursorOverlayPositions(props);
|
|
298
|
+
let t1;
|
|
299
|
+
if ($[6] !== classNames || $[7] !== disableCaret || $[8] !== disableSelection || $[9] !== onRenderCaret || $[10] !== onRenderSelectionRect) {
|
|
300
|
+
t1 = {
|
|
301
|
+
classNames,
|
|
302
|
+
disableCaret,
|
|
303
|
+
disableSelection,
|
|
304
|
+
onRenderCaret,
|
|
305
|
+
onRenderSelectionRect
|
|
306
|
+
};
|
|
307
|
+
$[6] = classNames;
|
|
308
|
+
$[7] = disableCaret;
|
|
309
|
+
$[8] = disableSelection;
|
|
310
|
+
$[9] = onRenderCaret;
|
|
311
|
+
$[10] = onRenderSelectionRect;
|
|
312
|
+
$[11] = t1;
|
|
313
|
+
} else t1 = $[11];
|
|
314
|
+
const cursorProps = t1;
|
|
315
|
+
if (!CursorComponent) return null;
|
|
316
|
+
let t2;
|
|
317
|
+
if ($[12] !== CursorComponent || $[13] !== cursorProps || $[14] !== cursors) {
|
|
318
|
+
let t3$1;
|
|
319
|
+
if ($[16] !== CursorComponent || $[17] !== cursorProps) {
|
|
320
|
+
t3$1 = (cursor) => /* @__PURE__ */ React.createElement(CursorComponent, {
|
|
321
|
+
id: cursor.key,
|
|
322
|
+
key: cursor.key,
|
|
323
|
+
...cursorProps,
|
|
324
|
+
...cursor
|
|
325
|
+
});
|
|
326
|
+
$[16] = CursorComponent;
|
|
327
|
+
$[17] = cursorProps;
|
|
328
|
+
$[18] = t3$1;
|
|
329
|
+
} else t3$1 = $[18];
|
|
330
|
+
t2 = cursors.map(t3$1);
|
|
331
|
+
$[12] = CursorComponent;
|
|
332
|
+
$[13] = cursorProps;
|
|
333
|
+
$[14] = cursors;
|
|
334
|
+
$[15] = t2;
|
|
335
|
+
} else t2 = $[15];
|
|
336
|
+
let t3;
|
|
337
|
+
if ($[19] !== t2) {
|
|
338
|
+
t3 = /* @__PURE__ */ React.createElement(React.Fragment, null, t2);
|
|
339
|
+
$[19] = t2;
|
|
340
|
+
$[20] = t3;
|
|
341
|
+
} else t3 = $[20];
|
|
342
|
+
return t3;
|
|
343
|
+
}
|
|
344
|
+
function CursorOverlay(props) {
|
|
345
|
+
const $ = c(2);
|
|
346
|
+
if (!useEditorMounted()) return null;
|
|
347
|
+
let t0;
|
|
348
|
+
if ($[0] !== props) {
|
|
349
|
+
t0 = /* @__PURE__ */ React.createElement(CursorOverlayContent, props);
|
|
350
|
+
$[0] = props;
|
|
351
|
+
$[1] = t0;
|
|
352
|
+
} else t0 = $[1];
|
|
353
|
+
return t0;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
//#endregion
|
|
357
|
+
export { CursorOverlay, CursorOverlayContent, FROZEN_EMPTY_ARRAY, getCaretPosition, getCursorOverlayState, getSelectionRects, useCursorOverlayPositions, useRefreshOnResize, useRequestReRender };
|
|
358
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["React","useRequestReRender","setUpdateCounter","useState","animationFrameRef","useRef","requestReRender","useCallback","immediate","current","requestAnimationFrame","state","cancelAnimationFrame","useEffect","React","TRange","CursorOverlayProps","SelectionRect","useRequestReRender","useRefreshOnResizeOptions","Pick","selectionRectCache","MutableRefObject","WeakMap","useRefreshOnResize","t0","$","_c","containerRef","refreshOnResize","selectionRectCacheRef","requestReRender","t1","t2","sync","undefined","current","refresh","t3","resizeObserver","ResizeObserver","observe","disconnect","useEffect","t4","TRange","RangeApi","CaretPosition","SelectionRect","getCaretPosition","selectionRects","range","isCollapsed","isBackward","anchorRect","length","height","left","width","top","UnknownObject","CursorOverlayProps","CursorOverlayState","SelectionRect","FROZEN_EMPTY_ARRAY","getCaretPosition","getCursorOverlayState","cursors","cursorStates","selectionRects","Record","Pick","TCursorData","Object","entries","map","key","cursorState","selection","rects","caretPosition","Editor","TRange","PathApi","RangeApi","TextApi","SelectionRect","getSelectionRects","editor","range","xOffset","yOffset","start","end","edges","domRange","api","toDOMRange","selectionRects","textEntries","nodes","at","match","isText","textNode","textPath","domNode","toDOMNode","parentElement","isStartNode","equals","path","isEndNode","clientRects","DOMRectList","nodeRange","document","createRange","selectNode","setStart","startContainer","startOffset","setEnd","endContainer","endOffset","getClientRects","i","length","clientRect","item","push","height","left","top","width","React","TRange","UnknownObject","useEditorRef","useIsomorphicLayoutEffect","CursorOverlayProps","CursorState","SelectionRect","getCursorOverlayState","getSelectionRects","useRefreshOnResize","FROZEN_EMPTY_ARRAY","Object","freeze","useCursorOverlayPositions","t0","$","_c","t1","undefined","containerRef","cursors","cursorStates","refreshOnResize","t2","editor","t3","Symbol","for","WeakMap","selectionRectCache","useRef","t4","selectionRects","setSelectionRects","useState","t5","current","xOffset","yOffset","contentRect","getBoundingClientRect","x","y","scrollTop","selectionRectsChanged","keys","length","getCachedSelectionRects","t6","cursor","range","selection","cached","get","rects","set","updated","fromEntries","entries","map","t7","key","cursor_0","updateSelectionRects","t8","refresh","t9","React","UnknownObject","useEditorMounted","CursorData","CursorOverlayState","CursorState","SelectionRect","useCursorOverlayPositions","CursorOverlayProps","Pick","CursorProps","containerRef","RefObject","HTMLElement","cursors","Record","TCursorData","onRenderCursor","FC","refreshOnResize","id","classNames","Partial","caret","selectionRect","disableCaret","disableSelection","onRenderCaret","onRenderSelectionRect","CursorOverlayContent","t0","$","_c","CursorComponent","props","t1","cursorProps","t2","t3","cursor","key","map","CursorOverlay","isMounted"],"sources":["../src/hooks/useRequestReRender.ts","../src/hooks/useRefreshOnResize.ts","../src/queries/getCaretPosition.ts","../src/queries/getCursorOverlayState.ts","../src/queries/getSelectionRects.ts","../src/hooks/useCursorOverlayPositions.ts","../src/components/CursorOverlay.tsx"],"sourcesContent":["import React from 'react';\n\nexport const useRequestReRender = () => {\n const [, setUpdateCounter] = React.useState(0);\n const animationFrameRef = React.useRef<number | null>(null);\n\n const requestReRender = React.useCallback((immediate = false) => {\n if (animationFrameRef.current && !immediate) {\n return;\n }\n if (!immediate) {\n animationFrameRef.current = requestAnimationFrame(() => {\n setUpdateCounter((state) => state + 1);\n animationFrameRef.current = null;\n });\n\n return;\n }\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n\n setUpdateCounter((state) => state + 1);\n }, []);\n\n React.useEffect(() => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n });\n\n React.useEffect(\n () => () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n },\n []\n );\n\n return requestReRender;\n};\n","import React from 'react';\n\nimport type { TRange } from 'platejs';\n\nimport type { CursorOverlayProps } from '../components';\nimport type { SelectionRect } from '../types';\n\nimport { useRequestReRender } from './useRequestReRender';\n\nexport interface useRefreshOnResizeOptions\n extends Pick<CursorOverlayProps, 'containerRef' | 'refreshOnResize'> {\n selectionRectCache: React.MutableRefObject<WeakMap<TRange, SelectionRect[]>>;\n}\n\nexport const useRefreshOnResize = ({\n containerRef,\n refreshOnResize,\n selectionRectCache: selectionRectCacheRef,\n}: useRefreshOnResizeOptions) => {\n const requestReRender = useRequestReRender();\n\n // Reset the selection rect cache and request re-render.\n // ✅ Mutating ref inside callback is OK - callbacks run in response to events, not during render\n const refresh = React.useCallback(\n (sync = false) => {\n selectionRectCacheRef.current = new WeakMap();\n requestReRender(sync);\n },\n [requestReRender, selectionRectCacheRef]\n );\n\n // Refresh on container resize\n React.useEffect(() => {\n if (!refreshOnResize || !containerRef?.current) {\n return;\n }\n\n const resizeObserver = new ResizeObserver(() => refresh());\n resizeObserver.observe(containerRef.current);\n\n return () => resizeObserver.disconnect();\n }, [containerRef, refresh, refreshOnResize]);\n\n return {\n refresh,\n };\n};\n","import type { TRange } from 'platejs';\n\nimport { RangeApi } from 'platejs';\n\nimport type { CaretPosition, SelectionRect } from '../types';\n\n/** Get the caret position of a range from selectionRects. */\nexport const getCaretPosition = (\n selectionRects: SelectionRect[],\n range: TRange\n): CaretPosition | null => {\n const isCollapsed = range && RangeApi.isCollapsed(range);\n const isBackward = range && RangeApi.isBackward(range);\n const anchorRect = selectionRects[isBackward ? 0 : selectionRects.length - 1];\n\n if (!anchorRect) {\n return null;\n }\n\n return {\n height: anchorRect.height,\n left: anchorRect.left + (isBackward || isCollapsed ? 0 : anchorRect.width),\n top: anchorRect.top,\n };\n};\n","import type { UnknownObject } from 'platejs';\n\nimport type { CursorOverlayProps } from '../components';\nimport type { CursorOverlayState, SelectionRect } from '../types';\n\nimport { FROZEN_EMPTY_ARRAY } from '../hooks';\nimport { getCaretPosition } from './getCaretPosition';\n\n/** Get cursor overlay state from selection rects. */\nexport const getCursorOverlayState = <\n TCursorData extends UnknownObject = UnknownObject,\n>({\n cursors: cursorStates,\n selectionRects,\n}: {\n selectionRects: Record<string, SelectionRect[]>;\n} & Pick<\n CursorOverlayProps<TCursorData>,\n 'cursors'\n>): CursorOverlayState<TCursorData>[] => {\n if (!cursorStates) return [];\n\n return Object.entries(cursorStates).map(([key, cursorState]) => {\n const selection = cursorState?.selection ?? null;\n const rects = selectionRects[key] ?? FROZEN_EMPTY_ARRAY;\n\n const caretPosition = selection ? getCaretPosition(rects, selection) : null;\n\n return {\n ...cursorState,\n caretPosition,\n selection,\n selectionRects: rects,\n };\n });\n};\n","import { type Editor, type TRange, PathApi, RangeApi, TextApi } from 'platejs';\n\nimport type { SelectionRect } from '../types';\n\nexport const getSelectionRects = (\n editor: Editor,\n {\n range,\n xOffset,\n yOffset,\n }: {\n range: TRange;\n xOffset: number;\n yOffset: number;\n }\n): SelectionRect[] => {\n const [start, end] = RangeApi.edges(range);\n const domRange = editor.api.toDOMRange(range);\n\n if (!domRange) {\n return [];\n }\n\n const selectionRects: SelectionRect[] = [];\n const textEntries = editor.api.nodes({\n at: range,\n match: TextApi.isText,\n });\n\n for (const [textNode, textPath] of textEntries) {\n const domNode = editor.api.toDOMNode(textNode);\n\n // Fix: failed to execute 'selectNode' on 'Range': the given Node has no parent\n if (!domNode?.parentElement) {\n return [];\n }\n\n const isStartNode = PathApi.equals(textPath, start.path);\n const isEndNode = PathApi.equals(textPath, end.path);\n\n let clientRects: DOMRectList | null = null;\n\n if (isStartNode || isEndNode) {\n const nodeRange = document.createRange();\n\n nodeRange.selectNode(domNode);\n\n if (isStartNode) {\n nodeRange.setStart(domRange.startContainer, domRange.startOffset);\n }\n if (isEndNode) {\n nodeRange.setEnd(domRange.endContainer, domRange.endOffset);\n }\n\n clientRects = nodeRange.getClientRects();\n } else {\n clientRects = domNode.getClientRects();\n }\n\n for (let i = 0; i < clientRects.length; i++) {\n const clientRect = clientRects.item(i);\n\n if (!clientRect) {\n continue;\n }\n\n selectionRects.push({\n height: clientRect.height,\n left: clientRect.left - xOffset,\n top: clientRect.top - yOffset,\n width: clientRect.width,\n });\n }\n }\n\n return selectionRects;\n};\n","import React from 'react';\n\nimport type { TRange, UnknownObject } from 'platejs';\n\nimport { useEditorRef, useIsomorphicLayoutEffect } from 'platejs/react';\n\nimport type { CursorOverlayProps } from '../components/CursorOverlay';\nimport type { CursorState, SelectionRect } from '../types';\n\nimport { getCursorOverlayState } from '../queries/getCursorOverlayState';\nimport { getSelectionRects } from '../queries/getSelectionRects';\nimport { useRefreshOnResize } from './useRefreshOnResize';\n\nexport const FROZEN_EMPTY_ARRAY = Object.freeze(\n []\n) as unknown as SelectionRect[];\n\nexport const useCursorOverlayPositions = <TCursorData extends UnknownObject>({\n containerRef,\n cursors: cursorStates,\n refreshOnResize = true,\n}: CursorOverlayProps<TCursorData> = {}) => {\n const editor = useEditorRef();\n\n const selectionRectCache = React.useRef<WeakMap<TRange, SelectionRect[]>>(\n new WeakMap()\n );\n\n const [selectionRects, setSelectionRects] = React.useState<\n Record<string, SelectionRect[]>\n >({});\n\n const updateSelectionRects = React.useCallback(() => {\n // We have a container ref but the ref is null => container\n // isn't mounted to we can't calculate the selection rects.\n if (!containerRef?.current) return;\n if (!cursorStates) return;\n\n let xOffset = 0;\n let yOffset = 0;\n\n if (containerRef) {\n const contentRect = containerRef.current!.getBoundingClientRect();\n xOffset = contentRect.x;\n yOffset = contentRect.y;\n yOffset -= containerRef.current.scrollTop;\n }\n\n let selectionRectsChanged =\n Object.keys(selectionRects).length !== Object.keys(cursorStates).length;\n\n const getCachedSelectionRects = ({\n cursor,\n }: {\n cursor: CursorState<TCursorData>;\n }) => {\n const range = cursor.selection;\n\n if (!range) {\n return FROZEN_EMPTY_ARRAY;\n }\n\n const cached = selectionRectCache.current.get(range);\n\n if (cached) {\n return cached;\n }\n\n const rects = getSelectionRects(editor, { range, xOffset, yOffset });\n selectionRectsChanged = true;\n selectionRectCache.current.set(range, rects);\n\n return rects;\n };\n\n const updated: Record<string, SelectionRect[]> = Object.fromEntries(\n Object.entries(cursorStates).map(([key, cursor]) => [\n key,\n getCachedSelectionRects({\n cursor,\n }),\n ])\n );\n\n if (selectionRectsChanged) {\n setSelectionRects(updated);\n }\n }, [containerRef, cursorStates, editor, selectionRects]);\n\n // Update selection rects after paint\n\n useIsomorphicLayoutEffect(() => {\n updateSelectionRects();\n });\n\n const cursors = React.useMemo(\n () =>\n getCursorOverlayState({\n cursors: cursorStates,\n selectionRects,\n }),\n [cursorStates, selectionRects]\n );\n\n const { refresh } = useRefreshOnResize({\n containerRef,\n refreshOnResize,\n selectionRectCache,\n });\n\n return { cursors, refresh };\n};\n","import React from 'react';\n\nimport type { UnknownObject } from 'platejs';\n\nimport { useEditorMounted } from 'platejs/react';\n\nimport type {\n CursorData,\n CursorOverlayState,\n CursorState,\n SelectionRect,\n} from '../types';\n\nimport { useCursorOverlayPositions } from '../hooks/useCursorOverlayPositions';\n\nexport interface CursorOverlayProps<\n TCursorData extends UnknownObject = UnknownObject,\n> extends Pick<\n CursorProps<CursorData>,\n | 'classNames'\n | 'disableCaret'\n | 'disableSelection'\n | 'onRenderCaret'\n | 'onRenderSelectionRect'\n > {\n /**\n * Container the overlay will be rendered in. If set, all returned overlay\n * positions will be relative to this container.\n */\n containerRef?: React.RefObject<HTMLElement | null>;\n\n /** Cursor states to use for calculating the overlay positions, by key. */\n cursors?: Record<string, CursorState<TCursorData>>;\n\n /** Overrides `Cursor` component. */\n onRenderCursor?: React.FC<CursorProps>;\n\n /**\n * Whether to refresh the cursor overlay positions on container resize.\n *\n * @default true\n */\n refreshOnResize?: boolean;\n}\n\nexport type CursorProps<TCursorData extends UnknownObject = UnknownObject> =\n CursorOverlayState<TCursorData> & {\n id: string;\n classNames?: Partial<{\n caret: string;\n selectionRect: string;\n }>;\n /** Whether to disable the caret. */\n disableCaret?: boolean;\n /** Whether to disable the selection rects. */\n disableSelection?: boolean;\n /**\n * Custom caret component. For example, you could display a label next to\n * the caret.\n *\n * @default styled div\n */\n onRenderCaret?: React.FC<\n Pick<CursorProps<TCursorData>, 'caretPosition' | 'data'>\n >;\n /** Overrides `Caret` component */\n onRenderSelectionRect?: React.FC<\n {\n selectionRect: SelectionRect;\n } & Pick<CursorProps<TCursorData>, 'data'>\n >;\n };\n\nexport function CursorOverlayContent<\n TCursorData extends UnknownObject = UnknownObject,\n>({\n classNames,\n onRenderCaret,\n onRenderCursor: CursorComponent,\n onRenderSelectionRect,\n ...props\n}: CursorOverlayProps<TCursorData>) {\n const { disableCaret, disableSelection } = props;\n\n const { cursors } = useCursorOverlayPositions(props);\n\n const cursorProps = {\n classNames,\n disableCaret,\n disableSelection,\n onRenderCaret,\n onRenderSelectionRect,\n };\n\n if (!CursorComponent) return null;\n\n return (\n <>\n {cursors.map((cursor) => (\n <CursorComponent\n id={cursor.key}\n key={cursor.key}\n {...cursorProps}\n {...cursor}\n />\n ))}\n </>\n );\n}\n\nexport function CursorOverlay<\n TCursorData extends UnknownObject = UnknownObject,\n>(props: CursorOverlayProps<TCursorData>) {\n const isMounted = useEditorMounted();\n\n if (!isMounted) return null;\n\n return <CursorOverlayContent {...props} />;\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lofcz/platejs-cursor",
|
|
3
|
+
"version": "52.0.11",
|
|
4
|
+
"description": "Cursor for Plate React",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"plate",
|
|
7
|
+
"plugin",
|
|
8
|
+
"slate"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://platejs.org",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/udecode/plate/issues"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/lofcz/plate.git",
|
|
17
|
+
"directory": "packages/cursor"
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"exports": {
|
|
22
|
+
".": "./dist/index.js",
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"files": [
|
|
28
|
+
"dist/**/*"
|
|
29
|
+
],
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@plate/scripts": "1.0.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"platejs": ">=52.0.11",
|
|
35
|
+
"react": ">=18.0.0",
|
|
36
|
+
"react-dom": ">=18.0.0"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"type": "module",
|
|
42
|
+
"module": "./dist/index.js",
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"react-compiler-runtime": "^1.0.0"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"brl": "plate-pkg p:brl",
|
|
48
|
+
"build": "plate-pkg p:build",
|
|
49
|
+
"build:watch": "plate-pkg p:build:watch",
|
|
50
|
+
"clean": "plate-pkg p:clean",
|
|
51
|
+
"lint": "plate-pkg p:lint",
|
|
52
|
+
"lint:fix": "plate-pkg p:lint:fix",
|
|
53
|
+
"test": "plate-pkg p:test",
|
|
54
|
+
"test:watch": "plate-pkg p:test:watch",
|
|
55
|
+
"typecheck": "plate-pkg p:typecheck"
|
|
56
|
+
}
|
|
57
|
+
}
|