@guildofgleks/ui 21.4.1 → 21.4.2

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/AGENTS.md CHANGED
@@ -5,7 +5,7 @@ an app that **consumes** the published `@guildofgleks/ui` npm package. It is not
5
5
  authoring the library — if you are working inside the `gleks_web_ui` monorepo itself, read
6
6
  `.github/instructions/*.md` instead.
7
7
 
8
- Everything below reflects the library's actual source as of **`21.4.0`**. `README.md` covers the
8
+ Everything below reflects the library's actual source as of **`21.4.1`**. `README.md` covers the
9
9
  same ground at a higher level — install, setup, theming, global configuration — and is accurate;
10
10
  this file goes further, into per-component input tables, and is the one to trust for exact names,
11
11
  types and defaults.
@@ -648,23 +648,41 @@ a generic accessor, unlike select/multiselect/button-toggle).
648
648
 
649
649
  #### `gog-slider`
650
650
 
651
- | Input | Type | Default |
652
- | ------------------------------ | ---------------------- | ---------------------------------------------- |
653
- | `label`, `ariaLabel` | `string` | `''` |
654
- | `min`, `max`, `step` | `number` | `0`, `100`, `1` |
655
- | `showValue`, `showThumb` | `boolean` | `true` |
656
- | `errorMessage`, `errorDisplay` | | `''`, `'manual'` |
657
- | `disabled` | `boolean` | `false` |
658
- | `fullWidth` | `boolean` | `true` (ignored when `orientation="vertical"`) |
659
- | `orientation` | `GogSliderOrientation` | `'horizontal'` |
660
-
661
- Model: `value: number`. CVA: yes. Backed by a real `<input type="range">` (rotated via
662
- `writing-mode` for vertical), so dragging/touch/keyboard all come from the platform.
651
+ | Input | Type | Default |
652
+ | --------------------------------- | ---------------------- | ---------------------------------------------- |
653
+ | `label`, `ariaLabel` | `string` | `''` |
654
+ | `min`, `max`, `step` | `number` | `0`, `100`, `1` |
655
+ | `showValue`, `showThumb` | `boolean` | `true` |
656
+ | `errorMessage`, `errorDisplay` | | `''`, `'manual'` |
657
+ | `disabled` | `boolean` | `false` |
658
+ | `fullWidth` | `boolean` | `true` (ignored when `orientation="vertical"`) |
659
+ | `orientation` | `GogSliderOrientation` | `'horizontal'` |
660
+ | `range` | `boolean` | `false` — two thumbs; see below |
661
+ | `startDisabled`, `endDisabled` | `boolean` | `false` `range` only |
662
+ | `startAriaLabel`, `endAriaLabel` | `string` | `'Minimum'` / `'Maximum'`, prefixed by `label` |
663
+
664
+ Models: `value: number`, and `rangeValue: GogSliderRange` (`{ start: number; end: number }`).
665
+ CVA: yes. Backed by a real `<input type="range">` (rotated via `writing-mode` for vertical), so
666
+ dragging/touch/keyboard all come from the platform.
663
667
 
664
668
  ```html
665
669
  <gog-slider label="Volume" [min]="0" [max]="100" formControlName="volume" />
666
670
  ```
667
671
 
