@lofcz/platejs-tabbable 52.0.11

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/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) Ziad Beyens, Dylan Schiemann, Joe Anderson, Felix Feng
4
+
5
+ Unless otherwise specified in a LICENSE file within an individual package directory,
6
+ this license applies to all files in this repository outside of those package directories.
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in
16
+ all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,12 @@
1
+ # Plate tabbable plugin
2
+
3
+ This package programmatically corrects the tab order for tabbable elements inside the editor
4
+
5
+ ## Documentation
6
+
7
+ Check out
8
+ [Tabbable](https://platejs.org/docs/tabbable).
9
+
10
+ ## License
11
+
12
+ [MIT](../../LICENSE)
@@ -0,0 +1,70 @@
1
+ import { KEYS, PathApi, createTSlatePlugin } from "platejs";
2
+
3
+ //#region src/lib/BaseTabbablePlugin.ts
4
+ const BaseTabbablePlugin = createTSlatePlugin({
5
+ key: KEYS.tabbable,
6
+ options: {
7
+ globalEventListener: false,
8
+ insertTabbableEntries: () => [],
9
+ query: () => true
10
+ }
11
+ }).extend(({ editor }) => ({ options: { isTabbable: (tabbableEntry) => editor.api.isVoid(tabbableEntry.slateNode) } }));
12
+
13
+ //#endregion
14
+ //#region src/lib/findTabDestination.ts
15
+ const findTabDestination = (editor, { activeTabbableEntry, direction, tabbableEntries }) => {
16
+ if (activeTabbableEntry) {
17
+ const nextTabbableEntry$1 = tabbableEntries[tabbableEntries.indexOf(activeTabbableEntry) + (direction === "forward" ? 1 : -1)];
18
+ /**
19
+ * If the next tabbable entry originated from the same path as the active
20
+ * tabbable entry, focus it.
21
+ *
22
+ * Examples of when this is true:
23
+ *
24
+ * - We're inside a void node and there is an additional tabbable inside the
25
+ * same void node.
26
+ * - We're inside a popover containing multiple tabbable elements all anchored
27
+ * to the same slate node, and there is an additional tabbable inside the
28
+ * same popover.
29
+ *
30
+ * Examples of when this is false:
31
+ *
32
+ * - We're inside a void node and the next tabbable is outside the void node.
33
+ * - We're in the last tabbable element of a popover.
34
+ * - There is no next tabbable element.
35
+ */
36
+ if (nextTabbableEntry$1 && PathApi.equals(activeTabbableEntry.path, nextTabbableEntry$1.path)) return {
37
+ domNode: nextTabbableEntry$1.domNode,
38
+ type: "dom-node"
39
+ };
40
+ /**
41
+ * Otherwise, return the focus to the editor. If we're moving forward, focus
42
+ * the first point after the active tabbable's path. If we're moving
43
+ * backward, focus the point of the active tabbable's path. TODO: Let a
44
+ * tabbable entry specify custom before and after points.
45
+ */
46
+ if (direction === "forward") {
47
+ const pointAfter = editor.api.after(activeTabbableEntry.path);
48
+ if (!pointAfter) return null;
49
+ return {
50
+ path: pointAfter.path,
51
+ type: "path"
52
+ };
53
+ }
54
+ return {
55
+ path: editor.api.point(activeTabbableEntry.path).path,
56
+ type: "path"
57
+ };
58
+ }
59
+ const selectionPath = editor.selection?.anchor?.path || [];
60
+ const nextTabbableEntry = direction === "forward" ? tabbableEntries.find((entry) => !PathApi.isBefore(entry.path, selectionPath)) : [...tabbableEntries].reverse().find((entry) => PathApi.isBefore(entry.path, selectionPath));
61
+ if (nextTabbableEntry) return {
62
+ domNode: nextTabbableEntry.domNode,
63
+ type: "dom-node"
64
+ };
65
+ return null;
66
+ };
67
+
68
+ //#endregion
69
+ export { BaseTabbablePlugin as n, findTabDestination as t };
70
+ //# sourceMappingURL=findTabDestination-VIYSr_9z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findTabDestination-VIYSr_9z.js","names":["PluginConfig","createTSlatePlugin","KEYS","TabbableEntry","TabblableConfig","globalEventListener","insertTabbableEntries","event","KeyboardEvent","isTabbable","entry","query","BaseTabbablePlugin","key","tabbable","options","extend","editor","tabbableEntry","api","isVoid","slateNode","SlateEditor","PathApi","TabbableEntry","TabDestination","FindTabDestinationOptions","activeTabbableEntry","direction","tabbableEntries","findTabDestination","editor","activeTabbableEntryIndex","indexOf","nextTabbableEntryIndex","nextTabbableEntry","equals","path","domNode","type","pointAfter","api","after","point","selectionPath","selection","anchor","find","entry","isBefore","reverse"],"sources":["../src/lib/BaseTabbablePlugin.ts","../src/lib/findTabDestination.ts"],"sourcesContent":["import { type PluginConfig, createTSlatePlugin, KEYS } from 'platejs';\n\nimport type { TabbableEntry } from './types';\n\nexport type TabblableConfig = PluginConfig<\n 'tabbable',\n {\n /**\n * When true, the plugin will add its event listener to the document instead\n * of the editor, allowing it to capture events from outside the editor.\n *\n * @default: false\n */\n globalEventListener?: boolean;\n /**\n * Add additional tabbables to the list of tabbables. Useful for adding\n * tabbables that are not contained within the editor. Ignores\n * `isTabbable`.\n *\n * @default: () => []\n */\n insertTabbableEntries?: (event: KeyboardEvent) => TabbableEntry[];\n /**\n * Determine whether an element should be included in the tabbable list.\n *\n * @default: (editor, tabbableEntry) => editor.api.isVoid(tabbableEntry.slateNode)\n */\n isTabbable?: (entry: TabbableEntry) => boolean;\n /**\n * Dynamically enable or disable the plugin.\n *\n * @default: () => true\n */\n query?: (event: KeyboardEvent) => boolean;\n }\n>;\n\nexport const BaseTabbablePlugin = createTSlatePlugin<TabblableConfig>({\n key: KEYS.tabbable,\n options: {\n globalEventListener: false,\n insertTabbableEntries: () => [],\n query: () => true,\n },\n}).extend(({ editor }) => ({\n options: {\n isTabbable: (tabbableEntry) => editor.api.isVoid(tabbableEntry.slateNode),\n },\n}));\n","import { type SlateEditor, PathApi } from 'platejs';\n\nimport type { TabbableEntry, TabDestination } from './types';\n\nexport type FindTabDestinationOptions = {\n activeTabbableEntry: TabbableEntry | null;\n direction: 'backward' | 'forward';\n tabbableEntries: TabbableEntry[];\n};\n\nexport const findTabDestination = (\n editor: SlateEditor,\n { activeTabbableEntry, direction, tabbableEntries }: FindTabDestinationOptions\n): TabDestination | null => {\n // Case 1: A tabbable entry was active before tab was pressed\n if (activeTabbableEntry) {\n // Find the next tabbable entry after the active one\n const activeTabbableEntryIndex =\n tabbableEntries.indexOf(activeTabbableEntry);\n const nextTabbableEntryIndex =\n activeTabbableEntryIndex + (direction === 'forward' ? 1 : -1);\n const nextTabbableEntry = tabbableEntries[nextTabbableEntryIndex];\n\n /**\n * If the next tabbable entry originated from the same path as the active\n * tabbable entry, focus it.\n *\n * Examples of when this is true:\n *\n * - We're inside a void node and there is an additional tabbable inside the\n * same void node.\n * - We're inside a popover containing multiple tabbable elements all anchored\n * to the same slate node, and there is an additional tabbable inside the\n * same popover.\n *\n * Examples of when this is false:\n *\n * - We're inside a void node and the next tabbable is outside the void node.\n * - We're in the last tabbable element of a popover.\n * - There is no next tabbable element.\n */\n if (\n nextTabbableEntry &&\n PathApi.equals(activeTabbableEntry.path, nextTabbableEntry.path)\n ) {\n return {\n domNode: nextTabbableEntry.domNode,\n type: 'dom-node',\n };\n }\n /**\n * Otherwise, return the focus to the editor. If we're moving forward, focus\n * the first point after the active tabbable's path. If we're moving\n * backward, focus the point of the active tabbable's path. TODO: Let a\n * tabbable entry specify custom before and after points.\n */\n if (direction === 'forward') {\n const pointAfter = editor.api.after(activeTabbableEntry.path);\n\n if (!pointAfter) return null;\n\n return {\n path: pointAfter.path,\n type: 'path',\n };\n }\n\n return {\n path: editor.api.point(activeTabbableEntry.path)!.path,\n type: 'path',\n };\n }\n\n // Case 2: No tabbable entry was active before tab was pressed\n\n const selectionPath = editor.selection?.anchor?.path || [];\n\n // Find the first tabbable entry after the selection\n const nextTabbableEntry =\n direction === 'forward'\n ? tabbableEntries.find(\n (entry) => !PathApi.isBefore(entry.path, selectionPath)\n )\n : [...tabbableEntries]\n .reverse()\n .find((entry) => PathApi.isBefore(entry.path, selectionPath));\n\n // If it exists, focus it\n if (nextTabbableEntry) {\n return {\n domNode: nextTabbableEntry.domNode,\n type: 'dom-node',\n };\n }\n\n // Otherwise, use the default behaviour\n return null;\n};\n"],"mappings":";;;AAqCA,MAAaY,qBAAqBX,mBAAoC;CACpEY,KAAKX,KAAKY;CACVC,SAAS;EACPV,qBAAqB;EACrBC,6BAA6B,EAAE;EAC/BK,aAAa;EACf;CACD,CAAC,CAACK,QAAQ,EAAEC,cAAc,EACzBF,SAAS,EACPN,aAAaS,kBAAkBD,OAAOE,IAAIC,OAAOF,cAAcG,UAAS,EAC1E,EACD,EAAE;;;;ACtCH,MAAaS,sBACXC,QACA,EAAEJ,qBAAqBC,WAAWC,sBACR;AAE1B,KAAIF,qBAAqB;EAMvB,MAAMQ,sBAAoBN,gBAHxBA,gBAAgBI,QAAQN,oBAAoB,IAEhBC,cAAc,YAAY,IAAI;;;;;;;;;;;;;;;;;;;AAqB5D,MACEO,uBACAZ,QAAQa,OAAOT,oBAAoBU,MAAMF,oBAAkBE,KAAK,CAEhE,QAAO;GACLC,SAASH,oBAAkBG;GAC3BC,MAAM;GACP;;;;;;;AAQH,MAAIX,cAAc,WAAW;GAC3B,MAAMY,aAAaT,OAAOU,IAAIC,MAAMf,oBAAoBU,KAAK;AAE7D,OAAI,CAACG,WAAY,QAAO;AAExB,UAAO;IACLH,MAAMG,WAAWH;IACjBE,MAAM;IACP;;AAGH,SAAO;GACLF,MAAMN,OAAOU,IAAIE,MAAMhB,oBAAoBU,KAAK,CAAEA;GAClDE,MAAM;GACP;;CAKH,MAAMK,gBAAgBb,OAAOc,WAAWC,QAAQT,QAAQ,EAAE;CAG1D,MAAMF,oBACJP,cAAc,YACVC,gBAAgBkB,MACbC,UAAU,CAACzB,QAAQ0B,SAASD,MAAMX,MAAMO,cAC3C,CAAC,GACD,CAAC,GAAGf,gBAAgB,CACjBqB,SAAS,CACTH,MAAMC,UAAUzB,QAAQ0B,SAASD,MAAMX,MAAMO,cAAc,CAAC;AAGrE,KAAIT,kBACF,QAAO;EACLG,SAASH,kBAAkBG;EAC3BC,MAAM;EACP;AAIH,QAAO"}
@@ -0,0 +1,64 @@
1
+ import { Path, PluginConfig, SlateEditor, TElement } from "platejs";
2
+
3
+ //#region src/lib/types.d.ts
4
+ type TabbableEntry = {
5
+ domNode: HTMLElement;
6
+ path: Path;
7
+ slateNode: TElement;
8
+ };
9
+ type TabDestination = TabDestinationDOMNode | TabDestinationPath;
10
+ type TabDestinationDOMNode = {
11
+ domNode: HTMLElement;
12
+ type: 'dom-node';
13
+ };
14
+ type TabDestinationPath = {
15
+ path: Path;
16
+ type: 'path';
17
+ };
18
+ //#endregion
19
+ //#region src/lib/BaseTabbablePlugin.d.ts
20
+ type TabblableConfig = PluginConfig<'tabbable', {
21
+ /**
22
+ * When true, the plugin will add its event listener to the document instead
23
+ * of the editor, allowing it to capture events from outside the editor.
24
+ *
25
+ * @default: false
26
+ */
27
+ globalEventListener?: boolean;
28
+ /**
29
+ * Add additional tabbables to the list of tabbables. Useful for adding
30
+ * tabbables that are not contained within the editor. Ignores
31
+ * `isTabbable`.
32
+ *
33
+ * @default: () => []
34
+ */
35
+ insertTabbableEntries?: (event: KeyboardEvent) => TabbableEntry[];
36
+ /**
37
+ * Determine whether an element should be included in the tabbable list.
38
+ *
39
+ * @default: (editor, tabbableEntry) => editor.api.isVoid(tabbableEntry.slateNode)
40
+ */
41
+ isTabbable?: (entry: TabbableEntry) => boolean;
42
+ /**
43
+ * Dynamically enable or disable the plugin.
44
+ *
45
+ * @default: () => true
46
+ */
47
+ query?: (event: KeyboardEvent) => boolean;
48
+ }>;
49
+ declare const BaseTabbablePlugin: any;
50
+ //#endregion
51
+ //#region src/lib/findTabDestination.d.ts
52
+ type FindTabDestinationOptions = {
53
+ activeTabbableEntry: TabbableEntry | null;
54
+ direction: 'backward' | 'forward';
55
+ tabbableEntries: TabbableEntry[];
56
+ };
57
+ declare const findTabDestination: (editor: SlateEditor, {
58
+ activeTabbableEntry,
59
+ direction,
60
+ tabbableEntries
61
+ }: FindTabDestinationOptions) => TabDestination | null;
62
+ //#endregion
63
+ export { BaseTabbablePlugin, FindTabDestinationOptions, TabDestination, TabDestinationDOMNode, TabDestinationPath, TabbableEntry, TabblableConfig, findTabDestination };
64
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/lib/types.ts","../src/lib/BaseTabbablePlugin.ts","../src/lib/findTabDestination.ts"],"sourcesContent":[],"mappings":";;;KAEY,aAAA;WACD;EADC,IAAA,EAEJ,IAFI;EACD,SAAA,EAEE,QAFF;CACH;AACK,KAGD,cAAA,GAAiB,qBAHhB,GAGwC,kBAHxC;AAAQ,KAKT,qBAAA,GALS;EAGT,OAAA,EAGD,WAHe;EAEd,IAAA,EAAA,UAAA;AAKZ,CAAA;KAAY,kBAAA;QACJ;;ACZR,CAAA;;;KAAY,eAAA,GAAkB;EDFlB;;;;;AAMZ;EAEY,mBAAA,CAAA,EAAA,OAAqB;EAKrB;;;;ACXZ;;;EAuByB,qBAAA,CAAA,EAAA,CAAA,KAAA,EANW,aAMX,EAAA,GAN6B,aAM7B,EAAA;EAML;;;AAIpB;;uBAVyB;;ACvBzB;AAMA;;;EAEE,KAAA,CAAA,EAAA,CAAA,KAAA,EDqBkB,aCrBlB,EAAA,GAAA,OAAA;CAAA,CAAA;AAAqD,cDyB1C,kBCzB0C,EAAA,GAAA;;;KAR3C,yBAAA;EFFA,mBAAa,EEGF,aFHE,GAAA,IAAA;EACd,SAAA,EAAA,UAAA,GAAA,SAAA;EACH,eAAA,EEGW,aFHX,EAAA;CACK;AAAQ,cEKR,kBFLQ,EAAA,CAAA,MAAA,EEMX,WFNW,EAAA;EAAA,mBAAA;EAAA,SAAA;EAAA;AAAA,CAAA,EEOkC,yBFPlC,EAAA,GEQlB,cFRkB,GAAA,IAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import { n as BaseTabbablePlugin, t as findTabDestination } from "./findTabDestination-VIYSr_9z.js";
2
+
3
+ export { BaseTabbablePlugin, findTabDestination };
@@ -0,0 +1,8 @@
1
+ //#region src/react/TabbableEffects.d.ts
2
+ declare function TabbableEffects(): null;
3
+ //#endregion
4
+ //#region src/react/TabbablePlugin.d.ts
5
+ declare const TabbablePlugin: any;
6
+ //#endregion
7
+ export { TabbableEffects, TabbablePlugin };
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/react/TabbableEffects.tsx","../../src/react/TabbablePlugin.tsx"],"sourcesContent":[],"mappings":";iBAWgB,eAAA,CAAA;;;cCNH"}
@@ -0,0 +1,101 @@
1
+ import { n as BaseTabbablePlugin, t as findTabDestination } from "../findTabDestination-VIYSr_9z.js";
2
+ import { PathApi } from "platejs";
3
+ import { c } from "react-compiler-runtime";
4
+ import React from "react";
5
+ import { toPlatePlugin, useEditorReadOnly, useEditorRef } from "platejs/react";
6
+ import { tabbable } from "tabbable";
7
+
8
+ //#region src/react/TabbableEffects.tsx
9
+ function TabbableEffects() {
10
+ const $ = c(4);
11
+ const editor = useEditorRef();
12
+ const readOnly = useEditorReadOnly();
13
+ let t0;
14
+ let t1;
15
+ if ($[0] !== editor || $[1] !== readOnly) {
16
+ t0 = () => {
17
+ if (readOnly) return;
18
+ const { globalEventListener, insertTabbableEntries, isTabbable, query } = editor.getOptions(BaseTabbablePlugin);
19
+ const editorDOMNode = editor.api.toDOMNode(editor);
20
+ if (!editorDOMNode) return;
21
+ const handler = (event) => {
22
+ if (event.key !== "Tab" || event.defaultPrevented || !query?.(event)) return;
23
+ const insertedTabbableEntries = insertTabbableEntries?.(event);
24
+ if (globalEventListener && event.target && ![editorDOMNode, ...insertedTabbableEntries.map(_temp)].some((container) => container.contains(event.target))) return;
25
+ const tabbableDOMNodes = tabbable(editorDOMNode);
26
+ const defaultTabbableEntries = tabbableDOMNodes.map((domNode_0) => {
27
+ const slateNode = editor.api.toSlateNode(domNode_0);
28
+ if (!slateNode) return null;
29
+ return {
30
+ domNode: domNode_0,
31
+ path: editor.api.findPath(slateNode),
32
+ slateNode
33
+ };
34
+ }).filter((entry) => entry && isTabbable?.(entry));
35
+ const tabbableEntries = [...insertedTabbableEntries, ...defaultTabbableEntries].sort(_temp2);
36
+ const { activeElement } = document;
37
+ const tabDestination = findTabDestination(editor, {
38
+ activeTabbableEntry: (activeElement && tabbableEntries.find((entry_0) => entry_0.domNode === activeElement)) ?? null,
39
+ direction: event.shiftKey ? "backward" : "forward",
40
+ tabbableEntries
41
+ });
42
+ if (tabDestination) {
43
+ event.preventDefault();
44
+ bb76: switch (tabDestination.type) {
45
+ case "dom-node":
46
+ tabDestination.domNode.focus();
47
+ break bb76;
48
+ case "path": editor.tf.focus({ at: {
49
+ anchor: {
50
+ offset: 0,
51
+ path: tabDestination.path
52
+ },
53
+ focus: {
54
+ offset: 0,
55
+ path: tabDestination.path
56
+ }
57
+ } });
58
+ }
59
+ return;
60
+ }
61
+ tabbableDOMNodes.forEach(_temp3);
62
+ };
63
+ const eventListenerNode = globalEventListener ? document.body : editorDOMNode;
64
+ eventListenerNode.addEventListener("keydown", handler, true);
65
+ return () => eventListenerNode.removeEventListener("keydown", handler, true);
66
+ };
67
+ t1 = [readOnly, editor];
68
+ $[0] = editor;
69
+ $[1] = readOnly;
70
+ $[2] = t0;
71
+ $[3] = t1;
72
+ } else {
73
+ t0 = $[2];
74
+ t1 = $[3];
75
+ }
76
+ React.useEffect(t0, t1);
77
+ return null;
78
+ }
79
+ function _temp3(domNode_1) {
80
+ const oldTabIndex = domNode_1.getAttribute("tabindex");
81
+ domNode_1.setAttribute("tabindex", "-1");
82
+ setTimeout(() => {
83
+ if (oldTabIndex) domNode_1.setAttribute("tabindex", oldTabIndex);
84
+ else domNode_1.removeAttribute("tabindex");
85
+ }, 0);
86
+ }
87
+ function _temp2(a, b) {
88
+ return PathApi.compare(a.path, b.path);
89
+ }
90
+ function _temp(t0) {
91
+ const { domNode } = t0;
92
+ return domNode;
93
+ }
94
+
95
+ //#endregion
96
+ //#region src/react/TabbablePlugin.tsx
97
+ const TabbablePlugin = toPlatePlugin(BaseTabbablePlugin, { render: { afterEditable: TabbableEffects } });
98
+
99
+ //#endregion
100
+ export { TabbableEffects, TabbablePlugin };
101
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["React","PathApi","useEditorReadOnly","useEditorRef","tabbable","TabbableEntry","BaseTabbablePlugin","findTabDestination","TabbableEffects","$","_c","editor","readOnly","t0","t1","globalEventListener","insertTabbableEntries","isTabbable","query","getOptions","editorDOMNode","api","toDOMNode","handler","event","key","defaultPrevented","insertedTabbableEntries","target","map","_temp","some","container","contains","Node","tabbableDOMNodes","HTMLElement","defaultTabbableEntries","domNode_0","slateNode","toSlateNode","domNode","path","findPath","filter","entry","tabbableEntries","sort","_temp2","activeElement","document","activeTabbableEntry","find","entry_0","tabDestination","direction","shiftKey","preventDefault","bb76","type","focus","tf","at","anchor","offset","forEach","_temp3","eventListenerNode","body","addEventListener","removeEventListener","useEffect","domNode_1","oldTabIndex","getAttribute","setAttribute","setTimeout","removeAttribute","a","b","compare","toPlatePlugin","BaseTabbablePlugin","TabbableEffects","TabbablePlugin","render","afterEditable"],"sources":["../../src/react/TabbableEffects.tsx","../../src/react/TabbablePlugin.tsx"],"sourcesContent":["import React from 'react';\n\nimport { PathApi } from 'platejs';\nimport { useEditorReadOnly, useEditorRef } from 'platejs/react';\nimport { tabbable } from 'tabbable';\n\nimport type { TabbableEntry } from '../lib/types';\n\nimport { BaseTabbablePlugin } from '../lib/BaseTabbablePlugin';\nimport { findTabDestination } from '../lib/findTabDestination';\n\nexport function TabbableEffects() {\n const editor = useEditorRef();\n const readOnly = useEditorReadOnly();\n\n React.useEffect(() => {\n if (readOnly) return;\n\n const { globalEventListener, insertTabbableEntries, isTabbable, query } =\n editor.getOptions(BaseTabbablePlugin);\n\n const editorDOMNode = editor.api.toDOMNode(editor);\n\n if (!editorDOMNode) return;\n\n const handler = (event: KeyboardEvent) => {\n // Check if the keydown is a tab key that should be handled\n if (event.key !== 'Tab' || event.defaultPrevented || !query?.(event)) {\n return;\n }\n\n /**\n * Get the list of additional tabbable entries specified in the plugin\n * options\n */\n const insertedTabbableEntries = insertTabbableEntries?.(\n event\n ) as TabbableEntry[];\n\n /**\n * Global event listener only. Do not handle the tab event if the keydown\n * was sent to an element other than the editor or one of the additional\n * tabbable elements.\n */\n if (\n globalEventListener &&\n event.target &&\n ![\n editorDOMNode,\n ...insertedTabbableEntries.map(({ domNode }) => domNode),\n ].some((container) => container.contains(event.target as Node))\n ) {\n return;\n }\n\n // Get all tabbable DOM nodes in the editor\n const tabbableDOMNodes = tabbable(editorDOMNode) as HTMLElement[];\n\n /**\n * Construct a tabbable entry for each tabbable Slate node, filtered by\n * the `isTabbable` option (defaulting to only void nodes).\n */\n const defaultTabbableEntries = tabbableDOMNodes\n .map((domNode) => {\n const slateNode = editor.api.toSlateNode(domNode);\n\n if (!slateNode) return null;\n\n return {\n domNode,\n path: editor.api.findPath(slateNode),\n slateNode,\n } as TabbableEntry;\n })\n .filter((entry) => entry && isTabbable?.(entry)) as TabbableEntry[];\n\n /**\n * The list of all tabbable entries. Sorting by path ensures a consistent\n * tab order.\n */\n const tabbableEntries = [\n ...insertedTabbableEntries,\n ...defaultTabbableEntries,\n ].sort((a, b) => PathApi.compare(a.path, b.path));\n\n /**\n * TODO: Refactor everything ABOVE this line into a util function and test\n * separately\n */\n\n // Check if any tabbable entry is the active element\n const { activeElement } = document;\n const activeTabbableEntry =\n (activeElement &&\n tabbableEntries.find((entry) => entry.domNode === activeElement)) ??\n null;\n\n // Find the next Slate node or DOM node to focus\n const tabDestination = findTabDestination(editor, {\n activeTabbableEntry,\n direction: event.shiftKey ? 'backward' : 'forward',\n tabbableEntries,\n });\n\n if (tabDestination) {\n event.preventDefault();\n\n switch (tabDestination.type) {\n case 'dom-node': {\n tabDestination.domNode.focus();\n\n break;\n }\n case 'path': {\n editor.tf.focus({\n at: {\n anchor: { offset: 0, path: tabDestination.path },\n focus: { offset: 0, path: tabDestination.path },\n },\n });\n\n break;\n }\n }\n\n return;\n }\n\n /**\n * There was no tab destination, so let the browser handle the tab event.\n * We don't want the browser to focus anything that could have been\n * focused by us, so we make make all tabbable DOM nodes in the editor\n * unfocusable. This ensures that the focus exits the editor cleanly.\n */\n tabbableDOMNodes.forEach((domNode) => {\n const oldTabIndex = domNode.getAttribute('tabindex');\n domNode.setAttribute('tabindex', '-1');\n\n setTimeout(() => {\n if (oldTabIndex) {\n domNode.setAttribute('tabindex', oldTabIndex);\n } else {\n domNode.removeAttribute('tabindex');\n }\n }, 0);\n });\n };\n\n const eventListenerNode = globalEventListener\n ? document.body\n : editorDOMNode;\n\n eventListenerNode.addEventListener('keydown', handler, true);\n\n return () =>\n eventListenerNode.removeEventListener('keydown', handler, true);\n }, [readOnly, editor]);\n\n return null;\n}\n","import { toPlatePlugin } from 'platejs/react';\n\nimport { BaseTabbablePlugin } from '../lib/BaseTabbablePlugin';\nimport { TabbableEffects } from './TabbableEffects';\n\nexport const TabbablePlugin = toPlatePlugin(BaseTabbablePlugin, {\n render: { afterEditable: TabbableEffects },\n});\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@lofcz/platejs-tabbable",
3
+ "version": "52.0.11",
4
+ "description": "Tab into and out of void nodes and other elements",
5
+ "keywords": [
6
+ "plate",
7
+ "plugin",
8
+ "slate"
9
+ ],
10
+ "homepage": "https://platejs.org",
11
+ "bugs": {
12
+ "url": "https://github.com/udecode/plate/issues"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/lofcz/plate.git",
17
+ "directory": "packages/tabbable"
18
+ },
19
+ "license": "MIT",
20
+ "sideEffects": false,
21
+ "exports": {
22
+ ".": "./dist/index.js",
23
+ "./react": "./dist/react/index.js",
24
+ "./package.json": "./package.json"
25
+ },
26
+ "main": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "files": [
29
+ "dist/**/*"
30
+ ],
31
+ "dependencies": {
32
+ "react-compiler-runtime": "^1.0.0",
33
+ "tabbable": "^6.2.0"
34
+ },
35
+ "devDependencies": {
36
+ "@plate/scripts": "1.0.0"
37
+ },
38
+ "peerDependencies": {
39
+ "platejs": ">=52.0.11",
40
+ "react": ">=18.0.0",
41
+ "react-dom": ">=18.0.0"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "type": "module",
47
+ "module": "./dist/index.js",
48
+ "scripts": {
49
+ "brl": "plate-pkg p:brl",
50
+ "build": "plate-pkg p:build",
51
+ "build:watch": "plate-pkg p:build:watch",
52
+ "clean": "plate-pkg p:clean",
53
+ "lint": "plate-pkg p:lint",
54
+ "lint:fix": "plate-pkg p:lint:fix",
55
+ "test": "plate-pkg p:test",
56
+ "test:watch": "plate-pkg p:test:watch",
57
+ "typecheck": "plate-pkg p:typecheck"
58
+ }
59
+ }