@liveblocks/react-tiptap 0.0.1 → 2.8.3-tiptap1
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/dist/LiveblocksExtension.js +144 -0
- package/dist/LiveblocksExtension.js.map +1 -0
- package/dist/LiveblocksExtension.mjs +142 -0
- package/dist/LiveblocksExtension.mjs.map +1 -0
- package/dist/classnames.js +8 -0
- package/dist/classnames.js.map +1 -0
- package/dist/classnames.mjs +6 -0
- package/dist/classnames.mjs.map +1 -0
- package/dist/comments/AnchoredThreads.js +178 -0
- package/dist/comments/AnchoredThreads.js.map +1 -0
- package/dist/comments/AnchoredThreads.mjs +176 -0
- package/dist/comments/AnchoredThreads.mjs.map +1 -0
- package/dist/comments/CommentsExtension.js +207 -0
- package/dist/comments/CommentsExtension.js.map +1 -0
- package/dist/comments/CommentsExtension.mjs +205 -0
- package/dist/comments/CommentsExtension.mjs.map +1 -0
- package/dist/comments/FloatingComposer.js +103 -0
- package/dist/comments/FloatingComposer.js.map +1 -0
- package/dist/comments/FloatingComposer.mjs +100 -0
- package/dist/comments/FloatingComposer.mjs.map +1 -0
- package/dist/comments/FloatingThreads.js +154 -0
- package/dist/comments/FloatingThreads.js.map +1 -0
- package/dist/comments/FloatingThreads.mjs +151 -0
- package/dist/comments/FloatingThreads.mjs.map +1 -0
- package/dist/index.d.mts +65 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +10 -0
- package/dist/index.mjs.map +1 -0
- package/dist/mentions/Avatar.js +53 -0
- package/dist/mentions/Avatar.js.map +1 -0
- package/dist/mentions/Avatar.mjs +51 -0
- package/dist/mentions/Avatar.mjs.map +1 -0
- package/dist/mentions/Mention.js +24 -0
- package/dist/mentions/Mention.js.map +1 -0
- package/dist/mentions/Mention.mjs +22 -0
- package/dist/mentions/Mention.mjs.map +1 -0
- package/dist/mentions/MentionExtension.js +221 -0
- package/dist/mentions/MentionExtension.js.map +1 -0
- package/dist/mentions/MentionExtension.mjs +219 -0
- package/dist/mentions/MentionExtension.mjs.map +1 -0
- package/dist/mentions/MentionsList.js +123 -0
- package/dist/mentions/MentionsList.js.map +1 -0
- package/dist/mentions/MentionsList.mjs +119 -0
- package/dist/mentions/MentionsList.mjs.map +1 -0
- package/dist/types.js +33 -0
- package/dist/types.js.map +1 -0
- package/dist/types.mjs +24 -0
- package/dist/types.mjs.map +1 -0
- package/dist/utils.js +47 -0
- package/dist/utils.js.map +1 -0
- package/dist/utils.mjs +43 -0
- package/dist/utils.mjs.map +1 -0
- package/dist/version-history/HistoryVersionPreview.js +79 -0
- package/dist/version-history/HistoryVersionPreview.js.map +1 -0
- package/dist/version-history/HistoryVersionPreview.mjs +77 -0
- package/dist/version-history/HistoryVersionPreview.mjs.map +1 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/dist/version.mjs +6 -0
- package/dist/version.mjs.map +1 -0
- package/package.json +77 -1
- package/src/styles/constants.css +9 -0
- package/src/styles/index.css +247 -0
- package/src/styles/utils.css +6 -0
- package/styles.css +1 -0
- package/styles.css.d.ts +1 -0
- package/styles.css.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommentsExtension.mjs","sources":["../../src/comments/CommentsExtension.ts"],"sourcesContent":["import { Extension, Mark, mergeAttributes } from \"@tiptap/core\";\nimport type { Node } from \"@tiptap/pm/model\";\nimport type { Transaction } from \"@tiptap/pm/state\";\nimport { Plugin, TextSelection } from \"@tiptap/pm/state\";\nimport { Decoration, DecorationSet } from \"@tiptap/pm/view\";\nimport { ySyncPluginKey } from \"y-prosemirror\";\n\nimport type { CommentsExtensionStorage, ThreadPluginState } from \"../types\";\nimport {\n ACTIVE_SELECTION_PLUGIN,\n LIVEBLOCKS_COMMENT_MARK_TYPE,\n ThreadPluginActions,\n THREADS_PLUGIN_KEY,\n} from \"../types\";\n\ntype ThreadPluginAction = {\n name: ThreadPluginActions;\n data: string | null;\n};\n\n/**\n * Known issues: Overlapping marks are merged when reloading the doc. May be related:\n * https://github.com/ueberdosis/tiptap/issues/4339\n * https://github.com/yjs/y-prosemirror/issues/47\n */\nconst Comment = Mark.create({\n name: LIVEBLOCKS_COMMENT_MARK_TYPE,\n excludes: \"\",\n inclusive: false,\n keepOnSplit: true,\n addAttributes() {\n // Return an object with attribute configuration\n return {\n threadId: {\n parseHTML: (element) => element.getAttribute(\"data-lb-thread-id\"),\n renderHTML: (attributes) => {\n return {\n \"data-lb-thread-id\": (attributes as { threadId: string }).threadId,\n };\n },\n default: \"\",\n },\n };\n },\n\n renderHTML({ HTMLAttributes }: { HTMLAttributes: Record<string, any> }) {\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n class: \"lb-root lb-tiptap-thread-mark\",\n }),\n ];\n },\n\n /**\n * This plugin tracks the (first) position of each thread mark in the doc and creates a decoration for the selected thread\n */\n addProseMirrorPlugins() {\n const updateState = (doc: Node, selectedThreadId: string | null) => {\n const threadPositions = new Map<string, { from: number; to: number }>();\n const decorations: Decoration[] = [];\n // find all thread marks and store their position + create decoration for selected thread\n doc.descendants((node, pos) => {\n node.marks.forEach((mark) => {\n if (mark.type === this.type) {\n const thisThreadId = (\n mark.attrs as { threadId: string | undefined }\n ).threadId;\n if (!thisThreadId) {\n return;\n }\n const from = pos;\n const to = from + node.nodeSize;\n\n // FloatingThreads component uses \"to\" as the position, so always store the largest \"to\" found\n // AnchoredThreads component uses \"from\" as the position, so always store the smallest \"from\" found\n const currentPosition = threadPositions.get(thisThreadId) ?? {\n from: Infinity,\n to: 0,\n };\n threadPositions.set(thisThreadId, {\n from: Math.min(from, currentPosition.from),\n to: Math.max(to, currentPosition.to),\n });\n\n if (selectedThreadId === thisThreadId) {\n decorations.push(\n Decoration.inline(from, to, {\n class: \"lb-root lb-tiptap-thread-mark-selected\",\n })\n );\n }\n }\n });\n });\n return {\n decorations: DecorationSet.create(doc, decorations),\n selectedThreadId,\n threadPositions,\n selectedThreadPos:\n selectedThreadId !== null\n ? threadPositions.get(selectedThreadId)?.to ?? null\n : null,\n };\n };\n\n return [\n new Plugin({\n key: THREADS_PLUGIN_KEY,\n state: {\n init() {\n return {\n threadPositions: new Map<string, { from: number; to: number }>(),\n selectedThreadId: null,\n selectedThreadPos: null,\n decorations: DecorationSet.empty,\n } as ThreadPluginState;\n },\n apply(tr, state) {\n const action = tr.getMeta(THREADS_PLUGIN_KEY) as ThreadPluginAction;\n if (!tr.docChanged && !action) {\n return state;\n }\n\n if (!action) {\n // Doc changed, but no action, just update rects\n return updateState(tr.doc, state.selectedThreadId);\n }\n // handle actions, possibly support more actions\n if (\n action.name === ThreadPluginActions.SET_SELECTED_THREAD_ID &&\n state.selectedThreadId !== action.data\n ) {\n return updateState(tr.doc, action.data);\n }\n\n return state;\n },\n },\n props: {\n decorations: (state) => {\n return (\n THREADS_PLUGIN_KEY.getState(state)?.decorations ??\n DecorationSet.empty\n );\n },\n handleClick: (view, pos, event) => {\n if (event.button !== 0) {\n return;\n }\n\n const selectThread = (threadId: string | null) => {\n view.dispatch(\n view.state.tr.setMeta(THREADS_PLUGIN_KEY, {\n name: ThreadPluginActions.SET_SELECTED_THREAD_ID,\n data: threadId,\n })\n );\n };\n\n const node = view.state.doc.nodeAt(pos);\n if (!node) {\n selectThread(null);\n return;\n }\n const threadId = node.marks.find((mark) => mark.type === this.type)\n ?.attrs.threadId as string | undefined;\n selectThread(threadId ?? null);\n },\n },\n }),\n ];\n },\n});\n\nexport const CommentsExtension = Extension.create<\n never,\n CommentsExtensionStorage\n>({\n name: \"liveblocksComments\",\n addExtensions() {\n return [Comment];\n },\n\n addStorage() {\n return {\n pendingCommentSelection: null,\n };\n },\n\n addCommands() {\n return {\n addPendingComment: () => () => {\n if (this.editor.state.selection.empty) {\n return false;\n }\n // unselect any open threads\n this.editor.view.dispatch(\n this.editor.state.tr.setMeta(THREADS_PLUGIN_KEY, {\n name: ThreadPluginActions.SET_SELECTED_THREAD_ID,\n data: null,\n })\n );\n this.storage.pendingCommentSelection = new TextSelection(\n this.editor.state.selection.$anchor,\n this.editor.state.selection.$head\n );\n return true;\n },\n selectThread: (id: string | null) => () => {\n this.editor.view.dispatch(\n this.editor.state.tr.setMeta(THREADS_PLUGIN_KEY, {\n name: ThreadPluginActions.SET_SELECTED_THREAD_ID,\n data: id,\n })\n );\n return true;\n },\n addComment:\n (id: string) =>\n ({ commands }) => {\n if (!this.storage.pendingCommentSelection) {\n return false;\n }\n this.editor.state.selection = this.storage.pendingCommentSelection;\n commands.setMark(LIVEBLOCKS_COMMENT_MARK_TYPE, { threadId: id });\n this.storage.pendingCommentSelection = null;\n\n return true;\n },\n };\n },\n\n //@ts-expect-error - this is incorrectly typed upstream in Mark.ts of TipTap. This event does include transaction\n // correct: https://github.com/ueberdosis/tiptap/blob/2ff327ced84df6865b4ef98947b667aa79992292/packages/core/src/types.ts#L60\n // incorrect: https://github.com/ueberdosis/tiptap/blob/2ff327ced84df6865b4ef98947b667aa79992292/packages/core/src/Mark.ts#L330\n onSelectionUpdate(\n this: { storage: Storage }, // NOTE: there are more types here I didn't override, this gets removed after submitting PR to tiptap\n { transaction }: { transaction: Transaction } // TODO: remove this after submitting PR to tiptap\n ) {\n // ignore changes made by yjs\n if (\n !this.storage.pendingCommentSelection ||\n transaction.getMeta(ySyncPluginKey)\n ) {\n return;\n }\n this.storage.pendingCommentSelection = null;\n },\n // TODO: this.storage.pendingCommentSelection needs to be a Yjs Relative Position that gets translated back to absolute position.\n // Commit: eba949d32d6010a3d8b3f7967d73d4deb015b02a has code that can help with this.\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: ACTIVE_SELECTION_PLUGIN,\n props: {\n decorations: ({ doc }) => {\n const active = this.storage.pendingCommentSelection !== null;\n if (!active) {\n return DecorationSet.create(doc, []);\n }\n const { from, to } = this.storage\n .pendingCommentSelection as TextSelection;\n const decorations: Decoration[] = [\n Decoration.inline(from, to, {\n class: \"lb-root lb-tiptap-active-selection\",\n }),\n ];\n return DecorationSet.create(doc, decorations);\n },\n },\n }),\n ];\n },\n});\n"],"names":["threadId"],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var reactDom = require('@floating-ui/react-dom');
|
|
4
|
+
var react = require('@liveblocks/react');
|
|
5
|
+
var reactUi = require('@liveblocks/react-ui');
|
|
6
|
+
var React = require('react');
|
|
7
|
+
var utils = require('../utils.js');
|
|
8
|
+
|
|
9
|
+
const FLOATING_COMPOSER_COLLISION_PADDING = 10;
|
|
10
|
+
const FloatingComposer = React.forwardRef(function FloatingComposer2(props, forwardedRef) {
|
|
11
|
+
const createThread = react.useCreateThread();
|
|
12
|
+
const { editor } = props;
|
|
13
|
+
const storage = editor?.storage.liveblocksComments;
|
|
14
|
+
const showComposer = !!storage?.pendingCommentSelection;
|
|
15
|
+
const {
|
|
16
|
+
refs: { setReference, setFloating },
|
|
17
|
+
strategy,
|
|
18
|
+
x,
|
|
19
|
+
y
|
|
20
|
+
} = reactDom.useFloating({
|
|
21
|
+
strategy: "fixed",
|
|
22
|
+
placement: "bottom",
|
|
23
|
+
middleware: [
|
|
24
|
+
reactDom.flip({ padding: FLOATING_COMPOSER_COLLISION_PADDING, crossAxis: false }),
|
|
25
|
+
reactDom.offset(10),
|
|
26
|
+
reactDom.hide({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),
|
|
27
|
+
reactDom.shift({
|
|
28
|
+
padding: FLOATING_COMPOSER_COLLISION_PADDING,
|
|
29
|
+
limiter: reactDom.limitShift()
|
|
30
|
+
}),
|
|
31
|
+
reactDom.size({ padding: FLOATING_COMPOSER_COLLISION_PADDING })
|
|
32
|
+
],
|
|
33
|
+
whileElementsMounted: (...args) => {
|
|
34
|
+
return reactDom.autoUpdate(...args, {
|
|
35
|
+
animationFrame: true
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
React.useEffect(() => {
|
|
40
|
+
if (!editor || !showComposer) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const updateRect = () => {
|
|
44
|
+
if (!storage) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const seclection = storage.pendingCommentSelection;
|
|
48
|
+
if (!seclection) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const coords = editor.view.coordsAtPos(Math.min(seclection.from, editor.state.doc.content.size - 1));
|
|
52
|
+
if (coords) {
|
|
53
|
+
setReference({
|
|
54
|
+
getBoundingClientRect: () => utils.getRectFromCoords(coords)
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
updateRect();
|
|
59
|
+
editor.on("update", updateRect);
|
|
60
|
+
return () => {
|
|
61
|
+
editor.off("update", updateRect);
|
|
62
|
+
};
|
|
63
|
+
}, [editor, setReference, showComposer]);
|
|
64
|
+
const handleComposerSubmit = React.useCallback(
|
|
65
|
+
({ body }, event) => {
|
|
66
|
+
if (!editor) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
event.preventDefault();
|
|
70
|
+
const thread = createThread({
|
|
71
|
+
body
|
|
72
|
+
});
|
|
73
|
+
editor.commands.addComment(thread.id);
|
|
74
|
+
},
|
|
75
|
+
[editor, createThread]
|
|
76
|
+
);
|
|
77
|
+
if (!showComposer || !editor) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
81
|
+
className: "lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-composer",
|
|
82
|
+
ref: setFloating,
|
|
83
|
+
style: {
|
|
84
|
+
position: strategy,
|
|
85
|
+
top: 0,
|
|
86
|
+
left: 0,
|
|
87
|
+
transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,
|
|
88
|
+
minWidth: "max-content"
|
|
89
|
+
}
|
|
90
|
+
}, /* @__PURE__ */ React.createElement(reactUi.Composer, {
|
|
91
|
+
ref: forwardedRef,
|
|
92
|
+
...props,
|
|
93
|
+
onComposerSubmit: handleComposerSubmit,
|
|
94
|
+
onClick: (e) => {
|
|
95
|
+
e.stopPropagation();
|
|
96
|
+
},
|
|
97
|
+
autoFocus: true
|
|
98
|
+
}));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
exports.FLOATING_COMPOSER_COLLISION_PADDING = FLOATING_COMPOSER_COLLISION_PADDING;
|
|
102
|
+
exports.FloatingComposer = FloatingComposer;
|
|
103
|
+
//# sourceMappingURL=FloatingComposer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FloatingComposer.js","sources":["../../src/comments/FloatingComposer.tsx"],"sourcesContent":["import { autoUpdate, flip, hide, limitShift, offset, shift, size, useFloating } from \"@floating-ui/react-dom\";\nimport type { BaseMetadata } from \"@liveblocks/client\";\nimport type { DM } from \"@liveblocks/core\";\nimport { useCreateThread } from \"@liveblocks/react\";\nimport type { ComposerProps, ComposerSubmitComment } from \"@liveblocks/react-ui\";\nimport { Composer } from \"@liveblocks/react-ui\";\nimport type { Editor } from \"@tiptap/react\";\nimport type { ComponentRef, FormEvent } from \"react\";\nimport React, { forwardRef, useCallback, useEffect } from \"react\";\n\nimport type { CommentsExtensionStorage } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\nexport type FloatingComposerProps<M extends BaseMetadata = DM> = Omit<\n ComposerProps<M>,\n \"threadId\" | \"commentId\"\n> & {\n editor: Editor | null;\n};\n\ntype ComposerElement = ComponentRef<typeof Composer>;\n\nexport const FLOATING_COMPOSER_COLLISION_PADDING = 10;\n\nexport const FloatingComposer = forwardRef<\n ComposerElement,\n FloatingComposerProps\n>(function FloatingComposer(props, forwardedRef) {\n const createThread = useCreateThread();\n const { editor } = props;\n\n const storage = editor?.storage.liveblocksComments as CommentsExtensionStorage | undefined;\n\n const showComposer = !!storage?.pendingCommentSelection;\n\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"fixed\",\n placement: \"bottom\",\n middleware: [\n flip({ padding: FLOATING_COMPOSER_COLLISION_PADDING, crossAxis: false }),\n offset(10),\n hide({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n shift({\n padding: FLOATING_COMPOSER_COLLISION_PADDING,\n limiter: limitShift(),\n }),\n size({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n useEffect(() => {\n if (!editor || !showComposer) {\n return;\n }\n const updateRect = () => {\n if (!storage) {\n return;\n }\n const seclection = storage.pendingCommentSelection;\n if (!seclection) {\n return;\n }\n const coords = editor.view.coordsAtPos(Math.min(seclection.from, editor.state.doc.content.size - 1));\n if (coords) {\n setReference({\n getBoundingClientRect: () => getRectFromCoords(coords)\n });\n }\n }\n updateRect();\n editor.on(\"update\", updateRect);\n return () => {\n editor.off(\"update\", updateRect);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [editor, setReference, showComposer]); // don't need storage explicitly here\n\n\n // Submit a new thread and update the comment highlight to show a completed highlight\n const handleComposerSubmit = useCallback(\n ({ body }: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n if (!editor) {\n return;\n }\n event.preventDefault();\n\n const thread = createThread({\n body,\n });\n editor.commands.addComment(thread.id);\n\n },\n [editor, createThread]\n );\n\n if (!showComposer || !editor) {\n return null;\n }\n\n return (\n <div\n className=\"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-composer\"\n ref={setFloating} style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n minWidth: \"max-content\",\n }}>\n <Composer\n ref={forwardedRef}\n {...props}\n onComposerSubmit={handleComposerSubmit}\n onClick={(e) => {\n // Don't send up a click event from emoji popout and close the composer\n e.stopPropagation();\n }}\n autoFocus={true}\n />\n </div>\n );\n});"],"names":["forwardRef","FloatingComposer","useCreateThread","useFloating","flip","offset","hide","shift","limitShift","size","autoUpdate","useEffect","getRectFromCoords","useCallback","Composer"],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { useFloating, flip, offset, hide, shift, limitShift, size, autoUpdate } from '@floating-ui/react-dom';
|
|
2
|
+
import { useCreateThread } from '@liveblocks/react';
|
|
3
|
+
import { Composer } from '@liveblocks/react-ui';
|
|
4
|
+
import React, { forwardRef, useEffect, useCallback } from 'react';
|
|
5
|
+
import { getRectFromCoords } from '../utils.mjs';
|
|
6
|
+
|
|
7
|
+
const FLOATING_COMPOSER_COLLISION_PADDING = 10;
|
|
8
|
+
const FloatingComposer = forwardRef(function FloatingComposer2(props, forwardedRef) {
|
|
9
|
+
const createThread = useCreateThread();
|
|
10
|
+
const { editor } = props;
|
|
11
|
+
const storage = editor?.storage.liveblocksComments;
|
|
12
|
+
const showComposer = !!storage?.pendingCommentSelection;
|
|
13
|
+
const {
|
|
14
|
+
refs: { setReference, setFloating },
|
|
15
|
+
strategy,
|
|
16
|
+
x,
|
|
17
|
+
y
|
|
18
|
+
} = useFloating({
|
|
19
|
+
strategy: "fixed",
|
|
20
|
+
placement: "bottom",
|
|
21
|
+
middleware: [
|
|
22
|
+
flip({ padding: FLOATING_COMPOSER_COLLISION_PADDING, crossAxis: false }),
|
|
23
|
+
offset(10),
|
|
24
|
+
hide({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),
|
|
25
|
+
shift({
|
|
26
|
+
padding: FLOATING_COMPOSER_COLLISION_PADDING,
|
|
27
|
+
limiter: limitShift()
|
|
28
|
+
}),
|
|
29
|
+
size({ padding: FLOATING_COMPOSER_COLLISION_PADDING })
|
|
30
|
+
],
|
|
31
|
+
whileElementsMounted: (...args) => {
|
|
32
|
+
return autoUpdate(...args, {
|
|
33
|
+
animationFrame: true
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (!editor || !showComposer) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const updateRect = () => {
|
|
42
|
+
if (!storage) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const seclection = storage.pendingCommentSelection;
|
|
46
|
+
if (!seclection) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const coords = editor.view.coordsAtPos(Math.min(seclection.from, editor.state.doc.content.size - 1));
|
|
50
|
+
if (coords) {
|
|
51
|
+
setReference({
|
|
52
|
+
getBoundingClientRect: () => getRectFromCoords(coords)
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
updateRect();
|
|
57
|
+
editor.on("update", updateRect);
|
|
58
|
+
return () => {
|
|
59
|
+
editor.off("update", updateRect);
|
|
60
|
+
};
|
|
61
|
+
}, [editor, setReference, showComposer]);
|
|
62
|
+
const handleComposerSubmit = useCallback(
|
|
63
|
+
({ body }, event) => {
|
|
64
|
+
if (!editor) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
const thread = createThread({
|
|
69
|
+
body
|
|
70
|
+
});
|
|
71
|
+
editor.commands.addComment(thread.id);
|
|
72
|
+
},
|
|
73
|
+
[editor, createThread]
|
|
74
|
+
);
|
|
75
|
+
if (!showComposer || !editor) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
79
|
+
className: "lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-composer",
|
|
80
|
+
ref: setFloating,
|
|
81
|
+
style: {
|
|
82
|
+
position: strategy,
|
|
83
|
+
top: 0,
|
|
84
|
+
left: 0,
|
|
85
|
+
transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,
|
|
86
|
+
minWidth: "max-content"
|
|
87
|
+
}
|
|
88
|
+
}, /* @__PURE__ */ React.createElement(Composer, {
|
|
89
|
+
ref: forwardedRef,
|
|
90
|
+
...props,
|
|
91
|
+
onComposerSubmit: handleComposerSubmit,
|
|
92
|
+
onClick: (e) => {
|
|
93
|
+
e.stopPropagation();
|
|
94
|
+
},
|
|
95
|
+
autoFocus: true
|
|
96
|
+
}));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
export { FLOATING_COMPOSER_COLLISION_PADDING, FloatingComposer };
|
|
100
|
+
//# sourceMappingURL=FloatingComposer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FloatingComposer.mjs","sources":["../../src/comments/FloatingComposer.tsx"],"sourcesContent":["import { autoUpdate, flip, hide, limitShift, offset, shift, size, useFloating } from \"@floating-ui/react-dom\";\nimport type { BaseMetadata } from \"@liveblocks/client\";\nimport type { DM } from \"@liveblocks/core\";\nimport { useCreateThread } from \"@liveblocks/react\";\nimport type { ComposerProps, ComposerSubmitComment } from \"@liveblocks/react-ui\";\nimport { Composer } from \"@liveblocks/react-ui\";\nimport type { Editor } from \"@tiptap/react\";\nimport type { ComponentRef, FormEvent } from \"react\";\nimport React, { forwardRef, useCallback, useEffect } from \"react\";\n\nimport type { CommentsExtensionStorage } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\nexport type FloatingComposerProps<M extends BaseMetadata = DM> = Omit<\n ComposerProps<M>,\n \"threadId\" | \"commentId\"\n> & {\n editor: Editor | null;\n};\n\ntype ComposerElement = ComponentRef<typeof Composer>;\n\nexport const FLOATING_COMPOSER_COLLISION_PADDING = 10;\n\nexport const FloatingComposer = forwardRef<\n ComposerElement,\n FloatingComposerProps\n>(function FloatingComposer(props, forwardedRef) {\n const createThread = useCreateThread();\n const { editor } = props;\n\n const storage = editor?.storage.liveblocksComments as CommentsExtensionStorage | undefined;\n\n const showComposer = !!storage?.pendingCommentSelection;\n\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"fixed\",\n placement: \"bottom\",\n middleware: [\n flip({ padding: FLOATING_COMPOSER_COLLISION_PADDING, crossAxis: false }),\n offset(10),\n hide({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n shift({\n padding: FLOATING_COMPOSER_COLLISION_PADDING,\n limiter: limitShift(),\n }),\n size({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n useEffect(() => {\n if (!editor || !showComposer) {\n return;\n }\n const updateRect = () => {\n if (!storage) {\n return;\n }\n const seclection = storage.pendingCommentSelection;\n if (!seclection) {\n return;\n }\n const coords = editor.view.coordsAtPos(Math.min(seclection.from, editor.state.doc.content.size - 1));\n if (coords) {\n setReference({\n getBoundingClientRect: () => getRectFromCoords(coords)\n });\n }\n }\n updateRect();\n editor.on(\"update\", updateRect);\n return () => {\n editor.off(\"update\", updateRect);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [editor, setReference, showComposer]); // don't need storage explicitly here\n\n\n // Submit a new thread and update the comment highlight to show a completed highlight\n const handleComposerSubmit = useCallback(\n ({ body }: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n if (!editor) {\n return;\n }\n event.preventDefault();\n\n const thread = createThread({\n body,\n });\n editor.commands.addComment(thread.id);\n\n },\n [editor, createThread]\n );\n\n if (!showComposer || !editor) {\n return null;\n }\n\n return (\n <div\n className=\"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-composer\"\n ref={setFloating} style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n minWidth: \"max-content\",\n }}>\n <Composer\n ref={forwardedRef}\n {...props}\n onComposerSubmit={handleComposerSubmit}\n onClick={(e) => {\n // Don't send up a click event from emoji popout and close the composer\n e.stopPropagation();\n }}\n autoFocus={true}\n />\n </div>\n );\n});"],"names":["FloatingComposer"],"mappings":";;;;;;AAsBO,MAAM,mCAAsC,GAAA,GAAA;AAE5C,MAAM,gBAAmB,GAAA,UAAA,CAG9B,SAASA,iBAAAA,CAAiB,OAAO,YAAc,EAAA;AAC/C,EAAA,MAAM,eAAe,eAAgB,EAAA,CAAA;AACrC,EAAM,MAAA,EAAE,QAAW,GAAA,KAAA,CAAA;AAEnB,EAAM,MAAA,OAAA,GAAU,QAAQ,OAAQ,CAAA,kBAAA,CAAA;AAEhC,EAAM,MAAA,YAAA,GAAe,CAAC,CAAC,OAAS,EAAA,uBAAA,CAAA;AAEhC,EAAM,MAAA;AAAA,IACJ,IAAA,EAAM,EAAE,YAAA,EAAc,WAAY,EAAA;AAAA,IAClC,QAAA;AAAA,IACA,CAAA;AAAA,IACA,CAAA;AAAA,MACE,WAAY,CAAA;AAAA,IACd,QAAU,EAAA,OAAA;AAAA,IACV,SAAW,EAAA,QAAA;AAAA,IACX,UAAY,EAAA;AAAA,MACV,KAAK,EAAE,OAAA,EAAS,mCAAqC,EAAA,SAAA,EAAW,OAAO,CAAA;AAAA,MACvE,OAAO,EAAE,CAAA;AAAA,MACT,IAAK,CAAA,EAAE,OAAS,EAAA,mCAAA,EAAqC,CAAA;AAAA,MACrD,KAAM,CAAA;AAAA,QACJ,OAAS,EAAA,mCAAA;AAAA,QACT,SAAS,UAAW,EAAA;AAAA,OACrB,CAAA;AAAA,MACD,IAAK,CAAA,EAAE,OAAS,EAAA,mCAAA,EAAqC,CAAA;AAAA,KACvD;AAAA,IACA,oBAAA,EAAsB,IAAI,IAAS,KAAA;AACjC,MAAO,OAAA,UAAA,CAAW,GAAG,IAAM,EAAA;AAAA,QACzB,cAAgB,EAAA,IAAA;AAAA,OACjB,CAAA,CAAA;AAAA,KACH;AAAA,GACD,CAAA,CAAA;AAED,EAAA,SAAA,CAAU,MAAM;AACd,IAAI,IAAA,CAAC,MAAU,IAAA,CAAC,YAAc,EAAA;AAC5B,MAAA,OAAA;AAAA,KACF;AACA,IAAA,MAAM,aAAa,MAAM;AACvB,MAAA,IAAI,CAAC,OAAS,EAAA;AACZ,QAAA,OAAA;AAAA,OACF;AACA,MAAA,MAAM,aAAa,OAAQ,CAAA,uBAAA,CAAA;AAC3B,MAAA,IAAI,CAAC,UAAY,EAAA;AACf,QAAA,OAAA;AAAA,OACF;AACA,MAAA,MAAM,MAAS,GAAA,MAAA,CAAO,IAAK,CAAA,WAAA,CAAY,KAAK,GAAI,CAAA,UAAA,CAAW,IAAM,EAAA,MAAA,CAAO,KAAM,CAAA,GAAA,CAAI,OAAQ,CAAA,IAAA,GAAO,CAAC,CAAC,CAAA,CAAA;AACnG,MAAA,IAAI,MAAQ,EAAA;AACV,QAAa,YAAA,CAAA;AAAA,UACX,qBAAA,EAAuB,MAAM,iBAAA,CAAkB,MAAM,CAAA;AAAA,SACtD,CAAA,CAAA;AAAA,OACH;AAAA,KACF,CAAA;AACA,IAAW,UAAA,EAAA,CAAA;AACX,IAAO,MAAA,CAAA,EAAA,CAAG,UAAU,UAAU,CAAA,CAAA;AAC9B,IAAA,OAAO,MAAM;AACX,MAAO,MAAA,CAAA,GAAA,CAAI,UAAU,UAAU,CAAA,CAAA;AAAA,KACjC,CAAA;AAAA,GAEC,EAAA,CAAC,MAAQ,EAAA,YAAA,EAAc,YAAY,CAAC,CAAA,CAAA;AAIvC,EAAA,MAAM,oBAAuB,GAAA,WAAA;AAAA,IAC3B,CAAC,EAAE,IAAK,EAAA,EAA0B,KAAsC,KAAA;AACtE,MAAA,IAAI,CAAC,MAAQ,EAAA;AACX,QAAA,OAAA;AAAA,OACF;AACA,MAAA,KAAA,CAAM,cAAe,EAAA,CAAA;AAErB,MAAA,MAAM,SAAS,YAAa,CAAA;AAAA,QAC1B,IAAA;AAAA,OACD,CAAA,CAAA;AACD,MAAO,MAAA,CAAA,QAAA,CAAS,UAAW,CAAA,MAAA,CAAO,EAAE,CAAA,CAAA;AAAA,KAEtC;AAAA,IACA,CAAC,QAAQ,YAAY,CAAA;AAAA,GACvB,CAAA;AAEA,EAAI,IAAA,CAAC,YAAgB,IAAA,CAAC,MAAQ,EAAA;AAC5B,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAEA,EAAA,uBACG,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA;AAAA,IACC,SAAU,EAAA,+EAAA;AAAA,IACV,GAAK,EAAA,WAAA;AAAA,IAAa,KAAO,EAAA;AAAA,MACvB,QAAU,EAAA,QAAA;AAAA,MACV,GAAK,EAAA,CAAA;AAAA,MACL,IAAM,EAAA,CAAA;AAAA,MACN,SAAA,EAAW,eAAe,IAAK,CAAA,KAAA,CAAM,CAAC,CAAQ,CAAA,IAAA,EAAA,IAAA,CAAK,MAAM,CAAC,CAAA,CAAA,MAAA,CAAA;AAAA,MAC1D,QAAU,EAAA,aAAA;AAAA,KACZ;AAAA,GAAA,kBACC,KAAA,CAAA,aAAA,CAAA,QAAA,EAAA;AAAA,IACC,GAAK,EAAA,YAAA;AAAA,IACJ,GAAG,KAAA;AAAA,IACJ,gBAAkB,EAAA,oBAAA;AAAA,IAClB,OAAA,EAAS,CAAC,CAAM,KAAA;AAEd,MAAA,CAAA,CAAE,eAAgB,EAAA,CAAA;AAAA,KACpB;AAAA,IACA,SAAW,EAAA,IAAA;AAAA,GACb,CACF,CAAA,CAAA;AAEJ,CAAC;;;;"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var reactDom = require('@floating-ui/react-dom');
|
|
4
|
+
var reactUi = require('@liveblocks/react-ui');
|
|
5
|
+
var React = require('react');
|
|
6
|
+
var reactDom$1 = require('react-dom');
|
|
7
|
+
var classnames = require('../classnames.js');
|
|
8
|
+
var types = require('../types.js');
|
|
9
|
+
var utils = require('../utils.js');
|
|
10
|
+
|
|
11
|
+
function FloatingThreads({
|
|
12
|
+
threads,
|
|
13
|
+
components,
|
|
14
|
+
editor,
|
|
15
|
+
...props
|
|
16
|
+
}) {
|
|
17
|
+
const Thread = components?.Thread ?? reactUi.Thread;
|
|
18
|
+
const pluginState = editor ? types.THREADS_PLUGIN_KEY.getState(editor.state) : null;
|
|
19
|
+
const [activeThreads, setActiveThreads] = React.useState(null);
|
|
20
|
+
React.useEffect(() => {
|
|
21
|
+
if (!editor || !pluginState) {
|
|
22
|
+
setActiveThreads(null);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const { selectedThreadId, selectedThreadPos } = pluginState;
|
|
26
|
+
if (selectedThreadId === null || selectedThreadPos === null) {
|
|
27
|
+
setActiveThreads(null);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const coords = editor.view.coordsAtPos(Math.min(selectedThreadPos, editor.state.doc.content.size - 1));
|
|
31
|
+
const active = (threads ?? []).filter(
|
|
32
|
+
(thread) => selectedThreadId === thread.id
|
|
33
|
+
);
|
|
34
|
+
setActiveThreads({
|
|
35
|
+
rect: utils.getRectFromCoords(coords),
|
|
36
|
+
threads: active
|
|
37
|
+
});
|
|
38
|
+
}, [editor, pluginState, threads]);
|
|
39
|
+
const handleEscapeKeydown = React.useCallback(() => {
|
|
40
|
+
if (!editor || activeThreads === null)
|
|
41
|
+
return false;
|
|
42
|
+
editor.commands.selectThread(null);
|
|
43
|
+
return true;
|
|
44
|
+
}, [activeThreads, editor]);
|
|
45
|
+
if (!activeThreads)
|
|
46
|
+
return null;
|
|
47
|
+
return /* @__PURE__ */ React.createElement(FloatingThreadPortal, {
|
|
48
|
+
rect: activeThreads.rect,
|
|
49
|
+
container: document.body,
|
|
50
|
+
...props
|
|
51
|
+
}, activeThreads.threads.map((thread) => /* @__PURE__ */ React.createElement(ThreadWrapper, {
|
|
52
|
+
key: thread.id,
|
|
53
|
+
thread,
|
|
54
|
+
Thread,
|
|
55
|
+
onEscapeKeydown: handleEscapeKeydown,
|
|
56
|
+
className: "lb-tiptap-floating-threads-thread"
|
|
57
|
+
})));
|
|
58
|
+
}
|
|
59
|
+
const FLOATING_THREAD_COLLISION_PADDING = 10;
|
|
60
|
+
function FloatingThreadPortal({
|
|
61
|
+
container,
|
|
62
|
+
rect,
|
|
63
|
+
children,
|
|
64
|
+
className,
|
|
65
|
+
style,
|
|
66
|
+
...props
|
|
67
|
+
}) {
|
|
68
|
+
const {
|
|
69
|
+
refs: { setReference, setFloating },
|
|
70
|
+
strategy,
|
|
71
|
+
x,
|
|
72
|
+
y
|
|
73
|
+
} = reactDom.useFloating({
|
|
74
|
+
strategy: "absolute",
|
|
75
|
+
placement: "bottom",
|
|
76
|
+
middleware: [
|
|
77
|
+
reactDom.flip({ padding: FLOATING_THREAD_COLLISION_PADDING, crossAxis: false }),
|
|
78
|
+
reactDom.offset(10),
|
|
79
|
+
reactDom.hide({ padding: FLOATING_THREAD_COLLISION_PADDING }),
|
|
80
|
+
reactDom.shift({
|
|
81
|
+
padding: FLOATING_THREAD_COLLISION_PADDING,
|
|
82
|
+
limiter: reactDom.limitShift()
|
|
83
|
+
}),
|
|
84
|
+
reactDom.size({
|
|
85
|
+
padding: FLOATING_THREAD_COLLISION_PADDING,
|
|
86
|
+
apply({ availableWidth, availableHeight, elements }) {
|
|
87
|
+
elements.floating.style.setProperty(
|
|
88
|
+
"--lb-tiptap-floating-threads-available-width",
|
|
89
|
+
`${availableWidth}px`
|
|
90
|
+
);
|
|
91
|
+
elements.floating.style.setProperty(
|
|
92
|
+
"--lb-tiptap-floating-threads-available-height",
|
|
93
|
+
`${availableHeight}px`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
],
|
|
98
|
+
whileElementsMounted: (...args) => {
|
|
99
|
+
return reactDom.autoUpdate(...args, {
|
|
100
|
+
animationFrame: true
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
React.useLayoutEffect(() => {
|
|
105
|
+
setReference({
|
|
106
|
+
getBoundingClientRect: () => rect
|
|
107
|
+
});
|
|
108
|
+
}, [setReference, rect]);
|
|
109
|
+
return reactDom$1.createPortal(
|
|
110
|
+
/* @__PURE__ */ React.createElement("div", {
|
|
111
|
+
ref: setFloating,
|
|
112
|
+
...props,
|
|
113
|
+
style: {
|
|
114
|
+
...style,
|
|
115
|
+
position: strategy,
|
|
116
|
+
top: 0,
|
|
117
|
+
left: 0,
|
|
118
|
+
transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,
|
|
119
|
+
minWidth: "max-content"
|
|
120
|
+
},
|
|
121
|
+
className: classnames.classNames(
|
|
122
|
+
"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-threads",
|
|
123
|
+
className
|
|
124
|
+
)
|
|
125
|
+
}, children),
|
|
126
|
+
container
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
function ThreadWrapper({
|
|
130
|
+
thread,
|
|
131
|
+
Thread,
|
|
132
|
+
onEscapeKeydown,
|
|
133
|
+
onKeyDown,
|
|
134
|
+
...threadProps
|
|
135
|
+
}) {
|
|
136
|
+
const handleKeyDown = React.useCallback(
|
|
137
|
+
(event) => {
|
|
138
|
+
onKeyDown?.(event);
|
|
139
|
+
if (event.key === "Escape") {
|
|
140
|
+
onEscapeKeydown();
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
[onEscapeKeydown, onKeyDown]
|
|
144
|
+
);
|
|
145
|
+
return /* @__PURE__ */ React.createElement(Thread, {
|
|
146
|
+
thread,
|
|
147
|
+
onKeyDown: handleKeyDown,
|
|
148
|
+
...threadProps
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
exports.FLOATING_THREAD_COLLISION_PADDING = FLOATING_THREAD_COLLISION_PADDING;
|
|
153
|
+
exports.FloatingThreads = FloatingThreads;
|
|
154
|
+
//# sourceMappingURL=FloatingThreads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FloatingThreads.js","sources":["../../src/comments/FloatingThreads.tsx"],"sourcesContent":["import type {\n ClientRectObject\n} from \"@floating-ui/react-dom\";\nimport {\n autoUpdate,\n flip,\n hide,\n limitShift,\n offset,\n shift,\n size,\n useFloating,\n} from \"@floating-ui/react-dom\";\nimport type { BaseMetadata, DM, ThreadData } from \"@liveblocks/core\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport type { Editor } from \"@tiptap/react\";\nimport React, {\n type ComponentType,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n useCallback,\n useEffect,\n useLayoutEffect,\n useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { classNames } from \"../classnames\";\nimport type { ThreadPluginState } from \"../types\";\nimport { THREADS_PLUGIN_KEY } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\ntype ThreadPanelComponents = {\n Thread: ComponentType<ThreadProps>;\n};\n\nexport interface FloatingThreadsProps<M extends BaseMetadata = DM>\n extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /**\n * The threads to display.\n */\n threads: ThreadData<M>[];\n\n /**\n * Override the component's components.\n */\n components?: Partial<ThreadPanelComponents>;\n\n /**\n * The tiptap editor\n */\n editor: Editor | null;\n}\n\nexport function FloatingThreads({\n threads,\n components,\n editor,\n ...props\n}: FloatingThreadsProps) {\n\n const Thread = components?.Thread ?? DefaultThread;\n const pluginState = editor ? THREADS_PLUGIN_KEY.getState(editor.state) as ThreadPluginState : null;\n\n const [activeThreads, setActiveThreads] = useState<{\n rect: ClientRectObject;\n threads: ThreadData[];\n } | null>(null);\n\n\n useEffect(() => {\n if (!editor || !pluginState) {\n setActiveThreads(null);\n return;\n }\n const { selectedThreadId, selectedThreadPos } = pluginState;\n if (selectedThreadId === null || selectedThreadPos === null) {\n setActiveThreads(null);\n return;\n }\n const coords = editor.view.coordsAtPos(Math.min(selectedThreadPos, editor.state.doc.content.size - 1));\n const active = (threads ?? []).filter((thread) =>\n selectedThreadId === thread.id\n );\n setActiveThreads({\n rect: getRectFromCoords(coords), threads: active\n });\n }, [editor, pluginState, threads]);\n\n const handleEscapeKeydown = useCallback((): boolean => {\n if (!editor || activeThreads === null) return false;\n editor.commands.selectThread(null);\n return true;\n }, [activeThreads, editor]);\n\n if (!activeThreads) return null;\n\n return (\n <FloatingThreadPortal\n rect={activeThreads.rect}\n container={document.body}\n {...props}\n >\n {activeThreads.threads.map((thread) => (\n <ThreadWrapper\n key={thread.id}\n thread={thread}\n Thread={Thread}\n onEscapeKeydown={handleEscapeKeydown}\n className=\"lb-tiptap-floating-threads-thread\"\n />\n ))}\n </FloatingThreadPortal>\n );\n}\n\ninterface FloatingThreadPortalProps\n extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n rect: ClientRectObject;\n container: HTMLElement;\n children: ReactNode;\n}\n\nexport const FLOATING_THREAD_COLLISION_PADDING = 10;\n\nfunction FloatingThreadPortal({\n container,\n rect,\n children,\n className,\n style,\n ...props\n}: FloatingThreadPortalProps) {\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"absolute\",\n placement: \"bottom\",\n middleware: [\n flip({ padding: FLOATING_THREAD_COLLISION_PADDING, crossAxis: false }),\n offset(10),\n hide({ padding: FLOATING_THREAD_COLLISION_PADDING }),\n shift({\n padding: FLOATING_THREAD_COLLISION_PADDING,\n limiter: limitShift(),\n }),\n size({\n padding: FLOATING_THREAD_COLLISION_PADDING,\n apply({ availableWidth, availableHeight, elements }) {\n elements.floating.style.setProperty(\n \"--lb-tiptap-floating-threads-available-width\",\n `${availableWidth}px`\n );\n elements.floating.style.setProperty(\n \"--lb-tiptap-floating-threads-available-height\",\n `${availableHeight}px`\n );\n },\n }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n useLayoutEffect(() => {\n setReference({\n getBoundingClientRect: () => rect,\n });\n }, [setReference, rect]);\n\n return createPortal(\n <div\n ref={setFloating}\n {...props}\n style={{\n ...style,\n position: strategy,\n top: 0,\n left: 0,\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n minWidth: \"max-content\",\n }}\n className={classNames(\n \"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-threads\",\n className\n )}\n >\n {children}\n </div>,\n container\n );\n}\n\ninterface ThreadWrapperProps extends ThreadProps {\n thread: ThreadData;\n Thread: ComponentType<ThreadProps>;\n onEscapeKeydown: () => void;\n}\n\nfunction ThreadWrapper({\n thread,\n Thread,\n onEscapeKeydown,\n onKeyDown,\n ...threadProps\n}: ThreadWrapperProps) {\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n // TODO: Add ability to preventDefault on keydown to override the default behavior, e.g. to show an alert dialog\n if (event.key === \"Escape\") {\n onEscapeKeydown();\n }\n },\n [onEscapeKeydown, onKeyDown]\n );\n\n return <Thread thread={thread} onKeyDown={handleKeyDown} {...threadProps} />;\n}\n\n"],"names":["DefaultThread","THREADS_PLUGIN_KEY","useState","useEffect","getRectFromCoords","useCallback","useFloating","flip","offset","hide","shift","limitShift","size","autoUpdate","useLayoutEffect","createPortal","classNames"],"mappings":"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"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { useFloating, flip, offset, hide, shift, limitShift, size, autoUpdate } from '@floating-ui/react-dom';
|
|
2
|
+
import { Thread } from '@liveblocks/react-ui';
|
|
3
|
+
import React, { useState, useEffect, useCallback, useLayoutEffect } from 'react';
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
5
|
+
import { classNames } from '../classnames.mjs';
|
|
6
|
+
import { THREADS_PLUGIN_KEY } from '../types.mjs';
|
|
7
|
+
import { getRectFromCoords } from '../utils.mjs';
|
|
8
|
+
|
|
9
|
+
function FloatingThreads({
|
|
10
|
+
threads,
|
|
11
|
+
components,
|
|
12
|
+
editor,
|
|
13
|
+
...props
|
|
14
|
+
}) {
|
|
15
|
+
const Thread$1 = components?.Thread ?? Thread;
|
|
16
|
+
const pluginState = editor ? THREADS_PLUGIN_KEY.getState(editor.state) : null;
|
|
17
|
+
const [activeThreads, setActiveThreads] = useState(null);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!editor || !pluginState) {
|
|
20
|
+
setActiveThreads(null);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const { selectedThreadId, selectedThreadPos } = pluginState;
|
|
24
|
+
if (selectedThreadId === null || selectedThreadPos === null) {
|
|
25
|
+
setActiveThreads(null);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const coords = editor.view.coordsAtPos(Math.min(selectedThreadPos, editor.state.doc.content.size - 1));
|
|
29
|
+
const active = (threads ?? []).filter(
|
|
30
|
+
(thread) => selectedThreadId === thread.id
|
|
31
|
+
);
|
|
32
|
+
setActiveThreads({
|
|
33
|
+
rect: getRectFromCoords(coords),
|
|
34
|
+
threads: active
|
|
35
|
+
});
|
|
36
|
+
}, [editor, pluginState, threads]);
|
|
37
|
+
const handleEscapeKeydown = useCallback(() => {
|
|
38
|
+
if (!editor || activeThreads === null)
|
|
39
|
+
return false;
|
|
40
|
+
editor.commands.selectThread(null);
|
|
41
|
+
return true;
|
|
42
|
+
}, [activeThreads, editor]);
|
|
43
|
+
if (!activeThreads)
|
|
44
|
+
return null;
|
|
45
|
+
return /* @__PURE__ */ React.createElement(FloatingThreadPortal, {
|
|
46
|
+
rect: activeThreads.rect,
|
|
47
|
+
container: document.body,
|
|
48
|
+
...props
|
|
49
|
+
}, activeThreads.threads.map((thread) => /* @__PURE__ */ React.createElement(ThreadWrapper, {
|
|
50
|
+
key: thread.id,
|
|
51
|
+
thread,
|
|
52
|
+
Thread: Thread$1,
|
|
53
|
+
onEscapeKeydown: handleEscapeKeydown,
|
|
54
|
+
className: "lb-tiptap-floating-threads-thread"
|
|
55
|
+
})));
|
|
56
|
+
}
|
|
57
|
+
const FLOATING_THREAD_COLLISION_PADDING = 10;
|
|
58
|
+
function FloatingThreadPortal({
|
|
59
|
+
container,
|
|
60
|
+
rect,
|
|
61
|
+
children,
|
|
62
|
+
className,
|
|
63
|
+
style,
|
|
64
|
+
...props
|
|
65
|
+
}) {
|
|
66
|
+
const {
|
|
67
|
+
refs: { setReference, setFloating },
|
|
68
|
+
strategy,
|
|
69
|
+
x,
|
|
70
|
+
y
|
|
71
|
+
} = useFloating({
|
|
72
|
+
strategy: "absolute",
|
|
73
|
+
placement: "bottom",
|
|
74
|
+
middleware: [
|
|
75
|
+
flip({ padding: FLOATING_THREAD_COLLISION_PADDING, crossAxis: false }),
|
|
76
|
+
offset(10),
|
|
77
|
+
hide({ padding: FLOATING_THREAD_COLLISION_PADDING }),
|
|
78
|
+
shift({
|
|
79
|
+
padding: FLOATING_THREAD_COLLISION_PADDING,
|
|
80
|
+
limiter: limitShift()
|
|
81
|
+
}),
|
|
82
|
+
size({
|
|
83
|
+
padding: FLOATING_THREAD_COLLISION_PADDING,
|
|
84
|
+
apply({ availableWidth, availableHeight, elements }) {
|
|
85
|
+
elements.floating.style.setProperty(
|
|
86
|
+
"--lb-tiptap-floating-threads-available-width",
|
|
87
|
+
`${availableWidth}px`
|
|
88
|
+
);
|
|
89
|
+
elements.floating.style.setProperty(
|
|
90
|
+
"--lb-tiptap-floating-threads-available-height",
|
|
91
|
+
`${availableHeight}px`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
],
|
|
96
|
+
whileElementsMounted: (...args) => {
|
|
97
|
+
return autoUpdate(...args, {
|
|
98
|
+
animationFrame: true
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
useLayoutEffect(() => {
|
|
103
|
+
setReference({
|
|
104
|
+
getBoundingClientRect: () => rect
|
|
105
|
+
});
|
|
106
|
+
}, [setReference, rect]);
|
|
107
|
+
return createPortal(
|
|
108
|
+
/* @__PURE__ */ React.createElement("div", {
|
|
109
|
+
ref: setFloating,
|
|
110
|
+
...props,
|
|
111
|
+
style: {
|
|
112
|
+
...style,
|
|
113
|
+
position: strategy,
|
|
114
|
+
top: 0,
|
|
115
|
+
left: 0,
|
|
116
|
+
transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,
|
|
117
|
+
minWidth: "max-content"
|
|
118
|
+
},
|
|
119
|
+
className: classNames(
|
|
120
|
+
"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-threads",
|
|
121
|
+
className
|
|
122
|
+
)
|
|
123
|
+
}, children),
|
|
124
|
+
container
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
function ThreadWrapper({
|
|
128
|
+
thread,
|
|
129
|
+
Thread,
|
|
130
|
+
onEscapeKeydown,
|
|
131
|
+
onKeyDown,
|
|
132
|
+
...threadProps
|
|
133
|
+
}) {
|
|
134
|
+
const handleKeyDown = useCallback(
|
|
135
|
+
(event) => {
|
|
136
|
+
onKeyDown?.(event);
|
|
137
|
+
if (event.key === "Escape") {
|
|
138
|
+
onEscapeKeydown();
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
[onEscapeKeydown, onKeyDown]
|
|
142
|
+
);
|
|
143
|
+
return /* @__PURE__ */ React.createElement(Thread, {
|
|
144
|
+
thread,
|
|
145
|
+
onKeyDown: handleKeyDown,
|
|
146
|
+
...threadProps
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export { FLOATING_THREAD_COLLISION_PADDING, FloatingThreads };
|
|
151
|
+
//# sourceMappingURL=FloatingThreads.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FloatingThreads.mjs","sources":["../../src/comments/FloatingThreads.tsx"],"sourcesContent":["import type {\n ClientRectObject\n} from \"@floating-ui/react-dom\";\nimport {\n autoUpdate,\n flip,\n hide,\n limitShift,\n offset,\n shift,\n size,\n useFloating,\n} from \"@floating-ui/react-dom\";\nimport type { BaseMetadata, DM, ThreadData } from \"@liveblocks/core\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport type { Editor } from \"@tiptap/react\";\nimport React, {\n type ComponentType,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n useCallback,\n useEffect,\n useLayoutEffect,\n useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { classNames } from \"../classnames\";\nimport type { ThreadPluginState } from \"../types\";\nimport { THREADS_PLUGIN_KEY } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\ntype ThreadPanelComponents = {\n Thread: ComponentType<ThreadProps>;\n};\n\nexport interface FloatingThreadsProps<M extends BaseMetadata = DM>\n extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /**\n * The threads to display.\n */\n threads: ThreadData<M>[];\n\n /**\n * Override the component's components.\n */\n components?: Partial<ThreadPanelComponents>;\n\n /**\n * The tiptap editor\n */\n editor: Editor | null;\n}\n\nexport function FloatingThreads({\n threads,\n components,\n editor,\n ...props\n}: FloatingThreadsProps) {\n\n const Thread = components?.Thread ?? DefaultThread;\n const pluginState = editor ? THREADS_PLUGIN_KEY.getState(editor.state) as ThreadPluginState : null;\n\n const [activeThreads, setActiveThreads] = useState<{\n rect: ClientRectObject;\n threads: ThreadData[];\n } | null>(null);\n\n\n useEffect(() => {\n if (!editor || !pluginState) {\n setActiveThreads(null);\n return;\n }\n const { selectedThreadId, selectedThreadPos } = pluginState;\n if (selectedThreadId === null || selectedThreadPos === null) {\n setActiveThreads(null);\n return;\n }\n const coords = editor.view.coordsAtPos(Math.min(selectedThreadPos, editor.state.doc.content.size - 1));\n const active = (threads ?? []).filter((thread) =>\n selectedThreadId === thread.id\n );\n setActiveThreads({\n rect: getRectFromCoords(coords), threads: active\n });\n }, [editor, pluginState, threads]);\n\n const handleEscapeKeydown = useCallback((): boolean => {\n if (!editor || activeThreads === null) return false;\n editor.commands.selectThread(null);\n return true;\n }, [activeThreads, editor]);\n\n if (!activeThreads) return null;\n\n return (\n <FloatingThreadPortal\n rect={activeThreads.rect}\n container={document.body}\n {...props}\n >\n {activeThreads.threads.map((thread) => (\n <ThreadWrapper\n key={thread.id}\n thread={thread}\n Thread={Thread}\n onEscapeKeydown={handleEscapeKeydown}\n className=\"lb-tiptap-floating-threads-thread\"\n />\n ))}\n </FloatingThreadPortal>\n );\n}\n\ninterface FloatingThreadPortalProps\n extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n rect: ClientRectObject;\n container: HTMLElement;\n children: ReactNode;\n}\n\nexport const FLOATING_THREAD_COLLISION_PADDING = 10;\n\nfunction FloatingThreadPortal({\n container,\n rect,\n children,\n className,\n style,\n ...props\n}: FloatingThreadPortalProps) {\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"absolute\",\n placement: \"bottom\",\n middleware: [\n flip({ padding: FLOATING_THREAD_COLLISION_PADDING, crossAxis: false }),\n offset(10),\n hide({ padding: FLOATING_THREAD_COLLISION_PADDING }),\n shift({\n padding: FLOATING_THREAD_COLLISION_PADDING,\n limiter: limitShift(),\n }),\n size({\n padding: FLOATING_THREAD_COLLISION_PADDING,\n apply({ availableWidth, availableHeight, elements }) {\n elements.floating.style.setProperty(\n \"--lb-tiptap-floating-threads-available-width\",\n `${availableWidth}px`\n );\n elements.floating.style.setProperty(\n \"--lb-tiptap-floating-threads-available-height\",\n `${availableHeight}px`\n );\n },\n }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n useLayoutEffect(() => {\n setReference({\n getBoundingClientRect: () => rect,\n });\n }, [setReference, rect]);\n\n return createPortal(\n <div\n ref={setFloating}\n {...props}\n style={{\n ...style,\n position: strategy,\n top: 0,\n left: 0,\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n minWidth: \"max-content\",\n }}\n className={classNames(\n \"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-threads\",\n className\n )}\n >\n {children}\n </div>,\n container\n );\n}\n\ninterface ThreadWrapperProps extends ThreadProps {\n thread: ThreadData;\n Thread: ComponentType<ThreadProps>;\n onEscapeKeydown: () => void;\n}\n\nfunction ThreadWrapper({\n thread,\n Thread,\n onEscapeKeydown,\n onKeyDown,\n ...threadProps\n}: ThreadWrapperProps) {\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n // TODO: Add ability to preventDefault on keydown to override the default behavior, e.g. to show an alert dialog\n if (event.key === \"Escape\") {\n onEscapeKeydown();\n }\n },\n [onEscapeKeydown, onKeyDown]\n );\n\n return <Thread thread={thread} onKeyDown={handleKeyDown} {...threadProps} />;\n}\n\n"],"names":["Thread","DefaultThread"],"mappings":";;;;;;;;AA0DO,SAAS,eAAgB,CAAA;AAAA,EAC9B,OAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AAAA,EACG,GAAA,KAAA;AACL,CAAyB,EAAA;AAEvB,EAAM,MAAAA,QAAA,GAAS,YAAY,MAAU,IAAAC,MAAA,CAAA;AACrC,EAAA,MAAM,cAAc,MAAS,GAAA,kBAAA,CAAmB,QAAS,CAAA,MAAA,CAAO,KAAK,CAAyB,GAAA,IAAA,CAAA;AAE9F,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAGhC,IAAI,CAAA,CAAA;AAGd,EAAA,SAAA,CAAU,MAAM;AACd,IAAI,IAAA,CAAC,MAAU,IAAA,CAAC,WAAa,EAAA;AAC3B,MAAA,gBAAA,CAAiB,IAAI,CAAA,CAAA;AACrB,MAAA,OAAA;AAAA,KACF;AACA,IAAM,MAAA,EAAE,gBAAkB,EAAA,iBAAA,EAAsB,GAAA,WAAA,CAAA;AAChD,IAAI,IAAA,gBAAA,KAAqB,IAAQ,IAAA,iBAAA,KAAsB,IAAM,EAAA;AAC3D,MAAA,gBAAA,CAAiB,IAAI,CAAA,CAAA;AACrB,MAAA,OAAA;AAAA,KACF;AACA,IAAA,MAAM,MAAS,GAAA,MAAA,CAAO,IAAK,CAAA,WAAA,CAAY,IAAK,CAAA,GAAA,CAAI,iBAAmB,EAAA,MAAA,CAAO,KAAM,CAAA,GAAA,CAAI,OAAQ,CAAA,IAAA,GAAO,CAAC,CAAC,CAAA,CAAA;AACrG,IAAM,MAAA,MAAA,GAAA,CAAU,OAAW,IAAA,EAAI,EAAA,MAAA;AAAA,MAAO,CAAC,MACrC,KAAA,gBAAA,KAAqB,MAAO,CAAA,EAAA;AAAA,KAC9B,CAAA;AACA,IAAiB,gBAAA,CAAA;AAAA,MACf,IAAA,EAAM,kBAAkB,MAAM,CAAA;AAAA,MAAG,OAAS,EAAA,MAAA;AAAA,KAC3C,CAAA,CAAA;AAAA,GACA,EAAA,CAAC,MAAQ,EAAA,WAAA,EAAa,OAAO,CAAC,CAAA,CAAA;AAEjC,EAAM,MAAA,mBAAA,GAAsB,YAAY,MAAe;AACrD,IAAI,IAAA,CAAC,UAAU,aAAkB,KAAA,IAAA;AAAM,MAAO,OAAA,KAAA,CAAA;AAC9C,IAAO,MAAA,CAAA,QAAA,CAAS,aAAa,IAAI,CAAA,CAAA;AACjC,IAAO,OAAA,IAAA,CAAA;AAAA,GACN,EAAA,CAAC,aAAe,EAAA,MAAM,CAAC,CAAA,CAAA;AAE1B,EAAA,IAAI,CAAC,aAAA;AAAe,IAAO,OAAA,IAAA,CAAA;AAE3B,EAAA,uBACG,KAAA,CAAA,aAAA,CAAA,oBAAA,EAAA;AAAA,IACC,MAAM,aAAc,CAAA,IAAA;AAAA,IACpB,WAAW,QAAS,CAAA,IAAA;AAAA,IACnB,GAAG,KAAA;AAAA,GAAA,EAEH,aAAc,CAAA,OAAA,CAAQ,GAAI,CAAA,CAAC,2BACzB,KAAA,CAAA,aAAA,CAAA,aAAA,EAAA;AAAA,IACC,KAAK,MAAO,CAAA,EAAA;AAAA,IACZ,MAAA;AAAA,YACAD,QAAA;AAAA,IACA,eAAiB,EAAA,mBAAA;AAAA,IACjB,SAAU,EAAA,mCAAA;AAAA,GACZ,CACD,CACH,CAAA,CAAA;AAEJ,CAAA;AASO,MAAM,iCAAoC,GAAA,GAAA;AAEjD,SAAS,oBAAqB,CAAA;AAAA,EAC5B,SAAA;AAAA,EACA,IAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACG,GAAA,KAAA;AACL,CAA8B,EAAA;AAC5B,EAAM,MAAA;AAAA,IACJ,IAAA,EAAM,EAAE,YAAA,EAAc,WAAY,EAAA;AAAA,IAClC,QAAA;AAAA,IACA,CAAA;AAAA,IACA,CAAA;AAAA,MACE,WAAY,CAAA;AAAA,IACd,QAAU,EAAA,UAAA;AAAA,IACV,SAAW,EAAA,QAAA;AAAA,IACX,UAAY,EAAA;AAAA,MACV,KAAK,EAAE,OAAA,EAAS,iCAAmC,EAAA,SAAA,EAAW,OAAO,CAAA;AAAA,MACrE,OAAO,EAAE,CAAA;AAAA,MACT,IAAK,CAAA,EAAE,OAAS,EAAA,iCAAA,EAAmC,CAAA;AAAA,MACnD,KAAM,CAAA;AAAA,QACJ,OAAS,EAAA,iCAAA;AAAA,QACT,SAAS,UAAW,EAAA;AAAA,OACrB,CAAA;AAAA,MACD,IAAK,CAAA;AAAA,QACH,OAAS,EAAA,iCAAA;AAAA,QACT,KAAM,CAAA,EAAE,cAAgB,EAAA,eAAA,EAAiB,UAAY,EAAA;AACnD,UAAA,QAAA,CAAS,SAAS,KAAM,CAAA,WAAA;AAAA,YACtB,8CAAA;AAAA,YACA,CAAG,EAAA,cAAA,CAAA,EAAA,CAAA;AAAA,WACL,CAAA;AACA,UAAA,QAAA,CAAS,SAAS,KAAM,CAAA,WAAA;AAAA,YACtB,+CAAA;AAAA,YACA,CAAG,EAAA,eAAA,CAAA,EAAA,CAAA;AAAA,WACL,CAAA;AAAA,SACF;AAAA,OACD,CAAA;AAAA,KACH;AAAA,IACA,oBAAA,EAAsB,IAAI,IAAS,KAAA;AACjC,MAAO,OAAA,UAAA,CAAW,GAAG,IAAM,EAAA;AAAA,QACzB,cAAgB,EAAA,IAAA;AAAA,OACjB,CAAA,CAAA;AAAA,KACH;AAAA,GACD,CAAA,CAAA;AAED,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAa,YAAA,CAAA;AAAA,MACX,uBAAuB,MAAM,IAAA;AAAA,KAC9B,CAAA,CAAA;AAAA,GACA,EAAA,CAAC,YAAc,EAAA,IAAI,CAAC,CAAA,CAAA;AAEvB,EAAO,OAAA,YAAA;AAAA,oBACJ,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA;AAAA,MACC,GAAK,EAAA,WAAA;AAAA,MACJ,GAAG,KAAA;AAAA,MACJ,KAAO,EAAA;AAAA,QACL,GAAG,KAAA;AAAA,QACH,QAAU,EAAA,QAAA;AAAA,QACV,GAAK,EAAA,CAAA;AAAA,QACL,IAAM,EAAA,CAAA;AAAA,QACN,SAAA,EAAW,eAAe,IAAK,CAAA,KAAA,CAAM,CAAC,CAAQ,CAAA,IAAA,EAAA,IAAA,CAAK,MAAM,CAAC,CAAA,CAAA,MAAA,CAAA;AAAA,QAC1D,QAAU,EAAA,aAAA;AAAA,OACZ;AAAA,MACA,SAAW,EAAA,UAAA;AAAA,QACT,8EAAA;AAAA,QACA,SAAA;AAAA,OACF;AAAA,KAAA,EAEC,QACH,CAAA;AAAA,IACA,SAAA;AAAA,GACF,CAAA;AACF,CAAA;AAQA,SAAS,aAAc,CAAA;AAAA,EACrB,MAAA;AAAA,EACA,MAAA;AAAA,EACA,eAAA;AAAA,EACA,SAAA;AAAA,EACG,GAAA,WAAA;AACL,CAAuB,EAAA;AACrB,EAAA,MAAM,aAAgB,GAAA,WAAA;AAAA,IACpB,CAAC,KAAyC,KAAA;AACxC,MAAA,SAAA,GAAY,KAAK,CAAA,CAAA;AAGjB,MAAI,IAAA,KAAA,CAAM,QAAQ,QAAU,EAAA;AAC1B,QAAgB,eAAA,EAAA,CAAA;AAAA,OAClB;AAAA,KACF;AAAA,IACA,CAAC,iBAAiB,SAAS,CAAA;AAAA,GAC7B,CAAA;AAEA,EAAA,uBAAQ,KAAA,CAAA,aAAA,CAAA,MAAA,EAAA;AAAA,IAAO,MAAA;AAAA,IAAgB,SAAW,EAAA,aAAA;AAAA,IAAgB,GAAG,WAAA;AAAA,GAAa,CAAA,CAAA;AAC5E;;;;"}
|