672
+ **Range mode.** `[range]="true"` puts a second thumb on the track and switches which model is
673
+ live: bind `[(rangeValue)]` instead of `[(value)]`. The two are **mutually exclusive** — `value`
674
+ (and a form control's `writeValue`) is ignored while `range` is on, and vice versa.
675
+
676
+ ```html
677
+ <gog-slider label="Price" [range]="true" [(rangeValue)]="price" startAriaLabel="Lowest" />
678
+ ```
679
+
680
+ Each thumb needs its own accessible name, because one `<label>` cannot be associated with two
681
+ inputs through `for`; unset, they fall back to `'Minimum'`/`'Maximum'` prefixed with `label`
682
+ (`'Price Minimum'`). `startDisabled`/`endDisabled` pin one end while the other stays movable —
683
+ they are ORed with `disabled` rather than overriding it, and unlike it they do not dim the whole
684
+ control or cut pointer events over the track, which would take the still-enabled thumb with them.
685
+
668
686
  #### `gog-datepicker` / `gog-calendar`
669
687
 
670
688
  `gog-datepicker` is a field + panel; `gog-calendar` is the month grid alone (what `inline` mode
@@ -780,6 +798,11 @@ providers: [
780
798
  as `provideGogConfig`: a lazy route can register only what it uses.
781
799
  - **An unknown name renders nothing and warns in dev mode; it never throws.** An icon is
782
800
  decoration — failing the render over a typo would be the worse outcome.
801
+ - **`GOG_ICONS`** is the `InjectionToken<Readonly<Record<string, string>>>` behind it, exported
802
+ for the one case `provideGogIcons` does not cover: reading the registered set back
803
+ (`inject(GOG_ICONS)`) to enumerate it in an icon picker. Provide it through
804
+ `provideGogIcons(...)` rather than directly — the helper is what layers a child injector's
805
+ icons onto the parent's instead of replacing them.
783
806
  - **Write the SVG for inheritance:** a `viewBox`, `stroke="currentColor"` (or `fill`), and no
784
807
  width/height — `gog-icon` drives size and stroke width from the `--gog-icon-*` tokens, so a
785
808
  registered icon scales and colours like a built-in.
package/CHANGELOG.md ADDED
@@ -0,0 +1,957 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@guildofgleks/ui` are documented here. Format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.0.0/); this project has not yet
5
+ reached 1.0, so breaking changes may land in minor versions.
6
+
7
+ ## [21.5.0] - planned
8
+
9
+ ### Changed
10
+
11
+ - **`CHANGELOG.md` now ships inside the npm package**, alongside `README.md`, `AGENTS.md` and
12
+ `TOKENS.md`. It was repo-only, which meant the documentation site could not render release
13
+ notes for the exact version a reader has installed — the only source that cannot drift, since
14
+ it travels with the package rather than being copied next to it. Nothing changes for a
15
+ consumer who does not read it; the file adds a few KB to the tarball.
16
+
17
+ ### Fixed
18
+
19
+ - **`AGENTS.md` was missing `gog-slider`'s range mode.** `range`, `rangeValue`
20
+ (`GogSliderRange`), `startDisabled`/`endDisabled` and `startAriaLabel`/`endAriaLabel` shipped
21
+ in 21.3.1 but never reached the agent reference, so an agent reading it would conclude the
22
+ slider cannot express a range and build a two-slider workaround.
23
+ - **`AGENTS.md` did not mention the `GOG_ICONS` token.** `provideGogIcons(...)` was documented,
24
+ but not the token it provides — which is what an app injects to read the registered set back
25
+ (an icon picker enumerating it). Public since 21.4.0, undocumented until now.
26
+
27
+ ## [21.4.1] - 14.08.2026
28
+
29
+ ### Fixed
30
+
31
+ - **Overlays ignored custom properties set on `:root`.** A select panel, tooltip or any other
32
+ overlay rendered into `<body>` copied the `data-theme` of its trigger's nearest themed
33
+ ancestor. When that ancestor is `<html>` — the usual case — the copy made the overlay match
34
+ `theme.css`'s derived layer (`:root, [data-theme]`) *locally*, re-declaring every component
35
+ token against the plain preset palette and discarding anything set on the root that the preset
36
+ does not itself declare.
37
+
38
+ Inline custom properties are what this hit: a page that overrides `--gog-*` on
39
+ `document.documentElement` — a live theme editor, or any runtime accent switch — saw the
40
+ document follow while every overlay kept rendering the un-edited theme.
41
+
42
+ The attribute is now copied only for a genuinely *scoped* theme, where the overlay would
43
+ otherwise pick up the document's; when the theme sits on the document element, inheritance
44
+ already does the work. Several themes rendered side by side in scoped subtrees keep working
45
+ exactly as before.
46
+
47
+ ## [21.4.0] - 14.08.2026
48
+
49
+ A minor rather than a patch: this adds public API. Iterations 5 and 6 of the consumer-DX plan
50
+ (`docs/consumer-dx-plan.md`).
51
+
52
+ ### Added
53
+
54
+ - **`gog-table`: outputs.** The component had none at all, which is what made it a display-only
55
+ grid. `gogSortChange` (`{ field, direction }`, including the third click that clears the sort),
56
+ `gogPageChange` (the new 1-based page), and `gogRowClick`
57
+ (`{ row, index, originalEvent }`).
58
+
59
+ `gogPageChange` deliberately stays quiet in two cases: the initial render, and the reset to
60
+ page 1 that a new sort causes — that reset is part of the sort, and a consumer refetching from
61
+ both events would issue two requests for one user action.
62
+ - **`gog-table`: `lazy` — server-driven sorting and paging.** With `[lazy]="true"` the table
63
+ stops sorting and slicing `value` and renders it exactly as handed over, treating it as the
64
+ current page; `totalRecords` tells the paginator how many pages exist, and the two outputs are
65
+ the refetch signals. Row numbering still counts from the current page, and `showTotal` reports
66
+ `totalRecords` rather than `value.length`. Without `totalRecords` pagination stays hidden and
67
+ the table warns in dev mode. Until now the table sorted and paged purely in memory, so anything
68
+ backed by a real endpoint had to be built on something else.
69
+ - **`gog-table`: row selection.** `selectionMode` (`'none' | 'single' | 'multiple'`) plus a
70
+ two-way `[(selection)]`, always a `T[]` — in `'single'` mode it simply holds zero or one row,
71
+ which is one shape to read rather than a `T | T[] | null` union to narrow. A checkbox column
72
+ renders automatically (`showSelectionColumn` turns it off), and the header select-all appears
73
+ only in `'multiple'` mode.
74
+
75
+ **The select-all covers the current page, not the whole data set** — in `lazy` mode the table
76
+ has never seen the other pages, and a control that meant different things in the two modes
77
+ would be worse than either behaviour on its own.
78
+ - **`gog-table`: `dataKey`.** The field (or dot-path) identifying a row. Selection matches on it
79
+ instead of object identity — without it a refetch producing new objects silently drops the
80
+ selection — and it becomes the `@for` track key, so the rendered DOM survives a refetch of the
81
+ same page instead of being torn down and rebuilt.
82
+ - **`gog-table`: `interactiveRows`.** Makes rows focusable and styles them as clickable, with
83
+ Enter and Space activating the focused row. `gogRowClick` fires on a click either way; this is
84
+ what stops a whole-row target from being mouse-only.
85
+ - **`[gogButton]` — a link that looks like a button.** `gog-button` renders its own `<button>`,
86
+ so it could never *be* a link, and a large share of buttons on a real site are navigation. The
87
+ directive inverts the relationship: the element stays the consumer's, and only the look is
88
+ applied.
89
+
90
+ ```html
91
+ <a gogButton routerLink="/pricing">See pricing</a>
92
+ <a gogButton variant="ghost" href="https://example.com" target="_blank" rel="noreferrer">Docs</a>
93
+ <button gogButton variant="outline" size="sm" type="submit">Save</button>
94
+ ```
95
+
96
+ Chosen over an `as="a"` / `routerLink` input trio on `gog-button` because that would mean
97
+ brokering the router's whole input surface through the component **and taking a dependency on
98
+ `@angular/router`** — a fifth peer, and one that would break every app without a router. With
99
+ the directive, `routerLink`, `href`, `target`, `download`, `type="submit"` and anything else
100
+ keep working because they were never taken away.
101
+
102
+ `variant`, `size` and `fullWidth` behave exactly as on the component, `size` included in its
103
+ `GOG_CONFIG.control.size` fallback. It deliberately has no `disabled` (there is no such thing
104
+ on an `<a>`) and no `loading` (the spinner is a projected child a directive cannot add). The
105
+ selector is `a[gogButton], button[gogButton]`, not a bare attribute, so it cannot be put on a
106
+ `<div>` and produce something that looks clickable and is invisible to the keyboard.
107
+ - **`gog-paginator`: a rows-per-page select.** `showPageSizeSelect` turns it on (**off by
108
+ default** — a paginator that silently grew a control would change every existing layout) and
109
+ `pageSizeOptions` sets the choices, defaulting to `[10, 20, 30, 40, 50]`. Both are also
110
+ settable app-wide through the new `GOG_CONFIG.paginator`, so one page can offer `5, 10, 20`
111
+ while the rest of the app uses the house default.
112
+ - **`gog-paginator`: `pageSize` (a `model`) and `totalRecords`.** Given `totalRecords`, the
113
+ paginator derives the page count from `pageSize` itself — which removes the
114
+ `computed(() => Math.ceil(total / size))` a consumer would otherwise have to write *and* keep
115
+ in sync with the select. `totalPages` still works and is right when a server hands you a page
116
+ count directly; `totalRecords` wins if both are set. Changing the size returns to page 1:
117
+ "page 5" of 10-row pages is not "page 5" of 50-row ones, so clamping alone would leave the user
118
+ somewhere they never asked to be.
119
+ - **`gog-table`: `showPageSizeSelect` / `pageSizeOptions`**, forwarded to its paginator, and
120
+ **`GOG_CONFIG.labels.rowsPerPage`** for the select's accessible name.
121
+ - **`GOG_CONFIG.labels`: `total`, `tablePagination`, `selectRow`, `selectAllRows`.** The table's
122
+ own chrome — the row-count label read `Total:` from a hardcoded string, and its paginator was
123
+ labelled `Table pagination` with no way to change either.
124
+
125
+ - **`provideGogIcons(...)` — register your own icons by name.** `gog-icon` shipped a closed set
126
+ of 20 glyphs, and the only way to render anything else was a `TemplateRef` per instance,
127
+ which costs an `<ng-template>` at every use site and does not work at all for the components
128
+ that take an icon *name* (`gog-tag`, `gog-chip`, `gog-tabs`, `gog-button-toggle-group`,
129
+ `ToastService`, `DialogService`). In practice that meant installing a second icon library —
130
+ precisely the dependency the "no CDK, no Material" footprint exists to avoid.
131
+
132
+ ```ts
133
+ // app.config.ts
134
+ providers: [provideGogIcons({ cart: '<svg viewBox="0 0 24 24">…</svg>' })];
135
+ ```
136
+
137
+ ```html
138
+ <gog-icon name="cart" />
139
+ <gog-tag iconName="cart">In basket</gog-tag>
140
+ ```
141
+
142
+ - A registered name **overrides a built-in of the same name**, so an app can replace the
143
+ library's checkmark or chevrons everywhere without touching a single component.
144
+ - Providing it again lower in the injector tree **layers onto** the parent set rather than
145
+ replacing it, matching `provideGogConfig`.
146
+ - The registry is also exposed as the `GOG_ICONS` injection token.
147
+ - **`GogBuiltinIconName`** — the closed union of the shipped glyphs, for code that wants
148
+ exhaustiveness (an icon gallery, a `Record` keyed by icon).
149
+ - **21 more built-in icons, taking the set from 20 to 41.** The old set covered what the
150
+ library's own components needed and almost nothing an app needs: there was no `search` for a
151
+ field, no `trash` for a destructive action, no `more-vertical` for a table row menu. Added, all
152
+ Lucide, all on the same 24×24 / stroke-2 grid as the existing ones:
153
+ - actions — `search`, `plus`, `minus`, `trash`, `pencil`, `download`, `upload`, `refresh`,
154
+ `filter`, `external-link`;
155
+ - chrome — `menu`, `more-horizontal`, `more-vertical`, `settings`;
156
+ - navigation — `arrow-left`, `arrow-right` (distinct from the chevrons, which read as
157
+ disclosure rather than movement);
158
+ - objects and state — `user`, `lock`, `mail`, `star`, `star-filled`.
159
+
160
+ `star`/`star-filled` is the only outline/filled pair, for a rating or favourite **toggle** —
161
+ the same case `checkbox`/`checkbox-checked` already covers. The set stays outline-only
162
+ otherwise: a solid duplicate of every glyph would double the payload for a distinction almost
163
+ nothing needs, and `provideGogIcons` covers the exceptions.
164
+
165
+ Cost: `ICON_DEFS` is one object, so every consumer pays for all of it — it grew from 8.0 KB to
166
+ 16.5 KB raw, **1.6 KB to 2.7 KB gzipped**.
167
+ - **Attribution for the icons.** The glyphs were always Lucide but the package said so nowhere;
168
+ Lucide's ISC licence asks for the notice to travel with them. It is now at the top of
169
+ `icons.ts` and summarised in the README's licence section.
170
+
171
+ ### Changed
172
+
173
+ - **The button's `.gog-btn*` block moved from the component stylesheet into
174
+ `styles/button.css`**, which `styles/index.css` imports. Angular's emulated encapsulation
175
+ would never let a component stylesheet reach an `<a>` declared in a consumer's template, so
176
+ `[gogButton]` needs the rules to be global — the same reason `gogBadge` and `gog-collapsible`
177
+ already keep theirs there. One source for both, no duplication. Costs about 1 KB gzipped in the
178
+ always-loaded stylesheet; nothing changes for anyone already importing `index.css`, which the
179
+ Setup section has always required.
180
+ - **`npm run check:tokens` now covers the global stylesheets too.** It scanned `lib/**/*.scss`
181
+ plus a hardcoded `utilities.css`; it now walks `styles/*.css` as a directory, so a new global
182
+ stylesheet is under the token contract the moment it exists rather than whenever someone
183
+ remembers to add it. 34 stylesheets checked before, 38 now.
184
+ - **`gog-table`'s `pageSize` is a `model`, not an `input`.** `[pageSize]="20"` is unchanged;
185
+ `[(pageSize)]="size"` is now possible, and that is what lets the rows-per-page select work with
186
+ no wiring — the table binds its own model straight to the paginator's, so nothing is ferried
187
+ between the two by hand. `pageSizeChange` comes free from the model and is the refetch signal
188
+ in `lazy` mode.
189
+ - **The table footer no longer hides at a single page while the size select is on.** Hiding it
190
+ would strand the user on whatever size produced that one page, with no control left to choose a
191
+ smaller one. With the select off, the old behaviour is unchanged.
192
+ - **`GogIconName` is now open: `GogBuiltinIconName | (string & {})`.** The built-ins still
193
+ autocomplete; a registered name is now accepted wherever an icon name is taken, with no change
194
+ at any of the ten call sites that use the type. The trade is deliberate and comes with the
195
+ registry: a typo is no longer a compile error, so **an unknown name renders nothing and warns
196
+ in dev mode** (once per name) instead of throwing — an icon is decoration, and failing a render
197
+ over a glyph name would be the worse failure. Code that relied on `GogIconName` being closed
198
+ — an exhaustive `switch`, `Record<GogIconName, …>` — should move to `GogBuiltinIconName`.
199
+
200
+ ### Fixed
201
+
202
+ - **Buttons no longer inherit the anchor underline.** `.gog-btn` never reset `text-decoration`,
203
+ because a `<button>` has none to reset — the moment the same block landed on an `<a>` via
204
+ `[gogButton]`, every link-button came out underlined.
205
+ - **`--gog-icon-stroke-width` now applies to every shape in an icon.** The rule listed only
206
+ `path`, `circle` and `rect` — which happened to be all the original 20 glyphs used, so the gap
207
+ was invisible. Any icon drawn with `line`, `polyline` or `polygon` (half the new ones, and
208
+ whatever a consumer registers) silently ignored the token and fell back to the `stroke-width`
209
+ attribute baked into its own markup. `ellipse` is covered too.
210
+
211
+ ## [21.3.2] - 13.08.2026
212
+
213
+ First batch of the consumer-DX plan (`docs/consumer-dx-plan.md`, iterations 1–4): the seam
214
+ between the package and the developer installing it — setup that failed on the documented
215
+ path, accessibility that depended on optional inputs, and native attributes a wrapper component
216
+ made unreachable.
217
+
218
+ ### Added
219
+
220
+ - **The baseline stylesheet now also ships at `@guildofgleks/ui/styles/`.** This is the path the
221
+ README has always documented, and until now it did not exist in the package — the files were
222
+ only under `src/styles/`, so a setup copied from the README failed on a missing file and every
223
+ component rendered unstyled. Both paths ship for one deprecation window, and `package.json`'s
224
+ `exports` map now lists them, so `@import '@guildofgleks/ui/styles/index.css'` resolves from
225
+ SCSS as well as from `angular.json`.
226
+ - **`gog-inputfield` / `gog-textarea`: the native attribute space.** `readonly`, `maxlength`,
227
+ `minlength`, `spellcheck`, plus `pattern` and `inputMode` on the input. `readonly` differs
228
+ from `disabled` in the usual way (still focusable, still submitted) and suppresses the clear
229
+ button and the number field's spin buttons, since both offer an edit the field would refuse.
230
+ `autofocus` is deliberately **not** forwarded — moving focus unasked is a documented a11y
231
+ problem and the repo's own lint rule rejects it.
232
+ - **`gog-inputfield`: `tel`, `url`, `search`, `time` and `datetime-local` types**, via the new
233
+ exported `GogInputType`. The new `GogInputMode` types the `inputMode` input.
234
+ - **`GOG_CONFIG.labels`.** App-wide defaults for every fixed string the library renders —
235
+ `clear`, `clearSelection`, `clearDate`, `selectAll`, `clearAll`, `increment`, `decrement`,
236
+ `showPassword`, `hidePassword`, `closeDialog`, `closeToast`, `pagination`, `previousPage`,
237
+ `nextPage`, `openCalendar`, `today`, `thisMonth`, `previousMonth`, `nextMonth`,
238
+ `previousYear`, `nextYear`, `hours`, `minutes`, `seconds`, plus `page` (a formatter — see
239
+ Fixed). A non-English app relabels the library once instead of on every instance.
240
+ Per-instance inputs still win where they exist.
241
+ - **`gog-multiselect`: `selectAllLabel` / `clearAllLabel`.** The panel's two buttons rendered
242
+ literal `Select all` / `Clear` with no way to change them at all.
243
+ - **`gog-calendar`: `hoursLabel` / `minutesLabel` / `secondsLabel`.** The time section's three
244
+ fields had hardcoded English `aria-label`s.
245
+ - **`GOG_CONFIG.theme`.** `storageKey` persists the chosen theme in `localStorage`;
246
+ `followSystem` opens in the OS `prefers-color-scheme` setting and keeps following it until the
247
+ app calls `setTheme`; `defaultTheme`, `lightTheme` and `darkTheme` name the themes involved.
248
+ All off by default, so an app that configures nothing keeps today's behaviour exactly.
249
+ - **`@angular/platform-browser` is now declared as a peer dependency.** `gog-icon` has always
250
+ imported `DomSanitizer` from it; the omission only worked because npm's flat tree hides it,
251
+ and broke under strict pnpm.
252
+
253
+ ### Fixed
254
+
255
+ - **Form controls are labelled without an `inputId`.** `gog-inputfield` and `gog-textarea` now
256
+ generate an id when none is given, so the `<label for>` actually points at the field (clicking
257
+ the label focuses it, assistive tech gets a name) and the error message is reachable through
258
+ `aria-describedby`. Previously both were silently dropped unless the consumer happened to pass
259
+ `inputId` — the default configuration was inaccessible. `gog-select` already worked this way;
260
+ the id generator is now shared (`gog-radio-group` and `gog-slider` use it too, with unchanged
261
+ output).
262
+ - **`aria-describedby` no longer points at an element that isn't rendered.** It was keyed off
263
+ `hasError()`, while the message element renders on `visibleError()` — with `errorDisplay="auto"`
264
+ and an empty `errorMessage` the two disagree.
265
+ - **Toasts are announced reliably.** `aria-live` moved off the individual toast, which enters the
266
+ DOM together with its own text (a live region created at the same moment as its content is
267
+ routinely skipped by screen readers), onto two permanently-mounted regions in
268
+ `gog-toast-container` — polite, and assertive for `error`/`warning`. Individual toasts no longer
269
+ carry `role`/`aria-live`, so nothing is announced twice.
270
+ - **`gog-inputfield`: the clear button on a number field wrote `''` instead of `null`.** A
271
+ `FormControl<number | null>` ended up holding a string, which then failed numeric validators
272
+ and round-tripped the wrong type. It now writes exactly what emptying the field by hand writes.
273
+ - **`ThemeService.theme` is read-only.** It was a writable signal, so `theme.set(...)` moved the
274
+ signal without touching the `data-theme` attribute the styles read, leaving the two out of
275
+ sync. Use `setTheme`/`toggleTheme`.
276
+ - **`gog-inputfield`: a `clearable` number field had no clear button.** The stepper and the
277
+ clear button share the field's end slot, and the stepper won outright — so `clearable` was
278
+ silently a no-op on `type="number"` unless `showSpinButtons` was also off. Both now render:
279
+ the clear button sits one stepper-width further in, and the field's text gutter widens to fit
280
+ the pair (`--gog-input-spin-width`, new). It still disappears when there is nothing to clear,
281
+ taking the extra gutter with it.
282
+ - **`gog-calendar` now reads `GOG_CONFIG.datepicker`.** `locale` and `firstDayOfWeek` were
283
+ documented as applying to `gog-calendar` as well as `gog-datepicker`, but the calendar only
284
+ ever honoured its own inputs — so a standalone `<gog-calendar>` in an app with an app-wide
285
+ locale silently rendered in `en-US`. Rendered through `gog-datepicker` nothing changes: that
286
+ component passes its own already-resolved values, which still win.
287
+ - **`gog-paginator`: the per-page button names are translatable.** "Go to page 4" / "Page 4,
288
+ current page" were built by string concatenation in the template. They now come from
289
+ `GOG_CONFIG.labels.page`, a `(page, isCurrent) => string` formatter — a function rather than a
290
+ placeholder string, since the number's position and the grammar around it are language
291
+ dependent.
292
+ - **The textarea resize grip's offsets are real tokens.** `--gog-textarea-resize-grip-offset`
293
+ and `--gog-textarea-resize-inset-right`/`-bottom` are declared in `theme.css` instead of
294
+ living as literal `var()` fallbacks in the component stylesheet, which the token-contract
295
+ check (`npm run check:tokens`) had been failing on. Geometry is unchanged.
296
+
297
+ ### Changed
298
+
299
+ - **The generated token catalogue moved from `README.md` to `TOKENS.md`.** It was ~200 KB of
300
+ reference table in the middle of the README, burying the Setup section that a new consumer has
301
+ to find within seconds on npm. The README keeps the three-layer explanation and links across;
302
+ the README itself is now ~14 KB. `GOG_TOKEN_GROUPS` is unaffected.
303
+ - **README: `<gog-dialog />` and `<gog-toast-container />` are documented.** `DialogService.open()`
304
+ and `ToastService.show()` render nothing until those host elements are in a template, which
305
+ the README never said.
306
+ - **README: a `## Global configuration` section.** `provideGogConfig` was never documented in the
307
+ README at all — only individual keys mentioned in passing — so the app-wide settings, and now
308
+ `labels` and `theme` with them, were undiscoverable to anyone reading the package page. Adds
309
+ the key list, the precedence rule, the injector-tree merge, and a translation example.
310
+ - **README / `AGENTS.md`: the new API is documented.** Both ship inside the package. `AGENTS.md`
311
+ (the consumer-facing agent reference) has the native attributes, `GOG_CONFIG.labels` and
312
+ `.theme`, the read-only `ThemeService.theme`, generated field ids, the toast live regions, and
313
+ `GogInputType`/`GogInputMode` in its type table.
314
+ - **README: the component list is complete again.** It advertised 18 components and listed 21,
315
+ while omitting `gog-autocomplete`, `gogBadge`, `gog-button-toggle-group`, `gog-datepicker`,
316
+ `gog-divider`, `gog-progressbar`, `gog-tabs` and `gog-toggle` entirely.
317
+ - Label inputs that now resolve through `GOG_CONFIG.labels` changed their default from a literal
318
+ string to `undefined` (`clearAriaLabel`, `incrementLabel`, `decrementLabel`, `showPasswordLabel`,
319
+ `hidePasswordLabel`, `todayLabel`, `thisMonthLabel`, `previousMonthLabel`, `nextMonthLabel`,
320
+ `previousYearLabel`, `nextYearLabel`, `openCalendarLabel`, `gog-paginator`'s `ariaLabel`).
321
+ Rendered output is identical unless the app configures `labels`; only reading the input back
322
+ in TypeScript now yields `undefined` rather than the English default. `gog-calendar`'s
323
+ `locale` and `firstDayOfWeek` changed the same way, for the same reason.
324
+
325
+ ### Deprecated
326
+
327
+ - `@guildofgleks/ui/src/styles/…` — use `@guildofgleks/ui/styles/…`. Both ship until **21.5.0**,
328
+ when the `src/styles/` copy is removed.
329
+
330
+ ## [21.3.1] - 11.08.2026
331
+
332
+ ### Added
333
+
334
+ - **`gog-slider`: `range`.** Switches the slider to two independently focusable native
335
+ thumbs for picking a span instead of a single value — bind `[(rangeValue)]` (a
336
+ `GogSliderRange` `{ start, end }` pair) instead of `[(value)]`; the two are mutually
337
+ exclusive, and `writeValue`/the `ControlValueAccessor` follow whichever one `range` selects.
338
+ Neither thumb can be dragged, keyboard-nudged, or written past the other — crossing is
339
+ clamped in JS rather than through the native `min`/`max` attribute, since narrowing that
340
+ per thumb would desync the browser's own (invisible) thumb position from the custom
341
+ `--range-start-pos`/`--range-end-pos`-driven visuals. Works in both orientations and with
342
+ `showThumb`/`fullWidth`/`disabled`/error display exactly as the single-value mode does.
343
+ `startAriaLabel`/`endAriaLabel` (defaulting to `'Minimum'`/`'Maximum'`, prefixed with
344
+ `label()` when set) name the two thumbs for assistive tech, since a single `<label for>`
345
+ can't target both. The value readout (`showValue`) reserves stable width up front, sized
346
+ from `min()`/`max()`/`step()` rather than the live value, so it — and, in a `fit-content`
347
+ vertical slider, the whole control along with it — doesn't visibly resize on every drag.
348
+ - **`gog-slider`: `startDisabled`/`endDisabled`.** Disable just one thumb in `range` mode —
349
+ e.g. pin a range's floor while leaving its ceiling adjustable, or vice versa — instead of
350
+ `disabled`, which still takes out both together. ORed with `disabled` rather than
351
+ overriding it, and ignored outside `range` mode (nothing to disable "one side" of there). A
352
+ one-sided disable only dims and disables that one thumb (its native input's own `disabled`
353
+ attribute takes it out of the tab order); the whole-control `.gog-slider--disabled` styling
354
+ (dimming + `pointer-events: none` over the whole track) only kicks in once *both* sides are
355
+ disabled, since applying it for just one would also block pointer input to the other,
356
+ still-enabled thumb. Reactive forms are unaffected by this addition: a `[formControl]`'s own
357
+ `.disable()`/`.enable()` still speaks for both thumbs at once, same as before — one
358
+ `FormControl` backs one `rangeValue` and has no way to target just one side of it.
359
+
360
+ - **`gog-autocomplete`: `openOnFocus`.** Focusing the field now opens the panel immediately with
361
+ the full option list, ignoring `minLength` — the common "browse everything, then narrow it
362
+ down" pattern a plain type-ahead can't offer. On by default; turn it off (or set
363
+ `GOG_CONFIG.autocomplete.openOnFocus = false`) to keep the previous behaviour of nothing
364
+ showing until enough has been typed. The list stays unfiltered even when the field already
365
+ displays a previously-selected label, and normal filtering resumes on the first keystroke.
366
+ - **`gog-autocomplete`: `gogLoadMore`.** Fires once the panel is scrolled to the end of the
367
+ option list — the signal to fetch and append another page, instead of handing a huge or
368
+ server-backed source over up front (500,000 rows loaded 20 at a time, not all at once).
369
+ Forwarded from the panel's own `gog-scroll`.
370
+ - **`gog-tabs`: `scrollActiveIntoView`.** With an overflowing header row, selecting a tab —
371
+ by click, the arrow keys, or a consumer setting `activeIndex` directly — now scrolls the
372
+ header so the active tab stays in view, centered where there's room so its neighbours on
373
+ both sides stay visible too. The same "show what's around the current position" idea
374
+ `gog-paginator` already uses for pages. On by default; instant on first render, smooth (or
375
+ instant under `prefers-reduced-motion`) after. Turn it off to own the scroll position
376
+ yourself.
377
+ - **`gog-tabs`: `showScrollTrack`; `gog-scroll`: `showTrack`.** With `scrollActiveIntoView`
378
+ driving the header's scroll position, its own draggable thumb/track next to the active-tab
379
+ underline read as two conflicting position indicators for the same thing — confusing rather
380
+ than helpful, per feedback on the first cut of `scrollActiveIntoView`. `gog-tabs` now hides
381
+ the track by default while `scrollActiveIntoView` is on, and shows it by default once that's
382
+ off (the only way left to reach an off-screen tab by mouse); either can be pinned explicitly
383
+ with `showScrollTrack`, regardless of the other. Native scrolling — wheel, touch, keyboard,
384
+ and any programmatic `scrollTo`/`scrollIntoView` — is unaffected either way; only the visual
385
+ affordance is gone. The underlying toggle lives on `gog-scroll` itself as `showTrack`
386
+ (instance input, or app-wide via `GOG_CONFIG.scroll.showTrack`), so any other panel built on
387
+ it gets the same option.
388
+ - **`gog-textarea`: `resize`.** Which direction(s) the field's own drag handle resizes it in —
389
+ `'vertical'` (the default, matching a plain `<textarea>`), `'horizontal'`, `'both'`, or
390
+ `'none'` to remove it entirely. Settable app-wide via `GOG_CONFIG.textarea.resize`. The
391
+ handle itself is also restyled: the browser's native glyph is barely visible at a glance, so
392
+ it's blanked out (`::-webkit-resizer`, where that's even stylable — Firefox never exposed a
393
+ hook for its own) and replaced with two short diagonal strokes in the field's own border
394
+ colour, sized and positioned to sit inside the border rather than past it. A `ResizeObserver`
395
+ on the field keeps the grip glued to its actual corner as it's dragged narrower/shorter than
396
+ its container (`'horizontal'`/`'both'`) — it's anchored to the container, not the field
397
+ itself, since a `<textarea>` can't reliably host `::after`. The drag stays entirely native;
398
+ only the glyph and its tracking are new.
399
+ - **`gog-inputfield`: number spin buttons.** A `type="number"` field now gets the library's own
400
+ increment/decrement buttons instead of the browser's native ones, which render inconsistently
401
+ across Chromium/Firefox/Safari and were never themed. Flush against the field's own border as
402
+ one grouped stepper (a divider on each side), not floating loose in the icon gutter. Steps by
403
+ `step` (default `1`), clamps to `min`/`max`, and disables the button at whichever boundary is
404
+ reached. Arrow-key stepping on the focused field is untouched — that's native
405
+ `<input type="number">` behaviour, unrelated to which glyphs are visible. `showSpinButtons`
406
+ turns them off entirely (native glyphs never come back — off means no stepper UI at all);
407
+ settable app-wide via `GOG_CONFIG.inputfield.showSpinButtons`.
408
+ - **`gog-icon`: `copy`.** A new glyph for the common "copy this field's value" trailing-action
409
+ pattern (see the inputfield showcase page for a full example built on `gogInputAddonEnd`).
410
+ - **`AGENTS.md`.** A consumer-facing reference for AI coding agents building apps against the
411
+ published package — conventions, theming/`GOG_CONFIG` summary, a full per-component API table
412
+ (inputs, outputs, slots, CVA status), and the deprecated-pattern list, all derived from the
413
+ library's actual source rather than the (currently lagging) `README.md`. Shipped alongside
414
+ `README.md`/`LICENSE` in the npm package via `ng-package.json`'s `assets`.
415
+
416
+ ### Fixed
417
+
418
+ - **`gog-scroll`: thumb too small to reliably click, especially at `size="thin"`.** The
419
+ thumb's own visible box is exactly as wide as `size` says — that part is unchanged — but its
420
+ clickable/draggable _region_ now extends a few pixels past every edge
421
+ (`--gog-scroll-thumb-hit-padding`, bigger on `thin`, where the visible thumb was hardest to
422
+ land a cursor on), so a near-miss click still grabs the thumb instead of falling through to
423
+ the track, which pages the view rather than dragging. Purely an invisible hit-area change —
424
+ no new input, no behaviour change for the mouse wheel, which already worked fine.
425
+ - **`gog-autocomplete`: option rows spilling out of the panel.** The panel's `.gog-scroll` was
426
+ never actually constrained to `--gog-autocomplete-panel-max-height` — a classic flexbox trap
427
+ where a `max-height`-only container doesn't give its flex-grow children a definite size to
428
+ shrink into, so the option list rendered at full content height and visibly overflowed past
429
+ the panel's own border into whatever sat below it. Most visible with `appendToBody` and a
430
+ longer list (typing narrowed it back under the cap, masking the issue until the panel was
431
+ reopened with more matches, which also made it look like "the panel closes on its own" — it
432
+ hadn't; the list had just spilled out from under it). Fixed by giving the panel the same
433
+ `display: flex` + `flex: 1; min-height: 0` chain `gog-select` and `gog-multiselect` already
434
+ use, plus a defensive `overflow: hidden`.
435
+
436
+ ## [21.3.0] - 08.08.2026
437
+
438
+ ### Added
439
+
440
+ - **Eight new components**, the Angular Material set this library was missing:
441
+
442
+ - **`gog-datepicker`** — a date field with a calendar panel: single date, `selectionMode="range"`
443
+ (with `numberOfMonths` for a two-month view), and an optional clock via `showTime` /
444
+ `hourFormat` / `minuteStep` / `showSeconds`. `min`, `max` and a `disabledDates` **predicate**
445
+ (an array cannot express "weekends"), `inline` for an always-visible calendar, `allowTextInput`
446
+ with parsing, plus the usual `clearable` / `floatLabel` / `errorMessage` / `appendToBody`.
447
+
448
+ The panel's footer carries **two separate actions**, never one: `showTodayButton` (on by
449
+ default) _selects_ today, and `showThisMonthButton` (off by default) only moves the view back
450
+ to the current month. A single button doing both is ambiguous — after paging away, the same
451
+ label reads as "take me back" to one person and "set it to today" to another. "Today" is
452
+ disabled when `min`/`max` or `disabledDates` rule today out, rather than silently doing
453
+ nothing. Wording via `todayLabel` / `thisMonthLabel`.
454
+
455
+ Native `Date`, **no date library and no adapter abstraction** — the package keeps its zero
456
+ runtime dependencies. `Intl` supplies month and weekday names; the display format is a token
457
+ pattern (`dd.MM.yyyy`, `yyyy-MM-dd`, …) used for _both_ rendering and parsing, so what is
458
+ written can always be read back. `31.02.2026` is rejected rather than silently becoming
459
+ 3 March. `locale`, `firstDayOfWeek` and `format` are also settable app-wide through
460
+ `GOG_CONFIG.datepicker`.
461
+
462
+ - **`gog-calendar`** — the month grid behind it, exported and usable on its own. Follows the
463
+ ARIA grid pattern: arrows by day, `PageUp`/`PageDown` by month, `Shift` + those by year,
464
+ `Home`/`End` to the week's ends, and one tab stop across all 42 cells. Always six weeks, so
465
+ the calendar's height never changes as you page through months.
466
+ - **`gog-autocomplete`** — a text field that suggests options as you type, on the same
467
+ `GogDropdownBase` as `gog-select` and taking the same `optionLabel` / `optionValue` /
468
+ `optionDisabled` accessors. The trigger is a real `<input>`, which is what makes it a separate
469
+ control rather than a mode of `gog-select`: focus never leaves the field and the highlighted
470
+ row is pointed at with `aria-activedescendant`. `gogSearch` is debounced (`searchDebounce`,
471
+ 300 ms) for a server-backed source, and `[filterLocal]="false"` stops that server's answer
472
+ being filtered a second time. Plus `minLength`, `loading`, `emptyMessage` and
473
+ `forceSelection`.
474
+ - **`gog-tabs` / `gog-tab`** — a tablist over projected children, each tab declaring its own
475
+ `label`, `iconName` and `disabled`. Content written inside a tab renders eagerly and is
476
+ merely hidden while inactive, so scroll position and half-typed input survive a switch; an
477
+ `<ng-template gogTabContent>` is instead built on first activation and kept alive after.
478
+ Which you get is decided by whether that template is present. `gogTabHeader` replaces the
479
+ header button entirely. Overflowing headers scroll inside a `<gog-scroll>`, not a native
480
+ `overflow-x`.
481
+ - **`gog-button-toggle-group`** — a row of buttons where one, or with `multiple` several, can
482
+ be picked. Options-driven with the same accessors as the dropdowns, plus `optionIcon` and a
483
+ `gogButtonToggleOption` slot. Single and multiple are genuinely different widgets to
484
+ assistive tech and are exposed as such: `role="radiogroup"`/`aria-checked` with arrows that
485
+ move _and_ select, versus `role="group"`/`aria-pressed` with arrows that only move.
486
+ `appearance` picks between one segmented control and discrete buttons.
487
+ - **`gog-toggle`** — an on/off switch. A native `<input type="checkbox">` carrying
488
+ `role="switch"`, so it announces as "switch, on" rather than "checkbox, checked" while the
489
+ platform keeps owning the keyboard and forms. `onLabel` / `offLabel` render _inside_ the
490
+ track — the one thing a checkbox cannot do — and both stay in the DOM so the track's width
491
+ cannot jump as it flips. Shares `gog-checkbox`'s size scale.
492
+ - **`gog-progressbar`** — determinate, indeterminate and buffer modes, five sizes and the
493
+ semantic colour set. `value` and `buffer` are clamped to 0–100 rather than trusted.
494
+ Indeterminate reports **no** `aria-valuenow` at all, which is what marks it indeterminate,
495
+ and its animation is replaced by a static stripe under `prefers-reduced-motion`.
496
+ - **`gogBadge`** — a count or dot pinned to another element's corner. A directive, so it
497
+ decorates a button, icon or avatar without wrapping it. `badgePosition`, `badgeVariant`,
498
+ `badgeDot`, `badgeMax` (`99+` beyond it), `badgeHidden` and `badgeAriaLabel`. It renders
499
+ **nothing at all** for `0`, `null` or `''` — a badge reading "0" is the defining bug of this
500
+ component class, so it is not reachable.
501
+ - **`gog-divider`** — a rule between two regions, horizontal or vertical, solid/dashed/dotted,
502
+ with an optional projected label running through it and an `inset` variant for lists. No
503
+ `hasLabel` input: the two forms are told apart by whether anything was actually projected.
504
+
505
+ - `GogOrientation` — one shared `'horizontal' | 'vertical'` type. `GogSliderOrientation` is now
506
+ an alias of it, so nothing changes for existing code.
507
+ - `roving-focus.ts` gained an `orientation` (so a horizontal tablist leaves `ArrowDown` to the
508
+ page) and an optional predicate for skipping disabled items. Both default to the previous
509
+ behaviour, so `gog-select`, `gog-multiselect` and `gog-accordion` are unaffected.
510
+ - Four icons: `calendar`, `clock`, `chevron-left`, `chevron-right`.
511
+ - `.gog-visually-hidden` in `styles/utilities.css`.
512
+
513
+ - `GogFloatLabelState` (exported) — the shared float-label state behind `gog-inputfield`,
514
+ `gog-textarea`, `gog-select` and `gog-multiselect`, previously three near-identical copies of
515
+ the same five `computed()`s. A plain composition class in the mould of `GogErrorState`, so it
516
+ serves the two components that share no base class as well as `GogDropdownBase`, which is one.
517
+ Each control still supplies its own "has content" signal, since that genuinely differs
518
+ (non-empty string / non-null selection / non-empty selection array).
519
+ - `resolveConfigured(instanceValue, configuredValue, fallback)` (exported) — the library's
520
+ input → `GOG_CONFIG` → built-in default precedence rule in one place, instead of the `??`
521
+ chain hand-written at each configurable input.
522
+ - `GOG_CONFIG` now covers the settings an app otherwise repeats on every instance:
523
+ `control.size` and `control.errorDisplay` (the latter is what makes `errorDisplay="auto"` an
524
+ app-wide decision for a Reactive Forms app rather than per-field boilerplate),
525
+ `dropdown.appendToBody`, `dropdown.direction`, and `toast.position` / `toast.duration`.
526
+ `control.size` deliberately covers only the interactive form controls — `gog-table`,
527
+ `gog-accordion` and `gog-paginator` keep their own density defaults, as do `gog-spinner`,
528
+ `gog-skeleton`, `gog-tag` and `gog-chip`. All stay per-instance overridable.
529
+
530
+ - **A built-in clear button** on `gog-inputfield`, `gog-textarea`, `gog-select` and
531
+ `gog-multiselect`, via a `clearable` input (plus `clearAriaLabel`). It appears only once the
532
+ control has something to clear and disappears again when empty, so it adds no permanent
533
+ chrome — and it removes the need for a fake `"— not selected —"` option just to let someone
534
+ undo a choice. Also settable app-wide through `GOG_CONFIG.control.clearable`. Defaults to
535
+ `false`, except `gog-multiselect`, which already had a clear button and keeps it. On a
536
+ password field the built-in reveal toggle keeps the trailing slot.
537
+ - `filterPosition` on `gog-select` / `gog-multiselect` (`'top'` | `'bottom'`, plus
538
+ `GOG_CONFIG.dropdown.filterPosition`) sticks the search box to either end of the panel, and it
539
+ now carries a divider on the side facing the list so it reads as chrome rather than a row. The
540
+ name matches `gog-multiselect`'s existing `controlsPosition` rather than inventing a second
541
+ vocabulary for the same idea.
542
+ - **Filtering in `gog-select` and `gog-multiselect`** — `filter` puts a search box at the top of
543
+ the panel, matching case-insensitively on the resolved `optionLabel`. `filterMatch` swaps that
544
+ for your own predicate, `filterPlaceholder` and `filterEmptyMessage` cover the wording, and
545
+ `GOG_CONFIG.dropdown.filter` turns it on app-wide. The query resets when the panel closes, and
546
+ `gog-multiselect`'s "select all" deliberately takes only the _visible_ options so it means what
547
+ it says while a filter is active.
548
+ - `styles/presets/one-dark.css` and `styles/presets/one-light.css` — the Atom/JetBrains One
549
+ palettes, with the syntax hues mapped onto the library's semantic roles (blue is the accent,
550
+ green/red/yellow/cyan become success/danger/warning/info).
551
+
552
+ - **The token catalogue is generated, not hand-copied.** `npm run generate:tokens` derives
553
+ `GogTokenName` (a union of every `--gog-*` the library declares or documents), the
554
+ `GOG_TOKEN_GROUPS` runtime metadata, and the README's theming table straight from
555
+ `theme.css`. `npm run check:tokens` fails when they are out of date, so a stylesheet edit
556
+ cannot silently leave the docs behind. `GOG_TOKEN_GROUPS` is exported so a theme editor can
557
+ enumerate real tokens instead of keeping its own copy.
558
+ - `styles/presets/slate.css` — a second, importable preset (`data-theme="slate"`, cool/indigo).
559
+ It declares palette tokens only and still restyles everything, which is the theming contract
560
+ demonstrated rather than described.
561
+
562
+ - **`gog-select` and `gog-multiselect` take your own objects.** `optionLabel`, `optionValue` and
563
+ `optionDisabled` accept a property path (dot-paths included, `'profile.fullName'`) or a
564
+ function, so a real DTO goes straight in — no mapping into `{ id, name }` first, and no losing
565
+ the original object on the way back out. Set `[optionValue]="null"` and the control emits the
566
+ **option object itself** instead of an id. Both controls are now generic over their option and
567
+ value types, inferred from the bindings.
568
+
569
+ Defaults are `'name'` / `'id'` / `'disabled'`, so **existing code is unaffected** — the whole
570
+ 21.2.x select/multiselect spec suite passes unchanged. `GogDropdownOption` is no longer a
571
+ requirement, just the shape those default accessors expect.
572
+
573
+ - `gogDropdownOption` — a projected template for one option row, with
574
+ `{ $implicit: option, selected, disabled, label }` as its context.
575
+ - `getByPath`, `readOption`, `isSameOptionValue` and the `GogOptionAccessor<TOption, TResult>`
576
+ type are exported; `gog-table` now shares the same `getByPath` rather than keeping its own copy.
577
+
578
+ - **One slot mechanism across the library.** Custom markup is now projected as content and
579
+ picked up with `contentChild`, instead of a `TemplateRef` input per slot. New directives:
580
+ `gogColumnBody` / `gogColumnHeader` (per column, replacing the string-keyed
581
+ `<ng-template template="…" type="…">`), `gogCheckboxIcon`, `gogTagIcon`,
582
+ `gogMultiselectClearIcon`, `gogDropdownChevron`, and `gogInputAddonStart` /
583
+ `gogInputAddonEnd`. A projected slot always wins over the deprecated input it replaces, so a
584
+ codebase can migrate one call site at a time.
585
+ - `GogColumn` with the `gog-column` selector — the library's last unprefixed element name.
586
+ - `gog-inputfield` addon slots take arbitrary markup, including a real `<button>` with its own
587
+ `aria-label` and `(click)`. This replaces six inputs (`icon{Start,End}{Template,Fn,Label}`)
588
+ with two slots. On `type="password"` the built-in reveal toggle keeps the trailing slot, so a
589
+ projected addon can never displace the only control that shows the value.
590
+
591
+ ### Fixed
592
+
593
+ - **An auto-width dropdown clipped its own options.** With `[fullWidth]="false"` the trigger
594
+ sizes to the _current_ selection, and the panel copied that width — so picking a short option
595
+ cut the longer ones off the list. The relationship is now inverted: the panel sizes to its own
596
+ content with the trigger's width as a **floor**, capped by
597
+ `--gog-{select,multiselect}-panel-max-width`. New `minWidth` input (any CSS length) plus
598
+ `--gog-{select,multiselect}-min-width` (120px) so an auto-width trigger cannot collapse to its
599
+ own chrome either.
600
+ - **`gog-multiselect` now collapses a long selection into `+N`.** The trigger shows what fits on
601
+ one line and a count for the rest, with the full list in a tooltip. Measured with
602
+ `canvas.measureText` rather than by rendering candidates, and re-measured from a
603
+ `ResizeObserver` on the value element, since the space available changes when the _container_
604
+ resizes — something Angular never renders for.
605
+ - **`gog-select`'s chevron sat 42px from the trigger's right edge.**
606
+ `--gog-select-chevron-inset` was applied as the trigger's `padding-right` while the chevron
607
+ itself was a flex child _inside_ that padding, so the inset was counted twice. It now lands on
608
+ `--gog-control-icon-offset` (10px), the same line as `gog-inputfield`'s icons and
609
+ `gog-multiselect`'s arrow, which were at 10px and 16px — the three controls did not line up in
610
+ a form. The token keeps its name and now means what it says.
611
+ - **`gog-textarea`'s clear button sat inside the scrollbar.** It was inset 8px from the border
612
+ box while a scrolling textarea's scrollbar is ~19px wide, so once the content overflowed the
613
+ button was half-covered and competed with the thumb for clicks. It is now offset by the
614
+ measured scrollbar width (`--gog-textarea-scrollbar-width`, written from
615
+ `offsetWidth - clientWidth`; `scrollbar-gutter: stable` was rejected because it reserves the
616
+ gutter even when the field isn't scrolling).
617
+ - **`gog-textarea`'s clear glyph was 30% too small** — 13.4px against the library's 19.2px,
618
+ because it reused the dropdowns' 0.7 ratio, which suits their dense trigger and not a large
619
+ multi-line box. New `--gog-textarea-clear-icon-ratio` defaults to a full-size glyph.
620
+ - Nine specs in `scroll.component.spec.ts` awaited a single animation frame after dispatching a
621
+ scroll, while `ScrollComponent` coalesces measurement into its own frame — if that frame fired
622
+ during `whenStable()`, the effect scheduled a second one _after_ the test's, and the assertion
623
+ ran before the measurement. Intermittent by construction; replaced with a `settleMeasure()`
624
+ helper that covers both orderings.
625
+
626
+ ### Changed
627
+
628
+ - The clear button now takes the **outermost** trailing position on `gog-select` and
629
+ `gog-multiselect`, with the chevron/arrow shifting inward when it appears. Previously
630
+ `gog-multiselect` had them the other way round. Keeps the trigger width stable and keeps the
631
+ destructive control off the very edge.
632
+ - Float-label fields are less tall: `--gog-field-float-label-reserve` 18px → 14px and
633
+ `--gog-field-float-label-in-top` 8px → 6px, taking an `md` field from 63px to 59px (a plain one
634
+ is 45px). Both are tokens, so the old numbers are one declaration away.
635
+
636
+ ### Deprecated
637
+
638
+ Each of these keeps working unchanged and is **removed in 21.5.0**; the `@deprecated` tag on
639
+ every symbol carries the same date and removal version, so `grep -rn "@deprecated since"` lists
640
+ the full set at any time.
641
+
642
+ - `<column>` → `<gog-column>`, and the `Column` export → `GogColumn`.
643
+ - All `--gog-ms-*` tokens → `--gog-multiselect-*`. Both spellings work for the whole window:
644
+ the `--gog-ms-*` name stays the _declared_ one and the new name derives from it, so an
645
+ existing override of either still reaches the component. Verified in a browser both ways.
646
+ - `<ng-template template="field" type="body|header">` inside `gog-table` → a `gogColumnBody` /
647
+ `gogColumnHeader` template declared inside the column itself. The old form matched columns by
648
+ a string the compiler cannot check, so a typo silently fell back to the default cell.
649
+ - `gog-checkbox`'s `checkIconTemplate` → `gogCheckboxIcon`.
650
+ - `gog-tag`'s `iconTemplate` → `gogTagIcon`.
651
+ - `gog-multiselect`'s `clearIconTemplate` → `gogMultiselectClearIcon`.
652
+ - `gog-select` / `gog-multiselect` `chevronTemplate` → `gogDropdownChevron`.
653
+ - `gog-inputfield`'s `iconStartTemplate`, `iconEndTemplate`, `iconStartFn`, `iconEndFn`,
654
+ `iconStartLabel`, `iconEndLabel` → `gogInputAddonStart` / `gogInputAddonEnd`. `iconStart` and
655
+ `iconEnd` (a bare icon name) stay — that is the genuinely common case.
656
+
657
+ ### Changed
658
+
659
+ - **`provideGogConfig(...)` now merges with the config from the parent injector instead of
660
+ replacing it.** Previously a nested call — in a route's or a component's `providers` —
661
+ silently dropped every key it did not restate, so a route setting only `{ tooltip: … }` lost
662
+ the app-wide `button.debounce` with no error anywhere. Merging is one level deep, per
663
+ component key, nearest provider winning field by field. If you were working around the old
664
+ behaviour by repeating the whole config at each level, those repeats are now redundant but
665
+ harmless.
666
+
667
+ - Float label geometry is now themeable through `theme.css` instead of being hardcoded in the
668
+ component stylesheets. `--gog-{input,select,ms}-float-label-{reserve,in-top,over-gap,over-reserve}`
669
+ previously existed only as literal fallbacks (`18px`, `8px`, `1.4em`) inside four component
670
+ `.scss` files, so they were overridable but not discoverable, and `theme.css` did not describe
671
+ the components' full surface. They are now declared component tokens deriving from a new
672
+ shared `--gog-field-float-label-{reserve,in-top,over-gap,over-reserve}` scale, so one
673
+ declaration retunes every field at once while a single control can still be overridden.
674
+ No visual change — the defaults are identical. `--gog-{input,select,ms}-float-label-on-bg`
675
+ stays an instance-layer (undeclared) token as before.
676
+
677
+ ### Added
678
+
679
+ - Float label support for `gog-inputfield`, `gog-select`, `gog-multiselect` and
680
+ `gog-textarea`: a `floatLabel` input (`GogFloatLabelVariant`: `'none'` default, or
681
+ `'in'`/`'on'`/`'over'`, modeled on PrimeNG's own variant names) that rests the label inside
682
+ the field like a placeholder and floats it up on focus or once the field has content —
683
+ `'in'` stays fully inside the border, `'on'` ends up centered on the top border line (with
684
+ a background patch masking it), `'over'` floats fully above the field, outside the border.
685
+ A `floatLabelShowPlaceholder` input (`boolean`, default `false`) reveals the field's own
686
+ `placeholder` once the label has floated out of the way; left off, the placeholder stays
687
+ hidden the whole time a float label is active since the resting label already occupies that
688
+ space. Both are also settable app-wide via the new `GOG_CONFIG.floatLabel` (`variant` /
689
+ `showPlaceholder`), with the usual per-instance input taking priority. Implemented as a
690
+ style variant on each component (not a directive, unlike `gogTooltip`) since each control
691
+ already owns its label and has a different notion of "has content" (`value`,
692
+ `selectedOption`, selection length) that a directive sitting outside the component couldn't
693
+ see. New `--gog-{input,select,ms}-float-label-{in-top,on-bg,over-gap,over-reserve}` tokens.
694
+ - `gog-slider`'s new `orientation` input (`'horizontal'` default / `'vertical'`) — the
695
+ developer picks per instance, no global default, since it's a layout decision rather than a
696
+ house style. The vertical variant is the same native `<input type="range">` rotated via
697
+ `writing-mode: vertical-lr` + `direction: rtl` (not a custom drag implementation), so
698
+ dragging, touch and keyboard (Up/Down as well as Left/Right) all keep working exactly as they
699
+ do horizontally; value increases upward, matching a volume-fader convention. New
700
+ `--gog-slider-vertical-length` token (default `160px`) sizes its length, the vertical
701
+ counterpart to `--gog-slider-auto-width`. `fullWidth` is ignored when vertical, since a
702
+ vertical slider's width is its thickness, not its length.
703
+ - `gog-radio-group`: a new options-driven radio control (`GogRadioOption[]`), the radio
704
+ counterpart to `gog-checkbox`. Renders native `<input type="radio">`s sharing one
705
+ auto-generated (or explicit `name`) group name, so mutual exclusivity and arrow-key/Home/End
706
+ navigation between options come from the browser for free — no roving-focus code needed.
707
+ `ControlValueAccessor`-based, works with `formControl`/`formControlName`. `label`,
708
+ `ariaLabel`, `name`, `size`, `disabled` (group-level, plus per-option `disabled`),
709
+ `orientation` (`'vertical'` default / `'horizontal'`), `errorMessage`, `errorDisplay` and
710
+ `fullWidth` inputs; `[(value)]` two-way bindable. Reuses the `--gog-control-checkbox-*`
711
+ size scale via the shared checkable-control config, plus new `--gog-radio-*` tokens in
712
+ `theme.css`.
713
+ - `gog-collapsible`'s `collapseOnFocusOut` input (`boolean`, default `false`): closes the
714
+ panel once focus leaves both the trigger and the content — e.g. Tabbing past the last
715
+ focusable element inside, or a click landing elsewhere on the page. Off by default, since
716
+ plenty of consumers (an FAQ list, a settings section read top to bottom) want the panel to
717
+ stay open regardless of where focus goes next.
718
+ - `gogTooltip`: a new directive, not a component — drop it on any element, a `gog-*`
719
+ component's own host tag or a plain native one (`<button gogTooltip="Save changes">`,
720
+ `<gog-chip [gogTooltip]="hint">`), to add a hover/focus tooltip without that element
721
+ needing to know anything about it. Content is a plain string or a `TemplateRef` for richer
722
+ markup. `gogTooltipPosition` (`GogTooltipPosition`: `'auto'` default, or an explicit
723
+ `'top'`/`'bottom'`/`'left'`/`'right'` that flips to its opposite if it has no room),
724
+ `gogTooltipShowDelay` (default `300`ms), `gogTooltipHideDelay` (default `100`ms) and
725
+ `gogTooltipDisabled` inputs; the first three also read `GOG_CONFIG.tooltip` for an
726
+ app-wide default the same way `gog-scroll`/`gog-button` already do, with an instance's own
727
+ input always winning. Shown on both mouse hover and keyboard focus (`focusin`/`focusout`,
728
+ not `focus`/`blur`, so it stays replay-safe under SSR event replay), dismissible with
729
+ Escape, and hoverable — moving the pointer from the trigger onto the bubble itself (e.g. to
730
+ read more of a long one, or scroll one taller than `--gog-tooltip-max-height`) cancels the
731
+ pending hide instead of racing it — per WCAG 2.1 SC 1.4.13. The bubble is appended to
732
+ `document.body` (so it's never clipped by an ancestor's `overflow: hidden`) via a new
733
+ internal `GogTooltipOverlay`, built on `ViewContainerRef.createComponent` + relocating the
734
+ node rather than `GogDropdownOverlay`'s `TemplateRef` approach, since a directive has no
735
+ template of its own to attach from. Visually it's the same "floating panel" recipe as
736
+ `gog-dialog`'s panel and `gog-select`'s dropdown (`--gog-surface-color` background, plain
737
+ `--gog-border-color` border, `--gog-panel-shadow`), not a bespoke inverted bubble, so it
738
+ reads as part of a themed app rather than a generic dark tooltip dropped on top of it.
739
+ Content wraps to `--gog-tooltip-max-width` (`280px`) and is capped at
740
+ `--gog-tooltip-max-height` (`220px`) through an internal `gog-scroll` — content under the
741
+ cap renders at exactly its own height, content over it scrolls, using the same themeable
742
+ scrollbar every other overflowing panel in this library uses instead of a native one (see
743
+ `styling.instructions.md`'s new "Scrollable content" section for that convention).
744
+ `gogTooltipClass` applies a class straight to the bubble, for restyling (or resizing) one
745
+ instance — needed because the bubble sits outside any scoped ancestor's stylesheet once
746
+ appended to `document.body`, the same "Panels rendered outside the component subtree"
747
+ limitation `gog-select`'s `[appendToBody]` panel already has, so the class has to come from
748
+ an unscoped (global) stylesheet. New `--gog-tooltip-*` tokens in `theme.css`; `gog-dialog`'s
749
+ panel now also raises `--gog-tooltip-z` (mirroring the existing `--gog-dropdown-z` bump) so
750
+ a tooltip triggered inside a dialog stacks above it.
751
+
752
+ ### Changed
753
+
754
+ - `gog-slider`'s track now paints a border (new `--gog-slider-track-border-width`/`-style`/
755
+ `-color` tokens, transparent by default — same opt-in convention as `--gog-btn-primary-border`)
756
+ and its fill is bound via `background` instead of `background-color`, so
757
+ `--gog-slider-fill-bg` also accepts a gradient (e.g. `linear-gradient(...)`), not just a
758
+ solid color. The thumb ("handle") was already fully customizable via its existing
759
+ `--gog-slider-thumb-*` tokens (size, background, border, radius, glow) — no change there.
760
+
761
+ ### Fixed
762
+
763
+ - `gog-slider`'s track background (`--gog-slider-track-bg`) no longer reuses
764
+ `--gog-accent-dim` — it sat on the same hue ramp as the fill (`--gog-accent-color`), so at
765
+ the track's 4px height the two read as one blob instead of a recessed groove with an
766
+ accent fill on top. Now `color-mix(in srgb, var(--gog-text-color) 30%, var(--gog-border-color))`:
767
+ a desaturated, theme-adaptive gray that darkens toward black in the light theme and
768
+ lightens toward parchment in the dark theme (`--gog-text-color` sits at whichever end of
769
+ that range per theme), so it's always distinct from the accent-colored fill and legible
770
+ against its own theme's surface.
771
+
772
+ ## [21.2.4] - 05.08.2026
773
+
774
+ ### Added
775
+
776
+ - `gog-collapsible`: a headless expand/collapse primitive — inline, not a portal (unlike
777
+ `gog-select`/`gog-multiselect`'s panel). Owns no markup: project any element as the
778
+ trigger via `gogCollapsibleTrigger` and any element as the panel via
779
+ `gogCollapsibleContent`; `[(open)]` is two-way bindable, `disabled` blocks toggling.
780
+ New `--gog-collapsible-*` tokens in `theme.css`; the trigger/content CSS classes live in
781
+ `utilities.css` since the projected content sits outside the component's own view.
782
+ - `gog-textarea`: a multi-line counterpart to `gog-inputfield`, sharing its
783
+ `--gog-input-*` tokens. `ControlValueAccessor`-based, works with
784
+ `formControl`/`formControlName`. `label`, `placeholder`, `errorMessage`,
785
+ `errorDisplay`, `disabled`, `size`, `fullWidth` and `rows` inputs.
786
+ - `gog-inputfield`'s `type` input now also accepts `'number'` and `'date'`,
787
+ plus new `min`/`max`/`step` inputs (applied only for `type="number"`). For a
788
+ `number` field the value written to/read from an attached
789
+ `formControl`/`formControlName` is a `number` (`null` when the field is
790
+ empty) rather than a string — `[(value)]` stays a string either way, since
791
+ it mirrors the native input's raw text.
792
+ - `gog-scroll`: a drop-in replacement for a native `overflow: auto` region.
793
+ Content keeps scrolling natively (wheel, touch, keyboard, focus-into-view);
794
+ only the browser's own scrollbar chrome is hidden and replaced with a
795
+ themeable, draggable overlay thumb. `axis` (`vertical`/`horizontal`/`both`),
796
+ `size` (`normal`/`thin`), `autoHide`/`hideDelay`, `reachThreshold` with
797
+ `gogReachStart`/`gogReachEnd` outputs, a `gogScroll` metrics output, and
798
+ `scrollTo`/`scrollToTop`/`scrollToBottom`/`scrollToLeft`/`scrollToRight`
799
+ public methods. New `--gog-scroll-*` tokens in `theme.css`.
800
+ - `gog-scroll`'s `overscrollBehavior` input (`'auto'` | `'contain'` | `'none'`,
801
+ mirrors the CSS property of the same name): what happens when a scroll
802
+ gesture reaches this instance's edge. Defaults to `'auto'` — chains to the
803
+ next scrollable ancestor, same as an un-customized `overflow: auto` div, so
804
+ scrolling to the end of a `gog-scroll`'d section and continuing the same
805
+ gesture now keeps scrolling the page instead of stopping dead. `gog-select`/
806
+ `gog-multiselect`'s option panel and `gog-dialog`'s body now set
807
+ `overscrollBehavior="contain"` explicitly, preserving their existing
808
+ (correct, overlay-appropriate) behavior now that the component-wide default
809
+ has changed to chain-through.
810
+ - `GOG_CONFIG`/`GogGlobalConfig`/`provideGogConfig(...)`: one injection token
811
+ for app-wide defaults across the library's component inputs, instead of a
812
+ separate token per component per setting. Call `provideGogConfig({ scroll:
813
+ {...}, button: {...} })` once in your app's providers (or a route's/
814
+ component's own `providers` for a subtree-scoped override); any instance
815
+ that doesn't set the input itself falls back to the configured value, then
816
+ to the component's own hardcoded default. `gog-scroll`'s `size`, `autoHide`,
817
+ `hideDelay` and `overscrollBehavior` and `gog-button`'s `debounce` are the
818
+ first inputs wired up to it — see the "Global configuration" section in
819
+ `gleks-ui-library.instructions.md` for how to add more. This only covers
820
+ inputs read in TypeScript that can't already be a CSS token; visual
821
+ defaults remain the `--gog-*` custom properties in `theme.css`.
822
+
823
+ ### Changed
824
+
825
+ - `gog-checkbox` now registers its `ControlValueAccessor` by self-injecting
826
+ `NgControl` in the constructor, matching every other form control in the
827
+ library, instead of the `NG_VALUE_ACCESSOR`/`forwardRef` provider pattern.
828
+ No behavior change — `formControl`/`formControlName` usage is unaffected.
829
+ - `gog-scroll`'s `size`, `autoHide`, `hideDelay` and `overscrollBehavior` inputs and
830
+ `gog-button`'s `debounce` input now default to `undefined` instead of a hardcoded
831
+ value, so they can fall through to `GOG_CONFIG` — read the resolved value (e.g. via
832
+ the rendered DOM) rather than the raw input signal if you need the effective default.
833
+ - `gog-select` and `gog-multiselect`: the option panel now scrolls via
834
+ `gog-scroll` instead of native `overflow-y`.
835
+ - `gog-dialog`: the body now scrolls via `gog-scroll` instead of native
836
+ `overflow-y`.
837
+ - `gog-table`: horizontal scrolling now goes through `gog-scroll` instead of
838
+ native `overflow-x`.
839
+
840
+ ### Fixed
841
+
842
+ - `gog-scroll` internals used `height: 100%` chains from `:host` down to the
843
+ viewport. A host whose own height comes from being flex-grown inside a
844
+ `max-height`-only ancestor (exactly the select/multiselect dropdown panel
845
+ and dialog body cases above) still failed to resolve a percentage height
846
+ read off it, collapsing back to content size — the panel stopped clipping
847
+ and scrolling. Switched every level to flex-basis chains
848
+ (`flex: 1 1 auto` + `min-height: 0`), which don't have that failure mode.
849
+ - `gog-scroll`'s horizontal content wrapper used `width: max-content`, which
850
+ created a circular sizing reference against a `width: 100%` child (e.g.
851
+ `gog-table`'s own `<table>`) and made some browsers fall back to a huge
852
+ sentinel width (~1,000,000px), pushing the table off-screen. Removed —
853
+ children already overflow a normal block parent without it.
854
+ - `gog-scroll` set `overscroll-behavior: contain` (both axes) on the
855
+ viewport unconditionally, which also blocked wheel scroll on an axis the
856
+ instance never actually scrolls (e.g. vertical wheel over a horizontal-only
857
+ instance), preventing it from bubbling up to scroll the page. Now set only
858
+ on the axis that's actually acting as a scroll container.
859
+ - `gog-scroll` kept a disabled or currently-non-overflowing axis at
860
+ `overflow: hidden`/`auto`, which makes an element a "scroll container" per
861
+ spec regardless of whether it has anything to scroll — becoming the
862
+ containing block for `position: sticky` descendants and a scroll-chaining
863
+ boundary, whether needed or not. This broke `gog-table`'s `stickyHeader`
864
+ and swallowed wheel scroll whenever a `gog-table` (which always wraps its
865
+ own horizontal scroll in a `gog-scroll`) was itself nested inside another
866
+ scrolling container, e.g. a `gog-scroll` capping its height. Both axes are
867
+ now `visible` unless that specific axis is genuinely scrolling.
868
+
869
+ ## [21.2.3] - 03.08.2026
870
+
871
+ ### Added
872
+
873
+ - `fullWidth` input on `gog-checkbox`, `gog-chip` and `gog-tag`, matching the
874
+ existing `gog-button` behavior: `false` by default (sized to content), `true`
875
+ stretches the component to fill its container.
876
+ - `fullWidth` input on `gog-inputfield`, `gog-select`, `gog-multiselect`,
877
+ `gog-table`, `gog-paginator` and `gog-slider`. Inverted from the input above:
878
+ these are already full width of their container by default, so `fullWidth`
879
+ defaults to `true` and set it to `false` to shrink the control to fit its
880
+ content instead (a fixed `--gog-slider-auto-width`, 240px by default, for
881
+ `gog-slider` specifically — its track has no content of its own to size to).
882
+ - `gog-accordion`'s `skeletonCount` input: how many skeleton rows to render
883
+ while `loading` is true and `items` is still empty. Defaults to `3`.
884
+
885
+ ### Changed
886
+
887
+ - `gog-accordion`'s `loading` skeleton now renders with `gog-skeleton` instead
888
+ of a bespoke shimmer implementation. **Breaking:** the
889
+ `--gog-accordion-skeleton-start/-mid/-end/-radius/-height/-width/-duration`
890
+ tokens are gone — restyle the loading state via the shared `--gog-skeleton-*`
891
+ tokens instead.
892
+
893
+ ### Fixed
894
+
895
+ - `gog-accordion`'s `loading` skeleton now actually renders while `items` is
896
+ empty. It previously rendered one skeleton row per existing item, so the
897
+ most common real-world case — showing loading state before the item list
898
+ has arrived at all — silently rendered nothing. It now falls back to
899
+ `skeletonCount` rows whenever `items` is empty, and still mirrors `items`
900
+ once they exist.
901
+ - `gog-accordion`'s chevron no longer force-rotates 180° when a custom
902
+ `gogAccordionChevron` template is supplied. Previously the wrapper always
903
+ rotated on open regardless of what the template rendered, so a template that
904
+ swapped between a `chevron-up`/`chevron-down` icon per `open` state ended up
905
+ double-transformed (both states visually pointing the same way). The rotation
906
+ now only applies to the built-in default chevron; a custom template owns its
907
+ open/closed presentation entirely, including bringing its own animation or
908
+ swapping in a completely different icon.
909
+
910
+ ## [21.2.2] - 30.07.2026
911
+
912
+ ### Added
913
+
914
+ - `column`'s `comparator` input for custom per-column sort ordering; the default
915
+ comparator now uses `Intl.Collator` for numeric-aware string sorting
916
+ (`"item2" < "item10"`) instead of raw `<`/`>`.
917
+ - `gog-table` cell/sort values now resolve dot-path nested fields (e.g.
918
+ `field="address.city"`).
919
+ - ESLint (`@angular-eslint`, flat config) across `@gleks/ui` and `ui-showcase`, wired
920
+ into CI alongside `format:check` and a token-consistency check (every
921
+ `var(--gog-*)` read with no fallback must resolve to a declared default).
922
+ - `LICENSE` (MIT) and this changelog.
923
+
924
+ ### Changed
925
+
926
+ - **Breaking:** every previously unprefixed global design token in `theme.css`
927
+ (`--accent-color`, `--text-color`, `--radius`, `--control-*`, `--field-*`,
928
+ `--dropdown-z`, etc.) is now `--gog-*` prefixed, matching the component-token
929
+ convention. Update any consumer theme overrides to the new names.
930
+ - **Breaking:** `column`'s `field` input is now a plain `string` (was
931
+ `keyof T & string`) to support nested dot-paths.
932
+ - `gog-select`/`gog-multiselect` panel sizing constants (max height, estimated row
933
+ height) are now read from CSS custom properties
934
+ (`--gog-select-panel-max-height`, `--gog-select-option-height`, and the
935
+ multiselect equivalents) instead of hardcoded in TypeScript, so they're themeable.
936
+ - `gog-table`'s pagination state now uses `linkedSignal` instead of a manual
937
+ `effect`, resetting to page 1 on sort changes and clamping to `totalPages` on
938
+ data/page-size changes, while still deferring to `gog-paginator`'s own
939
+ self-clamping `page` model.
940
+
941
+ ### Fixed
942
+
943
+ - An append-to-body dropdown panel now copies the trigger's scoped `data-theme`
944
+ (not just `:root`'s) onto its overlay host, so panels stay themed when opened
945
+ inside a themed subtree.
946
+ - `gog-select` now correctly reads its own `--gog-select-option-gap` token for
947
+ panel-height estimation instead of the unused base default, fixing a latent
948
+ under-estimate in the panel's up/down placement math.
949
+
950
+ ## [0.0.1] through 0.2.2
951
+
952
+ Initial development, published as `0.0.1`: accordion, button, checkbox, chip, dialog,
953
+ icon, inputfield, multiselect, paginator, select, skeleton, slider, spinner, table, tag
954
+ and toast components, plus the shared theme (`styles/theme.css`) and `ThemeService`.
955
+ Versions up to `0.2.2` were developed without per-release changelog entries. `0.2.2` was
956
+ published with the wrong version scheme and immediately re-published, with no code
957
+ changes, as `21.2.2` — this file tracks changes from `21.2.2` onward.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guildofgleks/ui",
3
- "version": "21.4.1",
3
+ "version": "21.4.2",
4
4
  "engines": {
5
5
  "node": ">=20.19.0"
6
6
  },
@@ -1639,7 +1639,7 @@ declare class CalendarComponent {
1639
1639
  protected readonly seconds: _angular_core.Signal<number>;
1640
1640
  protected readonly isPm: _angular_core.Signal<boolean>;
1641
1641
  protected readonly maxHour: _angular_core.Signal<12 | 23>;
1642
- protected readonly minHour: _angular_core.Signal<1 | 0>;
1642
+ protected readonly minHour: _angular_core.Signal<0 | 1>;
1643
1643
  constructor();
1644
1644
  /** Steps the view by whole months or years. */
1645
1645
  protected shiftView(months: number): void;