@estiva-app/ui 0.54.0 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BlockHandle.d.ts +47 -0
- package/dist/BlockHandle.d.ts.map +1 -0
- package/dist/EditorBlockHandle.d.ts +57 -0
- package/dist/EditorBlockHandle.d.ts.map +1 -0
- package/dist/editor.d.ts +1 -0
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +127 -11
- package/dist/editor.js.map +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +716 -573
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/registry.json +211 -3
- package/src/BlockHandle.mdx +58 -0
- package/src/BlockHandle.stories.tsx +44 -0
- package/src/BlockHandle.test.tsx +216 -0
- package/src/BlockHandle.tsx +220 -0
- package/src/EditorBlockHandle.mdx +65 -0
- package/src/EditorBlockHandle.stories.tsx +81 -0
- package/src/EditorBlockHandle.tsx +181 -0
- package/src/editor.ts +1 -0
- package/src/index.ts +1 -0
- package/src/registry/registry.test.ts +5 -5
- package/stories/Choosing.mdx +2 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
/** The block the pointer is on: its §13.3 id, its element, and — in an editor — where it starts. */
|
|
3
|
+
export interface HandleBlock {
|
|
4
|
+
/** `undefined` only for a block that has none yet, which nothing can address. */
|
|
5
|
+
id: string | undefined;
|
|
6
|
+
element: HTMLElement;
|
|
7
|
+
/** Position before the block in the editor's document; absent when reading. */
|
|
8
|
+
pos?: number;
|
|
9
|
+
}
|
|
10
|
+
/** What `EditorBlockHandle` hands this part to make it edit. A reading surface leaves it off. */
|
|
11
|
+
export interface BlockHandleEditing {
|
|
12
|
+
/** The block level with `y`, in the editor's own document. */
|
|
13
|
+
find: (y: number) => HandleBlock | null;
|
|
14
|
+
/** The editing rows, before the caller's. */
|
|
15
|
+
rows: (block: HandleBlock) => ReactNode;
|
|
16
|
+
/** Starts the browser's drag of the block; the handle is draggable only with this. */
|
|
17
|
+
startDrag: (block: HandleBlock, event: globalThis.DragEvent) => void;
|
|
18
|
+
/** Drops the dragged block before `target`, or after it: a drop in the gutter, where the editor has none. */
|
|
19
|
+
drop: (target: HandleBlock, after: boolean) => void;
|
|
20
|
+
endDrag: () => void;
|
|
21
|
+
/** Calls back when the document changes, so a block found before is found again. Returns the unsubscribe. */
|
|
22
|
+
onChange: (changed: () => void) => () => void;
|
|
23
|
+
}
|
|
24
|
+
export interface BlockHandleProps {
|
|
25
|
+
/** The caller's own rows — a link to the block, a comment on it. Reading, they are the whole menu; with none, no handle is drawn. */
|
|
26
|
+
actions?: (block: HandleBlock) => ReactNode;
|
|
27
|
+
/** Given by `EditorBlockHandle` (from `@estiva-app/ui/editor`). Leave it off to read. */
|
|
28
|
+
editing?: BlockHandleEditing;
|
|
29
|
+
/** What the blocks are drawn in. Reading: a surface whose top-level elements carry `data-block-id`, as `RichText` draws. */
|
|
30
|
+
children: ReactNode;
|
|
31
|
+
className?: string;
|
|
32
|
+
}
|
|
33
|
+
/** The last of `blocks` whose top is at or above `y` — the gap between two blocks belongs to the one before. */
|
|
34
|
+
export declare function blockAtY(blocks: readonly HandleBlock[], y: number): HandleBlock | null;
|
|
35
|
+
/**
|
|
36
|
+
* The handle at the left of the block under the pointer, and the block's menu
|
|
37
|
+
* behind it. Reading, the menu is the caller's rows only, so someone who
|
|
38
|
+
* cannot edit can still point at one block — copy a link to it, comment on
|
|
39
|
+
* it. In an editor, `EditorBlockHandle` adds the drag, Turn into, Duplicate
|
|
40
|
+
* and Delete.
|
|
41
|
+
*
|
|
42
|
+
* It sits in the gutter left of the content, 28px out, so wrapping the content
|
|
43
|
+
* moves nothing. The gutter counts as the block's, as in Notion: the handle
|
|
44
|
+
* stays while the pointer crosses to it.
|
|
45
|
+
*/
|
|
46
|
+
export declare function BlockHandle({ actions, editing, children, className }: BlockHandleProps): import("react").JSX.Element;
|
|
47
|
+
//# sourceMappingURL=BlockHandle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlockHandle.d.ts","sourceRoot":"","sources":["../src/BlockHandle.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6E,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAOjH,oGAAoG;AACpG,MAAM,WAAW,WAAW;IAC1B,iFAAiF;IACjF,EAAE,EAAE,MAAM,GAAG,SAAS,CAAA;IACtB,OAAO,EAAE,WAAW,CAAA;IACpB,+EAA+E;IAC/E,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,iGAAiG;AACjG,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,WAAW,GAAG,IAAI,CAAA;IACvC,6CAA6C;IAC7C,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,SAAS,CAAA;IACvC,sFAAsF;IACtF,SAAS,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,SAAS,KAAK,IAAI,CAAA;IACpE,6GAA6G;IAC7G,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACnD,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,6GAA6G;IAC7G,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAA;CAC9C;AAED,MAAM,WAAW,gBAAgB;IAC/B,qIAAqI;IACrI,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,SAAS,CAAA;IAC3C,yFAAyF;IACzF,OAAO,CAAC,EAAE,kBAAkB,CAAA;IAC5B,4HAA4H;IAC5H,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAQD,gHAAgH;AAChH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAOtF;AA2BD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,gBAAgB,+BA8HtF"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { Editor } from '@tiptap/core';
|
|
3
|
+
import type { Node as ProseMirrorNode } from '@tiptap/pm/model';
|
|
4
|
+
import { type HandleBlock } from './BlockHandle';
|
|
5
|
+
/** A row of the block menu's Turn into: what the block becomes. */
|
|
6
|
+
export interface BlockTurnInto {
|
|
7
|
+
label: string;
|
|
8
|
+
/** Turns the block the caret is in. The handle puts the caret there first. */
|
|
9
|
+
run: (editor: Editor) => void;
|
|
10
|
+
/** Whether the block already is this — drawn as the selected row. */
|
|
11
|
+
active?: (node: ProseMirrorNode) => boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface EditorBlockHandleProps {
|
|
14
|
+
/** The editor. Not editable, or not there yet, and the handle reads: the caller's rows only. */
|
|
15
|
+
editor: Editor | null;
|
|
16
|
+
/** Turn into's rows. Offered for a block of text — a paragraph, a heading, code. */
|
|
17
|
+
turnInto?: readonly BlockTurnInto[];
|
|
18
|
+
/** The caller's own rows, after the editing ones. */
|
|
19
|
+
actions?: (block: HandleBlock) => ReactNode;
|
|
20
|
+
/** The `EditorContent`. */
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
className?: string;
|
|
23
|
+
}
|
|
24
|
+
/** A copy of the block straight after it, the caret in the copy. `BlockId` gives the copy a fresh id; the original keeps its own. */
|
|
25
|
+
export declare function duplicateBlock(editor: Editor, pos: number): boolean;
|
|
26
|
+
/** Removes the block, the caret where it was. The last block of a document becomes an empty paragraph, since a document holds at least one. */
|
|
27
|
+
export declare function deleteBlock(editor: Editor, pos: number): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Moves the block at `from` to `to` (a position between top-level blocks), the
|
|
30
|
+
* caret in it: what a drop in the gutter does, where the editor has no drop of
|
|
31
|
+
* its own. The node moves whole, attributes and all, so it keeps its id, as
|
|
32
|
+
* ProseMirror's drop keeps it.
|
|
33
|
+
*/
|
|
34
|
+
export declare function moveBlock(editor: Editor, from: number, to: number): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Starts the browser's own drag of the block, as ProseMirror starts one of a
|
|
37
|
+
* selected node: the block selected, its slice on `view.dragging` with `move`,
|
|
38
|
+
* and its HTML on the transfer. ProseMirror's drop then deletes the selection
|
|
39
|
+
* and inserts the slice with its attributes — so the block arrives with the
|
|
40
|
+
* id it left with, and every comment anchored to it still finds it.
|
|
41
|
+
*/
|
|
42
|
+
export declare function startBlockDrag(editor: Editor, pos: number, event: Pick<globalThis.DragEvent, 'dataTransfer'>, image?: Element): void;
|
|
43
|
+
/**
|
|
44
|
+
* The drop line a dragged block shows, in the package's accent instead of the
|
|
45
|
+
* editor's default (the text's colour). Give it to the editor's StarterKit:
|
|
46
|
+
* `StarterKit.configure({ dropcursor: blockDropCursor })`.
|
|
47
|
+
*/
|
|
48
|
+
export declare const blockDropCursor: {
|
|
49
|
+
readonly color: false;
|
|
50
|
+
readonly class: "bg-accent-primary";
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* `BlockHandle` in an editor: drag the handle to move the block, and the menu
|
|
54
|
+
* gains Turn into, Duplicate and Delete before the caller's rows.
|
|
55
|
+
*/
|
|
56
|
+
export declare function EditorBlockHandle({ editor, turnInto, actions, children, className }: EditorBlockHandleProps): import("react").JSX.Element;
|
|
57
|
+
//# sourceMappingURL=EditorBlockHandle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EditorBlockHandle.d.ts","sourceRoot":"","sources":["../src/EditorBlockHandle.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,IAAI,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAG/D,OAAO,EAAkD,KAAK,WAAW,EAAE,MAAM,eAAe,CAAA;AAGhG,mEAAmE;AACnE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,8EAA8E;IAC9E,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,qEAAqE;IACrE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,OAAO,CAAA;CAC5C;AAED,MAAM,WAAW,sBAAsB;IACrC,gGAAgG;IAChG,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,SAAS,aAAa,EAAE,CAAA;IACnC,qDAAqD;IACrD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,SAAS,CAAA;IAC3C,2BAA2B;IAC3B,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAmBD,qIAAqI;AACrI,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAMnE;AAED,+IAA+I;AAC/I,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAQhE;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAQ3E;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,cAAc,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,QAc7H;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe;;;CAAwD,CAAA;AAIpF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,QAAa,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,sBAAsB,+BA8DhH"}
|
package/dist/editor.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export { KeptSelection, SelectionToolbar, normalizeHref, type MarkId, type SelectionToolbarProps } from './SelectionToolbar';
|
|
11
11
|
export { suggestionPopup, isSuggestionActive, isSuggestionOpen, type SuggestionPopupOptions } from './suggestionPopup';
|
|
12
|
+
export { EditorBlockHandle, blockDropCursor, deleteBlock, moveBlock, duplicateBlock, startBlockDrag, type EditorBlockHandleProps, type BlockTurnInto } from './EditorBlockHandle';
|
|
12
13
|
export { PersonMention, UrgentPersonMention, CaptionedReference, ReferenceTrigger, SlashCommands, peopleMenu, referenceMenu, filterPeople, urgentPubkeys, filterReferences, formatSection, applyFormat, typeTrigger, filterSlashSections, FORMATS, type MentionPerson, type PersonMentionOptions, type CaptionedItem, type CaptionedReferenceOptions, type ReferenceTriggerOptions, type ReferenceMenuOptions, type SlashCommand, type SlashSection, type SlashCommandsOptions, type FormatId, type FormatDef, type CodeFormat, } from './ComposerTriggers';
|
|
13
14
|
export { BlockId, UnknownBlock, ReferenceNode, AttachmentNode, type ReferenceNodeOptions, type ReferenceViewProps, type AttachmentNodeOptions, type AttachmentViewProps, } from './BlockNodes';
|
|
14
15
|
//# sourceMappingURL=editor.d.ts.map
|
package/dist/editor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,aAAa,EAAE,KAAK,MAAM,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC5H,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,KAAK,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AACtH,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,OAAO,EACP,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,UAAU,GAChB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,OAAO,EACP,YAAY,EACZ,aAAa,EACb,cAAc,EACd,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,GACzB,MAAM,cAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,aAAa,EAAE,KAAK,MAAM,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC5H,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,KAAK,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AACtH,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,sBAAsB,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACjL,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,OAAO,EACP,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,UAAU,GAChB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,OAAO,EACP,YAAY,EACZ,aAAa,EACb,cAAc,EACd,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,GACzB,MAAM,cAAc,CAAA"}
|
package/dist/editor.js
CHANGED
|
@@ -364,6 +364,116 @@ function suggestionPopup(options) {
|
|
|
364
364
|
};
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
+
// src/EditorBlockHandle.tsx
|
|
368
|
+
import { useMemo } from "react";
|
|
369
|
+
import { IconCopy, IconTrash } from "@tabler/icons-react";
|
|
370
|
+
import { BlockHandle, blockAtY } from "@estiva-app/ui";
|
|
371
|
+
import { MenuItem, MenuSub } from "@estiva-app/ui";
|
|
372
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
373
|
+
function editorBlocks(view) {
|
|
374
|
+
const out = [];
|
|
375
|
+
view.state.doc.forEach((node, pos) => {
|
|
376
|
+
const element = view.nodeDOM(pos);
|
|
377
|
+
if (element instanceof HTMLElement) out.push({ id: node.attrs.blockId || void 0, element, pos });
|
|
378
|
+
});
|
|
379
|
+
return out;
|
|
380
|
+
}
|
|
381
|
+
function duplicateBlock(editor, pos) {
|
|
382
|
+
const node = editor.state.doc.nodeAt(pos);
|
|
383
|
+
if (!node) return false;
|
|
384
|
+
editor.view.dispatch(editor.state.tr.insert(pos + node.nodeSize, node).scrollIntoView());
|
|
385
|
+
editor.commands.focus(pos + node.nodeSize + 1);
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
function deleteBlock(editor, pos) {
|
|
389
|
+
const { state } = editor;
|
|
390
|
+
const node = state.doc.nodeAt(pos);
|
|
391
|
+
if (!node) return false;
|
|
392
|
+
const tr = state.doc.childCount === 1 ? state.tr.replaceWith(pos, pos + node.nodeSize, state.schema.nodes.paragraph.create()) : state.tr.delete(pos, pos + node.nodeSize);
|
|
393
|
+
editor.view.dispatch(tr);
|
|
394
|
+
editor.commands.focus(Math.min(pos + 1, editor.state.doc.content.size));
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
function moveBlock(editor, from, to) {
|
|
398
|
+
const node = editor.state.doc.nodeAt(from);
|
|
399
|
+
if (!node || to >= from && to <= from + node.nodeSize) return false;
|
|
400
|
+
const tr = editor.state.tr.delete(from, from + node.nodeSize);
|
|
401
|
+
const at = tr.mapping.map(to);
|
|
402
|
+
editor.view.dispatch(tr.insert(at, node).scrollIntoView());
|
|
403
|
+
editor.commands.focus(at + 1);
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
function startBlockDrag(editor, pos, event, image) {
|
|
407
|
+
editor.commands.setNodeSelection(pos);
|
|
408
|
+
const { view } = editor;
|
|
409
|
+
const slice = view.state.selection.content();
|
|
410
|
+
const { dom, text } = view.serializeForClipboard(slice);
|
|
411
|
+
const transfer = event.dataTransfer;
|
|
412
|
+
if (transfer) {
|
|
413
|
+
transfer.clearData();
|
|
414
|
+
transfer.setData("text/html", dom.innerHTML);
|
|
415
|
+
transfer.setData("text/plain", text);
|
|
416
|
+
transfer.effectAllowed = "copyMove";
|
|
417
|
+
if (image) transfer.setDragImage(image, 0, 0);
|
|
418
|
+
}
|
|
419
|
+
view.dragging = { slice, move: true };
|
|
420
|
+
}
|
|
421
|
+
var blockDropCursor = { color: false, class: "bg-accent-primary" };
|
|
422
|
+
var NOTHING_YET = { find: () => null, rows: () => null, startDrag: () => {
|
|
423
|
+
}, drop: () => {
|
|
424
|
+
}, endDrag: () => {
|
|
425
|
+
}, onChange: () => () => {
|
|
426
|
+
} };
|
|
427
|
+
function EditorBlockHandle({ editor, turnInto = [], actions, children, className }) {
|
|
428
|
+
const editing = useMemo(() => {
|
|
429
|
+
if (!editor) return NOTHING_YET;
|
|
430
|
+
let dragged;
|
|
431
|
+
return {
|
|
432
|
+
find: (y) => blockAtY(editorBlocks(editor.view), y),
|
|
433
|
+
rows: (block) => {
|
|
434
|
+
const pos = block.pos;
|
|
435
|
+
if (!editor.isEditable) return null;
|
|
436
|
+
const node = pos === void 0 ? null : editor.state.doc.nodeAt(pos);
|
|
437
|
+
if (pos === void 0 || !node) return null;
|
|
438
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
439
|
+
node.isTextblock && turnInto.length > 0 && /* @__PURE__ */ jsx3(MenuSub, { label: "Turn into", children: turnInto.map((row) => /* @__PURE__ */ jsx3(
|
|
440
|
+
MenuItem,
|
|
441
|
+
{
|
|
442
|
+
label: row.label,
|
|
443
|
+
selected: row.active?.(node) ?? false,
|
|
444
|
+
onClick: () => {
|
|
445
|
+
editor.chain().focus().setTextSelection(pos + 1).run();
|
|
446
|
+
row.run(editor);
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
row.label
|
|
450
|
+
)) }),
|
|
451
|
+
/* @__PURE__ */ jsx3(MenuItem, { label: "Duplicate", leading: /* @__PURE__ */ jsx3(IconCopy, { size: 16, stroke: 1.5, className: "text-text-secondary" }), onClick: () => duplicateBlock(editor, pos) }),
|
|
452
|
+
/* @__PURE__ */ jsx3(MenuItem, { label: "Delete", destructive: true, leading: /* @__PURE__ */ jsx3(IconTrash, { size: 16, stroke: 1.5, className: "text-error-default" }), onClick: () => deleteBlock(editor, pos) })
|
|
453
|
+
] });
|
|
454
|
+
},
|
|
455
|
+
startDrag: (block, event) => {
|
|
456
|
+
dragged = block.pos;
|
|
457
|
+
if (block.pos !== void 0) startBlockDrag(editor, block.pos, event, block.element);
|
|
458
|
+
},
|
|
459
|
+
drop: (target, after) => {
|
|
460
|
+
const node = target.pos === void 0 ? null : editor.state.doc.nodeAt(target.pos);
|
|
461
|
+
if (dragged === void 0 || target.pos === void 0 || !node) return;
|
|
462
|
+
moveBlock(editor, dragged, after ? target.pos + node.nodeSize : target.pos);
|
|
463
|
+
},
|
|
464
|
+
// ProseMirror clears `dragging` on its own dragend, which a drag started outside its element never reaches.
|
|
465
|
+
endDrag: () => setTimeout(() => editor.view.dragging = null, 50),
|
|
466
|
+
onChange: (changed) => {
|
|
467
|
+
editor.on("update", changed);
|
|
468
|
+
return () => {
|
|
469
|
+
editor.off("update", changed);
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
}, [editor, turnInto]);
|
|
474
|
+
return /* @__PURE__ */ jsx3(BlockHandle, { editing, actions, className, children });
|
|
475
|
+
}
|
|
476
|
+
|
|
367
477
|
// src/ComposerTriggers.tsx
|
|
368
478
|
import { Extension as Extension2 } from "@tiptap/core";
|
|
369
479
|
import { PluginKey as PluginKey2 } from "@tiptap/pm/state";
|
|
@@ -373,7 +483,7 @@ import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react";
|
|
|
373
483
|
import { Avatar } from "@estiva-app/ui";
|
|
374
484
|
import { EnterHint } from "@estiva-app/ui";
|
|
375
485
|
import { inlineChipClassName } from "@estiva-app/ui";
|
|
376
|
-
import { jsx as
|
|
486
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
377
487
|
var PEOPLE_LIMIT = 6;
|
|
378
488
|
function filterPeople(people, query) {
|
|
379
489
|
const q = query.toLowerCase();
|
|
@@ -391,8 +501,8 @@ function peopleMenu(urgent) {
|
|
|
391
501
|
row: (person) => ({
|
|
392
502
|
label: person.name,
|
|
393
503
|
description: person.description,
|
|
394
|
-
leading: person.avatar ?? /* @__PURE__ */
|
|
395
|
-
hint: /* @__PURE__ */
|
|
504
|
+
leading: person.avatar ?? /* @__PURE__ */ jsx4(Avatar, { size: 32, alt: person.name }),
|
|
505
|
+
hint: /* @__PURE__ */ jsx4(EnterHint, {}),
|
|
396
506
|
size: "tall"
|
|
397
507
|
})
|
|
398
508
|
};
|
|
@@ -513,9 +623,9 @@ var CaptionedReference = Mention.extend({
|
|
|
513
623
|
addNodeView() {
|
|
514
624
|
const icon = this.options.icon;
|
|
515
625
|
return ReactNodeViewRenderer(
|
|
516
|
-
({ node }) => /* @__PURE__ */
|
|
517
|
-
/* @__PURE__ */
|
|
518
|
-
/* @__PURE__ */
|
|
626
|
+
({ node }) => /* @__PURE__ */ jsxs3(NodeViewWrapper, { as: "span", className: inlineChipClassName("neutral", "max-w-[24ch] cursor-default"), children: [
|
|
627
|
+
/* @__PURE__ */ jsx4("span", { className: "flex items-center justify-center w-4 h-4 shrink-0 text-text-secondary", children: icon }),
|
|
628
|
+
/* @__PURE__ */ jsx4("span", { className: "truncate", children: captionOf(node.attrs.label, node.attrs.snippet) })
|
|
519
629
|
] }),
|
|
520
630
|
{ as: "span" }
|
|
521
631
|
);
|
|
@@ -537,7 +647,7 @@ function referenceMenu({ ariaLabel, sectionLabel, icon }) {
|
|
|
537
647
|
return groups;
|
|
538
648
|
},
|
|
539
649
|
itemKey: (item) => `reference-${item.id}`,
|
|
540
|
-
row: (item) => ({ leading: icon, label: captionOf(item.label, item.snippet), description: item.description, hint: /* @__PURE__ */
|
|
650
|
+
row: (item) => ({ leading: icon, label: captionOf(item.label, item.snippet), description: item.description, hint: /* @__PURE__ */ jsx4(EnterHint, {}) })
|
|
541
651
|
};
|
|
542
652
|
}
|
|
543
653
|
var listed = (options, query) => options.sections ? options.sections(query).flatMap((section) => section.items.map((item) => ({ ...item, section: section.label }))) : filterReferences(options.items(), query);
|
|
@@ -665,7 +775,7 @@ import { Extension as Extension3, Node as Node2 } from "@tiptap/core";
|
|
|
665
775
|
import { Plugin as Plugin2, PluginKey as PluginKey3 } from "@tiptap/pm/state";
|
|
666
776
|
import { Mapping, ReplaceAroundStep } from "@tiptap/pm/transform";
|
|
667
777
|
import { NodeViewWrapper as NodeViewWrapper2, ReactNodeViewRenderer as ReactNodeViewRenderer2 } from "@tiptap/react";
|
|
668
|
-
import { jsx as
|
|
778
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
669
779
|
var BLOCK_TYPES = [
|
|
670
780
|
"paragraph",
|
|
671
781
|
"heading",
|
|
@@ -882,7 +992,7 @@ var ReferenceNode = Node2.create({
|
|
|
882
992
|
addNodeView() {
|
|
883
993
|
const View = this.options.view;
|
|
884
994
|
if (!View) return null;
|
|
885
|
-
return ReactNodeViewRenderer2(({ node }) => /* @__PURE__ */
|
|
995
|
+
return ReactNodeViewRenderer2(({ node }) => /* @__PURE__ */ jsx5(NodeViewWrapper2, { as: "span", className: "inline", children: /* @__PURE__ */ jsx5(View, { uri: String(node.attrs.uri ?? "") }) }));
|
|
886
996
|
}
|
|
887
997
|
});
|
|
888
998
|
var UnknownBlock = Node2.create({
|
|
@@ -910,7 +1020,7 @@ var UnknownBlock = Node2.create({
|
|
|
910
1020
|
function UnknownBlockView({ node }) {
|
|
911
1021
|
const source = node.attrs.source;
|
|
912
1022
|
const text = String(node.attrs.text ?? "");
|
|
913
|
-
return /* @__PURE__ */
|
|
1023
|
+
return /* @__PURE__ */ jsx5(
|
|
914
1024
|
NodeViewWrapper2,
|
|
915
1025
|
{
|
|
916
1026
|
"data-block-id": source?.id,
|
|
@@ -950,13 +1060,14 @@ var AttachmentNode = Node2.create({
|
|
|
950
1060
|
addNodeView() {
|
|
951
1061
|
const View = this.options.view;
|
|
952
1062
|
if (!View) return null;
|
|
953
|
-
return ReactNodeViewRenderer2(({ node }) => /* @__PURE__ */
|
|
1063
|
+
return ReactNodeViewRenderer2(({ node }) => /* @__PURE__ */ jsx5(NodeViewWrapper2, { className: "my-2", children: /* @__PURE__ */ jsx5(View, { attrs: node.attrs }) }));
|
|
954
1064
|
}
|
|
955
1065
|
});
|
|
956
1066
|
export {
|
|
957
1067
|
AttachmentNode,
|
|
958
1068
|
BlockId,
|
|
959
1069
|
CaptionedReference,
|
|
1070
|
+
EditorBlockHandle,
|
|
960
1071
|
FORMATS,
|
|
961
1072
|
KeptSelection,
|
|
962
1073
|
PersonMention,
|
|
@@ -967,15 +1078,20 @@ export {
|
|
|
967
1078
|
UnknownBlock,
|
|
968
1079
|
UrgentPersonMention,
|
|
969
1080
|
applyFormat,
|
|
1081
|
+
blockDropCursor,
|
|
1082
|
+
deleteBlock,
|
|
1083
|
+
duplicateBlock,
|
|
970
1084
|
filterPeople,
|
|
971
1085
|
filterReferences,
|
|
972
1086
|
filterSlashSections,
|
|
973
1087
|
formatSection,
|
|
974
1088
|
isSuggestionActive,
|
|
975
1089
|
isSuggestionOpen,
|
|
1090
|
+
moveBlock,
|
|
976
1091
|
normalizeHref,
|
|
977
1092
|
peopleMenu,
|
|
978
1093
|
referenceMenu,
|
|
1094
|
+
startBlockDrag,
|
|
979
1095
|
suggestionPopup,
|
|
980
1096
|
typeTrigger,
|
|
981
1097
|
urgentPubkeys
|