yukit-web 0.0.2 → 0.0.3

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.
@@ -1,10 +1,11 @@
1
1
  import { clsx } from 'clsx';
2
2
  import { twMerge } from 'tailwind-merge';
3
3
  import * as i0 from '@angular/core';
4
- import { forwardRef, signal, Directive, input, booleanAttribute, output, computed, ChangeDetectionStrategy, Component, model, inject, TemplateRef, contentChild, viewChild, ElementRef, contentChildren, effect, numberAttribute } from '@angular/core';
4
+ import { forwardRef, signal, Directive, input, inject, TemplateRef, booleanAttribute, output, computed, contentChildren, ChangeDetectionStrategy, Component, model, contentChild, viewChild, ElementRef, Injector, afterNextRender, effect, numberAttribute, linkedSignal } from '@angular/core';
5
5
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
6
+ import { NgTemplateOutlet } from '@angular/common';
6
7
  import { HlmButton, provideBrnButtonConfig } from 'yukit-web/helm/button';
7
- import { HlmButtonGroup } from 'yukit-web/helm/button-group';
8
+ import { HlmButtonGroup, HlmButtonGroupText } from 'yukit-web/helm/button-group';
8
9
  import * as i2 from 'yukit-web/helm/dropdown-menu';
9
10
  import { HlmDropdownMenuTrigger, HlmDropdownMenuCheckboxIndicator, HlmDropdownMenuRadioIndicator, HlmDropdownMenuItemSubIndicator, HlmDropdownMenu, HlmDropdownMenuItem, HlmDropdownMenuCheckbox, HlmDropdownMenuRadio, HlmDropdownMenuLabel, HlmDropdownMenuGroup, HlmDropdownMenuSeparator, HlmDropdownMenuShortcut, HlmDropdownMenuSub, HlmDropdownMenuSubTrigger } from 'yukit-web/helm/dropdown-menu';
10
11
  import { cva } from 'class-variance-authority';
@@ -15,7 +16,6 @@ import { HlmBadge } from 'yukit-web/helm/badge';
15
16
  import * as i1$1 from 'yukit-web/helm/breadcrumb';
16
17
  import { HlmBreadcrumbImports } from 'yukit-web/helm/breadcrumb';
17
18
  import { HlmPagination, HlmPaginationContent, HlmPaginationItem, HlmPaginationLink, HlmPaginationEllipsis } from 'yukit-web/helm/pagination';
18
- import { NgTemplateOutlet } from '@angular/common';
19
19
  import * as i1$2 from 'yukit-web/helm/combobox';
20
20
  import { HlmComboboxContent, HlmComboboxImports } from 'yukit-web/helm/combobox';
21
21
  import { HlmAccordion, HlmAccordionItem, HlmAccordionTrigger, HlmAccordionContent } from 'yukit-web/helm/accordion';
@@ -169,6 +169,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
169
169
  args: [{}]
170
170
  }] });
171
171
 
172
+ /**
173
+ * Declares the content of a single text segment inside `<yuw-button>`'s button group. Apply it to
174
+ * an `<ng-template>` and pass the id of the `kind: 'text'` entry it belongs to, matching an entry
175
+ * in the `buttons` input.
176
+ *
177
+ * Exists because a text segment often isn't text — a checkbox, a badge, an icon with a count — and a
178
+ * component cannot travel through a data array. An entry whose id has no matching template falls
179
+ * back to its `label`, so the plain-string case needs no template at all.
180
+ *
181
+ * Usage:
182
+ * ```html
183
+ * <yuw-button [buttons]="entries">
184
+ * <ng-template yuwButtonGroupText="select-all">
185
+ * <yuw-checkbox [(checked)]="allSelected" />
186
+ * </ng-template>
187
+ * </yuw-button>
188
+ * ```
189
+ */
190
+ class YuwButtonGroupTextDirective {
191
+ constructor() {
192
+ this.entryId = input.required({ ...(ngDevMode ? { debugName: "entryId" } : /* istanbul ignore next */ {}), alias: 'yuwButtonGroupText' });
193
+ this.templateRef = inject(TemplateRef);
194
+ }
195
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: YuwButtonGroupTextDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
196
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.8", type: YuwButtonGroupTextDirective, isStandalone: true, selector: "ng-template[yuwButtonGroupText]", inputs: { entryId: { classPropertyName: "entryId", publicName: "yuwButtonGroupText", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 }); }
197
+ }
198
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: YuwButtonGroupTextDirective, decorators: [{
199
+ type: Directive,
200
+ args: [{
201
+ selector: 'ng-template[yuwButtonGroupText]',
202
+ }]
203
+ }], propDecorators: { entryId: [{ type: i0.Input, args: [{ isSignal: true, alias: "yuwButtonGroupText", required: true }] }] } });
204
+
172
205
  /**
173
206
  * yukit-web's own class layer for `<yuw-button>`, applied on top of the helm `buttonVariants()`
174
207
  * output. The result lands on the `[class]` binding of the inner `[hlmBtn]` element; the helm
@@ -192,6 +225,16 @@ const yuwButtonVariants = cva('', {
192
225
  destructive: '',
193
226
  link: '',
194
227
  },
228
+ size: {
229
+ default: 'h-10',
230
+ xs: "h-7",
231
+ sm: "h-9",
232
+ lg: 'h-11',
233
+ icon: 'size-10',
234
+ 'icon-xs': "size-7",
235
+ 'icon-sm': 'size-9',
236
+ 'icon-lg': 'size-11',
237
+ },
195
238
  /**
196
239
  * Strips every background/border the helm variants set, covering each modifier stack they
197
240
  * use (`hover:`, `aria-expanded:`, `dark:`, `dark:hover:`, `[a]:hover:`) so `twMerge` drops
@@ -209,6 +252,7 @@ const yuwButtonVariants = cva('', {
209
252
  transparent: false,
210
253
  },
211
254
  });
255
+ const yuwButtonGroupTextClass = 'bg-transparent text-foreground';
212
256
 
213
257
  /**
214
258
  * Button component supporting single action button or grouped button options.
@@ -225,7 +269,9 @@ const yuwButtonVariants = cva('', {
225
269
  * - `class`: Extra CSS classes merged onto the inner button/anchor (single and link mode); merged last, so they win over the component's own classes.
226
270
  * - `yuwDropdownMenuTrigger`: Optional dropdown menu template reference to trigger.
227
271
  * - `ariaLabel`: Accessible label for screen readers.
228
- * - `buttons`: Array of button items (`YuwButtonGroupItemDto[]`) to render as a welded button group.
272
+ * - `buttons`: Array of group entries (`YuwButtonGroupEntryDto[]`) to render as a welded button group.
273
+ * A `kind: 'text'` entry renders a static, non-interactive segment instead of a button; give it an
274
+ * `<ng-template yuwButtonGroupText="<id>">` to fill it with markup rather than a plain `label`.
229
275
  * - `orientation`: Group layout orientation (`'horizontal'` or `'vertical'`). Default is `'horizontal'`.
230
276
  * - `groupAriaLabel`: Accessible name for button group container.
231
277
  *
@@ -248,6 +294,12 @@ const yuwButtonVariants = cva('', {
248
294
  * Button Group Usage:
249
295
  * ```html
250
296
  * <yuw-button [buttons]="actionList" (buttonClicked)="onAction($event)" />
297
+ *
298
+ * <yuw-button [buttons]="[{id: 'caption', kind: 'text', label: 'Sort by'}, ...sortActions]">
299
+ * <ng-template yuwButtonGroupText="select-all">
300
+ * <yuw-checkbox [(checked)]="allSelected" />
301
+ * </ng-template>
302
+ * </yuw-button>
251
303
  * ```
252
304
  */
