@window-splitter/web-component 1.0.1
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/.storybook/main.js +27 -0
- package/.storybook/preview.css +23 -0
- package/.storybook/preview.js +15 -0
- package/.tshy/build.json +8 -0
- package/.tshy/commonjs.json +19 -0
- package/.tshy/esm.json +18 -0
- package/.turbo/turbo-build.log +5 -0
- package/CHANGELOG.md +24 -0
- package/LICENSE +7 -0
- package/README.md +59 -0
- package/dist/commonjs/index.d.ts +77 -0
- package/dist/commonjs/index.d.ts.map +1 -0
- package/dist/commonjs/index.js +533 -0
- package/dist/commonjs/index.js.map +1 -0
- package/dist/commonjs/package.json +3 -0
- package/dist/esm/index.d.ts +77 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +527 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/eslint.config.mjs +10 -0
- package/package.json +84 -0
- package/src/WebComponentWindowSplitter.stories.ts +873 -0
- package/src/WebComponentWindowSplitter.test.ts +299 -0
- package/src/__snapshots__/WebComponentWindowSplitter.test.ts.snap +54 -0
- package/src/index.ts +660 -0
- package/tsconfig.json +8 -0
- package/vitest.config.js +17 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
import {
|
|
2
|
+
groupMachine,
|
|
3
|
+
initializePanel,
|
|
4
|
+
initializePanelHandleData,
|
|
5
|
+
buildTemplate,
|
|
6
|
+
getCursor,
|
|
7
|
+
PanelHandleData,
|
|
8
|
+
SendFn,
|
|
9
|
+
GroupMachineContextValue,
|
|
10
|
+
Unit,
|
|
11
|
+
PixelUnit,
|
|
12
|
+
isPanelData,
|
|
13
|
+
getGroupSize,
|
|
14
|
+
PanelData,
|
|
15
|
+
Orientation,
|
|
16
|
+
prepareSnapshot,
|
|
17
|
+
getCollapsiblePanelForHandleId,
|
|
18
|
+
OnResizeCallback,
|
|
19
|
+
getPanelPixelSize,
|
|
20
|
+
getPanelPercentageSize,
|
|
21
|
+
getPanelGroupPixelSizes,
|
|
22
|
+
getPanelGroupPercentageSizes,
|
|
23
|
+
prepareItems,
|
|
24
|
+
State,
|
|
25
|
+
haveConstraintsChangedForPanel,
|
|
26
|
+
haveConstraintsChangedForPanelHandle,
|
|
27
|
+
} from "@window-splitter/state";
|
|
28
|
+
import { html, LitElement, PropertyValues } from "lit";
|
|
29
|
+
import { property } from "lit/decorators.js";
|
|
30
|
+
import {
|
|
31
|
+
getPanelDomAttributes,
|
|
32
|
+
getPanelResizerDomAttributes,
|
|
33
|
+
move,
|
|
34
|
+
MoveEvents,
|
|
35
|
+
SharedPanelProps,
|
|
36
|
+
} from "@window-splitter/interface";
|
|
37
|
+
import { consume, createContext, provide } from "@lit/context";
|
|
38
|
+
|
|
39
|
+
const isPrerenderContext = createContext<boolean>("isPrerender");
|
|
40
|
+
const sendContext = createContext<SendFn>("send");
|
|
41
|
+
const contextContext = createContext<GroupMachineContextValue>("context");
|
|
42
|
+
const stateContext = createContext<{ current: State }>("state");
|
|
43
|
+
|
|
44
|
+
let _id = 0;
|
|
45
|
+
|
|
46
|
+
const getNextId = () => {
|
|
47
|
+
_id++;
|
|
48
|
+
return `${_id}`;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function updateAttributes(
|
|
52
|
+
element: HTMLElement,
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
+
attributes: Record<string, any> | { style: Record<string, any> }
|
|
55
|
+
) {
|
|
56
|
+
for (const attr of Object.keys(attributes)) {
|
|
57
|
+
if (attr === "style") {
|
|
58
|
+
for (const style of Object.keys(attributes[attr])) {
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
|
+
(element.style as any)[style] = attributes[attr][style];
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
|
+
element.setAttribute(attr, (attributes as any)[attr]);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isBooleanPropValue(value: string | null) {
|
|
70
|
+
if (value === "true" || value === "") return true;
|
|
71
|
+
if (value === "false") return false;
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class PanelGroup extends LitElement {
|
|
76
|
+
private observer: ResizeObserver;
|
|
77
|
+
private cleanupChildrenObserver: () => void;
|
|
78
|
+
private groupId: string;
|
|
79
|
+
|
|
80
|
+
@provide({ context: contextContext })
|
|
81
|
+
private context: GroupMachineContextValue;
|
|
82
|
+
@provide({ context: sendContext })
|
|
83
|
+
private send: SendFn;
|
|
84
|
+
@provide({ context: isPrerenderContext })
|
|
85
|
+
private isPrerender = true;
|
|
86
|
+
@provide({ context: stateContext })
|
|
87
|
+
private state: { current: State };
|
|
88
|
+
|
|
89
|
+
constructor() {
|
|
90
|
+
super();
|
|
91
|
+
|
|
92
|
+
this.observer = new ResizeObserver(() => {});
|
|
93
|
+
this.cleanupChildrenObserver = () => {};
|
|
94
|
+
|
|
95
|
+
const autosaveId = this.getAttribute("autosaveId");
|
|
96
|
+
const autosaveStrategy =
|
|
97
|
+
(this.getAttribute(
|
|
98
|
+
"autosaveStrategy"
|
|
99
|
+
) as GroupMachineContextValue["autosaveStrategy"]) || "localStorage";
|
|
100
|
+
|
|
101
|
+
this.groupId = this.getAttribute("id") || autosaveId || getNextId();
|
|
102
|
+
|
|
103
|
+
let snapshot = this.getAttribute("snapshot");
|
|
104
|
+
|
|
105
|
+
if (
|
|
106
|
+
!snapshot &&
|
|
107
|
+
typeof window !== "undefined" &&
|
|
108
|
+
autosaveId &&
|
|
109
|
+
autosaveStrategy === "localStorage"
|
|
110
|
+
) {
|
|
111
|
+
const localSnapshot = localStorage.getItem(autosaveId);
|
|
112
|
+
|
|
113
|
+
if (localSnapshot) {
|
|
114
|
+
snapshot = localSnapshot;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const [initialState, send, machineState] = groupMachine(
|
|
119
|
+
{
|
|
120
|
+
orientation:
|
|
121
|
+
(this.getAttribute("orientation") as Orientation) || "horizontal",
|
|
122
|
+
groupId: this.groupId,
|
|
123
|
+
autosaveStrategy,
|
|
124
|
+
...(snapshot ? prepareSnapshot(JSON.parse(snapshot)) : undefined),
|
|
125
|
+
},
|
|
126
|
+
(s) => {
|
|
127
|
+
const oldContext = this.context;
|
|
128
|
+
this.context = s;
|
|
129
|
+
|
|
130
|
+
if (oldContext.items.length !== s.items.length) {
|
|
131
|
+
this.cleanupChildrenObserver?.();
|
|
132
|
+
this.measureChildren();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.requestUpdate();
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
this.state = machineState;
|
|
140
|
+
this.send = send;
|
|
141
|
+
this.context = initialState;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public getId() {
|
|
145
|
+
return this.groupId;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public getPixelSizes() {
|
|
149
|
+
return getPanelGroupPixelSizes(this.context);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public getPercentageSizes() {
|
|
153
|
+
return getPanelGroupPercentageSizes(this.context);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
public getTemplate() {
|
|
157
|
+
return buildTemplate({
|
|
158
|
+
...this.context,
|
|
159
|
+
items: prepareItems(this.context),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public getState() {
|
|
164
|
+
return this.state.current === "idle" ? "idle" : "dragging";
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public setSizes(updates: Unit[]) {
|
|
168
|
+
for (let index = 0; index < updates.length; index++) {
|
|
169
|
+
const item = this.context.items[index];
|
|
170
|
+
const update = updates[index];
|
|
171
|
+
|
|
172
|
+
if (item && isPanelData(item) && update) {
|
|
173
|
+
this.send({
|
|
174
|
+
type: "setPanelPixelSize",
|
|
175
|
+
panelId: item.id,
|
|
176
|
+
size: update,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private measureSize() {
|
|
183
|
+
if (this.observer) {
|
|
184
|
+
this.observer.disconnect();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
this.observer = new ResizeObserver(([entry]) => {
|
|
188
|
+
if (!entry) return;
|
|
189
|
+
if (entry.contentRect.width > 0 && entry.contentRect.height > 0) {
|
|
190
|
+
this.send({ type: "setSize", size: entry.contentRect });
|
|
191
|
+
this.measureChildren();
|
|
192
|
+
this.isPrerender = false;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
this.observer.observe(this.renderRoot.querySelector("div")!);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private measureChildren() {
|
|
200
|
+
if (this.cleanupChildrenObserver) {
|
|
201
|
+
this.cleanupChildrenObserver();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const childrenObserver = new ResizeObserver((childrenEntries) => {
|
|
205
|
+
const childrenSizes: Record<string, { width: number; height: number }> =
|
|
206
|
+
{};
|
|
207
|
+
|
|
208
|
+
for (const childEntry of childrenEntries) {
|
|
209
|
+
const child = childEntry.target as HTMLElement;
|
|
210
|
+
const childId = child.getAttribute("data-splitter-id");
|
|
211
|
+
const childSize = childEntry.borderBoxSize[0];
|
|
212
|
+
|
|
213
|
+
if (childId && childSize) {
|
|
214
|
+
childrenSizes[childId] = {
|
|
215
|
+
width: childSize.inlineSize,
|
|
216
|
+
height: childSize.blockSize,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
this.send({ type: "setActualItemsSize", childrenSizes });
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
if (!this.shadowRoot) return;
|
|
225
|
+
|
|
226
|
+
const slot = this.shadowRoot.querySelector("slot");
|
|
227
|
+
if (!slot) return;
|
|
228
|
+
|
|
229
|
+
const elements = slot.assignedElements();
|
|
230
|
+
|
|
231
|
+
for (const child of elements) {
|
|
232
|
+
childrenObserver.observe(child);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
this.cleanupChildrenObserver = () => {
|
|
236
|
+
childrenObserver.disconnect();
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
firstUpdated() {
|
|
241
|
+
this.send({ type: "unlockGroup" });
|
|
242
|
+
|
|
243
|
+
if (this.observer) {
|
|
244
|
+
this.observer.disconnect();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
this.measureSize();
|
|
248
|
+
this.setAttribute("data-panel-group-wrapper", "");
|
|
249
|
+
this.setAttribute("id", this.groupId);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
disconnectedCallback() {
|
|
253
|
+
this.observer?.disconnect();
|
|
254
|
+
this.cleanupChildrenObserver?.();
|
|
255
|
+
this.send({ type: "lockGroup" });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
render() {
|
|
259
|
+
const template =
|
|
260
|
+
this.context.orientation === "horizontal"
|
|
261
|
+
? `grid-template-columns: ${buildTemplate(this.context)};`
|
|
262
|
+
: `grid-template-rows: ${buildTemplate(this.context)}`;
|
|
263
|
+
|
|
264
|
+
return html`
|
|
265
|
+
<div style="display: grid;height: 100%;${template}">
|
|
266
|
+
<slot></slot>
|
|
267
|
+
</div>
|
|
268
|
+
`;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export class Panel extends LitElement {
|
|
273
|
+
static observedContexts = ["send"];
|
|
274
|
+
|
|
275
|
+
public onCollapseChange?: (newCollapsed: boolean, el: Panel) => void;
|
|
276
|
+
public onResize?: OnResizeCallback;
|
|
277
|
+
public collapseAnimation?: SharedPanelProps<boolean>["collapseAnimation"];
|
|
278
|
+
|
|
279
|
+
@property({ type: Boolean, reflect: true })
|
|
280
|
+
collapsed?: boolean;
|
|
281
|
+
@property({ type: String, reflect: true })
|
|
282
|
+
min?: string;
|
|
283
|
+
@property({ type: String, reflect: true })
|
|
284
|
+
max?: string;
|
|
285
|
+
@property({ type: String, reflect: true })
|
|
286
|
+
default?: string;
|
|
287
|
+
@property({ type: String, reflect: true })
|
|
288
|
+
collapsible?: string;
|
|
289
|
+
@property({ type: String, reflect: true })
|
|
290
|
+
defaultCollapsed?: string;
|
|
291
|
+
@property({ type: String, reflect: true })
|
|
292
|
+
collapsedSize?: string;
|
|
293
|
+
@property({ type: String, reflect: true })
|
|
294
|
+
isStaticAtRest?: string;
|
|
295
|
+
|
|
296
|
+
@consume({ context: contextContext })
|
|
297
|
+
@property({ attribute: false })
|
|
298
|
+
public context: GroupMachineContextValue;
|
|
299
|
+
|
|
300
|
+
@consume({ context: sendContext })
|
|
301
|
+
@property({ attribute: false })
|
|
302
|
+
public send: SendFn;
|
|
303
|
+
|
|
304
|
+
@consume({ context: isPrerenderContext })
|
|
305
|
+
@property({ attribute: false })
|
|
306
|
+
public isPrerender = true;
|
|
307
|
+
|
|
308
|
+
id: string;
|
|
309
|
+
|
|
310
|
+
constructor() {
|
|
311
|
+
super();
|
|
312
|
+
|
|
313
|
+
this.id ||= getNextId();
|
|
314
|
+
this.send = () => {};
|
|
315
|
+
this.context = {} as GroupMachineContextValue;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
public collapse() {
|
|
319
|
+
if (!this.getPanelData()?.collapsible) return;
|
|
320
|
+
this.send({ type: "collapsePanel", panelId: this.id, controlled: true });
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
public isCollapsed() {
|
|
324
|
+
return Boolean(
|
|
325
|
+
this.getPanelData()?.collapsible && this.getPanelData()?.collapsed
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
public expand() {
|
|
330
|
+
if (!this.getPanelData()?.collapsible) return;
|
|
331
|
+
this.send({ type: "expandPanel", panelId: this.id, controlled: true });
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
public isExpanded() {
|
|
335
|
+
return Boolean(
|
|
336
|
+
this.getPanelData()?.collapsible && !this.getPanelData()?.collapsed
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
public getPixelSize() {
|
|
341
|
+
return getPanelPixelSize(this.context, this.id);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
public getPercentageSize() {
|
|
345
|
+
return getPanelPercentageSize(this.context, this.id);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
public setSize(size: Unit) {
|
|
349
|
+
this.send({ type: "setPanelPixelSize", panelId: this.id, size });
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private initPanel() {
|
|
353
|
+
return initializePanel({
|
|
354
|
+
id: this.id,
|
|
355
|
+
min: this.getAttribute("min") as Unit | undefined,
|
|
356
|
+
max: this.getAttribute("max") as Unit | undefined,
|
|
357
|
+
collapsible: isBooleanPropValue(this.getAttribute("collapsible")),
|
|
358
|
+
collapsed: isBooleanPropValue(this.getAttribute("collapsed")),
|
|
359
|
+
collapsedSize: this.getAttribute("collapsedSize") as Unit | undefined,
|
|
360
|
+
onCollapseChange: this.onCollapseChange
|
|
361
|
+
? { current: (e) => this.onCollapseChange?.(e, this) }
|
|
362
|
+
: undefined,
|
|
363
|
+
collapseAnimation: this.collapseAnimation,
|
|
364
|
+
onResize: this.onResize ? { current: this.onResize } : undefined,
|
|
365
|
+
defaultCollapsed: isBooleanPropValue(
|
|
366
|
+
this.getAttribute("defaultCollapsed")
|
|
367
|
+
),
|
|
368
|
+
default: this.getAttribute("default") as Unit | undefined,
|
|
369
|
+
isStaticAtRest: isBooleanPropValue(this.getAttribute("isStaticAtRest")),
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private getPanelData() {
|
|
374
|
+
return this.context?.items.find((item) => item.id === this.id) as
|
|
375
|
+
| PanelData
|
|
376
|
+
| undefined;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
private getAttributes() {
|
|
380
|
+
return {
|
|
381
|
+
...getPanelDomAttributes({
|
|
382
|
+
groupId: this.context.groupId,
|
|
383
|
+
id: this.id,
|
|
384
|
+
collapsible: this.getAttribute("collapsible") === "true",
|
|
385
|
+
collapsed:
|
|
386
|
+
this.getAttribute("collapsed") === "true"
|
|
387
|
+
? true
|
|
388
|
+
: this.getAttribute("collapsed") === "false"
|
|
389
|
+
? false
|
|
390
|
+
: undefined,
|
|
391
|
+
}),
|
|
392
|
+
style: {
|
|
393
|
+
minWidth: 0,
|
|
394
|
+
minHeight: 0,
|
|
395
|
+
overflow: "hidden",
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
protected firstUpdated(_changedProperties: PropertyValues): void {
|
|
401
|
+
updateAttributes(this, this.getAttributes());
|
|
402
|
+
|
|
403
|
+
if (this.getPanelData()) {
|
|
404
|
+
this.send?.({
|
|
405
|
+
type: "rebindPanelCallbacks",
|
|
406
|
+
data: {
|
|
407
|
+
id: this.id,
|
|
408
|
+
onCollapseChange: this.onCollapseChange
|
|
409
|
+
? { current: (e) => this.onCollapseChange?.(e, this) }
|
|
410
|
+
: undefined,
|
|
411
|
+
onResize: this.onResize ? { current: this.onResize } : undefined,
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
} else {
|
|
415
|
+
if (this.isPrerender) {
|
|
416
|
+
this.send({ type: "registerPanel", data: this.initPanel() });
|
|
417
|
+
} else {
|
|
418
|
+
const groupElement = this.closest("window-splitter");
|
|
419
|
+
if (!groupElement) return;
|
|
420
|
+
|
|
421
|
+
const order = Array.from(groupElement.children).indexOf(this);
|
|
422
|
+
if (typeof order !== "number") return;
|
|
423
|
+
|
|
424
|
+
this.send({
|
|
425
|
+
type: "registerDynamicPanel",
|
|
426
|
+
data: { ...this.initPanel(), order },
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
disconnectedCallback(): void {
|
|
433
|
+
requestAnimationFrame(() =>
|
|
434
|
+
this.send({ type: "unregisterPanel", id: this.id })
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
updated(changedProperties: PropertyValues) {
|
|
439
|
+
if (changedProperties.has("collapsed")) {
|
|
440
|
+
const isControlled = this.getPanelData()?.collapseIsControlled;
|
|
441
|
+
|
|
442
|
+
if (isControlled) {
|
|
443
|
+
if (this.collapsed) {
|
|
444
|
+
this.send({
|
|
445
|
+
type: "collapsePanel",
|
|
446
|
+
panelId: this.id,
|
|
447
|
+
controlled: true,
|
|
448
|
+
});
|
|
449
|
+
} else {
|
|
450
|
+
this.send({
|
|
451
|
+
type: "expandPanel",
|
|
452
|
+
panelId: this.id,
|
|
453
|
+
controlled: true,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (
|
|
460
|
+
changedProperties.has("min") ||
|
|
461
|
+
changedProperties.has("max") ||
|
|
462
|
+
changedProperties.has("default") ||
|
|
463
|
+
changedProperties.has("collapsible") ||
|
|
464
|
+
changedProperties.has("collapsedSize") ||
|
|
465
|
+
changedProperties.has("defaultCollapsed") ||
|
|
466
|
+
changedProperties.has("isStaticAtRest")
|
|
467
|
+
) {
|
|
468
|
+
const currentPanelData = this.getPanelData();
|
|
469
|
+
|
|
470
|
+
if (
|
|
471
|
+
currentPanelData &&
|
|
472
|
+
haveConstraintsChangedForPanel(currentPanelData, this.initPanel())
|
|
473
|
+
) {
|
|
474
|
+
this.send({ type: "updateConstraints", data: this.initPanel() });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
render() {
|
|
480
|
+
return html`<slot></slot>`;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
export class PanelResizer extends LitElement {
|
|
485
|
+
static observedContexts = ["send"];
|
|
486
|
+
|
|
487
|
+
public onDragStart?: () => void;
|
|
488
|
+
public onDrag?: (e: Parameters<NonNullable<MoveEvents["onMove"]>>[0]) => void;
|
|
489
|
+
public onDragEnd?: () => void;
|
|
490
|
+
|
|
491
|
+
@consume({ context: contextContext })
|
|
492
|
+
@property({ attribute: false })
|
|
493
|
+
public context: GroupMachineContextValue;
|
|
494
|
+
@consume({ context: sendContext })
|
|
495
|
+
@property({ attribute: false })
|
|
496
|
+
public send: SendFn;
|
|
497
|
+
@consume({ context: isPrerenderContext })
|
|
498
|
+
@property({ attribute: false })
|
|
499
|
+
public isPrerender = true;
|
|
500
|
+
|
|
501
|
+
@property({ type: String, reflect: true })
|
|
502
|
+
size?: string;
|
|
503
|
+
@property({ type: Boolean, reflect: true })
|
|
504
|
+
disabled?: boolean;
|
|
505
|
+
|
|
506
|
+
constructor() {
|
|
507
|
+
super();
|
|
508
|
+
|
|
509
|
+
this.id ||= getNextId();
|
|
510
|
+
this.send = () => {};
|
|
511
|
+
this.context = {} as GroupMachineContextValue;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
private initPanelResizer() {
|
|
515
|
+
return initializePanelHandleData({
|
|
516
|
+
id: this.id,
|
|
517
|
+
size: (this.getAttribute("size") as PixelUnit | undefined) || "0px",
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
private getHandleData() {
|
|
522
|
+
return this.context?.items.find((item) => item.id === this.id) as
|
|
523
|
+
| PanelHandleData
|
|
524
|
+
| undefined;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
private getAttributes() {
|
|
528
|
+
const handleIndex = this.context?.items.findIndex(
|
|
529
|
+
(item) => item.id === this.id
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
const panelBeforeHandle =
|
|
533
|
+
handleIndex !== undefined &&
|
|
534
|
+
isPanelData(this.context?.items[handleIndex - 1])
|
|
535
|
+
? (this.context?.items[handleIndex - 1] as PanelData | undefined)
|
|
536
|
+
: undefined;
|
|
537
|
+
const handleData = this.getHandleData() || this.initPanelResizer();
|
|
538
|
+
|
|
539
|
+
return {
|
|
540
|
+
...getPanelResizerDomAttributes({
|
|
541
|
+
groupId: this.context?.groupId,
|
|
542
|
+
id: this.id,
|
|
543
|
+
orientation: this.context?.orientation || "horizontal",
|
|
544
|
+
isDragging: this.context?.activeDragHandleId === this.id,
|
|
545
|
+
activeDragHandleId: this.context?.activeDragHandleId,
|
|
546
|
+
disabled: this.disabled,
|
|
547
|
+
controlsId: panelBeforeHandle?.id,
|
|
548
|
+
min: panelBeforeHandle?.min,
|
|
549
|
+
max: panelBeforeHandle?.max,
|
|
550
|
+
currentValue: panelBeforeHandle?.currentValue,
|
|
551
|
+
groupSize: getGroupSize(this.context),
|
|
552
|
+
}),
|
|
553
|
+
tabIndex: this.disabled ? -1 : 0,
|
|
554
|
+
style: {
|
|
555
|
+
cursor: this.context ? getCursor(this.context) : undefined,
|
|
556
|
+
width:
|
|
557
|
+
this.context?.orientation === "horizontal"
|
|
558
|
+
? `${handleData.size.value.toNumber()}px`
|
|
559
|
+
: "100%",
|
|
560
|
+
height:
|
|
561
|
+
this.context?.orientation === "vertical"
|
|
562
|
+
? `${handleData.size.value.toNumber()}px`
|
|
563
|
+
: "100%",
|
|
564
|
+
},
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
protected firstUpdated(_changedProperties: PropertyValues): void {
|
|
569
|
+
const { moveProps } = move({
|
|
570
|
+
onMoveStart: () => {
|
|
571
|
+
this.send({
|
|
572
|
+
type: "dragHandleStart",
|
|
573
|
+
handleId: this.id,
|
|
574
|
+
});
|
|
575
|
+
this.onDragStart?.();
|
|
576
|
+
document.body.style.cursor = getCursor(this.context) || "auto";
|
|
577
|
+
},
|
|
578
|
+
onMove: (e) => {
|
|
579
|
+
this.send({
|
|
580
|
+
type: "dragHandle",
|
|
581
|
+
handleId: this.id,
|
|
582
|
+
value: e,
|
|
583
|
+
});
|
|
584
|
+
this.onDrag?.(e);
|
|
585
|
+
},
|
|
586
|
+
onMoveEnd: () => {
|
|
587
|
+
this.send({ type: "dragHandleEnd", handleId: this.id });
|
|
588
|
+
this.onDragEnd?.();
|
|
589
|
+
document.body.style.cursor = "auto";
|
|
590
|
+
},
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
this.addEventListener("pointerdown", (e) => {
|
|
594
|
+
if (this.disabled) return;
|
|
595
|
+
moveProps.onPointerDown(e);
|
|
596
|
+
});
|
|
597
|
+
this.addEventListener("keydown", (e) => {
|
|
598
|
+
moveProps.onKeyDown(e);
|
|
599
|
+
|
|
600
|
+
const collapsiblePanel = getCollapsiblePanelForHandleId(
|
|
601
|
+
this.context,
|
|
602
|
+
this.id
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
if (e.key === "Enter" && collapsiblePanel) {
|
|
606
|
+
if (collapsiblePanel.collapsed) {
|
|
607
|
+
this.send({ type: "expandPanel", panelId: collapsiblePanel.id });
|
|
608
|
+
} else {
|
|
609
|
+
this.send({ type: "collapsePanel", panelId: collapsiblePanel.id });
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
updateAttributes(this, this.getAttributes());
|
|
615
|
+
|
|
616
|
+
if (!this.getHandleData()) {
|
|
617
|
+
if (this.isPrerender) {
|
|
618
|
+
this.send({
|
|
619
|
+
type: "registerPanelHandle",
|
|
620
|
+
data: this.initPanelResizer(),
|
|
621
|
+
});
|
|
622
|
+
} else {
|
|
623
|
+
const groupElement = this.closest("window-splitter");
|
|
624
|
+
if (!groupElement) return;
|
|
625
|
+
|
|
626
|
+
const order = Array.from(groupElement.children).indexOf(this);
|
|
627
|
+
if (typeof order !== "number") return;
|
|
628
|
+
|
|
629
|
+
this.send({
|
|
630
|
+
type: "registerPanelHandle",
|
|
631
|
+
data: { ...this.initPanelResizer(), order },
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
updated(changedProperties: PropertyValues) {
|
|
638
|
+
if (changedProperties.has("size")) {
|
|
639
|
+
const currentHandleData = this.getHandleData();
|
|
640
|
+
if (
|
|
641
|
+
currentHandleData &&
|
|
642
|
+
haveConstraintsChangedForPanelHandle(
|
|
643
|
+
currentHandleData,
|
|
644
|
+
this.initPanelResizer()
|
|
645
|
+
)
|
|
646
|
+
) {
|
|
647
|
+
this.send({ type: "updateConstraints", data: this.initPanelResizer() });
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
disconnectedCallback(): void {
|
|
652
|
+
requestAnimationFrame(() =>
|
|
653
|
+
this.send({ type: "unregisterPanelHandle", id: this.id })
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
render() {
|
|
658
|
+
return html`<slot></slot>`;
|
|
659
|
+
}
|
|
660
|
+
}
|
package/tsconfig.json
ADDED
package/vitest.config.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
coverage: {
|
|
6
|
+
provider: "istanbul",
|
|
7
|
+
include: ["**/*.vue", "!**/*.stories.ts", "!**/*.test.ts"],
|
|
8
|
+
reporter: ["text", "html", "json-summary", "json"],
|
|
9
|
+
reportOnFailure: true,
|
|
10
|
+
},
|
|
11
|
+
browser: {
|
|
12
|
+
enabled: true,
|
|
13
|
+
provider: "playwright",
|
|
14
|
+
instances: [{ browser: "chromium" }],
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
});
|