@besaitech/ng-design-system 0.0.54 → 0.0.55

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.
@@ -119,6 +119,10 @@ const SDS_ICONS = {
119
119
  'grip-vertical': '<circle cx="9" cy="12" r="1"/><circle cx="9" cy="5" r="1"/><circle cx="9" cy="19" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="15" cy="5" r="1"/><circle cx="15" cy="19" r="1"/>',
120
120
  'upload': '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" x2="12" y1="3" y2="15"/>',
121
121
  'image': '<rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/>',
122
+ // A photo that failed or is missing (sds-avatar fallbacks, photo trays).
123
+ 'image-off': '<line x1="2" x2="22" y1="2" y2="22"/><path d="M10.41 10.41a2 2 0 1 1-2.83-2.83"/><line x1="13.5" x2="6" y1="13.5" y2="21"/><line x1="18" x2="21" y1="12" y2="15"/><path d="M3.59 3.59A1.99 1.99 0 0 0 3 5v14a2 2 0 0 0 2 2h14c.55 0 1.052-.22 1.41-.59"/><path d="M21 15V5a2 2 0 0 0-2-2H9"/>',
124
+ // Fork and knife: the placeholder of a menu item without a photo.
125
+ 'utensils': '<path d="M3 2v7c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2V2"/><path d="M7 2v20"/><path d="M21 15V2a5 5 0 0 0-5 5v6c0 1.1.9 2 2 2h3Zm0 0v7"/>',
122
126
  'zoom-in': '<circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/><line x1="11" x2="11" y1="8" y2="14"/><line x1="8" x2="14" y1="11" y2="11"/>',
123
127
  'zoom-out': '<circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/><line x1="8" x2="14" y1="11" y2="11"/>',
124
128
  'trash-2': '<path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/>',
@@ -218,7 +222,7 @@ const VARIANTS$2 = {
218
222
  'dark:hover:bg-white/10 dark:active:bg-white/15',
219
223
  danger: 'bg-danger text-white shadow-sm hover:brightness-95 active:brightness-90',
220
224
  };
221
- const SIZES$b = {
225
+ const SIZES$c = {
222
226
  sm: 'h-8 px-3 text-sm',
223
227
  md: 'h-10 px-4 text-sm',
224
228
  lg: 'h-12 px-5 text-base',
@@ -278,7 +282,7 @@ class SdsButton {
278
282
  BASE,
279
283
  this.shape() === 'pill' ? 'rounded-full' : 'rounded-control',
280
284
  VARIANTS$2[this.variant()],
281
- SIZES$b[size],
285
+ SIZES$c[size],
282
286
  this.iconOnly() ? ICON_ONLY[size] : '',
283
287
  this.block() ? 'w-full' : '',
284
288
  ]
@@ -330,8 +334,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
330
334
  `, styles: [".sds-spinner{width:1em;height:1em;border:2px solid currentColor;border-right-color:transparent;border-radius:9999px;animation:sds-spin .6s linear infinite}@keyframes sds-spin{to{transform:rotate(360deg)}}\n"] }]
331
335
  }], ctorParameters: () => [], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], block: [{ type: i0.Input, args: [{ isSignal: true, alias: "block", required: false }] }], iconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnly", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
332
336
 
333
- let nextId$l = 0;
334
- const SIZES$a = {
337
+ let nextId$n = 0;
338
+ const SIZES$b = {
335
339
  sm: 'h-9 text-sm',
336
340
  md: 'h-10 text-sm',
337
341
  lg: 'h-12 text-base',
@@ -343,7 +347,7 @@ const SIZES$a = {
343
347
  * sidebar search field).
344
348
  */
345
349
  class SdsInput {
346
- inputId = `sds-input-${nextId$l++}`;
350
+ inputId = `sds-input-${nextId$n++}`;
347
351
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
348
352
  /** Accessible name used when no visible [label] is provided. */
349
353
  ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
@@ -427,7 +431,7 @@ class SdsInput {
427
431
  return `${left} ${right}`;
428
432
  }, ...(ngDevMode ? [{ debugName: "fieldPadding" }] : []));
429
433
  wrapperClasses = computed(() => {
430
- const parts = [SIZES$a[this.size()], this.pill() ? 'rounded-full' : 'rounded-control'];
434
+ const parts = [SIZES$b[this.size()], this.pill() ? 'rounded-full' : 'rounded-control'];
431
435
  if (this.isDisabled())
432
436
  parts.push('opacity-60 cursor-not-allowed');
433
437
  if (this.filled()) {
@@ -675,7 +679,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
675
679
  `, styles: ["input:-webkit-autofill,input:-webkit-autofill:hover,input:-webkit-autofill:focus,input:-webkit-autofill:active{transition:background-color 100000s ease-in-out 0s;-webkit-text-fill-color:currentColor;caret-color:currentColor}\n"] }]
676
680
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], leadingIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "leadingIcon", required: false }] }], trailingIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailingIcon", required: false }] }], trailingAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailingAction", required: false }] }], trailingActionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailingActionLabel", required: false }] }], trailingActionPressed: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailingActionPressed", required: false }] }], trailingActionClick: [{ type: i0.Output, args: ["trailingActionClick"] }] } });
677
681
 
678
- let nextId$k = 0;
682
+ let nextId$m = 0;
679
683
  const HEIGHTS = {
680
684
  sm: 'h-9',
681
685
  md: 'h-10',
@@ -703,7 +707,7 @@ const TEXT = {
703
707
  * with "." ("1.234" in nl-BE) stays a thousands separator.
704
708
  */
705
709
  class SdsInputNumber {
706
- inputId = `sds-input-number-${nextId$k++}`;
710
+ inputId = `sds-input-number-${nextId$m++}`;
707
711
  fieldRef = viewChild('field', ...(ngDevMode ? [{ debugName: "fieldRef" }] : []));
708
712
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
709
713
  /** Accessible name used when no visible [label] is provided. */
@@ -1470,7 +1474,577 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
1470
1474
  }]
1471
1475
  }], ctorParameters: () => [], propDecorators: { fieldRef: [{ type: i0.ViewChild, args: ['field', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], ariaDescribedby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedby", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], currency: [{ type: i0.Input, args: [{ isSignal: true, alias: "currency", required: false }] }], currencyDisplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "currencyDisplay", required: false }] }], useGrouping: [{ type: i0.Input, args: [{ isSignal: true, alias: "useGrouping", required: false }] }], minFractionDigits: [{ type: i0.Input, args: [{ isSignal: true, alias: "minFractionDigits", required: false }] }], maxFractionDigits: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFractionDigits", required: false }] }], prefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefix", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], showButtons: [{ type: i0.Input, args: [{ isSignal: true, alias: "showButtons", required: false }] }], buttonLayout: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttonLayout", required: false }] }], incrementIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "incrementIcon", required: false }] }], decrementIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "decrementIcon", required: false }] }], allowEmpty: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowEmpty", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], incrementLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "incrementLabel", required: false }] }], decrementLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "decrementLabel", required: false }] }] } });
1472
1476
 
