@andrewcyuan/focusgrid 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +39 -0
- package/dist/core/index.cjs +812 -0
- package/dist/core/index.d.cts +184 -0
- package/dist/core/index.d.ts +184 -0
- package/dist/core/index.js +807 -0
- package/dist/dom/index.cjs +318 -0
- package/dist/dom/index.d.cts +177 -0
- package/dist/dom/index.d.ts +177 -0
- package/dist/dom/index.js +315 -0
- package/dist/react/index.cjs +1336 -0
- package/dist/react/index.d.cts +294 -0
- package/dist/react/index.d.ts +294 -0
- package/dist/react/index.js +1328 -0
- package/dist/react/styles.css +42 -0
- package/package.json +66 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { ShortcutBinding } from "@andrewcyuan/shortcut-engine";
|
|
3
|
+
|
|
4
|
+
//#region core/src/layout/types.d.ts
|
|
5
|
+
type PaneId = string;
|
|
6
|
+
type NodeId = string;
|
|
7
|
+
type Orientation = "horizontal" | "vertical";
|
|
8
|
+
declare const cardinalDirections: readonly ["left", "right", "up", "down"];
|
|
9
|
+
type CardinalDirection = (typeof cardinalDirections)[number];
|
|
10
|
+
declare enum PaneCommandCapability {
|
|
11
|
+
ResizeX = "canResizeX",
|
|
12
|
+
ResizeY = "canResizeY",
|
|
13
|
+
Remove = "canRemove",
|
|
14
|
+
SplitHorizontal = "canSplitHorizontal",
|
|
15
|
+
SplitVertical = "canSplitVertical",
|
|
16
|
+
SwapX = "canSwapX",
|
|
17
|
+
SwapY = "canSwapY",
|
|
18
|
+
Focus = "canFocus"
|
|
19
|
+
}
|
|
20
|
+
type PaneCommandCapabilityInput = Partial<Record<PaneCommandCapability, boolean>>;
|
|
21
|
+
type PaneNode = {
|
|
22
|
+
kind: "pane";
|
|
23
|
+
id: NodeId;
|
|
24
|
+
paneId: PaneId;
|
|
25
|
+
} & PaneCommandCapabilityInput;
|
|
26
|
+
type SplitNode = {
|
|
27
|
+
kind: "split";
|
|
28
|
+
id: NodeId;
|
|
29
|
+
orientation: Orientation;
|
|
30
|
+
children: LayoutNode[];
|
|
31
|
+
sizes: number[];
|
|
32
|
+
lastFocusedChildId?: NodeId;
|
|
33
|
+
};
|
|
34
|
+
type LayoutNode = PaneNode | SplitNode;
|
|
35
|
+
type Rect = {
|
|
36
|
+
x: number;
|
|
37
|
+
y: number;
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
};
|
|
41
|
+
type ComputedPane = {
|
|
42
|
+
paneId: PaneId;
|
|
43
|
+
nodeId: NodeId;
|
|
44
|
+
rect: Rect;
|
|
45
|
+
active: boolean;
|
|
46
|
+
};
|
|
47
|
+
type ComputedHandle = {
|
|
48
|
+
id: string;
|
|
49
|
+
splitId: NodeId;
|
|
50
|
+
index: number;
|
|
51
|
+
rect: Rect;
|
|
52
|
+
direction: Orientation;
|
|
53
|
+
};
|
|
54
|
+
type ComputedNode = {
|
|
55
|
+
kind: "pane";
|
|
56
|
+
pane: ComputedPane;
|
|
57
|
+
} | {
|
|
58
|
+
kind: "split";
|
|
59
|
+
orientation: Orientation;
|
|
60
|
+
children: ComputedNode[];
|
|
61
|
+
handles: ComputedHandle[];
|
|
62
|
+
};
|
|
63
|
+
type ComputedLayout = {
|
|
64
|
+
root: ComputedNode;
|
|
65
|
+
rectByNodeId: Map<NodeId, Rect>;
|
|
66
|
+
panes: ComputedPane[];
|
|
67
|
+
handles: ComputedHandle[];
|
|
68
|
+
};
|
|
69
|
+
type FocusGridControllerState = {
|
|
70
|
+
root: LayoutNode;
|
|
71
|
+
activePaneId: PaneId | null;
|
|
72
|
+
container: {
|
|
73
|
+
width: number;
|
|
74
|
+
height: number;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region core/src/controller.d.ts
|
|
79
|
+
interface DomController {
|
|
80
|
+
focus(paneId: PaneId): boolean;
|
|
81
|
+
}
|
|
82
|
+
type Listener = (next: FocusGridControllerState, previous: FocusGridControllerState) => void;
|
|
83
|
+
type PaneLayoutChangeEvent = {
|
|
84
|
+
pane: ComputedPane;
|
|
85
|
+
previousPane: ComputedPane;
|
|
86
|
+
controller: FocusGridController;
|
|
87
|
+
};
|
|
88
|
+
type PaneCloseEvent = {
|
|
89
|
+
paneId: PaneId;
|
|
90
|
+
previousPane: ComputedPane;
|
|
91
|
+
controller: FocusGridController;
|
|
92
|
+
};
|
|
93
|
+
type PaneEventHandlers = {
|
|
94
|
+
onPaneLayoutChange?: (event: PaneLayoutChangeEvent) => void;
|
|
95
|
+
onPaneClose?: (event: PaneCloseEvent) => void;
|
|
96
|
+
};
|
|
97
|
+
type PaneDefaults = PaneCommandCapabilityInput;
|
|
98
|
+
type FocusGridControllerProps = FocusGridControllerState & {
|
|
99
|
+
paneDefaults?: PaneDefaults;
|
|
100
|
+
minWidth?: number;
|
|
101
|
+
minHeight?: number;
|
|
102
|
+
directionalFocusOverflow?: boolean;
|
|
103
|
+
};
|
|
104
|
+
type SplitPaneOptions = Partial<Omit<PaneNode, "kind" | "id" | "paneId">> & {
|
|
105
|
+
side: CardinalDirection;
|
|
106
|
+
newPaneId?: PaneId;
|
|
107
|
+
newPaneNodeId?: NodeId;
|
|
108
|
+
splitId?: NodeId;
|
|
109
|
+
preserveActivePane?: boolean;
|
|
110
|
+
};
|
|
111
|
+
type ResizePaneOptions = {
|
|
112
|
+
direction: CardinalDirection;
|
|
113
|
+
deltaPx: number;
|
|
114
|
+
};
|
|
115
|
+
type ResizeHandleOptions = {
|
|
116
|
+
index: number;
|
|
117
|
+
deltaPx: number;
|
|
118
|
+
snapshotSizes?: number[];
|
|
119
|
+
};
|
|
120
|
+
declare class FocusGridController {
|
|
121
|
+
private readonly domController;
|
|
122
|
+
readonly minWidth: number;
|
|
123
|
+
readonly minHeight: number;
|
|
124
|
+
readonly directionalFocusOverflow: boolean;
|
|
125
|
+
private currentState;
|
|
126
|
+
private cachedLayout;
|
|
127
|
+
private readonly paneDefaults;
|
|
128
|
+
private readonly listeners;
|
|
129
|
+
constructor({
|
|
130
|
+
paneDefaults,
|
|
131
|
+
minWidth,
|
|
132
|
+
minHeight,
|
|
133
|
+
directionalFocusOverflow,
|
|
134
|
+
...state
|
|
135
|
+
}: FocusGridControllerProps, domController: DomController);
|
|
136
|
+
/** Computed render data; stable until the next committed change. */
|
|
137
|
+
getLayout(): ComputedLayout;
|
|
138
|
+
getPane(paneId: PaneId): PaneNode | null;
|
|
139
|
+
getContainerSize(): {
|
|
140
|
+
width: number;
|
|
141
|
+
height: number;
|
|
142
|
+
};
|
|
143
|
+
getSplitSizes(splitId: NodeId): number[] | null;
|
|
144
|
+
splitActive(side: CardinalDirection): PaneId | null;
|
|
145
|
+
removeActive(): boolean;
|
|
146
|
+
focusAdjacent(direction: CardinalDirection): boolean;
|
|
147
|
+
swapAdjacent(direction: CardinalDirection): boolean;
|
|
148
|
+
resizeActive(direction: CardinalDirection, deltaPx: number): boolean;
|
|
149
|
+
split(paneNodeId: NodeId, props: SplitPaneOptions): PaneId | null;
|
|
150
|
+
remove(paneId: PaneId): boolean;
|
|
151
|
+
swap(firstPaneId: PaneId, secondPaneId: PaneId): boolean;
|
|
152
|
+
resize(paneId: PaneId, props: ResizePaneOptions): boolean;
|
|
153
|
+
resizeHandle(splitId: NodeId, props: ResizeHandleOptions): boolean;
|
|
154
|
+
focus(paneId: PaneId): boolean;
|
|
155
|
+
updatePane(paneId: PaneId, patch: Partial<Omit<PaneNode, "kind" | "id" | "paneId">>): boolean;
|
|
156
|
+
setContainerSize(width: number, height: number): boolean;
|
|
157
|
+
private commit;
|
|
158
|
+
subscribePaneEvents({
|
|
159
|
+
onPaneLayoutChange,
|
|
160
|
+
onPaneClose
|
|
161
|
+
}: PaneEventHandlers): () => void;
|
|
162
|
+
subscribe(listener: Listener): () => void;
|
|
163
|
+
}
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region dom/src/controller.d.ts
|
|
166
|
+
/** Attach the renderer root; pane elements are resolved only when focus is requested. */
|
|
167
|
+
declare class FocusGridDomController implements DomController {
|
|
168
|
+
private root;
|
|
169
|
+
setRoot(root: HTMLElement | null): void;
|
|
170
|
+
focus(paneId: PaneId): boolean;
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region react/src/FocusgridController.d.ts
|
|
174
|
+
declare const FocusgridController: import("react").Context<{
|
|
175
|
+
controller: FocusGridController;
|
|
176
|
+
domController: FocusGridDomController;
|
|
177
|
+
} | null>;
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region react/src/PaneView.d.ts
|
|
180
|
+
type Pane = {
|
|
181
|
+
paneId: PaneId;
|
|
182
|
+
rect: Rect;
|
|
183
|
+
active: boolean;
|
|
184
|
+
controller: FocusGridController;
|
|
185
|
+
};
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region react/src/FocusGrid.d.ts
|
|
188
|
+
type FocusGridProps = PaneEventHandlers & {
|
|
189
|
+
keymap?: readonly ShortcutBinding[];
|
|
190
|
+
renderPane: (ctx: Pane) => ReactNode;
|
|
191
|
+
className?: string;
|
|
192
|
+
};
|
|
193
|
+
declare function FocusGrid({
|
|
194
|
+
className,
|
|
195
|
+
onPaneLayoutChange,
|
|
196
|
+
onPaneClose,
|
|
197
|
+
...content
|
|
198
|
+
}: FocusGridProps): import("react").JSX.Element;
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region react/src/hooks.d.ts
|
|
201
|
+
declare function useFocusGridController(createProps: () => FocusGridControllerProps, domController: DomController): FocusGridController;
|
|
202
|
+
declare function useControllerLayout(controller: FocusGridController): ComputedLayout;
|
|
203
|
+
//#endregion
|
|
204
|
+
//#region react/src/composite-shortcuts.d.ts
|
|
205
|
+
type CompositeNavigationDirection = "left" | "right" | "up" | "down" | "start" | "end";
|
|
206
|
+
declare function createCompositeNavigationKeymap(move: (direction: CompositeNavigationDirection, event: KeyboardEvent) => void): ShortcutBinding[];
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region react/src/default-pane-keymap.d.ts
|
|
209
|
+
declare const defaultPaneShortcutActions: readonly [{
|
|
210
|
+
readonly id: "split-right";
|
|
211
|
+
readonly label: "Split right";
|
|
212
|
+
readonly action: (controller: FocusGridController) => string | null;
|
|
213
|
+
readonly defaultSequence: "Ctrl-B %";
|
|
214
|
+
}, {
|
|
215
|
+
readonly id: "split-down";
|
|
216
|
+
readonly label: "Split down";
|
|
217
|
+
readonly action: (controller: FocusGridController) => string | null;
|
|
218
|
+
readonly defaultSequence: "Ctrl-B \"";
|
|
219
|
+
}, {
|
|
220
|
+
readonly id: "close";
|
|
221
|
+
readonly label: "Close active";
|
|
222
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
223
|
+
readonly defaultSequence: "Ctrl-B X";
|
|
224
|
+
}, {
|
|
225
|
+
readonly id: "focus-left";
|
|
226
|
+
readonly label: "Focus left";
|
|
227
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
228
|
+
readonly defaultSequence: "Ctrl-B Left";
|
|
229
|
+
}, {
|
|
230
|
+
readonly id: "focus-right";
|
|
231
|
+
readonly label: "Focus right";
|
|
232
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
233
|
+
readonly defaultSequence: "Ctrl-B Right";
|
|
234
|
+
}, {
|
|
235
|
+
readonly id: "focus-up";
|
|
236
|
+
readonly label: "Focus up";
|
|
237
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
238
|
+
readonly defaultSequence: "Ctrl-B Up";
|
|
239
|
+
}, {
|
|
240
|
+
readonly id: "focus-down";
|
|
241
|
+
readonly label: "Focus down";
|
|
242
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
243
|
+
readonly defaultSequence: "Ctrl-B Down";
|
|
244
|
+
}, {
|
|
245
|
+
readonly id: "swap-left";
|
|
246
|
+
readonly label: "Swap left";
|
|
247
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
248
|
+
readonly defaultSequence: "Ctrl-B Shift-Left";
|
|
249
|
+
}, {
|
|
250
|
+
readonly id: "swap-right";
|
|
251
|
+
readonly label: "Swap right";
|
|
252
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
253
|
+
readonly defaultSequence: "Ctrl-B Shift-Right";
|
|
254
|
+
}, {
|
|
255
|
+
readonly id: "swap-up";
|
|
256
|
+
readonly label: "Swap up";
|
|
257
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
258
|
+
readonly defaultSequence: "Ctrl-B Shift-Up";
|
|
259
|
+
}, {
|
|
260
|
+
readonly id: "swap-down";
|
|
261
|
+
readonly label: "Swap down";
|
|
262
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
263
|
+
readonly defaultSequence: "Ctrl-B Shift-Down";
|
|
264
|
+
}, {
|
|
265
|
+
readonly id: "resize-left";
|
|
266
|
+
readonly label: "Resize left";
|
|
267
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
268
|
+
readonly defaultSequence: "Ctrl-B H";
|
|
269
|
+
readonly repeat: true;
|
|
270
|
+
}, {
|
|
271
|
+
readonly id: "resize-right";
|
|
272
|
+
readonly label: "Resize right";
|
|
273
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
274
|
+
readonly defaultSequence: "Ctrl-B L";
|
|
275
|
+
readonly repeat: true;
|
|
276
|
+
}, {
|
|
277
|
+
readonly id: "resize-up";
|
|
278
|
+
readonly label: "Resize up";
|
|
279
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
280
|
+
readonly defaultSequence: "Ctrl-B K";
|
|
281
|
+
readonly repeat: true;
|
|
282
|
+
}, {
|
|
283
|
+
readonly id: "resize-down";
|
|
284
|
+
readonly label: "Resize down";
|
|
285
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
286
|
+
readonly defaultSequence: "Ctrl-B J";
|
|
287
|
+
readonly repeat: true;
|
|
288
|
+
}];
|
|
289
|
+
type PaneShortcutId = (typeof defaultPaneShortcutActions)[number]["id"];
|
|
290
|
+
type PaneShortcutValues = Record<PaneShortcutId, string>;
|
|
291
|
+
declare function createDefaultPaneShortcuts(): PaneShortcutValues;
|
|
292
|
+
declare function createDefaultPaneKeymap(controller: FocusGridController): ShortcutBinding[];
|
|
293
|
+
//#endregion
|
|
294
|
+
export { CompositeNavigationDirection, FocusGrid, type FocusGridProps, FocusgridController, type Pane, type PaneShortcutId, type PaneShortcutValues, createCompositeNavigationKeymap, createDefaultPaneKeymap, createDefaultPaneShortcuts, defaultPaneShortcutActions, useControllerLayout, useFocusGridController };
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { ShortcutBinding } from "@andrewcyuan/shortcut-engine";
|
|
3
|
+
|
|
4
|
+
//#region core/src/layout/types.d.ts
|
|
5
|
+
type PaneId = string;
|
|
6
|
+
type NodeId = string;
|
|
7
|
+
type Orientation = "horizontal" | "vertical";
|
|
8
|
+
declare const cardinalDirections: readonly ["left", "right", "up", "down"];
|
|
9
|
+
type CardinalDirection = (typeof cardinalDirections)[number];
|
|
10
|
+
declare enum PaneCommandCapability {
|
|
11
|
+
ResizeX = "canResizeX",
|
|
12
|
+
ResizeY = "canResizeY",
|
|
13
|
+
Remove = "canRemove",
|
|
14
|
+
SplitHorizontal = "canSplitHorizontal",
|
|
15
|
+
SplitVertical = "canSplitVertical",
|
|
16
|
+
SwapX = "canSwapX",
|
|
17
|
+
SwapY = "canSwapY",
|
|
18
|
+
Focus = "canFocus"
|
|
19
|
+
}
|
|
20
|
+
type PaneCommandCapabilityInput = Partial<Record<PaneCommandCapability, boolean>>;
|
|
21
|
+
type PaneNode = {
|
|
22
|
+
kind: "pane";
|
|
23
|
+
id: NodeId;
|
|
24
|
+
paneId: PaneId;
|
|
25
|
+
} & PaneCommandCapabilityInput;
|
|
26
|
+
type SplitNode = {
|
|
27
|
+
kind: "split";
|
|
28
|
+
id: NodeId;
|
|
29
|
+
orientation: Orientation;
|
|
30
|
+
children: LayoutNode[];
|
|
31
|
+
sizes: number[];
|
|
32
|
+
lastFocusedChildId?: NodeId;
|
|
33
|
+
};
|
|
34
|
+
type LayoutNode = PaneNode | SplitNode;
|
|
35
|
+
type Rect = {
|
|
36
|
+
x: number;
|
|
37
|
+
y: number;
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
};
|
|
41
|
+
type ComputedPane = {
|
|
42
|
+
paneId: PaneId;
|
|
43
|
+
nodeId: NodeId;
|
|
44
|
+
rect: Rect;
|
|
45
|
+
active: boolean;
|
|
46
|
+
};
|
|
47
|
+
type ComputedHandle = {
|
|
48
|
+
id: string;
|
|
49
|
+
splitId: NodeId;
|
|
50
|
+
index: number;
|
|
51
|
+
rect: Rect;
|
|
52
|
+
direction: Orientation;
|
|
53
|
+
};
|
|
54
|
+
type ComputedNode = {
|
|
55
|
+
kind: "pane";
|
|
56
|
+
pane: ComputedPane;
|
|
57
|
+
} | {
|
|
58
|
+
kind: "split";
|
|
59
|
+
orientation: Orientation;
|
|
60
|
+
children: ComputedNode[];
|
|
61
|
+
handles: ComputedHandle[];
|
|
62
|
+
};
|
|
63
|
+
type ComputedLayout = {
|
|
64
|
+
root: ComputedNode;
|
|
65
|
+
rectByNodeId: Map<NodeId, Rect>;
|
|
66
|
+
panes: ComputedPane[];
|
|
67
|
+
handles: ComputedHandle[];
|
|
68
|
+
};
|
|
69
|
+
type FocusGridControllerState = {
|
|
70
|
+
root: LayoutNode;
|
|
71
|
+
activePaneId: PaneId | null;
|
|
72
|
+
container: {
|
|
73
|
+
width: number;
|
|
74
|
+
height: number;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region core/src/controller.d.ts
|
|
79
|
+
interface DomController {
|
|
80
|
+
focus(paneId: PaneId): boolean;
|
|
81
|
+
}
|
|
82
|
+
type Listener = (next: FocusGridControllerState, previous: FocusGridControllerState) => void;
|
|
83
|
+
type PaneLayoutChangeEvent = {
|
|
84
|
+
pane: ComputedPane;
|
|
85
|
+
previousPane: ComputedPane;
|
|
86
|
+
controller: FocusGridController;
|
|
87
|
+
};
|
|
88
|
+
type PaneCloseEvent = {
|
|
89
|
+
paneId: PaneId;
|
|
90
|
+
previousPane: ComputedPane;
|
|
91
|
+
controller: FocusGridController;
|
|
92
|
+
};
|
|
93
|
+
type PaneEventHandlers = {
|
|
94
|
+
onPaneLayoutChange?: (event: PaneLayoutChangeEvent) => void;
|
|
95
|
+
onPaneClose?: (event: PaneCloseEvent) => void;
|
|
96
|
+
};
|
|
97
|
+
type PaneDefaults = PaneCommandCapabilityInput;
|
|
98
|
+
type FocusGridControllerProps = FocusGridControllerState & {
|
|
99
|
+
paneDefaults?: PaneDefaults;
|
|
100
|
+
minWidth?: number;
|
|
101
|
+
minHeight?: number;
|
|
102
|
+
directionalFocusOverflow?: boolean;
|
|
103
|
+
};
|
|
104
|
+
type SplitPaneOptions = Partial<Omit<PaneNode, "kind" | "id" | "paneId">> & {
|
|
105
|
+
side: CardinalDirection;
|
|
106
|
+
newPaneId?: PaneId;
|
|
107
|
+
newPaneNodeId?: NodeId;
|
|
108
|
+
splitId?: NodeId;
|
|
109
|
+
preserveActivePane?: boolean;
|
|
110
|
+
};
|
|
111
|
+
type ResizePaneOptions = {
|
|
112
|
+
direction: CardinalDirection;
|
|
113
|
+
deltaPx: number;
|
|
114
|
+
};
|
|
115
|
+
type ResizeHandleOptions = {
|
|
116
|
+
index: number;
|
|
117
|
+
deltaPx: number;
|
|
118
|
+
snapshotSizes?: number[];
|
|
119
|
+
};
|
|
120
|
+
declare class FocusGridController {
|
|
121
|
+
private readonly domController;
|
|
122
|
+
readonly minWidth: number;
|
|
123
|
+
readonly minHeight: number;
|
|
124
|
+
readonly directionalFocusOverflow: boolean;
|
|
125
|
+
private currentState;
|
|
126
|
+
private cachedLayout;
|
|
127
|
+
private readonly paneDefaults;
|
|
128
|
+
private readonly listeners;
|
|
129
|
+
constructor({
|
|
130
|
+
paneDefaults,
|
|
131
|
+
minWidth,
|
|
132
|
+
minHeight,
|
|
133
|
+
directionalFocusOverflow,
|
|
134
|
+
...state
|
|
135
|
+
}: FocusGridControllerProps, domController: DomController);
|
|
136
|
+
/** Computed render data; stable until the next committed change. */
|
|
137
|
+
getLayout(): ComputedLayout;
|
|
138
|
+
getPane(paneId: PaneId): PaneNode | null;
|
|
139
|
+
getContainerSize(): {
|
|
140
|
+
width: number;
|
|
141
|
+
height: number;
|
|
142
|
+
};
|
|
143
|
+
getSplitSizes(splitId: NodeId): number[] | null;
|
|
144
|
+
splitActive(side: CardinalDirection): PaneId | null;
|
|
145
|
+
removeActive(): boolean;
|
|
146
|
+
focusAdjacent(direction: CardinalDirection): boolean;
|
|
147
|
+
swapAdjacent(direction: CardinalDirection): boolean;
|
|
148
|
+
resizeActive(direction: CardinalDirection, deltaPx: number): boolean;
|
|
149
|
+
split(paneNodeId: NodeId, props: SplitPaneOptions): PaneId | null;
|
|
150
|
+
remove(paneId: PaneId): boolean;
|
|
151
|
+
swap(firstPaneId: PaneId, secondPaneId: PaneId): boolean;
|
|
152
|
+
resize(paneId: PaneId, props: ResizePaneOptions): boolean;
|
|
153
|
+
resizeHandle(splitId: NodeId, props: ResizeHandleOptions): boolean;
|
|
154
|
+
focus(paneId: PaneId): boolean;
|
|
155
|
+
updatePane(paneId: PaneId, patch: Partial<Omit<PaneNode, "kind" | "id" | "paneId">>): boolean;
|
|
156
|
+
setContainerSize(width: number, height: number): boolean;
|
|
157
|
+
private commit;
|
|
158
|
+
subscribePaneEvents({
|
|
159
|
+
onPaneLayoutChange,
|
|
160
|
+
onPaneClose
|
|
161
|
+
}: PaneEventHandlers): () => void;
|
|
162
|
+
subscribe(listener: Listener): () => void;
|
|
163
|
+
}
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region dom/src/controller.d.ts
|
|
166
|
+
/** Attach the renderer root; pane elements are resolved only when focus is requested. */
|
|
167
|
+
declare class FocusGridDomController implements DomController {
|
|
168
|
+
private root;
|
|
169
|
+
setRoot(root: HTMLElement | null): void;
|
|
170
|
+
focus(paneId: PaneId): boolean;
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region react/src/FocusgridController.d.ts
|
|
174
|
+
declare const FocusgridController: import("react").Context<{
|
|
175
|
+
controller: FocusGridController;
|
|
176
|
+
domController: FocusGridDomController;
|
|
177
|
+
} | null>;
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region react/src/PaneView.d.ts
|
|
180
|
+
type Pane = {
|
|
181
|
+
paneId: PaneId;
|
|
182
|
+
rect: Rect;
|
|
183
|
+
active: boolean;
|
|
184
|
+
controller: FocusGridController;
|
|
185
|
+
};
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region react/src/FocusGrid.d.ts
|
|
188
|
+
type FocusGridProps = PaneEventHandlers & {
|
|
189
|
+
keymap?: readonly ShortcutBinding[];
|
|
190
|
+
renderPane: (ctx: Pane) => ReactNode;
|
|
191
|
+
className?: string;
|
|
192
|
+
};
|
|
193
|
+
declare function FocusGrid({
|
|
194
|
+
className,
|
|
195
|
+
onPaneLayoutChange,
|
|
196
|
+
onPaneClose,
|
|
197
|
+
...content
|
|
198
|
+
}: FocusGridProps): import("react").JSX.Element;
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region react/src/hooks.d.ts
|
|
201
|
+
declare function useFocusGridController(createProps: () => FocusGridControllerProps, domController: DomController): FocusGridController;
|
|
202
|
+
declare function useControllerLayout(controller: FocusGridController): ComputedLayout;
|
|
203
|
+
//#endregion
|
|
204
|
+
//#region react/src/composite-shortcuts.d.ts
|
|
205
|
+
type CompositeNavigationDirection = "left" | "right" | "up" | "down" | "start" | "end";
|
|
206
|
+
declare function createCompositeNavigationKeymap(move: (direction: CompositeNavigationDirection, event: KeyboardEvent) => void): ShortcutBinding[];
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region react/src/default-pane-keymap.d.ts
|
|
209
|
+
declare const defaultPaneShortcutActions: readonly [{
|
|
210
|
+
readonly id: "split-right";
|
|
211
|
+
readonly label: "Split right";
|
|
212
|
+
readonly action: (controller: FocusGridController) => string | null;
|
|
213
|
+
readonly defaultSequence: "Ctrl-B %";
|
|
214
|
+
}, {
|
|
215
|
+
readonly id: "split-down";
|
|
216
|
+
readonly label: "Split down";
|
|
217
|
+
readonly action: (controller: FocusGridController) => string | null;
|
|
218
|
+
readonly defaultSequence: "Ctrl-B \"";
|
|
219
|
+
}, {
|
|
220
|
+
readonly id: "close";
|
|
221
|
+
readonly label: "Close active";
|
|
222
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
223
|
+
readonly defaultSequence: "Ctrl-B X";
|
|
224
|
+
}, {
|
|
225
|
+
readonly id: "focus-left";
|
|
226
|
+
readonly label: "Focus left";
|
|
227
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
228
|
+
readonly defaultSequence: "Ctrl-B Left";
|
|
229
|
+
}, {
|
|
230
|
+
readonly id: "focus-right";
|
|
231
|
+
readonly label: "Focus right";
|
|
232
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
233
|
+
readonly defaultSequence: "Ctrl-B Right";
|
|
234
|
+
}, {
|
|
235
|
+
readonly id: "focus-up";
|
|
236
|
+
readonly label: "Focus up";
|
|
237
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
238
|
+
readonly defaultSequence: "Ctrl-B Up";
|
|
239
|
+
}, {
|
|
240
|
+
readonly id: "focus-down";
|
|
241
|
+
readonly label: "Focus down";
|
|
242
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
243
|
+
readonly defaultSequence: "Ctrl-B Down";
|
|
244
|
+
}, {
|
|
245
|
+
readonly id: "swap-left";
|
|
246
|
+
readonly label: "Swap left";
|
|
247
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
248
|
+
readonly defaultSequence: "Ctrl-B Shift-Left";
|
|
249
|
+
}, {
|
|
250
|
+
readonly id: "swap-right";
|
|
251
|
+
readonly label: "Swap right";
|
|
252
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
253
|
+
readonly defaultSequence: "Ctrl-B Shift-Right";
|
|
254
|
+
}, {
|
|
255
|
+
readonly id: "swap-up";
|
|
256
|
+
readonly label: "Swap up";
|
|
257
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
258
|
+
readonly defaultSequence: "Ctrl-B Shift-Up";
|
|
259
|
+
}, {
|
|
260
|
+
readonly id: "swap-down";
|
|
261
|
+
readonly label: "Swap down";
|
|
262
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
263
|
+
readonly defaultSequence: "Ctrl-B Shift-Down";
|
|
264
|
+
}, {
|
|
265
|
+
readonly id: "resize-left";
|
|
266
|
+
readonly label: "Resize left";
|
|
267
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
268
|
+
readonly defaultSequence: "Ctrl-B H";
|
|
269
|
+
readonly repeat: true;
|
|
270
|
+
}, {
|
|
271
|
+
readonly id: "resize-right";
|
|
272
|
+
readonly label: "Resize right";
|
|
273
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
274
|
+
readonly defaultSequence: "Ctrl-B L";
|
|
275
|
+
readonly repeat: true;
|
|
276
|
+
}, {
|
|
277
|
+
readonly id: "resize-up";
|
|
278
|
+
readonly label: "Resize up";
|
|
279
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
280
|
+
readonly defaultSequence: "Ctrl-B K";
|
|
281
|
+
readonly repeat: true;
|
|
282
|
+
}, {
|
|
283
|
+
readonly id: "resize-down";
|
|
284
|
+
readonly label: "Resize down";
|
|
285
|
+
readonly action: (controller: FocusGridController) => boolean;
|
|
286
|
+
readonly defaultSequence: "Ctrl-B J";
|
|
287
|
+
readonly repeat: true;
|
|
288
|
+
}];
|
|
289
|
+
type PaneShortcutId = (typeof defaultPaneShortcutActions)[number]["id"];
|
|
290
|
+
type PaneShortcutValues = Record<PaneShortcutId, string>;
|
|
291
|
+
declare function createDefaultPaneShortcuts(): PaneShortcutValues;
|
|
292
|
+
declare function createDefaultPaneKeymap(controller: FocusGridController): ShortcutBinding[];
|
|
293
|
+
//#endregion
|
|
294
|
+
export { CompositeNavigationDirection, FocusGrid, type FocusGridProps, FocusgridController, type Pane, type PaneShortcutId, type PaneShortcutValues, createCompositeNavigationKeymap, createDefaultPaneKeymap, createDefaultPaneShortcuts, defaultPaneShortcutActions, useControllerLayout, useFocusGridController };
|