@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.
- package/AGENTS.md +179 -28
- package/CHANGELOG.md +612 -0
- package/README.md +23 -3
- package/TOKENS.md +52 -51
- package/fesm2022/guildofgleks-ui-datepicker.mjs +20 -0
- package/fesm2022/guildofgleks-ui-datepicker.mjs.map +1 -0
- package/fesm2022/guildofgleks-ui-dialog.mjs +20 -0
- package/fesm2022/guildofgleks-ui-dialog.mjs.map +1 -0
- package/fesm2022/guildofgleks-ui-shared.mjs +4692 -0
- package/fesm2022/guildofgleks-ui-shared.mjs.map +1 -0
- package/fesm2022/guildofgleks-ui-table.mjs +20 -0
- package/fesm2022/guildofgleks-ui-table.mjs.map +1 -0
- package/fesm2022/guildofgleks-ui.mjs +607 -3760
- package/fesm2022/guildofgleks-ui.mjs.map +1 -1
- package/package.json +17 -1
- package/styles/button.css +5 -5
- package/styles/presets/parchment.css +89 -86
- package/styles/presets/terminal.css +107 -102
- package/styles/theme.css +128 -23
- package/types/guildofgleks-ui-datepicker.d.ts +1 -0
- package/types/guildofgleks-ui-dialog.d.ts +1 -0
- package/types/guildofgleks-ui-shared.d.ts +2000 -0
- package/types/guildofgleks-ui-table.d.ts +1 -0
- package/types/guildofgleks-ui.d.ts +279 -1226
|
@@ -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
|