1473
- let nextId$j = 0;
1477
+ let nextId$l = 0;
1478
+ const SIZES$a = {
1479
+ sm: 'h-9 text-sm',
1480
+ md: 'h-10 text-sm',
1481
+ lg: 'h-12 text-base',
1482
+ };
1483
+ const pad2$1 = (n) => String(n).padStart(2, '0');
1484
+ /** Parse `HH:mm` or `HH:mm:ss` (the seconds are ignored). Anything else is null. */
1485
+ function parseTime(value) {
1486
+ if (typeof value !== 'string')
1487
+ return null;
1488
+ const match = /^(\d{1,2}):(\d{2})(?::\d{2}(?:\.\d+)?)?$/.exec(value.trim());
1489
+ if (!match)
1490
+ return null;
1491
+ const h = Number(match[1]);
1492
+ const m = Number(match[2]);
1493
+ if (h > 23 || m > 59)
1494
+ return null;
1495
+ return { h, m };
1496
+ }
1497
+ /**
1498
+ * Time-of-day field, 24-hour, whatever the browser's locale: two segments (hours : minutes) in one
1499
+ * framed field, the value being a `"HH:mm"` string (or null) — the shape of a backend `LocalTime`.
1500
+ * Implements ControlValueAccessor: use `formControlName` or `[(ngModel)]`.
1501
+ *
1502
+ * Unlike a native `<input type="time">` it never switches to a 12-hour clock with AM/PM, and it is themed
1503
+ * like every other field (label, hint, error, sizes, `filled`, dark mode).
1504
+ *
1505
+ * Keyboard: type digits (the hours move on to the minutes by themselves after two digits, or one digit
1506
+ * above 2); `:`/`.`/→ jump to the minutes, ← or Backspace on an empty minute go back; ↑/↓ step the hours
1507
+ * by 1 and the minutes by [minuteStep] (wrapping, never carrying into the hour). Each segment is a
1508
+ * `spinbutton` with its own accessible name. An hour typed without minutes gets `:00` when the field is
1509
+ * left; emptying both segments sets null.
1510
+ *
1511
+ * @example
1512
+ * <sds-time-input label="Opens at" formControlName="start" [minuteStep]="15" />
1513
+ *
1514
+ * @example
1515
+ * <sds-time-input [ariaLabel]="'Closes at'" [ngModel]="end()" (ngModelChange)="end.set($event)" clearable />
1516
+ */
1517
+ class SdsTimeInput {
1518
+ fieldId = `sds-time-input-${nextId$l++}`;
1519
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
1520
+ /** Accessible name of the field when there is no visible [label]. */
1521
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
1522
+ hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : []));
1523
+ error = input('', ...(ngDevMode ? [{ debugName: "error" }] : []));
1524
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
1525
+ /** `filled` is borderless on a muted ground, like sds-input's `filled`. */
1526
+ appearance = input('default', ...(ngDevMode ? [{ debugName: "appearance" }] : []));
1527
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
1528
+ readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
1529
+ /** Show a clear button (sets null) while there is a value. */
1530
+ clearable = input(false, ...(ngDevMode ? [{ debugName: "clearable", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
1531
+ /** Leading clock icon. */
1532
+ showIcon = input(true, ...(ngDevMode ? [{ debugName: "showIcon", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
1533
+ /** Minutes moved by ↑/↓ on the minutes segment (snaps to the step first). Default 1. */
1534
+ minuteStep = input(1, ...(ngDevMode ? [{ debugName: "minuteStep", transform: numberAttribute }] : [{ transform: numberAttribute }]));
1535
+ /** Emit `HH:mm:ss` (seconds always `00`) instead of `HH:mm`. */
1536
+ withSeconds = input(false, ...(ngDevMode ? [{ debugName: "withSeconds", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
1537
+ /** Placeholder of each empty segment. */
1538
+ placeholderText = input('--', ...(ngDevMode ? [{ debugName: "placeholderText" }] : []));
1539
+ /** Accessible name of the hours segment. English default — translate it. */
1540
+ hoursLabel = input('Hours', ...(ngDevMode ? [{ debugName: "hoursLabel" }] : []));
1541
+ /** Accessible name of the minutes segment. English default — translate it. */
1542
+ minutesLabel = input('Minutes', ...(ngDevMode ? [{ debugName: "minutesLabel" }] : []));
1543
+ /** Accessible name of the clear button. English default — translate it. */
1544
+ clearLabel = input('Clear time', ...(ngDevMode ? [{ debugName: "clearLabel" }] : []));
1545
+ hoursEl = viewChild.required('hoursEl');
1546
+ minutesEl = viewChild.required('minutesEl');
1547
+ hours = signal(null, ...(ngDevMode ? [{ debugName: "hours" }] : []));
1548
+ minutes = signal(null, ...(ngDevMode ? [{ debugName: "minutes" }] : []));
1549
+ /** Raw text of a segment while it is being typed (null = show the committed value). */
1550
+ hoursDraft = signal(null, ...(ngDevMode ? [{ debugName: "hoursDraft" }] : []));
1551
+ minutesDraft = signal(null, ...(ngDevMode ? [{ debugName: "minutesDraft" }] : []));
1552
+ cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : []));
1553
+ isDisabled = computed(() => this.disabled() || this.cvaDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
1554
+ hasValue = computed(() => this.hours() !== null || this.minutes() !== null, ...(ngDevMode ? [{ debugName: "hasValue" }] : []));
1555
+ hoursText = computed(() => this.hoursDraft() ?? (this.hours() === null ? '' : pad2$1(this.hours())), ...(ngDevMode ? [{ debugName: "hoursText" }] : []));
1556
+ minutesText = computed(() => this.minutesDraft() ?? (this.minutes() === null ? '' : pad2$1(this.minutes())), ...(ngDevMode ? [{ debugName: "minutesText" }] : []));
1557
+ describedBy = computed(() => {
1558
+ if (this.error())
1559
+ return `${this.fieldId}-err`;
1560
+ if (this.hint())
1561
+ return `${this.fieldId}-hint`;
1562
+ return null;
1563
+ }, ...(ngDevMode ? [{ debugName: "describedBy" }] : []));
1564
+ wrapperClasses = computed(() => {
1565
+ const parts = [SIZES$a[this.size()], 'rounded-control'];
1566
+ if (this.isDisabled())
1567
+ parts.push('opacity-60 cursor-not-allowed');
1568
+ if (this.appearance() === 'filled') {
1569
+ parts.push('border-transparent bg-muted');
1570
+ parts.push(this.error()
1571
+ ? 'border-danger focus-within:ring-2 focus-within:ring-danger/30'
1572
+ : 'focus-within:ring-2 focus-within:ring-brand-500/25');
1573
+ }
1574
+ else {
1575
+ parts.push('bg-surface');
1576
+ parts.push(this.error()
1577
+ ? 'border-danger focus-within:border-danger focus-within:ring-2 focus-within:ring-danger/25'
1578
+ : 'border-border focus-within:border-brand-500 focus-within:ring-2 focus-within:ring-brand-500/25');
1579
+ }
1580
+ return parts.join(' ');
1581
+ }, ...(ngDevMode ? [{ debugName: "wrapperClasses" }] : []));
1582
+ pad(n) {
1583
+ return pad2$1(n);
1584
+ }
1585
+ // --- ControlValueAccessor -------------------------------------------------
1586
+ onChange = () => { };
1587
+ onTouched = () => { };
1588
+ writeValue(value) {
1589
+ const t = parseTime(value);
1590
+ this.hours.set(t ? t.h : null);
1591
+ this.minutes.set(t ? t.m : null);
1592
+ this.hoursDraft.set(null);
1593
+ this.minutesDraft.set(null);
1594
+ }
1595
+ registerOnChange(fn) {
1596
+ this.onChange = fn;
1597
+ }
1598
+ registerOnTouched(fn) {
1599
+ this.onTouched = fn;
1600
+ }
1601
+ setDisabledState(isDisabled) {
1602
+ this.cvaDisabled.set(isDisabled);
1603
+ }
1604
+ // --- typing ---------------------------------------------------------------
1605
+ selectAll(event) {
1606
+ const el = event.target;
1607
+ queueMicrotask(() => el.select());
1608
+ }
1609
+ /** Clicking the frame (icon, padding) focuses the hours, like clicking a native field. */
1610
+ onFrameClick(event) {
1611
+ if (this.isDisabled())
1612
+ return;
1613
+ const target = event.target;
1614
+ if (target.tagName !== 'INPUT' && !target.closest('button'))
1615
+ this.hoursEl().nativeElement.focus();
1616
+ }
1617
+ onHoursInput(event) {
1618
+ const el = event.target;
1619
+ const text = el.value;
1620
+ // A pasted/autofilled full time ("09:45", "9.45", "9h45") fills both segments.
1621
+ if (/[:.h]/i.test(text)) {
1622
+ const t = parseTime(text.replace(/[.h]/i, ':'));
1623
+ if (t) {
1624
+ this.commitHours(t.h);
1625
+ this.commitMinutes(t.m);
1626
+ this.syncSegments();
1627
+ this.focusMinutes();
1628
+ return;
1629
+ }
1630
+ }
1631
+ const digits = text.replace(/\D/g, '');
1632
+ if (digits === '') {
1633
+ el.value = '';
1634
+ this.hoursDraft.set('');
1635
+ this.hours.set(null);
1636
+ this.emitIfComplete();
1637
+ return;
1638
+ }
1639
+ // How many leading digits make the hour: "9" is 09 (no hour starts with 3-9 and has a second digit);
1640
+ // "1"/"2" wait for a second digit; "25" is 2 then 5 minutes. Whatever follows spills into minutes, so
1641
+ // typing "945" in one go (or a fast/IME/autofill burst) still reads as 09:45.
1642
+ let hourLength;
1643
+ if (Number(digits[0]) > 2)
1644
+ hourLength = 1;
1645
+ else if (digits.length === 1)
1646
+ hourLength = 0;
1647
+ else
1648
+ hourLength = Number(digits.slice(0, 2)) <= 23 ? 2 : 1;
1649
+ if (hourLength === 0) {
1650
+ el.value = digits;
1651
+ this.hoursDraft.set(digits);
1652
+ return;
1653
+ }
1654
+ this.commitHours(Number(digits.slice(0, hourLength)));
1655
+ el.value = this.hoursText();
1656
+ const rest = digits.slice(hourLength, hourLength + 2);
1657
+ if (rest)
1658
+ this.takeMinuteDigits(rest);
1659
+ this.focusMinutes();
1660
+ }
1661
+ onMinutesInput(event) {
1662
+ const el = event.target;
1663
+ const digits = el.value.replace(/\D/g, '').slice(0, 2);
1664
+ if (digits === '') {
1665
+ el.value = '';
1666
+ this.minutesDraft.set('');
1667
+ this.minutes.set(null);
1668
+ this.emitIfComplete();
1669
+ return;
1670
+ }
1671
+ this.takeMinuteDigits(digits);
1672
+ el.value = this.minutesText();
1673
+ }
1674
+ /** "7" (no minute starts with 6-9 and has a second digit) and any 2 digits commit; "0"-"5" wait. */
1675
+ takeMinuteDigits(digits) {
1676
+ if (digits.length >= 2 || Number(digits) > 5) {
1677
+ this.commitMinutes(Math.min(59, Number(digits.slice(0, 2))));
1678
+ }
1679
+ else {
1680
+ this.minutesDraft.set(digits);
1681
+ }
1682
+ this.minutesEl().nativeElement.value = this.minutesText();
1683
+ }
1684
+ /** Force both inputs to show the committed values (a binding does not repaint an unchanged value). */
1685
+ syncSegments() {
1686
+ this.hoursEl().nativeElement.value = this.hoursText();
1687
+ this.minutesEl().nativeElement.value = this.minutesText();
1688
+ }
1689
+ onHoursKeydown(event) {
1690
+ if (this.readonly())
1691
+ return;
1692
+ const el = event.target;
1693
+ switch (event.key) {
1694
+ case 'ArrowUp':
1695
+ case 'ArrowDown': {
1696
+ event.preventDefault();
1697
+ const delta = event.key === 'ArrowUp' ? 1 : -1;
1698
+ const current = this.draftNumber(this.hoursDraft()) ?? this.hours();
1699
+ const next = current === null ? (delta > 0 ? 0 : 23) : (current + delta + 24) % 24;
1700
+ this.commitHours(next);
1701
+ this.syncSegments();
1702
+ break;
1703
+ }
1704
+ case ':':
1705
+ case '.':
1706
+ case 'h':
1707
+ event.preventDefault();
1708
+ this.flushHours();
1709
+ this.syncSegments();
1710
+ this.focusMinutes();
1711
+ break;
1712
+ case 'ArrowRight':
1713
+ if (el.selectionStart === el.value.length) {
1714
+ event.preventDefault();
1715
+ this.flushHours();
1716
+ this.focusMinutes();
1717
+ }
1718
+ break;
1719
+ }
1720
+ }
1721
+ onMinutesKeydown(event) {
1722
+ if (this.readonly())
1723
+ return;
1724
+ const el = event.target;
1725
+ switch (event.key) {
1726
+ case 'ArrowUp':
1727
+ case 'ArrowDown': {
1728
+ event.preventDefault();
1729
+ const step = Math.max(1, Math.min(60, this.minuteStep()));
1730
+ const current = this.draftNumber(this.minutesDraft()) ?? this.minutes();
1731
+ let next;
1732
+ if (current === null) {
1733
+ next = event.key === 'ArrowUp' ? 0 : 60 - step;
1734
+ }
1735
+ else if (event.key === 'ArrowUp') {
1736
+ next = (Math.floor(current / step) + 1) * step;
1737
+ }
1738
+ else {
1739
+ next = current % step === 0 ? current - step : Math.floor(current / step) * step;
1740
+ }
1741
+ this.commitMinutes(((next % 60) + 60) % 60);
1742
+ this.syncSegments();
1743
+ break;
1744
+ }
1745
+ case 'Backspace':
1746
+ if (el.value === '') {
1747
+ event.preventDefault();
1748
+ this.focusHours();
1749
+ }
1750
+ break;
1751
+ case 'ArrowLeft':
1752
+ if (el.selectionStart === 0 && el.selectionEnd === 0) {
1753
+ event.preventDefault();
1754
+ this.flushMinutes();
1755
+ this.focusHours();
1756
+ }
1757
+ break;
1758
+ }
1759
+ }
1760
+ /** Leaving the whole field completes a half-typed time and marks the control touched. */
1761
+ onFocusOut(event) {
1762
+ const next = event.relatedTarget;
1763
+ const group = event.currentTarget;
1764
+ if (next && group.contains(next))
1765
+ return;
1766
+ this.flushHours();
1767
+ this.flushMinutes();
1768
+ if (this.hours() !== null && this.minutes() === null)
1769
+ this.commitMinutes(0);
1770
+ else if (this.hours() === null && this.minutes() !== null)
1771
+ this.commitHours(0);
1772
+ this.syncSegments();
1773
+ this.onTouched();
1774
+ }
1775
+ clear() {
1776
+ this.hours.set(null);
1777
+ this.minutes.set(null);
1778
+ this.hoursDraft.set(null);
1779
+ this.minutesDraft.set(null);
1780
+ this.onChange(null);
1781
+ this.onTouched();
1782
+ }
1783
+ // --- internals ------------------------------------------------------------
1784
+ draftNumber(draft) {
1785
+ return draft === null || draft === '' ? null : Number(draft);
1786
+ }
1787
+ flushHours() {
1788
+ const d = this.hoursDraft();
1789
+ if (d !== null && d !== '')
1790
+ this.commitHours(Math.min(23, Number(d)));
1791
+ else
1792
+ this.hoursDraft.set(null);
1793
+ }
1794
+ flushMinutes() {
1795
+ const d = this.minutesDraft();
1796
+ if (d !== null && d !== '')
1797
+ this.commitMinutes(Math.min(59, Number(d)));
1798
+ else
1799
+ this.minutesDraft.set(null);
1800
+ }
1801
+ commitHours(h) {
1802
+ this.hours.set(h);
1803
+ this.hoursDraft.set(null);
1804
+ this.emitIfComplete();
1805
+ }
1806
+ commitMinutes(m) {
1807
+ this.minutes.set(m);
1808
+ this.minutesDraft.set(null);
1809
+ this.emitIfComplete();
1810
+ }
1811
+ /** Emits `HH:mm` once both segments hold a value, and null once both are empty. */
1812
+ emitIfComplete() {
1813
+ const h = this.hours();
1814
+ const m = this.minutes();
1815
+ if (h !== null && m !== null) {
1816
+ this.onChange(`${pad2$1(h)}:${pad2$1(m)}${this.withSeconds() ? ':00' : ''}`);
1817
+ }
1818
+ else if (h === null && m === null) {
1819
+ this.onChange(null);
1820
+ }
1821
+ }
1822
+ focusMinutes() {
1823
+ const el = this.minutesEl().nativeElement;
1824
+ el.focus();
1825
+ el.select();
1826
+ }
1827
+ focusHours() {
1828
+ const el = this.hoursEl().nativeElement;
1829
+ el.focus();
1830
+ el.select();
1831
+ }
1832
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTimeInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
1833
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsTimeInput, isStandalone: true, selector: "sds-time-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", 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 }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, showIcon: { classPropertyName: "showIcon", publicName: "showIcon", isSignal: true, isRequired: false, transformFunction: null }, minuteStep: { classPropertyName: "minuteStep", publicName: "minuteStep", isSignal: true, isRequired: false, transformFunction: null }, withSeconds: { classPropertyName: "withSeconds", publicName: "withSeconds", isSignal: true, isRequired: false, transformFunction: null }, placeholderText: { classPropertyName: "placeholderText", publicName: "placeholderText", isSignal: true, isRequired: false, transformFunction: null }, hoursLabel: { classPropertyName: "hoursLabel", publicName: "hoursLabel", isSignal: true, isRequired: false, transformFunction: null }, minutesLabel: { classPropertyName: "minutesLabel", publicName: "minutesLabel", isSignal: true, isRequired: false, transformFunction: null }, clearLabel: { classPropertyName: "clearLabel", publicName: "clearLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, providers: [
1834
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SdsTimeInput), multi: true },
1835
+ ], viewQueries: [{ propertyName: "hoursEl", first: true, predicate: ["hoursEl"], descendants: true, isSignal: true }, { propertyName: "minutesEl", first: true, predicate: ["minutesEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
1836
+ @if (label()) {
1837
+ <label
1838
+ [id]="fieldId + '-label'"
1839
+ [for]="fieldId + '-h'"
1840
+ class="mb-1.5 block text-sm font-medium text-ink"
1841
+ >
1842
+ {{ label() }}
1843
+ </label>
1844
+ }
1845
+
1846
+ <div
1847
+ role="group"
1848
+ [attr.aria-labelledby]="label() ? fieldId + '-label' : null"
1849
+ [attr.aria-label]="!label() && ariaLabel() ? ariaLabel() : null"
1850
+ [attr.aria-describedby]="describedBy()"
1851
+ class="relative flex items-center border transition-colors"
1852
+ [class]="wrapperClasses()"
1853
+ (focusout)="onFocusOut($event)"
1854
+ (click)="onFrameClick($event)"
1855
+ >
1856
+ @if (showIcon()) {
1857
+ <sds-icon
1858
+ name="clock"
1859
+ [size]="16"
1860
+ class="pointer-events-none ml-3 shrink-0 text-ink-faint"
1861
+ />
1862
+ }
1863
+ <div class="flex items-center tabular-nums" [class]="showIcon() ? 'pl-2' : 'pl-3.5'">
1864
+ <input
1865
+ #hoursEl
1866
+ [id]="fieldId + '-h'"
1867
+ type="text"
1868
+ inputmode="numeric"
1869
+ autocomplete="off"
1870
+ maxlength="5"
1871
+ role="spinbutton"
1872
+ aria-valuemin="0"
1873
+ aria-valuemax="23"
1874
+ [attr.aria-valuenow]="hours()"
1875
+ [attr.aria-valuetext]="hours() === null ? null : pad(hours()!)"
1876
+ [attr.aria-label]="hoursLabel()"
1877
+ [attr.aria-invalid]="error() ? 'true' : null"
1878
+ [attr.aria-describedby]="describedBy()"
1879
+ [value]="hoursText()"
1880
+ [placeholder]="placeholderText()"
1881
+ [disabled]="isDisabled()"
1882
+ [readOnly]="readonly()"
1883
+ class="w-[2.6ch] bg-transparent text-center text-ink outline-none placeholder:text-ink-faint disabled:cursor-not-allowed"
1884
+ (focus)="selectAll($event)"
1885
+ (input)="onHoursInput($event)"
1886
+ (keydown)="onHoursKeydown($event)"
1887
+ />
1888
+ <span class="px-px text-ink-soft" aria-hidden="true">:</span>
1889
+ <input
1890
+ #minutesEl
1891
+ [id]="fieldId + '-m'"
1892
+ type="text"
1893
+ inputmode="numeric"
1894
+ autocomplete="off"
1895
+ maxlength="2"
1896
+ role="spinbutton"
1897
+ aria-valuemin="0"
1898
+ aria-valuemax="59"
1899
+ [attr.aria-valuenow]="minutes()"
1900
+ [attr.aria-valuetext]="minutes() === null ? null : pad(minutes()!)"
1901
+ [attr.aria-label]="minutesLabel()"
1902
+ [attr.aria-invalid]="error() ? 'true' : null"
1903
+ [attr.aria-describedby]="describedBy()"
1904
+ [value]="minutesText()"
1905
+ [placeholder]="placeholderText()"
1906
+ [disabled]="isDisabled()"
1907
+ [readOnly]="readonly()"
1908
+ class="w-[2.6ch] bg-transparent text-center text-ink outline-none placeholder:text-ink-faint disabled:cursor-not-allowed"
1909
+ (focus)="selectAll($event)"
1910
+ (input)="onMinutesInput($event)"
1911
+ (keydown)="onMinutesKeydown($event)"
1912
+ />
1913
+ </div>
1914
+
1915
+ @if (clearable() && hasValue() && !isDisabled() && !readonly()) {
1916
+ <button
1917
+ type="button"
1918
+ tabindex="-1"
1919
+ [attr.aria-label]="clearLabel()"
1920
+ class="ml-auto mr-2 inline-flex h-6 w-6 items-center justify-center rounded-full text-ink-faint hover:bg-black/5 hover:text-ink-soft dark:hover:bg-white/10"
1921
+ (click)="clear()"
1922
+ >
1923
+ <sds-icon name="x" [size]="15" />
1924
+ </button>
1925
+ }
1926
+ </div>
1927
+
1928
+ @if (error()) {
1929
+ <p [id]="fieldId + '-err'" class="mt-1.5 text-xs font-medium text-danger">{{ error() }}</p>
1930
+ } @else if (hint()) {
1931
+ <p [id]="fieldId + '-hint'" class="mt-1.5 text-xs text-ink-faint">{{ hint() }}</p>
1932
+ }
1933
+ `, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1934
+ }
1935
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTimeInput, decorators: [{
1936
+ type: Component,
1937
+ args: [{
1938
+ selector: 'sds-time-input',
1939
+ changeDetection: ChangeDetectionStrategy.OnPush,
1940
+ imports: [SdsIcon],
1941
+ providers: [
1942
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SdsTimeInput), multi: true },
1943
+ ],
1944
+ host: { class: 'block' },
1945
+ template: `
1946
+ @if (label()) {
1947
+ <label
1948
+ [id]="fieldId + '-label'"
1949
+ [for]="fieldId + '-h'"
1950
+ class="mb-1.5 block text-sm font-medium text-ink"
1951
+ >
1952
+ {{ label() }}
1953
+ </label>
1954
+ }
1955
+
1956
+ <div
1957
+ role="group"
1958
+ [attr.aria-labelledby]="label() ? fieldId + '-label' : null"
1959
+ [attr.aria-label]="!label() && ariaLabel() ? ariaLabel() : null"
1960
+ [attr.aria-describedby]="describedBy()"
1961
+ class="relative flex items-center border transition-colors"
1962
+ [class]="wrapperClasses()"
1963
+ (focusout)="onFocusOut($event)"
1964
+ (click)="onFrameClick($event)"
1965
+ >
1966
+ @if (showIcon()) {
1967
+ <sds-icon
1968
+ name="clock"
1969
+ [size]="16"
1970
+ class="pointer-events-none ml-3 shrink-0 text-ink-faint"
1971
+ />
1972
+ }
1973
+ <div class="flex items-center tabular-nums" [class]="showIcon() ? 'pl-2' : 'pl-3.5'">
1974
+ <input
1975
+ #hoursEl
1976
+ [id]="fieldId + '-h'"
1977
+ type="text"
1978
+ inputmode="numeric"
1979
+ autocomplete="off"
1980
+ maxlength="5"
1981
+ role="spinbutton"
1982
+ aria-valuemin="0"
1983
+ aria-valuemax="23"
1984
+ [attr.aria-valuenow]="hours()"
1985
+ [attr.aria-valuetext]="hours() === null ? null : pad(hours()!)"
1986
+ [attr.aria-label]="hoursLabel()"
1987
+ [attr.aria-invalid]="error() ? 'true' : null"
1988
+ [attr.aria-describedby]="describedBy()"
1989
+ [value]="hoursText()"
1990
+ [placeholder]="placeholderText()"
1991
+ [disabled]="isDisabled()"
1992
+ [readOnly]="readonly()"
1993
+ class="w-[2.6ch] bg-transparent text-center text-ink outline-none placeholder:text-ink-faint disabled:cursor-not-allowed"
1994
+ (focus)="selectAll($event)"
1995
+ (input)="onHoursInput($event)"
1996
+ (keydown)="onHoursKeydown($event)"
1997
+ />
1998
+ <span class="px-px text-ink-soft" aria-hidden="true">:</span>
1999
+ <input
2000
+ #minutesEl
2001
+ [id]="fieldId + '-m'"
2002
+ type="text"
2003
+ inputmode="numeric"
2004
+ autocomplete="off"
2005
+ maxlength="2"
2006
+ role="spinbutton"
2007
+ aria-valuemin="0"
2008
+ aria-valuemax="59"
2009
+ [attr.aria-valuenow]="minutes()"
2010
+ [attr.aria-valuetext]="minutes() === null ? null : pad(minutes()!)"
2011
+ [attr.aria-label]="minutesLabel()"
2012
+ [attr.aria-invalid]="error() ? 'true' : null"
2013
+ [attr.aria-describedby]="describedBy()"
2014
+ [value]="minutesText()"
2015
+ [placeholder]="placeholderText()"
2016
+ [disabled]="isDisabled()"
2017
+ [readOnly]="readonly()"
2018
+ class="w-[2.6ch] bg-transparent text-center text-ink outline-none placeholder:text-ink-faint disabled:cursor-not-allowed"
2019
+ (focus)="selectAll($event)"
2020
+ (input)="onMinutesInput($event)"
2021
+ (keydown)="onMinutesKeydown($event)"
2022
+ />
2023
+ </div>
2024
+
2025
+ @if (clearable() && hasValue() && !isDisabled() && !readonly()) {
2026
+ <button
2027
+ type="button"
2028
+ tabindex="-1"
2029
+ [attr.aria-label]="clearLabel()"
2030
+ class="ml-auto mr-2 inline-flex h-6 w-6 items-center justify-center rounded-full text-ink-faint hover:bg-black/5 hover:text-ink-soft dark:hover:bg-white/10"
2031
+ (click)="clear()"
2032
+ >
2033
+ <sds-icon name="x" [size]="15" />
2034
+ </button>
2035
+ }
2036
+ </div>
2037
+
2038
+ @if (error()) {
2039
+ <p [id]="fieldId + '-err'" class="mt-1.5 text-xs font-medium text-danger">{{ error() }}</p>
2040
+ } @else if (hint()) {
2041
+ <p [id]="fieldId + '-hint'" class="mt-1.5 text-xs text-ink-faint">{{ hint() }}</p>
2042
+ }
2043
+ `,
2044
+ }]
2045
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], showIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIcon", required: false }] }], minuteStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "minuteStep", required: false }] }], withSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "withSeconds", required: false }] }], placeholderText: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderText", required: false }] }], hoursLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoursLabel", required: false }] }], minutesLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "minutesLabel", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], hoursEl: [{ type: i0.ViewChild, args: ['hoursEl', { isSignal: true }] }], minutesEl: [{ type: i0.ViewChild, args: ['minutesEl', { isSignal: true }] }] } });
2046
+
2047
+ let nextId$k = 0;
1474
2048
  const SIZES$9 = {
1475
2049
  sm: 'min-h-9 text-sm',
1476
2050
  md: 'min-h-10 text-sm',
@@ -1490,7 +2064,7 @@ const SIZES$9 = {
1490
2064
  * `<sds-localized-input [languages]="['fr','nl','en']" [(ngModel)]="title" />`.
1491
2065
  */
1492
2066
  class SdsLocalizedInput {
1493
- inputId = `sds-localized-input-${nextId$j++}`;
2067
+ inputId = `sds-localized-input-${nextId$k++}`;
1494
2068
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
1495
2069
  hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : []));
1496
2070
  placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
@@ -1746,7 +2320,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
1746
2320
  }]
1747
2321
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], languages: [{ type: i0.Input, args: [{ isSignal: true, alias: "languages", required: false }] }], currentLanguage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentLanguage", required: false }] }], multiline: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiline", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], languageSwitcherLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "languageSwitcherLabel", required: false }] }], untranslatedLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "untranslatedLabel", required: false }] }] } });
1748
2322
 
1749
- let nextId$i = 0;
2323
+ let nextId$j = 0;
1750
2324
  const SIZES$8 = {
1751
2325
  md: 'h-12 w-11 text-lg',
1752
2326
  lg: 'h-14 w-12 text-xl',
@@ -1763,7 +2337,7 @@ const SIZES$8 = {
1763
2337
  * first box and have it land correctly.
1764
2338
  */
1765
2339
  class SdsOtpInput {
1766
- groupId = `sds-otp-${nextId$i++}`;
2340
+ groupId = `sds-otp-${nextId$j++}`;
1767
2341
  /** How many boxes / characters the code has. */
1768
2342
  length = input(6, ...(ngDevMode ? [{ debugName: "length", transform: numberAttribute }] : [{ transform: numberAttribute }]));
1769
2343
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
@@ -2063,7 +2637,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
2063
2637
  }]
2064
2638
  }], ctorParameters: () => [], propDecorators: { length: [{ type: i0.Input, args: [{ isSignal: true, alias: "length", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], inputMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputMode", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], autoFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFocus", required: false }] }], cellLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellLabel", required: false }] }], completed: [{ type: i0.Output, args: ["completed"] }], cells: [{ type: i0.ViewChildren, args: ['cell', { isSignal: true }] }] } });
2065
2639
 
2066
- let nextId$h = 0;
2640
+ let nextId$i = 0;
2067
2641
  const SIZES$7 = {
2068
2642
  sm: 'text-sm px-3 py-2',
2069
2643
  md: 'text-sm px-3.5 py-2.5',
@@ -2075,7 +2649,7 @@ const SIZES$7 = {
2075
2649
  * [(ngModel)].
2076
2650
  */
2077
2651
  class SdsTextarea {
2078
- fieldId = `sds-textarea-${nextId$h++}`;
2652
+ fieldId = `sds-textarea-${nextId$i++}`;
2079
2653
  field = viewChild('field', ...(ngDevMode ? [{ debugName: "field" }] : []));
2080
2654
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
2081
2655
  /** Accessible name used when no visible [label] is provided. */
@@ -2335,7 +2909,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
2335
2909
  }]
2336
2910
  }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.ViewChild, args: ['field', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", 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 }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], autoResize: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoResize", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], showCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCount", required: false }] }], countLimitLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "countLimitLabel", required: false }] }], countLabelTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "countLabelTemplate", required: false }] }], resize: [{ type: i0.Input, args: [{ isSignal: true, alias: "resize", required: false }] }] } });
2337
2911
 
2338
- let nextId$g = 0;
2912
+ let nextId$h = 0;
2339
2913
  const SIZES$6 = {
2340
2914
  sm: { track: 'h-5 w-9', knob: 'h-4 w-4', on: 'translate-x-4', icon: 11 },
2341
2915
  md: { track: 'h-6 w-11', knob: 'h-5 w-5', on: 'translate-x-5', icon: 13 },
@@ -2348,7 +2922,7 @@ const SIZES$6 = {
2348
2922
  * states.
2349
2923
  */
2350
2924
  class SdsToggleSwitch {
2351
- fieldId = `sds-toggle-switch-${nextId$g++}`;
2925
+ fieldId = `sds-toggle-switch-${nextId$h++}`;
2352
2926
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
2353
2927
  /** Accessible name used when no visible [label] is provided. */
2354
2928
  ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
@@ -2592,7 +3166,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
2592
3166
  }]
2593
3167
  }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], trueValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "trueValue", required: false }] }], falseValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "falseValue", required: false }] }], onIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "onIcon", required: false }] }], offIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "offIcon", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }] } });
2594
3168
 
2595
- let nextId$f = 0;
3169
+ let nextId$g = 0;
2596
3170
  /**
2597
3171
  * Boolean checkbox. Implements ControlValueAccessor — use with reactive forms
2598
3172
  * or `[(ngModel)]`. Renders a styled box (brand-filled with a check when on)
@@ -2607,7 +3181,7 @@ let nextId$f = 0;
2607
3181
  * <sds-checkbox label="Auto-merge" [(ngModel)]="autoMerge" />
2608
3182
  */
2609
3183
  class SdsCheckbox {
2610
- id = `sds-checkbox-${nextId$f++}`;
3184
+ id = `sds-checkbox-${nextId$g++}`;
2611
3185
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
2612
3186
  /** Accessible name used when no visible [label] is provided. */
2613
3187
  ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
@@ -2755,7 +3329,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
2755
3329
  }]
2756
3330
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }] } });
2757
3331
 
2758
- let nextId$e = 0;
3332
+ let nextId$f = 0;
2759
3333
  /**
2760
3334
  * Mounted radios by group name. A pick updates the rest of its group, because a native radio says
2761
3335
  * nothing when the browser unchecks it — without this a reactive form would leave the old choice drawn.
@@ -2781,7 +3355,7 @@ const groups = new Map();
2781
3355
  * <sds-radio name="size" value="large" label="Grand" [(ngModel)]="size" />
2782
3356
  */
2783
3357
  class SdsRadio {
2784
- id = `sds-radio-${nextId$e++}`;
3358
+ id = `sds-radio-${nextId$f++}`;
2785
3359
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
2786
3360
  /** Accessible name used when no visible [label] is provided. */
2787
3361
  ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
@@ -2964,8 +3538,30 @@ const STATUS_COLOR = {
2964
3538
  busy: 'bg-danger',
2965
3539
  away: 'bg-warning',
2966
3540
  };
3541
+ const SHAPE = {
3542
+ circle: 'rounded-full',
3543
+ rounded: 'rounded-control',
3544
+ square: 'rounded-none',
3545
+ };
2967
3546
  /**
2968
- * Circular avatar showing an image or initials, with an optional status dot.
3547
+ * Avatar showing an image, initials or a placeholder icon, with an optional status dot.
3548
+ *
3549
+ * - `shape`: `circle` (default, people), `rounded` (things: a dish photo, a product, a logo) or `square`.
3550
+ * - `fallbackIcon`: shown instead of initials when there is no image (or it fails to load) — e.g.
3551
+ * `utensils` or `image` for a menu item without a photo. The placeholder is drawn on `placeholderClass`
3552
+ * (a muted tile by default), not on the initials' brand fill.
3553
+ * - A broken `src` falls back to the icon or the initials instead of showing the browser's broken-image
3554
+ * glyph; a new `src` retries.
3555
+ * - `decorative`: the avatar only repeats a name that is already written next to it (a list row, a select
3556
+ * option), so the image gets `alt=""` and the whole avatar is hidden from assistive technology.
3557
+ *
3558
+ * @example
3559
+ * <sds-avatar name="Ada Lovelace" [src]="user.photoUrl" status="online" />
3560
+ *
3561
+ * @example
3562
+ * <!-- a menu item in a list: rounded photo, dish placeholder when there is none -->
3563
+ * <sds-avatar shape="rounded" [size]="32" [src]="item.thumbnailUrl" [name]="item.name"
3564
+ * fallbackIcon="utensils" decorative />
2969
3565
  */
2970
3566
  class SdsAvatar {
2971
3567
  name = input('', ...(ngDevMode ? [{ debugName: "name" }] : []));
@@ -2973,8 +3569,48 @@ class SdsAvatar {
2973
3569
  src = input(null, ...(ngDevMode ? [{ debugName: "src" }] : []));
2974
3570
  size = input(36, ...(ngDevMode ? [{ debugName: "size" }] : []));
2975
3571
  status = input(null, ...(ngDevMode ? [{ debugName: "status" }] : []));
3572
+ /** `circle` (default), `rounded` (radius of a control) or `square`. */
3573
+ shape = input('circle', ...(ngDevMode ? [{ debugName: "shape" }] : []));
2976
3574
  /** Tailwind bg utility for the initials background. */
2977
3575
  bgClass = input('bg-brand-400', ...(ngDevMode ? [{ debugName: "bgClass" }] : []));
3576
+ /**
3577
+ * Icon shown instead of initials when there is no usable image (e.g. `utensils` or `image` for a dish).
3578
+ * Null (default) keeps the initials.
3579
+ */
3580
+ fallbackIcon = input(null, ...(ngDevMode ? [{ debugName: "fallbackIcon" }] : []));
3581
+ /** Classes of the placeholder tile behind {@link fallbackIcon}: background and icon colour. */
3582
+ placeholderClass = input('bg-muted text-ink-faint', ...(ngDevMode ? [{ debugName: "placeholderClass" }] : []));
3583
+ /**
3584
+ * The avatar repeats a name already visible beside it: `alt=""` and `aria-hidden`, so a screen reader
3585
+ * does not read the name twice.
3586
+ */
3587
+ decorative = input(false, ...(ngDevMode ? [{ debugName: "decorative", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
3588
+ /** Native lazy loading for the image (default on — avatars are often in long lists). */
3589
+ lazy = input(true, ...(ngDevMode ? [{ debugName: "lazy", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
3590
+ /** The `src` that failed to load. Any other `src` is tried (so a new photo URL retries). */
3591
+ failedSrc = signal(undefined, ...(ngDevMode ? [{ debugName: "failedSrc" }] : []));
3592
+ showImage = computed(() => {
3593
+ const src = this.src();
3594
+ return !!src && src !== this.failedSrc();
3595
+ }, ...(ngDevMode ? [{ debugName: "showImage" }] : []));
3596
+ shapeClass = computed(() => SHAPE[this.shape()], ...(ngDevMode ? [{ debugName: "shapeClass" }] : []));
3597
+ frameClass = computed(() => {
3598
+ const parts = [this.shapeClass()];
3599
+ if (this.showImage()) {
3600
+ parts.push('bg-muted');
3601
+ }
3602
+ else if (this.fallbackIcon()) {
3603
+ parts.push(this.placeholderClass());
3604
+ }
3605
+ else {
3606
+ parts.push(this.bgClass(), 'text-white');
3607
+ }
3608
+ // People get the soft ring the avatar always had; things get a hairline so a white plate on a white
3609
+ // page still has an edge.
3610
+ parts.push(this.shape() === 'circle' ? 'ring-2 ring-white/20' : 'ring-1 ring-inset ring-border');
3611
+ return parts.join(' ');
3612
+ }, ...(ngDevMode ? [{ debugName: "frameClass" }] : []));
3613
+ iconSize = computed(() => Math.max(12, Math.round(this.size() * 0.5)), ...(ngDevMode ? [{ debugName: "iconSize" }] : []));
2978
3614
  dotSize = computed(() => Math.max(8, Math.round(this.size() * 0.28)), ...(ngDevMode ? [{ debugName: "dotSize" }] : []));
2979
3615
  statusColor = computed(() => this.status() ? STATUS_COLOR[this.status()] : '', ...(ngDevMode ? [{ debugName: "statusColor" }] : []));
2980
3616
  resolvedInitials = computed(() => {
@@ -2988,19 +3624,31 @@ class SdsAvatar {
2988
3624
  return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
2989
3625
  }, ...(ngDevMode ? [{ debugName: "resolvedInitials" }] : []));
2990
3626
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsAvatar, deps: [], target: i0.ɵɵFactoryTarget.Component });
2991
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsAvatar, isStandalone: true, selector: "sds-avatar", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, initials: { classPropertyName: "initials", publicName: "initials", isSignal: true, isRequired: false, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, bgClass: { classPropertyName: "bgClass", publicName: "bgClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "inline-block shrink-0 align-middle" }, ngImport: i0, template: `
3627
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsAvatar, isStandalone: true, selector: "sds-avatar", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, initials: { classPropertyName: "initials", publicName: "initials", isSignal: true, isRequired: false, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, bgClass: { classPropertyName: "bgClass", publicName: "bgClass", isSignal: true, isRequired: false, transformFunction: null }, fallbackIcon: { classPropertyName: "fallbackIcon", publicName: "fallbackIcon", isSignal: true, isRequired: false, transformFunction: null }, placeholderClass: { classPropertyName: "placeholderClass", publicName: "placeholderClass", isSignal: true, isRequired: false, transformFunction: null }, decorative: { classPropertyName: "decorative", publicName: "decorative", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-hidden": "decorative() ? 'true' : null" }, classAttribute: "inline-block shrink-0 align-middle" }, ngImport: i0, template: `
2992
3628
  <div
2993
- class="relative grid place-items-center overflow-hidden rounded-full font-semibold text-white ring-2 ring-white/20"
2994
- [class]="bgClass()"
3629
+ class="relative grid place-items-center font-semibold"
3630
+ [class]="frameClass()"
2995
3631
  [style.width.px]="size()"
2996
3632
  [style.height.px]="size()"
2997
3633
  [style.fontSize.px]="size() * 0.4"
2998
3634
  >
2999
- @if (src()) {
3000
- <img [src]="src()" [alt]="name()" class="h-full w-full object-cover" />
3001
- } @else {
3002
- <span>{{ resolvedInitials() }}</span>
3003
- }
3635
+ <!-- The image and placeholder are clipped to the shape; the status dot is not (it straddles the edge). -->
3636
+ <div class="absolute inset-0 grid place-items-center overflow-hidden" [class]="shapeClass()">
3637
+ @if (showImage()) {
3638
+ <img
3639
+ [src]="src()"
3640
+ [alt]="decorative() ? '' : name()"
3641
+ [attr.loading]="lazy() ? 'lazy' : null"
3642
+ decoding="async"
3643
+ class="h-full w-full object-cover"
3644
+ (error)="failedSrc.set(src())"
3645
+ />
3646
+ } @else if (fallbackIcon()) {
3647
+ <sds-icon [name]="fallbackIcon()!" [size]="iconSize()" />
3648
+ } @else {
3649
+ <span>{{ resolvedInitials() }}</span>
3650
+ }
3651
+ </div>
3004
3652
 
3005
3653
  @if (status()) {
3006
3654
  <span
@@ -3011,27 +3659,43 @@ class SdsAvatar {
3011
3659
  ></span>
3012
3660
  }
3013
3661
  </div>
3014
- `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3662
+ `, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3015
3663
  }
3016
3664
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsAvatar, decorators: [{
3017
3665
  type: Component,
3018
3666
  args: [{
3019
3667
  selector: 'sds-avatar',
3020
3668
  changeDetection: ChangeDetectionStrategy.OnPush,
3021
- host: { class: 'inline-block shrink-0 align-middle' },
3669
+ imports: [SdsIcon],
3670
+ host: {
3671
+ class: 'inline-block shrink-0 align-middle',
3672
+ '[attr.aria-hidden]': "decorative() ? 'true' : null",
3673
+ },
3022
3674
  template: `
3023
3675
  <div
3024
- class="relative grid place-items-center overflow-hidden rounded-full font-semibold text-white ring-2 ring-white/20"
3025
- [class]="bgClass()"
3676
+ class="relative grid place-items-center font-semibold"
3677
+ [class]="frameClass()"
3026
3678
  [style.width.px]="size()"
3027
3679
  [style.height.px]="size()"
3028
3680
  [style.fontSize.px]="size() * 0.4"
3029
3681
  >
3030
- @if (src()) {
3031
- <img [src]="src()" [alt]="name()" class="h-full w-full object-cover" />
3032
- } @else {
3033
- <span>{{ resolvedInitials() }}</span>
3034
- }
3682
+ <!-- The image and placeholder are clipped to the shape; the status dot is not (it straddles the edge). -->
3683
+ <div class="absolute inset-0 grid place-items-center overflow-hidden" [class]="shapeClass()">
3684
+ @if (showImage()) {
3685
+ <img
3686
+ [src]="src()"
3687
+ [alt]="decorative() ? '' : name()"
3688
+ [attr.loading]="lazy() ? 'lazy' : null"
3689
+ decoding="async"
3690
+ class="h-full w-full object-cover"
3691
+ (error)="failedSrc.set(src())"
3692
+ />
3693
+ } @else if (fallbackIcon()) {
3694
+ <sds-icon [name]="fallbackIcon()!" [size]="iconSize()" />
3695
+ } @else {
3696
+ <span>{{ resolvedInitials() }}</span>
3697
+ }
3698
+ </div>
3035
3699
 
3036
3700
  @if (status()) {
3037
3701
  <span
@@ -3044,7 +3708,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
3044
3708
  </div>
3045
3709
  `,
3046
3710
  }]
3047
- }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], initials: [{ type: i0.Input, args: [{ isSignal: true, alias: "initials", required: false }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], bgClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "bgClass", required: false }] }] } });
3711
+ }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], initials: [{ type: i0.Input, args: [{ isSignal: true, alias: "initials", required: false }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], bgClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "bgClass", required: false }] }], fallbackIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "fallbackIcon", required: false }] }], placeholderClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderClass", required: false }] }], decorative: [{ type: i0.Input, args: [{ isSignal: true, alias: "decorative", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }] } });
3048
3712
 
