@nofinite/nui 1.1.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -48
- package/dist/components/accordion/Accordion.cjs +1 -1
- package/dist/components/accordion/Accordion.cjs.map +1 -1
- package/dist/components/accordion/Accordion.js +64 -43
- package/dist/components/accordion/Accordion.js.map +1 -1
- package/dist/components/alert/Alert.cjs +1 -1
- package/dist/components/alert/Alert.cjs.map +1 -1
- package/dist/components/alert/Alert.js +39 -25
- package/dist/components/alert/Alert.js.map +1 -1
- package/dist/components/avatar/Avatar.cjs +1 -1
- package/dist/components/avatar/Avatar.cjs.map +1 -1
- package/dist/components/avatar/Avatar.js +58 -44
- package/dist/components/avatar/Avatar.js.map +1 -1
- package/dist/components/avatar/AvatarGroup.cjs +1 -1
- package/dist/components/avatar/AvatarGroup.cjs.map +1 -1
- package/dist/components/avatar/AvatarGroup.js +34 -25
- package/dist/components/avatar/AvatarGroup.js.map +1 -1
- package/dist/components/badge/Badge.cjs +1 -1
- package/dist/components/badge/Badge.cjs.map +1 -1
- package/dist/components/badge/Badge.js +43 -68
- package/dist/components/badge/Badge.js.map +1 -1
- package/dist/components/badge/BadgeGroup.cjs +1 -1
- package/dist/components/badge/BadgeGroup.cjs.map +1 -1
- package/dist/components/badge/BadgeGroup.js +20 -10
- package/dist/components/badge/BadgeGroup.js.map +1 -1
- package/dist/components/breadcrumbs/Breadcrumbs.cjs +1 -1
- package/dist/components/breadcrumbs/Breadcrumbs.cjs.map +1 -1
- package/dist/components/breadcrumbs/Breadcrumbs.js +59 -39
- package/dist/components/breadcrumbs/Breadcrumbs.js.map +1 -1
- package/dist/components/button/Button.cjs +1 -1
- package/dist/components/button/Button.cjs.map +1 -1
- package/dist/components/button/Button.js +52 -17
- package/dist/components/button/Button.js.map +1 -1
- package/dist/components/card/Card.cjs +1 -1
- package/dist/components/card/Card.cjs.map +1 -1
- package/dist/components/card/Card.js +44 -41
- package/dist/components/card/Card.js.map +1 -1
- package/dist/components/checkbox/Checkbox.cjs +1 -1
- package/dist/components/checkbox/Checkbox.cjs.map +1 -1
- package/dist/components/checkbox/Checkbox.js +59 -40
- package/dist/components/checkbox/Checkbox.js.map +1 -1
- package/dist/components/chip/Chip.cjs +1 -1
- package/dist/components/chip/Chip.cjs.map +1 -1
- package/dist/components/chip/Chip.js +67 -47
- package/dist/components/chip/Chip.js.map +1 -1
- package/dist/components/combobox/Combobox.cjs +1 -1
- package/dist/components/combobox/Combobox.cjs.map +1 -1
- package/dist/components/combobox/Combobox.js +123 -108
- package/dist/components/combobox/Combobox.js.map +1 -1
- package/dist/components/commandpalette/CommandPalette.cjs +1 -1
- package/dist/components/commandpalette/CommandPalette.cjs.map +1 -1
- package/dist/components/commandpalette/CommandPalette.js +96 -73
- package/dist/components/commandpalette/CommandPalette.js.map +1 -1
- package/dist/components/contextmenu/ContextMenu.cjs +1 -1
- package/dist/components/contextmenu/ContextMenu.cjs.map +1 -1
- package/dist/components/contextmenu/ContextMenu.js +79 -58
- package/dist/components/contextmenu/ContextMenu.js.map +1 -1
- package/dist/components/datagrid/DataGrid.cjs +1 -1
- package/dist/components/datagrid/DataGrid.cjs.map +1 -1
- package/dist/components/datagrid/DataGrid.js +184 -202
- package/dist/components/datagrid/DataGrid.js.map +1 -1
- package/dist/components/datepicker/DatePicker.cjs +1 -1
- package/dist/components/datepicker/DatePicker.cjs.map +1 -1
- package/dist/components/datepicker/DatePicker.js +197 -164
- package/dist/components/datepicker/DatePicker.js.map +1 -1
- package/dist/components/daterangepicker/DateRangePicker.cjs +1 -1
- package/dist/components/daterangepicker/DateRangePicker.cjs.map +1 -1
- package/dist/components/daterangepicker/DateRangePicker.js +254 -213
- package/dist/components/daterangepicker/DateRangePicker.js.map +1 -1
- package/dist/components/dialog/DialogProvider.cjs +2 -0
- package/dist/components/dialog/DialogProvider.cjs.map +1 -0
- package/dist/components/dialog/DialogProvider.js +71 -0
- package/dist/components/dialog/DialogProvider.js.map +1 -0
- package/dist/components/dialog/dialogStore.cjs +2 -0
- package/dist/components/dialog/dialogStore.cjs.map +1 -0
- package/dist/components/dialog/dialogStore.js +60 -0
- package/dist/components/dialog/dialogStore.js.map +1 -0
- package/dist/components/drawer/Drawer.cjs +1 -1
- package/dist/components/drawer/Drawer.cjs.map +1 -1
- package/dist/components/drawer/Drawer.js +69 -47
- package/dist/components/drawer/Drawer.js.map +1 -1
- package/dist/components/dropdown/Dropdown.cjs +1 -1
- package/dist/components/dropdown/Dropdown.cjs.map +1 -1
- package/dist/components/dropdown/Dropdown.js +134 -108
- package/dist/components/dropdown/Dropdown.js.map +1 -1
- package/dist/components/fileuploader/FileUploader.cjs +1 -1
- package/dist/components/fileuploader/FileUploader.cjs.map +1 -1
- package/dist/components/fileuploader/FileUploader.js +96 -61
- package/dist/components/fileuploader/FileUploader.js.map +1 -1
- package/dist/components/hovercard/HoverCard.cjs +1 -1
- package/dist/components/hovercard/HoverCard.cjs.map +1 -1
- package/dist/components/hovercard/HoverCard.js +124 -69
- package/dist/components/hovercard/HoverCard.js.map +1 -1
- package/dist/components/input/Input.cjs +1 -1
- package/dist/components/input/Input.cjs.map +1 -1
- package/dist/components/input/Input.js +62 -37
- package/dist/components/input/Input.js.map +1 -1
- package/dist/components/layout/Container.cjs +1 -1
- package/dist/components/layout/Container.cjs.map +1 -1
- package/dist/components/layout/Container.js +21 -30
- package/dist/components/layout/Container.js.map +1 -1
- package/dist/components/layout/Flex.cjs +1 -1
- package/dist/components/layout/Flex.cjs.map +1 -1
- package/dist/components/layout/Flex.js +36 -19
- package/dist/components/layout/Flex.js.map +1 -1
- package/dist/components/layout/Grid.cjs +1 -1
- package/dist/components/layout/Grid.cjs.map +1 -1
- package/dist/components/layout/Grid.js +30 -18
- package/dist/components/layout/Grid.js.map +1 -1
- package/dist/components/link/Link.cjs +2 -0
- package/dist/components/link/Link.cjs.map +1 -0
- package/dist/components/link/Link.js +41 -0
- package/dist/components/link/Link.js.map +1 -0
- package/dist/components/megamenu/MegaMenu.cjs +1 -1
- package/dist/components/megamenu/MegaMenu.cjs.map +1 -1
- package/dist/components/megamenu/MegaMenu.js +107 -38
- package/dist/components/megamenu/MegaMenu.js.map +1 -1
- package/dist/components/modal/Modal.cjs +1 -1
- package/dist/components/modal/Modal.cjs.map +1 -1
- package/dist/components/modal/Modal.js +91 -83
- package/dist/components/modal/Modal.js.map +1 -1
- package/dist/components/multiselect/MultiSelect.cjs +2 -0
- package/dist/components/multiselect/MultiSelect.cjs.map +1 -0
- package/dist/components/multiselect/MultiSelect.js +176 -0
- package/dist/components/multiselect/MultiSelect.js.map +1 -0
- package/dist/components/nuiprovider/NUIProvider.cjs +2 -0
- package/dist/components/nuiprovider/NUIProvider.cjs.map +1 -0
- package/dist/components/nuiprovider/NUIProvider.js +36 -0
- package/dist/components/nuiprovider/NUIProvider.js.map +1 -0
- package/dist/components/pagination/Pagination.cjs +1 -1
- package/dist/components/pagination/Pagination.cjs.map +1 -1
- package/dist/components/pagination/Pagination.js +74 -41
- package/dist/components/pagination/Pagination.js.map +1 -1
- package/dist/components/popover/Popover.cjs +1 -1
- package/dist/components/popover/Popover.cjs.map +1 -1
- package/dist/components/popover/Popover.js +99 -100
- package/dist/components/popover/Popover.js.map +1 -1
- package/dist/components/progress/Progress.cjs +1 -1
- package/dist/components/progress/Progress.cjs.map +1 -1
- package/dist/components/progress/Progress.js +44 -22
- package/dist/components/progress/Progress.js.map +1 -1
- package/dist/components/radiogroup/RadioGroup.cjs +1 -1
- package/dist/components/radiogroup/RadioGroup.cjs.map +1 -1
- package/dist/components/radiogroup/RadioGroup.js +69 -74
- package/dist/components/radiogroup/RadioGroup.js.map +1 -1
- package/dist/components/rating/Rating.cjs +1 -1
- package/dist/components/rating/Rating.cjs.map +1 -1
- package/dist/components/rating/Rating.js +72 -33
- package/dist/components/rating/Rating.js.map +1 -1
- package/dist/components/resizable/Resizable.cjs +2 -0
- package/dist/components/resizable/Resizable.cjs.map +1 -0
- package/dist/components/resizable/Resizable.js +134 -0
- package/dist/components/resizable/Resizable.js.map +1 -0
- package/dist/components/select/Select.cjs +1 -1
- package/dist/components/select/Select.cjs.map +1 -1
- package/dist/components/select/Select.js +114 -113
- package/dist/components/select/Select.js.map +1 -1
- package/dist/components/skeleton/Skeleton.cjs +1 -1
- package/dist/components/skeleton/Skeleton.cjs.map +1 -1
- package/dist/components/skeleton/Skeleton.js +90 -67
- package/dist/components/skeleton/Skeleton.js.map +1 -1
- package/dist/components/slider/Slider.cjs +1 -1
- package/dist/components/slider/Slider.cjs.map +1 -1
- package/dist/components/slider/Slider.js +85 -82
- package/dist/components/slider/Slider.js.map +1 -1
- package/dist/components/spinner/Spinner.cjs +1 -1
- package/dist/components/spinner/Spinner.cjs.map +1 -1
- package/dist/components/spinner/Spinner.js +60 -17
- package/dist/components/spinner/Spinner.js.map +1 -1
- package/dist/components/stepper/Stepper.cjs +1 -5
- package/dist/components/stepper/Stepper.cjs.map +1 -1
- package/dist/components/stepper/Stepper.js +65 -39
- package/dist/components/stepper/Stepper.js.map +1 -1
- package/dist/components/switch/Switch.cjs +1 -1
- package/dist/components/switch/Switch.cjs.map +1 -1
- package/dist/components/switch/Switch.js +89 -62
- package/dist/components/switch/Switch.js.map +1 -1
- package/dist/components/table/Table.cjs +1 -1
- package/dist/components/table/Table.cjs.map +1 -1
- package/dist/components/table/Table.js +62 -35
- package/dist/components/table/Table.js.map +1 -1
- package/dist/components/tabs/Tabs.cjs +1 -1
- package/dist/components/tabs/Tabs.cjs.map +1 -1
- package/dist/components/tabs/Tabs.js +110 -50
- package/dist/components/tabs/Tabs.js.map +1 -1
- package/dist/components/textarea/Textarea.cjs +1 -1
- package/dist/components/textarea/Textarea.cjs.map +1 -1
- package/dist/components/textarea/Textarea.js +63 -58
- package/dist/components/textarea/Textarea.js.map +1 -1
- package/dist/components/timepicker/TimePicker.cjs +2 -0
- package/dist/components/timepicker/TimePicker.cjs.map +1 -0
- package/dist/components/timepicker/TimePicker.js +159 -0
- package/dist/components/timepicker/TimePicker.js.map +1 -0
- package/dist/components/timerangepicker/TimeRangePicker.cjs +2 -0
- package/dist/components/timerangepicker/TimeRangePicker.cjs.map +1 -0
- package/dist/components/timerangepicker/TimeRangePicker.js +208 -0
- package/dist/components/timerangepicker/TimeRangePicker.js.map +1 -0
- package/dist/components/toast/Toast.cjs +1 -1
- package/dist/components/toast/Toast.cjs.map +1 -1
- package/dist/components/toast/Toast.js +91 -38
- package/dist/components/toast/Toast.js.map +1 -1
- package/dist/components/tooltip/Tooltip.cjs +1 -1
- package/dist/components/tooltip/Tooltip.cjs.map +1 -1
- package/dist/components/tooltip/Tooltip.js +72 -56
- package/dist/components/tooltip/Tooltip.js.map +1 -1
- package/dist/components/treeview/TreeView.cjs +1 -1
- package/dist/components/treeview/TreeView.cjs.map +1 -1
- package/dist/components/treeview/TreeView.js +120 -90
- package/dist/components/treeview/TreeView.js.map +1 -1
- package/dist/components/virtuallist/VirtualList.cjs +1 -1
- package/dist/components/virtuallist/VirtualList.cjs.map +1 -1
- package/dist/components/virtuallist/VirtualList.js +52 -34
- package/dist/components/virtuallist/VirtualList.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.css +1 -0
- package/dist/index.js +118 -107
- package/dist/index.js.map +1 -1
- package/dist/package.json +49 -6
- package/dist/types/components/accordion/Accordion.d.ts +7 -3
- package/dist/types/components/accordion/Accordion.d.ts.map +1 -1
- package/dist/types/components/alert/Alert.d.ts +18 -5
- package/dist/types/components/alert/Alert.d.ts.map +1 -1
- package/dist/types/components/avatar/Avatar.d.ts +12 -8
- package/dist/types/components/avatar/Avatar.d.ts.map +1 -1
- package/dist/types/components/avatar/AvatarGroup.d.ts +11 -4
- package/dist/types/components/avatar/AvatarGroup.d.ts.map +1 -1
- package/dist/types/components/badge/Badge.d.ts +19 -11
- package/dist/types/components/badge/Badge.d.ts.map +1 -1
- package/dist/types/components/badge/BadgeGroup.d.ts +7 -4
- package/dist/types/components/badge/BadgeGroup.d.ts.map +1 -1
- package/dist/types/components/breadcrumbs/Breadcrumbs.d.ts +14 -6
- package/dist/types/components/breadcrumbs/Breadcrumbs.d.ts.map +1 -1
- package/dist/types/components/button/Button.d.ts +25 -10
- package/dist/types/components/button/Button.d.ts.map +1 -1
- package/dist/types/components/card/Card.d.ts +12 -21
- package/dist/types/components/card/Card.d.ts.map +1 -1
- package/dist/types/components/checkbox/Checkbox.d.ts +12 -7
- package/dist/types/components/checkbox/Checkbox.d.ts.map +1 -1
- package/dist/types/components/chip/Chip.d.ts +14 -11
- package/dist/types/components/chip/Chip.d.ts.map +1 -1
- package/dist/types/components/combobox/Combobox.d.ts +15 -4
- package/dist/types/components/combobox/Combobox.d.ts.map +1 -1
- package/dist/types/components/commandpalette/CommandPalette.d.ts +12 -3
- package/dist/types/components/commandpalette/CommandPalette.d.ts.map +1 -1
- package/dist/types/components/contextmenu/ContextMenu.d.ts +14 -6
- package/dist/types/components/contextmenu/ContextMenu.d.ts.map +1 -1
- package/dist/types/components/datagrid/DataGrid.d.ts +16 -4
- package/dist/types/components/datagrid/DataGrid.d.ts.map +1 -1
- package/dist/types/components/datepicker/DatePicker.d.ts +13 -1
- package/dist/types/components/datepicker/DatePicker.d.ts.map +1 -1
- package/dist/types/components/daterangepicker/DateRangePicker.d.ts +3 -1
- package/dist/types/components/daterangepicker/DateRangePicker.d.ts.map +1 -1
- package/dist/types/components/dialog/DialogProvider.d.ts +2 -0
- package/dist/types/components/dialog/DialogProvider.d.ts.map +1 -0
- package/dist/types/components/dialog/dialogStore.d.ts +42 -0
- package/dist/types/components/dialog/dialogStore.d.ts.map +1 -0
- package/dist/types/components/drawer/Drawer.d.ts +18 -4
- package/dist/types/components/drawer/Drawer.d.ts.map +1 -1
- package/dist/types/components/dropdown/Dropdown.d.ts +21 -16
- package/dist/types/components/dropdown/Dropdown.d.ts.map +1 -1
- package/dist/types/components/fileuploader/FileUploader.d.ts +22 -3
- package/dist/types/components/fileuploader/FileUploader.d.ts.map +1 -1
- package/dist/types/components/hovercard/HoverCard.d.ts +45 -5
- package/dist/types/components/hovercard/HoverCard.d.ts.map +1 -1
- package/dist/types/components/input/Input.d.ts +20 -10
- package/dist/types/components/input/Input.d.ts.map +1 -1
- package/dist/types/components/layout/Container.d.ts +8 -4
- package/dist/types/components/layout/Container.d.ts.map +1 -1
- package/dist/types/components/layout/Flex.d.ts +27 -10
- package/dist/types/components/layout/Flex.d.ts.map +1 -1
- package/dist/types/components/layout/Grid.d.ts +11 -5
- package/dist/types/components/layout/Grid.d.ts.map +1 -1
- package/dist/types/components/link/Link.d.ts +22 -0
- package/dist/types/components/link/Link.d.ts.map +1 -0
- package/dist/types/components/megamenu/MegaMenu.d.ts +8 -11
- package/dist/types/components/megamenu/MegaMenu.d.ts.map +1 -1
- package/dist/types/components/modal/Modal.d.ts +8 -7
- package/dist/types/components/modal/Modal.d.ts.map +1 -1
- package/dist/types/components/multiselect/MultiSelect.d.ts +33 -0
- package/dist/types/components/multiselect/MultiSelect.d.ts.map +1 -0
- package/dist/types/components/nuiprovider/NUIProvider.d.ts +29 -0
- package/dist/types/components/nuiprovider/NUIProvider.d.ts.map +1 -0
- package/dist/types/components/pagination/Pagination.d.ts +17 -3
- package/dist/types/components/pagination/Pagination.d.ts.map +1 -1
- package/dist/types/components/popover/Popover.d.ts +54 -16
- package/dist/types/components/popover/Popover.d.ts.map +1 -1
- package/dist/types/components/progress/Progress.d.ts +17 -7
- package/dist/types/components/progress/Progress.d.ts.map +1 -1
- package/dist/types/components/radiogroup/RadioGroup.d.ts +15 -10
- package/dist/types/components/radiogroup/RadioGroup.d.ts.map +1 -1
- package/dist/types/components/rating/Rating.d.ts +24 -10
- package/dist/types/components/rating/Rating.d.ts.map +1 -1
- package/dist/types/components/resizable/Resizable.d.ts +24 -0
- package/dist/types/components/resizable/Resizable.d.ts.map +1 -0
- package/dist/types/components/select/Select.d.ts +17 -8
- package/dist/types/components/select/Select.d.ts.map +1 -1
- package/dist/types/components/skeleton/Skeleton.d.ts +37 -36
- package/dist/types/components/skeleton/Skeleton.d.ts.map +1 -1
- package/dist/types/components/slider/Slider.d.ts +15 -4
- package/dist/types/components/slider/Slider.d.ts.map +1 -1
- package/dist/types/components/spinner/Spinner.d.ts +14 -4
- package/dist/types/components/spinner/Spinner.d.ts.map +1 -1
- package/dist/types/components/stepper/Stepper.d.ts +17 -3
- package/dist/types/components/stepper/Stepper.d.ts.map +1 -1
- package/dist/types/components/switch/Switch.d.ts +20 -5
- package/dist/types/components/switch/Switch.d.ts.map +1 -1
- package/dist/types/components/table/Table.d.ts +24 -4
- package/dist/types/components/table/Table.d.ts.map +1 -1
- package/dist/types/components/tabs/Tabs.d.ts +25 -12
- package/dist/types/components/tabs/Tabs.d.ts.map +1 -1
- package/dist/types/components/textarea/Textarea.d.ts +8 -5
- package/dist/types/components/textarea/Textarea.d.ts.map +1 -1
- package/dist/types/components/timepicker/TimePicker.d.ts +26 -0
- package/dist/types/components/timepicker/TimePicker.d.ts.map +1 -0
- package/dist/types/components/timerangepicker/TimeRangePicker.d.ts +32 -0
- package/dist/types/components/timerangepicker/TimeRangePicker.d.ts.map +1 -0
- package/dist/types/components/toast/Toast.d.ts +23 -7
- package/dist/types/components/toast/Toast.d.ts.map +1 -1
- package/dist/types/components/tooltip/Tooltip.d.ts +13 -2
- package/dist/types/components/tooltip/Tooltip.d.ts.map +1 -1
- package/dist/types/components/treeview/TreeView.d.ts +20 -6
- package/dist/types/components/treeview/TreeView.d.ts.map +1 -1
- package/dist/types/components/virtuallist/VirtualList.d.ts +12 -16
- package/dist/types/components/virtuallist/VirtualList.d.ts.map +1 -1
- package/dist/types/index.d.ts +8 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/utils/cn/cn.d.ts +19 -0
- package/dist/types/utils/cn/cn.d.ts.map +1 -0
- package/dist/types/utils/generateid/generateId.d.ts +7 -0
- package/dist/types/utils/generateid/generateId.d.ts.map +1 -1
- package/dist/types/utils/index.d.ts +2 -0
- package/dist/types/utils/index.d.ts.map +1 -1
- package/dist/types/utils/inertmanager/inertManager.d.ts +13 -0
- package/dist/types/utils/inertmanager/inertManager.d.ts.map +1 -1
- package/dist/types/utils/keyboardnav/keyboardNav.d.ts +17 -6
- package/dist/types/utils/keyboardnav/keyboardNav.d.ts.map +1 -1
- package/dist/types/utils/onclickoutside/onClickOutside.d.ts +9 -1
- package/dist/types/utils/onclickoutside/onClickOutside.d.ts.map +1 -1
- package/dist/types/utils/portal/portal.d.ts +14 -1
- package/dist/types/utils/portal/portal.d.ts.map +1 -1
- package/dist/types/utils/restorefocus/restoreFocus.d.ts +8 -4
- package/dist/types/utils/restorefocus/restoreFocus.d.ts.map +1 -1
- package/dist/types/utils/scrolllock/scrollLock.d.ts +10 -2
- package/dist/types/utils/scrolllock/scrollLock.d.ts.map +1 -1
- package/dist/types/utils/slot/slot.d.ts +12 -0
- package/dist/types/utils/slot/slot.d.ts.map +1 -0
- package/dist/types/utils/trapfocus/trapFocus.d.ts +6 -2
- package/dist/types/utils/trapfocus/trapFocus.d.ts.map +1 -1
- package/dist/utils/cn/cn.cjs +2 -0
- package/dist/utils/cn/cn.cjs.map +1 -0
- package/dist/utils/cn/cn.js +21 -0
- package/dist/utils/cn/cn.js.map +1 -0
- package/dist/utils/inertmanager/inertManager.cjs.map +1 -1
- package/dist/utils/inertmanager/inertManager.js.map +1 -1
- package/dist/utils/onclickoutside/onClickOutside.cjs +1 -1
- package/dist/utils/onclickoutside/onClickOutside.cjs.map +1 -1
- package/dist/utils/onclickoutside/onClickOutside.js +10 -6
- package/dist/utils/onclickoutside/onClickOutside.js.map +1 -1
- package/dist/utils/portal/portal.cjs.map +1 -1
- package/dist/utils/portal/portal.js.map +1 -1
- package/dist/utils/restorefocus/restoreFocus.cjs.map +1 -1
- package/dist/utils/restorefocus/restoreFocus.js.map +1 -1
- package/dist/utils/scrolllock/scrollLock.cjs.map +1 -1
- package/dist/utils/scrolllock/scrollLock.js +7 -0
- package/dist/utils/scrolllock/scrollLock.js.map +1 -1
- package/dist/utils/slot/slot.cjs +2 -0
- package/dist/utils/slot/slot.cjs.map +1 -0
- package/dist/utils/slot/slot.js +57 -0
- package/dist/utils/slot/slot.js.map +1 -0
- package/dist/utils/trapfocus/trapFocus.cjs.map +1 -1
- package/dist/utils/trapfocus/trapFocus.js.map +1 -1
- package/package.json +49 -6
- package/dist/components/layout/HStack.cjs +0 -2
- package/dist/components/layout/HStack.cjs.map +0 -1
- package/dist/components/layout/HStack.js +0 -9
- package/dist/components/layout/HStack.js.map +0 -1
- package/dist/components/layout/Stack.cjs +0 -2
- package/dist/components/layout/Stack.cjs.map +0 -1
- package/dist/components/layout/Stack.js +0 -9
- package/dist/components/layout/Stack.js.map +0 -1
- package/dist/styles/nui.css +0 -1
- package/dist/theme/NUIProvider.cjs +0 -2
- package/dist/theme/NUIProvider.cjs.map +0 -1
- package/dist/theme/NUIProvider.js +0 -34
- package/dist/theme/NUIProvider.js.map +0 -1
- package/dist/theme/useTheme.cjs +0 -2
- package/dist/theme/useTheme.cjs.map +0 -1
- package/dist/theme/useTheme.js +0 -9
- package/dist/theme/useTheme.js.map +0 -1
- package/dist/types/components/layout/HStack.d.ts +0 -8
- package/dist/types/components/layout/HStack.d.ts.map +0 -1
- package/dist/types/components/layout/Stack.d.ts +0 -8
- package/dist/types/components/layout/Stack.d.ts.map +0 -1
- package/dist/types/theme/NUIProvider.d.ts +0 -14
- package/dist/types/theme/NUIProvider.d.ts.map +0 -1
- package/dist/types/theme/useTheme.d.ts +0 -11
- package/dist/types/theme/useTheme.d.ts.map +0 -1
- package/dist/utils/generateid/generateId.cjs +0 -2
- package/dist/utils/generateid/generateId.cjs.map +0 -1
- package/dist/utils/generateid/generateId.js +0 -7
- package/dist/utils/generateid/generateId.js.map +0 -1
- package/dist/utils/keyboardnav/keyboardNav.cjs +0 -2
- package/dist/utils/keyboardnav/keyboardNav.cjs.map +0 -1
- package/dist/utils/keyboardnav/keyboardNav.js +0 -10
- package/dist/utils/keyboardnav/keyboardNav.js.map +0 -1
|
@@ -1,52 +1,105 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useState as
|
|
3
|
-
import { Portal as T } from "../../utils/portal/portal.js";
|
|
1
|
+
import { jsxs as m, jsx as o } from "react/jsx-runtime";
|
|
2
|
+
import { useContext as g, createContext as T, useState as p, useEffect as h, useCallback as a, useRef as y } from "react";
|
|
4
3
|
/* empty css */
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"aria-label": "Close notification",
|
|
13
|
-
onClick: () => o(e),
|
|
14
|
-
children: "×"
|
|
15
|
-
}
|
|
16
|
-
)
|
|
17
|
-
] });
|
|
4
|
+
import { Portal as C } from "../../utils/portal/portal.js";
|
|
5
|
+
import { cn as _ } from "../../utils/cn/cn.js";
|
|
6
|
+
const v = T(null);
|
|
7
|
+
function M() {
|
|
8
|
+
const t = g(v);
|
|
9
|
+
if (!t) throw new Error("useToast must be used inside a <ToastProvider>");
|
|
10
|
+
return t;
|
|
18
11
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
e
|
|
31
|
-
|
|
12
|
+
function D({ children: t }) {
|
|
13
|
+
const [d, r] = p([]), [f, c] = p(!1);
|
|
14
|
+
h(() => {
|
|
15
|
+
c(!0);
|
|
16
|
+
}, []);
|
|
17
|
+
const u = a((e, s) => {
|
|
18
|
+
const i = typeof crypto < "u" && crypto.randomUUID ? crypto.randomUUID() : `toast-${Date.now()}-${Math.floor(Math.random() * 1e4)}`;
|
|
19
|
+
return r((x) => [...x, { id: i, message: e, ...s }]), i;
|
|
20
|
+
}, []), n = a((e) => {
|
|
21
|
+
r((s) => s.map((i) => i.id === e ? { ...i, isClosing: !0 } : i));
|
|
22
|
+
}, []), l = a((e) => {
|
|
23
|
+
r((s) => s.filter((i) => i.id !== e));
|
|
24
|
+
}, []);
|
|
25
|
+
return /* @__PURE__ */ m(v.Provider, { value: { show: u, dismiss: n }, children: [
|
|
26
|
+
t,
|
|
27
|
+
f && /* @__PURE__ */ o(C, { children: /* @__PURE__ */ o(
|
|
32
28
|
"div",
|
|
33
29
|
{
|
|
34
|
-
className: "
|
|
30
|
+
className: "nui-toast-region",
|
|
35
31
|
"aria-live": "polite",
|
|
36
32
|
"aria-atomic": "true",
|
|
37
|
-
|
|
33
|
+
role: "region",
|
|
34
|
+
"aria-label": "Notifications",
|
|
35
|
+
children: d.map((e) => /* @__PURE__ */ o(
|
|
36
|
+
N,
|
|
37
|
+
{
|
|
38
|
+
toast: e,
|
|
39
|
+
onDismiss: () => n(e.id),
|
|
40
|
+
onRemove: () => l(e.id)
|
|
41
|
+
},
|
|
42
|
+
e.id
|
|
43
|
+
))
|
|
38
44
|
}
|
|
39
45
|
) })
|
|
40
46
|
] });
|
|
41
47
|
}
|
|
42
|
-
function
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
function N({ toast: t, onRemove: d }) {
|
|
49
|
+
const [r, f] = p(!1), c = y(null), u = t.duration !== void 0 ? t.duration : 4e3, n = a(() => {
|
|
50
|
+
f(!0), setTimeout(() => {
|
|
51
|
+
d();
|
|
52
|
+
}, 200);
|
|
53
|
+
}, [d]);
|
|
54
|
+
h(() => {
|
|
55
|
+
t.isClosing && n();
|
|
56
|
+
}, [t.isClosing, n]);
|
|
57
|
+
const l = a(() => {
|
|
58
|
+
u === 1 / 0 || r || (c.current = setTimeout(() => {
|
|
59
|
+
n();
|
|
60
|
+
}, u));
|
|
61
|
+
}, [u, r, n]), e = a(() => {
|
|
62
|
+
c.current && clearTimeout(c.current);
|
|
63
|
+
}, []);
|
|
64
|
+
h(() => (l(), e), [l, e]);
|
|
65
|
+
const s = t.variant === "error" ? "alert" : "status";
|
|
66
|
+
return /* @__PURE__ */ m(
|
|
67
|
+
"div",
|
|
68
|
+
{
|
|
69
|
+
className: _(
|
|
70
|
+
"nui-toast",
|
|
71
|
+
`nui-toast--${t.variant || "default"}`
|
|
72
|
+
),
|
|
73
|
+
"data-state": r ? "closed" : "open",
|
|
74
|
+
onMouseEnter: e,
|
|
75
|
+
onMouseLeave: l,
|
|
76
|
+
role: s,
|
|
77
|
+
children: [
|
|
78
|
+
/* @__PURE__ */ m("div", { className: "nui-toast__content", children: [
|
|
79
|
+
/* @__PURE__ */ o("strong", { className: "nui-toast__title", children: t.message }),
|
|
80
|
+
t.description && /* @__PURE__ */ o("p", { className: "nui-toast__description", children: t.description })
|
|
81
|
+
] }),
|
|
82
|
+
/* @__PURE__ */ o(
|
|
83
|
+
"button",
|
|
84
|
+
{
|
|
85
|
+
type: "button",
|
|
86
|
+
"aria-label": "Close notification",
|
|
87
|
+
className: "nui-toast__close",
|
|
88
|
+
onClick: (i) => {
|
|
89
|
+
i.preventDefault(), e(), n();
|
|
90
|
+
},
|
|
91
|
+
children: /* @__PURE__ */ m("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
92
|
+
/* @__PURE__ */ o("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
93
|
+
/* @__PURE__ */ o("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
94
|
+
] })
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
);
|
|
46
100
|
}
|
|
47
101
|
export {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
j as useToast
|
|
102
|
+
D as ToastProvider,
|
|
103
|
+
M as useToast
|
|
51
104
|
};
|
|
52
105
|
//# sourceMappingURL=Toast.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toast.js","sources":["../../../src/components/toast/Toast.tsx"],"sourcesContent":["/**\r\n * Toast.tsx\r\n * ----------\r\n * Basic toast component UI. The provider handles logic.\r\n * ------------------\r\n * - Global provider that renders all toasts\r\n * - Contains aria-live region for screen readers\r\n * - Handles auto-dismiss\r\n * - Provides useToast() hook\r\n */\r\n\r\nimport React, { createContext, useContext, useState, useCallback } from 'react';\r\nimport { Portal } from '../../utils/portal/portal';\r\nimport './Toast.css';\r\n\r\ninterface ToastItem {\r\n id: string;\r\n message: string;\r\n}\r\n\r\ninterface ToastContextValue {\r\n show: (message: string, timeout?: number) => void;\r\n}\r\n\r\ninterface ToastProps {\r\n id: string;\r\n message: string;\r\n onClose: (id: string) => void;\r\n}\r\n\r\nexport function Toast({ id, message, onClose }: ToastProps) {\r\n return (\r\n <div className=\"ui-toast\" role=\"alert\">\r\n <span>{message}</span>\r\n <button\r\n type=\"button\"\r\n aria-label=\"Close notification\"\r\n onClick={() => onClose(id)}\r\n >\r\n ×\r\n </button>\r\n </div>\r\n );\r\n}\r\n\r\nconst ToastContext = createContext<ToastContextValue | null>(null);\r\n\r\nexport function ToastProvider({ children }: { children: React.ReactNode }) {\r\n const [toasts, setToasts] = useState<ToastItem[]>([]);\r\n\r\n const show = useCallback((message: string, timeout = 3000) => {\r\n const id = Math.random().toString(36).slice(2);\r\n\r\n setToasts((prev) => [...prev, { id, message }]);\r\n\r\n setTimeout(() => {\r\n setToasts((prev) => prev.filter((t) => t.id !== id));\r\n }, timeout);\r\n }, []);\r\n\r\n const remove = (id: string) => {\r\n setToasts((prev) => prev.filter((t) => t.id !== id));\r\n };\r\n\r\n return (\r\n <ToastContext.Provider value={{ show }}>\r\n {/* Main app */}\r\n {children}\r\n\r\n {/* Portal for notification list */}\r\n <Portal>\r\n <div\r\n className=\"ui-toast-container\"\r\n aria-live=\"polite\"\r\n aria-atomic=\"true\"\r\n >\r\n {toasts.map((t) => (\r\n <Toast key={t.id} id={t.id} message={t.message} onClose={remove} />\r\n ))}\r\n </div>\r\n </Portal>\r\n </ToastContext.Provider>\r\n );\r\n}\r\n\r\nexport function useToast() {\r\n const ctx = useContext(ToastContext);\r\n if (!ctx) throw new Error('useToast must be used inside <ToastProvider>');\r\n return ctx;\r\n}\r\n"],"names":["Toast","id","message","onClose","jsxs","jsx","ToastContext","createContext","ToastProvider","children","toasts","setToasts","useState","show","useCallback","timeout","prev","t","remove","Portal","useToast","ctx","useContext"],"mappings":";;;;AA8BO,SAASA,EAAM,EAAE,IAAAC,GAAI,SAAAC,GAAS,SAAAC,KAAuB;AAC1D,SACE,gBAAAC,EAAC,OAAA,EAAI,WAAU,YAAW,MAAK,SAC7B,UAAA;AAAA,IAAA,gBAAAC,EAAC,UAAM,UAAAH,EAAA,CAAQ;AAAA,IACf,gBAAAG;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,cAAW;AAAA,QACX,SAAS,MAAMF,EAAQF,CAAE;AAAA,QAC1B,UAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAED,GACF;AAEJ;AAEA,MAAMK,IAAeC,EAAwC,IAAI;AAE1D,SAASC,EAAc,EAAE,UAAAC,KAA2C;AACzE,QAAM,CAACC,GAAQC,CAAS,IAAIC,EAAsB,CAAA,CAAE,GAE9CC,IAAOC,EAAY,CAACZ,GAAiBa,IAAU,QAAS;AAC5D,UAAMd,IAAK,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,CAAC;AAE7C,IAAAU,EAAU,CAACK,MAAS,CAAC,GAAGA,GAAM,EAAE,IAAAf,GAAI,SAAAC,EAAA,CAAS,CAAC,GAE9C,WAAW,MAAM;AACf,MAAAS,EAAU,CAACK,MAASA,EAAK,OAAO,CAACC,MAAMA,EAAE,OAAOhB,CAAE,CAAC;AAAA,IACrD,GAAGc,CAAO;AAAA,EACZ,GAAG,CAAA,CAAE,GAECG,IAAS,CAACjB,MAAe;AAC7B,IAAAU,EAAU,CAACK,MAASA,EAAK,OAAO,CAACC,MAAMA,EAAE,OAAOhB,CAAE,CAAC;AAAA,EACrD;AAEA,2BACGK,EAAa,UAAb,EAAsB,OAAO,EAAE,MAAAO,KAE7B,UAAA;AAAA,IAAAJ;AAAA,sBAGAU,GAAA,EACC,UAAA,gBAAAd;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV,aAAU;AAAA,QACV,eAAY;AAAA,QAEX,YAAO,IAAI,CAAC,MACX,gBAAAA,EAACL,KAAiB,IAAI,EAAE,IAAI,SAAS,EAAE,SAAS,SAASkB,EAAA,GAA7C,EAAE,EAAmD,CAClE;AAAA,MAAA;AAAA,IAAA,EACH,CACF;AAAA,EAAA,GACF;AAEJ;AAEO,SAASE,IAAW;AACzB,QAAMC,IAAMC,EAAWhB,CAAY;AACnC,MAAI,CAACe,EAAK,OAAM,IAAI,MAAM,8CAA8C;AACxE,SAAOA;AACT;"}
|
|
1
|
+
{"version":3,"file":"Toast.js","sources":["../../../src/components/toast/Toast.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { createContext, useContext, useState, useCallback, useEffect, useRef } from 'react';\nimport { cn } from '../../utils';\nimport { Portal } from '../../utils';\nimport './Toast.css';\n\n/* ============================================================\n * Types\n * ============================================================ */\n\nexport type ToastVariant = 'default' | 'success' | 'error' | 'warning';\n\nexport interface ToastOptions {\n /** Time in milliseconds before the toast auto-dismisses. Set to Infinity to disable. Defaults to 4000. */\n duration?: number;\n /** The semantic visual variant of the toast. Defaults to 'default'. */\n variant?: ToastVariant;\n /** Secondary descriptive text displayed below the main message. */\n description?: React.ReactNode;\n}\n\nexport interface ToastData extends ToastOptions {\n id: string;\n message: React.ReactNode;\n}\n\ninterface ToastContextValue {\n /** Displays a new toast notification and returns its unique ID */\n show: (message: React.ReactNode, options?: ToastOptions) => string;\n /** Programmatically triggers the exit animation and removes the toast by ID */\n dismiss: (id: string) => void;\n}\n\n/* ============================================================\n * Context\n * ============================================================ */\n\nconst ToastContext = createContext<ToastContextValue | null>(null);\n\nexport function useToast() {\n const ctx = useContext(ToastContext);\n if (!ctx) throw new Error('useToast must be used inside a <ToastProvider>');\n return ctx;\n}\n\n/* ============================================================\n * 1. Provider & Container\n * ============================================================ */\n\n/**\n * Toast Provider\n * * Wraps your application to provide the `useToast` hook.\n * * Automatically manages the WAI-ARIA live region Portal for rendering notifications.\n * * Note: Does not use `forwardRef` as it strictly returns a Context Provider and a Portal.\n */\nexport function ToastProvider({ children }: { children: React.ReactNode }) {\n const [toasts, setToasts] = useState<ToastData[]>([]);\n const [isMounted, setIsMounted] = useState(false);\n\n // Prevent SSR Hydration mismatch for the Portal by only mounting the region on the client\n useEffect(() => {\n setIsMounted(true);\n }, []);\n\n const show = useCallback((message: React.ReactNode, options?: ToastOptions) => {\n // Generate a secure, unique ID without relying on math.random() alone\n const id = typeof crypto !== 'undefined' && crypto.randomUUID \n ? crypto.randomUUID() \n : `toast-${Date.now()}-${Math.floor(Math.random() * 10000)}`;\n\n setToasts((prev) => [...prev, { id, message, ...options }]);\n return id;\n }, []);\n\n const dismiss = useCallback((id: string) => {\n // We don't remove it from the array immediately. \n // Flagging it triggers the ToastItem's CSS exit animation, which then calls remove() safely.\n setToasts((prev) => prev.map((t) => (t.id === id ? { ...t, isClosing: true } : t)));\n }, []);\n\n const remove = useCallback((id: string) => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n }, []);\n\n return (\n <ToastContext.Provider value={{ show, dismiss }}>\n {children}\n\n {isMounted && (\n <Portal>\n <div\n className=\"nui-toast-region\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n role=\"region\"\n aria-label=\"Notifications\"\n >\n {toasts.map((toast) => (\n <ToastItem \n key={toast.id} \n toast={toast} \n onDismiss={() => dismiss(toast.id)} \n onRemove={() => remove(toast.id)} \n />\n ))}\n </div>\n </Portal>\n )}\n </ToastContext.Provider>\n );\n}\n\n/* ============================================================\n * 2. Individual Toast Item (Smart Component)\n * ============================================================ */\n\ninterface ToastItemProps {\n toast: ToastData & { isClosing?: boolean };\n onDismiss: () => void;\n onRemove: () => void;\n}\n\nfunction ToastItem({ toast, onRemove }: ToastItemProps) {\n const [isExiting, setIsExiting] = useState(false);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Duration defaults to 4000ms. Set to Infinity to disable auto-close.\n const duration = toast.duration !== undefined ? toast.duration : 4000;\n\n // 1. Handle Animation and Removal\n const triggerExit = useCallback(() => {\n setIsExiting(true);\n // Wait for the CSS exit animation to finish before destroying the DOM node (matches CSS 0.2s duration)\n setTimeout(() => {\n onRemove();\n }, 200); \n }, [onRemove]);\n\n // If the provider tells us to close programmatically, trigger exit\n useEffect(() => {\n if (toast.isClosing) triggerExit();\n }, [toast.isClosing, triggerExit]);\n\n // 2. Timer Management (Pause on Hover)\n const startTimer = useCallback(() => {\n if (duration === Infinity || isExiting) return;\n timerRef.current = setTimeout(() => {\n triggerExit();\n }, duration);\n }, [duration, isExiting, triggerExit]);\n\n const clearTimer = useCallback(() => {\n if (timerRef.current) clearTimeout(timerRef.current);\n }, []);\n\n // Start timer on mount\n useEffect(() => {\n startTimer();\n return clearTimer;\n }, [startTimer, clearTimer]);\n\n // 3. Render\n // Critical WAI-ARIA logic: Errors must use role=\"alert\" to interrupt screen readers immediately.\n const role = toast.variant === 'error' ? 'alert' : 'status';\n\n return (\n <div\n className={cn(\n 'nui-toast',\n `nui-toast--${toast.variant || 'default'}`,\n )}\n data-state={isExiting ? 'closed' : 'open'}\n onMouseEnter={clearTimer}\n onMouseLeave={startTimer}\n role={role}\n >\n <div className=\"nui-toast__content\">\n <strong className=\"nui-toast__title\">{toast.message}</strong>\n {toast.description && (\n <p className=\"nui-toast__description\">{toast.description}</p>\n )}\n </div>\n\n <button\n type=\"button\"\n aria-label=\"Close notification\"\n className=\"nui-toast__close\"\n onClick={(e) => {\n e.preventDefault();\n clearTimer();\n triggerExit();\n }}\n >\n {/* Simple crisp SVG close icon */}\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n </button>\n </div>\n );\n}"],"names":["ToastContext","createContext","useToast","ctx","useContext","ToastProvider","children","toasts","setToasts","useState","isMounted","setIsMounted","useEffect","show","useCallback","message","options","id","prev","dismiss","t","remove","jsxs","Portal","jsx","toast","ToastItem","onRemove","isExiting","setIsExiting","timerRef","useRef","duration","triggerExit","startTimer","clearTimer","role","cn","e"],"mappings":";;;;;AAsCA,MAAMA,IAAeC,EAAwC,IAAI;AAE1D,SAASC,IAAW;AACzB,QAAMC,IAAMC,EAAWJ,CAAY;AACnC,MAAI,CAACG,EAAK,OAAM,IAAI,MAAM,gDAAgD;AAC1E,SAAOA;AACT;AAYO,SAASE,EAAc,EAAE,UAAAC,KAA2C;AACzE,QAAM,CAACC,GAAQC,CAAS,IAAIC,EAAsB,CAAA,CAAE,GAC9C,CAACC,GAAWC,CAAY,IAAIF,EAAS,EAAK;AAGhD,EAAAG,EAAU,MAAM;AACd,IAAAD,EAAa,EAAI;AAAA,EACnB,GAAG,CAAA,CAAE;AAEL,QAAME,IAAOC,EAAY,CAACC,GAA0BC,MAA2B;AAE7E,UAAMC,IAAK,OAAO,SAAW,OAAe,OAAO,aAC/C,OAAO,eACP,SAAS,KAAK,IAAA,CAAK,IAAI,KAAK,MAAM,KAAK,OAAA,IAAW,GAAK,CAAC;AAE5D,WAAAT,EAAU,CAACU,MAAS,CAAC,GAAGA,GAAM,EAAE,IAAAD,GAAI,SAAAF,GAAS,GAAGC,EAAA,CAAS,CAAC,GACnDC;AAAA,EACT,GAAG,CAAA,CAAE,GAECE,IAAUL,EAAY,CAACG,MAAe;AAG1C,IAAAT,EAAU,CAACU,MAASA,EAAK,IAAI,CAACE,MAAOA,EAAE,OAAOH,IAAK,EAAE,GAAGG,GAAG,WAAW,GAAA,IAASA,CAAE,CAAC;AAAA,EACpF,GAAG,CAAA,CAAE,GAECC,IAASP,EAAY,CAACG,MAAe;AACzC,IAAAT,EAAU,CAACU,MAASA,EAAK,OAAO,CAACE,MAAMA,EAAE,OAAOH,CAAE,CAAC;AAAA,EACrD,GAAG,CAAA,CAAE;AAEL,SACE,gBAAAK,EAACtB,EAAa,UAAb,EAAsB,OAAO,EAAE,MAAAa,GAAM,SAAAM,KACnC,UAAA;AAAA,IAAAb;AAAA,IAEAI,uBACEa,GAAA,EACC,UAAA,gBAAAC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV,aAAU;AAAA,QACV,eAAY;AAAA,QACZ,MAAK;AAAA,QACL,cAAW;AAAA,QAEV,UAAAjB,EAAO,IAAI,CAACkB,MACX,gBAAAD;AAAA,UAACE;AAAA,UAAA;AAAA,YAEC,OAAAD;AAAA,YACA,WAAW,MAAMN,EAAQM,EAAM,EAAE;AAAA,YACjC,UAAU,MAAMJ,EAAOI,EAAM,EAAE;AAAA,UAAA;AAAA,UAH1BA,EAAM;AAAA,QAAA,CAKd;AAAA,MAAA;AAAA,IAAA,EACH,CACF;AAAA,EAAA,GAEJ;AAEJ;AAYA,SAASC,EAAU,EAAE,OAAAD,GAAO,UAAAE,KAA4B;AACtD,QAAM,CAACC,GAAWC,CAAY,IAAIpB,EAAS,EAAK,GAC1CqB,IAAWC,EAA6C,IAAI,GAG5DC,IAAWP,EAAM,aAAa,SAAYA,EAAM,WAAW,KAG3DQ,IAAcnB,EAAY,MAAM;AACpC,IAAAe,EAAa,EAAI,GAEjB,WAAW,MAAM;AACf,MAAAF,EAAA;AAAA,IACF,GAAG,GAAG;AAAA,EACR,GAAG,CAACA,CAAQ,CAAC;AAGb,EAAAf,EAAU,MAAM;AACd,IAAIa,EAAM,aAAWQ,EAAA;AAAA,EACvB,GAAG,CAACR,EAAM,WAAWQ,CAAW,CAAC;AAGjC,QAAMC,IAAapB,EAAY,MAAM;AACnC,IAAIkB,MAAa,SAAYJ,MAC7BE,EAAS,UAAU,WAAW,MAAM;AAClC,MAAAG,EAAA;AAAA,IACF,GAAGD,CAAQ;AAAA,EACb,GAAG,CAACA,GAAUJ,GAAWK,CAAW,CAAC,GAE/BE,IAAarB,EAAY,MAAM;AACnC,IAAIgB,EAAS,WAAS,aAAaA,EAAS,OAAO;AAAA,EACrD,GAAG,CAAA,CAAE;AAGL,EAAAlB,EAAU,OACRsB,EAAA,GACOC,IACN,CAACD,GAAYC,CAAU,CAAC;AAI3B,QAAMC,IAAOX,EAAM,YAAY,UAAU,UAAU;AAEnD,SACE,gBAAAH;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWe;AAAA,QACT;AAAA,QACA,cAAcZ,EAAM,WAAW,SAAS;AAAA,MAAA;AAAA,MAE1C,cAAYG,IAAY,WAAW;AAAA,MACnC,cAAcO;AAAA,MACd,cAAcD;AAAA,MACd,MAAAE;AAAA,MAEA,UAAA;AAAA,QAAA,gBAAAd,EAAC,OAAA,EAAI,WAAU,sBACb,UAAA;AAAA,UAAA,gBAAAE,EAAC,UAAA,EAAO,WAAU,oBAAoB,UAAAC,EAAM,SAAQ;AAAA,UACnDA,EAAM,eACL,gBAAAD,EAAC,OAAE,WAAU,0BAA0B,YAAM,YAAA,CAAY;AAAA,QAAA,GAE7D;AAAA,QAEA,gBAAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAW;AAAA,YACX,WAAU;AAAA,YACV,SAAS,CAACc,MAAM;AACd,cAAAA,EAAE,eAAA,GACFH,EAAA,GACAF,EAAA;AAAA,YACF;AAAA,YAGA,4BAAC,OAAA,EAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QACzJ,UAAA;AAAA,cAAA,gBAAAT,EAAC,QAAA,EAAK,IAAG,MAAK,IAAG,KAAI,IAAG,KAAI,IAAG,KAAA,CAAK;AAAA,cACpC,gBAAAA,EAAC,UAAK,IAAG,KAAI,IAAG,KAAI,IAAG,MAAK,IAAG,KAAA,CAAK;AAAA,YAAA,EAAA,CACtC;AAAA,UAAA;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAAA;AAGN;"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("react/jsx-runtime"),t=require("react");;/* empty css */const X=require("../../utils/portal/portal.cjs"),Y=require("../../utils/cn/cn.cjs");function $({label:j,children:k,className:P,delay:h=200,offset:a=8}){const[c,g]=t.useState(!1),[p,q]=t.useState({top:0,left:0,arrowLeft:50}),[I,S]=t.useState("top"),w=t.useRef(null),m=t.useRef(null),r=t.useRef(null),E=`tooltip-${t.useId()}`,L=t.useCallback(()=>{r.current&&clearTimeout(r.current),r.current=setTimeout(()=>{g(!0)},h)},[h]),d=t.useCallback(()=>{r.current&&clearTimeout(r.current),g(!1)},[]),i=t.useCallback(()=>{if(!w.current||!m.current)return;const e=w.current.getBoundingClientRect(),o=m.current.getBoundingClientRect(),O=window.scrollX,v=window.scrollY,R=e.left+O+e.width/2;let l=R;const y=o.width/2+8,b=document.documentElement.clientWidth-o.width/2-8;l<y&&(l=y),l>b&&(l=b);const z=l-o.width/2;let u=R-z;const x=12,C=o.width-12;u<x&&(u=x),u>C&&(u=C);let M=e.top+v-o.height-a,T="top";e.top-o.height-a<0&&(M=e.bottom+v+a,T="bottom"),S(T),q({top:M,left:l,arrowLeft:u})},[a]);t.useLayoutEffect(()=>{if(c)return i(),window.addEventListener("scroll",i,!0),window.addEventListener("resize",i),()=>{window.removeEventListener("scroll",i,!0),window.removeEventListener("resize",i)}},[c,i]),t.useEffect(()=>{if(!c)return;const e=o=>{o.key==="Escape"&&d()};return document.addEventListener("keydown",e),()=>document.removeEventListener("keydown",e)},[c,d]),t.useEffect(()=>()=>{r.current&&clearTimeout(r.current)},[]);const n=t.Children.only(k),s=n.props.ref??n.ref,B={ref:e=>{e&&(w.current=e,typeof s=="function"?s(e):s&&typeof s=="object"&&"current"in s&&(s.current=e))},"aria-describedby":c?E:void 0,onMouseEnter:e=>{L(),n.props.onMouseEnter?.(e)},onMouseLeave:e=>{d(),n.props.onMouseLeave?.(e)},onFocus:e=>{L(),n.props.onFocus?.(e)},onBlur:e=>{d(),n.props.onBlur?.(e)}},F=t.cloneElement(n,B);return f.jsxs(f.Fragment,{children:[F,c&&f.jsx(X.Portal,{children:f.jsx("div",{ref:m,id:E,role:"tooltip","data-placement":I,className:Y.cn("nui-tooltip",P),style:{position:"absolute",top:p.top,left:p.left,"--nui-tooltip-arrow-x":`${p.arrowLeft}px`},children:j})})]})}exports.Tooltip=$;
|
|
2
2
|
//# sourceMappingURL=Tooltip.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tooltip.cjs","sources":["../../../src/components/tooltip/Tooltip.tsx"],"sourcesContent":["import React, {\r\n useState,\r\n useRef,\r\n useLayoutEffect,\r\n useEffect,\r\n useCallback,\r\n useId,\r\n} from 'react';\r\nimport './Tooltip.css';\r\nimport { Portal } from '../../utils/portal/portal';\r\n\r\ntype Placement = 'top' | 'bottom';\r\n\r\nexport interface TooltipProps {\r\n label: string;\r\n children: React.ReactNode;\r\n className?: string;\r\n delay?: number;\r\n offset?: number;\r\n}\r\n\r\nexport function Tooltip({\r\n label,\r\n children,\r\n className = '',\r\n delay = 200,\r\n offset = 8,\r\n}: TooltipProps) {\r\n const [open, setOpen] = useState(false);\r\n const [coords, setCoords] = useState({ top: 0, left: 0 });\r\n const [placement, setPlacement] = useState<Placement>('top');\r\n\r\n const triggerRef = useRef<HTMLSpanElement | null>(null);\r\n const tooltipRef = useRef<HTMLDivElement | null>(null);\r\nconst timeoutRef = useRef<number | null>(null);\r\n\r\n const id = `tooltip-${useId()}`;\r\n\r\n /* ---------------- Visibility ---------------- */\r\n\r\n const show = () => {\r\n timeoutRef.current = window.setTimeout(() => {\r\n setOpen(true);\r\n }, delay);\r\n };\r\n\r\n const hide = () => {\r\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\r\n setOpen(false);\r\n };\r\n\r\n /* ---------------- Positioning ---------------- */\r\n\r\n const updatePosition = useCallback(() => {\r\n if (!triggerRef.current || !tooltipRef.current) return;\r\n\r\n const triggerRect = triggerRef.current.getBoundingClientRect();\r\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\r\n\r\n const scrollX = window.scrollX;\r\n const scrollY = window.scrollY;\r\n\r\n const centerX =\r\n triggerRect.left + scrollX + triggerRect.width / 2;\r\n\r\n // Try top first\r\n let top =\r\n triggerRect.top +\r\n scrollY -\r\n tooltipRect.height -\r\n offset;\r\n\r\n let nextPlacement: Placement = 'top';\r\n\r\n // Flip to bottom if clipped\r\n if (top < scrollY + 8) {\r\n top =\r\n triggerRect.bottom +\r\n scrollY +\r\n offset;\r\n nextPlacement = 'bottom';\r\n }\r\n\r\n setPlacement(nextPlacement);\r\n setCoords({ top, left: centerX });\r\n }, [offset]);\r\n\r\n /* ---------------- Effects ---------------- */\r\n\r\n useLayoutEffect(() => {\r\n if (!open) return;\r\n\r\n updatePosition();\r\n\r\n window.addEventListener('scroll', updatePosition, true);\r\n window.addEventListener('resize', updatePosition);\r\n\r\n return () => {\r\n window.removeEventListener('scroll', updatePosition, true);\r\n window.removeEventListener('resize', updatePosition);\r\n };\r\n }, [open, updatePosition]);\r\n\r\n useEffect(() => {\r\n if (!open) return;\r\n\r\n const onKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape') hide();\r\n };\r\n\r\n document.addEventListener('keydown', onKeyDown);\r\n return () => document.removeEventListener('keydown', onKeyDown);\r\n }, [open]);\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\r\n };\r\n }, []);\r\n\r\n /* ---------------- Render ---------------- */\r\n\r\n return (\r\n <>\r\n <span\r\n ref={triggerRef}\r\n className=\"ui-tooltip-container\"\r\n aria-describedby={open ? id : undefined}\r\n onMouseEnter={show}\r\n onMouseLeave={hide}\r\n onFocus={show}\r\n onBlur={hide}\r\n tabIndex={0}\r\n >\r\n {children}\r\n </span>\r\n\r\n {open && (\r\n <Portal>\r\n <div\r\n ref={tooltipRef}\r\n id={id}\r\n role=\"tooltip\"\r\n data-placement={placement}\r\n className={`ui-tooltip-bubble ${className}`}\r\n style={{\r\n position: 'absolute',\r\n top: coords.top,\r\n left: coords.left,\r\n }}\r\n >\r\n {label}\r\n </div>\r\n </Portal>\r\n )}\r\n </>\r\n );\r\n}\r\n"],"names":["Tooltip","label","children","className","delay","offset","open","setOpen","useState","coords","setCoords","placement","setPlacement","triggerRef","useRef","tooltipRef","timeoutRef","id","useId","show","hide","updatePosition","useCallback","triggerRect","tooltipRect","scrollX","scrollY","centerX","top","nextPlacement","useLayoutEffect","useEffect","onKeyDown","e","jsxs","Fragment","jsx","Portal"],"mappings":"sNAqBO,SAASA,EAAQ,CACtB,MAAAC,EACA,SAAAC,EACA,UAAAC,EAAY,GACZ,MAAAC,EAAQ,IACR,OAAAC,EAAS,CACX,EAAiB,CACf,KAAM,CAACC,EAAMC,CAAO,EAAIC,EAAAA,SAAS,EAAK,EAChC,CAACC,EAAQC,CAAS,EAAIF,EAAAA,SAAS,CAAE,IAAK,EAAG,KAAM,EAAG,EAClD,CAACG,EAAWC,CAAY,EAAIJ,EAAAA,SAAoB,KAAK,EAErDK,EAAaC,EAAAA,OAA+B,IAAI,EAChDC,EAAaD,EAAAA,OAA8B,IAAI,EACjDE,EAAaF,EAAAA,OAAsB,IAAI,EAErCG,EAAK,WAAWC,EAAAA,MAAA,CAAO,GAIvBC,EAAO,IAAM,CACjBH,EAAW,QAAU,OAAO,WAAW,IAAM,CAC3CT,EAAQ,EAAI,CACd,EAAGH,CAAK,CACV,EAEMgB,EAAO,IAAM,CACbJ,EAAW,SAAS,aAAaA,EAAW,OAAO,EACvDT,EAAQ,EAAK,CACf,EAIMc,EAAiBC,EAAAA,YAAY,IAAM,CACvC,GAAI,CAACT,EAAW,SAAW,CAACE,EAAW,QAAS,OAEhD,MAAMQ,EAAcV,EAAW,QAAQ,sBAAA,EACjCW,EAAcT,EAAW,QAAQ,sBAAA,EAEjCU,EAAU,OAAO,QACjBC,EAAU,OAAO,QAEjBC,EACJJ,EAAY,KAAOE,EAAUF,EAAY,MAAQ,EAGnD,IAAIK,EACFL,EAAY,IACZG,EACAF,EAAY,OACZnB,EAEEwB,EAA2B,MAG3BD,EAAMF,EAAU,IAClBE,EACEL,EAAY,OACZG,EACArB,EACFwB,EAAgB,UAGlBjB,EAAaiB,CAAa,EAC1BnB,EAAU,CAAE,IAAAkB,EAAK,KAAMD,CAAA,CAAS,CAClC,EAAG,CAACtB,CAAM,CAAC,EAIXyB,OAAAA,EAAAA,gBAAgB,IAAM,CACpB,GAAKxB,EAEL,OAAAe,EAAA,EAEA,OAAO,iBAAiB,SAAUA,EAAgB,EAAI,EACtD,OAAO,iBAAiB,SAAUA,CAAc,EAEzC,IAAM,CACX,OAAO,oBAAoB,SAAUA,EAAgB,EAAI,EACzD,OAAO,oBAAoB,SAAUA,CAAc,CACrD,CACF,EAAG,CAACf,EAAMe,CAAc,CAAC,EAEzBU,EAAAA,UAAU,IAAM,CACd,GAAI,CAACzB,EAAM,OAEX,MAAM0B,EAAaC,GAAqB,CAClCA,EAAE,MAAQ,UAAUb,EAAA,CAC1B,EAEA,gBAAS,iBAAiB,UAAWY,CAAS,EACvC,IAAM,SAAS,oBAAoB,UAAWA,CAAS,CAChE,EAAG,CAAC1B,CAAI,CAAC,EAETyB,EAAAA,UAAU,IACD,IAAM,CACPf,EAAW,SAAS,aAAaA,EAAW,OAAO,CACzD,EACC,CAAA,CAAE,EAKHkB,EAAAA,KAAAC,WAAA,CACE,SAAA,CAAAC,EAAAA,IAAC,OAAA,CACC,IAAKvB,EACL,UAAU,uBACV,mBAAkBP,EAAOW,EAAK,OAC9B,aAAcE,EACd,aAAcC,EACd,QAASD,EACT,OAAQC,EACR,SAAU,EAET,SAAAlB,CAAA,CAAA,EAGFI,SACE+B,SAAA,CACC,SAAAD,EAAAA,IAAC,MAAA,CACC,IAAKrB,EACL,GAAAE,EACA,KAAK,UACL,iBAAgBN,EAChB,UAAW,qBAAqBR,CAAS,GACzC,MAAO,CACL,SAAU,WACV,IAAKM,EAAO,IACZ,KAAMA,EAAO,IAAA,EAGd,SAAAR,CAAA,CAAA,CACH,CACF,CAAA,EAEJ,CAEJ"}
|
|
1
|
+
{"version":3,"file":"Tooltip.cjs","sources":["../../../src/components/tooltip/Tooltip.tsx"],"sourcesContent":["\"use client\";\n\nimport React, {\n useState,\n useRef,\n useLayoutEffect,\n useEffect,\n useCallback,\n useId,\n} from 'react';\nimport { cn } from '../../utils';\nimport { Portal } from '../../utils';\nimport './Tooltip.css';\n\n/* ============================================================\n * Types\n * ============================================================ */\n\nexport type TooltipPlacement = 'top' | 'bottom';\n\nexport interface TooltipProps {\n /** The text or content displayed inside the tooltip */\n label: React.ReactNode;\n /** The trigger element. Must be a single valid React Element (like a button) */\n children: React.ReactElement;\n /** Additional CSS classes for the tooltip container */\n className?: string;\n /** Delay in milliseconds before showing the tooltip on hover/focus. Defaults to 200. */\n delay?: number;\n /** Distance in pixels between the tooltip and the trigger. Defaults to 8. */\n offset?: number;\n}\n\n/* ============================================================\n * Component\n * ============================================================ */\n\n/**\n * Tooltip Component\n * * A contextual popup that displays information when hovering or focusing an element.\n * * Uses a custom math engine to calculate collision-safe positioning and dynamic arrow rendering.\n */\nexport function Tooltip({\n label,\n children,\n className,\n delay = 200,\n offset = 8,\n}: TooltipProps) {\n const [isOpen, setIsOpen] = useState(false);\n // ArrowLeft is injected as a CSS custom property to slide the pseudo-element arrow!\n const [coords, setCoords] = useState({ top: 0, left: 0, arrowLeft: 50 });\n const [placement, setPlacement] = useState<TooltipPlacement>('top');\n\n const triggerRef = useRef<HTMLElement>(null);\n const tooltipRef = useRef<HTMLDivElement>(null);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const reactId = useId();\n const tooltipId = `tooltip-${reactId}`;\n\n /* ----------------------------------------------------\n Visibility\n ---------------------------------------------------- */\n const show = useCallback(() => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => {\n setIsOpen(true);\n }, delay);\n }, [delay]);\n\n const hide = useCallback(() => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n setIsOpen(false);\n }, []);\n\n /* ----------------------------------------------------\n Positioning Engine\n ---------------------------------------------------- */\n const updatePosition = useCallback(() => {\n if (!triggerRef.current || !tooltipRef.current) return;\n\n const triggerRect = triggerRef.current.getBoundingClientRect();\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\n\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n // 1. Calculate ideal X (Center of the trigger)\n const actualCenter = triggerRect.left + scrollX + triggerRect.width / 2;\n let left = actualCenter;\n \n // Prevent bleeding off left/right edges of the viewport\n const minLeft = tooltipRect.width / 2 + 8;\n const maxLeft = document.documentElement.clientWidth - (tooltipRect.width / 2) - 8;\n if (left < minLeft) left = minLeft;\n if (left > maxLeft) left = maxLeft;\n\n // 2. Arrow offset math (Keep arrow pointing at trigger even if the box got clamped)\n const physicalLeftEdge = left - (tooltipRect.width / 2);\n let arrowLeft = actualCenter - physicalLeftEdge;\n\n // Clamp arrow so it doesn't break out of the rounded corners of the tooltip box\n const arrowMin = 12;\n const arrowMax = tooltipRect.width - 12;\n if (arrowLeft < arrowMin) arrowLeft = arrowMin;\n if (arrowLeft > arrowMax) arrowLeft = arrowMax;\n\n // 3. Calculate Y\n let top = triggerRect.top + scrollY - tooltipRect.height - offset;\n let nextPlacement: TooltipPlacement = 'top';\n\n // Flip to bottom if there is no space at the top of the viewport\n if (triggerRect.top - tooltipRect.height - offset < 0) {\n top = triggerRect.bottom + scrollY + offset;\n nextPlacement = 'bottom';\n }\n\n setPlacement(nextPlacement);\n setCoords({ top, left, arrowLeft });\n }, [offset]);\n\n /* ----------------------------------------------------\n Effects\n ---------------------------------------------------- */\n useLayoutEffect(() => {\n if (!isOpen) return;\n updatePosition();\n window.addEventListener('scroll', updatePosition, true);\n window.addEventListener('resize', updatePosition);\n return () => {\n window.removeEventListener('scroll', updatePosition, true);\n window.removeEventListener('resize', updatePosition);\n };\n }, [isOpen, updatePosition]);\n\n useEffect(() => {\n if (!isOpen) return;\n const onKeyDown = (e: KeyboardEvent) => {\n // WAI-ARIA states Tooltips must close on Escape\n if (e.key === 'Escape') hide();\n };\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, [isOpen, hide]);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n };\n }, []);\n\n /* ----------------------------------------------------\n Render\n ---------------------------------------------------- */\n \n // 1. Extract the child and type it to include ALL HTML props + Ref attributes\n const child = React.Children.only(children) as React.ReactElement<React.HTMLProps<HTMLElement>>;\n\n // 2. Safely extract the original ref (Handles both React 18 element refs and React 19 prop refs)\n const childRef = child.props.ref ?? (child as unknown as { ref?: React.Ref<HTMLElement> }).ref;\n\n // 3. Explicitly type the new props object so TypeScript knows 'ref' is allowed\n const triggerProps: React.HTMLProps<HTMLElement> = {\n ref: (node: HTMLElement | null) => {\n if (!node) return;\n \n // Assign to our internal ref for positioning math\n triggerRef.current = node;\n \n // Preserve user's ref if they passed one originally\n if (typeof childRef === 'function') {\n childRef(node);\n } else if (childRef && typeof childRef === 'object' && 'current' in childRef) {\n (childRef as { current: HTMLElement | null }).current = node;\n }\n },\n 'aria-describedby': isOpen ? tooltipId : undefined,\n onMouseEnter: (e: React.MouseEvent<HTMLElement>) => {\n show();\n child.props.onMouseEnter?.(e);\n },\n onMouseLeave: (e: React.MouseEvent<HTMLElement>) => {\n hide();\n child.props.onMouseLeave?.(e);\n },\n onFocus: (e: React.FocusEvent<HTMLElement>) => {\n show();\n child.props.onFocus?.(e);\n },\n onBlur: (e: React.FocusEvent<HTMLElement>) => {\n hide();\n child.props.onBlur?.(e);\n },\n };\n\n // 4. Clone the child and inject the event listeners and refs\n const trigger = React.cloneElement(child, triggerProps);\n\n return (\n <>\n {trigger}\n\n {isOpen && (\n <Portal>\n <div\n ref={tooltipRef}\n id={tooltipId}\n role=\"tooltip\"\n data-placement={placement}\n className={cn('nui-tooltip', className)}\n style={{\n position: 'absolute',\n top: coords.top,\n left: coords.left,\n // Pass the arrow position dynamically to CSS!\n '--nui-tooltip-arrow-x': `${coords.arrowLeft}px`,\n } as React.CSSProperties}\n >\n {label}\n </div>\n </Portal>\n )}\n </>\n );\n}"],"names":["Tooltip","label","children","className","delay","offset","isOpen","setIsOpen","useState","coords","setCoords","placement","setPlacement","triggerRef","useRef","tooltipRef","timeoutRef","tooltipId","useId","show","useCallback","hide","updatePosition","triggerRect","tooltipRect","scrollX","scrollY","actualCenter","left","minLeft","maxLeft","physicalLeftEdge","arrowLeft","arrowMin","arrowMax","top","nextPlacement","useLayoutEffect","useEffect","onKeyDown","e","child","React","childRef","triggerProps","node","trigger","jsxs","Fragment","Portal","jsx","cn"],"mappings":"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"}
|
|
@@ -1,73 +1,89 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useState as
|
|
1
|
+
import { jsxs as S, Fragment as W, jsx as T } from "react/jsx-runtime";
|
|
2
|
+
import k, { useState as m, useRef as w, useId as q, useCallback as h, useLayoutEffect as A, useEffect as B } from "react";
|
|
3
3
|
/* empty css */
|
|
4
|
-
import { Portal as
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
import { Portal as G } from "../../utils/portal/portal.js";
|
|
5
|
+
import { cn as H } from "../../utils/cn/cn.js";
|
|
6
|
+
function tt({
|
|
7
|
+
label: j,
|
|
8
|
+
children: F,
|
|
9
|
+
className: z,
|
|
10
|
+
delay: g = 200,
|
|
11
|
+
offset: u = 8
|
|
11
12
|
}) {
|
|
12
|
-
const [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
|
|
18
|
-
},
|
|
19
|
-
if (!
|
|
20
|
-
const
|
|
21
|
-
let
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
13
|
+
const [n, L] = m(!1), [d, O] = m({ top: 0, left: 0, arrowLeft: 50 }), [X, Y] = m("top"), f = w(null), a = w(null), o = w(null), E = `tooltip-${q()}`, v = h(() => {
|
|
14
|
+
o.current && clearTimeout(o.current), o.current = setTimeout(() => {
|
|
15
|
+
L(!0);
|
|
16
|
+
}, g);
|
|
17
|
+
}, [g]), p = h(() => {
|
|
18
|
+
o.current && clearTimeout(o.current), L(!1);
|
|
19
|
+
}, []), i = h(() => {
|
|
20
|
+
if (!f.current || !a.current) return;
|
|
21
|
+
const t = f.current.getBoundingClientRect(), e = a.current.getBoundingClientRect(), K = window.scrollX, y = window.scrollY, R = t.left + K + t.width / 2;
|
|
22
|
+
let s = R;
|
|
23
|
+
const x = e.width / 2 + 8, b = document.documentElement.clientWidth - e.width / 2 - 8;
|
|
24
|
+
s < x && (s = x), s > b && (s = b);
|
|
25
|
+
const N = s - e.width / 2;
|
|
26
|
+
let l = R - N;
|
|
27
|
+
const C = 12, M = e.width - 12;
|
|
28
|
+
l < C && (l = C), l > M && (l = M);
|
|
29
|
+
let I = t.top + y - e.height - u, P = "top";
|
|
30
|
+
t.top - e.height - u < 0 && (I = t.bottom + y + u, P = "bottom"), Y(P), O({ top: I, left: s, arrowLeft: l });
|
|
31
|
+
}, [u]);
|
|
32
|
+
A(() => {
|
|
33
|
+
if (n)
|
|
34
|
+
return i(), window.addEventListener("scroll", i, !0), window.addEventListener("resize", i), () => {
|
|
35
|
+
window.removeEventListener("scroll", i, !0), window.removeEventListener("resize", i);
|
|
28
36
|
};
|
|
29
|
-
}, [
|
|
30
|
-
if (!
|
|
31
|
-
const
|
|
32
|
-
|
|
37
|
+
}, [n, i]), B(() => {
|
|
38
|
+
if (!n) return;
|
|
39
|
+
const t = (e) => {
|
|
40
|
+
e.key === "Escape" && p();
|
|
33
41
|
};
|
|
34
|
-
return document.addEventListener("keydown",
|
|
35
|
-
}, [
|
|
36
|
-
|
|
37
|
-
}, [])
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
t
|
|
42
|
+
return document.addEventListener("keydown", t), () => document.removeEventListener("keydown", t);
|
|
43
|
+
}, [n, p]), B(() => () => {
|
|
44
|
+
o.current && clearTimeout(o.current);
|
|
45
|
+
}, []);
|
|
46
|
+
const r = k.Children.only(F), c = r.props.ref ?? r.ref, $ = {
|
|
47
|
+
ref: (t) => {
|
|
48
|
+
t && (f.current = t, typeof c == "function" ? c(t) : c && typeof c == "object" && "current" in c && (c.current = t));
|
|
49
|
+
},
|
|
50
|
+
"aria-describedby": n ? E : void 0,
|
|
51
|
+
onMouseEnter: (t) => {
|
|
52
|
+
v(), r.props.onMouseEnter?.(t);
|
|
53
|
+
},
|
|
54
|
+
onMouseLeave: (t) => {
|
|
55
|
+
p(), r.props.onMouseLeave?.(t);
|
|
56
|
+
},
|
|
57
|
+
onFocus: (t) => {
|
|
58
|
+
v(), r.props.onFocus?.(t);
|
|
59
|
+
},
|
|
60
|
+
onBlur: (t) => {
|
|
61
|
+
p(), r.props.onBlur?.(t);
|
|
62
|
+
}
|
|
63
|
+
}, D = k.cloneElement(r, $);
|
|
64
|
+
return /* @__PURE__ */ S(W, { children: [
|
|
65
|
+
D,
|
|
66
|
+
n && /* @__PURE__ */ T(G, { children: /* @__PURE__ */ T(
|
|
53
67
|
"div",
|
|
54
68
|
{
|
|
55
|
-
ref:
|
|
56
|
-
id:
|
|
69
|
+
ref: a,
|
|
70
|
+
id: E,
|
|
57
71
|
role: "tooltip",
|
|
58
|
-
"data-placement":
|
|
59
|
-
className:
|
|
72
|
+
"data-placement": X,
|
|
73
|
+
className: H("nui-tooltip", z),
|
|
60
74
|
style: {
|
|
61
75
|
position: "absolute",
|
|
62
|
-
top:
|
|
63
|
-
left:
|
|
76
|
+
top: d.top,
|
|
77
|
+
left: d.left,
|
|
78
|
+
// Pass the arrow position dynamically to CSS!
|
|
79
|
+
"--nui-tooltip-arrow-x": `${d.arrowLeft}px`
|
|
64
80
|
},
|
|
65
|
-
children:
|
|
81
|
+
children: j
|
|
66
82
|
}
|
|
67
83
|
) })
|
|
68
84
|
] });
|
|
69
85
|
}
|
|
70
86
|
export {
|
|
71
|
-
|
|
87
|
+
tt as Tooltip
|
|
72
88
|
};
|
|
73
89
|
//# sourceMappingURL=Tooltip.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tooltip.js","sources":["../../../src/components/tooltip/Tooltip.tsx"],"sourcesContent":["import React, {\r\n useState,\r\n useRef,\r\n useLayoutEffect,\r\n useEffect,\r\n useCallback,\r\n useId,\r\n} from 'react';\r\nimport './Tooltip.css';\r\nimport { Portal } from '../../utils/portal/portal';\r\n\r\ntype Placement = 'top' | 'bottom';\r\n\r\nexport interface TooltipProps {\r\n label: string;\r\n children: React.ReactNode;\r\n className?: string;\r\n delay?: number;\r\n offset?: number;\r\n}\r\n\r\nexport function Tooltip({\r\n label,\r\n children,\r\n className = '',\r\n delay = 200,\r\n offset = 8,\r\n}: TooltipProps) {\r\n const [open, setOpen] = useState(false);\r\n const [coords, setCoords] = useState({ top: 0, left: 0 });\r\n const [placement, setPlacement] = useState<Placement>('top');\r\n\r\n const triggerRef = useRef<HTMLSpanElement | null>(null);\r\n const tooltipRef = useRef<HTMLDivElement | null>(null);\r\nconst timeoutRef = useRef<number | null>(null);\r\n\r\n const id = `tooltip-${useId()}`;\r\n\r\n /* ---------------- Visibility ---------------- */\r\n\r\n const show = () => {\r\n timeoutRef.current = window.setTimeout(() => {\r\n setOpen(true);\r\n }, delay);\r\n };\r\n\r\n const hide = () => {\r\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\r\n setOpen(false);\r\n };\r\n\r\n /* ---------------- Positioning ---------------- */\r\n\r\n const updatePosition = useCallback(() => {\r\n if (!triggerRef.current || !tooltipRef.current) return;\r\n\r\n const triggerRect = triggerRef.current.getBoundingClientRect();\r\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\r\n\r\n const scrollX = window.scrollX;\r\n const scrollY = window.scrollY;\r\n\r\n const centerX =\r\n triggerRect.left + scrollX + triggerRect.width / 2;\r\n\r\n // Try top first\r\n let top =\r\n triggerRect.top +\r\n scrollY -\r\n tooltipRect.height -\r\n offset;\r\n\r\n let nextPlacement: Placement = 'top';\r\n\r\n // Flip to bottom if clipped\r\n if (top < scrollY + 8) {\r\n top =\r\n triggerRect.bottom +\r\n scrollY +\r\n offset;\r\n nextPlacement = 'bottom';\r\n }\r\n\r\n setPlacement(nextPlacement);\r\n setCoords({ top, left: centerX });\r\n }, [offset]);\r\n\r\n /* ---------------- Effects ---------------- */\r\n\r\n useLayoutEffect(() => {\r\n if (!open) return;\r\n\r\n updatePosition();\r\n\r\n window.addEventListener('scroll', updatePosition, true);\r\n window.addEventListener('resize', updatePosition);\r\n\r\n return () => {\r\n window.removeEventListener('scroll', updatePosition, true);\r\n window.removeEventListener('resize', updatePosition);\r\n };\r\n }, [open, updatePosition]);\r\n\r\n useEffect(() => {\r\n if (!open) return;\r\n\r\n const onKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape') hide();\r\n };\r\n\r\n document.addEventListener('keydown', onKeyDown);\r\n return () => document.removeEventListener('keydown', onKeyDown);\r\n }, [open]);\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\r\n };\r\n }, []);\r\n\r\n /* ---------------- Render ---------------- */\r\n\r\n return (\r\n <>\r\n <span\r\n ref={triggerRef}\r\n className=\"ui-tooltip-container\"\r\n aria-describedby={open ? id : undefined}\r\n onMouseEnter={show}\r\n onMouseLeave={hide}\r\n onFocus={show}\r\n onBlur={hide}\r\n tabIndex={0}\r\n >\r\n {children}\r\n </span>\r\n\r\n {open && (\r\n <Portal>\r\n <div\r\n ref={tooltipRef}\r\n id={id}\r\n role=\"tooltip\"\r\n data-placement={placement}\r\n className={`ui-tooltip-bubble ${className}`}\r\n style={{\r\n position: 'absolute',\r\n top: coords.top,\r\n left: coords.left,\r\n }}\r\n >\r\n {label}\r\n </div>\r\n </Portal>\r\n )}\r\n </>\r\n );\r\n}\r\n"],"names":["Tooltip","label","children","className","delay","offset","open","setOpen","useState","coords","setCoords","placement","setPlacement","triggerRef","useRef","tooltipRef","timeoutRef","id","useId","show","hide","updatePosition","useCallback","triggerRect","tooltipRect","scrollX","scrollY","centerX","top","nextPlacement","useLayoutEffect","useEffect","onKeyDown","e","jsxs","Fragment","jsx","Portal"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Tooltip.js","sources":["../../../src/components/tooltip/Tooltip.tsx"],"sourcesContent":["\"use client\";\n\nimport React, {\n useState,\n useRef,\n useLayoutEffect,\n useEffect,\n useCallback,\n useId,\n} from 'react';\nimport { cn } from '../../utils';\nimport { Portal } from '../../utils';\nimport './Tooltip.css';\n\n/* ============================================================\n * Types\n * ============================================================ */\n\nexport type TooltipPlacement = 'top' | 'bottom';\n\nexport interface TooltipProps {\n /** The text or content displayed inside the tooltip */\n label: React.ReactNode;\n /** The trigger element. Must be a single valid React Element (like a button) */\n children: React.ReactElement;\n /** Additional CSS classes for the tooltip container */\n className?: string;\n /** Delay in milliseconds before showing the tooltip on hover/focus. Defaults to 200. */\n delay?: number;\n /** Distance in pixels between the tooltip and the trigger. Defaults to 8. */\n offset?: number;\n}\n\n/* ============================================================\n * Component\n * ============================================================ */\n\n/**\n * Tooltip Component\n * * A contextual popup that displays information when hovering or focusing an element.\n * * Uses a custom math engine to calculate collision-safe positioning and dynamic arrow rendering.\n */\nexport function Tooltip({\n label,\n children,\n className,\n delay = 200,\n offset = 8,\n}: TooltipProps) {\n const [isOpen, setIsOpen] = useState(false);\n // ArrowLeft is injected as a CSS custom property to slide the pseudo-element arrow!\n const [coords, setCoords] = useState({ top: 0, left: 0, arrowLeft: 50 });\n const [placement, setPlacement] = useState<TooltipPlacement>('top');\n\n const triggerRef = useRef<HTMLElement>(null);\n const tooltipRef = useRef<HTMLDivElement>(null);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const reactId = useId();\n const tooltipId = `tooltip-${reactId}`;\n\n /* ----------------------------------------------------\n Visibility\n ---------------------------------------------------- */\n const show = useCallback(() => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => {\n setIsOpen(true);\n }, delay);\n }, [delay]);\n\n const hide = useCallback(() => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n setIsOpen(false);\n }, []);\n\n /* ----------------------------------------------------\n Positioning Engine\n ---------------------------------------------------- */\n const updatePosition = useCallback(() => {\n if (!triggerRef.current || !tooltipRef.current) return;\n\n const triggerRect = triggerRef.current.getBoundingClientRect();\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\n\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n // 1. Calculate ideal X (Center of the trigger)\n const actualCenter = triggerRect.left + scrollX + triggerRect.width / 2;\n let left = actualCenter;\n \n // Prevent bleeding off left/right edges of the viewport\n const minLeft = tooltipRect.width / 2 + 8;\n const maxLeft = document.documentElement.clientWidth - (tooltipRect.width / 2) - 8;\n if (left < minLeft) left = minLeft;\n if (left > maxLeft) left = maxLeft;\n\n // 2. Arrow offset math (Keep arrow pointing at trigger even if the box got clamped)\n const physicalLeftEdge = left - (tooltipRect.width / 2);\n let arrowLeft = actualCenter - physicalLeftEdge;\n\n // Clamp arrow so it doesn't break out of the rounded corners of the tooltip box\n const arrowMin = 12;\n const arrowMax = tooltipRect.width - 12;\n if (arrowLeft < arrowMin) arrowLeft = arrowMin;\n if (arrowLeft > arrowMax) arrowLeft = arrowMax;\n\n // 3. Calculate Y\n let top = triggerRect.top + scrollY - tooltipRect.height - offset;\n let nextPlacement: TooltipPlacement = 'top';\n\n // Flip to bottom if there is no space at the top of the viewport\n if (triggerRect.top - tooltipRect.height - offset < 0) {\n top = triggerRect.bottom + scrollY + offset;\n nextPlacement = 'bottom';\n }\n\n setPlacement(nextPlacement);\n setCoords({ top, left, arrowLeft });\n }, [offset]);\n\n /* ----------------------------------------------------\n Effects\n ---------------------------------------------------- */\n useLayoutEffect(() => {\n if (!isOpen) return;\n updatePosition();\n window.addEventListener('scroll', updatePosition, true);\n window.addEventListener('resize', updatePosition);\n return () => {\n window.removeEventListener('scroll', updatePosition, true);\n window.removeEventListener('resize', updatePosition);\n };\n }, [isOpen, updatePosition]);\n\n useEffect(() => {\n if (!isOpen) return;\n const onKeyDown = (e: KeyboardEvent) => {\n // WAI-ARIA states Tooltips must close on Escape\n if (e.key === 'Escape') hide();\n };\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, [isOpen, hide]);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n };\n }, []);\n\n /* ----------------------------------------------------\n Render\n ---------------------------------------------------- */\n \n // 1. Extract the child and type it to include ALL HTML props + Ref attributes\n const child = React.Children.only(children) as React.ReactElement<React.HTMLProps<HTMLElement>>;\n\n // 2. Safely extract the original ref (Handles both React 18 element refs and React 19 prop refs)\n const childRef = child.props.ref ?? (child as unknown as { ref?: React.Ref<HTMLElement> }).ref;\n\n // 3. Explicitly type the new props object so TypeScript knows 'ref' is allowed\n const triggerProps: React.HTMLProps<HTMLElement> = {\n ref: (node: HTMLElement | null) => {\n if (!node) return;\n \n // Assign to our internal ref for positioning math\n triggerRef.current = node;\n \n // Preserve user's ref if they passed one originally\n if (typeof childRef === 'function') {\n childRef(node);\n } else if (childRef && typeof childRef === 'object' && 'current' in childRef) {\n (childRef as { current: HTMLElement | null }).current = node;\n }\n },\n 'aria-describedby': isOpen ? tooltipId : undefined,\n onMouseEnter: (e: React.MouseEvent<HTMLElement>) => {\n show();\n child.props.onMouseEnter?.(e);\n },\n onMouseLeave: (e: React.MouseEvent<HTMLElement>) => {\n hide();\n child.props.onMouseLeave?.(e);\n },\n onFocus: (e: React.FocusEvent<HTMLElement>) => {\n show();\n child.props.onFocus?.(e);\n },\n onBlur: (e: React.FocusEvent<HTMLElement>) => {\n hide();\n child.props.onBlur?.(e);\n },\n };\n\n // 4. Clone the child and inject the event listeners and refs\n const trigger = React.cloneElement(child, triggerProps);\n\n return (\n <>\n {trigger}\n\n {isOpen && (\n <Portal>\n <div\n ref={tooltipRef}\n id={tooltipId}\n role=\"tooltip\"\n data-placement={placement}\n className={cn('nui-tooltip', className)}\n style={{\n position: 'absolute',\n top: coords.top,\n left: coords.left,\n // Pass the arrow position dynamically to CSS!\n '--nui-tooltip-arrow-x': `${coords.arrowLeft}px`,\n } as React.CSSProperties}\n >\n {label}\n </div>\n </Portal>\n )}\n </>\n );\n}"],"names":["Tooltip","label","children","className","delay","offset","isOpen","setIsOpen","useState","coords","setCoords","placement","setPlacement","triggerRef","useRef","tooltipRef","timeoutRef","tooltipId","useId","show","useCallback","hide","updatePosition","triggerRect","tooltipRect","scrollX","scrollY","actualCenter","left","minLeft","maxLeft","physicalLeftEdge","arrowLeft","arrowMin","arrowMax","top","nextPlacement","useLayoutEffect","useEffect","onKeyDown","child","React","childRef","triggerProps","node","e","trigger","jsxs","Fragment","Portal","jsx","cn"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("react/jsx-runtime"),o=require("react");;/* empty css */const h=require("../../utils/cn/cn.cjs"),w=o.forwardRef(({data:m,selectedId:k,defaultExpandedIds:j=[],onSelect:f,className:y,...A},E)=>{const[p,N]=o.useState(new Set(j)),b=o.useRef(null),I=`nui-tree-${o.useId().replace(/:/g,"")}`,c=o.useCallback((t,r)=>{r?.stopPropagation(),N(d=>{const e=new Set(d);return e.has(t)?e.delete(t):e.add(t),e})},[]),T=o.useCallback((t,r,d)=>{["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End"," ","Enter"].includes(t.key)&&t.preventDefault(),t.stopPropagation();const e=t.currentTarget,l=p.has(r.id),n=!!r.children?.length,i=b.current?Array.from(b.current.querySelectorAll('[role="treeitem"]:not([aria-disabled="true"])')):[],u=i.indexOf(e);switch(t.key){case"Enter":case" ":if(r.disabled)return;n&&c(r.id),f?.(r.id,r);break;case"ArrowRight":n&&!l?c(r.id):n&&l&&i[u+1]?.focus();break;case"ArrowLeft":n&&l?c(r.id):d&&i.find(v=>v.id.endsWith(`-${d.id}`))?.focus();break;case"ArrowDown":i[u+1]?.focus();break;case"ArrowUp":i[u-1]?.focus();break;case"Home":i[0]?.focus();break;case"End":i[i.length-1]?.focus();break}},[p,c,f]),g=(t,r=1,d)=>s.jsx("ul",{role:r===1?"tree":"group",className:h.cn("nui-tree",r>1&&"nui-tree--nested"),ref:r===1?b:void 0,children:t.map((e,l)=>{const n=p.has(e.id),x=k===e.id,i=!!e.children?.length,u=r===1&&l===0;return s.jsxs("li",{id:`${I}-${e.id}`,role:"treeitem","aria-expanded":i?n:void 0,"aria-selected":x,"aria-disabled":e.disabled,tabIndex:u?0:-1,className:h.cn("nui-tree-item",x&&"nui-tree-item--selected",e.disabled&&"nui-tree-item--disabled"),onKeyDown:a=>T(a,e,d),onClick:a=>{a.stopPropagation(),!e.disabled&&(a.currentTarget.focus(),i&&c(e.id),f?.(e.id,e))},children:[s.jsxs("div",{className:"nui-tree-item-content",style:{paddingLeft:`${(r-1)*16}px`},children:[s.jsx("span",{className:h.cn("nui-tree-chevron",!i&&"nui-tree-chevron--hidden"),onClick:a=>{i&&c(e.id,a)},children:s.jsx("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",className:h.cn("nui-tree-chevron-icon",n&&"nui-tree-chevron-icon--open"),"aria-hidden":"true",children:s.jsx("polyline",{points:"9 18 15 12 9 6"})})}),e.icon&&s.jsx("span",{className:"nui-tree-icon","aria-hidden":"true",children:e.icon}),s.jsx("span",{className:"nui-tree-label",children:e.label})]}),i&&n&&e.children&&g(e.children,r+1,e)]},e.id)})});return s.jsx("div",{ref:E,className:h.cn("nui-tree-wrapper",y),...A,children:g(m)})});w.displayName="TreeView";exports.TreeView=w;
|
|
2
2
|
//# sourceMappingURL=TreeView.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TreeView.cjs","sources":["../../../src/components/treeview/TreeView.tsx"],"sourcesContent":["/**\r\n * TreeView.tsx — FINAL VERSION\r\n * ----------------------------\r\n * Features:\r\n * - Expand/collapse\r\n * - Recursive rendering\r\n * - Arrow key navigation\r\n * - Enter/Space to select\r\n * - ARIA TreeView roles\r\n * - Value controlled OR internal state\r\n */\r\n\r\nimport React, { useState } from 'react';\r\nimport './TreeView.css';\r\n\r\nexport interface TreeNode {\r\n id: string;\r\n label: string;\r\n icon?: React.ReactNode;\r\n children?: TreeNode[];\r\n}\r\n\r\ninterface TreeViewProps {\r\n data: TreeNode[];\r\n selectedId?: string;\r\n onSelect?: (id: string) => void;\r\n className?: string;\r\n}\r\n\r\nexport function TreeView({\r\n data,\r\n selectedId,\r\n onSelect,\r\n className = '',\r\n}: TreeViewProps) {\r\n const [openNodes, setOpenNodes] = useState<Set<string>>(new Set());\r\n const [activeNode, setActiveNode] = useState<string | null>(null);\r\n\r\n const toggleNode = (id: string) => {\r\n setOpenNodes((prev) => {\r\n const next = new Set(prev);\r\n if (next.has(id)) next.delete(id);\r\n else next.add(id);\r\n return next;\r\n });\r\n };\r\n\r\n const flatten = (nodes: TreeNode[], acc: TreeNode[] = []): TreeNode[] => {\r\n nodes.forEach((node) => {\r\n acc.push(node);\r\n\r\n const isOpen = openNodes.has(node.id);\r\n if (node.children && node.children.length > 0 && isOpen) {\r\n flatten(node.children, acc);\r\n }\r\n });\r\n return acc;\r\n };\r\n\r\n const handleKeyDown = (\r\n e: React.KeyboardEvent,\r\n node: TreeNode,\r\n parent: TreeNode | undefined\r\n ) => {\r\n const key = e.key;\r\n\r\n // Select\r\n if (key === 'Enter' || key === ' ') {\r\n e.preventDefault();\r\n if (onSelect) onSelect(node.id);\r\n return;\r\n }\r\n\r\n // Collapse or go to parent\r\n if (key === 'ArrowLeft') {\r\n e.preventDefault();\r\n\r\n const hasChildren = node.children && node.children.length > 0;\r\n const isOpen = openNodes.has(node.id);\r\n\r\n if (hasChildren && isOpen) {\r\n toggleNode(node.id);\r\n return;\r\n }\r\n\r\n if (parent) {\r\n setActiveNode(parent.id);\r\n }\r\n return;\r\n }\r\n\r\n // Expand or go to first child\r\n if (key === 'ArrowRight') {\r\n e.preventDefault();\r\n\r\n const hasChildren = node.children && node.children.length > 0;\r\n const isOpen = openNodes.has(node.id);\r\n\r\n if (hasChildren && !isOpen) {\r\n toggleNode(node.id);\r\n return;\r\n }\r\n\r\n if (hasChildren && isOpen) {\r\n const firstChild =\r\n Array.isArray(node.children) && node.children.length > 0\r\n ? node.children[0]\r\n : undefined;\r\n\r\n if (firstChild) {\r\n setActiveNode(firstChild.id);\r\n }\r\n }\r\n return;\r\n }\r\n\r\n // Up/Down navigation\r\n if (key === 'ArrowUp' || key === 'ArrowDown') {\r\n e.preventDefault();\r\n const flat = flatten(data);\r\n const index = flat.findIndex((n) => n.id === node.id);\r\n\r\n if (index === -1) return;\r\n\r\n const nextIndex = key === 'ArrowUp' ? index - 1 : index + 1;\r\n const nextNode = flat[nextIndex];\r\n\r\n if (nextNode) {\r\n setActiveNode(nextNode.id);\r\n }\r\n }\r\n };\r\n\r\n const renderNode = (\r\n node: TreeNode,\r\n parent: TreeNode | undefined,\r\n depth: number\r\n ) => {\r\n const isOpen = openNodes.has(node.id);\r\n const isSelected = node.id === selectedId;\r\n const isActive = node.id === activeNode;\r\n const hasChildren =\r\n Array.isArray(node.children) && node.children.length > 0;\r\n\r\n return (\r\n <div key={node.id}>\r\n <div\r\n role=\"treeitem\"\r\n aria-expanded={hasChildren ? isOpen : undefined}\r\n tabIndex={isActive ? 0 : -1}\r\n className={[\r\n 'ui-tree-node',\r\n isSelected ? 'selected' : '',\r\n isActive ? 'active' : '',\r\n ]\r\n .filter(Boolean)\r\n .join(' ')}\r\n style={{ paddingLeft: depth * 16 }}\r\n onClick={() => {\r\n if (onSelect) onSelect(node.id);\r\n setActiveNode(node.id);\r\n\r\n const hasChildren =\r\n Array.isArray(node.children) && node.children.length > 0;\r\n\r\n // Clicking node should expand/collapse just like arrow\r\n if (hasChildren) {\r\n toggleNode(node.id);\r\n }\r\n }}\r\n onKeyDown={(e) => handleKeyDown(e, node, parent)}\r\n >\r\n {/* Arrow */}\r\n {hasChildren ? (\r\n <span\r\n className={`ui-tree-arrow ${isOpen ? 'open' : ''}`}\r\n onClick={(e) => {\r\n e.stopPropagation();\r\n toggleNode(node.id);\r\n }}\r\n >\r\n ▶\r\n </span>\r\n ) : (\r\n <span className=\"ui-tree-arrow empty\">•</span>\r\n )}\r\n\r\n {/* Icon */}\r\n {node.icon && <span className=\"ui-tree-icon\">{node.icon}</span>}\r\n\r\n <span className=\"ui-tree-label\">{node.label}</span>\r\n </div>\r\n\r\n {/* Children */}\r\n {hasChildren && isOpen && node.children && (\r\n <div role=\"group\">\r\n {node.children.map((child) => renderNode(child, node, depth + 1))}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n };\r\n\r\n return (\r\n <div className={`ui-treeview ${className}`} role=\"tree\">\r\n {data.map((node) => renderNode(node, undefined, 0))}\r\n </div>\r\n );\r\n}\r\n"],"names":["TreeView","data","selectedId","onSelect","className","openNodes","setOpenNodes","useState","activeNode","setActiveNode","toggleNode","id","prev","next","flatten","nodes","acc","node","isOpen","handleKeyDown","parent","key","hasChildren","firstChild","flat","index","n","nextIndex","nextNode","renderNode","depth","isSelected","isActive","jsxs","e","jsx","child"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TreeView.cjs","sources":["../../../src/components/treeview/TreeView.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useState, useCallback, useRef, forwardRef } from 'react';\nimport { cn } from '../../utils';\nimport './TreeView.css';\n\n/* ============================================================\n * Types\n * ============================================================ */\n\nexport interface TreeNode {\n /** Unique identifier for the node */\n id: string;\n /** Text or element to display */\n label: React.ReactNode;\n /** Optional icon to display next to the label */\n icon?: React.ReactNode;\n /** Nested children nodes */\n children?: TreeNode[];\n /** Prevents interaction and applies muted styles */\n disabled?: boolean;\n}\n\nexport interface TreeViewProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSelect'> {\n /** The hierarchical data structure to render */\n data: TreeNode[];\n /** The ID of the currently selected node (Controlled) */\n selectedId?: string;\n /** Array of node IDs to expand by default on initial render */\n defaultExpandedIds?: string[];\n /** Callback fired when a node is selected */\n onSelect?: (id: string, node: TreeNode) => void;\n}\n\n/* ============================================================\n * Component\n * ============================================================ */\n\n/**\n * TreeView Component\n * * A hierarchical list display with nested collapsible folders.\n * * Fully WAI-ARIA compliant with a smart roving tabindex for keyboard navigation.\n */\nexport const TreeView = forwardRef<HTMLDivElement, TreeViewProps>(({\n data,\n selectedId,\n defaultExpandedIds = [],\n onSelect,\n className,\n ...props\n}, ref) => {\n const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set(defaultExpandedIds));\n const treeRef = useRef<HTMLUListElement>(null);\n \n // Generate a unique prefix so multiple TreeViews on the same page never clash IDs\n const baseId = React.useId();\n // Strip the colons that React adds to make it strictly valid for query selectors\n const treeIdPrefix = `nui-tree-${baseId.replace(/:/g, '')}`;\n\n const toggleExpand = useCallback((id: string, e?: React.MouseEvent) => {\n e?.stopPropagation();\n setExpandedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n }, []);\n\n /* ----------------------------------------------------\n Keyboard Navigation (WAI-ARIA Standard)\n ---------------------------------------------------- */\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent, node: TreeNode, parentNode?: TreeNode) => {\n // 1. Instantly kill all default browser scrolling for arrow keys\n if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End', ' ', 'Enter'].includes(e.key)) {\n e.preventDefault();\n }\n \n // 2. Stop parent folders from stealing the keypress\n e.stopPropagation();\n\n const target = e.currentTarget as HTMLLIElement;\n const isExpanded = expandedIds.has(node.id);\n const hasChildren = !!node.children?.length;\n\n // Get all currently visible nodes in the exact DOM order\n const getVisibleNodes = () => {\n if (!treeRef.current) return [];\n return Array.from(\n treeRef.current.querySelectorAll('[role=\"treeitem\"]:not([aria-disabled=\"true\"])')\n ) as HTMLLIElement[];\n };\n\n const nodes = getVisibleNodes();\n const currentIndex = nodes.indexOf(target);\n\n switch (e.key) {\n case 'Enter':\n case ' ':\n if (node.disabled) return;\n // Modern UX: Enter/Space should expand/collapse folders\n if (hasChildren) toggleExpand(node.id);\n onSelect?.(node.id, node);\n break;\n\n case 'ArrowRight':\n if (hasChildren && !isExpanded) {\n // Closed folder -> Open it\n toggleExpand(node.id);\n } else if (hasChildren && isExpanded) {\n // Open folder -> The very next visible node in the DOM is ALWAYS its first child!\n nodes[currentIndex + 1]?.focus();\n }\n break;\n\n case 'ArrowLeft':\n if (hasChildren && isExpanded) {\n // Open folder -> Close it\n toggleExpand(node.id);\n } else if (parentNode) {\n // Closed folder or File -> Safely find parent by checking the end of the ID\n const parentEl = nodes.find(n => n.id.endsWith(`-${parentNode.id}`));\n parentEl?.focus();\n }\n break;\n\n case 'ArrowDown':\n nodes[currentIndex + 1]?.focus();\n break;\n\n case 'ArrowUp':\n nodes[currentIndex - 1]?.focus();\n break;\n\n case 'Home':\n nodes[0]?.focus();\n break;\n\n case 'End':\n nodes[nodes.length - 1]?.focus();\n break;\n }\n },\n [expandedIds, toggleExpand, onSelect]\n );\n\n /* ----------------------------------------------------\n Recursive Render Function\n ---------------------------------------------------- */\n const renderTree = (nodes: TreeNode[], level = 1, parentNode?: TreeNode) => {\n return (\n <ul\n role={level === 1 ? 'tree' : 'group'}\n className={cn(\"nui-tree\", level > 1 && \"nui-tree--nested\")}\n ref={level === 1 ? treeRef : undefined}\n >\n {nodes.map((node, index) => {\n const isExpanded = expandedIds.has(node.id);\n const isSelected = selectedId === node.id;\n const hasChildren = !!node.children?.length;\n \n // Only the first item in the entire tree is tabbable by default (Roving Tabindex)\n const isTabbable = level === 1 && index === 0;\n\n return (\n <li\n key={node.id}\n id={`${treeIdPrefix}-${node.id}`} \n role=\"treeitem\"\n aria-expanded={hasChildren ? isExpanded : undefined}\n aria-selected={isSelected}\n aria-disabled={node.disabled}\n tabIndex={isTabbable ? 0 : -1}\n className={cn(\n \"nui-tree-item\",\n isSelected && \"nui-tree-item--selected\",\n node.disabled && \"nui-tree-item--disabled\"\n )}\n onKeyDown={(e) => handleKeyDown(e, node, parentNode)} \n onClick={(e) => {\n e.stopPropagation();\n if (node.disabled) return;\n \n (e.currentTarget as HTMLElement).focus();\n \n if (hasChildren) {\n toggleExpand(node.id);\n }\n onSelect?.(node.id, node);\n }}\n >\n <div \n className=\"nui-tree-item-content\"\n style={{ paddingLeft: `${(level - 1) * 16}px` }}\n >\n <span \n className={cn(\"nui-tree-chevron\", !hasChildren && \"nui-tree-chevron--hidden\")}\n onClick={(e) => {\n if (hasChildren) toggleExpand(node.id, e);\n }}\n >\n <svg \n width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\"\n className={cn(\"nui-tree-chevron-icon\", isExpanded && \"nui-tree-chevron-icon--open\")}\n aria-hidden=\"true\"\n >\n <polyline points=\"9 18 15 12 9 6\"></polyline>\n </svg>\n </span>\n\n {node.icon && (\n <span className=\"nui-tree-icon\" aria-hidden=\"true\">{node.icon}</span>\n )}\n\n <span className=\"nui-tree-label\">{node.label}</span>\n </div>\n\n {hasChildren && isExpanded && node.children && (\n renderTree(node.children, level + 1, node) // Pass 'node' as the parent for the next level\n )}\n </li>\n );\n })}\n </ul>\n );\n };\n\n return (\n <div ref={ref} className={cn(\"nui-tree-wrapper\", className)} {...props}>\n {renderTree(data)}\n </div>\n );\n});\n\nTreeView.displayName = 'TreeView';"],"names":["TreeView","forwardRef","data","selectedId","defaultExpandedIds","onSelect","className","props","ref","expandedIds","setExpandedIds","useState","treeRef","useRef","treeIdPrefix","React","toggleExpand","useCallback","id","e","prev","next","handleKeyDown","node","parentNode","target","isExpanded","hasChildren","nodes","currentIndex","n","renderTree","level","jsx","cn","index","isSelected","isTabbable","jsxs"],"mappings":"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"}
|