@chenzy-design/svelte 0.1.0 → 0.2.0

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 (476) hide show
  1. package/dist/components.json +1 -1
  2. package/package.json +7 -8
  3. package/src/_floating/use-floating.ts +0 -208
  4. package/src/anchor/Anchor.a11y.test.ts +0 -49
  5. package/src/anchor/Anchor.kbd.test.ts +0 -70
  6. package/src/anchor/Anchor.svelte +0 -359
  7. package/src/anchor/AnchorKbdFixture.svelte +0 -24
  8. package/src/anchor/index.ts +0 -3
  9. package/src/anchor/meta.ts +0 -70
  10. package/src/anchor/types.ts +0 -8
  11. package/src/autocomplete/AutoComplete.a11y.test.ts +0 -49
  12. package/src/autocomplete/AutoComplete.kbd.test.ts +0 -69
  13. package/src/autocomplete/AutoComplete.svelte +0 -706
  14. package/src/autocomplete/AutoCompleteKbdFixture.svelte +0 -24
  15. package/src/autocomplete/index.ts +0 -2
  16. package/src/autocomplete/meta.ts +0 -41
  17. package/src/avatar/Avatar.a11y.test.ts +0 -37
  18. package/src/avatar/Avatar.svelte +0 -315
  19. package/src/avatar/AvatarGroup.svelte +0 -142
  20. package/src/avatar/context.ts +0 -21
  21. package/src/avatar/index.ts +0 -10
  22. package/src/avatar/meta.ts +0 -73
  23. package/src/back-top/BackTop.a11y.test.ts +0 -40
  24. package/src/back-top/BackTop.svelte +0 -314
  25. package/src/back-top/index.ts +0 -2
  26. package/src/back-top/meta.ts +0 -108
  27. package/src/badge/Badge.a11y.test.ts +0 -35
  28. package/src/badge/Badge.svelte +0 -328
  29. package/src/badge/BadgeA11yFixture.svelte +0 -18
  30. package/src/badge/index.ts +0 -2
  31. package/src/badge/meta.ts +0 -62
  32. package/src/banner/Banner.a11y.test.ts +0 -48
  33. package/src/banner/Banner.svelte +0 -368
  34. package/src/banner/index.ts +0 -3
  35. package/src/banner/meta.ts +0 -121
  36. package/src/breadcrumb/Breadcrumb.a11y.test.ts +0 -35
  37. package/src/breadcrumb/Breadcrumb.kbd.test.ts +0 -45
  38. package/src/breadcrumb/Breadcrumb.svelte +0 -414
  39. package/src/breadcrumb/BreadcrumbKbdFixture.svelte +0 -23
  40. package/src/breadcrumb/Item.svelte +0 -77
  41. package/src/breadcrumb/context.ts +0 -27
  42. package/src/breadcrumb/index.ts +0 -21
  43. package/src/breadcrumb/meta.ts +0 -128
  44. package/src/breadcrumb/types.ts +0 -4
  45. package/src/button/Button.a11y.test.ts +0 -54
  46. package/src/button/Button.svelte +0 -176
  47. package/src/button/index.ts +0 -2
  48. package/src/button/meta.ts +0 -28
  49. package/src/calendar/Calendar.a11y.test.ts +0 -51
  50. package/src/calendar/Calendar.kbd.test.ts +0 -59
  51. package/src/calendar/Calendar.svelte +0 -1344
  52. package/src/calendar/CalendarKbdFixture.svelte +0 -23
  53. package/src/calendar/index.ts +0 -3
  54. package/src/calendar/meta.ts +0 -117
  55. package/src/card/Card.a11y.test.ts +0 -38
  56. package/src/card/Card.svelte +0 -341
  57. package/src/card/CardMeta.svelte +0 -60
  58. package/src/card/index.ts +0 -8
  59. package/src/card/meta.ts +0 -83
  60. package/src/carousel/Carousel.a11y.test.ts +0 -53
  61. package/src/carousel/Carousel.kbd.test.ts +0 -67
  62. package/src/carousel/Carousel.svelte +0 -806
  63. package/src/carousel/CarouselA11yFixture.svelte +0 -25
  64. package/src/carousel/CarouselKbdFixture.svelte +0 -24
  65. package/src/carousel/index.ts +0 -2
  66. package/src/carousel/meta.ts +0 -74
  67. package/src/cascader/Cascader.a11y.test.ts +0 -59
  68. package/src/cascader/Cascader.kbd.test.ts +0 -56
  69. package/src/cascader/Cascader.svelte +0 -1570
  70. package/src/cascader/CascaderKbdFixture.svelte +0 -31
  71. package/src/cascader/index.ts +0 -3
  72. package/src/cascader/meta.ts +0 -57
  73. package/src/cascader/types.ts +0 -9
  74. package/src/checkbox/Checkbox.svelte +0 -281
  75. package/src/checkbox/CheckboxGroup.a11y.test.ts +0 -43
  76. package/src/checkbox/CheckboxGroup.kbd.test.ts +0 -40
  77. package/src/checkbox/CheckboxGroup.svelte +0 -122
  78. package/src/checkbox/CheckboxGroupKbdFixture.svelte +0 -20
  79. package/src/checkbox/context.ts +0 -31
  80. package/src/checkbox/index.ts +0 -4
  81. package/src/checkbox/meta.ts +0 -67
  82. package/src/collapse/Collapse.a11y.test.ts +0 -53
  83. package/src/collapse/Collapse.kbd.test.ts +0 -89
  84. package/src/collapse/Collapse.svelte +0 -406
  85. package/src/collapse/CollapseA11yFixture.svelte +0 -27
  86. package/src/collapse/CollapseKbdFixture.svelte +0 -25
  87. package/src/collapse/CollapsePanel.svelte +0 -102
  88. package/src/collapse/context.ts +0 -54
  89. package/src/collapse/index.ts +0 -17
  90. package/src/collapse/meta.ts +0 -79
  91. package/src/collapse/types.ts +0 -5
  92. package/src/color-picker/ColorPicker.a11y.test.ts +0 -60
  93. package/src/color-picker/ColorPicker.kbd.test.ts +0 -73
  94. package/src/color-picker/ColorPicker.svelte +0 -985
  95. package/src/color-picker/ColorPickerKbdFixture.svelte +0 -25
  96. package/src/color-picker/index.ts +0 -2
  97. package/src/color-picker/meta.ts +0 -47
  98. package/src/config-provider/ConfigProvider.svelte +0 -227
  99. package/src/config-provider/context.ts +0 -29
  100. package/src/config-provider/index.ts +0 -48
  101. package/src/config-provider/meta.ts +0 -128
  102. package/src/date-picker/DatePicker.a11y.test.ts +0 -45
  103. package/src/date-picker/DatePicker.kbd.test.ts +0 -71
  104. package/src/date-picker/DatePicker.svelte +0 -1798
  105. package/src/date-picker/DatePickerKbdFixture.svelte +0 -26
  106. package/src/date-picker/RangePicker.kbd.test.ts +0 -69
  107. package/src/date-picker/RangePicker.svelte +0 -775
  108. package/src/date-picker/RangePickerKbdFixture.svelte +0 -30
  109. package/src/date-picker/index.ts +0 -4
  110. package/src/date-picker/meta.ts +0 -67
  111. package/src/date-picker/range-meta.ts +0 -44
  112. package/src/descriptions/Descriptions.a11y.test.ts +0 -33
  113. package/src/descriptions/Descriptions.svelte +0 -287
  114. package/src/descriptions/DescriptionsItem.svelte +0 -43
  115. package/src/descriptions/context.ts +0 -21
  116. package/src/descriptions/index.ts +0 -16
  117. package/src/descriptions/meta.ts +0 -45
  118. package/src/descriptions/types.ts +0 -6
  119. package/src/divider/Divider.a11y.test.ts +0 -23
  120. package/src/divider/Divider.svelte +0 -154
  121. package/src/divider/index.ts +0 -2
  122. package/src/divider/meta.ts +0 -23
  123. package/src/drawer/Drawer.a11y.test.ts +0 -41
  124. package/src/drawer/Drawer.kbd.test.ts +0 -67
  125. package/src/drawer/Drawer.svelte +0 -554
  126. package/src/drawer/DrawerKbdFixture.svelte +0 -28
  127. package/src/drawer/index.ts +0 -2
  128. package/src/drawer/meta.ts +0 -127
  129. package/src/dropdown/Dropdown.a11y.test.ts +0 -46
  130. package/src/dropdown/Dropdown.kbd.test.ts +0 -56
  131. package/src/dropdown/Dropdown.svelte +0 -573
  132. package/src/dropdown/DropdownA11yFixture.svelte +0 -24
  133. package/src/dropdown/DropdownDivider.svelte +0 -17
  134. package/src/dropdown/DropdownItem.svelte +0 -127
  135. package/src/dropdown/DropdownItemNode.svelte +0 -355
  136. package/src/dropdown/DropdownKbdFixture.svelte +0 -26
  137. package/src/dropdown/DropdownMenu.svelte +0 -24
  138. package/src/dropdown/DropdownSubMenu.svelte +0 -212
  139. package/src/dropdown/DropdownTitle.svelte +0 -34
  140. package/src/dropdown/context.ts +0 -13
  141. package/src/dropdown/index.ts +0 -36
  142. package/src/dropdown/meta.ts +0 -97
  143. package/src/dropdown/types.test.ts +0 -50
  144. package/src/dropdown/types.ts +0 -67
  145. package/src/empty/Empty.a11y.test.ts +0 -24
  146. package/src/empty/Empty.svelte +0 -304
  147. package/src/empty/index.ts +0 -2
  148. package/src/empty/meta.ts +0 -41
  149. package/src/form/Field.svelte +0 -492
  150. package/src/form/FieldArray.svelte +0 -75
  151. package/src/form/Form.a11y.test.ts +0 -39
  152. package/src/form/Form.svelte +0 -243
  153. package/src/form/FormA11yFixture.svelte +0 -31
  154. package/src/form/FormCascader.svelte +0 -82
  155. package/src/form/FormCheckbox.svelte +0 -83
  156. package/src/form/FormDatePicker.svelte +0 -80
  157. package/src/form/FormErrorMessage.svelte +0 -44
  158. package/src/form/FormInput.svelte +0 -95
  159. package/src/form/FormLabel.svelte +0 -46
  160. package/src/form/FormRadio.svelte +0 -83
  161. package/src/form/FormRating.svelte +0 -85
  162. package/src/form/FormSection.svelte +0 -45
  163. package/src/form/FormSelect.svelte +0 -96
  164. package/src/form/FormSlider.svelte +0 -95
  165. package/src/form/FormSlot.svelte +0 -51
  166. package/src/form/FormSwitch.svelte +0 -82
  167. package/src/form/FormTagInput.svelte +0 -79
  168. package/src/form/FormTreeSelect.svelte +0 -85
  169. package/src/form/FormUpload.svelte +0 -92
  170. package/src/form/context.ts +0 -47
  171. package/src/form/index.ts +0 -22
  172. package/src/form/meta.ts +0 -85
  173. package/src/grid/Col.svelte +0 -142
  174. package/src/grid/Grid.a11y.test.ts +0 -14
  175. package/src/grid/GridA11yFixture.svelte +0 -12
  176. package/src/grid/Row.svelte +0 -116
  177. package/src/grid/index.ts +0 -3
  178. package/src/grid/meta.ts +0 -72
  179. package/src/grid/use-breakpoint.svelte.ts +0 -48
  180. package/src/highlight/Highlight.a11y.test.ts +0 -25
  181. package/src/highlight/Highlight.svelte +0 -99
  182. package/src/highlight/index.ts +0 -2
  183. package/src/highlight/meta.ts +0 -80
  184. package/src/icon/Icon.svelte +0 -156
  185. package/src/icon/index.ts +0 -2
  186. package/src/icon/meta.ts +0 -41
  187. package/src/image/Image.a11y.test.ts +0 -30
  188. package/src/image/Image.svelte +0 -387
  189. package/src/image/ImagePreview.kbd.test.ts +0 -52
  190. package/src/image/ImagePreview.svelte +0 -309
  191. package/src/image/ImagePreviewGroup.svelte +0 -93
  192. package/src/image/ImagePreviewKbdFixture.svelte +0 -32
  193. package/src/image/context.ts +0 -26
  194. package/src/image/index.ts +0 -15
  195. package/src/image/meta.ts +0 -131
  196. package/src/index.ts +0 -208
  197. package/src/input/Input.a11y.test.ts +0 -58
  198. package/src/input/Input.svelte +0 -505
  199. package/src/input/index.ts +0 -2
  200. package/src/input/meta.ts +0 -40
  201. package/src/input-number/InputNumber.a11y.test.ts +0 -43
  202. package/src/input-number/InputNumber.kbd.test.ts +0 -41
  203. package/src/input-number/InputNumber.svelte +0 -674
  204. package/src/input-number/InputNumberKbdFixture.svelte +0 -15
  205. package/src/input-number/index.ts +0 -2
  206. package/src/input-number/meta.ts +0 -65
  207. package/src/layout/Content.svelte +0 -37
  208. package/src/layout/Footer.svelte +0 -49
  209. package/src/layout/Header.svelte +0 -46
  210. package/src/layout/Layout.a11y.test.ts +0 -34
  211. package/src/layout/Layout.svelte +0 -60
  212. package/src/layout/LayoutA11yFixture.svelte +0 -21
  213. package/src/layout/Sider.svelte +0 -207
  214. package/src/layout/index.ts +0 -26
  215. package/src/layout/meta.ts +0 -116
  216. package/src/list/List.a11y.test.ts +0 -69
  217. package/src/list/List.bench.ts +0 -26
  218. package/src/list/List.kbd.test.ts +0 -71
  219. package/src/list/List.svelte +0 -920
  220. package/src/list/ListA11yFixture.svelte +0 -38
  221. package/src/list/ListBenchFixture.svelte +0 -22
  222. package/src/list/ListItem.svelte +0 -175
  223. package/src/list/ListKbdFixture.svelte +0 -31
  224. package/src/list/ListMeta.svelte +0 -73
  225. package/src/list/context.ts +0 -39
  226. package/src/list/index.ts +0 -21
  227. package/src/list/meta.ts +0 -99
  228. package/src/locale-provider/LocaleProvider.svelte +0 -110
  229. package/src/locale-provider/context.ts +0 -22
  230. package/src/locale-provider/index.ts +0 -21
  231. package/src/locale-provider/meta.ts +0 -93
  232. package/src/locale-provider/use-locale.ts +0 -25
  233. package/src/lottie-icon/LottieIcon.svelte +0 -330
  234. package/src/lottie-icon/index.ts +0 -9
  235. package/src/lottie-icon/meta.ts +0 -162
  236. package/src/menu/Menu.a11y.test.ts +0 -54
  237. package/src/menu/Menu.kbd.test.ts +0 -69
  238. package/src/menu/Menu.svelte +0 -762
  239. package/src/menu/MenuKbdFixture.svelte +0 -22
  240. package/src/menu/MenuPopupNode.svelte +0 -525
  241. package/src/menu/index.ts +0 -4
  242. package/src/menu/meta.ts +0 -137
  243. package/src/menu/types.ts +0 -120
  244. package/src/meta.test.ts +0 -173
  245. package/src/modal/ConfirmModal.svelte +0 -173
  246. package/src/modal/Modal.a11y.test.ts +0 -46
  247. package/src/modal/Modal.kbd.test.ts +0 -65
  248. package/src/modal/Modal.svelte +0 -511
  249. package/src/modal/ModalKbdFixture.svelte +0 -24
  250. package/src/modal/command.svelte.ts +0 -103
  251. package/src/modal/index.ts +0 -30
  252. package/src/modal/meta.ts +0 -165
  253. package/src/modal/z-stack.ts +0 -26
  254. package/src/notification/NotificationContainer.svelte +0 -192
  255. package/src/notification/NotificationItem.a11y.test.ts +0 -73
  256. package/src/notification/NotificationItem.svelte +0 -368
  257. package/src/notification/index.ts +0 -9
  258. package/src/notification/meta.ts +0 -120
  259. package/src/notification/store.ts +0 -191
  260. package/src/overflow-list/OverflowList.a11y.test.ts +0 -46
  261. package/src/overflow-list/OverflowList.kbd.test.ts +0 -34
  262. package/src/overflow-list/OverflowList.svelte +0 -539
  263. package/src/overflow-list/OverflowListA11yFixture.svelte +0 -22
  264. package/src/overflow-list/OverflowListKbdFixture.svelte +0 -21
  265. package/src/overflow-list/index.ts +0 -2
  266. package/src/overflow-list/meta.ts +0 -138
  267. package/src/pagination/Pagination.a11y.test.ts +0 -62
  268. package/src/pagination/Pagination.kbd.test.ts +0 -66
  269. package/src/pagination/Pagination.svelte +0 -428
  270. package/src/pagination/PaginationKbdFixture.svelte +0 -15
  271. package/src/pagination/index.ts +0 -2
  272. package/src/pagination/meta.ts +0 -73
  273. package/src/popconfirm/Popconfirm.a11y.test.ts +0 -46
  274. package/src/popconfirm/Popconfirm.kbd.test.ts +0 -42
  275. package/src/popconfirm/Popconfirm.svelte +0 -724
  276. package/src/popconfirm/PopconfirmA11yFixture.svelte +0 -34
  277. package/src/popconfirm/PopconfirmKbdFixture.svelte +0 -17
  278. package/src/popconfirm/index.ts +0 -2
  279. package/src/popconfirm/meta.ts +0 -200
  280. package/src/popover/Popover.a11y.test.ts +0 -58
  281. package/src/popover/Popover.kbd.test.ts +0 -42
  282. package/src/popover/Popover.svelte +0 -643
  283. package/src/popover/PopoverA11yFixture.svelte +0 -35
  284. package/src/popover/PopoverKbdFixture.svelte +0 -18
  285. package/src/popover/index.ts +0 -2
  286. package/src/popover/meta.ts +0 -103
  287. package/src/progress/Progress.a11y.test.ts +0 -37
  288. package/src/progress/Progress.svelte +0 -423
  289. package/src/progress/index.ts +0 -3
  290. package/src/progress/meta.ts +0 -132
  291. package/src/radio/Radio.svelte +0 -330
  292. package/src/radio/RadioGroup.a11y.test.ts +0 -43
  293. package/src/radio/RadioGroup.kbd.test.ts +0 -75
  294. package/src/radio/RadioGroup.svelte +0 -199
  295. package/src/radio/RadioGroupKbdFixture.svelte +0 -21
  296. package/src/radio/context.ts +0 -42
  297. package/src/radio/index.ts +0 -4
  298. package/src/radio/meta.ts +0 -68
  299. package/src/rating/Rating.a11y.test.ts +0 -41
  300. package/src/rating/Rating.kbd.test.ts +0 -48
  301. package/src/rating/Rating.svelte +0 -407
  302. package/src/rating/RatingKbdFixture.svelte +0 -15
  303. package/src/rating/index.ts +0 -2
  304. package/src/rating/meta.ts +0 -40
  305. package/src/resize-observer/ResizeObserver.svelte +0 -169
  306. package/src/resize-observer/action.ts +0 -80
  307. package/src/resize-observer/index.ts +0 -9
  308. package/src/resize-observer/meta.ts +0 -102
  309. package/src/scroll-list/ScrollColumn.svelte +0 -592
  310. package/src/scroll-list/ScrollList.svelte +0 -257
  311. package/src/scroll-list/index.ts +0 -4
  312. package/src/scroll-list/meta.ts +0 -93
  313. package/src/scroll-list/types.ts +0 -18
  314. package/src/select/Select.a11y.test.ts +0 -59
  315. package/src/select/Select.kbd.test.ts +0 -59
  316. package/src/select/Select.svelte +0 -1155
  317. package/src/select/SelectKbdFixture.svelte +0 -29
  318. package/src/select/index.ts +0 -3
  319. package/src/select/meta.ts +0 -78
  320. package/src/select/types.ts +0 -2
  321. package/src/side-sheet/SideSheet.a11y.test.ts +0 -53
  322. package/src/side-sheet/SideSheet.kbd.test.ts +0 -63
  323. package/src/side-sheet/SideSheet.svelte +0 -659
  324. package/src/side-sheet/SideSheetKbdFixture.svelte +0 -30
  325. package/src/side-sheet/index.ts +0 -2
  326. package/src/side-sheet/meta.ts +0 -116
  327. package/src/skeleton/Skeleton.a11y.test.ts +0 -35
  328. package/src/skeleton/Skeleton.svelte +0 -131
  329. package/src/skeleton/SkeletonAvatar.svelte +0 -82
  330. package/src/skeleton/SkeletonButton.svelte +0 -90
  331. package/src/skeleton/SkeletonImage.svelte +0 -100
  332. package/src/skeleton/SkeletonParagraph.svelte +0 -103
  333. package/src/skeleton/SkeletonTitle.svelte +0 -74
  334. package/src/skeleton/context.ts +0 -11
  335. package/src/skeleton/index.ts +0 -8
  336. package/src/skeleton/meta.ts +0 -65
  337. package/src/slider/Slider.a11y.test.ts +0 -44
  338. package/src/slider/Slider.kbd.test.ts +0 -74
  339. package/src/slider/Slider.svelte +0 -716
  340. package/src/slider/SliderKbdFixture.svelte +0 -21
  341. package/src/slider/index.ts +0 -2
  342. package/src/slider/meta.ts +0 -55
  343. package/src/space/Space.a11y.test.ts +0 -22
  344. package/src/space/Space.svelte +0 -109
  345. package/src/space/SpaceA11yFixture.svelte +0 -17
  346. package/src/space/index.ts +0 -2
  347. package/src/space/meta.ts +0 -27
  348. package/src/spin/Spin.a11y.test.ts +0 -29
  349. package/src/spin/Spin.svelte +0 -293
  350. package/src/spin/SpinA11yFixture.svelte +0 -18
  351. package/src/spin/index.ts +0 -2
  352. package/src/spin/meta.ts +0 -72
  353. package/src/steps/Steps.a11y.test.ts +0 -39
  354. package/src/steps/Steps.kbd.test.ts +0 -73
  355. package/src/steps/Steps.svelte +0 -392
  356. package/src/steps/StepsKbdFixture.svelte +0 -21
  357. package/src/steps/index.ts +0 -3
  358. package/src/steps/meta.ts +0 -59
  359. package/src/steps/types.ts +0 -8
  360. package/src/switch/Switch.a11y.test.ts +0 -46
  361. package/src/switch/Switch.kbd.test.ts +0 -40
  362. package/src/switch/Switch.svelte +0 -310
  363. package/src/switch/SwitchKbdFixture.svelte +0 -22
  364. package/src/switch/index.ts +0 -2
  365. package/src/switch/meta.ts +0 -44
  366. package/src/table/Table.a11y.test.ts +0 -70
  367. package/src/table/Table.bench.ts +0 -51
  368. package/src/table/Table.kbd.test.ts +0 -94
  369. package/src/table/Table.svelte +0 -2327
  370. package/src/table/TableKbdFixture.svelte +0 -35
  371. package/src/table/grid-nav.test.ts +0 -173
  372. package/src/table/grid-nav.ts +0 -146
  373. package/src/table/index.ts +0 -13
  374. package/src/table/meta.ts +0 -121
  375. package/src/table/types.ts +0 -133
  376. package/src/tabs/TabPane.svelte +0 -92
  377. package/src/tabs/Tabs.a11y.test.ts +0 -47
  378. package/src/tabs/Tabs.kbd.test.ts +0 -74
  379. package/src/tabs/Tabs.svelte +0 -1128
  380. package/src/tabs/TabsA11yFixture.svelte +0 -20
  381. package/src/tabs/TabsKbdFixture.svelte +0 -25
  382. package/src/tabs/context.ts +0 -43
  383. package/src/tabs/index.ts +0 -14
  384. package/src/tabs/meta.ts +0 -152
  385. package/src/tabs/types.ts +0 -6
  386. package/src/tag/Tag.a11y.test.ts +0 -33
  387. package/src/tag/Tag.svelte +0 -366
  388. package/src/tag/TagA11yFixture.svelte +0 -18
  389. package/src/tag/index.ts +0 -2
  390. package/src/tag/meta.ts +0 -92
  391. package/src/tag-input/TagInput.a11y.test.ts +0 -41
  392. package/src/tag-input/TagInput.svelte +0 -544
  393. package/src/tag-input/index.ts +0 -2
  394. package/src/tag-input/meta.ts +0 -43
  395. package/src/tag-input/reorder.test.ts +0 -59
  396. package/src/tag-input/reorder.ts +0 -41
  397. package/src/test-utils/LocaleHarness.svelte +0 -29
  398. package/src/test-utils/a11y.ts +0 -108
  399. package/src/test-utils/kbd.ts +0 -52
  400. package/src/textarea/TextArea.a11y.test.ts +0 -44
  401. package/src/textarea/TextArea.svelte +0 -420
  402. package/src/textarea/index.ts +0 -2
  403. package/src/textarea/meta.ts +0 -104
  404. package/src/time-picker/TimePicker.a11y.test.ts +0 -45
  405. package/src/time-picker/TimePicker.kbd.test.ts +0 -70
  406. package/src/time-picker/TimePicker.svelte +0 -1023
  407. package/src/time-picker/TimePickerKbdFixture.svelte +0 -23
  408. package/src/time-picker/index.ts +0 -2
  409. package/src/time-picker/meta.ts +0 -64
  410. package/src/timeline/Timeline.a11y.test.ts +0 -54
  411. package/src/timeline/Timeline.svelte +0 -599
  412. package/src/timeline/TimelineItem.svelte +0 -164
  413. package/src/timeline/context.ts +0 -28
  414. package/src/timeline/index.ts +0 -16
  415. package/src/timeline/meta.ts +0 -129
  416. package/src/timeline/types.ts +0 -9
  417. package/src/toast/Toast.a11y.test.ts +0 -67
  418. package/src/toast/ToastContainer.svelte +0 -184
  419. package/src/toast/ToastItem.svelte +0 -247
  420. package/src/toast/index.ts +0 -9
  421. package/src/toast/live-region.ts +0 -78
  422. package/src/toast/meta.ts +0 -95
  423. package/src/toast/store.ts +0 -187
  424. package/src/tooltip/Tooltip.a11y.test.ts +0 -41
  425. package/src/tooltip/Tooltip.kbd.test.ts +0 -37
  426. package/src/tooltip/Tooltip.svelte +0 -416
  427. package/src/tooltip/TooltipA11yFixture.svelte +0 -31
  428. package/src/tooltip/TooltipKbdFixture.svelte +0 -16
  429. package/src/tooltip/index.ts +0 -2
  430. package/src/tooltip/meta.ts +0 -59
  431. package/src/tooltip/placement.ts +0 -6
  432. package/src/transfer/Transfer.a11y.test.ts +0 -63
  433. package/src/transfer/Transfer.kbd.test.ts +0 -75
  434. package/src/transfer/Transfer.svelte +0 -1451
  435. package/src/transfer/TransferKbdFixture.svelte +0 -23
  436. package/src/transfer/group.test.ts +0 -55
  437. package/src/transfer/group.ts +0 -61
  438. package/src/transfer/index.ts +0 -3
  439. package/src/transfer/meta.ts +0 -55
  440. package/src/transfer/reorder.test.ts +0 -52
  441. package/src/transfer/reorder.ts +0 -42
  442. package/src/transfer/tree.test.ts +0 -44
  443. package/src/transfer/tree.ts +0 -52
  444. package/src/transfer/types.ts +0 -32
  445. package/src/tree/Tree.a11y.test.ts +0 -63
  446. package/src/tree/Tree.kbd.test.ts +0 -91
  447. package/src/tree/Tree.svelte +0 -1500
  448. package/src/tree/TreeKbdFixture.svelte +0 -30
  449. package/src/tree/index.ts +0 -3
  450. package/src/tree/meta.ts +0 -152
  451. package/src/tree-select/TreeSelect.a11y.test.ts +0 -52
  452. package/src/tree-select/TreeSelect.kbd.test.ts +0 -53
  453. package/src/tree-select/TreeSelect.svelte +0 -1611
  454. package/src/tree-select/TreeSelectKbdFixture.svelte +0 -20
  455. package/src/tree-select/index.ts +0 -3
  456. package/src/tree-select/meta.ts +0 -73
  457. package/src/tree-select/types.ts +0 -11
  458. package/src/tree-select/virtual.test.ts +0 -104
  459. package/src/typography/Link.svelte +0 -120
  460. package/src/typography/Paragraph.svelte +0 -92
  461. package/src/typography/Text.svelte +0 -93
  462. package/src/typography/Title.svelte +0 -91
  463. package/src/typography/Typography.a11y.test.ts +0 -34
  464. package/src/typography/TypographyA11yFixture.svelte +0 -27
  465. package/src/typography/TypographyBase.svelte +0 -807
  466. package/src/typography/index.ts +0 -16
  467. package/src/typography/meta.ts +0 -158
  468. package/src/upload/Upload.a11y.test.ts +0 -58
  469. package/src/upload/Upload.svelte +0 -1094
  470. package/src/upload/UploadA11yFixture.svelte +0 -23
  471. package/src/upload/index.ts +0 -3
  472. package/src/upload/meta.ts +0 -64
  473. package/src/upload/types.ts +0 -16
  474. package/src/virtual-list/VirtualList.svelte +0 -401
  475. package/src/virtual-list/index.ts +0 -2
  476. package/src/virtual-list/meta.ts +0 -106