253
305
  class YuwButtonComponent {
@@ -272,23 +324,39 @@ class YuwButtonComponent {
272
324
  this.userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
273
325
  this.clicked = output();
274
326
  this.buttonClicked = output();
327
+ this._textClass = yuwButtonGroupTextClass;
275
328
  this._isGroup = computed(() => (this.buttons()?.length ?? 0) > 0, ...(ngDevMode ? [{ debugName: "_isGroup" }] : /* istanbul ignore next */ []));
276
329
  this._effectiveVariant = computed(() => this.transparent() ? 'ghost' : this.variant(), ...(ngDevMode ? [{ debugName: "_effectiveVariant" }] : /* istanbul ignore next */ []));
277
330
  /** Classes for the single-mode button/anchor: our variant layer + the consumer's `class`, which goes last so it wins. */
278
- this._btnClass = computed(() => cn(yuwButtonVariants({ variant: this._effectiveVariant(), transparent: this.transparent() }), this.userClass()), ...(ngDevMode ? [{ debugName: "_btnClass" }] : /* istanbul ignore next */ []));
279
- this._resolved = computed(() => (this.buttons() ?? []).map((button) => {
280
- const transparent = button.transparent ?? false;
281
- const variant = transparent ? 'ghost' : (button.variant ?? this.variant());
282
- return {
283
- ...button,
284
- variant,
285
- size: button.size ?? this.size(),
286
- color: button.color ?? this.color(),
287
- disabled: button.disabled ?? false,
288
- type: button.type ?? 'button',
289
- class: yuwButtonVariants({ variant, transparent }),
290
- };
291
- }), ...(ngDevMode ? [{ debugName: "_resolved" }] : /* istanbul ignore next */ []));
331
+ this._btnClass = computed(() => cn(yuwButtonVariants({ size: this.size(), variant: this._effectiveVariant(), transparent: this.transparent() }), this.userClass()), ...(ngDevMode ? [{ debugName: "_btnClass" }] : /* istanbul ignore next */ []));
332
+ this._texts = contentChildren(YuwButtonGroupTextDirective, ...(ngDevMode ? [{ debugName: "_texts" }] : /* istanbul ignore next */ []));
333
+ /**
334
+ * The group's entries, each carrying everything its branch of the template needs.
335
+ *
336
+ * A text entry has no variant/size/color to fall back to, so it takes its own path rather than
337
+ * being run through the button resolver, which would invent fields its DTO doesn't declare. Its
338
+ * content template is looked up through a `Map` rather than a per-entry `.find()`, which would
339
+ * make an n-entry group cost n² comparisons.
340
+ */
341
+ this._resolved = computed(() => {
342
+ const templates = new Map(this._texts().map((text) => [text.entryId(), text.templateRef]));
343
+ return (this.buttons() ?? []).map((entry) => {
344
+ if (entry.kind === 'text') {
345
+ return { ...entry, template: templates.get(entry.id) };
346
+ }
347
+ const transparent = entry.transparent ?? false;
348
+ const variant = transparent ? 'ghost' : (entry.variant ?? this.variant());
349
+ return {
350
+ ...entry,
351
+ variant,
352
+ size: entry.size ?? this.size(),
353
+ color: entry.color ?? this.color(),
354
+ disabled: entry.disabled ?? false,
355
+ type: entry.type ?? 'button',
356
+ class: yuwButtonVariants({ variant, transparent }),
357
+ };
358
+ });
359
+ }, ...(ngDevMode ? [{ debugName: "_resolved" }] : /* istanbul ignore next */ []));
292
360
  }
293
361
  onClick(event) {
294
362
  if (this.disabled()) {
@@ -303,16 +371,16 @@ class YuwButtonComponent {
303
371
  this.buttonClicked.emit({ id: button.id, event });
304
372
  }
305
373
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: YuwButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
306
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: YuwButtonComponent, isStandalone: true, selector: "yuw-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, href: { classPropertyName: "href", publicName: "href", isSignal: true, isRequired: false, transformFunction: null }, target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, transparent: { classPropertyName: "transparent", publicName: "transparent", isSignal: true, isRequired: false, transformFunction: null }, yuwDropdownMenuTrigger: { classPropertyName: "yuwDropdownMenuTrigger", publicName: "yuwDropdownMenuTrigger", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, buttons: { classPropertyName: "buttons", publicName: "buttons", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, groupAriaLabel: { classPropertyName: "groupAriaLabel", publicName: "groupAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked", buttonClicked: "buttonClicked" }, host: { properties: { "class.cursor-not-allowed": "disabled()", "attr.data-yuw-color": "color()" }, classAttribute: "inline-flex" }, ngImport: i0, template: "@if (_isGroup()) {\n <div hlmButtonGroup [orientation]=\"orientation()\" [attr.aria-label]=\"groupAriaLabel()\">\n @for (button of _resolved(); track button.id) {\n @if (button.href && !button.disabled) {\n <a hlmBtn [variant]=\"button.variant\" [size]=\"button.size\" [href]=\"button.href\" [target]=\"button.target\"\n [attr.data-yuw-color]=\"button.color\" [attr.aria-label]=\"button.ariaLabel\"\n (click)=\"onGroupClick(button, $event)\" [hlmDropdownMenuTrigger]=\"button.yuwDropdownMenuTrigger\"\n [class]=\"button.class\">\n {{ button.label }}\n </a>\n } @else {\n <button hlmBtn [variant]=\"button.variant\" [size]=\"button.size\" [type]=\"button.type\" [disabled]=\"button.disabled\"\n [attr.data-yuw-color]=\"button.color\" [attr.aria-label]=\"button.ariaLabel\"\n (click)=\"onGroupClick(button, $event)\" [hlmDropdownMenuTrigger]=\"button.yuwDropdownMenuTrigger\"\n [class]=\"button.class\">\n {{ button.label }}\n </button>\n }\n }\n </div>\n \n} @else if (href(); as hrefValue) {\n <a hlmBtn [variant]=\"_effectiveVariant()\" [size]=\"size()\" [disabled]=\"disabled()\" [href]=\"disabled() ? null : hrefValue\"\n [target]=\"target()\" [attr.aria-label]=\"ariaLabel()\" (click)=\"onClick($event)\"\n [hlmDropdownMenuTrigger]=\"yuwDropdownMenuTrigger()\" [class]=\"_btnClass()\">\n <ng-content select=\"[yuwButtonIcon]\"/>\n <ng-content/>\n </a>\n\n} @else {\n <button hlmBtn [variant]=\"_effectiveVariant()\" [size]=\"size()\" [type]=\"type()\" [disabled]=\"disabled()\"\n [attr.aria-label]=\"ariaLabel()\" (click)=\"onClick($event)\" [hlmDropdownMenuTrigger]=\"yuwDropdownMenuTrigger()\"\n [class]=\"_btnClass()\">\n <ng-content select=\"[yuwButtonIcon]\"/>\n <ng-content/>\n </button>\n}\n", dependencies: [{ kind: "directive", type: HlmButton, selector: "button[hlmBtn], a[hlmBtn]", inputs: ["variant", "size"], exportAs: ["hlmBtn"] }, { kind: "directive", type: HlmButtonGroup, selector: "[hlmButtonGroup],hlm-button-group", inputs: ["orientation"] }, { kind: "directive", type: HlmDropdownMenuTrigger, selector: "[hlmDropdownMenuTrigger]", inputs: ["align", "side"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
374
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: YuwButtonComponent, isStandalone: true, selector: "yuw-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, href: { classPropertyName: "href", publicName: "href", isSignal: true, isRequired: false, transformFunction: null }, target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, transparent: { classPropertyName: "transparent", publicName: "transparent", isSignal: true, isRequired: false, transformFunction: null }, yuwDropdownMenuTrigger: { classPropertyName: "yuwDropdownMenuTrigger", publicName: "yuwDropdownMenuTrigger", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, buttons: { classPropertyName: "buttons", publicName: "buttons", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, groupAriaLabel: { classPropertyName: "groupAriaLabel", publicName: "groupAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked", buttonClicked: "buttonClicked" }, host: { properties: { "class.cursor-not-allowed": "disabled()", "attr.data-yuw-color": "color()" }, classAttribute: "inline-flex" }, queries: [{ propertyName: "_texts", predicate: YuwButtonGroupTextDirective, isSignal: true }], ngImport: i0, template: "@if (_isGroup()) {\n <div hlmButtonGroup [orientation]=\"orientation()\" [attr.aria-label]=\"groupAriaLabel()\">\n @for (button of _resolved(); track button.id) {\n @if (button.kind === 'text') {\n <hlm-button-group-text [class]=\"_textClass\">\n @if (button.template) {\n <ng-container [ngTemplateOutlet]=\"button.template\"/>\n } @else {\n {{ button.label }}\n }\n </hlm-button-group-text>\n\n } @else if (button.href && !button.disabled) {\n <a hlmBtn [variant]=\"button.variant\" [size]=\"button.size\" [href]=\"button.href\" [target]=\"button.target\"\n [attr.data-yuw-color]=\"button.color\" [attr.aria-label]=\"button.ariaLabel\"\n (click)=\"onGroupClick(button, $event)\" [hlmDropdownMenuTrigger]=\"button.yuwDropdownMenuTrigger\"\n [class]=\"button.class\">\n {{ button.label }}\n </a>\n } @else {\n <button hlmBtn [variant]=\"button.variant\" [size]=\"button.size\" [type]=\"button.type\" [disabled]=\"button.disabled\"\n [attr.data-yuw-color]=\"button.color\" [attr.aria-label]=\"button.ariaLabel\"\n (click)=\"onGroupClick(button, $event)\" [hlmDropdownMenuTrigger]=\"button.yuwDropdownMenuTrigger\"\n [class]=\"button.class\">\n {{ button.label }}\n </button>\n }\n }\n </div>\n \n} @else if (href(); as hrefValue) {\n <a hlmBtn [variant]=\"_effectiveVariant()\" [size]=\"size()\" [disabled]=\"disabled()\" [href]=\"disabled() ? null : hrefValue\"\n [target]=\"target()\" [attr.aria-label]=\"ariaLabel()\" (click)=\"onClick($event)\"\n [hlmDropdownMenuTrigger]=\"yuwDropdownMenuTrigger()\" [class]=\"_btnClass()\">\n <ng-content select=\"[yuwButtonIcon]\"/>\n <ng-content/>\n </a>\n\n} @else {\n <button hlmBtn [variant]=\"_effectiveVariant()\" [size]=\"size()\" [type]=\"type()\" [disabled]=\"disabled()\"\n [attr.aria-label]=\"ariaLabel()\" (click)=\"onClick($event)\" [hlmDropdownMenuTrigger]=\"yuwDropdownMenuTrigger()\"\n [class]=\"_btnClass()\">\n <ng-content select=\"[yuwButtonIcon]\"/>\n <ng-content/>\n </button>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: HlmButton, selector: "button[hlmBtn], a[hlmBtn]", inputs: ["variant", "size"], exportAs: ["hlmBtn"] }, { kind: "directive", type: HlmButtonGroup, selector: "[hlmButtonGroup],hlm-button-group", inputs: ["orientation"] }, { kind: "directive", type: HlmButtonGroupText, selector: "[hlmButtonGroupText],hlm-button-group-text" }, { kind: "directive", type: HlmDropdownMenuTrigger, selector: "[hlmDropdownMenuTrigger]", inputs: ["align", "side"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
307
375
  }
308
376
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: YuwButtonComponent, decorators: [{
309
377
  type: Component,
310
- args: [{ selector: 'yuw-button', imports: [HlmButton, HlmButtonGroup, HlmDropdownMenuTrigger], host: {
378
+ args: [{ selector: 'yuw-button', imports: [NgTemplateOutlet, HlmButton, HlmButtonGroup, HlmButtonGroupText, HlmDropdownMenuTrigger], host: {
311
379
  class: 'inline-flex',
312
380
  '[class.cursor-not-allowed]': 'disabled()',
313
381
  '[attr.data-yuw-color]': 'color()',
314
- }, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (_isGroup()) {\n <div hlmButtonGroup [orientation]=\"orientation()\" [attr.aria-label]=\"groupAriaLabel()\">\n @for (button of _resolved(); track button.id) {\n @if (button.href && !button.disabled) {\n <a hlmBtn [variant]=\"button.variant\" [size]=\"button.size\" [href]=\"button.href\" [target]=\"button.target\"\n [attr.data-yuw-color]=\"button.color\" [attr.aria-label]=\"button.ariaLabel\"\n (click)=\"onGroupClick(button, $event)\" [hlmDropdownMenuTrigger]=\"button.yuwDropdownMenuTrigger\"\n [class]=\"button.class\">\n {{ button.label }}\n </a>\n } @else {\n <button hlmBtn [variant]=\"button.variant\" [size]=\"button.size\" [type]=\"button.type\" [disabled]=\"button.disabled\"\n [attr.data-yuw-color]=\"button.color\" [attr.aria-label]=\"button.ariaLabel\"\n (click)=\"onGroupClick(button, $event)\" [hlmDropdownMenuTrigger]=\"button.yuwDropdownMenuTrigger\"\n [class]=\"button.class\">\n {{ button.label }}\n </button>\n }\n }\n </div>\n \n} @else if (href(); as hrefValue) {\n <a hlmBtn [variant]=\"_effectiveVariant()\" [size]=\"size()\" [disabled]=\"disabled()\" [href]=\"disabled() ? null : hrefValue\"\n [target]=\"target()\" [attr.aria-label]=\"ariaLabel()\" (click)=\"onClick($event)\"\n [hlmDropdownMenuTrigger]=\"yuwDropdownMenuTrigger()\" [class]=\"_btnClass()\">\n <ng-content select=\"[yuwButtonIcon]\"/>\n <ng-content/>\n </a>\n\n} @else {\n <button hlmBtn [variant]=\"_effectiveVariant()\" [size]=\"size()\" [type]=\"type()\" [disabled]=\"disabled()\"\n [attr.aria-label]=\"ariaLabel()\" (click)=\"onClick($event)\" [hlmDropdownMenuTrigger]=\"yuwDropdownMenuTrigger()\"\n [class]=\"_btnClass()\">\n <ng-content select=\"[yuwButtonIcon]\"/>\n <ng-content/>\n </button>\n}\n" }]
315
- }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], href: [{ type: i0.Input, args: [{ isSignal: true, alias: "href", required: false }] }], target: [{ type: i0.Input, args: [{ isSignal: true, alias: "target", required: false }] }], transparent: [{ type: i0.Input, args: [{ isSignal: true, alias: "transparent", required: false }] }], yuwDropdownMenuTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "yuwDropdownMenuTrigger", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], buttons: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttons", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], groupAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupAriaLabel", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }], buttonClicked: [{ type: i0.Output, args: ["buttonClicked"] }] } });
382
+ }, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (_isGroup()) {\n <div hlmButtonGroup [orientation]=\"orientation()\" [attr.aria-label]=\"groupAriaLabel()\">\n @for (button of _resolved(); track button.id) {\n @if (button.kind === 'text') {\n <hlm-button-group-text [class]=\"_textClass\">\n @if (button.template) {\n <ng-container [ngTemplateOutlet]=\"button.template\"/>\n } @else {\n {{ button.label }}\n }\n </hlm-button-group-text>\n\n } @else if (button.href && !button.disabled) {\n <a hlmBtn [variant]=\"button.variant\" [size]=\"button.size\" [href]=\"button.href\" [target]=\"button.target\"\n [attr.data-yuw-color]=\"button.color\" [attr.aria-label]=\"button.ariaLabel\"\n (click)=\"onGroupClick(button, $event)\" [hlmDropdownMenuTrigger]=\"button.yuwDropdownMenuTrigger\"\n [class]=\"button.class\">\n {{ button.label }}\n </a>\n } @else {\n <button hlmBtn [variant]=\"button.variant\" [size]=\"button.size\" [type]=\"button.type\" [disabled]=\"button.disabled\"\n [attr.data-yuw-color]=\"button.color\" [attr.aria-label]=\"button.ariaLabel\"\n (click)=\"onGroupClick(button, $event)\" [hlmDropdownMenuTrigger]=\"button.yuwDropdownMenuTrigger\"\n [class]=\"button.class\">\n {{ button.label }}\n </button>\n }\n }\n </div>\n \n} @else if (href(); as hrefValue) {\n <a hlmBtn [variant]=\"_effectiveVariant()\" [size]=\"size()\" [disabled]=\"disabled()\" [href]=\"disabled() ? null : hrefValue\"\n [target]=\"target()\" [attr.aria-label]=\"ariaLabel()\" (click)=\"onClick($event)\"\n [hlmDropdownMenuTrigger]=\"yuwDropdownMenuTrigger()\" [class]=\"_btnClass()\">\n <ng-content select=\"[yuwButtonIcon]\"/>\n <ng-content/>\n </a>\n\n} @else {\n <button hlmBtn [variant]=\"_effectiveVariant()\" [size]=\"size()\" [type]=\"type()\" [disabled]=\"disabled()\"\n [attr.aria-label]=\"ariaLabel()\" (click)=\"onClick($event)\" [hlmDropdownMenuTrigger]=\"yuwDropdownMenuTrigger()\"\n [class]=\"_btnClass()\">\n <ng-content select=\"[yuwButtonIcon]\"/>\n <ng-content/>\n </button>\n}\n" }]
383
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], href: [{ type: i0.Input, args: [{ isSignal: true, alias: "href", required: false }] }], target: [{ type: i0.Input, args: [{ isSignal: true, alias: "target", required: false }] }], transparent: [{ type: i0.Input, args: [{ isSignal: true, alias: "transparent", required: false }] }], yuwDropdownMenuTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "yuwDropdownMenuTrigger", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], buttons: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttons", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], groupAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupAriaLabel", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }], buttonClicked: [{ type: i0.Output, args: ["buttonClicked"] }], _texts: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => YuwButtonGroupTextDirective), { isSignal: true }] }] } });
316
384
 
