@ng-cn/core 1.0.10 → 1.0.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/package.json +3 -2
- package/src/app/lib/components/ui/accordion/accordion-content.component.ts +53 -0
- package/src/app/lib/components/ui/accordion/accordion-context.ts +33 -0
- package/src/app/lib/components/ui/accordion/accordion-item.component.ts +86 -0
- package/src/app/lib/components/ui/accordion/accordion-trigger.component.ts +73 -0
- package/src/app/lib/components/ui/accordion/accordion.component.ts +197 -0
- package/src/app/lib/components/ui/accordion/index.ts +15 -0
- package/src/app/lib/components/ui/alert/alert-description.component.ts +33 -0
- package/src/app/lib/components/ui/alert/alert-title.component.ts +30 -0
- package/src/app/lib/components/ui/alert/alert-variants.ts +23 -0
- package/src/app/lib/components/ui/alert/alert.component.ts +50 -0
- package/src/app/lib/components/ui/alert/index.ts +5 -0
- package/src/app/lib/components/ui/alert-dialog/alert-dialog-action.component.ts +44 -0
- package/src/app/lib/components/ui/alert-dialog/alert-dialog-cancel.component.ts +45 -0
- package/src/app/lib/components/ui/alert-dialog/alert-dialog-content.component.ts +146 -0
- package/src/app/lib/components/ui/alert-dialog/alert-dialog-context.ts +14 -0
- package/src/app/lib/components/ui/alert-dialog/alert-dialog-description.component.ts +37 -0
- package/src/app/lib/components/ui/alert-dialog/alert-dialog-footer.component.ts +35 -0
- package/src/app/lib/components/ui/alert-dialog/alert-dialog-header.component.ts +35 -0
- package/src/app/lib/components/ui/alert-dialog/alert-dialog-title.component.ts +37 -0
- package/src/app/lib/components/ui/alert-dialog/alert-dialog-trigger.component.ts +44 -0
- package/src/app/lib/components/ui/alert-dialog/alert-dialog.component.ts +91 -0
- package/src/app/lib/components/ui/alert-dialog/index.ts +11 -0
- package/src/app/lib/components/ui/aspect-ratio/aspect-ratio.component.ts +63 -0
- package/src/app/lib/components/ui/aspect-ratio/index.ts +1 -0
- package/src/app/lib/components/ui/avatar/avatar-fallback.component.ts +34 -0
- package/src/app/lib/components/ui/avatar/avatar-image.component.ts +31 -0
- package/src/app/lib/components/ui/avatar/avatar.component.ts +37 -0
- package/src/app/lib/components/ui/avatar/index.ts +5 -0
- package/src/app/lib/components/ui/avatar/ui-avatar.component.ts +52 -0
- package/src/app/lib/components/ui/badge/badge-variants.ts +28 -0
- package/src/app/lib/components/ui/badge/badge.component.ts +50 -0
- package/src/app/lib/components/ui/badge/index.ts +3 -0
- package/src/app/lib/components/ui/breadcrumb/breadcrumb-ellipsis.component.ts +48 -0
- package/src/app/lib/components/ui/breadcrumb/breadcrumb-item.component.ts +28 -0
- package/src/app/lib/components/ui/breadcrumb/breadcrumb-link.component.ts +32 -0
- package/src/app/lib/components/ui/breadcrumb/breadcrumb-list.component.ts +31 -0
- package/src/app/lib/components/ui/breadcrumb/breadcrumb-page.component.ts +31 -0
- package/src/app/lib/components/ui/breadcrumb/breadcrumb-separator.component.ts +47 -0
- package/src/app/lib/components/ui/breadcrumb/breadcrumb.component.ts +43 -0
- package/src/app/lib/components/ui/breadcrumb/index.ts +8 -0
- package/src/app/lib/components/ui/button/button-variants.ts +38 -0
- package/src/app/lib/components/ui/button/button.component.ts +103 -0
- package/src/app/lib/components/ui/button/index.ts +3 -0
- package/src/app/lib/components/ui/button-group/button-group-variants.ts +24 -0
- package/src/app/lib/components/ui/button-group/button-group.component.ts +57 -0
- package/src/app/lib/components/ui/button-group/index.ts +6 -0
- package/src/app/lib/components/ui/calendar/calendar.component.ts +368 -0
- package/src/app/lib/components/ui/calendar/index.ts +1 -0
- package/src/app/lib/components/ui/card/card-action.component.ts +39 -0
- package/src/app/lib/components/ui/card/card-content.component.ts +31 -0
- package/src/app/lib/components/ui/card/card-description.component.ts +31 -0
- package/src/app/lib/components/ui/card/card-footer.component.ts +34 -0
- package/src/app/lib/components/ui/card/card-header.component.ts +37 -0
- package/src/app/lib/components/ui/card/card-title.component.ts +31 -0
- package/src/app/lib/components/ui/card/card.component.ts +41 -0
- package/src/app/lib/components/ui/card/index.ts +8 -0
- package/src/app/lib/components/ui/carousel/carousel-content.component.ts +38 -0
- package/src/app/lib/components/ui/carousel/carousel-context.ts +18 -0
- package/src/app/lib/components/ui/carousel/carousel-item.component.ts +32 -0
- package/src/app/lib/components/ui/carousel/carousel-next.component.ts +54 -0
- package/src/app/lib/components/ui/carousel/carousel-previous.component.ts +54 -0
- package/src/app/lib/components/ui/carousel/carousel.component.ts +125 -0
- package/src/app/lib/components/ui/carousel/index.ts +7 -0
- package/src/app/lib/components/ui/chart/chart-container.component.ts +81 -0
- package/src/app/lib/components/ui/chart/chart-context.ts +38 -0
- package/src/app/lib/components/ui/chart/chart-legend-content.component.ts +51 -0
- package/src/app/lib/components/ui/chart/chart-legend.component.ts +28 -0
- package/src/app/lib/components/ui/chart/chart-tooltip-content.component.ts +37 -0
- package/src/app/lib/components/ui/chart/chart-tooltip.component.ts +28 -0
- package/src/app/lib/components/ui/chart/chart.component.ts +308 -0
- package/src/app/lib/components/ui/chart/index.ts +16 -0
- package/src/app/lib/components/ui/checkbox/checkbox.component.ts +203 -0
- package/src/app/lib/components/ui/checkbox/index.ts +1 -0
- package/src/app/lib/components/ui/collapsible/collapsible-content.component.ts +58 -0
- package/src/app/lib/components/ui/collapsible/collapsible-context.ts +17 -0
- package/src/app/lib/components/ui/collapsible/collapsible-trigger.component.ts +56 -0
- package/src/app/lib/components/ui/collapsible/collapsible.component.ts +102 -0
- package/src/app/lib/components/ui/collapsible/index.ts +5 -0
- package/src/app/lib/components/ui/combobox/combobox-content.component.ts +59 -0
- package/src/app/lib/components/ui/combobox/combobox-context.ts +49 -0
- package/src/app/lib/components/ui/combobox/combobox-empty.component.ts +35 -0
- package/src/app/lib/components/ui/combobox/combobox-group.component.ts +32 -0
- package/src/app/lib/components/ui/combobox/combobox-input.component.ts +89 -0
- package/src/app/lib/components/ui/combobox/combobox-item.component.ts +129 -0
- package/src/app/lib/components/ui/combobox/combobox-list.component.ts +40 -0
- package/src/app/lib/components/ui/combobox/combobox-trigger.component.ts +53 -0
- package/src/app/lib/components/ui/combobox/combobox-value.component.ts +47 -0
- package/src/app/lib/components/ui/combobox/combobox.component.ts +290 -0
- package/src/app/lib/components/ui/combobox/index.ts +15 -0
- package/src/app/lib/components/ui/command/command-context.ts +24 -0
- package/src/app/lib/components/ui/command/command-dialog.component.ts +69 -0
- package/src/app/lib/components/ui/command/command-empty.component.ts +23 -0
- package/src/app/lib/components/ui/command/command-group.component.ts +66 -0
- package/src/app/lib/components/ui/command/command-input.component.ts +137 -0
- package/src/app/lib/components/ui/command/command-item.component.ts +148 -0
- package/src/app/lib/components/ui/command/command-list.component.ts +30 -0
- package/src/app/lib/components/ui/command/command-separator.component.ts +23 -0
- package/src/app/lib/components/ui/command/command-shortcut.component.ts +23 -0
- package/src/app/lib/components/ui/command/command.component.ts +105 -0
- package/src/app/lib/components/ui/command/index.ts +11 -0
- package/src/app/lib/components/ui/context-menu/context-menu-checkbox-item.component.ts +68 -0
- package/src/app/lib/components/ui/context-menu/context-menu-content.component.ts +213 -0
- package/src/app/lib/components/ui/context-menu/context-menu-context.ts +17 -0
- package/src/app/lib/components/ui/context-menu/context-menu-item.component.ts +63 -0
- package/src/app/lib/components/ui/context-menu/context-menu-label.component.ts +30 -0
- package/src/app/lib/components/ui/context-menu/context-menu-radio-group.component.ts +36 -0
- package/src/app/lib/components/ui/context-menu/context-menu-radio-item.component.ts +71 -0
- package/src/app/lib/components/ui/context-menu/context-menu-separator.component.ts +24 -0
- package/src/app/lib/components/ui/context-menu/context-menu-shortcut.component.ts +23 -0
- package/src/app/lib/components/ui/context-menu/context-menu-sub-content.component.ts +51 -0
- package/src/app/lib/components/ui/context-menu/context-menu-sub-trigger.component.ts +50 -0
- package/src/app/lib/components/ui/context-menu/context-menu-sub.component.ts +31 -0
- package/src/app/lib/components/ui/context-menu/context-menu-trigger.component.ts +51 -0
- package/src/app/lib/components/ui/context-menu/context-menu.component.ts +27 -0
- package/src/app/lib/components/ui/context-menu/index.ts +15 -0
- package/src/app/lib/components/ui/data-table/data-table-content.component.ts +226 -0
- package/src/app/lib/components/ui/data-table/data-table-context.ts +49 -0
- package/src/app/lib/components/ui/data-table/data-table-pagination.component.ts +138 -0
- package/src/app/lib/components/ui/data-table/data-table-search.component.ts +52 -0
- package/src/app/lib/components/ui/data-table/data-table-toolbar.component.ts +27 -0
- package/src/app/lib/components/ui/data-table/data-table-view-options.component.ts +92 -0
- package/src/app/lib/components/ui/data-table/data-table.component.ts +131 -0
- package/src/app/lib/components/ui/data-table/index.ts +16 -0
- package/src/app/lib/components/ui/date-picker/date-picker.component.ts +94 -0
- package/src/app/lib/components/ui/date-picker/index.ts +1 -0
- package/src/app/lib/components/ui/dialog/dialog-close.component.ts +31 -0
- package/src/app/lib/components/ui/dialog/dialog-content.component.ts +177 -0
- package/src/app/lib/components/ui/dialog/dialog-context.ts +15 -0
- package/src/app/lib/components/ui/dialog/dialog-description.component.ts +34 -0
- package/src/app/lib/components/ui/dialog/dialog-footer.component.ts +28 -0
- package/src/app/lib/components/ui/dialog/dialog-header.component.ts +28 -0
- package/src/app/lib/components/ui/dialog/dialog-title.component.ts +34 -0
- package/src/app/lib/components/ui/dialog/dialog-trigger.component.ts +38 -0
- package/src/app/lib/components/ui/dialog/dialog.component.ts +87 -0
- package/src/app/lib/components/ui/dialog/index.ts +10 -0
- package/src/app/lib/components/ui/drawer/drawer-close.component.ts +31 -0
- package/src/app/lib/components/ui/drawer/drawer-content.component.ts +143 -0
- package/src/app/lib/components/ui/drawer/drawer-context.ts +17 -0
- package/src/app/lib/components/ui/drawer/drawer-description.component.ts +33 -0
- package/src/app/lib/components/ui/drawer/drawer-footer.component.ts +28 -0
- package/src/app/lib/components/ui/drawer/drawer-header.component.ts +28 -0
- package/src/app/lib/components/ui/drawer/drawer-title.component.ts +33 -0
- package/src/app/lib/components/ui/drawer/drawer-trigger.component.ts +38 -0
- package/src/app/lib/components/ui/drawer/drawer.component.ts +93 -0
- package/src/app/lib/components/ui/drawer/index.ts +10 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.component.ts +68 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-content.component.ts +234 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-context.ts +15 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-group.component.ts +15 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-item.component.ts +56 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-label.component.ts +30 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.component.ts +42 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.component.ts +71 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-separator.component.ts +24 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.component.ts +23 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.component.ts +51 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.component.ts +53 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-sub.component.ts +31 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-trigger.component.ts +45 -0
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu.component.ts +32 -0
- package/src/app/lib/components/ui/dropdown-menu/index.ts +16 -0
- package/src/app/lib/components/ui/empty/empty-action.component.ts +28 -0
- package/src/app/lib/components/ui/empty/empty-description.component.ts +31 -0
- package/src/app/lib/components/ui/empty/empty-icon.component.ts +31 -0
- package/src/app/lib/components/ui/empty/empty-title.component.ts +28 -0
- package/src/app/lib/components/ui/empty/empty.component.ts +53 -0
- package/src/app/lib/components/ui/empty/index.ts +6 -0
- package/src/app/lib/components/ui/form/form-context.ts +34 -0
- package/src/app/lib/components/ui/form/form-control.component.ts +137 -0
- package/src/app/lib/components/ui/form/form-description.component.ts +37 -0
- package/src/app/lib/components/ui/form/form-field.component.ts +84 -0
- package/src/app/lib/components/ui/form/form-item.component.ts +42 -0
- package/src/app/lib/components/ui/form/form-label.component.ts +58 -0
- package/src/app/lib/components/ui/form/form-message.component.ts +107 -0
- package/src/app/lib/components/ui/form/form.component.ts +123 -0
- package/src/app/lib/components/ui/form/index.ts +17 -0
- package/src/app/lib/components/ui/hover-card/hover-card-content.component.ts +203 -0
- package/src/app/lib/components/ui/hover-card/hover-card-context.ts +25 -0
- package/src/app/lib/components/ui/hover-card/hover-card-trigger.component.ts +160 -0
- package/src/app/lib/components/ui/hover-card/hover-card.component.ts +147 -0
- package/src/app/lib/components/ui/hover-card/index.ts +13 -0
- package/src/app/lib/components/ui/index.ts +551 -0
- package/src/app/lib/components/ui/input/index.ts +1 -0
- package/src/app/lib/components/ui/input/input.component.ts +165 -0
- package/src/app/lib/components/ui/input-group/index.ts +4 -0
- package/src/app/lib/components/ui/input-group/input-group-addon.component.ts +43 -0
- package/src/app/lib/components/ui/input-group/input-group-input.component.ts +33 -0
- package/src/app/lib/components/ui/input-group/input-group.component.ts +53 -0
- package/src/app/lib/components/ui/input-otp/index.ts +14 -0
- package/src/app/lib/components/ui/input-otp/input-otp-context.ts +31 -0
- package/src/app/lib/components/ui/input-otp/input-otp-group.component.ts +23 -0
- package/src/app/lib/components/ui/input-otp/input-otp-separator.component.ts +31 -0
- package/src/app/lib/components/ui/input-otp/input-otp-slot.component.ts +67 -0
- package/src/app/lib/components/ui/input-otp/input-otp.component.ts +240 -0
- package/src/app/lib/components/ui/kbd/index.ts +3 -0
- package/src/app/lib/components/ui/kbd/kbd-variants.ts +23 -0
- package/src/app/lib/components/ui/kbd/kbd.component.ts +50 -0
- package/src/app/lib/components/ui/label/index.ts +1 -0
- package/src/app/lib/components/ui/label/label.component.ts +139 -0
- package/src/app/lib/components/ui/menubar/index.ts +26 -0
- package/src/app/lib/components/ui/menubar/menubar-checkbox-item.component.ts +66 -0
- package/src/app/lib/components/ui/menubar/menubar-content.component.ts +236 -0
- package/src/app/lib/components/ui/menubar/menubar-context.ts +63 -0
- package/src/app/lib/components/ui/menubar/menubar-item.component.ts +60 -0
- package/src/app/lib/components/ui/menubar/menubar-label.component.ts +30 -0
- package/src/app/lib/components/ui/menubar/menubar-menu.component.ts +40 -0
- package/src/app/lib/components/ui/menubar/menubar-radio-group.component.ts +36 -0
- package/src/app/lib/components/ui/menubar/menubar-radio-item.component.ts +66 -0
- package/src/app/lib/components/ui/menubar/menubar-separator.component.ts +24 -0
- package/src/app/lib/components/ui/menubar/menubar-shortcut.component.ts +23 -0
- package/src/app/lib/components/ui/menubar/menubar-sub-content.component.ts +51 -0
- package/src/app/lib/components/ui/menubar/menubar-sub-trigger.component.ts +50 -0
- package/src/app/lib/components/ui/menubar/menubar-sub.component.ts +29 -0
- package/src/app/lib/components/ui/menubar/menubar-trigger.component.ts +132 -0
- package/src/app/lib/components/ui/menubar/menubar.component.ts +158 -0
- package/src/app/lib/components/ui/native-select/index.ts +6 -0
- package/src/app/lib/components/ui/native-select/native-select-variants.ts +23 -0
- package/src/app/lib/components/ui/native-select/native-select.component.ts +74 -0
- package/src/app/lib/components/ui/navigation-menu/index.ts +21 -0
- package/src/app/lib/components/ui/navigation-menu/navigation-menu-content.component.ts +66 -0
- package/src/app/lib/components/ui/navigation-menu/navigation-menu-context.ts +55 -0
- package/src/app/lib/components/ui/navigation-menu/navigation-menu-indicator.component.ts +28 -0
- package/src/app/lib/components/ui/navigation-menu/navigation-menu-item.component.ts +29 -0
- package/src/app/lib/components/ui/navigation-menu/navigation-menu-link.component.ts +43 -0
- package/src/app/lib/components/ui/navigation-menu/navigation-menu-list.component.ts +26 -0
- package/src/app/lib/components/ui/navigation-menu/navigation-menu-trigger-style.ts +7 -0
- package/src/app/lib/components/ui/navigation-menu/navigation-menu-trigger.component.ts +58 -0
- package/src/app/lib/components/ui/navigation-menu/navigation-menu-viewport.component.ts +26 -0
- package/src/app/lib/components/ui/navigation-menu/navigation-menu.component.ts +149 -0
- package/src/app/lib/components/ui/pagination/index.ts +8 -0
- package/src/app/lib/components/ui/pagination/pagination-content.component.ts +28 -0
- package/src/app/lib/components/ui/pagination/pagination-ellipsis.component.ts +47 -0
- package/src/app/lib/components/ui/pagination/pagination-item.component.ts +28 -0
- package/src/app/lib/components/ui/pagination/pagination-link.component.ts +46 -0
- package/src/app/lib/components/ui/pagination/pagination-next.component.ts +54 -0
- package/src/app/lib/components/ui/pagination/pagination-previous.component.ts +54 -0
- package/src/app/lib/components/ui/pagination/pagination.component.ts +48 -0
- package/src/app/lib/components/ui/popover/index.ts +14 -0
- package/src/app/lib/components/ui/popover/popover-anchor.component.ts +64 -0
- package/src/app/lib/components/ui/popover/popover-content.component.ts +231 -0
- package/src/app/lib/components/ui/popover/popover-context.ts +29 -0
- package/src/app/lib/components/ui/popover/popover-trigger.component.ts +100 -0
- package/src/app/lib/components/ui/popover/popover.component.ts +163 -0
- package/src/app/lib/components/ui/progress/index.ts +6 -0
- package/src/app/lib/components/ui/progress/progress.component.ts +212 -0
- package/src/app/lib/components/ui/radio-group/index.ts +10 -0
- package/src/app/lib/components/ui/radio-group/radio-group-context.ts +38 -0
- package/src/app/lib/components/ui/radio-group/radio-group-item.component.ts +298 -0
- package/src/app/lib/components/ui/radio-group/radio-group.component.ts +275 -0
- package/src/app/lib/components/ui/resizable/index.ts +5 -0
- package/src/app/lib/components/ui/resizable/resizable-context.ts +14 -0
- package/src/app/lib/components/ui/resizable/resizable-handle.component.ts +232 -0
- package/src/app/lib/components/ui/resizable/resizable-panel-group.component.ts +140 -0
- package/src/app/lib/components/ui/resizable/resizable-panel.component.ts +77 -0
- package/src/app/lib/components/ui/scroll-area/index.ts +8 -0
- package/src/app/lib/components/ui/scroll-area/scroll-area.component.ts +126 -0
- package/src/app/lib/components/ui/scroll-area/scroll-bar.component.ts +93 -0
- package/src/app/lib/components/ui/segmented/index.ts +13 -0
- package/src/app/lib/components/ui/segmented/segmented-context.ts +11 -0
- package/src/app/lib/components/ui/segmented/segmented-item.component.ts +72 -0
- package/src/app/lib/components/ui/segmented/segmented-variants.ts +40 -0
- package/src/app/lib/components/ui/segmented/segmented.component.ts +99 -0
- package/src/app/lib/components/ui/select/index.ts +19 -0
- package/src/app/lib/components/ui/select/select-content.component.ts +97 -0
- package/src/app/lib/components/ui/select/select-context.ts +53 -0
- package/src/app/lib/components/ui/select/select-group.component.ts +56 -0
- package/src/app/lib/components/ui/select/select-item.component.ts +163 -0
- package/src/app/lib/components/ui/select/select-label.component.ts +32 -0
- package/src/app/lib/components/ui/select/select-separator.component.ts +34 -0
- package/src/app/lib/components/ui/select/select-trigger.component.ts +164 -0
- package/src/app/lib/components/ui/select/select-value.component.ts +49 -0
- package/src/app/lib/components/ui/select/select.component.ts +263 -0
- package/src/app/lib/components/ui/separator/index.ts +6 -0
- package/src/app/lib/components/ui/separator/separator.component.ts +128 -0
- package/src/app/lib/components/ui/sheet/index.ts +11 -0
- package/src/app/lib/components/ui/sheet/sheet-close.component.ts +32 -0
- package/src/app/lib/components/ui/sheet/sheet-content.component.ts +157 -0
- package/src/app/lib/components/ui/sheet/sheet-context.ts +15 -0
- package/src/app/lib/components/ui/sheet/sheet-description.component.ts +34 -0
- package/src/app/lib/components/ui/sheet/sheet-footer.component.ts +28 -0
- package/src/app/lib/components/ui/sheet/sheet-header.component.ts +28 -0
- package/src/app/lib/components/ui/sheet/sheet-title.component.ts +34 -0
- package/src/app/lib/components/ui/sheet/sheet-trigger.component.ts +38 -0
- package/src/app/lib/components/ui/sheet/sheet-variants.ts +22 -0
- package/src/app/lib/components/ui/sheet/sheet.component.ts +97 -0
- package/src/app/lib/components/ui/sidebar/index.ts +41 -0
- package/src/app/lib/components/ui/sidebar/sidebar-content.component.ts +31 -0
- package/src/app/lib/components/ui/sidebar/sidebar-context.ts +33 -0
- package/src/app/lib/components/ui/sidebar/sidebar-footer.component.ts +28 -0
- package/src/app/lib/components/ui/sidebar/sidebar-group-action.component.ts +33 -0
- package/src/app/lib/components/ui/sidebar/sidebar-group-content.component.ts +28 -0
- package/src/app/lib/components/ui/sidebar/sidebar-group-label.component.ts +32 -0
- package/src/app/lib/components/ui/sidebar/sidebar-group.component.ts +28 -0
- package/src/app/lib/components/ui/sidebar/sidebar-header.component.ts +28 -0
- package/src/app/lib/components/ui/sidebar/sidebar-input.component.ts +31 -0
- package/src/app/lib/components/ui/sidebar/sidebar-inset.component.ts +31 -0
- package/src/app/lib/components/ui/sidebar/sidebar-menu-action.component.ts +56 -0
- package/src/app/lib/components/ui/sidebar/sidebar-menu-badge.component.ts +42 -0
- package/src/app/lib/components/ui/sidebar/sidebar-menu-button.component.ts +64 -0
- package/src/app/lib/components/ui/sidebar/sidebar-menu-item.component.ts +32 -0
- package/src/app/lib/components/ui/sidebar/sidebar-menu-skeleton.component.ts +39 -0
- package/src/app/lib/components/ui/sidebar/sidebar-menu-sub-button.component.ts +59 -0
- package/src/app/lib/components/ui/sidebar/sidebar-menu-sub-item.component.ts +25 -0
- package/src/app/lib/components/ui/sidebar/sidebar-menu-sub.component.ts +32 -0
- package/src/app/lib/components/ui/sidebar/sidebar-menu.component.ts +31 -0
- package/src/app/lib/components/ui/sidebar/sidebar-provider.component.ts +141 -0
- package/src/app/lib/components/ui/sidebar/sidebar-rail.component.ts +47 -0
- package/src/app/lib/components/ui/sidebar/sidebar-route-active.service.ts +124 -0
- package/src/app/lib/components/ui/sidebar/sidebar-separator.component.ts +28 -0
- package/src/app/lib/components/ui/sidebar/sidebar-trigger.component.ts +57 -0
- package/src/app/lib/components/ui/sidebar/sidebar.component.ts +130 -0
- package/src/app/lib/components/ui/skeleton/index.ts +1 -0
- package/src/app/lib/components/ui/skeleton/skeleton.component.ts +52 -0
- package/src/app/lib/components/ui/slider/index.ts +6 -0
- package/src/app/lib/components/ui/slider/slider.component.ts +477 -0
- package/src/app/lib/components/ui/spinner/index.ts +3 -0
- package/src/app/lib/components/ui/spinner/spinner-variants.ts +32 -0
- package/src/app/lib/components/ui/spinner/spinner.component.ts +77 -0
- package/src/app/lib/components/ui/switch/index.ts +6 -0
- package/src/app/lib/components/ui/switch/switch.component.ts +282 -0
- package/src/app/lib/components/ui/table/index.ts +9 -0
- package/src/app/lib/components/ui/table/table-body.component.ts +28 -0
- package/src/app/lib/components/ui/table/table-caption.component.ts +28 -0
- package/src/app/lib/components/ui/table/table-cell.component.ts +31 -0
- package/src/app/lib/components/ui/table/table-footer.component.ts +28 -0
- package/src/app/lib/components/ui/table/table-head.component.ts +36 -0
- package/src/app/lib/components/ui/table/table-header.component.ts +28 -0
- package/src/app/lib/components/ui/table/table-row.component.ts +34 -0
- package/src/app/lib/components/ui/table/table.component.ts +52 -0
- package/src/app/lib/components/ui/tabs/index.ts +14 -0
- package/src/app/lib/components/ui/tabs/tabs-content.component.ts +132 -0
- package/src/app/lib/components/ui/tabs/tabs-context.ts +33 -0
- package/src/app/lib/components/ui/tabs/tabs-list.component.ts +228 -0
- package/src/app/lib/components/ui/tabs/tabs-trigger.component.ts +167 -0
- package/src/app/lib/components/ui/tabs/tabs.component.ts +203 -0
- package/src/app/lib/components/ui/textarea/index.ts +1 -0
- package/src/app/lib/components/ui/textarea/textarea.component.ts +44 -0
- package/src/app/lib/components/ui/toast/index.ts +16 -0
- package/src/app/lib/components/ui/toast/toast-action.component.ts +77 -0
- package/src/app/lib/components/ui/toast/toast-description.component.ts +52 -0
- package/src/app/lib/components/ui/toast/toast-title.component.ts +52 -0
- package/src/app/lib/components/ui/toast/toast-variants.ts +24 -0
- package/src/app/lib/components/ui/toast/toast.component.ts +177 -0
- package/src/app/lib/components/ui/toast/toast.service.ts +202 -0
- package/src/app/lib/components/ui/toast/toaster.component.ts +128 -0
- package/src/app/lib/components/ui/toggle/index.ts +6 -0
- package/src/app/lib/components/ui/toggle/toggle-variants.ts +30 -0
- package/src/app/lib/components/ui/toggle/toggle.component.ts +199 -0
- package/src/app/lib/components/ui/toggle-group/index.ts +11 -0
- package/src/app/lib/components/ui/toggle-group/toggle-group-context.ts +48 -0
- package/src/app/lib/components/ui/toggle-group/toggle-group-item.component.ts +241 -0
- package/src/app/lib/components/ui/toggle-group/toggle-group.component.ts +288 -0
- package/src/app/lib/components/ui/tooltip/index.ts +14 -0
- package/src/app/lib/components/ui/tooltip/tooltip-content.component.ts +154 -0
- package/src/app/lib/components/ui/tooltip/tooltip-context.ts +29 -0
- package/src/app/lib/components/ui/tooltip/tooltip-provider.component.ts +95 -0
- package/src/app/lib/components/ui/tooltip/tooltip-trigger.component.ts +138 -0
- package/src/app/lib/components/ui/tooltip/tooltip.component.ts +159 -0
- package/src/app/lib/components/ui/typography/index.ts +13 -0
- package/src/app/lib/components/ui/typography/typography-blockquote.component.ts +31 -0
- package/src/app/lib/components/ui/typography/typography-h1.component.ts +32 -0
- package/src/app/lib/components/ui/typography/typography-h2.component.ts +32 -0
- package/src/app/lib/components/ui/typography/typography-h3.component.ts +29 -0
- package/src/app/lib/components/ui/typography/typography-h4.component.ts +29 -0
- package/src/app/lib/components/ui/typography/typography-inline-code.component.ts +31 -0
- package/src/app/lib/components/ui/typography/typography-large.component.ts +28 -0
- package/src/app/lib/components/ui/typography/typography-lead.component.ts +31 -0
- package/src/app/lib/components/ui/typography/typography-list.component.ts +31 -0
- package/src/app/lib/components/ui/typography/typography-muted.component.ts +28 -0
- package/src/app/lib/components/ui/typography/typography-p.component.ts +29 -0
- package/src/app/lib/components/ui/typography/typography-small.component.ts +28 -0
- package/src/app/lib/index.ts +7 -0
- package/src/app/lib/utils/accessibility/aria-id.service.ts +118 -0
- package/src/app/lib/utils/accessibility/click-outside.directive.ts +85 -0
- package/src/app/lib/utils/accessibility/focus-management.service.ts +231 -0
- package/src/app/lib/utils/accessibility/focus-trap.directive.ts +203 -0
- package/src/app/lib/utils/accessibility/index.ts +23 -0
- package/src/app/lib/utils/accessibility/keyboard-navigation.directive.ts +440 -0
- package/src/app/lib/utils/accessibility/live-region.directive.ts +260 -0
- package/src/app/lib/utils/accessibility/touch-target.directive.ts +81 -0
- package/src/app/lib/utils/accessibility/visually-hidden.component.ts +79 -0
- package/src/app/lib/utils/animation/animated.directive.ts +191 -0
- package/src/app/lib/utils/animation/animation-tokens.service.ts +88 -0
- package/src/app/lib/utils/animation/animation.types.ts +55 -0
- package/src/app/lib/utils/animation/animation.utils.ts +158 -0
- package/src/app/lib/utils/animation/index.ts +17 -0
- package/src/app/lib/utils/animation/presence.component.ts +168 -0
- package/src/app/lib/utils/animation/presence.directive.ts +169 -0
- package/src/app/lib/utils/cn.ts +15 -0
- package/src/app/lib/utils/index.ts +11 -0
- package/src/app/lib/utils/positioning/index.ts +218 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { cn } from '@/lib/utils';
|
|
2
|
+
import {
|
|
3
|
+
ChangeDetectionStrategy,
|
|
4
|
+
Component,
|
|
5
|
+
computed,
|
|
6
|
+
inject,
|
|
7
|
+
input,
|
|
8
|
+
} from '@angular/core';
|
|
9
|
+
import { FORM_FIELD_CONTEXT } from './form-context';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* FormMessage component - displays validation error messages.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* <FormMessage />
|
|
16
|
+
*
|
|
17
|
+
* <!-- Or with custom message -->
|
|
18
|
+
* <FormMessage>Custom error message</FormMessage>
|
|
19
|
+
*/
|
|
20
|
+
@Component({
|
|
21
|
+
selector: 'FormMessage',
|
|
22
|
+
template: `
|
|
23
|
+
@if (errorMessage() || hasContent) {
|
|
24
|
+
<ng-content />
|
|
25
|
+
@if (!hasContent && errorMessage()) {
|
|
26
|
+
{{ errorMessage() }}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
`,
|
|
30
|
+
host: {
|
|
31
|
+
'[class]': 'computedClass()',
|
|
32
|
+
'[attr.id]': 'fieldContext?.formMessageId()',
|
|
33
|
+
'[attr.role]': 'hasError() ? "alert" : null',
|
|
34
|
+
'data-slot': 'form-message',
|
|
35
|
+
},
|
|
36
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
37
|
+
})
|
|
38
|
+
export class FormMessage {
|
|
39
|
+
protected readonly fieldContext = inject(FORM_FIELD_CONTEXT, { optional: true });
|
|
40
|
+
|
|
41
|
+
/** Additional CSS classes to apply */
|
|
42
|
+
readonly class = input<string>('');
|
|
43
|
+
|
|
44
|
+
/** Whether component has projected content */
|
|
45
|
+
hasContent = false;
|
|
46
|
+
|
|
47
|
+
/** Check if field has error */
|
|
48
|
+
protected readonly hasError = computed(() => {
|
|
49
|
+
const control = this.fieldContext?.control();
|
|
50
|
+
return control?.invalid && (control?.dirty || control?.touched);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
/** Get first error message */
|
|
54
|
+
protected readonly errorMessage = computed(() => {
|
|
55
|
+
const control = this.fieldContext?.control();
|
|
56
|
+
if (!control?.errors || !this.hasError()) return null;
|
|
57
|
+
|
|
58
|
+
const errors = control.errors;
|
|
59
|
+
const errorKeys = Object.keys(errors);
|
|
60
|
+
if (errorKeys.length === 0) return null;
|
|
61
|
+
|
|
62
|
+
const firstErrorKey = errorKeys[0];
|
|
63
|
+
const errorValue = errors[firstErrorKey];
|
|
64
|
+
|
|
65
|
+
// Handle common error types
|
|
66
|
+
switch (firstErrorKey) {
|
|
67
|
+
case 'required':
|
|
68
|
+
return 'This field is required';
|
|
69
|
+
case 'minlength':
|
|
70
|
+
return `Minimum length is ${errorValue.requiredLength} characters`;
|
|
71
|
+
case 'maxlength':
|
|
72
|
+
return `Maximum length is ${errorValue.requiredLength} characters`;
|
|
73
|
+
case 'min':
|
|
74
|
+
return `Minimum value is ${errorValue.min}`;
|
|
75
|
+
case 'max':
|
|
76
|
+
return `Maximum value is ${errorValue.max}`;
|
|
77
|
+
case 'email':
|
|
78
|
+
return 'Please enter a valid email address';
|
|
79
|
+
case 'pattern':
|
|
80
|
+
return 'Invalid format';
|
|
81
|
+
default:
|
|
82
|
+
// If error is a string, return it directly
|
|
83
|
+
if (typeof errorValue === 'string') {
|
|
84
|
+
return errorValue;
|
|
85
|
+
}
|
|
86
|
+
// If error has a message property
|
|
87
|
+
if (errorValue?.message) {
|
|
88
|
+
return errorValue.message;
|
|
89
|
+
}
|
|
90
|
+
return 'Invalid value';
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
/** Computed class combining base styles and custom classes */
|
|
95
|
+
protected readonly computedClass = computed(() =>
|
|
96
|
+
cn(
|
|
97
|
+
'text-destructive text-[0.8rem] font-medium',
|
|
98
|
+
!this.hasError() && !this.hasContent && 'hidden',
|
|
99
|
+
this.class()
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
ngAfterContentInit() {
|
|
104
|
+
// Check if there's projected content
|
|
105
|
+
// This is a simplified check - in production you might want to use ContentChild
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { cn } from '@/lib/utils';
|
|
2
|
+
import {
|
|
3
|
+
ChangeDetectionStrategy,
|
|
4
|
+
Component,
|
|
5
|
+
computed,
|
|
6
|
+
forwardRef,
|
|
7
|
+
input,
|
|
8
|
+
signal,
|
|
9
|
+
} from '@angular/core';
|
|
10
|
+
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
|
|
11
|
+
import { FORM_CONTEXT, type FormContext } from './form-context';
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Types
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Props for the Form component
|
|
19
|
+
*/
|
|
20
|
+
export interface FormProps {
|
|
21
|
+
/** The reactive form group to bind to */
|
|
22
|
+
formGroup?: FormGroup | null;
|
|
23
|
+
/** Additional CSS classes */
|
|
24
|
+
class?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Component
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @component Form
|
|
33
|
+
*
|
|
34
|
+
* Building forms with Angular Reactive Forms.
|
|
35
|
+
*
|
|
36
|
+
* @description
|
|
37
|
+
* Form provides a wrapper for Angular Reactive Forms that integrates with
|
|
38
|
+
* the form component system for consistent styling and accessibility.
|
|
39
|
+
*
|
|
40
|
+
* ## Features
|
|
41
|
+
* - Integrates with Angular Reactive Forms
|
|
42
|
+
* - Provides context for FormField, FormItem, FormLabel, etc.
|
|
43
|
+
* - Automatic error message display
|
|
44
|
+
* - Accessible form structure with proper ARIA relationships
|
|
45
|
+
* - Consistent validation styling
|
|
46
|
+
*
|
|
47
|
+
* ## Form Components
|
|
48
|
+
* - `Form` - Wrapper providing form context
|
|
49
|
+
* - `FormField` - Wraps a form control with context
|
|
50
|
+
* - `FormItem` - Container for label, control, description, message
|
|
51
|
+
* - `FormLabel` - Label with accessibility bindings
|
|
52
|
+
* - `FormControl` - Control with ARIA attributes
|
|
53
|
+
* - `FormDescription` - Helper text for the field
|
|
54
|
+
* - `FormMessage` - Error/validation messages
|
|
55
|
+
*
|
|
56
|
+
* ## Accessibility
|
|
57
|
+
* - `aria-describedby` links controls to description and errors
|
|
58
|
+
* - `aria-invalid` when field has errors
|
|
59
|
+
* - Labels properly associated with controls
|
|
60
|
+
*
|
|
61
|
+
* @example Basic usage
|
|
62
|
+
* ```html
|
|
63
|
+
* <Form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
|
|
64
|
+
* <FormField name="email">
|
|
65
|
+
* <FormItem>
|
|
66
|
+
* <FormLabel>Email</FormLabel>
|
|
67
|
+
* <FormControl>
|
|
68
|
+
* <Input type="email" formControlName="email" />
|
|
69
|
+
* </FormControl>
|
|
70
|
+
* <FormDescription>Enter your email address</FormDescription>
|
|
71
|
+
* <FormMessage />
|
|
72
|
+
* </FormItem>
|
|
73
|
+
* </FormField>
|
|
74
|
+
* <Button type="submit">Submit</Button>
|
|
75
|
+
* </Form>
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @example With validation
|
|
79
|
+
* ```typescript
|
|
80
|
+
* loginForm = this.fb.group({
|
|
81
|
+
* email: ['', [Validators.required, Validators.email]],
|
|
82
|
+
* password: ['', [Validators.required, Validators.minLength(8)]],
|
|
83
|
+
* });
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
@Component({
|
|
87
|
+
selector: 'Form',
|
|
88
|
+
imports: [ReactiveFormsModule],
|
|
89
|
+
template: `<ng-content />`,
|
|
90
|
+
host: {
|
|
91
|
+
'[class]': 'computedClass()',
|
|
92
|
+
'data-slot': 'form',
|
|
93
|
+
},
|
|
94
|
+
providers: [
|
|
95
|
+
{
|
|
96
|
+
provide: FORM_CONTEXT,
|
|
97
|
+
useFactory: (component: Form) => component.context,
|
|
98
|
+
deps: [forwardRef(() => Form)],
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
102
|
+
})
|
|
103
|
+
export class Form {
|
|
104
|
+
/** The reactive form group */
|
|
105
|
+
readonly formGroup = input<FormGroup | null>(null);
|
|
106
|
+
|
|
107
|
+
/** Additional CSS classes */
|
|
108
|
+
readonly class = input<string>('');
|
|
109
|
+
|
|
110
|
+
/** Context for child components */
|
|
111
|
+
readonly context: FormContext = {
|
|
112
|
+
form: signal(this.formGroup()),
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/** Computed class combining base styles and custom classes */
|
|
116
|
+
protected readonly computedClass = computed(() =>
|
|
117
|
+
cn('space-y-6', this.class())
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
ngOnChanges() {
|
|
121
|
+
this.context.form.set(this.formGroup());
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Context and types
|
|
2
|
+
export {
|
|
3
|
+
FORM_CONTEXT,
|
|
4
|
+
FORM_FIELD_CONTEXT,
|
|
5
|
+
type FormContext,
|
|
6
|
+
type FormFieldContext
|
|
7
|
+
} from './form-context';
|
|
8
|
+
|
|
9
|
+
// Components and their types
|
|
10
|
+
export { FormControl, FormControlInput } from './form-control.component';
|
|
11
|
+
export { FormDescription } from './form-description.component';
|
|
12
|
+
export { FormField } from './form-field.component';
|
|
13
|
+
export { FormItem } from './form-item.component';
|
|
14
|
+
export { FormLabel } from './form-label.component';
|
|
15
|
+
export { FormMessage } from './form-message.component';
|
|
16
|
+
export { Form, type FormProps } from './form.component';
|
|
17
|
+
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { cn, Presence } from '@/lib/utils';
|
|
2
|
+
import {
|
|
3
|
+
ChangeDetectionStrategy,
|
|
4
|
+
Component,
|
|
5
|
+
computed,
|
|
6
|
+
ElementRef,
|
|
7
|
+
inject,
|
|
8
|
+
input,
|
|
9
|
+
OnDestroy,
|
|
10
|
+
} from '@angular/core';
|
|
11
|
+
import { HOVER_CARD_CONTEXT, HoverCardAlign, HoverCardSide } from './hover-card-context';
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Types
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
export type HoverCardContentState = 'open' | 'closed';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Props for the HoverCardContent component
|
|
21
|
+
*/
|
|
22
|
+
export interface HoverCardContentProps {
|
|
23
|
+
/** The preferred side of the trigger to render against.
|
|
24
|
+
* @default 'bottom' */
|
|
25
|
+
side?: HoverCardSide;
|
|
26
|
+
/** The distance in pixels from the trigger.
|
|
27
|
+
* @default 4 */
|
|
28
|
+
sideOffset?: number;
|
|
29
|
+
/** The preferred alignment against the trigger.
|
|
30
|
+
* @default 'center' */
|
|
31
|
+
align?: HoverCardAlign;
|
|
32
|
+
/** Additional CSS classes */
|
|
33
|
+
class?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Component
|
|
38
|
+
// ============================================================================
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @component HoverCardContent
|
|
42
|
+
*
|
|
43
|
+
* The component that pops out when the hover card is open.
|
|
44
|
+
*
|
|
45
|
+
* @description
|
|
46
|
+
* HoverCardContent displays the preview content. It stays open when
|
|
47
|
+
* hovered, allowing users to interact with the content.
|
|
48
|
+
*
|
|
49
|
+
* ## Features
|
|
50
|
+
* - Stays open when content is hovered
|
|
51
|
+
* - Configurable side and alignment
|
|
52
|
+
* - Smooth animations
|
|
53
|
+
* - Escape key to dismiss
|
|
54
|
+
*
|
|
55
|
+
* ## Accessibility
|
|
56
|
+
* - `role="dialog"` on the content
|
|
57
|
+
* - Focusable content items
|
|
58
|
+
* - Escape returns focus to trigger
|
|
59
|
+
*
|
|
60
|
+
* @example Basic usage
|
|
61
|
+
* ```html
|
|
62
|
+
* <HoverCardContent>
|
|
63
|
+
* <p>Preview content</p>
|
|
64
|
+
* </HoverCardContent>
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* @example With positioning
|
|
68
|
+
* ```html
|
|
69
|
+
* <HoverCardContent side="right" align="start">
|
|
70
|
+
* <p>Right-aligned content</p>
|
|
71
|
+
* </HoverCardContent>
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @data-attributes
|
|
75
|
+
* - `data-state` - 'open' | 'closed'
|
|
76
|
+
* - `data-side` - 'top' | 'right' | 'bottom' | 'left'
|
|
77
|
+
* - `data-align` - 'start' | 'center' | 'end'
|
|
78
|
+
*/
|
|
79
|
+
@Component({
|
|
80
|
+
selector: 'HoverCardContent',
|
|
81
|
+
imports: [Presence],
|
|
82
|
+
template: `
|
|
83
|
+
<Presence [present]="context.open()">
|
|
84
|
+
<div
|
|
85
|
+
role="dialog"
|
|
86
|
+
[attr.aria-modal]="false"
|
|
87
|
+
tabindex="-1"
|
|
88
|
+
[class]="computedClass()"
|
|
89
|
+
[attr.data-state]="state()"
|
|
90
|
+
[attr.data-side]="side()"
|
|
91
|
+
[attr.data-align]="align()"
|
|
92
|
+
data-slot="hover-card-content"
|
|
93
|
+
(mouseenter)="onMouseEnter()"
|
|
94
|
+
(mouseleave)="onMouseLeave()"
|
|
95
|
+
(focusin)="onFocusIn()"
|
|
96
|
+
(focusout)="onFocusOut($event)"
|
|
97
|
+
(keydown.escape)="onEscape()"
|
|
98
|
+
>
|
|
99
|
+
<ng-content />
|
|
100
|
+
</div>
|
|
101
|
+
</Presence>
|
|
102
|
+
`,
|
|
103
|
+
host: {
|
|
104
|
+
class: 'contents',
|
|
105
|
+
},
|
|
106
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
107
|
+
})
|
|
108
|
+
export class HoverCardContent implements OnDestroy {
|
|
109
|
+
protected readonly context = inject(HOVER_CARD_CONTEXT);
|
|
110
|
+
private readonly elementRef = inject(ElementRef<HTMLElement>);
|
|
111
|
+
|
|
112
|
+
/** The preferred side of the trigger to render against */
|
|
113
|
+
readonly side = input<HoverCardSide>('bottom');
|
|
114
|
+
|
|
115
|
+
/** The distance in pixels from the trigger */
|
|
116
|
+
readonly sideOffset = input<number>(4);
|
|
117
|
+
|
|
118
|
+
/** The preferred alignment against the trigger */
|
|
119
|
+
readonly align = input<HoverCardAlign>('center');
|
|
120
|
+
|
|
121
|
+
/** Additional CSS classes */
|
|
122
|
+
readonly class = input<string>('');
|
|
123
|
+
|
|
124
|
+
private closeTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
125
|
+
|
|
126
|
+
/** Current state: open or closed */
|
|
127
|
+
protected readonly state = computed<HoverCardContentState>(() =>
|
|
128
|
+
this.context.open() ? 'open' : 'closed'
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
protected readonly computedClass = computed(() => {
|
|
132
|
+
const sideClasses = {
|
|
133
|
+
top: 'bottom-full mb-2',
|
|
134
|
+
bottom: 'top-full mt-2',
|
|
135
|
+
left: 'right-full mr-2',
|
|
136
|
+
right: 'left-full ml-2',
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const alignClasses = {
|
|
140
|
+
start: 'left-0',
|
|
141
|
+
center: 'left-1/2 -translate-x-1/2',
|
|
142
|
+
end: 'right-0',
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
return cn(
|
|
146
|
+
'absolute z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none',
|
|
147
|
+
'data-[state=open]:animate-in data-[state=closed]:animate-out',
|
|
148
|
+
'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
|
149
|
+
'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
|
|
150
|
+
'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',
|
|
151
|
+
'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
152
|
+
sideClasses[this.side()],
|
|
153
|
+
this.side() === 'top' || this.side() === 'bottom' ? alignClasses[this.align()] : '',
|
|
154
|
+
this.class()
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
ngOnDestroy(): void {
|
|
159
|
+
this.clearTimeout();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
onMouseEnter(): void {
|
|
163
|
+
this.clearTimeout();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
onMouseLeave(): void {
|
|
167
|
+
this.closeTimeout = setTimeout(() => {
|
|
168
|
+
this.context.setOpen(false);
|
|
169
|
+
}, this.context.closeDelay);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
onFocusIn(): void {
|
|
173
|
+
this.clearTimeout();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
onFocusOut(event: FocusEvent): void {
|
|
177
|
+
const relatedTarget = event.relatedTarget as HTMLElement | null;
|
|
178
|
+
const trigger = this.elementRef.nativeElement.parentElement?.querySelector('[data-state]');
|
|
179
|
+
|
|
180
|
+
// Check if focus moved to trigger or stayed within content
|
|
181
|
+
if (relatedTarget && (trigger === relatedTarget || trigger?.contains(relatedTarget))) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
this.closeTimeout = setTimeout(() => {
|
|
186
|
+
this.context.setOpen(false);
|
|
187
|
+
}, this.context.closeDelay);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
onEscape(): void {
|
|
191
|
+
this.context.setOpen(false);
|
|
192
|
+
// Return focus to trigger
|
|
193
|
+
const trigger = this.elementRef.nativeElement.parentElement?.querySelector('[data-state]') as HTMLElement;
|
|
194
|
+
trigger?.focus();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private clearTimeout(): void {
|
|
198
|
+
if (this.closeTimeout) {
|
|
199
|
+
clearTimeout(this.closeTimeout);
|
|
200
|
+
this.closeTimeout = null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { InjectionToken, WritableSignal } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Types
|
|
5
|
+
// ============================================================================
|
|
6
|
+
|
|
7
|
+
export type HoverCardSide = 'top' | 'right' | 'bottom' | 'left';
|
|
8
|
+
export type HoverCardAlign = 'start' | 'center' | 'end';
|
|
9
|
+
|
|
10
|
+
export interface HoverCardContextValue {
|
|
11
|
+
/** Signal for open state */
|
|
12
|
+
open: WritableSignal<boolean>;
|
|
13
|
+
/** Set open state */
|
|
14
|
+
setOpen: (open: boolean) => void;
|
|
15
|
+
/** The duration from when the pointer enters the trigger until the hover card opens (ms) */
|
|
16
|
+
openDelay: number;
|
|
17
|
+
/** The duration from when the pointer leaves the trigger/content until the hover card closes (ms) */
|
|
18
|
+
closeDelay: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Injection Tokens
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
export const HOVER_CARD_CONTEXT = new InjectionToken<HoverCardContextValue>('HOVER_CARD_CONTEXT');
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChangeDetectionStrategy,
|
|
3
|
+
Component,
|
|
4
|
+
ElementRef,
|
|
5
|
+
inject,
|
|
6
|
+
input,
|
|
7
|
+
OnDestroy,
|
|
8
|
+
} from '@angular/core';
|
|
9
|
+
import { HOVER_CARD_CONTEXT } from './hover-card-context';
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Types
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Props for the HoverCardTrigger component
|
|
17
|
+
*/
|
|
18
|
+
export interface HoverCardTriggerProps {
|
|
19
|
+
/** Change the default rendered element for the one passed as a child.
|
|
20
|
+
* @default false */
|
|
21
|
+
asChild?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Component
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @component HoverCardTrigger
|
|
30
|
+
*
|
|
31
|
+
* The link that opens the hover card when hovered.
|
|
32
|
+
*
|
|
33
|
+
* @description
|
|
34
|
+
* HoverCardTrigger wraps the element that activates the hover card on hover
|
|
35
|
+
* or focus. It manages timing for open/close delays.
|
|
36
|
+
*
|
|
37
|
+
* ## Features
|
|
38
|
+
* - Opens on hover with delay
|
|
39
|
+
* - Opens immediately on focus (keyboard)
|
|
40
|
+
* - Toggle on Enter/Space (keyboard)
|
|
41
|
+
* - Escape dismisses and returns focus
|
|
42
|
+
*
|
|
43
|
+
* ## Accessibility
|
|
44
|
+
* - `aria-expanded` reflects open state
|
|
45
|
+
* - `aria-haspopup="dialog"` indicates content type
|
|
46
|
+
* - Focus triggers immediate open for keyboard users
|
|
47
|
+
* - Escape returns focus to trigger
|
|
48
|
+
*
|
|
49
|
+
* ## Keyboard Navigation
|
|
50
|
+
* - `Tab` - Focus triggers hover card
|
|
51
|
+
* - `Enter` / `Space` - Toggle hover card
|
|
52
|
+
* - `Escape` - Close and return focus
|
|
53
|
+
*
|
|
54
|
+
* @example Basic usage
|
|
55
|
+
* ```html
|
|
56
|
+
* <HoverCardTrigger>
|
|
57
|
+
* <a href="#">@username</a>
|
|
58
|
+
* </HoverCardTrigger>
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @example With custom element
|
|
62
|
+
* ```html
|
|
63
|
+
* <HoverCardTrigger>
|
|
64
|
+
* <span class="cursor-pointer underline">Hover for info</span>
|
|
65
|
+
* </HoverCardTrigger>
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* @data-attributes
|
|
69
|
+
* - `data-state` - 'open' | 'closed'
|
|
70
|
+
*/
|
|
71
|
+
@Component({
|
|
72
|
+
selector: 'HoverCardTrigger',
|
|
73
|
+
template: `<ng-content />`,
|
|
74
|
+
host: {
|
|
75
|
+
'tabindex': '0',
|
|
76
|
+
'role': 'button',
|
|
77
|
+
'(mouseenter)': 'onMouseEnter()',
|
|
78
|
+
'(mouseleave)': 'onMouseLeave()',
|
|
79
|
+
'(focus)': 'onFocus()',
|
|
80
|
+
'(blur)': 'onBlur($event)',
|
|
81
|
+
'(keydown.enter)': 'onKeyDown($event)',
|
|
82
|
+
'(keydown.space)': 'onKeyDown($event)',
|
|
83
|
+
'(keydown.escape)': 'onEscape()',
|
|
84
|
+
'[attr.data-state]': 'context.open() ? "open" : "closed"',
|
|
85
|
+
'[attr.aria-expanded]': 'context.open()',
|
|
86
|
+
'[attr.aria-haspopup]': '"dialog"',
|
|
87
|
+
},
|
|
88
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
89
|
+
})
|
|
90
|
+
export class HoverCardTrigger implements OnDestroy {
|
|
91
|
+
protected readonly context = inject(HOVER_CARD_CONTEXT);
|
|
92
|
+
private readonly elementRef = inject(ElementRef<HTMLElement>);
|
|
93
|
+
|
|
94
|
+
/** Change the default rendered element for the one passed as a child */
|
|
95
|
+
readonly asChild = input<boolean>(false);
|
|
96
|
+
|
|
97
|
+
private openTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
98
|
+
private closeTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
99
|
+
|
|
100
|
+
ngOnDestroy(): void {
|
|
101
|
+
this.clearTimeouts();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
onMouseEnter(): void {
|
|
105
|
+
this.clearTimeouts();
|
|
106
|
+
this.openTimeout = setTimeout(() => {
|
|
107
|
+
this.context.setOpen(true);
|
|
108
|
+
}, this.context.openDelay);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
onMouseLeave(): void {
|
|
112
|
+
this.clearTimeouts();
|
|
113
|
+
this.closeTimeout = setTimeout(() => {
|
|
114
|
+
this.context.setOpen(false);
|
|
115
|
+
}, this.context.closeDelay);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
onFocus(): void {
|
|
119
|
+
this.clearTimeouts();
|
|
120
|
+
// Open immediately on focus for keyboard users
|
|
121
|
+
this.context.setOpen(true);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
onBlur(event: FocusEvent): void {
|
|
125
|
+
// Check if focus moved to the hover card content
|
|
126
|
+
const relatedTarget = event.relatedTarget as HTMLElement | null;
|
|
127
|
+
const hoverCardContent = this.elementRef.nativeElement.parentElement?.querySelector('[data-slot="hover-card-content"]');
|
|
128
|
+
|
|
129
|
+
if (relatedTarget && hoverCardContent?.contains(relatedTarget)) {
|
|
130
|
+
// Focus moved to content, don't close
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.clearTimeouts();
|
|
135
|
+
this.closeTimeout = setTimeout(() => {
|
|
136
|
+
this.context.setOpen(false);
|
|
137
|
+
}, this.context.closeDelay);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
onKeyDown(event: Event): void {
|
|
141
|
+
event.preventDefault();
|
|
142
|
+
this.context.setOpen(!this.context.open());
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
onEscape(): void {
|
|
146
|
+
this.context.setOpen(false);
|
|
147
|
+
this.elementRef.nativeElement.focus();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private clearTimeouts(): void {
|
|
151
|
+
if (this.openTimeout) {
|
|
152
|
+
clearTimeout(this.openTimeout);
|
|
153
|
+
this.openTimeout = null;
|
|
154
|
+
}
|
|
155
|
+
if (this.closeTimeout) {
|
|
156
|
+
clearTimeout(this.closeTimeout);
|
|
157
|
+
this.closeTimeout = null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|