3049
3713
  const VARIANTS$1 = {
3050
3714
  neutral: 'bg-muted text-ink-soft border-border',
@@ -3267,7 +3931,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
3267
3931
 
3268
3932
  /** Activation order: none → contains → may-contain → none. */
3269
3933
  const ORDER = ['none', 'contains', 'may-contain'];
3270
- let nextId$d = 0;
3934
+ let nextId$e = 0;
3271
3935
  /**
3272
3936
  * A chip that cycles through three states — `none`, `contains`, `may-contain` — built for the 14-tile
3273
3937
  * allergen declaration grid, but generic (any "no / yes / maybe" declaration).
@@ -3287,7 +3951,7 @@ let nextId$d = 0;
3287
3951
  * containsLabel="Contains" mayContainLabel="May contain" noneLabel="Not present" />
3288
3952
  */
3289
3953
  class SdsTriStateChip {
3290
- chipId = `sds-tri-state-chip-${nextId$d++}`;
3954
+ chipId = `sds-tri-state-chip-${nextId$e++}`;
3291
3955
  /** Current state. Two-way: `[(value)]`. */
3292
3956
  value = model('none', ...(ngDevMode ? [{ debugName: "value" }] : []));
3293
3957
  /** Visible name (e.g. the allergen). Required — the chip has no other accessible name. */
@@ -3509,7 +4173,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
3509
4173
  }]
