@keenmate/pure-admin-core 2.9.0-rc01 → 2.9.0-rc03
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 +16 -14
- package/dist/css/main.css +208 -0
- package/package.json +1 -1
- package/snippets/splitter.html +210 -0
- package/src/scss/_core.scss +3 -0
- package/src/scss/core-components/_buttons.scss +15 -0
- package/src/scss/core-components/_cards.scss +149 -0
- package/src/scss/core-components/_splitter.scss +206 -0
- package/src/scss/variables/_components.scss +22 -0
package/README.md
CHANGED
|
@@ -2,20 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
Lightweight, data-focused CSS/SCSS admin framework with Corporate theme as default.
|
|
4
4
|
|
|
5
|
-
## What's New in 2.9.0-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
- **`.pa-
|
|
18
|
-
-
|
|
5
|
+
## What's New in 2.9.0-rc03
|
|
6
|
+
|
|
7
|
+
- **`.pa-splitter` drag model reworked to "rebalance-on-drag".** Each gutter now resizes only its primary neighbour (edge-closer side, LTR/RTL tiebreaker on ties); the matching delta is absorbed by the immediate adjacent non-rail pane (classic boundary feel), or — when the adjacent pane is rail — tunnels through the rail wall to the contiguous non-rail block beyond. Eliminates the "boundary locks when both sides are rail" deadlock from the prior model — drag-from-rail with a railed neighbour now grows the primary by pulling room from the next section.
|
|
8
|
+
- **Middle panes can now minimize to rail** (previously first / last only). Any pane with `data-pa-splitter-minimize` collapses on toggle / dblclick / drag-snap regardless of position. Snap-into-rail enforces an "at least one expanded pane" invariant — you can't drag the last visible pane to rail and end up with nothing to grab.
|
|
9
|
+
- **Restoring a minimized pane reclaims layout gaps cleanly.** When a prior minimize hit an absorber's `max` cap and left empty space in the layout, restoring the pane now reaches its remembered size by drawing from the gap (not just from other panes' shrink headroom) — fixes the "restored pane stuck at min width" surprise. Conversely, restoring no longer aggressively inflates a sibling pane to fill remaining gap — empty space stays as empty space and gets consumed as more panes restore. No more "sibling pane suddenly jumps" UX.
|
|
10
|
+
- **Rail width in `--pa-splitter-rail-size` now correctly resolves `rem` / non-px units.** Previously `parseFloat("4rem")` returned `4` and snapped rail panes to 4px-wide strips; rebuilt via a hidden probe element so the browser does the unit resolution. The three-step fallback chain (per-instance attribute → CSS var → literal 40) still applies.
|
|
11
|
+
- **Gutter highlighting now isolates the dragged gutter only.** During a drag, the splitter `--dragging` modifier suppresses `:hover` / `:focus-visible` on sibling gutters so they don't light up as the cursor passes over them or as a stale focus ring lingers — only the active gutter carries the highlight.
|
|
12
|
+
- **Splitter padding subtracted from available space.** Flex children sit inside the splitter's content box but `clientWidth` includes padding — without subtracting it the last pane's `flex-basis` overflowed and got clipped by `overflow: hidden`. Visible in the N-pane demo where the rightmost rail's right edge touched the splitter border.
|
|
13
|
+
|
|
14
|
+
## What's New in 2.9.0-rc02
|
|
15
|
+
|
|
16
|
+
- **`.pa-splitter` — resizable container with optional collapse-to-rail.** Vanilla JS + SCSS, two orientations (`--horizontal` / `--vertical`), per-instance constraints (`min-start` / `max-start` accept px or %), and `localStorage` persistence via `data-pa-splitter-id`. Pointer-event drag (mouse / touch / pen), `gap`-aware constraint math, keyboard a11y on the gutter (`role="separator"`, arrow keys, `Home`/`End`, `Enter`/`Space` to toggle), and a `ResizeObserver`-driven re-clamp on container resize. Opt-in `data-pa-splitter-minimize="start" | "end"` collapses a pane to a thin sideways-rotated header rail; dragging past a snap threshold commits, dragging back outward restores.
|
|
17
|
+
- **`.pa-splitter` N-pane mode (3+ panes).** `init()` is now a dispatcher: the legacy 2-pane path (selected by `pane--start` + `pane--end` markup) is preserved byte-identical; everything else goes through `initNPane()`. Per-pane API: `data-pa-splitter-size="240px|30%"` for initial sizing, `-min` / `-max` for per-pane clamps, `data-pa-splitter-minimize` as presence marker (first / last pane only — middle-pane rail rotation has nowhere clean to dock). Drag math is per-gutter stop-at-min (Split.js basic-mode semantics). Container-resize redistributes proportionally to unminimized panes; minimized panes stay pinned to rail. Storage shape versioned under the same `pa-splitter:<id>` key.
|
|
18
|
+
- **`pa-card__actions--responsive` — CSS-only collapse to a split button when the header runs out of space.** Render both forms inline (`pa-card__actions-full` + `pa-card__actions-collapsed`, the latter hosting a `pa-btn-split`); a container query on `.pa-card__header:has(> .pa-card__actions--responsive)` swaps which is visible. Threshold lives in one SCSS variable (`$card-actions-collapse-at`, default `28rem`). No JS, no `ResizeObserver` — but action data is duplicated in the DOM.
|
|
19
|
+
- **`pa-card__actions--overflow` — JS-driven progressive collapse into a "..." menu.** Complement to `--responsive`: walks buttons into an overflow menu one at a time as space shrinks, restoring them in original DOM order as it grows. `data-pa-actions-overflow-from="end" | "start"` controls drop direction; `data-pa-actions-priority="N"` per button pins primary actions. Menu reuses `pa-btn-split__menu` styling (one menu source across split-button and overflow). Positioning via Floating UI (`computePosition` + `offset` + `flip` + `shift` + `autoUpdate`), with a hand-rolled fallback when `FloatingUIDOM` isn't loaded. Auto-closes on any wrapper resize or when the trigger goes `display: none` (e.g. splitter rail mode).
|
|
20
|
+
- **`pa-btn--ghost` variant defined in core.** Previously referenced by demo files (`alerts`, `notifications`, `splitter`) but had no SCSS rule — every usage silently rendered as a base `.pa-btn`. Now defined in `_buttons.scss`: transparent background and border, `var(--pa-text-secondary)` text, hover snaps to `var(--pa-surface-hover)` + `--pa-text-color-1`. No new tokens introduced; themes need a rebuild to pick up the variant.
|
|
19
21
|
|
|
20
22
|
## Installation
|
|
21
23
|
|
package/dist/css/main.css
CHANGED
|
@@ -5484,6 +5484,12 @@ body:not(.sidebar-hidden) .pa-layout__sidebar--icon-collapse .pa-sidebar__icon {
|
|
|
5484
5484
|
.pa-card__header .pa-btn-group {
|
|
5485
5485
|
flex-shrink: 0;
|
|
5486
5486
|
}
|
|
5487
|
+
.pa-card__header .pa-card__actions--overflow {
|
|
5488
|
+
flex-shrink: 1;
|
|
5489
|
+
}
|
|
5490
|
+
.pa-card__header:has(> .pa-card__actions--overflow) > .pa-card__title {
|
|
5491
|
+
min-width: 6rem;
|
|
5492
|
+
}
|
|
5487
5493
|
.pa-card__header .pa-btn {
|
|
5488
5494
|
margin-top: -0.25rem;
|
|
5489
5495
|
margin-bottom: -0.25rem;
|
|
@@ -5593,6 +5599,25 @@ body:not(.sidebar-hidden) .pa-layout__sidebar--icon-collapse .pa-sidebar__icon {
|
|
|
5593
5599
|
gap: 0.8rem;
|
|
5594
5600
|
align-items: center;
|
|
5595
5601
|
}
|
|
5602
|
+
.pa-card__actions--responsive > .pa-card__actions-full {
|
|
5603
|
+
display: flex;
|
|
5604
|
+
gap: 0.8rem;
|
|
5605
|
+
align-items: center;
|
|
5606
|
+
}
|
|
5607
|
+
.pa-card__actions--responsive > .pa-card__actions-collapsed {
|
|
5608
|
+
display: none;
|
|
5609
|
+
}
|
|
5610
|
+
.pa-card__actions--overflow {
|
|
5611
|
+
min-width: 0;
|
|
5612
|
+
overflow: hidden;
|
|
5613
|
+
}
|
|
5614
|
+
.pa-card__actions--overflow > * {
|
|
5615
|
+
flex-shrink: 0;
|
|
5616
|
+
}
|
|
5617
|
+
.pa-card__header:has(> .pa-card__actions--responsive) {
|
|
5618
|
+
container-type: inline-size;
|
|
5619
|
+
container-name: pa-card-header;
|
|
5620
|
+
}
|
|
5596
5621
|
.pa-card__meta {
|
|
5597
5622
|
color: var(--pa-text-color-2);
|
|
5598
5623
|
font-size: 1.4rem;
|
|
@@ -5922,6 +5947,48 @@ a.pa-card p {
|
|
|
5922
5947
|
padding-bottom: 0.8rem;
|
|
5923
5948
|
}
|
|
5924
5949
|
|
|
5950
|
+
@container pa-card-header (max-width: 28rem) {
|
|
5951
|
+
.pa-card__actions--responsive > .pa-card__actions-full {
|
|
5952
|
+
display: none;
|
|
5953
|
+
}
|
|
5954
|
+
.pa-card__actions--responsive > .pa-card__actions-collapsed {
|
|
5955
|
+
display: flex;
|
|
5956
|
+
align-items: center;
|
|
5957
|
+
}
|
|
5958
|
+
}
|
|
5959
|
+
.pa-splitter__pane--minimized > .pa-card {
|
|
5960
|
+
height: 100%;
|
|
5961
|
+
display: flex;
|
|
5962
|
+
flex-direction: column;
|
|
5963
|
+
margin: 0;
|
|
5964
|
+
}
|
|
5965
|
+
.pa-splitter__pane--minimized > .pa-card > .pa-card__body,
|
|
5966
|
+
.pa-splitter__pane--minimized > .pa-card > .pa-card__footer {
|
|
5967
|
+
display: none;
|
|
5968
|
+
}
|
|
5969
|
+
.pa-splitter__pane--minimized > .pa-card > .pa-card__header {
|
|
5970
|
+
flex: 1 1 auto;
|
|
5971
|
+
justify-content: flex-start;
|
|
5972
|
+
writing-mode: sideways-rl;
|
|
5973
|
+
}
|
|
5974
|
+
.pa-splitter__pane--minimized > .pa-card > .pa-card__header i,
|
|
5975
|
+
.pa-splitter__pane--minimized > .pa-card > .pa-card__header svg {
|
|
5976
|
+
writing-mode: initial;
|
|
5977
|
+
}
|
|
5978
|
+
.pa-splitter__pane--minimized > .pa-card > .pa-card__header .pa-card__title {
|
|
5979
|
+
padding-top: calc((4rem - 1.6rem) / 2 - 0.5rem);
|
|
5980
|
+
}
|
|
5981
|
+
.pa-splitter__pane--minimized > .pa-card > .pa-card__header .pa-btn,
|
|
5982
|
+
.pa-splitter__pane--minimized > .pa-card > .pa-card__header .pa-btn-group,
|
|
5983
|
+
.pa-splitter__pane--minimized > .pa-card > .pa-card__header button,
|
|
5984
|
+
.pa-splitter__pane--minimized > .pa-card > .pa-card__header input {
|
|
5985
|
+
display: none;
|
|
5986
|
+
}
|
|
5987
|
+
|
|
5988
|
+
.pa-splitter--minimize-mirror .pa-splitter__pane--minimized > .pa-card > .pa-card__header :is(h1, h2, h3, h4, h5, h6) {
|
|
5989
|
+
transform: scale(-1, -1);
|
|
5990
|
+
}
|
|
5991
|
+
|
|
5925
5992
|
/* ========================================
|
|
5926
5993
|
Tabs Component
|
|
5927
5994
|
Standalone tabs for organizing content into separate sections
|
|
@@ -7306,6 +7373,15 @@ a.pa-card p {
|
|
|
7306
7373
|
background-color: var(--pa-main-bg);
|
|
7307
7374
|
border-color: var(--pa-accent);
|
|
7308
7375
|
}
|
|
7376
|
+
.pa-btn--ghost {
|
|
7377
|
+
background-color: transparent;
|
|
7378
|
+
border-color: transparent;
|
|
7379
|
+
color: var(--pa-text-secondary);
|
|
7380
|
+
}
|
|
7381
|
+
.pa-btn--ghost:hover {
|
|
7382
|
+
background-color: var(--pa-surface-hover);
|
|
7383
|
+
color: var(--pa-text-color-1);
|
|
7384
|
+
}
|
|
7309
7385
|
.pa-btn--xs {
|
|
7310
7386
|
height: 3.1rem;
|
|
7311
7387
|
padding: 0.6rem 0.8rem;
|
|
@@ -15242,6 +15318,138 @@ code {
|
|
|
15242
15318
|
transform: rotate(180deg);
|
|
15243
15319
|
}
|
|
15244
15320
|
|
|
15321
|
+
/* ========================================
|
|
15322
|
+
Splitter Component
|
|
15323
|
+
Resizable container with draggable gutters (2 or more panes).
|
|
15324
|
+
Modifiers:
|
|
15325
|
+
--horizontal panes side-by-side, vertical gutter (default)
|
|
15326
|
+
--vertical panes stacked, horizontal gutter
|
|
15327
|
+
--dragging applied to root while user is dragging
|
|
15328
|
+
--collapsed start pane has been collapsed to 0
|
|
15329
|
+
--minimize-mirror flip rail-title text 180° (bottom-to-top reading)
|
|
15330
|
+
|
|
15331
|
+
Rail mode is fully decoupled from any specific component. The splitter
|
|
15332
|
+
only rotates elements marked `[data-pa-splitter-rail-title]` inside a
|
|
15333
|
+
minimized pane; per-component adaptation (e.g. card body / footer
|
|
15334
|
+
hiding) lives in that component's SCSS, keyed off
|
|
15335
|
+
`.pa-splitter__pane--minimized > .pa-foo`. Card integration is in
|
|
15336
|
+
`_cards.scss`.
|
|
15337
|
+
======================================== */
|
|
15338
|
+
:root {
|
|
15339
|
+
--pa-splitter-rail-size: 4rem;
|
|
15340
|
+
}
|
|
15341
|
+
|
|
15342
|
+
.pa-splitter {
|
|
15343
|
+
display: flex;
|
|
15344
|
+
width: 100%;
|
|
15345
|
+
height: 100%;
|
|
15346
|
+
min-width: 0;
|
|
15347
|
+
min-height: 0;
|
|
15348
|
+
overflow: hidden;
|
|
15349
|
+
}
|
|
15350
|
+
.pa-splitter--horizontal {
|
|
15351
|
+
flex-direction: row;
|
|
15352
|
+
}
|
|
15353
|
+
.pa-splitter--horizontal > .pa-splitter__gutter {
|
|
15354
|
+
cursor: col-resize;
|
|
15355
|
+
width: var(--pa-splitter-gutter-size, 0.6rem);
|
|
15356
|
+
height: auto;
|
|
15357
|
+
align-self: stretch;
|
|
15358
|
+
}
|
|
15359
|
+
.pa-splitter--horizontal > .pa-splitter__gutter::before {
|
|
15360
|
+
width: 2px;
|
|
15361
|
+
height: 2.4rem;
|
|
15362
|
+
}
|
|
15363
|
+
.pa-splitter--vertical {
|
|
15364
|
+
flex-direction: column;
|
|
15365
|
+
}
|
|
15366
|
+
.pa-splitter--vertical > .pa-splitter__gutter {
|
|
15367
|
+
cursor: row-resize;
|
|
15368
|
+
width: auto;
|
|
15369
|
+
height: var(--pa-splitter-gutter-size, 0.6rem);
|
|
15370
|
+
align-self: stretch;
|
|
15371
|
+
}
|
|
15372
|
+
.pa-splitter--vertical > .pa-splitter__gutter::before {
|
|
15373
|
+
width: 2.4rem;
|
|
15374
|
+
height: 2px;
|
|
15375
|
+
}
|
|
15376
|
+
.pa-splitter--dragging {
|
|
15377
|
+
user-select: none;
|
|
15378
|
+
}
|
|
15379
|
+
.pa-splitter--dragging > .pa-splitter__pane {
|
|
15380
|
+
pointer-events: none;
|
|
15381
|
+
}
|
|
15382
|
+
.pa-splitter--dragging > .pa-splitter__gutter:not(.pa-splitter__gutter--active) {
|
|
15383
|
+
pointer-events: none;
|
|
15384
|
+
}
|
|
15385
|
+
.pa-splitter--dragging > .pa-splitter__gutter:not(.pa-splitter__gutter--active):focus-visible {
|
|
15386
|
+
box-shadow: none;
|
|
15387
|
+
}
|
|
15388
|
+
.pa-splitter--dragging > .pa-splitter__gutter:not(.pa-splitter__gutter--active):focus-visible::before {
|
|
15389
|
+
background-color: rgba(128, 128, 128, 0.45);
|
|
15390
|
+
}
|
|
15391
|
+
.pa-splitter__gutter--active {
|
|
15392
|
+
background-color: rgba(0, 123, 255, 0.35);
|
|
15393
|
+
}
|
|
15394
|
+
.pa-splitter__gutter--active::before {
|
|
15395
|
+
background-color: #007bff;
|
|
15396
|
+
}
|
|
15397
|
+
.pa-splitter__pane {
|
|
15398
|
+
overflow: auto;
|
|
15399
|
+
min-width: 0;
|
|
15400
|
+
min-height: 0;
|
|
15401
|
+
}
|
|
15402
|
+
.pa-splitter__pane--start {
|
|
15403
|
+
flex: 0 0 auto;
|
|
15404
|
+
}
|
|
15405
|
+
.pa-splitter__pane--end {
|
|
15406
|
+
flex: 1 1 0;
|
|
15407
|
+
}
|
|
15408
|
+
.pa-splitter__pane--minimized {
|
|
15409
|
+
cursor: pointer;
|
|
15410
|
+
overflow: hidden;
|
|
15411
|
+
}
|
|
15412
|
+
.pa-splitter__pane--minimized [data-pa-splitter-rail-title] {
|
|
15413
|
+
writing-mode: sideways-rl;
|
|
15414
|
+
}
|
|
15415
|
+
.pa-splitter__pane--minimized [data-pa-splitter-rail-title] i,
|
|
15416
|
+
.pa-splitter__pane--minimized [data-pa-splitter-rail-title] svg {
|
|
15417
|
+
writing-mode: initial;
|
|
15418
|
+
}
|
|
15419
|
+
.pa-splitter--minimize-mirror .pa-splitter__pane--minimized [data-pa-splitter-rail-title] :is(h1, h2, h3, h4, h5, h6) {
|
|
15420
|
+
transform: scale(-1, -1);
|
|
15421
|
+
}
|
|
15422
|
+
.pa-splitter__gutter {
|
|
15423
|
+
position: relative;
|
|
15424
|
+
flex: 0 0 auto;
|
|
15425
|
+
background-color: rgba(128, 128, 128, 0.08);
|
|
15426
|
+
transition: background-color 0.15s ease;
|
|
15427
|
+
touch-action: none;
|
|
15428
|
+
outline: none;
|
|
15429
|
+
}
|
|
15430
|
+
.pa-splitter__gutter::before {
|
|
15431
|
+
content: "";
|
|
15432
|
+
position: absolute;
|
|
15433
|
+
top: 50%;
|
|
15434
|
+
left: 50%;
|
|
15435
|
+
transform: translate(-50%, -50%);
|
|
15436
|
+
background-color: rgba(128, 128, 128, 0.45);
|
|
15437
|
+
border-radius: 2px;
|
|
15438
|
+
transition: background-color 0.15s ease;
|
|
15439
|
+
}
|
|
15440
|
+
.pa-splitter__gutter:hover {
|
|
15441
|
+
background-color: rgba(128, 128, 128, 0.18);
|
|
15442
|
+
}
|
|
15443
|
+
.pa-splitter__gutter:hover::before {
|
|
15444
|
+
background-color: #007bff;
|
|
15445
|
+
}
|
|
15446
|
+
.pa-splitter__gutter:focus-visible {
|
|
15447
|
+
box-shadow: inset 0 0 0 2px rgba(0, 123, 255, 0.4);
|
|
15448
|
+
}
|
|
15449
|
+
.pa-splitter__gutter:focus-visible::before {
|
|
15450
|
+
background-color: #007bff;
|
|
15451
|
+
}
|
|
15452
|
+
|
|
15245
15453
|
/* ========================================
|
|
15246
15454
|
Data Display Components
|
|
15247
15455
|
Read-only label-value field pairs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keenmate/pure-admin-core",
|
|
3
|
-
"version": "2.9.0-
|
|
3
|
+
"version": "2.9.0-rc03",
|
|
4
4
|
"description": "Lightweight, data-focused HTML/CSS admin framework built with PureCSS foundation and comprehensive component system",
|
|
5
5
|
"style": "dist/css/main.css",
|
|
6
6
|
"exports": {
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
<!-- ================================
|
|
2
|
+
SPLITTER
|
|
3
|
+
Pure Admin Visual Framework
|
|
4
|
+
|
|
5
|
+
Resizable container with two or more panes. Drag a gutter to resize,
|
|
6
|
+
double-click to collapse / restore, arrow keys nudge size when the
|
|
7
|
+
gutter is focused.
|
|
8
|
+
|
|
9
|
+
CSS + structure here; drag, keyboard, persistence behaviour is wired up
|
|
10
|
+
by packages/core/src/js/splitter.js. The script auto-initializes on
|
|
11
|
+
[data-pa-splitter] at DOMContentLoaded.
|
|
12
|
+
|
|
13
|
+
Two markup flavours:
|
|
14
|
+
* 2-pane shorthand — uses --start / --end modifiers and root-level
|
|
15
|
+
min-start / max-start / default attributes (sections 1–3 below).
|
|
16
|
+
* N-pane (N ≥ 2) — generic panes with per-pane size / min / max
|
|
17
|
+
attributes (section 4 below). Use this when you have 3+ panes
|
|
18
|
+
or want more than one minimizable pane.
|
|
19
|
+
|
|
20
|
+
Orientation modifiers (match flexbox direction of the panes):
|
|
21
|
+
--horizontal panes side-by-side, vertical gutter (default)
|
|
22
|
+
--vertical panes stacked, horizontal gutter
|
|
23
|
+
|
|
24
|
+
The splitter takes whatever width/height its parent gives it — make
|
|
25
|
+
sure the parent has a sized cross axis (height for --horizontal,
|
|
26
|
+
width for --vertical) or the panes will collapse.
|
|
27
|
+
================================ -->
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
<!-- ================================
|
|
31
|
+
HORIZONTAL SPLIT
|
|
32
|
+
Side-by-side panes; vertical gutter. Start pane has a 200px floor
|
|
33
|
+
and 60% ceiling, defaults to 280px, persists under "demo-sidebar".
|
|
34
|
+
================================ -->
|
|
35
|
+
|
|
36
|
+
<div class="pa-splitter pa-splitter--horizontal"
|
|
37
|
+
data-pa-splitter
|
|
38
|
+
data-pa-splitter-id="demo-sidebar"
|
|
39
|
+
data-pa-splitter-min-start="200px"
|
|
40
|
+
data-pa-splitter-max-start="60%"
|
|
41
|
+
data-pa-splitter-default="280px"
|
|
42
|
+
style="height: 400px;">
|
|
43
|
+
<div class="pa-splitter__pane pa-splitter__pane--start">
|
|
44
|
+
<!-- Your sidebar / list / nav content -->
|
|
45
|
+
</div>
|
|
46
|
+
<div class="pa-splitter__gutter"
|
|
47
|
+
role="separator"
|
|
48
|
+
aria-orientation="vertical"
|
|
49
|
+
tabindex="0"></div>
|
|
50
|
+
<div class="pa-splitter__pane pa-splitter__pane--end">
|
|
51
|
+
<!-- Your main / detail content -->
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
<!-- ================================
|
|
57
|
+
VERTICAL SPLIT
|
|
58
|
+
Stacked panes; horizontal gutter. Useful for editor + console / log
|
|
59
|
+
pane patterns.
|
|
60
|
+
================================ -->
|
|
61
|
+
|
|
62
|
+
<div class="pa-splitter pa-splitter--vertical"
|
|
63
|
+
data-pa-splitter
|
|
64
|
+
data-pa-splitter-id="demo-console"
|
|
65
|
+
data-pa-splitter-min-start="80px"
|
|
66
|
+
data-pa-splitter-max-start="80%"
|
|
67
|
+
data-pa-splitter-default="60%"
|
|
68
|
+
style="height: 400px;">
|
|
69
|
+
<div class="pa-splitter__pane pa-splitter__pane--start">
|
|
70
|
+
<!-- Editor / primary content -->
|
|
71
|
+
</div>
|
|
72
|
+
<div class="pa-splitter__gutter"
|
|
73
|
+
role="separator"
|
|
74
|
+
aria-orientation="horizontal"
|
|
75
|
+
tabindex="0"></div>
|
|
76
|
+
<div class="pa-splitter__pane pa-splitter__pane--end">
|
|
77
|
+
<!-- Console / log / secondary -->
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
<!-- ================================
|
|
83
|
+
SPACED CARDS
|
|
84
|
+
Set `gap` on the splitter root to put breathing room between the
|
|
85
|
+
panes and the gutter — the JS subtracts it from the available
|
|
86
|
+
space so percent constraints still resolve correctly. Override
|
|
87
|
+
gutter thickness via the --pa-splitter-gutter-size custom property.
|
|
88
|
+
================================ -->
|
|
89
|
+
|
|
90
|
+
<div class="pa-splitter pa-splitter--horizontal"
|
|
91
|
+
data-pa-splitter
|
|
92
|
+
style="height: 280px; gap: 1.6rem; --pa-splitter-gutter-size: 1rem;">
|
|
93
|
+
<div class="pa-splitter__pane pa-splitter__pane--start" style="padding: 0;">
|
|
94
|
+
<div class="pa-card" style="height: 100%; margin: 0;">…</div>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="pa-splitter__gutter"
|
|
97
|
+
role="separator"
|
|
98
|
+
aria-orientation="vertical"
|
|
99
|
+
tabindex="0"></div>
|
|
100
|
+
<div class="pa-splitter__pane pa-splitter__pane--end" style="padding: 0;">
|
|
101
|
+
<div class="pa-card" style="height: 100%; margin: 0;">…</div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
<!-- ================================
|
|
107
|
+
N-PANE LAYOUT (3 panes — sidebar + content + inspector)
|
|
108
|
+
Per-pane attributes replace the root-level --start ones. Only the
|
|
109
|
+
first and last panes honour the minimize marker. Panes without a
|
|
110
|
+
data-pa-splitter-size share the leftover equally.
|
|
111
|
+
================================ -->
|
|
112
|
+
|
|
113
|
+
<div class="pa-splitter pa-splitter--horizontal"
|
|
114
|
+
data-pa-splitter
|
|
115
|
+
data-pa-splitter-id="demo-three-pane"
|
|
116
|
+
style="height: 400px;">
|
|
117
|
+
<div class="pa-splitter__pane"
|
|
118
|
+
data-pa-splitter-size="240px"
|
|
119
|
+
data-pa-splitter-min="180px"
|
|
120
|
+
data-pa-splitter-max="360px"
|
|
121
|
+
data-pa-splitter-minimize>
|
|
122
|
+
<!-- File tree / nav (minimizable to a left rail) -->
|
|
123
|
+
</div>
|
|
124
|
+
<div class="pa-splitter__gutter"
|
|
125
|
+
role="separator"
|
|
126
|
+
aria-orientation="vertical"
|
|
127
|
+
tabindex="0"></div>
|
|
128
|
+
<div class="pa-splitter__pane"
|
|
129
|
+
data-pa-splitter-min="240px">
|
|
130
|
+
<!-- Editor / main content (fills the rest) -->
|
|
131
|
+
</div>
|
|
132
|
+
<div class="pa-splitter__gutter"
|
|
133
|
+
role="separator"
|
|
134
|
+
aria-orientation="vertical"
|
|
135
|
+
tabindex="0"></div>
|
|
136
|
+
<div class="pa-splitter__pane"
|
|
137
|
+
data-pa-splitter-size="280px"
|
|
138
|
+
data-pa-splitter-min="220px"
|
|
139
|
+
data-pa-splitter-max="420px"
|
|
140
|
+
data-pa-splitter-minimize>
|
|
141
|
+
<!-- Inspector / detail (minimizable to a right rail) -->
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
<!-- ================================
|
|
147
|
+
DATA ATTRIBUTES — root (both modes)
|
|
148
|
+
data-pa-splitter marker; required
|
|
149
|
+
data-pa-splitter-id enables localStorage persistence
|
|
150
|
+
under "pa-splitter:<id>"
|
|
151
|
+
data-pa-splitter-step keyboard step in px (default: 10)
|
|
152
|
+
data-pa-splitter-rail-size rail width in px (default: 40)
|
|
153
|
+
data-pa-splitter-minimize-threshold drag-to-rail snap ratio (default: 0.40)
|
|
154
|
+
|
|
155
|
+
DATA ATTRIBUTES — root (legacy 2-pane only)
|
|
156
|
+
data-pa-splitter-min-start "200px" or "20%" (default: 0)
|
|
157
|
+
data-pa-splitter-max-start "60%" or "800px" (default: available)
|
|
158
|
+
data-pa-splitter-default initial size when no saved state
|
|
159
|
+
data-pa-splitter-minimize "start" or "end" — opt into rail collapse
|
|
160
|
+
|
|
161
|
+
DATA ATTRIBUTES — per pane (N-pane only)
|
|
162
|
+
data-pa-splitter-size "240px" or "30%" (default: shared leftover)
|
|
163
|
+
data-pa-splitter-min "150px" or "10%" (default: 0)
|
|
164
|
+
data-pa-splitter-max "400px" or "50%" (default: available)
|
|
165
|
+
data-pa-splitter-minimize marker — only honoured on the first
|
|
166
|
+
and last panes
|
|
167
|
+
|
|
168
|
+
CHILD ATTRIBUTES (both modes)
|
|
169
|
+
data-pa-splitter-toggle put on any element inside the splitter
|
|
170
|
+
(typically a button in the card header)
|
|
171
|
+
to trigger collapse / restore on click.
|
|
172
|
+
In N-pane mode, toggles the enclosing
|
|
173
|
+
pane (if it's minimizable).
|
|
174
|
+
|
|
175
|
+
RAIL-TITLE HOOK (non-card content)
|
|
176
|
+
Any element marked with `[data-pa-splitter-rail-title]` inside a
|
|
177
|
+
minimized pane rotates to vertical writing (sideways-rl). Use this
|
|
178
|
+
when you put plain content (not a `.pa-card`) in a minimizable pane:
|
|
179
|
+
|
|
180
|
+
<div class="pa-splitter__pane" data-pa-splitter-minimize>
|
|
181
|
+
<div data-pa-splitter-rail-title>📁 Files</div>
|
|
182
|
+
<!-- The rest of your pane content here -->
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
Cards adapt automatically — `_cards.scss` rotates the card header
|
|
186
|
+
inside any `.pa-splitter__pane--minimized` without needing the
|
|
187
|
+
attribute. No markup change needed for the card snippets above.
|
|
188
|
+
|
|
189
|
+
CSS CUSTOMIZATION
|
|
190
|
+
--pa-splitter-gutter-size gutter thickness (default: 6px)
|
|
191
|
+
--pa-splitter-rail-size rail width when a pane is minimized
|
|
192
|
+
(default: 40px / $splitter-rail-size).
|
|
193
|
+
JS picks this up via getComputedStyle, so
|
|
194
|
+
setting it on `:root` or per-instance via
|
|
195
|
+
inline style overrides the rail width
|
|
196
|
+
without also setting the data-attr.
|
|
197
|
+
gap space between panes and gutter
|
|
198
|
+
(native flexbox property)
|
|
199
|
+
|
|
200
|
+
MODIFIER CLASSES
|
|
201
|
+
pa-splitter--minimize-mirror flip the minimized title 180°
|
|
202
|
+
(transform: scale(-1,-1) on the heading
|
|
203
|
+
inside any `[data-pa-splitter-rail-title]`
|
|
204
|
+
or `.pa-card__header`)
|
|
205
|
+
|
|
206
|
+
JAVASCRIPT API (window.PaSplitter)
|
|
207
|
+
PaSplitter.init(el) initialize a single element (idempotent)
|
|
208
|
+
PaSplitter.initAll(root?) initialize all uninitialized splitters
|
|
209
|
+
under root (default: document)
|
|
210
|
+
================================ -->
|
package/src/scss/_core.scss
CHANGED
|
@@ -111,6 +111,9 @@
|
|
|
111
111
|
// Settings Panel
|
|
112
112
|
@use 'core-components/settings-panel' as *;
|
|
113
113
|
|
|
114
|
+
// Splitter (resizable two-pane container)
|
|
115
|
+
@use 'core-components/splitter' as *;
|
|
116
|
+
|
|
114
117
|
// Data Display (read-only fields)
|
|
115
118
|
@use 'core-components/data-display' as *;
|
|
116
119
|
|
|
@@ -44,6 +44,21 @@
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
// Chromeless action button — no fill, no border, just the label / icon.
|
|
48
|
+
// Used for inline affordances inside dense surfaces (card-header actions,
|
|
49
|
+
// notification dismiss, toast close, splitter minimize) where a full button
|
|
50
|
+
// would compete with the content.
|
|
51
|
+
&--ghost {
|
|
52
|
+
background-color: transparent;
|
|
53
|
+
border-color: transparent;
|
|
54
|
+
color: var(--pa-text-secondary);
|
|
55
|
+
|
|
56
|
+
&:hover {
|
|
57
|
+
background-color: var(--pa-surface-hover);
|
|
58
|
+
color: var(--pa-text-color-1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
47
62
|
&--xs {
|
|
48
63
|
height: $btn-height-xs;
|
|
49
64
|
padding: $btn-padding-xs-v $btn-padding-xs-h;
|
|
@@ -100,6 +100,24 @@
|
|
|
100
100
|
flex-shrink: 0;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
// …unless it's the progressive-overflow variant, which intentionally
|
|
104
|
+
// shrinks below content so the JS can read `scrollWidth > clientWidth`
|
|
105
|
+
// as the "needs overflow" signal. Nested under `&__header` so the
|
|
106
|
+
// selector matches `.pa-card__header .pa-card__actions--overflow` —
|
|
107
|
+
// the same specificity as the rule above, but later in source order
|
|
108
|
+
// and explicit on the modifier.
|
|
109
|
+
.pa-card__actions--overflow {
|
|
110
|
+
flex-shrink: 1;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// When the actions wrapper can shrink (overflow variant), give the title
|
|
114
|
+
// a floor so it can't be squeezed to 0 — icon + a few chars + ellipsis
|
|
115
|
+
// stay visible no matter how narrow. `:has()` scopes the override so
|
|
116
|
+
// ordinary cards keep `min-width: 0` and their full ellipsis range.
|
|
117
|
+
&:has(> .pa-card__actions--overflow) > .pa-card__title {
|
|
118
|
+
min-width: 6rem;
|
|
119
|
+
}
|
|
120
|
+
|
|
103
121
|
// Buttons in card headers - negative margin to prevent header height growth
|
|
104
122
|
.pa-btn {
|
|
105
123
|
margin-top: -0.25rem;
|
|
@@ -237,6 +255,61 @@
|
|
|
237
255
|
display: flex;
|
|
238
256
|
gap: $spacing-sm;
|
|
239
257
|
align-items: center;
|
|
258
|
+
|
|
259
|
+
// Responsive actions — render both the spread button list AND a
|
|
260
|
+
// collapsed split-button form; a container query on the header swaps
|
|
261
|
+
// which is visible. Stays CSS-only (no ResizeObserver / layout reads)
|
|
262
|
+
// but the markup carries both forms.
|
|
263
|
+
//
|
|
264
|
+
// <div class="pa-card__header">
|
|
265
|
+
// <div class="pa-card__title">…</div>
|
|
266
|
+
// <div class="pa-card__actions pa-card__actions--responsive">
|
|
267
|
+
// <div class="pa-card__actions-full">
|
|
268
|
+
// …spread buttons…
|
|
269
|
+
// </div>
|
|
270
|
+
// <div class="pa-card__actions-collapsed">
|
|
271
|
+
// <div class="pa-btn-split">…same actions as a menu…</div>
|
|
272
|
+
// </div>
|
|
273
|
+
// </div>
|
|
274
|
+
// </div>
|
|
275
|
+
//
|
|
276
|
+
// Container is the header; threshold is $card-actions-collapse-at.
|
|
277
|
+
// Wired up via :has() (Baseline 2023) so authors don't need a second
|
|
278
|
+
// modifier on the header.
|
|
279
|
+
&--responsive {
|
|
280
|
+
> .pa-card__actions-full {
|
|
281
|
+
display: flex;
|
|
282
|
+
gap: $spacing-sm;
|
|
283
|
+
align-items: center;
|
|
284
|
+
}
|
|
285
|
+
> .pa-card__actions-collapsed {
|
|
286
|
+
display: none;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Progressive overflow variant — driven by card-actions-overflow.js.
|
|
291
|
+
// `min-width: 0` lets flex shrink the wrapper below its content; with
|
|
292
|
+
// `overflow: hidden` the wrapper clips its children when narrow, which
|
|
293
|
+
// is what makes `scrollWidth > clientWidth` a reliable overflow signal
|
|
294
|
+
// for the JS. Buttons themselves get `flex-shrink: 0` so they don't
|
|
295
|
+
// squeeze to fit — they either fit fully or get moved to the menu.
|
|
296
|
+
&--overflow {
|
|
297
|
+
min-width: 0;
|
|
298
|
+
overflow: hidden;
|
|
299
|
+
|
|
300
|
+
> * {
|
|
301
|
+
flex-shrink: 0;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Enable the container query on any header that contains a responsive
|
|
307
|
+
// actions wrapper. Container query rule itself is emitted at the bottom
|
|
308
|
+
// of this file (outside the .pa-card block) since `@container` can't
|
|
309
|
+
// hide inside an arbitrarily-nested selector cleanly.
|
|
310
|
+
&__header:has(> .pa-card__actions--responsive) {
|
|
311
|
+
container-type: inline-size;
|
|
312
|
+
container-name: pa-card-header;
|
|
240
313
|
}
|
|
241
314
|
|
|
242
315
|
&__meta {
|
|
@@ -499,3 +572,79 @@ a.pa-card {
|
|
|
499
572
|
border-bottom: $border-width-medium solid var(--pa-accent);
|
|
500
573
|
padding-bottom: $spacing-sm;
|
|
501
574
|
}
|
|
575
|
+
|
|
576
|
+
// Responsive card actions — container query for the spread/collapsed swap.
|
|
577
|
+
// Container context set by `.pa-card__header:has(> .pa-card__actions--responsive)`
|
|
578
|
+
// above. Threshold needs explicit interpolation in `@container` (the Sass
|
|
579
|
+
// auto-interpolation that works in `@media` doesn't apply here).
|
|
580
|
+
@container pa-card-header (max-width: #{$card-actions-collapse-at}) {
|
|
581
|
+
.pa-card__actions--responsive {
|
|
582
|
+
> .pa-card__actions-full {
|
|
583
|
+
display: none;
|
|
584
|
+
}
|
|
585
|
+
> .pa-card__actions-collapsed {
|
|
586
|
+
display: flex;
|
|
587
|
+
align-items: center;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Card adaptation inside a minimized splitter pane.
|
|
593
|
+
// Lives here (not in `_splitter.scss`) so the splitter stays unaware of card
|
|
594
|
+
// internals — the rotation hook on the splitter side is `[data-pa-splitter-rail-title]`
|
|
595
|
+
// and this rule applies that attribute's contract to the card header.
|
|
596
|
+
//
|
|
597
|
+
// Layout: pa-card already has `height: 100%` etc., we just hide body/footer,
|
|
598
|
+
// promote the header to fill the rail, and pin the title to the top.
|
|
599
|
+
.pa-splitter__pane--minimized > .pa-card {
|
|
600
|
+
height: 100%;
|
|
601
|
+
display: flex;
|
|
602
|
+
flex-direction: column;
|
|
603
|
+
margin: 0;
|
|
604
|
+
|
|
605
|
+
> .pa-card__body,
|
|
606
|
+
> .pa-card__footer {
|
|
607
|
+
display: none;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
> .pa-card__header {
|
|
611
|
+
flex: 1 1 auto;
|
|
612
|
+
// Pin title to the rail's top (inline-start in sideways-rl writing mode).
|
|
613
|
+
justify-content: flex-start;
|
|
614
|
+
writing-mode: sideways-rl;
|
|
615
|
+
|
|
616
|
+
// Icons reset to natural orientation (descendant combinator covers both
|
|
617
|
+
// flat markup and the BEM `pa-card__title > i` nesting).
|
|
618
|
+
i,
|
|
619
|
+
svg {
|
|
620
|
+
writing-mode: initial;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Vertically align the icon between expanded and minimized states.
|
|
624
|
+
// In expanded mode, `align-items: center` centers the title vertically
|
|
625
|
+
// (icon center at header-min-height / 2 from top). In minimized mode,
|
|
626
|
+
// `justify-content: flex-start` pins the title to the rail's top edge.
|
|
627
|
+
// Push the title down by the difference so the icon sits at the same
|
|
628
|
+
// visual y-position in both states — no "jump" when toggling.
|
|
629
|
+
.pa-card__title {
|
|
630
|
+
padding-top: calc((#{$card-header-min-height} - #{$font-size-base}) / 2 - #{$card-header-padding-v});
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Hide interactive controls inside the header — they'd be sideways and
|
|
634
|
+
// unreadable. The list here is intentional: cards know their own header
|
|
635
|
+
// contents better than the splitter ever could.
|
|
636
|
+
.pa-btn,
|
|
637
|
+
.pa-btn-group,
|
|
638
|
+
button,
|
|
639
|
+
input {
|
|
640
|
+
display: none;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Mirror modifier — when the splitter root carries `pa-splitter--minimize-mirror`,
|
|
646
|
+
// the card heading flips 180° (matches the generic mirror behaviour, scoped
|
|
647
|
+
// to the card title heading).
|
|
648
|
+
.pa-splitter--minimize-mirror .pa-splitter__pane--minimized > .pa-card > .pa-card__header :is(h1, h2, h3, h4, h5, h6) {
|
|
649
|
+
transform: scale(-1, -1);
|
|
650
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/* ========================================
|
|
2
|
+
Splitter Component
|
|
3
|
+
Resizable container with draggable gutters (2 or more panes).
|
|
4
|
+
Modifiers:
|
|
5
|
+
--horizontal panes side-by-side, vertical gutter (default)
|
|
6
|
+
--vertical panes stacked, horizontal gutter
|
|
7
|
+
--dragging applied to root while user is dragging
|
|
8
|
+
--collapsed start pane has been collapsed to 0
|
|
9
|
+
--minimize-mirror flip rail-title text 180° (bottom-to-top reading)
|
|
10
|
+
|
|
11
|
+
Rail mode is fully decoupled from any specific component. The splitter
|
|
12
|
+
only rotates elements marked `[data-pa-splitter-rail-title]` inside a
|
|
13
|
+
minimized pane; per-component adaptation (e.g. card body / footer
|
|
14
|
+
hiding) lives in that component's SCSS, keyed off
|
|
15
|
+
`.pa-splitter__pane--minimized > .pa-foo`. Card integration is in
|
|
16
|
+
`_cards.scss`.
|
|
17
|
+
======================================== */
|
|
18
|
+
@use '../variables' as *;
|
|
19
|
+
|
|
20
|
+
// Default rail width — exposed as a runtime token so themes and per-instance
|
|
21
|
+
// inline styles can override without having to set both the SCSS variable
|
|
22
|
+
// (compile-time) and the JS attribute. The JS reads this same custom
|
|
23
|
+
// property via getComputedStyle to keep its default in sync.
|
|
24
|
+
:root {
|
|
25
|
+
--pa-splitter-rail-size: #{$splitter-rail-size};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.pa-splitter {
|
|
29
|
+
display: flex;
|
|
30
|
+
width: 100%;
|
|
31
|
+
height: 100%;
|
|
32
|
+
min-width: 0;
|
|
33
|
+
min-height: 0;
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
|
|
36
|
+
// Horizontal split: panes laid out as a row, gutter is a vertical bar.
|
|
37
|
+
// Gutter thickness is overridable per-instance via --pa-splitter-gutter-size;
|
|
38
|
+
// breathing room around the gutter via native `gap` (the JS subtracts it
|
|
39
|
+
// from the available space, so percent constraints stay accurate).
|
|
40
|
+
&--horizontal {
|
|
41
|
+
flex-direction: row;
|
|
42
|
+
|
|
43
|
+
> .pa-splitter__gutter {
|
|
44
|
+
cursor: col-resize;
|
|
45
|
+
width: var(--pa-splitter-gutter-size, #{$splitter-gutter-size});
|
|
46
|
+
height: auto;
|
|
47
|
+
align-self: stretch;
|
|
48
|
+
|
|
49
|
+
&::before {
|
|
50
|
+
width: $splitter-gutter-grip-thickness;
|
|
51
|
+
height: $splitter-gutter-grip-length;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Vertical split: panes stacked as a column, gutter is a horizontal bar.
|
|
57
|
+
&--vertical {
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
|
|
60
|
+
> .pa-splitter__gutter {
|
|
61
|
+
cursor: row-resize;
|
|
62
|
+
width: auto;
|
|
63
|
+
height: var(--pa-splitter-gutter-size, #{$splitter-gutter-size});
|
|
64
|
+
align-self: stretch;
|
|
65
|
+
|
|
66
|
+
&::before {
|
|
67
|
+
width: $splitter-gutter-grip-length;
|
|
68
|
+
height: $splitter-gutter-grip-thickness;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// While dragging: kill text selection and transitions, neutralize pane content
|
|
74
|
+
// pointer events so dragging over an iframe/embedded widget doesn't get hijacked.
|
|
75
|
+
// Only the gutter that's actually being dragged gets the "active" highlight —
|
|
76
|
+
// siblings stay in their resting state. The JS adds `--active` on the dragged
|
|
77
|
+
// gutter in pointerdown and removes it in pointerup. Sibling gutters also get
|
|
78
|
+
// `pointer-events: none` so their `:hover` doesn't fire as the cursor passes
|
|
79
|
+
// over them during the drag (otherwise they'd light up via hover).
|
|
80
|
+
&--dragging {
|
|
81
|
+
user-select: none;
|
|
82
|
+
|
|
83
|
+
> .pa-splitter__pane {
|
|
84
|
+
pointer-events: none;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Sibling gutters during drag: kill pointer events (no hover firing as
|
|
88
|
+
// cursor passes over them) AND override any lingering focus-visible
|
|
89
|
+
// styling (a previous keyboard focus on a non-active gutter would
|
|
90
|
+
// otherwise look identical to the active state — both colour the
|
|
91
|
+
// gutter blue).
|
|
92
|
+
> .pa-splitter__gutter:not(.pa-splitter__gutter--active) {
|
|
93
|
+
pointer-events: none;
|
|
94
|
+
|
|
95
|
+
&:focus-visible {
|
|
96
|
+
box-shadow: none;
|
|
97
|
+
|
|
98
|
+
&::before {
|
|
99
|
+
background-color: $splitter-gutter-grip-color;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
&__gutter--active {
|
|
106
|
+
background-color: $splitter-gutter-active-bg;
|
|
107
|
+
|
|
108
|
+
&::before {
|
|
109
|
+
background-color: $splitter-gutter-grip-hover-color;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
&__pane {
|
|
114
|
+
overflow: auto;
|
|
115
|
+
min-width: 0;
|
|
116
|
+
min-height: 0;
|
|
117
|
+
|
|
118
|
+
// Start pane carries the explicit size set inline by JS (flex-basis).
|
|
119
|
+
// flex-grow: 0 keeps it from absorbing free space; flex-shrink: 0 stops
|
|
120
|
+
// the browser from squishing it below the JS-applied size when content
|
|
121
|
+
// overflows.
|
|
122
|
+
&--start {
|
|
123
|
+
flex: 0 0 auto;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// End pane fills whatever's left over.
|
|
127
|
+
&--end {
|
|
128
|
+
flex: 1 1 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Minimized state — pane locks to rail width via flex-basis (JS-set).
|
|
132
|
+
// The splitter's only job here is providing the rail-title rotation hook;
|
|
133
|
+
// it does NOT hide sibling content or know about specific components.
|
|
134
|
+
// Per-component adaptation (e.g. hiding card body / footer) lives in
|
|
135
|
+
// that component's SCSS, see `_cards.scss` for the card integration.
|
|
136
|
+
&--minimized {
|
|
137
|
+
cursor: pointer;
|
|
138
|
+
overflow: hidden;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Generic rail-title hook. Any element marked `[data-pa-splitter-rail-title]`
|
|
143
|
+
// inside a minimized pane rotates to vertical writing. Consumers can put
|
|
144
|
+
// this attribute on a card header, a plain heading, a div with an icon —
|
|
145
|
+
// anything they want shown rotated when the pane is railed.
|
|
146
|
+
//
|
|
147
|
+
// sideways-rl: text reads top-to-bottom, glyphs rotated 90° clockwise
|
|
148
|
+
// (tilt head right). Script-agnostic — for Latin text this matches
|
|
149
|
+
// vertical-rl visually.
|
|
150
|
+
&__pane--minimized [data-pa-splitter-rail-title] {
|
|
151
|
+
writing-mode: sideways-rl;
|
|
152
|
+
|
|
153
|
+
// Keep icons upright — they inherit the parent writing-mode by default,
|
|
154
|
+
// which rotates the glyph along with the title. Resetting to initial
|
|
155
|
+
// (horizontal-tb) keeps them in their natural orientation.
|
|
156
|
+
i,
|
|
157
|
+
svg {
|
|
158
|
+
writing-mode: initial;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Optional: mirror the minimized title 180° so the text reads bottom-to-top
|
|
163
|
+
// with upside-down glyphs. Applied to the heading element inside any
|
|
164
|
+
// rail-title element — padding, alignment, and sibling content are
|
|
165
|
+
// unaffected.
|
|
166
|
+
&--minimize-mirror .pa-splitter__pane--minimized [data-pa-splitter-rail-title] :is(h1, h2, h3, h4, h5, h6) {
|
|
167
|
+
transform: scale(-1, -1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
&__gutter {
|
|
171
|
+
position: relative;
|
|
172
|
+
flex: 0 0 auto;
|
|
173
|
+
background-color: $splitter-gutter-bg;
|
|
174
|
+
transition: $splitter-transition;
|
|
175
|
+
touch-action: none; // prevents browser scroll/zoom while dragging on touch
|
|
176
|
+
outline: none;
|
|
177
|
+
|
|
178
|
+
// Centered grip indicator. Width/height per orientation modifier above.
|
|
179
|
+
&::before {
|
|
180
|
+
content: '';
|
|
181
|
+
position: absolute;
|
|
182
|
+
top: 50%;
|
|
183
|
+
left: 50%;
|
|
184
|
+
transform: translate(-50%, -50%);
|
|
185
|
+
background-color: $splitter-gutter-grip-color;
|
|
186
|
+
border-radius: $border-radius-sm;
|
|
187
|
+
transition: $splitter-transition;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
&:hover {
|
|
191
|
+
background-color: $splitter-gutter-hover-bg;
|
|
192
|
+
|
|
193
|
+
&::before {
|
|
194
|
+
background-color: $splitter-gutter-grip-hover-color;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
&:focus-visible {
|
|
199
|
+
box-shadow: inset 0 0 0 $splitter-focus-ring-width $splitter-focus-ring-color;
|
|
200
|
+
|
|
201
|
+
&::before {
|
|
202
|
+
background-color: $splitter-gutter-grip-hover-color;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
@@ -187,6 +187,11 @@ $card-tab-inline-padding-v: 0.3rem !default; // 3px - compact for header
|
|
|
187
187
|
$card-tab-inline-padding-h: 0.8rem !default; // 8px
|
|
188
188
|
$card-tab-hover-opacity: 0.1 !default;
|
|
189
189
|
|
|
190
|
+
// Responsive actions — header width below which the spread button list
|
|
191
|
+
// hides and the collapsed (split-button) form takes over. Default 28rem
|
|
192
|
+
// = 280px, which fits a 3-button action set comfortably.
|
|
193
|
+
$card-actions-collapse-at: 28rem !default;
|
|
194
|
+
|
|
190
195
|
// ============================================================================
|
|
191
196
|
// TABLE SYSTEM
|
|
192
197
|
// ============================================================================
|
|
@@ -772,3 +777,20 @@ $bar-list-bar-bg: rgba(0, 0, 0, 0.06) !default;
|
|
|
772
777
|
$bar-list-label-font-size: $font-size-sm !default;
|
|
773
778
|
$bar-list-value-font-size: $font-size-sm !default;
|
|
774
779
|
$bar-list-value-font-weight: $font-weight-semibold !default;
|
|
780
|
+
|
|
781
|
+
// ============================================================================
|
|
782
|
+
// SPLITTER SYSTEM
|
|
783
|
+
// ============================================================================
|
|
784
|
+
$splitter-gutter-size: 0.6rem !default; // 6px gutter thickness
|
|
785
|
+
$splitter-gutter-bg: rgba(128, 128, 128, 0.08) !default;
|
|
786
|
+
$splitter-gutter-hover-bg: rgba(128, 128, 128, 0.18) !default;
|
|
787
|
+
$splitter-gutter-active-bg: rgba($accent-color, 0.35) !default; // mid-drag
|
|
788
|
+
$splitter-gutter-grip-length: 2.4rem !default; // 24px grip indicator
|
|
789
|
+
$splitter-gutter-grip-thickness: 2px !default;
|
|
790
|
+
$splitter-gutter-grip-color: rgba(128, 128, 128, 0.45) !default;
|
|
791
|
+
$splitter-gutter-grip-hover-color: $accent-color !default;
|
|
792
|
+
$splitter-focus-ring-color: rgba($accent-color, 0.4) !default;
|
|
793
|
+
$splitter-focus-ring-width: 2px !default;
|
|
794
|
+
$splitter-transition: background-color 0.15s ease !default;
|
|
795
|
+
$splitter-pane-min-size: 0 !default; // absolute floor regardless of constraints
|
|
796
|
+
$splitter-rail-size: 4rem !default; // 40px — start pane width when minimized to rail
|