@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,317 @@
|
|
|
1
|
+
import { createMachine } from "@zag-js/core";
|
|
2
|
+
import { addDomEvent, dataAttr, isInputElement } from "@zag-js/dom-query";
|
|
3
|
+
//#region src/password-input/password-input.ts
|
|
4
|
+
/**
|
|
5
|
+
* PasswordInput 密码格 / 验证码:一个真实 `<input>` 承载焦点、选区、粘贴、读屏与表单,
|
|
6
|
+
* 格子只是 `aria-hidden` 的呈现层。
|
|
7
|
+
*
|
|
8
|
+
* Core 持有:长度/字符集归一化、受控提议与拒绝写回、粘贴合并、掩码、选区→高亮格、
|
|
9
|
+
* 一次填满一次 onComplete、表单 reset 恢复默认值。Core 从不提交表单、也不校验验证码。
|
|
10
|
+
*/
|
|
11
|
+
/** Stable DOM parts shared by every adapter. */
|
|
12
|
+
const PASSWORD_INPUT_PARTS = [
|
|
13
|
+
"root",
|
|
14
|
+
"input",
|
|
15
|
+
"cells",
|
|
16
|
+
"cell",
|
|
17
|
+
"hint"
|
|
18
|
+
];
|
|
19
|
+
const PASSWORD_INPUT_DEFAULT_LENGTH = 6;
|
|
20
|
+
/** Inclusive length bounds; out-of-range or non-integer lengths are clamped / rounded down. */
|
|
21
|
+
const PASSWORD_INPUT_MIN_LENGTH = 1;
|
|
22
|
+
const PASSWORD_INPUT_MAX_LENGTH = 12;
|
|
23
|
+
/** Character kept by each charset. Full-width digits/letters are folded to ASCII first. */
|
|
24
|
+
const PASSWORD_INPUT_CHARSETS = ["numeric", "alphanumeric"];
|
|
25
|
+
const PASSWORD_INPUT_MODES = ["password", "otp"];
|
|
26
|
+
/** Decorative glyph drawn in a filled cell while masked. */
|
|
27
|
+
const PASSWORD_INPUT_MASK_GLYPH = "•";
|
|
28
|
+
const DEFAULT_LABEL = {
|
|
29
|
+
password: "密码",
|
|
30
|
+
otp: "验证码"
|
|
31
|
+
};
|
|
32
|
+
const text = (value) => typeof value === "string" ? value.trim() : "";
|
|
33
|
+
/** Clamp to an integer in 1–12; anything unusable falls back to 6. */
|
|
34
|
+
function normalizePasswordInputLength(length) {
|
|
35
|
+
if (typeof length !== "number" || !Number.isFinite(length)) return 6;
|
|
36
|
+
return Math.min(12, Math.max(1, Math.floor(length)));
|
|
37
|
+
}
|
|
38
|
+
/** Fold full-width ASCII (IME digits like "123") to half-width, drop disallowed characters, then truncate. Never throws. */
|
|
39
|
+
function normalizePasswordInputValue(value, length, charset = "numeric") {
|
|
40
|
+
if (typeof value !== "string" && typeof value !== "number") return "";
|
|
41
|
+
const allowed = charset === "alphanumeric" ? /[0-9A-Za-z]/ : /[0-9]/;
|
|
42
|
+
let result = "";
|
|
43
|
+
for (const raw of String(value)) {
|
|
44
|
+
const code = raw.charCodeAt(0), char = code >= 65281 && code <= 65374 ? String.fromCharCode(code - 65248) : raw;
|
|
45
|
+
if (allowed.test(char)) result += char;
|
|
46
|
+
}
|
|
47
|
+
return result.slice(0, normalizePasswordInputLength(length));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Merge pasted text into the current value. A paste that alone fills every cell (after filtering)
|
|
51
|
+
* replaces the whole value — the usual "paste the code from SMS" case; otherwise it replaces the selection.
|
|
52
|
+
* Returns the next value and the caret after the inserted text.
|
|
53
|
+
*/
|
|
54
|
+
function mergePasswordInputPaste(current, pasted, selection, length, charset) {
|
|
55
|
+
const size = normalizePasswordInputLength(length), clean = normalizePasswordInputValue(pasted, 48, charset);
|
|
56
|
+
if (clean.length >= size) return {
|
|
57
|
+
value: clean.slice(0, size),
|
|
58
|
+
caret: size
|
|
59
|
+
};
|
|
60
|
+
const start = Math.max(0, Math.min(selection.start, current.length)), end = Math.max(start, Math.min(selection.end, current.length));
|
|
61
|
+
const value = normalizePasswordInputValue(current.slice(0, start) + clean + current.slice(end), size, charset);
|
|
62
|
+
return {
|
|
63
|
+
value,
|
|
64
|
+
caret: Math.min(value.length, start + clean.length)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function createPasswordInputProps(props = {}) {
|
|
68
|
+
const mode = props.mode === "otp" ? "otp" : "password", charset = props.charset === "alphanumeric" ? "alphanumeric" : "numeric";
|
|
69
|
+
const length = normalizePasswordInputLength(props.length);
|
|
70
|
+
return {
|
|
71
|
+
...props,
|
|
72
|
+
mode,
|
|
73
|
+
charset,
|
|
74
|
+
length,
|
|
75
|
+
mask: typeof props.mask === "boolean" ? props.mask : mode === "password",
|
|
76
|
+
label: text(props.label) || DEFAULT_LABEL[mode],
|
|
77
|
+
hint: text(props.hint) || `${length} 位${charset === "numeric" ? "数字" : "字母或数字"}`,
|
|
78
|
+
autoComplete: props.autoComplete ?? (mode === "otp" ? "one-time-code" : "off"),
|
|
79
|
+
disabled: !!props.disabled,
|
|
80
|
+
readOnly: !!props.readOnly,
|
|
81
|
+
required: !!props.required
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const normalizeWith = (prop, value) => normalizePasswordInputValue(value, prop("length"), prop("charset"));
|
|
85
|
+
const inputId = (id) => `password-input:${id}:input`;
|
|
86
|
+
const hintId = (id) => `password-input:${id}:hint`;
|
|
87
|
+
const passwordInputMachine = createMachine({
|
|
88
|
+
props: ({ props }) => createPasswordInputProps(props),
|
|
89
|
+
initialState: () => "idle",
|
|
90
|
+
context: ({ bindable, prop }) => ({
|
|
91
|
+
value: bindable(() => ({
|
|
92
|
+
defaultValue: normalizeWith(prop, prop("defaultValue") ?? ""),
|
|
93
|
+
value: prop("value") === void 0 ? void 0 : normalizeWith(prop, prop("value")),
|
|
94
|
+
onChange: (value) => prop("onValueChange")?.({ value })
|
|
95
|
+
})),
|
|
96
|
+
selection: bindable(() => ({
|
|
97
|
+
defaultValue: {
|
|
98
|
+
start: 0,
|
|
99
|
+
end: 0
|
|
100
|
+
},
|
|
101
|
+
hash: (value) => `${value.start}:${value.end}`
|
|
102
|
+
})),
|
|
103
|
+
focused: bindable(() => ({ defaultValue: false }))
|
|
104
|
+
}),
|
|
105
|
+
refs: ({ prop }) => ({ completed: normalizeWith(prop, prop("value") ?? prop("defaultValue") ?? "").length === prop("length") }),
|
|
106
|
+
watch({ track, context, prop, action }) {
|
|
107
|
+
track([() => context.get("value"), () => prop("length")], () => action(["notifyComplete"]));
|
|
108
|
+
},
|
|
109
|
+
effects: ["trackForm"],
|
|
110
|
+
on: { "VALUE.SET": { actions: ["setValue"] } },
|
|
111
|
+
states: { idle: {} },
|
|
112
|
+
implementations: {
|
|
113
|
+
actions: {
|
|
114
|
+
setValue({ event, context, prop }) {
|
|
115
|
+
if (prop("disabled") || prop("readOnly")) return;
|
|
116
|
+
context.set("value", normalizeWith(prop, event.value));
|
|
117
|
+
},
|
|
118
|
+
notifyComplete({ context, prop, refs }) {
|
|
119
|
+
const value = context.get("value"), full = value.length === prop("length");
|
|
120
|
+
if (full && !refs.get("completed")) {
|
|
121
|
+
refs.set("completed", true);
|
|
122
|
+
prop("onComplete")?.({ value });
|
|
123
|
+
} else if (!full) refs.set("completed", false);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
effects: {
|
|
127
|
+
/**
|
|
128
|
+
* Form reset. Reset does not bubble, so listen in the document capture phase and match the input's current form owner:
|
|
129
|
+
* re-parenting, the form attribute and re-rendered inputs need no rebinding. Native reset runs after listeners, so the
|
|
130
|
+
* default is restored on the next task.
|
|
131
|
+
*/
|
|
132
|
+
trackForm({ scope, context, prop }) {
|
|
133
|
+
const doc = scope.getDoc();
|
|
134
|
+
let stopped = false;
|
|
135
|
+
const timers = /* @__PURE__ */ new Set();
|
|
136
|
+
const release = addDomEvent(doc, "reset", (event) => {
|
|
137
|
+
const input = scope.getById(inputId(scope.id ?? ""));
|
|
138
|
+
if (!isInputElement(input) || !input.form || event.target !== input.form) return;
|
|
139
|
+
const timer = setTimeout(() => {
|
|
140
|
+
timers.delete(timer);
|
|
141
|
+
if (stopped || event.defaultPrevented) return;
|
|
142
|
+
context.set("value", normalizeWith(prop, prop("defaultValue") ?? ""));
|
|
143
|
+
if (input.isConnected) input.value = context.get("value");
|
|
144
|
+
}, 0);
|
|
145
|
+
timers.add(timer);
|
|
146
|
+
}, true);
|
|
147
|
+
return () => {
|
|
148
|
+
stopped = true;
|
|
149
|
+
release();
|
|
150
|
+
timers.forEach(clearTimeout);
|
|
151
|
+
timers.clear();
|
|
152
|
+
};
|
|
153
|
+
} }
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
const field = (event) => event.currentTarget ?? event.target;
|
|
157
|
+
const place = (target, caret) => {
|
|
158
|
+
try {
|
|
159
|
+
target.setSelectionRange?.(caret, caret);
|
|
160
|
+
} catch {}
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Caret semantics: the real input owns caret and selection; arrows, Home/End, Backspace/Delete and shift-selection are native.
|
|
164
|
+
* The visual caret cell follows selectionStart (clamped to the last cell when full); a non-collapsed selection marks its cells.
|
|
165
|
+
* A plain tap/click collapses the caret to the end of the value, because input text geometry does not line up with the cells.
|
|
166
|
+
*/
|
|
167
|
+
function connectPasswordInput(service, normalize) {
|
|
168
|
+
const { context, prop, send, scope } = service;
|
|
169
|
+
const value = context.get("value"), length = prop("length") ?? 6, masked = !!prop("mask"), focused = context.get("focused");
|
|
170
|
+
const disabled = !!prop("disabled"), readOnly = !!prop("readOnly"), locked = disabled || readOnly, selection = context.get("selection");
|
|
171
|
+
const label = prop("label") || DEFAULT_LABEL[prop("mode") ?? "password"], hint = prop("hint") ?? "";
|
|
172
|
+
const id = scope.id ?? "";
|
|
173
|
+
const part = (name) => ({
|
|
174
|
+
"data-scope": "password-input",
|
|
175
|
+
"data-part": name
|
|
176
|
+
});
|
|
177
|
+
const ranged = selection.end > selection.start, caretIndex = Math.min(selection.start, length - 1);
|
|
178
|
+
const cells = Array.from({ length }, (_, index) => {
|
|
179
|
+
const char = value[index] ?? "";
|
|
180
|
+
return {
|
|
181
|
+
index,
|
|
182
|
+
char,
|
|
183
|
+
display: char ? masked ? "•" : char : "",
|
|
184
|
+
filled: !!char,
|
|
185
|
+
caret: focused && !disabled && !ranged && index === caretIndex,
|
|
186
|
+
selected: focused && ranged && index >= selection.start && index < selection.end
|
|
187
|
+
};
|
|
188
|
+
});
|
|
189
|
+
const select = (start, end) => {
|
|
190
|
+
const from = Math.max(0, start), current = context.get("selection"), to = Math.max(from, end);
|
|
191
|
+
if (current.start !== from || current.end !== to) context.set("selection", {
|
|
192
|
+
start: from,
|
|
193
|
+
end: to
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
const track = (event) => {
|
|
197
|
+
const target = field(event), start = target.selectionStart ?? target.value.length;
|
|
198
|
+
select(start, target.selectionEnd ?? start);
|
|
199
|
+
};
|
|
200
|
+
/** Propose, then write the effective value back so a rejected or filtered edit never lingers in the DOM. */
|
|
201
|
+
const commit = (target, proposed, caret) => {
|
|
202
|
+
if (!locked) context.set("value", proposed);
|
|
203
|
+
const accepted = context.get("value");
|
|
204
|
+
if (target.value !== accepted) {
|
|
205
|
+
target.value = accepted;
|
|
206
|
+
place(target, Math.min(caret, accepted.length));
|
|
207
|
+
}
|
|
208
|
+
select(target.selectionStart ?? accepted.length, target.selectionEnd ?? accepted.length);
|
|
209
|
+
queueMicrotask(() => {
|
|
210
|
+
if (target.isConnected !== false && target.value !== context.get("value")) {
|
|
211
|
+
target.value = context.get("value");
|
|
212
|
+
place(target, context.get("value").length);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
};
|
|
216
|
+
return {
|
|
217
|
+
value,
|
|
218
|
+
length,
|
|
219
|
+
masked,
|
|
220
|
+
focused,
|
|
221
|
+
complete: value.length === length,
|
|
222
|
+
disabled,
|
|
223
|
+
readOnly,
|
|
224
|
+
label,
|
|
225
|
+
hint,
|
|
226
|
+
cells,
|
|
227
|
+
setValue: (next) => send({
|
|
228
|
+
type: "VALUE.SET",
|
|
229
|
+
value: next
|
|
230
|
+
}),
|
|
231
|
+
clear: () => send({
|
|
232
|
+
type: "VALUE.SET",
|
|
233
|
+
value: ""
|
|
234
|
+
}),
|
|
235
|
+
getRootProps: () => normalize.element({
|
|
236
|
+
...part("root"),
|
|
237
|
+
"data-disabled": dataAttr(disabled),
|
|
238
|
+
"data-readonly": dataAttr(readOnly),
|
|
239
|
+
"data-focused": dataAttr(focused),
|
|
240
|
+
"data-complete": dataAttr(value.length === length),
|
|
241
|
+
"data-masked": dataAttr(masked)
|
|
242
|
+
}),
|
|
243
|
+
getInputProps: () => normalize.input({
|
|
244
|
+
...part("input"),
|
|
245
|
+
id: inputId(id),
|
|
246
|
+
type: masked ? "password" : "text",
|
|
247
|
+
value,
|
|
248
|
+
name: prop("name"),
|
|
249
|
+
form: prop("form"),
|
|
250
|
+
disabled,
|
|
251
|
+
readOnly,
|
|
252
|
+
required: prop("required"),
|
|
253
|
+
maxLength: length,
|
|
254
|
+
pattern: `${prop("charset") === "alphanumeric" ? "[0-9A-Za-z]" : "[0-9]"}{${length}}`,
|
|
255
|
+
inputMode: prop("charset") === "numeric" ? "numeric" : "text",
|
|
256
|
+
autoComplete: prop("autoComplete"),
|
|
257
|
+
enterKeyHint: prop("enterKeyHint"),
|
|
258
|
+
autoCapitalize: "off",
|
|
259
|
+
autoCorrect: "off",
|
|
260
|
+
spellCheck: "false",
|
|
261
|
+
"aria-label": label,
|
|
262
|
+
"aria-describedby": hintId(id),
|
|
263
|
+
onChange(event) {
|
|
264
|
+
const target = field(event), caret = target.selectionStart ?? target.value.length;
|
|
265
|
+
if (locked) {
|
|
266
|
+
commit(target, context.get("value"), caret);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
commit(target, normalizeWith(prop, target.value), normalizeWith(prop, target.value.slice(0, caret)).length);
|
|
270
|
+
},
|
|
271
|
+
onPaste(event) {
|
|
272
|
+
event.preventDefault();
|
|
273
|
+
if (locked) return;
|
|
274
|
+
const target = field(event), start = target.selectionStart ?? value.length;
|
|
275
|
+
const merged = mergePasswordInputPaste(context.get("value"), event.clipboardData?.getData("text") ?? "", {
|
|
276
|
+
start,
|
|
277
|
+
end: target.selectionEnd ?? start
|
|
278
|
+
}, length, prop("charset"));
|
|
279
|
+
commit(target, merged.value, merged.caret);
|
|
280
|
+
},
|
|
281
|
+
onClick(event) {
|
|
282
|
+
const target = field(event);
|
|
283
|
+
if (target.selectionStart === target.selectionEnd) place(target, target.value.length);
|
|
284
|
+
track(event);
|
|
285
|
+
},
|
|
286
|
+
onKeyUp: track,
|
|
287
|
+
onSelect: track,
|
|
288
|
+
onFocus(event) {
|
|
289
|
+
context.set("focused", true);
|
|
290
|
+
track(event);
|
|
291
|
+
},
|
|
292
|
+
onBlur() {
|
|
293
|
+
context.set("focused", false);
|
|
294
|
+
}
|
|
295
|
+
}),
|
|
296
|
+
getCellsProps: () => normalize.element({
|
|
297
|
+
...part("cells"),
|
|
298
|
+
"aria-hidden": "true"
|
|
299
|
+
}),
|
|
300
|
+
getCellProps: (cell) => normalize.element({
|
|
301
|
+
...part("cell"),
|
|
302
|
+
"data-index": cell.index,
|
|
303
|
+
"data-filled": dataAttr(cell.filled),
|
|
304
|
+
"data-caret": dataAttr(cell.caret),
|
|
305
|
+
"data-selected": dataAttr(cell.selected),
|
|
306
|
+
"data-disabled": dataAttr(disabled)
|
|
307
|
+
}),
|
|
308
|
+
getHintProps: () => normalize.element({
|
|
309
|
+
...part("hint"),
|
|
310
|
+
id: hintId(id)
|
|
311
|
+
})
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
//#endregion
|
|
315
|
+
export { PASSWORD_INPUT_CHARSETS, PASSWORD_INPUT_DEFAULT_LENGTH, PASSWORD_INPUT_MASK_GLYPH, PASSWORD_INPUT_MAX_LENGTH, PASSWORD_INPUT_MIN_LENGTH, PASSWORD_INPUT_MODES, PASSWORD_INPUT_PARTS, connectPasswordInput, createPasswordInputProps, mergePasswordInputPaste, normalizePasswordInputLength, normalizePasswordInputValue, passwordInputMachine };
|
|
316
|
+
|
|
317
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/password-input/password-input.ts"],"sourcesContent":["import { createMachine, type Machine, type Service } from \"@zag-js/core\";\nimport { addDomEvent, dataAttr, isInputElement } from \"@zag-js/dom-query\";\nimport type { NormalizeProps, PropTypes } from \"@zag-js/types\";\n\n/**\n * PasswordInput 密码格 / 验证码:一个真实 `<input>` 承载焦点、选区、粘贴、读屏与表单,\n * 格子只是 `aria-hidden` 的呈现层。\n *\n * Core 持有:长度/字符集归一化、受控提议与拒绝写回、粘贴合并、掩码、选区→高亮格、\n * 一次填满一次 onComplete、表单 reset 恢复默认值。Core 从不提交表单、也不校验验证码。\n */\n\n/** Stable DOM parts shared by every adapter. */\nexport const PASSWORD_INPUT_PARTS = [\"root\", \"input\", \"cells\", \"cell\", \"hint\"] as const;\nexport const PASSWORD_INPUT_DEFAULT_LENGTH = 6;\n/** Inclusive length bounds; out-of-range or non-integer lengths are clamped / rounded down. */\nexport const PASSWORD_INPUT_MIN_LENGTH = 1;\nexport const PASSWORD_INPUT_MAX_LENGTH = 12;\n/** Character kept by each charset. Full-width digits/letters are folded to ASCII first. */\nexport const PASSWORD_INPUT_CHARSETS = [\"numeric\", \"alphanumeric\"] as const;\nexport const PASSWORD_INPUT_MODES = [\"password\", \"otp\"] as const;\n/** Decorative glyph drawn in a filled cell while masked. */\nexport const PASSWORD_INPUT_MASK_GLYPH = \"•\";\n\nexport type PasswordInputPart = (typeof PASSWORD_INPUT_PARTS)[number];\nexport type PasswordInputCharset = (typeof PASSWORD_INPUT_CHARSETS)[number];\nexport type PasswordInputMode = (typeof PASSWORD_INPUT_MODES)[number];\nexport type PasswordInputAutoComplete = \"off\" | \"one-time-code\" | \"current-password\" | \"new-password\";\nexport interface PasswordInputValueChangeDetails { value: string }\nexport interface PasswordInputCompleteDetails { value: string }\n/** One presentational cell. `char` is the real character; adapters render `display`. */\nexport interface PasswordInputCell { index: number; char: string; display: string; filled: boolean; caret: boolean; selected: boolean }\n\nexport interface QwPasswordInputProps {\n /** Instance identity; adapters generate one when omitted. */\n id?: string;\n /** Number of cells; integer 1–12, default 6. */\n length?: number;\n /** Controlled value. Edits are proposed through onValueChange; the shown value only changes when the owner accepts. */\n value?: string;\n /** Uncontrolled initial value, also the value a form reset restores. Default \"\". */\n defaultValue?: string;\n /** Proposed value after an edit, paste or reset (already filtered and truncated). */\n onValueChange?: (details: PasswordInputValueChangeDetails) => void;\n /** The effective value just became full. Fires once per fill; never submits or verifies anything. */\n onComplete?: (details: PasswordInputCompleteDetails) => void;\n /** Allowed characters: numeric (default) keeps 0–9, alphanumeric keeps A–Z/a–z/0–9. */\n charset?: PasswordInputCharset;\n /** password (default) masks by default and turns autofill off; otp shows plain text and asks for one-time-code autofill. */\n mode?: PasswordInputMode;\n /** Draw dots instead of characters; the real input becomes type=\"password\". Default: true for password, false for otp. */\n mask?: boolean;\n /** Overrides the autocomplete token chosen by mode. */\n autoComplete?: PasswordInputAutoComplete;\n /** Accessible name of the real input; defaults to 密码 / 验证码 by mode. */\n label?: string;\n /** Spoken length description; defaults to \"6 位数字\" / \"6 位字母或数字\". */\n hint?: string;\n disabled?: boolean;\n readOnly?: boolean;\n required?: boolean;\n /** Form field name; the real input submits the value under it. */\n name?: string;\n /** Owner form id, as the native form attribute. */\n form?: string;\n enterKeyHint?: \"done\" | \"go\" | \"next\" | \"send\" | \"enter\";\n}\n\nconst DEFAULT_LABEL: Record<PasswordInputMode, string> = { password: \"密码\", otp: \"验证码\" };\nconst text = (value: unknown): string => (typeof value === \"string\" ? value.trim() : \"\");\n\n/** Clamp to an integer in 1–12; anything unusable falls back to 6. */\nexport function normalizePasswordInputLength(length?: number): number {\n if (typeof length !== \"number\" || !Number.isFinite(length)) return PASSWORD_INPUT_DEFAULT_LENGTH;\n return Math.min(PASSWORD_INPUT_MAX_LENGTH, Math.max(PASSWORD_INPUT_MIN_LENGTH, Math.floor(length)));\n}\n\n/** Fold full-width ASCII (IME digits like \"123\") to half-width, drop disallowed characters, then truncate. Never throws. */\nexport function normalizePasswordInputValue(value: unknown, length?: number, charset: PasswordInputCharset = \"numeric\"): string {\n if (typeof value !== \"string\" && typeof value !== \"number\") return \"\";\n const allowed = charset === \"alphanumeric\" ? /[0-9A-Za-z]/ : /[0-9]/;\n let result = \"\";\n for (const raw of String(value)) {\n const code = raw.charCodeAt(0), char = code >= 0xff01 && code <= 0xff5e ? String.fromCharCode(code - 0xfee0) : raw;\n if (allowed.test(char)) result += char;\n }\n return result.slice(0, normalizePasswordInputLength(length));\n}\n\n/**\n * Merge pasted text into the current value. A paste that alone fills every cell (after filtering)\n * replaces the whole value — the usual \"paste the code from SMS\" case; otherwise it replaces the selection.\n * Returns the next value and the caret after the inserted text.\n */\nexport function mergePasswordInputPaste(current: string, pasted: string, selection: { start: number; end: number }, length?: number, charset?: PasswordInputCharset): { value: string; caret: number } {\n const size = normalizePasswordInputLength(length), clean = normalizePasswordInputValue(pasted, PASSWORD_INPUT_MAX_LENGTH * 4, charset);\n if (clean.length >= size) return { value: clean.slice(0, size), caret: size };\n const start = Math.max(0, Math.min(selection.start, current.length)), end = Math.max(start, Math.min(selection.end, current.length));\n const value = normalizePasswordInputValue(current.slice(0, start) + clean + current.slice(end), size, charset);\n return { value, caret: Math.min(value.length, start + clean.length) };\n}\n\nexport function createPasswordInputProps(props: QwPasswordInputProps = {}): QwPasswordInputProps {\n const mode: PasswordInputMode = props.mode === \"otp\" ? \"otp\" : \"password\", charset: PasswordInputCharset = props.charset === \"alphanumeric\" ? \"alphanumeric\" : \"numeric\";\n const length = normalizePasswordInputLength(props.length);\n return {\n ...props, mode, charset, length,\n mask: typeof props.mask === \"boolean\" ? props.mask : mode === \"password\",\n label: text(props.label) || DEFAULT_LABEL[mode],\n hint: text(props.hint) || `${length} 位${charset === \"numeric\" ? \"数字\" : \"字母或数字\"}`,\n autoComplete: props.autoComplete ?? (mode === \"otp\" ? \"one-time-code\" : \"off\"),\n disabled: !!props.disabled, readOnly: !!props.readOnly, required: !!props.required,\n };\n}\n\ninterface Selection { start: number; end: number }\nexport interface QwPasswordInputSchema {\n props: QwPasswordInputProps;\n context: { value: string; selection: Selection; focused: boolean };\n refs: { completed: boolean };\n state: \"idle\";\n action: \"setValue\" | \"notifyComplete\";\n effect: \"trackForm\";\n /** Programmatic proposal (api.setValue / clear). DOM handlers write context synchronously instead. */\n event: { type: \"VALUE.SET\"; value: string };\n}\nexport type QwPasswordInputMachine = Machine<QwPasswordInputSchema>;\nexport type QwPasswordInputService = Service<QwPasswordInputSchema>;\n\nconst normalizeWith = (prop: QwPasswordInputService[\"prop\"], value: unknown) => normalizePasswordInputValue(value, prop(\"length\"), prop(\"charset\"));\nconst inputId = (id: string) => `password-input:${id}:input`;\nconst hintId = (id: string) => `password-input:${id}:hint`;\n\nexport const passwordInputMachine: QwPasswordInputMachine = createMachine<QwPasswordInputSchema>({\n props: ({ props }) => createPasswordInputProps(props),\n initialState: () => \"idle\",\n context: ({ bindable, prop }) => ({\n value: bindable(() => ({\n defaultValue: normalizeWith(prop, prop(\"defaultValue\") ?? \"\"),\n value: prop(\"value\") === undefined ? undefined : normalizeWith(prop, prop(\"value\")),\n onChange: (value) => prop(\"onValueChange\")?.({ value }),\n })),\n selection: bindable<Selection>(() => ({ defaultValue: { start: 0, end: 0 }, hash: (value) => `${value.start}:${value.end}` })),\n focused: bindable(() => ({ defaultValue: false })),\n }),\n // A value that is already full on mount is not a fresh completion.\n refs: ({ prop }) => ({ completed: normalizeWith(prop, prop(\"value\") ?? prop(\"defaultValue\") ?? \"\").length === prop(\"length\") }),\n watch({ track, context, prop, action }) {\n track([() => context.get(\"value\"), () => prop(\"length\")], () => action([\"notifyComplete\"]));\n },\n effects: [\"trackForm\"],\n on: { \"VALUE.SET\": { actions: [\"setValue\"] } },\n states: { idle: {} },\n implementations: {\n actions: {\n setValue({ event, context, prop }) {\n if (prop(\"disabled\") || prop(\"readOnly\")) return;\n context.set(\"value\", normalizeWith(prop, event.value));\n },\n notifyComplete({ context, prop, refs }) {\n const value = context.get(\"value\"), full = value.length === prop(\"length\");\n if (full && !refs.get(\"completed\")) { refs.set(\"completed\", true); prop(\"onComplete\")?.({ value }); } else if (!full) refs.set(\"completed\", false);\n },\n },\n effects: {\n /**\n * Form reset. Reset does not bubble, so listen in the document capture phase and match the input's current form owner:\n * re-parenting, the form attribute and re-rendered inputs need no rebinding. Native reset runs after listeners, so the\n * default is restored on the next task.\n */\n trackForm({ scope, context, prop }) {\n const doc = scope.getDoc();\n let stopped = false;\n const timers = new Set<ReturnType<typeof setTimeout>>();\n const release = addDomEvent(doc, \"reset\", (event) => {\n const input = scope.getById<HTMLInputElement>(inputId(scope.id ?? \"\"));\n if (!isInputElement(input) || !input.form || event.target !== input.form) return;\n const timer = setTimeout(() => {\n timers.delete(timer);\n if (stopped || event.defaultPrevented) return;\n // Reset is a form operation, not a user edit: like native reset it restores the default even when disabled or readOnly.\n context.set(\"value\", normalizeWith(prop, prop(\"defaultValue\") ?? \"\"));\n // Write the effective value back: a rejected controlled reset or an unchanged default must not keep the browser's reset text.\n if (input.isConnected) input.value = context.get(\"value\");\n }, 0);\n timers.add(timer);\n }, true);\n return () => { stopped = true; release(); timers.forEach(clearTimeout); timers.clear(); };\n },\n },\n },\n});\n\nexport interface QwPasswordInputApi<T extends PropTypes = PropTypes> {\n value: string;\n length: number;\n masked: boolean;\n focused: boolean;\n complete: boolean;\n disabled: boolean;\n readOnly: boolean;\n label: string;\n hint: string;\n cells: PasswordInputCell[];\n /** Propose a value programmatically (normalized; ignored when disabled or readOnly). */\n setValue(value: string): void;\n /** Propose \"\" (ignored when disabled or readOnly). */\n clear(): void;\n getRootProps(): T[\"element\"];\n getInputProps(): T[\"input\"];\n getCellsProps(): T[\"element\"];\n getCellProps(cell: PasswordInputCell): T[\"element\"];\n getHintProps(): T[\"element\"];\n}\n\ninterface FieldTarget { value: string; selectionStart: number | null; selectionEnd: number | null; isConnected?: boolean; setSelectionRange?(start: number, end: number): void }\ninterface FieldEvent { currentTarget?: unknown; target?: unknown }\ninterface PasteEvent extends FieldEvent { clipboardData?: { getData(type: string): string } | null; preventDefault(): void }\nconst field = (event: FieldEvent): FieldTarget => (event.currentTarget ?? event.target) as FieldTarget;\nconst place = (target: FieldTarget, caret: number) => { try { target.setSelectionRange?.(caret, caret); } catch { /* Detached or non-text input. */ } };\n\n/**\n * Caret semantics: the real input owns caret and selection; arrows, Home/End, Backspace/Delete and shift-selection are native.\n * The visual caret cell follows selectionStart (clamped to the last cell when full); a non-collapsed selection marks its cells.\n * A plain tap/click collapses the caret to the end of the value, because input text geometry does not line up with the cells.\n */\nexport function connectPasswordInput<T extends PropTypes>(service: QwPasswordInputService, normalize: NormalizeProps<T>): QwPasswordInputApi<T> {\n const { context, prop, send, scope } = service;\n const value = context.get(\"value\"), length = prop(\"length\") ?? PASSWORD_INPUT_DEFAULT_LENGTH, masked = !!prop(\"mask\"), focused = context.get(\"focused\");\n const disabled = !!prop(\"disabled\"), readOnly = !!prop(\"readOnly\"), locked = disabled || readOnly, selection = context.get(\"selection\");\n const label = prop(\"label\") || DEFAULT_LABEL[prop(\"mode\") ?? \"password\"], hint = prop(\"hint\") ?? \"\";\n const id = scope.id ?? \"\";\n const part = (name: PasswordInputPart) => ({ \"data-scope\": \"password-input\", \"data-part\": name });\n const ranged = selection.end > selection.start, caretIndex = Math.min(selection.start, length - 1);\n const cells = Array.from({ length }, (_, index): PasswordInputCell => {\n const char = value[index] ?? \"\";\n return { index, char, display: char ? (masked ? PASSWORD_INPUT_MASK_GLYPH : char) : \"\", filled: !!char, caret: focused && !disabled && !ranged && index === caretIndex, selected: focused && ranged && index >= selection.start && index < selection.end };\n });\n // Handlers write context synchronously (as Input does): adapter sends are deferred, but write-back must see the accepted value now.\n const select = (start: number, end: number) => { const from = Math.max(0, start), current = context.get(\"selection\"), to = Math.max(from, end); if (current.start !== from || current.end !== to) context.set(\"selection\", { start: from, end: to }); };\n const track = (event: FieldEvent) => { const target = field(event), start = target.selectionStart ?? target.value.length; select(start, target.selectionEnd ?? start); };\n /** Propose, then write the effective value back so a rejected or filtered edit never lingers in the DOM. */\n const commit = (target: FieldTarget, proposed: string, caret: number) => {\n if (!locked) context.set(\"value\", proposed);\n const accepted = context.get(\"value\");\n if (target.value !== accepted) { target.value = accepted; place(target, Math.min(caret, accepted.length)); }\n select(target.selectionStart ?? accepted.length, target.selectionEnd ?? accepted.length);\n queueMicrotask(() => { if (target.isConnected !== false && target.value !== context.get(\"value\")) { target.value = context.get(\"value\"); place(target, context.get(\"value\").length); } });\n };\n return {\n value, length, masked, focused, complete: value.length === length, disabled, readOnly, label, hint, cells,\n setValue: (next) => send({ type: \"VALUE.SET\", value: next }),\n clear: () => send({ type: \"VALUE.SET\", value: \"\" }),\n getRootProps: () => normalize.element({ ...part(\"root\"), \"data-disabled\": dataAttr(disabled), \"data-readonly\": dataAttr(readOnly), \"data-focused\": dataAttr(focused), \"data-complete\": dataAttr(value.length === length), \"data-masked\": dataAttr(masked) }),\n getInputProps: () => normalize.input({\n ...part(\"input\"), id: inputId(id), type: masked ? \"password\" : \"text\", value, name: prop(\"name\"), form: prop(\"form\"),\n disabled, readOnly, required: prop(\"required\"), maxLength: length, pattern: `${prop(\"charset\") === \"alphanumeric\" ? \"[0-9A-Za-z]\" : \"[0-9]\"}{${length}}`,\n inputMode: prop(\"charset\") === \"numeric\" ? \"numeric\" : \"text\", autoComplete: prop(\"autoComplete\"), enterKeyHint: prop(\"enterKeyHint\"),\n autoCapitalize: \"off\", autoCorrect: \"off\", spellCheck: \"false\", \"aria-label\": label, \"aria-describedby\": hintId(id),\n onChange(event: FieldEvent) {\n const target = field(event), caret = target.selectionStart ?? target.value.length;\n if (locked) { commit(target, context.get(\"value\"), caret); return; }\n commit(target, normalizeWith(prop, target.value), normalizeWith(prop, target.value.slice(0, caret)).length);\n },\n onPaste(event: PasteEvent) {\n event.preventDefault();\n if (locked) return;\n const target = field(event), start = target.selectionStart ?? value.length;\n const merged = mergePasswordInputPaste(context.get(\"value\"), event.clipboardData?.getData(\"text\") ?? \"\", { start, end: target.selectionEnd ?? start }, length, prop(\"charset\"));\n commit(target, merged.value, merged.caret);\n },\n onClick(event: FieldEvent) {\n const target = field(event);\n if (target.selectionStart === target.selectionEnd) place(target, target.value.length);\n track(event);\n },\n onKeyUp: track, onSelect: track,\n onFocus(event: FieldEvent) { context.set(\"focused\", true); track(event); },\n onBlur() { context.set(\"focused\", false); },\n }),\n getCellsProps: () => normalize.element({ ...part(\"cells\"), \"aria-hidden\": \"true\" }),\n getCellProps: (cell) => normalize.element({ ...part(\"cell\"), \"data-index\": cell.index, \"data-filled\": dataAttr(cell.filled), \"data-caret\": dataAttr(cell.caret), \"data-selected\": dataAttr(cell.selected), \"data-disabled\": dataAttr(disabled) }),\n getHintProps: () => normalize.element({ ...part(\"hint\"), id: hintId(id) }),\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { type Machine, type Service } from "@zag-js/core";
|
|
2
|
+
import type { NormalizeProps, PropTypes } from "@zag-js/types";
|
|
3
|
+
/**
|
|
4
|
+
* PasswordInput 密码格 / 验证码:一个真实 `<input>` 承载焦点、选区、粘贴、读屏与表单,
|
|
5
|
+
* 格子只是 `aria-hidden` 的呈现层。
|
|
6
|
+
*
|
|
7
|
+
* Core 持有:长度/字符集归一化、受控提议与拒绝写回、粘贴合并、掩码、选区→高亮格、
|
|
8
|
+
* 一次填满一次 onComplete、表单 reset 恢复默认值。Core 从不提交表单、也不校验验证码。
|
|
9
|
+
*/
|
|
10
|
+
/** Stable DOM parts shared by every adapter. */
|
|
11
|
+
export declare const PASSWORD_INPUT_PARTS: readonly ["root", "input", "cells", "cell", "hint"];
|
|
12
|
+
export declare const PASSWORD_INPUT_DEFAULT_LENGTH = 6;
|
|
13
|
+
/** Inclusive length bounds; out-of-range or non-integer lengths are clamped / rounded down. */
|
|
14
|
+
export declare const PASSWORD_INPUT_MIN_LENGTH = 1;
|
|
15
|
+
export declare const PASSWORD_INPUT_MAX_LENGTH = 12;
|
|
16
|
+
/** Character kept by each charset. Full-width digits/letters are folded to ASCII first. */
|
|
17
|
+
export declare const PASSWORD_INPUT_CHARSETS: readonly ["numeric", "alphanumeric"];
|
|
18
|
+
export declare const PASSWORD_INPUT_MODES: readonly ["password", "otp"];
|
|
19
|
+
/** Decorative glyph drawn in a filled cell while masked. */
|
|
20
|
+
export declare const PASSWORD_INPUT_MASK_GLYPH = "\u2022";
|
|
21
|
+
export type PasswordInputPart = (typeof PASSWORD_INPUT_PARTS)[number];
|
|
22
|
+
export type PasswordInputCharset = (typeof PASSWORD_INPUT_CHARSETS)[number];
|
|
23
|
+
export type PasswordInputMode = (typeof PASSWORD_INPUT_MODES)[number];
|
|
24
|
+
export type PasswordInputAutoComplete = "off" | "one-time-code" | "current-password" | "new-password";
|
|
25
|
+
export interface PasswordInputValueChangeDetails {
|
|
26
|
+
value: string;
|
|
27
|
+
}
|
|
28
|
+
export interface PasswordInputCompleteDetails {
|
|
29
|
+
value: string;
|
|
30
|
+
}
|
|
31
|
+
/** One presentational cell. `char` is the real character; adapters render `display`. */
|
|
32
|
+
export interface PasswordInputCell {
|
|
33
|
+
index: number;
|
|
34
|
+
char: string;
|
|
35
|
+
display: string;
|
|
36
|
+
filled: boolean;
|
|
37
|
+
caret: boolean;
|
|
38
|
+
selected: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface QwPasswordInputProps {
|
|
41
|
+
/** Instance identity; adapters generate one when omitted. */
|
|
42
|
+
id?: string;
|
|
43
|
+
/** Number of cells; integer 1–12, default 6. */
|
|
44
|
+
length?: number;
|
|
45
|
+
/** Controlled value. Edits are proposed through onValueChange; the shown value only changes when the owner accepts. */
|
|
46
|
+
value?: string;
|
|
47
|
+
/** Uncontrolled initial value, also the value a form reset restores. Default "". */
|
|
48
|
+
defaultValue?: string;
|
|
49
|
+
/** Proposed value after an edit, paste or reset (already filtered and truncated). */
|
|
50
|
+
onValueChange?: (details: PasswordInputValueChangeDetails) => void;
|
|
51
|
+
/** The effective value just became full. Fires once per fill; never submits or verifies anything. */
|
|
52
|
+
onComplete?: (details: PasswordInputCompleteDetails) => void;
|
|
53
|
+
/** Allowed characters: numeric (default) keeps 0–9, alphanumeric keeps A–Z/a–z/0–9. */
|
|
54
|
+
charset?: PasswordInputCharset;
|
|
55
|
+
/** password (default) masks by default and turns autofill off; otp shows plain text and asks for one-time-code autofill. */
|
|
56
|
+
mode?: PasswordInputMode;
|
|
57
|
+
/** Draw dots instead of characters; the real input becomes type="password". Default: true for password, false for otp. */
|
|
58
|
+
mask?: boolean;
|
|
59
|
+
/** Overrides the autocomplete token chosen by mode. */
|
|
60
|
+
autoComplete?: PasswordInputAutoComplete;
|
|
61
|
+
/** Accessible name of the real input; defaults to 密码 / 验证码 by mode. */
|
|
62
|
+
label?: string;
|
|
63
|
+
/** Spoken length description; defaults to "6 位数字" / "6 位字母或数字". */
|
|
64
|
+
hint?: string;
|
|
65
|
+
disabled?: boolean;
|
|
66
|
+
readOnly?: boolean;
|
|
67
|
+
required?: boolean;
|
|
68
|
+
/** Form field name; the real input submits the value under it. */
|
|
69
|
+
name?: string;
|
|
70
|
+
/** Owner form id, as the native form attribute. */
|
|
71
|
+
form?: string;
|
|
72
|
+
enterKeyHint?: "done" | "go" | "next" | "send" | "enter";
|
|
73
|
+
}
|
|
74
|
+
/** Clamp to an integer in 1–12; anything unusable falls back to 6. */
|
|
75
|
+
export declare function normalizePasswordInputLength(length?: number): number;
|
|
76
|
+
/** Fold full-width ASCII (IME digits like "123") to half-width, drop disallowed characters, then truncate. Never throws. */
|
|
77
|
+
export declare function normalizePasswordInputValue(value: unknown, length?: number, charset?: PasswordInputCharset): string;
|
|
78
|
+
/**
|
|
79
|
+
* Merge pasted text into the current value. A paste that alone fills every cell (after filtering)
|
|
80
|
+
* replaces the whole value — the usual "paste the code from SMS" case; otherwise it replaces the selection.
|
|
81
|
+
* Returns the next value and the caret after the inserted text.
|
|
82
|
+
*/
|
|
83
|
+
export declare function mergePasswordInputPaste(current: string, pasted: string, selection: {
|
|
84
|
+
start: number;
|
|
85
|
+
end: number;
|
|
86
|
+
}, length?: number, charset?: PasswordInputCharset): {
|
|
87
|
+
value: string;
|
|
88
|
+
caret: number;
|
|
89
|
+
};
|
|
90
|
+
export declare function createPasswordInputProps(props?: QwPasswordInputProps): QwPasswordInputProps;
|
|
91
|
+
interface Selection {
|
|
92
|
+
start: number;
|
|
93
|
+
end: number;
|
|
94
|
+
}
|
|
95
|
+
export interface QwPasswordInputSchema {
|
|
96
|
+
props: QwPasswordInputProps;
|
|
97
|
+
context: {
|
|
98
|
+
value: string;
|
|
99
|
+
selection: Selection;
|
|
100
|
+
focused: boolean;
|
|
101
|
+
};
|
|
102
|
+
refs: {
|
|
103
|
+
completed: boolean;
|
|
104
|
+
};
|
|
105
|
+
state: "idle";
|
|
106
|
+
action: "setValue" | "notifyComplete";
|
|
107
|
+
effect: "trackForm";
|
|
108
|
+
/** Programmatic proposal (api.setValue / clear). DOM handlers write context synchronously instead. */
|
|
109
|
+
event: {
|
|
110
|
+
type: "VALUE.SET";
|
|
111
|
+
value: string;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export type QwPasswordInputMachine = Machine<QwPasswordInputSchema>;
|
|
115
|
+
export type QwPasswordInputService = Service<QwPasswordInputSchema>;
|
|
116
|
+
export declare const passwordInputMachine: QwPasswordInputMachine;
|
|
117
|
+
export interface QwPasswordInputApi<T extends PropTypes = PropTypes> {
|
|
118
|
+
value: string;
|
|
119
|
+
length: number;
|
|
120
|
+
masked: boolean;
|
|
121
|
+
focused: boolean;
|
|
122
|
+
complete: boolean;
|
|
123
|
+
disabled: boolean;
|
|
124
|
+
readOnly: boolean;
|
|
125
|
+
label: string;
|
|
126
|
+
hint: string;
|
|
127
|
+
cells: PasswordInputCell[];
|
|
128
|
+
/** Propose a value programmatically (normalized; ignored when disabled or readOnly). */
|
|
129
|
+
setValue(value: string): void;
|
|
130
|
+
/** Propose "" (ignored when disabled or readOnly). */
|
|
131
|
+
clear(): void;
|
|
132
|
+
getRootProps(): T["element"];
|
|
133
|
+
getInputProps(): T["input"];
|
|
134
|
+
getCellsProps(): T["element"];
|
|
135
|
+
getCellProps(cell: PasswordInputCell): T["element"];
|
|
136
|
+
getHintProps(): T["element"];
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Caret semantics: the real input owns caret and selection; arrows, Home/End, Backspace/Delete and shift-selection are native.
|
|
140
|
+
* The visual caret cell follows selectionStart (clamped to the last cell when full); a non-collapsed selection marks its cells.
|
|
141
|
+
* A plain tap/click collapses the caret to the end of the value, because input text geometry does not line up with the cells.
|
|
142
|
+
*/
|
|
143
|
+
export declare function connectPasswordInput<T extends PropTypes>(service: QwPasswordInputService, normalize: NormalizeProps<T>): QwPasswordInputApi<T>;
|
|
144
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { pickerMachine } from "./picker-machine.js";
|
|
2
|
+
export type { QwPickerSchema, QwPickerService, QwPickerMachine } from "./picker-machine.js";
|
|
3
|
+
export { createPickerProps, connectPicker, getPickerAccessibleName, getPickerDataAttrs, isPickerColumns, isPickerValue, resolvePickerColumns, resolvePickerMode, } from "./picker.js";
|
|
4
|
+
export type { QwPickerProps, QwPickerApi, QwPickerMachineProps, QwResolvedPickerColumn, QwResolvedPickerOption } from "./picker.js";
|
|
5
|
+
export { PickerSelectionModel, getPickerDiagnostics, getPickerFirstValue, getPickerKeyboardTarget, getPickerOptionIndex, isPickerOptionSelectable, } from "./selection-model.js";
|
|
6
|
+
export type { PickerChangeDetails, PickerColumn, PickerColumnSignal, PickerColumnToken, PickerDiagnostic, PickerGeometry, PickerKeyboardMove, PickerMode, PickerOption, PickerScrollCommand, PickerSelectionNotice, PickerSelectionProps, PickerSelectionSnapshot, PickerValue, } from "./selection-model.js";
|
|
7
|
+
export { attachPickerColumn, connectPickerColumn } from "./platform.js";
|
|
8
|
+
export type { PickerBridgeCommand, PickerBridgeNotice, PickerColumnBinding, PickerColumnController, PickerColumnHost, PickerColumnSurface, PickerRawEventFacts, } from "./platform.js";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { _ as isPickerOptionSelectable, a as getPickerAccessibleName, c as isPickerValue, d as pickerMachine, f as PickerSelectionModel, g as getPickerOptionIndex, h as getPickerKeyboardTarget, i as createPickerProps, l as resolvePickerColumns, m as getPickerFirstValue, n as connectPickerColumn, o as getPickerDataAttrs, p as getPickerDiagnostics, r as connectPicker, s as isPickerColumns, t as attachPickerColumn, u as resolvePickerMode } from "../picker-BykFntQN.js";
|
|
2
|
+
export { PickerSelectionModel, attachPickerColumn, connectPicker, connectPickerColumn, createPickerProps, getPickerAccessibleName, getPickerDataAttrs, getPickerDiagnostics, getPickerFirstValue, getPickerKeyboardTarget, getPickerOptionIndex, isPickerColumns, isPickerOptionSelectable, isPickerValue, pickerMachine, resolvePickerColumns, resolvePickerMode };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Machine, Service } from "@zag-js/core";
|
|
2
|
+
import { popupMachine } from "../popup/popup.js";
|
|
3
|
+
import { PickerSelectionModel, type PickerColumnSignal, type PickerColumnToken, type PickerKeyboardMove, type PickerMode, type PickerSelectionNotice, type PickerSelectionProps, type PickerSelectionSnapshot, type PickerValue } from "./selection-model.js";
|
|
4
|
+
/**
|
|
5
|
+
* Picker 的单一 Foundation service(T002)。
|
|
6
|
+
*
|
|
7
|
+
* 不是第二个 machine:它是锁定的上游 `popupMachine` 的显式 typed 投影 —— props / context / states /
|
|
8
|
+
* transitions / actions / effects 全部来自同一个 Popup machine,只追加选择会话的 context、refs 与事件。
|
|
9
|
+
* 工具栏(popup)形态的开合、焦点、滚动锁、portal id 与模态票据都由这一个 service 承担;
|
|
10
|
+
* 内嵌(inline)形态把所有上游打开路径守卫为 closed:没有锁、没有 portal、没有模态登记。
|
|
11
|
+
*
|
|
12
|
+
* 迁移自 specs/054-mobile-picker/spike/machine-foundation.ts(T002 + T035–T039 证据),
|
|
13
|
+
* 仅删去 spike 的 PREVIEW.SET 探针并增加离散选择/键盘/列焦点事件。
|
|
14
|
+
*/
|
|
15
|
+
type PopupSchema = typeof popupMachine extends Machine<infer Schema> ? Schema : never;
|
|
16
|
+
export interface QwPickerSchema extends Omit<PopupSchema, "action" | "event" | "effect"> {
|
|
17
|
+
props: PopupSchema["props"] & {
|
|
18
|
+
mode: PickerMode;
|
|
19
|
+
selection?: PickerSelectionProps;
|
|
20
|
+
onSelectionNotice?: (event: PickerSelectionNotice) => void;
|
|
21
|
+
};
|
|
22
|
+
context: {
|
|
23
|
+
rendered: PopupSchema["context"]["rendered"];
|
|
24
|
+
triggerValue: PopupSchema["context"]["triggerValue"];
|
|
25
|
+
preview: PickerValue;
|
|
26
|
+
selectionSnapshot: PickerSelectionSnapshot | null;
|
|
27
|
+
surfaceTokens: readonly (PickerColumnToken | undefined)[];
|
|
28
|
+
};
|
|
29
|
+
refs: {
|
|
30
|
+
/** 构造时固定的形态;切换形态须重新挂载。 */
|
|
31
|
+
mode: PickerMode;
|
|
32
|
+
selectionModel: PickerSelectionModel;
|
|
33
|
+
selectionTransport: {
|
|
34
|
+
notice?: (event: PickerSelectionNotice) => void;
|
|
35
|
+
closeIntent: boolean;
|
|
36
|
+
session: number;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
action: PopupSchema["action"] | "selectionInput";
|
|
40
|
+
effect: PopupSchema["effect"] | "selectionLifetime";
|
|
41
|
+
event: PopupSchema["event"] | {
|
|
42
|
+
type: "SELECTION.MOUNT";
|
|
43
|
+
columnIndex: number;
|
|
44
|
+
} | {
|
|
45
|
+
type: "SELECTION.UNMOUNT";
|
|
46
|
+
token: PickerColumnToken;
|
|
47
|
+
} | {
|
|
48
|
+
type: "SELECTION.INPUT";
|
|
49
|
+
token: PickerColumnToken;
|
|
50
|
+
signal: PickerColumnSignal;
|
|
51
|
+
} | {
|
|
52
|
+
type: "SELECTION.SELECT";
|
|
53
|
+
columnIndex: number;
|
|
54
|
+
optionIndex: number;
|
|
55
|
+
} | {
|
|
56
|
+
type: "SELECTION.MOVE";
|
|
57
|
+
columnIndex: number;
|
|
58
|
+
move: PickerKeyboardMove;
|
|
59
|
+
} | {
|
|
60
|
+
type: "SELECTION.FOCUS";
|
|
61
|
+
columnIndex: number;
|
|
62
|
+
} | {
|
|
63
|
+
type: "SELECTION.CONFIRM";
|
|
64
|
+
} | {
|
|
65
|
+
type: "SELECTION.CANCEL";
|
|
66
|
+
} | {
|
|
67
|
+
type: "SELECTION.CLOSE";
|
|
68
|
+
session: number;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export type QwPickerService = Service<QwPickerSchema>;
|
|
72
|
+
export type QwPickerMachine = Machine<QwPickerSchema>;
|
|
73
|
+
/** Typed pinned upstream projection: one service, the original two modal states. */
|
|
74
|
+
export declare const pickerMachine: QwPickerMachine;
|
|
75
|
+
/** Project the one service for the upstream Popup connector; never instantiate another. */
|
|
76
|
+
export declare function toPopupService(service: Service<QwPickerSchema>): Service<PopupSchema>;
|
|
77
|
+
type SelectionEvent = QwPickerSchema["event"];
|
|
78
|
+
/** Keep input ordering when the machine scheduler has not consumed an earlier selection yet. */
|
|
79
|
+
export declare function sendPickerSelection(service: QwPickerService, event: SelectionEvent): void;
|
|
80
|
+
export {};
|