@testgorilla/tgo-ui 11.18.7 → 11.19.0
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.
|
@@ -3,6 +3,7 @@ import { inject, ElementRef, DestroyRef, input, model, booleanAttribute, compute
|
|
|
3
3
|
import { MatTooltip } from '@angular/material/tooltip';
|
|
4
4
|
import * as i1 from '@testgorilla/tgo-ui/components/icon';
|
|
5
5
|
import { IconComponentModule } from '@testgorilla/tgo-ui/components/icon';
|
|
6
|
+
import { SkeletonComponent } from '@testgorilla/tgo-ui/components/skeleton';
|
|
6
7
|
import { TooltipPositionType } from '@testgorilla/tgo-ui/components/tooltip';
|
|
7
8
|
|
|
8
9
|
class SegmentedToggleComponent {
|
|
@@ -12,6 +13,11 @@ class SegmentedToggleComponent {
|
|
|
12
13
|
this.options = input.required(...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
13
14
|
this.value = model(...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
|
|
14
15
|
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
16
|
+
/**
|
|
17
|
+
* Shows a small skeleton to the right of each option's content, in place of
|
|
18
|
+
* its `count`, while the quantities are being fetched. Segments stay selectable.
|
|
19
|
+
*/
|
|
20
|
+
this.loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
15
21
|
this.ariaLabel = input(...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
16
22
|
this.testId = input(null, ...(ngDevMode ? [{ debugName: "testId" }] : /* istanbul ignore next */ []));
|
|
17
23
|
this.applicationTheme = input(undefined, ...(ngDevMode ? [{ debugName: "applicationTheme" }] : /* istanbul ignore next */ []));
|
|
@@ -19,6 +25,20 @@ class SegmentedToggleComponent {
|
|
|
19
25
|
optional: true,
|
|
20
26
|
});
|
|
21
27
|
this.theme = computed(() => this.applicationTheme() ?? this.defaultAppTheme ?? 'light', ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
|
|
28
|
+
/**
|
|
29
|
+
* Pill-shaped placeholder sized to a short count next to a 16px icon/label.
|
|
30
|
+
* Tinted from the segment's text colour so it follows every state (unselected,
|
|
31
|
+
* selected, brand, disabled); the library's fixed light grey vanishes on white
|
|
32
|
+
* and glares on the black pill.
|
|
33
|
+
*/
|
|
34
|
+
this.skeletonTheme = {
|
|
35
|
+
'background-color': 'color-mix(in srgb, currentColor 20%, transparent)',
|
|
36
|
+
width: '16px',
|
|
37
|
+
height: '12px',
|
|
38
|
+
margin: '0',
|
|
39
|
+
'border-radius': '999px',
|
|
40
|
+
'vertical-align': 'middle',
|
|
41
|
+
};
|
|
22
42
|
/** One ElementRef per segment button; used to measure the active pill geometry. */
|
|
23
43
|
this.optionButtons = viewChildren('optionButton', ...(ngDevMode ? [{ debugName: "optionButtons" }] : /* istanbul ignore next */ []));
|
|
24
44
|
/** Index of the selected option; -1 when value() matches no option. */
|
|
@@ -66,13 +86,16 @@ class SegmentedToggleComponent {
|
|
|
66
86
|
* Last-resort accessible name for a segment. Used only for an icon-only option
|
|
67
87
|
* that provides neither a visible `label` nor an `iconAriaLabel`, so the button
|
|
68
88
|
* is never a nameless control. When a label or icon label is present those name
|
|
69
|
-
* the button and this returns `null` (no `aria-label` attribute).
|
|
89
|
+
* the button and this returns `null` (no `aria-label` attribute). The `aria-label`
|
|
90
|
+
* replaces the button's content as its name, so a visible `count` is appended
|
|
91
|
+
* here or it would never be announced.
|
|
70
92
|
*/
|
|
71
93
|
fallbackAriaLabel(option) {
|
|
72
94
|
if (option.label || option.iconAriaLabel) {
|
|
73
95
|
return null;
|
|
74
96
|
}
|
|
75
|
-
|
|
97
|
+
const name = String(option.value);
|
|
98
|
+
return option.count !== undefined && !this.loading() ? `${name} ${option.count}` : name;
|
|
76
99
|
}
|
|
77
100
|
optionTestId(option) {
|
|
78
101
|
if (option.testId) {
|
|
@@ -106,16 +129,16 @@ class SegmentedToggleComponent {
|
|
|
106
129
|
this.indicatorHeight.set(button.offsetHeight);
|
|
107
130
|
}
|
|
108
131
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: SegmentedToggleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
109
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: SegmentedToggleComponent, isStandalone: true, selector: "ui-segmented-toggle", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null }, applicationTheme: { classPropertyName: "applicationTheme", publicName: "applicationTheme", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "attr.theme": "theme()", "class.ui-segmented-toggle--brand-active": "selectedOptionIsBrand()", "class.ui-segmented-toggle--disabled": "disabled()" } }, viewQueries: [{ propertyName: "optionButtons", predicate: ["optionButton"], descendants: true, isSignal: true }], ngImport: i0, template: "<div
|
|
132
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: SegmentedToggleComponent, isStandalone: true, selector: "ui-segmented-toggle", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null }, applicationTheme: { classPropertyName: "applicationTheme", publicName: "applicationTheme", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "attr.theme": "theme()", "class.ui-segmented-toggle--brand-active": "selectedOptionIsBrand()", "class.ui-segmented-toggle--disabled": "disabled()" } }, viewQueries: [{ propertyName: "optionButtons", predicate: ["optionButton"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"ui-segmented-toggle__group\"\n role=\"group\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-busy]=\"loading() || null\"\n [attr.data-testid]=\"testId()\"\n>\n <span\n class=\"ui-segmented-toggle__indicator\"\n [class.ui-segmented-toggle__indicator--hidden]=\"selectedIndex() < 0\"\n [class.ui-segmented-toggle__indicator--animated]=\"animated()\"\n aria-hidden=\"true\"\n [style.transform]=\"'translateX(' + indicatorOffset() + 'px) translateY(' + indicatorTop() + 'px)'\"\n [style.width.px]=\"indicatorWidth()\"\n [style.height.px]=\"indicatorHeight()\"\n ></span>\n\n @for (option of options(); track option.value) {\n <button\n #optionButton\n type=\"button\"\n class=\"ui-segmented-toggle__option\"\n [attr.aria-pressed]=\"isSelected(option.value)\"\n [attr.aria-label]=\"fallbackAriaLabel(option)\"\n [disabled]=\"disabled()\"\n [matTooltip]=\"option.tooltip ?? ''\"\n [matTooltipPosition]=\"tooltipPosition(option)\"\n [matTooltipClass]=\"theme()\"\n [attr.data-testid]=\"optionTestId(option)\"\n (click)=\"select(option.value)\"\n >\n @if (option.icon) {\n <ui-icon\n class=\"ui-segmented-toggle__icon\"\n [name]=\"option.icon\"\n size=\"16\"\n [attr.aria-hidden]=\"option.iconAriaLabel ? null : 'true'\"\n ></ui-icon>\n @if (option.iconAriaLabel) {\n <span class=\"ui-segmented-toggle__sr-only\">{{ option.iconAriaLabel }}</span>\n }\n }\n @if (option.label) {\n <span class=\"ui-segmented-toggle__label\">{{ option.label }}</span>\n }\n @if (loading()) {\n <span class=\"ui-segmented-toggle__skeleton\" aria-hidden=\"true\">\n <ui-skeleton [theme]=\"skeletonTheme\" [applicationTheme]=\"theme()\"></ui-skeleton>\n </span>\n } @else if (option.count !== undefined) {\n <span class=\"ui-segmented-toggle__count\">{{ option.count }}</span>\n }\n </button>\n }\n</div>\n", styles: [".bg-teal-60b{background:#1c443c}.bg-teal-30b{background:#31766a}.bg-teal-default{background:#46a997}.bg-teal-30w{background:#7ec3b6}.bg-teal-60w{background:#b5ddd5}.bg-teal-secondary{background:#cbd6cb}.bg-teal-90w{background:#ecf6f5}.bg-petrol-60b{background:#102930}.bg-petrol-30b{background:#1b4754}.bg-petrol-default{background:#276678}.bg-petrol-30w{background:#6894a0}.bg-petrol-60w{background:#a9c2c9}.bg-petrol-secondary{background:#c8d7de}.bg-petrol-90w{background:#e9f0f1}.bg-error-60b{background:#513131}.bg-error-30b{background:#8e5655}.bg-error-60w{background:#e3c3c6}.bg-error-secondary{background:#f0dad9}.bg-error-default{background:#cb7b7a}.bg-warning-secondary{background:#f0d6bb}.bg-warning-default{background:#cca45f}.bg-black{background:#000}.bg-dark{background:#888}.bg-medium{background:#e0e0e0}.bg-grey{background:#ededed}.bg-light{background:#f6f6f6}.bg-white{background:#fff}.bg-box-shadow{background:#00000014}.bg-navigation-subtitle{background:#528593}.bgc-teal-60b{background-color:#1c443c}.bgc-teal-30b{background-color:#31766a}.bgc-teal-default{background-color:#46a997}.bgc-teal-30w{background-color:#7ec3b6}.bgc-teal-60w{background-color:#b5ddd5}.bgc-teal-secondary{background-color:#cbd6cb}.bgc-teal-90w{background-color:#ecf6f5}.bgc-petrol-60b{background-color:#102930}.bgc-petrol-30b{background-color:#1b4754}.bgc-petrol-default{background-color:#276678}.bgc-petrol-30w{background-color:#6894a0}.bgc-petrol-60w{background-color:#a9c2c9}.bgc-petrol-secondary{background-color:#c8d7de}.bgc-petrol-90w{background-color:#e9f0f1}.bgc-error-60b{background-color:#513131}.bgc-error-30b{background-color:#8e5655}.bgc-error-60w{background-color:#e3c3c6}.bgc-error-secondary{background-color:#f0dad9}.bgc-error-default{background-color:#cb7b7a}.bgc-warning-secondary{background-color:#f0d6bb}.bgc-warning-default{background-color:#cca45f}.bgc-black{background-color:#000}.bgc-dark{background-color:#888}.bgc-medium{background-color:#e0e0e0}.bgc-grey{background-color:#ededed}.bgc-light{background-color:#f6f6f6}.bgc-white{background-color:#fff}.bgc-box-shadow{background-color:#00000014}.bgc-navigation-subtitle{background-color:#528593}:host{display:inline-flex}.ui-segmented-toggle__group{position:relative;display:flex;align-items:center;gap:0;padding:4px;box-shadow:inset 0 0 0 1px #242424;border-radius:999px;background:#fff}.ui-segmented-toggle__indicator{position:absolute;top:0;left:0;z-index:0;border-radius:999px;background:#242424}.ui-segmented-toggle__indicator--hidden{opacity:0}.ui-segmented-toggle__indicator--animated{transition:transform .2s ease,width .2s ease,opacity .2s ease}.ui-segmented-toggle__option{--cnp-icon-color: #242424;position:relative;z-index:1;display:inline-flex;flex:0 0 auto;gap:4px;align-items:center;justify-content:center;margin:0;padding:3px 16px;border:0;border-radius:999px;background:transparent;color:#242424;font:inherit;white-space:nowrap;cursor:pointer;transition:transform .1s ease}.ui-segmented-toggle__option:hover:not(:disabled,[aria-pressed=true]){background:#f4f4f4}.ui-segmented-toggle__option:active:not(:disabled,[aria-pressed=true]){transform:scale(.97)}.ui-segmented-toggle__option:focus-visible{outline:2px solid #242424;outline-offset:-2px}.ui-segmented-toggle__option[aria-pressed=true]:focus-visible{outline-color:#919191}.ui-segmented-toggle__option[aria-pressed=true]{--cnp-icon-color: #ffffff;color:#fff}.ui-segmented-toggle__option[aria-pressed=true] .ui-segmented-toggle__label{font-weight:700}:host(.ui-segmented-toggle--brand-active) .ui-segmented-toggle__indicator{background:#d410aa}:host(.ui-segmented-toggle--brand-active) .ui-segmented-toggle__option[aria-pressed=true]:focus-visible{outline-color:#fff}:host(.ui-segmented-toggle--brand-active) .ui-segmented-toggle__option[aria-pressed=true]{--cnp-icon-color: #ffe6fa;color:#ffe6fa}:host(.ui-segmented-toggle--disabled) .ui-segmented-toggle__group{box-shadow:inset 0 0 0 1px #919191}:host(.ui-segmented-toggle--disabled) .ui-segmented-toggle__indicator{background:#e9e9e9}:host(.ui-segmented-toggle--disabled) .ui-segmented-toggle__option,:host(.ui-segmented-toggle--disabled) .ui-segmented-toggle__option[aria-pressed=true]{--cnp-icon-color: #919191;color:#919191;cursor:not-allowed}.ui-segmented-toggle__skeleton{display:inline-flex;align-items:center;line-height:0}.ui-segmented-toggle__sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}@media(prefers-reduced-motion:reduce){.ui-segmented-toggle__indicator--animated,.ui-segmented-toggle__option{transition:none}.ui-segmented-toggle__option:active:not(:disabled,[aria-pressed=true]){transform:none}}\n"], dependencies: [{ kind: "ngmodule", type: IconComponentModule }, { kind: "component", type: i1.IconComponent, selector: "ui-icon", inputs: ["size", "cssClass", "name", "color", "filled", "toggleIconStyle", "applicationTheme", "useFullIconName"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SkeletonComponent, selector: "ui-skeleton", inputs: ["count", "theme", "appearance", "isAiTheme", "applicationTheme"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
110
133
|
}
|
|
111
134
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: SegmentedToggleComponent, decorators: [{
|
|
112
135
|
type: Component,
|
|
113
|
-
args: [{ selector: 'ui-segmented-toggle', standalone: true, imports: [IconComponentModule, MatTooltip], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
136
|
+
args: [{ selector: 'ui-segmented-toggle', standalone: true, imports: [IconComponentModule, MatTooltip, SkeletonComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
114
137
|
'[attr.theme]': 'theme()',
|
|
115
138
|
'[class.ui-segmented-toggle--brand-active]': 'selectedOptionIsBrand()',
|
|
116
139
|
'[class.ui-segmented-toggle--disabled]': 'disabled()',
|
|
117
|
-
}, template: "<div
|
|
118
|
-
}], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], applicationTheme: [{ type: i0.Input, args: [{ isSignal: true, alias: "applicationTheme", required: false }] }], optionButtons: [{ type: i0.ViewChildren, args: ['optionButton', { isSignal: true }] }] } });
|
|
140
|
+
}, template: "<div\n class=\"ui-segmented-toggle__group\"\n role=\"group\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-busy]=\"loading() || null\"\n [attr.data-testid]=\"testId()\"\n>\n <span\n class=\"ui-segmented-toggle__indicator\"\n [class.ui-segmented-toggle__indicator--hidden]=\"selectedIndex() < 0\"\n [class.ui-segmented-toggle__indicator--animated]=\"animated()\"\n aria-hidden=\"true\"\n [style.transform]=\"'translateX(' + indicatorOffset() + 'px) translateY(' + indicatorTop() + 'px)'\"\n [style.width.px]=\"indicatorWidth()\"\n [style.height.px]=\"indicatorHeight()\"\n ></span>\n\n @for (option of options(); track option.value) {\n <button\n #optionButton\n type=\"button\"\n class=\"ui-segmented-toggle__option\"\n [attr.aria-pressed]=\"isSelected(option.value)\"\n [attr.aria-label]=\"fallbackAriaLabel(option)\"\n [disabled]=\"disabled()\"\n [matTooltip]=\"option.tooltip ?? ''\"\n [matTooltipPosition]=\"tooltipPosition(option)\"\n [matTooltipClass]=\"theme()\"\n [attr.data-testid]=\"optionTestId(option)\"\n (click)=\"select(option.value)\"\n >\n @if (option.icon) {\n <ui-icon\n class=\"ui-segmented-toggle__icon\"\n [name]=\"option.icon\"\n size=\"16\"\n [attr.aria-hidden]=\"option.iconAriaLabel ? null : 'true'\"\n ></ui-icon>\n @if (option.iconAriaLabel) {\n <span class=\"ui-segmented-toggle__sr-only\">{{ option.iconAriaLabel }}</span>\n }\n }\n @if (option.label) {\n <span class=\"ui-segmented-toggle__label\">{{ option.label }}</span>\n }\n @if (loading()) {\n <span class=\"ui-segmented-toggle__skeleton\" aria-hidden=\"true\">\n <ui-skeleton [theme]=\"skeletonTheme\" [applicationTheme]=\"theme()\"></ui-skeleton>\n </span>\n } @else if (option.count !== undefined) {\n <span class=\"ui-segmented-toggle__count\">{{ option.count }}</span>\n }\n </button>\n }\n</div>\n", styles: [".bg-teal-60b{background:#1c443c}.bg-teal-30b{background:#31766a}.bg-teal-default{background:#46a997}.bg-teal-30w{background:#7ec3b6}.bg-teal-60w{background:#b5ddd5}.bg-teal-secondary{background:#cbd6cb}.bg-teal-90w{background:#ecf6f5}.bg-petrol-60b{background:#102930}.bg-petrol-30b{background:#1b4754}.bg-petrol-default{background:#276678}.bg-petrol-30w{background:#6894a0}.bg-petrol-60w{background:#a9c2c9}.bg-petrol-secondary{background:#c8d7de}.bg-petrol-90w{background:#e9f0f1}.bg-error-60b{background:#513131}.bg-error-30b{background:#8e5655}.bg-error-60w{background:#e3c3c6}.bg-error-secondary{background:#f0dad9}.bg-error-default{background:#cb7b7a}.bg-warning-secondary{background:#f0d6bb}.bg-warning-default{background:#cca45f}.bg-black{background:#000}.bg-dark{background:#888}.bg-medium{background:#e0e0e0}.bg-grey{background:#ededed}.bg-light{background:#f6f6f6}.bg-white{background:#fff}.bg-box-shadow{background:#00000014}.bg-navigation-subtitle{background:#528593}.bgc-teal-60b{background-color:#1c443c}.bgc-teal-30b{background-color:#31766a}.bgc-teal-default{background-color:#46a997}.bgc-teal-30w{background-color:#7ec3b6}.bgc-teal-60w{background-color:#b5ddd5}.bgc-teal-secondary{background-color:#cbd6cb}.bgc-teal-90w{background-color:#ecf6f5}.bgc-petrol-60b{background-color:#102930}.bgc-petrol-30b{background-color:#1b4754}.bgc-petrol-default{background-color:#276678}.bgc-petrol-30w{background-color:#6894a0}.bgc-petrol-60w{background-color:#a9c2c9}.bgc-petrol-secondary{background-color:#c8d7de}.bgc-petrol-90w{background-color:#e9f0f1}.bgc-error-60b{background-color:#513131}.bgc-error-30b{background-color:#8e5655}.bgc-error-60w{background-color:#e3c3c6}.bgc-error-secondary{background-color:#f0dad9}.bgc-error-default{background-color:#cb7b7a}.bgc-warning-secondary{background-color:#f0d6bb}.bgc-warning-default{background-color:#cca45f}.bgc-black{background-color:#000}.bgc-dark{background-color:#888}.bgc-medium{background-color:#e0e0e0}.bgc-grey{background-color:#ededed}.bgc-light{background-color:#f6f6f6}.bgc-white{background-color:#fff}.bgc-box-shadow{background-color:#00000014}.bgc-navigation-subtitle{background-color:#528593}:host{display:inline-flex}.ui-segmented-toggle__group{position:relative;display:flex;align-items:center;gap:0;padding:4px;box-shadow:inset 0 0 0 1px #242424;border-radius:999px;background:#fff}.ui-segmented-toggle__indicator{position:absolute;top:0;left:0;z-index:0;border-radius:999px;background:#242424}.ui-segmented-toggle__indicator--hidden{opacity:0}.ui-segmented-toggle__indicator--animated{transition:transform .2s ease,width .2s ease,opacity .2s ease}.ui-segmented-toggle__option{--cnp-icon-color: #242424;position:relative;z-index:1;display:inline-flex;flex:0 0 auto;gap:4px;align-items:center;justify-content:center;margin:0;padding:3px 16px;border:0;border-radius:999px;background:transparent;color:#242424;font:inherit;white-space:nowrap;cursor:pointer;transition:transform .1s ease}.ui-segmented-toggle__option:hover:not(:disabled,[aria-pressed=true]){background:#f4f4f4}.ui-segmented-toggle__option:active:not(:disabled,[aria-pressed=true]){transform:scale(.97)}.ui-segmented-toggle__option:focus-visible{outline:2px solid #242424;outline-offset:-2px}.ui-segmented-toggle__option[aria-pressed=true]:focus-visible{outline-color:#919191}.ui-segmented-toggle__option[aria-pressed=true]{--cnp-icon-color: #ffffff;color:#fff}.ui-segmented-toggle__option[aria-pressed=true] .ui-segmented-toggle__label{font-weight:700}:host(.ui-segmented-toggle--brand-active) .ui-segmented-toggle__indicator{background:#d410aa}:host(.ui-segmented-toggle--brand-active) .ui-segmented-toggle__option[aria-pressed=true]:focus-visible{outline-color:#fff}:host(.ui-segmented-toggle--brand-active) .ui-segmented-toggle__option[aria-pressed=true]{--cnp-icon-color: #ffe6fa;color:#ffe6fa}:host(.ui-segmented-toggle--disabled) .ui-segmented-toggle__group{box-shadow:inset 0 0 0 1px #919191}:host(.ui-segmented-toggle--disabled) .ui-segmented-toggle__indicator{background:#e9e9e9}:host(.ui-segmented-toggle--disabled) .ui-segmented-toggle__option,:host(.ui-segmented-toggle--disabled) .ui-segmented-toggle__option[aria-pressed=true]{--cnp-icon-color: #919191;color:#919191;cursor:not-allowed}.ui-segmented-toggle__skeleton{display:inline-flex;align-items:center;line-height:0}.ui-segmented-toggle__sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}@media(prefers-reduced-motion:reduce){.ui-segmented-toggle__indicator--animated,.ui-segmented-toggle__option{transition:none}.ui-segmented-toggle__option:active:not(:disabled,[aria-pressed=true]){transform:none}}\n"] }]
|
|
141
|
+
}], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], applicationTheme: [{ type: i0.Input, args: [{ isSignal: true, alias: "applicationTheme", required: false }] }], optionButtons: [{ type: i0.ViewChildren, args: ['optionButton', { isSignal: true }] }] } });
|
|
119
142
|
|
|
120
143
|
/**
|
|
121
144
|
* Generated bundle index. Do not edit.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testgorilla-tgo-ui-components-segmented-toggle.mjs","sources":["../../../components/segmented-toggle/segmented-toggle.component.ts","../../../components/segmented-toggle/segmented-toggle.component.html","../../../components/segmented-toggle/testgorilla-tgo-ui-components-segmented-toggle.ts"],"sourcesContent":["import {\n afterNextRender,\n afterRenderEffect,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n ElementRef,\n inject,\n input,\n model,\n signal,\n viewChildren,\n} from '@angular/core';\nimport { MatTooltip, TooltipPosition } from '@angular/material/tooltip';\n\nimport { ApplicationTheme } from '@testgorilla/tgo-ui/components/core';\nimport { IconComponentModule } from '@testgorilla/tgo-ui/components/icon';\nimport { TooltipPositionType } from '@testgorilla/tgo-ui/components/tooltip';\n\nimport { SegmentedToggleOption } from './segmented-toggle.model';\n\n@Component({\n selector: 'ui-segmented-toggle',\n standalone: true,\n imports: [IconComponentModule, MatTooltip],\n templateUrl: './segmented-toggle.component.html',\n styleUrls: ['./segmented-toggle.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[attr.theme]': 'theme()',\n '[class.ui-segmented-toggle--brand-active]': 'selectedOptionIsBrand()',\n '[class.ui-segmented-toggle--disabled]': 'disabled()',\n },\n})\nexport class SegmentedToggleComponent<T> {\n private readonly hostRef = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly destroyRef = inject(DestroyRef);\n\n readonly options = input.required<SegmentedToggleOption<T>[]>();\n readonly value = model<T>();\n readonly disabled = input(false, { transform: booleanAttribute });\n readonly ariaLabel = input<string>();\n\n readonly testId = input<string | null>(null);\n readonly applicationTheme = input<ApplicationTheme | undefined>(undefined);\n private readonly defaultAppTheme = inject<ApplicationTheme>('CANOPYUI_DEFAULT_APPLICATION_THEME' as never, {\n optional: true,\n });\n readonly theme = computed<ApplicationTheme>(() => this.applicationTheme() ?? this.defaultAppTheme ?? 'light');\n\n /** One ElementRef per segment button; used to measure the active pill geometry. */\n private readonly optionButtons = viewChildren<ElementRef<HTMLButtonElement>>('optionButton');\n\n /** Index of the selected option; -1 when value() matches no option. */\n protected readonly selectedIndex = computed(() => this.options().findIndex(option => option.value === this.value()));\n\n /** True when the currently-selected option is flagged `brand: true`. */\n protected readonly selectedOptionIsBrand = computed(() => this.options()[this.selectedIndex()]?.brand === true);\n\n /** Measured pixel geometry of the active button, driving the sliding indicator. */\n protected readonly indicatorOffset = signal(0);\n protected readonly indicatorTop = signal(0);\n protected readonly indicatorWidth = signal(0);\n protected readonly indicatorHeight = signal(0);\n\n /** Enables the slide transition only after the initial position is painted. */\n protected readonly animated = signal(false);\n\n constructor() {\n // Re-measure after every render that follows a selection or options change.\n afterRenderEffect(() => {\n // Read these so the effect re-runs (post-render) whenever selection changes.\n this.value();\n this.options();\n this.measureIndicator();\n });\n\n // Re-measure on container resize (width changes don't touch any signal).\n afterNextRender(() => {\n // Position the indicator for the initial selection without animating, then\n // enable the slide on the next frame so only user-driven changes animate.\n this.measureIndicator();\n requestAnimationFrame(() => this.animated.set(true));\n\n if (typeof ResizeObserver === 'undefined') {\n return;\n }\n const observer = new ResizeObserver(() => this.measureIndicator());\n observer.observe(this.hostRef.nativeElement);\n this.destroyRef.onDestroy(() => observer.disconnect());\n });\n }\n\n protected select(optionValue: T): void {\n if (this.disabled()) {\n return;\n }\n this.value.set(optionValue);\n }\n\n protected isSelected(optionValue: T): boolean {\n return optionValue === this.value();\n }\n\n /**\n * Last-resort accessible name for a segment. Used only for an icon-only option\n * that provides neither a visible `label` nor an `iconAriaLabel`, so the button\n * is never a nameless control. When a label or icon label is present those name\n * the button and this returns `null` (no `aria-label` attribute).\n */\n protected fallbackAriaLabel(option: SegmentedToggleOption<T>): string | null {\n if (option.label || option.iconAriaLabel) {\n return null;\n }\n return String(option.value);\n }\n\n protected optionTestId(option: SegmentedToggleOption<T>): string | null {\n if (option.testId) {\n return option.testId;\n }\n const testId = this.testId();\n return testId ? `${testId}-${String(option.value)}` : null;\n }\n\n /** Maps the option's Canopy tooltip position (default TOP) to the MatTooltip position. */\n protected tooltipPosition(option: SegmentedToggleOption<T>): TooltipPosition {\n switch (option.tooltipPosition ?? TooltipPositionType.TOP) {\n case TooltipPositionType.BOTTOM:\n return 'below';\n case TooltipPositionType.LEFT:\n return 'left';\n case TooltipPositionType.RIGHT:\n return 'right';\n case TooltipPositionType.TOP:\n return 'above';\n }\n }\n\n /** Reads the active button's box and writes it to the indicator signals. */\n private measureIndicator(): void {\n const button = this.optionButtons()[this.selectedIndex()]?.nativeElement;\n if (!button) {\n return;\n }\n this.indicatorOffset.set(button.offsetLeft);\n this.indicatorTop.set(button.offsetTop);\n this.indicatorWidth.set(button.offsetWidth);\n this.indicatorHeight.set(button.offsetHeight);\n }\n}\n","<div class=\"ui-segmented-toggle__group\" role=\"group\" [attr.aria-label]=\"ariaLabel()\" [attr.data-testid]=\"testId()\">\n <span\n class=\"ui-segmented-toggle__indicator\"\n [class.ui-segmented-toggle__indicator--hidden]=\"selectedIndex() < 0\"\n [class.ui-segmented-toggle__indicator--animated]=\"animated()\"\n aria-hidden=\"true\"\n [style.transform]=\"'translateX(' + indicatorOffset() + 'px) translateY(' + indicatorTop() + 'px)'\"\n [style.width.px]=\"indicatorWidth()\"\n [style.height.px]=\"indicatorHeight()\"\n ></span>\n\n @for (option of options(); track option.value) {\n <button\n #optionButton\n type=\"button\"\n class=\"ui-segmented-toggle__option\"\n [attr.aria-pressed]=\"isSelected(option.value)\"\n [attr.aria-label]=\"fallbackAriaLabel(option)\"\n [disabled]=\"disabled()\"\n [matTooltip]=\"option.tooltip ?? ''\"\n [matTooltipPosition]=\"tooltipPosition(option)\"\n [matTooltipClass]=\"theme()\"\n [attr.data-testid]=\"optionTestId(option)\"\n (click)=\"select(option.value)\"\n >\n @if (option.icon) {\n <ui-icon\n class=\"ui-segmented-toggle__icon\"\n [name]=\"option.icon\"\n size=\"16\"\n [attr.aria-hidden]=\"option.iconAriaLabel ? null : 'true'\"\n ></ui-icon>\n @if (option.iconAriaLabel) {\n <span class=\"ui-segmented-toggle__sr-only\">{{ option.iconAriaLabel }}</span>\n }\n }\n @if (option.label) {\n <span class=\"ui-segmented-toggle__label\">{{ option.label }}</span>\n }\n </button>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"testgorilla-tgo-ui-components-segmented-toggle.mjs","sources":["../../../components/segmented-toggle/segmented-toggle.component.ts","../../../components/segmented-toggle/segmented-toggle.component.html","../../../components/segmented-toggle/testgorilla-tgo-ui-components-segmented-toggle.ts"],"sourcesContent":["import {\n afterNextRender,\n afterRenderEffect,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n ElementRef,\n inject,\n input,\n model,\n signal,\n viewChildren,\n} from '@angular/core';\nimport { MatTooltip, TooltipPosition } from '@angular/material/tooltip';\n\nimport { ApplicationTheme } from '@testgorilla/tgo-ui/components/core';\nimport { IconComponentModule } from '@testgorilla/tgo-ui/components/icon';\nimport { SkeletonComponent } from '@testgorilla/tgo-ui/components/skeleton';\nimport { TooltipPositionType } from '@testgorilla/tgo-ui/components/tooltip';\n\nimport { SegmentedToggleOption } from './segmented-toggle.model';\n\n@Component({\n selector: 'ui-segmented-toggle',\n standalone: true,\n imports: [IconComponentModule, MatTooltip, SkeletonComponent],\n templateUrl: './segmented-toggle.component.html',\n styleUrls: ['./segmented-toggle.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[attr.theme]': 'theme()',\n '[class.ui-segmented-toggle--brand-active]': 'selectedOptionIsBrand()',\n '[class.ui-segmented-toggle--disabled]': 'disabled()',\n },\n})\nexport class SegmentedToggleComponent<T> {\n private readonly hostRef = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly destroyRef = inject(DestroyRef);\n\n readonly options = input.required<SegmentedToggleOption<T>[]>();\n readonly value = model<T>();\n readonly disabled = input(false, { transform: booleanAttribute });\n /**\n * Shows a small skeleton to the right of each option's content, in place of\n * its `count`, while the quantities are being fetched. Segments stay selectable.\n */\n readonly loading = input(false, { transform: booleanAttribute });\n readonly ariaLabel = input<string>();\n\n readonly testId = input<string | null>(null);\n readonly applicationTheme = input<ApplicationTheme | undefined>(undefined);\n private readonly defaultAppTheme = inject<ApplicationTheme>('CANOPYUI_DEFAULT_APPLICATION_THEME' as never, {\n optional: true,\n });\n readonly theme = computed<ApplicationTheme>(() => this.applicationTheme() ?? this.defaultAppTheme ?? 'light');\n\n /**\n * Pill-shaped placeholder sized to a short count next to a 16px icon/label.\n * Tinted from the segment's text colour so it follows every state (unselected,\n * selected, brand, disabled); the library's fixed light grey vanishes on white\n * and glares on the black pill.\n */\n protected readonly skeletonTheme = {\n 'background-color': 'color-mix(in srgb, currentColor 20%, transparent)',\n width: '16px',\n height: '12px',\n margin: '0',\n 'border-radius': '999px',\n 'vertical-align': 'middle',\n };\n\n /** One ElementRef per segment button; used to measure the active pill geometry. */\n private readonly optionButtons = viewChildren<ElementRef<HTMLButtonElement>>('optionButton');\n\n /** Index of the selected option; -1 when value() matches no option. */\n protected readonly selectedIndex = computed(() => this.options().findIndex(option => option.value === this.value()));\n\n /** True when the currently-selected option is flagged `brand: true`. */\n protected readonly selectedOptionIsBrand = computed(() => this.options()[this.selectedIndex()]?.brand === true);\n\n /** Measured pixel geometry of the active button, driving the sliding indicator. */\n protected readonly indicatorOffset = signal(0);\n protected readonly indicatorTop = signal(0);\n protected readonly indicatorWidth = signal(0);\n protected readonly indicatorHeight = signal(0);\n\n /** Enables the slide transition only after the initial position is painted. */\n protected readonly animated = signal(false);\n\n constructor() {\n // Re-measure after every render that follows a selection or options change.\n afterRenderEffect(() => {\n // Read these so the effect re-runs (post-render) whenever selection changes.\n this.value();\n this.options();\n this.measureIndicator();\n });\n\n // Re-measure on container resize (width changes don't touch any signal).\n afterNextRender(() => {\n // Position the indicator for the initial selection without animating, then\n // enable the slide on the next frame so only user-driven changes animate.\n this.measureIndicator();\n requestAnimationFrame(() => this.animated.set(true));\n\n if (typeof ResizeObserver === 'undefined') {\n return;\n }\n const observer = new ResizeObserver(() => this.measureIndicator());\n observer.observe(this.hostRef.nativeElement);\n this.destroyRef.onDestroy(() => observer.disconnect());\n });\n }\n\n protected select(optionValue: T): void {\n if (this.disabled()) {\n return;\n }\n this.value.set(optionValue);\n }\n\n protected isSelected(optionValue: T): boolean {\n return optionValue === this.value();\n }\n\n /**\n * Last-resort accessible name for a segment. Used only for an icon-only option\n * that provides neither a visible `label` nor an `iconAriaLabel`, so the button\n * is never a nameless control. When a label or icon label is present those name\n * the button and this returns `null` (no `aria-label` attribute). The `aria-label`\n * replaces the button's content as its name, so a visible `count` is appended\n * here or it would never be announced.\n */\n protected fallbackAriaLabel(option: SegmentedToggleOption<T>): string | null {\n if (option.label || option.iconAriaLabel) {\n return null;\n }\n const name = String(option.value);\n return option.count !== undefined && !this.loading() ? `${name} ${option.count}` : name;\n }\n\n protected optionTestId(option: SegmentedToggleOption<T>): string | null {\n if (option.testId) {\n return option.testId;\n }\n const testId = this.testId();\n return testId ? `${testId}-${String(option.value)}` : null;\n }\n\n /** Maps the option's Canopy tooltip position (default TOP) to the MatTooltip position. */\n protected tooltipPosition(option: SegmentedToggleOption<T>): TooltipPosition {\n switch (option.tooltipPosition ?? TooltipPositionType.TOP) {\n case TooltipPositionType.BOTTOM:\n return 'below';\n case TooltipPositionType.LEFT:\n return 'left';\n case TooltipPositionType.RIGHT:\n return 'right';\n case TooltipPositionType.TOP:\n return 'above';\n }\n }\n\n /** Reads the active button's box and writes it to the indicator signals. */\n private measureIndicator(): void {\n const button = this.optionButtons()[this.selectedIndex()]?.nativeElement;\n if (!button) {\n return;\n }\n this.indicatorOffset.set(button.offsetLeft);\n this.indicatorTop.set(button.offsetTop);\n this.indicatorWidth.set(button.offsetWidth);\n this.indicatorHeight.set(button.offsetHeight);\n }\n}\n","<div\n class=\"ui-segmented-toggle__group\"\n role=\"group\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-busy]=\"loading() || null\"\n [attr.data-testid]=\"testId()\"\n>\n <span\n class=\"ui-segmented-toggle__indicator\"\n [class.ui-segmented-toggle__indicator--hidden]=\"selectedIndex() < 0\"\n [class.ui-segmented-toggle__indicator--animated]=\"animated()\"\n aria-hidden=\"true\"\n [style.transform]=\"'translateX(' + indicatorOffset() + 'px) translateY(' + indicatorTop() + 'px)'\"\n [style.width.px]=\"indicatorWidth()\"\n [style.height.px]=\"indicatorHeight()\"\n ></span>\n\n @for (option of options(); track option.value) {\n <button\n #optionButton\n type=\"button\"\n class=\"ui-segmented-toggle__option\"\n [attr.aria-pressed]=\"isSelected(option.value)\"\n [attr.aria-label]=\"fallbackAriaLabel(option)\"\n [disabled]=\"disabled()\"\n [matTooltip]=\"option.tooltip ?? ''\"\n [matTooltipPosition]=\"tooltipPosition(option)\"\n [matTooltipClass]=\"theme()\"\n [attr.data-testid]=\"optionTestId(option)\"\n (click)=\"select(option.value)\"\n >\n @if (option.icon) {\n <ui-icon\n class=\"ui-segmented-toggle__icon\"\n [name]=\"option.icon\"\n size=\"16\"\n [attr.aria-hidden]=\"option.iconAriaLabel ? null : 'true'\"\n ></ui-icon>\n @if (option.iconAriaLabel) {\n <span class=\"ui-segmented-toggle__sr-only\">{{ option.iconAriaLabel }}</span>\n }\n }\n @if (option.label) {\n <span class=\"ui-segmented-toggle__label\">{{ option.label }}</span>\n }\n @if (loading()) {\n <span class=\"ui-segmented-toggle__skeleton\" aria-hidden=\"true\">\n <ui-skeleton [theme]=\"skeletonTheme\" [applicationTheme]=\"theme()\"></ui-skeleton>\n </span>\n } @else if (option.count !== undefined) {\n <span class=\"ui-segmented-toggle__count\">{{ option.count }}</span>\n }\n </button>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|