@ojiepermana/angular 22.1.11 → 22.1.12
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 +32 -26
- package/chart/README.md +272 -0
- package/component/README.md +89 -0
- package/component/accordion/README.md +195 -0
- package/component/alert/README.md +184 -0
- package/component/alert-dialog/README.md +255 -0
- package/component/aspect-ratio/README.md +116 -0
- package/component/avatar/README.md +182 -0
- package/component/badge/README.md +143 -0
- package/component/breadcrumb/README.md +223 -0
- package/component/button/README.md +141 -0
- package/component/button-group/README.md +214 -0
- package/component/calendar/README.md +137 -0
- package/component/card/README.md +232 -0
- package/component/carousel/README.md +276 -0
- package/component/checkbox/README.md +153 -0
- package/component/collapsible/README.md +216 -0
- package/component/combobox/README.md +202 -0
- package/component/command/README.md +287 -0
- package/component/composer/README.md +241 -0
- package/component/context-menu/README.md +268 -0
- package/component/date-picker/README.md +202 -0
- package/component/dialog/README.md +276 -0
- package/component/drawer/README.md +147 -0
- package/component/dropdown-menu/README.md +336 -0
- package/component/editor/README.md +152 -0
- package/component/empty/README.md +193 -0
- package/component/form/README.md +216 -0
- package/component/hover-card/README.md +150 -0
- package/component/icon/README.md +25 -0
- package/component/input/README.md +160 -0
- package/component/input-group/README.md +245 -0
- package/component/input-otp/README.md +331 -0
- package/component/item/README.md +254 -0
- package/component/kanban/README.md +81 -0
- package/component/kbd/README.md +141 -0
- package/component/label/README.md +137 -0
- package/component/menubar/README.md +270 -0
- package/component/native-select/README.md +176 -0
- package/component/navigation-menu/README.md +165 -0
- package/component/pagination/README.md +151 -0
- package/component/pillbox/README.md +68 -0
- package/component/popover/README.md +54 -0
- package/component/progress/README.md +160 -0
- package/component/radio/README.md +212 -0
- package/component/resizable/README.md +178 -0
- package/component/scroll-area/README.md +150 -0
- package/component/select/README.md +228 -0
- package/component/separator/README.md +175 -0
- package/component/sheet/README.md +190 -0
- package/component/skeleton/README.md +158 -0
- package/component/slider/README.md +207 -0
- package/component/spinner/README.md +160 -0
- package/component/switch/README.md +170 -0
- package/component/table/README.md +292 -0
- package/component/tabs/README.md +220 -0
- package/component/textarea/README.md +169 -0
- package/component/timeline/README.md +94 -0
- package/component/toast/README.md +173 -0
- package/component/toggle/README.md +133 -0
- package/component/toggle-group/README.md +234 -0
- package/component/tooltip/README.md +230 -0
- package/fesm2022/ojiepermana-angular-chart-area.mjs +289 -3
- package/fesm2022/ojiepermana-angular-chart-bar.mjs +722 -3
- package/fesm2022/ojiepermana-angular-chart-core.mjs +1482 -3
- package/fesm2022/ojiepermana-angular-chart-line.mjs +294 -3
- package/fesm2022/ojiepermana-angular-chart-pie.mjs +291 -3
- package/fesm2022/ojiepermana-angular-chart-primitives.mjs +1301 -3
- package/fesm2022/ojiepermana-angular-chart-radar.mjs +421 -3
- package/fesm2022/ojiepermana-angular-chart-radial.mjs +295 -3
- package/fesm2022/ojiepermana-angular-chart-scatter.mjs +306 -3
- package/fesm2022/ojiepermana-angular-chart.mjs +12 -3
- package/fesm2022/ojiepermana-angular-component-accordion.mjs +267 -3
- package/fesm2022/ojiepermana-angular-component-alert-dialog.mjs +359 -3
- package/fesm2022/ojiepermana-angular-component-alert.mjs +101 -3
- package/fesm2022/ojiepermana-angular-component-aspect-ratio.mjs +31 -3
- package/fesm2022/ojiepermana-angular-component-avatar.mjs +139 -3
- package/fesm2022/ojiepermana-angular-component-badge.mjs +57 -3
- package/fesm2022/ojiepermana-angular-component-breadcrumb.mjs +192 -3
- package/fesm2022/ojiepermana-angular-component-button-group.mjs +95 -3
- package/fesm2022/ojiepermana-angular-component-button.mjs +73 -3
- package/fesm2022/ojiepermana-angular-component-calendar.mjs +718 -3
- package/fesm2022/ojiepermana-angular-component-card.mjs +167 -3
- package/fesm2022/ojiepermana-angular-component-carousel.mjs +326 -3
- package/fesm2022/ojiepermana-angular-component-checkbox.mjs +190 -3
- package/fesm2022/ojiepermana-angular-component-collapsible.mjs +117 -3
- package/fesm2022/ojiepermana-angular-component-combobox.mjs +327 -3
- package/fesm2022/ojiepermana-angular-component-command.mjs +351 -3
- package/fesm2022/ojiepermana-angular-component-composer.mjs +359 -3
- package/fesm2022/ojiepermana-angular-component-context-menu.mjs +158 -3
- package/fesm2022/ojiepermana-angular-component-date-picker.mjs +482 -3
- package/fesm2022/ojiepermana-angular-component-dialog.mjs +400 -3
- package/fesm2022/ojiepermana-angular-component-drawer.mjs +1 -3
- package/fesm2022/ojiepermana-angular-component-dropdown-menu.mjs +689 -3
- package/fesm2022/ojiepermana-angular-component-editor.mjs +511 -3
- package/fesm2022/ojiepermana-angular-component-empty.mjs +134 -3
- package/fesm2022/ojiepermana-angular-component-form.mjs +438 -3
- package/fesm2022/ojiepermana-angular-component-hover-card.mjs +273 -3
- package/fesm2022/ojiepermana-angular-component-icon.mjs +139 -3
- package/fesm2022/ojiepermana-angular-component-input-group.mjs +176 -3
- package/fesm2022/ojiepermana-angular-component-input-otp.mjs +550 -3
- package/fesm2022/ojiepermana-angular-component-input.mjs +52 -3
- package/fesm2022/ojiepermana-angular-component-item.mjs +264 -3
- package/fesm2022/ojiepermana-angular-component-kanban.mjs +310 -3
- package/fesm2022/ojiepermana-angular-component-kbd.mjs +48 -3
- package/fesm2022/ojiepermana-angular-component-label.mjs +27 -3
- package/fesm2022/ojiepermana-angular-component-menubar.mjs +327 -3
- package/fesm2022/ojiepermana-angular-component-native-select.mjs +62 -3
- package/fesm2022/ojiepermana-angular-component-navigation-menu.mjs +378 -3
- package/fesm2022/ojiepermana-angular-component-pagination.mjs +238 -3
- package/fesm2022/ojiepermana-angular-component-pillbox.mjs +999 -3
- package/fesm2022/ojiepermana-angular-component-popover.mjs +201 -3
- package/fesm2022/ojiepermana-angular-component-progress.mjs +57 -3
- package/fesm2022/ojiepermana-angular-component-radio.mjs +180 -3
- package/fesm2022/ojiepermana-angular-component-resizable.mjs +493 -3
- package/fesm2022/ojiepermana-angular-component-scroll-area.mjs +51 -3
- package/fesm2022/ojiepermana-angular-component-segmented-control.mjs +227 -3
- package/fesm2022/ojiepermana-angular-component-select.mjs +609 -3
- package/fesm2022/ojiepermana-angular-component-separator.mjs +31 -3
- package/fesm2022/ojiepermana-angular-component-sheet.mjs +407 -3
- package/fesm2022/ojiepermana-angular-component-skeleton.mjs +25 -3
- package/fesm2022/ojiepermana-angular-component-slider.mjs +456 -3
- package/fesm2022/ojiepermana-angular-component-spinner.mjs +56 -3
- package/fesm2022/ojiepermana-angular-component-switch.mjs +145 -3
- package/fesm2022/ojiepermana-angular-component-table.mjs +155 -3
- package/fesm2022/ojiepermana-angular-component-tabs.mjs +315 -3
- package/fesm2022/ojiepermana-angular-component-textarea.mjs +46 -3
- package/fesm2022/ojiepermana-angular-component-timeline.mjs +237 -3
- package/fesm2022/ojiepermana-angular-component-toast.mjs +241 -3
- package/fesm2022/ojiepermana-angular-component-toggle-group.mjs +290 -3
- package/fesm2022/ojiepermana-angular-component-toggle.mjs +76 -3
- package/fesm2022/ojiepermana-angular-component-tooltip.mjs +519 -3
- package/fesm2022/ojiepermana-angular-component-utils-core.mjs +310 -3
- package/fesm2022/ojiepermana-angular-component-utils-overlay.mjs +62 -3
- package/fesm2022/ojiepermana-angular-component-utils-scale.mjs +34 -3
- package/fesm2022/ojiepermana-angular-component-utils.mjs +3 -2
- package/fesm2022/ojiepermana-angular-component.mjs +4 -4
- package/fesm2022/ojiepermana-angular-navigation-core.mjs +21 -3
- package/fesm2022/ojiepermana-angular-navigation-icon.mjs +20 -3
- package/fesm2022/ojiepermana-angular-navigation-navigation-badge-count.component-CDBxQ8rk.mjs +91 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-dockbar-menu.component-DBQHdvGa.mjs +512 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-entry-grid.component-Cd-jh6tg.mjs +623 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-flyout-menu.component-BCGvhoYj.mjs +404 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-horizontal.component-Dk_03e-6.mjs +466 -0
- package/fesm2022/ojiepermana-angular-navigation-ojiepermana-angular-navigation-DNKU6viC.mjs +2642 -0
- package/fesm2022/ojiepermana-angular-navigation-service.mjs +725 -3
- package/fesm2022/ojiepermana-angular-navigation-types.mjs +65 -4
- package/fesm2022/ojiepermana-angular-navigation.mjs +4 -10
- package/fesm2022/ojiepermana-angular-sdk.mjs +204 -4
- package/fesm2022/ojiepermana-angular-theme-component-settings.mjs +1387 -3
- package/fesm2022/ojiepermana-angular-theme-layout-services.mjs +331 -3
- package/fesm2022/ojiepermana-angular-theme-layout-types.mjs +45 -4
- package/fesm2022/ojiepermana-angular-theme-layout-wrapper.mjs +1044 -3
- package/fesm2022/ojiepermana-angular-theme-layout.mjs +729 -3
- package/fesm2022/ojiepermana-angular-theme-page-core.mjs +381 -3
- package/fesm2022/ojiepermana-angular-theme-page-filter.mjs +267 -3
- package/fesm2022/ojiepermana-angular-theme-page-root.mjs +361 -3
- package/fesm2022/ojiepermana-angular-theme-page-side.mjs +198 -3
- package/fesm2022/ojiepermana-angular-theme-page-slots.mjs +110 -3
- package/fesm2022/ojiepermana-angular-theme-page.mjs +7 -2
- package/fesm2022/ojiepermana-angular-theme-shell-services.mjs +338 -3
- package/fesm2022/ojiepermana-angular-theme-shell-types.mjs +160 -4
- package/fesm2022/ojiepermana-angular-theme-shell.mjs +811 -3
- package/fesm2022/ojiepermana-angular-theme-styles.mjs +783 -3
- package/fesm2022/ojiepermana-angular-theme.mjs +4 -4
- package/fesm2022/ojiepermana-angular.mjs +6 -11
- package/navigation/README.md +374 -0
- package/package.json +81 -20
- package/sdk/README.md +212 -0
- package/sdk/schematics/collection.json +20 -0
- package/sdk/schematics/init/index.js +83 -0
- package/sdk/schematics/init/schema.json +19 -0
- package/sdk/schematics/ng-add/index.js +147 -0
- package/sdk/schematics/ng-add/schema.json +14 -0
- package/sdk/schematics/package.json +3 -0
- package/sdk/schematics/sdk/index.js +175 -0
- package/sdk/schematics/sdk/schema.json +19 -0
- package/sdk/sdk.config.example.json +24 -0
- package/sdk/src/config/loader.js +43 -0
- package/sdk/src/config/schema.js +59 -0
- package/sdk/src/emit/client.js +251 -0
- package/sdk/src/emit/metadata.js +310 -0
- package/sdk/src/emit/models.js +104 -0
- package/sdk/src/emit/navigation.js +56 -0
- package/sdk/src/emit/operations.js +118 -0
- package/sdk/src/emit/public-api.js +54 -0
- package/sdk/src/emit/services.js +110 -0
- package/sdk/src/engine.js +67 -0
- package/sdk/src/layout/per-domain.js +360 -0
- package/sdk/src/package.json +3 -0
- package/sdk/src/parser/bundle.js +25 -0
- package/sdk/src/parser/ir.js +429 -0
- package/sdk/src/parser/types.js +7 -0
- package/sdk/src/render/template.js +152 -0
- package/sdk/src/writer/index.js +286 -0
- package/theme/README.md +126 -0
- package/theme/component/settings/README.md +135 -0
- package/theme/layout/README.md +539 -0
- package/theme/page/README.md +453 -0
- package/theme/shell/README.md +340 -0
- package/theme/styles/README.md +178 -0
- package/theme/styles/css/base/components.css +553 -0
- package/theme/styles/css/base/index.css +14 -0
- package/theme/styles/css/base/package.css +9 -0
- package/theme/styles/css/base/prefers-dark.css +86 -0
- package/theme/styles/css/base/print.css +62 -0
- package/theme/styles/css/base/status.css +38 -0
- package/theme/styles/css/base/tailwind.css +165 -0
- package/theme/styles/css/base/theme.css +373 -0
- package/theme/styles/css/base/tokens.css +361 -0
- package/theme/styles/css/color/amber.css +72 -0
- package/theme/styles/css/color/blue.css +72 -0
- package/theme/styles/css/color/brand.css +38 -0
- package/theme/styles/css/color/cyan.css +72 -0
- package/theme/styles/css/color/emerald.css +72 -0
- package/theme/styles/css/color/fuchsia.css +72 -0
- package/theme/styles/css/color/green.css +72 -0
- package/theme/styles/css/color/index.css +19 -0
- package/theme/styles/css/color/indigo.css +72 -0
- package/theme/styles/css/color/lime.css +72 -0
- package/theme/styles/css/color/orange.css +72 -0
- package/theme/styles/css/color/pink.css +72 -0
- package/theme/styles/css/color/purple.css +72 -0
- package/theme/styles/css/color/red.css +72 -0
- package/theme/styles/css/color/rose.css +72 -0
- package/theme/styles/css/color/sky.css +72 -0
- package/theme/styles/css/color/teal.css +72 -0
- package/theme/styles/css/color/violet.css +72 -0
- package/theme/styles/css/color/yellow.css +72 -0
- package/theme/styles/css/index.css +18 -0
- package/theme/styles/css/neutral/gray.css +63 -0
- package/theme/styles/css/neutral/index.css +11 -0
- package/theme/styles/css/neutral/mauve.css +63 -0
- package/theme/styles/css/neutral/mist.css +63 -0
- package/theme/styles/css/neutral/neutral.css +63 -0
- package/theme/styles/css/neutral/olive.css +63 -0
- package/theme/styles/css/neutral/slate.css +63 -0
- package/theme/styles/css/neutral/stone.css +63 -0
- package/theme/styles/css/neutral/taupe.css +63 -0
- package/theme/styles/css/neutral/zinc.css +63 -0
- package/theme/styles/css/radius/index.css +30 -0
- package/theme/styles/css/space/index.css +20 -0
- package/types/ojiepermana-angular-chart-area.d.ts +76 -1
- package/types/ojiepermana-angular-chart-bar.d.ts +201 -1
- package/types/ojiepermana-angular-chart-core.d.ts +811 -1
- package/types/ojiepermana-angular-chart-line.d.ts +75 -1
- package/types/ojiepermana-angular-chart-pie.d.ts +131 -1
- package/types/ojiepermana-angular-chart-primitives.d.ts +291 -1
- package/types/ojiepermana-angular-chart-radar.d.ts +116 -1
- package/types/ojiepermana-angular-chart-radial.d.ts +116 -1
- package/types/ojiepermana-angular-chart-scatter.d.ts +125 -1
- package/types/ojiepermana-angular-chart.d.ts +2 -1
- package/types/ojiepermana-angular-component-accordion.d.ts +85 -1
- package/types/ojiepermana-angular-component-alert-dialog.d.ts +140 -1
- package/types/ojiepermana-angular-component-alert.d.ts +42 -1
- package/types/ojiepermana-angular-component-aspect-ratio.d.ts +12 -1
- package/types/ojiepermana-angular-component-avatar.d.ts +56 -1
- package/types/ojiepermana-angular-component-badge.d.ts +24 -1
- package/types/ojiepermana-angular-component-breadcrumb.d.ts +46 -1
- package/types/ojiepermana-angular-component-button-group.d.ts +26 -1
- package/types/ojiepermana-angular-component-button.d.ts +27 -1
- package/types/ojiepermana-angular-component-calendar.d.ts +111 -1
- package/types/ojiepermana-angular-component-card.d.ts +69 -1
- package/types/ojiepermana-angular-component-carousel.d.ts +86 -1
- package/types/ojiepermana-angular-component-checkbox.d.ts +42 -1
- package/types/ojiepermana-angular-component-collapsible.d.ts +42 -1
- package/types/ojiepermana-angular-component-combobox.d.ts +60 -1
- package/types/ojiepermana-angular-component-command.d.ts +130 -1
- package/types/ojiepermana-angular-component-composer.d.ts +90 -1
- package/types/ojiepermana-angular-component-context-menu.d.ts +54 -1
- package/types/ojiepermana-angular-component-date-picker.d.ts +109 -1
- package/types/ojiepermana-angular-component-dialog.d.ts +136 -1
- package/types/ojiepermana-angular-component-drawer.d.ts +1 -1
- package/types/ojiepermana-angular-component-dropdown-menu.d.ts +248 -1
- package/types/ojiepermana-angular-component-editor.d.ts +135 -1
- package/types/ojiepermana-angular-component-empty.d.ts +50 -1
- package/types/ojiepermana-angular-component-form.d.ts +159 -1
- package/types/ojiepermana-angular-component-hover-card.d.ts +75 -1
- package/types/ojiepermana-angular-component-icon.d.ts +89 -1
- package/types/ojiepermana-angular-component-input-group.d.ts +51 -1
- package/types/ojiepermana-angular-component-input-otp.d.ts +155 -1
- package/types/ojiepermana-angular-component-input.d.ts +21 -1
- package/types/ojiepermana-angular-component-item.d.ts +93 -1
- package/types/ojiepermana-angular-component-kanban.d.ts +70 -1
- package/types/ojiepermana-angular-component-kbd.d.ts +16 -1
- package/types/ojiepermana-angular-component-label.d.ts +11 -1
- package/types/ojiepermana-angular-component-menubar.d.ts +93 -1
- package/types/ojiepermana-angular-component-native-select.d.ts +26 -1
- package/types/ojiepermana-angular-component-navigation-menu.d.ts +104 -1
- package/types/ojiepermana-angular-component-pagination.d.ts +33 -1
- package/types/ojiepermana-angular-component-pillbox.d.ts +222 -1
- package/types/ojiepermana-angular-component-popover.d.ts +66 -1
- package/types/ojiepermana-angular-component-progress.d.ts +17 -1
- package/types/ojiepermana-angular-component-radio.d.ts +60 -1
- package/types/ojiepermana-angular-component-resizable.d.ts +117 -1
- package/types/ojiepermana-angular-component-scroll-area.d.ts +19 -1
- package/types/ojiepermana-angular-component-segmented-control.d.ts +58 -1
- package/types/ojiepermana-angular-component-select.d.ts +122 -1
- package/types/ojiepermana-angular-component-separator.d.ts +14 -1
- package/types/ojiepermana-angular-component-sheet.d.ts +130 -1
- package/types/ojiepermana-angular-component-skeleton.d.ts +10 -1
- package/types/ojiepermana-angular-component-slider.d.ts +83 -1
- package/types/ojiepermana-angular-component-spinner.d.ts +13 -1
- package/types/ojiepermana-angular-component-switch.d.ts +44 -1
- package/types/ojiepermana-angular-component-table.d.ts +57 -1
- package/types/ojiepermana-angular-component-tabs.d.ts +124 -1
- package/types/ojiepermana-angular-component-textarea.d.ts +17 -1
- package/types/ojiepermana-angular-component-timeline.d.ts +64 -1
- package/types/ojiepermana-angular-component-toast.d.ts +64 -1
- package/types/ojiepermana-angular-component-toggle-group.d.ts +90 -1
- package/types/ojiepermana-angular-component-toggle.d.ts +25 -1
- package/types/ojiepermana-angular-component-tooltip.d.ts +143 -1
- package/types/ojiepermana-angular-component-utils-core.d.ts +184 -1
- package/types/ojiepermana-angular-component-utils-overlay.d.ts +21 -1
- package/types/ojiepermana-angular-component-utils-scale.d.ts +19 -1
- package/types/ojiepermana-angular-component-utils.d.ts +2 -1
- package/types/ojiepermana-angular-component.d.ts +2 -1
- package/types/ojiepermana-angular-navigation-core.d.ts +59 -1
- package/types/ojiepermana-angular-navigation-icon.d.ts +12 -1
- package/types/ojiepermana-angular-navigation-service.d.ts +116 -1
- package/types/ojiepermana-angular-navigation-types.d.ts +144 -1
- package/types/ojiepermana-angular-navigation.d.ts +507 -1
- package/types/ojiepermana-angular-sdk.d.ts +89 -1
- package/types/ojiepermana-angular-theme-component-settings.d.ts +154 -1
- package/types/ojiepermana-angular-theme-layout-services.d.ts +147 -1
- package/types/ojiepermana-angular-theme-layout-types.d.ts +40 -1
- package/types/ojiepermana-angular-theme-layout-wrapper.d.ts +299 -1
- package/types/ojiepermana-angular-theme-layout.d.ts +129 -1
- package/types/ojiepermana-angular-theme-page-core.d.ts +157 -1
- package/types/ojiepermana-angular-theme-page-filter.d.ts +77 -1
- package/types/ojiepermana-angular-theme-page-root.d.ts +88 -1
- package/types/ojiepermana-angular-theme-page-side.d.ts +50 -1
- package/types/ojiepermana-angular-theme-page-slots.d.ts +40 -1
- package/types/ojiepermana-angular-theme-page.d.ts +5 -1
- package/types/ojiepermana-angular-theme-shell-services.d.ts +193 -1
- package/types/ojiepermana-angular-theme-shell-types.d.ts +105 -1
- package/types/ojiepermana-angular-theme-shell.d.ts +100 -1
- package/types/ojiepermana-angular-theme-styles.d.ts +319 -1
- package/types/ojiepermana-angular-theme.d.ts +2 -1
- package/types/ojiepermana-angular.d.ts +5 -5
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
# Dropdown Menu
|
|
2
|
+
|
|
3
|
+
Accessible menu rendered in a CDK overlay with keyboard navigation (arrow keys,
|
|
4
|
+
Home/End, type-ahead, Escape).
|
|
5
|
+
|
|
6
|
+
This Angular implementation follows the shadcn Dropdown Menu information architecture while mapping the React root
|
|
7
|
+
component to an explicit trigger directive and projected menu template.
|
|
8
|
+
|
|
9
|
+
## Import
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import {
|
|
13
|
+
MenuCheckboxItemComponent,
|
|
14
|
+
MenuTriggerDirective,
|
|
15
|
+
MenuContentDirective,
|
|
16
|
+
MenuGroupComponent,
|
|
17
|
+
MenuSurfaceComponent,
|
|
18
|
+
MenuItemComponent,
|
|
19
|
+
MenuRadioGroupComponent,
|
|
20
|
+
MenuRadioItemComponent,
|
|
21
|
+
MenuSeparatorComponent,
|
|
22
|
+
MenuLabelComponent,
|
|
23
|
+
MenuShortcutComponent,
|
|
24
|
+
} from '@ojiepermana/angular/component/dropdown-menu';
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```html
|
|
30
|
+
<button
|
|
31
|
+
Button
|
|
32
|
+
type="button"
|
|
33
|
+
variant="outline"
|
|
34
|
+
#accountTrigger="MenuTrigger"
|
|
35
|
+
[MenuTrigger]="menu"
|
|
36
|
+
side="bottom"
|
|
37
|
+
align="end"
|
|
38
|
+
>
|
|
39
|
+
Open
|
|
40
|
+
</button>
|
|
41
|
+
|
|
42
|
+
<ng-template MenuContent #menu="MenuContent">
|
|
43
|
+
<MenuSurface class="w-56">
|
|
44
|
+
<MenuGroup>
|
|
45
|
+
<MenuLabel>My account</MenuLabel>
|
|
46
|
+
<button MenuItem type="button" (selected)="onProfile()">
|
|
47
|
+
Profile
|
|
48
|
+
<span MenuShortcut>⌘P</span>
|
|
49
|
+
</button>
|
|
50
|
+
<button MenuItem type="button" (selected)="onSettings()">Settings</button>
|
|
51
|
+
</MenuGroup>
|
|
52
|
+
<MenuSeparator />
|
|
53
|
+
<MenuGroup>
|
|
54
|
+
<button MenuItem type="button" disabled>Disabled</button>
|
|
55
|
+
<button MenuItem type="button" variant="destructive" (selected)="onSignOut()">
|
|
56
|
+
Sign out
|
|
57
|
+
</button>
|
|
58
|
+
</MenuGroup>
|
|
59
|
+
</MenuSurface>
|
|
60
|
+
</ng-template>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Use the trigger export when a standard command row should dismiss the overlay. Checkbox and radio rows can stay open so
|
|
64
|
+
the user can adjust multiple settings in one pass.
|
|
65
|
+
|
|
66
|
+
## Composition
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
button[Button][MenuTrigger]
|
|
70
|
+
└── ng-template[MenuContent]
|
|
71
|
+
└── MenuSurface
|
|
72
|
+
├── MenuGroup
|
|
73
|
+
│ ├── MenuLabel
|
|
74
|
+
│ ├── button[MenuItem]
|
|
75
|
+
│ └── button[MenuItem][MenuTrigger]
|
|
76
|
+
│ └── ng-template[MenuContent]
|
|
77
|
+
│ └── MenuSurface
|
|
78
|
+
├── MenuSeparator
|
|
79
|
+
├── button[MenuCheckboxItem]
|
|
80
|
+
└── MenuRadioGroup
|
|
81
|
+
└── button[MenuRadioItem]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Common patterns
|
|
85
|
+
|
|
86
|
+
### Basic account menu
|
|
87
|
+
|
|
88
|
+
```html
|
|
89
|
+
<button Button type="button" variant="outline" #menuTrigger="MenuTrigger" [MenuTrigger]="menu">
|
|
90
|
+
Open
|
|
91
|
+
</button>
|
|
92
|
+
|
|
93
|
+
<ng-template MenuContent #menu="MenuContent">
|
|
94
|
+
<MenuSurface class="w-56">
|
|
95
|
+
<MenuGroup>
|
|
96
|
+
<MenuLabel>My Account</MenuLabel>
|
|
97
|
+
<button MenuItem type="button">Profile</button>
|
|
98
|
+
<button MenuItem type="button">Billing</button>
|
|
99
|
+
<button MenuItem type="button">Settings</button>
|
|
100
|
+
</MenuGroup>
|
|
101
|
+
<MenuSeparator />
|
|
102
|
+
<MenuGroup>
|
|
103
|
+
<button MenuItem type="button">GitHub</button>
|
|
104
|
+
<button MenuItem type="button">Support</button>
|
|
105
|
+
<button MenuItem type="button" [disabled]="true">API</button>
|
|
106
|
+
</MenuGroup>
|
|
107
|
+
</MenuSurface>
|
|
108
|
+
</ng-template>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Submenu
|
|
112
|
+
|
|
113
|
+
Map shadcn `DropdownMenuSub` to `MenuTrigger` on a `button[MenuItem]`.
|
|
114
|
+
|
|
115
|
+
```html
|
|
116
|
+
<ng-template MenuContent #inviteUsersMenu="MenuContent">
|
|
117
|
+
<MenuSurface class="w-48">
|
|
118
|
+
<button MenuItem type="button">Email</button>
|
|
119
|
+
<button MenuItem type="button">Message</button>
|
|
120
|
+
<button MenuItem type="button">Calendar invite</button>
|
|
121
|
+
</MenuSurface>
|
|
122
|
+
</ng-template>
|
|
123
|
+
|
|
124
|
+
<MenuSurface class="w-56">
|
|
125
|
+
<MenuGroup>
|
|
126
|
+
<button MenuItem type="button">Team</button>
|
|
127
|
+
<button MenuItem type="button" [MenuTrigger]="inviteUsersMenu" side="right" align="start">
|
|
128
|
+
Invite users
|
|
129
|
+
<span class="ml-auto text-xs text-muted-foreground">›</span>
|
|
130
|
+
</button>
|
|
131
|
+
</MenuGroup>
|
|
132
|
+
</MenuSurface>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Checkboxes
|
|
136
|
+
|
|
137
|
+
Use checkbox rows when the menu exposes independent toggles.
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
const showStatusBar = signal(true);
|
|
141
|
+
const showActivityBar = signal(false);
|
|
142
|
+
const showPanel = signal(false);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```html
|
|
146
|
+
<MenuSurface class="w-44">
|
|
147
|
+
<MenuGroup>
|
|
148
|
+
<MenuLabel>Appearance</MenuLabel>
|
|
149
|
+
<button MenuCheckboxItem type="button" [(checked)]="showStatusBar">Status Bar</button>
|
|
150
|
+
<button MenuCheckboxItem type="button" [(checked)]="showActivityBar">Activity Bar</button>
|
|
151
|
+
<button MenuCheckboxItem type="button" [(checked)]="showPanel">Panel</button>
|
|
152
|
+
</MenuGroup>
|
|
153
|
+
</MenuSurface>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Radio group
|
|
157
|
+
|
|
158
|
+
Use a radio group for one exclusive choice.
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
const panelPosition = signal<'top' | 'bottom' | 'right'>('bottom');
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
```html
|
|
165
|
+
<MenuSurface class="w-44">
|
|
166
|
+
<MenuLabel [inset]="true">Panel Position</MenuLabel>
|
|
167
|
+
<MenuRadioGroup [(value)]="panelPosition">
|
|
168
|
+
<button MenuRadioItem type="button" value="top">Top</button>
|
|
169
|
+
<button MenuRadioItem type="button" value="bottom">Bottom</button>
|
|
170
|
+
<button MenuRadioItem type="button" value="right">Right</button>
|
|
171
|
+
</MenuRadioGroup>
|
|
172
|
+
</MenuSurface>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Shortcuts and icons
|
|
176
|
+
|
|
177
|
+
Project shortcut text and inline icons directly into the row content.
|
|
178
|
+
|
|
179
|
+
```html
|
|
180
|
+
<MenuSurface class="w-56">
|
|
181
|
+
<button MenuItem type="button">
|
|
182
|
+
<svg aria-hidden="true" class="size-4" viewBox="0 0 24 24">...</svg>
|
|
183
|
+
Profile
|
|
184
|
+
<span MenuShortcut>⇧⌘P</span>
|
|
185
|
+
</button>
|
|
186
|
+
<button MenuItem type="button">
|
|
187
|
+
<svg aria-hidden="true" class="size-4" viewBox="0 0 24 24">...</svg>
|
|
188
|
+
Billing
|
|
189
|
+
<span MenuShortcut>⌘B</span>
|
|
190
|
+
</button>
|
|
191
|
+
</MenuSurface>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Avatar trigger composition
|
|
195
|
+
|
|
196
|
+
Also import the avatar entrypoint when the trigger should be an account avatar.
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
import {
|
|
200
|
+
AvatarComponent,
|
|
201
|
+
AvatarFallbackComponent,
|
|
202
|
+
AvatarImageComponent,
|
|
203
|
+
} from '@ojiepermana/angular/component/avatar';
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
```html
|
|
207
|
+
<button
|
|
208
|
+
Button
|
|
209
|
+
type="button"
|
|
210
|
+
variant="ghost"
|
|
211
|
+
size="icon"
|
|
212
|
+
class="h-10 w-10 rounded-full p-0"
|
|
213
|
+
[MenuTrigger]="accountMenu"
|
|
214
|
+
align="end"
|
|
215
|
+
>
|
|
216
|
+
<Avatar>
|
|
217
|
+
<AvatarImage src="https://github.com/shadcn.png" alt="Ada Lovelace" />
|
|
218
|
+
<AvatarFallback>AL</AvatarFallback>
|
|
219
|
+
</Avatar>
|
|
220
|
+
</button>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### RTL
|
|
224
|
+
|
|
225
|
+
Set `dir="rtl"` and `lang` on the surface, and prefer `side="left"` when nested content should open toward the visual
|
|
226
|
+
start edge.
|
|
227
|
+
|
|
228
|
+
```html
|
|
229
|
+
<ng-template MenuContent #rtlAccountMenu="MenuContent">
|
|
230
|
+
<MenuSurface dir="rtl" lang="ar" class="w-44 text-right">
|
|
231
|
+
<button MenuItem type="button">الملف الشخصي</button>
|
|
232
|
+
<button MenuItem type="button">الفوترة</button>
|
|
233
|
+
</MenuSurface>
|
|
234
|
+
</ng-template>
|
|
235
|
+
|
|
236
|
+
<MenuSurface dir="rtl" lang="ar" class="w-56 text-right">
|
|
237
|
+
<button MenuItem type="button" [MenuTrigger]="rtlAccountMenu" side="left" align="start">
|
|
238
|
+
الحساب
|
|
239
|
+
<span class="mr-auto text-xs text-muted-foreground">‹</span>
|
|
240
|
+
</button>
|
|
241
|
+
</MenuSurface>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## API
|
|
245
|
+
|
|
246
|
+
### `[MenuTrigger]`
|
|
247
|
+
|
|
248
|
+
| Input | Type | Default |
|
|
249
|
+
| --------------- | ---------------------------------------- | ---------- |
|
|
250
|
+
| `MenuTrigger` | `MenuContentDirective` | _required_ |
|
|
251
|
+
| `side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'bottom'` |
|
|
252
|
+
| `align` | `'start' \| 'center' \| 'end'` | `'start'` |
|
|
253
|
+
| `disabled` | `boolean` | `false` |
|
|
254
|
+
| `closeOnSelect` | `boolean` | `true` |
|
|
255
|
+
|
|
256
|
+
Output: `openedChange: boolean`. Methods: `open()`, `close()`, `toggle()`.
|
|
257
|
+
|
|
258
|
+
The trigger carries `aria-haspopup="menu"`, `aria-expanded`, and, while open,
|
|
259
|
+
`aria-controls` pointing at the surface. Set `closeOnSelect` to `false` for a menu
|
|
260
|
+
that stays open after a choice, for example a run of checkbox items.
|
|
261
|
+
|
|
262
|
+
### `ng-template[MenuContent]`
|
|
263
|
+
|
|
264
|
+
Projected template wrapper for the menu surface that is consumed by `[MenuTrigger]`.
|
|
265
|
+
|
|
266
|
+
### `MenuSurface`
|
|
267
|
+
|
|
268
|
+
Container with `role="menu"` and arrow key navigation wired through
|
|
269
|
+
`FocusKeyManager` (wrapping + type-ahead). It carries `aria-labelledby` pointing
|
|
270
|
+
back at the trigger.
|
|
271
|
+
|
|
272
|
+
Keyboard behavior you get for free, no wiring required:
|
|
273
|
+
|
|
274
|
+
- choosing an item closes the menu and returns focus to the trigger, unless the
|
|
275
|
+
trigger sets `closeOnSelect="false"`;
|
|
276
|
+
- `Tab` closes the menu and continues the normal tab order from the trigger;
|
|
277
|
+
- `Escape` closes the menu and returns focus to the trigger.
|
|
278
|
+
|
|
279
|
+
`closeRequested` still fires on `Tab` for anyone already listening to it, but you
|
|
280
|
+
no longer have to handle it.
|
|
281
|
+
|
|
282
|
+
### `MenuItem`
|
|
283
|
+
|
|
284
|
+
`role="menuitem"`. Inputs: `disabled`, `inset`, `class`. Output: `selected`.
|
|
285
|
+
|
|
286
|
+
### `MenuCheckboxItem`
|
|
287
|
+
|
|
288
|
+
`role="menuitemcheckbox"`. Inputs: `[(checked)]`, `disabled`, `variant`, `class`.
|
|
289
|
+
|
|
290
|
+
### `MenuRadioGroup` and `MenuRadioItem`
|
|
291
|
+
|
|
292
|
+
Use `[(value)]` on the group and `value` on each radio row. Radio items expose `role="menuitemradio"`.
|
|
293
|
+
|
|
294
|
+
### Auxiliary
|
|
295
|
+
|
|
296
|
+
- `MenuGroup` → structural wrapper with `role="group"`.
|
|
297
|
+
- `MenuSeparator` → `role="separator"`.
|
|
298
|
+
- `MenuLabel` — non-interactive label row.
|
|
299
|
+
- `MenuShortcut` — right-aligned shortcut badge.
|
|
300
|
+
|
|
301
|
+
## Styling and theming
|
|
302
|
+
|
|
303
|
+
Pass `class` to the surface or individual rows to control width, spacing, and emphasis. Borders and separators use the
|
|
304
|
+
shared theme border tokens, destructive rows use the destructive palette, and projected icons inherit the current text
|
|
305
|
+
color.
|
|
306
|
+
|
|
307
|
+
## Accessibility
|
|
308
|
+
|
|
309
|
+
- Trigger: `aria-haspopup="menu"`, `aria-expanded` reflects state.
|
|
310
|
+
- Surface: `role="menu"`, items receive `role="menuitem"`, disabled items get
|
|
311
|
+
`aria-disabled="true"`.
|
|
312
|
+
- Checkbox and radio items expose `aria-checked` semantics through the relevant menuitem roles.
|
|
313
|
+
- Close on outside click, Escape, or Tab. Focus returns to the trigger.
|
|
314
|
+
|
|
315
|
+
## Keyboard interactions
|
|
316
|
+
|
|
317
|
+
- Enter, Space, and ArrowDown on the trigger open the menu.
|
|
318
|
+
- Arrow Up and Arrow Down move between enabled rows; Home and End jump to the first or last row.
|
|
319
|
+
- Typing matches a row by its label text, skipping disabled rows. Space is not part of the search: it always activates the focused row, so a label is matched on its first word.
|
|
320
|
+
- `ArrowLeft` in a submenu closes it and returns focus to the row that opened it. A root menu leaves the key alone, so a `Menubar` can use it to walk to the previous menu.
|
|
321
|
+
- Enter and Space activate the focused row.
|
|
322
|
+
|
|
323
|
+
## Angular notes
|
|
324
|
+
|
|
325
|
+
- There is no React-style `DropdownMenu` root component; the root behavior is handled by `MenuTrigger` and
|
|
326
|
+
`ng-template[MenuContent]`.
|
|
327
|
+
- Reuse `MenuTrigger` on `button[MenuItem]` when you need a submenu.
|
|
328
|
+
- Standard command rows can dismiss the overlay explicitly through a `#menuTrigger="MenuTrigger"` template reference,
|
|
329
|
+
while checkbox and radio rows can stay open for multi-step preference changes.
|
|
330
|
+
|
|
331
|
+
## Source parity
|
|
332
|
+
|
|
333
|
+
This Angular entrypoint keeps the main shadcn dropdown-menu patterns: grouped commands, shortcuts, icons, nested menus,
|
|
334
|
+
checkbox items, radio groups, avatar triggers, destructive rows, and RTL guidance. The shadcn page includes additional
|
|
335
|
+
permutations such as checkbox-icons, radio-icons, and a very large complex menu; the local demos cover the same
|
|
336
|
+
primitives through representative compositions instead of duplicating every permutation one-to-one.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Editor
|
|
2
|
+
|
|
3
|
+
Rich text editor shell with an accessible toolbar, configurable toolbar items, contenteditable form control support, and shadcn-style tokens.
|
|
4
|
+
|
|
5
|
+
This component is conceptually inspired by Flux Editor. It does not bundle Tiptap or Flux JavaScript; instead, it provides a lightweight Angular primitive that applications can compose, bind, and extend.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { FormsModule } from '@angular/forms';
|
|
11
|
+
import {
|
|
12
|
+
EditorButtonComponent,
|
|
13
|
+
EditorComponent,
|
|
14
|
+
EditorContentComponent,
|
|
15
|
+
EditorSeparatorComponent,
|
|
16
|
+
EditorSpacerComponent,
|
|
17
|
+
EditorToolbarComponent,
|
|
18
|
+
} from '@ojiepermana/angular/component/editor';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```html
|
|
24
|
+
<Editor>
|
|
25
|
+
<EditorToolbar items="heading | bold italic underline | align ~ undo redo" />
|
|
26
|
+
<EditorContent placeholder="Write something..." aria-label="Article body">
|
|
27
|
+
<h3>What's changed</h3>
|
|
28
|
+
<p>Draft release notes, article copy, or support responses here.</p>
|
|
29
|
+
</EditorContent>
|
|
30
|
+
</Editor>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Forms
|
|
34
|
+
|
|
35
|
+
`EditorContent` implements `ControlValueAccessor`, so it can bind to Angular forms as an HTML string.
|
|
36
|
+
|
|
37
|
+
```html
|
|
38
|
+
<Editor>
|
|
39
|
+
<EditorToolbar />
|
|
40
|
+
<EditorContent [(ngModel)]="content" name="content" aria-label="Article body" />
|
|
41
|
+
</Editor>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`EditorContent` sanitizes both values written into the control and edited HTML
|
|
45
|
+
before emitting it. Applications can provide a stricter allow-list by overriding
|
|
46
|
+
`EDITOR_HTML_SANITIZER`; sanitize again at any unrelated rendering boundary.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { EDITOR_HTML_SANITIZER } from '@ojiepermana/angular/component/editor';
|
|
50
|
+
|
|
51
|
+
const providers = [
|
|
52
|
+
{
|
|
53
|
+
provide: EDITOR_HTML_SANITIZER,
|
|
54
|
+
useValue: (html: string) => applicationAllowList.sanitize(html),
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Toolbar Items
|
|
60
|
+
|
|
61
|
+
The toolbar accepts a space-separated `items` string. Use `|` for a separator and `~` for a spacer.
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
<EditorToolbar
|
|
65
|
+
items="heading | bold italic strike underline | bullet ordered blockquote link align ~ undo redo"
|
|
66
|
+
/>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Supported item names:
|
|
70
|
+
|
|
71
|
+
| Item | Behavior |
|
|
72
|
+
| ------------- | ---------------------------------------------------- |
|
|
73
|
+
| `heading` | Text, heading 1, heading 2, and heading 3 selector. |
|
|
74
|
+
| `bold` | Bold formatting. |
|
|
75
|
+
| `italic` | Italic formatting. |
|
|
76
|
+
| `strike` | Strikethrough formatting. |
|
|
77
|
+
| `underline` | Underline formatting. |
|
|
78
|
+
| `bullet` | Bulleted list. |
|
|
79
|
+
| `ordered` | Numbered list. |
|
|
80
|
+
| `blockquote` | Block quote block. |
|
|
81
|
+
| `subscript` | Subscript formatting. |
|
|
82
|
+
| `superscript` | Superscript formatting. |
|
|
83
|
+
| `highlight` | Highlight selected text. |
|
|
84
|
+
| `link` | Create a placeholder link for the current selection. |
|
|
85
|
+
| `code` | Code block. |
|
|
86
|
+
| `align` | Left, center, and right alignment selector. |
|
|
87
|
+
| `undo` | Undo last edit. |
|
|
88
|
+
| `redo` | Redo last edit. |
|
|
89
|
+
|
|
90
|
+
## Custom Toolbar
|
|
91
|
+
|
|
92
|
+
Pass `items=""` and project custom toolbar controls.
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<Editor>
|
|
96
|
+
<EditorToolbar items="">
|
|
97
|
+
<button EditorButton command="bold" aria-label="Bold">B</button>
|
|
98
|
+
<button EditorButton command="italic" aria-label="Italic">I</button>
|
|
99
|
+
<EditorSeparator />
|
|
100
|
+
<button EditorButton command="highlight" aria-label="Highlight">H</button>
|
|
101
|
+
<EditorSpacer />
|
|
102
|
+
<button EditorButton command="undo" aria-label="Undo">
|
|
103
|
+
<svg
|
|
104
|
+
aria-hidden="true"
|
|
105
|
+
class="size-4"
|
|
106
|
+
viewBox="0 0 24 24"
|
|
107
|
+
fill="none"
|
|
108
|
+
stroke="currentColor"
|
|
109
|
+
stroke-width="1.75"
|
|
110
|
+
stroke-linecap="round"
|
|
111
|
+
stroke-linejoin="round"
|
|
112
|
+
>
|
|
113
|
+
<path d="M9 14 4 9l5-5" />
|
|
114
|
+
<path d="M4 9h10a6 6 0 0 1 0 12h-2" />
|
|
115
|
+
</svg>
|
|
116
|
+
</button>
|
|
117
|
+
</EditorToolbar>
|
|
118
|
+
<EditorContent aria-label="Custom editor" />
|
|
119
|
+
</Editor>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Keyboard Shortcuts
|
|
123
|
+
|
|
124
|
+
| Action | Shortcut |
|
|
125
|
+
| ------------- | ----------------- |
|
|
126
|
+
| Bold | Cmd/Ctrl+B |
|
|
127
|
+
| Italic | Cmd/Ctrl+I |
|
|
128
|
+
| Underline | Cmd/Ctrl+U |
|
|
129
|
+
| Strikethrough | Cmd/Ctrl+Shift+X |
|
|
130
|
+
| Ordered list | Cmd/Ctrl+Shift+7 |
|
|
131
|
+
| Bulleted list | Cmd/Ctrl+Shift+8 |
|
|
132
|
+
| Heading 1-3 | Cmd/Ctrl+Alt+1..3 |
|
|
133
|
+
| Undo | Cmd/Ctrl+Z |
|
|
134
|
+
| Redo | Cmd/Ctrl+Shift+Z |
|
|
135
|
+
|
|
136
|
+
## Styling
|
|
137
|
+
|
|
138
|
+
The editor uses `border-input`, `bg-background`, `ring-ring`, `text-foreground`, `text-muted-foreground`, and `bg-muted` tokens. Pass sizing classes directly to the content slot when adjusting height.
|
|
139
|
+
|
|
140
|
+
```html
|
|
141
|
+
<Editor>
|
|
142
|
+
<EditorToolbar />
|
|
143
|
+
<EditorContent class="min-h-30 max-h-60" aria-label="Compact editor" />
|
|
144
|
+
</Editor>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Accessibility
|
|
148
|
+
|
|
149
|
+
- `EditorToolbar` renders `role="toolbar"`.
|
|
150
|
+
- `EditorContent` renders `role="textbox"` and `aria-multiline="true"`.
|
|
151
|
+
- Provide a clear `aria-label` for each editor content surface.
|
|
152
|
+
- Give terse or icon-only custom toolbar buttons an explicit `aria-label`.
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Empty
|
|
2
|
+
|
|
3
|
+
Displays a reusable empty state with header, media, title, description, and content slots.
|
|
4
|
+
|
|
5
|
+
Use Empty for onboarding gaps, no-results screens, no-notification panels, and 404-like recovery surfaces that need a clear next action.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
EmptyComponent,
|
|
12
|
+
EmptyContentComponent,
|
|
13
|
+
EmptyDescriptionComponent,
|
|
14
|
+
EmptyHeaderComponent,
|
|
15
|
+
EmptyMediaComponent,
|
|
16
|
+
EmptyTitleComponent,
|
|
17
|
+
} from '@ojiepermana/angular/component/empty';
|
|
18
|
+
import { ButtonComponent } from '@ojiepermana/angular/component/button';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Composition
|
|
22
|
+
|
|
23
|
+
The Angular structure follows the shadcn Empty information architecture while keeping actions on native buttons and anchors.
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
Empty
|
|
27
|
+
├── EmptyHeader
|
|
28
|
+
│ ├── EmptyMedia
|
|
29
|
+
│ ├── EmptyTitle
|
|
30
|
+
│ └── EmptyDescription
|
|
31
|
+
└── EmptyContent
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Basic usage
|
|
35
|
+
|
|
36
|
+
```html
|
|
37
|
+
<Empty class="max-w-xl rounded-2xl border border-border bg-card/40">
|
|
38
|
+
<EmptyHeader>
|
|
39
|
+
<EmptyMedia variant="icon">
|
|
40
|
+
<svg
|
|
41
|
+
aria-hidden="true"
|
|
42
|
+
viewBox="0 0 24 24"
|
|
43
|
+
fill="none"
|
|
44
|
+
stroke="currentColor"
|
|
45
|
+
stroke-width="2"
|
|
46
|
+
>
|
|
47
|
+
<path d="M12 6v12" />
|
|
48
|
+
<path d="M6 12h12" />
|
|
49
|
+
</svg>
|
|
50
|
+
</EmptyMedia>
|
|
51
|
+
<EmptyTitle>No projects yet</EmptyTitle>
|
|
52
|
+
<EmptyDescription>
|
|
53
|
+
You have not created any projects yet. Start by creating a new project or importing an
|
|
54
|
+
existing one.
|
|
55
|
+
</EmptyDescription>
|
|
56
|
+
</EmptyHeader>
|
|
57
|
+
|
|
58
|
+
<EmptyContent class="sm:flex-row">
|
|
59
|
+
<button Button type="button">Create project</button>
|
|
60
|
+
<button Button type="button" variant="outline">Import project</button>
|
|
61
|
+
</EmptyContent>
|
|
62
|
+
|
|
63
|
+
<a Button href="/docs/getting-started" variant="link" class="text-muted-foreground">Learn more</a>
|
|
64
|
+
</Empty>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Common patterns
|
|
68
|
+
|
|
69
|
+
### Outline shell
|
|
70
|
+
|
|
71
|
+
Use border utilities on the root when the empty state should read like an inset card or dashboard panel.
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<Empty class="rounded-2xl border border-dashed border-border">
|
|
75
|
+
<EmptyHeader>
|
|
76
|
+
<EmptyMedia variant="icon">...</EmptyMedia>
|
|
77
|
+
<EmptyTitle>Cloud storage empty</EmptyTitle>
|
|
78
|
+
<EmptyDescription>Upload files to your cloud storage to access them anywhere.</EmptyDescription>
|
|
79
|
+
</EmptyHeader>
|
|
80
|
+
<EmptyContent>
|
|
81
|
+
<button Button type="button" variant="outline" size="sm">Upload files</button>
|
|
82
|
+
</EmptyContent>
|
|
83
|
+
</Empty>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Avatar media
|
|
87
|
+
|
|
88
|
+
Leave `EmptyMedia` on the default variant when projecting an avatar, avatar group, or any other custom artwork.
|
|
89
|
+
|
|
90
|
+
```html
|
|
91
|
+
<Empty>
|
|
92
|
+
<EmptyHeader>
|
|
93
|
+
<EmptyMedia>
|
|
94
|
+
<Avatar class="size-12">
|
|
95
|
+
<AvatarImage src="https://github.com/shadcn.png" alt="shadcn" />
|
|
96
|
+
<AvatarFallback>CN</AvatarFallback>
|
|
97
|
+
</Avatar>
|
|
98
|
+
</EmptyMedia>
|
|
99
|
+
<EmptyTitle>User offline</EmptyTitle>
|
|
100
|
+
<EmptyDescription
|
|
101
|
+
>Leave a message and they will be notified when they come back online.</EmptyDescription
|
|
102
|
+
>
|
|
103
|
+
</EmptyHeader>
|
|
104
|
+
<EmptyContent>
|
|
105
|
+
<button Button type="button" size="sm">Leave message</button>
|
|
106
|
+
</EmptyContent>
|
|
107
|
+
</Empty>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Input group recovery
|
|
111
|
+
|
|
112
|
+
Compose `EmptyContent` with the existing input-group primitives when the empty state should immediately guide recovery or search.
|
|
113
|
+
|
|
114
|
+
```html
|
|
115
|
+
<Empty class="max-w-xl rounded-2xl border border-border">
|
|
116
|
+
<EmptyHeader>
|
|
117
|
+
<EmptyTitle>404 - Not found</EmptyTitle>
|
|
118
|
+
<EmptyDescription>
|
|
119
|
+
The page you are looking for does not exist. Try searching for what you need below.
|
|
120
|
+
</EmptyDescription>
|
|
121
|
+
</EmptyHeader>
|
|
122
|
+
<EmptyContent class="max-w-md">
|
|
123
|
+
<InputGroup class="w-full">
|
|
124
|
+
<input InputGroupInput aria-label="Search pages" placeholder="Try searching for pages..." />
|
|
125
|
+
<InputGroupAddon>
|
|
126
|
+
<span aria-hidden="true">⌕</span>
|
|
127
|
+
</InputGroupAddon>
|
|
128
|
+
<InputGroupAddon align="inline-end">
|
|
129
|
+
<span
|
|
130
|
+
aria-hidden="true"
|
|
131
|
+
class="inline-flex h-6 items-center rounded-md border border-border bg-background px-2 font-mono text-[11px] font-medium text-foreground shadow-sm"
|
|
132
|
+
>
|
|
133
|
+
/
|
|
134
|
+
</span>
|
|
135
|
+
</InputGroupAddon>
|
|
136
|
+
</InputGroup>
|
|
137
|
+
</EmptyContent>
|
|
138
|
+
</Empty>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## API reference
|
|
142
|
+
|
|
143
|
+
### `EmptyComponent`
|
|
144
|
+
|
|
145
|
+
| Input | Type | Default |
|
|
146
|
+
| ------- | -------- | ------- |
|
|
147
|
+
| `class` | `string` | `''` |
|
|
148
|
+
|
|
149
|
+
### `EmptyMediaComponent`
|
|
150
|
+
|
|
151
|
+
| Input | Type | Default |
|
|
152
|
+
| --------- | --------------------- | ----------- |
|
|
153
|
+
| `variant` | `'default' \| 'icon'` | `'default'` |
|
|
154
|
+
| `class` | `string` | `''` |
|
|
155
|
+
|
|
156
|
+
### Parts
|
|
157
|
+
|
|
158
|
+
- `EmptyHeader` centers the media, title, and description stack.
|
|
159
|
+
- `EmptyTitle` provides the primary heading.
|
|
160
|
+
- `EmptyDescription` renders supporting copy with muted foreground color.
|
|
161
|
+
- `EmptyContent` groups actions, inputs, or recovery affordances under the header.
|
|
162
|
+
|
|
163
|
+
All Empty parts also accept a `class` input.
|
|
164
|
+
|
|
165
|
+
## Styling and theming
|
|
166
|
+
|
|
167
|
+
The primitive keeps styling neutral by default: the root controls spacing and alignment, while visual shells such as borders, gradients, and muted backgrounds are layered through `class`.
|
|
168
|
+
|
|
169
|
+
`EmptyMedia` uses `border-border` and `bg-muted/40` for the icon variant so the icon chip matches the rest of the theme without inheriting text color for borders.
|
|
170
|
+
|
|
171
|
+
Use `class` on the root or parts to add panel borders, gradient backgrounds, tighter widths, or horizontal action layouts.
|
|
172
|
+
|
|
173
|
+
## Accessibility
|
|
174
|
+
|
|
175
|
+
- Keep the title and description visible so the empty state communicates both the missing content and the next step.
|
|
176
|
+
- Put actions on native `<button>` or `<a Button>` hosts instead of faking interactivity with generic elements.
|
|
177
|
+
- Mark decorative icons inside `EmptyMedia` as `aria-hidden="true"` unless the icon itself provides unique meaning.
|
|
178
|
+
|
|
179
|
+
## Keyboard interactions
|
|
180
|
+
|
|
181
|
+
- The Empty primitives themselves are passive layout containers and do not enter the tab order.
|
|
182
|
+
- Projected buttons, anchors, and inputs keep their native Tab, Enter, and Space behavior.
|
|
183
|
+
- When `EmptyContent` contains an input group, the control remains first in the DOM so tab order stays predictable.
|
|
184
|
+
|
|
185
|
+
## Angular notes
|
|
186
|
+
|
|
187
|
+
- `Empty` is a reusable content primitive, separate from application route-shell layout concerns.
|
|
188
|
+
- The link action from the upstream shadcn preview maps cleanly to `<a Button variant="link">` in Angular.
|
|
189
|
+
- Project any custom artwork into `EmptyMedia`; only the `icon` variant applies a circular icon shell.
|
|
190
|
+
|
|
191
|
+
## Source parity
|
|
192
|
+
|
|
193
|
+
This Angular implementation follows the shadcn Empty page structure, examples, and API reference while translating React composition to Angular selectors and keeping page-shell layout concerns separate from the reusable empty-state primitive.
|