@guildofgleks/ui 21.12.0 → 21.13.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.
@@ -0,0 +1,4692 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, computed, signal, TemplateRef, Directive, input, contentChild, ElementRef, Injector, PLATFORM_ID, ApplicationRef, DestroyRef, effect, afterNextRender } from '@angular/core';
3
+ import { isPlatformBrowser, DOCUMENT } from '@angular/common';
4
+ import { NgControl } from '@angular/forms';
5
+
6
+ const GOG_CHECKABLE_CONTROL_PADDING = 'var(--gog-control-checkbox-padding, 8px)';
7
+ const GOG_CHECKABLE_CONTROL_SIZE_MAP = {
8
+ xsm: {
9
+ boxSize: 'var(--gog-control-checkbox-box-size-xsm, 12px)',
10
+ labelSize: 'var(--gog-control-checkbox-label-size-xsm, 0.6875rem)',
11
+ indicatorSize: 'var(--gog-control-checkbox-icon-size-xsm, 10px)',
12
+ },
13
+ sm: {
14
+ boxSize: 'var(--gog-control-checkbox-box-size-sm, 18px)',
15
+ labelSize: 'var(--gog-control-checkbox-label-size-sm, 0.8125rem)',
16
+ indicatorSize: 'var(--gog-control-checkbox-icon-size-sm, 12px)',
17
+ },
18
+ md: {
19
+ boxSize: 'var(--gog-control-checkbox-box-size-md, 24px)',
20
+ labelSize: 'var(--gog-control-checkbox-label-size-md, 0.9375rem)',
21
+ indicatorSize: 'var(--gog-control-checkbox-icon-size-md, 14px)',
22
+ },
23
+ lg: {
24
+ boxSize: 'var(--gog-control-checkbox-box-size-lg, 32px)',
25
+ labelSize: 'var(--gog-control-checkbox-label-size-lg, 1.0625rem)',
26
+ indicatorSize: 'var(--gog-control-checkbox-icon-size-lg, 18px)',
27
+ },
28
+ slg: {
29
+ boxSize: 'var(--gog-control-checkbox-box-size-slg, 40px)',
30
+ labelSize: 'var(--gog-control-checkbox-label-size-slg, 1.1875rem)',
31
+ indicatorSize: 'var(--gog-control-checkbox-icon-size-slg, 22px)',
32
+ },
33
+ };
34
+
35
+ /**
36
+ * Resolves to `{}` — every field falls through to its component's own hardcoded default —
37
+ * until a `provideGogConfig(...)` call overrides it somewhere in the injector tree.
38
+ */
39
+ const GOG_CONFIG = new InjectionToken('GOG_CONFIG', {
40
+ providedIn: 'root',
41
+ factory: () => ({}),
42
+ });
43
+ /**
44
+ * Layers `override` onto `base`, one level deep: a component key present in both has its
45
+ * fields merged (so `{ tooltip: { showDelay } }` keeps the parent's `tooltip.position`), and a
46
+ * key present in only one is taken as-is.
47
+ *
48
+ * Deliberately not a deep merge — `GogGlobalConfig` is exactly two levels by design, and a
49
+ * recursive merge would start doing surprising things to any future field whose value is
50
+ * itself an object the consumer means to replace wholesale.
51
+ */
52
+ function mergeGogConfig(base, override) {
53
+ const merged = { ...base };
54
+ for (const [key, overrideValue] of Object.entries(override)) {
55
+ const baseValue = base[key];
56
+ merged[key] =
57
+ isPlainObject(baseValue) && isPlainObject(overrideValue)
58
+ ? { ...baseValue, ...overrideValue }
59
+ : overrideValue;
60
+ }
61
+ return merged;
62
+ }
63
+ function isPlainObject(value) {
64
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
65
+ }
66
+ /**
67
+ * Sets app-wide (or subtree-wide, if placed in a route's or component's own `providers`
68
+ * instead of the bootstrap config) defaults for every `@guildofgleks/ui` component that
69
+ * reads `GOG_CONFIG` — one call instead of a separate injection token per setting:
70
+ *
71
+ * ```ts
72
+ * // app.config.ts
73
+ * providers: [
74
+ * provideGogConfig({
75
+ * scroll: { hideDelay: 1000 },
76
+ * button: { debounce: 500 },
77
+ * }),
78
+ * ]
79
+ * ```
80
+ *
81
+ * **Providing this again further down the injector tree layers onto the parent's config
82
+ * rather than replacing it.** A route that only cares about tooltips can say so, and the
83
+ * app-wide `button.debounce` stays in effect inside it:
84
+ *
85
+ * ```ts
86
+ * // a route's providers — button.debounce from app.config.ts still applies here
87
+ * provideGogConfig({ tooltip: { showDelay: 0 } })
88
+ * ```
89
+ *
90
+ * Merging is one level deep, per component key: the nearest provider wins field by field, so
91
+ * `{ tooltip: { showDelay: 0 } }` overrides only `showDelay` and leaves a parent's
92
+ * `tooltip.position` alone. To drop an inherited value rather than change it, set it back to
93
+ * the component's own default explicitly — there is no "unset" marker.
94
+ */
95
+ function provideGogConfig(config) {
96
+ return {
97
+ provide: GOG_CONFIG,
98
+ // skipSelf so this reads the *parent* injector's config rather than recursing into the
99
+ // provider being defined here; optional because at the root there is no parent providing it.
100
+ useFactory: () => mergeGogConfig(inject(GOG_CONFIG, { skipSelf: true, optional: true }) ?? {}, config),
101
+ };
102
+ }
103
+ /**
104
+ * The library's precedence rule for a configurable input, in one place: an instance's own
105
+ * input wins, then the app-wide `GOG_CONFIG` value, then the component's built-in default.
106
+ *
107
+ * Every configurable input resolves through this rather than repeating the `??` chain, so the
108
+ * order can't drift between components — a component that accidentally checked the config
109
+ * first would silently ignore the input on that one control only, which is close to invisible
110
+ * in review. Kept a plain function (no `inject`) so it works anywhere: inside a `computed`, in
111
+ * a composition class like `GogFloatLabelState`, and in unit tests without an injector.
112
+ *
113
+ * ```ts
114
+ * private readonly globalConfig = inject(GOG_CONFIG);
115
+ * protected readonly resolvedDebounce = computed(() =>
116
+ * resolveConfigured(this.debounce(), this.globalConfig.button?.debounce, DEFAULT_DEBOUNCE),
117
+ * );
118
+ * ```
119
+ */
120
+ function resolveConfigured(instanceValue, configuredValue, fallback) {
121
+ return instanceValue ?? configuredValue ?? fallback;
122
+ }
123
+
124
+ /**
125
+ * Shared "show a clear button" state for the field-style controls.
126
+ *
127
+ * A plain composition class, same reasoning as `GogErrorState` and `GogFloatLabelState`: it has
128
+ * to serve `gog-inputfield` and `gog-textarea` (no common base class) as well as
129
+ * `GogDropdownBase`, which is one.
130
+ *
131
+ * The two things each control supplies itself are `hasValue` — "there is something to clear"
132
+ * differs per control (non-empty string / non-null selection / non-empty array) — and the
133
+ * actual clearing, since only the control knows its own empty value and how to notify forms.
134
+ */
135
+ class GogClearableState {
136
+ clearableInput;
137
+ hasValue;
138
+ isNotEditable;
139
+ config;
140
+ fallback;
141
+ /**
142
+ * @param clearableInput the control's own `clearable` input (`undefined` when unset)
143
+ * @param hasValue whether there is anything to clear
144
+ * @param isNotEditable whether the control currently refuses edits — disabled, or (for the
145
+ * text controls) read-only. Either way it offers no clear button: the affordance would
146
+ * promise a change the control won't accept.
147
+ * @param config the injected `GOG_CONFIG`
148
+ * @param fallback the control's own default, read lazily — `GogDropdownBase` constructs this
149
+ * in a field initializer, before its subclass has assigned `clearableByDefault`. `false`
150
+ * everywhere except `gog-multiselect`, which shipped a clear button before this input
151
+ * existed and keeps it.
152
+ */
153
+ constructor(clearableInput, hasValue, isNotEditable, config, fallback) {
154
+ this.clearableInput = clearableInput;
155
+ this.hasValue = hasValue;
156
+ this.isNotEditable = isNotEditable;
157
+ this.config = config;
158
+ this.fallback = fallback;
159
+ }
160
+ /** Whether the control is clearable at all — instance input, then config, then the default. */
161
+ enabled = computed(() => resolveConfigured(this.clearableInput(), this.config.control?.clearable, this.fallback()), ...(ngDevMode ? [{ debugName: "enabled" }] : /* istanbul ignore next */ []));
162
+ /**
163
+ * Whether the clear button should render *right now*. Deliberately value-driven: the control
164
+ * shows nothing to clear until there is something to clear, so the affordance appears with the
165
+ * content rather than sitting there permanently as dead chrome.
166
+ */
167
+ isVisible = computed(() => this.enabled() && this.hasValue() && !this.isNotEditable(), ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
168
+ }
169
+
170
+ /**
171
+ * Per-prefix counter behind every auto-generated DOM id in the library.
172
+ *
173
+ * A form control needs a real `id` whether or not the consumer supplied one: without it a
174
+ * `<label for>` cannot point at the field (so clicking the label does not focus it, and
175
+ * assistive tech gets no accessible name), and `aria-describedby` cannot point at the error
176
+ * message. Making the consumer pass `inputId` for that is a trap — the field looks fine and is
177
+ * quietly inaccessible.
178
+ *
179
+ * Counters are keyed by prefix rather than shared, so ids stay stable per component type
180
+ * (`gog-input-1`, `gog-slider-1`) instead of depending on how many *other* controls happened to
181
+ * be constructed first. That also keeps them predictable in tests and diffs.
182
+ *
183
+ * SSR/hydration-safe by construction: the server and the client walk the same component tree in
184
+ * the same order, so both arrive at the same ids. Do not seed this with randomness for that
185
+ * reason.
186
+ */
187
+ const counters = new Map();
188
+ /** Returns the next id for `prefix`, e.g. `nextGogControlId('gog-input')` → `'gog-input-3'`. */
189
+ function nextGogControlId(prefix) {
190
+ const next = (counters.get(prefix) ?? 0) + 1;
191
+ counters.set(prefix, next);
192
+ return `${prefix}-${next}`;
193
+ }
194
+
195
+ /**
196
+ * Every date calculation `gog-datepicker` and `gog-calendar` do, as pure functions.
197
+ *
198
+ * Kept out of the components so the arithmetic is testable without a fixture, and so a range
199
+ * calculation is written once rather than once per selection mode.
200
+ *
201
+ * **Everything here works in local time.** Nothing round-trips through `toISOString()` or
202
+ * compares `Date` objects directly: an ISO round trip converts to UTC, which moves the date
203
+ * across midnight for anyone east or west of Greenwich, and two `Date`s for "the same day"
204
+ * are almost never equal because they carry different times. Comparisons go through
205
+ * `startOfDay` and `isSameDay` instead. This is the single most common defect in a datepicker.
206
+ */
207
+ /** Midnight local time on the same calendar day. */
208
+ function startOfDay(date) {
209
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
210
+ }
211
+ /** Whether two dates fall on the same calendar day, ignoring the time of day. */
212
+ function isSameDay(a, b) {
213
+ if (!a || !b)
214
+ return false;
215
+ return (a.getFullYear() === b.getFullYear() &&
216
+ a.getMonth() === b.getMonth() &&
217
+ a.getDate() === b.getDate());
218
+ }
219
+ /** Whether two dates fall in the same calendar month. */
220
+ function isSameMonth(a, b) {
221
+ if (!a || !b)
222
+ return false;
223
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
224
+ }
225
+ function addDays(date, days) {
226
+ const next = new Date(date.getTime());
227
+ next.setDate(next.getDate() + days);
228
+ return next;
229
+ }
230
+ /** Days in a given month. `month` is 0-based, as `Date` uses it. */
231
+ function daysInMonth(year, month) {
232
+ // Day 0 of the next month is the last day of this one.
233
+ return new Date(year, month + 1, 0).getDate();
234
+ }
235
+ /**
236
+ * Adds whole months, **clamping the day** to the target month's length: 31 January plus one
237
+ * month is 28 February, not 3 March. `Date.setMonth` does the latter, which is why this is
238
+ * not a one-liner.
239
+ */
240
+ function addMonths(date, months) {
241
+ const year = date.getFullYear();
242
+ const month = date.getMonth() + months;
243
+ const targetYear = year + Math.floor(month / 12);
244
+ const targetMonth = ((month % 12) + 12) % 12;
245
+ const day = Math.min(date.getDate(), daysInMonth(targetYear, targetMonth));
246
+ return new Date(targetYear, targetMonth, day, date.getHours(), date.getMinutes(), date.getSeconds());
247
+ }
248
+ function addYears(date, years) {
249
+ return addMonths(date, years * 12);
250
+ }
251
+ /** Day-granularity comparison: `a` is strictly before `b`'s day. */
252
+ function isBeforeDay(a, b) {
253
+ return startOfDay(a).getTime() < startOfDay(b).getTime();
254
+ }
255
+ /** Day-granularity comparison: `a` is strictly after `b`'s day. */
256
+ function isAfterDay(a, b) {
257
+ return startOfDay(a).getTime() > startOfDay(b).getTime();
258
+ }
259
+ /** Whether `date`'s day falls within `[min, max]`, either bound optional. */
260
+ function isWithinBounds(date, min, max) {
261
+ if (min && isBeforeDay(date, min))
262
+ return false;
263
+ if (max && isAfterDay(date, max))
264
+ return false;
265
+ return true;
266
+ }
267
+ /** Whether `date`'s day falls inside a range, inclusive of both ends. */
268
+ function isInRange(date, start, end) {
269
+ if (!start || !end)
270
+ return false;
271
+ const [from, to] = isAfterDay(start, end) ? [end, start] : [start, end];
272
+ return !isBeforeDay(date, from) && !isAfterDay(date, to);
273
+ }
274
+ /** Pulls `date` inside `[min, max]`, keeping its time of day. */
275
+ function clampDate(date, min, max) {
276
+ if (min && isBeforeDay(date, min))
277
+ return copyTimeOnto(min, date);
278
+ if (max && isAfterDay(date, max))
279
+ return copyTimeOnto(max, date);
280
+ return date;
281
+ }
282
+ /** A new date on `day`'s calendar day carrying `time`'s clock. */
283
+ function copyTimeOnto(day, time) {
284
+ return new Date(day.getFullYear(), day.getMonth(), day.getDate(), time.getHours(), time.getMinutes(), time.getSeconds());
285
+ }
286
+ /** A new date on the same day with the clock replaced. */
287
+ function withTime(date, hours, minutes, seconds = 0) {
288
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate(), hours, minutes, seconds);
289
+ }
290
+ /**
291
+ * The six-week grid a month is drawn on, always 6×7 so the calendar's height never changes as
292
+ * you page through months — a grid that grows and shrinks pushes the rest of the page around
293
+ * and moves the button you were about to click.
294
+ *
295
+ * `firstDayOfWeek` is 0 (Sunday) to 6 (Saturday). Leading and trailing cells come from the
296
+ * neighbouring months, as every calendar does.
297
+ */
298
+ function buildMonthGrid(year, month, firstDayOfWeek) {
299
+ const first = new Date(year, month, 1);
300
+ const shift = (first.getDay() - firstDayOfWeek + 7) % 7;
301
+ const gridStart = addDays(first, -shift);
302
+ const weeks = [];
303
+ for (let week = 0; week < 6; week++) {
304
+ const days = [];
305
+ for (let day = 0; day < 7; day++) {
306
+ days.push(addDays(gridStart, week * 7 + day));
307
+ }
308
+ weeks.push(days);
309
+ }
310
+ return weeks;
311
+ }
312
+ /** Weekday names in `locale`, rotated so `firstDayOfWeek` comes first. */
313
+ function weekdayNames(locale, firstDayOfWeek, style = 'short') {
314
+ const formatter = new Intl.DateTimeFormat(locale, { weekday: style });
315
+ // 2024-01-07 was a Sunday, so index 0 lines up with `Date.getDay()` 0.
316
+ const sunday = new Date(2024, 0, 7);
317
+ return Array.from({ length: 7 }, (_, i) => formatter.format(addDays(sunday, (i + firstDayOfWeek) % 7)));
318
+ }
319
+ /** Month names in `locale`. */
320
+ function monthNames(locale, style = 'long') {
321
+ const formatter = new Intl.DateTimeFormat(locale, { month: style });
322
+ return Array.from({ length: 12 }, (_, month) => formatter.format(new Date(2024, month, 1)));
323
+ }
324
+ /**
325
+ * The first day of the week for a locale, from `Intl.Locale.prototype.getWeekInfo` where the
326
+ * engine has it, falling back to Monday.
327
+ *
328
+ * `getWeekInfo` reports 1–7 with 1 = Monday and 7 = Sunday; `Date.getDay()` uses 0 = Sunday.
329
+ * Converting between the two is exactly the kind of off-by-one that silently shifts a whole
330
+ * calendar by a day, which is why it happens here once.
331
+ */
332
+ function localeFirstDayOfWeek(locale) {
333
+ try {
334
+ const resolved = new Intl.Locale(locale);
335
+ const info = resolved.getWeekInfo?.() ?? resolved.weekInfo;
336
+ if (info && typeof info.firstDay === 'number') {
337
+ return info.firstDay % 7;
338
+ }
339
+ }
340
+ catch {
341
+ // An invalid locale tag shouldn't take the calendar down with it.
342
+ }
343
+ return 1;
344
+ }
345
+ const PAD2 = (value) => String(value).padStart(2, '0');
346
+ /**
347
+ * Formats a date against a token pattern.
348
+ *
349
+ * Supported tokens: `yyyy`, `MM`, `dd`, `HH` (24h), `hh` (12h), `mm`, `ss`, `a` (AM/PM).
350
+ * Anything else is copied through literally.
351
+ *
352
+ * Deliberately **not** `Intl.DateTimeFormat`: this pattern is also what `parseDate` reads, and
353
+ * a formatter whose output cannot be parsed back is how a typed date silently becomes a
354
+ * different one. `Intl` is still used for month and weekday *names*, which are never parsed.
355
+ */
356
+ function formatDate(date, pattern) {
357
+ const hours24 = date.getHours();
358
+ const hours12 = hours24 % 12 === 0 ? 12 : hours24 % 12;
359
+ const replacements = {
360
+ yyyy: String(date.getFullYear()).padStart(4, '0'),
361
+ MM: PAD2(date.getMonth() + 1),
362
+ dd: PAD2(date.getDate()),
363
+ HH: PAD2(hours24),
364
+ hh: PAD2(hours12),
365
+ mm: PAD2(date.getMinutes()),
366
+ ss: PAD2(date.getSeconds()),
367
+ a: hours24 < 12 ? 'AM' : 'PM',
368
+ };
369
+ return pattern.replace(/yyyy|MM|dd|HH|hh|mm|ss|a/g, (token) => replacements[token]);
370
+ }
371
+ /**
372
+ * Reads a date back out of text written in `pattern`. Returns `null` when the text doesn't
373
+ * match, or matches but isn't a real date (`31.02.2026`).
374
+ *
375
+ * The out-of-range check matters: `new Date(2026, 1, 31)` happily rolls over to 3 March, so a
376
+ * typo would be accepted as a different date rather than rejected.
377
+ */
378
+ function parseDate(text, pattern) {
379
+ const trimmed = text.trim();
380
+ if (trimmed === '')
381
+ return null;
382
+ const order = [];
383
+ const source = pattern.replace(/yyyy|MM|dd|HH|hh|mm|ss|a|./g, (token) => {
384
+ switch (token) {
385
+ case 'yyyy':
386
+ order.push(token);
387
+ return '(\\d{4})';
388
+ case 'MM':
389
+ case 'dd':
390
+ case 'HH':
391
+ case 'hh':
392
+ case 'mm':
393
+ case 'ss':
394
+ order.push(token);
395
+ return '(\\d{1,2})';
396
+ case 'a':
397
+ order.push(token);
398
+ return '([AaPp][Mm])';
399
+ default:
400
+ return token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
401
+ }
402
+ });
403
+ const match = new RegExp(`^${source}$`).exec(trimmed);
404
+ if (!match)
405
+ return null;
406
+ const parts = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0 };
407
+ let meridiem = null;
408
+ order.forEach((token, index) => {
409
+ const raw = match[index + 1];
410
+ if (token === 'a') {
411
+ meridiem = raw.toLowerCase();
412
+ }
413
+ else {
414
+ parts[token] = Number(raw);
415
+ }
416
+ });
417
+ if (order.includes('hh') && meridiem) {
418
+ const base = parts['hh'] % 12;
419
+ parts['HH'] = meridiem === 'pm' ? base + 12 : base;
420
+ }
421
+ else if (order.includes('hh')) {
422
+ parts['HH'] = parts['hh'];
423
+ }
424
+ const year = parts['yyyy'];
425
+ const month = parts['MM'] - 1;
426
+ const day = parts['dd'];
427
+ if (month < 0 || month > 11)
428
+ return null;
429
+ if (day < 1 || day > daysInMonth(year, month))
430
+ return null;
431
+ if (parts['HH'] > 23 || parts['mm'] > 59 || parts['ss'] > 59)
432
+ return null;
433
+ return new Date(year, month, day, parts['HH'], parts['mm'], parts['ss']);
434
+ }
435
+
436
+ // GENERATED by scripts/generate-deprecations.mjs — do not edit by hand.
437
+ // Run `npm run generate:deprecations`; `npm run check:deprecations` fails on a stale copy.
438
+ /**
439
+ * Everything `@guildofgleks/ui` currently deprecates: 28 symbol(s) and 3 token(s).
440
+ *
441
+ * Generated from the library's own source — `@deprecated` tags for symbols, and the stylesheets
442
+ * themselves for tokens — so it cannot drift from what actually still resolves. Meant for tooling
443
+ * that has to answer "is this still supported, and until when?": a docs site marking an API row,
444
+ * an editor plugin, a codemod.
445
+ *
446
+ * An empty `symbol` half means exactly what it says: nothing in the TypeScript API is deprecated
447
+ * right now.
448
+ */
449
+ const GOG_DEPRECATIONS = [
450
+ {
451
+ kind: 'symbol',
452
+ name: 'CalendarComponent',
453
+ replacement: 'import it from `@guildofgleks/ui/datepicker`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
454
+ since: '21.13.0',
455
+ sinceDate: '2026-09-13',
456
+ removedIn: '21.14.0',
457
+ },
458
+ {
459
+ kind: 'symbol',
460
+ name: 'ConfirmationDialogComponent',
461
+ replacement: 'import it from `@guildofgleks/ui/dialog`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
462
+ since: '21.13.0',
463
+ sinceDate: '2026-09-13',
464
+ removedIn: '21.14.0',
465
+ },
466
+ {
467
+ kind: 'symbol',
468
+ name: 'ConfirmDialogData',
469
+ replacement: 'import it from `@guildofgleks/ui/dialog`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
470
+ since: '21.13.0',
471
+ sinceDate: '2026-09-13',
472
+ removedIn: '21.14.0',
473
+ },
474
+ {
475
+ kind: 'symbol',
476
+ name: 'DatepickerComponent',
477
+ replacement: 'import it from `@guildofgleks/ui/datepicker`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
478
+ since: '21.13.0',
479
+ sinceDate: '2026-09-13',
480
+ removedIn: '21.14.0',
481
+ },
482
+ {
483
+ kind: 'symbol',
484
+ name: 'defaultCompare',
485
+ replacement: 'import it from `@guildofgleks/ui/table`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
486
+ since: '21.13.0',
487
+ sinceDate: '2026-09-13',
488
+ removedIn: '21.14.0',
489
+ },
490
+ {
491
+ kind: 'symbol',
492
+ name: 'DIALOG_DATA',
493
+ replacement: 'import it from `@guildofgleks/ui/dialog`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
494
+ since: '21.13.0',
495
+ sinceDate: '2026-09-13',
496
+ removedIn: '21.14.0',
497
+ },
498
+ {
499
+ kind: 'symbol',
500
+ name: 'DIALOG_REF',
501
+ replacement: 'import it from `@guildofgleks/ui/dialog`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
502
+ since: '21.13.0',
503
+ sinceDate: '2026-09-13',
504
+ removedIn: '21.14.0',
505
+ },
506
+ {
507
+ kind: 'symbol',
508
+ name: 'DialogComponent',
509
+ replacement: 'import it from `@guildofgleks/ui/dialog`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
510
+ since: '21.13.0',
511
+ sinceDate: '2026-09-13',
512
+ removedIn: '21.14.0',
513
+ },
514
+ {
515
+ kind: 'symbol',
516
+ name: 'DialogConfig',
517
+ replacement: 'import it from `@guildofgleks/ui/dialog`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
518
+ since: '21.13.0',
519
+ sinceDate: '2026-09-13',
520
+ removedIn: '21.14.0',
521
+ },
522
+ {
523
+ kind: 'symbol',
524
+ name: 'DialogHandle',
525
+ replacement: 'import it from `@guildofgleks/ui/dialog`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
526
+ since: '21.13.0',
527
+ sinceDate: '2026-09-13',
528
+ removedIn: '21.14.0',
529
+ },
530
+ {
531
+ kind: 'symbol',
532
+ name: 'DialogRef',
533
+ replacement: 'import it from `@guildofgleks/ui/dialog`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
534
+ since: '21.13.0',
535
+ sinceDate: '2026-09-13',
536
+ removedIn: '21.14.0',
537
+ },
538
+ {
539
+ kind: 'symbol',
540
+ name: 'DialogService',
541
+ replacement: 'import it from `@guildofgleks/ui/dialog`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
542
+ since: '21.13.0',
543
+ sinceDate: '2026-09-13',
544
+ removedIn: '21.14.0',
545
+ },
546
+ {
547
+ kind: 'symbol',
548
+ name: 'getByPath',
549
+ replacement: "read the field yourself; this is the package's own plumbing and it leaves the root. It stays inside `@guildofgleks/ui/shared`, the internal entry point, which is not a replacement to build on.",
550
+ since: '21.13.0',
551
+ sinceDate: '2026-09-12',
552
+ removedIn: '21.14.0',
553
+ },
554
+ {
555
+ kind: 'symbol',
556
+ name: 'GogCalendarDay',
557
+ replacement: 'import it from `@guildofgleks/ui/datepicker`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
558
+ since: '21.13.0',
559
+ sinceDate: '2026-09-13',
560
+ removedIn: '21.14.0',
561
+ },
562
+ {
563
+ kind: 'symbol',
564
+ name: 'GogColumn',
565
+ replacement: 'import it from `@guildofgleks/ui/table`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
566
+ since: '21.13.0',
567
+ sinceDate: '2026-09-13',
568
+ removedIn: '21.14.0',
569
+ },
570
+ {
571
+ kind: 'symbol',
572
+ name: 'GogColumnBodyContext',
573
+ replacement: 'import it from `@guildofgleks/ui/table`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
574
+ since: '21.13.0',
575
+ sinceDate: '2026-09-13',
576
+ removedIn: '21.14.0',
577
+ },
578
+ {
579
+ kind: 'symbol',
580
+ name: 'GogColumnBodyDirective',
581
+ replacement: 'import it from `@guildofgleks/ui/table`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
582
+ since: '21.13.0',
583
+ sinceDate: '2026-09-13',
584
+ removedIn: '21.14.0',
585
+ },
586
+ {
587
+ kind: 'symbol',
588
+ name: 'GogColumnHeaderContext',
589
+ replacement: 'import it from `@guildofgleks/ui/table`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
590
+ since: '21.13.0',
591
+ sinceDate: '2026-09-13',
592
+ removedIn: '21.14.0',
593
+ },
594
+ {
595
+ kind: 'symbol',
596
+ name: 'GogColumnHeaderDirective',
597
+ replacement: 'import it from `@guildofgleks/ui/table`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
598
+ since: '21.13.0',
599
+ sinceDate: '2026-09-13',
600
+ removedIn: '21.14.0',
601
+ },
602
+ {
603
+ kind: 'symbol',
604
+ name: 'GogDatepickerValue',
605
+ replacement: 'import it from `@guildofgleks/ui/datepicker`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
606
+ since: '21.13.0',
607
+ sinceDate: '2026-09-13',
608
+ removedIn: '21.14.0',
609
+ },
610
+ {
611
+ kind: 'symbol',
612
+ name: 'GogTableRowClickEvent',
613
+ replacement: 'import it from `@guildofgleks/ui/table`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
614
+ since: '21.13.0',
615
+ sinceDate: '2026-09-13',
616
+ removedIn: '21.14.0',
617
+ },
618
+ {
619
+ kind: 'symbol',
620
+ name: 'GogTableSelectionMode',
621
+ replacement: 'import it from `@guildofgleks/ui/table`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
622
+ since: '21.13.0',
623
+ sinceDate: '2026-09-13',
624
+ removedIn: '21.14.0',
625
+ },
626
+ {
627
+ kind: 'symbol',
628
+ name: 'GogTableSortEvent',
629
+ replacement: 'import it from `@guildofgleks/ui/table`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
630
+ since: '21.13.0',
631
+ sinceDate: '2026-09-13',
632
+ removedIn: '21.14.0',
633
+ },
634
+ {
635
+ kind: 'symbol',
636
+ name: 'isSameOptionValue',
637
+ replacement: "read the field yourself; this is the package's own plumbing and it leaves the root. It stays inside `@guildofgleks/ui/shared`, the internal entry point, which is not a replacement to build on.",
638
+ since: '21.13.0',
639
+ sinceDate: '2026-09-12',
640
+ removedIn: '21.14.0',
641
+ },
642
+ {
643
+ kind: 'symbol',
644
+ name: 'OpenDialog',
645
+ replacement: 'import it from `@guildofgleks/ui/dialog`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
646
+ since: '21.13.0',
647
+ sinceDate: '2026-09-13',
648
+ removedIn: '21.14.0',
649
+ },
650
+ {
651
+ kind: 'symbol',
652
+ name: 'readOption',
653
+ replacement: "read the field yourself; this is the package's own plumbing and it leaves the root. It stays inside `@guildofgleks/ui/shared`, the internal entry point, which is not a replacement to build on.",
654
+ since: '21.13.0',
655
+ sinceDate: '2026-09-12',
656
+ removedIn: '21.14.0',
657
+ },
658
+ {
659
+ kind: 'symbol',
660
+ name: 'SortDirection',
661
+ replacement: 'import it from `@guildofgleks/ui/table`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
662
+ since: '21.13.0',
663
+ sinceDate: '2026-09-13',
664
+ removedIn: '21.14.0',
665
+ },
666
+ {
667
+ kind: 'symbol',
668
+ name: 'TableComponent',
669
+ replacement: 'import it from `@guildofgleks/ui/table`. From that minor on it stays out of the initial bundle of an app that only uses it behind a lazy route.',
670
+ since: '21.13.0',
671
+ sinceDate: '2026-09-13',
672
+ removedIn: '21.14.0',
673
+ },
674
+ {
675
+ kind: 'token',
676
+ name: '--gog-multiselect-panel-offset',
677
+ replacement: '--gog-multiselect-panel-gap',
678
+ since: '21.13.0',
679
+ sinceDate: '2026-09-12',
680
+ removedIn: '21.14.0',
681
+ },
682
+ {
683
+ kind: 'token',
684
+ name: '--gog-select-panel-offset',
685
+ replacement: '--gog-select-panel-gap',
686
+ since: '21.13.0',
687
+ sinceDate: '2026-09-12',
688
+ removedIn: '21.14.0',
689
+ },
690
+ {
691
+ kind: 'token',
692
+ name: '--gog-slider-thumb-shadow',
693
+ replacement: '--gog-slider-thumb-glow-color',
694
+ since: '21.13.0',
695
+ sinceDate: '2026-09-12',
696
+ removedIn: '21.14.0',
697
+ },
698
+ ];
699
+
700
+ /**
701
+ * Which writing direction a portaled overlay has to carry, if any.
702
+ *
703
+ * A dropdown panel or tooltip bubble is appended to `<body>`, which takes it out of whatever
704
+ * subtree it was opened from. `dir` is inherited, so an overlay opened inside an RTL region of
705
+ * an otherwise-LTR page would render LTR — text aligned the wrong way, the panel's own
706
+ * `inset-inline-*` resolving against the wrong side. Copying the nearest scoped `dir` onto the
707
+ * portal host fixes that, exactly as `scopedOverlayTheme` does for `data-theme`.
708
+ *
709
+ * **Only when the direction really is scoped.** When the nearest `[dir]` is the document
710
+ * element — the ordinary case, a whole RTL app — the overlay already inherits it through
711
+ * `<body>`, and re-stating it is noise on every panel in the app.
712
+ *
713
+ * Known limitation, shared with `scopedOverlayTheme`: a direction set through CSS
714
+ * (`direction: rtl` in a stylesheet) rather than the `dir` attribute is not detected. Reading
715
+ * `getComputedStyle().direction` would catch it, but that forces layout on every open for a
716
+ * case the HTML spec itself discourages — `dir` is the attribute browsers, form controls and
717
+ * assistive tech all key off.
718
+ */
719
+ function scopedOverlayDirection(directionSource, documentElement) {
720
+ const scoped = directionSource?.closest('[dir]');
721
+ if (!scoped || scoped === documentElement || scoped === documentElement.ownerDocument?.body) {
722
+ return null;
723
+ }
724
+ const dir = scoped.getAttribute('dir')?.trim().toLowerCase();
725
+ return dir === 'rtl' || dir === 'ltr' ? dir : null;
726
+ }
727
+
728
+ /**
729
+ * Which `data-theme` an overlay rendered into `<body>` has to carry, if any.
730
+ *
731
+ * A panel or tooltip bubble is appended to `<body>`, which puts it outside whatever subtree it
732
+ * was opened from. `data-theme` can be scoped to any subtree — several themes rendering side by
733
+ * side is a documented use — so an overlay opened inside one of those has to be told which theme
734
+ * it belongs to, or it silently picks up the document's instead.
735
+ *
736
+ * **But only when the theme really is scoped.** When the nearest themed ancestor is the document
737
+ * element, copying the attribute is not merely redundant, it is actively wrong: the overlay
738
+ * already inherits everything from `<html>` through `<body>`, and re-stating `data-theme` on it
739
+ * makes it match `theme.css`'s derived layer (`:root, [data-theme]`) *locally*. That re-declares
740
+ * every component token on the overlay itself, resolved against the plain preset palette — which
741
+ * discards anything set on `<html>` that is not part of that preset. Custom properties written
742
+ * inline on `:root` are the case that bites: a live theme editor sets them there, the page
743
+ * follows, and every portal keeps rendering the un-edited theme.
744
+ *
745
+ * So: return the theme only for a genuinely scoped ancestor, and let inheritance do the work
746
+ * otherwise.
747
+ *
748
+ * Known limitation, currently unreachable: a *scoped* theme that is itself being edited through
749
+ * inline custom properties would still lose those on the overlay, since only the attribute is
750
+ * carried across. Copying resolved values instead would mean reading ~1200 properties on every
751
+ * open, which is not worth it for a case nothing does yet.
752
+ */
753
+ function scopedOverlayTheme(themeSource, documentElement) {
754
+ const themedAncestor = themeSource?.closest('[data-theme]');
755
+ if (!themedAncestor || themedAncestor === documentElement)
756
+ return null;
757
+ return themedAncestor.getAttribute('data-theme');
758
+ }
759
+
760
+ /**
761
+ * Renders a template into `document.body` while keeping it wired to the declaring
762
+ * component: the view is stamped with that component's style encapsulation and reads
763
+ * its signals directly.
764
+ *
765
+ * That is the whole point of doing it this way rather than with a second, portal-only
766
+ * component: one `<ng-template>` can serve both the inline and the appended-to-body
767
+ * dropdown, so the option markup exists once and cannot drift between the two modes,
768
+ * and selection state needs no manual field-syncing to stay in step.
769
+ */
770
+ class GogDropdownOverlay {
771
+ appRef;
772
+ document;
773
+ hostEl = null;
774
+ viewRef = null;
775
+ constructor(appRef, document) {
776
+ this.appRef = appRef;
777
+ this.document = document;
778
+ }
779
+ /** The `<body>` child holding the rendered panel, or null while detached. */
780
+ get hostElement() {
781
+ return this.hostEl;
782
+ }
783
+ get isAttached() {
784
+ return this.viewRef !== null;
785
+ }
786
+ attach(template, themeSource = null) {
787
+ this.detach();
788
+ this.hostEl = this.document.createElement('div');
789
+ this.hostEl.classList.add('gog-overlay-host');
790
+ // Only for a genuinely scoped theme — see `scopedOverlayTheme` for why copying it when the
791
+ // theme sits on `<html>` breaks live-edited themes.
792
+ const theme = scopedOverlayTheme(themeSource, this.document.documentElement);
793
+ if (theme) {
794
+ this.hostEl.setAttribute('data-theme', theme);
795
+ }
796
+ // Same reasoning for the writing direction — see `scopedOverlayDirection`.
797
+ const direction = scopedOverlayDirection(themeSource, this.document.documentElement);
798
+ if (direction) {
799
+ this.hostEl.setAttribute('dir', direction);
800
+ }
801
+ this.document.body.appendChild(this.hostEl);
802
+ this.viewRef = template.createEmbeddedView({});
803
+ // Attached as its own change-detection root so signal reads inside the panel still
804
+ // mark it dirty even though its DOM no longer sits under the declaring component.
805
+ this.appRef.attachView(this.viewRef);
806
+ for (const node of this.viewRef.rootNodes) {
807
+ this.hostEl.appendChild(node);
808
+ }
809
+ this.viewRef.detectChanges();
810
+ }
811
+ detach() {
812
+ if (this.viewRef) {
813
+ this.appRef.detachView(this.viewRef);
814
+ this.viewRef.destroy();
815
+ this.viewRef = null;
816
+ }
817
+ this.hostEl?.remove();
818
+ this.hostEl = null;
819
+ }
820
+ }
821
+
822
+ const DEFAULT_GAP$1 = 8;
823
+ const DEFAULT_VIEWPORT_PADDING$1 = 8;
824
+ function resolveDropdownDirection(direction, triggerRect, panelHeight, viewportHeight, gap = DEFAULT_GAP$1, viewportPadding = DEFAULT_VIEWPORT_PADDING$1) {
825
+ if (direction !== 'auto') {
826
+ return direction;
827
+ }
828
+ const spaceAbove = Math.max(0, triggerRect.top - gap - viewportPadding);
829
+ const spaceBelow = Math.max(0, viewportHeight - triggerRect.bottom - gap - viewportPadding);
830
+ if (spaceBelow >= panelHeight && spaceAbove < panelHeight) {
831
+ return 'down';
832
+ }
833
+ if (spaceAbove >= panelHeight && spaceBelow < panelHeight) {
834
+ return 'up';
835
+ }
836
+ return spaceBelow >= spaceAbove ? 'down' : 'up';
837
+ }
838
+ function resolveDropdownPlacement(direction, triggerRect, panelHeight, viewportHeight, gap = DEFAULT_GAP$1, viewportPadding = DEFAULT_VIEWPORT_PADDING$1) {
839
+ const resolvedDirection = resolveDropdownDirection(direction, triggerRect, panelHeight, viewportHeight, gap, viewportPadding);
840
+ if (resolvedDirection === 'up') {
841
+ const availableSpace = Math.max(0, triggerRect.top - gap - viewportPadding);
842
+ const actualHeight = Math.min(panelHeight, availableSpace || panelHeight);
843
+ return {
844
+ direction: resolvedDirection,
845
+ top: Math.max(viewportPadding, triggerRect.top - gap - actualHeight),
846
+ left: triggerRect.left,
847
+ width: triggerRect.width,
848
+ // Must match actualHeight, not availableSpace: `top` above was computed assuming
849
+ // the panel is actualHeight tall, so this is what goes into the panel's CSS
850
+ // max-height too. Returning the full availableSpace here let real content taller
851
+ // than actualHeight (but still under availableSpace) render past its computed
852
+ // top and cover the trigger it's supposed to sit above.
853
+ maxHeight: actualHeight,
854
+ };
855
+ }
856
+ const maxHeight = Math.max(0, viewportHeight - triggerRect.bottom - gap - viewportPadding);
857
+ return {
858
+ direction: resolvedDirection,
859
+ top: triggerRect.bottom + gap,
860
+ left: triggerRect.left,
861
+ width: triggerRect.width,
862
+ maxHeight,
863
+ };
864
+ }
865
+ /**
866
+ * Resolves a CSS length to pixels for the placement math. Only `px`, `%` and `vh` can be
867
+ * resolved without laying out the DOM — `%` and `vh` are taken relative to the viewport
868
+ * height, which is exactly what a `position: fixed` panel's height resolves against in CSS.
869
+ * Any other unit (`rem`, `em`, `auto`, a bare number, …) returns null: the caller falls back
870
+ * to its own estimate for the up/down decision, while the original string is still written
871
+ * to the panel's `style.max-height` as-is — only the direction heuristic loses precision.
872
+ */
873
+ function resolveCssLengthPx(value, viewportHeight) {
874
+ const match = /^(-?[\d.]+)(px|%|vh)$/.exec(value.trim());
875
+ if (!match)
876
+ return null;
877
+ const amount = Number.parseFloat(match[1]);
878
+ if (!Number.isFinite(amount))
879
+ return null;
880
+ return match[2] === 'px' ? amount : (amount / 100) * viewportHeight;
881
+ }
882
+
883
+ /**
884
+ * Off, so the ripple is purely additive: adding it to the library changed the appearance of
885
+ * nothing until an app asks for it. Flipping this default would change how every button in every
886
+ * consuming app looks, which is a release of its own, not a line in a feature commit.
887
+ */
888
+ const DEFAULT_RIPPLE = false;
889
+ /**
890
+ * Shared "does this control ripple" state, resolved the usual way — instance input, then
891
+ * `GOG_CONFIG.ripple.enabled`, then off.
892
+ *
893
+ * A plain function rather than a class (`GogClearableState`'s shape) because there is exactly one
894
+ * derived value and nothing to hold: every component that ripples writes the same two lines, and
895
+ * this is what keeps the precedence identical across all nine of them instead of nine separate
896
+ * `??` chains that can drift.
897
+ *
898
+ * The component then binds the *negation* onto its own inner element:
899
+ *
900
+ * ```html
901
+ * <button class="gog-btn" gogRipple [rippleDisabled]="!rippleEnabled()">
902
+ * ```
903
+ *
904
+ * `[gogRipple]` is always applied rather than toggled, because a directive cannot be added and
905
+ * removed by a binding — and it costs nothing while disabled: `GogRippleDirective` attaches no
906
+ * event listeners at all until `rippleDisabled` goes false.
907
+ */
908
+ function resolveRipple(rippleInput, config) {
909
+ return computed(() => resolveConfigured(rippleInput(), config.ripple?.enabled, DEFAULT_RIPPLE));
910
+ }
911
+
912
+ /**
913
+ * Shared error-visibility state for the library's form controls. A plain class rather than
914
+ * a base class or directive so it drops into components that can't share a base class
915
+ * (`gog-inputfield`, `gog-slider`) as well as ones that already do (`GogDropdownBase`).
916
+ * Each consumer owns its own instance and wires `check()` into its own `ngDoCheck`.
917
+ */
918
+ class GogErrorState {
919
+ errorMessage;
920
+ errorDisplay;
921
+ ngControl;
922
+ controlTouched = signal(false, ...(ngDevMode ? [{ debugName: "controlTouched" }] : /* istanbul ignore next */ []));
923
+ controlInvalid = signal(false, ...(ngDevMode ? [{ debugName: "controlInvalid" }] : /* istanbul ignore next */ []));
924
+ constructor(errorMessage, errorDisplay, ngControl) {
925
+ this.errorMessage = errorMessage;
926
+ this.errorDisplay = errorDisplay;
927
+ this.ngControl = ngControl;
928
+ }
929
+ hasError = computed(() => {
930
+ if (this.errorDisplay() === 'auto' && this.ngControl) {
931
+ return this.controlTouched() && this.controlInvalid();
932
+ }
933
+ return !!this.errorMessage();
934
+ }, ...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
935
+ visibleError = computed(() => (this.hasError() ? this.errorMessage() : ''), ...(ngDevMode ? [{ debugName: "visibleError" }] : /* istanbul ignore next */ []));
936
+ /**
937
+ * `NgControl` exposes validity as plain properties, and has no change notification for
938
+ * `touched` at all — `statusChanges` fires only on validity transitions, never on
939
+ * `markAsTouched()` — while its `control` does not exist yet during construction, so
940
+ * subscribing early would miss it regardless. Mirroring on each check sidesteps both;
941
+ * re-setting a signal to its current value notifies nothing, so the common case costs two
942
+ * comparisons.
943
+ */
944
+ check() {
945
+ const control = this.ngControl?.control;
946
+ if (!control)
947
+ return;
948
+ this.controlTouched.set(control.touched);
949
+ this.controlInvalid.set(control.invalid);
950
+ }
951
+ }
952
+
953
+ /** Resolves `field` against `source`, following dot-paths (e.g. `"address.city"`). */
954
+ function getByPath(source, field) {
955
+ if (!field.includes('.'))
956
+ return source?.[field];
957
+ let value = source;
958
+ for (const key of field.split('.')) {
959
+ if (value == null)
960
+ return undefined;
961
+ value = value[key];
962
+ }
963
+ return value;
964
+ }
965
+ /** Applies an accessor — a property path or a function — to one option. */
966
+ function readOption(option, accessor) {
967
+ return typeof accessor === 'function'
968
+ ? accessor(option)
969
+ : getByPath(option, accessor);
970
+ }
971
+ /**
972
+ * Whether two resolved option values refer to the same option.
973
+ *
974
+ * Primitives are compared as strings, so a `formControl` holding `'1'` still matches an option
975
+ * whose value is the number `1` — the library behaved this way before option values could be
976
+ * anything, and forms routinely stringify. Objects are compared by identity instead: coercing
977
+ * them would make every plain object equal to every other (`"[object Object]"`).
978
+ */
979
+ function isSameOptionValue(a, b) {
980
+ if (a === b)
981
+ return true;
982
+ if (a == null || b == null)
983
+ return false;
984
+ if (typeof a === 'object' || typeof b === 'object')
985
+ return false;
986
+ return String(a) === String(b);
987
+ }
988
+
989
+ const DEFAULT_VARIANT = 'none';
990
+ const DEFAULT_SHOW_PLACEHOLDER = false;
991
+ /**
992
+ * Shared float-label state for the library's field-style controls.
993
+ *
994
+ * A plain class rather than a base class or directive, for the same reason as `GogErrorState`:
995
+ * it has to serve `gog-inputfield` and `gog-textarea`, which share no base class, as well as
996
+ * `GogDropdownBase`, which is one. Each consumer owns an instance and exposes whichever
997
+ * members its template needs.
998
+ *
999
+ * The one thing each control must supply itself is `hasValue` — "this field has content" means
1000
+ * something different everywhere (a non-empty string, a non-null selection, a non-empty
1001
+ * selection array), which is exactly why this is composed in rather than implemented as a
1002
+ * directive sitting outside the component.
1003
+ */
1004
+ class GogFloatLabelState {
1005
+ variantInput;
1006
+ showPlaceholderInput;
1007
+ placeholder;
1008
+ isFocused;
1009
+ hasValue;
1010
+ config;
1011
+ /**
1012
+ * @param variantInput the control's own `floatLabel` input (`undefined` when unset)
1013
+ * @param showPlaceholderInput the control's own `floatLabelShowPlaceholder` input
1014
+ * @param placeholder the control's `placeholder` input
1015
+ * @param isFocused whether the control currently has focus
1016
+ * @param hasValue whether the control has a value — see the class note
1017
+ * @param config the injected `GOG_CONFIG`
1018
+ */
1019
+ constructor(variantInput, showPlaceholderInput, placeholder, isFocused, hasValue, config) {
1020
+ this.variantInput = variantInput;
1021
+ this.showPlaceholderInput = showPlaceholderInput;
1022
+ this.placeholder = placeholder;
1023
+ this.isFocused = isFocused;
1024
+ this.hasValue = hasValue;
1025
+ this.config = config;
1026
+ }
1027
+ /** The resolved variant: instance input, else `GOG_CONFIG.floatLabel.variant`, else `'none'`. */
1028
+ variant = computed(() => resolveConfigured(this.variantInput(), this.config.floatLabel?.variant, DEFAULT_VARIANT), ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
1029
+ showPlaceholder = computed(() => resolveConfigured(this.showPlaceholderInput(), this.config.floatLabel?.showPlaceholder, DEFAULT_SHOW_PLACEHOLDER), ...(ngDevMode ? [{ debugName: "showPlaceholder" }] : /* istanbul ignore next */ []));
1030
+ /** Whether a float label is in effect at all — `false` keeps the static label-above layout. */
1031
+ isActive = computed(() => this.variant() !== 'none', ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
1032
+ /** Whether the label is at its floated target rather than resting like a placeholder. */
1033
+ isFloated = computed(() => this.isFocused() || this.hasValue(), ...(ngDevMode ? [{ debugName: "isFloated" }] : /* istanbul ignore next */ []));
1034
+ /**
1035
+ * The placeholder the control should actually render. While a float label is active the
1036
+ * resting label already occupies that space, so the placeholder stays hidden unless the
1037
+ * consumer opted into `floatLabelShowPlaceholder` — and even then only once the label has
1038
+ * floated out of the way.
1039
+ */
1040
+ effectivePlaceholder = computed(() => {
1041
+ if (!this.isActive())
1042
+ return this.placeholder();
1043
+ if (!this.showPlaceholder())
1044
+ return '';
1045
+ return this.isFloated() ? this.placeholder() : '';
1046
+ }, ...(ngDevMode ? [{ debugName: "effectivePlaceholder" }] : /* istanbul ignore next */ []));
1047
+ }
1048
+
1049
+ const VERTICAL_ARROWS = ['ArrowDown', 'ArrowUp'];
1050
+ const HORIZONTAL_ARROWS = ['ArrowRight', 'ArrowLeft'];
1051
+ const EDGE_KEYS = ['Home', 'End'];
1052
+ /** Which arrow pair drives a given orientation. `Home`/`End` apply to both. */
1053
+ function arrowsFor(orientation) {
1054
+ return orientation === 'horizontal' ? HORIZONTAL_ARROWS : VERTICAL_ARROWS;
1055
+ }
1056
+ /**
1057
+ * Whether `key` navigates a roving-tabindex list laid out along `orientation`.
1058
+ *
1059
+ * The orientation filter is the point: a horizontal tablist must leave `ArrowDown` alone so
1060
+ * the page still scrolls, and a vertical listbox must not steal `ArrowRight` from a caret
1061
+ * inside a text field. `Home`/`End` are accepted for both.
1062
+ */
1063
+ function isRovingFocusKey(key, orientation = 'vertical') {
1064
+ return arrowsFor(orientation).includes(key) || EDGE_KEYS.includes(key);
1065
+ }
1066
+ /** Whether `key` moves backwards (up / left) rather than forwards. */
1067
+ function isBackwards(key) {
1068
+ return key === 'ArrowUp' || key === 'ArrowLeft';
1069
+ }
1070
+ /**
1071
+ * The index `key` moves to from `currentIndex`, wrapping at both ends.
1072
+ *
1073
+ * `isEnabled` lets a caller keep disabled items in the list — which it must, since they still
1074
+ * occupy a position in the DOM — while navigation steps over them. Without it every index is a
1075
+ * target, which is the behaviour the dropdowns rely on (they pre-filter their own list).
1076
+ * Returns `currentIndex` when nothing else is reachable, so a group of one enabled item cannot
1077
+ * spin.
1078
+ */
1079
+ function nextRovingFocusIndex(key, currentIndex, count, isEnabled) {
1080
+ if (count === 0)
1081
+ return currentIndex;
1082
+ const enabled = isEnabled ?? (() => true);
1083
+ const lastIndex = count - 1;
1084
+ // Home/End mean the first/last *reachable* item, not the first/last element.
1085
+ if (key === 'Home' || key === 'End') {
1086
+ const order = key === 'Home'
1087
+ ? Array.from({ length: count }, (_, i) => i)
1088
+ : Array.from({ length: count }, (_, i) => lastIndex - i);
1089
+ return order.find(enabled) ?? currentIndex;
1090
+ }
1091
+ const step = isBackwards(key) ? -1 : 1;
1092
+ // At most `count` hops: enough to come back round to where we started, never more.
1093
+ for (let hop = 1; hop <= count; hop++) {
1094
+ const candidate = (currentIndex + step * hop + count * hop) % count;
1095
+ if (enabled(candidate))
1096
+ return candidate;
1097
+ }
1098
+ return currentIndex;
1099
+ }
1100
+ /**
1101
+ * Arrow/Home/End navigation across a roving-tabindex-style list of focusable elements.
1102
+ * `event.currentTarget` must be one of `items`. Returns false (and leaves the event
1103
+ * untouched) for any other key, or when `items` doesn't contain the current target,
1104
+ * so callers can safely invoke this unconditionally from a keydown handler.
1105
+ *
1106
+ * Defaults to a vertical list with every item reachable — the shape the option lists and the
1107
+ * accordion were written against — so existing callers need no options object.
1108
+ */
1109
+ function handleRovingFocusKeydown(event, items, options = {}) {
1110
+ const orientation = options.orientation ?? 'vertical';
1111
+ if (!isRovingFocusKey(event.key, orientation) || items.length === 0) {
1112
+ return false;
1113
+ }
1114
+ const current = event.currentTarget;
1115
+ const index = items.indexOf(current);
1116
+ if (index === -1) {
1117
+ return false;
1118
+ }
1119
+ const { isDisabled } = options;
1120
+ const isEnabled = isDisabled
1121
+ ? (candidate) => !isDisabled(items[candidate], candidate)
1122
+ : undefined;
1123
+ // Swallowed even when there is nowhere else to go: an open listbox or a focused tablist owns
1124
+ // its arrow keys, and letting one through would scroll the page out from under the widget.
1125
+ event.preventDefault();
1126
+ const nextIndex = nextRovingFocusIndex(event.key, index, items.length, isEnabled);
1127
+ if (nextIndex !== index) {
1128
+ items[nextIndex]?.focus();
1129
+ }
1130
+ return true;
1131
+ }
1132
+
1133
+ const DEFAULT_OVERSCAN$1 = 4;
1134
+ /**
1135
+ * The arithmetic behind a windowed list, and nothing else.
1136
+ *
1137
+ * **No DOM, no template, no scroll listener.** The component that uses this owns its scroller —
1138
+ * the three dropdowns already own theirs (a `gog-scroll` inside the panel) and `gog-table` owns a
1139
+ * different one — so a primitive that grabbed an element would have to be told which, and would
1140
+ * still not know when that element moved. It reads signals and returns numbers; everything that
1141
+ * touches the page stays in the component.
1142
+ *
1143
+ * That is the same division `GogRippleController` made, for the reason `docs/ripple.md` records:
1144
+ * the engine lives beside the components rather than inside a directive, because the things that
1145
+ * need it cannot all reach a directive through `hostDirectives`.
1146
+ *
1147
+ * ```ts
1148
+ * private readonly window = new GogVirtualWindow({
1149
+ * count: computed(() => this.visibleOptions().length),
1150
+ * rowHeight: this.measuredRowHeight,
1151
+ * viewportHeight: this.panelHeight,
1152
+ * scrollTop: this.panelScrollTop,
1153
+ * });
1154
+ * // window.range() -> { start, end }
1155
+ * // window.padBefore() / window.padEnd() -> px of filler above and below the rendered slice
1156
+ * ```
1157
+ *
1158
+ * **Why padding rather than absolute positioning.** A spacer above and below the rendered rows
1159
+ * keeps the list in normal flow, so the rows stay `display: flex` children of the same container
1160
+ * and every selector, gap and `:last-child` a component already relies on keeps working. Absolute
1161
+ * positioning each row is the other common shape and it changes what the component's own CSS
1162
+ * means, which is a large price for a list that is one column wide.
1163
+ *
1164
+ * @see docs/virtualization.md — the plan, its measurements, and what still has to be got right in
1165
+ * the components that adopt this (ARIA counts, keyboard reach, panel height, filter resets).
1166
+ */
1167
+ class GogVirtualWindow {
1168
+ inputs;
1169
+ overscan;
1170
+ constructor(inputs) {
1171
+ this.inputs = inputs;
1172
+ this.overscan = Math.max(0, Math.trunc(inputs.overscan ?? DEFAULT_OVERSCAN$1));
1173
+ }
1174
+ /**
1175
+ * The total height the list would have if every row were rendered. What the scroller needs to
1176
+ * believe so its thumb is the right size and `scrollTop` spans the whole list.
1177
+ */
1178
+ totalHeight = computed(() => {
1179
+ const rowHeight = this.safeRowHeight();
1180
+ return rowHeight === 0 ? 0 : this.safeCount() * rowHeight;
1181
+ }, ...(ngDevMode ? [{ debugName: "totalHeight" }] : /* istanbul ignore next */ []));
1182
+ /**
1183
+ * The slice worth rendering.
1184
+ *
1185
+ * **Degrades to the whole list rather than to nothing.** A row height of zero is what a caller
1186
+ * has before it has measured anything, and a window computed from it would be `{0, 0}` — an
1187
+ * empty panel that looks like a bug and hides the data. Rendering everything is the pre-window
1188
+ * behaviour, which is slow and correct; that is the right way round for a fallback.
1189
+ */
1190
+ range = computed(() => {
1191
+ const count = this.safeCount();
1192
+ const rowHeight = this.safeRowHeight();
1193
+ const viewportHeight = this.safeViewportHeight();
1194
+ if (count === 0)
1195
+ return { start: 0, end: 0 };
1196
+ if (rowHeight === 0 || viewportHeight === 0)
1197
+ return { start: 0, end: count };
1198
+ const first = Math.floor(this.safeScrollTop() / rowHeight);
1199
+ // `ceil` on the viewport, then one more: a viewport that is not an exact multiple of the row
1200
+ // height always straddles one extra row, and scrolling by a fraction of a row straddles
1201
+ // another. Getting this wrong leaves a sliver of blank at the bottom edge on every scroll.
1202
+ const visible = Math.ceil(viewportHeight / rowHeight) + 1;
1203
+ const start = Math.max(0, first - this.overscan);
1204
+ const end = Math.min(count, first + visible + this.overscan);
1205
+ return { start, end };
1206
+ }, ...(ngDevMode ? [{ debugName: "range" }] : /* istanbul ignore next */ []));
1207
+ /** Filler above the rendered slice, in px, so the rows sit where their indices say. */
1208
+ padBefore = computed(() => this.range().start * this.safeRowHeight(), ...(ngDevMode ? [{ debugName: "padBefore" }] : /* istanbul ignore next */ []));
1209
+ /** Filler below, in px. Derived from the total so rounding cannot leave a gap. */
1210
+ padAfter = computed(() => {
1211
+ const rendered = this.range().end * this.safeRowHeight();
1212
+ return Math.max(0, this.totalHeight() - rendered);
1213
+ }, ...(ngDevMode ? [{ debugName: "padAfter" }] : /* istanbul ignore next */ []));
1214
+ /**
1215
+ * Where the scroller has to be for `index` to be fully visible, or `null` if it already is.
1216
+ *
1217
+ * This is what keyboard navigation needs: arrowing to a row outside the window is a scroll
1218
+ * first and a focus move second, because the element does not exist until the scroll has
1219
+ * re-rendered the slice. Returning `null` rather than the current position lets a caller skip
1220
+ * the scroll entirely, which matters because scrolling when nothing needs to move cancels a
1221
+ * user's own in-progress scroll in some browsers.
1222
+ */
1223
+ scrollOffsetFor(index) {
1224
+ const count = this.safeCount();
1225
+ const rowHeight = this.safeRowHeight();
1226
+ const viewportHeight = this.safeViewportHeight();
1227
+ if (count === 0 || rowHeight === 0 || viewportHeight === 0)
1228
+ return null;
1229
+ const clamped = Math.min(Math.max(0, Math.trunc(index)), count - 1);
1230
+ const rowTop = clamped * rowHeight;
1231
+ const rowBottom = rowTop + rowHeight;
1232
+ const scrollTop = this.safeScrollTop();
1233
+ if (rowTop < scrollTop)
1234
+ return rowTop;
1235
+ if (rowBottom > scrollTop + viewportHeight)
1236
+ return rowBottom - viewportHeight;
1237
+ return null;
1238
+ }
1239
+ /** Guards against the shapes a caller can legitimately be in before it has measured anything. */
1240
+ safeCount() {
1241
+ return Math.max(0, Math.trunc(this.inputs.count()));
1242
+ }
1243
+ safeRowHeight() {
1244
+ const value = this.inputs.rowHeight();
1245
+ return Number.isFinite(value) && value > 0 ? value : 0;
1246
+ }
1247
+ safeViewportHeight() {
1248
+ const value = this.inputs.viewportHeight();
1249
+ return Number.isFinite(value) && value > 0 ? value : 0;
1250
+ }
1251
+ safeScrollTop() {
1252
+ const value = this.inputs.scrollTop();
1253
+ if (!Number.isFinite(value) || value <= 0)
1254
+ return 0;
1255
+ // A scroller can report a scrollTop past its own end during an elastic/overscroll bounce.
1256
+ // Clamping keeps `range()` inside the list instead of returning an empty slice at the moment
1257
+ // a user flicks to the bottom.
1258
+ return Math.min(value, Math.max(0, this.totalHeight() - this.safeViewportHeight()));
1259
+ }
1260
+ }
1261
+
1262
+ /**
1263
+ * Custom markup for the trigger's chevron, on `gog-select` and `gog-multiselect`:
1264
+ *
1265
+ * ```html
1266
+ * <gog-select [options]="opts">
1267
+ * <ng-template gogDropdownChevron><gog-icon name="sort" /></ng-template>
1268
+ * </gog-select>
1269
+ * ```
1270
+ */
1271
+ class GogDropdownChevronDirective {
1272
+ templateRef = inject(TemplateRef);
1273
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: GogDropdownChevronDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1274
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.17", type: GogDropdownChevronDirective, isStandalone: true, selector: "[gogDropdownChevron]", ngImport: i0 });
1275
+ }
1276
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: GogDropdownChevronDirective, decorators: [{
1277
+ type: Directive,
1278
+ args: [{ selector: '[gogDropdownChevron]' }]
1279
+ }] });
1280
+ /**
1281
+ * Custom markup for one option row, on `gog-select` and `gog-multiselect`:
1282
+ *
1283
+ * ```html
1284
+ * <gog-select [options]="users" optionLabel="fullName" optionValue="id" [(value)]="userId">
1285
+ * <ng-template gogDropdownOption let-user let-selected="selected">
1286
+ * <img [src]="user.avatar" alt="" /> {{ user.fullName }}
1287
+ * </ng-template>
1288
+ * </gog-select>
1289
+ * ```
1290
+ */
1291
+ class GogDropdownOptionDirective {
1292
+ templateRef = inject(TemplateRef);
1293
+ static ngTemplateContextGuard(_dir,
1294
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- only used as a type guard
1295
+ ctx) {
1296
+ return true;
1297
+ }
1298
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: GogDropdownOptionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1299
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.17", type: GogDropdownOptionDirective, isStandalone: true, selector: "[gogDropdownOption]", ngImport: i0 });
1300
+ }
1301
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: GogDropdownOptionDirective, decorators: [{
1302
+ type: Directive,
1303
+ args: [{ selector: '[gogDropdownOption]' }]
1304
+ }] });
1305
+ /** Gap between the trigger and the panel. */
1306
+ const PANEL_GAP = 2;
1307
+ /** Smallest allowed gap between the panel and the viewport edge. */
1308
+ const VIEWPORT_PADDING = 8;
1309
+ /** Used only if a subclass's `panelMaxHeightToken` isn't declared anywhere in the cascade. */
1310
+ const FALLBACK_MAX_PANEL_HEIGHT = 260;
1311
+ /** Used only if a subclass's `optionHeightToken` isn't declared anywhere in the cascade. */
1312
+ const FALLBACK_OPTION_HEIGHT = 40;
1313
+ /** Must match the `var(--gog-dropdown-z, …)` fallback in the component stylesheets. */
1314
+ const DEFAULT_PANEL_Z_INDEX = 300;
1315
+ /** Built-in defaults, used when neither the instance input nor `GOG_CONFIG` supplies one. */
1316
+ const DEFAULT_SIZE = 'md';
1317
+ const DEFAULT_ERROR_DISPLAY = 'manual';
1318
+ const DEFAULT_APPEND_TO_BODY = false;
1319
+ const DEFAULT_DROPDOWN_DIRECTION = 'auto';
1320
+ const DEFAULT_FILTER = false;
1321
+ const DEFAULT_FILTER_POSITION = 'top';
1322
+ const DEFAULT_CLEAR_SELECTION_LABEL = 'Clear selection';
1323
+ /**
1324
+ * Shared behaviour for the listbox-style controls: open/close, placement, the
1325
+ * append-to-body overlay, click-outside, keyboard navigation and `ControlValueAccessor`
1326
+ * plumbing. Subclasses supply only what actually differs — the value type, the panel
1327
+ * markup, and the BEM class names used to find the trigger and the options.
1328
+ *
1329
+ * Exported because it appears in the public type signatures of the components that
1330
+ * extend it; it is not meant to be used or subclassed by consumers.
1331
+ */
1332
+ class GogDropdownBase {
1333
+ static nextUid = 0;
1334
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1335
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
1336
+ placeholder = input('Select...', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
1337
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
1338
+ /**
1339
+ * How to read an option's visible text — a property path (`'name'`, `'profile.title'`) or a
1340
+ * function. Defaults to `'name'`, matching `GogDropdownOption`.
1341
+ */
1342
+ optionLabel = input('name', ...(ngDevMode ? [{ debugName: "optionLabel" }] : /* istanbul ignore next */ []));
1343
+ /**
1344
+ * How to read the value this control emits for an option. A property path or a function, or
1345
+ * `null` to emit **the option object itself** — which is what lets a consumer keep their own
1346
+ * DTO end to end instead of mapping ids back to objects on every change.
1347
+ *
1348
+ * Defaults to `'id'`, matching `GogDropdownOption`, so existing code is unaffected.
1349
+ */
1350
+ optionValue = input('id', ...(ngDevMode ? [{ debugName: "optionValue" }] : /* istanbul ignore next */ []));
1351
+ /** How to read whether an option is disabled. Defaults to `'disabled'`. */
1352
+ optionDisabled = input('disabled', ...(ngDevMode ? [{ debugName: "optionDisabled" }] : /* istanbul ignore next */ []));
1353
+ /**
1354
+ * Whether to offer a clear button once something is selected. Unset, falls back to
1355
+ * `GOG_CONFIG.control.clearable`, then to the control's own default.
1356
+ */
1357
+ clearable = input(undefined, ...(ngDevMode ? [{ debugName: "clearable" }] : /* istanbul ignore next */ []));
1358
+ /**
1359
+ * Accessible name for the clear button. Unset, falls back to
1360
+ * `GOG_CONFIG.labels.clearSelection`, then to `'Clear selection'`.
1361
+ */
1362
+ clearAriaLabel = input(undefined, ...(ngDevMode ? [{ debugName: "clearAriaLabel" }] : /* istanbul ignore next */ []));
1363
+ /** Instance input → `GOG_CONFIG.labels` → the built-in English default. */
1364
+ resolvedClearLabel = computed(() => resolveConfigured(this.clearAriaLabel(), this.globalConfig.labels?.clearSelection, DEFAULT_CLEAR_SELECTION_LABEL), ...(ngDevMode ? [{ debugName: "resolvedClearLabel" }] : /* istanbul ignore next */ []));
1365
+ /**
1366
+ * Smallest width the trigger may shrink to, as any CSS length. Only meaningful with
1367
+ * `[fullWidth]="false"`, where the trigger otherwise sizes to whatever is currently selected
1368
+ * and can collapse to almost nothing on a short option. Left unset it falls back to the
1369
+ * `--gog-{select,multiselect}-min-width` token.
1370
+ */
1371
+ minWidth = input(null, ...(ngDevMode ? [{ debugName: "minWidth" }] : /* istanbul ignore next */ []));
1372
+ /**
1373
+ * Whether the panel shows a search box that narrows the option list. Unset, falls back to
1374
+ * `GOG_CONFIG.dropdown.filter`, then to `false`.
1375
+ */
1376
+ filter = input(undefined, ...(ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []));
1377
+ filterPlaceholder = input('Search...', ...(ngDevMode ? [{ debugName: "filterPlaceholder" }] : /* istanbul ignore next */ []));
1378
+ /**
1379
+ * Which end of the panel the search box sticks to. Named to match `gog-multiselect`'s
1380
+ * `controlsPosition`, which is the same idea for its select-all row. Unset, falls back to
1381
+ * `GOG_CONFIG.dropdown.filterPosition`, then to `'top'`.
1382
+ */
1383
+ filterPosition = input(undefined, ...(ngDevMode ? [{ debugName: "filterPosition" }] : /* istanbul ignore next */ []));
1384
+ /** Shown in place of the list when the query matches nothing. */
1385
+ filterEmptyMessage = input('No matches', ...(ngDevMode ? [{ debugName: "filterEmptyMessage" }] : /* istanbul ignore next */ []));
1386
+ /**
1387
+ * How an option is matched against the query. Left null, the resolved `optionLabel` is
1388
+ * matched case-insensitively as a substring — pass a function to search other fields, match
1389
+ * on a prefix, or plug in your own fuzzy matcher.
1390
+ */
1391
+ filterMatch = input(null, ...(ngDevMode ? [{ debugName: "filterMatch" }] : /* istanbul ignore next */ []));
1392
+ errorMessage = input('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
1393
+ /**
1394
+ * See `GogErrorDisplay`. Unset, falls back to `GOG_CONFIG.control.errorDisplay`, then to
1395
+ * `'manual'` — matching every other control in the library.
1396
+ */
1397
+ errorDisplay = input(undefined, ...(ngDevMode ? [{ debugName: "errorDisplay" }] : /* istanbul ignore next */ []));
1398
+ /** Unset, falls back to `GOG_CONFIG.control.size`, then to `'md'`. */
1399
+ size = input(undefined, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
1400
+ /** Unset, falls back to `GOG_CONFIG.dropdown.direction`, then to `'auto'`. */
1401
+ dropdownDirection = input(undefined, ...(ngDevMode ? [{ debugName: "dropdownDirection" }] : /* istanbul ignore next */ []));
1402
+ /**
1403
+ * Explicit stacking order for the panel. Left unset the panel falls back to the
1404
+ * stylesheet's `--gog-dropdown-z`, so the default lives in CSS where a consumer can
1405
+ * retheme it, rather than being baked into the component.
1406
+ */
1407
+ dropdownZIndex = input(null, ...(ngDevMode ? [{ debugName: "dropdownZIndex" }] : /* istanbul ignore next */ []));
1408
+ /**
1409
+ * Fixed panel width as any CSS length (`'320px'`, `'40ch'`, `'100%'`, …), applied only
1410
+ * when `appendToBody` is set. Left unset the panel matches the trigger's width, same as
1411
+ * the inline panel already does via CSS.
1412
+ */
1413
+ dropdownWidth = input(null, ...(ngDevMode ? [{ debugName: "dropdownWidth" }] : /* istanbul ignore next */ []));
1414
+ /**
1415
+ * Fixed panel max-height as any CSS length, applied only when `appendToBody` is set.
1416
+ * `px`, `%` and `vh` also feed the up/down placement math (see `resolveCssLengthPx`); any
1417
+ * other unit still renders correctly but is invisible to that heuristic, so the panel may
1418
+ * pick the "wrong" side close to a viewport edge. Left unset the panel keeps the existing
1419
+ * viewport-derived, auto-flipping height.
1420
+ */
1421
+ dropdownMaxHeight = input(null, ...(ngDevMode ? [{ debugName: "dropdownMaxHeight" }] : /* istanbul ignore next */ []));
1422
+ /** Unset, falls back to `GOG_CONFIG.dropdown.appendToBody`, then to `false`. */
1423
+ appendToBody = input(undefined, ...(ngDevMode ? [{ debugName: "appendToBody" }] : /* istanbul ignore next */ []));
1424
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
1425
+ /** Projected `gogDropdownChevron` template, replacing the built-in chevron. */
1426
+ chevronSlot = contentChild(GogDropdownChevronDirective, ...(ngDevMode ? [{ debugName: "chevronSlot" }] : /* istanbul ignore next */ []));
1427
+ /**
1428
+ * Full width of the container by default, matching every other field-style control.
1429
+ * Set to `false` to shrink the trigger to fit its selected label instead.
1430
+ */
1431
+ fullWidth = input(true, ...(ngDevMode ? [{ debugName: "fullWidth" }] : /* istanbul ignore next */ []));
1432
+ /** Unset, falls back to `GOG_CONFIG.floatLabel.variant`, then to `'none'` (off). */
1433
+ floatLabel = input(undefined, ...(ngDevMode ? [{ debugName: "floatLabel" }] : /* istanbul ignore next */ []));
1434
+ /** Unset, falls back to `GOG_CONFIG.floatLabel.showPlaceholder`, then to `false`. */
1435
+ floatLabelShowPlaceholder = input(undefined, ...(ngDevMode ? [{ debugName: "floatLabelShowPlaceholder" }] : /* istanbul ignore next */ []));
1436
+ /**
1437
+ * Press ripple on the panel's options. Unset, falls back to `GOG_CONFIG.ripple.enabled`, then
1438
+ * to `false`. The trigger itself never ripples — it is a field, not a button.
1439
+ */
1440
+ ripple = input(undefined, ...(ngDevMode ? [{ debugName: "ripple" }] : /* istanbul ignore next */ []));
1441
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
1442
+ /** Projected `gogDropdownOption` template, if the consumer supplied one. */
1443
+ optionSlot = contentChild(GogDropdownOptionDirective, ...(ngDevMode ? [{ debugName: "optionSlot" }] : /* istanbul ignore next */ []));
1444
+ /** An option's visible text, via `optionLabel`. */
1445
+ labelOf(option) {
1446
+ return readOption(option, this.optionLabel());
1447
+ }
1448
+ /**
1449
+ * The value this control emits for an option, via `optionValue` — or the option object
1450
+ * itself when `optionValue` is `null`.
1451
+ */
1452
+ valueOf(option) {
1453
+ const accessor = this.optionValue();
1454
+ return accessor === null ? option : readOption(option, accessor);
1455
+ }
1456
+ /** Whether an option is disabled, via `optionDisabled`. Anything falsy counts as enabled. */
1457
+ isOptionDisabled(option) {
1458
+ return !!readOption(option, this.optionDisabled());
1459
+ }
1460
+ /** Context for a projected `gogDropdownOption` row. */
1461
+ optionContext(option, selected) {
1462
+ return {
1463
+ $implicit: option,
1464
+ selected,
1465
+ disabled: this.isOptionDisabled(option),
1466
+ label: this.labelOf(option),
1467
+ };
1468
+ }
1469
+ /** Shared by both controls to match a resolved option value against the current value. */
1470
+ sameValue(a, b) {
1471
+ return isSameOptionValue(a, b);
1472
+ }
1473
+ /**
1474
+ * Spacing tokens feeding the height estimate. Overridable so each control keeps the
1475
+ * `--gog-<block>-*` names it already exposes to consumers for theming.
1476
+ */
1477
+ /**
1478
+ * The gap **between rows**, as a seed for the first frame. Like `optionHeightToken`, the real
1479
+ * value is measured from the rendered list and replaces this.
1480
+ *
1481
+ * **Read the options container's CSS before pointing this at a token, not the token's name.**
1482
+ * Two of the three controls name a token `--gog-<block>-option-gap` and use it for the gap
1483
+ * *inside* a row — between the check mark and the label — with no gap between rows at all.
1484
+ * Seeding from it added 12px per row of panel that does not exist. Only `gog-multiselect`'s
1485
+ * options container declares a `gap`, and only it overrides this.
1486
+ */
1487
+ optionGapToken = '--gog-dropdown-option-gap';
1488
+ optionsPaddingToken = '--gog-dropdown-options-padding';
1489
+ /** Keep in sync with the real `max-height` on the subclass's `__dropdown` block. */
1490
+ panelMaxHeightToken = '--gog-dropdown-panel-max-height';
1491
+ /** Estimated row height fed into the placement math; not a real layout property. */
1492
+ optionHeightToken = '--gog-dropdown-option-height';
1493
+ /**
1494
+ * Unique per-instance suffix, so several dropdowns on a page can't collide on DOM ids.
1495
+ *
1496
+ * Deliberately a bare number rather than `nextGogControlId()` (which the single-element
1497
+ * controls use): each subclass derives *several* ids from this one instance — trigger,
1498
+ * listbox, label, error — so what it needs is the instance number, not one finished id.
1499
+ */
1500
+ uid = ++GogDropdownBase.nextUid;
1501
+ elRef = inject((ElementRef));
1502
+ injector = inject(Injector);
1503
+ isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
1504
+ document = inject(DOCUMENT);
1505
+ appRef = inject(ApplicationRef);
1506
+ destroyRef = inject(DestroyRef);
1507
+ ngControl = inject(NgControl, { optional: true, self: true });
1508
+ /** `protected` so subclasses can resolve their own inputs against the same config object. */
1509
+ globalConfig = inject(GOG_CONFIG);
1510
+ overlay = new GogDropdownOverlay(this.appRef, this.document);
1511
+ /** Set from `(focus)`/`(blur)` on the trigger — see `onFocusIn`/`onFocusOut`. */
1512
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
1513
+ /** Instance input → `GOG_CONFIG` → the component's own default. See `resolveConfigured`. */
1514
+ resolvedSize = computed(() => resolveConfigured(this.size(), this.globalConfig.control?.size, DEFAULT_SIZE), ...(ngDevMode ? [{ debugName: "resolvedSize" }] : /* istanbul ignore next */ []));
1515
+ rippleEnabled = resolveRipple(this.ripple, this.globalConfig);
1516
+ resolvedErrorDisplay = computed(() => resolveConfigured(this.errorDisplay(), this.globalConfig.control?.errorDisplay, DEFAULT_ERROR_DISPLAY), ...(ngDevMode ? [{ debugName: "resolvedErrorDisplay" }] : /* istanbul ignore next */ []));
1517
+ resolvedAppendToBody = computed(() => resolveConfigured(this.appendToBody(), this.globalConfig.dropdown?.appendToBody, DEFAULT_APPEND_TO_BODY), ...(ngDevMode ? [{ debugName: "resolvedAppendToBody" }] : /* istanbul ignore next */ []));
1518
+ resolvedDropdownDirection = computed(() => resolveConfigured(this.dropdownDirection(), this.globalConfig.dropdown?.direction, DEFAULT_DROPDOWN_DIRECTION), ...(ngDevMode ? [{ debugName: "resolvedDropdownDirection" }] : /* istanbul ignore next */ []));
1519
+ /**
1520
+ * The single size modifier for the wrapper, replacing one `[class.<block>--<size>]` binding
1521
+ * per size. Empty for `'md'`: that is the default size and has no modifier rule of its own —
1522
+ * every `--gog-<block>-size-*` chain bottoms out at the `md` tokens.
1523
+ */
1524
+ sizeClass = computed(() => this.resolvedSize() === DEFAULT_SIZE ? '' : `${this.sizeBlockClass}--${this.resolvedSize()}`, ...(ngDevMode ? [{ debugName: "sizeClass" }] : /* istanbul ignore next */ []));
1525
+ /**
1526
+ * Same modifier repeated on the panel, but only when it is appended to `<body>` — outside
1527
+ * the component's subtree the panel no longer inherits the wrapper's size tokens, so it has
1528
+ * to carry them itself. See the `--portal` blocks in the component stylesheets.
1529
+ */
1530
+ panelSizeClass = computed(() => this.resolvedAppendToBody() && this.resolvedSize() !== DEFAULT_SIZE
1531
+ ? `${this.panelBlockClass}--${this.resolvedSize()}`
1532
+ : '', ...(ngDevMode ? [{ debugName: "panelSizeClass" }] : /* istanbul ignore next */ []));
1533
+ cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
1534
+ errorState = new GogErrorState(this.errorMessage, this.resolvedErrorDisplay, this.ngControl);
1535
+ isDisabled = computed(() => this.disabled() || this.cvaDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
1536
+ dropdownDirectionState = signal('down', ...(ngDevMode ? [{ debugName: "dropdownDirectionState" }] : /* istanbul ignore next */ []));
1537
+ panelPlacement = signal(null, ...(ngDevMode ? [{ debugName: "panelPlacement" }] : /* istanbul ignore next */ []));
1538
+ /**
1539
+ * Stacking order to write onto the panel, or null to leave it to the stylesheet.
1540
+ *
1541
+ * Only needed when appending to `<body>`: an inline panel inherits `--gog-dropdown-z` from
1542
+ * its surroundings — which is how a dropdown inside a dialog ends up above the dialog —
1543
+ * but an appended one sits outside that subtree and inherits nothing, so the value has
1544
+ * to be resolved from the trigger and written out explicitly.
1545
+ */
1546
+ panelZIndex = signal(null, ...(ngDevMode ? [{ debugName: "panelZIndex" }] : /* istanbul ignore next */ []));
1547
+ /**
1548
+ * Explicit panel width, or null to let it size to its own content.
1549
+ *
1550
+ * The panel used to be pinned to the trigger's width, which broke a trigger narrower than its
1551
+ * options: with `[fullWidth]="false"` the trigger shrinks to the *current* selection, so
1552
+ * picking a short option cut the longer ones off in the list. It now sizes to its content with
1553
+ * the trigger width as a floor — see `resolvedPanelMinWidth`.
1554
+ */
1555
+ resolvedPanelWidth = computed(() => this.dropdownWidth(), ...(ngDevMode ? [{ debugName: "resolvedPanelWidth" }] : /* istanbul ignore next */ []));
1556
+ /** The trigger's width, used as the panel's minimum so it never renders narrower than it. */
1557
+ resolvedPanelMinWidth = computed(() => {
1558
+ const width = this.panelPlacement()?.width;
1559
+ return width != null ? `${width}px` : null;
1560
+ }, ...(ngDevMode ? [{ debugName: "resolvedPanelMinWidth" }] : /* istanbul ignore next */ []));
1561
+ /** `dropdownMaxHeight`, or the viewport-derived max-height from `panelPlacement`. */
1562
+ resolvedPanelMaxHeight = computed(() => {
1563
+ const maxHeight = this.panelPlacement()?.maxHeight;
1564
+ return this.dropdownMaxHeight() ?? (maxHeight != null ? `${maxHeight}px` : null);
1565
+ }, ...(ngDevMode ? [{ debugName: "resolvedPanelMaxHeight" }] : /* istanbul ignore next */ []));
1566
+ hasError = this.errorState.hasError;
1567
+ visibleError = this.errorState.visibleError;
1568
+ clearableState = new GogClearableState(this.clearable, computed(() => this.hasFloatValue()), this.isDisabled, this.globalConfig,
1569
+ // read lazily for the same field-initialisation-order reason as hasFloatValue below
1570
+ () => this.clearableByDefault);
1571
+ /** Whether to render the clear button right now — see `GogClearableState`. */
1572
+ showClear = this.clearableState.isVisible;
1573
+ resolvedFilter = computed(() => resolveConfigured(this.filter(), this.globalConfig.dropdown?.filter, DEFAULT_FILTER), ...(ngDevMode ? [{ debugName: "resolvedFilter" }] : /* istanbul ignore next */ []));
1574
+ resolvedFilterPosition = computed(() => resolveConfigured(this.filterPosition(), this.globalConfig.dropdown?.filterPosition, DEFAULT_FILTER_POSITION), ...(ngDevMode ? [{ debugName: "resolvedFilterPosition" }] : /* istanbul ignore next */ []));
1575
+ /** Current search text. Cleared whenever the panel closes, so reopening starts fresh. */
1576
+ filterQuery = signal('', ...(ngDevMode ? [{ debugName: "filterQuery" }] : /* istanbul ignore next */ []));
1577
+ /**
1578
+ * The options actually rendered. Everything downstream — the loops, the keyboard navigation
1579
+ * target list, the panel height estimate, and multiselect's select-all — reads this rather
1580
+ * than `options()`, so filtering stays consistent instead of only hiding rows visually.
1581
+ */
1582
+ visibleOptions = computed(() => {
1583
+ const query = this.filterQuery().trim();
1584
+ if (!this.resolvedFilter() || query === '')
1585
+ return this.options();
1586
+ const match = this.filterMatch();
1587
+ if (match)
1588
+ return this.options().filter((option) => match(option, query));
1589
+ const needle = query.toLowerCase();
1590
+ return this.options().filter((option) => this.labelOf(option).toLowerCase().includes(needle));
1591
+ }, ...(ngDevMode ? [{ debugName: "visibleOptions" }] : /* istanbul ignore next */ []));
1592
+ onFilterInput(event) {
1593
+ this.filterQuery.set(event.target.value);
1594
+ // Typing can take 10 000 options to 3. The window's range and the scroller's position have
1595
+ // to reset *together*: leave the scroller where it was and the range is computed from a
1596
+ // scrollTop that is past the end of the new list, so the panel renders rows 400-420 of a
1597
+ // three-row list and shows nothing. The panel still looks right until you type, which is
1598
+ // why this is the bug a reviewer does not see.
1599
+ this.resetPanelScroll();
1600
+ }
1601
+ /**
1602
+ * Scrolls `index` into view **without moving focus**, which is what a combobox needs: focus
1603
+ * stays in the text field and the highlight is pointed at with `aria-activedescendant`, so
1604
+ * nothing scrolls the row into view on its own the way focusing it would.
1605
+ */
1606
+ revealOption(index) {
1607
+ const offset = this.virtualWindow.scrollOffsetFor(index);
1608
+ if (offset !== null)
1609
+ this.setPanelScrollTop(offset);
1610
+ }
1611
+ /** Puts the scroller, the window and the keyboard's idea of "here" back at the top together. */
1612
+ resetPanelScroll() {
1613
+ this.activeOptionIndex.set(-1);
1614
+ this.setPanelScrollTop(0);
1615
+ }
1616
+ /**
1617
+ * Moves the scroller and the window's own idea of where it is, in that order and in the same
1618
+ * turn.
1619
+ *
1620
+ * The signal is written here rather than waiting for the scroller to echo the change back
1621
+ * through `(gogScroll)`: that echo is coalesced into an animation frame, so a keyboard move
1622
+ * that scrolled would compute its new range one frame after it moved focus -- which is one
1623
+ * frame during which the row it is trying to focus has not been rendered.
1624
+ */
1625
+ setPanelScrollTop(top) {
1626
+ this.panelViewport.update((viewport) => ({ ...viewport, scrollTop: top }));
1627
+ const viewport = this.panelViewportElement();
1628
+ if (!viewport)
1629
+ return;
1630
+ // Feature-detected rather than assumed, for the reason `gog-autocomplete` records against
1631
+ // `scrollIntoView`: jsdom implements neither, and an unhandled throw here fails a whole test
1632
+ // run rather than this line. Assigning `scrollTop` is the equivalent every host does have.
1633
+ if (typeof viewport.scrollTo === 'function') {
1634
+ viewport.scrollTo({ top, behavior: 'auto' });
1635
+ }
1636
+ else {
1637
+ viewport.scrollTop = top;
1638
+ }
1639
+ }
1640
+ /**
1641
+ * `gog-scroll`'s scrolling element inside the open panel, found in the DOM rather than with a
1642
+ * `viewChild`: an appended panel is attached to `<body>` as its own change-detection root, so a
1643
+ * view query on this component does not reach it.
1644
+ */
1645
+ panelViewportElement() {
1646
+ if (!this.isBrowser)
1647
+ return null;
1648
+ const scope = this.overlay.hostElement ?? this.elRef.nativeElement;
1649
+ return scope.querySelector('.gog-scroll__viewport');
1650
+ }
1651
+ /** Resets the control to its empty value and notifies any attached form. */
1652
+ clearValue(event) {
1653
+ event.preventDefault();
1654
+ event.stopPropagation();
1655
+ this.commitValue(this.emptyValue);
1656
+ }
1657
+ /**
1658
+ * `hasFloatValue` is wrapped in a `computed` rather than passed straight through: these
1659
+ * field initializers run before the *subclass's* do, so `this.hasFloatValue` is still
1660
+ * undefined right here. The arrow defers the read until the signal is first evaluated, by
1661
+ * which time the subclass has assigned it.
1662
+ */
1663
+ floatLabelState = new GogFloatLabelState(this.floatLabel, this.floatLabelShowPlaceholder, this.placeholder, this.isFocused, computed(() => this.hasFloatValue()), this.globalConfig);
1664
+ resolvedFloatLabel = this.floatLabelState.variant;
1665
+ isFloatLabelActive = this.floatLabelState.isActive;
1666
+ isFloatLabelFloated = this.floatLabelState.isFloated;
1667
+ effectivePlaceholder = this.floatLabelState.effectivePlaceholder;
1668
+ /** Measured once per open rather than per scroll tick — see `refreshPanelMetrics`. */
1669
+ optionGap = 0;
1670
+ optionsPadding = 0;
1671
+ maxPanelHeight = FALLBACK_MAX_PANEL_HEIGHT;
1672
+ optionHeight = FALLBACK_OPTION_HEIGHT;
1673
+ /**
1674
+ * A real row's height, once one has ever been rendered. `null` until then.
1675
+ *
1676
+ * The token this class reads for `optionHeight` calls itself an estimate, and it is a bad one:
1677
+ * measured against a rendered row in all eleven shipped themes it is wrong in every one, from
1678
+ * -0.62px (`terminal`) to +8.38px (`parchment`), and low in ten of them. No static value can be
1679
+ * right -- the same `parchment` row is 48.38px at `--gog-density: 1` and 42.38px at 0.85,
1680
+ * because the height is padding plus leading plus border and a theme or a consumer can move
1681
+ * every term.
1682
+ *
1683
+ * That matters because the estimate is not decorative: `estimatePanelHeight` multiplies it by
1684
+ * the option count and `resolveDropdownDirection` opens the panel up or down on the result. Low
1685
+ * by 7px a row means a five-row panel judged to fit below when it needs 37px more than there is.
1686
+ * Only short lists were ever affected -- above `panelMaxHeightToken` the cap dominates and the
1687
+ * error is masked -- which is why it went unseen.
1688
+ */
1689
+ measuredOptionHeight = null;
1690
+ /**
1691
+ * The real gap between two rows, once a list has ever rendered. `null` until then.
1692
+ *
1693
+ * Measured for the same reason the row height is, and it came out of the same audit: the token
1694
+ * `optionGapToken` seeds from is the *inside* of a row in two of the three controls (mark to
1695
+ * label), while their options containers declare no row gap at all. Reading the container is
1696
+ * exact where reading a token is a guess about what the token means.
1697
+ */
1698
+ measuredOptionGap = null;
1699
+ measureFrame = null;
1700
+ repositionFrame = null;
1701
+ // ── Windowing ────────────────────────────────────────────────────────────────
1702
+ //
1703
+ // See `docs/virtualization.md`. The arithmetic is `GogVirtualWindow`'s; what lives here is
1704
+ // everything that touches the page: what the row pitch really is, how tall the scroller
1705
+ // actually became, where it is scrolled to, and which index the keyboard is on.
1706
+ /**
1707
+ * Whether this instance windows its list. Resolved the same way every other dropdown setting
1708
+ * is: instance input, then `GOG_CONFIG.dropdown.virtualize`, then off.
1709
+ *
1710
+ * **Off by default and never switched on automatically.** A windowed list and a plain one
1711
+ * differ under `Ctrl+F`, under a screen reader's "list all items", and under any consumer CSS
1712
+ * that targets `:last-child`; flipping that at a row-count threshold would make the component's
1713
+ * behaviour depend on how much data happened to arrive, which works in development and
1714
+ * surprises in production. `GOG_CONFIG.ripple.enabled` is off by default for the same reason.
1715
+ */
1716
+ resolvedVirtualize = computed(() => resolveConfigured(this.virtualizeRequest(), this.globalConfig.dropdown?.virtualize, false), ...(ngDevMode ? [{ debugName: "resolvedVirtualize" }] : /* istanbul ignore next */ []));
1717
+ /**
1718
+ * The subclass's own `virtualize` input, if it offers one.
1719
+ *
1720
+ * A subclass overrides this with its input rather than the base declaring one, so a control
1721
+ * that has not adopted windowing yet does not inherit a public input that does nothing. Read
1722
+ * only from inside a `computed`, which is what lets a subclass field override a base field
1723
+ * that was initialised first.
1724
+ */
1725
+ virtualizeRequest = signal(undefined, ...(ngDevMode ? [{ debugName: "virtualizeRequest" }] : /* istanbul ignore next */ []));
1726
+ /**
1727
+ * How far apart two rows start, in px: the row's own height plus whatever gap the list puts
1728
+ * between rows. Seeded from the tokens and replaced by the measurement, exactly as the
1729
+ * placement estimate is -- and for a window it matters more, because the error accumulates
1730
+ * once per row instead of once per panel.
1731
+ */
1732
+ rowPitch = signal(0, ...(ngDevMode ? [{ debugName: "rowPitch" }] : /* istanbul ignore next */ []));
1733
+ /**
1734
+ * The gap alone, as a signal, because `spacerHeight` needs it from inside a `computed` and the
1735
+ * cached `measuredOptionGap` beside it is a plain field -- a computed reading that would be
1736
+ * correct only by the accident of `rowPitch` changing in the same statement.
1737
+ */
1738
+ rowGap = signal(0, ...(ngDevMode ? [{ debugName: "rowGap" }] : /* istanbul ignore next */ []));
1739
+ /**
1740
+ * The scroller's real geometry, fed from `gog-scroll`'s own `(gogScroll)`.
1741
+ *
1742
+ * The plan called for a `ResizeObserver` on the scroller; it is not needed, because the
1743
+ * scroller already runs one and already coalesces scroll and resize into a single
1744
+ * rAF-batched emission carrying both numbers. A second observer would have measured the same
1745
+ * element one frame later.
1746
+ *
1747
+ * `height` is the viewport's `clientHeight`, never `--gog-*-panel-max-height`: that token is a
1748
+ * cap, and a panel with three options is three rows tall.
1749
+ */
1750
+ panelViewport = signal({
1751
+ scrollTop: 0,
1752
+ height: 0,
1753
+ }, ...(ngDevMode ? [{ debugName: "panelViewport" }] : /* istanbul ignore next */ []));
1754
+ virtualWindow = new GogVirtualWindow({
1755
+ count: computed(() => this.visibleOptions().length),
1756
+ rowHeight: this.rowPitch,
1757
+ viewportHeight: computed(() => this.panelViewport().height),
1758
+ scrollTop: computed(() => this.panelViewport().scrollTop),
1759
+ });
1760
+ /** Index into `visibleOptions()` of the row the keyboard is on, or -1 for none. */
1761
+ activeOptionIndex = signal(-1, ...(ngDevMode ? [{ debugName: "activeOptionIndex" }] : /* istanbul ignore next */ []));
1762
+ /** The slice of `visibleOptions()` actually stamped into the panel. */
1763
+ optionWindow = computed(() => this.resolvedVirtualize()
1764
+ ? this.virtualWindow.range()
1765
+ : { start: 0, end: this.visibleOptions().length }, ...(ngDevMode ? [{ debugName: "optionWindow" }] : /* istanbul ignore next */ []));
1766
+ /**
1767
+ * What the template loops over. Identical to `visibleOptions()` when not windowing, and the
1768
+ * same array instance, so nothing re-renders for the sake of a slice that changed nothing.
1769
+ */
1770
+ renderedOptions = computed(() => {
1771
+ const all = this.visibleOptions();
1772
+ const { start, end } = this.optionWindow();
1773
+ return start === 0 && end === all.length ? all : all.slice(start, end);
1774
+ }, ...(ngDevMode ? [{ debugName: "renderedOptions" }] : /* istanbul ignore next */ []));
1775
+ /**
1776
+ * Filler above and below the rendered rows, in px.
1777
+ *
1778
+ * Spacers rather than absolute positioning: the rows stay flex children of the same container,
1779
+ * so every gap, selector and `:last-child` the component already relies on keeps working. See
1780
+ * `GogVirtualWindow`'s own note for why that trade is worth making for a one-column list.
1781
+ */
1782
+ padBefore = computed(() => this.resolvedVirtualize() ? this.spacerHeight(this.virtualWindow.padBefore()) : 0, ...(ngDevMode ? [{ debugName: "padBefore" }] : /* istanbul ignore next */ []));
1783
+ padAfter = computed(() => this.resolvedVirtualize() ? this.spacerHeight(this.virtualWindow.padAfter()) : 0, ...(ngDevMode ? [{ debugName: "padAfter" }] : /* istanbul ignore next */ []));
1784
+ /**
1785
+ * A spacer's own height, less the gap the flex column puts either side of it.
1786
+ *
1787
+ * A spacer is a flex child like a row, so a list that declares `gap` gets one *around the
1788
+ * spacer too* -- and the window's padding already accounts for every gap in the rows it stands
1789
+ * in for. Left uncorrected, an open panel is two gaps too tall and every rendered row sits one
1790
+ * gap lower than its index says. It is a constant, not an accumulating error, which is exactly
1791
+ * why it would have survived review: at `gog-multiselect`'s 4px nothing looks wrong, it is just
1792
+ * 4px wrong everywhere.
1793
+ *
1794
+ * `gog-select` and `gog-autocomplete` declare no row gap, so this subtracts nothing there. The
1795
+ * subtraction can never go negative on a spacer that exists: any non-zero padding is at least
1796
+ * one whole row, and a row is taller than the gap beside it.
1797
+ */
1798
+ spacerHeight(padding) {
1799
+ if (padding <= 0)
1800
+ return 0;
1801
+ return Math.max(0, padding - this.rowGap());
1802
+ }
1803
+ /**
1804
+ * A windowed listbox holds twenty `role="option"` children and has to announce ten thousand.
1805
+ *
1806
+ * Set only while windowing. An unwindowed list has every option in the DOM, and the browser's
1807
+ * own count is then both correct and free -- restating it would be one more thing to keep true.
1808
+ */
1809
+ ariaSetSize = computed(() => this.resolvedVirtualize() ? this.visibleOptions().length : null, ...(ngDevMode ? [{ debugName: "ariaSetSize" }] : /* istanbul ignore next */ []));
1810
+ /** The real position of a rendered row in the full list, 1-based, or null when not windowing. */
1811
+ ariaPosInSet(renderedIndex) {
1812
+ return this.resolvedVirtualize() ? this.optionWindow().start + renderedIndex + 1 : null;
1813
+ }
1814
+ /** `(gogScroll)` on the panel's `gog-scroll`. */
1815
+ onPanelScroll(metrics) {
1816
+ this.panelViewport.update((viewport) => ({
1817
+ scrollTop: metrics.scrollTop,
1818
+ // A zero is "not laid out yet", not "no viewport": the scroller's first emission comes
1819
+ // from its own `afterNextRender`, which can land before the panel has a height. Taking it
1820
+ // literally would throw away the seed and render the whole list for a frame -- the one
1821
+ // thing the seed exists to prevent -- and then render it again once the real height
1822
+ // arrived. The last number known to be real is a better answer than a zero.
1823
+ height: metrics.clientHeight > 0 ? metrics.clientHeight : viewport.height,
1824
+ }));
1825
+ this.releaseFocusLeavingTheWindow();
1826
+ }
1827
+ /**
1828
+ * A row that scrolls out of the window is unmounted, and an unmounted element holding focus
1829
+ * drops it on `<body>` -- where an open panel has no keyboard at all: Escape does not close it
1830
+ * and the arrows scroll the page instead of the list.
1831
+ *
1832
+ * So a mouse scroll that would take the focused row away hands focus back to the trigger,
1833
+ * which is a state both Escape and ArrowDown work from. Checked here rather than in an effect
1834
+ * because this runs *before* the re-render, while the row still exists and can still be asked
1835
+ * whether it is the focused one -- after the unmount that question has no answer.
1836
+ *
1837
+ * This is the price of windowing that a plain list does not pay, and the reason it is opt-in.
1838
+ */
1839
+ releaseFocusLeavingTheWindow() {
1840
+ const active = this.activeOptionIndex();
1841
+ if (!this.isBrowser || !this.resolvedVirtualize() || active < 0)
1842
+ return;
1843
+ const { start, end } = this.optionWindow();
1844
+ if (active >= start && active < end)
1845
+ return;
1846
+ this.activeOptionIndex.set(-1);
1847
+ const scope = this.overlay.hostElement ?? this.elRef.nativeElement;
1848
+ const focused = this.document.activeElement;
1849
+ if (focused instanceof HTMLElement &&
1850
+ scope.contains(focused) &&
1851
+ focused.classList.contains(this.optionClass)) {
1852
+ this.focusTrigger();
1853
+ }
1854
+ }
1855
+ onChangeFn = () => { };
1856
+ onTouchedFn = () => { };
1857
+ constructor() {
1858
+ // Registering through NgControl instead of NG_VALUE_ACCESSOR keeps `this.ngControl`
1859
+ // available for `hasError` — providing NG_VALUE_ACCESSOR on the component while also
1860
+ // injecting NgControl would be a dependency cycle.
1861
+ if (this.ngControl) {
1862
+ this.ngControl.valueAccessor = this;
1863
+ }
1864
+ this.destroyRef.onDestroy(() => this.overlay.detach());
1865
+ this.bindWhileOpen();
1866
+ }
1867
+ /**
1868
+ * Binds the click-outside and reposition listeners only for as long as the panel is
1869
+ * open. Bound permanently — as host listeners were — every dropdown on the page runs a
1870
+ * handler on every document click and every scroll frame, whether or not it is showing
1871
+ * anything.
1872
+ */
1873
+ bindWhileOpen() {
1874
+ effect((onCleanup) => {
1875
+ if (!this.isOpen() || !this.isBrowser)
1876
+ return;
1877
+ const onDocumentClick = (event) => this.closeIfClickedOutside(event);
1878
+ const onReflow = () => this.scheduleReposition();
1879
+ // Capture phase: scroll does not bubble, so a bubble-phase listener on window would
1880
+ // miss scrolling inside a nested container — which is exactly the case appendToBody
1881
+ // exists for. Passive: these handlers never call preventDefault.
1882
+ this.document.addEventListener('click', onDocumentClick);
1883
+ window.addEventListener('scroll', onReflow, { passive: true, capture: true });
1884
+ window.addEventListener('resize', onReflow, { passive: true });
1885
+ onCleanup(() => {
1886
+ this.document.removeEventListener('click', onDocumentClick);
1887
+ window.removeEventListener('scroll', onReflow, { capture: true });
1888
+ window.removeEventListener('resize', onReflow);
1889
+ if (this.repositionFrame !== null) {
1890
+ cancelAnimationFrame(this.repositionFrame);
1891
+ this.repositionFrame = null;
1892
+ }
1893
+ if (this.measureFrame !== null) {
1894
+ cancelAnimationFrame(this.measureFrame);
1895
+ this.measureFrame = null;
1896
+ }
1897
+ });
1898
+ });
1899
+ }
1900
+ ngDoCheck() {
1901
+ this.errorState.check();
1902
+ }
1903
+ writeValue(val) {
1904
+ this.value.set(val ?? this.emptyValue);
1905
+ }
1906
+ registerOnChange(fn) {
1907
+ this.onChangeFn = fn;
1908
+ }
1909
+ registerOnTouched(fn) {
1910
+ this.onTouchedFn = fn;
1911
+ }
1912
+ setDisabledState(isDisabled) {
1913
+ this.cvaDisabled.set(isDisabled);
1914
+ if (isDisabled) {
1915
+ this.close();
1916
+ }
1917
+ }
1918
+ /** Bound to `(focus)` on the trigger — only drives the float-label floated state. */
1919
+ onFocusIn() {
1920
+ this.isFocused.set(true);
1921
+ }
1922
+ /** Bound to `(blur)` on the trigger — only drives the float-label floated state. */
1923
+ onFocusOut() {
1924
+ this.isFocused.set(false);
1925
+ }
1926
+ toggle() {
1927
+ if (this.isDisabled())
1928
+ return;
1929
+ if (this.isOpen()) {
1930
+ this.close();
1931
+ }
1932
+ else {
1933
+ this.open();
1934
+ }
1935
+ }
1936
+ open() {
1937
+ if (this.isDisabled() || this.isOpen())
1938
+ return;
1939
+ this.isOpen.set(true);
1940
+ this.refreshPanelMetrics();
1941
+ this.seedWindow();
1942
+ this.updatePlacement();
1943
+ if (this.resolvedAppendToBody()) {
1944
+ this.attachOverlay();
1945
+ }
1946
+ this.scheduleOptionMeasure();
1947
+ }
1948
+ /**
1949
+ * Gives the window numbers to work from before anything has rendered.
1950
+ *
1951
+ * Without this the first frame has a viewport height of zero, which `GogVirtualWindow`
1952
+ * deliberately reads as "render everything" -- correct, and exactly the 10 000 rows the window
1953
+ * exists to avoid, stamped once before the scroller reports its real height a frame later. The
1954
+ * seed is the same panel-height estimate placement already uses, so no new arithmetic and no
1955
+ * new token.
1956
+ */
1957
+ seedWindow() {
1958
+ this.activeOptionIndex.set(-1);
1959
+ const gap = this.measuredOptionGap ?? this.optionGap;
1960
+ this.rowGap.set(gap);
1961
+ this.rowPitch.set((this.measuredOptionHeight ?? this.optionHeight) + gap);
1962
+ this.panelViewport.set({
1963
+ scrollTop: 0,
1964
+ height: this.isBrowser ? this.estimatePanelHeight() : 0,
1965
+ });
1966
+ }
1967
+ close() {
1968
+ if (!this.isOpen())
1969
+ return;
1970
+ this.isOpen.set(false);
1971
+ this.filterQuery.set('');
1972
+ this.overlay.detach();
1973
+ // Closing is this control's equivalent of a blur, which is when a form control is
1974
+ // conventionally considered touched.
1975
+ this.markTouched();
1976
+ }
1977
+ /** Writes a new selection out to both the model and the attached form control. */
1978
+ commitValue(next) {
1979
+ this.value.set(next);
1980
+ this.onChangeFn(next);
1981
+ this.markTouched();
1982
+ }
1983
+ markTouched() {
1984
+ this.onTouchedFn();
1985
+ }
1986
+ closeIfClickedOutside(event) {
1987
+ const target = event.target;
1988
+ if (!target)
1989
+ return;
1990
+ const inside = this.elRef.nativeElement.contains(target) ||
1991
+ (this.overlay.hostElement?.contains(target) ?? false);
1992
+ if (!inside) {
1993
+ this.close();
1994
+ }
1995
+ }
1996
+ /**
1997
+ * Coalesces bursts of scroll/resize events into one reposition per frame. Placement
1998
+ * reads layout, so running it per event is what makes a scroll janky.
1999
+ */
2000
+ scheduleReposition() {
2001
+ if (this.repositionFrame !== null)
2002
+ return;
2003
+ this.repositionFrame = requestAnimationFrame(() => {
2004
+ this.repositionFrame = null;
2005
+ if (this.isOpen()) {
2006
+ this.updatePlacement();
2007
+ }
2008
+ });
2009
+ }
2010
+ /**
2011
+ * Reads one real row after the panel has rendered, and re-places if the token had lied.
2012
+ *
2013
+ * One frame late by construction -- the row has to exist -- so the very first open of an
2014
+ * instance can be placed from the estimate and corrected before the next paint. Every later
2015
+ * open starts from the measurement and is right immediately, which is why this caches rather
2016
+ * than measuring each time.
2017
+ *
2018
+ * Deliberately does **not** fall back to the token when no row is found: an empty list has no
2019
+ * row to measure and no rows to be wrong about, and overwriting a good measurement with the
2020
+ * token because the user filtered everything away would undo the fix.
2021
+ */
2022
+ scheduleOptionMeasure() {
2023
+ if (!this.isBrowser || this.measureFrame !== null)
2024
+ return;
2025
+ this.measureFrame = requestAnimationFrame(() => {
2026
+ this.measureFrame = null;
2027
+ if (!this.isOpen())
2028
+ return;
2029
+ const scope = this.overlay.hostElement ?? this.elRef.nativeElement;
2030
+ const row = scope.querySelector(`.${this.optionClass}`);
2031
+ const height = row?.getBoundingClientRect().height ?? 0;
2032
+ if (height <= 0)
2033
+ return;
2034
+ // The row's own parent is the options container, so the gap comes for free once the row
2035
+ // has been found -- no second selector for a class each subclass would have to declare.
2036
+ // `row-gap` computes to the keyword `normal` when a flex container sets no gap, which is
2037
+ // used as zero; `readPx` returns the fallback for anything it cannot parse, which is that.
2038
+ const gap = row?.parentElement
2039
+ ? readPx$1(getComputedStyle(row.parentElement).rowGap, 0)
2040
+ : (this.measuredOptionGap ?? this.optionGap);
2041
+ const changed = this.measuredOptionHeight === null
2042
+ ? Math.abs(height - this.optionHeight) > 0.5 || Math.abs(gap - this.optionGap) > 0.5
2043
+ : Math.abs(height - this.measuredOptionHeight) > 0.5 ||
2044
+ Math.abs(gap - (this.measuredOptionGap ?? this.optionGap)) > 0.5;
2045
+ this.measuredOptionHeight = height;
2046
+ this.measuredOptionGap = gap;
2047
+ // The window reads this too, and it is the reason the measurement is not optional there:
2048
+ // a placement is wrong once, a pitch is wrong once per row and the error accumulates down
2049
+ // the list until the rendered rows and the scrollbar disagree about where they are.
2050
+ this.rowGap.set(gap);
2051
+ this.rowPitch.set(height + gap);
2052
+ if (changed)
2053
+ this.updatePlacement();
2054
+ });
2055
+ }
2056
+ /** Jumps from the trigger into the option list on ArrowDown/ArrowUp while open. */
2057
+ onTriggerArrowKeydown(event) {
2058
+ if (!this.isOpen())
2059
+ return;
2060
+ event.preventDefault();
2061
+ // Windowed, the last option is not in the DOM to be focused, so the entry point is an index
2062
+ // in the full list rather than an element in the rendered slice. Home/End mean the first and
2063
+ // last *reachable* option, which is what ArrowDown and ArrowUp mean from the trigger.
2064
+ if (this.resolvedVirtualize()) {
2065
+ this.moveActiveOption(event.key === 'ArrowUp' ? 'End' : 'Home');
2066
+ return;
2067
+ }
2068
+ const options = this.enabledOptionElements();
2069
+ if (options.length === 0)
2070
+ return;
2071
+ const index = event.key === 'ArrowUp' ? options.length - 1 : 0;
2072
+ options[index]?.focus();
2073
+ }
2074
+ onOptionKeydown(event) {
2075
+ if (event.key === 'Escape') {
2076
+ event.preventDefault();
2077
+ this.close();
2078
+ this.focusTrigger();
2079
+ return;
2080
+ }
2081
+ if (event.key === 'Tab') {
2082
+ // Deliberately not prevented: closing and handing focus back to the trigger lets
2083
+ // the browser's own Tab handling continue from there, so focus lands on whatever
2084
+ // follows the control. Without this, tabbing out of an appended panel would jump
2085
+ // to the end of the document, since that is where the panel's DOM lives.
2086
+ this.close();
2087
+ this.focusTrigger();
2088
+ return;
2089
+ }
2090
+ // The inversion windowing needs: arrow keys move an index in `visibleOptions()`, and the DOM
2091
+ // follows it. Walking rendered elements stops at the edge of the window, so ArrowDown from
2092
+ // the last rendered row would wrap to the first rendered row rather than advance the list.
2093
+ if (this.resolvedVirtualize() && isRovingFocusKey(event.key)) {
2094
+ event.preventDefault();
2095
+ this.moveActiveOption(event.key);
2096
+ return;
2097
+ }
2098
+ handleRovingFocusKeydown(event, this.enabledOptionElements());
2099
+ }
2100
+ /**
2101
+ * Moves the keyboard's index by one roving-focus key, skipping disabled options and wrapping,
2102
+ * then makes the DOM agree: scroll first if the target is outside the window, focus second.
2103
+ */
2104
+ moveActiveOption(key) {
2105
+ const options = this.visibleOptions();
2106
+ if (options.length === 0)
2107
+ return;
2108
+ const current = this.activeOptionIndex();
2109
+ // With nothing active yet, start one step *behind* the intended first target and let the
2110
+ // wrap do the work, so a disabled first (or last) option is skipped by the same code that
2111
+ // skips one in the middle.
2112
+ const from = current >= 0 ? current : key === 'ArrowUp' || key === 'End' ? 0 : options.length - 1;
2113
+ this.focusOptionAt(nextRovingFocusIndex(key, from, options.length, (index) => !this.isOptionDisabled(options[index])));
2114
+ }
2115
+ /**
2116
+ * Puts the keyboard on `index` and the focus with it.
2117
+ *
2118
+ * The wait is conditional on purpose: `scrollOffsetFor` returns null when the row is already
2119
+ * visible, which means it is already rendered and can be focused in this turn. Only a move
2120
+ * that actually changes the window has to wait for the render that stamps the row.
2121
+ */
2122
+ focusOptionAt(index) {
2123
+ this.activeOptionIndex.set(index);
2124
+ const offset = this.virtualWindow.scrollOffsetFor(index);
2125
+ if (offset === null) {
2126
+ this.focusRenderedOption(index);
2127
+ return;
2128
+ }
2129
+ this.setPanelScrollTop(offset);
2130
+ if (!this.isBrowser)
2131
+ return;
2132
+ afterNextRender(() => this.focusRenderedOption(index), { injector: this.injector });
2133
+ }
2134
+ focusRenderedOption(index) {
2135
+ if (!this.isBrowser)
2136
+ return;
2137
+ const scope = this.overlay.hostElement ?? this.elRef.nativeElement;
2138
+ scope
2139
+ .querySelector(`.${this.optionClass}[data-gog-option-index="${index}"]`)
2140
+ ?.focus();
2141
+ }
2142
+ focusTrigger() {
2143
+ this.elRef.nativeElement
2144
+ .querySelector(`.${this.triggerClass}`)
2145
+ ?.focus();
2146
+ }
2147
+ /** Extra chrome stacked above the options (e.g. a select-all row), in px. */
2148
+ extraPanelHeight() {
2149
+ return 0;
2150
+ }
2151
+ enabledOptionElements() {
2152
+ const scope = this.overlay.hostElement ?? this.elRef.nativeElement;
2153
+ return Array.from(scope.querySelectorAll(`.${this.optionClass}:not([aria-disabled="true"])`));
2154
+ }
2155
+ attachOverlay() {
2156
+ if (!this.isBrowser)
2157
+ return;
2158
+ const template = this.panelTemplate();
2159
+ if (!template)
2160
+ return;
2161
+ this.overlay.attach(template, this.elRef.nativeElement);
2162
+ }
2163
+ /**
2164
+ * Reads the spacing tokens that feed the height estimate. Done once per open instead of
2165
+ * on every scroll tick, because `getComputedStyle` forces a style recalculation and the
2166
+ * tokens cannot change while the panel is on screen.
2167
+ */
2168
+ refreshPanelMetrics() {
2169
+ if (!this.isBrowser) {
2170
+ this.optionGap = 0;
2171
+ this.optionsPadding = 0;
2172
+ this.maxPanelHeight = FALLBACK_MAX_PANEL_HEIGHT;
2173
+ this.optionHeight = FALLBACK_OPTION_HEIGHT;
2174
+ this.panelZIndex.set(null);
2175
+ return;
2176
+ }
2177
+ const styles = getComputedStyle(this.elRef.nativeElement);
2178
+ this.optionGap = readPx$1(styles.getPropertyValue(this.optionGapToken), 0);
2179
+ this.optionsPadding = readPx$1(styles.getPropertyValue(this.optionsPaddingToken), 0);
2180
+ this.maxPanelHeight = readPx$1(styles.getPropertyValue(this.panelMaxHeightToken), FALLBACK_MAX_PANEL_HEIGHT);
2181
+ this.optionHeight = readPx$1(styles.getPropertyValue(this.optionHeightToken), FALLBACK_OPTION_HEIGHT);
2182
+ this.panelZIndex.set(this.resolvedAppendToBody() ? this.resolvePanelZIndex(styles) : null);
2183
+ }
2184
+ /**
2185
+ * The panel's height, used to choose up/down and to cap `max-height`.
2186
+ *
2187
+ * Estimated on the very first open of an instance and exact from then on. Measuring needs a
2188
+ * rendered row and placement runs before the panel renders, so the first pass uses the row
2189
+ * height token; `measureOptionHeight` then reads a real row and re-places if it disagreed.
2190
+ * Every open after that starts from the measurement.
2191
+ *
2192
+ * A resolvable `dropdownMaxHeight` replaces the row-count arithmetic outright, since it is
2193
+ * exact rather than derived.
2194
+ */
2195
+ estimatePanelHeight() {
2196
+ const custom = this.dropdownMaxHeight();
2197
+ if (custom) {
2198
+ const resolved = resolveCssLengthPx(custom, window.innerHeight);
2199
+ if (resolved !== null)
2200
+ return resolved;
2201
+ }
2202
+ const count = Math.max(this.visibleOptions().length, 1);
2203
+ const rowHeight = this.measuredOptionHeight ?? this.optionHeight;
2204
+ const gap = this.measuredOptionGap ?? this.optionGap;
2205
+ const rows = count * rowHeight + Math.max(count - 1, 0) * gap;
2206
+ return Math.min(rows + this.optionsPadding * 2 + this.extraPanelHeight(), this.maxPanelHeight);
2207
+ }
2208
+ /**
2209
+ * The trigger's own rect, not the whole component's — a label above it or an error
2210
+ * message below it sit in normal document flow and are not part of what the panel needs
2211
+ * to clear. This also keeps `appendToBody` placement consistent with the inline panel,
2212
+ * which is already positioned purely relative to the trigger via CSS.
2213
+ */
2214
+ triggerRect() {
2215
+ const host = this.elRef.nativeElement;
2216
+ const trigger = host.querySelector(`.${this.triggerClass}`) ?? host;
2217
+ return trigger.getBoundingClientRect();
2218
+ }
2219
+ /** The stacking order the panel would have had if it were still inside the subtree. */
2220
+ resolvePanelZIndex(triggerStyles) {
2221
+ const inherited = triggerStyles.getPropertyValue('--gog-dropdown-z').trim();
2222
+ if (inherited) {
2223
+ return readPx$1(inherited, DEFAULT_PANEL_Z_INDEX);
2224
+ }
2225
+ // Not every DOM implementation resolves inherited custom properties through
2226
+ // getComputedStyle (jsdom does not), so also look for an inline declaration up the
2227
+ // tree — which is exactly how gog-dialog hands its stacking order to nested dropdowns.
2228
+ for (let el = this.elRef.nativeElement; el; el = el.parentElement) {
2229
+ const declared = el.style.getPropertyValue('--gog-dropdown-z').trim();
2230
+ if (declared) {
2231
+ return readPx$1(declared, DEFAULT_PANEL_Z_INDEX);
2232
+ }
2233
+ }
2234
+ return DEFAULT_PANEL_Z_INDEX;
2235
+ }
2236
+ updatePlacement() {
2237
+ if (!this.isBrowser)
2238
+ return;
2239
+ const placement = resolveDropdownPlacement(this.resolvedDropdownDirection(), this.triggerRect(), this.estimatePanelHeight(), window.innerHeight, PANEL_GAP, VIEWPORT_PADDING);
2240
+ this.dropdownDirectionState.set(placement.direction);
2241
+ this.panelPlacement.set(placement);
2242
+ }
2243
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: GogDropdownBase, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2244
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "21.2.17", type: GogDropdownBase, isStandalone: true, inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: false, transformFunction: null }, optionValue: { classPropertyName: "optionValue", publicName: "optionValue", isSignal: true, isRequired: false, transformFunction: null }, optionDisabled: { classPropertyName: "optionDisabled", publicName: "optionDisabled", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, clearAriaLabel: { classPropertyName: "clearAriaLabel", publicName: "clearAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, filterPlaceholder: { classPropertyName: "filterPlaceholder", publicName: "filterPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, filterPosition: { classPropertyName: "filterPosition", publicName: "filterPosition", isSignal: true, isRequired: false, transformFunction: null }, filterEmptyMessage: { classPropertyName: "filterEmptyMessage", publicName: "filterEmptyMessage", isSignal: true, isRequired: false, transformFunction: null }, filterMatch: { classPropertyName: "filterMatch", publicName: "filterMatch", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, errorDisplay: { classPropertyName: "errorDisplay", publicName: "errorDisplay", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, dropdownDirection: { classPropertyName: "dropdownDirection", publicName: "dropdownDirection", isSignal: true, isRequired: false, transformFunction: null }, dropdownZIndex: { classPropertyName: "dropdownZIndex", publicName: "dropdownZIndex", isSignal: true, isRequired: false, transformFunction: null }, dropdownWidth: { classPropertyName: "dropdownWidth", publicName: "dropdownWidth", isSignal: true, isRequired: false, transformFunction: null }, dropdownMaxHeight: { classPropertyName: "dropdownMaxHeight", publicName: "dropdownMaxHeight", isSignal: true, isRequired: false, transformFunction: null }, appendToBody: { classPropertyName: "appendToBody", publicName: "appendToBody", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, floatLabel: { classPropertyName: "floatLabel", publicName: "floatLabel", isSignal: true, isRequired: false, transformFunction: null }, floatLabelShowPlaceholder: { classPropertyName: "floatLabelShowPlaceholder", publicName: "floatLabelShowPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, ripple: { classPropertyName: "ripple", publicName: "ripple", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.gog-host--auto-width": "!fullWidth()" } }, queries: [{ propertyName: "chevronSlot", first: true, predicate: GogDropdownChevronDirective, descendants: true, isSignal: true }, { propertyName: "optionSlot", first: true, predicate: GogDropdownOptionDirective, descendants: true, isSignal: true }], ngImport: i0 });
2245
+ }
2246
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: GogDropdownBase, decorators: [{
2247
+ type: Directive,
2248
+ args: [{
2249
+ host: {
2250
+ // Drives the :host(.gog-host--auto-width) rules in each subclass's stylesheet —
2251
+ // without this binding the `fullWidth` input has no visible effect. Inverted from
2252
+ // gog-button's full-width class: these controls are full width by default, so the
2253
+ // class only appears once a consumer opts *out* of that.
2254
+ '[class.gog-host--auto-width]': '!fullWidth()',
2255
+ },
2256
+ }]
2257
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: false }] }], optionValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValue", required: false }] }], optionDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionDisabled", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], clearAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearAriaLabel", required: false }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidth", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], filterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPlaceholder", required: false }] }], filterPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPosition", required: false }] }], filterEmptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterEmptyMessage", required: false }] }], filterMatch: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterMatch", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], errorDisplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorDisplay", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], dropdownDirection: [{ type: i0.Input, args: [{ isSignal: true, alias: "dropdownDirection", required: false }] }], dropdownZIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "dropdownZIndex", required: false }] }], dropdownWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "dropdownWidth", required: false }] }], dropdownMaxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "dropdownMaxHeight", required: false }] }], appendToBody: [{ type: i0.Input, args: [{ isSignal: true, alias: "appendToBody", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], chevronSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => GogDropdownChevronDirective), { isSignal: true }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], floatLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "floatLabel", required: false }] }], floatLabelShowPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "floatLabelShowPlaceholder", required: false }] }], ripple: [{ type: i0.Input, args: [{ isSignal: true, alias: "ripple", required: false }] }], optionSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => GogDropdownOptionDirective), { isSignal: true }] }] } });
2258
+ function readPx$1(raw, fallback) {
2259
+ const parsed = Number.parseFloat(raw.trim());
2260
+ return Number.isFinite(parsed) ? parsed : fallback;
2261
+ }
2262
+
2263
+ /**
2264
+ * Icons an app has registered, keyed by the name `gog-icon` will be asked for. Resolves to `{}`
2265
+ * — only the built-ins are available — until a `provideGogIcons(...)` call fills it in.
2266
+ *
2267
+ * Values are raw `<svg>` markup, injected with `bypassSecurityTrustHtml` exactly like the
2268
+ * built-ins. See `provideGogIcons` for what that means for you.
2269
+ */
2270
+ const GOG_ICONS = new InjectionToken('GOG_ICONS', {
2271
+ providedIn: 'root',
2272
+ factory: () => ({}),
2273
+ });
2274
+ /**
2275
+ * Registers icons by name, so `<gog-icon name="cart" />` works for glyphs the library does not
2276
+ * ship. This is the supported way to use your own icon set: the alternative — a `TemplateRef`
2277
+ * per instance through the `template` input — costs a `<ng-template>` at every use site and is
2278
+ * meant for one-offs, not for an icon set.
2279
+ *
2280
+ * ```ts
2281
+ * // app.config.ts
2282
+ * providers: [
2283
+ * provideGogIcons({
2284
+ * cart: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">…</svg>',
2285
+ * user: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">…</svg>',
2286
+ * }),
2287
+ * ]
2288
+ * ```
2289
+ *
2290
+ * **A registered name overrides a built-in of the same name**, which is how you swap the
2291
+ * library's checkmark or chevron for your own without touching every component that renders one.
2292
+ *
2293
+ * **Providing this again further down the injector tree layers onto the parent's set** rather
2294
+ * than replacing it, matching `provideGogConfig`: a lazy feature can register the three icons
2295
+ * only it uses, and the app-wide set stays available inside it.
2296
+ *
2297
+ * ## What to put in the SVG
2298
+ *
2299
+ * Use `stroke="currentColor"` (or `fill="currentColor"`) and a `viewBox`, and leave the sizing
2300
+ * alone — `gog-icon`'s stylesheet drives width, height and stroke width from the
2301
+ * `--gog-icon-*` tokens, so an icon inherits size and colour from wherever it is used, the same
2302
+ * as a built-in.
2303
+ *
2304
+ * ## Security
2305
+ *
2306
+ * The markup is inserted with `DomSanitizer.bypassSecurityTrustHtml`, because Angular's HTML
2307
+ * sanitizer strips SVG and would leave you with nothing. That is safe for what this is for —
2308
+ * static icon markup you wrote or imported at build time — and unsafe for anything derived from
2309
+ * user input or fetched at runtime. **Never build a registered icon string from data you did
2310
+ * not author.** If you need remote icons, fetch them yourself, sanitize them with a real SVG
2311
+ * sanitizer, and register the result.
2312
+ */
2313
+ function provideGogIcons(icons) {
2314
+ return {
2315
+ provide: GOG_ICONS,
2316
+ // skipSelf so this reads the *parent* injector's set rather than recursing into the provider
2317
+ // being defined here; optional because at the root there is no parent providing it.
2318
+ useFactory: () => ({
2319
+ ...(inject(GOG_ICONS, { skipSelf: true, optional: true }) ?? {}),
2320
+ ...icons,
2321
+ }),
2322
+ };
2323
+ }
2324
+
2325
+ /*
2326
+ * The glyphs are from **Lucide** (https://lucide.dev), ISC licensed:
2327
+ *
2328
+ * Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather
2329
+ * (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2022.
2330
+ *
2331
+ * Permission to use, copy, modify, and/or distribute this software for any purpose with or
2332
+ * without fee is hereby granted, provided that the above copyright notice and this permission
2333
+ * notice appear in all copies.
2334
+ *
2335
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO
2336
+ * THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT
2337
+ * SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR
2338
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
2339
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
2340
+ * OR PERFORMANCE OF THIS SOFTWARE.
2341
+ *
2342
+ * They are inlined rather than pulled from `lucide` so the package keeps zero runtime
2343
+ * dependencies. Anything added here must be a Lucide glyph too: a 24×24 grid, `stroke-width`
2344
+ * 2, round caps and joins. Mixing in a set drawn for a different weight (Heroicons is drawn for
2345
+ * 1.5) shows up immediately as uneven visual mass in a row of icons.
2346
+ *
2347
+ * **A new glyph centres its ink in that 24×24 box**, and `npm run check:geometry` measures it
2348
+ * (L7, `docs/component-geometry.md`): centring the box has to centre the mark, because that is
2349
+ * all any caller does. A *filled* glyph is measured on its area rather than its extent — a solid
2350
+ * triangle's centroid sits a sixth of its width from the middle of its bounding box, which is the
2351
+ * one case where drawing inside a centred box still reads as off-centre.
2352
+ *
2353
+ * An app that wants a different set does not need a fork — `provideGogIcons` overrides any of
2354
+ * these by name.
2355
+ */
2356
+ const ICON_DEFS = {
2357
+ check: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>`,
2358
+ close: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>`,
2359
+ 'chevron-up': `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-up-icon lucide-chevron-up"><path d="m18 15-6-6-6 6"/></svg>`,
2360
+ 'chevron-down': `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>`,
2361
+ 'chevron-left': `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-left-icon lucide-chevron-left"><path d="m15 18-6-6 6-6"/></svg>`,
2362
+ 'chevron-right': `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-right-icon lucide-chevron-right"><path d="m9 18 6-6-6-6"/></svg>`,
2363
+ calendar: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-calendar-icon lucide-calendar"><path d="M8 2v4"/><path d="M16 2v4"/><rect width="18" height="18" x="3" y="4" rx="2"/><path d="M3 10h18"/></svg>`,
2364
+ clock: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clock-icon lucide-clock"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="10"/></svg>`,
2365
+ sort: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-up-icon lucide-arrow-down-up"><path d="m3 16 4 4 4-4"/><path d="M7 20V4"/><path d="m21 8-4-4-4 4"/><path d="M17 4v16"/></svg>`,
2366
+ 'sort-up': `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-narrow-wide-icon lucide-arrow-up-narrow-wide"><path d="m3 8 4-4 4 4"/><path d="M7 4v16"/><path d="M11 12h4"/><path d="M11 16h7"/><path d="M11 20h10"/></svg>`,
2367
+ 'sort-down': `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-wide-narrow-icon lucide-arrow-down-wide-narrow"><path d="m3 16 4 4 4-4"/><path d="M7 20V4"/><path d="M11 4h10"/><path d="M11 8h7"/><path d="M11 12h4"/></svg>`,
2368
+ success: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>`,
2369
+ error: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bug-icon lucide-bug"><path d="M12 20v-9"/><path d="M14 7a4 4 0 0 1 4 4v3a6 6 0 0 1-12 0v-3a4 4 0 0 1 4-4z"/><path d="M14.12 3.88 16 2"/><path d="M21 21a4 4 0 0 0-3.81-4"/><path d="M21 5a4 4 0 0 1-3.55 3.97"/><path d="M22 13h-4"/><path d="M3 21a4 4 0 0 1 3.81-4"/><path d="M3 5a4 4 0 0 0 3.55 3.97"/><path d="M6 13H2"/><path d="m8 2 1.88 1.88"/><path d="M9 7.13V6a3 3 0 1 1 6 0v1.13"/></svg>`,
2370
+ warning: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-triangle-alert-icon lucide-triangle-alert"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>`,
2371
+ info: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-info-icon lucide-info"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>`,
2372
+ checkbox: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-icon lucide-square"><rect width="18" height="18" x="3" y="3" rx="2"/></svg>`,
2373
+ 'checkbox-checked': `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>`,
2374
+ eye: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-eye-icon lucide-eye"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"/><circle cx="12" cy="12" r="3"/></svg>`,
2375
+ 'eye-off': `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-eye-off-icon lucide-eye-off"><path d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"/><path d="M14.084 14.158a3 3 0 0 1-4.242-4.242"/><path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"/><path d="m2 2 20 20"/></svg>`,
2376
+ copy: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy-icon lucide-copy"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>`,
2377
+ /* ── Actions ─────────────────────────────────────────────────────────────── */
2378
+ search: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>`,
2379
+ plus: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-plus"><path d="M5 12h14"/><path d="M12 5v14"/></svg>`,
2380
+ minus: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-minus"><path d="M5 12h14"/></svg>`,
2381
+ trash: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-trash-2"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/></svg>`,
2382
+ pencil: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-pencil"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/><path d="m15 5 4 4"/></svg>`,
2383
+ download: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>`,
2384
+ upload: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-upload"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" x2="12" y1="3" y2="15"/></svg>`,
2385
+ refresh: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-refresh-cw"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M8 16H3v5"/></svg>`,
2386
+ filter: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-filter"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>`,
2387
+ 'external-link': `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg>`,
2388
+ /* ── Navigation & chrome ─────────────────────────────────────────────────── */
2389
+ menu: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-menu"><line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/></svg>`,
2390
+ 'more-horizontal': `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-ellipsis"><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></svg>`,
2391
+ 'more-vertical': `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-ellipsis-vertical"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>`,
2392
+ 'arrow-left': `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-left"><path d="m12 19-7-7 7-7"/><path d="M19 12H5"/></svg>`,
2393
+ 'arrow-right': `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-right"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>`,
2394
+ /* ── Objects & state ─────────────────────────────────────────────────────── */
2395
+ user: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-user"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>`,
2396
+ settings: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-settings"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>`,
2397
+ lock: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-lock"><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>`,
2398
+ mail: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-mail"><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>`,
2399
+ star: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-star"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>`,
2400
+ 'star-filled': `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-star"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" fill="currentColor"/></svg>`,
2401
+ };
2402
+
2403
+ // GENERATED FILE — do not edit by hand.
2404
+ // Run `npm run generate:tokens` after changing src/styles/theme.css.
2405
+ // See scripts/generate-tokens.mjs for why theme.css is the source and this is the output.
2406
+ /**
2407
+ * The token catalogue, grouped as theme.css groups it. Exported so a theme editor or docs page
2408
+ * can enumerate the real tokens instead of keeping a hand-copied list that drifts.
2409
+ */
2410
+ const GOG_TOKEN_GROUPS = [
2411
+ {
2412
+ section: 'Typography',
2413
+ layer: 'foundation',
2414
+ tokens: [
2415
+ '--gog-font-body',
2416
+ '--gog-font-heading',
2417
+ '--gog-font-mono',
2418
+ '--gog-font-weight-bold',
2419
+ '--gog-font-weight-heavy',
2420
+ '--gog-font-weight-medium',
2421
+ '--gog-font-weight-semibold',
2422
+ '--gog-letter-spacing',
2423
+ '--gog-line-height-loose',
2424
+ '--gog-line-height-none',
2425
+ '--gog-line-height-normal',
2426
+ '--gog-line-height-relaxed',
2427
+ '--gog-line-height-snug',
2428
+ '--gog-line-height-tight',
2429
+ '--gog-text-2xl',
2430
+ '--gog-text-2xs',
2431
+ '--gog-text-3xl',
2432
+ '--gog-text-lg',
2433
+ '--gog-text-md',
2434
+ '--gog-text-slg',
2435
+ '--gog-text-sm',
2436
+ '--gog-text-transform',
2437
+ '--gog-text-xl',
2438
+ '--gog-text-xs',
2439
+ ],
2440
+ },
2441
+ {
2442
+ section: 'Spacing & geometry',
2443
+ layer: 'foundation',
2444
+ tokens: [
2445
+ '--gog-border-style',
2446
+ '--gog-border-width',
2447
+ '--gog-density',
2448
+ '--gog-panel-border-style',
2449
+ '--gog-panel-border-width',
2450
+ '--gog-radius',
2451
+ ],
2452
+ },
2453
+ {
2454
+ section: 'Motion',
2455
+ layer: 'foundation',
2456
+ tokens: ['--gog-duration-base', '--gog-duration-fast', '--gog-duration-slow', '--gog-easing'],
2457
+ },
2458
+ {
2459
+ section: 'Focus & disabled',
2460
+ layer: 'foundation',
2461
+ tokens: ['--gog-disabled-opacity', '--gog-focus-ring-offset', '--gog-focus-ring-width'],
2462
+ },
2463
+ {
2464
+ section: 'Control metrics',
2465
+ layer: 'foundation',
2466
+ tokens: [
2467
+ '--gog-control-border-style',
2468
+ '--gog-control-border-width',
2469
+ '--gog-control-checkbox-box-size-lg',
2470
+ '--gog-control-checkbox-box-size-md',
2471
+ '--gog-control-checkbox-box-size-slg',
2472
+ '--gog-control-checkbox-box-size-sm',
2473
+ '--gog-control-checkbox-box-size-xsm',
2474
+ '--gog-control-checkbox-icon-size-lg',
2475
+ '--gog-control-checkbox-icon-size-md',
2476
+ '--gog-control-checkbox-icon-size-slg',
2477
+ '--gog-control-checkbox-icon-size-sm',
2478
+ '--gog-control-checkbox-icon-size-xsm',
2479
+ '--gog-control-checkbox-label-size-lg',
2480
+ '--gog-control-checkbox-label-size-md',
2481
+ '--gog-control-checkbox-label-size-slg',
2482
+ '--gog-control-checkbox-label-size-sm',
2483
+ '--gog-control-checkbox-label-size-xsm',
2484
+ '--gog-control-clear-icon-ratio',
2485
+ ],
2486
+ },
2487
+ {
2488
+ section: 'Field sizing (input / select / multiselect share one scale)',
2489
+ layer: 'foundation',
2490
+ tokens: ['--gog-field-icon-glyph', '--gog-field-icon-glyph-sm', '--gog-field-icon-glyph-xsm'],
2491
+ },
2492
+ {
2493
+ section: 'Float label geometry (input / textarea / select / multiselect)',
2494
+ layer: 'foundation',
2495
+ tokens: [
2496
+ '--gog-field-float-label-in-top',
2497
+ '--gog-field-float-label-over-reserve',
2498
+ '--gog-field-float-label-reserve',
2499
+ ],
2500
+ },
2501
+ {
2502
+ section: 'Elevation',
2503
+ layer: 'foundation',
2504
+ tokens: [
2505
+ '--gog-elevation-ambient-alpha',
2506
+ '--gog-elevation-contact-blur',
2507
+ '--gog-elevation-highlight-alpha',
2508
+ '--gog-elevation-highlight-ink',
2509
+ '--gog-elevation-ink',
2510
+ '--gog-elevation-key-alpha',
2511
+ '--gog-elevation-key-blur',
2512
+ '--gog-elevation-key-x',
2513
+ '--gog-elevation-key-y',
2514
+ '--gog-elevation-ring-width',
2515
+ ],
2516
+ },
2517
+ {
2518
+ section: 'Icon & spinner',
2519
+ layer: 'foundation',
2520
+ tokens: [
2521
+ '--gog-icon-size',
2522
+ '--gog-icon-stroke-width',
2523
+ '--gog-spinner-rune-size',
2524
+ '--gog-spinner-size-lg',
2525
+ '--gog-spinner-size-md',
2526
+ '--gog-spinner-size-slg',
2527
+ '--gog-spinner-size-sm',
2528
+ '--gog-spinner-size-xsm',
2529
+ ],
2530
+ },
2531
+ {
2532
+ section: 'Toast stack',
2533
+ layer: 'foundation',
2534
+ tokens: ['--gog-toast-enter-distance', '--gog-toast-min-width', '--gog-toast-z-index'],
2535
+ },
2536
+ {
2537
+ section: 'Overlay stacking',
2538
+ layer: 'foundation',
2539
+ tokens: ['--gog-z-base'],
2540
+ },
2541
+ {
2542
+ section: 'Light palette (also the no-data-theme default)',
2543
+ layer: 'foundation',
2544
+ tokens: [
2545
+ '--gog-accent-bright',
2546
+ '--gog-accent-color',
2547
+ '--gog-accent-dim',
2548
+ '--gog-accent-pale',
2549
+ '--gog-accent-text-color',
2550
+ '--gog-background-color',
2551
+ '--gog-border-color',
2552
+ '--gog-control-boundary-color',
2553
+ '--gog-danger-color',
2554
+ '--gog-elevation-ambient-alpha',
2555
+ '--gog-elevation-contact-blur',
2556
+ '--gog-elevation-highlight-alpha',
2557
+ '--gog-elevation-highlight-ink',
2558
+ '--gog-elevation-ink',
2559
+ '--gog-elevation-key-alpha',
2560
+ '--gog-elevation-key-blur',
2561
+ '--gog-elevation-key-x',
2562
+ '--gog-elevation-key-y',
2563
+ '--gog-elevation-ring-width',
2564
+ '--gog-hover-color',
2565
+ '--gog-info-color',
2566
+ '--gog-muted-text-color',
2567
+ '--gog-primary-color',
2568
+ '--gog-secondary-color',
2569
+ '--gog-spinner-overlay-bg',
2570
+ '--gog-success-color',
2571
+ '--gog-surface-color',
2572
+ '--gog-text-color',
2573
+ '--gog-warning-color',
2574
+ ],
2575
+ },
2576
+ {
2577
+ section: 'The elevation ladder',
2578
+ layer: 'component',
2579
+ tokens: [
2580
+ '--gog-dialog-shadow',
2581
+ '--gog-elevation-0',
2582
+ '--gog-elevation-1',
2583
+ '--gog-elevation-2',
2584
+ '--gog-elevation-3',
2585
+ '--gog-elevation-4',
2586
+ '--gog-elevation-5',
2587
+ '--gog-elevation-contact',
2588
+ '--gog-elevation-highlight',
2589
+ '--gog-elevation-ring',
2590
+ '--gog-panel-shadow',
2591
+ '--gog-toast-shadow',
2592
+ '--gog-toggle-thumb-shadow',
2593
+ ],
2594
+ },
2595
+ {
2596
+ section: 'Text on a status fill',
2597
+ layer: 'component',
2598
+ tokens: [
2599
+ '--gog-danger-shade',
2600
+ '--gog-danger-text-color',
2601
+ '--gog-info-shade',
2602
+ '--gog-info-text-color',
2603
+ '--gog-success-shade',
2604
+ '--gog-success-text-color',
2605
+ '--gog-warning-shade',
2606
+ '--gog-warning-text-color',
2607
+ ],
2608
+ },
2609
+ {
2610
+ section: 'Stacking layers',
2611
+ layer: 'component',
2612
+ tokens: [
2613
+ '--gog-badge-z',
2614
+ '--gog-control-checkbox-padding',
2615
+ '--gog-control-icon-offset',
2616
+ '--gog-control-padding-x',
2617
+ '--gog-control-padding-y',
2618
+ '--gog-dropdown-z',
2619
+ '--gog-elevated-surface-color',
2620
+ '--gog-field-error-line-height',
2621
+ '--gog-field-float-label-over-gap',
2622
+ '--gog-field-label-line-height',
2623
+ '--gog-field-lg-font-size',
2624
+ '--gog-field-lg-icon-inset',
2625
+ '--gog-field-lg-icon-offset',
2626
+ '--gog-field-lg-padding-x',
2627
+ '--gog-field-lg-padding-y',
2628
+ '--gog-field-line-height',
2629
+ '--gog-field-md-font-size',
2630
+ '--gog-field-md-icon-inset',
2631
+ '--gog-field-md-icon-offset',
2632
+ '--gog-field-md-padding-x',
2633
+ '--gog-field-md-padding-y',
2634
+ '--gog-field-slg-font-size',
2635
+ '--gog-field-slg-icon-inset',
2636
+ '--gog-field-slg-icon-offset',
2637
+ '--gog-field-slg-padding-x',
2638
+ '--gog-field-slg-padding-y',
2639
+ '--gog-field-sm-font-size',
2640
+ '--gog-field-sm-icon-inset',
2641
+ '--gog-field-sm-icon-offset',
2642
+ '--gog-field-sm-padding-x',
2643
+ '--gog-field-sm-padding-y',
2644
+ '--gog-field-xsm-font-size',
2645
+ '--gog-field-xsm-icon-inset',
2646
+ '--gog-field-xsm-icon-offset',
2647
+ '--gog-field-xsm-padding-x',
2648
+ '--gog-field-xsm-padding-y',
2649
+ '--gog-panel-radius',
2650
+ '--gog-space-12',
2651
+ '--gog-space-16',
2652
+ '--gog-space-20',
2653
+ '--gog-space-24',
2654
+ '--gog-space-28',
2655
+ '--gog-space-2xl',
2656
+ '--gog-space-32',
2657
+ '--gog-space-4',
2658
+ '--gog-space-40',
2659
+ '--gog-space-48',
2660
+ '--gog-space-8',
2661
+ '--gog-space-lg',
2662
+ '--gog-space-md',
2663
+ '--gog-space-sm',
2664
+ '--gog-space-xs',
2665
+ '--gog-spinner-overlay-z',
2666
+ '--gog-toast-base-z',
2667
+ '--gog-toast-max-width',
2668
+ '--gog-toast-stack-padding',
2669
+ '--gog-tooltip-z',
2670
+ ],
2671
+ },
2672
+ {
2673
+ section: 'Accordion',
2674
+ layer: 'component',
2675
+ tokens: [
2676
+ '--gog-accordion-accent-color',
2677
+ '--gog-accordion-body-bg',
2678
+ '--gog-accordion-body-color',
2679
+ '--gog-accordion-body-font-family',
2680
+ '--gog-accordion-body-lift',
2681
+ '--gog-accordion-body-line-height',
2682
+ '--gog-accordion-body-radius',
2683
+ '--gog-accordion-body-transition-duration',
2684
+ '--gog-accordion-border-color',
2685
+ '--gog-accordion-border-style',
2686
+ '--gog-accordion-border-width',
2687
+ '--gog-accordion-chevron-line-height',
2688
+ '--gog-accordion-chevron-transition-duration',
2689
+ '--gog-accordion-disabled-opacity',
2690
+ '--gog-accordion-focus-ring-color',
2691
+ '--gog-accordion-focus-ring-width',
2692
+ '--gog-accordion-font-family',
2693
+ '--gog-accordion-header-bg',
2694
+ '--gog-accordion-header-gap',
2695
+ '--gog-accordion-header-text-transform',
2696
+ '--gog-accordion-hover-bg',
2697
+ '--gog-accordion-hover-color',
2698
+ '--gog-accordion-lg-body-font-size',
2699
+ '--gog-accordion-lg-body-line-height',
2700
+ '--gog-accordion-lg-body-padding-bottom',
2701
+ '--gog-accordion-lg-body-padding-top',
2702
+ '--gog-accordion-lg-chevron-font-size',
2703
+ '--gog-accordion-lg-chevron-size',
2704
+ '--gog-accordion-lg-content-gap',
2705
+ '--gog-accordion-lg-font-size',
2706
+ '--gog-accordion-lg-letter-spacing',
2707
+ '--gog-accordion-lg-padding-x',
2708
+ '--gog-accordion-lg-padding-y',
2709
+ '--gog-accordion-line-height',
2710
+ '--gog-accordion-md-body-font-size',
2711
+ '--gog-accordion-md-body-line-height',
2712
+ '--gog-accordion-md-body-padding-bottom',
2713
+ '--gog-accordion-md-body-padding-top',
2714
+ '--gog-accordion-md-chevron-font-size',
2715
+ '--gog-accordion-md-chevron-size',
2716
+ '--gog-accordion-md-content-gap',
2717
+ '--gog-accordion-md-font-size',
2718
+ '--gog-accordion-md-letter-spacing',
2719
+ '--gog-accordion-md-padding-x',
2720
+ '--gog-accordion-md-padding-y',
2721
+ '--gog-accordion-press-bg',
2722
+ '--gog-accordion-radius',
2723
+ '--gog-accordion-slg-body-font-size',
2724
+ '--gog-accordion-slg-body-line-height',
2725
+ '--gog-accordion-slg-body-padding-bottom',
2726
+ '--gog-accordion-slg-body-padding-top',
2727
+ '--gog-accordion-slg-chevron-font-size',
2728
+ '--gog-accordion-slg-chevron-size',
2729
+ '--gog-accordion-slg-content-gap',
2730
+ '--gog-accordion-slg-font-size',
2731
+ '--gog-accordion-slg-letter-spacing',
2732
+ '--gog-accordion-slg-padding-x',
2733
+ '--gog-accordion-slg-padding-y',
2734
+ '--gog-accordion-sm-body-font-size',
2735
+ '--gog-accordion-sm-body-line-height',
2736
+ '--gog-accordion-sm-body-padding-bottom',
2737
+ '--gog-accordion-sm-body-padding-top',
2738
+ '--gog-accordion-sm-chevron-font-size',
2739
+ '--gog-accordion-sm-chevron-size',
2740
+ '--gog-accordion-sm-content-gap',
2741
+ '--gog-accordion-sm-font-size',
2742
+ '--gog-accordion-sm-letter-spacing',
2743
+ '--gog-accordion-sm-padding-x',
2744
+ '--gog-accordion-sm-padding-y',
2745
+ '--gog-accordion-text-color',
2746
+ '--gog-accordion-transition-duration',
2747
+ '--gog-accordion-xsm-body-font-size',
2748
+ '--gog-accordion-xsm-body-line-height',
2749
+ '--gog-accordion-xsm-body-padding-bottom',
2750
+ '--gog-accordion-xsm-body-padding-top',
2751
+ '--gog-accordion-xsm-chevron-font-size',
2752
+ '--gog-accordion-xsm-chevron-size',
2753
+ '--gog-accordion-xsm-content-gap',
2754
+ '--gog-accordion-xsm-font-size',
2755
+ '--gog-accordion-xsm-letter-spacing',
2756
+ '--gog-accordion-xsm-padding-x',
2757
+ '--gog-accordion-xsm-padding-y',
2758
+ ],
2759
+ },
2760
+ {
2761
+ section: 'Alert',
2762
+ layer: 'component',
2763
+ tokens: [
2764
+ '--gog-alert-accent-color',
2765
+ '--gog-alert-bg',
2766
+ '--gog-alert-body-font-size',
2767
+ '--gog-alert-body-line-height',
2768
+ '--gog-alert-border-color',
2769
+ '--gog-alert-border-style',
2770
+ '--gog-alert-border-width',
2771
+ '--gog-alert-color',
2772
+ '--gog-alert-danger-color',
2773
+ '--gog-alert-edge-width',
2774
+ '--gog-alert-font-family',
2775
+ '--gog-alert-gap',
2776
+ '--gog-alert-heading-color',
2777
+ '--gog-alert-heading-font-size',
2778
+ '--gog-alert-heading-font-weight',
2779
+ '--gog-alert-heading-line-height',
2780
+ '--gog-alert-icon-font-size',
2781
+ '--gog-alert-icon-line-height',
2782
+ '--gog-alert-info-color',
2783
+ '--gog-alert-main-gap',
2784
+ '--gog-alert-padding-x',
2785
+ '--gog-alert-padding-y',
2786
+ '--gog-alert-radius',
2787
+ '--gog-alert-success-color',
2788
+ '--gog-alert-warning-color',
2789
+ ],
2790
+ },
2791
+ {
2792
+ section: 'Collapsible',
2793
+ layer: 'component',
2794
+ tokens: [
2795
+ '--gog-collapsible-disabled-opacity',
2796
+ '--gog-collapsible-max-height',
2797
+ '--gog-collapsible-transition-duration',
2798
+ ],
2799
+ },
2800
+ {
2801
+ section: 'Button',
2802
+ layer: 'component',
2803
+ tokens: [
2804
+ '--gog-button-active-scale',
2805
+ '--gog-button-border-style',
2806
+ '--gog-button-border-width',
2807
+ '--gog-button-disabled-opacity',
2808
+ '--gog-button-focus-ring-color',
2809
+ '--gog-button-focus-ring-offset',
2810
+ '--gog-button-focus-ring-width',
2811
+ '--gog-button-font-family',
2812
+ '--gog-button-font-weight',
2813
+ '--gog-button-gap',
2814
+ '--gog-button-ghost-bg',
2815
+ '--gog-button-ghost-border',
2816
+ '--gog-button-ghost-color',
2817
+ '--gog-button-ghost-hover-bg',
2818
+ '--gog-button-ghost-hover-color',
2819
+ '--gog-button-ghost-hover-shadow',
2820
+ '--gog-button-ghost-press-bg',
2821
+ '--gog-button-ghost-press-color',
2822
+ '--gog-button-ghost-shadow',
2823
+ '--gog-button-ghost-spinner-color',
2824
+ '--gog-button-ghost-toggled-shadow',
2825
+ '--gog-button-letter-spacing',
2826
+ '--gog-button-lg-font-size',
2827
+ '--gog-button-lg-padding',
2828
+ '--gog-button-line-height',
2829
+ '--gog-button-loading-opacity',
2830
+ '--gog-button-md-font-size',
2831
+ '--gog-button-md-padding',
2832
+ '--gog-button-outline-bg',
2833
+ '--gog-button-outline-border',
2834
+ '--gog-button-outline-color',
2835
+ '--gog-button-outline-hover-bg',
2836
+ '--gog-button-outline-hover-color',
2837
+ '--gog-button-outline-hover-shadow',
2838
+ '--gog-button-outline-press-bg',
2839
+ '--gog-button-outline-press-color',
2840
+ '--gog-button-outline-shadow',
2841
+ '--gog-button-outline-spinner-color',
2842
+ '--gog-button-outline-toggled-shadow',
2843
+ '--gog-button-primary-bg',
2844
+ '--gog-button-primary-border',
2845
+ '--gog-button-primary-color',
2846
+ '--gog-button-primary-hover-bg',
2847
+ '--gog-button-primary-hover-color',
2848
+ '--gog-button-primary-hover-shadow',
2849
+ '--gog-button-primary-press-bg',
2850
+ '--gog-button-primary-press-color',
2851
+ '--gog-button-primary-shadow',
2852
+ '--gog-button-primary-spinner-color',
2853
+ '--gog-button-primary-toggled-shadow',
2854
+ '--gog-button-radius',
2855
+ '--gog-button-secondary-bg',
2856
+ '--gog-button-secondary-border',
2857
+ '--gog-button-secondary-color',
2858
+ '--gog-button-secondary-hover-bg',
2859
+ '--gog-button-secondary-hover-color',
2860
+ '--gog-button-secondary-hover-shadow',
2861
+ '--gog-button-secondary-press-bg',
2862
+ '--gog-button-secondary-press-color',
2863
+ '--gog-button-secondary-shadow',
2864
+ '--gog-button-secondary-spinner-color',
2865
+ '--gog-button-secondary-toggled-shadow',
2866
+ '--gog-button-slg-font-size',
2867
+ '--gog-button-slg-padding',
2868
+ '--gog-button-sm-font-size',
2869
+ '--gog-button-sm-padding',
2870
+ '--gog-button-spinner-max-size',
2871
+ '--gog-button-text-transform',
2872
+ '--gog-button-toggled-ring-width',
2873
+ '--gog-button-transition-duration',
2874
+ '--gog-button-xsm-font-size',
2875
+ '--gog-button-xsm-padding',
2876
+ ],
2877
+ },
2878
+ {
2879
+ section: 'Button severity',
2880
+ layer: 'component',
2881
+ tokens: [
2882
+ '--gog-button-danger-fill',
2883
+ '--gog-button-danger-fill-hover',
2884
+ '--gog-button-danger-fill-press',
2885
+ '--gog-button-danger-ink',
2886
+ '--gog-button-danger-on-fill',
2887
+ '--gog-button-danger-wash',
2888
+ '--gog-button-info-fill',
2889
+ '--gog-button-info-fill-hover',
2890
+ '--gog-button-info-fill-press',
2891
+ '--gog-button-info-ink',
2892
+ '--gog-button-info-on-fill',
2893
+ '--gog-button-info-wash',
2894
+ '--gog-button-success-fill',
2895
+ '--gog-button-success-fill-hover',
2896
+ '--gog-button-success-fill-press',
2897
+ '--gog-button-success-ink',
2898
+ '--gog-button-success-on-fill',
2899
+ '--gog-button-success-wash',
2900
+ '--gog-button-warning-fill',
2901
+ '--gog-button-warning-fill-hover',
2902
+ '--gog-button-warning-fill-press',
2903
+ '--gog-button-warning-ink',
2904
+ '--gog-button-warning-on-fill',
2905
+ '--gog-button-warning-wash',
2906
+ ],
2907
+ },
2908
+ {
2909
+ section: 'Button toggle group',
2910
+ layer: 'component',
2911
+ tokens: [
2912
+ '--gog-button-toggle-border-color',
2913
+ '--gog-button-toggle-border-style',
2914
+ '--gog-button-toggle-border-width',
2915
+ '--gog-button-toggle-disabled-opacity',
2916
+ '--gog-button-toggle-focus-ring-color',
2917
+ '--gog-button-toggle-focus-ring-offset',
2918
+ '--gog-button-toggle-focus-ring-width',
2919
+ '--gog-button-toggle-font-family',
2920
+ '--gog-button-toggle-font-weight',
2921
+ '--gog-button-toggle-gap',
2922
+ '--gog-button-toggle-hover-bg',
2923
+ '--gog-button-toggle-hover-color',
2924
+ '--gog-button-toggle-icon-size',
2925
+ '--gog-button-toggle-letter-spacing',
2926
+ '--gog-button-toggle-line-height',
2927
+ '--gog-button-toggle-press-bg',
2928
+ '--gog-button-toggle-radius',
2929
+ '--gog-button-toggle-rest-bg',
2930
+ '--gog-button-toggle-rest-color',
2931
+ '--gog-button-toggle-selected-bg',
2932
+ '--gog-button-toggle-selected-border-color',
2933
+ '--gog-button-toggle-selected-color',
2934
+ '--gog-button-toggle-selected-press-bg',
2935
+ '--gog-button-toggle-separated-gap',
2936
+ '--gog-button-toggle-text-transform',
2937
+ '--gog-button-toggle-transition-duration',
2938
+ ],
2939
+ },
2940
+ {
2941
+ section: 'Calendar (the month grid inside gog-datepicker, and gog-calendar on its own)',
2942
+ layer: 'component',
2943
+ tokens: [
2944
+ '--gog-calendar-action-padding',
2945
+ '--gog-calendar-color',
2946
+ '--gog-calendar-day-border-style',
2947
+ '--gog-calendar-day-border-width',
2948
+ '--gog-calendar-day-hover-bg',
2949
+ '--gog-calendar-day-outside-color',
2950
+ '--gog-calendar-day-radius',
2951
+ '--gog-calendar-day-rest-bg',
2952
+ '--gog-calendar-day-rest-color',
2953
+ '--gog-calendar-disabled-opacity',
2954
+ '--gog-calendar-divider-color',
2955
+ '--gog-calendar-divider-style',
2956
+ '--gog-calendar-divider-width',
2957
+ '--gog-calendar-focus-ring-color',
2958
+ '--gog-calendar-focus-ring-offset',
2959
+ '--gog-calendar-focus-ring-width',
2960
+ '--gog-calendar-font-family',
2961
+ '--gog-calendar-footer-gap',
2962
+ '--gog-calendar-header-gap',
2963
+ '--gog-calendar-header-margin',
2964
+ '--gog-calendar-lg-day-size',
2965
+ '--gog-calendar-lg-font-size',
2966
+ '--gog-calendar-line-height',
2967
+ '--gog-calendar-max-width',
2968
+ '--gog-calendar-md-day-size',
2969
+ '--gog-calendar-md-font-size',
2970
+ '--gog-calendar-months-gap',
2971
+ '--gog-calendar-nav-bg',
2972
+ '--gog-calendar-nav-color',
2973
+ '--gog-calendar-nav-hover-bg',
2974
+ '--gog-calendar-nav-hover-color',
2975
+ '--gog-calendar-nav-icon-overlap',
2976
+ '--gog-calendar-nav-icon-size',
2977
+ '--gog-calendar-nav-radius',
2978
+ '--gog-calendar-nav-size',
2979
+ '--gog-calendar-padding',
2980
+ '--gog-calendar-range-bg',
2981
+ '--gog-calendar-range-color',
2982
+ '--gog-calendar-selected-bg',
2983
+ '--gog-calendar-selected-color',
2984
+ '--gog-calendar-selected-font-weight',
2985
+ '--gog-calendar-slg-day-size',
2986
+ '--gog-calendar-slg-font-size',
2987
+ '--gog-calendar-sm-day-size',
2988
+ '--gog-calendar-sm-font-size',
2989
+ '--gog-calendar-time-gap',
2990
+ '--gog-calendar-time-input-bg',
2991
+ '--gog-calendar-time-input-padding',
2992
+ '--gog-calendar-time-input-width',
2993
+ '--gog-calendar-time-margin',
2994
+ '--gog-calendar-title-color',
2995
+ '--gog-calendar-title-font-weight',
2996
+ '--gog-calendar-today-border-color',
2997
+ '--gog-calendar-today-font-weight',
2998
+ '--gog-calendar-transition-duration',
2999
+ '--gog-calendar-weekday-color',
3000
+ '--gog-calendar-weekday-font-size',
3001
+ '--gog-calendar-weekday-font-weight',
3002
+ '--gog-calendar-weekday-line-height',
3003
+ '--gog-calendar-weekday-padding',
3004
+ '--gog-calendar-weekday-text-transform',
3005
+ '--gog-calendar-xsm-day-size',
3006
+ '--gog-calendar-xsm-font-size',
3007
+ ],
3008
+ },
3009
+ {
3010
+ section: 'Card',
3011
+ layer: 'component',
3012
+ tokens: [
3013
+ '--gog-card-border-style',
3014
+ '--gog-card-border-width',
3015
+ '--gog-card-color',
3016
+ '--gog-card-disabled-opacity',
3017
+ '--gog-card-elevated-bg',
3018
+ '--gog-card-elevated-border-color',
3019
+ '--gog-card-elevated-shadow',
3020
+ '--gog-card-filled-bg',
3021
+ '--gog-card-filled-border-color',
3022
+ '--gog-card-filled-shadow',
3023
+ '--gog-card-focus-ring',
3024
+ '--gog-card-focus-ring-offset',
3025
+ '--gog-card-focus-ring-width',
3026
+ '--gog-card-font-family',
3027
+ '--gog-card-footer-border-color',
3028
+ '--gog-card-footer-gap',
3029
+ '--gog-card-footer-padding-top',
3030
+ '--gog-card-heading-color',
3031
+ '--gog-card-heading-font-family',
3032
+ '--gog-card-heading-font-size',
3033
+ '--gog-card-heading-font-weight',
3034
+ '--gog-card-heading-line-height',
3035
+ '--gog-card-hover-border-color',
3036
+ '--gog-card-hover-shadow',
3037
+ '--gog-card-lg-gap',
3038
+ '--gog-card-lg-padding-x',
3039
+ '--gog-card-lg-padding-y',
3040
+ '--gog-card-md-gap',
3041
+ '--gog-card-md-padding-x',
3042
+ '--gog-card-md-padding-y',
3043
+ '--gog-card-outlined-bg',
3044
+ '--gog-card-outlined-border-color',
3045
+ '--gog-card-outlined-shadow',
3046
+ '--gog-card-radius',
3047
+ '--gog-card-slg-gap',
3048
+ '--gog-card-slg-padding-x',
3049
+ '--gog-card-slg-padding-y',
3050
+ '--gog-card-sm-gap',
3051
+ '--gog-card-sm-padding-x',
3052
+ '--gog-card-sm-padding-y',
3053
+ '--gog-card-transition-duration',
3054
+ '--gog-card-xsm-gap',
3055
+ '--gog-card-xsm-padding-x',
3056
+ '--gog-card-xsm-padding-y',
3057
+ ],
3058
+ },
3059
+ {
3060
+ section: 'Checkbox',
3061
+ layer: 'component',
3062
+ tokens: [
3063
+ '--gog-checkbox-bg',
3064
+ '--gog-checkbox-border-color',
3065
+ '--gog-checkbox-border-style',
3066
+ '--gog-checkbox-border-width',
3067
+ '--gog-checkbox-checked-bg',
3068
+ '--gog-checkbox-checked-border',
3069
+ '--gog-checkbox-dash-height',
3070
+ '--gog-checkbox-dash-radius',
3071
+ '--gog-checkbox-dash-width-ratio',
3072
+ '--gog-checkbox-disabled-opacity',
3073
+ '--gog-checkbox-focus-ring',
3074
+ '--gog-checkbox-focus-ring-offset',
3075
+ '--gog-checkbox-focus-ring-width',
3076
+ '--gog-checkbox-font-family',
3077
+ '--gog-checkbox-gap',
3078
+ '--gog-checkbox-icon-color',
3079
+ '--gog-checkbox-icon-line-height',
3080
+ '--gog-checkbox-label-color',
3081
+ '--gog-checkbox-label-line-height',
3082
+ '--gog-checkbox-radius',
3083
+ '--gog-checkbox-transition-duration',
3084
+ ],
3085
+ },
3086
+ {
3087
+ section: 'Radio group',
3088
+ layer: 'component',
3089
+ tokens: [
3090
+ '--gog-radio-bg',
3091
+ '--gog-radio-border-color',
3092
+ '--gog-radio-border-style',
3093
+ '--gog-radio-border-width',
3094
+ '--gog-radio-checked-bg',
3095
+ '--gog-radio-checked-border',
3096
+ '--gog-radio-disabled-opacity',
3097
+ '--gog-radio-dot-color',
3098
+ '--gog-radio-dot-size-ratio',
3099
+ '--gog-radio-error-color',
3100
+ '--gog-radio-error-font-size',
3101
+ '--gog-radio-error-line-height',
3102
+ '--gog-radio-focus-ring',
3103
+ '--gog-radio-focus-ring-offset',
3104
+ '--gog-radio-focus-ring-width',
3105
+ '--gog-radio-font-family',
3106
+ '--gog-radio-gap',
3107
+ '--gog-radio-group-gap',
3108
+ '--gog-radio-group-label-color',
3109
+ '--gog-radio-group-label-size',
3110
+ '--gog-radio-group-option-gap',
3111
+ '--gog-radio-group-option-gap-horizontal',
3112
+ '--gog-radio-label-color',
3113
+ '--gog-radio-label-line-height',
3114
+ '--gog-radio-transition-duration',
3115
+ ],
3116
+ },
3117
+ {
3118
+ section: 'Chip',
3119
+ layer: 'component',
3120
+ tokens: [
3121
+ '--gog-chip-avatar-inset-ratio',
3122
+ '--gog-chip-bg',
3123
+ '--gog-chip-border',
3124
+ '--gog-chip-border-style',
3125
+ '--gog-chip-border-width',
3126
+ '--gog-chip-color',
3127
+ '--gog-chip-disabled-opacity',
3128
+ '--gog-chip-focus-ring-color',
3129
+ '--gog-chip-focus-ring-offset',
3130
+ '--gog-chip-focus-ring-width',
3131
+ '--gog-chip-font-family',
3132
+ '--gog-chip-font-weight',
3133
+ '--gog-chip-hover-bg',
3134
+ '--gog-chip-lg-avatar-size',
3135
+ '--gog-chip-lg-font-size',
3136
+ '--gog-chip-lg-gap',
3137
+ '--gog-chip-lg-icon-size',
3138
+ '--gog-chip-lg-padding-block',
3139
+ '--gog-chip-lg-padding-inline',
3140
+ '--gog-chip-lg-remove-size',
3141
+ '--gog-chip-line-height',
3142
+ '--gog-chip-md-avatar-size',
3143
+ '--gog-chip-md-font-size',
3144
+ '--gog-chip-md-gap',
3145
+ '--gog-chip-md-icon-size',
3146
+ '--gog-chip-md-padding-block',
3147
+ '--gog-chip-md-padding-inline',
3148
+ '--gog-chip-md-remove-size',
3149
+ '--gog-chip-pill-radius',
3150
+ '--gog-chip-press-bg',
3151
+ '--gog-chip-radius',
3152
+ '--gog-chip-remove-color',
3153
+ '--gog-chip-remove-hover-color',
3154
+ '--gog-chip-remove-inset-ratio',
3155
+ '--gog-chip-remove-scale',
3156
+ '--gog-chip-selected-ring-width',
3157
+ '--gog-chip-selected-shadow',
3158
+ '--gog-chip-slg-avatar-size',
3159
+ '--gog-chip-slg-font-size',
3160
+ '--gog-chip-slg-gap',
3161
+ '--gog-chip-slg-icon-size',
3162
+ '--gog-chip-slg-padding-block',
3163
+ '--gog-chip-slg-padding-inline',
3164
+ '--gog-chip-slg-remove-size',
3165
+ '--gog-chip-sm-avatar-size',
3166
+ '--gog-chip-sm-font-size',
3167
+ '--gog-chip-sm-gap',
3168
+ '--gog-chip-sm-icon-size',
3169
+ '--gog-chip-sm-padding-block',
3170
+ '--gog-chip-sm-padding-inline',
3171
+ '--gog-chip-sm-remove-size',
3172
+ '--gog-chip-xsm-avatar-size',
3173
+ '--gog-chip-xsm-font-size',
3174
+ '--gog-chip-xsm-gap',
3175
+ '--gog-chip-xsm-icon-size',
3176
+ '--gog-chip-xsm-padding-block',
3177
+ '--gog-chip-xsm-padding-inline',
3178
+ '--gog-chip-xsm-remove-size',
3179
+ ],
3180
+ },
3181
+ {
3182
+ section: 'Dialog',
3183
+ layer: 'component',
3184
+ tokens: [
3185
+ '--gog-confirmation-dialog-actions-gap',
3186
+ '--gog-confirmation-dialog-actions-offset',
3187
+ '--gog-confirmation-dialog-color',
3188
+ '--gog-confirmation-dialog-description-color',
3189
+ '--gog-confirmation-dialog-description-font-size',
3190
+ '--gog-confirmation-dialog-description-line-height',
3191
+ '--gog-confirmation-dialog-gap',
3192
+ '--gog-confirmation-dialog-max-width',
3193
+ '--gog-confirmation-dialog-min-width',
3194
+ '--gog-confirmation-dialog-title-font-size',
3195
+ '--gog-confirmation-dialog-title-line-height',
3196
+ '--gog-dialog-backdrop-bg',
3197
+ '--gog-dialog-backdrop-blur',
3198
+ '--gog-dialog-backdrop-fade-duration',
3199
+ '--gog-dialog-backdrop-padding',
3200
+ '--gog-dialog-bg',
3201
+ '--gog-dialog-body-max-height',
3202
+ '--gog-dialog-body-padding',
3203
+ '--gog-dialog-border',
3204
+ '--gog-dialog-border-style',
3205
+ '--gog-dialog-border-width',
3206
+ '--gog-dialog-close-color',
3207
+ '--gog-dialog-close-focus-ring',
3208
+ '--gog-dialog-close-focus-ring-offset',
3209
+ '--gog-dialog-close-focus-ring-width',
3210
+ '--gog-dialog-close-font-size',
3211
+ '--gog-dialog-close-hover-bg',
3212
+ '--gog-dialog-close-hover-border',
3213
+ '--gog-dialog-close-hover-color',
3214
+ '--gog-dialog-close-line-height',
3215
+ '--gog-dialog-close-size',
3216
+ '--gog-dialog-color',
3217
+ '--gog-dialog-enter-distance',
3218
+ '--gog-dialog-enter-duration',
3219
+ '--gog-dialog-font-family',
3220
+ '--gog-dialog-header-border-color',
3221
+ '--gog-dialog-header-gap',
3222
+ '--gog-dialog-header-padding',
3223
+ '--gog-dialog-max-height',
3224
+ '--gog-dialog-min-width',
3225
+ '--gog-dialog-radius',
3226
+ '--gog-dialog-title-font-size',
3227
+ '--gog-dialog-title-line-height',
3228
+ ],
3229
+ },
3230
+ {
3231
+ section: 'Autocomplete',
3232
+ layer: 'component',
3233
+ tokens: [
3234
+ '--gog-autocomplete-actions-gap',
3235
+ '--gog-autocomplete-actions-inset',
3236
+ '--gog-autocomplete-actions-reserve',
3237
+ '--gog-autocomplete-border-color',
3238
+ '--gog-autocomplete-border-style',
3239
+ '--gog-autocomplete-border-width',
3240
+ '--gog-autocomplete-clear-color',
3241
+ '--gog-autocomplete-clear-hover-color',
3242
+ '--gog-autocomplete-clear-icon-ratio',
3243
+ '--gog-autocomplete-disabled-opacity',
3244
+ '--gog-autocomplete-empty-color',
3245
+ '--gog-autocomplete-empty-font-size',
3246
+ '--gog-autocomplete-empty-line-height',
3247
+ '--gog-autocomplete-error-border-color',
3248
+ '--gog-autocomplete-error-color',
3249
+ '--gog-autocomplete-error-font-family',
3250
+ '--gog-autocomplete-error-font-size',
3251
+ '--gog-autocomplete-error-line-height',
3252
+ '--gog-autocomplete-field-bg',
3253
+ '--gog-autocomplete-float-label-in-top',
3254
+ '--gog-autocomplete-float-label-over-gap',
3255
+ '--gog-autocomplete-float-label-over-reserve',
3256
+ '--gog-autocomplete-float-label-reserve',
3257
+ '--gog-autocomplete-focus-ring-color',
3258
+ '--gog-autocomplete-focus-ring-offset',
3259
+ '--gog-autocomplete-focus-ring-width',
3260
+ '--gog-autocomplete-font-family',
3261
+ '--gog-autocomplete-gap',
3262
+ '--gog-autocomplete-hover-border-color',
3263
+ '--gog-autocomplete-label-color',
3264
+ '--gog-autocomplete-label-font-family',
3265
+ '--gog-autocomplete-label-font-size',
3266
+ '--gog-autocomplete-label-font-weight',
3267
+ '--gog-autocomplete-label-letter-spacing',
3268
+ '--gog-autocomplete-label-line-height',
3269
+ '--gog-autocomplete-label-text-transform',
3270
+ '--gog-autocomplete-line-height',
3271
+ '--gog-autocomplete-min-width',
3272
+ '--gog-autocomplete-option-color',
3273
+ '--gog-autocomplete-option-gap',
3274
+ '--gog-autocomplete-option-height',
3275
+ '--gog-autocomplete-option-hover-bg',
3276
+ '--gog-autocomplete-option-hover-color',
3277
+ '--gog-autocomplete-option-padding',
3278
+ '--gog-autocomplete-option-press-bg',
3279
+ '--gog-autocomplete-option-radius',
3280
+ '--gog-autocomplete-option-selected-bg',
3281
+ '--gog-autocomplete-option-selected-color',
3282
+ '--gog-autocomplete-options-padding',
3283
+ '--gog-autocomplete-panel-bg',
3284
+ '--gog-autocomplete-panel-border-color',
3285
+ '--gog-autocomplete-panel-border-style',
3286
+ '--gog-autocomplete-panel-border-width',
3287
+ '--gog-autocomplete-panel-gap',
3288
+ '--gog-autocomplete-panel-max-height',
3289
+ '--gog-autocomplete-panel-max-width',
3290
+ '--gog-autocomplete-panel-radius',
3291
+ '--gog-autocomplete-panel-shadow',
3292
+ '--gog-autocomplete-placeholder-color',
3293
+ '--gog-autocomplete-radius',
3294
+ '--gog-autocomplete-spinner-size',
3295
+ '--gog-autocomplete-text-color',
3296
+ '--gog-autocomplete-transition-duration',
3297
+ ],
3298
+ },
3299
+ {
3300
+ section: 'Badge (the `gogBadge` directive; its classes live in utilities.css)',
3301
+ layer: 'component',
3302
+ tokens: [
3303
+ '--gog-badge-border-color',
3304
+ '--gog-badge-border-style',
3305
+ '--gog-badge-border-width',
3306
+ '--gog-badge-danger-bg',
3307
+ '--gog-badge-danger-color',
3308
+ '--gog-badge-dot-size',
3309
+ '--gog-badge-font-family',
3310
+ '--gog-badge-font-size',
3311
+ '--gog-badge-font-weight',
3312
+ '--gog-badge-info-bg',
3313
+ '--gog-badge-info-color',
3314
+ '--gog-badge-line-height',
3315
+ '--gog-badge-offset',
3316
+ '--gog-badge-padding-inline',
3317
+ '--gog-badge-radius',
3318
+ '--gog-badge-size',
3319
+ '--gog-badge-success-bg',
3320
+ '--gog-badge-success-color',
3321
+ '--gog-badge-warning-bg',
3322
+ '--gog-badge-warning-color',
3323
+ ],
3324
+ },
3325
+ {
3326
+ section: 'Datepicker (the field; the grid inside it is themed by --gog-calendar-*)',
3327
+ layer: 'component',
3328
+ tokens: [
3329
+ '--gog-datepicker-actions-gap',
3330
+ '--gog-datepicker-actions-inset',
3331
+ '--gog-datepicker-actions-reserve',
3332
+ '--gog-datepicker-border-color',
3333
+ '--gog-datepicker-border-style',
3334
+ '--gog-datepicker-border-width',
3335
+ '--gog-datepicker-clear-color',
3336
+ '--gog-datepicker-clear-icon-ratio',
3337
+ '--gog-datepicker-disabled-opacity',
3338
+ '--gog-datepicker-error-border-color',
3339
+ '--gog-datepicker-error-color',
3340
+ '--gog-datepicker-error-font-family',
3341
+ '--gog-datepicker-error-font-size',
3342
+ '--gog-datepicker-error-line-height',
3343
+ '--gog-datepicker-field-bg',
3344
+ '--gog-datepicker-float-label-in-top',
3345
+ '--gog-datepicker-float-label-over-gap',
3346
+ '--gog-datepicker-float-label-over-reserve',
3347
+ '--gog-datepicker-float-label-reserve',
3348
+ '--gog-datepicker-focus-ring-color',
3349
+ '--gog-datepicker-focus-ring-offset',
3350
+ '--gog-datepicker-focus-ring-width',
3351
+ '--gog-datepicker-font-family',
3352
+ '--gog-datepicker-gap',
3353
+ '--gog-datepicker-hover-border-color',
3354
+ '--gog-datepicker-icon-color',
3355
+ '--gog-datepicker-icon-hover-color',
3356
+ '--gog-datepicker-label-color',
3357
+ '--gog-datepicker-label-font-family',
3358
+ '--gog-datepicker-label-font-size',
3359
+ '--gog-datepicker-label-letter-spacing',
3360
+ '--gog-datepicker-label-line-height',
3361
+ '--gog-datepicker-label-text-transform',
3362
+ '--gog-datepicker-line-height',
3363
+ '--gog-datepicker-min-width',
3364
+ '--gog-datepicker-panel-bg',
3365
+ '--gog-datepicker-panel-border-color',
3366
+ '--gog-datepicker-panel-border-style',
3367
+ '--gog-datepicker-panel-border-width',
3368
+ '--gog-datepicker-panel-gap',
3369
+ '--gog-datepicker-panel-radius',
3370
+ '--gog-datepicker-panel-shadow',
3371
+ '--gog-datepicker-panel-width',
3372
+ '--gog-datepicker-placeholder-color',
3373
+ '--gog-datepicker-radius',
3374
+ '--gog-datepicker-text-color',
3375
+ '--gog-datepicker-toggle-icon-size',
3376
+ '--gog-datepicker-transition-duration',
3377
+ ],
3378
+ },
3379
+ {
3380
+ section: 'Divider',
3381
+ layer: 'component',
3382
+ tokens: [
3383
+ '--gog-divider-block-spacing',
3384
+ '--gog-divider-dashed-style',
3385
+ '--gog-divider-dotted-style',
3386
+ '--gog-divider-inline-spacing',
3387
+ '--gog-divider-inset-size',
3388
+ '--gog-divider-label-color',
3389
+ '--gog-divider-label-font-family',
3390
+ '--gog-divider-label-font-size',
3391
+ '--gog-divider-label-font-weight',
3392
+ '--gog-divider-label-gap',
3393
+ '--gog-divider-label-line-height',
3394
+ '--gog-divider-line-color',
3395
+ '--gog-divider-line-thickness',
3396
+ '--gog-divider-solid-style',
3397
+ '--gog-divider-vertical-length',
3398
+ ],
3399
+ },
3400
+ {
3401
+ section: 'Icon',
3402
+ layer: 'component',
3403
+ tokens: ['--gog-icon-fallback-size'],
3404
+ },
3405
+ {
3406
+ section: 'Input field',
3407
+ layer: 'component',
3408
+ tokens: [
3409
+ '--gog-input-clear-icon-ratio',
3410
+ '--gog-input-clear-inset',
3411
+ '--gog-input-clear-line-height',
3412
+ '--gog-input-clear-radius',
3413
+ '--gog-input-disabled-opacity',
3414
+ '--gog-input-error-color',
3415
+ '--gog-input-error-font-size',
3416
+ '--gog-input-error-line-height',
3417
+ '--gog-input-error-offset',
3418
+ '--gog-input-field-bg',
3419
+ '--gog-input-field-border',
3420
+ '--gog-input-field-border-style',
3421
+ '--gog-input-field-border-width',
3422
+ '--gog-input-field-color',
3423
+ '--gog-input-float-label-in-top',
3424
+ '--gog-input-float-label-over-gap',
3425
+ '--gog-input-float-label-over-reserve',
3426
+ '--gog-input-float-label-reserve',
3427
+ '--gog-input-focus-border',
3428
+ '--gog-input-focus-glow',
3429
+ '--gog-input-focus-ring',
3430
+ '--gog-input-focus-ring-offset',
3431
+ '--gog-input-focus-ring-width',
3432
+ '--gog-input-font-family',
3433
+ '--gog-input-gap',
3434
+ '--gog-input-icon-action-radius',
3435
+ '--gog-input-icon-color',
3436
+ '--gog-input-icon-focus-ring-width',
3437
+ '--gog-input-icon-hover-color',
3438
+ '--gog-input-icon-line-height',
3439
+ '--gog-input-label-color',
3440
+ '--gog-input-label-font-family',
3441
+ '--gog-input-label-font-size',
3442
+ '--gog-input-label-letter-spacing',
3443
+ '--gog-input-label-line-height',
3444
+ '--gog-input-label-text-transform',
3445
+ '--gog-input-placeholder-color',
3446
+ '--gog-input-radius',
3447
+ '--gog-input-spin-hover-bg',
3448
+ '--gog-input-spin-width',
3449
+ '--gog-input-transition-duration',
3450
+ '--gog-textarea-clear-icon-ratio',
3451
+ '--gog-textarea-line-height',
3452
+ '--gog-textarea-resize-grip-color',
3453
+ '--gog-textarea-resize-grip-offset',
3454
+ '--gog-textarea-resize-grip-opacity',
3455
+ '--gog-textarea-resize-grip-size',
3456
+ '--gog-textarea-resize-grip-stripe-gap',
3457
+ '--gog-textarea-resize-grip-stripe-width',
3458
+ '--gog-textarea-resize-inset-bottom',
3459
+ '--gog-textarea-resize-inset-right',
3460
+ ],
3461
+ },
3462
+ {
3463
+ section: 'Multiselect',
3464
+ layer: 'component',
3465
+ tokens: [
3466
+ '--gog-multiselect-actions-gap',
3467
+ '--gog-multiselect-actions-inset',
3468
+ '--gog-multiselect-arrow-icon-ratio',
3469
+ '--gog-multiselect-arrow-transition-duration',
3470
+ '--gog-multiselect-border-color',
3471
+ '--gog-multiselect-checkbox-border',
3472
+ '--gog-multiselect-checkbox-checked-bg',
3473
+ '--gog-multiselect-clear-icon-ratio',
3474
+ '--gog-multiselect-clear-line-height',
3475
+ '--gog-multiselect-clear-radius',
3476
+ '--gog-multiselect-controls-gap',
3477
+ '--gog-multiselect-controls-padding',
3478
+ '--gog-multiselect-disabled-opacity',
3479
+ '--gog-multiselect-error-color',
3480
+ '--gog-multiselect-error-font-size',
3481
+ '--gog-multiselect-error-line-height',
3482
+ '--gog-multiselect-error-offset',
3483
+ '--gog-multiselect-field-bg',
3484
+ '--gog-multiselect-field-border',
3485
+ '--gog-multiselect-field-border-style',
3486
+ '--gog-multiselect-field-border-width',
3487
+ '--gog-multiselect-field-color',
3488
+ '--gog-multiselect-filter-border-color',
3489
+ '--gog-multiselect-filter-border-style',
3490
+ '--gog-multiselect-filter-border-width',
3491
+ '--gog-multiselect-filter-empty-color',
3492
+ '--gog-multiselect-filter-empty-padding',
3493
+ '--gog-multiselect-filter-input-bg',
3494
+ '--gog-multiselect-filter-input-border',
3495
+ '--gog-multiselect-filter-input-color',
3496
+ '--gog-multiselect-filter-input-padding-x',
3497
+ '--gog-multiselect-filter-input-padding-y',
3498
+ '--gog-multiselect-filter-input-radius',
3499
+ '--gog-multiselect-filter-padding',
3500
+ '--gog-multiselect-float-label-in-top',
3501
+ '--gog-multiselect-float-label-over-gap',
3502
+ '--gog-multiselect-float-label-over-reserve',
3503
+ '--gog-multiselect-float-label-reserve',
3504
+ '--gog-multiselect-focus-border',
3505
+ '--gog-multiselect-focus-glow',
3506
+ '--gog-multiselect-focus-ring',
3507
+ '--gog-multiselect-focus-ring-offset',
3508
+ '--gog-multiselect-focus-ring-width',
3509
+ '--gog-multiselect-font-family',
3510
+ '--gog-multiselect-gap',
3511
+ '--gog-multiselect-label-color',
3512
+ '--gog-multiselect-label-font-family',
3513
+ '--gog-multiselect-label-font-size',
3514
+ '--gog-multiselect-label-letter-spacing',
3515
+ '--gog-multiselect-label-line-height',
3516
+ '--gog-multiselect-label-text-transform',
3517
+ '--gog-multiselect-mark-icon-ratio',
3518
+ '--gog-multiselect-mark-size-ratio',
3519
+ '--gog-multiselect-min-width',
3520
+ '--gog-multiselect-option-color',
3521
+ '--gog-multiselect-option-disabled-opacity',
3522
+ '--gog-multiselect-option-gap',
3523
+ '--gog-multiselect-option-gap-inline',
3524
+ '--gog-multiselect-option-height',
3525
+ '--gog-multiselect-option-hover-bg',
3526
+ '--gog-multiselect-option-press-bg',
3527
+ '--gog-multiselect-option-radius',
3528
+ '--gog-multiselect-option-transition-duration',
3529
+ '--gog-multiselect-options-padding',
3530
+ '--gog-multiselect-overflow-color',
3531
+ '--gog-multiselect-overflow-font-size',
3532
+ '--gog-multiselect-overflow-gap',
3533
+ '--gog-multiselect-overflow-line-height',
3534
+ '--gog-multiselect-panel-bg',
3535
+ '--gog-multiselect-panel-border',
3536
+ '--gog-multiselect-panel-gap',
3537
+ '--gog-multiselect-panel-max-height',
3538
+ '--gog-multiselect-panel-max-width',
3539
+ '--gog-multiselect-panel-radius',
3540
+ '--gog-multiselect-panel-shadow',
3541
+ '--gog-multiselect-placeholder-color',
3542
+ '--gog-multiselect-radius',
3543
+ '--gog-multiselect-transition-duration',
3544
+ '--gog-multiselect-value-color',
3545
+ ],
3546
+ },
3547
+ {
3548
+ section: 'Menu',
3549
+ layer: 'component',
3550
+ tokens: [
3551
+ '--gog-menu-bg',
3552
+ '--gog-menu-border-color',
3553
+ '--gog-menu-border-style',
3554
+ '--gog-menu-border-width',
3555
+ '--gog-menu-focus-ring',
3556
+ '--gog-menu-focus-ring-width',
3557
+ '--gog-menu-font-family',
3558
+ '--gog-menu-gap',
3559
+ '--gog-menu-item-color',
3560
+ '--gog-menu-item-disabled-color',
3561
+ '--gog-menu-item-disabled-opacity',
3562
+ '--gog-menu-item-font-size',
3563
+ '--gog-menu-item-gap',
3564
+ '--gog-menu-item-hover-bg',
3565
+ '--gog-menu-item-hover-color',
3566
+ '--gog-menu-item-icon-size',
3567
+ '--gog-menu-item-line-height',
3568
+ '--gog-menu-item-padding',
3569
+ '--gog-menu-item-press-bg',
3570
+ '--gog-menu-item-radius',
3571
+ '--gog-menu-max-height',
3572
+ '--gog-menu-max-width',
3573
+ '--gog-menu-min-width',
3574
+ '--gog-menu-padding',
3575
+ '--gog-menu-panel-gap',
3576
+ '--gog-menu-radius',
3577
+ '--gog-menu-shadow',
3578
+ '--gog-menu-transition-duration',
3579
+ '--gog-menu-z',
3580
+ ],
3581
+ },
3582
+ {
3583
+ section: 'Paginator',
3584
+ layer: 'component',
3585
+ tokens: [
3586
+ '--gog-paginator-ellipsis-color',
3587
+ '--gog-paginator-ellipsis-font-family',
3588
+ '--gog-paginator-ellipsis-font-size',
3589
+ '--gog-paginator-ellipsis-line-height',
3590
+ '--gog-paginator-ellipsis-min-width',
3591
+ '--gog-paginator-gap',
3592
+ '--gog-paginator-page-size-min-width',
3593
+ ],
3594
+ },
3595
+ {
3596
+ section: 'Panel',
3597
+ layer: 'component',
3598
+ tokens: [
3599
+ '--gog-panel-chevron-size',
3600
+ '--gog-panel-color',
3601
+ '--gog-panel-disabled-opacity',
3602
+ '--gog-panel-elevated-bg',
3603
+ '--gog-panel-elevated-border-color',
3604
+ '--gog-panel-filled-bg',
3605
+ '--gog-panel-filled-border-color',
3606
+ '--gog-panel-filled-shadow',
3607
+ '--gog-panel-focus-ring',
3608
+ '--gog-panel-focus-ring-offset',
3609
+ '--gog-panel-focus-ring-width',
3610
+ '--gog-panel-font-family',
3611
+ '--gog-panel-footer-border-color',
3612
+ '--gog-panel-footer-gap',
3613
+ '--gog-panel-footer-padding-top',
3614
+ '--gog-panel-header-gap',
3615
+ '--gog-panel-heading-color',
3616
+ '--gog-panel-heading-font-family',
3617
+ '--gog-panel-heading-font-size',
3618
+ '--gog-panel-heading-font-weight',
3619
+ '--gog-panel-heading-line-height',
3620
+ '--gog-panel-lg-gap',
3621
+ '--gog-panel-lg-padding-x',
3622
+ '--gog-panel-lg-padding-y',
3623
+ '--gog-panel-md-gap',
3624
+ '--gog-panel-md-padding-x',
3625
+ '--gog-panel-md-padding-y',
3626
+ '--gog-panel-outlined-bg',
3627
+ '--gog-panel-outlined-border-color',
3628
+ '--gog-panel-outlined-shadow',
3629
+ '--gog-panel-slg-gap',
3630
+ '--gog-panel-slg-padding-x',
3631
+ '--gog-panel-slg-padding-y',
3632
+ '--gog-panel-sm-gap',
3633
+ '--gog-panel-sm-padding-x',
3634
+ '--gog-panel-sm-padding-y',
3635
+ '--gog-panel-toggle-color',
3636
+ '--gog-panel-toggle-hover-bg',
3637
+ '--gog-panel-toggle-radius',
3638
+ '--gog-panel-toggle-size',
3639
+ '--gog-panel-transition-duration',
3640
+ '--gog-panel-xsm-gap',
3641
+ '--gog-panel-xsm-padding-x',
3642
+ '--gog-panel-xsm-padding-y',
3643
+ ],
3644
+ },
3645
+ {
3646
+ section: 'Progressbar',
3647
+ layer: 'component',
3648
+ tokens: [
3649
+ '--gog-progressbar-accent-bg',
3650
+ '--gog-progressbar-accent-buffer-bg',
3651
+ '--gog-progressbar-danger-bg',
3652
+ '--gog-progressbar-danger-buffer-bg',
3653
+ '--gog-progressbar-edge-backing-color',
3654
+ '--gog-progressbar-edge-color',
3655
+ '--gog-progressbar-edge-width',
3656
+ '--gog-progressbar-indeterminate-duration',
3657
+ '--gog-progressbar-indeterminate-easing',
3658
+ '--gog-progressbar-info-bg',
3659
+ '--gog-progressbar-info-buffer-bg',
3660
+ '--gog-progressbar-lg-height',
3661
+ '--gog-progressbar-md-height',
3662
+ '--gog-progressbar-radius',
3663
+ '--gog-progressbar-slg-height',
3664
+ '--gog-progressbar-sm-height',
3665
+ '--gog-progressbar-stripe-color',
3666
+ '--gog-progressbar-stripe-size',
3667
+ '--gog-progressbar-success-bg',
3668
+ '--gog-progressbar-success-buffer-bg',
3669
+ '--gog-progressbar-track-base-bg',
3670
+ '--gog-progressbar-transition-duration',
3671
+ '--gog-progressbar-value-color',
3672
+ '--gog-progressbar-value-font-family',
3673
+ '--gog-progressbar-value-font-size',
3674
+ '--gog-progressbar-value-gap',
3675
+ '--gog-progressbar-value-line-height',
3676
+ '--gog-progressbar-value-min-width',
3677
+ '--gog-progressbar-warning-bg',
3678
+ '--gog-progressbar-warning-buffer-bg',
3679
+ '--gog-progressbar-xsm-height',
3680
+ ],
3681
+ },
3682
+ {
3683
+ section: 'Ripple (the `gogRipple` directive; its classes live in ripple.css)',
3684
+ layer: 'component',
3685
+ tokens: [
3686
+ '--gog-ripple-color',
3687
+ '--gog-ripple-easing',
3688
+ '--gog-ripple-enter-duration',
3689
+ '--gog-ripple-exit-duration',
3690
+ '--gog-ripple-opacity',
3691
+ ],
3692
+ },
3693
+ {
3694
+ section: 'Scroll',
3695
+ layer: 'component',
3696
+ tokens: [
3697
+ '--gog-scroll-corner-bg',
3698
+ '--gog-scroll-fade-duration',
3699
+ '--gog-scroll-focus-ring',
3700
+ '--gog-scroll-focus-ring-width',
3701
+ '--gog-scroll-normal-thumb-hit-padding',
3702
+ '--gog-scroll-normal-thumb-min-size',
3703
+ '--gog-scroll-normal-track-width',
3704
+ '--gog-scroll-thin-thumb-hit-padding',
3705
+ '--gog-scroll-thin-thumb-min-size',
3706
+ '--gog-scroll-thin-track-width',
3707
+ '--gog-scroll-thumb-active-bg',
3708
+ '--gog-scroll-thumb-bg',
3709
+ '--gog-scroll-thumb-hover-bg',
3710
+ '--gog-scroll-thumb-inset',
3711
+ '--gog-scroll-thumb-radius',
3712
+ '--gog-scroll-track-bg',
3713
+ '--gog-scroll-track-radius',
3714
+ ],
3715
+ },
3716
+ {
3717
+ section: 'Select',
3718
+ layer: 'component',
3719
+ tokens: [
3720
+ '--gog-select-chevron-color',
3721
+ '--gog-select-chevron-icon-ratio',
3722
+ '--gog-select-chevron-inset',
3723
+ '--gog-select-clear-color',
3724
+ '--gog-select-clear-gap',
3725
+ '--gog-select-clear-hover-color',
3726
+ '--gog-select-clear-icon-ratio',
3727
+ '--gog-select-clear-line-height',
3728
+ '--gog-select-clear-radius',
3729
+ '--gog-select-control-gap',
3730
+ '--gog-select-disabled-opacity',
3731
+ '--gog-select-error-color',
3732
+ '--gog-select-error-font-size',
3733
+ '--gog-select-error-line-height',
3734
+ '--gog-select-field-bg',
3735
+ '--gog-select-field-border',
3736
+ '--gog-select-field-border-style',
3737
+ '--gog-select-field-border-width',
3738
+ '--gog-select-field-color',
3739
+ '--gog-select-filter-border-color',
3740
+ '--gog-select-filter-border-style',
3741
+ '--gog-select-filter-border-width',
3742
+ '--gog-select-filter-empty-color',
3743
+ '--gog-select-filter-empty-padding',
3744
+ '--gog-select-filter-input-bg',
3745
+ '--gog-select-filter-input-border',
3746
+ '--gog-select-filter-input-color',
3747
+ '--gog-select-filter-input-padding-x',
3748
+ '--gog-select-filter-input-padding-y',
3749
+ '--gog-select-filter-input-radius',
3750
+ '--gog-select-filter-padding',
3751
+ '--gog-select-float-label-in-top',
3752
+ '--gog-select-float-label-over-gap',
3753
+ '--gog-select-float-label-over-reserve',
3754
+ '--gog-select-float-label-reserve',
3755
+ '--gog-select-focus-border',
3756
+ '--gog-select-focus-glow',
3757
+ '--gog-select-focus-ring',
3758
+ '--gog-select-focus-ring-offset',
3759
+ '--gog-select-focus-ring-width',
3760
+ '--gog-select-font-family',
3761
+ '--gog-select-gap',
3762
+ '--gog-select-label-color',
3763
+ '--gog-select-label-font-family',
3764
+ '--gog-select-label-font-size',
3765
+ '--gog-select-label-letter-spacing',
3766
+ '--gog-select-label-line-height',
3767
+ '--gog-select-label-text-transform',
3768
+ '--gog-select-mark-icon-ratio',
3769
+ '--gog-select-mark-size-ratio',
3770
+ '--gog-select-min-width',
3771
+ '--gog-select-option-color',
3772
+ '--gog-select-option-disabled-opacity',
3773
+ '--gog-select-option-gap',
3774
+ '--gog-select-option-height',
3775
+ '--gog-select-option-hover-bg',
3776
+ '--gog-select-option-press-bg',
3777
+ '--gog-select-option-radius',
3778
+ '--gog-select-option-selected-color',
3779
+ '--gog-select-option-transition-duration',
3780
+ '--gog-select-options-padding',
3781
+ '--gog-select-panel-bg',
3782
+ '--gog-select-panel-gap',
3783
+ '--gog-select-panel-max-height',
3784
+ '--gog-select-panel-max-width',
3785
+ '--gog-select-panel-radius',
3786
+ '--gog-select-panel-shadow',
3787
+ '--gog-select-placeholder-color',
3788
+ '--gog-select-radius',
3789
+ '--gog-select-transition-duration',
3790
+ ],
3791
+ },
3792
+ {
3793
+ section: 'Skeleton',
3794
+ layer: 'component',
3795
+ tokens: [
3796
+ '--gog-skeleton-base',
3797
+ '--gog-skeleton-circle-size-lg',
3798
+ '--gog-skeleton-circle-size-md',
3799
+ '--gog-skeleton-circle-size-slg',
3800
+ '--gog-skeleton-circle-size-sm',
3801
+ '--gog-skeleton-circle-size-xsm',
3802
+ '--gog-skeleton-line-gap',
3803
+ '--gog-skeleton-line-height-lg',
3804
+ '--gog-skeleton-line-height-md',
3805
+ '--gog-skeleton-line-height-slg',
3806
+ '--gog-skeleton-line-height-sm',
3807
+ '--gog-skeleton-line-height-xsm',
3808
+ '--gog-skeleton-line-radius',
3809
+ '--gog-skeleton-pulse-duration',
3810
+ '--gog-skeleton-pulse-min-opacity',
3811
+ '--gog-skeleton-radius',
3812
+ '--gog-skeleton-rect-height-lg',
3813
+ '--gog-skeleton-rect-height-md',
3814
+ '--gog-skeleton-rect-height-slg',
3815
+ '--gog-skeleton-rect-height-sm',
3816
+ '--gog-skeleton-rect-height-xsm',
3817
+ '--gog-skeleton-shine',
3818
+ '--gog-skeleton-short-line-width',
3819
+ '--gog-skeleton-square-radius',
3820
+ '--gog-skeleton-wave-duration',
3821
+ ],
3822
+ },
3823
+ {
3824
+ section: 'Slider',
3825
+ layer: 'component',
3826
+ tokens: [
3827
+ '--gog-slider-auto-width',
3828
+ '--gog-slider-disabled-opacity',
3829
+ '--gog-slider-error-color',
3830
+ '--gog-slider-error-font-family',
3831
+ '--gog-slider-error-font-size',
3832
+ '--gog-slider-error-line-height',
3833
+ '--gog-slider-fill-bg',
3834
+ '--gog-slider-focus-ring',
3835
+ '--gog-slider-focus-ring-width',
3836
+ '--gog-slider-gap',
3837
+ '--gog-slider-label-color',
3838
+ '--gog-slider-label-font-family',
3839
+ '--gog-slider-label-font-size',
3840
+ '--gog-slider-label-letter-spacing',
3841
+ '--gog-slider-label-line-height',
3842
+ '--gog-slider-label-text-transform',
3843
+ '--gog-slider-range-color',
3844
+ '--gog-slider-range-font-size',
3845
+ '--gog-slider-range-line-height',
3846
+ '--gog-slider-thumb-bg',
3847
+ '--gog-slider-thumb-border',
3848
+ '--gog-slider-thumb-border-width',
3849
+ '--gog-slider-thumb-glow-color',
3850
+ '--gog-slider-thumb-glow-size',
3851
+ '--gog-slider-thumb-radius',
3852
+ '--gog-slider-thumb-size',
3853
+ '--gog-slider-track-area-height',
3854
+ '--gog-slider-track-bg',
3855
+ '--gog-slider-track-border-color',
3856
+ '--gog-slider-track-border-style',
3857
+ '--gog-slider-track-border-width',
3858
+ '--gog-slider-track-height',
3859
+ '--gog-slider-track-radius',
3860
+ '--gog-slider-value-color',
3861
+ '--gog-slider-value-font-family',
3862
+ '--gog-slider-value-font-size',
3863
+ '--gog-slider-value-line-height',
3864
+ '--gog-slider-value-min-width',
3865
+ '--gog-slider-vertical-length',
3866
+ ],
3867
+ },
3868
+ {
3869
+ section: 'Spinner',
3870
+ layer: 'component',
3871
+ tokens: [
3872
+ '--gog-spinner-arc-inner-color',
3873
+ '--gog-spinner-arc-inner-dash',
3874
+ '--gog-spinner-arc-inner-glow',
3875
+ '--gog-spinner-arc-inner-opacity',
3876
+ '--gog-spinner-arc-inner-pulse-opacity',
3877
+ '--gog-spinner-arc-inner-stroke-compact',
3878
+ '--gog-spinner-arc-outer-color',
3879
+ '--gog-spinner-arc-outer-dash',
3880
+ '--gog-spinner-arc-outer-glow',
3881
+ '--gog-spinner-arc-outer-pulse-opacity',
3882
+ '--gog-spinner-diamond-color',
3883
+ '--gog-spinner-diamond-glow',
3884
+ '--gog-spinner-diamond-opacity',
3885
+ '--gog-spinner-glow-color',
3886
+ '--gog-spinner-overlay-blur',
3887
+ '--gog-spinner-overlay-fade-duration',
3888
+ '--gog-spinner-pulse-duration',
3889
+ '--gog-spinner-ring-duration',
3890
+ '--gog-spinner-ring-glow',
3891
+ '--gog-spinner-ring-padding',
3892
+ '--gog-spinner-rune-color',
3893
+ '--gog-spinner-rune-font-family',
3894
+ '--gog-spinner-rune-glow',
3895
+ '--gog-spinner-rune-pulse-opacity',
3896
+ '--gog-spinner-spin-duration',
3897
+ '--gog-spinner-spin-slow-duration',
3898
+ '--gog-spinner-ticks-duration',
3899
+ '--gog-spinner-track-color',
3900
+ '--gog-spinner-track-opacity',
3901
+ ],
3902
+ },
3903
+ {
3904
+ section: 'Table',
3905
+ layer: 'component',
3906
+ tokens: [
3907
+ '--gog-table-accent-bright',
3908
+ '--gog-table-accent-color',
3909
+ '--gog-table-border-color',
3910
+ '--gog-table-border-style',
3911
+ '--gog-table-border-width',
3912
+ '--gog-table-empty-font-size',
3913
+ '--gog-table-empty-line-height',
3914
+ '--gog-table-empty-padding',
3915
+ '--gog-table-focus-ring',
3916
+ '--gog-table-focus-ring-width',
3917
+ '--gog-table-font-family',
3918
+ '--gog-table-footer-gap',
3919
+ '--gog-table-footer-min-height',
3920
+ '--gog-table-gap',
3921
+ '--gog-table-header-bg',
3922
+ '--gog-table-header-font-family',
3923
+ '--gog-table-header-letter-spacing',
3924
+ '--gog-table-header-text-transform',
3925
+ '--gog-table-hover-bg',
3926
+ '--gog-table-lg-padding-v',
3927
+ '--gog-table-lg-td-font-size',
3928
+ '--gog-table-lg-th-font-size',
3929
+ '--gog-table-loading-opacity',
3930
+ '--gog-table-loading-padding',
3931
+ '--gog-table-md-padding-v',
3932
+ '--gog-table-md-td-font-size',
3933
+ '--gog-table-md-th-font-size',
3934
+ '--gog-table-muted-color',
3935
+ '--gog-table-num-col-width',
3936
+ '--gog-table-num-font-size',
3937
+ '--gog-table-num-line-height',
3938
+ '--gog-table-numeric-font-family',
3939
+ '--gog-table-padding-h',
3940
+ '--gog-table-row-border-width',
3941
+ '--gog-table-row-press-bg',
3942
+ '--gog-table-row-transition-duration',
3943
+ '--gog-table-select-col-width',
3944
+ '--gog-table-selected-bg',
3945
+ '--gog-table-slg-padding-v',
3946
+ '--gog-table-slg-td-font-size',
3947
+ '--gog-table-slg-th-font-size',
3948
+ '--gog-table-sm-padding-v',
3949
+ '--gog-table-sm-td-font-size',
3950
+ '--gog-table-sm-th-font-size',
3951
+ '--gog-table-sort-icon-opacity',
3952
+ '--gog-table-sort-icon-size',
3953
+ '--gog-table-sort-icon-width',
3954
+ '--gog-table-surface',
3955
+ '--gog-table-td-line-height',
3956
+ '--gog-table-text-color',
3957
+ '--gog-table-th-inner-gap',
3958
+ '--gog-table-th-line-height',
3959
+ '--gog-table-total-font-size',
3960
+ '--gog-table-total-letter-spacing',
3961
+ '--gog-table-total-line-height',
3962
+ '--gog-table-total-text-transform',
3963
+ '--gog-table-xsm-padding-v',
3964
+ '--gog-table-xsm-td-font-size',
3965
+ '--gog-table-xsm-th-font-size',
3966
+ ],
3967
+ },
3968
+ {
3969
+ section: 'Tabs',
3970
+ layer: 'component',
3971
+ tokens: [
3972
+ '--gog-tabs-active-color',
3973
+ '--gog-tabs-disabled-opacity',
3974
+ '--gog-tabs-focus-ring-color',
3975
+ '--gog-tabs-focus-ring-offset',
3976
+ '--gog-tabs-focus-ring-width',
3977
+ '--gog-tabs-font-family',
3978
+ '--gog-tabs-font-weight',
3979
+ '--gog-tabs-gap',
3980
+ '--gog-tabs-header-border-color',
3981
+ '--gog-tabs-header-border-style',
3982
+ '--gog-tabs-header-border-width',
3983
+ '--gog-tabs-hover-color',
3984
+ '--gog-tabs-icon-size',
3985
+ '--gog-tabs-indicator-color',
3986
+ '--gog-tabs-indicator-radius',
3987
+ '--gog-tabs-indicator-thickness',
3988
+ '--gog-tabs-letter-spacing',
3989
+ '--gog-tabs-lg-font-size',
3990
+ '--gog-tabs-lg-padding',
3991
+ '--gog-tabs-line-height',
3992
+ '--gog-tabs-md-font-size',
3993
+ '--gog-tabs-md-padding',
3994
+ '--gog-tabs-panel-color',
3995
+ '--gog-tabs-panel-padding',
3996
+ '--gog-tabs-press-bg',
3997
+ '--gog-tabs-press-color',
3998
+ '--gog-tabs-rest-bg',
3999
+ '--gog-tabs-rest-color',
4000
+ '--gog-tabs-slg-font-size',
4001
+ '--gog-tabs-slg-padding',
4002
+ '--gog-tabs-sm-font-size',
4003
+ '--gog-tabs-sm-padding',
4004
+ '--gog-tabs-tab-gap',
4005
+ '--gog-tabs-text-transform',
4006
+ '--gog-tabs-transition-duration',
4007
+ '--gog-tabs-xsm-font-size',
4008
+ '--gog-tabs-xsm-padding',
4009
+ ],
4010
+ },
4011
+ {
4012
+ section: 'Tag',
4013
+ layer: 'component',
4014
+ tokens: [
4015
+ '--gog-tag-bg-base',
4016
+ '--gog-tag-bg-mix',
4017
+ '--gog-tag-border-style',
4018
+ '--gog-tag-border-width',
4019
+ '--gog-tag-color-base',
4020
+ '--gog-tag-color-mix',
4021
+ '--gog-tag-danger-color',
4022
+ '--gog-tag-default-color',
4023
+ '--gog-tag-font-family',
4024
+ '--gog-tag-font-weight',
4025
+ '--gog-tag-info-color',
4026
+ '--gog-tag-inner-border-width',
4027
+ '--gog-tag-lg-font-size',
4028
+ '--gog-tag-lg-gap',
4029
+ '--gog-tag-lg-icon-size',
4030
+ '--gog-tag-lg-padding-block',
4031
+ '--gog-tag-lg-padding-inline',
4032
+ '--gog-tag-line-height',
4033
+ '--gog-tag-md-font-size',
4034
+ '--gog-tag-md-gap',
4035
+ '--gog-tag-md-icon-size',
4036
+ '--gog-tag-md-padding-block',
4037
+ '--gog-tag-md-padding-inline',
4038
+ '--gog-tag-pill-radius',
4039
+ '--gog-tag-radius',
4040
+ '--gog-tag-slg-font-size',
4041
+ '--gog-tag-slg-gap',
4042
+ '--gog-tag-slg-icon-size',
4043
+ '--gog-tag-slg-padding-block',
4044
+ '--gog-tag-slg-padding-inline',
4045
+ '--gog-tag-sm-font-size',
4046
+ '--gog-tag-sm-gap',
4047
+ '--gog-tag-sm-icon-size',
4048
+ '--gog-tag-sm-padding-block',
4049
+ '--gog-tag-sm-padding-inline',
4050
+ '--gog-tag-success-color',
4051
+ '--gog-tag-warning-color',
4052
+ '--gog-tag-xsm-font-size',
4053
+ '--gog-tag-xsm-gap',
4054
+ '--gog-tag-xsm-icon-size',
4055
+ '--gog-tag-xsm-padding-block',
4056
+ '--gog-tag-xsm-padding-inline',
4057
+ ],
4058
+ },
4059
+ {
4060
+ section: 'Toast',
4061
+ layer: 'component',
4062
+ tokens: [
4063
+ '--gog-toast-accent-width',
4064
+ '--gog-toast-action-font-size',
4065
+ '--gog-toast-action-line-height',
4066
+ '--gog-toast-action-padding',
4067
+ '--gog-toast-actions-gap',
4068
+ '--gog-toast-bg',
4069
+ '--gog-toast-border',
4070
+ '--gog-toast-close-font-size',
4071
+ '--gog-toast-close-line-height',
4072
+ '--gog-toast-close-padding',
4073
+ '--gog-toast-color',
4074
+ '--gog-toast-content-gap',
4075
+ '--gog-toast-error-color',
4076
+ '--gog-toast-font-family',
4077
+ '--gog-toast-icon-color',
4078
+ '--gog-toast-icon-line-height',
4079
+ '--gog-toast-icon-size',
4080
+ '--gog-toast-info-color',
4081
+ '--gog-toast-main-gap',
4082
+ '--gog-toast-message-font-size',
4083
+ '--gog-toast-message-line-height',
4084
+ '--gog-toast-padding',
4085
+ '--gog-toast-progress-height',
4086
+ '--gog-toast-progress-opacity',
4087
+ '--gog-toast-radius',
4088
+ '--gog-toast-stack-expanded-gap',
4089
+ '--gog-toast-stack-max-depth',
4090
+ '--gog-toast-stack-min-opacity',
4091
+ '--gog-toast-stack-opacity-step',
4092
+ '--gog-toast-stack-peek',
4093
+ '--gog-toast-stack-scale-step',
4094
+ '--gog-toast-success-color',
4095
+ '--gog-toast-transition-duration',
4096
+ '--gog-toast-warning-color',
4097
+ ],
4098
+ },
4099
+ {
4100
+ section: 'Toggle',
4101
+ layer: 'component',
4102
+ tokens: [
4103
+ '--gog-toggle-border-color',
4104
+ '--gog-toggle-border-style',
4105
+ '--gog-toggle-border-width',
4106
+ '--gog-toggle-disabled-opacity',
4107
+ '--gog-toggle-focus-ring-color',
4108
+ '--gog-toggle-focus-ring-offset',
4109
+ '--gog-toggle-focus-ring-width',
4110
+ '--gog-toggle-font-family',
4111
+ '--gog-toggle-gap',
4112
+ '--gog-toggle-label-color',
4113
+ '--gog-toggle-label-line-height',
4114
+ '--gog-toggle-lg-label-size',
4115
+ '--gog-toggle-lg-state-font-size',
4116
+ '--gog-toggle-lg-thumb-size',
4117
+ '--gog-toggle-lg-track-height',
4118
+ '--gog-toggle-lg-track-width',
4119
+ '--gog-toggle-md-label-size',
4120
+ '--gog-toggle-md-state-font-size',
4121
+ '--gog-toggle-md-thumb-size',
4122
+ '--gog-toggle-md-track-height',
4123
+ '--gog-toggle-md-track-width',
4124
+ '--gog-toggle-on-border-color',
4125
+ '--gog-toggle-radius',
4126
+ '--gog-toggle-slg-label-size',
4127
+ '--gog-toggle-slg-state-font-size',
4128
+ '--gog-toggle-slg-thumb-size',
4129
+ '--gog-toggle-slg-track-height',
4130
+ '--gog-toggle-slg-track-width',
4131
+ '--gog-toggle-sm-label-size',
4132
+ '--gog-toggle-sm-state-font-size',
4133
+ '--gog-toggle-sm-thumb-size',
4134
+ '--gog-toggle-sm-track-height',
4135
+ '--gog-toggle-sm-track-width',
4136
+ '--gog-toggle-state-color',
4137
+ '--gog-toggle-state-font-weight',
4138
+ '--gog-toggle-state-letter-spacing',
4139
+ '--gog-toggle-state-line-height',
4140
+ '--gog-toggle-state-offset',
4141
+ '--gog-toggle-state-padding-inline',
4142
+ '--gog-toggle-state-thumb-gap',
4143
+ '--gog-toggle-thumb-inset',
4144
+ '--gog-toggle-thumb-off-bg',
4145
+ '--gog-toggle-thumb-on-bg',
4146
+ '--gog-toggle-track-off-bg',
4147
+ '--gog-toggle-track-on-bg',
4148
+ '--gog-toggle-transition-duration',
4149
+ '--gog-toggle-xsm-label-size',
4150
+ '--gog-toggle-xsm-state-font-size',
4151
+ '--gog-toggle-xsm-thumb-size',
4152
+ '--gog-toggle-xsm-track-height',
4153
+ '--gog-toggle-xsm-track-width',
4154
+ ],
4155
+ },
4156
+ {
4157
+ section: 'Tooltip',
4158
+ layer: 'component',
4159
+ tokens: [
4160
+ '--gog-tooltip-arrow-size',
4161
+ '--gog-tooltip-bg',
4162
+ '--gog-tooltip-border-color',
4163
+ '--gog-tooltip-border-style',
4164
+ '--gog-tooltip-border-width',
4165
+ '--gog-tooltip-color',
4166
+ '--gog-tooltip-font-family',
4167
+ '--gog-tooltip-font-size',
4168
+ '--gog-tooltip-gap',
4169
+ '--gog-tooltip-line-height',
4170
+ '--gog-tooltip-max-height',
4171
+ '--gog-tooltip-max-width',
4172
+ '--gog-tooltip-padding',
4173
+ '--gog-tooltip-radius',
4174
+ '--gog-tooltip-shadow',
4175
+ '--gog-tooltip-transition-duration',
4176
+ ],
4177
+ },
4178
+ {
4179
+ section: 'Instance (deliberately undeclared)',
4180
+ layer: 'instance',
4181
+ tokens: [
4182
+ '--gog-accordion-body-font-size',
4183
+ '--gog-accordion-body-padding-bottom',
4184
+ '--gog-accordion-body-padding-top',
4185
+ '--gog-accordion-chevron-font-size',
4186
+ '--gog-accordion-chevron-size',
4187
+ '--gog-accordion-content-gap',
4188
+ '--gog-accordion-font-size',
4189
+ '--gog-accordion-letter-spacing',
4190
+ '--gog-accordion-padding-x',
4191
+ '--gog-accordion-padding-y',
4192
+ '--gog-autocomplete-bg',
4193
+ '--gog-autocomplete-color',
4194
+ '--gog-autocomplete-float-label-on-bg',
4195
+ '--gog-autocomplete-font-size',
4196
+ '--gog-autocomplete-padding-x',
4197
+ '--gog-autocomplete-padding-y',
4198
+ '--gog-badge-bg',
4199
+ '--gog-badge-color',
4200
+ '--gog-button-bg',
4201
+ '--gog-button-border',
4202
+ '--gog-button-color',
4203
+ '--gog-button-hover-bg',
4204
+ '--gog-button-hover-color',
4205
+ '--gog-button-hover-shadow',
4206
+ '--gog-button-press-bg',
4207
+ '--gog-button-press-color',
4208
+ '--gog-button-shadow',
4209
+ '--gog-button-spinner-color',
4210
+ '--gog-button-toggle-bg',
4211
+ '--gog-button-toggle-color',
4212
+ '--gog-button-toggle-font-size',
4213
+ '--gog-button-toggle-padding',
4214
+ '--gog-button-toggled-shadow',
4215
+ '--gog-calendar-day-bg',
4216
+ '--gog-calendar-day-color',
4217
+ '--gog-calendar-day-size',
4218
+ '--gog-card-bg',
4219
+ '--gog-card-border-color',
4220
+ '--gog-card-gap',
4221
+ '--gog-card-padding-x',
4222
+ '--gog-card-padding-y',
4223
+ '--gog-card-shadow',
4224
+ '--gog-checkbox-box-size',
4225
+ '--gog-checkbox-icon-size',
4226
+ '--gog-checkbox-label-size',
4227
+ '--gog-checkbox-padding',
4228
+ '--gog-chip-avatar-size',
4229
+ '--gog-chip-font-size',
4230
+ '--gog-chip-gap',
4231
+ '--gog-chip-icon-size',
4232
+ '--gog-chip-padding-block',
4233
+ '--gog-chip-padding-inline',
4234
+ '--gog-chip-remove-size',
4235
+ '--gog-datepicker-bg',
4236
+ '--gog-datepicker-color',
4237
+ '--gog-datepicker-float-label-on-bg',
4238
+ '--gog-datepicker-font-size',
4239
+ '--gog-datepicker-padding-x',
4240
+ '--gog-datepicker-padding-y',
4241
+ '--gog-dialog-offset-x',
4242
+ '--gog-dialog-offset-y',
4243
+ '--gog-divider-color',
4244
+ '--gog-divider-spacing',
4245
+ '--gog-divider-thickness',
4246
+ '--gog-input-float-label-on-bg',
4247
+ '--gog-input-font',
4248
+ '--gog-input-padding-x',
4249
+ '--gog-input-padding-y',
4250
+ '--gog-menu-available-height',
4251
+ '--gog-multiselect-float-label-on-bg',
4252
+ '--gog-multiselect-font-size',
4253
+ '--gog-multiselect-padding-x',
4254
+ '--gog-multiselect-padding-y',
4255
+ '--gog-panel-bg',
4256
+ '--gog-panel-border-color',
4257
+ '--gog-panel-gap',
4258
+ '--gog-panel-padding-x',
4259
+ '--gog-panel-padding-y',
4260
+ '--gog-progressbar-buffer-bg',
4261
+ '--gog-progressbar-fill-bg',
4262
+ '--gog-progressbar-height',
4263
+ '--gog-progressbar-track-bg',
4264
+ '--gog-radio-box-size',
4265
+ '--gog-radio-label-size',
4266
+ '--gog-radio-padding',
4267
+ '--gog-select-control-font',
4268
+ '--gog-select-control-padding-x',
4269
+ '--gog-select-control-padding-y',
4270
+ '--gog-select-float-label-on-bg',
4271
+ '--gog-table-td-font-size',
4272
+ '--gog-table-td-padding-v',
4273
+ '--gog-table-th-font-size',
4274
+ '--gog-table-th-padding-v',
4275
+ '--gog-tabs-tab-bg',
4276
+ '--gog-tabs-tab-color',
4277
+ '--gog-tabs-tab-font-size',
4278
+ '--gog-tabs-tab-padding',
4279
+ '--gog-tag-accent',
4280
+ '--gog-tag-bg',
4281
+ '--gog-tag-border',
4282
+ '--gog-tag-color',
4283
+ '--gog-tag-font-size',
4284
+ '--gog-tag-gap',
4285
+ '--gog-tag-icon-size',
4286
+ '--gog-tag-padding-block',
4287
+ '--gog-tag-padding-inline',
4288
+ '--gog-textarea-scrollbar-width',
4289
+ '--gog-toggle-padding',
4290
+ '--gog-toggle-thumb-bg',
4291
+ '--gog-toggle-track-bg',
4292
+ ],
4293
+ },
4294
+ ];
4295
+
4296
+ /**
4297
+ * Reading a `--gog-*` token's value from TypeScript, safely.
4298
+ *
4299
+ * **The trap these exist for.** `getComputedStyle(el).getPropertyValue('--x')` returns a custom
4300
+ * property's *specified* value, not a used one. A token declared
4301
+ * `calc(10px * var(--gog-density))` comes back as that whole string, and `Number.parseFloat` on
4302
+ * it returns `NaN` — which every caller in this library turned into its fallback, silently. No
4303
+ * error, no failing test, just a component quietly using the wrong number.
4304
+ *
4305
+ * That is not hypothetical. 21.7.0's density scale (`docs/themes.md` iteration 6) made 178
4306
+ * tokens `calc(<n>px * var(--gog-density))`; none of the three tokens read from TypeScript were
4307
+ * among them, but nothing stopped the next one from being. And a **consumer** can write
4308
+ * `--gog-scroll-thumb-min-size: calc(2rem + 4px)` in their own theme at any time, which no
4309
+ * repo-side check could ever catch.
4310
+ *
4311
+ * So these resolve rather than parse: the fast path still handles a plain `12px`, and anything
4312
+ * else is handed to the browser on a throwaway element, which also gets `rem`, `em`, `%` and
4313
+ * nested `var()` right for free. The probe is appended to the element the token was read from,
4314
+ * so relative units resolve against the same context the real value would.
4315
+ */
4316
+ /** Reads a length token in pixels. Returns `fallback` for an empty, unparseable or zero value. */
4317
+ function resolveLengthToken(el, token, fallback) {
4318
+ const raw = getComputedStyle(el).getPropertyValue(token).trim();
4319
+ if (!raw)
4320
+ return fallback;
4321
+ const plain = PX.exec(raw);
4322
+ if (plain)
4323
+ return Number.parseFloat(plain[1]);
4324
+ return probe(el, `width:${raw}`, (style) => readPx(style.width), fallback);
4325
+ }
4326
+ /**
4327
+ * Reads a unitless numeric token (a `z-index`). Same resolution path as lengths — `z-index`
4328
+ * takes an integer, so the browser computes `calc()` there too.
4329
+ */
4330
+ function resolveNumberToken(el, token, fallback) {
4331
+ const raw = getComputedStyle(el).getPropertyValue(token).trim();
4332
+ if (!raw)
4333
+ return fallback;
4334
+ const plain = NUMBER.exec(raw);
4335
+ if (plain)
4336
+ return Number.parseFloat(plain[1]);
4337
+ return probe(el, `position:relative;z-index:${raw}`, (s) => {
4338
+ const resolved = NUMBER.exec(s.zIndex.trim());
4339
+ return resolved ? Number.parseFloat(resolved[1]) : Number.NaN;
4340
+ }, fallback);
4341
+ }
4342
+ const PX = /^(-?\d*\.?\d+)px$/;
4343
+ const NUMBER = /^(-?\d*\.?\d+)$/;
4344
+ /**
4345
+ * A computed length, but only if it really was computed.
4346
+ *
4347
+ * An environment with no layout engine — jsdom, which is what this library's own unit tests run
4348
+ * in — hands back the *specified* string from `getComputedStyle`, so a probe styled `width: 2em`
4349
+ * reads back `"2em"`, and `parseFloat` would happily call that `2`. A wrong number is worse than
4350
+ * the fallback, so anything that is not a resolved pixel value is rejected.
4351
+ */
4352
+ function readPx(value) {
4353
+ const m = PX.exec(value.trim());
4354
+ return m ? Number.parseFloat(m[1]) : Number.NaN;
4355
+ }
4356
+ /**
4357
+ * Measures `declaration` on a hidden child of `host` and reads the result back.
4358
+ *
4359
+ * `visibility: hidden` rather than `display: none`, because a `display: none` element has no
4360
+ * used value to read — that is the whole failure this helper exists to avoid. `position:
4361
+ * absolute` keeps it out of the host's layout, so a flex or grid host is not disturbed by the
4362
+ * measurement.
4363
+ */
4364
+ function probe(host, declaration, read, fallback) {
4365
+ const doc = host.ownerDocument;
4366
+ if (!doc?.defaultView)
4367
+ return fallback;
4368
+ const el = doc.createElement('div');
4369
+ el.style.cssText = `position:absolute;visibility:hidden;pointer-events:none;${declaration}`;
4370
+ host.appendChild(el);
4371
+ let value;
4372
+ try {
4373
+ value = read(doc.defaultView.getComputedStyle(el));
4374
+ }
4375
+ finally {
4376
+ el.remove();
4377
+ }
4378
+ return Number.isFinite(value) ? value : fallback;
4379
+ }
4380
+
4381
+ const DEFAULT_GAP = 8;
4382
+ const DEFAULT_VIEWPORT_PADDING = 8;
4383
+ function spaceFor(side, target, viewport) {
4384
+ switch (side) {
4385
+ case 'top':
4386
+ return target.top;
4387
+ case 'bottom':
4388
+ return viewport.height - target.bottom;
4389
+ case 'left':
4390
+ return target.left;
4391
+ case 'right':
4392
+ return viewport.width - target.right;
4393
+ }
4394
+ }
4395
+ function fits(side, target, bubble, viewport, gap, viewportPadding) {
4396
+ const needed = (side === 'top' || side === 'bottom' ? bubble.height : bubble.width) + gap + viewportPadding;
4397
+ return spaceFor(side, target, viewport) >= needed;
4398
+ }
4399
+ const OPPOSITE = {
4400
+ top: 'bottom',
4401
+ bottom: 'top',
4402
+ left: 'right',
4403
+ right: 'left',
4404
+ };
4405
+ /**
4406
+ * Picks which side the bubble renders on. `'auto'` tries top, bottom, then the two horizontal
4407
+ * sides — top-first matches the conventional tooltip default — and falls back to whichever
4408
+ * side has the most room if the bubble doesn't fully fit anywhere. An explicit side flips to
4409
+ * its opposite when it has no room but the opposite does, same as `resolveDropdownDirection`
4410
+ * does for up/down; unlike that helper, a request for a specific side is honoured as-is
4411
+ * (no flip) whenever it fits, since the caller asked for that side deliberately.
4412
+ *
4413
+ * **`direction` mirrors the horizontal preference only.** In RTL, `'auto'` prefers the left
4414
+ * side where LTR prefers the right, so a tooltip opens away from the text it belongs to in the
4415
+ * same way in both. An explicit `'left'`/`'right'` is *not* mirrored: those are physical words
4416
+ * in a physical API, and a consumer who wrote `position="right"` meant the right of the screen.
4417
+ * `'start'`/`'end'` would be the logical spelling, and there is deliberately no such value —
4418
+ * `'auto'` already does the right thing for a direction-aware layout.
4419
+ */
4420
+ function resolveTooltipSide(position, target, bubble, viewport, gap = DEFAULT_GAP, viewportPadding = DEFAULT_VIEWPORT_PADDING, direction = 'ltr') {
4421
+ if (position !== 'auto') {
4422
+ if (fits(position, target, bubble, viewport, gap, viewportPadding))
4423
+ return position;
4424
+ const opposite = OPPOSITE[position];
4425
+ return fits(opposite, target, bubble, viewport, gap, viewportPadding) ? opposite : position;
4426
+ }
4427
+ const preferenceOrder = direction === 'rtl' ? ['top', 'bottom', 'left', 'right'] : ['top', 'bottom', 'right', 'left'];
4428
+ const fitting = preferenceOrder.find((side) => fits(side, target, bubble, viewport, gap, viewportPadding));
4429
+ if (fitting)
4430
+ return fitting;
4431
+ return preferenceOrder.reduce((best, side) => spaceFor(side, target, viewport) > spaceFor(best, target, viewport) ? side : best);
4432
+ }
4433
+ /**
4434
+ * Resolves the bubble's side and its `position: fixed` top/left, centered on the target's
4435
+ * midpoint along the cross axis and clamped so it never renders past the viewport edge —
4436
+ * the centering can push it there for a target near a corner.
4437
+ */
4438
+ function resolveTooltipPlacement(position, target, bubble, viewport, gap = DEFAULT_GAP, viewportPadding = DEFAULT_VIEWPORT_PADDING, direction = 'ltr') {
4439
+ const side = resolveTooltipSide(position, target, bubble, viewport, gap, viewportPadding, direction);
4440
+ const centerX = target.left + target.width / 2;
4441
+ const centerY = target.top + target.height / 2;
4442
+ let top;
4443
+ let left;
4444
+ switch (side) {
4445
+ case 'top':
4446
+ top = target.top - gap - bubble.height;
4447
+ left = centerX - bubble.width / 2;
4448
+ break;
4449
+ case 'bottom':
4450
+ top = target.bottom + gap;
4451
+ left = centerX - bubble.width / 2;
4452
+ break;
4453
+ case 'left':
4454
+ top = centerY - bubble.height / 2;
4455
+ left = target.left - gap - bubble.width;
4456
+ break;
4457
+ case 'right':
4458
+ top = centerY - bubble.height / 2;
4459
+ left = target.right + gap;
4460
+ break;
4461
+ }
4462
+ const maxLeft = Math.max(viewportPadding, viewport.width - bubble.width - viewportPadding);
4463
+ const maxTop = Math.max(viewportPadding, viewport.height - bubble.height - viewportPadding);
4464
+ left = Math.min(Math.max(left, viewportPadding), maxLeft);
4465
+ top = Math.min(Math.max(top, viewportPadding), maxTop);
4466
+ return { side, top, left };
4467
+ }
4468
+
4469
+ const DEFAULT_OVERSCAN = 4;
4470
+ /**
4471
+ * The arithmetic behind a windowed list whose rows are **not** all the same height.
4472
+ *
4473
+ * `GogVirtualWindow` is the one to reach for wherever the rows are uniform — it is exact, it needs
4474
+ * no measurements, and a multiplication beats a binary search. This exists because `gog-table`
4475
+ * cannot use it, for a reason that is a fact about CSS rather than about this library:
4476
+ *
4477
+ * **A table row's height cannot be pinned.** `height` on a `<tr>` or a `<td>` is a *minimum* in
4478
+ * table layout, so a cell whose content wraps makes its row taller and nothing can stop it — one
4479
+ * cell taken from 40 to 600 characters measured 39px → 173.75px, under `table-layout: fixed`, with
4480
+ * the column width unchanged. A single-pitch window cannot describe that list.
4481
+ *
4482
+ * So this holds a height per row: the measured one where a row has ever been rendered, and an
4483
+ * estimate everywhere else. `range()` is then a binary search over the prefix sums rather than a
4484
+ * division.
4485
+ *
4486
+ * **Being an estimate is the whole difficulty**, and it lives in `applyMeasurements`, not here.
4487
+ * See its note: correcting a row *above* the viewport moves everything below it, under the reader,
4488
+ * while they scroll — so the correction has to be reported as a scroll delta and applied in the
4489
+ * same frame.
4490
+ *
4491
+ * @see docs/table-virtualization.md — the survey behind this, and what adopting it has to get right
4492
+ */
4493
+ class GogVariableWindow {
4494
+ inputs;
4495
+ overscan;
4496
+ /**
4497
+ * Measured heights by row index, `undefined` where a row has never been rendered.
4498
+ *
4499
+ * A sparse array rather than a `Map`: the index *is* the key, the reads are sequential, and the
4500
+ * prefix sum walks it start to finish either way.
4501
+ */
4502
+ measured = signal([], ...(ngDevMode ? [{ debugName: "measured" }] : /* istanbul ignore next */ []));
4503
+ constructor(inputs) {
4504
+ this.inputs = inputs;
4505
+ this.overscan = Math.max(0, Math.trunc(inputs.overscan ?? DEFAULT_OVERSCAN));
4506
+ }
4507
+ /**
4508
+ * Running total of row heights: `offsets[i]` is where row `i` starts, and `offsets[count]` is
4509
+ * the list's full height.
4510
+ *
4511
+ * Recomputed whole rather than patched. It is O(n) on a signal that changes only when a
4512
+ * measurement actually differs, and an incremental structure here would be a Fenwick tree
4513
+ * guarding a loop that costs a fraction of the layout it feeds.
4514
+ */
4515
+ offsets = computed(() => {
4516
+ const count = this.safeCount();
4517
+ const estimate = this.safeEstimate();
4518
+ const measured = this.measured();
4519
+ const offsets = new Array(count + 1);
4520
+ offsets[0] = 0;
4521
+ for (let i = 0; i < count; i++) {
4522
+ const height = measured[i];
4523
+ offsets[i + 1] = offsets[i] + (height === undefined ? estimate : height);
4524
+ }
4525
+ return offsets;
4526
+ }, ...(ngDevMode ? [{ debugName: "offsets" }] : /* istanbul ignore next */ []));
4527
+ /** What the scroller has to believe, so its thumb is the right size. */
4528
+ totalHeight = computed(() => {
4529
+ const offsets = this.offsets();
4530
+ return offsets[offsets.length - 1] ?? 0;
4531
+ }, ...(ngDevMode ? [{ debugName: "totalHeight" }] : /* istanbul ignore next */ []));
4532
+ /**
4533
+ * The slice worth rendering.
4534
+ *
4535
+ * **Degrades to the whole list rather than to nothing**, the same rule `GogVirtualWindow`
4536
+ * follows and for the same reason: a caller that has not measured anything yet has an estimate
4537
+ * of zero, and a window computed from it renders an empty panel that reads as broken. Rendering
4538
+ * everything is the pre-window behaviour — slow, and correct.
4539
+ */
4540
+ range = computed(() => {
4541
+ const count = this.safeCount();
4542
+ if (count === 0)
4543
+ return { start: 0, end: 0 };
4544
+ const viewportHeight = this.safeViewportHeight();
4545
+ if (viewportHeight === 0 || this.totalHeight() === 0)
4546
+ return { start: 0, end: count };
4547
+ const offsets = this.offsets();
4548
+ const scrollTop = this.safeScrollTop();
4549
+ const first = this.indexAt(offsets, scrollTop);
4550
+ const last = this.indexAt(offsets, scrollTop + viewportHeight);
4551
+ return {
4552
+ start: Math.max(0, first - this.overscan),
4553
+ // `last` is the row the viewport's bottom edge falls in, so it is rendered: +1 for the
4554
+ // exclusive end, and another because a viewport that ends exactly on a boundary still
4555
+ // straddles the next row the moment it moves by a fraction of a pixel.
4556
+ end: Math.min(count, last + 1 + this.overscan),
4557
+ };
4558
+ }, ...(ngDevMode ? [{ debugName: "range" }] : /* istanbul ignore next */ []));
4559
+ /** Filler above the rendered slice, in px, so the rows sit where their offsets say. */
4560
+ padBefore = computed(() => this.offsets()[this.range().start] ?? 0, ...(ngDevMode ? [{ debugName: "padBefore" }] : /* istanbul ignore next */ []));
4561
+ /** Filler below, in px. Derived from the total so rounding cannot leave a gap. */
4562
+ padAfter = computed(() => Math.max(0, this.totalHeight() - (this.offsets()[this.range().end] ?? 0)), ...(ngDevMode ? [{ debugName: "padAfter" }] : /* istanbul ignore next */ []));
4563
+ /**
4564
+ * Records what the rendered rows actually measured, and returns **how far the content above the
4565
+ * viewport moved** — which the caller must add to the scroller's `scrollTop`, in the same frame.
4566
+ *
4567
+ * This is the whole difficulty of a variable window, and it is not obvious from the arithmetic.
4568
+ * Every row starts as an estimate. The moment one is measured and disagrees, every row after it
4569
+ * shifts by the difference — including, when the corrected row is *above* the viewport, the rows
4570
+ * the reader is looking at. Scroll up into a region of taller-than-estimated rows and the
4571
+ * content jumps away from the pointer on every frame; the list appears to fight the scroll.
4572
+ *
4573
+ * Adding the delta back to `scrollTop` holds the visible rows still: the content moved down by
4574
+ * `delta`, so the viewport moves down by `delta` too and the same pixels stay under the reader.
4575
+ *
4576
+ * `heights[i]` is keyed by **real row index**, and a `0` or a negative is ignored rather than
4577
+ * stored — an unlaid-out row measures zero, and believing it would collapse the list.
4578
+ */
4579
+ applyMeasurements(heights) {
4580
+ if (heights.size === 0)
4581
+ return 0;
4582
+ const estimate = this.safeEstimate();
4583
+ const current = this.measured();
4584
+ const next = current.slice();
4585
+ let changed = false;
4586
+ let deltaAbove = 0;
4587
+ const firstVisible = this.indexAt(this.offsets(), this.safeScrollTop());
4588
+ for (const [index, height] of heights) {
4589
+ if (!Number.isFinite(height) || height <= 0 || index < 0)
4590
+ continue;
4591
+ const previous = next[index] ?? estimate;
4592
+ if (Math.abs(previous - height) < 0.5)
4593
+ continue;
4594
+ next[index] = height;
4595
+ changed = true;
4596
+ // Only rows that start above the first visible one move it. A correction to a row the
4597
+ // reader is looking at, or below it, moves what comes after and leaves the viewport alone.
4598
+ if (index < firstVisible)
4599
+ deltaAbove += height - previous;
4600
+ }
4601
+ if (!changed)
4602
+ return 0;
4603
+ this.measured.set(next);
4604
+ return deltaAbove;
4605
+ }
4606
+ /**
4607
+ * Drops every measurement. For when the rows stop describing the same data — a new page, a new
4608
+ * sort, a filter — after which a cached height belongs to a row that is no longer there.
4609
+ */
4610
+ reset() {
4611
+ if (this.measured().length > 0)
4612
+ this.measured.set([]);
4613
+ }
4614
+ /** Where the scroller has to be for `index` to be fully visible, or `null` if it already is. */
4615
+ scrollOffsetFor(index) {
4616
+ const count = this.safeCount();
4617
+ const viewportHeight = this.safeViewportHeight();
4618
+ if (count === 0 || viewportHeight === 0)
4619
+ return null;
4620
+ const offsets = this.offsets();
4621
+ const clamped = Math.min(Math.max(0, Math.trunc(index)), count - 1);
4622
+ const rowTop = offsets[clamped];
4623
+ const rowBottom = offsets[clamped + 1];
4624
+ const scrollTop = this.safeScrollTop();
4625
+ if (rowTop < scrollTop)
4626
+ return rowTop;
4627
+ if (rowBottom > scrollTop + viewportHeight)
4628
+ return rowBottom - viewportHeight;
4629
+ return null;
4630
+ }
4631
+ /**
4632
+ * The index of the row containing `position`, by binary search over the offsets.
4633
+ *
4634
+ * Returns the **last** index whose start is `<= position`, clamped to the list. A linear scan
4635
+ * would be fine at a thousand rows and is not at the hundreds of thousands this exists for, and
4636
+ * it runs on every scroll frame.
4637
+ */
4638
+ indexAt(offsets, position) {
4639
+ const count = offsets.length - 1;
4640
+ if (count <= 0)
4641
+ return 0;
4642
+ let low = 0;
4643
+ let high = count - 1;
4644
+ while (low < high) {
4645
+ const mid = (low + high + 1) >> 1;
4646
+ if (offsets[mid] <= position)
4647
+ low = mid;
4648
+ else
4649
+ high = mid - 1;
4650
+ }
4651
+ return low;
4652
+ }
4653
+ safeCount() {
4654
+ return Math.max(0, Math.trunc(this.inputs.count()));
4655
+ }
4656
+ safeEstimate() {
4657
+ const value = this.inputs.estimatedRowHeight();
4658
+ return Number.isFinite(value) && value > 0 ? value : 0;
4659
+ }
4660
+ safeViewportHeight() {
4661
+ const value = this.inputs.viewportHeight();
4662
+ return Number.isFinite(value) && value > 0 ? value : 0;
4663
+ }
4664
+ safeScrollTop() {
4665
+ const value = this.inputs.scrollTop();
4666
+ if (!Number.isFinite(value) || value <= 0)
4667
+ return 0;
4668
+ // A scroller can report past its own end during an elastic bounce, and — more often here —
4669
+ // right after a measurement shortened the list under it.
4670
+ return Math.min(value, Math.max(0, this.totalHeight() - this.safeViewportHeight()));
4671
+ }
4672
+ }
4673
+
4674
+ /*
4675
+ * `@guildofgleks/ui/shared` -- the floor every other entry point stands on.
4676
+ *
4677
+ * An entry point of its own for one reason, measured in docs/entry-points.md: an
4678
+ * `InjectionToken` is one instance only if the file declaring it is compiled into one bundle, and
4679
+ * a file imported across entry points by relative path is compiled into every bundle that
4680
+ * reaches it. So `GOG_CONFIG` and everything beside it live here, and the root and every
4681
+ * secondary import them by package path.
4682
+ *
4683
+ * Whole modules are re-exported because this barrel is the package's internal plumbing, not a
4684
+ * consumer-facing API: `src/public-api.ts` names what the root publishes, one symbol at a time.
4685
+ */
4686
+
4687
+ /**
4688
+ * Generated bundle index. Do not edit.
4689
+ */
4690
+
4691
+ export { DEFAULT_RIPPLE, GOG_CHECKABLE_CONTROL_PADDING, GOG_CHECKABLE_CONTROL_SIZE_MAP, GOG_CONFIG, GOG_DEPRECATIONS, GOG_ICONS, GOG_TOKEN_GROUPS, GogClearableState, GogDropdownBase, GogDropdownChevronDirective, GogDropdownOptionDirective, GogDropdownOverlay, GogErrorState, GogFloatLabelState, GogVariableWindow, GogVirtualWindow, ICON_DEFS, addDays, addMonths, addYears, buildMonthGrid, clampDate, copyTimeOnto, daysInMonth, formatDate, getByPath, handleRovingFocusKeydown, isAfterDay, isBeforeDay, isInRange, isRovingFocusKey, isSameDay, isSameMonth, isSameOptionValue, isWithinBounds, localeFirstDayOfWeek, monthNames, nextGogControlId, nextRovingFocusIndex, parseDate, provideGogConfig, provideGogIcons, readOption, resolveConfigured, resolveCssLengthPx, resolveDropdownDirection, resolveDropdownPlacement, resolveLengthToken, resolveNumberToken, resolveRipple, resolveTooltipPlacement, resolveTooltipSide, scopedOverlayDirection, scopedOverlayTheme, startOfDay, weekdayNames, withTime };
4692
+ //# sourceMappingURL=guildofgleks-ui-shared.mjs.map