3510
4174
  }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showState: [{ type: i0.Input, args: [{ isSignal: true, alias: "showState", required: false }] }], noneLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "noneLabel", required: false }] }], containsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "containsLabel", required: false }] }], mayContainLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "mayContainLabel", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }] } });
3511
4175
 
3512
- let nextId$c = 0;
4176
+ let nextId$d = 0;
3513
4177
  /**
3514
4178
  * Segmented control: a small set of mutually exclusive options shown side by side (e.g. "Pick exactly /
3515
4179
  * at least / up to"). Radio-group semantics: `role="radiogroup"` with `role="radio"` segments, one tab
@@ -3525,7 +4189,7 @@ let nextId$c = 0;
3525
4189
  */
3526
4190
  class SdsSegmented {
3527
4191
  host = inject(ElementRef);
3528
- baseId = `sds-segmented-${nextId$c++}`;
4192
+ baseId = `sds-segmented-${nextId$d++}`;
3529
4193
  /** Options: `{ value, label, icon?, disabled? }` objects, or bare strings/numbers. */
3530
4194
  options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
3531
4195
  /** Selected value. Two-way: `[(value)]`. */
@@ -3703,6 +4367,374 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
3703
4367
  }]
3704
4368
  }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledby", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], iconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnly", required: false }] }] } });
3705
4369
 
