@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,408 @@
1
+ "use client";
2
+ import { toneFor } from "../core/tagTone.mjs";
3
+ import { getCanvasPalette } from "./canvasPalette.mjs";
4
+ import { formatDuration, formatLinkLabel, resolveDurationRange, resolveOption, resolveUser, toValueList } from "../core/cellValue.mjs";
5
+ import { editHintGutterFor, fitLabel, measureArrayChipWidth, measureLabelWidth, measureLinkPillWidth, measureOverflowBadgeWidth, measureTagWidth, overflowBadgeWidth, packItems } from "./paintUtils.mjs";
6
+ import { createEmptyCellText } from "./textPainters.mjs";
7
+ import { CustomLayout } from "@visactor/vtable";
8
+ //#region src/Table/canvas/chipPainters.ts
9
+ /**
10
+ * "+N" badge shown when chips do not fit. Shared by every chip layout so the badge
11
+ * keeps one size, colour and baseline.
12
+ */
13
+ const createOverflowText = (count, palette, height, x) => new CustomLayout.Text({
14
+ fontFamily: palette.fontFamily,
15
+ fill: palette.text,
16
+ fontSize: 13,
17
+ pickable: false,
18
+ text: `+${count}`,
19
+ textAlign: "left",
20
+ textBaseline: "middle",
21
+ x,
22
+ y: height / 2
23
+ });
24
+ /**
25
+ * 打包用的可用宽度。绘制是从 `palette.paddingX` 起笔的,不减掉左右内边距的话,最后一个
26
+ * chip 会顶到甚至越过单元格右边界 —— 自定义容器没有 clip,会盖到相邻单元格上。
27
+ * 文本 painter 早就是这么算的(`width - paddingX * 2`)。
28
+ */
29
+ const availableWidth = (width, paddingX) => Math.max(width - paddingX * 2, 48);
30
+ /**
31
+ * 各个 chip 布局共用的排布:打包 → 按可用宽截断 → 从左往右累加 x。
32
+ *
33
+ * 抽成自由函数是因为**画布与 DOM 悬停浮层必须共用同一份计算**:浮层要在画布画出来的 chip
34
+ * 正上方盖一个 ×,两边各算一次就会错位。用到的度量(`measureLabelWidth` / `fitLabel` /
35
+ * `packItems`)都是纯函数、不依赖 canvas 上下文,所以 DOM 这边也能直接调。
36
+ *
37
+ * `Extra` 让调用方带上自己的载荷(标签带 option、人员带 user),排布本身不关心那是什么。
38
+ */
39
+ const layoutChips = (items, widthOf, overflowOf, { paddingX, width }) => {
40
+ const available = availableWidth(width, paddingX);
41
+ const packed = packItems(items.map(({ label }) => widthOf(label)), available, overflowOf);
42
+ const boxes = [];
43
+ let x = paddingX;
44
+ items.slice(0, packed.visible).forEach((item) => {
45
+ const label = fitLabel(item.label, available - overflowBadgeWidth(packed.overflow), widthOf);
46
+ const chipWidth = widthOf(label);
47
+ boxes.push({
48
+ ...item,
49
+ label,
50
+ width: chipWidth,
51
+ x
52
+ });
53
+ x += chipWidth + 4;
54
+ });
55
+ return {
56
+ boxes,
57
+ overflow: packed.overflow,
58
+ overflowX: x
59
+ };
60
+ };
61
+ /**
62
+ * 标签 chip 的排布。供悬停浮层在画布画出来的 chip 上盖 × 用。
63
+ *
64
+ * 值的容器类型不同(数组 / 单个字符串)不影响排布 —— `packed.visible` 之外的那些由 +N 徽标
65
+ * 代表,`index` 始终是**原始数组下标**。
66
+ */
67
+ /**
68
+ * 状态 chip 外标注的时长文案与它占的宽度。**画布与悬停浮层必须用同一份**:打包预算要把它
69
+ * 预留掉,两边各算一遍截断点就会漂移。
70
+ */
71
+ const tagDurationSuffix = (record, duration) => {
72
+ const text = formatDuration(resolveDurationRange(record, duration));
73
+ if (text === "") return {
74
+ suffix: "",
75
+ width: 0
76
+ };
77
+ const suffix = ` ${text}`;
78
+ return {
79
+ suffix,
80
+ width: 4 + measureLabelWidth(suffix, 13)
81
+ };
82
+ };
83
+ const layoutTagChips = ({ options, paddingX, reserve = 0, value, width }) => layoutChips(toValueList(value).map((item) => resolveOption(item, options)).map((option, index) => ({
84
+ index,
85
+ label: option.label,
86
+ option
87
+ })), measureTagWidth, (rest) => measureOverflowBadgeWidth(`+${rest}`), {
88
+ paddingX,
89
+ width: width - reserve
90
+ });
91
+ const createTagLayout = (options, placeholder, duration) => (args) => {
92
+ const palette = getCanvasPalette(args.table);
93
+ const width = args.rect?.width ?? 120;
94
+ const height = args.rect?.height ?? 40;
95
+ const record = args.table?.getCellOriginRecord?.(args.col, args.row);
96
+ const { suffix, width: suffixWidth } = tagDurationSuffix(record, duration);
97
+ const root = new CustomLayout.Group({
98
+ height,
99
+ pickable: false,
100
+ width
101
+ });
102
+ const { boxes, overflow, overflowX } = layoutTagChips({
103
+ options,
104
+ paddingX: palette.paddingX,
105
+ reserve: suffixWidth + editHintGutterFor(args.table, args.col, args.row),
106
+ value: args.value,
107
+ width
108
+ });
109
+ if (boxes.length === 0) {
110
+ if (placeholder) root.add(createEmptyCellText(placeholder, palette, height, args.table));
111
+ return {
112
+ enableCellPadding: false,
113
+ renderDefault: false,
114
+ rootContainer: root
115
+ };
116
+ }
117
+ const chipHeight = 24;
118
+ const chipTop = Math.max(0, (height - chipHeight) / 2);
119
+ boxes.forEach(({ label, option, width: chipWidth, x }) => {
120
+ const tone = toneFor(option.value, option.color, palette.isDarkMode, palette.checkboxBg);
121
+ const textFill = palette.isDarkMode ? tone.color : palette.text;
122
+ root.add(new CustomLayout.Rect({
123
+ cornerRadius: 6,
124
+ fill: tone.bg,
125
+ height: chipHeight,
126
+ lineWidth: tone.border ? 1 : 0,
127
+ pickable: false,
128
+ stroke: tone.border,
129
+ width: chipWidth,
130
+ x,
131
+ y: chipTop
132
+ }));
133
+ root.add(new CustomLayout.Text({
134
+ fontFamily: palette.fontFamily,
135
+ fill: textFill,
136
+ fontSize: 13,
137
+ pickable: false,
138
+ text: label,
139
+ textAlign: "left",
140
+ textBaseline: "middle",
141
+ x: x + 14,
142
+ y: height / 2
143
+ }));
144
+ });
145
+ let after = overflowX;
146
+ if (suffix !== "") {
147
+ const remaining = Math.max(0, width - palette.paddingX - (overflowX + 4));
148
+ const drawnSuffix = remaining >= suffixWidth ? suffix : fitLabel(suffix, remaining, (label) => measureLabelWidth(label, 13));
149
+ root.add(new CustomLayout.Text({
150
+ fontFamily: palette.fontFamily,
151
+ fill: palette.textMuted,
152
+ fontSize: 13,
153
+ pickable: false,
154
+ text: drawnSuffix,
155
+ textAlign: "left",
156
+ textBaseline: "middle",
157
+ x: overflowX + 4,
158
+ y: height / 2
159
+ }));
160
+ after += suffixWidth;
161
+ }
162
+ if (overflow > 0) root.add(createOverflowText(overflow, palette, height, after));
163
+ return {
164
+ enableCellPadding: false,
165
+ renderDefault: false,
166
+ rootContainer: root
167
+ };
168
+ };
169
+ /**
170
+ * 人员 chip 的排布。
171
+ *
172
+ * 抽成自由函数是因为**画布与 DOM 悬停浮层必须共用同一份计算**:浮层要在画布画出来的 chip
173
+ * 正上方盖一个 ×,两边各算一次就会错位。用到的度量(`measureLabelWidth` / `fitLabel` /
174
+ * `packItems`)都是纯函数、不依赖 canvas 上下文,所以 DOM 这边也能直接调。
175
+ */
176
+ const layoutUserChips = ({ paddingX, users, width }) => layoutChips(users.map(({ index, user }) => ({
177
+ index,
178
+ label: user.name,
179
+ user
180
+ })), (label) => 18 + measureLabelWidth(label, 13) + 22, () => 24, {
181
+ paddingX,
182
+ width
183
+ });
184
+ /** 值的每一项解析成人员并保留**原始下标**;没有可用名字的(`{}` / `{ id }` / `{ name: 42 }`)
185
+ * 一律丢掉 —— `isEmptyCellValue({})` 判它是空值,两条路径口径必须一致。 */
186
+ const resolveUserEntries = (value) => toValueList(value).map(resolveUser).map((user, index) => ({
187
+ index,
188
+ user
189
+ })).filter(({ user }) => user.name !== "");
190
+ const createUserLayout = (placeholder) => (args) => {
191
+ const palette = getCanvasPalette(args.table);
192
+ const width = args.rect?.width ?? 140;
193
+ const height = args.rect?.height ?? 40;
194
+ const entries = resolveUserEntries(args.value);
195
+ const root = new CustomLayout.Group({
196
+ height,
197
+ pickable: false,
198
+ width
199
+ });
200
+ if (entries.length === 0) {
201
+ if (placeholder) root.add(createEmptyCellText(placeholder, palette, height, args.table));
202
+ return {
203
+ enableCellPadding: false,
204
+ renderDefault: false,
205
+ rootContainer: root
206
+ };
207
+ }
208
+ const chipTop = Math.max(0, (height - 24) / 2);
209
+ const { boxes, overflow, overflowX } = layoutUserChips({
210
+ paddingX: palette.paddingX,
211
+ users: entries,
212
+ width: width - editHintGutterFor(args.table, args.col, args.row)
213
+ });
214
+ boxes.forEach(({ label, user, width: chipWidth, x }) => {
215
+ const tone = toneFor(user.name, void 0, palette.isDarkMode, palette.checkboxBg);
216
+ const initials = user.name.trim().slice(0, 1) || "?";
217
+ root.add(new CustomLayout.Rect({
218
+ cornerRadius: 4,
219
+ fill: palette.pillBg,
220
+ height: 24,
221
+ pickable: false,
222
+ width: chipWidth,
223
+ x,
224
+ y: chipTop
225
+ }));
226
+ if (user.avatar) root.add(new CustomLayout.Image({
227
+ cornerRadius: 999,
228
+ height: 14,
229
+ pickable: false,
230
+ shape: "square",
231
+ src: user.avatar,
232
+ width: 14,
233
+ x: x + 4,
234
+ y: chipTop + 5
235
+ }));
236
+ else {
237
+ root.add(new CustomLayout.Rect({
238
+ cornerRadius: 999,
239
+ fill: tone.bg,
240
+ height: 14,
241
+ pickable: false,
242
+ width: 14,
243
+ x: x + 4,
244
+ y: chipTop + 5
245
+ }));
246
+ root.add(new CustomLayout.Text({
247
+ fontFamily: palette.fontFamily,
248
+ fill: tone.color,
249
+ fontSize: 9,
250
+ pickable: false,
251
+ text: initials,
252
+ textAlign: "center",
253
+ textBaseline: "middle",
254
+ x: x + 4 + 7,
255
+ y: chipTop + 5 + 7
256
+ }));
257
+ }
258
+ root.add(new CustomLayout.Text({
259
+ fontFamily: palette.fontFamily,
260
+ fill: palette.text,
261
+ fontSize: 13,
262
+ pickable: false,
263
+ text: label,
264
+ textAlign: "left",
265
+ textBaseline: "middle",
266
+ x: x + 4 + 14 + 4,
267
+ y: height / 2
268
+ }));
269
+ });
270
+ if (overflow > 0) root.add(createOverflowText(overflow, palette, height, overflowX));
271
+ return {
272
+ enableCellPadding: false,
273
+ renderDefault: false,
274
+ rootContainer: root
275
+ };
276
+ };
277
+ /** 值 → 文本项,保留**原始下标**;空串不画(与 painter 原先的 `filter(Boolean)` 同口径)。
278
+ * 下标必须是原始的:记录是 `['', '核心']` 时画面上只有一枚 chip,点它的 × 要删掉「核心」。 */
279
+ const resolveTextEntries = (value) => toValueList(value).map((item) => String(item)).map((label, index) => ({
280
+ index,
281
+ label
282
+ })).filter(({ label }) => label !== "");
283
+ /** 链接 pill 的排布。文案与画布同源:`kind === 'link'` 时要一起剥掉 scheme。 */
284
+ const layoutLinkChips = ({ kind, paddingX, value, width }) => layoutChips(resolveTextEntries(value).map(({ index, label }) => ({
285
+ index,
286
+ label: kind === "link" ? formatLinkLabel(label) : label
287
+ })), measureLinkPillWidth, () => 24, {
288
+ paddingX,
289
+ width
290
+ });
291
+ const createLinkLayout = (kind, placeholder) => (args) => {
292
+ const palette = getCanvasPalette(args.table);
293
+ const width = args.rect?.width ?? 140;
294
+ const height = args.rect?.height ?? 40;
295
+ const root = new CustomLayout.Group({
296
+ height,
297
+ pickable: false,
298
+ width
299
+ });
300
+ const { boxes, overflow, overflowX } = layoutLinkChips({
301
+ kind,
302
+ paddingX: palette.paddingX,
303
+ value: args.value,
304
+ width: width - editHintGutterFor(args.table, args.col, args.row)
305
+ });
306
+ if (boxes.length === 0) {
307
+ if (placeholder) root.add(createEmptyCellText(placeholder, palette, height, args.table));
308
+ return {
309
+ enableCellPadding: false,
310
+ renderDefault: false,
311
+ rootContainer: root
312
+ };
313
+ }
314
+ const pillHeight = 24;
315
+ const pillTop = Math.max(0, (height - pillHeight) / 2);
316
+ boxes.forEach(({ label, width: pillWidth, x }) => {
317
+ root.add(new CustomLayout.Rect({
318
+ cornerRadius: 999,
319
+ fill: palette.pillBg,
320
+ height: pillHeight,
321
+ lineWidth: 1,
322
+ pickable: false,
323
+ stroke: palette.overlayBorder,
324
+ width: pillWidth,
325
+ x,
326
+ y: pillTop
327
+ }));
328
+ root.add(new CustomLayout.Text({
329
+ fontFamily: palette.fontFamily,
330
+ fill: palette.text,
331
+ fontSize: 13,
332
+ pickable: false,
333
+ text: label,
334
+ textAlign: "left",
335
+ textBaseline: "middle",
336
+ x: x + 14,
337
+ y: height / 2
338
+ }));
339
+ });
340
+ if (overflow > 0) root.add(createOverflowText(overflow, palette, height, overflowX));
341
+ return {
342
+ enableCellPadding: false,
343
+ renderDefault: false,
344
+ rootContainer: root
345
+ };
346
+ };
347
+ /** 数组 chip 的排布。内边距与标签 / 链接同为 CHIP_PAD —— 右内边距要整颗装下悬停的 ×。 */
348
+ const layoutArrayChips = ({ paddingX, value, width }) => layoutChips(resolveTextEntries(value), measureArrayChipWidth, () => 24, {
349
+ paddingX,
350
+ width
351
+ });
352
+ const createArrayLayout = (placeholder) => (args) => {
353
+ const palette = getCanvasPalette(args.table);
354
+ const width = args.rect?.width ?? 140;
355
+ const height = args.rect?.height ?? 40;
356
+ const root = new CustomLayout.Group({
357
+ height,
358
+ pickable: false,
359
+ width
360
+ });
361
+ const { boxes, overflow, overflowX } = layoutArrayChips({
362
+ paddingX: palette.paddingX,
363
+ value: args.value,
364
+ width: width - editHintGutterFor(args.table, args.col, args.row)
365
+ });
366
+ if (boxes.length === 0) {
367
+ if (placeholder) root.add(createEmptyCellText(placeholder, palette, height, args.table));
368
+ return {
369
+ enableCellPadding: false,
370
+ renderDefault: false,
371
+ rootContainer: root
372
+ };
373
+ }
374
+ const chipHeight = 24;
375
+ const chipTop = Math.max(0, (height - chipHeight) / 2);
376
+ boxes.forEach(({ label, width: chipWidth, x }) => {
377
+ root.add(new CustomLayout.Rect({
378
+ cornerRadius: 4,
379
+ fill: palette.pillBg,
380
+ height: chipHeight,
381
+ pickable: false,
382
+ width: chipWidth,
383
+ x,
384
+ y: chipTop
385
+ }));
386
+ root.add(new CustomLayout.Text({
387
+ fontFamily: palette.fontFamily,
388
+ fill: palette.text,
389
+ fontSize: 13,
390
+ pickable: false,
391
+ text: label,
392
+ textAlign: "left",
393
+ textBaseline: "middle",
394
+ x: x + 14,
395
+ y: height / 2
396
+ }));
397
+ });
398
+ if (overflow > 0) root.add(createOverflowText(overflow, palette, height, overflowX));
399
+ return {
400
+ enableCellPadding: false,
401
+ renderDefault: false,
402
+ rootContainer: root
403
+ };
404
+ };
405
+ //#endregion
406
+ export { createArrayLayout, createLinkLayout, createTagLayout, createUserLayout, layoutArrayChips, layoutLinkChips, layoutTagChips, layoutUserChips, resolveUserEntries, tagDurationSuffix };
407
+
408
+ //# sourceMappingURL=chipPainters.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chipPainters.mjs","names":[],"sources":["../../../src/Table/canvas/chipPainters.ts"],"sourcesContent":["'use client';\n\nimport { CustomLayout } from '@visactor/vtable';\nimport type { ICustomLayoutFuc } from '@visactor/vtable/es/ts-types/customLayout';\n\nimport {\n formatDuration,\n formatLinkLabel,\n type LinkKind,\n resolveDurationRange,\n resolveOption,\n resolveUser,\n toValueList,\n} from '../core/cellValue';\nimport { toneFor } from '../core/tagTone';\nimport type { TableSelectOption, TableUser } from '../type';\nimport { type CanvasPalette, getCanvasPalette } from './canvasPalette';\nimport {\n ARRAY_PAD_LEFT,\n editHintGutterFor,\n fitLabel,\n LINK_PAD_LEFT,\n measureArrayChipWidth,\n measureLabelWidth,\n measureLinkPillWidth,\n measureOverflowBadgeWidth,\n measureTagWidth,\n overflowBadgeWidth,\n packItems,\n TAG_FONT_SIZE,\n TAG_GAP,\n TAG_PAD_LEFT,\n} from './paintUtils';\nimport { createEmptyCellText } from './textPainters';\n\nexport { EDIT_HINT_GUTTER, editHintGutterFor } from './paintUtils';\n\n/**\n * \"+N\" badge shown when chips do not fit. Shared by every chip layout so the badge\n * keeps one size, colour and baseline.\n */\nconst createOverflowText = (count: number, palette: CanvasPalette, height: number, x: number) =>\n new CustomLayout.Text({\n fontFamily: palette.fontFamily,\n fill: palette.text,\n fontSize: 13,\n pickable: false,\n text: `+${count}`,\n textAlign: 'left',\n textBaseline: 'middle',\n x,\n y: height / 2,\n } as never);\n\n/**\n * 打包用的可用宽度。绘制是从 `palette.paddingX` 起笔的,不减掉左右内边距的话,最后一个\n * chip 会顶到甚至越过单元格右边界 —— 自定义容器没有 clip,会盖到相邻单元格上。\n * 文本 painter 早就是这么算的(`width - paddingX * 2`)。\n */\nconst availableWidth = (width: number, paddingX: number): number =>\n Math.max(width - paddingX * 2, 48);\n\n/** 画布与 DOM 悬停浮层共用的 chip 排布结果。 */\nexport interface ChipBox {\n /** 在**记录原始数组**里的下标 —— 移除时按它 splice,不能用过滤后的位置。 */\n index: number;\n /** 画进 chip 的文字(可能已被截断)。 */\n label: string;\n width: number;\n /** 相对单元格左边界的起点,与 painter 的落笔位置同源。 */\n x: number;\n}\n\n/**\n * 各个 chip 布局共用的排布:打包 → 按可用宽截断 → 从左往右累加 x。\n *\n * 抽成自由函数是因为**画布与 DOM 悬停浮层必须共用同一份计算**:浮层要在画布画出来的 chip\n * 正上方盖一个 ×,两边各算一次就会错位。用到的度量(`measureLabelWidth` / `fitLabel` /\n * `packItems`)都是纯函数、不依赖 canvas 上下文,所以 DOM 这边也能直接调。\n *\n * `Extra` 让调用方带上自己的载荷(标签带 option、人员带 user),排布本身不关心那是什么。\n */\nconst layoutChips = <Extra>(\n items: ({ index: number; label: string } & Extra)[],\n widthOf: (label: string) => number,\n overflowOf: (rest: number) => number,\n { paddingX, width }: { paddingX: number; width: number },\n): { boxes: (ChipBox & Extra)[]; overflow: number; overflowX: number } => {\n const available = availableWidth(width, paddingX);\n const packed = packItems(\n items.map(({ label }) => widthOf(label)),\n available,\n overflowOf,\n );\n const boxes: (ChipBox & Extra)[] = [];\n let x = paddingX;\n\n items.slice(0, packed.visible).forEach((item) => {\n // 截断必须发生在**算宽度之前**:只截文本节点的话矩形仍按完整标签算宽,照样越界。\n const label = fitLabel(item.label, available - overflowBadgeWidth(packed.overflow), widthOf);\n const chipWidth = widthOf(label);\n\n boxes.push({ ...item, label, width: chipWidth, x });\n x += chipWidth + TAG_GAP;\n });\n\n // `x` 已经停在下一枚 chip 该落笔的位置,正好是 +N 徽标的起点。\n return { boxes, overflow: packed.overflow, overflowX: x };\n};\n\n/**\n * 标签 chip 的排布。供悬停浮层在画布画出来的 chip 上盖 × 用。\n *\n * 值的容器类型不同(数组 / 单个字符串)不影响排布 —— `packed.visible` 之外的那些由 +N 徽标\n * 代表,`index` 始终是**原始数组下标**。\n */\n/**\n * 状态 chip 外标注的时长文案与它占的宽度。**画布与悬停浮层必须用同一份**:打包预算要把它\n * 预留掉,两边各算一遍截断点就会漂移。\n */\nexport const tagDurationSuffix = (\n record: unknown,\n duration?: string | { from: string; to: string },\n): { suffix: string; width: number } => {\n const text = formatDuration(resolveDurationRange(record, duration));\n if (text === '') return { suffix: '', width: 0 };\n\n const suffix = ` ${text}`;\n return { suffix, width: TAG_GAP + measureLabelWidth(suffix, TAG_FONT_SIZE) };\n};\n\nexport const layoutTagChips = ({\n options,\n paddingX,\n reserve = 0,\n value,\n width,\n}: {\n options: TableSelectOption[] | undefined;\n paddingX: number;\n /** chip 之后还要放独立文字(「5天」这类时长)时预留的宽度(含间距)。 */\n reserve?: number;\n value: unknown;\n width: number;\n}): {\n boxes: (ChipBox & { option: TableSelectOption })[];\n overflow: number;\n overflowX: number;\n} =>\n layoutChips(\n toValueList(value)\n .map((item) => resolveOption(item, options))\n .map((option, index) => ({ index, label: option.label, option })),\n measureTagWidth,\n (rest) => measureOverflowBadgeWidth(`+${rest}`),\n { paddingX, width: width - reserve },\n );\n\nexport const createTagLayout =\n (\n options: TableSelectOption[] | undefined,\n placeholder?: string,\n duration?: string | { from: string; to: string },\n ): ICustomLayoutFuc =>\n (args) => {\n const palette = getCanvasPalette(args.table);\n const width = args.rect?.width ?? 120;\n const height = args.rect?.height ?? 40;\n // 状态标题后跟的时长(「5天 / 1个月」):`duration` 指名记录里存日期范围的字段,时长现算,\n // 作为 chip **外面**的独立文字画出来 —— 参考图里它就是 chip 后面的标注,不是 chip 的一部分。\n const record = (args.table as any)?.getCellOriginRecord?.(args.col, args.row);\n const { suffix, width: suffixWidth } = tagDurationSuffix(record, duration);\n const root = new CustomLayout.Group({ height, pickable: false, width });\n const { boxes, overflow, overflowX } = layoutTagChips({\n options,\n paddingX: palette.paddingX,\n reserve: suffixWidth + editHintGutterFor(args.table as never, args.col, args.row),\n value: args.value,\n width,\n });\n\n if (boxes.length === 0) {\n if (placeholder) {\n root.add(createEmptyCellText(placeholder, palette, height, args.table));\n }\n\n return { enableCellPadding: false, renderDefault: false, rootContainer: root };\n }\n\n const chipHeight = 24;\n const chipTop = Math.max(0, (height - chipHeight) / 2);\n\n boxes.forEach(({ label, option, width: chipWidth, x }) => {\n const tone = toneFor(option.value, option.color, palette.isDarkMode, palette.checkboxBg);\n // 文字是主题文字色而不是 tone.color:浅底 + 统一近黑字是「状态」参考图的样式,也正好是\n // antd Tag 默认的「浅底 + colorText 字」。tone.color 仍是色相源(hex 吸附、暗色提亮都靠\n // 它),暗色下 chip 底是彩色半透明,白字对比度没有保证,所以暗色继续用 tone 的提亮色。\n const textFill = palette.isDarkMode ? tone.color : palette.text;\n\n root.add(\n new CustomLayout.Rect({\n cornerRadius: 6,\n fill: tone.bg,\n height: chipHeight,\n lineWidth: tone.border ? 1 : 0,\n pickable: false,\n stroke: tone.border,\n width: chipWidth,\n x,\n y: chipTop,\n } as never),\n );\n root.add(\n new CustomLayout.Text({\n fontFamily: palette.fontFamily,\n fill: textFill,\n fontSize: TAG_FONT_SIZE,\n pickable: false,\n text: label,\n textAlign: 'left',\n textBaseline: 'middle',\n x: x + TAG_PAD_LEFT,\n y: height / 2,\n } as never),\n );\n });\n\n // 时长文字画在**最后一枚 chip 的外面**(参考图里「4天」是 chip 后面的标注),\n // 用弱色 —— 它是补充信息,不能与 chip 标题同权重。已通过 `reserve` 参与打包预算。\n // 极窄列 + 长时长时剩余宽度装不下完整标注,按剩余宽度截断 —— 不能顶出单元格。\n let after = overflowX;\n if (suffix !== '') {\n const remaining = Math.max(0, width - palette.paddingX - (overflowX + TAG_GAP));\n const drawnSuffix =\n remaining >= suffixWidth\n ? suffix\n : fitLabel(suffix, remaining, (label) => measureLabelWidth(label, TAG_FONT_SIZE));\n root.add(\n new CustomLayout.Text({\n fontFamily: palette.fontFamily,\n fill: palette.textMuted,\n fontSize: TAG_FONT_SIZE,\n pickable: false,\n text: drawnSuffix,\n textAlign: 'left',\n textBaseline: 'middle',\n x: overflowX + TAG_GAP,\n y: height / 2,\n } as never),\n );\n after += suffixWidth;\n }\n\n if (overflow > 0) {\n root.add(createOverflowText(overflow, palette, height, after));\n }\n\n return { enableCellPadding: false, renderDefault: false, rootContainer: root };\n };\n\n/** 人员 chip 的固定尺寸,画布与 DOM 悬停浮层共用。 */\nexport const USER_CHIP_HEIGHT = 24;\nexport const USER_CHIP_AVATAR = 14;\n/**\n * chip 右侧留白。**不是纯装饰**:悬停时要在这一格里盖一个 14px 的移除按钮。\n *\n * 取值有下界:文字从 `x + 22` 起笔,按钮是「贴右 + margin-inline-end 2px」,所以留白必须\n * ≥ 4 + 2 + 14 = 20 才不压到字上。留 8px 时按钮整个盖在最后一个字上 —— 近黑的 × 叠在近黑的\n * 笔画上等于没画,实测截图里完全看不见。22 是 20 再加 2px 余量。\n */\nexport const USER_CHIP_TRAILING = 22;\n\nexport interface UserChipBox extends ChipBox {\n user: TableUser;\n}\n\n/**\n * 人员 chip 的排布。\n *\n * 抽成自由函数是因为**画布与 DOM 悬停浮层必须共用同一份计算**:浮层要在画布画出来的 chip\n * 正上方盖一个 ×,两边各算一次就会错位。用到的度量(`measureLabelWidth` / `fitLabel` /\n * `packItems`)都是纯函数、不依赖 canvas 上下文,所以 DOM 这边也能直接调。\n */\nexport const layoutUserChips = ({\n paddingX,\n users,\n width,\n}: {\n paddingX: number;\n users: { index: number; user: TableUser }[];\n width: number;\n}): { boxes: UserChipBox[]; overflow: number; overflowX: number } =>\n layoutChips(\n users.map(({ index, user }) => ({ index, label: user.name, user })),\n // Packing and drawing must share one expression: the avatar's width is what makes them\n // differ, and a mismatch would shift the +N overflow count.\n (label) => USER_CHIP_AVATAR + 4 + measureLabelWidth(label, 13) + USER_CHIP_TRAILING,\n () => 24,\n { paddingX, width },\n );\n\n/** 值的每一项解析成人员并保留**原始下标**;没有可用名字的(`{}` / `{ id }` / `{ name: 42 }`)\n * 一律丢掉 —— `isEmptyCellValue({})` 判它是空值,两条路径口径必须一致。 */\nexport const resolveUserEntries = (value: unknown): { index: number; user: TableUser }[] =>\n toValueList(value)\n .map(resolveUser)\n .map((user, index) => ({ index, user }))\n .filter(({ user }) => user.name !== '');\n\nexport const createUserLayout =\n (placeholder?: string): ICustomLayoutFuc =>\n (args) => {\n const palette = getCanvasPalette(args.table);\n const width = args.rect?.width ?? 140;\n const height = args.rect?.height ?? 40;\n const entries = resolveUserEntries(args.value);\n const root = new CustomLayout.Group({ height, pickable: false, width });\n\n if (entries.length === 0) {\n if (placeholder) {\n root.add(createEmptyCellText(placeholder, palette, height, args.table));\n }\n\n return { enableCellPadding: false, renderDefault: false, rootContainer: root };\n }\n\n const chipTop = Math.max(0, (height - USER_CHIP_HEIGHT) / 2);\n const { boxes, overflow, overflowX } = layoutUserChips({\n paddingX: palette.paddingX,\n users: entries,\n width: width - editHintGutterFor(args.table as never, args.col, args.row),\n });\n\n boxes.forEach(({ label, user, width: chipWidth, x }) => {\n const tone = toneFor(user.name, undefined, palette.isDarkMode, palette.checkboxBg);\n const initials = user.name.trim().slice(0, 1) || '?';\n\n root.add(\n new CustomLayout.Rect({\n cornerRadius: 4,\n fill: palette.pillBg,\n height: USER_CHIP_HEIGHT,\n pickable: false,\n width: chipWidth,\n x,\n y: chipTop,\n } as never),\n );\n if (user.avatar) {\n root.add(\n new CustomLayout.Image({\n cornerRadius: 999,\n height: USER_CHIP_AVATAR,\n pickable: false,\n shape: 'square',\n src: user.avatar,\n width: USER_CHIP_AVATAR,\n x: x + 4,\n y: chipTop + (USER_CHIP_HEIGHT - USER_CHIP_AVATAR) / 2,\n } as never),\n );\n } else {\n root.add(\n new CustomLayout.Rect({\n // 圆形:人员头像一律是圆的(参考图里单元格 chip 与选择面板同形)。\n cornerRadius: 999,\n fill: tone.bg,\n height: USER_CHIP_AVATAR,\n pickable: false,\n width: USER_CHIP_AVATAR,\n x: x + 4,\n y: chipTop + (USER_CHIP_HEIGHT - USER_CHIP_AVATAR) / 2,\n } as never),\n );\n root.add(\n new CustomLayout.Text({\n fontFamily: palette.fontFamily,\n fill: tone.color,\n fontSize: 9,\n pickable: false,\n text: initials,\n textAlign: 'center',\n textBaseline: 'middle',\n x: x + 4 + USER_CHIP_AVATAR / 2,\n y: chipTop + (USER_CHIP_HEIGHT - USER_CHIP_AVATAR) / 2 + USER_CHIP_AVATAR / 2,\n } as never),\n );\n }\n root.add(\n new CustomLayout.Text({\n fontFamily: palette.fontFamily,\n fill: palette.text,\n fontSize: 13,\n pickable: false,\n text: label,\n textAlign: 'left',\n textBaseline: 'middle',\n x: x + 4 + USER_CHIP_AVATAR + 4,\n y: height / 2,\n } as never),\n );\n });\n\n if (overflow > 0) {\n root.add(createOverflowText(overflow, palette, height, overflowX));\n }\n\n return { enableCellPadding: false, renderDefault: false, rootContainer: root };\n };\n\n/** 值 → 文本项,保留**原始下标**;空串不画(与 painter 原先的 `filter(Boolean)` 同口径)。\n * 下标必须是原始的:记录是 `['', '核心']` 时画面上只有一枚 chip,点它的 × 要删掉「核心」。 */\nconst resolveTextEntries = (value: unknown): { index: number; label: string }[] =>\n toValueList(value)\n .map((item) => String(item))\n .map((label, index) => ({ index, label }))\n .filter(({ label }) => label !== '');\n\n/** 链接 pill 的排布。文案与画布同源:`kind === 'link'` 时要一起剥掉 scheme。 */\nexport const layoutLinkChips = ({\n kind,\n paddingX,\n value,\n width,\n}: {\n kind: LinkKind;\n paddingX: number;\n value: unknown;\n width: number;\n}): { boxes: ChipBox[]; overflow: number; overflowX: number } =>\n layoutChips(\n resolveTextEntries(value).map(({ index, label }) => ({\n index,\n label: kind === 'link' ? formatLinkLabel(label) : label,\n })),\n measureLinkPillWidth,\n () => 24,\n { paddingX, width },\n );\n\nexport const createLinkLayout =\n (kind: LinkKind, placeholder?: string): ICustomLayoutFuc =>\n (args) => {\n const palette = getCanvasPalette(args.table);\n const width = args.rect?.width ?? 140;\n const height = args.rect?.height ?? 40;\n const root = new CustomLayout.Group({ height, pickable: false, width });\n const { boxes, overflow, overflowX } = layoutLinkChips({\n kind,\n paddingX: palette.paddingX,\n value: args.value,\n width: width - editHintGutterFor(args.table as never, args.col, args.row),\n });\n\n if (boxes.length === 0) {\n if (placeholder) {\n root.add(createEmptyCellText(placeholder, palette, height, args.table));\n }\n\n return { enableCellPadding: false, renderDefault: false, rootContainer: root };\n }\n\n const pillHeight = 24;\n const pillTop = Math.max(0, (height - pillHeight) / 2);\n\n boxes.forEach(({ label, width: pillWidth, x }) => {\n root.add(\n new CustomLayout.Rect({\n cornerRadius: 999,\n fill: palette.pillBg,\n height: pillHeight,\n lineWidth: 1,\n pickable: false,\n stroke: palette.overlayBorder,\n width: pillWidth,\n x,\n y: pillTop,\n } as never),\n );\n root.add(\n new CustomLayout.Text({\n fontFamily: palette.fontFamily,\n fill: palette.text,\n fontSize: 13,\n pickable: false,\n text: label,\n textAlign: 'left',\n textBaseline: 'middle',\n x: x + LINK_PAD_LEFT,\n y: height / 2,\n } as never),\n );\n });\n\n if (overflow > 0) {\n root.add(createOverflowText(overflow, palette, height, overflowX));\n }\n\n return { enableCellPadding: false, renderDefault: false, rootContainer: root };\n };\n\n/** 数组 chip 的排布。内边距与标签 / 链接同为 CHIP_PAD —— 右内边距要整颗装下悬停的 ×。 */\nexport const layoutArrayChips = ({\n paddingX,\n value,\n width,\n}: {\n paddingX: number;\n value: unknown;\n width: number;\n}): { boxes: ChipBox[]; overflow: number; overflowX: number } =>\n layoutChips(resolveTextEntries(value), measureArrayChipWidth, () => 24, { paddingX, width });\n\nexport const createArrayLayout =\n (placeholder?: string): ICustomLayoutFuc =>\n (args) => {\n const palette = getCanvasPalette(args.table);\n const width = args.rect?.width ?? 140;\n const height = args.rect?.height ?? 40;\n const root = new CustomLayout.Group({ height, pickable: false, width });\n const { boxes, overflow, overflowX } = layoutArrayChips({\n paddingX: palette.paddingX,\n value: args.value,\n width: width - editHintGutterFor(args.table as never, args.col, args.row),\n });\n\n if (boxes.length === 0) {\n if (placeholder) {\n root.add(createEmptyCellText(placeholder, palette, height, args.table));\n }\n\n return { enableCellPadding: false, renderDefault: false, rootContainer: root };\n }\n\n const chipHeight = 24;\n const chipTop = Math.max(0, (height - chipHeight) / 2);\n\n boxes.forEach(({ label, width: chipWidth, x }) => {\n root.add(\n new CustomLayout.Rect({\n cornerRadius: 4,\n fill: palette.pillBg,\n height: chipHeight,\n pickable: false,\n width: chipWidth,\n x,\n y: chipTop,\n } as never),\n );\n root.add(\n new CustomLayout.Text({\n fontFamily: palette.fontFamily,\n fill: palette.text,\n fontSize: 13,\n pickable: false,\n text: label,\n textAlign: 'left',\n textBaseline: 'middle',\n x: x + ARRAY_PAD_LEFT,\n y: height / 2,\n } as never),\n );\n });\n\n if (overflow > 0) {\n root.add(createOverflowText(overflow, palette, height, overflowX));\n }\n\n return { enableCellPadding: false, renderDefault: false, rootContainer: root };\n };\n"],"mappings":"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"}
@@ -0,0 +1,193 @@
1
+ "use client";
2
+ import { resolveOnAccent } from "./theme.mjs";
3
+ import { getCanvasPalette } from "./canvasPalette.mjs";
4
+ import { toBoolean, toRating } from "../core/cellValue.mjs";
5
+ import { CustomLayout } from "@visactor/vtable";
6
+ //#region src/Table/canvas/controlPainters.ts
7
+ const CHECK_MARK_POINTS = [
8
+ {
9
+ x: 3,
10
+ y: 7.2
11
+ },
12
+ {
13
+ x: 5.8,
14
+ y: 10
15
+ },
16
+ {
17
+ x: 11,
18
+ y: 4
19
+ }
20
+ ];
21
+ const drawCheckMark = (root, centerX, centerY, size, color) => {
22
+ const scale = size / 14;
23
+ const left = centerX - size / 2;
24
+ const top = centerY - size / 2;
25
+ root.add(new CustomLayout.Line({
26
+ lineCap: "round",
27
+ lineJoin: "round",
28
+ lineWidth: 1.6 * scale,
29
+ pickable: false,
30
+ points: CHECK_MARK_POINTS.map((point) => ({
31
+ x: left + point.x * scale,
32
+ y: top + point.y * scale
33
+ })),
34
+ stroke: color
35
+ }));
36
+ };
37
+ const CROSS_MARK_SEGMENTS = [[{
38
+ x: 4,
39
+ y: 4
40
+ }, {
41
+ x: 10,
42
+ y: 10
43
+ }], [{
44
+ x: 10,
45
+ y: 4
46
+ }, {
47
+ x: 4,
48
+ y: 10
49
+ }]];
50
+ const drawCrossMark = (root, centerX, centerY, size, color) => {
51
+ const scale = size / 14;
52
+ const left = centerX - size / 2;
53
+ const top = centerY - size / 2;
54
+ CROSS_MARK_SEGMENTS.forEach((segment) => {
55
+ root.add(new CustomLayout.Line({
56
+ lineCap: "round",
57
+ lineWidth: 1.6 * scale,
58
+ pickable: false,
59
+ points: segment.map((point) => ({
60
+ x: left + point.x * scale,
61
+ y: top + point.y * scale
62
+ })),
63
+ stroke: color
64
+ }));
65
+ });
66
+ };
67
+ const createCheckboxLayout = () => (args) => {
68
+ const palette = getCanvasPalette(args.table);
69
+ const width = args.rect?.width ?? 64;
70
+ const height = args.rect?.height ?? 40;
71
+ const checked = toBoolean(args.value) === true;
72
+ const size = 16;
73
+ const x = palette.paddingX;
74
+ const y = (height - size) / 2;
75
+ const root = new CustomLayout.Group({
76
+ height,
77
+ pickable: false,
78
+ width
79
+ });
80
+ root.add(new CustomLayout.Rect({
81
+ cornerRadius: 4,
82
+ fill: checked ? palette.accent : palette.checkboxBg,
83
+ height: size,
84
+ lineWidth: 1,
85
+ pickable: false,
86
+ stroke: checked ? palette.accent : palette.overlayBorder,
87
+ width: size,
88
+ x,
89
+ y
90
+ }));
91
+ if (checked) drawCheckMark(root, x + size / 2, height / 2, 12, resolveOnAccent(palette.accent));
92
+ return {
93
+ enableCellPadding: false,
94
+ renderDefault: false,
95
+ rootContainer: root
96
+ };
97
+ };
98
+ const createRatingLayout = () => (args) => {
99
+ const palette = getCanvasPalette(args.table);
100
+ const width = args.rect?.width ?? 96;
101
+ const height = args.rect?.height ?? 40;
102
+ const rating = toRating(args.value);
103
+ const root = new CustomLayout.Group({
104
+ height,
105
+ pickable: false,
106
+ width
107
+ });
108
+ const starSize = 16;
109
+ for (let star = 1; star <= 5; star += 1) {
110
+ const active = star <= rating;
111
+ root.add(new CustomLayout.Text({
112
+ fontFamily: palette.fontFamily,
113
+ fill: active ? palette.starOn : palette.starOff,
114
+ fontSize: 14,
115
+ pickable: false,
116
+ text: active ? "★" : "☆",
117
+ textAlign: "left",
118
+ textBaseline: "middle",
119
+ x: palette.paddingX + (star - 1) * starSize,
120
+ y: height / 2
121
+ }));
122
+ }
123
+ return {
124
+ enableCellPadding: false,
125
+ renderDefault: false,
126
+ rootContainer: root
127
+ };
128
+ };
129
+ const createBooleanLayout = (editable) => (args) => {
130
+ const palette = getCanvasPalette(args.table);
131
+ const width = args.rect?.width ?? 80;
132
+ const height = args.rect?.height ?? 40;
133
+ const flag = toBoolean(args.value);
134
+ const root = new CustomLayout.Group({
135
+ height,
136
+ pickable: false,
137
+ width
138
+ });
139
+ if (flag == null && editable === false) return {
140
+ enableCellPadding: false,
141
+ renderDefault: false,
142
+ rootContainer: root
143
+ };
144
+ const checked = flag === true;
145
+ if (checked) drawCheckMark(root, palette.paddingX + 7, height / 2, 13, palette.success);
146
+ else drawCrossMark(root, palette.paddingX + 7, height / 2, 13, palette.textMuted);
147
+ root.add(new CustomLayout.Text({
148
+ fontFamily: palette.fontFamily,
149
+ fill: palette.text,
150
+ fontSize: 13,
151
+ pickable: false,
152
+ text: checked ? "是" : "否",
153
+ textAlign: "left",
154
+ textBaseline: "middle",
155
+ x: palette.paddingX + 14,
156
+ y: height / 2
157
+ }));
158
+ return {
159
+ enableCellPadding: false,
160
+ renderDefault: false,
161
+ rootContainer: root
162
+ };
163
+ };
164
+ const drawSeriesCheckbox = (root, centerX, centerY, size, checked, indeterminate, palette) => {
165
+ const x = centerX - size / 2;
166
+ const y = centerY - size / 2;
167
+ const active = checked || indeterminate;
168
+ root.add(new CustomLayout.Rect({
169
+ cornerRadius: 4,
170
+ fill: active ? palette.accent : palette.checkboxBg,
171
+ height: size,
172
+ lineWidth: 1,
173
+ pickable: false,
174
+ stroke: active ? palette.accent : palette.overlayBorder,
175
+ width: size,
176
+ x,
177
+ y
178
+ }));
179
+ if (checked) drawCheckMark(root, centerX, centerY, size - 4, resolveOnAccent(palette.accent));
180
+ else if (indeterminate) root.add(new CustomLayout.Rect({
181
+ cornerRadius: 999,
182
+ fill: resolveOnAccent(palette.accent),
183
+ height: 2,
184
+ pickable: false,
185
+ width: 8,
186
+ x: centerX - 4,
187
+ y: centerY - 1
188
+ }));
189
+ };
190
+ //#endregion
191
+ export { createBooleanLayout, createCheckboxLayout, createRatingLayout, drawSeriesCheckbox };
192
+
193
+ //# sourceMappingURL=controlPainters.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controlPainters.mjs","names":[],"sources":["../../../src/Table/canvas/controlPainters.ts"],"sourcesContent":["'use client';\n\nimport { CustomLayout } from '@visactor/vtable';\nimport type { ICustomLayoutFuc } from '@visactor/vtable/es/ts-types/customLayout';\n\nimport { toBoolean, toRating } from '../core/cellValue';\nimport { type CanvasPalette, getCanvasPalette } from './canvasPalette';\nimport { resolveOnAccent } from './theme';\n\nconst CHECK_MARK_POINTS = [\n { x: 3, y: 7.2 },\n { x: 5.8, y: 10 },\n { x: 11, y: 4 },\n];\n\nconst drawCheckMark = (\n root: any,\n centerX: number,\n centerY: number,\n size: number,\n color: string,\n): void => {\n const scale = size / 14;\n const left = centerX - size / 2;\n const top = centerY - size / 2;\n\n root.add(\n new CustomLayout.Line({\n lineCap: 'round',\n lineJoin: 'round',\n lineWidth: 1.6 * scale,\n pickable: false,\n points: CHECK_MARK_POINTS.map((point) => ({\n x: left + point.x * scale,\n y: top + point.y * scale,\n })),\n stroke: color,\n } as never),\n );\n};\n\nconst CROSS_MARK_SEGMENTS = [\n [\n { x: 4, y: 4 },\n { x: 10, y: 10 },\n ],\n [\n { x: 10, y: 4 },\n { x: 4, y: 10 },\n ],\n];\n\nconst drawCrossMark = (\n root: any,\n centerX: number,\n centerY: number,\n size: number,\n color: string,\n): void => {\n const scale = size / 14;\n const left = centerX - size / 2;\n const top = centerY - size / 2;\n\n CROSS_MARK_SEGMENTS.forEach((segment) => {\n root.add(\n new CustomLayout.Line({\n lineCap: 'round',\n lineWidth: 1.6 * scale,\n pickable: false,\n points: segment.map((point) => ({\n x: left + point.x * scale,\n y: top + point.y * scale,\n })),\n stroke: color,\n } as never),\n );\n });\n};\n\nexport const createCheckboxLayout = (): ICustomLayoutFuc => (args) => {\n const palette = getCanvasPalette(args.table);\n const width = args.rect?.width ?? 64;\n const height = args.rect?.height ?? 40;\n const checked = toBoolean(args.value) === true;\n const size = 16;\n const x = palette.paddingX;\n const y = (height - size) / 2;\n const root = new CustomLayout.Group({ height, pickable: false, width });\n\n root.add(\n new CustomLayout.Rect({\n cornerRadius: 4,\n fill: checked ? palette.accent : palette.checkboxBg,\n height: size,\n lineWidth: 1,\n pickable: false,\n stroke: checked ? palette.accent : palette.overlayBorder,\n width: size,\n x,\n y,\n } as never),\n );\n\n if (checked) {\n drawCheckMark(root, x + size / 2, height / 2, size - 4, resolveOnAccent(palette.accent));\n }\n\n return { enableCellPadding: false, renderDefault: false, rootContainer: root };\n};\n\nexport const createRatingLayout = (): ICustomLayoutFuc => (args) => {\n const palette = getCanvasPalette(args.table);\n const width = args.rect?.width ?? 96;\n const height = args.rect?.height ?? 40;\n const rating = toRating(args.value);\n const root = new CustomLayout.Group({ height, pickable: false, width });\n const starSize = 16;\n\n for (let star = 1; star <= 5; star += 1) {\n const active = star <= rating;\n\n root.add(\n new CustomLayout.Text({\n fontFamily: palette.fontFamily,\n fill: active ? palette.starOn : palette.starOff,\n fontSize: 14,\n pickable: false,\n text: active ? '★' : '☆',\n textAlign: 'left',\n textBaseline: 'middle',\n x: palette.paddingX + (star - 1) * starSize,\n y: height / 2,\n } as never),\n );\n }\n\n return { enableCellPadding: false, renderDefault: false, rootContainer: root };\n};\n\nexport const createBooleanLayout =\n (editable?: boolean): ICustomLayoutFuc =>\n (args) => {\n const palette = getCanvasPalette(args.table);\n const width = args.rect?.width ?? 80;\n const height = args.rect?.height ?? 40;\n const flag = toBoolean(args.value);\n const root = new CustomLayout.Group({ height, pickable: false, width });\n\n if (flag == null && editable === false) {\n return { enableCellPadding: false, renderDefault: false, rootContainer: root };\n }\n\n const checked = flag === true;\n\n if (checked) {\n drawCheckMark(root, palette.paddingX + 7, height / 2, 13, palette.success);\n } else {\n drawCrossMark(root, palette.paddingX + 7, height / 2, 13, palette.textMuted);\n }\n root.add(\n new CustomLayout.Text({\n fontFamily: palette.fontFamily,\n fill: palette.text,\n fontSize: 13,\n pickable: false,\n text: checked ? '是' : '否',\n textAlign: 'left',\n textBaseline: 'middle',\n x: palette.paddingX + 14,\n y: height / 2,\n } as never),\n );\n\n return { enableCellPadding: false, renderDefault: false, rootContainer: root };\n };\n\nexport const drawSeriesCheckbox = (\n root: any,\n centerX: number,\n centerY: number,\n size: number,\n checked: boolean,\n indeterminate: boolean,\n palette: CanvasPalette,\n): void => {\n const x = centerX - size / 2;\n const y = centerY - size / 2;\n const active = checked || indeterminate;\n\n root.add(\n new CustomLayout.Rect({\n cornerRadius: 4,\n fill: active ? palette.accent : palette.checkboxBg,\n height: size,\n lineWidth: 1,\n pickable: false,\n stroke: active ? palette.accent : palette.overlayBorder,\n width: size,\n x,\n y,\n } as never),\n );\n\n if (checked) {\n drawCheckMark(root, centerX, centerY, size - 4, resolveOnAccent(palette.accent));\n } else if (indeterminate) {\n root.add(\n new CustomLayout.Rect({\n cornerRadius: 999,\n fill: resolveOnAccent(palette.accent),\n height: 2,\n pickable: false,\n width: 8,\n x: centerX - 4,\n y: centerY - 1,\n } as never),\n );\n }\n};\n"],"mappings":"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"}