@besaitech/ng-design-system 0.0.66 → 0.0.67

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.
@@ -310,7 +310,7 @@ const VARIANTS$2 = {
310
310
  'dark:hover:bg-white/10 dark:active:bg-white/15',
311
311
  danger: 'bg-danger text-white shadow-sm hover:brightness-95 active:brightness-90',
312
312
  };
313
- const SIZES$d = {
313
+ const SIZES$e = {
314
314
  sm: 'h-8 px-3 text-sm',
315
315
  md: 'h-10 px-4 text-sm',
316
316
  lg: 'h-12 px-5 text-base',
@@ -370,7 +370,7 @@ class SdsButton {
370
370
  BASE,
371
371
  this.shape() === 'pill' ? 'rounded-full' : 'rounded-control',
372
372
  VARIANTS$2[this.variant()],
373
- SIZES$d[size],
373
+ SIZES$e[size],
374
374
  this.iconOnly() ? ICON_ONLY[size] : '',
375
375
  this.block() ? 'w-full' : '',
376
376
  ]
@@ -422,8 +422,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
422
422
  `, 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"] }]
423
423
  }], 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 }] }] } });
424
424
 
425
- let nextId$o = 0;
426
- const SIZES$c = {
425
+ let nextId$p = 0;
426
+ const SIZES$d = {
427
427
  sm: 'h-9 text-sm',
428
428
  md: 'h-10 text-sm',
429
429
  lg: 'h-12 text-base',
@@ -435,7 +435,7 @@ const SIZES$c = {
435
435
  * sidebar search field).
436
436
  */
437
437
  class SdsInput {
438
- autoId = `sds-input-${nextId$o++}`;
438
+ autoId = `sds-input-${nextId$p++}`;
439
439
  /**
440
440
  * `id` of the native `<input>` — for a `<label for>` outside the component, or to focus the field with
441
441
  * `document.getElementById(id).focus()` (after adding a row, say). Empty (default) generates one. Set it
@@ -539,7 +539,7 @@ class SdsInput {
539
539
  }, ...(ngDevMode ? [{ debugName: "fieldPadding" }] : []));
540
540
  wrapperClasses = computed(() => {
541
541
  const danger = !!this.error() || this.invalid();
542
- const parts = [SIZES$c[this.size()], this.pill() ? 'rounded-full' : 'rounded-control'];
542
+ const parts = [SIZES$d[this.size()], this.pill() ? 'rounded-full' : 'rounded-control'];
543
543
  if (this.isDisabled())
544
544
  parts.push('opacity-60 cursor-not-allowed');
545
545
  if (this.filled()) {
@@ -795,7 +795,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
795
795
  `, 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"] }]
796
796
  }], propDecorators: { inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], 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 }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", 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"] }] } });
797
797
 
798
- let nextId$n = 0;
798
+ let nextId$o = 0;
799
799
  const HEIGHTS = {
800
800
  sm: 'h-9',
801
801
  md: 'h-10',
@@ -823,7 +823,7 @@ const TEXT = {
823
823
  * with "." ("1.234" in nl-BE) stays a thousands separator.
824
824
  */
825
825
  class SdsInputNumber {
826
- inputId = `sds-input-number-${nextId$n++}`;
826
+ inputId = `sds-input-number-${nextId$o++}`;
827
827
  fieldRef = viewChild('field', ...(ngDevMode ? [{ debugName: "fieldRef" }] : []));
828
828
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
829
829
  /** Accessible name used when no visible [label] is provided. */
@@ -1607,8 +1607,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
1607
1607
  }]
1608
1608
  }], 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 }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", 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 }] }] } });
1609
1609
 
1610
- let nextId$m = 0;
1611
- const SIZES$b = {
1610
+ let nextId$n = 0;
1611
+ const SIZES$c = {
1612
1612
  sm: 'h-9 text-sm',
1613
1613
  md: 'h-10 text-sm',
1614
1614
  lg: 'h-12 text-base',
@@ -1648,7 +1648,7 @@ function parseTime(value) {
1648
1648
  * <sds-time-input [ariaLabel]="'Closes at'" [ngModel]="end()" (ngModelChange)="end.set($event)" clearable />
1649
1649
  */
1650
1650
  class SdsTimeInput {
1651
- fieldId = `sds-time-input-${nextId$m++}`;
1651
+ fieldId = `sds-time-input-${nextId$n++}`;
1652
1652
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
1653
1653
  /** Accessible name of the field when there is no visible [label]. */
1654
1654
  ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
@@ -1695,7 +1695,7 @@ class SdsTimeInput {
1695
1695
  return null;
1696
1696
  }, ...(ngDevMode ? [{ debugName: "describedBy" }] : []));
1697
1697
  wrapperClasses = computed(() => {
1698
- const parts = [SIZES$b[this.size()], 'rounded-control'];
1698
+ const parts = [SIZES$c[this.size()], 'rounded-control'];
1699
1699
  if (this.isDisabled())
1700
1700
  parts.push('opacity-60 cursor-not-allowed');
1701
1701
  if (this.appearance() === 'filled') {
@@ -2177,8 +2177,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
2177
2177
  }]
2178
2178
  }], 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 }] }] } });
2179
2179
 
2180
- let nextId$l = 0;
2181
- const SIZES$a = {
2180
+ let nextId$m = 0;
2181
+ const SIZES$b = {
2182
2182
  sm: 'min-h-9 text-sm',
2183
2183
  md: 'min-h-10 text-sm',
2184
2184
  lg: 'min-h-12 text-base',
@@ -2200,7 +2200,7 @@ const SIZES$a = {
2200
2200
  * `<sds-localized-input [languages]="['fr','nl','en']" [(ngModel)]="title" />`.
2201
2201
  */
2202
2202
  class SdsLocalizedInput {
2203
- autoId = `sds-localized-input-${nextId$l++}`;
2203
+ autoId = `sds-localized-input-${nextId$m++}`;
2204
2204
  /** The native field's id; a generated one when empty (set it to pair an external `<label for>`). */
2205
2205
  inputId = input('', ...(ngDevMode ? [{ debugName: "inputId" }] : []));
2206
2206
  /** The native field's id: {@link inputId} or the generated one. */
@@ -2251,7 +2251,7 @@ class SdsLocalizedInput {
2251
2251
  cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : []));
2252
2252
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
2253
2253
  wrapperClasses = computed(() => {
2254
- const parts = [SIZES$a[this.size()]];
2254
+ const parts = [SIZES$b[this.size()]];
2255
2255
  const danger = !!this.error() || this.invalid();
2256
2256
  parts.push(this.isDisabled()
2257
2257
  ? 'border-border opacity-60 cursor-not-allowed'
@@ -2560,8 +2560,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
2560
2560
  }]
2561
2561
  }], propDecorators: { inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", 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 }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], requiredLanguages: [{ type: i0.Input, args: [{ isSignal: true, alias: "requiredLanguages", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], showCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCount", 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 }] }], requiredLanguageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "requiredLanguageLabel", required: false }] }] } });
2562
2562
 
2563
- let nextId$k = 0;
2564
- const SIZES$9 = {
2563
+ let nextId$l = 0;
2564
+ const SIZES$a = {
2565
2565
  md: 'h-12 w-11 text-lg',
2566
2566
  lg: 'h-14 w-12 text-xl',
2567
2567
  };
@@ -2577,7 +2577,7 @@ const SIZES$9 = {
2577
2577
  * first box and have it land correctly.
2578
2578
  */
2579
2579
  class SdsOtpInput {
2580
- groupId = `sds-otp-${nextId$k++}`;
2580
+ groupId = `sds-otp-${nextId$l++}`;
2581
2581
  /** How many boxes / characters the code has. */
2582
2582
  length = input(6, ...(ngDevMode ? [{ debugName: "length", transform: numberAttribute }] : [{ transform: numberAttribute }]));
2583
2583
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
@@ -2613,7 +2613,7 @@ class SdsOtpInput {
2613
2613
  return null;
2614
2614
  }, ...(ngDevMode ? [{ debugName: "describedBy" }] : []));
2615
2615
  cellClasses = computed(() => {
2616
- const parts = ['rounded-control border', SIZES$9[this.size()]];
2616
+ const parts = ['rounded-control border', SIZES$a[this.size()]];
2617
2617
  parts.push(this.error()
2618
2618
  ? 'border-danger focus:border-danger focus:ring-2 focus:ring-danger/25'
2619
2619
  : 'border-border text-ink focus:border-brand-500 focus:ring-2 focus:ring-brand-500/25');
@@ -2877,8 +2877,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
2877
2877
  }]
2878
2878
  }], 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 }] }] } });
2879
2879
 
2880
- let nextId$j = 0;
2881
- const SIZES$8 = {
2880
+ let nextId$k = 0;
2881
+ const SIZES$9 = {
2882
2882
  sm: 'text-sm px-3 py-2',
2883
2883
  md: 'text-sm px-3.5 py-2.5',
2884
2884
  lg: 'text-base px-4 py-3',
@@ -2889,7 +2889,7 @@ const SIZES$8 = {
2889
2889
  * [(ngModel)].
2890
2890
  */
2891
2891
  class SdsTextarea {
2892
- fieldId = `sds-textarea-${nextId$j++}`;
2892
+ fieldId = `sds-textarea-${nextId$k++}`;
2893
2893
  field = viewChild('field', ...(ngDevMode ? [{ debugName: "field" }] : []));
2894
2894
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
2895
2895
  /** Accessible name used when no visible [label] is provided. */
@@ -2946,7 +2946,7 @@ class SdsTextarea {
2946
2946
  fieldClasses = computed(() => {
2947
2947
  const parts = [
2948
2948
  'block w-full rounded-control border text-ink outline-none transition-colors placeholder:text-ink-faint',
2949
- SIZES$8[this.size()],
2949
+ SIZES$9[this.size()],
2950
2950
  ];
2951
2951
  if (this.autoResize())
2952
2952
  parts.push('overflow-hidden');
@@ -3156,8 +3156,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
3156
3156
  }]
3157
3157
  }], 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 }] }] } });
3158
3158
 
3159
- let nextId$i = 0;
3160
- const SIZES$7 = {
3159
+ let nextId$j = 0;
3160
+ const SIZES$8 = {
3161
3161
  sm: { track: 'h-5 w-9', knob: 'h-4 w-4', on: 'translate-x-4', icon: 11 },
3162
3162
  md: { track: 'h-6 w-11', knob: 'h-5 w-5', on: 'translate-x-5', icon: 13 },
3163
3163
  lg: { track: 'h-7 w-[3.4rem]', knob: 'h-6 w-6', on: 'translate-x-7', icon: 15 },
@@ -3169,7 +3169,7 @@ const SIZES$7 = {
3169
3169
  * states.
3170
3170
  */
3171
3171
  class SdsToggleSwitch {
3172
- fieldId = `sds-toggle-switch-${nextId$i++}`;
3172
+ fieldId = `sds-toggle-switch-${nextId$j++}`;
3173
3173
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
3174
3174
  /** Accessible name used when no visible [label] is provided. */
3175
3175
  ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
@@ -3197,7 +3197,7 @@ class SdsToggleSwitch {
3197
3197
  cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : []));
3198
3198
  checked = computed(() => this.value() === this.trueValue(), ...(ngDevMode ? [{ debugName: "checked" }] : []));
3199
3199
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
3200
- sizing = computed(() => SIZES$7[this.size()], ...(ngDevMode ? [{ debugName: "sizing" }] : []));
3200
+ sizing = computed(() => SIZES$8[this.size()], ...(ngDevMode ? [{ debugName: "sizing" }] : []));
3201
3201
  describedBy = computed(() => {
3202
3202
  if (this.error())
3203
3203
  return `${this.fieldId}-err`;
@@ -3416,7 +3416,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
3416
3416
  }]
3417
3417
  }], 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 }] }] } });
3418
3418
 
3419
- let nextId$h = 0;
3419
+ let nextId$i = 0;
3420
3420
  /**
3421
3421
  * Boolean checkbox. Implements ControlValueAccessor — use with reactive forms
3422
3422
  * or `[(ngModel)]`. Renders a styled box (brand-filled with a check when on)
@@ -3431,7 +3431,7 @@ let nextId$h = 0;
3431
3431
  * <sds-checkbox label="Auto-merge" [(ngModel)]="autoMerge" />
3432
3432
  */
3433
3433
  class SdsCheckbox {
3434
- id = `sds-checkbox-${nextId$h++}`;
3434
+ id = `sds-checkbox-${nextId$i++}`;
3435
3435
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
3436
3436
  /** Accessible name used when no visible [label] is provided. */
3437
3437
  ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
@@ -3579,7 +3579,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
3579
3579
  }]
3580
3580
  }], 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 }] }] } });
3581
3581
 
3582
- let nextId$g = 0;
3582
+ let nextId$h = 0;
3583
3583
  /**
3584
3584
  * Mounted radios by group name. A pick updates the rest of its group, because a native radio says
3585
3585
  * nothing when the browser unchecks it — without this a reactive form would leave the old choice drawn.
@@ -3605,7 +3605,7 @@ const groups = new Map();
3605
3605
  * <sds-radio name="size" value="large" label="Grand" [(ngModel)]="size" />
3606
3606
  */
3607
3607
  class SdsRadio {
3608
- id = `sds-radio-${nextId$g++}`;
3608
+ id = `sds-radio-${nextId$h++}`;
3609
3609
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
3610
3610
  /** Accessible name used when no visible [label] is provided. */
3611
3611
  ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
@@ -4181,7 +4181,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
4181
4181
 
4182
4182
  /** Activation order: none → contains → may-contain → none. */
4183
4183
  const ORDER = ['none', 'contains', 'may-contain'];
4184
- let nextId$f = 0;
4184
+ let nextId$g = 0;
4185
4185
  /**
4186
4186
  * A chip that cycles through three states — `none`, `contains`, `may-contain` — built for the 14-tile
4187
4187
  * allergen declaration grid, but generic (any "no / yes / maybe" declaration).
@@ -4201,7 +4201,7 @@ let nextId$f = 0;
4201
4201
  * containsLabel="Contains" mayContainLabel="May contain" noneLabel="Not present" />
4202
4202
  */
4203
4203
  class SdsTriStateChip {
4204
- chipId = `sds-tri-state-chip-${nextId$f++}`;
4204
+ chipId = `sds-tri-state-chip-${nextId$g++}`;
4205
4205
  /** Current state. Two-way: `[(value)]`. */
4206
4206
  value = model('none', ...(ngDevMode ? [{ debugName: "value" }] : []));
4207
4207
  /** Visible name (e.g. the allergen). Required — the chip has no other accessible name. */
@@ -4426,7 +4426,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
4426
4426
  }]
4427
4427
  }], 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 }] }] } });
4428
4428
 
4429
- let nextId$e = 0;
4429
+ let nextId$f = 0;
4430
4430
  /**
4431
4431
  * Segmented control: a small set of mutually exclusive options shown side by side (e.g. "Pick exactly /
4432
4432
  * at least / up to"). Radio-group semantics: `role="radiogroup"` with `role="radio"` segments, one tab
@@ -4442,7 +4442,7 @@ let nextId$e = 0;
4442
4442
  */
