@superad/ui 0.1.1 → 0.1.2

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.
@@ -15,13 +15,16 @@ const useTableRenderState = ({ autoFillWidth, autoHeight, columns, editable, emp
15
15
  const { colorBgContainer, colorBorderSecondary, colorFillQuaternary, colorFillSecondary, colorFillTertiary, colorInfo, colorPrimary, colorSplit, colorSuccess, colorText, colorTextQuaternary, colorTextSecondary, colorTextTertiary, fontFamily, isDarkMode } = themeTokens;
16
16
  /** 送给编辑器 root 的主题。不传的话那边是全新 root、没有父级 context,一律退回 antd 默认。 */
17
17
  const editorTheme = useMemo(() => {
18
+ const built = createSuperadAntdTheme({
19
+ appearance: isDarkMode ? "dark" : "light",
20
+ neutralColor: themeTokens.neutralColor,
21
+ primaryColor: themeTokens.primaryColor
22
+ });
23
+ const token = {};
24
+ for (const [key, value] of Object.entries(themeTokens)) if (value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") token[key] = value;
18
25
  return {
19
- components: createSuperadAntdTheme({
20
- appearance: isDarkMode ? "dark" : "light",
21
- neutralColor: themeTokens.neutralColor,
22
- primaryColor: themeTokens.primaryColor
23
- }).components,
24
- token: themeTokens
26
+ components: built.components,
27
+ token
25
28
  };
26
29
  }, [isDarkMode, themeTokens]);
27
30
  const token = useMemo(() => ({
@@ -1 +1 @@
1
- {"version":3,"file":"useTableRenderState.mjs","names":[],"sources":["../../../src/Table/react/useTableRenderState.ts"],"sourcesContent":["'use client';\n\nimport type { ListTable as ListTableInstance } from '@visactor/vtable';\nimport type { ThemeConfig } from 'antd';\nimport { useTheme } from 'antd-style';\nimport { type CSSProperties, type RefObject, useEffect, useMemo, useRef } from 'react';\n\nimport { useI18n } from '@/ConfigProvider';\nimport type { NeutralColors, PrimaryColors } from '@/styles/customTheme';\nimport { createSuperadAntdTheme } from '@/styles/theme/antdTheme';\n\nimport {\n createTableTheme,\n isOpaqueColor,\n resolveGroupFill,\n resolveTableCanvasPalette,\n type TableToken,\n} from '../canvas/theme';\nimport { computeTableHeight, parsePxSize, toCssSize } from '../core/size';\nimport { resolveTableI18n, type TableI18n } from '../core/tableI18n';\nimport type { TableProps } from '../type';\nimport { createTableOption } from '../vtable/option';\nimport { getScenegraph, isTableReleased, updateCellContentRange } from '../vtable/vtableAdapter';\nimport { sameRenderInputs } from './sameRenderInputs';\n\ntype RenderStateProps = Pick<\n TableProps,\n | 'autoFillWidth'\n | 'autoHeight'\n | 'editable'\n | 'emptyText'\n | 'frozenColCount'\n | 'groupBy'\n | 'groupTitleColors'\n | 'groupTitleStyle'\n | 'headerRowHeight'\n | 'height'\n | 'hover'\n | 'option'\n | 'records'\n | 'rowDrag'\n | 'rowHeight'\n | 'showHeader'\n | 'showRowIndex'\n | 'showRowSelection'\n | 'showVerticalBorder'\n | 'size'\n | 'style'\n | 'theme'\n | 'tree'\n | 'treeExpandLevel'\n | 'treeIndent'\n | 'width'\n> & {\n columns?: TableProps['columns'];\n /**\n * 引擎实测的内容高度(含底部那 8px 的槽),折叠 / 展开之后由\n * `useTableVTableEvents` 写入;没发生过折叠时是 null,走按数据算的那条。\n */\n measuredContentHeight: number | null;\n tableRef: RefObject<ListTableInstance | null>;\n};\n\nexport const useTableRenderState = ({\n autoFillWidth,\n autoHeight,\n columns,\n editable,\n emptyText,\n frozenColCount,\n groupBy,\n groupTitleColors,\n groupTitleStyle,\n headerRowHeight,\n height,\n hover,\n measuredContentHeight,\n option,\n records,\n rowDrag,\n rowHeight,\n showHeader,\n showRowIndex,\n showRowSelection,\n showVerticalBorder,\n size,\n style,\n tableRef,\n theme,\n tree,\n treeExpandLevel,\n treeIndent,\n width,\n}: RenderStateProps) => {\n // 自定义种子(`createSuperadAntdTheme` 的入参)会留在解析后的 token 上;读回来是为了在**编辑器\n // 那个独立 React root** 里原样重建同一份主题 —— context 不跨 root,那边拿不到 ThemeProvider。\n const themeTokens = useTheme() as ReturnType<typeof useTheme> & {\n neutralColor?: NeutralColors;\n primaryColor?: PrimaryColors;\n };\n const {\n colorBgContainer,\n colorBorderSecondary,\n colorFillQuaternary,\n colorFillSecondary,\n colorFillTertiary,\n colorInfo,\n colorPrimary,\n colorSplit,\n colorSuccess,\n colorText,\n colorTextQuaternary,\n colorTextSecondary,\n colorTextTertiary,\n fontFamily,\n isDarkMode,\n } = themeTokens;\n\n /** 送给编辑器 root 的主题。不传的话那边是全新 root、没有父级 context,一律退回 antd 默认。 */\n const editorTheme = useMemo(() => {\n const built = createSuperadAntdTheme({\n appearance: isDarkMode ? 'dark' : 'light',\n neutralColor: themeTokens.neutralColor,\n primaryColor: themeTokens.primaryColor,\n });\n\n // 只交 **已经解析好的 token** + `components` 覆盖,**不带 `algorithm`**:\n // 项目那套自定义 algorithm 在编辑器那个 root 里会算出残缺的 token(实测 `fontSize` /\n // `controlHeight` 是 undefined),日期面板拿不到 `--ant-font-size`,字号退回继承的 16px、\n // 日期区宽度从 288 涨到 906。这里给的是外层主题的终值,让 antd 用默认算法原样收下。\n return { components: built.components, token: themeTokens };\n }, [isDarkMode, themeTokens]);\n\n const token = useMemo<TableToken>(\n () => ({\n colorBgContainer,\n colorBorderSecondary,\n colorFillQuaternary,\n colorFillSecondary,\n colorFillTertiary,\n colorInfo,\n colorPrimary,\n colorSplit,\n colorSuccess,\n colorText,\n colorTextQuaternary,\n colorTextSecondary,\n colorTextTertiary,\n fontFamily,\n isDarkMode,\n }),\n [\n colorBgContainer,\n colorBorderSecondary,\n colorFillQuaternary,\n colorFillSecondary,\n colorFillTertiary,\n colorInfo,\n colorPrimary,\n colorSplit,\n colorSuccess,\n colorText,\n colorTextQuaternary,\n colorTextSecondary,\n colorTextTertiary,\n fontFamily,\n isDarkMode,\n ],\n );\n\n const baseTheme = useMemo(\n () => createTableTheme(token, { showVerticalBorder }),\n [showVerticalBorder, token],\n );\n\n /** Column-level style beats the table theme, which beats the built-in theme. */\n const groupTitleStyleMerged = useMemo(\n () => ({ ...baseTheme.groupTitleStyle, ...theme?.groupTitleStyle, ...groupTitleStyle }),\n [baseTheme, groupTitleStyle, theme],\n );\n\n const stickyGroupBackground = useMemo(() => {\n const background = groupTitleStyleMerged?.bgColor;\n\n // A sticky title sits over the rows scrolling beneath it, so a caller-supplied fill has\n // to clear the same opacity bar as the token does — otherwise `bgColor: 'rgba(…, 0.02)'`\n // would let content show through the pinned title.\n if (typeof background === 'string' && background && isOpaqueColor(background)) {\n return background;\n }\n\n return resolveGroupFill({ isDarkMode: token.isDarkMode } as TableToken);\n }, [groupTitleStyleMerged, token]);\n\n const { locale, t } = useI18n();\n const i18n = useMemo(\n () => resolveTableI18n(locale, (key) => t(key as Parameters<typeof t>[0])),\n [locale, t],\n );\n\n const optionInputs = {\n autoFillWidth: autoFillWidth ?? true,\n columns,\n editable,\n emptyText,\n i18n,\n frozenColCount,\n groupBy,\n groupTitleColors,\n headerRowHeight,\n hover,\n option,\n records,\n rowDrag,\n rowHeight,\n showHeader,\n showRowIndex,\n showRowSelection,\n showVerticalBorder,\n size,\n theme: {\n ...baseTheme,\n ...theme,\n groupTitleStyle: groupTitleStyleMerged,\n },\n token,\n tree,\n treeExpandLevel,\n treeIndent,\n /**\n * 送给编辑器的主题。编辑器渲染在 `createRoot` 起的**独立 React root** 里,React context 不跨\n * root —— 那边不显式传 theme 就退回 antd 默认(主色是 `#1677ff` 的蓝,而本项目是 `#222222`)。\n * 与 palette 同一条通道:写进 `customConfig`,主题一变随 option 重建一起更新。\n */\n editorTheme,\n };\n /**\n * 按**内容**复用上一次的 option 对象,而不是按引用 memo。\n *\n * 使用方写 `columns={[...]}` / `records={rows.map(...)}` 时每次渲染都是新数组,`useMemo`\n * 的引用比较必然失效 → 新 option 里全是新建的函数与编辑器实例 → react-vtable 的\n * `isEqual` 对非纯对象直接返回 false → 每次父组件渲染都走一遍 `updateOption`(整表重建:\n * 重新编译列、重建数据源、9 个事件解绑重绑)。按内容认出来就能让引用保持稳定,\n * `updateOption` 完全不会发生。\n *\n * 在渲染期写 ref 是 `useMemo` 本身的实现方式,这里只是把判据从引用换成内容。\n */\n const optionRef = useRef<\n { inputs: typeof optionInputs; value: ReturnType<typeof createTableOption> } | undefined\n >(undefined);\n const mergedOption = (() => {\n const previous = optionRef.current;\n if (previous != null && sameRenderInputs(previous.inputs, optionInputs)) return previous.value;\n\n const value = createTableOption(optionInputs);\n optionRef.current = { inputs: optionInputs, value };\n return value;\n })();\n\n useEffect(() => {\n const table = tableRef.current;\n // 用 isTableReleased 精确守卫,而不是把整个 effect 包进 try/catch:那个 catch 会把\n // palette 写入失败、私有 API 漂移一并吞掉,症状是「主题换了但 canvas 颜色不更新」而且\n // 没有任何信号。表被 release() 之后 scenegraph 与 internalProps 都是 null,读任何\n // getter 都会抛,那一种情形由守卫覆盖。\n if (table == null || isTableReleased(table)) return;\n\n (\n table as ListTableInstance & {\n __superadPalette?: ReturnType<typeof resolveTableCanvasPalette>;\n }\n ).__superadPalette = resolveTableCanvasPalette(token);\n // 与 palette 同一条通道:编辑器渲染在自己的 React root 里,只能从表实例上取主题。\n (table as ListTableInstance & { __superadTheme?: ThemeConfig }).__superadTheme = editorTheme;\n (table as ListTableInstance & { __superadI18n?: TableI18n }).__superadI18n = i18n;\n\n const scenegraph = getScenegraph(table);\n const headerRows = table.columnHeaderLevelCount ?? 1;\n const startRow = Math.max(headerRows, scenegraph.proxy?.rowStart ?? headerRows);\n // rowCount 是数量、不是行号,要 -1;对照 vtableAdapter 的 getVisibleRowRange 也是这么写的。\n const endRow = Math.min((table.rowCount ?? 1) - 1, scenegraph.proxy?.rowEnd ?? startRow + 40);\n const endCol = (table.colCount ?? 1) - 1;\n\n if (headerRows > 0) updateCellContentRange(table, 0, 0, endCol, headerRows - 1);\n if (endRow >= startRow) updateCellContentRange(table, 0, startRow, endCol, endRow);\n scenegraph.updateNextFrame?.();\n }, [editorTheme, i18n, mergedOption, tableRef, token]);\n\n const dataContentHeight = useMemo(\n () =>\n computeTableHeight({\n groupBy,\n headerRowHeight: parsePxSize(headerRowHeight),\n records,\n rowHeight: parsePxSize(rowHeight),\n showHeader,\n size,\n tree,\n }),\n [groupBy, headerRowHeight, records, rowHeight, showHeader, size, tree],\n );\n /**\n * 引擎实测优先:按数据算只在首帧成立(那时还没有引擎实例,SSR 也要有个值),一旦用户折叠过,\n * 行数就只有引擎知道了。见 `useTableVTableEvents` 里 `onHierarchyChange` 的注释。\n */\n const contentHeight = measuredContentHeight ?? dataContentHeight;\n const resolvedHeight =\n autoHeight !== false && typeof height === 'number' && records?.length\n ? Math.min(height, contentHeight)\n : (height ?? contentHeight);\n const rootStyle: CSSProperties = {\n height: toCssSize(resolvedHeight),\n width: toCssSize(width ?? '100%'),\n ...style,\n };\n\n return { mergedOption, rootStyle, stickyGroupBackground, token };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"useTableRenderState.mjs","names":[],"sources":["../../../src/Table/react/useTableRenderState.ts"],"sourcesContent":["'use client';\n\nimport type { ListTable as ListTableInstance } from '@visactor/vtable';\nimport type { ThemeConfig } from 'antd';\nimport { useTheme } from 'antd-style';\nimport { type CSSProperties, type RefObject, useEffect, useMemo, useRef } from 'react';\n\nimport { useI18n } from '@/ConfigProvider';\nimport type { NeutralColors, PrimaryColors } from '@/styles/customTheme';\nimport { createSuperadAntdTheme } from '@/styles/theme/antdTheme';\n\nimport {\n createTableTheme,\n isOpaqueColor,\n resolveGroupFill,\n resolveTableCanvasPalette,\n type TableToken,\n} from '../canvas/theme';\nimport { computeTableHeight, parsePxSize, toCssSize } from '../core/size';\nimport { resolveTableI18n, type TableI18n } from '../core/tableI18n';\nimport type { TableProps } from '../type';\nimport { createTableOption } from '../vtable/option';\nimport { getScenegraph, isTableReleased, updateCellContentRange } from '../vtable/vtableAdapter';\nimport { sameRenderInputs } from './sameRenderInputs';\n\ntype RenderStateProps = Pick<\n TableProps,\n | 'autoFillWidth'\n | 'autoHeight'\n | 'editable'\n | 'emptyText'\n | 'frozenColCount'\n | 'groupBy'\n | 'groupTitleColors'\n | 'groupTitleStyle'\n | 'headerRowHeight'\n | 'height'\n | 'hover'\n | 'option'\n | 'records'\n | 'rowDrag'\n | 'rowHeight'\n | 'showHeader'\n | 'showRowIndex'\n | 'showRowSelection'\n | 'showVerticalBorder'\n | 'size'\n | 'style'\n | 'theme'\n | 'tree'\n | 'treeExpandLevel'\n | 'treeIndent'\n | 'width'\n> & {\n columns?: TableProps['columns'];\n /**\n * 引擎实测的内容高度(含底部那 8px 的槽),折叠 / 展开之后由\n * `useTableVTableEvents` 写入;没发生过折叠时是 null,走按数据算的那条。\n */\n measuredContentHeight: number | null;\n tableRef: RefObject<ListTableInstance | null>;\n};\n\nexport const useTableRenderState = ({\n autoFillWidth,\n autoHeight,\n columns,\n editable,\n emptyText,\n frozenColCount,\n groupBy,\n groupTitleColors,\n groupTitleStyle,\n headerRowHeight,\n height,\n hover,\n measuredContentHeight,\n option,\n records,\n rowDrag,\n rowHeight,\n showHeader,\n showRowIndex,\n showRowSelection,\n showVerticalBorder,\n size,\n style,\n tableRef,\n theme,\n tree,\n treeExpandLevel,\n treeIndent,\n width,\n}: RenderStateProps) => {\n // 自定义种子(`createSuperadAntdTheme` 的入参)会留在解析后的 token 上;读回来是为了在**编辑器\n // 那个独立 React root** 里原样重建同一份主题 —— context 不跨 root,那边拿不到 ThemeProvider。\n const themeTokens = useTheme() as ReturnType<typeof useTheme> & {\n neutralColor?: NeutralColors;\n primaryColor?: PrimaryColors;\n };\n const {\n colorBgContainer,\n colorBorderSecondary,\n colorFillQuaternary,\n colorFillSecondary,\n colorFillTertiary,\n colorInfo,\n colorPrimary,\n colorSplit,\n colorSuccess,\n colorText,\n colorTextQuaternary,\n colorTextSecondary,\n colorTextTertiary,\n fontFamily,\n isDarkMode,\n } = themeTokens;\n\n /** 送给编辑器 root 的主题。不传的话那边是全新 root、没有父级 context,一律退回 antd 默认。 */\n const editorTheme = useMemo(() => {\n const built = createSuperadAntdTheme({\n appearance: isDarkMode ? 'dark' : 'light',\n neutralColor: themeTokens.neutralColor,\n primaryColor: themeTokens.primaryColor,\n });\n\n // 只交 **已经解析好的 token** + `components` 覆盖,**不带 `algorithm`**:\n // 项目那套自定义 algorithm 在编辑器那个 root 里会算出残缺的 token(实测 `fontSize` /\n // `controlHeight` 是 undefined),日期面板拿不到 `--ant-font-size`,字号退回继承的 16px、\n // 日期区宽度从 288 涨到 906。这里给的是外层主题的终值,让 antd 用默认算法原样收下。\n //\n // 不能把 `useTheme()` 整包塞进去:antd-style 的返回值带 stylish / css 等函数,\n // 每次渲染都是新引用。sameRenderInputs 对函数按引用比,option 就会被判成变了,\n // 编辑时 hover 一闪就 updateOption —— Tree 分组上表现为界面抖动、固定列被再摊一次。\n const token: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(themeTokens)) {\n if (\n value == null ||\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n token[key] = value;\n }\n }\n\n return { components: built.components, token };\n }, [isDarkMode, themeTokens]);\n\n const token = useMemo<TableToken>(\n () => ({\n colorBgContainer,\n colorBorderSecondary,\n colorFillQuaternary,\n colorFillSecondary,\n colorFillTertiary,\n colorInfo,\n colorPrimary,\n colorSplit,\n colorSuccess,\n colorText,\n colorTextQuaternary,\n colorTextSecondary,\n colorTextTertiary,\n fontFamily,\n isDarkMode,\n }),\n [\n colorBgContainer,\n colorBorderSecondary,\n colorFillQuaternary,\n colorFillSecondary,\n colorFillTertiary,\n colorInfo,\n colorPrimary,\n colorSplit,\n colorSuccess,\n colorText,\n colorTextQuaternary,\n colorTextSecondary,\n colorTextTertiary,\n fontFamily,\n isDarkMode,\n ],\n );\n\n const baseTheme = useMemo(\n () => createTableTheme(token, { showVerticalBorder }),\n [showVerticalBorder, token],\n );\n\n /** Column-level style beats the table theme, which beats the built-in theme. */\n const groupTitleStyleMerged = useMemo(\n () => ({ ...baseTheme.groupTitleStyle, ...theme?.groupTitleStyle, ...groupTitleStyle }),\n [baseTheme, groupTitleStyle, theme],\n );\n\n const stickyGroupBackground = useMemo(() => {\n const background = groupTitleStyleMerged?.bgColor;\n\n // A sticky title sits over the rows scrolling beneath it, so a caller-supplied fill has\n // to clear the same opacity bar as the token does — otherwise `bgColor: 'rgba(…, 0.02)'`\n // would let content show through the pinned title.\n if (typeof background === 'string' && background && isOpaqueColor(background)) {\n return background;\n }\n\n return resolveGroupFill({ isDarkMode: token.isDarkMode } as TableToken);\n }, [groupTitleStyleMerged, token]);\n\n const { locale, t } = useI18n();\n const i18n = useMemo(\n () => resolveTableI18n(locale, (key) => t(key as Parameters<typeof t>[0])),\n [locale, t],\n );\n\n const optionInputs = {\n autoFillWidth: autoFillWidth ?? true,\n columns,\n editable,\n emptyText,\n i18n,\n frozenColCount,\n groupBy,\n groupTitleColors,\n headerRowHeight,\n hover,\n option,\n records,\n rowDrag,\n rowHeight,\n showHeader,\n showRowIndex,\n showRowSelection,\n showVerticalBorder,\n size,\n theme: {\n ...baseTheme,\n ...theme,\n groupTitleStyle: groupTitleStyleMerged,\n },\n token,\n tree,\n treeExpandLevel,\n treeIndent,\n /**\n * 送给编辑器的主题。编辑器渲染在 `createRoot` 起的**独立 React root** 里,React context 不跨\n * root —— 那边不显式传 theme 就退回 antd 默认(主色是 `#1677ff` 的蓝,而本项目是 `#222222`)。\n * 与 palette 同一条通道:写进 `customConfig`,主题一变随 option 重建一起更新。\n */\n editorTheme,\n };\n /**\n * 按**内容**复用上一次的 option 对象,而不是按引用 memo。\n *\n * 使用方写 `columns={[...]}` / `records={rows.map(...)}` 时每次渲染都是新数组,`useMemo`\n * 的引用比较必然失效 → 新 option 里全是新建的函数与编辑器实例 → react-vtable 的\n * `isEqual` 对非纯对象直接返回 false → 每次父组件渲染都走一遍 `updateOption`(整表重建:\n * 重新编译列、重建数据源、9 个事件解绑重绑)。按内容认出来就能让引用保持稳定,\n * `updateOption` 完全不会发生。\n *\n * 在渲染期写 ref 是 `useMemo` 本身的实现方式,这里只是把判据从引用换成内容。\n */\n const optionRef = useRef<\n { inputs: typeof optionInputs; value: ReturnType<typeof createTableOption> } | undefined\n >(undefined);\n const mergedOption = (() => {\n const previous = optionRef.current;\n if (previous != null && sameRenderInputs(previous.inputs, optionInputs)) return previous.value;\n\n const value = createTableOption(optionInputs);\n optionRef.current = { inputs: optionInputs, value };\n return value;\n })();\n\n useEffect(() => {\n const table = tableRef.current;\n // 用 isTableReleased 精确守卫,而不是把整个 effect 包进 try/catch:那个 catch 会把\n // palette 写入失败、私有 API 漂移一并吞掉,症状是「主题换了但 canvas 颜色不更新」而且\n // 没有任何信号。表被 release() 之后 scenegraph 与 internalProps 都是 null,读任何\n // getter 都会抛,那一种情形由守卫覆盖。\n if (table == null || isTableReleased(table)) return;\n\n (\n table as ListTableInstance & {\n __superadPalette?: ReturnType<typeof resolveTableCanvasPalette>;\n }\n ).__superadPalette = resolveTableCanvasPalette(token);\n // 与 palette 同一条通道:编辑器渲染在自己的 React root 里,只能从表实例上取主题。\n (table as ListTableInstance & { __superadTheme?: ThemeConfig }).__superadTheme = editorTheme;\n (table as ListTableInstance & { __superadI18n?: TableI18n }).__superadI18n = i18n;\n\n const scenegraph = getScenegraph(table);\n const headerRows = table.columnHeaderLevelCount ?? 1;\n const startRow = Math.max(headerRows, scenegraph.proxy?.rowStart ?? headerRows);\n // rowCount 是数量、不是行号,要 -1;对照 vtableAdapter 的 getVisibleRowRange 也是这么写的。\n const endRow = Math.min((table.rowCount ?? 1) - 1, scenegraph.proxy?.rowEnd ?? startRow + 40);\n const endCol = (table.colCount ?? 1) - 1;\n\n if (headerRows > 0) updateCellContentRange(table, 0, 0, endCol, headerRows - 1);\n if (endRow >= startRow) updateCellContentRange(table, 0, startRow, endCol, endRow);\n scenegraph.updateNextFrame?.();\n }, [editorTheme, i18n, mergedOption, tableRef, token]);\n\n const dataContentHeight = useMemo(\n () =>\n computeTableHeight({\n groupBy,\n headerRowHeight: parsePxSize(headerRowHeight),\n records,\n rowHeight: parsePxSize(rowHeight),\n showHeader,\n size,\n tree,\n }),\n [groupBy, headerRowHeight, records, rowHeight, showHeader, size, tree],\n );\n /**\n * 引擎实测优先:按数据算只在首帧成立(那时还没有引擎实例,SSR 也要有个值),一旦用户折叠过,\n * 行数就只有引擎知道了。见 `useTableVTableEvents` 里 `onHierarchyChange` 的注释。\n */\n const contentHeight = measuredContentHeight ?? dataContentHeight;\n const resolvedHeight =\n autoHeight !== false && typeof height === 'number' && records?.length\n ? Math.min(height, contentHeight)\n : (height ?? contentHeight);\n const rootStyle: CSSProperties = {\n height: toCssSize(resolvedHeight),\n width: toCssSize(width ?? '100%'),\n ...style,\n };\n\n return { mergedOption, rootStyle, stickyGroupBackground, token };\n};\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { resolveTableCanvasPalette } from "../canvas/theme.mjs";
3
- import { getAllRowsHeight } from "../vtable/vtableAdapter.mjs";
3
+ import { getAllRowsHeight, pinRowSeriesMaxWidth } from "../vtable/vtableAdapter.mjs";
4
4
  import { patchFrozenShadowControls } from "../vtable/frozenShadow.mjs";
5
5
  import { alignStickyGroupTitles } from "../canvas/groupTitle.mjs";
6
6
  import { syncCascadeSelectionRanges } from "../vtable/seriesSelection.mjs";
@@ -80,6 +80,7 @@ const useTableVTableEvents = ({ lastRowSeriesHoverRef, onReady, rootRef, schedul
80
80
  const onBeforeUpdateOption = () => {
81
81
  table.completeEditCell?.();
82
82
  };
83
+ const onUpdated = () => pinRowSeriesMaxWidth(table);
83
84
  /**
84
85
  * a11y 隐藏层每个 cell 现读 `table.getCellValue`,但只在 `a11yVersion` 自增时才重读。
85
86
  * 编辑提交走的是 `changeCellValue`,既不滚动也不改变选中 —— 不订阅它,读屏读到的会一直是
@@ -93,6 +94,7 @@ const useTableVTableEvents = ({ lastRowSeriesHoverRef, onReady, rootRef, schedul
93
94
  table.on("resize_column_end", onResizeEnd);
94
95
  table.on("scroll", onScrollSticky);
95
96
  table.on(TABLE_EVENT_TYPE.BEFORE_UPDATE_OPTION, onBeforeUpdateOption);
97
+ table.on(TABLE_EVENT_TYPE.UPDATED, onUpdated);
96
98
  table.on(TABLE_EVENT_TYPE.CHANGE_CELL_VALUE, onCellValueChange);
97
99
  table.on(TABLE_EVENT_TYPE.CHANGE_CELL_VALUES, onCellValueChange);
98
100
  table.on(TABLE_EVENT_TYPE.CLICK_CELL, onEditTrigger);
@@ -106,6 +108,7 @@ const useTableVTableEvents = ({ lastRowSeriesHoverRef, onReady, rootRef, schedul
106
108
  table.off("resize_column_end", onResizeEnd);
107
109
  table.off("scroll", onScrollSticky);
108
110
  table.off(TABLE_EVENT_TYPE.BEFORE_UPDATE_OPTION, onBeforeUpdateOption);
111
+ table.off(TABLE_EVENT_TYPE.UPDATED, onUpdated);
109
112
  table.off(TABLE_EVENT_TYPE.CHANGE_CELL_VALUE, onCellValueChange);
110
113
  table.off(TABLE_EVENT_TYPE.CHANGE_CELL_VALUES, onCellValueChange);
111
114
  table.off(TABLE_EVENT_TYPE.CLICK_CELL, onEditTrigger);
@@ -118,6 +121,7 @@ const useTableVTableEvents = ({ lastRowSeriesHoverRef, onReady, rootRef, schedul
118
121
  tableRef.current = table;
119
122
  setSelectionVersion((version) => version + 1);
120
123
  table.__superadPalette = resolveTableCanvasPalette(token);
124
+ pinRowSeriesMaxWidth(table);
121
125
  patchFrozenShadowControls(table);
122
126
  syncFrozenColumnShadow();
123
127
  alignStickyGroupTitles(table, stickyGroupBackground);
@@ -1 +1 @@
1
- {"version":3,"file":"useTableVTableEvents.mjs","names":[],"sources":["../../../src/Table/react/useTableVTableEvents.ts"],"sourcesContent":["'use client';\n\nimport { type ListTable as ListTableInstance, TABLE_EVENT_TYPE } from '@visactor/vtable';\nimport type { Dispatch, RefObject, SetStateAction } from 'react';\n\nimport { alignStickyGroupTitles } from '../canvas/groupTitle';\nimport { refreshRowSeriesHover, refreshVisibleRowSeriesCells } from '../canvas/rowSeries';\nimport { resolveTableCanvasPalette, type TableToken } from '../canvas/theme';\nimport { TABLE_HEIGHT_GUTTER } from '../core/size';\nimport type { TableProps } from '../type';\nimport { patchFrozenShadowControls } from '../vtable/frozenShadow';\nimport { syncCascadeSelectionRanges } from '../vtable/seriesSelection';\nimport { getAllRowsHeight } from '../vtable/vtableAdapter';\n\ntype MutableRef<T> = { current: T };\n\nexport const useTableVTableEvents = ({\n lastRowSeriesHoverRef,\n onReady,\n rootRef,\n scheduleEditorSync,\n setA11yVersion,\n setMeasuredContentHeight,\n setResizeGuide,\n setSelectionVersion,\n stickyGroupBackground,\n syncFrozenColumnShadow,\n tableCleanupRef,\n tableRef,\n token,\n}: {\n lastRowSeriesHoverRef: MutableRef<number | null>;\n onReady?: TableProps['onReady'];\n rootRef: RefObject<HTMLDivElement | null>;\n scheduleEditorSync: () => void;\n setA11yVersion: Dispatch<SetStateAction<number>>;\n setMeasuredContentHeight: Dispatch<SetStateAction<number | null>>;\n setResizeGuide: Dispatch<SetStateAction<number | null>>;\n setSelectionVersion: Dispatch<SetStateAction<number>>;\n stickyGroupBackground: string;\n syncFrozenColumnShadow: () => void;\n tableCleanupRef: MutableRef<(() => void) | undefined>;\n tableRef: MutableRef<ListTableInstance | null>;\n token: TableToken;\n}) => {\n const handleReady = (instance: unknown, isInitial: boolean) => {\n const table = instance as ListTableInstance;\n const updateDomContainer = () => table.scenegraph.updateDomContainer();\n const onResize = (args: any) => {\n updateDomContainer();\n alignStickyGroupTitles(table, stickyGroupBackground);\n const host = rootRef.current;\n const el = table.getElement();\n if (host == null || el == null) return;\n const rect = table.getCellRelativeRect(args?.col ?? 0, 0);\n const hr = host.getBoundingClientRect();\n const tr = el.getBoundingClientRect();\n const guideLeft = rect.left + rect.width + tr.left - hr.left;\n setResizeGuide(Math.max(0, Math.min(guideLeft, host.clientWidth - 1)));\n };\n const onResizeEnd = () => {\n updateDomContainer();\n alignStickyGroupTitles(table, stickyGroupBackground);\n setResizeGuide(null);\n };\n\n const onSelectionChange = () => {\n lastRowSeriesHoverRef.current = null;\n setSelectionVersion((version) => version + 1);\n setA11yVersion((version) => version + 1);\n refreshVisibleRowSeriesCells(table);\n };\n const onEditTrigger = () => scheduleEditorSync();\n /**\n * 折叠 / 展开之后**容器高度必须重新问引擎**。外层容器的高度是 React 按 `records` 算的\n * (`computeTableHeight`),而折叠只改引擎内部状态:`records` 这个 prop 的身份和内容都没变,\n * 那个 memo 不会重算 —— 折起来时容器留一大片空白,展开时反过来把内容裁掉。\n *\n * 引擎发这个事件时行高**已经是新的**(实测:handler 里同步读 `getAllRowsHeight()` 就是\n * 折叠后的值),所以同步读,不用等下一帧。\n *\n * 这里不能改成「按数据重算」:树节点的 `hierarchyState` 引擎会写回记录,但分组标题行\n * (`groupBy` 生成的那些)的折叠状态只存在引擎的分组结构里,`records` 上没有。\n */\n const onHierarchyChange = () => {\n syncCascadeSelectionRanges(table);\n alignStickyGroupTitles(table, stickyGroupBackground);\n refreshVisibleRowSeriesCells(table);\n setA11yVersion((version) => version + 1);\n const measured = getAllRowsHeight(table);\n if (measured != null) setMeasuredContentHeight(measured + TABLE_HEIGHT_GUTTER);\n };\n const onLeaveTable = () => {\n const previous = lastRowSeriesHoverRef.current;\n lastRowSeriesHoverRef.current = null;\n if (previous == null || previous < 0) return;\n refreshRowSeriesHover(table, previous, -1);\n };\n const onScrollSticky = () => {\n alignStickyGroupTitles(table, stickyGroupBackground);\n };\n /**\n * `updateOption` 的第一件事是 `editorManager.cancelEdit()`,而它**只调 `exit()` + `onEnd()`、\n * 不写值**(提交走 `completeEdit()`)—— 正在输入的内容会被静默丢弃。\n *\n * 而 `updateOption` 在我们的用法下**几乎必然被调用**:react-vtable 用 vutils 的 `isEqual`\n * 比较前后 option,它的实现对任何**非纯对象**直接返回 false,而我们的列上挂着\n * `editor: new TextEditor()` 这类类实例 —— 内容完全相同的两个实例也判不等。所以只要使用方\n * 把 `columns` / `records` 内联传入(父组件每次渲染都换数组身份),每次渲染都会走一遍\n * `updateOption`。实测:父组件每 1.5s 重渲染一次时,刚打开的编辑器会在 2s 内被关掉;\n * 换成静态父组件则一直开着。\n *\n * VTable 在 `cancelEdit()` **之前**发这个事件,所以在这里补一次提交,把「静默丢弃」变成\n * 「正常结束」。\n */\n const onBeforeUpdateOption = () => {\n (table as ListTableInstance & { completeEditCell?: () => void }).completeEditCell?.();\n };\n /**\n * a11y 隐藏层每个 cell 现读 `table.getCellValue`,但只在 `a11yVersion` 自增时才重读。\n * 编辑提交走的是 `changeCellValue`,既不滚动也不改变选中 —— 不订阅它,读屏读到的会一直是\n * 改前的旧值(canvas 上已经是新值),要等下一次滚动或点选才自愈。\n */\n const onCellValueChange = () => {\n setA11yVersion((version) => version + 1);\n };\n\n tableCleanupRef.current?.();\n table.on('resize_column', onResize);\n table.on('resize_column_end', onResizeEnd);\n table.on('scroll', onScrollSticky);\n table.on(TABLE_EVENT_TYPE.BEFORE_UPDATE_OPTION, onBeforeUpdateOption);\n table.on(TABLE_EVENT_TYPE.CHANGE_CELL_VALUE, onCellValueChange);\n table.on(TABLE_EVENT_TYPE.CHANGE_CELL_VALUES, onCellValueChange);\n table.on(TABLE_EVENT_TYPE.CLICK_CELL, onEditTrigger);\n table.on(TABLE_EVENT_TYPE.DBLCLICK_CELL, onEditTrigger);\n table.on(TABLE_EVENT_TYPE.SELECTED_CHANGED, onSelectionChange);\n table.on(TABLE_EVENT_TYPE.SELECTED_CLEAR, onSelectionChange);\n table.on(TABLE_EVENT_TYPE.TREE_HIERARCHY_STATE_CHANGE, onHierarchyChange);\n table.on(TABLE_EVENT_TYPE.MOUSELEAVE_TABLE, onLeaveTable);\n tableCleanupRef.current = () => {\n table.off('resize_column', onResize);\n table.off('resize_column_end', onResizeEnd);\n table.off('scroll', onScrollSticky);\n table.off(TABLE_EVENT_TYPE.BEFORE_UPDATE_OPTION, onBeforeUpdateOption);\n table.off(TABLE_EVENT_TYPE.CHANGE_CELL_VALUE, onCellValueChange);\n table.off(TABLE_EVENT_TYPE.CHANGE_CELL_VALUES, onCellValueChange);\n table.off(TABLE_EVENT_TYPE.CLICK_CELL, onEditTrigger);\n table.off(TABLE_EVENT_TYPE.DBLCLICK_CELL, onEditTrigger);\n table.off(TABLE_EVENT_TYPE.SELECTED_CHANGED, onSelectionChange);\n table.off(TABLE_EVENT_TYPE.SELECTED_CLEAR, onSelectionChange);\n table.off(TABLE_EVENT_TYPE.TREE_HIERARCHY_STATE_CHANGE, onHierarchyChange);\n table.off(TABLE_EVENT_TYPE.MOUSELEAVE_TABLE, onLeaveTable);\n };\n tableRef.current = table;\n setSelectionVersion((version) => version + 1);\n (\n table as ListTableInstance & {\n __superadPalette?: ReturnType<typeof resolveTableCanvasPalette>;\n }\n ).__superadPalette = resolveTableCanvasPalette(token);\n patchFrozenShadowControls(table);\n syncFrozenColumnShadow();\n alignStickyGroupTitles(table, stickyGroupBackground);\n setA11yVersion((version) => version + 1);\n onReady?.(table, isInitial);\n };\n\n return { onReady: handleReady };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"useTableVTableEvents.mjs","names":[],"sources":["../../../src/Table/react/useTableVTableEvents.ts"],"sourcesContent":["'use client';\n\nimport { type ListTable as ListTableInstance, TABLE_EVENT_TYPE } from '@visactor/vtable';\nimport type { Dispatch, RefObject, SetStateAction } from 'react';\n\nimport { alignStickyGroupTitles } from '../canvas/groupTitle';\nimport { refreshRowSeriesHover, refreshVisibleRowSeriesCells } from '../canvas/rowSeries';\nimport { resolveTableCanvasPalette, type TableToken } from '../canvas/theme';\nimport { TABLE_HEIGHT_GUTTER } from '../core/size';\nimport type { TableProps } from '../type';\nimport { patchFrozenShadowControls } from '../vtable/frozenShadow';\nimport { syncCascadeSelectionRanges } from '../vtable/seriesSelection';\nimport { getAllRowsHeight, pinRowSeriesMaxWidth } from '../vtable/vtableAdapter';\n\ntype MutableRef<T> = { current: T };\n\nexport const useTableVTableEvents = ({\n lastRowSeriesHoverRef,\n onReady,\n rootRef,\n scheduleEditorSync,\n setA11yVersion,\n setMeasuredContentHeight,\n setResizeGuide,\n setSelectionVersion,\n stickyGroupBackground,\n syncFrozenColumnShadow,\n tableCleanupRef,\n tableRef,\n token,\n}: {\n lastRowSeriesHoverRef: MutableRef<number | null>;\n onReady?: TableProps['onReady'];\n rootRef: RefObject<HTMLDivElement | null>;\n scheduleEditorSync: () => void;\n setA11yVersion: Dispatch<SetStateAction<number>>;\n setMeasuredContentHeight: Dispatch<SetStateAction<number | null>>;\n setResizeGuide: Dispatch<SetStateAction<number | null>>;\n setSelectionVersion: Dispatch<SetStateAction<number>>;\n stickyGroupBackground: string;\n syncFrozenColumnShadow: () => void;\n tableCleanupRef: MutableRef<(() => void) | undefined>;\n tableRef: MutableRef<ListTableInstance | null>;\n token: TableToken;\n}) => {\n const handleReady = (instance: unknown, isInitial: boolean) => {\n const table = instance as ListTableInstance;\n const updateDomContainer = () => table.scenegraph.updateDomContainer();\n const onResize = (args: any) => {\n updateDomContainer();\n alignStickyGroupTitles(table, stickyGroupBackground);\n const host = rootRef.current;\n const el = table.getElement();\n if (host == null || el == null) return;\n const rect = table.getCellRelativeRect(args?.col ?? 0, 0);\n const hr = host.getBoundingClientRect();\n const tr = el.getBoundingClientRect();\n const guideLeft = rect.left + rect.width + tr.left - hr.left;\n setResizeGuide(Math.max(0, Math.min(guideLeft, host.clientWidth - 1)));\n };\n const onResizeEnd = () => {\n updateDomContainer();\n alignStickyGroupTitles(table, stickyGroupBackground);\n setResizeGuide(null);\n };\n\n const onSelectionChange = () => {\n lastRowSeriesHoverRef.current = null;\n setSelectionVersion((version) => version + 1);\n setA11yVersion((version) => version + 1);\n refreshVisibleRowSeriesCells(table);\n };\n const onEditTrigger = () => scheduleEditorSync();\n /**\n * 折叠 / 展开之后**容器高度必须重新问引擎**。外层容器的高度是 React 按 `records` 算的\n * (`computeTableHeight`),而折叠只改引擎内部状态:`records` 这个 prop 的身份和内容都没变,\n * 那个 memo 不会重算 —— 折起来时容器留一大片空白,展开时反过来把内容裁掉。\n *\n * 引擎发这个事件时行高**已经是新的**(实测:handler 里同步读 `getAllRowsHeight()` 就是\n * 折叠后的值),所以同步读,不用等下一帧。\n *\n * 这里不能改成「按数据重算」:树节点的 `hierarchyState` 引擎会写回记录,但分组标题行\n * (`groupBy` 生成的那些)的折叠状态只存在引擎的分组结构里,`records` 上没有。\n */\n const onHierarchyChange = () => {\n syncCascadeSelectionRanges(table);\n alignStickyGroupTitles(table, stickyGroupBackground);\n refreshVisibleRowSeriesCells(table);\n setA11yVersion((version) => version + 1);\n const measured = getAllRowsHeight(table);\n if (measured != null) setMeasuredContentHeight(measured + TABLE_HEIGHT_GUTTER);\n };\n const onLeaveTable = () => {\n const previous = lastRowSeriesHoverRef.current;\n lastRowSeriesHoverRef.current = null;\n if (previous == null || previous < 0) return;\n refreshRowSeriesHover(table, previous, -1);\n };\n const onScrollSticky = () => {\n alignStickyGroupTitles(table, stickyGroupBackground);\n };\n /**\n * `updateOption` 的第一件事是 `editorManager.cancelEdit()`,而它**只调 `exit()` + `onEnd()`、\n * 不写值**(提交走 `completeEdit()`)—— 正在输入的内容会被静默丢弃。\n *\n * 而 `updateOption` 在我们的用法下**几乎必然被调用**:react-vtable 用 vutils 的 `isEqual`\n * 比较前后 option,它的实现对任何**非纯对象**直接返回 false,而我们的列上挂着\n * `editor: new TextEditor()` 这类类实例 —— 内容完全相同的两个实例也判不等。所以只要使用方\n * 把 `columns` / `records` 内联传入(父组件每次渲染都换数组身份),每次渲染都会走一遍\n * `updateOption`。实测:父组件每 1.5s 重渲染一次时,刚打开的编辑器会在 2s 内被关掉;\n * 换成静态父组件则一直开着。\n *\n * VTable 在 `cancelEdit()` **之前**发这个事件,所以在这里补一次提交,把「静默丢弃」变成\n * 「正常结束」。\n */\n const onBeforeUpdateOption = () => {\n (table as ListTableInstance & { completeEditCell?: () => void }).completeEditCell?.();\n };\n const onUpdated = () => pinRowSeriesMaxWidth(table);\n /**\n * a11y 隐藏层每个 cell 现读 `table.getCellValue`,但只在 `a11yVersion` 自增时才重读。\n * 编辑提交走的是 `changeCellValue`,既不滚动也不改变选中 —— 不订阅它,读屏读到的会一直是\n * 改前的旧值(canvas 上已经是新值),要等下一次滚动或点选才自愈。\n */\n const onCellValueChange = () => {\n setA11yVersion((version) => version + 1);\n };\n\n tableCleanupRef.current?.();\n table.on('resize_column', onResize);\n table.on('resize_column_end', onResizeEnd);\n table.on('scroll', onScrollSticky);\n table.on(TABLE_EVENT_TYPE.BEFORE_UPDATE_OPTION, onBeforeUpdateOption);\n table.on(TABLE_EVENT_TYPE.UPDATED, onUpdated);\n table.on(TABLE_EVENT_TYPE.CHANGE_CELL_VALUE, onCellValueChange);\n table.on(TABLE_EVENT_TYPE.CHANGE_CELL_VALUES, onCellValueChange);\n table.on(TABLE_EVENT_TYPE.CLICK_CELL, onEditTrigger);\n table.on(TABLE_EVENT_TYPE.DBLCLICK_CELL, onEditTrigger);\n table.on(TABLE_EVENT_TYPE.SELECTED_CHANGED, onSelectionChange);\n table.on(TABLE_EVENT_TYPE.SELECTED_CLEAR, onSelectionChange);\n table.on(TABLE_EVENT_TYPE.TREE_HIERARCHY_STATE_CHANGE, onHierarchyChange);\n table.on(TABLE_EVENT_TYPE.MOUSELEAVE_TABLE, onLeaveTable);\n tableCleanupRef.current = () => {\n table.off('resize_column', onResize);\n table.off('resize_column_end', onResizeEnd);\n table.off('scroll', onScrollSticky);\n table.off(TABLE_EVENT_TYPE.BEFORE_UPDATE_OPTION, onBeforeUpdateOption);\n table.off(TABLE_EVENT_TYPE.UPDATED, onUpdated);\n table.off(TABLE_EVENT_TYPE.CHANGE_CELL_VALUE, onCellValueChange);\n table.off(TABLE_EVENT_TYPE.CHANGE_CELL_VALUES, onCellValueChange);\n table.off(TABLE_EVENT_TYPE.CLICK_CELL, onEditTrigger);\n table.off(TABLE_EVENT_TYPE.DBLCLICK_CELL, onEditTrigger);\n table.off(TABLE_EVENT_TYPE.SELECTED_CHANGED, onSelectionChange);\n table.off(TABLE_EVENT_TYPE.SELECTED_CLEAR, onSelectionChange);\n table.off(TABLE_EVENT_TYPE.TREE_HIERARCHY_STATE_CHANGE, onHierarchyChange);\n table.off(TABLE_EVENT_TYPE.MOUSELEAVE_TABLE, onLeaveTable);\n };\n tableRef.current = table;\n setSelectionVersion((version) => version + 1);\n (\n table as ListTableInstance & {\n __superadPalette?: ReturnType<typeof resolveTableCanvasPalette>;\n }\n ).__superadPalette = resolveTableCanvasPalette(token);\n pinRowSeriesMaxWidth(table);\n patchFrozenShadowControls(table);\n syncFrozenColumnShadow();\n alignStickyGroupTitles(table, stickyGroupBackground);\n setA11yVersion((version) => version + 1);\n onReady?.(table, isInitial);\n };\n\n return { onReady: handleReady };\n};\n"],"mappings":"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"}
@@ -69,6 +69,11 @@ interface TableColumn {
69
69
  * @default YYYY-MM-DD
70
70
  */
71
71
  format?: string;
72
+ /**
73
+ * Maximum column width. A numeric `width` also becomes the max when this is omitted,
74
+ * so `autoFillWidth` cannot stretch a fixed column (status chips, dates, …).
75
+ */
76
+ maxWidth?: number | string;
72
77
  /** Minimum column width. Defaults to a type-specific value. */
73
78
  minWidth?: number | string;
74
79
  /**
@@ -241,7 +246,9 @@ interface TableProps {
241
246
  */
242
247
  showRowSelection?: boolean;
243
248
  /**
244
- * Show vertical borders between columns, including the leading row index column.
249
+ * Show vertical borders between columns. The first column (including the row
250
+ * index) omits both edges, so there is no divider after it. The last column
251
+ * omits its outer right edge. Every other column shows vertical hairlines.
245
252
  * @default false
246
253
  */
247
254
  showVerticalBorder?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"type.mjs","names":[],"sources":["../../src/Table/type.ts"],"sourcesContent":["import type { ColumnDefine, ListTable, ListTableConstructorOptions } from '@visactor/vtable';\nimport type { CSSProperties, ReactNode, Ref } from 'react';\n\nexport type TableSize = 'large' | 'middle' | 'small';\n\nexport type TableHover = 'cell' | 'column' | 'cross' | 'none' | 'row';\n\nexport type TableCellType =\n | 'address'\n | 'array'\n | 'boolean'\n | 'checkbox'\n | 'currency'\n | 'date'\n | 'dateRange'\n | 'datetime'\n | 'email'\n | 'image'\n | 'json'\n | 'link'\n | 'number'\n | 'phone'\n | 'progress'\n | 'rating'\n | 'select'\n | 'tag'\n | 'text'\n | 'user';\n\nexport const TABLE_CELL_MIN_WIDTH: Record<TableCellType, number> = {\n address: 160,\n array: 104,\n boolean: 64,\n checkbox: 56,\n currency: 96,\n date: 112,\n dateRange: 216,\n datetime: 144,\n email: 144,\n image: 80,\n json: 120,\n link: 144,\n number: 80,\n phone: 128,\n progress: 112,\n rating: 96,\n select: 96,\n tag: 104,\n text: 80,\n user: 112,\n};\n\nexport const DEFAULT_TABLE_COLUMN_MIN_WIDTH = 80;\n\nexport const TABLE_ROW_HEIGHT: Record<TableSize, number> = {\n large: 44,\n middle: 36,\n small: 32,\n};\nexport const TABLE_HEADER_HEIGHT = 36;\n\nexport interface TableSelectOption {\n /**\n * SuperAd tag color name (`blue`, `green`, `purple`…) or a hex that snaps to that palette.\n */\n color?: string;\n label: string;\n value: string;\n}\n\nexport interface TableUser {\n avatar?: string;\n /**\n * 第二行说明(部门 / 公司 / 邮箱…)。只出现在**人员选择面板**里,单元格仍只画姓名。\n */\n description?: string;\n name: string;\n}\n\nexport interface TableAddress {\n city?: string;\n country?: string;\n postcode?: string;\n state?: string;\n street?: string;\n street2?: string;\n}\n\nexport interface TableRecord {\n [key: string]: unknown;\n children?: TableRecord[];\n hierarchyState?: 'collapse' | 'expand';\n}\n\n/** SuperAd column. Use `type` for bitable-style cells; raw VTable columns are also accepted. */\nexport interface TableColumn {\n /**\n * Currency symbol for `currency` cells.\n * @default ¥\n */\n currency?: string;\n /**\n * 状态 / 标签标题后跟的时长来源:记录里存日期范围的字段名(值本身是 `[起, 止]` 数组),\n * 或 `{ from, to }` 两个字段。时长按「N天 / N个月」现算,不写死在选项里。\n */\n duration?: string | { from: string; to: string };\n /**\n * Enable editor for this column.\n * @default inherits Table.editable\n */\n editable?: boolean;\n field: string;\n /**\n * Custom cell text, passed straight through to VTable. When set it wins over the\n * formatter this component would generate for `type`; the column still keeps its\n * editor, so `fieldFormat` and `editable` can be used together.\n */\n fieldFormat?: ColumnDefine['fieldFormat'];\n /**\n * Datetime display format, also used by `dateRange`.\n * @default YYYY-MM-DD\n */\n format?: string;\n /** Minimum column width. Defaults to a type-specific value. */\n minWidth?: number | string;\n /**\n * Allow multiple values for tag / select / user / email / phone.\n * @default false for select, true for tag / user / email / phone / array\n */\n multiple?: boolean;\n options?: TableSelectOption[];\n /**\n * Enable header sort.\n * @default false\n */\n sort?: boolean;\n /**\n * Optional VTable cell style. Useful for row-level highlighting in tree / grouped tables.\n */\n style?: ColumnDefine['style'];\n title?: string;\n /**\n * Mark this column as the tree expand column.\n * @default first column when Table.tree is true\n */\n tree?: boolean;\n /**\n * Cell type.\n *\n * `image` is preview-only (`clickToPreview`). It never mounts an editor, even\n * when `Table.editable` is true.\n * @default text\n */\n type?: TableCellType;\n /**\n * Suffix for `number` / `progress` cells, such as `%`.\n * `progress` defaults to `%`.\n */\n unit?: string;\n /** People directory for `user` picker. */\n users?: TableUser[];\n width?: number | string;\n}\n\nexport type TableColumnInput = ColumnDefine | TableColumn;\n\n/** SuperAd table, wrapping VisActor VTable ListTable. */\nexport interface TableProps {\n /**\n * Stretch columns to fill the container.\n * @default true\n */\n autoFillWidth?: boolean;\n /**\n * Shrink numeric height to content height.\n * @default true\n */\n autoHeight?: boolean;\n className?: string;\n /** SuperAd or VTable column definitions. */\n columns?: TableColumnInput[];\n /**\n * Enable editors on supported cell types. Click a cell to edit; select / tag cells open an option picker.\n * @default false\n */\n editable?: boolean;\n /**\n * Empty state copy.\n * @default No data\n */\n emptyText?: string;\n /**\n * Freeze the first N data columns. The row index / drag column stays fixed on\n * the left, so with it present the frozen columns start at the second column.\n *\n * VTable caps the frozen block at 80% of the table width. Rather than dropping\n * the whole freeze once the block would be wider, the block is trimmed to the\n * columns that fit — a narrow container freezes fewer columns instead of none.\n * Pass `option={{ unfreezeAllOnExceedsMaxWidth: true }}` to drop it entirely\n * instead. Reaching the column count (N plus the row index column) zeroes it too.\n * @default 0\n */\n frozenColCount?: number;\n /**\n * Group rows by field name.\n */\n groupBy?: string | string[];\n /**\n * Per-group title tag colors, keyed by group value. When provided, group\n * titles render as colored tags while keeping the standard collapse icon.\n */\n groupTitleColors?: Record<string, string | undefined>;\n /** Merged on top of the default group title style. */\n groupTitleStyle?: NonNullable<ListTableConstructorOptions['theme']>['groupTitleStyle'];\n /**\n * Header row height in pixels. Strings like `32px` are accepted.\n */\n headerRowHeight?: number | string;\n /**\n * Table height. When autoHeight is true and records exist, a numeric value shrinks to content height.\n * @default auto\n */\n height?: number | string;\n /**\n * Hover highlight mode.\n * @default row\n */\n hover?: TableHover;\n /**\n * Called after a column is moved with the column settings menu.\n */\n onColumnOrderChange?: (fields: string[]) => void;\n /**\n * Called after a column is shown or hidden with the column settings menu.\n */\n onColumnVisibilityChange?: (field: string, visible: boolean) => void;\n onReady?: (instance: ListTable, isInitial: boolean) => void;\n /**\n * Extra VTable options. First-class props win on conflict.\n *\n * Column resizing is off by default — pass `{ columnResizeMode: 'all' }`\n * (or `'header'` / `'body'`) to enable it. The column drag guide only appears\n * while it is on.\n *\n * `rowSeriesNumber` is merged onto the built-in row index column rather than\n * replacing it, so a custom `field` / `cellType` / `title` applies and the\n * checkbox state follows the `field` you give. One exception: while the row\n * selection column is on, its checkbox is drawn by this component on canvas, so\n * `cellType` / `headerType` are dropped — passing `checkbox` would make VTable\n * draw a second one in the same cell.\n */\n option?: ListTableConstructorOptions;\n /** Row data. Nested rows use `children` when `tree` is on. */\n records?: TableRecord[];\n ref?: Ref<ListTable>;\n /**\n * Reorder rows via VTable's native `rowSeriesNumber.dragOrder` handle.\n * Opt-in per table (`@default false`); other tables are unchanged unless they pass it.\n * Tree / grouped tables can turn it on, but VTable reorders visible rows and does not\n * keep parent–child or group-title semantics — dragging a parent will not move children\n * as a unit, and group title rows are not data records.\n * @default false\n */\n rowDrag?: boolean;\n /**\n * Body row height in pixels. Strings like `32px` are accepted.\n */\n rowHeight?: number | string;\n /**\n * Render extra actions in the selection status bar.\n *\n * `selectedRowIndexes` lists **visible** rows only. The \"已选 N 行\" count next to it\n * counts every checked record instead, so it can exceed `selectedRowIndexes.length`\n * when a group node is collapsed — collapsed rows are still selected, they just are\n * not on screen. Do not derive one from the other.\n */\n selectionActions?: (selectedRowIndexes: number[], clearSelection: () => void) => ReactNode;\n /**\n * Show the column settings menu button in the header.\n * @default true\n */\n showColumnSettings?: boolean;\n /**\n * @default true\n */\n showHeader?: boolean;\n /**\n * Show a leading row index column.\n * @default false\n */\n showRowIndex?: boolean;\n /**\n * Show the row selection checkbox column.\n * @default true\n */\n showRowSelection?: boolean;\n /**\n * Show vertical borders between columns, including the leading row index column.\n * @default false\n */\n showVerticalBorder?: boolean;\n /**\n * Row density.\n * @default middle\n */\n size?: TableSize;\n style?: CSSProperties;\n /** Merged on top of the SuperAd VTable theme. */\n theme?: ListTableConstructorOptions['theme'];\n /**\n * Enable tree rows. Records nest via `children`.\n * @default false\n */\n tree?: boolean;\n /**\n * Expanded tree depth. Use `Infinity` to expand all.\n * @default 1\n */\n treeExpandLevel?: number;\n /**\n * Indent per tree level.\n * @default 16\n */\n treeIndent?: number;\n /**\n * @default 100%\n */\n width?: number | string;\n}\n\nexport type { ColumnDefine, ColumnsDefine, ListTableConstructorOptions } from '@visactor/vtable';\nexport type TableTheme = NonNullable<ListTableConstructorOptions['theme']>;\n"],"mappings":";AA6BA,MAAa,uBAAsD;CACjE,SAAS;CACT,OAAO;CACP,SAAS;CACT,UAAU;CACV,UAAU;CACV,MAAM;CACN,WAAW;CACX,UAAU;CACV,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,QAAQ;CACR,OAAO;CACP,UAAU;CACV,QAAQ;CACR,QAAQ;CACR,KAAK;CACL,MAAM;CACN,MAAM;AACR;AAEA,MAAa,iCAAiC;AAE9C,MAAa,mBAA8C;CACzD,OAAO;CACP,QAAQ;CACR,OAAO;AACT;AACA,MAAa,sBAAsB"}
1
+ {"version":3,"file":"type.mjs","names":[],"sources":["../../src/Table/type.ts"],"sourcesContent":["import type { ColumnDefine, ListTable, ListTableConstructorOptions } from '@visactor/vtable';\nimport type { CSSProperties, ReactNode, Ref } from 'react';\n\nexport type TableSize = 'large' | 'middle' | 'small';\n\nexport type TableHover = 'cell' | 'column' | 'cross' | 'none' | 'row';\n\nexport type TableCellType =\n | 'address'\n | 'array'\n | 'boolean'\n | 'checkbox'\n | 'currency'\n | 'date'\n | 'dateRange'\n | 'datetime'\n | 'email'\n | 'image'\n | 'json'\n | 'link'\n | 'number'\n | 'phone'\n | 'progress'\n | 'rating'\n | 'select'\n | 'tag'\n | 'text'\n | 'user';\n\nexport const TABLE_CELL_MIN_WIDTH: Record<TableCellType, number> = {\n address: 160,\n array: 104,\n boolean: 64,\n checkbox: 56,\n currency: 96,\n date: 112,\n dateRange: 216,\n datetime: 144,\n email: 144,\n image: 80,\n json: 120,\n link: 144,\n number: 80,\n phone: 128,\n progress: 112,\n rating: 96,\n select: 96,\n tag: 104,\n text: 80,\n user: 112,\n};\n\nexport const DEFAULT_TABLE_COLUMN_MIN_WIDTH = 80;\n\nexport const TABLE_ROW_HEIGHT: Record<TableSize, number> = {\n large: 44,\n middle: 36,\n small: 32,\n};\nexport const TABLE_HEADER_HEIGHT = 36;\n\nexport interface TableSelectOption {\n /**\n * SuperAd tag color name (`blue`, `green`, `purple`…) or a hex that snaps to that palette.\n */\n color?: string;\n label: string;\n value: string;\n}\n\nexport interface TableUser {\n avatar?: string;\n /**\n * 第二行说明(部门 / 公司 / 邮箱…)。只出现在**人员选择面板**里,单元格仍只画姓名。\n */\n description?: string;\n name: string;\n}\n\nexport interface TableAddress {\n city?: string;\n country?: string;\n postcode?: string;\n state?: string;\n street?: string;\n street2?: string;\n}\n\nexport interface TableRecord {\n [key: string]: unknown;\n children?: TableRecord[];\n hierarchyState?: 'collapse' | 'expand';\n}\n\n/** SuperAd column. Use `type` for bitable-style cells; raw VTable columns are also accepted. */\nexport interface TableColumn {\n /**\n * Currency symbol for `currency` cells.\n * @default ¥\n */\n currency?: string;\n /**\n * 状态 / 标签标题后跟的时长来源:记录里存日期范围的字段名(值本身是 `[起, 止]` 数组),\n * 或 `{ from, to }` 两个字段。时长按「N天 / N个月」现算,不写死在选项里。\n */\n duration?: string | { from: string; to: string };\n /**\n * Enable editor for this column.\n * @default inherits Table.editable\n */\n editable?: boolean;\n field: string;\n /**\n * Custom cell text, passed straight through to VTable. When set it wins over the\n * formatter this component would generate for `type`; the column still keeps its\n * editor, so `fieldFormat` and `editable` can be used together.\n */\n fieldFormat?: ColumnDefine['fieldFormat'];\n /**\n * Datetime display format, also used by `dateRange`.\n * @default YYYY-MM-DD\n */\n format?: string;\n /**\n * Maximum column width. A numeric `width` also becomes the max when this is omitted,\n * so `autoFillWidth` cannot stretch a fixed column (status chips, dates, …).\n */\n maxWidth?: number | string;\n /** Minimum column width. Defaults to a type-specific value. */\n minWidth?: number | string;\n /**\n * Allow multiple values for tag / select / user / email / phone.\n * @default false for select, true for tag / user / email / phone / array\n */\n multiple?: boolean;\n options?: TableSelectOption[];\n /**\n * Enable header sort.\n * @default false\n */\n sort?: boolean;\n /**\n * Optional VTable cell style. Useful for row-level highlighting in tree / grouped tables.\n */\n style?: ColumnDefine['style'];\n title?: string;\n /**\n * Mark this column as the tree expand column.\n * @default first column when Table.tree is true\n */\n tree?: boolean;\n /**\n * Cell type.\n *\n * `image` is preview-only (`clickToPreview`). It never mounts an editor, even\n * when `Table.editable` is true.\n * @default text\n */\n type?: TableCellType;\n /**\n * Suffix for `number` / `progress` cells, such as `%`.\n * `progress` defaults to `%`.\n */\n unit?: string;\n /** People directory for `user` picker. */\n users?: TableUser[];\n width?: number | string;\n}\n\nexport type TableColumnInput = ColumnDefine | TableColumn;\n\n/** SuperAd table, wrapping VisActor VTable ListTable. */\nexport interface TableProps {\n /**\n * Stretch columns to fill the container.\n * @default true\n */\n autoFillWidth?: boolean;\n /**\n * Shrink numeric height to content height.\n * @default true\n */\n autoHeight?: boolean;\n className?: string;\n /** SuperAd or VTable column definitions. */\n columns?: TableColumnInput[];\n /**\n * Enable editors on supported cell types. Click a cell to edit; select / tag cells open an option picker.\n * @default false\n */\n editable?: boolean;\n /**\n * Empty state copy.\n * @default No data\n */\n emptyText?: string;\n /**\n * Freeze the first N data columns. The row index / drag column stays fixed on\n * the left, so with it present the frozen columns start at the second column.\n *\n * VTable caps the frozen block at 80% of the table width. Rather than dropping\n * the whole freeze once the block would be wider, the block is trimmed to the\n * columns that fit — a narrow container freezes fewer columns instead of none.\n * Pass `option={{ unfreezeAllOnExceedsMaxWidth: true }}` to drop it entirely\n * instead. Reaching the column count (N plus the row index column) zeroes it too.\n * @default 0\n */\n frozenColCount?: number;\n /**\n * Group rows by field name.\n */\n groupBy?: string | string[];\n /**\n * Per-group title tag colors, keyed by group value. When provided, group\n * titles render as colored tags while keeping the standard collapse icon.\n */\n groupTitleColors?: Record<string, string | undefined>;\n /** Merged on top of the default group title style. */\n groupTitleStyle?: NonNullable<ListTableConstructorOptions['theme']>['groupTitleStyle'];\n /**\n * Header row height in pixels. Strings like `32px` are accepted.\n */\n headerRowHeight?: number | string;\n /**\n * Table height. When autoHeight is true and records exist, a numeric value shrinks to content height.\n * @default auto\n */\n height?: number | string;\n /**\n * Hover highlight mode.\n * @default row\n */\n hover?: TableHover;\n /**\n * Called after a column is moved with the column settings menu.\n */\n onColumnOrderChange?: (fields: string[]) => void;\n /**\n * Called after a column is shown or hidden with the column settings menu.\n */\n onColumnVisibilityChange?: (field: string, visible: boolean) => void;\n onReady?: (instance: ListTable, isInitial: boolean) => void;\n /**\n * Extra VTable options. First-class props win on conflict.\n *\n * Column resizing is off by default — pass `{ columnResizeMode: 'all' }`\n * (or `'header'` / `'body'`) to enable it. The column drag guide only appears\n * while it is on.\n *\n * `rowSeriesNumber` is merged onto the built-in row index column rather than\n * replacing it, so a custom `field` / `cellType` / `title` applies and the\n * checkbox state follows the `field` you give. One exception: while the row\n * selection column is on, its checkbox is drawn by this component on canvas, so\n * `cellType` / `headerType` are dropped — passing `checkbox` would make VTable\n * draw a second one in the same cell.\n */\n option?: ListTableConstructorOptions;\n /** Row data. Nested rows use `children` when `tree` is on. */\n records?: TableRecord[];\n ref?: Ref<ListTable>;\n /**\n * Reorder rows via VTable's native `rowSeriesNumber.dragOrder` handle.\n * Opt-in per table (`@default false`); other tables are unchanged unless they pass it.\n * Tree / grouped tables can turn it on, but VTable reorders visible rows and does not\n * keep parent–child or group-title semantics — dragging a parent will not move children\n * as a unit, and group title rows are not data records.\n * @default false\n */\n rowDrag?: boolean;\n /**\n * Body row height in pixels. Strings like `32px` are accepted.\n */\n rowHeight?: number | string;\n /**\n * Render extra actions in the selection status bar.\n *\n * `selectedRowIndexes` lists **visible** rows only. The \"已选 N 行\" count next to it\n * counts every checked record instead, so it can exceed `selectedRowIndexes.length`\n * when a group node is collapsed — collapsed rows are still selected, they just are\n * not on screen. Do not derive one from the other.\n */\n selectionActions?: (selectedRowIndexes: number[], clearSelection: () => void) => ReactNode;\n /**\n * Show the column settings menu button in the header.\n * @default true\n */\n showColumnSettings?: boolean;\n /**\n * @default true\n */\n showHeader?: boolean;\n /**\n * Show a leading row index column.\n * @default false\n */\n showRowIndex?: boolean;\n /**\n * Show the row selection checkbox column.\n * @default true\n */\n showRowSelection?: boolean;\n /**\n * Show vertical borders between columns. The first column (including the row\n * index) omits both edges, so there is no divider after it. The last column\n * omits its outer right edge. Every other column shows vertical hairlines.\n * @default false\n */\n showVerticalBorder?: boolean;\n /**\n * Row density.\n * @default middle\n */\n size?: TableSize;\n style?: CSSProperties;\n /** Merged on top of the SuperAd VTable theme. */\n theme?: ListTableConstructorOptions['theme'];\n /**\n * Enable tree rows. Records nest via `children`.\n * @default false\n */\n tree?: boolean;\n /**\n * Expanded tree depth. Use `Infinity` to expand all.\n * @default 1\n */\n treeExpandLevel?: number;\n /**\n * Indent per tree level.\n * @default 16\n */\n treeIndent?: number;\n /**\n * @default 100%\n */\n width?: number | string;\n}\n\nexport type { ColumnDefine, ColumnsDefine, ListTableConstructorOptions } from '@visactor/vtable';\nexport type TableTheme = NonNullable<ListTableConstructorOptions['theme']>;\n"],"mappings":";AA6BA,MAAa,uBAAsD;CACjE,SAAS;CACT,OAAO;CACP,SAAS;CACT,UAAU;CACV,UAAU;CACV,MAAM;CACN,WAAW;CACX,UAAU;CACV,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,QAAQ;CACR,OAAO;CACP,UAAU;CACV,QAAQ;CACR,QAAQ;CACR,KAAK;CACL,MAAM;CACN,MAAM;AACR;AAEA,MAAa,iCAAiC;AAE9C,MAAa,mBAA8C;CACzD,OAAO;CACP,QAAQ;CACR,OAAO;AACT;AACA,MAAa,sBAAsB"}
@@ -287,11 +287,14 @@ const resolveColumnMinWidth = (column, treeField) => {
287
287
  return TABLE_CELL_MIN_WIDTH[column.type ?? "text"];
288
288
  };
289
289
  const withMinWidth = (column, minWidth) => {
290
+ const width = column.width;
291
+ const currentMin = column.minWidth ?? minWidth;
290
292
  const next = {
291
293
  ...column,
292
- minWidth: column.minWidth ?? minWidth
294
+ minWidth: currentMin
293
295
  };
294
296
  const children = column.columns;
297
+ if (next.maxWidth == null && typeof width === "number") next.maxWidth = Math.max(width, typeof currentMin === "number" ? currentMin : width);
295
298
  if (Array.isArray(children)) next.columns = children.map((child) => withMinWidth(child, 80));
296
299
  return next;
297
300
  };
@@ -372,6 +375,7 @@ const compileColumns = (columns, ctx = {}) => {
372
375
  withBlankMutedColor(compiled, tableColumn, ctx.token);
373
376
  withEditHintPadding(compiled);
374
377
  if (compiled.editor != null) compiled.superadEditable = true;
378
+ if (tableColumn.maxWidth != null) compiled.maxWidth = tableColumn.maxWidth;
375
379
  return withMinWidth(compiled, resolveColumnMinWidth(tableColumn, treeField));
376
380
  });
377
381
  };
@@ -1 +1 @@
1
- {"version":3,"file":"columns.mjs","names":[],"sources":["../../../src/Table/vtable/columns.ts"],"sourcesContent":["import type { ColumnDefine, ColumnsDefine } from '@visactor/vtable';\n\nimport {\n createArrayLayout,\n createLinkLayout,\n createTagLayout,\n createUserLayout,\n} from '../canvas/chipPainters';\nimport {\n createBooleanLayout,\n createCheckboxLayout,\n createRatingLayout,\n} from '../canvas/controlPainters';\nimport { EDIT_HINT_GUTTER } from '../canvas/paintUtils';\nimport { createCanvasTextLayout, createCurrencyLayout } from '../canvas/textPainters';\nimport { TABLE_CHROME, type TableToken } from '../canvas/theme';\nimport {\n emptyCellLabel,\n formatAddress,\n formatDateRange,\n formatDateTime,\n formatJson,\n formatNumber,\n isEmptyCellValue,\n isGroupTitleRecord,\n type LinkKind,\n} from '../core/cellValue';\nimport { type TableI18n, tableText } from '../core/tableI18n';\nimport {\n AddressEditor,\n ArrayEditor,\n DateRangeEditor,\n DateTimeEditor,\n JsonEditor,\n NumberEditor,\n OptionEditor,\n TextEditor,\n UserEditor,\n} from '../react/editors';\nimport {\n DEFAULT_TABLE_COLUMN_MIN_WIDTH,\n TABLE_CELL_MIN_WIDTH,\n type TableCellType,\n type TableColumn,\n type TableColumnInput,\n type TableSelectOption,\n} from '../type';\n\nexport interface CompileColumnsContext {\n editable?: boolean;\n groupedTree?: boolean;\n i18n?: TableI18n;\n token?: TableToken;\n tree?: boolean;\n}\n\nconst isGroupColumn = (column: TableColumnInput): boolean =>\n 'columns' in column && Array.isArray((column as { columns?: unknown }).columns);\n\n/**\n * 使用方是否要这一列**整列交回 VTable**。\n *\n * 判据只看「这列的渲染方式由 VTable 原生负责」的信号:`cellType` / `customRender` /\n * `customLayout` / `headerType` / 分组子列。\n *\n * **`fieldFormat` **不在**这里**。它只是自定义显示,是极普通的写法,而我们照样能编译这一列 ——\n * 曾经把它也算作「原生列」,代价是整列被原样返回、**不挂编辑器**:只是加了个 `fieldFormat`,\n * 列就静默失去编辑能力(树形表格 demo 的前三列连同树列本身都中招)。\n */\nconst isRawVTableColumn = (column: TableColumnInput): column is ColumnDefine =>\n isGroupColumn(column) ||\n 'cellType' in column ||\n 'customRender' in column ||\n 'customLayout' in column ||\n 'headerType' in column;\n\nconst canEdit = (column: TableColumn, editable?: boolean) => column.editable ?? editable ?? false;\n\nconst fieldFormatter =\n (\n field: string,\n format: (value: unknown) => string,\n empty?: string,\n i18n?: TableI18n,\n ): ((record: Record<string, unknown>) => string) =>\n (record) => {\n if (isGroupTitleRecord(record)) return '';\n const text = format(record == null ? undefined : record[field]);\n\n if (text.trim()) return text;\n if (!empty) return '';\n return i18n\n ? tableText({ __superadI18n: i18n }, 'setField', { title: empty })\n : emptyCellLabel(empty);\n };\n\n/** Kinds that toggle in place when their cell is clicked. */\ntype SuperAdToggleKind = 'boolean' | 'checkbox' | 'rating';\n\n/**\n * Every `superadKind` marker value written onto a compiled VTable column.\n *\n * `array` / `select` / `tag` / `user` 不参与点击切换,标出来是给 **a11y 隐藏层与剪贴板**\n * 用的:这几类的显示文本由 painter 从 `options` / 姓名 / 分隔符映射出来,而 `getCellValue`\n * 给的是记录原始值(读屏于是念 `done` 而不是「已完成」,对象值更是一串 `[object Object]`)。\n */\nexport type SuperAdCellKind = SuperAdToggleKind | 'array' | 'link' | 'select' | 'tag' | 'user';\n\n/** Markers read back by `interaction.ts` via `getBodyColumnDefine`. */\nexport interface SuperAdCellMarkers {\n /** 状态标题后跟的时长来源(见 `TableColumn.duration`),供悬停浮层与画布用同一份预留。 */\n superadDuration?: string | { from: string; to: string };\n superadEditable?: boolean;\n superadKind?: SuperAdCellKind;\n superadLinkKind?: LinkKind;\n /** 选择项,供 a11y / 剪贴板把原始值还原成画面上的标签(见 `withOptions`)。 */\n superadOptions?: TableSelectOption[];\n}\n\nconst withCellKind = (\n define: ColumnDefine,\n kind: SuperAdToggleKind,\n editable: boolean,\n): ColumnDefine => {\n const next = define as ColumnDefine & SuperAdCellMarkers;\n next.superadEditable = editable;\n next.superadKind = kind;\n return next;\n};\n\n/** 标 `superadKind`,并带上是否可编辑(画布铅笔槽 / 悬停浮层要读)。 */\nconst withTextKind = (\n define: ColumnDefine,\n kind: 'array' | 'user',\n editable: boolean,\n): ColumnDefine =>\n Object.assign(define, {\n superadEditable: editable,\n superadKind: kind,\n } as Partial<SuperAdCellMarkers>);\n\n/**\n * 把选择项挂到编译结果上。\n *\n * painter 是把 `options` 闭包进去的,编译后的列上只剩一个 `customLayout` 函数 —— 于是画布上\n * 是「已完成」、而**按列定义取值的地方**(a11y 隐藏层、剪贴板桥)只能拿到记录里的原始值\n * `done`。屏幕阅读器因此朗读英文键。把 `options` 一并留在列上,这些地方才能解析出同一个标签。\n */\nconst withOptions = (\n define: ColumnDefine,\n options: TableSelectOption[],\n kind: 'select' | 'tag',\n editable: boolean,\n duration?: string | { from: string; to: string },\n): ColumnDefine =>\n Object.assign(define, {\n superadDuration: duration,\n superadEditable: editable,\n superadKind: kind,\n superadOptions: options,\n } as Partial<SuperAdCellMarkers>);\n\nconst withLinkKind = (\n define: ColumnDefine,\n linkKind: LinkKind,\n editable: boolean,\n): ColumnDefine => {\n const next = define as ColumnDefine & SuperAdCellMarkers;\n next.superadEditable = editable;\n next.superadKind = 'link';\n next.superadLinkKind = linkKind;\n return next;\n};\n\nconst compileSuperAdColumn = (\n column: TableColumn,\n ctx: CompileColumnsContext,\n treeField?: string,\n): ColumnDefine => {\n const { currency = '¥', field, format, options = [], title, users = [], width } = column;\n const type: TableCellType = column.type ?? 'text';\n const editable = canEdit(column, ctx.editable);\n const empty = editable ? title : undefined;\n const multiple = column.multiple ?? ['array', 'email', 'phone', 'tag', 'user'].includes(type);\n const tree = field === treeField || column.tree ? true : undefined;\n const sort = column.sort || undefined;\n const token = ctx.token;\n const useCanvasTextLayout = Boolean(ctx.groupedTree) && tree !== true;\n\n if (type === 'checkbox') {\n return withCellKind(\n {\n cellType: 'text',\n customLayout: createCheckboxLayout(),\n field,\n sort,\n title,\n tree,\n width,\n },\n 'checkbox',\n editable,\n );\n }\n\n if (type === 'boolean') {\n return withCellKind(\n {\n cellType: 'text',\n customLayout: createBooleanLayout(editable),\n field,\n sort,\n title,\n tree,\n width,\n },\n 'boolean',\n editable,\n );\n }\n\n if (type === 'rating') {\n return withCellKind(\n {\n cellType: 'text',\n customLayout: createRatingLayout(),\n field,\n sort,\n title,\n tree,\n width,\n },\n 'rating',\n editable,\n );\n }\n\n if (type === 'array') {\n return withTextKind(\n {\n cellType: 'text',\n customLayout: createArrayLayout(empty),\n editor: editable ? new ArrayEditor() : undefined,\n field,\n sort,\n title,\n tree,\n width,\n },\n 'array',\n editable,\n );\n }\n\n if (type === 'email' || type === 'phone' || type === 'link') {\n return withLinkKind(\n {\n cellType: 'text',\n customLayout: createLinkLayout(type, empty),\n editor: editable ? (multiple ? new ArrayEditor() : new TextEditor()) : undefined,\n field,\n sort,\n title,\n tree,\n width,\n },\n type,\n editable,\n );\n }\n\n if (type === 'currency') {\n return {\n cellType: 'text',\n customLayout: createCurrencyLayout(currency, empty),\n editor: editable ? new NumberEditor({ prefix: currency }) : undefined,\n field,\n sort,\n title,\n tree,\n width,\n };\n }\n\n if (type === 'json') {\n return {\n cellType: 'text',\n ...(useCanvasTextLayout ? { customLayout: createCanvasTextLayout() } : null),\n editor: editable ? new JsonEditor() : undefined,\n field,\n fieldFormat: fieldFormatter(field, formatJson, empty, ctx.i18n),\n sort,\n title,\n tree,\n width,\n };\n }\n\n if (type === 'address') {\n return {\n cellType: 'text',\n ...(useCanvasTextLayout ? { customLayout: createCanvasTextLayout() } : null),\n editor: editable ? new AddressEditor() : undefined,\n field,\n fieldFormat: fieldFormatter(field, formatAddress, empty, ctx.i18n),\n sort,\n title,\n tree,\n width,\n };\n }\n\n if (type === 'image') {\n return {\n cellType: 'image',\n clickToPreview: true,\n field,\n imageAutoSizing: false,\n keepAspectRatio: true,\n style: { margin: 4 },\n title,\n tree,\n width,\n };\n }\n\n if (type === 'progress') {\n const unit = column.unit ?? '%';\n return {\n cellType: 'progressbar',\n editor: editable ? new NumberEditor({ max: 100, min: 0, suffix: unit }) : undefined,\n field,\n // 走 fieldFormatter:它有分组标题行与 record == null 的守卫,其它类型的 fieldFormat\n // 都从这里出。直接 `record[field]` 会在分组标题行上抛(record 可能为空)。\n fieldFormat: fieldFormatter(field, (value) => formatNumber(value, unit)),\n max: 100,\n min: 0,\n sort,\n style: {\n barBgColor: token?.colorFillSecondary ?? '#f5f5f5',\n barBottom: 10,\n barColor: (args) => {\n const value = Number(args.dataValue ?? args.value);\n if (Number.isFinite(value) && value >= 100)\n return token?.colorSuccess ?? TABLE_CHROME.success;\n return token?.colorInfo ?? TABLE_CHROME.accent;\n },\n barHeight: 6,\n // 走 token,否则暗色模式下是近黑色压在同为暗色的容器上。\n color: token?.colorText ?? TABLE_CHROME.text,\n fontSize: 12,\n textAlign: 'right',\n },\n title,\n tree,\n width,\n };\n }\n\n if (type === 'number') {\n const formatValue = (value: unknown) => formatNumber(value, column.unit);\n return {\n cellType: 'text',\n ...(useCanvasTextLayout ? { customLayout: createCanvasTextLayout() } : null),\n editor: editable ? new NumberEditor({ suffix: column.unit }) : undefined,\n field,\n fieldFormat: fieldFormatter(field, formatValue, empty, ctx.i18n),\n sort,\n title,\n tree,\n width,\n };\n }\n\n if (type === 'date' || type === 'datetime') {\n const pattern = format ?? (type === 'datetime' ? 'YYYY-MM-DD HH:mm' : 'YYYY-MM-DD');\n return {\n cellType: 'text',\n ...(useCanvasTextLayout ? { customLayout: createCanvasTextLayout() } : null),\n editor: editable ? new DateTimeEditor(pattern.includes('HH')) : undefined,\n field,\n fieldFormat: fieldFormatter(\n field,\n (value) => formatDateTime(value, pattern),\n empty,\n ctx.i18n,\n ),\n sort,\n title,\n tree,\n width,\n };\n }\n\n if (type === 'dateRange') {\n const pattern = format ?? 'YYYY-MM-DD';\n return {\n cellType: 'text',\n ...(useCanvasTextLayout ? { customLayout: createCanvasTextLayout() } : null),\n editor: editable ? new DateRangeEditor(pattern) : undefined,\n field,\n fieldFormat: fieldFormatter(\n field,\n (value) => formatDateRange(value, pattern),\n empty,\n ctx.i18n,\n ),\n sort,\n title,\n tree,\n width,\n };\n }\n\n if (type === 'tag' || type === 'select') {\n return withOptions(\n {\n cellType: 'text',\n customLayout: createTagLayout(options, empty, column.duration),\n editor: editable ? new OptionEditor(options, multiple) : undefined,\n field,\n sort,\n title,\n tree,\n width,\n },\n options,\n type,\n editable,\n column.duration,\n );\n }\n\n if (type === 'user') {\n return withTextKind(\n {\n cellType: 'text',\n customLayout: createUserLayout(empty),\n editor: editable ? new UserEditor(users, multiple) : undefined,\n field,\n sort,\n title,\n tree,\n width,\n },\n 'user',\n editable,\n );\n }\n\n return {\n cellType: 'text',\n ...(useCanvasTextLayout ? { customLayout: createCanvasTextLayout() } : null),\n editor: editable ? new TextEditor() : undefined,\n field,\n fieldFormat: fieldFormatter(\n field,\n (value) => (value == null ? '' : String(value)),\n empty,\n ctx.i18n,\n ),\n sort,\n style: tree ? { color: token?.colorText || TABLE_CHROME.text, fontSize: 14 } : undefined,\n title,\n tree,\n width,\n };\n};\n\nconst resolveTreeField = (columns: TableColumnInput[], tree?: boolean): string | undefined => {\n const marked = columns.find(\n (column) => !isRawVTableColumn(column) && 'tree' in column && column.tree,\n ) as TableColumn | undefined;\n if (marked?.field) return marked.field;\n if (!tree) return undefined;\n const first = columns.find((column) => !isRawVTableColumn(column)) as TableColumn | undefined;\n return first?.field;\n};\n\nconst resolveColumnMinWidth = (column: TableColumn, treeField?: string): number | string => {\n if (column.minWidth != null) return column.minWidth;\n if (column.tree || column.field === treeField) return 160;\n return TABLE_CELL_MIN_WIDTH[column.type ?? 'text'];\n};\n\nconst withMinWidth = (column: ColumnDefine, minWidth: number | string): ColumnDefine => {\n const next = {\n ...column,\n minWidth: (column as { minWidth?: number | string }).minWidth ?? minWidth,\n } as ColumnDefine & { columns?: ColumnDefine[] };\n const children = (column as { columns?: ColumnDefine[] }).columns;\n\n if (Array.isArray(children)) {\n next.columns = children.map((child) => withMinWidth(child, DEFAULT_TABLE_COLUMN_MIN_WIDTH));\n }\n\n return next;\n};\n\nexport const withDefaultColumnMinWidths = (columns?: ColumnsDefine): ColumnsDefine | undefined =>\n columns?.map((column) => withMinWidth(column, DEFAULT_TABLE_COLUMN_MIN_WIDTH));\n\n/**\n * 空值时把文字压成弱色 —— 与 canvas 那条路径(`createEmptyCellText`)对齐。\n *\n * 原生渲染的列(有 `fieldFormat`、没有 `customLayout`)用的是列样式,不区分空值就会同一张表里\n * 「设置 名称」深、「设置 状态」浅。json / address / progress / number / date 这几个分支各写各的\n * `fieldFormat`,所以在这里一处收口。\n *\n * 两个必须注意的点:\n * - 判空看**原始值**:格式化后的文本在空值时正好是占位文案(「设置 X」),拿它比会误判。\n * - 使用方给了 `fieldFormat` 时**不介入**:显示值来自那个函数、未必是 `field`。树表的 department\n * 列就是这样(值取 `rec.department ?? rec.group ?? rec.name`,而记录里 `group` 是空的),\n * 按 `field` 判空会把整列错涂成弱色。\n */\nconst withBlankMutedColor = (\n define: ColumnDefine,\n column: TableColumn,\n token?: TableToken,\n): void => {\n if (column.fieldFormat != null || define.fieldFormat == null || define.customLayout != null)\n return;\n\n const base = define.style as\n ((args: unknown) => Record<string, unknown>) | Record<string, unknown> | undefined;\n\n define.style = ((args: { col: number; row: number; table?: unknown }) => {\n const table = args.table as\n | { getCellOriginRecord?: (col: number, row: number) => Record<string, unknown> | undefined }\n | undefined;\n const record = table?.getCellOriginRecord?.(args.col, args.row);\n const blank = record != null && isEmptyCellValue(record[column.field]);\n const inherited = typeof base === 'function' ? base(args) : (base ?? {});\n\n return {\n ...inherited,\n color: blank\n ? (token?.colorTextTertiary ?? TABLE_CHROME.textMuted)\n : ((inherited as { color?: string }).color ?? token?.colorText ?? TABLE_CHROME.text),\n };\n }) as ColumnDefine['style'];\n};\n\n/**\n * 原生渲染(无 customLayout)且**真有编辑器**的列:右侧 padding 多留铅笔槽。\n *\n * 判据必须与 `resolveEditHint`(看 `getEditor`)对齐 —— 只看 `canEdit` 会给 image 这类\n * 「表可编辑但本列没有 editor」的列白白让出 28px,进度条 / 封面被挤窄却永远不出现铅笔。\n * customLayout 那条路径由各 painter 自己减 `EDIT_HINT_GUTTER`,这里再加会双倍。\n */\nconst withEditHintPadding = (define: ColumnDefine): void => {\n if (define.customLayout != null || define.editor == null) return;\n\n const base = define.style as\n ((args: unknown) => Record<string, unknown>) | Record<string, unknown> | undefined;\n\n define.style = ((args: unknown) => {\n const inherited = typeof base === 'function' ? base(args) : (base ?? {});\n const padding = (inherited as { padding?: number | (number | null)[] }).padding;\n let next: [number, number, number, number];\n\n if (Array.isArray(padding)) {\n next = [\n Number(padding[0] ?? 0),\n Number(padding[1] ?? 0) + EDIT_HINT_GUTTER,\n Number(padding[2] ?? 0),\n Number(padding[3] ?? 0),\n ];\n } else if (typeof padding === 'number') {\n next = [padding, padding + EDIT_HINT_GUTTER, padding, padding];\n } else {\n next = [0, TABLE_CHROME.paddingX + EDIT_HINT_GUTTER, 0, TABLE_CHROME.paddingX];\n }\n\n return { ...inherited, padding: next };\n }) as ColumnDefine['style'];\n};\n\nexport const compileColumns = (\n columns: TableColumnInput[] | undefined,\n ctx: CompileColumnsContext = {},\n): ColumnsDefine | undefined => {\n if (!columns) return undefined;\n const treeField = resolveTreeField(columns, ctx.tree);\n\n return columns.map((column) => {\n if (isRawVTableColumn(column)) {\n return withMinWidth(column, DEFAULT_TABLE_COLUMN_MIN_WIDTH);\n }\n\n const tableColumn = column as TableColumn;\n const compiled = compileSuperAdColumn(tableColumn, ctx, treeField);\n if (tableColumn.style != null) compiled.style = tableColumn.style;\n // 使用方给的 `fieldFormat` 是这一列显示的唯一真相,压过按 `type` 生成的格式化器 ——\n // 否则「带 fieldFormat 的列现在会被编译」这条改动会把它们的显示改掉。\n if (tableColumn.fieldFormat != null) compiled.fieldFormat = tableColumn.fieldFormat;\n withBlankMutedColor(compiled, tableColumn, ctx.token);\n withEditHintPadding(compiled);\n // customLayout 列的铅笔槽读 `superadEditable`,不走上面的 padding。chip / 开关列各自\n // 已经打过;currency 与 groupedTree 下的文本 / 日期 / JSON 一直漏 —— CellTypes 的预算列\n // 和 Group demo 的日期列就会金额 / 日期顶进铅笔。有 editor 就打,与 resolveEditHint 同口径。\n if (compiled.editor != null) {\n (compiled as ColumnDefine & SuperAdCellMarkers).superadEditable = true;\n }\n return withMinWidth(compiled, resolveColumnMinWidth(tableColumn, treeField));\n });\n};\n"],"mappings":";;;;;;;;;;AAwDA,MAAM,iBAAiB,WACrB,aAAa,UAAU,MAAM,QAAS,OAAiC,OAAO;;;;;;;;;;;AAYhF,MAAM,qBAAqB,WACzB,cAAc,MAAM,KACpB,cAAc,UACd,kBAAkB,UAClB,kBAAkB,UAClB,gBAAgB;AAElB,MAAM,WAAW,QAAqB,aAAuB,OAAO,YAAY,YAAY;AAE5F,MAAM,kBAEF,OACA,QACA,OACA,UAED,WAAW;CACV,IAAI,mBAAmB,MAAM,GAAG,OAAO;CACvC,MAAM,OAAO,OAAO,UAAU,OAAO,KAAA,IAAY,OAAO,MAAM;CAE9D,IAAI,KAAK,KAAK,GAAG,OAAO;CACxB,IAAI,CAAC,OAAO,OAAO;CACnB,OAAO,OACH,UAAU,EAAE,eAAe,KAAK,GAAG,YAAY,EAAE,OAAO,MAAM,CAAC,IAC/D,eAAe,KAAK;AAC1B;AAyBF,MAAM,gBACJ,QACA,MACA,aACiB;CACjB,MAAM,OAAO;CACb,KAAK,kBAAkB;CACvB,KAAK,cAAc;CACnB,OAAO;AACT;;AAGA,MAAM,gBACJ,QACA,MACA,aAEA,OAAO,OAAO,QAAQ;CACpB,iBAAiB;CACjB,aAAa;AACf,CAAgC;;;;;;;;AASlC,MAAM,eACJ,QACA,SACA,MACA,UACA,aAEA,OAAO,OAAO,QAAQ;CACpB,iBAAiB;CACjB,iBAAiB;CACjB,aAAa;CACb,gBAAgB;AAClB,CAAgC;AAElC,MAAM,gBACJ,QACA,UACA,aACiB;CACjB,MAAM,OAAO;CACb,KAAK,kBAAkB;CACvB,KAAK,cAAc;CACnB,KAAK,kBAAkB;CACvB,OAAO;AACT;AAEA,MAAM,wBACJ,QACA,KACA,cACiB;CACjB,MAAM,EAAE,WAAW,KAAK,OAAO,QAAQ,UAAU,CAAC,GAAG,OAAO,QAAQ,CAAC,GAAG,UAAU;CAClF,MAAM,OAAsB,OAAO,QAAQ;CAC3C,MAAM,WAAW,QAAQ,QAAQ,IAAI,QAAQ;CAC7C,MAAM,QAAQ,WAAW,QAAQ,KAAA;CACjC,MAAM,WAAW,OAAO,YAAY;EAAC;EAAS;EAAS;EAAS;EAAO;CAAM,CAAC,CAAC,SAAS,IAAI;CAC5F,MAAM,OAAO,UAAU,aAAa,OAAO,OAAO,OAAO,KAAA;CACzD,MAAM,OAAO,OAAO,QAAQ,KAAA;CAC5B,MAAM,QAAQ,IAAI;CAClB,MAAM,sBAAsB,QAAQ,IAAI,WAAW,KAAK,SAAS;CAEjE,IAAI,SAAS,YACX,OAAO,aACL;EACE,UAAU;EACV,cAAc,qBAAqB;EACnC;EACA;EACA;EACA;EACA;CACF,GACA,YACA,QACF;CAGF,IAAI,SAAS,WACX,OAAO,aACL;EACE,UAAU;EACV,cAAc,oBAAoB,QAAQ;EAC1C;EACA;EACA;EACA;EACA;CACF,GACA,WACA,QACF;CAGF,IAAI,SAAS,UACX,OAAO,aACL;EACE,UAAU;EACV,cAAc,mBAAmB;EACjC;EACA;EACA;EACA;EACA;CACF,GACA,UACA,QACF;CAGF,IAAI,SAAS,SACX,OAAO,aACL;EACE,UAAU;EACV,cAAc,kBAAkB,KAAK;EACrC,QAAQ,WAAW,IAAI,YAAY,IAAI,KAAA;EACvC;EACA;EACA;EACA;EACA;CACF,GACA,SACA,QACF;CAGF,IAAI,SAAS,WAAW,SAAS,WAAW,SAAS,QACnD,OAAO,aACL;EACE,UAAU;EACV,cAAc,iBAAiB,MAAM,KAAK;EAC1C,QAAQ,WAAY,WAAW,IAAI,YAAY,IAAI,IAAI,WAAW,IAAK,KAAA;EACvE;EACA;EACA;EACA;EACA;CACF,GACA,MACA,QACF;CAGF,IAAI,SAAS,YACX,OAAO;EACL,UAAU;EACV,cAAc,qBAAqB,UAAU,KAAK;EAClD,QAAQ,WAAW,IAAI,aAAa,EAAE,QAAQ,SAAS,CAAC,IAAI,KAAA;EAC5D;EACA;EACA;EACA;EACA;CACF;CAGF,IAAI,SAAS,QACX,OAAO;EACL,UAAU;EACV,GAAI,sBAAsB,EAAE,cAAc,uBAAuB,EAAE,IAAI;EACvE,QAAQ,WAAW,IAAI,WAAW,IAAI,KAAA;EACtC;EACA,aAAa,eAAe,OAAO,YAAY,OAAO,IAAI,IAAI;EAC9D;EACA;EACA;EACA;CACF;CAGF,IAAI,SAAS,WACX,OAAO;EACL,UAAU;EACV,GAAI,sBAAsB,EAAE,cAAc,uBAAuB,EAAE,IAAI;EACvE,QAAQ,WAAW,IAAI,cAAc,IAAI,KAAA;EACzC;EACA,aAAa,eAAe,OAAO,eAAe,OAAO,IAAI,IAAI;EACjE;EACA;EACA;EACA;CACF;CAGF,IAAI,SAAS,SACX,OAAO;EACL,UAAU;EACV,gBAAgB;EAChB;EACA,iBAAiB;EACjB,iBAAiB;EACjB,OAAO,EAAE,QAAQ,EAAE;EACnB;EACA;EACA;CACF;CAGF,IAAI,SAAS,YAAY;EACvB,MAAM,OAAO,OAAO,QAAQ;EAC5B,OAAO;GACL,UAAU;GACV,QAAQ,WAAW,IAAI,aAAa;IAAE,KAAK;IAAK,KAAK;IAAG,QAAQ;GAAK,CAAC,IAAI,KAAA;GAC1E;GAGA,aAAa,eAAe,QAAQ,UAAU,aAAa,OAAO,IAAI,CAAC;GACvE,KAAK;GACL,KAAK;GACL;GACA,OAAO;IACL,YAAY,OAAO,sBAAsB;IACzC,WAAW;IACX,WAAW,SAAS;KAClB,MAAM,QAAQ,OAAO,KAAK,aAAa,KAAK,KAAK;KACjD,IAAI,OAAO,SAAS,KAAK,KAAK,SAAS,KACrC,OAAO,OAAO,gBAAgB,aAAa;KAC7C,OAAO,OAAO,aAAa,aAAa;IAC1C;IACA,WAAW;IAEX,OAAO,OAAO,aAAa,aAAa;IACxC,UAAU;IACV,WAAW;GACb;GACA;GACA;GACA;EACF;CACF;CAEA,IAAI,SAAS,UAAU;EACrB,MAAM,eAAe,UAAmB,aAAa,OAAO,OAAO,IAAI;EACvE,OAAO;GACL,UAAU;GACV,GAAI,sBAAsB,EAAE,cAAc,uBAAuB,EAAE,IAAI;GACvE,QAAQ,WAAW,IAAI,aAAa,EAAE,QAAQ,OAAO,KAAK,CAAC,IAAI,KAAA;GAC/D;GACA,aAAa,eAAe,OAAO,aAAa,OAAO,IAAI,IAAI;GAC/D;GACA;GACA;GACA;EACF;CACF;CAEA,IAAI,SAAS,UAAU,SAAS,YAAY;EAC1C,MAAM,UAAU,WAAW,SAAS,aAAa,qBAAqB;EACtE,OAAO;GACL,UAAU;GACV,GAAI,sBAAsB,EAAE,cAAc,uBAAuB,EAAE,IAAI;GACvE,QAAQ,WAAW,IAAI,eAAe,QAAQ,SAAS,IAAI,CAAC,IAAI,KAAA;GAChE;GACA,aAAa,eACX,QACC,UAAU,eAAe,OAAO,OAAO,GACxC,OACA,IAAI,IACN;GACA;GACA;GACA;GACA;EACF;CACF;CAEA,IAAI,SAAS,aAAa;EACxB,MAAM,UAAU,UAAU;EAC1B,OAAO;GACL,UAAU;GACV,GAAI,sBAAsB,EAAE,cAAc,uBAAuB,EAAE,IAAI;GACvE,QAAQ,WAAW,IAAI,gBAAgB,OAAO,IAAI,KAAA;GAClD;GACA,aAAa,eACX,QACC,UAAU,gBAAgB,OAAO,OAAO,GACzC,OACA,IAAI,IACN;GACA;GACA;GACA;GACA;EACF;CACF;CAEA,IAAI,SAAS,SAAS,SAAS,UAC7B,OAAO,YACL;EACE,UAAU;EACV,cAAc,gBAAgB,SAAS,OAAO,OAAO,QAAQ;EAC7D,QAAQ,WAAW,IAAI,aAAa,SAAS,QAAQ,IAAI,KAAA;EACzD;EACA;EACA;EACA;EACA;CACF,GACA,SACA,MACA,UACA,OAAO,QACT;CAGF,IAAI,SAAS,QACX,OAAO,aACL;EACE,UAAU;EACV,cAAc,iBAAiB,KAAK;EACpC,QAAQ,WAAW,IAAI,WAAW,OAAO,QAAQ,IAAI,KAAA;EACrD;EACA;EACA;EACA;EACA;CACF,GACA,QACA,QACF;CAGF,OAAO;EACL,UAAU;EACV,GAAI,sBAAsB,EAAE,cAAc,uBAAuB,EAAE,IAAI;EACvE,QAAQ,WAAW,IAAI,WAAW,IAAI,KAAA;EACtC;EACA,aAAa,eACX,QACC,UAAW,SAAS,OAAO,KAAK,OAAO,KAAK,GAC7C,OACA,IAAI,IACN;EACA;EACA,OAAO,OAAO;GAAE,OAAO,OAAO,aAAa,aAAa;GAAM,UAAU;EAAG,IAAI,KAAA;EAC/E;EACA;EACA;CACF;AACF;AAEA,MAAM,oBAAoB,SAA6B,SAAuC;CAC5F,MAAM,SAAS,QAAQ,MACpB,WAAW,CAAC,kBAAkB,MAAM,KAAK,UAAU,UAAU,OAAO,IACvE;CACA,IAAI,QAAQ,OAAO,OAAO,OAAO;CACjC,IAAI,CAAC,MAAM,OAAO,KAAA;CAElB,OADc,QAAQ,MAAM,WAAW,CAAC,kBAAkB,MAAM,CACrD,CAAC,EAAE;AAChB;AAEA,MAAM,yBAAyB,QAAqB,cAAwC;CAC1F,IAAI,OAAO,YAAY,MAAM,OAAO,OAAO;CAC3C,IAAI,OAAO,QAAQ,OAAO,UAAU,WAAW,OAAO;CACtD,OAAO,qBAAqB,OAAO,QAAQ;AAC7C;AAEA,MAAM,gBAAgB,QAAsB,aAA4C;CACtF,MAAM,OAAO;EACX,GAAG;EACH,UAAW,OAA0C,YAAY;CACnE;CACA,MAAM,WAAY,OAAwC;CAE1D,IAAI,MAAM,QAAQ,QAAQ,GACxB,KAAK,UAAU,SAAS,KAAK,UAAU,aAAa,OAAA,EAAqC,CAAC;CAG5F,OAAO;AACT;AAEA,MAAa,8BAA8B,YACzC,SAAS,KAAK,WAAW,aAAa,QAAA,EAAsC,CAAC;;;;;;;;;;;;;;AAe/E,MAAM,uBACJ,QACA,QACA,UACS;CACT,IAAI,OAAO,eAAe,QAAQ,OAAO,eAAe,QAAQ,OAAO,gBAAgB,MACrF;CAEF,MAAM,OAAO,OAAO;CAGpB,OAAO,UAAU,SAAwD;EAIvE,MAAM,SAHQ,KAAK,OAGG,sBAAsB,KAAK,KAAK,KAAK,GAAG;EAC9D,MAAM,QAAQ,UAAU,QAAQ,iBAAiB,OAAO,OAAO,MAAM;EACrE,MAAM,YAAY,OAAO,SAAS,aAAa,KAAK,IAAI,IAAK,QAAQ,CAAC;EAEtE,OAAO;GACL,GAAG;GACH,OAAO,QACF,OAAO,qBAAqB,aAAa,YACxC,UAAiC,SAAS,OAAO,aAAa,aAAa;EACnF;CACF;AACF;;;;;;;;AASA,MAAM,uBAAuB,WAA+B;CAC1D,IAAI,OAAO,gBAAgB,QAAQ,OAAO,UAAU,MAAM;CAE1D,MAAM,OAAO,OAAO;CAGpB,OAAO,UAAU,SAAkB;EACjC,MAAM,YAAY,OAAO,SAAS,aAAa,KAAK,IAAI,IAAK,QAAQ,CAAC;EACtE,MAAM,UAAW,UAAuD;EACxE,IAAI;EAEJ,IAAI,MAAM,QAAQ,OAAO,GACvB,OAAO;GACL,OAAO,QAAQ,MAAM,CAAC;GACtB,OAAO,QAAQ,MAAM,CAAC,IAAA;GACtB,OAAO,QAAQ,MAAM,CAAC;GACtB,OAAO,QAAQ,MAAM,CAAC;EACxB;OACK,IAAI,OAAO,YAAY,UAC5B,OAAO;GAAC;GAAS,UAAA;GAA4B;GAAS;EAAO;OAE7D,OAAO;GAAC;GAAG,aAAa,WAAA;GAA6B;GAAG,aAAa;EAAQ;EAG/E,OAAO;GAAE,GAAG;GAAW,SAAS;EAAK;CACvC;AACF;AAEA,MAAa,kBACX,SACA,MAA6B,CAAC,MACA;CAC9B,IAAI,CAAC,SAAS,OAAO,KAAA;CACrB,MAAM,YAAY,iBAAiB,SAAS,IAAI,IAAI;CAEpD,OAAO,QAAQ,KAAK,WAAW;EAC7B,IAAI,kBAAkB,MAAM,GAC1B,OAAO,aAAa,QAAA,EAAsC;EAG5D,MAAM,cAAc;EACpB,MAAM,WAAW,qBAAqB,aAAa,KAAK,SAAS;EACjE,IAAI,YAAY,SAAS,MAAM,SAAS,QAAQ,YAAY;EAG5D,IAAI,YAAY,eAAe,MAAM,SAAS,cAAc,YAAY;EACxE,oBAAoB,UAAU,aAAa,IAAI,KAAK;EACpD,oBAAoB,QAAQ;EAI5B,IAAI,SAAS,UAAU,MACrB,SAAgD,kBAAkB;EAEpE,OAAO,aAAa,UAAU,sBAAsB,aAAa,SAAS,CAAC;CAC7E,CAAC;AACH"}
1
+ {"version":3,"file":"columns.mjs","names":[],"sources":["../../../src/Table/vtable/columns.ts"],"sourcesContent":["import type { ColumnDefine, ColumnsDefine } from '@visactor/vtable';\n\nimport {\n createArrayLayout,\n createLinkLayout,\n createTagLayout,\n createUserLayout,\n} from '../canvas/chipPainters';\nimport {\n createBooleanLayout,\n createCheckboxLayout,\n createRatingLayout,\n} from '../canvas/controlPainters';\nimport { EDIT_HINT_GUTTER } from '../canvas/paintUtils';\nimport { createCanvasTextLayout, createCurrencyLayout } from '../canvas/textPainters';\nimport { TABLE_CHROME, type TableToken } from '../canvas/theme';\nimport {\n emptyCellLabel,\n formatAddress,\n formatDateRange,\n formatDateTime,\n formatJson,\n formatNumber,\n isEmptyCellValue,\n isGroupTitleRecord,\n type LinkKind,\n} from '../core/cellValue';\nimport { type TableI18n, tableText } from '../core/tableI18n';\nimport {\n AddressEditor,\n ArrayEditor,\n DateRangeEditor,\n DateTimeEditor,\n JsonEditor,\n NumberEditor,\n OptionEditor,\n TextEditor,\n UserEditor,\n} from '../react/editors';\nimport {\n DEFAULT_TABLE_COLUMN_MIN_WIDTH,\n TABLE_CELL_MIN_WIDTH,\n type TableCellType,\n type TableColumn,\n type TableColumnInput,\n type TableSelectOption,\n} from '../type';\n\nexport interface CompileColumnsContext {\n editable?: boolean;\n groupedTree?: boolean;\n i18n?: TableI18n;\n token?: TableToken;\n tree?: boolean;\n}\n\nconst isGroupColumn = (column: TableColumnInput): boolean =>\n 'columns' in column && Array.isArray((column as { columns?: unknown }).columns);\n\n/**\n * 使用方是否要这一列**整列交回 VTable**。\n *\n * 判据只看「这列的渲染方式由 VTable 原生负责」的信号:`cellType` / `customRender` /\n * `customLayout` / `headerType` / 分组子列。\n *\n * **`fieldFormat` **不在**这里**。它只是自定义显示,是极普通的写法,而我们照样能编译这一列 ——\n * 曾经把它也算作「原生列」,代价是整列被原样返回、**不挂编辑器**:只是加了个 `fieldFormat`,\n * 列就静默失去编辑能力(树形表格 demo 的前三列连同树列本身都中招)。\n */\nconst isRawVTableColumn = (column: TableColumnInput): column is ColumnDefine =>\n isGroupColumn(column) ||\n 'cellType' in column ||\n 'customRender' in column ||\n 'customLayout' in column ||\n 'headerType' in column;\n\nconst canEdit = (column: TableColumn, editable?: boolean) => column.editable ?? editable ?? false;\n\nconst fieldFormatter =\n (\n field: string,\n format: (value: unknown) => string,\n empty?: string,\n i18n?: TableI18n,\n ): ((record: Record<string, unknown>) => string) =>\n (record) => {\n if (isGroupTitleRecord(record)) return '';\n const text = format(record == null ? undefined : record[field]);\n\n if (text.trim()) return text;\n if (!empty) return '';\n return i18n\n ? tableText({ __superadI18n: i18n }, 'setField', { title: empty })\n : emptyCellLabel(empty);\n };\n\n/** Kinds that toggle in place when their cell is clicked. */\ntype SuperAdToggleKind = 'boolean' | 'checkbox' | 'rating';\n\n/**\n * Every `superadKind` marker value written onto a compiled VTable column.\n *\n * `array` / `select` / `tag` / `user` 不参与点击切换,标出来是给 **a11y 隐藏层与剪贴板**\n * 用的:这几类的显示文本由 painter 从 `options` / 姓名 / 分隔符映射出来,而 `getCellValue`\n * 给的是记录原始值(读屏于是念 `done` 而不是「已完成」,对象值更是一串 `[object Object]`)。\n */\nexport type SuperAdCellKind = SuperAdToggleKind | 'array' | 'link' | 'select' | 'tag' | 'user';\n\n/** Markers read back by `interaction.ts` via `getBodyColumnDefine`. */\nexport interface SuperAdCellMarkers {\n /** 状态标题后跟的时长来源(见 `TableColumn.duration`),供悬停浮层与画布用同一份预留。 */\n superadDuration?: string | { from: string; to: string };\n superadEditable?: boolean;\n superadKind?: SuperAdCellKind;\n superadLinkKind?: LinkKind;\n /** 选择项,供 a11y / 剪贴板把原始值还原成画面上的标签(见 `withOptions`)。 */\n superadOptions?: TableSelectOption[];\n}\n\nconst withCellKind = (\n define: ColumnDefine,\n kind: SuperAdToggleKind,\n editable: boolean,\n): ColumnDefine => {\n const next = define as ColumnDefine & SuperAdCellMarkers;\n next.superadEditable = editable;\n next.superadKind = kind;\n return next;\n};\n\n/** 标 `superadKind`,并带上是否可编辑(画布铅笔槽 / 悬停浮层要读)。 */\nconst withTextKind = (\n define: ColumnDefine,\n kind: 'array' | 'user',\n editable: boolean,\n): ColumnDefine =>\n Object.assign(define, {\n superadEditable: editable,\n superadKind: kind,\n } as Partial<SuperAdCellMarkers>);\n\n/**\n * 把选择项挂到编译结果上。\n *\n * painter 是把 `options` 闭包进去的,编译后的列上只剩一个 `customLayout` 函数 —— 于是画布上\n * 是「已完成」、而**按列定义取值的地方**(a11y 隐藏层、剪贴板桥)只能拿到记录里的原始值\n * `done`。屏幕阅读器因此朗读英文键。把 `options` 一并留在列上,这些地方才能解析出同一个标签。\n */\nconst withOptions = (\n define: ColumnDefine,\n options: TableSelectOption[],\n kind: 'select' | 'tag',\n editable: boolean,\n duration?: string | { from: string; to: string },\n): ColumnDefine =>\n Object.assign(define, {\n superadDuration: duration,\n superadEditable: editable,\n superadKind: kind,\n superadOptions: options,\n } as Partial<SuperAdCellMarkers>);\n\nconst withLinkKind = (\n define: ColumnDefine,\n linkKind: LinkKind,\n editable: boolean,\n): ColumnDefine => {\n const next = define as ColumnDefine & SuperAdCellMarkers;\n next.superadEditable = editable;\n next.superadKind = 'link';\n next.superadLinkKind = linkKind;\n return next;\n};\n\nconst compileSuperAdColumn = (\n column: TableColumn,\n ctx: CompileColumnsContext,\n treeField?: string,\n): ColumnDefine => {\n const { currency = '¥', field, format, options = [], title, users = [], width } = column;\n const type: TableCellType = column.type ?? 'text';\n const editable = canEdit(column, ctx.editable);\n const empty = editable ? title : undefined;\n const multiple = column.multiple ?? ['array', 'email', 'phone', 'tag', 'user'].includes(type);\n const tree = field === treeField || column.tree ? true : undefined;\n const sort = column.sort || undefined;\n const token = ctx.token;\n const useCanvasTextLayout = Boolean(ctx.groupedTree) && tree !== true;\n\n if (type === 'checkbox') {\n return withCellKind(\n {\n cellType: 'text',\n customLayout: createCheckboxLayout(),\n field,\n sort,\n title,\n tree,\n width,\n },\n 'checkbox',\n editable,\n );\n }\n\n if (type === 'boolean') {\n return withCellKind(\n {\n cellType: 'text',\n customLayout: createBooleanLayout(editable),\n field,\n sort,\n title,\n tree,\n width,\n },\n 'boolean',\n editable,\n );\n }\n\n if (type === 'rating') {\n return withCellKind(\n {\n cellType: 'text',\n customLayout: createRatingLayout(),\n field,\n sort,\n title,\n tree,\n width,\n },\n 'rating',\n editable,\n );\n }\n\n if (type === 'array') {\n return withTextKind(\n {\n cellType: 'text',\n customLayout: createArrayLayout(empty),\n editor: editable ? new ArrayEditor() : undefined,\n field,\n sort,\n title,\n tree,\n width,\n },\n 'array',\n editable,\n );\n }\n\n if (type === 'email' || type === 'phone' || type === 'link') {\n return withLinkKind(\n {\n cellType: 'text',\n customLayout: createLinkLayout(type, empty),\n editor: editable ? (multiple ? new ArrayEditor() : new TextEditor()) : undefined,\n field,\n sort,\n title,\n tree,\n width,\n },\n type,\n editable,\n );\n }\n\n if (type === 'currency') {\n return {\n cellType: 'text',\n customLayout: createCurrencyLayout(currency, empty),\n editor: editable ? new NumberEditor({ prefix: currency }) : undefined,\n field,\n sort,\n title,\n tree,\n width,\n };\n }\n\n if (type === 'json') {\n return {\n cellType: 'text',\n ...(useCanvasTextLayout ? { customLayout: createCanvasTextLayout() } : null),\n editor: editable ? new JsonEditor() : undefined,\n field,\n fieldFormat: fieldFormatter(field, formatJson, empty, ctx.i18n),\n sort,\n title,\n tree,\n width,\n };\n }\n\n if (type === 'address') {\n return {\n cellType: 'text',\n ...(useCanvasTextLayout ? { customLayout: createCanvasTextLayout() } : null),\n editor: editable ? new AddressEditor() : undefined,\n field,\n fieldFormat: fieldFormatter(field, formatAddress, empty, ctx.i18n),\n sort,\n title,\n tree,\n width,\n };\n }\n\n if (type === 'image') {\n return {\n cellType: 'image',\n clickToPreview: true,\n field,\n imageAutoSizing: false,\n keepAspectRatio: true,\n style: { margin: 4 },\n title,\n tree,\n width,\n };\n }\n\n if (type === 'progress') {\n const unit = column.unit ?? '%';\n return {\n cellType: 'progressbar',\n editor: editable ? new NumberEditor({ max: 100, min: 0, suffix: unit }) : undefined,\n field,\n // 走 fieldFormatter:它有分组标题行与 record == null 的守卫,其它类型的 fieldFormat\n // 都从这里出。直接 `record[field]` 会在分组标题行上抛(record 可能为空)。\n fieldFormat: fieldFormatter(field, (value) => formatNumber(value, unit)),\n max: 100,\n min: 0,\n sort,\n style: {\n barBgColor: token?.colorFillSecondary ?? '#f5f5f5',\n barBottom: 10,\n barColor: (args) => {\n const value = Number(args.dataValue ?? args.value);\n if (Number.isFinite(value) && value >= 100)\n return token?.colorSuccess ?? TABLE_CHROME.success;\n return token?.colorInfo ?? TABLE_CHROME.accent;\n },\n barHeight: 6,\n // 走 token,否则暗色模式下是近黑色压在同为暗色的容器上。\n color: token?.colorText ?? TABLE_CHROME.text,\n fontSize: 12,\n textAlign: 'right',\n },\n title,\n tree,\n width,\n };\n }\n\n if (type === 'number') {\n const formatValue = (value: unknown) => formatNumber(value, column.unit);\n return {\n cellType: 'text',\n ...(useCanvasTextLayout ? { customLayout: createCanvasTextLayout() } : null),\n editor: editable ? new NumberEditor({ suffix: column.unit }) : undefined,\n field,\n fieldFormat: fieldFormatter(field, formatValue, empty, ctx.i18n),\n sort,\n title,\n tree,\n width,\n };\n }\n\n if (type === 'date' || type === 'datetime') {\n const pattern = format ?? (type === 'datetime' ? 'YYYY-MM-DD HH:mm' : 'YYYY-MM-DD');\n return {\n cellType: 'text',\n ...(useCanvasTextLayout ? { customLayout: createCanvasTextLayout() } : null),\n editor: editable ? new DateTimeEditor(pattern.includes('HH')) : undefined,\n field,\n fieldFormat: fieldFormatter(\n field,\n (value) => formatDateTime(value, pattern),\n empty,\n ctx.i18n,\n ),\n sort,\n title,\n tree,\n width,\n };\n }\n\n if (type === 'dateRange') {\n const pattern = format ?? 'YYYY-MM-DD';\n return {\n cellType: 'text',\n ...(useCanvasTextLayout ? { customLayout: createCanvasTextLayout() } : null),\n editor: editable ? new DateRangeEditor(pattern) : undefined,\n field,\n fieldFormat: fieldFormatter(\n field,\n (value) => formatDateRange(value, pattern),\n empty,\n ctx.i18n,\n ),\n sort,\n title,\n tree,\n width,\n };\n }\n\n if (type === 'tag' || type === 'select') {\n return withOptions(\n {\n cellType: 'text',\n customLayout: createTagLayout(options, empty, column.duration),\n editor: editable ? new OptionEditor(options, multiple) : undefined,\n field,\n sort,\n title,\n tree,\n width,\n },\n options,\n type,\n editable,\n column.duration,\n );\n }\n\n if (type === 'user') {\n return withTextKind(\n {\n cellType: 'text',\n customLayout: createUserLayout(empty),\n editor: editable ? new UserEditor(users, multiple) : undefined,\n field,\n sort,\n title,\n tree,\n width,\n },\n 'user',\n editable,\n );\n }\n\n return {\n cellType: 'text',\n ...(useCanvasTextLayout ? { customLayout: createCanvasTextLayout() } : null),\n editor: editable ? new TextEditor() : undefined,\n field,\n fieldFormat: fieldFormatter(\n field,\n (value) => (value == null ? '' : String(value)),\n empty,\n ctx.i18n,\n ),\n sort,\n style: tree ? { color: token?.colorText || TABLE_CHROME.text, fontSize: 14 } : undefined,\n title,\n tree,\n width,\n };\n};\n\nconst resolveTreeField = (columns: TableColumnInput[], tree?: boolean): string | undefined => {\n const marked = columns.find(\n (column) => !isRawVTableColumn(column) && 'tree' in column && column.tree,\n ) as TableColumn | undefined;\n if (marked?.field) return marked.field;\n if (!tree) return undefined;\n const first = columns.find((column) => !isRawVTableColumn(column)) as TableColumn | undefined;\n return first?.field;\n};\n\nconst resolveColumnMinWidth = (column: TableColumn, treeField?: string): number | string => {\n if (column.minWidth != null) return column.minWidth;\n if (column.tree || column.field === treeField) return 160;\n return TABLE_CELL_MIN_WIDTH[column.type ?? 'text'];\n};\n\nconst withMinWidth = (column: ColumnDefine, minWidth: number | string): ColumnDefine => {\n const width = (column as { width?: number | string }).width;\n const currentMin = (column as { minWidth?: number | string }).minWidth ?? minWidth;\n const next = {\n ...column,\n minWidth: currentMin,\n } as ColumnDefine & { columns?: ColumnDefine[]; maxWidth?: number | string };\n const children = (column as { columns?: ColumnDefine[] }).columns;\n\n // 数字 width 是设计宽度。VTable 的 autoFillWidth 会按比例把剩余空间摊到**每一列**,\n // 固定列(状态 chip + 时长)就被越编越宽。调用方没写 maxWidth 时钉在 width 上,\n // 剩余空间只进 `width: 'auto'` 的列。minWidth 更大时(select 默认 96、width 却写 92)\n // 取两者较大值,避免 max < min。\n if (next.maxWidth == null && typeof width === 'number') {\n const minPx = typeof currentMin === 'number' ? currentMin : width;\n next.maxWidth = Math.max(width, minPx);\n }\n\n if (Array.isArray(children)) {\n next.columns = children.map((child) => withMinWidth(child, DEFAULT_TABLE_COLUMN_MIN_WIDTH));\n }\n\n return next;\n};\n\nexport const withDefaultColumnMinWidths = (columns?: ColumnsDefine): ColumnsDefine | undefined =>\n columns?.map((column) => withMinWidth(column, DEFAULT_TABLE_COLUMN_MIN_WIDTH));\n\n/**\n * 空值时把文字压成弱色 —— 与 canvas 那条路径(`createEmptyCellText`)对齐。\n *\n * 原生渲染的列(有 `fieldFormat`、没有 `customLayout`)用的是列样式,不区分空值就会同一张表里\n * 「设置 名称」深、「设置 状态」浅。json / address / progress / number / date 这几个分支各写各的\n * `fieldFormat`,所以在这里一处收口。\n *\n * 两个必须注意的点:\n * - 判空看**原始值**:格式化后的文本在空值时正好是占位文案(「设置 X」),拿它比会误判。\n * - 使用方给了 `fieldFormat` 时**不介入**:显示值来自那个函数、未必是 `field`。树表的 department\n * 列就是这样(值取 `rec.department ?? rec.group ?? rec.name`,而记录里 `group` 是空的),\n * 按 `field` 判空会把整列错涂成弱色。\n */\nconst withBlankMutedColor = (\n define: ColumnDefine,\n column: TableColumn,\n token?: TableToken,\n): void => {\n if (column.fieldFormat != null || define.fieldFormat == null || define.customLayout != null)\n return;\n\n const base = define.style as\n ((args: unknown) => Record<string, unknown>) | Record<string, unknown> | undefined;\n\n define.style = ((args: { col: number; row: number; table?: unknown }) => {\n const table = args.table as\n | { getCellOriginRecord?: (col: number, row: number) => Record<string, unknown> | undefined }\n | undefined;\n const record = table?.getCellOriginRecord?.(args.col, args.row);\n const blank = record != null && isEmptyCellValue(record[column.field]);\n const inherited = typeof base === 'function' ? base(args) : (base ?? {});\n\n return {\n ...inherited,\n color: blank\n ? (token?.colorTextTertiary ?? TABLE_CHROME.textMuted)\n : ((inherited as { color?: string }).color ?? token?.colorText ?? TABLE_CHROME.text),\n };\n }) as ColumnDefine['style'];\n};\n\n/**\n * 原生渲染(无 customLayout)且**真有编辑器**的列:右侧 padding 多留铅笔槽。\n *\n * 判据必须与 `resolveEditHint`(看 `getEditor`)对齐 —— 只看 `canEdit` 会给 image 这类\n * 「表可编辑但本列没有 editor」的列白白让出 28px,进度条 / 封面被挤窄却永远不出现铅笔。\n * customLayout 那条路径由各 painter 自己减 `EDIT_HINT_GUTTER`,这里再加会双倍。\n */\nconst withEditHintPadding = (define: ColumnDefine): void => {\n if (define.customLayout != null || define.editor == null) return;\n\n const base = define.style as\n ((args: unknown) => Record<string, unknown>) | Record<string, unknown> | undefined;\n\n define.style = ((args: unknown) => {\n const inherited = typeof base === 'function' ? base(args) : (base ?? {});\n const padding = (inherited as { padding?: number | (number | null)[] }).padding;\n let next: [number, number, number, number];\n\n if (Array.isArray(padding)) {\n next = [\n Number(padding[0] ?? 0),\n Number(padding[1] ?? 0) + EDIT_HINT_GUTTER,\n Number(padding[2] ?? 0),\n Number(padding[3] ?? 0),\n ];\n } else if (typeof padding === 'number') {\n next = [padding, padding + EDIT_HINT_GUTTER, padding, padding];\n } else {\n next = [0, TABLE_CHROME.paddingX + EDIT_HINT_GUTTER, 0, TABLE_CHROME.paddingX];\n }\n\n return { ...inherited, padding: next };\n }) as ColumnDefine['style'];\n};\n\nexport const compileColumns = (\n columns: TableColumnInput[] | undefined,\n ctx: CompileColumnsContext = {},\n): ColumnsDefine | undefined => {\n if (!columns) return undefined;\n const treeField = resolveTreeField(columns, ctx.tree);\n\n return columns.map((column) => {\n if (isRawVTableColumn(column)) {\n return withMinWidth(column, DEFAULT_TABLE_COLUMN_MIN_WIDTH);\n }\n\n const tableColumn = column as TableColumn;\n const compiled = compileSuperAdColumn(tableColumn, ctx, treeField);\n if (tableColumn.style != null) compiled.style = tableColumn.style;\n // 使用方给的 `fieldFormat` 是这一列显示的唯一真相,压过按 `type` 生成的格式化器 ——\n // 否则「带 fieldFormat 的列现在会被编译」这条改动会把它们的显示改掉。\n if (tableColumn.fieldFormat != null) compiled.fieldFormat = tableColumn.fieldFormat;\n withBlankMutedColor(compiled, tableColumn, ctx.token);\n withEditHintPadding(compiled);\n // customLayout 列的铅笔槽读 `superadEditable`,不走上面的 padding。chip / 开关列各自\n // 已经打过;currency 与 groupedTree 下的文本 / 日期 / JSON 一直漏 —— CellTypes 的预算列\n // 和 Group demo 的日期列就会金额 / 日期顶进铅笔。有 editor 就打,与 resolveEditHint 同口径。\n if (compiled.editor != null) {\n (compiled as ColumnDefine & SuperAdCellMarkers).superadEditable = true;\n }\n if (tableColumn.maxWidth != null) {\n (compiled as ColumnDefine & { maxWidth?: number | string }).maxWidth = tableColumn.maxWidth;\n }\n return withMinWidth(compiled, resolveColumnMinWidth(tableColumn, treeField));\n });\n};\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { TABLE_CHROME, resolveGroupFill, resolveTableCanvasPalette } from "../canvas/theme.mjs";
1
+ import { TABLE_CHROME, resolveGroupFill, resolveTableCanvasPalette, resolveVerticalBorderLineWidth } from "../canvas/theme.mjs";
2
2
  import { isGroupTitleRecord, resolveOption } from "../core/cellValue.mjs";
3
3
  import { getLayoutMapHierarchyIndent } from "./vtableAdapter.mjs";
4
4
  import { countGroupRecords, createGroupTitleTagLayout, getGroupByField, getGroupTitleLevel } from "../canvas/groupTitle.mjs";
@@ -33,57 +33,52 @@ const groupTitleSeriesLayout = (showIndex, showSelection) => {
33
33
  const render = createRowSeriesLayout(showIndex, showSelection);
34
34
  return (args) => isGroupTitleRow(args.table, args.row ?? 0) ? render(args) : { renderDefault: true };
35
35
  };
36
- const rowSeriesNumber = ({ dragOrder, groupBackground, grouped, showIndex, showSelection, showVerticalBorder, token }) => ({
37
- customLayout: showSelection ? createRowSeriesLayout(Boolean(showIndex), true, Boolean(dragOrder)) : grouped ? groupTitleSeriesLayout(Boolean(showIndex), Boolean(showSelection)) : void 0,
38
- disableColumnResize: true,
39
- dragOrder: Boolean(dragOrder),
40
- format: showSelection !== true ? (_col, row, table) => {
41
- if (row == null) return "";
42
- if (showIndex !== true && dragOrder === true) return "";
43
- return resolveRowSeriesValue(table, row);
44
- } : dragOrder === true ? () => "" : void 0,
45
- headerCustomLayout: showSelection ? createRowSeriesHeaderLayout(Boolean(showIndex), Boolean(dragOrder)) : void 0,
46
- headerStyle: {
47
- bgColor: token?.colorBgContainer,
48
- borderLineWidth: showVerticalBorder ? [
49
- 0,
50
- 1,
51
- 0,
52
- 0
53
- ] : [
54
- 0,
55
- 0,
56
- 0,
57
- 0
58
- ],
59
- color: token?.colorTextSecondary ?? TABLE_CHROME.textAux,
60
- fontSize: 13,
61
- fontWeight: 500,
62
- textAlign: "center"
63
- },
64
- style: (args) => {
65
- const record = args.table.getCellOriginRecord(args.col, args.row);
66
- return {
67
- bgColor: Boolean(record?.vtableMerge) || isGroupTitleRow(args.table, args.row) ? groupBackground : token?.colorBgContainer ?? "#ffffff",
68
- borderLineWidth: showVerticalBorder ? [
69
- 1,
70
- 1,
71
- 0,
72
- 0
73
- ] : [
74
- 1,
75
- 0,
76
- 0,
77
- 0
78
- ],
79
- color: showIndex ? token?.colorText ?? TABLE_CHROME.text : token?.colorTextTertiary ?? TABLE_CHROME.textMuted,
80
- fontSize: 14,
36
+ const rowSeriesWidth = ({ dragOrder, showIndex, showSelection }) => showSelection ? showIndex ? dragOrder ? 64 : 52 : dragOrder ? 52 : 44 : dragOrder && showIndex ? 48 : 36;
37
+ const rowSeriesNumber = ({ colCount, dragOrder, groupBackground, grouped, showIndex, showSelection, showVerticalBorder, token }) => {
38
+ const width = rowSeriesWidth({
39
+ dragOrder,
40
+ showIndex,
41
+ showSelection
42
+ });
43
+ return {
44
+ customLayout: showSelection ? createRowSeriesLayout(Boolean(showIndex), true, Boolean(dragOrder)) : grouped ? groupTitleSeriesLayout(Boolean(showIndex), Boolean(showSelection)) : void 0,
45
+ disableColumnResize: true,
46
+ dragOrder: Boolean(dragOrder),
47
+ format: showSelection !== true ? (_col, row, table) => {
48
+ if (row == null) return "";
49
+ if (showIndex !== true && dragOrder === true) return "";
50
+ return resolveRowSeriesValue(table, row);
51
+ } : dragOrder === true ? () => "" : void 0,
52
+ headerCustomLayout: showSelection ? createRowSeriesHeaderLayout(Boolean(showIndex), Boolean(dragOrder)) : void 0,
53
+ headerStyle: {
54
+ bgColor: token?.colorBgContainer,
55
+ borderLineWidth: resolveVerticalBorderLineWidth({
56
+ col: 0,
57
+ table: { colCount }
58
+ }, 0, Boolean(showVerticalBorder)),
59
+ color: token?.colorTextSecondary ?? TABLE_CHROME.textAux,
60
+ fontSize: 13,
61
+ fontWeight: 500,
81
62
  textAlign: "center"
82
- };
83
- },
84
- title: showIndex ? "序号" : "",
85
- width: showSelection ? showIndex ? dragOrder ? 64 : 52 : dragOrder ? 52 : 44 : dragOrder && showIndex ? 48 : 36
86
- });
63
+ },
64
+ style: (args) => {
65
+ const record = args.table.getCellOriginRecord(args.col, args.row);
66
+ return {
67
+ bgColor: Boolean(record?.vtableMerge) || isGroupTitleRow(args.table, args.row) ? groupBackground : token?.colorBgContainer ?? "#ffffff",
68
+ borderLineWidth: resolveVerticalBorderLineWidth({
69
+ col: args.col ?? 0,
70
+ row: args.row,
71
+ table: args.table ?? { colCount }
72
+ }, 1, Boolean(showVerticalBorder)),
73
+ color: showIndex ? token?.colorText ?? TABLE_CHROME.text : token?.colorTextTertiary ?? TABLE_CHROME.textMuted,
74
+ fontSize: 14,
75
+ textAlign: "center"
76
+ };
77
+ },
78
+ title: showIndex ? "序号" : "",
79
+ width
80
+ };
81
+ };
87
82
  const createTableOption = ({ autoFillWidth, columns, editable, emptyText, frozenColCount, groupBy, groupTitleColors, headerRowHeight, hover, option, records, rowDrag, rowHeight, showHeader, showRowIndex, showRowSelection, showVerticalBorder, size = "middle", theme, token, editorTheme, i18n, tree, treeExpandLevel, treeIndent }) => {
88
83
  const density = parsePxSize(rowHeight) ?? TABLE_ROW_HEIGHT[size];
89
84
  const groupTitleBackground = theme?.groupTitleStyle?.bgColor;
@@ -131,10 +126,12 @@ const createTableOption = ({ autoFillWidth, columns, editable, emptyText, frozen
131
126
  })();
132
127
  const callerSeriesObject = option?.rowSeriesNumber;
133
128
  const needSeries = Boolean(showRowIndex || rowDrag || showRowSelection || option?.rowSeriesNumber);
134
- const resolvedFrozenColCount = frozenColCount != null && frozenColCount > 0 ? frozenColCount + (needSeries ? 1 : 0) : frozenColCount ?? option?.frozenColCount ?? 0;
129
+ const seriesColCount = needSeries ? 1 : 0;
130
+ const resolvedFrozenColCount = frozenColCount != null && frozenColCount > 0 ? frozenColCount + seriesColCount : frozenColCount ?? option?.frozenColCount ?? 0;
135
131
  const seriesOption = (() => {
136
132
  if (needSeries === false) return void 0;
137
133
  const built = rowSeriesNumber({
134
+ colCount: seriesColCount + (compiledWithGroupIndent?.length ?? 0),
138
135
  dragOrder: rowDrag,
139
136
  groupBackground,
140
137
  grouped: grouped !== void 0,