@guildofgleks/ui 21.5.2 → 21.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guildofgleks/ui",
3
- "version": "21.5.2",
3
+ "version": "21.6.0",
4
4
  "engines": {
5
5
  "node": ">=20.19.0"
6
6
  },
package/styles/button.css CHANGED
@@ -67,7 +67,24 @@
67
67
  color var(--gog-button-transition-duration) var(--gog-easing);
68
68
  }
69
69
 
70
- .gog-btn:focus-visible {
70
+ /*
71
+ * The doubled class on this rule and the disabled one below is deliberate, and the only two
72
+ * rules in this file that need it.
73
+ *
74
+ * `[gogButton]` goes on the *consumer's* own element, which is the element they style — and
75
+ * Angular's emulated encapsulation stamps `[_ngcontent-…]` onto every rule in a component
76
+ * stylesheet, so an ordinary `.my-btn { cursor: pointer }` is (0,2,0). That exactly ties
77
+ * `.gog-btn:disabled` and wins on source order, because this file loads before the app's own
78
+ * styles. Measured in the showcase: a disabled button went from `not-allowed`/`0.4` to
79
+ * `pointer`/`1` under a single-class consumer rule — it looked and felt enabled while disabled.
80
+ *
81
+ * `:hover` and `:active` below need no help: their `:not(:disabled)` already makes them (0,3,0).
82
+ * The base `.gog-btn` is left beatable on purpose — restyling the look is the consumer's call.
83
+ * These two are not look, they are state and focus visibility, and losing them silently is a
84
+ * correctness and accessibility regression rather than a taste one. A consumer who genuinely
85
+ * wants to restyle them can still do it by being specific about it.
86
+ */
87
+ .gog-btn.gog-btn:focus-visible {
71
88
  outline: var(--gog-button-focus-ring-width) solid
72
89
  var(
73
90
  --gog-button-hover-bg,
@@ -114,7 +131,7 @@
114
131
  }
115
132
  }
116
133
 
117
- .gog-btn:disabled {
134
+ .gog-btn.gog-btn:disabled {
118
135
  opacity: var(--gog-button-disabled-opacity);
119
136
  cursor: not-allowed;
120
137
  }
package/styles/menu.css CHANGED
@@ -74,18 +74,28 @@
74
74
  transition: background-color var(--gog-menu-transition-duration) var(--gog-easing);
75
75
  }
76
76
 
77
+ /*
78
+ * Doubled class on the `:focus-visible` and `:disabled` rules, for the reason spelled out in
79
+ * `button.css`: a menu item is the consumer's own `<button>`, so an ordinary single-class rule
80
+ * in their component stylesheet ties these at (0,2,0) and wins on source order. The `:hover`
81
+ * selector is already (0,3,0) through its `:not(:disabled)` and is left alone.
82
+ *
83
+ * It matters more here than for a button. The arrow keys step over a disabled item, so an item
84
+ * that has lost its disabled styling is one the pointer can still see as ordinary while the
85
+ * keyboard refuses to reach it — the two input methods disagreeing about what exists.
86
+ */
77
87
  .gog-menu__item:hover:not(:disabled),
78
- .gog-menu__item:focus-visible {
88
+ .gog-menu__item.gog-menu__item:focus-visible {
79
89
  background-color: var(--gog-menu-item-hover-bg);
80
90
  color: var(--gog-menu-item-hover-color);
81
91
  }
82
92
 
83
- .gog-menu__item:focus-visible {
93
+ .gog-menu__item.gog-menu__item:focus-visible {
84
94
  outline: var(--gog-menu-focus-ring-width) solid var(--gog-menu-focus-ring);
85
95
  outline-offset: calc(var(--gog-menu-focus-ring-width) * -1);
86
96
  }
87
97
 
88
- .gog-menu__item:disabled {
98
+ .gog-menu__item.gog-menu__item:disabled {
89
99
  color: var(--gog-menu-item-disabled-color);
90
100
  opacity: var(--gog-menu-item-disabled-opacity);
91
101
  cursor: not-allowed;
@@ -19,6 +19,31 @@
19
19
  --gog-direction-sign: 1;
20
20
  }
21
21
 
22
+ /*
23
+ * Every element the library renders sizes itself border-box, so the package does not depend on
24
+ * the consumer having a `* { box-sizing: border-box }` reset.
25
+ *
26
+ * It used to. `.gog-input__field` is `width: 100%` plus horizontal padding plus a border, so
27
+ * under the CSS default of `content-box` it overflowed its own container by exactly that much —
28
+ * measured 20px at `xsm` rising to 48px at `slg`. It stayed invisible for the life of the
29
+ * package because both apps that render it (`ui-showcase`, and `gleks-ui-lab` since later) carry
30
+ * a global reset of their own, so the library was only ever *looked at* under border-box.
31
+ *
32
+ * Matched on the class prefix rather than by listing elements, because the set that needs it is
33
+ * exactly "things the library painted" — including the ones that live on a consumer's own
34
+ * element (`.gog-btn`, `.gog-menu__item`, `.gog-collapsible__trigger`) and would be missed by a
35
+ * `gog-*` tag list. A consumer's own elements are untouched.
36
+ *
37
+ * Specificity is a single class, deliberately: this is a default the library needs, not a
38
+ * decision it should win. `:where()` would drop it to zero and lose to the UA's own
39
+ * `content-box`… which is not a thing, but it would also lose to a consumer's `*` reset setting
40
+ * `content-box` back, and matching that intent is right.
41
+ */
42
+ [class^='gog-'],
43
+ [class*=' gog-'] {
44
+ box-sizing: border-box;
45
+ }
46
+
22
47
  [dir='rtl'] {
23
48
  --gog-inline-start-side: right;
24
49
  --gog-inline-end-side: left;
@@ -171,7 +196,18 @@
171
196
  cursor: pointer;
172
197
  }
173
198
 
174
- .gog-collapsible__trigger[aria-disabled='true'] {
199
+ /*
200
+ * Scoped through the host on purpose, for one point of specificity the plain
201
+ * `.gog-collapsible__trigger[aria-disabled='true']` (0,2,0) does not have.
202
+ *
203
+ * The trigger is the *consumer's* element, and Angular's emulated encapsulation stamps
204
+ * `[_ngcontent-…]` onto every rule in a component stylesheet — so an ordinary
205
+ * `.my-trigger { cursor: pointer }` in the consumer's own component is also (0,2,0) and wins on
206
+ * source order, since this file loads before their app's styles. Measured: a disabled trigger
207
+ * reading `cursor: pointer` while this rule's `opacity` applied, in the showcase's own demo.
208
+ * A disabled state the consumer has to know to opt out of is not a disabled state.
209
+ */
210
+ gog-collapsible .gog-collapsible__trigger[aria-disabled='true'] {
175
211
  cursor: not-allowed;
176
212
  opacity: var(--gog-collapsible-disabled-opacity);
177
213
  }
@@ -282,7 +282,7 @@ declare class AccordionComponent {
282
282
  * Left unset by default since not every accordion instance represents document structure
283
283
  * (e.g. one nested inside a card).
284
284
  */
285
- readonly headingLevel: _angular_core.InputSignal<2 | 3 | 4 | 5 | 6 | undefined>;
285
+ readonly headingLevel: _angular_core.InputSignal<2 | 4 | 3 | 5 | 6 | undefined>;
286
286
  /**
287
287
  * Two-way bindable set of currently open item ids. Exposed as a model so consumers
288
288
  * can drive the accordion externally (e.g. `[(openIds)]="mySet"`) instead of only
@@ -310,6 +310,15 @@ declare class AccordionComponent {
310
310
  */
311
311
  private readonly autoExpanded;
312
312
  protected readonly skeletonRows: _angular_core.Signal<number[]>;
313
+ /**
314
+ * Placeholder title widths, cycled by row index.
315
+ *
316
+ * Every bar used to be 55%, which reads as a repeating progress artifact rather than as text
317
+ * that has not arrived — real titles are not all the same length. Cycled rather than random:
318
+ * a random width would differ between the server-rendered pass and hydration, and would make
319
+ * every snapshot of this component unstable.
320
+ */
321
+ protected skeletonWidth(index: number): string;
313
322
  constructor();
314
323
  protected isOpen(id: string | number): boolean;
315
324
  protected toggle(item: GogAccordionItem): void;
@@ -935,6 +944,18 @@ declare class AutocompleteComponent<TOption = GogDropdownOption, TValue = string
935
944
  protected readonly visibleOptions: _angular_core.Signal<TOption[]>;
936
945
  protected readonly activeOptionId: _angular_core.Signal<string | null>;
937
946
  private searchTimer;
947
+ /**
948
+ * True from the first keystroke until the edit is resolved — by blur, Escape, picking an
949
+ * option, or the clear button. Deliberately a plain field, not a signal: the sync effect below
950
+ * must not re-run when it changes, only consult it.
951
+ *
952
+ * It exists because the effect used to use `!isOpen()` to mean "the user is not mid-edit", and
953
+ * the panel closes for reasons that are not that. Backspacing the last character takes the text
954
+ * under `minLength`, which closes the panel — so the effect fired and wrote the selected label
955
+ * straight back into the field. Nine backspaces on "Amsterdam" left "Amsterdam": the field
956
+ * could not be cleared at all while a selection was held.
957
+ */
958
+ private editing;
938
959
  constructor();
939
960
  protected isSelected(option: TOption): boolean;
940
961
  protected onInput(event: Event): void;
@@ -3671,8 +3692,34 @@ declare class TableComponent<T extends object> {
3671
3692
  readonly loading: _angular_core.InputSignal<boolean>;
3672
3693
  /** Show vertical borders between columns */
3673
3694
  readonly showColumnBorders: _angular_core.InputSignal<boolean>;
3674
- /** Stick header row to the top of the viewport while scrolling */
3695
+ /**
3696
+ * Stick the header row to the top of the table's own scroll viewport.
3697
+ *
3698
+ * **Needs `maxHeight` to be reliable.** A sticky element resolves against its nearest scroll
3699
+ * container, and the table wraps itself in a `gog-scroll`; the moment that scroller starts
3700
+ * scrolling sideways it is a scroll container on both axes — CSS coerces `overflow-y: visible`
3701
+ * to `auto` beside a scrolling `overflow-x`, and `clip` to `hidden`, so there is no value that
3702
+ * scrolls one axis and stays out of the sticky chain on the other. Without `maxHeight` that
3703
+ * viewport is exactly as tall as its content and never scrolls vertically, so a header pinned
3704
+ * to it rides out of view along with everything else.
3705
+ *
3706
+ * With `maxHeight` set the viewport is the vertical scrollport, and the header pins to it.
3707
+ */
3675
3708
  readonly stickyHeader: _angular_core.InputSignal<boolean>;
3709
+ /**
3710
+ * Caps the table's own scroll viewport, in any CSS length — `'420px'`, `'60vh'`. The table then
3711
+ * owns its vertical scrolling instead of growing to its content and letting an ancestor scroll
3712
+ * it, which is what makes `stickyHeader` work (see above).
3713
+ *
3714
+ * An input rather than a `--gog-table-*` token, even though the value only ever lands in CSS:
3715
+ * it also decides whether the internal scroller handles the vertical axis at all. A capped
3716
+ * viewport has to scroll vertically; an uncapped one must *not* become a scroll container,
3717
+ * because that would put every table in the sticky chain of its own descendants and take the
3718
+ * consumer's own scrolling region out of it. That is behaviour, not appearance.
3719
+ *
3720
+ * `null` leaves the table exactly as it was: viewport at content height, vertical axis inert.
3721
+ */
3722
+ readonly maxHeight: _angular_core.InputSignal<string | null>;
3676
3723
  /** Row density: lg (default) / md (compact) / sm (dense) */
3677
3724
  readonly size: _angular_core.InputSignal<GogSize>;
3678
3725
  /**
@@ -3782,7 +3829,7 @@ declare class TableComponent<T extends object> {
3782
3829
  /** CSS grid-area for the pagination block */
3783
3830
  readonly paginatorGridArea: _angular_core.Signal<"left" | "right" | "center">;
3784
3831
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TableComponent<any>, never>;
3785
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<TableComponent<any>, "gog-table", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "fullWidth": { "alias": "fullWidth"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "showPageSizeSelect": { "alias": "showPageSizeSelect"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "lazy": { "alias": "lazy"; "required": false; "isSignal": true; }; "totalRecords": { "alias": "totalRecords"; "required": false; "isSignal": true; }; "interactiveRows": { "alias": "interactiveRows"; "required": false; "isSignal": true; }; "selectionMode": { "alias": "selectionMode"; "required": false; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "dataKey": { "alias": "dataKey"; "required": false; "isSignal": true; }; "showSelectionColumn": { "alias": "showSelectionColumn"; "required": false; "isSignal": true; }; "showRowNumbers": { "alias": "showRowNumbers"; "required": false; "isSignal": true; }; "showTotal": { "alias": "showTotal"; "required": false; "isSignal": true; }; "emptyPlaceholder": { "alias": "emptyPlaceholder"; "required": false; "isSignal": true; }; "paginatorPosition": { "alias": "paginatorPosition"; "required": false; "isSignal": true; }; "totalPosition": { "alias": "totalPosition"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "showColumnBorders": { "alias": "showColumnBorders"; "required": false; "isSignal": true; }; "stickyHeader": { "alias": "stickyHeader"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, { "pageSize": "pageSizeChange"; "selection": "selectionChange"; "gogSortChange": "gogSortChange"; "gogPageChange": "gogPageChange"; "gogRowClick": "gogRowClick"; }, ["columns"], never, true, never>;
3832
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TableComponent<any>, "gog-table", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "fullWidth": { "alias": "fullWidth"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "showPageSizeSelect": { "alias": "showPageSizeSelect"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "lazy": { "alias": "lazy"; "required": false; "isSignal": true; }; "totalRecords": { "alias": "totalRecords"; "required": false; "isSignal": true; }; "interactiveRows": { "alias": "interactiveRows"; "required": false; "isSignal": true; }; "selectionMode": { "alias": "selectionMode"; "required": false; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "dataKey": { "alias": "dataKey"; "required": false; "isSignal": true; }; "showSelectionColumn": { "alias": "showSelectionColumn"; "required": false; "isSignal": true; }; "showRowNumbers": { "alias": "showRowNumbers"; "required": false; "isSignal": true; }; "showTotal": { "alias": "showTotal"; "required": false; "isSignal": true; }; "emptyPlaceholder": { "alias": "emptyPlaceholder"; "required": false; "isSignal": true; }; "paginatorPosition": { "alias": "paginatorPosition"; "required": false; "isSignal": true; }; "totalPosition": { "alias": "totalPosition"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "showColumnBorders": { "alias": "showColumnBorders"; "required": false; "isSignal": true; }; "stickyHeader": { "alias": "stickyHeader"; "required": false; "isSignal": true; }; "maxHeight": { "alias": "maxHeight"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, { "pageSize": "pageSizeChange"; "selection": "selectionChange"; "gogSortChange": "gogSortChange"; "gogPageChange": "gogPageChange"; "gogRowClick": "gogRowClick"; }, ["columns"], never, true, never>;
3786
3833
  }
3787
3834
 
3788
3835
  declare class SpinnerComponent {