@superad/ui 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (343) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -0
  3. package/es/Button/Button.d.mts +7 -0
  4. package/es/Button/Button.mjs +29 -0
  5. package/es/Button/Button.mjs.map +1 -0
  6. package/es/Button/index.d.mts +3 -0
  7. package/es/Button/index.d.ts +1 -0
  8. package/es/Button/index.js +1 -0
  9. package/es/Button/index.mjs +2 -0
  10. package/es/Button/style.mjs +33 -0
  11. package/es/Button/style.mjs.map +1 -0
  12. package/es/Button/type.d.mts +21 -0
  13. package/es/ConfigProvider/index.d.mts +9 -0
  14. package/es/ConfigProvider/index.d.ts +1 -0
  15. package/es/ConfigProvider/index.js +1 -0
  16. package/es/ConfigProvider/index.mjs +82 -0
  17. package/es/ConfigProvider/index.mjs.map +1 -0
  18. package/es/ConfigProvider/type.d.mts +24 -0
  19. package/es/DraggablePanel/DraggablePanel.d.mts +6 -0
  20. package/es/DraggablePanel/DraggablePanel.mjs +104 -0
  21. package/es/DraggablePanel/DraggablePanel.mjs.map +1 -0
  22. package/es/DraggablePanel/index.d.mts +3 -0
  23. package/es/DraggablePanel/index.d.ts +1 -0
  24. package/es/DraggablePanel/index.js +1 -0
  25. package/es/DraggablePanel/index.mjs +3 -0
  26. package/es/DraggablePanel/style.mjs +86 -0
  27. package/es/DraggablePanel/style.mjs.map +1 -0
  28. package/es/DraggablePanel/type.d.mts +27 -0
  29. package/es/Flex/Center.d.mts +7 -0
  30. package/es/Flex/Center.mjs +14 -0
  31. package/es/Flex/Center.mjs.map +1 -0
  32. package/es/Flex/FlexBasic.d.mts +6 -0
  33. package/es/Flex/FlexBasic.mjs +46 -0
  34. package/es/Flex/FlexBasic.mjs.map +1 -0
  35. package/es/Flex/Flexbox.d.mts +2 -0
  36. package/es/Flex/index.d.mts +5 -0
  37. package/es/Flex/index.d.ts +1 -0
  38. package/es/Flex/index.js +1 -0
  39. package/es/Flex/index.mjs +3 -0
  40. package/es/Flex/type.d.mts +35 -0
  41. package/es/Flex/utils.mjs +24 -0
  42. package/es/Flex/utils.mjs.map +1 -0
  43. package/es/FontLoader/index.d.mts +9 -0
  44. package/es/FontLoader/index.d.ts +1 -0
  45. package/es/FontLoader/index.js +1 -0
  46. package/es/FontLoader/index.mjs +23 -0
  47. package/es/FontLoader/index.mjs.map +1 -0
  48. package/es/GlobalFocusRing/index.d.mts +6 -0
  49. package/es/GlobalFocusRing/index.d.ts +1 -0
  50. package/es/GlobalFocusRing/index.js +1 -0
  51. package/es/GlobalFocusRing/index.mjs +204 -0
  52. package/es/GlobalFocusRing/index.mjs.map +1 -0
  53. package/es/GlobalFocusRing/style.mjs +35 -0
  54. package/es/GlobalFocusRing/style.mjs.map +1 -0
  55. package/es/Provider.d.mts +5 -0
  56. package/es/Table/canvas/canvasPalette.mjs +11 -0
  57. package/es/Table/canvas/canvasPalette.mjs.map +1 -0
  58. package/es/Table/canvas/chipPainters.mjs +408 -0
  59. package/es/Table/canvas/chipPainters.mjs.map +1 -0
  60. package/es/Table/canvas/controlPainters.mjs +193 -0
  61. package/es/Table/canvas/controlPainters.mjs.map +1 -0
  62. package/es/Table/canvas/groupTitle.mjs +181 -0
  63. package/es/Table/canvas/groupTitle.mjs.map +1 -0
  64. package/es/Table/canvas/paintUtils.mjs +70 -0
  65. package/es/Table/canvas/paintUtils.mjs.map +1 -0
  66. package/es/Table/canvas/rowSeries.mjs +130 -0
  67. package/es/Table/canvas/rowSeries.mjs.map +1 -0
  68. package/es/Table/canvas/textPainters.mjs +97 -0
  69. package/es/Table/canvas/textPainters.mjs.map +1 -0
  70. package/es/Table/canvas/theme.d.mts +14 -0
  71. package/es/Table/canvas/theme.mjs +276 -0
  72. package/es/Table/canvas/theme.mjs.map +1 -0
  73. package/es/Table/core/cellValue.d.mts +1 -0
  74. package/es/Table/core/cellValue.mjs +240 -0
  75. package/es/Table/core/cellValue.mjs.map +1 -0
  76. package/es/Table/core/rowSelection.mjs +138 -0
  77. package/es/Table/core/rowSelection.mjs.map +1 -0
  78. package/es/Table/core/size.mjs +60 -0
  79. package/es/Table/core/size.mjs.map +1 -0
  80. package/es/Table/core/tableI18n.d.mts +11 -0
  81. package/es/Table/core/tableI18n.mjs +123 -0
  82. package/es/Table/core/tableI18n.mjs.map +1 -0
  83. package/es/Table/core/tagTone.mjs +245 -0
  84. package/es/Table/core/tagTone.mjs.map +1 -0
  85. package/es/Table/core/zIndex.mjs +7 -0
  86. package/es/Table/core/zIndex.mjs.map +1 -0
  87. package/es/Table/index.d.mts +6 -0
  88. package/es/Table/index.d.ts +1 -0
  89. package/es/Table/index.js +1 -0
  90. package/es/Table/index.mjs +6 -0
  91. package/es/Table/react/Table.d.mts +9 -0
  92. package/es/Table/react/Table.mjs +407 -0
  93. package/es/Table/react/Table.mjs.map +1 -0
  94. package/es/Table/react/TableColumnSettings.mjs +65 -0
  95. package/es/Table/react/TableColumnSettings.mjs.map +1 -0
  96. package/es/Table/react/a11y.mjs +72 -0
  97. package/es/Table/react/a11y.mjs.map +1 -0
  98. package/es/Table/react/editors/base.mjs +427 -0
  99. package/es/Table/react/editors/base.mjs.map +1 -0
  100. package/es/Table/react/editors/inline.mjs +225 -0
  101. package/es/Table/react/editors/inline.mjs.map +1 -0
  102. package/es/Table/react/editors/picker.mjs +368 -0
  103. package/es/Table/react/editors/picker.mjs.map +1 -0
  104. package/es/Table/react/overlayChips.mjs +50 -0
  105. package/es/Table/react/overlayChips.mjs.map +1 -0
  106. package/es/Table/react/sameRenderInputs.mjs +50 -0
  107. package/es/Table/react/sameRenderInputs.mjs.map +1 -0
  108. package/es/Table/react/style.mjs +650 -0
  109. package/es/Table/react/style.mjs.map +1 -0
  110. package/es/Table/react/useTableCellEvents.mjs +32 -0
  111. package/es/Table/react/useTableCellEvents.mjs.map +1 -0
  112. package/es/Table/react/useTableColumnSettings.mjs +107 -0
  113. package/es/Table/react/useTableColumnSettings.mjs.map +1 -0
  114. package/es/Table/react/useTableEditorSync.mjs +41 -0
  115. package/es/Table/react/useTableEditorSync.mjs.map +1 -0
  116. package/es/Table/react/useTableHoverHint.mjs +62 -0
  117. package/es/Table/react/useTableHoverHint.mjs.map +1 -0
  118. package/es/Table/react/useTableKeyboard.mjs +41 -0
  119. package/es/Table/react/useTableKeyboard.mjs.map +1 -0
  120. package/es/Table/react/useTableRenderState.mjs +194 -0
  121. package/es/Table/react/useTableRenderState.mjs.map +1 -0
  122. package/es/Table/react/useTableScroll.mjs +33 -0
  123. package/es/Table/react/useTableScroll.mjs.map +1 -0
  124. package/es/Table/react/useTableSelectionInfo.mjs +48 -0
  125. package/es/Table/react/useTableSelectionInfo.mjs.map +1 -0
  126. package/es/Table/react/useTableVTableEvents.mjs +132 -0
  127. package/es/Table/react/useTableVTableEvents.mjs.map +1 -0
  128. package/es/Table/type.d.mts +279 -0
  129. package/es/Table/type.mjs +34 -0
  130. package/es/Table/type.mjs.map +1 -0
  131. package/es/Table/vtable/columns.d.mts +17 -0
  132. package/es/Table/vtable/columns.mjs +381 -0
  133. package/es/Table/vtable/columns.mjs.map +1 -0
  134. package/es/Table/vtable/frozenShadow.mjs +27 -0
  135. package/es/Table/vtable/frozenShadow.mjs.map +1 -0
  136. package/es/Table/vtable/interaction.mjs +175 -0
  137. package/es/Table/vtable/interaction.mjs.map +1 -0
  138. package/es/Table/vtable/option.d.mts +39 -0
  139. package/es/Table/vtable/option.mjs +232 -0
  140. package/es/Table/vtable/option.mjs.map +1 -0
  141. package/es/Table/vtable/seriesSelection.mjs +374 -0
  142. package/es/Table/vtable/seriesSelection.mjs.map +1 -0
  143. package/es/Table/vtable/vtableAdapter.mjs +96 -0
  144. package/es/Table/vtable/vtableAdapter.mjs.map +1 -0
  145. package/es/ThemeProvider/AppElementContext.d.mts +5 -0
  146. package/es/ThemeProvider/AppElementContext.mjs +10 -0
  147. package/es/ThemeProvider/AppElementContext.mjs.map +1 -0
  148. package/es/ThemeProvider/ConfigProvider.mjs +29 -0
  149. package/es/ThemeProvider/ConfigProvider.mjs.map +1 -0
  150. package/es/ThemeProvider/GlobalStyle/antdOverride.mjs +73 -0
  151. package/es/ThemeProvider/GlobalStyle/antdOverride.mjs.map +1 -0
  152. package/es/ThemeProvider/GlobalStyle/essential.mjs +50 -0
  153. package/es/ThemeProvider/GlobalStyle/essential.mjs.map +1 -0
  154. package/es/ThemeProvider/GlobalStyle/global.mjs +90 -0
  155. package/es/ThemeProvider/GlobalStyle/global.mjs.map +1 -0
  156. package/es/ThemeProvider/GlobalStyle/index.mjs +14 -0
  157. package/es/ThemeProvider/GlobalStyle/index.mjs.map +1 -0
  158. package/es/ThemeProvider/Meta.d.mts +7 -0
  159. package/es/ThemeProvider/Meta.mjs +48 -0
  160. package/es/ThemeProvider/Meta.mjs.map +1 -0
  161. package/es/ThemeProvider/ThemeProvider.d.mts +6 -0
  162. package/es/ThemeProvider/ThemeProvider.mjs +89 -0
  163. package/es/ThemeProvider/ThemeProvider.mjs.map +1 -0
  164. package/es/ThemeProvider/constants.d.mts +5 -0
  165. package/es/ThemeProvider/constants.mjs +6 -0
  166. package/es/ThemeProvider/constants.mjs.map +1 -0
  167. package/es/ThemeProvider/index.d.mts +6 -0
  168. package/es/ThemeProvider/index.d.ts +1 -0
  169. package/es/ThemeProvider/index.js +1 -0
  170. package/es/ThemeProvider/index.mjs +5 -0
  171. package/es/ThemeProvider/type.d.mts +48 -0
  172. package/es/_virtual/_rolldown/runtime.mjs +13 -0
  173. package/es/color/colors/blue.d.mts +6 -0
  174. package/es/color/colors/blue.mjs +67 -0
  175. package/es/color/colors/blue.mjs.map +1 -0
  176. package/es/color/colors/cyan.d.mts +6 -0
  177. package/es/color/colors/cyan.mjs +67 -0
  178. package/es/color/colors/cyan.mjs.map +1 -0
  179. package/es/color/colors/geekblue.d.mts +6 -0
  180. package/es/color/colors/geekblue.mjs +67 -0
  181. package/es/color/colors/geekblue.mjs.map +1 -0
  182. package/es/color/colors/gold.d.mts +6 -0
  183. package/es/color/colors/gold.mjs +67 -0
  184. package/es/color/colors/gold.mjs.map +1 -0
  185. package/es/color/colors/gray.d.mts +6 -0
  186. package/es/color/colors/gray.mjs +67 -0
  187. package/es/color/colors/gray.mjs.map +1 -0
  188. package/es/color/colors/green.d.mts +6 -0
  189. package/es/color/colors/green.mjs +67 -0
  190. package/es/color/colors/green.mjs.map +1 -0
  191. package/es/color/colors/index.d.mts +22 -0
  192. package/es/color/colors/index.mjs +35 -0
  193. package/es/color/colors/index.mjs.map +1 -0
  194. package/es/color/colors/lime.d.mts +6 -0
  195. package/es/color/colors/lime.mjs +67 -0
  196. package/es/color/colors/lime.mjs.map +1 -0
  197. package/es/color/colors/magenta.d.mts +6 -0
  198. package/es/color/colors/magenta.mjs +67 -0
  199. package/es/color/colors/magenta.mjs.map +1 -0
  200. package/es/color/colors/orange.d.mts +6 -0
  201. package/es/color/colors/orange.mjs +67 -0
  202. package/es/color/colors/orange.mjs.map +1 -0
  203. package/es/color/colors/primary.d.mts +6 -0
  204. package/es/color/colors/primary.mjs +67 -0
  205. package/es/color/colors/primary.mjs.map +1 -0
  206. package/es/color/colors/purple.d.mts +6 -0
  207. package/es/color/colors/purple.mjs +67 -0
  208. package/es/color/colors/purple.mjs.map +1 -0
  209. package/es/color/colors/red.d.mts +6 -0
  210. package/es/color/colors/red.mjs +67 -0
  211. package/es/color/colors/red.mjs.map +1 -0
  212. package/es/color/colors/volcano.d.mts +6 -0
  213. package/es/color/colors/volcano.mjs +67 -0
  214. package/es/color/colors/volcano.mjs.map +1 -0
  215. package/es/color/colors/yellow.d.mts +6 -0
  216. package/es/color/colors/yellow.mjs +67 -0
  217. package/es/color/colors/yellow.mjs.map +1 -0
  218. package/es/color/index.d.mts +23 -0
  219. package/es/color/index.d.ts +1 -0
  220. package/es/color/index.js +1 -0
  221. package/es/color/index.mjs +22 -0
  222. package/es/color/neutrals/index.d.mts +13 -0
  223. package/es/color/neutrals/index.mjs +17 -0
  224. package/es/color/neutrals/index.mjs.map +1 -0
  225. package/es/color/neutrals/mauve.d.mts +6 -0
  226. package/es/color/neutrals/mauve.mjs +67 -0
  227. package/es/color/neutrals/mauve.mjs.map +1 -0
  228. package/es/color/neutrals/olive.d.mts +6 -0
  229. package/es/color/neutrals/olive.mjs +67 -0
  230. package/es/color/neutrals/olive.mjs.map +1 -0
  231. package/es/color/neutrals/sage.d.mts +6 -0
  232. package/es/color/neutrals/sage.mjs +67 -0
  233. package/es/color/neutrals/sage.mjs.map +1 -0
  234. package/es/color/neutrals/sand.d.mts +6 -0
  235. package/es/color/neutrals/sand.mjs +67 -0
  236. package/es/color/neutrals/sand.mjs.map +1 -0
  237. package/es/color/neutrals/slate.d.mts +6 -0
  238. package/es/color/neutrals/slate.mjs +67 -0
  239. package/es/color/neutrals/slate.mjs.map +1 -0
  240. package/es/color/types.d.mts +16 -0
  241. package/es/eslint/index.d.mts +26 -0
  242. package/es/eslint/index.d.ts +1 -0
  243. package/es/eslint/index.js +1 -0
  244. package/es/eslint/index.mjs +11 -0
  245. package/es/eslint/index.mjs.map +1 -0
  246. package/es/hooks/useIsClient.d.mts +8 -0
  247. package/es/hooks/useIsClient.mjs +13 -0
  248. package/es/hooks/useIsClient.mjs.map +1 -0
  249. package/es/i18n/context.d.mts +7 -0
  250. package/es/i18n/context.mjs +16 -0
  251. package/es/i18n/context.mjs.map +1 -0
  252. package/es/i18n/index.d.mts +6 -0
  253. package/es/i18n/index.d.ts +1 -0
  254. package/es/i18n/index.js +1 -0
  255. package/es/i18n/index.mjs +5 -0
  256. package/es/i18n/resources/en/common.d.mts +35 -0
  257. package/es/i18n/resources/en/common.mjs +36 -0
  258. package/es/i18n/resources/en/common.mjs.map +1 -0
  259. package/es/i18n/resources/en/index.d.mts +7 -0
  260. package/es/i18n/resources/en/index.mjs +8 -0
  261. package/es/i18n/resources/en/index.mjs.map +1 -0
  262. package/es/i18n/resources/zhCn/common.d.mts +35 -0
  263. package/es/i18n/resources/zhCn/common.mjs +36 -0
  264. package/es/i18n/resources/zhCn/common.mjs.map +1 -0
  265. package/es/i18n/resources/zhCn/index.d.mts +7 -0
  266. package/es/i18n/resources/zhCn/index.mjs +8 -0
  267. package/es/i18n/resources/zhCn/index.mjs.map +1 -0
  268. package/es/i18n/types.d.mts +22 -0
  269. package/es/i18n/useTranslation.d.mts +12 -0
  270. package/es/i18n/useTranslation.mjs +22 -0
  271. package/es/i18n/useTranslation.mjs.map +1 -0
  272. package/es/index.d.mts +43 -0
  273. package/es/index.mjs +26 -0
  274. package/es/storybook/StoryBook/StoryBook.d.mts +7 -0
  275. package/es/storybook/StoryBook/StoryBook.mjs +52 -0
  276. package/es/storybook/StoryBook/StoryBook.mjs.map +1 -0
  277. package/es/storybook/StoryBook/index.d.mts +3 -0
  278. package/es/storybook/StoryBook/style.mjs +97 -0
  279. package/es/storybook/StoryBook/style.mjs.map +1 -0
  280. package/es/storybook/StoryBook/type.d.mts +13 -0
  281. package/es/storybook/index.d.mts +4 -0
  282. package/es/storybook/index.d.ts +1 -0
  283. package/es/storybook/index.js +1 -0
  284. package/es/storybook/index.mjs +2 -0
  285. package/es/styles/classNames.d.mts +11 -0
  286. package/es/styles/classNames.mjs +12 -0
  287. package/es/styles/classNames.mjs.map +1 -0
  288. package/es/styles/customTheme.d.mts +36 -0
  289. package/es/styles/customTheme.mjs +71 -0
  290. package/es/styles/customTheme.mjs.map +1 -0
  291. package/es/styles/index.d.mts +6 -0
  292. package/es/styles/index.d.ts +1 -0
  293. package/es/styles/index.js +1 -0
  294. package/es/styles/index.mjs +6 -0
  295. package/es/styles/theme/algorithms/darkAlgorithm.mjs +18 -0
  296. package/es/styles/theme/algorithms/darkAlgorithm.mjs.map +1 -0
  297. package/es/styles/theme/algorithms/lightAlgorithm.mjs +18 -0
  298. package/es/styles/theme/algorithms/lightAlgorithm.mjs.map +1 -0
  299. package/es/styles/theme/algorithms/resolveScales.mjs +25 -0
  300. package/es/styles/theme/algorithms/resolveScales.mjs.map +1 -0
  301. package/es/styles/theme/antdTheme.mjs +31 -0
  302. package/es/styles/theme/antdTheme.mjs.map +1 -0
  303. package/es/styles/theme/customStylish.d.mts +7 -0
  304. package/es/styles/theme/customStylish.mjs +158 -0
  305. package/es/styles/theme/customStylish.mjs.map +1 -0
  306. package/es/styles/theme/customStylishStatic.d.mts +6 -0
  307. package/es/styles/theme/customStylishStatic.mjs +156 -0
  308. package/es/styles/theme/customStylishStatic.mjs.map +1 -0
  309. package/es/styles/theme/customToken.d.mts +7 -0
  310. package/es/styles/theme/customToken.mjs +127 -0
  311. package/es/styles/theme/customToken.mjs.map +1 -0
  312. package/es/styles/theme/generateColorPalette.d.mts +16 -0
  313. package/es/styles/theme/generateColorPalette.mjs +170 -0
  314. package/es/styles/theme/generateColorPalette.mjs.map +1 -0
  315. package/es/styles/theme/token/base.mjs +64 -0
  316. package/es/styles/theme/token/base.mjs.map +1 -0
  317. package/es/styles/theme/token/dark.mjs +37 -0
  318. package/es/styles/theme/token/dark.mjs.map +1 -0
  319. package/es/styles/theme/token/light.mjs +37 -0
  320. package/es/styles/theme/token/light.mjs.map +1 -0
  321. package/es/types/customStylish.d.mts +28 -0
  322. package/es/types/customToken.d.mts +34 -0
  323. package/es/types/customToken.mjs +32 -0
  324. package/es/types/customToken.mjs.map +1 -0
  325. package/es/types/index.d.mts +15 -0
  326. package/es/types/index.d.ts +1 -0
  327. package/es/types/index.js +1 -0
  328. package/es/types/index.mjs +2 -0
  329. package/es/utils/composeEventHandlers.d.mts +5 -0
  330. package/es/utils/composeEventHandlers.mjs +10 -0
  331. package/es/utils/composeEventHandlers.mjs.map +1 -0
  332. package/es/utils/copyToClipboard.d.mts +5 -0
  333. package/es/utils/copyToClipboard.mjs +28 -0
  334. package/es/utils/copyToClipboard.mjs.map +1 -0
  335. package/es/utils/dom.d.mts +15 -0
  336. package/es/utils/dom.mjs +16 -0
  337. package/es/utils/dom.mjs.map +1 -0
  338. package/es/utils/genCdnUrl.d.mts +12 -0
  339. package/es/utils/genCdnUrl.mjs +17 -0
  340. package/es/utils/genCdnUrl.mjs.map +1 -0
  341. package/es/utils/safeReadableColor.mjs +6 -0
  342. package/es/utils/safeReadableColor.mjs.map +1 -0
  343. package/package.json +96 -0
