@ojiepermana/angular-component 22.0.27
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 +82 -0
- package/accordion/README.md +193 -0
- package/alert/README.md +180 -0
- package/alert-dialog/README.md +239 -0
- package/aspect-ratio/README.md +112 -0
- package/avatar/README.md +176 -0
- package/badge/README.md +133 -0
- package/breadcrumb/README.md +216 -0
- package/button/README.md +139 -0
- package/button-group/README.md +208 -0
- package/calendar/README.md +135 -0
- package/card/README.md +220 -0
- package/carousel/README.md +276 -0
- package/checkbox/README.md +149 -0
- package/collapsible/README.md +195 -0
- package/combobox/README.md +198 -0
- package/command/README.md +275 -0
- package/composer/README.md +235 -0
- package/context-menu/README.md +267 -0
- package/date-picker/README.md +179 -0
- package/dialog/README.md +235 -0
- package/drawer/README.md +145 -0
- package/dropdown-menu/README.md +311 -0
- package/editor/README.md +136 -0
- package/empty/README.md +183 -0
- package/fesm2022/ojiepermana-angular-component-accordion.mjs +186 -0
- package/fesm2022/ojiepermana-angular-component-accordion.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-alert-dialog.mjs +276 -0
- package/fesm2022/ojiepermana-angular-component-alert-dialog.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-alert.mjs +99 -0
- package/fesm2022/ojiepermana-angular-component-alert.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-aspect-ratio.mjs +37 -0
- package/fesm2022/ojiepermana-angular-component-aspect-ratio.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-avatar.mjs +139 -0
- package/fesm2022/ojiepermana-angular-component-avatar.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-badge.mjs +50 -0
- package/fesm2022/ojiepermana-angular-component-badge.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-breadcrumb.mjs +200 -0
- package/fesm2022/ojiepermana-angular-component-breadcrumb.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-button-group.mjs +103 -0
- package/fesm2022/ojiepermana-angular-component-button-group.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-button.mjs +68 -0
- package/fesm2022/ojiepermana-angular-component-button.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-calendar.mjs +103 -0
- package/fesm2022/ojiepermana-angular-component-calendar.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-card.mjs +152 -0
- package/fesm2022/ojiepermana-angular-component-card.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-carousel.mjs +334 -0
- package/fesm2022/ojiepermana-angular-component-carousel.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-checkbox.mjs +165 -0
- package/fesm2022/ojiepermana-angular-component-checkbox.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-collapsible.mjs +121 -0
- package/fesm2022/ojiepermana-angular-component-collapsible.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-combobox.mjs +274 -0
- package/fesm2022/ojiepermana-angular-component-combobox.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-command.mjs +297 -0
- package/fesm2022/ojiepermana-angular-component-command.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-composer.mjs +352 -0
- package/fesm2022/ojiepermana-angular-component-composer.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-context-menu.mjs +108 -0
- package/fesm2022/ojiepermana-angular-component-context-menu.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-date-picker.mjs +186 -0
- package/fesm2022/ojiepermana-angular-component-date-picker.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-dialog.mjs +283 -0
- package/fesm2022/ojiepermana-angular-component-dialog.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-drawer.mjs +6 -0
- package/fesm2022/ojiepermana-angular-component-drawer.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-dropdown-menu.mjs +494 -0
- package/fesm2022/ojiepermana-angular-component-dropdown-menu.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-editor.mjs +680 -0
- package/fesm2022/ojiepermana-angular-component-editor.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-empty.mjs +145 -0
- package/fesm2022/ojiepermana-angular-component-empty.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-form.mjs +364 -0
- package/fesm2022/ojiepermana-angular-component-form.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-hover-card.mjs +275 -0
- package/fesm2022/ojiepermana-angular-component-hover-card.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-icon.mjs +86 -0
- package/fesm2022/ojiepermana-angular-component-icon.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-input-group.mjs +179 -0
- package/fesm2022/ojiepermana-angular-component-input-group.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-input-otp.mjs +517 -0
- package/fesm2022/ojiepermana-angular-component-input-otp.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-input.mjs +45 -0
- package/fesm2022/ojiepermana-angular-component-input.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-item.mjs +264 -0
- package/fesm2022/ojiepermana-angular-component-item.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-kanban.mjs +314 -0
- package/fesm2022/ojiepermana-angular-component-kanban.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-kbd.mjs +55 -0
- package/fesm2022/ojiepermana-angular-component-kbd.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-label.mjs +33 -0
- package/fesm2022/ojiepermana-angular-component-label.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-menubar.mjs +311 -0
- package/fesm2022/ojiepermana-angular-component-menubar.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-native-select.mjs +67 -0
- package/fesm2022/ojiepermana-angular-component-native-select.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-navigation-menu.mjs +408 -0
- package/fesm2022/ojiepermana-angular-component-navigation-menu.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-pagination.mjs +226 -0
- package/fesm2022/ojiepermana-angular-component-pagination.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-pillbox.mjs +810 -0
- package/fesm2022/ojiepermana-angular-component-pillbox.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-popover.mjs +145 -0
- package/fesm2022/ojiepermana-angular-component-popover.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-progress.mjs +60 -0
- package/fesm2022/ojiepermana-angular-component-progress.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-radio.mjs +173 -0
- package/fesm2022/ojiepermana-angular-component-radio.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-resizable.mjs +478 -0
- package/fesm2022/ojiepermana-angular-component-resizable.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-scroll-area.mjs +54 -0
- package/fesm2022/ojiepermana-angular-component-scroll-area.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-select.mjs +297 -0
- package/fesm2022/ojiepermana-angular-component-select.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-separator.mjs +37 -0
- package/fesm2022/ojiepermana-angular-component-separator.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-sheet.mjs +297 -0
- package/fesm2022/ojiepermana-angular-component-sheet.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-skeleton.mjs +31 -0
- package/fesm2022/ojiepermana-angular-component-skeleton.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-slider.mjs +423 -0
- package/fesm2022/ojiepermana-angular-component-slider.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-spinner.mjs +60 -0
- package/fesm2022/ojiepermana-angular-component-spinner.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-switch.mjs +140 -0
- package/fesm2022/ojiepermana-angular-component-switch.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-table.mjs +155 -0
- package/fesm2022/ojiepermana-angular-component-table.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-tabs.mjs +271 -0
- package/fesm2022/ojiepermana-angular-component-tabs.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-textarea.mjs +39 -0
- package/fesm2022/ojiepermana-angular-component-textarea.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-timeline.mjs +237 -0
- package/fesm2022/ojiepermana-angular-component-timeline.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-toast.mjs +161 -0
- package/fesm2022/ojiepermana-angular-component-toast.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-toggle-group.mjs +289 -0
- package/fesm2022/ojiepermana-angular-component-toggle-group.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-toggle.mjs +82 -0
- package/fesm2022/ojiepermana-angular-component-toggle.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-tooltip.mjs +410 -0
- package/fesm2022/ojiepermana-angular-component-tooltip.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-utils.mjs +81 -0
- package/fesm2022/ojiepermana-angular-component-utils.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component.mjs +11 -0
- package/fesm2022/ojiepermana-angular-component.mjs.map +1 -0
- package/form/README.md +210 -0
- package/hover-card/README.md +142 -0
- package/icon/README.md +25 -0
- package/input/README.md +159 -0
- package/input-group/README.md +237 -0
- package/input-otp/README.md +278 -0
- package/item/README.md +247 -0
- package/kanban/README.md +81 -0
- package/kbd/README.md +139 -0
- package/label/README.md +135 -0
- package/menubar/README.md +269 -0
- package/native-select/README.md +176 -0
- package/navigation-menu/README.md +160 -0
- package/package.json +291 -0
- package/pagination/README.md +144 -0
- package/pillbox/README.md +67 -0
- package/popover/README.md +43 -0
- package/progress/README.md +160 -0
- package/radio/README.md +209 -0
- package/resizable/README.md +168 -0
- package/scroll-area/README.md +143 -0
- package/select/README.md +174 -0
- package/separator/README.md +170 -0
- package/sheet/README.md +183 -0
- package/skeleton/README.md +158 -0
- package/slider/README.md +207 -0
- package/spinner/README.md +160 -0
- package/switch/README.md +166 -0
- package/table/README.md +291 -0
- package/tabs/README.md +214 -0
- package/textarea/README.md +153 -0
- package/timeline/README.md +94 -0
- package/toast/README.md +321 -0
- package/toggle/README.md +131 -0
- package/toggle-group/README.md +206 -0
- package/tooltip/README.md +207 -0
- package/types/ojiepermana-angular-component-accordion.d.ts +51 -0
- package/types/ojiepermana-angular-component-alert-dialog.d.ts +93 -0
- package/types/ojiepermana-angular-component-alert.d.ts +37 -0
- package/types/ojiepermana-angular-component-aspect-ratio.d.ts +12 -0
- package/types/ojiepermana-angular-component-avatar.d.ts +51 -0
- package/types/ojiepermana-angular-component-badge.d.ts +19 -0
- package/types/ojiepermana-angular-component-breadcrumb.d.ts +46 -0
- package/types/ojiepermana-angular-component-button-group.d.ts +26 -0
- package/types/ojiepermana-angular-component-button.d.ts +22 -0
- package/types/ojiepermana-angular-component-calendar.d.ts +39 -0
- package/types/ojiepermana-angular-component-card.d.ts +60 -0
- package/types/ojiepermana-angular-component-carousel.d.ts +86 -0
- package/types/ojiepermana-angular-component-checkbox.d.ts +42 -0
- package/types/ojiepermana-angular-component-collapsible.d.ts +42 -0
- package/types/ojiepermana-angular-component-combobox.d.ts +53 -0
- package/types/ojiepermana-angular-component-command.d.ts +102 -0
- package/types/ojiepermana-angular-component-composer.d.ts +90 -0
- package/types/ojiepermana-angular-component-context-menu.d.ts +36 -0
- package/types/ojiepermana-angular-component-date-picker.d.ts +48 -0
- package/types/ojiepermana-angular-component-dialog.d.ts +87 -0
- package/types/ojiepermana-angular-component-drawer.d.ts +1 -0
- package/types/ojiepermana-angular-component-dropdown-menu.d.ts +140 -0
- package/types/ojiepermana-angular-component-editor.d.ts +126 -0
- package/types/ojiepermana-angular-component-empty.d.ts +50 -0
- package/types/ojiepermana-angular-component-form.d.ts +140 -0
- package/types/ojiepermana-angular-component-hover-card.d.ts +75 -0
- package/types/ojiepermana-angular-component-icon.d.ts +31 -0
- package/types/ojiepermana-angular-component-input-group.d.ts +51 -0
- package/types/ojiepermana-angular-component-input-otp.d.ts +142 -0
- package/types/ojiepermana-angular-component-input.d.ts +16 -0
- package/types/ojiepermana-angular-component-item.d.ts +88 -0
- package/types/ojiepermana-angular-component-kanban.d.ts +70 -0
- package/types/ojiepermana-angular-component-kbd.d.ts +16 -0
- package/types/ojiepermana-angular-component-label.d.ts +11 -0
- package/types/ojiepermana-angular-component-menubar.d.ts +69 -0
- package/types/ojiepermana-angular-component-native-select.d.ts +26 -0
- package/types/ojiepermana-angular-component-navigation-menu.d.ts +98 -0
- package/types/ojiepermana-angular-component-pagination.d.ts +33 -0
- package/types/ojiepermana-angular-component-pillbox.d.ts +156 -0
- package/types/ojiepermana-angular-component-popover.d.ts +50 -0
- package/types/ojiepermana-angular-component-progress.d.ts +17 -0
- package/types/ojiepermana-angular-component-radio.d.ts +57 -0
- package/types/ojiepermana-angular-component-resizable.d.ts +99 -0
- package/types/ojiepermana-angular-component-scroll-area.d.ts +19 -0
- package/types/ojiepermana-angular-component-select.d.ts +56 -0
- package/types/ojiepermana-angular-component-separator.d.ts +14 -0
- package/types/ojiepermana-angular-component-sheet.d.ts +78 -0
- package/types/ojiepermana-angular-component-skeleton.d.ts +10 -0
- package/types/ojiepermana-angular-component-slider.d.ts +74 -0
- package/types/ojiepermana-angular-component-spinner.d.ts +13 -0
- package/types/ojiepermana-angular-component-switch.d.ts +44 -0
- package/types/ojiepermana-angular-component-table.d.ts +52 -0
- package/types/ojiepermana-angular-component-tabs.d.ts +92 -0
- package/types/ojiepermana-angular-component-textarea.d.ts +12 -0
- package/types/ojiepermana-angular-component-timeline.d.ts +63 -0
- package/types/ojiepermana-angular-component-toast.d.ts +51 -0
- package/types/ojiepermana-angular-component-toggle-group.d.ts +89 -0
- package/types/ojiepermana-angular-component-toggle.d.ts +25 -0
- package/types/ojiepermana-angular-component-tooltip.d.ts +101 -0
- package/types/ojiepermana-angular-component-utils.d.ts +30 -0
- package/types/ojiepermana-angular-component.d.ts +2 -0
package/table/README.md
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# Table
|
|
2
|
+
|
|
3
|
+
Responsive native table primitives for invoices, product lists, row actions, and compact data summaries.
|
|
4
|
+
|
|
5
|
+
Use Table when information has real row and column relationships. The Angular primitive keeps shadcn's composition model while preserving semantic `<table>`, `<thead>`, `<tbody>`, `<tfoot>`, `<tr>`, `<th>`, `<td>`, and `<caption>` elements.
|
|
6
|
+
|
|
7
|
+
shadcn's `data-table` guide maps to this entrypoint rather than a separate Angular package surface. Compose Table with the local button, checkbox, input, badge, and dropdown-menu primitives when you need sorting, filtering, row selection, column visibility, row actions, or pagination.
|
|
8
|
+
|
|
9
|
+
## Import
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import {
|
|
13
|
+
TableBodyComponent,
|
|
14
|
+
TableCaptionComponent,
|
|
15
|
+
TableCellComponent,
|
|
16
|
+
TableComponent,
|
|
17
|
+
TableFooterComponent,
|
|
18
|
+
TableHeadComponent,
|
|
19
|
+
TableHeaderComponent,
|
|
20
|
+
TableRowComponent,
|
|
21
|
+
} from '@ojiepermana/angular-component/table';
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Row action menus can compose with the local button and dropdown-menu primitives.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { ButtonComponent } from '@ojiepermana/angular-component/button';
|
|
28
|
+
import {
|
|
29
|
+
MenuContentDirective,
|
|
30
|
+
MenuItemComponent,
|
|
31
|
+
MenuSeparatorComponent,
|
|
32
|
+
MenuSurfaceComponent,
|
|
33
|
+
MenuTriggerDirective,
|
|
34
|
+
} from '@ojiepermana/angular-component/dropdown-menu';
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Composition
|
|
38
|
+
|
|
39
|
+
Apply the attribute-selector parts to the matching semantic table tags.
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
Table
|
|
43
|
+
|-- caption[TableCaption]
|
|
44
|
+
|-- thead[TableHeader]
|
|
45
|
+
| `-- tr[TableRow]
|
|
46
|
+
| |-- th[TableHead]
|
|
47
|
+
| |-- th[TableHead]
|
|
48
|
+
| |-- th[TableHead]
|
|
49
|
+
| `-- th[TableHead]
|
|
50
|
+
|-- tbody[TableBody]
|
|
51
|
+
| `-- tr[TableRow]
|
|
52
|
+
| |-- td[TableCell]
|
|
53
|
+
| |-- td[TableCell]
|
|
54
|
+
| |-- td[TableCell]
|
|
55
|
+
| `-- td[TableCell]
|
|
56
|
+
`-- tfoot[TableFooter]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
Use `Table` as the responsive wrapper. Bind rows with Angular control flow and stable tracking.
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
<table>
|
|
65
|
+
<caption TableCaption>
|
|
66
|
+
A list of your recent invoices.
|
|
67
|
+
</caption>
|
|
68
|
+
<thead TableHeader>
|
|
69
|
+
<tr TableRow>
|
|
70
|
+
<th TableHead class="w-25">Invoice</th>
|
|
71
|
+
<th TableHead>Status</th>
|
|
72
|
+
<th TableHead>Method</th>
|
|
73
|
+
<th TableHead class="text-right">Amount</th>
|
|
74
|
+
</tr>
|
|
75
|
+
</thead>
|
|
76
|
+
<tbody TableBody>
|
|
77
|
+
@for (invoice of invoices; track invoice.invoice) {
|
|
78
|
+
<tr TableRow>
|
|
79
|
+
<td TableCell class="font-medium">{{ invoice.invoice }}</td>
|
|
80
|
+
<td TableCell>{{ invoice.status }}</td>
|
|
81
|
+
<td TableCell>{{ invoice.method }}</td>
|
|
82
|
+
<td TableCell class="text-right">{{ invoice.amount }}</td>
|
|
83
|
+
</tr>
|
|
84
|
+
}
|
|
85
|
+
</tbody>
|
|
86
|
+
</table>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Common patterns
|
|
90
|
+
|
|
91
|
+
### Footer totals
|
|
92
|
+
|
|
93
|
+
Use `tfoot[TableFooter]` for totals and summaries. `colspan` stays on the native `td`.
|
|
94
|
+
|
|
95
|
+
```html
|
|
96
|
+
<table>
|
|
97
|
+
<caption TableCaption>
|
|
98
|
+
A list of your recent invoices.
|
|
99
|
+
</caption>
|
|
100
|
+
<thead TableHeader>
|
|
101
|
+
...
|
|
102
|
+
</thead>
|
|
103
|
+
<tbody TableBody>
|
|
104
|
+
...
|
|
105
|
+
</tbody>
|
|
106
|
+
<tfoot TableFooter>
|
|
107
|
+
<tr TableRow>
|
|
108
|
+
<td TableCell colspan="3">Total</td>
|
|
109
|
+
<td TableCell class="text-right">$2,500.00</td>
|
|
110
|
+
</tr>
|
|
111
|
+
</tfoot>
|
|
112
|
+
</table>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Row actions
|
|
116
|
+
|
|
117
|
+
Compose action menus with `Button`, `MenuTrigger`, and the dropdown-menu surface.
|
|
118
|
+
|
|
119
|
+
```html
|
|
120
|
+
<ng-template MenuContent #productActionsMenu="MenuContent">
|
|
121
|
+
<MenuSurface class="w-36">
|
|
122
|
+
<button MenuItem>Edit</button>
|
|
123
|
+
<button MenuItem>Duplicate</button>
|
|
124
|
+
<MenuSeparator />
|
|
125
|
+
<button MenuItem variant="destructive">Delete</button>
|
|
126
|
+
</MenuSurface>
|
|
127
|
+
</ng-template>
|
|
128
|
+
|
|
129
|
+
<table>
|
|
130
|
+
<thead TableHeader>
|
|
131
|
+
<tr TableRow>
|
|
132
|
+
<th TableHead>Product</th>
|
|
133
|
+
<th TableHead>Price</th>
|
|
134
|
+
<th TableHead class="text-right">Actions</th>
|
|
135
|
+
</tr>
|
|
136
|
+
</thead>
|
|
137
|
+
<tbody TableBody>
|
|
138
|
+
@for (product of products; track product.name) {
|
|
139
|
+
<tr TableRow>
|
|
140
|
+
<td TableCell class="font-medium">{{ product.name }}</td>
|
|
141
|
+
<td TableCell>{{ product.price }}</td>
|
|
142
|
+
<td TableCell class="text-right">
|
|
143
|
+
<button
|
|
144
|
+
Button
|
|
145
|
+
type="button"
|
|
146
|
+
variant="ghost"
|
|
147
|
+
size="icon-sm"
|
|
148
|
+
[attr.aria-label]="'Open actions for ' + product.name"
|
|
149
|
+
[MenuTrigger]="productActionsMenu">
|
|
150
|
+
<span aria-hidden="true">...</span>
|
|
151
|
+
</button>
|
|
152
|
+
</td>
|
|
153
|
+
</tr>
|
|
154
|
+
}
|
|
155
|
+
</tbody>
|
|
156
|
+
</table>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Data table guide mapping
|
|
160
|
+
|
|
161
|
+
The upstream shadcn `data-table` page is a composition guide, not a standalone primitive. In this Angular library the same guidance starts with `@ojiepermana/angular-component/table` and layers behavior around it.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { BadgeComponent } from '@ojiepermana/angular-component/badge';
|
|
165
|
+
import { ButtonComponent } from '@ojiepermana/angular-component/button';
|
|
166
|
+
import { CheckboxComponent } from '@ojiepermana/angular-component/checkbox';
|
|
167
|
+
import {
|
|
168
|
+
MenuCheckboxItemComponent,
|
|
169
|
+
MenuContentDirective,
|
|
170
|
+
MenuGroupComponent,
|
|
171
|
+
MenuItemComponent,
|
|
172
|
+
MenuLabelComponent,
|
|
173
|
+
MenuSeparatorComponent,
|
|
174
|
+
MenuSurfaceComponent,
|
|
175
|
+
MenuTriggerDirective,
|
|
176
|
+
} from '@ojiepermana/angular-component/dropdown-menu';
|
|
177
|
+
import { InputComponent } from '@ojiepermana/angular-component/input';
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Keep Table as the markup layer and put behavior in Angular signals, services, or a table state library. A small explicit pipeline is usually easier to maintain than a generic catch-all data-table wrapper.
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
readonly filterText = signal('');
|
|
184
|
+
readonly sortState = signal<{ column: 'status' | 'email' | 'amount'; direction: 'asc' | 'desc' } | null>(null);
|
|
185
|
+
readonly selectedIds = signal<ReadonlySet<string>>(new Set());
|
|
186
|
+
|
|
187
|
+
readonly filteredRows = computed(() => {
|
|
188
|
+
const query = filterText().trim().toLowerCase();
|
|
189
|
+
return query ? rows.filter((row) => row.email.toLowerCase().includes(query)) : rows;
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
readonly sortedRows = computed(() => sortRows(filteredRows(), sortState()));
|
|
193
|
+
readonly pageRows = computed(() => paginateRows(sortedRows(), currentPage(), pageSize));
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Selected rows can use the shadcn-compatible `data-state="selected"` hook so row styling stays synchronized with the checkbox state.
|
|
197
|
+
|
|
198
|
+
```html
|
|
199
|
+
<table>
|
|
200
|
+
<thead TableHeader>
|
|
201
|
+
...
|
|
202
|
+
</thead>
|
|
203
|
+
<tbody TableBody>
|
|
204
|
+
@for (row of filteredRows(); track row.id) {
|
|
205
|
+
<tr TableRow [attr.data-state]="selectedIds().has(row.id) ? 'selected' : null">
|
|
206
|
+
<td TableCell>{{ row.title }}</td>
|
|
207
|
+
<td TableCell>{{ row.status }}</td>
|
|
208
|
+
<td TableCell class="text-right">{{ row.amount }}</td>
|
|
209
|
+
</tr>
|
|
210
|
+
}
|
|
211
|
+
</tbody>
|
|
212
|
+
</table>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### RTL
|
|
216
|
+
|
|
217
|
+
Set direction on a wrapper or directly on `Table`. Keep alignment explicit for amount and numeric columns.
|
|
218
|
+
|
|
219
|
+
```html
|
|
220
|
+
<div dir="rtl" lang="ar" class="text-right">
|
|
221
|
+
<table>
|
|
222
|
+
<caption TableCaption>
|
|
223
|
+
قائمة بفواتيرك الأخيرة.
|
|
224
|
+
</caption>
|
|
225
|
+
<thead TableHeader>
|
|
226
|
+
<tr TableRow>
|
|
227
|
+
<th TableHead class="w-25 text-right">الفاتورة</th>
|
|
228
|
+
<th TableHead class="text-right">الحالة</th>
|
|
229
|
+
<th TableHead class="text-right">الطريقة</th>
|
|
230
|
+
<th TableHead class="text-right">المبلغ</th>
|
|
231
|
+
</tr>
|
|
232
|
+
</thead>
|
|
233
|
+
<tbody TableBody>
|
|
234
|
+
...
|
|
235
|
+
</tbody>
|
|
236
|
+
</table>
|
|
237
|
+
</div>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## API reference
|
|
241
|
+
|
|
242
|
+
### `TableComponent`
|
|
243
|
+
|
|
244
|
+
| Input | Type | Default | Description |
|
|
245
|
+
| ------- | -------- | ------- | ------------------------------------------------- |
|
|
246
|
+
| `class` | `string` | `''` | Classes applied to the rendered native `<table>`. |
|
|
247
|
+
|
|
248
|
+
### Attribute parts
|
|
249
|
+
|
|
250
|
+
| Part | Selector | Input | Notes |
|
|
251
|
+
| ----------------------- | ----------------------- | ------- | ------------------------------------------------------- |
|
|
252
|
+
| `TableCaptionComponent` | `caption[TableCaption]` | `class` | Muted caption below the table. |
|
|
253
|
+
| `TableHeaderComponent` | `thead[TableHeader]` | `class` | Header section with row dividers. |
|
|
254
|
+
| `TableBodyComponent` | `tbody[TableBody]` | `class` | Body section that removes the final row border. |
|
|
255
|
+
| `TableFooterComponent` | `tfoot[TableFooter]` | `class` | Footer section with muted background and top divider. |
|
|
256
|
+
| `TableRowComponent` | `tr[TableRow]` | `class` | Row hover state and `data-state="selected"` support. |
|
|
257
|
+
| `TableHeadComponent` | `th[TableHead]` | `class` | Header cell with muted foreground and nowrap text. |
|
|
258
|
+
| `TableCellComponent` | `td[TableCell]` | `class` | Body or footer cell with compact padding and alignment. |
|
|
259
|
+
|
|
260
|
+
## Styling and theming
|
|
261
|
+
|
|
262
|
+
The root host is `relative block w-full overflow-x-auto`; the rendered table is `w-full caption-bottom text-sm`.
|
|
263
|
+
|
|
264
|
+
Dividers use the shared `border-border` token, row hover uses `hover:bg-muted/50`, footer uses `bg-muted/50`, and selected rows respond to `data-state="selected"` with `bg-muted`.
|
|
265
|
+
|
|
266
|
+
Pass utility classes to individual parts for column width, alignment, density, responsive visibility, and typography.
|
|
267
|
+
|
|
268
|
+
## Accessibility
|
|
269
|
+
|
|
270
|
+
- Use a `<caption TableCaption>` when the table conveys meaningful data.
|
|
271
|
+
- Keep column headers in native `<th TableHead>` cells so screen readers can announce header relationships.
|
|
272
|
+
- Use `scope`, `colspan`, `rowspan`, and `aria-sort` on native cells when your data model needs them.
|
|
273
|
+
- Do not use Table for non-tabular layout. Use CSS grid or flex layouts for purely visual alignment.
|
|
274
|
+
|
|
275
|
+
## Keyboard interactions
|
|
276
|
+
|
|
277
|
+
Static tables do not add custom keyboard behavior. Focusable controls inside cells, such as action menu buttons, keep their own native or primitive-provided keyboard behavior.
|
|
278
|
+
|
|
279
|
+
When composing a row menu, the trigger uses native button activation and the local menu handles Arrow Up, Arrow Down, Home, End, Tab dismissal, and typeahead.
|
|
280
|
+
|
|
281
|
+
## Angular notes
|
|
282
|
+
|
|
283
|
+
- All table parts are standalone components and can be imported directly.
|
|
284
|
+
- The root `Table` wraps projected content in a native `<table>`, so child parts should be direct table descendants in normal HTML order.
|
|
285
|
+
- Prefer Angular `@for` with a stable `track` expression for row rendering.
|
|
286
|
+
- Keep sorting, filtering, pagination, and selection state outside the primitive; Table is intentionally presentational.
|
|
287
|
+
- Attribute-selector primitives must be applied to their semantic tag, for example `th[TableHead]` and `td[TableCell]`.
|
|
288
|
+
|
|
289
|
+
## Source parity
|
|
290
|
+
|
|
291
|
+
This Angular implementation follows the shadcn Table docs for preview, usage, composition, footer, actions, data-table guidance, and RTL. React component names map to Angular selectors, React `className` maps to `class`, and row action menus compose with the local Angular dropdown-menu primitives.
|
package/tabs/README.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Tabs
|
|
2
|
+
|
|
3
|
+
A set of layered sections of content, known as tab panels, that are displayed one at a time.
|
|
4
|
+
|
|
5
|
+
Use Tabs for compact settings surfaces, dashboards, account forms, and any view where related panels should share the same page location.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { TabsComponent, TabsContentComponent, TabsListComponent, TabsTriggerComponent } from '@ojiepermana/angular-component/tabs';
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Composition
|
|
14
|
+
|
|
15
|
+
The Angular structure mirrors shadcn and Radix while using Angular selectors and native button triggers.
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
Tabs
|
|
19
|
+
├── TabsList
|
|
20
|
+
│ ├── button[TabsTrigger]
|
|
21
|
+
│ └── button[TabsTrigger]
|
|
22
|
+
├── TabsContent
|
|
23
|
+
└── TabsContent
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Basic usage
|
|
27
|
+
|
|
28
|
+
Pair every trigger value with a matching panel value. Bind `[(value)]` when the parent should seed the active tab or observe changes.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
const activeTab = signal<string | null>('account');
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```html
|
|
35
|
+
<Tabs [(value)]="activeTab" class="w-full max-w-md">
|
|
36
|
+
<TabsList>
|
|
37
|
+
<button TabsTrigger value="account">Account</button>
|
|
38
|
+
<button TabsTrigger value="password">Password</button>
|
|
39
|
+
</TabsList>
|
|
40
|
+
|
|
41
|
+
<TabsContent value="account">Make changes to your account here.</TabsContent>
|
|
42
|
+
<TabsContent value="password">Change your password here.</TabsContent>
|
|
43
|
+
</Tabs>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Common patterns
|
|
47
|
+
|
|
48
|
+
### Line variant
|
|
49
|
+
|
|
50
|
+
Use `variant="line"` on `TabsList` for the underline treatment from the shadcn examples.
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
const lineTab = signal<string | null>('overview');
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```html
|
|
57
|
+
<Tabs [(value)]="lineTab">
|
|
58
|
+
<TabsList variant="line">
|
|
59
|
+
<button TabsTrigger value="overview">Overview</button>
|
|
60
|
+
<button TabsTrigger value="analytics">Analytics</button>
|
|
61
|
+
<button TabsTrigger value="reports">Reports</button>
|
|
62
|
+
</TabsList>
|
|
63
|
+
|
|
64
|
+
<TabsContent value="overview">Overview metrics are selected.</TabsContent>
|
|
65
|
+
<TabsContent value="analytics">Analytics metrics are selected.</TabsContent>
|
|
66
|
+
<TabsContent value="reports">Reports metrics are selected.</TabsContent>
|
|
67
|
+
</Tabs>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Vertical orientation
|
|
71
|
+
|
|
72
|
+
Set `orientation="vertical"` on `Tabs` when triggers should stack beside the active panel.
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
const verticalTab = signal<string | null>('account');
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```html
|
|
79
|
+
<Tabs [(value)]="verticalTab" orientation="vertical" class="w-full max-w-2xl">
|
|
80
|
+
<TabsList class="min-w-40">
|
|
81
|
+
<button TabsTrigger value="account">Account</button>
|
|
82
|
+
<button TabsTrigger value="password">Password</button>
|
|
83
|
+
<button TabsTrigger value="notifications">Notifications</button>
|
|
84
|
+
</TabsList>
|
|
85
|
+
|
|
86
|
+
<TabsContent value="account" class="mt-0 flex-1 rounded-lg border border-border p-4">
|
|
87
|
+
Account preferences and details live in this panel.
|
|
88
|
+
</TabsContent>
|
|
89
|
+
</Tabs>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Disabled triggers
|
|
93
|
+
|
|
94
|
+
Disable a trigger with `[disabled]="true"`. Disabled triggers remain visible, expose disabled state, and are skipped by arrow-key navigation.
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
const disabledTab = signal<string | null>('home');
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```html
|
|
101
|
+
<Tabs [(value)]="disabledTab">
|
|
102
|
+
<TabsList>
|
|
103
|
+
<button TabsTrigger value="home">Home</button>
|
|
104
|
+
<button TabsTrigger value="settings" [disabled]="true">Disabled</button>
|
|
105
|
+
</TabsList>
|
|
106
|
+
|
|
107
|
+
<TabsContent value="home">Home content stays reachable.</TabsContent>
|
|
108
|
+
<TabsContent value="settings">Settings is intentionally unavailable.</TabsContent>
|
|
109
|
+
</Tabs>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Icons
|
|
113
|
+
|
|
114
|
+
Project icon components or inline SVG before trigger text. The trigger includes icon and label spacing.
|
|
115
|
+
|
|
116
|
+
```html
|
|
117
|
+
<Tabs value="preview">
|
|
118
|
+
<TabsList>
|
|
119
|
+
<button TabsTrigger value="preview">
|
|
120
|
+
<svg aria-hidden="true" class="size-4" viewBox="0 0 24 24">...</svg>
|
|
121
|
+
Preview
|
|
122
|
+
</button>
|
|
123
|
+
<button TabsTrigger value="code">
|
|
124
|
+
<svg aria-hidden="true" class="size-4" viewBox="0 0 24 24">...</svg>
|
|
125
|
+
Code
|
|
126
|
+
</button>
|
|
127
|
+
</TabsList>
|
|
128
|
+
|
|
129
|
+
<TabsContent value="preview">Rendered preview is selected.</TabsContent>
|
|
130
|
+
<TabsContent value="code">Code sample is selected.</TabsContent>
|
|
131
|
+
</Tabs>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### RTL
|
|
135
|
+
|
|
136
|
+
Set `dir="rtl"` on the tabs root or an ancestor container when the surrounding interface runs right to left.
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<Tabs value="overview" dir="rtl" lang="ar" class="w-full max-w-md text-right">
|
|
140
|
+
<TabsList>
|
|
141
|
+
<button TabsTrigger value="overview">نظرة عامة</button>
|
|
142
|
+
<button TabsTrigger value="analytics">التحليلات</button>
|
|
143
|
+
<button TabsTrigger value="reports">التقارير</button>
|
|
144
|
+
<button TabsTrigger value="settings">الإعدادات</button>
|
|
145
|
+
</TabsList>
|
|
146
|
+
|
|
147
|
+
<TabsContent value="overview">لديك ١٢ مشروعًا نشطًا و٣ مهام معلقة.</TabsContent>
|
|
148
|
+
</Tabs>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## API reference
|
|
152
|
+
|
|
153
|
+
### `TabsComponent`
|
|
154
|
+
|
|
155
|
+
| Input | Type | Default |
|
|
156
|
+
| --------------- | ---------------------------- | -------------- |
|
|
157
|
+
| `value` (model) | `string \| null` | `null` |
|
|
158
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` |
|
|
159
|
+
| `class` | `string` | `''` |
|
|
160
|
+
|
|
161
|
+
### `TabsListComponent`
|
|
162
|
+
|
|
163
|
+
| Input | Type | Default |
|
|
164
|
+
| --------- | --------------------- | ----------- |
|
|
165
|
+
| `variant` | `'default' \| 'line'` | `'default'` |
|
|
166
|
+
| `class` | `string` | `''` |
|
|
167
|
+
|
|
168
|
+
### `TabsTriggerComponent`
|
|
169
|
+
|
|
170
|
+
| Input | Type | Default |
|
|
171
|
+
| ---------- | --------- | ------- |
|
|
172
|
+
| `value` | `string` | — |
|
|
173
|
+
| `disabled` | `boolean` | `false` |
|
|
174
|
+
| `class` | `string` | `''` |
|
|
175
|
+
|
|
176
|
+
### `TabsContentComponent`
|
|
177
|
+
|
|
178
|
+
| Input | Type | Default |
|
|
179
|
+
| ------- | -------- | ------- |
|
|
180
|
+
| `value` | `string` | — |
|
|
181
|
+
| `class` | `string` | `''` |
|
|
182
|
+
|
|
183
|
+
## Styling and theming
|
|
184
|
+
|
|
185
|
+
The default list uses `bg-muted` and active triggers use `bg-background`, `text-foreground`, and `shadow-sm` to match the shadcn look.
|
|
186
|
+
|
|
187
|
+
The line variant uses `border-border` on the list and an active `border-foreground` underline on the trigger. Pass `class` to the root, list, trigger, or content to tune width, grid layouts, spacing, and panel framing.
|
|
188
|
+
|
|
189
|
+
## Accessibility
|
|
190
|
+
|
|
191
|
+
- `TabsList` renders `role="tablist"` and `aria-orientation`.
|
|
192
|
+
- `button[TabsTrigger]` renders `role="tab"`, `aria-selected`, roving `tabindex`, `aria-controls`, and `aria-disabled` when disabled.
|
|
193
|
+
- `TabsContent` renders `role="tabpanel"`, `aria-labelledby`, `tabindex="0"`, and `hidden` when inactive.
|
|
194
|
+
- Keep trigger labels concise and unique. Icons inside triggers should be decorative or have their own accessible text outside the icon.
|
|
195
|
+
|
|
196
|
+
## Keyboard interactions
|
|
197
|
+
|
|
198
|
+
- `ArrowRight` and `ArrowLeft` move selection and focus through horizontal tabs.
|
|
199
|
+
- `ArrowDown` and `ArrowUp` move selection and focus through vertical tabs.
|
|
200
|
+
- `Home` and `End` jump to the first and last enabled trigger.
|
|
201
|
+
- Disabled triggers are skipped by arrow-key, Home, and End navigation.
|
|
202
|
+
- Enter and Space retain native button activation behavior.
|
|
203
|
+
|
|
204
|
+
## Angular notes
|
|
205
|
+
|
|
206
|
+
- The component uses Angular signal-based `model()` binding for `value`.
|
|
207
|
+
- Use `signal<string | null>()` with `[(value)]` as the Angular equivalent of shadcn `defaultValue` when parent state matters.
|
|
208
|
+
- Values are strings and should be unique within a tabs root.
|
|
209
|
+
- `variant="line"` belongs to `TabsList`, matching the upstream shadcn API.
|
|
210
|
+
- Icons are projected content; bring an app icon component or inline SVG where needed.
|
|
211
|
+
|
|
212
|
+
## Source parity
|
|
213
|
+
|
|
214
|
+
This Angular implementation follows the current shadcn Tabs docs: preview card tabs, usage, composition, line, vertical, disabled, icons, RTL, and Radix API guidance. React props are translated to Angular selectors, native buttons, and signal-friendly state.
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Textarea
|
|
2
|
+
|
|
3
|
+
Displays a multiline native `<textarea>` with the shared Angular theme tokens and shadcn-style field composition.
|
|
4
|
+
|
|
5
|
+
Use Textarea for feedback, notes, comments, support messages, and any form surface that needs multi-line entry while keeping browser-native editing behavior.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { TextareaComponent } from '@ojiepermana/angular-component/textarea';
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Import the `form`, `label`, or `button` entrypoints separately when the page also needs structured field copy or submit actions.
|
|
14
|
+
|
|
15
|
+
## Composition
|
|
16
|
+
|
|
17
|
+
Textarea stays intentionally small because it styles the native control instead of wrapping it in a custom value accessor.
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
label[Label]
|
|
21
|
+
└── textarea[Textarea]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
For the richer shadcn field pattern, compose it with the existing form primitives.
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
FormField
|
|
28
|
+
├── FormLabel
|
|
29
|
+
├── textarea[Textarea][FormControl]
|
|
30
|
+
└── FormDescription or FormMessage
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Basic Usage
|
|
34
|
+
|
|
35
|
+
Use a visible label plus the styled native textarea when you only need the base multiline control.
|
|
36
|
+
|
|
37
|
+
```html
|
|
38
|
+
<label Label for="bio">Bio</label> <textarea id="bio" Textarea rows="4" placeholder="Tell us about yourself"></textarea>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
When the textarea lives inside `FormField`, add `FormControl` so labels, descriptions, and invalid state stay wired together.
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<FormField>
|
|
45
|
+
<FormLabel>Message</FormLabel>
|
|
46
|
+
<textarea Textarea FormControl rows="4" placeholder="Type your message here."></textarea>
|
|
47
|
+
<FormDescription>Enter your message below.</FormDescription>
|
|
48
|
+
</FormField>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Common Patterns
|
|
52
|
+
|
|
53
|
+
### Field Composition
|
|
54
|
+
|
|
55
|
+
The upstream shadcn `Field`, `FieldLabel`, and `FieldDescription` helpers map to the local `form` entrypoint.
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
<FormField>
|
|
59
|
+
<FormLabel>Feedback</FormLabel>
|
|
60
|
+
<textarea Textarea FormControl rows="4" placeholder="Your feedback helps us improve..."></textarea>
|
|
61
|
+
<FormDescription>Share your thoughts about our service.</FormDescription>
|
|
62
|
+
</FormField>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Disabled and Invalid States
|
|
66
|
+
|
|
67
|
+
Use the native `disabled` attribute for disabled state and `aria-invalid="true"` for validation styling.
|
|
68
|
+
|
|
69
|
+
```html
|
|
70
|
+
<FormField>
|
|
71
|
+
<FormLabel>Message</FormLabel>
|
|
72
|
+
<textarea Textarea FormControl rows="4" disabled></textarea>
|
|
73
|
+
<FormDescription>This field is currently disabled.</FormDescription>
|
|
74
|
+
</FormField>
|
|
75
|
+
|
|
76
|
+
<FormField>
|
|
77
|
+
<FormLabel>Message</FormLabel>
|
|
78
|
+
<textarea Textarea FormControl rows="4" aria-invalid="true"></textarea>
|
|
79
|
+
<FormDescription>Please enter a valid message.</FormDescription>
|
|
80
|
+
</FormField>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Submit Row
|
|
84
|
+
|
|
85
|
+
Pair the textarea with `button[Button]` to create feedback and messaging layouts.
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<div class="grid w-full max-w-md gap-2">
|
|
89
|
+
<textarea Textarea rows="5" class="min-h-[140px]" placeholder="Type your message here."></textarea>
|
|
90
|
+
<button Button type="button" class="w-fit">Send message</button>
|
|
91
|
+
</div>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### RTL
|
|
95
|
+
|
|
96
|
+
Set `dir="rtl"` on a wrapping container or manage direction globally in your layout. The textarea stays native while labels and helper text follow the document direction.
|
|
97
|
+
|
|
98
|
+
```html
|
|
99
|
+
<div dir="rtl" lang="ar" class="max-w-md text-right">
|
|
100
|
+
<FormField>
|
|
101
|
+
<FormLabel>التعليقات</FormLabel>
|
|
102
|
+
<textarea Textarea FormControl rows="4" placeholder="تعليقاتك تساعدنا على التحسين..."></textarea>
|
|
103
|
+
<FormDescription>شاركنا أفكارك حول خدمتنا.</FormDescription>
|
|
104
|
+
</FormField>
|
|
105
|
+
</div>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## API Reference
|
|
109
|
+
|
|
110
|
+
### `TextareaComponent`
|
|
111
|
+
|
|
112
|
+
| Input | Type | Default |
|
|
113
|
+
| ------- | -------- | ------- |
|
|
114
|
+
| `class` | `string` | `''` |
|
|
115
|
+
|
|
116
|
+
### Native Attributes
|
|
117
|
+
|
|
118
|
+
All standard `<textarea>` attributes such as `rows`, `placeholder`, `disabled`, `required`, `readonly`, `dir`, and `aria-invalid` pass through unchanged.
|
|
119
|
+
|
|
120
|
+
The component also exposes a `focus()` instance method when accessed through `ViewChild(TextareaComponent)`.
|
|
121
|
+
|
|
122
|
+
## Styling and Theming
|
|
123
|
+
|
|
124
|
+
The component uses the shared `border-input`, `ring-ring`, `text-foreground`, `placeholder:text-muted-foreground`, and `aria-[invalid=true]:border-destructive` tokens.
|
|
125
|
+
|
|
126
|
+
Base styling includes a `min-h-[60px]` guard to avoid collapsed textareas and `resize-y` so users can increase the writing area without breaking width-constrained layouts.
|
|
127
|
+
|
|
128
|
+
Pass `class` to tune height, spacing, or layout while keeping the base focus, disabled, invalid, and resize treatment.
|
|
129
|
+
|
|
130
|
+
## Accessibility
|
|
131
|
+
|
|
132
|
+
- Pair the textarea with a visible label using `label[Label]` or `FormLabel`.
|
|
133
|
+
- Use `FormControl` inside `FormField` so helper and error text participate in the same accessible description chain.
|
|
134
|
+
- Mark validation failures with `aria-invalid="true"` and provide descriptive helper or error text nearby.
|
|
135
|
+
- Keep the native resize handle available so pointer and touch users can increase the input area when needed.
|
|
136
|
+
|
|
137
|
+
## Keyboard Interactions
|
|
138
|
+
|
|
139
|
+
- `Tab` and `Shift+Tab` move focus in and out using the normal browser order.
|
|
140
|
+
- `Enter` inserts a new line instead of submitting by itself.
|
|
141
|
+
- Arrow keys, Home, End, selection, and clipboard shortcuts stay browser-native.
|
|
142
|
+
|
|
143
|
+
## Angular Notes
|
|
144
|
+
|
|
145
|
+
- Textarea styles the native `<textarea>`, so it works with both `ngModel` and reactive forms.
|
|
146
|
+
- Prefer using `FormControl` when the textarea lives inside `FormField`.
|
|
147
|
+
- Let Angular form state drive invalid styling when possible, but `aria-invalid="true"` also works for externally managed validation.
|
|
148
|
+
|
|
149
|
+
## Source Parity
|
|
150
|
+
|
|
151
|
+
This Angular implementation follows the shadcn Textarea page closely for the core textarea, disabled and invalid states, button layout, and RTL guidance.
|
|
152
|
+
|
|
153
|
+
The upstream `Field` helper family intentionally maps to the existing Angular `form` entrypoint instead of adding a separate `field` runtime surface.
|