@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/button/README.md
CHANGED
|
@@ -1,3 +1,483 @@
|
|
|
1
|
-
# Button
|
|
1
|
+
# Button (`@egose/shadcn-theme-ng/button`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The shadcn/ui Button: a consistently styled clickable control for actions, links-that-look-like
|
|
4
|
+
buttons, and form submits — with tones, sizes, outline appearances, icon slots, and a built-in
|
|
5
|
+
loading spinner.
|
|
6
|
+
|
|
7
|
+
This subpath exports **two** controls over the headless `BrnButton` from
|
|
8
|
+
`@spartan-ng/brain/button`:
|
|
9
|
+
|
|
10
|
+
- `HlmButton` (`button[hlmButton], a[hlmButton]`) — the full-featured component: `variant`,
|
|
11
|
+
`size`, `appearance`, `loading` (overlay spinner via `HlmSpinner`), `icon` template + position,
|
|
12
|
+
`disabled`, `type`, and `class` merging through `buttonVariants` (`cva`) + `hlm()`.
|
|
13
|
+
- `HlmBtn` (`button[hlmBtn], a[hlmBtn]`, `exportAs: hlmBtn`) — a thin `BrnButton` wrapper with only
|
|
14
|
+
`variant` / `size` / `type` (defaults injectable via `provideBrnButtonConfig`). Used internally
|
|
15
|
+
by footer-style consumers (e.g. `alert-dialog`) and handy when you need the tones without the
|
|
16
|
+
spinner/icon machinery.
|
|
17
|
+
|
|
18
|
+
> **Ships as:** `@egose/shadcn-theme-ng/button` and `@egose/shadcn-theme-ng-tw/button`
|
|
19
|
+
> (the `tw:`-prefixed Tailwind variant). Both expose the identical TypeScript surface; only the
|
|
20
|
+
> emitted Tailwind class strings differ. See the [package README](../../README.md) for install
|
|
21
|
+
> steps, peer dependencies, and Tailwind setup. Do not publish this project directory independently.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Plain Tailwind (no prefix)
|
|
27
|
+
npm install @egose/shadcn-theme-ng
|
|
28
|
+
|
|
29
|
+
# tw:-prefixed Tailwind variant
|
|
30
|
+
npm install @egose/shadcn-theme-ng-tw
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Peer dependencies (Angular, `@spartan-ng/brain`, `@ng-icons/*`, `rxjs`, …) are documented in the
|
|
34
|
+
[package README](../../README.md#peer-dependencies). This subpath additionally relies at runtime on
|
|
35
|
+
`@egose/shadcn-theme-ng/utils` (`hlm()`) and `@egose/shadcn-theme-ng/spinner` (`HlmSpinner`,
|
|
36
|
+
used by the `HlmButton` loading state).
|
|
37
|
+
|
|
38
|
+
## Imports
|
|
39
|
+
|
|
40
|
+
All symbols are exported from the subpath root (`projects/button/src/public-api.ts`):
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import {
|
|
44
|
+
HlmButton,
|
|
45
|
+
HlmBtn,
|
|
46
|
+
buttonVariants,
|
|
47
|
+
provideBrnButtonConfig,
|
|
48
|
+
injectBrnButtonConfig,
|
|
49
|
+
HlmButtonImports, // [HlmButton, HlmBtn]
|
|
50
|
+
HlmButtonModule,
|
|
51
|
+
type ButtonVariants,
|
|
52
|
+
type VariantType,
|
|
53
|
+
type SizeType,
|
|
54
|
+
type AppearanceType,
|
|
55
|
+
type BrnButtonConfig,
|
|
56
|
+
} from '@egose/shadcn-theme-ng/button';
|
|
57
|
+
// tw variant: replace with '@egose/shadcn-theme-ng-tw/button'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Standalone component — spread the `*Imports` array (both controls):
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { Component } from '@angular/core';
|
|
64
|
+
import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
|
|
65
|
+
|
|
66
|
+
@Component({
|
|
67
|
+
selector: 'app-demo',
|
|
68
|
+
standalone: true,
|
|
69
|
+
imports: [...HlmButtonImports],
|
|
70
|
+
template: `...`,
|
|
71
|
+
})
|
|
72
|
+
export class DemoComponent {}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
NgModule-based consumer — import the module:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { NgModule } from '@angular/core';
|
|
79
|
+
import { HlmButtonModule } from '@egose/shadcn-theme-ng/button';
|
|
80
|
+
|
|
81
|
+
@NgModule({ imports: [HlmButtonModule] })
|
|
82
|
+
export class DemoModule {}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Global defaults — provide a `BrnButtonConfig` once (consumed by `HlmBtn`):
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { Component } from '@angular/core';
|
|
89
|
+
import { HlmButtonImports, provideBrnButtonConfig } from '@egose/shadcn-theme-ng/button';
|
|
90
|
+
|
|
91
|
+
@Component({
|
|
92
|
+
selector: 'app-shell',
|
|
93
|
+
standalone: true,
|
|
94
|
+
imports: [...HlmButtonImports],
|
|
95
|
+
providers: [provideBrnButtonConfig({ variant: 'secondary', size: 'sm' })],
|
|
96
|
+
template: `...`,
|
|
97
|
+
})
|
|
98
|
+
export class ShellComponent {}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Anatomy / Structure
|
|
102
|
+
|
|
103
|
+
```html
|
|
104
|
+
<!-- full-featured -->
|
|
105
|
+
<button hlmButton variant="primary" size="default" appearance="solid">Save</button>
|
|
106
|
+
<a hlmButton variant="outline" href="/docs">Docs</a>
|
|
107
|
+
|
|
108
|
+
<!-- thin wrapper -->
|
|
109
|
+
<button hlmBtn variant="ghost" size="sm">Cancel</button>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Real selectors (from source):
|
|
113
|
+
|
|
114
|
+
| Class | Selector(s) | Kind |
|
|
115
|
+
| ----------- | ------------------------------------------------ | --------- |
|
|
116
|
+
| `HlmButton` | `button[hlmButton], a[hlmButton]` | Component |
|
|
117
|
+
| `HlmBtn` | `button[hlmBtn], a[hlmBtn]` (`exportAs: hlmBtn`) | Directive |
|
|
118
|
+
|
|
119
|
+
`HlmButton` loading template (from source): when `loading()` is true, the projected content is
|
|
120
|
+
kept invisible for sizing while an absolutely centered `<hlm-spinner>` overlays it; otherwise the
|
|
121
|
+
content renders in a flex row with the optional `icon()` template on the `left`/`right`.
|
|
122
|
+
|
|
123
|
+
## API reference
|
|
124
|
+
|
|
125
|
+
### `HlmButton` — `button[hlmButton], a[hlmButton]`
|
|
126
|
+
|
|
127
|
+
| Input | Type | Default | Description |
|
|
128
|
+
| ------------------------------ | ----------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
129
|
+
| `variant` | `VariantType` | `'primary'` | Semantic color tone, plus legacy `default`, `outline`, `link`, and `ghost` variants (15 values). |
|
|
130
|
+
| `size` | `SizeType` | `'default'` | Height/padding scale (13 values, see below). |
|
|
131
|
+
| `appearance` | `AppearanceType` | `'solid'` | `'solid'`, `'outline'` (theme background + tone border/text), `'outline-filled'` (fills on hover), `'ghost'`, or `'link'`. |
|
|
132
|
+
| `loading` | `boolean` | `false` | Shows the spinner overlay; sets `aria-busy`, forces `disabled`, adds `pointer-events-none`. |
|
|
133
|
+
| `icon` | `TemplateRef<unknown> \| undefined` | `undefined` | Icon template rendered beside the label. |
|
|
134
|
+
| `iconPosition` | `'left' \| 'right'` | `'left'` | Which side the `icon()` renders on. |
|
|
135
|
+
| `disabled` | `boolean` | `false` | Disables the control (also forwarded to `BrnButton`). |
|
|
136
|
+
| `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | Native button type (reflected as `type` attr). |
|
|
137
|
+
| `class` (alias of `userClass`) | `ClassValue` | `''` | Extra classes merged via `hlm()`. |
|
|
138
|
+
| `spinnerUserClass` | `ClassValue` | `''` | Extra classes for the loading `<hlm-spinner>` (defaults to the button's current text color). |
|
|
139
|
+
|
|
140
|
+
| Method | Signature | Description |
|
|
141
|
+
| ---------- | --------------------------------- | ----------------------------------- |
|
|
142
|
+
| `setClass` | `setClass(classes: string): void` | Imperatively appends extra classes. |
|
|
143
|
+
|
|
144
|
+
`variant` values: `default` · `primary` · `secondary` · `success` · `warning` · `danger` ·
|
|
145
|
+
`info` · `light` · `dark` · `accent` · `destructive` · `muted` · `outline` · `link` · `ghost`.
|
|
146
|
+
|
|
147
|
+
`size` values: `xs` · `sm` · `default` · `lg` · `icon` · `icon-xs` · `icon-sm` · `icon-lg` ·
|
|
148
|
+
`compact-xs` · `compact-sm` · `compact-default` · `compact-lg` · `compact-icon`.
|
|
149
|
+
|
|
150
|
+
`appearance` values: `solid` · `outline` · `outline-filled` · `ghost` · `link`.
|
|
151
|
+
|
|
152
|
+
Prefer a semantic `variant` with an independent `appearance`, for example
|
|
153
|
+
`variant="success" appearance="ghost"` or `variant="danger" appearance="link"`.
|
|
154
|
+
`ghost` is transparent with a subtle tone-colored hover background; `link` is transparent
|
|
155
|
+
with an underline on hover. Both have no border or shadow.
|
|
156
|
+
Legacy `variant="outline"`, `variant="link"`, and `variant="ghost"` remain supported;
|
|
157
|
+
`default` remains an alias for the primary color. The legacy ghost variant retains its
|
|
158
|
+
light-colored hover treatment.
|
|
159
|
+
|
|
160
|
+
### `HlmBtn` — `button[hlmBtn], a[hlmBtn]`
|
|
161
|
+
|
|
162
|
+
Thin wrapper: only `variant` / `size` / `type` (+ `class`), no `appearance`, `loading`, or icon
|
|
163
|
+
support. Its `variant`/`size` defaults come from `injectBrnButtonConfig()` (global default
|
|
164
|
+
`{ variant: 'default', size: 'default' }`, overridable per subtree with
|
|
165
|
+
`provideBrnButtonConfig()`).
|
|
166
|
+
|
|
167
|
+
| Input | Type | Default | Description |
|
|
168
|
+
| ------------------------------ | --------------------------------- | ----------------------------- | ------------------- |
|
|
169
|
+
| `variant` | `ButtonVariants['variant']` | injected config (`'default'`) | Tone. |
|
|
170
|
+
| `size` | `ButtonVariants['size']` | injected config (`'default'`) | Size. |
|
|
171
|
+
| `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | Native button type. |
|
|
172
|
+
| `class` (alias of `userClass`) | `ClassValue` | `''` | Extra classes. |
|
|
173
|
+
|
|
174
|
+
| Method | Signature | Description |
|
|
175
|
+
| ---------- | --------------------------------- | ----------------------------------- |
|
|
176
|
+
| `setClass` | `setClass(classes: string): void` | Imperatively appends extra classes. |
|
|
177
|
+
|
|
178
|
+
### Config helpers (`button.token.ts`)
|
|
179
|
+
|
|
180
|
+
| Symbol | Signature | Description |
|
|
181
|
+
| ------------------------ | ----------------------------------------------------- | ------------------------------------------------------------ |
|
|
182
|
+
| `BrnButtonConfig` | `{ variant; size }` | Shape of the button defaults object. |
|
|
183
|
+
| `provideBrnButtonConfig` | `(config: Partial<BrnButtonConfig>) => ValueProvider` | Provide subtree defaults for `HlmBtn`. |
|
|
184
|
+
| `injectBrnButtonConfig` | `() => BrnButtonConfig` | Read the effective config (falls back to built-in defaults). |
|
|
185
|
+
|
|
186
|
+
Exported styling/types: `buttonVariants` (`cva` table — reuse for custom hosts), `ButtonVariants`,
|
|
187
|
+
`VariantType`, `SizeType`, `AppearanceType`.
|
|
188
|
+
|
|
189
|
+
## Examples
|
|
190
|
+
|
|
191
|
+
### 1. Basic usage
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
import { Component } from '@angular/core';
|
|
195
|
+
import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
|
|
196
|
+
|
|
197
|
+
@Component({
|
|
198
|
+
selector: 'app-button-basic',
|
|
199
|
+
standalone: true,
|
|
200
|
+
imports: [...HlmButtonImports],
|
|
201
|
+
template: `
|
|
202
|
+
<div class="tw:flex tw:gap-2">
|
|
203
|
+
<button hlmButton>Primary</button>
|
|
204
|
+
<button hlmBtn variant="secondary">Thin wrapper</button>
|
|
205
|
+
<a hlmButton variant="outline" href="/docs">Docs link</a>
|
|
206
|
+
</div>
|
|
207
|
+
`,
|
|
208
|
+
})
|
|
209
|
+
export class ButtonBasicComponent {}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
```html
|
|
213
|
+
<app-button-basic />
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 2. All variants and sizes
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
import { Component } from '@angular/core';
|
|
220
|
+
import { HlmButton, type VariantType, type SizeType } from '@egose/shadcn-theme-ng/button';
|
|
221
|
+
|
|
222
|
+
@Component({
|
|
223
|
+
selector: 'app-button-matrix',
|
|
224
|
+
standalone: true,
|
|
225
|
+
imports: [HlmButton],
|
|
226
|
+
template: `
|
|
227
|
+
<div class="tw:flex tw:flex-wrap tw:gap-2">
|
|
228
|
+
@for (v of variants; track v) {
|
|
229
|
+
<button hlmButton [variant]="v">{{ v }}</button>
|
|
230
|
+
}
|
|
231
|
+
</div>
|
|
232
|
+
<div class="tw:mt-4 tw:flex tw:flex-wrap tw:items-center tw:gap-2">
|
|
233
|
+
@for (s of sizes; track s) {
|
|
234
|
+
<button hlmButton variant="secondary" [size]="s">{{ s }}</button>
|
|
235
|
+
}
|
|
236
|
+
</div>
|
|
237
|
+
`,
|
|
238
|
+
})
|
|
239
|
+
export class ButtonMatrixComponent {
|
|
240
|
+
readonly variants: VariantType[] = [
|
|
241
|
+
'default',
|
|
242
|
+
'primary',
|
|
243
|
+
'secondary',
|
|
244
|
+
'success',
|
|
245
|
+
'warning',
|
|
246
|
+
'danger',
|
|
247
|
+
'info',
|
|
248
|
+
'light',
|
|
249
|
+
'dark',
|
|
250
|
+
'accent',
|
|
251
|
+
'destructive',
|
|
252
|
+
'muted',
|
|
253
|
+
'outline',
|
|
254
|
+
'link',
|
|
255
|
+
'ghost',
|
|
256
|
+
];
|
|
257
|
+
readonly sizes: SizeType[] = [
|
|
258
|
+
'xs',
|
|
259
|
+
'sm',
|
|
260
|
+
'default',
|
|
261
|
+
'lg',
|
|
262
|
+
'icon',
|
|
263
|
+
'compact-xs',
|
|
264
|
+
'compact-sm',
|
|
265
|
+
'compact-default',
|
|
266
|
+
'compact-lg',
|
|
267
|
+
];
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### 3. Outline appearances + icon buttons
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
import { Component } from '@angular/core';
|
|
275
|
+
import { NgIcon, provideIcons } from '@ng-icons/core';
|
|
276
|
+
import { lucidePlus, lucideTrash } from '@ng-icons/lucide';
|
|
277
|
+
import { HlmButton } from '@egose/shadcn-theme-ng/button';
|
|
278
|
+
import { HlmIcon } from '@egose/shadcn-theme-ng/icon';
|
|
279
|
+
|
|
280
|
+
@Component({
|
|
281
|
+
selector: 'app-button-appearance',
|
|
282
|
+
standalone: true,
|
|
283
|
+
imports: [HlmButton, NgIcon, HlmIcon],
|
|
284
|
+
providers: [provideIcons({ lucidePlus, lucideTrash })],
|
|
285
|
+
template: `
|
|
286
|
+
<div class="tw:flex tw:flex-wrap tw:items-center tw:gap-2">
|
|
287
|
+
<button hlmButton variant="success" appearance="outline">Outline</button>
|
|
288
|
+
<button hlmButton variant="destructive" appearance="outline-filled">Outline-filled (hover me)</button>
|
|
289
|
+
<button hlmButton variant="success" appearance="ghost">Ghost</button>
|
|
290
|
+
<button hlmButton variant="danger" appearance="link">Link appearance</button>
|
|
291
|
+
<button hlmButton size="icon" aria-label="Create">
|
|
292
|
+
<ng-icon hlm name="lucidePlus" />
|
|
293
|
+
</button>
|
|
294
|
+
<button hlmButton variant="destructive" size="icon-sm" aria-label="Delete">
|
|
295
|
+
<ng-icon hlm name="lucideTrash" size="sm" />
|
|
296
|
+
</button>
|
|
297
|
+
</div>
|
|
298
|
+
`,
|
|
299
|
+
})
|
|
300
|
+
export class ButtonAppearanceComponent {}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### 4. Loading state with `HlmButton` (async submit)
|
|
304
|
+
|
|
305
|
+
`loading` overlays a tone-matched spinner, keeps the button width stable, sets `aria-busy`, and
|
|
306
|
+
blocks interaction until done:
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
import { Component, signal } from '@angular/core';
|
|
310
|
+
import { HlmButton } from '@egose/shadcn-theme-ng/button';
|
|
311
|
+
|
|
312
|
+
@Component({
|
|
313
|
+
selector: 'app-button-loading',
|
|
314
|
+
standalone: true,
|
|
315
|
+
imports: [HlmButton],
|
|
316
|
+
template: `
|
|
317
|
+
<form (ngSubmit)="submit()">
|
|
318
|
+
<button hlmButton type="submit" [loading]="saving()">Save changes</button>
|
|
319
|
+
<button hlmButton variant="ghost" type="button" [disabled]="saving()">Cancel</button>
|
|
320
|
+
</form>
|
|
321
|
+
`,
|
|
322
|
+
})
|
|
323
|
+
export class ButtonLoadingComponent {
|
|
324
|
+
readonly saving = signal(false);
|
|
325
|
+
|
|
326
|
+
async submit() {
|
|
327
|
+
this.saving.set(true);
|
|
328
|
+
try {
|
|
329
|
+
await fetch('/api/save', { method: 'POST' });
|
|
330
|
+
} finally {
|
|
331
|
+
this.saving.set(false);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### 5. Icon templates with `icon` / `iconPosition`
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
import { Component } from '@angular/core';
|
|
341
|
+
import { NgIcon, provideIcons } from '@ng-icons/core';
|
|
342
|
+
import { lucideArrowLeft, lucideArrowRight } from '@ng-icons/lucide';
|
|
343
|
+
import { HlmButton } from '@egose/shadcn-theme-ng/button';
|
|
344
|
+
|
|
345
|
+
@Component({
|
|
346
|
+
selector: 'app-button-icons',
|
|
347
|
+
standalone: true,
|
|
348
|
+
imports: [HlmButton, NgIcon],
|
|
349
|
+
providers: [provideIcons({ lucideArrowLeft, lucideArrowRight })],
|
|
350
|
+
template: `
|
|
351
|
+
<ng-template #backIcon><ng-icon name="lucideArrowLeft" /></ng-template>
|
|
352
|
+
<ng-template #nextIcon><ng-icon name="lucideArrowRight" /></ng-template>
|
|
353
|
+
|
|
354
|
+
<div class="tw:flex tw:gap-2">
|
|
355
|
+
<button hlmButton variant="outline" [icon]="backIcon" iconPosition="left">Back</button>
|
|
356
|
+
<button hlmButton [icon]="nextIcon" iconPosition="right">Next</button>
|
|
357
|
+
</div>
|
|
358
|
+
`,
|
|
359
|
+
})
|
|
360
|
+
export class ButtonIconsComponent {}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### 6. Reactive form submit + global `HlmBtn` defaults
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
import { Component } from '@angular/core';
|
|
367
|
+
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
368
|
+
import { HlmButtonImports, provideBrnButtonConfig } from '@egose/shadcn-theme-ng/button';
|
|
369
|
+
|
|
370
|
+
@Component({
|
|
371
|
+
selector: 'app-button-form',
|
|
372
|
+
standalone: true,
|
|
373
|
+
imports: [...HlmButtonImports, ReactiveFormsModule],
|
|
374
|
+
// Every hlmBtn in this subtree defaults to compact secondary unless overridden.
|
|
375
|
+
providers: [provideBrnButtonConfig({ variant: 'secondary', size: 'compact-default' })],
|
|
376
|
+
template: `
|
|
377
|
+
<form [formGroup]="form" (ngSubmit)="submit()" class="tw:flex tw:flex-col tw:gap-3">
|
|
378
|
+
<input formControlName="name" placeholder="Project name" />
|
|
379
|
+
<div class="tw:flex tw:gap-2">
|
|
380
|
+
<button hlmButton [disabled]="form.invalid || saving">Create project</button>
|
|
381
|
+
<button hlmBtn type="button" (click)="form.reset()">Reset</button>
|
|
382
|
+
</div>
|
|
383
|
+
</form>
|
|
384
|
+
`,
|
|
385
|
+
})
|
|
386
|
+
export class ButtonFormComponent {
|
|
387
|
+
readonly form = new FormGroup({ name: new FormControl('', { validators: Validators.required, nonNullable: true }) });
|
|
388
|
+
saving = false;
|
|
389
|
+
|
|
390
|
+
submit() {
|
|
391
|
+
if (this.form.invalid) return;
|
|
392
|
+
this.saving = true;
|
|
393
|
+
setTimeout(() => (this.saving = false), 800);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## Accessibility notes
|
|
399
|
+
|
|
400
|
+
- These are native `<button>` / `<a>` elements enhanced by `BrnButton`: keyboard focus,
|
|
401
|
+
`Enter`/`Space` activation, and `disabled` semantics work out of the box. Never render a
|
|
402
|
+
`div` with `hlmButton` — the selector only matches `button`/`a` for exactly this reason.
|
|
403
|
+
- `loading` sets `aria-busy="true"` and disables the control; announce completion with adjacent
|
|
404
|
+
text or a toast (see `.../sonner`) since the spinner itself is silent.
|
|
405
|
+
- Icon-only buttons (`size="icon*"`) **must** have an `aria-label`. Keep visible labels verb-led
|
|
406
|
+
and unique per context ("Delete project", not "Delete" × 5).
|
|
407
|
+
- `disabled` uses `pointer-events-none` + reduced opacity — pair disabled states with a visible
|
|
408
|
+
explanation (e.g. "Complete the required fields") rather than leaving users guessing.
|
|
409
|
+
|
|
410
|
+
## Theming / CSS variables
|
|
411
|
+
|
|
412
|
+
Buttons use shared semantic Tailwind color tokens, so the consumer controls their palette in
|
|
413
|
+
global CSS. Define each tone and its `-foreground` partner: `primary`, `secondary`, `success`,
|
|
414
|
+
`warning`, `danger`, `info`, `light`, `dark`, `accent`, `destructive`, and `muted`. Also provide
|
|
415
|
+
`background` for outline surfaces and `ring` for keyboard focus.
|
|
416
|
+
|
|
417
|
+
For example, add these mappings to your Tailwind v4 stylesheet (alongside the package source
|
|
418
|
+
scan described in the package README):
|
|
419
|
+
|
|
420
|
+
```css
|
|
421
|
+
@theme inline {
|
|
422
|
+
--color-primary: var(--primary);
|
|
423
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
424
|
+
--color-success: var(--success);
|
|
425
|
+
--color-success-foreground: var(--success-foreground);
|
|
426
|
+
--color-background: hsl(var(--background));
|
|
427
|
+
--color-ring: hsl(var(--ring));
|
|
428
|
+
/* Map the remaining semantic tones in the same way. */
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
:root {
|
|
432
|
+
--primary: #228be6;
|
|
433
|
+
--primary-foreground: #ffffff;
|
|
434
|
+
--success: #28a745;
|
|
435
|
+
--success-foreground: #ffffff;
|
|
436
|
+
--background: 0 0% 100%;
|
|
437
|
+
--ring: 0 0% 3.9%;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.dark {
|
|
441
|
+
--primary: #74c0fc;
|
|
442
|
+
--primary-foreground: #102a43;
|
|
443
|
+
--success: #75b798;
|
|
444
|
+
--success-foreground: #0a3622;
|
|
445
|
+
--background: 0 0% 3.9%;
|
|
446
|
+
--ring: 0 0% 83.1%;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.brand-theme {
|
|
450
|
+
--primary: #7950f2;
|
|
451
|
+
--primary-foreground: #ffffff;
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
The semantic palette variables above contain complete CSS colors; `background` and `ring`
|
|
456
|
+
use HSL channels with `hsl(...)` mappings. `@theme inline` ensures a `.dark` or `.brand-theme`
|
|
457
|
+
ancestor can override colors for just its subtree. The example app's `src/styles.css` supplies
|
|
458
|
+
the full light/dark palette. Keep the existing `prefix(tw)` import when using the `-tw` package;
|
|
459
|
+
the `@theme` token names stay unprefixed.
|
|
460
|
+
|
|
461
|
+
```html
|
|
462
|
+
<section class="brand-theme">
|
|
463
|
+
<button hlmButton variant="primary" appearance="outline">Branded outline</button>
|
|
464
|
+
<button hlmButton variant="success" appearance="ghost">Save changes</button>
|
|
465
|
+
</section>
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
Adding a CSS token such as `--color-brand` does **not** register `variant="brand"`;
|
|
469
|
+
variant names remain a fixed typed API. Customize an existing semantic token or supply
|
|
470
|
+
utility classes through `class` instead.
|
|
471
|
+
|
|
472
|
+
`class` overrides are merged after the variant/appearance styles; `setClass()` appends
|
|
473
|
+
imperative overrides. The loading spinner inherits the button's resolved text color, and
|
|
474
|
+
`spinnerUserClass` can override it. For custom hosts, use
|
|
475
|
+
`hlm(buttonVariants({ variant: 'success', appearance: 'outline' }), customClasses)`;
|
|
476
|
+
the shared variant function includes all appearance styles.
|
|
477
|
+
|
|
478
|
+
## Related subpaths
|
|
479
|
+
|
|
480
|
+
- `@egose/shadcn-theme-ng/spinner` — the loader rendered in the `loading` state
|
|
481
|
+
- `@egose/shadcn-theme-ng/badge` — counts/status chips composed inside buttons
|
|
482
|
+
- `@egose/shadcn-theme-ng/button-group` — joined button rows
|
|
483
|
+
- `@egose/shadcn-theme-ng/alert-dialog` — footer `hlmAlertDialogAction` / `hlmAlertDialogCancel` (built on `HlmBtn`)
|