@egose/shadcn-theme-ng-tw 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/fesm2022/alert.mjs +1 -1
- 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/button-group/fesm2022/button-group.mjs +1 -1
- 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 +211 -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 +273 -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 +60 -16
- 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 +239 -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 +236 -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 +194 -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 +229 -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 +187 -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 +203 -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 +211 -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 +24 -15
- package/form-select/types/form-select.d.ts +18 -1
- package/form-slider/README.md +182 -0
- package/form-slider/fesm2022/form-slider.mjs +186 -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 +178 -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 +241 -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 +216 -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 +23 -13
- 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 +877 -409
- 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/sheet/fesm2022/sheet.mjs +1 -1
- package/sidebar/README.md +457 -5
- package/skeleton/README.md +217 -5
- package/slider/README.md +273 -5
- package/slider/fesm2022/slider.mjs +17 -13
- 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 +12 -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-tw/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: 'tw:text-primary',
|
|
13
|
+
primary: 'tw:text-primary',
|
|
14
|
+
secondary: 'tw:text-secondary',
|
|
15
|
+
success: 'tw:text-success',
|
|
16
|
+
warning: 'tw:text-warning',
|
|
17
|
+
danger: 'tw:text-danger',
|
|
18
|
+
info: 'tw:text-info',
|
|
19
|
+
light: 'tw:text-light-foreground',
|
|
20
|
+
dark: 'tw:text-dark',
|
|
21
|
+
accent: 'tw:text-accent',
|
|
22
|
+
destructive: 'tw:text-destructive',
|
|
23
|
+
muted: 'tw:text-muted-foreground',
|
|
24
|
+
outline: 'tw:text-primary',
|
|
25
|
+
link: 'tw:text-primary',
|
|
26
|
+
ghost: 'tw:text-light-foreground',
|
|
27
|
+
};
|
|
28
|
+
const outlineClasses = {
|
|
29
|
+
default: 'tw:border-primary tw:hover:bg-primary/10',
|
|
30
|
+
primary: 'tw:border-primary tw:hover:bg-primary/10',
|
|
31
|
+
secondary: 'tw:border-secondary tw:hover:bg-secondary/10',
|
|
32
|
+
success: 'tw:border-success tw:hover:bg-success/10',
|
|
33
|
+
warning: 'tw:border-warning tw:hover:bg-warning/10',
|
|
34
|
+
danger: 'tw:border-danger tw:hover:bg-danger/10',
|
|
35
|
+
info: 'tw:border-info tw:hover:bg-info/10',
|
|
36
|
+
light: 'tw:border-light tw:hover:bg-light/10',
|
|
37
|
+
dark: 'tw:border-dark tw:hover:bg-dark/10',
|
|
38
|
+
accent: 'tw:border-accent tw:hover:bg-accent/10',
|
|
39
|
+
destructive: 'tw:border-destructive tw:hover:bg-destructive/10',
|
|
40
|
+
muted: 'tw:border-muted tw:hover:bg-muted/10',
|
|
41
|
+
outline: 'tw:border-primary tw:hover:bg-primary/10',
|
|
42
|
+
link: '',
|
|
43
|
+
ghost: '',
|
|
44
|
+
};
|
|
45
|
+
const outlineFilledClasses = {
|
|
46
|
+
default: 'tw:hover:bg-primary tw:hover:text-primary-foreground',
|
|
47
|
+
primary: 'tw:hover:bg-primary tw:hover:text-primary-foreground',
|
|
48
|
+
secondary: 'tw:hover:bg-secondary tw:hover:text-secondary-foreground',
|
|
49
|
+
success: 'tw:hover:bg-success tw:hover:text-success-foreground',
|
|
50
|
+
warning: 'tw:hover:bg-warning tw:hover:text-warning-foreground',
|
|
51
|
+
danger: 'tw:hover:bg-danger tw:hover:text-danger-foreground',
|
|
52
|
+
info: 'tw:hover:bg-info tw:hover:text-info-foreground',
|
|
53
|
+
light: 'tw:hover:bg-light tw:hover:text-light-foreground',
|
|
54
|
+
dark: 'tw:hover:bg-dark tw:hover:text-dark-foreground',
|
|
55
|
+
accent: 'tw:hover:bg-accent tw:hover:text-accent-foreground',
|
|
56
|
+
destructive: 'tw:hover:bg-destructive tw:hover:text-destructive-foreground',
|
|
57
|
+
muted: 'tw:hover:bg-muted tw:hover:text-muted-foreground',
|
|
58
|
+
outline: 'tw:hover:bg-primary tw:hover:text-primary-foreground',
|
|
59
|
+
link: 'tw:hover:underline',
|
|
60
|
+
ghost: 'tw: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
|
'tw:cursor-pointer',
|
|
12
65
|
'tw:active:translate-y-[0.5px]',
|
|
@@ -45,7 +98,7 @@ const buttonVariants = cva([
|
|
|
45
98
|
accent: 'tw:border tw:border-accent tw:bg-accent tw:text-accent-foreground tw:shadow-sm tw:hover:bg-accent/90',
|
|
46
99
|
destructive: 'tw:border tw:border-destructive tw:bg-destructive tw:text-destructive-foreground tw:shadow-sm tw:hover:bg-destructive/90',
|
|
47
100
|
muted: 'tw:border tw:border-muted tw:bg-muted tw:text-muted-foreground tw:shadow-sm tw:hover:bg-muted/90',
|
|
48
|
-
outline: 'tw:border tw:border-primary tw:bg-
|
|
101
|
+
outline: 'tw:border tw:border-primary tw:bg-background tw:text-primary tw:shadow-sm tw:hover:bg-primary/10',
|
|
49
102
|
link: 'tw:text-primary tw:underline-offset-4 tw:hover:underline',
|
|
50
103
|
ghost: 'tw:hover:bg-light tw:hover:text-light-foreground',
|
|
51
104
|
},
|
|
@@ -66,14 +119,33 @@ const buttonVariants = cva([
|
|
|
66
119
|
},
|
|
67
120
|
appearance: {
|
|
68
121
|
solid: '',
|
|
69
|
-
outline: 'tw:bg-
|
|
70
|
-
'outline-filled': 'tw:bg-
|
|
122
|
+
outline: 'tw:bg-background tw:border tw:shadow-sm',
|
|
123
|
+
'outline-filled': 'tw:bg-background tw:border tw:shadow-sm',
|
|
124
|
+
ghost: 'tw:bg-transparent tw:border-0 tw:shadow-none',
|
|
125
|
+
link: 'tw:bg-transparent tw:border-0 tw:shadow-none tw:hover:bg-transparent tw:underline-offset-4 tw:hover:underline',
|
|
71
126
|
},
|
|
72
127
|
loading: {
|
|
73
128
|
true: 'tw: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() ? 'tw: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() ? 'tw: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('tw:[&>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: 'tw:border-primary tw:text-primary tw:shadow-sm tw:hover:bg-primary/10',
|
|
139
|
-
primary: 'tw:border-primary tw:text-primary tw:shadow-sm tw:hover:bg-primary/10',
|
|
140
|
-
secondary: 'tw:border-secondary tw:text-secondary tw:shadow-sm tw:hover:bg-secondary/10',
|
|
141
|
-
success: 'tw:border-success tw:text-success tw:shadow-sm tw:hover:bg-success/10',
|
|
142
|
-
warning: 'tw:border-warning tw:text-warning tw:shadow-sm tw:hover:bg-warning/10',
|
|
143
|
-
danger: 'tw:border-danger tw:text-danger tw:shadow-sm tw:hover:bg-danger/10',
|
|
144
|
-
info: 'tw:border-info tw:text-info tw:shadow-sm tw:hover:bg-info/10',
|
|
145
|
-
light: 'tw:border-light tw:text-light-foreground tw:shadow-sm tw:hover:bg-light/10',
|
|
146
|
-
dark: 'tw:border-dark tw:text-dark tw:shadow-sm tw:hover:bg-dark/10',
|
|
147
|
-
accent: 'tw:border-accent tw:text-accent tw:shadow-sm tw:hover:bg-accent/10',
|
|
148
|
-
destructive: 'tw:border-destructive tw:text-destructive tw:shadow-sm tw:hover:bg-destructive/10',
|
|
149
|
-
muted: 'tw:border-muted tw:text-muted-foreground tw:shadow-sm tw:hover:bg-muted/10',
|
|
150
|
-
outline: 'tw:border-primary tw:text-primary tw:shadow-sm tw:hover:bg-primary/10',
|
|
151
|
-
link: 'tw:text-primary',
|
|
152
|
-
ghost: 'tw:text-light-foreground',
|
|
153
|
-
};
|
|
154
|
-
return colors[variant];
|
|
155
|
-
}
|
|
156
|
-
getOutlineFilledClasses(variant) {
|
|
157
|
-
const colors = {
|
|
158
|
-
default: 'tw:hover:bg-primary tw:hover:text-primary-foreground',
|
|
159
|
-
primary: 'tw:hover:bg-primary tw:hover:text-primary-foreground',
|
|
160
|
-
secondary: 'tw:hover:bg-secondary tw:hover:text-secondary-foreground',
|
|
161
|
-
success: 'tw:hover:bg-success tw:hover:text-success-foreground',
|
|
162
|
-
warning: 'tw:hover:bg-warning tw:hover:text-warning-foreground',
|
|
163
|
-
danger: 'tw:hover:bg-danger tw:hover:text-danger-foreground',
|
|
164
|
-
info: 'tw:hover:bg-info tw:hover:text-info-foreground',
|
|
165
|
-
light: 'tw:hover:bg-light tw:hover:text-light-foreground',
|
|
166
|
-
dark: 'tw:hover:bg-dark tw:hover:text-dark-foreground',
|
|
167
|
-
accent: 'tw:hover:bg-accent tw:hover:text-accent-foreground',
|
|
168
|
-
destructive: 'tw:hover:bg-destructive tw:hover:text-destructive-foreground',
|
|
169
|
-
muted: 'tw:hover:bg-muted tw:hover:text-muted-foreground',
|
|
170
|
-
outline: 'tw:hover:bg-primary tw:hover:text-primary-foreground',
|
|
171
|
-
link: 'tw:hover:underline',
|
|
172
|
-
ghost: 'tw:hover:bg-light',
|
|
173
|
-
};
|
|
174
|
-
return colors[variant];
|
|
175
|
-
}
|
|
176
|
-
getSpinnerClasses(variant) {
|
|
177
|
-
const colors = {
|
|
178
|
-
default: 'tw:[&>svg]:text-primary-foreground',
|
|
179
|
-
primary: 'tw:[&>svg]:text-primary-foreground',
|
|
180
|
-
secondary: 'tw:[&>svg]:text-secondary-foreground',
|
|
181
|
-
success: 'tw:[&>svg]:text-success-foreground',
|
|
182
|
-
warning: 'tw:[&>svg]:text-warning-foreground',
|
|
183
|
-
danger: 'tw:[&>svg]:text-danger-foreground',
|
|
184
|
-
info: 'tw:[&>svg]:text-info-foreground',
|
|
185
|
-
light: 'tw:[&>svg]:text-light-foreground',
|
|
186
|
-
dark: 'tw:[&>svg]:text-dark-foreground',
|
|
187
|
-
accent: 'tw:[&>svg]:text-accent-foreground',
|
|
188
|
-
destructive: 'tw:[&>svg]:text-destructive-foreground',
|
|
189
|
-
muted: 'tw:[&>svg]:text-muted-foreground',
|
|
190
|
-
outline: 'tw:[&>svg]:text-primary',
|
|
191
|
-
link: 'tw:[&>svg]:text-primary',
|
|
192
|
-
ghost: 'tw:[&>svg]:text-light-foreground',
|
|
193
|
-
};
|
|
194
|
-
return colors[variant];
|
|
195
|
-
}
|
|
196
|
-
getOutlineSpinnerClasses(variant) {
|
|
197
|
-
const colors = {
|
|
198
|
-
default: 'tw:[&>svg]:text-primary',
|
|
199
|
-
primary: 'tw:[&>svg]:text-primary',
|
|
200
|
-
secondary: 'tw:[&>svg]:text-secondary',
|
|
201
|
-
success: 'tw:[&>svg]:text-success',
|
|
202
|
-
warning: 'tw:[&>svg]:text-warning',
|
|
203
|
-
danger: 'tw:[&>svg]:text-danger',
|
|
204
|
-
info: 'tw:[&>svg]:text-info',
|
|
205
|
-
light: 'tw:[&>svg]:text-light',
|
|
206
|
-
dark: 'tw:[&>svg]:text-dark',
|
|
207
|
-
accent: 'tw:[&>svg]:text-accent',
|
|
208
|
-
destructive: 'tw:[&>svg]:text-destructive',
|
|
209
|
-
muted: 'tw:[&>svg]:text-muted-foreground',
|
|
210
|
-
outline: 'tw:[&>svg]:text-primary',
|
|
211
|
-
link: 'tw:[&>svg]:text-primary',
|
|
212
|
-
ghost: 'tw:[&>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: `
|
|
218
196
|
<ng-template #projected>
|
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).
|
|
@@ -5,7 +5,7 @@ import { cva } from 'class-variance-authority';
|
|
|
5
5
|
import * as i1 from '@spartan-ng/brain/separator';
|
|
6
6
|
import { provideBrnSeparatorConfig, BrnSeparator } from '@spartan-ng/brain/separator';
|
|
7
7
|
|
|
8
|
-
const buttonGroupVariants = cva(
|
|
8
|
+
const buttonGroupVariants = cva("tw:has-[>[data-slot=button-group]]:gap-2 tw:has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-lg tw:flex tw:w-fit tw:items-stretch tw:*:focus-visible:relative tw:*:focus-visible:z-10 tw:[&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit tw:[&>input]:flex-1", {
|
|
9
9
|
variants: {
|
|
10
10
|
orientation: {
|
|
11
11
|
horizontal: 'tw:[&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-lg! tw:[&>*:not(:first-child)]:rounded-l-none tw:[&>*:not(:first-child)]:border-l-0 tw:[&>*:not(:last-child)]:rounded-r-none',
|