@egose/shadcn-theme-ng 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/README.md +2 -2
  2. package/accordion/README.md +405 -2
  3. package/alert/README.md +372 -2
  4. package/alert-dialog/README.md +471 -5
  5. package/aspect-ratio/README.md +272 -5
  6. package/autocomplete/README.md +502 -2
  7. package/autocomplete/fesm2022/autocomplete.mjs +1 -1
  8. package/avatar/README.md +357 -5
  9. package/badge/README.md +318 -2
  10. package/basic-alert/README.md +353 -2
  11. package/breadcrumb/README.md +406 -5
  12. package/button/README.md +482 -2
  13. package/button/fesm2022/button.mjs +85 -107
  14. package/button/types/button.d.ts +5 -8
  15. package/button-group/README.md +318 -5
  16. package/calendar/README.md +357 -2
  17. package/card/README.md +331 -5
  18. package/carousel/README.md +333 -5
  19. package/carousel/fesm2022/carousel.mjs +4 -1
  20. package/checkbox/README.md +320 -2
  21. package/checkbox/fesm2022/checkbox.mjs +6 -7
  22. package/checkbox/types/checkbox.d.ts +1 -1
  23. package/collapsible/README.md +332 -5
  24. package/combobox/README.md +507 -5
  25. package/combobox/fesm2022/combobox.mjs +5 -2
  26. package/command/README.md +435 -5
  27. package/confirmation-dialog/README.md +301 -2
  28. package/context-menu/README.md +366 -5
  29. package/date-picker/README.md +469 -2
  30. package/date-picker/fesm2022/date-picker.mjs +150 -32
  31. package/date-picker/types/date-picker.d.ts +102 -9
  32. package/dialog/README.md +448 -2
  33. package/drawer/README.md +395 -5
  34. package/dropdown-menu/README.md +417 -5
  35. package/empty/README.md +329 -5
  36. package/field/README.md +385 -5
  37. package/form-autocomplete/README.md +177 -0
  38. package/form-autocomplete/fesm2022/form-autocomplete.mjs +125 -0
  39. package/form-autocomplete/package.json +24 -0
  40. package/form-autocomplete/types/form-autocomplete.d.ts +61 -0
  41. package/form-checkbox/README.md +322 -2
  42. package/form-checkbox/fesm2022/form-checkbox.mjs +22 -9
  43. package/form-checkbox/types/form-checkbox.d.ts +23 -4
  44. package/form-combobox/README.md +202 -0
  45. package/form-combobox/fesm2022/form-combobox.mjs +147 -0
  46. package/form-combobox/package.json +24 -0
  47. package/form-combobox/types/form-combobox.d.ts +73 -0
  48. package/form-date-picker/README.md +348 -2
  49. package/form-date-picker/fesm2022/form-date-picker.mjs +38 -12
  50. package/form-date-picker/types/form-date-picker.d.ts +17 -1
  51. package/form-date-picker-multi/README.md +191 -0
  52. package/form-date-picker-multi/fesm2022/form-date-picker-multi.mjs +129 -0
  53. package/form-date-picker-multi/package.json +24 -0
  54. package/form-date-picker-multi/types/form-date-picker-multi.d.ts +58 -0
  55. package/form-date-range-picker/README.md +253 -0
  56. package/form-date-range-picker/fesm2022/form-date-range-picker.mjs +130 -0
  57. package/form-date-range-picker/package.json +24 -0
  58. package/form-date-range-picker/types/form-date-range-picker.d.ts +55 -0
  59. package/form-field/README.md +356 -2
  60. package/form-field-simple/README.md +340 -2
  61. package/form-input-otp/README.md +194 -0
  62. package/form-input-otp/fesm2022/form-input-otp.mjs +106 -0
  63. package/form-input-otp/package.json +24 -0
  64. package/form-input-otp/types/form-input-otp.d.ts +58 -0
  65. package/form-month-year-picker/README.md +188 -0
  66. package/form-month-year-picker/fesm2022/form-month-year-picker.mjs +125 -0
  67. package/form-month-year-picker/package.json +24 -0
  68. package/form-month-year-picker/types/form-month-year-picker.d.ts +53 -0
  69. package/form-native-select/README.md +205 -0
  70. package/form-native-select/fesm2022/form-native-select.mjs +103 -0
  71. package/form-native-select/package.json +24 -0
  72. package/form-native-select/types/form-native-select.d.ts +57 -0
  73. package/form-phone-input/README.md +188 -0
  74. package/form-phone-input/fesm2022/form-phone-input.mjs +113 -0
  75. package/form-phone-input/package.json +24 -0
  76. package/form-phone-input/types/form-phone-input.d.ts +58 -0
  77. package/form-radio-group/README.md +198 -0
  78. package/form-radio-group/fesm2022/form-radio-group.mjs +111 -0
  79. package/form-radio-group/package.json +24 -0
  80. package/form-radio-group/types/form-radio-group.d.ts +63 -0
  81. package/form-searchable-multiselect/README.md +371 -2
  82. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +19 -10
  83. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +18 -1
  84. package/form-select/README.md +360 -2
  85. package/form-select/fesm2022/form-select.mjs +20 -11
  86. package/form-select/types/form-select.d.ts +18 -1
  87. package/form-slider/README.md +182 -0
  88. package/form-slider/fesm2022/form-slider.mjs +106 -0
  89. package/form-slider/package.json +24 -0
  90. package/form-slider/types/form-slider.d.ts +60 -0
  91. package/form-switch/README.md +173 -0
  92. package/form-switch/fesm2022/form-switch.mjs +100 -0
  93. package/form-switch/package.json +24 -0
  94. package/form-switch/types/form-switch.d.ts +50 -0
  95. package/form-text-input/README.md +381 -2
  96. package/form-text-input/fesm2022/form-text-input.mjs +19 -10
  97. package/form-text-input/types/form-text-input.d.ts +18 -1
  98. package/form-textarea/README.md +357 -2
  99. package/form-textarea/fesm2022/form-textarea.mjs +19 -10
  100. package/form-textarea/types/form-textarea.d.ts +18 -1
  101. package/form-toggle/README.md +186 -0
  102. package/form-toggle/fesm2022/form-toggle.mjs +159 -0
  103. package/form-toggle/package.json +24 -0
  104. package/form-toggle/types/form-toggle.d.ts +82 -0
  105. package/form-toggle-group/README.md +176 -0
  106. package/form-toggle-group/fesm2022/form-toggle-group.mjs +116 -0
  107. package/form-toggle-group/package.json +24 -0
  108. package/form-toggle-group/types/form-toggle-group.d.ts +65 -0
  109. package/hover-card/README.md +256 -5
  110. package/icon/README.md +239 -2
  111. package/input/README.md +269 -2
  112. package/input-group/README.md +335 -5
  113. package/input-group/fesm2022/input-group.mjs +22 -12
  114. package/input-group/types/input-group.d.ts +4 -1
  115. package/input-otp/README.md +375 -5
  116. package/item/README.md +385 -5
  117. package/item/fesm2022/item.mjs +3 -3
  118. package/kbd/README.md +291 -5
  119. package/label/README.md +272 -2
  120. package/layout-simple/README.md +193 -2
  121. package/layout-simple/fesm2022/layout-simple.mjs +472 -236
  122. package/layout-simple/types/layout-simple.d.ts +174 -137
  123. package/menu/README.md +417 -2
  124. package/menubar/README.md +343 -5
  125. package/native-select/README.md +323 -5
  126. package/native-select/fesm2022/native-select.mjs +18 -6
  127. package/native-select/types/native-select.d.ts +7 -2
  128. package/navigation-menu/README.md +369 -5
  129. package/package.json +57 -1
  130. package/pagination/README.md +388 -5
  131. package/phone-input/README.md +114 -0
  132. package/phone-input/fesm2022/phone-input.mjs +191 -0
  133. package/phone-input/package.json +24 -0
  134. package/phone-input/types/phone-input.d.ts +67 -0
  135. package/popover/README.md +331 -2
  136. package/progress/README.md +311 -5
  137. package/radio-group/README.md +364 -2
  138. package/radio-group/fesm2022/radio-group.mjs +5 -1
  139. package/resizable/README.md +269 -5
  140. package/scroll-area/README.md +233 -5
  141. package/searchable-multiselect/README.md +323 -2
  142. package/select/README.md +437 -2
  143. package/separator/README.md +222 -2
  144. package/sheet/README.md +311 -2
  145. package/sidebar/README.md +457 -5
  146. package/skeleton/README.md +217 -5
  147. package/slider/README.md +273 -5
  148. package/slider/fesm2022/slider.mjs +3 -3
  149. package/sonner/README.md +346 -2
  150. package/spinner/README.md +284 -2
  151. package/switch/README.md +310 -2
  152. package/switch/fesm2022/switch.mjs +7 -5
  153. package/switch/types/switch.d.ts +2 -1
  154. package/table/README.md +423 -5
  155. package/tabs/README.md +411 -2
  156. package/tabs/fesm2022/tabs.mjs +2 -2
  157. package/textarea/README.md +282 -5
  158. package/toggle/README.md +270 -5
  159. package/toggle-group/README.md +340 -5
  160. package/tooltip/README.md +269 -2
  161. package/typography/README.md +271 -5
  162. package/utils/README.md +303 -2
