@egose/shadcn-theme-ng-tw 0.1.0 → 0.2.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 +1 -1
- 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/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/collapsible/README.md +332 -5
- package/combobox/README.md +507 -5
- package/combobox/fesm2022/combobox.mjs +4 -1
- 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 +465 -2
- package/date-picker/fesm2022/date-picker.mjs +2 -2
- 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-checkbox/README.md +312 -2
- package/form-date-picker/README.md +322 -2
- package/form-field/README.md +356 -2
- package/form-field-simple/README.md +340 -2
- package/form-searchable-multiselect/README.md +361 -2
- package/form-select/README.md +350 -2
- package/form-text-input/README.md +371 -2
- package/form-textarea/README.md +347 -2
- 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 +3 -3
- 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/navigation-menu/README.md +369 -5
- package/package.json +1 -1
- package/pagination/README.md +388 -5
- 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 +17 -13
- package/sonner/README.md +346 -2
- package/spinner/README.md +284 -2
- package/switch/README.md +310 -2
- 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
package/textarea/README.md
CHANGED
|
@@ -1,11 +1,288 @@
|
|
|
1
|
-
# Textarea
|
|
1
|
+
# Textarea (`@egose/shadcn-theme-ng/textarea`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A multi-line text input, equivalent to [shadcn/ui Textarea](https://ui.shadcn.com/docs/components/textarea). This subpath ships a single thin styling directive, `HlmTextarea` (`[hlmTextarea]`), over spartan-ng's `BrnTextarea` primitive (plus `BrnFieldControlDescribedBy` for form-field hint/error wiring). All behavior — value binding, validation, auto-sizing — comes from the Brn primitive and Angular forms; this package only applies shadcn styling (border, focus ring, invalid state, placeholder color).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> **Ships as:** `@egose/shadcn-theme-ng/textarea` and `@egose/shadcn-theme-ng-tw/textarea` (the `tw:`-prefixed Tailwind variant). See the [package README](../../README.md) for install steps, peer dependencies, Tailwind setup, and testing/release guidance. Do not publish this project directory independently.
|
|
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
|
+
`@spartan-ng/brain` arrives transitively; `FormsModule`/`ReactiveFormsModule` come from your app when you need form bindings. See the [package README](../../README.md) for the full peer-dependency table.
|
|
18
|
+
|
|
19
|
+
## Imports
|
|
20
|
+
|
|
21
|
+
All public symbols are re-exported from `projects/textarea/src/public-api.ts`:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { HlmTextarea, HlmTextareaImports, HlmTextareaModule } from '@egose/shadcn-theme-ng/textarea';
|
|
25
|
+
// tw variant:
|
|
26
|
+
// import { HlmTextarea, HlmTextareaImports, HlmTextareaModule } from '@egose/shadcn-theme-ng-tw/textarea';
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Standalone-component usage (preferred):
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { Component } from '@angular/core';
|
|
33
|
+
import { HlmTextareaImports } from '@egose/shadcn-theme-ng/textarea';
|
|
34
|
+
|
|
35
|
+
@Component({
|
|
36
|
+
selector: 'app-demo',
|
|
37
|
+
standalone: true,
|
|
38
|
+
imports: [...HlmTextareaImports],
|
|
39
|
+
template: `<textarea hlmTextarea placeholder="Type here…"></textarea>`,
|
|
40
|
+
})
|
|
41
|
+
export class DemoComponent {}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
NgModule usage:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { NgModule } from '@angular/core';
|
|
48
|
+
import { HlmTextareaModule } from '@egose/shadcn-theme-ng/textarea';
|
|
49
|
+
|
|
50
|
+
@NgModule({ imports: [HlmTextareaModule] })
|
|
51
|
+
export class FeatureModule {}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
| Symbol | Kind | Description |
|
|
55
|
+
| -------------------- | ------------- | ---------------------------------------------------- |
|
|
56
|
+
| `HlmTextarea` | Directive | `[hlmTextarea]` — shadcn styling over `BrnTextarea`. |
|
|
57
|
+
| `HlmTextareaImports` | `const` array | `[HlmTextarea]` — spread into `imports: [...]`. |
|
|
58
|
+
| `HlmTextareaModule` | NgModule | Imports + re-exports `HlmTextarea`. |
|
|
59
|
+
|
|
60
|
+
## Anatomy / Structure
|
|
61
|
+
|
|
62
|
+
```html
|
|
63
|
+
<!-- basic -->
|
|
64
|
+
<textarea hlmTextarea placeholder="Tell us about yourself…"></textarea>
|
|
65
|
+
|
|
66
|
+
<!-- with label + hint -->
|
|
67
|
+
<div class="space-y-2">
|
|
68
|
+
<label hlmLabel for="bio">Bio</label>
|
|
69
|
+
<textarea hlmTextarea id="bio" rows="4" placeholder="Short bio…"></textarea>
|
|
70
|
+
<p class="text-sm text-muted-foreground">Up to 500 characters.</p>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<!-- reactive form -->
|
|
74
|
+
<textarea hlmTextarea formControlName="notes" rows="5"></textarea>
|
|
75
|
+
|
|
76
|
+
<!-- forced invalid state -->
|
|
77
|
+
<textarea hlmTextarea [forceInvalid]="true"></textarea>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## API reference
|
|
81
|
+
|
|
82
|
+
### `HlmTextarea` — selector `[hlmTextarea]` (directive, hosts `BrnTextarea` + `BrnFieldControlDescribedBy`)
|
|
83
|
+
|
|
84
|
+
The directive declares **no own inputs/outputs**. All behavior comes from host directives:
|
|
85
|
+
|
|
86
|
+
| Input | Source | Description |
|
|
87
|
+
| -------------- | ---------------------------- | ---------------------------------------------------------------------------------------- |
|
|
88
|
+
| `id` | `BrnTextarea` | Element id (pairs with `<label for>` and form-field wiring). |
|
|
89
|
+
| `forceInvalid` | `BrnTextarea` | Force the `data-[matches-spartan-invalid=true]` invalid styles even without form errors. |
|
|
90
|
+
| (described-by) | `BrnFieldControlDescribedBy` | Auto-wires `aria-describedby` to enclosing form-field hint/error. |
|
|
91
|
+
|
|
92
|
+
Standard native attributes (`rows`, `placeholder`, `disabled`, `readonly`, `maxlength`, `name`, …) and Angular form bindings (`formControl`, `formControlName`, `ngModel`) work as usual through the underlying `<textarea>`.
|
|
93
|
+
|
|
94
|
+
Host: `data-slot="textarea"`. Invalid styling keys off `data-[matches-spartan-invalid=true]` (`border-destructive`, `ring-destructive`). Disabled state renders `cursor-not-allowed` + `opacity-50`. User `class` is preserved and merged via `classes()`.
|
|
95
|
+
|
|
96
|
+
## Examples
|
|
97
|
+
|
|
98
|
+
### 1. Basic usage with label
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
// demo-basic.component.ts
|
|
102
|
+
import { Component } from '@angular/core';
|
|
103
|
+
import { HlmTextareaImports } from '@egose/shadcn-theme-ng/textarea';
|
|
104
|
+
import { HlmLabel } from '@egose/shadcn-theme-ng/label';
|
|
105
|
+
|
|
106
|
+
@Component({
|
|
107
|
+
selector: 'demo-basic',
|
|
108
|
+
standalone: true,
|
|
109
|
+
imports: [...HlmTextareaImports, HlmLabel],
|
|
110
|
+
template: `
|
|
111
|
+
<div class="space-y-2">
|
|
112
|
+
<label hlmLabel for="bio">Bio</label>
|
|
113
|
+
<textarea hlmTextarea id="bio" rows="4" placeholder="Tell us about yourself…"></textarea>
|
|
114
|
+
</div>
|
|
115
|
+
`,
|
|
116
|
+
})
|
|
117
|
+
export class DemoBasic {}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 2. Reactive form with validation + character count
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
// demo-reactive.component.ts
|
|
124
|
+
import { Component } from '@angular/core';
|
|
125
|
+
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
126
|
+
import { HlmTextareaImports } from '@egose/shadcn-theme-ng/textarea';
|
|
127
|
+
import { HlmLabel } from '@egose/shadcn-theme-ng/label';
|
|
128
|
+
|
|
129
|
+
@Component({
|
|
130
|
+
selector: 'demo-reactive',
|
|
131
|
+
standalone: true,
|
|
132
|
+
imports: [...HlmTextareaImports, ReactiveFormsModule, HlmLabel],
|
|
133
|
+
template: `
|
|
134
|
+
<form [formGroup]="form" class="space-y-2">
|
|
135
|
+
<label hlmLabel for="feedback">Feedback</label>
|
|
136
|
+
<textarea
|
|
137
|
+
hlmTextarea
|
|
138
|
+
id="feedback"
|
|
139
|
+
rows="5"
|
|
140
|
+
maxlength="500"
|
|
141
|
+
placeholder="What did you think?"
|
|
142
|
+
formControlName="feedback"
|
|
143
|
+
></textarea>
|
|
144
|
+
<div class="flex justify-between text-sm">
|
|
145
|
+
<span class="text-destructive">
|
|
146
|
+
@if (form.controls.feedback.hasError('required') && form.controls.feedback.touched) {
|
|
147
|
+
Feedback is required.
|
|
148
|
+
} @else if (form.controls.feedback.hasError('minlength')) {
|
|
149
|
+
At least 10 characters.
|
|
150
|
+
}
|
|
151
|
+
</span>
|
|
152
|
+
<span class="text-muted-foreground">{{ form.controls.feedback.value?.length ?? 0 }}/500</span>
|
|
153
|
+
</div>
|
|
154
|
+
</form>
|
|
155
|
+
`,
|
|
156
|
+
})
|
|
157
|
+
export class DemoReactive {
|
|
158
|
+
readonly form = new FormGroup({
|
|
159
|
+
feedback: new FormControl('', [Validators.required, Validators.minLength(10)]),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 3. Template-driven form (`ngModel`)
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
// demo-template.component.ts
|
|
168
|
+
import { Component } from '@angular/core';
|
|
169
|
+
import { FormsModule } from '@angular/forms';
|
|
170
|
+
import { HlmTextareaImports } from '@egose/shadcn-theme-ng/textarea';
|
|
171
|
+
import { HlmLabel } from '@egose/shadcn-theme-ng/label';
|
|
172
|
+
|
|
173
|
+
@Component({
|
|
174
|
+
selector: 'demo-template',
|
|
175
|
+
standalone: true,
|
|
176
|
+
imports: [...HlmTextareaImports, FormsModule, HlmLabel],
|
|
177
|
+
template: `
|
|
178
|
+
<div class="space-y-2">
|
|
179
|
+
<label hlmLabel for="notes">Notes</label>
|
|
180
|
+
<textarea hlmTextarea id="notes" name="notes" rows="3" [(ngModel)]="notes"></textarea>
|
|
181
|
+
<p class="text-sm text-muted-foreground">Preview: {{ notes || '—' }}</p>
|
|
182
|
+
</div>
|
|
183
|
+
`,
|
|
184
|
+
})
|
|
185
|
+
export class DemoTemplate {
|
|
186
|
+
notes = '';
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### 4. Disabled / readonly / error states
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
// demo-states.component.ts
|
|
194
|
+
import { Component } from '@angular/core';
|
|
195
|
+
import { HlmTextareaImports } from '@egose/shadcn-theme-ng/textarea';
|
|
196
|
+
import { HlmLabel } from '@egose/shadcn-theme-ng/label';
|
|
197
|
+
|
|
198
|
+
@Component({
|
|
199
|
+
selector: 'demo-states',
|
|
200
|
+
standalone: true,
|
|
201
|
+
imports: [...HlmTextareaImports, HlmLabel],
|
|
202
|
+
template: `
|
|
203
|
+
<div class="space-y-4">
|
|
204
|
+
<div class="space-y-2">
|
|
205
|
+
<label hlmLabel for="t-disabled">Disabled</label>
|
|
206
|
+
<textarea hlmTextarea id="t-disabled" disabled placeholder="Cannot edit…"></textarea>
|
|
207
|
+
</div>
|
|
208
|
+
<div class="space-y-2">
|
|
209
|
+
<label hlmLabel for="t-readonly">Readonly</label>
|
|
210
|
+
<textarea hlmTextarea id="t-readonly" readonly rows="2">Submitted value, read only.</textarea>
|
|
211
|
+
</div>
|
|
212
|
+
<div class="space-y-2">
|
|
213
|
+
<label hlmLabel for="t-invalid">Forced invalid</label>
|
|
214
|
+
<textarea hlmTextarea id="t-invalid" [forceInvalid]="true" rows="2">Fails custom rule.</textarea>
|
|
215
|
+
<p class="text-sm text-destructive">Custom server error goes here.</p>
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
`,
|
|
219
|
+
})
|
|
220
|
+
export class DemoStates {}
|
|
11
221
|
```
|
|
222
|
+
|
|
223
|
+
### 5. Inside a form-field (hint + error wiring)
|
|
224
|
+
|
|
225
|
+
`BrnFieldControlDescribedBy` auto-links the textarea to the surrounding field's hint/error for screen readers:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
// demo-field.component.ts
|
|
229
|
+
import { Component } from '@angular/core';
|
|
230
|
+
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
231
|
+
import { HlmTextareaImports } from '@egose/shadcn-theme-ng/textarea';
|
|
232
|
+
import { HlmFormFieldImports } from '@egose/shadcn-theme-ng/form-field';
|
|
233
|
+
|
|
234
|
+
@Component({
|
|
235
|
+
selector: 'demo-field',
|
|
236
|
+
standalone: true,
|
|
237
|
+
imports: [...HlmTextareaImports, ...HlmFormFieldImports, ReactiveFormsModule],
|
|
238
|
+
template: `
|
|
239
|
+
<form [formGroup]="form">
|
|
240
|
+
<hlm-form-field>
|
|
241
|
+
<label hlmLabel for="desc">Description</label>
|
|
242
|
+
<textarea hlmTextarea id="desc" formControlName="description" rows="4"></textarea>
|
|
243
|
+
<hlm-hint>Describe the issue in a few sentences.</hlm-hint>
|
|
244
|
+
<hlm-error>Description is required.</hlm-error>
|
|
245
|
+
</hlm-form-field>
|
|
246
|
+
</form>
|
|
247
|
+
`,
|
|
248
|
+
})
|
|
249
|
+
export class DemoField {
|
|
250
|
+
readonly form = new FormGroup({ description: new FormControl('', Validators.required) });
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
(Exact `hlm-form-field`/`hlm-hint`/`hlm-error` selector names come from `@egose/shadcn-theme-ng/form-field` — check that subpath for the current API.)
|
|
255
|
+
|
|
256
|
+
### 6. Autosize / custom sizing
|
|
257
|
+
|
|
258
|
+
The base styles include `field-sizing-content` + `min-h-16`, so the textarea grows with content in supporting browsers. Combine with `rows` and width utilities:
|
|
259
|
+
|
|
260
|
+
```html
|
|
261
|
+
<!-- fixed 8 rows, full width (default w-full) -->
|
|
262
|
+
<textarea hlmTextarea rows="8" placeholder="Long-form answer…"></textarea>
|
|
263
|
+
|
|
264
|
+
<!-- constrained width + no manual resize -->
|
|
265
|
+
<textarea hlmTextarea rows="4" class="max-w-xl resize-none" placeholder="No drag handle…"></textarea>
|
|
266
|
+
|
|
267
|
+
<!-- tall monospace variant for code -->
|
|
268
|
+
<textarea hlmTextarea rows="10" class="font-mono text-sm" spellcheck="false"></textarea>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Accessibility notes
|
|
272
|
+
|
|
273
|
+
- Every textarea needs a visible `<label>` (matching `for`/`id`) — placeholders are not labels.
|
|
274
|
+
- Wire hint/error text with `aria-describedby` (automatic inside `hlm-form-field` via `BrnFieldControlDescribedBy`; manual `aria-describedby="hint-id"` otherwise).
|
|
275
|
+
- Surface validation errors both visually (`forceInvalid` / form errors → `border-destructive`) and as text adjacent to the field; do not rely on color alone.
|
|
276
|
+
- Keep `maxlength` announcements truthful — show a live character count for constrained fields.
|
|
277
|
+
- Disabled textareas are skipped by assistive tech; use `readonly` when the value must remain perceivable but not editable.
|
|
278
|
+
|
|
279
|
+
## Theming / CSS variables
|
|
280
|
+
|
|
281
|
+
Keys off theme tokens (`border-input`, `bg-input/30` in dark mode, `ring-ring/50`, `text-muted-foreground` placeholder, `border-destructive`/`ring-destructive` invalid). Follows your shadcn theme automatically; extend via `class` (merged through `classes()`).
|
|
282
|
+
|
|
283
|
+
## Related subpaths
|
|
284
|
+
|
|
285
|
+
- `@egose/shadcn-theme-ng/input` — single-line sibling control.
|
|
286
|
+
- `@egose/shadcn-theme-ng/label` — accessible labels.
|
|
287
|
+
- `@egose/shadcn-theme-ng/form-field` / `@egose/shadcn-theme-ng/form-textarea` — labeled field with hint/error wiring.
|
|
288
|
+
- `@egose/shadcn-theme-ng/button` — submit actions for textarea forms.
|
package/toggle/README.md
CHANGED
|
@@ -1,11 +1,276 @@
|
|
|
1
|
-
# Toggle
|
|
1
|
+
# Toggle (`@egose/shadcn-theme-ng/toggle`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A two-state press button, equivalent to [shadcn/ui Toggle](https://ui.shadcn.com/docs/components/toggle). This subpath ships a single thin styling directive, `HlmToggle` (`button[hlmToggle]`), over spartan-ng's `BrnToggle` primitive, with `variant` (`default`/`outline`) and `size` (`default`/`sm`/`lg`) inputs powered by `toggleVariants` (cva). Use it for standalone on/off buttons (bold, italic, mute); use `toggle-group` for sets of related toggles.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> **Ships as:** `@egose/shadcn-theme-ng/toggle` and `@egose/shadcn-theme-ng-tw/toggle` (the `tw:`-prefixed Tailwind variant). See the [package README](../../README.md) for install steps, peer dependencies, Tailwind setup, and testing/release guidance. Do not publish this project directory independently.
|
|
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
|
+
`@spartan-ng/brain` and `class-variance-authority` arrive transitively. See the [package README](../../README.md) for the full peer-dependency table.
|
|
18
|
+
|
|
19
|
+
## Imports
|
|
20
|
+
|
|
21
|
+
All public symbols are re-exported from `projects/toggle/src/public-api.ts`:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { HlmToggle, HlmToggleImports, HlmToggleModule, toggleVariants } from '@egose/shadcn-theme-ng/toggle';
|
|
25
|
+
import type { ToggleVariants } from '@egose/shadcn-theme-ng/toggle';
|
|
26
|
+
// tw variant: swap to '@egose/shadcn-theme-ng-tw/toggle'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Standalone-component usage (preferred):
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { Component } from '@angular/core';
|
|
33
|
+
import { HlmToggleImports } from '@egose/shadcn-theme-ng/toggle';
|
|
34
|
+
|
|
35
|
+
@Component({
|
|
36
|
+
selector: 'app-demo',
|
|
37
|
+
standalone: true,
|
|
38
|
+
imports: [...HlmToggleImports],
|
|
39
|
+
template: `<button hlmToggle aria-label="Bold">B</button>`,
|
|
40
|
+
})
|
|
41
|
+
export class DemoComponent {}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
NgModule usage:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { NgModule } from '@angular/core';
|
|
48
|
+
import { HlmToggleModule } from '@egose/shadcn-theme-ng/toggle';
|
|
49
|
+
|
|
50
|
+
@NgModule({ imports: [HlmToggleModule] })
|
|
51
|
+
export class FeatureModule {}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
| Symbol | Kind | Description |
|
|
55
|
+
| ------------------ | ------------- | ------------------------------------------------------------------------------ |
|
|
56
|
+
| `HlmToggle` | Directive | `button[hlmToggle]` — styled toggle button over `BrnToggle`. |
|
|
57
|
+
| `toggleVariants` | cva fn | `toggleVariants({ variant, size })` — class generator, reused by toggle-group. |
|
|
58
|
+
| `ToggleVariants` | Type | `VariantProps<typeof toggleVariants>` — `variant`/`size` value types. |
|
|
59
|
+
| `HlmToggleImports` | `const` array | `[HlmToggle]` — spread into `imports: [...]`. |
|
|
60
|
+
| `HlmToggleModule` | NgModule | Imports + re-exports `HlmToggle`. |
|
|
61
|
+
|
|
62
|
+
## Anatomy / Structure
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<!-- basic (pressed when aria-pressed="true" / state on) -->
|
|
66
|
+
<button hlmToggle aria-label="Toggle bold">B</button>
|
|
67
|
+
|
|
68
|
+
<!-- variants + sizes -->
|
|
69
|
+
<button hlmToggle variant="outline" size="sm">Toggle</button>
|
|
70
|
+
<button hlmToggle variant="default" size="lg" aria-label="Toggle italic">I</button>
|
|
71
|
+
|
|
72
|
+
<!-- controlled pressed state -->
|
|
73
|
+
<button hlmToggle [(state)]="bold" aria-label="Bold">B</button>
|
|
74
|
+
|
|
75
|
+
<!-- disabled -->
|
|
76
|
+
<button hlmToggle disabled aria-label="Unavailable">X</button>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
> Only use on `<button>` elements — the selector is `button[hlmToggle]`.
|
|
80
|
+
|
|
81
|
+
## API reference
|
|
82
|
+
|
|
83
|
+
### `HlmToggle` — selector `button[hlmToggle]` (directive, hosts `BrnToggle`)
|
|
84
|
+
|
|
85
|
+
Own styling inputs:
|
|
86
|
+
|
|
87
|
+
| Input | Type | Default | Description |
|
|
88
|
+
| --------- | ------------------------------------------------------ | ----------- | --------------------------------------------------------------------- |
|
|
89
|
+
| `variant` | `ToggleVariants['variant']` (`'default' \| 'outline'`) | `'default'` | `default`: transparent; `outline`: bordered + shadow. |
|
|
90
|
+
| `size` | `ToggleVariants['size']` (`'default' \| 'sm' \| 'lg'`) | `'default'` | `default`: `h-9 min-w-9`; `sm`: `h-8 min-w-8`; `lg`: `h-10 min-w-10`. |
|
|
91
|
+
|
|
92
|
+
Forwarded to `BrnToggle` via `hostDirectives`:
|
|
93
|
+
|
|
94
|
+
| Input / Output | Direction | Description |
|
|
95
|
+
| -------------- | ----------------------- | -------------------------------------------------------------- |
|
|
96
|
+
| `id` | input | Element id. |
|
|
97
|
+
| `value` | input | Toggle value (used in groups / forms). |
|
|
98
|
+
| `disabled` | input | Disabled state. |
|
|
99
|
+
| `state` | input (two-way capable) | Pressed state (`'on' \| 'off'` per Brn). Supports `[(state)]`. |
|
|
100
|
+
| `aria-label` | input | Accessible name — **required** for icon-only toggles. |
|
|
101
|
+
| `type` | input | Button `type` (`button` default). |
|
|
102
|
+
| `stateChange` | output | Emits when the pressed state changes. |
|
|
103
|
+
|
|
104
|
+
Host: `data-slot="toggle"`. Active styling keys off `aria-pressed`/`data-[state]` (`aria-pressed:bg-muted`). User `class` is preserved and merged via `classes()`.
|
|
105
|
+
|
|
106
|
+
## Examples
|
|
107
|
+
|
|
108
|
+
### 1. Basic icon toggles (editor toolbar)
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
// demo-basic.component.ts
|
|
112
|
+
import { Component } from '@angular/core';
|
|
113
|
+
import { HlmToggleImports } from '@egose/shadcn-theme-ng/toggle';
|
|
114
|
+
import { NgIcon, provideIcons } from '@ng-icons/core';
|
|
115
|
+
import { lucideBold, lucideItalic, lucideUnderline } from '@ng-icons/lucide';
|
|
116
|
+
|
|
117
|
+
@Component({
|
|
118
|
+
selector: 'demo-basic',
|
|
119
|
+
standalone: true,
|
|
120
|
+
imports: [...HlmToggleImports, NgIcon],
|
|
121
|
+
providers: [provideIcons({ lucideBold, lucideItalic, lucideUnderline })],
|
|
122
|
+
template: `
|
|
123
|
+
<div class="flex gap-1">
|
|
124
|
+
<button hlmToggle aria-label="Toggle bold"><ng-icon name="lucideBold" /></button>
|
|
125
|
+
<button hlmToggle aria-label="Toggle italic"><ng-icon name="lucideItalic" /></button>
|
|
126
|
+
<button hlmToggle aria-label="Toggle underline"><ng-icon name="lucideUnderline" /></button>
|
|
127
|
+
</div>
|
|
128
|
+
`,
|
|
129
|
+
})
|
|
130
|
+
export class DemoBasic {}
|
|
11
131
|
```
|
|
132
|
+
|
|
133
|
+
### 2. All variants and sizes
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
// demo-variants.component.ts
|
|
137
|
+
import { Component } from '@angular/core';
|
|
138
|
+
import { HlmToggleImports } from '@egose/shadcn-theme-ng/toggle';
|
|
139
|
+
|
|
140
|
+
@Component({
|
|
141
|
+
selector: 'demo-variants',
|
|
142
|
+
standalone: true,
|
|
143
|
+
imports: [...HlmToggleImports],
|
|
144
|
+
template: `
|
|
145
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
146
|
+
<button hlmToggle variant="default" size="sm">Small</button>
|
|
147
|
+
<button hlmToggle variant="default" size="default">Default</button>
|
|
148
|
+
<button hlmToggle variant="default" size="lg">Large</button>
|
|
149
|
+
<button hlmToggle variant="outline" size="sm">Small outline</button>
|
|
150
|
+
<button hlmToggle variant="outline" size="default">Outline</button>
|
|
151
|
+
<button hlmToggle variant="outline" size="lg">Large outline</button>
|
|
152
|
+
</div>
|
|
153
|
+
`,
|
|
154
|
+
})
|
|
155
|
+
export class DemoVariants {}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 3. Controlled state with signals
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
// demo-controlled.component.ts
|
|
162
|
+
import { Component, signal } from '@angular/core';
|
|
163
|
+
import { HlmToggleImports } from '@egose/shadcn-theme-ng/toggle';
|
|
164
|
+
|
|
165
|
+
@Component({
|
|
166
|
+
selector: 'demo-controlled',
|
|
167
|
+
standalone: true,
|
|
168
|
+
imports: [...HlmToggleImports],
|
|
169
|
+
template: `
|
|
170
|
+
<button hlmToggle variant="outline" [(state)]="muted" aria-label="Toggle mute">
|
|
171
|
+
{{ muted() === 'on' ? 'Muted' : 'Unmuted' }}
|
|
172
|
+
</button>
|
|
173
|
+
<p class="text-sm text-muted-foreground">State: {{ muted() }}</p>
|
|
174
|
+
`,
|
|
175
|
+
})
|
|
176
|
+
export class DemoControlled {
|
|
177
|
+
readonly muted = signal<'on' | 'off'>('off');
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
React to changes imperatively with `(stateChange)`:
|
|
182
|
+
|
|
183
|
+
```html
|
|
184
|
+
<button hlmToggle (stateChange)="onToggle($event)" aria-label="Toggle preview">Preview</button>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
onToggle(state: unknown) {
|
|
189
|
+
console.log('toggle state:', state);
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 4. Disabled state
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
// demo-disabled.component.ts
|
|
197
|
+
import { Component } from '@angular/core';
|
|
198
|
+
import { HlmToggleImports } from '@egose/shadcn-theme-ng/toggle';
|
|
199
|
+
|
|
200
|
+
@Component({
|
|
201
|
+
selector: 'demo-disabled',
|
|
202
|
+
standalone: true,
|
|
203
|
+
imports: [...HlmToggleImports],
|
|
204
|
+
template: `
|
|
205
|
+
<div class="flex gap-2">
|
|
206
|
+
<button hlmToggle disabled aria-label="Unavailable feature">Off</button>
|
|
207
|
+
<button hlmToggle variant="outline" disabled aria-label="Unavailable outline">Off</button>
|
|
208
|
+
</div>
|
|
209
|
+
`,
|
|
210
|
+
})
|
|
211
|
+
export class DemoDisabled {}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### 5. Text toggle + composition with tooltip
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
// demo-composed.component.ts
|
|
218
|
+
import { Component } from '@angular/core';
|
|
219
|
+
import { HlmToggleImports } from '@egose/shadcn-theme-ng/toggle';
|
|
220
|
+
import { HlmTooltipImports } from '@egose/shadcn-theme-ng/tooltip';
|
|
221
|
+
|
|
222
|
+
@Component({
|
|
223
|
+
selector: 'demo-composed',
|
|
224
|
+
standalone: true,
|
|
225
|
+
imports: [...HlmToggleImports, ...HlmTooltipImports],
|
|
226
|
+
template: `
|
|
227
|
+
<button hlmToggle variant="outline" aria-label="Toggle sidebar" [hlmTooltip]="'Toggle sidebar'">☰</button>
|
|
228
|
+
`,
|
|
229
|
+
})
|
|
230
|
+
export class DemoComposed {}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
> `hlmTooltip` attaches to the same `<button>` — see `@egose/shadcn-theme-ng/tooltip` for delay/position options.
|
|
234
|
+
|
|
235
|
+
### 6. Advanced: reusing `toggleVariants` for custom components
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
// custom-toggle.component.ts
|
|
239
|
+
import { Component, input } from '@angular/core';
|
|
240
|
+
import { hlm } from '@egose/shadcn-theme-ng/utils';
|
|
241
|
+
import { toggleVariants, type ToggleVariants } from '@egose/shadcn-theme-ng/toggle';
|
|
242
|
+
|
|
243
|
+
@Component({
|
|
244
|
+
selector: 'custom-toggle',
|
|
245
|
+
standalone: true,
|
|
246
|
+
template: `<button [class]="classes()" [attr.aria-pressed]="pressed()"><ng-content /></button>`,
|
|
247
|
+
})
|
|
248
|
+
export class CustomToggle {
|
|
249
|
+
readonly variant = input<ToggleVariants['variant']>('default');
|
|
250
|
+
readonly size = input<ToggleVariants['size']>('default');
|
|
251
|
+
readonly pressed = input(false);
|
|
252
|
+
|
|
253
|
+
protected classes() {
|
|
254
|
+
return hlm(toggleVariants({ variant: this.variant(), size: this.size() }), 'my-extra-class');
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Accessibility notes
|
|
260
|
+
|
|
261
|
+
- Icon-only toggles **must** have `aria-label` (e.g. `aria-label="Toggle bold"`); text toggles get their name from content.
|
|
262
|
+
- The pressed state is exposed via `aria-pressed` by the Brn primitive — do not manage `aria-pressed` yourself when using `[(state)]`.
|
|
263
|
+
- Keep toggles keyboard-focusable (native `<button>` behavior); the focus ring (`focus-visible:ring-[3px]`) is built in.
|
|
264
|
+
- Use a toggle for an immediate on/off effect in place; use a switch for settings rows and a checkbox for form-submission choices.
|
|
265
|
+
- Disabled toggles render `pointer-events-none` + `opacity-50`; explain unavailability in nearby text when needed.
|
|
266
|
+
|
|
267
|
+
## Theming / CSS variables
|
|
268
|
+
|
|
269
|
+
`toggleVariants` keys off theme tokens (`hover:bg-muted`, `aria-pressed:bg-muted`, `border-input`, `ring-ring/50`, invalid-state `ring-destructive`). Follows your shadcn theme automatically; append overrides via `class` (merged through `classes()`).
|
|
270
|
+
|
|
271
|
+
## Related subpaths
|
|
272
|
+
|
|
273
|
+
- `@egose/shadcn-theme-ng/toggle-group` — sets of related toggles (alignment, formatting groups); reuses `toggleVariants`.
|
|
274
|
+
- `@egose/shadcn-theme-ng/switch` — on/off settings switch.
|
|
275
|
+
- `@egose/shadcn-theme-ng/tooltip` — label affordance for icon-only toggles.
|
|
276
|
+
- `@egose/shadcn-theme-ng/button` — action buttons (vs. pressed-state toggles).
|