@domternal/vanilla 0.15.0 → 1.0.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/README.md +24 -5
- package/dist/index.d.ts +5 -83
- package/dist/index.js +52 -227
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
[](https://github.com/domternal/domternal/blob/main/LICENSE)
|
|
5
5
|
|
|
6
6
|
Framework-free DOM components for the [Domternal](https://domternal.dev) editor.
|
|
7
|
-
|
|
8
|
-
`
|
|
9
|
-
`DomternalEmojiPicker
|
|
7
|
+
Most are classes you instantiate against a host element: `DomternalEditor`,
|
|
8
|
+
`DomternalToolbar`, `DomternalBubbleMenu`, `DomternalFloatingMenu`, and
|
|
9
|
+
`DomternalEmojiPicker`. `DomternalNotionColorPicker` is the exception: it takes only an
|
|
10
|
+
options object and resolves its own `.dm-editor` host from the editor. Every class extends
|
|
10
11
|
`EventTarget`, exposes plain getters and mutator methods, dispatches `CustomEvent`s for state
|
|
11
12
|
changes, and tears down with an idempotent `destroy()`. Use it in Astro, Svelte, Solid,
|
|
12
13
|
Lit, Web Components, or plain HTML - anywhere without a framework runtime.
|
|
@@ -21,8 +22,8 @@ Lit, Web Components, or plain HTML - anywhere without a framework runtime.
|
|
|
21
22
|
pnpm add @domternal/core @domternal/theme @domternal/vanilla
|
|
22
23
|
```
|
|
23
24
|
|
|
24
|
-
`@domternal/core` is a peer dependency. `@domternal/theme` supplies the editor
|
|
25
|
-
(import it once in your app).
|
|
25
|
+
`@domternal/core` (>=1.0.0) is a peer dependency. `@domternal/theme` supplies the editor
|
|
26
|
+
styles (import it once in your app).
|
|
26
27
|
|
|
27
28
|
## Usage
|
|
28
29
|
|
|
@@ -70,6 +71,24 @@ The matching mount points:
|
|
|
70
71
|
> SSR. Module-scope imports stay SSR-safe, so gate instantiation behind a client-side
|
|
71
72
|
> entry point (e.g. an Astro `<script>` block or `client:only`).
|
|
72
73
|
|
|
74
|
+
## Options
|
|
75
|
+
|
|
76
|
+
`DomternalEditorOptions`, the second constructor argument:
|
|
77
|
+
|
|
78
|
+
| Option | Type | Default | Description |
|
|
79
|
+
|---|---|---|---|
|
|
80
|
+
| `extensions` | `AnyExtension[]` | `[]` | Extensions merged on top of `DEFAULT_EXTENSIONS`. |
|
|
81
|
+
| `history` | `boolean` | `true` | Whether the built-in History extension is loaded. Turn it off when an extension brings its own undo. |
|
|
82
|
+
| `content` | `Content` | `''` | Initial content, HTML string or JSON. |
|
|
83
|
+
| `editable` | `boolean` | `true` | Whether the editor is editable. |
|
|
84
|
+
| `preset` | `'classic' \| 'notion'` | `'classic'` | `'notion'` paints `dm-notion-mode` on the `.dm-editor` host and switches preset-aware extensions to their Notion behavior. Create-time only. |
|
|
85
|
+
| `autofocus` | `FocusPosition` | `false` | Where to place the caret on mount. |
|
|
86
|
+
| `outputFormat` | `'html' \| 'json'` | `'html'` | Format hint for host frameworks comparing controlled content. Does not change editor behavior. |
|
|
87
|
+
|
|
88
|
+
`onCreate`, `onUpdate`, `onSelectionChange`, `onFocus`, `onBlur`, and `onDestroy` callbacks are
|
|
89
|
+
accepted alongside them, and the same moments are dispatched on the instance as `create`,
|
|
90
|
+
`update`, `selectionchange`, `focus`, `blur`, and `destroy` `CustomEvent`s.
|
|
91
|
+
|
|
73
92
|
## Exports
|
|
74
93
|
|
|
75
94
|
- `DomternalEditor` / `DomternalEditorOptions` - wraps core's `Editor`, plus
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { PluginKey, IconSet,
|
|
1
|
+
import { PluginKey, IconSet, AnyExtension, Editor, Content, EditorPreset, FocusPosition, JSONContent, ToolbarController, ToolbarLayoutEntry, ToolbarButton, ToolbarDropdown, BubbleMenuOptions, BubbleContexts, FloatingMenuController, FloatingMenuOptions, FloatingMenuItemsOverride, FloatingMenuKeymap } from '@domternal/core';
|
|
2
|
+
export { BubbleMenuItem, BubbleMenuSeparator, BubbleItemMaps as ItemMaps, ResolvedPosShape, SelectionShape, buildBubbleItemMaps as buildItemMaps, detectBubbleContext as detectContext, filterBubbleItemsBySchema as filterBySchema, getBubbleFormatItems as getFormatItems, isInsideTableCell, resolveBubbleNames as resolveNames } from '@domternal/core';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* SSR-safe environment check.
|
|
@@ -440,85 +441,6 @@ declare function getComputedStyleAtCursor(editor: Editor, prop: string): string
|
|
|
440
441
|
*/
|
|
441
442
|
declare function getInlineStyleAtCursor(editor: Editor, prop: string): string | null;
|
|
442
443
|
|
|
443
|
-
interface ResolvedPosShape {
|
|
444
|
-
parent: {
|
|
445
|
-
type: {
|
|
446
|
-
name: string;
|
|
447
|
-
spec: {
|
|
448
|
-
marks?: string;
|
|
449
|
-
};
|
|
450
|
-
};
|
|
451
|
-
};
|
|
452
|
-
depth: number;
|
|
453
|
-
node: (depth: number) => {
|
|
454
|
-
type: {
|
|
455
|
-
name: string;
|
|
456
|
-
};
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
interface SelectionShape {
|
|
460
|
-
empty: boolean;
|
|
461
|
-
$from: ResolvedPosShape;
|
|
462
|
-
$to: ResolvedPosShape;
|
|
463
|
-
node?: {
|
|
464
|
-
type: {
|
|
465
|
-
name: string;
|
|
466
|
-
};
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
|
-
interface BubbleMenuSeparator {
|
|
470
|
-
type: 'separator';
|
|
471
|
-
name: string;
|
|
472
|
-
}
|
|
473
|
-
type BubbleMenuItem = ToolbarButton | ToolbarDropdown | BubbleMenuSeparator;
|
|
474
|
-
interface ItemMaps {
|
|
475
|
-
itemMap: Map<string, ToolbarButton>;
|
|
476
|
-
dropdownMap: Map<string, ToolbarDropdown>;
|
|
477
|
-
/** Bubble defaults indexed by context name (e.g. 'text', 'codeBlock'). */
|
|
478
|
-
bubbleDefaults: Map<string, BubbleMenuItem[]>;
|
|
479
|
-
}
|
|
480
|
-
/**
|
|
481
|
-
* Walks `editor.toolbarItems` and indexes them by name. Dropdowns are kept
|
|
482
|
-
* separately so the bubble menu can resolve them by name (e.g. text-align).
|
|
483
|
-
*/
|
|
484
|
-
declare function buildItemMaps(editor: Editor): ItemMaps;
|
|
485
|
-
/**
|
|
486
|
-
* Resolve a name array to BubbleMenuItems via the item/dropdown maps.
|
|
487
|
-
* Dropdowns take priority over buttons sharing the same name. Pipe `|`
|
|
488
|
-
* tokens become separator entries.
|
|
489
|
-
*/
|
|
490
|
-
declare function resolveNames(names: string[], itemMap: Map<string, ToolbarButton>, dropdownMap: Map<string, ToolbarDropdown>): BubbleMenuItem[];
|
|
491
|
-
/**
|
|
492
|
-
* All `format`-group buttons sorted by priority (used by `context: true`
|
|
493
|
-
* shorthand to show "all format marks for this context").
|
|
494
|
-
*/
|
|
495
|
-
declare function getFormatItems(itemMap: Map<string, ToolbarButton>): ToolbarButton[];
|
|
496
|
-
/**
|
|
497
|
-
* Determine the active bubble-menu context based on the selection. Returns
|
|
498
|
-
* `null` when no context matches (menu should not show).
|
|
499
|
-
*
|
|
500
|
-
* Resolution order:
|
|
501
|
-
* 1. CellSelection (`$anchorCell`) - no menu inside table cell selections
|
|
502
|
-
* 2. NodeSelection (image, HR, etc.) - return the node's type name
|
|
503
|
-
* 3. Empty selection - no context (caret-only)
|
|
504
|
-
* 4. Inside a table cell - return 'table' (when from/to share a cell)
|
|
505
|
-
* 5. `$from.parent.type.name` if listed in contexts
|
|
506
|
-
* 6. 'text' if the parent allows marks
|
|
507
|
-
*/
|
|
508
|
-
declare function detectContext(selection: SelectionShape, ctxs: Record<string, string[] | true | null>): string | null;
|
|
509
|
-
/**
|
|
510
|
-
* Filter items by what the schema actually allows in the given context.
|
|
511
|
-
* E.g. inside a `codeBlock` node, marks like Bold/Italic aren't permitted.
|
|
512
|
-
*
|
|
513
|
-
* Pass-through for 'text' and 'table' contexts (schema check would be
|
|
514
|
-
* lossy there because marks are allowed but some items still apply).
|
|
515
|
-
*/
|
|
516
|
-
declare function filterBySchema(editor: Editor, contextName: string, schemaItems: ToolbarButton[]): ToolbarButton[];
|
|
517
|
-
/**
|
|
518
|
-
* Detect whether `$pos` is inside a table cell (cell or header).
|
|
519
|
-
*/
|
|
520
|
-
declare function isInsideTableCell($pos: ResolvedPosShape): boolean;
|
|
521
|
-
|
|
522
444
|
/**
|
|
523
445
|
* Live state for the bubble-menu trailing buttons.
|
|
524
446
|
*
|
|
@@ -576,7 +498,7 @@ interface DomternalBubbleMenuOptions extends CustomContentOption {
|
|
|
576
498
|
* to item name list, `true` (show all format items), or `null` (no menu for
|
|
577
499
|
* this context). Defaults to `defaultBubbleContexts(editor)`.
|
|
578
500
|
*/
|
|
579
|
-
contexts?:
|
|
501
|
+
contexts?: BubbleContexts;
|
|
580
502
|
/** Custom icon overrides. Falls back to default Phosphor icons for unmapped keys. */
|
|
581
503
|
icons?: IconSet;
|
|
582
504
|
}
|
|
@@ -666,7 +588,7 @@ declare class DomternalBubbleMenu extends EventTarget {
|
|
|
666
588
|
* plugin at construction; this only changes WHICH items render once the
|
|
667
589
|
* menu is visible.
|
|
668
590
|
*/
|
|
669
|
-
setContexts(contexts:
|
|
591
|
+
setContexts(contexts: BubbleContexts | undefined): void;
|
|
670
592
|
/** Replace the icon set. `undefined` restores default Phosphor icons. */
|
|
671
593
|
setIcons(icons: IconSet | undefined): void;
|
|
672
594
|
/** Close any open dropdown (text-align). No-op if nothing is open. */
|
|
@@ -878,4 +800,4 @@ declare class DomternalEmojiPicker extends EventTarget {
|
|
|
878
800
|
destroy(): void;
|
|
879
801
|
}
|
|
880
802
|
|
|
881
|
-
export { type
|
|
803
|
+
export { type BubbleMenuTrailingState, type CustomContentOption, DEFAULT_EXTENSIONS, DROPDOWN_CARET, DomternalBubbleMenu, type DomternalBubbleMenuOptions, DomternalEditor, type DomternalEditorOptions, DomternalEmojiPicker, type DomternalEmojiPickerOptions, DomternalFloatingMenu, type DomternalFloatingMenuOptions, DomternalNotionColorPicker, type DomternalNotionColorPickerOptions, DomternalToolbar, type DomternalToolbarOptions, type EmojiPickerItem, INITIAL_TRAILING_STATE, type IconCache, assertBrowser, computeTrailingState, createIconCache, createPluginKey, getComputedStyleAtCursor, getInlineStyleAtCursor, getTooltip, isBrowser, renderIconInto, resolveIcon, subscribe };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Document, Paragraph, Text, BaseKeymap, History, PluginKey, defaultIcons, Editor, ToolbarController, positionFloatingOnce, refocusEditorAfterCommand, defaultBubbleContexts, createBubbleMenuPlugin, createFloatingMenuPlugin, FloatingMenuController, positionFloating } from '@domternal/core';
|
|
1
|
+
import { Document, Paragraph, Text, BaseKeymap, History, PluginKey, defaultIcons, Editor, ToolbarController, positionFloatingOnce, refocusEditorAfterCommand, resolveBubbleNames, defaultBubbleContexts, buildBubbleItemMaps, createBubbleMenuPlugin, createBubbleShouldShow, resolveBubbleMenuItems, createFloatingMenuPlugin, FloatingMenuController, positionFloating } from '@domternal/core';
|
|
2
|
+
export { buildBubbleItemMaps as buildItemMaps, detectBubbleContext as detectContext, filterBubbleItemsBySchema as filterBySchema, getBubbleFormatItems as getFormatItems, isInsideTableCell, resolveBubbleNames as resolveNames } from '@domternal/core';
|
|
2
3
|
|
|
3
4
|
// src/shared/isBrowser.ts
|
|
4
5
|
var isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
|
|
@@ -889,129 +890,6 @@ var DomternalToolbar = class extends EventTarget {
|
|
|
889
890
|
}
|
|
890
891
|
};
|
|
891
892
|
|
|
892
|
-
// src/bubble-menu/itemResolver.ts
|
|
893
|
-
function buildItemMaps(editor) {
|
|
894
|
-
const itemMap = /* @__PURE__ */ new Map();
|
|
895
|
-
const dropdownMap = /* @__PURE__ */ new Map();
|
|
896
|
-
for (const item of editor.toolbarItems) {
|
|
897
|
-
if (item.type === "button") {
|
|
898
|
-
itemMap.set(item.name, item);
|
|
899
|
-
} else if (item.type === "dropdown") {
|
|
900
|
-
dropdownMap.set(item.name, item);
|
|
901
|
-
for (const sub of item.items) {
|
|
902
|
-
itemMap.set(sub.name, sub);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
return {
|
|
907
|
-
itemMap,
|
|
908
|
-
dropdownMap,
|
|
909
|
-
bubbleDefaults: buildBubbleDefaults(editor)
|
|
910
|
-
};
|
|
911
|
-
}
|
|
912
|
-
function buildBubbleDefaults(editor) {
|
|
913
|
-
const byCtx = /* @__PURE__ */ new Map();
|
|
914
|
-
const addItem = (btn) => {
|
|
915
|
-
const ctx = btn["bubbleMenu"];
|
|
916
|
-
if (!ctx) return;
|
|
917
|
-
let arr = byCtx.get(ctx);
|
|
918
|
-
if (!arr) {
|
|
919
|
-
arr = [];
|
|
920
|
-
byCtx.set(ctx, arr);
|
|
921
|
-
}
|
|
922
|
-
arr.push(btn);
|
|
923
|
-
};
|
|
924
|
-
for (const item of editor.toolbarItems) {
|
|
925
|
-
if (item.type === "button") addItem(item);
|
|
926
|
-
else if (item.type === "dropdown") {
|
|
927
|
-
for (const sub of item.items) addItem(sub);
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
const result = /* @__PURE__ */ new Map();
|
|
931
|
-
for (const [ctx, ctxItems] of byCtx) {
|
|
932
|
-
ctxItems.sort((a, b) => (b.priority ?? 100) - (a.priority ?? 100));
|
|
933
|
-
const list = [];
|
|
934
|
-
let lastGroup;
|
|
935
|
-
let sepIdx = 0;
|
|
936
|
-
for (const item of ctxItems) {
|
|
937
|
-
if (lastGroup !== void 0 && item.group !== lastGroup) {
|
|
938
|
-
list.push({ type: "separator", name: `bsep-${String(sepIdx++)}` });
|
|
939
|
-
}
|
|
940
|
-
list.push(item);
|
|
941
|
-
lastGroup = item.group;
|
|
942
|
-
}
|
|
943
|
-
result.set(ctx, list);
|
|
944
|
-
}
|
|
945
|
-
return result;
|
|
946
|
-
}
|
|
947
|
-
function resolveNames(names, itemMap, dropdownMap) {
|
|
948
|
-
const result = [];
|
|
949
|
-
let sepIdx = 0;
|
|
950
|
-
for (const name of names) {
|
|
951
|
-
if (name === "|") {
|
|
952
|
-
result.push({ type: "separator", name: `sep-${String(sepIdx++)}` });
|
|
953
|
-
continue;
|
|
954
|
-
}
|
|
955
|
-
const dropdown = dropdownMap.get(name);
|
|
956
|
-
if (dropdown) {
|
|
957
|
-
result.push(dropdown);
|
|
958
|
-
continue;
|
|
959
|
-
}
|
|
960
|
-
const item = itemMap.get(name);
|
|
961
|
-
if (item) result.push(item);
|
|
962
|
-
}
|
|
963
|
-
return result;
|
|
964
|
-
}
|
|
965
|
-
function getFormatItems(itemMap) {
|
|
966
|
-
return Array.from(itemMap.values()).filter((item) => item.group === "format").sort((a, b) => (b.priority ?? 100) - (a.priority ?? 100));
|
|
967
|
-
}
|
|
968
|
-
function detectContext(selection, ctxs) {
|
|
969
|
-
if ("$anchorCell" in selection) return null;
|
|
970
|
-
if (selection.node) return selection.node.type.name;
|
|
971
|
-
if (selection.empty) return null;
|
|
972
|
-
const fromCell = findCellNode(selection.$from);
|
|
973
|
-
if (fromCell) {
|
|
974
|
-
const toCell = findCellNode(selection.$to);
|
|
975
|
-
if (toCell && fromCell !== toCell) return null;
|
|
976
|
-
return "table";
|
|
977
|
-
}
|
|
978
|
-
const fromName = selection.$from.parent.type.name;
|
|
979
|
-
if (fromName in ctxs) return fromName;
|
|
980
|
-
if ("text" in ctxs && selection.$from.parent.type.spec.marks !== "") return "text";
|
|
981
|
-
const toName = selection.$to.parent.type.name;
|
|
982
|
-
if (toName in ctxs) return toName;
|
|
983
|
-
if ("text" in ctxs && selection.$to.parent.type.spec.marks !== "") return "text";
|
|
984
|
-
return null;
|
|
985
|
-
}
|
|
986
|
-
function filterBySchema(editor, contextName, schemaItems) {
|
|
987
|
-
if (contextName === "text" || contextName === "table") return schemaItems;
|
|
988
|
-
const schema = editor.state.schema;
|
|
989
|
-
if (!schema) return schemaItems;
|
|
990
|
-
const nodeType = schema.nodes[contextName];
|
|
991
|
-
if (!nodeType) return schemaItems;
|
|
992
|
-
return schemaItems.filter((item) => {
|
|
993
|
-
const markName = typeof item.isActive === "string" ? item.isActive : null;
|
|
994
|
-
if (!markName) return true;
|
|
995
|
-
const markType = schema.marks[markName];
|
|
996
|
-
if (!markType) return true;
|
|
997
|
-
return nodeType.allowsMarkType(markType);
|
|
998
|
-
});
|
|
999
|
-
}
|
|
1000
|
-
function isInsideTableCell($pos) {
|
|
1001
|
-
for (let d = $pos.depth; d > 0; d--) {
|
|
1002
|
-
const name = $pos.node(d).type.name;
|
|
1003
|
-
if (name === "tableCell" || name === "tableHeader") return true;
|
|
1004
|
-
}
|
|
1005
|
-
return false;
|
|
1006
|
-
}
|
|
1007
|
-
function findCellNode(pos) {
|
|
1008
|
-
for (let d = pos.depth; d > 0; d--) {
|
|
1009
|
-
const node = pos.node(d);
|
|
1010
|
-
if (node.type.name === "tableCell" || node.type.name === "tableHeader") return node;
|
|
1011
|
-
}
|
|
1012
|
-
return null;
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
893
|
// src/bubble-menu/trailingState.ts
|
|
1016
894
|
var INITIAL_TRAILING_STATE = {
|
|
1017
895
|
isNodeSelection: false,
|
|
@@ -1189,7 +1067,7 @@ var DomternalBubbleMenu = class extends EventTarget {
|
|
|
1189
1067
|
if (this.#destroyed) return;
|
|
1190
1068
|
this.#explicitItems = items;
|
|
1191
1069
|
if (this.#maps) {
|
|
1192
|
-
this.#defaultItemList = items ?
|
|
1070
|
+
this.#defaultItemList = items ? resolveBubbleNames(items, this.#maps.itemMap, this.#maps.dropdownMap) : resolveBubbleNames(["bold", "italic", "underline"], this.#maps.itemMap, this.#maps.dropdownMap);
|
|
1193
1071
|
}
|
|
1194
1072
|
this.#updateResolvedItems();
|
|
1195
1073
|
this.#updateStates();
|
|
@@ -1246,9 +1124,9 @@ var DomternalBubbleMenu = class extends EventTarget {
|
|
|
1246
1124
|
const exts = ed.extensionManager.extensions;
|
|
1247
1125
|
this.#hasNotionColorPicker = exts.some((e) => e.name === "notionColorPicker");
|
|
1248
1126
|
this.#hasBlockContextMenu = exts.some((e) => e.name === "blockContextMenu");
|
|
1249
|
-
this.#maps =
|
|
1127
|
+
this.#maps = buildBubbleItemMaps(ed);
|
|
1250
1128
|
this.#effectiveContexts = this.#explicitContexts ?? (this.#explicitItems ? void 0 : defaultBubbleContexts(ed));
|
|
1251
|
-
this.#defaultItemList = this.#explicitItems ?
|
|
1129
|
+
this.#defaultItemList = this.#explicitItems ? resolveBubbleNames(this.#explicitItems, this.#maps.itemMap, this.#maps.dropdownMap) : resolveBubbleNames(["bold", "italic", "underline"], this.#maps.itemMap, this.#maps.dropdownMap);
|
|
1252
1130
|
const shouldShow = this.#shouldShowOpt ?? this.#buildDefaultShouldShow();
|
|
1253
1131
|
const plugin = createBubbleMenuPlugin({
|
|
1254
1132
|
pluginKey: this.#pluginKey,
|
|
@@ -1280,76 +1158,28 @@ var DomternalBubbleMenu = class extends EventTarget {
|
|
|
1280
1158
|
this.#render();
|
|
1281
1159
|
}
|
|
1282
1160
|
#buildDefaultShouldShow() {
|
|
1283
|
-
const
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
return ({ state }) => {
|
|
1287
|
-
const ctx = detectContext(
|
|
1288
|
-
state.selection,
|
|
1289
|
-
contexts
|
|
1290
|
-
);
|
|
1291
|
-
if (!ctx) return false;
|
|
1292
|
-
if (ctx in contexts) {
|
|
1293
|
-
const val = contexts[ctx];
|
|
1294
|
-
if (val === null) return false;
|
|
1295
|
-
return val === true || Array.isArray(val) && val.length > 0;
|
|
1296
|
-
}
|
|
1297
|
-
return defaults?.has(ctx) ?? false;
|
|
1298
|
-
};
|
|
1299
|
-
}
|
|
1300
|
-
return ({ state }) => {
|
|
1301
|
-
const sel = state.selection;
|
|
1302
|
-
if (sel.empty) return false;
|
|
1303
|
-
if (sel.node) return defaults?.has(sel.node.type.name) ?? false;
|
|
1304
|
-
if (isInsideTableCell(sel.$from)) return false;
|
|
1305
|
-
return sel.$from.parent.type.spec.marks !== "" || sel.$to.parent.type.spec.marks !== "";
|
|
1306
|
-
};
|
|
1161
|
+
const maps = this.#maps;
|
|
1162
|
+
if (!maps) return () => false;
|
|
1163
|
+
return createBubbleShouldShow(maps, this.#effectiveContexts);
|
|
1307
1164
|
}
|
|
1308
1165
|
// === State updates (called per transaction) ===
|
|
1166
|
+
/**
|
|
1167
|
+
* One call per transaction, into the resolver core owns. Every renderer
|
|
1168
|
+
* asks the same question and has to get the same answer, so the answer is
|
|
1169
|
+
* not written here.
|
|
1170
|
+
*/
|
|
1309
1171
|
#updateResolvedItems() {
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
if (contexts) {
|
|
1314
|
-
const ctx = detectContext(
|
|
1315
|
-
ed.state.selection,
|
|
1316
|
-
contexts
|
|
1317
|
-
);
|
|
1318
|
-
if (!ctx) {
|
|
1319
|
-
this.#resolvedItems = [];
|
|
1320
|
-
return;
|
|
1321
|
-
}
|
|
1322
|
-
if (ctx in contexts) {
|
|
1323
|
-
const val = contexts[ctx];
|
|
1324
|
-
if (val === null || Array.isArray(val) && val.length === 0) {
|
|
1325
|
-
this.#resolvedItems = [];
|
|
1326
|
-
return;
|
|
1327
|
-
}
|
|
1328
|
-
if (val === true) {
|
|
1329
|
-
this.#resolvedItems = filterBySchema(ed, ctx, getFormatItems(this.#maps.itemMap));
|
|
1330
|
-
return;
|
|
1331
|
-
}
|
|
1332
|
-
if (Array.isArray(val)) {
|
|
1333
|
-
const resolved = resolveNames(val, this.#maps.itemMap, this.#maps.dropdownMap);
|
|
1334
|
-
const buttons = resolved.filter(
|
|
1335
|
-
(i) => i.type !== "separator"
|
|
1336
|
-
);
|
|
1337
|
-
const allowed = new Set(filterBySchema(ed, ctx, buttons).map((b) => b.name));
|
|
1338
|
-
this.#resolvedItems = resolved.filter(
|
|
1339
|
-
(i) => i.type === "separator" || allowed.has(i.name)
|
|
1340
|
-
);
|
|
1341
|
-
return;
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
this.#resolvedItems = this.#maps.bubbleDefaults.get(ctx) ?? [];
|
|
1172
|
+
const maps = this.#maps;
|
|
1173
|
+
if (!maps) {
|
|
1174
|
+
this.#resolvedItems = [];
|
|
1345
1175
|
return;
|
|
1346
1176
|
}
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
}
|
|
1177
|
+
this.#resolvedItems = resolveBubbleMenuItems({
|
|
1178
|
+
editor: this.#editor,
|
|
1179
|
+
maps,
|
|
1180
|
+
contexts: this.#effectiveContexts,
|
|
1181
|
+
fallbackItems: this.#defaultItemList
|
|
1182
|
+
});
|
|
1353
1183
|
}
|
|
1354
1184
|
#updateStates() {
|
|
1355
1185
|
const ed = this.#editor;
|
|
@@ -1445,12 +1275,18 @@ var DomternalBubbleMenu = class extends EventTarget {
|
|
|
1445
1275
|
}
|
|
1446
1276
|
}
|
|
1447
1277
|
const t = this.#trailing;
|
|
1448
|
-
|
|
1449
|
-
|
|
1278
|
+
const showColor = t.showColorPickerButton && !t.isNodeSelection;
|
|
1279
|
+
const showBlock = t.showBlockMenuButton && !t.isNodeSelection;
|
|
1280
|
+
if (showColor) {
|
|
1281
|
+
if (this.#resolvedItems.length > 0) {
|
|
1282
|
+
this.host.appendChild(this.#createSeparator("trailing-sep-color"));
|
|
1283
|
+
}
|
|
1450
1284
|
this.host.appendChild(this.#createColorTrigger());
|
|
1451
1285
|
}
|
|
1452
|
-
if (
|
|
1453
|
-
|
|
1286
|
+
if (showBlock) {
|
|
1287
|
+
if (this.#resolvedItems.length > 0 || showColor) {
|
|
1288
|
+
this.host.appendChild(this.#createSeparator("trailing-sep-block"));
|
|
1289
|
+
}
|
|
1454
1290
|
this.host.appendChild(this.#createBlockMenuTrigger());
|
|
1455
1291
|
}
|
|
1456
1292
|
if (this.#customContent) {
|
|
@@ -1977,6 +1813,14 @@ var DomternalFloatingMenu = class extends EventTarget {
|
|
|
1977
1813
|
}
|
|
1978
1814
|
}
|
|
1979
1815
|
};
|
|
1816
|
+
function paletteFromExtensionOptions(options) {
|
|
1817
|
+
if (typeof options !== "object" || options === null || !("palette" in options)) return [];
|
|
1818
|
+
const palette = options.palette;
|
|
1819
|
+
if (!Array.isArray(palette) || !palette.every((token) => typeof token === "string")) {
|
|
1820
|
+
return [];
|
|
1821
|
+
}
|
|
1822
|
+
return [...palette];
|
|
1823
|
+
}
|
|
1980
1824
|
var TOKEN_LABELS = {
|
|
1981
1825
|
gray: "Gray",
|
|
1982
1826
|
brown: "Brown",
|
|
@@ -2053,8 +1897,7 @@ var DomternalNotionColorPicker = class extends EventTarget {
|
|
|
2053
1897
|
const ext = this.#editor.extensionManager.extensions.find(
|
|
2054
1898
|
(e) => e.name === "notionColorPicker"
|
|
2055
1899
|
);
|
|
2056
|
-
|
|
2057
|
-
this.#palette = extOpts?.palette ? [...extOpts.palette] : [];
|
|
1900
|
+
this.#palette = paletteFromExtensionOptions(ext?.options);
|
|
2058
1901
|
this.#onOpen = (...args) => {
|
|
2059
1902
|
const detail = args[0];
|
|
2060
1903
|
const incoming = detail?.anchorElement;
|
|
@@ -2135,9 +1978,7 @@ var DomternalNotionColorPicker = class extends EventTarget {
|
|
|
2135
1978
|
this.#editor.commands.setTextColorToken(token);
|
|
2136
1979
|
this.#syncFromSelection();
|
|
2137
1980
|
this.#updateActiveClasses();
|
|
2138
|
-
this.dispatchEvent(
|
|
2139
|
-
new CustomEvent("apply", { detail: { kind: "text", token } })
|
|
2140
|
-
);
|
|
1981
|
+
this.dispatchEvent(new CustomEvent("apply", { detail: { kind: "text", token } }));
|
|
2141
1982
|
}
|
|
2142
1983
|
/** Apply a background color token to the current selection. Picker stays open. */
|
|
2143
1984
|
applyBg(token) {
|
|
@@ -2145,9 +1986,7 @@ var DomternalNotionColorPicker = class extends EventTarget {
|
|
|
2145
1986
|
this.#editor.commands.setBackgroundColorToken(token);
|
|
2146
1987
|
this.#syncFromSelection();
|
|
2147
1988
|
this.#updateActiveClasses();
|
|
2148
|
-
this.dispatchEvent(
|
|
2149
|
-
new CustomEvent("apply", { detail: { kind: "bg", token } })
|
|
2150
|
-
);
|
|
1989
|
+
this.dispatchEvent(new CustomEvent("apply", { detail: { kind: "bg", token } }));
|
|
2151
1990
|
}
|
|
2152
1991
|
/** Display label for a palette token (title-case fallback). */
|
|
2153
1992
|
tokenLabel(token) {
|
|
@@ -2357,9 +2196,7 @@ var DomternalNotionColorPicker = class extends EventTarget {
|
|
|
2357
2196
|
return rect;
|
|
2358
2197
|
}
|
|
2359
2198
|
if (this.#anchorBubbleMenu?.isConnected) {
|
|
2360
|
-
const fresh = this.#anchorBubbleMenu.querySelector(
|
|
2361
|
-
".dm-ncp-trigger"
|
|
2362
|
-
);
|
|
2199
|
+
const fresh = this.#anchorBubbleMenu.querySelector(".dm-ncp-trigger");
|
|
2363
2200
|
if (fresh) {
|
|
2364
2201
|
this.#anchor = fresh;
|
|
2365
2202
|
const rect = fresh.getBoundingClientRect();
|
|
@@ -2429,15 +2266,13 @@ var DomternalNotionColorPicker = class extends EventTarget {
|
|
|
2429
2266
|
#onPanelKeydown(event) {
|
|
2430
2267
|
const cols = 5;
|
|
2431
2268
|
if (!this.#panel) return;
|
|
2432
|
-
const swatches = Array.from(
|
|
2433
|
-
this.#panel.querySelectorAll(".dm-ncp-swatch")
|
|
2434
|
-
);
|
|
2269
|
+
const swatches = Array.from(this.#panel.querySelectorAll(".dm-ncp-swatch"));
|
|
2435
2270
|
if (!swatches.length) return;
|
|
2436
2271
|
const active = document.activeElement;
|
|
2437
2272
|
if (!(active instanceof HTMLElement)) return;
|
|
2438
2273
|
const idx = swatches.indexOf(active);
|
|
2439
2274
|
if (idx === -1) return;
|
|
2440
|
-
let next
|
|
2275
|
+
let next;
|
|
2441
2276
|
switch (event.key) {
|
|
2442
2277
|
case "ArrowRight":
|
|
2443
2278
|
event.preventDefault();
|
|
@@ -2664,11 +2499,7 @@ var DomternalEmojiPicker = class extends EventTarget {
|
|
|
2664
2499
|
return panel;
|
|
2665
2500
|
}
|
|
2666
2501
|
#renderPanelChildren() {
|
|
2667
|
-
return [
|
|
2668
|
-
this.#renderSearch(),
|
|
2669
|
-
this.#renderTabs(),
|
|
2670
|
-
this.#renderGrid()
|
|
2671
|
-
];
|
|
2502
|
+
return [this.#renderSearch(), this.#renderTabs(), this.#renderGrid()];
|
|
2672
2503
|
}
|
|
2673
2504
|
#renderSearch() {
|
|
2674
2505
|
const wrapper = document.createElement("div");
|
|
@@ -2783,9 +2614,7 @@ var DomternalEmojiPicker = class extends EventTarget {
|
|
|
2783
2614
|
*/
|
|
2784
2615
|
#setSearchQuery(value) {
|
|
2785
2616
|
this.#searchQuery = value;
|
|
2786
|
-
const input = this.#panel?.querySelector(
|
|
2787
|
-
".dm-emoji-picker-search input"
|
|
2788
|
-
);
|
|
2617
|
+
const input = this.#panel?.querySelector(".dm-emoji-picker-search input");
|
|
2789
2618
|
if (input && input.value !== value) input.value = value;
|
|
2790
2619
|
}
|
|
2791
2620
|
/** Replace grid contents only (used when searchQuery changes). */
|
|
@@ -2849,9 +2678,7 @@ var DomternalEmojiPicker = class extends EventTarget {
|
|
|
2849
2678
|
#onGridKeydown(event) {
|
|
2850
2679
|
const grid = this.#panel?.querySelector(".dm-emoji-picker-grid");
|
|
2851
2680
|
if (!grid) return;
|
|
2852
|
-
const swatches = Array.from(
|
|
2853
|
-
grid.querySelectorAll(".dm-emoji-swatch")
|
|
2854
|
-
);
|
|
2681
|
+
const swatches = Array.from(grid.querySelectorAll(".dm-emoji-swatch"));
|
|
2855
2682
|
if (!swatches.length) return;
|
|
2856
2683
|
const active = document.activeElement;
|
|
2857
2684
|
const idx = active instanceof HTMLElement ? swatches.indexOf(active) : -1;
|
|
@@ -2863,7 +2690,7 @@ var DomternalEmojiPicker = class extends EventTarget {
|
|
|
2863
2690
|
return;
|
|
2864
2691
|
}
|
|
2865
2692
|
const cols = 8;
|
|
2866
|
-
let next
|
|
2693
|
+
let next;
|
|
2867
2694
|
switch (event.key) {
|
|
2868
2695
|
case "ArrowRight":
|
|
2869
2696
|
event.preventDefault();
|
|
@@ -2901,9 +2728,7 @@ var DomternalEmojiPicker = class extends EventTarget {
|
|
|
2901
2728
|
offsetValue: 4
|
|
2902
2729
|
});
|
|
2903
2730
|
}
|
|
2904
|
-
const input = this.#panel.querySelector(
|
|
2905
|
-
".dm-emoji-picker-search input"
|
|
2906
|
-
);
|
|
2731
|
+
const input = this.#panel.querySelector(".dm-emoji-picker-search input");
|
|
2907
2732
|
input?.focus({ preventScroll: true });
|
|
2908
2733
|
});
|
|
2909
2734
|
}
|
|
@@ -2942,6 +2767,6 @@ var DomternalEmojiPicker = class extends EventTarget {
|
|
|
2942
2767
|
}
|
|
2943
2768
|
};
|
|
2944
2769
|
|
|
2945
|
-
export { DEFAULT_EXTENSIONS, DROPDOWN_CARET, DomternalBubbleMenu, DomternalEditor, DomternalEmojiPicker, DomternalFloatingMenu, DomternalNotionColorPicker, DomternalToolbar, INITIAL_TRAILING_STATE, assertBrowser,
|
|
2770
|
+
export { DEFAULT_EXTENSIONS, DROPDOWN_CARET, DomternalBubbleMenu, DomternalEditor, DomternalEmojiPicker, DomternalFloatingMenu, DomternalNotionColorPicker, DomternalToolbar, INITIAL_TRAILING_STATE, assertBrowser, computeTrailingState, createIconCache, createPluginKey, getComputedStyleAtCursor, getInlineStyleAtCursor, getTooltip, isBrowser, renderIconInto, resolveIcon, subscribe };
|
|
2946
2771
|
//# sourceMappingURL=index.js.map
|
|
2947
2772
|
//# sourceMappingURL=index.js.map
|