@milkdown/plugin-tooltip 7.18.0 → 7.19.1

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/lib/index.js CHANGED
@@ -1,171 +1,142 @@
1
- import { computePosition, flip, offset, shift, autoUpdate } from "@floating-ui/dom";
1
+ import { autoUpdate, computePosition, flip, offset, shift } from "@floating-ui/dom";
2
2
  import { posToDOMRect } from "@milkdown/prose";
3
- import { TextSelection, Plugin, PluginKey } from "@milkdown/prose/state";
3
+ import { Plugin, PluginKey, TextSelection } from "@milkdown/prose/state";
4
4
  import { throttle } from "lodash-es";
5
5
  import { $ctx, $prose } from "@milkdown/utils";
6
- class TooltipProvider {
7
- constructor(options) {
8
- this.#initialized = false;
9
- this.onShow = () => {
10
- };
11
- this.onHide = () => {
12
- };
13
- this.#updatePosition = (reference) => {
14
- computePosition(reference, this.element, {
15
- placement: this.#floatingUIOptions.placement ?? "top",
16
- middleware: [
17
- flip(),
18
- offset(this.#offset),
19
- shift(this.#shift),
20
- ...this.#middleware
21
- ],
22
- ...this.#floatingUIOptions
23
- }).then(({ x, y }) => {
24
- Object.assign(this.element.style, {
25
- left: `${x}px`,
26
- top: `${y}px`
27
- });
28
- }).catch(console.error);
29
- };
30
- this.#shouldAutoUpdate = (editorView) => {
31
- return this.#root !== editorView.dom.parentElement;
32
- };
33
- this.#onUpdate = (view, prevState) => {
34
- const { state, composing } = view;
35
- const { selection, doc } = state;
36
- const { ranges } = selection;
37
- const from = Math.min(...ranges.map((range) => range.$from.pos));
38
- const to = Math.max(...ranges.map((range) => range.$to.pos));
39
- const isSame = prevState && prevState.doc.eq(doc) && prevState.selection.eq(selection);
40
- if (!this.#initialized) {
41
- const root = this.#root ?? view.dom.parentElement ?? document.body;
42
- root.appendChild(this.element);
43
- this.#initialized = true;
44
- }
45
- if (composing || isSame) return;
46
- this.#cleanupAutoUpdate?.();
47
- this.#cleanupAutoUpdate = void 0;
48
- if (!this.#shouldShow(view, prevState)) {
49
- this.hide();
50
- return;
51
- }
52
- const virtualEl = {
53
- getBoundingClientRect: () => posToDOMRect(view, from, to),
54
- contextElement: view.dom
55
- };
56
- if (this.#shouldAutoUpdate(view)) {
57
- this.#cleanupAutoUpdate = autoUpdate(
58
- virtualEl,
59
- this.element,
60
- () => this.#updatePosition(virtualEl)
61
- );
62
- } else {
63
- this.#updatePosition(virtualEl);
64
- }
65
- this.show();
66
- };
67
- this.update = (view, prevState) => {
68
- this.#updater(view, prevState);
69
- };
70
- this.destroy = () => {
71
- this.#cleanupAutoUpdate?.();
72
- this.#updater.cancel();
73
- };
74
- this.show = (virtualElement, editorView) => {
75
- this.element.dataset.show = "true";
76
- if (virtualElement) {
77
- this.#cleanupAutoUpdate?.();
78
- this.#cleanupAutoUpdate = void 0;
79
- const reference = { ...virtualElement, contextElement: editorView?.dom };
80
- if (editorView && this.#shouldAutoUpdate(editorView)) {
81
- this.#cleanupAutoUpdate = autoUpdate(
82
- reference,
83
- this.element,
84
- () => this.#updatePosition(reference)
85
- );
86
- } else {
87
- this.#updatePosition(reference);
88
- }
89
- }
90
- this.onShow();
91
- };
92
- this.hide = () => {
93
- if (this.element.dataset.show === "false") return;
94
- this.element.dataset.show = "false";
95
- this.onHide();
96
- };
97
- this.element = options.content;
98
- this.#debounce = options.debounce ?? 200;
99
- this.#shouldShow = options.shouldShow ?? this.#_shouldShow;
100
- this.#offset = options.offset;
101
- this.#shift = options.shift;
102
- this.#middleware = options.middleware ?? [];
103
- this.#floatingUIOptions = options.floatingUIOptions ?? {};
104
- this.#root = options.root;
105
- this.element.dataset.show = "false";
106
- this.#updater = throttle(this.#onUpdate, this.#debounce);
107
- }
108
- /// @internal
109
- #debounce;
110
- /// @internal
111
- #shouldShow;
112
- /// @internal
113
- #middleware;
114
- /// @internal
115
- #floatingUIOptions;
116
- /// @internal
117
- #root;
118
- #initialized;
119
- /// @internal
120
- #cleanupAutoUpdate;
121
- /// @internal
122
- #offset;
123
- /// @internal
124
- #shift;
125
- /// @internal
126
- #updater;
127
- #updatePosition;
128
- #shouldAutoUpdate;
129
- #onUpdate;
130
- /// @internal
131
- #_shouldShow(view) {
132
- const { doc, selection } = view.state;
133
- const { empty, from, to } = selection;
134
- const isEmptyTextBlock = !doc.textBetween(from, to).length && view.state.selection instanceof TextSelection;
135
- const isTooltipChildren = this.element.contains(document.activeElement);
136
- const notHasFocus = !view.hasFocus() && !isTooltipChildren;
137
- const isReadonly = !view.editable;
138
- if (notHasFocus || empty || isEmptyTextBlock || isReadonly) return false;
139
- return true;
140
- }
141
- }
6
+ //#region src/tooltip-provider.ts
7
+ var TooltipProvider = class {
8
+ #debounce;
9
+ #shouldShow;
10
+ #middleware;
11
+ #floatingUIOptions;
12
+ #root;
13
+ #initialized = false;
14
+ #cleanupAutoUpdate;
15
+ #offset;
16
+ #shift;
17
+ #updater;
18
+ constructor(options) {
19
+ this.onShow = () => {};
20
+ this.onHide = () => {};
21
+ this.update = (view, prevState) => {
22
+ this.#updater(view, prevState);
23
+ };
24
+ this.destroy = () => {
25
+ this.#cleanupAutoUpdate?.();
26
+ this.#updater.cancel();
27
+ };
28
+ this.show = (virtualElement, editorView) => {
29
+ this.element.dataset.show = "true";
30
+ if (virtualElement) {
31
+ this.#cleanupAutoUpdate?.();
32
+ this.#cleanupAutoUpdate = void 0;
33
+ const reference = {
34
+ ...virtualElement,
35
+ contextElement: editorView?.dom
36
+ };
37
+ if (editorView && this.#shouldAutoUpdate(editorView)) this.#cleanupAutoUpdate = autoUpdate(reference, this.element, () => this.#updatePosition(reference));
38
+ else this.#updatePosition(reference);
39
+ }
40
+ this.onShow();
41
+ };
42
+ this.hide = () => {
43
+ if (this.element.dataset.show === "false") return;
44
+ this.element.dataset.show = "false";
45
+ this.onHide();
46
+ };
47
+ this.element = options.content;
48
+ this.#debounce = options.debounce ?? 200;
49
+ this.#shouldShow = options.shouldShow ?? this.#_shouldShow;
50
+ this.#offset = options.offset;
51
+ this.#shift = options.shift;
52
+ this.#middleware = options.middleware ?? [];
53
+ this.#floatingUIOptions = options.floatingUIOptions ?? {};
54
+ this.#root = options.root;
55
+ this.element.dataset.show = "false";
56
+ this.#updater = throttle(this.#onUpdate, this.#debounce);
57
+ }
58
+ #updatePosition = (reference) => {
59
+ computePosition(reference, this.element, {
60
+ placement: this.#floatingUIOptions.placement ?? "top",
61
+ middleware: [
62
+ flip(),
63
+ offset(this.#offset),
64
+ shift(this.#shift),
65
+ ...this.#middleware
66
+ ],
67
+ ...this.#floatingUIOptions
68
+ }).then(({ x, y }) => {
69
+ Object.assign(this.element.style, {
70
+ left: `${x}px`,
71
+ top: `${y}px`
72
+ });
73
+ }).catch(console.error);
74
+ };
75
+ #shouldAutoUpdate = (editorView) => {
76
+ return this.#root !== editorView.dom.parentElement;
77
+ };
78
+ #onUpdate = (view, prevState) => {
79
+ const { state, composing } = view;
80
+ const { selection, doc } = state;
81
+ const { ranges } = selection;
82
+ const from = Math.min(...ranges.map((range) => range.$from.pos));
83
+ const to = Math.max(...ranges.map((range) => range.$to.pos));
84
+ const isSame = prevState && prevState.doc.eq(doc) && prevState.selection.eq(selection);
85
+ if (!this.#initialized) {
86
+ (this.#root ?? view.dom.parentElement ?? document.body).appendChild(this.element);
87
+ this.#initialized = true;
88
+ }
89
+ if (composing || isSame) return;
90
+ this.#cleanupAutoUpdate?.();
91
+ this.#cleanupAutoUpdate = void 0;
92
+ if (!this.#shouldShow(view, prevState)) {
93
+ this.hide();
94
+ return;
95
+ }
96
+ const virtualEl = {
97
+ getBoundingClientRect: () => posToDOMRect(view, from, to),
98
+ contextElement: view.dom
99
+ };
100
+ if (this.#shouldAutoUpdate(view)) this.#cleanupAutoUpdate = autoUpdate(virtualEl, this.element, () => this.#updatePosition(virtualEl));
101
+ else this.#updatePosition(virtualEl);
102
+ this.show();
103
+ };
104
+ #_shouldShow(view) {
105
+ const { doc, selection } = view.state;
106
+ const { empty, from, to } = selection;
107
+ const isEmptyTextBlock = !doc.textBetween(from, to).length && view.state.selection instanceof TextSelection;
108
+ const isTooltipChildren = this.element.contains(document.activeElement);
109
+ const notHasFocus = !view.hasFocus() && !isTooltipChildren;
110
+ const isReadonly = !view.editable;
111
+ if (notHasFocus || empty || isEmptyTextBlock || isReadonly) return false;
112
+ return true;
113
+ }
114
+ };
115
+ //#endregion
116
+ //#region src/tooltip-plugin.ts
142
117
  function tooltipFactory(id) {
143
- const tooltipSpec = $ctx(
144
- {},
145
- `${id}_TOOLTIP_SPEC`
146
- );
147
- const tooltipPlugin = $prose((ctx) => {
148
- const spec = ctx.get(tooltipSpec.key);
149
- return new Plugin({
150
- key: new PluginKey(`${id}_TOOLTIP`),
151
- ...spec
152
- });
153
- });
154
- const result = [tooltipSpec, tooltipPlugin];
155
- result.key = tooltipSpec.key;
156
- result.pluginKey = tooltipPlugin.key;
157
- tooltipSpec.meta = {
158
- package: "@milkdown/plugin-tooltip",
159
- displayName: `Ctx<tooltipSpec>|${id}`
160
- };
161
- tooltipPlugin.meta = {
162
- package: "@milkdown/plugin-tooltip",
163
- displayName: `Prose<tooltip>|${id}`
164
- };
165
- return result;
118
+ const tooltipSpec = $ctx({}, `${id}_TOOLTIP_SPEC`);
119
+ const tooltipPlugin = $prose((ctx) => {
120
+ const spec = ctx.get(tooltipSpec.key);
121
+ return new Plugin({
122
+ key: new PluginKey(`${id}_TOOLTIP`),
123
+ ...spec
124
+ });
125
+ });
126
+ const result = [tooltipSpec, tooltipPlugin];
127
+ result.key = tooltipSpec.key;
128
+ result.pluginKey = tooltipPlugin.key;
129
+ tooltipSpec.meta = {
130
+ package: "@milkdown/plugin-tooltip",
131
+ displayName: `Ctx<tooltipSpec>|${id}`
132
+ };
133
+ tooltipPlugin.meta = {
134
+ package: "@milkdown/plugin-tooltip",
135
+ displayName: `Prose<tooltip>|${id}`
136
+ };
137
+ return result;
166
138
  }
167
- export {
168
- TooltipProvider,
169
- tooltipFactory
170
- };
171
- //# sourceMappingURL=index.js.map
139
+ //#endregion
140
+ export { TooltipProvider, tooltipFactory };
141
+
142
+ //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/tooltip-provider.ts","../src/tooltip-plugin.ts"],"sourcesContent":["import type {\n ComputePositionConfig,\n Middleware,\n OffsetOptions,\n ShiftOptions,\n VirtualElement,\n} from '@floating-ui/dom'\nimport type { EditorState } from '@milkdown/prose/state'\nimport type { EditorView } from '@milkdown/prose/view'\n\nimport {\n autoUpdate,\n computePosition,\n flip,\n offset,\n shift,\n} from '@floating-ui/dom'\nimport { posToDOMRect } from '@milkdown/prose'\nimport { TextSelection } from '@milkdown/prose/state'\nimport { throttle } from 'lodash-es'\n\n/// Options for tooltip provider.\nexport interface TooltipProviderOptions {\n /// The tooltip content.\n content: HTMLElement\n /// The debounce time for updating tooltip, 200ms by default.\n debounce?: number\n /// The function to determine whether the tooltip should be shown.\n shouldShow?: (view: EditorView, prevState?: EditorState) => boolean\n /// The offset to get the block. Default is 0.\n offset?: OffsetOptions\n /// The amount to shift options the block by.\n shift?: ShiftOptions\n /// Other middlewares for floating ui. This will be added after the internal middlewares.\n middleware?: Middleware[]\n /// Options for floating ui. If you pass `middleware` or `placement`, it will override the internal settings.\n floatingUIOptions?: Partial<ComputePositionConfig>\n /// The root element that the tooltip will be appended to.\n root?: HTMLElement\n}\n\n/// A provider for creating tooltip.\nexport class TooltipProvider {\n /// @internal\n readonly #debounce: number\n\n /// @internal\n readonly #shouldShow: (view: EditorView, prevState?: EditorState) => boolean\n\n /// @internal\n readonly #middleware: Middleware[]\n\n /// @internal\n readonly #floatingUIOptions: Partial<ComputePositionConfig>\n\n /// @internal\n readonly #root?: HTMLElement\n\n /// @internal\n #initialized = false\n\n /// @internal\n #cleanupAutoUpdate?: () => void\n\n /// @internal\n readonly #offset?: OffsetOptions\n\n /// @internal\n readonly #shift?: ShiftOptions\n\n /// @internal\n readonly #updater: {\n (view: EditorView, prevState?: EditorState): void\n cancel: () => void\n }\n\n /// The root element of the tooltip.\n element: HTMLElement\n\n /// On show callback.\n onShow = () => {}\n\n /// On hide callback.\n onHide = () => {}\n\n constructor(options: TooltipProviderOptions) {\n this.element = options.content\n this.#debounce = options.debounce ?? 200\n this.#shouldShow = options.shouldShow ?? this.#_shouldShow\n this.#offset = options.offset\n this.#shift = options.shift\n this.#middleware = options.middleware ?? []\n this.#floatingUIOptions = options.floatingUIOptions ?? {}\n this.#root = options.root\n this.element.dataset.show = 'false'\n this.#updater = throttle(this.#onUpdate, this.#debounce)\n }\n\n /// @internel\n #updatePosition = (reference: VirtualElement) => {\n computePosition(reference, this.element, {\n placement: this.#floatingUIOptions.placement ?? 'top',\n middleware: [\n flip(),\n offset(this.#offset),\n shift(this.#shift),\n ...this.#middleware,\n ],\n ...this.#floatingUIOptions,\n })\n .then(({ x, y }) => {\n Object.assign(this.element.style, {\n left: `${x}px`,\n top: `${y}px`,\n })\n })\n .catch(console.error)\n }\n\n /// @internal\n #shouldAutoUpdate = (editorView: EditorView) => {\n return this.#root !== editorView.dom.parentElement\n }\n\n /// @internal\n #onUpdate = (view: EditorView, prevState?: EditorState): void => {\n const { state, composing } = view\n const { selection, doc } = state\n const { ranges } = selection\n const from = Math.min(...ranges.map((range) => range.$from.pos))\n const to = Math.max(...ranges.map((range) => range.$to.pos))\n const isSame =\n prevState && prevState.doc.eq(doc) && prevState.selection.eq(selection)\n\n if (!this.#initialized) {\n const root = this.#root ?? view.dom.parentElement ?? document.body\n root.appendChild(this.element)\n this.#initialized = true\n }\n\n if (composing || isSame) return\n\n this.#cleanupAutoUpdate?.()\n this.#cleanupAutoUpdate = void 0\n\n if (!this.#shouldShow(view, prevState)) {\n this.hide()\n return\n }\n\n const virtualEl: VirtualElement = {\n getBoundingClientRect: () => posToDOMRect(view, from, to),\n contextElement: view.dom,\n }\n\n if (this.#shouldAutoUpdate(view)) {\n this.#cleanupAutoUpdate = autoUpdate(virtualEl, this.element, () =>\n this.#updatePosition(virtualEl)\n )\n } else {\n this.#updatePosition(virtualEl)\n }\n\n this.show()\n }\n\n /// Update provider state by editor view.\n update = (view: EditorView, prevState?: EditorState): void => {\n this.#updater(view, prevState)\n }\n\n /// @internal\n #_shouldShow(view: EditorView): boolean {\n const { doc, selection } = view.state\n const { empty, from, to } = selection\n\n const isEmptyTextBlock =\n !doc.textBetween(from, to).length &&\n view.state.selection instanceof TextSelection\n\n const isTooltipChildren = this.element.contains(document.activeElement)\n\n const notHasFocus = !view.hasFocus() && !isTooltipChildren\n\n const isReadonly = !view.editable\n\n if (notHasFocus || empty || isEmptyTextBlock || isReadonly) return false\n\n return true\n }\n\n /// Destroy the tooltip.\n destroy = () => {\n this.#cleanupAutoUpdate?.()\n this.#updater.cancel()\n }\n\n /// Show the tooltip.\n show = (virtualElement?: VirtualElement, editorView?: EditorView) => {\n this.element.dataset.show = 'true'\n\n if (virtualElement) {\n this.#cleanupAutoUpdate?.()\n this.#cleanupAutoUpdate = void 0\n\n const reference = { ...virtualElement, contextElement: editorView?.dom }\n\n if (editorView && this.#shouldAutoUpdate(editorView)) {\n this.#cleanupAutoUpdate = autoUpdate(reference, this.element, () =>\n this.#updatePosition(reference)\n )\n } else {\n this.#updatePosition(reference)\n }\n }\n\n this.onShow()\n }\n\n /// Hide the tooltip.\n hide = () => {\n if (this.element.dataset.show === 'false') return\n this.element.dataset.show = 'false'\n\n this.onHide()\n }\n}\n","import type { SliceType } from '@milkdown/ctx'\nimport type { PluginSpec } from '@milkdown/prose/state'\nimport type { $Ctx, $Prose } from '@milkdown/utils'\n\nimport { Plugin, PluginKey } from '@milkdown/prose/state'\nimport { $ctx, $prose } from '@milkdown/utils'\n\n/// @internal\nexport type TooltipSpecId<Id extends string> = `${Id}_TOOLTIP_SPEC`\n\n/// @internal\nexport type TooltipPlugin<Id extends string, State = any> = [\n $Ctx<PluginSpec<State>, TooltipSpecId<Id>>,\n $Prose,\n] & {\n key: SliceType<PluginSpec<State>, TooltipSpecId<Id>>\n pluginKey: $Prose['key']\n}\n\n/// Create a tooltip plugin with a unique id.\nexport function tooltipFactory<Id extends string, State = any>(id: Id) {\n const tooltipSpec = $ctx<PluginSpec<State>, TooltipSpecId<Id>>(\n {},\n `${id}_TOOLTIP_SPEC`\n )\n const tooltipPlugin = $prose((ctx) => {\n const spec = ctx.get(tooltipSpec.key)\n return new Plugin({\n key: new PluginKey(`${id}_TOOLTIP`),\n ...spec,\n })\n })\n const result = [tooltipSpec, tooltipPlugin] as TooltipPlugin<Id>\n result.key = tooltipSpec.key\n result.pluginKey = tooltipPlugin.key\n tooltipSpec.meta = {\n package: '@milkdown/plugin-tooltip',\n displayName: `Ctx<tooltipSpec>|${id}`,\n }\n tooltipPlugin.meta = {\n package: '@milkdown/plugin-tooltip',\n displayName: `Prose<tooltip>|${id}`,\n }\n\n return result\n}\n"],"names":[],"mappings":";;;;;AA0CO,MAAM,gBAAgB;AAAA,EA2C3B,YAAY,SAAiC;AA1B7C,SAAA,eAAe;AAqBf,SAAA,SAAS,MAAM;AAAA,IAAC;AAGhB,SAAA,SAAS,MAAM;AAAA,IAAC;AAgBhB,SAAA,kBAAkB,CAAC,cAA8B;AAC/C,sBAAgB,WAAW,KAAK,SAAS;AAAA,QACvC,WAAW,KAAK,mBAAmB,aAAa;AAAA,QAChD,YAAY;AAAA,UACV,KAAA;AAAA,UACA,OAAO,KAAK,OAAO;AAAA,UACnB,MAAM,KAAK,MAAM;AAAA,UACjB,GAAG,KAAK;AAAA,QAAA;AAAA,QAEV,GAAG,KAAK;AAAA,MAAA,CACT,EACE,KAAK,CAAC,EAAE,GAAG,QAAQ;AAClB,eAAO,OAAO,KAAK,QAAQ,OAAO;AAAA,UAChC,MAAM,GAAG,CAAC;AAAA,UACV,KAAK,GAAG,CAAC;AAAA,QAAA,CACV;AAAA,MACH,CAAC,EACA,MAAM,QAAQ,KAAK;AAAA,IACxB;AAGA,SAAA,oBAAoB,CAAC,eAA2B;AAC9C,aAAO,KAAK,UAAU,WAAW,IAAI;AAAA,IACvC;AAGA,SAAA,YAAY,CAAC,MAAkB,cAAkC;AAC/D,YAAM,EAAE,OAAO,UAAA,IAAc;AAC7B,YAAM,EAAE,WAAW,IAAA,IAAQ;AAC3B,YAAM,EAAE,WAAW;AACnB,YAAM,OAAO,KAAK,IAAI,GAAG,OAAO,IAAI,CAAC,UAAU,MAAM,MAAM,GAAG,CAAC;AAC/D,YAAM,KAAK,KAAK,IAAI,GAAG,OAAO,IAAI,CAAC,UAAU,MAAM,IAAI,GAAG,CAAC;AAC3D,YAAM,SACJ,aAAa,UAAU,IAAI,GAAG,GAAG,KAAK,UAAU,UAAU,GAAG,SAAS;AAExE,UAAI,CAAC,KAAK,cAAc;AACtB,cAAM,OAAO,KAAK,SAAS,KAAK,IAAI,iBAAiB,SAAS;AAC9D,aAAK,YAAY,KAAK,OAAO;AAC7B,aAAK,eAAe;AAAA,MACtB;AAEA,UAAI,aAAa,OAAQ;AAEzB,WAAK,qBAAA;AACL,WAAK,qBAAqB;AAE1B,UAAI,CAAC,KAAK,YAAY,MAAM,SAAS,GAAG;AACtC,aAAK,KAAA;AACL;AAAA,MACF;AAEA,YAAM,YAA4B;AAAA,QAChC,uBAAuB,MAAM,aAAa,MAAM,MAAM,EAAE;AAAA,QACxD,gBAAgB,KAAK;AAAA,MAAA;AAGvB,UAAI,KAAK,kBAAkB,IAAI,GAAG;AAChC,aAAK,qBAAqB;AAAA,UAAW;AAAA,UAAW,KAAK;AAAA,UAAS,MAC5D,KAAK,gBAAgB,SAAS;AAAA,QAAA;AAAA,MAElC,OAAO;AACL,aAAK,gBAAgB,SAAS;AAAA,MAChC;AAEA,WAAK,KAAA;AAAA,IACP;AAGA,SAAA,SAAS,CAAC,MAAkB,cAAkC;AAC5D,WAAK,SAAS,MAAM,SAAS;AAAA,IAC/B;AAuBA,SAAA,UAAU,MAAM;AACd,WAAK,qBAAA;AACL,WAAK,SAAS,OAAA;AAAA,IAChB;AAGA,SAAA,OAAO,CAAC,gBAAiC,eAA4B;AACnE,WAAK,QAAQ,QAAQ,OAAO;AAE5B,UAAI,gBAAgB;AAClB,aAAK,qBAAA;AACL,aAAK,qBAAqB;AAE1B,cAAM,YAAY,EAAE,GAAG,gBAAgB,gBAAgB,YAAY,IAAA;AAEnE,YAAI,cAAc,KAAK,kBAAkB,UAAU,GAAG;AACpD,eAAK,qBAAqB;AAAA,YAAW;AAAA,YAAW,KAAK;AAAA,YAAS,MAC5D,KAAK,gBAAgB,SAAS;AAAA,UAAA;AAAA,QAElC,OAAO;AACL,eAAK,gBAAgB,SAAS;AAAA,QAChC;AAAA,MACF;AAEA,WAAK,OAAA;AAAA,IACP;AAGA,SAAA,OAAO,MAAM;AACX,UAAI,KAAK,QAAQ,QAAQ,SAAS,QAAS;AAC3C,WAAK,QAAQ,QAAQ,OAAO;AAE5B,WAAK,OAAA;AAAA,IACP;AA3IE,SAAK,UAAU,QAAQ;AACvB,SAAK,YAAY,QAAQ,YAAY;AACrC,SAAK,cAAc,QAAQ,cAAc,KAAK;AAC9C,SAAK,UAAU,QAAQ;AACvB,SAAK,SAAS,QAAQ;AACtB,SAAK,cAAc,QAAQ,cAAc,CAAA;AACzC,SAAK,qBAAqB,QAAQ,qBAAqB,CAAA;AACvD,SAAK,QAAQ,QAAQ;AACrB,SAAK,QAAQ,QAAQ,OAAO;AAC5B,SAAK,WAAW,SAAS,KAAK,WAAW,KAAK,SAAS;AAAA,EACzD;AAAA;AAAA,EApDS;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA,EAGT;AAAA;AAAA,EAGA;AAAA;AAAA,EAGS;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA,EA4BT;AAAA,EAqBA;AAAA,EAKA;AAAA;AAAA,EA+CA,aAAa,MAA2B;AACtC,UAAM,EAAE,KAAK,UAAA,IAAc,KAAK;AAChC,UAAM,EAAE,OAAO,MAAM,GAAA,IAAO;AAE5B,UAAM,mBACJ,CAAC,IAAI,YAAY,MAAM,EAAE,EAAE,UAC3B,KAAK,MAAM,qBAAqB;AAElC,UAAM,oBAAoB,KAAK,QAAQ,SAAS,SAAS,aAAa;AAEtE,UAAM,cAAc,CAAC,KAAK,SAAA,KAAc,CAAC;AAEzC,UAAM,aAAa,CAAC,KAAK;AAEzB,QAAI,eAAe,SAAS,oBAAoB,WAAY,QAAO;AAEnE,WAAO;AAAA,EACT;AAqCF;AC9MO,SAAS,eAA+C,IAAQ;AACrE,QAAM,cAAc;AAAA,IAClB,CAAA;AAAA,IACA,GAAG,EAAE;AAAA,EAAA;AAEP,QAAM,gBAAgB,OAAO,CAAC,QAAQ;AACpC,UAAM,OAAO,IAAI,IAAI,YAAY,GAAG;AACpC,WAAO,IAAI,OAAO;AAAA,MAChB,KAAK,IAAI,UAAU,GAAG,EAAE,UAAU;AAAA,MAClC,GAAG;AAAA,IAAA,CACJ;AAAA,EACH,CAAC;AACD,QAAM,SAAS,CAAC,aAAa,aAAa;AAC1C,SAAO,MAAM,YAAY;AACzB,SAAO,YAAY,cAAc;AACjC,cAAY,OAAO;AAAA,IACjB,SAAS;AAAA,IACT,aAAa,oBAAoB,EAAE;AAAA,EAAA;AAErC,gBAAc,OAAO;AAAA,IACnB,SAAS;AAAA,IACT,aAAa,kBAAkB,EAAE;AAAA,EAAA;AAGnC,SAAO;AACT;"}
1
+ {"version":3,"file":"index.js","names":["#debounce","#shouldShow","#middleware","#floatingUIOptions","#root","#offset","#shift","#updater","#cleanupAutoUpdate","#shouldAutoUpdate","#updatePosition","#_shouldShow","#onUpdate","#initialized"],"sources":["../src/tooltip-provider.ts","../src/tooltip-plugin.ts"],"sourcesContent":["import type {\n ComputePositionConfig,\n Middleware,\n OffsetOptions,\n ShiftOptions,\n VirtualElement,\n} from '@floating-ui/dom'\nimport type { EditorState } from '@milkdown/prose/state'\nimport type { EditorView } from '@milkdown/prose/view'\n\nimport {\n autoUpdate,\n computePosition,\n flip,\n offset,\n shift,\n} from '@floating-ui/dom'\nimport { posToDOMRect } from '@milkdown/prose'\nimport { TextSelection } from '@milkdown/prose/state'\nimport { throttle } from 'lodash-es'\n\n/// Options for tooltip provider.\nexport interface TooltipProviderOptions {\n /// The tooltip content.\n content: HTMLElement\n /// The debounce time for updating tooltip, 200ms by default.\n debounce?: number\n /// The function to determine whether the tooltip should be shown.\n shouldShow?: (view: EditorView, prevState?: EditorState) => boolean\n /// The offset to get the block. Default is 0.\n offset?: OffsetOptions\n /// The amount to shift options the block by.\n shift?: ShiftOptions\n /// Other middlewares for floating ui. This will be added after the internal middlewares.\n middleware?: Middleware[]\n /// Options for floating ui. If you pass `middleware` or `placement`, it will override the internal settings.\n floatingUIOptions?: Partial<ComputePositionConfig>\n /// The root element that the tooltip will be appended to.\n root?: HTMLElement\n}\n\n/// A provider for creating tooltip.\nexport class TooltipProvider {\n /// @internal\n readonly #debounce: number\n\n /// @internal\n readonly #shouldShow: (view: EditorView, prevState?: EditorState) => boolean\n\n /// @internal\n readonly #middleware: Middleware[]\n\n /// @internal\n readonly #floatingUIOptions: Partial<ComputePositionConfig>\n\n /// @internal\n readonly #root?: HTMLElement\n\n /// @internal\n #initialized = false\n\n /// @internal\n #cleanupAutoUpdate?: () => void\n\n /// @internal\n readonly #offset?: OffsetOptions\n\n /// @internal\n readonly #shift?: ShiftOptions\n\n /// @internal\n readonly #updater: {\n (view: EditorView, prevState?: EditorState): void\n cancel: () => void\n }\n\n /// The root element of the tooltip.\n element: HTMLElement\n\n /// On show callback.\n onShow = () => {}\n\n /// On hide callback.\n onHide = () => {}\n\n constructor(options: TooltipProviderOptions) {\n this.element = options.content\n this.#debounce = options.debounce ?? 200\n this.#shouldShow = options.shouldShow ?? this.#_shouldShow\n this.#offset = options.offset\n this.#shift = options.shift\n this.#middleware = options.middleware ?? []\n this.#floatingUIOptions = options.floatingUIOptions ?? {}\n this.#root = options.root\n this.element.dataset.show = 'false'\n this.#updater = throttle(this.#onUpdate, this.#debounce)\n }\n\n /// @internel\n #updatePosition = (reference: VirtualElement) => {\n computePosition(reference, this.element, {\n placement: this.#floatingUIOptions.placement ?? 'top',\n middleware: [\n flip(),\n offset(this.#offset),\n shift(this.#shift),\n ...this.#middleware,\n ],\n ...this.#floatingUIOptions,\n })\n .then(({ x, y }) => {\n Object.assign(this.element.style, {\n left: `${x}px`,\n top: `${y}px`,\n })\n })\n .catch(console.error)\n }\n\n /// @internal\n #shouldAutoUpdate = (editorView: EditorView) => {\n return this.#root !== editorView.dom.parentElement\n }\n\n /// @internal\n #onUpdate = (view: EditorView, prevState?: EditorState): void => {\n const { state, composing } = view\n const { selection, doc } = state\n const { ranges } = selection\n const from = Math.min(...ranges.map((range) => range.$from.pos))\n const to = Math.max(...ranges.map((range) => range.$to.pos))\n const isSame =\n prevState && prevState.doc.eq(doc) && prevState.selection.eq(selection)\n\n if (!this.#initialized) {\n const root = this.#root ?? view.dom.parentElement ?? document.body\n root.appendChild(this.element)\n this.#initialized = true\n }\n\n if (composing || isSame) return\n\n this.#cleanupAutoUpdate?.()\n this.#cleanupAutoUpdate = void 0\n\n if (!this.#shouldShow(view, prevState)) {\n this.hide()\n return\n }\n\n const virtualEl: VirtualElement = {\n getBoundingClientRect: () => posToDOMRect(view, from, to),\n contextElement: view.dom,\n }\n\n if (this.#shouldAutoUpdate(view)) {\n this.#cleanupAutoUpdate = autoUpdate(virtualEl, this.element, () =>\n this.#updatePosition(virtualEl)\n )\n } else {\n this.#updatePosition(virtualEl)\n }\n\n this.show()\n }\n\n /// Update provider state by editor view.\n update = (view: EditorView, prevState?: EditorState): void => {\n this.#updater(view, prevState)\n }\n\n /// @internal\n #_shouldShow(view: EditorView): boolean {\n const { doc, selection } = view.state\n const { empty, from, to } = selection\n\n const isEmptyTextBlock =\n !doc.textBetween(from, to).length &&\n view.state.selection instanceof TextSelection\n\n const isTooltipChildren = this.element.contains(document.activeElement)\n\n const notHasFocus = !view.hasFocus() && !isTooltipChildren\n\n const isReadonly = !view.editable\n\n if (notHasFocus || empty || isEmptyTextBlock || isReadonly) return false\n\n return true\n }\n\n /// Destroy the tooltip.\n destroy = () => {\n this.#cleanupAutoUpdate?.()\n this.#updater.cancel()\n }\n\n /// Show the tooltip.\n show = (virtualElement?: VirtualElement, editorView?: EditorView) => {\n this.element.dataset.show = 'true'\n\n if (virtualElement) {\n this.#cleanupAutoUpdate?.()\n this.#cleanupAutoUpdate = void 0\n\n const reference = { ...virtualElement, contextElement: editorView?.dom }\n\n if (editorView && this.#shouldAutoUpdate(editorView)) {\n this.#cleanupAutoUpdate = autoUpdate(reference, this.element, () =>\n this.#updatePosition(reference)\n )\n } else {\n this.#updatePosition(reference)\n }\n }\n\n this.onShow()\n }\n\n /// Hide the tooltip.\n hide = () => {\n if (this.element.dataset.show === 'false') return\n this.element.dataset.show = 'false'\n\n this.onHide()\n }\n}\n","import type { SliceType } from '@milkdown/ctx'\nimport type { PluginSpec } from '@milkdown/prose/state'\nimport type { $Ctx, $Prose } from '@milkdown/utils'\n\nimport { Plugin, PluginKey } from '@milkdown/prose/state'\nimport { $ctx, $prose } from '@milkdown/utils'\n\n/// @internal\nexport type TooltipSpecId<Id extends string> = `${Id}_TOOLTIP_SPEC`\n\n/// @internal\nexport type TooltipPlugin<Id extends string, State = any> = [\n $Ctx<PluginSpec<State>, TooltipSpecId<Id>>,\n $Prose,\n] & {\n key: SliceType<PluginSpec<State>, TooltipSpecId<Id>>\n pluginKey: $Prose['key']\n}\n\n/// Create a tooltip plugin with a unique id.\nexport function tooltipFactory<Id extends string, State = any>(id: Id) {\n const tooltipSpec = $ctx<PluginSpec<State>, TooltipSpecId<Id>>(\n {},\n `${id}_TOOLTIP_SPEC`\n )\n const tooltipPlugin = $prose((ctx) => {\n const spec = ctx.get(tooltipSpec.key)\n return new Plugin({\n key: new PluginKey(`${id}_TOOLTIP`),\n ...spec,\n })\n })\n const result = [tooltipSpec, tooltipPlugin] as TooltipPlugin<Id>\n result.key = tooltipSpec.key\n result.pluginKey = tooltipPlugin.key\n tooltipSpec.meta = {\n package: '@milkdown/plugin-tooltip',\n displayName: `Ctx<tooltipSpec>|${id}`,\n }\n tooltipPlugin.meta = {\n package: '@milkdown/plugin-tooltip',\n displayName: `Prose<tooltip>|${id}`,\n }\n\n return result\n}\n"],"mappings":"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"}