@kerfjs/ui 5.0.0-beta.53 → 5.0.0-beta.54
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/README.md +1 -0
- package/ai/application-ui-diagnostic-ids-v1.json +1 -0
- package/ai/component-catalog-v2.json +82 -6
- package/ai/component-catalog-v2.schema.json +2 -1
- package/ai/component-catalog.json +105 -10
- package/ai/public-api-signatures-v1.md +204 -18
- package/ai/skill.md +23 -17
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/analyzer/index.mjs +48 -0
- package/analyzer/loud-pairs.d.mts +49 -0
- package/analyzer/loud-pairs.mjs +357 -0
- package/dist/app-tab.d.ts +7 -5
- package/dist/app-tab.js +1 -1
- package/dist/{chunk-EKYZCRCX.js → chunk-3CGQ7ZNO.js} +54 -22
- package/dist/chunk-3CGQ7ZNO.js.map +1 -0
- package/dist/{chunk-UKKSMZYT.js → chunk-46263QLK.js} +51 -1
- package/dist/chunk-46263QLK.js.map +1 -0
- package/dist/{chunk-NDUGJ4TU.js → chunk-5NDQABWK.js} +1 -1
- package/dist/{chunk-NDUGJ4TU.js.map → chunk-5NDQABWK.js.map} +1 -1
- package/dist/{chunk-CCMKJWLX.js → chunk-NE5JAOQS.js} +7 -7
- package/dist/chunk-NE5JAOQS.js.map +1 -0
- package/dist/chunk-PSHUSWQH.js +18 -0
- package/dist/chunk-PSHUSWQH.js.map +1 -0
- package/dist/{chunk-VOCPXGQN.js → chunk-VHADGP3B.js} +9 -8
- package/dist/chunk-VHADGP3B.js.map +1 -0
- package/dist/chunk-XQV2B4IL.js +14 -0
- package/dist/chunk-XQV2B4IL.js.map +1 -0
- package/dist/collapsible-panel.d.ts +6 -2
- package/dist/collapsible-panel.js +1 -0
- package/dist/collapsible-panel.js.map +1 -1
- package/dist/floating-toolbar.d.ts +5 -2
- package/dist/floating-toolbar.js +1 -1
- package/dist/index.js +6 -5
- package/dist/resizable-region.d.ts +4 -1
- package/dist/resizable-region.js +1 -1
- package/dist/split-view.js +1 -1
- package/dist/styles/collapsible-panel.css +88 -10
- package/dist/styles/floating-toolbar.css +11 -6
- package/dist/styles/resizable-region.css +81 -13
- package/dist/styles/workbench.css +472 -10
- package/dist/wire-resizable-regions.d.ts +2 -1
- package/dist/wire-resizable-regions.js +2 -1
- package/dist/wire-tab-bars.js +1 -1
- package/dist/wire-workbench.d.ts +109 -0
- package/dist/wire-workbench.js +355 -0
- package/dist/wire-workbench.js.map +1 -0
- package/dist/workbench.d.ts +65 -6
- package/dist/workbench.js +180 -76
- package/dist/workbench.js.map +1 -1
- package/docs/accessibility.md +54 -9
- package/docs/app-layouts.md +12 -1
- package/docs/collapsible-panel.md +59 -1
- package/docs/component-selection.md +16 -6
- package/docs/document-baseline.md +4 -0
- package/docs/layout.md +5 -1
- package/docs/recipes.md +5 -3
- package/docs/type-contracts.md +2 -1
- package/docs/ui-analyzer.md +94 -19
- package/docs/ui-doctor.md +6 -0
- package/docs/ux-demo.md +1 -1
- package/docs/webawesome-theme.md +11 -2
- package/docs/workbench.md +302 -7
- package/llms.txt +7 -3
- package/package.json +5 -1
- package/ux-demo/recipes/collapsible-sidebar.tsx +8 -7
- package/dist/chunk-CCMKJWLX.js.map +0 -1
- package/dist/chunk-EKYZCRCX.js.map +0 -1
- package/dist/chunk-UKKSMZYT.js.map +0 -1
- package/dist/chunk-VOCPXGQN.js.map +0 -1
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
.kui-workbench {
|
|
2
2
|
position: relative;
|
|
3
|
+
/* The rail and drawer overlays and the restore controls stack within the
|
|
4
|
+
Workbench, never against the page around it. */
|
|
5
|
+
isolation: isolate;
|
|
3
6
|
display: flex;
|
|
4
7
|
min-width: 0;
|
|
5
8
|
min-height: 0;
|
|
@@ -39,6 +42,9 @@
|
|
|
39
42
|
var(--kui-workbench-rail-width, 17.5rem) +
|
|
40
43
|
var(--_kui-workbench-rail-safe-inset)
|
|
41
44
|
);
|
|
45
|
+
/* The size is the whole track, separator border included, in or out of
|
|
46
|
+
flow and whatever box model the app defaults to. */
|
|
47
|
+
box-sizing: border-box;
|
|
42
48
|
flex: 0 0 var(--_kui-workbench-rail-extent);
|
|
43
49
|
min-width: 0;
|
|
44
50
|
overflow: hidden;
|
|
@@ -69,7 +75,10 @@
|
|
|
69
75
|
border-color: transparent;
|
|
70
76
|
}
|
|
71
77
|
|
|
78
|
+
/* The center is the overlay drawer's containing block, so a drawer that
|
|
79
|
+
leaves the flow still covers exactly the work-area column it docks under. */
|
|
72
80
|
.kui-workbench__center {
|
|
81
|
+
position: relative;
|
|
73
82
|
--kui-edge-inset-block-start: var(--_kui-workbench-safe-block-start);
|
|
74
83
|
--kui-edge-inset-block-end: var(--_kui-workbench-safe-block-end);
|
|
75
84
|
--kui-edge-inset-inline-start: var(--_kui-workbench-safe-inline-start);
|
|
@@ -185,6 +194,7 @@
|
|
|
185
194
|
var(--kui-workbench-drawer-height, 13.75rem) +
|
|
186
195
|
var(--_kui-workbench-safe-block-end)
|
|
187
196
|
);
|
|
197
|
+
box-sizing: border-box;
|
|
188
198
|
position: relative;
|
|
189
199
|
flex: 0 0 var(--_kui-workbench-drawer-extent);
|
|
190
200
|
min-height: 0;
|
|
@@ -253,16 +263,197 @@
|
|
|
253
263
|
transform: translateY(100%);
|
|
254
264
|
}
|
|
255
265
|
|
|
266
|
+
/* Opt-in resizable panels. The size lives in the resize wiring's track
|
|
267
|
+
property, which the wiring updates live during a drag, so the rail width or
|
|
268
|
+
drawer height follows it; collapsing leaves it alone, so the panel expands
|
|
269
|
+
back to the size it had. These rules precede the presentation rules below,
|
|
270
|
+
which must keep winning for overlay and hidden panels. */
|
|
271
|
+
.kui-workbench__rail[data-resizable="true"] {
|
|
272
|
+
--kui-workbench-rail-width: var(--kui-resizable-region-size);
|
|
273
|
+
position: relative;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.kui-workbench__drawer[data-resizable="true"] {
|
|
277
|
+
--kui-workbench-drawer-height: var(--kui-resizable-region-size);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* The work area keeps a minimum width beside the rails (the Workbench's
|
|
281
|
+
`mainMinSize`) and a minimum height above the drawer (`mainMinHeight`),
|
|
282
|
+
never larger than the Workbench itself. Inline panels, fixed or resizable,
|
|
283
|
+
give way instead: when the container narrows the rails shrink in proportion
|
|
284
|
+
to their sizes, when it gets shorter the drawer shrinks, and their content
|
|
285
|
+
follows the shown size. The wiring stops a resize where the work area would
|
|
286
|
+
drop below the minimum. */
|
|
287
|
+
.kui-workbench[data-main-min-size] > .kui-workbench__center {
|
|
288
|
+
min-width: min(var(--_kui-workbench-main-min-width), 100%);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.kui-workbench[data-main-min-height]
|
|
292
|
+
> .kui-workbench__center
|
|
293
|
+
> .kui-workbench__main {
|
|
294
|
+
min-height: min(var(--_kui-workbench-main-min-height), 100%);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.kui-workbench__rail[data-presentation="inline"]:not([data-collapsed="true"]),
|
|
298
|
+
.kui-workbench__drawer[data-presentation="inline"]:not(
|
|
299
|
+
[data-collapsed="true"]
|
|
300
|
+
) {
|
|
301
|
+
flex-shrink: 1;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* Low specificity, so a responsive overlay (below) restores the fixed size. */
|
|
305
|
+
.kui-workbench__rail:where(
|
|
306
|
+
[data-presentation="inline"]:not([data-collapsed="true"])
|
|
307
|
+
)
|
|
308
|
+
> .kui-workbench__panel-content {
|
|
309
|
+
width: 100%;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.kui-workbench__drawer:where(
|
|
313
|
+
[data-presentation="inline"]:not([data-collapsed="true"])
|
|
314
|
+
)
|
|
315
|
+
> .kui-workbench__panel-content {
|
|
316
|
+
height: 100%;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* The panel clips its sliding content, so a scrolling panel scrolls its
|
|
320
|
+
content box instead of itself: the separator handle is a child of the panel
|
|
321
|
+
and must not scroll away with the content. */
|
|
322
|
+
.kui-workbench__rail[data-resizable="true"][data-content-overflow="auto"],
|
|
323
|
+
.kui-workbench__drawer[data-resizable="true"][data-content-overflow="auto"] {
|
|
324
|
+
overflow: hidden;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.kui-workbench__rail[data-resizable="true"][data-content-overflow="auto"]
|
|
328
|
+
> .kui-workbench__panel-content,
|
|
329
|
+
.kui-workbench__drawer[data-resizable="true"][data-content-overflow="auto"]
|
|
330
|
+
> .kui-workbench__panel-content {
|
|
331
|
+
overflow: auto;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/* The separator handle: the 20px hit target and dormant grip a
|
|
335
|
+
ResizableRegion handle has. The panel's own border stays the visible
|
|
336
|
+
separator line. By default the target sits wholly inside the panel, at its
|
|
337
|
+
inner edge, because the panel clips its sliding content. */
|
|
338
|
+
.kui-workbench__handle {
|
|
339
|
+
position: absolute;
|
|
340
|
+
z-index: 3;
|
|
341
|
+
display: grid;
|
|
342
|
+
color: var(--kui-color-neutral-on-quiet);
|
|
343
|
+
place-items: center;
|
|
344
|
+
touch-action: none;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.kui-workbench__rail > .kui-workbench__handle {
|
|
348
|
+
inset-block: 0;
|
|
349
|
+
width: 20px;
|
|
350
|
+
cursor: col-resize;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.kui-workbench__rail--left > .kui-workbench__handle {
|
|
354
|
+
inset-inline-end: 0;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.kui-workbench__rail--right > .kui-workbench__handle {
|
|
358
|
+
inset-inline-start: 0;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.kui-workbench__drawer > .kui-workbench__handle {
|
|
362
|
+
inset-inline: 0;
|
|
363
|
+
inset-block-start: 0;
|
|
364
|
+
height: 20px;
|
|
365
|
+
cursor: row-resize;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.kui-workbench__rail[data-collapsed="true"] > .kui-workbench__handle,
|
|
369
|
+
.kui-workbench__drawer[data-collapsed="true"] > .kui-workbench__handle,
|
|
370
|
+
.kui-workbench__rail:not([data-presentation="inline"]) > .kui-workbench__handle,
|
|
371
|
+
.kui-workbench__drawer:not([data-presentation="inline"])
|
|
372
|
+
> .kui-workbench__handle {
|
|
373
|
+
display: none;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/* Where the engine can extend a clip past the panel's edge, an expanded
|
|
377
|
+
panel lets exactly the handle's outer half through, so the hit target
|
|
378
|
+
straddles the separator like a ResizableRegion handle does. Collapsing drops
|
|
379
|
+
the margin again before the content slides out. */
|
|
380
|
+
@supports (overflow-clip-margin: 10px) {
|
|
381
|
+
.kui-workbench__rail[data-resizable="true"]:not(
|
|
382
|
+
[data-collapsed="true"],
|
|
383
|
+
[data-content-overflow="visible"]
|
|
384
|
+
),
|
|
385
|
+
.kui-workbench__drawer[data-resizable="true"]:not(
|
|
386
|
+
[data-collapsed="true"],
|
|
387
|
+
[data-content-overflow="visible"]
|
|
388
|
+
) {
|
|
389
|
+
overflow: clip;
|
|
390
|
+
overflow-clip-margin: 10px;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.kui-workbench__rail--left[data-resizable="true"] > .kui-workbench__handle {
|
|
394
|
+
inset-inline-end: -10px;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.kui-workbench__rail--right[data-resizable="true"] > .kui-workbench__handle {
|
|
398
|
+
inset-inline-start: -10px;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.kui-workbench__drawer[data-resizable="true"] > .kui-workbench__handle {
|
|
402
|
+
inset-block-start: -10px;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.kui-workbench__handle:hover,
|
|
407
|
+
.kui-workbench__handle:focus-visible {
|
|
408
|
+
color: var(--kui-color-text-link);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/* The ring stays wholly inside the handle's box, as a ResizableRegion
|
|
412
|
+
handle's does. */
|
|
413
|
+
.kui-workbench__handle:focus-visible {
|
|
414
|
+
border-radius: var(--kui-radius-s);
|
|
415
|
+
outline: var(--kui-focus-ring);
|
|
416
|
+
outline-offset: -3px;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.kui-workbench__handle-icon {
|
|
420
|
+
position: relative;
|
|
421
|
+
display: grid;
|
|
422
|
+
width: 0.9rem;
|
|
423
|
+
height: 0.9rem;
|
|
424
|
+
opacity: 0;
|
|
425
|
+
pointer-events: none;
|
|
426
|
+
place-items: center;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.kui-workbench__handle-icon > svg {
|
|
430
|
+
width: 100%;
|
|
431
|
+
height: 100%;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.kui-workbench__handle:hover .kui-workbench__handle-icon,
|
|
435
|
+
.kui-workbench__handle:focus-visible .kui-workbench__handle-icon {
|
|
436
|
+
opacity: 1;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/* Overlay stacking order: both rails stack above the bottom drawer, one below
|
|
440
|
+
the overlay z-index, as inline rails span the Workbench's full height beside
|
|
441
|
+
the drawer's column; the right rail stacks above the left (document order).
|
|
442
|
+
Restore controls stack below every overlay (see .kui-workbench__restore). */
|
|
256
443
|
.kui-workbench__rail[data-presentation="overlay"],
|
|
257
444
|
.kui-workbench__drawer[data-presentation="overlay"] {
|
|
258
445
|
position: absolute;
|
|
259
446
|
z-index: var(--kui-workbench-overlay-z, 41);
|
|
260
|
-
max-width: var(--kui-workbench-overlay-max-width, 85vw);
|
|
261
|
-
max-height: var(--kui-workbench-overlay-max-height, 85vh);
|
|
262
447
|
box-shadow: var(--kui-shadow-l);
|
|
263
448
|
}
|
|
264
449
|
|
|
450
|
+
/* An out-of-flow rail sizes to its content plus its border, so it takes its
|
|
451
|
+
extent explicitly, as its in-flow flex basis does, border included. Each
|
|
452
|
+
overlay maximum caps only the panel's own axis: a rail spans the
|
|
453
|
+
Workbench's full height and a drawer its full width. */
|
|
265
454
|
.kui-workbench__rail[data-presentation="overlay"] {
|
|
455
|
+
width: var(--_kui-workbench-rail-extent);
|
|
456
|
+
max-width: var(--kui-workbench-overlay-max-width, 85vw);
|
|
266
457
|
inset-block: 0;
|
|
267
458
|
}
|
|
268
459
|
|
|
@@ -274,41 +465,312 @@
|
|
|
274
465
|
inset-inline-end: 0;
|
|
275
466
|
}
|
|
276
467
|
|
|
468
|
+
/* The drawer's content is absolutely positioned against its bottom edge, so
|
|
469
|
+
an out-of-flow drawer has no content height of its own: it takes its extent
|
|
470
|
+
explicitly, as the in-flow track does through its flex basis. */
|
|
277
471
|
.kui-workbench__drawer[data-presentation="overlay"] {
|
|
472
|
+
z-index: calc(var(--kui-workbench-overlay-z, 41) - 1);
|
|
473
|
+
height: var(--_kui-workbench-drawer-extent);
|
|
474
|
+
max-height: var(--kui-workbench-overlay-max-height, 85vh);
|
|
278
475
|
inset-inline: 0;
|
|
279
476
|
inset-block-end: 0;
|
|
280
477
|
}
|
|
281
478
|
|
|
479
|
+
/* A collapsed overlay keeps its box for the slide-out, so it drops its surface
|
|
480
|
+
and shadow; the sliding content carries the surface instead. */
|
|
282
481
|
.kui-workbench__rail[data-presentation="overlay"][data-collapsed="true"],
|
|
283
482
|
.kui-workbench__drawer[data-presentation="overlay"][data-collapsed="true"] {
|
|
483
|
+
background: transparent;
|
|
484
|
+
box-shadow: none;
|
|
284
485
|
pointer-events: none;
|
|
285
486
|
}
|
|
286
487
|
|
|
488
|
+
/* Overlay content fills the rail inside its separator border, so it is
|
|
489
|
+
clamped with the rail and carries the surface through the slide-out. */
|
|
490
|
+
.kui-workbench__rail[data-presentation="overlay"]
|
|
491
|
+
> .kui-workbench__panel-content {
|
|
492
|
+
width: 100%;
|
|
493
|
+
background: var(--kui-color-surface);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/* The overlay content fills the drawer box below its separator border, so it
|
|
497
|
+
is clamped with the box and carries the surface through the slide-out. */
|
|
498
|
+
.kui-workbench__drawer[data-presentation="overlay"]
|
|
499
|
+
> .kui-workbench__panel-content {
|
|
500
|
+
height: 100%;
|
|
501
|
+
background: var(--kui-color-surface);
|
|
502
|
+
}
|
|
503
|
+
|
|
287
504
|
.kui-workbench__rail[data-presentation="hidden"],
|
|
288
505
|
.kui-workbench__drawer[data-presentation="hidden"] {
|
|
289
506
|
display: none;
|
|
290
507
|
}
|
|
291
508
|
|
|
509
|
+
/* Responsive overlay panels. A rail or the drawer with `responsiveOverlayAt`
|
|
510
|
+
presents as an overlay — out of flow, over the work area, without a
|
|
511
|
+
separator — below the Workbench's own container breakpoint (`narrow`: 704px
|
|
512
|
+
or less, `compact`: 448px or less, as `ResizableRegion`'s
|
|
513
|
+
`responsiveFillAt`), and inline above it. The Workbench becomes a named
|
|
514
|
+
inline-size container only when a panel opts in. The work area keeps its
|
|
515
|
+
safe-area inset under an overlay panel. */
|
|
516
|
+
.kui-workbench:has(
|
|
517
|
+
> .kui-workbench__rail[data-responsive-overlay-at],
|
|
518
|
+
> .kui-workbench__center > .kui-workbench__drawer[data-responsive-overlay-at]
|
|
519
|
+
) {
|
|
520
|
+
container: kui-workbench / inline-size;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
@container kui-workbench (max-width: 44rem) {
|
|
524
|
+
.kui-workbench__rail[data-responsive-overlay-at="narrow"] {
|
|
525
|
+
position: absolute;
|
|
526
|
+
z-index: var(--kui-workbench-overlay-z, 41);
|
|
527
|
+
width: var(--_kui-workbench-rail-extent);
|
|
528
|
+
max-width: var(--kui-workbench-overlay-max-width, 85vw);
|
|
529
|
+
inset-block: 0;
|
|
530
|
+
box-shadow: var(--kui-shadow-l);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.kui-workbench__rail--left[data-responsive-overlay-at="narrow"] {
|
|
534
|
+
inset-inline-start: 0;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.kui-workbench__rail--right[data-responsive-overlay-at="narrow"] {
|
|
538
|
+
inset-inline-end: 0;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
.kui-workbench__rail[data-responsive-overlay-at="narrow"][data-collapsed="true"] {
|
|
542
|
+
background: transparent;
|
|
543
|
+
box-shadow: none;
|
|
544
|
+
pointer-events: none;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.kui-workbench__rail[data-responsive-overlay-at="narrow"]
|
|
548
|
+
> .kui-workbench__panel-content {
|
|
549
|
+
width: 100%;
|
|
550
|
+
background: var(--kui-color-surface);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.kui-workbench__rail[data-responsive-overlay-at="narrow"]
|
|
554
|
+
> .kui-workbench__handle {
|
|
555
|
+
display: none;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.kui-workbench:has(
|
|
559
|
+
> .kui-workbench__rail--left[data-responsive-overlay-at="narrow"][data-presentation="inline"]
|
|
560
|
+
)
|
|
561
|
+
> .kui-workbench__center {
|
|
562
|
+
--kui-edge-inset-inline-start: var(--_kui-workbench-safe-inline-start);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.kui-workbench:has(
|
|
566
|
+
> .kui-workbench__rail--right[data-responsive-overlay-at="narrow"][data-presentation="inline"]
|
|
567
|
+
)
|
|
568
|
+
> .kui-workbench__center {
|
|
569
|
+
--kui-edge-inset-inline-end: var(--_kui-workbench-safe-inline-end);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.kui-workbench__drawer[data-responsive-overlay-at="narrow"] {
|
|
573
|
+
position: absolute;
|
|
574
|
+
anchor-name: none;
|
|
575
|
+
z-index: calc(var(--kui-workbench-overlay-z, 41) - 1);
|
|
576
|
+
height: var(--_kui-workbench-drawer-extent);
|
|
577
|
+
max-height: var(--kui-workbench-overlay-max-height, 85vh);
|
|
578
|
+
inset-inline: 0;
|
|
579
|
+
inset-block-end: 0;
|
|
580
|
+
box-shadow: var(--kui-shadow-l);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.kui-workbench__drawer[data-responsive-overlay-at="narrow"][data-collapsed="true"] {
|
|
584
|
+
background: transparent;
|
|
585
|
+
box-shadow: none;
|
|
586
|
+
pointer-events: none;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
.kui-workbench__drawer[data-responsive-overlay-at="narrow"]
|
|
590
|
+
> .kui-workbench__panel-content {
|
|
591
|
+
height: 100%;
|
|
592
|
+
background: var(--kui-color-surface);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.kui-workbench__drawer[data-responsive-overlay-at="narrow"]
|
|
596
|
+
> .kui-workbench__handle {
|
|
597
|
+
display: none;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
.kui-workbench__main:has(
|
|
601
|
+
~ .kui-workbench__drawer[data-responsive-overlay-at="narrow"][data-presentation="inline"]
|
|
602
|
+
) {
|
|
603
|
+
--kui-edge-inset-block-end: var(--_kui-workbench-safe-block-end);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
@container kui-workbench (max-width: 28rem) {
|
|
608
|
+
.kui-workbench__rail[data-responsive-overlay-at="compact"] {
|
|
609
|
+
position: absolute;
|
|
610
|
+
z-index: var(--kui-workbench-overlay-z, 41);
|
|
611
|
+
width: var(--_kui-workbench-rail-extent);
|
|
612
|
+
max-width: var(--kui-workbench-overlay-max-width, 85vw);
|
|
613
|
+
inset-block: 0;
|
|
614
|
+
box-shadow: var(--kui-shadow-l);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
.kui-workbench__rail--left[data-responsive-overlay-at="compact"] {
|
|
618
|
+
inset-inline-start: 0;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.kui-workbench__rail--right[data-responsive-overlay-at="compact"] {
|
|
622
|
+
inset-inline-end: 0;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.kui-workbench__rail[data-responsive-overlay-at="compact"][data-collapsed="true"] {
|
|
626
|
+
background: transparent;
|
|
627
|
+
box-shadow: none;
|
|
628
|
+
pointer-events: none;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.kui-workbench__rail[data-responsive-overlay-at="compact"]
|
|
632
|
+
> .kui-workbench__panel-content {
|
|
633
|
+
width: 100%;
|
|
634
|
+
background: var(--kui-color-surface);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.kui-workbench__rail[data-responsive-overlay-at="compact"]
|
|
638
|
+
> .kui-workbench__handle {
|
|
639
|
+
display: none;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
.kui-workbench:has(
|
|
643
|
+
> .kui-workbench__rail--left[data-responsive-overlay-at="compact"][data-presentation="inline"]
|
|
644
|
+
)
|
|
645
|
+
> .kui-workbench__center {
|
|
646
|
+
--kui-edge-inset-inline-start: var(--_kui-workbench-safe-inline-start);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
.kui-workbench:has(
|
|
650
|
+
> .kui-workbench__rail--right[data-responsive-overlay-at="compact"][data-presentation="inline"]
|
|
651
|
+
)
|
|
652
|
+
> .kui-workbench__center {
|
|
653
|
+
--kui-edge-inset-inline-end: var(--_kui-workbench-safe-inline-end);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.kui-workbench__drawer[data-responsive-overlay-at="compact"] {
|
|
657
|
+
position: absolute;
|
|
658
|
+
anchor-name: none;
|
|
659
|
+
z-index: calc(var(--kui-workbench-overlay-z, 41) - 1);
|
|
660
|
+
height: var(--_kui-workbench-drawer-extent);
|
|
661
|
+
max-height: var(--kui-workbench-overlay-max-height, 85vh);
|
|
662
|
+
inset-inline: 0;
|
|
663
|
+
inset-block-end: 0;
|
|
664
|
+
box-shadow: var(--kui-shadow-l);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.kui-workbench__drawer[data-responsive-overlay-at="compact"][data-collapsed="true"] {
|
|
668
|
+
background: transparent;
|
|
669
|
+
box-shadow: none;
|
|
670
|
+
pointer-events: none;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
.kui-workbench__drawer[data-responsive-overlay-at="compact"]
|
|
674
|
+
> .kui-workbench__panel-content {
|
|
675
|
+
height: 100%;
|
|
676
|
+
background: var(--kui-color-surface);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
.kui-workbench__drawer[data-responsive-overlay-at="compact"]
|
|
680
|
+
> .kui-workbench__handle {
|
|
681
|
+
display: none;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.kui-workbench__main:has(
|
|
685
|
+
~ .kui-workbench__drawer[data-responsive-overlay-at="compact"][data-presentation="inline"]
|
|
686
|
+
) {
|
|
687
|
+
--kui-edge-inset-block-end: var(--_kui-workbench-safe-block-end);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/* A collapsed panel's restore control floats in a corner of the Workbench
|
|
692
|
+
itself, never of the viewport, so an embedded Workbench cannot float it over
|
|
693
|
+
page chrome outside it. It is inset from the corner plus the unsafe area of
|
|
694
|
+
each edge that corner reaches. The drawer's control sits in the work-area
|
|
695
|
+
column it restores (the center is its containing block), inset from the
|
|
696
|
+
edges that column still reaches, so it never lands on an expanded rail.
|
|
697
|
+
It belongs to the layer beneath the overlays, two below the overlay
|
|
698
|
+
z-index: an open overlay covers it as it covers the rest of the work area,
|
|
699
|
+
so no other panel's control floats over the overlay's content. A collapsed
|
|
700
|
+
overlay drops its pointer events, so the control stays usable beneath it. */
|
|
292
701
|
.kui-workbench__restore {
|
|
293
|
-
|
|
294
|
-
|
|
702
|
+
--_kui-workbench-restore-inset: var(
|
|
703
|
+
--kui-workbench-restore-inset,
|
|
704
|
+
1rem
|
|
705
|
+
);
|
|
706
|
+
/* The corner owns the inset, so a FloatingToolbar composed here floats
|
|
707
|
+
from this point instead of adding its own inset on top. */
|
|
708
|
+
--kui-floating-toolbar-inset: 0px;
|
|
709
|
+
position: absolute;
|
|
710
|
+
z-index: var(
|
|
711
|
+
--kui-workbench-restore-z,
|
|
712
|
+
calc(var(--kui-workbench-overlay-z, 41) - 2)
|
|
713
|
+
);
|
|
295
714
|
inset-block-end: calc(
|
|
296
|
-
var(--
|
|
297
|
-
var(--kui-safe-area-block-end, env(safe-area-inset-bottom, 0px))
|
|
715
|
+
var(--_kui-workbench-restore-inset) + var(--_kui-workbench-safe-block-end)
|
|
298
716
|
);
|
|
299
717
|
}
|
|
300
718
|
|
|
301
719
|
.kui-workbench__restore[data-position="bottom-start"] {
|
|
302
720
|
inset-inline-start: calc(
|
|
303
|
-
var(--
|
|
304
|
-
var(--
|
|
721
|
+
var(--_kui-workbench-restore-inset) +
|
|
722
|
+
var(--_kui-workbench-safe-inline-start)
|
|
305
723
|
);
|
|
306
724
|
}
|
|
307
725
|
|
|
308
726
|
.kui-workbench__restore[data-position="bottom-end"] {
|
|
309
727
|
inset-inline-end: calc(
|
|
310
|
-
var(--
|
|
311
|
-
|
|
728
|
+
var(--_kui-workbench-restore-inset) + var(--_kui-workbench-safe-inline-end)
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/* An expanded inline bottom drawer owns the bottom of the work-area column,
|
|
733
|
+
which reaches the Workbench's bottom corners once a rail collapses, so a
|
|
734
|
+
collapsed rail's control floats the restore inset above the drawer's top
|
|
735
|
+
edge instead of over it — the standalone CollapsiblePanel rule, scoped to
|
|
736
|
+
this Workbench so a nested Workbench or an enclosing layout never sees its
|
|
737
|
+
drawer. The drawer publishes that edge only while it is expanded and in
|
|
738
|
+
flow: an overlay drawer, static or responsive (see the container queries
|
|
739
|
+
above), covers the control as every open overlay does. Without anchor
|
|
740
|
+
positioning the control keeps the Workbench corner. */
|
|
741
|
+
.kui-workbench {
|
|
742
|
+
anchor-scope: --kui-restore-drawer;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
.kui-workbench__drawer:where(
|
|
746
|
+
[data-presentation="inline"]:not([data-collapsed="true"])
|
|
747
|
+
) {
|
|
748
|
+
anchor-name: --kui-restore-drawer;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
.kui-workbench > .kui-workbench__restore {
|
|
752
|
+
position-visibility: always;
|
|
753
|
+
inset-block-end: calc(
|
|
754
|
+
var(--_kui-workbench-restore-inset) +
|
|
755
|
+
anchor(--kui-restore-drawer top, var(--_kui-workbench-safe-block-end))
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
.kui-workbench__center > .kui-workbench__restore {
|
|
760
|
+
inset-block-end: calc(
|
|
761
|
+
var(--_kui-workbench-restore-inset) + var(--kui-edge-inset-block-end)
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.kui-workbench__center > .kui-workbench__restore[data-position="bottom-start"] {
|
|
766
|
+
inset-inline-start: calc(
|
|
767
|
+
var(--_kui-workbench-restore-inset) + var(--kui-edge-inset-inline-start)
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.kui-workbench__center > .kui-workbench__restore[data-position="bottom-end"] {
|
|
772
|
+
inset-inline-end: calc(
|
|
773
|
+
var(--_kui-workbench-restore-inset) + var(--kui-edge-inset-inline-end)
|
|
312
774
|
);
|
|
313
775
|
}
|
|
314
776
|
|
|
@@ -9,7 +9,8 @@ interface WireResizableRegionsOptions {
|
|
|
9
9
|
onPreview?: (change: ResizeCommit) => void;
|
|
10
10
|
onCommit: (change: ResizeCommit) => void;
|
|
11
11
|
}
|
|
12
|
+
|
|
12
13
|
/** Wire pointer and separator-keyboard behavior for every ResizableRegion below root. */
|
|
13
|
-
declare function wireResizableRegions(root: HTMLElement,
|
|
14
|
+
declare function wireResizableRegions(root: HTMLElement, options: WireResizableRegionsOptions): () => void;
|
|
14
15
|
|
|
15
16
|
export { type ResizeCommit, type WireResizableRegionsOptions, wireResizableRegions };
|
package/dist/wire-tab-bars.js
CHANGED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Signal, ReadonlySignal } from 'kerfjs';
|
|
2
|
+
import { DeviceClass } from './device-class.js';
|
|
3
|
+
|
|
4
|
+
/** A Workbench panel slot, named after its `WorkbenchProps` prop. */
|
|
5
|
+
type WorkbenchPanelKey = 'leftRail' | 'rightRail' | 'bottomDrawer';
|
|
6
|
+
|
|
7
|
+
/** Minimal `localStorage`-shaped store, so the persistence hook is testable. */
|
|
8
|
+
interface WorkbenchStorage {
|
|
9
|
+
getItem(key: string): string | null;
|
|
10
|
+
setItem(key: string, value: string): void;
|
|
11
|
+
}
|
|
12
|
+
/** One Workbench panel's app-owned state. */
|
|
13
|
+
interface WireWorkbenchPanel {
|
|
14
|
+
/**
|
|
15
|
+
* The app-owned size signal a `resizable` panel renders as its `size`.
|
|
16
|
+
* `wireWorkbench` writes each committed resize here; collapsing never
|
|
17
|
+
* touches it, so an expanded panel returns at the size it had. Omit it for
|
|
18
|
+
* a panel that is not resizable.
|
|
19
|
+
*/
|
|
20
|
+
size?: Signal<number>;
|
|
21
|
+
/**
|
|
22
|
+
* When set with `size`, the size is loaded from and saved to `storage`
|
|
23
|
+
* under this key, so the panel remembers the user's size.
|
|
24
|
+
*/
|
|
25
|
+
storageKey?: string;
|
|
26
|
+
/**
|
|
27
|
+
* The app-owned signal the panel renders as its `collapsed`. With it (and
|
|
28
|
+
* `dismissOverlays`, on by default) `wireWorkbench` treats the panel as a
|
|
29
|
+
* transient overlay while it presents as one: it collapses when its
|
|
30
|
+
* `responsiveOverlayAt` breakpoint begins to apply and gets its inline state
|
|
31
|
+
* back when the breakpoint stops applying, an open overlay takes focus and
|
|
32
|
+
* keeps Tab inside it, and it closes on Escape or a press outside it.
|
|
33
|
+
*/
|
|
34
|
+
collapsed?: Signal<boolean>;
|
|
35
|
+
}
|
|
36
|
+
/** A resize the user made, after `wireWorkbench` wrote it to the size signal. */
|
|
37
|
+
interface WorkbenchResize {
|
|
38
|
+
panel: WorkbenchPanelKey;
|
|
39
|
+
size: number;
|
|
40
|
+
source: 'keyboard' | 'pointer';
|
|
41
|
+
}
|
|
42
|
+
interface WireWorkbenchOptions {
|
|
43
|
+
/** The `id` the `Workbench` was rendered with. */
|
|
44
|
+
id: string;
|
|
45
|
+
/**
|
|
46
|
+
* The wired panels, keyed like the `Workbench` props. Render a panel given
|
|
47
|
+
* a `size` with `resizable` and `size={panel.size.value}`, and one given a
|
|
48
|
+
* `collapsed` signal with `collapsed={panel.collapsed.value}`.
|
|
49
|
+
*/
|
|
50
|
+
panels: Partial<Record<WorkbenchPanelKey, WireWorkbenchPanel>>;
|
|
51
|
+
/**
|
|
52
|
+
* When provided, resizing is suspended while `deviceClass.compact` is true —
|
|
53
|
+
* the classes where rails become overlay drawers or are replaced.
|
|
54
|
+
*/
|
|
55
|
+
deviceClass?: ReadonlySignal<DeviceClass>;
|
|
56
|
+
/** Persistence store (default `globalThis.localStorage`, if present). */
|
|
57
|
+
storage?: WorkbenchStorage;
|
|
58
|
+
/** Keyboard step in px (default 16). */
|
|
59
|
+
step?: number;
|
|
60
|
+
/** Shift+arrow step in px (default 64). */
|
|
61
|
+
largeStep?: number;
|
|
62
|
+
/** Called after each committed resize. */
|
|
63
|
+
onResize?: (change: WorkbenchResize) => void;
|
|
64
|
+
/**
|
|
65
|
+
* Treat the panels given a `collapsed` signal as transient overlays while
|
|
66
|
+
* they present as overlays (default `true`), like `wireSidebar`'s compact
|
|
67
|
+
* overlay: a panel whose `responsiveOverlayAt` breakpoint begins to apply
|
|
68
|
+
* starts collapsed, with no collapse motion, and gets its inline collapsed
|
|
69
|
+
* state back when the breakpoint stops applying (and on disposal); an open
|
|
70
|
+
* overlay panel, responsive or `presentation: "overlay"`, closes on Escape
|
|
71
|
+
* or a press that starts and ends outside it. A panel that opens as an
|
|
72
|
+
* overlay takes focus on its first focusable control, and while it is open
|
|
73
|
+
* Tab and Shift+Tab cycle through its controls instead of reaching the
|
|
74
|
+
* work area it covers (the ARIA dialog pattern of `wireSidebar`'s compact
|
|
75
|
+
* overlay); inline panels never move focus. Focus stranded in a closing
|
|
76
|
+
* panel — however it closed, the app's own control inside it included —
|
|
77
|
+
* returns to the control that had it when the panel opened, else to the
|
|
78
|
+
* panel's restore control, else to a control outside the panel whose
|
|
79
|
+
* `aria-controls` names it (each panel's `id` is its region id, e.g.
|
|
80
|
+
* `studio-left-rail`) — the fallback for a panel already open at wire-up.
|
|
81
|
+
* An opener inside a panel that has closed since is skipped. `false` leaves
|
|
82
|
+
* every `collapsed` write and all focus handling to the app.
|
|
83
|
+
*/
|
|
84
|
+
dismissOverlays?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Keep overlays exclusive (default `true`), like `wireSidebar`'s
|
|
87
|
+
* `exclusiveCompact`: when a panel opens while it presents as an overlay,
|
|
88
|
+
* every other open overlay panel — rails and the bottom drawer alike —
|
|
89
|
+
* closes, so one overlay never covers another's controls. Panels presenting
|
|
90
|
+
* inline are never closed by it. Applies only with `dismissOverlays`.
|
|
91
|
+
*/
|
|
92
|
+
exclusiveOverlays?: boolean;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Wire a `Workbench`'s panels. For `resizable` panels given a `size` signal:
|
|
96
|
+
* pointer drags and arrow / Shift+arrow / Home / End on each panel's
|
|
97
|
+
* separator, clamped to the panel's limits and to the room that leaves the
|
|
98
|
+
* work area its minimum width and height, committed to the app-owned size signals.
|
|
99
|
+
* Optional persistence loads and saves each size; optional `deviceClass`
|
|
100
|
+
* suspends resizing on compact classes. Collapse stays the app's `collapsed`
|
|
101
|
+
* flag and never changes a size. For panels given a `collapsed` signal,
|
|
102
|
+
* overlays are transient (`dismissOverlays`): a responsive overlay starts
|
|
103
|
+
* collapsed, an open overlay takes focus and keeps Tab inside it, it closes
|
|
104
|
+
* on Escape or an outside press, and
|
|
105
|
+
* opening one overlay closes the others (`exclusiveOverlays`). Returns a disposer. See `docs/23-app-layouts.md` §3.3.
|
|
106
|
+
*/
|
|
107
|
+
declare function wireWorkbench(root: HTMLElement, { id, panels, deviceClass, storage, step, largeStep, onResize, dismissOverlays, exclusiveOverlays, }: WireWorkbenchOptions): () => void;
|
|
108
|
+
|
|
109
|
+
export { type WireWorkbenchOptions, type WireWorkbenchPanel, type WorkbenchPanelKey, type WorkbenchResize, type WorkbenchStorage, wireWorkbench };
|