4370
+ const SIZE = {
4371
+ sm: 'h-7 gap-1 px-2.5 text-xs',
4372
+ md: 'h-8 gap-1.5 px-3 text-sm',
4373
+ };
4374
+ /**
4375
+ * Chip look shared by sds-chip and sds-chip-group. Selected is more than a tint — a brand border, bolder
4376
+ * text and (by default) a check — so it reads in greyscale too. In dark mode the brand text/tint are
4377
+ * swapped for ink/muted (the library's rule for a monochrome brand's dark ramp).
4378
+ */
4379
+ function chipClass(size, selected) {
4380
+ const base = 'inline-flex shrink-0 select-none items-center whitespace-nowrap rounded-full border font-medium ' +
4381
+ 'transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 ' +
4382
+ 'focus-visible:ring-offset-1 focus-visible:ring-offset-surface disabled:cursor-not-allowed disabled:opacity-50 ' +
4383
+ SIZE[size];
4384
+ return selected
4385
+ ? `${base} cursor-pointer border-brand-600 bg-brand-50 font-semibold text-brand-700 ` +
4386
+ 'dark:border-ink-soft dark:bg-muted dark:text-ink'
4387
+ : `${base} cursor-pointer border-border bg-surface text-ink-soft hover:border-ink-faint hover:text-ink`;
4388
+ }
4389
+ const COUNT_CLASS = 'inline-flex min-w-5 items-center justify-center rounded-full bg-ink/5 px-1.5 text-[11px] font-semibold tabular-nums dark:bg-white/10';
4390
+ /**
4391
+ * A single on/off filter chip (`aria-pressed`), e.g. "Sold out only" or "With comments". Two-way
4392
+ * `[(selected)]`. For a set of chips — one or several picked — use `sds-chip-group`.
4393
+ *
4394
+ * @example
4395
+ * <sds-chip label="Sold out only" icon="ban" [(selected)]="soldOutOnly" />
4396
+ */
4397
+ class SdsChip {
4398
+ /** Visible text — also the chip's accessible name. */
4399
+ label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
4400
+ /** On/off. Two-way: `[(selected)]`. */
4401
+ selected = model(false, ...(ngDevMode ? [{ debugName: "selected" }] : []));
4402
+ icon = input(null, ...(ngDevMode ? [{ debugName: "icon" }] : []));
4403
+ count = input(null, ...(ngDevMode ? [{ debugName: "count" }] : []));
4404
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
4405
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
4406
+ /** Replace the icon with a check while selected. */
4407
+ showCheck = input(true, ...(ngDevMode ? [{ debugName: "showCheck", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
4408
+ countClass = COUNT_CLASS;
4409
+ classes = computed(() => chipClass(this.size(), this.selected()), ...(ngDevMode ? [{ debugName: "classes" }] : []));
4410
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
4411
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsChip, isStandalone: true, selector: "sds-chip", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showCheck: { classPropertyName: "showCheck", publicName: "showCheck", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selectedChange" }, host: { classAttribute: "inline-flex" }, ngImport: i0, template: `
4412
+ <button
4413
+ type="button"
4414
+ [attr.aria-pressed]="selected()"
4415
+ [disabled]="disabled()"
4416
+ [class]="classes()"
4417
+ (click)="selected.set(!selected())"
4418
+ >
4419
+ @if (selected() && showCheck()) {
4420
+ <sds-icon
4421
+ name="check"
4422
+ [size]="size() === 'sm' ? 12 : 14"
4423
+ [strokeWidth]="3"
4424
+ class="shrink-0"
4425
+ />
4426
+ } @else if (icon()) {
4427
+ <sds-icon [name]="icon()!" [size]="size() === 'sm' ? 13 : 15" class="shrink-0" />
4428
+ }
4429
+ <span class="truncate">{{ label() }}</span>
4430
+ @if (count() !== null && count() !== undefined) {
4431
+ <span [class]="countClass">{{ count() }}</span>
4432
+ }
4433
+ </button>
4434
+ `, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4435
+ }
4436
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsChip, decorators: [{
4437
+ type: Component,
4438
+ args: [{
4439
+ selector: 'sds-chip',
4440
+ changeDetection: ChangeDetectionStrategy.OnPush,
4441
+ imports: [SdsIcon],
4442
+ host: { class: 'inline-flex' },
4443
+ template: `
4444
+ <button
4445
+ type="button"
4446
+ [attr.aria-pressed]="selected()"
4447
+ [disabled]="disabled()"
4448
+ [class]="classes()"
4449
+ (click)="selected.set(!selected())"
4450
+ >
4451
+ @if (selected() && showCheck()) {
4452
+ <sds-icon
4453
+ name="check"
4454
+ [size]="size() === 'sm' ? 12 : 14"
4455
+ [strokeWidth]="3"
4456
+ class="shrink-0"
4457
+ />
4458
+ } @else if (icon()) {
4459
+ <sds-icon [name]="icon()!" [size]="size() === 'sm' ? 13 : 15" class="shrink-0" />
4460
+ }
4461
+ <span class="truncate">{{ label() }}</span>
4462
+ @if (count() !== null && count() !== undefined) {
4463
+ <span [class]="countClass">{{ count() }}</span>
4464
+ }
4465
+ </button>
4466
+ `,
4467
+ }]
4468
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }, { type: i0.Output, args: ["selectedChange"] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCheck", required: false }] }] } });
4469
+ let nextId$c = 0;
4470
+ /**
4471
+ * A set of chips: pick ONE (a filter row such as "All · Missing photo · Hidden", VAT presets, a date
4472
+ * range) or, with `multiple`, SEVERAL (categories of an item, days of the week, dietary tags). Unlike
4473
+ * sds-segmented (a compact joined bar), chips wrap onto several lines and can carry a count.
4474
+ *
4475
+ * Value: two-way `[(value)]`, or a form control (`formControlName` / `[(ngModel)]` —
4476
+ * ControlValueAccessor). Single mode holds the picked value (or null); `multiple` holds an array, in the
4477
+ * order of the options.
4478
+ *
4479
+ * Accessibility: single mode is a `radiogroup` (one tab stop, arrows/Home/End move and select, like
4480
+ * native radios); `multiple` is a `group` of `aria-pressed` toggle buttons. Name it with [ariaLabel] or
4481
+ * [ariaLabelledby].
4482
+ *
4483
+ * @example
4484
+ * <sds-chip-group ariaLabel="Filter" [options]="filters()" [(value)]="issue" />
4485
+ * <!-- filters = [{ value: null, label: 'All', count: 34 }, { value: 'NO_PHOTO', label: 'Missing photo', count: 5 }] -->
4486
+ *
4487
+ * @example
4488
+ * <sds-chip-group ariaLabel="Categories" multiple [options]="categories()" formControlName="categoryIds" />
4489
+ */
4490
+ class SdsChipGroup {
4491
+ host = inject(ElementRef);
4492
+ baseId = `sds-chip-group-${nextId$c++}`;
4493
+ /** Options: `{ value, label, icon?, count?, disabled? }` objects, or bare strings/numbers. */
4494
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
4495
+ /** Picked value (single) or values (multiple). Two-way: `[(value)]`. */
4496
+ value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
4497
+ /** Allow several chips at once; the value becomes an array. */
4498
+ multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
4499
+ /** Single mode: clicking the picked chip again clears the value to null. */
4500
+ deselectable = input(false, ...(ngDevMode ? [{ debugName: "deselectable", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
4501
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
4502
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
4503
+ ariaLabelledby = input('', ...(ngDevMode ? [{ debugName: "ariaLabelledby" }] : []));
4504
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
4505
+ /** Wrap onto several lines (default) or scroll horizontally on one line. */
4506
+ wrap = input(true, ...(ngDevMode ? [{ debugName: "wrap", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
4507
+ /** `multiple`: a check replaces the icon on picked chips. */
4508
+ showCheck = input(true, ...(ngDevMode ? [{ debugName: "showCheck", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
4509
+ countClass = COUNT_CLASS;
4510
+ cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : []));
4511
+ isDisabled = computed(() => this.disabled() || this.cvaDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
4512
+ chips = computed(() => this.options().map((o) => o !== null && typeof o === 'object'
4513
+ ? {
4514
+ value: o.value,
4515
+ label: o.label,
4516
+ icon: o.icon ?? null,
4517
+ count: o.count ?? null,
4518
+ disabled: !!o.disabled,
4519
+ }
4520
+ : { value: o, label: String(o), icon: null, count: null, disabled: false }), ...(ngDevMode ? [{ debugName: "chips" }] : []));
4521
+ selectedValues = computed(() => {
4522
+ const v = this.value();
4523
+ if (this.multiple())
4524
+ return Array.isArray(v) ? v : [];
4525
+ return [v];
4526
+ }, ...(ngDevMode ? [{ debugName: "selectedValues" }] : []));
4527
+ /** Single mode's roving tab stop: the picked chip, else the first enabled one. */
4528
+ tabStop = computed(() => {
4529
+ const chips = this.chips();
4530
+ const sel = chips.findIndex((c) => this.isSelected(c));
4531
+ if (sel >= 0 && !chips[sel].disabled)
4532
+ return sel;
4533
+ return chips.findIndex((c) => !c.disabled);
4534
+ }, ...(ngDevMode ? [{ debugName: "tabStop" }] : []));
4535
+ isSelected(chip) {
4536
+ return this.selectedValues().includes(chip.value);
4537
+ }
4538
+ chipId(i) {
4539
+ return `${this.baseId}-${i}`;
4540
+ }
4541
+ classFor(chip) {
4542
+ return chipClass(this.size(), this.isSelected(chip));
4543
+ }
4544
+ toggle(i) {
4545
+ const chip = this.chips()[i];
4546
+ if (!chip || chip.disabled || this.isDisabled())
4547
+ return;
4548
+ let next;
4549
+ if (this.multiple()) {
4550
+ const picked = new Set(this.selectedValues());
4551
+ if (picked.has(chip.value))
4552
+ picked.delete(chip.value);
4553
+ else
4554
+ picked.add(chip.value);
4555
+ // Keep the options' order, whatever the click order.
4556
+ next = this.chips()
4557
+ .map((c) => c.value)
4558
+ .filter((v) => picked.has(v));
4559
+ }
4560
+ else {
4561
+ if (this.isSelected(chip)) {
4562
+ if (!this.deselectable())
4563
+ return;
4564
+ next = null;
4565
+ }
4566
+ else {
4567
+ next = chip.value;
4568
+ }
4569
+ }
4570
+ this.value.set(next);
4571
+ this.onChange(next);
4572
+ }
4573
+ onKeydown(event) {
4574
+ if (this.multiple() || this.isDisabled())
4575
+ return;
4576
+ const chips = this.chips();
4577
+ if (!chips.length)
4578
+ return;
4579
+ const current = Number(event.target.dataset['index'] ?? this.tabStop());
4580
+ let next = null;
4581
+ switch (event.key) {
4582
+ case 'ArrowRight':
4583
+ case 'ArrowDown':
4584
+ next = this.step(current, 1);
4585
+ break;
4586
+ case 'ArrowLeft':
4587
+ case 'ArrowUp':
4588
+ next = this.step(current, -1);
4589
+ break;
4590
+ case 'Home':
4591
+ next = chips.findIndex((c) => !c.disabled);
4592
+ break;
4593
+ case 'End':
4594
+ for (let i = chips.length - 1; i >= 0; i--) {
4595
+ if (!chips[i].disabled) {
4596
+ next = i;
4597
+ break;
4598
+ }
4599
+ }
4600
+ break;
4601
+ default:
4602
+ return;
4603
+ }
4604
+ event.preventDefault();
4605
+ if (next === null || next < 0)
4606
+ return;
4607
+ if (!this.isSelected(chips[next]))
4608
+ this.toggle(next);
4609
+ this.host.nativeElement.querySelector(`#${this.chipId(next)}`)?.focus();
4610
+ }
4611
+ step(from, delta) {
4612
+ const chips = this.chips();
4613
+ let i = from;
4614
+ for (let n = 0; n < chips.length; n++) {
4615
+ i = (i + delta + chips.length) % chips.length;
4616
+ if (!chips[i].disabled)
4617
+ return i;
4618
+ }
4619
+ return null;
4620
+ }
4621
+ // --- ControlValueAccessor -------------------------------------------------
4622
+ onChange = () => { };
4623
+ onTouched = () => { };
4624
+ writeValue(value) {
4625
+ // Normalised on read (selectedValues), so a null written to a `multiple` group is simply "none".
4626
+ this.value.set(value ?? null);
4627
+ }
4628
+ registerOnChange(fn) {
4629
+ this.onChange = fn;
4630
+ }
4631
+ registerOnTouched(fn) {
4632
+ this.onTouched = fn;
4633
+ }
4634
+ setDisabledState(isDisabled) {
4635
+ this.cvaDisabled.set(isDisabled);
4636
+ }
4637
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsChipGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
4638
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsChipGroup, isStandalone: true, selector: "sds-chip-group", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, deselectable: { classPropertyName: "deselectable", publicName: "deselectable", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "ariaLabelledby", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, wrap: { classPropertyName: "wrap", publicName: "wrap", isSignal: true, isRequired: false, transformFunction: null }, showCheck: { classPropertyName: "showCheck", publicName: "showCheck", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { classAttribute: "block" }, providers: [
4639
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SdsChipGroup), multi: true },
4640
+ ], ngImport: i0, template: `
4641
+ <div
4642
+ [attr.role]="multiple() ? 'group' : 'radiogroup'"
4643
+ [attr.aria-label]="ariaLabel() || null"
4644
+ [attr.aria-labelledby]="ariaLabelledby() || null"
4645
+ [attr.aria-disabled]="isDisabled() ? 'true' : null"
4646
+ class="flex gap-2"
4647
+ [class]="wrap() ? 'flex-wrap' : 'overflow-x-auto pb-1'"
4648
+ (keydown)="onKeydown($event)"
4649
+ (focusout)="onTouched()"
4650
+ >
4651
+ @for (chip of chips(); track $index; let i = $index) {
4652
+ <button
4653
+ type="button"
4654
+ [id]="chipId(i)"
4655
+ [attr.role]="multiple() ? null : 'radio'"
4656
+ [attr.aria-checked]="multiple() ? null : isSelected(chip)"
4657
+ [attr.aria-pressed]="multiple() ? isSelected(chip) : null"
4658
+ [attr.tabindex]="multiple() || i === tabStop() ? 0 : -1"
4659
+ [attr.data-index]="i"
4660
+ [disabled]="isDisabled() || chip.disabled"
4661
+ [class]="classFor(chip)"
4662
+ (click)="toggle(i)"
4663
+ >
4664
+ @if (isSelected(chip) && multiple() && showCheck()) {
4665
+ <sds-icon
4666
+ name="check"
4667
+ [size]="size() === 'sm' ? 12 : 14"
4668
+ [strokeWidth]="3"
4669
+ class="shrink-0"
4670
+ />
4671
+ } @else if (chip.icon) {
4672
+ <sds-icon [name]="chip.icon" [size]="size() === 'sm' ? 13 : 15" class="shrink-0" />
4673
+ }
4674
+ <span class="truncate">{{ chip.label }}</span>
4675
+ @if (chip.count !== null) {
4676
+ <span [class]="countClass">{{ chip.count }}</span>
4677
+ }
4678
+ </button>
4679
+ }
4680
+ </div>
4681
+ `, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4682
+ }
4683
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsChipGroup, decorators: [{
4684
+ type: Component,
4685
+ args: [{
4686
+ selector: 'sds-chip-group',
4687
+ changeDetection: ChangeDetectionStrategy.OnPush,
4688
+ imports: [SdsIcon],
4689
+ providers: [
4690
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SdsChipGroup), multi: true },
4691
+ ],
4692
+ host: { class: 'block' },
4693
+ template: `
4694
+ <div
4695
+ [attr.role]="multiple() ? 'group' : 'radiogroup'"
4696
+ [attr.aria-label]="ariaLabel() || null"
4697
+ [attr.aria-labelledby]="ariaLabelledby() || null"
4698
+ [attr.aria-disabled]="isDisabled() ? 'true' : null"
4699
+ class="flex gap-2"
4700
+ [class]="wrap() ? 'flex-wrap' : 'overflow-x-auto pb-1'"
4701
+ (keydown)="onKeydown($event)"
4702
+ (focusout)="onTouched()"
4703
+ >
4704
+ @for (chip of chips(); track $index; let i = $index) {
4705
+ <button
4706
+ type="button"
4707
+ [id]="chipId(i)"
4708
+ [attr.role]="multiple() ? null : 'radio'"
4709
+ [attr.aria-checked]="multiple() ? null : isSelected(chip)"
4710
+ [attr.aria-pressed]="multiple() ? isSelected(chip) : null"
4711
+ [attr.tabindex]="multiple() || i === tabStop() ? 0 : -1"
4712
+ [attr.data-index]="i"
4713
+ [disabled]="isDisabled() || chip.disabled"
4714
+ [class]="classFor(chip)"
4715
+ (click)="toggle(i)"
4716
+ >
4717
+ @if (isSelected(chip) && multiple() && showCheck()) {
4718
+ <sds-icon
4719
+ name="check"
4720
+ [size]="size() === 'sm' ? 12 : 14"
4721
+ [strokeWidth]="3"
4722
+ class="shrink-0"
4723
+ />
4724
+ } @else if (chip.icon) {
4725
+ <sds-icon [name]="chip.icon" [size]="size() === 'sm' ? 13 : 15" class="shrink-0" />
4726
+ }
4727
+ <span class="truncate">{{ chip.label }}</span>
4728
+ @if (chip.count !== null) {
4729
+ <span [class]="countClass">{{ chip.count }}</span>
4730
+ }
4731
+ </button>
4732
+ }
4733
+ </div>
4734
+ `,
4735
+ }]
4736
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], deselectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "deselectable", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledby", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], wrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrap", required: false }] }], showCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCheck", required: false }] }] } });
4737
+
3706
4738
  /**
3707
4739
  * Each status has its own glyph SHAPE (circle-check, triangle, octagon, circle-i) as well as its own
3708
4740
  * tint, so the row still reads in greyscale or for a colour-blind reader. `label` is the English
@@ -10727,8 +11759,13 @@ class SdsTabsStore {
10727
11759
  id = `sds-tabs-${nextId$3++}`;
10728
11760
  /** Whether the tab list scrolls horizontally (mirrors [scrollable]). */