@@ -0,0 +1,72 @@
1
+ "use client";
2
+ import { describeCellValue, isGroupTitleRecord } from "../core/cellValue.mjs";
3
+ import { getScenegraph, isTableReleased } from "../vtable/vtableAdapter.mjs";
4
+ import { countGroupRecords } from "../canvas/groupTitle.mjs";
5
+ import { styles } from "./style.mjs";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ //#region src/Table/react/a11y.tsx
8
+ const readA11yCellText = (table, col, row) => {
9
+ try {
10
+ const define = table.getBodyColumnDefine?.(col, row);
11
+ return describeCellValue(table.getCellValue(col, row), {
12
+ duration: define?.superadDuration,
13
+ kind: define?.superadKind,
14
+ options: define?.superadOptions
15
+ }, table.getCellOriginRecord(col, row));
16
+ } catch {
17
+ return "";
18
+ }
19
+ };
20
+ /** Visually hidden role=grid mirror of the visible viewport for screen readers. */
21
+ const TableA11yLayer = ({ table, version }) => {
22
+ const scenegraph = getScenegraph(table);
23
+ if (isTableReleased(table)) return null;
24
+ const headerRows = table.columnHeaderLevelCount ?? 1;
25
+ const rowCount = table.rowCount ?? 0;
26
+ const colCount = table.colCount ?? 0;
27
+ const startRow = Math.max(headerRows, scenegraph.proxy?.rowStart ?? headerRows);
28
+ const fallbackEnd = Math.min(rowCount - 1, headerRows + 24);
29
+ const endRow = Math.min(rowCount - 1, scenegraph.proxy?.rowEnd ?? fallbackEnd);
30
+ const rows = [];
31
+ for (let row = startRow; row <= endRow; row += 1) rows.push(row);
32
+ const headerCells = [];
33
+ for (let col = 0; col < colCount; col += 1) headerCells.push(readA11yCellText(table, col, 0));
34
+ return /* @__PURE__ */ jsxs("div", {
35
+ "aria-colcount": colCount,
36
+ "aria-rowcount": rowCount,
37
+ className: styles.a11yGrid,
38
+ "data-a11y-version": version,
39
+ "data-superad-a11y": "",
40
+ role: "grid",
41
+ tabIndex: 0,
42
+ children: [/* @__PURE__ */ jsx("div", {
43
+ "aria-rowindex": 1,
44
+ role: "row",
45
+ children: headerCells.map((text, index) => /* @__PURE__ */ jsx("div", {
46
+ "aria-colindex": index + 1,
47
+ role: "columnheader",
48
+ children: text
49
+ }, index))
50
+ }), rows.map((row) => {
51
+ const record = table.getCellOriginRecord?.(0, row);
52
+ const groupTitle = isGroupTitleRecord(record);
53
+ return /* @__PURE__ */ jsx("div", {
54
+ "aria-rowindex": row + 1,
55
+ role: "row",
56
+ children: Array.from({ length: colCount }, (_, col) => /* @__PURE__ */ jsx("div", {
57
+ "aria-colindex": col + 1,
58
+ role: "gridcell",
59
+ children: groupTitle && col === 0 ? (() => {
60
+ const name = String(record?.vtableMergeName ?? "");
61
+ const count = countGroupRecords(record);
62
+ return count > 0 ? `${name} 共${count}个` : name;
63
+ })() : readA11yCellText(table, col, row)
64
+ }, col))
65
+ }, row);
66
+ })]
67
+ });
68
+ };
69
+ //#endregion
70
+ export { TableA11yLayer };
71
+
72
+ //# sourceMappingURL=a11y.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a11y.mjs","names":[],"sources":["../../../src/Table/react/a11y.tsx"],"sourcesContent":["'use client';\n\nimport type { ListTable as ListTableInstance } from '@visactor/vtable';\n\nimport { countGroupRecords } from '../canvas/groupTitle';\nimport { describeCellValue, isGroupTitleRecord } from '../core/cellValue';\nimport type { SuperAdCellMarkers } from '../vtable/columns';\nimport { getScenegraph, isTableReleased } from '../vtable/vtableAdapter';\nimport { styles } from './style';\n\nconst readA11yCellText = (table: ListTableInstance, col: number, row: number): string => {\n try {\n const define = (\n table as {\n getBodyColumnDefine?: (col: number, row: number) => SuperAdCellMarkers | undefined;\n }\n ).getBodyColumnDefine?.(col, row);\n // 读的是**画面上显示的那个文本**,不是记录里的原始值:select / tag 的标签映射、user 的\n // 姓名、数组的分隔符都闭包在 painter 里,直接 `String(value)` 会让读屏念出 `done`,\n // 对象值更是一串 `[object Object]`。\n return describeCellValue(\n table.getCellValue(col, row),\n {\n duration: define?.superadDuration,\n kind: define?.superadKind,\n options: define?.superadOptions,\n },\n table.getCellOriginRecord(col, row),\n );\n } catch {\n return '';\n }\n};\n\n/** Visually hidden role=grid mirror of the visible viewport for screen readers. */\nexport const TableA11yLayer = ({\n table,\n version,\n}: {\n table: ListTableInstance;\n version: number;\n}) => {\n const scenegraph = getScenegraph(table);\n // 表被 release() 之后 scenegraph 是 null,下面每一行都会抛。这一层是纯镜像,表没了就没有\n // 可镜像的内容,直接不渲染;新表 onReady 时会 bump version 让它回来。\n if (isTableReleased(table)) return null;\n const headerRows = table.columnHeaderLevelCount ?? 1;\n const rowCount = table.rowCount ?? 0;\n const colCount = table.colCount ?? 0;\n const startRow = Math.max(headerRows, scenegraph.proxy?.rowStart ?? headerRows);\n const fallbackEnd = Math.min(rowCount - 1, headerRows + 24);\n const endRow = Math.min(rowCount - 1, scenegraph.proxy?.rowEnd ?? fallbackEnd);\n const rows: number[] = [];\n\n for (let row = startRow; row <= endRow; row += 1) rows.push(row);\n\n const headerCells: string[] = [];\n for (let col = 0; col < colCount; col += 1) headerCells.push(readA11yCellText(table, col, 0));\n\n return (\n <div\n aria-colcount={colCount}\n aria-rowcount={rowCount}\n className={styles.a11yGrid}\n data-a11y-version={version}\n data-superad-a11y=\"\"\n role=\"grid\"\n tabIndex={0}\n >\n <div aria-rowindex={1} role=\"row\">\n {headerCells.map((text, index) => (\n // 表头是定长、永不重排的列列表,列序号就是它的稳定标识(表头文本可能重复甚至为空)。\n // eslint-disable-next-line @eslint-react/no-array-index-key\n <div aria-colindex={index + 1} key={index} role=\"columnheader\">\n {text}\n </div>\n ))}\n </div>\n {rows.map((row) => {\n const record = table.getCellOriginRecord?.(0, row) as\n { vtableMerge?: boolean; vtableMergeName?: unknown } | undefined;\n // Annotated as boolean so the type guard does not narrow `record` and drop the\n // vtableMergeName this branch still reads.\n const groupTitle: boolean = isGroupTitleRecord(record);\n\n return (\n <div aria-rowindex={row + 1} key={row} role=\"row\">\n {Array.from({ length: colCount }, (_, col) => (\n <div aria-colindex={col + 1} key={col} role=\"gridcell\">\n {groupTitle && col === 0\n ? (() => {\n // 与画布同口径:分组名 + 「共 N 个」。\n const name = String(record?.vtableMergeName ?? '');\n const count = countGroupRecords(record);\n return count > 0 ? `${name} 共${count}个` : name;\n })()\n : readA11yCellText(table, col, row)}\n </div>\n ))}\n </div>\n );\n })}\n </div>\n );\n};\n"],"mappings":";;;;;;;AAUA,MAAM,oBAAoB,OAA0B,KAAa,QAAwB;CACvF,IAAI;EACF,MAAM,SACJ,MAGA,sBAAsB,KAAK,GAAG;EAIhC,OAAO,kBACL,MAAM,aAAa,KAAK,GAAG,GAC3B;GACE,UAAU,QAAQ;GAClB,MAAM,QAAQ;GACd,SAAS,QAAQ;EACnB,GACA,MAAM,oBAAoB,KAAK,GAAG,CACpC;CACF,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,MAAa,kBAAkB,EAC7B,OACA,cAII;CACJ,MAAM,aAAa,cAAc,KAAK;CAGtC,IAAI,gBAAgB,KAAK,GAAG,OAAO;CACnC,MAAM,aAAa,MAAM,0BAA0B;CACnD,MAAM,WAAW,MAAM,YAAY;CACnC,MAAM,WAAW,MAAM,YAAY;CACnC,MAAM,WAAW,KAAK,IAAI,YAAY,WAAW,OAAO,YAAY,UAAU;CAC9E,MAAM,cAAc,KAAK,IAAI,WAAW,GAAG,aAAa,EAAE;CAC1D,MAAM,SAAS,KAAK,IAAI,WAAW,GAAG,WAAW,OAAO,UAAU,WAAW;CAC7E,MAAM,OAAiB,CAAC;CAExB,KAAK,IAAI,MAAM,UAAU,OAAO,QAAQ,OAAO,GAAG,KAAK,KAAK,GAAG;CAE/D,MAAM,cAAwB,CAAC;CAC/B,KAAK,IAAI,MAAM,GAAG,MAAM,UAAU,OAAO,GAAG,YAAY,KAAK,iBAAiB,OAAO,KAAK,CAAC,CAAC;CAE5F,OACE,qBAAC,OAAD;EACE,iBAAe;EACf,iBAAe;EACf,WAAW,OAAO;EAClB,qBAAmB;EACnB,qBAAkB;EAClB,MAAK;EACL,UAAU;EAPZ,UAAA,CASE,oBAAC,OAAD;GAAK,iBAAe;GAAG,MAAK;GACzB,UAAA,YAAY,KAAK,MAAM,UAGtB,oBAAC,OAAD;IAAK,iBAAe,QAAQ;IAAe,MAAK;IAC7C,UAAA;GACE,GAF+B,KAE/B,CACN;EACE,CAAA,GACJ,KAAK,KAAK,QAAQ;GACjB,MAAM,SAAS,MAAM,sBAAsB,GAAG,GAAG;GAIjD,MAAM,aAAsB,mBAAmB,MAAM;GAErD,OACE,oBAAC,OAAD;IAAK,iBAAe,MAAM;IAAa,MAAK;IACzC,UAAA,MAAM,KAAK,EAAE,QAAQ,SAAS,IAAI,GAAG,QACpC,oBAAC,OAAD;KAAK,iBAAe,MAAM;KAAa,MAAK;KACzC,UAAA,cAAc,QAAQ,WACZ;MAEL,MAAM,OAAO,OAAO,QAAQ,mBAAmB,EAAE;MACjD,MAAM,QAAQ,kBAAkB,MAAM;MACtC,OAAO,QAAQ,IAAI,GAAG,KAAK,IAAI,MAAM,KAAK;KAC5C,EAAA,CAAG,IACH,iBAAiB,OAAO,KAAK,GAAG;IACjC,GAT6B,GAS7B,CACN;GACE,GAb6B,GAa7B;EAET,CAAC,CACE;;AAET"}
@@ -0,0 +1,427 @@
1
+ "use client";
2
+ import { antdLocaleFor, readTableI18n, tableText } from "../../core/tableI18n.mjs";
3
+ import { updateCellContent, updateNextFrame } from "../../vtable/vtableAdapter.mjs";
4
+ import { copyToClipboard } from "../../../utils/copyToClipboard.mjs";
5
+ import { styles } from "../style.mjs";
6
+ import { ConfigProvider, Empty, Input } from "antd";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ import { createRoot } from "react-dom/client";
9
+ import { ValidateEnum } from "@visactor/vtable-editors";
10
+ //#region src/Table/react/editors/base.tsx
11
+ /** 按表记录:模块级单例会让一张表关掉 picker 之后,另一张表 160ms 内的点击被误吞。 */
12
+ const pickerClosedAt = /* @__PURE__ */ new WeakMap();
13
+ /**
14
+ * 编辑器里的 Ctrl/Cmd+A 必须**留在输入框里**。
15
+ *
16
+ * 引擎在 `table.getElement()` 上监听 keydown(编辑器 DOM 正是它的子节点),见到这个组合就
17
+ * `preventDefault()` + `deelTableSelectAll()`:输入框里的文本永远选不中,紧接着的输入是
18
+ * **插到光标处**而不是替换 —— 数字列会写出用户没打算写的值,JSON 列直接判非法。引擎自带的
19
+ * `InputEditor` 专门为它加了 `stopPropagation()`,我们的编辑器要对齐。
20
+ *
21
+ * 监听挂在编辑器根节点的**冒泡**阶段:事件先到输入框自己,再被这里截住,不再往上走到表元素。
22
+ */
23
+ const keepSelectAllInEditor = (root) => {
24
+ root.addEventListener("keydown", (event) => {
25
+ if ((event.ctrlKey || event.metaKey) && (event.key === "a" || event.key === "A")) event.stopPropagation();
26
+ });
27
+ };
28
+ const markPickerClosed = (table) => {
29
+ if (table == null || typeof table !== "object") return;
30
+ pickerClosedAt.set(table, Date.now());
31
+ };
32
+ const pickerJustClosed = (table) => {
33
+ if (table == null || typeof table !== "object") return false;
34
+ const at = pickerClosedAt.get(table);
35
+ return at != null && Date.now() - at < 160;
36
+ };
37
+ const POPUP_SELECTOR = ".ant-picker-dropdown, .ant-picker-panel-container, .ant-picker-footer";
38
+ /** 浮层离容器左右边缘留出的缝。贴边看起来像是被裁掉。 */
39
+ const EDGE_GUTTER = 8;
40
+ /**
41
+ * 水平方向把浮层收进容器:右缘放不下先向左回移,回移到头还放不下就收窄。
42
+ *
43
+ * `rect` 是相对**容器**(VTable 传进来的 container 就是 `table.getElement()`)的,所以夹取的
44
+ * 右界取 `container.clientWidth`,与 `left` 同源。保的是「在容器可视宽度内展示全」,不是视口。
45
+ *
46
+ * **调用方必须先把浮层挂进容器**,否则这里读不到 `parentElement`,夹取被整条跳过。
47
+ */
48
+ const clampHorizontal = (element, rect, desiredWidth) => {
49
+ const containerWidth = element.parentElement?.clientWidth;
50
+ const left = rect.left - 1;
51
+ if (containerWidth == null || containerWidth <= 0) return {
52
+ left,
53
+ width: desiredWidth
54
+ };
55
+ const width = Math.min(desiredWidth, Math.max(containerWidth - 16, 1));
56
+ const maxLeft = Math.max(containerWidth - width - EDGE_GUTTER, EDGE_GUTTER);
57
+ return {
58
+ left: Math.min(left, maxLeft),
59
+ width
60
+ };
61
+ };
62
+ const applyOverlayBox = (element, rect) => {
63
+ const { left, width } = clampHorizontal(element, rect, Math.max(rect.width + 2, 240));
64
+ element.style.top = `${rect.top - 1}px`;
65
+ element.style.left = `${left}px`;
66
+ element.style.width = `${width}px`;
67
+ element.style.minHeight = `${Math.max(rect.height, 32)}px`;
68
+ element.style.height = `${Math.max(rect.height, 32)}px`;
69
+ };
70
+ /**
71
+ * `rect` 是**相对表格**的,而 `window.innerHeight` 是视口单位 —— 翻转判断必须加上表格自己在
72
+ * 视口里的位置,否则两者不同源、算式再对也不成立。
73
+ *
74
+ * 曾经这里取的是 `element.parentElement` 的 top。挂载点未必就是 `rect` 的基准元素(VTable 传进来
75
+ * 的 container 与表格元素不是同一个),实测面板超出视口 105px 却完全不翻转 —— 而单看算式
76
+ * (`containerTop + belowTop + estimatedHeight > innerHeight`)是满足翻转条件的。
77
+ */
78
+ /** 面板的最大高度,翻转判断按它估高(见 `placePicker`)。**必须与 `styles.picker` 的
79
+ * `max-height` 一致**:`placePicker` 优先读计算样式,这里是读不到时的兜底。 */
80
+ const PANEL_MAX_HEIGHT = 320;
81
+ const placePicker = (element, rect, minWidth = 240, baseTop = 0) => {
82
+ const { left, width } = clampHorizontal(element, rect, Math.max(rect.width + 2, minWidth));
83
+ const estimatedHeight = Number.parseFloat(getComputedStyle(element).maxHeight) || PANEL_MAX_HEIGHT;
84
+ const belowTop = rect.top + rect.height + 4;
85
+ let top = belowTop;
86
+ if (typeof window !== "undefined" && baseTop + belowTop + estimatedHeight > window.innerHeight) top = Math.max(rect.top - estimatedHeight - 4, 0);
87
+ element.style.top = `${top}px`;
88
+ element.style.left = `${left}px`;
89
+ element.style.minWidth = `${width}px`;
90
+ element.style.width = `${width}px`;
91
+ element.style.height = "auto";
92
+ element.style.maxHeight = `${PANEL_MAX_HEIGHT}px`;
93
+ };
94
+ const detach = (element) => {
95
+ element?.parentElement?.removeChild(element);
96
+ };
97
+ const COPY_ICON = "<svg fill=\"none\" height=\"14\" viewBox=\"0 0 16 16\" width=\"14\"><rect height=\"8\" rx=\"1.5\" stroke=\"currentColor\" width=\"8\" x=\"5\" y=\"5\"/><path d=\"M3 11V4.5A1.5 1.5 0 0 1 4.5 3H11\" stroke=\"currentColor\" stroke-linecap=\"round\"/></svg>";
98
+ const createCopyButton = (getValue, table) => {
99
+ const button = document.createElement("button");
100
+ button.type = "button";
101
+ button.className = styles.copyButton;
102
+ button.setAttribute("aria-label", tableText(table, "copy"));
103
+ button.innerHTML = COPY_ICON;
104
+ button.addEventListener("mousedown", (event) => event.preventDefault());
105
+ button.addEventListener("click", (event) => {
106
+ event.preventDefault();
107
+ event.stopPropagation();
108
+ copyToClipboard(getValue());
109
+ });
110
+ return button;
111
+ };
112
+ /** 提交被拒时打在编辑器宿主上的属性,样式见 `styles.editor`。 */
113
+ const INVALID_ATTR = "data-superad-invalid";
114
+ /**
115
+ * 提交被拒时给出**可见**反馈。静默拒绝和静默改型一样糟:用户按了回车,什么都没发生。
116
+ */
117
+ const markEditorInvalid = (host) => {
118
+ host?.setAttribute(INVALID_ATTR, "");
119
+ };
120
+ /** 重新编辑即清掉非法态,否则用户改对了还是红的。 */
121
+ const watchEditorValidity = (host, input) => {
122
+ input.addEventListener("input", () => host.removeAttribute(INVALID_ATTR));
123
+ };
124
+ /**
125
+ * 点在编辑器**外面**就提交(inline 编辑器用,picker 走自己的 `onDocumentPointerDown`)。
126
+ *
127
+ * 引擎的提交手势只有三类,**全部绑在 canvas 或表元素上**:canvas 上的 pointerdown、
128
+ * `table.getElement()` 上的 Enter/Tab/Escape/方向键。于是「打开编辑器 → 输入 → 点页面别处」
129
+ * 这条路什么都不触发:编辑器既不提交也不关闭,用户以为已经保存。表随后被卸载/更新时走的是
130
+ * `cancelEditCell`(不写值),输入就此**静默丢失**。
131
+ *
132
+ * 返回解绑函数,`onEnd` 里调用。
133
+ */
134
+ const watchOutsideCommit = (host, getTable) => {
135
+ const onPointerDown = (event) => {
136
+ if (host.contains(event.target)) return;
137
+ getTable()?.completeEditCell?.();
138
+ };
139
+ document.addEventListener("pointerdown", onPointerDown, true);
140
+ return () => document.removeEventListener("pointerdown", onPointerDown, true);
141
+ };
142
+ /**
143
+ * 编辑器渲染在**自己的 React root** 里(`createRoot(mount)`,由 VTable 的编辑器协议驱动),
144
+ * 而 React context 不跨 root —— 这里这个 `ConfigProvider` 拿不到外层 `ThemeProvider` 的主题,
145
+ * 不显式传 `theme` 就会退回 antd 默认(主色 `#1677ff` 的蓝)。
146
+ *
147
+ * 实测:日期面板的「此刻 / 确定」按钮与选中日期都是 `rgb(22,119,255)`,而本项目的
148
+ * `--ant-color-primary` 是 `#222222`(近黑)。同一个 root 里所有 antd 组件(Input / Empty /
149
+ * Avatar…)都受这一条影响。主题由 `table.__superadTheme` 送进来,与 canvas 调色板走同一条通道
150
+ * (见 `useTableRenderState`)。
151
+ *
152
+ * 弹层仍然 portal 到 `document.body`:挂进表格容器的话,鼠标扫过弹层会命中表格的
153
+ * pointermove,凭空点亮 hover 悬浮框。
154
+ */
155
+ const wrapEditor = (node, theme, locale) => /* @__PURE__ */ jsx(ConfigProvider, {
156
+ getPopupContainer: () => document.body,
157
+ locale: antdLocaleFor(locale),
158
+ theme,
159
+ children: node
160
+ });
161
+ /**
162
+ * 提交时**自己补写一次**。
163
+ *
164
+ * 引擎在「分组 + 树」的**嵌套行**上写入会失败:`_CachedDataSource` 映射不到记录,
165
+ * `changeCellValues` **静默无操作**(`changeCellValue` 则直接抛
166
+ * `TypeError: Cannot read properties of undefined (reading 'toString')`)。
167
+ * 症状是「编辑完值不见了」,而且整张表和原始 records 里都找不到它 —— 触发条件是
168
+ * **有子节点的行**,以及**嵌在它们下面的行**,平铺表完全不受影响。
169
+ *
170
+ * 这里按 `getCellOriginRecord` 拿到记录对象直接写;调用方再返回 `invalidateExit`,
171
+ * 引擎会 `setValue(旧值)` + `doExit()` —— 它自己那次写入本来就会失败,不影响我们写进去
172
+ * 的值。正常行上是同值重写,无害。
173
+ *
174
+ * `invalidateExit` 会调 `setValue` 回滚,值已由这里写好,所以调用方给个空实现即可。
175
+ *
176
+ * 注意:inline 那三个编辑器**没有继承 `ReactEditor`**(它们是独立类、只 implements 接口),
177
+ * 所以这里是自由函数而不是基类方法 —— 只挂在基类上会漏掉它们。
178
+ */
179
+ const writeBackOnCommit = (editor) => {
180
+ const table = editor.table;
181
+ const cell = table?.editorManager?.editCell;
182
+ if (table == null || cell == null || editor.cancelled === true) return;
183
+ const record = table.getCellOriginRecord?.(cell.col, cell.row);
184
+ const field = table.getBodyColumnDefine?.(cell.col, cell.row)?.field;
185
+ const value = editor.getValue();
186
+ if (record != null && field != null && record[field] !== value) {
187
+ record[field] = value;
188
+ updateCellContent(table, cell.col, cell.row);
189
+ updateNextFrame(table);
190
+ }
191
+ };
192
+ var ReactEditor = class {
193
+ constructor() {
194
+ this.kind = "inline";
195
+ this.cancelled = false;
196
+ this.ended = false;
197
+ this.onDocumentPointerDown = (event) => {
198
+ if (this.mount && this.isEditorElement(event.target) === false) {
199
+ this.beforeFinish();
200
+ this.finish();
201
+ }
202
+ };
203
+ }
204
+ attach(container, rect, className = styles.editor) {
205
+ const mount = document.createElement("div");
206
+ mount.className = className;
207
+ container.appendChild(mount);
208
+ applyOverlayBox(mount, rect);
209
+ keepSelectAllInEditor(mount);
210
+ document.addEventListener("pointerdown", this.onDocumentPointerDown, true);
211
+ this.mount = mount;
212
+ this.root = createRoot(mount);
213
+ }
214
+ /**
215
+ * 提交前的收尾钩子。`ArrayEditor` 用它把**还没回车**的草稿收进值里:document 的捕获
216
+ * pointerdown 先于输入框的 blur 跑,等到 blur 再 `commitDraft` 已经晚了(编辑器已结束)。
217
+ */
218
+ beforeFinish() {}
219
+ validateValue() {
220
+ writeBackOnCommit(this);
221
+ return ValidateEnum.invalidateExit;
222
+ }
223
+ /** `invalidateExit` 会调它回滚,值已由 `writeBackOnCommit` 写好 —— 空实现即可。 */
224
+ setValue() {}
225
+ paint(node) {
226
+ this.root?.render(wrapEditor(node, this.antdTheme(), readTableI18n(this.table).locale));
227
+ }
228
+ /**
229
+ * 当前主题。由 `createTableOption` 写进 `customConfig`(与 `__superadPalette` 同一条通道),
230
+ * 主题变化时随 option 重建一起更新。
231
+ */
232
+ antdTheme() {
233
+ return this.table?.options?.customConfig?.__superadTheme;
234
+ }
235
+ /** 表格元素在视口里的顶边 —— `rect` 是相对表格的,翻转判断要拿它换算。 */
236
+ tableTop() {
237
+ const element = this.table?.getElement?.();
238
+ return element ? element.getBoundingClientRect().top : 0;
239
+ }
240
+ finish() {
241
+ if (this.ended) return;
242
+ this.ended = true;
243
+ this.endEdit?.();
244
+ }
245
+ getValue() {
246
+ return this.cancelled ? this.original : this.current;
247
+ }
248
+ onEnd() {
249
+ if (this.kind === "picker") markPickerClosed(this.table);
250
+ document.removeEventListener("pointerdown", this.onDocumentPointerDown, true);
251
+ this.root?.unmount();
252
+ detach(this.mount);
253
+ this.root = void 0;
254
+ this.mount = void 0;
255
+ this.endEdit = void 0;
256
+ }
257
+ isEditorElement(target) {
258
+ return Boolean(this.mount?.contains(target) || typeof target?.closest === "function" && target.closest(POPUP_SELECTOR));
259
+ }
260
+ adjustPosition(rect) {
261
+ if (this.mount) applyOverlayBox(this.mount, rect);
262
+ }
263
+ };
264
+ /**
265
+ * Shared picker for list-backed editors (options, people, …). The panel, the search
266
+ * box and the keyboard handling are identical across them, so they live here once and
267
+ * each editor supplies only its item shape through the hooks below.
268
+ */
269
+ var ListPickerEditor = class extends ReactEditor {
270
+ constructor(multiple = false) {
271
+ super();
272
+ this.kind = "picker";
273
+ this.activeIndex = 0;
274
+ this.query = "";
275
+ this.selected = [];
276
+ this.initialKeys = "";
277
+ this.multiple = multiple;
278
+ }
279
+ /** 搜索框的占位文案。各 picker 自己说清楚搜的是什么。 */
280
+ searchPlaceholder() {
281
+ return tableText(this.table, "search");
282
+ }
283
+ /**
284
+ * 面板的最小宽度。默认 240(Notion 人员 / 选项浮层实测宽度);
285
+ * 地址表单要更宽,因为它是两列字段。
286
+ */
287
+ panelMinWidth() {
288
+ return 240;
289
+ }
290
+ onStart({ container, endEdit, referencePosition, table, value }) {
291
+ this.table = table;
292
+ this.ended = false;
293
+ this.cancelled = false;
294
+ this.endEdit = endEdit;
295
+ this.original = value;
296
+ this.query = "";
297
+ this.activeIndex = 0;
298
+ this.selected = this.fromValue(value);
299
+ this.initialKeys = this.selectedKeys();
300
+ this.syncCurrent();
301
+ this.attach(container, referencePosition.rect, styles.picker);
302
+ this.place(referencePosition.rect);
303
+ this.paint(this.panel());
304
+ }
305
+ adjustPosition(rect) {
306
+ this.place(rect);
307
+ }
308
+ selectedKeys() {
309
+ return this.selected.map((item) => this.keyOf(item)).sort().join("\0");
310
+ }
311
+ syncCurrent() {
312
+ this.current = this.selectedKeys() === this.initialKeys ? this.original : this.publish(this.selected);
313
+ }
314
+ place(rect) {
315
+ if (this.mount) placePicker(this.mount, rect, this.panelMinWidth(), this.tableTop());
316
+ }
317
+ visible() {
318
+ return this.candidates().filter((item) => this.matches(item, this.query));
319
+ }
320
+ has(item) {
321
+ const key = this.keyOf(item);
322
+ return this.selected.some((entry) => this.keyOf(entry) === key);
323
+ }
324
+ removeSelected(item) {
325
+ const key = this.keyOf(item);
326
+ this.selected = this.selected.filter((entry) => this.keyOf(entry) !== key);
327
+ this.syncCurrent();
328
+ this.activeIndex = Math.max(0, Math.min(this.activeIndex, this.visible().length - 1));
329
+ this.paint(this.panel());
330
+ }
331
+ toggle(item) {
332
+ if (this.multiple === false) {
333
+ this.selected = this.has(item) ? [] : [item];
334
+ this.syncCurrent();
335
+ this.finish();
336
+ return;
337
+ }
338
+ this.selected = this.has(item) ? this.selected.filter((entry) => this.keyOf(entry) !== this.keyOf(item)) : [...this.selected, item];
339
+ this.syncCurrent();
340
+ this.paint(this.panel());
341
+ }
342
+ moveActive(delta, rowCount) {
343
+ this.activeIndex = Math.min(Math.max(this.activeIndex + delta, 0), Math.max(rowCount - 1, 0));
344
+ this.paint(this.panel());
345
+ }
346
+ selectActive(items) {
347
+ const item = items[this.activeIndex];
348
+ if (item) this.toggle(item);
349
+ }
350
+ activate(index) {
351
+ if (this.activeIndex === index) return;
352
+ this.activeIndex = index;
353
+ this.paint(this.panel());
354
+ }
355
+ panel() {
356
+ const items = this.visible();
357
+ const rowCount = items.length;
358
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
359
+ /* @__PURE__ */ jsx(Input, {
360
+ autoFocus: true,
361
+ className: styles.pickerSearch,
362
+ placeholder: this.searchPlaceholder(),
363
+ size: "middle",
364
+ value: this.query,
365
+ variant: "borderless",
366
+ onChange: (event) => {
367
+ this.query = event.target.value;
368
+ this.activeIndex = 0;
369
+ this.paint(this.panel());
370
+ },
371
+ onKeyDown: (event) => {
372
+ if (event.key === "Backspace" && this.query === "" && this.multiple && this.selected.length > 0) {
373
+ event.preventDefault();
374
+ event.stopPropagation();
375
+ const last = this.selected.at(-1);
376
+ if (last) this.removeSelected(last);
377
+ return;
378
+ }
379
+ if (event.key === "ArrowDown") {
380
+ event.preventDefault();
381
+ event.stopPropagation();
382
+ this.moveActive(1, rowCount);
383
+ return;
384
+ }
385
+ if (event.key === "ArrowUp") {
386
+ event.preventDefault();
387
+ event.stopPropagation();
388
+ this.moveActive(-1, rowCount);
389
+ return;
390
+ }
391
+ if (event.key === "Enter") {
392
+ event.preventDefault();
393
+ event.stopPropagation();
394
+ this.selectActive(items);
395
+ return;
396
+ }
397
+ if (event.key === "Escape") {
398
+ event.preventDefault();
399
+ event.stopPropagation();
400
+ this.cancelled = true;
401
+ this.finish();
402
+ }
403
+ }
404
+ }),
405
+ items.length === 0 ? /* @__PURE__ */ jsx(Empty, {
406
+ description: tableText(this.table, "noMatch"),
407
+ image: Empty.PRESENTED_IMAGE_SIMPLE
408
+ }) : null,
409
+ items.map((item, index) => /* @__PURE__ */ jsxs("button", {
410
+ className: styles.pickerItem,
411
+ "data-active": index === this.activeIndex ? "true" : "false",
412
+ type: "button",
413
+ onClick: () => this.toggle(item),
414
+ onMouseEnter: () => this.activate(index),
415
+ children: [this.renderRow(item), /* @__PURE__ */ jsx("span", {
416
+ className: styles.check,
417
+ children: this.has(item) ? "✓" : ""
418
+ })]
419
+ }, this.keyOf(item)))
420
+ ] });
421
+ }
422
+ };
423
+ const isPickerEditor = (editor) => Boolean(editor && typeof editor === "object" && "kind" in editor && editor.kind === "picker");
424
+ //#endregion
425
+ export { INVALID_ATTR, ListPickerEditor, ReactEditor, applyOverlayBox, createCopyButton, detach, isPickerEditor, keepSelectAllInEditor, markEditorInvalid, markPickerClosed, pickerJustClosed, placePicker, watchEditorValidity, watchOutsideCommit, writeBackOnCommit };
426
+
427
+ //# sourceMappingURL=base.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.mjs","names":[],"sources":["../../../../src/Table/react/editors/base.tsx"],"sourcesContent":["'use client';\n\nimport {\n type EditContext,\n type IEditor,\n type RectProps,\n ValidateEnum,\n} from '@visactor/vtable-editors';\nimport { ConfigProvider, Empty, Input, type ThemeConfig } from 'antd';\nimport { type ReactNode } from 'react';\nimport { createRoot, type Root } from 'react-dom/client';\n\nimport { copyToClipboard } from '@/utils/copyToClipboard';\n\nimport { antdLocaleFor, readTableI18n, tableText } from '../../core/tableI18n';\nimport { updateCellContent, updateNextFrame } from '../../vtable/vtableAdapter';\nimport { styles } from '../style';\n\nexport type EditorKind = 'inline' | 'picker';\n\nexport interface TableEditor {\n kind: EditorKind;\n}\n\n/** 按表记录:模块级单例会让一张表关掉 picker 之后,另一张表 160ms 内的点击被误吞。 */\nconst pickerClosedAt = new WeakMap<object, number>();\n\n/**\n * 编辑器里的 Ctrl/Cmd+A 必须**留在输入框里**。\n *\n * 引擎在 `table.getElement()` 上监听 keydown(编辑器 DOM 正是它的子节点),见到这个组合就\n * `preventDefault()` + `deelTableSelectAll()`:输入框里的文本永远选不中,紧接着的输入是\n * **插到光标处**而不是替换 —— 数字列会写出用户没打算写的值,JSON 列直接判非法。引擎自带的\n * `InputEditor` 专门为它加了 `stopPropagation()`,我们的编辑器要对齐。\n *\n * 监听挂在编辑器根节点的**冒泡**阶段:事件先到输入框自己,再被这里截住,不再往上走到表元素。\n */\nexport const keepSelectAllInEditor = (root: HTMLElement): void => {\n root.addEventListener('keydown', (event) => {\n if ((event.ctrlKey || event.metaKey) && (event.key === 'a' || event.key === 'A')) {\n event.stopPropagation();\n }\n });\n};\n\nexport const markPickerClosed = (table?: unknown) => {\n if (table == null || typeof table !== 'object') return;\n pickerClosedAt.set(table as object, Date.now());\n};\n\nexport const pickerJustClosed = (table?: unknown) => {\n if (table == null || typeof table !== 'object') return false;\n const at = pickerClosedAt.get(table as object);\n return at != null && Date.now() - at < 160;\n};\n\nconst POPUP_SELECTOR = '.ant-picker-dropdown, .ant-picker-panel-container, .ant-picker-footer';\n\n/** 浮层离容器左右边缘留出的缝。贴边看起来像是被裁掉。 */\nconst EDGE_GUTTER = 8;\n\n/**\n * 水平方向把浮层收进容器:右缘放不下先向左回移,回移到头还放不下就收窄。\n *\n * `rect` 是相对**容器**(VTable 传进来的 container 就是 `table.getElement()`)的,所以夹取的\n * 右界取 `container.clientWidth`,与 `left` 同源。保的是「在容器可视宽度内展示全」,不是视口。\n *\n * **调用方必须先把浮层挂进容器**,否则这里读不到 `parentElement`,夹取被整条跳过。\n */\nconst clampHorizontal = (\n element: HTMLElement,\n rect: RectProps,\n desiredWidth: number,\n): { left: number; width: number } => {\n const containerWidth = element.parentElement?.clientWidth;\n const left = rect.left - 1;\n if (containerWidth == null || containerWidth <= 0) return { left, width: desiredWidth };\n\n // 容器比浮层还窄(窄容器 / 窄视口里的表)时只能收窄:光回移左缘也顶不住,右侧照样探出去。\n const width = Math.min(desiredWidth, Math.max(containerWidth - EDGE_GUTTER * 2, 1));\n const maxLeft = Math.max(containerWidth - width - EDGE_GUTTER, EDGE_GUTTER);\n\n return { left: Math.min(left, maxLeft), width };\n};\n\nexport const applyOverlayBox = (element: HTMLElement, rect: RectProps) => {\n const { left, width } = clampHorizontal(element, rect, Math.max(rect.width + 2, 240));\n element.style.top = `${rect.top - 1}px`;\n element.style.left = `${left}px`;\n element.style.width = `${width}px`;\n element.style.minHeight = `${Math.max(rect.height, 32)}px`;\n element.style.height = `${Math.max(rect.height, 32)}px`;\n};\n\n/**\n * `rect` 是**相对表格**的,而 `window.innerHeight` 是视口单位 —— 翻转判断必须加上表格自己在\n * 视口里的位置,否则两者不同源、算式再对也不成立。\n *\n * 曾经这里取的是 `element.parentElement` 的 top。挂载点未必就是 `rect` 的基准元素(VTable 传进来\n * 的 container 与表格元素不是同一个),实测面板超出视口 105px 却完全不翻转 —— 而单看算式\n * (`containerTop + belowTop + estimatedHeight > innerHeight`)是满足翻转条件的。\n */\n/** 面板的最大高度,翻转判断按它估高(见 `placePicker`)。**必须与 `styles.picker` 的\n * `max-height` 一致**:`placePicker` 优先读计算样式,这里是读不到时的兜底。 */\nconst PANEL_MAX_HEIGHT = 320;\n\nexport const placePicker = (element: HTMLElement, rect: RectProps, minWidth = 240, baseTop = 0) => {\n const { left, width } = clampHorizontal(element, rect, Math.max(rect.width + 2, minWidth));\n // 用面板的 max-height 估高,**不能用 `scrollHeight`**:`attach` 刚把高度设成单元格高度,\n // 而且 React 的首次渲染还没落地 —— 此刻 scrollHeight 是 36 之类的中间值(不是 0,所以连\n // `|| 240` 的兜底都救不回来),翻转判断因此算出「放得下」,而实际超出视口 105px 却完全不翻。\n const estimatedHeight =\n Number.parseFloat(getComputedStyle(element).maxHeight) || PANEL_MAX_HEIGHT;\n const belowTop = rect.top + rect.height + 4;\n let top = belowTop;\n\n if (typeof window !== 'undefined' && baseTop + belowTop + estimatedHeight > window.innerHeight) {\n top = Math.max(rect.top - estimatedHeight - 4, 0);\n }\n\n element.style.top = `${top}px`;\n element.style.left = `${left}px`;\n // min-width 跟着夹取后的宽度写:照 minWidth 原样写会在窄容器里把面板重新顶宽。\n element.style.minWidth = `${width}px`;\n element.style.width = `${width}px`;\n element.style.height = 'auto';\n element.style.maxHeight = `${PANEL_MAX_HEIGHT}px`;\n};\n\nexport const detach = (element?: HTMLElement) => {\n element?.parentElement?.removeChild(element);\n};\n\nconst COPY_ICON =\n '<svg fill=\"none\" height=\"14\" viewBox=\"0 0 16 16\" width=\"14\"><rect height=\"8\" rx=\"1.5\" stroke=\"currentColor\" width=\"8\" x=\"5\" y=\"5\"/><path d=\"M3 11V4.5A1.5 1.5 0 0 1 4.5 3H11\" stroke=\"currentColor\" stroke-linecap=\"round\"/></svg>';\n\nexport const createCopyButton = (getValue: () => string, table?: unknown) => {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = styles.copyButton;\n button.setAttribute('aria-label', tableText(table, 'copy'));\n button.innerHTML = COPY_ICON;\n button.addEventListener('mousedown', (event) => event.preventDefault());\n button.addEventListener('click', (event) => {\n event.preventDefault();\n event.stopPropagation();\n void copyToClipboard(getValue());\n });\n\n return button;\n};\n\n/** 提交被拒时打在编辑器宿主上的属性,样式见 `styles.editor`。 */\nexport const INVALID_ATTR = 'data-superad-invalid';\n\n/**\n * 提交被拒时给出**可见**反馈。静默拒绝和静默改型一样糟:用户按了回车,什么都没发生。\n */\nexport const markEditorInvalid = (host?: HTMLElement): void => {\n host?.setAttribute(INVALID_ATTR, '');\n};\n\n/** 重新编辑即清掉非法态,否则用户改对了还是红的。 */\nexport const watchEditorValidity = (host: HTMLElement, input: HTMLElement): void => {\n input.addEventListener('input', () => host.removeAttribute(INVALID_ATTR));\n};\n\n/**\n * 点在编辑器**外面**就提交(inline 编辑器用,picker 走自己的 `onDocumentPointerDown`)。\n *\n * 引擎的提交手势只有三类,**全部绑在 canvas 或表元素上**:canvas 上的 pointerdown、\n * `table.getElement()` 上的 Enter/Tab/Escape/方向键。于是「打开编辑器 → 输入 → 点页面别处」\n * 这条路什么都不触发:编辑器既不提交也不关闭,用户以为已经保存。表随后被卸载/更新时走的是\n * `cancelEditCell`(不写值),输入就此**静默丢失**。\n *\n * 返回解绑函数,`onEnd` 里调用。\n */\nexport const watchOutsideCommit = (host: HTMLElement, getTable: () => unknown): (() => void) => {\n const onPointerDown = (event: PointerEvent) => {\n if (host.contains(event.target as Node)) return;\n\n (getTable() as { completeEditCell?: () => void } | undefined)?.completeEditCell?.();\n };\n\n document.addEventListener('pointerdown', onPointerDown, true);\n\n return () => document.removeEventListener('pointerdown', onPointerDown, true);\n};\n\n/**\n * 编辑器渲染在**自己的 React root** 里(`createRoot(mount)`,由 VTable 的编辑器协议驱动),\n * 而 React context 不跨 root —— 这里这个 `ConfigProvider` 拿不到外层 `ThemeProvider` 的主题,\n * 不显式传 `theme` 就会退回 antd 默认(主色 `#1677ff` 的蓝)。\n *\n * 实测:日期面板的「此刻 / 确定」按钮与选中日期都是 `rgb(22,119,255)`,而本项目的\n * `--ant-color-primary` 是 `#222222`(近黑)。同一个 root 里所有 antd 组件(Input / Empty /\n * Avatar…)都受这一条影响。主题由 `table.__superadTheme` 送进来,与 canvas 调色板走同一条通道\n * (见 `useTableRenderState`)。\n *\n * 弹层仍然 portal 到 `document.body`:挂进表格容器的话,鼠标扫过弹层会命中表格的\n * pointermove,凭空点亮 hover 悬浮框。\n */\nconst wrapEditor = (node: ReactNode, theme?: ThemeConfig, locale?: string) => (\n <ConfigProvider\n getPopupContainer={() => document.body}\n locale={antdLocaleFor(locale)}\n theme={theme}\n >\n {node}\n </ConfigProvider>\n);\n\n/**\n * 提交时**自己补写一次**。\n *\n * 引擎在「分组 + 树」的**嵌套行**上写入会失败:`_CachedDataSource` 映射不到记录,\n * `changeCellValues` **静默无操作**(`changeCellValue` 则直接抛\n * `TypeError: Cannot read properties of undefined (reading 'toString')`)。\n * 症状是「编辑完值不见了」,而且整张表和原始 records 里都找不到它 —— 触发条件是\n * **有子节点的行**,以及**嵌在它们下面的行**,平铺表完全不受影响。\n *\n * 这里按 `getCellOriginRecord` 拿到记录对象直接写;调用方再返回 `invalidateExit`,\n * 引擎会 `setValue(旧值)` + `doExit()` —— 它自己那次写入本来就会失败,不影响我们写进去\n * 的值。正常行上是同值重写,无害。\n *\n * `invalidateExit` 会调 `setValue` 回滚,值已由这里写好,所以调用方给个空实现即可。\n *\n * 注意:inline 那三个编辑器**没有继承 `ReactEditor`**(它们是独立类、只 implements 接口),\n * 所以这里是自由函数而不是基类方法 —— 只挂在基类上会漏掉它们。\n */\nexport const writeBackOnCommit = (editor: {\n getValue: () => unknown;\n table?: unknown;\n cancelled?: boolean;\n}): void => {\n const table = editor.table as\n | {\n editorManager?: { editCell?: { col: number; row: number } };\n getBodyColumnDefine?: (col: number, row: number) => { field?: string } | undefined;\n getCellOriginRecord?: (col: number, row: number) => Record<string, unknown> | undefined;\n }\n | undefined;\n const cell = table?.editorManager?.editCell;\n\n if (table == null || cell == null || editor.cancelled === true) return;\n\n const record = table.getCellOriginRecord?.(cell.col, cell.row);\n const field = table.getBodyColumnDefine?.(cell.col, cell.row)?.field;\n const value = editor.getValue();\n\n if (record != null && field != null && record[field] !== value) {\n record[field] = value;\n updateCellContent(table as never, cell.col, cell.row);\n updateNextFrame(table as never);\n }\n};\n\nexport abstract class ReactEditor<V> implements IEditor<V>, TableEditor {\n kind: EditorKind = 'inline';\n protected current!: V;\n protected original!: V;\n protected cancelled = false;\n protected ended = false;\n protected endEdit?: () => void;\n protected mount?: HTMLDivElement;\n /** picker 关闭要靠它标明是哪张表,所以 onStart 里要把它存下来。 */\n protected table?: unknown;\n private root?: Root;\n\n abstract onStart(context: EditContext<V>): void;\n\n protected attach(container: HTMLElement, rect: RectProps, className = styles.editor) {\n const mount = document.createElement('div');\n mount.className = className;\n\n // 先挂进容器再定位:applyOverlayBox / placePicker 的水平夹取要读 parentElement.clientWidth。\n container.appendChild(mount);\n applyOverlayBox(mount, rect);\n // 面板里的搜索框 / antd 日期输入框都在表元素内部,Ctrl+A 同样会被引擎吞掉(见\n // `keepSelectAllInEditor`)。\n keepSelectAllInEditor(mount);\n document.addEventListener('pointerdown', this.onDocumentPointerDown, true);\n\n this.mount = mount;\n this.root = createRoot(mount);\n }\n\n /**\n * 提交前的收尾钩子。`ArrayEditor` 用它把**还没回车**的草稿收进值里:document 的捕获\n * pointerdown 先于输入框的 blur 跑,等到 blur 再 `commitDraft` 已经晚了(编辑器已结束)。\n */\n protected beforeFinish(): void {}\n\n private onDocumentPointerDown = (event: PointerEvent) => {\n if (this.mount && this.isEditorElement(event.target as HTMLElement) === false) {\n // 点面板外 = 提交,不是取消。inline 编辑器在 VTable 那边走的是 completeEdit(提交),\n // picker 若在这里判取消,多选(tag / user 默认 multiple)就完全没有提交手势 —— 每勾\n // 一项只重绘不 finish,用户点走即整份丢弃。取消只剩 Escape。\n this.beforeFinish();\n this.finish();\n }\n };\n\n validateValue(): ValidateEnum {\n writeBackOnCommit(this as never);\n return ValidateEnum.invalidateExit;\n }\n\n /** `invalidateExit` 会调它回滚,值已由 `writeBackOnCommit` 写好 —— 空实现即可。 */\n setValue(): void {}\n\n protected paint(node: ReactNode) {\n this.root?.render(wrapEditor(node, this.antdTheme(), readTableI18n(this.table).locale));\n }\n\n /**\n * 当前主题。由 `createTableOption` 写进 `customConfig`(与 `__superadPalette` 同一条通道),\n * 主题变化时随 option 重建一起更新。\n */\n protected antdTheme(): ThemeConfig | undefined {\n return (\n this.table as\n | {\n __superadTheme?: ThemeConfig;\n options?: { customConfig?: { __superadTheme?: ThemeConfig } };\n }\n | undefined\n )?.options?.customConfig?.__superadTheme;\n }\n\n /** 表格元素在视口里的顶边 —— `rect` 是相对表格的,翻转判断要拿它换算。 */\n protected tableTop(): number {\n const element = (this.table as { getElement?: () => HTMLElement } | undefined)?.getElement?.();\n return element ? element.getBoundingClientRect().top : 0;\n }\n\n protected finish() {\n if (this.ended) return;\n this.ended = true;\n this.endEdit?.();\n }\n\n getValue() {\n return this.cancelled ? this.original : this.current;\n }\n\n onEnd() {\n if (this.kind === 'picker') markPickerClosed(this.table);\n document.removeEventListener('pointerdown', this.onDocumentPointerDown, true);\n this.root?.unmount();\n detach(this.mount);\n this.root = undefined;\n this.mount = undefined;\n this.endEdit = undefined;\n }\n\n isEditorElement(target: HTMLElement) {\n return Boolean(\n this.mount?.contains(target) ||\n (typeof target?.closest === 'function' && target.closest(POPUP_SELECTOR)),\n );\n }\n\n adjustPosition(rect: RectProps) {\n if (this.mount) applyOverlayBox(this.mount, rect);\n }\n}\n\n/**\n * Shared picker for list-backed editors (options, people, …). The panel, the search\n * box and the keyboard handling are identical across them, so they live here once and\n * each editor supplies only its item shape through the hooks below.\n */\nexport abstract class ListPickerEditor<TItem, V> extends ReactEditor<V> {\n kind: EditorKind = 'picker';\n protected activeIndex = 0;\n protected multiple: boolean;\n protected query = '';\n protected selected: TItem[] = [];\n\n constructor(multiple = false) {\n super();\n this.multiple = multiple;\n }\n\n /** Every candidate, before the search query is applied. */\n protected abstract candidates(): TItem[];\n /** The items a cell value names. */\n protected abstract fromValue(value: V): TItem[];\n /** Stable identity, used for React keys and for equality. */\n protected abstract keyOf(item: TItem): string;\n protected abstract matches(item: TItem, query: string): boolean;\n /** 候选列表里一行长什么样。已选项只靠行尾的 ✓ 标出来,面板顶部不再另起一排 chip。 */\n protected abstract renderRow(item: TItem): ReactNode;\n /** The value the cell keeps for the current selection. */\n protected abstract publish(items: TItem[]): V;\n\n /** 搜索框的占位文案。各 picker 自己说清楚搜的是什么。 */\n protected searchPlaceholder(): string {\n return tableText(this.table, 'search');\n }\n\n /**\n * 面板的最小宽度。默认 240(Notion 人员 / 选项浮层实测宽度);\n * 地址表单要更宽,因为它是两列字段。\n */\n protected panelMinWidth(): number {\n return 240;\n }\n\n onStart({ container, endEdit, referencePosition, table, value }: EditContext<V>) {\n this.table = table;\n this.ended = false;\n this.cancelled = false;\n this.endEdit = endEdit;\n this.original = value;\n this.query = '';\n this.activeIndex = 0;\n this.selected = this.fromValue(value);\n this.initialKeys = this.selectedKeys();\n this.syncCurrent();\n this.attach(container, referencePosition.rect, styles.picker);\n this.place(referencePosition.rect);\n this.paint(this.panel());\n }\n\n adjustPosition(rect: RectProps) {\n this.place(rect);\n }\n\n /** 打开那一刻选中项的键快照 —— 用来判断用户到底动没动过。 */\n private initialKeys = '';\n\n private selectedKeys(): string {\n return this.selected\n .map((item) => this.keyOf(item))\n .sort()\n .join('\\u0000');\n }\n\n protected syncCurrent() {\n // **没动过就原样回传使用方的值。** `fromValue` → `publish` 是一次归一化往返:\n // `'a@b.com'` 会变成 `['a@b.com']`、`{ name }` 会变成 `[{ name }]`。而单击即开、\n // 点面板外即提交 —— 用户只是看了一眼再点走,照归一化结果写回去就是**静默改型**:\n // 使用方按 string 读该字段的代码从此拿到数组。inline 编辑器一直守着这条\n // (`raw === this.displayed ? this.original : raw`),picker 这边漏了。\n this.current =\n this.selectedKeys() === this.initialKeys ? this.original : this.publish(this.selected);\n }\n\n private place(rect: RectProps) {\n if (this.mount) placePicker(this.mount, rect, this.panelMinWidth(), this.tableTop());\n }\n\n private visible() {\n return this.candidates().filter((item) => this.matches(item, this.query));\n }\n\n private has(item: TItem) {\n const key = this.keyOf(item);\n return this.selected.some((entry) => this.keyOf(entry) === key);\n }\n\n private removeSelected(item: TItem) {\n const key = this.keyOf(item);\n this.selected = this.selected.filter((entry) => this.keyOf(entry) !== key);\n this.syncCurrent();\n // candidates() 可能回退成已选项(UserEditor 没传 users 时就是这样),删一个 chip 就会\n // 让候选列变短。不夹紧的话 activeIndex 越界,Enter 静默失效且没有任何行是高亮的。\n // 下界也要夹:候选清空时 `length - 1` 是 -1,留个负数会让后面每次 paint 都不高亮任何行。\n this.activeIndex = Math.max(0, Math.min(this.activeIndex, this.visible().length - 1));\n this.paint(this.panel());\n }\n\n private toggle(item: TItem) {\n if (this.multiple === false) {\n // **再点一次已选中的那一项 = 取消**。单选没有别的手势能回到空值:「清空」行与面板顶部的\n // 已选 chip 都已移除,而单元格 chip 上那颗 × 是纯鼠标的(span + onClick,不可聚焦)。\n // 走这一条路,键盘也就通了(方向键 + 回车)。\n this.selected = this.has(item) ? [] : [item];\n this.syncCurrent();\n this.finish();\n return;\n }\n\n this.selected = this.has(item)\n ? this.selected.filter((entry) => this.keyOf(entry) !== this.keyOf(item))\n : [...this.selected, item];\n this.syncCurrent();\n this.paint(this.panel());\n }\n\n private moveActive(delta: number, rowCount: number) {\n this.activeIndex = Math.min(Math.max(this.activeIndex + delta, 0), Math.max(rowCount - 1, 0));\n this.paint(this.panel());\n }\n\n private selectActive(items: TItem[]) {\n const item = items[this.activeIndex];\n if (item) this.toggle(item);\n }\n\n private activate(index: number) {\n if (this.activeIndex === index) return;\n this.activeIndex = index;\n this.paint(this.panel());\n }\n\n private panel() {\n const items = this.visible();\n const rowCount = items.length;\n\n return (\n <>\n <Input\n autoFocus\n className={styles.pickerSearch}\n placeholder={this.searchPlaceholder()}\n size=\"middle\"\n value={this.query}\n variant=\"borderless\"\n onChange={(event) => {\n this.query = event.target.value;\n this.activeIndex = 0;\n this.paint(this.panel());\n }}\n onKeyDown={(event) => {\n if (\n event.key === 'Backspace' &&\n this.query === '' &&\n this.multiple &&\n this.selected.length > 0\n ) {\n event.preventDefault();\n event.stopPropagation();\n const last = this.selected.at(-1);\n if (last) this.removeSelected(last);\n return;\n }\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n event.stopPropagation();\n this.moveActive(1, rowCount);\n return;\n }\n if (event.key === 'ArrowUp') {\n event.preventDefault();\n event.stopPropagation();\n this.moveActive(-1, rowCount);\n return;\n }\n if (event.key === 'Enter') {\n event.preventDefault();\n event.stopPropagation();\n this.selectActive(items);\n return;\n }\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n this.cancelled = true;\n this.finish();\n }\n }}\n />\n {items.length === 0 ? (\n <Empty\n description={tableText(this.table, 'noMatch')}\n image={Empty.PRESENTED_IMAGE_SIMPLE}\n />\n ) : null}\n {items.map((item, index) => (\n <button\n className={styles.pickerItem}\n data-active={index === this.activeIndex ? 'true' : 'false'}\n key={this.keyOf(item)}\n type=\"button\"\n onClick={() => this.toggle(item)}\n onMouseEnter={() => this.activate(index)}\n >\n {this.renderRow(item)}\n <span className={styles.check}>{this.has(item) ? '✓' : ''}</span>\n </button>\n ))}\n </>\n );\n }\n}\n\nexport const isPickerEditor = (editor: unknown): editor is TableEditor =>\n Boolean(\n editor &&\n typeof editor === 'object' &&\n 'kind' in editor &&\n (editor as TableEditor).kind === 'picker',\n );\n"],"mappings":";;;;;;;;;;;AAyBA,MAAM,iCAAiB,IAAI,QAAwB;;;;;;;;;;;AAYnD,MAAa,yBAAyB,SAA4B;CAChE,KAAK,iBAAiB,YAAY,UAAU;EAC1C,KAAK,MAAM,WAAW,MAAM,aAAa,MAAM,QAAQ,OAAO,MAAM,QAAQ,MAC1E,MAAM,gBAAgB;CAE1B,CAAC;AACH;AAEA,MAAa,oBAAoB,UAAoB;CACnD,IAAI,SAAS,QAAQ,OAAO,UAAU,UAAU;CAChD,eAAe,IAAI,OAAiB,KAAK,IAAI,CAAC;AAChD;AAEA,MAAa,oBAAoB,UAAoB;CACnD,IAAI,SAAS,QAAQ,OAAO,UAAU,UAAU,OAAO;CACvD,MAAM,KAAK,eAAe,IAAI,KAAe;CAC7C,OAAO,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK;AACzC;AAEA,MAAM,iBAAiB;;AAGvB,MAAM,cAAc;;;;;;;;;AAUpB,MAAM,mBACJ,SACA,MACA,iBACoC;CACpC,MAAM,iBAAiB,QAAQ,eAAe;CAC9C,MAAM,OAAO,KAAK,OAAO;CACzB,IAAI,kBAAkB,QAAQ,kBAAkB,GAAG,OAAO;EAAE;EAAM,OAAO;CAAa;CAGtF,MAAM,QAAQ,KAAK,IAAI,cAAc,KAAK,IAAI,iBAAiB,IAAiB,CAAC,CAAC;CAClF,MAAM,UAAU,KAAK,IAAI,iBAAiB,QAAQ,aAAa,WAAW;CAE1E,OAAO;EAAE,MAAM,KAAK,IAAI,MAAM,OAAO;EAAG;CAAM;AAChD;AAEA,MAAa,mBAAmB,SAAsB,SAAoB;CACxE,MAAM,EAAE,MAAM,UAAU,gBAAgB,SAAS,MAAM,KAAK,IAAI,KAAK,QAAQ,GAAG,GAAG,CAAC;CACpF,QAAQ,MAAM,MAAM,GAAG,KAAK,MAAM,EAAE;CACpC,QAAQ,MAAM,OAAO,GAAG,KAAK;CAC7B,QAAQ,MAAM,QAAQ,GAAG,MAAM;CAC/B,QAAQ,MAAM,YAAY,GAAG,KAAK,IAAI,KAAK,QAAQ,EAAE,EAAE;CACvD,QAAQ,MAAM,SAAS,GAAG,KAAK,IAAI,KAAK,QAAQ,EAAE,EAAE;AACtD;;;;;;;;;;;AAYA,MAAM,mBAAmB;AAEzB,MAAa,eAAe,SAAsB,MAAiB,WAAW,KAAK,UAAU,MAAM;CACjG,MAAM,EAAE,MAAM,UAAU,gBAAgB,SAAS,MAAM,KAAK,IAAI,KAAK,QAAQ,GAAG,QAAQ,CAAC;CAIzF,MAAM,kBACJ,OAAO,WAAW,iBAAiB,OAAO,CAAC,CAAC,SAAS,KAAK;CAC5D,MAAM,WAAW,KAAK,MAAM,KAAK,SAAS;CAC1C,IAAI,MAAM;CAEV,IAAI,OAAO,WAAW,eAAe,UAAU,WAAW,kBAAkB,OAAO,aACjF,MAAM,KAAK,IAAI,KAAK,MAAM,kBAAkB,GAAG,CAAC;CAGlD,QAAQ,MAAM,MAAM,GAAG,IAAI;CAC3B,QAAQ,MAAM,OAAO,GAAG,KAAK;CAE7B,QAAQ,MAAM,WAAW,GAAG,MAAM;CAClC,QAAQ,MAAM,QAAQ,GAAG,MAAM;CAC/B,QAAQ,MAAM,SAAS;CACvB,QAAQ,MAAM,YAAY,GAAG,iBAAiB;AAChD;AAEA,MAAa,UAAU,YAA0B;CAC/C,SAAS,eAAe,YAAY,OAAO;AAC7C;AAEA,MAAM,YACJ;AAEF,MAAa,oBAAoB,UAAwB,UAAoB;CAC3E,MAAM,SAAS,SAAS,cAAc,QAAQ;CAC9C,OAAO,OAAO;CACd,OAAO,YAAY,OAAO;CAC1B,OAAO,aAAa,cAAc,UAAU,OAAO,MAAM,CAAC;CAC1D,OAAO,YAAY;CACnB,OAAO,iBAAiB,cAAc,UAAU,MAAM,eAAe,CAAC;CACtE,OAAO,iBAAiB,UAAU,UAAU;EAC1C,MAAM,eAAe;EACrB,MAAM,gBAAgB;EACtB,gBAAqB,SAAS,CAAC;CACjC,CAAC;CAED,OAAO;AACT;;AAGA,MAAa,eAAe;;;;AAK5B,MAAa,qBAAqB,SAA6B;CAC7D,MAAM,aAAa,cAAc,EAAE;AACrC;;AAGA,MAAa,uBAAuB,MAAmB,UAA6B;CAClF,MAAM,iBAAiB,eAAe,KAAK,gBAAgB,YAAY,CAAC;AAC1E;;;;;;;;;;;AAYA,MAAa,sBAAsB,MAAmB,aAA0C;CAC9F,MAAM,iBAAiB,UAAwB;EAC7C,IAAI,KAAK,SAAS,MAAM,MAAc,GAAG;EAEzC,SAAU,CAAC,EAAoD,mBAAmB;CACpF;CAEA,SAAS,iBAAiB,eAAe,eAAe,IAAI;CAE5D,aAAa,SAAS,oBAAoB,eAAe,eAAe,IAAI;AAC9E;;;;;;;;;;;;;;AAeA,MAAM,cAAc,MAAiB,OAAqB,WACxD,oBAAC,gBAAD;CACE,yBAAyB,SAAS;CAClC,QAAQ,cAAc,MAAM;CACrB;CAEN,UAAA;AACa,CAAA;;;;;;;;;;;;;;;;;;;AAqBlB,MAAa,qBAAqB,WAItB;CACV,MAAM,QAAQ,OAAO;CAOrB,MAAM,OAAO,OAAO,eAAe;CAEnC,IAAI,SAAS,QAAQ,QAAQ,QAAQ,OAAO,cAAc,MAAM;CAEhE,MAAM,SAAS,MAAM,sBAAsB,KAAK,KAAK,KAAK,GAAG;CAC7D,MAAM,QAAQ,MAAM,sBAAsB,KAAK,KAAK,KAAK,GAAG,CAAC,EAAE;CAC/D,MAAM,QAAQ,OAAO,SAAS;CAE9B,IAAI,UAAU,QAAQ,SAAS,QAAQ,OAAO,WAAW,OAAO;EAC9D,OAAO,SAAS;EAChB,kBAAkB,OAAgB,KAAK,KAAK,KAAK,GAAG;EACpD,gBAAgB,KAAc;CAChC;AACF;AAEA,IAAsB,cAAtB,MAAwE;;EACnD,KAAA,OAAA;EAGG,KAAA,YAAA;EACJ,KAAA,QAAA;EA+Be,KAAA,yBAAA,UAAwB;GACvD,IAAI,KAAK,SAAS,KAAK,gBAAgB,MAAM,MAAqB,MAAM,OAAO;IAI7E,KAAK,aAAa;IAClB,KAAK,OAAO;GACd;EACF;;CA9BA,OAAiB,WAAwB,MAAiB,YAAY,OAAO,QAAQ;EACnF,MAAM,QAAQ,SAAS,cAAc,KAAK;EAC1C,MAAM,YAAY;EAGlB,UAAU,YAAY,KAAK;EAC3B,gBAAgB,OAAO,IAAI;EAG3B,sBAAsB,KAAK;EAC3B,SAAS,iBAAiB,eAAe,KAAK,uBAAuB,IAAI;EAEzE,KAAK,QAAQ;EACb,KAAK,OAAO,WAAW,KAAK;CAC9B;;;;;CAMA,eAA+B,CAAC;CAYhC,gBAA8B;EAC5B,kBAAkB,IAAa;EAC/B,OAAO,aAAa;CACtB;;CAGA,WAAiB,CAAC;CAElB,MAAgB,MAAiB;EAC/B,KAAK,MAAM,OAAO,WAAW,MAAM,KAAK,UAAU,GAAG,cAAc,KAAK,KAAK,CAAC,CAAC,MAAM,CAAC;CACxF;;;;;CAMA,YAA+C;EAC7C,OACE,KAAK,OAMJ,SAAS,cAAc;CAC5B;;CAGA,WAA6B;EAC3B,MAAM,UAAW,KAAK,OAA0D,aAAa;EAC7F,OAAO,UAAU,QAAQ,sBAAsB,CAAC,CAAC,MAAM;CACzD;CAEA,SAAmB;EACjB,IAAI,KAAK,OAAO;EAChB,KAAK,QAAQ;EACb,KAAK,UAAU;CACjB;CAEA,WAAW;EACT,OAAO,KAAK,YAAY,KAAK,WAAW,KAAK;CAC/C;CAEA,QAAQ;EACN,IAAI,KAAK,SAAS,UAAU,iBAAiB,KAAK,KAAK;EACvD,SAAS,oBAAoB,eAAe,KAAK,uBAAuB,IAAI;EAC5E,KAAK,MAAM,QAAQ;EACnB,OAAO,KAAK,KAAK;EACjB,KAAK,OAAO,KAAA;EACZ,KAAK,QAAQ,KAAA;EACb,KAAK,UAAU,KAAA;CACjB;CAEA,gBAAgB,QAAqB;EACnC,OAAO,QACL,KAAK,OAAO,SAAS,MAAM,KAC1B,OAAO,QAAQ,YAAY,cAAc,OAAO,QAAQ,cAAc,CACzE;CACF;CAEA,eAAe,MAAiB;EAC9B,IAAI,KAAK,OAAO,gBAAgB,KAAK,OAAO,IAAI;CAClD;AACF;;;;;;AAOA,IAAsB,mBAAtB,cAAyD,YAAe;CAOtE,YAAY,WAAW,OAAO;EAC5B,MAAM;EAPW,KAAA,OAAA;EACK,KAAA,cAAA;EAEN,KAAA,QAAA;EACY,KAAA,WAAA,CAAC;EAqDT,KAAA,cAAA;EAjDpB,KAAK,WAAW;CAClB;;CAeA,oBAAsC;EACpC,OAAO,UAAU,KAAK,OAAO,QAAQ;CACvC;;;;;CAMA,gBAAkC;EAChC,OAAO;CACT;CAEA,QAAQ,EAAE,WAAW,SAAS,mBAAmB,OAAO,SAAyB;EAC/E,KAAK,QAAQ;EACb,KAAK,QAAQ;EACb,KAAK,YAAY;EACjB,KAAK,UAAU;EACf,KAAK,WAAW;EAChB,KAAK,QAAQ;EACb,KAAK,cAAc;EACnB,KAAK,WAAW,KAAK,UAAU,KAAK;EACpC,KAAK,cAAc,KAAK,aAAa;EACrC,KAAK,YAAY;EACjB,KAAK,OAAO,WAAW,kBAAkB,MAAM,OAAO,MAAM;EAC5D,KAAK,MAAM,kBAAkB,IAAI;EACjC,KAAK,MAAM,KAAK,MAAM,CAAC;CACzB;CAEA,eAAe,MAAiB;EAC9B,KAAK,MAAM,IAAI;CACjB;CAKA,eAA+B;EAC7B,OAAO,KAAK,SACT,KAAK,SAAS,KAAK,MAAM,IAAI,CAAC,CAAC,CAC/B,KAAK,CAAC,CACN,KAAK,IAAQ;CAClB;CAEA,cAAwB;EAMtB,KAAK,UACH,KAAK,aAAa,MAAM,KAAK,cAAc,KAAK,WAAW,KAAK,QAAQ,KAAK,QAAQ;CACzF;CAEA,MAAc,MAAiB;EAC7B,IAAI,KAAK,OAAO,YAAY,KAAK,OAAO,MAAM,KAAK,cAAc,GAAG,KAAK,SAAS,CAAC;CACrF;CAEA,UAAkB;EAChB,OAAO,KAAK,WAAW,CAAC,CAAC,QAAQ,SAAS,KAAK,QAAQ,MAAM,KAAK,KAAK,CAAC;CAC1E;CAEA,IAAY,MAAa;EACvB,MAAM,MAAM,KAAK,MAAM,IAAI;EAC3B,OAAO,KAAK,SAAS,MAAM,UAAU,KAAK,MAAM,KAAK,MAAM,GAAG;CAChE;CAEA,eAAuB,MAAa;EAClC,MAAM,MAAM,KAAK,MAAM,IAAI;EAC3B,KAAK,WAAW,KAAK,SAAS,QAAQ,UAAU,KAAK,MAAM,KAAK,MAAM,GAAG;EACzE,KAAK,YAAY;EAIjB,KAAK,cAAc,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,aAAa,KAAK,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC;EACpF,KAAK,MAAM,KAAK,MAAM,CAAC;CACzB;CAEA,OAAe,MAAa;EAC1B,IAAI,KAAK,aAAa,OAAO;GAI3B,KAAK,WAAW,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI;GAC3C,KAAK,YAAY;GACjB,KAAK,OAAO;GACZ;EACF;EAEA,KAAK,WAAW,KAAK,IAAI,IAAI,IACzB,KAAK,SAAS,QAAQ,UAAU,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,IAAI,CAAC,IACtE,CAAC,GAAG,KAAK,UAAU,IAAI;EAC3B,KAAK,YAAY;EACjB,KAAK,MAAM,KAAK,MAAM,CAAC;CACzB;CAEA,WAAmB,OAAe,UAAkB;EAClD,KAAK,cAAc,KAAK,IAAI,KAAK,IAAI,KAAK,cAAc,OAAO,CAAC,GAAG,KAAK,IAAI,WAAW,GAAG,CAAC,CAAC;EAC5F,KAAK,MAAM,KAAK,MAAM,CAAC;CACzB;CAEA,aAAqB,OAAgB;EACnC,MAAM,OAAO,MAAM,KAAK;EACxB,IAAI,MAAM,KAAK,OAAO,IAAI;CAC5B;CAEA,SAAiB,OAAe;EAC9B,IAAI,KAAK,gBAAgB,OAAO;EAChC,KAAK,cAAc;EACnB,KAAK,MAAM,KAAK,MAAM,CAAC;CACzB;CAEA,QAAgB;EACd,MAAM,QAAQ,KAAK,QAAQ;EAC3B,MAAM,WAAW,MAAM;EAEvB,OACE,qBAAA,UAAA,EAAA,UAAA;GACE,oBAAC,OAAD;IACE,WAAA;IACA,WAAW,OAAO;IAClB,aAAa,KAAK,kBAAkB;IACpC,MAAK;IACL,OAAO,KAAK;IACZ,SAAQ;IACR,WAAW,UAAU;KACnB,KAAK,QAAQ,MAAM,OAAO;KAC1B,KAAK,cAAc;KACnB,KAAK,MAAM,KAAK,MAAM,CAAC;IACzB;IACA,YAAY,UAAU;KACpB,IACE,MAAM,QAAQ,eACd,KAAK,UAAU,MACf,KAAK,YACL,KAAK,SAAS,SAAS,GACvB;MACA,MAAM,eAAe;MACrB,MAAM,gBAAgB;MACtB,MAAM,OAAO,KAAK,SAAS,GAAG,EAAE;MAChC,IAAI,MAAM,KAAK,eAAe,IAAI;MAClC;KACF;KACA,IAAI,MAAM,QAAQ,aAAa;MAC7B,MAAM,eAAe;MACrB,MAAM,gBAAgB;MACtB,KAAK,WAAW,GAAG,QAAQ;MAC3B;KACF;KACA,IAAI,MAAM,QAAQ,WAAW;MAC3B,MAAM,eAAe;MACrB,MAAM,gBAAgB;MACtB,KAAK,WAAW,IAAI,QAAQ;MAC5B;KACF;KACA,IAAI,MAAM,QAAQ,SAAS;MACzB,MAAM,eAAe;MACrB,MAAM,gBAAgB;MACtB,KAAK,aAAa,KAAK;MACvB;KACF;KACA,IAAI,MAAM,QAAQ,UAAU;MAC1B,MAAM,eAAe;MACrB,MAAM,gBAAgB;MACtB,KAAK,YAAY;MACjB,KAAK,OAAO;KACd;IACF;GACD,CAAA;GACA,MAAM,WAAW,IAChB,oBAAC,OAAD;IACE,aAAa,UAAU,KAAK,OAAO,SAAS;IAC5C,OAAO,MAAM;GACd,CAAA,IACC;GACH,MAAM,KAAK,MAAM,UAChB,qBAAC,UAAD;IACE,WAAW,OAAO;IAClB,eAAa,UAAU,KAAK,cAAc,SAAS;IAEnD,MAAK;IACL,eAAe,KAAK,OAAO,IAAI;IAC/B,oBAAoB,KAAK,SAAS,KAAK;IANzC,UAAA,CAQG,KAAK,UAAU,IAAI,GACpB,oBAAC,QAAD;KAAM,WAAW,OAAO;KAAQ,UAAA,KAAK,IAAI,IAAI,IAAI,MAAM;IAAS,CAAA,CAC1D;GAPD,GAAA,KAAK,MAAM,IAAI,CAOd,CACT;EACD,EAAA,CAAA;CAEN;AACF;AAEA,MAAa,kBAAkB,WAC7B,QACE,UACA,OAAO,WAAW,YAClB,UAAU,UACT,OAAuB,SAAS,QACnC"}