@milkdown/plugin-slash 7.4.0 → 7.5.8

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.es.js CHANGED
@@ -1,96 +1,97 @@
1
- var $ = (s, e, t) => {
2
- if (!e.has(s))
3
- throw TypeError("Cannot " + t);
1
+ var H = (s) => {
2
+ throw TypeError(s);
4
3
  };
5
- var n = (s, e, t) => ($(s, e, "read from private field"), t ? t.call(s) : e.get(s)), a = (s, e, t) => {
6
- if (e.has(s))
7
- throw TypeError("Cannot add the same private member more than once");
8
- e instanceof WeakSet ? e.add(s) : e.set(s, t);
9
- }, r = (s, e, t, o) => ($(s, e, "write to private field"), o ? o.call(s, t) : e.set(s, t), t);
10
- var O = (s, e, t) => ($(s, e, "access private method"), t);
11
- import { Plugin as B, PluginKey as M, TextSelection as N } from "@milkdown/prose/state";
12
- import { $ctx as T, $prose as _ } from "@milkdown/utils";
13
- import { findParentNode as F, posToDOMRect as R } from "@milkdown/prose";
4
+ var C = (s, e, t) => e.has(s) || H("Cannot " + t);
5
+ var o = (s, e, t) => (C(s, e, "read from private field"), t ? t.call(s) : e.get(s)), i = (s, e, t) => e.has(s) ? H("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(s) : e.set(s, t), a = (s, e, t, n) => (C(s, e, "write to private field"), n ? n.call(s, t) : e.set(s, t), t), A = (s, e, t) => (C(s, e, "access private method"), t);
6
+ import { Plugin as N, PluginKey as T, TextSelection as _ } from "@milkdown/prose/state";
7
+ import { $ctx as F, $prose as I } from "@milkdown/utils";
8
+ import { findParentNode as R, posToDOMRect as U } from "@milkdown/prose";
14
9
  import q from "lodash.debounce";
15
- import { computePosition as K, flip as L, offset as j, platform as E } from "@floating-ui/dom";
16
- import { offsetParent as z } from "composed-offset-position";
10
+ import { computePosition as K, flip as L, offset as j } from "@floating-ui/dom";
17
11
  function W(s) {
18
- const e = T({}, `${s}_SLASH_SPEC`), t = _((i) => {
19
- const p = i.get(e.key);
20
- return new B({
21
- key: new M(`${s}_SLASH`),
22
- ...p
12
+ const e = F(
13
+ {},
14
+ `${s}_SLASH_SPEC`
15
+ ), t = I((r) => {
16
+ const m = r.get(e.key);
17
+ return new N({
18
+ key: new T(`${s}_SLASH`),
19
+ ...m
23
20
  });
24
- }), o = [e, t];
25
- return o.key = e.key, o.pluginKey = t.key, e.meta = {
21
+ }), n = [e, t];
22
+ return n.key = e.key, n.pluginKey = t.key, e.meta = {
26
23
  package: "@milkdown/plugin-slash",
27
24
  displayName: `Ctx<slashSpec>|${s}`
28
25
  }, t.meta = {
29
26
  package: "@milkdown/plugin-slash",
30
27
  displayName: `Prose<slash>|${s}`
31
- }, o;
28
+ }, n;
32
29
  }
33
- var m, f, l, u, d, g, S, H;
30
+ var c, d, u, f, h, g, y, S, x, B;
34
31
  class X {
35
32
  constructor(e) {
33
+ i(this, x);
34
+ i(this, c);
36
35
  /// @internal
37
- a(this, S);
38
- a(this, m, void 0);
36
+ i(this, d);
39
37
  /// @internal
40
- a(this, f, void 0);
38
+ i(this, u);
41
39
  /// @internal
42
- a(this, l, void 0);
40
+ i(this, f);
43
41
  /// @internal
44
- a(this, u, void 0);
42
+ i(this, h);
43
+ /// @internal
44
+ i(this, g);
45
45
  /// The offset to get the block. Default is 0.
46
- a(this, d, void 0);
47
- a(this, g, void 0);
48
- r(this, m, !1), this.onShow = () => {
46
+ i(this, y);
47
+ i(this, S);
48
+ a(this, c, !1), this.onShow = () => {
49
49
  }, this.onHide = () => {
50
- }, r(this, g, (t, o) => {
51
- var b;
52
- const { state: i, composing: p } = t, { selection: c, doc: P } = i, { ranges: y } = c, k = Math.min(...y.map((h) => h.$from.pos)), x = Math.max(...y.map((h) => h.$to.pos)), w = o && o.doc.eq(P) && o.selection.eq(c);
53
- if (n(this, m) || ((b = t.dom.parentElement) == null || b.appendChild(this.element), r(this, m, !0)), p || w)
54
- return;
55
- if (!n(this, u).call(this, t, o)) {
50
+ }, a(this, S, (t, n) => {
51
+ var E;
52
+ const { state: r, composing: m } = t, { selection: l, doc: $ } = r, { ranges: k } = l, w = Math.min(...k.map((p) => p.$from.pos)), P = Math.max(...k.map((p) => p.$to.pos)), b = n && n.doc.eq($) && n.selection.eq(l);
53
+ if (o(this, c) || ((E = t.dom.parentElement) == null || E.appendChild(this.element), a(this, c, !0)), m || b) return;
54
+ if (!o(this, g).call(this, t, n)) {
56
55
  this.hide();
57
56
  return;
58
57
  }
59
58
  K({
60
- getBoundingClientRect: () => R(t, k, x)
59
+ getBoundingClientRect: () => U(t, w, P)
61
60
  }, this.element, {
62
61
  placement: "bottom-start",
63
- middleware: [L(), j(n(this, d))],
64
- platform: {
65
- ...E,
66
- getOffsetParent: (h) => E.getOffsetParent(h, z)
67
- }
68
- }).then(({ x: h, y: A }) => {
62
+ middleware: [L(), j(o(this, y)), ...o(this, d)],
63
+ ...o(this, u)
64
+ }).then(({ x: p, y: M }) => {
69
65
  Object.assign(this.element.style, {
70
- left: `${h}px`,
71
- top: `${A}px`
66
+ left: `${p}px`,
67
+ top: `${M}px`
72
68
  });
73
69
  }), this.show();
74
- }), this.update = (t, o) => {
75
- q(n(this, g), n(this, f))(t, o);
76
- }, this.getContent = (t, o = (i) => i.type.name === "paragraph") => {
77
- const { selection: i } = t.state, { empty: p, $from: c } = i, P = t.state.selection instanceof N, y = this.element.contains(document.activeElement), k = !t.hasFocus() && !y, x = !t.editable, C = !F(o)(t.state.selection);
78
- if (!(k || x || !p || !P || C))
79
- return c.parent.textBetween(Math.max(0, c.parentOffset - 500), c.parentOffset, void 0, "");
70
+ }), this.update = (t, n) => {
71
+ q(o(this, S), o(this, f))(t, n);
72
+ }, this.getContent = (t, n = (r) => r.type.name === "paragraph") => {
73
+ const { selection: r } = t.state, { empty: m, $from: l } = r, $ = t.state.selection instanceof _, k = this.element.contains(document.activeElement), w = !t.hasFocus() && !k, P = !t.editable, O = !R(n)(t.state.selection);
74
+ if (!(w || P || !m || !$ || O))
75
+ return l.parent.textBetween(
76
+ Math.max(0, l.parentOffset - 500),
77
+ l.parentOffset,
78
+ void 0,
79
+ ""
80
+ );
80
81
  }, this.destroy = () => {
81
82
  }, this.show = () => {
82
83
  this.element.dataset.show = "true", this.onShow();
83
84
  }, this.hide = () => {
84
85
  this.element.dataset.show = "false", this.onHide();
85
- }, this.element = e.content, r(this, f, e.debounce ?? 200), r(this, u, e.shouldShow ?? O(this, S, H)), r(this, l, e.trigger ?? "/"), r(this, d, e.offset);
86
+ }, this.element = e.content, a(this, f, e.debounce ?? 200), a(this, g, e.shouldShow ?? A(this, x, B)), a(this, h, e.trigger ?? "/"), a(this, y, e.offset), a(this, d, e.middleware ?? []), a(this, u, e.floatingUIOptions ?? {});
86
87
  }
87
88
  }
88
- m = new WeakMap(), f = new WeakMap(), l = new WeakMap(), u = new WeakMap(), d = new WeakMap(), g = new WeakMap(), S = new WeakSet(), H = function(e) {
89
+ c = new WeakMap(), d = new WeakMap(), u = new WeakMap(), f = new WeakMap(), h = new WeakMap(), g = new WeakMap(), y = new WeakMap(), S = new WeakMap(), x = new WeakSet(), /// @internal
90
+ B = function(e) {
89
91
  const t = this.getContent(e);
90
- if (!t)
91
- return !1;
92
- const o = t.at(-1);
93
- return o ? Array.isArray(n(this, l)) ? n(this, l).includes(o) : n(this, l) === o : !1;
92
+ if (!t) return !1;
93
+ const n = t.at(-1);
94
+ return n ? Array.isArray(o(this, h)) ? o(this, h).includes(n) : o(this, h) === n : !1;
94
95
  };
95
96
  export {
96
97
  X as SlashProvider,
@@ -1 +1 @@
1
- {"version":3,"file":"index.es.js","sources":["../src/slash-plugin.ts","../src/slash-provider.ts"],"sourcesContent":["import type { SliceType } from '@milkdown/ctx'\nimport type { PluginSpec } from '@milkdown/prose/state'\nimport { Plugin, PluginKey } from '@milkdown/prose/state'\nimport type { $Ctx, $Prose } from '@milkdown/utils'\nimport { $ctx, $prose } from '@milkdown/utils'\n\n/// @internal\nexport type SlashPluginSpecId<Id extends string> = `${Id}_SLASH_SPEC`\n\n/// @internal\nexport type SlashPlugin<Id extends string, State = any> = [$Ctx<PluginSpec<State>, SlashPluginSpecId<Id>>, $Prose] & {\n key: SliceType<PluginSpec<State>, SlashPluginSpecId<Id>>\n pluginKey: $Prose['key']\n}\n\n/// Create a slash plugin with a unique id.\nexport function slashFactory<Id extends string, State = any>(id: Id) {\n const slashSpec = $ctx<PluginSpec<State>, SlashPluginSpecId<Id>>({}, `${id}_SLASH_SPEC`)\n const slashPlugin = $prose((ctx) => {\n const spec = ctx.get(slashSpec.key)\n return new Plugin({\n key: new PluginKey(`${id}_SLASH`),\n ...spec,\n })\n })\n const result = [slashSpec, slashPlugin] as SlashPlugin<Id>\n result.key = slashSpec.key\n result.pluginKey = slashPlugin.key\n slashSpec.meta = {\n package: '@milkdown/plugin-slash',\n displayName: `Ctx<slashSpec>|${id}`,\n }\n slashPlugin.meta = {\n package: '@milkdown/plugin-slash',\n displayName: `Prose<slash>|${id}`,\n }\n\n return result\n}\n","import { findParentNode, posToDOMRect } from '@milkdown/prose'\nimport type { EditorState } from '@milkdown/prose/state'\nimport type { Node } from '@milkdown/prose/model'\nimport { TextSelection } from '@milkdown/prose/state'\nimport type { EditorView } from '@milkdown/prose/view'\nimport debounce from 'lodash.debounce'\nimport type { VirtualElement } from '@floating-ui/dom'\nimport { computePosition, flip, offset, platform } from '@floating-ui/dom'\nimport { offsetParent } from 'composed-offset-position'\n\n/// Options for slash provider.\nexport interface SlashProviderOptions {\n /// The slash content.\n content: HTMLElement\n /// The debounce time for updating slash, 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 key trigger for shouldShow, '/' by default.\n trigger?: string | string[]\n /// The offset to get the block. Default is 0.\n offset?: number | {\n mainAxis?: number\n crossAxis?: number\n alignmentAxis?: number | null\n }\n}\n\n/// A provider for creating slash.\nexport class SlashProvider {\n /// The root element of the slash.\n element: HTMLElement\n\n /// @internal\n #initialized = false\n\n /// @internal\n readonly #debounce: number\n\n /// @internal\n readonly #trigger: string | string[]\n\n /// @internal\n readonly #shouldShow: (view: EditorView, prevState?: EditorState) => boolean\n\n /// The offset to get the block. Default is 0.\n readonly #offset?: number | {\n mainAxis?: number\n crossAxis?: number\n alignmentAxis?: number | null\n }\n\n /// On show callback.\n onShow = () => {}\n\n /// On hide callback.\n onHide = () => {}\n\n constructor(options: SlashProviderOptions) {\n this.element = options.content\n this.#debounce = options.debounce ?? 200\n this.#shouldShow = options.shouldShow ?? this.#_shouldShow\n this.#trigger = options.trigger ?? '/'\n this.#offset = options.offset\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 = prevState && prevState.doc.eq(doc) && prevState.selection.eq(selection)\n\n if (!this.#initialized) {\n view.dom.parentElement?.appendChild(this.element)\n this.#initialized = true\n }\n\n if (composing || isSame)\n return\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 }\n computePosition(virtualEl, this.element, {\n placement: 'bottom-start',\n middleware: [flip(), offset(this.#offset)],\n platform: {\n ...platform,\n getOffsetParent: element =>\n platform.getOffsetParent(element, offsetParent),\n },\n })\n .then(({ x, y }) => {\n Object.assign(this.element.style, {\n left: `${x}px`,\n top: `${y}px`,\n })\n })\n\n this.show()\n }\n\n /// @internal\n #_shouldShow(view: EditorView): boolean {\n const currentTextBlockContent = this.getContent(view)\n\n if (!currentTextBlockContent)\n return false\n\n const target = currentTextBlockContent.at(-1)\n\n if (!target)\n return false\n\n return Array.isArray(this.#trigger) ? this.#trigger.includes(target) : this.#trigger === target\n }\n\n /// Update provider state by editor view.\n update = (view: EditorView, prevState?: EditorState): void => {\n const updater = debounce(this.#onUpdate, this.#debounce)\n\n updater(view, prevState)\n }\n\n /// Get the content of the current text block.\n /// Pass the `matchNode` function to determine whether the current node should be matched, by default, it will match the paragraph node.\n getContent = (view: EditorView, matchNode: (node: Node) => boolean = node => node.type.name === 'paragraph'): string | undefined => {\n const { selection } = view.state\n const { empty, $from } = selection\n const isTextBlock = view.state.selection instanceof TextSelection\n\n const isSlashChildren = this.element.contains(document.activeElement)\n\n const notHasFocus = !view.hasFocus() && !isSlashChildren\n\n const isReadonly = !view.editable\n\n const paragraph = findParentNode(matchNode)(view.state.selection)\n\n const isNotInParagraph = !paragraph\n\n if (notHasFocus || isReadonly || !empty || !isTextBlock || isNotInParagraph)\n return\n\n return $from.parent.textBetween(Math.max(0, $from.parentOffset - 500), $from.parentOffset, undefined, '\\uFFFC')\n }\n\n /// Destroy the slash.\n destroy = () => {\n }\n\n /// Show the slash.\n show = () => {\n this.element.dataset.show = 'true'\n this.onShow()\n }\n\n /// Hide the slash.\n hide = () => {\n this.element.dataset.show = 'false'\n this.onHide()\n }\n}\n"],"names":["slashFactory","id","slashSpec","$ctx","slashPlugin","$prose","ctx","spec","Plugin","PluginKey","result","SlashProvider","options","__privateAdd","__shouldShow","_initialized","_debounce","_trigger","_shouldShow","_offset","_onUpdate","__privateSet","view","prevState","state","composing","selection","doc","ranges","from","range","to","isSame","__privateGet","_a","computePosition","posToDOMRect","flip","offset","platform","element","offsetParent","x","y","debounce","matchNode","node","empty","$from","isTextBlock","TextSelection","isSlashChildren","notHasFocus","isReadonly","isNotInParagraph","findParentNode","__privateMethod","_shouldShow_fn","currentTextBlockContent","target"],"mappings":"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"}
1
+ {"version":3,"file":"index.es.js","sources":["../src/slash-plugin.ts","../src/slash-provider.ts"],"sourcesContent":["import type { SliceType } from '@milkdown/ctx'\nimport type { PluginSpec } from '@milkdown/prose/state'\nimport { Plugin, PluginKey } from '@milkdown/prose/state'\nimport type { $Ctx, $Prose } from '@milkdown/utils'\nimport { $ctx, $prose } from '@milkdown/utils'\n\n/// @internal\nexport type SlashPluginSpecId<Id extends string> = `${Id}_SLASH_SPEC`\n\n/// @internal\nexport type SlashPlugin<Id extends string, State = any> = [\n $Ctx<PluginSpec<State>, SlashPluginSpecId<Id>>,\n $Prose,\n] & {\n key: SliceType<PluginSpec<State>, SlashPluginSpecId<Id>>\n pluginKey: $Prose['key']\n}\n\n/// Create a slash plugin with a unique id.\nexport function slashFactory<Id extends string, State = any>(id: Id) {\n const slashSpec = $ctx<PluginSpec<State>, SlashPluginSpecId<Id>>(\n {},\n `${id}_SLASH_SPEC`\n )\n const slashPlugin = $prose((ctx) => {\n const spec = ctx.get(slashSpec.key)\n return new Plugin({\n key: new PluginKey(`${id}_SLASH`),\n ...spec,\n })\n })\n const result = [slashSpec, slashPlugin] as SlashPlugin<Id>\n result.key = slashSpec.key\n result.pluginKey = slashPlugin.key\n slashSpec.meta = {\n package: '@milkdown/plugin-slash',\n displayName: `Ctx<slashSpec>|${id}`,\n }\n slashPlugin.meta = {\n package: '@milkdown/plugin-slash',\n displayName: `Prose<slash>|${id}`,\n }\n\n return result\n}\n","import { findParentNode, posToDOMRect } from '@milkdown/prose'\nimport type { EditorState } from '@milkdown/prose/state'\nimport type { Node } from '@milkdown/prose/model'\nimport { TextSelection } from '@milkdown/prose/state'\nimport type { EditorView } from '@milkdown/prose/view'\nimport debounce from 'lodash.debounce'\nimport type {\n ComputePositionConfig,\n Middleware,\n VirtualElement,\n} from '@floating-ui/dom'\nimport { computePosition, flip, offset } from '@floating-ui/dom'\n\n/// Options for slash provider.\nexport interface SlashProviderOptions {\n /// The slash content.\n content: HTMLElement\n /// The debounce time for updating slash, 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 key trigger for shouldShow, '/' by default.\n trigger?: string | string[]\n /// The offset to get the block. Default is 0.\n offset?:\n | number\n | {\n mainAxis?: number\n crossAxis?: number\n alignmentAxis?: number | null\n }\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}\n\n/// A provider for creating slash.\nexport class SlashProvider {\n /// The root element of the slash.\n element: HTMLElement\n\n /// @internal\n #initialized = false\n\n /// @internal\n readonly #middleware: Middleware[]\n\n /// @internal\n readonly #floatingUIOptions: Partial<ComputePositionConfig>\n\n /// @internal\n readonly #debounce: number\n\n /// @internal\n readonly #trigger: string | string[]\n\n /// @internal\n readonly #shouldShow: (view: EditorView, prevState?: EditorState) => boolean\n\n /// The offset to get the block. Default is 0.\n readonly #offset?:\n | number\n | {\n mainAxis?: number\n crossAxis?: number\n alignmentAxis?: number | null\n }\n\n /// On show callback.\n onShow = () => {}\n\n /// On hide callback.\n onHide = () => {}\n\n constructor(options: SlashProviderOptions) {\n this.element = options.content\n this.#debounce = options.debounce ?? 200\n this.#shouldShow = options.shouldShow ?? this.#_shouldShow\n this.#trigger = options.trigger ?? '/'\n this.#offset = options.offset\n this.#middleware = options.middleware ?? []\n this.#floatingUIOptions = options.floatingUIOptions ?? {}\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 view.dom.parentElement?.appendChild(this.element)\n this.#initialized = true\n }\n\n if (composing || isSame) return\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 }\n computePosition(virtualEl, this.element, {\n placement: 'bottom-start',\n middleware: [flip(), offset(this.#offset), ...this.#middleware],\n ...this.#floatingUIOptions,\n }).then(({ x, y }) => {\n Object.assign(this.element.style, {\n left: `${x}px`,\n top: `${y}px`,\n })\n })\n\n this.show()\n }\n\n /// @internal\n #_shouldShow(view: EditorView): boolean {\n const currentTextBlockContent = this.getContent(view)\n\n if (!currentTextBlockContent) return false\n\n const target = currentTextBlockContent.at(-1)\n\n if (!target) return false\n\n return Array.isArray(this.#trigger)\n ? this.#trigger.includes(target)\n : this.#trigger === target\n }\n\n /// Update provider state by editor view.\n update = (view: EditorView, prevState?: EditorState): void => {\n const updater = debounce(this.#onUpdate, this.#debounce)\n\n updater(view, prevState)\n }\n\n /// Get the content of the current text block.\n /// Pass the `matchNode` function to determine whether the current node should be matched, by default, it will match the paragraph node.\n getContent = (\n view: EditorView,\n matchNode: (node: Node) => boolean = (node) =>\n node.type.name === 'paragraph'\n ): string | undefined => {\n const { selection } = view.state\n const { empty, $from } = selection\n const isTextBlock = view.state.selection instanceof TextSelection\n\n const isSlashChildren = this.element.contains(document.activeElement)\n\n const notHasFocus = !view.hasFocus() && !isSlashChildren\n\n const isReadonly = !view.editable\n\n const paragraph = findParentNode(matchNode)(view.state.selection)\n\n const isNotInParagraph = !paragraph\n\n if (notHasFocus || isReadonly || !empty || !isTextBlock || isNotInParagraph)\n return\n\n return $from.parent.textBetween(\n Math.max(0, $from.parentOffset - 500),\n $from.parentOffset,\n undefined,\n '\\uFFFC'\n )\n }\n\n /// Destroy the slash.\n destroy = () => {}\n\n /// Show the slash.\n show = () => {\n this.element.dataset.show = 'true'\n this.onShow()\n }\n\n /// Hide the slash.\n hide = () => {\n this.element.dataset.show = 'false'\n this.onHide()\n }\n}\n"],"names":["slashFactory","id","slashSpec","$ctx","slashPlugin","$prose","ctx","spec","Plugin","PluginKey","result","SlashProvider","options","__privateAdd","_SlashProvider_instances","_initialized","_middleware","_floatingUIOptions","_debounce","_trigger","_shouldShow","_offset","_onUpdate","__privateSet","view","prevState","state","composing","selection","doc","ranges","from","range","to","isSame","__privateGet","_a","computePosition","posToDOMRect","flip","offset","x","y","debounce","matchNode","node","empty","$from","isTextBlock","TextSelection","isSlashChildren","notHasFocus","isReadonly","isNotInParagraph","findParentNode","__privateMethod","_shouldShow_fn","currentTextBlockContent","target"],"mappings":"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"}
@@ -2,7 +2,10 @@ import type { SliceType } from '@milkdown/ctx';
2
2
  import type { PluginSpec } from '@milkdown/prose/state';
3
3
  import type { $Ctx, $Prose } from '@milkdown/utils';
4
4
  export type SlashPluginSpecId<Id extends string> = `${Id}_SLASH_SPEC`;
5
- export type SlashPlugin<Id extends string, State = any> = [$Ctx<PluginSpec<State>, SlashPluginSpecId<Id>>, $Prose] & {
5
+ export type SlashPlugin<Id extends string, State = any> = [
6
+ $Ctx<PluginSpec<State>, SlashPluginSpecId<Id>>,
7
+ $Prose
8
+ ] & {
6
9
  key: SliceType<PluginSpec<State>, SlashPluginSpecId<Id>>;
7
10
  pluginKey: $Prose['key'];
8
11
  };
@@ -1 +1 @@
1
- {"version":3,"file":"slash-plugin.d.ts","sourceRoot":"","sources":["../src/slash-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAEvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAInD,MAAM,MAAM,iBAAiB,CAAC,EAAE,SAAS,MAAM,IAAI,GAAG,EAAE,aAAa,CAAA;AAGrE,MAAM,MAAM,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,KAAK,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG;IACnH,GAAG,EAAE,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAA;IACxD,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;CACzB,CAAA;AAGD,wBAAgB,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,KAAK,GAAG,GAAG,EAAE,EAAE,EAAE,EAAE,wBAsBlE"}
1
+ {"version":3,"file":"slash-plugin.d.ts","sourceRoot":"","sources":["../src/slash-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAEvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAInD,MAAM,MAAM,iBAAiB,CAAC,EAAE,SAAS,MAAM,IAAI,GAAG,EAAE,aAAa,CAAA;AAGrE,MAAM,MAAM,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,KAAK,GAAG,GAAG,IAAI;IACxD,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC,EAAE,CAAC,CAAC;IAC9C,MAAM;CACP,GAAG;IACF,GAAG,EAAE,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAA;IACxD,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;CACzB,CAAA;AAGD,wBAAgB,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,KAAK,GAAG,GAAG,EAAE,EAAE,EAAE,EAAE,wBAyBlE"}
@@ -1,6 +1,7 @@
1
1
  import type { EditorState } from '@milkdown/prose/state';
2
2
  import type { Node } from '@milkdown/prose/model';
3
3
  import type { EditorView } from '@milkdown/prose/view';
4
+ import type { ComputePositionConfig, Middleware } from '@floating-ui/dom';
4
5
  export interface SlashProviderOptions {
5
6
  content: HTMLElement;
6
7
  debounce?: number;
@@ -11,6 +12,8 @@ export interface SlashProviderOptions {
11
12
  crossAxis?: number;
12
13
  alignmentAxis?: number | null;
13
14
  };
15
+ middleware?: Middleware[];
16
+ floatingUIOptions?: Partial<ComputePositionConfig>;
14
17
  }
15
18
  export declare class SlashProvider {
16
19
  #private;
@@ -1 +1 @@
1
- {"version":3,"file":"slash-provider.d.ts","sourceRoot":"","sources":["../src/slash-provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAA;AAEjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAOtD,MAAM,WAAW,oBAAoB;IAEnC,OAAO,EAAE,WAAW,CAAA;IAEpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,WAAW,KAAK,OAAO,CAAA;IAEnE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAE3B,MAAM,CAAC,EAAE,MAAM,GAAG;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAC9B,CAAA;CACF;AAGD,qBAAa,aAAa;;IAExB,OAAO,EAAE,WAAW,CAAA;IAsBpB,MAAM,aAAW;IAGjB,MAAM,aAAW;gBAEL,OAAO,EAAE,oBAAoB;IAoEzC,MAAM,SAAU,UAAU,cAAc,WAAW,KAAG,IAAI,CAIzD;IAID,UAAU,SAAU,UAAU,cAAa,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,KAA4C,MAAM,GAAG,SAAS,CAmB/H;IAGD,OAAO,aACN;IAGD,IAAI,aAGH;IAGD,IAAI,aAGH;CACF"}
1
+ {"version":3,"file":"slash-provider.d.ts","sourceRoot":"","sources":["../src/slash-provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAA;AAEjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEtD,OAAO,KAAK,EACV,qBAAqB,EACrB,UAAU,EAEX,MAAM,kBAAkB,CAAA;AAIzB,MAAM,WAAW,oBAAoB;IAEnC,OAAO,EAAE,WAAW,CAAA;IAEpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,WAAW,KAAK,OAAO,CAAA;IAEnE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAE3B,MAAM,CAAC,EACH,MAAM,GACN;QACE,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAC9B,CAAA;IAEL,UAAU,CAAC,EAAE,UAAU,EAAE,CAAA;IAEzB,iBAAiB,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAA;CACnD;AAGD,qBAAa,aAAa;;IAExB,OAAO,EAAE,WAAW,CAAA;IA8BpB,MAAM,aAAW;IAGjB,MAAM,aAAW;gBAEL,OAAO,EAAE,oBAAoB;IAiEzC,MAAM,SAAU,UAAU,cAAc,WAAW,KAAG,IAAI,CAIzD;IAID,UAAU,SACF,UAAU,cACL,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,KAEjC,MAAM,GAAG,SAAS,CAwBpB;IAGD,OAAO,aAAW;IAGlB,IAAI,aAGH;IAGD,IAAI,aAGH;CACF"}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@milkdown/plugin-slash",
3
3
  "type": "module",
4
- "version": "7.4.0",
4
+ "version": "7.5.8",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/Milkdown/milkdown.git",
9
- "directory": "packages/plugin-slash"
9
+ "directory": "packages/plugins/plugin-slash"
10
10
  },
11
11
  "keywords": [
12
12
  "milkdown",
@@ -32,16 +32,15 @@
32
32
  "dependencies": {
33
33
  "@floating-ui/dom": "^1.5.1",
34
34
  "@types/lodash.debounce": "^4.0.7",
35
- "composed-offset-position": "^0.0.4",
36
35
  "lodash.debounce": "^4.0.8",
37
36
  "tslib": "^2.5.0",
38
- "@milkdown/exception": "7.4.0",
39
- "@milkdown/utils": "7.4.0"
37
+ "@milkdown/exception": "7.5.8",
38
+ "@milkdown/utils": "7.5.8"
40
39
  },
41
40
  "devDependencies": {
42
- "@milkdown/core": "7.4.0",
43
- "@milkdown/ctx": "7.4.0",
44
- "@milkdown/prose": "7.4.0"
41
+ "@milkdown/core": "7.5.8",
42
+ "@milkdown/ctx": "7.5.8",
43
+ "@milkdown/prose": "7.5.8"
45
44
  },
46
45
  "nx": {
47
46
  "targets": {
@@ -50,19 +49,13 @@
50
49
  "{projectRoot}/lib"
51
50
  ],
52
51
  "dependsOn": [
53
- {
54
- "target": "build",
55
- "projects": "dependencies"
56
- }
52
+ "build"
57
53
  ]
58
54
  },
59
55
  "tsc": {
60
56
  "outputs": [],
61
57
  "dependsOn": [
62
- {
63
- "target": "build",
64
- "projects": "dependencies"
65
- }
58
+ "build"
66
59
  ]
67
60
  }
68
61
  }
@@ -8,14 +8,20 @@ import { $ctx, $prose } from '@milkdown/utils'
8
8
  export type SlashPluginSpecId<Id extends string> = `${Id}_SLASH_SPEC`
9
9
 
10
10
  /// @internal
11
- export type SlashPlugin<Id extends string, State = any> = [$Ctx<PluginSpec<State>, SlashPluginSpecId<Id>>, $Prose] & {
11
+ export type SlashPlugin<Id extends string, State = any> = [
12
+ $Ctx<PluginSpec<State>, SlashPluginSpecId<Id>>,
13
+ $Prose,
14
+ ] & {
12
15
  key: SliceType<PluginSpec<State>, SlashPluginSpecId<Id>>
13
16
  pluginKey: $Prose['key']
14
17
  }
15
18
 
16
19
  /// Create a slash plugin with a unique id.
17
20
  export function slashFactory<Id extends string, State = any>(id: Id) {
18
- const slashSpec = $ctx<PluginSpec<State>, SlashPluginSpecId<Id>>({}, `${id}_SLASH_SPEC`)
21
+ const slashSpec = $ctx<PluginSpec<State>, SlashPluginSpecId<Id>>(
22
+ {},
23
+ `${id}_SLASH_SPEC`
24
+ )
19
25
  const slashPlugin = $prose((ctx) => {
20
26
  const spec = ctx.get(slashSpec.key)
21
27
  return new Plugin({
@@ -4,9 +4,12 @@ import type { Node } from '@milkdown/prose/model'
4
4
  import { TextSelection } from '@milkdown/prose/state'
5
5
  import type { EditorView } from '@milkdown/prose/view'
6
6
  import debounce from 'lodash.debounce'
7
- import type { VirtualElement } from '@floating-ui/dom'
8
- import { computePosition, flip, offset, platform } from '@floating-ui/dom'
9
- import { offsetParent } from 'composed-offset-position'
7
+ import type {
8
+ ComputePositionConfig,
9
+ Middleware,
10
+ VirtualElement,
11
+ } from '@floating-ui/dom'
12
+ import { computePosition, flip, offset } from '@floating-ui/dom'
10
13
 
11
14
  /// Options for slash provider.
12
15
  export interface SlashProviderOptions {
@@ -19,11 +22,17 @@ export interface SlashProviderOptions {
19
22
  /// The key trigger for shouldShow, '/' by default.
20
23
  trigger?: string | string[]
21
24
  /// The offset to get the block. Default is 0.
22
- offset?: number | {
23
- mainAxis?: number
24
- crossAxis?: number
25
- alignmentAxis?: number | null
26
- }
25
+ offset?:
26
+ | number
27
+ | {
28
+ mainAxis?: number
29
+ crossAxis?: number
30
+ alignmentAxis?: number | null
31
+ }
32
+ /// Other middlewares for floating ui. This will be added after the internal middlewares.
33
+ middleware?: Middleware[]
34
+ /// Options for floating ui. If you pass `middleware` or `placement`, it will override the internal settings.
35
+ floatingUIOptions?: Partial<ComputePositionConfig>
27
36
  }
28
37
 
29
38
  /// A provider for creating slash.
@@ -34,6 +43,12 @@ export class SlashProvider {
34
43
  /// @internal
35
44
  #initialized = false
36
45
 
46
+ /// @internal
47
+ readonly #middleware: Middleware[]
48
+
49
+ /// @internal
50
+ readonly #floatingUIOptions: Partial<ComputePositionConfig>
51
+
37
52
  /// @internal
38
53
  readonly #debounce: number
39
54
 
@@ -44,11 +59,13 @@ export class SlashProvider {
44
59
  readonly #shouldShow: (view: EditorView, prevState?: EditorState) => boolean
45
60
 
46
61
  /// The offset to get the block. Default is 0.
47
- readonly #offset?: number | {
48
- mainAxis?: number
49
- crossAxis?: number
50
- alignmentAxis?: number | null
51
- }
62
+ readonly #offset?:
63
+ | number
64
+ | {
65
+ mainAxis?: number
66
+ crossAxis?: number
67
+ alignmentAxis?: number | null
68
+ }
52
69
 
53
70
  /// On show callback.
54
71
  onShow = () => {}
@@ -62,6 +79,8 @@ export class SlashProvider {
62
79
  this.#shouldShow = options.shouldShow ?? this.#_shouldShow
63
80
  this.#trigger = options.trigger ?? '/'
64
81
  this.#offset = options.offset
82
+ this.#middleware = options.middleware ?? []
83
+ this.#floatingUIOptions = options.floatingUIOptions ?? {}
65
84
  }
66
85
 
67
86
  /// @internal
@@ -69,17 +88,17 @@ export class SlashProvider {
69
88
  const { state, composing } = view
70
89
  const { selection, doc } = state
71
90
  const { ranges } = selection
72
- const from = Math.min(...ranges.map(range => range.$from.pos))
73
- const to = Math.max(...ranges.map(range => range.$to.pos))
74
- const isSame = prevState && prevState.doc.eq(doc) && prevState.selection.eq(selection)
91
+ const from = Math.min(...ranges.map((range) => range.$from.pos))
92
+ const to = Math.max(...ranges.map((range) => range.$to.pos))
93
+ const isSame =
94
+ prevState && prevState.doc.eq(doc) && prevState.selection.eq(selection)
75
95
 
76
96
  if (!this.#initialized) {
77
97
  view.dom.parentElement?.appendChild(this.element)
78
98
  this.#initialized = true
79
99
  }
80
100
 
81
- if (composing || isSame)
82
- return
101
+ if (composing || isSame) return
83
102
 
84
103
  if (!this.#shouldShow(view, prevState)) {
85
104
  this.hide()
@@ -91,19 +110,14 @@ export class SlashProvider {
91
110
  }
92
111
  computePosition(virtualEl, this.element, {
93
112
  placement: 'bottom-start',
94
- middleware: [flip(), offset(this.#offset)],
95
- platform: {
96
- ...platform,
97
- getOffsetParent: element =>
98
- platform.getOffsetParent(element, offsetParent),
99
- },
100
- })
101
- .then(({ x, y }) => {
102
- Object.assign(this.element.style, {
103
- left: `${x}px`,
104
- top: `${y}px`,
105
- })
113
+ middleware: [flip(), offset(this.#offset), ...this.#middleware],
114
+ ...this.#floatingUIOptions,
115
+ }).then(({ x, y }) => {
116
+ Object.assign(this.element.style, {
117
+ left: `${x}px`,
118
+ top: `${y}px`,
106
119
  })
120
+ })
107
121
 
108
122
  this.show()
109
123
  }
@@ -112,15 +126,15 @@ export class SlashProvider {
112
126
  #_shouldShow(view: EditorView): boolean {
113
127
  const currentTextBlockContent = this.getContent(view)
114
128
 
115
- if (!currentTextBlockContent)
116
- return false
129
+ if (!currentTextBlockContent) return false
117
130
 
118
131
  const target = currentTextBlockContent.at(-1)
119
132
 
120
- if (!target)
121
- return false
133
+ if (!target) return false
122
134
 
123
- return Array.isArray(this.#trigger) ? this.#trigger.includes(target) : this.#trigger === target
135
+ return Array.isArray(this.#trigger)
136
+ ? this.#trigger.includes(target)
137
+ : this.#trigger === target
124
138
  }
125
139
 
126
140
  /// Update provider state by editor view.
@@ -132,7 +146,11 @@ export class SlashProvider {
132
146
 
133
147
  /// Get the content of the current text block.
134
148
  /// Pass the `matchNode` function to determine whether the current node should be matched, by default, it will match the paragraph node.
135
- getContent = (view: EditorView, matchNode: (node: Node) => boolean = node => node.type.name === 'paragraph'): string | undefined => {
149
+ getContent = (
150
+ view: EditorView,
151
+ matchNode: (node: Node) => boolean = (node) =>
152
+ node.type.name === 'paragraph'
153
+ ): string | undefined => {
136
154
  const { selection } = view.state
137
155
  const { empty, $from } = selection
138
156
  const isTextBlock = view.state.selection instanceof TextSelection
@@ -150,12 +168,16 @@ export class SlashProvider {
150
168
  if (notHasFocus || isReadonly || !empty || !isTextBlock || isNotInParagraph)
151
169
  return
152
170
 
153
- return $from.parent.textBetween(Math.max(0, $from.parentOffset - 500), $from.parentOffset, undefined, '\uFFFC')
171
+ return $from.parent.textBetween(
172
+ Math.max(0, $from.parentOffset - 500),
173
+ $from.parentOffset,
174
+ undefined,
175
+ '\uFFFC'
176
+ )
154
177
  }
155
178
 
156
179
  /// Destroy the slash.
157
- destroy = () => {
158
- }
180
+ destroy = () => {}
159
181
 
160
182
  /// Show the slash.
161
183
  show = () => {