@keenmate/pure-admin-core 2.9.0-rc01 → 2.9.0-rc04

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.
Files changed (36) hide show
  1. package/README.md +61 -14
  2. package/dist/css/main.css +748 -2
  3. package/package.json +3 -1
  4. package/snippets/buttons.html +51 -0
  5. package/snippets/cards.html +34 -4
  6. package/snippets/comparison.html +26 -22
  7. package/snippets/manifest.json +180 -115
  8. package/snippets/range-group.html +125 -0
  9. package/snippets/splitter.html +216 -0
  10. package/snippets/statistics.html +31 -0
  11. package/src/js/btn-split-auto-absorb.js +327 -0
  12. package/src/js/command-palette.js +472 -0
  13. package/src/js/file-selector.js +1275 -0
  14. package/src/js/internal/logging.js +121 -0
  15. package/src/js/logic-tree-renderer.js +303 -0
  16. package/src/js/modal-dialogs.js +460 -0
  17. package/src/js/overflow.js +371 -0
  18. package/src/js/pa-stat-fit.js +184 -0
  19. package/src/js/range-group.js +663 -0
  20. package/src/js/search-autocomplete-v2.js +907 -0
  21. package/src/js/search-autocomplete.js +434 -0
  22. package/src/js/settings-panel.js +245 -0
  23. package/src/js/split-button.js +141 -0
  24. package/src/js/splitter.js +1323 -0
  25. package/src/js/toast-service.js +302 -0
  26. package/src/js/tooltips-popovers.js +275 -0
  27. package/src/js/virtual-scroll.js +143 -0
  28. package/src/js/virtual-textbox.js +803 -0
  29. package/src/scss/_core.scss +10 -0
  30. package/src/scss/core-components/_buttons.scss +59 -0
  31. package/src/scss/core-components/_cards.scss +206 -0
  32. package/src/scss/core-components/_overflow.scss +50 -0
  33. package/src/scss/core-components/_range-group.scss +474 -0
  34. package/src/scss/core-components/_splitter.scss +206 -0
  35. package/src/scss/core-components/_statistics.scss +163 -0
  36. package/src/scss/variables/_components.scss +56 -2
@@ -72,6 +72,9 @@
72
72
  // Filter card (expandable table filters)
73
73
  @use 'core-components/filter-card' as *;
74
74
 
75
+ // Range group (compact multi-range filter: slider primitive + floating panel)
76
+ @use 'core-components/range-group' as *;
77
+
75
78
  // Comparison tables
76
79
  @use 'core-components/comparison' as *;
77
80
 
@@ -111,6 +114,13 @@
111
114
  // Settings Panel
112
115
  @use 'core-components/settings-panel' as *;
113
116
 
117
+ // Splitter (resizable two-pane container)
118
+ @use 'core-components/splitter' as *;
119
+
120
+ // Overflow (progressive-collapse row — generic primitive used by toolbars,
121
+ // breadcrumb rows, chip rows, button bars, plus the card-actions variant)
122
+ @use 'core-components/overflow' as *;
123
+
114
124
  // Data Display (read-only fields)
115
125
  @use 'core-components/data-display' as *;
116
126
 
@@ -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;
@@ -477,6 +492,18 @@
477
492
  border-end-start-radius: 0;
478
493
  }
479
494
 
495
+ // Auto-absorb collapsed state (btn-split-auto-absorb.js): the split
496
+ // button's own primary action has folded into the dropdown, leaving only
497
+ // the toggle. Render the lone toggle as a standalone, fully-rounded button
498
+ // — override the flattened inner corners (the toggle is now :first-child,
499
+ // so it also picks up the primary's flat-end rule) and drop the divider
500
+ // border. `:first-child` keeps specificity at/above the `> .pa-btn:first-child`
501
+ // rule so the all-corner radius wins.
502
+ &--collapsed > .pa-btn-split__toggle:first-child {
503
+ border-radius: var(--pa-border-radius);
504
+ border-inline-start: none;
505
+ }
506
+
480
507
  // Toggle button (chevron) - fixed square width
481
508
  &__toggle {
482
509
  border-inline-start: $border-width-base solid rgba(255, 255, 255, 0.25);
@@ -582,3 +609,35 @@
582
609
  }
583
610
  }
584
611
 
