@guildofgleks/ui 21.11.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 +2133 -1964
- package/CHANGELOG.md +3468 -2692
- package/README.md +523 -467
- package/TOKENS.md +52 -49
- 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 +609 -3719
- package/fesm2022/guildofgleks-ui.mjs.map +1 -1
- package/package.json +17 -1
- package/styles/button.css +5 -5
- package/styles/presets/bevel.css +110 -87
- package/styles/presets/ledger.css +22 -2
- package/styles/presets/material.css +157 -122
- package/styles/presets/one-dark.css +101 -82
- package/styles/presets/one-light.css +99 -73
- package/styles/presets/parchment.css +89 -63
- package/styles/presets/primeng.css +175 -139
- package/styles/presets/slate.css +21 -1
- package/styles/presets/terminal.css +43 -8
- package/styles/theme.css +2905 -2591
- 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,2000 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { Signal, InjectionToken, Type, Provider, DoCheck, TemplateRef, ModelSignal, ElementRef, ApplicationRef } from '@angular/core';
|
|
3
|
+
import { NgControl, ControlValueAccessor } from '@angular/forms';
|
|
4
|
+
import * as _guildofgleks_ui_shared from '@guildofgleks/ui/shared';
|
|
5
|
+
|
|
6
|
+
interface GogCheckableControlSizeConfig {
|
|
7
|
+
readonly boxSize: string;
|
|
8
|
+
readonly labelSize: string;
|
|
9
|
+
readonly indicatorSize: string;
|
|
10
|
+
}
|
|
11
|
+
declare const GOG_CHECKABLE_CONTROL_PADDING = "var(--gog-control-checkbox-padding, 8px)";
|
|
12
|
+
declare const GOG_CHECKABLE_CONTROL_SIZE_MAP: {
|
|
13
|
+
readonly xsm: {
|
|
14
|
+
readonly boxSize: "var(--gog-control-checkbox-box-size-xsm, 12px)";
|
|
15
|
+
readonly labelSize: "var(--gog-control-checkbox-label-size-xsm, 0.6875rem)";
|
|
16
|
+
readonly indicatorSize: "var(--gog-control-checkbox-icon-size-xsm, 10px)";
|
|
17
|
+
};
|
|
18
|
+
readonly sm: {
|
|
19
|
+
readonly boxSize: "var(--gog-control-checkbox-box-size-sm, 18px)";
|
|
20
|
+
readonly labelSize: "var(--gog-control-checkbox-label-size-sm, 0.8125rem)";
|
|
21
|
+
readonly indicatorSize: "var(--gog-control-checkbox-icon-size-sm, 12px)";
|
|
22
|
+
};
|
|
23
|
+
readonly md: {
|
|
24
|
+
readonly boxSize: "var(--gog-control-checkbox-box-size-md, 24px)";
|
|
25
|
+
readonly labelSize: "var(--gog-control-checkbox-label-size-md, 0.9375rem)";
|
|
26
|
+
readonly indicatorSize: "var(--gog-control-checkbox-icon-size-md, 14px)";
|
|
27
|
+
};
|
|
28
|
+
readonly lg: {
|
|
29
|
+
readonly boxSize: "var(--gog-control-checkbox-box-size-lg, 32px)";
|
|
30
|
+
readonly labelSize: "var(--gog-control-checkbox-label-size-lg, 1.0625rem)";
|
|
31
|
+
readonly indicatorSize: "var(--gog-control-checkbox-icon-size-lg, 18px)";
|
|
32
|
+
};
|
|
33
|
+
readonly slg: {
|
|
34
|
+
readonly boxSize: "var(--gog-control-checkbox-box-size-slg, 40px)";
|
|
35
|
+
readonly labelSize: "var(--gog-control-checkbox-label-size-slg, 1.1875rem)";
|
|
36
|
+
readonly indicatorSize: "var(--gog-control-checkbox-icon-size-slg, 22px)";
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type GogSize = 'xsm' | 'sm' | 'md' | 'lg' | 'slg';
|
|
41
|
+
type GogVariant = 'primary' | 'secondary' | 'outline' | 'ghost';
|
|
42
|
+
/**
|
|
43
|
+
* The `aria-haspopup` values that make sense on a button, matching the ARIA 1.2 enumeration.
|
|
44
|
+
* `true` is a synonym for `'menu'` in the spec; prefer the specific value, since a screen
|
|
45
|
+
* reader announces it ("has menu", "has dialog").
|
|
46
|
+
*/
|
|
47
|
+
type GogAriaHasPopup = boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog';
|
|
48
|
+
/**
|
|
49
|
+
* How a surface paints itself, shared by `gog-card` and `gog-panel` so the two agree:
|
|
50
|
+
* - `'outlined'` — a border, no shadow. The quietest, and right for a grid of many.
|
|
51
|
+
* - `'elevated'` — the shared surface shadow (`--gog-panel-shadow`), no border.
|
|
52
|
+
* - `'filled'` — a tinted background, neither border nor shadow.
|
|
53
|
+
*/
|
|
54
|
+
type GogSurfaceVariant = 'outlined' | 'elevated' | 'filled';
|
|
55
|
+
/**
|
|
56
|
+
* Which way a component lays its children out. Shared rather than re-declared per component:
|
|
57
|
+
* `gog-radio-group`, `gog-slider`, `gog-divider`, `gog-tabs` and `gog-button-toggle-group` all
|
|
58
|
+
* mean the same thing by it, and `roving-focus.ts` picks its arrow pair from this.
|
|
59
|
+
*/
|
|
60
|
+
type GogOrientation = 'horizontal' | 'vertical';
|
|
61
|
+
type GogSpinnerVariant = 'runic' | 'ring' | 'custom';
|
|
62
|
+
type GogTagVariant = 'success' | 'danger' | 'warning' | 'info';
|
|
63
|
+
type GogTagShape = 'rounded' | 'pill';
|
|
64
|
+
type GogSkeletonShape = 'text' | 'circle' | 'rect';
|
|
65
|
+
type GogSkeletonAnimation = 'pulse' | 'wave' | 'none';
|
|
66
|
+
type GogPaginatorRangeMode = 'window' | 'ellipsis';
|
|
67
|
+
type GogSliderOrientation = GogOrientation;
|
|
68
|
+
/** A `gog-slider`'s value pair when `range` is on. `start` is always kept ≤ `end`. */
|
|
69
|
+
interface GogSliderRange {
|
|
70
|
+
start: number;
|
|
71
|
+
end: number;
|
|
72
|
+
}
|
|
73
|
+
type GogScrollAxis = 'vertical' | 'horizontal' | 'both';
|
|
74
|
+
type GogScrollSize = 'normal' | 'thin';
|
|
75
|
+
/**
|
|
76
|
+
* Mirrors the CSS `overscroll-behavior` value space directly:
|
|
77
|
+
* - `'auto'` — scrolling past this instance's edge chains to the next scrollable ancestor
|
|
78
|
+
* (a page, a dialog body, another gog-scroll), the same as an un-customized native
|
|
79
|
+
* `overflow: auto` div. The default — matches what most embedded content expects.
|
|
80
|
+
* - `'contain'` — scrolling stops dead at this instance's edge; nothing behind it moves.
|
|
81
|
+
* Suited to overlay-style panels (a dropdown, a modal) where scrolling past the end
|
|
82
|
+
* shouldn't also scroll whatever is behind them.
|
|
83
|
+
* - `'none'` — like `'contain'`, and additionally suppresses the platform's own overscroll
|
|
84
|
+
* affordances (rubber-banding, pull-to-refresh) for this instance.
|
|
85
|
+
*/
|
|
86
|
+
type GogScrollOverscrollBehavior = 'auto' | 'contain' | 'none';
|
|
87
|
+
/**
|
|
88
|
+
* Which side of the trigger the tooltip bubble renders on. `'auto'` (the default) picks
|
|
89
|
+
* whichever of the four sides has room for the bubble, preferring top, then bottom, then
|
|
90
|
+
* right, then left — see `resolveTooltipPlacement` in `tooltip-position.ts`. An explicit
|
|
91
|
+
* side still flips to its opposite if the requested side has no room but the opposite does.
|
|
92
|
+
*/
|
|
93
|
+
type GogTooltipPosition = 'auto' | 'top' | 'bottom' | 'left' | 'right';
|
|
94
|
+
/**
|
|
95
|
+
* Where a field's label sits once it "floats" out of its resting position (overlapping the
|
|
96
|
+
* field like a placeholder) on focus or once the field has content. `'none'` (the default)
|
|
97
|
+
* keeps today's static label-above-the-field layout — nothing floats.
|
|
98
|
+
* - `'in'` — floats up but stays fully inside the field's border.
|
|
99
|
+
* - `'on'` — floats up until vertically centered on the top border line.
|
|
100
|
+
* - `'over'` — floats all the way above the field, outside the border.
|
|
101
|
+
*/
|
|
102
|
+
type GogFloatLabelVariant = 'none' | 'in' | 'on' | 'over';
|
|
103
|
+
/**
|
|
104
|
+
* Which end of a dropdown panel the search box sticks to. Matches `gog-multiselect`'s
|
|
105
|
+
* `controlsPosition` vocabulary — the same idea applied to its select-all row.
|
|
106
|
+
*/
|
|
107
|
+
type GogDropdownFilterPosition = 'top' | 'bottom';
|
|
108
|
+
/** How `gog-divider` paints its rule. */
|
|
109
|
+
type GogDividerVariant = 'solid' | 'dashed' | 'dotted';
|
|
110
|
+
/**
|
|
111
|
+
* Which corner of its host a `gogBadge` sits on. Named by block/inline edge rather than
|
|
112
|
+
* left/right so it follows the writing direction in an RTL layout.
|
|
113
|
+
*/
|
|
114
|
+
type GogBadgePosition = 'top-end' | 'top-start' | 'bottom-end' | 'bottom-start';
|
|
115
|
+
/**
|
|
116
|
+
* - `'determinate'` — the bar reflects `value`.
|
|
117
|
+
* - `'indeterminate'` — work of unknown length; the bar animates and reports no value to
|
|
118
|
+
* assistive tech, which is what marks it indeterminate.
|
|
119
|
+
* - `'buffer'` — `value` plus a second, lighter `buffer` level ahead of it (streaming/preload).
|
|
120
|
+
*/
|
|
121
|
+
type GogProgressbarMode = 'determinate' | 'indeterminate' | 'buffer';
|
|
122
|
+
/**
|
|
123
|
+
* `gog-progressbar`'s colour. Wider than `GogTagVariant` by one: progress is usually just
|
|
124
|
+
* "the app is working", which is the accent colour rather than any of the four status hues —
|
|
125
|
+
* so `'accent'` is the default and the status names are the exception.
|
|
126
|
+
*/
|
|
127
|
+
type GogProgressbarVariant = GogSeverity;
|
|
128
|
+
/**
|
|
129
|
+
* What an action or a reading *means*, as opposed to how loudly it is drawn. `'accent'` is the
|
|
130
|
+
* absence of a claim — the library's own colour, and the default everywhere this appears — and
|
|
131
|
+
* the four status names are the exception, so nothing has to opt out of a severity it does not
|
|
132
|
+
* have.
|
|
133
|
+
*
|
|
134
|
+
* Deliberately orthogonal to `GogVariant` on `gog-button`: a destructive action can be filled,
|
|
135
|
+
* outlined or a ghost and is destructive in all three, so `severity` says what it does and
|
|
136
|
+
* `variant` says only how much room it takes up. Shared rather than re-declared per component —
|
|
137
|
+
* `gog-progressbar` had this exact union first, under its own name, which is kept as an alias.
|
|
138
|
+
*/
|
|
139
|
+
type GogSeverity = 'accent' | 'success' | 'danger' | 'warning' | 'info';
|
|
140
|
+
/**
|
|
141
|
+
* How `gog-alert` announces itself, and it is a decision the component cannot make for you.
|
|
142
|
+
*
|
|
143
|
+
* - `'assertive'` — interrupts whatever a screen reader is saying. For a message that *just
|
|
144
|
+
* happened* and changes what the reader should do: a failed save, a rejected payment.
|
|
145
|
+
* - `'polite'` — waits for a pause. The right answer for most appearing messages.
|
|
146
|
+
* - `'off'` — announces nothing. **The right answer for a message that was already on the page
|
|
147
|
+
* when it loaded**, which is otherwise an interruption about something nobody asked about yet.
|
|
148
|
+
*
|
|
149
|
+
* The choice follows from *why the message appeared*, not from how bad it is — the same red box
|
|
150
|
+
* earns an interruption on submit and does not on page load. Nothing in `@angular/core` lets a
|
|
151
|
+
* component tell those apart: `ApplicationRef` exposes no stability member a component can read
|
|
152
|
+
* synchronously at construction, and `afterNextRender` reports the component's *own* first render
|
|
153
|
+
* rather than the application's. Measured before being decided; see `docs/alert.md` §1.
|
|
154
|
+
*/
|
|
155
|
+
type GogAlertLive = 'assertive' | 'polite' | 'off';
|
|
156
|
+
/**
|
|
157
|
+
* - `'joined'` — one segmented control, buttons sharing borders (Material's look).
|
|
158
|
+
* - `'separated'` — discrete buttons with a gap between them.
|
|
159
|
+
*/
|
|
160
|
+
type GogButtonToggleAppearance = 'joined' | 'separated';
|
|
161
|
+
/** How the tab headers distribute along the tablist. `'stretch'` makes them share the width. */
|
|
162
|
+
type GogTabsAlign = 'start' | 'center' | 'end' | 'stretch';
|
|
163
|
+
/** Whether `gog-datepicker` picks one day or a start/end pair. */
|
|
164
|
+
type GogDateSelectionMode = 'single' | 'range';
|
|
165
|
+
/** Clock convention for `gog-datepicker`'s time section. */
|
|
166
|
+
type GogHourFormat = '12' | '24';
|
|
167
|
+
/** Matches the native CSS `resize` value space, applied to `gog-textarea`'s field. */
|
|
168
|
+
type GogTextareaResize = 'vertical' | 'horizontal' | 'both' | 'none';
|
|
169
|
+
/**
|
|
170
|
+
* The `type`s `gog-inputfield` renders as a single-line text-entry field.
|
|
171
|
+
*
|
|
172
|
+
* Deliberately not the whole native list: `checkbox`, `radio`, `range`, `file`, `color`,
|
|
173
|
+
* `submit`, `hidden` and friends are different controls with different markup and different
|
|
174
|
+
* accessibility contracts — the library ships `gog-checkbox`, `gog-radio-group`, `gog-slider`
|
|
175
|
+
* and `gog-button` for those rather than making one component shape-shift.
|
|
176
|
+
*/
|
|
177
|
+
type GogInputType = 'text' | 'password' | 'email' | 'number' | 'search' | 'tel' | 'url' | 'date' | 'time' | 'datetime-local';
|
|
178
|
+
/**
|
|
179
|
+
* Mirrors the native `inputmode` attribute — the hint that decides which on-screen keyboard a
|
|
180
|
+
* touch device brings up. Worth setting whenever `type` alone doesn't imply it: a postcode or a
|
|
181
|
+
* card number is `type="text"` (no browser validation wanted) but `inputmode="numeric"`.
|
|
182
|
+
*/
|
|
183
|
+
type GogInputMode = 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
|
|
184
|
+
/**
|
|
185
|
+
* Which corner a toast stack occupies.
|
|
186
|
+
*
|
|
187
|
+
* Here rather than beside `ToastService`, where it was declared until 2026-09-12, for two reasons
|
|
188
|
+
* that turned out to be the same one: `gleks-ui-library.instructions.md` puts public types in this
|
|
189
|
+
* file, and `shared/config.ts` needs it — so declaring it in a service made `shared` depend on
|
|
190
|
+
* `services`, which depends on `shared`. A cycle nothing minded while the package is one flat
|
|
191
|
+
* bundle, and one that a secondary entry point cannot express at all.
|
|
192
|
+
*/
|
|
193
|
+
type ToastPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* `'manual'` (default): the field shows `errorMessage` for as long as it is non-empty — the
|
|
197
|
+
* consumer decides when to clear it, e.g.
|
|
198
|
+
* `errorMessage="control.invalid && control.touched ? 'Required' : ''"`.
|
|
199
|
+
*
|
|
200
|
+
* `'auto'`: shown once the attached form control has been touched and is invalid — the
|
|
201
|
+
* consumer only supplies the message text, not the timing. Requires a
|
|
202
|
+
* `[formControl]` / `formControlName` on the element; without one this falls back to
|
|
203
|
+
* `'manual'`, since there is no control state to key off.
|
|
204
|
+
*/
|
|
205
|
+
type GogErrorDisplay = 'auto' | 'manual';
|
|
206
|
+
/**
|
|
207
|
+
* Shared error-visibility state for the library's form controls. A plain class rather than
|
|
208
|
+
* a base class or directive so it drops into components that can't share a base class
|
|
209
|
+
* (`gog-inputfield`, `gog-slider`) as well as ones that already do (`GogDropdownBase`).
|
|
210
|
+
* Each consumer owns its own instance and wires `check()` into its own `ngDoCheck`.
|
|
211
|
+
*/
|
|
212
|
+
declare class GogErrorState {
|
|
213
|
+
private readonly errorMessage;
|
|
214
|
+
private readonly errorDisplay;
|
|
215
|
+
private readonly ngControl;
|
|
216
|
+
private readonly controlTouched;
|
|
217
|
+
private readonly controlInvalid;
|
|
218
|
+
constructor(errorMessage: Signal<string>, errorDisplay: Signal<GogErrorDisplay>, ngControl: NgControl | null);
|
|
219
|
+
readonly hasError: Signal<boolean>;
|
|
220
|
+
readonly visibleError: Signal<string>;
|
|
221
|
+
/**
|
|
222
|
+
* `NgControl` exposes validity as plain properties, and has no change notification for
|
|
223
|
+
* `touched` at all — `statusChanges` fires only on validity transitions, never on
|
|
224
|
+
* `markAsTouched()` — while its `control` does not exist yet during construction, so
|
|
225
|
+
* subscribing early would miss it regardless. Mirroring on each check sidesteps both;
|
|
226
|
+
* re-setting a signal to its current value notifies nothing, so the common case costs two
|
|
227
|
+
* comparisons.
|
|
228
|
+
*/
|
|
229
|
+
check(): void;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
type GogDropdownDirection = 'auto' | 'up' | 'down';
|
|
233
|
+
interface GogDropdownTriggerRect {
|
|
234
|
+
top: number;
|
|
235
|
+
bottom: number;
|
|
236
|
+
left: number;
|
|
237
|
+
width: number;
|
|
238
|
+
}
|
|
239
|
+
interface GogDropdownPlacement {
|
|
240
|
+
direction: 'up' | 'down';
|
|
241
|
+
top: number;
|
|
242
|
+
left: number;
|
|
243
|
+
width: number;
|
|
244
|
+
maxHeight: number;
|
|
245
|
+
}
|
|
246
|
+
declare function resolveDropdownDirection(direction: GogDropdownDirection, triggerRect: GogDropdownTriggerRect, panelHeight: number, viewportHeight: number, gap?: number, viewportPadding?: number): 'up' | 'down';
|
|
247
|
+
declare function resolveDropdownPlacement(direction: GogDropdownDirection, triggerRect: GogDropdownTriggerRect, panelHeight: number, viewportHeight: number, gap?: number, viewportPadding?: number): GogDropdownPlacement;
|
|
248
|
+
/**
|
|
249
|
+
* Resolves a CSS length to pixels for the placement math. Only `px`, `%` and `vh` can be
|
|
250
|
+
* resolved without laying out the DOM — `%` and `vh` are taken relative to the viewport
|
|
251
|
+
* height, which is exactly what a `position: fixed` panel's height resolves against in CSS.
|
|
252
|
+
* Any other unit (`rem`, `em`, `auto`, a bare number, …) returns null: the caller falls back
|
|
253
|
+
* to its own estimate for the up/down decision, while the original string is still written
|
|
254
|
+
* to the panel's `style.max-height` as-is — only the direction heuristic loses precision.
|
|
255
|
+
*/
|
|
256
|
+
declare function resolveCssLengthPx(value: string, viewportHeight: number): number | null;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* App-wide defaults for the handful of component inputs where that actually makes sense —
|
|
260
|
+
* timing/behavior knobs a whole app typically wants to set once (a house style for how long
|
|
261
|
+
* scrollbars stay visible, how aggressively buttons debounce clicks), not visual ones.
|
|
262
|
+
* Visual defaults (colors, radii, animation *durations expressed in CSS*) already have a
|
|
263
|
+
* global mechanism: the `--gog-*` custom properties in `styles/theme.css`. This is only for
|
|
264
|
+
* inputs that can't be a CSS token because a component reads them in TypeScript (a
|
|
265
|
+
* `setTimeout` delay, an RxJS `timer` duration) — see `styling.instructions.md` and
|
|
266
|
+
* `gleks-ui-library.instructions.md`'s "Global configuration" section for the line between
|
|
267
|
+
* the two and for how to add a new field here when a component needs one.
|
|
268
|
+
*
|
|
269
|
+
* Every field is optional at every level: set only the ones you want to change from the
|
|
270
|
+
* component's own built-in default. An instance's own input, when set, always wins over
|
|
271
|
+
* this — this only fills in what the instance didn't specify itself.
|
|
272
|
+
*/
|
|
273
|
+
interface GogGlobalConfig {
|
|
274
|
+
/**
|
|
275
|
+
* The loading indicator, app-wide.
|
|
276
|
+
*
|
|
277
|
+
* `component` is the one config key that carries markup rather than a value, and it is here
|
|
278
|
+
* because of what it removes: a house spinner otherwise has to be passed into every control
|
|
279
|
+
* that can show one, and cannot be passed into the ones that render their own. Set it once and
|
|
280
|
+
* **every** spinner the library draws is yours — `gog-spinner`, `gog-spinner-overlay`, and the
|
|
281
|
+
* ones inside `gog-button`, `gog-autocomplete` and `gog-table`'s loading states, which have no
|
|
282
|
+
* input to reach. It is rendered through `NgComponentOutlet` inside the size wrapper, so it
|
|
283
|
+
* keeps the sizing, the overlay behaviour, the `role="status"` and the accessible name; only
|
|
284
|
+
* the visual is yours.
|
|
285
|
+
*
|
|
286
|
+
* Precedence is the library's usual one, with a wrinkle worth stating: an instance's own
|
|
287
|
+
* `variant` wins over both keys here, `component` wins over `variant`, and the built-in
|
|
288
|
+
* `runic` is the fallback. So `<gog-spinner variant="ring">` still gets a ring in an app that
|
|
289
|
+
* has set a component — an instance asking for something specific is not overridden by a
|
|
290
|
+
* default.
|
|
291
|
+
*
|
|
292
|
+
* Applies to: `gog-spinner`, `gog-spinner-overlay`, and the spinners inside `gog-button`,
|
|
293
|
+
* `gog-autocomplete` and `gog-table`. Two of those were wrong until 21.10.0 and are worth the
|
|
294
|
+
* warning: `gog-table` was never listed although it always honoured the key, and
|
|
295
|
+
* `gog-spinner-overlay` was listed although it did not — it forwards its own `variant` down,
|
|
296
|
+
* and that input defaulted to `'runic'`, which the spinner inside correctly read as an
|
|
297
|
+
* instance overruling a default. Neither shows up in a grep for `globalConfig.spinner`,
|
|
298
|
+
* because neither reads the config: they render a `gog-spinner` that does.
|
|
299
|
+
*/
|
|
300
|
+
spinner?: {
|
|
301
|
+
component?: Type<unknown>;
|
|
302
|
+
variant?: GogSpinnerVariant;
|
|
303
|
+
};
|
|
304
|
+
scroll?: {
|
|
305
|
+
autoHide?: boolean;
|
|
306
|
+
hideDelay?: number;
|
|
307
|
+
size?: GogScrollSize;
|
|
308
|
+
overscrollBehavior?: GogScrollOverscrollBehavior;
|
|
309
|
+
/**
|
|
310
|
+
* Whether the overlay thumb/track ever renders. Scrolling itself is unaffected either way
|
|
311
|
+
* — this is purely the visual affordance. Defaults to `true`.
|
|
312
|
+
*/
|
|
313
|
+
showTrack?: boolean;
|
|
314
|
+
/**
|
|
315
|
+
* Turns a vertical wheel into horizontal scrolling on a `gog-scroll` that has nothing to
|
|
316
|
+
* scroll vertically. Defaults to `false`; see the component's `horizontalWheel` input for
|
|
317
|
+
* the conditions it applies under and why it never swallows a gesture at the content's end.
|
|
318
|
+
*/
|
|
319
|
+
horizontalWheel?: boolean;
|
|
320
|
+
};
|
|
321
|
+
button?: {
|
|
322
|
+
debounce?: number;
|
|
323
|
+
};
|
|
324
|
+
/**
|
|
325
|
+
* Whether the library's own interactive surfaces show a press ripple: `gog-button`,
|
|
326
|
+
* `[gogButton]`, `gog-button-toggle`, `gog-chip`, `gog-tabs`, `gog-accordion`,
|
|
327
|
+
* `gog-collapsible`, `gog-menu` and the three dropdowns' options. **Off by default**, so the
|
|
328
|
+
* effect is additive and nothing changes appearance until an app asks for it.
|
|
329
|
+
*
|
|
330
|
+
* This is the one visual default that is *not* expressible as a CSS token, which is why it is
|
|
331
|
+
* here at all: `--gog-ripple-opacity: 0` would hide the wash but still pay for the DOM node,
|
|
332
|
+
* the pointer listeners and the animation frames on every press. A real off has to reach the
|
|
333
|
+
* TypeScript.
|
|
334
|
+
*
|
|
335
|
+
* Per-instance: every component listed above takes a `ripple` input that wins over this, so
|
|
336
|
+
* `[ripple]="false"` opts one control out of an app-wide `true` (and `[ripple]="true"` opts one
|
|
337
|
+
* in without switching the app over). The `[gogRipple]` directive on your **own** element is
|
|
338
|
+
* not covered — writing the attribute is already the per-element decision, the same way
|
|
339
|
+
* `[filter]="true"` on one `gog-select` is; `rippleDisabled` turns that one off.
|
|
340
|
+
*/
|
|
341
|
+
ripple?: {
|
|
342
|
+
enabled?: boolean;
|
|
343
|
+
};
|
|
344
|
+
tooltip?: {
|
|
345
|
+
showDelay?: number;
|
|
346
|
+
hideDelay?: number;
|
|
347
|
+
position?: GogTooltipPosition;
|
|
348
|
+
};
|
|
349
|
+
/**
|
|
350
|
+
* Applies to `gog-inputfield`, `gog-textarea`, `gog-select`, `gog-multiselect`,
|
|
351
|
+
* `gog-autocomplete` and `gog-datepicker` — every field that can float a label. All six read
|
|
352
|
+
* it through the same `GogFloatLabelState`, three of them via `GogDropdownBase`.
|
|
353
|
+
*/
|
|
354
|
+
floatLabel?: {
|
|
355
|
+
variant?: GogFloatLabelVariant;
|
|
356
|
+
/**
|
|
357
|
+
* Whether the field's own `placeholder` reappears once its label has floated out of
|
|
358
|
+
* the way. Defaults to `false` — the placeholder stays hidden the whole time a float
|
|
359
|
+
* label is active, since the resting label already occupies that space.
|
|
360
|
+
*/
|
|
361
|
+
showPlaceholder?: boolean;
|
|
362
|
+
};
|
|
363
|
+
/**
|
|
364
|
+
* Defaults for the interactive form controls — the two settings an app otherwise repeats on
|
|
365
|
+
* literally every one of them: a compact app writes `size="sm"` everywhere, and a
|
|
366
|
+
* Reactive-Forms app writes `errorDisplay="auto"` on every field.
|
|
367
|
+
*
|
|
368
|
+
* - `size` applies to `gog-button` and `[gogButton]`, `gog-button-toggle-group`,
|
|
369
|
+
* `gog-checkbox`, `gog-toggle`, `gog-radio-group`, `gog-inputfield`, `gog-textarea`,
|
|
370
|
+
* `gog-select`, `gog-multiselect`, `gog-autocomplete` and `gog-datepicker`. Deliberately
|
|
371
|
+
* **not** to `gog-table`, `gog-accordion` or `gog-paginator` (whose `size` means row/layout
|
|
372
|
+
* density and whose defaults differ), nor to `gog-spinner`, `gog-skeleton`, `gog-tag` or
|
|
373
|
+
* `gog-chip` (sized to fit whatever they sit next to, not to a form's density).
|
|
374
|
+
* - `errorDisplay` applies to every control that renders a validation message:
|
|
375
|
+
* `gog-inputfield`, `gog-textarea`, `gog-select`, `gog-multiselect`, `gog-autocomplete`,
|
|
376
|
+
* `gog-datepicker`, `gog-radio-group` and `gog-slider`.
|
|
377
|
+
*
|
|
378
|
+
* A component's own built-in default still applies when this is unset, so setting `size`
|
|
379
|
+
* here does not flatten the different defaults those excluded components have.
|
|
380
|
+
*
|
|
381
|
+
* **When you add a reader, add it here — and note that "reader" is not the same as "names
|
|
382
|
+
* the field".** These lists were wrong for four keys until 2026-09-02, always in the same
|
|
383
|
+
* direction and for the same reason: `gog-select`, `gog-multiselect` and `gog-autocomplete`
|
|
384
|
+
* resolve `size`/`errorDisplay`/`clearable`/`floatLabel` inside `GogDropdownBase`, and
|
|
385
|
+
* `gog-datepicker` composes the same state classes directly, so none of the four contains the
|
|
386
|
+
* `globalConfig.control?.…` expression that a grep for readers finds. Nor does the resolved
|
|
387
|
+
* value always reach a template by name — `size` arrives as a computed class (`sizeClass`,
|
|
388
|
+
* `panelSizeClass`) and `dropdown.direction` is consumed by placement code, so "is the
|
|
389
|
+
* computed referenced in the component's HTML?" answers *no* for components that genuinely
|
|
390
|
+
* honour the key. Trace the shared state classes, not just the components.
|
|
391
|
+
*/
|
|
392
|
+
control?: {
|
|
393
|
+
size?: GogSize;
|
|
394
|
+
errorDisplay?: GogErrorDisplay;
|
|
395
|
+
/**
|
|
396
|
+
* Whether `gog-inputfield`, `gog-textarea`, `gog-select`, `gog-multiselect`,
|
|
397
|
+
* `gog-autocomplete` and `gog-datepicker` offer a clear button — every control that
|
|
398
|
+
* composes `GogClearableState`. The button only ever appears once the control actually has
|
|
399
|
+
* something to clear, so switching this on app-wide adds no permanent chrome.
|
|
400
|
+
*
|
|
401
|
+
* Component defaults are `false`, except `gog-multiselect`, which had a clear button
|
|
402
|
+
* before this input existed and keeps it.
|
|
403
|
+
*/
|
|
404
|
+
clearable?: boolean;
|
|
405
|
+
};
|
|
406
|
+
/**
|
|
407
|
+
* Applies per field rather than per component, which is why this is not one sentence:
|
|
408
|
+
*
|
|
409
|
+
* - `appendToBody` and `direction` reach `gog-select`, `gog-multiselect`,
|
|
410
|
+
* `gog-autocomplete` (all three through `GogDropdownBase`) and `gog-datepicker`, which
|
|
411
|
+
* resolves the same pair itself.
|
|
412
|
+
* - `filter` and `filterPosition` reach `gog-select` and `gog-multiselect` only.
|
|
413
|
+
* `gog-autocomplete` inherits both inputs from the same base class and renders no filter
|
|
414
|
+
* box for them — its own text field *is* the filter, so a second search box inside the
|
|
415
|
+
* panel would be a duplicate. Setting these does nothing to an autocomplete.
|
|
416
|
+
*/
|
|
417
|
+
dropdown?: {
|
|
418
|
+
/**
|
|
419
|
+
* Whether the panel is rendered into `<body>` instead of inline. Worth setting app-wide
|
|
420
|
+
* for a layout whose dropdowns generally live inside scrollable or overflow-clipped
|
|
421
|
+
* containers, which is the case this exists for.
|
|
422
|
+
*/
|
|
423
|
+
appendToBody?: boolean;
|
|
424
|
+
direction?: GogDropdownDirection;
|
|
425
|
+
/** Whether the panel shows a search box. Off by default. */
|
|
426
|
+
filter?: boolean;
|
|
427
|
+
/** Which end of the panel the search box sticks to. `'top'` by default. */
|
|
428
|
+
filterPosition?: GogDropdownFilterPosition;
|
|
429
|
+
/**
|
|
430
|
+
* Whether the option list renders only the rows in view instead of all of them. Reaches
|
|
431
|
+
* `gog-select`. **Off by default**, and deliberately not switched on above some row count:
|
|
432
|
+
* a windowed list and a plain one differ under `Ctrl+F`, under a screen reader's "list all
|
|
433
|
+
* items", and under consumer CSS that targets `:last-child`, so a data-dependent threshold
|
|
434
|
+
* would make behaviour depend on how much data happened to arrive.
|
|
435
|
+
*
|
|
436
|
+
* Worth setting app-wide for an app whose selects are generally fed long lists; a per-field
|
|
437
|
+
* `virtualize` still wins over it.
|
|
438
|
+
*/
|
|
439
|
+
virtualize?: boolean;
|
|
440
|
+
};
|
|
441
|
+
/**
|
|
442
|
+
* Applies to `gog-datepicker` and `gog-calendar`. Every field here is something an app sets
|
|
443
|
+
* once rather than per field — a locale and a date format repeated on every date input is
|
|
444
|
+
* exactly the boilerplate this config exists to remove.
|
|
445
|
+
*/
|
|
446
|
+
datepicker?: {
|
|
447
|
+
/** BCP-47 tag driving month and weekday names. */
|
|
448
|
+
locale?: string;
|
|
449
|
+
/** 0 = Sunday … 6 = Saturday. Unset, it comes from the locale. */
|
|
450
|
+
firstDayOfWeek?: number;
|
|
451
|
+
/** Display and parse pattern (`dd.MM.yyyy`, `yyyy-MM-dd`, …). */
|
|
452
|
+
format?: string;
|
|
453
|
+
};
|
|
454
|
+
/**
|
|
455
|
+
* Applies to `gog-paginator`, and through it to `gog-table`'s built-in pagination.
|
|
456
|
+
*
|
|
457
|
+
* The page-size select is the one piece of paginator chrome an app decides once — either its
|
|
458
|
+
* tables offer the choice or they don't, and the option list is a house style. Both are still
|
|
459
|
+
* overridable per instance, for the page whose table genuinely needs `5, 10, 20` while the rest
|
|
460
|
+
* of the app uses `20, 50, 100`.
|
|
461
|
+
*/
|
|
462
|
+
paginator?: {
|
|
463
|
+
/** Whether the rows-per-page select renders at all. **Off by default.** */
|
|
464
|
+
showPageSizeSelect?: boolean;
|
|
465
|
+
/** The choices it offers. Defaults to `[10, 20, 30, 40, 50]`. */
|
|
466
|
+
pageSizeOptions?: number[];
|
|
467
|
+
};
|
|
468
|
+
/** Applies to `gog-autocomplete`. */
|
|
469
|
+
autocomplete?: {
|
|
470
|
+
/**
|
|
471
|
+
* Milliseconds to wait after the last keystroke before `gogSearch` fires. A whole app
|
|
472
|
+
* usually wants one answer here, tuned to its backend rather than to each field.
|
|
473
|
+
*/
|
|
474
|
+
searchDebounce?: number;
|
|
475
|
+
/** How many characters before the panel opens at all. */
|
|
476
|
+
minLength?: number;
|
|
477
|
+
/**
|
|
478
|
+
* Whether focusing the field opens the panel immediately, showing the full option list.
|
|
479
|
+
* Defaults to `true`.
|
|
480
|
+
*/
|
|
481
|
+
openOnFocus?: boolean;
|
|
482
|
+
};
|
|
483
|
+
/** Applies to `gog-inputfield`. */
|
|
484
|
+
inputfield?: {
|
|
485
|
+
/**
|
|
486
|
+
* Whether a `type="number"` field shows the library's own spin buttons in place of the
|
|
487
|
+
* browser's native ones. Defaults to `true`.
|
|
488
|
+
*/
|
|
489
|
+
showSpinButtons?: boolean;
|
|
490
|
+
};
|
|
491
|
+
/** Applies to `gog-textarea`. */
|
|
492
|
+
textarea?: {
|
|
493
|
+
/**
|
|
494
|
+
* Which direction(s) the field's own drag handle resizes it in — matches the native CSS
|
|
495
|
+
* `resize` value space. Defaults to `'vertical'`.
|
|
496
|
+
*/
|
|
497
|
+
resize?: GogTextareaResize;
|
|
498
|
+
};
|
|
499
|
+
toast?: {
|
|
500
|
+
position?: ToastPosition;
|
|
501
|
+
/** How long a non-sticky toast stays up, in ms. */
|
|
502
|
+
duration?: number;
|
|
503
|
+
};
|
|
504
|
+
/**
|
|
505
|
+
* Every fixed, user-visible string the library renders — button text and accessible names for
|
|
506
|
+
* chrome the consumer never writes markup for.
|
|
507
|
+
*
|
|
508
|
+
* These are here rather than as one input per string because they are the definition of a
|
|
509
|
+
* setting an app states once: a Russian-language app relabels "Clear" once, not on all 340
|
|
510
|
+
* fields. Where a per-instance input already exists (`clearAriaLabel`, `todayLabel`, …) it
|
|
511
|
+
* still wins for that one control — the usual instance → config → default order.
|
|
512
|
+
*
|
|
513
|
+
* Deliberately excludes anything that is *content* rather than chrome: `gog-checkbox`'s
|
|
514
|
+
* `ariaLabel`, `gog-button`'s `ariaLabel`, a field's `label` or `placeholder`. Those differ
|
|
515
|
+
* per instance by definition and have no meaningful app-wide value.
|
|
516
|
+
*/
|
|
517
|
+
labels?: {
|
|
518
|
+
/** Clear button on `gog-inputfield` / `gog-textarea`. */
|
|
519
|
+
clear?: string;
|
|
520
|
+
/**
|
|
521
|
+
* Clear button on `gog-select` / `gog-multiselect` / `gog-autocomplete`, which clears a
|
|
522
|
+
* selection rather than text.
|
|
523
|
+
*/
|
|
524
|
+
clearSelection?: string;
|
|
525
|
+
/** Clear button on `gog-datepicker`. */
|
|
526
|
+
clearDate?: string;
|
|
527
|
+
/** `gog-multiselect`'s select-all / clear-all buttons — visible text, not just a label. */
|
|
528
|
+
selectAll?: string;
|
|
529
|
+
clearAll?: string;
|
|
530
|
+
/** `gog-inputfield`'s number spin buttons. */
|
|
531
|
+
increment?: string;
|
|
532
|
+
decrement?: string;
|
|
533
|
+
/** `gog-inputfield`'s password reveal toggle, in its two states. */
|
|
534
|
+
showPassword?: string;
|
|
535
|
+
hidePassword?: string;
|
|
536
|
+
/** Close buttons on the two service-driven overlays. */
|
|
537
|
+
closeDialog?: string;
|
|
538
|
+
closeToast?: string;
|
|
539
|
+
/**
|
|
540
|
+
* `gog-alert`'s dismiss button. Separate from `closeToast` on purpose: a toast closes
|
|
541
|
+
* something that was going to leave anyway, an alert dismisses a message that would have
|
|
542
|
+
* stayed, and several languages use different verbs for the two.
|
|
543
|
+
*/
|
|
544
|
+
closeAlert?: string;
|
|
545
|
+
/** `gog-paginator`'s `<nav>` accessible name and its two step buttons. */
|
|
546
|
+
pagination?: string;
|
|
547
|
+
previousPage?: string;
|
|
548
|
+
nextPage?: string;
|
|
549
|
+
/**
|
|
550
|
+
* `gog-paginator`'s per-page button names ("Go to page 4", "Page 4, current page").
|
|
551
|
+
*
|
|
552
|
+
* A function rather than a string, and the only one in this block: these interpolate the
|
|
553
|
+
* page number, and a template string with a `{0}` placeholder would be a second, weaker
|
|
554
|
+
* formatting language to learn — one that also can't express languages where the number's
|
|
555
|
+
* position or the surrounding grammar depends on its value. The default is
|
|
556
|
+
* `` (page, isCurrent) => isCurrent ? `Page ${page}, current page` : `Go to page ${page}` ``.
|
|
557
|
+
*/
|
|
558
|
+
page?: (page: number, isCurrent: boolean) => string;
|
|
559
|
+
/** `gog-datepicker`'s button that opens the calendar panel. */
|
|
560
|
+
openCalendar?: string;
|
|
561
|
+
/**
|
|
562
|
+
* `gog-panel`'s collapse toggle, used **only** when the panel has no `gogPanelHeader` to
|
|
563
|
+
* borrow a name from. A panel with a heading names its toggle after that heading, which is
|
|
564
|
+
* both more useful and per-instance by definition.
|
|
565
|
+
*/
|
|
566
|
+
togglePanel?: string;
|
|
567
|
+
/** `gog-paginator`'s rows-per-page select. */
|
|
568
|
+
rowsPerPage?: string;
|
|
569
|
+
/** `gog-table`: the row-count label, its paginator, and the selection checkboxes. */
|
|
570
|
+
total?: string;
|
|
571
|
+
tablePagination?: string;
|
|
572
|
+
selectRow?: string;
|
|
573
|
+
selectAllRows?: string;
|
|
574
|
+
/** `gog-calendar` navigation and shortcuts. */
|
|
575
|
+
today?: string;
|
|
576
|
+
thisMonth?: string;
|
|
577
|
+
previousMonth?: string;
|
|
578
|
+
nextMonth?: string;
|
|
579
|
+
previousYear?: string;
|
|
580
|
+
nextYear?: string;
|
|
581
|
+
/** `gog-calendar`'s time section. */
|
|
582
|
+
hours?: string;
|
|
583
|
+
minutes?: string;
|
|
584
|
+
seconds?: string;
|
|
585
|
+
};
|
|
586
|
+
/**
|
|
587
|
+
* Applies to `ThemeService`. Every field is off/neutral by default, so an app that configures
|
|
588
|
+
* nothing keeps the pre-21.3.2 behaviour: adopt whatever `data-theme` is already on `<html>`,
|
|
589
|
+
* else `'light'`.
|
|
590
|
+
*/
|
|
591
|
+
theme?: {
|
|
592
|
+
/**
|
|
593
|
+
* `localStorage` key the chosen theme is written to and read back from. Unset, nothing is
|
|
594
|
+
* persisted and the theme resets on every load.
|
|
595
|
+
*/
|
|
596
|
+
storageKey?: string | null;
|
|
597
|
+
/** Theme applied when nothing else decides. `'light'` by default. */
|
|
598
|
+
defaultTheme?: string;
|
|
599
|
+
/**
|
|
600
|
+
* Whether to fall back to the OS `prefers-color-scheme` setting — and to keep following it
|
|
601
|
+
* until the app calls `setTheme`/`toggleTheme`. Off by default: switching this on changes
|
|
602
|
+
* which theme an existing app opens in.
|
|
603
|
+
*/
|
|
604
|
+
followSystem?: boolean;
|
|
605
|
+
/** Theme names `toggleTheme()` alternates between, and `followSystem` maps the OS setting to. */
|
|
606
|
+
lightTheme?: string;
|
|
607
|
+
darkTheme?: string;
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Resolves to `{}` — every field falls through to its component's own hardcoded default —
|
|
612
|
+
* until a `provideGogConfig(...)` call overrides it somewhere in the injector tree.
|
|
613
|
+
*/
|
|
614
|
+
declare const GOG_CONFIG: InjectionToken<GogGlobalConfig>;
|
|
615
|
+
/**
|
|
616
|
+
* Sets app-wide (or subtree-wide, if placed in a route's or component's own `providers`
|
|
617
|
+
* instead of the bootstrap config) defaults for every `@guildofgleks/ui` component that
|
|
618
|
+
* reads `GOG_CONFIG` — one call instead of a separate injection token per setting:
|
|
619
|
+
*
|
|
620
|
+
* ```ts
|
|
621
|
+
* // app.config.ts
|
|
622
|
+
* providers: [
|
|
623
|
+
* provideGogConfig({
|
|
624
|
+
* scroll: { hideDelay: 1000 },
|
|
625
|
+
* button: { debounce: 500 },
|
|
626
|
+
* }),
|
|
627
|
+
* ]
|
|
628
|
+
* ```
|
|
629
|
+
*
|
|
630
|
+
* **Providing this again further down the injector tree layers onto the parent's config
|
|
631
|
+
* rather than replacing it.** A route that only cares about tooltips can say so, and the
|
|
632
|
+
* app-wide `button.debounce` stays in effect inside it:
|
|
633
|
+
*
|
|
634
|
+
* ```ts
|
|
635
|
+
* // a route's providers — button.debounce from app.config.ts still applies here
|
|
636
|
+
* provideGogConfig({ tooltip: { showDelay: 0 } })
|
|
637
|
+
* ```
|
|
638
|
+
*
|
|
639
|
+
* Merging is one level deep, per component key: the nearest provider wins field by field, so
|
|
640
|
+
* `{ tooltip: { showDelay: 0 } }` overrides only `showDelay` and leaves a parent's
|
|
641
|
+
* `tooltip.position` alone. To drop an inherited value rather than change it, set it back to
|
|
642
|
+
* the component's own default explicitly — there is no "unset" marker.
|
|
643
|
+
*/
|
|
644
|
+
declare function provideGogConfig(config: GogGlobalConfig): Provider;
|
|
645
|
+
/**
|
|
646
|
+
* The library's precedence rule for a configurable input, in one place: an instance's own
|
|
647
|
+
* input wins, then the app-wide `GOG_CONFIG` value, then the component's built-in default.
|
|
648
|
+
*
|
|
649
|
+
* Every configurable input resolves through this rather than repeating the `??` chain, so the
|
|
650
|
+
* order can't drift between components — a component that accidentally checked the config
|
|
651
|
+
* first would silently ignore the input on that one control only, which is close to invisible
|
|
652
|
+
* in review. Kept a plain function (no `inject`) so it works anywhere: inside a `computed`, in
|
|
653
|
+
* a composition class like `GogFloatLabelState`, and in unit tests without an injector.
|
|
654
|
+
*
|
|
655
|
+
* ```ts
|
|
656
|
+
* private readonly globalConfig = inject(GOG_CONFIG);
|
|
657
|
+
* protected readonly resolvedDebounce = computed(() =>
|
|
658
|
+
* resolveConfigured(this.debounce(), this.globalConfig.button?.debounce, DEFAULT_DEBOUNCE),
|
|
659
|
+
* );
|
|
660
|
+
* ```
|
|
661
|
+
*/
|
|
662
|
+
declare function resolveConfigured<T>(instanceValue: T | undefined, configuredValue: T | undefined, fallback: T): T;
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Shared "show a clear button" state for the field-style controls.
|
|
666
|
+
*
|
|
667
|
+
* A plain composition class, same reasoning as `GogErrorState` and `GogFloatLabelState`: it has
|
|
668
|
+
* to serve `gog-inputfield` and `gog-textarea` (no common base class) as well as
|
|
669
|
+
* `GogDropdownBase`, which is one.
|
|
670
|
+
*
|
|
671
|
+
* The two things each control supplies itself are `hasValue` — "there is something to clear"
|
|
672
|
+
* differs per control (non-empty string / non-null selection / non-empty array) — and the
|
|
673
|
+
* actual clearing, since only the control knows its own empty value and how to notify forms.
|
|
674
|
+
*/
|
|
675
|
+
declare class GogClearableState {
|
|
676
|
+
private readonly clearableInput;
|
|
677
|
+
private readonly hasValue;
|
|
678
|
+
private readonly isNotEditable;
|
|
679
|
+
private readonly config;
|
|
680
|
+
private readonly fallback;
|
|
681
|
+
/**
|
|
682
|
+
* @param clearableInput the control's own `clearable` input (`undefined` when unset)
|
|
683
|
+
* @param hasValue whether there is anything to clear
|
|
684
|
+
* @param isNotEditable whether the control currently refuses edits — disabled, or (for the
|
|
685
|
+
* text controls) read-only. Either way it offers no clear button: the affordance would
|
|
686
|
+
* promise a change the control won't accept.
|
|
687
|
+
* @param config the injected `GOG_CONFIG`
|
|
688
|
+
* @param fallback the control's own default, read lazily — `GogDropdownBase` constructs this
|
|
689
|
+
* in a field initializer, before its subclass has assigned `clearableByDefault`. `false`
|
|
690
|
+
* everywhere except `gog-multiselect`, which shipped a clear button before this input
|
|
691
|
+
* existed and keeps it.
|
|
692
|
+
*/
|
|
693
|
+
constructor(clearableInput: Signal<boolean | undefined>, hasValue: Signal<boolean>, isNotEditable: Signal<boolean>, config: GogGlobalConfig, fallback: () => boolean);
|
|
694
|
+
/** Whether the control is clearable at all — instance input, then config, then the default. */
|
|
695
|
+
readonly enabled: Signal<boolean>;
|
|
696
|
+
/**
|
|
697
|
+
* Whether the clear button should render *right now*. Deliberately value-driven: the control
|
|
698
|
+
* shows nothing to clear until there is something to clear, so the affordance appears with the
|
|
699
|
+
* content rather than sitting there permanently as dead chrome.
|
|
700
|
+
*/
|
|
701
|
+
readonly isVisible: Signal<boolean>;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/** Returns the next id for `prefix`, e.g. `nextGogControlId('gog-input')` → `'gog-input-3'`. */
|
|
705
|
+
declare function nextGogControlId(prefix: string): string;
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Every date calculation `gog-datepicker` and `gog-calendar` do, as pure functions.
|
|
709
|
+
*
|
|
710
|
+
* Kept out of the components so the arithmetic is testable without a fixture, and so a range
|
|
711
|
+
* calculation is written once rather than once per selection mode.
|
|
712
|
+
*
|
|
713
|
+
* **Everything here works in local time.** Nothing round-trips through `toISOString()` or
|
|
714
|
+
* compares `Date` objects directly: an ISO round trip converts to UTC, which moves the date
|
|
715
|
+
* across midnight for anyone east or west of Greenwich, and two `Date`s for "the same day"
|
|
716
|
+
* are almost never equal because they carry different times. Comparisons go through
|
|
717
|
+
* `startOfDay` and `isSameDay` instead. This is the single most common defect in a datepicker.
|
|
718
|
+
*/
|
|
719
|
+
/** A start/end pair. `end` is null while a range is half-picked. */
|
|
720
|
+
interface GogDateRange {
|
|
721
|
+
start: Date | null;
|
|
722
|
+
end: Date | null;
|
|
723
|
+
}
|
|
724
|
+
/** Midnight local time on the same calendar day. */
|
|
725
|
+
declare function startOfDay(date: Date): Date;
|
|
726
|
+
/** Whether two dates fall on the same calendar day, ignoring the time of day. */
|
|
727
|
+
declare function isSameDay(a: Date | null, b: Date | null): boolean;
|
|
728
|
+
/** Whether two dates fall in the same calendar month. */
|
|
729
|
+
declare function isSameMonth(a: Date | null, b: Date | null): boolean;
|
|
730
|
+
declare function addDays(date: Date, days: number): Date;
|
|
731
|
+
/** Days in a given month. `month` is 0-based, as `Date` uses it. */
|
|
732
|
+
declare function daysInMonth(year: number, month: number): number;
|
|
733
|
+
/**
|
|
734
|
+
* Adds whole months, **clamping the day** to the target month's length: 31 January plus one
|
|
735
|
+
* month is 28 February, not 3 March. `Date.setMonth` does the latter, which is why this is
|
|
736
|
+
* not a one-liner.
|
|
737
|
+
*/
|
|
738
|
+
declare function addMonths(date: Date, months: number): Date;
|
|
739
|
+
declare function addYears(date: Date, years: number): Date;
|
|
740
|
+
/** Day-granularity comparison: `a` is strictly before `b`'s day. */
|
|
741
|
+
declare function isBeforeDay(a: Date, b: Date): boolean;
|
|
742
|
+
/** Day-granularity comparison: `a` is strictly after `b`'s day. */
|
|
743
|
+
declare function isAfterDay(a: Date, b: Date): boolean;
|
|
744
|
+
/** Whether `date`'s day falls within `[min, max]`, either bound optional. */
|
|
745
|
+
declare function isWithinBounds(date: Date, min: Date | null, max: Date | null): boolean;
|
|
746
|
+
/** Whether `date`'s day falls inside a range, inclusive of both ends. */
|
|
747
|
+
declare function isInRange(date: Date, start: Date | null, end: Date | null): boolean;
|
|
748
|
+
/** Pulls `date` inside `[min, max]`, keeping its time of day. */
|
|
749
|
+
declare function clampDate(date: Date, min: Date | null, max: Date | null): Date;
|
|
750
|
+
/** A new date on `day`'s calendar day carrying `time`'s clock. */
|
|
751
|
+
declare function copyTimeOnto(day: Date, time: Date): Date;
|
|
752
|
+
/** A new date on the same day with the clock replaced. */
|
|
753
|
+
declare function withTime(date: Date, hours: number, minutes: number, seconds?: number): Date;
|
|
754
|
+
/**
|
|
755
|
+
* The six-week grid a month is drawn on, always 6×7 so the calendar's height never changes as
|
|
756
|
+
* you page through months — a grid that grows and shrinks pushes the rest of the page around
|
|
757
|
+
* and moves the button you were about to click.
|
|
758
|
+
*
|
|
759
|
+
* `firstDayOfWeek` is 0 (Sunday) to 6 (Saturday). Leading and trailing cells come from the
|
|
760
|
+
* neighbouring months, as every calendar does.
|
|
761
|
+
*/
|
|
762
|
+
declare function buildMonthGrid(year: number, month: number, firstDayOfWeek: number): Date[][];
|
|
763
|
+
/** Weekday names in `locale`, rotated so `firstDayOfWeek` comes first. */
|
|
764
|
+
declare function weekdayNames(locale: string, firstDayOfWeek: number, style?: 'short' | 'narrow' | 'long'): string[];
|
|
765
|
+
/** Month names in `locale`. */
|
|
766
|
+
declare function monthNames(locale: string, style?: 'long' | 'short'): string[];
|
|
767
|
+
/**
|
|
768
|
+
* The first day of the week for a locale, from `Intl.Locale.prototype.getWeekInfo` where the
|
|
769
|
+
* engine has it, falling back to Monday.
|
|
770
|
+
*
|
|
771
|
+
* `getWeekInfo` reports 1–7 with 1 = Monday and 7 = Sunday; `Date.getDay()` uses 0 = Sunday.
|
|
772
|
+
* Converting between the two is exactly the kind of off-by-one that silently shifts a whole
|
|
773
|
+
* calendar by a day, which is why it happens here once.
|
|
774
|
+
*/
|
|
775
|
+
declare function localeFirstDayOfWeek(locale: string): number;
|
|
776
|
+
/**
|
|
777
|
+
* Formats a date against a token pattern.
|
|
778
|
+
*
|
|
779
|
+
* Supported tokens: `yyyy`, `MM`, `dd`, `HH` (24h), `hh` (12h), `mm`, `ss`, `a` (AM/PM).
|
|
780
|
+
* Anything else is copied through literally.
|
|
781
|
+
*
|
|
782
|
+
* Deliberately **not** `Intl.DateTimeFormat`: this pattern is also what `parseDate` reads, and
|
|
783
|
+
* a formatter whose output cannot be parsed back is how a typed date silently becomes a
|
|
784
|
+
* different one. `Intl` is still used for month and weekday *names*, which are never parsed.
|
|
785
|
+
*/
|
|
786
|
+
declare function formatDate(date: Date, pattern: string): string;
|
|
787
|
+
/**
|
|
788
|
+
* Reads a date back out of text written in `pattern`. Returns `null` when the text doesn't
|
|
789
|
+
* match, or matches but isn't a real date (`31.02.2026`).
|
|
790
|
+
*
|
|
791
|
+
* The out-of-range check matters: `new Date(2026, 1, 31)` happily rolls over to 3 March, so a
|
|
792
|
+
* typo would be accepted as a different date rather than rejected.
|
|
793
|
+
*/
|
|
794
|
+
declare function parseDate(text: string, pattern: string): Date | null;
|
|
795
|
+
|
|
796
|
+
/** What kind of thing a deprecation is about: an exported symbol, or a CSS custom property. */
|
|
797
|
+
type GogDeprecationKind = 'symbol' | 'token';
|
|
798
|
+
/** One deprecated part of the public API, with the version that removes it. */
|
|
799
|
+
interface GogDeprecation {
|
|
800
|
+
kind: GogDeprecationKind;
|
|
801
|
+
/** The deprecated name — an export, an input, or a `--gog-*` custom property. */
|
|
802
|
+
name: string;
|
|
803
|
+
/** What to use instead. */
|
|
804
|
+
replacement: string;
|
|
805
|
+
/** The version that deprecated it, and the day it was announced. */
|
|
806
|
+
since: string;
|
|
807
|
+
sinceDate: string;
|
|
808
|
+
/** The version that removes it. Until then, the deprecated name still works. */
|
|
809
|
+
removedIn: string;
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Everything `@guildofgleks/ui` currently deprecates: 28 symbol(s) and 3 token(s).
|
|
813
|
+
*
|
|
814
|
+
* Generated from the library's own source — `@deprecated` tags for symbols, and the stylesheets
|
|
815
|
+
* themselves for tokens — so it cannot drift from what actually still resolves. Meant for tooling
|
|
816
|
+
* that has to answer "is this still supported, and until when?": a docs site marking an API row,
|
|
817
|
+
* an editor plugin, a codemod.
|
|
818
|
+
*
|
|
819
|
+
* An empty `symbol` half means exactly what it says: nothing in the TypeScript API is deprecated
|
|
820
|
+
* right now.
|
|
821
|
+
*/
|
|
822
|
+
declare const GOG_DEPRECATIONS: readonly GogDeprecation[];
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* How a collection-driven component reads one field off a consumer's own object.
|
|
826
|
+
*
|
|
827
|
+
* A `string` is a property name, or a dot-path into a nested one (`'profile.city'`). A function
|
|
828
|
+
* is anything else. This is what lets `gog-select` / `gog-multiselect` take a consumer's real
|
|
829
|
+
* DTO instead of forcing everything through a `{ id, name }` shape first.
|
|
830
|
+
*/
|
|
831
|
+
type GogOptionAccessor<TOption, TResult> = string | ((option: TOption) => TResult);
|
|
832
|
+
/** Resolves `field` against `source`, following dot-paths (e.g. `"address.city"`). */
|
|
833
|
+
declare function getByPath(source: unknown, field: string): unknown;
|
|
834
|
+
/** Applies an accessor — a property path or a function — to one option. */
|
|
835
|
+
declare function readOption<TOption, TResult>(option: TOption, accessor: GogOptionAccessor<TOption, TResult>): TResult;
|
|
836
|
+
/**
|
|
837
|
+
* Whether two resolved option values refer to the same option.
|
|
838
|
+
*
|
|
839
|
+
* Primitives are compared as strings, so a `formControl` holding `'1'` still matches an option
|
|
840
|
+
* whose value is the number `1` — the library behaved this way before option values could be
|
|
841
|
+
* anything, and forms routinely stringify. Objects are compared by identity instead: coercing
|
|
842
|
+
* them would make every plain object equal to every other (`"[object Object]"`).
|
|
843
|
+
*/
|
|
844
|
+
declare function isSameOptionValue(a: unknown, b: unknown): boolean;
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* Custom markup for the trigger's chevron, on `gog-select` and `gog-multiselect`:
|
|
848
|
+
*
|
|
849
|
+
* ```html
|
|
850
|
+
* <gog-select [options]="opts">
|
|
851
|
+
* <ng-template gogDropdownChevron><gog-icon name="sort" /></ng-template>
|
|
852
|
+
* </gog-select>
|
|
853
|
+
* ```
|
|
854
|
+
*/
|
|
855
|
+
declare class GogDropdownChevronDirective {
|
|
856
|
+
readonly templateRef: TemplateRef<unknown>;
|
|
857
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<GogDropdownChevronDirective, never>;
|
|
858
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<GogDropdownChevronDirective, "[gogDropdownChevron]", never, {}, {}, never, never, true, never>;
|
|
859
|
+
}
|
|
860
|
+
/** Context handed to a `gogDropdownOption` template. */
|
|
861
|
+
interface GogDropdownOptionContext<TOption> {
|
|
862
|
+
/** The consumer's own option object, untouched. */
|
|
863
|
+
$implicit: TOption;
|
|
864
|
+
selected: boolean;
|
|
865
|
+
disabled: boolean;
|
|
866
|
+
/** The resolved label, so a custom row can decorate it rather than re-derive it. */
|
|
867
|
+
label: string;
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Custom markup for one option row, on `gog-select` and `gog-multiselect`:
|
|
871
|
+
*
|
|
872
|
+
* ```html
|
|
873
|
+
* <gog-select [options]="users" optionLabel="fullName" optionValue="id" [(value)]="userId">
|
|
874
|
+
* <ng-template gogDropdownOption let-user let-selected="selected">
|
|
875
|
+
* <img [src]="user.avatar" alt="" /> {{ user.fullName }}
|
|
876
|
+
* </ng-template>
|
|
877
|
+
* </gog-select>
|
|
878
|
+
* ```
|
|
879
|
+
*/
|
|
880
|
+
declare class GogDropdownOptionDirective<TOption = unknown> {
|
|
881
|
+
readonly templateRef: TemplateRef<GogDropdownOptionContext<TOption>>;
|
|
882
|
+
static ngTemplateContextGuard<TOption>(_dir: GogDropdownOptionDirective<TOption>, ctx: unknown): ctx is GogDropdownOptionContext<TOption>;
|
|
883
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<GogDropdownOptionDirective<any>, never>;
|
|
884
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<GogDropdownOptionDirective<any>, "[gogDropdownOption]", never, {}, {}, never, never, true, never>;
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* The shape the listbox controls assume when their accessor inputs are left at their defaults.
|
|
888
|
+
*
|
|
889
|
+
* It is no longer required: `optionLabel` / `optionValue` / `optionDisabled` accept any property
|
|
890
|
+
* path or function, so a consumer can pass their own DTO straight through. This interface just
|
|
891
|
+
* describes what the *default* accessors (`'name'` / `'id'` / `'disabled'`) expect to find.
|
|
892
|
+
*/
|
|
893
|
+
interface GogDropdownOption {
|
|
894
|
+
id: string | number;
|
|
895
|
+
name: string;
|
|
896
|
+
disabled?: boolean;
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* Shared behaviour for the listbox-style controls: open/close, placement, the
|
|
900
|
+
* append-to-body overlay, click-outside, keyboard navigation and `ControlValueAccessor`
|
|
901
|
+
* plumbing. Subclasses supply only what actually differs — the value type, the panel
|
|
902
|
+
* markup, and the BEM class names used to find the trigger and the options.
|
|
903
|
+
*
|
|
904
|
+
* Exported because it appears in the public type signatures of the components that
|
|
905
|
+
* extend it; it is not meant to be used or subclassed by consumers.
|
|
906
|
+
*/
|
|
907
|
+
declare abstract class GogDropdownBase<TValue, TOption = GogDropdownOption> implements ControlValueAccessor, DoCheck {
|
|
908
|
+
private static nextUid;
|
|
909
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
910
|
+
readonly ariaLabel: _angular_core.InputSignal<string>;
|
|
911
|
+
readonly placeholder: _angular_core.InputSignal<string>;
|
|
912
|
+
readonly options: _angular_core.InputSignal<TOption[]>;
|
|
913
|
+
/**
|
|
914
|
+
* How to read an option's visible text — a property path (`'name'`, `'profile.title'`) or a
|
|
915
|
+
* function. Defaults to `'name'`, matching `GogDropdownOption`.
|
|
916
|
+
*/
|
|
917
|
+
readonly optionLabel: _angular_core.InputSignal<GogOptionAccessor<TOption, string>>;
|
|
918
|
+
/**
|
|
919
|
+
* How to read the value this control emits for an option. A property path or a function, or
|
|
920
|
+
* `null` to emit **the option object itself** — which is what lets a consumer keep their own
|
|
921
|
+
* DTO end to end instead of mapping ids back to objects on every change.
|
|
922
|
+
*
|
|
923
|
+
* Defaults to `'id'`, matching `GogDropdownOption`, so existing code is unaffected.
|
|
924
|
+
*/
|
|
925
|
+
readonly optionValue: _angular_core.InputSignal<GogOptionAccessor<TOption, unknown> | null>;
|
|
926
|
+
/** How to read whether an option is disabled. Defaults to `'disabled'`. */
|
|
927
|
+
readonly optionDisabled: _angular_core.InputSignal<GogOptionAccessor<TOption, boolean>>;
|
|
928
|
+
/**
|
|
929
|
+
* Whether to offer a clear button once something is selected. Unset, falls back to
|
|
930
|
+
* `GOG_CONFIG.control.clearable`, then to the control's own default.
|
|
931
|
+
*/
|
|
932
|
+
readonly clearable: _angular_core.InputSignal<boolean | undefined>;
|
|
933
|
+
/**
|
|
934
|
+
* Accessible name for the clear button. Unset, falls back to
|
|
935
|
+
* `GOG_CONFIG.labels.clearSelection`, then to `'Clear selection'`.
|
|
936
|
+
*/
|
|
937
|
+
readonly clearAriaLabel: _angular_core.InputSignal<string | undefined>;
|
|
938
|
+
/** Instance input → `GOG_CONFIG.labels` → the built-in English default. */
|
|
939
|
+
protected readonly resolvedClearLabel: Signal<string>;
|
|
940
|
+
/**
|
|
941
|
+
* Smallest width the trigger may shrink to, as any CSS length. Only meaningful with
|
|
942
|
+
* `[fullWidth]="false"`, where the trigger otherwise sizes to whatever is currently selected
|
|
943
|
+
* and can collapse to almost nothing on a short option. Left unset it falls back to the
|
|
944
|
+
* `--gog-{select,multiselect}-min-width` token.
|
|
945
|
+
*/
|
|
946
|
+
readonly minWidth: _angular_core.InputSignal<string | null>;
|
|
947
|
+
/**
|
|
948
|
+
* Whether the panel shows a search box that narrows the option list. Unset, falls back to
|
|
949
|
+
* `GOG_CONFIG.dropdown.filter`, then to `false`.
|
|
950
|
+
*/
|
|
951
|
+
readonly filter: _angular_core.InputSignal<boolean | undefined>;
|
|
952
|
+
readonly filterPlaceholder: _angular_core.InputSignal<string>;
|
|
953
|
+
/**
|
|
954
|
+
* Which end of the panel the search box sticks to. Named to match `gog-multiselect`'s
|
|
955
|
+
* `controlsPosition`, which is the same idea for its select-all row. Unset, falls back to
|
|
956
|
+
* `GOG_CONFIG.dropdown.filterPosition`, then to `'top'`.
|
|
957
|
+
*/
|
|
958
|
+
readonly filterPosition: _angular_core.InputSignal<GogDropdownFilterPosition | undefined>;
|
|
959
|
+
/** Shown in place of the list when the query matches nothing. */
|
|
960
|
+
readonly filterEmptyMessage: _angular_core.InputSignal<string>;
|
|
961
|
+
/**
|
|
962
|
+
* How an option is matched against the query. Left null, the resolved `optionLabel` is
|
|
963
|
+
* matched case-insensitively as a substring — pass a function to search other fields, match
|
|
964
|
+
* on a prefix, or plug in your own fuzzy matcher.
|
|
965
|
+
*/
|
|
966
|
+
readonly filterMatch: _angular_core.InputSignal<((option: TOption, query: string) => boolean) | null>;
|
|
967
|
+
readonly errorMessage: _angular_core.InputSignal<string>;
|
|
968
|
+
/**
|
|
969
|
+
* See `GogErrorDisplay`. Unset, falls back to `GOG_CONFIG.control.errorDisplay`, then to
|
|
970
|
+
* `'manual'` — matching every other control in the library.
|
|
971
|
+
*/
|
|
972
|
+
readonly errorDisplay: _angular_core.InputSignal<GogErrorDisplay | undefined>;
|
|
973
|
+
/** Unset, falls back to `GOG_CONFIG.control.size`, then to `'md'`. */
|
|
974
|
+
readonly size: _angular_core.InputSignal<GogSize | undefined>;
|
|
975
|
+
/** Unset, falls back to `GOG_CONFIG.dropdown.direction`, then to `'auto'`. */
|
|
976
|
+
readonly dropdownDirection: _angular_core.InputSignal<GogDropdownDirection | undefined>;
|
|
977
|
+
/**
|
|
978
|
+
* Explicit stacking order for the panel. Left unset the panel falls back to the
|
|
979
|
+
* stylesheet's `--gog-dropdown-z`, so the default lives in CSS where a consumer can
|
|
980
|
+
* retheme it, rather than being baked into the component.
|
|
981
|
+
*/
|
|
982
|
+
readonly dropdownZIndex: _angular_core.InputSignal<number | null>;
|
|
983
|
+
/**
|
|
984
|
+
* Fixed panel width as any CSS length (`'320px'`, `'40ch'`, `'100%'`, …), applied only
|
|
985
|
+
* when `appendToBody` is set. Left unset the panel matches the trigger's width, same as
|
|
986
|
+
* the inline panel already does via CSS.
|
|
987
|
+
*/
|
|
988
|
+
readonly dropdownWidth: _angular_core.InputSignal<string | null>;
|
|
989
|
+
/**
|
|
990
|
+
* Fixed panel max-height as any CSS length, applied only when `appendToBody` is set.
|
|
991
|
+
* `px`, `%` and `vh` also feed the up/down placement math (see `resolveCssLengthPx`); any
|
|
992
|
+
* other unit still renders correctly but is invisible to that heuristic, so the panel may
|
|
993
|
+
* pick the "wrong" side close to a viewport edge. Left unset the panel keeps the existing
|
|
994
|
+
* viewport-derived, auto-flipping height.
|
|
995
|
+
*/
|
|
996
|
+
readonly dropdownMaxHeight: _angular_core.InputSignal<string | null>;
|
|
997
|
+
/** Unset, falls back to `GOG_CONFIG.dropdown.appendToBody`, then to `false`. */
|
|
998
|
+
readonly appendToBody: _angular_core.InputSignal<boolean | undefined>;
|
|
999
|
+
readonly disabled: _angular_core.InputSignal<boolean>;
|
|
1000
|
+
/** Projected `gogDropdownChevron` template, replacing the built-in chevron. */
|
|
1001
|
+
protected readonly chevronSlot: Signal<GogDropdownChevronDirective | undefined>;
|
|
1002
|
+
/**
|
|
1003
|
+
* Full width of the container by default, matching every other field-style control.
|
|
1004
|
+
* Set to `false` to shrink the trigger to fit its selected label instead.
|
|
1005
|
+
*/
|
|
1006
|
+
readonly fullWidth: _angular_core.InputSignal<boolean>;
|
|
1007
|
+
/** Unset, falls back to `GOG_CONFIG.floatLabel.variant`, then to `'none'` (off). */
|
|
1008
|
+
readonly floatLabel: _angular_core.InputSignal<GogFloatLabelVariant | undefined>;
|
|
1009
|
+
/** Unset, falls back to `GOG_CONFIG.floatLabel.showPlaceholder`, then to `false`. */
|
|
1010
|
+
readonly floatLabelShowPlaceholder: _angular_core.InputSignal<boolean | undefined>;
|
|
1011
|
+
/**
|
|
1012
|
+
* Press ripple on the panel's options. Unset, falls back to `GOG_CONFIG.ripple.enabled`, then
|
|
1013
|
+
* to `false`. The trigger itself never ripples — it is a field, not a button.
|
|
1014
|
+
*/
|
|
1015
|
+
readonly ripple: _angular_core.InputSignal<boolean | undefined>;
|
|
1016
|
+
readonly isOpen: _angular_core.WritableSignal<boolean>;
|
|
1017
|
+
/** Projected `gogDropdownOption` template, if the consumer supplied one. */
|
|
1018
|
+
protected readonly optionSlot: Signal<GogDropdownOptionDirective<any> | undefined>;
|
|
1019
|
+
/** An option's visible text, via `optionLabel`. */
|
|
1020
|
+
protected labelOf(option: TOption): string;
|
|
1021
|
+
/**
|
|
1022
|
+
* The value this control emits for an option, via `optionValue` — or the option object
|
|
1023
|
+
* itself when `optionValue` is `null`.
|
|
1024
|
+
*/
|
|
1025
|
+
protected valueOf(option: TOption): unknown;
|
|
1026
|
+
/** Whether an option is disabled, via `optionDisabled`. Anything falsy counts as enabled. */
|
|
1027
|
+
protected isOptionDisabled(option: TOption): boolean;
|
|
1028
|
+
/** Context for a projected `gogDropdownOption` row. */
|
|
1029
|
+
protected optionContext(option: TOption, selected: boolean): GogDropdownOptionContext<TOption>;
|
|
1030
|
+
/** Shared by both controls to match a resolved option value against the current value. */
|
|
1031
|
+
protected sameValue(a: unknown, b: unknown): boolean;
|
|
1032
|
+
/** The `<ng-template>` holding the panel markup, rendered inline or into `<body>`. */
|
|
1033
|
+
protected abstract readonly panelTemplate: Signal<TemplateRef<unknown> | undefined>;
|
|
1034
|
+
/** Declared by the subclass so each control keeps its own value type. */
|
|
1035
|
+
protected abstract readonly value: ModelSignal<TValue>;
|
|
1036
|
+
/** Value applied when a form writes `null` / the control is reset. */
|
|
1037
|
+
protected abstract readonly emptyValue: TValue;
|
|
1038
|
+
/** BEM class of one option button, used to collect keyboard-navigable elements. */
|
|
1039
|
+
protected abstract readonly optionClass: string;
|
|
1040
|
+
/** BEM class of the focusable trigger. */
|
|
1041
|
+
protected abstract readonly triggerClass: string;
|
|
1042
|
+
/**
|
|
1043
|
+
* BEM block that carries the size modifier — `.gog-select` vs `.gog-ms-wrapper`. Separate
|
|
1044
|
+
* from `triggerClass` because in `gog-multiselect` the two are different elements.
|
|
1045
|
+
*/
|
|
1046
|
+
protected abstract readonly sizeBlockClass: string;
|
|
1047
|
+
/** BEM block of the panel, which repeats the size modifier when appended to `<body>`. */
|
|
1048
|
+
protected abstract readonly panelBlockClass: string;
|
|
1049
|
+
/** Whether the control currently has a selection — drives the float-label "filled" state. */
|
|
1050
|
+
protected abstract readonly hasFloatValue: Signal<boolean>;
|
|
1051
|
+
/**
|
|
1052
|
+
* Spacing tokens feeding the height estimate. Overridable so each control keeps the
|
|
1053
|
+
* `--gog-<block>-*` names it already exposes to consumers for theming.
|
|
1054
|
+
*/
|
|
1055
|
+
/**
|
|
1056
|
+
* The gap **between rows**, as a seed for the first frame. Like `optionHeightToken`, the real
|
|
1057
|
+
* value is measured from the rendered list and replaces this.
|
|
1058
|
+
*
|
|
1059
|
+
* **Read the options container's CSS before pointing this at a token, not the token's name.**
|
|
1060
|
+
* Two of the three controls name a token `--gog-<block>-option-gap` and use it for the gap
|
|
1061
|
+
* *inside* a row — between the check mark and the label — with no gap between rows at all.
|
|
1062
|
+
* Seeding from it added 12px per row of panel that does not exist. Only `gog-multiselect`'s
|
|
1063
|
+
* options container declares a `gap`, and only it overrides this.
|
|
1064
|
+
*/
|
|
1065
|
+
protected readonly optionGapToken: string;
|
|
1066
|
+
protected readonly optionsPaddingToken: string;
|
|
1067
|
+
/** Keep in sync with the real `max-height` on the subclass's `__dropdown` block. */
|
|
1068
|
+
protected readonly panelMaxHeightToken: string;
|
|
1069
|
+
/** Estimated row height fed into the placement math; not a real layout property. */
|
|
1070
|
+
protected readonly optionHeightToken: string;
|
|
1071
|
+
/**
|
|
1072
|
+
* Unique per-instance suffix, so several dropdowns on a page can't collide on DOM ids.
|
|
1073
|
+
*
|
|
1074
|
+
* Deliberately a bare number rather than `nextGogControlId()` (which the single-element
|
|
1075
|
+
* controls use): each subclass derives *several* ids from this one instance — trigger,
|
|
1076
|
+
* listbox, label, error — so what it needs is the instance number, not one finished id.
|
|
1077
|
+
*/
|
|
1078
|
+
protected readonly uid: number;
|
|
1079
|
+
protected readonly elRef: ElementRef<any>;
|
|
1080
|
+
private readonly injector;
|
|
1081
|
+
protected readonly isBrowser: boolean;
|
|
1082
|
+
private readonly document;
|
|
1083
|
+
private readonly appRef;
|
|
1084
|
+
private readonly destroyRef;
|
|
1085
|
+
private readonly ngControl;
|
|
1086
|
+
/** `protected` so subclasses can resolve their own inputs against the same config object. */
|
|
1087
|
+
protected readonly globalConfig: _guildofgleks_ui_shared.GogGlobalConfig;
|
|
1088
|
+
private readonly overlay;
|
|
1089
|
+
/** Set from `(focus)`/`(blur)` on the trigger — see `onFocusIn`/`onFocusOut`. */
|
|
1090
|
+
protected readonly isFocused: _angular_core.WritableSignal<boolean>;
|
|
1091
|
+
/** Instance input → `GOG_CONFIG` → the component's own default. See `resolveConfigured`. */
|
|
1092
|
+
protected readonly resolvedSize: Signal<GogSize>;
|
|
1093
|
+
protected readonly rippleEnabled: Signal<boolean>;
|
|
1094
|
+
protected readonly resolvedErrorDisplay: Signal<GogErrorDisplay>;
|
|
1095
|
+
protected readonly resolvedAppendToBody: Signal<boolean>;
|
|
1096
|
+
protected readonly resolvedDropdownDirection: Signal<GogDropdownDirection>;
|
|
1097
|
+
/**
|
|
1098
|
+
* The single size modifier for the wrapper, replacing one `[class.<block>--<size>]` binding
|
|
1099
|
+
* per size. Empty for `'md'`: that is the default size and has no modifier rule of its own —
|
|
1100
|
+
* every `--gog-<block>-size-*` chain bottoms out at the `md` tokens.
|
|
1101
|
+
*/
|
|
1102
|
+
protected readonly sizeClass: Signal<string>;
|
|
1103
|
+
/**
|
|
1104
|
+
* Same modifier repeated on the panel, but only when it is appended to `<body>` — outside
|
|
1105
|
+
* the component's subtree the panel no longer inherits the wrapper's size tokens, so it has
|
|
1106
|
+
* to carry them itself. See the `--portal` blocks in the component stylesheets.
|
|
1107
|
+
*/
|
|
1108
|
+
protected readonly panelSizeClass: Signal<string>;
|
|
1109
|
+
private readonly cvaDisabled;
|
|
1110
|
+
private readonly errorState;
|
|
1111
|
+
protected readonly isDisabled: Signal<boolean>;
|
|
1112
|
+
protected readonly dropdownDirectionState: _angular_core.WritableSignal<"up" | "down">;
|
|
1113
|
+
protected readonly panelPlacement: _angular_core.WritableSignal<GogDropdownPlacement | null>;
|
|
1114
|
+
/**
|
|
1115
|
+
* Stacking order to write onto the panel, or null to leave it to the stylesheet.
|
|
1116
|
+
*
|
|
1117
|
+
* Only needed when appending to `<body>`: an inline panel inherits `--gog-dropdown-z` from
|
|
1118
|
+
* its surroundings — which is how a dropdown inside a dialog ends up above the dialog —
|
|
1119
|
+
* but an appended one sits outside that subtree and inherits nothing, so the value has
|
|
1120
|
+
* to be resolved from the trigger and written out explicitly.
|
|
1121
|
+
*/
|
|
1122
|
+
protected readonly panelZIndex: _angular_core.WritableSignal<number | null>;
|
|
1123
|
+
/**
|
|
1124
|
+
* Explicit panel width, or null to let it size to its own content.
|
|
1125
|
+
*
|
|
1126
|
+
* The panel used to be pinned to the trigger's width, which broke a trigger narrower than its
|
|
1127
|
+
* options: with `[fullWidth]="false"` the trigger shrinks to the *current* selection, so
|
|
1128
|
+
* picking a short option cut the longer ones off in the list. It now sizes to its content with
|
|
1129
|
+
* the trigger width as a floor — see `resolvedPanelMinWidth`.
|
|
1130
|
+
*/
|
|
1131
|
+
protected readonly resolvedPanelWidth: Signal<string | null>;
|
|
1132
|
+
/** The trigger's width, used as the panel's minimum so it never renders narrower than it. */
|
|
1133
|
+
protected readonly resolvedPanelMinWidth: Signal<string | null>;
|
|
1134
|
+
/** `dropdownMaxHeight`, or the viewport-derived max-height from `panelPlacement`. */
|
|
1135
|
+
protected readonly resolvedPanelMaxHeight: Signal<string | null>;
|
|
1136
|
+
protected readonly hasError: Signal<boolean>;
|
|
1137
|
+
protected readonly visibleError: Signal<string>;
|
|
1138
|
+
/**
|
|
1139
|
+
* Each control supplies its own default: `gog-multiselect` shipped a clear button before
|
|
1140
|
+
* `clearable` existed, so it keeps one; `gog-select` opts in.
|
|
1141
|
+
*/
|
|
1142
|
+
protected abstract readonly clearableByDefault: boolean;
|
|
1143
|
+
private readonly clearableState;
|
|
1144
|
+
/** Whether to render the clear button right now — see `GogClearableState`. */
|
|
1145
|
+
protected readonly showClear: Signal<boolean>;
|
|
1146
|
+
protected readonly resolvedFilter: Signal<boolean>;
|
|
1147
|
+
protected readonly resolvedFilterPosition: Signal<GogDropdownFilterPosition>;
|
|
1148
|
+
/** Current search text. Cleared whenever the panel closes, so reopening starts fresh. */
|
|
1149
|
+
protected readonly filterQuery: _angular_core.WritableSignal<string>;
|
|
1150
|
+
/**
|
|
1151
|
+
* The options actually rendered. Everything downstream — the loops, the keyboard navigation
|
|
1152
|
+
* target list, the panel height estimate, and multiselect's select-all — reads this rather
|
|
1153
|
+
* than `options()`, so filtering stays consistent instead of only hiding rows visually.
|
|
1154
|
+
*/
|
|
1155
|
+
protected readonly visibleOptions: Signal<TOption[]>;
|
|
1156
|
+
protected onFilterInput(event: Event): void;
|
|
1157
|
+
/**
|
|
1158
|
+
* Scrolls `index` into view **without moving focus**, which is what a combobox needs: focus
|
|
1159
|
+
* stays in the text field and the highlight is pointed at with `aria-activedescendant`, so
|
|
1160
|
+
* nothing scrolls the row into view on its own the way focusing it would.
|
|
1161
|
+
*/
|
|
1162
|
+
protected revealOption(index: number): void;
|
|
1163
|
+
/** Puts the scroller, the window and the keyboard's idea of "here" back at the top together. */
|
|
1164
|
+
protected resetPanelScroll(): void;
|
|
1165
|
+
/**
|
|
1166
|
+
* Moves the scroller and the window's own idea of where it is, in that order and in the same
|
|
1167
|
+
* turn.
|
|
1168
|
+
*
|
|
1169
|
+
* The signal is written here rather than waiting for the scroller to echo the change back
|
|
1170
|
+
* through `(gogScroll)`: that echo is coalesced into an animation frame, so a keyboard move
|
|
1171
|
+
* that scrolled would compute its new range one frame after it moved focus -- which is one
|
|
1172
|
+
* frame during which the row it is trying to focus has not been rendered.
|
|
1173
|
+
*/
|
|
1174
|
+
private setPanelScrollTop;
|
|
1175
|
+
/**
|
|
1176
|
+
* `gog-scroll`'s scrolling element inside the open panel, found in the DOM rather than with a
|
|
1177
|
+
* `viewChild`: an appended panel is attached to `<body>` as its own change-detection root, so a
|
|
1178
|
+
* view query on this component does not reach it.
|
|
1179
|
+
*/
|
|
1180
|
+
private panelViewportElement;
|
|
1181
|
+
/** Resets the control to its empty value and notifies any attached form. */
|
|
1182
|
+
protected clearValue(event: Event): void;
|
|
1183
|
+
/**
|
|
1184
|
+
* `hasFloatValue` is wrapped in a `computed` rather than passed straight through: these
|
|
1185
|
+
* field initializers run before the *subclass's* do, so `this.hasFloatValue` is still
|
|
1186
|
+
* undefined right here. The arrow defers the read until the signal is first evaluated, by
|
|
1187
|
+
* which time the subclass has assigned it.
|
|
1188
|
+
*/
|
|
1189
|
+
private readonly floatLabelState;
|
|
1190
|
+
protected readonly resolvedFloatLabel: Signal<GogFloatLabelVariant>;
|
|
1191
|
+
protected readonly isFloatLabelActive: Signal<boolean>;
|
|
1192
|
+
protected readonly isFloatLabelFloated: Signal<boolean>;
|
|
1193
|
+
protected readonly effectivePlaceholder: Signal<string>;
|
|
1194
|
+
/** Measured once per open rather than per scroll tick — see `refreshPanelMetrics`. */
|
|
1195
|
+
private optionGap;
|
|
1196
|
+
private optionsPadding;
|
|
1197
|
+
private maxPanelHeight;
|
|
1198
|
+
private optionHeight;
|
|
1199
|
+
/**
|
|
1200
|
+
* A real row's height, once one has ever been rendered. `null` until then.
|
|
1201
|
+
*
|
|
1202
|
+
* The token this class reads for `optionHeight` calls itself an estimate, and it is a bad one:
|
|
1203
|
+
* measured against a rendered row in all eleven shipped themes it is wrong in every one, from
|
|
1204
|
+
* -0.62px (`terminal`) to +8.38px (`parchment`), and low in ten of them. No static value can be
|
|
1205
|
+
* right -- the same `parchment` row is 48.38px at `--gog-density: 1` and 42.38px at 0.85,
|
|
1206
|
+
* because the height is padding plus leading plus border and a theme or a consumer can move
|
|
1207
|
+
* every term.
|
|
1208
|
+
*
|
|
1209
|
+
* That matters because the estimate is not decorative: `estimatePanelHeight` multiplies it by
|
|
1210
|
+
* the option count and `resolveDropdownDirection` opens the panel up or down on the result. Low
|
|
1211
|
+
* by 7px a row means a five-row panel judged to fit below when it needs 37px more than there is.
|
|
1212
|
+
* Only short lists were ever affected -- above `panelMaxHeightToken` the cap dominates and the
|
|
1213
|
+
* error is masked -- which is why it went unseen.
|
|
1214
|
+
*/
|
|
1215
|
+
private measuredOptionHeight;
|
|
1216
|
+
/**
|
|
1217
|
+
* The real gap between two rows, once a list has ever rendered. `null` until then.
|
|
1218
|
+
*
|
|
1219
|
+
* Measured for the same reason the row height is, and it came out of the same audit: the token
|
|
1220
|
+
* `optionGapToken` seeds from is the *inside* of a row in two of the three controls (mark to
|
|
1221
|
+
* label), while their options containers declare no row gap at all. Reading the container is
|
|
1222
|
+
* exact where reading a token is a guess about what the token means.
|
|
1223
|
+
*/
|
|
1224
|
+
private measuredOptionGap;
|
|
1225
|
+
private measureFrame;
|
|
1226
|
+
private repositionFrame;
|
|
1227
|
+
/**
|
|
1228
|
+
* Whether this instance windows its list. Resolved the same way every other dropdown setting
|
|
1229
|
+
* is: instance input, then `GOG_CONFIG.dropdown.virtualize`, then off.
|
|
1230
|
+
*
|
|
1231
|
+
* **Off by default and never switched on automatically.** A windowed list and a plain one
|
|
1232
|
+
* differ under `Ctrl+F`, under a screen reader's "list all items", and under any consumer CSS
|
|
1233
|
+
* that targets `:last-child`; flipping that at a row-count threshold would make the component's
|
|
1234
|
+
* behaviour depend on how much data happened to arrive, which works in development and
|
|
1235
|
+
* surprises in production. `GOG_CONFIG.ripple.enabled` is off by default for the same reason.
|
|
1236
|
+
*/
|
|
1237
|
+
protected readonly resolvedVirtualize: Signal<boolean>;
|
|
1238
|
+
/**
|
|
1239
|
+
* The subclass's own `virtualize` input, if it offers one.
|
|
1240
|
+
*
|
|
1241
|
+
* A subclass overrides this with its input rather than the base declaring one, so a control
|
|
1242
|
+
* that has not adopted windowing yet does not inherit a public input that does nothing. Read
|
|
1243
|
+
* only from inside a `computed`, which is what lets a subclass field override a base field
|
|
1244
|
+
* that was initialised first.
|
|
1245
|
+
*/
|
|
1246
|
+
protected readonly virtualizeRequest: Signal<boolean | undefined>;
|
|
1247
|
+
/**
|
|
1248
|
+
* How far apart two rows start, in px: the row's own height plus whatever gap the list puts
|
|
1249
|
+
* between rows. Seeded from the tokens and replaced by the measurement, exactly as the
|
|
1250
|
+
* placement estimate is -- and for a window it matters more, because the error accumulates
|
|
1251
|
+
* once per row instead of once per panel.
|
|
1252
|
+
*/
|
|
1253
|
+
protected readonly rowPitch: _angular_core.WritableSignal<number>;
|
|
1254
|
+
/**
|
|
1255
|
+
* The gap alone, as a signal, because `spacerHeight` needs it from inside a `computed` and the
|
|
1256
|
+
* cached `measuredOptionGap` beside it is a plain field -- a computed reading that would be
|
|
1257
|
+
* correct only by the accident of `rowPitch` changing in the same statement.
|
|
1258
|
+
*/
|
|
1259
|
+
private readonly rowGap;
|
|
1260
|
+
/**
|
|
1261
|
+
* The scroller's real geometry, fed from `gog-scroll`'s own `(gogScroll)`.
|
|
1262
|
+
*
|
|
1263
|
+
* The plan called for a `ResizeObserver` on the scroller; it is not needed, because the
|
|
1264
|
+
* scroller already runs one and already coalesces scroll and resize into a single
|
|
1265
|
+
* rAF-batched emission carrying both numbers. A second observer would have measured the same
|
|
1266
|
+
* element one frame later.
|
|
1267
|
+
*
|
|
1268
|
+
* `height` is the viewport's `clientHeight`, never `--gog-*-panel-max-height`: that token is a
|
|
1269
|
+
* cap, and a panel with three options is three rows tall.
|
|
1270
|
+
*/
|
|
1271
|
+
protected readonly panelViewport: _angular_core.WritableSignal<{
|
|
1272
|
+
scrollTop: number;
|
|
1273
|
+
height: number;
|
|
1274
|
+
}>;
|
|
1275
|
+
private readonly virtualWindow;
|
|
1276
|
+
/** Index into `visibleOptions()` of the row the keyboard is on, or -1 for none. */
|
|
1277
|
+
protected readonly activeOptionIndex: _angular_core.WritableSignal<number>;
|
|
1278
|
+
/** The slice of `visibleOptions()` actually stamped into the panel. */
|
|
1279
|
+
protected readonly optionWindow: Signal<_guildofgleks_ui_shared.GogVirtualRange>;
|
|
1280
|
+
/**
|
|
1281
|
+
* What the template loops over. Identical to `visibleOptions()` when not windowing, and the
|
|
1282
|
+
* same array instance, so nothing re-renders for the sake of a slice that changed nothing.
|
|
1283
|
+
*/
|
|
1284
|
+
protected readonly renderedOptions: Signal<TOption[]>;
|
|
1285
|
+
/**
|
|
1286
|
+
* Filler above and below the rendered rows, in px.
|
|
1287
|
+
*
|
|
1288
|
+
* Spacers rather than absolute positioning: the rows stay flex children of the same container,
|
|
1289
|
+
* so every gap, selector and `:last-child` the component already relies on keeps working. See
|
|
1290
|
+
* `GogVirtualWindow`'s own note for why that trade is worth making for a one-column list.
|
|
1291
|
+
*/
|
|
1292
|
+
protected readonly padBefore: Signal<number>;
|
|
1293
|
+
protected readonly padAfter: Signal<number>;
|
|
1294
|
+
/**
|
|
1295
|
+
* A spacer's own height, less the gap the flex column puts either side of it.
|
|
1296
|
+
*
|
|
1297
|
+
* A spacer is a flex child like a row, so a list that declares `gap` gets one *around the
|
|
1298
|
+
* spacer too* -- and the window's padding already accounts for every gap in the rows it stands
|
|
1299
|
+
* in for. Left uncorrected, an open panel is two gaps too tall and every rendered row sits one
|
|
1300
|
+
* gap lower than its index says. It is a constant, not an accumulating error, which is exactly
|
|
1301
|
+
* why it would have survived review: at `gog-multiselect`'s 4px nothing looks wrong, it is just
|
|
1302
|
+
* 4px wrong everywhere.
|
|
1303
|
+
*
|
|
1304
|
+
* `gog-select` and `gog-autocomplete` declare no row gap, so this subtracts nothing there. The
|
|
1305
|
+
* subtraction can never go negative on a spacer that exists: any non-zero padding is at least
|
|
1306
|
+
* one whole row, and a row is taller than the gap beside it.
|
|
1307
|
+
*/
|
|
1308
|
+
private spacerHeight;
|
|
1309
|
+
/**
|
|
1310
|
+
* A windowed listbox holds twenty `role="option"` children and has to announce ten thousand.
|
|
1311
|
+
*
|
|
1312
|
+
* Set only while windowing. An unwindowed list has every option in the DOM, and the browser's
|
|
1313
|
+
* own count is then both correct and free -- restating it would be one more thing to keep true.
|
|
1314
|
+
*/
|
|
1315
|
+
protected readonly ariaSetSize: Signal<number | null>;
|
|
1316
|
+
/** The real position of a rendered row in the full list, 1-based, or null when not windowing. */
|
|
1317
|
+
protected ariaPosInSet(renderedIndex: number): number | null;
|
|
1318
|
+
/** `(gogScroll)` on the panel's `gog-scroll`. */
|
|
1319
|
+
protected onPanelScroll(metrics: {
|
|
1320
|
+
scrollTop: number;
|
|
1321
|
+
clientHeight: number;
|
|
1322
|
+
}): void;
|
|
1323
|
+
/**
|
|
1324
|
+
* A row that scrolls out of the window is unmounted, and an unmounted element holding focus
|
|
1325
|
+
* drops it on `<body>` -- where an open panel has no keyboard at all: Escape does not close it
|
|
1326
|
+
* and the arrows scroll the page instead of the list.
|
|
1327
|
+
*
|
|
1328
|
+
* So a mouse scroll that would take the focused row away hands focus back to the trigger,
|
|
1329
|
+
* which is a state both Escape and ArrowDown work from. Checked here rather than in an effect
|
|
1330
|
+
* because this runs *before* the re-render, while the row still exists and can still be asked
|
|
1331
|
+
* whether it is the focused one -- after the unmount that question has no answer.
|
|
1332
|
+
*
|
|
1333
|
+
* This is the price of windowing that a plain list does not pay, and the reason it is opt-in.
|
|
1334
|
+
*/
|
|
1335
|
+
private releaseFocusLeavingTheWindow;
|
|
1336
|
+
private onChangeFn;
|
|
1337
|
+
private onTouchedFn;
|
|
1338
|
+
constructor();
|
|
1339
|
+
/**
|
|
1340
|
+
* Binds the click-outside and reposition listeners only for as long as the panel is
|
|
1341
|
+
* open. Bound permanently — as host listeners were — every dropdown on the page runs a
|
|
1342
|
+
* handler on every document click and every scroll frame, whether or not it is showing
|
|
1343
|
+
* anything.
|
|
1344
|
+
*/
|
|
1345
|
+
private bindWhileOpen;
|
|
1346
|
+
ngDoCheck(): void;
|
|
1347
|
+
writeValue(val: TValue | null): void;
|
|
1348
|
+
registerOnChange(fn: (val: TValue) => void): void;
|
|
1349
|
+
registerOnTouched(fn: () => void): void;
|
|
1350
|
+
setDisabledState(isDisabled: boolean): void;
|
|
1351
|
+
/** Bound to `(focus)` on the trigger — only drives the float-label floated state. */
|
|
1352
|
+
protected onFocusIn(): void;
|
|
1353
|
+
/** Bound to `(blur)` on the trigger — only drives the float-label floated state. */
|
|
1354
|
+
protected onFocusOut(): void;
|
|
1355
|
+
protected toggle(): void;
|
|
1356
|
+
protected open(): void;
|
|
1357
|
+
/**
|
|
1358
|
+
* Gives the window numbers to work from before anything has rendered.
|
|
1359
|
+
*
|
|
1360
|
+
* Without this the first frame has a viewport height of zero, which `GogVirtualWindow`
|
|
1361
|
+
* deliberately reads as "render everything" -- correct, and exactly the 10 000 rows the window
|
|
1362
|
+
* exists to avoid, stamped once before the scroller reports its real height a frame later. The
|
|
1363
|
+
* seed is the same panel-height estimate placement already uses, so no new arithmetic and no
|
|
1364
|
+
* new token.
|
|
1365
|
+
*/
|
|
1366
|
+
private seedWindow;
|
|
1367
|
+
protected close(): void;
|
|
1368
|
+
/** Writes a new selection out to both the model and the attached form control. */
|
|
1369
|
+
protected commitValue(next: TValue): void;
|
|
1370
|
+
protected markTouched(): void;
|
|
1371
|
+
private closeIfClickedOutside;
|
|
1372
|
+
/**
|
|
1373
|
+
* Coalesces bursts of scroll/resize events into one reposition per frame. Placement
|
|
1374
|
+
* reads layout, so running it per event is what makes a scroll janky.
|
|
1375
|
+
*/
|
|
1376
|
+
private scheduleReposition;
|
|
1377
|
+
/**
|
|
1378
|
+
* Reads one real row after the panel has rendered, and re-places if the token had lied.
|
|
1379
|
+
*
|
|
1380
|
+
* One frame late by construction -- the row has to exist -- so the very first open of an
|
|
1381
|
+
* instance can be placed from the estimate and corrected before the next paint. Every later
|
|
1382
|
+
* open starts from the measurement and is right immediately, which is why this caches rather
|
|
1383
|
+
* than measuring each time.
|
|
1384
|
+
*
|
|
1385
|
+
* Deliberately does **not** fall back to the token when no row is found: an empty list has no
|
|
1386
|
+
* row to measure and no rows to be wrong about, and overwriting a good measurement with the
|
|
1387
|
+
* token because the user filtered everything away would undo the fix.
|
|
1388
|
+
*/
|
|
1389
|
+
private scheduleOptionMeasure;
|
|
1390
|
+
/** Jumps from the trigger into the option list on ArrowDown/ArrowUp while open. */
|
|
1391
|
+
protected onTriggerArrowKeydown(event: Event): void;
|
|
1392
|
+
protected onOptionKeydown(event: KeyboardEvent): void;
|
|
1393
|
+
/**
|
|
1394
|
+
* Moves the keyboard's index by one roving-focus key, skipping disabled options and wrapping,
|
|
1395
|
+
* then makes the DOM agree: scroll first if the target is outside the window, focus second.
|
|
1396
|
+
*/
|
|
1397
|
+
private moveActiveOption;
|
|
1398
|
+
/**
|
|
1399
|
+
* Puts the keyboard on `index` and the focus with it.
|
|
1400
|
+
*
|
|
1401
|
+
* The wait is conditional on purpose: `scrollOffsetFor` returns null when the row is already
|
|
1402
|
+
* visible, which means it is already rendered and can be focused in this turn. Only a move
|
|
1403
|
+
* that actually changes the window has to wait for the render that stamps the row.
|
|
1404
|
+
*/
|
|
1405
|
+
private focusOptionAt;
|
|
1406
|
+
private focusRenderedOption;
|
|
1407
|
+
protected focusTrigger(): void;
|
|
1408
|
+
/** Extra chrome stacked above the options (e.g. a select-all row), in px. */
|
|
1409
|
+
protected extraPanelHeight(): number;
|
|
1410
|
+
private enabledOptionElements;
|
|
1411
|
+
private attachOverlay;
|
|
1412
|
+
/**
|
|
1413
|
+
* Reads the spacing tokens that feed the height estimate. Done once per open instead of
|
|
1414
|
+
* on every scroll tick, because `getComputedStyle` forces a style recalculation and the
|
|
1415
|
+
* tokens cannot change while the panel is on screen.
|
|
1416
|
+
*/
|
|
1417
|
+
private refreshPanelMetrics;
|
|
1418
|
+
/**
|
|
1419
|
+
* The panel's height, used to choose up/down and to cap `max-height`.
|
|
1420
|
+
*
|
|
1421
|
+
* Estimated on the very first open of an instance and exact from then on. Measuring needs a
|
|
1422
|
+
* rendered row and placement runs before the panel renders, so the first pass uses the row
|
|
1423
|
+
* height token; `measureOptionHeight` then reads a real row and re-places if it disagreed.
|
|
1424
|
+
* Every open after that starts from the measurement.
|
|
1425
|
+
*
|
|
1426
|
+
* A resolvable `dropdownMaxHeight` replaces the row-count arithmetic outright, since it is
|
|
1427
|
+
* exact rather than derived.
|
|
1428
|
+
*/
|
|
1429
|
+
private estimatePanelHeight;
|
|
1430
|
+
/**
|
|
1431
|
+
* The trigger's own rect, not the whole component's — a label above it or an error
|
|
1432
|
+
* message below it sit in normal document flow and are not part of what the panel needs
|
|
1433
|
+
* to clear. This also keeps `appendToBody` placement consistent with the inline panel,
|
|
1434
|
+
* which is already positioned purely relative to the trigger via CSS.
|
|
1435
|
+
*/
|
|
1436
|
+
private triggerRect;
|
|
1437
|
+
/** The stacking order the panel would have had if it were still inside the subtree. */
|
|
1438
|
+
private resolvePanelZIndex;
|
|
1439
|
+
private updatePlacement;
|
|
1440
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<GogDropdownBase<any, any>, never>;
|
|
1441
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<GogDropdownBase<any, any>, never, never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "optionLabel": { "alias": "optionLabel"; "required": false; "isSignal": true; }; "optionValue": { "alias": "optionValue"; "required": false; "isSignal": true; }; "optionDisabled": { "alias": "optionDisabled"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; "clearAriaLabel": { "alias": "clearAriaLabel"; "required": false; "isSignal": true; }; "minWidth": { "alias": "minWidth"; "required": false; "isSignal": true; }; "filter": { "alias": "filter"; "required": false; "isSignal": true; }; "filterPlaceholder": { "alias": "filterPlaceholder"; "required": false; "isSignal": true; }; "filterPosition": { "alias": "filterPosition"; "required": false; "isSignal": true; }; "filterEmptyMessage": { "alias": "filterEmptyMessage"; "required": false; "isSignal": true; }; "filterMatch": { "alias": "filterMatch"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "errorDisplay": { "alias": "errorDisplay"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "dropdownDirection": { "alias": "dropdownDirection"; "required": false; "isSignal": true; }; "dropdownZIndex": { "alias": "dropdownZIndex"; "required": false; "isSignal": true; }; "dropdownWidth": { "alias": "dropdownWidth"; "required": false; "isSignal": true; }; "dropdownMaxHeight": { "alias": "dropdownMaxHeight"; "required": false; "isSignal": true; }; "appendToBody": { "alias": "appendToBody"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "fullWidth": { "alias": "fullWidth"; "required": false; "isSignal": true; }; "floatLabel": { "alias": "floatLabel"; "required": false; "isSignal": true; }; "floatLabelShowPlaceholder": { "alias": "floatLabelShowPlaceholder"; "required": false; "isSignal": true; }; "ripple": { "alias": "ripple"; "required": false; "isSignal": true; }; }, {}, ["chevronSlot", "optionSlot"], never, true, never>;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
/**
|
|
1445
|
+
* Renders a template into `document.body` while keeping it wired to the declaring
|
|
1446
|
+
* component: the view is stamped with that component's style encapsulation and reads
|
|
1447
|
+
* its signals directly.
|
|
1448
|
+
*
|
|
1449
|
+
* That is the whole point of doing it this way rather than with a second, portal-only
|
|
1450
|
+
* component: one `<ng-template>` can serve both the inline and the appended-to-body
|
|
1451
|
+
* dropdown, so the option markup exists once and cannot drift between the two modes,
|
|
1452
|
+
* and selection state needs no manual field-syncing to stay in step.
|
|
1453
|
+
*/
|
|
1454
|
+
declare class GogDropdownOverlay {
|
|
1455
|
+
private readonly appRef;
|
|
1456
|
+
private readonly document;
|
|
1457
|
+
private hostEl;
|
|
1458
|
+
private viewRef;
|
|
1459
|
+
constructor(appRef: ApplicationRef, document: Document);
|
|
1460
|
+
/** The `<body>` child holding the rendered panel, or null while detached. */
|
|
1461
|
+
get hostElement(): HTMLElement | null;
|
|
1462
|
+
get isAttached(): boolean;
|
|
1463
|
+
attach(template: TemplateRef<unknown>, themeSource?: Element | null): void;
|
|
1464
|
+
detach(): void;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
/**
|
|
1468
|
+
* Shared float-label state for the library's field-style controls.
|
|
1469
|
+
*
|
|
1470
|
+
* A plain class rather than a base class or directive, for the same reason as `GogErrorState`:
|
|
1471
|
+
* it has to serve `gog-inputfield` and `gog-textarea`, which share no base class, as well as
|
|
1472
|
+
* `GogDropdownBase`, which is one. Each consumer owns an instance and exposes whichever
|
|
1473
|
+
* members its template needs.
|
|
1474
|
+
*
|
|
1475
|
+
* The one thing each control must supply itself is `hasValue` — "this field has content" means
|
|
1476
|
+
* something different everywhere (a non-empty string, a non-null selection, a non-empty
|
|
1477
|
+
* selection array), which is exactly why this is composed in rather than implemented as a
|
|
1478
|
+
* directive sitting outside the component.
|
|
1479
|
+
*/
|
|
1480
|
+
declare class GogFloatLabelState {
|
|
1481
|
+
private readonly variantInput;
|
|
1482
|
+
private readonly showPlaceholderInput;
|
|
1483
|
+
private readonly placeholder;
|
|
1484
|
+
private readonly isFocused;
|
|
1485
|
+
private readonly hasValue;
|
|
1486
|
+
private readonly config;
|
|
1487
|
+
/**
|
|
1488
|
+
* @param variantInput the control's own `floatLabel` input (`undefined` when unset)
|
|
1489
|
+
* @param showPlaceholderInput the control's own `floatLabelShowPlaceholder` input
|
|
1490
|
+
* @param placeholder the control's `placeholder` input
|
|
1491
|
+
* @param isFocused whether the control currently has focus
|
|
1492
|
+
* @param hasValue whether the control has a value — see the class note
|
|
1493
|
+
* @param config the injected `GOG_CONFIG`
|
|
1494
|
+
*/
|
|
1495
|
+
constructor(variantInput: Signal<GogFloatLabelVariant | undefined>, showPlaceholderInput: Signal<boolean | undefined>, placeholder: Signal<string>, isFocused: Signal<boolean>, hasValue: Signal<boolean>, config: GogGlobalConfig);
|
|
1496
|
+
/** The resolved variant: instance input, else `GOG_CONFIG.floatLabel.variant`, else `'none'`. */
|
|
1497
|
+
readonly variant: Signal<GogFloatLabelVariant>;
|
|
1498
|
+
readonly showPlaceholder: Signal<boolean>;
|
|
1499
|
+
/** Whether a float label is in effect at all — `false` keeps the static label-above layout. */
|
|
1500
|
+
readonly isActive: Signal<boolean>;
|
|
1501
|
+
/** Whether the label is at its floated target rather than resting like a placeholder. */
|
|
1502
|
+
readonly isFloated: Signal<boolean>;
|
|
1503
|
+
/**
|
|
1504
|
+
* The placeholder the control should actually render. While a float label is active the
|
|
1505
|
+
* resting label already occupies that space, so the placeholder stays hidden unless the
|
|
1506
|
+
* consumer opted into `floatLabelShowPlaceholder` — and even then only once the label has
|
|
1507
|
+
* floated out of the way.
|
|
1508
|
+
*/
|
|
1509
|
+
readonly effectivePlaceholder: Signal<string>;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
/**
|
|
1513
|
+
* Icons an app has registered, keyed by the name `gog-icon` will be asked for. Resolves to `{}`
|
|
1514
|
+
* — only the built-ins are available — until a `provideGogIcons(...)` call fills it in.
|
|
1515
|
+
*
|
|
1516
|
+
* Values are raw `<svg>` markup, injected with `bypassSecurityTrustHtml` exactly like the
|
|
1517
|
+
* built-ins. See `provideGogIcons` for what that means for you.
|
|
1518
|
+
*/
|
|
1519
|
+
declare const GOG_ICONS: InjectionToken<Readonly<Record<string, string>>>;
|
|
1520
|
+
/**
|
|
1521
|
+
* Registers icons by name, so `<gog-icon name="cart" />` works for glyphs the library does not
|
|
1522
|
+
* ship. This is the supported way to use your own icon set: the alternative — a `TemplateRef`
|
|
1523
|
+
* per instance through the `template` input — costs a `<ng-template>` at every use site and is
|
|
1524
|
+
* meant for one-offs, not for an icon set.
|
|
1525
|
+
*
|
|
1526
|
+
* ```ts
|
|
1527
|
+
* // app.config.ts
|
|
1528
|
+
* providers: [
|
|
1529
|
+
* provideGogIcons({
|
|
1530
|
+
* cart: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">…</svg>',
|
|
1531
|
+
* user: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">…</svg>',
|
|
1532
|
+
* }),
|
|
1533
|
+
* ]
|
|
1534
|
+
* ```
|
|
1535
|
+
*
|
|
1536
|
+
* **A registered name overrides a built-in of the same name**, which is how you swap the
|
|
1537
|
+
* library's checkmark or chevron for your own without touching every component that renders one.
|
|
1538
|
+
*
|
|
1539
|
+
* **Providing this again further down the injector tree layers onto the parent's set** rather
|
|
1540
|
+
* than replacing it, matching `provideGogConfig`: a lazy feature can register the three icons
|
|
1541
|
+
* only it uses, and the app-wide set stays available inside it.
|
|
1542
|
+
*
|
|
1543
|
+
* ## What to put in the SVG
|
|
1544
|
+
*
|
|
1545
|
+
* Use `stroke="currentColor"` (or `fill="currentColor"`) and a `viewBox`, and leave the sizing
|
|
1546
|
+
* alone — `gog-icon`'s stylesheet drives width, height and stroke width from the
|
|
1547
|
+
* `--gog-icon-*` tokens, so an icon inherits size and colour from wherever it is used, the same
|
|
1548
|
+
* as a built-in.
|
|
1549
|
+
*
|
|
1550
|
+
* ## Security
|
|
1551
|
+
*
|
|
1552
|
+
* The markup is inserted with `DomSanitizer.bypassSecurityTrustHtml`, because Angular's HTML
|
|
1553
|
+
* sanitizer strips SVG and would leave you with nothing. That is safe for what this is for —
|
|
1554
|
+
* static icon markup you wrote or imported at build time — and unsafe for anything derived from
|
|
1555
|
+
* user input or fetched at runtime. **Never build a registered icon string from data you did
|
|
1556
|
+
* not author.** If you need remote icons, fetch them yourself, sanitize them with a real SVG
|
|
1557
|
+
* sanitizer, and register the result.
|
|
1558
|
+
*/
|
|
1559
|
+
declare function provideGogIcons(icons: Readonly<Record<string, string>>): Provider;
|
|
1560
|
+
|
|
1561
|
+
/**
|
|
1562
|
+
* The glyphs the package ships. Kept as a closed union so they still autocomplete and so
|
|
1563
|
+
* `ICON_DEFS` below stays exhaustive — a `Record` over the open `GogIconName` would collapse
|
|
1564
|
+
* into an index signature and stop catching a missing definition.
|
|
1565
|
+
*/
|
|
1566
|
+
type GogBuiltinIconName = 'check' | 'close' | 'chevron-up' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'calendar' | 'clock' | 'sort' | 'sort-up' | 'sort-down' | 'success' | 'error' | 'warning' | 'info' | 'checkbox' | 'checkbox-checked' | 'eye' | 'eye-off' | 'copy' | 'search' | 'plus' | 'minus' | 'trash' | 'pencil' | 'download' | 'upload' | 'refresh' | 'filter' | 'external-link' | 'menu' | 'more-horizontal' | 'more-vertical' | 'arrow-left' | 'arrow-right' | 'user' | 'settings' | 'lock' | 'mail' | 'star' | 'star-filled';
|
|
1567
|
+
/**
|
|
1568
|
+
* Any icon `gog-icon` can render: one of the built-ins, or a name registered through
|
|
1569
|
+
* `provideGogIcons(...)`. The `(string & {})` half keeps the built-in names in autocomplete
|
|
1570
|
+
* while letting a registered name through — an open union is the point, since the set of icons
|
|
1571
|
+
* an app uses is the app's business, not the library's.
|
|
1572
|
+
*/
|
|
1573
|
+
type GogIconName = GogBuiltinIconName | (string & {});
|
|
1574
|
+
declare const ICON_DEFS: Record<GogBuiltinIconName, string>;
|
|
1575
|
+
|
|
1576
|
+
/**
|
|
1577
|
+
* Which writing direction a portaled overlay has to carry, if any.
|
|
1578
|
+
*
|
|
1579
|
+
* A dropdown panel or tooltip bubble is appended to `<body>`, which takes it out of whatever
|
|
1580
|
+
* subtree it was opened from. `dir` is inherited, so an overlay opened inside an RTL region of
|
|
1581
|
+
* an otherwise-LTR page would render LTR — text aligned the wrong way, the panel's own
|
|
1582
|
+
* `inset-inline-*` resolving against the wrong side. Copying the nearest scoped `dir` onto the
|
|
1583
|
+
* portal host fixes that, exactly as `scopedOverlayTheme` does for `data-theme`.
|
|
1584
|
+
*
|
|
1585
|
+
* **Only when the direction really is scoped.** When the nearest `[dir]` is the document
|
|
1586
|
+
* element — the ordinary case, a whole RTL app — the overlay already inherits it through
|
|
1587
|
+
* `<body>`, and re-stating it is noise on every panel in the app.
|
|
1588
|
+
*
|
|
1589
|
+
* Known limitation, shared with `scopedOverlayTheme`: a direction set through CSS
|
|
1590
|
+
* (`direction: rtl` in a stylesheet) rather than the `dir` attribute is not detected. Reading
|
|
1591
|
+
* `getComputedStyle().direction` would catch it, but that forces layout on every open for a
|
|
1592
|
+
* case the HTML spec itself discourages — `dir` is the attribute browsers, form controls and
|
|
1593
|
+
* assistive tech all key off.
|
|
1594
|
+
*/
|
|
1595
|
+
declare function scopedOverlayDirection(directionSource: Element | null, documentElement: Element): 'ltr' | 'rtl' | null;
|
|
1596
|
+
|
|
1597
|
+
/**
|
|
1598
|
+
* Which `data-theme` an overlay rendered into `<body>` has to carry, if any.
|
|
1599
|
+
*
|
|
1600
|
+
* A panel or tooltip bubble is appended to `<body>`, which puts it outside whatever subtree it
|
|
1601
|
+
* was opened from. `data-theme` can be scoped to any subtree — several themes rendering side by
|
|
1602
|
+
* side is a documented use — so an overlay opened inside one of those has to be told which theme
|
|
1603
|
+
* it belongs to, or it silently picks up the document's instead.
|
|
1604
|
+
*
|
|
1605
|
+
* **But only when the theme really is scoped.** When the nearest themed ancestor is the document
|
|
1606
|
+
* element, copying the attribute is not merely redundant, it is actively wrong: the overlay
|
|
1607
|
+
* already inherits everything from `<html>` through `<body>`, and re-stating `data-theme` on it
|
|
1608
|
+
* makes it match `theme.css`'s derived layer (`:root, [data-theme]`) *locally*. That re-declares
|
|
1609
|
+
* every component token on the overlay itself, resolved against the plain preset palette — which
|
|
1610
|
+
* discards anything set on `<html>` that is not part of that preset. Custom properties written
|
|
1611
|
+
* inline on `:root` are the case that bites: a live theme editor sets them there, the page
|
|
1612
|
+
* follows, and every portal keeps rendering the un-edited theme.
|
|
1613
|
+
*
|
|
1614
|
+
* So: return the theme only for a genuinely scoped ancestor, and let inheritance do the work
|
|
1615
|
+
* otherwise.
|
|
1616
|
+
*
|
|
1617
|
+
* Known limitation, currently unreachable: a *scoped* theme that is itself being edited through
|
|
1618
|
+
* inline custom properties would still lose those on the overlay, since only the attribute is
|
|
1619
|
+
* carried across. Copying resolved values instead would mean reading ~1200 properties on every
|
|
1620
|
+
* open, which is not worth it for a case nothing does yet.
|
|
1621
|
+
*/
|
|
1622
|
+
declare function scopedOverlayTheme(themeSource: Element | null, documentElement: Element): string | null;
|
|
1623
|
+
|
|
1624
|
+
/**
|
|
1625
|
+
* Off, so the ripple is purely additive: adding it to the library changed the appearance of
|
|
1626
|
+
* nothing until an app asks for it. Flipping this default would change how every button in every
|
|
1627
|
+
* consuming app looks, which is a release of its own, not a line in a feature commit.
|
|
1628
|
+
*/
|
|
1629
|
+
declare const DEFAULT_RIPPLE = false;
|
|
1630
|
+
/**
|
|
1631
|
+
* Shared "does this control ripple" state, resolved the usual way — instance input, then
|
|
1632
|
+
* `GOG_CONFIG.ripple.enabled`, then off.
|
|
1633
|
+
*
|
|
1634
|
+
* A plain function rather than a class (`GogClearableState`'s shape) because there is exactly one
|
|
1635
|
+
* derived value and nothing to hold: every component that ripples writes the same two lines, and
|
|
1636
|
+
* this is what keeps the precedence identical across all nine of them instead of nine separate
|
|
1637
|
+
* `??` chains that can drift.
|
|
1638
|
+
*
|
|
1639
|
+
* The component then binds the *negation* onto its own inner element:
|
|
1640
|
+
*
|
|
1641
|
+
* ```html
|
|
1642
|
+
* <button class="gog-btn" gogRipple [rippleDisabled]="!rippleEnabled()">
|
|
1643
|
+
* ```
|
|
1644
|
+
*
|
|
1645
|
+
* `[gogRipple]` is always applied rather than toggled, because a directive cannot be added and
|
|
1646
|
+
* removed by a binding — and it costs nothing while disabled: `GogRippleDirective` attaches no
|
|
1647
|
+
* event listeners at all until `rippleDisabled` goes false.
|
|
1648
|
+
*/
|
|
1649
|
+
declare function resolveRipple(rippleInput: Signal<boolean | undefined>, config: GogGlobalConfig): Signal<boolean>;
|
|
1650
|
+
|
|
1651
|
+
declare const VERTICAL_ARROWS: readonly ["ArrowDown", "ArrowUp"];
|
|
1652
|
+
declare const HORIZONTAL_ARROWS: readonly ["ArrowRight", "ArrowLeft"];
|
|
1653
|
+
declare const EDGE_KEYS: readonly ["Home", "End"];
|
|
1654
|
+
type RovingFocusKey = (typeof VERTICAL_ARROWS)[number] | (typeof HORIZONTAL_ARROWS)[number] | (typeof EDGE_KEYS)[number];
|
|
1655
|
+
/**
|
|
1656
|
+
* Whether `key` navigates a roving-tabindex list laid out along `orientation`.
|
|
1657
|
+
*
|
|
1658
|
+
* The orientation filter is the point: a horizontal tablist must leave `ArrowDown` alone so
|
|
1659
|
+
* the page still scrolls, and a vertical listbox must not steal `ArrowRight` from a caret
|
|
1660
|
+
* inside a text field. `Home`/`End` are accepted for both.
|
|
1661
|
+
*/
|
|
1662
|
+
declare function isRovingFocusKey(key: string, orientation?: GogOrientation): key is RovingFocusKey;
|
|
1663
|
+
/**
|
|
1664
|
+
* The index `key` moves to from `currentIndex`, wrapping at both ends.
|
|
1665
|
+
*
|
|
1666
|
+
* `isEnabled` lets a caller keep disabled items in the list — which it must, since they still
|
|
1667
|
+
* occupy a position in the DOM — while navigation steps over them. Without it every index is a
|
|
1668
|
+
* target, which is the behaviour the dropdowns rely on (they pre-filter their own list).
|
|
1669
|
+
* Returns `currentIndex` when nothing else is reachable, so a group of one enabled item cannot
|
|
1670
|
+
* spin.
|
|
1671
|
+
*/
|
|
1672
|
+
declare function nextRovingFocusIndex(key: RovingFocusKey, currentIndex: number, count: number, isEnabled?: (index: number) => boolean): number;
|
|
1673
|
+
/**
|
|
1674
|
+
* Arrow/Home/End navigation across a roving-tabindex-style list of focusable elements.
|
|
1675
|
+
* `event.currentTarget` must be one of `items`. Returns false (and leaves the event
|
|
1676
|
+
* untouched) for any other key, or when `items` doesn't contain the current target,
|
|
1677
|
+
* so callers can safely invoke this unconditionally from a keydown handler.
|
|
1678
|
+
*
|
|
1679
|
+
* Defaults to a vertical list with every item reachable — the shape the option lists and the
|
|
1680
|
+
* accordion were written against — so existing callers need no options object.
|
|
1681
|
+
*/
|
|
1682
|
+
declare function handleRovingFocusKeydown(event: KeyboardEvent, items: readonly HTMLElement[], options?: {
|
|
1683
|
+
orientation?: GogOrientation;
|
|
1684
|
+
/** Items this returns true for are skipped rather than focused. */
|
|
1685
|
+
isDisabled?: (item: HTMLElement, index: number) => boolean;
|
|
1686
|
+
}): boolean;
|
|
1687
|
+
|
|
1688
|
+
/** Every `--gog-*` custom property the library declares or documents as an override point. */
|
|
1689
|
+
type GogTokenName = '--gog-accent-bright' | '--gog-accent-color' | '--gog-accent-dim' | '--gog-accent-pale' | '--gog-accent-text-color' | '--gog-accordion-accent-color' | '--gog-accordion-body-bg' | '--gog-accordion-body-color' | '--gog-accordion-body-font-family' | '--gog-accordion-body-font-size' | '--gog-accordion-body-lift' | '--gog-accordion-body-line-height' | '--gog-accordion-body-padding-bottom' | '--gog-accordion-body-padding-top' | '--gog-accordion-body-radius' | '--gog-accordion-body-transition-duration' | '--gog-accordion-border-color' | '--gog-accordion-border-style' | '--gog-accordion-border-width' | '--gog-accordion-chevron-font-size' | '--gog-accordion-chevron-line-height' | '--gog-accordion-chevron-size' | '--gog-accordion-chevron-transition-duration' | '--gog-accordion-content-gap' | '--gog-accordion-disabled-opacity' | '--gog-accordion-focus-ring-color' | '--gog-accordion-focus-ring-width' | '--gog-accordion-font-family' | '--gog-accordion-font-size' | '--gog-accordion-header-bg' | '--gog-accordion-header-gap' | '--gog-accordion-header-text-transform' | '--gog-accordion-hover-bg' | '--gog-accordion-hover-color' | '--gog-accordion-letter-spacing' | '--gog-accordion-lg-body-font-size' | '--gog-accordion-lg-body-line-height' | '--gog-accordion-lg-body-padding-bottom' | '--gog-accordion-lg-body-padding-top' | '--gog-accordion-lg-chevron-font-size' | '--gog-accordion-lg-chevron-size' | '--gog-accordion-lg-content-gap' | '--gog-accordion-lg-font-size' | '--gog-accordion-lg-letter-spacing' | '--gog-accordion-lg-padding-x' | '--gog-accordion-lg-padding-y' | '--gog-accordion-line-height' | '--gog-accordion-md-body-font-size' | '--gog-accordion-md-body-line-height' | '--gog-accordion-md-body-padding-bottom' | '--gog-accordion-md-body-padding-top' | '--gog-accordion-md-chevron-font-size' | '--gog-accordion-md-chevron-size' | '--gog-accordion-md-content-gap' | '--gog-accordion-md-font-size' | '--gog-accordion-md-letter-spacing' | '--gog-accordion-md-padding-x' | '--gog-accordion-md-padding-y' | '--gog-accordion-padding-x' | '--gog-accordion-padding-y' | '--gog-accordion-press-bg' | '--gog-accordion-radius' | '--gog-accordion-slg-body-font-size' | '--gog-accordion-slg-body-line-height' | '--gog-accordion-slg-body-padding-bottom' | '--gog-accordion-slg-body-padding-top' | '--gog-accordion-slg-chevron-font-size' | '--gog-accordion-slg-chevron-size' | '--gog-accordion-slg-content-gap' | '--gog-accordion-slg-font-size' | '--gog-accordion-slg-letter-spacing' | '--gog-accordion-slg-padding-x' | '--gog-accordion-slg-padding-y' | '--gog-accordion-sm-body-font-size' | '--gog-accordion-sm-body-line-height' | '--gog-accordion-sm-body-padding-bottom' | '--gog-accordion-sm-body-padding-top' | '--gog-accordion-sm-chevron-font-size' | '--gog-accordion-sm-chevron-size' | '--gog-accordion-sm-content-gap' | '--gog-accordion-sm-font-size' | '--gog-accordion-sm-letter-spacing' | '--gog-accordion-sm-padding-x' | '--gog-accordion-sm-padding-y' | '--gog-accordion-text-color' | '--gog-accordion-transition-duration' | '--gog-accordion-xsm-body-font-size' | '--gog-accordion-xsm-body-line-height' | '--gog-accordion-xsm-body-padding-bottom' | '--gog-accordion-xsm-body-padding-top' | '--gog-accordion-xsm-chevron-font-size' | '--gog-accordion-xsm-chevron-size' | '--gog-accordion-xsm-content-gap' | '--gog-accordion-xsm-font-size' | '--gog-accordion-xsm-letter-spacing' | '--gog-accordion-xsm-padding-x' | '--gog-accordion-xsm-padding-y' | '--gog-alert-accent-color' | '--gog-alert-bg' | '--gog-alert-body-font-size' | '--gog-alert-body-line-height' | '--gog-alert-border-color' | '--gog-alert-border-style' | '--gog-alert-border-width' | '--gog-alert-color' | '--gog-alert-danger-color' | '--gog-alert-edge-width' | '--gog-alert-font-family' | '--gog-alert-gap' | '--gog-alert-heading-color' | '--gog-alert-heading-font-size' | '--gog-alert-heading-font-weight' | '--gog-alert-heading-line-height' | '--gog-alert-icon-font-size' | '--gog-alert-icon-line-height' | '--gog-alert-info-color' | '--gog-alert-main-gap' | '--gog-alert-padding-x' | '--gog-alert-padding-y' | '--gog-alert-radius' | '--gog-alert-success-color' | '--gog-alert-warning-color' | '--gog-autocomplete-actions-gap' | '--gog-autocomplete-actions-inset' | '--gog-autocomplete-actions-reserve' | '--gog-autocomplete-bg' | '--gog-autocomplete-border-color' | '--gog-autocomplete-border-style' | '--gog-autocomplete-border-width' | '--gog-autocomplete-clear-color' | '--gog-autocomplete-clear-hover-color' | '--gog-autocomplete-clear-icon-ratio' | '--gog-autocomplete-color' | '--gog-autocomplete-disabled-opacity' | '--gog-autocomplete-empty-color' | '--gog-autocomplete-empty-font-size' | '--gog-autocomplete-empty-line-height' | '--gog-autocomplete-error-border-color' | '--gog-autocomplete-error-color' | '--gog-autocomplete-error-font-family' | '--gog-autocomplete-error-font-size' | '--gog-autocomplete-error-line-height' | '--gog-autocomplete-field-bg' | '--gog-autocomplete-float-label-in-top' | '--gog-autocomplete-float-label-on-bg' | '--gog-autocomplete-float-label-over-gap' | '--gog-autocomplete-float-label-over-reserve' | '--gog-autocomplete-float-label-reserve' | '--gog-autocomplete-focus-ring-color' | '--gog-autocomplete-focus-ring-offset' | '--gog-autocomplete-focus-ring-width' | '--gog-autocomplete-font-family' | '--gog-autocomplete-font-size' | '--gog-autocomplete-gap' | '--gog-autocomplete-hover-border-color' | '--gog-autocomplete-label-color' | '--gog-autocomplete-label-font-family' | '--gog-autocomplete-label-font-size' | '--gog-autocomplete-label-font-weight' | '--gog-autocomplete-label-letter-spacing' | '--gog-autocomplete-label-line-height' | '--gog-autocomplete-label-text-transform' | '--gog-autocomplete-line-height' | '--gog-autocomplete-min-width' | '--gog-autocomplete-option-color' | '--gog-autocomplete-option-gap' | '--gog-autocomplete-option-height' | '--gog-autocomplete-option-hover-bg' | '--gog-autocomplete-option-hover-color' | '--gog-autocomplete-option-padding' | '--gog-autocomplete-option-press-bg' | '--gog-autocomplete-option-radius' | '--gog-autocomplete-option-selected-bg' | '--gog-autocomplete-option-selected-color' | '--gog-autocomplete-options-padding' | '--gog-autocomplete-padding-x' | '--gog-autocomplete-padding-y' | '--gog-autocomplete-panel-bg' | '--gog-autocomplete-panel-border-color' | '--gog-autocomplete-panel-border-style' | '--gog-autocomplete-panel-border-width' | '--gog-autocomplete-panel-gap' | '--gog-autocomplete-panel-max-height' | '--gog-autocomplete-panel-max-width' | '--gog-autocomplete-panel-radius' | '--gog-autocomplete-panel-shadow' | '--gog-autocomplete-placeholder-color' | '--gog-autocomplete-radius' | '--gog-autocomplete-spinner-size' | '--gog-autocomplete-text-color' | '--gog-autocomplete-transition-duration' | '--gog-background-color' | '--gog-badge-bg' | '--gog-badge-border-color' | '--gog-badge-border-style' | '--gog-badge-border-width' | '--gog-badge-color' | '--gog-badge-danger-bg' | '--gog-badge-danger-color' | '--gog-badge-dot-size' | '--gog-badge-font-family' | '--gog-badge-font-size' | '--gog-badge-font-weight' | '--gog-badge-info-bg' | '--gog-badge-info-color' | '--gog-badge-line-height' | '--gog-badge-offset' | '--gog-badge-padding-inline' | '--gog-badge-radius' | '--gog-badge-size' | '--gog-badge-success-bg' | '--gog-badge-success-color' | '--gog-badge-warning-bg' | '--gog-badge-warning-color' | '--gog-badge-z' | '--gog-border-color' | '--gog-border-style' | '--gog-border-width' | '--gog-button-active-scale' | '--gog-button-bg' | '--gog-button-border' | '--gog-button-border-style' | '--gog-button-border-width' | '--gog-button-color' | '--gog-button-danger-fill' | '--gog-button-danger-fill-hover' | '--gog-button-danger-fill-press' | '--gog-button-danger-ink' | '--gog-button-danger-on-fill' | '--gog-button-danger-wash' | '--gog-button-disabled-opacity' | '--gog-button-focus-ring-color' | '--gog-button-focus-ring-offset' | '--gog-button-focus-ring-width' | '--gog-button-font-family' | '--gog-button-font-weight' | '--gog-button-gap' | '--gog-button-ghost-bg' | '--gog-button-ghost-border' | '--gog-button-ghost-color' | '--gog-button-ghost-hover-bg' | '--gog-button-ghost-hover-color' | '--gog-button-ghost-hover-shadow' | '--gog-button-ghost-press-bg' | '--gog-button-ghost-press-color' | '--gog-button-ghost-shadow' | '--gog-button-ghost-spinner-color' | '--gog-button-ghost-toggled-shadow' | '--gog-button-hover-bg' | '--gog-button-hover-color' | '--gog-button-hover-shadow' | '--gog-button-info-fill' | '--gog-button-info-fill-hover' | '--gog-button-info-fill-press' | '--gog-button-info-ink' | '--gog-button-info-on-fill' | '--gog-button-info-wash' | '--gog-button-letter-spacing' | '--gog-button-lg-font-size' | '--gog-button-lg-padding' | '--gog-button-line-height' | '--gog-button-loading-opacity' | '--gog-button-md-font-size' | '--gog-button-md-padding' | '--gog-button-outline-bg' | '--gog-button-outline-border' | '--gog-button-outline-color' | '--gog-button-outline-hover-bg' | '--gog-button-outline-hover-color' | '--gog-button-outline-hover-shadow' | '--gog-button-outline-press-bg' | '--gog-button-outline-press-color' | '--gog-button-outline-shadow' | '--gog-button-outline-spinner-color' | '--gog-button-outline-toggled-shadow' | '--gog-button-press-bg' | '--gog-button-press-color' | '--gog-button-primary-bg' | '--gog-button-primary-border' | '--gog-button-primary-color' | '--gog-button-primary-hover-bg' | '--gog-button-primary-hover-color' | '--gog-button-primary-hover-shadow' | '--gog-button-primary-press-bg' | '--gog-button-primary-press-color' | '--gog-button-primary-shadow' | '--gog-button-primary-spinner-color' | '--gog-button-primary-toggled-shadow' | '--gog-button-radius' | '--gog-button-secondary-bg' | '--gog-button-secondary-border' | '--gog-button-secondary-color' | '--gog-button-secondary-hover-bg' | '--gog-button-secondary-hover-color' | '--gog-button-secondary-hover-shadow' | '--gog-button-secondary-press-bg' | '--gog-button-secondary-press-color' | '--gog-button-secondary-shadow' | '--gog-button-secondary-spinner-color' | '--gog-button-secondary-toggled-shadow' | '--gog-button-shadow' | '--gog-button-slg-font-size' | '--gog-button-slg-padding' | '--gog-button-sm-font-size' | '--gog-button-sm-padding' | '--gog-button-spinner-color' | '--gog-button-spinner-max-size' | '--gog-button-success-fill' | '--gog-button-success-fill-hover' | '--gog-button-success-fill-press' | '--gog-button-success-ink' | '--gog-button-success-on-fill' | '--gog-button-success-wash' | '--gog-button-text-transform' | '--gog-button-toggle-bg' | '--gog-button-toggle-border-color' | '--gog-button-toggle-border-style' | '--gog-button-toggle-border-width' | '--gog-button-toggle-color' | '--gog-button-toggle-disabled-opacity' | '--gog-button-toggle-focus-ring-color' | '--gog-button-toggle-focus-ring-offset' | '--gog-button-toggle-focus-ring-width' | '--gog-button-toggle-font-family' | '--gog-button-toggle-font-size' | '--gog-button-toggle-font-weight' | '--gog-button-toggle-gap' | '--gog-button-toggle-hover-bg' | '--gog-button-toggle-hover-color' | '--gog-button-toggle-icon-size' | '--gog-button-toggle-letter-spacing' | '--gog-button-toggle-line-height' | '--gog-button-toggle-padding' | '--gog-button-toggle-press-bg' | '--gog-button-toggle-radius' | '--gog-button-toggle-rest-bg' | '--gog-button-toggle-rest-color' | '--gog-button-toggle-selected-bg' | '--gog-button-toggle-selected-border-color' | '--gog-button-toggle-selected-color' | '--gog-button-toggle-selected-press-bg' | '--gog-button-toggle-separated-gap' | '--gog-button-toggle-text-transform' | '--gog-button-toggle-transition-duration' | '--gog-button-toggled-ring-width' | '--gog-button-toggled-shadow' | '--gog-button-transition-duration' | '--gog-button-warning-fill' | '--gog-button-warning-fill-hover' | '--gog-button-warning-fill-press' | '--gog-button-warning-ink' | '--gog-button-warning-on-fill' | '--gog-button-warning-wash' | '--gog-button-xsm-font-size' | '--gog-button-xsm-padding' | '--gog-calendar-action-padding' | '--gog-calendar-color' | '--gog-calendar-day-bg' | '--gog-calendar-day-border-style' | '--gog-calendar-day-border-width' | '--gog-calendar-day-color' | '--gog-calendar-day-hover-bg' | '--gog-calendar-day-outside-color' | '--gog-calendar-day-radius' | '--gog-calendar-day-rest-bg' | '--gog-calendar-day-rest-color' | '--gog-calendar-day-size' | '--gog-calendar-disabled-opacity' | '--gog-calendar-divider-color' | '--gog-calendar-divider-style' | '--gog-calendar-divider-width' | '--gog-calendar-focus-ring-color' | '--gog-calendar-focus-ring-offset' | '--gog-calendar-focus-ring-width' | '--gog-calendar-font-family' | '--gog-calendar-footer-gap' | '--gog-calendar-header-gap' | '--gog-calendar-header-margin' | '--gog-calendar-lg-day-size' | '--gog-calendar-lg-font-size' | '--gog-calendar-line-height' | '--gog-calendar-max-width' | '--gog-calendar-md-day-size' | '--gog-calendar-md-font-size' | '--gog-calendar-months-gap' | '--gog-calendar-nav-bg' | '--gog-calendar-nav-color' | '--gog-calendar-nav-hover-bg' | '--gog-calendar-nav-hover-color' | '--gog-calendar-nav-icon-overlap' | '--gog-calendar-nav-icon-size' | '--gog-calendar-nav-radius' | '--gog-calendar-nav-size' | '--gog-calendar-padding' | '--gog-calendar-range-bg' | '--gog-calendar-range-color' | '--gog-calendar-selected-bg' | '--gog-calendar-selected-color' | '--gog-calendar-selected-font-weight' | '--gog-calendar-slg-day-size' | '--gog-calendar-slg-font-size' | '--gog-calendar-sm-day-size' | '--gog-calendar-sm-font-size' | '--gog-calendar-time-gap' | '--gog-calendar-time-input-bg' | '--gog-calendar-time-input-padding' | '--gog-calendar-time-input-width' | '--gog-calendar-time-margin' | '--gog-calendar-title-color' | '--gog-calendar-title-font-weight' | '--gog-calendar-today-border-color' | '--gog-calendar-today-font-weight' | '--gog-calendar-transition-duration' | '--gog-calendar-weekday-color' | '--gog-calendar-weekday-font-size' | '--gog-calendar-weekday-font-weight' | '--gog-calendar-weekday-line-height' | '--gog-calendar-weekday-padding' | '--gog-calendar-weekday-text-transform' | '--gog-calendar-xsm-day-size' | '--gog-calendar-xsm-font-size' | '--gog-card-bg' | '--gog-card-border-color' | '--gog-card-border-style' | '--gog-card-border-width' | '--gog-card-color' | '--gog-card-disabled-opacity' | '--gog-card-elevated-bg' | '--gog-card-elevated-border-color' | '--gog-card-elevated-shadow' | '--gog-card-filled-bg' | '--gog-card-filled-border-color' | '--gog-card-filled-shadow' | '--gog-card-focus-ring' | '--gog-card-focus-ring-offset' | '--gog-card-focus-ring-width' | '--gog-card-font-family' | '--gog-card-footer-border-color' | '--gog-card-footer-gap' | '--gog-card-footer-padding-top' | '--gog-card-gap' | '--gog-card-heading-color' | '--gog-card-heading-font-family' | '--gog-card-heading-font-size' | '--gog-card-heading-font-weight' | '--gog-card-heading-line-height' | '--gog-card-hover-border-color' | '--gog-card-hover-shadow' | '--gog-card-lg-gap' | '--gog-card-lg-padding-x' | '--gog-card-lg-padding-y' | '--gog-card-md-gap' | '--gog-card-md-padding-x' | '--gog-card-md-padding-y' | '--gog-card-outlined-bg' | '--gog-card-outlined-border-color' | '--gog-card-outlined-shadow' | '--gog-card-padding-x' | '--gog-card-padding-y' | '--gog-card-radius' | '--gog-card-shadow' | '--gog-card-slg-gap' | '--gog-card-slg-padding-x' | '--gog-card-slg-padding-y' | '--gog-card-sm-gap' | '--gog-card-sm-padding-x' | '--gog-card-sm-padding-y' | '--gog-card-transition-duration' | '--gog-card-xsm-gap' | '--gog-card-xsm-padding-x' | '--gog-card-xsm-padding-y' | '--gog-checkbox-bg' | '--gog-checkbox-border-color' | '--gog-checkbox-border-style' | '--gog-checkbox-border-width' | '--gog-checkbox-box-size' | '--gog-checkbox-checked-bg' | '--gog-checkbox-checked-border' | '--gog-checkbox-dash-height' | '--gog-checkbox-dash-radius' | '--gog-checkbox-dash-width-ratio' | '--gog-checkbox-disabled-opacity' | '--gog-checkbox-focus-ring' | '--gog-checkbox-focus-ring-offset' | '--gog-checkbox-focus-ring-width' | '--gog-checkbox-font-family' | '--gog-checkbox-gap' | '--gog-checkbox-icon-color' | '--gog-checkbox-icon-line-height' | '--gog-checkbox-icon-size' | '--gog-checkbox-label-color' | '--gog-checkbox-label-line-height' | '--gog-checkbox-label-size' | '--gog-checkbox-padding' | '--gog-checkbox-radius' | '--gog-checkbox-transition-duration' | '--gog-chip-avatar-inset-ratio' | '--gog-chip-avatar-size' | '--gog-chip-bg' | '--gog-chip-border' | '--gog-chip-border-style' | '--gog-chip-border-width' | '--gog-chip-color' | '--gog-chip-disabled-opacity' | '--gog-chip-focus-ring-color' | '--gog-chip-focus-ring-offset' | '--gog-chip-focus-ring-width' | '--gog-chip-font-family' | '--gog-chip-font-size' | '--gog-chip-font-weight' | '--gog-chip-gap' | '--gog-chip-hover-bg' | '--gog-chip-icon-size' | '--gog-chip-lg-avatar-size' | '--gog-chip-lg-font-size' | '--gog-chip-lg-gap' | '--gog-chip-lg-icon-size' | '--gog-chip-lg-padding-block' | '--gog-chip-lg-padding-inline' | '--gog-chip-lg-remove-size' | '--gog-chip-line-height' | '--gog-chip-md-avatar-size' | '--gog-chip-md-font-size' | '--gog-chip-md-gap' | '--gog-chip-md-icon-size' | '--gog-chip-md-padding-block' | '--gog-chip-md-padding-inline' | '--gog-chip-md-remove-size' | '--gog-chip-padding-block' | '--gog-chip-padding-inline' | '--gog-chip-pill-radius' | '--gog-chip-press-bg' | '--gog-chip-radius' | '--gog-chip-remove-color' | '--gog-chip-remove-hover-color' | '--gog-chip-remove-inset-ratio' | '--gog-chip-remove-scale' | '--gog-chip-remove-size' | '--gog-chip-selected-ring-width' | '--gog-chip-selected-shadow' | '--gog-chip-slg-avatar-size' | '--gog-chip-slg-font-size' | '--gog-chip-slg-gap' | '--gog-chip-slg-icon-size' | '--gog-chip-slg-padding-block' | '--gog-chip-slg-padding-inline' | '--gog-chip-slg-remove-size' | '--gog-chip-sm-avatar-size' | '--gog-chip-sm-font-size' | '--gog-chip-sm-gap' | '--gog-chip-sm-icon-size' | '--gog-chip-sm-padding-block' | '--gog-chip-sm-padding-inline' | '--gog-chip-sm-remove-size' | '--gog-chip-xsm-avatar-size' | '--gog-chip-xsm-font-size' | '--gog-chip-xsm-gap' | '--gog-chip-xsm-icon-size' | '--gog-chip-xsm-padding-block' | '--gog-chip-xsm-padding-inline' | '--gog-chip-xsm-remove-size' | '--gog-collapsible-disabled-opacity' | '--gog-collapsible-max-height' | '--gog-collapsible-transition-duration' | '--gog-confirmation-dialog-actions-gap' | '--gog-confirmation-dialog-actions-offset' | '--gog-confirmation-dialog-color' | '--gog-confirmation-dialog-description-color' | '--gog-confirmation-dialog-description-font-size' | '--gog-confirmation-dialog-description-line-height' | '--gog-confirmation-dialog-gap' | '--gog-confirmation-dialog-max-width' | '--gog-confirmation-dialog-min-width' | '--gog-confirmation-dialog-title-font-size' | '--gog-confirmation-dialog-title-line-height' | '--gog-control-border-style' | '--gog-control-border-width' | '--gog-control-boundary-color' | '--gog-control-checkbox-box-size-lg' | '--gog-control-checkbox-box-size-md' | '--gog-control-checkbox-box-size-slg' | '--gog-control-checkbox-box-size-sm' | '--gog-control-checkbox-box-size-xsm' | '--gog-control-checkbox-icon-size-lg' | '--gog-control-checkbox-icon-size-md' | '--gog-control-checkbox-icon-size-slg' | '--gog-control-checkbox-icon-size-sm' | '--gog-control-checkbox-icon-size-xsm' | '--gog-control-checkbox-label-size-lg' | '--gog-control-checkbox-label-size-md' | '--gog-control-checkbox-label-size-slg' | '--gog-control-checkbox-label-size-sm' | '--gog-control-checkbox-label-size-xsm' | '--gog-control-checkbox-padding' | '--gog-control-clear-icon-ratio' | '--gog-control-icon-offset' | '--gog-control-padding-x' | '--gog-control-padding-y' | '--gog-danger-color' | '--gog-danger-shade' | '--gog-danger-text-color' | '--gog-datepicker-actions-gap' | '--gog-datepicker-actions-inset' | '--gog-datepicker-actions-reserve' | '--gog-datepicker-bg' | '--gog-datepicker-border-color' | '--gog-datepicker-border-style' | '--gog-datepicker-border-width' | '--gog-datepicker-clear-color' | '--gog-datepicker-clear-icon-ratio' | '--gog-datepicker-color' | '--gog-datepicker-disabled-opacity' | '--gog-datepicker-error-border-color' | '--gog-datepicker-error-color' | '--gog-datepicker-error-font-family' | '--gog-datepicker-error-font-size' | '--gog-datepicker-error-line-height' | '--gog-datepicker-field-bg' | '--gog-datepicker-float-label-in-top' | '--gog-datepicker-float-label-on-bg' | '--gog-datepicker-float-label-over-gap' | '--gog-datepicker-float-label-over-reserve' | '--gog-datepicker-float-label-reserve' | '--gog-datepicker-focus-ring-color' | '--gog-datepicker-focus-ring-offset' | '--gog-datepicker-focus-ring-width' | '--gog-datepicker-font-family' | '--gog-datepicker-font-size' | '--gog-datepicker-gap' | '--gog-datepicker-hover-border-color' | '--gog-datepicker-icon-color' | '--gog-datepicker-icon-hover-color' | '--gog-datepicker-label-color' | '--gog-datepicker-label-font-family' | '--gog-datepicker-label-font-size' | '--gog-datepicker-label-letter-spacing' | '--gog-datepicker-label-line-height' | '--gog-datepicker-label-text-transform' | '--gog-datepicker-line-height' | '--gog-datepicker-min-width' | '--gog-datepicker-padding-x' | '--gog-datepicker-padding-y' | '--gog-datepicker-panel-bg' | '--gog-datepicker-panel-border-color' | '--gog-datepicker-panel-border-style' | '--gog-datepicker-panel-border-width' | '--gog-datepicker-panel-gap' | '--gog-datepicker-panel-radius' | '--gog-datepicker-panel-shadow' | '--gog-datepicker-panel-width' | '--gog-datepicker-placeholder-color' | '--gog-datepicker-radius' | '--gog-datepicker-text-color' | '--gog-datepicker-toggle-icon-size' | '--gog-datepicker-transition-duration' | '--gog-density' | '--gog-dialog-backdrop-bg' | '--gog-dialog-backdrop-blur' | '--gog-dialog-backdrop-fade-duration' | '--gog-dialog-backdrop-padding' | '--gog-dialog-bg' | '--gog-dialog-body-max-height' | '--gog-dialog-body-padding' | '--gog-dialog-border' | '--gog-dialog-border-style' | '--gog-dialog-border-width' | '--gog-dialog-close-color' | '--gog-dialog-close-focus-ring' | '--gog-dialog-close-focus-ring-offset' | '--gog-dialog-close-focus-ring-width' | '--gog-dialog-close-font-size' | '--gog-dialog-close-hover-bg' | '--gog-dialog-close-hover-border' | '--gog-dialog-close-hover-color' | '--gog-dialog-close-line-height' | '--gog-dialog-close-size' | '--gog-dialog-color' | '--gog-dialog-enter-distance' | '--gog-dialog-enter-duration' | '--gog-dialog-font-family' | '--gog-dialog-header-border-color' | '--gog-dialog-header-gap' | '--gog-dialog-header-padding' | '--gog-dialog-max-height' | '--gog-dialog-min-width' | '--gog-dialog-offset-x' | '--gog-dialog-offset-y' | '--gog-dialog-radius' | '--gog-dialog-shadow' | '--gog-dialog-title-font-size' | '--gog-dialog-title-line-height' | '--gog-disabled-opacity' | '--gog-divider-block-spacing' | '--gog-divider-color' | '--gog-divider-dashed-style' | '--gog-divider-dotted-style' | '--gog-divider-inline-spacing' | '--gog-divider-inset-size' | '--gog-divider-label-color' | '--gog-divider-label-font-family' | '--gog-divider-label-font-size' | '--gog-divider-label-font-weight' | '--gog-divider-label-gap' | '--gog-divider-label-line-height' | '--gog-divider-line-color' | '--gog-divider-line-thickness' | '--gog-divider-solid-style' | '--gog-divider-spacing' | '--gog-divider-thickness' | '--gog-divider-vertical-length' | '--gog-dropdown-z' | '--gog-duration-base' | '--gog-duration-fast' | '--gog-duration-slow' | '--gog-easing' | '--gog-elevated-surface-color' | '--gog-elevation-0' | '--gog-elevation-1' | '--gog-elevation-2' | '--gog-elevation-3' | '--gog-elevation-4' | '--gog-elevation-5' | '--gog-elevation-ambient-alpha' | '--gog-elevation-contact' | '--gog-elevation-contact-blur' | '--gog-elevation-highlight' | '--gog-elevation-highlight-alpha' | '--gog-elevation-highlight-ink' | '--gog-elevation-ink' | '--gog-elevation-key-alpha' | '--gog-elevation-key-blur' | '--gog-elevation-key-x' | '--gog-elevation-key-y' | '--gog-elevation-ring' | '--gog-elevation-ring-width' | '--gog-field-error-line-height' | '--gog-field-float-label-in-top' | '--gog-field-float-label-over-gap' | '--gog-field-float-label-over-reserve' | '--gog-field-float-label-reserve' | '--gog-field-icon-glyph' | '--gog-field-icon-glyph-sm' | '--gog-field-icon-glyph-xsm' | '--gog-field-label-line-height' | '--gog-field-lg-font-size' | '--gog-field-lg-icon-inset' | '--gog-field-lg-icon-offset' | '--gog-field-lg-padding-x' | '--gog-field-lg-padding-y' | '--gog-field-line-height' | '--gog-field-md-font-size' | '--gog-field-md-icon-inset' | '--gog-field-md-icon-offset' | '--gog-field-md-padding-x' | '--gog-field-md-padding-y' | '--gog-field-slg-font-size' | '--gog-field-slg-icon-inset' | '--gog-field-slg-icon-offset' | '--gog-field-slg-padding-x' | '--gog-field-slg-padding-y' | '--gog-field-sm-font-size' | '--gog-field-sm-icon-inset' | '--gog-field-sm-icon-offset' | '--gog-field-sm-padding-x' | '--gog-field-sm-padding-y' | '--gog-field-xsm-font-size' | '--gog-field-xsm-icon-inset' | '--gog-field-xsm-icon-offset' | '--gog-field-xsm-padding-x' | '--gog-field-xsm-padding-y' | '--gog-focus-ring-offset' | '--gog-focus-ring-width' | '--gog-font-body' | '--gog-font-heading' | '--gog-font-mono' | '--gog-font-weight-bold' | '--gog-font-weight-heavy' | '--gog-font-weight-medium' | '--gog-font-weight-semibold' | '--gog-hover-color' | '--gog-icon-fallback-size' | '--gog-icon-size' | '--gog-icon-stroke-width' | '--gog-info-color' | '--gog-info-shade' | '--gog-info-text-color' | '--gog-input-clear-icon-ratio' | '--gog-input-clear-inset' | '--gog-input-clear-line-height' | '--gog-input-clear-radius' | '--gog-input-disabled-opacity' | '--gog-input-error-color' | '--gog-input-error-font-size' | '--gog-input-error-line-height' | '--gog-input-error-offset' | '--gog-input-field-bg' | '--gog-input-field-border' | '--gog-input-field-border-style' | '--gog-input-field-border-width' | '--gog-input-field-color' | '--gog-input-float-label-in-top' | '--gog-input-float-label-on-bg' | '--gog-input-float-label-over-gap' | '--gog-input-float-label-over-reserve' | '--gog-input-float-label-reserve' | '--gog-input-focus-border' | '--gog-input-focus-glow' | '--gog-input-focus-ring' | '--gog-input-focus-ring-offset' | '--gog-input-focus-ring-width' | '--gog-input-font' | '--gog-input-font-family' | '--gog-input-gap' | '--gog-input-icon-action-radius' | '--gog-input-icon-color' | '--gog-input-icon-focus-ring-width' | '--gog-input-icon-hover-color' | '--gog-input-icon-line-height' | '--gog-input-label-color' | '--gog-input-label-font-family' | '--gog-input-label-font-size' | '--gog-input-label-letter-spacing' | '--gog-input-label-line-height' | '--gog-input-label-text-transform' | '--gog-input-padding-x' | '--gog-input-padding-y' | '--gog-input-placeholder-color' | '--gog-input-radius' | '--gog-input-spin-hover-bg' | '--gog-input-spin-width' | '--gog-input-transition-duration' | '--gog-letter-spacing' | '--gog-line-height-loose' | '--gog-line-height-none' | '--gog-line-height-normal' | '--gog-line-height-relaxed' | '--gog-line-height-snug' | '--gog-line-height-tight' | '--gog-menu-available-height' | '--gog-menu-bg' | '--gog-menu-border-color' | '--gog-menu-border-style' | '--gog-menu-border-width' | '--gog-menu-focus-ring' | '--gog-menu-focus-ring-width' | '--gog-menu-font-family' | '--gog-menu-gap' | '--gog-menu-item-color' | '--gog-menu-item-disabled-color' | '--gog-menu-item-disabled-opacity' | '--gog-menu-item-font-size' | '--gog-menu-item-gap' | '--gog-menu-item-hover-bg' | '--gog-menu-item-hover-color' | '--gog-menu-item-icon-size' | '--gog-menu-item-line-height' | '--gog-menu-item-padding' | '--gog-menu-item-press-bg' | '--gog-menu-item-radius' | '--gog-menu-max-height' | '--gog-menu-max-width' | '--gog-menu-min-width' | '--gog-menu-padding' | '--gog-menu-panel-gap' | '--gog-menu-radius' | '--gog-menu-shadow' | '--gog-menu-transition-duration' | '--gog-menu-z' | '--gog-multiselect-actions-gap' | '--gog-multiselect-actions-inset' | '--gog-multiselect-arrow-icon-ratio' | '--gog-multiselect-arrow-transition-duration' | '--gog-multiselect-border-color' | '--gog-multiselect-checkbox-border' | '--gog-multiselect-checkbox-checked-bg' | '--gog-multiselect-clear-icon-ratio' | '--gog-multiselect-clear-line-height' | '--gog-multiselect-clear-radius' | '--gog-multiselect-controls-gap' | '--gog-multiselect-controls-padding' | '--gog-multiselect-disabled-opacity' | '--gog-multiselect-error-color' | '--gog-multiselect-error-font-size' | '--gog-multiselect-error-line-height' | '--gog-multiselect-error-offset' | '--gog-multiselect-field-bg' | '--gog-multiselect-field-border' | '--gog-multiselect-field-border-style' | '--gog-multiselect-field-border-width' | '--gog-multiselect-field-color' | '--gog-multiselect-filter-border-color' | '--gog-multiselect-filter-border-style' | '--gog-multiselect-filter-border-width' | '--gog-multiselect-filter-empty-color' | '--gog-multiselect-filter-empty-padding' | '--gog-multiselect-filter-input-bg' | '--gog-multiselect-filter-input-border' | '--gog-multiselect-filter-input-color' | '--gog-multiselect-filter-input-padding-x' | '--gog-multiselect-filter-input-padding-y' | '--gog-multiselect-filter-input-radius' | '--gog-multiselect-filter-padding' | '--gog-multiselect-float-label-in-top' | '--gog-multiselect-float-label-on-bg' | '--gog-multiselect-float-label-over-gap' | '--gog-multiselect-float-label-over-reserve' | '--gog-multiselect-float-label-reserve' | '--gog-multiselect-focus-border' | '--gog-multiselect-focus-glow' | '--gog-multiselect-focus-ring' | '--gog-multiselect-focus-ring-offset' | '--gog-multiselect-focus-ring-width' | '--gog-multiselect-font-family' | '--gog-multiselect-font-size' | '--gog-multiselect-gap' | '--gog-multiselect-label-color' | '--gog-multiselect-label-font-family' | '--gog-multiselect-label-font-size' | '--gog-multiselect-label-letter-spacing' | '--gog-multiselect-label-line-height' | '--gog-multiselect-label-text-transform' | '--gog-multiselect-mark-icon-ratio' | '--gog-multiselect-mark-size-ratio' | '--gog-multiselect-min-width' | '--gog-multiselect-option-color' | '--gog-multiselect-option-disabled-opacity' | '--gog-multiselect-option-gap' | '--gog-multiselect-option-gap-inline' | '--gog-multiselect-option-height' | '--gog-multiselect-option-hover-bg' | '--gog-multiselect-option-press-bg' | '--gog-multiselect-option-radius' | '--gog-multiselect-option-transition-duration' | '--gog-multiselect-options-padding' | '--gog-multiselect-overflow-color' | '--gog-multiselect-overflow-font-size' | '--gog-multiselect-overflow-gap' | '--gog-multiselect-overflow-line-height' | '--gog-multiselect-padding-x' | '--gog-multiselect-padding-y' | '--gog-multiselect-panel-bg' | '--gog-multiselect-panel-border' | '--gog-multiselect-panel-gap' | '--gog-multiselect-panel-max-height' | '--gog-multiselect-panel-max-width' | '--gog-multiselect-panel-radius' | '--gog-multiselect-panel-shadow' | '--gog-multiselect-placeholder-color' | '--gog-multiselect-radius' | '--gog-multiselect-transition-duration' | '--gog-multiselect-value-color' | '--gog-muted-text-color' | '--gog-paginator-ellipsis-color' | '--gog-paginator-ellipsis-font-family' | '--gog-paginator-ellipsis-font-size' | '--gog-paginator-ellipsis-line-height' | '--gog-paginator-ellipsis-min-width' | '--gog-paginator-gap' | '--gog-paginator-page-size-min-width' | '--gog-panel-bg' | '--gog-panel-border-color' | '--gog-panel-border-style' | '--gog-panel-border-width' | '--gog-panel-chevron-size' | '--gog-panel-color' | '--gog-panel-disabled-opacity' | '--gog-panel-elevated-bg' | '--gog-panel-elevated-border-color' | '--gog-panel-filled-bg' | '--gog-panel-filled-border-color' | '--gog-panel-filled-shadow' | '--gog-panel-focus-ring' | '--gog-panel-focus-ring-offset' | '--gog-panel-focus-ring-width' | '--gog-panel-font-family' | '--gog-panel-footer-border-color' | '--gog-panel-footer-gap' | '--gog-panel-footer-padding-top' | '--gog-panel-gap' | '--gog-panel-header-gap' | '--gog-panel-heading-color' | '--gog-panel-heading-font-family' | '--gog-panel-heading-font-size' | '--gog-panel-heading-font-weight' | '--gog-panel-heading-line-height' | '--gog-panel-lg-gap' | '--gog-panel-lg-padding-x' | '--gog-panel-lg-padding-y' | '--gog-panel-md-gap' | '--gog-panel-md-padding-x' | '--gog-panel-md-padding-y' | '--gog-panel-outlined-bg' | '--gog-panel-outlined-border-color' | '--gog-panel-outlined-shadow' | '--gog-panel-padding-x' | '--gog-panel-padding-y' | '--gog-panel-radius' | '--gog-panel-shadow' | '--gog-panel-slg-gap' | '--gog-panel-slg-padding-x' | '--gog-panel-slg-padding-y' | '--gog-panel-sm-gap' | '--gog-panel-sm-padding-x' | '--gog-panel-sm-padding-y' | '--gog-panel-toggle-color' | '--gog-panel-toggle-hover-bg' | '--gog-panel-toggle-radius' | '--gog-panel-toggle-size' | '--gog-panel-transition-duration' | '--gog-panel-xsm-gap' | '--gog-panel-xsm-padding-x' | '--gog-panel-xsm-padding-y' | '--gog-primary-color' | '--gog-progressbar-accent-bg' | '--gog-progressbar-accent-buffer-bg' | '--gog-progressbar-buffer-bg' | '--gog-progressbar-danger-bg' | '--gog-progressbar-danger-buffer-bg' | '--gog-progressbar-edge-backing-color' | '--gog-progressbar-edge-color' | '--gog-progressbar-edge-width' | '--gog-progressbar-fill-bg' | '--gog-progressbar-height' | '--gog-progressbar-indeterminate-duration' | '--gog-progressbar-indeterminate-easing' | '--gog-progressbar-info-bg' | '--gog-progressbar-info-buffer-bg' | '--gog-progressbar-lg-height' | '--gog-progressbar-md-height' | '--gog-progressbar-radius' | '--gog-progressbar-slg-height' | '--gog-progressbar-sm-height' | '--gog-progressbar-stripe-color' | '--gog-progressbar-stripe-size' | '--gog-progressbar-success-bg' | '--gog-progressbar-success-buffer-bg' | '--gog-progressbar-track-base-bg' | '--gog-progressbar-track-bg' | '--gog-progressbar-transition-duration' | '--gog-progressbar-value-color' | '--gog-progressbar-value-font-family' | '--gog-progressbar-value-font-size' | '--gog-progressbar-value-gap' | '--gog-progressbar-value-line-height' | '--gog-progressbar-value-min-width' | '--gog-progressbar-warning-bg' | '--gog-progressbar-warning-buffer-bg' | '--gog-progressbar-xsm-height' | '--gog-radio-bg' | '--gog-radio-border-color' | '--gog-radio-border-style' | '--gog-radio-border-width' | '--gog-radio-box-size' | '--gog-radio-checked-bg' | '--gog-radio-checked-border' | '--gog-radio-disabled-opacity' | '--gog-radio-dot-color' | '--gog-radio-dot-size-ratio' | '--gog-radio-error-color' | '--gog-radio-error-font-size' | '--gog-radio-error-line-height' | '--gog-radio-focus-ring' | '--gog-radio-focus-ring-offset' | '--gog-radio-focus-ring-width' | '--gog-radio-font-family' | '--gog-radio-gap' | '--gog-radio-group-gap' | '--gog-radio-group-label-color' | '--gog-radio-group-label-size' | '--gog-radio-group-option-gap' | '--gog-radio-group-option-gap-horizontal' | '--gog-radio-label-color' | '--gog-radio-label-line-height' | '--gog-radio-label-size' | '--gog-radio-padding' | '--gog-radio-transition-duration' | '--gog-radius' | '--gog-ripple-color' | '--gog-ripple-easing' | '--gog-ripple-enter-duration' | '--gog-ripple-exit-duration' | '--gog-ripple-opacity' | '--gog-scroll-corner-bg' | '--gog-scroll-fade-duration' | '--gog-scroll-focus-ring' | '--gog-scroll-focus-ring-width' | '--gog-scroll-normal-thumb-hit-padding' | '--gog-scroll-normal-thumb-min-size' | '--gog-scroll-normal-track-width' | '--gog-scroll-thin-thumb-hit-padding' | '--gog-scroll-thin-thumb-min-size' | '--gog-scroll-thin-track-width' | '--gog-scroll-thumb-active-bg' | '--gog-scroll-thumb-bg' | '--gog-scroll-thumb-hover-bg' | '--gog-scroll-thumb-inset' | '--gog-scroll-thumb-radius' | '--gog-scroll-track-bg' | '--gog-scroll-track-radius' | '--gog-secondary-color' | '--gog-select-chevron-color' | '--gog-select-chevron-icon-ratio' | '--gog-select-chevron-inset' | '--gog-select-clear-color' | '--gog-select-clear-gap' | '--gog-select-clear-hover-color' | '--gog-select-clear-icon-ratio' | '--gog-select-clear-line-height' | '--gog-select-clear-radius' | '--gog-select-control-font' | '--gog-select-control-gap' | '--gog-select-control-padding-x' | '--gog-select-control-padding-y' | '--gog-select-disabled-opacity' | '--gog-select-error-color' | '--gog-select-error-font-size' | '--gog-select-error-line-height' | '--gog-select-field-bg' | '--gog-select-field-border' | '--gog-select-field-border-style' | '--gog-select-field-border-width' | '--gog-select-field-color' | '--gog-select-filter-border-color' | '--gog-select-filter-border-style' | '--gog-select-filter-border-width' | '--gog-select-filter-empty-color' | '--gog-select-filter-empty-padding' | '--gog-select-filter-input-bg' | '--gog-select-filter-input-border' | '--gog-select-filter-input-color' | '--gog-select-filter-input-padding-x' | '--gog-select-filter-input-padding-y' | '--gog-select-filter-input-radius' | '--gog-select-filter-padding' | '--gog-select-float-label-in-top' | '--gog-select-float-label-on-bg' | '--gog-select-float-label-over-gap' | '--gog-select-float-label-over-reserve' | '--gog-select-float-label-reserve' | '--gog-select-focus-border' | '--gog-select-focus-glow' | '--gog-select-focus-ring' | '--gog-select-focus-ring-offset' | '--gog-select-focus-ring-width' | '--gog-select-font-family' | '--gog-select-gap' | '--gog-select-label-color' | '--gog-select-label-font-family' | '--gog-select-label-font-size' | '--gog-select-label-letter-spacing' | '--gog-select-label-line-height' | '--gog-select-label-text-transform' | '--gog-select-mark-icon-ratio' | '--gog-select-mark-size-ratio' | '--gog-select-min-width' | '--gog-select-option-color' | '--gog-select-option-disabled-opacity' | '--gog-select-option-gap' | '--gog-select-option-height' | '--gog-select-option-hover-bg' | '--gog-select-option-press-bg' | '--gog-select-option-radius' | '--gog-select-option-selected-color' | '--gog-select-option-transition-duration' | '--gog-select-options-padding' | '--gog-select-panel-bg' | '--gog-select-panel-gap' | '--gog-select-panel-max-height' | '--gog-select-panel-max-width' | '--gog-select-panel-radius' | '--gog-select-panel-shadow' | '--gog-select-placeholder-color' | '--gog-select-radius' | '--gog-select-transition-duration' | '--gog-skeleton-base' | '--gog-skeleton-circle-size-lg' | '--gog-skeleton-circle-size-md' | '--gog-skeleton-circle-size-slg' | '--gog-skeleton-circle-size-sm' | '--gog-skeleton-circle-size-xsm' | '--gog-skeleton-line-gap' | '--gog-skeleton-line-height-lg' | '--gog-skeleton-line-height-md' | '--gog-skeleton-line-height-slg' | '--gog-skeleton-line-height-sm' | '--gog-skeleton-line-height-xsm' | '--gog-skeleton-line-radius' | '--gog-skeleton-pulse-duration' | '--gog-skeleton-pulse-min-opacity' | '--gog-skeleton-radius' | '--gog-skeleton-rect-height-lg' | '--gog-skeleton-rect-height-md' | '--gog-skeleton-rect-height-slg' | '--gog-skeleton-rect-height-sm' | '--gog-skeleton-rect-height-xsm' | '--gog-skeleton-shine' | '--gog-skeleton-short-line-width' | '--gog-skeleton-square-radius' | '--gog-skeleton-wave-duration' | '--gog-slider-auto-width' | '--gog-slider-disabled-opacity' | '--gog-slider-error-color' | '--gog-slider-error-font-family' | '--gog-slider-error-font-size' | '--gog-slider-error-line-height' | '--gog-slider-fill-bg' | '--gog-slider-focus-ring' | '--gog-slider-focus-ring-width' | '--gog-slider-gap' | '--gog-slider-label-color' | '--gog-slider-label-font-family' | '--gog-slider-label-font-size' | '--gog-slider-label-letter-spacing' | '--gog-slider-label-line-height' | '--gog-slider-label-text-transform' | '--gog-slider-range-color' | '--gog-slider-range-font-size' | '--gog-slider-range-line-height' | '--gog-slider-thumb-bg' | '--gog-slider-thumb-border' | '--gog-slider-thumb-border-width' | '--gog-slider-thumb-glow-color' | '--gog-slider-thumb-glow-size' | '--gog-slider-thumb-radius' | '--gog-slider-thumb-size' | '--gog-slider-track-area-height' | '--gog-slider-track-bg' | '--gog-slider-track-border-color' | '--gog-slider-track-border-style' | '--gog-slider-track-border-width' | '--gog-slider-track-height' | '--gog-slider-track-radius' | '--gog-slider-value-color' | '--gog-slider-value-font-family' | '--gog-slider-value-font-size' | '--gog-slider-value-line-height' | '--gog-slider-value-min-width' | '--gog-slider-vertical-length' | '--gog-space-12' | '--gog-space-16' | '--gog-space-20' | '--gog-space-24' | '--gog-space-28' | '--gog-space-2xl' | '--gog-space-32' | '--gog-space-4' | '--gog-space-40' | '--gog-space-48' | '--gog-space-8' | '--gog-space-lg' | '--gog-space-md' | '--gog-space-sm' | '--gog-space-xs' | '--gog-spinner-arc-inner-color' | '--gog-spinner-arc-inner-dash' | '--gog-spinner-arc-inner-glow' | '--gog-spinner-arc-inner-opacity' | '--gog-spinner-arc-inner-pulse-opacity' | '--gog-spinner-arc-inner-stroke-compact' | '--gog-spinner-arc-outer-color' | '--gog-spinner-arc-outer-dash' | '--gog-spinner-arc-outer-glow' | '--gog-spinner-arc-outer-pulse-opacity' | '--gog-spinner-diamond-color' | '--gog-spinner-diamond-glow' | '--gog-spinner-diamond-opacity' | '--gog-spinner-glow-color' | '--gog-spinner-overlay-bg' | '--gog-spinner-overlay-blur' | '--gog-spinner-overlay-fade-duration' | '--gog-spinner-overlay-z' | '--gog-spinner-pulse-duration' | '--gog-spinner-ring-duration' | '--gog-spinner-ring-glow' | '--gog-spinner-ring-padding' | '--gog-spinner-rune-color' | '--gog-spinner-rune-font-family' | '--gog-spinner-rune-glow' | '--gog-spinner-rune-pulse-opacity' | '--gog-spinner-rune-size' | '--gog-spinner-size-lg' | '--gog-spinner-size-md' | '--gog-spinner-size-slg' | '--gog-spinner-size-sm' | '--gog-spinner-size-xsm' | '--gog-spinner-spin-duration' | '--gog-spinner-spin-slow-duration' | '--gog-spinner-ticks-duration' | '--gog-spinner-track-color' | '--gog-spinner-track-opacity' | '--gog-success-color' | '--gog-success-shade' | '--gog-success-text-color' | '--gog-surface-color' | '--gog-table-accent-bright' | '--gog-table-accent-color' | '--gog-table-border-color' | '--gog-table-border-style' | '--gog-table-border-width' | '--gog-table-empty-font-size' | '--gog-table-empty-line-height' | '--gog-table-empty-padding' | '--gog-table-focus-ring' | '--gog-table-focus-ring-width' | '--gog-table-font-family' | '--gog-table-footer-gap' | '--gog-table-footer-min-height' | '--gog-table-gap' | '--gog-table-header-bg' | '--gog-table-header-font-family' | '--gog-table-header-letter-spacing' | '--gog-table-header-text-transform' | '--gog-table-hover-bg' | '--gog-table-lg-padding-v' | '--gog-table-lg-td-font-size' | '--gog-table-lg-th-font-size' | '--gog-table-loading-opacity' | '--gog-table-loading-padding' | '--gog-table-md-padding-v' | '--gog-table-md-td-font-size' | '--gog-table-md-th-font-size' | '--gog-table-muted-color' | '--gog-table-num-col-width' | '--gog-table-num-font-size' | '--gog-table-num-line-height' | '--gog-table-numeric-font-family' | '--gog-table-padding-h' | '--gog-table-row-border-width' | '--gog-table-row-press-bg' | '--gog-table-row-transition-duration' | '--gog-table-select-col-width' | '--gog-table-selected-bg' | '--gog-table-slg-padding-v' | '--gog-table-slg-td-font-size' | '--gog-table-slg-th-font-size' | '--gog-table-sm-padding-v' | '--gog-table-sm-td-font-size' | '--gog-table-sm-th-font-size' | '--gog-table-sort-icon-opacity' | '--gog-table-sort-icon-size' | '--gog-table-sort-icon-width' | '--gog-table-surface' | '--gog-table-td-font-size' | '--gog-table-td-line-height' | '--gog-table-td-padding-v' | '--gog-table-text-color' | '--gog-table-th-font-size' | '--gog-table-th-inner-gap' | '--gog-table-th-line-height' | '--gog-table-th-padding-v' | '--gog-table-total-font-size' | '--gog-table-total-letter-spacing' | '--gog-table-total-line-height' | '--gog-table-total-text-transform' | '--gog-table-xsm-padding-v' | '--gog-table-xsm-td-font-size' | '--gog-table-xsm-th-font-size' | '--gog-tabs-active-color' | '--gog-tabs-disabled-opacity' | '--gog-tabs-focus-ring-color' | '--gog-tabs-focus-ring-offset' | '--gog-tabs-focus-ring-width' | '--gog-tabs-font-family' | '--gog-tabs-font-weight' | '--gog-tabs-gap' | '--gog-tabs-header-border-color' | '--gog-tabs-header-border-style' | '--gog-tabs-header-border-width' | '--gog-tabs-hover-color' | '--gog-tabs-icon-size' | '--gog-tabs-indicator-color' | '--gog-tabs-indicator-radius' | '--gog-tabs-indicator-thickness' | '--gog-tabs-letter-spacing' | '--gog-tabs-lg-font-size' | '--gog-tabs-lg-padding' | '--gog-tabs-line-height' | '--gog-tabs-md-font-size' | '--gog-tabs-md-padding' | '--gog-tabs-panel-color' | '--gog-tabs-panel-padding' | '--gog-tabs-press-bg' | '--gog-tabs-press-color' | '--gog-tabs-rest-bg' | '--gog-tabs-rest-color' | '--gog-tabs-slg-font-size' | '--gog-tabs-slg-padding' | '--gog-tabs-sm-font-size' | '--gog-tabs-sm-padding' | '--gog-tabs-tab-bg' | '--gog-tabs-tab-color' | '--gog-tabs-tab-font-size' | '--gog-tabs-tab-gap' | '--gog-tabs-tab-padding' | '--gog-tabs-text-transform' | '--gog-tabs-transition-duration' | '--gog-tabs-xsm-font-size' | '--gog-tabs-xsm-padding' | '--gog-tag-accent' | '--gog-tag-bg' | '--gog-tag-bg-base' | '--gog-tag-bg-mix' | '--gog-tag-border' | '--gog-tag-border-style' | '--gog-tag-border-width' | '--gog-tag-color' | '--gog-tag-color-base' | '--gog-tag-color-mix' | '--gog-tag-danger-color' | '--gog-tag-default-color' | '--gog-tag-font-family' | '--gog-tag-font-size' | '--gog-tag-font-weight' | '--gog-tag-gap' | '--gog-tag-icon-size' | '--gog-tag-info-color' | '--gog-tag-inner-border-width' | '--gog-tag-lg-font-size' | '--gog-tag-lg-gap' | '--gog-tag-lg-icon-size' | '--gog-tag-lg-padding-block' | '--gog-tag-lg-padding-inline' | '--gog-tag-line-height' | '--gog-tag-md-font-size' | '--gog-tag-md-gap' | '--gog-tag-md-icon-size' | '--gog-tag-md-padding-block' | '--gog-tag-md-padding-inline' | '--gog-tag-padding-block' | '--gog-tag-padding-inline' | '--gog-tag-pill-radius' | '--gog-tag-radius' | '--gog-tag-slg-font-size' | '--gog-tag-slg-gap' | '--gog-tag-slg-icon-size' | '--gog-tag-slg-padding-block' | '--gog-tag-slg-padding-inline' | '--gog-tag-sm-font-size' | '--gog-tag-sm-gap' | '--gog-tag-sm-icon-size' | '--gog-tag-sm-padding-block' | '--gog-tag-sm-padding-inline' | '--gog-tag-success-color' | '--gog-tag-warning-color' | '--gog-tag-xsm-font-size' | '--gog-tag-xsm-gap' | '--gog-tag-xsm-icon-size' | '--gog-tag-xsm-padding-block' | '--gog-tag-xsm-padding-inline' | '--gog-text-2xl' | '--gog-text-2xs' | '--gog-text-3xl' | '--gog-text-color' | '--gog-text-lg' | '--gog-text-md' | '--gog-text-slg' | '--gog-text-sm' | '--gog-text-transform' | '--gog-text-xl' | '--gog-text-xs' | '--gog-textarea-clear-icon-ratio' | '--gog-textarea-line-height' | '--gog-textarea-resize-grip-color' | '--gog-textarea-resize-grip-offset' | '--gog-textarea-resize-grip-opacity' | '--gog-textarea-resize-grip-size' | '--gog-textarea-resize-grip-stripe-gap' | '--gog-textarea-resize-grip-stripe-width' | '--gog-textarea-resize-inset-bottom' | '--gog-textarea-resize-inset-right' | '--gog-textarea-scrollbar-width' | '--gog-toast-accent-width' | '--gog-toast-action-font-size' | '--gog-toast-action-line-height' | '--gog-toast-action-padding' | '--gog-toast-actions-gap' | '--gog-toast-base-z' | '--gog-toast-bg' | '--gog-toast-border' | '--gog-toast-close-font-size' | '--gog-toast-close-line-height' | '--gog-toast-close-padding' | '--gog-toast-color' | '--gog-toast-content-gap' | '--gog-toast-enter-distance' | '--gog-toast-error-color' | '--gog-toast-font-family' | '--gog-toast-icon-color' | '--gog-toast-icon-line-height' | '--gog-toast-icon-size' | '--gog-toast-info-color' | '--gog-toast-main-gap' | '--gog-toast-max-width' | '--gog-toast-message-font-size' | '--gog-toast-message-line-height' | '--gog-toast-min-width' | '--gog-toast-padding' | '--gog-toast-progress-height' | '--gog-toast-progress-opacity' | '--gog-toast-radius' | '--gog-toast-shadow' | '--gog-toast-stack-expanded-gap' | '--gog-toast-stack-max-depth' | '--gog-toast-stack-min-opacity' | '--gog-toast-stack-opacity-step' | '--gog-toast-stack-padding' | '--gog-toast-stack-peek' | '--gog-toast-stack-scale-step' | '--gog-toast-success-color' | '--gog-toast-transition-duration' | '--gog-toast-warning-color' | '--gog-toast-z-index' | '--gog-toggle-border-color' | '--gog-toggle-border-style' | '--gog-toggle-border-width' | '--gog-toggle-disabled-opacity' | '--gog-toggle-focus-ring-color' | '--gog-toggle-focus-ring-offset' | '--gog-toggle-focus-ring-width' | '--gog-toggle-font-family' | '--gog-toggle-gap' | '--gog-toggle-label-color' | '--gog-toggle-label-line-height' | '--gog-toggle-lg-label-size' | '--gog-toggle-lg-state-font-size' | '--gog-toggle-lg-thumb-size' | '--gog-toggle-lg-track-height' | '--gog-toggle-lg-track-width' | '--gog-toggle-md-label-size' | '--gog-toggle-md-state-font-size' | '--gog-toggle-md-thumb-size' | '--gog-toggle-md-track-height' | '--gog-toggle-md-track-width' | '--gog-toggle-on-border-color' | '--gog-toggle-padding' | '--gog-toggle-radius' | '--gog-toggle-slg-label-size' | '--gog-toggle-slg-state-font-size' | '--gog-toggle-slg-thumb-size' | '--gog-toggle-slg-track-height' | '--gog-toggle-slg-track-width' | '--gog-toggle-sm-label-size' | '--gog-toggle-sm-state-font-size' | '--gog-toggle-sm-thumb-size' | '--gog-toggle-sm-track-height' | '--gog-toggle-sm-track-width' | '--gog-toggle-state-color' | '--gog-toggle-state-font-weight' | '--gog-toggle-state-letter-spacing' | '--gog-toggle-state-line-height' | '--gog-toggle-state-offset' | '--gog-toggle-state-padding-inline' | '--gog-toggle-state-thumb-gap' | '--gog-toggle-thumb-bg' | '--gog-toggle-thumb-inset' | '--gog-toggle-thumb-off-bg' | '--gog-toggle-thumb-on-bg' | '--gog-toggle-thumb-shadow' | '--gog-toggle-track-bg' | '--gog-toggle-track-off-bg' | '--gog-toggle-track-on-bg' | '--gog-toggle-transition-duration' | '--gog-toggle-xsm-label-size' | '--gog-toggle-xsm-state-font-size' | '--gog-toggle-xsm-thumb-size' | '--gog-toggle-xsm-track-height' | '--gog-toggle-xsm-track-width' | '--gog-tooltip-arrow-size' | '--gog-tooltip-bg' | '--gog-tooltip-border-color' | '--gog-tooltip-border-style' | '--gog-tooltip-border-width' | '--gog-tooltip-color' | '--gog-tooltip-font-family' | '--gog-tooltip-font-size' | '--gog-tooltip-gap' | '--gog-tooltip-line-height' | '--gog-tooltip-max-height' | '--gog-tooltip-max-width' | '--gog-tooltip-padding' | '--gog-tooltip-radius' | '--gog-tooltip-shadow' | '--gog-tooltip-transition-duration' | '--gog-tooltip-z' | '--gog-warning-color' | '--gog-warning-shade' | '--gog-warning-text-color' | '--gog-z-base';
|
|
1690
|
+
/** Which of the three layers a token belongs to — see the README's theming section. */
|
|
1691
|
+
type GogTokenLayer = 'foundation' | 'component' | 'instance';
|
|
1692
|
+
interface GogTokenGroup {
|
|
1693
|
+
/** The `── Section ──` heading the token sits under in theme.css. */
|
|
1694
|
+
readonly section: string;
|
|
1695
|
+
readonly layer: GogTokenLayer;
|
|
1696
|
+
readonly tokens: readonly GogTokenName[];
|
|
1697
|
+
}
|
|
1698
|
+
/**
|
|
1699
|
+
* The token catalogue, grouped as theme.css groups it. Exported so a theme editor or docs page
|
|
1700
|
+
* can enumerate the real tokens instead of keeping a hand-copied list that drifts.
|
|
1701
|
+
*/
|
|
1702
|
+
declare const GOG_TOKEN_GROUPS: readonly GogTokenGroup[];
|
|
1703
|
+
|
|
1704
|
+
/**
|
|
1705
|
+
* Reading a `--gog-*` token's value from TypeScript, safely.
|
|
1706
|
+
*
|
|
1707
|
+
* **The trap these exist for.** `getComputedStyle(el).getPropertyValue('--x')` returns a custom
|
|
1708
|
+
* property's *specified* value, not a used one. A token declared
|
|
1709
|
+
* `calc(10px * var(--gog-density))` comes back as that whole string, and `Number.parseFloat` on
|
|
1710
|
+
* it returns `NaN` — which every caller in this library turned into its fallback, silently. No
|
|
1711
|
+
* error, no failing test, just a component quietly using the wrong number.
|
|
1712
|
+
*
|
|
1713
|
+
* That is not hypothetical. 21.7.0's density scale (`docs/themes.md` iteration 6) made 178
|
|
1714
|
+
* tokens `calc(<n>px * var(--gog-density))`; none of the three tokens read from TypeScript were
|
|
1715
|
+
* among them, but nothing stopped the next one from being. And a **consumer** can write
|
|
1716
|
+
* `--gog-scroll-thumb-min-size: calc(2rem + 4px)` in their own theme at any time, which no
|
|
1717
|
+
* repo-side check could ever catch.
|
|
1718
|
+
*
|
|
1719
|
+
* So these resolve rather than parse: the fast path still handles a plain `12px`, and anything
|
|
1720
|
+
* else is handed to the browser on a throwaway element, which also gets `rem`, `em`, `%` and
|
|
1721
|
+
* nested `var()` right for free. The probe is appended to the element the token was read from,
|
|
1722
|
+
* so relative units resolve against the same context the real value would.
|
|
1723
|
+
*/
|
|
1724
|
+
/** Reads a length token in pixels. Returns `fallback` for an empty, unparseable or zero value. */
|
|
1725
|
+
declare function resolveLengthToken(el: HTMLElement, token: string, fallback: number): number;
|
|
1726
|
+
/**
|
|
1727
|
+
* Reads a unitless numeric token (a `z-index`). Same resolution path as lengths — `z-index`
|
|
1728
|
+
* takes an integer, so the browser computes `calc()` there too.
|
|
1729
|
+
*/
|
|
1730
|
+
declare function resolveNumberToken(el: HTMLElement, token: string, fallback: number): number;
|
|
1731
|
+
|
|
1732
|
+
type GogTooltipSide = 'top' | 'bottom' | 'left' | 'right';
|
|
1733
|
+
/** Writing direction of the element a placement is resolved against. */
|
|
1734
|
+
type GogWritingDirection = 'ltr' | 'rtl';
|
|
1735
|
+
interface GogTooltipTargetRect {
|
|
1736
|
+
top: number;
|
|
1737
|
+
bottom: number;
|
|
1738
|
+
left: number;
|
|
1739
|
+
right: number;
|
|
1740
|
+
width: number;
|
|
1741
|
+
height: number;
|
|
1742
|
+
}
|
|
1743
|
+
interface GogTooltipSize {
|
|
1744
|
+
width: number;
|
|
1745
|
+
height: number;
|
|
1746
|
+
}
|
|
1747
|
+
interface GogTooltipViewport {
|
|
1748
|
+
width: number;
|
|
1749
|
+
height: number;
|
|
1750
|
+
}
|
|
1751
|
+
interface GogTooltipPlacement {
|
|
1752
|
+
side: GogTooltipSide;
|
|
1753
|
+
top: number;
|
|
1754
|
+
left: number;
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Picks which side the bubble renders on. `'auto'` tries top, bottom, then the two horizontal
|
|
1758
|
+
* sides — top-first matches the conventional tooltip default — and falls back to whichever
|
|
1759
|
+
* side has the most room if the bubble doesn't fully fit anywhere. An explicit side flips to
|
|
1760
|
+
* its opposite when it has no room but the opposite does, same as `resolveDropdownDirection`
|
|
1761
|
+
* does for up/down; unlike that helper, a request for a specific side is honoured as-is
|
|
1762
|
+
* (no flip) whenever it fits, since the caller asked for that side deliberately.
|
|
1763
|
+
*
|
|
1764
|
+
* **`direction` mirrors the horizontal preference only.** In RTL, `'auto'` prefers the left
|
|
1765
|
+
* side where LTR prefers the right, so a tooltip opens away from the text it belongs to in the
|
|
1766
|
+
* same way in both. An explicit `'left'`/`'right'` is *not* mirrored: those are physical words
|
|
1767
|
+
* in a physical API, and a consumer who wrote `position="right"` meant the right of the screen.
|
|
1768
|
+
* `'start'`/`'end'` would be the logical spelling, and there is deliberately no such value —
|
|
1769
|
+
* `'auto'` already does the right thing for a direction-aware layout.
|
|
1770
|
+
*/
|
|
1771
|
+
declare function resolveTooltipSide(position: GogTooltipPosition, target: GogTooltipTargetRect, bubble: GogTooltipSize, viewport: GogTooltipViewport, gap?: number, viewportPadding?: number, direction?: GogWritingDirection): GogTooltipSide;
|
|
1772
|
+
/**
|
|
1773
|
+
* Resolves the bubble's side and its `position: fixed` top/left, centered on the target's
|
|
1774
|
+
* midpoint along the cross axis and clamped so it never renders past the viewport edge —
|
|
1775
|
+
* the centering can push it there for a target near a corner.
|
|
1776
|
+
*/
|
|
1777
|
+
declare function resolveTooltipPlacement(position: GogTooltipPosition, target: GogTooltipTargetRect, bubble: GogTooltipSize, viewport: GogTooltipViewport, gap?: number, viewportPadding?: number, direction?: GogWritingDirection): GogTooltipPlacement;
|
|
1778
|
+
|
|
1779
|
+
/** The slice of a list that is worth rendering, as indices into the full list. */
|
|
1780
|
+
interface GogVirtualRange {
|
|
1781
|
+
/** First index to render, inclusive. */
|
|
1782
|
+
readonly start: number;
|
|
1783
|
+
/** One past the last index to render, so `slice(start, end)` is the window. */
|
|
1784
|
+
readonly end: number;
|
|
1785
|
+
}
|
|
1786
|
+
/** What `GogVirtualWindow` needs to do its arithmetic. Every input is a signal it reads. */
|
|
1787
|
+
interface GogVirtualWindowInputs {
|
|
1788
|
+
/** How many rows the list has in total — not how many are rendered. */
|
|
1789
|
+
readonly count: Signal<number>;
|
|
1790
|
+
/**
|
|
1791
|
+
* One row's height in px, measured rather than assumed.
|
|
1792
|
+
*
|
|
1793
|
+
* The library learned this the expensive way: `--gog-*-option-height` described itself as an
|
|
1794
|
+
* estimate and was wrong in all eleven shipped themes, low by up to 8.38px a row, while a
|
|
1795
|
+
* placement decision depended on it. A window is far less forgiving than a placement — the error
|
|
1796
|
+
* accumulates once per row — so this takes a number that came from `getBoundingClientRect`.
|
|
1797
|
+
*/
|
|
1798
|
+
readonly rowHeight: Signal<number>;
|
|
1799
|
+
/** The scroller's visible height in px. Not the panel's max-height, which is a cap. */
|
|
1800
|
+
readonly viewportHeight: Signal<number>;
|
|
1801
|
+
/** How far the scroller is scrolled, in px. */
|
|
1802
|
+
readonly scrollTop: Signal<number>;
|
|
1803
|
+
/**
|
|
1804
|
+
* Rows rendered beyond each edge of the viewport, so a scroll does not expose blank space
|
|
1805
|
+
* before the next frame lands. Defaults to 4.
|
|
1806
|
+
*/
|
|
1807
|
+
readonly overscan?: number;
|
|
1808
|
+
}
|
|
1809
|
+
/**
|
|
1810
|
+
* The arithmetic behind a windowed list, and nothing else.
|
|
1811
|
+
*
|
|
1812
|
+
* **No DOM, no template, no scroll listener.** The component that uses this owns its scroller —
|
|
1813
|
+
* the three dropdowns already own theirs (a `gog-scroll` inside the panel) and `gog-table` owns a
|
|
1814
|
+
* different one — so a primitive that grabbed an element would have to be told which, and would
|
|
1815
|
+
* still not know when that element moved. It reads signals and returns numbers; everything that
|
|
1816
|
+
* touches the page stays in the component.
|
|
1817
|
+
*
|
|
1818
|
+
* That is the same division `GogRippleController` made, for the reason `docs/ripple.md` records:
|
|
1819
|
+
* the engine lives beside the components rather than inside a directive, because the things that
|
|
1820
|
+
* need it cannot all reach a directive through `hostDirectives`.
|
|
1821
|
+
*
|
|
1822
|
+
* ```ts
|
|
1823
|
+
* private readonly window = new GogVirtualWindow({
|
|
1824
|
+
* count: computed(() => this.visibleOptions().length),
|
|
1825
|
+
* rowHeight: this.measuredRowHeight,
|
|
1826
|
+
* viewportHeight: this.panelHeight,
|
|
1827
|
+
* scrollTop: this.panelScrollTop,
|
|
1828
|
+
* });
|
|
1829
|
+
* // window.range() -> { start, end }
|
|
1830
|
+
* // window.padBefore() / window.padEnd() -> px of filler above and below the rendered slice
|
|
1831
|
+
* ```
|
|
1832
|
+
*
|
|
1833
|
+
* **Why padding rather than absolute positioning.** A spacer above and below the rendered rows
|
|
1834
|
+
* keeps the list in normal flow, so the rows stay `display: flex` children of the same container
|
|
1835
|
+
* and every selector, gap and `:last-child` a component already relies on keeps working. Absolute
|
|
1836
|
+
* positioning each row is the other common shape and it changes what the component's own CSS
|
|
1837
|
+
* means, which is a large price for a list that is one column wide.
|
|
1838
|
+
*
|
|
1839
|
+
* @see docs/virtualization.md — the plan, its measurements, and what still has to be got right in
|
|
1840
|
+
* the components that adopt this (ARIA counts, keyboard reach, panel height, filter resets).
|
|
1841
|
+
*/
|
|
1842
|
+
declare class GogVirtualWindow {
|
|
1843
|
+
private readonly inputs;
|
|
1844
|
+
private readonly overscan;
|
|
1845
|
+
constructor(inputs: GogVirtualWindowInputs);
|
|
1846
|
+
/**
|
|
1847
|
+
* The total height the list would have if every row were rendered. What the scroller needs to
|
|
1848
|
+
* believe so its thumb is the right size and `scrollTop` spans the whole list.
|
|
1849
|
+
*/
|
|
1850
|
+
readonly totalHeight: Signal<number>;
|
|
1851
|
+
/**
|
|
1852
|
+
* The slice worth rendering.
|
|
1853
|
+
*
|
|
1854
|
+
* **Degrades to the whole list rather than to nothing.** A row height of zero is what a caller
|
|
1855
|
+
* has before it has measured anything, and a window computed from it would be `{0, 0}` — an
|
|
1856
|
+
* empty panel that looks like a bug and hides the data. Rendering everything is the pre-window
|
|
1857
|
+
* behaviour, which is slow and correct; that is the right way round for a fallback.
|
|
1858
|
+
*/
|
|
1859
|
+
readonly range: Signal<GogVirtualRange>;
|
|
1860
|
+
/** Filler above the rendered slice, in px, so the rows sit where their indices say. */
|
|
1861
|
+
readonly padBefore: Signal<number>;
|
|
1862
|
+
/** Filler below, in px. Derived from the total so rounding cannot leave a gap. */
|
|
1863
|
+
readonly padAfter: Signal<number>;
|
|
1864
|
+
/**
|
|
1865
|
+
* Where the scroller has to be for `index` to be fully visible, or `null` if it already is.
|
|
1866
|
+
*
|
|
1867
|
+
* This is what keyboard navigation needs: arrowing to a row outside the window is a scroll
|
|
1868
|
+
* first and a focus move second, because the element does not exist until the scroll has
|
|
1869
|
+
* re-rendered the slice. Returning `null` rather than the current position lets a caller skip
|
|
1870
|
+
* the scroll entirely, which matters because scrolling when nothing needs to move cancels a
|
|
1871
|
+
* user's own in-progress scroll in some browsers.
|
|
1872
|
+
*/
|
|
1873
|
+
scrollOffsetFor(index: number): number | null;
|
|
1874
|
+
/** Guards against the shapes a caller can legitimately be in before it has measured anything. */
|
|
1875
|
+
private safeCount;
|
|
1876
|
+
private safeRowHeight;
|
|
1877
|
+
private safeViewportHeight;
|
|
1878
|
+
private safeScrollTop;
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
/** What `GogVariableWindow` needs. Every input is a signal it reads. */
|
|
1882
|
+
interface GogVariableWindowInputs {
|
|
1883
|
+
/** How many rows the list has in total — not how many are rendered. */
|
|
1884
|
+
readonly count: Signal<number>;
|
|
1885
|
+
/**
|
|
1886
|
+
* The height in px to assume for a row that has never been measured.
|
|
1887
|
+
*
|
|
1888
|
+
* Seeded from the first row the caller measures rather than from a token: `gog-table`'s row
|
|
1889
|
+
* height is padding plus leading plus a border, and `docs/virtualization.md`'s iteration 0
|
|
1890
|
+
* measured the equivalent token wrong in all eleven shipped themes.
|
|
1891
|
+
*/
|
|
1892
|
+
readonly estimatedRowHeight: Signal<number>;
|
|
1893
|
+
/** The scroller's visible height in px. Not a `max-height`, which is a cap. */
|
|
1894
|
+
readonly viewportHeight: Signal<number>;
|
|
1895
|
+
/** How far the scroller is scrolled, in px. */
|
|
1896
|
+
readonly scrollTop: Signal<number>;
|
|
1897
|
+
/**
|
|
1898
|
+
* Rows rendered beyond each edge of the viewport, so a scroll does not expose blank space
|
|
1899
|
+
* before the next frame lands. Defaults to 4.
|
|
1900
|
+
*/
|
|
1901
|
+
readonly overscan?: number;
|
|
1902
|
+
}
|
|
1903
|
+
/**
|
|
1904
|
+
* The arithmetic behind a windowed list whose rows are **not** all the same height.
|
|
1905
|
+
*
|
|
1906
|
+
* `GogVirtualWindow` is the one to reach for wherever the rows are uniform — it is exact, it needs
|
|
1907
|
+
* no measurements, and a multiplication beats a binary search. This exists because `gog-table`
|
|
1908
|
+
* cannot use it, for a reason that is a fact about CSS rather than about this library:
|
|
1909
|
+
*
|
|
1910
|
+
* **A table row's height cannot be pinned.** `height` on a `<tr>` or a `<td>` is a *minimum* in
|
|
1911
|
+
* table layout, so a cell whose content wraps makes its row taller and nothing can stop it — one
|
|
1912
|
+
* cell taken from 40 to 600 characters measured 39px → 173.75px, under `table-layout: fixed`, with
|
|
1913
|
+
* the column width unchanged. A single-pitch window cannot describe that list.
|
|
1914
|
+
*
|
|
1915
|
+
* So this holds a height per row: the measured one where a row has ever been rendered, and an
|
|
1916
|
+
* estimate everywhere else. `range()` is then a binary search over the prefix sums rather than a
|
|
1917
|
+
* division.
|
|
1918
|
+
*
|
|
1919
|
+
* **Being an estimate is the whole difficulty**, and it lives in `applyMeasurements`, not here.
|
|
1920
|
+
* See its note: correcting a row *above* the viewport moves everything below it, under the reader,
|
|
1921
|
+
* while they scroll — so the correction has to be reported as a scroll delta and applied in the
|
|
1922
|
+
* same frame.
|
|
1923
|
+
*
|
|
1924
|
+
* @see docs/table-virtualization.md — the survey behind this, and what adopting it has to get right
|
|
1925
|
+
*/
|
|
1926
|
+
declare class GogVariableWindow {
|
|
1927
|
+
private readonly inputs;
|
|
1928
|
+
private readonly overscan;
|
|
1929
|
+
/**
|
|
1930
|
+
* Measured heights by row index, `undefined` where a row has never been rendered.
|
|
1931
|
+
*
|
|
1932
|
+
* A sparse array rather than a `Map`: the index *is* the key, the reads are sequential, and the
|
|
1933
|
+
* prefix sum walks it start to finish either way.
|
|
1934
|
+
*/
|
|
1935
|
+
private readonly measured;
|
|
1936
|
+
constructor(inputs: GogVariableWindowInputs);
|
|
1937
|
+
/**
|
|
1938
|
+
* Running total of row heights: `offsets[i]` is where row `i` starts, and `offsets[count]` is
|
|
1939
|
+
* the list's full height.
|
|
1940
|
+
*
|
|
1941
|
+
* Recomputed whole rather than patched. It is O(n) on a signal that changes only when a
|
|
1942
|
+
* measurement actually differs, and an incremental structure here would be a Fenwick tree
|
|
1943
|
+
* guarding a loop that costs a fraction of the layout it feeds.
|
|
1944
|
+
*/
|
|
1945
|
+
private readonly offsets;
|
|
1946
|
+
/** What the scroller has to believe, so its thumb is the right size. */
|
|
1947
|
+
readonly totalHeight: Signal<number>;
|
|
1948
|
+
/**
|
|
1949
|
+
* The slice worth rendering.
|
|
1950
|
+
*
|
|
1951
|
+
* **Degrades to the whole list rather than to nothing**, the same rule `GogVirtualWindow`
|
|
1952
|
+
* follows and for the same reason: a caller that has not measured anything yet has an estimate
|
|
1953
|
+
* of zero, and a window computed from it renders an empty panel that reads as broken. Rendering
|
|
1954
|
+
* everything is the pre-window behaviour — slow, and correct.
|
|
1955
|
+
*/
|
|
1956
|
+
readonly range: Signal<GogVirtualRange>;
|
|
1957
|
+
/** Filler above the rendered slice, in px, so the rows sit where their offsets say. */
|
|
1958
|
+
readonly padBefore: Signal<number>;
|
|
1959
|
+
/** Filler below, in px. Derived from the total so rounding cannot leave a gap. */
|
|
1960
|
+
readonly padAfter: Signal<number>;
|
|
1961
|
+
/**
|
|
1962
|
+
* Records what the rendered rows actually measured, and returns **how far the content above the
|
|
1963
|
+
* viewport moved** — which the caller must add to the scroller's `scrollTop`, in the same frame.
|
|
1964
|
+
*
|
|
1965
|
+
* This is the whole difficulty of a variable window, and it is not obvious from the arithmetic.
|
|
1966
|
+
* Every row starts as an estimate. The moment one is measured and disagrees, every row after it
|
|
1967
|
+
* shifts by the difference — including, when the corrected row is *above* the viewport, the rows
|
|
1968
|
+
* the reader is looking at. Scroll up into a region of taller-than-estimated rows and the
|
|
1969
|
+
* content jumps away from the pointer on every frame; the list appears to fight the scroll.
|
|
1970
|
+
*
|
|
1971
|
+
* Adding the delta back to `scrollTop` holds the visible rows still: the content moved down by
|
|
1972
|
+
* `delta`, so the viewport moves down by `delta` too and the same pixels stay under the reader.
|
|
1973
|
+
*
|
|
1974
|
+
* `heights[i]` is keyed by **real row index**, and a `0` or a negative is ignored rather than
|
|
1975
|
+
* stored — an unlaid-out row measures zero, and believing it would collapse the list.
|
|
1976
|
+
*/
|
|
1977
|
+
applyMeasurements(heights: ReadonlyMap<number, number>): number;
|
|
1978
|
+
/**
|
|
1979
|
+
* Drops every measurement. For when the rows stop describing the same data — a new page, a new
|
|
1980
|
+
* sort, a filter — after which a cached height belongs to a row that is no longer there.
|
|
1981
|
+
*/
|
|
1982
|
+
reset(): void;
|
|
1983
|
+
/** Where the scroller has to be for `index` to be fully visible, or `null` if it already is. */
|
|
1984
|
+
scrollOffsetFor(index: number): number | null;
|
|
1985
|
+
/**
|
|
1986
|
+
* The index of the row containing `position`, by binary search over the offsets.
|
|
1987
|
+
*
|
|
1988
|
+
* Returns the **last** index whose start is `<= position`, clamped to the list. A linear scan
|
|
1989
|
+
* would be fine at a thousand rows and is not at the hundreds of thousands this exists for, and
|
|
1990
|
+
* it runs on every scroll frame.
|
|
1991
|
+
*/
|
|
1992
|
+
private indexAt;
|
|
1993
|
+
private safeCount;
|
|
1994
|
+
private safeEstimate;
|
|
1995
|
+
private safeViewportHeight;
|
|
1996
|
+
private safeScrollTop;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
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 };
|
|
2000
|
+
export type { GogAlertLive, GogAriaHasPopup, GogBadgePosition, GogBuiltinIconName, GogButtonToggleAppearance, GogCheckableControlSizeConfig, GogDateRange, GogDateSelectionMode, GogDeprecation, GogDeprecationKind, GogDividerVariant, GogDropdownDirection, GogDropdownFilterPosition, GogDropdownOption, GogDropdownOptionContext, GogDropdownPlacement, GogDropdownTriggerRect, GogErrorDisplay, GogFloatLabelVariant, GogGlobalConfig, GogHourFormat, GogIconName, GogInputMode, GogInputType, GogOptionAccessor, GogOrientation, GogPaginatorRangeMode, GogProgressbarMode, GogProgressbarVariant, GogScrollAxis, GogScrollOverscrollBehavior, GogScrollSize, GogSeverity, GogSize, GogSkeletonAnimation, GogSkeletonShape, GogSliderOrientation, GogSliderRange, GogSpinnerVariant, GogSurfaceVariant, GogTabsAlign, GogTagShape, GogTagVariant, GogTextareaResize, GogTokenGroup, GogTokenLayer, GogTokenName, GogTooltipPlacement, GogTooltipPosition, GogTooltipSide, GogTooltipSize, GogTooltipTargetRect, GogTooltipViewport, GogVariableWindowInputs, GogVariant, GogVirtualRange, GogVirtualWindowInputs, GogWritingDirection, RovingFocusKey, ToastPosition };
|