@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
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Composer
|
|
2
|
+
|
|
3
|
+
Configurable prompt composer for chat, AI, and message surfaces.
|
|
4
|
+
|
|
5
|
+
This Angular implementation takes conceptual inspiration from Flux Composer, but the API, styling, and composition model follow the shadcn-style patterns already established in this library.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
ComposerActionsLeadingComponent,
|
|
12
|
+
ComposerActionsTrailingComponent,
|
|
13
|
+
ComposerComponent,
|
|
14
|
+
ComposerFooterComponent,
|
|
15
|
+
ComposerHeaderComponent,
|
|
16
|
+
ComposerInputComponent,
|
|
17
|
+
ComposerTextareaComponent,
|
|
18
|
+
} from '@ojiepermana/angular-component/composer';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Composition
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
Composer
|
|
25
|
+
├── ComposerHeader (optional)
|
|
26
|
+
├── ComposerActionsLeading (optional)
|
|
27
|
+
├── textarea[ComposerInput] or ComposerInput
|
|
28
|
+
├── ComposerActionsTrailing (optional)
|
|
29
|
+
└── ComposerFooter (optional)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Use `textarea[ComposerInput]` when you want native textarea behavior, template-driven forms, or reactive forms.
|
|
33
|
+
Use `ComposerInput` when you want to project a custom rich-text surface or editor shell.
|
|
34
|
+
|
|
35
|
+
## Basic Usage
|
|
36
|
+
|
|
37
|
+
```html
|
|
38
|
+
<form (ngSubmit)="send()">
|
|
39
|
+
<Composer (submitRequested)="send()">
|
|
40
|
+
<textarea
|
|
41
|
+
ComposerInput
|
|
42
|
+
rows="2"
|
|
43
|
+
maxRows="6"
|
|
44
|
+
placeholder="How can I help you today?"
|
|
45
|
+
[(ngModel)]="prompt"
|
|
46
|
+
name="prompt"></textarea>
|
|
47
|
+
|
|
48
|
+
<ComposerActionsTrailing>
|
|
49
|
+
<button Button type="submit" size="icon-sm" aria-label="Send prompt">Send</button>
|
|
50
|
+
</ComposerActionsTrailing>
|
|
51
|
+
</Composer>
|
|
52
|
+
</form>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## With Header
|
|
56
|
+
|
|
57
|
+
Use the header slot for file previews, avatars, upload summaries, or compact context cards.
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<Composer>
|
|
61
|
+
<ComposerHeader>
|
|
62
|
+
<div class="flex items-center gap-3 rounded-lg border border-border bg-muted/40 px-3 py-2">
|
|
63
|
+
<img src="/avatar.png" alt="Uploaded file owner" class="size-10 rounded-md object-cover" />
|
|
64
|
+
<div class="min-w-0">
|
|
65
|
+
<p class="truncate text-sm font-medium">profile-shot.png</p>
|
|
66
|
+
<p class="text-xs text-muted-foreground">2.1 MB</p>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</ComposerHeader>
|
|
70
|
+
|
|
71
|
+
<textarea ComposerInput rows="2" maxRows="6" placeholder="Ask about this asset..."></textarea>
|
|
72
|
+
|
|
73
|
+
<ComposerActionsTrailing>
|
|
74
|
+
<button Button type="button" size="sm">Send</button>
|
|
75
|
+
</ComposerActionsTrailing>
|
|
76
|
+
</Composer>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Inline Layout
|
|
80
|
+
|
|
81
|
+
Set `inline` when actions and input should share a compact single-row shell.
|
|
82
|
+
|
|
83
|
+
```html
|
|
84
|
+
<Composer inline submit="enter">
|
|
85
|
+
<ComposerActionsLeading>
|
|
86
|
+
<button Button type="button" variant="ghost" size="icon-sm" aria-label="Add attachment">+</button>
|
|
87
|
+
</ComposerActionsLeading>
|
|
88
|
+
|
|
89
|
+
<textarea ComposerInput rows="1" maxRows="4" placeholder="Reply..."></textarea>
|
|
90
|
+
|
|
91
|
+
<ComposerActionsTrailing>
|
|
92
|
+
<button Button type="button" variant="secondary" size="icon-sm" aria-label="Voice input">Mic</button>
|
|
93
|
+
<button Button type="submit" size="icon-sm" aria-label="Send reply">Send</button>
|
|
94
|
+
</ComposerActionsTrailing>
|
|
95
|
+
</Composer>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Input Variant
|
|
99
|
+
|
|
100
|
+
Set `variant="input"` when the composer should align more closely with the radius and density of other input surfaces.
|
|
101
|
+
|
|
102
|
+
```html
|
|
103
|
+
<Composer variant="input">
|
|
104
|
+
<textarea ComposerInput rows="2" maxRows="5" placeholder="What's on your mind?"></textarea>
|
|
105
|
+
</Composer>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Height And Autosize
|
|
109
|
+
|
|
110
|
+
`rows` sets the initial height of the native textarea surface.
|
|
111
|
+
`maxRows` caps autosize growth before the textarea begins scrolling.
|
|
112
|
+
|
|
113
|
+
```html
|
|
114
|
+
<Composer>
|
|
115
|
+
<textarea ComposerInput rows="3" maxRows="8" placeholder="Write a longer prompt..."></textarea>
|
|
116
|
+
</Composer>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Submit Behavior
|
|
120
|
+
|
|
121
|
+
By default, keyboard submission uses `cmd-enter`, which means `Cmd+Enter` on macOS and `Ctrl+Enter` on Windows/Linux.
|
|
122
|
+
|
|
123
|
+
Use `submit="enter"` when pressing `Enter` should submit immediately. `Shift+Enter` still inserts a new line.
|
|
124
|
+
|
|
125
|
+
```html
|
|
126
|
+
<Composer submit="enter" (submitRequested)="send()">
|
|
127
|
+
<textarea ComposerInput rows="1" maxRows="4" placeholder="Quick reply..."></textarea>
|
|
128
|
+
</Composer>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
When `submitForm` stays `true`, the composer also calls the nearest native `form.requestSubmit()` after emitting `submitRequested`.
|
|
132
|
+
|
|
133
|
+
## Rich Text Slot
|
|
134
|
+
|
|
135
|
+
Project a custom editor shell through `ComposerInput` when you want a richer surface without changing the composer container API.
|
|
136
|
+
|
|
137
|
+
```html
|
|
138
|
+
<Composer>
|
|
139
|
+
<ComposerInput>
|
|
140
|
+
<div
|
|
141
|
+
contenteditable="true"
|
|
142
|
+
role="textbox"
|
|
143
|
+
aria-multiline="true"
|
|
144
|
+
class="min-h-20 w-full rounded-sm bg-transparent px-2 py-2 outline-none">
|
|
145
|
+
Draft a richer message...
|
|
146
|
+
</div>
|
|
147
|
+
</ComposerInput>
|
|
148
|
+
|
|
149
|
+
<ComposerActionsLeading>
|
|
150
|
+
<button Button type="button" variant="ghost" size="icon-sm" aria-label="Insert file">+</button>
|
|
151
|
+
</ComposerActionsLeading>
|
|
152
|
+
|
|
153
|
+
<ComposerActionsTrailing>
|
|
154
|
+
<button Button type="button" size="icon-sm" aria-label="Send rich text message">Send</button>
|
|
155
|
+
</ComposerActionsTrailing>
|
|
156
|
+
</Composer>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Disabled And Invalid
|
|
160
|
+
|
|
161
|
+
Use the root inputs for shared composer state.
|
|
162
|
+
|
|
163
|
+
```html
|
|
164
|
+
<Composer [disabled]="isSending" [invalid]="promptInvalid">
|
|
165
|
+
<textarea ComposerInput rows="2" maxRows="6"></textarea>
|
|
166
|
+
</Composer>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
For native textarea integrations, `aria-invalid` can still be supplied directly on `textarea[ComposerInput]` when state comes from another layer.
|
|
170
|
+
|
|
171
|
+
## API Reference
|
|
172
|
+
|
|
173
|
+
### `ComposerComponent`
|
|
174
|
+
|
|
175
|
+
| Input | Type | Default | Notes |
|
|
176
|
+
| ------------ | ------------------------ | ------------- | --------------------------------------------------------------------- |
|
|
177
|
+
| `variant` | `'default' \| 'input'` | `'default'` | Controls shell radius and density. |
|
|
178
|
+
| `inline` | `boolean` | `false` | Places leading actions, input, and trailing actions in a compact row. |
|
|
179
|
+
| `disabled` | `boolean` | `false` | Applies shared disabled treatment and prevents keyboard submit. |
|
|
180
|
+
| `invalid` | `boolean` | `false` | Applies destructive border and ring treatment. |
|
|
181
|
+
| `submit` | `'cmd-enter' \| 'enter'` | `'cmd-enter'` | Controls keyboard submit behavior. |
|
|
182
|
+
| `submitForm` | `boolean` | `true` | Requests native form submission after `submitRequested`. |
|
|
183
|
+
| `class` | `string` | `''` | Appends classes to the composer host. |
|
|
184
|
+
|
|
185
|
+
### `ComposerComponent` output
|
|
186
|
+
|
|
187
|
+
| Output | Type | Notes |
|
|
188
|
+
| ----------------- | --------------- | --------------------------------------------------------------------- |
|
|
189
|
+
| `submitRequested` | `KeyboardEvent` | Emitted when keyboard submission is triggered from the input surface. |
|
|
190
|
+
|
|
191
|
+
### `ComposerTextareaComponent`
|
|
192
|
+
|
|
193
|
+
| Input | Type | Default | Notes |
|
|
194
|
+
| -------------- | ---------------- | ------- | --------------------------------------------------- |
|
|
195
|
+
| `rows` | `number` | `2` | Initial line count. |
|
|
196
|
+
| `maxRows` | `number \| null` | `null` | Maximum autosize line count before scrolling. |
|
|
197
|
+
| `disabled` | `boolean` | `false` | Disables the textarea even if the root is enabled. |
|
|
198
|
+
| `aria-invalid` | `boolean` | `false` | Applies invalid styling on the textarea input slot. |
|
|
199
|
+
| `class` | `string` | `''` | Appends classes to the textarea host. |
|
|
200
|
+
|
|
201
|
+
### Slot Components
|
|
202
|
+
|
|
203
|
+
`ComposerHeaderComponent`, `ComposerFooterComponent`, `ComposerActionsLeadingComponent`, `ComposerActionsTrailingComponent`, and `ComposerInputComponent` each accept a single `class` input.
|
|
204
|
+
|
|
205
|
+
## Accessibility
|
|
206
|
+
|
|
207
|
+
- Pair `textarea[ComposerInput]` with an accessible label from surrounding copy, `aria-label`, or form-field composition.
|
|
208
|
+
- Give icon-only buttons inside action slots explicit `aria-label` values.
|
|
209
|
+
- Keep meaningful supporting text in `ComposerFooter` or nearby description content.
|
|
210
|
+
- Use `submit="enter"` carefully in multiline experiences so users still understand `Shift+Enter` for a new line.
|
|
211
|
+
- When projecting a custom editor through `ComposerInput`, provide `role="textbox"`, `aria-multiline="true"`, and clear keyboard handling in the editor surface itself.
|
|
212
|
+
|
|
213
|
+
## Keyboard Interactions
|
|
214
|
+
|
|
215
|
+
- `Cmd+Enter` or `Ctrl+Enter` submits by default.
|
|
216
|
+
- `Enter` submits when `submit="enter"`.
|
|
217
|
+
- `Shift+Enter` keeps inserting new lines in `submit="enter"` mode.
|
|
218
|
+
- Standard textarea editing, selection, clipboard, and IME behavior remain native when using `textarea[ComposerInput]`.
|
|
219
|
+
|
|
220
|
+
## Styling And Theming
|
|
221
|
+
|
|
222
|
+
The composer uses the shared `border-input`, `bg-background`, `ring-ring`, `text-foreground`, and `placeholder:text-muted-foreground` tokens.
|
|
223
|
+
|
|
224
|
+
Override layout and density by passing `class` to the root or individual slot components.
|
|
225
|
+
Keep the projected input surface transparent so the composer shell remains the visible boundary.
|
|
226
|
+
|
|
227
|
+
## Angular Notes
|
|
228
|
+
|
|
229
|
+
- `textarea[ComposerInput]` remains a native textarea surface and autosizes without replacing native browser editing behavior.
|
|
230
|
+
- `ComposerInput` is intentionally presentational. It is meant for custom editor projection, not as a built-in rich-text editor.
|
|
231
|
+
- The composer shell is presentational and does not import layout or theme services, which keeps the entrypoint compatible with the library's component-layer rules.
|
|
232
|
+
|
|
233
|
+
## Source Inspiration
|
|
234
|
+
|
|
235
|
+
This component borrows the high-level idea of a structured prompt composer from Flux Composer, but the Angular API, slot names, styling tokens, keyboard behavior contract, and examples are implemented specifically for this library's shadcn-style component system.
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# Context Menu
|
|
2
|
+
|
|
3
|
+
Displays a menu of actions triggered by a right click. The Angular version maps the
|
|
4
|
+
shadcn `ContextMenuTrigger` to a directive on any focusable host and reuses the
|
|
5
|
+
shared menu primitives from [Dropdown Menu](../dropdown-menu/README.md) for the
|
|
6
|
+
overlay content.
|
|
7
|
+
|
|
8
|
+
## Import
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { ContextMenuTriggerDirective } from '@ojiepermana/angular-component/context-menu';
|
|
12
|
+
import {
|
|
13
|
+
MenuCheckboxItemComponent,
|
|
14
|
+
MenuContentDirective,
|
|
15
|
+
MenuGroupComponent,
|
|
16
|
+
MenuItemComponent,
|
|
17
|
+
MenuLabelComponent,
|
|
18
|
+
MenuRadioGroupComponent,
|
|
19
|
+
MenuRadioItemComponent,
|
|
20
|
+
MenuSeparatorComponent,
|
|
21
|
+
MenuShortcutComponent,
|
|
22
|
+
MenuSurfaceComponent,
|
|
23
|
+
MenuTriggerDirective,
|
|
24
|
+
} from '@ojiepermana/angular-component/dropdown-menu';
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Composition
|
|
28
|
+
|
|
29
|
+
The Angular composition stays close to shadcn while exposing the root behavior
|
|
30
|
+
through directives and shared menu primitives.
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
div[ContextMenuTrigger]
|
|
34
|
+
└── ng-template[MenuContent]
|
|
35
|
+
└── MenuSurface
|
|
36
|
+
├── MenuGroup
|
|
37
|
+
│ └── button[MenuItem]
|
|
38
|
+
├── MenuSeparator
|
|
39
|
+
├── button[MenuCheckboxItem]
|
|
40
|
+
├── MenuLabel
|
|
41
|
+
├── MenuRadioGroup
|
|
42
|
+
│ └── button[MenuRadioItem]
|
|
43
|
+
└── button[MenuItem][MenuTrigger]
|
|
44
|
+
└── ng-template[MenuContent]
|
|
45
|
+
└── MenuSurface
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Basic usage
|
|
49
|
+
|
|
50
|
+
Prefer a focusable trigger so pointer users can right-click and keyboard users can
|
|
51
|
+
open the same menu with Shift+F10 or the context-menu key.
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<div
|
|
55
|
+
[ContextMenuTrigger]="menu"
|
|
56
|
+
tabindex="0"
|
|
57
|
+
aria-label="Open file actions"
|
|
58
|
+
class="flex h-32 w-full max-w-sm items-center justify-center rounded-xl border border-dashed border-border">
|
|
59
|
+
Right-click here
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<ng-template MenuContent #menu="MenuContent">
|
|
63
|
+
<MenuSurface class="w-56">
|
|
64
|
+
<MenuGroup>
|
|
65
|
+
<button MenuItem>Profile</button>
|
|
66
|
+
<button MenuItem>Billing</button>
|
|
67
|
+
<button MenuItem>Team</button>
|
|
68
|
+
</MenuGroup>
|
|
69
|
+
</MenuSurface>
|
|
70
|
+
</ng-template>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Common patterns
|
|
74
|
+
|
|
75
|
+
### Browser-style actions
|
|
76
|
+
|
|
77
|
+
Use grouped commands and shortcuts for browser or editor surfaces.
|
|
78
|
+
|
|
79
|
+
```html
|
|
80
|
+
<MenuSurface class="w-60">
|
|
81
|
+
<MenuGroup>
|
|
82
|
+
<button MenuItem>
|
|
83
|
+
Back
|
|
84
|
+
<span MenuShortcut>⌘[</span>
|
|
85
|
+
</button>
|
|
86
|
+
<button MenuItem [disabled]="true">
|
|
87
|
+
Forward
|
|
88
|
+
<span MenuShortcut>⌘]</span>
|
|
89
|
+
</button>
|
|
90
|
+
<button MenuItem>
|
|
91
|
+
Reload
|
|
92
|
+
<span MenuShortcut>⌘R</span>
|
|
93
|
+
</button>
|
|
94
|
+
</MenuGroup>
|
|
95
|
+
</MenuSurface>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Nested tools menu
|
|
99
|
+
|
|
100
|
+
Map shadcn `ContextMenuSub` to `MenuTrigger` on a menu item. This keeps submenu
|
|
101
|
+
composition explicit and works with the same overlay primitive used by dropdown menus.
|
|
102
|
+
|
|
103
|
+
```html
|
|
104
|
+
<ng-template MenuContent #moreTools="MenuContent">
|
|
105
|
+
<MenuSurface class="w-48">
|
|
106
|
+
<button MenuItem>Save Page...</button>
|
|
107
|
+
<button MenuItem>Create Shortcut...</button>
|
|
108
|
+
<button MenuItem>Name Window...</button>
|
|
109
|
+
</MenuSurface>
|
|
110
|
+
</ng-template>
|
|
111
|
+
|
|
112
|
+
<MenuSurface class="w-56">
|
|
113
|
+
<button MenuItem [MenuTrigger]="moreTools" side="right" align="start">
|
|
114
|
+
More tools
|
|
115
|
+
<span class="ml-auto text-xs text-muted-foreground">›</span>
|
|
116
|
+
</button>
|
|
117
|
+
</MenuSurface>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Checkbox rows
|
|
121
|
+
|
|
122
|
+
Use checkbox rows for independent toggles such as browser preferences.
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
const showBookmarks = signal(true);
|
|
126
|
+
const showFullUrls = signal(false);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```html
|
|
130
|
+
<MenuSurface class="w-56">
|
|
131
|
+
<MenuGroup>
|
|
132
|
+
<button MenuCheckboxItem [(checked)]="showBookmarks">Show bookmarks</button>
|
|
133
|
+
<button MenuCheckboxItem [(checked)]="showFullUrls">Show full URLs</button>
|
|
134
|
+
</MenuGroup>
|
|
135
|
+
</MenuSurface>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Radio groups
|
|
139
|
+
|
|
140
|
+
Use `MenuRadioGroup` when the menu exposes an exclusive choice.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const selectedTheme = signal<'light' | 'dark' | 'system'>('system');
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```html
|
|
147
|
+
<MenuSurface class="w-56">
|
|
148
|
+
<MenuLabel [inset]="true">Theme</MenuLabel>
|
|
149
|
+
<MenuRadioGroup [(value)]="selectedTheme">
|
|
150
|
+
<button MenuRadioItem value="light">Light</button>
|
|
151
|
+
<button MenuRadioItem value="dark">Dark</button>
|
|
152
|
+
<button MenuRadioItem value="system">System</button>
|
|
153
|
+
</MenuRadioGroup>
|
|
154
|
+
</MenuSurface>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Destructive rows
|
|
158
|
+
|
|
159
|
+
Pass `variant="destructive"` when the row should read as irreversible.
|
|
160
|
+
|
|
161
|
+
```html
|
|
162
|
+
<MenuSurface class="w-56">
|
|
163
|
+
<MenuGroup>
|
|
164
|
+
<button MenuItem>Edit</button>
|
|
165
|
+
<button MenuItem>Share</button>
|
|
166
|
+
</MenuGroup>
|
|
167
|
+
<MenuSeparator />
|
|
168
|
+
<MenuGroup>
|
|
169
|
+
<button MenuItem variant="destructive">Delete</button>
|
|
170
|
+
</MenuGroup>
|
|
171
|
+
</MenuSurface>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### RTL
|
|
175
|
+
|
|
176
|
+
Set `dir="rtl"` on the surface and prefer `side="left"` for nested menus so the
|
|
177
|
+
submenu opens toward the visual start edge.
|
|
178
|
+
|
|
179
|
+
```html
|
|
180
|
+
<ng-template MenuContent #rtlTools="MenuContent">
|
|
181
|
+
<MenuSurface dir="rtl" lang="ar" class="w-44 text-right">
|
|
182
|
+
<button MenuItem>حفظ الصفحة...</button>
|
|
183
|
+
<button MenuItem>إنشاء اختصار...</button>
|
|
184
|
+
</MenuSurface>
|
|
185
|
+
</ng-template>
|
|
186
|
+
|
|
187
|
+
<MenuSurface dir="rtl" lang="ar" class="w-56 text-right">
|
|
188
|
+
<button MenuItem [MenuTrigger]="rtlTools" side="left" align="start">
|
|
189
|
+
المزيد من الأدوات
|
|
190
|
+
<span class="mr-auto text-xs text-muted-foreground">‹</span>
|
|
191
|
+
</button>
|
|
192
|
+
</MenuSurface>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## API reference
|
|
196
|
+
|
|
197
|
+
### `[ContextMenuTrigger]`
|
|
198
|
+
|
|
199
|
+
| Input | Type | Default |
|
|
200
|
+
| -------------------- | ---------------------- | ---------- |
|
|
201
|
+
| `ContextMenuTrigger` | `MenuContentDirective` | _required_ |
|
|
202
|
+
| `disabled` | `boolean` | `false` |
|
|
203
|
+
|
|
204
|
+
Output: `openedChange: boolean`
|
|
205
|
+
|
|
206
|
+
Methods:
|
|
207
|
+
|
|
208
|
+
- `openAt(x, y)` opens the projected surface at the given viewport coordinates.
|
|
209
|
+
- `close()` closes the active overlay.
|
|
210
|
+
|
|
211
|
+
### Shared menu parts
|
|
212
|
+
|
|
213
|
+
| Part | Purpose |
|
|
214
|
+
| -------------------------- | ---------------------------------------------------------------------------------------- |
|
|
215
|
+
| `MenuSurface` | Overlay container with roving focus, typeahead, Escape close, and Tab-to-close behavior. |
|
|
216
|
+
| `MenuGroup` | Lightweight grouping wrapper for related rows. |
|
|
217
|
+
| `button[MenuItem]` | Standard command row with optional `variant="destructive"`. |
|
|
218
|
+
| `button[MenuCheckboxItem]` | Toggle row with `[(checked)]` and `role="menuitemcheckbox"`. |
|
|
219
|
+
| `MenuRadioGroup` | Exclusive-selection container with signal-friendly `[(value)]`. |
|
|
220
|
+
| `button[MenuRadioItem]` | Exclusive option row with `role="menuitemradio"`. |
|
|
221
|
+
| `MenuLabel` | Non-interactive label row. |
|
|
222
|
+
| `MenuShortcut` | Right-aligned shortcut hint. |
|
|
223
|
+
| `MenuSeparator` | Visual divider between command groups. |
|
|
224
|
+
|
|
225
|
+
For lower-level behavior details, see the Radix Context Menu API reference:
|
|
226
|
+
<https://www.radix-ui.com/docs/primitives/components/context-menu#api-reference>.
|
|
227
|
+
|
|
228
|
+
## Styling and theming
|
|
229
|
+
|
|
230
|
+
Pass `class` to `MenuSurface`, `MenuGroup`, labels, and items to tune width,
|
|
231
|
+
spacing, alignment, and emphasis. Destructive rows use the shared `destructive` theme
|
|
232
|
+
tokens. Visible dividers and borders follow the shared border tokens rather than the
|
|
233
|
+
current text color.
|
|
234
|
+
|
|
235
|
+
## Accessibility
|
|
236
|
+
|
|
237
|
+
- Make the trigger focusable if keyboard users need access to the menu.
|
|
238
|
+
- Native `contextmenu` is captured and its default prevented.
|
|
239
|
+
- Menu rows expose the expected `menuitem`, `menuitemcheckbox`, and `menuitemradio`
|
|
240
|
+
roles plus `aria-checked` and `aria-disabled` where appropriate.
|
|
241
|
+
- Keep item labels short and descriptive, and avoid placing unrelated interactive
|
|
242
|
+
controls inside a menu row.
|
|
243
|
+
|
|
244
|
+
## Keyboard interactions
|
|
245
|
+
|
|
246
|
+
- Right click opens the menu at the pointer location.
|
|
247
|
+
- Shift+F10 and the context-menu key open the menu at the trigger center when the
|
|
248
|
+
trigger is focusable.
|
|
249
|
+
- Arrow Up and Arrow Down move between enabled items.
|
|
250
|
+
- Home and End jump to the start or end of the list.
|
|
251
|
+
- Typeahead matches row text.
|
|
252
|
+
- Enter and Space activate the focused row.
|
|
253
|
+
- Escape and Tab close the surface.
|
|
254
|
+
|
|
255
|
+
## Angular notes
|
|
256
|
+
|
|
257
|
+
- There is no dedicated `context-menu` root component. The root behavior lives on the
|
|
258
|
+
`[ContextMenuTrigger]` directive plus `ng-template[MenuContent]`.
|
|
259
|
+
- Use signals with `[(checked)]` and `[(value)]` for checkbox and radio rows.
|
|
260
|
+
- Reuse `MenuTrigger` when a submenu is needed.
|
|
261
|
+
- Long-press support depends on the browser firing the native `contextmenu` event.
|
|
262
|
+
|
|
263
|
+
## Source parity
|
|
264
|
+
|
|
265
|
+
This implementation keeps the shadcn information architecture while mapping the trigger
|
|
266
|
+
to Angular directives, submenu behavior to nested `MenuTrigger` composition, and
|
|
267
|
+
checkbox or radio state to signal-friendly APIs.
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Date Picker
|
|
2
|
+
|
|
3
|
+
Popup date input backed by Angular Material `MatDatepicker`, styled through the shared shadcn token bridge.
|
|
4
|
+
|
|
5
|
+
The upstream shadcn Date Picker is composed from Popover and Calendar. This Angular library exposes `DatePicker` as the app-level wrapper while preserving the same product behavior: an input trigger, a calendar popup, selected state, and date constraints.
|
|
6
|
+
|
|
7
|
+
## Date adapter
|
|
8
|
+
|
|
9
|
+
The component self-provides a native `DateAdapter`, so it works out of the box in any consumer (including when federated into a host that never wired one). Only add an adapter at the app root for a custom adapter/locale:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { provideNativeDateAdapter } from '@angular/material/core';
|
|
13
|
+
|
|
14
|
+
export const appConfig = { providers: [provideNativeDateAdapter()] };
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Import
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { DatePickerComponent } from '@ojiepermana/angular-component/date-picker';
|
|
21
|
+
import { LabelComponent } from '@ojiepermana/angular-component/label';
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
Pair the picker with a visible label through the `id` input, then bind the selected date through the signal model.
|
|
27
|
+
|
|
28
|
+
```html
|
|
29
|
+
<label Label for="date-picker-demo">Date</label>
|
|
30
|
+
<DatePicker id="date-picker-demo" class="w-72" [(value)]="date" placeholder="Pick a date" />
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The component also implements `ControlValueAccessor`, so it can be used with reactive forms or `ngModel`.
|
|
34
|
+
|
|
35
|
+
## Common patterns
|
|
36
|
+
|
|
37
|
+
### Basic
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<label Label for="date-picker-simple">Date</label>
|
|
41
|
+
<DatePicker id="date-picker-simple" class="w-44" [(value)]="basicDate" placeholder="Pick a date" />
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Coordinated range
|
|
45
|
+
|
|
46
|
+
Use two date pickers when the product needs a start and end date but does not need a dedicated range-calendar primitive.
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<div class="grid gap-4 sm:grid-cols-2">
|
|
50
|
+
<div class="grid gap-2">
|
|
51
|
+
<label Label for="date-range-start">Start date</label>
|
|
52
|
+
<DatePicker id="date-range-start" [(value)]="rangeStart" [max]="rangeEnd()" />
|
|
53
|
+
</div>
|
|
54
|
+
<div class="grid gap-2">
|
|
55
|
+
<label Label for="date-range-end">End date</label>
|
|
56
|
+
<DatePicker id="date-range-end" [(value)]="rangeEnd" [min]="rangeStart()" />
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Date of birth
|
|
62
|
+
|
|
63
|
+
Use `startView="multi-year"` and `startAt` for dates that are usually far away from the current month.
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<label Label for="date-picker-birth">Date of birth</label>
|
|
67
|
+
<DatePicker
|
|
68
|
+
id="date-picker-birth"
|
|
69
|
+
class="w-48"
|
|
70
|
+
[(value)]="birthDate"
|
|
71
|
+
placeholder="Select date"
|
|
72
|
+
[startAt]="birthStartAt"
|
|
73
|
+
startView="multi-year" />
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Date plus time
|
|
77
|
+
|
|
78
|
+
Pair the date picker with a native `input[type="time"]` for date-time forms.
|
|
79
|
+
|
|
80
|
+
```html
|
|
81
|
+
<div class="grid gap-4 sm:grid-cols-[minmax(0,12rem)_8rem]">
|
|
82
|
+
<div class="grid gap-2">
|
|
83
|
+
<label Label for="date-picker-time-date">Date</label>
|
|
84
|
+
<DatePicker id="date-picker-time-date" [(value)]="timeDate" />
|
|
85
|
+
</div>
|
|
86
|
+
<div class="grid gap-2">
|
|
87
|
+
<label Label for="date-picker-time-input">Time</label>
|
|
88
|
+
<input id="date-picker-time-input" Input type="time" step="1" [value]="timeValue()" />
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Disabled dates
|
|
94
|
+
|
|
95
|
+
Use `min`, `max`, and `dateFilter` to prevent invalid choices in the calendar before submit.
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
readonly minBookingDate = new Date(2026, 0, 1);
|
|
99
|
+
readonly maxBookingDate = new Date(2026, 11, 31);
|
|
100
|
+
readonly weekdayFilter = (date: Date | null): boolean => {
|
|
101
|
+
const day = date?.getDay();
|
|
102
|
+
return day !== 0 && day !== 6;
|
|
103
|
+
};
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```html
|
|
107
|
+
<DatePicker
|
|
108
|
+
id="date-picker-subscription"
|
|
109
|
+
[(value)]="subscriptionDate"
|
|
110
|
+
[min]="minBookingDate"
|
|
111
|
+
[max]="maxBookingDate"
|
|
112
|
+
[dateFilter]="weekdayFilter" />
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### RTL
|
|
116
|
+
|
|
117
|
+
Set `dir="rtl"` on a wrapping container or at the application shell. The picker follows the surrounding direction.
|
|
118
|
+
|
|
119
|
+
```html
|
|
120
|
+
<div dir="rtl" lang="ar" class="grid max-w-xs gap-2 text-right">
|
|
121
|
+
<label Label for="date-picker-rtl">Date</label>
|
|
122
|
+
<DatePicker id="date-picker-rtl" [(value)]="rtlDate" placeholder="Pick a date" />
|
|
123
|
+
</div>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## API reference
|
|
127
|
+
|
|
128
|
+
| Input / Model | Type | Default |
|
|
129
|
+
| ----------------- | ----------------------------------- | -------------------- |
|
|
130
|
+
| `value` | `Date \| null` | `null` |
|
|
131
|
+
| `id` | `string \| null` | `null` |
|
|
132
|
+
| `placeholder` | `string` | `'Pick a date'` |
|
|
133
|
+
| `required` | `boolean` | `false` |
|
|
134
|
+
| `min` | `Date \| null` | `null` |
|
|
135
|
+
| `max` | `Date \| null` | `null` |
|
|
136
|
+
| `startAt` | `Date \| null` | `null` |
|
|
137
|
+
| `startView` | `'month' \| 'year' \| 'multi-year'` | `'month'` |
|
|
138
|
+
| `touchUi` | `boolean` | `false` |
|
|
139
|
+
| `dateFilter` | `(date: Date \| null) => boolean` | `null` |
|
|
140
|
+
| `panelClass` | `string \| string[]` | `'datepicker-panel'` |
|
|
141
|
+
| `aria-label` | `string \| null` | `null` |
|
|
142
|
+
| `aria-labelledby` | `string \| null` | `null` |
|
|
143
|
+
| `disabled` | `boolean` | `false` |
|
|
144
|
+
| `class` | `string` | `''` |
|
|
145
|
+
|
|
146
|
+
## Styling and theming
|
|
147
|
+
|
|
148
|
+
The host accepts `class` for width and layout utilities. The inner Material form field is full width, so classes such as `w-44`, `w-72`, and `w-full` should be placed on `DatePicker`.
|
|
149
|
+
|
|
150
|
+
The popup panel defaults to `datepicker-panel`, and calendar colors come from the shared Material token bridge for popover, foreground, primary, and ring colors.
|
|
151
|
+
|
|
152
|
+
## Accessibility
|
|
153
|
+
|
|
154
|
+
- Pair the date picker with a visible label using the `id` input, or provide `aria-label` / `aria-labelledby`.
|
|
155
|
+
- The input, toggle, popup focus management, calendar grid roles, and date keyboard navigation come from `MatDatepicker`.
|
|
156
|
+
- Use constraints such as `min`, `max`, and `dateFilter` when dates are not selectable.
|
|
157
|
+
- Keep the visible label specific, such as `Start date`, `End date`, or `Subscription date`.
|
|
158
|
+
|
|
159
|
+
## Keyboard interactions
|
|
160
|
+
|
|
161
|
+
- Tab moves focus to the date input and toggle button.
|
|
162
|
+
- Enter or Space on the toggle opens the calendar popup.
|
|
163
|
+
- Arrow keys navigate dates while the calendar is open.
|
|
164
|
+
- Enter selects the focused date.
|
|
165
|
+
- Escape closes the popup and returns focus to the trigger.
|
|
166
|
+
|
|
167
|
+
## Angular notes
|
|
168
|
+
|
|
169
|
+
- A native date adapter is built in; provide one at the application root only for a custom adapter/locale.
|
|
170
|
+
- Use `[(value)]` when binding to a signal-backed `Date | null` value.
|
|
171
|
+
- Use the component as a CVA inside Angular forms when validation and form submission belong to a `FormControl`.
|
|
172
|
+
- `startView="multi-year"` is useful for birthdays and other far-past dates.
|
|
173
|
+
- For range selection today, coordinate two `DatePicker` instances with reciprocal `min` and `max` bindings.
|
|
174
|
+
|
|
175
|
+
## Source parity
|
|
176
|
+
|
|
177
|
+
This Angular implementation follows the current shadcn Date Picker information architecture while translating React Popover plus Calendar composition into the local Material-backed wrapper.
|
|
178
|
+
|
|
179
|
+
The upstream Range Picker example is represented as two coordinated `DatePicker` controls rather than a single range-calendar API. The upstream Natural Language example can be built by parsing text into a `Date | null` value and binding that value to `DatePicker`.
|