612
+ // === Button Toolbar (auto-absorb host) ===
613
+ //
614
+ // Flex row that wraps a `.pa-btn-split.pa-btn-split--auto-absorb` together
615
+ // with the sibling buttons it should absorb when there's no horizontal
616
+ // room. Provides the layout contract `btn-split-auto-absorb.js` needs in
617
+ // order to read a truthful overflow signal:
618
+ //
619
+ // - `min-width: 0` so the row can shrink below its content width
620
+ // - `overflow: hidden` so `scrollWidth > clientWidth` actually goes
621
+ // truthful (without this the row would keep growing past its parent)
622
+ // - children `flex-shrink: 0` so individual buttons don't collapse into
623
+ // unreadable slivers — the JS moves them into the menu instead
624
+ //
625
+ // The split button itself stays put (it's the anchor for the dropdown
626
+ // menu) and never shrinks; siblings get moved into / out of the menu.
627
+ //
628
+ // Standalone (any flex context) or nested inside `.pa-card__actions` when it
629
+ // is a card header's actions — `_cards.scss` makes the actions wrapper
630
+ // shrinkable in that case so the overflow signal still works.
631
+ .pa-btn-toolbar {
632
+ display: flex;
633
+ align-items: center;
634
+ gap: $spacing-sm;
635
+ min-width: 0;
636
+ overflow: hidden;
637
+ flex-shrink: 1;
638
+
639
+ > * {
640
+ flex-shrink: 0;
641
+ }
642
+ }
643
+
@@ -100,6 +100,60 @@
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
+
121
+ // Auto-absorb button toolbar (btn-split-auto-absorb.js) hosted in a header,
122
+ // nested inside `.pa-card__actions`: prioritise the ACTIONS over the title.
123
+ // The toolbar must stay shrinkable so the JS can read `scrollWidth >
124
+ // clientWidth` and decide what to fold — but the title has to yield FIRST.
125
+ // Otherwise a long title and the toolbar share the deficit by proportional
126
+ // flex-shrink (weighted by the title's *natural* width), so the toolbar is
127
+ // allocated less width than its content even while the rendered title
128
+ // truncates and leaves a visible gap — and the split button folds all the
129
+ // way down to a lone toggle. A large shrink factor makes the title absorb
130
+ // nearly all the deficit (truncating to the min-width floor) so the toolbar
131
+ // keeps its buttons; siblings only begin folding once the title bottoms out
132
+ // at the floor. Matched as a descendant (not `> `) because the toolbar sits
133
+ // one level down, inside `.pa-card__actions`.
134
+ &:has(.pa-btn-toolbar) > h1,
135
+ &:has(.pa-btn-toolbar) > h2,
136
+ &:has(.pa-btn-toolbar) > h3,
137
+ &:has(.pa-btn-toolbar) > h4,
138
+ &:has(.pa-btn-toolbar) > h5,
139
+ &:has(.pa-btn-toolbar) > h6,
140
+ &:has(.pa-btn-toolbar) > .pa-card__title {
141
+ flex-shrink: 9999;
142
+ min-width: 8rem;
143
+ }
144
+
145
+ // The actions wrapper hosting the toolbar must itself be shrinkable.
146
+ // `.pa-card__actions` defaults to `flex-shrink: 0` (above) so it holds its
147
+ // natural width — which would keep the toolbar inside at natural width too,
148
+ // and the `scrollWidth > clientWidth` signal would never fire. Let it
149
+ // shrink (`min-width: 0`) so the deficit propagates down to the toolbar,
150
+ // which keeps the `overflow: hidden` clip. `:has()` scopes this so ordinary
151
+ // action wrappers stay fixed-width.
152
+ .pa-card__actions:has(> .pa-btn-toolbar) {
153
+ flex-shrink: 1;
154
+ min-width: 0;
155
+ }
156
+
103
157
  // Buttons in card headers - negative margin to prevent header height growth
104
158
  .pa-btn {
105
159
  margin-top: -0.25rem;
@@ -107,6 +161,27 @@
107
161
  flex-shrink: 0;
108
162
  }
109
163
 
164
+ // …but any actions wrapper that CLIPS its overflow — the auto-absorb
165
+ // button toolbar and the progressive-overflow actions container both
166
+ // set `overflow: hidden` so the JS can read `scrollWidth > clientWidth` —
167
+ // would shave the top/bottom borders off those buttons: the -0.25rem
168
+ // block margin above pushes each button past the wrapper's content box,
169
+ // and the clip lands right on the border. Re-parent the compaction onto
170
+ // the wrapper so its clip edge is flush with the button box; the header
171
+ // still gets the same height reduction, borders survive. Scoped under
172
+ // `&__header` so a standalone `.pa-btn-toolbar` (used outside a card
173
+ // header) is untouched.
174
+ .pa-btn-toolbar,
175
+ .pa-card__actions--overflow {
176
+ margin-top: -0.25rem;
177
+ margin-bottom: -0.25rem;
178
+
179
+ .pa-btn {
180
+ margin-top: 0;
181
+ margin-bottom: 0;
182
+ }
183
+ }
184
+
110
185
  // Underline modifier - accent border under heading inside card header
