@ojiepermana/angular 21.3.4 → 22.0.1
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 +10 -6
- package/component/accordion/README.md +195 -0
- package/component/alert/README.md +182 -0
- package/component/alert-dialog/README.md +239 -0
- package/component/aspect-ratio/README.md +112 -0
- package/component/avatar/README.md +176 -0
- package/component/badge/README.md +133 -0
- package/component/breadcrumb/README.md +216 -0
- package/component/button/README.md +139 -0
- package/component/button-group/README.md +208 -0
- package/component/calendar/README.md +132 -0
- package/component/card/README.md +220 -0
- package/component/carousel/README.md +276 -0
- package/component/checkbox/README.md +149 -0
- package/component/collapsible/README.md +195 -0
- package/component/combobox/README.md +198 -0
- package/component/command/README.md +275 -0
- package/component/composer/README.md +235 -0
- package/component/composer/package.json +4 -0
- package/component/context-menu/README.md +267 -0
- package/component/date-picker/README.md +177 -0
- package/component/dialog/README.md +237 -0
- package/component/drawer/README.md +145 -0
- package/component/dropdown-menu/README.md +311 -0
- package/component/editor/README.md +136 -0
- package/component/editor/package.json +4 -0
- package/component/empty/README.md +183 -0
- package/component/empty/package.json +4 -0
- package/component/form/README.md +210 -0
- package/component/hover-card/README.md +146 -0
- package/component/hover-card/package.json +4 -0
- package/component/input/README.md +159 -0
- package/component/input-group/README.md +239 -0
- package/component/input-otp/README.md +278 -0
- package/component/input-otp/package.json +4 -0
- package/component/item/README.md +247 -0
- package/component/kanban/README.md +81 -0
- package/component/kanban/package.json +4 -0
- package/component/kbd/README.md +139 -0
- package/component/kbd/package.json +4 -0
- package/component/label/README.md +136 -0
- package/component/menubar/README.md +269 -0
- package/component/menubar/package.json +4 -0
- package/component/native-select/README.md +176 -0
- package/component/native-select/package.json +4 -0
- package/component/navigation-menu/README.md +160 -0
- package/component/navigation-menu/package.json +4 -0
- package/component/pagination/README.md +144 -0
- package/component/pillbox/README.md +67 -0
- package/component/pillbox/package.json +4 -0
- package/component/popover/README.md +43 -0
- package/component/progress/README.md +160 -0
- package/component/radio/README.md +209 -0
- package/component/resizable/README.md +168 -0
- package/component/resizable/package.json +4 -0
- package/component/scroll-area/README.md +143 -0
- package/component/select/README.md +174 -0
- package/component/separator/README.md +170 -0
- package/component/sheet/README.md +183 -0
- package/component/skeleton/README.md +158 -0
- package/component/slider/README.md +207 -0
- package/component/spinner/README.md +160 -0
- package/component/spinner/package.json +4 -0
- package/component/switch/README.md +166 -0
- package/component/table/README.md +291 -0
- package/component/tabs/README.md +219 -0
- package/component/textarea/README.md +154 -0
- package/component/timeline/README.md +94 -0
- package/component/timeline/package.json +4 -0
- package/component/toast/README.md +321 -0
- package/component/toggle/README.md +131 -0
- package/component/toggle/package.json +4 -0
- package/component/toggle-group/README.md +206 -0
- package/component/toggle-group/package.json +4 -0
- package/component/tooltip/README.md +211 -0
- package/fesm2022/ojiepermana-angular-component-accordion.mjs +45 -30
- package/fesm2022/ojiepermana-angular-component-accordion.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-alert-dialog.mjs +95 -61
- package/fesm2022/ojiepermana-angular-component-alert-dialog.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-alert.mjs +30 -21
- package/fesm2022/ojiepermana-angular-component-alert.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-aspect-ratio.mjs +11 -7
- package/fesm2022/ojiepermana-angular-component-aspect-ratio.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-avatar.mjs +50 -34
- package/fesm2022/ojiepermana-angular-component-avatar.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-badge.mjs +9 -6
- package/fesm2022/ojiepermana-angular-component-badge.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-breadcrumb.mjs +49 -35
- package/fesm2022/ojiepermana-angular-component-breadcrumb.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-button-group.mjs +25 -17
- package/fesm2022/ojiepermana-angular-component-button-group.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-button.mjs +11 -7
- package/fesm2022/ojiepermana-angular-component-button.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-calendar.mjs +23 -13
- package/fesm2022/ojiepermana-angular-component-calendar.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-card.mjs +51 -36
- package/fesm2022/ojiepermana-angular-component-card.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-carousel.mjs +66 -42
- package/fesm2022/ojiepermana-angular-component-carousel.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-chart.mjs +494 -283
- package/fesm2022/ojiepermana-angular-component-chart.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-checkbox.mjs +23 -13
- package/fesm2022/ojiepermana-angular-component-checkbox.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-collapsible.mjs +28 -20
- package/fesm2022/ojiepermana-angular-component-collapsible.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-combobox.mjs +27 -18
- package/fesm2022/ojiepermana-angular-component-combobox.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-command.mjs +77 -52
- package/fesm2022/ojiepermana-angular-component-command.mjs.map +1 -1
- 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 +9 -6
- package/fesm2022/ojiepermana-angular-component-context-menu.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-date-picker.mjs +34 -19
- package/fesm2022/ojiepermana-angular-component-date-picker.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-dialog.mjs +55 -38
- package/fesm2022/ojiepermana-angular-component-dialog.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-drawer.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-dropdown-menu.mjs +108 -74
- package/fesm2022/ojiepermana-angular-component-dropdown-menu.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-editor.mjs +717 -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 +200 -42
- package/fesm2022/ojiepermana-angular-component-form.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-hover-card.mjs +297 -0
- package/fesm2022/ojiepermana-angular-component-hover-card.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-input-group.mjs +48 -33
- package/fesm2022/ojiepermana-angular-component-input-group.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-input-otp.mjs +514 -0
- package/fesm2022/ojiepermana-angular-component-input-otp.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-input.mjs +7 -5
- package/fesm2022/ojiepermana-angular-component-input.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-item.mjs +76 -53
- package/fesm2022/ojiepermana-angular-component-item.mjs.map +1 -1
- 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 +9 -6
- package/fesm2022/ojiepermana-angular-component-label.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-menubar.mjs +308 -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 +413 -0
- package/fesm2022/ojiepermana-angular-component-navigation-menu.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-pagination.mjs +65 -31
- package/fesm2022/ojiepermana-angular-component-pagination.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-pillbox.mjs +812 -0
- package/fesm2022/ojiepermana-angular-component-pillbox.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-popover.mjs +18 -12
- package/fesm2022/ojiepermana-angular-component-popover.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-progress.mjs +17 -10
- package/fesm2022/ojiepermana-angular-component-progress.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-radio.mjs +47 -17
- package/fesm2022/ojiepermana-angular-component-radio.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-resizable.mjs +481 -0
- package/fesm2022/ojiepermana-angular-component-resizable.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-component-scroll-area.mjs +15 -9
- package/fesm2022/ojiepermana-angular-component-scroll-area.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-select.mjs +71 -26
- package/fesm2022/ojiepermana-angular-component-select.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-separator.mjs +11 -7
- package/fesm2022/ojiepermana-angular-component-separator.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-sheet.mjs +91 -42
- package/fesm2022/ojiepermana-angular-component-sheet.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-skeleton.mjs +7 -5
- package/fesm2022/ojiepermana-angular-component-skeleton.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-slider.mjs +401 -7
- package/fesm2022/ojiepermana-angular-component-slider.mjs.map +1 -1
- 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 +47 -15
- package/fesm2022/ojiepermana-angular-component-switch.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-table.mjs +56 -40
- package/fesm2022/ojiepermana-angular-component-table.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-tabs.mjs +58 -38
- package/fesm2022/ojiepermana-angular-component-tabs.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-textarea.mjs +8 -6
- package/fesm2022/ojiepermana-angular-component-textarea.mjs.map +1 -1
- 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 +28 -4
- package/fesm2022/ojiepermana-angular-component-toast.mjs.map +1 -1
- 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 +304 -6
- package/fesm2022/ojiepermana-angular-component-tooltip.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-component-utils.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-layout-component.mjs +45 -24
- package/fesm2022/ojiepermana-angular-layout-component.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-layout-provider.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-layout-services.mjs +7 -5
- package/fesm2022/ojiepermana-angular-layout-services.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-layout-shell.mjs +3 -3
- package/fesm2022/ojiepermana-angular-layout-shell.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-layout-token-directive.mjs +9 -6
- package/fesm2022/ojiepermana-angular-layout-token-directive.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-layout-token.mjs.map +1 -1
- package/fesm2022/{ojiepermana-angular-layout-empty.mjs → ojiepermana-angular-layout-type-empty.mjs} +4 -4
- package/fesm2022/ojiepermana-angular-layout-type-empty.mjs.map +1 -0
- package/fesm2022/{ojiepermana-angular-layout-horizontal.mjs → ojiepermana-angular-layout-type-horizontal.mjs} +26 -17
- package/fesm2022/ojiepermana-angular-layout-type-horizontal.mjs.map +1 -0
- package/fesm2022/{ojiepermana-angular-layout-vertical.mjs → ojiepermana-angular-layout-type-vertical.mjs} +28 -18
- package/fesm2022/ojiepermana-angular-layout-type-vertical.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-layout.mjs +74 -50
- package/fesm2022/ojiepermana-angular-layout.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-navigation-demo-data.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-navigation-icon.mjs +11 -7
- package/fesm2022/ojiepermana-angular-navigation-icon.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-navigation-item.mjs +27 -16
- package/fesm2022/ojiepermana-angular-navigation-item.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-navigation-service.mjs +29 -20
- package/fesm2022/ojiepermana-angular-navigation-service.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-navigation-sidebar.mjs +71 -43
- package/fesm2022/ojiepermana-angular-navigation-sidebar.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-navigation-topbar.mjs +261 -24
- package/fesm2022/ojiepermana-angular-navigation-topbar.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-theme-provider.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-theme-services.mjs +19 -11
- package/fesm2022/ojiepermana-angular-theme-services.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-theme-token.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-theme.mjs +19 -11
- package/fesm2022/ojiepermana-angular-theme.mjs.map +1 -1
- package/generator/api/bin/src/emit/client.js +4 -2
- package/generator/api/bin/src/writer/index.js +2 -2
- package/generator/guide/bin/schematics/build/index.js +3 -2
- package/generator/guide/bin/src/engine/component.js +2 -2
- package/generator/guide/bin/src/engine/index.js +3 -3
- package/generator/guide/bin/src/engine/render.js +10 -5
- package/layout/type/empty/package.json +4 -0
- package/layout/type/horizontal/package.json +4 -0
- package/layout/type/vertical/package.json +4 -0
- package/navigation/topbar/README.md +196 -0
- package/package.json +89 -25
- package/theme/README.md +110 -3
- package/theme/styles/integrations/material/autocomplete.css +178 -0
- package/theme/styles/integrations/material/button.css +468 -0
- package/theme/styles/integrations/material/dialog.css +152 -0
- package/theme/styles/integrations/material/select.css +175 -0
- package/theme/styles/integrations/material/slide-toggle.css +234 -0
- package/theme/styles/integrations/material/slider.css +194 -0
- package/theme/styles/integrations/material/tabs.css +229 -0
- package/theme/styles/integrations/material.css +70 -60
- package/types/ojiepermana-angular-component-combobox.d.ts +1 -2
- package/types/ojiepermana-angular-component-composer.d.ts +90 -0
- package/types/ojiepermana-angular-component-dropdown-menu.d.ts +2 -0
- package/types/ojiepermana-angular-component-editor.d.ts +123 -0
- package/types/ojiepermana-angular-component-empty.d.ts +50 -0
- package/types/ojiepermana-angular-component-form.d.ts +52 -3
- package/types/ojiepermana-angular-component-hover-card.d.ts +74 -0
- package/types/ojiepermana-angular-component-input-otp.d.ts +136 -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-menubar.d.ts +67 -0
- package/types/ojiepermana-angular-component-native-select.d.ts +26 -0
- package/types/ojiepermana-angular-component-navigation-menu.d.ts +96 -0
- package/types/ojiepermana-angular-component-pagination.d.ts +10 -4
- package/types/ojiepermana-angular-component-pillbox.d.ts +157 -0
- package/types/ojiepermana-angular-component-radio.d.ts +7 -1
- package/types/ojiepermana-angular-component-resizable.d.ts +99 -0
- package/types/ojiepermana-angular-component-select.d.ts +17 -5
- package/types/ojiepermana-angular-component-sheet.d.ts +3 -1
- package/types/ojiepermana-angular-component-slider.d.ts +59 -1
- package/types/ojiepermana-angular-component-spinner.d.ts +13 -0
- package/types/ojiepermana-angular-component-switch.d.ts +13 -3
- package/types/ojiepermana-angular-component-timeline.d.ts +63 -0
- package/types/ojiepermana-angular-component-toast.d.ts +12 -3
- 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 +72 -5
- package/types/{ojiepermana-angular-layout-horizontal.d.ts → ojiepermana-angular-layout-type-horizontal.d.ts} +3 -3
- package/types/{ojiepermana-angular-layout-vertical.d.ts → ojiepermana-angular-layout-type-vertical.d.ts} +3 -3
- package/types/ojiepermana-angular-layout.d.ts +5 -5
- package/types/ojiepermana-angular-navigation-item.d.ts +1 -1
- package/types/ojiepermana-angular-navigation-service.d.ts +7 -7
- package/types/ojiepermana-angular-navigation-sidebar.d.ts +8 -8
- package/types/ojiepermana-angular-navigation-topbar.d.ts +24 -4
- package/types/ojiepermana-angular-navigation-types.d.ts +14 -8
- package/fesm2022/ojiepermana-angular-layout-empty.mjs.map +0 -1
- package/fesm2022/ojiepermana-angular-layout-horizontal.mjs.map +0 -1
- package/fesm2022/ojiepermana-angular-layout-vertical.mjs.map +0 -1
- package/layout/empty/package.json +0 -4
- package/layout/horizontal/package.json +0 -4
- package/layout/vertical/package.json +0 -4
- /package/types/{ojiepermana-angular-layout-empty.d.ts → ojiepermana-angular-layout-type-empty.d.ts} +0 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Card
|
|
2
|
+
|
|
3
|
+
Displays a card with header, content, footer, an optional header action, and a compact size variant.
|
|
4
|
+
|
|
5
|
+
Use Card for login panels, billing summaries, media/event highlights, and other grouped surfaces that need a clear title plus supporting actions.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
CardActionComponent,
|
|
12
|
+
CardComponent,
|
|
13
|
+
CardContentComponent,
|
|
14
|
+
CardDescriptionComponent,
|
|
15
|
+
CardFooterComponent,
|
|
16
|
+
CardHeaderComponent,
|
|
17
|
+
CardTitleComponent,
|
|
18
|
+
} from '@ojiepermana/angular/component/card';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Composition
|
|
22
|
+
|
|
23
|
+
The Angular structure matches the current shadcn card hierarchy while using Angular selectors.
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
ui-card
|
|
27
|
+
├── ui-card-header
|
|
28
|
+
│ ├── ui-card-title
|
|
29
|
+
│ ├── ui-card-description
|
|
30
|
+
│ └── ui-card-action
|
|
31
|
+
├── ui-card-content
|
|
32
|
+
└── ui-card-footer
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Basic usage
|
|
36
|
+
|
|
37
|
+
```html
|
|
38
|
+
<ui-card class="w-full max-w-sm">
|
|
39
|
+
<ui-card-header>
|
|
40
|
+
<ui-card-title>Card Title</ui-card-title>
|
|
41
|
+
<ui-card-description>Card Description</ui-card-description>
|
|
42
|
+
<ui-card-action>
|
|
43
|
+
<button ui-button variant="link" size="sm">Action</button>
|
|
44
|
+
</ui-card-action>
|
|
45
|
+
</ui-card-header>
|
|
46
|
+
|
|
47
|
+
<ui-card-content>
|
|
48
|
+
<p>Card Content</p>
|
|
49
|
+
</ui-card-content>
|
|
50
|
+
|
|
51
|
+
<ui-card-footer>
|
|
52
|
+
<p>Card Footer</p>
|
|
53
|
+
</ui-card-footer>
|
|
54
|
+
</ui-card>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Common patterns
|
|
58
|
+
|
|
59
|
+
### Login card
|
|
60
|
+
|
|
61
|
+
The current shadcn preview uses the card as a login surface. In Angular, compose the card with button, input, and label primitives directly.
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
<ui-card class="w-full max-w-sm">
|
|
65
|
+
<ui-card-header>
|
|
66
|
+
<ui-card-title>Login to your account</ui-card-title>
|
|
67
|
+
<ui-card-description>Enter your email below to login to your account</ui-card-description>
|
|
68
|
+
<ui-card-action>
|
|
69
|
+
<button ui-button variant="link" size="sm">Sign Up</button>
|
|
70
|
+
</ui-card-action>
|
|
71
|
+
</ui-card-header>
|
|
72
|
+
|
|
73
|
+
<ui-card-content>
|
|
74
|
+
<form class="grid gap-6">
|
|
75
|
+
<div class="grid gap-2">
|
|
76
|
+
<label ui-label for="email">Email</label>
|
|
77
|
+
<input ui-input id="email" type="email" placeholder="m@example.com" required />
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div class="grid gap-2">
|
|
81
|
+
<div class="flex items-center">
|
|
82
|
+
<label ui-label for="password">Password</label>
|
|
83
|
+
<a href="#forgot-password" class="ms-auto inline-block text-sm underline-offset-4 hover:underline">
|
|
84
|
+
Forgot your password?
|
|
85
|
+
</a>
|
|
86
|
+
</div>
|
|
87
|
+
<input ui-input id="password" type="password" required />
|
|
88
|
+
</div>
|
|
89
|
+
</form>
|
|
90
|
+
</ui-card-content>
|
|
91
|
+
|
|
92
|
+
<ui-card-footer class="flex-col gap-2">
|
|
93
|
+
<button ui-button class="w-full">Login</button>
|
|
94
|
+
<button ui-button variant="outline" class="w-full">Login with Google</button>
|
|
95
|
+
</ui-card-footer>
|
|
96
|
+
</ui-card>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Small size
|
|
100
|
+
|
|
101
|
+
Use `size="sm"` on the root card when the surface should feel denser. The compact size cascades to header spacing, content padding, footer padding, and heading/body typography.
|
|
102
|
+
|
|
103
|
+
```html
|
|
104
|
+
<ui-card size="sm" class="w-full max-w-sm">
|
|
105
|
+
<ui-card-header>
|
|
106
|
+
<ui-card-title>Small Card</ui-card-title>
|
|
107
|
+
<ui-card-description>This card uses the small size variant.</ui-card-description>
|
|
108
|
+
</ui-card-header>
|
|
109
|
+
<ui-card-content>
|
|
110
|
+
<p class="text-sm text-muted-foreground">Compact spacing for dense dashboards or inset panels.</p>
|
|
111
|
+
</ui-card-content>
|
|
112
|
+
<ui-card-footer>
|
|
113
|
+
<button ui-button variant="outline" size="sm" class="w-full">Action</button>
|
|
114
|
+
</ui-card-footer>
|
|
115
|
+
</ui-card>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Image card
|
|
119
|
+
|
|
120
|
+
Add media before the header when the card introduces an event, article, or campaign. `ui-card-action` still sits in the top-right of the header and can host a badge or button.
|
|
121
|
+
|
|
122
|
+
```html
|
|
123
|
+
<ui-card class="relative w-full max-w-sm overflow-hidden">
|
|
124
|
+
<div aria-hidden="true" class="pointer-events-none absolute inset-x-0 top-0 z-30 aspect-video bg-black/35"></div>
|
|
125
|
+
<img
|
|
126
|
+
src="https://avatar.vercel.sh/shadcn1"
|
|
127
|
+
alt="Event cover"
|
|
128
|
+
class="relative z-20 aspect-video w-full object-cover brightness-75 grayscale" />
|
|
129
|
+
|
|
130
|
+
<ui-card-header>
|
|
131
|
+
<ui-card-action>
|
|
132
|
+
<ui-badge variant="secondary">Featured</ui-badge>
|
|
133
|
+
</ui-card-action>
|
|
134
|
+
<ui-card-title>Design systems meetup</ui-card-title>
|
|
135
|
+
<ui-card-description> A practical talk on component APIs, accessibility, and shipping faster. </ui-card-description>
|
|
136
|
+
</ui-card-header>
|
|
137
|
+
|
|
138
|
+
<ui-card-footer>
|
|
139
|
+
<button ui-button class="w-full">View event</button>
|
|
140
|
+
</ui-card-footer>
|
|
141
|
+
</ui-card>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### RTL
|
|
145
|
+
|
|
146
|
+
Set `dir="rtl"` on the card host or a wrapper when the surrounding interface runs right to left. The composition stays the same.
|
|
147
|
+
|
|
148
|
+
```html
|
|
149
|
+
<ui-card dir="rtl" lang="ar" class="w-full max-w-sm text-right">
|
|
150
|
+
<ui-card-header>
|
|
151
|
+
<ui-card-title>تسجيل الدخول إلى حسابك</ui-card-title>
|
|
152
|
+
<ui-card-description>أدخل بريدك الإلكتروني أدناه لتسجيل الدخول إلى حسابك</ui-card-description>
|
|
153
|
+
<ui-card-action>
|
|
154
|
+
<button ui-button variant="link" size="sm">إنشاء حساب</button>
|
|
155
|
+
</ui-card-action>
|
|
156
|
+
</ui-card-header>
|
|
157
|
+
<ui-card-content>
|
|
158
|
+
<!-- same form fields as the login example -->
|
|
159
|
+
</ui-card-content>
|
|
160
|
+
<ui-card-footer class="flex-col gap-2">
|
|
161
|
+
<button ui-button class="w-full">تسجيل الدخول</button>
|
|
162
|
+
<button ui-button variant="outline" class="w-full">تسجيل الدخول باستخدام Google</button>
|
|
163
|
+
</ui-card-footer>
|
|
164
|
+
</ui-card>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## API reference
|
|
168
|
+
|
|
169
|
+
### `CardComponent`
|
|
170
|
+
|
|
171
|
+
| Input | Type | Default |
|
|
172
|
+
| ------- | ------------------- | ----------- |
|
|
173
|
+
| `size` | `"default" \| "sm"` | `'default'` |
|
|
174
|
+
| `class` | `string` | `''` |
|
|
175
|
+
|
|
176
|
+
The selected size is also exposed on the host as `data-size`, which can be used for styling overrides.
|
|
177
|
+
|
|
178
|
+
### Parts
|
|
179
|
+
|
|
180
|
+
| Part | Selector | Notes |
|
|
181
|
+
| -------------------------- | --------------------- | ----------------------------------------------------------------- |
|
|
182
|
+
| `CardHeaderComponent` | `ui-card-header` | Header grid that places `ui-card-action` in the top-right corner. |
|
|
183
|
+
| `CardTitleComponent` | `ui-card-title` | Primary heading for the card. |
|
|
184
|
+
| `CardDescriptionComponent` | `ui-card-description` | Supporting text under the title. |
|
|
185
|
+
| `CardActionComponent` | `ui-card-action` | Optional slot for badges, links, or buttons in the header. |
|
|
186
|
+
| `CardContentComponent` | `ui-card-content` | Main body area. |
|
|
187
|
+
| `CardFooterComponent` | `ui-card-footer` | Bottom action row or secondary metadata. |
|
|
188
|
+
|
|
189
|
+
All card parts also accept a `class` input.
|
|
190
|
+
|
|
191
|
+
## Styling and theming
|
|
192
|
+
|
|
193
|
+
The root card uses the shared theme tokens `border-border`, `bg-card`, and `text-card-foreground` plus `shadow-sm`.
|
|
194
|
+
|
|
195
|
+
Pass `class` to the root or any part to layer width, layout, spacing, media overflow, or custom action alignment without changing the primitive structure.
|
|
196
|
+
|
|
197
|
+
The `size` input is the primary spacing control. Switching to `sm` reduces header spacing, content/footer padding, and title/description typography together.
|
|
198
|
+
|
|
199
|
+
## Accessibility
|
|
200
|
+
|
|
201
|
+
- Keep the card root non-interactive. Put clicks on native buttons or anchors inside the card.
|
|
202
|
+
- Label every form field inside `ui-card-content`.
|
|
203
|
+
- Provide meaningful alt text for media cards unless the image is purely decorative.
|
|
204
|
+
- Do not rely on the card border or shadow alone to communicate state.
|
|
205
|
+
|
|
206
|
+
## Keyboard interactions
|
|
207
|
+
|
|
208
|
+
- The card primitives themselves are passive layout containers and do not enter the tab order.
|
|
209
|
+
- Buttons, anchors, and inputs projected into the card keep their native Tab, Enter, and Space behavior.
|
|
210
|
+
- `ui-card-action` does not alter focus order; it only positions header content visually.
|
|
211
|
+
|
|
212
|
+
## Angular notes
|
|
213
|
+
|
|
214
|
+
- `size` is declared only on `ui-card` and cascades to descendant part spacing and typography.
|
|
215
|
+
- `ui-card-action` is the Angular equivalent of the current shadcn `CardAction` slot.
|
|
216
|
+
- The login and RTL examples compose `ButtonComponent`, `InputComponent`, and `LabelComponent` directly with the card primitives.
|
|
217
|
+
|
|
218
|
+
## Source parity
|
|
219
|
+
|
|
220
|
+
This Angular implementation follows the current shadcn card docs, including `CardAction`, `size="sm"`, media cards, and the RTL login example, while translating the structure to Angular selectors and native controls.
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Carousel
|
|
2
|
+
|
|
3
|
+
A carousel with motion, drag gestures, keyboard navigation, and Embla-powered slide state.
|
|
4
|
+
|
|
5
|
+
Use Carousel for image galleries, card reels, onboarding panels, and compact item pickers where adjacent content should be discoverable without leaving the current view.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
CarouselComponent,
|
|
12
|
+
CarouselContentComponent,
|
|
13
|
+
CarouselItemComponent,
|
|
14
|
+
CarouselNextComponent,
|
|
15
|
+
CarouselPreviousComponent,
|
|
16
|
+
type CarouselApi,
|
|
17
|
+
type CarouselOptions,
|
|
18
|
+
} from '@ojiepermana/angular/component/carousel';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
Compose the carousel from a root, content track, items, and optional previous/next buttons.
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<ui-carousel class="w-full max-w-xs">
|
|
27
|
+
<ui-carousel-content>
|
|
28
|
+
<ui-carousel-item>...</ui-carousel-item>
|
|
29
|
+
<ui-carousel-item>...</ui-carousel-item>
|
|
30
|
+
<ui-carousel-item>...</ui-carousel-item>
|
|
31
|
+
</ui-carousel-content>
|
|
32
|
+
<button ui-carousel-previous></button>
|
|
33
|
+
<button ui-carousel-next></button>
|
|
34
|
+
</ui-carousel>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Composition
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
ui-carousel
|
|
41
|
+
├── ui-carousel-content
|
|
42
|
+
│ ├── ui-carousel-item
|
|
43
|
+
│ └── ui-carousel-item
|
|
44
|
+
├── button[ui-carousel-previous]
|
|
45
|
+
└── button[ui-carousel-next]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Common patterns
|
|
49
|
+
|
|
50
|
+
### Card slides
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<ui-carousel class="mx-auto w-full max-w-xs">
|
|
54
|
+
<ui-carousel-content>
|
|
55
|
+
@for (slide of slides; track slide.id) {
|
|
56
|
+
<ui-carousel-item [ariaLabel]="slide.label">
|
|
57
|
+
<div class="p-1">
|
|
58
|
+
<ui-card>
|
|
59
|
+
<ui-card-content class="flex aspect-square items-center justify-center p-6">
|
|
60
|
+
<span class="text-4xl font-semibold">{{ slide.value }}</span>
|
|
61
|
+
</ui-card-content>
|
|
62
|
+
</ui-card>
|
|
63
|
+
</div>
|
|
64
|
+
</ui-carousel-item>
|
|
65
|
+
}
|
|
66
|
+
</ui-carousel-content>
|
|
67
|
+
<button ui-carousel-previous></button>
|
|
68
|
+
<button ui-carousel-next></button>
|
|
69
|
+
</ui-carousel>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Sizes
|
|
73
|
+
|
|
74
|
+
Use basis utilities on each `ui-carousel-item` when multiple slides should be partially visible.
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
readonly startOptions: CarouselOptions = { align: 'start' };
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```html
|
|
81
|
+
<ui-carousel [opts]="startOptions" class="w-full max-w-sm">
|
|
82
|
+
<ui-carousel-content>
|
|
83
|
+
<ui-carousel-item class="basis-1/2 lg:basis-1/3">...</ui-carousel-item>
|
|
84
|
+
<ui-carousel-item class="basis-1/2 lg:basis-1/3">...</ui-carousel-item>
|
|
85
|
+
<ui-carousel-item class="basis-1/2 lg:basis-1/3">...</ui-carousel-item>
|
|
86
|
+
</ui-carousel-content>
|
|
87
|
+
</ui-carousel>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Spacing
|
|
91
|
+
|
|
92
|
+
Match shadcn spacing by pairing a negative margin on `ui-carousel-content` with padding on `ui-carousel-item`.
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<ui-carousel class="w-full max-w-sm">
|
|
96
|
+
<ui-carousel-content class="-ml-2 md:-ml-4">
|
|
97
|
+
<ui-carousel-item class="basis-1/2 pl-2 md:pl-4 lg:basis-1/3">...</ui-carousel-item>
|
|
98
|
+
<ui-carousel-item class="basis-1/2 pl-2 md:pl-4 lg:basis-1/3">...</ui-carousel-item>
|
|
99
|
+
<ui-carousel-item class="basis-1/2 pl-2 md:pl-4 lg:basis-1/3">...</ui-carousel-item>
|
|
100
|
+
</ui-carousel-content>
|
|
101
|
+
</ui-carousel>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Vertical orientation
|
|
105
|
+
|
|
106
|
+
Set `orientation="vertical"` and give the content viewport a height so Embla can measure the track.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
readonly verticalOptions: CarouselOptions = { align: 'start' };
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```html
|
|
113
|
+
<ui-carousel orientation="vertical" [opts]="verticalOptions" class="w-full max-w-xs">
|
|
114
|
+
<ui-carousel-content class="-mt-1 h-67.5">
|
|
115
|
+
<ui-carousel-item class="basis-1/2 pt-1">...</ui-carousel-item>
|
|
116
|
+
<ui-carousel-item class="basis-1/2 pt-1">...</ui-carousel-item>
|
|
117
|
+
<ui-carousel-item class="basis-1/2 pt-1">...</ui-carousel-item>
|
|
118
|
+
</ui-carousel-content>
|
|
119
|
+
</ui-carousel>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Options
|
|
123
|
+
|
|
124
|
+
Pass Embla options through `[opts]`. Keep the object as a class property so Angular does not create a new object every change detection pass.
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
readonly loopOptions: CarouselOptions = {
|
|
128
|
+
align: 'start',
|
|
129
|
+
loop: true,
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```html
|
|
134
|
+
<ui-carousel [opts]="loopOptions">
|
|
135
|
+
<ui-carousel-content>
|
|
136
|
+
<ui-carousel-item>...</ui-carousel-item>
|
|
137
|
+
<ui-carousel-item>...</ui-carousel-item>
|
|
138
|
+
<ui-carousel-item>...</ui-carousel-item>
|
|
139
|
+
</ui-carousel-content>
|
|
140
|
+
</ui-carousel>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### API and events
|
|
144
|
+
|
|
145
|
+
The root component exposes `selectedIndex`, `slideCount`, `canScrollPrev`, and `canScrollNext` as signals. Use a template reference for simple status UI.
|
|
146
|
+
|
|
147
|
+
```html
|
|
148
|
+
<ui-carousel #carouselRef class="w-full max-w-xs">
|
|
149
|
+
<ui-carousel-content>
|
|
150
|
+
<ui-carousel-item>...</ui-carousel-item>
|
|
151
|
+
<ui-carousel-item>...</ui-carousel-item>
|
|
152
|
+
<ui-carousel-item>...</ui-carousel-item>
|
|
153
|
+
</ui-carousel-content>
|
|
154
|
+
</ui-carousel>
|
|
155
|
+
|
|
156
|
+
<p>Slide {{ carouselRef.selectedIndex() + 1 }} of {{ carouselRef.slideCount() }}</p>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Use `(apiReady)` when you need the lower-level Embla API.
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
connectCarousel(api: CarouselApi): void {
|
|
163
|
+
api.on('select', () => {
|
|
164
|
+
this.currentSlide.set(api.selectedScrollSnap() + 1);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```html
|
|
170
|
+
<ui-carousel (apiReady)="connectCarousel($event)"> ... </ui-carousel>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Plugins
|
|
174
|
+
|
|
175
|
+
Optional Embla plugins are passed through `[plugins]`. Install plugin packages separately.
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
import Autoplay from 'embla-carousel-autoplay';
|
|
179
|
+
|
|
180
|
+
readonly plugins = [Autoplay({ delay: 2000 })];
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```html
|
|
184
|
+
<ui-carousel [plugins]="plugins">
|
|
185
|
+
<ui-carousel-content>
|
|
186
|
+
<ui-carousel-item>...</ui-carousel-item>
|
|
187
|
+
</ui-carousel-content>
|
|
188
|
+
</ui-carousel>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### RTL
|
|
192
|
+
|
|
193
|
+
Set both `dir="rtl"` and `opts.direction` so layout direction and Embla motion agree.
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
readonly rtlOptions: CarouselOptions = { direction: 'rtl' };
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
```html
|
|
200
|
+
<section dir="rtl">
|
|
201
|
+
<ui-carousel dir="rtl" [opts]="rtlOptions" class="w-full max-w-xs">
|
|
202
|
+
<ui-carousel-content>
|
|
203
|
+
<ui-carousel-item>...</ui-carousel-item>
|
|
204
|
+
<ui-carousel-item>...</ui-carousel-item>
|
|
205
|
+
<ui-carousel-item>...</ui-carousel-item>
|
|
206
|
+
</ui-carousel-content>
|
|
207
|
+
<button ui-carousel-previous class="rtl:rotate-180"></button>
|
|
208
|
+
<button ui-carousel-next class="rtl:rotate-180"></button>
|
|
209
|
+
</ui-carousel>
|
|
210
|
+
</section>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## API reference
|
|
214
|
+
|
|
215
|
+
### `CarouselComponent`
|
|
216
|
+
|
|
217
|
+
| Input or output | Type | Default |
|
|
218
|
+
| --------------- | ---------------------------- | ---------------------------------------------- |
|
|
219
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` |
|
|
220
|
+
| `opts` | `CarouselOptions` | `{}` |
|
|
221
|
+
| `plugins` | `readonly CarouselPlugin[]` | `[]` |
|
|
222
|
+
| `keyboard` | `boolean` | `true` |
|
|
223
|
+
| `label` | `string` | `'Carousel'` |
|
|
224
|
+
| `class` | `string` | `''` |
|
|
225
|
+
| `apiReady` | `CarouselApi` output | emitted after Embla initializes |
|
|
226
|
+
| `apiChange` | `CarouselApi \| null` output | emitted when Embla initializes or is destroyed |
|
|
227
|
+
|
|
228
|
+
### Root signals
|
|
229
|
+
|
|
230
|
+
| Signal | Type |
|
|
231
|
+
| --------------- | ----------------- |
|
|
232
|
+
| `selectedIndex` | `Signal<number>` |
|
|
233
|
+
| `slideCount` | `Signal<number>` |
|
|
234
|
+
| `canScrollPrev` | `Signal<boolean>` |
|
|
235
|
+
| `canScrollNext` | `Signal<boolean>` |
|
|
236
|
+
|
|
237
|
+
### Parts
|
|
238
|
+
|
|
239
|
+
| Part | Inputs |
|
|
240
|
+
| ------------------------------ | -------------------- |
|
|
241
|
+
| `ui-carousel-content` | `class` |
|
|
242
|
+
| `ui-carousel-item` | `ariaLabel`, `class` |
|
|
243
|
+
| `button[ui-carousel-previous]` | `label`, `class` |
|
|
244
|
+
| `button[ui-carousel-next]` | `label`, `class` |
|
|
245
|
+
|
|
246
|
+
## Styling and theming
|
|
247
|
+
|
|
248
|
+
The primitive follows the shadcn layout recipe: root is `relative`, content owns the overflow-hidden viewport, and items are `basis-full` by default. Pass `class` to tune width, item basis, spacing, and control placement.
|
|
249
|
+
|
|
250
|
+
Use theme-aware classes such as `border-border`, `bg-card`, `text-foreground`, `bg-background`, `hover:bg-accent`, and `focus-visible:ring-ring` for custom slide and control styling.
|
|
251
|
+
|
|
252
|
+
## Accessibility
|
|
253
|
+
|
|
254
|
+
- The root renders as a labelled `region` with `aria-roledescription="carousel"`.
|
|
255
|
+
- Previous and next controls are native buttons with descriptive labels and disabled states.
|
|
256
|
+
- Each item renders as a `group` with `aria-roledescription="slide"`; pass `ariaLabel` when the slide needs an explicit spoken label.
|
|
257
|
+
- Keep interactive controls inside slides reachable by normal tab order and avoid hiding focus outlines.
|
|
258
|
+
|
|
259
|
+
## Keyboard interactions
|
|
260
|
+
|
|
261
|
+
- The root is focusable when `keyboard` is `true`.
|
|
262
|
+
- Horizontal carousels use ArrowLeft and ArrowRight to move between slides.
|
|
263
|
+
- Vertical carousels use ArrowUp and ArrowDown to move between slides.
|
|
264
|
+
- Home moves to the first slide and End moves to the last slide.
|
|
265
|
+
- Previous and next buttons also support native Enter and Space activation.
|
|
266
|
+
|
|
267
|
+
## Angular notes
|
|
268
|
+
|
|
269
|
+
- This implementation uses vanilla Embla Carousel rather than `embla-carousel-react`.
|
|
270
|
+
- Embla initializes only in the browser, so server rendering can produce the static slide markup without touching the DOM API.
|
|
271
|
+
- `orientation` controls Embla axis; if `opts.axis` is provided, the Angular `orientation` input wins.
|
|
272
|
+
- Define `opts` and `plugins` as class properties to avoid unnecessary Embla reinitialization.
|
|
273
|
+
|
|
274
|
+
## Source parity
|
|
275
|
+
|
|
276
|
+
This Angular implementation follows the shadcn Carousel anatomy, options, API, events, plugins, and RTL guidance while translating React props into Angular inputs, outputs, signals, and standalone component imports.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Checkbox
|
|
2
|
+
|
|
3
|
+
A shadcn-style checkbox primitive built on Angular Material's `mat-checkbox`, with full `ControlValueAccessor` support for `ngModel` and reactive forms.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { CheckboxComponent } from '@ojiepermana/angular/component/checkbox';
|
|
9
|
+
import { LabelComponent } from '@ojiepermana/angular/component/label';
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
Wrap the control and copy in `label[ui-label]` when the text should toggle the checkbox. This is the closest Angular equivalent to the upstream shadcn `Field` + `FieldLabel` composition.
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<label ui-label class="flex items-start gap-3">
|
|
18
|
+
<ui-checkbox [(ngModel)]="accepted" name="terms-checkbox" class="mt-0.5" />
|
|
19
|
+
<span class="grid gap-1 leading-none">
|
|
20
|
+
<span>Accept terms and conditions</span>
|
|
21
|
+
<span class="text-sm font-normal leading-5 text-muted-foreground">
|
|
22
|
+
By clicking this checkbox, you agree to the terms.
|
|
23
|
+
</span>
|
|
24
|
+
</span>
|
|
25
|
+
</label>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Reactive forms continue to work through the component's value accessor.
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<ui-checkbox [formControl]="form.controls.marketingOptIn" aria-label="Receive product updates" />
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Common patterns
|
|
35
|
+
|
|
36
|
+
### Controlled checked state
|
|
37
|
+
|
|
38
|
+
Use `checkedChange` when the parent owns the signal and you want to keep the checkbox controlled without wrapping a form model.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const marketingOptIn = signal(false);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```html
|
|
45
|
+
<ui-checkbox
|
|
46
|
+
[ngModel]="marketingOptIn()"
|
|
47
|
+
(checkedChange)="marketingOptIn.set($event)"
|
|
48
|
+
aria-label="Receive product updates" />
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Invalid state
|
|
52
|
+
|
|
53
|
+
Forward `aria-invalid="true"` to switch the control into the destructive invalid treatment.
|
|
54
|
+
|
|
55
|
+
```html
|
|
56
|
+
<label ui-label class="flex items-start gap-3 text-destructive">
|
|
57
|
+
<ui-checkbox aria-invalid="true" name="terms-checkbox-invalid" class="mt-0.5" />
|
|
58
|
+
<span class="grid gap-1 leading-none">
|
|
59
|
+
<span>Accept terms and conditions</span>
|
|
60
|
+
<span class="text-sm font-normal leading-5 text-muted-foreground">
|
|
61
|
+
Choose this before continuing to checkout.
|
|
62
|
+
</span>
|
|
63
|
+
</span>
|
|
64
|
+
</label>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Grouped preferences
|
|
68
|
+
|
|
69
|
+
Use a semantic `fieldset` for a checkbox list.
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<fieldset class="space-y-3">
|
|
73
|
+
<legend class="text-sm font-semibold">Show these items on the desktop:</legend>
|
|
74
|
+
@for (item of groupOptions; track item.id) {
|
|
75
|
+
<label ui-label class="flex items-start gap-3">
|
|
76
|
+
<ui-checkbox
|
|
77
|
+
[ngModel]="desktopItems().has(item.id)"
|
|
78
|
+
(checkedChange)="toggleDesktopItem(item.id, $event)"
|
|
79
|
+
[name]="item.id"
|
|
80
|
+
class="mt-0.5" />
|
|
81
|
+
<span class="font-normal">{{ item.label }}</span>
|
|
82
|
+
</label>
|
|
83
|
+
}
|
|
84
|
+
</fieldset>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Table row selection
|
|
88
|
+
|
|
89
|
+
Use `indeterminate` for the select-all checkbox and set `data-state="selected"` on the selected row so the table highlight stays in sync.
|
|
90
|
+
|
|
91
|
+
```html
|
|
92
|
+
<ui-checkbox
|
|
93
|
+
[ngModel]="selectAll()"
|
|
94
|
+
[indeterminate]="someRowsSelected()"
|
|
95
|
+
aria-label="Select all team members"
|
|
96
|
+
(checkedChange)="handleSelectAll($event)" />
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## API reference
|
|
100
|
+
|
|
101
|
+
| Input | Type | Default | Notes |
|
|
102
|
+
| ------------------ | --------------------------- | -------------- | ------------------------------------------------------------------------ |
|
|
103
|
+
| `id` | `string` | auto-generated | Forwarded to Angular Material; the native input id becomes `<id>-input`. |
|
|
104
|
+
| `name` | `string \| null` | `null` | Forwarded to the native checkbox input. |
|
|
105
|
+
| `indeterminate` | `boolean` | `false` | Useful for select-all patterns. |
|
|
106
|
+
| `required` | `boolean` | `false` | Forwarded to the native input. |
|
|
107
|
+
| `aria-label` | `string \| null` | `null` | Use for icon-only or table selection checkboxes. |
|
|
108
|
+
| `aria-describedby` | `string \| null` | `null` | Links helper or error text outside the checkbox. |
|
|
109
|
+
| `aria-labelledby` | `string \| null` | `null` | Alternative to projected text or wrapper labels. |
|
|
110
|
+
| `aria-invalid` | `boolean \| string \| null` | `null` | Switches the control into the destructive invalid treatment. |
|
|
111
|
+
| `class` | `string` | `''` | Adds utility classes to the wrapped Material checkbox host. |
|
|
112
|
+
|
|
113
|
+
| Output | Payload |
|
|
114
|
+
| --------------- | --------- |
|
|
115
|
+
| `checkedChange` | `boolean` |
|
|
116
|
+
|
|
117
|
+
Public method: `focus()`.
|
|
118
|
+
|
|
119
|
+
## Styling and theming
|
|
120
|
+
|
|
121
|
+
The component uses a Material bridge so Angular Material's structural markup is restyled through the library theme tokens.
|
|
122
|
+
|
|
123
|
+
- Default borders and icon color use the shared `input` and `primary` tokens.
|
|
124
|
+
- `aria-invalid="true"` switches the control to the `destructive` token set.
|
|
125
|
+
- Pass spacing or alignment utilities through the `class` input when the checkbox needs to align with multi-line content.
|
|
126
|
+
|
|
127
|
+
Extra visual adjustments belong in `checkbox.component.css` inside the library.
|
|
128
|
+
|
|
129
|
+
## Accessibility
|
|
130
|
+
|
|
131
|
+
- Always give the checkbox an accessible name via wrapper label text, projected content, `aria-label`, or `aria-labelledby`.
|
|
132
|
+
- Use `aria-describedby` to connect helper or error text rendered outside the checkbox.
|
|
133
|
+
- Use `indeterminate` for tri-state list headers and table select-all behavior.
|
|
134
|
+
- `required` and `name` are forwarded to the underlying native input.
|
|
135
|
+
|
|
136
|
+
## Keyboard interactions
|
|
137
|
+
|
|
138
|
+
- `Tab` moves focus to the checkbox in DOM order.
|
|
139
|
+
- `Space` toggles the checked state.
|
|
140
|
+
|
|
141
|
+
## Angular notes
|
|
142
|
+
|
|
143
|
+
- `ngModel`, reactive forms, and `checkedChange` all work with the same primitive.
|
|
144
|
+
- When you pass `id`, Angular Material derives the native input id as `<id>-input`; wrapper labels or `aria-labelledby` are usually the simplest Angular pattern.
|
|
145
|
+
- Projected text still works if you prefer `<ui-checkbox>Accept</ui-checkbox>` for compact markup.
|
|
146
|
+
|
|
147
|
+
## Source parity
|
|
148
|
+
|
|
149
|
+
This Angular implementation follows the shadcn Checkbox examples while mapping the upstream `Field` helpers to Angular-native label wrappers, `fieldset` composition, semantic table rows, and signal-friendly state handling.
|