@ojiepermana/angular 22.1.11 → 22.1.13
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 +374 -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
|
@@ -1,7 +1,693 @@
|
|
|
1
|
-
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, ElementRef, input, output, Directive, computed, Component, model, forwardRef, contentChildren, Injector, TemplateRef, ViewContainerRef, DestroyRef, signal } from '@angular/core';
|
|
3
|
+
import { cn, injectUniqueId } from '@ojiepermana/angular/component/utils/core';
|
|
4
|
+
import { FocusKeyManager } from '@angular/cdk/a11y';
|
|
5
|
+
import * as i1 from '@ojiepermana/angular/component/utils/scale';
|
|
6
|
+
import { ComponentScaleDirective } from '@ojiepermana/angular/component/utils/scale';
|
|
7
|
+
import { Overlay } from '@angular/cdk/overlay';
|
|
8
|
+
import { TemplatePortal } from '@angular/cdk/portal';
|
|
9
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
10
|
+
import { merge } from 'rxjs';
|
|
11
|
+
import { filter } from 'rxjs/operators';
|
|
12
|
+
import { connectedPositionFor, oppositeSide } from '@ojiepermana/angular/component/utils/overlay';
|
|
2
13
|
|
|
3
|
-
|
|
14
|
+
const MENU_OVERLAY = new InjectionToken('MenuOverlayController');
|
|
15
|
+
/**
|
|
16
|
+
* Marker every `MenuTrigger` provides on its own element.
|
|
17
|
+
*
|
|
18
|
+
* A submenu is a menu item that also opens a menu. Choosing it must open the
|
|
19
|
+
* submenu, not close the menu it lives in, so an item that finds this marker on
|
|
20
|
+
* itself skips the close-after-select step.
|
|
21
|
+
*/
|
|
22
|
+
const MENU_SUBMENU_TRIGGER = new InjectionToken('MenuSubmenuTrigger');
|
|
23
|
+
|
|
24
|
+
class MenuFocusableItem {
|
|
25
|
+
el = inject(ElementRef);
|
|
26
|
+
/** Present whenever the item is rendered inside a `MenuTrigger` overlay. */
|
|
27
|
+
overlay = inject(MENU_OVERLAY, { optional: true });
|
|
28
|
+
/** True when this same element also opens a submenu. */
|
|
29
|
+
opensSubmenu = inject(MENU_SUBMENU_TRIGGER, { optional: true, self: true }) ?? false;
|
|
30
|
+
disabledInput = input(false, { ...(ngDevMode ? { debugName: "disabledInput" } : /* istanbul ignore next */ {}), alias: 'disabled' });
|
|
31
|
+
inset = input(false, /* @ts-ignore */
|
|
32
|
+
...(ngDevMode ? [{ debugName: "inset" }] : /* istanbul ignore next */ []));
|
|
33
|
+
class = input('', /* @ts-ignore */
|
|
34
|
+
...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
|
|
35
|
+
variant = input('default', /* @ts-ignore */
|
|
36
|
+
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
37
|
+
selected = output();
|
|
38
|
+
get disabled() {
|
|
39
|
+
return this.disabledInput();
|
|
40
|
+
}
|
|
41
|
+
focus() {
|
|
42
|
+
this.el.nativeElement.focus();
|
|
43
|
+
}
|
|
44
|
+
getLabel() {
|
|
45
|
+
return this.el.nativeElement.textContent?.trim() ?? '';
|
|
46
|
+
}
|
|
47
|
+
buildItemClasses(extraClass = '') {
|
|
48
|
+
return cn('relative flex w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none', 'transition-colors focus:bg-accent focus:text-accent-foreground', 'aria-disabled:pointer-events-none aria-disabled:opacity-50', 'data-[inset=true]:pl-8', 'data-[variant=destructive]:text-destructive', 'data-[variant=destructive]:focus:bg-destructive/10', 'data-[variant=destructive]:focus:text-destructive', extraClass, this.class());
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Consume the keystroke that activated the item.
|
|
52
|
+
*
|
|
53
|
+
* Items render on `button` hosts, and a button turns Enter and Space into an
|
|
54
|
+
* activation click of its own. With `(click)` and `(keydown.enter|space)` both
|
|
55
|
+
* wired to the same handler that click runs the handler a SECOND time: a
|
|
56
|
+
* checkbox toggled on keydown flipped straight back, so `aria-checked` never
|
|
57
|
+
* moved, and a plain item emitted `selected` twice whenever the menu was told
|
|
58
|
+
* not to close. Preventing the default suppresses the synthesised click, which
|
|
59
|
+
* leaves exactly one activation per keystroke on native and non-native hosts
|
|
60
|
+
* alike. It also stops Space from scrolling the page behind the menu.
|
|
61
|
+
*/
|
|
62
|
+
emitSelection(event) {
|
|
63
|
+
if (this.disabled) {
|
|
64
|
+
event.preventDefault();
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (event.type === 'keydown') {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
}
|
|
70
|
+
this.selected.emit(event);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Close the menu and return focus to the trigger, which is the menu pattern.
|
|
75
|
+
*
|
|
76
|
+
* Call this AFTER the item has updated its own state: closing tears down the
|
|
77
|
+
* overlay view, and a checkbox or radio that wrote its model afterwards would
|
|
78
|
+
* never propagate the change. A consumer that already closes the menu by hand
|
|
79
|
+
* is fine, closing twice is a no-op.
|
|
80
|
+
*/
|
|
81
|
+
finishSelection() {
|
|
82
|
+
if (this.opensSubmenu) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
this.overlay?.closeAfterSelect();
|
|
86
|
+
}
|
|
87
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuFocusableItem, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
88
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.4", type: MenuFocusableItem, isStandalone: true, inputs: { disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, inset: { classPropertyName: "inset", publicName: "inset", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selected" }, ngImport: i0 });
|
|
89
|
+
}
|
|
90
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuFocusableItem, decorators: [{
|
|
91
|
+
type: Directive
|
|
92
|
+
}], propDecorators: { disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], inset: [{ type: i0.Input, args: [{ isSignal: true, alias: "inset", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], selected: [{ type: i0.Output, args: ["selected"] }] } });
|
|
93
|
+
class MenuGroupComponent {
|
|
94
|
+
class = input('', /* @ts-ignore */
|
|
95
|
+
...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
|
|
96
|
+
classes = computed(() => cn('block', this.class()), /* @ts-ignore */
|
|
97
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
98
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
99
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.4", type: MenuGroupComponent, isStandalone: true, selector: "MenuGroup", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.role": "\"group\"" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
100
|
+
}
|
|
101
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuGroupComponent, decorators: [{
|
|
102
|
+
type: Component,
|
|
103
|
+
args: [{
|
|
104
|
+
selector: 'MenuGroup',
|
|
105
|
+
host: {
|
|
106
|
+
'[class]': 'classes()',
|
|
107
|
+
'[attr.role]': '"group"',
|
|
108
|
+
},
|
|
109
|
+
template: `<ng-content />`,
|
|
110
|
+
}]
|
|
111
|
+
}], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
|
|
112
|
+
class MenuRadioGroupBase {
|
|
113
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuRadioGroupBase, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
114
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.4", type: MenuRadioGroupBase, isStandalone: true, ngImport: i0 });
|
|
115
|
+
}
|
|
116
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuRadioGroupBase, decorators: [{
|
|
117
|
+
type: Directive
|
|
118
|
+
}] });
|
|
119
|
+
class MenuRadioGroupComponent extends MenuRadioGroupBase {
|
|
120
|
+
value = model(null, /* @ts-ignore */
|
|
121
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
122
|
+
class = input('', /* @ts-ignore */
|
|
123
|
+
...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
|
|
124
|
+
classes = computed(() => cn('block', this.class()), /* @ts-ignore */
|
|
125
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
126
|
+
isChecked(value) {
|
|
127
|
+
return this.value() === value;
|
|
128
|
+
}
|
|
129
|
+
select(value) {
|
|
130
|
+
this.value.set(value);
|
|
131
|
+
}
|
|
132
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuRadioGroupComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
133
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.4", type: MenuRadioGroupComponent, isStandalone: true, selector: "MenuRadioGroup", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class": "classes()", "attr.role": "\"group\"" } }, providers: [
|
|
134
|
+
{ provide: MenuRadioGroupBase, useExisting: forwardRef(() => MenuRadioGroupComponent) },
|
|
135
|
+
], usesInheritance: true, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
136
|
+
}
|
|
137
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuRadioGroupComponent, decorators: [{
|
|
138
|
+
type: Component,
|
|
139
|
+
args: [{
|
|
140
|
+
selector: 'MenuRadioGroup',
|
|
141
|
+
providers: [
|
|
142
|
+
{ provide: MenuRadioGroupBase, useExisting: forwardRef(() => MenuRadioGroupComponent) },
|
|
143
|
+
],
|
|
144
|
+
host: {
|
|
145
|
+
'[class]': 'classes()',
|
|
146
|
+
'[attr.role]': '"group"',
|
|
147
|
+
},
|
|
148
|
+
template: `<ng-content />`,
|
|
149
|
+
}]
|
|
150
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
|
|
151
|
+
class MenuItemComponent extends MenuFocusableItem {
|
|
152
|
+
classes = computed(() => this.buildItemClasses(), /* @ts-ignore */
|
|
153
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
154
|
+
handleClick(e) {
|
|
155
|
+
if (!this.emitSelection(e)) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
this.finishSelection();
|
|
159
|
+
}
|
|
160
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuItemComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
161
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.4", type: MenuItemComponent, isStandalone: true, selector: "MenuItem, button[MenuItem]", host: { attributes: { "tabindex": "-1" }, listeners: { "click": "handleClick($any($event))", "keydown.enter": "handleClick($any($event))", "keydown.space": "handleClick($any($event))" }, properties: { "class": "classes()", "attr.role": "\"menuitem\"", "attr.aria-disabled": "disabled ? \"true\" : null", "attr.data-inset": "inset() ? \"true\" : null", "attr.data-variant": "variant()" } }, providers: [{ provide: MenuFocusableItem, useExisting: forwardRef(() => MenuItemComponent) }], usesInheritance: true, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
162
|
+
}
|
|
163
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuItemComponent, decorators: [{
|
|
164
|
+
type: Component,
|
|
165
|
+
args: [{
|
|
166
|
+
selector: 'MenuItem, button[MenuItem]',
|
|
167
|
+
providers: [{ provide: MenuFocusableItem, useExisting: forwardRef(() => MenuItemComponent) }],
|
|
168
|
+
host: {
|
|
169
|
+
'[class]': 'classes()',
|
|
170
|
+
'[attr.role]': '"menuitem"',
|
|
171
|
+
tabindex: '-1',
|
|
172
|
+
'[attr.aria-disabled]': 'disabled ? "true" : null',
|
|
173
|
+
'[attr.data-inset]': 'inset() ? "true" : null',
|
|
174
|
+
'[attr.data-variant]': 'variant()',
|
|
175
|
+
'(click)': 'handleClick($any($event))',
|
|
176
|
+
'(keydown.enter)': 'handleClick($any($event))',
|
|
177
|
+
'(keydown.space)': 'handleClick($any($event))',
|
|
178
|
+
},
|
|
179
|
+
template: `<ng-content />`,
|
|
180
|
+
}]
|
|
181
|
+
}] });
|
|
182
|
+
class MenuCheckboxItemComponent extends MenuFocusableItem {
|
|
183
|
+
checked = model(false, /* @ts-ignore */
|
|
184
|
+
...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
|
|
185
|
+
classes = computed(() => this.buildItemClasses('pl-8'), /* @ts-ignore */
|
|
186
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
187
|
+
// A checkbox item does NOT close the menu, unlike a plain item or a radio.
|
|
188
|
+
//
|
|
189
|
+
// Ticking boxes is a repeated action by nature -- a column picker, a filter
|
|
190
|
+
// list -- and closing after each one turned "hide three columns" into three
|
|
191
|
+
// trips through the trigger. Radio stays as it is: choosing one of several
|
|
192
|
+
// genuinely ends the interaction, so closing there is right.
|
|
193
|
+
//
|
|
194
|
+
// Escape, Tab, a click outside, or picking a plain item all still close it, so
|
|
195
|
+
// there is no way to get stuck in an open menu.
|
|
196
|
+
toggle(event) {
|
|
197
|
+
if (!this.emitSelection(event)) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
this.checked.set(!this.checked());
|
|
201
|
+
}
|
|
202
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuCheckboxItemComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
203
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: MenuCheckboxItemComponent, isStandalone: true, selector: "MenuCheckboxItem, button[MenuCheckboxItem]", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange" }, host: { attributes: { "tabindex": "-1" }, listeners: { "click": "toggle($any($event))", "keydown.enter": "toggle($any($event))", "keydown.space": "toggle($any($event))" }, properties: { "class": "classes()", "attr.role": "\"menuitemcheckbox\"", "attr.aria-disabled": "disabled ? \"true\" : null", "attr.aria-checked": "checked()", "attr.data-state": "checked() ? \"checked\" : \"unchecked\"", "attr.data-variant": "variant()" } }, providers: [
|
|
204
|
+
{ provide: MenuFocusableItem, useExisting: forwardRef(() => MenuCheckboxItemComponent) },
|
|
205
|
+
], usesInheritance: true, ngImport: i0, template: `
|
|
206
|
+
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
207
|
+
@if (checked()) {
|
|
208
|
+
<svg
|
|
209
|
+
aria-hidden="true"
|
|
210
|
+
class="h-4 w-4"
|
|
211
|
+
viewBox="0 0 24 24"
|
|
212
|
+
fill="none"
|
|
213
|
+
stroke="currentColor"
|
|
214
|
+
stroke-width="2"
|
|
215
|
+
stroke-linecap="round"
|
|
216
|
+
stroke-linejoin="round"
|
|
217
|
+
>
|
|
218
|
+
<polyline points="20 6 9 17 4 12" />
|
|
219
|
+
</svg>
|
|
220
|
+
}
|
|
221
|
+
</span>
|
|
222
|
+
<ng-content />
|
|
223
|
+
`, isInline: true });
|
|
224
|
+
}
|
|
225
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuCheckboxItemComponent, decorators: [{
|
|
226
|
+
type: Component,
|
|
227
|
+
args: [{
|
|
228
|
+
selector: 'MenuCheckboxItem, button[MenuCheckboxItem]',
|
|
229
|
+
providers: [
|
|
230
|
+
{ provide: MenuFocusableItem, useExisting: forwardRef(() => MenuCheckboxItemComponent) },
|
|
231
|
+
],
|
|
232
|
+
host: {
|
|
233
|
+
'[class]': 'classes()',
|
|
234
|
+
'[attr.role]': '"menuitemcheckbox"',
|
|
235
|
+
tabindex: '-1',
|
|
236
|
+
'[attr.aria-disabled]': 'disabled ? "true" : null',
|
|
237
|
+
'[attr.aria-checked]': 'checked()',
|
|
238
|
+
'[attr.data-state]': 'checked() ? "checked" : "unchecked"',
|
|
239
|
+
'[attr.data-variant]': 'variant()',
|
|
240
|
+
'(click)': 'toggle($any($event))',
|
|
241
|
+
'(keydown.enter)': 'toggle($any($event))',
|
|
242
|
+
'(keydown.space)': 'toggle($any($event))',
|
|
243
|
+
},
|
|
244
|
+
template: `
|
|
245
|
+
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
246
|
+
@if (checked()) {
|
|
247
|
+
<svg
|
|
248
|
+
aria-hidden="true"
|
|
249
|
+
class="h-4 w-4"
|
|
250
|
+
viewBox="0 0 24 24"
|
|
251
|
+
fill="none"
|
|
252
|
+
stroke="currentColor"
|
|
253
|
+
stroke-width="2"
|
|
254
|
+
stroke-linecap="round"
|
|
255
|
+
stroke-linejoin="round"
|
|
256
|
+
>
|
|
257
|
+
<polyline points="20 6 9 17 4 12" />
|
|
258
|
+
</svg>
|
|
259
|
+
}
|
|
260
|
+
</span>
|
|
261
|
+
<ng-content />
|
|
262
|
+
`,
|
|
263
|
+
}]
|
|
264
|
+
}], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }] } });
|
|
265
|
+
class MenuRadioItemComponent extends MenuFocusableItem {
|
|
266
|
+
group = inject(MenuRadioGroupBase);
|
|
267
|
+
value = input.required(/* @ts-ignore */
|
|
268
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
269
|
+
checked = computed(() => this.group.isChecked(this.value()), /* @ts-ignore */
|
|
270
|
+
...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
|
|
271
|
+
classes = computed(() => this.buildItemClasses('pl-8'), /* @ts-ignore */
|
|
272
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
273
|
+
select(event) {
|
|
274
|
+
if (!this.emitSelection(event)) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
this.group.select(this.value());
|
|
278
|
+
this.finishSelection();
|
|
279
|
+
}
|
|
280
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuRadioItemComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
281
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: MenuRadioItemComponent, isStandalone: true, selector: "MenuRadioItem, button[MenuRadioItem]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "tabindex": "-1" }, listeners: { "click": "select($any($event))", "keydown.enter": "select($any($event))", "keydown.space": "select($any($event))" }, properties: { "class": "classes()", "attr.role": "\"menuitemradio\"", "attr.aria-disabled": "disabled ? \"true\" : null", "attr.aria-checked": "checked()", "attr.data-state": "checked() ? \"checked\" : \"unchecked\"", "attr.data-variant": "variant()" } }, providers: [
|
|
282
|
+
{ provide: MenuFocusableItem, useExisting: forwardRef(() => MenuRadioItemComponent) },
|
|
283
|
+
], usesInheritance: true, ngImport: i0, template: `
|
|
284
|
+
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
285
|
+
@if (checked()) {
|
|
286
|
+
<span aria-hidden="true" class="h-2 w-2 rounded-full bg-current"></span>
|
|
287
|
+
}
|
|
288
|
+
</span>
|
|
289
|
+
<ng-content />
|
|
290
|
+
`, isInline: true });
|
|
291
|
+
}
|
|
292
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuRadioItemComponent, decorators: [{
|
|
293
|
+
type: Component,
|
|
294
|
+
args: [{
|
|
295
|
+
selector: 'MenuRadioItem, button[MenuRadioItem]',
|
|
296
|
+
providers: [
|
|
297
|
+
{ provide: MenuFocusableItem, useExisting: forwardRef(() => MenuRadioItemComponent) },
|
|
298
|
+
],
|
|
299
|
+
host: {
|
|
300
|
+
'[class]': 'classes()',
|
|
301
|
+
'[attr.role]': '"menuitemradio"',
|
|
302
|
+
tabindex: '-1',
|
|
303
|
+
'[attr.aria-disabled]': 'disabled ? "true" : null',
|
|
304
|
+
'[attr.aria-checked]': 'checked()',
|
|
305
|
+
'[attr.data-state]': 'checked() ? "checked" : "unchecked"',
|
|
306
|
+
'[attr.data-variant]': 'variant()',
|
|
307
|
+
'(click)': 'select($any($event))',
|
|
308
|
+
'(keydown.enter)': 'select($any($event))',
|
|
309
|
+
'(keydown.space)': 'select($any($event))',
|
|
310
|
+
},
|
|
311
|
+
template: `
|
|
312
|
+
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
313
|
+
@if (checked()) {
|
|
314
|
+
<span aria-hidden="true" class="h-2 w-2 rounded-full bg-current"></span>
|
|
315
|
+
}
|
|
316
|
+
</span>
|
|
317
|
+
<ng-content />
|
|
318
|
+
`,
|
|
319
|
+
}]
|
|
320
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }] } });
|
|
321
|
+
class MenuSeparatorComponent {
|
|
322
|
+
class = input('', /* @ts-ignore */
|
|
323
|
+
...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
|
|
324
|
+
classes = computed(() => cn('-mx-1 my-1 block h-px bg-border', this.class()), /* @ts-ignore */
|
|
325
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
326
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuSeparatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
327
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.4", type: MenuSeparatorComponent, isStandalone: true, selector: "MenuSeparator", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.role": "\"separator\"", "attr.aria-orientation": "\"horizontal\"" } }, ngImport: i0, template: '', isInline: true });
|
|
328
|
+
}
|
|
329
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuSeparatorComponent, decorators: [{
|
|
330
|
+
type: Component,
|
|
331
|
+
args: [{
|
|
332
|
+
selector: 'MenuSeparator',
|
|
333
|
+
host: {
|
|
334
|
+
'[class]': 'classes()',
|
|
335
|
+
'[attr.role]': '"separator"',
|
|
336
|
+
'[attr.aria-orientation]': '"horizontal"',
|
|
337
|
+
},
|
|
338
|
+
template: '',
|
|
339
|
+
}]
|
|
340
|
+
}], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
|
|
341
|
+
class MenuLabelComponent {
|
|
342
|
+
inset = input(false, /* @ts-ignore */
|
|
343
|
+
...(ngDevMode ? [{ debugName: "inset" }] : /* istanbul ignore next */ []));
|
|
344
|
+
class = input('', /* @ts-ignore */
|
|
345
|
+
...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
|
|
346
|
+
classes = computed(() => cn('px-2 py-1.5 text-sm font-semibold text-foreground', this.inset() ? 'pl-8' : '', this.class()), /* @ts-ignore */
|
|
347
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
348
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuLabelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
349
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.4", type: MenuLabelComponent, isStandalone: true, selector: "MenuLabel", inputs: { inset: { classPropertyName: "inset", publicName: "inset", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
350
|
+
}
|
|
351
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuLabelComponent, decorators: [{
|
|
352
|
+
type: Component,
|
|
353
|
+
args: [{
|
|
354
|
+
selector: 'MenuLabel',
|
|
355
|
+
host: { '[class]': 'classes()' },
|
|
356
|
+
template: `<ng-content />`,
|
|
357
|
+
}]
|
|
358
|
+
}], propDecorators: { inset: [{ type: i0.Input, args: [{ isSignal: true, alias: "inset", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
|
|
359
|
+
class MenuShortcutComponent {
|
|
360
|
+
class = input('', /* @ts-ignore */
|
|
361
|
+
...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
|
|
362
|
+
classes = computed(() => cn('ml-auto text-xs tracking-widest text-muted-foreground', this.class()), /* @ts-ignore */
|
|
363
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
364
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuShortcutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
365
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.4", type: MenuShortcutComponent, isStandalone: true, selector: "MenuShortcut, span[MenuShortcut]", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
366
|
+
}
|
|
367
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuShortcutComponent, decorators: [{
|
|
368
|
+
type: Component,
|
|
369
|
+
args: [{
|
|
370
|
+
selector: 'MenuShortcut, span[MenuShortcut]',
|
|
371
|
+
host: { '[class]': 'classes()' },
|
|
372
|
+
template: `<ng-content />`,
|
|
373
|
+
}]
|
|
374
|
+
}], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Surface rendered inside the overlay. Hosts keyboard navigation for its items.
|
|
378
|
+
*/
|
|
379
|
+
class MenuSurfaceComponent {
|
|
380
|
+
class = input('', /* @ts-ignore */
|
|
381
|
+
...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
|
|
382
|
+
items = contentChildren(MenuFocusableItem, { ...(ngDevMode ? { debugName: "items" } : /* istanbul ignore next */ {}), descendants: true });
|
|
383
|
+
/** Present whenever the surface is rendered by a `MenuTrigger`. */
|
|
384
|
+
overlay = inject(MENU_OVERLAY, { optional: true });
|
|
385
|
+
/**
|
|
386
|
+
* Emitted when the surface asks to be closed (today: on Tab). The trigger now
|
|
387
|
+
* closes itself, so consumers no longer have to wire this up; it stays for
|
|
388
|
+
* anyone who already listens to it.
|
|
389
|
+
*/
|
|
390
|
+
closeRequested = output();
|
|
391
|
+
/** Tied to the trigger's `aria-controls`, so the pair references each other. */
|
|
392
|
+
surfaceId = this.overlay?.surfaceId ?? injectUniqueId('MenuSurface');
|
|
393
|
+
/** Names the menu after the control that opened it. */
|
|
394
|
+
labelledBy = this.overlay?.triggerId ?? null;
|
|
395
|
+
keyManager = new FocusKeyManager(this.items, inject(Injector))
|
|
396
|
+
.withWrap()
|
|
397
|
+
.withTypeAhead()
|
|
398
|
+
.skipPredicate((item) => item.disabled);
|
|
399
|
+
classes = computed(() => cn('z-50 min-w-32 overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none', this.class()), /* @ts-ignore */
|
|
400
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
401
|
+
ngAfterContentInit() {
|
|
402
|
+
// Focus the first enabled item when the menu opens.
|
|
403
|
+
queueMicrotask(() => this.keyManager.setFirstItemActive());
|
|
404
|
+
}
|
|
405
|
+
onArrow(e, delta) {
|
|
406
|
+
e.preventDefault();
|
|
407
|
+
this.keyManager.cancelTypeahead();
|
|
408
|
+
if (delta > 0) {
|
|
409
|
+
this.keyManager.setNextItemActive();
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
this.keyManager.setPreviousItemActive();
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// ArrowLeft closes a SUBMENU and hands focus back to the item that opened it,
|
|
416
|
+
// which is the menu pattern.
|
|
417
|
+
//
|
|
418
|
+
// A root menu leaves the key alone. The row of menus above it owns left and
|
|
419
|
+
// right there: a `Menubar` listens for them on its own overlay to walk to the
|
|
420
|
+
// neighbouring menu, and it only ever sees the key because this surface let it
|
|
421
|
+
// carry on bubbling.
|
|
422
|
+
onArrowLeft(e) {
|
|
423
|
+
if (!this.overlay?.isSubmenu) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
e.preventDefault();
|
|
427
|
+
this.overlay.close();
|
|
428
|
+
}
|
|
429
|
+
onHome(e) {
|
|
430
|
+
e.preventDefault();
|
|
431
|
+
this.keyManager.cancelTypeahead();
|
|
432
|
+
this.keyManager.setFirstItemActive();
|
|
433
|
+
}
|
|
434
|
+
onEnd(e) {
|
|
435
|
+
e.preventDefault();
|
|
436
|
+
this.keyManager.cancelTypeahead();
|
|
437
|
+
this.keyManager.setLastItemActive();
|
|
438
|
+
}
|
|
439
|
+
// Routes printable characters into the key manager's type-ahead buffer, which
|
|
440
|
+
// is the only thing that fills it: `withTypeAhead()` above configures the
|
|
441
|
+
// buffer, but nothing reaches it unless `onKeydown` is called.
|
|
442
|
+
//
|
|
443
|
+
// Only printable characters are forwarded, and Space is not one of them here.
|
|
444
|
+
// Every other key already has an owner -- the arrows and Home/End above, Enter
|
|
445
|
+
// and Space on the item, Tab on the trigger -- and handing those to the key
|
|
446
|
+
// manager as well would run two handlers for one keystroke.
|
|
447
|
+
//
|
|
448
|
+
// Space belongs to the item unconditionally. Letting it extend a search in
|
|
449
|
+
// progress, so that a two-word label could be matched past its first word,
|
|
450
|
+
// would mean intercepting the key BEFORE the focused item acts on it: this
|
|
451
|
+
// listener runs on the bubbling phase, by which point `(keydown.space)` on the
|
|
452
|
+
// item has already activated it and closed the menu. Reaching it would take a
|
|
453
|
+
// capture-phase listener that swallows Space, which is a lot of machinery, and
|
|
454
|
+
// a way to activate an item that stops working mid-search, for a matching
|
|
455
|
+
// nicety the menu contract never asked for.
|
|
456
|
+
onTypeAhead(e) {
|
|
457
|
+
if (e.key.length !== 1 || e.key === ' ' || e.ctrlKey || e.metaKey || e.altKey) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
this.keyManager.onKeydown(e);
|
|
461
|
+
}
|
|
462
|
+
onTab(e) {
|
|
463
|
+
// No preventDefault: the menu closes, focus goes back to the trigger, and
|
|
464
|
+
// the browser carries on with the tab sequence from there.
|
|
465
|
+
void e;
|
|
466
|
+
this.closeRequested.emit();
|
|
467
|
+
this.overlay?.close();
|
|
468
|
+
}
|
|
469
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuSurfaceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
470
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.4", type: MenuSurfaceComponent, isStandalone: true, selector: "MenuSurface", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closeRequested: "closeRequested" }, host: { attributes: { "tabindex": "-1" }, listeners: { "keydown.arrowDown": "onArrow($any($event), 1)", "keydown.arrowUp": "onArrow($any($event), -1)", "keydown.arrowLeft": "onArrowLeft($any($event))", "keydown.home": "onHome($any($event))", "keydown.end": "onEnd($any($event))", "keydown.tab": "onTab($any($event))", "keydown": "onTypeAhead($any($event))" }, properties: { "class": "classes()", "attr.role": "\"menu\"", "attr.id": "surfaceId", "attr.aria-labelledby": "labelledBy" } }, queries: [{ propertyName: "items", predicate: MenuFocusableItem, descendants: true, isSignal: true }], hostDirectives: [{ directive: i1.ComponentScaleDirective, inputs: ["radius", "radius", "density", "density"] }], ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
471
|
+
}
|
|
472
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuSurfaceComponent, decorators: [{
|
|
473
|
+
type: Component,
|
|
474
|
+
args: [{
|
|
475
|
+
selector: 'MenuSurface',
|
|
476
|
+
hostDirectives: [
|
|
477
|
+
{
|
|
478
|
+
directive: ComponentScaleDirective,
|
|
479
|
+
inputs: ['radius', 'density'],
|
|
480
|
+
},
|
|
481
|
+
],
|
|
482
|
+
host: {
|
|
483
|
+
'[class]': 'classes()',
|
|
484
|
+
'[attr.role]': '"menu"',
|
|
485
|
+
'[attr.id]': 'surfaceId',
|
|
486
|
+
'[attr.aria-labelledby]': 'labelledBy',
|
|
487
|
+
tabindex: '-1',
|
|
488
|
+
'(keydown.arrowDown)': 'onArrow($any($event), 1)',
|
|
489
|
+
'(keydown.arrowUp)': 'onArrow($any($event), -1)',
|
|
490
|
+
'(keydown.arrowLeft)': 'onArrowLeft($any($event))',
|
|
491
|
+
'(keydown.home)': 'onHome($any($event))',
|
|
492
|
+
'(keydown.end)': 'onEnd($any($event))',
|
|
493
|
+
'(keydown.tab)': 'onTab($any($event))',
|
|
494
|
+
'(keydown)': 'onTypeAhead($any($event))',
|
|
495
|
+
},
|
|
496
|
+
template: `<ng-content />`,
|
|
497
|
+
}]
|
|
498
|
+
}], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], items: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MenuFocusableItem), { ...{ descendants: true }, isSignal: true }] }], closeRequested: [{ type: i0.Output, args: ["closeRequested"] }] } });
|
|
499
|
+
/**
|
|
500
|
+
* Wraps an `<ng-template>` that holds `<MenuSurface>` + items.
|
|
501
|
+
*/
|
|
502
|
+
class MenuContentDirective {
|
|
503
|
+
componentScale = inject(ComponentScaleDirective);
|
|
504
|
+
radius = this.componentScale.radius;
|
|
505
|
+
density = this.componentScale.density;
|
|
506
|
+
template = inject(TemplateRef);
|
|
507
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
508
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.4", type: MenuContentDirective, isStandalone: true, selector: "ng-template[MenuContent]", exportAs: ["MenuContent"], hostDirectives: [{ directive: i1.ComponentScaleDirective, inputs: ["radius", "radius", "density", "density"] }], ngImport: i0 });
|
|
509
|
+
}
|
|
510
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuContentDirective, decorators: [{
|
|
511
|
+
type: Directive,
|
|
512
|
+
args: [{
|
|
513
|
+
selector: 'ng-template[MenuContent]',
|
|
514
|
+
exportAs: 'MenuContent',
|
|
515
|
+
hostDirectives: [
|
|
516
|
+
{
|
|
517
|
+
directive: ComponentScaleDirective,
|
|
518
|
+
inputs: ['radius', 'density'],
|
|
519
|
+
},
|
|
520
|
+
],
|
|
521
|
+
}]
|
|
522
|
+
}] });
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Zero, because the gap between a trigger and its menu is NOT a position offset.
|
|
526
|
+
*
|
|
527
|
+
* CDK applies `offsetX`/`offsetY` as a `transform` on the pane, and it does so
|
|
528
|
+
* AFTER clamping the pane to the viewport -- so an offset pushes the panel back
|
|
529
|
+
* off screen by exactly its own size, which is how the panel's top edge ended up
|
|
530
|
+
* at minus four on a phone. The gap is transparent padding on the pane instead
|
|
531
|
+
* (`.menu-panel` in the theme's global layer): padding is part of the box CDK
|
|
532
|
+
* measures, so the clamp accounts for it.
|
|
533
|
+
*/
|
|
534
|
+
const MENU_SIDE_OFFSET = 0;
|
|
535
|
+
/**
|
|
536
|
+
* Opens an `<ng-template MenuContent>` anchored to the trigger element.
|
|
537
|
+
*/
|
|
538
|
+
class MenuTriggerDirective {
|
|
539
|
+
overlay = inject(Overlay);
|
|
540
|
+
vcr = inject(ViewContainerRef);
|
|
541
|
+
el = inject(ElementRef);
|
|
542
|
+
destroyRef = inject(DestroyRef);
|
|
543
|
+
injector = inject(Injector);
|
|
544
|
+
/**
|
|
545
|
+
* The controller of the menu this trigger lives in, if any. Present only when
|
|
546
|
+
* the trigger is itself an item of another menu, which is exactly what makes
|
|
547
|
+
* the menu it opens a submenu.
|
|
548
|
+
*/
|
|
549
|
+
parentMenu = inject(MENU_OVERLAY, { optional: true });
|
|
550
|
+
MenuTrigger = input.required(/* @ts-ignore */
|
|
551
|
+
...(ngDevMode ? [{ debugName: "MenuTrigger" }] : /* istanbul ignore next */ []));
|
|
552
|
+
side = input('bottom', /* @ts-ignore */
|
|
553
|
+
...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
|
|
554
|
+
align = input('start', /* @ts-ignore */
|
|
555
|
+
...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
|
|
556
|
+
disabled = input(false, /* @ts-ignore */
|
|
557
|
+
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
558
|
+
/**
|
|
559
|
+
* Close the menu when an item is chosen, which is the WAI ARIA menu pattern
|
|
560
|
+
* and the default here. Set it to `false` for a menu that stays open, for
|
|
561
|
+
* example a list of checkbox items the user toggles several of in a row.
|
|
562
|
+
*/
|
|
563
|
+
closeOnSelect = input(true, /* @ts-ignore */
|
|
564
|
+
...(ngDevMode ? [{ debugName: "closeOnSelect" }] : /* istanbul ignore next */ []));
|
|
565
|
+
/**
|
|
566
|
+
* Id used for `aria-controls` and for the surface's `aria-labelledby`. A
|
|
567
|
+
* consumer's own `id` on the trigger element always wins.
|
|
568
|
+
*/
|
|
569
|
+
triggerId = this.el.nativeElement.id || injectUniqueId('MenuTrigger');
|
|
570
|
+
surfaceId = injectUniqueId('MenuSurface');
|
|
571
|
+
openedChange = output();
|
|
572
|
+
overlayRef = null;
|
|
573
|
+
isOpen = signal(false, /* @ts-ignore */
|
|
574
|
+
...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
|
|
575
|
+
constructor() {
|
|
576
|
+
this.destroyRef.onDestroy(() => this.close({ restoreFocus: false }));
|
|
577
|
+
}
|
|
578
|
+
toggle() {
|
|
579
|
+
if (this.disabled())
|
|
580
|
+
return;
|
|
581
|
+
if (this.isOpen()) {
|
|
582
|
+
this.close();
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
this.open();
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
openAndFocus(e) {
|
|
589
|
+
if (this.disabled())
|
|
590
|
+
return;
|
|
591
|
+
e.preventDefault();
|
|
592
|
+
this.open();
|
|
593
|
+
}
|
|
594
|
+
openForSide(e, side) {
|
|
595
|
+
if (this.disabled() || this.side() !== side)
|
|
596
|
+
return;
|
|
597
|
+
e.preventDefault();
|
|
598
|
+
this.open();
|
|
599
|
+
}
|
|
600
|
+
open() {
|
|
601
|
+
if (this.isOpen() || this.disabled())
|
|
602
|
+
return;
|
|
603
|
+
const primary = connectedPositionFor(this.side(), this.align(), MENU_SIDE_OFFSET);
|
|
604
|
+
const fallbackSide = this.side() === 'top' || this.side() === 'bottom' ? oppositeSide(this.side()) : this.side();
|
|
605
|
+
const fallback = connectedPositionFor(fallbackSide, this.align(), MENU_SIDE_OFFSET);
|
|
606
|
+
this.overlayRef = this.overlay.create({
|
|
607
|
+
positionStrategy: this.overlay
|
|
608
|
+
.position()
|
|
609
|
+
.flexibleConnectedTo(this.el)
|
|
610
|
+
.withPositions([primary, fallback]),
|
|
611
|
+
scrollStrategy: this.overlay.scrollStrategies.reposition(),
|
|
612
|
+
hasBackdrop: false,
|
|
613
|
+
panelClass: 'menu-panel',
|
|
614
|
+
});
|
|
615
|
+
const controller = {
|
|
616
|
+
surfaceId: this.surfaceId,
|
|
617
|
+
triggerId: this.triggerId,
|
|
618
|
+
isSubmenu: this.parentMenu !== null,
|
|
619
|
+
close: () => this.close(),
|
|
620
|
+
closeAfterSelect: () => {
|
|
621
|
+
if (this.closeOnSelect())
|
|
622
|
+
this.close();
|
|
623
|
+
},
|
|
624
|
+
};
|
|
625
|
+
const portal = new TemplatePortal(this.MenuTrigger().template, this.vcr, undefined, Injector.create({
|
|
626
|
+
parent: this.injector,
|
|
627
|
+
providers: [{ provide: MENU_OVERLAY, useValue: controller }],
|
|
628
|
+
}));
|
|
629
|
+
this.overlayRef.attach(portal);
|
|
630
|
+
merge(this.overlayRef
|
|
631
|
+
.outsidePointerEvents()
|
|
632
|
+
.pipe(filter((e) => !this.el.nativeElement.contains(e.target))), this.overlayRef.detachments())
|
|
633
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
634
|
+
.subscribe(() => this.close({ restoreFocus: false }));
|
|
635
|
+
this.overlayRef
|
|
636
|
+
.keydownEvents()
|
|
637
|
+
.pipe(takeUntilDestroyed(this.destroyRef), filter((e) => e.key === 'Escape'))
|
|
638
|
+
.subscribe(() => this.close());
|
|
639
|
+
// Tab closes the menu and hands focus back to the trigger WITHOUT preventing
|
|
640
|
+
// the default, so the browser then continues the tab sequence from the
|
|
641
|
+
// trigger. That is what the menu pattern asks for: Tab leaves the menu and
|
|
642
|
+
// lands wherever normal tab order says, rather than being swallowed.
|
|
643
|
+
this.overlayRef
|
|
644
|
+
.keydownEvents()
|
|
645
|
+
.pipe(takeUntilDestroyed(this.destroyRef), filter((e) => e.key === 'Tab'))
|
|
646
|
+
.subscribe(() => this.close());
|
|
647
|
+
this.isOpen.set(true);
|
|
648
|
+
this.openedChange.emit(true);
|
|
649
|
+
}
|
|
650
|
+
close(options = {}) {
|
|
651
|
+
const { restoreFocus = true } = options;
|
|
652
|
+
if (!this.isOpen())
|
|
653
|
+
return;
|
|
654
|
+
const overlayRef = this.overlayRef;
|
|
655
|
+
this.overlayRef = null;
|
|
656
|
+
this.isOpen.set(false);
|
|
657
|
+
overlayRef?.dispose();
|
|
658
|
+
if (!this.destroyRef.destroyed) {
|
|
659
|
+
this.openedChange.emit(false);
|
|
660
|
+
}
|
|
661
|
+
if (restoreFocus) {
|
|
662
|
+
this.el.nativeElement.focus();
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
666
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.4", type: MenuTriggerDirective, isStandalone: true, selector: "[MenuTrigger]", inputs: { MenuTrigger: { classPropertyName: "MenuTrigger", publicName: "MenuTrigger", isSignal: true, isRequired: true, transformFunction: null }, side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, closeOnSelect: { classPropertyName: "closeOnSelect", publicName: "closeOnSelect", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { openedChange: "openedChange" }, host: { listeners: { "click": "toggle()", "keydown.enter": "openAndFocus($any($event))", "keydown.space": "openAndFocus($any($event))", "keydown.arrowDown": "openAndFocus($any($event))", "keydown.arrowRight": "openForSide($any($event), \"right\")", "keydown.arrowLeft": "openForSide($any($event), \"left\")" }, properties: { "attr.id": "triggerId", "attr.aria-haspopup": "\"menu\"", "attr.aria-expanded": "isOpen()", "attr.aria-controls": "isOpen() ? surfaceId : null" } }, providers: [{ provide: MENU_SUBMENU_TRIGGER, useValue: true }], exportAs: ["MenuTrigger"], ngImport: i0 });
|
|
667
|
+
}
|
|
668
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MenuTriggerDirective, decorators: [{
|
|
669
|
+
type: Directive,
|
|
670
|
+
args: [{
|
|
671
|
+
selector: '[MenuTrigger]',
|
|
672
|
+
exportAs: 'MenuTrigger',
|
|
673
|
+
providers: [{ provide: MENU_SUBMENU_TRIGGER, useValue: true }],
|
|
674
|
+
host: {
|
|
675
|
+
'[attr.id]': 'triggerId',
|
|
676
|
+
'[attr.aria-haspopup]': '"menu"',
|
|
677
|
+
'[attr.aria-expanded]': 'isOpen()',
|
|
678
|
+
'[attr.aria-controls]': 'isOpen() ? surfaceId : null',
|
|
679
|
+
'(click)': 'toggle()',
|
|
680
|
+
'(keydown.enter)': 'openAndFocus($any($event))',
|
|
681
|
+
'(keydown.space)': 'openAndFocus($any($event))',
|
|
682
|
+
'(keydown.arrowDown)': 'openAndFocus($any($event))',
|
|
683
|
+
'(keydown.arrowRight)': 'openForSide($any($event), "right")',
|
|
684
|
+
'(keydown.arrowLeft)': 'openForSide($any($event), "left")',
|
|
685
|
+
},
|
|
686
|
+
}]
|
|
687
|
+
}], ctorParameters: () => [], propDecorators: { MenuTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "MenuTrigger", required: true }] }], side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], closeOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnSelect", required: false }] }], openedChange: [{ type: i0.Output, args: ["openedChange"] }] } });
|
|
4
688
|
|
|
5
689
|
/**
|
|
6
690
|
* Generated bundle index. Do not edit.
|
|
7
|
-
*/
|
|
691
|
+
*/
|
|
692
|
+
|
|
693
|
+
export { MENU_OVERLAY, MENU_SUBMENU_TRIGGER, MenuCheckboxItemComponent, MenuContentDirective, MenuFocusableItem, MenuGroupComponent, MenuItemComponent, MenuLabelComponent, MenuRadioGroupBase, MenuRadioGroupComponent, MenuRadioItemComponent, MenuSeparatorComponent, MenuShortcutComponent, MenuSurfaceComponent, MenuTriggerDirective };
|