4443
4443
  class SdsSegmented {
4444
4444
  host = inject(ElementRef);
4445
- baseId = `sds-segmented-${nextId$e++}`;
4445
+ baseId = `sds-segmented-${nextId$f++}`;
4446
4446
  /** Options: `{ value, label, icon?, disabled? }` objects, or bare strings/numbers. */
4447
4447
  options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
4448
4448
  /** Selected value. Two-way: `[(value)]`. */
@@ -4720,7 +4720,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
4720
4720
  `,
4721
4721
  }]
4722
4722
  }], 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 }] }] } });
4723
- let nextId$d = 0;
4723
+ let nextId$e = 0;
4724
4724
  /**
4725
4725
  * A set of chips: pick ONE (a filter row such as "All · Missing photo · Hidden", VAT presets, a date
4726
4726
  * range) or, with `multiple`, SEVERAL (categories of an item, days of the week, dietary tags). Unlike
@@ -4743,7 +4743,7 @@ let nextId$d = 0;
4743
4743
  */
4744
4744
  class SdsChipGroup {
4745
4745
  host = inject(ElementRef);
4746
- baseId = `sds-chip-group-${nextId$d++}`;
4746
+ baseId = `sds-chip-group-${nextId$e++}`;
4747
4747
  /** Options: `{ value, label, icon?, count?, disabled? }` objects, or bare strings/numbers. */
4748
4748
  options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
4749
4749
  /** Picked value (single) or values (multiple). Two-way: `[(value)]`. */
@@ -5645,7 +5645,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
5645
5645
  }]
5646
5646
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], image: [{ type: i0.Input, args: [{ isSignal: true, alias: "image", required: false }] }], imageAlt: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageAlt", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], note: [{ type: i0.Input, args: [{ isSignal: true, alias: "note", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], availableLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "availableLabel", required: false }] }], soldOutLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "soldOutLabel", required: false }] }], hiddenLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "hiddenLabel", required: false }] }], activate: [{ type: i0.Output, args: ["activate"] }], imageError: [{ type: i0.Output, args: ["imageError"] }] } });
5647
5647
 
5648
- let nextId$c = 0;
5648
+ let nextId$d = 0;
5649
5649
  /**
5650
5650
  * Marks the element inside `<sds-menu>` that opens it. It may be a native button or a component that
5651
5651
  * renders one (e.g. `<sds-button sdsMenuTrigger>`): the menu wires `aria-haspopup`, `aria-expanded` and
@@ -5693,7 +5693,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
5693
5693
  class SdsMenu {
5694
5694
  host = inject(ElementRef);
5695
5695
  injector = inject(Injector);
5696
- menuId = `sds-menu-${nextId$c++}`;
5696
+ menuId = `sds-menu-${nextId$d++}`;
5697
5697
  triggerId = `${this.menuId}-trigger`;
5698
5698
  /** Actions and separators, in order. */
5699
5699
  items = input([], ...(ngDevMode ? [{ debugName: "items" }] : []));
@@ -6289,7 +6289,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
6289
6289
  standalone: true,
6290
6290
  }]
6291
6291
  }] });
6292
- let nextId$b = 0;
6292
+ let nextId$c = 0;
6293
6293
  /** Fastest auto-scroll step, in px per animation frame (reached at the very edge). */
6294
6294
  const AUTO_SCROLL_MAX_STEP = 18;
6295
6295
  /** The nearest ancestor that scrolls vertically (below the document, which is handled as the window). */
@@ -6358,7 +6358,7 @@ function moveItem(list, from, to) {
6358
6358
  class SdsSortableList {
6359
6359
  host = inject(ElementRef);
6360
6360
  injector = inject(Injector);
6361
- instructionsId = `sds-sortable-${nextId$b++}-help`;
6361
+ instructionsId = `sds-sortable-${nextId$c++}-help`;
6362
6362
  /** The items, in order. Two-way: `[(items)]` receives the new order on drop. */
6363
6363
  items = model([], ...(ngDevMode ? [{ debugName: "items" }] : []));
6364
6364
  /** Property used as a stable identity for rows (strongly recommended). */
@@ -6950,7 +6950,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
6950
6950
  }]
6951
6951
  }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }, { type: i0.Output, args: ["itemsChange"] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: false }] }], labelField: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelField", required: false }] }], itemLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemLabel", required: false }] }], itemDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemDisabled", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], autoScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoScroll", required: false }] }], autoScrollEdge: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoScrollEdge", required: false }] }], handleLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "handleLabel", required: false }] }], instructions: [{ type: i0.Input, args: [{ isSignal: true, alias: "instructions", required: false }] }], pickedUpMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "pickedUpMessage", required: false }] }], movedMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "movedMessage", required: false }] }], droppedMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "droppedMessage", required: false }] }], cancelledMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelledMessage", required: false }] }], reorder: [{ type: i0.Output, args: ["reorder"] }], itemTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsSortableItem), { isSignal: true }] }], handleTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsSortableHandle), { isSignal: true }] }] } });
6952
6952
 
6953
- let nextId$a = 0;
6953
+ let nextId$b = 0;
6954
6954
  /** "1.5 MB" style size (decimal units, like file managers show them), for messages. */
6955
6955
  function sdsFormatBytes(bytes) {
6956
6956
  if (!Number.isFinite(bytes) || bytes < 0)
@@ -7007,7 +7007,7 @@ function sdsFileMatchesAccept(file, accept) {
7007
7007
  * (files)="upload($event)" (rejected)="onRejected($event)" />
7008
7008
  */
7009
7009
  class SdsFileDrop {
7010
- zoneId = `sds-file-drop-${nextId$a++}`;
7010
+ zoneId = `sds-file-drop-${nextId$b++}`;
7011
7011
  fileInput = viewChild('fileInput', ...(ngDevMode ? [{ debugName: "fileInput" }] : []));
7012
7012
  /** Accepted types, native `accept` syntax. Empty = anything. */
7013
7013
  accept = input('', ...(ngDevMode ? [{ debugName: "accept" }] : []));
@@ -7389,7 +7389,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
7389
7389
  }]
7390
7390
  }], propDecorators: { fileInput: [{ type: i0.ViewChild, args: ['fileInput', { isSignal: true }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], maxSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSize", required: false }] }], maxFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFiles", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], dropLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dropLabel", required: false }] }], browseLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "browseLabel", required: false }] }], loadingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingLabel", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], wrongTypeMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrongTypeMessage", required: false }] }], tooLargeMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooLargeMessage", required: false }] }], tooManyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooManyMessage", required: false }] }], files: [{ type: i0.Output, args: ["files"] }], rejected: [{ type: i0.Output, args: ["rejected"] }] } });
7391
7391
 
7392
- let nextId$9 = 0;
7392
+ let nextId$a = 0;
7393
7393
  /**
7394
7394
  * Crops an image to a FIXED aspect ratio (e.g. the shop's card ratio): the frame is fixed and the image
7395
7395
  * moves and zooms behind it, always covering the frame, so every crop is valid.
@@ -7407,7 +7407,7 @@ let nextId$9 = 0;
7407
7407
  * <sds-button (click)="upload(await cropper.toBlob({ width: 1600 }))">Save</sds-button>
7408
7408
  */
7409
7409
  class SdsImageCropper {
7410
- helpId = `sds-image-cropper-${nextId$9++}-help`;
7410
+ helpId = `sds-image-cropper-${nextId$a++}-help`;
7411
7411
  viewport = viewChild('viewport', ...(ngDevMode ? [{ debugName: "viewport" }] : []));
7412
7412
  /** Image to crop: a URL, or a Blob/File. */
7413
7413
  src = input(null, ...(ngDevMode ? [{ debugName: "src" }] : []));
@@ -7956,6 +7956,28 @@ function formatDdMmYyyy(d) {
7956
7956
  const p = (n) => String(n).padStart(2, '0');
7957
7957
  return `${p(d.getDate())}/${p(d.getMonth() + 1)}/${d.getFullYear()}`;
7958
7958
  }
7959
+ /** Whole calendar days from `a` to `b` (b − a), ignoring time-of-day and DST shifts. */
7960
+ function diffInDays(a, b) {
7961
+ const ua = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
7962
+ const ub = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
7963
+ return Math.round((ub - ua) / 86_400_000);
7964
+ }
7965
+ /**
7966
+ * Format an inclusive day range for display, e.g. `1–7 Oct 2026`, `28 Sept – 4 Oct 2026` or
7967
+ * `20 Dec 2026 – 3 Jan 2027` (en-GB). Uses `Intl.DateTimeFormat#formatRange`, which collapses the
7968
+ * parts both ends share; falls back to `start – end` where it is unavailable. A one-day range prints
7969
+ * a single date.
7970
+ */
7971
+ function formatDateRange(range, locale = 'en-GB', options = { day: 'numeric', month: 'short', year: 'numeric' }) {
7972
+ const fmt = new Intl.DateTimeFormat(locale, options);
7973
+ if (isSameDay(range.start, range.end))
7974
+ return fmt.format(range.start);
7975
+ const withRange = fmt;
7976
+ if (typeof withRange.formatRange === 'function') {
7977
+ return withRange.formatRange(range.start, range.end);
7978
+ }
7979
+ return `${fmt.format(range.start)} – ${fmt.format(range.end)}`;
7980
+ }
7959
7981
 
7960
7982
  /**
7961
7983
  * Inline month calendar with day → month → year drill-down navigation,
@@ -8755,7 +8777,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
8755
8777
  }]
8756
8778
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], firstDayOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstDayOfWeek", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], showActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActions", required: false }] }], showTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTime", required: false }] }], showDone: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDone", required: false }] }], timeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeLabel", required: false }] }], hoursLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoursLabel", required: false }] }], minutesLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "minutesLabel", required: false }] }], doneLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "doneLabel", required: false }] }], calendarLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "calendarLabel", required: false }] }], previousLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], todayActionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "todayActionLabel", required: false }] }], clearActionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearActionLabel", required: false }] }], dateSelect: [{ type: i0.Output, args: ["dateSelect"] }], done: [{ type: i0.Output, args: ["done"] }] } });
8757
8779
 
8758
- const SIZES$6 = {
8780
+ const SIZES$7 = {
8759
8781
  sm: 'h-9 text-sm',
8760
8782
  md: 'h-10 text-sm',
8761
8783
  lg: 'h-12 text-base',
@@ -8771,7 +8793,7 @@ const SIZES$6 = {
8771
8793
  class SdsDatePicker {
8772
8794
  host = inject(ElementRef);
8773
8795
  injector = inject(Injector);
8774
- fieldId = `sds-date-${nextId$8++}`;
8796
+ fieldId = `sds-date-${nextId$9++}`;
8775
8797
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
8776
8798
  ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
8777
8799
  placeholder = input('dd/mm/yyyy', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
@@ -8847,7 +8869,7 @@ class SdsDatePicker {
8847
8869
  /** Whether the clear button is shown (affects the field's right padding). */
8848
8870
  showClear = computed(() => this.clearable() && !!this.value() && !this.isDisabled(), ...(ngDevMode ? [{ debugName: "showClear" }] : []));
8849
8871
  wrapperClasses = computed(() => {
8850
- const parts = [SIZES$6[this.size()]];
8872
+ const parts = [SIZES$7[this.size()]];
8851
8873
  parts.push(this.error()
8852
8874
  ? 'border-danger focus-within:border-danger focus-within:ring-2 focus-within:ring-danger/25'
8853
8875
  : 'border-border focus-within:border-brand-500 focus-within:ring-2 focus-within:ring-brand-500/25');
@@ -9084,84 +9106,1201 @@ class SdsDatePicker {
9084
9106
  [class.pr-11]="!showClear()"
9085
9107
  />
9086
9108
 
9087
- @if (showClear()) {
9088
- <button
9089
- type="button"
9090
- tabindex="-1"
9091
- [attr.aria-label]="clearLabel()"
9092
- class="absolute right-10 inline-flex h-6 w-6 items-center justify-center rounded-full text-ink-faint hover:bg-black/5 hover:text-ink-soft"
9093
- (click)="clear($event)"
9109
+ @if (showClear()) {
9110
+ <button
9111
+ type="button"
9112
+ tabindex="-1"
9113
+ [attr.aria-label]="clearLabel()"
9114
+ class="absolute right-10 inline-flex h-6 w-6 items-center justify-center rounded-full text-ink-faint hover:bg-black/5 hover:text-ink-soft"
9115
+ (click)="clear($event)"
9116
+ >
9117
+ <sds-icon name="x" [size]="15" />
9118
+ </button>
9119
+ }
9120
+
9121
+ <button
9122
+ type="button"
9123
+ tabindex="-1"
9124
+ [attr.aria-label]="open() ? closeCalendarLabel() : openCalendarLabel()"
9125
+ class="absolute right-2 inline-flex h-7 w-7 items-center justify-center rounded-control text-ink-soft hover:bg-muted"
9126
+ (click)="toggle($event)"
9127
+ >
9128
+ <sds-icon name="calendar" [size]="18" />
9129
+ </button>
9130
+ </div>
9131
+
9132
+ @if (open()) {
9133
+ <!-- Relocated to document.body on open (appendTo body) to escape
9134
+ ancestor overflow/stacking; positioned via positionPanel(). -->
9135
+ <div
9136
+ class="fixed z-[1000]"
9137
+ role="dialog"
9138
+ aria-modal="false"
9139
+ [attr.aria-label]="panelLabel()"
9140
+ (keydown.escape)="onEscape($event)"
9141
+ (keydown)="onPanelKeydown($event)"
9142
+ >
9143
+ <sds-calendar
9144
+ [value]="value()"
9145
+ [min]="min()"
9146
+ [max]="max()"
9147
+ [firstDayOfWeek]="firstDayOfWeek()"
9148
+ [locale]="locale()"
9149
+ [showActions]="showActions()"
9150
+ [showTime]="showTime()"
9151
+ [showDone]="showTime()"
9152
+ [timeLabel]="timeLabel()"
9153
+ [hoursLabel]="hoursLabel()"
9154
+ [minutesLabel]="minutesLabel()"
9155
+ [doneLabel]="doneLabel()"
9156
+ [calendarLabel]="calendarLabel()"
9157
+ [previousLabel]="previousLabel()"
9158
+ [nextLabel]="nextLabel()"
9159
+ [todayActionLabel]="todayActionLabel()"
9160
+ [clearActionLabel]="clearActionLabel()"
9161
+ (valueChange)="onCalendarValue($event)"
9162
+ (dateSelect)="onDateSelect()"
9163
+ (done)="close(true)"
9164
+ />
9165
+ </div>
9166
+ }
9167
+ </div>
9168
+ }
9169
+
9170
+ @if (hint() && !error()) {
9171
+ <p class="mt-1.5 text-xs text-ink-faint">{{ hint() }}</p>
9172
+ }
9173
+ @if (error()) {
9174
+ <p class="mt-1.5 text-xs font-medium text-danger">{{ error() }}</p>
9175
+ }
9176
+ `, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth", "filled"] }, { kind: "component", type: SdsCalendar, selector: "sds-calendar", inputs: ["value", "min", "max", "firstDayOfWeek", "locale", "showActions", "showTime", "showDone", "timeLabel", "hoursLabel", "minutesLabel", "doneLabel", "calendarLabel", "previousLabel", "nextLabel", "todayActionLabel", "clearActionLabel"], outputs: ["valueChange", "dateSelect", "done"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9177
+ }
9178
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsDatePicker, decorators: [{
9179
+ type: Component,
9180
+ args: [{
9181
+ selector: 'sds-date-picker',
9182
+ changeDetection: ChangeDetectionStrategy.OnPush,
9183
+ imports: [SdsIcon, SdsCalendar],
9184
+ providers: [
9185
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SdsDatePicker), multi: true },
9186
+ ],
9187
+ host: {
9188
+ class: 'block',
9189
+ '(document:click)': 'onDocumentClick($event)',
9190
+ '(keydown.escape)': 'onEscape($event)',
9191
+ },
9192
+ template: `
9193
+ @if (label()) {
9194
+ <label [for]="fieldId" class="mb-1.5 block text-sm font-medium text-ink">
9195
+ {{ label() }}
9196
+ </label>
9197
+ }
9198
+
9199
+ @if (inline()) {
9200
+ <sds-calendar
9201
+ [value]="value()"
9202
+ [min]="min()"
9203
+ [max]="max()"
9204
+ [firstDayOfWeek]="firstDayOfWeek()"
9205
+ [locale]="locale()"
9206
+ [showActions]="showActions()"
9207
+ [showTime]="showTime()"
9208
+ [timeLabel]="timeLabel()"
9209
+ [hoursLabel]="hoursLabel()"
9210
+ [minutesLabel]="minutesLabel()"
9211
+ [calendarLabel]="calendarLabel()"
9212
+ [previousLabel]="previousLabel()"
9213
+ [nextLabel]="nextLabel()"
9214
+ [todayActionLabel]="todayActionLabel()"
9215
+ [clearActionLabel]="clearActionLabel()"
9216
+ (valueChange)="onCalendarValue($event)"
9217
+ />
9218
+ } @else {
9219
+ <div class="relative">
9220
+ <div
9221
+ #trigger
9222
+ class="relative flex items-center rounded-control border bg-surface transition-colors"
9223
+ [class]="wrapperClasses()"
9224
+ (click)="toggle()"
9225
+ >
9226
+ <input
9227
+ [id]="fieldId"
9228
+ type="text"
9229
+ readonly
9230
+ [value]="displayValue()"
9231
+ [placeholder]="placeholder()"
9232
+ [disabled]="isDisabled()"
9233
+ [attr.aria-haspopup]="'dialog'"
9234
+ [attr.aria-expanded]="open()"
9235
+ [attr.aria-label]="!label() && ariaLabel() ? ariaLabel() : null"
9236
+ (keydown.enter)="onTriggerKeydown($event)"
9237
+ (keydown.space)="onTriggerKeydown($event)"
9238
+ (keydown.arrowdown)="onTriggerKeydown($event)"
9239
+ class="w-full cursor-pointer bg-transparent pl-3.5 text-ink outline-none placeholder:text-ink-faint disabled:cursor-not-allowed"
9240
+ [class.pr-20]="showClear()"
9241
+ [class.pr-11]="!showClear()"
9242
+ />
9243
+
9244
+ @if (showClear()) {
9245
+ <button
9246
+ type="button"
9247
+ tabindex="-1"
9248
+ [attr.aria-label]="clearLabel()"
9249
+ class="absolute right-10 inline-flex h-6 w-6 items-center justify-center rounded-full text-ink-faint hover:bg-black/5 hover:text-ink-soft"
9250
+ (click)="clear($event)"
9251
+ >
9252
+ <sds-icon name="x" [size]="15" />
9253
+ </button>
9254
+ }
9255
+
9256
+ <button
9257
+ type="button"
9258
+ tabindex="-1"
9259
+ [attr.aria-label]="open() ? closeCalendarLabel() : openCalendarLabel()"
9260
+ class="absolute right-2 inline-flex h-7 w-7 items-center justify-center rounded-control text-ink-soft hover:bg-muted"
9261
+ (click)="toggle($event)"
9262
+ >
9263
+ <sds-icon name="calendar" [size]="18" />
9264
+ </button>
9265
+ </div>
9266
+
9267
+ @if (open()) {
9268
+ <!-- Relocated to document.body on open (appendTo body) to escape
9269
+ ancestor overflow/stacking; positioned via positionPanel(). -->
9270
+ <div
9271
+ class="fixed z-[1000]"
9272
+ role="dialog"
9273
+ aria-modal="false"
9274
+ [attr.aria-label]="panelLabel()"
9275
+ (keydown.escape)="onEscape($event)"
9276
+ (keydown)="onPanelKeydown($event)"
9277
+ >
9278
+ <sds-calendar
9279
+ [value]="value()"
9280
+ [min]="min()"
9281
+ [max]="max()"
9282
+ [firstDayOfWeek]="firstDayOfWeek()"
9283
+ [locale]="locale()"
9284
+ [showActions]="showActions()"
9285
+ [showTime]="showTime()"
9286
+ [showDone]="showTime()"
9287
+ [timeLabel]="timeLabel()"
9288
+ [hoursLabel]="hoursLabel()"
9289
+ [minutesLabel]="minutesLabel()"
9290
+ [doneLabel]="doneLabel()"
9291
+ [calendarLabel]="calendarLabel()"
9292
+ [previousLabel]="previousLabel()"
9293
+ [nextLabel]="nextLabel()"
9294
+ [todayActionLabel]="todayActionLabel()"
9295
+ [clearActionLabel]="clearActionLabel()"
9296
+ (valueChange)="onCalendarValue($event)"
9297
+ (dateSelect)="onDateSelect()"
9298
+ (done)="close(true)"
9299
+ />
9300
+ </div>
9301
+ }
9302
+ </div>
9303
+ }
9304
+
9305
+ @if (hint() && !error()) {
9306
+ <p class="mt-1.5 text-xs text-ink-faint">{{ hint() }}</p>
9307
+ }
9308
+ @if (error()) {
9309
+ <p class="mt-1.5 text-xs font-medium text-danger">{{ error() }}</p>
9310
+ }
9311
+ `,
9312
+ }]
9313
+ }], 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 }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], firstDayOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstDayOfWeek", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], inline: [{ type: i0.Input, args: [{ isSignal: true, alias: "inline", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], showActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActions", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTime", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], timeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeLabel", required: false }] }], hoursLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoursLabel", required: false }] }], minutesLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "minutesLabel", required: false }] }], doneLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "doneLabel", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], openCalendarLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "openCalendarLabel", required: false }] }], closeCalendarLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeCalendarLabel", required: false }] }], panelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelLabel", required: false }] }], calendarLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "calendarLabel", required: false }] }], previousLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], todayActionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "todayActionLabel", required: false }] }], clearActionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearActionLabel", required: false }] }], calendar: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SdsCalendar), { isSignal: true }] }], trigger: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }] } });
9314
+ let nextId$9 = 0;
9315
+
9316
+ /** Same first and last day (time-of-day ignored). `null` only equals `null`. */
9317
+ function isSameRange(a, b) {
9318
+ if (!a || !b)
9319
+ return !a && !b;
9320
+ return isSameDay(a.start, b.start) && isSameDay(a.end, b.end);
9321
+ }
9322
+ /** The last `days` days, today included: `sdsLastDays(7)` = today − 6 … today. */
9323
+ function sdsLastDays(days, today = new Date()) {
9324
+ const end = startOfDay(today);
9325
+ return { start: addDays(end, -(Math.max(1, Math.floor(days)) - 1)), end };
9326
+ }
9327
+ /**
9328
+ * The last `months` months, today included: `sdsLastMonths(12)` on 13 Oct 2026 = 14 Oct 2025 …
9329
+ * 13 Oct 2026 (the day after the same day `months` months ago, clamped to short months).
9330
+ */
9331
+ function sdsLastMonths(months, today = new Date()) {
9332
+ const end = startOfDay(today);
9333
+ return { start: addDays(addMonthsKeepDay(end, -Math.max(1, Math.floor(months))), 1), end };
9334
+ }
9335
+
9336
+ /** Viewport width (px) from which two months sit side by side — Tailwind's `sm`. */
9337
+ const TWO_MONTH_MIN_WIDTH = 640;
9338
+ /**
9339
+ * Range calendar: one or two side-by-side months on which a day range is picked with two clicks.
9340
+ * Used by `<sds-date-range-picker>`, and usable on its own (bind `[(start)]` and `[(end)]`).
9341
+ *
9342
+ * Selection rule: the first click sets `start` (and clears `end`); the second sets `end` — or, when
9343
+ * it lands BEFORE `start`, becomes the new `start`. Clicking the start day again gives a one-day
9344
+ * range. While the end is being chosen, the band previews start → hovered (or keyboard-focused) day.
9345
+ * `maxDays` caps the span: once a start is picked, days past `start + maxDays − 1` are disabled.
9346
+ *
9347
+ * Bare (no border / shadow / background): place it in your own card or panel.
9348
+ *
9349
+ * Keyboard: arrows move by day / week, Home / End to the week's start / end, PageUp / PageDown by
9350
+ * month (roving tabindex across both months); Enter / Space picks the focused day.
9351
+ *
9352
+ * @example
9353
+ * <sds-range-calendar [(start)]="from" [(end)]="to" [max]="today" />
9354
+ */
9355
+ class SdsRangeCalendar {
9356
+ host = inject(ElementRef);
9357
+ injector = inject(Injector);
9358
+ /** First day of the range (two-way: `[(start)]`). */
9359
+ start = model(null, ...(ngDevMode ? [{ debugName: "start" }] : []));
9360
+ /** Last day of the range, inclusive (two-way: `[(end)]`). `null` while the end is being chosen. */
9361
+ end = model(null, ...(ngDevMode ? [{ debugName: "end" }] : []));
9362
+ min = input(null, ...(ngDevMode ? [{ debugName: "min" }] : []));
9363
+ max = input(null, ...(ngDevMode ? [{ debugName: "max" }] : []));
9364
+ /** Longest selectable span in days (start and end included). `null` = no limit. */
9365
+ maxDays = input(null, ...(ngDevMode ? [{ debugName: "maxDays" }] : []));
9366
+ /** 0 = Sunday, 1 = Monday (default, European). */
9367
+ firstDayOfWeek = input(1, ...(ngDevMode ? [{ debugName: "firstDayOfWeek" }] : []));
9368
+ /** BCP 47 locale for month and weekday names. Defaults to `en-GB`; pass the app's locale. */
9369
+ locale = input('en-GB', ...(ngDevMode ? [{ debugName: "locale" }] : []));
9370
+ /** Months shown side by side (from the `sm` breakpoint; always one below it). */
9371
+ months = input(2, ...(ngDevMode ? [{ debugName: "months" }] : []));
9372
+ /** Accessible name of the calendar group. English default — translate it in the app. */
9373
+ calendarLabel = input('Calendar', ...(ngDevMode ? [{ debugName: "calendarLabel" }] : []));
9374
+ /** Accessible name of the icon-only "previous month" button (its ONLY name — translate it). */
9375
+ previousLabel = input('Previous', ...(ngDevMode ? [{ debugName: "previousLabel" }] : []));
9376
+ /** Accessible name of the icon-only "next month" button. See {@link previousLabel}. */
9377
+ nextLabel = input('Next', ...(ngDevMode ? [{ debugName: "nextLabel" }] : []));
9378
+ /** Emitted on every day pick (start or end). */
9379
+ daySelect = output();
9380
+ /** Emitted when a pick completes the range (both ends set). */
9381
+ rangeSelect = output();
9382
+ /** The day under the pointer / keyboard focus (`null` when the pointer leaves the grid). */
9383
+ dayHover = output();
9384
+ /** First month on screen. */
9385
+ viewDate = signal(startOfMonth(new Date()), ...(ngDevMode ? [{ debugName: "viewDate" }] : []));
9386
+ focusedDate = signal(startOfDay(new Date()), ...(ngDevMode ? [{ debugName: "focusedDate" }] : []));
9387
+ hovered = signal(null, ...(ngDevMode ? [{ debugName: "hovered" }] : []));
9388
+ wide = signal(true, ...(ngDevMode ? [{ debugName: "wide" }] : []));
9389
+ /** Months actually rendered: {@link months}, but one on narrow viewports. */
9390
+ monthsShown = computed(() => (this.months() === 2 && this.wide() ? 2 : 1), ...(ngDevMode ? [{ debugName: "monthsShown" }] : []));
9391
+ constructor() {
9392
+ this.watchViewport();
9393
+ // Keep a range set from outside on screen (a form writing a value, a preset). A pick inside the
9394
+ // grid is already visible, so this never moves the months under the pointer.
9395
+ effect(() => {
9396
+ const s = this.start();
9397
+ const e = this.end();
9398
+ untracked(() => {
9399
+ const anchor = e ?? s;
9400
+ if (anchor && !this.isVisible(anchor))
9401
+ this.anchorView(anchor, !!e);
9402
+ });
9403
+ });
9404
+ }
9405
+ watchViewport() {
9406
+ if (typeof window === 'undefined' || typeof window.matchMedia !== 'function')
9407
+ return;
9408
+ const mq = window.matchMedia(`(min-width: ${TWO_MONTH_MIN_WIDTH}px)`);
9409
+ this.wide.set(mq.matches);
9410
+ const onChange = (e) => this.wide.set(e.matches);
9411
+ mq.addEventListener?.('change', onChange);
9412
+ inject(DestroyRef).onDestroy(() => mq.removeEventListener?.('change', onChange));
9413
+ }
9414
+ // --- styling helpers ---
9415
+ navBtn = 'inline-flex h-8 w-8 items-center justify-center rounded-control text-ink-soft hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 disabled:cursor-not-allowed disabled:opacity-40';
9416
+ // --- derived state ---
9417
+ weekdayLabels = computed(() => {
9418
+ const fmt = new Intl.DateTimeFormat(this.locale(), { weekday: 'short' });
9419
+ const sunday = new Date(2023, 0, 1); // a known Sunday
9420
+ return Array.from({ length: 7 }, (_, i) => fmt.format(addDays(sunday, (this.firstDayOfWeek() + i) % 7)));
9421
+ }, ...(ngDevMode ? [{ debugName: "weekdayLabels" }] : []));
9422
+ /** The end used for drawing: the committed end, else the previewed (hovered) one. */
9423
+ previewEnd = computed(() => {
9424
+ const s = this.start();
9425
+ const h = this.hovered();
9426
+ if (!s || this.end() || !h)
9427
+ return null;
9428
+ const day = startOfDay(h);
9429
+ if (day < startOfDay(s) || this.isDisabled(day))
9430
+ return null;
9431
+ return day;
9432
+ }, ...(ngDevMode ? [{ debugName: "previewEnd" }] : []));
9433
+ prevDisabled = computed(() => {
9434
+ const min = this.min();
9435
+ return !!min && this.viewDate() <= startOfMonth(min);
9436
+ }, ...(ngDevMode ? [{ debugName: "prevDisabled" }] : []));
9437
+ nextDisabled = computed(() => {
9438
+ const max = this.max();
9439
+ return !!max && addMonths(this.viewDate(), this.monthsShown() - 1) >= startOfMonth(max);
9440
+ }, ...(ngDevMode ? [{ debugName: "nextDisabled" }] : []));
9441
+ monthViews = computed(() => {
9442
+ const n = this.monthsShown();
9443
+ const first = this.viewDate();
9444
+ const titleFmt = new Intl.DateTimeFormat(this.locale(), { month: 'long', year: 'numeric' });
9445
+ const today = startOfDay(new Date());
9446
+ const s = this.start() ? startOfDay(this.start()) : null;
9447
+ const committedEnd = this.end() ? startOfDay(this.end()) : null;
9448
+ const preview = this.previewEnd();
9449
+ const e = committedEnd ?? preview;
9450
+ const lastVisible = new Date(first.getFullYear(), first.getMonth() + n, 0);
9451
+ const focus = this.focusedDate();
9452
+ const tabbable = focus >= first && focus <= lastVisible ? focus : first;
9453
+ // Re-read so the disabled state follows start / end / maxDays / min / max.
9454
+ this.maxDays();
9455
+ this.min();
9456
+ this.max();
9457
+ const out = [];
9458
+ for (let m = 0; m < n; m++) {
9459
+ const month = addMonths(first, m);
9460
+ const offset = (month.getDay() - this.firstDayOfWeek() + 7) % 7;
9461
+ const daysInMonth = new Date(month.getFullYear(), month.getMonth() + 1, 0).getDate();
9462
+ const weeks = [];
9463
+ for (let w = 0; w < 6; w++) {
9464
+ const row = [];
9465
+ for (let col = 0; col < 7; col++) {
9466
+ const dayNum = w * 7 + col - offset + 1;
9467
+ if (dayNum < 1 || dayNum > daysInMonth) {
9468
+ row.push(blankCell(`${isoDay(month)}-b${w}${col}`));
9469
+ continue;
9470
+ }
9471
+ const date = new Date(month.getFullYear(), month.getMonth(), dayNum);
9472
+ const isStart = isSameDay(date, s);
9473
+ const isEndDay = isSameDay(date, e);
9474
+ const hasSpan = !!s && !!e && !isSameDay(s, e);
9475
+ const inRange = !!s && !!e && date > s && date < e;
9476
+ let band = 'none';
9477
+ if (hasSpan) {
9478
+ if (inRange)
9479
+ band = 'full';
9480
+ else if (isStart)
9481
+ band = 'right';
9482
+ else if (isEndDay)
9483
+ band = 'left';
9484
+ }
9485
+ row.push({
9486
+ key: isoDay(date),
9487
+ date,
9488
+ day: dayNum,
9489
+ today: isSameDay(date, today),
9490
+ disabled: this.isDisabled(date),
9491
+ tabbable: isSameDay(date, tabbable),
9492
+ isStart,
9493
+ isEnd: isEndDay && !!committedEnd,
9494
+ isPreviewEnd: isEndDay && !committedEnd && !isStart,
9495
+ inRange,
9496
+ band,
9497
+ roundLeft: col === 0 || dayNum === 1,
9498
+ roundRight: col === 6 || dayNum === daysInMonth,
9499
+ });
9500
+ }
9501
+ weeks.push(row);
9502
+ }
9503
+ out.push({ key: isoDay(month), title: titleFmt.format(month), weeks });
9504
+ }
9505
+ return out;
9506
+ }, ...(ngDevMode ? [{ debugName: "monthViews" }] : []));
9507
+ // --- selection ---
9508
+ isDisabled(d) {
9509
+ if (isOutOfRange(d, this.min(), this.max()))
9510
+ return true;
9511
+ const s = this.start();
9512
+ const limit = this.maxDays();
9513
+ if (s && !this.end() && limit != null && limit > 0) {
9514
+ return diffInDays(s, d) >= limit;
9515
+ }
9516
+ return false;
9517
+ }
9518
+ selectDay(d) {
9519
+ const day = startOfDay(d);
9520
+ if (this.isDisabled(day))
9521
+ return;
9522
+ const s = this.start();
9523
+ if (!s || this.end()) {
9524
+ this.start.set(day);
9525
+ this.end.set(null);
9526
+ }
9527
+ else if (day < startOfDay(s)) {
9528
+ this.start.set(day);
9529
+ }
9530
+ else {
9531
+ this.end.set(day);
9532
+ this.rangeSelect.emit({ start: startOfDay(s), end: day });
9533
+ }
9534
+ this.focusedDate.set(day);
9535
+ this.daySelect.emit(day);
9536
+ }
9537
+ setHover(d) {
9538
+ if (isSameDay(d, this.hovered()) || (!d && !this.hovered()))
9539
+ return;
9540
+ this.hovered.set(d);
9541
+ this.dayHover.emit(d);
9542
+ }
9543
+ onDayFocus(d) {
9544
+ this.focusedDate.set(startOfDay(d));
9545
+ this.setHover(d);
9546
+ }
9547
+ // --- navigation ---
9548
+ shiftView(delta) {
9549
+ this.viewDate.set(addMonths(this.viewDate(), delta));
9550
+ }
9551
+ isVisible(d) {
9552
+ const first = this.viewDate();
9553
+ const last = new Date(first.getFullYear(), first.getMonth() + this.monthsShown(), 0);
9554
+ const day = startOfDay(d);
9555
+ return day >= first && day <= last;
9556
+ }
9557
+ /** Show `d`'s month first, or — with `last` — as the right-most month. */
9558
+ anchorView(d, last) {
9559
+ const m = startOfMonth(d);
9560
+ this.viewDate.set(last ? addMonths(m, -(this.monthsShown() - 1)) : m);
9561
+ }
9562
+ onGridKeydown(e) {
9563
+ let f = this.focusedDate();
9564
+ const weekPos = (f.getDay() - this.firstDayOfWeek() + 7) % 7;
9565
+ switch (e.key) {
9566
+ case 'ArrowLeft':
9567
+ f = addDays(f, -1);
9568
+ break;
9569
+ case 'ArrowRight':
9570
+ f = addDays(f, 1);
9571
+ break;
9572
+ case 'ArrowUp':
9573
+ f = addDays(f, -7);
9574
+ break;
9575
+ case 'ArrowDown':
9576
+ f = addDays(f, 7);
9577
+ break;
9578
+ case 'Home':
9579
+ f = addDays(f, -weekPos);
9580
+ break;
9581
+ case 'End':
9582
+ f = addDays(f, 6 - weekPos);
9583
+ break;
9584
+ case 'PageUp':
9585
+ f = addMonthsKeepDay(f, -1);
9586
+ break;
9587
+ case 'PageDown':
9588
+ f = addMonthsKeepDay(f, 1);
9589
+ break;
9590
+ case 'Enter':
9591
+ case ' ':
9592
+ e.preventDefault();
9593
+ this.selectDay(f);
9594
+ return;
9595
+ default:
9596
+ return;
9597
+ }
9598
+ e.preventDefault();
9599
+ if (!this.isVisible(f))
9600
+ this.anchorView(f, f > this.viewDate());
9601
+ this.focusedDate.set(f);
9602
+ this.setHover(f);
9603
+ this.focusCell(f);
9604
+ }
9605
+ /**
9606
+ * Show and focus the active day: the range's end (as the right-most month), else its start, else
9607
+ * today (right-most when `max` is this month or earlier, so a "past only" calendar does not open on
9608
+ * an all-disabled next month). Called by the picker when its panel opens.
9609
+ */
9610
+ focusActiveCell() {
9611
+ const s = this.start();
9612
+ const e = this.end();
9613
+ const today = startOfDay(new Date());
9614
+ let target;
9615
+ if (e) {
9616
+ target = startOfDay(e);
9617
+ this.anchorView(target, true);
9618
+ }
9619
+ else if (s) {
9620
+ target = startOfDay(s);
9621
+ this.anchorView(target, false);
9622
+ }
9623
+ else {
9624
+ const max = this.max();
9625
+ target = max && today > startOfDay(max) ? startOfDay(max) : today;
9626
+ this.anchorView(target, !!max && startOfMonth(max) <= startOfMonth(today));
9627
+ }
9628
+ this.focusedDate.set(target);
9629
+ this.focusCell(target);
9630
+ }
9631
+ focusHandle;
9632
+ /**
9633
+ * Focus a day once it is rendered. A month change re-renders the grid, so try after the next
9634
+ * render AND on a timer: whichever runs once the cell exists wins (focusing twice is harmless),
9635
+ * and the timer still fires when nothing changed and no render is coming.
9636
+ */
9637
+ focusCell(d) {
9638
+ const key = isoDay(d);
9639
+ const focus = () => {
9640
+ if (isoDay(this.focusedDate()) !== key)
9641
+ return; // superseded by a later move
9642
+ const el = this.host.nativeElement.querySelector(`[data-date="${key}"]`);
9643
+ if (el && document.activeElement !== el)
9644
+ el.focus();
9645
+ };
9646
+ clearTimeout(this.focusHandle);
9647
+ this.focusHandle = setTimeout(focus);
9648
+ afterNextRender(focus, { injector: this.injector });
9649
+ }
9650
+ // --- template helpers ---
9651
+ iso(d) {
9652
+ return isoDay(d);
9653
+ }
9654
+ fullLabel(d) {
9655
+ return new Intl.DateTimeFormat(this.locale(), {
9656
+ weekday: 'long',
9657
+ day: 'numeric',
9658
+ month: 'long',
9659
+ year: 'numeric',
9660
+ }).format(d);
9661
+ }
9662
+ bandClass(cell) {
9663
+ let cls = 'pointer-events-none absolute inset-y-0.5 bg-brand-50';
9664
+ if (cell.band === 'full') {
9665
+ cls += ' inset-x-0';
9666
+ if (cell.roundLeft)
9667
+ cls += ' rounded-l-full';
9668
+ if (cell.roundRight)
9669
+ cls += ' rounded-r-full';
9670
+ }
9671
+ else if (cell.band === 'right') {
9672
+ cls += ' left-1/2 right-0';
9673
+ if (cell.roundRight)
9674
+ cls += ' rounded-r-full';
9675
+ }
9676
+ else {
9677
+ cls += ' left-0 right-1/2';
9678
+ if (cell.roundLeft)
9679
+ cls += ' rounded-l-full';
9680
+ }
9681
+ return cls;
9682
+ }
9683
+ dayClass(cell) {
9684
+ const base = 'inline-flex h-9 w-9 items-center justify-center rounded-full text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 disabled:cursor-not-allowed disabled:opacity-40';
9685
+ if (cell.isStart || cell.isEnd) {
9686
+ return `${base} bg-brand-600 font-semibold text-white hover:bg-brand-700 ${DARK_BRAND_FILL_HOVER} focus-visible:ring-offset-1 focus-visible:ring-offset-surface`;
9687
+ }
9688
+ if (cell.isPreviewEnd) {
9689
+ return `${base} bg-brand-100 font-semibold text-ink ring-1 ring-inset ring-brand-300`;
9690
+ }
9691
+ if (cell.today) {
9692
+ return `${base} font-bold text-brand-600 ring-1 ring-inset ring-brand-300 ${cell.inRange ? 'hover:bg-brand-100' : 'hover:bg-brand-50'}`;
9693
+ }
9694
+ return `${base} text-ink ${cell.inRange ? 'hover:bg-brand-100' : 'hover:bg-brand-50'}`;
9695
+ }
9696
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsRangeCalendar, deps: [], target: i0.ɵɵFactoryTarget.Component });
9697
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsRangeCalendar, isStandalone: true, selector: "sds-range-calendar", inputs: { start: { classPropertyName: "start", publicName: "start", isSignal: true, isRequired: false, transformFunction: null }, end: { classPropertyName: "end", publicName: "end", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, maxDays: { classPropertyName: "maxDays", publicName: "maxDays", isSignal: true, isRequired: false, transformFunction: null }, firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, months: { classPropertyName: "months", publicName: "months", isSignal: true, isRequired: false, transformFunction: null }, calendarLabel: { classPropertyName: "calendarLabel", publicName: "calendarLabel", isSignal: true, isRequired: false, transformFunction: null }, previousLabel: { classPropertyName: "previousLabel", publicName: "previousLabel", isSignal: true, isRequired: false, transformFunction: null }, nextLabel: { classPropertyName: "nextLabel", publicName: "nextLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { start: "startChange", end: "endChange", daySelect: "daySelect", rangeSelect: "rangeSelect", dayHover: "dayHover" }, host: { classAttribute: "block" }, ngImport: i0, template: `
9698
+ <div class="select-none" role="group" [attr.aria-label]="calendarLabel()" (mouseleave)="setHover(null)">
9699
+ <div class="flex flex-col gap-4 sm:flex-row sm:gap-6">
9700
+ @for (m of monthViews(); track m.key; let first = $first; let last = $last) {
9701
+ <div class="w-[17.5rem]">
9702
+ <div class="mb-2 flex items-center justify-between">
9703
+ @if (first) {
9704
+ <button
9705
+ type="button"
9706
+ [class]="navBtn"
9707
+ [disabled]="prevDisabled()"
9708
+ [attr.aria-label]="previousLabel()"
9709
+ (click)="shiftView(-1)"
9710
+ >
9711
+ <sds-icon name="chevron-left" [size]="18" />
9712
+ </button>
9713
+ } @else {
9714
+ <span class="h-8 w-8" aria-hidden="true"></span>
9715
+ }
9716
+ <span class="px-1 text-sm font-semibold capitalize text-ink" aria-live="polite">
9717
+ {{ m.title }}
9718
+ </span>
9719
+ @if (last) {
9720
+ <button
9721
+ type="button"
9722
+ [class]="navBtn"
9723
+ [disabled]="nextDisabled()"
9724
+ [attr.aria-label]="nextLabel()"
9725
+ (click)="shiftView(1)"
9726
+ >
9727
+ <sds-icon name="chevron-right" [size]="18" />
9728
+ </button>
9729
+ } @else {
9730
+ <span class="h-8 w-8" aria-hidden="true"></span>
9731
+ }
9732
+ </div>
9733
+
9734
+ <table class="w-full table-fixed border-collapse" role="grid" [attr.aria-label]="m.title">
9735
+ <thead>
9736
+ <tr>
9737
+ @for (wd of weekdayLabels(); track $index) {
9738
+ <th scope="col" class="pb-1 text-[11px] font-medium uppercase text-ink-faint">
9739
+ {{ wd }}
9740
+ </th>
9741
+ }
9742
+ </tr>
9743
+ </thead>
9744
+ <tbody (keydown)="onGridKeydown($event)">
9745
+ @for (week of m.weeks; track $index) {
9746
+ <tr>
9747
+ @for (cell of week; track cell.key) {
9748
+ <td class="relative p-0 text-center">
9749
+ @if (cell.date; as date) {
9750
+ @if (cell.band !== 'none') {
9751
+ <span aria-hidden="true" [class]="bandClass(cell)"></span>
9752
+ }
9753
+ <span class="relative flex justify-center py-0.5">
9754
+ <button
9755
+ type="button"
9756
+ [attr.data-date]="iso(date)"
9757
+ [tabindex]="cell.tabbable ? 0 : -1"
9758
+ [disabled]="cell.disabled"
9759
+ [attr.aria-selected]="cell.isStart || cell.isEnd"
9760
+ [attr.aria-current]="cell.today ? 'date' : null"
9761
+ [attr.aria-label]="fullLabel(date)"
9762
+ [class]="dayClass(cell)"
9763
+ (click)="selectDay(date)"
9764
+ (mouseenter)="setHover(date)"
9765
+ (focus)="onDayFocus(date)"
9766
+ >
9767
+ {{ cell.day }}
9768
+ </button>
9769
+ </span>
9770
+ }
9771
+ </td>
9772
+ }
9773
+ </tr>
9774
+ }
9775
+ </tbody>
9776
+ </table>
9777
+ </div>
9778
+ }
9779
+ </div>
9780
+ </div>
9781
+ `, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth", "filled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9782
+ }
9783
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsRangeCalendar, decorators: [{
9784
+ type: Component,
9785
+ args: [{
9786
+ selector: 'sds-range-calendar',
9787
+ changeDetection: ChangeDetectionStrategy.OnPush,
9788
+ imports: [SdsIcon],
9789
+ host: { class: 'block' },
9790
+ template: `
9791
+ <div class="select-none" role="group" [attr.aria-label]="calendarLabel()" (mouseleave)="setHover(null)">
9792
+ <div class="flex flex-col gap-4 sm:flex-row sm:gap-6">
9793
+ @for (m of monthViews(); track m.key; let first = $first; let last = $last) {
9794
+ <div class="w-[17.5rem]">
9795
+ <div class="mb-2 flex items-center justify-between">
9796
+ @if (first) {
9797
+ <button
9798
+ type="button"
9799
+ [class]="navBtn"
9800
+ [disabled]="prevDisabled()"
9801
+ [attr.aria-label]="previousLabel()"
9802
+ (click)="shiftView(-1)"
9803
+ >
9804
+ <sds-icon name="chevron-left" [size]="18" />
9805
+ </button>
9806
+ } @else {
9807
+ <span class="h-8 w-8" aria-hidden="true"></span>
9808
+ }
9809
+ <span class="px-1 text-sm font-semibold capitalize text-ink" aria-live="polite">
9810
+ {{ m.title }}
9811
+ </span>
9812
+ @if (last) {
9813
+ <button
9814
+ type="button"
9815
+ [class]="navBtn"
9816
+ [disabled]="nextDisabled()"
9817
+ [attr.aria-label]="nextLabel()"
9818
+ (click)="shiftView(1)"
9819
+ >
9820
+ <sds-icon name="chevron-right" [size]="18" />
9821
+ </button>
9822
+ } @else {
9823
+ <span class="h-8 w-8" aria-hidden="true"></span>
9824
+ }
9825
+ </div>
9826
+
9827
+ <table class="w-full table-fixed border-collapse" role="grid" [attr.aria-label]="m.title">
9828
+ <thead>
9829
+ <tr>
9830
+ @for (wd of weekdayLabels(); track $index) {
9831
+ <th scope="col" class="pb-1 text-[11px] font-medium uppercase text-ink-faint">
9832
+ {{ wd }}
9833
+ </th>
9834
+ }
9835
+ </tr>
9836
+ </thead>
9837
+ <tbody (keydown)="onGridKeydown($event)">
9838
+ @for (week of m.weeks; track $index) {
9839
+ <tr>
9840
+ @for (cell of week; track cell.key) {
9841
+ <td class="relative p-0 text-center">
9842
+ @if (cell.date; as date) {
9843
+ @if (cell.band !== 'none') {
9844
+ <span aria-hidden="true" [class]="bandClass(cell)"></span>
9845
+ }
9846
+ <span class="relative flex justify-center py-0.5">
9847
+ <button
9848
+ type="button"
9849
+ [attr.data-date]="iso(date)"
9850
+ [tabindex]="cell.tabbable ? 0 : -1"
9851
+ [disabled]="cell.disabled"
9852
+ [attr.aria-selected]="cell.isStart || cell.isEnd"
9853
+ [attr.aria-current]="cell.today ? 'date' : null"
9854
+ [attr.aria-label]="fullLabel(date)"
9855
+ [class]="dayClass(cell)"
9856
+ (click)="selectDay(date)"
9857
+ (mouseenter)="setHover(date)"
9858
+ (focus)="onDayFocus(date)"
9859
+ >
9860
+ {{ cell.day }}
9861
+ </button>
9862
+ </span>
9863
+ }
9864
+ </td>
9865
+ }
9866
+ </tr>
9867
+ }
9868
+ </tbody>
9869
+ </table>
9870
+ </div>
9871
+ }
9872
+ </div>
9873
+ </div>
9874
+ `,
9875
+ }]
9876
+ }], ctorParameters: () => [], propDecorators: { start: [{ type: i0.Input, args: [{ isSignal: true, alias: "start", required: false }] }, { type: i0.Output, args: ["startChange"] }], end: [{ type: i0.Input, args: [{ isSignal: true, alias: "end", required: false }] }, { type: i0.Output, args: ["endChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], maxDays: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDays", required: false }] }], firstDayOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstDayOfWeek", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], months: [{ type: i0.Input, args: [{ isSignal: true, alias: "months", required: false }] }], calendarLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "calendarLabel", required: false }] }], previousLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], daySelect: [{ type: i0.Output, args: ["daySelect"] }], rangeSelect: [{ type: i0.Output, args: ["rangeSelect"] }], dayHover: [{ type: i0.Output, args: ["dayHover"] }] } });
9877
+ function blankCell(key) {
9878
+ return {
9879
+ key,
9880
+ date: null,
9881
+ day: 0,
9882
+ today: false,
9883
+ disabled: true,
9884
+ tabbable: false,
9885
+ isStart: false,
9886
+ isEnd: false,
9887
+ isPreviewEnd: false,
9888
+ inRange: false,
9889
+ band: 'none',
9890
+ roundLeft: false,
9891
+ roundRight: false,
9892
+ };
9893
+ }
9894
+
9895
+ const SIZES$6 = {
9896
+ sm: 'h-9 text-sm',
9897
+ md: 'h-10 text-sm',
9898
+ lg: 'h-12 text-base',
9899
+ };
9900
+ /**
9901
+ * Date range picker: a field that opens a panel with quick ranges (presets) on the left and a
9902
+ * two-month range calendar on the right. A preset applies at once and closes the panel; a custom
9903
+ * range is picked with two clicks and stays PENDING until "Apply" (Cancel, Escape and a click away
9904
+ * discard it).
9905
+ *
9906
+ * The value is an inclusive `SdsDateRange` (`{ start, end }`, local midnight) or `null`, bound with
9907
+ * `[(value)]` (a `model`, not a ControlValueAccessor). `(presetSelect)` emits the preset `id` when
9908
+ * one is chosen, and `activePresetId()` tells which preset the current value equals (`null` = a
9909
+ * custom range), so an app can remember "30d" rather than two dates.
9910
+ *
9911
+ * Every visible and accessible string is an input with an English default — translate them all.
9912
+ *
9913
+ * @example
9914
+ * <sds-date-range-picker label="Period" [presets]="presets" [max]="today" [(value)]="range" />
9915
+ */
9916
+ class SdsDateRangePicker {
9917
+ host = inject(ElementRef);
9918
+ injector = inject(Injector);
9919
+ uid = nextId$8++;
9920
+ fieldId = `sds-date-range-${this.uid}`;
9921
+ labelId = `${this.fieldId}-label`;
9922
+ valueId = `${this.fieldId}-value`;
9923
+ /** The selected range (two-way: `[(value)]`), `null` when none. Day granularity, end inclusive. */
9924
+ value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
9925
+ /** Quick ranges listed on the left of the panel; choosing one applies it and closes the panel. */
9926
+ presets = input([], ...(ngDevMode ? [{ debugName: "presets" }] : []));
9927
+ min = input(null, ...(ngDevMode ? [{ debugName: "min" }] : []));
9928
+ max = input(null, ...(ngDevMode ? [{ debugName: "max" }] : []));
9929
+ /** Longest custom range in days (start and end included). `null` = no limit. */
9930
+ maxDays = input(null, ...(ngDevMode ? [{ debugName: "maxDays" }] : []));
9931
+ /** 0 = Sunday, 1 = Monday (default, European). */
9932
+ firstDayOfWeek = input(1, ...(ngDevMode ? [{ debugName: "firstDayOfWeek" }] : []));
9933
+ /** BCP 47 locale for the calendar and the default range format. Defaults to `en-GB`. */
9934
+ locale = input('en-GB', ...(ngDevMode ? [{ debugName: "locale" }] : []));
9935
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
9936
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
9937
+ /** Months side by side in the panel (from the `sm` breakpoint; always one below it). */
9938
+ months = input(2, ...(ngDevMode ? [{ debugName: "months" }] : []));
9939
+ /**
9940
+ * Display formatter for the field. Default: `Intl.DateTimeFormat#formatRange` with day, short
9941
+ * month and year in {@link locale} (e.g. `1–7 Oct 2026`) — see `formatDateRange`.
9942
+ */
9943
+ format = input(null, ...(ngDevMode ? [{ debugName: "format" }] : []));
9944
+ /** Visible label above the field. */
9945
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
9946
+ /** Accessible name when there is no visible {@link label}. */
9947
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
9948
+ /** Field text when no range is selected. */
9949
+ placeholder = input('Select dates', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
9950
+ /** Accessible name of the overlay dialog. */
9951
+ panelLabel = input('Choose a date range', ...(ngDevMode ? [{ debugName: "panelLabel" }] : []));
9952
+ /** Heading (and group name) of the presets column. */
9953
+ presetsLabel = input('Quick ranges', ...(ngDevMode ? [{ debugName: "presetsLabel" }] : []));
9954
+ /** Caption of the pending start date in the panel footer. */
9955
+ startLabel = input('Start', ...(ngDevMode ? [{ debugName: "startLabel" }] : []));
9956
+ /** Caption of the pending end date in the panel footer. */
9957
+ endLabel = input('End', ...(ngDevMode ? [{ debugName: "endLabel" }] : []));
9958
+ /** Text of the footer's apply action. */
9959
+ applyLabel = input('Apply', ...(ngDevMode ? [{ debugName: "applyLabel" }] : []));
9960
+ /** Text of the footer's cancel action. */
9961
+ cancelLabel = input('Cancel', ...(ngDevMode ? [{ debugName: "cancelLabel" }] : []));
9962
+ /** Accessible name of the calendar group (forwarded to `<sds-range-calendar>`). */
9963
+ calendarLabel = input('Calendar', ...(ngDevMode ? [{ debugName: "calendarLabel" }] : []));
9964
+ /** Accessible name of the icon-only "previous month" button. */
9965
+ previousLabel = input('Previous', ...(ngDevMode ? [{ debugName: "previousLabel" }] : []));
9966
+ /** Accessible name of the icon-only "next month" button. */
9967
+ nextLabel = input('Next', ...(ngDevMode ? [{ debugName: "nextLabel" }] : []));
9968
+ /** Emits the preset `id` when a preset is chosen (after `value` has been updated). */
9969
+ presetSelect = output();
9970
+ open = signal(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
9971
+ pendingStart = signal(null, ...(ngDevMode ? [{ debugName: "pendingStart" }] : []));
9972
+ pendingEnd = signal(null, ...(ngDevMode ? [{ debugName: "pendingEnd" }] : []));
9973
+ calendar = viewChild(SdsRangeCalendar, ...(ngDevMode ? [{ debugName: "calendar" }] : []));
9974
+ trigger = viewChild('trigger', ...(ngDevMode ? [{ debugName: "trigger" }] : []));
9975
+ /** The overlay panel, relocated to document.body while open. */
9976
+ panelEl = null;
9977
+ /** Id of the preset the current value equals, or `null` (no value, or a custom range). */
9978
+ activePresetId = computed(() => this.presetIdFor(this.value()), ...(ngDevMode ? [{ debugName: "activePresetId" }] : []));
9979
+ pendingPresetId = computed(() => {
9980
+ const s = this.pendingStart();
9981
+ const e = this.pendingEnd();
9982
+ return s && e ? this.presetIdFor({ start: s, end: e }) : null;
9983
+ }, ...(ngDevMode ? [{ debugName: "pendingPresetId" }] : []));
9984
+ displayValue = computed(() => {
9985
+ const v = this.value();
9986
+ if (!v)
9987
+ return '';
9988
+ const fmt = this.format();
9989
+ return fmt ? fmt(v) : formatDateRange(v, this.locale());
9990
+ }, ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
9991
+ dayFmt = computed(() => new Intl.DateTimeFormat(this.locale(), { day: 'numeric', month: 'short', year: 'numeric' }), ...(ngDevMode ? [{ debugName: "dayFmt" }] : []));
9992
+ pendingStartText = computed(() => {
9993
+ const s = this.pendingStart();
9994
+ return s ? this.dayFmt().format(s) : '—';
9995
+ }, ...(ngDevMode ? [{ debugName: "pendingStartText" }] : []));
9996
+ pendingEndText = computed(() => {
9997
+ const e = this.pendingEnd();
9998
+ return e ? this.dayFmt().format(e) : '—';
9999
+ }, ...(ngDevMode ? [{ debugName: "pendingEndText" }] : []));
10000
+ canApply = computed(() => !!this.pendingStart() && !!this.pendingEnd(), ...(ngDevMode ? [{ debugName: "canApply" }] : []));
10001
+ triggerClasses = computed(() => {
10002
+ const parts = [
10003
+ 'flex w-full items-center gap-2 rounded-control border border-border bg-surface px-3 text-left transition-colors focus-visible:border-brand-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500/25',
10004
+ SIZES$6[this.size()],
10005
+ ];
10006
+ if (this.open())
10007
+ parts.push('border-brand-500 ring-2 ring-brand-500/25');
10008
+ parts.push(this.disabled() ? 'cursor-not-allowed opacity-60' : 'cursor-pointer hover:border-ink-faint');
10009
+ return parts.join(' ');
10010
+ }, ...(ngDevMode ? [{ debugName: "triggerClasses" }] : []));
10011
+ cancelBtn = 'rounded-control px-3 py-1.5 text-sm font-medium text-ink-soft hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500';
10012
+ applyBtn = `rounded-control bg-brand-600 px-3 py-1.5 text-sm font-semibold text-white hover:bg-brand-700 ${DARK_BRAND_FILL_HOVER} focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-1 focus-visible:ring-offset-surface disabled:cursor-not-allowed disabled:opacity-40`;
10013
+ presetClass(active) {
10014
+ const base = 'rounded-control px-2.5 py-1.5 text-left text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500';
10015
+ return active
10016
+ ? `${base} bg-brand-50 font-semibold text-ink`
10017
+ : `${base} text-ink-soft hover:bg-muted hover:text-ink`;
10018
+ }
10019
+ presetIdFor(range) {
10020
+ if (!range)
10021
+ return null;
10022
+ return this.presets().find((p) => isSameRange(p.range(), range))?.id ?? null;
10023
+ }
10024
+ // --- open / close ---
10025
+ toggle(event) {
10026
+ event?.stopPropagation();
10027
+ if (this.disabled())
10028
+ return;
10029
+ this.open() ? this.close() : this.openPanel();
10030
+ }
10031
+ onTriggerArrowDown(event) {
10032
+ event.preventDefault();
10033
+ if (!this.disabled() && !this.open())
10034
+ this.openPanel();
10035
+ }
10036
+ openPanel() {
10037
+ if (this.disabled())
10038
+ return;
10039
+ const v = this.value();
10040
+ this.pendingStart.set(v ? startOfDay(v.start) : null);
10041
+ this.pendingEnd.set(v ? startOfDay(v.end) : null);
10042
+ this.open.set(true);
10043
+ // Run after the overlay is rendered, then relocate it to <body>.
10044
+ afterNextRender(() => {
10045
+ this.panelEl = this.host.nativeElement.querySelector('[role="dialog"]');
10046
+ this.positionPanel(); // also relocates to document.body
10047
+ this.addReflowListeners();
10048
+ this.calendar()?.focusActiveCell();
10049
+ }, { injector: this.injector });
10050
+ }
10051
+ /** Close the panel, discarding a pending (un-applied) custom range. */
10052
+ close(returnFocus = false) {
10053
+ if (!this.open())
10054
+ return;
10055
+ this.removeReflowListeners();
10056
+ this.panelEl?.remove(); // pull the body-relocated node before destroying the view
10057
+ this.panelEl = null;
10058
+ this.open.set(false);
10059
+ if (returnFocus)
10060
+ this.trigger()?.nativeElement.focus();
10061
+ }
10062
+ choosePreset(p) {
10063
+ const r = p.range();
10064
+ this.value.set({ start: startOfDay(r.start), end: startOfDay(r.end) });
10065
+ this.close(true);
10066
+ this.presetSelect.emit(p.id);
10067
+ }
10068
+ apply() {
10069
+ const s = this.pendingStart();
10070
+ const e = this.pendingEnd();
10071
+ if (!s || !e)
10072
+ return;
10073
+ const next = { start: startOfDay(s), end: startOfDay(e) };
10074
+ if (!isSameRange(next, this.value()))
10075
+ this.value.set(next);
10076
+ this.close(true);
10077
+ }
10078
+ /**
10079
+ * Escape closes the panel and consumes the event while it is open, so an ancestor modal does not
10080
+ * also dismiss on the same keystroke. When closed, the event passes through.
10081
+ */
10082
+ onEscape(event) {
10083
+ if (!this.open())
10084
+ return;
10085
+ event.preventDefault();
10086
+ event.stopPropagation();
10087
+ this.close(true);
10088
+ }
10089
+ /**
10090
+ * Self-contained Tab trap for the overlay. The panel lives in document.body, outside any ancestor
10091
+ * modal's focus trap; this keeps Tab / Shift+Tab cycling within the panel's own focusables.
10092
+ */
10093
+ onPanelKeydown(event) {
10094
+ if (event.key !== 'Tab' || !this.panelEl)
10095
+ return;
10096
+ const sel = 'a[href],button:not([disabled]):not([tabindex="-1"]),input:not([disabled]),' +
10097
+ '[tabindex]:not([tabindex="-1"])';
10098
+ const items = Array.from(this.panelEl.querySelectorAll(sel)).filter((el) => el.offsetParent !== null);
10099
+ if (items.length === 0)
10100
+ return;
10101
+ const first = items[0];
10102
+ const last = items[items.length - 1];
10103
+ const active = document.activeElement;
10104
+ if (event.shiftKey && (active === first || !this.panelEl.contains(active))) {
10105
+ event.preventDefault();
10106
+ last.focus();
10107
+ }
10108
+ else if (!event.shiftKey && active === last) {
10109
+ event.preventDefault();
10110
+ first.focus();
10111
+ }
10112
+ }
10113
+ onDocumentClick(event) {
10114
+ if (!this.open())
10115
+ return;
10116
+ // composedPath is fixed at dispatch time, so a click on a node re-rendered away still counts.
10117
+ const path = event.composedPath();
10118
+ const inField = path.includes(this.host.nativeElement);
10119
+ const inPanel = !!this.panelEl && path.includes(this.panelEl);
10120
+ if (!inField && !inPanel)
10121
+ this.close();
10122
+ }
10123
+ // --- body-appended overlay positioning ---
10124
+ reflow = () => this.positionPanel();
10125
+ listening = false;
10126
+ addReflowListeners() {
10127
+ if (this.listening)
10128
+ return;
10129
+ window.addEventListener('scroll', this.reflow, true);
10130
+ window.addEventListener('resize', this.reflow);
10131
+ this.listening = true;
10132
+ }
10133
+ removeReflowListeners() {
10134
+ if (!this.listening)
10135
+ return;
10136
+ window.removeEventListener('scroll', this.reflow, true);
10137
+ window.removeEventListener('resize', this.reflow);
10138
+ this.listening = false;
10139
+ }
10140
+ positionPanel() {
10141
+ const triggerEl = this.trigger()?.nativeElement;
10142
+ const panelEl = this.panelEl;
10143
+ if (!triggerEl || !panelEl)
10144
+ return;
10145
+ if (panelEl.parentElement !== document.body) {
10146
+ document.body.appendChild(panelEl);
10147
+ }
10148
+ const r = triggerEl.getBoundingClientRect();
10149
+ const ph = panelEl.offsetHeight || 420;
10150
+ const pw = panelEl.offsetWidth || 760;
10151
+ const gap = 6;
10152
+ const margin = 8;
10153
+ // Below the field by default; flip above when there isn't room; else clamp into the viewport.
10154
+ let top = r.bottom + gap;
10155
+ if (top + ph > window.innerHeight - margin) {
10156
+ if (r.top - gap - ph > margin)
10157
+ top = r.top - gap - ph;
10158
+ else
10159
+ top = Math.max(margin, window.innerHeight - margin - ph);
10160
+ }
10161
+ let left = r.left;
10162
+ if (left + pw > window.innerWidth - margin) {
10163
+ left = Math.max(margin, window.innerWidth - margin - pw);
10164
+ }
10165
+ panelEl.style.top = `${Math.round(top)}px`;
10166
+ panelEl.style.left = `${Math.round(left)}px`;
10167
+ }
10168
+ ngOnDestroy() {
10169
+ this.removeReflowListeners();
10170
+ this.panelEl?.remove();
10171
+ }
10172
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsDateRangePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
10173
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsDateRangePicker, isStandalone: true, selector: "sds-date-range-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, presets: { classPropertyName: "presets", publicName: "presets", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, maxDays: { classPropertyName: "maxDays", publicName: "maxDays", isSignal: true, isRequired: false, transformFunction: null }, firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", 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 }, months: { classPropertyName: "months", publicName: "months", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, panelLabel: { classPropertyName: "panelLabel", publicName: "panelLabel", isSignal: true, isRequired: false, transformFunction: null }, presetsLabel: { classPropertyName: "presetsLabel", publicName: "presetsLabel", isSignal: true, isRequired: false, transformFunction: null }, startLabel: { classPropertyName: "startLabel", publicName: "startLabel", isSignal: true, isRequired: false, transformFunction: null }, endLabel: { classPropertyName: "endLabel", publicName: "endLabel", isSignal: true, isRequired: false, transformFunction: null }, applyLabel: { classPropertyName: "applyLabel", publicName: "applyLabel", isSignal: true, isRequired: false, transformFunction: null }, cancelLabel: { classPropertyName: "cancelLabel", publicName: "cancelLabel", isSignal: true, isRequired: false, transformFunction: null }, calendarLabel: { classPropertyName: "calendarLabel", publicName: "calendarLabel", isSignal: true, isRequired: false, transformFunction: null }, previousLabel: { classPropertyName: "previousLabel", publicName: "previousLabel", isSignal: true, isRequired: false, transformFunction: null }, nextLabel: { classPropertyName: "nextLabel", publicName: "nextLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", presetSelect: "presetSelect" }, host: { listeners: { "document:click": "onDocumentClick($event)", "keydown.escape": "onEscape($event)" }, classAttribute: "block" }, viewQueries: [{ propertyName: "calendar", first: true, predicate: SdsRangeCalendar, descendants: true, isSignal: true }, { propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true, isSignal: true }], ngImport: i0, template: `
10174
+ @if (label()) {
10175
+ <label [id]="labelId" [for]="fieldId" class="mb-1.5 block text-sm font-medium text-ink">
10176
+ {{ label() }}
10177
+ </label>
10178
+ }
10179
+
10180
+ <button
10181
+ #trigger
10182
+ type="button"
10183
+ [id]="fieldId"
10184
+ [disabled]="disabled()"
10185
+ aria-haspopup="dialog"
10186
+ [attr.aria-expanded]="open()"
10187
+ [attr.aria-labelledby]="label() ? labelId + ' ' + valueId : null"
10188
+ [attr.aria-label]="!label() && ariaLabel() ? ariaLabel() + ', ' + (displayValue() || placeholder()) : null"
10189
+ [class]="triggerClasses()"
10190
+ (click)="toggle($event)"
10191
+ (keydown.arrowdown)="onTriggerArrowDown($event)"
10192
+ >
10193
+ <sds-icon name="calendar" [size]="17" class="shrink-0 text-ink-soft" />
10194
+ <span
10195
+ [id]="valueId"
10196
+ class="min-w-0 flex-1 truncate"
10197
+ [class.text-ink]="!!displayValue()"
10198
+ [class.text-ink-faint]="!displayValue()"
10199
+ >
10200
+ {{ displayValue() || placeholder() }}
10201
+ </span>
10202
+ <span
10203
+ class="inline-flex shrink-0 text-ink-faint transition-transform"
10204
+ [class.rotate-180]="open()"
10205
+ aria-hidden="true"
10206
+ >
10207
+ <sds-icon name="chevron-down" [size]="16" />
10208
+ </span>
10209
+ </button>
10210
+
10211
+ @if (open()) {
10212
+ <!-- Relocated to document.body on open (appendTo body) to escape ancestor overflow/stacking;
10213
+ positioned via positionPanel(). -->
10214
+ <div
10215
+ class="fixed z-[1000] max-w-[calc(100vw-1rem)] overflow-auto rounded-card border border-border bg-surface shadow-lg"
10216
+ style="max-height: calc(100vh - 1rem)"
10217
+ role="dialog"
10218
+ aria-modal="false"
10219
+ [attr.aria-label]="panelLabel()"
10220
+ (keydown.escape)="onEscape($event)"
10221
+ (keydown)="onPanelKeydown($event)"
10222
+ >
10223
+ <div class="flex flex-col sm:flex-row">
10224
+ @if (presets().length) {
10225
+ <div
10226
+ class="border-b border-border p-2 sm:w-44 sm:shrink-0 sm:border-b-0 sm:border-r"
10227
+ role="group"
10228
+ [attr.aria-label]="presetsLabel()"
9094
10229
  >
9095
- <sds-icon name="x" [size]="15" />
9096
- </button>
10230
+ <p class="px-2 pb-1.5 pt-1 text-[11px] font-semibold uppercase tracking-wide text-ink-faint">
10231
+ {{ presetsLabel() }}
10232
+ </p>
10233
+ <div class="flex flex-wrap gap-1 sm:flex-col sm:flex-nowrap">
10234
+ @for (p of presets(); track p.id) {
10235
+ <button
10236
+ type="button"
10237
+ [attr.aria-pressed]="p.id === pendingPresetId()"
10238
+ [class]="presetClass(p.id === pendingPresetId())"
10239
+ (click)="choosePreset(p)"
10240
+ >
10241
+ {{ p.label }}
10242
+ </button>
10243
+ }
10244
+ </div>
10245
+ </div>
9097
10246
  }
9098
10247
 
9099
- <button
9100
- type="button"
9101
- tabindex="-1"
9102
- [attr.aria-label]="open() ? closeCalendarLabel() : openCalendarLabel()"
9103
- class="absolute right-2 inline-flex h-7 w-7 items-center justify-center rounded-control text-ink-soft hover:bg-muted"
9104
- (click)="toggle($event)"
9105
- >
9106
- <sds-icon name="calendar" [size]="18" />
9107
- </button>
9108
- </div>
10248
+ <div class="flex min-w-0 flex-col">
10249
+ <div class="p-3">
10250
+ <sds-range-calendar
10251
+ [(start)]="pendingStart"
10252
+ [(end)]="pendingEnd"
10253
+ [min]="min()"
10254
+ [max]="max()"
10255
+ [maxDays]="maxDays()"
10256
+ [firstDayOfWeek]="firstDayOfWeek()"
10257
+ [locale]="locale()"
10258
+ [months]="months()"
10259
+ [calendarLabel]="calendarLabel()"
10260
+ [previousLabel]="previousLabel()"
10261
+ [nextLabel]="nextLabel()"
10262
+ />
10263
+ </div>
9109
10264
 
9110
- @if (open()) {
9111
- <!-- Relocated to document.body on open (appendTo body) to escape
9112
- ancestor overflow/stacking; positioned via positionPanel(). -->
9113
- <div
9114
- class="fixed z-[1000]"
9115
- role="dialog"
9116
- aria-modal="false"
9117
- [attr.aria-label]="panelLabel()"
9118
- (keydown.escape)="onEscape($event)"
9119
- (keydown)="onPanelKeydown($event)"
9120
- >
9121
- <sds-calendar
9122
- [value]="value()"
9123
- [min]="min()"
9124
- [max]="max()"
9125
- [firstDayOfWeek]="firstDayOfWeek()"
9126
- [locale]="locale()"
9127
- [showActions]="showActions()"
9128
- [showTime]="showTime()"
9129
- [showDone]="showTime()"
9130
- [timeLabel]="timeLabel()"
9131
- [hoursLabel]="hoursLabel()"
9132
- [minutesLabel]="minutesLabel()"
9133
- [doneLabel]="doneLabel()"
9134
- [calendarLabel]="calendarLabel()"
9135
- [previousLabel]="previousLabel()"
9136
- [nextLabel]="nextLabel()"
9137
- [todayActionLabel]="todayActionLabel()"
9138
- [clearActionLabel]="clearActionLabel()"
9139
- (valueChange)="onCalendarValue($event)"
9140
- (dateSelect)="onDateSelect()"
9141
- (done)="close(true)"
9142
- />
10265
+ <div
10266
+ class="flex flex-wrap items-center justify-between gap-x-4 gap-y-2 border-t border-border px-3 py-2.5"
10267
+ >
10268
+ <p class="flex items-stretch gap-2 text-sm" aria-live="polite">
10269
+ <span class="flex flex-col leading-tight">
10270
+ <span class="text-[11px] font-medium uppercase text-ink-faint">{{ startLabel() }}</span>
10271
+ <span class="font-medium" [class.text-ink]="pendingStart()" [class.text-ink-faint]="!pendingStart()">
10272
+ {{ pendingStartText() }}
10273
+ </span>
10274
+ </span>
10275
+ <span class="self-end text-ink-faint" aria-hidden="true">–</span>
10276
+ <span class="flex flex-col leading-tight">
10277
+ <span class="text-[11px] font-medium uppercase text-ink-faint">{{ endLabel() }}</span>
10278
+ <span class="font-medium" [class.text-ink]="pendingEnd()" [class.text-ink-faint]="!pendingEnd()">
10279
+ {{ pendingEndText() }}
10280
+ </span>
10281
+ </span>
10282
+ </p>
10283
+ <div class="ml-auto flex items-center gap-2">
10284
+ <button type="button" [class]="cancelBtn" (click)="close(true)">
10285
+ {{ cancelLabel() }}
10286
+ </button>
10287
+ <button type="button" [class]="applyBtn" [disabled]="!canApply()" (click)="apply()">
10288
+ {{ applyLabel() }}
10289
+ </button>
10290
+ </div>
10291
+ </div>
9143
10292
  </div>
9144
- }
10293
+ </div>
9145
10294
  </div>
9146
10295
  }
9147
-
9148
- @if (hint() && !error()) {
9149
- <p class="mt-1.5 text-xs text-ink-faint">{{ hint() }}</p>
9150
- }
9151
- @if (error()) {
9152
- <p class="mt-1.5 text-xs font-medium text-danger">{{ error() }}</p>
9153
- }
9154
- `, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth", "filled"] }, { kind: "component", type: SdsCalendar, selector: "sds-calendar", inputs: ["value", "min", "max", "firstDayOfWeek", "locale", "showActions", "showTime", "showDone", "timeLabel", "hoursLabel", "minutesLabel", "doneLabel", "calendarLabel", "previousLabel", "nextLabel", "todayActionLabel", "clearActionLabel"], outputs: ["valueChange", "dateSelect", "done"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10296
+ `, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth", "filled"] }, { kind: "component", type: SdsRangeCalendar, selector: "sds-range-calendar", inputs: ["start", "end", "min", "max", "maxDays", "firstDayOfWeek", "locale", "months", "calendarLabel", "previousLabel", "nextLabel"], outputs: ["startChange", "endChange", "daySelect", "rangeSelect", "dayHover"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9155
10297
  }
9156
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsDatePicker, decorators: [{
10298
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsDateRangePicker, decorators: [{
9157
10299
  type: Component,
9158
10300
  args: [{
9159
- selector: 'sds-date-picker',
10301
+ selector: 'sds-date-range-picker',
9160
10302
  changeDetection: ChangeDetectionStrategy.OnPush,
9161
- imports: [SdsIcon, SdsCalendar],
9162
- providers: [
9163
- { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SdsDatePicker), multi: true },
9164
- ],
10303
+ imports: [SdsIcon, SdsRangeCalendar],
9165
10304
  host: {
9166
10305
  class: 'block',
9167
10306
  '(document:click)': 'onDocumentClick($event)',
@@ -9169,126 +10308,130 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
9169
10308
  },
9170
10309
  template: `
9171
10310
  @if (label()) {
9172
- <label [for]="fieldId" class="mb-1.5 block text-sm font-medium text-ink">
10311
+ <label [id]="labelId" [for]="fieldId" class="mb-1.5 block text-sm font-medium text-ink">
9173
10312
  {{ label() }}
9174
10313
  </label>
9175
10314
  }
9176
10315
 
9177
- @if (inline()) {
9178
- <sds-calendar
9179
- [value]="value()"
9180
- [min]="min()"
9181
- [max]="max()"
9182
- [firstDayOfWeek]="firstDayOfWeek()"
9183
- [locale]="locale()"
9184
- [showActions]="showActions()"
9185
- [showTime]="showTime()"
9186
- [timeLabel]="timeLabel()"
9187
- [hoursLabel]="hoursLabel()"
9188
- [minutesLabel]="minutesLabel()"
9189
- [calendarLabel]="calendarLabel()"
9190
- [previousLabel]="previousLabel()"
9191
- [nextLabel]="nextLabel()"
9192
- [todayActionLabel]="todayActionLabel()"
9193
- [clearActionLabel]="clearActionLabel()"
9194
- (valueChange)="onCalendarValue($event)"
9195
- />
9196
- } @else {
9197
- <div class="relative">
9198
- <div
9199
- #trigger
9200
- class="relative flex items-center rounded-control border bg-surface transition-colors"
9201
- [class]="wrapperClasses()"
9202
- (click)="toggle()"
9203
- >
9204
- <input
9205
- [id]="fieldId"
9206
- type="text"
9207
- readonly
9208
- [value]="displayValue()"
9209
- [placeholder]="placeholder()"
9210
- [disabled]="isDisabled()"
9211
- [attr.aria-haspopup]="'dialog'"
9212
- [attr.aria-expanded]="open()"
9213
- [attr.aria-label]="!label() && ariaLabel() ? ariaLabel() : null"
9214
- (keydown.enter)="onTriggerKeydown($event)"
9215
- (keydown.space)="onTriggerKeydown($event)"
9216
- (keydown.arrowdown)="onTriggerKeydown($event)"
9217
- class="w-full cursor-pointer bg-transparent pl-3.5 text-ink outline-none placeholder:text-ink-faint disabled:cursor-not-allowed"
9218
- [class.pr-20]="showClear()"
9219
- [class.pr-11]="!showClear()"
9220
- />
10316
+ <button
10317
+ #trigger
10318
+ type="button"
10319
+ [id]="fieldId"
10320
+ [disabled]="disabled()"
10321
+ aria-haspopup="dialog"
10322
+ [attr.aria-expanded]="open()"
10323
+ [attr.aria-labelledby]="label() ? labelId + ' ' + valueId : null"
10324
+ [attr.aria-label]="!label() && ariaLabel() ? ariaLabel() + ', ' + (displayValue() || placeholder()) : null"
10325
+ [class]="triggerClasses()"
10326
+ (click)="toggle($event)"
10327
+ (keydown.arrowdown)="onTriggerArrowDown($event)"
10328
+ >
10329
+ <sds-icon name="calendar" [size]="17" class="shrink-0 text-ink-soft" />
10330
+ <span
10331
+ [id]="valueId"
10332
+ class="min-w-0 flex-1 truncate"
10333
+ [class.text-ink]="!!displayValue()"
10334
+ [class.text-ink-faint]="!displayValue()"
10335
+ >
10336
+ {{ displayValue() || placeholder() }}
10337
+ </span>
10338
+ <span
10339
+ class="inline-flex shrink-0 text-ink-faint transition-transform"
10340
+ [class.rotate-180]="open()"
10341
+ aria-hidden="true"
10342
+ >
10343
+ <sds-icon name="chevron-down" [size]="16" />
10344
+ </span>
10345
+ </button>
9221
10346
 
9222
- @if (showClear()) {
9223
- <button
9224
- type="button"
9225
- tabindex="-1"
9226
- [attr.aria-label]="clearLabel()"
9227
- class="absolute right-10 inline-flex h-6 w-6 items-center justify-center rounded-full text-ink-faint hover:bg-black/5 hover:text-ink-soft"
9228
- (click)="clear($event)"
10347
+ @if (open()) {
10348
+ <!-- Relocated to document.body on open (appendTo body) to escape ancestor overflow/stacking;
10349
+ positioned via positionPanel(). -->
10350
+ <div
10351
+ class="fixed z-[1000] max-w-[calc(100vw-1rem)] overflow-auto rounded-card border border-border bg-surface shadow-lg"
10352
+ style="max-height: calc(100vh - 1rem)"
10353
+ role="dialog"
10354
+ aria-modal="false"
10355
+ [attr.aria-label]="panelLabel()"
10356
+ (keydown.escape)="onEscape($event)"
10357
+ (keydown)="onPanelKeydown($event)"
10358
+ >
10359
+ <div class="flex flex-col sm:flex-row">
10360
+ @if (presets().length) {
10361
+ <div
10362
+ class="border-b border-border p-2 sm:w-44 sm:shrink-0 sm:border-b-0 sm:border-r"
10363
+ role="group"
10364
+ [attr.aria-label]="presetsLabel()"
9229
10365
  >
9230
- <sds-icon name="x" [size]="15" />
9231
- </button>
10366
+ <p class="px-2 pb-1.5 pt-1 text-[11px] font-semibold uppercase tracking-wide text-ink-faint">
10367
+ {{ presetsLabel() }}
10368
+ </p>
10369
+ <div class="flex flex-wrap gap-1 sm:flex-col sm:flex-nowrap">
10370
+ @for (p of presets(); track p.id) {
10371
+ <button
10372
+ type="button"
10373
+ [attr.aria-pressed]="p.id === pendingPresetId()"
10374
+ [class]="presetClass(p.id === pendingPresetId())"
10375
+ (click)="choosePreset(p)"
10376
+ >
10377
+ {{ p.label }}
10378
+ </button>
10379
+ }
10380
+ </div>
10381
+ </div>
9232
10382
  }
9233
10383
 
9234
- <button
9235
- type="button"
9236
- tabindex="-1"
9237
- [attr.aria-label]="open() ? closeCalendarLabel() : openCalendarLabel()"
9238
- class="absolute right-2 inline-flex h-7 w-7 items-center justify-center rounded-control text-ink-soft hover:bg-muted"
9239
- (click)="toggle($event)"
9240
- >
9241
- <sds-icon name="calendar" [size]="18" />
9242
- </button>
9243
- </div>
10384
+ <div class="flex min-w-0 flex-col">
10385
+ <div class="p-3">
10386
+ <sds-range-calendar
10387
+ [(start)]="pendingStart"
10388
+ [(end)]="pendingEnd"
10389
+ [min]="min()"
10390
+ [max]="max()"
10391
+ [maxDays]="maxDays()"
10392
+ [firstDayOfWeek]="firstDayOfWeek()"
10393
+ [locale]="locale()"
10394
+ [months]="months()"
10395
+ [calendarLabel]="calendarLabel()"
10396
+ [previousLabel]="previousLabel()"
10397
+ [nextLabel]="nextLabel()"
10398
+ />
10399
+ </div>
9244
10400
 
9245
- @if (open()) {
9246
- <!-- Relocated to document.body on open (appendTo body) to escape
9247
- ancestor overflow/stacking; positioned via positionPanel(). -->
9248
- <div
9249
- class="fixed z-[1000]"
9250
- role="dialog"
9251
- aria-modal="false"
9252
- [attr.aria-label]="panelLabel()"
9253
- (keydown.escape)="onEscape($event)"
9254
- (keydown)="onPanelKeydown($event)"
9255
- >
9256
- <sds-calendar
9257
- [value]="value()"
9258
- [min]="min()"
9259
- [max]="max()"
9260
- [firstDayOfWeek]="firstDayOfWeek()"
9261
- [locale]="locale()"
9262
- [showActions]="showActions()"
9263
- [showTime]="showTime()"
9264
- [showDone]="showTime()"
9265
- [timeLabel]="timeLabel()"
9266
- [hoursLabel]="hoursLabel()"
9267
- [minutesLabel]="minutesLabel()"
9268
- [doneLabel]="doneLabel()"
9269
- [calendarLabel]="calendarLabel()"
9270
- [previousLabel]="previousLabel()"
9271
- [nextLabel]="nextLabel()"
9272
- [todayActionLabel]="todayActionLabel()"
9273
- [clearActionLabel]="clearActionLabel()"
9274
- (valueChange)="onCalendarValue($event)"
9275
- (dateSelect)="onDateSelect()"
9276
- (done)="close(true)"
9277
- />
10401
+ <div
10402
+ class="flex flex-wrap items-center justify-between gap-x-4 gap-y-2 border-t border-border px-3 py-2.5"
10403
+ >
10404
+ <p class="flex items-stretch gap-2 text-sm" aria-live="polite">
10405
+ <span class="flex flex-col leading-tight">
10406
+ <span class="text-[11px] font-medium uppercase text-ink-faint">{{ startLabel() }}</span>
10407
+ <span class="font-medium" [class.text-ink]="pendingStart()" [class.text-ink-faint]="!pendingStart()">
10408
+ {{ pendingStartText() }}
10409
+ </span>
10410
+ </span>
10411
+ <span class="self-end text-ink-faint" aria-hidden="true">–</span>
10412
+ <span class="flex flex-col leading-tight">
10413
+ <span class="text-[11px] font-medium uppercase text-ink-faint">{{ endLabel() }}</span>
10414
+ <span class="font-medium" [class.text-ink]="pendingEnd()" [class.text-ink-faint]="!pendingEnd()">
10415
+ {{ pendingEndText() }}
10416
+ </span>
10417
+ </span>
10418
+ </p>
10419
+ <div class="ml-auto flex items-center gap-2">
10420
+ <button type="button" [class]="cancelBtn" (click)="close(true)">
10421
+ {{ cancelLabel() }}
10422
+ </button>
10423
+ <button type="button" [class]="applyBtn" [disabled]="!canApply()" (click)="apply()">
10424
+ {{ applyLabel() }}
10425
+ </button>
10426
+ </div>
10427
+ </div>
9278
10428
  </div>
9279
- }
10429
+ </div>
9280
10430
  </div>
9281
10431
  }
9282
-
9283
- @if (hint() && !error()) {
9284
- <p class="mt-1.5 text-xs text-ink-faint">{{ hint() }}</p>
9285
- }
9286
- @if (error()) {
9287
- <p class="mt-1.5 text-xs font-medium text-danger">{{ error() }}</p>
9288
- }
9289
10432
  `,
9290
10433
  }]
9291
- }], 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 }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], firstDayOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstDayOfWeek", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], inline: [{ type: i0.Input, args: [{ isSignal: true, alias: "inline", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], showActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActions", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTime", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], timeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeLabel", required: false }] }], hoursLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoursLabel", required: false }] }], minutesLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "minutesLabel", required: false }] }], doneLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "doneLabel", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], openCalendarLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "openCalendarLabel", required: false }] }], closeCalendarLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeCalendarLabel", required: false }] }], panelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelLabel", required: false }] }], calendarLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "calendarLabel", required: false }] }], previousLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], todayActionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "todayActionLabel", required: false }] }], clearActionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearActionLabel", required: false }] }], calendar: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SdsCalendar), { isSignal: true }] }], trigger: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }] } });
10434
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], presets: [{ type: i0.Input, args: [{ isSignal: true, alias: "presets", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], maxDays: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDays", required: false }] }], firstDayOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstDayOfWeek", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], months: [{ type: i0.Input, args: [{ isSignal: true, alias: "months", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], 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 }] }], panelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelLabel", required: false }] }], presetsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "presetsLabel", required: false }] }], startLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "startLabel", required: false }] }], endLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "endLabel", required: false }] }], applyLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "applyLabel", required: false }] }], cancelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelLabel", required: false }] }], calendarLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "calendarLabel", required: false }] }], previousLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], presetSelect: [{ type: i0.Output, args: ["presetSelect"] }], calendar: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SdsRangeCalendar), { isSignal: true }] }], trigger: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }] } });
9292
10435
  let nextId$8 = 0;
9293
10436
 
9294
10437
  /**
@@ -18885,12 +20028,62 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
18885
20028
  }]
18886
20029
  }], propDecorators: { env: [{ type: i0.Input, args: [{ isSignal: true, alias: "env", required: false }] }], devSrLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "devSrLabel", required: false }] }], testSrLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "testSrLabel", required: false }] }] } });
18887
20030
 
20031
+ /** English defaults of {@link SdsChartLabels}. Spread it to override a few keys. */
20032
+ const SDS_CHART_LABELS_EN = {
20033
+ legend: 'Legend',
20034
+ showTable: 'Table',
20035
+ showChart: 'Chart',
20036
+ empty: 'No data to display',
20037
+ noData: 'No data',
20038
+ chartRole: 'chart',
20039
+ heatmapRole: 'heatmap',
20040
+ lineChart: 'Line chart',
20041
+ barChart: 'Bar chart',
20042
+ donutChart: 'Donut chart',
20043
+ scatterChart: 'Scatter plot',
20044
+ heatmap: 'Heatmap',
20045
+ treemap: 'Treemap',
20046
+ breadcrumb: 'Breadcrumb',
20047
+ trend: 'Trend',
20048
+ category: 'Category',
20049
+ date: 'Date',
20050
+ value: 'Value',
20051
+ share: 'Share',
20052
+ item: 'Item',
20053
+ group: 'Group',
20054
+ size: 'Size',
20055
+ total: 'Total',
20056
+ other: 'Other',
20057
+ all: 'All',
20058
+ shareOfTotal: 'Share of total',
20059
+ shareOf: (parent) => `Share of ${parent}`,
20060
+ labelValue: (label, value) => `${label}: ${value}`,
20061
+ sliceCount: (n) => `${n} ${n === 1 ? 'slice' : 'slices'}`,
20062
+ totalOf: (value) => `total ${value}`,
20063
+ gridSize: (rows, columns) => `${rows} ${rows === 1 ? 'row' : 'rows'} × ${columns} ${columns === 1 ? 'column' : 'columns'}`,
20064
+ itemCount: (n) => `${n} ${n === 1 ? 'item' : 'items'}`,
20065
+ zoomedTo: (path) => `zoomed to ${path}`,
20066
+ versus: (y, x) => `${y} vs ${x}`,
20067
+ sparklineSummary: (s) => `${s.label || 'Trend'}: from ${s.first} to ${s.last} — min ${s.min}, max ${s.max}, ${s.count} ${s.count === 1 ? 'point' : 'points'}`,
20068
+ kpiChange: ({ direction, delta, period, good }) => {
20069
+ const p = period ? ` ${period}` : '';
20070
+ if (direction === 'flat')
20071
+ return `unchanged${p}`;
20072
+ const verdict = good === null ? '' : good ? ', favourable' : ', unfavourable';
20073
+ return `${direction === 'up' ? 'up' : 'down'} ${delta}${p}${verdict}`;
20074
+ },
20075
+ };
18888
20076
  const SDS_CHART_CONFIG = new InjectionToken('SDS_CHART_CONFIG');
18889
20077
  /**
18890
20078
  * App-wide chart defaults.
18891
20079
  *
18892
20080
  * @example
18893
- * providers: [provideSdsCharts({ locale: () => translate.currentLang() })]
20081
+ * providers: [
20082
+ * provideSdsCharts({
20083
+ * locale: () => translate.currentLang(),
20084
+ * labels: () => (translate.currentLang() === 'fr' ? CHART_LABELS_FR : {}),
20085
+ * }),
20086
+ * ]
18894
20087
  */
18895
20088
  function provideSdsCharts(config) {
18896
20089
  return { provide: SDS_CHART_CONFIG, useValue: config };
@@ -19040,7 +20233,7 @@ const INK_HI = 'var(--sds-chart-ink-hi, #ffffff)';
19040
20233
  // ---------------------------------------------------------------------------------------------------
19041
20234
  // Locale & formatting
19042
20235
  // ---------------------------------------------------------------------------------------------------
19043
- /** Resolve the chart locale: explicit input → provideSdsCharts() → Angular LOCALE_ID. */
20236
+ /** Resolve the chart locale: explicit input → provideSdsCharts() → Angular LOCALE_ID → en-GB. */
19044
20237
  function chartLocale(explicit) {
19045
20238
  const config = inject(SDS_CHART_CONFIG, { optional: true });
19046
20239
  const ngLocale = inject(LOCALE_ID);
@@ -19050,7 +20243,19 @@ function chartLocale(explicit) {
19050
20243
  return own;
19051
20244
  const cfg = config?.locale;
19052
20245
  const resolved = typeof cfg === 'function' ? cfg() : cfg;
19053
- return resolved || ngLocale || 'fr-FR';
20246
+ return resolved || ngLocale || 'en-GB';
20247
+ });
20248
+ }
20249
+ /**
20250
+ * The chart strings: provideSdsCharts({ labels }) merged over the English defaults. The function
20251
+ * form is called inside the computed, so labels returned from a signal switch language live.
20252
+ */
20253
+ function chartLabels() {
20254
+ const config = inject(SDS_CHART_CONFIG, { optional: true });
20255
+ return computed(() => {
20256
+ const own = config?.labels;
20257
+ const resolved = typeof own === 'function' ? own() : own;
20258
+ return resolved ? { ...SDS_CHART_LABELS_EN, ...resolved } : SDS_CHART_LABELS_EN;
19054
20259
  });
19055
20260
  }
19056
20261
  function chartAnimates(explicit) {
@@ -19512,18 +20717,18 @@ class SdsChartFrame {
19512
20717
  subtitle = input('', ...(ngDevMode ? [{ debugName: "subtitle" }] : []));
19513
20718
  items = input([], ...(ngDevMode ? [{ debugName: "items" }] : []));
19514
20719
  legend = input('top', ...(ngDevMode ? [{ debugName: "legend" }] : []));
19515
- legendLabel = input('Légende', ...(ngDevMode ? [{ debugName: "legendLabel" }] : []));
19516
20720
  hidden = input([], ...(ngDevMode ? [{ debugName: "hidden" }] : []));
19517
20721
  interactive = input(true, ...(ngDevMode ? [{ debugName: "interactive", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
19518
20722
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
19519
20723
  empty = input(false, ...(ngDevMode ? [{ debugName: "empty", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
19520
- emptyText = input('Aucune donnée à afficher', ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
20724
+ /** Empty-state text; '' falls back to the configured labels. */
20725
+ emptyText = input(null, ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
19521
20726
  height = input(300, ...(ngDevMode ? [{ debugName: "height" }] : []));
19522
20727
  table = input({ columns: [], rows: [] }, ...(ngDevMode ? [{ debugName: "table" }] : []));
19523
20728
  tableCaption = input('', ...(ngDevMode ? [{ debugName: "tableCaption" }] : []));
19524
20729
  tableToggle = input(false, ...(ngDevMode ? [{ debugName: "tableToggle", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
19525
- tableLabel = input('Tableau', ...(ngDevMode ? [{ debugName: "tableLabel" }] : []));
19526
- chartLabel = input('Graphique', ...(ngDevMode ? [{ debugName: "chartLabel" }] : []));
20730
+ /** Built-in strings (provideSdsCharts labels over the English defaults). */
20731
+ text = chartLabels();
19527
20732
  toggle = output();
19528
20733
  focusKey = output();
19529
20734
  showTable = signal(false, ...(ngDevMode ? [{ debugName: "showTable" }] : []));
@@ -19540,7 +20745,7 @@ class SdsChartFrame {
19540
20745
  this.toggle.emit(key);
19541
20746
  }
19542
20747
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsChartFrame, deps: [], target: i0.ɵɵFactoryTarget.Component });
19543
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsChartFrame, isStandalone: true, selector: "sds-chart-frame", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, legend: { classPropertyName: "legend", publicName: "legend", isSignal: true, isRequired: false, transformFunction: null }, legendLabel: { classPropertyName: "legendLabel", publicName: "legendLabel", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, empty: { classPropertyName: "empty", publicName: "empty", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, table: { classPropertyName: "table", publicName: "table", isSignal: true, isRequired: false, transformFunction: null }, tableCaption: { classPropertyName: "tableCaption", publicName: "tableCaption", isSignal: true, isRequired: false, transformFunction: null }, tableToggle: { classPropertyName: "tableToggle", publicName: "tableToggle", isSignal: true, isRequired: false, transformFunction: null }, tableLabel: { classPropertyName: "tableLabel", publicName: "tableLabel", isSignal: true, isRequired: false, transformFunction: null }, chartLabel: { classPropertyName: "chartLabel", publicName: "chartLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggle: "toggle", focusKey: "focusKey" }, host: { classAttribute: "block @container" }, ngImport: i0, template: `
20748
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsChartFrame, isStandalone: true, selector: "sds-chart-frame", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, legend: { classPropertyName: "legend", publicName: "legend", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, empty: { classPropertyName: "empty", publicName: "empty", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, table: { classPropertyName: "table", publicName: "table", isSignal: true, isRequired: false, transformFunction: null }, tableCaption: { classPropertyName: "tableCaption", publicName: "tableCaption", isSignal: true, isRequired: false, transformFunction: null }, tableToggle: { classPropertyName: "tableToggle", publicName: "tableToggle", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggle: "toggle", focusKey: "focusKey" }, host: { classAttribute: "block @container" }, ngImport: i0, template: `
19544
20749
  <figure class="m-0 flex flex-col gap-3" [attr.aria-busy]="loading() || null">
19545
20750
  @if (heading() || subtitle() || tableToggle()) {
19546
20751
  <figcaption class="flex items-start justify-between gap-3">
@@ -19559,7 +20764,7 @@ class SdsChartFrame {
19559
20764
  (click)="showTable.set(!showTable())"
19560
20765
  >
19561
20766
  <sds-icon [name]="showTable() ? 'chart-column' : 'table'" [size]="14" />
19562
- {{ showTable() ? chartLabel() : tableLabel() }}
20767
+ {{ showTable() ? text().showChart : text().showTable }}
19563
20768
  </button>
19564
20769
  }
19565
20770
  </figcaption>
@@ -19577,7 +20782,7 @@ class SdsChartFrame {
19577
20782
  >
19578
20783
  <div class="flex flex-col items-center gap-2 px-4 text-ink-faint">
19579
20784
  <sds-icon name="bar-chart-3" [size]="22" />
19580
- <p class="text-sm">{{ emptyText() }}</p>
20785
+ <p class="text-sm">{{ emptyText() || text().empty }}</p>
19581
20786
  </div>
19582
20787
  </div>
19583
20788
  } @else if (showTable()) {
@@ -19608,7 +20813,7 @@ class SdsChartFrame {
19608
20813
  class="flex flex-wrap gap-x-4 gap-y-1.5"
19609
20814
  [class.@lg:flex-col]="legend() === 'right'"
19610
20815
  [class.@lg:min-w-44]="legend() === 'right'"
19611
- [attr.aria-label]="legendLabel()"
20816
+ [attr.aria-label]="text().legend"
19612
20817
  >
19613
20818
  @for (item of items(); track item.key) {
19614
20819
  <li>
@@ -19723,7 +20928,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
19723
20928
  (click)="showTable.set(!showTable())"
19724
20929
  >
19725
20930
  <sds-icon [name]="showTable() ? 'chart-column' : 'table'" [size]="14" />
19726
- {{ showTable() ? chartLabel() : tableLabel() }}
20931
+ {{ showTable() ? text().showChart : text().showTable }}
19727
20932
  </button>
19728
20933
  }
19729
20934
  </figcaption>
@@ -19741,7 +20946,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
19741
20946
  >
19742
20947
  <div class="flex flex-col items-center gap-2 px-4 text-ink-faint">
19743
20948
  <sds-icon name="bar-chart-3" [size]="22" />
19744
- <p class="text-sm">{{ emptyText() }}</p>
20949
+ <p class="text-sm">{{ emptyText() || text().empty }}</p>
19745
20950
  </div>
19746
20951
  </div>
19747
20952
  } @else if (showTable()) {
@@ -19772,7 +20977,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
19772
20977
  class="flex flex-wrap gap-x-4 gap-y-1.5"
19773
20978
  [class.@lg:flex-col]="legend() === 'right'"
19774
20979
  [class.@lg:min-w-44]="legend() === 'right'"
19775
- [attr.aria-label]="legendLabel()"
20980
+ [attr.aria-label]="text().legend"
19776
20981
  >
19777
20982
  @for (item of items(); track item.key) {
19778
20983
  <li>
@@ -19865,7 +21070,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
19865
21070
  </table>
19866
21071
  </ng-template>
19867
21072
  `, styles: [".sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
19868
- }], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], legend: [{ type: i0.Input, args: [{ isSignal: true, alias: "legend", required: false }] }], legendLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "legendLabel", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], empty: [{ type: i0.Input, args: [{ isSignal: true, alias: "empty", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], table: [{ type: i0.Input, args: [{ isSignal: true, alias: "table", required: false }] }], tableCaption: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableCaption", required: false }] }], tableToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableToggle", required: false }] }], tableLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableLabel", required: false }] }], chartLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "chartLabel", required: false }] }], toggle: [{ type: i0.Output, args: ["toggle"] }], focusKey: [{ type: i0.Output, args: ["focusKey"] }] } });
21073
+ }], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], legend: [{ type: i0.Input, args: [{ isSignal: true, alias: "legend", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], empty: [{ type: i0.Input, args: [{ isSignal: true, alias: "empty", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], table: [{ type: i0.Input, args: [{ isSignal: true, alias: "table", required: false }] }], tableCaption: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableCaption", required: false }] }], tableToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableToggle", required: false }] }], toggle: [{ type: i0.Output, args: ["toggle"] }], focusKey: [{ type: i0.Output, args: ["focusKey"] }] } });
19869
21074
  /** The floating tooltip: default rows, or the consumer's `sdsChartTooltip` template. */
19870
21075
  class SdsChartTooltipBox {
19871
21076
  ctx = input(null, ...(ngDevMode ? [{ debugName: "ctx" }] : []));
@@ -20223,7 +21428,8 @@ class SdsLineChart {
20223
21428
  animate = input(true, ...(ngDevMode ? [{ debugName: "animate", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
20224
21429
  /** Hold the current render at reduced opacity while new data loads (no skeleton flash). */
20225
21430
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
20226
- emptyText = input('Aucune donnée à afficher', ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
21431
+ /** Empty-state text. Default (`null`): the configured `labels.empty` ("No data to display"). */
21432
+ emptyText = input(null, ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
20227
21433
  /** Show a button that swaps the plot for its data table. */
20228
21434
  tableToggle = input(false, ...(ngDevMode ? [{ debugName: "tableToggle", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
20229
21435
  ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
@@ -20245,6 +21451,8 @@ class SdsLineChart {
20245
21451
  pointerY = signal(0, ...(ngDevMode ? [{ debugName: "pointerY" }] : []));
20246
21452
  width = hostWidth();
20247
21453
  loc = chartLocale(() => this.locale());
21454
+ /** Built-in strings (provideSdsCharts labels over the English defaults). */
21455
+ text = chartLabels();
20248
21456
  animates = chartAnimates(() => this.animate());
20249
21457
  palette = chartPalette();
20250
21458
  stacked = computed(() => this.stack() !== 'none', ...(ngDevMode ? [{ debugName: "stacked" }] : []));
@@ -20700,7 +21908,7 @@ class SdsLineChart {
20700
21908
  return { key: s.key, label: s.label, value, raw, color: s.color, shape: 'line' };
20701
21909
  });
20702
21910
  const fmt = this.valueFormat();
20703
- const footer = this.stacked() && vis.length > 1 ? `Total · ${valueFormatter(this.loc(), fmt)(total)}` : undefined;
21911
+ const footer = this.stacked() && vis.length > 1 ? `${this.text().total} · ${valueFormatter(this.loc(), fmt)(total)}` : undefined;
20704
21912
  return { title: this.fmtX()(p.x), rows, footer, data: [p.row], index: p.index };
20705
21913
  }, ...(ngDevMode ? [{ debugName: "tooltipCtx" }] : []));
20706
21914
  liveText = computed(() => {
@@ -20709,7 +21917,7 @@ class SdsLineChart {
20709
21917
  const c = this.tooltipCtx();
20710
21918
  if (!c)
20711
21919
  return '';
20712
- return `${c.title} — ${c.rows.map((r) => `${r.label} : ${r.value}`).join(', ')}${c.footer ? `, ${c.footer}` : ''}`;
21920
+ return `${c.title} — ${c.rows.map((r) => this.text().labelValue(r.label, r.value)).join(', ')}${c.footer ? `, ${c.footer}` : ''}`;
20713
21921
  }, ...(ngDevMode ? [{ debugName: "liveText" }] : []));
20714
21922
  onPointer(event) {
20715
21923
  const L = this.layout();
@@ -20832,7 +22040,7 @@ class SdsLineChart {
20832
22040
  if (this.ariaLabel())
20833
22041
  return this.ariaLabel();
20834
22042
  const names = this.allSeries().map((s) => s.label).join(', ');
20835
- return [this.heading() || 'Graphique linéaire', names].filter(Boolean).join(' — ');
22043
+ return [this.heading() || this.text().lineChart, names].filter(Boolean).join(' — ');
20836
22044
  }, ...(ngDevMode ? [{ debugName: "label" }] : []));
20837
22045
  table = computed(() => {
20838
22046
  const { pts } = this.model();
@@ -20840,7 +22048,7 @@ class SdsLineChart {
20840
22048
  const values = this.values();
20841
22049
  const fx = this.fmtX();
20842
22050
  return {
20843
- columns: [this.xLabel() || (this.model().kind === 'time' ? 'Date' : 'Catégorie'), ...vis.map((s) => s.label)],
22051
+ columns: [this.xLabel() || (this.model().kind === 'time' ? this.text().date : this.text().category), ...vis.map((s) => s.label)],
20844
22052
  rows: pts.map((p, i) => [
20845
22053
  fx(p.x),
20846
22054
  ...vis.map((s) => {
@@ -20873,7 +22081,7 @@ class SdsLineChart {
20873
22081
  [style.height.px]="height()"
20874
22082
  tabindex="0"
20875
22083
  role="group"
20876
- aria-roledescription="graphique"
22084
+ [attr.aria-roledescription]="text().chartRole"
20877
22085
  [attr.aria-label]="label()"
20878
22086
  (keydown)="onKey($event)"
20879
22087
  (blur)="onBlur()"
@@ -20995,7 +22203,7 @@ class SdsLineChart {
20995
22203
  <div class="sr-only" aria-live="polite" aria-atomic="true">{{ liveText() }}</div>
20996
22204
  </div>
20997
22205
  </sds-chart-frame>
20998
- `, isInline: true, styles: [".sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: SdsChartFrame, selector: "sds-chart-frame", inputs: ["heading", "subtitle", "items", "legend", "legendLabel", "hidden", "interactive", "loading", "empty", "emptyText", "height", "table", "tableCaption", "tableToggle", "tableLabel", "chartLabel"], outputs: ["toggle", "focusKey"] }, { kind: "component", type: SdsChartAxes, selector: "g[sdsChartAxes]", inputs: ["plot", "xTicks", "yTicks", "grid", "baselineY", "baselineX", "xTitle", "yTitle", "width"] }, { kind: "component", type: SdsChartAnnotations, selector: "g[sdsChartAnnotations]", inputs: ["items", "layer"] }, { kind: "component", type: SdsChartTooltipBox, selector: "sds-chart-tooltip-box", inputs: ["ctx", "template", "x", "y", "containerWidth", "containerHeight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
22206
+ `, isInline: true, styles: [".sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: SdsChartFrame, selector: "sds-chart-frame", inputs: ["heading", "subtitle", "items", "legend", "hidden", "interactive", "loading", "empty", "emptyText", "height", "table", "tableCaption", "tableToggle"], outputs: ["toggle", "focusKey"] }, { kind: "component", type: SdsChartAxes, selector: "g[sdsChartAxes]", inputs: ["plot", "xTicks", "yTicks", "grid", "baselineY", "baselineX", "xTitle", "yTitle", "width"] }, { kind: "component", type: SdsChartAnnotations, selector: "g[sdsChartAnnotations]", inputs: ["items", "layer"] }, { kind: "component", type: SdsChartTooltipBox, selector: "sds-chart-tooltip-box", inputs: ["ctx", "template", "x", "y", "containerWidth", "containerHeight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
20999
22207
  }
21000
22208
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsLineChart, decorators: [{
21001
22209
  type: Component,
@@ -21021,7 +22229,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
21021
22229
  [style.height.px]="height()"
21022
22230
  tabindex="0"
21023
22231
  role="group"
21024
- aria-roledescription="graphique"
22232
+ [attr.aria-roledescription]="text().chartRole"
21025
22233
  [attr.aria-label]="label()"
21026
22234
  (keydown)="onKey($event)"
21027
22235
  (blur)="onBlur()"
@@ -21206,7 +22414,8 @@ class SdsBarChart {
21206
22414
  tooltip = input(true, ...(ngDevMode ? [{ debugName: "tooltip", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
21207
22415
  animate = input(true, ...(ngDevMode ? [{ debugName: "animate", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
21208
22416
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
21209
- emptyText = input('Aucune donnée à afficher', ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
22417
+ /** Empty-state text. Default (`null`): the configured `labels.empty` ("No data to display"). */
22418
+ emptyText = input(null, ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
21210
22419
  tableToggle = input(false, ...(ngDevMode ? [{ debugName: "tableToggle", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
21211
22420
  ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
21212
22421
  hidden = model([], ...(ngDevMode ? [{ debugName: "hidden" }] : []));
@@ -21225,6 +22434,8 @@ class SdsBarChart {
21225
22434
  pointer = signal({ x: 0, y: 0 }, ...(ngDevMode ? [{ debugName: "pointer" }] : []));
21226
22435
  width = hostWidth();
21227
22436
  loc = chartLocale(() => this.locale());
22437
+ /** Built-in strings (provideSdsCharts labels over the English defaults). */
22438
+ text = chartLabels();
21228
22439
  animates = chartAnimates(() => this.animate());
21229
22440
  palette = chartPalette();
21230
22441
  horizontal = computed(() => this.orientation() === 'horizontal', ...(ngDevMode ? [{ debugName: "horizontal" }] : []));
@@ -21781,14 +22992,14 @@ class SdsBarChart {
21781
22992
  const m = this.meta().get(`${s.key}|${c.key}`);
21782
22993
  return { key: s.key, label: s.label, value, raw, color: m?.color ?? s.color, shape: s.line ? 'line' : 'rect' };
21783
22994
  });
21784
- const footer = this.stacked() && bars.length > 1 ? `Total · ${valueFormatter(this.loc(), this.valueFormat())(total)}` : undefined;
22995
+ const footer = this.stacked() && bars.length > 1 ? `${this.text().total} · ${valueFormatter(this.loc(), this.valueFormat())(total)}` : undefined;
21785
22996
  return { title: this.fmtCat()(c.x), rows, footer, data: [c.row], index: c.index };
21786
22997
  }, ...(ngDevMode ? [{ debugName: "tooltipCtx" }] : []));
21787
22998
  liveText = computed(() => {
21788
22999
  if (!this.keyboard())
21789
23000
  return '';
21790
23001
  const c = this.tooltipCtx();
21791
- return c ? `${c.title} — ${c.rows.map((r) => `${r.label} : ${r.value}`).join(', ')}${c.footer ? `, ${c.footer}` : ''}` : '';
23002
+ return c ? `${c.title} — ${c.rows.map((r) => this.text().labelValue(r.label, r.value)).join(', ')}${c.footer ? `, ${c.footer}` : ''}` : '';
21792
23003
  }, ...(ngDevMode ? [{ debugName: "liveText" }] : []));
21793
23004
  local(event) {
21794
23005
  const L = this.layout();
@@ -21887,7 +23098,7 @@ class SdsBarChart {
21887
23098
  if (this.ariaLabel())
21888
23099
  return this.ariaLabel();
21889
23100
  const names = this.allSeries().map((s) => s.label).join(', ');
21890
- return [this.heading() || 'Graphique en barres', names].filter(Boolean).join(' — ');
23101
+ return [this.heading() || this.text().barChart, names].filter(Boolean).join(' — ');
21891
23102
  }, ...(ngDevMode ? [{ debugName: "label" }] : []));
21892
23103
  table = computed(() => {
21893
23104
  const { cats } = this.raw();
@@ -21895,7 +23106,7 @@ class SdsBarChart {
21895
23106
  const values = this.rawValues();
21896
23107
  const fc = this.fmtCat();
21897
23108
  return {
21898
- columns: [this.xLabel() || 'Catégorie', ...vis.map((s) => s.label)],
23109
+ columns: [this.xLabel() || this.text().category, ...vis.map((s) => s.label)],
21899
23110
  rows: this.order().map((i) => [
21900
23111
  fc(cats[i].x),
21901
23112
  ...vis.map((s) => {
@@ -21928,7 +23139,7 @@ class SdsBarChart {
21928
23139
  [style.height.px]="resolvedHeight()"
21929
23140
  tabindex="0"
21930
23141
  role="group"
21931
- aria-roledescription="graphique"
23142
+ [attr.aria-roledescription]="text().chartRole"
21932
23143
  [attr.aria-label]="label()"
21933
23144
  (keydown)="onKey($event)"
21934
23145
  (blur)="onBlur()"
@@ -22008,7 +23219,7 @@ class SdsBarChart {
22008
23219
  <div class="sr-only" aria-live="polite" aria-atomic="true">{{ liveText() }}</div>
22009
23220
  </div>
22010
23221
  </sds-chart-frame>
22011
- `, isInline: true, styles: [".sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: SdsChartFrame, selector: "sds-chart-frame", inputs: ["heading", "subtitle", "items", "legend", "legendLabel", "hidden", "interactive", "loading", "empty", "emptyText", "height", "table", "tableCaption", "tableToggle", "tableLabel", "chartLabel"], outputs: ["toggle", "focusKey"] }, { kind: "component", type: SdsChartAxes, selector: "g[sdsChartAxes]", inputs: ["plot", "xTicks", "yTicks", "grid", "baselineY", "baselineX", "xTitle", "yTitle", "width"] }, { kind: "component", type: SdsChartAnnotations, selector: "g[sdsChartAnnotations]", inputs: ["items", "layer"] }, { kind: "component", type: SdsChartTooltipBox, selector: "sds-chart-tooltip-box", inputs: ["ctx", "template", "x", "y", "containerWidth", "containerHeight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
23222
+ `, isInline: true, styles: [".sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: SdsChartFrame, selector: "sds-chart-frame", inputs: ["heading", "subtitle", "items", "legend", "hidden", "interactive", "loading", "empty", "emptyText", "height", "table", "tableCaption", "tableToggle"], outputs: ["toggle", "focusKey"] }, { kind: "component", type: SdsChartAxes, selector: "g[sdsChartAxes]", inputs: ["plot", "xTicks", "yTicks", "grid", "baselineY", "baselineX", "xTitle", "yTitle", "width"] }, { kind: "component", type: SdsChartAnnotations, selector: "g[sdsChartAnnotations]", inputs: ["items", "layer"] }, { kind: "component", type: SdsChartTooltipBox, selector: "sds-chart-tooltip-box", inputs: ["ctx", "template", "x", "y", "containerWidth", "containerHeight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
22012
23223
  }
22013
23224
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsBarChart, decorators: [{
22014
23225
  type: Component,
@@ -22034,7 +23245,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
22034
23245
  [style.height.px]="resolvedHeight()"
22035
23246
  tabindex="0"
22036
23247
  role="group"
22037
- aria-roledescription="graphique"
23248
+ [attr.aria-roledescription]="text().chartRole"
22038
23249
  [attr.aria-label]="label()"
22039
23250
  (keydown)="onKey($event)"
22040
23251
  (blur)="onBlur()"
@@ -22179,13 +23390,16 @@ class SdsScatterChart {
22179
23390
  sizeFormat = input(null, ...(ngDevMode ? [{ debugName: "sizeFormat" }] : []));
22180
23391
  xLabel = input('', ...(ngDevMode ? [{ debugName: "xLabel" }] : []));
22181
23392
  yLabel = input('', ...(ngDevMode ? [{ debugName: "yLabel" }] : []));
22182
- sizeLabel = input('Taille', ...(ngDevMode ? [{ debugName: "sizeLabel" }] : []));
23393
+ /** Name of the bubble size (tooltip, table). Default (`null`): the configured `labels.size`. */
23394
+ sizeLabel = input(null, ...(ngDevMode ? [{ debugName: "sizeLabel" }] : []));
23395
+ sizeName = computed(() => this.sizeLabel() ?? this.text().size, ...(ngDevMode ? [{ debugName: "sizeName" }] : []));
22183
23396
  locale = input(null, ...(ngDevMode ? [{ debugName: "locale" }] : []));
22184
23397
  // --- Behaviour --------------------------------------------------------------------------------
22185
23398
  tooltip = input(true, ...(ngDevMode ? [{ debugName: "tooltip", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
22186
23399
  animate = input(true, ...(ngDevMode ? [{ debugName: "animate", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
22187
23400
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
22188
- emptyText = input('Aucune donnée à afficher', ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
23401
+ /** Empty-state text. Default (`null`): the configured `labels.empty` ("No data to display"). */
23402
+ emptyText = input(null, ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
22189
23403
  tableToggle = input(false, ...(ngDevMode ? [{ debugName: "tableToggle", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
22190
23404
  ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
22191
23405
  /** Hidden group names (two-way bindable; the legend toggles them). */
@@ -22204,6 +23418,8 @@ class SdsScatterChart {
22204
23418
  }
22205
23419
  width = hostWidth();
22206
23420
  loc = chartLocale(() => this.locale());
23421
+ /** Built-in strings (provideSdsCharts labels over the English defaults). */
23422
+ text = chartLabels();
22207
23423
  animates = chartAnimates(() => this.animate());
22208
23424
  palette = chartPalette();
22209
23425
  bubbles = computed(() => this.size() !== null, ...(ngDevMode ? [{ debugName: "bubbles" }] : []));
@@ -22492,7 +23708,7 @@ class SdsScatterChart {
22492
23708
  { key: 'y', label: this.yLabel() || 'y', value: this.fmtY()(p.y), raw: p.y, color, shape: 'dot' },
22493
23709
  ];
22494
23710
  if (this.bubbles() && p.size !== null)
22495
- rows.push({ key: 'size', label: this.sizeLabel(), value: this.fmtSize()(p.size), raw: p.size, color, shape: 'dot' });
23711
+ rows.push({ key: 'size', label: this.sizeName(), value: this.fmtSize()(p.size), raw: p.size, color, shape: 'dot' });
22496
23712
  const title = [p.label, p.group].filter(Boolean).join(' · ');
22497
23713
  return { title, rows, data: [p.row], index: p.index };
22498
23714
  }, ...(ngDevMode ? [{ debugName: "tooltipCtx" }] : []));
@@ -22500,7 +23716,7 @@ class SdsScatterChart {
22500
23716
  if (!this.keyboard())
22501
23717
  return '';
22502
23718
  const c = this.tooltipCtx();
22503
- return c ? `${c.title} — ${c.rows.map((r) => `${r.label} : ${r.value}`).join(', ')}` : '';
23719
+ return c ? `${c.title} — ${c.rows.map((r) => this.text().labelValue(r.label, r.value)).join(', ')}` : '';
22504
23720
  }, ...(ngDevMode ? [{ debugName: "liveText" }] : []));
22505
23721
  onPointer(event) {
22506
23722
  const L = this.layout();
@@ -22591,8 +23807,10 @@ class SdsScatterChart {
22591
23807
  label = computed(() => {
22592
23808
  if (this.ariaLabel())
22593
23809
  return this.ariaLabel();
22594
- const axes = [this.yLabel(), this.xLabel()].filter(Boolean).join(' selon ');
22595
- return [this.heading() || 'Nuage de points', axes].filter(Boolean).join(' — ');
23810
+ const y = this.yLabel();
23811
+ const x = this.xLabel();
23812
+ const axes = y && x ? this.text().versus(y, x) : y || x;
23813
+ return [this.heading() || this.text().scatterChart, axes].filter(Boolean).join(' — ');
22596
23814
  }, ...(ngDevMode ? [{ debugName: "label" }] : []));
22597
23815
  table = computed(() => {
22598
23816
  const hasLabel = this.pointLabel() !== null;
@@ -22602,10 +23820,10 @@ class SdsScatterChart {
22602
23820
  const fs = this.fmtSize();
22603
23821
  const columns = [
22604
23822
  hasLabel ? '' : '#',
22605
- ...(hasGroup ? ['Groupe'] : []),
23823
+ ...(hasGroup ? [this.text().group] : []),
22606
23824
  this.xLabel() || 'x',
22607
23825
  this.yLabel() || 'y',
22608
- ...(this.bubbles() ? [this.sizeLabel()] : []),
23826
+ ...(this.bubbles() ? [this.sizeName()] : []),
22609
23827
  ];
22610
23828
  return {
22611
23829
  columns,
@@ -22641,7 +23859,7 @@ class SdsScatterChart {
22641
23859
  [style.height.px]="height()"
22642
23860
  tabindex="0"
22643
23861
  role="group"
22644
- aria-roledescription="graphique"
23862
+ [attr.aria-roledescription]="text().chartRole"
22645
23863
  [attr.aria-label]="label()"
22646
23864
  (keydown)="onKey($event)"
22647
23865
  (blur)="onBlur()"
@@ -22736,7 +23954,7 @@ class SdsScatterChart {
22736
23954
  <div class="sr-only" aria-live="polite" aria-atomic="true">{{ liveText() }}</div>
22737
23955
  </div>
22738
23956
  </sds-chart-frame>
22739
- `, isInline: true, styles: [".sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: SdsChartFrame, selector: "sds-chart-frame", inputs: ["heading", "subtitle", "items", "legend", "legendLabel", "hidden", "interactive", "loading", "empty", "emptyText", "height", "table", "tableCaption", "tableToggle", "tableLabel", "chartLabel"], outputs: ["toggle", "focusKey"] }, { kind: "component", type: SdsChartAxes, selector: "g[sdsChartAxes]", inputs: ["plot", "xTicks", "yTicks", "grid", "baselineY", "baselineX", "xTitle", "yTitle", "width"] }, { kind: "component", type: SdsChartAnnotations, selector: "g[sdsChartAnnotations]", inputs: ["items", "layer"] }, { kind: "component", type: SdsChartTooltipBox, selector: "sds-chart-tooltip-box", inputs: ["ctx", "template", "x", "y", "containerWidth", "containerHeight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
23957
+ `, isInline: true, styles: [".sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: SdsChartFrame, selector: "sds-chart-frame", inputs: ["heading", "subtitle", "items", "legend", "hidden", "interactive", "loading", "empty", "emptyText", "height", "table", "tableCaption", "tableToggle"], outputs: ["toggle", "focusKey"] }, { kind: "component", type: SdsChartAxes, selector: "g[sdsChartAxes]", inputs: ["plot", "xTicks", "yTicks", "grid", "baselineY", "baselineX", "xTitle", "yTitle", "width"] }, { kind: "component", type: SdsChartAnnotations, selector: "g[sdsChartAnnotations]", inputs: ["items", "layer"] }, { kind: "component", type: SdsChartTooltipBox, selector: "sds-chart-tooltip-box", inputs: ["ctx", "template", "x", "y", "containerWidth", "containerHeight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
22740
23958
  }
22741
23959
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsScatterChart, decorators: [{
22742
23960
  type: Component,
@@ -22762,7 +23980,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
22762
23980
  [style.height.px]="height()"
22763
23981
  tabindex="0"
22764
23982
  role="group"
22765
- aria-roledescription="graphique"
23983
+ [attr.aria-roledescription]="text().chartRole"
22766
23984
  [attr.aria-label]="label()"
22767
23985
  (keydown)="onKey($event)"
22768
23986
  (blur)="onBlur()"
@@ -22860,11 +24078,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
22860
24078
  `, styles: [".sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"] }]
22861
24079
  }], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], x: [{ type: i0.Input, args: [{ isSignal: true, alias: "x", required: true }] }], y: [{ type: i0.Input, args: [{ isSignal: true, alias: "y", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], group: [{ type: i0.Input, args: [{ isSignal: true, alias: "group", required: false }] }], pointLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "pointLabel", required: false }] }], groupOrder: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupOrder", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], radius: [{ type: i0.Input, args: [{ isSignal: true, alias: "radius", required: false }] }], maxRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRadius", required: false }] }], trendline: [{ type: i0.Input, args: [{ isSignal: true, alias: "trendline", required: false }] }], grid: [{ type: i0.Input, args: [{ isSignal: true, alias: "grid", required: false }] }], legend: [{ type: i0.Input, args: [{ isSignal: true, alias: "legend", required: false }] }], annotations: [{ type: i0.Input, args: [{ isSignal: true, alias: "annotations", required: false }] }], xScale: [{ type: i0.Input, args: [{ isSignal: true, alias: "xScale", required: false }] }], yScale: [{ type: i0.Input, args: [{ isSignal: true, alias: "yScale", required: false }] }], xMin: [{ type: i0.Input, args: [{ isSignal: true, alias: "xMin", required: false }] }], xMax: [{ type: i0.Input, args: [{ isSignal: true, alias: "xMax", required: false }] }], yMin: [{ type: i0.Input, args: [{ isSignal: true, alias: "yMin", required: false }] }], yMax: [{ type: i0.Input, args: [{ isSignal: true, alias: "yMax", required: false }] }], includeZero: [{ type: i0.Input, args: [{ isSignal: true, alias: "includeZero", required: false }] }], xFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "xFormat", required: false }] }], valueFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormat", required: false }] }], axisFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "axisFormat", required: false }] }], sizeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizeFormat", required: false }] }], xLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "xLabel", required: false }] }], yLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "yLabel", required: false }] }], sizeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizeLabel", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: false }] }], animate: [{ type: i0.Input, args: [{ isSignal: true, alias: "animate", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], tableToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableToggle", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }, { type: i0.Output, args: ["hiddenChange"] }], pointClick: [{ type: i0.Output, args: ["pointClick"] }], customTooltip: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsChartTooltip), { isSignal: true }] }] } });
22862
24080
 
22863
- /** Key of the folded "Autres" slice — in `hidden` and in `pointClick.key`. */
24081
+ /** Key of the folded "Other" slice — in `hidden` and in `pointClick.key`. */
22864
24082
  const SDS_DONUT_OTHER_KEY = '__sds_other__';
22865
24083
  const OTHER_KEY = SDS_DONUT_OTHER_KEY;
22866
24084
  /**
22867
- * Donut, pie and half-donut for part-to-whole at a glance. Small tails fold into "Autres" past
24085
+ * Donut, pie and half-donut for part-to-whole at a glance. Small tails fold into "Other" past
22868
24086
  * `maxSlices` (a pie stops reading beyond ~6 parts), the legend doubles as direct labels with each
22869
24087
  * share, the centre states the total and swaps to the hovered slice, slices separate with a
22870
24088
  * constant 2 px gap, and the whole thing is keyboard-navigable with a data-table twin. Colour follows
@@ -22890,16 +24108,21 @@ class SdsDonutChart {
22890
24108
  height = input(240, ...(ngDevMode ? [{ debugName: "height", transform: numberAttribute }] : [{ transform: numberAttribute }]));
22891
24109
  /** Ring thickness in px. Defaults to ~26 % of the radius. */
22892
24110
  thickness = input(null, ...(ngDevMode ? [{ debugName: "thickness", transform: numberOrNull }] : [{ transform: numberOrNull }]));
22893
- /** Beyond this many slices the smallest fold into one "Autres" slice. */
24111
+ /** Beyond this many slices the smallest fold into one "Other" slice. */
22894
24112
  maxSlices = input(6, ...(ngDevMode ? [{ debugName: "maxSlices", transform: numberAttribute }] : [{ transform: numberAttribute }]));
22895
- otherLabel = input('Autres', ...(ngDevMode ? [{ debugName: "otherLabel" }] : []));
24113
+ /** Name of the folded tail slice. Default (`null`): the configured `labels.other` ("Other"). */
24114
+ otherLabel = input(null, ...(ngDevMode ? [{ debugName: "otherLabel" }] : []));
22896
24115
  /** `desc` (default) orders slices largest first; `none` keeps the data order. */
22897
24116
  sort = input('desc', ...(ngDevMode ? [{ debugName: "sort" }] : []));
22898
24117
  /** `auto` = right of the ring (below it on narrow containers). */
22899
24118
  legend = input('auto', ...(ngDevMode ? [{ debugName: "legend" }] : []));
22900
24119
  legendValue = input('percent', ...(ngDevMode ? [{ debugName: "legendValue" }] : []));
22901
- /** Headline in the hole. `null` hides it; default "Total". */
22902
- centerLabel = input('Total', ...(ngDevMode ? [{ debugName: "centerLabel" }] : []));
24120
+ /**
24121
+ * Headline in the hole. Default (`null`): the configured `labels.total` ("Total"). An empty string
24122
+ * `''` hides the centre readout entirely (label and figure — what `null` did before 0.0.67), and the
24123
+ * hover tooltip takes over.
24124
+ */
24125
+ centerLabel = input(null, ...(ngDevMode ? [{ debugName: "centerLabel" }] : []));
22903
24126
  /** Override the centre figure (e.g. "72 %" for a completion donut). */
22904
24127
  centerValue = input(null, ...(ngDevMode ? [{ debugName: "centerValue" }] : []));
22905
24128
  valueFormat = input(null, ...(ngDevMode ? [{ debugName: "valueFormat" }] : []));
@@ -22907,10 +24130,11 @@ class SdsDonutChart {
22907
24130
  tooltip = input(true, ...(ngDevMode ? [{ debugName: "tooltip", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
22908
24131
  animate = input(true, ...(ngDevMode ? [{ debugName: "animate", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
22909
24132
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
22910
- emptyText = input('Aucune donnée à afficher', ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
24133
+ /** Empty-state text. Default (`null`): the configured `labels.empty` ("No data to display"). */
24134
+ emptyText = input(null, ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
22911
24135
  tableToggle = input(false, ...(ngDevMode ? [{ debugName: "tableToggle", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
22912
24136
  ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
22913
- /** Hidden slice keys (category names, or the "Autres" slice). Two-way bindable. */
24137
+ /** Hidden slice keys (category names, or the "Other" slice). Two-way bindable. */
22914
24138
  hidden = model([], ...(ngDevMode ? [{ debugName: "hidden" }] : []));
22915
24139
  pointClick = output();
22916
24140
  customTooltip = contentChild(SdsChartTooltip, ...(ngDevMode ? [{ debugName: "customTooltip" }] : []));
@@ -22926,11 +24150,13 @@ class SdsDonutChart {
22926
24150
  pointer = signal({ x: 0, y: 0 }, ...(ngDevMode ? [{ debugName: "pointer" }] : []));
22927
24151
  width = hostWidth();
22928
24152
  loc = chartLocale(() => this.locale());
24153
+ /** Built-in strings (provideSdsCharts labels over the English defaults). */
24154
+ text = chartLabels();
22929
24155
  animates = chartAnimates(() => this.animate());
22930
24156
  palette = chartPalette();
22931
24157
  fmt = computed(() => valueFormatter(this.loc(), this.valueFormat()), ...(ngDevMode ? [{ debugName: "fmt" }] : []));
22932
24158
  pct = computed(() => valueFormatter(this.loc(), PERCENT_VALUE), ...(ngDevMode ? [{ debugName: "pct" }] : []));
22933
- /** Slices after grouping, sorting and folding the tail into "Autres". */
24159
+ /** Slices after grouping, sorting and folding the tail into "Other". */
22934
24160
  slices = computed(() => {
22935
24161
  const data = this.data() ?? [];
22936
24162
  const cAcc = this.category();
@@ -22954,7 +24180,7 @@ class SdsDonutChart {
22954
24180
  let list = [...byKey.values()].filter((s) => s.value > 0);
22955
24181
  const assign = (kept) => {
22956
24182
  // Slot = position among the slices actually drawn, in DATA order (never by size) — so a slice
22957
- // that appears 9th in the data still gets a hue, distinct from the gray "Autres".
24183
+ // that appears 9th in the data still gets a hue, distinct from the gray "Other".
22958
24184
  let slot = 0;
22959
24185
  for (const s of [...kept].sort((a, b) => a.index - b.index))
22960
24186
  if (!s.color)
@@ -22974,7 +24200,7 @@ class SdsDonutChart {
22974
24200
  assign(list);
22975
24201
  list.push({
22976
24202
  key: OTHER_KEY,
22977
- label: `${this.otherLabel()} (${tail.length})`,
24203
+ label: `${this.otherLabel() ?? this.text().other} (${tail.length})`,
22978
24204
  value: tail.reduce((sum, s) => sum + s.value, 0),
22979
24205
  color: 'var(--color-ink-faint)',
22980
24206
  rows: tail.flatMap((s) => s.rows),
@@ -22996,7 +24222,7 @@ class SdsDonutChart {
22996
24222
  return Math.max(120, Math.min(this.height(), w));
22997
24223
  }, ...(ngDevMode ? [{ debugName: "size" }] : []));
22998
24224
  boxHeight = computed(() => (this.half() ? Math.round(this.size() / 2 + 18) : this.size()), ...(ngDevMode ? [{ debugName: "boxHeight" }] : []));
22999
- showCenter = computed(() => this.variant() === 'donut' && this.centerLabel() !== null, ...(ngDevMode ? [{ debugName: "showCenter" }] : []));
24225
+ showCenter = computed(() => this.variant() === 'donut' && this.centerLabel() !== '', ...(ngDevMode ? [{ debugName: "showCenter" }] : []));
23000
24226
  showTooltip = computed(() => this.tooltip() && (!this.showCenter() || !!this.customTooltip()), ...(ngDevMode ? [{ debugName: "showTooltip" }] : []));
23001
24227
  geo = computed(() => {
23002
24228
  if (this.width() <= 0 || this.isEmpty())
@@ -23093,7 +24319,7 @@ class SdsDonutChart {
23093
24319
  sub: total ? this.pct()(s.value / total) : '',
23094
24320
  };
23095
24321
  }
23096
- return { label: this.centerLabel() ?? '', value: this.centerValue() ?? this.fmt()(total), sub: '' };
24322
+ return { label: this.centerLabel() ?? this.text().total, value: this.centerValue() ?? this.fmt()(total), sub: '' };
23097
24323
  }, ...(ngDevMode ? [{ debugName: "center" }] : []));
23098
24324
  tooltipCtx = computed(() => {
23099
24325
  const key = this.active();
@@ -23116,7 +24342,7 @@ class SdsDonutChart {
23116
24342
  if (!s)
23117
24343
  return '';
23118
24344
  const total = this.total();
23119
- return `${s.label} : ${this.fmt()(s.value)}${total ? `, ${this.pct()(s.value / total)}` : ''}`;
24345
+ return `${this.text().labelValue(s.label, this.fmt()(s.value))}${total ? `, ${this.pct()(s.value / total)}` : ''}`;
23120
24346
  }, ...(ngDevMode ? [{ debugName: "liveText" }] : []));
23121
24347
  legendPosition = computed(() => (this.legend() === 'auto' ? 'right' : this.legend()), ...(ngDevMode ? [{ debugName: "legendPosition" }] : []));
23122
24348
  hover(key, event) {
@@ -23205,12 +24431,12 @@ class SdsDonutChart {
23205
24431
  label = computed(() => {
23206
24432
  if (this.ariaLabel())
23207
24433
  return this.ariaLabel();
23208
- return [this.heading() || 'Graphique en anneau', `${this.slices().length} parts`, `total ${this.fmt()(this.total())}`].join(' — ');
24434
+ return [this.heading() || this.text().donutChart, this.text().sliceCount(this.slices().length), this.text().totalOf(this.fmt()(this.total()))].join(' — ');
23209
24435
  }, ...(ngDevMode ? [{ debugName: "label" }] : []));
23210
24436
  table = computed(() => {
23211
24437
  const total = this.total();
23212
24438
  return {
23213
- columns: ['Catégorie', 'Valeur', 'Part'],
24439
+ columns: [this.text().category, this.text().value, this.text().share],
23214
24440
  rows: this.visible().map((s) => [s.label, this.fmt()(s.value), total ? this.pct()(s.value / total) : '—']),
23215
24441
  };
23216
24442
  }, ...(ngDevMode ? [{ debugName: "table" }] : []));
@@ -23238,7 +24464,7 @@ class SdsDonutChart {
23238
24464
  [style.height.px]="boxHeight()"
23239
24465
  tabindex="0"
23240
24466
  role="group"
23241
- aria-roledescription="graphique"
24467
+ [attr.aria-roledescription]="text().chartRole"
23242
24468
  [attr.aria-label]="label()"
23243
24469
  (keydown)="onKey($event)"
23244
24470
  (blur)="onBlur()"
@@ -23282,7 +24508,7 @@ class SdsDonutChart {
23282
24508
  <div class="sr-only" aria-live="polite" aria-atomic="true">{{ liveText() }}</div>
23283
24509
  </div>
23284
24510
  </sds-chart-frame>
23285
- `, isInline: true, styles: [".sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: SdsChartFrame, selector: "sds-chart-frame", inputs: ["heading", "subtitle", "items", "legend", "legendLabel", "hidden", "interactive", "loading", "empty", "emptyText", "height", "table", "tableCaption", "tableToggle", "tableLabel", "chartLabel"], outputs: ["toggle", "focusKey"] }, { kind: "component", type: SdsChartTooltipBox, selector: "sds-chart-tooltip-box", inputs: ["ctx", "template", "x", "y", "containerWidth", "containerHeight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
24511
+ `, isInline: true, styles: [".sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: SdsChartFrame, selector: "sds-chart-frame", inputs: ["heading", "subtitle", "items", "legend", "hidden", "interactive", "loading", "empty", "emptyText", "height", "table", "tableCaption", "tableToggle"], outputs: ["toggle", "focusKey"] }, { kind: "component", type: SdsChartTooltipBox, selector: "sds-chart-tooltip-box", inputs: ["ctx", "template", "x", "y", "containerWidth", "containerHeight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
23286
24512
  }
23287
24513
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsDonutChart, decorators: [{
23288
24514
  type: Component,
@@ -23309,7 +24535,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
23309
24535
  [style.height.px]="boxHeight()"
23310
24536
  tabindex="0"
23311
24537
  role="group"
23312
- aria-roledescription="graphique"
24538
+ [attr.aria-roledescription]="text().chartRole"
23313
24539
  [attr.aria-label]="label()"
23314
24540
  (keydown)="onKey($event)"
23315
24541
  (blur)="onBlur()"
@@ -23397,8 +24623,8 @@ class SdsHeatmap {
23397
24623
  valueFormat = input(null, ...(ngDevMode ? [{ debugName: "valueFormat" }] : []));
23398
24624
  /** Format of the column / row labels (a function; dates default to their day). */
23399
24625
  xFormat = input(null, ...(ngDevMode ? [{ debugName: "xFormat" }] : []));
23400
- /** Name of the measure in the tooltip (e.g. "Arrivées"). */
23401
- valueLabel = input('Valeur', ...(ngDevMode ? [{ debugName: "valueLabel" }] : []));
24626
+ /** Name of the measure in the tooltip (e.g. "Arrivals"). Default (`null`): `labels.value`. */
24627
+ valueLabel = input(null, ...(ngDevMode ? [{ debugName: "valueLabel" }] : []));
23402
24628
  /** Column-axis title, drawn under the matrix. */
23403
24629
  xLabel = input('', ...(ngDevMode ? [{ debugName: "xLabel" }] : []));
23404
24630
  /** Row-axis title, drawn vertically on the left (and the table's first column header). */
@@ -23408,7 +24634,8 @@ class SdsHeatmap {
23408
24634
  /** Cross-fade cell colours when the data changes. */
23409
24635
  animate = input(true, ...(ngDevMode ? [{ debugName: "animate", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
23410
24636
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
23411
- emptyText = input('Aucune donnée à afficher', ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
24637
+ /** Empty-state text. Default (`null`): the configured `labels.empty` ("No data to display"). */
24638
+ emptyText = input(null, ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
23412
24639
  tableToggle = input(false, ...(ngDevMode ? [{ debugName: "tableToggle", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
23413
24640
  ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
23414
24641
  pointClick = output();
@@ -23423,6 +24650,8 @@ class SdsHeatmap {
23423
24650
  }
23424
24651
  width = hostWidth();
23425
24652
  loc = chartLocale(() => this.locale());
24653
+ /** Built-in strings (provideSdsCharts labels over the English defaults). */
24654
+ text = chartLabels();
23426
24655
  animates = chartAnimates(() => this.animate());
23427
24656
  fmt = computed(() => valueFormatter(this.loc(), this.valueFormat()), ...(ngDevMode ? [{ debugName: "fmt" }] : []));
23428
24657
  matrix = computed(() => {
@@ -23580,8 +24809,8 @@ class SdsHeatmap {
23580
24809
  rows: [
23581
24810
  {
23582
24811
  key: 'value',
23583
- label: this.valueLabel(),
23584
- value: c.value === null ? 'Aucune donnée' : this.fmt()(c.value),
24812
+ label: this.valueLabel() ?? this.text().value,
24813
+ value: c.value === null ? this.text().noData : this.fmt()(c.value),
23585
24814
  raw: c.value,
23586
24815
  color: vis.fill,
23587
24816
  shape: 'rect',
@@ -23595,7 +24824,7 @@ class SdsHeatmap {
23595
24824
  if (!this.keyboard())
23596
24825
  return '';
23597
24826
  const c = this.tooltipCtx();
23598
- return c ? `${c.title} : ${c.rows[0].value}` : '';
24827
+ return c ? this.text().labelValue(c.title ?? '', c.rows[0].value) : '';
23599
24828
  }, ...(ngDevMode ? [{ debugName: "liveText" }] : []));
23600
24829
  hover(i, _event) {
23601
24830
  this.keyboard.set(false);
@@ -23661,7 +24890,7 @@ class SdsHeatmap {
23661
24890
  if (this.ariaLabel())
23662
24891
  return this.ariaLabel();
23663
24892
  const { xs, ys } = this.matrix();
23664
- return [this.heading() || 'Carte de chaleur', `${ys.length} lignes × ${xs.length} colonnes`].join(' — ');
24893
+ return [this.heading() || this.text().heatmap, this.text().gridSize(ys.length, xs.length)].join(' — ');
23665
24894
  }, ...(ngDevMode ? [{ debugName: "label" }] : []));
23666
24895
  table = computed(() => {
23667
24896
  const { xs, ys, cells } = this.matrix();
@@ -23697,7 +24926,7 @@ class SdsHeatmap {
23697
24926
  [style.height.px]="resolvedHeight()"
23698
24927
  tabindex="0"
23699
24928
  role="group"
23700
- aria-roledescription="carte de chaleur"
24929
+ [attr.aria-roledescription]="text().heatmapRole"
23701
24930
  [attr.aria-label]="label()"
23702
24931
  (keydown)="onKey($event)"
23703
24932
  (blur)="onBlur()"
@@ -23780,7 +25009,7 @@ class SdsHeatmap {
23780
25009
  </div>
23781
25010
  }
23782
25011
  </sds-chart-frame>
23783
- `, isInline: true, styles: [".heat-cell{cursor:pointer}.heat-anim{transition:fill .3s ease}@media(prefers-reduced-motion:reduce){.heat-anim{transition:none}}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: SdsChartFrame, selector: "sds-chart-frame", inputs: ["heading", "subtitle", "items", "legend", "legendLabel", "hidden", "interactive", "loading", "empty", "emptyText", "height", "table", "tableCaption", "tableToggle", "tableLabel", "chartLabel"], outputs: ["toggle", "focusKey"] }, { kind: "component", type: SdsChartTooltipBox, selector: "sds-chart-tooltip-box", inputs: ["ctx", "template", "x", "y", "containerWidth", "containerHeight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
25012
+ `, isInline: true, styles: [".heat-cell{cursor:pointer}.heat-anim{transition:fill .3s ease}@media(prefers-reduced-motion:reduce){.heat-anim{transition:none}}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: SdsChartFrame, selector: "sds-chart-frame", inputs: ["heading", "subtitle", "items", "legend", "hidden", "interactive", "loading", "empty", "emptyText", "height", "table", "tableCaption", "tableToggle"], outputs: ["toggle", "focusKey"] }, { kind: "component", type: SdsChartTooltipBox, selector: "sds-chart-tooltip-box", inputs: ["ctx", "template", "x", "y", "containerWidth", "containerHeight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
23784
25013
  }
23785
25014
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsHeatmap, decorators: [{
23786
25015
  type: Component,
@@ -23802,7 +25031,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
23802
25031
  [style.height.px]="resolvedHeight()"
23803
25032
  tabindex="0"
23804
25033
  role="group"
23805
- aria-roledescription="carte de chaleur"
25034
+ [attr.aria-roledescription]="text().heatmapRole"
23806
25035
  [attr.aria-label]="label()"
23807
25036
  (keydown)="onKey($event)"
23808
25037
  (blur)="onBlur()"
@@ -23908,7 +25137,9 @@ class SdsTreemap {
23908
25137
  /** Click a branch to zoom into it (with a breadcrumb back). */
23909
25138
  drilldown = input(true, ...(ngDevMode ? [{ debugName: "drilldown", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
23910
25139
  /** Name of the top of the breadcrumb. */
23911
- rootLabel = input('Tout', ...(ngDevMode ? [{ debugName: "rootLabel" }] : []));
25140
+ /** Default (`null`): the configured `labels.all` ("All"). */
25141
+ rootLabel = input(null, ...(ngDevMode ? [{ debugName: "rootLabel" }] : []));
25142
+ rootName = computed(() => this.rootLabel() ?? this.text().all, ...(ngDevMode ? [{ debugName: "rootName" }] : []));
23912
25143
  /** Print the value under the name inside tiles large enough. */
23913
25144
  showValues = input(true, ...(ngDevMode ? [{ debugName: "showValues", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
23914
25145
  /** `auto` = the branch legend below the tiles when there are two or more branches. */
@@ -23917,7 +25148,8 @@ class SdsTreemap {
23917
25148
  tooltip = input(true, ...(ngDevMode ? [{ debugName: "tooltip", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
23918
25149
  animate = input(true, ...(ngDevMode ? [{ debugName: "animate", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
23919
25150
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
23920
- emptyText = input('Aucune donnée à afficher', ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
25151
+ /** Empty-state text. Default (`null`): the configured `labels.empty` ("No data to display"). */
25152
+ emptyText = input(null, ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
23921
25153
  tableToggle = input(false, ...(ngDevMode ? [{ debugName: "tableToggle", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
23922
25154
  ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
23923
25155
  /** A leaf tile was clicked (branches zoom instead while `drilldown` is on). `key` is the path "A › B". */
@@ -23931,6 +25163,8 @@ class SdsTreemap {
23931
25163
  pointer = signal({ x: 0, y: 0 }, ...(ngDevMode ? [{ debugName: "pointer" }] : []));
23932
25164
  width = hostWidth();
23933
25165
  loc = chartLocale(() => this.locale());
25166
+ /** Built-in strings (provideSdsCharts labels over the English defaults). */
25167
+ text = chartLabels();
23934
25168
  animates = chartAnimates(() => this.animate());
23935
25169
  palette = chartPalette();
23936
25170
  fmt = computed(() => valueFormatter(this.loc(), this.valueFormat()), ...(ngDevMode ? [{ debugName: "fmt" }] : []));
@@ -23942,7 +25176,7 @@ class SdsTreemap {
23942
25176
  this.keyboard.set(false);
23943
25177
  }
23944
25178
  /** Full hierarchy, summed once. */
23945
- root = computed(() => this.build({ name: this.rootLabel(), children: this.data() ?? [] }), ...(ngDevMode ? [{ debugName: "root" }] : []));
25179
+ root = computed(() => this.build({ name: this.rootName(), children: this.data() ?? [] }), ...(ngDevMode ? [{ debugName: "root" }] : []));
23946
25180
  build(data) {
23947
25181
  return hierarchy(data, (d) => d.children)
23948
25182
  .sum((d) => (d.children?.length ? 0 : Math.max(0, Number(d.value) || 0)))
@@ -23954,7 +25188,7 @@ class SdsTreemap {
23954
25188
  * the first index that no longer names a branch — so new data can never leave a stale zoom behind.
23955
25189
  */
23956
25190
  zoom = computed(() => {
23957
- let node = { name: this.rootLabel(), children: this.data() ?? [] };
25191
+ let node = { name: this.rootName(), children: this.data() ?? [] };
23958
25192
  const indices = [];
23959
25193
  const names = [];
23960
25194
  for (const i of this.path()) {
@@ -24057,17 +25291,17 @@ class SdsTreemap {
24057
25291
  const parent = t.node.parent;
24058
25292
  const color = this.activeRect()?.color ?? slotColor(0);
24059
25293
  const v = t.node.value ?? 0;
24060
- const rows = [{ key: 'value', label: 'Valeur', value: this.fmt()(v), raw: v, color, shape: 'rect' }];
25294
+ const rows = [{ key: 'value', label: this.text().value, value: this.fmt()(v), raw: v, color, shape: 'rect' }];
24061
25295
  if (total)
24062
- rows.push({ key: 'share', label: 'Part du total', value: this.pct()(v / total), raw: v / total, color, shape: 'rect' });
25296
+ rows.push({ key: 'share', label: this.text().shareOfTotal, value: this.pct()(v / total), raw: v / total, color, shape: 'rect' });
24063
25297
  if (parent && parent.depth > 0 && parent.value) {
24064
- rows.push({ key: 'parent', label: `Part de ${parent.data.name}`, value: this.pct()(v / parent.value), raw: v / parent.value, color, shape: 'rect' });
25298
+ rows.push({ key: 'parent', label: this.text().shareOf(parent.data.name), value: this.pct()(v / parent.value), raw: v / parent.value, color, shape: 'rect' });
24065
25299
  }
24066
25300
  return { title: t.path, rows, data: [t.node.data], index: 0 };
24067
25301
  }, ...(ngDevMode ? [{ debugName: "tooltipCtx" }] : []));
24068
25302
  liveText = computed(() => {
24069
25303
  const c = this.keyboard() ? this.tooltipCtx() : null;
24070
- return c ? `${c.title} : ${c.rows.map((r) => `${r.label} ${r.value}`).join(', ')}` : this.announcement();
25304
+ return c ? this.text().labelValue(c.title ?? '', c.rows.map((r) => `${r.label} ${r.value}`).join(', ')) : this.announcement();
24071
25305
  }, ...(ngDevMode ? [{ debugName: "liveText" }] : []));
24072
25306
  hover(key, event) {
24073
25307
  this.keyboard.set(false);
@@ -24089,7 +25323,8 @@ class SdsTreemap {
24089
25323
  announceZoom() {
24090
25324
  const z = this.zoom();
24091
25325
  const count = this.layoutRoot()?.leaves().length ?? 0;
24092
- this.announcement.set(z.names.length ? `Zoom sur ${z.names.join(PATH_SEP)} — ${count} éléments` : `${this.rootLabel()} — ${count} éléments`);
25326
+ const t = this.text();
25327
+ this.announcement.set(`${z.names.length ? t.zoomedTo(z.names.join(PATH_SEP)) : this.rootName()} — ${t.itemCount(count)}`);
24093
25328
  }
24094
25329
  /** Zoom into the tile's branch — or, at the deepest level, report the leaf. */
24095
25330
  activate(key) {
@@ -24181,14 +25416,14 @@ class SdsTreemap {
24181
25416
  if (this.ariaLabel())
24182
25417
  return this.ariaLabel();
24183
25418
  const zoomed = this.zoom().names;
24184
- return [this.heading() || 'Treemap', zoomed.length ? `zoom : ${zoomed.join(PATH_SEP)}` : '', `total ${this.fmt()(this.root().value ?? 0)}`]
25419
+ return [this.heading() || this.text().treemap, zoomed.length ? this.text().zoomedTo(zoomed.join(PATH_SEP)) : '', this.text().totalOf(this.fmt()(this.root().value ?? 0))]
24185
25420
  .filter(Boolean)
24186
25421
  .join(' — ');
24187
25422
  }, ...(ngDevMode ? [{ debugName: "label" }] : []));
24188
25423
  table = computed(() => {
24189
25424
  const total = this.root().value ?? 0;
24190
25425
  return {
24191
- columns: ['Élément', 'Valeur', 'Part'],
25426
+ columns: [this.text().item, this.text().value, this.text().share],
24192
25427
  rows: this.root()
24193
25428
  .leaves()
24194
25429
  .map((l) => [
@@ -24215,9 +25450,9 @@ class SdsTreemap {
24215
25450
  [tableToggle]="tableToggle()"
24216
25451
  >
24217
25452
  @if (drilldown() && zoom().names.length) {
24218
- <nav class="mb-2 flex flex-wrap items-center gap-1 text-xs" aria-label="Fil d'Ariane">
25453
+ <nav class="mb-2 flex flex-wrap items-center gap-1 text-xs" [attr.aria-label]="text().breadcrumb">
24219
25454
  <button type="button" class="rounded px-1.5 py-0.5 font-medium text-ink-soft underline-offset-2 hover:bg-muted hover:text-ink hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500" (click)="zoomTo(0)">
24220
- {{ rootLabel() }}
25455
+ {{ rootName() }}
24221
25456
  </button>
24222
25457
  @for (p of zoom().names; track $index) {
24223
25458
  <sds-icon name="chevron-right" [size]="12" class="text-ink-faint" />
@@ -24234,7 +25469,7 @@ class SdsTreemap {
24234
25469
  [style.height.px]="height()"
24235
25470
  tabindex="0"
24236
25471
  role="group"
24237
- aria-roledescription="graphique"
25472
+ [attr.aria-roledescription]="text().chartRole"
24238
25473
  [attr.aria-label]="label()"
24239
25474
  (keydown)="onKey($event)"
24240
25475
  (blur)="onBlur()"
@@ -24285,7 +25520,7 @@ class SdsTreemap {
24285
25520
  <div class="sr-only" aria-live="polite" aria-atomic="true">{{ liveText() }}</div>
24286
25521
  </div>
24287
25522
  </sds-chart-frame>
24288
- `, isInline: true, styles: [".sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: SdsChartFrame, selector: "sds-chart-frame", inputs: ["heading", "subtitle", "items", "legend", "legendLabel", "hidden", "interactive", "loading", "empty", "emptyText", "height", "table", "tableCaption", "tableToggle", "tableLabel", "chartLabel"], outputs: ["toggle", "focusKey"] }, { kind: "component", type: SdsChartTooltipBox, selector: "sds-chart-tooltip-box", inputs: ["ctx", "template", "x", "y", "containerWidth", "containerHeight"] }, { kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth", "filled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
25523
+ `, isInline: true, styles: [".sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: SdsChartFrame, selector: "sds-chart-frame", inputs: ["heading", "subtitle", "items", "legend", "hidden", "interactive", "loading", "empty", "emptyText", "height", "table", "tableCaption", "tableToggle"], outputs: ["toggle", "focusKey"] }, { kind: "component", type: SdsChartTooltipBox, selector: "sds-chart-tooltip-box", inputs: ["ctx", "template", "x", "y", "containerWidth", "containerHeight"] }, { kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth", "filled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
24289
25524
  }
24290
25525
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTreemap, decorators: [{
24291
25526
  type: Component,
@@ -24305,9 +25540,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
24305
25540
  [tableToggle]="tableToggle()"
24306
25541
  >
24307
25542
  @if (drilldown() && zoom().names.length) {
24308
- <nav class="mb-2 flex flex-wrap items-center gap-1 text-xs" aria-label="Fil d'Ariane">
25543
+ <nav class="mb-2 flex flex-wrap items-center gap-1 text-xs" [attr.aria-label]="text().breadcrumb">
24309
25544
  <button type="button" class="rounded px-1.5 py-0.5 font-medium text-ink-soft underline-offset-2 hover:bg-muted hover:text-ink hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500" (click)="zoomTo(0)">
24310
- {{ rootLabel() }}
25545
+ {{ rootName() }}
24311
25546
  </button>
24312
25547
  @for (p of zoom().names; track $index) {
24313
25548
  <sds-icon name="chevron-right" [size]="12" class="text-ink-faint" />
@@ -24324,7 +25559,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
24324
25559
  [style.height.px]="height()"
24325
25560
  tabindex="0"
24326
25561
  role="group"
24327
- aria-roledescription="graphique"
25562
+ [attr.aria-roledescription]="text().chartRole"
24328
25563
  [attr.aria-label]="label()"
24329
25564
  (keydown)="onKey($event)"
24330
25565
  (blur)="onBlur()"
@@ -24412,7 +25647,7 @@ class SdsSparkline {
24412
25647
  valueFormat = input(null, ...(ngDevMode ? [{ debugName: "valueFormat" }] : []));
24413
25648
  /** What the trend measures — prefixes the spoken summary. */
24414
25649
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
24415
- /** Replace the generated summary ("de 12 à 24 — min…") entirely. */
25650
+ /** Replace the generated summary ("Trend: from 12 to 24 — min…", `labels.sparklineSummary`) entirely. */
24416
25651
  ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
24417
25652
  tooltip = input(true, ...(ngDevMode ? [{ debugName: "tooltip", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
24418
25653
  animate = input(true, ...(ngDevMode ? [{ debugName: "animate", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
@@ -24421,6 +25656,8 @@ class SdsSparkline {
24421
25656
  active = signal(null, ...(ngDevMode ? [{ debugName: "active" }] : []));
24422
25657
  measured = hostWidth();
24423
25658
  loc = chartLocale(() => this.locale());
25659
+ /** Built-in strings (provideSdsCharts labels over the English defaults). */
25660
+ text = chartLabels();
24424
25661
  animates = chartAnimates(() => this.animate());
24425
25662
  fmt = computed(() => valueFormatter(this.loc(), this.valueFormat()), ...(ngDevMode ? [{ debugName: "fmt" }] : []));
24426
25663
  palette = chartPalette();
@@ -24575,16 +25812,23 @@ class SdsSparkline {
24575
25812
  index: i,
24576
25813
  };
24577
25814
  }, ...(ngDevMode ? [{ debugName: "tooltipCtx" }] : []));
24578
- /** "Admissions : de 12 à 24 — min 12, max 24, 7 points." */
25815
+ /** "Admissions: from 12 to 24 — min 12, max 24, 7 points" (`labels.sparklineSummary`). */
24579
25816
  summary = computed(() => {
24580
25817
  if (this.ariaLabel())
24581
25818
  return this.ariaLabel();
24582
25819
  const vals = this.values().filter((v) => v !== null);
25820
+ const t = this.text();
24583
25821
  if (!vals.length)
24584
- return this.label() || 'Tendance';
25822
+ return this.label() || t.trend;
24585
25823
  const f = this.fmt();
24586
- const head = this.label() ? `${this.label()} : ` : 'Tendance : ';
24587
- return `${head}de ${f(vals[0])} à ${f(vals[vals.length - 1])} — min ${f(Math.min(...vals))}, max ${f(Math.max(...vals))}, ${vals.length} points`;
25824
+ return t.sparklineSummary({
25825
+ label: this.label() || t.trend,
25826
+ first: f(vals[0]),
25827
+ last: f(vals[vals.length - 1]),
25828
+ min: f(Math.min(...vals)),
25829
+ max: f(Math.max(...vals)),
25830
+ count: vals.length,
25831
+ });
24588
25832
  }, ...(ngDevMode ? [{ debugName: "summary" }] : []));
24589
25833
  onPointer(event) {
24590
25834
  const G = this.geo();
@@ -24769,6 +26013,8 @@ class SdsKpi {
24769
26013
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
24770
26014
  locale = input(null, ...(ngDevMode ? [{ debugName: "locale" }] : []));
24771
26015
  loc = chartLocale(() => this.locale());
26016
+ /** Built-in strings (provideSdsCharts labels over the English defaults). */
26017
+ text = chartLabels();
24772
26018
  display = computed(() => {
24773
26019
  const v = this.value();
24774
26020
  if (v === null || v === undefined || !Number.isFinite(v))
@@ -24810,10 +26056,12 @@ class SdsKpi {
24810
26056
  ? 'var(--color-ink-soft)'
24811
26057
  : `color-mix(in oklab, ${good ? 'var(--color-accent-green)' : 'var(--color-accent-red)'} 70%, var(--color-ink))`;
24812
26058
  const icon = flat ? 'minus' : up ? 'trending-up' : 'trending-down';
24813
- const direction = flat ? 'stable' : up ? 'en hausse' : 'en baisse';
24814
- const verdict = good === null ? '' : good ? ', favorable' : ', défavorable';
24815
- const period = this.deltaLabel() ? ' ' + this.deltaLabel() : '';
24816
- const spoken = flat ? `${direction}${period}` : `${direction} de ${text.replace(/^[+−-]/, '')}${period}${verdict}`;
26059
+ const spoken = this.text().kpiChange({
26060
+ direction: flat ? 'flat' : up ? 'up' : 'down',
26061
+ delta: flat ? '' : text.replace(/^[+−-]/, ''),
26062
+ period: this.deltaLabel(),
26063
+ good,
26064
+ });
24817
26065
  return { text, tone, icon, spoken };
24818
26066
  }, ...(ngDevMode ? [{ debugName: "deltaView" }] : []));
24819
26067
  absoluteFormat() {
@@ -24967,5 +26215,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
24967
26215
  * Generated bundle index. Do not edit.
24968
26216
  */
24969
26217
 
24970
- export { SDS_ALLERGENS, SDS_ALLERGEN_ICONS, SDS_ALLERGEN_LABELS_EN, SDS_CHART_CONFIG, SDS_DONUT_OTHER_KEY, SDS_ICONS, SDS_PHONE_COUNTRIES, SDS_THEME_STORAGE_KEY, SDS_UI_PREF_LANG_PARAM, SDS_UI_PREF_THEME_PARAM, SdsAccordion, SdsAccordionContent, SdsAccordionHeader, SdsAccordionPanel, SdsAccordionStore, SdsAddressAutocomplete, SdsAddressExtra, SdsAllergenIcon, SdsAvatar, SdsBadge, SdsBarChart, SdsBulkBar, SdsButton, SdsCalendar, SdsCard, SdsChartTooltip, SdsCheckbox, SdsChecklistRow, SdsChip, SdsChipGroup, SdsDatePicker, SdsDonutChart, SdsEnvBanner, SdsFileDrop, SdsHeatmap, SdsIcon, SdsImageCropper, SdsInput, SdsInputNumber, SdsKpi, SdsLanguageSwitcher, SdsLineChart, SdsLocalizedInput, SdsMenu, SdsMenuItem, SdsMenuTrigger, SdsMultiSelect, SdsNavGroup, SdsNavItem, SdsOtpInput, SdsPaginator, SdsPhoneInput, SdsRadio, SdsScatterChart, SdsScore, SdsSearchCommand, SdsSegmented, SdsSelect, SdsSelectEmpty, SdsSelectFooter, SdsSelectOption, SdsShell, SdsSidebar, SdsSortableHandle, SdsSortableItem, SdsSortableList, SdsSparkline, SdsStatusTile, SdsTab, SdsTabList, SdsTabPanel, SdsTabPanels, SdsTable, SdsTableCell, SdsTableEmpty, SdsTableHeader, SdsTableRowExpansion, SdsTabs, SdsTabsStore, SdsTextarea, SdsThemeService, SdsThemeToggle, SdsTimeInput, SdsToastHost, SdsToastService, SdsToggleSwitch, SdsTopbar, SdsTreemap, SdsTriStateChip, SdsUserMenu, SidebarStore, addDays, addMonths, addMonthsKeepDay, appendUiPrefs, formatDdMmYyyy, formatHhMm, isOutOfRange, isSameDay, isSdsThemeMode, isoDay, pad2, provideSdsCharts, readUiPrefs, sdsFileMatchesAccept, sdsFormatBytes, sdsFormatNational, sdsMatchDialCode, sdsNormalizeSignificant, startOfDay, startOfMonth, stripUiPrefs };
26218
+ export { SDS_ALLERGENS, SDS_ALLERGEN_ICONS, SDS_ALLERGEN_LABELS_EN, SDS_CHART_CONFIG, SDS_CHART_LABELS_EN, SDS_DONUT_OTHER_KEY, SDS_ICONS, SDS_PHONE_COUNTRIES, SDS_THEME_STORAGE_KEY, SDS_UI_PREF_LANG_PARAM, SDS_UI_PREF_THEME_PARAM, SdsAccordion, SdsAccordionContent, SdsAccordionHeader, SdsAccordionPanel, SdsAccordionStore, SdsAddressAutocomplete, SdsAddressExtra, SdsAllergenIcon, SdsAvatar, SdsBadge, SdsBarChart, SdsBulkBar, SdsButton, SdsCalendar, SdsCard, SdsChartTooltip, SdsCheckbox, SdsChecklistRow, SdsChip, SdsChipGroup, SdsDatePicker, SdsDateRangePicker, SdsDonutChart, SdsEnvBanner, SdsFileDrop, SdsHeatmap, SdsIcon, SdsImageCropper, SdsInput, SdsInputNumber, SdsKpi, SdsLanguageSwitcher, SdsLineChart, SdsLocalizedInput, SdsMenu, SdsMenuItem, SdsMenuTrigger, SdsMultiSelect, SdsNavGroup, SdsNavItem, SdsOtpInput, SdsPaginator, SdsPhoneInput, SdsRadio, SdsRangeCalendar, SdsScatterChart, SdsScore, SdsSearchCommand, SdsSegmented, SdsSelect, SdsSelectEmpty, SdsSelectFooter, SdsSelectOption, SdsShell, SdsSidebar, SdsSortableHandle, SdsSortableItem, SdsSortableList, SdsSparkline, SdsStatusTile, SdsTab, SdsTabList, SdsTabPanel, SdsTabPanels, SdsTable, SdsTableCell, SdsTableEmpty, SdsTableHeader, SdsTableRowExpansion, SdsTabs, SdsTabsStore, SdsTextarea, SdsThemeService, SdsThemeToggle, SdsTimeInput, SdsToastHost, SdsToastService, SdsToggleSwitch, SdsTopbar, SdsTreemap, SdsTriStateChip, SdsUserMenu, SidebarStore, addDays, addMonths, addMonthsKeepDay, appendUiPrefs, diffInDays, formatDateRange, formatDdMmYyyy, formatHhMm, isOutOfRange, isSameDay, isSameRange, isSdsThemeMode, isoDay, pad2, provideSdsCharts, readUiPrefs, sdsFileMatchesAccept, sdsFormatBytes, sdsFormatNational, sdsLastDays, sdsLastMonths, sdsMatchDialCode, sdsNormalizeSignificant, startOfDay, startOfMonth, stripUiPrefs };
24971
26219
  //# sourceMappingURL=besaitech-ng-design-system.mjs.map