317
385
  /** Text class for the remove button per helm badge variant, mirroring each variant's own foreground. */
318
386
  const REMOVE_TEXT_CLASS = {
@@ -385,11 +453,11 @@ class YuwBreadcrumbComponent {
385
453
  this.ariaLabel = input.required(...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
386
454
  }
387
455
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: YuwBreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
388
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: YuwBreadcrumbComponent, isStandalone: true, selector: "yuw-breadcrumb", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<nav hlmBreadcrumb [aria-label]=\"ariaLabel()\">\n <ol hlmBreadcrumbList>\n @for (item of items(); track $index; let last = $last) {\n <li hlmBreadcrumbItem>\n @if (last) {\n <span hlmBreadcrumbPage>{{ item.label }}</span>\n } @else if (item.href) {\n <a hlmBreadcrumbLink [link]=\"item.href\">{{ item.label }}</a>\n } @else {\n <span class=\"text-foreground font-normal\">{{ item.label }}</span>\n }\n </li>\n @if (!last) {\n <li hlmBreadcrumbSeparator>{{ separator() }}</li>\n }\n }\n </ol>\n</nav>\n", dependencies: [{ kind: "directive", type: i1$1.HlmBreadcrumb, selector: "[hlmBreadcrumb]", inputs: ["aria-label"] }, { kind: "component", type: i1$1.HlmBreadcrumbSeparator, selector: "[hlmBreadcrumbSeparator]" }, { kind: "directive", type: i1$1.HlmBreadcrumbItem, selector: "[hlmBreadcrumbItem]" }, { kind: "directive", type: i1$1.HlmBreadcrumbLink, selector: "[hlmBreadcrumbLink]", inputs: ["link"] }, { kind: "directive", type: i1$1.HlmBreadcrumbPage, selector: "[hlmBreadcrumbPage]" }, { kind: "directive", type: i1$1.HlmBreadcrumbList, selector: "[hlmBreadcrumbList]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
456
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: YuwBreadcrumbComponent, isStandalone: true, selector: "yuw-breadcrumb", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<nav hlmBreadcrumb [aria-label]=\"ariaLabel()\">\n <ol hlmBreadcrumbList>\n @for (item of items(); track $index; let last = $last) {\n <li hlmBreadcrumbItem>\n @if (last) {\n <span hlmBreadcrumbPage>{{ item.label }}</span>\n } @else if (item.href) {\n <a hlmBreadcrumbLink [link]=\"item.href\">{{ item.label }}</a>\n } @else {\n <span class=\"font-normal\">{{ item.label }}</span>\n }\n </li>\n @if (!last) {\n <li hlmBreadcrumbSeparator>{{ separator() }}</li>\n }\n }\n </ol>\n</nav>\n", dependencies: [{ kind: "directive", type: i1$1.HlmBreadcrumb, selector: "[hlmBreadcrumb]", inputs: ["aria-label"] }, { kind: "component", type: i1$1.HlmBreadcrumbSeparator, selector: "[hlmBreadcrumbSeparator]" }, { kind: "directive", type: i1$1.HlmBreadcrumbItem, selector: "[hlmBreadcrumbItem]" }, { kind: "directive", type: i1$1.HlmBreadcrumbLink, selector: "[hlmBreadcrumbLink]", inputs: ["link"] }, { kind: "directive", type: i1$1.HlmBreadcrumbPage, selector: "[hlmBreadcrumbPage]" }, { kind: "directive", type: i1$1.HlmBreadcrumbList, selector: "[hlmBreadcrumbList]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
389
457
  }
390
458
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: YuwBreadcrumbComponent, decorators: [{
391
459
  type: Component,
392
- args: [{ selector: 'yuw-breadcrumb', imports: [...HlmBreadcrumbImports], changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav hlmBreadcrumb [aria-label]=\"ariaLabel()\">\n <ol hlmBreadcrumbList>\n @for (item of items(); track $index; let last = $last) {\n <li hlmBreadcrumbItem>\n @if (last) {\n <span hlmBreadcrumbPage>{{ item.label }}</span>\n } @else if (item.href) {\n <a hlmBreadcrumbLink [link]=\"item.href\">{{ item.label }}</a>\n } @else {\n <span class=\"text-foreground font-normal\">{{ item.label }}</span>\n }\n </li>\n @if (!last) {\n <li hlmBreadcrumbSeparator>{{ separator() }}</li>\n }\n }\n </ol>\n</nav>\n" }]
460
+ args: [{ selector: 'yuw-breadcrumb', imports: [...HlmBreadcrumbImports], changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav hlmBreadcrumb [aria-label]=\"ariaLabel()\">\n <ol hlmBreadcrumbList>\n @for (item of items(); track $index; let last = $last) {\n <li hlmBreadcrumbItem>\n @if (last) {\n <span hlmBreadcrumbPage>{{ item.label }}</span>\n } @else if (item.href) {\n <a hlmBreadcrumbLink [link]=\"item.href\">{{ item.label }}</a>\n } @else {\n <span class=\"font-normal\">{{ item.label }}</span>\n }\n </li>\n @if (!last) {\n <li hlmBreadcrumbSeparator>{{ separator() }}</li>\n }\n }\n </ol>\n</nav>\n" }]
393
461
  }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: true }] }] } });
394
462
 
395
463
  /**
@@ -887,10 +955,83 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
887
955
  }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- The control is repeated in both branches on purpose: a shared ng-template cannot express it,\n because the two variants differ in a statically-matched directive (hlmInputGroupTextarea vs\n hlmTextarea) and Angular resolves directives at compile time \u2014 a single template would drag the\n group's reset classes into the bare case (or vice versa). Keep the binding lists in sync. -->\n@if (_hasAddons()) {\n<hlm-input-group [class]=\"_classes()\">\n @if (_hasLeftAddon()) {\n <hlm-input-group-addon align=\"block-start\">\n <ng-content select=\"yuw-left-addon\" />\n </hlm-input-group-addon>\n }\n\n <textarea hlmInputGroupTextarea [id]=\"_inputId()\" [value]=\"value()\" [disabled]=\"_disabled()\" [readOnly]=\"readonly()\"\n [required]=\"required()\" [forceInvalid]=\"invalid()\" [attr.rows]=\"rows()\" [attr.maxlength]=\"maxlength()\"\n [attr.name]=\"name()\" [attr.autocomplete]=\"autocomplete()\" [attr.placeholder]=\"placeholder()\"\n (input)=\"onInput($event)\" (change)=\"onChange()\" (blur)=\"_onBlur()\"></textarea>\n\n @if (_hasRightAddon()) {\n <hlm-input-group-addon align=\"block-end\">\n <ng-content select=\"yuw-right-addon\" />\n </hlm-input-group-addon>\n }\n</hlm-input-group>\n} @else {\n<textarea hlmTextarea [class]=\"_classes()\" [id]=\"_inputId()\" [value]=\"value()\" [disabled]=\"_disabled()\" [readOnly]=\"readonly()\" [required]=\"required()\"\n [forceInvalid]=\"invalid()\" [attr.rows]=\"rows()\" [attr.maxlength]=\"maxlength()\" [attr.name]=\"name()\"\n [attr.autocomplete]=\"autocomplete()\" [attr.placeholder]=\"placeholder()\" (input)=\"onInput($event)\"\n (change)=\"onChange()\" (blur)=\"_onBlur()\"></textarea>\n}" }]
888
956
  }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], maxlength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxlength", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], committed: [{ type: i0.Output, args: ["committed"] }], _leftAddons: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => YuwLeftAddonDirective), { isSignal: true }] }], _rightAddons: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => YuwRightAddonDirective), { isSignal: true }] }] } });
889
957
 
958
+ /**
959
+ * Class map for the collapsible wrapper of `yuw-search-input`.
960
+ *
961
+ * The wrapper exists because the component host is `display: contents` and cannot own the animated
962
+ * width itself. Width animates as `max-width`, bound inline from `expandedWidth`, so the same
963
+ * element can be a 32px square when collapsed and the full field when expanded.
964
+ *
965
+ * Deliberately **not** `overflow-hidden`: the helm draws the group's focus ring outside the border
966
+ * box, so clipping here would crop it. The input clips itself instead — see
967
+ * `searchInputFieldVariants`.
968
+ *
969
+ * The anchoring and transition classes hang off `collapseFrom` rather than the cva base, because a
970
+ * non-collapsible instance renders the same wrapper and must stay a plain box with no transition.
971
+ * The anchor is an auto margin rather than `position: absolute`, so the control keeps participating
972
+ * in its parent's layout.
973
+ */
974
+ const searchInputCollapsibleVariants = cva('w-full', {
975
+ variants: {
976
+ collapseFrom: {
977
+ left: 'me-auto transition-[max-width] duration-300 ease-out motion-reduce:transition-none',
978
+ right: 'ms-auto transition-[max-width] duration-300 ease-out motion-reduce:transition-none',
979
+ },
980
+ expanded: {
981
+ true: '',
982
+ false: 'max-w-8',
983
+ },
984
+ },
985
+ defaultVariants: {
986
+ expanded: true,
987
+ },
988
+ });
989
+ /**
990
+ * Classes for the search icon slot.
991
+ *
992
+ * Fixed 32px square, never a share of the wrapper: sizing it with `w-full` would make it track the
993
+ * wrapper mid-transition, sliding the icon toward the middle and snapping it back at the end.
994
+ * `shrink-0` holds that width against the sibling input's `flex-1`.
995
+ *
996
+ * `ps-0` cancels the addon's own `ps-2`, and `has-[>button]:ms-0` cancels the helm's
997
+ * `has-[>button]:ms-[-0.3rem]`; the prefix has to be repeated because tailwind-merge keys conflicts
998
+ * per variant prefix, so a bare `ms-0` would not displace it.
999
+ */
1000
+ const searchInputIconVariants = cva('', {
1001
+ variants: {
1002
+ collapsible: {
1003
+ true: 'w-8 shrink-0 justify-center ps-0 has-[>button]:ms-0',
1004
+ false: '',
1005
+ },
1006
+ },
1007
+ defaultVariants: {
1008
+ collapsible: false,
1009
+ },
1010
+ });
1011
+ /**
1012
+ * Classes for the native input while the control is collapsible.
1013
+ *
1014
+ * The input clips itself so the wrapper doesn't have to (which would take the focus ring with it).
1015
+ * `min-w-0` is what lets it shrink: a flex item defaults to `min-width: auto` and would otherwise
1016
+ * hold the collapsed wrapper open past 32px.
1017
+ */
1018
+ const searchInputFieldVariants = cva('', {
1019
+ variants: {
1020
+ collapsible: {
1021
+ true: 'min-w-0 overflow-hidden',
1022
+ false: '',
1023
+ },
1024
+ },
1025
+ defaultVariants: {
1026
+ collapsible: false,
1027
+ },
1028
+ });
1029
+
890
1030
  /** Module-level counter backing each instance's stable fallback `id`. */
