@egose/shadcn-theme-ng 0.1.0 → 0.3.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.
- package/README.md +2 -2
- package/accordion/README.md +405 -2
- package/alert/README.md +372 -2
- package/alert-dialog/README.md +471 -5
- package/aspect-ratio/README.md +272 -5
- package/autocomplete/README.md +502 -2
- package/autocomplete/fesm2022/autocomplete.mjs +1 -1
- package/avatar/README.md +357 -5
- package/badge/README.md +318 -2
- package/basic-alert/README.md +353 -2
- package/breadcrumb/README.md +406 -5
- package/button/README.md +482 -2
- package/button/fesm2022/button.mjs +85 -107
- package/button/types/button.d.ts +5 -8
- package/button-group/README.md +318 -5
- package/calendar/README.md +357 -2
- package/card/README.md +331 -5
- package/carousel/README.md +333 -5
- package/carousel/fesm2022/carousel.mjs +4 -1
- package/checkbox/README.md +320 -2
- package/checkbox/fesm2022/checkbox.mjs +6 -7
- package/checkbox/types/checkbox.d.ts +1 -1
- package/collapsible/README.md +332 -5
- package/combobox/README.md +507 -5
- package/combobox/fesm2022/combobox.mjs +5 -2
- package/command/README.md +435 -5
- package/confirmation-dialog/README.md +301 -2
- package/context-menu/README.md +366 -5
- package/date-picker/README.md +469 -2
- package/date-picker/fesm2022/date-picker.mjs +150 -32
- package/date-picker/types/date-picker.d.ts +102 -9
- package/dialog/README.md +448 -2
- package/drawer/README.md +395 -5
- package/dropdown-menu/README.md +417 -5
- package/empty/README.md +329 -5
- package/field/README.md +385 -5
- package/form-autocomplete/README.md +177 -0
- package/form-autocomplete/fesm2022/form-autocomplete.mjs +125 -0
- package/form-autocomplete/package.json +24 -0
- package/form-autocomplete/types/form-autocomplete.d.ts +61 -0
- package/form-checkbox/README.md +322 -2
- package/form-checkbox/fesm2022/form-checkbox.mjs +22 -9
- package/form-checkbox/types/form-checkbox.d.ts +23 -4
- package/form-combobox/README.md +202 -0
- package/form-combobox/fesm2022/form-combobox.mjs +147 -0
- package/form-combobox/package.json +24 -0
- package/form-combobox/types/form-combobox.d.ts +73 -0
- package/form-date-picker/README.md +348 -2
- package/form-date-picker/fesm2022/form-date-picker.mjs +38 -12
- package/form-date-picker/types/form-date-picker.d.ts +17 -1
- package/form-date-picker-multi/README.md +191 -0
- package/form-date-picker-multi/fesm2022/form-date-picker-multi.mjs +129 -0
- package/form-date-picker-multi/package.json +24 -0
- package/form-date-picker-multi/types/form-date-picker-multi.d.ts +58 -0
- package/form-date-range-picker/README.md +253 -0
- package/form-date-range-picker/fesm2022/form-date-range-picker.mjs +130 -0
- package/form-date-range-picker/package.json +24 -0
- package/form-date-range-picker/types/form-date-range-picker.d.ts +55 -0
- package/form-field/README.md +356 -2
- package/form-field-simple/README.md +340 -2
- package/form-input-otp/README.md +194 -0
- package/form-input-otp/fesm2022/form-input-otp.mjs +106 -0
- package/form-input-otp/package.json +24 -0
- package/form-input-otp/types/form-input-otp.d.ts +58 -0
- package/form-month-year-picker/README.md +188 -0
- package/form-month-year-picker/fesm2022/form-month-year-picker.mjs +125 -0
- package/form-month-year-picker/package.json +24 -0
- package/form-month-year-picker/types/form-month-year-picker.d.ts +53 -0
- package/form-native-select/README.md +205 -0
- package/form-native-select/fesm2022/form-native-select.mjs +103 -0
- package/form-native-select/package.json +24 -0
- package/form-native-select/types/form-native-select.d.ts +57 -0
- package/form-phone-input/README.md +188 -0
- package/form-phone-input/fesm2022/form-phone-input.mjs +113 -0
- package/form-phone-input/package.json +24 -0
- package/form-phone-input/types/form-phone-input.d.ts +58 -0
- package/form-radio-group/README.md +198 -0
- package/form-radio-group/fesm2022/form-radio-group.mjs +111 -0
- package/form-radio-group/package.json +24 -0
- package/form-radio-group/types/form-radio-group.d.ts +63 -0
- package/form-searchable-multiselect/README.md +371 -2
- package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +19 -10
- package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +18 -1
- package/form-select/README.md +360 -2
- package/form-select/fesm2022/form-select.mjs +20 -11
- package/form-select/types/form-select.d.ts +18 -1
- package/form-slider/README.md +182 -0
- package/form-slider/fesm2022/form-slider.mjs +106 -0
- package/form-slider/package.json +24 -0
- package/form-slider/types/form-slider.d.ts +60 -0
- package/form-switch/README.md +173 -0
- package/form-switch/fesm2022/form-switch.mjs +100 -0
- package/form-switch/package.json +24 -0
- package/form-switch/types/form-switch.d.ts +50 -0
- package/form-text-input/README.md +381 -2
- package/form-text-input/fesm2022/form-text-input.mjs +19 -10
- package/form-text-input/types/form-text-input.d.ts +18 -1
- package/form-textarea/README.md +357 -2
- package/form-textarea/fesm2022/form-textarea.mjs +19 -10
- package/form-textarea/types/form-textarea.d.ts +18 -1
- package/form-toggle/README.md +186 -0
- package/form-toggle/fesm2022/form-toggle.mjs +159 -0
- package/form-toggle/package.json +24 -0
- package/form-toggle/types/form-toggle.d.ts +82 -0
- package/form-toggle-group/README.md +176 -0
- package/form-toggle-group/fesm2022/form-toggle-group.mjs +116 -0
- package/form-toggle-group/package.json +24 -0
- package/form-toggle-group/types/form-toggle-group.d.ts +65 -0
- package/hover-card/README.md +256 -5
- package/icon/README.md +239 -2
- package/input/README.md +269 -2
- package/input-group/README.md +335 -5
- package/input-group/fesm2022/input-group.mjs +22 -12
- package/input-group/types/input-group.d.ts +4 -1
- package/input-otp/README.md +375 -5
- package/item/README.md +385 -5
- package/item/fesm2022/item.mjs +3 -3
- package/kbd/README.md +291 -5
- package/label/README.md +272 -2
- package/layout-simple/README.md +193 -2
- package/layout-simple/fesm2022/layout-simple.mjs +472 -236
- package/layout-simple/types/layout-simple.d.ts +174 -137
- package/menu/README.md +417 -2
- package/menubar/README.md +343 -5
- package/native-select/README.md +323 -5
- package/native-select/fesm2022/native-select.mjs +18 -6
- package/native-select/types/native-select.d.ts +7 -2
- package/navigation-menu/README.md +369 -5
- package/package.json +57 -1
- package/pagination/README.md +388 -5
- package/phone-input/README.md +114 -0
- package/phone-input/fesm2022/phone-input.mjs +191 -0
- package/phone-input/package.json +24 -0
- package/phone-input/types/phone-input.d.ts +67 -0
- package/popover/README.md +331 -2
- package/progress/README.md +311 -5
- package/radio-group/README.md +364 -2
- package/radio-group/fesm2022/radio-group.mjs +5 -1
- package/resizable/README.md +269 -5
- package/scroll-area/README.md +233 -5
- package/searchable-multiselect/README.md +323 -2
- package/select/README.md +437 -2
- package/separator/README.md +222 -2
- package/sheet/README.md +311 -2
- package/sidebar/README.md +457 -5
- package/skeleton/README.md +217 -5
- package/slider/README.md +273 -5
- package/slider/fesm2022/slider.mjs +3 -3
- package/sonner/README.md +346 -2
- package/spinner/README.md +284 -2
- package/switch/README.md +310 -2
- package/switch/fesm2022/switch.mjs +7 -5
- package/switch/types/switch.d.ts +2 -1
- package/table/README.md +423 -5
- package/tabs/README.md +411 -2
- package/tabs/fesm2022/tabs.mjs +2 -2
- package/textarea/README.md +282 -5
- package/toggle/README.md +270 -5
- package/toggle-group/README.md +340 -5
- package/tooltip/README.md +269 -2
- package/typography/README.md +271 -5
- package/utils/README.md +303 -2
|
@@ -7,6 +7,59 @@ import { NgTemplateOutlet } from '@angular/common';
|
|
|
7
7
|
import { HlmSpinner } from '@egose/shadcn-theme-ng/spinner';
|
|
8
8
|
import { cva } from 'class-variance-authority';
|
|
9
9
|
|
|
10
|
+
// Keep complete utility names here so consumer Tailwind builds can discover every tone.
|
|
11
|
+
const toneTextClasses = {
|
|
12
|
+
default: "text-primary",
|
|
13
|
+
primary: "text-primary",
|
|
14
|
+
secondary: "text-secondary",
|
|
15
|
+
success: "text-success",
|
|
16
|
+
warning: "text-warning",
|
|
17
|
+
danger: "text-danger",
|
|
18
|
+
info: "text-info",
|
|
19
|
+
light: "text-light-foreground",
|
|
20
|
+
dark: "text-dark",
|
|
21
|
+
accent: "text-accent",
|
|
22
|
+
destructive: "text-destructive",
|
|
23
|
+
muted: "text-muted-foreground",
|
|
24
|
+
outline: "text-primary",
|
|
25
|
+
link: "text-primary",
|
|
26
|
+
ghost: "text-light-foreground",
|
|
27
|
+
};
|
|
28
|
+
const outlineClasses = {
|
|
29
|
+
default: "border-primary hover:bg-primary/10",
|
|
30
|
+
primary: "border-primary hover:bg-primary/10",
|
|
31
|
+
secondary: "border-secondary hover:bg-secondary/10",
|
|
32
|
+
success: "border-success hover:bg-success/10",
|
|
33
|
+
warning: "border-warning hover:bg-warning/10",
|
|
34
|
+
danger: "border-danger hover:bg-danger/10",
|
|
35
|
+
info: "border-info hover:bg-info/10",
|
|
36
|
+
light: "border-light hover:bg-light/10",
|
|
37
|
+
dark: "border-dark hover:bg-dark/10",
|
|
38
|
+
accent: "border-accent hover:bg-accent/10",
|
|
39
|
+
destructive: "border-destructive hover:bg-destructive/10",
|
|
40
|
+
muted: "border-muted hover:bg-muted/10",
|
|
41
|
+
outline: "border-primary hover:bg-primary/10",
|
|
42
|
+
link: '',
|
|
43
|
+
ghost: '',
|
|
44
|
+
};
|
|
45
|
+
const outlineFilledClasses = {
|
|
46
|
+
default: "hover:bg-primary hover:text-primary-foreground",
|
|
47
|
+
primary: "hover:bg-primary hover:text-primary-foreground",
|
|
48
|
+
secondary: "hover:bg-secondary hover:text-secondary-foreground",
|
|
49
|
+
success: "hover:bg-success hover:text-success-foreground",
|
|
50
|
+
warning: "hover:bg-warning hover:text-warning-foreground",
|
|
51
|
+
danger: "hover:bg-danger hover:text-danger-foreground",
|
|
52
|
+
info: "hover:bg-info hover:text-info-foreground",
|
|
53
|
+
light: "hover:bg-light hover:text-light-foreground",
|
|
54
|
+
dark: "hover:bg-dark hover:text-dark-foreground",
|
|
55
|
+
accent: "hover:bg-accent hover:text-accent-foreground",
|
|
56
|
+
destructive: "hover:bg-destructive hover:text-destructive-foreground",
|
|
57
|
+
muted: "hover:bg-muted hover:text-muted-foreground",
|
|
58
|
+
outline: "hover:bg-primary hover:text-primary-foreground",
|
|
59
|
+
link: "hover:underline",
|
|
60
|
+
ghost: "hover:bg-light",
|
|
61
|
+
};
|
|
62
|
+
/** Semantic color utilities and appearances. Merge the result with hlm() for custom hosts. */
|
|
10
63
|
const buttonVariants = cva([
|
|
11
64
|
"cursor-pointer",
|
|
12
65
|
"active:translate-y-[0.5px]",
|
|
@@ -45,7 +98,7 @@ const buttonVariants = cva([
|
|
|
45
98
|
accent: "border border-accent bg-accent text-accent-foreground shadow-sm hover:bg-accent/90",
|
|
46
99
|
destructive: "border border-destructive bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
|
47
100
|
muted: "border border-muted bg-muted text-muted-foreground shadow-sm hover:bg-muted/90",
|
|
48
|
-
outline: "border border-primary bg-
|
|
101
|
+
outline: "border border-primary bg-background text-primary shadow-sm hover:bg-primary/10",
|
|
49
102
|
link: "text-primary underline-offset-4 hover:underline",
|
|
50
103
|
ghost: "hover:bg-light hover:text-light-foreground",
|
|
51
104
|
},
|
|
@@ -66,14 +119,33 @@ const buttonVariants = cva([
|
|
|
66
119
|
},
|
|
67
120
|
appearance: {
|
|
68
121
|
solid: '',
|
|
69
|
-
outline: "bg-
|
|
70
|
-
'outline-filled': "bg-
|
|
122
|
+
outline: "bg-background border shadow-sm",
|
|
123
|
+
'outline-filled': "bg-background border shadow-sm",
|
|
124
|
+
ghost: "bg-transparent border-0 shadow-none",
|
|
125
|
+
link: "bg-transparent border-0 shadow-none hover:bg-transparent underline-offset-4 hover:underline",
|
|
71
126
|
},
|
|
72
127
|
loading: {
|
|
73
128
|
true: "pointer-events-none",
|
|
74
129
|
false: null,
|
|
75
130
|
},
|
|
76
131
|
},
|
|
132
|
+
compoundVariants: Object.keys(toneTextClasses).flatMap((variant) => [
|
|
133
|
+
{
|
|
134
|
+
variant,
|
|
135
|
+
appearance: ['outline', 'outline-filled', 'ghost', 'link'],
|
|
136
|
+
class: toneTextClasses[variant],
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
variant,
|
|
140
|
+
appearance: ['outline', 'outline-filled', 'ghost'],
|
|
141
|
+
class: outlineClasses[variant],
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
variant,
|
|
145
|
+
appearance: 'outline-filled',
|
|
146
|
+
class: outlineFilledClasses[variant],
|
|
147
|
+
},
|
|
148
|
+
]),
|
|
77
149
|
defaultVariants: {
|
|
78
150
|
variant: 'primary',
|
|
79
151
|
size: 'default',
|
|
@@ -82,11 +154,12 @@ const buttonVariants = cva([
|
|
|
82
154
|
},
|
|
83
155
|
});
|
|
84
156
|
class HlmButton {
|
|
85
|
-
/**
|
|
157
|
+
/** Semantic color token. Legacy default, outline, link, and ghost variants remain supported. */
|
|
86
158
|
variant = input('primary', /* @ts-ignore */
|
|
87
159
|
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
88
160
|
size = input('default', /* @ts-ignore */
|
|
89
161
|
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
162
|
+
/** Visual treatment independent of color: solid, outline, outline-filled, ghost, or link. */
|
|
90
163
|
appearance = input('solid', /* @ts-ignore */
|
|
91
164
|
...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
|
|
92
165
|
loading = input(false, /* @ts-ignore */
|
|
@@ -105,114 +178,19 @@ class HlmButton {
|
|
|
105
178
|
_additionalClasses = signal('', /* @ts-ignore */
|
|
106
179
|
...(ngDevMode ? [{ debugName: "_additionalClasses" }] : /* istanbul ignore next */ []));
|
|
107
180
|
/** Computed button class merging */
|
|
108
|
-
_computedClass = computed(() => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
return hlm(buttonVariants({
|
|
116
|
-
variant: this.variant(),
|
|
117
|
-
size: this.size(),
|
|
118
|
-
appearance: this.appearance(),
|
|
119
|
-
loading: this.loading(),
|
|
120
|
-
className: this.userClass(),
|
|
121
|
-
}), outlineClasses, this.loading() ? "pointer-events-none" : '', this._additionalClasses());
|
|
122
|
-
}, /* @ts-ignore */
|
|
181
|
+
_computedClass = computed(() => hlm(buttonVariants({
|
|
182
|
+
variant: this.variant(),
|
|
183
|
+
size: this.size(),
|
|
184
|
+
appearance: this.appearance(),
|
|
185
|
+
loading: this.loading(),
|
|
186
|
+
}), this.userClass(), this.loading() ? "pointer-events-none" : '', this._additionalClasses()), /* @ts-ignore */
|
|
123
187
|
...(ngDevMode ? [{ debugName: "_computedClass" }] : /* istanbul ignore next */ []));
|
|
124
|
-
/**
|
|
125
|
-
spinnerClass = computed(() =>
|
|
126
|
-
const base = this.appearance() === 'outline' || this.appearance() === 'outline-filled'
|
|
127
|
-
? this.getOutlineSpinnerClasses(this.variant())
|
|
128
|
-
: this.getSpinnerClasses(this.variant());
|
|
129
|
-
return hlm(base, this.spinnerUserClass());
|
|
130
|
-
}, /* @ts-ignore */
|
|
188
|
+
/** Inherit the button's resolved text color, including theme and consumer overrides. */
|
|
189
|
+
spinnerClass = computed(() => hlm("[&>svg]:text-current", this.spinnerUserClass()), /* @ts-ignore */
|
|
131
190
|
...(ngDevMode ? [{ debugName: "spinnerClass" }] : /* istanbul ignore next */ []));
|
|
132
191
|
setClass(classes) {
|
|
133
192
|
this._additionalClasses.set(classes);
|
|
134
193
|
}
|
|
135
|
-
/** Helper functions */
|
|
136
|
-
getOutlineClasses(variant) {
|
|
137
|
-
const colors = {
|
|
138
|
-
default: "border-primary text-primary shadow-sm hover:bg-primary/10",
|
|
139
|
-
primary: "border-primary text-primary shadow-sm hover:bg-primary/10",
|
|
140
|
-
secondary: "border-secondary text-secondary shadow-sm hover:bg-secondary/10",
|
|
141
|
-
success: "border-success text-success shadow-sm hover:bg-success/10",
|
|
142
|
-
warning: "border-warning text-warning shadow-sm hover:bg-warning/10",
|
|
143
|
-
danger: "border-danger text-danger shadow-sm hover:bg-danger/10",
|
|
144
|
-
info: "border-info text-info shadow-sm hover:bg-info/10",
|
|
145
|
-
light: "border-light text-light-foreground shadow-sm hover:bg-light/10",
|
|
146
|
-
dark: "border-dark text-dark shadow-sm hover:bg-dark/10",
|
|
147
|
-
accent: "border-accent text-accent shadow-sm hover:bg-accent/10",
|
|
148
|
-
destructive: "border-destructive text-destructive shadow-sm hover:bg-destructive/10",
|
|
149
|
-
muted: "border-muted text-muted-foreground shadow-sm hover:bg-muted/10",
|
|
150
|
-
outline: "border-primary text-primary shadow-sm hover:bg-primary/10",
|
|
151
|
-
link: "text-primary",
|
|
152
|
-
ghost: "text-light-foreground",
|
|
153
|
-
};
|
|
154
|
-
return colors[variant];
|
|
155
|
-
}
|
|
156
|
-
getOutlineFilledClasses(variant) {
|
|
157
|
-
const colors = {
|
|
158
|
-
default: "hover:bg-primary hover:text-primary-foreground",
|
|
159
|
-
primary: "hover:bg-primary hover:text-primary-foreground",
|
|
160
|
-
secondary: "hover:bg-secondary hover:text-secondary-foreground",
|
|
161
|
-
success: "hover:bg-success hover:text-success-foreground",
|
|
162
|
-
warning: "hover:bg-warning hover:text-warning-foreground",
|
|
163
|
-
danger: "hover:bg-danger hover:text-danger-foreground",
|
|
164
|
-
info: "hover:bg-info hover:text-info-foreground",
|
|
165
|
-
light: "hover:bg-light hover:text-light-foreground",
|
|
166
|
-
dark: "hover:bg-dark hover:text-dark-foreground",
|
|
167
|
-
accent: "hover:bg-accent hover:text-accent-foreground",
|
|
168
|
-
destructive: "hover:bg-destructive hover:text-destructive-foreground",
|
|
169
|
-
muted: "hover:bg-muted hover:text-muted-foreground",
|
|
170
|
-
outline: "hover:bg-primary hover:text-primary-foreground",
|
|
171
|
-
link: "hover:underline",
|
|
172
|
-
ghost: "hover:bg-light",
|
|
173
|
-
};
|
|
174
|
-
return colors[variant];
|
|
175
|
-
}
|
|
176
|
-
getSpinnerClasses(variant) {
|
|
177
|
-
const colors = {
|
|
178
|
-
default: "[&>svg]:text-primary-foreground",
|
|
179
|
-
primary: "[&>svg]:text-primary-foreground",
|
|
180
|
-
secondary: "[&>svg]:text-secondary-foreground",
|
|
181
|
-
success: "[&>svg]:text-success-foreground",
|
|
182
|
-
warning: "[&>svg]:text-warning-foreground",
|
|
183
|
-
danger: "[&>svg]:text-danger-foreground",
|
|
184
|
-
info: "[&>svg]:text-info-foreground",
|
|
185
|
-
light: "[&>svg]:text-light-foreground",
|
|
186
|
-
dark: "[&>svg]:text-dark-foreground",
|
|
187
|
-
accent: "[&>svg]:text-accent-foreground",
|
|
188
|
-
destructive: "[&>svg]:text-destructive-foreground",
|
|
189
|
-
muted: "[&>svg]:text-muted-foreground",
|
|
190
|
-
outline: "[&>svg]:text-primary",
|
|
191
|
-
link: "[&>svg]:text-primary",
|
|
192
|
-
ghost: "[&>svg]:text-light-foreground",
|
|
193
|
-
};
|
|
194
|
-
return colors[variant];
|
|
195
|
-
}
|
|
196
|
-
getOutlineSpinnerClasses(variant) {
|
|
197
|
-
const colors = {
|
|
198
|
-
default: "[&>svg]:text-primary",
|
|
199
|
-
primary: "[&>svg]:text-primary",
|
|
200
|
-
secondary: "[&>svg]:text-secondary",
|
|
201
|
-
success: "[&>svg]:text-success",
|
|
202
|
-
warning: "[&>svg]:text-warning",
|
|
203
|
-
danger: "[&>svg]:text-danger",
|
|
204
|
-
info: "[&>svg]:text-info",
|
|
205
|
-
light: "[&>svg]:text-light",
|
|
206
|
-
dark: "[&>svg]:text-dark",
|
|
207
|
-
accent: "[&>svg]:text-accent",
|
|
208
|
-
destructive: "[&>svg]:text-destructive",
|
|
209
|
-
muted: "[&>svg]:text-muted-foreground",
|
|
210
|
-
outline: "[&>svg]:text-primary",
|
|
211
|
-
link: "[&>svg]:text-primary",
|
|
212
|
-
ghost: "[&>svg]:text-light-foreground",
|
|
213
|
-
};
|
|
214
|
-
return colors[variant];
|
|
215
|
-
}
|
|
216
194
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
217
195
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: HlmButton, isStandalone: true, selector: "button[hlmButton], a[hlmButton]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", 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 }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconPosition: { classPropertyName: "iconPosition", publicName: "iconPosition", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, spinnerUserClass: { classPropertyName: "spinnerUserClass", publicName: "spinnerUserClass", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "_computedClass()", "attr.aria-busy": "loading() || null", "attr.disabled": "(loading() || disabled()) ? true : null", "attr.type": "type()" } }, hostDirectives: [{ directive: i1.BrnButton, inputs: ["disabled", "disabled"] }], ngImport: i0, template: "\n <ng-template #projected>\n <ng-content></ng-content>\n </ng-template>\n\n @if (loading()) {\n <div class=\"relative inline-flex items-center\">\n <span class=\"invisible\">\n <ng-container *ngTemplateOutlet=\"projected\" />\n </span>\n <span class=\"absolute inset-0 flex items-center justify-center\">\n <hlm-spinner [spinnerClass]=\"spinnerClass()\" />\n </span>\n </div>\n } @else {\n <div class=\"flex items-center gap-1\">\n @if (icon() && iconPosition() === 'left') {\n <ng-container *ngTemplateOutlet=\"icon()\" />\n }\n <ng-container *ngTemplateOutlet=\"projected\" />\n @if (icon() && iconPosition() === 'right') {\n <ng-container *ngTemplateOutlet=\"icon()\" />\n }\n </div>\n }\n ", isInline: true, dependencies: [{ kind: "component", type: HlmSpinner, selector: "hlm-spinner", inputs: ["size", "show", "class", "spinnerClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
|
|
218
196
|
}
|
package/button/types/button.d.ts
CHANGED
|
@@ -18,10 +18,11 @@ declare class HlmBtn {
|
|
|
18
18
|
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<HlmBtn, "button[hlmBtn], a[hlmBtn]", ["hlmBtn"], { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof i1.BrnButton; inputs: { "disabled": "disabled"; }; outputs: {}; }]>;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/** Semantic color utilities and appearances. Merge the result with hlm() for custom hosts. */
|
|
21
22
|
declare const buttonVariants: (props?: ({
|
|
22
23
|
variant?: "default" | "primary" | "secondary" | "success" | "warning" | "danger" | "info" | "light" | "dark" | "accent" | "destructive" | "muted" | "outline" | "link" | "ghost" | null | undefined;
|
|
23
24
|
size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | "compact-xs" | "compact-sm" | "compact-default" | "compact-lg" | "compact-icon" | null | undefined;
|
|
24
|
-
appearance?: "outline" | "solid" | "outline-filled" | null | undefined;
|
|
25
|
+
appearance?: "outline" | "link" | "ghost" | "solid" | "outline-filled" | null | undefined;
|
|
25
26
|
loading?: boolean | null | undefined;
|
|
26
27
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
27
28
|
type ButtonVariants = VariantProps<typeof buttonVariants>;
|
|
@@ -29,9 +30,10 @@ type VariantType = NonNullable<ButtonVariants['variant']>;
|
|
|
29
30
|
type SizeType = NonNullable<ButtonVariants['size']>;
|
|
30
31
|
type AppearanceType = NonNullable<ButtonVariants['appearance']>;
|
|
31
32
|
declare class HlmButton {
|
|
32
|
-
/**
|
|
33
|
+
/** Semantic color token. Legacy default, outline, link, and ghost variants remain supported. */
|
|
33
34
|
readonly variant: _angular_core.InputSignal<VariantType>;
|
|
34
35
|
readonly size: _angular_core.InputSignal<SizeType>;
|
|
36
|
+
/** Visual treatment independent of color: solid, outline, outline-filled, ghost, or link. */
|
|
35
37
|
readonly appearance: _angular_core.InputSignal<AppearanceType>;
|
|
36
38
|
readonly loading: _angular_core.InputSignal<boolean>;
|
|
37
39
|
readonly icon: _angular_core.InputSignal<TemplateRef<unknown> | undefined>;
|
|
@@ -43,14 +45,9 @@ declare class HlmButton {
|
|
|
43
45
|
private readonly _additionalClasses;
|
|
44
46
|
/** Computed button class merging */
|
|
45
47
|
protected readonly _computedClass: _angular_core.Signal<string>;
|
|
46
|
-
/**
|
|
48
|
+
/** Inherit the button's resolved text color, including theme and consumer overrides. */
|
|
47
49
|
protected readonly spinnerClass: _angular_core.Signal<string>;
|
|
48
50
|
setClass(classes: string): void;
|
|
49
|
-
/** Helper functions */
|
|
50
|
-
private getOutlineClasses;
|
|
51
|
-
private getOutlineFilledClasses;
|
|
52
|
-
private getSpinnerClasses;
|
|
53
|
-
private getOutlineSpinnerClasses;
|
|
54
51
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HlmButton, never>;
|
|
55
52
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HlmButton, "button[hlmButton], a[hlmButton]", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "appearance": { "alias": "appearance"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "iconPosition": { "alias": "iconPosition"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "spinnerUserClass": { "alias": "spinnerUserClass"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, [{ directive: typeof i1.BrnButton; inputs: { "disabled": "disabled"; }; outputs: {}; }]>;
|
|
56
53
|
}
|
package/button-group/README.md
CHANGED
|
@@ -1,11 +1,324 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Button Group (`@egose/shadcn-theme-ng/button-group`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A joined, toolbar-style group layout that fuses buttons, inputs, selects, and static text into one visually continuous control — the Angular port of shadcn/ui `Button Group`. There is no interactive behavior of its own: `HlmButtonGroup` is a thin `cva`-styled layout directive (`orientation: horizontal | vertical`) that strips inner border-radii and collapses adjacent borders, while `HlmButtonGroupText` and `HlmButtonGroupSeparator` provide the static-text cell and the divider cell. The only headless primitive involved is `BrnSeparator` from `@spartan-ng/brain/separator` (used inside the separator directive).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Ships as `@egose/shadcn-theme-ng/button-group` and `@egose/shadcn-theme-ng-tw/button-group` (the `tw:`-prefixed variant). See the [package README](../../README.md) for install, peer dependencies, and Tailwind setup. Do not publish this project directory independently — it is consumed via the published subpath only.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
10
|
+
# Plain Tailwind (no prefix):
|
|
11
|
+
npm install @egose/shadcn-theme-ng
|
|
12
|
+
|
|
13
|
+
# Or the tw:-prefixed variant:
|
|
14
|
+
npm install @egose/shadcn-theme-ng-tw
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Peer dependencies (Angular, `@spartan-ng/brain`, etc.) are documented in the [package README](../../README.md). This subpath itself declares `@angular/common`, `@angular/core`, and `@spartan-ng/brain` as peers (see `projects/button-group/package.json`).
|
|
18
|
+
|
|
19
|
+
## Imports
|
|
20
|
+
|
|
21
|
+
Named imports from the subpath public API (`projects/button-group/src/public-api.ts`):
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import {
|
|
25
|
+
HlmButtonGroup,
|
|
26
|
+
HlmButtonGroupText,
|
|
27
|
+
HlmButtonGroupSeparator,
|
|
28
|
+
HlmButtonGroupImports,
|
|
29
|
+
HlmButtonGroupModule,
|
|
30
|
+
} from '@egose/shadcn-theme-ng/button-group';
|
|
31
|
+
// tw variant:
|
|
32
|
+
// import { ... } from '@egose/shadcn-theme-ng-tw/button-group';
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Standalone-component usage (preferred):
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { Component } from '@angular/core';
|
|
39
|
+
import { HlmButtonGroupImports } from '@egose/shadcn-theme-ng/button-group';
|
|
40
|
+
|
|
41
|
+
@Component({
|
|
42
|
+
selector: 'app-demo',
|
|
43
|
+
standalone: true,
|
|
44
|
+
imports: [...HlmButtonGroupImports],
|
|
45
|
+
template: `...`,
|
|
46
|
+
})
|
|
47
|
+
export class DemoComponent {}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
NgModule usage:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { NgModule } from '@angular/core';
|
|
54
|
+
import { HlmButtonGroupModule } from '@egose/shadcn-theme-ng/button-group';
|
|
55
|
+
|
|
56
|
+
@NgModule({ imports: [HlmButtonGroupModule] })
|
|
57
|
+
export class DemoModule {}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
You can also import the three directives individually (`HlmButtonGroup`, `HlmButtonGroupText`, `HlmButtonGroupSeparator`) instead of the whole `HlmButtonGroupImports` array.
|
|
61
|
+
|
|
62
|
+
## Anatomy / Structure
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<!-- Element OR attribute form for the group itself -->
|
|
66
|
+
<hlm-button-group orientation="horizontal">
|
|
67
|
+
<button hlmBtn variant="outline">Left</button>
|
|
68
|
+
<button hlmBtn variant="outline">Middle</button>
|
|
69
|
+
<button hlmBtn variant="outline">Right</button>
|
|
70
|
+
</hlm-button-group>
|
|
71
|
+
|
|
72
|
+
<!-- With static text + separator (attribute forms also work) -->
|
|
73
|
+
<div hlmButtonGroup orientation="horizontal">
|
|
74
|
+
<span hlmButtonGroupText>https://</span>
|
|
75
|
+
<input hlmInput placeholder="example.com" />
|
|
76
|
+
<hlm-button-group-separator></hlm-button-group-separator>
|
|
77
|
+
<button hlmBtn>Go</button>
|
|
78
|
+
</div>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Real selectors (from source):
|
|
82
|
+
|
|
83
|
+
| Class | Selectors |
|
|
84
|
+
| ------------------------- | --------------------------------------------------------- |
|
|
85
|
+
| `HlmButtonGroup` | `[hlmButtonGroup]`, `hlm-button-group` |
|
|
86
|
+
| `HlmButtonGroupText` | `[hlmButtonGroupText]`, `hlm-button-group-text` |
|
|
87
|
+
| `HlmButtonGroupSeparator` | `[hlmButtonGroupSeparator]`, `hlm-button-group-separator` |
|
|
88
|
+
|
|
89
|
+
All three carry a `data-slot` host attribute (`button-group`, `button-group-text`, `button-group-separator`). The group host also sets `role="group"` and `[attr.data-orientation]`.
|
|
90
|
+
|
|
91
|
+
## API reference
|
|
92
|
+
|
|
93
|
+
### `HlmButtonGroup` (`[hlmButtonGroup]`, `hlm-button-group`)
|
|
94
|
+
|
|
95
|
+
| Input | Type | Default | Description |
|
|
96
|
+
| ------------- | ---------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
97
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout axis. `horizontal` collapses left/right radii and left borders; `vertical` stacks, collapses top/bottom radii and top borders. Reflected as `data-orientation`. |
|
|
98
|
+
|
|
99
|
+
No outputs, no methods. Styling is computed by `buttonGroupVariants({ orientation })` (cva) plus helpers for nested `select-trigger`, `input`, and nested `button-group` spacing.
|
|
100
|
+
|
|
101
|
+
### `HlmButtonGroupText` (`[hlmButtonGroupText]`, `hlm-button-group-text`)
|
|
102
|
+
|
|
103
|
+
Thin directive wrapper — no inputs, outputs, or methods. Renders a muted, bordered, static-text cell (`bg-muted`, `rounded-lg`, `px-2.5`, icon-aware). Use for prefixes/suffixes such as `https://`, `.com`, currency symbols, or counts.
|
|
104
|
+
|
|
105
|
+
### `HlmButtonGroupSeparator` (`[hlmButtonGroupSeparator]`, `hlm-button-group-separator`)
|
|
106
|
+
|
|
107
|
+
| Brain input (via `BrnSeparator` hostDirective) | Type | Default | Description |
|
|
108
|
+
| ---------------------------------------------- | ---------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
109
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'vertical'` (via `provideBrnSeparatorConfig`) | Separator axis. The provider defaults it to `vertical` so it divides horizontal groups out of the box; override for vertical groups. |
|
|
110
|
+
| `decorative` | `boolean` | — | Passed straight to `BrnSeparator` (aria-hidden handling). |
|
|
111
|
+
|
|
112
|
+
No outputs of its own. Host classes make it `self-stretch`, `bg-input`, 1px thick on the active axis.
|
|
113
|
+
|
|
114
|
+
## Examples
|
|
115
|
+
|
|
116
|
+
### 1. Basic horizontal button cluster
|
|
117
|
+
|
|
118
|
+
Three outline buttons fused into one segmented control.
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { Component } from '@angular/core';
|
|
122
|
+
import { HlmButtonGroupImports } from '@egose/shadcn-theme-ng/button-group';
|
|
123
|
+
import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
|
|
124
|
+
|
|
125
|
+
@Component({
|
|
126
|
+
selector: 'app-basic-group',
|
|
127
|
+
standalone: true,
|
|
128
|
+
imports: [...HlmButtonGroupImports, ...HlmButtonImports],
|
|
129
|
+
template: `
|
|
130
|
+
<hlm-button-group>
|
|
131
|
+
<button hlmBtn variant="outline">Left</button>
|
|
132
|
+
<button hlmBtn variant="outline">Center</button>
|
|
133
|
+
<button hlmBtn variant="outline">Right</button>
|
|
134
|
+
</hlm-button-group>
|
|
135
|
+
`,
|
|
136
|
+
})
|
|
137
|
+
export class BasicGroupComponent {}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 2. All orientations
|
|
141
|
+
|
|
142
|
+
`horizontal` (default) vs `vertical`. Vertical stacks children and collapses vertical borders instead.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { Component } from '@angular/core';
|
|
146
|
+
import { HlmButtonGroupImports } from '@egose/shadcn-theme-ng/button-group';
|
|
147
|
+
import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
|
|
148
|
+
|
|
149
|
+
@Component({
|
|
150
|
+
selector: 'app-orientations',
|
|
151
|
+
standalone: true,
|
|
152
|
+
imports: [...HlmButtonGroupImports, ...HlmButtonImports],
|
|
153
|
+
template: `
|
|
154
|
+
<div class="flex gap-8">
|
|
155
|
+
<hlm-button-group orientation="horizontal">
|
|
156
|
+
<button hlmBtn variant="outline">A</button>
|
|
157
|
+
<button hlmBtn variant="outline">B</button>
|
|
158
|
+
<button hlmBtn variant="outline">C</button>
|
|
159
|
+
</hlm-button-group>
|
|
160
|
+
|
|
161
|
+
<hlm-button-group orientation="vertical">
|
|
162
|
+
<button hlmBtn variant="outline">Top</button>
|
|
163
|
+
<button hlmBtn variant="outline">Middle</button>
|
|
164
|
+
<button hlmBtn variant="outline">Bottom</button>
|
|
165
|
+
</hlm-button-group>
|
|
166
|
+
</div>
|
|
167
|
+
`,
|
|
168
|
+
})
|
|
169
|
+
export class OrientationsComponent {}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 3. Input group with static prefix/suffix text
|
|
173
|
+
|
|
174
|
+
Classic URL / price field: muted text cells fused with a native-styled input and an action button.
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import { Component } from '@angular/core';
|
|
178
|
+
import { HlmButtonGroupImports } from '@egose/shadcn-theme-ng/button-group';
|
|
179
|
+
import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
|
|
180
|
+
import { HlmInput } from '@egose/shadcn-theme-ng/input';
|
|
181
|
+
|
|
182
|
+
@Component({
|
|
183
|
+
selector: 'app-addon-group',
|
|
184
|
+
standalone: true,
|
|
185
|
+
imports: [...HlmButtonGroupImports, ...HlmButtonImports, HlmInput],
|
|
186
|
+
template: `
|
|
187
|
+
<div hlmButtonGroup>
|
|
188
|
+
<span hlmButtonGroupText>https://</span>
|
|
189
|
+
<input hlmInput placeholder="example.com" class="w-48" />
|
|
190
|
+
<span hlmButtonGroupText>.com</span>
|
|
191
|
+
<button hlmBtn>Visit</button>
|
|
192
|
+
</div>
|
|
193
|
+
`,
|
|
194
|
+
})
|
|
195
|
+
export class AddonGroupComponent {}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 4. Separated actions (divider cells)
|
|
199
|
+
|
|
200
|
+
Use `hlm-button-group-separator` between clusters instead of spacing utilities so the divider stretches to the group height.
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
import { Component } from '@angular/core';
|
|
204
|
+
import { HlmButtonGroupImports } from '@egose/shadcn-theme-ng/button-group';
|
|
205
|
+
import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
|
|
206
|
+
|
|
207
|
+
@Component({
|
|
208
|
+
selector: 'app-separated-group',
|
|
209
|
+
standalone: true,
|
|
210
|
+
imports: [...HlmButtonGroupImports, ...HlmButtonImports],
|
|
211
|
+
template: `
|
|
212
|
+
<hlm-button-group>
|
|
213
|
+
<button hlmBtn variant="outline">Cut</button>
|
|
214
|
+
<button hlmBtn variant="outline">Copy</button>
|
|
215
|
+
<button hlmBtn variant="outline">Paste</button>
|
|
216
|
+
<hlm-button-group-separator></hlm-button-group-separator>
|
|
217
|
+
<button hlmBtn variant="outline">Undo</button>
|
|
218
|
+
<button hlmBtn variant="outline">Redo</button>
|
|
219
|
+
</hlm-button-group>
|
|
220
|
+
`,
|
|
221
|
+
})
|
|
222
|
+
export class SeparatedGroupComponent {}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Explanation: the separator defaults to `orientation="vertical"` via its provider. Inside a `orientation="vertical"` group, bind it explicitly:
|
|
226
|
+
|
|
227
|
+
```html
|
|
228
|
+
<hlm-button-group orientation="vertical">
|
|
229
|
+
<button hlmBtn variant="outline">One</button>
|
|
230
|
+
<hlm-button-group-separator orientation="horizontal"></hlm-button-group-separator>
|
|
231
|
+
<button hlmBtn variant="outline">Two</button>
|
|
232
|
+
</hlm-button-group>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### 5. Mixed controls: select + input + button
|
|
236
|
+
|
|
237
|
+
The group stylesheet special-cases `select-trigger` and `input` children (`w-fit` triggers, `flex-1` inputs), so mixed toolbars fuse cleanly.
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { Component, signal } from '@angular/core';
|
|
241
|
+
import { HlmButtonGroupImports } from '@egose/shadcn-theme-ng/button-group';
|
|
242
|
+
import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
|
|
243
|
+
import { HlmInput } from '@egose/shadcn-theme-ng/input';
|
|
244
|
+
import { HlmSelectImports } from '@egose/shadcn-theme-ng/select';
|
|
245
|
+
|
|
246
|
+
@Component({
|
|
247
|
+
selector: 'app-mixed-group',
|
|
248
|
+
standalone: true,
|
|
249
|
+
imports: [...HlmButtonGroupImports, ...HlmButtonImports, HlmInput, ...HlmSelectImports],
|
|
250
|
+
template: `
|
|
251
|
+
<div hlmButtonGroup>
|
|
252
|
+
<hlm-select [value]="unit()">
|
|
253
|
+
<hlm-select-trigger class="w-28">
|
|
254
|
+
<hlm-select-value />
|
|
255
|
+
</hlm-select-trigger>
|
|
256
|
+
<hlm-select-content *hlmSelectPortal>
|
|
257
|
+
<hlm-select-item value="px">px</hlm-select-item>
|
|
258
|
+
<hlm-select-item value="rem">rem</hlm-select-item>
|
|
259
|
+
<hlm-select-item value="em">em</hlm-select-item>
|
|
260
|
+
</hlm-select-content>
|
|
261
|
+
</hlm-select>
|
|
262
|
+
<input hlmInput type="number" value="16" class="w-24" />
|
|
263
|
+
<button hlmBtn (click)="apply()">Apply</button>
|
|
264
|
+
</div>
|
|
265
|
+
`,
|
|
266
|
+
})
|
|
267
|
+
export class MixedGroupComponent {
|
|
268
|
+
readonly unit = signal('px');
|
|
269
|
+
apply() {
|
|
270
|
+
console.log('apply', this.unit());
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### 6. Icon toolbar with labels for assistive tech
|
|
276
|
+
|
|
277
|
+
Buttons keep their own `aria-label`s; the group only adds `role="group"` — give it an `aria-label` for context.
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
import { Component } from '@angular/core';
|
|
281
|
+
import { HlmButtonGroupImports } from '@egose/shadcn-theme-ng/button-group';
|
|
282
|
+
import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
|
|
283
|
+
import { NgIcon, provideIcons } from '@ng-icons/core';
|
|
284
|
+
import { lucideBold, lucideItalic, lucideUnderline } from '@ng-icons/lucide';
|
|
285
|
+
|
|
286
|
+
@Component({
|
|
287
|
+
selector: 'app-toolbar-group',
|
|
288
|
+
standalone: true,
|
|
289
|
+
imports: [...HlmButtonGroupImports, ...HlmButtonImports, NgIcon],
|
|
290
|
+
providers: [provideIcons({ lucideBold, lucideItalic, lucideUnderline })],
|
|
291
|
+
template: `
|
|
292
|
+
<hlm-button-group aria-label="Text formatting">
|
|
293
|
+
<button hlmBtn variant="outline" size="icon" aria-label="Bold">
|
|
294
|
+
<ng-icon name="lucideBold" />
|
|
295
|
+
</button>
|
|
296
|
+
<button hlmBtn variant="outline" size="icon" aria-label="Italic">
|
|
297
|
+
<ng-icon name="lucideItalic" />
|
|
298
|
+
</button>
|
|
299
|
+
<button hlmBtn variant="outline" size="icon" aria-label="Underline">
|
|
300
|
+
<ng-icon name="lucideUnderline" />
|
|
301
|
+
</button>
|
|
302
|
+
</hlm-button-group>
|
|
303
|
+
`,
|
|
304
|
+
})
|
|
305
|
+
export class ToolbarGroupComponent {}
|
|
11
306
|
```
|
|
307
|
+
|
|
308
|
+
## Accessibility notes
|
|
309
|
+
|
|
310
|
+
- The group host renders `role="group"` — always pair it with `aria-label` or `aria-labelledby` when the purpose is not obvious from the buttons alone.
|
|
311
|
+
- `HlmButtonGroupSeparator` delegates to `BrnSeparator`: mark purely visual dividers `decorative`, otherwise keep them exposed so screen readers announce the grouping break.
|
|
312
|
+
- `HlmButtonGroupText` is a plain `<span>`-style cell with no semantics; never put interactive content inside it.
|
|
313
|
+
- Keyboard behavior is unchanged — Tab moves through each fused button/input normally; roving tabindex is not applied.
|
|
314
|
+
|
|
315
|
+
## Theming / CSS variables
|
|
316
|
+
|
|
317
|
+
No component-specific CSS variables. Styling is Tailwind-driven (`bg-muted` text cells, `bg-input` separators, ring/border tokens). Dark mode follows the shared `tw:dark:` theme tokens. Override via `class` on any cell — the `classes()` helper merges your classes with the defaults.
|
|
318
|
+
|
|
319
|
+
## Related subpaths
|
|
320
|
+
|
|
321
|
+
- `@egose/shadcn-theme-ng/button` — `hlmBtn` / `HlmBtn` used for every fused button.
|
|
322
|
+
- `@egose/shadcn-theme-ng/input` — `hlmInput` fused text/number fields.
|
|
323
|
+
- `@egose/shadcn-theme-ng/select` — triggers auto-size (`w-fit`) inside groups.
|
|
324
|
+
- `@egose/shadcn-theme-ng/separator` — standalone separator primitive (the group ships its own opinionated divider).
|