@qw-ui/core 0.3.0-next.40 → 0.3.0-next.42
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 +21 -37
- package/dist/action-sheet/action-sheet.d.ts +110 -0
- package/dist/action-sheet/index.d.ts +2 -0
- package/dist/action-sheet/index.js +2 -0
- package/dist/action-sheet-Cvcr1S_m.js +183 -0
- package/dist/action-sheet-Cvcr1S_m.js.map +1 -0
- package/dist/area-picker/area-picker.d.ts +113 -0
- package/dist/area-picker/index.d.ts +1 -0
- package/dist/area-picker/index.js +348 -0
- package/dist/area-picker/index.js.map +1 -0
- package/dist/aria-description-CimD2M_C.js +13 -0
- package/dist/aria-description-CimD2M_C.js.map +1 -0
- package/dist/aria-description.d.ts +2 -0
- package/dist/avatar/avatar-load.d.ts +76 -0
- package/dist/avatar/avatar.d.ts +22 -5
- package/dist/avatar/index.d.ts +1 -0
- package/dist/avatar/index.js +57 -18
- package/dist/avatar/index.js.map +1 -1
- package/dist/avatar-load-w9dq05oV.js +139 -0
- package/dist/avatar-load-w9dq05oV.js.map +1 -0
- package/dist/back-top/back-top.d.ts +77 -0
- package/dist/back-top/index.d.ts +1 -0
- package/dist/back-top/index.js +189 -0
- package/dist/back-top/index.js.map +1 -0
- package/dist/badge/badge.d.ts +9 -0
- package/dist/badge/index.js +14 -1
- package/dist/badge/index.js.map +1 -1
- package/dist/button/button.d.ts +13 -0
- package/dist/button/index.js +3 -5
- package/dist/button/index.js.map +1 -1
- package/dist/calendar/calendar.d.ts +100 -0
- package/dist/calendar/index.d.ts +1 -0
- package/dist/calendar/index.js +2 -0
- package/dist/calendar-CmpK3stz.js +363 -0
- package/dist/calendar-CmpK3stz.js.map +1 -0
- package/dist/card/card.d.ts +68 -0
- package/dist/card/index.d.ts +2 -0
- package/dist/card/index.js +2 -0
- package/dist/card-DRtWa6Cr.js +107 -0
- package/dist/card-DRtWa6Cr.js.map +1 -0
- package/dist/cascader/cascader.d.ts +115 -0
- package/dist/cascader/index.d.ts +1 -0
- package/dist/cascader/index.js +2 -0
- package/dist/cascader-D-xMjs7P.js +333 -0
- package/dist/cascader-D-xMjs7P.js.map +1 -0
- package/dist/cell/cell.d.ts +41 -0
- package/dist/cell/index.d.ts +1 -0
- package/dist/cell/index.js +43 -0
- package/dist/cell/index.js.map +1 -0
- package/dist/check-list/check-list.d.ts +78 -0
- package/dist/check-list/index.d.ts +2 -0
- package/dist/check-list/index.js +2 -0
- package/dist/check-list-BvZHQt5w.js +60 -0
- package/dist/check-list-BvZHQt5w.js.map +1 -0
- package/dist/checkbox/checkbox.d.ts +59 -0
- package/dist/checkbox/index.d.ts +2 -0
- package/dist/checkbox/index.js +2 -0
- package/dist/checkbox-BlndJrz5.js +127 -0
- package/dist/checkbox-BlndJrz5.js.map +1 -0
- package/dist/checkbox-group/checkbox-group.d.ts +86 -0
- package/dist/checkbox-group/index.d.ts +3 -0
- package/dist/checkbox-group/index.js +2 -0
- package/dist/checkbox-group-gGfsTv-e.js +223 -0
- package/dist/checkbox-group-gGfsTv-e.js.map +1 -0
- package/dist/choice-group-C4c9m0qe.js +373 -0
- package/dist/choice-group-C4c9m0qe.js.map +1 -0
- package/dist/circle/circle.d.ts +97 -0
- package/dist/circle/index.d.ts +1 -0
- package/dist/circle/index.js +119 -0
- package/dist/circle/index.js.map +1 -0
- package/dist/collapse/collapse.d.ts +98 -0
- package/dist/collapse/index.d.ts +2 -0
- package/dist/collapse/index.js +2 -0
- package/dist/collapse-B969h7-H.js +226 -0
- package/dist/collapse-B969h7-H.js.map +1 -0
- package/dist/countdown/countdown.d.ts +114 -0
- package/dist/countdown/index.d.ts +1 -0
- package/dist/countdown/index.js +309 -0
- package/dist/countdown/index.js.map +1 -0
- package/dist/date-picker/date-picker.d.ts +168 -0
- package/dist/date-picker/index.d.ts +1 -0
- package/dist/date-picker/index.js +401 -0
- package/dist/date-picker/index.js.map +1 -0
- package/dist/descriptions/descriptions.d.ts +69 -0
- package/dist/descriptions/index.d.ts +1 -0
- package/dist/descriptions/index.js +86 -0
- package/dist/descriptions/index.js.map +1 -0
- package/dist/dialog/dialog.d.ts +32 -3
- package/dist/dialog/index.js +58 -3
- package/dist/dialog/index.js.map +1 -1
- package/dist/divider/divider.d.ts +18 -2
- package/dist/divider/index.js +20 -2
- package/dist/divider/index.js.map +1 -1
- package/dist/dropdown/dropdown.d.ts +172 -0
- package/dist/dropdown/index.d.ts +2 -0
- package/dist/dropdown/index.js +467 -0
- package/dist/dropdown/index.js.map +1 -0
- package/dist/ellipsis/ellipsis-layout.d.ts +34 -0
- package/dist/ellipsis/ellipsis.d.ts +88 -0
- package/dist/ellipsis/index.d.ts +2 -0
- package/dist/ellipsis/index.js +320 -0
- package/dist/ellipsis/index.js.map +1 -0
- package/dist/empty/empty-illustration.d.ts +3 -0
- package/dist/empty/empty.d.ts +43 -1
- package/dist/empty/index.js +2 -21
- package/dist/empty-CAgyvhUI.js +166 -0
- package/dist/empty-CAgyvhUI.js.map +1 -0
- package/dist/field/field.d.ts +95 -0
- package/dist/field/index.d.ts +2 -0
- package/dist/field/index.js +2 -0
- package/dist/field-DMHGIMZF.js +241 -0
- package/dist/field-DMHGIMZF.js.map +1 -0
- package/dist/floating-bubble/floating-bubble.d.ts +116 -0
- package/dist/floating-bubble/index.d.ts +1 -0
- package/dist/floating-bubble/index.js +333 -0
- package/dist/floating-bubble/index.js.map +1 -0
- package/dist/floating-panel/floating-panel.d.ts +80 -0
- package/dist/floating-panel/index.d.ts +2 -0
- package/dist/floating-panel/index.js +2 -0
- package/dist/floating-panel-Cbm5njBJ.js +363 -0
- package/dist/floating-panel-Cbm5njBJ.js.map +1 -0
- package/dist/footer/footer.d.ts +94 -0
- package/dist/footer/index.d.ts +1 -0
- package/dist/footer/index.js +154 -0
- package/dist/footer/index.js.map +1 -0
- package/dist/form/connect.d.ts +6 -0
- package/dist/form/form.d.ts +100 -0
- package/dist/form/ids.d.ts +14 -0
- package/dist/form/index.d.ts +13 -0
- package/dist/form/index.js +3 -0
- package/dist/form/methods.d.ts +5 -0
- package/dist/form/model.d.ts +24 -0
- package/dist/form/native.d.ts +25 -0
- package/dist/form/rules.d.ts +40 -0
- package/dist/form/ssr.d.ts +7 -0
- package/dist/form/types.d.ts +150 -0
- package/dist/form-BAGkIEMY.js +1354 -0
- package/dist/form-BAGkIEMY.js.map +1 -0
- package/dist/format-BDUY0C_c.js +89 -0
- package/dist/format-BDUY0C_c.js.map +1 -0
- package/dist/gesture-_SRHIvBm.js +30 -0
- package/dist/gesture-_SRHIvBm.js.map +1 -0
- package/dist/grid/grid-item.d.ts +22 -0
- package/dist/grid/grid.d.ts +42 -0
- package/dist/grid/index.d.ts +2 -0
- package/dist/grid/index.js +73 -0
- package/dist/grid/index.js.map +1 -0
- package/dist/icon/icon.d.ts +37 -1
- package/dist/icon/identity.d.ts +3 -0
- package/dist/icon/index.js +2 -20
- package/dist/icon-Cgdbnk1u.js +77 -0
- package/dist/icon-Cgdbnk1u.js.map +1 -0
- package/dist/image/image-load.d.ts +24 -0
- package/dist/image/image.d.ts +45 -8
- package/dist/image/index.d.ts +1 -0
- package/dist/image/index.js +116 -14
- package/dist/image/index.js.map +1 -1
- package/dist/image-preview/image-preview.d.ts +124 -0
- package/dist/image-preview/index.d.ts +1 -0
- package/dist/image-preview/index.js +450 -0
- package/dist/image-preview/index.js.map +1 -0
- package/dist/index-bar/index-bar.d.ts +121 -0
- package/dist/index-bar/index.d.ts +1 -0
- package/dist/index-bar/index.js +386 -0
- package/dist/index-bar/index.js.map +1 -0
- package/dist/index.d.ts +107 -14
- package/dist/index.js +148 -14
- package/dist/index.js.map +1 -0
- package/dist/infinite-scroll/index.d.ts +1 -0
- package/dist/infinite-scroll/index.js +248 -0
- package/dist/infinite-scroll/index.js.map +1 -0
- package/dist/infinite-scroll/infinite-scroll.d.ts +95 -0
- package/dist/input/index.js +181 -22
- package/dist/input/index.js.map +1 -1
- package/dist/input/input.d.ts +44 -1
- package/dist/layer-IpFrl5--.js +414 -0
- package/dist/layer-IpFrl5--.js.map +1 -0
- package/dist/layout-BRSqi4AQ.js +43 -0
- package/dist/layout-BRSqi4AQ.js.map +1 -0
- package/dist/layout.d.ts +13 -0
- package/dist/list/index.d.ts +2 -0
- package/dist/list/index.js +2 -0
- package/dist/list/list.d.ts +30 -0
- package/dist/list-6-iz-Irh.js +28 -0
- package/dist/list-6-iz-Irh.js.map +1 -0
- package/dist/loading/index.js +45 -2
- package/dist/loading/index.js.map +1 -1
- package/dist/loading/loading.d.ts +45 -2
- package/dist/modal-dialog-machine-CF9_z3Dm.js +487 -0
- package/dist/modal-dialog-machine-CF9_z3Dm.js.map +1 -0
- package/dist/modal-dialog-machine.d.ts +21 -0
- package/dist/modal-dismissable-lifetime.d.ts +21 -0
- package/dist/modal-registry.d.ts +24 -0
- package/dist/modal-render-hydration.d.ts +35 -0
- package/dist/modal-render-runtime.d.ts +19 -0
- package/dist/modal-render-scope.d.ts +63 -0
- package/dist/nav-bar/index.d.ts +1 -0
- package/dist/nav-bar/index.js +115 -0
- package/dist/nav-bar/index.js.map +1 -0
- package/dist/nav-bar/nav-bar.d.ts +76 -0
- package/dist/notice-bar/index.d.ts +2 -0
- package/dist/notice-bar/index.js +2 -0
- package/dist/notice-bar/notice-bar.d.ts +103 -0
- package/dist/notice-bar-DMpwLvRb.js +231 -0
- package/dist/notice-bar-DMpwLvRb.js.map +1 -0
- package/dist/notify/index.d.ts +1 -0
- package/dist/notify/index.js +251 -0
- package/dist/notify/index.js.map +1 -0
- package/dist/notify/notify.d.ts +123 -0
- package/dist/number-keyboard/index.d.ts +1 -0
- package/dist/number-keyboard/index.js +333 -0
- package/dist/number-keyboard/index.js.map +1 -0
- package/dist/number-keyboard/number-keyboard.d.ts +141 -0
- package/dist/page-indicator/index.d.ts +1 -0
- package/dist/page-indicator/index.js +138 -0
- package/dist/page-indicator/index.js.map +1 -0
- package/dist/page-indicator/page-indicator.d.ts +93 -0
- package/dist/pagination/index.d.ts +1 -0
- package/dist/pagination/index.js +198 -0
- package/dist/pagination/index.js.map +1 -0
- package/dist/pagination/pagination.d.ts +125 -0
- package/dist/password-input/index.d.ts +1 -0
- package/dist/password-input/index.js +317 -0
- package/dist/password-input/index.js.map +1 -0
- package/dist/password-input/password-input.d.ts +144 -0
- package/dist/picker/index.d.ts +8 -0
- package/dist/picker/index.js +2 -0
- package/dist/picker/picker-machine.d.ts +80 -0
- package/dist/picker/picker.d.ts +121 -0
- package/dist/picker/platform.d.ts +84 -0
- package/dist/picker/selection-connect.d.ts +90 -0
- package/dist/picker/selection-model.d.ts +211 -0
- package/dist/picker-BykFntQN.js +1675 -0
- package/dist/picker-BykFntQN.js.map +1 -0
- package/dist/popover/index.d.ts +3 -0
- package/dist/popover/index.js +253 -0
- package/dist/popover/index.js.map +1 -0
- package/dist/popover/layer.d.ts +44 -0
- package/dist/popover/popover.d.ts +104 -0
- package/dist/popover/positioning.d.ts +132 -0
- package/dist/popup/index.d.ts +2 -0
- package/dist/popup/index.js +2 -0
- package/dist/popup/popup.d.ts +60 -0
- package/dist/popup-z5d8IYCt.js +576 -0
- package/dist/popup-z5d8IYCt.js.map +1 -0
- package/dist/portal-BHCNTWhg.js +78 -0
- package/dist/portal-BHCNTWhg.js.map +1 -0
- package/dist/portal.d.ts +61 -0
- package/dist/price/format.d.ts +19 -0
- package/dist/price/index.d.ts +1 -0
- package/dist/price/index.js +154 -0
- package/dist/price/index.js.map +1 -0
- package/dist/price/price.d.ts +88 -0
- package/dist/progress/index.d.ts +1 -0
- package/dist/progress/index.js +2 -0
- package/dist/progress/progress.d.ts +32 -0
- package/dist/progress-f3iYEfdA.js +37 -0
- package/dist/progress-f3iYEfdA.js.map +1 -0
- package/dist/pull-refresh/index.d.ts +1 -0
- package/dist/pull-refresh/index.js +414 -0
- package/dist/pull-refresh/index.js.map +1 -0
- package/dist/pull-refresh/pull-refresh.d.ts +118 -0
- package/dist/radio-group/index.d.ts +3 -0
- package/dist/radio-group/index.js +2 -0
- package/dist/radio-group/radio-group.d.ts +78 -0
- package/dist/radio-group-BOIMuqnS.js +230 -0
- package/dist/radio-group-BOIMuqnS.js.map +1 -0
- package/dist/rate/index.d.ts +1 -0
- package/dist/rate/index.js +2 -0
- package/dist/rate/rate-form.d.ts +28 -0
- package/dist/rate/rate.d.ts +144 -0
- package/dist/rate-5XsU2QkY.js +631 -0
- package/dist/rate-5XsU2QkY.js.map +1 -0
- package/dist/reorder/index.d.ts +1 -0
- package/dist/reorder/index.js +720 -0
- package/dist/reorder/index.js.map +1 -0
- package/dist/reorder/reorder.d.ts +190 -0
- package/dist/result/index.d.ts +1 -0
- package/dist/result/index.js +123 -0
- package/dist/result/index.js.map +1 -0
- package/dist/result/result.d.ts +88 -0
- package/dist/safe-area/index.d.ts +1 -0
- package/dist/safe-area/index.js +42 -0
- package/dist/safe-area/index.js.map +1 -0
- package/dist/safe-area/safe-area.d.ts +28 -0
- package/dist/search-bar/index.js +3 -1
- package/dist/search-bar/index.js.map +1 -1
- package/dist/search-bar/search-bar.d.ts +4 -0
- package/dist/segmented/index.d.ts +2 -0
- package/dist/segmented/index.js +2 -0
- package/dist/segmented/segmented.d.ts +127 -0
- package/dist/segmented-BaU38_L4.js +367 -0
- package/dist/segmented-BaU38_L4.js.map +1 -0
- package/dist/selection-form-BOM_odUk.js +88 -0
- package/dist/selection-form-BOM_odUk.js.map +1 -0
- package/dist/selection-form.d.ts +27 -0
- package/dist/selection-ivXqO1eu.js +55 -0
- package/dist/selection-ivXqO1eu.js.map +1 -0
- package/dist/selection.d.ts +29 -0
- package/dist/selector/choice-group.d.ts +114 -0
- package/dist/selector/index.d.ts +2 -0
- package/dist/selector/index.js +2 -0
- package/dist/selector/selector.d.ts +90 -0
- package/dist/selector-BU2MMJ4N.js +93 -0
- package/dist/selector-BU2MMJ4N.js.map +1 -0
- package/dist/share-sheet/index.d.ts +1 -0
- package/dist/share-sheet/index.js +138 -0
- package/dist/share-sheet/index.js.map +1 -0
- package/dist/share-sheet/share-sheet.d.ts +139 -0
- package/dist/skeleton/index.js +51 -2
- package/dist/skeleton/index.js.map +1 -1
- package/dist/skeleton/skeleton.d.ts +34 -3
- package/dist/slider/index.d.ts +1 -0
- package/dist/slider/index.js +482 -0
- package/dist/slider/index.js.map +1 -0
- package/dist/slider/slider.d.ts +174 -0
- package/dist/space/index.d.ts +1 -0
- package/dist/space/index.js +52 -0
- package/dist/space/index.js.map +1 -0
- package/dist/space/space.d.ts +46 -0
- package/dist/statistic/index.d.ts +1 -0
- package/dist/statistic/index.js +152 -0
- package/dist/statistic/index.js.map +1 -0
- package/dist/statistic/statistic.d.ts +101 -0
- package/dist/stepper/index.js +62 -3
- package/dist/stepper/index.js.map +1 -1
- package/dist/stepper/stepper.d.ts +44 -4
- package/dist/steps/index.d.ts +1 -0
- package/dist/steps/index.js +72 -0
- package/dist/steps/index.js.map +1 -0
- package/dist/steps/steps.d.ts +67 -0
- package/dist/sticky/index.d.ts +1 -0
- package/dist/sticky/index.js +145 -0
- package/dist/sticky/index.js.map +1 -0
- package/dist/sticky/sticky.d.ts +70 -0
- package/dist/swipe-action/index.d.ts +1 -0
- package/dist/swipe-action/index.js +417 -0
- package/dist/swipe-action/index.js.map +1 -0
- package/dist/swipe-action/swipe-action.d.ts +148 -0
- package/dist/swiper/gesture.d.ts +32 -0
- package/dist/swiper/index.d.ts +1 -0
- package/dist/swiper/index.js +2 -0
- package/dist/swiper/swiper.d.ts +177 -0
- package/dist/swiper-BKivDkHp.js +550 -0
- package/dist/swiper-BKivDkHp.js.map +1 -0
- package/dist/switch/index.js +135 -10
- package/dist/switch/index.js.map +1 -1
- package/dist/switch/switch.d.ts +93 -6
- package/dist/tabbar/index.js +46 -1
- package/dist/tabbar/index.js.map +1 -1
- package/dist/tabbar/tabbar.d.ts +50 -0
- package/dist/tabs/index.js +22 -1
- package/dist/tabs/index.js.map +1 -1
- package/dist/tabs/tabs.d.ts +12 -8
- package/dist/tag/index.js +123 -2
- package/dist/tag/index.js.map +1 -1
- package/dist/tag/tag.d.ts +67 -1
- package/dist/textarea/index.d.ts +2 -0
- package/dist/textarea/index.js +2 -242
- package/dist/textarea/platform.d.ts +22 -0
- package/dist/textarea/textarea.d.ts +165 -46
- package/dist/textarea-qbnruDUG.js +775 -0
- package/dist/textarea-qbnruDUG.js.map +1 -0
- package/dist/time-picker/index.d.ts +1 -0
- package/dist/time-picker/index.js +228 -0
- package/dist/time-picker/index.js.map +1 -0
- package/dist/time-picker/time-picker.d.ts +83 -0
- package/dist/timeline/index.d.ts +1 -0
- package/dist/timeline/index.js +131 -0
- package/dist/timeline/index.js.map +1 -0
- package/dist/timeline/timeline.d.ts +79 -0
- package/dist/toast/index.js +47 -4
- package/dist/toast/index.js.map +1 -1
- package/dist/toast/toast.d.ts +26 -3
- package/dist/top-bar/index.js +16 -2
- package/dist/top-bar/index.js.map +1 -1
- package/dist/top-bar/top-bar.d.ts +13 -2
- package/dist/tree-select/index.d.ts +1 -0
- package/dist/tree-select/index.js +512 -0
- package/dist/tree-select/index.js.map +1 -0
- package/dist/tree-select/tree-select.d.ts +215 -0
- package/dist/typography/index.d.ts +1 -0
- package/dist/typography/index.js +82 -0
- package/dist/typography/index.js.map +1 -0
- package/dist/typography/typography.d.ts +54 -0
- package/dist/upload/index.d.ts +1 -0
- package/dist/upload/index.js +458 -0
- package/dist/upload/index.js.map +1 -0
- package/dist/upload/upload.d.ts +172 -0
- package/dist/virtual-list/index.d.ts +1 -0
- package/dist/virtual-list/index.js +445 -0
- package/dist/virtual-list/index.js.map +1 -0
- package/dist/virtual-list/virtual-list.d.ts +160 -0
- package/dist/visual-manifest.d.ts +6 -0
- package/dist/watermark/index.d.ts +1 -0
- package/dist/watermark/index.js +260 -0
- package/dist/watermark/index.js.map +1 -0
- package/dist/watermark/watermark.d.ts +106 -0
- package/package.json +399 -50
- package/dist/empty/index.js.map +0 -1
- package/dist/icon/index.js.map +0 -1
- package/dist/textarea/index.js.map +0 -1
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
import { d as resolveFloatingPlacement, f as sameFloatingLayout, l as getFloatingPositionerStyle, m as trackFloatingLayout, n as restoreFloatingFocus, p as splitFloatingPlacement, r as trackFloatingDismiss, t as focusWhenReady } from "../layer-IpFrl5--.js";
|
|
2
|
+
import { createMachine } from "@zag-js/core";
|
|
3
|
+
//#region src/dropdown/dropdown.ts
|
|
4
|
+
/** Stable DOM parts shared by every adapter. */
|
|
5
|
+
const DROPDOWN_PARTS = [
|
|
6
|
+
"trigger",
|
|
7
|
+
"positioner",
|
|
8
|
+
"content",
|
|
9
|
+
"item"
|
|
10
|
+
];
|
|
11
|
+
const DROPDOWN_DEFAULT_PLACEMENT = "bottom-start";
|
|
12
|
+
/** Main-axis gap between the trigger and the menu in CSS px. */
|
|
13
|
+
const DROPDOWN_DEFAULT_OFFSET = 4;
|
|
14
|
+
/** Characters typed within this many milliseconds extend the typeahead query. */
|
|
15
|
+
const DROPDOWN_TYPEAHEAD_TIMEOUT = 500;
|
|
16
|
+
const DROPDOWN_MOVES = [
|
|
17
|
+
"first",
|
|
18
|
+
"last",
|
|
19
|
+
"next",
|
|
20
|
+
"prev"
|
|
21
|
+
];
|
|
22
|
+
const text = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
23
|
+
function resolveDropdownState(props = {}) {
|
|
24
|
+
return {
|
|
25
|
+
placement: resolveFloatingPlacement(props.placement, DROPDOWN_DEFAULT_PLACEMENT),
|
|
26
|
+
offset: typeof props.offset === "number" && Number.isFinite(props.offset) ? props.offset : 4,
|
|
27
|
+
label: text(props.label)
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Keeps every item in order; a repeated value only keeps its first item enabled (a disabled first item does not promote a later one). */
|
|
31
|
+
function resolveDropdownItems(items) {
|
|
32
|
+
const seen = /* @__PURE__ */ new Set();
|
|
33
|
+
return (Array.isArray(items) ? items : []).map((source, index) => {
|
|
34
|
+
const value = String(source?.value ?? ""), duplicate = seen.has(value);
|
|
35
|
+
seen.add(value);
|
|
36
|
+
return {
|
|
37
|
+
source,
|
|
38
|
+
index,
|
|
39
|
+
value,
|
|
40
|
+
label: String(source?.label ?? ""),
|
|
41
|
+
disabled: source?.disabled === true || duplicate,
|
|
42
|
+
danger: source?.danger === true,
|
|
43
|
+
duplicate
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** Source index of the enabled item a move lands on, or -1 when nothing is enabled. next/prev wrap; from no highlight they start at the ends. */
|
|
48
|
+
function getDropdownMoveTarget(items, current, move) {
|
|
49
|
+
const enabled = items.filter((item) => !item.disabled);
|
|
50
|
+
if (!enabled.length) return -1;
|
|
51
|
+
const at = enabled.findIndex((item) => item.value === current), last = enabled.length - 1;
|
|
52
|
+
return enabled[move === "first" ? 0 : move === "last" ? last : move === "next" ? at < 0 || at === last ? 0 : at + 1 : at <= 0 ? last : at - 1].index;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Explicit text match: the first enabled item, searching forward and wrapping, whose trimmed label starts
|
|
56
|
+
* with the query (locale lower case). A single or repeated character starts after the current item (so
|
|
57
|
+
* repeating it cycles); a longer query keeps the current item while it still matches. -1 when none matches.
|
|
58
|
+
*/
|
|
59
|
+
function getDropdownTypeaheadTarget(items, current, query) {
|
|
60
|
+
const search = query.toLocaleLowerCase();
|
|
61
|
+
if (!search) return -1;
|
|
62
|
+
const repeated = [...search].every((char) => char === search[0]), needle = repeated ? search[0] : search;
|
|
63
|
+
const enabled = items.filter((item) => !item.disabled);
|
|
64
|
+
const at = enabled.findIndex((item) => item.value === current), start = at < 0 ? 0 : repeated ? at + 1 : at;
|
|
65
|
+
for (let step = 0; step < enabled.length; step += 1) {
|
|
66
|
+
const item = enabled[(start + step) % enabled.length];
|
|
67
|
+
if (item.label.trim().toLocaleLowerCase().startsWith(needle)) return item.index;
|
|
68
|
+
}
|
|
69
|
+
return -1;
|
|
70
|
+
}
|
|
71
|
+
function createDropdownProps(props = {}) {
|
|
72
|
+
if (props.id !== void 0 && (typeof props.id !== "string" || !props.id.trim())) throw new TypeError("Dropdown requires a nonempty instance id when provided");
|
|
73
|
+
return { ...props };
|
|
74
|
+
}
|
|
75
|
+
const ids = (scope) => {
|
|
76
|
+
const base = `dropdown:${scope.id}`;
|
|
77
|
+
return {
|
|
78
|
+
trigger: `${base}:trigger`,
|
|
79
|
+
positioner: `${base}:positioner`,
|
|
80
|
+
content: `${base}:content`,
|
|
81
|
+
item: (index) => `${base}:item:${index}`
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
const live = (prop) => resolveDropdownState({
|
|
85
|
+
placement: prop("placement"),
|
|
86
|
+
offset: prop("offset"),
|
|
87
|
+
label: prop("label")
|
|
88
|
+
});
|
|
89
|
+
const reasonOf = (event) => typeof event.reason === "string" ? event.reason : "api";
|
|
90
|
+
/** Primitive signature for zag's tracker: value, order and enabled state of every item. */
|
|
91
|
+
const signature = (items) => resolveDropdownItems(items).map((item) => `${item.disabled ? 0 : 1}${item.value}`).join("\0");
|
|
92
|
+
/** Roving focus: real DOM focus on the item (scrolled into the menu's view), or the menu itself when nothing is enabled. */
|
|
93
|
+
function focusItem(scope, context, items, index) {
|
|
94
|
+
const id = ids(scope), item = items[index];
|
|
95
|
+
if (!item) {
|
|
96
|
+
scope.getById(id.content)?.focus({ preventScroll: true });
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
context.set("highlighted", item.value);
|
|
100
|
+
const node = scope.getById(id.item(index));
|
|
101
|
+
node?.focus({ preventScroll: true });
|
|
102
|
+
node?.scrollIntoView?.({ block: "nearest" });
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Core owns the menu: open/close with controlled refusal, Popover's positioning module while open, the
|
|
106
|
+
* shared non-modal layer (Escape, outside pointer, focus leaving), roving focus, typeahead, selection and
|
|
107
|
+
* focus return to the original trigger.
|
|
108
|
+
*/
|
|
109
|
+
const dropdownMachine = createMachine({
|
|
110
|
+
props: ({ props }) => createDropdownProps(props),
|
|
111
|
+
initialState: ({ prop }) => prop("open") ?? prop("defaultOpen") ? "open" : "closed",
|
|
112
|
+
context: ({ bindable }) => ({
|
|
113
|
+
dismissReady: bindable(() => ({ defaultValue: false })),
|
|
114
|
+
layout: bindable(() => ({
|
|
115
|
+
defaultValue: null,
|
|
116
|
+
isEqual: sameFloatingLayout
|
|
117
|
+
})),
|
|
118
|
+
highlighted: bindable(() => ({ defaultValue: null }))
|
|
119
|
+
}),
|
|
120
|
+
refs: () => ({
|
|
121
|
+
request: null,
|
|
122
|
+
focus: null,
|
|
123
|
+
typeahead: {
|
|
124
|
+
query: "",
|
|
125
|
+
at: 0
|
|
126
|
+
},
|
|
127
|
+
cancelFocus: null
|
|
128
|
+
}),
|
|
129
|
+
watch({ track, prop, action }) {
|
|
130
|
+
track([() => prop("open")], () => action(["syncControlled"]));
|
|
131
|
+
track([() => signature(prop("items"))], () => action(["reconcile"]));
|
|
132
|
+
},
|
|
133
|
+
states: {
|
|
134
|
+
closed: { on: {
|
|
135
|
+
"CONTROLLED.OPEN": { target: "open" },
|
|
136
|
+
OPEN: [{
|
|
137
|
+
guard: "isControlled",
|
|
138
|
+
actions: ["notifyOpen"]
|
|
139
|
+
}, {
|
|
140
|
+
target: "open",
|
|
141
|
+
actions: ["notifyOpen"]
|
|
142
|
+
}],
|
|
143
|
+
TOGGLE: [{
|
|
144
|
+
guard: "isControlled",
|
|
145
|
+
actions: ["notifyOpen"]
|
|
146
|
+
}, {
|
|
147
|
+
target: "open",
|
|
148
|
+
actions: ["notifyOpen"]
|
|
149
|
+
}]
|
|
150
|
+
} },
|
|
151
|
+
open: {
|
|
152
|
+
effects: ["trackLayout", "trackDismiss"],
|
|
153
|
+
exit: ["clearLayout"],
|
|
154
|
+
on: {
|
|
155
|
+
"CONTROLLED.CLOSE": {
|
|
156
|
+
target: "closed",
|
|
157
|
+
actions: ["restoreFocus"]
|
|
158
|
+
},
|
|
159
|
+
CLOSE: [{
|
|
160
|
+
guard: "isControlled",
|
|
161
|
+
actions: ["notifyClose"]
|
|
162
|
+
}, {
|
|
163
|
+
target: "closed",
|
|
164
|
+
actions: ["notifyClose", "restoreFocus"]
|
|
165
|
+
}],
|
|
166
|
+
TOGGLE: [{
|
|
167
|
+
guard: "isControlled",
|
|
168
|
+
actions: ["notifyClose"]
|
|
169
|
+
}, {
|
|
170
|
+
target: "closed",
|
|
171
|
+
actions: ["notifyClose", "restoreFocus"]
|
|
172
|
+
}],
|
|
173
|
+
LAYOUT: { actions: ["setLayout"] },
|
|
174
|
+
"DISMISS.READY": { actions: ["dismissReady"] },
|
|
175
|
+
HIGHLIGHT: { actions: ["highlight"] },
|
|
176
|
+
MOVE: { actions: ["move"] },
|
|
177
|
+
TYPEAHEAD: { actions: ["typeahead"] },
|
|
178
|
+
SELECT: { actions: ["select"] }
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
implementations: {
|
|
183
|
+
guards: { isControlled: ({ prop }) => prop("open") !== void 0 },
|
|
184
|
+
actions: {
|
|
185
|
+
notifyOpen({ prop, event, refs }) {
|
|
186
|
+
refs.set("request", null);
|
|
187
|
+
refs.set("focus", event.focus === "last" ? "last" : event.focus === "first" ? "first" : null);
|
|
188
|
+
prop("onOpenChange")?.({
|
|
189
|
+
open: true,
|
|
190
|
+
reason: reasonOf(event)
|
|
191
|
+
});
|
|
192
|
+
},
|
|
193
|
+
notifyClose({ prop, event, refs }) {
|
|
194
|
+
const reason = reasonOf(event);
|
|
195
|
+
refs.set("request", {
|
|
196
|
+
reason,
|
|
197
|
+
focusable: event.focusable === true
|
|
198
|
+
});
|
|
199
|
+
prop("onOpenChange")?.({
|
|
200
|
+
open: false,
|
|
201
|
+
reason
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
restoreFocus({ scope, refs }) {
|
|
205
|
+
const request = refs.get("request") ?? {
|
|
206
|
+
reason: "api",
|
|
207
|
+
focusable: false
|
|
208
|
+
}, id = ids(scope);
|
|
209
|
+
refs.set("request", null);
|
|
210
|
+
refs.set("focus", null);
|
|
211
|
+
restoreFloatingFocus(scope.getDoc(), scope.getById(id.trigger), scope.getById(id.content), request);
|
|
212
|
+
},
|
|
213
|
+
setLayout({ context, event, refs, scope, prop }) {
|
|
214
|
+
if (event.type !== "LAYOUT") return;
|
|
215
|
+
context.set("layout", event.layout);
|
|
216
|
+
const intent = refs.get("focus");
|
|
217
|
+
if (!intent) return;
|
|
218
|
+
refs.set("focus", null);
|
|
219
|
+
const items = resolveDropdownItems(prop("items")), index = getDropdownMoveTarget(items, null, intent), id = ids(scope);
|
|
220
|
+
if (index >= 0) context.set("highlighted", items[index].value);
|
|
221
|
+
refs.get("cancelFocus")?.();
|
|
222
|
+
refs.set("cancelFocus", focusWhenReady(scope.getDoc(), () => scope.getById(index >= 0 ? id.item(index) : id.content)));
|
|
223
|
+
},
|
|
224
|
+
dismissReady({ context }) {
|
|
225
|
+
context.set("dismissReady", true);
|
|
226
|
+
},
|
|
227
|
+
clearLayout({ context, refs }) {
|
|
228
|
+
context.set("dismissReady", false);
|
|
229
|
+
refs.get("cancelFocus")?.();
|
|
230
|
+
refs.set("cancelFocus", null);
|
|
231
|
+
refs.set("typeahead", {
|
|
232
|
+
query: "",
|
|
233
|
+
at: 0
|
|
234
|
+
});
|
|
235
|
+
context.set("layout", null);
|
|
236
|
+
context.set("highlighted", null);
|
|
237
|
+
},
|
|
238
|
+
syncControlled({ prop, send }) {
|
|
239
|
+
const open = prop("open");
|
|
240
|
+
if (open !== void 0) send({ type: open ? "CONTROLLED.OPEN" : "CONTROLLED.CLOSE" });
|
|
241
|
+
},
|
|
242
|
+
highlight({ context, event, prop }) {
|
|
243
|
+
if (event.type !== "HIGHLIGHT") return;
|
|
244
|
+
const item = resolveDropdownItems(prop("items")).find((entry) => entry.value === event.value && !entry.disabled);
|
|
245
|
+
if (item && context.get("highlighted") !== item.value) context.set("highlighted", item.value);
|
|
246
|
+
},
|
|
247
|
+
move({ context, event, prop, scope }) {
|
|
248
|
+
if (event.type !== "MOVE") return;
|
|
249
|
+
const items = resolveDropdownItems(prop("items"));
|
|
250
|
+
focusItem(scope, context, items, getDropdownMoveTarget(items, context.get("highlighted"), event.to));
|
|
251
|
+
},
|
|
252
|
+
typeahead({ context, event, prop, refs, scope }) {
|
|
253
|
+
if (event.type !== "TYPEAHEAD") return;
|
|
254
|
+
const now = Date.now(), previous = refs.get("typeahead");
|
|
255
|
+
const query = now - previous.at > 500 ? event.key : previous.query + event.key;
|
|
256
|
+
refs.set("typeahead", {
|
|
257
|
+
query,
|
|
258
|
+
at: now
|
|
259
|
+
});
|
|
260
|
+
const items = resolveDropdownItems(prop("items")), index = getDropdownTypeaheadTarget(items, context.get("highlighted"), query);
|
|
261
|
+
if (index >= 0) focusItem(scope, context, items, index);
|
|
262
|
+
},
|
|
263
|
+
select({ event, prop, send }) {
|
|
264
|
+
if (event.type !== "SELECT") return;
|
|
265
|
+
const item = resolveDropdownItems(prop("items"))[event.index];
|
|
266
|
+
if (!item || item.disabled || item.value !== event.value) return;
|
|
267
|
+
prop("onSelect")?.({ value: item.value });
|
|
268
|
+
send({
|
|
269
|
+
type: "CLOSE",
|
|
270
|
+
reason: "select"
|
|
271
|
+
});
|
|
272
|
+
},
|
|
273
|
+
reconcile({ context, prop, scope, state }) {
|
|
274
|
+
const value = context.get("highlighted");
|
|
275
|
+
if (!state.matches("open") || value === null) return;
|
|
276
|
+
if (resolveDropdownItems(prop("items")).some((item) => item.value === value && !item.disabled)) return;
|
|
277
|
+
context.set("highlighted", null);
|
|
278
|
+
const doc = scope.getDoc(), id = ids(scope);
|
|
279
|
+
const keep = () => {
|
|
280
|
+
const content = scope.getById(id.content), active = doc.activeElement;
|
|
281
|
+
if (content && (!active || active === doc.body || active.getAttribute("aria-disabled") === "true")) content.focus({ preventScroll: true });
|
|
282
|
+
};
|
|
283
|
+
const win = doc.defaultView;
|
|
284
|
+
if (win) win.requestAnimationFrame(keep);
|
|
285
|
+
else keep();
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
effects: {
|
|
289
|
+
trackLayout({ scope, prop, send }) {
|
|
290
|
+
const id = ids(scope);
|
|
291
|
+
return trackFloatingLayout(scope.getDoc(), {
|
|
292
|
+
anchor: () => scope.getById(id.trigger),
|
|
293
|
+
positioner: () => scope.getById(id.positioner),
|
|
294
|
+
content: () => scope.getById(id.content),
|
|
295
|
+
arrow: () => null,
|
|
296
|
+
placement: () => live(prop).placement,
|
|
297
|
+
offset: () => live(prop).offset,
|
|
298
|
+
fallback: DROPDOWN_DEFAULT_PLACEMENT,
|
|
299
|
+
onLayout: (layout) => send({
|
|
300
|
+
type: "LAYOUT",
|
|
301
|
+
layout
|
|
302
|
+
})
|
|
303
|
+
});
|
|
304
|
+
},
|
|
305
|
+
trackDismiss({ scope, send }) {
|
|
306
|
+
const id = ids(scope);
|
|
307
|
+
return trackFloatingDismiss(scope.getDoc(), {
|
|
308
|
+
onReady: () => send({ type: "DISMISS.READY" }),
|
|
309
|
+
type: "menu",
|
|
310
|
+
content: () => scope.getById(id.content),
|
|
311
|
+
trigger: () => scope.getById(id.trigger),
|
|
312
|
+
closeOnEscape: () => true,
|
|
313
|
+
closeOnOutside: () => true,
|
|
314
|
+
closeOnFocusOutside: () => true,
|
|
315
|
+
onDismiss: (request) => send({
|
|
316
|
+
type: "CLOSE",
|
|
317
|
+
reason: request.reason,
|
|
318
|
+
focusable: request.focusable
|
|
319
|
+
})
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
const CONTENT_MOVES = {
|
|
326
|
+
ArrowDown: "next",
|
|
327
|
+
ArrowUp: "prev",
|
|
328
|
+
Home: "first",
|
|
329
|
+
End: "last"
|
|
330
|
+
};
|
|
331
|
+
function connectDropdown(service, normalize) {
|
|
332
|
+
const state = live(service.prop), id = ids(service.scope), open = service.state.matches("open");
|
|
333
|
+
const layout = open && service.context.get("dismissReady") ? service.context.get("layout") : null, highlighted = open ? service.context.get("highlighted") : null;
|
|
334
|
+
const items = resolveDropdownItems(service.prop("items"));
|
|
335
|
+
const placement = layout?.placement ?? state.placement, { side, align } = layout ?? splitFloatingPlacement(state.placement);
|
|
336
|
+
const part = (name) => ({
|
|
337
|
+
"data-scope": "dropdown",
|
|
338
|
+
"data-part": name
|
|
339
|
+
});
|
|
340
|
+
const select = (item) => service.send({
|
|
341
|
+
type: "SELECT",
|
|
342
|
+
index: item.index,
|
|
343
|
+
value: item.value
|
|
344
|
+
});
|
|
345
|
+
return {
|
|
346
|
+
open,
|
|
347
|
+
placement,
|
|
348
|
+
side,
|
|
349
|
+
align,
|
|
350
|
+
positioned: !!layout,
|
|
351
|
+
items,
|
|
352
|
+
highlighted,
|
|
353
|
+
setOpen(next) {
|
|
354
|
+
if (next !== open) service.send({
|
|
355
|
+
type: next ? "OPEN" : "CLOSE",
|
|
356
|
+
reason: "api"
|
|
357
|
+
});
|
|
358
|
+
},
|
|
359
|
+
getTriggerProps: () => normalize.element({
|
|
360
|
+
...part("trigger"),
|
|
361
|
+
id: id.trigger,
|
|
362
|
+
"aria-haspopup": "menu",
|
|
363
|
+
"aria-expanded": open,
|
|
364
|
+
"aria-controls": id.content,
|
|
365
|
+
"data-state": open ? "open" : "closed",
|
|
366
|
+
onClick(event) {
|
|
367
|
+
if (!event.defaultPrevented) service.send({
|
|
368
|
+
type: "TOGGLE",
|
|
369
|
+
reason: "trigger",
|
|
370
|
+
focus: "first"
|
|
371
|
+
});
|
|
372
|
+
},
|
|
373
|
+
onKeyDown(event) {
|
|
374
|
+
const focus = event.key === "ArrowDown" ? "first" : event.key === "ArrowUp" ? "last" : null;
|
|
375
|
+
if (event.defaultPrevented || !focus) return;
|
|
376
|
+
event.preventDefault();
|
|
377
|
+
if (open) service.send({
|
|
378
|
+
type: "MOVE",
|
|
379
|
+
to: focus
|
|
380
|
+
});
|
|
381
|
+
else service.send({
|
|
382
|
+
type: "OPEN",
|
|
383
|
+
reason: "keyboard",
|
|
384
|
+
focus
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}),
|
|
388
|
+
getPositionerProps: () => normalize.element({
|
|
389
|
+
...part("positioner"),
|
|
390
|
+
id: id.positioner,
|
|
391
|
+
"data-placement": placement,
|
|
392
|
+
"data-side": side,
|
|
393
|
+
"data-align": align,
|
|
394
|
+
"data-positioned": layout ? "" : void 0,
|
|
395
|
+
style: getFloatingPositionerStyle(layout)
|
|
396
|
+
}),
|
|
397
|
+
getContentProps: () => normalize.element({
|
|
398
|
+
...part("content"),
|
|
399
|
+
id: id.content,
|
|
400
|
+
role: "menu",
|
|
401
|
+
tabIndex: -1,
|
|
402
|
+
"aria-label": state.label,
|
|
403
|
+
"aria-labelledby": state.label ? void 0 : id.trigger,
|
|
404
|
+
"data-state": open ? "open" : "closed",
|
|
405
|
+
"data-side": side,
|
|
406
|
+
"data-empty": items.length ? void 0 : "",
|
|
407
|
+
onKeyDown(event) {
|
|
408
|
+
if (event.defaultPrevented || event.isComposing) return;
|
|
409
|
+
const key = event.key, move = CONTENT_MOVES[key];
|
|
410
|
+
if (move) {
|
|
411
|
+
event.preventDefault();
|
|
412
|
+
service.send({
|
|
413
|
+
type: "MOVE",
|
|
414
|
+
to: move
|
|
415
|
+
});
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
if (key === "Enter" || key === " ") {
|
|
419
|
+
event.preventDefault();
|
|
420
|
+
const current = service.context.get("highlighted"), item = items.find((entry) => entry.value === current && !entry.disabled);
|
|
421
|
+
if (item) select(item);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
if (key === "Tab") {
|
|
425
|
+
service.scope.getById(id.trigger)?.focus({ preventScroll: true });
|
|
426
|
+
if (event.shiftKey) event.preventDefault();
|
|
427
|
+
service.send({
|
|
428
|
+
type: "CLOSE",
|
|
429
|
+
reason: "tab"
|
|
430
|
+
});
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
if (key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
|
434
|
+
event.preventDefault();
|
|
435
|
+
service.send({
|
|
436
|
+
type: "TYPEAHEAD",
|
|
437
|
+
key
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}),
|
|
442
|
+
getItemProps: (item) => normalize.element({
|
|
443
|
+
...part("item"),
|
|
444
|
+
id: id.item(item.index),
|
|
445
|
+
role: "menuitem",
|
|
446
|
+
tabIndex: item.disabled ? void 0 : -1,
|
|
447
|
+
"aria-disabled": item.disabled ? "true" : void 0,
|
|
448
|
+
"data-value": item.value,
|
|
449
|
+
"data-disabled": item.disabled ? "" : void 0,
|
|
450
|
+
"data-danger": item.danger ? "" : void 0,
|
|
451
|
+
"data-highlighted": !item.disabled && highlighted === item.value ? "" : void 0,
|
|
452
|
+
onClick(event) {
|
|
453
|
+
if (!event.defaultPrevented && !item.disabled) select(item);
|
|
454
|
+
},
|
|
455
|
+
onFocus() {
|
|
456
|
+
if (!item.disabled) service.send({
|
|
457
|
+
type: "HIGHLIGHT",
|
|
458
|
+
value: item.value
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
})
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
//#endregion
|
|
465
|
+
export { DROPDOWN_DEFAULT_OFFSET, DROPDOWN_DEFAULT_PLACEMENT, DROPDOWN_MOVES, DROPDOWN_PARTS, DROPDOWN_TYPEAHEAD_TIMEOUT, connectDropdown, createDropdownProps, dropdownMachine, getDropdownMoveTarget, getDropdownTypeaheadTarget, resolveDropdownItems, resolveDropdownState };
|
|
466
|
+
|
|
467
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/dropdown/dropdown.ts"],"sourcesContent":["import { createMachine, type Machine, type Service } from \"@zag-js/core\";\nimport type { NormalizeProps, PropTypes } from \"@zag-js/types\";\nimport {\n getFloatingPositionerStyle, resolveFloatingPlacement, sameFloatingLayout, splitFloatingPlacement, trackFloatingLayout,\n type FloatingAlign, type FloatingLayout, type FloatingPlacement, type FloatingSide,\n} from \"../popover/positioning.js\";\nimport { focusWhenReady, restoreFloatingFocus, trackFloatingDismiss, type FloatingCloseRequest } from \"../popover/layer.js\";\n\n/** Stable DOM parts shared by every adapter. */\nexport const DROPDOWN_PARTS = [\"trigger\", \"positioner\", \"content\", \"item\"] as const;\nexport type DropdownPlacement = FloatingPlacement;\nexport const DROPDOWN_DEFAULT_PLACEMENT: DropdownPlacement = \"bottom-start\";\n/** Main-axis gap between the trigger and the menu in CSS px. */\nexport const DROPDOWN_DEFAULT_OFFSET = 4;\n/** Characters typed within this many milliseconds extend the typeahead query. */\nexport const DROPDOWN_TYPEAHEAD_TIMEOUT = 500;\nexport const DROPDOWN_MOVES = [\"first\", \"last\", \"next\", \"prev\"] as const;\nexport type DropdownMove = (typeof DROPDOWN_MOVES)[number];\n\n/** One text menu item. value is the exact selection notification; label is the visible and typeahead text. */\nexport interface QwDropdownItem {\n value: string;\n label: string;\n /** Visible and announced (aria-disabled) but skipped by roving focus and never selected. */\n disabled?: boolean;\n /** Destructive styling only; there is no second confirmation. */\n danger?: boolean;\n}\nexport interface QwResolvedDropdownItem {\n readonly source: QwDropdownItem;\n /** Position in the caller's items array; part of the item id. */\n readonly index: number;\n readonly value: string;\n readonly label: string;\n /** Disabled by the caller, or a later duplicate of an earlier value. */\n readonly disabled: boolean;\n readonly danger: boolean;\n readonly duplicate: boolean;\n}\n/** keyboard: ArrowDown/ArrowUp on the trigger; tab / focus-outside: focus left the menu; select: an item was chosen. */\nexport type DropdownOpenChangeReason = \"trigger\" | \"keyboard\" | \"escape\" | \"outside\" | \"focus-outside\" | \"tab\" | \"select\" | \"api\";\nexport interface DropdownOpenChangeDetails { open: boolean; reason: DropdownOpenChangeReason }\nexport interface DropdownSelectDetails { value: string }\n\nexport interface QwDropdownProps {\n /** Page-unique instance identity; adapters generate one when omitted. */\n id?: string;\n /** Caller-owned menu items, never mutated; default []. */\n items?: readonly QwDropdownItem[];\n /** Controlled open state; undefined lets Core own it. */\n open?: boolean;\n /** Initial open state when uncontrolled. */\n defaultOpen?: boolean;\n /** Every open/close request, controlled or not; controlled owners accept by updating open, or refuse by keeping it. */\n onOpenChange?: (details: DropdownOpenChangeDetails) => void;\n /** An enabled item was activated (click, Enter or Space); a close request (reason select) follows. */\n onSelect?: (details: DropdownSelectDetails) => void;\n /** Preferred placement (default bottom-start; unknown values use it); positioning is Popover's Core module. */\n placement?: DropdownPlacement;\n /** Main-axis gap in CSS px; default 4. */\n offset?: number;\n /** Accessible name of the menu; omitted names it by the trigger (aria-labelledby). */\n label?: string;\n}\nexport interface ResolvedDropdownState { placement: DropdownPlacement; offset: number; label: string | undefined }\nconst text = (value: unknown): string | undefined => typeof value === \"string\" && value.trim() ? value.trim() : undefined;\nexport function resolveDropdownState(props: QwDropdownProps = {}): ResolvedDropdownState {\n return {\n placement: resolveFloatingPlacement(props.placement, DROPDOWN_DEFAULT_PLACEMENT),\n offset: typeof props.offset === \"number\" && Number.isFinite(props.offset) ? props.offset : DROPDOWN_DEFAULT_OFFSET,\n label: text(props.label),\n };\n}\n/** Keeps every item in order; a repeated value only keeps its first item enabled (a disabled first item does not promote a later one). */\nexport function resolveDropdownItems(items: readonly QwDropdownItem[] | undefined): readonly QwResolvedDropdownItem[] {\n const seen = new Set<string>();\n return (Array.isArray(items) ? items : []).map((source: QwDropdownItem, index) => {\n const value = String(source?.value ?? \"\"), duplicate = seen.has(value);\n seen.add(value);\n return { source, index, value, label: String(source?.label ?? \"\"), disabled: source?.disabled === true || duplicate, danger: source?.danger === true, duplicate };\n });\n}\n/** Source index of the enabled item a move lands on, or -1 when nothing is enabled. next/prev wrap; from no highlight they start at the ends. */\nexport function getDropdownMoveTarget(items: readonly QwResolvedDropdownItem[], current: string | null, move: DropdownMove): number {\n const enabled = items.filter((item) => !item.disabled);\n if (!enabled.length) return -1;\n const at = enabled.findIndex((item) => item.value === current), last = enabled.length - 1;\n const position = move === \"first\" ? 0 : move === \"last\" ? last\n : move === \"next\" ? (at < 0 || at === last ? 0 : at + 1)\n : (at <= 0 ? last : at - 1);\n return enabled[position]!.index;\n}\n/**\n * Explicit text match: the first enabled item, searching forward and wrapping, whose trimmed label starts\n * with the query (locale lower case). A single or repeated character starts after the current item (so\n * repeating it cycles); a longer query keeps the current item while it still matches. -1 when none matches.\n */\nexport function getDropdownTypeaheadTarget(items: readonly QwResolvedDropdownItem[], current: string | null, query: string): number {\n const search = query.toLocaleLowerCase();\n if (!search) return -1;\n const repeated = [...search].every((char) => char === search[0]), needle = repeated ? search[0]! : search;\n const enabled = items.filter((item) => !item.disabled);\n const at = enabled.findIndex((item) => item.value === current), start = at < 0 ? 0 : repeated ? at + 1 : at;\n for (let step = 0; step < enabled.length; step += 1) {\n const item = enabled[(start + step) % enabled.length]!;\n if (item.label.trim().toLocaleLowerCase().startsWith(needle)) return item.index;\n }\n return -1;\n}\nexport function createDropdownProps(props: QwDropdownProps = {}): QwDropdownProps {\n if (props.id !== undefined && (typeof props.id !== \"string\" || !props.id.trim())) throw new TypeError(\"Dropdown requires a nonempty instance id when provided\");\n return { ...props };\n}\n\ntype FocusIntent = \"first\" | \"last\";\nexport interface QwDropdownSchema {\n props: QwDropdownProps;\n context: { dismissReady: boolean; layout: FloatingLayout | null; highlighted: string | null };\n refs: { request: FloatingCloseRequest | null; focus: FocusIntent | null; typeahead: { query: string; at: number }; cancelFocus: (() => void) | null };\n state: \"open\" | \"closed\";\n guard: \"isControlled\";\n action: \"notifyOpen\" | \"notifyClose\" | \"restoreFocus\" | \"setLayout\" | \"dismissReady\" | \"clearLayout\" | \"syncControlled\" | \"highlight\" | \"move\" | \"typeahead\" | \"select\" | \"reconcile\";\n effect: \"trackLayout\" | \"trackDismiss\";\n event:\n | { type: \"DISMISS.READY\" }\n | { type: \"OPEN\" | \"TOGGLE\"; reason?: DropdownOpenChangeReason; focus?: FocusIntent }\n | { type: \"CLOSE\"; reason?: DropdownOpenChangeReason; focusable?: boolean }\n | { type: \"CONTROLLED.OPEN\" | \"CONTROLLED.CLOSE\" }\n | { type: \"LAYOUT\"; layout: FloatingLayout }\n | { type: \"HIGHLIGHT\"; value: string }\n | { type: \"MOVE\"; to: DropdownMove }\n | { type: \"TYPEAHEAD\"; key: string }\n | { type: \"SELECT\"; index: number; value: string };\n}\nexport type QwDropdownMachine = Machine<QwDropdownSchema>;\nexport type QwDropdownService = Service<QwDropdownSchema>;\ntype Scope = QwDropdownService[\"scope\"];\ntype Context = QwDropdownService[\"context\"];\nconst ids = (scope: Scope) => {\n const base = `dropdown:${scope.id}`;\n return { trigger: `${base}:trigger`, positioner: `${base}:positioner`, content: `${base}:content`, item: (index: number) => `${base}:item:${index}` };\n};\nconst live = (prop: QwDropdownService[\"prop\"]) => resolveDropdownState({ placement: prop(\"placement\"), offset: prop(\"offset\"), label: prop(\"label\") });\nconst reasonOf = (event: { readonly [key: string]: unknown }): DropdownOpenChangeReason => typeof event.reason === \"string\" ? event.reason as DropdownOpenChangeReason : \"api\";\n/** Primitive signature for zag's tracker: value, order and enabled state of every item. */\nconst signature = (items: readonly QwDropdownItem[] | undefined) => resolveDropdownItems(items).map((item) => `${item.disabled ? 0 : 1}${item.value}`).join(\"\\u0000\");\n/** Roving focus: real DOM focus on the item (scrolled into the menu's view), or the menu itself when nothing is enabled. */\nfunction focusItem(scope: Scope, context: Context, items: readonly QwResolvedDropdownItem[], index: number): void {\n const id = ids(scope), item = items[index];\n if (!item) { scope.getById(id.content)?.focus({ preventScroll: true }); return; }\n context.set(\"highlighted\", item.value);\n const node = scope.getById(id.item(index));\n node?.focus({ preventScroll: true });\n node?.scrollIntoView?.({ block: \"nearest\" });\n}\n\n/**\n * Core owns the menu: open/close with controlled refusal, Popover's positioning module while open, the\n * shared non-modal layer (Escape, outside pointer, focus leaving), roving focus, typeahead, selection and\n * focus return to the original trigger.\n */\nexport const dropdownMachine: QwDropdownMachine = createMachine<QwDropdownSchema>({\n props: ({ props }) => createDropdownProps(props),\n initialState: ({ prop }) => (prop(\"open\") ?? prop(\"defaultOpen\")) ? \"open\" : \"closed\",\n context: ({ bindable }) => ({ dismissReady: bindable(() => ({ defaultValue: false })),\n layout: bindable<FloatingLayout | null>(() => ({ defaultValue: null, isEqual: sameFloatingLayout })),\n highlighted: bindable<string | null>(() => ({ defaultValue: null })),\n }),\n refs: () => ({ request: null, focus: null, typeahead: { query: \"\", at: 0 }, cancelFocus: null }),\n watch({ track, prop, action }) {\n track([() => prop(\"open\")], () => action([\"syncControlled\"]));\n track([() => signature(prop(\"items\"))], () => action([\"reconcile\"]));\n },\n states: {\n closed: {\n on: {\n \"CONTROLLED.OPEN\": { target: \"open\" },\n OPEN: [{ guard: \"isControlled\", actions: [\"notifyOpen\"] }, { target: \"open\", actions: [\"notifyOpen\"] }],\n TOGGLE: [{ guard: \"isControlled\", actions: [\"notifyOpen\"] }, { target: \"open\", actions: [\"notifyOpen\"] }],\n },\n },\n open: {\n effects: [\"trackLayout\", \"trackDismiss\"],\n exit: [\"clearLayout\"],\n on: {\n \"CONTROLLED.CLOSE\": { target: \"closed\", actions: [\"restoreFocus\"] },\n CLOSE: [{ guard: \"isControlled\", actions: [\"notifyClose\"] }, { target: \"closed\", actions: [\"notifyClose\", \"restoreFocus\"] }],\n TOGGLE: [{ guard: \"isControlled\", actions: [\"notifyClose\"] }, { target: \"closed\", actions: [\"notifyClose\", \"restoreFocus\"] }],\n LAYOUT: { actions: [\"setLayout\"] },\n \"DISMISS.READY\": { actions: [\"dismissReady\"] },\n HIGHLIGHT: { actions: [\"highlight\"] },\n MOVE: { actions: [\"move\"] },\n TYPEAHEAD: { actions: [\"typeahead\"] },\n SELECT: { actions: [\"select\"] },\n },\n },\n },\n implementations: {\n guards: { isControlled: ({ prop }) => prop(\"open\") !== undefined },\n actions: {\n notifyOpen({ prop, event, refs }) {\n refs.set(\"request\", null);\n refs.set(\"focus\", event.focus === \"last\" ? \"last\" : event.focus === \"first\" ? \"first\" : null);\n prop(\"onOpenChange\")?.({ open: true, reason: reasonOf(event) });\n },\n notifyClose({ prop, event, refs }) {\n const reason = reasonOf(event);\n refs.set(\"request\", { reason, focusable: event.focusable === true });\n prop(\"onOpenChange\")?.({ open: false, reason });\n },\n // Runs while the menu is still in the DOM: focus moves before the items unmount.\n restoreFocus({ scope, refs }) {\n const request = refs.get(\"request\") ?? { reason: \"api\", focusable: false }, id = ids(scope);\n refs.set(\"request\", null);\n refs.set(\"focus\", null);\n restoreFloatingFocus(scope.getDoc(), scope.getById(id.trigger), scope.getById(id.content), request);\n },\n // The first layout makes the menu visible; a requested open then moves focus into it.\n setLayout({ context, event, refs, scope, prop }) {\n if (event.type !== \"LAYOUT\") return;\n context.set(\"layout\", event.layout);\n const intent = refs.get(\"focus\");\n if (!intent) return;\n refs.set(\"focus\", null);\n const items = resolveDropdownItems(prop(\"items\")), index = getDropdownMoveTarget(items, null, intent), id = ids(scope);\n if (index >= 0) context.set(\"highlighted\", items[index]!.value);\n refs.get(\"cancelFocus\")?.();\n refs.set(\"cancelFocus\", focusWhenReady(scope.getDoc(), () => scope.getById(index >= 0 ? id.item(index) : id.content)));\n },\n dismissReady({ context }) { context.set(\"dismissReady\", true); },\n clearLayout({ context, refs }) {\n context.set(\"dismissReady\", false);\n refs.get(\"cancelFocus\")?.();\n refs.set(\"cancelFocus\", null);\n refs.set(\"typeahead\", { query: \"\", at: 0 });\n context.set(\"layout\", null);\n context.set(\"highlighted\", null);\n },\n syncControlled({ prop, send }) {\n const open = prop(\"open\");\n if (open !== undefined) send({ type: open ? \"CONTROLLED.OPEN\" : \"CONTROLLED.CLOSE\" });\n },\n highlight({ context, event, prop }) {\n if (event.type !== \"HIGHLIGHT\") return;\n const item = resolveDropdownItems(prop(\"items\")).find((entry) => entry.value === event.value && !entry.disabled);\n if (item && context.get(\"highlighted\") !== item.value) context.set(\"highlighted\", item.value);\n },\n move({ context, event, prop, scope }) {\n if (event.type !== \"MOVE\") return;\n const items = resolveDropdownItems(prop(\"items\"));\n focusItem(scope, context, items, getDropdownMoveTarget(items, context.get(\"highlighted\"), event.to));\n },\n typeahead({ context, event, prop, refs, scope }) {\n if (event.type !== \"TYPEAHEAD\") return;\n const now = Date.now(), previous = refs.get(\"typeahead\");\n const query = now - previous.at > DROPDOWN_TYPEAHEAD_TIMEOUT ? event.key : previous.query + event.key;\n refs.set(\"typeahead\", { query, at: now });\n const items = resolveDropdownItems(prop(\"items\")), index = getDropdownTypeaheadTarget(items, context.get(\"highlighted\"), query);\n if (index >= 0) focusItem(scope, context, items, index);\n },\n // Re-validated against the latest items: a removed, replaced, disabled or duplicated item is never selected.\n select({ event, prop, send }) {\n if (event.type !== \"SELECT\") return;\n const item = resolveDropdownItems(prop(\"items\"))[event.index];\n if (!item || item.disabled || item.value !== event.value) return;\n prop(\"onSelect\")?.({ value: item.value });\n send({ type: \"CLOSE\", reason: \"select\" });\n },\n // Items changed while open: a highlight that is gone or disabled is dropped, keeping focus inside the menu.\n reconcile({ context, prop, scope, state }) {\n const value = context.get(\"highlighted\");\n if (!state.matches(\"open\") || value === null) return;\n if (resolveDropdownItems(prop(\"items\")).some((item) => item.value === value && !item.disabled)) return;\n context.set(\"highlighted\", null);\n const doc = scope.getDoc(), id = ids(scope);\n const keep = () => {\n const content = scope.getById(id.content), active = doc.activeElement;\n if (content && (!active || active === doc.body || active.getAttribute(\"aria-disabled\") === \"true\")) content.focus({ preventScroll: true });\n };\n const win = doc.defaultView;\n if (win) win.requestAnimationFrame(keep); else keep();\n },\n },\n effects: {\n trackLayout({ scope, prop, send }) {\n const id = ids(scope);\n return trackFloatingLayout(scope.getDoc(), {\n anchor: () => scope.getById(id.trigger), positioner: () => scope.getById(id.positioner), content: () => scope.getById(id.content), arrow: () => null,\n placement: () => live(prop).placement, offset: () => live(prop).offset, fallback: DROPDOWN_DEFAULT_PLACEMENT,\n onLayout: (layout) => send({ type: \"LAYOUT\", layout }),\n });\n },\n trackDismiss({ scope, send }) {\n const id = ids(scope);\n return trackFloatingDismiss(scope.getDoc(), {\n onReady: () => send({ type: \"DISMISS.READY\" }),\n type: \"menu\", content: () => scope.getById(id.content), trigger: () => scope.getById(id.trigger),\n closeOnEscape: () => true, closeOnOutside: () => true, closeOnFocusOutside: () => true,\n onDismiss: (request) => send({ type: \"CLOSE\", reason: request.reason, focusable: request.focusable }),\n });\n },\n },\n },\n});\n\n/** Minimal event surfaces shared by React, Vue and native events. */\nexport interface DropdownDomEvent { readonly defaultPrevented: boolean; preventDefault(): void }\nexport interface DropdownKeyboardEvent extends DropdownDomEvent {\n readonly key: string;\n readonly shiftKey?: boolean;\n readonly altKey?: boolean;\n readonly ctrlKey?: boolean;\n readonly metaKey?: boolean;\n readonly isComposing?: boolean;\n}\nconst CONTENT_MOVES: Readonly<Record<string, DropdownMove>> = { ArrowDown: \"next\", ArrowUp: \"prev\", Home: \"first\", End: \"last\" };\n\nexport interface QwDropdownApi<T extends PropTypes = PropTypes> {\n open: boolean;\n /** The measured placement while open (after flipping), otherwise the preferred one. */\n placement: DropdownPlacement;\n side: FloatingSide;\n align: FloatingAlign;\n positioned: boolean;\n items: readonly QwResolvedDropdownItem[];\n /** Value of the item holding roving focus. */\n highlighted: string | null;\n /** Request a state; controlled owners decide through onOpenChange (reason api). */\n setOpen(open: boolean): void;\n /** Merge onto the caller's own trigger node (click toggles; ArrowDown/ArrowUp open on the first/last item); preventDefault() in its handlers skips Core. */\n getTriggerProps(): T[\"element\"];\n getPositionerProps(): T[\"element\"];\n getContentProps(): T[\"element\"];\n getItemProps(item: QwResolvedDropdownItem): T[\"element\"];\n}\nexport function connectDropdown<T extends PropTypes>(service: QwDropdownService, normalize: NormalizeProps<T>): QwDropdownApi<T> {\n const state = live(service.prop), id = ids(service.scope), open = service.state.matches(\"open\");\n const layout = open && service.context.get(\"dismissReady\") ? service.context.get(\"layout\") : null, highlighted = open ? service.context.get(\"highlighted\") : null;\n const items = resolveDropdownItems(service.prop(\"items\"));\n const placement = layout?.placement ?? state.placement, { side, align } = layout ?? splitFloatingPlacement(state.placement);\n const part = (name: (typeof DROPDOWN_PARTS)[number]) => ({ \"data-scope\": \"dropdown\", \"data-part\": name });\n const select = (item: QwResolvedDropdownItem) => service.send({ type: \"SELECT\", index: item.index, value: item.value });\n return {\n open, placement, side, align, positioned: !!layout, items, highlighted,\n setOpen(next) { if (next !== open) service.send({ type: next ? \"OPEN\" : \"CLOSE\", reason: \"api\" }); },\n getTriggerProps: () => normalize.element({\n ...part(\"trigger\"), id: id.trigger, \"aria-haspopup\": \"menu\", \"aria-expanded\": open, \"aria-controls\": id.content, \"data-state\": open ? \"open\" : \"closed\",\n onClick(event: DropdownDomEvent) { if (!event.defaultPrevented) service.send({ type: \"TOGGLE\", reason: \"trigger\", focus: \"first\" }); },\n onKeyDown(event: DropdownKeyboardEvent) {\n const focus = event.key === \"ArrowDown\" ? \"first\" : event.key === \"ArrowUp\" ? \"last\" : null;\n if (event.defaultPrevented || !focus) return;\n event.preventDefault();\n if (open) service.send({ type: \"MOVE\", to: focus });\n else service.send({ type: \"OPEN\", reason: \"keyboard\", focus });\n },\n }),\n getPositionerProps: () => normalize.element({\n ...part(\"positioner\"), id: id.positioner, \"data-placement\": placement, \"data-side\": side, \"data-align\": align,\n \"data-positioned\": layout ? \"\" : undefined, style: getFloatingPositionerStyle(layout),\n }),\n getContentProps: () => normalize.element({\n ...part(\"content\"), id: id.content, role: \"menu\", tabIndex: -1, \"aria-label\": state.label, \"aria-labelledby\": state.label ? undefined : id.trigger,\n \"data-state\": open ? \"open\" : \"closed\", \"data-side\": side, \"data-empty\": items.length ? undefined : \"\",\n onKeyDown(event: DropdownKeyboardEvent) {\n if (event.defaultPrevented || event.isComposing) return;\n const key = event.key, move = CONTENT_MOVES[key];\n if (move) { event.preventDefault(); service.send({ type: \"MOVE\", to: move }); return; }\n if (key === \"Enter\" || key === \" \") {\n event.preventDefault();\n const current = service.context.get(\"highlighted\"), item = items.find((entry) => entry.value === current && !entry.disabled);\n if (item) select(item);\n return;\n }\n if (key === \"Tab\") {\n // Leave from the trigger's place in the tab order: Tab continues past it, Shift+Tab stops on it.\n service.scope.getById(id.trigger)?.focus({ preventScroll: true });\n if (event.shiftKey) event.preventDefault();\n service.send({ type: \"CLOSE\", reason: \"tab\" });\n return;\n }\n if (key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) { event.preventDefault(); service.send({ type: \"TYPEAHEAD\", key }); }\n },\n }),\n getItemProps: (item) => normalize.element({\n ...part(\"item\"), id: id.item(item.index), role: \"menuitem\", tabIndex: item.disabled ? undefined : -1, \"aria-disabled\": item.disabled ? \"true\" : undefined,\n \"data-value\": item.value, \"data-disabled\": item.disabled ? \"\" : undefined, \"data-danger\": item.danger ? \"\" : undefined,\n \"data-highlighted\": !item.disabled && highlighted === item.value ? \"\" : undefined,\n onClick(event: DropdownDomEvent) { if (!event.defaultPrevented && !item.disabled) select(item); },\n onFocus() { if (!item.disabled) service.send({ type: \"HIGHLIGHT\", value: item.value }); },\n }),\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { EllipsisDirection } from "./ellipsis.js";
|
|
2
|
+
/**
|
|
3
|
+
* Ellipsis platform boundary: DOM measurement and resize observation only.
|
|
4
|
+
* Truncation search and expanded/collapsed decisions stay in ellipsis.ts.
|
|
5
|
+
* Nothing here runs at module top level, so importing it is SSR-safe.
|
|
6
|
+
*/
|
|
7
|
+
export interface EllipsisMeasureInput {
|
|
8
|
+
content: string;
|
|
9
|
+
rows: number;
|
|
10
|
+
direction: EllipsisDirection;
|
|
11
|
+
symbol: string;
|
|
12
|
+
/** Visible action label measured on the same line as the truncated text ("" = none). */
|
|
13
|
+
actionText: string;
|
|
14
|
+
}
|
|
15
|
+
export interface EllipsisMeasurement {
|
|
16
|
+
/** True when the full content exceeds `rows` lines. */
|
|
17
|
+
overflow: boolean;
|
|
18
|
+
/** Collapsed visible text (with symbol) when overflowing; otherwise the full content. */
|
|
19
|
+
text: string;
|
|
20
|
+
/** Inline size used for the decision, in CSS pixels. */
|
|
21
|
+
width: number;
|
|
22
|
+
}
|
|
23
|
+
export declare const ELLIPSIS_PROBE_ATTRIBUTE = "data-qw-ellipsis-probe";
|
|
24
|
+
/** Code-point safe truncation candidates; `kept` counts content code points that remain visible. */
|
|
25
|
+
export declare function buildEllipsisCandidate(chars: readonly string[], kept: number, direction: EllipsisDirection, symbol: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Pure search: longest candidate that `fits`. `fits` must be monotonic (fewer kept characters never fit worse).
|
|
28
|
+
* Returns null when the full content fits.
|
|
29
|
+
*/
|
|
30
|
+
export declare function findEllipsisText(content: string, direction: EllipsisDirection, symbol: string, fits: (candidate: string) => boolean): string | null;
|
|
31
|
+
/** Measures with a detached-from-layout probe in the same document; returns null when not laid out (zero width). */
|
|
32
|
+
export declare function measureEllipsisLayout(root: HTMLElement, input: EllipsisMeasureInput): EllipsisMeasurement | null;
|
|
33
|
+
/** Calls `onChange` when the observed inline size changes (and once on start). Returns stop. */
|
|
34
|
+
export declare function observeEllipsisLayout(root: HTMLElement, onChange: () => void): () => void;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { type Machine, type Service } from "@zag-js/core";
|
|
2
|
+
import type { NormalizeProps, PropTypes } from "@zag-js/types";
|
|
3
|
+
import { type EllipsisMeasurement } from "./ellipsis-layout.js";
|
|
4
|
+
export declare const ELLIPSIS_DIRECTIONS: readonly ["end", "start", "middle"];
|
|
5
|
+
export declare const ELLIPSIS_PARTS: readonly ["root", "content", "text", "full-text", "toggle"];
|
|
6
|
+
export type EllipsisDirection = (typeof ELLIPSIS_DIRECTIONS)[number];
|
|
7
|
+
export type EllipsisPart = (typeof ELLIPSIS_PARTS)[number];
|
|
8
|
+
export interface EllipsisExpandedChangeDetails {
|
|
9
|
+
expanded: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface QwEllipsisProps {
|
|
12
|
+
/** Stable instance identity for aria-controls; frameworks generate one when omitted. */
|
|
13
|
+
id?: string;
|
|
14
|
+
/** Plain text content (measurement requires text). */
|
|
15
|
+
content: string;
|
|
16
|
+
/** Visible lines when collapsed; integer ≥ 1, invalid → 1. */
|
|
17
|
+
rows?: number;
|
|
18
|
+
/** Where the ellipsis symbol goes. Default end. */
|
|
19
|
+
direction?: EllipsisDirection;
|
|
20
|
+
/** Controlled expanded state when defined. */
|
|
21
|
+
expanded?: boolean;
|
|
22
|
+
defaultExpanded?: boolean;
|
|
23
|
+
/** Expand action label; "" disables expansion. Default 展开. */
|
|
24
|
+
expandText?: string;
|
|
25
|
+
/** Collapse action label; "" hides the collapse action. Default 收起. */
|
|
26
|
+
collapseText?: string;
|
|
27
|
+
/** Ellipsis symbol. Default …. */
|
|
28
|
+
symbol?: string;
|
|
29
|
+
onExpandedChange?: (details: EllipsisExpandedChangeDetails) => void;
|
|
30
|
+
}
|
|
31
|
+
export declare function normalizeEllipsisRows(rows: unknown): number;
|
|
32
|
+
type NormalizedProps = QwEllipsisProps & {
|
|
33
|
+
content: string;
|
|
34
|
+
rows: number;
|
|
35
|
+
direction: EllipsisDirection;
|
|
36
|
+
expandText: string;
|
|
37
|
+
collapseText: string;
|
|
38
|
+
symbol: string;
|
|
39
|
+
defaultExpanded: boolean;
|
|
40
|
+
};
|
|
41
|
+
export declare function createEllipsisProps(props: QwEllipsisProps): NormalizedProps;
|
|
42
|
+
interface KeyedMeasurement extends EllipsisMeasurement {
|
|
43
|
+
key: string;
|
|
44
|
+
}
|
|
45
|
+
export interface QwEllipsisSchema {
|
|
46
|
+
props: NormalizedProps;
|
|
47
|
+
context: {
|
|
48
|
+
expanded: boolean;
|
|
49
|
+
measurement: KeyedMeasurement | null;
|
|
50
|
+
};
|
|
51
|
+
state: "ready";
|
|
52
|
+
action: "toggle" | "setExpanded" | "measure";
|
|
53
|
+
effect: "observeLayout";
|
|
54
|
+
event: {
|
|
55
|
+
type: "TOGGLE";
|
|
56
|
+
} | {
|
|
57
|
+
type: "EXPANDED.SET";
|
|
58
|
+
value: boolean;
|
|
59
|
+
} | {
|
|
60
|
+
type: "MEASURE";
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export type QwEllipsisMachine = Machine<QwEllipsisSchema>;
|
|
64
|
+
export type QwEllipsisService = Service<QwEllipsisSchema>;
|
|
65
|
+
/** Expanded state + measurement lifecycle. The resize observer is a Core effect: start on mount, stop on unmount. */
|
|
66
|
+
export declare const ellipsisMachine: QwEllipsisMachine;
|
|
67
|
+
export interface QwEllipsisApi<T extends PropTypes = PropTypes> {
|
|
68
|
+
content: string;
|
|
69
|
+
expanded: boolean;
|
|
70
|
+
/** A current measurement exists for these props. */
|
|
71
|
+
measured: boolean;
|
|
72
|
+
overflow: boolean;
|
|
73
|
+
/** Visible text is shortened; full text is provided to assistive technology separately. */
|
|
74
|
+
truncated: boolean;
|
|
75
|
+
visibleText: string;
|
|
76
|
+
toggleText: string;
|
|
77
|
+
showToggle: boolean;
|
|
78
|
+
setExpanded(expanded: boolean): void;
|
|
79
|
+
/** Re-run measurement (for example after a caller-driven style change). */
|
|
80
|
+
remeasure(): void;
|
|
81
|
+
getRootProps(): T["element"];
|
|
82
|
+
getContentProps(): T["element"];
|
|
83
|
+
getTextProps(): T["element"];
|
|
84
|
+
getFullTextProps(): T["element"];
|
|
85
|
+
getToggleProps(): T["button"];
|
|
86
|
+
}
|
|
87
|
+
export declare function connectEllipsis<T extends PropTypes>(service: QwEllipsisService, normalize: NormalizeProps<T>): QwEllipsisApi<T>;
|
|
88
|
+
export {};
|