@egose/shadcn-theme-ng 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -1
- package/autocomplete/fesm2022/autocomplete.mjs +14 -4
- package/autocomplete/types/autocomplete.d.ts +4 -1
- package/checkbox/README.md +6 -2
- package/checkbox/fesm2022/checkbox.mjs +11 -7
- package/checkbox/types/checkbox.d.ts +6 -4
- package/combobox/fesm2022/combobox.mjs +13 -7
- package/combobox/types/combobox.d.ts +8 -2
- package/data-table/README.md +11 -1
- package/data-table/fesm2022/data-table.mjs +64 -18
- package/data-table/types/data-table.d.ts +25 -6
- package/date-picker/README.md +41 -9
- package/date-picker/fesm2022/date-picker.mjs +265 -59
- package/date-picker/types/date-picker.d.ts +58 -27
- package/form-autocomplete/README.md +4 -2
- package/form-autocomplete/fesm2022/form-autocomplete.mjs +19 -6
- package/form-autocomplete/types/form-autocomplete.d.ts +9 -3
- package/form-combobox/README.md +4 -2
- package/form-combobox/fesm2022/form-combobox.mjs +23 -6
- package/form-combobox/types/form-combobox.d.ts +10 -3
- package/form-input-otp/README.md +7 -5
- package/form-input-otp/fesm2022/form-input-otp.mjs +20 -8
- package/form-input-otp/types/form-input-otp.d.ts +9 -3
- package/form-searchable-multiselect/README.md +15 -1
- package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +15 -3
- package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +9 -1
- package/form-slider/README.md +3 -1
- package/form-slider/fesm2022/form-slider.mjs +19 -6
- package/form-slider/types/form-slider.d.ts +9 -3
- package/form-toggle/README.md +22 -18
- package/form-toggle/fesm2022/form-toggle.mjs +22 -9
- package/form-toggle/types/form-toggle.d.ts +6 -2
- package/input-otp/README.md +33 -9
- package/input-otp/fesm2022/input-otp.mjs +127 -11
- package/input-otp/types/input-otp.d.ts +24 -4
- package/package.json +1 -1
- package/pagination/README.md +21 -12
- package/pagination/fesm2022/pagination.mjs +73 -65
- package/pagination/types/pagination.d.ts +21 -8
- package/phone-input/README.md +16 -0
- package/phone-input/fesm2022/phone-input.mjs +41 -3
- package/phone-input/types/phone-input.d.ts +5 -2
- package/searchable-multiselect/README.md +62 -40
- package/searchable-multiselect/fesm2022/searchable-multiselect.mjs +59 -32
- package/searchable-multiselect/types/searchable-multiselect.d.ts +23 -6
- package/slider/fesm2022/slider.mjs +9 -5
- package/slider/types/slider.d.ts +6 -2
- package/stepper/README.md +39 -19
- package/stepper/fesm2022/stepper.mjs +64 -12
- package/stepper/types/stepper.d.ts +12 -1
package/README.md
CHANGED
|
@@ -167,9 +167,46 @@ export class DemoComponent {
|
|
|
167
167
|
}
|
|
168
168
|
```
|
|
169
169
|
|
|
170
|
+
## Searchable multiselect
|
|
171
|
+
|
|
172
|
+
`EgSearchableMultiselect` provides local, case-insensitive label search, named removable chips, and a `string[]` CVA. Import it directly from its public subpath:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { Component, signal } from '@angular/core';
|
|
176
|
+
import { EgSearchableMultiselect } from '@egose/shadcn-theme-ng/searchable-multiselect';
|
|
177
|
+
|
|
178
|
+
@Component({
|
|
179
|
+
selector: 'app-assignees',
|
|
180
|
+
imports: [EgSearchableMultiselect],
|
|
181
|
+
template: `
|
|
182
|
+
<eg-searchable-multiselect
|
|
183
|
+
[options]="people"
|
|
184
|
+
[(value)]="selected"
|
|
185
|
+
ariaLabel="Assignees"
|
|
186
|
+
searchLabel="Find people"
|
|
187
|
+
searchPlaceholder="Type a name…"
|
|
188
|
+
emptyMessage="No people match"
|
|
189
|
+
/>
|
|
190
|
+
`,
|
|
191
|
+
})
|
|
192
|
+
export class AssigneesComponent {
|
|
193
|
+
readonly people = [
|
|
194
|
+
{ value: 'ada', label: 'Ada Lovelace' },
|
|
195
|
+
{ value: 'grace', label: 'Grace Hopper' },
|
|
196
|
+
];
|
|
197
|
+
readonly selected = signal<string[]>(['ada']);
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Use `@egose/shadcn-theme-ng-tw/searchable-multiselect` for the prefixed variant. Pass the complete options list: the trimmed query filters labels in source order without changing selected IDs or chip labels. Clearing the query restores choices. Async option replacements update labels and results; unresolved selections retain their IDs. Search is local to supplied options, with no remote requests or virtualization. The query persists across closing/reopening and external value resets.
|
|
202
|
+
|
|
203
|
+
The native search is visibly labeled, empty results use a polite status region, and chip buttons are named `Remove <label>` (raw ID when unresolved). Customize names with `[removeLabel]="formatter"`, where `formatter: (option: SelectOption) => string` is pure and `SelectOption` comes from the same subpath. Search edits neither emit selection changes nor touch forms. Input/wrapper/form disabled flags block search and selection edits. With default popover focus settings, opening focuses search; Tab reaches checkbox buttons, Space/Enter toggles them, and Escape closes and restores trigger focus. Enter in search does not submit a form.
|
|
204
|
+
|
|
205
|
+
For reactive or template-driven forms, import `ReactiveFormsModule` or `FormsModule` and bind `formControl`/`formControlName` or `ngModel` instead of `value`. CVA writes own selection after the first write; null/empty arrays clear it without user emissions. Standalone new `[value]` arrays replace local edits; `[(value)]` synchronizes the parent. Supply unique IDs and immutable array/option updates. `EgFormSearchableMultiselect` from `@egose/shadcn-theme-ng/form-searchable-multiselect` adds the form label/hint/error layout and forwards `searchLabel`, `searchPlaceholder`, `emptyMessage`, and `removeLabel`.
|
|
206
|
+
|
|
170
207
|
## Date picker values
|
|
171
208
|
|
|
172
|
-
`hlm-date-picker` emits a native JS `Date` (or `null` when cleared) via the `dateChange` output. Read it in the
|
|
209
|
+
By default, `hlm-date-picker` emits a native JS `Date` (or `null` when cleared) via the `dateChange` output. Read it in the
|
|
173
210
|
controller with `(dateChange)`, a template ref (`picker.value()`), or a form binding (`ngModel`/`formControlName`,
|
|
174
211
|
the picker is a `ControlValueAccessor`):
|
|
175
212
|
|
|
@@ -216,6 +253,87 @@ export class MyComponent {}
|
|
|
216
253
|
then always sees local-midnight dates. Related: the picker stays open after selection by default; add the
|
|
217
254
|
`autoCloseOnSelect` attribute (or set it in the same config) to close it on select.
|
|
218
255
|
|
|
256
|
+
### Typed commits and constraints (single, range, multi)
|
|
257
|
+
|
|
258
|
+
Text commits on blur or Enter. Both the parsed value and the configured transform result must satisfy
|
|
259
|
+
`min`/`max`, inclusive of the adapter's entire boundary days. Comparisons and validity (`getTime` must be
|
|
260
|
+
finite) use Spartan's configured date adapter; custom date types also need matching parse/format/transform
|
|
261
|
+
config callbacks. A range's transformed endpoints must be ordered; a custom transform may sort them.
|
|
262
|
+
The default range parser accepts a single date as a same-day range, but rejects an explicitly unparseable
|
|
263
|
+
second endpoint.
|
|
264
|
+
|
|
265
|
+
Multi-date commits cannot exceed `maxSelection`. Consistent with calendar selection, `minSelection` is a
|
|
266
|
+
**deselection floor**: reducing the current count below it is rejected, but growing a selection from empty
|
|
267
|
+
is allowed. Counts apply to the supplied arrays before and after transformation. Supply unique dates.
|
|
268
|
+
At the calendar's maximum, its proposed reset to one new date is accepted only if the floor permits it.
|
|
269
|
+
|
|
270
|
+
Rejected text remains editable across blur, Enter and refocus, with native `aria-invalid="true"` and invalid
|
|
271
|
+
styling. It does **not** change the committed value or emit `dateChange`/CVA `onChange`; the interaction
|
|
272
|
+
marks the control touched. `inputInvalid()` on the text-input component exposes this local state. It does
|
|
273
|
+
not add Angular validation errors: validators still inspect the last committed form value. Use
|
|
274
|
+
`ariaDescribedby` for application-specific guidance, and include `inputInvalid()` in submission eligibility
|
|
275
|
+
if an unresolved draft should block submission. Existing `forceInvalid` is combined with this state.
|
|
276
|
+
|
|
277
|
+
A successful text/calendar commit clears the local invalid state. Empty text or the clear button explicitly
|
|
278
|
+
clears (`null` for single/range, `[]` for multi), bypassing transforms and the selection floor. Whitespace is
|
|
279
|
+
passed to the configured parser. Enter retains the edit format so later blur can parse it; blur uses the
|
|
280
|
+
display format. Rejected calendar selections restore the prior selection without emitting a replacement.
|
|
281
|
+
|
|
282
|
+
`updateDate(value)` is the user-commit boundary and returns `false` on rejection/disabled, `true` on success.
|
|
283
|
+
Programmatic CVA `writeValue` (including form `setValue`/reset) instead applies the transform **without**
|
|
284
|
+
user-constraint enforcement or user emissions, and replaces rejected text even for repeated values/null.
|
|
285
|
+
Standalone `[date]` updates retain their direct, untransformed input contract; they are not user commits.
|
|
286
|
+
`reset()` remains an explicit programmatic clear that emits. Parse/transform callbacks should be pure and
|
|
287
|
+
return values compatible with the configured adapter; parsing failure is represented by `null`.
|
|
288
|
+
|
|
289
|
+
## Validation descriptions on composed fields
|
|
290
|
+
|
|
291
|
+
`EgFormAutocomplete`, `EgFormInputOtp`, `EgFormSlider`, and `EgFormCombobox` connect the
|
|
292
|
+
displayed hint or validation error to their actual native input, slider thumbs, or combobox
|
|
293
|
+
trigger/search input. Errors replace hints when invalid and touched, dirty, or submitted;
|
|
294
|
+
correction and form reset restore the appropriate hint. `HlmError` is styled text, not a live region.
|
|
295
|
+
|
|
296
|
+
Use `[aria-describedby]="'external-help-id'"` on these wrappers to add external descriptions.
|
|
297
|
+
The wrapper normalizes/deduplicates IDs and appends its current message ID. Keep consumer-owned
|
|
298
|
+
description elements mounted, and reserve `<effectiveId>-error` / `-hint` for wrapper messages.
|
|
299
|
+
`[attr.aria-describedby]` only targets the custom host and is not this forwarding contract.
|
|
300
|
+
|
|
301
|
+
For primitive compositions, `HlmAutocompleteInput`, `HlmComboboxInput`, `HlmComboboxTrigger`,
|
|
302
|
+
`HlmSlider`, and `HlmInputOtpControl` accept `[aria-describedby]` and merge it with descriptions
|
|
303
|
+
registered by the enclosing Spartan field. Import `HlmInputOtpControl` (selector `hlm-input-otp`)
|
|
304
|
+
or `HlmInputOtpImports` from `@egose/shadcn-theme-ng/input-otp`; it retains the brain OTP CVA and
|
|
305
|
+
editing behavior while adding native-input descriptions, required and invalid state. Existing
|
|
306
|
+
`brn-input-otp hlmInputOtp` compositions remain available.
|
|
307
|
+
|
|
308
|
+
Single combobox mode labels its trigger with `<effectiveId>` and uses `<effectiveId>-search`
|
|
309
|
+
for its separately labeled popup search. Required/invalid selection state stays on the trigger;
|
|
310
|
+
the popup search filters options. Slider thumbs retain their labels and invalid state; the slider
|
|
311
|
+
role has no `aria-required`. Configure Angular validators independently of wrapper `required`.
|
|
312
|
+
Verification is rendered browser DOM/focus coverage, not screen-reader testing.
|
|
313
|
+
|
|
314
|
+
## Numbered pagination
|
|
315
|
+
|
|
316
|
+
Import `HlmNumberedPagination` or `HlmNumberedPaginationQueryParams` from
|
|
317
|
+
`@egose/shadcn-theme-ng/pagination` (or the `-tw` package), then bind
|
|
318
|
+
`[(currentPage)]`, `[(itemsPerPage)]` and `[totalItems]`.
|
|
319
|
+
|
|
320
|
+
Both pagers floor/clamp the current page when totals, size or page inputs change.
|
|
321
|
+
Empty/non-finite/non-positive totals or sizes produce one page with no previous/next;
|
|
322
|
+
invalid sizes are not rewritten. Non-finite pages become 1. A correction emits
|
|
323
|
+
`currentPageChange` once. Positive fractional totals/sizes use ceil division, with
|
|
324
|
+
page counts capped at `Number.MAX_SAFE_INTEGER`. `maxSize` budgets window entries,
|
|
325
|
+
including ellipses: finite positive values are floored/clamped to 1–100, otherwise 7.
|
|
326
|
+
Ranges below 5 show a contiguous active-page window. Helpers `createPageArray` and
|
|
327
|
+
`outOfBoundCorrection` use the same policy without side effects or allocation based
|
|
328
|
+
on an unbounded page count.
|
|
329
|
+
|
|
330
|
+
The query-params pager builds bounded `?page=` links using `queryParamsHandling="merge"`,
|
|
331
|
+
preserving unrelated query parameters. The parent owns reading route changes and
|
|
332
|
+
any URL synchronization: model corrections and size changes do not navigate. Handle
|
|
333
|
+
`currentPageChange` in the parent if corrected URLs should be replaced, merging other
|
|
334
|
+
parameters. Keep the previous total during loading if temporary zero results should
|
|
335
|
+
not reset the page.
|
|
336
|
+
|
|
219
337
|
## Working example
|
|
220
338
|
|
|
221
339
|
A complete Angular consumer app lives at [`@examples/standard`](https://github.com/egose/shadcn-theme/tree/main/packages/angular/@examples/standard) — see its `src/app/...` for real usage of `@egose/shadcn-theme-ng/button`, `.../layout-simple`, `.../form-text-input`, `.../autocomplete`, `.../select`, `.../form-checkbox`, `.../sheet`, etc.
|
|
@@ -9,6 +9,7 @@ import { NgIcon, provideIcons } from '@ng-icons/core';
|
|
|
9
9
|
import { lucideX, lucideSearch, lucideCheck } from '@ng-icons/lucide';
|
|
10
10
|
import * as i2$1 from '@egose/shadcn-theme-ng/input-group';
|
|
11
11
|
import { HlmInputGroup, HlmInputGroupImports } from '@egose/shadcn-theme-ng/input-group';
|
|
12
|
+
import { BrnFieldControlDescribedBy } from '@spartan-ng/brain/field';
|
|
12
13
|
|
|
13
14
|
class HlmAutocomplete {
|
|
14
15
|
constructor() {
|
|
@@ -103,6 +104,9 @@ class HlmAutocompleteInput {
|
|
|
103
104
|
...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
|
|
104
105
|
placeholder = input('', /* @ts-ignore */
|
|
105
106
|
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
107
|
+
/** Native input description IDs, merged with enclosing Spartan field descriptions. */
|
|
108
|
+
ariaDescribedBy = input(null, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : /* istanbul ignore next */ {}), alias: 'aria-describedby' });
|
|
109
|
+
required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
106
110
|
showSearch = input(true, { ...(ngDevMode ? { debugName: "showSearch" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
107
111
|
showClear = input(false, { ...(ngDevMode ? { debugName: "showClear" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
108
112
|
/** Forces the invalid state visually, regardless of form control state. */
|
|
@@ -114,13 +118,16 @@ class HlmAutocompleteInput {
|
|
|
114
118
|
classes(() => "w-auto");
|
|
115
119
|
}
|
|
116
120
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmAutocompleteInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
117
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: HlmAutocompleteInput, isStandalone: true, selector: "hlm-autocomplete-input", inputs: { inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, forceInvalid: { classPropertyName: "forceInvalid", publicName: "forceInvalid", isSignal: true, isRequired: false, transformFunction: null }, ariaInvalidOverride: { classPropertyName: "ariaInvalidOverride", publicName: "aria-invalid", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideIcons({ lucideSearch, lucideX })], hostDirectives: [{ directive: i1.BrnAutocompleteAnchor }, { directive: i2$1.HlmInputGroup }], ngImport: i0, template: `
|
|
121
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: HlmAutocompleteInput, isStandalone: true, selector: "hlm-autocomplete-input", inputs: { inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, forceInvalid: { classPropertyName: "forceInvalid", publicName: "forceInvalid", isSignal: true, isRequired: false, transformFunction: null }, ariaInvalidOverride: { classPropertyName: "ariaInvalidOverride", publicName: "aria-invalid", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideIcons({ lucideSearch, lucideX })], hostDirectives: [{ directive: i1.BrnAutocompleteAnchor }, { directive: i2$1.HlmInputGroup }], ngImport: i0, template: `
|
|
118
122
|
<input
|
|
119
123
|
brnAutocompleteInput
|
|
120
124
|
#autocompleteInput="brnAutocompleteInput"
|
|
121
125
|
hlmInputGroupInput
|
|
122
126
|
[id]="inputId()"
|
|
123
127
|
[placeholder]="placeholder()"
|
|
128
|
+
brnFieldControlDescribedBy
|
|
129
|
+
[aria-describedby]="ariaDescribedBy()"
|
|
130
|
+
[attr.aria-required]="required() || null"
|
|
124
131
|
[aria-invalid]="ariaInvalidOverride()"
|
|
125
132
|
[forceInvalid]="forceInvalid()"
|
|
126
133
|
/>
|
|
@@ -146,13 +153,13 @@ class HlmAutocompleteInput {
|
|
|
146
153
|
</hlm-input-group-addon>
|
|
147
154
|
}
|
|
148
155
|
<ng-content />
|
|
149
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: i2$1.HlmInputGroupAddon, selector: "[hlmInputGroupAddon],hlm-input-group-addon", inputs: ["align"] }, { kind: "directive", type: i2$1.HlmInputGroupButton, selector: "button[hlmInputGroupButton]", inputs: ["size", "type", "class"] }, { kind: "directive", type: i2$1.HlmInputGroupInput, selector: "input[hlmInputGroupInput]" }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: BrnAutocompleteClear, selector: "[brnAutocompleteClear]" }, { kind: "directive", type: BrnAutocompleteInput, selector: "input[brnAutocompleteInput]", inputs: ["id", "aria-invalid", "forceInvalid"], exportAs: ["brnAutocompleteInput"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
156
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: i2$1.HlmInputGroupAddon, selector: "[hlmInputGroupAddon],hlm-input-group-addon", inputs: ["align"] }, { kind: "directive", type: i2$1.HlmInputGroupButton, selector: "button[hlmInputGroupButton]", inputs: ["size", "type", "class"] }, { kind: "directive", type: i2$1.HlmInputGroupInput, selector: "input[hlmInputGroupInput]" }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: BrnAutocompleteClear, selector: "[brnAutocompleteClear]" }, { kind: "directive", type: BrnAutocompleteInput, selector: "input[brnAutocompleteInput]", inputs: ["id", "aria-invalid", "forceInvalid"], exportAs: ["brnAutocompleteInput"] }, { kind: "directive", type: BrnFieldControlDescribedBy, selector: "[brnFieldControlDescribedBy]", inputs: ["aria-describedby"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
150
157
|
}
|
|
151
158
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmAutocompleteInput, decorators: [{
|
|
152
159
|
type: Component,
|
|
153
160
|
args: [{
|
|
154
161
|
selector: 'hlm-autocomplete-input',
|
|
155
|
-
imports: [HlmInputGroupImports, NgIcon, BrnAutocompleteClear, BrnAutocompleteInput],
|
|
162
|
+
imports: [HlmInputGroupImports, NgIcon, BrnAutocompleteClear, BrnAutocompleteInput, BrnFieldControlDescribedBy],
|
|
156
163
|
providers: [provideIcons({ lucideSearch, lucideX })],
|
|
157
164
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
158
165
|
hostDirectives: [BrnAutocompleteAnchor, HlmInputGroup],
|
|
@@ -163,6 +170,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
163
170
|
hlmInputGroupInput
|
|
164
171
|
[id]="inputId()"
|
|
165
172
|
[placeholder]="placeholder()"
|
|
173
|
+
brnFieldControlDescribedBy
|
|
174
|
+
[aria-describedby]="ariaDescribedBy()"
|
|
175
|
+
[attr.aria-required]="required() || null"
|
|
166
176
|
[aria-invalid]="ariaInvalidOverride()"
|
|
167
177
|
[forceInvalid]="forceInvalid()"
|
|
168
178
|
/>
|
|
@@ -190,7 +200,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
190
200
|
<ng-content />
|
|
191
201
|
`,
|
|
192
202
|
}]
|
|
193
|
-
}], ctorParameters: () => [], propDecorators: { inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], showSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearch", required: false }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", required: false }] }], forceInvalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "forceInvalid", required: false }] }], ariaInvalidOverride: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-invalid", required: false }] }] } });
|
|
203
|
+
}], ctorParameters: () => [], propDecorators: { inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-describedby", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], showSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearch", required: false }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", required: false }] }], forceInvalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "forceInvalid", required: false }] }], ariaInvalidOverride: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-invalid", required: false }] }] } });
|
|
194
204
|
|
|
195
205
|
class HlmAutocompleteItem {
|
|
196
206
|
_brnAutocompleteItem = inject(BrnAutocompleteItem);
|
|
@@ -32,6 +32,9 @@ declare class HlmAutocompleteInput {
|
|
|
32
32
|
private static _id;
|
|
33
33
|
readonly inputId: i0.InputSignal<string>;
|
|
34
34
|
readonly placeholder: i0.InputSignal<string>;
|
|
35
|
+
/** Native input description IDs, merged with enclosing Spartan field descriptions. */
|
|
36
|
+
readonly ariaDescribedBy: i0.InputSignal<string | null>;
|
|
37
|
+
readonly required: i0.InputSignalWithTransform<boolean, BooleanInput>;
|
|
35
38
|
readonly showSearch: i0.InputSignalWithTransform<boolean, BooleanInput>;
|
|
36
39
|
readonly showClear: i0.InputSignalWithTransform<boolean, BooleanInput>;
|
|
37
40
|
/** Forces the invalid state visually, regardless of form control state. */
|
|
@@ -40,7 +43,7 @@ declare class HlmAutocompleteInput {
|
|
|
40
43
|
readonly ariaInvalidOverride: i0.InputSignalWithTransform<boolean | undefined, BooleanInput>;
|
|
41
44
|
constructor();
|
|
42
45
|
static ɵfac: i0.ɵɵFactoryDeclaration<HlmAutocompleteInput, never>;
|
|
43
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<HlmAutocompleteInput, "hlm-autocomplete-input", never, { "inputId": { "alias": "inputId"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "showSearch": { "alias": "showSearch"; "required": false; "isSignal": true; }; "showClear": { "alias": "showClear"; "required": false; "isSignal": true; }; "forceInvalid": { "alias": "forceInvalid"; "required": false; "isSignal": true; }; "ariaInvalidOverride": { "alias": "aria-invalid"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, [{ directive: typeof i1.BrnAutocompleteAnchor; inputs: {}; outputs: {}; }, { directive: typeof i2$1.HlmInputGroup; inputs: {}; outputs: {}; }]>;
|
|
46
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<HlmAutocompleteInput, "hlm-autocomplete-input", never, { "inputId": { "alias": "inputId"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "ariaDescribedBy": { "alias": "aria-describedby"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "showSearch": { "alias": "showSearch"; "required": false; "isSignal": true; }; "showClear": { "alias": "showClear"; "required": false; "isSignal": true; }; "forceInvalid": { "alias": "forceInvalid"; "required": false; "isSignal": true; }; "ariaInvalidOverride": { "alias": "aria-invalid"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, [{ directive: typeof i1.BrnAutocompleteAnchor; inputs: {}; outputs: {}; }, { directive: typeof i2$1.HlmInputGroup; inputs: {}; outputs: {}; }]>;
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
declare class HlmAutocompleteItem {
|
package/checkbox/README.md
CHANGED
|
@@ -75,6 +75,8 @@ export class DemoModule {}
|
|
|
75
75
|
|
|
76
76
|
Real selector: `hlm-checkbox` (element only). The host renders `class="contents peer"` so sibling label/description selectors (`peer-*`) keep working; `id` / `aria-*` attributes are nulled on the host and forwarded to the inner `brn-checkbox` instead. Set a visible label with a native `<label for>` — the component does not render label text itself.
|
|
77
77
|
|
|
78
|
+
Omitted/null IDs use `HlmFormIdGenerator` from `@egose/shadcn-theme-ng/form-field`: `hlm-checkbox-${APP_ID}-${sequence}`. The fallback is allocated once per instance from the application-scoped service, so separate applications restart their sequence and matching server/client `APP_ID` plus component creation order reproduce IDs. Explicit IDs are preserved on the focusable button; reverting to null restores that instance's fallback. Supply distinct `APP_ID` values for multiple applications on one page and unique explicit IDs. Checkboxes nested inside separate native labels each receive their own generated label association through Spartan; existing label IDs are retained.
|
|
79
|
+
|
|
78
80
|
## API reference
|
|
79
81
|
|
|
80
82
|
### `HlmCheckbox` (`hlm-checkbox`)
|
|
@@ -83,7 +85,7 @@ Real selector: `hlm-checkbox` (element only). The host renders `class="contents
|
|
|
83
85
|
| -------------------------------------- | ----------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
84
86
|
| `checked` | `model<boolean \| 'indeterminate'>` | `false` | Checked state. Two-way bindable (`[(checked)]`). `'indeterminate'` renders the checked icon; clicking it resolves to `true`. |
|
|
85
87
|
| `class` (`userClass`) | `ClassValue` | `''` | Extra classes merged into the box. |
|
|
86
|
-
| `id` | `string \| null` | `null` |
|
|
88
|
+
| `id` | `string \| null` | `null` | Explicit native button ID, or application-scoped fallback when null. Pair explicit IDs with `<label for>`. |
|
|
87
89
|
| `aria-label` (`ariaLabel`) | `string \| null` | `null` | Forwarded aria-label. |
|
|
88
90
|
| `aria-labelledby` (`ariaLabelledby`) | `string \| null` | `null` | Forwarded aria-labelledby. |
|
|
89
91
|
| `aria-describedby` (`ariaDescribedby`) | `string \| null` | `null` | Forwarded aria-describedby. |
|
|
@@ -102,6 +104,8 @@ Real selector: `hlm-checkbox` (element only). The host renders `class="contents
|
|
|
102
104
|
|
|
103
105
|
ControlValueAccessor: `writeValue(value)`, `registerOnChange`, `registerOnTouched`, `setDisabledState(isDisabled)` (writes `formDisabled`, hence `disabledState`). `HLM_CHECKBOX_VALUE_ACCESSOR` (`NG_VALUE_ACCESSOR`, `forwardRef(HlmCheckbox)`, `multi: true`) is provided on the component.
|
|
104
106
|
|
|
107
|
+
Both `[checked]` and CVA writes preserve `'indeterminate'`. The primitive forwards boolean checked and indeterminate separately to Spartan: the actual `button[role="checkbox"]` exposes `aria-checked="mixed"` and `data-state="indeterminate"`. An enabled click resolves mixed to `true`, then subsequent clicks toggle true/false; `changed` and CVA `onChange` emit the resulting boolean once. Thus clicking a partially selected table header selects its displayed page. Programmatic CVA writes do not call `changed`/`onChange` or touch the control; null resets to false. Any active input, wrapper, or form disabled lock blocks user toggles without discarding mixed state.
|
|
108
|
+
|
|
105
109
|
## Examples
|
|
106
110
|
|
|
107
111
|
### 1. Basic with label
|
|
@@ -307,7 +311,7 @@ export class ChangedCheckboxComponent {
|
|
|
307
311
|
- The component renders a native-checkbox-equivalent `brn-checkbox` with `aria-label`/`labelledby`/`describedby` forwarding — always provide one: visible `<label for>` preferred, `aria-label` for icon-only rows.
|
|
308
312
|
- `disabledState` adds `data-disabled`, `cursor-not-allowed`, and `opacity-50`; disabled boxes are skipped correctly because clicks early-return.
|
|
309
313
|
- The check glyph is decorative (`lucideCheck` icon, no text) — state is exposed via `data-[state]` / native semantics, not the icon.
|
|
310
|
-
- Tri-state: `'indeterminate'`
|
|
314
|
+
- Tri-state: `'indeterminate'` exposes `aria-checked="mixed"` on the focusable checkbox button. State and nested-label associations are covered by rendered browser DOM tests; screen-reader announcement behavior is not asserted by those tests.
|
|
311
315
|
|
|
312
316
|
## Theming / CSS variables
|
|
313
317
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { forwardRef, input, computed, model, booleanAttribute, signal, output, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
|
|
2
|
+
import { forwardRef, input, computed, inject, model, booleanAttribute, signal, output, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
|
|
3
3
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
4
4
|
import { provideIcons, NgIcon } from '@ng-icons/core';
|
|
5
5
|
import { lucideCheck } from '@ng-icons/lucide';
|
|
6
6
|
import { BrnCheckbox } from '@spartan-ng/brain/checkbox';
|
|
7
|
+
import { HlmFormIdGenerator } from '@egose/shadcn-theme-ng/form-field';
|
|
7
8
|
import { HlmIcon } from '@egose/shadcn-theme-ng/icon';
|
|
8
9
|
import { hlm } from '@egose/shadcn-theme-ng/utils';
|
|
9
10
|
|
|
@@ -16,16 +17,19 @@ class HlmCheckbox {
|
|
|
16
17
|
userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
|
|
17
18
|
_computedClass = computed(() => hlm("border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive shadow-xs peer size-4 shrink-0 cursor-default rounded-[4px] border outline-none transition-shadow focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", this.userClass(), this.disabledState() ? "cursor-not-allowed opacity-50" : ''), /* @ts-ignore */
|
|
18
19
|
...(ngDevMode ? [{ debugName: "_computedClass" }] : /* istanbul ignore next */ []));
|
|
19
|
-
/**
|
|
20
|
+
/** Native checkbox ID. Null uses a stable application-scoped fallback; explicit IDs are preserved. */
|
|
20
21
|
id = input(null, /* @ts-ignore */
|
|
21
22
|
...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
23
|
+
_generatedId = inject(HlmFormIdGenerator).generate('hlm-checkbox');
|
|
24
|
+
_id = computed(() => this.id() ?? this._generatedId, /* @ts-ignore */
|
|
25
|
+
...(ngDevMode ? [{ debugName: "_id" }] : /* istanbul ignore next */ []));
|
|
22
26
|
/** Used to set the aria-label attribute on the underlying brn element. */
|
|
23
27
|
ariaLabel = input(null, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
|
|
24
28
|
/** Used to set the aria-labelledby attribute on the underlying brn element. */
|
|
25
29
|
ariaLabelledby = input(null, { ...(ngDevMode ? { debugName: "ariaLabelledby" } : /* istanbul ignore next */ {}), alias: 'aria-labelledby' });
|
|
26
30
|
/** Used to set the aria-describedby attribute on the underlying brn element. */
|
|
27
31
|
ariaDescribedby = input(null, { ...(ngDevMode ? { debugName: "ariaDescribedby" } : /* istanbul ignore next */ {}), alias: 'aria-describedby' });
|
|
28
|
-
/**
|
|
32
|
+
/** Tri-state model. Indeterminate exposes native aria-checked="mixed"; clicking it selects (true). */
|
|
29
33
|
checked = model(false, /* @ts-ignore */
|
|
30
34
|
...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
|
|
31
35
|
/** The name attribute of the checkbox. */
|
|
@@ -51,9 +55,9 @@ class HlmCheckbox {
|
|
|
51
55
|
this._onChange?.(checked);
|
|
52
56
|
this.changed.emit(checked);
|
|
53
57
|
}
|
|
54
|
-
/**
|
|
58
|
+
/** Programmatic writes preserve indeterminate; null resets to false without changed/onChange notifications. */
|
|
55
59
|
writeValue(value) {
|
|
56
|
-
this.checked.set(!!value);
|
|
60
|
+
this.checked.set(value === 'indeterminate' ? value : !!value);
|
|
57
61
|
}
|
|
58
62
|
registerOnChange(fn) {
|
|
59
63
|
this._onChange = fn;
|
|
@@ -65,14 +69,14 @@ class HlmCheckbox {
|
|
|
65
69
|
this.formDisabled.set(isDisabled);
|
|
66
70
|
}
|
|
67
71
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmCheckbox, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
68
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: HlmCheckbox, isStandalone: true, selector: "hlm-checkbox", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedby: { classPropertyName: "ariaDescribedby", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, wrapperDisabled: { classPropertyName: "wrapperDisabled", publicName: "wrapperDisabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", changed: "changed" }, host: { properties: { "attr.id": "null", "attr.aria-label": "null", "attr.aria-labelledby": "null", "attr.aria-describedby": "null", "attr.data-disabled": "disabledState() ? \"\" : null" }, classAttribute: "contents peer" }, providers: [HLM_CHECKBOX_VALUE_ACCESSOR], ngImport: i0, template: "\n <brn-checkbox\n [id]=\"
|
|
72
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: HlmCheckbox, isStandalone: true, selector: "hlm-checkbox", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedby: { classPropertyName: "ariaDescribedby", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, wrapperDisabled: { classPropertyName: "wrapperDisabled", publicName: "wrapperDisabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", changed: "changed" }, host: { properties: { "attr.id": "null", "attr.aria-label": "null", "attr.aria-labelledby": "null", "attr.aria-describedby": "null", "attr.data-disabled": "disabledState() ? \"\" : null" }, classAttribute: "contents peer" }, providers: [HLM_CHECKBOX_VALUE_ACCESSOR], ngImport: i0, template: "\n <brn-checkbox\n [id]=\"_id()\"\n [name]=\"name()\"\n [class]=\"_computedClass()\"\n [checked]=\"checked() === true\"\n [indeterminate]=\"checked() === 'indeterminate'\"\n [disabled]=\"disabledState()\"\n [required]=\"required()\"\n [aria-label]=\"ariaLabel()\"\n [aria-labelledby]=\"ariaLabelledby()\"\n [aria-describedby]=\"ariaDescribedby()\"\n (checkedChange)=\"_handleCheckedChange($event)\"\n (touched)=\"_onTouched?.()\"\n >\n @if (checked()) {\n <span class=\"flex items-center justify-center text-current transition-none\">\n <ng-icon hlm size=\"14px\" name=\"lucideCheck\" />\n </span>\n }\n </brn-checkbox>\n ", isInline: true, dependencies: [{ kind: "component", type: BrnCheckbox, selector: "brn-checkbox", inputs: ["checked", "indeterminate", "id", "name", "class", "hostStyles", "aria-label", "aria-labelledby", "aria-describedby", "required", "disabled", "forceInvalid"], outputs: ["checkedChange", "indeterminateChange", "touched"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: HlmIcon, selector: "ng-icon[hlm]", inputs: ["size"] }], viewProviders: [provideIcons({ lucideCheck })], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
69
73
|
}
|
|
70
74
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmCheckbox, decorators: [{
|
|
71
75
|
type: Component,
|
|
72
76
|
args: [{
|
|
73
77
|
selector: 'hlm-checkbox',
|
|
74
78
|
imports: [BrnCheckbox, NgIcon, HlmIcon],
|
|
75
|
-
template: "\n <brn-checkbox\n [id]=\"
|
|
79
|
+
template: "\n <brn-checkbox\n [id]=\"_id()\"\n [name]=\"name()\"\n [class]=\"_computedClass()\"\n [checked]=\"checked() === true\"\n [indeterminate]=\"checked() === 'indeterminate'\"\n [disabled]=\"disabledState()\"\n [required]=\"required()\"\n [aria-label]=\"ariaLabel()\"\n [aria-labelledby]=\"ariaLabelledby()\"\n [aria-describedby]=\"ariaDescribedby()\"\n (checkedChange)=\"_handleCheckedChange($event)\"\n (touched)=\"_onTouched?.()\"\n >\n @if (checked()) {\n <span class=\"flex items-center justify-center text-current transition-none\">\n <ng-icon hlm size=\"14px\" name=\"lucideCheck\" />\n </span>\n }\n </brn-checkbox>\n ",
|
|
76
80
|
host: {
|
|
77
81
|
class: 'contents peer',
|
|
78
82
|
'[attr.id]': 'null',
|
|
@@ -12,15 +12,17 @@ declare const HLM_CHECKBOX_VALUE_ACCESSOR: {
|
|
|
12
12
|
declare class HlmCheckbox implements ControlValueAccessor {
|
|
13
13
|
readonly userClass: _angular_core.InputSignal<ClassValue>;
|
|
14
14
|
protected readonly _computedClass: _angular_core.Signal<string>;
|
|
15
|
-
/**
|
|
15
|
+
/** Native checkbox ID. Null uses a stable application-scoped fallback; explicit IDs are preserved. */
|
|
16
16
|
readonly id: _angular_core.InputSignal<string | null>;
|
|
17
|
+
private readonly _generatedId;
|
|
18
|
+
protected readonly _id: _angular_core.Signal<string>;
|
|
17
19
|
/** Used to set the aria-label attribute on the underlying brn element. */
|
|
18
20
|
readonly ariaLabel: _angular_core.InputSignal<string | null>;
|
|
19
21
|
/** Used to set the aria-labelledby attribute on the underlying brn element. */
|
|
20
22
|
readonly ariaLabelledby: _angular_core.InputSignal<string | null>;
|
|
21
23
|
/** Used to set the aria-describedby attribute on the underlying brn element. */
|
|
22
24
|
readonly ariaDescribedby: _angular_core.InputSignal<string | null>;
|
|
23
|
-
/**
|
|
25
|
+
/** Tri-state model. Indeterminate exposes native aria-checked="mixed"; clicking it selects (true). */
|
|
24
26
|
readonly checked: _angular_core.ModelSignal<CheckboxValue>;
|
|
25
27
|
/** The name attribute of the checkbox. */
|
|
26
28
|
readonly name: _angular_core.InputSignal<string | null>;
|
|
@@ -36,8 +38,8 @@ declare class HlmCheckbox implements ControlValueAccessor {
|
|
|
36
38
|
protected _onChange?: ChangeFn<CheckboxValue>;
|
|
37
39
|
protected _onTouched?: TouchFn;
|
|
38
40
|
protected _handleCheckedChange(checked: boolean): void;
|
|
39
|
-
/**
|
|
40
|
-
writeValue(value: CheckboxValue): void;
|
|
41
|
+
/** Programmatic writes preserve indeterminate; null resets to false without changed/onChange notifications. */
|
|
42
|
+
writeValue(value: CheckboxValue | null): void;
|
|
41
43
|
registerOnChange(fn: ChangeFn<CheckboxValue>): void;
|
|
42
44
|
registerOnTouched(fn: TouchFn): void;
|
|
43
45
|
setDisabledState(isDisabled: boolean): void;
|
|
@@ -211,6 +211,9 @@ class HlmComboboxInput {
|
|
|
211
211
|
...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
|
|
212
212
|
placeholder = input('', /* @ts-ignore */
|
|
213
213
|
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
214
|
+
/** Native input description IDs, merged with enclosing Spartan field descriptions. */
|
|
215
|
+
ariaDescribedBy = input(null, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : /* istanbul ignore next */ {}), alias: 'aria-describedby' });
|
|
216
|
+
ariaLabelledBy = input(null, { ...(ngDevMode ? { debugName: "ariaLabelledBy" } : /* istanbul ignore next */ {}), alias: 'aria-labelledby' });
|
|
214
217
|
showTrigger = input(true, { ...(ngDevMode ? { debugName: "showTrigger" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
215
218
|
showClear = input(false, { ...(ngDevMode ? { debugName: "showClear" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
216
219
|
forceInvalid = input(false, { ...(ngDevMode ? { debugName: "forceInvalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
@@ -221,19 +224,19 @@ class HlmComboboxInput {
|
|
|
221
224
|
classes(() => "w-auto");
|
|
222
225
|
}
|
|
223
226
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmComboboxInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
224
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: HlmComboboxInput, isStandalone: true, selector: "hlm-combobox-input", inputs: { inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, showTrigger: { classPropertyName: "showTrigger", publicName: "showTrigger", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, forceInvalid: { classPropertyName: "forceInvalid", publicName: "forceInvalid", isSignal: true, isRequired: false, transformFunction: null }, ariaInvalidOverride: { classPropertyName: "ariaInvalidOverride", publicName: "aria-invalid", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideIcons({ lucideChevronDown, lucideX })], hostDirectives: [{ directive: i1.BrnComboboxAnchor }, { directive: i2$1.HlmInputGroup }], ngImport: i0, template: "\n <input\n brnComboboxInput\n #comboboxInput=\"brnComboboxInput\"\n brnComboboxPopoverTrigger\n [closeOnTriggerClick]=\"false\"\n hlmInputGroupInput\n [id]=\"inputId()\"\n [placeholder]=\"placeholder()\"\n [forceInvalid]=\"forceInvalid()\"\n [aria-invalid]=\"ariaInvalidOverride()\"\n />\n\n <hlm-input-group-addon align=\"inline-end\">\n @if (showTrigger()) {\n <button\n brnComboboxPopoverTrigger\n hlmInputGroupButton\n data-slot=\"input-group-button\"\n [disabled]=\"comboboxInput.disabled()\"\n size=\"icon-xs\"\n variant=\"ghost\"\n class=\"group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent\"\n >\n <ng-icon name=\"lucideChevronDown\" />\n </button>\n }\n\n @if (showClear()) {\n <button\n *brnComboboxClear\n hlmInputGroupButton\n data-slot=\"combobox-clear\"\n [disabled]=\"comboboxInput.disabled()\"\n size=\"icon-xs\"\n variant=\"ghost\"\n >\n <ng-icon name=\"lucideX\" />\n </button>\n }\n </hlm-input-group-addon>\n\n <ng-content />\n ", isInline: true, dependencies: [{ kind: "directive", type: i2$1.HlmInputGroupAddon, selector: "[hlmInputGroupAddon],hlm-input-group-addon", inputs: ["align"] }, { kind: "directive", type: i2$1.HlmInputGroupButton, selector: "button[hlmInputGroupButton]", inputs: ["size", "type", "class"] }, { kind: "directive", type: i2$1.HlmInputGroupInput, selector: "input[hlmInputGroupInput]" }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: i1.BrnComboboxClear, selector: "[brnComboboxClear]" }, { kind: "directive", type: i1.BrnComboboxInput, selector: "input[brnComboboxInput]", inputs: ["id", "aria-invalid", "forceInvalid"], exportAs: ["brnComboboxInput"] }, { kind: "directive", type: i1.BrnComboboxPopoverTrigger, selector: "[brnComboboxPopoverTrigger]", inputs: ["closeOnTriggerClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
227
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: HlmComboboxInput, isStandalone: true, selector: "hlm-combobox-input", inputs: { inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null }, showTrigger: { classPropertyName: "showTrigger", publicName: "showTrigger", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, forceInvalid: { classPropertyName: "forceInvalid", publicName: "forceInvalid", isSignal: true, isRequired: false, transformFunction: null }, ariaInvalidOverride: { classPropertyName: "ariaInvalidOverride", publicName: "aria-invalid", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideIcons({ lucideChevronDown, lucideX })], hostDirectives: [{ directive: i1.BrnComboboxAnchor }, { directive: i2$1.HlmInputGroup }], ngImport: i0, template: "\n <input\n brnComboboxInput\n #comboboxInput=\"brnComboboxInput\"\n brnComboboxPopoverTrigger\n [closeOnTriggerClick]=\"false\"\n hlmInputGroupInput\n [id]=\"inputId()\"\n [placeholder]=\"placeholder()\"\n brnFieldControlDescribedBy\n [aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-labelledby]=\"ariaLabelledBy()\"\n [forceInvalid]=\"forceInvalid()\"\n [aria-invalid]=\"ariaInvalidOverride()\"\n />\n\n <hlm-input-group-addon align=\"inline-end\">\n @if (showTrigger()) {\n <button\n brnComboboxPopoverTrigger\n hlmInputGroupButton\n data-slot=\"input-group-button\"\n [disabled]=\"comboboxInput.disabled()\"\n size=\"icon-xs\"\n variant=\"ghost\"\n class=\"group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent\"\n >\n <ng-icon name=\"lucideChevronDown\" />\n </button>\n }\n\n @if (showClear()) {\n <button\n *brnComboboxClear\n hlmInputGroupButton\n data-slot=\"combobox-clear\"\n [disabled]=\"comboboxInput.disabled()\"\n size=\"icon-xs\"\n variant=\"ghost\"\n >\n <ng-icon name=\"lucideX\" />\n </button>\n }\n </hlm-input-group-addon>\n\n <ng-content />\n ", isInline: true, dependencies: [{ kind: "directive", type: i2$1.HlmInputGroupAddon, selector: "[hlmInputGroupAddon],hlm-input-group-addon", inputs: ["align"] }, { kind: "directive", type: i2$1.HlmInputGroupButton, selector: "button[hlmInputGroupButton]", inputs: ["size", "type", "class"] }, { kind: "directive", type: i2$1.HlmInputGroupInput, selector: "input[hlmInputGroupInput]" }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: i1.BrnComboboxClear, selector: "[brnComboboxClear]" }, { kind: "directive", type: i1.BrnComboboxInput, selector: "input[brnComboboxInput]", inputs: ["id", "aria-invalid", "forceInvalid"], exportAs: ["brnComboboxInput"] }, { kind: "directive", type: i1.BrnComboboxPopoverTrigger, selector: "[brnComboboxPopoverTrigger]", inputs: ["closeOnTriggerClick"] }, { kind: "directive", type: BrnFieldControlDescribedBy, selector: "[brnFieldControlDescribedBy]", inputs: ["aria-describedby"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
225
228
|
}
|
|
226
229
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmComboboxInput, decorators: [{
|
|
227
230
|
type: Component,
|
|
228
231
|
args: [{
|
|
229
232
|
selector: 'hlm-combobox-input',
|
|
230
|
-
imports: [HlmInputGroupImports, NgIcon, BrnComboboxImports, BrnComboboxPopoverTrigger],
|
|
233
|
+
imports: [HlmInputGroupImports, NgIcon, BrnComboboxImports, BrnComboboxPopoverTrigger, BrnFieldControlDescribedBy],
|
|
231
234
|
providers: [provideIcons({ lucideChevronDown, lucideX })],
|
|
232
235
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
233
236
|
hostDirectives: [BrnComboboxAnchor, HlmInputGroup],
|
|
234
|
-
template: "\n <input\n brnComboboxInput\n #comboboxInput=\"brnComboboxInput\"\n brnComboboxPopoverTrigger\n [closeOnTriggerClick]=\"false\"\n hlmInputGroupInput\n [id]=\"inputId()\"\n [placeholder]=\"placeholder()\"\n [forceInvalid]=\"forceInvalid()\"\n [aria-invalid]=\"ariaInvalidOverride()\"\n />\n\n <hlm-input-group-addon align=\"inline-end\">\n @if (showTrigger()) {\n <button\n brnComboboxPopoverTrigger\n hlmInputGroupButton\n data-slot=\"input-group-button\"\n [disabled]=\"comboboxInput.disabled()\"\n size=\"icon-xs\"\n variant=\"ghost\"\n class=\"group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent\"\n >\n <ng-icon name=\"lucideChevronDown\" />\n </button>\n }\n\n @if (showClear()) {\n <button\n *brnComboboxClear\n hlmInputGroupButton\n data-slot=\"combobox-clear\"\n [disabled]=\"comboboxInput.disabled()\"\n size=\"icon-xs\"\n variant=\"ghost\"\n >\n <ng-icon name=\"lucideX\" />\n </button>\n }\n </hlm-input-group-addon>\n\n <ng-content />\n ",
|
|
237
|
+
template: "\n <input\n brnComboboxInput\n #comboboxInput=\"brnComboboxInput\"\n brnComboboxPopoverTrigger\n [closeOnTriggerClick]=\"false\"\n hlmInputGroupInput\n [id]=\"inputId()\"\n [placeholder]=\"placeholder()\"\n brnFieldControlDescribedBy\n [aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-labelledby]=\"ariaLabelledBy()\"\n [forceInvalid]=\"forceInvalid()\"\n [aria-invalid]=\"ariaInvalidOverride()\"\n />\n\n <hlm-input-group-addon align=\"inline-end\">\n @if (showTrigger()) {\n <button\n brnComboboxPopoverTrigger\n hlmInputGroupButton\n data-slot=\"input-group-button\"\n [disabled]=\"comboboxInput.disabled()\"\n size=\"icon-xs\"\n variant=\"ghost\"\n class=\"group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent\"\n >\n <ng-icon name=\"lucideChevronDown\" />\n </button>\n }\n\n @if (showClear()) {\n <button\n *brnComboboxClear\n hlmInputGroupButton\n data-slot=\"combobox-clear\"\n [disabled]=\"comboboxInput.disabled()\"\n size=\"icon-xs\"\n variant=\"ghost\"\n >\n <ng-icon name=\"lucideX\" />\n </button>\n }\n </hlm-input-group-addon>\n\n <ng-content />\n ",
|
|
235
238
|
}]
|
|
236
|
-
}], ctorParameters: () => [], propDecorators: { inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], showTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTrigger", required: false }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", required: false }] }], forceInvalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "forceInvalid", required: false }] }], ariaInvalidOverride: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-invalid", required: false }] }] } });
|
|
239
|
+
}], ctorParameters: () => [], propDecorators: { inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-describedby", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-labelledby", required: false }] }], showTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTrigger", required: false }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", required: false }] }], forceInvalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "forceInvalid", required: false }] }], ariaInvalidOverride: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-invalid", required: false }] }] } });
|
|
237
240
|
|
|
238
241
|
class HlmComboboxItem {
|
|
239
242
|
_brnComboboxItem = inject(BrnComboboxItem);
|
|
@@ -405,11 +408,14 @@ class HlmComboboxTrigger {
|
|
|
405
408
|
...(ngDevMode ? [{ debugName: "_computedClass" }] : /* istanbul ignore next */ []));
|
|
406
409
|
buttonId = input(`hlm-combobox-trigger-${HlmComboboxTrigger._id++}`, /* @ts-ignore */
|
|
407
410
|
...(ngDevMode ? [{ debugName: "buttonId" }] : /* istanbul ignore next */ []));
|
|
411
|
+
/** Native button description IDs, merged with enclosing Spartan field descriptions. */
|
|
412
|
+
ariaDescribedBy = input(null, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : /* istanbul ignore next */ {}), alias: 'aria-describedby' });
|
|
413
|
+
required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
408
414
|
variant = input('outline', /* @ts-ignore */
|
|
409
415
|
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
410
416
|
forceInvalid = input(false, { ...(ngDevMode ? { debugName: "forceInvalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
411
417
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmComboboxTrigger, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
412
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: HlmComboboxTrigger, isStandalone: true, selector: "hlm-combobox-trigger", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, buttonId: { classPropertyName: "buttonId", publicName: "buttonId", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, forceInvalid: { classPropertyName: "forceInvalid", publicName: "forceInvalid", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideIcons({ lucideChevronDown })], ngImport: i0, template: "\n <button\n brnComboboxTrigger\n brnComboboxAnchor\n brnComboboxPopoverTrigger\n brnFieldControlDescribedBy\n hlmBtn\n data-slot=\"combobox-trigger\"\n [id]=\"buttonId()\"\n [class]=\"_computedClass()\"\n [variant]=\"variant()\"\n [forceInvalid]=\"forceInvalid()\"\n >\n <ng-content />\n <ng-icon name=\"lucideChevronDown\" class=\"text-muted-foreground text-[length:--spacing(4)]\" />\n </button>\n ", isInline: true, dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: HlmBtn, selector: "button[hlmBtn], a[hlmBtn]", inputs: ["variant", "size", "class", "type"], exportAs: ["hlmBtn"] }, { kind: "directive", type: BrnComboboxAnchor, selector: "[brnComboboxAnchor]" }, { kind: "directive", type: BrnComboboxTrigger, selector: "button[brnComboboxTrigger]", inputs: ["forceInvalid"] }, { kind: "directive", type: BrnComboboxPopoverTrigger, selector: "[brnComboboxPopoverTrigger]", inputs: ["closeOnTriggerClick"] }, { kind: "directive", type: BrnFieldControlDescribedBy, selector: "[brnFieldControlDescribedBy]", inputs: ["aria-describedby"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
418
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: HlmComboboxTrigger, isStandalone: true, selector: "hlm-combobox-trigger", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, buttonId: { classPropertyName: "buttonId", publicName: "buttonId", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, forceInvalid: { classPropertyName: "forceInvalid", publicName: "forceInvalid", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideIcons({ lucideChevronDown })], ngImport: i0, template: "\n <button\n brnComboboxTrigger\n brnComboboxAnchor\n brnComboboxPopoverTrigger\n brnFieldControlDescribedBy\n hlmBtn\n data-slot=\"combobox-trigger\"\n [id]=\"buttonId()\"\n [aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-required]=\"required() || null\"\n [class]=\"_computedClass()\"\n [variant]=\"variant()\"\n [forceInvalid]=\"forceInvalid()\"\n >\n <ng-content />\n <ng-icon name=\"lucideChevronDown\" class=\"text-muted-foreground text-[length:--spacing(4)]\" />\n </button>\n ", isInline: true, dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: HlmBtn, selector: "button[hlmBtn], a[hlmBtn]", inputs: ["variant", "size", "class", "type"], exportAs: ["hlmBtn"] }, { kind: "directive", type: BrnComboboxAnchor, selector: "[brnComboboxAnchor]" }, { kind: "directive", type: BrnComboboxTrigger, selector: "button[brnComboboxTrigger]", inputs: ["forceInvalid"] }, { kind: "directive", type: BrnComboboxPopoverTrigger, selector: "[brnComboboxPopoverTrigger]", inputs: ["closeOnTriggerClick"] }, { kind: "directive", type: BrnFieldControlDescribedBy, selector: "[brnFieldControlDescribedBy]", inputs: ["aria-describedby"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
413
419
|
}
|
|
414
420
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmComboboxTrigger, decorators: [{
|
|
415
421
|
type: Component,
|
|
@@ -425,9 +431,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
425
431
|
],
|
|
426
432
|
providers: [provideIcons({ lucideChevronDown })],
|
|
427
433
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
428
|
-
template: "\n <button\n brnComboboxTrigger\n brnComboboxAnchor\n brnComboboxPopoverTrigger\n brnFieldControlDescribedBy\n hlmBtn\n data-slot=\"combobox-trigger\"\n [id]=\"buttonId()\"\n [class]=\"_computedClass()\"\n [variant]=\"variant()\"\n [forceInvalid]=\"forceInvalid()\"\n >\n <ng-content />\n <ng-icon name=\"lucideChevronDown\" class=\"text-muted-foreground text-[length:--spacing(4)]\" />\n </button>\n ",
|
|
434
|
+
template: "\n <button\n brnComboboxTrigger\n brnComboboxAnchor\n brnComboboxPopoverTrigger\n brnFieldControlDescribedBy\n hlmBtn\n data-slot=\"combobox-trigger\"\n [id]=\"buttonId()\"\n [aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-required]=\"required() || null\"\n [class]=\"_computedClass()\"\n [variant]=\"variant()\"\n [forceInvalid]=\"forceInvalid()\"\n >\n <ng-content />\n <ng-icon name=\"lucideChevronDown\" class=\"text-muted-foreground text-[length:--spacing(4)]\" />\n </button>\n ",
|
|
429
435
|
}]
|
|
430
|
-
}], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], buttonId: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttonId", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], forceInvalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "forceInvalid", required: false }] }] } });
|
|
436
|
+
}], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], buttonId: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttonId", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-describedby", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], forceInvalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "forceInvalid", required: false }] }] } });
|
|
431
437
|
|
|
432
438
|
class HlmComboboxValue {
|
|
433
439
|
constructor() {
|
|
@@ -55,6 +55,9 @@ declare class HlmComboboxInput {
|
|
|
55
55
|
private static _id;
|
|
56
56
|
readonly inputId: i0.InputSignal<string>;
|
|
57
57
|
readonly placeholder: i0.InputSignal<string>;
|
|
58
|
+
/** Native input description IDs, merged with enclosing Spartan field descriptions. */
|
|
59
|
+
readonly ariaDescribedBy: i0.InputSignal<string | null>;
|
|
60
|
+
readonly ariaLabelledBy: i0.InputSignal<string | null>;
|
|
58
61
|
readonly showTrigger: i0.InputSignalWithTransform<boolean, BooleanInput>;
|
|
59
62
|
readonly showClear: i0.InputSignalWithTransform<boolean, BooleanInput>;
|
|
60
63
|
readonly forceInvalid: i0.InputSignalWithTransform<boolean, BooleanInput>;
|
|
@@ -62,7 +65,7 @@ declare class HlmComboboxInput {
|
|
|
62
65
|
readonly ariaInvalidOverride: i0.InputSignalWithTransform<boolean | undefined, BooleanInput>;
|
|
63
66
|
constructor();
|
|
64
67
|
static ɵfac: i0.ɵɵFactoryDeclaration<HlmComboboxInput, never>;
|
|
65
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<HlmComboboxInput, "hlm-combobox-input", never, { "inputId": { "alias": "inputId"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "showTrigger": { "alias": "showTrigger"; "required": false; "isSignal": true; }; "showClear": { "alias": "showClear"; "required": false; "isSignal": true; }; "forceInvalid": { "alias": "forceInvalid"; "required": false; "isSignal": true; }; "ariaInvalidOverride": { "alias": "aria-invalid"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, [{ directive: typeof i1.BrnComboboxAnchor; inputs: {}; outputs: {}; }, { directive: typeof i2$1.HlmInputGroup; inputs: {}; outputs: {}; }]>;
|
|
68
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<HlmComboboxInput, "hlm-combobox-input", never, { "inputId": { "alias": "inputId"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "ariaDescribedBy": { "alias": "aria-describedby"; "required": false; "isSignal": true; }; "ariaLabelledBy": { "alias": "aria-labelledby"; "required": false; "isSignal": true; }; "showTrigger": { "alias": "showTrigger"; "required": false; "isSignal": true; }; "showClear": { "alias": "showClear"; "required": false; "isSignal": true; }; "forceInvalid": { "alias": "forceInvalid"; "required": false; "isSignal": true; }; "ariaInvalidOverride": { "alias": "aria-invalid"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, [{ directive: typeof i1.BrnComboboxAnchor; inputs: {}; outputs: {}; }, { directive: typeof i2$1.HlmInputGroup; inputs: {}; outputs: {}; }]>;
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
declare class HlmComboboxItem {
|
|
@@ -119,10 +122,13 @@ declare class HlmComboboxTrigger {
|
|
|
119
122
|
readonly userClass: i0.InputSignal<ClassValue>;
|
|
120
123
|
protected readonly _computedClass: i0.Signal<string>;
|
|
121
124
|
readonly buttonId: i0.InputSignal<string>;
|
|
125
|
+
/** Native button description IDs, merged with enclosing Spartan field descriptions. */
|
|
126
|
+
readonly ariaDescribedBy: i0.InputSignal<string | null>;
|
|
127
|
+
readonly required: i0.InputSignalWithTransform<boolean, BooleanInput>;
|
|
122
128
|
readonly variant: i0.InputSignal<"default" | "primary" | "secondary" | "success" | "warning" | "danger" | "info" | "light" | "dark" | "accent" | "destructive" | "muted" | "outline" | "link" | "ghost" | null | undefined>;
|
|
123
129
|
readonly forceInvalid: i0.InputSignalWithTransform<boolean, BooleanInput>;
|
|
124
130
|
static ɵfac: i0.ɵɵFactoryDeclaration<HlmComboboxTrigger, never>;
|
|
125
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<HlmComboboxTrigger, "hlm-combobox-trigger", never, { "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "buttonId": { "alias": "buttonId"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "forceInvalid": { "alias": "forceInvalid"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
131
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<HlmComboboxTrigger, "hlm-combobox-trigger", never, { "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "buttonId": { "alias": "buttonId"; "required": false; "isSignal": true; }; "ariaDescribedBy": { "alias": "aria-describedby"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "forceInvalid": { "alias": "forceInvalid"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
126
132
|
}
|
|
127
133
|
|
|
128
134
|
declare class HlmComboboxValue {
|
package/data-table/README.md
CHANGED
|
@@ -108,12 +108,14 @@ export class ServerPaymentsComponent {
|
|
|
108
108
|
[gridColumns]="gridColumns"
|
|
109
109
|
[data]="payments()"
|
|
110
110
|
[enableSelection]="true"
|
|
111
|
+
[getRowId]="paymentId"
|
|
111
112
|
[(layout)]="layout"
|
|
112
113
|
(selectionChange)="onSelect($event)"
|
|
113
114
|
/>
|
|
114
115
|
```
|
|
115
116
|
|
|
116
117
|
- `enableSelection` auto-prepends a checkbox column; `selectionChange` emits the selected rows.
|
|
118
|
+
- Define `readonly paymentId = (payment: Payment): string => payment.id` on the host. `getRowId` is a pure callback returning a unique, stable string per entity; keep the callback reference stable.
|
|
117
119
|
- `[(layout)]` toggles `'table' | 'grid'` (grid requires `gridColumns`). Column `meta: { hideInTable: true }` hides a column in table layout; `meta: { thumbnail: true }` renders it as the card banner.
|
|
118
120
|
- Sortable plain-string headers get an inline sort button; richer headers should use `EgDataTableColumnHeader` via `flexRenderComponent` (no type arguments needed — its `column` input accepts any TanStack column through the `EgSortableColumn` structural type):
|
|
119
121
|
|
|
@@ -126,6 +128,14 @@ helper.accessor('email', {
|
|
|
126
128
|
});
|
|
127
129
|
```
|
|
128
130
|
|
|
131
|
+
### Selection identity and scope
|
|
132
|
+
|
|
133
|
+
- **Stable IDs:** supply `getRowId` whenever rows can be refreshed, reordered, or fetched from a server. Selection follows IDs still present in the supplied rows, and refreshes emit the current objects, not stale snapshots. Removed IDs are discarded and are not selected if they later return. Changing the callback clears selection.
|
|
134
|
+
- **Server pages:** selection is scoped to the loaded slice, not an accumulated cross-page bulk-action set. Replacing a page keeps only selected IDs also in the new slice. Refreshing the same slice preserves selection when IDs match. Passing `null` or an empty slice clears it; keep the current slice and set `isLoading` if selection should survive loading a refresh.
|
|
135
|
+
- **Client filtering/paging:** `filterRows` defines the available selection scope; removing a row there discards its selection. Built-in column filters and client pagination only hide rows: hidden selections remain in `selectionChange`. Header selection toggles only the displayed page; table and grid share selection. The footer selection count describes the filtered subset.
|
|
136
|
+
- **Without `getRowId`:** existing positional `initialRowSelection` keys (`'0'`, `'1'`, etc.) remain supported for the initial rows. Replacing the source array (including a same-record refresh) or changing the `filterRows` result array clears selection rather than transferring an index to a different entity. Built-in sorting/filtering/paging with the same source array preserves it. This intentionally tightens the former unsafe positional behavior.
|
|
137
|
+
- **Updates and outputs:** update row arrays/objects immutably; in-place mutations are not tracked. `initialRowSelection` seeds only IDs present on initialization; later seed changes are ignored. `selectionChange` fires once on initialization, then when the selected objects or their source order change, including removal and refreshed object references. Unchanged selections do not re-emit for sorting, layout, or other unrelated state changes. IDs must be unique and must not be reused for different entities.
|
|
138
|
+
|
|
129
139
|
## API reference
|
|
130
140
|
|
|
131
141
|
| Symbol | Kind |
|
|
@@ -146,7 +156,7 @@ Key `EgDataTable` inputs: `columns` (required), `gridColumns`, `data` (array, or
|
|
|
146
156
|
|
|
147
157
|
Outputs: `selectionChange` (also fires once on init, mirroring React's mount effect), `pageChange`, `pageSizeChange`, `rowClick`, `sortingChange`, `columnFiltersChange`, `filterChange` (toolbar input string), `columnVisibilityChange`.
|
|
148
158
|
|
|
149
|
-
The toolbar (`showToolbar`) always renders the filter input: with `filterColumnId` it filters that column client-side (and `filterChange` mirrors the string); without it, keystrokes only emit `filterChange` so the caller can filter server-side. The column-visibility dropdown is opt-in via `showColumnToggle` (default `false`). `toolbarActions` takes an `ng-template` rendered on the right side of the toolbar row (built-ins stay left) — it renders the row even when `showToolbar` is false.
|
|
159
|
+
The toolbar (`showToolbar`) always renders the filter input: with `filterColumnId` it filters that column client-side (and `filterChange` mirrors the string); without it, keystrokes only emit `filterChange` so the caller can filter server-side. The column-visibility dropdown is opt-in via `showColumnToggle` (default `false`, requires `showToolbar`). `showColumnToggle` also enables hiding: the `Hide` item in `EgDataTableColumnHeader` only renders when it is true (TanStack `column.getCanHide()`), so a hidden column always has a way back via View; per-column `enableHiding: false` disables individual columns even then. `toolbarActions` takes an `ng-template` rendered on the right side of the toolbar row (built-ins stay left) — it renders the row even when `showToolbar` is false.
|
|
150
160
|
|
|
151
161
|
`size` scales spatial density one step down/up (`sm`: `h-8` headers, `p-1` cells, `h-7` buttons; `lg`: `h-12` headers, `p-3` cells, `h-9` buttons; filter input, page-size trigger, and pager meta texts follow). `EgDataTableColumnHeader` (consumer-instantiated via `flexRenderComponent`) takes the same `size` — for reactive updates put every input in `bindings` (`inputBinding('size', ...)`, plus `column`/`title`, since `bindings` cannot mix with `inputs`).
|
|
152
162
|
|