111
186
  &--underlined {
112
187
  h1, h2, h3, h4, h5, h6 {
@@ -237,6 +312,61 @@
237
312
  display: flex;
238
313
  gap: $spacing-sm;
239
314
  align-items: center;
315
+
316
+ // Responsive actions — render both the spread button list AND a
317
+ // collapsed split-button form; a container query on the header swaps
318
+ // which is visible. Stays CSS-only (no ResizeObserver / layout reads)
319
+ // but the markup carries both forms.
320
+ //
321
+ // <div class="pa-card__header">
322
+ // <div class="pa-card__title">…</div>
323
+ // <div class="pa-card__actions pa-card__actions--responsive">
324
+ // <div class="pa-card__actions-full">
325
+ // …spread buttons…
326
+ // </div>
327
+ // <div class="pa-card__actions-collapsed">
328
+ // <div class="pa-btn-split">…same actions as a menu…</div>
329
+ // </div>
330
+ // </div>
331
+ // </div>
332
+ //
333
+ // Container is the header; threshold is $card-actions-collapse-at.
334
+ // Wired up via :has() (Baseline 2023) so authors don't need a second
335
+ // modifier on the header.
336
+ &--responsive {
337
+ > .pa-card__actions-full {
338
+ display: flex;
339
+ gap: $spacing-sm;
340
+ align-items: center;
341
+ }
342
+ > .pa-card__actions-collapsed {
343
+ display: none;
344
+ }
345
+ }
346
+
347
+ // Progressive overflow variant — driven by card-actions-overflow.js.
348
+ // `min-width: 0` lets flex shrink the wrapper below its content; with
349
+ // `overflow: hidden` the wrapper clips its children when narrow, which
350
+ // is what makes `scrollWidth > clientWidth` a reliable overflow signal
351
+ // for the JS. Buttons themselves get `flex-shrink: 0` so they don't
352
+ // squeeze to fit — they either fit fully or get moved to the menu.
353
+ &--overflow {
354
+ min-width: 0;
355
+ overflow: hidden;
356
+
357
+ > * {
358
+ flex-shrink: 0;
359
+ }
360
+ }
361
+ }
362
+
363
+ // Enable the container query on any header that contains a responsive
364
+ // actions wrapper. Container query rule itself is emitted at the bottom
365
+ // of this file (outside the .pa-card block) since `@container` can't
366
+ // hide inside an arbitrarily-nested selector cleanly.
367
+ &__header:has(> .pa-card__actions--responsive) {
368
+ container-type: inline-size;
369
+ container-name: pa-card-header;
240
370
  }
241
371
 
242
372
  &__meta {
@@ -499,3 +629,79 @@ a.pa-card {
499
629
  border-bottom: $border-width-medium solid var(--pa-accent);
500
630
  padding-bottom: $spacing-sm;
501
631
  }
632
+
633
+ // Responsive card actions — container query for the spread/collapsed swap.
634
+ // Container context set by `.pa-card__header:has(> .pa-card__actions--responsive)`
635
+ // above. Threshold needs explicit interpolation in `@container` (the Sass
636
+ // auto-interpolation that works in `@media` doesn't apply here).
637
+ @container pa-card-header (max-width: #{$card-actions-collapse-at}) {
638
+ .pa-card__actions--responsive {
639
+ > .pa-card__actions-full {
640
+ display: none;
641
+ }
642
+ > .pa-card__actions-collapsed {
643
+ display: flex;
644
+ align-items: center;
645
+ }
646
+ }
647
+ }
648
+
649
+ // Card adaptation inside a minimized splitter pane.
650
+ // Lives here (not in `_splitter.scss`) so the splitter stays unaware of card
651
+ // internals — the rotation hook on the splitter side is `[data-pa-splitter-rail-title]`
652
+ // and this rule applies that attribute's contract to the card header.
653
+ //
654
+ // Layout: pa-card already has `height: 100%` etc., we just hide body/footer,
655
+ // promote the header to fill the rail, and pin the title to the top.
656
+ .pa-splitter__pane--minimized > .pa-card {
657
+ height: 100%;
658
+ display: flex;
659
+ flex-direction: column;
660
+ margin: 0;
661
+
662
+ > .pa-card__body,
663
+ > .pa-card__footer {
664
+ display: none;
665
+ }
666
+
667
+ > .pa-card__header {
668
+ flex: 1 1 auto;
669
+ // Pin title to the rail's top (inline-start in sideways-rl writing mode).
670
+ justify-content: flex-start;
671
+ writing-mode: sideways-rl;
672
+
673
+ // Icons reset to natural orientation (descendant combinator covers both
674
+ // flat markup and the BEM `pa-card__title > i` nesting).
675
+ i,
676
+ svg {
677
+ writing-mode: initial;
678
+ }
679
+
680
+ // Vertically align the icon between expanded and minimized states.
681
+ // In expanded mode, `align-items: center` centers the title vertically
682
+ // (icon center at header-min-height / 2 from top). In minimized mode,
683
+ // `justify-content: flex-start` pins the title to the rail's top edge.
684
+ // Push the title down by the difference so the icon sits at the same
685
+ // visual y-position in both states — no "jump" when toggling.
686
+ .pa-card__title {
687
+ padding-top: calc((#{$card-header-min-height} - #{$font-size-base}) / 2 - #{$card-header-padding-v});
688
+ }
689
+
690
+ // Hide interactive controls inside the header — they'd be sideways and
691
+ // unreadable. The list here is intentional: cards know their own header
692
+ // contents better than the splitter ever could.
693
+ .pa-btn,
694
+ .pa-btn-group,
695
+ button,
696
+ input {
697
+ display: none;
698
+ }
699
+ }
700
+ }
701
+
702
+ // Mirror modifier — when the splitter root carries `pa-splitter--minimize-mirror`,
703
+ // the card heading flips 180° (matches the generic mirror behaviour, scoped
704
+ // to the card title heading).
705
+ .pa-splitter--minimize-mirror .pa-splitter__pane--minimized > .pa-card > .pa-card__header :is(h1, h2, h3, h4, h5, h6) {
706
+ transform: scale(-1, -1);
707
+ }
@@ -0,0 +1,50 @@
1
+ /* ========================================
2
+ Overflow (progressive-collapse row)
3
+
4
+ Generic primitive: a horizontal flex row whose children get walked into
5
+ a "more" menu when the row can't fit, driven by `overflow.js`. Use
6
+ anywhere you want a button toolbar, breadcrumb row, chip row, or tab bar
7
+ to collapse gracefully on narrow widths.
8
+
9
+ <div class="pa-overflow" data-pa-actions-overflow-from="end">
10
+ <button class="pa-btn pa-btn--xs">Save</button>
11
+ <button class="pa-btn pa-btn--xs" data-pa-actions-priority="10">Run</button>
12
+ </div>
13
+
14
+ The card-flavoured alias `pa-card__actions--overflow` carries equivalent
15
+ rules under `.pa-card__header` (see `_cards.scss`); both wire into the
16
+ same JS module. ======================================== */
17
+ @use '../variables' as *;
18
+
19
+ .pa-overflow {
20
+ // Flex row by default. The JS pulls children out and appends a trigger
21
+ // button at the end; both the row and the trigger inherit normal flex
22
+ // layout from this block. Consumers override gap / alignment per-instance
23
+ // via inline style or a local class.
24
+ display: flex;
25
+ align-items: center;
26
+ gap: $spacing-sm;
27
+
28
+ // Layout contract for the truthful-overflow signal: the wrapper has to
29
+ // shrink below its content (`min-width: 0`) and clip when it does
30
+ // (`overflow: hidden`) so `scrollWidth > clientWidth` reliably means
31
+ // "the row doesn't fit". Without these, modern flex defaults keep the
32
+ // wrapper at intrinsic content width and the JS sees no overflow.
33
+ // `flex-shrink: 1` so a parent flex layout can compress the row.
34
+ min-width: 0;
35
+ overflow: hidden;
36
+ flex-shrink: 1;
37
+
38
+ // Items themselves stay at intrinsic width — they either fit fully or
39
+ // get moved to the menu. Without this they'd squeeze unevenly as the
40
+ // wrapper narrows, defeating the "drop one at a time" UX.
41
+ > * {
42
+ flex-shrink: 0;
43
+ }
44
+
45
+ // Trigger button has no dedicated styling here — it ships with
46
+ // `pa-btn pa-btn--xs pa-btn--ghost` so it picks up the ghost button
47
+ // appearance automatically. The `__trigger` element class is set by
48
+ // the JS as a stable hook for consumers who want to retheme it
49
+ // (e.g. swap the ellipsis icon, change padding).
50
+ }