@@ -1,1611 +0,0 @@
1
- <!--
2
- TreeSelect — see specs/components/input/TreeSelect.spec.md
3
- 基础子集: 单选、可展开/收起的单面板树、叶子或任意节点选中 (leafOnly 控制)。
4
- multiple: checkbox 多选 + 父子联动 (复用 core conduct/toggleCheck),trigger 多 tag 回显可单独移除。
5
- filterable: 面板顶部搜索框过滤节点(复用 core computeFilteredKeys),命中 + 祖先链可见、命中文本高亮。
6
- Token-driven, a11y-correct, 受控/非受控。
7
- fieldNames:自定义节点字段名(key/label/children)映射任意后端数据;派生只读标准化,默认字段名时零开销。
8
- icon:自定义节点图标 snippet(showIcon 为真时渲染在 label 前),API 与 Tree 的 icon 对齐
9
- (参数 { node, expanded, level })。渲染层特性,不改 treeData(红线 #1)。
10
- loadData:展开未加载的非叶子节点时异步取子节点,结果缓存进本地 SvelteMap 并派生合并树喂给
11
- 所有 core 函数(不写回受控 treeData,红线 #1);加载中显示 spinner,竞态由 loadedKeys/loadingKeys 去重。
12
- virtualized:大数据树虚拟滚动。复用 Tree 范式——直接用 core fixedRange 纯函数自建轻量 fixed 定高
13
- 虚拟化(非复用 VirtualList 组件,其 role=list/listitem 会破坏 role=tree→treeitem 语义),保持
14
- role=tree 容器 + 行 role=treeitem 不变;只渲染视口内切片(flattenVisible 派生扁平节点 + 区间纯派生,
15
- 红线 #2)。滚动监听命令式 + rAF 节流 + cleanup(红线 #3)。virtualized 时与搜索 filterable 互斥
16
- (搜索强制展开命中链与定高虚拟化叠加复杂,故 virtualized 优先静态/异步大树场景)。
17
- -->
18
- <script lang="ts">
19
- import type { Snippet } from 'svelte';
20
- import {
21
- useId,
22
- useDismiss,
23
- conduct,
24
- toggleCheck,
25
- computeFilteredKeys,
26
- collectCheckedByStrategy,
27
- flattenVisible,
28
- fixedRange,
29
- scrollOffsetForIndex,
30
- rovingKeyFromEvent,
31
- type TreeNodeData,
32
- type FlatNode,
33
- type CheckedStrategy,
34
- } from '@chenzy-design/core';
35
- import { SvelteMap, SvelteSet } from 'svelte/reactivity';
36
- import { useLocale } from '../locale-provider/index.js';
37
- import { getGlobalPopupContainer } from '../config-provider/index.js';
38
- import { floating } from '../_floating/use-floating.js';
39
- import Tag from '../tag/Tag.svelte';
40
- import type { TreeNode, TreeKey } from './types.js';
41
-
42
- type Size = 'small' | 'default' | 'large';
43
- type Status = 'default' | 'warning' | 'error';
44
-
45
- /** 自定义节点字段名映射(适配任意后端数据结构)。默认 key/label/children。 */
46
- type FieldNames = { key?: string; label?: string; children?: string };
47
-
48
- interface Props {
49
- value?: TreeKey | TreeKey[] | null;
50
- defaultValue?: TreeKey | TreeKey[] | null;
51
- /**
52
- * 树数据源。默认节点字段为 key/label/children;
53
- * 用 fieldNames 自定义字段名时可传任意后端结构(如 { id, name, sub })。
54
- */
55
- treeData?: TreeNode[];
56
- /** 自定义节点字段名映射,如 { key:'id', label:'name', children:'sub' }。默认全部为标准名。 */
57
- fieldNames?: FieldNames;
58
- open?: boolean;
59
- defaultOpen?: boolean;
60
- /** 多选:面板节点显示 checkbox + 父子联动,trigger 多 tag 回显 */
61
- multiple?: boolean;
62
- /** 多选时父子勾选互不影响(无半选) */
63
- checkStrictly?: boolean;
64
- /**
65
- * 多选父子是否级联联动(对齐 Tree)。'related'(默认)父子联动;
66
- * 'unRelated' 互不影响(等价 checkStrictly)。checkStrictly=true 强制 unRelated(向后兼容)。
67
- */
68
- checkRelation?: 'related' | 'unRelated';
69
- /**
70
- * 多选回填值 / Tag 的收敛策略:'all'(默认,全部勾选节点)、
71
- * 'parent'(完全勾选父级折叠为父)、'child'(仅叶子)。仅 related 级联时生效。
72
- */
73
- showCheckedStrategy?: CheckedStrategy;
74
- /** 多选回填 Tag 最大展示数,超出折叠为 +N(仅影响显示,不改 value)。 */
75
- maxTagCount?: number;
76
- placeholder?: string;
77
- size?: Size;
78
- status?: Status;
79
- disabled?: boolean;
80
- clearable?: boolean;
81
- leafOnly?: boolean;
82
- defaultExpandAll?: boolean;
83
- /** 默认展开的节点 key(非受控初始展开集,与 defaultExpandAll 取并集)。 */
84
- treeDefaultExpandedKeys?: TreeKey[];
85
- /** 面板顶部搜索框过滤节点(命中 + 祖先链可见、高亮命中文本) */
86
- filterable?: boolean;
87
- /**
88
- * 远程搜索:输入仅触发 onSearch(防抖后),不本地过滤(由外部更新 treeData)。
89
- * 隐含 filterable 行为(显示搜索框)。默认 false。
90
- */
91
- remote?: boolean;
92
- /** onSearch 防抖毫秒(remote 时生效,默认 300)。 */
93
- searchDebounce?: number;
94
- /** 远程搜索输入回调(防抖后)。 */
95
- onSearch?: (query: string) => void;
96
- /** 是否预留节点图标位(icon 提供时渲染在 label 前)。默认 true,与 Tree 对齐。 */
97
- showIcon?: boolean;
98
- /** 异步加载子节点:展开未加载的非叶子节点时调用,返回该节点的子节点数组。与 Tree 的 loadData 对齐。 */
99
- loadData?: (node: TreeNode) => Promise<TreeNode[]>;
100
- /** 虚拟滚动:仅渲染视口内的可见节点行,适合大数据树(1000+ 节点)。默认 false(行为不变)。 */
101
- virtualized?: boolean;
102
- /**
103
- * 自动启用虚拟化的可见节点数阈值:可见扁平行数 ≥ 此值时自动开启虚拟化。
104
- * 默认 100。virtualized 显式为 true 时强制开启(不看阈值)。
105
- */
106
- virtualizeThreshold?: number;
107
- /** 浮层宽度对齐触发器(min-inline-size = 触发器宽)。默认 true。 */
108
- dropdownMatchSelectWidth?: boolean;
109
- /** 浮层挂载容器,缺省 ConfigProvider 全局值再回退 document.body。 */
110
- getPopupContainer?: () => HTMLElement | null | undefined;
111
- /** 关闭即从 DOM 卸载浮层(重开重建)。默认 false:首开后保留 DOM 仅隐藏。 */
112
- destroyOnClose?: boolean;
113
- /** 虚拟滚动视口高度(px)。virtualized 时生效,默认 224(与默认面板 max-height 一致)。 */
114
- height?: number;
115
- /** 虚拟滚动行高(px)。virtualized 时生效,默认取 token 行高 32。 */
116
- itemHeight?: number;
117
- onChange?: (value: TreeKey | TreeKey[] | null) => void;
118
- onOpenChange?: (open: boolean) => void;
119
- ariaLabel?: string;
120
- /** 自定义节点图标(showIcon 为真时渲染在 label 前);参数含节点与展开态,与 Tree 的 icon 对齐。 */
121
- icon?: Snippet<[{ node: TreeNode; expanded: boolean; level: number }]>;
122
-
123
- // --- Appearance ---
124
- /** 无边框模式:trigger 边框透明。默认 false。 */
125
- borderless?: boolean;
126
- /** trigger 前缀(Snippet 或字符串)。 */
127
- prefix?: Snippet | string;
128
- /** trigger 后缀(Snippet 或字符串)。 */
129
- suffix?: Snippet | string;
130
- /** 自定义清除按钮图标 Snippet。 */
131
- clearIcon?: Snippet;
132
- /**
133
- * 自定义展开图标 Snippet(参数 { node, expanded, level })。
134
- * 也可传普通 Snippet(无参数),此时统一渲染同一图标。
135
- */
136
- expandIcon?: Snippet<[{ node: TreeNode; expanded: boolean; level: number }]>;
137
- /** 自定义右侧下拉箭头(expandIcon 的别名) */
138
- arrowIcon?: Snippet;
139
- /** 显示节点连接线(垂直导引线)。默认 false。 */
140
- showLine?: boolean;
141
- /** 节点 label 单行省略截断。默认 true(保持原有行为,可关闭以允许换行)。 */
142
- labelEllipsis?: boolean;
143
- /** 选择框样式 */
144
- style?: string;
145
- /** 浮层遮挡时自动调整方向(默认 true) */
146
- autoAdjustOverflow?: boolean;
147
- /** status 的别名 */
148
- validateStatus?: 'default' | 'error' | 'warning';
149
- /** 失焦回调 */
150
- onBlur?: (e: FocusEvent) => void;
151
- /** 聚焦回调 */
152
- onFocus?: (e: FocusEvent) => void;
153
- /** 聚焦时阻止滚动 */
154
- preventScroll?: boolean;
155
-
156
- // --- Slots ---
157
- /** 面板顶部外层 slot(在搜索框之上)。 */
158
- outerTopSlot?: Snippet;
159
- /** 面板底部外层 slot(在树之下)。 */
160
- outerBottomSlot?: Snippet;
161
-
162
- // --- Search enhancements ---
163
- /** 面板打开时搜索框自动获焦。默认 false。 */
164
- searchAutoFocus?: boolean;
165
- /** 搜索框位置:'dropdown'(面板内,默认)或 'trigger'(trigger 内)。 */
166
- searchPosition?: 'dropdown' | 'trigger';
167
- /** 搜索框占位文字 */
168
- searchPlaceholder?: string;
169
- /** 搜索过滤属性(默认 'label') */
170
- treeNodeFilterProp?: string;
171
- /** 搜索框右侧显示清除按钮(有内容时)。默认 true。 */
172
- showSearchClear?: boolean;
173
- /** 搜索激活时仅显示命中节点,不显示祖先链。默认 false。 */
174
- showFilteredOnly?: boolean;
175
-
176
- // --- Expand control ---
177
- /** 受控展开的节点 keys */
178
- expandedKeys?: TreeKey[];
179
- /** 动态全部展开(与 defaultExpandAll 不同,此为受控/动态)。默认 false。 */
180
- expandAll?: boolean;
181
- /** 行点击展开方式:false(仅展开按钮触发)、'click'(单击行)、'doubleClick'(双击行)。默认 false。 */
182
- expandAction?: false | 'click' | 'doubleClick';
183
- /** 展开节点时自动展开其所有祖先链。默认 false。 */
184
- autoExpandParent?: boolean;
185
- /** 展开/折叠动画。默认 true。 */
186
- motionExpand?: boolean;
187
- /** 节点展开回调 */
188
- onExpand?: (expandedKeys: TreeKey[], info: { expanded: boolean; node: unknown }) => void;
189
-
190
- // --- Multi-select enhancements ---
191
- /**
192
- * 自动合并值:父节点全选时 value 不再包含其后代(等价 showCheckedStrategy='parent')。
193
- * 默认 true。autoMergeValue=false 时 showCheckedStrategy 原始生效。
194
- */
195
- autoMergeValue?: boolean;
196
- /** onChange 回调携带完整节点对象而非仅 key。默认 false。 */
197
- onChangeWithObject?: boolean;
198
- /** 超出 maxTagCount 折叠的 tag 以 popover 形式展示剩余项。默认 false。 */
199
- showRestTagsPopover?: boolean;
200
- /** 传给剩余 tags popover 的额外 props。 */
201
- restTagsPopoverProps?: Record<string, unknown>;
202
- /** trigger 多选 tags 换行显示(默认单行截断折叠)。默认 false。 */
203
- triggerTagWrap?: boolean;
204
-
205
- // --- Node rendering ---
206
- /** 自定义节点 label 渲染(仅替换文字部分,保留 checkbox/icon/expand 等)。 */
207
- renderLabel?: Snippet<[{ label: string; data: TreeNode; searchWord: string }]>;
208
- /** 完全自定义节点行渲染(替换整行内容)。 */
209
- renderFullLabel?: Snippet<[{ node: TreeNode; expanded: boolean; level: number; checked: boolean; halfChecked: boolean; selected: boolean }]>;
210
- /** 自定义 trigger 已选 tag 渲染(多选时每个 tag 独立渲染)。 */
211
- renderSelectedItem?: Snippet<[{ node: TreeNode; onRemove: () => void }]>;
212
- /** 节点数据中用作显示 label 的字段名(默认 'label')。 */
213
- treeNodeLabelProp?: string;
214
- /** 自定义 key/label/value 字段映射(优先级高于 fieldNames 的 key/label 部分)。 */
215
- keyMaps?: { key?: string; label?: string; value?: string };
216
- /** 选项列表容器的内联 style(字符串或对象形式)。 */
217
- optionListStyle?: string | Record<string, string>;
218
- /** 受控已加载的节点 keys */
219
- loadedKeys?: Set<TreeKey>;
220
-
221
- // --- Behavior ---
222
- /** 单选选中后自动关闭面板。默认 true。 */
223
- clickToHide?: boolean;
224
- /** 面板开启时点击 trigger 关闭面板。默认 true。 */
225
- clickTriggerToHide?: boolean;
226
- /**
227
- * 严格禁用:disabled 节点不因父节点联动而影响,禁用态独立(不传播给子节点)。
228
- * 默认 false(父禁用 conduct 不检查子)。
229
- */
230
- disableStrictly?: boolean;
231
- /** 浮层与 trigger 的额外间距(px),数字或四方向对象。 */
232
- dropdownMargin?: number | { marginTop?: number; marginBottom?: number; marginLeft?: number; marginRight?: number };
233
-
234
- // --- Events ---
235
- /** 点击清除按钮回调。 */
236
- onClear?: (e: MouseEvent) => void;
237
- /** 节点选中回调 */
238
- onSelect?: (selectedKey: TreeKey, selected: boolean, node: unknown) => void;
239
- /** 异步加载完成回调(含已加载 key 集合与当前节点)。 */
240
- onLoad?: (loadedKeys: TreeKey[], treeNode: TreeNode) => void;
241
- /** 面板可见性变化回调(与 onOpenChange 语义相同,Semi 风格别名)。 */
242
- onVisibleChange?: (isVisible: boolean) => void;
243
- }
244
-
245
- let {
246
- value,
247
- defaultValue = null,
248
- treeData = [],
249
- fieldNames,
250
- open,
251
- defaultOpen = false,
252
- multiple = false,
253
- checkStrictly = false,
254
- checkRelation = 'related',
255
- showCheckedStrategy = 'all',
256
- maxTagCount,
257
- placeholder = '请选择',
258
- size = 'default',
259
- status = 'default',
260
- disabled = false,
261
- clearable = false,
262
- leafOnly = false,
263
- defaultExpandAll = false,
264
- treeDefaultExpandedKeys,
265
- filterable = false,
266
- remote = false,
267
- searchDebounce = 300,
268
- onSearch,
269
- showIcon = true,
270
- loadData,
271
- virtualized = false,
272
- virtualizeThreshold = 100,
273
- dropdownMatchSelectWidth = true,
274
- getPopupContainer,
275
- destroyOnClose = false,
276
- height = 224,
277
- itemHeight = 32,
278
- onChange,
279
- onOpenChange,
280
- ariaLabel,
281
- icon,
282
- borderless = false,
283
- prefix,
284
- suffix,
285
- clearIcon,
286
- expandIcon,
287
- arrowIcon,
288
- showLine = false,
289
- labelEllipsis = true,
290
- style,
291
- autoAdjustOverflow = true,
292
- validateStatus,
293
- onBlur,
294
- onFocus,
295
- preventScroll = false,
296
- outerTopSlot,
297
- outerBottomSlot,
298
- searchAutoFocus = false,
299
- searchPosition = 'dropdown',
300
- searchPlaceholder,
301
- treeNodeFilterProp = 'label',
302
- showSearchClear = true,
303
- showFilteredOnly = false,
304
- expandedKeys: expandedKeysProp,
305
- expandAll = false,
306
- expandAction = false,
307
- autoExpandParent = false,
308
- motionExpand = true,
309
- onExpand,
310
- autoMergeValue = true,
311
- onChangeWithObject = false,
312
- showRestTagsPopover = false,
313
- restTagsPopoverProps,
314
- triggerTagWrap = false,
315
- renderLabel,
316
- renderFullLabel,
317
- renderSelectedItem,
318
- treeNodeLabelProp = 'label',
319
- keyMaps,
320
- optionListStyle,
321
- loadedKeys: loadedKeysProp,
322
- clickToHide = true,
323
- clickTriggerToHide = true,
324
- disableStrictly = false,
325
- dropdownMargin,
326
- onClear,
327
- onSelect,
328
- onLoad,
329
- onVisibleChange,
330
- }: Props = $props();
331
-
332
- const loc = useLocale();
333
- // ConfigProvider 全局浮层容器默认;自身 getPopupContainer prop 优先,未传时回退此值(再回退 body)。
334
- const globalPopupContainer = getGlobalPopupContainer();
335
- const resolvePopupContainer = $derived(getPopupContainer ?? globalPopupContainer);
336
-
337
- // remote 隐含可搜索(显示搜索框);checkRelation 归一:checkStrictly=true 强制 unRelated(向后兼容)。
338
- const isFilterable = $derived(filterable || remote);
339
- const isUnRelated = $derived(checkStrictly || checkRelation === 'unRelated');
340
-
341
- // validateStatus 是 status 别名;效值以 validateStatus 优先(未传时回退 status)。
342
- const effStatus = $derived(validateStatus ?? status ?? 'default');
343
-
344
- const treeId = useId('cd-tree-select-panel');
345
- // treeitem 行 id 基(aria-activedescendant 指向当前高亮行)。
346
- const itemBaseId = useId('cd-tree-select-item');
347
- function itemId(key: TreeKey): string {
348
- return `${itemBaseId}-${String(key)}`;
349
- }
350
-
351
- // --- fieldNames 字段映射:把用户自定义字段名的数据派生为标准 {key,label,children} 结构 ---
352
- // 默认(全标准名)时直接返回原 treeData 引用,零额外开销;映射为纯 $derived(红线 #2),不写回(红线 #1)。
353
- // 节点 key 取自原始 key 字段,故 onChange 回传的 value(key)即用户原始 id,无需额外映射回原节点。
354
- // keyMaps 优先级高于 fieldNames(key/label 部分);keyMaps.value 作为 key 字段别名。
355
- const keyField = $derived(keyMaps?.key ?? keyMaps?.value ?? fieldNames?.key ?? 'key');
356
- const labelField = $derived(keyMaps?.label ?? fieldNames?.label ?? 'label');
357
- const childrenField = $derived(fieldNames?.children ?? 'children');
358
- const fieldNamesDefault = $derived(
359
- keyField === 'key' && labelField === 'label' && childrenField === 'children',
360
- );
361
-
362
- function normalizeNodes(nodes: TreeNode[]): TreeNode[] {
363
- const kf = keyField;
364
- const lf = labelField;
365
- const cf = childrenField;
366
- const tnlp = treeNodeLabelProp;
367
- return nodes.map((raw) => {
368
- const r = raw as unknown as Record<string, unknown>;
369
- const kids = r[cf] as TreeNode[] | undefined;
370
- const out: TreeNode = {
371
- ...raw,
372
- key: r[kf] as TreeKey,
373
- label: r[lf] as string,
374
- };
375
- // treeNodeLabelProp 优先覆盖 label(当与 labelField 不同时)。
376
- if (tnlp !== 'label' && tnlp !== lf && r[tnlp] !== undefined) {
377
- out.label = r[tnlp] as string;
378
- }
379
- if (kids) out.children = normalizeNodes(kids);
380
- else delete out.children;
381
- return out;
382
- });
383
- }
384
-
385
- // 标准化后的树:默认时即 treeData 原引用(零开销),否则递归映射字段名。
386
- const normalizedTree = $derived<TreeNode[]>(
387
- fieldNamesDefault ? treeData : normalizeNodes(treeData),
388
- );
389
-
390
- // --- 异步加载(对齐 Tree):本地缓存子节点 + loading/loaded 标记(不写回受控 treeData,红线 #1)---
391
- // loadedChildren 缓存已加载子树;loadingKeys 加载中(防重入/竞态);loadedKeys 已完成(防重复请求)。
392
- const loadedChildren = new SvelteMap<TreeKey, TreeNode[]>();
393
- const loadingKeys = new SvelteSet<TreeKey>();
394
- const loadedKeys = new SvelteSet<TreeKey>();
395
-
396
- // 合并树:把已加载的子节点注入对应节点,喂给所有 core 纯函数与渲染。
397
- // 无加载时返回 normalizedTree 原引用,零开销(红线 #2 纯派生)。
398
- const mergedTree = $derived.by<TreeNode[]>(() => {
399
- if (loadedChildren.size === 0) return normalizedTree;
400
- const inject = (nodes: TreeNode[]): TreeNode[] =>
401
- nodes.map((n) => {
402
- const loaded = loadedChildren.get(n.key);
403
- const kids = n.children ?? loaded;
404
- if (!kids) return n;
405
- return { ...n, children: inject(kids) };
406
- });
407
- return inject(normalizedTree);
408
- });
409
-
410
- // 异步加载某节点子树(竞态:loadingKeys/loadedKeys 去重;快速展开/折叠不重复请求)。
411
- async function loadChildren(node: TreeNode) {
412
- if (!loadData || loadingKeys.has(node.key) || loadedKeys.has(node.key)) return;
413
- loadingKeys.add(node.key);
414
- try {
415
- const kids = await loadData(node);
416
- loadedChildren.set(node.key, fieldNamesDefault ? kids : normalizeNodes(kids));
417
- } finally {
418
- loadingKeys.delete(node.key);
419
- loadedKeys.add(node.key);
420
- onLoad?.([...loadedKeys], node);
421
- }
422
- }
423
-
424
- // --- 纯函数: 递归查找节点 (用于回显 label) ---
425
- function findNode(data: TreeNode[], key: TreeKey): TreeNode | undefined {
426
- for (const node of data) {
427
- if (node.key === key) return node;
428
- if (node.children) {
429
- const found = findNode(node.children, key);
430
- if (found) return found;
431
- }
432
- }
433
- return undefined;
434
- }
435
-
436
- // --- 纯函数: 收集全部有 children 的节点 key (defaultExpandAll 用) ---
437
- function collectExpandable(data: TreeNode[], acc: TreeKey[]): TreeKey[] {
438
- for (const node of data) {
439
- if (node.children && node.children.length > 0) {
440
- acc.push(node.key);
441
- collectExpandable(node.children, acc);
442
- }
443
- }
444
- return acc;
445
- }
446
-
447
- function asKeyArray(v: TreeKey | TreeKey[] | null | undefined): TreeKey[] {
448
- if (v === null || v === undefined) return [];
449
- return Array.isArray(v) ? [...v] : [v];
450
- }
451
-
452
- function getInitialSingle(): TreeKey | null {
453
- if (Array.isArray(defaultValue)) return defaultValue[0] ?? null;
454
- return defaultValue ?? null;
455
- }
456
- function getInitialChecked(): Set<TreeKey> {
457
- return new Set(asKeyArray(defaultValue));
458
- }
459
- function getInitialOpen(): boolean {
460
- return defaultOpen;
461
- }
462
- function getInitialExpanded(): Set<TreeKey> {
463
- // treeDefaultExpandedKeys 与 defaultExpandAll 取并集(非受控初始展开集)。
464
- const set = new Set<TreeKey>(treeDefaultExpandedKeys ?? []);
465
- if (defaultExpandAll) {
466
- // defaultExpandAll 需用标准化后的 key(fieldNames 自定义时才能识别 children)。
467
- const base = fieldNamesDefault ? treeData : normalizeNodes(treeData);
468
- for (const k of collectExpandable(base, [])) set.add(k);
469
- }
470
- return set;
471
- }
472
-
473
- // --- 受控 value (红线 #1): 不无条件回写 value,仅 onChange ---
474
- const isValueControlled = $derived(value !== undefined);
475
-
476
- // 单选:当前选中 key
477
- let innerValue = $state<TreeKey | null>(getInitialSingle());
478
- const currentValue = $derived<TreeKey | null>(
479
- isValueControlled
480
- ? (Array.isArray(value) ? (value[0] ?? null) : (value ?? null))
481
- : innerValue,
482
- );
483
-
484
- // 多选:叶子级显式勾选 base + conduct 归一(含半选)
485
- let innerChecked = $state<Set<TreeKey>>(getInitialChecked());
486
- const currentCheckedBase = $derived<Set<TreeKey>>(
487
- isValueControlled ? new Set(asKeyArray(value)) : innerChecked,
488
- );
489
- const checkState = $derived.by(() => {
490
- if (!multiple) return { checked: new Set<TreeKey>(), half: new Set<TreeKey>() };
491
- if (isUnRelated) return { checked: new Set(currentCheckedBase), half: new Set<TreeKey>() };
492
- return conduct(mergedTree as unknown as TreeNodeData[], currentCheckedBase);
493
- });
494
- // autoMergeValue=true 时把 showCheckedStrategy='all' 升级为 'parent'(父选不含后代)。
495
- const effectiveStrategy = $derived<CheckedStrategy>(
496
- !autoMergeValue
497
- ? showCheckedStrategy
498
- : showCheckedStrategy === 'all'
499
- ? 'parent'
500
- : showCheckedStrategy,
501
- );
502
-
503
- // 回填值/Tag 收敛集(effectiveStrategy):unRelated 无父子关系故策略不生效(取全 checked)。
504
- const strategyKeys = $derived.by<TreeKey[]>(() => {
505
- if (!multiple) return [];
506
- if (isUnRelated) return [...checkState.checked];
507
- return collectCheckedByStrategy(
508
- mergedTree as unknown as TreeNodeData[],
509
- checkState.checked,
510
- effectiveStrategy,
511
- );
512
- });
513
- // trigger 回显的已选节点(按收敛策略,树序)
514
- const checkedNodes = $derived.by<TreeNode[]>(() => {
515
- if (!multiple) return [];
516
- const keep = new Set(strategyKeys);
517
- const out: TreeNode[] = [];
518
- const walk = (nodes: TreeNode[]) => {
519
- for (const n of nodes) {
520
- if (keep.has(n.key)) out.push(n);
521
- if (n.children) walk(n.children);
522
- }
523
- };
524
- walk(mergedTree);
525
- return out;
526
- });
527
- // maxTagCount 折叠:显示前 N 个 tag + 隐藏数(仅影响显示,不改 value,红线 #1/#2)。
528
- const visibleTagNodes = $derived(
529
- maxTagCount !== undefined && maxTagCount >= 0
530
- ? checkedNodes.slice(0, maxTagCount)
531
- : checkedNodes,
532
- );
533
- const hiddenTagCount = $derived(
534
- maxTagCount !== undefined && maxTagCount >= 0
535
- ? Math.max(0, checkedNodes.length - maxTagCount)
536
- : 0,
537
- );
538
-
539
- // --- 受控 open (红线 #1): 不无条件回写 open,仅 onOpenChange ---
540
- const isOpenControlled = $derived(open !== undefined);
541
- let innerOpen = $state(getInitialOpen());
542
- const isOpen = $derived(isOpenControlled ? !!open : innerOpen);
543
-
544
- // onVisibleChange 是 onOpenChange 别名:isOpen 变化时同步触发。
545
- $effect(() => {
546
- onVisibleChange?.(isOpen);
547
- });
548
-
549
- // --- 本地展开状态 (红线 #2): expandedKeys 本地 $state Set,不依赖挂载 registry ---
550
- let expandedKeys = $state<Set<TreeKey>>(getInitialExpanded());
551
-
552
- const selectedNode = $derived(
553
- currentValue === null ? undefined : findNode(mergedTree, currentValue),
554
- );
555
- const displayLabel = $derived(selectedNode?.label ?? '');
556
- const hasSelection = $derived(
557
- multiple ? checkedNodes.length > 0 : selectedNode !== undefined,
558
- );
559
- const showClear = $derived(clearable && !disabled && hasSelection);
560
-
561
- function setValue(next: TreeKey | null) {
562
- if (!isValueControlled) innerValue = next;
563
- onChange?.(next);
564
- }
565
-
566
- // 多选:勾选 base 变更 → conduct 归一 → 按 effectiveStrategy 收敛后回调
567
- function setChecked(nextBase: Set<TreeKey>) {
568
- if (!isValueControlled) innerChecked = nextBase;
569
- if (isUnRelated) {
570
- onChange?.([...nextBase]);
571
- return;
572
- }
573
- const resolved = conduct(mergedTree as unknown as TreeNodeData[], nextBase).checked;
574
- onChange?.(
575
- collectCheckedByStrategy(
576
- mergedTree as unknown as TreeNodeData[],
577
- resolved,
578
- effectiveStrategy,
579
- ),
580
- );
581
- }
582
-
583
- function toggleCheckNode(node: TreeNode) {
584
- if (node.disabled || disabled) return;
585
- let nextBase: Set<TreeKey>;
586
- if (isUnRelated) {
587
- nextBase = new Set(currentCheckedBase);
588
- if (nextBase.has(node.key)) nextBase.delete(node.key);
589
- else nextBase.add(node.key);
590
- } else {
591
- nextBase = toggleCheck(mergedTree as unknown as TreeNodeData[], currentCheckedBase, node.key);
592
- }
593
- setChecked(nextBase);
594
- }
595
-
596
- // 移除某 tag:把该节点(及其子树,related)从勾选中去掉
597
- function removeChecked(node: TreeNode) {
598
- if (disabled) return;
599
- const isChecked = checkState.checked.has(node.key);
600
- if (isUnRelated) {
601
- const next = new Set(currentCheckedBase);
602
- next.delete(node.key);
603
- setChecked(next);
604
- } else if (isChecked) {
605
- // 复用 toggleCheck:已选 → 取消(含子树联动)
606
- setChecked(toggleCheck(mergedTree as unknown as TreeNodeData[], currentCheckedBase, node.key));
607
- }
608
- }
609
-
610
- function nodeCheckState(node: TreeNode): { checked: boolean; half: boolean } {
611
- return {
612
- checked: checkState.checked.has(node.key),
613
- half: !checkState.checked.has(node.key) && checkState.half.has(node.key),
614
- };
615
- }
616
-
617
- function setOpen(next: boolean) {
618
- if (next === isOpen) return;
619
- if (!next) searchValue = '';
620
- if (!isOpenControlled) innerOpen = next;
621
- onOpenChange?.(next);
622
- onVisibleChange?.(next);
623
- }
624
-
625
- function toggleOpen() {
626
- if (disabled) return;
627
- if (isOpen && !clickTriggerToHide) return;
628
- setOpen(!isOpen);
629
- }
630
-
631
- function hasChildren(node: TreeNode): boolean {
632
- return !!node.children && node.children.length > 0;
633
- }
634
-
635
- // 行是否可展开(含异步占位,对齐 Tree.isExpandable):
636
- // 有真实子节点 → 是;否则有 loadData、非叶子、且(未加载 → 显示箭头占位 / 已加载非空 → 是)。
637
- function isExpandable(node: TreeNode): boolean {
638
- if (hasChildren(node)) return true;
639
- if (!loadData || node.isLeaf === true) return false;
640
- if (loadedKeys.has(node.key)) return (loadedChildren.get(node.key)?.length ?? 0) > 0;
641
- return true;
642
- }
643
-
644
- // --- 搜索过滤(本地 state,复用 core computeFilteredKeys)---
645
- let searchValue = $state('');
646
- const trimmedSearch = $derived(searchValue.trim());
647
- // remote 模式不本地过滤(外部更新 treeData),仅本地 filterable 时高亮/收敛命中链。
648
- const searchActive = $derived(!remote && isFilterable && trimmedSearch.length > 0);
649
- const filterResult = $derived.by(() => {
650
- if (!searchActive) return { matched: new Set<TreeKey>(), expand: new Set<TreeKey>() };
651
- const lower = trimmedSearch.toLowerCase();
652
- return computeFilteredKeys(
653
- mergedTree as unknown as TreeNodeData[],
654
- (node) => node.label.toLowerCase().includes(lower),
655
- );
656
- });
657
- // 节点在搜索结果可见:命中本身、或在祖先链/含命中后代(expand 集)。
658
- // showFilteredOnly=true 时只显示精确命中节点,不显示祖先链。
659
- function nodeVisible(key: TreeKey): boolean {
660
- if (!searchActive) return true;
661
- if (showFilteredOnly) return filterResult.matched.has(key);
662
- return filterResult.matched.has(key) || filterResult.expand.has(key);
663
- }
664
-
665
- function isExpanded(key: TreeKey): boolean {
666
- // 搜索激活时强制展开命中链
667
- if (searchActive && filterResult.expand.has(key)) return true;
668
- return expandedKeys.has(key);
669
- }
670
-
671
- // --- 虚拟滚动(复用 Tree 范式):派生展开集 → flattenVisible 扁平可见行 → fixedRange 视口切片 ---
672
- // 搜索激活时把过滤展开集并入可见展开集(派生,不写回受控 expandedKeys,红线 #1/#2)。
673
- // expandAll=true 时动态收集所有可展开节点 key。
674
- const effectiveExpanded = $derived.by(() => {
675
- if (expandAll) {
676
- const set = new Set<TreeKey>();
677
- const walk = (nodes: TreeNode[]) => {
678
- for (const n of nodes) {
679
- if (n.children && n.children.length > 0) {
680
- set.add(n.key);
681
- walk(n.children);
682
- }
683
- }
684
- };
685
- walk(mergedTree);
686
- // 同时并入搜索展开链。
687
- if (searchActive) for (const k of filterResult.expand) set.add(k);
688
- return set;
689
- }
690
- if (!searchActive) return expandedKeys;
691
- const merged = new Set(expandedKeys);
692
- for (const k of filterResult.expand) merged.add(k);
693
- return merged;
694
- });
695
- // virtualizeThreshold:节点总数 ≥ 阈值时自动启用虚拟化(virtualized=true 时强制启用)。
696
- const totalNodeCount = $derived.by(() => {
697
- let n = 0;
698
- const walk = (nodes: TreeNode[]) => {
699
- for (const node of nodes) {
700
- n++;
701
- if (node.children) walk(node.children);
702
- }
703
- };
704
- walk(mergedTree);
705
- return n;
706
- });
707
- const useVirtual = $derived(virtualized || totalNodeCount >= virtualizeThreshold);
708
- // 可见扁平节点:虚拟化用于视口切片,非虚拟化也用于键盘 roving 导航(红线 #2 纯派生)。
709
- const flat = $derived(
710
- flattenVisible(mergedTree as unknown as TreeNodeData[], effectiveExpanded),
711
- );
712
- // 搜索时仅保留命中或祖先链上的行(与递归 snippet 的 nodeVisible 一致)。
713
- const visibleFlat = $derived.by<FlatNode[]>(() => {
714
- if (!searchActive) return flat;
715
- return flat.filter(
716
- (f) => filterResult.matched.has(f.node.key) || filterResult.expand.has(f.node.key),
717
- );
718
- });
719
-
720
- // --- roving 焦点:activeKey + 派生高亮(aria-activedescendant);render 不读 DOM(红线 #2)---
721
- let activeKey = $state<TreeKey | null>(null);
722
- const activeDescId = $derived.by(() => {
723
- if (activeKey === null) return undefined;
724
- const exists = visibleFlat.some((f) => f.node.key === activeKey);
725
- return exists ? itemId(activeKey) : undefined;
726
- });
727
-
728
- // 面板关闭时复位高亮,避免下次打开停在旧项。
729
- $effect(() => {
730
- if (!isOpen) activeKey = null;
731
- });
732
-
733
- const VIRTUAL_OVERSCAN = 4;
734
- let viewportEl = $state<HTMLDivElement | null>(null);
735
- let scrollTop = $state(0);
736
- let rafId = 0;
737
- const rowHeight = $derived(itemHeight > 0 ? itemHeight : 32);
738
- const totalHeight = $derived(visibleFlat.length * rowHeight);
739
- // 可视区间:纯 $derived,仅依赖本地 $state,render-safe 不读 DOM(红线 #2)。
740
- const vRange = $derived(
741
- useVirtual
742
- ? fixedRange(scrollTop, height, rowHeight, visibleFlat.length, VIRTUAL_OVERSCAN)
743
- : { startIndex: 0, endIndex: visibleFlat.length },
744
- );
745
- const renderFlat = $derived(
746
- useVirtual ? visibleFlat.slice(vRange.startIndex, vRange.endIndex) : visibleFlat,
747
- );
748
-
749
- // 滚动监听(命令式 + rAF 节流 + cleanup,红线 #3)。
750
- $effect(() => {
751
- const el = viewportEl;
752
- if (!el || !useVirtual) return;
753
- function onScroll() {
754
- if (rafId) return;
755
- rafId = requestAnimationFrame(() => {
756
- rafId = 0;
757
- if (el) scrollTop = el.scrollTop;
758
- });
759
- }
760
- el.addEventListener('scroll', onScroll, { passive: true });
761
- return () => {
762
- el.removeEventListener('scroll', onScroll);
763
- if (rafId) {
764
- cancelAnimationFrame(rafId);
765
- rafId = 0;
766
- }
767
- };
768
- });
769
-
770
- // 面板关闭时复位 scrollTop,避免下次打开停在旧位置(命令式 viewport 也随之卸载/重建)。
771
- $effect(() => {
772
- if (!isOpen) scrollTop = 0;
773
- });
774
-
775
- // --- 命中文本高亮:拆成片段供模板渲染 ---
776
- type HlPart = { text: string; mark: boolean };
777
- function highlightParts(text: string): HlPart[] {
778
- if (!searchActive) return [{ text, mark: false }];
779
- const lower = text.toLowerCase();
780
- const term = trimmedSearch.toLowerCase();
781
- const parts: HlPart[] = [];
782
- let from = 0;
783
- let idx = lower.indexOf(term, from);
784
- while (idx !== -1) {
785
- if (idx > from) parts.push({ text: text.slice(from, idx), mark: false });
786
- parts.push({ text: text.slice(idx, idx + term.length), mark: true });
787
- from = idx + term.length;
788
- idx = lower.indexOf(term, from);
789
- }
790
- if (from < text.length) parts.push({ text: text.slice(from), mark: false });
791
- return parts;
792
- }
793
-
794
- function toggleExpand(node: TreeNode) {
795
- const key = node.key;
796
- const next = new Set(expandedKeys);
797
- if (next.has(key)) {
798
- next.delete(key);
799
- } else {
800
- next.add(key);
801
- // autoExpandParent:展开时同时展开所有祖先链。
802
- if (autoExpandParent) {
803
- const findAncestors = (nodes: TreeNode[], target: TreeKey, path: TreeKey[]): TreeKey[] | null => {
804
- for (const n of nodes) {
805
- if (n.key === target) return path;
806
- if (n.children) {
807
- const found = findAncestors(n.children, target, [...path, n.key]);
808
- if (found) return found;
809
- }
810
- }
811
- return null;
812
- };
813
- const ancestors = findAncestors(mergedTree, key, []);
814
- if (ancestors) for (const ak of ancestors) next.add(ak);
815
- }
816
- // 展开未加载的异步节点:先取数据(数据到位后合并树派生即显示子节点)。
817
- if (!hasChildren(node) && loadData && !loadedKeys.has(key)) void loadChildren(node);
818
- }
819
- expandedKeys = next;
820
- }
821
-
822
- function selectNode(node: TreeNode) {
823
- if (node.disabled || disabled) return;
824
- if (multiple) {
825
- // 多选:父节点也可勾选(联动子树),不关面板
826
- toggleCheckNode(node);
827
- return;
828
- }
829
- if (leafOnly && isExpandable(node)) {
830
- toggleExpand(node);
831
- return;
832
- }
833
- setValue(node.key);
834
- if (clickToHide) setOpen(false);
835
- }
836
-
837
- // 行点击处理器:expandAction 控制行点击是否展开。
838
- function handleRowClick(node: TreeNode) {
839
- if (node.disabled || disabled) return;
840
- if (multiple) {
841
- toggleCheckNode(node);
842
- return;
843
- }
844
- if (leafOnly && isExpandable(node)) {
845
- if (expandAction !== false) toggleExpand(node);
846
- return;
847
- }
848
- // expandAction='click' 时行点击也展开。
849
- if (expandAction === 'click' && isExpandable(node)) {
850
- toggleExpand(node);
851
- }
852
- setValue(node.key);
853
- if (clickToHide) setOpen(false);
854
- }
855
-
856
- // 行双击处理器:expandAction='doubleClick' 时双击展开/折叠。
857
- function handleRowDoubleClick(node: TreeNode) {
858
- if (expandAction === 'doubleClick' && isExpandable(node)) toggleExpand(node);
859
- }
860
-
861
- function clearAll(e: MouseEvent) {
862
- e.stopPropagation();
863
- if (disabled) return;
864
- if (multiple) {
865
- setChecked(new Set());
866
- } else {
867
- setValue(null);
868
- }
869
- onClear?.(e);
870
- }
871
-
872
- // --- 键盘 roving 导航(aria-activedescendant 模型):焦点留 role=tree 容器,
873
- // 方向键移动 activeKey 高亮,全部基于派生 visibleFlat 与 activeKey(红线 #2)---
874
- function isRowDisabled(node: TreeNode): boolean {
875
- return disabled || !!node.disabled;
876
- }
877
- function activeIndex(): number {
878
- if (activeKey === null) return -1;
879
- return visibleFlat.findIndex((f) => f.node.key === activeKey);
880
- }
881
- function moveNext() {
882
- const cur = activeIndex();
883
- let i = cur < 0 ? 0 : cur + 1;
884
- while (i < visibleFlat.length && isRowDisabled((visibleFlat[i] as FlatNode).node as unknown as TreeNode)) i++;
885
- if (i < visibleFlat.length) activeKey = (visibleFlat[i] as FlatNode).node.key;
886
- }
887
- function movePrev() {
888
- const cur = activeIndex();
889
- let i = cur < 0 ? visibleFlat.length - 1 : cur - 1;
890
- while (i >= 0 && isRowDisabled((visibleFlat[i] as FlatNode).node as unknown as TreeNode)) i--;
891
- if (i >= 0) activeKey = (visibleFlat[i] as FlatNode).node.key;
892
- }
893
- function moveFirst() {
894
- let i = 0;
895
- while (i < visibleFlat.length && isRowDisabled((visibleFlat[i] as FlatNode).node as unknown as TreeNode)) i++;
896
- if (i < visibleFlat.length) activeKey = (visibleFlat[i] as FlatNode).node.key;
897
- }
898
- function moveLast() {
899
- let i = visibleFlat.length - 1;
900
- while (i >= 0 && isRowDisabled((visibleFlat[i] as FlatNode).node as unknown as TreeNode)) i--;
901
- if (i >= 0) activeKey = (visibleFlat[i] as FlatNode).node.key;
902
- }
903
- function currentFlat(): FlatNode | undefined {
904
- const i = activeIndex();
905
- return i >= 0 ? visibleFlat[i] : undefined;
906
- }
907
-
908
- // 命令式滚到指定行索引使其落入视口(虚拟化时键盘移动 activeKey 后调用)。
909
- function scrollIndexIntoView(index: number) {
910
- const el = viewportEl;
911
- if (!el || !useVirtual || index < 0) return;
912
- const itemStart = index * rowHeight;
913
- const top = el.scrollTop;
914
- const bottom = top + el.clientHeight;
915
- if (itemStart >= top && itemStart + rowHeight <= bottom) return;
916
- const align = itemStart < top ? 'start' : 'end';
917
- const target = scrollOffsetForIndex(itemStart, rowHeight, el.clientHeight, totalHeight, align);
918
- el.scrollTop = target;
919
- scrollTop = target;
920
- }
921
- function scrollActiveIntoView() {
922
- if (!useVirtual || activeKey === null) return;
923
- scrollIndexIntoView(visibleFlat.findIndex((f) => f.node.key === activeKey));
924
- }
925
-
926
- function onTreeKeydown(e: KeyboardEvent) {
927
- if (disabled || visibleFlat.length === 0) return;
928
- const intent = rovingKeyFromEvent(e.key);
929
- // 首次方向键:从首项起步。
930
- if (activeKey === null && intent) {
931
- e.preventDefault();
932
- moveFirst();
933
- scrollActiveIntoView();
934
- return;
935
- }
936
- const f = currentFlat();
937
- switch (e.key) {
938
- case 'ArrowDown':
939
- e.preventDefault();
940
- moveNext();
941
- break;
942
- case 'ArrowUp':
943
- e.preventDefault();
944
- movePrev();
945
- break;
946
- case 'ArrowRight':
947
- e.preventDefault();
948
- if (f && isExpandable(f.node as unknown as TreeNode)) {
949
- if (!isExpanded(f.node.key)) toggleExpand(f.node as unknown as TreeNode);
950
- else moveNext();
951
- }
952
- break;
953
- case 'ArrowLeft':
954
- e.preventDefault();
955
- if (f && isExpandable(f.node as unknown as TreeNode) && isExpanded(f.node.key)) {
956
- toggleExpand(f.node as unknown as TreeNode);
957
- } else if (f && f.parentKey !== null) {
958
- activeKey = f.parentKey;
959
- }
960
- break;
961
- case 'Home':
962
- e.preventDefault();
963
- moveFirst();
964
- break;
965
- case 'End':
966
- e.preventDefault();
967
- moveLast();
968
- break;
969
- case 'Enter':
970
- e.preventDefault();
971
- if (f) selectNode(f.node as unknown as TreeNode);
972
- break;
973
- case ' ':
974
- e.preventDefault();
975
- if (f) selectNode(f.node as unknown as TreeNode);
976
- break;
977
- default:
978
- return;
979
- }
980
- scrollActiveIntoView();
981
- }
982
-
983
- // --- remote 搜索防抖(命令式定时器 + cleanup,红线 #3)---
984
- let searchTimer: ReturnType<typeof setTimeout> | undefined;
985
- function scheduleSearch(q: string) {
986
- if (searchTimer !== undefined) clearTimeout(searchTimer);
987
- searchTimer = setTimeout(() => {
988
- searchTimer = undefined;
989
- onSearch?.(q);
990
- }, Math.max(0, searchDebounce));
991
- }
992
- // 卸载兜底清理。
993
- $effect(() => () => {
994
- if (searchTimer !== undefined) clearTimeout(searchTimer);
995
- });
996
- function onSearchInput(e: Event & { currentTarget: HTMLInputElement }) {
997
- searchValue = e.currentTarget.value;
998
- if (remote) scheduleSearch(searchValue.trim());
999
- }
1000
-
1001
- // --- DOM 引用:触发根 + portal 面板(定位由 use:floating action 接管)---
1002
- let rootEl = $state<HTMLDivElement | null>(null);
1003
- let panelEl = $state<HTMLDivElement | null>(null);
1004
-
1005
- // --- destroyOnClose:默认 false 时首开后保留浮层 DOM(仅隐藏),true 时关闭即卸载,重开重建。 ---
1006
- let hasBeenOpened = $state(false);
1007
- $effect(() => {
1008
- if (isOpen) hasBeenOpened = true;
1009
- });
1010
- const shouldRender = $derived(destroyOnClose ? isOpen : isOpen || hasBeenOpened);
1011
-
1012
- // --- useDismiss (红线 #3): panel portal 出 root 子树后列入 extraTargets ---
1013
- $effect(() => {
1014
- if (!isOpen || !rootEl) return;
1015
- return useDismiss(rootEl, {
1016
- onDismiss: () => setOpen(false),
1017
- escape: true,
1018
- outsideClick: true,
1019
- extraTargets: [panelEl],
1020
- });
1021
- });
1022
-
1023
- const cls = $derived(
1024
- [
1025
- 'cd-tree-select',
1026
- `cd-tree-select--${size}`,
1027
- `cd-tree-select--${effStatus}`,
1028
- disabled && 'cd-tree-select--disabled',
1029
- isOpen && 'cd-tree-select--open',
1030
- borderless && 'cd-tree-select--borderless',
1031
- showLine && 'cd-tree-select--show-line',
1032
- !motionExpand && 'cd-tree-select--no-motion-expand',
1033
- triggerTagWrap && 'cd-tree-select--tag-wrap',
1034
- ]
1035
- .filter(Boolean)
1036
- .join(' '),
1037
- );
1038
-
1039
- // optionListStyle 规范化为 style 字符串。
1040
- const optionListStyleStr = $derived(
1041
- typeof optionListStyle === 'string'
1042
- ? optionListStyle
1043
- : optionListStyle
1044
- ? Object.entries(optionListStyle)
1045
- .map(([k, v]) => `${k}:${v}`)
1046
- .join(';')
1047
- : undefined,
1048
- );
1049
- </script>
1050
-
1051
- {#snippet nodeRow(
1052
- node: TreeNode,
1053
- level: number,
1054
- posStyle: string | undefined,
1055
- setSize: number,
1056
- posInSet: number,
1057
- )}
1058
- {@const expandable = isExpandable(node)}
1059
- {@const loading = loadingKeys.has(node.key)}
1060
- {@const nodeOpen = expandable && isExpanded(node.key)}
1061
- {@const cs = nodeCheckState(node)}
1062
- {@const selected = multiple ? cs.checked : currentValue === node.key}
1063
- {@const active = activeKey === node.key}
1064
- <!-- treeitem 焦点经容器 aria-activedescendant 漫游管理,行本身 tabindex=-1,键盘统一在 role=tree 容器处理 -->
1065
- <!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
1066
- <div
1067
- id={itemId(node.key)}
1068
- class="cd-tree-select__node"
1069
- class:cd-tree-select__node--selected={!multiple && selected}
1070
- class:cd-tree-select__node--active={active}
1071
- role="treeitem"
1072
- aria-level={level + 1}
1073
- aria-setsize={setSize}
1074
- aria-posinset={posInSet}
1075
- aria-selected={selected}
1076
- aria-checked={multiple ? (cs.checked ? true : cs.half ? 'mixed' : false) : undefined}
1077
- aria-expanded={expandable ? nodeOpen : undefined}
1078
- aria-disabled={node.disabled || undefined}
1079
- style={[posStyle, `padding-inline-start: calc(${level} * var(--cd-tree-indent))`]
1080
- .filter(Boolean)
1081
- .join('; ')}
1082
- onclick={() => selectNode(node)}
1083
- tabindex={-1}
1084
- >
1085
- {#if loading}
1086
- <span class="cd-tree-select__expand cd-tree-select__expand--loading" aria-hidden="true">
1087
- <span class="cd-tree-select__spinner"></span>
1088
- </span>
1089
- {:else if expandable}
1090
- <span
1091
- class="cd-tree-select__expand"
1092
- class:cd-tree-select__expand--open={nodeOpen}
1093
- role="button"
1094
- tabindex="-1"
1095
- aria-label={nodeOpen ? loc().t('Tree.collapse') : loc().t('Tree.expand')}
1096
- onclick={(e) => {
1097
- e.stopPropagation();
1098
- toggleExpand(node);
1099
- }}
1100
- onkeydown={(e) => {
1101
- if (e.key === 'Enter' || e.key === ' ') {
1102
- e.preventDefault();
1103
- e.stopPropagation();
1104
- toggleExpand(node);
1105
- }
1106
- }}
1107
- >
1108
- <svg viewBox="0 0 16 16" width="10" height="10" aria-hidden="true" focusable="false">
1109
- <path fill="currentColor" d="M6 4l4 4-4 4V4Z" />
1110
- </svg>
1111
- </span>
1112
- {:else}
1113
- <span class="cd-tree-select__expand cd-tree-select__expand--placeholder" aria-hidden="true"></span>
1114
- {/if}
1115
- {#if multiple}
1116
- <span
1117
- class="cd-tree-select__checkbox"
1118
- class:cd-tree-select__checkbox--checked={cs.checked}
1119
- class:cd-tree-select__checkbox--half={cs.half}
1120
- aria-hidden="true"
1121
- >
1122
- {#if cs.checked}
1123
- <svg viewBox="0 0 16 16" width="12" height="12" aria-hidden="true" focusable="false">
1124
- <path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M3.5 8.5l3 3 6-6.5" />
1125
- </svg>
1126
- {:else if cs.half}
1127
- <svg viewBox="0 0 16 16" width="12" height="12" aria-hidden="true" focusable="false">
1128
- <path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M4 8h8" />
1129
- </svg>
1130
- {/if}
1131
- </span>
1132
- {/if}
1133
- {#if showIcon}
1134
- <span class="cd-tree-select__icon" aria-hidden="true">
1135
- {#if icon}{@render icon({ node, expanded: nodeOpen, level })}{/if}
1136
- </span>
1137
- {/if}
1138
- <span class="cd-tree-select__node-label">
1139
- {#if searchActive}
1140
- {#each highlightParts(node.label) as part, i (i)}
1141
- {#if part.mark}<mark class="cd-tree-select__highlight">{part.text}</mark>{:else}{part.text}{/if}
1142
- {/each}
1143
- {:else}
1144
- {node.label}
1145
- {/if}
1146
- </span>
1147
- </div>
1148
- {/snippet}
1149
-
1150
- {#snippet treeNodes(nodes: TreeNode[], level: number)}
1151
- {@const setSize = nodes.length}
1152
- {#each nodes as node, i (node.key)}
1153
- {#if nodeVisible(node.key)}
1154
- {@const nodeOpen = isExpandable(node) && isExpanded(node.key)}
1155
- {@render nodeRow(node, level, undefined, setSize, i + 1)}
1156
- {#if nodeOpen}
1157
- {@render treeNodes(node.children ?? [], level + 1)}
1158
- {/if}
1159
- {/if}
1160
- {/each}
1161
- {/snippet}
1162
-
1163
- <div class={cls} bind:this={rootEl} {style}>
1164
- <!-- combobox 容器用 div 以合法承载多选 tags / clear 等内部交互元素 -->
1165
- <div
1166
- class="cd-tree-select__trigger"
1167
- role="combobox"
1168
- aria-haspopup="tree"
1169
- aria-expanded={isOpen}
1170
- aria-controls={treeId}
1171
- aria-activedescendant={isOpen && !searchActive ? activeDescId : undefined}
1172
- aria-label={ariaLabel}
1173
- aria-invalid={effStatus === 'error' || undefined}
1174
- aria-disabled={disabled || undefined}
1175
- tabindex={disabled ? -1 : 0}
1176
- onclick={toggleOpen}
1177
- onfocus={(e) => onFocus?.(e)}
1178
- onblur={(e) => onBlur?.(e)}
1179
- onkeydown={(e) => {
1180
- if (disabled) return;
1181
- if (!isOpen) {
1182
- // 关闭态:Enter/Space/Down 打开浮层。
1183
- if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
1184
- e.preventDefault();
1185
- setOpen(true);
1186
- }
1187
- return;
1188
- }
1189
- if (e.key === 'Escape') {
1190
- setOpen(false);
1191
- return;
1192
- }
1193
- // 打开态:搜索激活时焦点在搜索框,由其处理;否则触发器(焦点宿主)驱动树 roving。
1194
- if (!searchActive) onTreeKeydown(e);
1195
- }}
1196
- >
1197
- {#if prefix}
1198
- <span class="cd-tree-select__prefix">
1199
- {#if typeof prefix === 'string'}{prefix}{:else}{@render prefix()}{/if}
1200
- </span>
1201
- {/if}
1202
- <span class="cd-tree-select__content">
1203
- {#if multiple}
1204
- {#if checkedNodes.length > 0}
1205
- <span class="cd-tree-select__tags">
1206
- {#each visibleTagNodes as node (node.key)}
1207
- <Tag
1208
- size={size === 'large' ? 'default' : 'small'}
1209
- closable={!disabled}
1210
- onClose={() => removeChecked(node)}
1211
- >
1212
- {node.label}
1213
- </Tag>
1214
- {/each}
1215
- {#if hiddenTagCount > 0}
1216
- <Tag size={size === 'large' ? 'default' : 'small'}>+{hiddenTagCount}</Tag>
1217
- {/if}
1218
- </span>
1219
- {:else}
1220
- <span class="cd-tree-select__placeholder">{placeholder}</span>
1221
- {/if}
1222
- {:else if hasSelection}
1223
- <span class="cd-tree-select__value">{displayLabel}</span>
1224
- {:else}
1225
- <span class="cd-tree-select__placeholder">{placeholder}</span>
1226
- {/if}
1227
- </span>
1228
-
1229
- {#if showClear}
1230
- <span
1231
- class="cd-tree-select__clear"
1232
- role="button"
1233
- tabindex="-1"
1234
- aria-label={loc().t('TreeSelect.clear')}
1235
- onclick={(e) => { onClear?.(e); clearAll(e); }}
1236
- onkeydown={(e) => {
1237
- if (e.key === 'Enter' || e.key === ' ') {
1238
- e.preventDefault();
1239
- onClear?.(e);
1240
- clearAll(e as unknown as MouseEvent);
1241
- }
1242
- }}
1243
- >
1244
- <svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true" focusable="false">
1245
- <path
1246
- fill="currentColor"
1247
- d="M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1Zm2.5 9.1-1.4 1.4L8 9.4 6.5 11l-1.4-1.4L6.6 8 5.1 6.5 6.5 5.1 8 6.6 9.5 5.1l1.4 1.4L9.4 8l1.1 1.1Z"
1248
- />
1249
- </svg>
1250
- </span>
1251
- {/if}
1252
-
1253
- {#if suffix}
1254
- <span class="cd-tree-select__suffix">
1255
- {#if typeof suffix === 'string'}{suffix}{:else}{@render suffix()}{/if}
1256
- </span>
1257
- {/if}
1258
- <span class="cd-tree-select__arrow" aria-hidden="true">
1259
- {#if arrowIcon}
1260
- {@render arrowIcon()}
1261
- {:else}
1262
- <svg viewBox="0 0 16 16" width="12" height="12" focusable="false">
1263
- <path fill="currentColor" d="M3.5 6 8 10.5 12.5 6l-1-1L8 8.5 4.5 5l-1 1Z" />
1264
- </svg>
1265
- {/if}
1266
- </span>
1267
- </div>
1268
-
1269
- {#if shouldRender}
1270
- <div
1271
- class="cd-tree-select__panel"
1272
- class:cd-tree-select__panel--hidden={!isOpen}
1273
- bind:this={panelEl}
1274
- use:floating={{
1275
- trigger: rootEl,
1276
- placement: 'bottomStart',
1277
- autoAdjust: true,
1278
- offset: 4,
1279
- matchWidth: dropdownMatchSelectWidth,
1280
- getContainer: resolvePopupContainer,
1281
- open: isOpen,
1282
- }}
1283
- id={treeId}
1284
- >
1285
- {#if isFilterable}
1286
- <div class="cd-tree-select__search">
1287
- <input
1288
- class="cd-tree-select__search-input"
1289
- type="text"
1290
- role="combobox"
1291
- aria-expanded={isOpen}
1292
- aria-controls={treeId}
1293
- aria-activedescendant={activeDescId}
1294
- placeholder={loc().t('TreeSelect.searchPlaceholder')}
1295
- aria-label={loc().t('TreeSelect.searchPlaceholder')}
1296
- value={searchValue}
1297
- oninput={onSearchInput}
1298
- onkeydown={(e) => {
1299
- if (e.key === 'Escape') {
1300
- setOpen(false);
1301
- return;
1302
- }
1303
- // 搜索框聚焦时方向键/Home/End/Enter 在过滤后的可见树上 roving。
1304
- onTreeKeydown(e);
1305
- }}
1306
- />
1307
- </div>
1308
- {/if}
1309
- {#if mergedTree.length === 0}
1310
- <div class="cd-tree-select__tree" role="tree">
1311
- <div class="cd-tree-select__empty">{loc().t('TreeSelect.emptyText')}</div>
1312
- </div>
1313
- {:else if useVirtual}
1314
- <!-- 虚拟滚动(复用 Tree 范式):role=tree 容器自身滚动,spacer 撑总高,行绝对定位按索引偏移。
1315
- 只渲染视口内切片 renderFlat,保持 role=tree → role=treeitem 语义不变。 -->
1316
- <div
1317
- class="cd-tree-select__tree cd-tree-select__tree--virtual"
1318
- role="tree"
1319
- aria-multiselectable={multiple || undefined}
1320
- aria-activedescendant={activeDescId}
1321
- aria-disabled={disabled || undefined}
1322
- tabindex={disabled ? -1 : 0}
1323
- bind:this={viewportEl}
1324
- style={`block-size:${height}px`}
1325
- onkeydown={onTreeKeydown}
1326
- >
1327
- {#if visibleFlat.length === 0}
1328
- <div class="cd-tree-select__empty">{loc().t('TreeSelect.emptyText')}</div>
1329
- {:else}
1330
- <div class="cd-tree-select__spacer" style={`block-size:${totalHeight}px`}>
1331
- {#each renderFlat as f, i (f.node.key)}
1332
- {@render nodeRow(
1333
- f.node as unknown as TreeNode,
1334
- f.level,
1335
- `position:absolute; inset-inline:0; transform:translateY(${(vRange.startIndex + i) * rowHeight}px); block-size:${rowHeight}px`,
1336
- f.setSize,
1337
- f.posInSet,
1338
- )}
1339
- {/each}
1340
- </div>
1341
- {/if}
1342
- </div>
1343
- {:else}
1344
- <div
1345
- class="cd-tree-select__tree"
1346
- role="tree"
1347
- aria-multiselectable={multiple || undefined}
1348
- aria-activedescendant={activeDescId}
1349
- aria-disabled={disabled || undefined}
1350
- tabindex={disabled ? -1 : 0}
1351
- onkeydown={onTreeKeydown}
1352
- >
1353
- {@render treeNodes(mergedTree, 0)}
1354
- {#if searchActive && filterResult.matched.size === 0}
1355
- <div class="cd-tree-select__empty">{loc().t('TreeSelect.emptyText')}</div>
1356
- {/if}
1357
- </div>
1358
- {/if}
1359
- </div>
1360
- {/if}
1361
- </div>
1362
-
1363
- <style>
1364
- .cd-tree-select {
1365
- position: relative;
1366
- display: inline-flex;
1367
- inline-size: 100%;
1368
- font-size: var(--cd-select-font-size);
1369
- }
1370
- .cd-tree-select__trigger {
1371
- display: flex;
1372
- align-items: center;
1373
- gap: var(--cd-spacing-2);
1374
- inline-size: 100%;
1375
- min-block-size: var(--cd-select-height-default);
1376
- padding-inline: var(--cd-select-padding-x);
1377
- background: var(--cd-select-bg);
1378
- border: 1px solid var(--cd-select-border);
1379
- border-radius: var(--cd-select-radius);
1380
- color: inherit;
1381
- font: inherit;
1382
- text-align: start;
1383
- cursor: pointer;
1384
- transition: border-color var(--cd-motion-duration-fast) var(--cd-motion-ease-standard);
1385
- }
1386
- .cd-tree-select--small .cd-tree-select__trigger {
1387
- min-block-size: var(--cd-select-height-small);
1388
- }
1389
- .cd-tree-select--large .cd-tree-select__trigger {
1390
- min-block-size: var(--cd-select-height-large);
1391
- }
1392
- .cd-tree-select__trigger:focus-visible {
1393
- outline: none;
1394
- border-color: var(--cd-select-border-active);
1395
- box-shadow: var(--cd-focus-ring);
1396
- }
1397
- .cd-tree-select--open .cd-tree-select__trigger {
1398
- border-color: var(--cd-select-border-active);
1399
- }
1400
- .cd-tree-select--error .cd-tree-select__trigger {
1401
- border-color: var(--cd-select-border-error);
1402
- }
1403
- .cd-tree-select__trigger[aria-disabled='true'] {
1404
- background: var(--cd-color-fill-0);
1405
- color: var(--cd-color-text-3);
1406
- cursor: not-allowed;
1407
- }
1408
- .cd-tree-select__content {
1409
- display: flex;
1410
- flex: 1 1 auto;
1411
- align-items: center;
1412
- min-inline-size: 0;
1413
- }
1414
- .cd-tree-select__value {
1415
- overflow: hidden;
1416
- white-space: nowrap;
1417
- text-overflow: ellipsis;
1418
- }
1419
- .cd-tree-select__placeholder {
1420
- color: var(--cd-input-color-placeholder);
1421
- }
1422
- .cd-tree-select__clear,
1423
- .cd-tree-select__arrow {
1424
- display: inline-flex;
1425
- align-items: center;
1426
- justify-content: center;
1427
- flex: 0 0 auto;
1428
- color: var(--cd-color-text-2);
1429
- }
1430
- .cd-tree-select__clear {
1431
- cursor: pointer;
1432
- }
1433
- .cd-tree-select__clear:hover {
1434
- color: var(--cd-color-text-0);
1435
- }
1436
- .cd-tree-select__arrow {
1437
- transition: transform var(--cd-motion-duration-fast) var(--cd-motion-ease-standard);
1438
- }
1439
- .cd-tree-select--open .cd-tree-select__arrow {
1440
- transform: rotate(180deg);
1441
- }
1442
- /* 面板 portal 到容器,由 use:floating action 写 position:fixed + transform 定位 */
1443
- .cd-tree-select__panel {
1444
- z-index: var(--cd-select-dropdown-z);
1445
- padding-block: var(--cd-spacing-1);
1446
- background: var(--cd-select-dropdown-bg);
1447
- border-radius: var(--cd-select-dropdown-radius);
1448
- box-shadow: var(--cd-select-dropdown-shadow);
1449
- }
1450
- /* destroyOnClose=false 关闭后保留 DOM 但不可见、不可交互、不占位 */
1451
- .cd-tree-select__panel--hidden {
1452
- display: none;
1453
- }
1454
- .cd-tree-select__search {
1455
- padding-block-end: var(--cd-spacing-1);
1456
- padding-inline: var(--cd-spacing-2);
1457
- }
1458
- .cd-tree-select__search-input {
1459
- inline-size: 100%;
1460
- block-size: var(--cd-input-height-small);
1461
- padding-inline: var(--cd-input-padding-x);
1462
- background: var(--cd-input-bg, transparent);
1463
- color: inherit;
1464
- border: 1px solid var(--cd-input-border);
1465
- border-radius: var(--cd-input-radius);
1466
- font: inherit;
1467
- font-size: var(--cd-font-size-1);
1468
- }
1469
- .cd-tree-select__search-input:focus-visible {
1470
- outline: none;
1471
- border-color: var(--cd-input-border-active);
1472
- box-shadow: var(--cd-focus-ring);
1473
- }
1474
- .cd-tree-select__tree {
1475
- max-block-size: 14rem;
1476
- overflow-y: auto;
1477
- }
1478
- /* 虚拟滚动:容器自身定高滚动,spacer 绝对定位行布局,max-block-size 让位给固定 height */
1479
- .cd-tree-select__tree--virtual {
1480
- position: relative;
1481
- max-block-size: none;
1482
- overflow-y: auto;
1483
- }
1484
- .cd-tree-select__spacer {
1485
- position: relative;
1486
- inline-size: 100%;
1487
- }
1488
- .cd-tree-select__highlight {
1489
- padding: 0;
1490
- color: var(--cd-tree-search-highlight-color);
1491
- background: var(--cd-tree-search-highlight-bg);
1492
- }
1493
- .cd-tree-select__node {
1494
- display: flex;
1495
- align-items: center;
1496
- gap: var(--cd-spacing-1);
1497
- block-size: var(--cd-tree-node-height);
1498
- padding-inline-end: var(--cd-tree-node-padding-x);
1499
- cursor: pointer;
1500
- }
1501
- .cd-tree-select__node:hover {
1502
- background: var(--cd-tree-node-bg-hover);
1503
- }
1504
- .cd-tree-select__node--selected {
1505
- color: var(--cd-tree-node-color-selected);
1506
- background: var(--cd-tree-node-bg-active);
1507
- }
1508
- /* 键盘 roving 高亮(aria-activedescendant 当前项),焦点环不依赖真实 DOM 焦点 */
1509
- .cd-tree-select__node--active {
1510
- box-shadow: var(--cd-focus-ring);
1511
- }
1512
- .cd-tree-select__node:focus-visible {
1513
- outline: none;
1514
- box-shadow: var(--cd-focus-ring);
1515
- }
1516
- .cd-tree-select__node[aria-disabled='true'] {
1517
- color: var(--cd-color-text-3);
1518
- cursor: not-allowed;
1519
- }
1520
- .cd-tree-select__expand {
1521
- display: inline-flex;
1522
- align-items: center;
1523
- justify-content: center;
1524
- flex: 0 0 auto;
1525
- inline-size: 1rem;
1526
- block-size: 1rem;
1527
- color: var(--cd-tree-expand-icon-color);
1528
- cursor: pointer;
1529
- transition: transform var(--cd-motion-duration-fast) var(--cd-motion-ease-standard);
1530
- }
1531
- .cd-tree-select__expand--open {
1532
- transform: rotate(90deg);
1533
- }
1534
- .cd-tree-select__expand--placeholder {
1535
- cursor: default;
1536
- }
1537
- .cd-tree-select__expand--loading {
1538
- cursor: default;
1539
- transition: none;
1540
- }
1541
- .cd-tree-select__spinner {
1542
- inline-size: 0.75rem;
1543
- block-size: 0.75rem;
1544
- border: 2px solid var(--cd-color-border, currentColor);
1545
- border-block-start-color: var(--cd-color-primary);
1546
- border-radius: 50%;
1547
- animation: cd-tree-select-spin 0.7s linear infinite;
1548
- }
1549
- @keyframes cd-tree-select-spin {
1550
- to {
1551
- transform: rotate(360deg);
1552
- }
1553
- }
1554
- .cd-tree-select__icon {
1555
- display: inline-flex;
1556
- align-items: center;
1557
- justify-content: center;
1558
- flex: 0 0 auto;
1559
- inline-size: 0;
1560
- block-size: 1rem;
1561
- color: var(--cd-tree-node-color);
1562
- }
1563
- /* 有自定义图标内容时撑开尺寸(对齐 Tree 的 .cd-tree__icon) */
1564
- .cd-tree-select__icon:not(:empty) {
1565
- inline-size: 1rem;
1566
- }
1567
- .cd-tree-select__node-label {
1568
- overflow: hidden;
1569
- white-space: nowrap;
1570
- text-overflow: ellipsis;
1571
- }
1572
- .cd-tree-select__checkbox {
1573
- display: inline-flex;
1574
- align-items: center;
1575
- justify-content: center;
1576
- flex: 0 0 auto;
1577
- inline-size: 1rem;
1578
- block-size: 1rem;
1579
- color: #fff;
1580
- background: var(--cd-color-bg-1, #fff);
1581
- border: 1px solid var(--cd-color-border);
1582
- border-radius: var(--cd-radius-small, 3px);
1583
- }
1584
- .cd-tree-select__checkbox--checked,
1585
- .cd-tree-select__checkbox--half {
1586
- background: var(--cd-color-primary);
1587
- border-color: var(--cd-color-primary);
1588
- }
1589
- .cd-tree-select__tags {
1590
- display: flex;
1591
- flex-wrap: wrap;
1592
- gap: var(--cd-spacing-1);
1593
- align-items: center;
1594
- min-inline-size: 0;
1595
- }
1596
- .cd-tree-select__empty {
1597
- padding: var(--cd-tree-node-padding-x);
1598
- color: var(--cd-color-text-3);
1599
- text-align: center;
1600
- }
1601
- @media (prefers-reduced-motion: reduce) {
1602
- .cd-tree-select__trigger,
1603
- .cd-tree-select__arrow,
1604
- .cd-tree-select__expand {
1605
- transition: none;
1606
- }
1607
- .cd-tree-select__spinner {
1608
- animation: none;
1609
- }
1610
- }
1611
- </style>