package/select/README.md CHANGED
@@ -1,3 +1,438 @@
1
- # Select Subpath
1
+ # Select (`@egose/shadcn-theme-ng/select`)
2
2
 
3
- This project ships only as `@egose/shadcn-theme-ng/select` or `@egose/shadcn-theme-ng-tw/select`. See the [package README](../../README.md) for installation, compatibility, Tailwind variant, test, and release guidance. Do not publish this project directory independently.
3
+ A shadcn/ui-style **Select** dropdown — button trigger, floating panel, grouped options with check-mark selection. This is the Angular equivalent of shadcn/ui `Select` (`SelectTrigger`, `SelectContent`, `SelectItem`, …).
4
+
5
+ Behavior comes from **spartan-ng/brain** (`BrnSelect` / `BrnSelectMultiple`, `BrnSelectContent`, `BrnSelectItem`, …, plus `BrnPopover` for positioning): popover open/close, keyboard navigation, type-ahead, single- and multi-value models. This package adds the shadcn structure and styling, including the check icon (`lucideCheck` via `@ng-icons`), scroll buttons, and the `sm`/`default` trigger sizes.
6
+
7
+ > **Ships as:** `@egose/shadcn-theme-ng/select` and `@egose/shadcn-theme-ng-tw/select` (the `tw:`-prefixed Tailwind variant — same API, class strings prefixed with `tw:`).
8
+ > See the [package README](../../README.md) for installation, peer dependencies, and Tailwind setup. Do not publish this project directory independently.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ # Plain Tailwind (no prefix)
14
+ npm install @egose/shadcn-theme-ng
15
+
16
+ # Or the tw:-prefixed variant
17
+ npm install @egose/shadcn-theme-ng-tw
18
+ ```
19
+
20
+ ```ts
21
+ import { HlmSelectImports } from '@egose/shadcn-theme-ng/select';
22
+ // tw variant:
23
+ // import { HlmSelectImports } from '@egose/shadcn-theme-ng-tw/select';
24
+ ```
25
+
26
+ Peer dependencies (see [package README](../../README.md) for versions): `@angular/core`, `@angular/common`, `@spartan-ng/brain`. Icons come from `@ng-icons` (installed transitively).
27
+
28
+ ## Imports
29
+
30
+ Real exported symbols (from `src/public-api.ts`):
31
+
32
+ | Symbol | Kind | Selector |
33
+ | ------------------------ | ------------- | ------------------------------------------------------------ |
34
+ | `HlmSelect` | Directive | `[hlmSelect],hlm-select` (single value) |
35
+ | `HlmSelectMultiple` | Directive | `[hlmSelectMultiple],hlm-select-multiple` (multi value) |
36
+ | `HlmSelectTrigger` | Component | `hlm-select-trigger` |
37
+ | `HlmSelectContent` | Component | `hlm-select-content` |
38
+ | `HlmSelectItem` | Component | `hlm-select-item` |
39
+ | `HlmSelectGroup` | Directive | `[hlmSelectGroup],hlm-select-group` |
40
+ | `HlmSelectLabel` | Directive | `[hlmSelectLabel],hlm-select-label` |
41
+ | `HlmSelectValue` | Directive | `[hlmSelectValue],hlm-select-value` |
42
+ | `HlmSelectValues` | Directive | `[hlmSelectValues]` (multi) |
43
+ | `HlmSelectValuesContent` | Directive | `[hlmSelectValuesContent],hlm-select-values-content` (multi) |
44
+ | `HlmSelectPlaceholder` | Directive | `[hlmSelectPlaceholder],hlm-select-placeholder` |
45
+ | `HlmSelectSeparator` | Directive | `[hlmSelectSeparator],hlm-select-separator` |
46
+ | `HlmSelectScrollUp` | Component | `hlm-select-scroll-up` |
47
+ | `HlmSelectScrollDown` | Component | `hlm-select-scroll-down` |
48
+ | `HlmSelectPortal` | Directive | `[hlmSelectPortal]` |
49
+ | `HlmSelectValueTemplate` | Directive | `[hlmSelectValueTemplate]` |
50
+ | `HlmSelectImports` | `const` array | All of the above, for standalone `imports` |
51
+ | `HlmSelectModule` | `NgModule` | NgModule wrapper re-exporting all of the above |
52
+
53
+ Standalone usage:
54
+
55
+ ```ts
56
+ import { Component } from '@angular/core';
57
+ import { HlmSelectImports } from '@egose/shadcn-theme-ng/select';
58
+
59
+ @Component({
60
+ selector: 'app-demo',
61
+ standalone: true,
62
+ imports: [HlmSelectImports],
63
+ template: `...`,
64
+ })
65
+ export class DemoComponent {}
66
+ ```
67
+
68
+ NgModule usage:
69
+
70
+ ```ts
71
+ import { NgModule } from '@angular/core';
72
+ import { HlmSelectModule } from '@egose/shadcn-theme-ng/select';
73
+
74
+ @NgModule({ imports: [HlmSelectModule] })
75
+ export class DemoModule {}
76
+ ```
77
+
78
+ ## Anatomy / Structure
79
+
80
+ Single select:
81
+
82
+ ```html
83
+ <div hlmSelect [(value)]="fruit">
84
+ <hlm-select-trigger>
85
+ <hlm-select-value placeholder="Pick a fruit" />
86
+ </hlm-select-trigger>
87
+
88
+ <hlm-select-content *hlmSelectPortal>
89
+ <div hlmSelectGroup>
90
+ <div hlmSelectLabel>Fruits</div>
91
+ <hlm-select-item value="apple">Apple</hlm-select-item>
92
+ <hlm-select-item value="banana">Banana</hlm-select-item>
93
+ <hlm-select-item value="blueberry" disabled>Blueberry</hlm-select-item>
94
+ </div>
95
+
96
+ <hlm-select-separator />
97
+
98
+ <hlm-select-item value="other">Other</hlm-select-item>
99
+ </hlm-select-content>
100
+ </div>
101
+ ```
102
+
103
+ Multi select (same skeleton, `hlmSelectMultiple` + `hlmSelectValues`):
104
+
105
+ ```html
106
+ <div hlmSelectMultiple [(value)]="toppings">
107
+ <hlm-select-trigger>
108
+ <span hlmSelectValues>
109
+ <span hlmSelectValuesContent>
110
+ <span hlmSelectValue placeholder="Pick toppings" />
111
+ </span>
112
+ </span>
113
+ </hlm-select-trigger>
114
+
115
+ <hlm-select-content *hlmSelectPortal>
116
+ <hlm-select-item value="cheese">Cheese</hlm-select-item>
117
+ <hlm-select-item value="bacon">Bacon</hlm-select-item>
118
+ </hlm-select-content>
119
+ </div>
120
+ ```
121
+
122
+ Selector/slot summary:
123
+
124
+ | Selector | `data-slot` | Notes |
125
+ | ---------------------------------------------------- | ---------------------------------- | ------------------------------------------------------------- |
126
+ | `[hlmSelect],hlm-select` | `select` | Provides popover config (`align: start`, `sideOffset: 6`) |
127
+ | `[hlmSelectMultiple],hlm-select-multiple` | `select` | Same popover config, multi-value model |
128
+ | `hlm-select-trigger` | `select-trigger` (on inner button) | Renders its own `<button brnSelectTrigger>` + chevron icon |
129
+ | `hlm-select-content` | — | Floating panel; optional `showScroll` scroll buttons |
130
+ | `hlm-select-item` | `select-item` | Option row; check icon appears when active |
131
+ | `[hlmSelectGroup],hlm-select-group` | `select-group` | Option grouping wrapper |
132
+ | `[hlmSelectLabel],hlm-select-label` | `select-label` | Group heading |
133
+ | `[hlmSelectValue],hlm-select-value` | `select-value` (when visible) | Current-value display (single) |
134
+ | `[hlmSelectValues]` | — | Current-values host (multi) |
135
+ | `[hlmSelectValuesContent],hlm-select-values-content` | — | Layout row for multi values |
136
+ | `[hlmSelectPlaceholder],hlm-select-placeholder` | `select-placeholder` | Shown when nothing is selected |
137
+ | `[hlmSelectSeparator],hlm-select-separator` | `select-separator` | Divider line (has `orientation` input) |
138
+ | `hlm-select-scroll-up` / `hlm-select-scroll-down` | — | Chevron buttons (auto-rendered by content when `showScroll`) |
139
+ | `[hlmSelectPortal]` | — | Binds the content to the popover overlay (`*hlmSelectPortal`) |
140
+ | `[hlmSelectValueTemplate]` | — | Custom value template marker |
141
+
142
+ ## API reference
143
+
144
+ ### HlmSelect / HlmSelectMultiple (directives)
145
+
146
+ Thin wrappers. Forwarded `BrnSelect` / `BrnSelectMultiple` bindings:
147
+
148
+ | Binding | Kind | Description |
149
+ | -------------------- | ------ | ------------------------------------------------------------ |
150
+ | `disabled` | input | Disables the select |
151
+ | `value` | input | Selected value (single) / values (multiple); use `[(value)]` |
152
+ | `isItemEqualToValue` | input | Custom equality fn for object values |
153
+ | `itemToString` | input | Custom label fn for object values |
154
+ | `valueChange` | output | Emits on selection change |
155
+
156
+ Forwarded `BrnPopover` bindings (positioning of the floating panel):
157
+
158
+ | Binding | Kind | Description |
159
+ | ----------------------------- | ------ | ---------------------------------------------------- |
160
+ | `align` | input | Overlay alignment (default from provider: `'start'`) |
161
+ | `sideOffset` | input | Offset px (default from provider: `6`) |
162
+ | `closeOnOutsidePointerEvents` | input | Close on outside pointer down |
163
+ | `state` | input | Controlled open state |
164
+ | `offsetX` | input | Extra X offset |
165
+ | `stateChanged` | output | Emits on open-state change |
166
+ | `closed` | output | Emits when the panel closes |
167
+
168
+ ### HlmSelectTrigger (component, `hlm-select-trigger`)
169
+
170
+ Own inputs (it renders an inner `<button brnSelectTrigger brnFieldControlDescribedBy>`):
171
+
172
+ | Input | Type | Default | Description |
173
+ | --------------------- | ------------------- | ----------------------------- | -------------------------------------------- |
174
+ | `buttonId` | `string` | auto (`hlm-select-trigger-N`) | `id` of the inner button |
175
+ | `ariaDescribedby` | `string \| null` | `null` | `aria-describedby` for the inner button |
176
+ | `wrapperDisabled` | `boolean` | `false` | Renders `disabled` attr on the inner button |
177
+ | `size` | `'default' \| 'sm'` | `'default'` | Height variant (`data-size`; `h-9` vs `h-8`) |
178
+ | `forceInvalid` | `boolean` | `false` | Forces the invalid ring styling |
179
+ | `class` (`userClass`) | `ClassValue` | `''` | Extra classes |
180
+
181
+ Invalid styling keys off `data-[matches-spartan-invalid=true]` (from the form control state) unless `forceInvalid` is set.
182
+
183
+ ### HlmSelectContent (component, `hlm-select-content`)
184
+
185
+ | Input | Type | Default | Description |
186
+ | ------------ | --------- | ------- | -------------------------------------------------------------------------------- |
187
+ | `showScroll` | `boolean` | `false` | Render `hlm-select-scroll-up/down` chevrons around the listbox (boolean-coerced) |
188
+
189
+ Panel width tracks the trigger (`w-(--brn-select-width)`), max height `tw:max-h-72`, with open/close animations.
190
+
191
+ ### HlmSelectItem (component, `hlm-select-item`)
192
+
193
+ Forwarded `BrnSelectItem` inputs: `id`, `disabled`, `value`. Shows a `lucideCheck` icon at the end when its `active` signal is true. `data-disabled` options are non-interactive (`pointer-events-none`, dimmed).
194
+
195
+ ### Remaining pieces
196
+
197
+ | Class | Forwarded inputs | Notes |
198
+ | ------------------------ | -------------------------------------------- | ----------------------------------------------- |
199
+ | `HlmSelectGroup` | — (plain `BrnSelectGroup`) | Padding wrapper |
200
+ | `HlmSelectLabel` | `id` (via `BrnSelectLabel`) | Muted small-caps-ish heading |
201
+ | `HlmSelectValue` | `placeholder` (via `BrnSelectValue`) | Hides itself (`data-hidden`) when a value shows |
202
+ | `HlmSelectValues` | — (plain `BrnSelectValues`) | Multi-value host |
203
+ | `HlmSelectValuesContent` | — | Flex row with gap |
204
+ | `HlmSelectPlaceholder` | — (plain `BrnSelectPlaceholder`) | Hides when a value shows (`data-hidden`) |
205
+ | `HlmSelectSeparator` | `orientation` (via `BrnSelectSeparator`) | 1px divider |
206
+ | `HlmSelectScrollUp/Down` | — | Sticky chevron affordances |
207
+ | `HlmSelectPortal` | `context`, `class` (via `BrnPopoverContent`) | Structural use: `*hlmSelectPortal="let ctx"` |
208
+ | `HlmSelectValueTemplate` | — (plain `BrnSelectValueTemplate`) | Marker for custom value rendering |
209
+
210
+ ## Examples
211
+
212
+ ### 1. Basic single select
213
+
214
+ ```ts
215
+ import { Component, signal } from '@angular/core';
216
+ import { HlmSelectImports } from '@egose/shadcn-theme-ng/select';
217
+
218
+ @Component({
219
+ selector: 'app-basic-select',
220
+ standalone: true,
221
+ imports: [HlmSelectImports],
222
+ template: `
223
+ <div hlmSelect [(value)]="fruit">
224
+ <hlm-select-trigger>
225
+ <hlm-select-value placeholder="Pick a fruit" />
226
+ </hlm-select-trigger>
227
+ <hlm-select-content *hlmSelectPortal>
228
+ <hlm-select-item value="apple">Apple</hlm-select-item>
229
+ <hlm-select-item value="banana">Banana</hlm-select-item>
230
+ <hlm-select-item value="orange">Orange</hlm-select-item>
231
+ </hlm-select-content>
232
+ </div>
233
+ <p>Selected: {{ fruit() ?? 'none' }}</p>
234
+ `,
235
+ })
236
+ export class BasicSelectComponent {
237
+ readonly fruit = signal<string | null>(null);
238
+ }
239
+ ```
240
+
241
+ ### 2. Grouped options with labels, separator, disabled item, scroll buttons
242
+
243
+ ```html
244
+ <div hlmSelect [(value)]="city">
245
+ <hlm-select-trigger>
246
+ <hlm-select-value placeholder="Pick a city" />
247
+ </hlm-select-trigger>
248
+
249
+ <hlm-select-content *hlmSelectPortal showScroll>
250
+ <div hlmSelectGroup>
251
+ <div hlmSelectLabel>Germany</div>
252
+ <hlm-select-item value="berlin">Berlin</hlm-select-item>
253
+ <hlm-select-item value="munich">Munich</hlm-select-item>
254
+ </div>
255
+
256
+ <hlm-select-separator />
257
+
258
+ <div hlmSelectGroup>
259
+ <div hlmSelectLabel>France</div>
260
+ <hlm-select-item value="paris">Paris</hlm-select-item>
261
+ <hlm-select-item value="lyon" disabled>Lyon (unavailable)</hlm-select-item>
262
+ </div>
263
+ </hlm-select-content>
264
+ </div>
265
+ ```
266
+
267
+ ### 3. Reactive forms + small trigger + forced invalid demo
268
+
269
+ `BrnSelect` is a `ControlValueAccessor`: bind `formControlName` on the `hlmSelect` host.
270
+
271
+ ```ts
272
+ import { Component } from '@angular/core';
273
+ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
274
+ import { HlmSelectImports } from '@egose/shadcn-theme-ng/select';
275
+
276
+ @Component({
277
+ selector: 'app-form-select',
278
+ standalone: true,
279
+ imports: [HlmSelectImports, ReactiveFormsModule],
280
+ template: `
281
+ <form [formGroup]="form" (ngSubmit)="submit()">
282
+ <div hlmSelect formControlName="country">
283
+ <hlm-select-trigger size="sm" ariaDescribedby="country-hint">
284
+ <hlm-select-value placeholder="Country" />
285
+ </hlm-select-trigger>
286
+ <hlm-select-content *hlmSelectPortal>
287
+ <hlm-select-item value="de">Germany</hlm-select-item>
288
+ <hlm-select-item value="fr">France</hlm-select-item>
289
+ <hlm-select-item value="es">Spain</hlm-select-item>
290
+ </hlm-select-content>
291
+ </div>
292
+ <p id="country-hint" class="tw:text-muted-foreground tw:text-xs">Used for shipping.</p>
293
+ <button type="submit">Save</button>
294
+ </form>
295
+ `,
296
+ })
297
+ export class FormSelectComponent {
298
+ readonly form = new FormGroup({
299
+ country: new FormControl<string | null>(null, Validators.required),
300
+ });
301
+
302
+ submit(): void {
303
+ this.form.markAllAsTouched();
304
+ }
305
+ }
306
+ ```
307
+
308
+ ### 4. Multi select with placeholder
309
+
310
+ ```ts
311
+ import { Component, signal } from '@angular/core';
312
+ import { HlmSelectImports } from '@egose/shadcn-theme-ng/select';
313
+
314
+ @Component({
315
+ selector: 'app-multi-select',
316
+ standalone: true,
317
+ imports: [HlmSelectImports],
318
+ template: `
319
+ <div hlmSelectMultiple [(value)]="toppings">
320
+ <hlm-select-trigger>
321
+ <span hlmSelectValues>
322
+ <span hlmSelectValuesContent>
323
+ <span hlmSelectValue placeholder="Pick toppings" />
324
+ </span>
325
+ </span>
326
+ </hlm-select-trigger>
327
+ <hlm-select-content *hlmSelectPortal>
328
+ <hlm-select-item value="cheese">Extra cheese</hlm-select-item>
329
+ <hlm-select-item value="bacon">Bacon</hlm-select-item>
330
+ <hlm-select-item value="mushrooms">Mushrooms</hlm-select-item>
331
+ </hlm-select-content>
332
+ </div>
333
+ <p>{{ toppings().length }} selected</p>
334
+ `,
335
+ })
336
+ export class MultiSelectComponent {
337
+ readonly toppings = signal<string[]>([]);
338
+ }
339
+ ```
340
+
341
+ ### 5. Object values with custom equality/labels + disabled select
342
+
343
+ ```ts
344
+ import { Component, signal } from '@angular/core';
345
+ import { HlmSelectImports } from '@egose/shadcn-theme-ng/select';
346
+
347
+ interface User {
348
+ id: string;
349
+ name: string;
350
+ }
351
+
352
+ @Component({
353
+ selector: 'app-object-select',
354
+ standalone: true,
355
+ imports: [HlmSelectImports],
356
+ template: `
357
+ <div hlmSelect [(value)]="assignee" [isItemEqualToValue]="byId" [itemToString]="toName" [disabled]="locked()">
358
+ <hlm-select-trigger>
359
+ <hlm-select-value placeholder="Assign to…" />
360
+ </hlm-select-trigger>
361
+ <hlm-select-content *hlmSelectPortal>
362
+ @for (user of users; track user.id) {
363
+ <hlm-select-item [value]="user">{{ user.name }}</hlm-select-item>
364
+ }
365
+ </hlm-select-content>
366
+ </div>
367
+ `,
368
+ })
369
+ export class ObjectSelectComponent {
370
+ readonly users: User[] = [
371
+ { id: 'u1', name: 'Ada' },
372
+ { id: 'u2', name: 'Grace' },
373
+ ];
374
+ readonly assignee = signal<User | null>(null);
375
+ readonly locked = signal(false);
376
+
377
+ readonly byId = (a: User | null, b: User | null) => a?.id === b?.id;
378
+ readonly toName = (u: User | null) => u?.name ?? '';
379
+ }
380
+ ```
381
+
382
+ ### 6. Controlled popover state + custom value template
383
+
384
+ ```ts
385
+ import { Component, signal } from '@angular/core';
386
+ import { HlmSelectImports } from '@egose/shadcn-theme-ng/select';
387
+
388
+ @Component({
389
+ selector: 'app-advanced-select',
390
+ standalone: true,
391
+ imports: [HlmSelectImports],
392
+ template: `
393
+ <button type="button" (click)="open.set(!open())">Toggle from outside</button>
394
+
395
+ <div
396
+ hlmSelect
397
+ [(value)]="plan"
398
+ [state]="open() ? 'open' : 'closed'"
399
+ (stateChanged)="open.set($event === 'open')"
400
+ (closed)="onClosed()"
401
+ >
402
+ <hlm-select-trigger>
403
+ <span hlmSelectValueTemplate>⭐ {{ plan() ?? 'Pick a plan' }}</span>
404
+ </hlm-select-trigger>
405
+ <hlm-select-content *hlmSelectPortal="let ctx">
406
+ <hlm-select-item value="hobby">Hobby</hlm-select-item>
407
+ <hlm-select-item value="pro">Pro</hlm-select-item>
408
+ </hlm-select-content>
409
+ </div>
410
+ `,
411
+ })
412
+ export class AdvancedSelectComponent {
413
+ readonly plan = signal<string | null>(null);
414
+ readonly open = signal(false);
415
+
416
+ onClosed(): void {
417
+ console.log('panel closed');
418
+ }
419
+ }
420
+ ```
421
+
422
+ ## Accessibility notes
423
+
424
+ - The trigger is a native `<button>` with popover/listbox semantics from brain: it announces expanded state and the selected value; keep the `hlm-select-value`/`placeholder` inside the trigger so there is always an accessible name.
425
+ - `hlm-select-content` renders `role="listbox"`; options are keyboard-navigable with type-ahead — do not intercept arrow keys inside the panel.
426
+ - Disabled items (`disabled` on `hlm-select-item`) are skipped by the key manager and dimmed; the whole select can be disabled via `disabled` on the host.
427
+ - Invalid form state surfaces through `data-[matches-spartan-invalid]` on the trigger (ring + border) — pair with a visible error message and `ariaDescribedby` pointing at it.
428
+
429
+ ## Theming / CSS variables
430
+
431
+ Class-driven (popover tokens `--popover`, trigger/input tokens, accent highlight). The panel width follows `--brn-select-width` (measured from the trigger). Override density via `size="sm"` on the trigger or the `class` input on any piece.
432
+
433
+ ## Related subpaths
434
+
435
+ - `@egose/shadcn-theme-ng/searchable-multiselect` — chip-style multi pick with popover checkboxes
436
+ - `@egose/shadcn-theme-ng/popover` — the underlying floating-panel primitive
437
+ - `@egose/shadcn-theme-ng/form-select` — form-field wrapper (label/description/error) for selects
438
+ - `@egose/shadcn-theme-ng/native-select` — lightweight native `<select>` alternative
@@ -1,3 +1,223 @@
1
- # Separator Subpath
1
+ # Separator (`@egose/shadcn-theme-ng/separator`)
2
2
 
3
- This project ships only as `@egose/shadcn-theme-ng/separator` or `@egose/shadcn-theme-ng-tw/separator`. See the [package README](../../README.md) for installation, compatibility, Tailwind variant, test, and release guidance. Do not publish this project directory independently.
3
+ A shadcn/ui-style **Separator** — a thin horizontal or vertical divider line between content. This is the Angular equivalent of shadcn/ui `Separator`.
4
+
5
+ It is a thin directive wrapper over `BrnSeparator` from **spartan-ng/brain** (which supplies the `separator` role semantics, orientation handling, and the `decorative` flag). Styling is a 1px `bg-border` line that stretches horizontally or vertically based on `data-[orientation]`-style state attributes.
6
+
7
+ > **Ships as:** `@egose/shadcn-theme-ng/separator` and `@egose/shadcn-theme-ng-tw/separator` (the `tw:`-prefixed Tailwind variant — same API, class strings prefixed with `tw:`).
8
+ > See the [package README](../../README.md) for installation, peer dependencies, and Tailwind setup. Do not publish this project directory independently.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ # Plain Tailwind (no prefix)
14
+ npm install @egose/shadcn-theme-ng
15
+
16
+ # Or the tw:-prefixed variant
17
+ npm install @egose/shadcn-theme-ng-tw
18
+ ```
19
+
20
+ ```ts
21
+ import { HlmSeparatorImports } from '@egose/shadcn-theme-ng/separator';
22
+ // tw variant:
23
+ // import { HlmSeparatorImports } from '@egose/shadcn-theme-ng-tw/separator';
24
+ ```
25
+
26
+ Peer dependencies (see [package README](../../README.md) for versions): `@angular/core`, `@angular/common`.
27
+
28
+ ## Imports
29
+
30
+ Real exported symbols (from `src/public-api.ts` plus `lib/hlm-separator.ts`):
31
+
32
+ | Symbol | Kind | Description |
33
+ | --------------------- | -------------- | --------------------------------------------------------------------------- |
34
+ | `HlmSeparator` | Directive | The divider; selector `[hlmSeparator],hlm-separator` |
35
+ | `hlmSeparatorClass` | `const string` | The raw class string the directive applies — reuse it for custom separators |
36
+ | `HlmSeparatorImports` | `const` array | `[HlmSeparator]` standalone imports |
37
+ | `HlmSeparatorModule` | `NgModule` | NgModule wrapper re-exporting `HlmSeparator` |
38
+
39
+ Standalone usage:
40
+
41
+ ```ts
42
+ import { Component } from '@angular/core';
43
+ import { HlmSeparatorImports } from '@egose/shadcn-theme-ng/separator';
44
+
45
+ @Component({
46
+ selector: 'app-demo',
47
+ standalone: true,
48
+ imports: [HlmSeparatorImports],
49
+ template: `
50
+ <p>Above</p>
51
+ <hlm-separator />
52
+ <p>Below</p>
53
+ `,
54
+ })
55
+ export class DemoComponent {}
56
+ ```
57
+
58
+ NgModule usage:
59
+
60
+ ```ts
61
+ import { NgModule } from '@angular/core';
62
+ import { HlmSeparatorModule } from '@egose/shadcn-theme-ng/separator';
63
+
64
+ @NgModule({ imports: [HlmSeparatorModule] })
65
+ export class DemoModule {}
66
+ ```
67
+
68
+ ## Anatomy / Structure
69
+
70
+ ```html
71
+ <div>
72
+ <h4>Account</h4>
73
+ <p class="tw:text-muted-foreground tw:text-sm">Manage your settings.</p>
74
+ </div>
75
+
76
+ <hlm-separator class="tw:my-4" />
77
+
78
+ <div>
79
+ <h4>Notifications</h4>
80
+ </div>
81
+ ```
82
+
83
+ Real selectors:
84
+
85
+ | Selector | Class | Notes |
86
+ | ------------------------------ | -------------- | -------------------------------------------------------------------- |
87
+ | `[hlmSeparator],hlm-separator` | `HlmSeparator` | `data-slot="separator"`; inline-flex 1px line, horizontal by default |
88
+
89
+ ## API reference
90
+
91
+ ### HlmSeparator (directive)
92
+
93
+ Thin wrapper — no own inputs. Forwarded `BrnSeparator` bindings:
94
+
95
+ | Binding | Kind | Description |
96
+ | ------------- | ----- | ---------------------------------------------------------------------------------------- |
97
+ | `orientation` | input | `'horizontal' \| 'vertical'` — line direction (default horizontal) |
98
+ | `decorative` | input | When true, the separator is presentational (`aria-hidden`) instead of `role="separator"` |
99
+
100
+ The applied classes: `inline-flex shrink-0 bg-border`, full-width 1px height when horizontal, full-stretch 1px width when vertical. Add spacing (e.g. `tw:my-4` / `tw:mx-2`) via the `class` attribute — the directive has no `class` input of its own, it merges host classes.
101
+
102
+ ## Examples
103
+
104
+ ### 1. Basic horizontal divider
105
+
106
+ ```ts
107
+ import { Component } from '@angular/core';
108
+ import { HlmSeparatorImports } from '@egose/shadcn-theme-ng/separator';
109
+
110
+ @Component({
111
+ selector: 'app-basic-separator',
112
+ standalone: true,
113
+ imports: [HlmSeparatorImports],
114
+ template: `
115
+ <div class="tw:space-y-1">
116
+ <h4 class="tw:font-medium">shadcn-theme</h4>
117
+ <p class="tw:text-muted-foreground tw:text-sm">An Angular shadcn/ui port.</p>
118
+ </div>
119
+ <hlm-separator class="tw:my-4" />
120
+ <div class="tw:flex tw:h-5 tw:items-center tw:gap-4 tw:text-sm">
121
+ <span>Docs</span>
122
+ <hlm-separator orientation="vertical" />
123
+ <span>API</span>
124
+ </div>
125
+ `,
126
+ })
127
+ export class BasicSeparatorComponent {}
128
+ ```
129
+
130
+ ### 2. Attribute form on a plain div
131
+
132
+ ```html
133
+ <section>
134
+ <h2>Profile</h2>
135
+ <div hlmSeparator class="tw:my-4"></div>
136
+ <h2>Security</h2>
137
+ </section>
138
+ ```
139
+
140
+ ### 3. Vertical separator in a toolbar / breadcrumb row
141
+
142
+ ```ts
143
+ import { Component } from '@angular/core';
144
+ import { HlmSeparatorImports } from '@egose/shadcn-theme-ng/separator';
145
+
146
+ @Component({
147
+ selector: 'app-toolbar-separator',
148
+ standalone: true,
149
+ imports: [HlmSeparatorImports],
150
+ template: `
151
+ <div class="tw:flex tw:h-8 tw:items-center tw:gap-2 tw:rounded-md tw:border tw:px-2">
152
+ <button type="button">Bold</button>
153
+ <hlm-separator orientation="vertical" class="tw:h-4" />
154
+ <button type="button">Italic</button>
155
+ <hlm-separator orientation="vertical" class="tw:h-4" />
156
+ <button type="button">Underline</button>
157
+ </div>
158
+ `,
159
+ })
160
+ export class ToolbarSeparatorComponent {}
161
+ ```
162
+
163
+ Constrain the height (`tw:h-4`) so the vertical line doesn't stretch the whole toolbar.
164
+
165
+ ### 4. Decorative vs semantic
166
+
167
+ ```html
168
+ <!-- Purely visual: hidden from assistive tech -->
169
+ <hlm-separator decorative class="tw:my-6" />
170
+
171
+ <!-- Meaningful section break: exposed as role="separator" -->
172
+ <hlm-separator orientation="horizontal" class="tw:my-6" />
173
+ ```
174
+
175
+ ### 5. Sidebar / menu dividers
176
+
177
+ ```html
178
+ <nav class="tw:flex tw:flex-col tw:gap-1">
179
+ <a href="/dashboard">Dashboard</a>
180
+ <a href="/settings">Settings</a>
181
+ <hlm-separator class="tw:my-2" />
182
+ <a href="/logout">Log out</a>
183
+ </nav>
184
+ ```
185
+
186
+ (For the app-sidebar layout, `hlm-sidebar-separator` from the `sidebar` subpath already wraps this directive with sidebar tokens — prefer it there.)
187
+
188
+ ### 6. Reusing `hlmSeparatorClass` in a custom component
189
+
190
+ ```ts
191
+ import { Component } from '@angular/core';
192
+ import { hlmSeparatorClass } from '@egose/shadcn-theme-ng/separator';
193
+ import { hlm } from '@egose/shadcn-theme-ng/utils';
194
+
195
+ @Component({
196
+ selector: 'app-fancy-divider',
197
+ standalone: true,
198
+ template: `<div role="separator" [class]="dividerClass">✦</div>`,
199
+ })
200
+ export class FancyDividerComponent {
201
+ protected readonly dividerClass = hlm(
202
+ hlmSeparatorClass,
203
+ 'tw:my-6 tw:items-center tw:justify-center tw:gap-2 tw:bg-transparent',
204
+ );
205
+ }
206
+ ```
207
+
208
+ ## Accessibility notes
209
+
210
+ - By default the separator exposes `role="separator"` with the correct orientation — use it for real section breaks.
211
+ - Pass `decorative` when the line is purely visual (repeated card dividers, toolbar ticks) so screen readers skip it.
212
+ - Never put content _inside_ the separator and never make it focusable — it is a boundary, not a control.
213
+
214
+ ## Theming / CSS variables
215
+
216
+ One token: `bg-border` for the line color (follows light/dark theme automatically). Thickness/spacing come from utilities you add via `class`.
217
+
218
+ ## Related subpaths
219
+
220
+ - `@egose/shadcn-theme-ng/sidebar` — `hlm-sidebar-separator` (sidebar-token variant of this divider)
221
+ - `@egose/shadcn-theme-ng/select` — `hlm-select-separator` (dropdown-section divider)
222
+ - `@egose/shadcn-theme-ng/card` — sectioned containers where separators commonly appear
223
+ - `@egose/shadcn-theme-ng/breadcrumb` — inline navigation that often uses vertical separators