@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 +24 -0
- package/README.md +12 -0
- package/dist/findTabDestination-VIYSr_9z.js +70 -0
- package/dist/findTabDestination-VIYSr_9z.js.map +1 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/react/index.d.ts +8 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +101 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +59 -0
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,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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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,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
|
+
}
|