10729
11761
  scrollable = signal(false, ...(ngDevMode ? [{ debugName: "scrollable" }] : []));
11762
+ /** Controlled mode (mirrors [controlled]): a click only REQUESTS a tab, the host sets [value]. */
11763
+ controlled = signal(false, ...(ngDevMode ? [{ debugName: "controlled" }] : []));
11764
+ /** Called with every tab the user picks (wired to the host's (tabSelect) output). */
11765
+ onSelect = () => { };
10730
11766
  value;
10731
11767
  registered = signal([], ...(ngDevMode ? [{ debugName: "registered" }] : []));
11768
+ panels = signal([], ...(ngDevMode ? [{ debugName: "panels" }] : []));
10732
11769
  /** Called once by <sds-tabs> to share its two-way `value` signal. */
10733
11770
  connect(value) {
10734
11771
  this.value = value;
@@ -10739,8 +11776,11 @@ class SdsTabsStore {
10739
11776
  isActive(value) {
10740
11777
  return this.value() === value;
10741
11778
  }
11779
+ /** A user pick. Uncontrolled: becomes the active tab. Controlled: only reported to the host. */
10742
11780
  select(value) {
10743
- this.value.set(value);
11781
+ this.onSelect(value);
11782
+ if (!this.controlled())
11783
+ this.value.set(value);
10744
11784
  }
10745
11785
  registerTab(value) {
10746
11786
  this.registered.update((list) => (list.includes(value) ? list : [...list, value]));
@@ -10751,6 +11791,16 @@ class SdsTabsStore {
10751
11791
  unregisterTab(value) {
10752
11792
  this.registered.update((list) => list.filter((v) => v !== value));
10753
11793
  }
11794
+ registerPanel(value) {
11795
+ this.panels.update((list) => (list.includes(value) ? list : [...list, value]));
11796
+ }
11797
+ unregisterPanel(value) {
11798
+ this.panels.update((list) => list.filter((v) => v !== value));
11799
+ }
11800
+ /** Whether an <sds-tab-panel> exists for this value (tabs whose content lives elsewhere have none). */
11801
+ hasPanel(value) {
11802
+ return this.panels().includes(value);
11803
+ }
10754
11804
  tabId(value) {
10755
11805
  return `${this.id}-tab-${value}`;
10756
11806
  }
@@ -10770,6 +11820,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
10770
11820
  * <sds-tab-panel> [value]; it is two-way bindable via [(value)]. When omitted,
10771
11821
  * the first registered tab is selected.
10772
11822
  *
11823
+ * **Controlled tabs** (`controlled`): a click, arrow key or Home/End only emits `(tabSelect)`; the active
11824
+ * tab changes when the host changes `[value]`. Use it when the active tab lives somewhere else — a
11825
+ * `?tab=` query param — or when switching can be refused (an unsaved-changes guard): the tab never
11826
+ * shows as selected before the host agrees. `(tabSelect)` also fires in the default mode.
11827
+ *
11828
+ * **Panels are optional.** A tab set whose content is rendered by the host (an `@switch` on the value, a
11829
+ * router outlet) can omit <sds-tab-panels>; a tab then carries no `aria-controls` to a missing panel.
11830
+ *
10773
11831
  * @example
10774
11832
  * <sds-tabs [(value)]="active">
10775
11833
  * <sds-tab-list>
@@ -10781,6 +11839,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
10781
11839
  * <sds-tab-panel value="1">…</sds-tab-panel>
10782
11840
  * </sds-tab-panels>
10783
11841
  * </sds-tabs>
11842
+ *
11843
+ * @example
11844
+ * <!-- driven by a query param, content rendered by the host -->
11845
+ * <sds-tabs controlled [value]="activeTab()" (tabSelect)="selectTab($event)">
11846
+ * <sds-tab-list>
11847
+ * @for (tab of tabs; track tab) {
11848
+ * <sds-tab [value]="tab">{{ 'menu.tabs.' + tab | translate }}</sds-tab>
11849
+ * }
11850
+ * </sds-tab-list>
11851
+ * </sds-tabs>
10784
11852
  */
10785
11853
  class SdsTabs {
10786
11854
  store = inject(SdsTabsStore);
@@ -10788,12 +11856,21 @@ class SdsTabs {
10788
11856
  value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
10789
11857
  /** Let the tab list scroll horizontally instead of overflowing. */
10790
11858
  scrollable = input(false, ...(ngDevMode ? [{ debugName: "scrollable", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
11859
+ /**
11860
+ * Controlled mode: a user pick only emits (tabSelect); the host decides by setting [value]. See the
11861
+ * class note.
11862
+ */
11863
+ controlled = input(false, ...(ngDevMode ? [{ debugName: "controlled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
11864
+ /** Emits the value of the tab the user picked (in both modes). */
11865
+ tabSelect = output();
10791
11866
  constructor() {
10792
11867
  this.store.connect(this.value);
11868
+ this.store.onSelect = (value) => this.tabSelect.emit(value);
10793
11869
  effect(() => this.store.scrollable.set(this.scrollable()));
11870
+ effect(() => this.store.controlled.set(this.controlled()));
10794
11871
  }
10795
11872
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTabs, deps: [], target: i0.ɵɵFactoryTarget.Component });
10796
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.25", type: SdsTabs, isStandalone: true, selector: "sds-tabs", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, scrollable: { classPropertyName: "scrollable", publicName: "scrollable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { classAttribute: "block" }, providers: [SdsTabsStore], ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
11873
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.25", type: SdsTabs, isStandalone: true, selector: "sds-tabs", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, scrollable: { classPropertyName: "scrollable", publicName: "scrollable", isSignal: true, isRequired: false, transformFunction: null }, controlled: { classPropertyName: "controlled", publicName: "controlled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", tabSelect: "tabSelect" }, host: { classAttribute: "block" }, providers: [SdsTabsStore], ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
10797
11874
  }
10798
11875
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTabs, decorators: [{
10799
11876
  type: Component,
@@ -10804,7 +11881,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
10804
11881
  host: { class: 'block' },
10805
11882
  template: `<ng-content />`,
10806
11883
  }]
10807
- }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], scrollable: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollable", required: false }] }] } });
11884
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], scrollable: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollable", required: false }] }], controlled: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlled", required: false }] }], tabSelect: [{ type: i0.Output, args: ["tabSelect"] }] } });
10808
11885
 
10809
11886
  /**
10810
11887
  * Container (role="tablist") for the <sds-tab> headers. Renders the bottom rule
@@ -10901,7 +11978,7 @@ class SdsTab {
10901
11978
  role="tab"
10902
11979
  [id]="store.tabId(value())"
10903
11980
  [attr.aria-selected]="active()"
10904
- [attr.aria-controls]="store.panelId(value())"
11981
+ [attr.aria-controls]="store.hasPanel(value()) ? store.panelId(value()) : null"
10905
11982
  [attr.aria-disabled]="disabled() ? 'true' : null"
10906
11983
  [attr.tabindex]="active() && !disabled() ? 0 : -1"
10907
11984
  [disabled]="disabled()"
@@ -10924,7 +12001,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
10924
12001
  role="tab"
10925
12002
  [id]="store.tabId(value())"
10926
12003
  [attr.aria-selected]="active()"
10927
- [attr.aria-controls]="store.panelId(value())"
12004
+ [attr.aria-controls]="store.hasPanel(value()) ? store.panelId(value()) : null"
10928
12005
  [attr.aria-disabled]="disabled() ? 'true' : null"
10929
12006
  [attr.tabindex]="active() && !disabled() ? 0 : -1"
10930
12007
  [disabled]="disabled()"
@@ -10969,6 +12046,12 @@ class SdsTabPanel {
10969
12046
  store = inject(SdsTabsStore);
10970
12047
  value = input.required(...(ngDevMode ? [{ debugName: "value" }] : []));
10971
12048
  active = computed(() => this.store.isActive(this.value()), ...(ngDevMode ? [{ debugName: "active" }] : []));
12049
+ ngOnInit() {
12050
+ this.store.registerPanel(this.value());
12051
+ }
12052
+ ngOnDestroy() {
12053
+ this.store.unregisterPanel(this.value());
12054
+ }
10972
12055
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTabPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
10973
12056
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsTabPanel, isStandalone: true, selector: "sds-tab-panel", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: `
10974
12057
  @if (active()) {
@@ -11382,6 +12465,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
11382
12465
  }]
11383
12466
  }] });
11384
12467
 
12468
+ /**
12469
+ * Replaces the table's plain `emptyMessage` with rich content — an explanation and a call to action,
12470
+ * a "no results for …" line. Rendered in the full-width empty row, only when not `loading`.
12471
+ *
12472
+ * @example
12473
+ * <sds-table [value]="categories()" [columns]="cols">
12474
+ * <ng-template sdsTableEmpty>
12475
+ * <p>No categories yet.</p>
12476
+ * <sds-button size="sm" (click)="create()">Add a category</sds-button>
12477
+ * </ng-template>
12478
+ * </sds-table>
12479
+ */
12480
+ class SdsTableEmpty {
12481
+ /** The wrapped template, rendered by the host table via ngTemplateOutlet. */
12482
+ template = inject(TemplateRef);
12483
+ /** Lets the compiler type-check `let-*` bindings against SdsTableEmptyContext. */
12484
+ static ngTemplateContextGuard(_dir, _ctx) {
12485
+ return true;
12486
+ }
12487
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTableEmpty, deps: [], target: i0.ɵɵFactoryTarget.Directive });
12488
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: SdsTableEmpty, isStandalone: true, selector: "ng-template[sdsTableEmpty]", ngImport: i0 });
12489
+ }
12490
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTableEmpty, decorators: [{
12491
+ type: Directive,
12492
+ args: [{
12493
+ selector: 'ng-template[sdsTableEmpty]',
12494
+ standalone: true,
12495
+ }]
12496
+ }] });
12497
+
11385
12498
  const SIZES = {
11386
12499
  sm: { cell: 'px-3 py-2 text-sm', head: 'px-3 py-2' },
11387
12500
  md: { cell: 'px-4 py-2.5 text-sm', head: 'px-4 py-2.5' },
@@ -11413,6 +12526,11 @@ let nextId$1 = 0;
11413
12526
  * - **Custom headers:** `<ng-template sdsHeader="field">` replaces a column's header content (label and
11414
12527
  * sort button) — e.g. a "select all" checkbox above your own checkbox column. Its context carries the
11415
12528
  * filtered `rows` (all pages) and the displayed `pageRows` (see {@link SdsTableHeaderContext}).
12529
+ * - **Clickable rows:** `rowClickable` makes each row an action (pointer, tab stop, Enter/Space) that
12530
+ * emits `(rowClick)`; clicks on buttons, links and fields inside a cell are not row clicks, so no
12531
+ * `stopPropagation()` is needed. `[rowClass]` adds per-row classes (e.g. dim a blocked row).
12532
+ * - **Empty state:** `<ng-template sdsTableEmpty let-query>` replaces the plain `emptyMessage` (e.g. a
12533
+ * sentence and a "create" button); it is not shown while `loading`.
11416
12534
  * - **Labels:** every visible and spoken string is an input with an English default (paginator,
11417
12535
  * filter, selection, expansion, editor). An app serving more than one language passes translations.
11418
12536
  *
