@estiva-app/ui 0.53.1 → 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/NavItem.d.ts +14 -1
- package/dist/NavItem.d.ts.map +1 -1
- package/dist/NavTree.d.ts +93 -13
- package/dist/NavTree.d.ts.map +1 -1
- package/dist/UnreadDot.d.ts +12 -0
- package/dist/UnreadDot.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/eslint/index.js +1 -1
- package/dist/eslint/index.js.map +2 -2
- package/dist/eslint/no-native-title.d.ts +1 -1
- package/dist/eslint/no-native-title.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +801 -584
- package/dist/index.js.map +4 -4
- package/dist/looks.d.ts +1 -1
- package/dist/looks.d.ts.map +1 -1
- package/package.json +1 -1
- package/registry.json +362 -11
- 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/NavItem.mdx +14 -1
- package/src/NavItem.stories.tsx +15 -0
- package/src/NavItem.test.tsx +31 -0
- package/src/NavItem.tsx +49 -12
- package/src/NavTree.mdx +25 -0
- package/src/NavTree.stories.tsx +57 -1
- package/src/NavTree.test.tsx +78 -1
- package/src/NavTree.tsx +175 -40
- package/src/SectionLabel.stories.tsx +2 -3
- package/src/UnreadDot.mdx +57 -0
- package/src/UnreadDot.stories.tsx +22 -0
- package/src/UnreadDot.test.tsx +17 -0
- package/src/UnreadDot.tsx +17 -0
- package/src/editor.ts +1 -0
- package/src/eslint/no-native-title.ts +1 -1
- package/src/index.ts +3 -1
- package/src/looks.ts +2 -1
- package/src/registry/registry.test.ts +5 -5
- package/stories/Choosing.mdx +3 -0
- package/tailwind-preset.js +1 -0
- package/tokens.css +5 -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/NavItem.d.ts
CHANGED
|
@@ -26,6 +26,15 @@ import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
|
26
26
|
* `hint` is shown only while the row is pointed at or focused, left of the
|
|
27
27
|
* count — a neutral `Chip`, typically. It takes its own room while it shows,
|
|
28
28
|
* so the label ends with "…" before it instead of running under it.
|
|
29
|
+
*
|
|
30
|
+
* `unread` brightens the row's words and icon, sets the label in medium, and
|
|
31
|
+
* draws the `UnreadDot` at the right; `urgent` with it draws the warning badge
|
|
32
|
+
* in the dot's place (Katerina, 2 October: Peek's row, moved in). The ⋮ lands
|
|
33
|
+
* on whatever is in that place, and with nothing there it takes no room until
|
|
34
|
+
* the row is pointed at, so a label at rest keeps the row's whole width.
|
|
35
|
+
*
|
|
36
|
+
* Active is `bg-nav-active`, the current row in a sidebar, which each theme
|
|
37
|
+
* sets: the selected tab's neutral fill in most, Signal's blue in Signal.
|
|
29
38
|
*/
|
|
30
39
|
export interface NavItemProps extends Omit<ComponentPropsWithoutRef<'a'>, 'href' | 'children'> {
|
|
31
40
|
label: string;
|
|
@@ -43,6 +52,10 @@ export interface NavItemProps extends Omit<ComponentPropsWithoutRef<'a'>, 'href'
|
|
|
43
52
|
menuLabel?: string;
|
|
44
53
|
/** Shown only while the row is pointed at or focused, left of the count: a neutral `Chip`, typically. */
|
|
45
54
|
hint?: ReactNode;
|
|
55
|
+
/** Something new here: brighter words, the label in medium, and the `UnreadDot` at the right. Say so in the row's own words for assistive tech too. */
|
|
56
|
+
unread?: boolean;
|
|
57
|
+
/** With `unread`: the warning badge in the dot's place. */
|
|
58
|
+
urgent?: boolean;
|
|
46
59
|
/** Rows under this one, folding under it: `NavItem`s. While the row is pointed at or focused, its icon becomes the arrow. */
|
|
47
60
|
children?: ReactNode;
|
|
48
61
|
/** With `children`: open unless told otherwise. */
|
|
@@ -52,5 +65,5 @@ export interface NavItemProps extends Omit<ComponentPropsWithoutRef<'a'>, 'href'
|
|
|
52
65
|
onOpenChange?: (open: boolean) => void;
|
|
53
66
|
className?: string;
|
|
54
67
|
}
|
|
55
|
-
export declare function NavItem({ label, href, count, countLabel, active, icon, menu, menuLabel, hint, children, defaultOpen, open: openProp, onOpenChange, className, ...props }: NavItemProps): import("react").JSX.Element;
|
|
68
|
+
export declare function NavItem({ label, href, count, countLabel, active, icon, menu, menuLabel, hint, unread, urgent, children, defaultOpen, open: openProp, onOpenChange, className, ...props }: NavItemProps): import("react").JSX.Element;
|
|
56
69
|
//# sourceMappingURL=NavItem.d.ts.map
|
package/dist/NavItem.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NavItem.d.ts","sourceRoot":"","sources":["../src/NavItem.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,wBAAwB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"NavItem.d.ts","sourceRoot":"","sources":["../src/NavItem.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,wBAAwB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAUtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,wBAAwB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAC5F,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,wBAAwB;IACxB,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,4GAA4G;IAC5G,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,yGAAyG;IACzG,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,uJAAuJ;IACvJ,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,6HAA6H;IAC7H,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,mDAAmD;IACnD,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,kDAAkD;IAClD,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,IAAI,EACJ,KAAK,EACL,UAAU,EACV,MAAc,EACd,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,MAAc,EACd,MAAc,EACd,QAAQ,EACR,WAAkB,EAClB,IAAI,EAAE,QAAQ,EACd,YAAY,EACZ,SAAS,EACT,GAAG,KAAK,EACT,EAAE,YAAY,+BAwId"}
|
package/dist/NavTree.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type MouseEvent, type ReactNode } from 'react';
|
|
2
|
+
import { type SectionAction } from './SectionHeader';
|
|
2
3
|
/** One row of the tree, and the rows under it. */
|
|
3
4
|
export interface NavTreeNode {
|
|
4
5
|
id: string;
|
|
@@ -12,38 +13,117 @@ export interface NavTreeNode {
|
|
|
12
13
|
countLabel?: string;
|
|
13
14
|
/** Shown only while the row is pointed at or focused, left of the count: a neutral `Chip`, typically. */
|
|
14
15
|
hint?: ReactNode;
|
|
16
|
+
/** Something new here: NavItem's `unread`. */
|
|
17
|
+
unread?: boolean;
|
|
18
|
+
/** With `unread`: the warning badge in the dot's place. */
|
|
19
|
+
urgent?: boolean;
|
|
15
20
|
/** The row's "More options" menu: `MenuItem`s. */
|
|
16
21
|
menu?: ReactNode;
|
|
17
|
-
/**
|
|
18
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Rows under this one. `null` while they are being read: the row has its
|
|
24
|
+
* arrow, and a skeleton under it once open. Absent with `hasChildren`, the
|
|
25
|
+
* same — for rows read only when the row opens.
|
|
26
|
+
*/
|
|
27
|
+
children?: NavTreeNode[] | null;
|
|
28
|
+
/** The row has rows under it that are not read yet: it gets its arrow, and `onOpenChange` says when to read them. */
|
|
29
|
+
hasChildren?: boolean;
|
|
30
|
+
/** Said under the row, level with its rows, in place of them: "Nothing in this yet.", "This could not be read." The caller's words. */
|
|
31
|
+
childrenMessage?: string;
|
|
19
32
|
defaultOpen?: boolean;
|
|
33
|
+
/** The caller owns whether its rows are open. */
|
|
34
|
+
open?: boolean;
|
|
35
|
+
onOpenChange?: (open: boolean) => void;
|
|
20
36
|
}
|
|
21
37
|
/** A group heading over its rows: the top of the tree. */
|
|
22
38
|
export interface NavTreeGroup {
|
|
23
39
|
id: string;
|
|
24
40
|
title: string;
|
|
25
|
-
|
|
41
|
+
/** The rows. `null` while they are being read: a skeleton in their place. */
|
|
42
|
+
nodes: NavTreeNode[] | null;
|
|
43
|
+
/** Said in the rows' place, level with them: "Nothing in this yet.", "This could not be read." The caller's words. */
|
|
44
|
+
message?: string;
|
|
45
|
+
/** Beside the title, always shown: an `UnreadDot`, typically. */
|
|
46
|
+
trailing?: ReactNode;
|
|
47
|
+
/** Beside the title, shown on hover or focus: "Open", typically. */
|
|
48
|
+
actions?: SectionAction[];
|
|
49
|
+
defaultOpen?: boolean;
|
|
50
|
+
/** The caller owns whether the group is open. */
|
|
51
|
+
open?: boolean;
|
|
52
|
+
onOpenChange?: (open: boolean) => void;
|
|
53
|
+
/**
|
|
54
|
+
* The pointer rests on the group, or the keyboard reaches it: read its rows
|
|
55
|
+
* now, so opening it draws them. Resting, not passing — a pointer sweeping
|
|
56
|
+
* down the column calls nothing.
|
|
57
|
+
*/
|
|
58
|
+
onIntent?: () => void;
|
|
26
59
|
}
|
|
27
60
|
export interface NavTreeProps {
|
|
28
61
|
/** The quiet label over the groups. Absent, none is drawn. */
|
|
29
62
|
title?: string;
|
|
30
|
-
/**
|
|
31
|
-
|
|
63
|
+
/** Beside the label, always shown: "New", typically. */
|
|
64
|
+
titleActions?: SectionAction[];
|
|
65
|
+
/**
|
|
66
|
+
* `null` while the first read is on its way: a skeleton in the groups' place.
|
|
67
|
+
* Absent when the app draws its own groups as `children`.
|
|
68
|
+
*/
|
|
69
|
+
groups?: NavTreeGroup[] | null;
|
|
70
|
+
/**
|
|
71
|
+
* The groups, drawn by the app: `NavTreeSection`s with `NavItem` rows — for
|
|
72
|
+
* an app whose groups and rows each read their own data. Used when `groups`
|
|
73
|
+
* is absent.
|
|
74
|
+
*/
|
|
75
|
+
children?: ReactNode;
|
|
32
76
|
/** The row that is current: `active`, and its group starts open. */
|
|
33
77
|
selected?: string;
|
|
34
78
|
/** A row was clicked. A router app prevents the default and navigates. */
|
|
35
79
|
onSelect?: (node: NavTreeNode, event: MouseEvent<HTMLAnchorElement>) => void;
|
|
36
|
-
/** What to say when
|
|
37
|
-
emptyMessage
|
|
80
|
+
/** What to say when `groups` is empty. The words are the caller's; absent, nothing is said. */
|
|
81
|
+
emptyMessage?: string;
|
|
38
82
|
/** Remember each group open or closed in this browser, under this key and the group's id. The app prefixes it. */
|
|
39
83
|
storageKey?: string;
|
|
40
84
|
}
|
|
85
|
+
/** How long the pointer rests on a group before `onIntent` (Peek's FolderTree, 150ms). */
|
|
86
|
+
export declare const NAV_TREE_INTENT_MS = 150;
|
|
87
|
+
export interface NavTreeSectionProps {
|
|
88
|
+
title: string;
|
|
89
|
+
/** Marks the group's box as `data-nav-group`, for tests and photo runs to find it. */
|
|
90
|
+
id?: string;
|
|
91
|
+
/** Said in the rows' place, level with them. The caller's words. */
|
|
92
|
+
message?: string;
|
|
93
|
+
/** Beside the title, always shown: an `UnreadDot`, typically. */
|
|
94
|
+
trailing?: ReactNode;
|
|
95
|
+
/** Beside the title, shown on hover or focus: "Open", typically. */
|
|
96
|
+
actions?: SectionAction[];
|
|
97
|
+
/** Open unless told otherwise. */
|
|
98
|
+
defaultOpen?: boolean;
|
|
99
|
+
/** The caller owns whether the group is open. */
|
|
100
|
+
open?: boolean;
|
|
101
|
+
onOpenChange?: (open: boolean) => void;
|
|
102
|
+
/** The pointer rests on the group, or the keyboard reaches it: read its rows now. */
|
|
103
|
+
onIntent?: () => void;
|
|
104
|
+
/** Its rows are being read: a skeleton in their place. */
|
|
105
|
+
loading?: boolean;
|
|
106
|
+
/** Remember it open or closed in this browser, under this key. */
|
|
107
|
+
storageKey?: string;
|
|
108
|
+
/** The rows: `NavItem`s. */
|
|
109
|
+
children?: ReactNode;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* One group of a `NavTree`, for an app that draws its own: the standard
|
|
113
|
+
* heading folding its rows, lightly indented under its title, with the
|
|
114
|
+
* group's line, skeleton, actions and `onIntent`. `NavTree` draws these from
|
|
115
|
+
* `groups`; an app whose groups each read their own data draws them itself,
|
|
116
|
+
* as `NavTree`'s children.
|
|
117
|
+
*/
|
|
118
|
+
export declare function NavTreeSection({ id, title, message, loading, trailing, actions, defaultOpen, open, onOpenChange, storageKey, onIntent, children }: NavTreeSectionProps): import("react").JSX.Element;
|
|
41
119
|
/**
|
|
42
120
|
* A sidebar's tree, from one nested list (Katerina, 2 October: Leaf's folder
|
|
43
|
-
* tree): a quiet label, then each
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
121
|
+
* tree, then Peek's and Ship's): a quiet label with its actions, then each
|
|
122
|
+
* group as the standard heading folding its rows, lightly indented under its
|
|
123
|
+
* title; rows under rows to any depth, each row a link whose icon becomes the
|
|
124
|
+
* arrow that folds what is under it. Groups and rows can be read when they
|
|
125
|
+
* open. Only the group that holds the current row starts open, unless the
|
|
126
|
+
* caller owns it.
|
|
47
127
|
*/
|
|
48
|
-
export declare function NavTree({ title, groups, selected, onSelect, emptyMessage, storageKey }: NavTreeProps): import("react").JSX.Element;
|
|
128
|
+
export declare function NavTree({ title, titleActions, groups, children, selected, onSelect, emptyMessage, storageKey }: NavTreeProps): import("react").JSX.Element;
|
|
49
129
|
//# sourceMappingURL=NavTree.d.ts.map
|
package/dist/NavTree.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NavTree.d.ts","sourceRoot":"","sources":["../src/NavTree.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"NavTree.d.ts","sourceRoot":"","sources":["../src/NavTree.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The accent dot that says "something new here": a 6px disc centred in a
|
|
3
|
+
* 24px slot, so it sits where a control would and the row does not shift when
|
|
4
|
+
* one replaces the other (Peek's UnreadDot, moved in on 2 October, Katerina).
|
|
5
|
+
*
|
|
6
|
+
* Purely visual: the row that draws it says "unread" in its own accessible
|
|
7
|
+
* name, because a coloured disc reads as nothing to a screen reader.
|
|
8
|
+
*/
|
|
9
|
+
export declare function UnreadDot({ className }: {
|
|
10
|
+
className?: string;
|
|
11
|
+
}): import("react").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=UnreadDot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UnreadDot.d.ts","sourceRoot":"","sources":["../src/UnreadDot.tsx"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAM9D"}
|
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
|