891
1031
  let nextYuwSearchInputId = 0;
892
1032
  /**
893
- * Search input control featuring a leading search icon, optional clear button, and form integration (`ngModel`, `formControlName`).
1033
+ * Search input control featuring a leading search icon, optional clear button, an optional
1034
+ * collapsible mode, and form integration (`ngModel`, `formControlName`).
894
1035
  *
895
1036
  * Inputs / Models:
896
1037
  * - `value`: Two-way search query string.
@@ -903,6 +1044,14 @@ let nextYuwSearchInputId = 0;
903
1044
  * - `autocomplete`: Native `autocomplete` attribute.
904
1045
  * - `placeholder`: Hint text shown inside the empty field.
905
1046
  * - `clearAriaLabel`: Accessible name for the clear button. Supplying it enables the clear button; omit to hide it.
1047
+ * - `collapseFrom`: Enables collapsible mode and picks the anchored edge — `'right'` collapses back
1048
+ * towards the right edge (the field grows leftwards), `'left'` mirrors it. Omit for a field that
1049
+ * is always expanded.
1050
+ * - `expandAriaLabel`: Accessible name for the collapsed search button. Required to make collapsible
1051
+ * mode usable, since the collapsed control has no visible text — user-facing text the consuming
1052
+ * app is responsible for translating.
1053
+ * - `expandedWidth`: CSS width the field animates out to, e.g. `'18rem'`. Default is `'16rem'`.
1054
+ * Collapsible mode only.
906
1055
  * - `class`: Extra classes merged onto the input group.
907
1056
  *
908
1057
  * Outputs:
@@ -910,9 +1059,26 @@ let nextYuwSearchInputId = 0;
910
1059
  * - `committed`: Emits the current query string when the user presses Enter — for searches that
911
1060
  * hit a server on explicit submit rather than on every keystroke.
912
1061
  *
1062
+ * ## Collapsible mode
1063
+ *
1064
+ * Collapsed, the control is a square icon button; activating it animates the same element out to its
1065
+ * expanded width and focuses the field. It collapses again on Escape, or on blur while the query is
1066
+ * empty — a blur with text still in the field keeps the field open, because collapsing would hide a
1067
+ * query the user can no longer see or edit.
1068
+ *
1069
+ * Button and field are one element tree rather than two swapped views, so the transition is
1070
+ * continuous and the input is never re-created mid-animation (which would drop focus and the caret).
1071
+ *
913
1072
  * Usage:
914
1073
  * ```html
915
1074
  * <yuw-search-input [(value)]="query" placeholder="Search items..." clearAriaLabel="Clear search" (searched)="onSearch($event)" />
1075
+ *
1076
+ * <yuw-search-input
1077
+ * [(value)]="query"
1078
+ * collapseFrom="right"
1079
+ * * expandAriaLabel="Open search"
1080
+ * placeholder="Search items..."
1081
+ * />
916
1082
  * ```
917
1083
  */
