@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.
Files changed (402) hide show
  1. package/README.md +21 -37
  2. package/dist/action-sheet/action-sheet.d.ts +110 -0
  3. package/dist/action-sheet/index.d.ts +2 -0
  4. package/dist/action-sheet/index.js +2 -0
  5. package/dist/action-sheet-Cvcr1S_m.js +183 -0
  6. package/dist/action-sheet-Cvcr1S_m.js.map +1 -0
  7. package/dist/area-picker/area-picker.d.ts +113 -0
  8. package/dist/area-picker/index.d.ts +1 -0
  9. package/dist/area-picker/index.js +348 -0
  10. package/dist/area-picker/index.js.map +1 -0
  11. package/dist/aria-description-CimD2M_C.js +13 -0
  12. package/dist/aria-description-CimD2M_C.js.map +1 -0
  13. package/dist/aria-description.d.ts +2 -0
  14. package/dist/avatar/avatar-load.d.ts +76 -0
  15. package/dist/avatar/avatar.d.ts +22 -5
  16. package/dist/avatar/index.d.ts +1 -0
  17. package/dist/avatar/index.js +57 -18
  18. package/dist/avatar/index.js.map +1 -1
  19. package/dist/avatar-load-w9dq05oV.js +139 -0
  20. package/dist/avatar-load-w9dq05oV.js.map +1 -0
  21. package/dist/back-top/back-top.d.ts +77 -0
  22. package/dist/back-top/index.d.ts +1 -0
  23. package/dist/back-top/index.js +189 -0
  24. package/dist/back-top/index.js.map +1 -0
  25. package/dist/badge/badge.d.ts +9 -0
  26. package/dist/badge/index.js +14 -1
  27. package/dist/badge/index.js.map +1 -1
  28. package/dist/button/button.d.ts +13 -0
  29. package/dist/button/index.js +3 -5
  30. package/dist/button/index.js.map +1 -1
  31. package/dist/calendar/calendar.d.ts +100 -0
  32. package/dist/calendar/index.d.ts +1 -0
  33. package/dist/calendar/index.js +2 -0
  34. package/dist/calendar-CmpK3stz.js +363 -0
  35. package/dist/calendar-CmpK3stz.js.map +1 -0
  36. package/dist/card/card.d.ts +68 -0
  37. package/dist/card/index.d.ts +2 -0
  38. package/dist/card/index.js +2 -0
  39. package/dist/card-DRtWa6Cr.js +107 -0
  40. package/dist/card-DRtWa6Cr.js.map +1 -0
  41. package/dist/cascader/cascader.d.ts +115 -0
  42. package/dist/cascader/index.d.ts +1 -0
  43. package/dist/cascader/index.js +2 -0
  44. package/dist/cascader-D-xMjs7P.js +333 -0
  45. package/dist/cascader-D-xMjs7P.js.map +1 -0
  46. package/dist/cell/cell.d.ts +41 -0
  47. package/dist/cell/index.d.ts +1 -0
  48. package/dist/cell/index.js +43 -0
  49. package/dist/cell/index.js.map +1 -0
  50. package/dist/check-list/check-list.d.ts +78 -0
  51. package/dist/check-list/index.d.ts +2 -0
  52. package/dist/check-list/index.js +2 -0
  53. package/dist/check-list-BvZHQt5w.js +60 -0
  54. package/dist/check-list-BvZHQt5w.js.map +1 -0
  55. package/dist/checkbox/checkbox.d.ts +59 -0
  56. package/dist/checkbox/index.d.ts +2 -0
  57. package/dist/checkbox/index.js +2 -0
  58. package/dist/checkbox-BlndJrz5.js +127 -0
  59. package/dist/checkbox-BlndJrz5.js.map +1 -0
  60. package/dist/checkbox-group/checkbox-group.d.ts +86 -0
  61. package/dist/checkbox-group/index.d.ts +3 -0
  62. package/dist/checkbox-group/index.js +2 -0
  63. package/dist/checkbox-group-gGfsTv-e.js +223 -0
  64. package/dist/checkbox-group-gGfsTv-e.js.map +1 -0
  65. package/dist/choice-group-C4c9m0qe.js +373 -0
  66. package/dist/choice-group-C4c9m0qe.js.map +1 -0
  67. package/dist/circle/circle.d.ts +97 -0
  68. package/dist/circle/index.d.ts +1 -0
  69. package/dist/circle/index.js +119 -0
  70. package/dist/circle/index.js.map +1 -0
  71. package/dist/collapse/collapse.d.ts +98 -0
  72. package/dist/collapse/index.d.ts +2 -0
  73. package/dist/collapse/index.js +2 -0
  74. package/dist/collapse-B969h7-H.js +226 -0
  75. package/dist/collapse-B969h7-H.js.map +1 -0
  76. package/dist/countdown/countdown.d.ts +114 -0
  77. package/dist/countdown/index.d.ts +1 -0
  78. package/dist/countdown/index.js +309 -0
  79. package/dist/countdown/index.js.map +1 -0
  80. package/dist/date-picker/date-picker.d.ts +168 -0
  81. package/dist/date-picker/index.d.ts +1 -0
  82. package/dist/date-picker/index.js +401 -0
  83. package/dist/date-picker/index.js.map +1 -0
  84. package/dist/descriptions/descriptions.d.ts +69 -0
  85. package/dist/descriptions/index.d.ts +1 -0
  86. package/dist/descriptions/index.js +86 -0
  87. package/dist/descriptions/index.js.map +1 -0
  88. package/dist/dialog/dialog.d.ts +32 -3
  89. package/dist/dialog/index.js +58 -3
  90. package/dist/dialog/index.js.map +1 -1
  91. package/dist/divider/divider.d.ts +18 -2
  92. package/dist/divider/index.js +20 -2
  93. package/dist/divider/index.js.map +1 -1
  94. package/dist/dropdown/dropdown.d.ts +172 -0
  95. package/dist/dropdown/index.d.ts +2 -0
  96. package/dist/dropdown/index.js +467 -0
  97. package/dist/dropdown/index.js.map +1 -0
  98. package/dist/ellipsis/ellipsis-layout.d.ts +34 -0
  99. package/dist/ellipsis/ellipsis.d.ts +88 -0
  100. package/dist/ellipsis/index.d.ts +2 -0
  101. package/dist/ellipsis/index.js +320 -0
  102. package/dist/ellipsis/index.js.map +1 -0
  103. package/dist/empty/empty-illustration.d.ts +3 -0
  104. package/dist/empty/empty.d.ts +43 -1
  105. package/dist/empty/index.js +2 -21
  106. package/dist/empty-CAgyvhUI.js +166 -0
  107. package/dist/empty-CAgyvhUI.js.map +1 -0
  108. package/dist/field/field.d.ts +95 -0
  109. package/dist/field/index.d.ts +2 -0
  110. package/dist/field/index.js +2 -0
  111. package/dist/field-DMHGIMZF.js +241 -0
  112. package/dist/field-DMHGIMZF.js.map +1 -0
  113. package/dist/floating-bubble/floating-bubble.d.ts +116 -0
  114. package/dist/floating-bubble/index.d.ts +1 -0
  115. package/dist/floating-bubble/index.js +333 -0
  116. package/dist/floating-bubble/index.js.map +1 -0
  117. package/dist/floating-panel/floating-panel.d.ts +80 -0
  118. package/dist/floating-panel/index.d.ts +2 -0
  119. package/dist/floating-panel/index.js +2 -0
  120. package/dist/floating-panel-Cbm5njBJ.js +363 -0
  121. package/dist/floating-panel-Cbm5njBJ.js.map +1 -0
  122. package/dist/footer/footer.d.ts +94 -0
  123. package/dist/footer/index.d.ts +1 -0
  124. package/dist/footer/index.js +154 -0
  125. package/dist/footer/index.js.map +1 -0
  126. package/dist/form/connect.d.ts +6 -0
  127. package/dist/form/form.d.ts +100 -0
  128. package/dist/form/ids.d.ts +14 -0
  129. package/dist/form/index.d.ts +13 -0
  130. package/dist/form/index.js +3 -0
  131. package/dist/form/methods.d.ts +5 -0
  132. package/dist/form/model.d.ts +24 -0
  133. package/dist/form/native.d.ts +25 -0
  134. package/dist/form/rules.d.ts +40 -0
  135. package/dist/form/ssr.d.ts +7 -0
  136. package/dist/form/types.d.ts +150 -0
  137. package/dist/form-BAGkIEMY.js +1354 -0
  138. package/dist/form-BAGkIEMY.js.map +1 -0
  139. package/dist/format-BDUY0C_c.js +89 -0
  140. package/dist/format-BDUY0C_c.js.map +1 -0
  141. package/dist/gesture-_SRHIvBm.js +30 -0
  142. package/dist/gesture-_SRHIvBm.js.map +1 -0
  143. package/dist/grid/grid-item.d.ts +22 -0
  144. package/dist/grid/grid.d.ts +42 -0
  145. package/dist/grid/index.d.ts +2 -0
  146. package/dist/grid/index.js +73 -0
  147. package/dist/grid/index.js.map +1 -0
  148. package/dist/icon/icon.d.ts +37 -1
  149. package/dist/icon/identity.d.ts +3 -0
  150. package/dist/icon/index.js +2 -20
  151. package/dist/icon-Cgdbnk1u.js +77 -0
  152. package/dist/icon-Cgdbnk1u.js.map +1 -0
  153. package/dist/image/image-load.d.ts +24 -0
  154. package/dist/image/image.d.ts +45 -8
  155. package/dist/image/index.d.ts +1 -0
  156. package/dist/image/index.js +116 -14
  157. package/dist/image/index.js.map +1 -1
  158. package/dist/image-preview/image-preview.d.ts +124 -0
  159. package/dist/image-preview/index.d.ts +1 -0
  160. package/dist/image-preview/index.js +450 -0
  161. package/dist/image-preview/index.js.map +1 -0
  162. package/dist/index-bar/index-bar.d.ts +121 -0
  163. package/dist/index-bar/index.d.ts +1 -0
  164. package/dist/index-bar/index.js +386 -0
  165. package/dist/index-bar/index.js.map +1 -0
  166. package/dist/index.d.ts +107 -14
  167. package/dist/index.js +148 -14
  168. package/dist/index.js.map +1 -0
  169. package/dist/infinite-scroll/index.d.ts +1 -0
  170. package/dist/infinite-scroll/index.js +248 -0
  171. package/dist/infinite-scroll/index.js.map +1 -0
  172. package/dist/infinite-scroll/infinite-scroll.d.ts +95 -0
  173. package/dist/input/index.js +181 -22
  174. package/dist/input/index.js.map +1 -1
  175. package/dist/input/input.d.ts +44 -1
  176. package/dist/layer-IpFrl5--.js +414 -0
  177. package/dist/layer-IpFrl5--.js.map +1 -0
  178. package/dist/layout-BRSqi4AQ.js +43 -0
  179. package/dist/layout-BRSqi4AQ.js.map +1 -0
  180. package/dist/layout.d.ts +13 -0
  181. package/dist/list/index.d.ts +2 -0
  182. package/dist/list/index.js +2 -0
  183. package/dist/list/list.d.ts +30 -0
  184. package/dist/list-6-iz-Irh.js +28 -0
  185. package/dist/list-6-iz-Irh.js.map +1 -0
  186. package/dist/loading/index.js +45 -2
  187. package/dist/loading/index.js.map +1 -1
  188. package/dist/loading/loading.d.ts +45 -2
  189. package/dist/modal-dialog-machine-CF9_z3Dm.js +487 -0
  190. package/dist/modal-dialog-machine-CF9_z3Dm.js.map +1 -0
  191. package/dist/modal-dialog-machine.d.ts +21 -0
  192. package/dist/modal-dismissable-lifetime.d.ts +21 -0
  193. package/dist/modal-registry.d.ts +24 -0
  194. package/dist/modal-render-hydration.d.ts +35 -0
  195. package/dist/modal-render-runtime.d.ts +19 -0
  196. package/dist/modal-render-scope.d.ts +63 -0
  197. package/dist/nav-bar/index.d.ts +1 -0
  198. package/dist/nav-bar/index.js +115 -0
  199. package/dist/nav-bar/index.js.map +1 -0
  200. package/dist/nav-bar/nav-bar.d.ts +76 -0
  201. package/dist/notice-bar/index.d.ts +2 -0
  202. package/dist/notice-bar/index.js +2 -0
  203. package/dist/notice-bar/notice-bar.d.ts +103 -0
  204. package/dist/notice-bar-DMpwLvRb.js +231 -0
  205. package/dist/notice-bar-DMpwLvRb.js.map +1 -0
  206. package/dist/notify/index.d.ts +1 -0
  207. package/dist/notify/index.js +251 -0
  208. package/dist/notify/index.js.map +1 -0
  209. package/dist/notify/notify.d.ts +123 -0
  210. package/dist/number-keyboard/index.d.ts +1 -0
  211. package/dist/number-keyboard/index.js +333 -0
  212. package/dist/number-keyboard/index.js.map +1 -0
  213. package/dist/number-keyboard/number-keyboard.d.ts +141 -0
  214. package/dist/page-indicator/index.d.ts +1 -0
  215. package/dist/page-indicator/index.js +138 -0
  216. package/dist/page-indicator/index.js.map +1 -0
  217. package/dist/page-indicator/page-indicator.d.ts +93 -0
  218. package/dist/pagination/index.d.ts +1 -0
  219. package/dist/pagination/index.js +198 -0
  220. package/dist/pagination/index.js.map +1 -0
  221. package/dist/pagination/pagination.d.ts +125 -0
  222. package/dist/password-input/index.d.ts +1 -0
  223. package/dist/password-input/index.js +317 -0
  224. package/dist/password-input/index.js.map +1 -0
  225. package/dist/password-input/password-input.d.ts +144 -0
  226. package/dist/picker/index.d.ts +8 -0
  227. package/dist/picker/index.js +2 -0
  228. package/dist/picker/picker-machine.d.ts +80 -0
  229. package/dist/picker/picker.d.ts +121 -0
  230. package/dist/picker/platform.d.ts +84 -0
  231. package/dist/picker/selection-connect.d.ts +90 -0
  232. package/dist/picker/selection-model.d.ts +211 -0
  233. package/dist/picker-BykFntQN.js +1675 -0
  234. package/dist/picker-BykFntQN.js.map +1 -0
  235. package/dist/popover/index.d.ts +3 -0
  236. package/dist/popover/index.js +253 -0
  237. package/dist/popover/index.js.map +1 -0
  238. package/dist/popover/layer.d.ts +44 -0
  239. package/dist/popover/popover.d.ts +104 -0
  240. package/dist/popover/positioning.d.ts +132 -0
  241. package/dist/popup/index.d.ts +2 -0
  242. package/dist/popup/index.js +2 -0
  243. package/dist/popup/popup.d.ts +60 -0
  244. package/dist/popup-z5d8IYCt.js +576 -0
  245. package/dist/popup-z5d8IYCt.js.map +1 -0
  246. package/dist/portal-BHCNTWhg.js +78 -0
  247. package/dist/portal-BHCNTWhg.js.map +1 -0
  248. package/dist/portal.d.ts +61 -0
  249. package/dist/price/format.d.ts +19 -0
  250. package/dist/price/index.d.ts +1 -0
  251. package/dist/price/index.js +154 -0
  252. package/dist/price/index.js.map +1 -0
  253. package/dist/price/price.d.ts +88 -0
  254. package/dist/progress/index.d.ts +1 -0
  255. package/dist/progress/index.js +2 -0
  256. package/dist/progress/progress.d.ts +32 -0
  257. package/dist/progress-f3iYEfdA.js +37 -0
  258. package/dist/progress-f3iYEfdA.js.map +1 -0
  259. package/dist/pull-refresh/index.d.ts +1 -0
  260. package/dist/pull-refresh/index.js +414 -0
  261. package/dist/pull-refresh/index.js.map +1 -0
  262. package/dist/pull-refresh/pull-refresh.d.ts +118 -0
  263. package/dist/radio-group/index.d.ts +3 -0
  264. package/dist/radio-group/index.js +2 -0
  265. package/dist/radio-group/radio-group.d.ts +78 -0
  266. package/dist/radio-group-BOIMuqnS.js +230 -0
  267. package/dist/radio-group-BOIMuqnS.js.map +1 -0
  268. package/dist/rate/index.d.ts +1 -0
  269. package/dist/rate/index.js +2 -0
  270. package/dist/rate/rate-form.d.ts +28 -0
  271. package/dist/rate/rate.d.ts +144 -0
  272. package/dist/rate-5XsU2QkY.js +631 -0
  273. package/dist/rate-5XsU2QkY.js.map +1 -0
  274. package/dist/reorder/index.d.ts +1 -0
  275. package/dist/reorder/index.js +720 -0
  276. package/dist/reorder/index.js.map +1 -0
  277. package/dist/reorder/reorder.d.ts +190 -0
  278. package/dist/result/index.d.ts +1 -0
  279. package/dist/result/index.js +123 -0
  280. package/dist/result/index.js.map +1 -0
  281. package/dist/result/result.d.ts +88 -0
  282. package/dist/safe-area/index.d.ts +1 -0
  283. package/dist/safe-area/index.js +42 -0
  284. package/dist/safe-area/index.js.map +1 -0
  285. package/dist/safe-area/safe-area.d.ts +28 -0
  286. package/dist/search-bar/index.js +3 -1
  287. package/dist/search-bar/index.js.map +1 -1
  288. package/dist/search-bar/search-bar.d.ts +4 -0
  289. package/dist/segmented/index.d.ts +2 -0
  290. package/dist/segmented/index.js +2 -0
  291. package/dist/segmented/segmented.d.ts +127 -0
  292. package/dist/segmented-BaU38_L4.js +367 -0
  293. package/dist/segmented-BaU38_L4.js.map +1 -0
  294. package/dist/selection-form-BOM_odUk.js +88 -0
  295. package/dist/selection-form-BOM_odUk.js.map +1 -0
  296. package/dist/selection-form.d.ts +27 -0
  297. package/dist/selection-ivXqO1eu.js +55 -0
  298. package/dist/selection-ivXqO1eu.js.map +1 -0
  299. package/dist/selection.d.ts +29 -0
  300. package/dist/selector/choice-group.d.ts +114 -0
  301. package/dist/selector/index.d.ts +2 -0
  302. package/dist/selector/index.js +2 -0
  303. package/dist/selector/selector.d.ts +90 -0
  304. package/dist/selector-BU2MMJ4N.js +93 -0
  305. package/dist/selector-BU2MMJ4N.js.map +1 -0
  306. package/dist/share-sheet/index.d.ts +1 -0
  307. package/dist/share-sheet/index.js +138 -0
  308. package/dist/share-sheet/index.js.map +1 -0
  309. package/dist/share-sheet/share-sheet.d.ts +139 -0
  310. package/dist/skeleton/index.js +51 -2
  311. package/dist/skeleton/index.js.map +1 -1
  312. package/dist/skeleton/skeleton.d.ts +34 -3
  313. package/dist/slider/index.d.ts +1 -0
  314. package/dist/slider/index.js +482 -0
  315. package/dist/slider/index.js.map +1 -0
  316. package/dist/slider/slider.d.ts +174 -0
  317. package/dist/space/index.d.ts +1 -0
  318. package/dist/space/index.js +52 -0
  319. package/dist/space/index.js.map +1 -0
  320. package/dist/space/space.d.ts +46 -0
  321. package/dist/statistic/index.d.ts +1 -0
  322. package/dist/statistic/index.js +152 -0
  323. package/dist/statistic/index.js.map +1 -0
  324. package/dist/statistic/statistic.d.ts +101 -0
  325. package/dist/stepper/index.js +62 -3
  326. package/dist/stepper/index.js.map +1 -1
  327. package/dist/stepper/stepper.d.ts +44 -4
  328. package/dist/steps/index.d.ts +1 -0
  329. package/dist/steps/index.js +72 -0
  330. package/dist/steps/index.js.map +1 -0
  331. package/dist/steps/steps.d.ts +67 -0
  332. package/dist/sticky/index.d.ts +1 -0
  333. package/dist/sticky/index.js +145 -0
  334. package/dist/sticky/index.js.map +1 -0
  335. package/dist/sticky/sticky.d.ts +70 -0
  336. package/dist/swipe-action/index.d.ts +1 -0
  337. package/dist/swipe-action/index.js +417 -0
  338. package/dist/swipe-action/index.js.map +1 -0
  339. package/dist/swipe-action/swipe-action.d.ts +148 -0
  340. package/dist/swiper/gesture.d.ts +32 -0
  341. package/dist/swiper/index.d.ts +1 -0
  342. package/dist/swiper/index.js +2 -0
  343. package/dist/swiper/swiper.d.ts +177 -0
  344. package/dist/swiper-BKivDkHp.js +550 -0
  345. package/dist/swiper-BKivDkHp.js.map +1 -0
  346. package/dist/switch/index.js +135 -10
  347. package/dist/switch/index.js.map +1 -1
  348. package/dist/switch/switch.d.ts +93 -6
  349. package/dist/tabbar/index.js +46 -1
  350. package/dist/tabbar/index.js.map +1 -1
  351. package/dist/tabbar/tabbar.d.ts +50 -0
  352. package/dist/tabs/index.js +22 -1
  353. package/dist/tabs/index.js.map +1 -1
  354. package/dist/tabs/tabs.d.ts +12 -8
  355. package/dist/tag/index.js +123 -2
  356. package/dist/tag/index.js.map +1 -1
  357. package/dist/tag/tag.d.ts +67 -1
  358. package/dist/textarea/index.d.ts +2 -0
  359. package/dist/textarea/index.js +2 -242
  360. package/dist/textarea/platform.d.ts +22 -0
  361. package/dist/textarea/textarea.d.ts +165 -46
  362. package/dist/textarea-qbnruDUG.js +775 -0
  363. package/dist/textarea-qbnruDUG.js.map +1 -0
  364. package/dist/time-picker/index.d.ts +1 -0
  365. package/dist/time-picker/index.js +228 -0
  366. package/dist/time-picker/index.js.map +1 -0
  367. package/dist/time-picker/time-picker.d.ts +83 -0
  368. package/dist/timeline/index.d.ts +1 -0
  369. package/dist/timeline/index.js +131 -0
  370. package/dist/timeline/index.js.map +1 -0
  371. package/dist/timeline/timeline.d.ts +79 -0
  372. package/dist/toast/index.js +47 -4
  373. package/dist/toast/index.js.map +1 -1
  374. package/dist/toast/toast.d.ts +26 -3
  375. package/dist/top-bar/index.js +16 -2
  376. package/dist/top-bar/index.js.map +1 -1
  377. package/dist/top-bar/top-bar.d.ts +13 -2
  378. package/dist/tree-select/index.d.ts +1 -0
  379. package/dist/tree-select/index.js +512 -0
  380. package/dist/tree-select/index.js.map +1 -0
  381. package/dist/tree-select/tree-select.d.ts +215 -0
  382. package/dist/typography/index.d.ts +1 -0
  383. package/dist/typography/index.js +82 -0
  384. package/dist/typography/index.js.map +1 -0
  385. package/dist/typography/typography.d.ts +54 -0
  386. package/dist/upload/index.d.ts +1 -0
  387. package/dist/upload/index.js +458 -0
  388. package/dist/upload/index.js.map +1 -0
  389. package/dist/upload/upload.d.ts +172 -0
  390. package/dist/virtual-list/index.d.ts +1 -0
  391. package/dist/virtual-list/index.js +445 -0
  392. package/dist/virtual-list/index.js.map +1 -0
  393. package/dist/virtual-list/virtual-list.d.ts +160 -0
  394. package/dist/visual-manifest.d.ts +6 -0
  395. package/dist/watermark/index.d.ts +1 -0
  396. package/dist/watermark/index.js +260 -0
  397. package/dist/watermark/index.js.map +1 -0
  398. package/dist/watermark/watermark.d.ts +106 -0
  399. package/package.json +399 -50
  400. package/dist/empty/index.js.map +0 -1
  401. package/dist/icon/index.js.map +0 -1
  402. package/dist/textarea/index.js.map +0 -1
