@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,184 @@
|
|
|
1
|
+
//#region core/src/layout/types.d.ts
|
|
2
|
+
type PaneId = string;
|
|
3
|
+
type NodeId = string;
|
|
4
|
+
type Orientation = "horizontal" | "vertical";
|
|
5
|
+
declare const cardinalDirections: readonly ["left", "right", "up", "down"];
|
|
6
|
+
type CardinalDirection = (typeof cardinalDirections)[number];
|
|
7
|
+
declare enum PaneCommandCapability {
|
|
8
|
+
ResizeX = "canResizeX",
|
|
9
|
+
ResizeY = "canResizeY",
|
|
10
|
+
Remove = "canRemove",
|
|
11
|
+
SplitHorizontal = "canSplitHorizontal",
|
|
12
|
+
SplitVertical = "canSplitVertical",
|
|
13
|
+
SwapX = "canSwapX",
|
|
14
|
+
SwapY = "canSwapY",
|
|
15
|
+
Focus = "canFocus"
|
|
16
|
+
}
|
|
17
|
+
type PaneCommandCapabilityInput = Partial<Record<PaneCommandCapability, boolean>>;
|
|
18
|
+
type PaneNode = {
|
|
19
|
+
kind: "pane";
|
|
20
|
+
id: NodeId;
|
|
21
|
+
paneId: PaneId;
|
|
22
|
+
} & PaneCommandCapabilityInput;
|
|
23
|
+
type SplitNode = {
|
|
24
|
+
kind: "split";
|
|
25
|
+
id: NodeId;
|
|
26
|
+
orientation: Orientation;
|
|
27
|
+
children: LayoutNode[];
|
|
28
|
+
sizes: number[];
|
|
29
|
+
lastFocusedChildId?: NodeId;
|
|
30
|
+
};
|
|
31
|
+
type LayoutNode = PaneNode | SplitNode;
|
|
32
|
+
type Rect = {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
width: number;
|
|
36
|
+
height: number;
|
|
37
|
+
};
|
|
38
|
+
type ComputedPane = {
|
|
39
|
+
paneId: PaneId;
|
|
40
|
+
nodeId: NodeId;
|
|
41
|
+
rect: Rect;
|
|
42
|
+
active: boolean;
|
|
43
|
+
};
|
|
44
|
+
type ComputedHandle = {
|
|
45
|
+
id: string;
|
|
46
|
+
splitId: NodeId;
|
|
47
|
+
index: number;
|
|
48
|
+
rect: Rect;
|
|
49
|
+
direction: Orientation;
|
|
50
|
+
};
|
|
51
|
+
type ComputedNode = {
|
|
52
|
+
kind: "pane";
|
|
53
|
+
pane: ComputedPane;
|
|
54
|
+
} | {
|
|
55
|
+
kind: "split";
|
|
56
|
+
orientation: Orientation;
|
|
57
|
+
children: ComputedNode[];
|
|
58
|
+
handles: ComputedHandle[];
|
|
59
|
+
};
|
|
60
|
+
type ComputedLayout = {
|
|
61
|
+
root: ComputedNode;
|
|
62
|
+
rectByNodeId: Map<NodeId, Rect>;
|
|
63
|
+
panes: ComputedPane[];
|
|
64
|
+
handles: ComputedHandle[];
|
|
65
|
+
};
|
|
66
|
+
type FocusGridControllerState = {
|
|
67
|
+
root: LayoutNode;
|
|
68
|
+
activePaneId: PaneId | null;
|
|
69
|
+
container: {
|
|
70
|
+
width: number;
|
|
71
|
+
height: number;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region core/src/controller.d.ts
|
|
76
|
+
interface DomController {
|
|
77
|
+
focus(paneId: PaneId): boolean;
|
|
78
|
+
}
|
|
79
|
+
type Listener = (next: FocusGridControllerState, previous: FocusGridControllerState) => void;
|
|
80
|
+
type PaneLayoutChangeEvent = {
|
|
81
|
+
pane: ComputedPane;
|
|
82
|
+
previousPane: ComputedPane;
|
|
83
|
+
controller: FocusGridController;
|
|
84
|
+
};
|
|
85
|
+
type PaneCloseEvent = {
|
|
86
|
+
paneId: PaneId;
|
|
87
|
+
previousPane: ComputedPane;
|
|
88
|
+
controller: FocusGridController;
|
|
89
|
+
};
|
|
90
|
+
type PaneEventHandlers = {
|
|
91
|
+
onPaneLayoutChange?: (event: PaneLayoutChangeEvent) => void;
|
|
92
|
+
onPaneClose?: (event: PaneCloseEvent) => void;
|
|
93
|
+
};
|
|
94
|
+
type PaneDefaults = PaneCommandCapabilityInput;
|
|
95
|
+
type FocusGridControllerProps = FocusGridControllerState & {
|
|
96
|
+
paneDefaults?: PaneDefaults;
|
|
97
|
+
minWidth?: number;
|
|
98
|
+
minHeight?: number;
|
|
99
|
+
directionalFocusOverflow?: boolean;
|
|
100
|
+
};
|
|
101
|
+
type SplitPaneOptions = Partial<Omit<PaneNode, "kind" | "id" | "paneId">> & {
|
|
102
|
+
side: CardinalDirection;
|
|
103
|
+
newPaneId?: PaneId;
|
|
104
|
+
newPaneNodeId?: NodeId;
|
|
105
|
+
splitId?: NodeId;
|
|
106
|
+
preserveActivePane?: boolean;
|
|
107
|
+
};
|
|
108
|
+
type ResizePaneOptions = {
|
|
109
|
+
direction: CardinalDirection;
|
|
110
|
+
deltaPx: number;
|
|
111
|
+
};
|
|
112
|
+
type ResizeHandleOptions = {
|
|
113
|
+
index: number;
|
|
114
|
+
deltaPx: number;
|
|
115
|
+
snapshotSizes?: number[];
|
|
116
|
+
};
|
|
117
|
+
declare class FocusGridController {
|
|
118
|
+
private readonly domController;
|
|
119
|
+
readonly minWidth: number;
|
|
120
|
+
readonly minHeight: number;
|
|
121
|
+
readonly directionalFocusOverflow: boolean;
|
|
122
|
+
private currentState;
|
|
123
|
+
private cachedLayout;
|
|
124
|
+
private readonly paneDefaults;
|
|
125
|
+
private readonly listeners;
|
|
126
|
+
constructor({
|
|
127
|
+
paneDefaults,
|
|
128
|
+
minWidth,
|
|
129
|
+
minHeight,
|
|
130
|
+
directionalFocusOverflow,
|
|
131
|
+
...state
|
|
132
|
+
}: FocusGridControllerProps, domController: DomController);
|
|
133
|
+
/** Computed render data; stable until the next committed change. */
|
|
134
|
+
getLayout(): ComputedLayout;
|
|
135
|
+
getPane(paneId: PaneId): PaneNode | null;
|
|
136
|
+
getContainerSize(): {
|
|
137
|
+
width: number;
|
|
138
|
+
height: number;
|
|
139
|
+
};
|
|
140
|
+
getSplitSizes(splitId: NodeId): number[] | null;
|
|
141
|
+
splitActive(side: CardinalDirection): PaneId | null;
|
|
142
|
+
removeActive(): boolean;
|
|
143
|
+
focusAdjacent(direction: CardinalDirection): boolean;
|
|
144
|
+
swapAdjacent(direction: CardinalDirection): boolean;
|
|
145
|
+
resizeActive(direction: CardinalDirection, deltaPx: number): boolean;
|
|
146
|
+
split(paneNodeId: NodeId, props: SplitPaneOptions): PaneId | null;
|
|
147
|
+
remove(paneId: PaneId): boolean;
|
|
148
|
+
swap(firstPaneId: PaneId, secondPaneId: PaneId): boolean;
|
|
149
|
+
resize(paneId: PaneId, props: ResizePaneOptions): boolean;
|
|
150
|
+
resizeHandle(splitId: NodeId, props: ResizeHandleOptions): boolean;
|
|
151
|
+
focus(paneId: PaneId): boolean;
|
|
152
|
+
updatePane(paneId: PaneId, patch: Partial<Omit<PaneNode, "kind" | "id" | "paneId">>): boolean;
|
|
153
|
+
setContainerSize(width: number, height: number): boolean;
|
|
154
|
+
private commit;
|
|
155
|
+
subscribePaneEvents({
|
|
156
|
+
onPaneLayoutChange,
|
|
157
|
+
onPaneClose
|
|
158
|
+
}: PaneEventHandlers): () => void;
|
|
159
|
+
subscribe(listener: Listener): () => void;
|
|
160
|
+
}
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region core/src/validation.d.ts
|
|
163
|
+
type FocusGridStateValidationError = {
|
|
164
|
+
code: string;
|
|
165
|
+
path: string;
|
|
166
|
+
message: string;
|
|
167
|
+
paneId?: PaneId;
|
|
168
|
+
nodeId?: NodeId;
|
|
169
|
+
};
|
|
170
|
+
type FocusGridStateValidationResult = {
|
|
171
|
+
ok: true;
|
|
172
|
+
state: FocusGridControllerState;
|
|
173
|
+
errors: [];
|
|
174
|
+
} | {
|
|
175
|
+
ok: false;
|
|
176
|
+
errors: FocusGridStateValidationError[];
|
|
177
|
+
};
|
|
178
|
+
declare class FocusGridStateValidationException extends Error {
|
|
179
|
+
readonly errors: FocusGridStateValidationError[];
|
|
180
|
+
constructor(errors: FocusGridStateValidationError[]);
|
|
181
|
+
}
|
|
182
|
+
declare function validateFocusGridControllerState(input: unknown): FocusGridStateValidationResult;
|
|
183
|
+
//#endregion
|
|
184
|
+
export { type CardinalDirection, type ComputedHandle, type ComputedLayout, type ComputedNode, type ComputedPane, type DomController, FocusGridController, type FocusGridControllerProps, type FocusGridControllerState, type FocusGridStateValidationError, FocusGridStateValidationException, type FocusGridStateValidationResult, type LayoutNode, type Listener, type NodeId, type PaneCloseEvent, PaneCommandCapability, type PaneCommandCapabilityInput, type PaneDefaults, type PaneEventHandlers, type PaneId, type PaneLayoutChangeEvent, type PaneNode, type Rect, type ResizeHandleOptions, type ResizePaneOptions, type SplitNode, type SplitPaneOptions, cardinalDirections, validateFocusGridControllerState };
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
//#region core/src/layout/types.d.ts
|
|
2
|
+
type PaneId = string;
|
|
3
|
+
type NodeId = string;
|
|
4
|
+
type Orientation = "horizontal" | "vertical";
|
|
5
|
+
declare const cardinalDirections: readonly ["left", "right", "up", "down"];
|
|
6
|
+
type CardinalDirection = (typeof cardinalDirections)[number];
|
|
7
|
+
declare enum PaneCommandCapability {
|
|
8
|
+
ResizeX = "canResizeX",
|
|
9
|
+
ResizeY = "canResizeY",
|
|
10
|
+
Remove = "canRemove",
|
|
11
|
+
SplitHorizontal = "canSplitHorizontal",
|
|
12
|
+
SplitVertical = "canSplitVertical",
|
|
13
|
+
SwapX = "canSwapX",
|
|
14
|
+
SwapY = "canSwapY",
|
|
15
|
+
Focus = "canFocus"
|
|
16
|
+
}
|
|
17
|
+
type PaneCommandCapabilityInput = Partial<Record<PaneCommandCapability, boolean>>;
|
|
18
|
+
type PaneNode = {
|
|
19
|
+
kind: "pane";
|
|
20
|
+
id: NodeId;
|
|
21
|
+
paneId: PaneId;
|
|
22
|
+
} & PaneCommandCapabilityInput;
|
|
23
|
+
type SplitNode = {
|
|
24
|
+
kind: "split";
|
|
25
|
+
id: NodeId;
|
|
26
|
+
orientation: Orientation;
|
|
27
|
+
children: LayoutNode[];
|
|
28
|
+
sizes: number[];
|
|
29
|
+
lastFocusedChildId?: NodeId;
|
|
30
|
+
};
|
|
31
|
+
type LayoutNode = PaneNode | SplitNode;
|
|
32
|
+
type Rect = {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
width: number;
|
|
36
|
+
height: number;
|
|
37
|
+
};
|
|
38
|
+
type ComputedPane = {
|
|
39
|
+
paneId: PaneId;
|
|
40
|
+
nodeId: NodeId;
|
|
41
|
+
rect: Rect;
|
|
42
|
+
active: boolean;
|
|
43
|
+
};
|
|
44
|
+
type ComputedHandle = {
|
|
45
|
+
id: string;
|
|
46
|
+
splitId: NodeId;
|
|
47
|
+
index: number;
|
|
48
|
+
rect: Rect;
|
|
49
|
+
direction: Orientation;
|
|
50
|
+
};
|
|
51
|
+
type ComputedNode = {
|
|
52
|
+
kind: "pane";
|
|
53
|
+
pane: ComputedPane;
|
|
54
|
+
} | {
|
|
55
|
+
kind: "split";
|
|
56
|
+
orientation: Orientation;
|
|
57
|
+
children: ComputedNode[];
|
|
58
|
+
handles: ComputedHandle[];
|
|
59
|
+
};
|
|
60
|
+
type ComputedLayout = {
|
|
61
|
+
root: ComputedNode;
|
|
62
|
+
rectByNodeId: Map<NodeId, Rect>;
|
|
63
|
+
panes: ComputedPane[];
|
|
64
|
+
handles: ComputedHandle[];
|
|
65
|
+
};
|
|
66
|
+
type FocusGridControllerState = {
|
|
67
|
+
root: LayoutNode;
|
|
68
|
+
activePaneId: PaneId | null;
|
|
69
|
+
container: {
|
|
70
|
+
width: number;
|
|
71
|
+
height: number;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region core/src/controller.d.ts
|
|
76
|
+
interface DomController {
|
|
77
|
+
focus(paneId: PaneId): boolean;
|
|
78
|
+
}
|
|
79
|
+
type Listener = (next: FocusGridControllerState, previous: FocusGridControllerState) => void;
|
|
80
|
+
type PaneLayoutChangeEvent = {
|
|
81
|
+
pane: ComputedPane;
|
|
82
|
+
previousPane: ComputedPane;
|
|
83
|
+
controller: FocusGridController;
|
|
84
|
+
};
|
|
85
|
+
type PaneCloseEvent = {
|
|
86
|
+
paneId: PaneId;
|
|
87
|
+
previousPane: ComputedPane;
|
|
88
|
+
controller: FocusGridController;
|
|
89
|
+
};
|
|
90
|
+
type PaneEventHandlers = {
|
|
91
|
+
onPaneLayoutChange?: (event: PaneLayoutChangeEvent) => void;
|
|
92
|
+
onPaneClose?: (event: PaneCloseEvent) => void;
|
|
93
|
+
};
|
|
94
|
+
type PaneDefaults = PaneCommandCapabilityInput;
|
|
95
|
+
type FocusGridControllerProps = FocusGridControllerState & {
|
|
96
|
+
paneDefaults?: PaneDefaults;
|
|
97
|
+
minWidth?: number;
|
|
98
|
+
minHeight?: number;
|
|
99
|
+
directionalFocusOverflow?: boolean;
|
|
100
|
+
};
|
|
101
|
+
type SplitPaneOptions = Partial<Omit<PaneNode, "kind" | "id" | "paneId">> & {
|
|
102
|
+
side: CardinalDirection;
|
|
103
|
+
newPaneId?: PaneId;
|
|
104
|
+
newPaneNodeId?: NodeId;
|
|
105
|
+
splitId?: NodeId;
|
|
106
|
+
preserveActivePane?: boolean;
|
|
107
|
+
};
|
|
108
|
+
type ResizePaneOptions = {
|
|
109
|
+
direction: CardinalDirection;
|
|
110
|
+
deltaPx: number;
|
|
111
|
+
};
|
|
112
|
+
type ResizeHandleOptions = {
|
|
113
|
+
index: number;
|
|
114
|
+
deltaPx: number;
|
|
115
|
+
snapshotSizes?: number[];
|
|
116
|
+
};
|
|
117
|
+
declare class FocusGridController {
|
|
118
|
+
private readonly domController;
|
|
119
|
+
readonly minWidth: number;
|
|
120
|
+
readonly minHeight: number;
|
|
121
|
+
readonly directionalFocusOverflow: boolean;
|
|
122
|
+
private currentState;
|
|
123
|
+
private cachedLayout;
|
|
124
|
+
private readonly paneDefaults;
|
|
125
|
+
private readonly listeners;
|
|
126
|
+
constructor({
|
|
127
|
+
paneDefaults,
|
|
128
|
+
minWidth,
|
|
129
|
+
minHeight,
|
|
130
|
+
directionalFocusOverflow,
|
|
131
|
+
...state
|
|
132
|
+
}: FocusGridControllerProps, domController: DomController);
|
|
133
|
+
/** Computed render data; stable until the next committed change. */
|
|
134
|
+
getLayout(): ComputedLayout;
|
|
135
|
+
getPane(paneId: PaneId): PaneNode | null;
|
|
136
|
+
getContainerSize(): {
|
|
137
|
+
width: number;
|
|
138
|
+
height: number;
|
|
139
|
+
};
|
|
140
|
+
getSplitSizes(splitId: NodeId): number[] | null;
|
|
141
|
+
splitActive(side: CardinalDirection): PaneId | null;
|
|
142
|
+
removeActive(): boolean;
|
|
143
|
+
focusAdjacent(direction: CardinalDirection): boolean;
|
|
144
|
+
swapAdjacent(direction: CardinalDirection): boolean;
|
|
145
|
+
resizeActive(direction: CardinalDirection, deltaPx: number): boolean;
|
|
146
|
+
split(paneNodeId: NodeId, props: SplitPaneOptions): PaneId | null;
|
|
147
|
+
remove(paneId: PaneId): boolean;
|
|
148
|
+
swap(firstPaneId: PaneId, secondPaneId: PaneId): boolean;
|
|
149
|
+
resize(paneId: PaneId, props: ResizePaneOptions): boolean;
|
|
150
|
+
resizeHandle(splitId: NodeId, props: ResizeHandleOptions): boolean;
|
|
151
|
+
focus(paneId: PaneId): boolean;
|
|
152
|
+
updatePane(paneId: PaneId, patch: Partial<Omit<PaneNode, "kind" | "id" | "paneId">>): boolean;
|
|
153
|
+
setContainerSize(width: number, height: number): boolean;
|
|
154
|
+
private commit;
|
|
155
|
+
subscribePaneEvents({
|
|
156
|
+
onPaneLayoutChange,
|
|
157
|
+
onPaneClose
|
|
158
|
+
}: PaneEventHandlers): () => void;
|
|
159
|
+
subscribe(listener: Listener): () => void;
|
|
160
|
+
}
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region core/src/validation.d.ts
|
|
163
|
+
type FocusGridStateValidationError = {
|
|
164
|
+
code: string;
|
|
165
|
+
path: string;
|
|
166
|
+
message: string;
|
|
167
|
+
paneId?: PaneId;
|
|
168
|
+
nodeId?: NodeId;
|
|
169
|
+
};
|
|
170
|
+
type FocusGridStateValidationResult = {
|
|
171
|
+
ok: true;
|
|
172
|
+
state: FocusGridControllerState;
|
|
173
|
+
errors: [];
|
|
174
|
+
} | {
|
|
175
|
+
ok: false;
|
|
176
|
+
errors: FocusGridStateValidationError[];
|
|
177
|
+
};
|
|
178
|
+
declare class FocusGridStateValidationException extends Error {
|
|
179
|
+
readonly errors: FocusGridStateValidationError[];
|
|
180
|
+
constructor(errors: FocusGridStateValidationError[]);
|
|
181
|
+
}
|
|
182
|
+
declare function validateFocusGridControllerState(input: unknown): FocusGridStateValidationResult;
|
|
183
|
+
//#endregion
|
|
184
|
+
export { type CardinalDirection, type ComputedHandle, type ComputedLayout, type ComputedNode, type ComputedPane, type DomController, FocusGridController, type FocusGridControllerProps, type FocusGridControllerState, type FocusGridStateValidationError, FocusGridStateValidationException, type FocusGridStateValidationResult, type LayoutNode, type Listener, type NodeId, type PaneCloseEvent, PaneCommandCapability, type PaneCommandCapabilityInput, type PaneDefaults, type PaneEventHandlers, type PaneId, type PaneLayoutChangeEvent, type PaneNode, type Rect, type ResizeHandleOptions, type ResizePaneOptions, type SplitNode, type SplitPaneOptions, cardinalDirections, validateFocusGridControllerState };
|