918
1084
  class YuwSearchInputComponent extends YuwValueAccessorBase {
@@ -928,15 +1094,31 @@ class YuwSearchInputComponent extends YuwValueAccessorBase {
928
1094
  this.autocomplete = input(undefined, ...(ngDevMode ? [{ debugName: "autocomplete" }] : /* istanbul ignore next */ []));
929
1095
  this.placeholder = input(undefined, ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
930
1096
  this.clearAriaLabel = input(undefined, ...(ngDevMode ? [{ debugName: "clearAriaLabel" }] : /* istanbul ignore next */ []));
1097
+ this.collapseFrom = input(undefined, ...(ngDevMode ? [{ debugName: "collapseFrom" }] : /* istanbul ignore next */ []));
1098
+ this.expandedWidth = input('16rem', ...(ngDevMode ? [{ debugName: "expandedWidth" }] : /* istanbul ignore next */ []));
1099
+ this.expandAriaLabel = input(undefined, ...(ngDevMode ? [{ debugName: "expandAriaLabel" }] : /* istanbul ignore next */ []));
931
1100
  this.userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
1101
+ this._open = signal(false, ...(ngDevMode ? [{ debugName: "_open" }] : /* istanbul ignore next */ []));
1102
+ this._autoId = `yuw-search-input-${++nextYuwSearchInputId}`;
1103
+ this._input = viewChild.required('searchInput');
1104
+ this._injector = inject(Injector);
932
1105
  this.searched = output();
933
1106
  this.committed = output();
934
- this._autoId = `yuw-search-input-${++nextYuwSearchInputId}`;
935
1107
  this._inputId = computed(() => this.inputId() ?? this._autoId, ...(ngDevMode ? [{ debugName: "_inputId" }] : /* istanbul ignore next */ []));
936
1108
  this._classes = computed(() => cn(this.userClass()), ...(ngDevMode ? [{ debugName: "_classes" }] : /* istanbul ignore next */ []));
937
1109
  this._disabled = computed(() => this._isDisabled(this.disabled()), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
938
- this._showClear = computed(() => this.clearAriaLabel() !== undefined && this.value().length > 0 && !this._disabled() && !this.readonly(), ...(ngDevMode ? [{ debugName: "_showClear" }] : /* istanbul ignore next */ []));
939
- this._input = viewChild.required('searchInput');
1110
+ this._collapsible = computed(() => this.collapseFrom() !== undefined, ...(ngDevMode ? [{ debugName: "_collapsible" }] : /* istanbul ignore next */ []));
1111
+ this._expanded = computed(() => !this._collapsible() || this._open(), ...(ngDevMode ? [{ debugName: "_expanded" }] : /* istanbul ignore next */ []));
1112
+ this._wrapperClasses = computed(() => searchInputCollapsibleVariants({ collapseFrom: this.collapseFrom(), expanded: this._expanded() }), ...(ngDevMode ? [{ debugName: "_wrapperClasses" }] : /* istanbul ignore next */ []));
1113
+ this._wrapperMaxWidth = computed(() => (this._expanded() ? this.expandedWidth() : null), ...(ngDevMode ? [{ debugName: "_wrapperMaxWidth" }] : /* istanbul ignore next */ []));
1114
+ this._iconClasses = computed(() => searchInputIconVariants({ collapsible: this._collapsible() }), ...(ngDevMode ? [{ debugName: "_iconClasses" }] : /* istanbul ignore next */ []));
1115
+ this._fieldClasses = computed(() => searchInputFieldVariants({ collapsible: this._collapsible() }), ...(ngDevMode ? [{ debugName: "_fieldClasses" }] : /* istanbul ignore next */ []));
1116
+ this._showExpandButton = computed(() => !this._expanded() && !this._disabled(), ...(ngDevMode ? [{ debugName: "_showExpandButton" }] : /* istanbul ignore next */ []));
1117
+ this._showClear = computed(() => this.clearAriaLabel() !== undefined &&
1118
+ this.value().length > 0 &&
1119
+ this._expanded() &&
1120
+ !this._disabled() &&
1121
+ !this.readonly(), ...(ngDevMode ? [{ debugName: "_showClear" }] : /* istanbul ignore next */ []));
940
1122
  }
941
1123
  _emptyValue() {
942
1124
  return '';
@@ -954,15 +1136,50 @@ class YuwSearchInputComponent extends YuwValueAccessorBase {
954
1136
  this.searched.emit('');
955
1137
  this._input().nativeElement.focus();
956
1138
  }
1139
+ /**
1140
+ * Focus is deferred to the next render: while collapsed the input carries `tabindex="-1"` and
1141
+ * `aria-hidden`, and focusing it before those bindings clear would be dropped by the browser.
1142
+ */
1143
+ onExpand() {
1144
+ this._open.set(true);
1145
+ afterNextRender(() => this._input().nativeElement.focus(), { injector: this._injector });
1146
+ }
1147
+ /**
1148
+ * Expands when the collapsed button receives focus, so reaching it with Tab opens the field
1149
+ * instead of leaving the user on a button they must also activate.
1150
+ *
1151
+ * Gated on `:focus-visible` to stay out of a focus loop: collapsing on blur remounts this button,
1152
+ * and if the browser then lands focus on it, an ungated handler would re-expand and the control
1153
+ * could never be tabbed past. `:focus-visible` is set for keyboard traversal but not for the
1154
+ * programmatic/pointer focus that a collapse produces.
1155
+ */
1156
+ onExpandFocus(event) {
1157
+ if (event.target.matches(':focus-visible')) {
1158
+ this.onExpand();
1159
+ }
1160
+ }
1161
+ onEscape() {
1162
+ if (!this._collapsible()) {
1163
+ return;
1164
+ }
1165
+ this._open.set(false);
1166
+ this._input().nativeElement.blur();
1167
+ }
1168
+ onBlur() {
1169
+ this._onBlur();
1170
+ if (this._collapsible() && this.value().length === 0) {
1171
+ this._open.set(false);
1172
+ }
1173
+ }
957
1174
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: YuwSearchInputComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
958
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: YuwSearchInputComponent, isStandalone: true, selector: "yuw-search-input", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, autocomplete: { classPropertyName: "autocomplete", publicName: "autocomplete", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, clearAriaLabel: { classPropertyName: "clearAriaLabel", publicName: "clearAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", searched: "searched", committed: "committed" }, host: { classAttribute: "contents" }, providers: [provideValueAccessor(YuwSearchInputComponent), provideIcons({ lucideSearch, lucideX })], viewQueries: [{ propertyName: "_input", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<hlm-input-group [class]=\"_classes()\">\n <hlm-input-group-addon align=\"inline-start\">\n <ng-icon name=\"lucideSearch\"/>\n </hlm-input-group-addon>\n\n <input\n #searchInput\n hlmInputGroupInput\n [id]=\"_inputId()\"\n type=\"text\"\n inputmode=\"search\"\n [value]=\"value()\"\n [disabled]=\"_disabled()\"\n [readOnly]=\"readonly()\"\n [required]=\"required()\"\n [forceInvalid]=\"invalid()\"\n [attr.name]=\"name()\"\n [attr.autocomplete]=\"autocomplete()\"\n [attr.placeholder]=\"placeholder()\"\n (input)=\"onInput($event)\"\n (keydown.enter)=\"onEnter()\"\n (blur)=\"_onBlur()\"\n />\n\n @if (_showClear()) {\n <hlm-input-group-addon align=\"inline-end\">\n <button hlmInputGroupButton size=\"icon-xs\" [attr.aria-label]=\"clearAriaLabel()\" (click)=\"onClear()\">\n <ng-icon name=\"lucideX\"/>\n </button>\n </hlm-input-group-addon>\n }\n</hlm-input-group>\n", dependencies: [{ kind: "directive", type: i1$3.HlmInputGroup, selector: "[hlmInputGroup],hlm-input-group" }, { kind: "directive", type: i1$3.HlmInputGroupAddon, selector: "[hlmInputGroupAddon],hlm-input-group-addon", inputs: ["align"] }, { kind: "directive", type: i1$3.HlmInputGroupButton, selector: "button[hlmInputGroupButton]", inputs: ["size", "type"] }, { kind: "directive", type: i1$3.HlmInputGroupInput, selector: "input[hlmInputGroupInput]" }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1175
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: YuwSearchInputComponent, isStandalone: true, selector: "yuw-search-input", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, autocomplete: { classPropertyName: "autocomplete", publicName: "autocomplete", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, clearAriaLabel: { classPropertyName: "clearAriaLabel", publicName: "clearAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, collapseFrom: { classPropertyName: "collapseFrom", publicName: "collapseFrom", isSignal: true, isRequired: false, transformFunction: null }, expandedWidth: { classPropertyName: "expandedWidth", publicName: "expandedWidth", isSignal: true, isRequired: false, transformFunction: null }, expandAriaLabel: { classPropertyName: "expandAriaLabel", publicName: "expandAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", searched: "searched", committed: "committed" }, host: { classAttribute: "contents" }, providers: [provideValueAccessor(YuwSearchInputComponent), provideIcons({ lucideSearch, lucideX })], viewQueries: [{ propertyName: "_input", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div [class]=\"_wrapperClasses()\" [style.max-width]=\"_wrapperMaxWidth()\">\n <hlm-input-group [class]=\"_classes()\">\n <hlm-input-group-addon align=\"inline-start\" [class]=\"_iconClasses()\">\n @if (_showExpandButton()) {\n <button\n hlmInputGroupButton\n size=\"icon-sm\"\n class=\"pointer-events-auto\"\n [attr.aria-label]=\"expandAriaLabel()\"\n [attr.aria-expanded]=\"false\"\n (focus)=\"onExpandFocus($event)\"\n (click)=\"onExpand()\"\n >\n <ng-icon name=\"lucideSearch\"/>\n </button>\n } @else {\n <ng-icon name=\"lucideSearch\"/>\n }\n </hlm-input-group-addon>\n\n <input\n #searchInput\n hlmInputGroupInput\n [class]=\"_fieldClasses()\"\n [id]=\"_inputId()\"\n type=\"text\"\n inputmode=\"search\"\n [value]=\"value()\"\n [disabled]=\"_disabled()\"\n [readOnly]=\"readonly()\"\n [required]=\"required()\"\n [forceInvalid]=\"invalid()\"\n [attr.name]=\"name()\"\n [attr.autocomplete]=\"autocomplete()\"\n [attr.placeholder]=\"placeholder()\"\n [attr.tabindex]=\"_expanded() ? null : -1\"\n [attr.aria-hidden]=\"_expanded() ? null : true\"\n (input)=\"onInput($event)\"\n (keydown.enter)=\"onEnter()\"\n (keydown.escape)=\"onEscape()\"\n (blur)=\"onBlur()\"\n />\n\n @if (_showClear()) {\n <hlm-input-group-addon align=\"inline-end\">\n <button hlmInputGroupButton size=\"icon-xs\" [attr.aria-label]=\"clearAriaLabel()\" (click)=\"onClear()\">\n <ng-icon name=\"lucideX\"/>\n </button>\n </hlm-input-group-addon>\n }\n </hlm-input-group>\n</div>\n", dependencies: [{ kind: "directive", type: i1$3.HlmInputGroup, selector: "[hlmInputGroup],hlm-input-group" }, { kind: "directive", type: i1$3.HlmInputGroupAddon, selector: "[hlmInputGroupAddon],hlm-input-group-addon", inputs: ["align"] }, { kind: "directive", type: i1$3.HlmInputGroupButton, selector: "button[hlmInputGroupButton]", inputs: ["size", "type"] }, { kind: "directive", type: i1$3.HlmInputGroupInput, selector: "input[hlmInputGroupInput]" }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
959
1176
  }
960
1177
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: YuwSearchInputComponent, decorators: [{
961
1178
  type: Component,
962
1179
  args: [{ selector: 'yuw-search-input', imports: [...HlmInputGroupImports, NgIcon], providers: [provideValueAccessor(YuwSearchInputComponent), provideIcons({ lucideSearch, lucideX })], host: {
963
1180
  class: 'contents',
964
- }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<hlm-input-group [class]=\"_classes()\">\n <hlm-input-group-addon align=\"inline-start\">\n <ng-icon name=\"lucideSearch\"/>\n </hlm-input-group-addon>\n\n <input\n #searchInput\n hlmInputGroupInput\n [id]=\"_inputId()\"\n type=\"text\"\n inputmode=\"search\"\n [value]=\"value()\"\n [disabled]=\"_disabled()\"\n [readOnly]=\"readonly()\"\n [required]=\"required()\"\n [forceInvalid]=\"invalid()\"\n [attr.name]=\"name()\"\n [attr.autocomplete]=\"autocomplete()\"\n [attr.placeholder]=\"placeholder()\"\n (input)=\"onInput($event)\"\n (keydown.enter)=\"onEnter()\"\n (blur)=\"_onBlur()\"\n />\n\n @if (_showClear()) {\n <hlm-input-group-addon align=\"inline-end\">\n <button hlmInputGroupButton size=\"icon-xs\" [attr.aria-label]=\"clearAriaLabel()\" (click)=\"onClear()\">\n <ng-icon name=\"lucideX\"/>\n </button>\n </hlm-input-group-addon>\n }\n</hlm-input-group>\n" }]
965
- }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], clearAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearAriaLabel", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], searched: [{ type: i0.Output, args: ["searched"] }], committed: [{ type: i0.Output, args: ["committed"] }], _input: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }] } });
1181
+ }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"_wrapperClasses()\" [style.max-width]=\"_wrapperMaxWidth()\">\n <hlm-input-group [class]=\"_classes()\">\n <hlm-input-group-addon align=\"inline-start\" [class]=\"_iconClasses()\">\n @if (_showExpandButton()) {\n <button\n hlmInputGroupButton\n size=\"icon-sm\"\n class=\"pointer-events-auto\"\n [attr.aria-label]=\"expandAriaLabel()\"\n [attr.aria-expanded]=\"false\"\n (focus)=\"onExpandFocus($event)\"\n (click)=\"onExpand()\"\n >\n <ng-icon name=\"lucideSearch\"/>\n </button>\n } @else {\n <ng-icon name=\"lucideSearch\"/>\n }\n </hlm-input-group-addon>\n\n <input\n #searchInput\n hlmInputGroupInput\n [class]=\"_fieldClasses()\"\n [id]=\"_inputId()\"\n type=\"text\"\n inputmode=\"search\"\n [value]=\"value()\"\n [disabled]=\"_disabled()\"\n [readOnly]=\"readonly()\"\n [required]=\"required()\"\n [forceInvalid]=\"invalid()\"\n [attr.name]=\"name()\"\n [attr.autocomplete]=\"autocomplete()\"\n [attr.placeholder]=\"placeholder()\"\n [attr.tabindex]=\"_expanded() ? null : -1\"\n [attr.aria-hidden]=\"_expanded() ? null : true\"\n (input)=\"onInput($event)\"\n (keydown.enter)=\"onEnter()\"\n (keydown.escape)=\"onEscape()\"\n (blur)=\"onBlur()\"\n />\n\n @if (_showClear()) {\n <hlm-input-group-addon align=\"inline-end\">\n <button hlmInputGroupButton size=\"icon-xs\" [attr.aria-label]=\"clearAriaLabel()\" (click)=\"onClear()\">\n <ng-icon name=\"lucideX\"/>\n </button>\n </hlm-input-group-addon>\n }\n </hlm-input-group>\n</div>\n" }]
1182
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], clearAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearAriaLabel", required: false }] }], collapseFrom: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseFrom", required: false }] }], expandedWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedWidth", required: false }] }], expandAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandAriaLabel", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], _input: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }], searched: [{ type: i0.Output, args: ["searched"] }], committed: [{ type: i0.Output, args: ["committed"] }] } });
966
1183
 
967
1184
  /** Lookup replacing the branch, keyed by `` `${boolean}` `` so a boolean signal indexes it
968
1185
  * directly (`STATES[`${masked()}`]`) with no ternary. Adding a third state (a "peek while held"
@@ -2218,6 +2435,10 @@ const YUW_DATATABLE_FEATURES = {
2218
2435
  * User-facing text the consuming app is responsible for translating.
2219
2436
  * - `nextPageLabel` (required): Accessible name for the footer's next-page button. User-facing
2220
2437
  * text the consuming app is responsible for translating.
2438
+ * - `paginationEllipsisSrText` (required): Screen-reader text for the pagination ellipsis.
2439
+ * User-facing text the consuming app is responsible for translating.
2440
+ * - `paginationAriaLabel` (required): Accessible name for the pagination navigation landmark.
2441
+ * User-facing text the consuming app is responsible for translating.
2221
2442
  * - `caption`: Optional `<caption>` text for the table. User-facing text the consuming app is
2222
2443
  * responsible for translating; when absent, no caption is rendered.
2223
2444
  * - `tableClass`: Extra classes merged onto the inner `<table>` element. The host is
@@ -2258,6 +2479,8 @@ class YuwDatatableComponent {
2258
2479
  this.loadingRows = input(5, { ...(ngDevMode ? { debugName: "loadingRows" } : /* istanbul ignore next */ {}), transform: numberAttribute });
2259
2480
  this.previousPageLabel = input.required(...(ngDevMode ? [{ debugName: "previousPageLabel" }] : /* istanbul ignore next */ []));
2260
2481
  this.nextPageLabel = input.required(...(ngDevMode ? [{ debugName: "nextPageLabel" }] : /* istanbul ignore next */ []));
2482
+ this.paginationEllipsisSrText = input.required(...(ngDevMode ? [{ debugName: "paginationEllipsisSrText" }] : /* istanbul ignore next */ []));
2483
+ this.paginationAriaLabel = input.required(...(ngDevMode ? [{ debugName: "paginationAriaLabel" }] : /* istanbul ignore next */ []));
2261
2484
  this.caption = input(undefined, ...(ngDevMode ? [{ debugName: "caption" }] : /* istanbul ignore next */ []));
2262
2485
  this.tableClass = input(undefined, ...(ngDevMode ? [{ debugName: "tableClass" }] : /* istanbul ignore next */ []));
2263
2486
  this.sorting = model([], ...(ngDevMode ? [{ debugName: "sorting" }] : /* istanbul ignore next */ []));