@@ -11484,6 +12602,14 @@ class SdsTable {
11484
12602
  selection = model(null, ...(ngDevMode ? [{ debugName: "selection" }] : []));
11485
12603
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
11486
12604
  emptyMessage = input('No data', ...(ngDevMode ? [{ debugName: "emptyMessage" }] : []));
12605
+ /**
12606
+ * Rows are actions: a pointer cursor, a tab stop per row, and Enter/Space emit `(rowClick)` like a
12607
+ * click. Clicks that land on a control inside a cell (button, link, input, checkbox…) do not count as
12608
+ * a row click, so a row can open a drawer while its cells keep their own buttons.
12609
+ */
12610
+ rowClickable = input(false, ...(ngDevMode ? [{ debugName: "rowClickable", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
12611
+ /** Extra classes per body row, e.g. `(row) => row.blocked ? 'opacity-60' : ''`. */
12612
+ rowClass = input(null, ...(ngDevMode ? [{ debugName: "rowClass" }] : []));
11487
12613
  caption = input('', ...(ngDevMode ? [{ debugName: "caption" }] : []));
11488
12614
  /** Fallback accessible name for the `<table>` when no `caption` is set. */
11489
12615
  ariaLabel = input('Data table', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
@@ -11574,6 +12700,11 @@ class SdsTable {
11574
12700
  expansionTemplate() {
11575
12701
  return this.rowExpansion()?.template ?? null;
11576
12702
  }
12703
+ // --- empty-state template -------------------------------------------------
12704
+ emptyTpl = contentChild(SdsTableEmpty, ...(ngDevMode ? [{ debugName: "emptyTpl" }] : []));
12705
+ emptyTemplate() {
12706
+ return this.emptyTpl()?.template ?? null;
12707
+ }
11577
12708
  // --- pipeline state -------------------------------------------------------
11578
12709
  filterText = signal('', ...(ngDevMode ? [{ debugName: "filterText" }] : []));
11579
12710
  sortField = signal(null, ...(ngDevMode ? [{ debugName: "sortField" }] : []));
@@ -11809,9 +12940,14 @@ class SdsTable {
11809
12940
  this.toggleRowSelection(row);
11810
12941
  }
11811
12942
  }
11812
- /** Enter/Space on a (single-mode) focusable row toggles its selection. */
12943
+ /** Enter/Space on a focusable row (single selection or `rowClickable`) acts like a click on it. */
11813
12944
  onRowKeydown(event, row) {
11814
- if (this.selectionMode() !== 'single' || this.loading() || this.editable())
12945
+ if (this.loading() || this.editable())
12946
+ return;
12947
+ if (this.selectionMode() !== 'single' && !this.rowClickable())
12948
+ return;
12949
+ // Only the row itself: Enter on a button inside a cell belongs to that button.
12950
+ if (event.target !== event.currentTarget)
11815
12951
  return;
11816
12952
  event.preventDefault();
11817
12953
  this.onRowClick(row);
@@ -11830,9 +12966,11 @@ class SdsTable {
11830
12966
  this.rowSelect.emit(row);
11831
12967
  }
11832
12968
  }
11833
- onRowClick(row) {
12969
+ onRowClick(row, event) {
11834
12970
  if (this.loading())
11835
12971
  return;
12972
+ if (event && this.fromInnerControl(event))
12973
+ return;
11836
12974
  this.rowClick.emit(row);
11837
12975
  if (this.selectionMode() === 'single') {
11838
12976
  if (this.equals(this.selection(), row)) {
@@ -11845,6 +12983,16 @@ class SdsTable {
11845
12983
  }
11846
12984
  }
11847
12985
  }
12986
+ /** Whether a click started on an interactive element inside the row (not on the row itself). */
12987
+ fromInnerControl(event) {
12988
+ const row = event.currentTarget;
12989
+ const target = event.target;
12990
+ if (!row || !target || !(target instanceof Element))
12991
+ return false;
12992
+ const control = target.closest('button, a[href], input, select, textarea, label, [contenteditable="true"], ' +
12993
+ '[role="button"], [role="checkbox"], [role="switch"], [role="radio"], [role="combobox"], [role="menuitem"]');
12994
+ return !!control && control !== row && row.contains(control);
12995
+ }
11848
12996
  // --- paginator ------------------------------------------------------------
11849
12997
  onPageChange(state) {
11850
12998
  this.editing.set(null);
@@ -12378,7 +13526,7 @@ class SdsTable {
12378
13526
  const align = ALIGN[col?.align ?? 'left'];
12379
13527
  return `${SIZES[this.size()].cell} ${align} text-ink align-middle`;
12380
13528
  }
12381
- rowClass(row, index) {
13529
+ bodyRowClass(row, index) {
12382
13530
  const parts = ['transition-colors'];
12383
13531
  if (this.gridlines())
12384
13532
  parts.push('divide-x divide-border');
@@ -12390,9 +13538,12 @@ class SdsTable {
12390
13538
  }
12391
13539
  if (this.hoverable() && !this.isRowSelected(row))
12392
13540
  parts.push('hover:bg-muted/60');
12393
- if (this.selectionMode() === 'single') {
13541
+ if (this.selectionMode() === 'single' || (this.rowClickable() && !this.editable())) {
12394
13542
  parts.push('cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-brand-500');
12395
13543
  }
13544
+ const extra = this.rowClass()?.(row, index);
13545
+ if (extra)
13546
+ parts.push(extra);
12396
13547
  return parts.join(' ');
12397
13548
  }
12398
13549
  checkboxClass(checked, indeterminate) {
@@ -12401,7 +13552,7 @@ class SdsTable {
12401
13552
  : 'border-border bg-surface';
12402
13553
  }
12403
13554
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
12404
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsTable, isStandalone: true, selector: "sds-table", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, dataKey: { classPropertyName: "dataKey", publicName: "dataKey", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, gridlines: { classPropertyName: "gridlines", publicName: "gridlines", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, paginator: { classPropertyName: "paginator", publicName: "paginator", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageOptions: { classPropertyName: "rowsPerPageOptions", publicName: "rowsPerPageOptions", isSignal: true, isRequired: false, transformFunction: null }, showCurrentPageReport: { classPropertyName: "showCurrentPageReport", publicName: "showCurrentPageReport", isSignal: true, isRequired: false, transformFunction: null }, currentPageReportTemplate: { classPropertyName: "currentPageReportTemplate", publicName: "currentPageReportTemplate", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageLabel: { classPropertyName: "rowsPerPageLabel", publicName: "rowsPerPageLabel", isSignal: true, isRequired: false, transformFunction: null }, paginationLabel: { classPropertyName: "paginationLabel", publicName: "paginationLabel", isSignal: true, isRequired: false, transformFunction: null }, firstPageLabel: { classPropertyName: "firstPageLabel", publicName: "firstPageLabel", isSignal: true, isRequired: false, transformFunction: null }, previousPageLabel: { classPropertyName: "previousPageLabel", publicName: "previousPageLabel", isSignal: true, isRequired: false, transformFunction: null }, nextPageLabel: { classPropertyName: "nextPageLabel", publicName: "nextPageLabel", isSignal: true, isRequired: false, transformFunction: null }, lastPageLabel: { classPropertyName: "lastPageLabel", publicName: "lastPageLabel", isSignal: true, isRequired: false, transformFunction: null }, pageLinkLabel: { classPropertyName: "pageLinkLabel", publicName: "pageLinkLabel", isSignal: true, isRequired: false, transformFunction: null }, globalFilter: { classPropertyName: "globalFilter", publicName: "globalFilter", isSignal: true, isRequired: false, transformFunction: null }, globalFilterPlaceholder: { classPropertyName: "globalFilterPlaceholder", publicName: "globalFilterPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, globalFilterAriaLabel: { classPropertyName: "globalFilterAriaLabel", publicName: "globalFilterAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, globalFilterFields: { classPropertyName: "globalFilterFields", publicName: "globalFilterFields", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, caption: { classPropertyName: "caption", publicName: "caption", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, scrollHeight: { classPropertyName: "scrollHeight", publicName: "scrollHeight", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null }, totalRecords: { classPropertyName: "totalRecords", publicName: "totalRecords", isSignal: true, isRequired: false, transformFunction: null }, expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, resizableColumns: { classPropertyName: "resizableColumns", publicName: "resizableColumns", isSignal: true, isRequired: false, transformFunction: null }, reorderableColumns: { classPropertyName: "reorderableColumns", publicName: "reorderableColumns", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, editOn: { classPropertyName: "editOn", publicName: "editOn", isSignal: true, isRequired: false, transformFunction: null }, cellErrors: { classPropertyName: "cellErrors", publicName: "cellErrors", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, dirtyLabel: { classPropertyName: "dirtyLabel", publicName: "dirtyLabel", isSignal: true, isRequired: false, transformFunction: null }, selectPlaceholder: { classPropertyName: "selectPlaceholder", publicName: "selectPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, selectAllLabel: { classPropertyName: "selectAllLabel", publicName: "selectAllLabel", isSignal: true, isRequired: false, transformFunction: null }, selectRowLabel: { classPropertyName: "selectRowLabel", publicName: "selectRowLabel", isSignal: true, isRequired: false, transformFunction: null }, selectionColumnLabel: { classPropertyName: "selectionColumnLabel", publicName: "selectionColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, expandColumnLabel: { classPropertyName: "expandColumnLabel", publicName: "expandColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, expandRowLabel: { classPropertyName: "expandRowLabel", publicName: "expandRowLabel", isSignal: true, isRequired: false, transformFunction: null }, collapseRowLabel: { classPropertyName: "collapseRowLabel", publicName: "collapseRowLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selection: "selectionChange", sortChange: "sortChange", pageChange: "pageChange", rowSelect: "rowSelect", rowUnselect: "rowUnselect", lazyLoad: "lazyLoad", rowClick: "rowClick", rowExpand: "rowExpand", rowCollapse: "rowCollapse", colReorder: "colReorder", cellCommit: "cellCommit" }, host: { classAttribute: "block" }, queries: [{ propertyName: "cellTemplates", predicate: SdsTableCell, isSignal: true }, { propertyName: "headerTemplates", predicate: SdsTableHeader, isSignal: true }, { propertyName: "rowExpansion", first: true, predicate: SdsTableRowExpansion, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "numberEditor", first: true, predicate: SdsInputNumber, descendants: true, isSignal: true }], ngImport: i0, template: `
13555
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsTable, isStandalone: true, selector: "sds-table", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, dataKey: { classPropertyName: "dataKey", publicName: "dataKey", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, gridlines: { classPropertyName: "gridlines", publicName: "gridlines", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, paginator: { classPropertyName: "paginator", publicName: "paginator", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageOptions: { classPropertyName: "rowsPerPageOptions", publicName: "rowsPerPageOptions", isSignal: true, isRequired: false, transformFunction: null }, showCurrentPageReport: { classPropertyName: "showCurrentPageReport", publicName: "showCurrentPageReport", isSignal: true, isRequired: false, transformFunction: null }, currentPageReportTemplate: { classPropertyName: "currentPageReportTemplate", publicName: "currentPageReportTemplate", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageLabel: { classPropertyName: "rowsPerPageLabel", publicName: "rowsPerPageLabel", isSignal: true, isRequired: false, transformFunction: null }, paginationLabel: { classPropertyName: "paginationLabel", publicName: "paginationLabel", isSignal: true, isRequired: false, transformFunction: null }, firstPageLabel: { classPropertyName: "firstPageLabel", publicName: "firstPageLabel", isSignal: true, isRequired: false, transformFunction: null }, previousPageLabel: { classPropertyName: "previousPageLabel", publicName: "previousPageLabel", isSignal: true, isRequired: false, transformFunction: null }, nextPageLabel: { classPropertyName: "nextPageLabel", publicName: "nextPageLabel", isSignal: true, isRequired: false, transformFunction: null }, lastPageLabel: { classPropertyName: "lastPageLabel", publicName: "lastPageLabel", isSignal: true, isRequired: false, transformFunction: null }, pageLinkLabel: { classPropertyName: "pageLinkLabel", publicName: "pageLinkLabel", isSignal: true, isRequired: false, transformFunction: null }, globalFilter: { classPropertyName: "globalFilter", publicName: "globalFilter", isSignal: true, isRequired: false, transformFunction: null }, globalFilterPlaceholder: { classPropertyName: "globalFilterPlaceholder", publicName: "globalFilterPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, globalFilterAriaLabel: { classPropertyName: "globalFilterAriaLabel", publicName: "globalFilterAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, globalFilterFields: { classPropertyName: "globalFilterFields", publicName: "globalFilterFields", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, rowClickable: { classPropertyName: "rowClickable", publicName: "rowClickable", isSignal: true, isRequired: false, transformFunction: null }, rowClass: { classPropertyName: "rowClass", publicName: "rowClass", isSignal: true, isRequired: false, transformFunction: null }, caption: { classPropertyName: "caption", publicName: "caption", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, scrollHeight: { classPropertyName: "scrollHeight", publicName: "scrollHeight", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null }, totalRecords: { classPropertyName: "totalRecords", publicName: "totalRecords", isSignal: true, isRequired: false, transformFunction: null }, expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, resizableColumns: { classPropertyName: "resizableColumns", publicName: "resizableColumns", isSignal: true, isRequired: false, transformFunction: null }, reorderableColumns: { classPropertyName: "reorderableColumns", publicName: "reorderableColumns", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, editOn: { classPropertyName: "editOn", publicName: "editOn", isSignal: true, isRequired: false, transformFunction: null }, cellErrors: { classPropertyName: "cellErrors", publicName: "cellErrors", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, dirtyLabel: { classPropertyName: "dirtyLabel", publicName: "dirtyLabel", isSignal: true, isRequired: false, transformFunction: null }, selectPlaceholder: { classPropertyName: "selectPlaceholder", publicName: "selectPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, selectAllLabel: { classPropertyName: "selectAllLabel", publicName: "selectAllLabel", isSignal: true, isRequired: false, transformFunction: null }, selectRowLabel: { classPropertyName: "selectRowLabel", publicName: "selectRowLabel", isSignal: true, isRequired: false, transformFunction: null }, selectionColumnLabel: { classPropertyName: "selectionColumnLabel", publicName: "selectionColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, expandColumnLabel: { classPropertyName: "expandColumnLabel", publicName: "expandColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, expandRowLabel: { classPropertyName: "expandRowLabel", publicName: "expandRowLabel", isSignal: true, isRequired: false, transformFunction: null }, collapseRowLabel: { classPropertyName: "collapseRowLabel", publicName: "collapseRowLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selection: "selectionChange", sortChange: "sortChange", pageChange: "pageChange", rowSelect: "rowSelect", rowUnselect: "rowUnselect", lazyLoad: "lazyLoad", rowClick: "rowClick", rowExpand: "rowExpand", rowCollapse: "rowCollapse", colReorder: "colReorder", cellCommit: "cellCommit" }, host: { classAttribute: "block" }, queries: [{ propertyName: "cellTemplates", predicate: SdsTableCell, isSignal: true }, { propertyName: "headerTemplates", predicate: SdsTableHeader, isSignal: true }, { propertyName: "rowExpansion", first: true, predicate: SdsTableRowExpansion, descendants: true, isSignal: true }, { propertyName: "emptyTpl", first: true, predicate: SdsTableEmpty, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "numberEditor", first: true, predicate: SdsInputNumber, descendants: true, isSignal: true }], ngImport: i0, template: `
12405
13556
  <div class="rounded-card border border-border bg-surface overflow-hidden">
12406
13557
  <!-- Toolbar: caption + global filter -->
12407
13558
  @if (caption() || globalFilter()) {
@@ -12555,10 +13706,10 @@ class SdsTable {
12555
13706
  <tbody class="divide-y divide-border">
12556
13707
  @for (row of displayRows(); track trackRow($index, row); let i = $index) {
12557
13708
  <tr
12558
- [class]="rowClass(row, i)"
12559
- [attr.tabindex]="selectionMode() === 'single' && !loading() && !editable() ? 0 : null"
13709
+ [class]="bodyRowClass(row, i)"
13710
+ [attr.tabindex]="(selectionMode() === 'single' || rowClickable()) && !loading() && !editable() ? 0 : null"
12560
13711
  [attr.aria-selected]="selectionMode() === 'single' ? (isRowSelected(row) ? 'true' : 'false') : null"
12561
- (click)="onRowClick(row)"
13712
+ (click)="onRowClick(row, $event)"
12562
13713
  (keydown.enter)="onRowKeydown($event, row)"
12563
13714
  (keydown.space)="onRowKeydown($event, row)"
12564
13715
  >
@@ -12783,7 +13934,14 @@ class SdsTable {
12783
13934
  [attr.colspan]="totalColumnCount()"
12784
13935
  class="py-10 text-center text-ink-faint"
12785
13936
  >
12786
- {{ emptyMessage() }}
13937
+ @if (emptyTemplate(); as tpl) {
13938
+ <ng-container
13939
+ [ngTemplateOutlet]="tpl"
13940
+ [ngTemplateOutletContext]="{ $implicit: filterText(), filterText: filterText() }"
13941
+ />
13942
+ } @else {
13943
+ {{ emptyMessage() }}
13944
+ }
12787
13945
  </td>
12788
13946
  </tr>
12789
13947
  }
@@ -12990,10 +14148,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
12990
14148
  <tbody class="divide-y divide-border">
12991
14149
  @for (row of displayRows(); track trackRow($index, row); let i = $index) {
12992
14150
  <tr
12993
- [class]="rowClass(row, i)"
12994
- [attr.tabindex]="selectionMode() === 'single' && !loading() && !editable() ? 0 : null"
14151
+ [class]="bodyRowClass(row, i)"
14152
+ [attr.tabindex]="(selectionMode() === 'single' || rowClickable()) && !loading() && !editable() ? 0 : null"
12995
14153
  [attr.aria-selected]="selectionMode() === 'single' ? (isRowSelected(row) ? 'true' : 'false') : null"
12996
- (click)="onRowClick(row)"
14154
+ (click)="onRowClick(row, $event)"
12997
14155
  (keydown.enter)="onRowKeydown($event, row)"
12998
14156
  (keydown.space)="onRowKeydown($event, row)"
12999
14157
  >
@@ -13218,7 +14376,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
13218
14376
  [attr.colspan]="totalColumnCount()"
13219
14377
  class="py-10 text-center text-ink-faint"
13220
14378
  >
13221
- {{ emptyMessage() }}
14379
+ @if (emptyTemplate(); as tpl) {
14380
+ <ng-container
14381
+ [ngTemplateOutlet]="tpl"
14382
+ [ngTemplateOutletContext]="{ $implicit: filterText(), filterText: filterText() }"
14383
+ />
14384
+ } @else {
14385
+ {{ emptyMessage() }}
14386
+ }
13222
14387
  </td>
13223
14388
  </tr>
13224
14389
  }
@@ -13264,7 +14429,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
13264
14429
  </div>
13265
14430
  `,
13266
14431
  }]
13267
- }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], gridlines: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridlines", required: false }] }], hoverable: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverable", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], paginator: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginator", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], rowsPerPageOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPageOptions", required: false }] }], showCurrentPageReport: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCurrentPageReport", required: false }] }], currentPageReportTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPageReportTemplate", required: false }] }], rowsPerPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPageLabel", required: false }] }], paginationLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginationLabel", required: false }] }], firstPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstPageLabel", required: false }] }], previousPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousPageLabel", required: false }] }], nextPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextPageLabel", required: false }] }], lastPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "lastPageLabel", required: false }] }], pageLinkLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageLinkLabel", required: false }] }], globalFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilter", required: false }] }], globalFilterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilterPlaceholder", required: false }] }], globalFilterAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilterAriaLabel", required: false }] }], globalFilterFields: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilterFields", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }, { type: i0.Output, args: ["selectionChange"] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], caption: [{ type: i0.Input, args: [{ isSignal: true, alias: "caption", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], scrollHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollHeight", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }], totalRecords: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalRecords", required: false }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], resizableColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizableColumns", required: false }] }], reorderableColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderableColumns", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], editOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "editOn", required: false }] }], cellErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellErrors", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], dirtyLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dirtyLabel", required: false }] }], selectPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectPlaceholder", required: false }] }], selectAllLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectAllLabel", required: false }] }], selectRowLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectRowLabel", required: false }] }], selectionColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionColumnLabel", required: false }] }], expandColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandColumnLabel", required: false }] }], expandRowLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandRowLabel", required: false }] }], collapseRowLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseRowLabel", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], rowSelect: [{ type: i0.Output, args: ["rowSelect"] }], rowUnselect: [{ type: i0.Output, args: ["rowUnselect"] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], rowExpand: [{ type: i0.Output, args: ["rowExpand"] }], rowCollapse: [{ type: i0.Output, args: ["rowCollapse"] }], colReorder: [{ type: i0.Output, args: ["colReorder"] }], cellCommit: [{ type: i0.Output, args: ["cellCommit"] }], cellTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SdsTableCell), { isSignal: true }] }], headerTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SdsTableHeader), { isSignal: true }] }], rowExpansion: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsTableRowExpansion), { isSignal: true }] }], numberEditor: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SdsInputNumber), { isSignal: true }] }] } });
14432
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], gridlines: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridlines", required: false }] }], hoverable: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverable", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], paginator: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginator", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], rowsPerPageOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPageOptions", required: false }] }], showCurrentPageReport: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCurrentPageReport", required: false }] }], currentPageReportTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPageReportTemplate", required: false }] }], rowsPerPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPageLabel", required: false }] }], paginationLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginationLabel", required: false }] }], firstPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstPageLabel", required: false }] }], previousPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousPageLabel", required: false }] }], nextPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextPageLabel", required: false }] }], lastPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "lastPageLabel", required: false }] }], pageLinkLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageLinkLabel", required: false }] }], globalFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilter", required: false }] }], globalFilterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilterPlaceholder", required: false }] }], globalFilterAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilterAriaLabel", required: false }] }], globalFilterFields: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilterFields", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }, { type: i0.Output, args: ["selectionChange"] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], rowClickable: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowClickable", required: false }] }], rowClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowClass", required: false }] }], caption: [{ type: i0.Input, args: [{ isSignal: true, alias: "caption", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], scrollHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollHeight", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }], totalRecords: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalRecords", required: false }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], resizableColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizableColumns", required: false }] }], reorderableColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderableColumns", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], editOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "editOn", required: false }] }], cellErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellErrors", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], dirtyLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dirtyLabel", required: false }] }], selectPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectPlaceholder", required: false }] }], selectAllLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectAllLabel", required: false }] }], selectRowLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectRowLabel", required: false }] }], selectionColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionColumnLabel", required: false }] }], expandColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandColumnLabel", required: false }] }], expandRowLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandRowLabel", required: false }] }], collapseRowLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseRowLabel", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], rowSelect: [{ type: i0.Output, args: ["rowSelect"] }], rowUnselect: [{ type: i0.Output, args: ["rowUnselect"] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], rowExpand: [{ type: i0.Output, args: ["rowExpand"] }], rowCollapse: [{ type: i0.Output, args: ["rowCollapse"] }], colReorder: [{ type: i0.Output, args: ["colReorder"] }], cellCommit: [{ type: i0.Output, args: ["cellCommit"] }], cellTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SdsTableCell), { isSignal: true }] }], headerTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SdsTableHeader), { isSignal: true }] }], rowExpansion: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsTableRowExpansion), { isSignal: true }] }], emptyTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsTableEmpty), { isSignal: true }] }], numberEditor: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SdsInputNumber), { isSignal: true }] }] } });
13268
14433
 
13269
14434
  /**
13270
14435
  * A tiny transient-notification bus. Application code calls {@link success} / {@link error} / {@link info}
@@ -14828,7 +15993,7 @@ class SdsUserMenu {
14828
15993
  </div>
14829
15994
  </div>
14830
15995
  }
14831
- `, isInline: true, styles: [".animate-sds-menu{animation:sds-menu-in .14s ease-out}@keyframes sds-menu-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}@media(prefers-reduced-motion:reduce){.animate-sds-menu{animation:none}}\n"], dependencies: [{ kind: "component", type: SdsAvatar, selector: "sds-avatar", inputs: ["name", "initials", "src", "size", "status", "bgClass"] }, { kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
15996
+ `, isInline: true, styles: [".animate-sds-menu{animation:sds-menu-in .14s ease-out}@keyframes sds-menu-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}@media(prefers-reduced-motion:reduce){.animate-sds-menu{animation:none}}\n"], dependencies: [{ kind: "component", type: SdsAvatar, selector: "sds-avatar", inputs: ["name", "initials", "src", "size", "status", "shape", "bgClass", "fallbackIcon", "placeholderClass", "decorative", "lazy"] }, { kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14832
15997
  }
14833
15998
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsUserMenu, decorators: [{
14834
15999
  type: Component,
@@ -15902,5 +17067,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
15902
17067
  * Generated bundle index. Do not edit.
15903
17068
  */
15904
17069
 
15905
- export { SDS_ALLERGENS, SDS_ALLERGEN_ICONS, SDS_ALLERGEN_LABELS_EN, SDS_ICONS, SDS_PHONE_COUNTRIES, SdsAccordion, SdsAccordionContent, SdsAccordionHeader, SdsAccordionPanel, SdsAccordionStore, SdsAllergenIcon, SdsAvatar, SdsBadge, SdsBulkBar, SdsButton, SdsCalendar, SdsCard, SdsCheckbox, SdsChecklistRow, SdsDatePicker, SdsFileDrop, SdsIcon, SdsImageCropper, SdsInput, SdsInputNumber, SdsLanguageSwitcher, SdsLocalizedInput, SdsMenu, SdsMenuItem, SdsMenuTrigger, SdsMultiSelect, SdsNavGroup, SdsNavItem, SdsOtpInput, SdsPaginator, SdsPhoneInput, SdsRadio, SdsScore, SdsSearchCommand, SdsSegmented, SdsSelect, SdsSelectEmpty, SdsSelectFooter, SdsSelectOption, SdsShell, SdsSidebar, SdsSortableHandle, SdsSortableItem, SdsSortableList, SdsStatusTile, SdsTab, SdsTabList, SdsTabPanel, SdsTabPanels, SdsTable, SdsTableCell, SdsTableHeader, SdsTableRowExpansion, SdsTabs, SdsTabsStore, SdsTextarea, SdsThemeService, SdsThemeToggle, SdsToastHost, SdsToastService, SdsToggleSwitch, SdsTopbar, SdsTriStateChip, SdsUserMenu, SidebarStore, addDays, addMonths, addMonthsKeepDay, formatDdMmYyyy, formatHhMm, isOutOfRange, isSameDay, isoDay, pad2, sdsFileMatchesAccept, sdsFormatBytes, sdsFormatNational, sdsMatchDialCode, sdsNormalizeSignificant, startOfDay, startOfMonth };
17070
+ export { SDS_ALLERGENS, SDS_ALLERGEN_ICONS, SDS_ALLERGEN_LABELS_EN, SDS_ICONS, SDS_PHONE_COUNTRIES, SdsAccordion, SdsAccordionContent, SdsAccordionHeader, SdsAccordionPanel, SdsAccordionStore, SdsAllergenIcon, SdsAvatar, SdsBadge, SdsBulkBar, SdsButton, SdsCalendar, SdsCard, SdsCheckbox, SdsChecklistRow, SdsChip, SdsChipGroup, SdsDatePicker, SdsFileDrop, SdsIcon, SdsImageCropper, SdsInput, SdsInputNumber, SdsLanguageSwitcher, SdsLocalizedInput, SdsMenu, SdsMenuItem, SdsMenuTrigger, SdsMultiSelect, SdsNavGroup, SdsNavItem, SdsOtpInput, SdsPaginator, SdsPhoneInput, SdsRadio, SdsScore, SdsSearchCommand, SdsSegmented, SdsSelect, SdsSelectEmpty, SdsSelectFooter, SdsSelectOption, SdsShell, SdsSidebar, SdsSortableHandle, SdsSortableItem, SdsSortableList, SdsStatusTile, SdsTab, SdsTabList, SdsTabPanel, SdsTabPanels, SdsTable, SdsTableCell, SdsTableEmpty, SdsTableHeader, SdsTableRowExpansion, SdsTabs, SdsTabsStore, SdsTextarea, SdsThemeService, SdsThemeToggle, SdsTimeInput, SdsToastHost, SdsToastService, SdsToggleSwitch, SdsTopbar, SdsTriStateChip, SdsUserMenu, SidebarStore, addDays, addMonths, addMonthsKeepDay, formatDdMmYyyy, formatHhMm, isOutOfRange, isSameDay, isoDay, pad2, sdsFileMatchesAccept, sdsFormatBytes, sdsFormatNational, sdsMatchDialCode, sdsNormalizeSignificant, startOfDay, startOfMonth };
15906
17071
  //# sourceMappingURL=besaitech-ng-design-system.mjs.map