@@ -1,10 +1,11 @@
1
+ import { a as getImageRequestStatus, i as createImageRequestFrames, n as connectAvatarLoad, r as createAvatarResourceBinding, t as avatarLoadMachine } from "../avatar-load-w9dq05oV.js";
1
2
  //#region src/avatar/avatar.ts
2
3
  /**
3
4
  * Avatar 的跨框架行为契约。
4
5
  *
5
- * Avatar 是无状态的 Prototype A:Core 归一化 props、推导降级模式和徽标状态,
6
+ * Core 归一化 props、推导降级模式和徽标状态;Zag 管图片请求生命周期。
6
7
  * 生成 root/image/badge 的 data 属性。React/Vue 适配器只投影内容通道并渲染固定 DOM。
7
- * 图片加载状态通过 `data-load-status` 属性由 CSS 声明式管理,不参与 Core 状态归一化。
8
+ * 图片请求生命周期由同目录的纯 Zag load machine 决定,适配器仅运输资源标量快照。
8
9
  */
9
10
  const AVATAR_SHAPES = ["circle", "square"];
10
11
  const AVATAR_SIZES = [
@@ -25,6 +26,17 @@ const AVATAR_LOAD_STATUS = [
25
26
  ];
26
27
  const AVATAR_BADGE_TYPES = ["dot", "number"];
27
28
  const BADGE_COUNT_MAX = 99;
29
+ /** Frameworks only report whether a content channel was supplied. */
30
+ function getAvatarContentPresence(props, channels) {
31
+ return {
32
+ hasImage: typeof props.src === "string" && !!props.src,
33
+ hasName: typeof props.name === "string" && !!props.name.trim(),
34
+ hasFallback: !!channels.fallback,
35
+ hasIcon: !!channels.icon,
36
+ hasChildren: !!channels.default,
37
+ hasBadge: props.badge != null
38
+ };
39
+ }
28
40
  const EMPTY_PRESENCE = {
29
41
  hasImage: false,
30
42
  hasName: false,
@@ -42,33 +54,35 @@ const EMPTY_PRESENCE = {
42
54
  * - 上限 2 字符
43
55
  * - 空字符串返回空字符串
44
56
  *
45
- * 使用 `Array.from()` 按码点迭代,不按 `charCodeAt` 截断。
57
+ * 按 grapheme 边界截取,不切开组合字符和 emoji 序列。
46
58
  */
47
59
  function extractInitials(name) {
48
60
  if (!name) return "";
49
- const chars = Array.from(name).filter((ch) => ch.trim() !== "");
61
+ const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" });
62
+ const chars = Array.from(segmenter.segment(name), ({ segment }) => segment).filter((ch) => ch.trim() !== "");
50
63
  if (chars.length === 0) return "";
51
- const isCjk = (ch) => /[\u{4E00}-\u{9FFF}\u{3040}-\u{30FF}\u{AC00}-\u{D7AF}]/u.test(ch);
64
+ const isCjk = (ch) => /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u.test(ch);
52
65
  if (chars.every(isCjk)) return chars[0];
53
- return chars.slice(0, 2).join("").toUpperCase();
66
+ const result = chars.slice(0, 2).join("").toUpperCase();
67
+ return Array.from(segmenter.segment(result), ({ segment }) => segment).slice(0, 2).join("");
54
68
  }
55
69
  /** 将公开输入和内容通道归一化为唯一的跨框架状态。 */
56
70
  function resolveAvatarState(props = {}, presence = EMPTY_PRESENCE) {
57
71
  const hasImage = presence.hasImage;
58
72
  const hasName = presence.hasName;
59
73
  const decorative = props.decorative ?? false;
60
- const bg = props.bg;
74
+ const bg = typeof props.bg === "string" ? props.bg : void 0;
61
75
  const displayMode = presence.hasChildren ? "children" : hasImage ? "image" : presence.hasIcon ? "icon" : hasName ? "initials" : "icon";
62
76
  const fallbackMode = presence.hasFallback ? "custom" : presence.hasIcon ? "icon" : hasName ? "initials" : "icon";
63
77
  const initials = hasName && props.name ? extractInitials(props.name) : void 0;
64
- const hasBadge = presence.hasBadge;
78
+ const hasBadge = presence.hasBadge && !!props.badge && AVATAR_BADGE_TYPES.includes(props.badge.type);
65
79
  const badgeType = hasBadge ? props.badge?.type : void 0;
66
80
  let badgeDisplayCount;
67
81
  let badgeOverflow = false;
68
82
  let badgeHidden = false;
69
83
  if (hasBadge && badgeType === "number") {
70
84
  const rawCount = props.badge?.count ?? 0;
71
- if (rawCount <= 0) {
85
+ if (typeof rawCount !== "number" || !Number.isFinite(rawCount) || rawCount <= 0) {
72
86
  badgeHidden = true;
73
87
  badgeDisplayCount = void 0;
74
88
  } else if (rawCount > 99) {
@@ -77,8 +91,8 @@ function resolveAvatarState(props = {}, presence = EMPTY_PRESENCE) {
77
91
  } else badgeDisplayCount = rawCount;
78
92
  }
79
93
  return {
80
- shape: props.shape ?? "circle",
81
- size: props.size ?? "m",
94
+ shape: AVATAR_SHAPES.includes(props.shape) ? props.shape : "circle",
95
+ size: AVATAR_SIZES.includes(props.size) ? props.size : "m",
82
96
  hasImage,
83
97
  hasName,
84
98
  decorative,
@@ -93,15 +107,14 @@ function resolveAvatarState(props = {}, presence = EMPTY_PRESENCE) {
93
107
  badgeHidden
94
108
  };
95
109
  }
96
- /** 将归一化结果转成 root 属性。 */
97
- function getAvatarRootProps(state) {
110
+ function getAvatarRootProps(state, options = {}) {
98
111
  const props = {
99
112
  "data-scope": "avatar",
100
113
  "data-part": "root",
101
114
  "data-shape": state.shape,
102
115
  "data-size": state.size,
103
116
  "data-display": state.displayMode,
104
- "data-load-status": state.displayMode === "image" ? "loading" : "error",
117
+ "data-load-status": state.displayMode === "image" ? options.loadStatus ?? "loading" : "error",
105
118
  "data-fallback": state.fallbackMode
106
119
  };
107
120
  if (state.decorative) {
@@ -112,7 +125,28 @@ function getAvatarRootProps(state) {
112
125
  props["data-has-badge"] = "true";
113
126
  props["data-badge-type"] = state.badgeType;
114
127
  }
115
- return props;
128
+ if (!state.decorative && state.hasName && options.name) {
129
+ const label = state.hasBadge && typeof options.badge?.ariaLabel === "string" ? options.badge.ariaLabel.trim() : "";
130
+ props["aria-label"] = label ? `${options.name}, ${label}` : options.name;
131
+ }
132
+ return {
133
+ ...props,
134
+ ...options.lang === void 0 ? {} : { lang: options.lang },
135
+ ...options.dir === void 0 ? {} : { dir: options.dir },
136
+ ...options.hidden === void 0 ? {} : { hidden: options.hidden }
137
+ };
138
+ }
139
+ function getAvatarImageDescriptor(state, src) {
140
+ return {
141
+ src: typeof src === "string" ? src : "",
142
+ active: state.displayMode === "image" && state.hasImage
143
+ };
144
+ }
145
+ function getAvatarRootStyle(state) {
146
+ return state.bg ? { "--qw-avatar-bg": state.bg } : void 0;
147
+ }
148
+ function getAvatarBadgeContent(state) {
149
+ return state.badgeType === "number" && state.hasBadge ? `${state.badgeDisplayCount}${state.badgeOverflow ? "+" : ""}` : "";
116
150
  }
117
151
  /** 将归一化结果转成图片包装元素的属性。 */
118
152
  function getAvatarImageProps(_state) {
@@ -137,14 +171,19 @@ function getAvatarFallbackProps(_state) {
137
171
  };
138
172
  }
139
173
  /** 将归一化结果转成徽标元素的属性。 */
140
- function getAvatarBadgeProps(state) {
141
- return {
174
+ function getAvatarBadgeProps(state, badge) {
175
+ const props = {
142
176
  "data-scope": "avatar",
143
177
  "data-part": "badge",
144
178
  "data-badge-type": state.badgeType ?? "dot"
145
179
  };
180
+ if (typeof badge?.ariaLabel === "string" && badge.ariaLabel.trim()) {
181
+ props.role = "img";
182
+ props["aria-label"] = badge.ariaLabel;
183
+ }
184
+ return props;
146
185
  }
147
186
  //#endregion
148
- export { AVATAR_BADGE_TYPES, AVATAR_LOAD_STATUS, AVATAR_PARTS, AVATAR_SHAPES, AVATAR_SIZES, BADGE_COUNT_MAX, extractInitials, getAvatarBadgeProps, getAvatarFallbackProps, getAvatarImageElementProps, getAvatarImageProps, getAvatarRootProps, resolveAvatarState };
187
+ export { AVATAR_BADGE_TYPES, AVATAR_LOAD_STATUS, AVATAR_PARTS, AVATAR_SHAPES, AVATAR_SIZES, BADGE_COUNT_MAX, avatarLoadMachine, connectAvatarLoad, createAvatarResourceBinding, createImageRequestFrames, extractInitials, getAvatarBadgeContent, getAvatarBadgeProps, getAvatarContentPresence, getAvatarFallbackProps, getAvatarImageDescriptor, getAvatarImageElementProps, getAvatarImageProps, getAvatarRootProps, getAvatarRootStyle, getImageRequestStatus, resolveAvatarState };
149
188
 
150
189
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/avatar/avatar.ts"],"sourcesContent":["/**\n * Avatar 的跨框架行为契约。\n *\n * Avatar 是无状态的 Prototype A:Core 归一化 props、推导降级模式和徽标状态,\n * 生成 root/image/badge 的 data 属性。React/Vue 适配器只投影内容通道并渲染固定 DOM。\n * 图片加载状态通过 `data-load-status` 属性由 CSS 声明式管理,不参与 Core 状态归一化。\n */\n\nexport const AVATAR_SHAPES = [\"circle\", \"square\"] as const;\nexport const AVATAR_SIZES = [\"s\", \"m\", \"l\"] as const;\nexport const AVATAR_PARTS = [\"root\", \"image\", \"fallback\", \"badge\"] as const;\nexport const AVATAR_LOAD_STATUS = [\"loading\", \"loaded\", \"error\"] as const;\nexport const AVATAR_BADGE_TYPES = [\"dot\", \"number\"] as const;\nexport const BADGE_COUNT_MAX = 99;\n\nexport type AvatarShape = (typeof AVATAR_SHAPES)[number];\nexport type AvatarSize = (typeof AVATAR_SIZES)[number];\nexport type AvatarPart = (typeof AVATAR_PARTS)[number];\nexport type AvatarLoadStatus = (typeof AVATAR_LOAD_STATUS)[number];\nexport type BadgeType = (typeof AVATAR_BADGE_TYPES)[number];\n\n/** 徽标配置(双栈共享标量,内容通道不进入 Core)。 */\nexport interface BadgeConfig {\n /** 徽标类型。 */\n type: BadgeType;\n /** 数字徽标的数值。 */\n count?: number;\n /** 徽标无障碍标签。 */\n ariaLabel?: string;\n}\n\n/** 双栈共享的标量 props;框架特有的内容通道不进入 Core。 */\nexport interface QwAvatarProps {\n /** 图片源地址。空字符串或 undefined 时不渲染 img,直接进入降级。 */\n src?: string;\n /** 名称,用于提取缩写和 aria-label。 */\n name?: string;\n /** 外形,默认 `circle`。 */\n shape?: AvatarShape;\n /** 尺寸,默认 `m`。 */\n size?: AvatarSize;\n /** 装饰模式(从辅助技术隐藏)。 */\n decorative?: boolean;\n /** 自定义背景色,设置后覆盖所有展示形式的默认背景色。 */\n bg?: string;\n /** 徽标配置。 */\n badge?: BadgeConfig;\n}\n\n/** 由各框架的内容通道存在性计算;不检查 DOM 或 VNode 内容。 */\nexport interface AvatarContentPresence {\n /** 是否提供有效 `src`(非空字符串)。 */\n hasImage: boolean;\n /** 是否提供 `name`。 */\n hasName: boolean;\n /** 是否提供自定义降级内容(`fallback` prop / `#fallback` slot)。 */\n hasFallback: boolean;\n /** 是否提供自定义图标内容(`icon` prop / `#icon` slot)。 */\n hasIcon: boolean;\n /** 是否提供子元素(`children` / 默认 slot)。 */\n hasChildren: boolean;\n /** 是否提供 `badge` 配置。 */\n hasBadge: boolean;\n}\n\n/** 内容展示模式,按优先级互斥推导:children > image > icon > name。 */\nexport type DisplayMode = \"children\" | \"image\" | \"icon\" | \"initials\";\n\n/** 图片加载失败时的降级展示模式。 */\nexport type FallbackMode = \"custom\" | \"initials\" | \"icon\";\n\n/** 适配器唯一可消费的归一化结果。 */\nexport interface ResolvedAvatarState {\n shape: AvatarShape;\n size: AvatarSize;\n hasImage: boolean;\n hasName: boolean;\n decorative: boolean;\n /** 用户传入的自定义背景色(undefined 时使用默认分色逻辑)。 */\n bg: string | undefined;\n /** 互斥展示模式:children > image > icon > name > default-icon。 */\n displayMode: DisplayMode;\n /** 图片加载失败时的降级模式(仅 displayMode=\"image\" 时有效)。 */\n fallbackMode: FallbackMode;\n initials: string | undefined;\n hasBadge: boolean;\n badgeType: BadgeType | undefined;\n badgeOverflow: boolean;\n badgeDisplayCount: number | undefined;\n badgeHidden: boolean;\n}\n\n/** Core 生成、由适配器覆盖到根元素的属性。 */\nexport interface AvatarRootProps {\n \"data-scope\": \"avatar\";\n \"data-part\": \"root\";\n \"data-shape\": AvatarShape;\n \"data-size\": AvatarSize;\n \"data-display\": DisplayMode;\n \"data-load-status\": AvatarLoadStatus;\n \"data-fallback\": FallbackMode;\n role?: \"img\";\n \"aria-label\"?: string;\n \"aria-hidden\"?: \"true\";\n \"data-has-badge\"?: \"true\";\n \"data-badge-type\"?: BadgeType;\n}\n\n/** Core 生成、由适配器覆盖到图片包装元素的属性。 */\nexport interface AvatarImageProps {\n \"data-scope\": \"avatar\";\n \"data-part\": \"image\";\n}\n\n/** Core 生成、由适配器覆盖到 img 元素的属性。 */\nexport interface AvatarImageElementProps {\n alt: \"\";\n loading: \"lazy\";\n}\n\n/** Core 生成、由适配器覆盖到降级内容元素的属性。 */\nexport interface AvatarFallbackProps {\n \"data-scope\": \"avatar\";\n \"data-part\": \"fallback\";\n \"aria-hidden\": \"true\";\n}\n\n/** Core 生成、由适配器覆盖到徽标元素的属性。 */\nexport interface AvatarBadgeProps {\n \"data-scope\": \"avatar\";\n \"data-part\": \"badge\";\n \"data-badge-type\": BadgeType;\n \"aria-label\"?: string;\n}\n\nconst EMPTY_PRESENCE: AvatarContentPresence = {\n hasImage: false,\n hasName: false,\n hasFallback: false,\n hasIcon: false,\n hasChildren: false,\n hasBadge: false,\n};\n\n/**\n * 从名称提取文字缩写。\n *\n * - 纯 CJK(汉字/假名/谚文)取首字\n * - 纯拉丁取前 2 字符大写\n * - 混合取前 2 字符\n * - 上限 2 字符\n * - 空字符串返回空字符串\n *\n * 使用 `Array.from()` 按码点迭代,不按 `charCodeAt` 截断。\n */\nexport function extractInitials(name: string): string {\n if (!name) return \"\";\n const chars = Array.from(name).filter((ch) => ch.trim() !== \"\");\n if (chars.length === 0) return \"\";\n\n const isCjk = (ch: string) => /[\\u{4E00}-\\u{9FFF}\\u{3040}-\\u{30FF}\\u{AC00}-\\u{D7AF}]/u.test(ch);\n const allCjk = chars.every(isCjk);\n\n if (allCjk) {\n return chars[0]!;\n }\n\n const result = chars.slice(0, 2).join(\"\").toUpperCase();\n return result;\n}\n\n/** 将公开输入和内容通道归一化为唯一的跨框架状态。 */\nexport function resolveAvatarState(\n props: QwAvatarProps = {},\n presence: AvatarContentPresence = EMPTY_PRESENCE,\n): ResolvedAvatarState {\n const hasImage = presence.hasImage;\n const hasName = presence.hasName;\n const decorative = props.decorative ?? false;\n const bg = props.bg;\n\n // 互斥展示优先级:children > image > icon > name > default-icon\n const displayMode: DisplayMode = presence.hasChildren\n ? \"children\"\n : hasImage\n ? \"image\"\n : presence.hasIcon\n ? \"icon\"\n : hasName\n ? \"initials\"\n : \"icon\"; // 默认:展示 User 图标\n\n // 图片加载失败时的降级模式(仅 displayMode=\"image\" 时有效)\n // 降级优先级:fallback prop > icon > name > default-icon\n const fallbackMode: FallbackMode = presence.hasFallback\n ? \"custom\"\n : presence.hasIcon\n ? \"icon\"\n : hasName\n ? \"initials\"\n : \"icon\";\n\n const initials = hasName && props.name ? extractInitials(props.name) : undefined;\n\n // 徽标逻辑\n const hasBadge = presence.hasBadge;\n const badgeType = hasBadge ? props.badge?.type : undefined;\n\n let badgeDisplayCount: number | undefined;\n let badgeOverflow = false;\n let badgeHidden = false;\n\n if (hasBadge && badgeType === \"number\") {\n const rawCount = props.badge?.count ?? 0;\n if (rawCount <= 0) {\n badgeHidden = true;\n badgeDisplayCount = undefined;\n } else if (rawCount > BADGE_COUNT_MAX) {\n badgeOverflow = true;\n badgeDisplayCount = BADGE_COUNT_MAX;\n } else {\n badgeDisplayCount = rawCount;\n }\n }\n\n return {\n shape: props.shape ?? \"circle\",\n size: props.size ?? \"m\",\n hasImage,\n hasName,\n decorative,\n bg,\n displayMode,\n fallbackMode,\n initials,\n hasBadge: hasBadge && !badgeHidden,\n badgeType,\n badgeOverflow,\n badgeDisplayCount,\n badgeHidden,\n };\n}\n\n/** 将归一化结果转成 root 属性。 */\nexport function getAvatarRootProps(state: ResolvedAvatarState): AvatarRootProps {\n const props: AvatarRootProps = {\n \"data-scope\": \"avatar\",\n \"data-part\": \"root\",\n \"data-shape\": state.shape,\n \"data-size\": state.size,\n \"data-display\": state.displayMode,\n \"data-load-status\": state.displayMode === \"image\" ? \"loading\" : \"error\",\n \"data-fallback\": state.fallbackMode,\n };\n\n // aria 策略:\n // - decorative → aria-hidden=\"true\" + role=\"img\"(AT 忽略)\n // - 有 name → role=\"img\" + aria-label(由适配器设置)\n // - 无 name 且非 decorative → 不设 role(避免 role-img-alt 违规)\n if (state.decorative) {\n props[\"aria-hidden\"] = \"true\";\n props[\"role\"] = \"img\";\n } else if (state.hasName) {\n props[\"role\"] = \"img\";\n // aria-label 由适配器从 name 设置(Core 不持有 name 的运行时值)\n }\n\n if (state.hasBadge && state.badgeType) {\n props[\"data-has-badge\"] = \"true\";\n props[\"data-badge-type\"] = state.badgeType;\n }\n\n return props;\n}\n\n/** 将归一化结果转成图片包装元素的属性。 */\nexport function getAvatarImageProps(_state: ResolvedAvatarState): AvatarImageProps {\n return {\n \"data-scope\": \"avatar\",\n \"data-part\": \"image\",\n };\n}\n\n/** 将归一化结果转成 img 元素的属性。 */\nexport function getAvatarImageElementProps(\n _state: ResolvedAvatarState,\n): AvatarImageElementProps {\n return {\n alt: \"\",\n loading: \"lazy\",\n };\n}\n\n/** 将归一化结果转成降级内容元素的属性。 */\nexport function getAvatarFallbackProps(\n _state: ResolvedAvatarState,\n): AvatarFallbackProps {\n return {\n \"data-scope\": \"avatar\",\n \"data-part\": \"fallback\",\n \"aria-hidden\": \"true\",\n };\n}\n\n/** 将归一化结果转成徽标元素的属性。 */\nexport function getAvatarBadgeProps(state: ResolvedAvatarState): AvatarBadgeProps {\n const props: AvatarBadgeProps = {\n \"data-scope\": \"avatar\",\n \"data-part\": \"badge\",\n \"data-badge-type\": state.badgeType ?? \"dot\",\n };\n\n // aria-label 来自 badge.ariaLabel,由适配器透传\n return props;\n}\n"],"mappings":";;;;;;;;AAQA,MAAa,gBAAgB,CAAC,UAAU,QAAQ;AAChD,MAAa,eAAe;CAAC;CAAK;CAAK;AAAG;AAC1C,MAAa,eAAe;CAAC;CAAQ;CAAS;CAAY;AAAO;AACjE,MAAa,qBAAqB;CAAC;CAAW;CAAU;AAAO;AAC/D,MAAa,qBAAqB,CAAC,OAAO,QAAQ;AAClD,MAAa,kBAAkB;AA0H/B,MAAM,iBAAwC;CAC5C,UAAU;CACV,SAAS;CACT,aAAa;CACb,SAAS;CACT,aAAa;CACb,UAAU;AACZ;;;;;;;;;;;;AAaA,SAAgB,gBAAgB,MAAsB;CACpD,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,QAAQ,MAAM,KAAK,IAAI,CAAC,CAAC,QAAQ,OAAO,GAAG,KAAK,MAAM,EAAE;CAC9D,IAAI,MAAM,WAAW,GAAG,OAAO;CAE/B,MAAM,SAAS,OAAe,yDAAyD,KAAK,EAAE;CAG9F,IAFe,MAAM,MAAM,KAElB,GACP,OAAO,MAAM;CAIf,OADe,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,YAC9B;AACd;;AAGA,SAAgB,mBACd,QAAuB,CAAC,GACxB,WAAkC,gBACb;CACrB,MAAM,WAAW,SAAS;CAC1B,MAAM,UAAU,SAAS;CACzB,MAAM,aAAa,MAAM,cAAc;CACvC,MAAM,KAAK,MAAM;CAGjB,MAAM,cAA2B,SAAS,cACtC,aACA,WACE,UACA,SAAS,UACP,SACA,UACE,aACA;CAIV,MAAM,eAA6B,SAAS,cACxC,WACA,SAAS,UACP,SACA,UACE,aACA;CAER,MAAM,WAAW,WAAW,MAAM,OAAO,gBAAgB,MAAM,IAAI,IAAI,KAAA;CAGvE,MAAM,WAAW,SAAS;CAC1B,MAAM,YAAY,WAAW,MAAM,OAAO,OAAO,KAAA;CAEjD,IAAI;CACJ,IAAI,gBAAgB;CACpB,IAAI,cAAc;CAElB,IAAI,YAAY,cAAc,UAAU;EACtC,MAAM,WAAW,MAAM,OAAO,SAAS;EACvC,IAAI,YAAY,GAAG;GACjB,cAAc;GACd,oBAAoB,KAAA;EACtB,OAAO,IAAI,WAAA,IAA4B;GACrC,gBAAgB;GAChB,oBAAA;EACF,OACE,oBAAoB;CAExB;CAEA,OAAO;EACL,OAAO,MAAM,SAAS;EACtB,MAAM,MAAM,QAAQ;EACpB;EACA;EACA;EACA;EACA;EACA;EACA;EACA,UAAU,YAAY,CAAC;EACvB;EACA;EACA;EACA;CACF;AACF;;AAGA,SAAgB,mBAAmB,OAA6C;CAC9E,MAAM,QAAyB;EAC7B,cAAc;EACd,aAAa;EACb,cAAc,MAAM;EACpB,aAAa,MAAM;EACnB,gBAAgB,MAAM;EACtB,oBAAoB,MAAM,gBAAgB,UAAU,YAAY;EAChE,iBAAiB,MAAM;CACzB;CAMA,IAAI,MAAM,YAAY;EACpB,MAAM,iBAAiB;EACvB,MAAM,UAAU;CAClB,OAAO,IAAI,MAAM,SACf,MAAM,UAAU;CAIlB,IAAI,MAAM,YAAY,MAAM,WAAW;EACrC,MAAM,oBAAoB;EAC1B,MAAM,qBAAqB,MAAM;CACnC;CAEA,OAAO;AACT;;AAGA,SAAgB,oBAAoB,QAA+C;CACjF,OAAO;EACL,cAAc;EACd,aAAa;CACf;AACF;;AAGA,SAAgB,2BACd,QACyB;CACzB,OAAO;EACL,KAAK;EACL,SAAS;CACX;AACF;;AAGA,SAAgB,uBACd,QACqB;CACrB,OAAO;EACL,cAAc;EACd,aAAa;EACb,eAAe;CACjB;AACF;;AAGA,SAAgB,oBAAoB,OAA8C;CAQhF,OAAO;EANL,cAAc;EACd,aAAa;EACb,mBAAmB,MAAM,aAAa;CAI7B;AACb"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/avatar/avatar.ts"],"sourcesContent":["/**\n * Avatar 的跨框架行为契约。\n *\n * Core 归一化 props、推导降级模式和徽标状态;Zag 管图片请求生命周期。\n * 生成 root/image/badge 的 data 属性。React/Vue 适配器只投影内容通道并渲染固定 DOM。\n * 图片请求生命周期由同目录的纯 Zag load machine 决定,适配器仅运输资源标量快照。\n */\n\nexport const AVATAR_SHAPES = [\"circle\", \"square\"] as const;\nexport const AVATAR_SIZES = [\"s\", \"m\", \"l\"] as const;\nexport const AVATAR_PARTS = [\"root\", \"image\", \"fallback\", \"badge\"] as const;\nexport const AVATAR_LOAD_STATUS = [\"loading\", \"loaded\", \"error\"] as const;\nexport const AVATAR_BADGE_TYPES = [\"dot\", \"number\"] as const;\nexport const BADGE_COUNT_MAX = 99;\n\nexport type AvatarShape = (typeof AVATAR_SHAPES)[number];\nexport type AvatarSize = (typeof AVATAR_SIZES)[number];\nexport type AvatarPart = (typeof AVATAR_PARTS)[number];\nexport type AvatarLoadStatus = (typeof AVATAR_LOAD_STATUS)[number];\nexport type BadgeType = (typeof AVATAR_BADGE_TYPES)[number];\n\n/** 徽标配置(双栈共享标量,内容通道不进入 Core)。 */\nexport interface BadgeConfig {\n /** 徽标类型。 */\n type: BadgeType;\n /** 数字徽标的数值。 */\n count?: number;\n /** 徽标无障碍标签。 */\n ariaLabel?: string;\n}\n\n/** 双栈共享的标量 props;框架特有的内容通道不进入 Core。 */\nexport interface QwAvatarProps {\n /** 图片源地址。空字符串或 undefined 时不渲染 img,直接进入降级。 */\n src?: string;\n /** 名称,用于提取缩写和 aria-label。 */\n name?: string;\n /** 外形,默认 `circle`。 */\n shape?: AvatarShape;\n /** 尺寸,默认 `m`。 */\n size?: AvatarSize;\n /** 装饰模式(从辅助技术隐藏)。 */\n decorative?: boolean;\n /** 自定义背景色,设置后覆盖所有展示形式的默认背景色。 */\n bg?: string;\n /** 徽标配置。 */\n badge?: BadgeConfig;\n}\n\n/** 由各框架的内容通道存在性计算;不检查 DOM 或 VNode 内容。 */\nexport interface AvatarContentPresence {\n /** 是否提供有效 `src`(非空字符串)。 */\n hasImage: boolean;\n /** 是否提供 `name`。 */\n hasName: boolean;\n /** 是否提供自定义降级内容(`fallback` prop / `#fallback` slot)。 */\n hasFallback: boolean;\n /** 是否提供自定义图标内容(`icon` prop / `#icon` slot)。 */\n hasIcon: boolean;\n /** 是否提供子元素(`children` / 默认 slot)。 */\n hasChildren: boolean;\n /** 是否提供 `badge` 配置。 */\n hasBadge: boolean;\n}\n/** Frameworks only report whether a content channel was supplied. */\nexport function getAvatarContentPresence(props: QwAvatarProps, channels: Readonly<Record<string,boolean>>): AvatarContentPresence {\n return {hasImage:typeof props.src === \"string\" && !!props.src,hasName:typeof props.name === \"string\" && !!props.name.trim(),hasFallback:!!channels.fallback,hasIcon:!!channels.icon,hasChildren:!!channels.default,hasBadge:props.badge != null};\n}\n\n/** 内容展示模式,按优先级互斥推导:children > image > icon > name。 */\nexport type DisplayMode = \"children\" | \"image\" | \"icon\" | \"initials\";\n\n/** 图片加载失败时的降级展示模式。 */\nexport type FallbackMode = \"custom\" | \"initials\" | \"icon\";\n\n/** 适配器唯一可消费的归一化结果。 */\nexport interface ResolvedAvatarState {\n shape: AvatarShape;\n size: AvatarSize;\n hasImage: boolean;\n hasName: boolean;\n decorative: boolean;\n /** 用户传入的自定义背景色(undefined 时使用默认分色逻辑)。 */\n bg: string | undefined;\n /** 互斥展示模式:children > image > icon > name > default-icon。 */\n displayMode: DisplayMode;\n /** 图片加载失败时的降级模式(仅 displayMode=\"image\" 时有效)。 */\n fallbackMode: FallbackMode;\n initials: string | undefined;\n hasBadge: boolean;\n badgeType: BadgeType | undefined;\n badgeOverflow: boolean;\n badgeDisplayCount: number | undefined;\n badgeHidden: boolean;\n}\n\n/** Core 生成、由适配器覆盖到根元素的属性。 */\nexport interface AvatarRootProps {\n \"data-scope\": \"avatar\";\n \"data-part\": \"root\";\n \"data-shape\": AvatarShape;\n \"data-size\": AvatarSize;\n \"data-display\": DisplayMode;\n \"data-load-status\": AvatarLoadStatus;\n \"data-fallback\": FallbackMode;\n role?: \"img\";\n \"aria-label\"?: string;\n \"aria-hidden\"?: \"true\";\n \"data-has-badge\"?: \"true\";\n \"data-badge-type\"?: BadgeType;\n}\n\n/** Core 生成、由适配器覆盖到图片包装元素的属性。 */\nexport interface AvatarImageProps {\n \"data-scope\": \"avatar\";\n \"data-part\": \"image\";\n}\n\n/** Core 生成、由适配器覆盖到 img 元素的属性。 */\nexport interface AvatarImageElementProps {\n alt: \"\";\n loading: \"lazy\";\n}\n\n/** Core 生成、由适配器覆盖到降级内容元素的属性。 */\nexport interface AvatarFallbackProps {\n \"data-scope\": \"avatar\";\n \"data-part\": \"fallback\";\n \"aria-hidden\": \"true\";\n}\n\n/** Core 生成、由适配器覆盖到徽标元素的属性。 */\nexport interface AvatarBadgeProps {\n \"data-scope\": \"avatar\";\n \"data-part\": \"badge\";\n \"data-badge-type\": BadgeType;\n \"aria-label\"?: string;\n role?: \"img\";\n}\n\nconst EMPTY_PRESENCE: AvatarContentPresence = {\n hasImage: false,\n hasName: false,\n hasFallback: false,\n hasIcon: false,\n hasChildren: false,\n hasBadge: false,\n};\n\n/**\n * 从名称提取文字缩写。\n *\n * - 纯 CJK(汉字/假名/谚文)取首字\n * - 纯拉丁取前 2 字符大写\n * - 混合取前 2 字符\n * - 上限 2 字符\n * - 空字符串返回空字符串\n *\n * 按 grapheme 边界截取,不切开组合字符和 emoji 序列。\n */\nexport function extractInitials(name: string): string {\n if (!name) return \"\";\n const segmenter = new Intl.Segmenter(\"en\", { granularity: \"grapheme\" });\n const chars = Array.from(segmenter.segment(name), ({ segment }) => segment).filter((ch) => ch.trim() !== \"\");\n if (chars.length === 0) return \"\";\n\n const isCjk = (ch: string) => /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]/u.test(ch);\n const allCjk = chars.every(isCjk);\n\n if (allCjk) {\n return chars[0]!;\n }\n\n const result = chars.slice(0, 2).join(\"\").toUpperCase();\n return Array.from(segmenter.segment(result), ({ segment }) => segment).slice(0, 2).join(\"\");\n}\n\n/** 将公开输入和内容通道归一化为唯一的跨框架状态。 */\nexport function resolveAvatarState(\n props: QwAvatarProps = {},\n presence: AvatarContentPresence = EMPTY_PRESENCE,\n): ResolvedAvatarState {\n const hasImage = presence.hasImage;\n const hasName = presence.hasName;\n const decorative = props.decorative ?? false;\n const bg = typeof props.bg === \"string\" ? props.bg : undefined;\n\n // 互斥展示优先级:children > image > icon > name > default-icon\n const displayMode: DisplayMode = presence.hasChildren\n ? \"children\"\n : hasImage\n ? \"image\"\n : presence.hasIcon\n ? \"icon\"\n : hasName\n ? \"initials\"\n : \"icon\"; // 默认:展示 User 图标\n\n // 图片加载失败时的降级模式(仅 displayMode=\"image\" 时有效)\n // 降级优先级:fallback prop > icon > name > default-icon\n const fallbackMode: FallbackMode = presence.hasFallback\n ? \"custom\"\n : presence.hasIcon\n ? \"icon\"\n : hasName\n ? \"initials\"\n : \"icon\";\n\n const initials = hasName && props.name ? extractInitials(props.name) : undefined;\n\n // 徽标逻辑\n const hasBadge = presence.hasBadge && !!props.badge && AVATAR_BADGE_TYPES.includes(props.badge.type);\n const badgeType = hasBadge ? props.badge?.type : undefined;\n\n let badgeDisplayCount: number | undefined;\n let badgeOverflow = false;\n let badgeHidden = false;\n\n if (hasBadge && badgeType === \"number\") {\n const rawCount = props.badge?.count ?? 0;\n if (typeof rawCount !== \"number\" || !Number.isFinite(rawCount) || rawCount <= 0) {\n badgeHidden = true;\n badgeDisplayCount = undefined;\n } else if (rawCount > BADGE_COUNT_MAX) {\n badgeOverflow = true;\n badgeDisplayCount = BADGE_COUNT_MAX;\n } else {\n badgeDisplayCount = rawCount;\n }\n }\n\n return {\n shape: AVATAR_SHAPES.includes(props.shape!) ? props.shape! : \"circle\",\n size: AVATAR_SIZES.includes(props.size!) ? props.size! : \"m\",\n hasImage,\n hasName,\n decorative,\n bg,\n displayMode,\n fallbackMode,\n initials,\n hasBadge: hasBadge && !badgeHidden,\n badgeType,\n badgeOverflow,\n badgeDisplayCount,\n badgeHidden,\n };\n}\n\n/** 将归一化结果转成 root 属性。 */\nexport interface AvatarRootOptions { name?:string; badge?:BadgeConfig; loadStatus?:AvatarLoadStatus; lang?:string; dir?:string; hidden?:boolean }\nexport function getAvatarRootProps(state: ResolvedAvatarState, options: AvatarRootOptions = {}): AvatarRootProps & Pick<AvatarRootOptions,\"lang\" | \"dir\" | \"hidden\"> {\n const props: AvatarRootProps = {\n \"data-scope\": \"avatar\",\n \"data-part\": \"root\",\n \"data-shape\": state.shape,\n \"data-size\": state.size,\n \"data-display\": state.displayMode,\n \"data-load-status\": state.displayMode === \"image\" ? options.loadStatus ?? \"loading\" : \"error\",\n \"data-fallback\": state.fallbackMode,\n };\n\n // aria 策略:\n // - decorative → aria-hidden=\"true\" + role=\"img\"(AT 忽略)\n // - 有 name → role=\"img\" + aria-label(由适配器设置)\n // - 无 name 且非 decorative → 不设 role(避免 role-img-alt 违规)\n if (state.decorative) {\n props[\"aria-hidden\"] = \"true\";\n props[\"role\"] = \"img\";\n } else if (state.hasName) {\n props[\"role\"] = \"img\";\n // The optional root options supply the scalar name through the same Core getter.\n }\n\n if (state.hasBadge && state.badgeType) {\n props[\"data-has-badge\"] = \"true\";\n props[\"data-badge-type\"] = state.badgeType;\n }\n if (!state.decorative && state.hasName && options.name) {\n const label=state.hasBadge && typeof options.badge?.ariaLabel===\"string\" ? options.badge.ariaLabel.trim() : \"\";\n props[\"aria-label\"] = label ? `${options.name}, ${label}` : options.name;\n }\n\n return { ...props, ...(options.lang === undefined ? {} : {lang:options.lang}), ...(options.dir === undefined ? {} : {dir:options.dir}), ...(options.hidden === undefined ? {} : {hidden:options.hidden}) };\n}\nexport function getAvatarImageDescriptor(state: ResolvedAvatarState, src?:string): {src:string;active:boolean} {\n return {src:typeof src === \"string\" ? src : \"\",active:state.displayMode === \"image\" && state.hasImage};\n}\nexport function getAvatarRootStyle(state: ResolvedAvatarState): Record<string,string> | undefined {\n return state.bg ? {\"--qw-avatar-bg\":state.bg} : undefined;\n}\nexport function getAvatarBadgeContent(state: ResolvedAvatarState): string {\n return state.badgeType === \"number\" && state.hasBadge ? `${state.badgeDisplayCount}${state.badgeOverflow ? \"+\" : \"\"}` : \"\";\n}\n\n/** 将归一化结果转成图片包装元素的属性。 */\nexport function getAvatarImageProps(_state: ResolvedAvatarState): AvatarImageProps {\n return {\n \"data-scope\": \"avatar\",\n \"data-part\": \"image\",\n };\n}\n\n/** 将归一化结果转成 img 元素的属性。 */\nexport function getAvatarImageElementProps(\n _state: ResolvedAvatarState,\n): AvatarImageElementProps {\n return {\n alt: \"\",\n loading: \"lazy\",\n };\n}\n\n/** 将归一化结果转成降级内容元素的属性。 */\nexport function getAvatarFallbackProps(\n _state: ResolvedAvatarState,\n): AvatarFallbackProps {\n return {\n \"data-scope\": \"avatar\",\n \"data-part\": \"fallback\",\n \"aria-hidden\": \"true\",\n };\n}\n\n/** 将归一化结果转成徽标元素的属性。 */\nexport function getAvatarBadgeProps(state: ResolvedAvatarState, badge?:BadgeConfig): AvatarBadgeProps {\n const props: AvatarBadgeProps = {\n \"data-scope\": \"avatar\",\n \"data-part\": \"badge\",\n \"data-badge-type\": state.badgeType ?? \"dot\",\n };\n\n if (typeof badge?.ariaLabel===\"string\" && badge.ariaLabel.trim()) { props.role = \"img\"; props[\"aria-label\"] = badge.ariaLabel; }\n return props;\n}\n"],"mappings":"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"}
@@ -0,0 +1,139 @@
1
+ import { createMachine } from "@zag-js/core";
2
+ //#region src/avatar/avatar-load.ts
3
+ /** Per-instance monotonic identity; aborted renders never activate a frame. */
4
+ function createImageRequestFrames(initial) {
5
+ if (initial && (!Number.isSafeInteger(initial.epoch) || initial.epoch < 0 || typeof initial.src !== "string" || typeof initial.active !== "boolean")) throw new TypeError("Invalid image request frame");
6
+ let epoch = initial?.epoch ?? -1;
7
+ let current = initial;
8
+ const create = (src, active) => {
9
+ src = typeof src === "string" ? src : "";
10
+ return Object.freeze({
11
+ epoch: ++epoch,
12
+ src,
13
+ active: !!active && !!src
14
+ });
15
+ };
16
+ return {
17
+ create,
18
+ resolve(descriptor) {
19
+ const src = typeof descriptor.src === "string" ? descriptor.src : "", active = !!descriptor.active && !!src;
20
+ if (!current || current.src !== src || current.active !== active) current = create(src, active);
21
+ return current;
22
+ }
23
+ };
24
+ }
25
+ function getImageRequestStatus(frame, result) {
26
+ if (!frame.active) return "error";
27
+ return result?.epoch === frame.epoch && result.src === frame.src ? result.status : "loading";
28
+ }
29
+ const sameFrame = (a, b) => a?.epoch === b.epoch && a.src === b.src;
30
+ const validSession = (params) => params.refs.get("active") && params.event.session === params.refs.get("session");
31
+ function accept(params) {
32
+ const event = params.event;
33
+ if (event.type === "RESOURCE.DETACH" || !validSession(params) || !event.frame.active || event.snapshot.srcAttribute !== event.frame.src) return;
34
+ const status = event.snapshot.complete ? Number.isFinite(event.snapshot.naturalWidth) && event.snapshot.naturalWidth > 0 ? "loaded" : "error" : "loading";
35
+ params.context.set("result", {
36
+ epoch: event.frame.epoch,
37
+ src: event.frame.src,
38
+ status
39
+ });
40
+ }
41
+ /** Pure Zag state: resource bridges supply scalars, never DOM objects. */
42
+ const avatarLoadMachine = createMachine({
43
+ props: ({ props }) => ({ frame: props.frame ?? {
44
+ epoch: 0,
45
+ src: "",
46
+ active: false
47
+ } }),
48
+ initialState: () => "ready",
49
+ states: { ready: {} },
50
+ context: ({ bindable }) => ({ result: bindable(() => ({
51
+ defaultValue: void 0,
52
+ hash: (value) => value ? `${value.epoch}:${value.src}:${value.status}` : "",
53
+ isEqual: (a, b) => a?.epoch === b?.epoch && a?.src === b?.src && a?.status === b?.status
54
+ })) }),
55
+ refs: () => ({
56
+ active: false,
57
+ session: 0,
58
+ committed: void 0
59
+ }),
60
+ effects: ["lifecycle"],
61
+ on: {
62
+ "RESOURCE.COMMIT": { actions: ["commit"] },
63
+ "RESOURCE.SNAPSHOT": { actions: ["snapshot"] },
64
+ "RESOURCE.DETACH": { actions: ["detach"] }
65
+ },
66
+ implementations: {
67
+ effects: { lifecycle({ refs }) {
68
+ refs.set("active", true);
69
+ refs.set("session", refs.get("session") + 1);
70
+ return () => {
71
+ refs.set("active", false);
72
+ refs.set("session", refs.get("session") + 1);
73
+ refs.set("committed", void 0);
74
+ };
75
+ } },
76
+ actions: {
77
+ commit(params) {
78
+ const event = params.event;
79
+ if (event.type !== "RESOURCE.COMMIT" || !validSession(params)) return;
80
+ const current = params.refs.get("committed");
81
+ if (current && current.epoch > event.frame.epoch) return;
82
+ params.refs.set("committed", event.frame);
83
+ accept(params);
84
+ },
85
+ snapshot(params) {
86
+ if (params.event.type === "RESOURCE.SNAPSHOT" && sameFrame(params.refs.get("committed"), params.event.frame)) accept(params);
87
+ },
88
+ detach(params) {
89
+ if (params.event.type === "RESOURCE.DETACH" && validSession(params) && sameFrame(params.refs.get("committed"), params.event.frame)) params.refs.set("committed", void 0);
90
+ }
91
+ }
92
+ }
93
+ });
94
+ /** A committed renderer binds without reading reactive result state. */
95
+ function createAvatarResourceBinding(service, frame) {
96
+ const session = service.refs.get("session");
97
+ let detached = false;
98
+ const send = (event) => {
99
+ if (!detached && service.refs.get("active") && service.refs.get("session") === session) service.send(event);
100
+ };
101
+ return {
102
+ commit(snapshot) {
103
+ send({
104
+ type: "RESOURCE.COMMIT",
105
+ frame,
106
+ session,
107
+ snapshot: { ...snapshot }
108
+ });
109
+ },
110
+ snapshot(snapshot) {
111
+ send({
112
+ type: "RESOURCE.SNAPSHOT",
113
+ frame,
114
+ session,
115
+ snapshot: { ...snapshot }
116
+ });
117
+ },
118
+ detach() {
119
+ send({
120
+ type: "RESOURCE.DETACH",
121
+ frame,
122
+ session
123
+ });
124
+ detached = true;
125
+ }
126
+ };
127
+ }
128
+ function connectAvatarLoad(service, frame = service.prop("frame")) {
129
+ return {
130
+ frame,
131
+ status: getImageRequestStatus(frame, service.context.get("result")),
132
+ imageKey: `${frame.epoch}:${frame.src}`,
133
+ bindResource: () => createAvatarResourceBinding(service, frame)
134
+ };
135
+ }
136
+ //#endregion
137
+ export { getImageRequestStatus as a, createImageRequestFrames as i, connectAvatarLoad as n, createAvatarResourceBinding as r, avatarLoadMachine as t };
138
+
139
+ //# sourceMappingURL=avatar-load-w9dq05oV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"avatar-load-w9dq05oV.js","names":[],"sources":["../src/avatar/avatar-load.ts"],"sourcesContent":["import { createMachine, type Machine, type Params, type Service } from \"@zag-js/core\";\nimport type { AvatarLoadStatus } from \"./avatar.js\";\n\n/** A render request does not become active until its resource is committed. */\nexport interface ImageRequestFrame { readonly epoch: number; readonly src: string; readonly active: boolean }\nexport interface ImageRequestDescriptor { src: string; active: boolean }\nexport interface ImageResourceSnapshot { srcAttribute: string | null; complete: boolean; naturalWidth: number }\nexport interface ImageRequestResult { epoch: number; src: string; status: AvatarLoadStatus }\nexport interface ImageResourceBinding {\n commit(snapshot: ImageResourceSnapshot): void;\n snapshot(snapshot: ImageResourceSnapshot): void;\n detach(): void;\n}\nexport interface ImageRequestFrames {\n create(src: string | undefined, active: boolean): ImageRequestFrame;\n resolve(descriptor:ImageRequestDescriptor):ImageRequestFrame;\n}\n/** Per-instance monotonic identity; aborted renders never activate a frame. */\nexport function createImageRequestFrames(initial?:ImageRequestFrame): ImageRequestFrames {\n if(initial && (!Number.isSafeInteger(initial.epoch) || initial.epoch<0 || typeof initial.src!==\"string\" || typeof initial.active!==\"boolean\"))throw new TypeError(\"Invalid image request frame\");\n let epoch = initial?.epoch ?? -1;\n let current:ImageRequestFrame|undefined=initial;\n const create=(src:string|undefined,active:boolean):ImageRequestFrame=>{\n src = typeof src === \"string\" ? src : \"\";\n return Object.freeze({epoch:++epoch,src,active:!!active && !!src});\n };\n return {create,resolve(descriptor){\n const src=typeof descriptor.src === \"string\" ? descriptor.src : \"\",active=!!descriptor.active && !!src;\n if(!current || current.src!==src || current.active!==active)current=create(src,active);\n return current;\n }};\n}\nexport function getImageRequestStatus(frame: ImageRequestFrame, result?: ImageRequestResult): AvatarLoadStatus {\n if (!frame.active) return \"error\";\n return result?.epoch === frame.epoch && result.src === frame.src ? result.status : \"loading\";\n}\nexport interface QwAvatarLoadProps { frame: ImageRequestFrame }\ntype ResourceEvent = { type:\"RESOURCE.COMMIT\" | \"RESOURCE.SNAPSHOT\"; frame:ImageRequestFrame; session:number; snapshot:ImageResourceSnapshot }\n | { type:\"RESOURCE.DETACH\"; frame:ImageRequestFrame; session:number };\nexport interface QwAvatarLoadSchema {\n props: QwAvatarLoadProps;\n context: { result: ImageRequestResult | undefined };\n refs: { active:boolean; session:number; committed:ImageRequestFrame | undefined };\n state: \"ready\";\n effect: \"lifecycle\";\n action: \"commit\" | \"snapshot\" | \"detach\";\n event: ResourceEvent;\n}\nexport type QwAvatarLoadService = Service<QwAvatarLoadSchema>;\nexport type QwAvatarLoadMachine = Machine<QwAvatarLoadSchema>;\nconst sameFrame = (a:ImageRequestFrame | undefined,b:ImageRequestFrame) => a?.epoch === b.epoch && a.src === b.src;\nconst validSession = (params:Params<QwAvatarLoadSchema>) => params.refs.get(\"active\") && params.event.session === params.refs.get(\"session\");\nfunction accept(params:Params<QwAvatarLoadSchema>): void {\n const event=params.event;\n if (event.type === \"RESOURCE.DETACH\" || !validSession(params) || !event.frame.active || event.snapshot.srcAttribute !== event.frame.src) return;\n const status:AvatarLoadStatus = event.snapshot.complete ? Number.isFinite(event.snapshot.naturalWidth) && event.snapshot.naturalWidth > 0 ? \"loaded\" : \"error\" : \"loading\";\n params.context.set(\"result\",{epoch:event.frame.epoch,src:event.frame.src,status});\n}\n/** Pure Zag state: resource bridges supply scalars, never DOM objects. */\nexport const avatarLoadMachine:QwAvatarLoadMachine=createMachine<QwAvatarLoadSchema>({\n props:({props})=>({frame:props.frame ?? {epoch:0,src:\"\",active:false}}),\n initialState:()=>\"ready\", states:{ready:{}},\n context:({bindable})=>({result:bindable<ImageRequestResult | undefined>(()=>({defaultValue:undefined,hash:value=>value ? `${value.epoch}:${value.src}:${value.status}` : \"\",isEqual:(a,b)=>a?.epoch === b?.epoch && a?.src === b?.src && a?.status === b?.status}))}),\n refs:()=>({active:false,session:0,committed:undefined}),\n effects:[\"lifecycle\"],\n on:{\"RESOURCE.COMMIT\":{actions:[\"commit\"]},\"RESOURCE.SNAPSHOT\":{actions:[\"snapshot\"]},\"RESOURCE.DETACH\":{actions:[\"detach\"]}},\n implementations:{\n effects:{lifecycle({refs}){\n refs.set(\"active\",true); refs.set(\"session\",refs.get(\"session\")+1);\n return ()=>{refs.set(\"active\",false);refs.set(\"session\",refs.get(\"session\")+1);refs.set(\"committed\",undefined);};\n }},\n actions:{\n commit(params){\n const event=params.event;if(event.type!==\"RESOURCE.COMMIT\" || !validSession(params))return;\n const current=params.refs.get(\"committed\");if(current && current.epoch>event.frame.epoch)return;\n params.refs.set(\"committed\",event.frame);accept(params);\n },\n snapshot(params){if(params.event.type===\"RESOURCE.SNAPSHOT\" && sameFrame(params.refs.get(\"committed\"),params.event.frame))accept(params);},\n detach(params){if(params.event.type===\"RESOURCE.DETACH\" && validSession(params) && sameFrame(params.refs.get(\"committed\"),params.event.frame))params.refs.set(\"committed\",undefined);},\n },\n },\n});\nexport interface QwAvatarLoadApi {\n readonly frame:ImageRequestFrame;\n readonly status:AvatarLoadStatus;\n readonly imageKey:string;\n bindResource():ImageResourceBinding;\n}\n/** A committed renderer binds without reading reactive result state. */\nexport function createAvatarResourceBinding(service:QwAvatarLoadService, frame:ImageRequestFrame):ImageResourceBinding {\n const session=service.refs.get(\"session\");let detached=false;\n const send=(event:ResourceEvent)=>{if(!detached && service.refs.get(\"active\") && service.refs.get(\"session\")===session)service.send(event);};\n return {\n commit(snapshot){send({type:\"RESOURCE.COMMIT\",frame,session,snapshot:{...snapshot}});},\n snapshot(snapshot){send({type:\"RESOURCE.SNAPSHOT\",frame,session,snapshot:{...snapshot}});},\n detach(){send({type:\"RESOURCE.DETACH\",frame,session});detached=true;},\n };\n}\nexport function connectAvatarLoad(service:QwAvatarLoadService, frame=service.prop(\"frame\")):QwAvatarLoadApi {\n return {\n frame,status:getImageRequestStatus(frame,service.context.get(\"result\")),imageKey:`${frame.epoch}:${frame.src}`,\n bindResource:()=>createAvatarResourceBinding(service,frame),\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import { type Machine, type Service } from "@zag-js/core";
2
+ import type { NormalizeProps, PropTypes } from "@zag-js/types";
3
+ import { type IconState } from "../icon/index.js";
4
+ /** Stable DOM parts shared by every adapter. */
5
+ export declare const BACK_TOP_PARTS: readonly ["root", "icon", "label"];
6
+ /** SSR-safe input shared by all adapters. */
7
+ export interface QwBackTopProps {
8
+ /** Page-unique instance identity; adapters generate one when omitted. */
9
+ id?: string;
10
+ /** Accessible button name; defaults to 回到顶部. */
11
+ label?: string;
12
+ /** Show the visible label next to the icon; the name is always present. */
13
+ showLabel?: boolean;
14
+ /** Scroll distance in CSS pixels after which the button appears; defaults to 400. */
15
+ visibilityHeight?: number;
16
+ /** Element id of the scrolling container; omitted or missing means the document scroller. */
17
+ target?: string;
18
+ /** Called once per activation, after the scroll request is issued. */
19
+ onActivate?: () => void;
20
+ }
21
+ export interface ResolvedBackTopState {
22
+ label: string;
23
+ showLabel: boolean;
24
+ visibilityHeight: number;
25
+ target: string | undefined;
26
+ }
27
+ export declare function resolveBackTopState(props?: QwBackTopProps): ResolvedBackTopState;
28
+ /** Visible above the threshold; a focused button is never hidden under the user. */
29
+ export declare function isBackTopShown(state: ResolvedBackTopState, scrollTop: number, focused: boolean): boolean;
30
+ export declare function createBackTopProps(props?: QwBackTopProps): QwBackTopProps;
31
+ export interface QwBackTopSchema {
32
+ props: QwBackTopProps;
33
+ context: {
34
+ scrollTop: number;
35
+ focused: boolean;
36
+ };
37
+ refs: {
38
+ schedule: (() => void) | null;
39
+ };
40
+ state: "ready";
41
+ action: "setScroll" | "setFocused" | "scrollToTop" | "remeasure";
42
+ effect: "trackScroll";
43
+ event: {
44
+ type: "SCROLL.SET";
45
+ scrollTop: number;
46
+ } | {
47
+ type: "FOCUS.SET";
48
+ focused: boolean;
49
+ } | {
50
+ type: "ACTIVATE";
51
+ };
52
+ }
53
+ export type QwBackTopMachine = Machine<QwBackTopSchema>;
54
+ export type QwBackTopService = Service<QwBackTopSchema>;
55
+ /** Core owns listening, threshold decisions, reduced-motion scrolling and cleanup. */
56
+ export declare const backTopMachine: QwBackTopMachine;
57
+ export interface QwBackTopApi<T extends PropTypes = PropTypes> {
58
+ shown: boolean;
59
+ label: string;
60
+ showLabel: boolean;
61
+ activate(): void;
62
+ getRootProps(): T["button"];
63
+ getIconProps(): T["element"];
64
+ getIconState(): IconState;
65
+ getLabelProps(): T["element"];
66
+ }
67
+ /** Framework/native event shape; only cancellation is read. */
68
+ export interface BackTopDomEvent {
69
+ readonly defaultPrevented: boolean;
70
+ }
71
+ /** Caller root handlers run first; Core then activates (unless prevented) and always tracks focus. Method syntax keeps framework handlers assignable. */
72
+ export interface BackTopConnectOptions {
73
+ onClick?(event: BackTopDomEvent): void;
74
+ onFocus?(event: BackTopDomEvent): void;
75
+ onBlur?(event: BackTopDomEvent): void;
76
+ }
77
+ export declare function connectBackTop<T extends PropTypes>(service: QwBackTopService, normalize: NormalizeProps<T>, options?: BackTopConnectOptions): QwBackTopApi<T>;
@@ -0,0 +1 @@
1
+ export * from "./back-top.js";
@@ -0,0 +1,189 @@
1
+ import { o as resolveIconState } from "../icon-Cgdbnk1u.js";
2
+ import { createMachine } from "@zag-js/core";
3
+ //#region src/back-top/back-top.ts
4
+ /** Stable DOM parts shared by every adapter. */
5
+ const BACK_TOP_PARTS = [
6
+ "root",
7
+ "icon",
8
+ "label"
9
+ ];
10
+ function resolveBackTopState(props = {}) {
11
+ const height = props.visibilityHeight;
12
+ return {
13
+ label: typeof props.label === "string" && props.label.trim() ? props.label : "回到顶部",
14
+ showLabel: !!props.showLabel,
15
+ visibilityHeight: typeof height === "number" && Number.isFinite(height) && height >= 0 ? height : 400,
16
+ target: typeof props.target === "string" && props.target.trim() ? props.target : void 0
17
+ };
18
+ }
19
+ /** Visible above the threshold; a focused button is never hidden under the user. */
20
+ function isBackTopShown(state, scrollTop, focused) {
21
+ return focused || scrollTop >= Math.max(1, state.visibilityHeight);
22
+ }
23
+ function createBackTopProps(props = {}) {
24
+ if (props.id !== void 0 && (typeof props.id !== "string" || !props.id.trim())) throw new TypeError("BackTop requires a nonempty instance id when provided");
25
+ return { ...props };
26
+ }
27
+ const rootId = (scope) => `back-top:${scope.id}`;
28
+ const live = (service) => resolveBackTopState({
29
+ label: service.prop("label"),
30
+ showLabel: service.prop("showLabel"),
31
+ visibilityHeight: service.prop("visibilityHeight"),
32
+ target: service.prop("target")
33
+ });
34
+ /** Platform boundary: the element id is looked up in the button's own root node. */
35
+ function resolveScroller(root, state) {
36
+ const win = root.ownerDocument.defaultView;
37
+ const owner = root.getRootNode();
38
+ const found = state.target ? owner.getElementById?.(state.target) ?? root.ownerDocument.getElementById(state.target) : null;
39
+ return found ? {
40
+ node: found,
41
+ events: found
42
+ } : {
43
+ node: null,
44
+ events: win
45
+ };
46
+ }
47
+ function readScrollTop(root, node) {
48
+ if (node) return node.scrollTop;
49
+ return root.ownerDocument.defaultView?.scrollY ?? root.ownerDocument.documentElement.scrollTop;
50
+ }
51
+ /** Core owns listening, threshold decisions, reduced-motion scrolling and cleanup. */
52
+ const backTopMachine = createMachine({
53
+ props: ({ props }) => createBackTopProps(props),
54
+ initialState: () => "ready",
55
+ states: { ready: {} },
56
+ context: ({ bindable }) => ({
57
+ scrollTop: bindable(() => ({ defaultValue: 0 })),
58
+ focused: bindable(() => ({ defaultValue: false }))
59
+ }),
60
+ refs: () => ({ schedule: null }),
61
+ watch({ track, prop, action }) {
62
+ track([() => prop("target"), () => prop("visibilityHeight")], () => action(["remeasure"]));
63
+ },
64
+ on: {
65
+ "SCROLL.SET": { actions: ["setScroll"] },
66
+ "FOCUS.SET": { actions: ["setFocused"] },
67
+ ACTIVATE: { actions: ["scrollToTop"] }
68
+ },
69
+ effects: ["trackScroll"],
70
+ implementations: {
71
+ actions: {
72
+ setScroll(params) {
73
+ if (params.event.type === "SCROLL.SET") params.context.set("scrollTop", params.event.scrollTop);
74
+ },
75
+ setFocused(params) {
76
+ if (params.event.type === "FOCUS.SET") params.context.set("focused", params.event.focused);
77
+ },
78
+ remeasure(params) {
79
+ params.refs.get("schedule")?.();
80
+ },
81
+ scrollToTop({ scope, prop }) {
82
+ const root = scope.getById(rootId(scope)), win = root?.ownerDocument.defaultView;
83
+ if (root && win) {
84
+ const reduce = win.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
85
+ const { node } = resolveScroller(root, live({ prop }));
86
+ const options = {
87
+ top: 0,
88
+ behavior: reduce ? "auto" : "smooth"
89
+ };
90
+ if (node) node.scrollTo?.(options);
91
+ else win.scrollTo?.(options);
92
+ }
93
+ prop("onActivate")?.();
94
+ }
95
+ },
96
+ effects: { trackScroll({ scope, prop, send, refs, context }) {
97
+ const root = scope.getById(rootId(scope)), win = root?.ownerDocument.defaultView;
98
+ if (!root || !win) return;
99
+ let frame = 0, events = null, node = null, disposed = false;
100
+ const run = () => {
101
+ frame = 0;
102
+ if (disposed) return;
103
+ const next = resolveScroller(root, live({ prop }));
104
+ if (next.events !== events) {
105
+ events?.removeEventListener("scroll", schedule);
106
+ events = next.events;
107
+ node = next.node;
108
+ events.addEventListener("scroll", schedule, { passive: true });
109
+ }
110
+ const top = readScrollTop(root, node);
111
+ if (top !== context.get("scrollTop")) send({
112
+ type: "SCROLL.SET",
113
+ scrollTop: top
114
+ });
115
+ };
116
+ function schedule() {
117
+ if (!frame && !disposed) frame = win.requestAnimationFrame(run);
118
+ }
119
+ refs.set("schedule", schedule);
120
+ run();
121
+ return () => {
122
+ disposed = true;
123
+ refs.set("schedule", null);
124
+ if (frame) win.cancelAnimationFrame(frame);
125
+ events?.removeEventListener("scroll", schedule);
126
+ };
127
+ } }
128
+ }
129
+ });
130
+ const callAll = (handlers, event) => {
131
+ for (const handler of Array.isArray(handlers) ? handlers : [handlers]) if (typeof handler === "function") handler(event);
132
+ };
133
+ function connectBackTop(service, normalize, options = {}) {
134
+ const state = live(service), id = rootId(service.scope);
135
+ const shown = isBackTopShown(state, service.context.get("scrollTop"), service.context.get("focused"));
136
+ const activate = () => service.send({ type: "ACTIVATE" });
137
+ return {
138
+ shown,
139
+ label: state.label,
140
+ showLabel: state.showLabel,
141
+ activate,
142
+ getRootProps: () => normalize.button({
143
+ "data-scope": "back-top",
144
+ "data-part": "root",
145
+ id,
146
+ type: "button",
147
+ hidden: !shown || void 0,
148
+ "data-state": shown ? "visible" : "hidden",
149
+ "data-show-label": state.showLabel ? "true" : void 0,
150
+ "aria-label": state.showLabel ? void 0 : state.label,
151
+ onClick(event) {
152
+ callAll(options.onClick, event);
153
+ if (!event.defaultPrevented) activate();
154
+ },
155
+ onFocus(event) {
156
+ callAll(options.onFocus, event);
157
+ service.send({
158
+ type: "FOCUS.SET",
159
+ focused: true
160
+ });
161
+ },
162
+ onBlur(event) {
163
+ callAll(options.onBlur, event);
164
+ service.send({
165
+ type: "FOCUS.SET",
166
+ focused: false
167
+ });
168
+ }
169
+ }),
170
+ getIconProps: () => normalize.element({
171
+ "data-scope": "back-top",
172
+ "data-part": "icon",
173
+ "aria-hidden": true
174
+ }),
175
+ getIconState: () => resolveIconState({
176
+ id: `${id}:icon`,
177
+ name: "to-top"
178
+ }),
179
+ getLabelProps: () => normalize.element({
180
+ "data-scope": "back-top",
181
+ "data-part": "label",
182
+ hidden: !state.showLabel || void 0
183
+ })
184
+ };
185
+ }
186
+ //#endregion
187
+ export { BACK_TOP_PARTS, backTopMachine, connectBackTop, createBackTopProps, isBackTopShown, resolveBackTopState };
188
+
189
+ //# sourceMappingURL=index.js.map