@@ -2265,6 +2488,8 @@ class YuwDatatableComponent {
2265
2488
  this.columnVisibility = model({}, ...(ngDevMode ? [{ debugName: "columnVisibility" }] : /* istanbul ignore next */ []));
2266
2489
  this.rowSelection = model({}, ...(ngDevMode ? [{ debugName: "rowSelection" }] : /* istanbul ignore next */ []));
2267
2490
  this.pagination = model({ pageIndex: 0, pageSize: 10 }, ...(ngDevMode ? [{ debugName: "pagination" }] : /* istanbul ignore next */ []));
2491
+ this.currentPage = linkedSignal({ ...(ngDevMode ? { debugName: "currentPage" } : /* istanbul ignore next */ {}), source: () => this.pagination().pageIndex,
2492
+ computation: (pageIndex) => pageIndex + 1 });
2268
2493
  this._options = () => ({
2269
2494
  features: {
2270
2495
  ...YUW_DATATABLE_FEATURES,
@@ -2306,6 +2531,10 @@ class YuwDatatableComponent {
2306
2531
  desc: 'descending',
2307
2532
  };
2308
2533
  }
2534
+ /** Translates a 1-based page from `<yuw-pagination>` back into TanStack's zero-based state. */
2535
+ onPageChange(page) {
2536
+ this.pagination.update((current) => ({ ...current, pageIndex: page - 1 }));
2537
+ }
2309
2538
  /**
2310
2539
  * Applies the real options once, after inputs are set.
2311
2540
  *
@@ -2325,14 +2554,14 @@ class YuwDatatableComponent {
2325
2554
  this.table.setOptions((previous) => ({ ...previous, ...options }));
2326
2555
  }
2327
2556
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: YuwDatatableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2328
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: YuwDatatableComponent, isStandalone: true, selector: "yuw-datatable", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, paginated: { classPropertyName: "paginated", publicName: "paginated", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingRows: { classPropertyName: "loadingRows", publicName: "loadingRows", isSignal: true, isRequired: false, transformFunction: null }, previousPageLabel: { classPropertyName: "previousPageLabel", publicName: "previousPageLabel", isSignal: true, isRequired: true, transformFunction: null }, nextPageLabel: { classPropertyName: "nextPageLabel", publicName: "nextPageLabel", isSignal: true, isRequired: true, transformFunction: null }, caption: { classPropertyName: "caption", publicName: "caption", isSignal: true, isRequired: false, transformFunction: null }, tableClass: { classPropertyName: "tableClass", publicName: "tableClass", isSignal: true, isRequired: false, transformFunction: null }, sorting: { classPropertyName: "sorting", publicName: "sorting", isSignal: true, isRequired: false, transformFunction: null }, columnFilters: { classPropertyName: "columnFilters", publicName: "columnFilters", isSignal: true, isRequired: false, transformFunction: null }, columnVisibility: { classPropertyName: "columnVisibility", publicName: "columnVisibility", isSignal: true, isRequired: false, transformFunction: null }, rowSelection: { classPropertyName: "rowSelection", publicName: "rowSelection", isSignal: true, isRequired: false, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sorting: "sortingChange", columnFilters: "columnFiltersChange", columnVisibility: "columnVisibilityChange", rowSelection: "rowSelectionChange", pagination: "paginationChange", selectedRowsChange: "selectedRowsChange" }, host: { classAttribute: "contents" }, ngImport: i0, template: "<div yuwTableContainer>\n <table yuwTable [class]=\"_tableClass()\">\n @if (caption()) {\n <caption yuwCaption>{{ caption() }}</caption>\n }\n\n <thead yuwTHead>\n @for (headerGroup of table.getHeaderGroups(); track headerGroup.id) {\n <tr yuwTr>\n @for (header of headerGroup.headers; track header.id) {\n @let sorted = header.column.getIsSorted();\n <th yuwTh scope=\"col\" [attr.colspan]=\"header.colSpan\" [attr.aria-sort]=\"sorted ? _ariaSort[sorted] : null\">\n @if (!header.isPlaceholder) {\n @if (sortable() && header.column.getCanSort()) {\n <button type=\"button\" class=\"inline-flex items-center gap-1 select-none hover:text-foreground/80\"\n (click)=\"header.column.toggleSorting()\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let rendered\">\n {{ rendered }}\n </ng-container>\n\n @if (sorted === 'asc') {\n <span aria-hidden=\"true\">\u2191</span>\n } @else if (sorted === 'desc') {\n <span aria-hidden=\"true\">\u2193</span>\n } @else {\n <span aria-hidden=\"true\">\u2195</span>\n }\n </button>\n } @else {\n <ng-container *flexRender=\"header.column.columnDef.header; props: header.getContext(); let rendered\">\n {{ rendered }}\n </ng-container>\n }\n }\n </th>\n }\n </tr>\n }\n </thead>\n\n <tbody yuwTBody>\n @if (loading()) {\n @for (row of _skeletonRows(); track row) {\n <tr yuwTr>\n @for (column of table.getVisibleLeafColumns(); track column.id) {\n <td yuwTd><span yuwSkeleton class=\"block h-4 w-full\"></span></td>\n }\n </tr>\n }\n } @else if (_isEmpty()) {\n <tr yuwTr>\n <td yuwTd [attr.colspan]=\"_columnCount()\" class=\"h-24 text-center text-muted-foreground\">\n {{ emptyText() ?? '' }}\n </td>\n </tr>\n } @else {\n @for (row of table.getRowModel().rows; track row.id) {\n <tr yuwTr [attr.data-state]=\"row.getIsSelected() ? 'selected' : null\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n <td yuwTd>\n <ng-container *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let rendered\">\n {{ rendered }}\n </ng-container>\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (paginated()) {\n <div class=\"flex items-center justify-between gap-4 py-3\">\n @if (selectable()) {\n <p class=\"text-sm text-muted-foreground\">\n {{ table.getSelectedRowModel().rows.length }} / {{ table.getFilteredRowModel().rows.length }}\n </p>\n } @else {\n <span></span>\n }\n\n <div class=\"flex items-center gap-2\">\n <yuw-button variant=\"ghost\" size=\"sm\" [disabled]=\"loading() || !table.getCanPreviousPage()\"\n [ariaLabel]=\"previousPageLabel()\" (clicked)=\"table.previousPage()\">\n <span aria-hidden=\"true\">&larr;</span>\n </yuw-button>\n <span class=\"text-sm text-muted-foreground\">\n {{ pagination().pageIndex + 1 }} / {{ table.getPageCount() || 1 }}\n </span>\n <yuw-button variant=\"ghost\" size=\"sm\" [disabled]=\"loading() || !table.getCanNextPage()\"\n [ariaLabel]=\"nextPageLabel()\" (clicked)=\"table.nextPage()\">\n <span aria-hidden=\"true\">&rarr;</span>\n </yuw-button>\n </div>\n </div>\n}\n", dependencies: [{ kind: "directive", type: YuwTableContainerDirective, selector: "[yuwTableContainer]" }, { kind: "directive", type: YuwTableDirective, selector: "[yuwTable]" }, { kind: "directive", type: YuwTHeadDirective, selector: "[yuwTHead]" }, { kind: "directive", type: YuwTBodyDirective, selector: "[yuwTBody]" }, { kind: "directive", type: YuwTrDirective, selector: "[yuwTr]" }, { kind: "directive", type: YuwThDirective, selector: "[yuwTh]" }, { kind: "directive", type: YuwTdDirective, selector: "[yuwTd]" }, { kind: "directive", type: YuwCaptionDirective, selector: "[yuwCaption]" }, { kind: "component", type: YuwButtonComponent, selector: "yuw-button", inputs: ["variant", "size", "color", "disabled", "type", "href", "target", "transparent", "yuwDropdownMenuTrigger", "ariaLabel", "buttons", "orientation", "groupAriaLabel", "class"], outputs: ["clicked", "buttonClicked"] }, { kind: "directive", type: YuwSkeletonDirective, selector: "[yuwSkeleton]" }, { kind: "directive", type: FlexRenderDirective, selector: "ng-template[flexRender]", inputs: ["flexRender", "flexRenderProps", "flexRenderInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2557
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: YuwDatatableComponent, isStandalone: true, selector: "yuw-datatable", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, paginated: { classPropertyName: "paginated", publicName: "paginated", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingRows: { classPropertyName: "loadingRows", publicName: "loadingRows", isSignal: true, isRequired: false, transformFunction: null }, previousPageLabel: { classPropertyName: "previousPageLabel", publicName: "previousPageLabel", isSignal: true, isRequired: true, transformFunction: null }, nextPageLabel: { classPropertyName: "nextPageLabel", publicName: "nextPageLabel", isSignal: true, isRequired: true, transformFunction: null }, paginationEllipsisSrText: { classPropertyName: "paginationEllipsisSrText", publicName: "paginationEllipsisSrText", isSignal: true, isRequired: true, transformFunction: null }, paginationAriaLabel: { classPropertyName: "paginationAriaLabel", publicName: "paginationAriaLabel", isSignal: true, isRequired: true, transformFunction: null }, caption: { classPropertyName: "caption", publicName: "caption", isSignal: true, isRequired: false, transformFunction: null }, tableClass: { classPropertyName: "tableClass", publicName: "tableClass", isSignal: true, isRequired: false, transformFunction: null }, sorting: { classPropertyName: "sorting", publicName: "sorting", isSignal: true, isRequired: false, transformFunction: null }, columnFilters: { classPropertyName: "columnFilters", publicName: "columnFilters", isSignal: true, isRequired: false, transformFunction: null }, columnVisibility: { classPropertyName: "columnVisibility", publicName: "columnVisibility", isSignal: true, isRequired: false, transformFunction: null }, rowSelection: { classPropertyName: "rowSelection", publicName: "rowSelection", isSignal: true, isRequired: false, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sorting: "sortingChange", columnFilters: "columnFiltersChange", columnVisibility: "columnVisibilityChange", rowSelection: "rowSelectionChange", pagination: "paginationChange", selectedRowsChange: "selectedRowsChange" }, host: { classAttribute: "contents" }, ngImport: i0, template: "<div yuwTableContainer>\n <table yuwTable [class]=\"_tableClass()\">\n @if (caption()) {\n <caption yuwCaption>{{ caption() }}</caption>\n }\n\n <thead yuwTHead>\n @for (headerGroup of table.getHeaderGroups(); track headerGroup.id) {\n <tr yuwTr>\n @for (header of headerGroup.headers; track header.id) {\n @let sorted = header.column.getIsSorted();\n <th yuwTh scope=\"col\" [attr.colspan]=\"header.colSpan\" [attr.aria-sort]=\"sorted ? _ariaSort[sorted] : null\">\n @if (!header.isPlaceholder) {\n @if (sortable() && header.column.getCanSort()) {\n <button type=\"button\" class=\"inline-flex items-center gap-1 select-none hover:text-foreground/80\"\n (click)=\"header.column.toggleSorting()\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let rendered\">\n {{ rendered }}\n </ng-container>\n\n @if (sorted === 'asc') {\n <span aria-hidden=\"true\">\u2191</span>\n } @else if (sorted === 'desc') {\n <span aria-hidden=\"true\">\u2193</span>\n } @else {\n <span aria-hidden=\"true\">\u2195</span>\n }\n </button>\n } @else {\n <ng-container *flexRender=\"header.column.columnDef.header; props: header.getContext(); let rendered\">\n {{ rendered }}\n </ng-container>\n }\n }\n </th>\n }\n </tr>\n }\n </thead>\n\n <tbody yuwTBody>\n @if (loading()) {\n @for (row of _skeletonRows(); track row) {\n <tr yuwTr>\n @for (column of table.getVisibleLeafColumns(); track column.id) {\n <td yuwTd><span yuwSkeleton class=\"block h-4 w-full\"></span></td>\n }\n </tr>\n }\n } @else if (_isEmpty()) {\n <tr yuwTr>\n <td yuwTd [attr.colspan]=\"_columnCount()\" class=\"h-24 text-center text-muted-foreground\">\n {{ emptyText() ?? '' }}\n </td>\n </tr>\n } @else {\n @for (row of table.getRowModel().rows; track row.id) {\n <tr yuwTr [attr.data-state]=\"row.getIsSelected() ? 'selected' : null\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n <td yuwTd>\n <ng-container *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let rendered\">\n {{ rendered }}\n </ng-container>\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (paginated()) {\n <div class=\"flex items-center justify-between gap-4 py-3\">\n @if (selectable()) {\n <p class=\"text-sm text-muted-foreground\">\n {{ table.getSelectedRowModel().rows.length }} / {{ table.getFilteredRowModel().rows.length }}\n </p>\n } @else {\n <span></span>\n }\n\n <div class=\"flex items-center\">\n <yuw-pagination [page]=\"currentPage()\" (pageChange)=\"onPageChange($event)\"\n [totalPages]=\"table.getPageCount() || 1\" [previousLabel]=\"previousPageLabel()\"\n [nextLabel]=\"nextPageLabel()\" [ellipsisSrText]=\"paginationEllipsisSrText()\"\n [ariaLabel]=\"paginationAriaLabel()\" size=\"icon-sm\"/>\n </div>\n </div>\n}\n", dependencies: [{ kind: "directive", type: YuwTableContainerDirective, selector: "[yuwTableContainer]" }, { kind: "directive", type: YuwTableDirective, selector: "[yuwTable]" }, { kind: "directive", type: YuwTHeadDirective, selector: "[yuwTHead]" }, { kind: "directive", type: YuwTBodyDirective, selector: "[yuwTBody]" }, { kind: "directive", type: YuwTrDirective, selector: "[yuwTr]" }, { kind: "directive", type: YuwThDirective, selector: "[yuwTh]" }, { kind: "directive", type: YuwTdDirective, selector: "[yuwTd]" }, { kind: "directive", type: YuwCaptionDirective, selector: "[yuwCaption]" }, { kind: "component", type: YuwPaginationComponent, selector: "yuw-pagination", inputs: ["page", "totalPages", "previousLabel", "nextLabel", "ellipsisSrText", "size", "color", "ariaLabel"], outputs: ["pageChange"] }, { kind: "directive", type: YuwSkeletonDirective, selector: "[yuwSkeleton]" }, { kind: "directive", type: FlexRenderDirective, selector: "ng-template[flexRender]", inputs: ["flexRender", "flexRenderProps", "flexRenderInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2329
2558
  }
2330
2559
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: YuwDatatableComponent, decorators: [{
2331
2560
  type: Component,
2332
- args: [{ selector: 'yuw-datatable', imports: [...YuwTableImports, YuwButtonComponent, YuwSkeletonDirective, FlexRenderDirective], host: {
2561
+ args: [{ selector: 'yuw-datatable', imports: [...YuwTableImports, YuwPaginationComponent, YuwSkeletonDirective, FlexRenderDirective], host: {
2333
2562
  class: 'contents',
2334
- }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div yuwTableContainer>\n <table yuwTable [class]=\"_tableClass()\">\n @if (caption()) {\n <caption yuwCaption>{{ caption() }}</caption>\n }\n\n <thead yuwTHead>\n @for (headerGroup of table.getHeaderGroups(); track headerGroup.id) {\n <tr yuwTr>\n @for (header of headerGroup.headers; track header.id) {\n @let sorted = header.column.getIsSorted();\n <th yuwTh scope=\"col\" [attr.colspan]=\"header.colSpan\" [attr.aria-sort]=\"sorted ? _ariaSort[sorted] : null\">\n @if (!header.isPlaceholder) {\n @if (sortable() && header.column.getCanSort()) {\n <button type=\"button\" class=\"inline-flex items-center gap-1 select-none hover:text-foreground/80\"\n (click)=\"header.column.toggleSorting()\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let rendered\">\n {{ rendered }}\n </ng-container>\n\n @if (sorted === 'asc') {\n <span aria-hidden=\"true\">\u2191</span>\n } @else if (sorted === 'desc') {\n <span aria-hidden=\"true\">\u2193</span>\n } @else {\n <span aria-hidden=\"true\">\u2195</span>\n }\n </button>\n } @else {\n <ng-container *flexRender=\"header.column.columnDef.header; props: header.getContext(); let rendered\">\n {{ rendered }}\n </ng-container>\n }\n }\n </th>\n }\n </tr>\n }\n </thead>\n\n <tbody yuwTBody>\n @if (loading()) {\n @for (row of _skeletonRows(); track row) {\n <tr yuwTr>\n @for (column of table.getVisibleLeafColumns(); track column.id) {\n <td yuwTd><span yuwSkeleton class=\"block h-4 w-full\"></span></td>\n }\n </tr>\n }\n } @else if (_isEmpty()) {\n <tr yuwTr>\n <td yuwTd [attr.colspan]=\"_columnCount()\" class=\"h-24 text-center text-muted-foreground\">\n {{ emptyText() ?? '' }}\n </td>\n </tr>\n } @else {\n @for (row of table.getRowModel().rows; track row.id) {\n <tr yuwTr [attr.data-state]=\"row.getIsSelected() ? 'selected' : null\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n <td yuwTd>\n <ng-container *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let rendered\">\n {{ rendered }}\n </ng-container>\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (paginated()) {\n <div class=\"flex items-center justify-between gap-4 py-3\">\n @if (selectable()) {\n <p class=\"text-sm text-muted-foreground\">\n {{ table.getSelectedRowModel().rows.length }} / {{ table.getFilteredRowModel().rows.length }}\n </p>\n } @else {\n <span></span>\n }\n\n <div class=\"flex items-center gap-2\">\n <yuw-button variant=\"ghost\" size=\"sm\" [disabled]=\"loading() || !table.getCanPreviousPage()\"\n [ariaLabel]=\"previousPageLabel()\" (clicked)=\"table.previousPage()\">\n <span aria-hidden=\"true\">&larr;</span>\n </yuw-button>\n <span class=\"text-sm text-muted-foreground\">\n {{ pagination().pageIndex + 1 }} / {{ table.getPageCount() || 1 }}\n </span>\n <yuw-button variant=\"ghost\" size=\"sm\" [disabled]=\"loading() || !table.getCanNextPage()\"\n [ariaLabel]=\"nextPageLabel()\" (clicked)=\"table.nextPage()\">\n <span aria-hidden=\"true\">&rarr;</span>\n </yuw-button>\n </div>\n </div>\n}\n" }]
2335
- }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], paginated: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginated", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingRows", required: false }] }], previousPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousPageLabel", required: true }] }], nextPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextPageLabel", required: true }] }], caption: [{ type: i0.Input, args: [{ isSignal: true, alias: "caption", required: false }] }], tableClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableClass", required: false }] }], sorting: [{ type: i0.Input, args: [{ isSignal: true, alias: "sorting", required: false }] }, { type: i0.Output, args: ["sortingChange"] }], columnFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnFilters", required: false }] }, { type: i0.Output, args: ["columnFiltersChange"] }], columnVisibility: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnVisibility", required: false }] }, { type: i0.Output, args: ["columnVisibilityChange"] }], rowSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowSelection", required: false }] }, { type: i0.Output, args: ["rowSelectionChange"] }], pagination: [{ type: i0.Input, args: [{ isSignal: true, alias: "pagination", required: false }] }, { type: i0.Output, args: ["paginationChange"] }], selectedRowsChange: [{ type: i0.Output, args: ["selectedRowsChange"] }] } });
2563
+ }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div yuwTableContainer>\n <table yuwTable [class]=\"_tableClass()\">\n @if (caption()) {\n <caption yuwCaption>{{ caption() }}</caption>\n }\n\n <thead yuwTHead>\n @for (headerGroup of table.getHeaderGroups(); track headerGroup.id) {\n <tr yuwTr>\n @for (header of headerGroup.headers; track header.id) {\n @let sorted = header.column.getIsSorted();\n <th yuwTh scope=\"col\" [attr.colspan]=\"header.colSpan\" [attr.aria-sort]=\"sorted ? _ariaSort[sorted] : null\">\n @if (!header.isPlaceholder) {\n @if (sortable() && header.column.getCanSort()) {\n <button type=\"button\" class=\"inline-flex items-center gap-1 select-none hover:text-foreground/80\"\n (click)=\"header.column.toggleSorting()\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let rendered\">\n {{ rendered }}\n </ng-container>\n\n @if (sorted === 'asc') {\n <span aria-hidden=\"true\">\u2191</span>\n } @else if (sorted === 'desc') {\n <span aria-hidden=\"true\">\u2193</span>\n } @else {\n <span aria-hidden=\"true\">\u2195</span>\n }\n </button>\n } @else {\n <ng-container *flexRender=\"header.column.columnDef.header; props: header.getContext(); let rendered\">\n {{ rendered }}\n </ng-container>\n }\n }\n </th>\n }\n </tr>\n }\n </thead>\n\n <tbody yuwTBody>\n @if (loading()) {\n @for (row of _skeletonRows(); track row) {\n <tr yuwTr>\n @for (column of table.getVisibleLeafColumns(); track column.id) {\n <td yuwTd><span yuwSkeleton class=\"block h-4 w-full\"></span></td>\n }\n </tr>\n }\n } @else if (_isEmpty()) {\n <tr yuwTr>\n <td yuwTd [attr.colspan]=\"_columnCount()\" class=\"h-24 text-center text-muted-foreground\">\n {{ emptyText() ?? '' }}\n </td>\n </tr>\n } @else {\n @for (row of table.getRowModel().rows; track row.id) {\n <tr yuwTr [attr.data-state]=\"row.getIsSelected() ? 'selected' : null\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n <td yuwTd>\n <ng-container *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let rendered\">\n {{ rendered }}\n </ng-container>\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (paginated()) {\n <div class=\"flex items-center justify-between gap-4 py-3\">\n @if (selectable()) {\n <p class=\"text-sm text-muted-foreground\">\n {{ table.getSelectedRowModel().rows.length }} / {{ table.getFilteredRowModel().rows.length }}\n </p>\n } @else {\n <span></span>\n }\n\n <div class=\"flex items-center\">\n <yuw-pagination [page]=\"currentPage()\" (pageChange)=\"onPageChange($event)\"\n [totalPages]=\"table.getPageCount() || 1\" [previousLabel]=\"previousPageLabel()\"\n [nextLabel]=\"nextPageLabel()\" [ellipsisSrText]=\"paginationEllipsisSrText()\"\n [ariaLabel]=\"paginationAriaLabel()\" size=\"icon-sm\"/>\n </div>\n </div>\n}\n" }]
2564
+ }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], paginated: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginated", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingRows", required: false }] }], previousPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousPageLabel", required: true }] }], nextPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextPageLabel", required: true }] }], paginationEllipsisSrText: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginationEllipsisSrText", required: true }] }], paginationAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginationAriaLabel", required: true }] }], caption: [{ type: i0.Input, args: [{ isSignal: true, alias: "caption", required: false }] }], tableClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableClass", required: false }] }], sorting: [{ type: i0.Input, args: [{ isSignal: true, alias: "sorting", required: false }] }, { type: i0.Output, args: ["sortingChange"] }], columnFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnFilters", required: false }] }, { type: i0.Output, args: ["columnFiltersChange"] }], columnVisibility: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnVisibility", required: false }] }, { type: i0.Output, args: ["columnVisibilityChange"] }], rowSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowSelection", required: false }] }, { type: i0.Output, args: ["rowSelectionChange"] }], pagination: [{ type: i0.Input, args: [{ isSignal: true, alias: "pagination", required: false }] }, { type: i0.Output, args: ["paginationChange"] }], selectedRowsChange: [{ type: i0.Output, args: ["selectedRowsChange"] }] } });
2336
2565
 
2337
2566
  /**
2338
2567
  * Displays a user or entity avatar with optional image, fallback text/initials, status badge, or overlapping group layout.
@@ -3471,13 +3700,13 @@ class YuwSidebarTriggerComponent {
3471
3700
  this.label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
3472
3701
  }
3473
3702
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: YuwSidebarTriggerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3474
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.8", type: YuwSidebarTriggerComponent, isStandalone: true, selector: "yuw-sidebar-trigger", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "contents" }, ngImport: i0, template: "<button hlmSidebarTrigger [srOnlyText]=\"label()\"></button>\n", dependencies: [{ kind: "component", type: HlmSidebarTrigger, selector: "button[hlmSidebarTrigger]", inputs: ["srOnlyText"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3703
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.8", type: YuwSidebarTriggerComponent, isStandalone: true, selector: "yuw-sidebar-trigger", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "contents" }, ngImport: i0, template: "<button hlmSidebarTrigger [srOnlyText]=\"label()\" class=\"cursor-pointer\"></button>\n", dependencies: [{ kind: "component", type: HlmSidebarTrigger, selector: "button[hlmSidebarTrigger]", inputs: ["srOnlyText"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3475
3704
  }
3476
3705
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: YuwSidebarTriggerComponent, decorators: [{
3477
3706
  type: Component,
3478
3707
  args: [{ selector: 'yuw-sidebar-trigger', imports: [HlmSidebarTrigger], host: {
3479
3708
  class: 'contents',
3480
- }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<button hlmSidebarTrigger [srOnlyText]=\"label()\"></button>\n" }]
3709
+ }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<button hlmSidebarTrigger [srOnlyText]=\"label()\" class=\"cursor-pointer\"></button>\n" }]
3481
3710
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }] } });
3482
3711
 
3483
3712
  /**
@@ -3746,5 +3975,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
3746
3975
  * Generated bundle index. Do not edit.
3747
3976
  */
3748
3977
 
3749
- export { YUW_DATATABLE_FEATURES, YUW_PASSWORD_REVEAL_STATES, YuwAccordion, YuwAccordionContentComponent, YuwAccordionImports, YuwAccordionItem, YuwAccordionTriggerComponent, YuwAlertActionDirective, YuwAlertComponent, YuwAlertDescriptionDirective, YuwAlertDialogComponent, YuwAlertDialogMediaDirective, YuwAlertDialogTriggerDirective, YuwAlertImports, YuwAlertTitleDirective, YuwAttachmentActionDirective, YuwAttachmentActionsDirective, YuwAttachmentContentDirective, YuwAttachmentDescriptionDirective, YuwAttachmentDirective, YuwAttachmentGroupDirective, YuwAttachmentMediaDirective, YuwAttachmentTitleDirective, YuwAttachmentTriggerDirective, YuwAvatarComponent, YuwBadgeComponent, YuwBreadcrumbComponent, YuwButtonComponent, YuwCaptionDirective, YuwCardActionDirective, YuwCardContentDirective, YuwCardDescriptionDirective, YuwCardDirective, YuwCardFooterDirective, YuwCardHeaderDirective, YuwCardImports, YuwCardTitleDirective, YuwCheckboxComponent, YuwComboboxComponent, YuwComboboxItemDirective, YuwContextMenuCheckboxDirective, YuwContextMenuCheckboxIndicatorComponent, YuwContextMenuDirective, YuwContextMenuGroupDirective, YuwContextMenuImports, YuwContextMenuItemDirective, YuwContextMenuLabelDirective, YuwContextMenuRadioDirective, YuwContextMenuRadioIndicatorComponent, YuwContextMenuSeparatorDirective, YuwContextMenuShortcutDirective, YuwContextMenuSubDirective, YuwContextMenuSubIndicatorComponent, YuwContextMenuSubTriggerDirective, YuwContextMenuTriggerDirective, YuwDatatableComponent, YuwDatepickerComponent, YuwDialogCloseDirective, YuwDialogComponent, YuwDialogFooterDirective, YuwDialogImports, YuwDialogTriggerDirective, YuwEmptyContentDirective, YuwEmptyDescriptionDirective, YuwEmptyDirective, YuwEmptyHeaderDirective, YuwEmptyImports, YuwEmptyMediaDirective, YuwEmptyTitleDirective, YuwInputAddonImports, YuwInputComponent, YuwInputOtpComponent, YuwItemActionsDirective, YuwItemContentDirective, YuwItemDescriptionDirective, YuwItemDirective, YuwItemFooterDirective, YuwItemGroupDirective, YuwItemHeaderDirective, YuwItemImports, YuwItemMediaDirective, YuwItemSeparatorDirective, YuwItemTitleDirective, YuwLabel, YuwLeftAddonDirective, YuwPaginationComponent, YuwPasswordInputComponent, YuwPopoverComponent, YuwPopoverTriggerDirective, YuwRadioGroupComponent, YuwRightAddonDirective, YuwSearchInputComponent, YuwSheetCloseDirective, YuwSheetComponent, YuwSheetFooterDirective, YuwSheetImports, YuwSheetTriggerDirective, YuwSidebarComponent, YuwSidebarContentDirective, YuwSidebarFooterDirective, YuwSidebarGroupContentDirective, YuwSidebarGroupDirective, YuwSidebarGroupLabelDirective, YuwSidebarHeaderDirective, YuwSidebarInsetDirective, YuwSidebarMenuBadgeDirective, YuwSidebarMenuButtonDirective, YuwSidebarMenuDirective, YuwSidebarMenuItemDirective, YuwSidebarMenuSubButtonDirective, YuwSidebarMenuSubDirective, YuwSidebarMenuSubItemDirective, YuwSidebarRailDirective, YuwSidebarSeparatorDirective, YuwSidebarTriggerComponent, YuwSidebarWrapperDirective, YuwSkeletonDirective, YuwSpinnerComponent, YuwSwitchComponent, YuwTBodyDirective, YuwTFootDirective, YuwTHeadDirective, YuwTabPanelDirective, YuwTableContainerDirective, YuwTableDirective, YuwTableImports, YuwTabsComponent, YuwTdDirective, YuwTextareaComponent, YuwThDirective, YuwToasterComponent, YuwTooltipDirective, YuwTrDirective, YuwValueAccessorBase, buildInputOtpGroups, cn, getPaginationRange, provideValueAccessor, totalInputOtpLength };
3978
+ export { YUW_DATATABLE_FEATURES, YUW_PASSWORD_REVEAL_STATES, YuwAccordion, YuwAccordionContentComponent, YuwAccordionImports, YuwAccordionItem, YuwAccordionTriggerComponent, YuwAlertActionDirective, YuwAlertComponent, YuwAlertDescriptionDirective, YuwAlertDialogComponent, YuwAlertDialogMediaDirective, YuwAlertDialogTriggerDirective, YuwAlertImports, YuwAlertTitleDirective, YuwAttachmentActionDirective, YuwAttachmentActionsDirective, YuwAttachmentContentDirective, YuwAttachmentDescriptionDirective, YuwAttachmentDirective, YuwAttachmentGroupDirective, YuwAttachmentMediaDirective, YuwAttachmentTitleDirective, YuwAttachmentTriggerDirective, YuwAvatarComponent, YuwBadgeComponent, YuwBreadcrumbComponent, YuwButtonComponent, YuwButtonGroupTextDirective, YuwCaptionDirective, YuwCardActionDirective, YuwCardContentDirective, YuwCardDescriptionDirective, YuwCardDirective, YuwCardFooterDirective, YuwCardHeaderDirective, YuwCardImports, YuwCardTitleDirective, YuwCheckboxComponent, YuwComboboxComponent, YuwComboboxItemDirective, YuwContextMenuCheckboxDirective, YuwContextMenuCheckboxIndicatorComponent, YuwContextMenuDirective, YuwContextMenuGroupDirective, YuwContextMenuImports, YuwContextMenuItemDirective, YuwContextMenuLabelDirective, YuwContextMenuRadioDirective, YuwContextMenuRadioIndicatorComponent, YuwContextMenuSeparatorDirective, YuwContextMenuShortcutDirective, YuwContextMenuSubDirective, YuwContextMenuSubIndicatorComponent, YuwContextMenuSubTriggerDirective, YuwContextMenuTriggerDirective, YuwDatatableComponent, YuwDatepickerComponent, YuwDialogCloseDirective, YuwDialogComponent, YuwDialogFooterDirective, YuwDialogImports, YuwDialogTriggerDirective, YuwEmptyContentDirective, YuwEmptyDescriptionDirective, YuwEmptyDirective, YuwEmptyHeaderDirective, YuwEmptyImports, YuwEmptyMediaDirective, YuwEmptyTitleDirective, YuwInputAddonImports, YuwInputComponent, YuwInputOtpComponent, YuwItemActionsDirective, YuwItemContentDirective, YuwItemDescriptionDirective, YuwItemDirective, YuwItemFooterDirective, YuwItemGroupDirective, YuwItemHeaderDirective, YuwItemImports, YuwItemMediaDirective, YuwItemSeparatorDirective, YuwItemTitleDirective, YuwLabel, YuwLeftAddonDirective, YuwPaginationComponent, YuwPasswordInputComponent, YuwPopoverComponent, YuwPopoverTriggerDirective, YuwRadioGroupComponent, YuwRightAddonDirective, YuwSearchInputComponent, YuwSheetCloseDirective, YuwSheetComponent, YuwSheetFooterDirective, YuwSheetImports, YuwSheetTriggerDirective, YuwSidebarComponent, YuwSidebarContentDirective, YuwSidebarFooterDirective, YuwSidebarGroupContentDirective, YuwSidebarGroupDirective, YuwSidebarGroupLabelDirective, YuwSidebarHeaderDirective, YuwSidebarInsetDirective, YuwSidebarMenuBadgeDirective, YuwSidebarMenuButtonDirective, YuwSidebarMenuDirective, YuwSidebarMenuItemDirective, YuwSidebarMenuSubButtonDirective, YuwSidebarMenuSubDirective, YuwSidebarMenuSubItemDirective, YuwSidebarRailDirective, YuwSidebarSeparatorDirective, YuwSidebarTriggerComponent, YuwSidebarWrapperDirective, YuwSkeletonDirective, YuwSpinnerComponent, YuwSwitchComponent, YuwTBodyDirective, YuwTFootDirective, YuwTHeadDirective, YuwTabPanelDirective, YuwTableContainerDirective, YuwTableDirective, YuwTableImports, YuwTabsComponent, YuwTdDirective, YuwTextareaComponent, YuwThDirective, YuwToasterComponent, YuwTooltipDirective, YuwTrDirective, YuwValueAccessorBase, buildInputOtpGroups, cn, getPaginationRange, provideValueAccessor, totalInputOtpLength };
3750
3979
  //# sourceMappingURL=yukit-web.mjs.map