@besaitech/ng-design-system 0.0.54 → 0.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/fesm2022/besaitech-ng-design-system.mjs +1522 -283
- package/fesm2022/besaitech-ng-design-system.mjs.map +1 -1
- package/index.d.ts +364 -29
- package/package.json +1 -1
- package/themes/belvigo-emerald.css +73 -0
|
@@ -119,6 +119,10 @@ const SDS_ICONS = {
|
|
|
119
119
|
'grip-vertical': '<circle cx="9" cy="12" r="1"/><circle cx="9" cy="5" r="1"/><circle cx="9" cy="19" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="15" cy="5" r="1"/><circle cx="15" cy="19" r="1"/>',
|
|
120
120
|
'upload': '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" x2="12" y1="3" y2="15"/>',
|
|
121
121
|
'image': '<rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/>',
|
|
122
|
+
// A photo that failed or is missing (sds-avatar fallbacks, photo trays).
|
|
123
|
+
'image-off': '<line x1="2" x2="22" y1="2" y2="22"/><path d="M10.41 10.41a2 2 0 1 1-2.83-2.83"/><line x1="13.5" x2="6" y1="13.5" y2="21"/><line x1="18" x2="21" y1="12" y2="15"/><path d="M3.59 3.59A1.99 1.99 0 0 0 3 5v14a2 2 0 0 0 2 2h14c.55 0 1.052-.22 1.41-.59"/><path d="M21 15V5a2 2 0 0 0-2-2H9"/>',
|
|
124
|
+
// Fork and knife: the placeholder of a menu item without a photo.
|
|
125
|
+
'utensils': '<path d="M3 2v7c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2V2"/><path d="M7 2v20"/><path d="M21 15V2a5 5 0 0 0-5 5v6c0 1.1.9 2 2 2h3Zm0 0v7"/>',
|
|
122
126
|
'zoom-in': '<circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/><line x1="11" x2="11" y1="8" y2="14"/><line x1="8" x2="14" y1="11" y2="11"/>',
|
|
123
127
|
'zoom-out': '<circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/><line x1="8" x2="14" y1="11" y2="11"/>',
|
|
124
128
|
'trash-2': '<path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/>',
|
|
@@ -218,7 +222,7 @@ const VARIANTS$2 = {
|
|
|
218
222
|
'dark:hover:bg-white/10 dark:active:bg-white/15',
|
|
219
223
|
danger: 'bg-danger text-white shadow-sm hover:brightness-95 active:brightness-90',
|
|
220
224
|
};
|
|
221
|
-
const SIZES$
|
|
225
|
+
const SIZES$c = {
|
|
222
226
|
sm: 'h-8 px-3 text-sm',
|
|
223
227
|
md: 'h-10 px-4 text-sm',
|
|
224
228
|
lg: 'h-12 px-5 text-base',
|
|
@@ -278,7 +282,7 @@ class SdsButton {
|
|
|
278
282
|
BASE,
|
|
279
283
|
this.shape() === 'pill' ? 'rounded-full' : 'rounded-control',
|
|
280
284
|
VARIANTS$2[this.variant()],
|
|
281
|
-
SIZES$
|
|
285
|
+
SIZES$c[size],
|
|
282
286
|
this.iconOnly() ? ICON_ONLY[size] : '',
|
|
283
287
|
this.block() ? 'w-full' : '',
|
|
284
288
|
]
|
|
@@ -330,8 +334,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
330
334
|
`, styles: [".sds-spinner{width:1em;height:1em;border:2px solid currentColor;border-right-color:transparent;border-radius:9999px;animation:sds-spin .6s linear infinite}@keyframes sds-spin{to{transform:rotate(360deg)}}\n"] }]
|
|
331
335
|
}], ctorParameters: () => [], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], block: [{ type: i0.Input, args: [{ isSignal: true, alias: "block", required: false }] }], iconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnly", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
|
|
332
336
|
|
|
333
|
-
let nextId$
|
|
334
|
-
const SIZES$
|
|
337
|
+
let nextId$n = 0;
|
|
338
|
+
const SIZES$b = {
|
|
335
339
|
sm: 'h-9 text-sm',
|
|
336
340
|
md: 'h-10 text-sm',
|
|
337
341
|
lg: 'h-12 text-base',
|
|
@@ -343,7 +347,7 @@ const SIZES$a = {
|
|
|
343
347
|
* sidebar search field).
|
|
344
348
|
*/
|
|
345
349
|
class SdsInput {
|
|
346
|
-
inputId = `sds-input-${nextId$
|
|
350
|
+
inputId = `sds-input-${nextId$n++}`;
|
|
347
351
|
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
348
352
|
/** Accessible name used when no visible [label] is provided. */
|
|
349
353
|
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
@@ -427,7 +431,7 @@ class SdsInput {
|
|
|
427
431
|
return `${left} ${right}`;
|
|
428
432
|
}, ...(ngDevMode ? [{ debugName: "fieldPadding" }] : []));
|
|
429
433
|
wrapperClasses = computed(() => {
|
|
430
|
-
const parts = [SIZES$
|
|
434
|
+
const parts = [SIZES$b[this.size()], this.pill() ? 'rounded-full' : 'rounded-control'];
|
|
431
435
|
if (this.isDisabled())
|
|
432
436
|
parts.push('opacity-60 cursor-not-allowed');
|
|
433
437
|
if (this.filled()) {
|
|
@@ -675,7 +679,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
675
679
|
`, styles: ["input:-webkit-autofill,input:-webkit-autofill:hover,input:-webkit-autofill:focus,input:-webkit-autofill:active{transition:background-color 100000s ease-in-out 0s;-webkit-text-fill-color:currentColor;caret-color:currentColor}\n"] }]
|
|
676
680
|
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], leadingIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "leadingIcon", required: false }] }], trailingIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailingIcon", required: false }] }], trailingAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailingAction", required: false }] }], trailingActionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailingActionLabel", required: false }] }], trailingActionPressed: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailingActionPressed", required: false }] }], trailingActionClick: [{ type: i0.Output, args: ["trailingActionClick"] }] } });
|
|
677
681
|
|
|
678
|
-
let nextId$
|
|
682
|
+
let nextId$m = 0;
|
|
679
683
|
const HEIGHTS = {
|
|
680
684
|
sm: 'h-9',
|
|
681
685
|
md: 'h-10',
|
|
@@ -703,7 +707,7 @@ const TEXT = {
|
|
|
703
707
|
* with "." ("1.234" in nl-BE) stays a thousands separator.
|
|
704
708
|
*/
|
|
705
709
|
class SdsInputNumber {
|
|
706
|
-
inputId = `sds-input-number-${nextId$
|
|
710
|
+
inputId = `sds-input-number-${nextId$m++}`;
|
|
707
711
|
fieldRef = viewChild('field', ...(ngDevMode ? [{ debugName: "fieldRef" }] : []));
|
|
708
712
|
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
709
713
|
/** Accessible name used when no visible [label] is provided. */
|
|
@@ -1470,108 +1474,123 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
1470
1474
|
}]
|
|
1471
1475
|
}], ctorParameters: () => [], propDecorators: { fieldRef: [{ type: i0.ViewChild, args: ['field', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], ariaDescribedby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedby", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], currency: [{ type: i0.Input, args: [{ isSignal: true, alias: "currency", required: false }] }], currencyDisplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "currencyDisplay", required: false }] }], useGrouping: [{ type: i0.Input, args: [{ isSignal: true, alias: "useGrouping", required: false }] }], minFractionDigits: [{ type: i0.Input, args: [{ isSignal: true, alias: "minFractionDigits", required: false }] }], maxFractionDigits: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFractionDigits", required: false }] }], prefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefix", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], showButtons: [{ type: i0.Input, args: [{ isSignal: true, alias: "showButtons", required: false }] }], buttonLayout: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttonLayout", required: false }] }], incrementIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "incrementIcon", required: false }] }], decrementIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "decrementIcon", required: false }] }], allowEmpty: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowEmpty", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], incrementLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "incrementLabel", required: false }] }], decrementLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "decrementLabel", required: false }] }] } });
|
|
1472
1476
|
|
|
1473
|
-
let nextId$
|
|
1474
|
-
const SIZES$
|
|
1475
|
-
sm: '
|
|
1476
|
-
md: '
|
|
1477
|
-
lg: '
|
|
1477
|
+
let nextId$l = 0;
|
|
1478
|
+
const SIZES$a = {
|
|
1479
|
+
sm: 'h-9 text-sm',
|
|
1480
|
+
md: 'h-10 text-sm',
|
|
1481
|
+
lg: 'h-12 text-base',
|
|
1478
1482
|
};
|
|
1483
|
+
const pad2$1 = (n) => String(n).padStart(2, '0');
|
|
1484
|
+
/** Parse `HH:mm` or `HH:mm:ss` (the seconds are ignored). Anything else is null. */
|
|
1485
|
+
function parseTime(value) {
|
|
1486
|
+
if (typeof value !== 'string')
|
|
1487
|
+
return null;
|
|
1488
|
+
const match = /^(\d{1,2}):(\d{2})(?::\d{2}(?:\.\d+)?)?$/.exec(value.trim());
|
|
1489
|
+
if (!match)
|
|
1490
|
+
return null;
|
|
1491
|
+
const h = Number(match[1]);
|
|
1492
|
+
const m = Number(match[2]);
|
|
1493
|
+
if (h > 23 || m > 59)
|
|
1494
|
+
return null;
|
|
1495
|
+
return { h, m };
|
|
1496
|
+
}
|
|
1479
1497
|
/**
|
|
1480
|
-
*
|
|
1481
|
-
*
|
|
1482
|
-
*
|
|
1483
|
-
* already have content, so a partially-translated value is obvious at a glance.
|
|
1498
|
+
* Time-of-day field, 24-hour, whatever the browser's locale: two segments (hours : minutes) in one
|
|
1499
|
+
* framed field, the value being a `"HH:mm"` string (or null) — the shape of a backend `LocalTime`.
|
|
1500
|
+
* Implements ControlValueAccessor: use `formControlName` or `[(ngModel)]`.
|
|
1484
1501
|
*
|
|
1485
|
-
*
|
|
1486
|
-
*
|
|
1487
|
-
* pruned from the emitted value, keeping the stored map clean.
|
|
1502
|
+
* Unlike a native `<input type="time">` it never switches to a 12-hour clock with AM/PM, and it is themed
|
|
1503
|
+
* like every other field (label, hint, error, sizes, `filled`, dark mode).
|
|
1488
1504
|
*
|
|
1489
|
-
*
|
|
1490
|
-
*
|
|
1505
|
+
* Keyboard: type digits (the hours move on to the minutes by themselves after two digits, or one digit
|
|
1506
|
+
* above 2); `:`/`.`/→ jump to the minutes, ← or Backspace on an empty minute go back; ↑/↓ step the hours
|
|
1507
|
+
* by 1 and the minutes by [minuteStep] (wrapping, never carrying into the hour). Each segment is a
|
|
1508
|
+
* `spinbutton` with its own accessible name. An hour typed without minutes gets `:00` when the field is
|
|
1509
|
+
* left; emptying both segments sets null.
|
|
1510
|
+
*
|
|
1511
|
+
* @example
|
|
1512
|
+
* <sds-time-input label="Opens at" formControlName="start" [minuteStep]="15" />
|
|
1513
|
+
*
|
|
1514
|
+
* @example
|
|
1515
|
+
* <sds-time-input [ariaLabel]="'Closes at'" [ngModel]="end()" (ngModelChange)="end.set($event)" clearable />
|
|
1491
1516
|
*/
|
|
1492
|
-
class
|
|
1493
|
-
|
|
1517
|
+
class SdsTimeInput {
|
|
1518
|
+
fieldId = `sds-time-input-${nextId$l++}`;
|
|
1494
1519
|
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
1520
|
+
/** Accessible name of the field when there is no visible [label]. */
|
|
1521
|
+
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
1495
1522
|
hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : []));
|
|
1496
|
-
|
|
1497
|
-
/** The accepted language codes, in display order (e.g. `['fr','nl','en']`). */
|
|
1498
|
-
languages = input([], ...(ngDevMode ? [{ debugName: "languages" }] : []));
|
|
1499
|
-
/** Language to pre-select; used when it is one of {@link languages}, else the first accepted language. */
|
|
1500
|
-
currentLanguage = input(null, ...(ngDevMode ? [{ debugName: "currentLanguage" }] : []));
|
|
1501
|
-
multiline = input(false, ...(ngDevMode ? [{ debugName: "multiline", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
1502
|
-
rows = input(3, ...(ngDevMode ? [{ debugName: "rows", transform: numberAttribute }] : [{ transform: numberAttribute }]));
|
|
1523
|
+
error = input('', ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
1503
1524
|
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
|
|
1525
|
+
/** `filled` is borderless on a muted ground, like sds-input's `filled`. */
|
|
1526
|
+
appearance = input('default', ...(ngDevMode ? [{ debugName: "appearance" }] : []));
|
|
1504
1527
|
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1528
|
+
readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
1529
|
+
/** Show a clear button (sets null) while there is a value. */
|
|
1530
|
+
clearable = input(false, ...(ngDevMode ? [{ debugName: "clearable", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
1531
|
+
/** Leading clock icon. */
|
|
1532
|
+
showIcon = input(true, ...(ngDevMode ? [{ debugName: "showIcon", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
1533
|
+
/** Minutes moved by ↑/↓ on the minutes segment (snaps to the step first). Default 1. */
|
|
1534
|
+
minuteStep = input(1, ...(ngDevMode ? [{ debugName: "minuteStep", transform: numberAttribute }] : [{ transform: numberAttribute }]));
|
|
1535
|
+
/** Emit `HH:mm:ss` (seconds always `00`) instead of `HH:mm`. */
|
|
1536
|
+
withSeconds = input(false, ...(ngDevMode ? [{ debugName: "withSeconds", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
1537
|
+
/** Placeholder of each empty segment. */
|
|
1538
|
+
placeholderText = input('--', ...(ngDevMode ? [{ debugName: "placeholderText" }] : []));
|
|
1539
|
+
/** Accessible name of the hours segment. English default — translate it. */
|
|
1540
|
+
hoursLabel = input('Hours', ...(ngDevMode ? [{ debugName: "hoursLabel" }] : []));
|
|
1541
|
+
/** Accessible name of the minutes segment. English default — translate it. */
|
|
1542
|
+
minutesLabel = input('Minutes', ...(ngDevMode ? [{ debugName: "minutesLabel" }] : []));
|
|
1543
|
+
/** Accessible name of the clear button. English default — translate it. */
|
|
1544
|
+
clearLabel = input('Clear time', ...(ngDevMode ? [{ debugName: "clearLabel" }] : []));
|
|
1545
|
+
hoursEl = viewChild.required('hoursEl');
|
|
1546
|
+
minutesEl = viewChild.required('minutesEl');
|
|
1547
|
+
hours = signal(null, ...(ngDevMode ? [{ debugName: "hours" }] : []));
|
|
1548
|
+
minutes = signal(null, ...(ngDevMode ? [{ debugName: "minutes" }] : []));
|
|
1549
|
+
/** Raw text of a segment while it is being typed (null = show the committed value). */
|
|
1550
|
+
hoursDraft = signal(null, ...(ngDevMode ? [{ debugName: "hoursDraft" }] : []));
|
|
1551
|
+
minutesDraft = signal(null, ...(ngDevMode ? [{ debugName: "minutesDraft" }] : []));
|
|
1515
1552
|
cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : []));
|
|
1516
1553
|
isDisabled = computed(() => this.disabled() || this.cvaDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
|
|
1554
|
+
hasValue = computed(() => this.hours() !== null || this.minutes() !== null, ...(ngDevMode ? [{ debugName: "hasValue" }] : []));
|
|
1555
|
+
hoursText = computed(() => this.hoursDraft() ?? (this.hours() === null ? '' : pad2$1(this.hours())), ...(ngDevMode ? [{ debugName: "hoursText" }] : []));
|
|
1556
|
+
minutesText = computed(() => this.minutesDraft() ?? (this.minutes() === null ? '' : pad2$1(this.minutes())), ...(ngDevMode ? [{ debugName: "minutesText" }] : []));
|
|
1557
|
+
describedBy = computed(() => {
|
|
1558
|
+
if (this.error())
|
|
1559
|
+
return `${this.fieldId}-err`;
|
|
1560
|
+
if (this.hint())
|
|
1561
|
+
return `${this.fieldId}-hint`;
|
|
1562
|
+
return null;
|
|
1563
|
+
}, ...(ngDevMode ? [{ debugName: "describedBy" }] : []));
|
|
1517
1564
|
wrapperClasses = computed(() => {
|
|
1518
|
-
const parts = [SIZES$
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
const langs = this.languages();
|
|
1527
|
-
if (!langs.length) {
|
|
1528
|
-
return '';
|
|
1529
|
-
}
|
|
1530
|
-
const manual = this.manual();
|
|
1531
|
-
if (manual && langs.includes(manual)) {
|
|
1532
|
-
return manual;
|
|
1533
|
-
}
|
|
1534
|
-
const preferred = this.currentLanguage();
|
|
1535
|
-
return preferred && langs.includes(preferred) ? preferred : langs[0];
|
|
1536
|
-
}, ...(ngDevMode ? [{ debugName: "activeLang" }] : []));
|
|
1537
|
-
current = computed(() => this.valueMap()[this.activeLang()] ?? '', ...(ngDevMode ? [{ debugName: "current" }] : []));
|
|
1538
|
-
filled(lang) {
|
|
1539
|
-
const text = this.valueMap()[lang];
|
|
1540
|
-
return !!text && text.trim().length > 0;
|
|
1541
|
-
}
|
|
1542
|
-
fieldPlaceholder() {
|
|
1543
|
-
const base = this.placeholder();
|
|
1544
|
-
const lang = this.activeLang().toUpperCase();
|
|
1545
|
-
if (base) {
|
|
1546
|
-
return lang ? `${base} (${lang})` : base;
|
|
1547
|
-
}
|
|
1548
|
-
return lang ? `(${lang})` : '';
|
|
1549
|
-
}
|
|
1550
|
-
select(lang) {
|
|
1551
|
-
this.manual.set(lang);
|
|
1552
|
-
this.onTouched();
|
|
1553
|
-
}
|
|
1554
|
-
write(event) {
|
|
1555
|
-
const lang = this.activeLang();
|
|
1556
|
-
if (!lang) {
|
|
1557
|
-
return;
|
|
1558
|
-
}
|
|
1559
|
-
const text = event.target.value;
|
|
1560
|
-
const next = { ...this.valueMap() };
|
|
1561
|
-
if (text) {
|
|
1562
|
-
next[lang] = text;
|
|
1565
|
+
const parts = [SIZES$a[this.size()], 'rounded-control'];
|
|
1566
|
+
if (this.isDisabled())
|
|
1567
|
+
parts.push('opacity-60 cursor-not-allowed');
|
|
1568
|
+
if (this.appearance() === 'filled') {
|
|
1569
|
+
parts.push('border-transparent bg-muted');
|
|
1570
|
+
parts.push(this.error()
|
|
1571
|
+
? 'border-danger focus-within:ring-2 focus-within:ring-danger/30'
|
|
1572
|
+
: 'focus-within:ring-2 focus-within:ring-brand-500/25');
|
|
1563
1573
|
}
|
|
1564
1574
|
else {
|
|
1565
|
-
|
|
1575
|
+
parts.push('bg-surface');
|
|
1576
|
+
parts.push(this.error()
|
|
1577
|
+
? 'border-danger focus-within:border-danger focus-within:ring-2 focus-within:ring-danger/25'
|
|
1578
|
+
: 'border-border focus-within:border-brand-500 focus-within:ring-2 focus-within:ring-brand-500/25');
|
|
1566
1579
|
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1580
|
+
return parts.join(' ');
|
|
1581
|
+
}, ...(ngDevMode ? [{ debugName: "wrapperClasses" }] : []));
|
|
1582
|
+
pad(n) {
|
|
1583
|
+
return pad2$1(n);
|
|
1569
1584
|
}
|
|
1570
1585
|
// --- ControlValueAccessor -------------------------------------------------
|
|
1571
1586
|
onChange = () => { };
|
|
1572
1587
|
onTouched = () => { };
|
|
1573
1588
|
writeValue(value) {
|
|
1574
|
-
|
|
1589
|
+
const t = parseTime(value);
|
|
1590
|
+
this.hours.set(t ? t.h : null);
|
|
1591
|
+
this.minutes.set(t ? t.m : null);
|
|
1592
|
+
this.hoursDraft.set(null);
|
|
1593
|
+
this.minutesDraft.set(null);
|
|
1575
1594
|
}
|
|
1576
1595
|
registerOnChange(fn) {
|
|
1577
1596
|
this.onChange = fn;
|
|
@@ -1582,44 +1601,599 @@ class SdsLocalizedInput {
|
|
|
1582
1601
|
setDisabledState(isDisabled) {
|
|
1583
1602
|
this.cvaDisabled.set(isDisabled);
|
|
1584
1603
|
}
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1604
|
+
// --- typing ---------------------------------------------------------------
|
|
1605
|
+
selectAll(event) {
|
|
1606
|
+
const el = event.target;
|
|
1607
|
+
queueMicrotask(() => el.select());
|
|
1608
|
+
}
|
|
1609
|
+
/** Clicking the frame (icon, padding) focuses the hours, like clicking a native field. */
|
|
1610
|
+
onFrameClick(event) {
|
|
1611
|
+
if (this.isDisabled())
|
|
1612
|
+
return;
|
|
1613
|
+
const target = event.target;
|
|
1614
|
+
if (target.tagName !== 'INPUT' && !target.closest('button'))
|
|
1615
|
+
this.hoursEl().nativeElement.focus();
|
|
1616
|
+
}
|
|
1617
|
+
onHoursInput(event) {
|
|
1618
|
+
const el = event.target;
|
|
1619
|
+
const text = el.value;
|
|
1620
|
+
// A pasted/autofilled full time ("09:45", "9.45", "9h45") fills both segments.
|
|
1621
|
+
if (/[:.h]/i.test(text)) {
|
|
1622
|
+
const t = parseTime(text.replace(/[.h]/i, ':'));
|
|
1623
|
+
if (t) {
|
|
1624
|
+
this.commitHours(t.h);
|
|
1625
|
+
this.commitMinutes(t.m);
|
|
1626
|
+
this.syncSegments();
|
|
1627
|
+
this.focusMinutes();
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
const digits = text.replace(/\D/g, '');
|
|
1632
|
+
if (digits === '') {
|
|
1633
|
+
el.value = '';
|
|
1634
|
+
this.hoursDraft.set('');
|
|
1635
|
+
this.hours.set(null);
|
|
1636
|
+
this.emitIfComplete();
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
// How many leading digits make the hour: "9" is 09 (no hour starts with 3-9 and has a second digit);
|
|
1640
|
+
// "1"/"2" wait for a second digit; "25" is 2 then 5 minutes. Whatever follows spills into minutes, so
|
|
1641
|
+
// typing "945" in one go (or a fast/IME/autofill burst) still reads as 09:45.
|
|
1642
|
+
let hourLength;
|
|
1643
|
+
if (Number(digits[0]) > 2)
|
|
1644
|
+
hourLength = 1;
|
|
1645
|
+
else if (digits.length === 1)
|
|
1646
|
+
hourLength = 0;
|
|
1647
|
+
else
|
|
1648
|
+
hourLength = Number(digits.slice(0, 2)) <= 23 ? 2 : 1;
|
|
1649
|
+
if (hourLength === 0) {
|
|
1650
|
+
el.value = digits;
|
|
1651
|
+
this.hoursDraft.set(digits);
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
this.commitHours(Number(digits.slice(0, hourLength)));
|
|
1655
|
+
el.value = this.hoursText();
|
|
1656
|
+
const rest = digits.slice(hourLength, hourLength + 2);
|
|
1657
|
+
if (rest)
|
|
1658
|
+
this.takeMinuteDigits(rest);
|
|
1659
|
+
this.focusMinutes();
|
|
1660
|
+
}
|
|
1661
|
+
onMinutesInput(event) {
|
|
1662
|
+
const el = event.target;
|
|
1663
|
+
const digits = el.value.replace(/\D/g, '').slice(0, 2);
|
|
1664
|
+
if (digits === '') {
|
|
1665
|
+
el.value = '';
|
|
1666
|
+
this.minutesDraft.set('');
|
|
1667
|
+
this.minutes.set(null);
|
|
1668
|
+
this.emitIfComplete();
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
this.takeMinuteDigits(digits);
|
|
1672
|
+
el.value = this.minutesText();
|
|
1673
|
+
}
|
|
1674
|
+
/** "7" (no minute starts with 6-9 and has a second digit) and any 2 digits commit; "0"-"5" wait. */
|
|
1675
|
+
takeMinuteDigits(digits) {
|
|
1676
|
+
if (digits.length >= 2 || Number(digits) > 5) {
|
|
1677
|
+
this.commitMinutes(Math.min(59, Number(digits.slice(0, 2))));
|
|
1678
|
+
}
|
|
1679
|
+
else {
|
|
1680
|
+
this.minutesDraft.set(digits);
|
|
1681
|
+
}
|
|
1682
|
+
this.minutesEl().nativeElement.value = this.minutesText();
|
|
1683
|
+
}
|
|
1684
|
+
/** Force both inputs to show the committed values (a binding does not repaint an unchanged value). */
|
|
1685
|
+
syncSegments() {
|
|
1686
|
+
this.hoursEl().nativeElement.value = this.hoursText();
|
|
1687
|
+
this.minutesEl().nativeElement.value = this.minutesText();
|
|
1688
|
+
}
|
|
1689
|
+
onHoursKeydown(event) {
|
|
1690
|
+
if (this.readonly())
|
|
1691
|
+
return;
|
|
1692
|
+
const el = event.target;
|
|
1693
|
+
switch (event.key) {
|
|
1694
|
+
case 'ArrowUp':
|
|
1695
|
+
case 'ArrowDown': {
|
|
1696
|
+
event.preventDefault();
|
|
1697
|
+
const delta = event.key === 'ArrowUp' ? 1 : -1;
|
|
1698
|
+
const current = this.draftNumber(this.hoursDraft()) ?? this.hours();
|
|
1699
|
+
const next = current === null ? (delta > 0 ? 0 : 23) : (current + delta + 24) % 24;
|
|
1700
|
+
this.commitHours(next);
|
|
1701
|
+
this.syncSegments();
|
|
1702
|
+
break;
|
|
1703
|
+
}
|
|
1704
|
+
case ':':
|
|
1705
|
+
case '.':
|
|
1706
|
+
case 'h':
|
|
1707
|
+
event.preventDefault();
|
|
1708
|
+
this.flushHours();
|
|
1709
|
+
this.syncSegments();
|
|
1710
|
+
this.focusMinutes();
|
|
1711
|
+
break;
|
|
1712
|
+
case 'ArrowRight':
|
|
1713
|
+
if (el.selectionStart === el.value.length) {
|
|
1714
|
+
event.preventDefault();
|
|
1715
|
+
this.flushHours();
|
|
1716
|
+
this.focusMinutes();
|
|
1717
|
+
}
|
|
1718
|
+
break;
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
onMinutesKeydown(event) {
|
|
1722
|
+
if (this.readonly())
|
|
1723
|
+
return;
|
|
1724
|
+
const el = event.target;
|
|
1725
|
+
switch (event.key) {
|
|
1726
|
+
case 'ArrowUp':
|
|
1727
|
+
case 'ArrowDown': {
|
|
1728
|
+
event.preventDefault();
|
|
1729
|
+
const step = Math.max(1, Math.min(60, this.minuteStep()));
|
|
1730
|
+
const current = this.draftNumber(this.minutesDraft()) ?? this.minutes();
|
|
1731
|
+
let next;
|
|
1732
|
+
if (current === null) {
|
|
1733
|
+
next = event.key === 'ArrowUp' ? 0 : 60 - step;
|
|
1734
|
+
}
|
|
1735
|
+
else if (event.key === 'ArrowUp') {
|
|
1736
|
+
next = (Math.floor(current / step) + 1) * step;
|
|
1737
|
+
}
|
|
1738
|
+
else {
|
|
1739
|
+
next = current % step === 0 ? current - step : Math.floor(current / step) * step;
|
|
1740
|
+
}
|
|
1741
|
+
this.commitMinutes(((next % 60) + 60) % 60);
|
|
1742
|
+
this.syncSegments();
|
|
1743
|
+
break;
|
|
1744
|
+
}
|
|
1745
|
+
case 'Backspace':
|
|
1746
|
+
if (el.value === '') {
|
|
1747
|
+
event.preventDefault();
|
|
1748
|
+
this.focusHours();
|
|
1749
|
+
}
|
|
1750
|
+
break;
|
|
1751
|
+
case 'ArrowLeft':
|
|
1752
|
+
if (el.selectionStart === 0 && el.selectionEnd === 0) {
|
|
1753
|
+
event.preventDefault();
|
|
1754
|
+
this.flushMinutes();
|
|
1755
|
+
this.focusHours();
|
|
1756
|
+
}
|
|
1757
|
+
break;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
/** Leaving the whole field completes a half-typed time and marks the control touched. */
|
|
1761
|
+
onFocusOut(event) {
|
|
1762
|
+
const next = event.relatedTarget;
|
|
1763
|
+
const group = event.currentTarget;
|
|
1764
|
+
if (next && group.contains(next))
|
|
1765
|
+
return;
|
|
1766
|
+
this.flushHours();
|
|
1767
|
+
this.flushMinutes();
|
|
1768
|
+
if (this.hours() !== null && this.minutes() === null)
|
|
1769
|
+
this.commitMinutes(0);
|
|
1770
|
+
else if (this.hours() === null && this.minutes() !== null)
|
|
1771
|
+
this.commitHours(0);
|
|
1772
|
+
this.syncSegments();
|
|
1773
|
+
this.onTouched();
|
|
1774
|
+
}
|
|
1775
|
+
clear() {
|
|
1776
|
+
this.hours.set(null);
|
|
1777
|
+
this.minutes.set(null);
|
|
1778
|
+
this.hoursDraft.set(null);
|
|
1779
|
+
this.minutesDraft.set(null);
|
|
1780
|
+
this.onChange(null);
|
|
1781
|
+
this.onTouched();
|
|
1782
|
+
}
|
|
1783
|
+
// --- internals ------------------------------------------------------------
|
|
1784
|
+
draftNumber(draft) {
|
|
1785
|
+
return draft === null || draft === '' ? null : Number(draft);
|
|
1786
|
+
}
|
|
1787
|
+
flushHours() {
|
|
1788
|
+
const d = this.hoursDraft();
|
|
1789
|
+
if (d !== null && d !== '')
|
|
1790
|
+
this.commitHours(Math.min(23, Number(d)));
|
|
1791
|
+
else
|
|
1792
|
+
this.hoursDraft.set(null);
|
|
1793
|
+
}
|
|
1794
|
+
flushMinutes() {
|
|
1795
|
+
const d = this.minutesDraft();
|
|
1796
|
+
if (d !== null && d !== '')
|
|
1797
|
+
this.commitMinutes(Math.min(59, Number(d)));
|
|
1798
|
+
else
|
|
1799
|
+
this.minutesDraft.set(null);
|
|
1800
|
+
}
|
|
1801
|
+
commitHours(h) {
|
|
1802
|
+
this.hours.set(h);
|
|
1803
|
+
this.hoursDraft.set(null);
|
|
1804
|
+
this.emitIfComplete();
|
|
1805
|
+
}
|
|
1806
|
+
commitMinutes(m) {
|
|
1807
|
+
this.minutes.set(m);
|
|
1808
|
+
this.minutesDraft.set(null);
|
|
1809
|
+
this.emitIfComplete();
|
|
1810
|
+
}
|
|
1811
|
+
/** Emits `HH:mm` once both segments hold a value, and null once both are empty. */
|
|
1812
|
+
emitIfComplete() {
|
|
1813
|
+
const h = this.hours();
|
|
1814
|
+
const m = this.minutes();
|
|
1815
|
+
if (h !== null && m !== null) {
|
|
1816
|
+
this.onChange(`${pad2$1(h)}:${pad2$1(m)}${this.withSeconds() ? ':00' : ''}`);
|
|
1817
|
+
}
|
|
1818
|
+
else if (h === null && m === null) {
|
|
1819
|
+
this.onChange(null);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
focusMinutes() {
|
|
1823
|
+
const el = this.minutesEl().nativeElement;
|
|
1824
|
+
el.focus();
|
|
1825
|
+
el.select();
|
|
1826
|
+
}
|
|
1827
|
+
focusHours() {
|
|
1828
|
+
const el = this.hoursEl().nativeElement;
|
|
1829
|
+
el.focus();
|
|
1830
|
+
el.select();
|
|
1831
|
+
}
|
|
1832
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTimeInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1833
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsTimeInput, isStandalone: true, selector: "sds-time-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, showIcon: { classPropertyName: "showIcon", publicName: "showIcon", isSignal: true, isRequired: false, transformFunction: null }, minuteStep: { classPropertyName: "minuteStep", publicName: "minuteStep", isSignal: true, isRequired: false, transformFunction: null }, withSeconds: { classPropertyName: "withSeconds", publicName: "withSeconds", isSignal: true, isRequired: false, transformFunction: null }, placeholderText: { classPropertyName: "placeholderText", publicName: "placeholderText", isSignal: true, isRequired: false, transformFunction: null }, hoursLabel: { classPropertyName: "hoursLabel", publicName: "hoursLabel", isSignal: true, isRequired: false, transformFunction: null }, minutesLabel: { classPropertyName: "minutesLabel", publicName: "minutesLabel", isSignal: true, isRequired: false, transformFunction: null }, clearLabel: { classPropertyName: "clearLabel", publicName: "clearLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, providers: [
|
|
1834
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SdsTimeInput), multi: true },
|
|
1835
|
+
], viewQueries: [{ propertyName: "hoursEl", first: true, predicate: ["hoursEl"], descendants: true, isSignal: true }, { propertyName: "minutesEl", first: true, predicate: ["minutesEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
1593
1836
|
@if (label()) {
|
|
1594
|
-
<label
|
|
1837
|
+
<label
|
|
1838
|
+
[id]="fieldId + '-label'"
|
|
1839
|
+
[for]="fieldId + '-h'"
|
|
1840
|
+
class="mb-1.5 block text-sm font-medium text-ink"
|
|
1841
|
+
>
|
|
1842
|
+
{{ label() }}
|
|
1843
|
+
</label>
|
|
1595
1844
|
}
|
|
1596
1845
|
|
|
1597
1846
|
<div
|
|
1598
|
-
|
|
1847
|
+
role="group"
|
|
1848
|
+
[attr.aria-labelledby]="label() ? fieldId + '-label' : null"
|
|
1849
|
+
[attr.aria-label]="!label() && ariaLabel() ? ariaLabel() : null"
|
|
1850
|
+
[attr.aria-describedby]="describedBy()"
|
|
1851
|
+
class="relative flex items-center border transition-colors"
|
|
1599
1852
|
[class]="wrapperClasses()"
|
|
1853
|
+
(focusout)="onFocusOut($event)"
|
|
1854
|
+
(click)="onFrameClick($event)"
|
|
1600
1855
|
>
|
|
1601
|
-
@if (
|
|
1602
|
-
<
|
|
1603
|
-
|
|
1604
|
-
[
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
(blur)="onTouched()"
|
|
1610
|
-
class="min-w-0 flex-1 resize-y bg-transparent px-3.5 py-2 outline-none placeholder:text-current/50 disabled:cursor-not-allowed"
|
|
1611
|
-
></textarea>
|
|
1612
|
-
} @else {
|
|
1856
|
+
@if (showIcon()) {
|
|
1857
|
+
<sds-icon
|
|
1858
|
+
name="clock"
|
|
1859
|
+
[size]="16"
|
|
1860
|
+
class="pointer-events-none ml-3 shrink-0 text-ink-faint"
|
|
1861
|
+
/>
|
|
1862
|
+
}
|
|
1863
|
+
<div class="flex items-center tabular-nums" [class]="showIcon() ? 'pl-2' : 'pl-3.5'">
|
|
1613
1864
|
<input
|
|
1614
|
-
|
|
1615
|
-
[
|
|
1865
|
+
#hoursEl
|
|
1866
|
+
[id]="fieldId + '-h'"
|
|
1867
|
+
type="text"
|
|
1868
|
+
inputmode="numeric"
|
|
1869
|
+
autocomplete="off"
|
|
1870
|
+
maxlength="5"
|
|
1871
|
+
role="spinbutton"
|
|
1872
|
+
aria-valuemin="0"
|
|
1873
|
+
aria-valuemax="23"
|
|
1874
|
+
[attr.aria-valuenow]="hours()"
|
|
1875
|
+
[attr.aria-valuetext]="hours() === null ? null : pad(hours()!)"
|
|
1876
|
+
[attr.aria-label]="hoursLabel()"
|
|
1877
|
+
[attr.aria-invalid]="error() ? 'true' : null"
|
|
1878
|
+
[attr.aria-describedby]="describedBy()"
|
|
1879
|
+
[value]="hoursText()"
|
|
1880
|
+
[placeholder]="placeholderText()"
|
|
1616
1881
|
[disabled]="isDisabled()"
|
|
1617
|
-
[
|
|
1618
|
-
|
|
1619
|
-
(
|
|
1620
|
-
|
|
1882
|
+
[readOnly]="readonly()"
|
|
1883
|
+
class="w-[2.6ch] bg-transparent text-center text-ink outline-none placeholder:text-ink-faint disabled:cursor-not-allowed"
|
|
1884
|
+
(focus)="selectAll($event)"
|
|
1885
|
+
(input)="onHoursInput($event)"
|
|
1886
|
+
(keydown)="onHoursKeydown($event)"
|
|
1621
1887
|
/>
|
|
1622
|
-
|
|
1888
|
+
<span class="px-px text-ink-soft" aria-hidden="true">:</span>
|
|
1889
|
+
<input
|
|
1890
|
+
#minutesEl
|
|
1891
|
+
[id]="fieldId + '-m'"
|
|
1892
|
+
type="text"
|
|
1893
|
+
inputmode="numeric"
|
|
1894
|
+
autocomplete="off"
|
|
1895
|
+
maxlength="2"
|
|
1896
|
+
role="spinbutton"
|
|
1897
|
+
aria-valuemin="0"
|
|
1898
|
+
aria-valuemax="59"
|
|
1899
|
+
[attr.aria-valuenow]="minutes()"
|
|
1900
|
+
[attr.aria-valuetext]="minutes() === null ? null : pad(minutes()!)"
|
|
1901
|
+
[attr.aria-label]="minutesLabel()"
|
|
1902
|
+
[attr.aria-invalid]="error() ? 'true' : null"
|
|
1903
|
+
[attr.aria-describedby]="describedBy()"
|
|
1904
|
+
[value]="minutesText()"
|
|
1905
|
+
[placeholder]="placeholderText()"
|
|
1906
|
+
[disabled]="isDisabled()"
|
|
1907
|
+
[readOnly]="readonly()"
|
|
1908
|
+
class="w-[2.6ch] bg-transparent text-center text-ink outline-none placeholder:text-ink-faint disabled:cursor-not-allowed"
|
|
1909
|
+
(focus)="selectAll($event)"
|
|
1910
|
+
(input)="onMinutesInput($event)"
|
|
1911
|
+
(keydown)="onMinutesKeydown($event)"
|
|
1912
|
+
/>
|
|
1913
|
+
</div>
|
|
1914
|
+
|
|
1915
|
+
@if (clearable() && hasValue() && !isDisabled() && !readonly()) {
|
|
1916
|
+
<button
|
|
1917
|
+
type="button"
|
|
1918
|
+
tabindex="-1"
|
|
1919
|
+
[attr.aria-label]="clearLabel()"
|
|
1920
|
+
class="ml-auto mr-2 inline-flex h-6 w-6 items-center justify-center rounded-full text-ink-faint hover:bg-black/5 hover:text-ink-soft dark:hover:bg-white/10"
|
|
1921
|
+
(click)="clear()"
|
|
1922
|
+
>
|
|
1923
|
+
<sds-icon name="x" [size]="15" />
|
|
1924
|
+
</button>
|
|
1925
|
+
}
|
|
1926
|
+
</div>
|
|
1927
|
+
|
|
1928
|
+
@if (error()) {
|
|
1929
|
+
<p [id]="fieldId + '-err'" class="mt-1.5 text-xs font-medium text-danger">{{ error() }}</p>
|
|
1930
|
+
} @else if (hint()) {
|
|
1931
|
+
<p [id]="fieldId + '-hint'" class="mt-1.5 text-xs text-ink-faint">{{ hint() }}</p>
|
|
1932
|
+
}
|
|
1933
|
+
`, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1934
|
+
}
|
|
1935
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTimeInput, decorators: [{
|
|
1936
|
+
type: Component,
|
|
1937
|
+
args: [{
|
|
1938
|
+
selector: 'sds-time-input',
|
|
1939
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1940
|
+
imports: [SdsIcon],
|
|
1941
|
+
providers: [
|
|
1942
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SdsTimeInput), multi: true },
|
|
1943
|
+
],
|
|
1944
|
+
host: { class: 'block' },
|
|
1945
|
+
template: `
|
|
1946
|
+
@if (label()) {
|
|
1947
|
+
<label
|
|
1948
|
+
[id]="fieldId + '-label'"
|
|
1949
|
+
[for]="fieldId + '-h'"
|
|
1950
|
+
class="mb-1.5 block text-sm font-medium text-ink"
|
|
1951
|
+
>
|
|
1952
|
+
{{ label() }}
|
|
1953
|
+
</label>
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
<div
|
|
1957
|
+
role="group"
|
|
1958
|
+
[attr.aria-labelledby]="label() ? fieldId + '-label' : null"
|
|
1959
|
+
[attr.aria-label]="!label() && ariaLabel() ? ariaLabel() : null"
|
|
1960
|
+
[attr.aria-describedby]="describedBy()"
|
|
1961
|
+
class="relative flex items-center border transition-colors"
|
|
1962
|
+
[class]="wrapperClasses()"
|
|
1963
|
+
(focusout)="onFocusOut($event)"
|
|
1964
|
+
(click)="onFrameClick($event)"
|
|
1965
|
+
>
|
|
1966
|
+
@if (showIcon()) {
|
|
1967
|
+
<sds-icon
|
|
1968
|
+
name="clock"
|
|
1969
|
+
[size]="16"
|
|
1970
|
+
class="pointer-events-none ml-3 shrink-0 text-ink-faint"
|
|
1971
|
+
/>
|
|
1972
|
+
}
|
|
1973
|
+
<div class="flex items-center tabular-nums" [class]="showIcon() ? 'pl-2' : 'pl-3.5'">
|
|
1974
|
+
<input
|
|
1975
|
+
#hoursEl
|
|
1976
|
+
[id]="fieldId + '-h'"
|
|
1977
|
+
type="text"
|
|
1978
|
+
inputmode="numeric"
|
|
1979
|
+
autocomplete="off"
|
|
1980
|
+
maxlength="5"
|
|
1981
|
+
role="spinbutton"
|
|
1982
|
+
aria-valuemin="0"
|
|
1983
|
+
aria-valuemax="23"
|
|
1984
|
+
[attr.aria-valuenow]="hours()"
|
|
1985
|
+
[attr.aria-valuetext]="hours() === null ? null : pad(hours()!)"
|
|
1986
|
+
[attr.aria-label]="hoursLabel()"
|
|
1987
|
+
[attr.aria-invalid]="error() ? 'true' : null"
|
|
1988
|
+
[attr.aria-describedby]="describedBy()"
|
|
1989
|
+
[value]="hoursText()"
|
|
1990
|
+
[placeholder]="placeholderText()"
|
|
1991
|
+
[disabled]="isDisabled()"
|
|
1992
|
+
[readOnly]="readonly()"
|
|
1993
|
+
class="w-[2.6ch] bg-transparent text-center text-ink outline-none placeholder:text-ink-faint disabled:cursor-not-allowed"
|
|
1994
|
+
(focus)="selectAll($event)"
|
|
1995
|
+
(input)="onHoursInput($event)"
|
|
1996
|
+
(keydown)="onHoursKeydown($event)"
|
|
1997
|
+
/>
|
|
1998
|
+
<span class="px-px text-ink-soft" aria-hidden="true">:</span>
|
|
1999
|
+
<input
|
|
2000
|
+
#minutesEl
|
|
2001
|
+
[id]="fieldId + '-m'"
|
|
2002
|
+
type="text"
|
|
2003
|
+
inputmode="numeric"
|
|
2004
|
+
autocomplete="off"
|
|
2005
|
+
maxlength="2"
|
|
2006
|
+
role="spinbutton"
|
|
2007
|
+
aria-valuemin="0"
|
|
2008
|
+
aria-valuemax="59"
|
|
2009
|
+
[attr.aria-valuenow]="minutes()"
|
|
2010
|
+
[attr.aria-valuetext]="minutes() === null ? null : pad(minutes()!)"
|
|
2011
|
+
[attr.aria-label]="minutesLabel()"
|
|
2012
|
+
[attr.aria-invalid]="error() ? 'true' : null"
|
|
2013
|
+
[attr.aria-describedby]="describedBy()"
|
|
2014
|
+
[value]="minutesText()"
|
|
2015
|
+
[placeholder]="placeholderText()"
|
|
2016
|
+
[disabled]="isDisabled()"
|
|
2017
|
+
[readOnly]="readonly()"
|
|
2018
|
+
class="w-[2.6ch] bg-transparent text-center text-ink outline-none placeholder:text-ink-faint disabled:cursor-not-allowed"
|
|
2019
|
+
(focus)="selectAll($event)"
|
|
2020
|
+
(input)="onMinutesInput($event)"
|
|
2021
|
+
(keydown)="onMinutesKeydown($event)"
|
|
2022
|
+
/>
|
|
2023
|
+
</div>
|
|
2024
|
+
|
|
2025
|
+
@if (clearable() && hasValue() && !isDisabled() && !readonly()) {
|
|
2026
|
+
<button
|
|
2027
|
+
type="button"
|
|
2028
|
+
tabindex="-1"
|
|
2029
|
+
[attr.aria-label]="clearLabel()"
|
|
2030
|
+
class="ml-auto mr-2 inline-flex h-6 w-6 items-center justify-center rounded-full text-ink-faint hover:bg-black/5 hover:text-ink-soft dark:hover:bg-white/10"
|
|
2031
|
+
(click)="clear()"
|
|
2032
|
+
>
|
|
2033
|
+
<sds-icon name="x" [size]="15" />
|
|
2034
|
+
</button>
|
|
2035
|
+
}
|
|
2036
|
+
</div>
|
|
2037
|
+
|
|
2038
|
+
@if (error()) {
|
|
2039
|
+
<p [id]="fieldId + '-err'" class="mt-1.5 text-xs font-medium text-danger">{{ error() }}</p>
|
|
2040
|
+
} @else if (hint()) {
|
|
2041
|
+
<p [id]="fieldId + '-hint'" class="mt-1.5 text-xs text-ink-faint">{{ hint() }}</p>
|
|
2042
|
+
}
|
|
2043
|
+
`,
|
|
2044
|
+
}]
|
|
2045
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], showIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIcon", required: false }] }], minuteStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "minuteStep", required: false }] }], withSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "withSeconds", required: false }] }], placeholderText: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderText", required: false }] }], hoursLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoursLabel", required: false }] }], minutesLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "minutesLabel", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], hoursEl: [{ type: i0.ViewChild, args: ['hoursEl', { isSignal: true }] }], minutesEl: [{ type: i0.ViewChild, args: ['minutesEl', { isSignal: true }] }] } });
|
|
2046
|
+
|
|
2047
|
+
let nextId$k = 0;
|
|
2048
|
+
const SIZES$9 = {
|
|
2049
|
+
sm: 'min-h-9 text-sm',
|
|
2050
|
+
md: 'min-h-10 text-sm',
|
|
2051
|
+
lg: 'min-h-12 text-base',
|
|
2052
|
+
};
|
|
2053
|
+
/**
|
|
2054
|
+
* One language-aware text field for editing a translated value (a {@link SdsLocalizedValue} — a title,
|
|
2055
|
+
* label or description in several languages). Only one language is edited at a time; a compact switcher
|
|
2056
|
+
* sitting **inside the field** flips which translation you are editing, and a dot marks the languages that
|
|
2057
|
+
* already have content, so a partially-translated value is obvious at a glance.
|
|
2058
|
+
*
|
|
2059
|
+
* The active language defaults to {@link currentLanguage} when it is one of {@link languages}, otherwise the
|
|
2060
|
+
* first accepted language — so the field "opens on the language you are working in". Empty translations are
|
|
2061
|
+
* pruned from the emitted value, keeping the stored map clean.
|
|
2062
|
+
*
|
|
2063
|
+
* Implements ControlValueAccessor over the map — use with reactive forms or `[(ngModel)]`:
|
|
2064
|
+
* `<sds-localized-input [languages]="['fr','nl','en']" [(ngModel)]="title" />`.
|
|
2065
|
+
*/
|
|
2066
|
+
class SdsLocalizedInput {
|
|
2067
|
+
inputId = `sds-localized-input-${nextId$k++}`;
|
|
2068
|
+
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
2069
|
+
hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : []));
|
|
2070
|
+
placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
|
|
2071
|
+
/** The accepted language codes, in display order (e.g. `['fr','nl','en']`). */
|
|
2072
|
+
languages = input([], ...(ngDevMode ? [{ debugName: "languages" }] : []));
|
|
2073
|
+
/** Language to pre-select; used when it is one of {@link languages}, else the first accepted language. */
|
|
2074
|
+
currentLanguage = input(null, ...(ngDevMode ? [{ debugName: "currentLanguage" }] : []));
|
|
2075
|
+
multiline = input(false, ...(ngDevMode ? [{ debugName: "multiline", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
2076
|
+
rows = input(3, ...(ngDevMode ? [{ debugName: "rows", transform: numberAttribute }] : [{ transform: numberAttribute }]));
|
|
2077
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
|
|
2078
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
2079
|
+
/**
|
|
2080
|
+
* Accessible name for the in-field language switcher, and the "no translation yet" title suffix on each
|
|
2081
|
+
* language button. English defaults; translate them in a multi-language app (the buttons are
|
|
2082
|
+
* letter-only, so the title carries their meaning).
|
|
2083
|
+
*/
|
|
2084
|
+
languageSwitcherLabel = input('Language', ...(ngDevMode ? [{ debugName: "languageSwitcherLabel" }] : []));
|
|
2085
|
+
untranslatedLabel = input('not translated yet', ...(ngDevMode ? [{ debugName: "untranslatedLabel" }] : []));
|
|
2086
|
+
valueMap = signal({}, ...(ngDevMode ? [{ debugName: "valueMap" }] : []));
|
|
2087
|
+
/** Language chosen via the switcher; null means "follow currentLanguage / the first language". */
|
|
2088
|
+
manual = signal(null, ...(ngDevMode ? [{ debugName: "manual" }] : []));
|
|
2089
|
+
cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : []));
|
|
2090
|
+
isDisabled = computed(() => this.disabled() || this.cvaDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
|
|
2091
|
+
wrapperClasses = computed(() => {
|
|
2092
|
+
const parts = [SIZES$9[this.size()]];
|
|
2093
|
+
parts.push(this.isDisabled()
|
|
2094
|
+
? 'border-border opacity-60 cursor-not-allowed'
|
|
2095
|
+
: 'border-border focus-within:border-brand-500 focus-within:ring-2 focus-within:ring-brand-500/25');
|
|
2096
|
+
return parts.join(' ');
|
|
2097
|
+
}, ...(ngDevMode ? [{ debugName: "wrapperClasses" }] : []));
|
|
2098
|
+
/** The language currently being edited. */
|
|
2099
|
+
activeLang = computed(() => {
|
|
2100
|
+
const langs = this.languages();
|
|
2101
|
+
if (!langs.length) {
|
|
2102
|
+
return '';
|
|
2103
|
+
}
|
|
2104
|
+
const manual = this.manual();
|
|
2105
|
+
if (manual && langs.includes(manual)) {
|
|
2106
|
+
return manual;
|
|
2107
|
+
}
|
|
2108
|
+
const preferred = this.currentLanguage();
|
|
2109
|
+
return preferred && langs.includes(preferred) ? preferred : langs[0];
|
|
2110
|
+
}, ...(ngDevMode ? [{ debugName: "activeLang" }] : []));
|
|
2111
|
+
current = computed(() => this.valueMap()[this.activeLang()] ?? '', ...(ngDevMode ? [{ debugName: "current" }] : []));
|
|
2112
|
+
filled(lang) {
|
|
2113
|
+
const text = this.valueMap()[lang];
|
|
2114
|
+
return !!text && text.trim().length > 0;
|
|
2115
|
+
}
|
|
2116
|
+
fieldPlaceholder() {
|
|
2117
|
+
const base = this.placeholder();
|
|
2118
|
+
const lang = this.activeLang().toUpperCase();
|
|
2119
|
+
if (base) {
|
|
2120
|
+
return lang ? `${base} (${lang})` : base;
|
|
2121
|
+
}
|
|
2122
|
+
return lang ? `(${lang})` : '';
|
|
2123
|
+
}
|
|
2124
|
+
select(lang) {
|
|
2125
|
+
this.manual.set(lang);
|
|
2126
|
+
this.onTouched();
|
|
2127
|
+
}
|
|
2128
|
+
write(event) {
|
|
2129
|
+
const lang = this.activeLang();
|
|
2130
|
+
if (!lang) {
|
|
2131
|
+
return;
|
|
2132
|
+
}
|
|
2133
|
+
const text = event.target.value;
|
|
2134
|
+
const next = { ...this.valueMap() };
|
|
2135
|
+
if (text) {
|
|
2136
|
+
next[lang] = text;
|
|
2137
|
+
}
|
|
2138
|
+
else {
|
|
2139
|
+
delete next[lang];
|
|
2140
|
+
}
|
|
2141
|
+
this.valueMap.set(next);
|
|
2142
|
+
this.onChange(next);
|
|
2143
|
+
}
|
|
2144
|
+
// --- ControlValueAccessor -------------------------------------------------
|
|
2145
|
+
onChange = () => { };
|
|
2146
|
+
onTouched = () => { };
|
|
2147
|
+
writeValue(value) {
|
|
2148
|
+
this.valueMap.set(value ? { ...value } : {});
|
|
2149
|
+
}
|
|
2150
|
+
registerOnChange(fn) {
|
|
2151
|
+
this.onChange = fn;
|
|
2152
|
+
}
|
|
2153
|
+
registerOnTouched(fn) {
|
|
2154
|
+
this.onTouched = fn;
|
|
2155
|
+
}
|
|
2156
|
+
setDisabledState(isDisabled) {
|
|
2157
|
+
this.cvaDisabled.set(isDisabled);
|
|
2158
|
+
}
|
|
2159
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsLocalizedInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2160
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsLocalizedInput, isStandalone: true, selector: "sds-localized-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, languages: { classPropertyName: "languages", publicName: "languages", isSignal: true, isRequired: false, transformFunction: null }, currentLanguage: { classPropertyName: "currentLanguage", publicName: "currentLanguage", isSignal: true, isRequired: false, transformFunction: null }, multiline: { classPropertyName: "multiline", publicName: "multiline", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", 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 }, languageSwitcherLabel: { classPropertyName: "languageSwitcherLabel", publicName: "languageSwitcherLabel", isSignal: true, isRequired: false, transformFunction: null }, untranslatedLabel: { classPropertyName: "untranslatedLabel", publicName: "untranslatedLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, providers: [
|
|
2161
|
+
{
|
|
2162
|
+
provide: NG_VALUE_ACCESSOR,
|
|
2163
|
+
useExisting: forwardRef(() => SdsLocalizedInput),
|
|
2164
|
+
multi: true,
|
|
2165
|
+
},
|
|
2166
|
+
], ngImport: i0, template: `
|
|
2167
|
+
@if (label()) {
|
|
2168
|
+
<label [for]="inputId" class="mb-1.5 block text-sm font-medium text-ink">{{ label() }}</label>
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
<div
|
|
2172
|
+
class="relative flex items-stretch rounded-control border bg-surface text-ink transition-colors"
|
|
2173
|
+
[class]="wrapperClasses()"
|
|
2174
|
+
>
|
|
2175
|
+
@if (multiline()) {
|
|
2176
|
+
<textarea
|
|
2177
|
+
[id]="inputId"
|
|
2178
|
+
[rows]="rows()"
|
|
2179
|
+
[placeholder]="fieldPlaceholder()"
|
|
2180
|
+
[disabled]="isDisabled()"
|
|
2181
|
+
[value]="current()"
|
|
2182
|
+
(input)="write($event)"
|
|
2183
|
+
(blur)="onTouched()"
|
|
2184
|
+
class="min-w-0 flex-1 resize-y bg-transparent px-3.5 py-2 outline-none placeholder:text-current/50 disabled:cursor-not-allowed"
|
|
2185
|
+
></textarea>
|
|
2186
|
+
} @else {
|
|
2187
|
+
<input
|
|
2188
|
+
[id]="inputId"
|
|
2189
|
+
[placeholder]="fieldPlaceholder()"
|
|
2190
|
+
[disabled]="isDisabled()"
|
|
2191
|
+
[value]="current()"
|
|
2192
|
+
(input)="write($event)"
|
|
2193
|
+
(blur)="onTouched()"
|
|
2194
|
+
class="min-w-0 flex-1 bg-transparent px-3.5 outline-none placeholder:text-current/50 disabled:cursor-not-allowed"
|
|
2195
|
+
/>
|
|
2196
|
+
}
|
|
1623
2197
|
|
|
1624
2198
|
@if (languages().length > 1) {
|
|
1625
2199
|
<div
|
|
@@ -1746,7 +2320,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
1746
2320
|
}]
|
|
1747
2321
|
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], languages: [{ type: i0.Input, args: [{ isSignal: true, alias: "languages", required: false }] }], currentLanguage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentLanguage", required: false }] }], multiline: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiline", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], languageSwitcherLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "languageSwitcherLabel", required: false }] }], untranslatedLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "untranslatedLabel", required: false }] }] } });
|
|
1748
2322
|
|
|
1749
|
-
let nextId$
|
|
2323
|
+
let nextId$j = 0;
|
|
1750
2324
|
const SIZES$8 = {
|
|
1751
2325
|
md: 'h-12 w-11 text-lg',
|
|
1752
2326
|
lg: 'h-14 w-12 text-xl',
|
|
@@ -1763,7 +2337,7 @@ const SIZES$8 = {
|
|
|
1763
2337
|
* first box and have it land correctly.
|
|
1764
2338
|
*/
|
|
1765
2339
|
class SdsOtpInput {
|
|
1766
|
-
groupId = `sds-otp-${nextId$
|
|
2340
|
+
groupId = `sds-otp-${nextId$j++}`;
|
|
1767
2341
|
/** How many boxes / characters the code has. */
|
|
1768
2342
|
length = input(6, ...(ngDevMode ? [{ debugName: "length", transform: numberAttribute }] : [{ transform: numberAttribute }]));
|
|
1769
2343
|
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
@@ -2063,7 +2637,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
2063
2637
|
}]
|
|
2064
2638
|
}], ctorParameters: () => [], propDecorators: { length: [{ type: i0.Input, args: [{ isSignal: true, alias: "length", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], inputMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputMode", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], autoFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFocus", required: false }] }], cellLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellLabel", required: false }] }], completed: [{ type: i0.Output, args: ["completed"] }], cells: [{ type: i0.ViewChildren, args: ['cell', { isSignal: true }] }] } });
|
|
2065
2639
|
|
|
2066
|
-
let nextId$
|
|
2640
|
+
let nextId$i = 0;
|
|
2067
2641
|
const SIZES$7 = {
|
|
2068
2642
|
sm: 'text-sm px-3 py-2',
|
|
2069
2643
|
md: 'text-sm px-3.5 py-2.5',
|
|
@@ -2075,7 +2649,7 @@ const SIZES$7 = {
|
|
|
2075
2649
|
* [(ngModel)].
|
|
2076
2650
|
*/
|
|
2077
2651
|
class SdsTextarea {
|
|
2078
|
-
fieldId = `sds-textarea-${nextId$
|
|
2652
|
+
fieldId = `sds-textarea-${nextId$i++}`;
|
|
2079
2653
|
field = viewChild('field', ...(ngDevMode ? [{ debugName: "field" }] : []));
|
|
2080
2654
|
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
2081
2655
|
/** Accessible name used when no visible [label] is provided. */
|
|
@@ -2335,7 +2909,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
2335
2909
|
}]
|
|
2336
2910
|
}], ctorParameters: () => [], propDecorators: { field: [{ type: i0.ViewChild, args: ['field', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], autoResize: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoResize", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], showCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCount", required: false }] }], countLimitLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "countLimitLabel", required: false }] }], countLabelTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "countLabelTemplate", required: false }] }], resize: [{ type: i0.Input, args: [{ isSignal: true, alias: "resize", required: false }] }] } });
|
|
2337
2911
|
|
|
2338
|
-
let nextId$
|
|
2912
|
+
let nextId$h = 0;
|
|
2339
2913
|
const SIZES$6 = {
|
|
2340
2914
|
sm: { track: 'h-5 w-9', knob: 'h-4 w-4', on: 'translate-x-4', icon: 11 },
|
|
2341
2915
|
md: { track: 'h-6 w-11', knob: 'h-5 w-5', on: 'translate-x-5', icon: 13 },
|
|
@@ -2348,7 +2922,7 @@ const SIZES$6 = {
|
|
|
2348
2922
|
* states.
|
|
2349
2923
|
*/
|
|
2350
2924
|
class SdsToggleSwitch {
|
|
2351
|
-
fieldId = `sds-toggle-switch-${nextId$
|
|
2925
|
+
fieldId = `sds-toggle-switch-${nextId$h++}`;
|
|
2352
2926
|
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
2353
2927
|
/** Accessible name used when no visible [label] is provided. */
|
|
2354
2928
|
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
@@ -2592,7 +3166,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
2592
3166
|
}]
|
|
2593
3167
|
}], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], trueValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "trueValue", required: false }] }], falseValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "falseValue", required: false }] }], onIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "onIcon", required: false }] }], offIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "offIcon", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }] } });
|
|
2594
3168
|
|
|
2595
|
-
let nextId$
|
|
3169
|
+
let nextId$g = 0;
|
|
2596
3170
|
/**
|
|
2597
3171
|
* Boolean checkbox. Implements ControlValueAccessor — use with reactive forms
|
|
2598
3172
|
* or `[(ngModel)]`. Renders a styled box (brand-filled with a check when on)
|
|
@@ -2607,7 +3181,7 @@ let nextId$f = 0;
|
|
|
2607
3181
|
* <sds-checkbox label="Auto-merge" [(ngModel)]="autoMerge" />
|
|
2608
3182
|
*/
|
|
2609
3183
|
class SdsCheckbox {
|
|
2610
|
-
id = `sds-checkbox-${nextId$
|
|
3184
|
+
id = `sds-checkbox-${nextId$g++}`;
|
|
2611
3185
|
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
2612
3186
|
/** Accessible name used when no visible [label] is provided. */
|
|
2613
3187
|
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
@@ -2755,7 +3329,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
2755
3329
|
}]
|
|
2756
3330
|
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }] } });
|
|
2757
3331
|
|
|
2758
|
-
let nextId$
|
|
3332
|
+
let nextId$f = 0;
|
|
2759
3333
|
/**
|
|
2760
3334
|
* Mounted radios by group name. A pick updates the rest of its group, because a native radio says
|
|
2761
3335
|
* nothing when the browser unchecks it — without this a reactive form would leave the old choice drawn.
|
|
@@ -2781,7 +3355,7 @@ const groups = new Map();
|
|
|
2781
3355
|
* <sds-radio name="size" value="large" label="Grand" [(ngModel)]="size" />
|
|
2782
3356
|
*/
|
|
2783
3357
|
class SdsRadio {
|
|
2784
|
-
id = `sds-radio-${nextId$
|
|
3358
|
+
id = `sds-radio-${nextId$f++}`;
|
|
2785
3359
|
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
2786
3360
|
/** Accessible name used when no visible [label] is provided. */
|
|
2787
3361
|
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
@@ -2964,8 +3538,30 @@ const STATUS_COLOR = {
|
|
|
2964
3538
|
busy: 'bg-danger',
|
|
2965
3539
|
away: 'bg-warning',
|
|
2966
3540
|
};
|
|
3541
|
+
const SHAPE = {
|
|
3542
|
+
circle: 'rounded-full',
|
|
3543
|
+
rounded: 'rounded-control',
|
|
3544
|
+
square: 'rounded-none',
|
|
3545
|
+
};
|
|
2967
3546
|
/**
|
|
2968
|
-
*
|
|
3547
|
+
* Avatar showing an image, initials or a placeholder icon, with an optional status dot.
|
|
3548
|
+
*
|
|
3549
|
+
* - `shape`: `circle` (default, people), `rounded` (things: a dish photo, a product, a logo) or `square`.
|
|
3550
|
+
* - `fallbackIcon`: shown instead of initials when there is no image (or it fails to load) — e.g.
|
|
3551
|
+
* `utensils` or `image` for a menu item without a photo. The placeholder is drawn on `placeholderClass`
|
|
3552
|
+
* (a muted tile by default), not on the initials' brand fill.
|
|
3553
|
+
* - A broken `src` falls back to the icon or the initials instead of showing the browser's broken-image
|
|
3554
|
+
* glyph; a new `src` retries.
|
|
3555
|
+
* - `decorative`: the avatar only repeats a name that is already written next to it (a list row, a select
|
|
3556
|
+
* option), so the image gets `alt=""` and the whole avatar is hidden from assistive technology.
|
|
3557
|
+
*
|
|
3558
|
+
* @example
|
|
3559
|
+
* <sds-avatar name="Ada Lovelace" [src]="user.photoUrl" status="online" />
|
|
3560
|
+
*
|
|
3561
|
+
* @example
|
|
3562
|
+
* <!-- a menu item in a list: rounded photo, dish placeholder when there is none -->
|
|
3563
|
+
* <sds-avatar shape="rounded" [size]="32" [src]="item.thumbnailUrl" [name]="item.name"
|
|
3564
|
+
* fallbackIcon="utensils" decorative />
|
|
2969
3565
|
*/
|
|
2970
3566
|
class SdsAvatar {
|
|
2971
3567
|
name = input('', ...(ngDevMode ? [{ debugName: "name" }] : []));
|
|
@@ -2973,11 +3569,51 @@ class SdsAvatar {
|
|
|
2973
3569
|
src = input(null, ...(ngDevMode ? [{ debugName: "src" }] : []));
|
|
2974
3570
|
size = input(36, ...(ngDevMode ? [{ debugName: "size" }] : []));
|
|
2975
3571
|
status = input(null, ...(ngDevMode ? [{ debugName: "status" }] : []));
|
|
3572
|
+
/** `circle` (default), `rounded` (radius of a control) or `square`. */
|
|
3573
|
+
shape = input('circle', ...(ngDevMode ? [{ debugName: "shape" }] : []));
|
|
2976
3574
|
/** Tailwind bg utility for the initials background. */
|
|
2977
3575
|
bgClass = input('bg-brand-400', ...(ngDevMode ? [{ debugName: "bgClass" }] : []));
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
3576
|
+
/**
|
|
3577
|
+
* Icon shown instead of initials when there is no usable image (e.g. `utensils` or `image` for a dish).
|
|
3578
|
+
* Null (default) keeps the initials.
|
|
3579
|
+
*/
|
|
3580
|
+
fallbackIcon = input(null, ...(ngDevMode ? [{ debugName: "fallbackIcon" }] : []));
|
|
3581
|
+
/** Classes of the placeholder tile behind {@link fallbackIcon}: background and icon colour. */
|
|
3582
|
+
placeholderClass = input('bg-muted text-ink-faint', ...(ngDevMode ? [{ debugName: "placeholderClass" }] : []));
|
|
3583
|
+
/**
|
|
3584
|
+
* The avatar repeats a name already visible beside it: `alt=""` and `aria-hidden`, so a screen reader
|
|
3585
|
+
* does not read the name twice.
|
|
3586
|
+
*/
|
|
3587
|
+
decorative = input(false, ...(ngDevMode ? [{ debugName: "decorative", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
3588
|
+
/** Native lazy loading for the image (default on — avatars are often in long lists). */
|
|
3589
|
+
lazy = input(true, ...(ngDevMode ? [{ debugName: "lazy", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
3590
|
+
/** The `src` that failed to load. Any other `src` is tried (so a new photo URL retries). */
|
|
3591
|
+
failedSrc = signal(undefined, ...(ngDevMode ? [{ debugName: "failedSrc" }] : []));
|
|
3592
|
+
showImage = computed(() => {
|
|
3593
|
+
const src = this.src();
|
|
3594
|
+
return !!src && src !== this.failedSrc();
|
|
3595
|
+
}, ...(ngDevMode ? [{ debugName: "showImage" }] : []));
|
|
3596
|
+
shapeClass = computed(() => SHAPE[this.shape()], ...(ngDevMode ? [{ debugName: "shapeClass" }] : []));
|
|
3597
|
+
frameClass = computed(() => {
|
|
3598
|
+
const parts = [this.shapeClass()];
|
|
3599
|
+
if (this.showImage()) {
|
|
3600
|
+
parts.push('bg-muted');
|
|
3601
|
+
}
|
|
3602
|
+
else if (this.fallbackIcon()) {
|
|
3603
|
+
parts.push(this.placeholderClass());
|
|
3604
|
+
}
|
|
3605
|
+
else {
|
|
3606
|
+
parts.push(this.bgClass(), 'text-white');
|
|
3607
|
+
}
|
|
3608
|
+
// People get the soft ring the avatar always had; things get a hairline so a white plate on a white
|
|
3609
|
+
// page still has an edge.
|
|
3610
|
+
parts.push(this.shape() === 'circle' ? 'ring-2 ring-white/20' : 'ring-1 ring-inset ring-border');
|
|
3611
|
+
return parts.join(' ');
|
|
3612
|
+
}, ...(ngDevMode ? [{ debugName: "frameClass" }] : []));
|
|
3613
|
+
iconSize = computed(() => Math.max(12, Math.round(this.size() * 0.5)), ...(ngDevMode ? [{ debugName: "iconSize" }] : []));
|
|
3614
|
+
dotSize = computed(() => Math.max(8, Math.round(this.size() * 0.28)), ...(ngDevMode ? [{ debugName: "dotSize" }] : []));
|
|
3615
|
+
statusColor = computed(() => this.status() ? STATUS_COLOR[this.status()] : '', ...(ngDevMode ? [{ debugName: "statusColor" }] : []));
|
|
3616
|
+
resolvedInitials = computed(() => {
|
|
2981
3617
|
if (this.initials())
|
|
2982
3618
|
return this.initials().slice(0, 2).toUpperCase();
|
|
2983
3619
|
const parts = this.name().trim().split(/\s+/).filter(Boolean);
|
|
@@ -2988,19 +3624,31 @@ class SdsAvatar {
|
|
|
2988
3624
|
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
2989
3625
|
}, ...(ngDevMode ? [{ debugName: "resolvedInitials" }] : []));
|
|
2990
3626
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsAvatar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2991
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsAvatar, isStandalone: true, selector: "sds-avatar", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, initials: { classPropertyName: "initials", publicName: "initials", isSignal: true, isRequired: false, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, bgClass: { classPropertyName: "bgClass", publicName: "bgClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "inline-block shrink-0 align-middle" }, ngImport: i0, template: `
|
|
3627
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsAvatar, isStandalone: true, selector: "sds-avatar", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, initials: { classPropertyName: "initials", publicName: "initials", isSignal: true, isRequired: false, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, bgClass: { classPropertyName: "bgClass", publicName: "bgClass", isSignal: true, isRequired: false, transformFunction: null }, fallbackIcon: { classPropertyName: "fallbackIcon", publicName: "fallbackIcon", isSignal: true, isRequired: false, transformFunction: null }, placeholderClass: { classPropertyName: "placeholderClass", publicName: "placeholderClass", isSignal: true, isRequired: false, transformFunction: null }, decorative: { classPropertyName: "decorative", publicName: "decorative", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-hidden": "decorative() ? 'true' : null" }, classAttribute: "inline-block shrink-0 align-middle" }, ngImport: i0, template: `
|
|
2992
3628
|
<div
|
|
2993
|
-
class="relative grid place-items-center
|
|
2994
|
-
[class]="
|
|
3629
|
+
class="relative grid place-items-center font-semibold"
|
|
3630
|
+
[class]="frameClass()"
|
|
2995
3631
|
[style.width.px]="size()"
|
|
2996
3632
|
[style.height.px]="size()"
|
|
2997
3633
|
[style.fontSize.px]="size() * 0.4"
|
|
2998
3634
|
>
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3635
|
+
<!-- The image and placeholder are clipped to the shape; the status dot is not (it straddles the edge). -->
|
|
3636
|
+
<div class="absolute inset-0 grid place-items-center overflow-hidden" [class]="shapeClass()">
|
|
3637
|
+
@if (showImage()) {
|
|
3638
|
+
<img
|
|
3639
|
+
[src]="src()"
|
|
3640
|
+
[alt]="decorative() ? '' : name()"
|
|
3641
|
+
[attr.loading]="lazy() ? 'lazy' : null"
|
|
3642
|
+
decoding="async"
|
|
3643
|
+
class="h-full w-full object-cover"
|
|
3644
|
+
(error)="failedSrc.set(src())"
|
|
3645
|
+
/>
|
|
3646
|
+
} @else if (fallbackIcon()) {
|
|
3647
|
+
<sds-icon [name]="fallbackIcon()!" [size]="iconSize()" />
|
|
3648
|
+
} @else {
|
|
3649
|
+
<span>{{ resolvedInitials() }}</span>
|
|
3650
|
+
}
|
|
3651
|
+
</div>
|
|
3004
3652
|
|
|
3005
3653
|
@if (status()) {
|
|
3006
3654
|
<span
|
|
@@ -3011,27 +3659,43 @@ class SdsAvatar {
|
|
|
3011
3659
|
></span>
|
|
3012
3660
|
}
|
|
3013
3661
|
</div>
|
|
3014
|
-
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3662
|
+
`, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3015
3663
|
}
|
|
3016
3664
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsAvatar, decorators: [{
|
|
3017
3665
|
type: Component,
|
|
3018
3666
|
args: [{
|
|
3019
3667
|
selector: 'sds-avatar',
|
|
3020
3668
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3021
|
-
|
|
3669
|
+
imports: [SdsIcon],
|
|
3670
|
+
host: {
|
|
3671
|
+
class: 'inline-block shrink-0 align-middle',
|
|
3672
|
+
'[attr.aria-hidden]': "decorative() ? 'true' : null",
|
|
3673
|
+
},
|
|
3022
3674
|
template: `
|
|
3023
3675
|
<div
|
|
3024
|
-
class="relative grid place-items-center
|
|
3025
|
-
[class]="
|
|
3676
|
+
class="relative grid place-items-center font-semibold"
|
|
3677
|
+
[class]="frameClass()"
|
|
3026
3678
|
[style.width.px]="size()"
|
|
3027
3679
|
[style.height.px]="size()"
|
|
3028
3680
|
[style.fontSize.px]="size() * 0.4"
|
|
3029
3681
|
>
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3682
|
+
<!-- The image and placeholder are clipped to the shape; the status dot is not (it straddles the edge). -->
|
|
3683
|
+
<div class="absolute inset-0 grid place-items-center overflow-hidden" [class]="shapeClass()">
|
|
3684
|
+
@if (showImage()) {
|
|
3685
|
+
<img
|
|
3686
|
+
[src]="src()"
|
|
3687
|
+
[alt]="decorative() ? '' : name()"
|
|
3688
|
+
[attr.loading]="lazy() ? 'lazy' : null"
|
|
3689
|
+
decoding="async"
|
|
3690
|
+
class="h-full w-full object-cover"
|
|
3691
|
+
(error)="failedSrc.set(src())"
|
|
3692
|
+
/>
|
|
3693
|
+
} @else if (fallbackIcon()) {
|
|
3694
|
+
<sds-icon [name]="fallbackIcon()!" [size]="iconSize()" />
|
|
3695
|
+
} @else {
|
|
3696
|
+
<span>{{ resolvedInitials() }}</span>
|
|
3697
|
+
}
|
|
3698
|
+
</div>
|
|
3035
3699
|
|
|
3036
3700
|
@if (status()) {
|
|
3037
3701
|
<span
|
|
@@ -3044,7 +3708,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
3044
3708
|
</div>
|
|
3045
3709
|
`,
|
|
3046
3710
|
}]
|
|
3047
|
-
}], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], initials: [{ type: i0.Input, args: [{ isSignal: true, alias: "initials", required: false }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], bgClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "bgClass", required: false }] }] } });
|
|
3711
|
+
}], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], initials: [{ type: i0.Input, args: [{ isSignal: true, alias: "initials", required: false }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], bgClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "bgClass", required: false }] }], fallbackIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "fallbackIcon", required: false }] }], placeholderClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderClass", required: false }] }], decorative: [{ type: i0.Input, args: [{ isSignal: true, alias: "decorative", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }] } });
|
|
3048
3712
|
|
|
3049
3713
|
const VARIANTS$1 = {
|
|
3050
3714
|
neutral: 'bg-muted text-ink-soft border-border',
|
|
@@ -3267,7 +3931,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
3267
3931
|
|
|
3268
3932
|
/** Activation order: none → contains → may-contain → none. */
|
|
3269
3933
|
const ORDER = ['none', 'contains', 'may-contain'];
|
|
3270
|
-
let nextId$
|
|
3934
|
+
let nextId$e = 0;
|
|
3271
3935
|
/**
|
|
3272
3936
|
* A chip that cycles through three states — `none`, `contains`, `may-contain` — built for the 14-tile
|
|
3273
3937
|
* allergen declaration grid, but generic (any "no / yes / maybe" declaration).
|
|
@@ -3287,7 +3951,7 @@ let nextId$d = 0;
|
|
|
3287
3951
|
* containsLabel="Contains" mayContainLabel="May contain" noneLabel="Not present" />
|
|
3288
3952
|
*/
|
|
3289
3953
|
class SdsTriStateChip {
|
|
3290
|
-
chipId = `sds-tri-state-chip-${nextId$
|
|
3954
|
+
chipId = `sds-tri-state-chip-${nextId$e++}`;
|
|
3291
3955
|
/** Current state. Two-way: `[(value)]`. */
|
|
3292
3956
|
value = model('none', ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
3293
3957
|
/** Visible name (e.g. the allergen). Required — the chip has no other accessible name. */
|
|
@@ -3500,87 +4164,417 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
3500
4164
|
}
|
|
3501
4165
|
</span>
|
|
3502
4166
|
</button>
|
|
3503
|
-
|
|
3504
|
-
@if (hint()) {
|
|
3505
|
-
<span [id]="chipId + '-hint'" class="sr-only">{{ hint() }}</span>
|
|
3506
|
-
}
|
|
3507
|
-
<span class="sr-only" aria-live="polite" aria-atomic="true">{{ announcement() }}</span>
|
|
4167
|
+
|
|
4168
|
+
@if (hint()) {
|
|
4169
|
+
<span [id]="chipId + '-hint'" class="sr-only">{{ hint() }}</span>
|
|
4170
|
+
}
|
|
4171
|
+
<span class="sr-only" aria-live="polite" aria-atomic="true">{{ announcement() }}</span>
|
|
4172
|
+
`,
|
|
4173
|
+
}]
|
|
4174
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showState: [{ type: i0.Input, args: [{ isSignal: true, alias: "showState", required: false }] }], noneLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "noneLabel", required: false }] }], containsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "containsLabel", required: false }] }], mayContainLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "mayContainLabel", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }] } });
|
|
4175
|
+
|
|
4176
|
+
let nextId$d = 0;
|
|
4177
|
+
/**
|
|
4178
|
+
* Segmented control: a small set of mutually exclusive options shown side by side (e.g. "Pick exactly /
|
|
4179
|
+
* at least / up to"). Radio-group semantics: `role="radiogroup"` with `role="radio"` segments, one tab
|
|
4180
|
+
* stop, and the arrow keys (with Home/End) move AND select, like native radios. Disabled segments are
|
|
4181
|
+
* skipped.
|
|
4182
|
+
*
|
|
4183
|
+
* The value is a two-way `model` — bind `[(value)]`. Give the group a name with [ariaLabel] (or
|
|
4184
|
+
* [ariaLabelledby]); with [iconOnly] each segment keeps its label as its accessible name.
|
|
4185
|
+
*
|
|
4186
|
+
* @example
|
|
4187
|
+
* <sds-segmented ariaLabel="Rule" [options]="[{ value: 'exactly', label: 'Exactly' }, { value: 'upTo', label: 'Up to' }]"
|
|
4188
|
+
* [(value)]="rule" />
|
|
4189
|
+
*/
|
|
4190
|
+
class SdsSegmented {
|
|
4191
|
+
host = inject(ElementRef);
|
|
4192
|
+
baseId = `sds-segmented-${nextId$d++}`;
|
|
4193
|
+
/** Options: `{ value, label, icon?, disabled? }` objects, or bare strings/numbers. */
|
|
4194
|
+
options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
4195
|
+
/** Selected value. Two-way: `[(value)]`. */
|
|
4196
|
+
value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
4197
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
|
|
4198
|
+
/** Accessible name of the group. */
|
|
4199
|
+
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
4200
|
+
/** Id of an element that names the group (alternative to [ariaLabel]). */
|
|
4201
|
+
ariaLabelledby = input('', ...(ngDevMode ? [{ debugName: "ariaLabelledby" }] : []));
|
|
4202
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
4203
|
+
/** Stretch to the container width, segments sharing it equally. */
|
|
4204
|
+
fullWidth = input(false, ...(ngDevMode ? [{ debugName: "fullWidth", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
4205
|
+
/** Show icons only; labels stay as accessible names and tooltips. */
|
|
4206
|
+
iconOnly = input(false, ...(ngDevMode ? [{ debugName: "iconOnly", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
4207
|
+
segments = computed(() => this.options().map((o) => o !== null && typeof o === 'object'
|
|
4208
|
+
? { value: o.value, label: o.label, icon: o.icon ?? null, disabled: !!o.disabled }
|
|
4209
|
+
: { value: o, label: String(o), icon: null, disabled: false }), ...(ngDevMode ? [{ debugName: "segments" }] : []));
|
|
4210
|
+
selectedIndex = computed(() => this.segments().findIndex((s) => s.value === this.value()), ...(ngDevMode ? [{ debugName: "selectedIndex" }] : []));
|
|
4211
|
+
/** Roving tab stop: the selected segment, else the first enabled one. */
|
|
4212
|
+
tabStop = computed(() => {
|
|
4213
|
+
const sel = this.selectedIndex();
|
|
4214
|
+
if (sel >= 0 && !this.segments()[sel].disabled)
|
|
4215
|
+
return sel;
|
|
4216
|
+
return this.segments().findIndex((s) => !s.disabled);
|
|
4217
|
+
}, ...(ngDevMode ? [{ debugName: "tabStop" }] : []));
|
|
4218
|
+
isSelected(seg) {
|
|
4219
|
+
return seg.value === this.value();
|
|
4220
|
+
}
|
|
4221
|
+
segmentId(i) {
|
|
4222
|
+
return `${this.baseId}-${i}`;
|
|
4223
|
+
}
|
|
4224
|
+
segmentClass(seg) {
|
|
4225
|
+
const sm = this.size() === 'sm';
|
|
4226
|
+
const base = 'inline-flex min-w-0 items-center justify-center gap-1.5 whitespace-nowrap rounded-[calc(var(--radius-control)-2px)] ' +
|
|
4227
|
+
'font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 ' +
|
|
4228
|
+
'disabled:cursor-not-allowed disabled:opacity-50 ' +
|
|
4229
|
+
(sm ? 'h-7 px-2.5 text-xs' : 'h-8 px-3.5 text-sm') +
|
|
4230
|
+
(this.fullWidth() ? ' flex-1' : '') +
|
|
4231
|
+
(this.iconOnly() ? (sm ? ' w-8 px-0' : ' w-9 px-0') : '');
|
|
4232
|
+
// Selected: raised surface, ring and bold text — more than a tint, so it reads in greyscale too.
|
|
4233
|
+
return this.isSelected(seg)
|
|
4234
|
+
? `${base} bg-surface font-semibold text-ink shadow-sm ring-1 ring-border dark:bg-border`
|
|
4235
|
+
: `${base} cursor-pointer text-ink-soft hover:bg-surface/60 hover:text-ink`;
|
|
4236
|
+
}
|
|
4237
|
+
choose(i) {
|
|
4238
|
+
const seg = this.segments()[i];
|
|
4239
|
+
if (!seg || seg.disabled || this.disabled())
|
|
4240
|
+
return;
|
|
4241
|
+
this.value.set(seg.value);
|
|
4242
|
+
}
|
|
4243
|
+
onKeydown(event) {
|
|
4244
|
+
if (this.disabled())
|
|
4245
|
+
return;
|
|
4246
|
+
const segs = this.segments();
|
|
4247
|
+
if (!segs.length)
|
|
4248
|
+
return;
|
|
4249
|
+
const current = Number(event.target.dataset['index'] ?? this.tabStop());
|
|
4250
|
+
let next = null;
|
|
4251
|
+
switch (event.key) {
|
|
4252
|
+
case 'ArrowRight':
|
|
4253
|
+
case 'ArrowDown':
|
|
4254
|
+
next = this.step(current, 1);
|
|
4255
|
+
break;
|
|
4256
|
+
case 'ArrowLeft':
|
|
4257
|
+
case 'ArrowUp':
|
|
4258
|
+
next = this.step(current, -1);
|
|
4259
|
+
break;
|
|
4260
|
+
case 'Home':
|
|
4261
|
+
next = segs.findIndex((s) => !s.disabled);
|
|
4262
|
+
break;
|
|
4263
|
+
case 'End':
|
|
4264
|
+
for (let i = segs.length - 1; i >= 0; i--) {
|
|
4265
|
+
if (!segs[i].disabled) {
|
|
4266
|
+
next = i;
|
|
4267
|
+
break;
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
4270
|
+
break;
|
|
4271
|
+
default:
|
|
4272
|
+
return;
|
|
4273
|
+
}
|
|
4274
|
+
event.preventDefault();
|
|
4275
|
+
if (next === null || next < 0)
|
|
4276
|
+
return;
|
|
4277
|
+
this.choose(next);
|
|
4278
|
+
this.host.nativeElement.querySelector(`#${this.segmentId(next)}`)?.focus();
|
|
4279
|
+
}
|
|
4280
|
+
step(from, delta) {
|
|
4281
|
+
const segs = this.segments();
|
|
4282
|
+
let i = from;
|
|
4283
|
+
for (let n = 0; n < segs.length; n++) {
|
|
4284
|
+
i = (i + delta + segs.length) % segs.length;
|
|
4285
|
+
if (!segs[i].disabled)
|
|
4286
|
+
return i;
|
|
4287
|
+
}
|
|
4288
|
+
return null;
|
|
4289
|
+
}
|
|
4290
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsSegmented, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4291
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsSegmented, isStandalone: true, selector: "sds-segmented", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "ariaLabelledby", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, iconOnly: { classPropertyName: "iconOnly", publicName: "iconOnly", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class.w-full": "fullWidth()" }, classAttribute: "inline-flex max-w-full" }, ngImport: i0, template: `
|
|
4292
|
+
<div
|
|
4293
|
+
role="radiogroup"
|
|
4294
|
+
[attr.aria-label]="ariaLabel() || null"
|
|
4295
|
+
[attr.aria-labelledby]="ariaLabelledby() || null"
|
|
4296
|
+
[attr.aria-disabled]="disabled() ? 'true' : null"
|
|
4297
|
+
class="flex max-w-full items-stretch gap-0.5 overflow-x-auto rounded-control border border-border bg-muted p-0.5"
|
|
4298
|
+
[class.w-full]="fullWidth()"
|
|
4299
|
+
(keydown)="onKeydown($event)"
|
|
4300
|
+
>
|
|
4301
|
+
@for (seg of segments(); track $index; let i = $index) {
|
|
4302
|
+
<button
|
|
4303
|
+
type="button"
|
|
4304
|
+
role="radio"
|
|
4305
|
+
[id]="segmentId(i)"
|
|
4306
|
+
[attr.aria-checked]="isSelected(seg)"
|
|
4307
|
+
[attr.aria-label]="iconOnly() ? seg.label : null"
|
|
4308
|
+
[attr.title]="iconOnly() ? seg.label : null"
|
|
4309
|
+
[attr.tabindex]="i === tabStop() ? 0 : -1"
|
|
4310
|
+
[attr.data-index]="i"
|
|
4311
|
+
[disabled]="disabled() || seg.disabled"
|
|
4312
|
+
[class]="segmentClass(seg)"
|
|
4313
|
+
(click)="choose(i)"
|
|
4314
|
+
>
|
|
4315
|
+
@if (seg.icon) {
|
|
4316
|
+
<sds-icon [name]="seg.icon" [size]="size() === 'sm' ? 15 : 17" class="shrink-0" />
|
|
4317
|
+
}
|
|
4318
|
+
@if (!iconOnly()) {
|
|
4319
|
+
<span class="truncate">{{ seg.label }}</span>
|
|
4320
|
+
}
|
|
4321
|
+
</button>
|
|
4322
|
+
}
|
|
4323
|
+
</div>
|
|
4324
|
+
`, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4325
|
+
}
|
|
4326
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsSegmented, decorators: [{
|
|
4327
|
+
type: Component,
|
|
4328
|
+
args: [{
|
|
4329
|
+
selector: 'sds-segmented',
|
|
4330
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
4331
|
+
imports: [SdsIcon],
|
|
4332
|
+
host: { class: 'inline-flex max-w-full', '[class.w-full]': 'fullWidth()' },
|
|
4333
|
+
template: `
|
|
4334
|
+
<div
|
|
4335
|
+
role="radiogroup"
|
|
4336
|
+
[attr.aria-label]="ariaLabel() || null"
|
|
4337
|
+
[attr.aria-labelledby]="ariaLabelledby() || null"
|
|
4338
|
+
[attr.aria-disabled]="disabled() ? 'true' : null"
|
|
4339
|
+
class="flex max-w-full items-stretch gap-0.5 overflow-x-auto rounded-control border border-border bg-muted p-0.5"
|
|
4340
|
+
[class.w-full]="fullWidth()"
|
|
4341
|
+
(keydown)="onKeydown($event)"
|
|
4342
|
+
>
|
|
4343
|
+
@for (seg of segments(); track $index; let i = $index) {
|
|
4344
|
+
<button
|
|
4345
|
+
type="button"
|
|
4346
|
+
role="radio"
|
|
4347
|
+
[id]="segmentId(i)"
|
|
4348
|
+
[attr.aria-checked]="isSelected(seg)"
|
|
4349
|
+
[attr.aria-label]="iconOnly() ? seg.label : null"
|
|
4350
|
+
[attr.title]="iconOnly() ? seg.label : null"
|
|
4351
|
+
[attr.tabindex]="i === tabStop() ? 0 : -1"
|
|
4352
|
+
[attr.data-index]="i"
|
|
4353
|
+
[disabled]="disabled() || seg.disabled"
|
|
4354
|
+
[class]="segmentClass(seg)"
|
|
4355
|
+
(click)="choose(i)"
|
|
4356
|
+
>
|
|
4357
|
+
@if (seg.icon) {
|
|
4358
|
+
<sds-icon [name]="seg.icon" [size]="size() === 'sm' ? 15 : 17" class="shrink-0" />
|
|
4359
|
+
}
|
|
4360
|
+
@if (!iconOnly()) {
|
|
4361
|
+
<span class="truncate">{{ seg.label }}</span>
|
|
4362
|
+
}
|
|
4363
|
+
</button>
|
|
4364
|
+
}
|
|
4365
|
+
</div>
|
|
4366
|
+
`,
|
|
4367
|
+
}]
|
|
4368
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledby", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], iconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnly", required: false }] }] } });
|
|
4369
|
+
|
|
4370
|
+
const SIZE = {
|
|
4371
|
+
sm: 'h-7 gap-1 px-2.5 text-xs',
|
|
4372
|
+
md: 'h-8 gap-1.5 px-3 text-sm',
|
|
4373
|
+
};
|
|
4374
|
+
/**
|
|
4375
|
+
* Chip look shared by sds-chip and sds-chip-group. Selected is more than a tint — a brand border, bolder
|
|
4376
|
+
* text and (by default) a check — so it reads in greyscale too. In dark mode the brand text/tint are
|
|
4377
|
+
* swapped for ink/muted (the library's rule for a monochrome brand's dark ramp).
|
|
4378
|
+
*/
|
|
4379
|
+
function chipClass(size, selected) {
|
|
4380
|
+
const base = 'inline-flex shrink-0 select-none items-center whitespace-nowrap rounded-full border font-medium ' +
|
|
4381
|
+
'transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 ' +
|
|
4382
|
+
'focus-visible:ring-offset-1 focus-visible:ring-offset-surface disabled:cursor-not-allowed disabled:opacity-50 ' +
|
|
4383
|
+
SIZE[size];
|
|
4384
|
+
return selected
|
|
4385
|
+
? `${base} cursor-pointer border-brand-600 bg-brand-50 font-semibold text-brand-700 ` +
|
|
4386
|
+
'dark:border-ink-soft dark:bg-muted dark:text-ink'
|
|
4387
|
+
: `${base} cursor-pointer border-border bg-surface text-ink-soft hover:border-ink-faint hover:text-ink`;
|
|
4388
|
+
}
|
|
4389
|
+
const COUNT_CLASS = 'inline-flex min-w-5 items-center justify-center rounded-full bg-ink/5 px-1.5 text-[11px] font-semibold tabular-nums dark:bg-white/10';
|
|
4390
|
+
/**
|
|
4391
|
+
* A single on/off filter chip (`aria-pressed`), e.g. "Sold out only" or "With comments". Two-way
|
|
4392
|
+
* `[(selected)]`. For a set of chips — one or several picked — use `sds-chip-group`.
|
|
4393
|
+
*
|
|
4394
|
+
* @example
|
|
4395
|
+
* <sds-chip label="Sold out only" icon="ban" [(selected)]="soldOutOnly" />
|
|
4396
|
+
*/
|
|
4397
|
+
class SdsChip {
|
|
4398
|
+
/** Visible text — also the chip's accessible name. */
|
|
4399
|
+
label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
4400
|
+
/** On/off. Two-way: `[(selected)]`. */
|
|
4401
|
+
selected = model(false, ...(ngDevMode ? [{ debugName: "selected" }] : []));
|
|
4402
|
+
icon = input(null, ...(ngDevMode ? [{ debugName: "icon" }] : []));
|
|
4403
|
+
count = input(null, ...(ngDevMode ? [{ debugName: "count" }] : []));
|
|
4404
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
|
|
4405
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
4406
|
+
/** Replace the icon with a check while selected. */
|
|
4407
|
+
showCheck = input(true, ...(ngDevMode ? [{ debugName: "showCheck", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
4408
|
+
countClass = COUNT_CLASS;
|
|
4409
|
+
classes = computed(() => chipClass(this.size(), this.selected()), ...(ngDevMode ? [{ debugName: "classes" }] : []));
|
|
4410
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4411
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsChip, isStandalone: true, selector: "sds-chip", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showCheck: { classPropertyName: "showCheck", publicName: "showCheck", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selectedChange" }, host: { classAttribute: "inline-flex" }, ngImport: i0, template: `
|
|
4412
|
+
<button
|
|
4413
|
+
type="button"
|
|
4414
|
+
[attr.aria-pressed]="selected()"
|
|
4415
|
+
[disabled]="disabled()"
|
|
4416
|
+
[class]="classes()"
|
|
4417
|
+
(click)="selected.set(!selected())"
|
|
4418
|
+
>
|
|
4419
|
+
@if (selected() && showCheck()) {
|
|
4420
|
+
<sds-icon
|
|
4421
|
+
name="check"
|
|
4422
|
+
[size]="size() === 'sm' ? 12 : 14"
|
|
4423
|
+
[strokeWidth]="3"
|
|
4424
|
+
class="shrink-0"
|
|
4425
|
+
/>
|
|
4426
|
+
} @else if (icon()) {
|
|
4427
|
+
<sds-icon [name]="icon()!" [size]="size() === 'sm' ? 13 : 15" class="shrink-0" />
|
|
4428
|
+
}
|
|
4429
|
+
<span class="truncate">{{ label() }}</span>
|
|
4430
|
+
@if (count() !== null && count() !== undefined) {
|
|
4431
|
+
<span [class]="countClass">{{ count() }}</span>
|
|
4432
|
+
}
|
|
4433
|
+
</button>
|
|
4434
|
+
`, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4435
|
+
}
|
|
4436
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsChip, decorators: [{
|
|
4437
|
+
type: Component,
|
|
4438
|
+
args: [{
|
|
4439
|
+
selector: 'sds-chip',
|
|
4440
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
4441
|
+
imports: [SdsIcon],
|
|
4442
|
+
host: { class: 'inline-flex' },
|
|
4443
|
+
template: `
|
|
4444
|
+
<button
|
|
4445
|
+
type="button"
|
|
4446
|
+
[attr.aria-pressed]="selected()"
|
|
4447
|
+
[disabled]="disabled()"
|
|
4448
|
+
[class]="classes()"
|
|
4449
|
+
(click)="selected.set(!selected())"
|
|
4450
|
+
>
|
|
4451
|
+
@if (selected() && showCheck()) {
|
|
4452
|
+
<sds-icon
|
|
4453
|
+
name="check"
|
|
4454
|
+
[size]="size() === 'sm' ? 12 : 14"
|
|
4455
|
+
[strokeWidth]="3"
|
|
4456
|
+
class="shrink-0"
|
|
4457
|
+
/>
|
|
4458
|
+
} @else if (icon()) {
|
|
4459
|
+
<sds-icon [name]="icon()!" [size]="size() === 'sm' ? 13 : 15" class="shrink-0" />
|
|
4460
|
+
}
|
|
4461
|
+
<span class="truncate">{{ label() }}</span>
|
|
4462
|
+
@if (count() !== null && count() !== undefined) {
|
|
4463
|
+
<span [class]="countClass">{{ count() }}</span>
|
|
4464
|
+
}
|
|
4465
|
+
</button>
|
|
3508
4466
|
`,
|
|
3509
4467
|
}]
|
|
3510
|
-
}], propDecorators: {
|
|
3511
|
-
|
|
4468
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }, { type: i0.Output, args: ["selectedChange"] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCheck", required: false }] }] } });
|
|
3512
4469
|
let nextId$c = 0;
|
|
3513
4470
|
/**
|
|
3514
|
-
*
|
|
3515
|
-
*
|
|
3516
|
-
*
|
|
3517
|
-
* skipped.
|
|
4471
|
+
* A set of chips: pick ONE (a filter row such as "All · Missing photo · Hidden", VAT presets, a date
|
|
4472
|
+
* range) or, with `multiple`, SEVERAL (categories of an item, days of the week, dietary tags). Unlike
|
|
4473
|
+
* sds-segmented (a compact joined bar), chips wrap onto several lines and can carry a count.
|
|
3518
4474
|
*
|
|
3519
|
-
*
|
|
3520
|
-
*
|
|
4475
|
+
* Value: two-way `[(value)]`, or a form control (`formControlName` / `[(ngModel)]` —
|
|
4476
|
+
* ControlValueAccessor). Single mode holds the picked value (or null); `multiple` holds an array, in the
|
|
4477
|
+
* order of the options.
|
|
4478
|
+
*
|
|
4479
|
+
* Accessibility: single mode is a `radiogroup` (one tab stop, arrows/Home/End move and select, like
|
|
4480
|
+
* native radios); `multiple` is a `group` of `aria-pressed` toggle buttons. Name it with [ariaLabel] or
|
|
4481
|
+
* [ariaLabelledby].
|
|
3521
4482
|
*
|
|
3522
4483
|
* @example
|
|
3523
|
-
* <sds-
|
|
3524
|
-
*
|
|
4484
|
+
* <sds-chip-group ariaLabel="Filter" [options]="filters()" [(value)]="issue" />
|
|
4485
|
+
* <!-- filters = [{ value: null, label: 'All', count: 34 }, { value: 'NO_PHOTO', label: 'Missing photo', count: 5 }] -->
|
|
4486
|
+
*
|
|
4487
|
+
* @example
|
|
4488
|
+
* <sds-chip-group ariaLabel="Categories" multiple [options]="categories()" formControlName="categoryIds" />
|
|
3525
4489
|
*/
|
|
3526
|
-
class
|
|
4490
|
+
class SdsChipGroup {
|
|
3527
4491
|
host = inject(ElementRef);
|
|
3528
|
-
baseId = `sds-
|
|
3529
|
-
/** Options: `{ value, label, icon?, disabled? }` objects, or bare strings/numbers. */
|
|
4492
|
+
baseId = `sds-chip-group-${nextId$c++}`;
|
|
4493
|
+
/** Options: `{ value, label, icon?, count?, disabled? }` objects, or bare strings/numbers. */
|
|
3530
4494
|
options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
3531
|
-
/**
|
|
4495
|
+
/** Picked value (single) or values (multiple). Two-way: `[(value)]`. */
|
|
3532
4496
|
value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
4497
|
+
/** Allow several chips at once; the value becomes an array. */
|
|
4498
|
+
multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
4499
|
+
/** Single mode: clicking the picked chip again clears the value to null. */
|
|
4500
|
+
deselectable = input(false, ...(ngDevMode ? [{ debugName: "deselectable", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
3533
4501
|
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
|
|
3534
|
-
/** Accessible name of the group. */
|
|
3535
4502
|
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
3536
|
-
/** Id of an element that names the group (alternative to [ariaLabel]). */
|
|
3537
4503
|
ariaLabelledby = input('', ...(ngDevMode ? [{ debugName: "ariaLabelledby" }] : []));
|
|
3538
4504
|
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
3539
|
-
/**
|
|
3540
|
-
|
|
3541
|
-
/**
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
4505
|
+
/** Wrap onto several lines (default) or scroll horizontally on one line. */
|
|
4506
|
+
wrap = input(true, ...(ngDevMode ? [{ debugName: "wrap", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
4507
|
+
/** `multiple`: a check replaces the icon on picked chips. */
|
|
4508
|
+
showCheck = input(true, ...(ngDevMode ? [{ debugName: "showCheck", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
4509
|
+
countClass = COUNT_CLASS;
|
|
4510
|
+
cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : []));
|
|
4511
|
+
isDisabled = computed(() => this.disabled() || this.cvaDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
|
|
4512
|
+
chips = computed(() => this.options().map((o) => o !== null && typeof o === 'object'
|
|
4513
|
+
? {
|
|
4514
|
+
value: o.value,
|
|
4515
|
+
label: o.label,
|
|
4516
|
+
icon: o.icon ?? null,
|
|
4517
|
+
count: o.count ?? null,
|
|
4518
|
+
disabled: !!o.disabled,
|
|
4519
|
+
}
|
|
4520
|
+
: { value: o, label: String(o), icon: null, count: null, disabled: false }), ...(ngDevMode ? [{ debugName: "chips" }] : []));
|
|
4521
|
+
selectedValues = computed(() => {
|
|
4522
|
+
const v = this.value();
|
|
4523
|
+
if (this.multiple())
|
|
4524
|
+
return Array.isArray(v) ? v : [];
|
|
4525
|
+
return [v];
|
|
4526
|
+
}, ...(ngDevMode ? [{ debugName: "selectedValues" }] : []));
|
|
4527
|
+
/** Single mode's roving tab stop: the picked chip, else the first enabled one. */
|
|
3548
4528
|
tabStop = computed(() => {
|
|
3549
|
-
const
|
|
3550
|
-
|
|
4529
|
+
const chips = this.chips();
|
|
4530
|
+
const sel = chips.findIndex((c) => this.isSelected(c));
|
|
4531
|
+
if (sel >= 0 && !chips[sel].disabled)
|
|
3551
4532
|
return sel;
|
|
3552
|
-
return
|
|
4533
|
+
return chips.findIndex((c) => !c.disabled);
|
|
3553
4534
|
}, ...(ngDevMode ? [{ debugName: "tabStop" }] : []));
|
|
3554
|
-
isSelected(
|
|
3555
|
-
return
|
|
4535
|
+
isSelected(chip) {
|
|
4536
|
+
return this.selectedValues().includes(chip.value);
|
|
3556
4537
|
}
|
|
3557
|
-
|
|
4538
|
+
chipId(i) {
|
|
3558
4539
|
return `${this.baseId}-${i}`;
|
|
3559
4540
|
}
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
const base = 'inline-flex min-w-0 items-center justify-center gap-1.5 whitespace-nowrap rounded-[calc(var(--radius-control)-2px)] ' +
|
|
3563
|
-
'font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 ' +
|
|
3564
|
-
'disabled:cursor-not-allowed disabled:opacity-50 ' +
|
|
3565
|
-
(sm ? 'h-7 px-2.5 text-xs' : 'h-8 px-3.5 text-sm') +
|
|
3566
|
-
(this.fullWidth() ? ' flex-1' : '') +
|
|
3567
|
-
(this.iconOnly() ? (sm ? ' w-8 px-0' : ' w-9 px-0') : '');
|
|
3568
|
-
// Selected: raised surface, ring and bold text — more than a tint, so it reads in greyscale too.
|
|
3569
|
-
return this.isSelected(seg)
|
|
3570
|
-
? `${base} bg-surface font-semibold text-ink shadow-sm ring-1 ring-border dark:bg-border`
|
|
3571
|
-
: `${base} cursor-pointer text-ink-soft hover:bg-surface/60 hover:text-ink`;
|
|
4541
|
+
classFor(chip) {
|
|
4542
|
+
return chipClass(this.size(), this.isSelected(chip));
|
|
3572
4543
|
}
|
|
3573
|
-
|
|
3574
|
-
const
|
|
3575
|
-
if (!
|
|
4544
|
+
toggle(i) {
|
|
4545
|
+
const chip = this.chips()[i];
|
|
4546
|
+
if (!chip || chip.disabled || this.isDisabled())
|
|
3576
4547
|
return;
|
|
3577
|
-
|
|
4548
|
+
let next;
|
|
4549
|
+
if (this.multiple()) {
|
|
4550
|
+
const picked = new Set(this.selectedValues());
|
|
4551
|
+
if (picked.has(chip.value))
|
|
4552
|
+
picked.delete(chip.value);
|
|
4553
|
+
else
|
|
4554
|
+
picked.add(chip.value);
|
|
4555
|
+
// Keep the options' order, whatever the click order.
|
|
4556
|
+
next = this.chips()
|
|
4557
|
+
.map((c) => c.value)
|
|
4558
|
+
.filter((v) => picked.has(v));
|
|
4559
|
+
}
|
|
4560
|
+
else {
|
|
4561
|
+
if (this.isSelected(chip)) {
|
|
4562
|
+
if (!this.deselectable())
|
|
4563
|
+
return;
|
|
4564
|
+
next = null;
|
|
4565
|
+
}
|
|
4566
|
+
else {
|
|
4567
|
+
next = chip.value;
|
|
4568
|
+
}
|
|
4569
|
+
}
|
|
4570
|
+
this.value.set(next);
|
|
4571
|
+
this.onChange(next);
|
|
3578
4572
|
}
|
|
3579
4573
|
onKeydown(event) {
|
|
3580
|
-
if (this.
|
|
4574
|
+
if (this.multiple() || this.isDisabled())
|
|
3581
4575
|
return;
|
|
3582
|
-
const
|
|
3583
|
-
if (!
|
|
4576
|
+
const chips = this.chips();
|
|
4577
|
+
if (!chips.length)
|
|
3584
4578
|
return;
|
|
3585
4579
|
const current = Number(event.target.dataset['index'] ?? this.tabStop());
|
|
3586
4580
|
let next = null;
|
|
@@ -3594,11 +4588,11 @@ class SdsSegmented {
|
|
|
3594
4588
|
next = this.step(current, -1);
|
|
3595
4589
|
break;
|
|
3596
4590
|
case 'Home':
|
|
3597
|
-
next =
|
|
4591
|
+
next = chips.findIndex((c) => !c.disabled);
|
|
3598
4592
|
break;
|
|
3599
4593
|
case 'End':
|
|
3600
|
-
for (let i =
|
|
3601
|
-
if (!
|
|
4594
|
+
for (let i = chips.length - 1; i >= 0; i--) {
|
|
4595
|
+
if (!chips[i].disabled) {
|
|
3602
4596
|
next = i;
|
|
3603
4597
|
break;
|
|
3604
4598
|
}
|
|
@@ -3610,98 +4604,136 @@ class SdsSegmented {
|
|
|
3610
4604
|
event.preventDefault();
|
|
3611
4605
|
if (next === null || next < 0)
|
|
3612
4606
|
return;
|
|
3613
|
-
this.
|
|
3614
|
-
|
|
4607
|
+
if (!this.isSelected(chips[next]))
|
|
4608
|
+
this.toggle(next);
|
|
4609
|
+
this.host.nativeElement.querySelector(`#${this.chipId(next)}`)?.focus();
|
|
3615
4610
|
}
|
|
3616
4611
|
step(from, delta) {
|
|
3617
|
-
const
|
|
4612
|
+
const chips = this.chips();
|
|
3618
4613
|
let i = from;
|
|
3619
|
-
for (let n = 0; n <
|
|
3620
|
-
i = (i + delta +
|
|
3621
|
-
if (!
|
|
4614
|
+
for (let n = 0; n < chips.length; n++) {
|
|
4615
|
+
i = (i + delta + chips.length) % chips.length;
|
|
4616
|
+
if (!chips[i].disabled)
|
|
3622
4617
|
return i;
|
|
3623
4618
|
}
|
|
3624
4619
|
return null;
|
|
3625
4620
|
}
|
|
3626
|
-
|
|
3627
|
-
|
|
4621
|
+
// --- ControlValueAccessor -------------------------------------------------
|
|
4622
|
+
onChange = () => { };
|
|
4623
|
+
onTouched = () => { };
|
|
4624
|
+
writeValue(value) {
|
|
4625
|
+
// Normalised on read (selectedValues), so a null written to a `multiple` group is simply "none".
|
|
4626
|
+
this.value.set(value ?? null);
|
|
4627
|
+
}
|
|
4628
|
+
registerOnChange(fn) {
|
|
4629
|
+
this.onChange = fn;
|
|
4630
|
+
}
|
|
4631
|
+
registerOnTouched(fn) {
|
|
4632
|
+
this.onTouched = fn;
|
|
4633
|
+
}
|
|
4634
|
+
setDisabledState(isDisabled) {
|
|
4635
|
+
this.cvaDisabled.set(isDisabled);
|
|
4636
|
+
}
|
|
4637
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsChipGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4638
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsChipGroup, isStandalone: true, selector: "sds-chip-group", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, deselectable: { classPropertyName: "deselectable", publicName: "deselectable", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "ariaLabelledby", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, wrap: { classPropertyName: "wrap", publicName: "wrap", isSignal: true, isRequired: false, transformFunction: null }, showCheck: { classPropertyName: "showCheck", publicName: "showCheck", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { classAttribute: "block" }, providers: [
|
|
4639
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SdsChipGroup), multi: true },
|
|
4640
|
+
], ngImport: i0, template: `
|
|
3628
4641
|
<div
|
|
3629
|
-
role="radiogroup"
|
|
4642
|
+
[attr.role]="multiple() ? 'group' : 'radiogroup'"
|
|
3630
4643
|
[attr.aria-label]="ariaLabel() || null"
|
|
3631
4644
|
[attr.aria-labelledby]="ariaLabelledby() || null"
|
|
3632
|
-
[attr.aria-disabled]="
|
|
3633
|
-
class="flex
|
|
3634
|
-
[class
|
|
4645
|
+
[attr.aria-disabled]="isDisabled() ? 'true' : null"
|
|
4646
|
+
class="flex gap-2"
|
|
4647
|
+
[class]="wrap() ? 'flex-wrap' : 'overflow-x-auto pb-1'"
|
|
3635
4648
|
(keydown)="onKeydown($event)"
|
|
4649
|
+
(focusout)="onTouched()"
|
|
3636
4650
|
>
|
|
3637
|
-
@for (
|
|
4651
|
+
@for (chip of chips(); track $index; let i = $index) {
|
|
3638
4652
|
<button
|
|
3639
4653
|
type="button"
|
|
3640
|
-
|
|
3641
|
-
[
|
|
3642
|
-
[attr.aria-checked]="isSelected(
|
|
3643
|
-
[attr.aria-
|
|
3644
|
-
[attr.
|
|
3645
|
-
[attr.tabindex]="i === tabStop() ? 0 : -1"
|
|
4654
|
+
[id]="chipId(i)"
|
|
4655
|
+
[attr.role]="multiple() ? null : 'radio'"
|
|
4656
|
+
[attr.aria-checked]="multiple() ? null : isSelected(chip)"
|
|
4657
|
+
[attr.aria-pressed]="multiple() ? isSelected(chip) : null"
|
|
4658
|
+
[attr.tabindex]="multiple() || i === tabStop() ? 0 : -1"
|
|
3646
4659
|
[attr.data-index]="i"
|
|
3647
|
-
[disabled]="
|
|
3648
|
-
[class]="
|
|
3649
|
-
(click)="
|
|
4660
|
+
[disabled]="isDisabled() || chip.disabled"
|
|
4661
|
+
[class]="classFor(chip)"
|
|
4662
|
+
(click)="toggle(i)"
|
|
3650
4663
|
>
|
|
3651
|
-
@if (
|
|
3652
|
-
<sds-icon
|
|
4664
|
+
@if (isSelected(chip) && multiple() && showCheck()) {
|
|
4665
|
+
<sds-icon
|
|
4666
|
+
name="check"
|
|
4667
|
+
[size]="size() === 'sm' ? 12 : 14"
|
|
4668
|
+
[strokeWidth]="3"
|
|
4669
|
+
class="shrink-0"
|
|
4670
|
+
/>
|
|
4671
|
+
} @else if (chip.icon) {
|
|
4672
|
+
<sds-icon [name]="chip.icon" [size]="size() === 'sm' ? 13 : 15" class="shrink-0" />
|
|
3653
4673
|
}
|
|
3654
|
-
|
|
3655
|
-
|
|
4674
|
+
<span class="truncate">{{ chip.label }}</span>
|
|
4675
|
+
@if (chip.count !== null) {
|
|
4676
|
+
<span [class]="countClass">{{ chip.count }}</span>
|
|
3656
4677
|
}
|
|
3657
4678
|
</button>
|
|
3658
4679
|
}
|
|
3659
4680
|
</div>
|
|
3660
4681
|
`, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3661
4682
|
}
|
|
3662
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type:
|
|
4683
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsChipGroup, decorators: [{
|
|
3663
4684
|
type: Component,
|
|
3664
4685
|
args: [{
|
|
3665
|
-
selector: 'sds-
|
|
4686
|
+
selector: 'sds-chip-group',
|
|
3666
4687
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3667
4688
|
imports: [SdsIcon],
|
|
3668
|
-
|
|
4689
|
+
providers: [
|
|
4690
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SdsChipGroup), multi: true },
|
|
4691
|
+
],
|
|
4692
|
+
host: { class: 'block' },
|
|
3669
4693
|
template: `
|
|
3670
4694
|
<div
|
|
3671
|
-
role="radiogroup"
|
|
4695
|
+
[attr.role]="multiple() ? 'group' : 'radiogroup'"
|
|
3672
4696
|
[attr.aria-label]="ariaLabel() || null"
|
|
3673
4697
|
[attr.aria-labelledby]="ariaLabelledby() || null"
|
|
3674
|
-
[attr.aria-disabled]="
|
|
3675
|
-
class="flex
|
|
3676
|
-
[class
|
|
4698
|
+
[attr.aria-disabled]="isDisabled() ? 'true' : null"
|
|
4699
|
+
class="flex gap-2"
|
|
4700
|
+
[class]="wrap() ? 'flex-wrap' : 'overflow-x-auto pb-1'"
|
|
3677
4701
|
(keydown)="onKeydown($event)"
|
|
4702
|
+
(focusout)="onTouched()"
|
|
3678
4703
|
>
|
|
3679
|
-
@for (
|
|
4704
|
+
@for (chip of chips(); track $index; let i = $index) {
|
|
3680
4705
|
<button
|
|
3681
4706
|
type="button"
|
|
3682
|
-
|
|
3683
|
-
[
|
|
3684
|
-
[attr.aria-checked]="isSelected(
|
|
3685
|
-
[attr.aria-
|
|
3686
|
-
[attr.
|
|
3687
|
-
[attr.tabindex]="i === tabStop() ? 0 : -1"
|
|
4707
|
+
[id]="chipId(i)"
|
|
4708
|
+
[attr.role]="multiple() ? null : 'radio'"
|
|
4709
|
+
[attr.aria-checked]="multiple() ? null : isSelected(chip)"
|
|
4710
|
+
[attr.aria-pressed]="multiple() ? isSelected(chip) : null"
|
|
4711
|
+
[attr.tabindex]="multiple() || i === tabStop() ? 0 : -1"
|
|
3688
4712
|
[attr.data-index]="i"
|
|
3689
|
-
[disabled]="
|
|
3690
|
-
[class]="
|
|
3691
|
-
(click)="
|
|
4713
|
+
[disabled]="isDisabled() || chip.disabled"
|
|
4714
|
+
[class]="classFor(chip)"
|
|
4715
|
+
(click)="toggle(i)"
|
|
3692
4716
|
>
|
|
3693
|
-
@if (
|
|
3694
|
-
<sds-icon
|
|
4717
|
+
@if (isSelected(chip) && multiple() && showCheck()) {
|
|
4718
|
+
<sds-icon
|
|
4719
|
+
name="check"
|
|
4720
|
+
[size]="size() === 'sm' ? 12 : 14"
|
|
4721
|
+
[strokeWidth]="3"
|
|
4722
|
+
class="shrink-0"
|
|
4723
|
+
/>
|
|
4724
|
+
} @else if (chip.icon) {
|
|
4725
|
+
<sds-icon [name]="chip.icon" [size]="size() === 'sm' ? 13 : 15" class="shrink-0" />
|
|
3695
4726
|
}
|
|
3696
|
-
|
|
3697
|
-
|
|
4727
|
+
<span class="truncate">{{ chip.label }}</span>
|
|
4728
|
+
@if (chip.count !== null) {
|
|
4729
|
+
<span [class]="countClass">{{ chip.count }}</span>
|
|
3698
4730
|
}
|
|
3699
4731
|
</button>
|
|
3700
4732
|
}
|
|
3701
4733
|
</div>
|
|
3702
4734
|
`,
|
|
3703
4735
|
}]
|
|
3704
|
-
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledby", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }],
|
|
4736
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], deselectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "deselectable", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledby", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], wrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrap", required: false }] }], showCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCheck", required: false }] }] } });
|
|
3705
4737
|
|
|
3706
4738
|
/**
|
|
3707
4739
|
* Each status has its own glyph SHAPE (circle-check, triangle, octagon, circle-i) as well as its own
|
|
@@ -5702,6 +6734,10 @@ function sdsFileMatchesAccept(file, accept) {
|
|
|
5702
6734
|
* affordance); clicking anywhere on the zone opens the picker too. All text is set through inputs with
|
|
5703
6735
|
* English defaults.
|
|
5704
6736
|
*
|
|
6737
|
+
* `appearance="button"` drops the zone and renders only the Browse button (files can still be dropped
|
|
6738
|
+
* on it) — for a "Replace photo" / "Upload logo" / "Add document" action next to a preview, with the
|
|
6739
|
+
* same accept/size/count checks and error messages. `browse()` opens the picker from your own control.
|
|
6740
|
+
*
|
|
5705
6741
|
* @example
|
|
5706
6742
|
* <sds-file-drop accept="image/jpeg,image/png,image/webp" multiple [maxSize]="10_000_000"
|
|
5707
6743
|
* label="Drop photos here" browseLabel="Choose photos"
|
|
@@ -5720,6 +6756,10 @@ class SdsFileDrop {
|
|
|
5720
6756
|
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
5721
6757
|
/** Icon in the zone at rest. */
|
|
5722
6758
|
icon = input('upload', ...(ngDevMode ? [{ debugName: "icon" }] : []));
|
|
6759
|
+
/** `zone` (default): the dashed drop zone with a Browse button. `button`: the Browse button alone. */
|
|
6760
|
+
appearance = input('zone', ...(ngDevMode ? [{ debugName: "appearance" }] : []));
|
|
6761
|
+
/** `button` appearance: size of the button. */
|
|
6762
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
|
|
5723
6763
|
label = input('Drag and drop files here', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
5724
6764
|
/** Shown while files are dragged over the zone. */
|
|
5725
6765
|
dropLabel = input('Drop to add', ...(ngDevMode ? [{ debugName: "dropLabel" }] : []));
|
|
@@ -5756,6 +6796,14 @@ class SdsFileDrop {
|
|
|
5756
6796
|
? `${base} cursor-copy border-brand-500 bg-brand-50 dark:bg-muted`
|
|
5757
6797
|
: `${base} cursor-pointer border-border bg-muted/50 hover:border-ink-faint`;
|
|
5758
6798
|
}, ...(ngDevMode ? [{ debugName: "zoneClass" }] : []));
|
|
6799
|
+
buttonClass = computed(() => {
|
|
6800
|
+
const size = this.size() === 'sm' ? 'h-8 px-3' : 'h-9 px-3.5';
|
|
6801
|
+
const drag = this.dragging() ? ' border-brand-500 bg-brand-50 dark:bg-muted' : ' border-border bg-surface hover:bg-muted';
|
|
6802
|
+
return (`inline-flex ${size} items-center gap-1.5 rounded-control border text-sm font-semibold text-ink shadow-sm ` +
|
|
6803
|
+
'transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 ' +
|
|
6804
|
+
'disabled:cursor-not-allowed disabled:opacity-60' +
|
|
6805
|
+
drag);
|
|
6806
|
+
}, ...(ngDevMode ? [{ debugName: "buttonClass" }] : []));
|
|
5759
6807
|
/** Open the native file picker. */
|
|
5760
6808
|
browse(event) {
|
|
5761
6809
|
event?.stopPropagation();
|
|
@@ -5849,7 +6897,35 @@ class SdsFileDrop {
|
|
|
5849
6897
|
this.errors.set([]);
|
|
5850
6898
|
}
|
|
5851
6899
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsFileDrop, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5852
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsFileDrop, isStandalone: true, selector: "sds-file-drop", inputs: { accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, maxSize: { classPropertyName: "maxSize", publicName: "maxSize", isSignal: true, isRequired: false, transformFunction: null }, maxFiles: { classPropertyName: "maxFiles", publicName: "maxFiles", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, dropLabel: { classPropertyName: "dropLabel", publicName: "dropLabel", isSignal: true, isRequired: false, transformFunction: null }, browseLabel: { classPropertyName: "browseLabel", publicName: "browseLabel", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, wrongTypeMessage: { classPropertyName: "wrongTypeMessage", publicName: "wrongTypeMessage", isSignal: true, isRequired: false, transformFunction: null }, tooLargeMessage: { classPropertyName: "tooLargeMessage", publicName: "tooLargeMessage", isSignal: true, isRequired: false, transformFunction: null }, tooManyMessage: { classPropertyName: "tooManyMessage", publicName: "tooManyMessage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { files: "files", rejected: "rejected" }, host: { classAttribute: "block" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
6900
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsFileDrop, isStandalone: true, selector: "sds-file-drop", inputs: { accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, maxSize: { classPropertyName: "maxSize", publicName: "maxSize", isSignal: true, isRequired: false, transformFunction: null }, maxFiles: { classPropertyName: "maxFiles", publicName: "maxFiles", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, dropLabel: { classPropertyName: "dropLabel", publicName: "dropLabel", isSignal: true, isRequired: false, transformFunction: null }, browseLabel: { classPropertyName: "browseLabel", publicName: "browseLabel", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, wrongTypeMessage: { classPropertyName: "wrongTypeMessage", publicName: "wrongTypeMessage", isSignal: true, isRequired: false, transformFunction: null }, tooLargeMessage: { classPropertyName: "tooLargeMessage", publicName: "tooLargeMessage", isSignal: true, isRequired: false, transformFunction: null }, tooManyMessage: { classPropertyName: "tooManyMessage", publicName: "tooManyMessage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { files: "files", rejected: "rejected" }, host: { classAttribute: "block" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
6901
|
+
@if (appearance() === 'button') {
|
|
6902
|
+
<button
|
|
6903
|
+
type="button"
|
|
6904
|
+
[class]="buttonClass()"
|
|
6905
|
+
[disabled]="disabled()"
|
|
6906
|
+
[attr.aria-describedby]="errors().length ? zoneId + '-errors' : null"
|
|
6907
|
+
[attr.data-dragging]="dragging() ? 'true' : null"
|
|
6908
|
+
(click)="browse($event)"
|
|
6909
|
+
(dragenter)="onDragEnter($event)"
|
|
6910
|
+
(dragover)="onDragOver($event)"
|
|
6911
|
+
(dragleave)="onDragLeave($event)"
|
|
6912
|
+
(drop)="onDrop($event)"
|
|
6913
|
+
>
|
|
6914
|
+
<sds-icon [name]="dragging() ? 'upload' : icon()" [size]="16" />
|
|
6915
|
+
{{ dragging() ? dropLabel() : browseLabel() }}
|
|
6916
|
+
</button>
|
|
6917
|
+
<input
|
|
6918
|
+
#fileInput
|
|
6919
|
+
type="file"
|
|
6920
|
+
class="sr-only"
|
|
6921
|
+
tabindex="-1"
|
|
6922
|
+
aria-hidden="true"
|
|
6923
|
+
[accept]="accept()"
|
|
6924
|
+
[multiple]="multiple()"
|
|
6925
|
+
[disabled]="disabled()"
|
|
6926
|
+
(change)="onInputChange($event)"
|
|
6927
|
+
/>
|
|
6928
|
+
} @else {
|
|
5853
6929
|
<div
|
|
5854
6930
|
[class]="zoneClass()"
|
|
5855
6931
|
[attr.aria-disabled]="disabled() ? 'true' : null"
|
|
@@ -5894,6 +6970,7 @@ class SdsFileDrop {
|
|
|
5894
6970
|
(change)="onInputChange($event)"
|
|
5895
6971
|
/>
|
|
5896
6972
|
</div>
|
|
6973
|
+
}
|
|
5897
6974
|
|
|
5898
6975
|
<div aria-live="polite" aria-atomic="true">
|
|
5899
6976
|
@if (errors().length) {
|
|
@@ -5917,6 +6994,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
5917
6994
|
imports: [SdsIcon],
|
|
5918
6995
|
host: { class: 'block' },
|
|
5919
6996
|
template: `
|
|
6997
|
+
@if (appearance() === 'button') {
|
|
6998
|
+
<button
|
|
6999
|
+
type="button"
|
|
7000
|
+
[class]="buttonClass()"
|
|
7001
|
+
[disabled]="disabled()"
|
|
7002
|
+
[attr.aria-describedby]="errors().length ? zoneId + '-errors' : null"
|
|
7003
|
+
[attr.data-dragging]="dragging() ? 'true' : null"
|
|
7004
|
+
(click)="browse($event)"
|
|
7005
|
+
(dragenter)="onDragEnter($event)"
|
|
7006
|
+
(dragover)="onDragOver($event)"
|
|
7007
|
+
(dragleave)="onDragLeave($event)"
|
|
7008
|
+
(drop)="onDrop($event)"
|
|
7009
|
+
>
|
|
7010
|
+
<sds-icon [name]="dragging() ? 'upload' : icon()" [size]="16" />
|
|
7011
|
+
{{ dragging() ? dropLabel() : browseLabel() }}
|
|
7012
|
+
</button>
|
|
7013
|
+
<input
|
|
7014
|
+
#fileInput
|
|
7015
|
+
type="file"
|
|
7016
|
+
class="sr-only"
|
|
7017
|
+
tabindex="-1"
|
|
7018
|
+
aria-hidden="true"
|
|
7019
|
+
[accept]="accept()"
|
|
7020
|
+
[multiple]="multiple()"
|
|
7021
|
+
[disabled]="disabled()"
|
|
7022
|
+
(change)="onInputChange($event)"
|
|
7023
|
+
/>
|
|
7024
|
+
} @else {
|
|
5920
7025
|
<div
|
|
5921
7026
|
[class]="zoneClass()"
|
|
5922
7027
|
[attr.aria-disabled]="disabled() ? 'true' : null"
|
|
@@ -5961,6 +7066,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
5961
7066
|
(change)="onInputChange($event)"
|
|
5962
7067
|
/>
|
|
5963
7068
|
</div>
|
|
7069
|
+
}
|
|
5964
7070
|
|
|
5965
7071
|
<div aria-live="polite" aria-atomic="true">
|
|
5966
7072
|
@if (errors().length) {
|
|
@@ -5976,7 +7082,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
5976
7082
|
</div>
|
|
5977
7083
|
`,
|
|
5978
7084
|
}]
|
|
5979
|
-
}], 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 }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", 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 }] }], 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"] }] } });
|
|
7085
|
+
}], 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 }] }], 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 }] }], 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"] }] } });
|
|
5980
7086
|
|
|
5981
7087
|
let nextId$8 = 0;
|
|
5982
7088
|
/**
|
|
@@ -10727,8 +11833,13 @@ class SdsTabsStore {
|
|
|
10727
11833
|
id = `sds-tabs-${nextId$3++}`;
|
|
10728
11834
|
/** Whether the tab list scrolls horizontally (mirrors [scrollable]). */
|
|
10729
11835
|
scrollable = signal(false, ...(ngDevMode ? [{ debugName: "scrollable" }] : []));
|
|
11836
|
+
/** Controlled mode (mirrors [controlled]): a click only REQUESTS a tab, the host sets [value]. */
|
|
11837
|
+
controlled = signal(false, ...(ngDevMode ? [{ debugName: "controlled" }] : []));
|
|
11838
|
+
/** Called with every tab the user picks (wired to the host's (tabSelect) output). */
|
|
11839
|
+
onSelect = () => { };
|
|
10730
11840
|
value;
|
|
10731
11841
|
registered = signal([], ...(ngDevMode ? [{ debugName: "registered" }] : []));
|
|
11842
|
+
panels = signal([], ...(ngDevMode ? [{ debugName: "panels" }] : []));
|
|
10732
11843
|
/** Called once by <sds-tabs> to share its two-way `value` signal. */
|
|
10733
11844
|
connect(value) {
|
|
10734
11845
|
this.value = value;
|
|
@@ -10739,8 +11850,11 @@ class SdsTabsStore {
|
|
|
10739
11850
|
isActive(value) {
|
|
10740
11851
|
return this.value() === value;
|
|
10741
11852
|
}
|
|
11853
|
+
/** A user pick. Uncontrolled: becomes the active tab. Controlled: only reported to the host. */
|
|
10742
11854
|
select(value) {
|
|
10743
|
-
this.
|
|
11855
|
+
this.onSelect(value);
|
|
11856
|
+
if (!this.controlled())
|
|
11857
|
+
this.value.set(value);
|
|
10744
11858
|
}
|
|
10745
11859
|
registerTab(value) {
|
|
10746
11860
|
this.registered.update((list) => (list.includes(value) ? list : [...list, value]));
|
|
@@ -10751,6 +11865,16 @@ class SdsTabsStore {
|
|
|
10751
11865
|
unregisterTab(value) {
|
|
10752
11866
|
this.registered.update((list) => list.filter((v) => v !== value));
|
|
10753
11867
|
}
|
|
11868
|
+
registerPanel(value) {
|
|
11869
|
+
this.panels.update((list) => (list.includes(value) ? list : [...list, value]));
|
|
11870
|
+
}
|
|
11871
|
+
unregisterPanel(value) {
|
|
11872
|
+
this.panels.update((list) => list.filter((v) => v !== value));
|
|
11873
|
+
}
|
|
11874
|
+
/** Whether an <sds-tab-panel> exists for this value (tabs whose content lives elsewhere have none). */
|
|
11875
|
+
hasPanel(value) {
|
|
11876
|
+
return this.panels().includes(value);
|
|
11877
|
+
}
|
|
10754
11878
|
tabId(value) {
|
|
10755
11879
|
return `${this.id}-tab-${value}`;
|
|
10756
11880
|
}
|
|
@@ -10770,6 +11894,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
10770
11894
|
* <sds-tab-panel> [value]; it is two-way bindable via [(value)]. When omitted,
|
|
10771
11895
|
* the first registered tab is selected.
|
|
10772
11896
|
*
|
|
11897
|
+
* **Controlled tabs** (`controlled`): a click, arrow key or Home/End only emits `(tabSelect)`; the active
|
|
11898
|
+
* tab changes when the host changes `[value]`. Use it when the active tab lives somewhere else — a
|
|
11899
|
+
* `?tab=` query param — or when switching can be refused (an unsaved-changes guard): the tab never
|
|
11900
|
+
* shows as selected before the host agrees. `(tabSelect)` also fires in the default mode.
|
|
11901
|
+
*
|
|
11902
|
+
* **Panels are optional.** A tab set whose content is rendered by the host (an `@switch` on the value, a
|
|
11903
|
+
* router outlet) can omit <sds-tab-panels>; a tab then carries no `aria-controls` to a missing panel.
|
|
11904
|
+
*
|
|
10773
11905
|
* @example
|
|
10774
11906
|
* <sds-tabs [(value)]="active">
|
|
10775
11907
|
* <sds-tab-list>
|
|
@@ -10781,6 +11913,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
10781
11913
|
* <sds-tab-panel value="1">…</sds-tab-panel>
|
|
10782
11914
|
* </sds-tab-panels>
|
|
10783
11915
|
* </sds-tabs>
|
|
11916
|
+
*
|
|
11917
|
+
* @example
|
|
11918
|
+
* <!-- driven by a query param, content rendered by the host -->
|
|
11919
|
+
* <sds-tabs controlled [value]="activeTab()" (tabSelect)="selectTab($event)">
|
|
11920
|
+
* <sds-tab-list>
|
|
11921
|
+
* @for (tab of tabs; track tab) {
|
|
11922
|
+
* <sds-tab [value]="tab">{{ 'menu.tabs.' + tab | translate }}</sds-tab>
|
|
11923
|
+
* }
|
|
11924
|
+
* </sds-tab-list>
|
|
11925
|
+
* </sds-tabs>
|
|
10784
11926
|
*/
|
|
10785
11927
|
class SdsTabs {
|
|
10786
11928
|
store = inject(SdsTabsStore);
|
|
@@ -10788,12 +11930,21 @@ class SdsTabs {
|
|
|
10788
11930
|
value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
10789
11931
|
/** Let the tab list scroll horizontally instead of overflowing. */
|
|
10790
11932
|
scrollable = input(false, ...(ngDevMode ? [{ debugName: "scrollable", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
11933
|
+
/**
|
|
11934
|
+
* Controlled mode: a user pick only emits (tabSelect); the host decides by setting [value]. See the
|
|
11935
|
+
* class note.
|
|
11936
|
+
*/
|
|
11937
|
+
controlled = input(false, ...(ngDevMode ? [{ debugName: "controlled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
11938
|
+
/** Emits the value of the tab the user picked (in both modes). */
|
|
11939
|
+
tabSelect = output();
|
|
10791
11940
|
constructor() {
|
|
10792
11941
|
this.store.connect(this.value);
|
|
11942
|
+
this.store.onSelect = (value) => this.tabSelect.emit(value);
|
|
10793
11943
|
effect(() => this.store.scrollable.set(this.scrollable()));
|
|
11944
|
+
effect(() => this.store.controlled.set(this.controlled()));
|
|
10794
11945
|
}
|
|
10795
11946
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTabs, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10796
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.25", type: SdsTabs, isStandalone: true, selector: "sds-tabs", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, scrollable: { classPropertyName: "scrollable", publicName: "scrollable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { classAttribute: "block" }, providers: [SdsTabsStore], ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
11947
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.25", type: SdsTabs, isStandalone: true, selector: "sds-tabs", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, scrollable: { classPropertyName: "scrollable", publicName: "scrollable", isSignal: true, isRequired: false, transformFunction: null }, controlled: { classPropertyName: "controlled", publicName: "controlled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", tabSelect: "tabSelect" }, host: { classAttribute: "block" }, providers: [SdsTabsStore], ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10797
11948
|
}
|
|
10798
11949
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTabs, decorators: [{
|
|
10799
11950
|
type: Component,
|
|
@@ -10804,7 +11955,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
10804
11955
|
host: { class: 'block' },
|
|
10805
11956
|
template: `<ng-content />`,
|
|
10806
11957
|
}]
|
|
10807
|
-
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], scrollable: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollable", required: false }] }] } });
|
|
11958
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], scrollable: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollable", required: false }] }], controlled: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlled", required: false }] }], tabSelect: [{ type: i0.Output, args: ["tabSelect"] }] } });
|
|
10808
11959
|
|
|
10809
11960
|
/**
|
|
10810
11961
|
* Container (role="tablist") for the <sds-tab> headers. Renders the bottom rule
|
|
@@ -10901,7 +12052,7 @@ class SdsTab {
|
|
|
10901
12052
|
role="tab"
|
|
10902
12053
|
[id]="store.tabId(value())"
|
|
10903
12054
|
[attr.aria-selected]="active()"
|
|
10904
|
-
[attr.aria-controls]="store.panelId(value())"
|
|
12055
|
+
[attr.aria-controls]="store.hasPanel(value()) ? store.panelId(value()) : null"
|
|
10905
12056
|
[attr.aria-disabled]="disabled() ? 'true' : null"
|
|
10906
12057
|
[attr.tabindex]="active() && !disabled() ? 0 : -1"
|
|
10907
12058
|
[disabled]="disabled()"
|
|
@@ -10924,7 +12075,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
10924
12075
|
role="tab"
|
|
10925
12076
|
[id]="store.tabId(value())"
|
|
10926
12077
|
[attr.aria-selected]="active()"
|
|
10927
|
-
[attr.aria-controls]="store.panelId(value())"
|
|
12078
|
+
[attr.aria-controls]="store.hasPanel(value()) ? store.panelId(value()) : null"
|
|
10928
12079
|
[attr.aria-disabled]="disabled() ? 'true' : null"
|
|
10929
12080
|
[attr.tabindex]="active() && !disabled() ? 0 : -1"
|
|
10930
12081
|
[disabled]="disabled()"
|
|
@@ -10969,6 +12120,12 @@ class SdsTabPanel {
|
|
|
10969
12120
|
store = inject(SdsTabsStore);
|
|
10970
12121
|
value = input.required(...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
10971
12122
|
active = computed(() => this.store.isActive(this.value()), ...(ngDevMode ? [{ debugName: "active" }] : []));
|
|
12123
|
+
ngOnInit() {
|
|
12124
|
+
this.store.registerPanel(this.value());
|
|
12125
|
+
}
|
|
12126
|
+
ngOnDestroy() {
|
|
12127
|
+
this.store.unregisterPanel(this.value());
|
|
12128
|
+
}
|
|
10972
12129
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTabPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10973
12130
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsTabPanel, isStandalone: true, selector: "sds-tab-panel", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: `
|
|
10974
12131
|
@if (active()) {
|
|
@@ -11382,6 +12539,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
11382
12539
|
}]
|
|
11383
12540
|
}] });
|
|
11384
12541
|
|
|
12542
|
+
/**
|
|
12543
|
+
* Replaces the table's plain `emptyMessage` with rich content — an explanation and a call to action,
|
|
12544
|
+
* a "no results for …" line. Rendered in the full-width empty row, only when not `loading`.
|
|
12545
|
+
*
|
|
12546
|
+
* @example
|
|
12547
|
+
* <sds-table [value]="categories()" [columns]="cols">
|
|
12548
|
+
* <ng-template sdsTableEmpty>
|
|
12549
|
+
* <p>No categories yet.</p>
|
|
12550
|
+
* <sds-button size="sm" (click)="create()">Add a category</sds-button>
|
|
12551
|
+
* </ng-template>
|
|
12552
|
+
* </sds-table>
|
|
12553
|
+
*/
|
|
12554
|
+
class SdsTableEmpty {
|
|
12555
|
+
/** The wrapped template, rendered by the host table via ngTemplateOutlet. */
|
|
12556
|
+
template = inject(TemplateRef);
|
|
12557
|
+
/** Lets the compiler type-check `let-*` bindings against SdsTableEmptyContext. */
|
|
12558
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
12559
|
+
return true;
|
|
12560
|
+
}
|
|
12561
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTableEmpty, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
12562
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: SdsTableEmpty, isStandalone: true, selector: "ng-template[sdsTableEmpty]", ngImport: i0 });
|
|
12563
|
+
}
|
|
12564
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTableEmpty, decorators: [{
|
|
12565
|
+
type: Directive,
|
|
12566
|
+
args: [{
|
|
12567
|
+
selector: 'ng-template[sdsTableEmpty]',
|
|
12568
|
+
standalone: true,
|
|
12569
|
+
}]
|
|
12570
|
+
}] });
|
|
12571
|
+
|
|
11385
12572
|
const SIZES = {
|
|
11386
12573
|
sm: { cell: 'px-3 py-2 text-sm', head: 'px-3 py-2' },
|
|
11387
12574
|
md: { cell: 'px-4 py-2.5 text-sm', head: 'px-4 py-2.5' },
|
|
@@ -11413,6 +12600,11 @@ let nextId$1 = 0;
|
|
|
11413
12600
|
* - **Custom headers:** `<ng-template sdsHeader="field">` replaces a column's header content (label and
|
|
11414
12601
|
* sort button) — e.g. a "select all" checkbox above your own checkbox column. Its context carries the
|
|
11415
12602
|
* filtered `rows` (all pages) and the displayed `pageRows` (see {@link SdsTableHeaderContext}).
|
|
12603
|
+
* - **Clickable rows:** `rowClickable` makes each row an action (pointer, tab stop, Enter/Space) that
|
|
12604
|
+
* emits `(rowClick)`; clicks on buttons, links and fields inside a cell are not row clicks, so no
|
|
12605
|
+
* `stopPropagation()` is needed. `[rowClass]` adds per-row classes (e.g. dim a blocked row).
|
|
12606
|
+
* - **Empty state:** `<ng-template sdsTableEmpty let-query>` replaces the plain `emptyMessage` (e.g. a
|
|
12607
|
+
* sentence and a "create" button); it is not shown while `loading`.
|
|
11416
12608
|
* - **Labels:** every visible and spoken string is an input with an English default (paginator,
|
|
11417
12609
|
* filter, selection, expansion, editor). An app serving more than one language passes translations.
|
|
11418
12610
|
*
|
|
@@ -11484,6 +12676,14 @@ class SdsTable {
|
|
|
11484
12676
|
selection = model(null, ...(ngDevMode ? [{ debugName: "selection" }] : []));
|
|
11485
12677
|
loading = input(false, ...(ngDevMode ? [{ debugName: "loading", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
11486
12678
|
emptyMessage = input('No data', ...(ngDevMode ? [{ debugName: "emptyMessage" }] : []));
|
|
12679
|
+
/**
|
|
12680
|
+
* Rows are actions: a pointer cursor, a tab stop per row, and Enter/Space emit `(rowClick)` like a
|
|
12681
|
+
* click. Clicks that land on a control inside a cell (button, link, input, checkbox…) do not count as
|
|
12682
|
+
* a row click, so a row can open a drawer while its cells keep their own buttons.
|
|
12683
|
+
*/
|
|
12684
|
+
rowClickable = input(false, ...(ngDevMode ? [{ debugName: "rowClickable", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
12685
|
+
/** Extra classes per body row, e.g. `(row) => row.blocked ? 'opacity-60' : ''`. */
|
|
12686
|
+
rowClass = input(null, ...(ngDevMode ? [{ debugName: "rowClass" }] : []));
|
|
11487
12687
|
caption = input('', ...(ngDevMode ? [{ debugName: "caption" }] : []));
|
|
11488
12688
|
/** Fallback accessible name for the `<table>` when no `caption` is set. */
|
|
11489
12689
|
ariaLabel = input('Data table', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
@@ -11574,6 +12774,11 @@ class SdsTable {
|
|
|
11574
12774
|
expansionTemplate() {
|
|
11575
12775
|
return this.rowExpansion()?.template ?? null;
|
|
11576
12776
|
}
|
|
12777
|
+
// --- empty-state template -------------------------------------------------
|
|
12778
|
+
emptyTpl = contentChild(SdsTableEmpty, ...(ngDevMode ? [{ debugName: "emptyTpl" }] : []));
|
|
12779
|
+
emptyTemplate() {
|
|
12780
|
+
return this.emptyTpl()?.template ?? null;
|
|
12781
|
+
}
|
|
11577
12782
|
// --- pipeline state -------------------------------------------------------
|
|
11578
12783
|
filterText = signal('', ...(ngDevMode ? [{ debugName: "filterText" }] : []));
|
|
11579
12784
|
sortField = signal(null, ...(ngDevMode ? [{ debugName: "sortField" }] : []));
|
|
@@ -11809,9 +13014,14 @@ class SdsTable {
|
|
|
11809
13014
|
this.toggleRowSelection(row);
|
|
11810
13015
|
}
|
|
11811
13016
|
}
|
|
11812
|
-
/** Enter/Space on a (single
|
|
13017
|
+
/** Enter/Space on a focusable row (single selection or `rowClickable`) acts like a click on it. */
|
|
11813
13018
|
onRowKeydown(event, row) {
|
|
11814
|
-
if (this.
|
|
13019
|
+
if (this.loading() || this.editable())
|
|
13020
|
+
return;
|
|
13021
|
+
if (this.selectionMode() !== 'single' && !this.rowClickable())
|
|
13022
|
+
return;
|
|
13023
|
+
// Only the row itself: Enter on a button inside a cell belongs to that button.
|
|
13024
|
+
if (event.target !== event.currentTarget)
|
|
11815
13025
|
return;
|
|
11816
13026
|
event.preventDefault();
|
|
11817
13027
|
this.onRowClick(row);
|
|
@@ -11830,9 +13040,11 @@ class SdsTable {
|
|
|
11830
13040
|
this.rowSelect.emit(row);
|
|
11831
13041
|
}
|
|
11832
13042
|
}
|
|
11833
|
-
onRowClick(row) {
|
|
13043
|
+
onRowClick(row, event) {
|
|
11834
13044
|
if (this.loading())
|
|
11835
13045
|
return;
|
|
13046
|
+
if (event && this.fromInnerControl(event))
|
|
13047
|
+
return;
|
|
11836
13048
|
this.rowClick.emit(row);
|
|
11837
13049
|
if (this.selectionMode() === 'single') {
|
|
11838
13050
|
if (this.equals(this.selection(), row)) {
|
|
@@ -11845,6 +13057,16 @@ class SdsTable {
|
|
|
11845
13057
|
}
|
|
11846
13058
|
}
|
|
11847
13059
|
}
|
|
13060
|
+
/** Whether a click started on an interactive element inside the row (not on the row itself). */
|
|
13061
|
+
fromInnerControl(event) {
|
|
13062
|
+
const row = event.currentTarget;
|
|
13063
|
+
const target = event.target;
|
|
13064
|
+
if (!row || !target || !(target instanceof Element))
|
|
13065
|
+
return false;
|
|
13066
|
+
const control = target.closest('button, a[href], input, select, textarea, label, [contenteditable="true"], ' +
|
|
13067
|
+
'[role="button"], [role="checkbox"], [role="switch"], [role="radio"], [role="combobox"], [role="menuitem"]');
|
|
13068
|
+
return !!control && control !== row && row.contains(control);
|
|
13069
|
+
}
|
|
11848
13070
|
// --- paginator ------------------------------------------------------------
|
|
11849
13071
|
onPageChange(state) {
|
|
11850
13072
|
this.editing.set(null);
|
|
@@ -12378,7 +13600,7 @@ class SdsTable {
|
|
|
12378
13600
|
const align = ALIGN[col?.align ?? 'left'];
|
|
12379
13601
|
return `${SIZES[this.size()].cell} ${align} text-ink align-middle`;
|
|
12380
13602
|
}
|
|
12381
|
-
|
|
13603
|
+
bodyRowClass(row, index) {
|
|
12382
13604
|
const parts = ['transition-colors'];
|
|
12383
13605
|
if (this.gridlines())
|
|
12384
13606
|
parts.push('divide-x divide-border');
|
|
@@ -12390,9 +13612,12 @@ class SdsTable {
|
|
|
12390
13612
|
}
|
|
12391
13613
|
if (this.hoverable() && !this.isRowSelected(row))
|
|
12392
13614
|
parts.push('hover:bg-muted/60');
|
|
12393
|
-
if (this.selectionMode() === 'single') {
|
|
13615
|
+
if (this.selectionMode() === 'single' || (this.rowClickable() && !this.editable())) {
|
|
12394
13616
|
parts.push('cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-brand-500');
|
|
12395
13617
|
}
|
|
13618
|
+
const extra = this.rowClass()?.(row, index);
|
|
13619
|
+
if (extra)
|
|
13620
|
+
parts.push(extra);
|
|
12396
13621
|
return parts.join(' ');
|
|
12397
13622
|
}
|
|
12398
13623
|
checkboxClass(checked, indeterminate) {
|
|
@@ -12401,7 +13626,7 @@ class SdsTable {
|
|
|
12401
13626
|
: 'border-border bg-surface';
|
|
12402
13627
|
}
|
|
12403
13628
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12404
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsTable, isStandalone: true, selector: "sds-table", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, dataKey: { classPropertyName: "dataKey", publicName: "dataKey", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, gridlines: { classPropertyName: "gridlines", publicName: "gridlines", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, paginator: { classPropertyName: "paginator", publicName: "paginator", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageOptions: { classPropertyName: "rowsPerPageOptions", publicName: "rowsPerPageOptions", isSignal: true, isRequired: false, transformFunction: null }, showCurrentPageReport: { classPropertyName: "showCurrentPageReport", publicName: "showCurrentPageReport", isSignal: true, isRequired: false, transformFunction: null }, currentPageReportTemplate: { classPropertyName: "currentPageReportTemplate", publicName: "currentPageReportTemplate", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageLabel: { classPropertyName: "rowsPerPageLabel", publicName: "rowsPerPageLabel", isSignal: true, isRequired: false, transformFunction: null }, paginationLabel: { classPropertyName: "paginationLabel", publicName: "paginationLabel", isSignal: true, isRequired: false, transformFunction: null }, firstPageLabel: { classPropertyName: "firstPageLabel", publicName: "firstPageLabel", isSignal: true, isRequired: false, transformFunction: null }, previousPageLabel: { classPropertyName: "previousPageLabel", publicName: "previousPageLabel", isSignal: true, isRequired: false, transformFunction: null }, nextPageLabel: { classPropertyName: "nextPageLabel", publicName: "nextPageLabel", isSignal: true, isRequired: false, transformFunction: null }, lastPageLabel: { classPropertyName: "lastPageLabel", publicName: "lastPageLabel", isSignal: true, isRequired: false, transformFunction: null }, pageLinkLabel: { classPropertyName: "pageLinkLabel", publicName: "pageLinkLabel", isSignal: true, isRequired: false, transformFunction: null }, globalFilter: { classPropertyName: "globalFilter", publicName: "globalFilter", isSignal: true, isRequired: false, transformFunction: null }, globalFilterPlaceholder: { classPropertyName: "globalFilterPlaceholder", publicName: "globalFilterPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, globalFilterAriaLabel: { classPropertyName: "globalFilterAriaLabel", publicName: "globalFilterAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, globalFilterFields: { classPropertyName: "globalFilterFields", publicName: "globalFilterFields", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, caption: { classPropertyName: "caption", publicName: "caption", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, scrollHeight: { classPropertyName: "scrollHeight", publicName: "scrollHeight", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null }, totalRecords: { classPropertyName: "totalRecords", publicName: "totalRecords", isSignal: true, isRequired: false, transformFunction: null }, expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, resizableColumns: { classPropertyName: "resizableColumns", publicName: "resizableColumns", isSignal: true, isRequired: false, transformFunction: null }, reorderableColumns: { classPropertyName: "reorderableColumns", publicName: "reorderableColumns", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, editOn: { classPropertyName: "editOn", publicName: "editOn", isSignal: true, isRequired: false, transformFunction: null }, cellErrors: { classPropertyName: "cellErrors", publicName: "cellErrors", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, dirtyLabel: { classPropertyName: "dirtyLabel", publicName: "dirtyLabel", isSignal: true, isRequired: false, transformFunction: null }, selectPlaceholder: { classPropertyName: "selectPlaceholder", publicName: "selectPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, selectAllLabel: { classPropertyName: "selectAllLabel", publicName: "selectAllLabel", isSignal: true, isRequired: false, transformFunction: null }, selectRowLabel: { classPropertyName: "selectRowLabel", publicName: "selectRowLabel", isSignal: true, isRequired: false, transformFunction: null }, selectionColumnLabel: { classPropertyName: "selectionColumnLabel", publicName: "selectionColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, expandColumnLabel: { classPropertyName: "expandColumnLabel", publicName: "expandColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, expandRowLabel: { classPropertyName: "expandRowLabel", publicName: "expandRowLabel", isSignal: true, isRequired: false, transformFunction: null }, collapseRowLabel: { classPropertyName: "collapseRowLabel", publicName: "collapseRowLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selection: "selectionChange", sortChange: "sortChange", pageChange: "pageChange", rowSelect: "rowSelect", rowUnselect: "rowUnselect", lazyLoad: "lazyLoad", rowClick: "rowClick", rowExpand: "rowExpand", rowCollapse: "rowCollapse", colReorder: "colReorder", cellCommit: "cellCommit" }, host: { classAttribute: "block" }, queries: [{ propertyName: "cellTemplates", predicate: SdsTableCell, isSignal: true }, { propertyName: "headerTemplates", predicate: SdsTableHeader, isSignal: true }, { propertyName: "rowExpansion", first: true, predicate: SdsTableRowExpansion, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "numberEditor", first: true, predicate: SdsInputNumber, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
13629
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsTable, isStandalone: true, selector: "sds-table", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, dataKey: { classPropertyName: "dataKey", publicName: "dataKey", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, gridlines: { classPropertyName: "gridlines", publicName: "gridlines", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, paginator: { classPropertyName: "paginator", publicName: "paginator", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageOptions: { classPropertyName: "rowsPerPageOptions", publicName: "rowsPerPageOptions", isSignal: true, isRequired: false, transformFunction: null }, showCurrentPageReport: { classPropertyName: "showCurrentPageReport", publicName: "showCurrentPageReport", isSignal: true, isRequired: false, transformFunction: null }, currentPageReportTemplate: { classPropertyName: "currentPageReportTemplate", publicName: "currentPageReportTemplate", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageLabel: { classPropertyName: "rowsPerPageLabel", publicName: "rowsPerPageLabel", isSignal: true, isRequired: false, transformFunction: null }, paginationLabel: { classPropertyName: "paginationLabel", publicName: "paginationLabel", isSignal: true, isRequired: false, transformFunction: null }, firstPageLabel: { classPropertyName: "firstPageLabel", publicName: "firstPageLabel", isSignal: true, isRequired: false, transformFunction: null }, previousPageLabel: { classPropertyName: "previousPageLabel", publicName: "previousPageLabel", isSignal: true, isRequired: false, transformFunction: null }, nextPageLabel: { classPropertyName: "nextPageLabel", publicName: "nextPageLabel", isSignal: true, isRequired: false, transformFunction: null }, lastPageLabel: { classPropertyName: "lastPageLabel", publicName: "lastPageLabel", isSignal: true, isRequired: false, transformFunction: null }, pageLinkLabel: { classPropertyName: "pageLinkLabel", publicName: "pageLinkLabel", isSignal: true, isRequired: false, transformFunction: null }, globalFilter: { classPropertyName: "globalFilter", publicName: "globalFilter", isSignal: true, isRequired: false, transformFunction: null }, globalFilterPlaceholder: { classPropertyName: "globalFilterPlaceholder", publicName: "globalFilterPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, globalFilterAriaLabel: { classPropertyName: "globalFilterAriaLabel", publicName: "globalFilterAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, globalFilterFields: { classPropertyName: "globalFilterFields", publicName: "globalFilterFields", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, rowClickable: { classPropertyName: "rowClickable", publicName: "rowClickable", isSignal: true, isRequired: false, transformFunction: null }, rowClass: { classPropertyName: "rowClass", publicName: "rowClass", isSignal: true, isRequired: false, transformFunction: null }, caption: { classPropertyName: "caption", publicName: "caption", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, scrollHeight: { classPropertyName: "scrollHeight", publicName: "scrollHeight", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null }, totalRecords: { classPropertyName: "totalRecords", publicName: "totalRecords", isSignal: true, isRequired: false, transformFunction: null }, expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, resizableColumns: { classPropertyName: "resizableColumns", publicName: "resizableColumns", isSignal: true, isRequired: false, transformFunction: null }, reorderableColumns: { classPropertyName: "reorderableColumns", publicName: "reorderableColumns", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, editOn: { classPropertyName: "editOn", publicName: "editOn", isSignal: true, isRequired: false, transformFunction: null }, cellErrors: { classPropertyName: "cellErrors", publicName: "cellErrors", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, dirtyLabel: { classPropertyName: "dirtyLabel", publicName: "dirtyLabel", isSignal: true, isRequired: false, transformFunction: null }, selectPlaceholder: { classPropertyName: "selectPlaceholder", publicName: "selectPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, selectAllLabel: { classPropertyName: "selectAllLabel", publicName: "selectAllLabel", isSignal: true, isRequired: false, transformFunction: null }, selectRowLabel: { classPropertyName: "selectRowLabel", publicName: "selectRowLabel", isSignal: true, isRequired: false, transformFunction: null }, selectionColumnLabel: { classPropertyName: "selectionColumnLabel", publicName: "selectionColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, expandColumnLabel: { classPropertyName: "expandColumnLabel", publicName: "expandColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, expandRowLabel: { classPropertyName: "expandRowLabel", publicName: "expandRowLabel", isSignal: true, isRequired: false, transformFunction: null }, collapseRowLabel: { classPropertyName: "collapseRowLabel", publicName: "collapseRowLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selection: "selectionChange", sortChange: "sortChange", pageChange: "pageChange", rowSelect: "rowSelect", rowUnselect: "rowUnselect", lazyLoad: "lazyLoad", rowClick: "rowClick", rowExpand: "rowExpand", rowCollapse: "rowCollapse", colReorder: "colReorder", cellCommit: "cellCommit" }, host: { classAttribute: "block" }, queries: [{ propertyName: "cellTemplates", predicate: SdsTableCell, isSignal: true }, { propertyName: "headerTemplates", predicate: SdsTableHeader, isSignal: true }, { propertyName: "rowExpansion", first: true, predicate: SdsTableRowExpansion, descendants: true, isSignal: true }, { propertyName: "emptyTpl", first: true, predicate: SdsTableEmpty, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "numberEditor", first: true, predicate: SdsInputNumber, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
12405
13630
|
<div class="rounded-card border border-border bg-surface overflow-hidden">
|
|
12406
13631
|
<!-- Toolbar: caption + global filter -->
|
|
12407
13632
|
@if (caption() || globalFilter()) {
|
|
@@ -12555,10 +13780,10 @@ class SdsTable {
|
|
|
12555
13780
|
<tbody class="divide-y divide-border">
|
|
12556
13781
|
@for (row of displayRows(); track trackRow($index, row); let i = $index) {
|
|
12557
13782
|
<tr
|
|
12558
|
-
[class]="
|
|
12559
|
-
[attr.tabindex]="selectionMode() === 'single' && !loading() && !editable() ? 0 : null"
|
|
13783
|
+
[class]="bodyRowClass(row, i)"
|
|
13784
|
+
[attr.tabindex]="(selectionMode() === 'single' || rowClickable()) && !loading() && !editable() ? 0 : null"
|
|
12560
13785
|
[attr.aria-selected]="selectionMode() === 'single' ? (isRowSelected(row) ? 'true' : 'false') : null"
|
|
12561
|
-
(click)="onRowClick(row)"
|
|
13786
|
+
(click)="onRowClick(row, $event)"
|
|
12562
13787
|
(keydown.enter)="onRowKeydown($event, row)"
|
|
12563
13788
|
(keydown.space)="onRowKeydown($event, row)"
|
|
12564
13789
|
>
|
|
@@ -12783,7 +14008,14 @@ class SdsTable {
|
|
|
12783
14008
|
[attr.colspan]="totalColumnCount()"
|
|
12784
14009
|
class="py-10 text-center text-ink-faint"
|
|
12785
14010
|
>
|
|
12786
|
-
|
|
14011
|
+
@if (emptyTemplate(); as tpl) {
|
|
14012
|
+
<ng-container
|
|
14013
|
+
[ngTemplateOutlet]="tpl"
|
|
14014
|
+
[ngTemplateOutletContext]="{ $implicit: filterText(), filterText: filterText() }"
|
|
14015
|
+
/>
|
|
14016
|
+
} @else {
|
|
14017
|
+
{{ emptyMessage() }}
|
|
14018
|
+
}
|
|
12787
14019
|
</td>
|
|
12788
14020
|
</tr>
|
|
12789
14021
|
}
|
|
@@ -12990,10 +14222,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
12990
14222
|
<tbody class="divide-y divide-border">
|
|
12991
14223
|
@for (row of displayRows(); track trackRow($index, row); let i = $index) {
|
|
12992
14224
|
<tr
|
|
12993
|
-
[class]="
|
|
12994
|
-
[attr.tabindex]="selectionMode() === 'single' && !loading() && !editable() ? 0 : null"
|
|
14225
|
+
[class]="bodyRowClass(row, i)"
|
|
14226
|
+
[attr.tabindex]="(selectionMode() === 'single' || rowClickable()) && !loading() && !editable() ? 0 : null"
|
|
12995
14227
|
[attr.aria-selected]="selectionMode() === 'single' ? (isRowSelected(row) ? 'true' : 'false') : null"
|
|
12996
|
-
(click)="onRowClick(row)"
|
|
14228
|
+
(click)="onRowClick(row, $event)"
|
|
12997
14229
|
(keydown.enter)="onRowKeydown($event, row)"
|
|
12998
14230
|
(keydown.space)="onRowKeydown($event, row)"
|
|
12999
14231
|
>
|
|
@@ -13218,7 +14450,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
13218
14450
|
[attr.colspan]="totalColumnCount()"
|
|
13219
14451
|
class="py-10 text-center text-ink-faint"
|
|
13220
14452
|
>
|
|
13221
|
-
|
|
14453
|
+
@if (emptyTemplate(); as tpl) {
|
|
14454
|
+
<ng-container
|
|
14455
|
+
[ngTemplateOutlet]="tpl"
|
|
14456
|
+
[ngTemplateOutletContext]="{ $implicit: filterText(), filterText: filterText() }"
|
|
14457
|
+
/>
|
|
14458
|
+
} @else {
|
|
14459
|
+
{{ emptyMessage() }}
|
|
14460
|
+
}
|
|
13222
14461
|
</td>
|
|
13223
14462
|
</tr>
|
|
13224
14463
|
}
|
|
@@ -13264,7 +14503,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
13264
14503
|
</div>
|
|
13265
14504
|
`,
|
|
13266
14505
|
}]
|
|
13267
|
-
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], gridlines: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridlines", required: false }] }], hoverable: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverable", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], paginator: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginator", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], rowsPerPageOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPageOptions", required: false }] }], showCurrentPageReport: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCurrentPageReport", required: false }] }], currentPageReportTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPageReportTemplate", required: false }] }], rowsPerPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPageLabel", required: false }] }], paginationLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginationLabel", required: false }] }], firstPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstPageLabel", required: false }] }], previousPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousPageLabel", required: false }] }], nextPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextPageLabel", required: false }] }], lastPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "lastPageLabel", required: false }] }], pageLinkLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageLinkLabel", required: false }] }], globalFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilter", required: false }] }], globalFilterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilterPlaceholder", required: false }] }], globalFilterAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilterAriaLabel", required: false }] }], globalFilterFields: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilterFields", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }, { type: i0.Output, args: ["selectionChange"] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], caption: [{ type: i0.Input, args: [{ isSignal: true, alias: "caption", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], scrollHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollHeight", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }], totalRecords: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalRecords", required: false }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], resizableColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizableColumns", required: false }] }], reorderableColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderableColumns", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], editOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "editOn", required: false }] }], cellErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellErrors", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], dirtyLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dirtyLabel", required: false }] }], selectPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectPlaceholder", required: false }] }], selectAllLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectAllLabel", required: false }] }], selectRowLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectRowLabel", required: false }] }], selectionColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionColumnLabel", required: false }] }], expandColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandColumnLabel", required: false }] }], expandRowLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandRowLabel", required: false }] }], collapseRowLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseRowLabel", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], rowSelect: [{ type: i0.Output, args: ["rowSelect"] }], rowUnselect: [{ type: i0.Output, args: ["rowUnselect"] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], rowExpand: [{ type: i0.Output, args: ["rowExpand"] }], rowCollapse: [{ type: i0.Output, args: ["rowCollapse"] }], colReorder: [{ type: i0.Output, args: ["colReorder"] }], cellCommit: [{ type: i0.Output, args: ["cellCommit"] }], cellTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SdsTableCell), { isSignal: true }] }], headerTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SdsTableHeader), { isSignal: true }] }], rowExpansion: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsTableRowExpansion), { isSignal: true }] }], numberEditor: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SdsInputNumber), { isSignal: true }] }] } });
|
|
14506
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], gridlines: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridlines", required: false }] }], hoverable: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverable", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], paginator: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginator", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], rowsPerPageOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPageOptions", required: false }] }], showCurrentPageReport: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCurrentPageReport", required: false }] }], currentPageReportTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPageReportTemplate", required: false }] }], rowsPerPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPageLabel", required: false }] }], paginationLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginationLabel", required: false }] }], firstPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstPageLabel", required: false }] }], previousPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousPageLabel", required: false }] }], nextPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextPageLabel", required: false }] }], lastPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "lastPageLabel", required: false }] }], pageLinkLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageLinkLabel", required: false }] }], globalFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilter", required: false }] }], globalFilterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilterPlaceholder", required: false }] }], globalFilterAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilterAriaLabel", required: false }] }], globalFilterFields: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilterFields", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }, { type: i0.Output, args: ["selectionChange"] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], rowClickable: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowClickable", required: false }] }], rowClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowClass", required: false }] }], caption: [{ type: i0.Input, args: [{ isSignal: true, alias: "caption", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], scrollHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollHeight", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }], totalRecords: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalRecords", required: false }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], resizableColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizableColumns", required: false }] }], reorderableColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderableColumns", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], editOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "editOn", required: false }] }], cellErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellErrors", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], dirtyLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dirtyLabel", required: false }] }], selectPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectPlaceholder", required: false }] }], selectAllLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectAllLabel", required: false }] }], selectRowLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectRowLabel", required: false }] }], selectionColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionColumnLabel", required: false }] }], expandColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandColumnLabel", required: false }] }], expandRowLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandRowLabel", required: false }] }], collapseRowLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseRowLabel", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], rowSelect: [{ type: i0.Output, args: ["rowSelect"] }], rowUnselect: [{ type: i0.Output, args: ["rowUnselect"] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], rowExpand: [{ type: i0.Output, args: ["rowExpand"] }], rowCollapse: [{ type: i0.Output, args: ["rowCollapse"] }], colReorder: [{ type: i0.Output, args: ["colReorder"] }], cellCommit: [{ type: i0.Output, args: ["cellCommit"] }], cellTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SdsTableCell), { isSignal: true }] }], headerTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SdsTableHeader), { isSignal: true }] }], rowExpansion: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsTableRowExpansion), { isSignal: true }] }], emptyTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsTableEmpty), { isSignal: true }] }], numberEditor: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SdsInputNumber), { isSignal: true }] }] } });
|
|
13268
14507
|
|
|
13269
14508
|
/**
|
|
13270
14509
|
* A tiny transient-notification bus. Application code calls {@link success} / {@link error} / {@link info}
|
|
@@ -14828,7 +16067,7 @@ class SdsUserMenu {
|
|
|
14828
16067
|
</div>
|
|
14829
16068
|
</div>
|
|
14830
16069
|
}
|
|
14831
|
-
`, isInline: true, styles: [".animate-sds-menu{animation:sds-menu-in .14s ease-out}@keyframes sds-menu-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}@media(prefers-reduced-motion:reduce){.animate-sds-menu{animation:none}}\n"], dependencies: [{ kind: "component", type: SdsAvatar, selector: "sds-avatar", inputs: ["name", "initials", "src", "size", "status", "bgClass"] }, { kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
16070
|
+
`, isInline: true, styles: [".animate-sds-menu{animation:sds-menu-in .14s ease-out}@keyframes sds-menu-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}@media(prefers-reduced-motion:reduce){.animate-sds-menu{animation:none}}\n"], dependencies: [{ kind: "component", type: SdsAvatar, selector: "sds-avatar", inputs: ["name", "initials", "src", "size", "status", "shape", "bgClass", "fallbackIcon", "placeholderClass", "decorative", "lazy"] }, { kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
14832
16071
|
}
|
|
14833
16072
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsUserMenu, decorators: [{
|
|
14834
16073
|
type: Component,
|
|
@@ -15902,5 +17141,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
15902
17141
|
* Generated bundle index. Do not edit.
|
|
15903
17142
|
*/
|
|
15904
17143
|
|
|
15905
|
-
export { SDS_ALLERGENS, SDS_ALLERGEN_ICONS, SDS_ALLERGEN_LABELS_EN, SDS_ICONS, SDS_PHONE_COUNTRIES, SdsAccordion, SdsAccordionContent, SdsAccordionHeader, SdsAccordionPanel, SdsAccordionStore, SdsAllergenIcon, SdsAvatar, SdsBadge, SdsBulkBar, SdsButton, SdsCalendar, SdsCard, SdsCheckbox, SdsChecklistRow, SdsDatePicker, SdsFileDrop, SdsIcon, SdsImageCropper, SdsInput, SdsInputNumber, SdsLanguageSwitcher, SdsLocalizedInput, SdsMenu, SdsMenuItem, SdsMenuTrigger, SdsMultiSelect, SdsNavGroup, SdsNavItem, SdsOtpInput, SdsPaginator, SdsPhoneInput, SdsRadio, SdsScore, SdsSearchCommand, SdsSegmented, SdsSelect, SdsSelectEmpty, SdsSelectFooter, SdsSelectOption, SdsShell, SdsSidebar, SdsSortableHandle, SdsSortableItem, SdsSortableList, SdsStatusTile, SdsTab, SdsTabList, SdsTabPanel, SdsTabPanels, SdsTable, SdsTableCell, SdsTableHeader, SdsTableRowExpansion, SdsTabs, SdsTabsStore, SdsTextarea, SdsThemeService, SdsThemeToggle, SdsToastHost, SdsToastService, SdsToggleSwitch, SdsTopbar, SdsTriStateChip, SdsUserMenu, SidebarStore, addDays, addMonths, addMonthsKeepDay, formatDdMmYyyy, formatHhMm, isOutOfRange, isSameDay, isoDay, pad2, sdsFileMatchesAccept, sdsFormatBytes, sdsFormatNational, sdsMatchDialCode, sdsNormalizeSignificant, startOfDay, startOfMonth };
|
|
17144
|
+
export { SDS_ALLERGENS, SDS_ALLERGEN_ICONS, SDS_ALLERGEN_LABELS_EN, SDS_ICONS, SDS_PHONE_COUNTRIES, SdsAccordion, SdsAccordionContent, SdsAccordionHeader, SdsAccordionPanel, SdsAccordionStore, SdsAllergenIcon, SdsAvatar, SdsBadge, SdsBulkBar, SdsButton, SdsCalendar, SdsCard, SdsCheckbox, SdsChecklistRow, SdsChip, SdsChipGroup, SdsDatePicker, SdsFileDrop, SdsIcon, SdsImageCropper, SdsInput, SdsInputNumber, SdsLanguageSwitcher, SdsLocalizedInput, SdsMenu, SdsMenuItem, SdsMenuTrigger, SdsMultiSelect, SdsNavGroup, SdsNavItem, SdsOtpInput, SdsPaginator, SdsPhoneInput, SdsRadio, SdsScore, SdsSearchCommand, SdsSegmented, SdsSelect, SdsSelectEmpty, SdsSelectFooter, SdsSelectOption, SdsShell, SdsSidebar, SdsSortableHandle, SdsSortableItem, SdsSortableList, SdsStatusTile, SdsTab, SdsTabList, SdsTabPanel, SdsTabPanels, SdsTable, SdsTableCell, SdsTableEmpty, SdsTableHeader, SdsTableRowExpansion, SdsTabs, SdsTabsStore, SdsTextarea, SdsThemeService, SdsThemeToggle, SdsTimeInput, SdsToastHost, SdsToastService, SdsToggleSwitch, SdsTopbar, SdsTriStateChip, SdsUserMenu, SidebarStore, addDays, addMonths, addMonthsKeepDay, formatDdMmYyyy, formatHhMm, isOutOfRange, isSameDay, isoDay, pad2, sdsFileMatchesAccept, sdsFormatBytes, sdsFormatNational, sdsMatchDialCode, sdsNormalizeSignificant, startOfDay, startOfMonth };
|
|
15906
17145
|
//# sourceMappingURL=besaitech-ng-design-system.mjs.map
|