@keenaioc/data-view 0.2.0-alpha.10 → 0.2.0-alpha.11
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.
- package/CHANGELOG.md +12 -0
- package/INTEGRATION.md +40 -0
- package/KNOWN-LIMITATIONS.md +14 -0
- package/README.md +6 -2
- package/dist/column-actions.d.ts +47 -0
- package/dist/column-actions.d.ts.map +1 -0
- package/dist/column-actions.js +92 -0
- package/dist/column-actions.js.map +1 -0
- package/dist/column-menu.d.ts +25 -0
- package/dist/column-menu.d.ts.map +1 -0
- package/dist/column-menu.js +64 -0
- package/dist/column-menu.js.map +1 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +2 -1
- package/dist/contracts.js.map +1 -1
- package/dist/data-view.d.ts.map +1 -1
- package/dist/data-view.js +62 -23
- package/dist/data-view.js.map +1 -1
- package/dist/engine.d.ts +1 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +9 -1
- package/dist/engine.js.map +1 -1
- package/dist/group-order.d.ts +8 -0
- package/dist/group-order.d.ts.map +1 -0
- package/dist/group-order.js +24 -0
- package/dist/group-order.js.map +1 -0
- package/dist/group-settings.d.ts +17 -0
- package/dist/group-settings.d.ts.map +1 -0
- package/dist/group-settings.js +44 -0
- package/dist/group-settings.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/style.css +25 -0
- package/dist/types.d.ts +3 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/v2-panels.d.ts.map +1 -1
- package/dist/v2-panels.js +3 -4
- package/dist/v2-panels.js.map +1 -1
- package/package.json +1 -1
package/dist/data-view.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { Fragment, Children, useEffect, useMemo, useRef, useState, } from "react";
|
|
3
|
+
import { Fragment, Children, useEffect, useId, useMemo, useRef, useState, } from "react";
|
|
4
4
|
import { columnMovePeers, moveColumnSetting } from "./field-order.js";
|
|
5
|
+
import { ColumnMenu } from "./column-menu.js";
|
|
6
|
+
import { headerItems, changeHeaderView, FieldActionForm, ColumnFilterForm, FilterValueInput } from "./column-actions.js";
|
|
7
|
+
import { GroupSettings } from "./group-settings.js";
|
|
5
8
|
import { DataViewToolbar } from "./toolbar.js";
|
|
6
|
-
import { buildGroupTree, DataViewConflictError, filterRows, getFieldValue, getPinnedOffsets, getRightPinnedOffsets,
|
|
9
|
+
import { buildGroupTree, DataViewConflictError, filterRows, getFieldValue, getPinnedOffsets, getRightPinnedOffsets, moveColumn, normalizeColumnWidth, normalizeViewConfig, retainViewReferences, searchRows, sortRows, validateValue, visibleFields, } from "./engine.js";
|
|
7
10
|
import { FILTER_OPERATOR_LABELS, RelationSelect, getFieldTypeDefinition, renderFieldValue, formatClipboardValue, formatReadableValue, formatReadableRow } from "./registry.js";
|
|
8
11
|
import { canWriteField, createPasteChanges, encodeTSV, newOperationId, validateChanges, validateFieldChange } from "./operations.js";
|
|
9
12
|
import { createDataViewQuery, dataViewQueryKey, resolveMetrics, mergeViewState, splitViewState, createWriteQueue, virtualWindow } from "./contracts.js";
|
|
@@ -43,6 +46,9 @@ function DataViewInner(props) {
|
|
|
43
46
|
const [anchor, setAnchor] = useState(null);
|
|
44
47
|
const [detailId, setDetailId] = useState(null);
|
|
45
48
|
const [dialog, setDialog] = useState(null);
|
|
49
|
+
const menuId = useId();
|
|
50
|
+
const [headerMenu, setHeaderMenu] = useState(null);
|
|
51
|
+
const [fieldAction, setFieldAction] = useState(null);
|
|
46
52
|
const [pending, setPending] = useState(null);
|
|
47
53
|
const [operation, setOperation] = useState({ state: "idle" });
|
|
48
54
|
const [undoStack, setUndoStack] = useState([]);
|
|
@@ -64,6 +70,14 @@ function DataViewInner(props) {
|
|
|
64
70
|
const detailWriter = useRef(createSnapshotWriter((v) => detailCallback.current?.(v)));
|
|
65
71
|
const features = props.features ?? {};
|
|
66
72
|
const availableFields = useMemo(() => fields.filter((f) => !f.internal), [fields]);
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
setHeaderMenu(null);
|
|
75
|
+
setFieldAction(null);
|
|
76
|
+
}, [props.presentation, activeView.viewType]);
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
setHeaderMenu((current) => current && (!current.anchor.isConnected || !availableFields.some((field) => field.id === current.fieldId)) ? null : current);
|
|
79
|
+
setFieldAction((current) => current && !availableFields.some((field) => field.id === current.fieldId) ? null : current);
|
|
80
|
+
}, [availableFields]);
|
|
67
81
|
const latest = useRef({ rows, fields, permissions, access, ganttDependencies, mapping: activeView.gantt });
|
|
68
82
|
useEffect(() => { latest.current = { rows, fields, permissions, access, ganttDependencies, mapping: activeView.gantt }; }, [rows, fields, permissions, access, ganttDependencies, activeView.gantt]);
|
|
69
83
|
useEffect(() => {
|
|
@@ -700,7 +714,7 @@ function DataViewInner(props) {
|
|
|
700
714
|
}
|
|
701
715
|
const range = gridRange();
|
|
702
716
|
const desktopRowIndices = new Map(desktopRows.map((row, index) => [getRowId(row), index]));
|
|
703
|
-
return _jsxs("section", { ref: rootRef, className: `hy-dv density-${activeView.density ?? "standard"} presentation-${props.presentation ?? "auto"} chrome-${props.chrome ?? "standard"} ${props.height !== undefined ? "has-total-height" : ""}`, style: { height: props.height, "--hy-row-height": `${rowHeight}px`, "--hy-header-height": `${metrics.headerHeight}px`, "--hy-group-height": `${metrics.groupHeight}px`, "--hy-cell-font": `${metrics.cellFontSize}px`, "--hy-cell-line": `${metrics.cellLineHeight}px`, "--hy-header-font": `${metrics.headerFontSize}px`, "--hy-cell-padding": `${metrics.cellPadding}px`, "--hy-header-left": `${metrics.headerPaddingLeft}px`, "--hy-header-right": `${metrics.headerPaddingRight}px`, "--hy-control-height": `${metrics.controlHeight}px`, "--hy-action-height": `${metrics.actionHeight}px`, "--hy-badge-height": `${metrics.badgeHeight}px`, "--hy-badge-font": `${metrics.badgeFontSize}px`, "--hy-badge-padding": `${metrics.badgePadding}px`, "--hy-viewport-height": typeof props.viewportHeight === "number" ? `${props.viewportHeight}px` : props.viewportHeight ?? "560px", "--hy-scrollbar-height": `${metrics.scrollbarHeight}px`, "--hy-scrollbar-thumb": `${metrics.scrollbarThumb}px` }, "aria-label": title, onKeyDown: handleKey, onCopy: handleCopy, onPaste: handlePaste, children: [_jsxs("div", { className: "hy-dv-heading", children: [_jsxs("header", { className: "hy-dv-titlebar", children: [props.headerStart, _jsxs("div", { children: [_jsx("h2", { children: title }), _jsx("span", { children: props.remote ? `符合 ${remoteStale ? "…" : props.remote.total ?? "…"} 筆,已載入 ${processedRows.length} 筆${props.remote.complete && !remoteStale ? "(完整)" : "(尚未完整)"}` : `${processedRows.length} / ${rows.length} 筆` })] }), _jsxs("div", { className: "hy-dv-title-status", children: [selected.length > 0 && _jsxs("span", { children: ["\u5DF2\u9078 ", selected.length, " \u7B46"] }), !props.hideViewSaveStatus && _jsx(SaveBadge, { state: viewSaveState, prefix: "\u6AA2\u8996" }), !props.hideViewSaveStatus && (viewSaveState === "failed" || viewSaveState === "conflict") && _jsx("button", { onClick: () => setView(viewRef.current), children: "\u91CD\u8A66\u5132\u5B58" })] }), props.headerEnd] }), features.viewTabs !== false && _jsxs("nav", { className: "hy-dv-view-tabs", "aria-label": "\u8CC7\u6599\u8996\u5716", children: [[["table", "資料表格"], ["gantt", "甘特圖"], ["calendar", "行事曆"]].map(([type, label]) => _jsx("button", { "aria-pressed": activeView.viewType === type, disabled: permissions?.canSwitchView === false, onClick: () => { patchView({ viewType: type }, configurable ? "layout" : "navigation"); setEditing(null); setPanel(null); }, children: label }, type)), configurable && features.viewSettings !== false && _jsx("button", { onClick: () => setPanel(panel === "settings" ? null : "settings"), children: activeView.viewType === "gantt" ? "甘特圖設定" : activeView.viewType === "calendar" ? "行事曆設定" : "視圖設定" })] })] }), _jsxs(DataViewToolbar, { compact: props.chrome === "compact", start: props.toolbarStart, end: props.toolbarEnd, secondary: _jsxs(_Fragment, { children: [configurable && features.columns !== false && _jsx(ToolbarButton, { active: panel === "columns", onClick: () => setPanel(panel === "columns" ? null : "columns"), children: "\u96FB\u8166\u8868\u683C\u6B04\u4F4D" }), configurable && features.colors !== false && _jsx(ToolbarButton, { active: panel === "colors", onClick: () => setPanel(panel === "colors" ? null : "colors"), children: "\u586B\u8272" }), canManageFields && _jsxs(_Fragment, { children: [_jsx(ToolbarButton, { onClick: () => { setPanel(null); setDialog("add-field"); }, children: "\uFF0B \u65B0\u589E\u6B04\u4F4D" }), _jsx(ToolbarButton, { onClick: () => { setPanel(null); setDialog("fields"); }, children: "\u7BA1\u7406\u6B04\u4F4D" })] }), onBatchCommit && _jsxs(_Fragment, { children: [features.batch !== false && _jsxs("button", { disabled: !selectedRows.length || busy, onClick: () => { setPanel(null); setDialog("batch"); }, children: ["\u6279\u6B21\u4FEE\u6539 ", selectedRows.length || ""] }), features.undo !== false && _jsx("button", { disabled: !undoStack.length || busy, onClick: () => void historyAction("undo"), children: "\u5FA9\u539F" }), features.redo !== false && _jsx("button", { disabled: !redoStack.length || busy, onClick: () => void historyAction("redo"), children: "\u91CD\u505A" })] }), selected.length > 0 && _jsx(ToolbarButton, { onClick: () => changeSelection([]), children: "\u6E05\u9664\u9078\u53D6" }), activeView.groups.length > 0 && permissions?.canCollapseGroups !== false && _jsxs(_Fragment, { children: [_jsx(ToolbarButton, { onClick: () => setAllGroups(true), children: "\u5168\u90E8\u6536\u5408" }), _jsx(ToolbarButton, { onClick: () => setAllGroups(false), children: "\u5168\u90E8\u5C55\u958B" })] })] }), children: [features.search !== false && permissions?.canSearch !== false && _jsxs("label", { className: "hy-dv-search", children: [_jsx("span", { "aria-hidden": true, children: "\u2315" }), _jsx("input", { "aria-label": searchPlaceholder, value: search, onChange: (event) => patchLocation({ search: event.target.value, tableTop: 0, ganttTop: 0 }), placeholder: searchPlaceholder })] }), canFilter && features.filter !== false && _jsxs(ToolbarButton, { active: panel === "filter" || activeView.filters.rules.length > 0, onClick: () => setPanel(panel === "filter" ? null : "filter"), children: ["\u7BE9\u9078 ", activeView.filters.rules.length || ""] }), canSort && features.sort !== false && _jsxs(ToolbarButton, { active: panel === "sort" || activeView.sorts.length > 0, onClick: () => setPanel(panel === "sort" ? null : "sort"), children: ["\u6392\u5E8F ", activeView.sorts.length || ""] }), configurable && features.group !== false && _jsxs(ToolbarButton, { active: panel === "group" || activeView.groups.length > 0, onClick: () => setPanel(panel === "group" ? null : "group"), children: ["\u5206\u7D44 ", activeView.groups.length || ""] }), props.onPresentationChange && _jsxs("select", { "aria-label": "\u5448\u73FE\u65B9\u5F0F", value: props.presentation ?? "auto", onChange: (e) => props.onPresentationChange?.(e.target.value), children: [_jsx("option", { value: "auto", children: "\u81EA\u52D5" }), _jsx("option", { value: "table", children: "\u8868\u683C" }), _jsx("option", { value: "cards", children: "\u5361\u7247" })] })] }), move.message && _jsx("p", { role: "status", children: move.message }), operation.state !== "idle" && _jsxs("div", { className: `hy-dv-operation is-${operation.state}`, role: operation.state === "failed" || operation.state === "conflict" ? "alert" : "status", children: [_jsx(SaveBadge, { state: operation.state }), _jsx("span", { children: operation.message })] }), panel && (configurable || panel === "filter" && canFilter || panel === "sort" && canSort) && _jsxs("div", { className: "hy-dv-panel-wrap", children: [panel === "filter" && _jsx(FilterPanel, { fields: availableFields, view: activeView, onChange: (v) => setView(v, "query"), onClose: () => setPanel(null) }), panel === "sort" && _jsx(SortPanel, { fields: availableFields, view: activeView, onChange: (v) => setView(v, "query"), onClose: () => setPanel(null) }), panel === "group" && _jsx(GroupPanel, { fields: fields, view: activeView, onChange: setView, onClose: () => setPanel(null) }), panel === "columns" && _jsx(ColumnPanel, { fields: availableFields, view: { ...activeView, columns: activeView.columns.filter((c) => availableFields.some((f) => f.id === c.fieldId)) }, onChange: setView, onClose: () => setPanel(null), dragged: draggedColumn, setDragged: setDraggedColumn, drop: dropColumn }), panel === "settings" && _jsx(PanelShell, { title: "\u8996\u5716\u8A2D\u5B9A", onClose: () => setPanel(null), children: _jsx(ViewSettings, { fields: availableFields, view: activeView, onChange: setView }) }), panel === "colors" && _jsx(PanelShell, { title: "\u586B\u8272\u898F\u5247", onClose: () => setPanel(null), children: _jsx(ColorSettings, { fields: availableFields, view: activeView, onChange: setView }) })] }), _jsx("div", { className: "hy-dv-body", children: remoteStale || props.remote?.loading && !processedRows.length ? _jsx(LoadingState, {}) : processedRows.length === 0 && props.remote?.error ? _jsx(StateCard, { title: "\u67E5\u8A62\u5931\u6557", message: props.remote.error, tone: "error" }) : processedRows.length === 0 ? _jsx(StateCard, { title: "\u6C92\u6709\u7B26\u5408\u689D\u4EF6\u7684\u8CC7\u6599", message: rows.length === 0 ? emptyMessage : "請調整搜尋或篩選條件。" }) : activeView.viewType !== "table" ? _jsx(ScheduleView, { metrics: metrics, getRowLabel: readableLabel, groupLabel: groupLabel, onLoadNearEnd: loadNearEnd, allRows: rows, dependencies: ganttDependencies, onDependencyChange: onGanttDependencyChange && permissions?.canManageDependencies === true ? changeDependency : undefined, onScheduleCommit: onBatchCommit ? execute : undefined, rows: processedRows, renderMobileRow: (row) => _jsx("dl", { className: "hy-dv-schedule-fields", children: mobileFields.filter((field) => canRead(row, field)).map((field) => _jsxs(Fragment, { children: [_jsx("dt", { children: field.label }), _jsx("dd", { children: renderCell(row, field, "mobile") })] }, field.id)) }), onLocationChange: patchLocation, onToggleGroup: toggleGroup, renderLeftHeader: () => _jsxs("tr", { children: [_jsx("th", { className: "hy-dv-open-header is-pinned", style: { left: 0 }, children: "\u8A73\u60C5" }), visible.map((v) => renderColumnHeader(v.field, v.column, true))] }), renderLeftRow: (row) => _jsxs("tr", { children: [_jsx("td", { className: "is-pinned", style: { left: 0 }, children: _jsx("button", { "aria-label": `開啟 ${getRowId(row)} 詳情`, onClick: () => openRow(row), children: "\u2197" }) }), visible.map(({ field }) => _jsx("td", { className: ganttLeftOffsets.has(field.id) || ganttRightOffsets.has(field.id) ? "is-pinned" : "", style: { left: ganttLeftOffsets.get(field.id), right: ganttRightOffsets.get(field.id), backgroundColor: colorMap.get(getRowId(row))?.get(field.id) ?? colorMap.get(getRowId(row))?.get("row") }, children: _jsx("div", { className: "hy-dv-gantt-cell", children: renderCell(row, field, "gantt") }) }, field.id))] }, getRowId(row)), fields: fields, getRowId: getRowId, view: activeView, permissions: permissions, busy: busy, onCommit: onBatchCommit ? (changes) => execute({ id: newOperationId(), reason: "edit", changes }) : undefined, onOpen: openRow }, activeView.viewType) : _jsxs(_Fragment, { children: [_jsx("div", { className: "hy-dv-desktop", children: _jsx("div", { ref: scrollRef, className: `hy-dv-table-scroll ${virtual ? "is-virtual" : ""}`, onScroll: (event) => { setScrollTop(event.currentTarget.scrollTop); loadNearEnd(event.currentTarget); if (bottomRef.current)
|
|
717
|
+
return _jsxs("section", { ref: rootRef, className: `hy-dv density-${activeView.density ?? "standard"} presentation-${props.presentation ?? "auto"} chrome-${props.chrome ?? "standard"} ${props.height !== undefined ? "has-total-height" : ""}`, style: { height: props.height, "--hy-row-height": `${rowHeight}px`, "--hy-header-height": `${metrics.headerHeight}px`, "--hy-group-height": `${metrics.groupHeight}px`, "--hy-cell-font": `${metrics.cellFontSize}px`, "--hy-cell-line": `${metrics.cellLineHeight}px`, "--hy-header-font": `${metrics.headerFontSize}px`, "--hy-cell-padding": `${metrics.cellPadding}px`, "--hy-header-left": `${metrics.headerPaddingLeft}px`, "--hy-header-right": `${metrics.headerPaddingRight}px`, "--hy-control-height": `${metrics.controlHeight}px`, "--hy-action-height": `${metrics.actionHeight}px`, "--hy-badge-height": `${metrics.badgeHeight}px`, "--hy-badge-font": `${metrics.badgeFontSize}px`, "--hy-badge-padding": `${metrics.badgePadding}px`, "--hy-viewport-height": typeof props.viewportHeight === "number" ? `${props.viewportHeight}px` : props.viewportHeight ?? "560px", "--hy-scrollbar-height": `${metrics.scrollbarHeight}px`, "--hy-scrollbar-thumb": `${metrics.scrollbarThumb}px` }, "aria-label": title, onKeyDown: handleKey, onCopy: handleCopy, onPaste: handlePaste, children: [_jsxs("div", { className: "hy-dv-heading", children: [_jsxs("header", { className: "hy-dv-titlebar", children: [props.headerStart, _jsxs("div", { children: [_jsx("h2", { children: title }), _jsx("span", { children: props.remote ? `符合 ${remoteStale ? "…" : props.remote.total ?? "…"} 筆,已載入 ${processedRows.length} 筆${props.remote.complete && !remoteStale ? "(完整)" : "(尚未完整)"}` : `${processedRows.length} / ${rows.length} 筆` })] }), _jsxs("div", { className: "hy-dv-title-status", children: [selected.length > 0 && _jsxs("span", { children: ["\u5DF2\u9078 ", selected.length, " \u7B46"] }), !props.hideViewSaveStatus && _jsx(SaveBadge, { state: viewSaveState, prefix: "\u6AA2\u8996" }), !props.hideViewSaveStatus && (viewSaveState === "failed" || viewSaveState === "conflict") && _jsx("button", { onClick: () => setView(viewRef.current), children: "\u91CD\u8A66\u5132\u5B58" })] }), props.headerEnd] }), features.viewTabs !== false && _jsxs("nav", { className: "hy-dv-view-tabs", "aria-label": "\u8CC7\u6599\u8996\u5716", children: [[["table", "資料表格"], ["gantt", "甘特圖"], ["calendar", "行事曆"]].map(([type, label]) => _jsx("button", { "aria-pressed": activeView.viewType === type, disabled: permissions?.canSwitchView === false, onClick: () => { patchView({ viewType: type }, configurable ? "layout" : "navigation"); setEditing(null); setPanel(null); }, children: label }, type)), configurable && features.viewSettings !== false && _jsx("button", { onClick: () => setPanel(panel === "settings" ? null : "settings"), children: activeView.viewType === "gantt" ? "甘特圖設定" : activeView.viewType === "calendar" ? "行事曆設定" : "視圖設定" })] })] }), _jsxs(DataViewToolbar, { compact: props.chrome === "compact", start: props.toolbarStart, end: props.toolbarEnd, secondary: _jsxs(_Fragment, { children: [configurable && features.columns !== false && _jsx(ToolbarButton, { active: panel === "columns", onClick: () => setPanel(panel === "columns" ? null : "columns"), children: "\u96FB\u8166\u8868\u683C\u6B04\u4F4D" }), configurable && features.colors !== false && _jsx(ToolbarButton, { active: panel === "colors", onClick: () => setPanel(panel === "colors" ? null : "colors"), children: "\u586B\u8272" }), canManageFields && _jsxs(_Fragment, { children: [_jsx(ToolbarButton, { onClick: () => { setPanel(null); setDialog("add-field"); }, children: "\uFF0B \u65B0\u589E\u6B04\u4F4D" }), _jsx(ToolbarButton, { onClick: () => { setPanel(null); setDialog("fields"); }, children: "\u7BA1\u7406\u6B04\u4F4D" })] }), onBatchCommit && _jsxs(_Fragment, { children: [features.batch !== false && _jsxs("button", { disabled: !selectedRows.length || busy, onClick: () => { setPanel(null); setDialog("batch"); }, children: ["\u6279\u6B21\u4FEE\u6539 ", selectedRows.length || ""] }), features.undo !== false && _jsx("button", { disabled: !undoStack.length || busy, onClick: () => void historyAction("undo"), children: "\u5FA9\u539F" }), features.redo !== false && _jsx("button", { disabled: !redoStack.length || busy, onClick: () => void historyAction("redo"), children: "\u91CD\u505A" })] }), selected.length > 0 && _jsx(ToolbarButton, { onClick: () => changeSelection([]), children: "\u6E05\u9664\u9078\u53D6" }), activeView.groups.length > 0 && permissions?.canCollapseGroups !== false && _jsxs(_Fragment, { children: [_jsx(ToolbarButton, { onClick: () => setAllGroups(true), children: "\u5168\u90E8\u6536\u5408" }), _jsx(ToolbarButton, { onClick: () => setAllGroups(false), children: "\u5168\u90E8\u5C55\u958B" })] })] }), children: [features.search !== false && permissions?.canSearch !== false && _jsxs("label", { className: "hy-dv-search", children: [_jsx("span", { "aria-hidden": true, children: "\u2315" }), _jsx("input", { "aria-label": searchPlaceholder, value: search, onChange: (event) => patchLocation({ search: event.target.value, tableTop: 0, ganttTop: 0 }), placeholder: searchPlaceholder })] }), canFilter && features.filter !== false && _jsxs(ToolbarButton, { active: panel === "filter" || activeView.filters.rules.length > 0, onClick: () => setPanel(panel === "filter" ? null : "filter"), children: ["\u7BE9\u9078 ", activeView.filters.rules.length || ""] }), canSort && features.sort !== false && _jsxs(ToolbarButton, { active: panel === "sort" || activeView.sorts.length > 0, onClick: () => setPanel(panel === "sort" ? null : "sort"), children: ["\u6392\u5E8F ", activeView.sorts.length || ""] }), configurable && features.group !== false && _jsxs(ToolbarButton, { active: panel === "group" || activeView.groups.length > 0, onClick: () => setPanel(panel === "group" ? null : "group"), children: ["\u5206\u7D44 ", activeView.groups.length || ""] }), props.onPresentationChange && _jsxs("select", { "aria-label": "\u5448\u73FE\u65B9\u5F0F", value: props.presentation ?? "auto", onChange: (e) => props.onPresentationChange?.(e.target.value), children: [_jsx("option", { value: "auto", children: "\u81EA\u52D5" }), _jsx("option", { value: "table", children: "\u8868\u683C" }), _jsx("option", { value: "cards", children: "\u5361\u7247" })] })] }), move.message && _jsx("p", { role: "status", children: move.message }), operation.state !== "idle" && _jsxs("div", { className: `hy-dv-operation is-${operation.state}`, role: operation.state === "failed" || operation.state === "conflict" ? "alert" : "status", children: [_jsx(SaveBadge, { state: operation.state }), _jsx("span", { children: operation.message })] }), panel && (configurable || panel === "filter" && canFilter || panel === "sort" && canSort) && _jsxs("div", { className: "hy-dv-panel-wrap", children: [panel === "filter" && _jsx(FilterPanel, { fields: availableFields, view: activeView, onChange: (v) => setView(v, "query"), onClose: () => setPanel(null) }), panel === "sort" && _jsx(SortPanel, { fields: availableFields, view: activeView, onChange: (v) => setView(v, "query"), onClose: () => setPanel(null) }), panel === "group" && _jsx(PanelShell, { title: "\u5206\u7D44\u8A2D\u5B9A", onClose: () => setPanel(null), children: _jsx(GroupSettings, { fields: fields, rows: remoteStale ? [] : rows, view: activeView, onChange: setView }) }), panel === "columns" && _jsx(ColumnPanel, { fields: availableFields, view: { ...activeView, columns: activeView.columns.filter((c) => availableFields.some((f) => f.id === c.fieldId)) }, onChange: setView, onClose: () => setPanel(null), dragged: draggedColumn, setDragged: setDraggedColumn, drop: dropColumn }), panel === "settings" && _jsx(PanelShell, { title: "\u8996\u5716\u8A2D\u5B9A", onClose: () => setPanel(null), children: _jsx(ViewSettings, { fields: availableFields, view: activeView, onChange: setView }) }), panel === "colors" && _jsx(PanelShell, { title: "\u586B\u8272\u898F\u5247", onClose: () => setPanel(null), children: _jsx(ColorSettings, { fields: availableFields, view: activeView, onChange: setView }) })] }), _jsx("div", { className: "hy-dv-body", children: remoteStale || props.remote?.loading && !processedRows.length ? _jsx(LoadingState, {}) : processedRows.length === 0 && props.remote?.error ? _jsx(StateCard, { title: "\u67E5\u8A62\u5931\u6557", message: props.remote.error, tone: "error" }) : processedRows.length === 0 ? _jsx(StateCard, { title: "\u6C92\u6709\u7B26\u5408\u689D\u4EF6\u7684\u8CC7\u6599", message: rows.length === 0 ? emptyMessage : "請調整搜尋或篩選條件。" }) : activeView.viewType !== "table" ? _jsx(ScheduleView, { metrics: metrics, getRowLabel: readableLabel, groupLabel: groupLabel, onLoadNearEnd: loadNearEnd, allRows: rows, dependencies: ganttDependencies, onDependencyChange: onGanttDependencyChange && permissions?.canManageDependencies === true ? changeDependency : undefined, onScheduleCommit: onBatchCommit ? execute : undefined, rows: processedRows, renderMobileRow: (row) => _jsx("dl", { className: "hy-dv-schedule-fields", children: mobileFields.filter((field) => canRead(row, field)).map((field) => _jsxs(Fragment, { children: [_jsx("dt", { children: field.label }), _jsx("dd", { children: renderCell(row, field, "mobile") })] }, field.id)) }), onLocationChange: patchLocation, onToggleGroup: toggleGroup, renderLeftHeader: () => _jsxs("tr", { children: [_jsx("th", { className: "hy-dv-open-header is-pinned", style: { left: 0 }, children: "\u8A73\u60C5" }), visible.map((v) => renderColumnHeader(v.field, v.column, true))] }), renderLeftRow: (row) => _jsxs("tr", { children: [_jsx("td", { className: "is-pinned", style: { left: 0 }, children: _jsx("button", { "aria-label": `開啟 ${getRowId(row)} 詳情`, onClick: () => openRow(row), children: "\u2197" }) }), visible.map(({ field }) => _jsx("td", { className: ganttLeftOffsets.has(field.id) || ganttRightOffsets.has(field.id) ? "is-pinned" : "", style: { left: ganttLeftOffsets.get(field.id), right: ganttRightOffsets.get(field.id), backgroundColor: colorMap.get(getRowId(row))?.get(field.id) ?? colorMap.get(getRowId(row))?.get("row") }, children: _jsx("div", { className: "hy-dv-gantt-cell", children: renderCell(row, field, "gantt") }) }, field.id))] }, getRowId(row)), fields: fields, getRowId: getRowId, view: activeView, permissions: permissions, busy: busy, onCommit: onBatchCommit ? (changes) => execute({ id: newOperationId(), reason: "edit", changes }) : undefined, onOpen: openRow }, activeView.viewType) : _jsxs(_Fragment, { children: [_jsx("div", { className: "hy-dv-desktop", children: _jsx("div", { ref: scrollRef, className: `hy-dv-table-scroll ${virtual ? "is-virtual" : ""}`, onScroll: (event) => { setScrollTop(event.currentTarget.scrollTop); loadNearEnd(event.currentTarget); if (bottomRef.current)
|
|
704
718
|
bottomRef.current.scrollLeft = event.currentTarget.scrollLeft; patchLocation({ tableTop: event.currentTarget.scrollTop, tableLeft: event.currentTarget.scrollLeft }); }, children: _jsxs("table", { className: "hy-dv-table", style: { width: tableWidth }, "aria-label": "\u8CC7\u6599\u5132\u5B58\u683C", "aria-rowcount": desktopRows.length + 1, children: [_jsxs("colgroup", { children: [controlColumn && _jsx("col", { style: { width: controlWidth } }), visible.map(({ field, column }) => _jsx("col", { style: { width: column.width } }, field.id)), canManageFields && _jsx("col", { style: { width: addColumnWidth } })] }), _jsx("thead", { children: _jsxs("tr", { children: [controlColumn && _jsx("th", { className: "hy-dv-selection is-pinned", style: { left: 0 }, children: selectable && _jsx("input", { type: "checkbox", checked: allPageSelected, onChange: togglePageSelection, "aria-label": props.remote && !props.remote.complete ? "選取目前已載入資料" : "選取全部符合條件資料" }) }), visible.map(({ field, column }) => renderColumnHeader(field, column)), canManageFields && _jsx("th", { className: "hy-dv-add-column is-pinned", style: { right: 0 }, children: _jsx("button", { type: "button", disabled: busy, "aria-label": "\u65B0\u589E\u6B04\u4F4D", title: "\u65B0\u589E\u6B04\u4F4D", onClick: () => { setPanel(null); setDialog("add-field"); }, children: "\uFF0B" }) })] }) }), _jsxs("tbody", { children: [virtual && virtualStart > 0 && _jsx("tr", { "aria-hidden": true, children: _jsx("td", { colSpan: tableColumnCount, style: { height: windowed.before, padding: 0, border: 0 } }) }), flatEntries.slice(virtualStart, virtualEnd).map((entry) => entry.kind === "row" ? renderDesktopRow(entry.row) : _jsx("tr", { "data-group-key": entry.node.key, className: `hy-dv-group depth-${entry.node.depth} ${move.target?.position === "groupEnd" && move.target.targetGroupPath.at(-1)?.key === entry.node.key ? "is-drop-after" : ""}`, style: { height: entryHeights[flatEntries.indexOf(entry)] }, children: _jsx("td", { colSpan: tableColumnCount, children: _jsxs("button", { type: "button", onClick: () => toggleGroup(entry.node.key), "aria-expanded": !collapsed.has(entry.node.key), children: [collapsed.has(entry.node.key) ? "›" : "⌄", groupLabel(entry.node)] }) }) }, `group:${entry.node.key}`)), virtual && virtualEnd < flatEntries.length && _jsx("tr", { "aria-hidden": true, children: _jsx("td", { colSpan: tableColumnCount, style: { height: windowed.after, padding: 0, border: 0 } }) })] })] }) }) }), _jsx(VirtualList, { items: flatEntries, itemKey: (e) => e.kind === "group" ? `g:${e.node.key}` : getRowId(e.row), estimate: (e) => e.kind === "group" ? 40 : activeView.mobile?.presentation === "compact" ? 80 + mobileFields.length * 28 : 100 + mobileFields.length * 36, className: `hy-dv-mobile mobile-${activeView.mobile?.presentation ?? "cards"}`, initialTop: activeView.location?.mobileTop, onScroll: (el) => { loadNearEnd(el); patchLocation({ mobileTop: el.scrollTop }); }, renderItem: (entry) => entry.kind === "group" ? _jsxs("button", { "data-group-key": entry.node.key, className: "hy-dv-mobile-group-title", "aria-expanded": !collapsed.has(entry.node.key), onClick: () => toggleGroup(entry.node.key), children: [collapsed.has(entry.node.key) ? "›" : "⌄", groupLabel(entry.node)] }) : renderMobileCard(entry.row) }), _jsxs("p", { className: "hy-dv-keyboard-help", children: ["\u65B9\u5411\u9375\u79FB\u52D5 \u00B7 Shift\uFF0B\u65B9\u5411\u9375\u9078\u7BC4\u570D \u00B7 Enter \u7DE8\u8F2F \u00B7 Ctrl/Cmd\uFF0BC\uFF0FV \u8907\u88FD\u8CBC\u4E0A \u00B7 Delete \u6E05\u7A7A\u9810\u89BD", virtual ? ` · 連續捲動,共 ${desktopRows.length} 筆` : ""] }), props.bottomScrollbar !== false && tableWidth > viewportWidth && _jsx("div", { ref: bottomRef, className: "hy-dv-bottom-scroll", "aria-label": "\u8868\u683C\u6C34\u5E73\u6372\u52D5", onScroll: (e) => { if (scrollRef.current)
|
|
705
719
|
scrollRef.current.scrollLeft = e.currentTarget.scrollLeft; }, children: _jsx("div", { style: { width: tableWidth, height: 1 } }) })] }) }), props.remote && _jsxs("div", { className: "hy-dv-load-status", role: props.remote.error ? "alert" : "status", children: [props.remote.error || (props.remote.loading || remoteStale ? "正在載入符合條件的資料…" : props.remote.complete ? `已完整載入 ${processedRows.length} 筆` : `尚未完整:已載入 ${processedRows.length}/${props.remote.total ?? "…"} 筆`), !props.remote.complete && !props.remote.loading && !remoteStale && _jsx("button", { onClick: props.remote.error ? props.remote.onRetry : props.remote.onLoadMore, children: props.remote.error ? "重新查詢" : "繼續載入" })] }), quick && (() => { const row = rows.find((r) => getRowId(r) === quick.rowId); const field = fields.find((f) => f.id === quick.fieldId); return row && field && !field.internal && canRead(row, field) && _jsxs(DataViewDialog, { title: field.label, onClose: () => setQuick(null), children: [field.renderQuickView?.({ row, field, value: getFieldValue(row, field), close: () => setQuick(null), openRow: () => { setQuick(null); openRow(row); } }), canEdit(row, field) && _jsx("button", { onClick: () => { setQuick(null); setDetailId(getRowId(row)); setEditing({ rowId: getRowId(row), fieldId: field.id, surface: "detail" }); }, children: "\u7DE8\u8F2F" })] }); })(), detailRow && _jsxs(DataViewDialog, { title: readableLabel(detailRow), busy: busy, onClose: () => { setDetailId(null); setEditing(null); }, children: [props.renderDetail ? props.renderDetail(detailContext(detailRow)) : _jsx("div", { className: "hy-dv-detail", children: availableFields.filter((field) => canRead(detailRow, field)).map((field) => _jsxs("label", { children: [_jsx("span", { children: field.label }), renderCell(detailRow, field, "detail")] }, field.id)) }), features.copy !== false && _jsx("button", { onClick: async () => { try {
|
|
706
720
|
await navigator.clipboard.writeText(formatReadableRow(detailRow, availableFields));
|
|
@@ -708,7 +722,27 @@ function DataViewInner(props) {
|
|
|
708
722
|
}
|
|
709
723
|
catch {
|
|
710
724
|
reportFailure(new Error("無法使用剪貼簿,請確認瀏覽器權限"));
|
|
711
|
-
} }, children: "\u8907\u88FD\u6587\u5B57" }), props.renderDetailFooter?.(detailContext(detailRow)), operation.state !== "idle" && _jsx("p", { role: "status", children: operation.message })] }),
|
|
725
|
+
} }, children: "\u8907\u88FD\u6587\u5B57" }), props.renderDetailFooter?.(detailContext(detailRow)), operation.state !== "idle" && _jsx("p", { role: "status", children: operation.message })] }), headerMenu && features.columnMenu !== false && (() => {
|
|
726
|
+
const field = availableFields.find((item) => item.id === headerMenu.fieldId);
|
|
727
|
+
return field && _jsx(ColumnMenu, { id: menuId, label: field.label, anchor: headerMenu.anchor, items: headerItems({ field, fields, view: activeView, permissions, features, hasFieldCallback: !!onFieldChange, busy }), onChoose: (action) => chooseHeaderAction(field, action), onClose: () => setHeaderMenu(null) });
|
|
728
|
+
})(), fieldAction && (() => {
|
|
729
|
+
const field = availableFields.find((item) => item.id === fieldAction.fieldId);
|
|
730
|
+
if (!field)
|
|
731
|
+
return null;
|
|
732
|
+
const action = fieldAction.action;
|
|
733
|
+
return _jsxs(DataViewDialog, { title: `${action === "rename" ? "重新命名" : action === "copy" ? "複製欄位與值" : action === "delete" ? "刪除欄位" : "篩選"}:${field.label}`, busy: busy, onClose: () => setFieldAction(null), children: [action === "filter" ? _jsx(ColumnFilterForm, { field: field, filters: activeView.filters, disabled: busy || !canFilter || features.filter === false, onCancel: () => setFieldAction(null), onApply: (rule) => {
|
|
734
|
+
if (busyRef.current || !canFilter || features.filter === false)
|
|
735
|
+
return;
|
|
736
|
+
const current = viewRef.current.filters;
|
|
737
|
+
if (current.rules.length >= 20 && !current.rules.some((item) => item.id === rule.id)) {
|
|
738
|
+
setOperation({ state: "failed", message: "最多二十個篩選條件,請先移除其他條件。" });
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
const rules = current.rules.some((item) => item.id === rule.id) ? current.rules.map((item) => item.id === rule.id ? rule : item) : [...current.rules, rule];
|
|
742
|
+
setView({ ...viewRef.current, filters: { ...current, rules } }, "query");
|
|
743
|
+
setFieldAction(null);
|
|
744
|
+
} }, field.id) : _jsx(FieldActionForm, { action: action, field: field, fields: fields, permissions: permissions, busy: busy, onApply: applyFieldChange, onCancel: () => setFieldAction(null) }, `${field.id}:${action}`), (operation.state === "failed" || operation.state === "conflict") && _jsx("p", { role: "alert", children: operation.message })] });
|
|
745
|
+
})(), dialog && _jsxs(DataViewDialog, { title: dialog === "add-field" ? "新增欄位" : dialog === "fields" ? "管理欄位" : dialog === "batch" ? "批次修改" : "確認資料修改", busy: busy, onClose: () => { setDialog(null); setPending(null); }, children: [dialog === "add-field" && _jsx(FieldCreator, { fields: fields, permissions: permissions, onApply: async (change) => { const saved = await applyFieldChange(change); if (saved)
|
|
712
746
|
setDialog(null); return saved; }, busy: busy, onCancel: () => setDialog(null) }), dialog === "fields" && _jsx(FieldManager, { fields: availableFields, permissions: permissions, onApply: applyFieldChange, busy: busy }), dialog === "batch" && _jsx(BatchEditor, { fields: availableFields, rows: selectedRows, getRowId: getRowId, permissions: permissions, onReview: reviewChanges }), dialog === "review" && pending && _jsxs("div", { className: "hy-dv-form", children: [_jsxs("p", { children: ["\u5171 ", pending.changes.length, " \u683C\u3002\u6240\u6709\u6B04\u4F4D\u9A57\u8B49\u901A\u904E\u5F8C\uFF0C\u7531\u5C08\u6848\u4E00\u6B21\u5BEB\u5165\uFF1B\u5931\u6557\u6216\u885D\u7A81\u6642\u4FDD\u7559\u539F\u8CC7\u6599\u3002"] }), _jsx("div", { className: "hy-dv-review-list", children: pending.changes.slice(0, 30).map((change) => _jsxs("div", { children: [_jsxs("strong", { children: [change.rowId, "\uFF0F", change.field.label] }), _jsxs("span", { children: [formatClipboardValue(change.previousValue, change.field) || "空白", " \u2192 ", formatClipboardValue(change.nextValue, change.field) || "空白"] })] }, `${change.rowId}:${change.field.id}`)) }), pending.changes.length > 30 && _jsxs("p", { children: ["\u9810\u89BD\u524D 30 \u683C\uFF0C\u5168\u90E8 ", pending.changes.length, " \u683C\u90FD\u6703\u9A57\u8B49\u3002"] }), _jsxs("button", { disabled: busy, onClick: async () => { if (await execute(pending)) {
|
|
713
747
|
setDialog(null);
|
|
714
748
|
setPending(null);
|
|
@@ -727,10 +761,33 @@ function DataViewInner(props) {
|
|
|
727
761
|
} }, className: `${left === undefined && right === undefined ? "" : "is-pinned"} ${inRange ? "is-range" : ""}`, style: { left, right, backgroundColor: colors?.get(field.id) ?? colors?.get("row") }, children: _jsx("div", { className: "hy-dv-cell-content", children: renderCell(row, field, "desktop") }) }, field.id);
|
|
728
762
|
}), canManageFields && _jsx("td", { className: "hy-dv-add-column is-pinned", style: { right: 0 } })] }, rowId);
|
|
729
763
|
}
|
|
764
|
+
function chooseHeaderAction(field, action) {
|
|
765
|
+
const allowed = headerItems({ field, fields, view: viewRef.current, permissions, features, hasFieldCallback: !!onFieldChange, busy: busyRef.current }).find((item) => item.action === action);
|
|
766
|
+
if (!allowed || allowed.disabled)
|
|
767
|
+
return;
|
|
768
|
+
setHeaderMenu(null);
|
|
769
|
+
if (["rename", "copy", "delete", "filter"].includes(action)) {
|
|
770
|
+
setFieldAction({ fieldId: field.id, action: action });
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
if (action === "columns") {
|
|
774
|
+
setPanel("columns");
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
const next = changeHeaderView(viewRef.current, field.id, action);
|
|
778
|
+
if (next !== viewRef.current)
|
|
779
|
+
setView(next, action === "sort-asc" || action === "sort-desc" || action === "clear-sort" ? "query" : "layout");
|
|
780
|
+
if (action === "group")
|
|
781
|
+
setPanel("group");
|
|
782
|
+
}
|
|
730
783
|
function renderColumnHeader(field, column, gantt = false) {
|
|
731
784
|
const left = (gantt ? ganttLeftOffsets : pinnedOffsets).get(field.id);
|
|
732
785
|
const right = (gantt ? ganttRightOffsets : rightOffsets).get(field.id);
|
|
733
|
-
return _jsxs("th", { draggable: configurable, onDragStart: () => setDraggedColumn(field.id), onDragOver: (event) => event.preventDefault(), onDrop: () => dropColumn(field.id), className: left === undefined && right === undefined ? "" : "is-pinned", style: { left, right }, children: [_jsx("span", { children: field.label }), activeView.sorts.findIndex((rule) => rule.fieldId === field.id) >= 0 && _jsxs("small", { children: [activeView.sorts.findIndex((rule) => rule.fieldId === field.id) + 1, activeView.sorts.find((rule) => rule.fieldId === field.id)?.direction === "asc" ? "↑" : "↓"] }), configurable && _jsx("
|
|
786
|
+
return _jsxs("th", { draggable: configurable, onDragStart: () => setDraggedColumn(field.id), onDragOver: (event) => event.preventDefault(), onDrop: () => dropColumn(field.id), className: left === undefined && right === undefined ? "" : "is-pinned", style: { left, right }, children: [_jsxs("div", { className: "hy-dv-column-heading", children: [_jsx("span", { className: "hy-dv-column-title", children: field.label }), activeView.sorts.findIndex((rule) => rule.fieldId === field.id) >= 0 && _jsxs("small", { children: [activeView.sorts.findIndex((rule) => rule.fieldId === field.id) + 1, activeView.sorts.find((rule) => rule.fieldId === field.id)?.direction === "asc" ? "↑" : "↓"] }), features.columnMenu !== false && (configurable || canManageFields || canFilter || canSort) && _jsx("button", { type: "button", className: "hy-dv-header-menu-trigger", "aria-label": `${field.label}欄位操作`, title: `${field.label}欄位操作`, "aria-haspopup": "menu", "aria-expanded": headerMenu?.fieldId === field.id, "aria-controls": headerMenu?.fieldId === field.id ? menuId : undefined, draggable: false, onDragStart: (event) => { event.preventDefault(); event.stopPropagation(); }, onPointerDown: (event) => event.stopPropagation(), onClick: (event) => { event.stopPropagation(); setHeaderMenu(headerMenu?.fieldId === field.id ? null : { fieldId: field.id, anchor: event.currentTarget }); }, onKeyDown: (event) => { if (event.key === "ArrowDown") {
|
|
787
|
+
event.preventDefault();
|
|
788
|
+
event.stopPropagation();
|
|
789
|
+
setHeaderMenu({ fieldId: field.id, anchor: event.currentTarget });
|
|
790
|
+
} }, children: "\u2304" })] }), configurable && _jsx("span", { className: "hy-dv-resize", onPointerDown: (event) => resizeColumn(field.id, column.width, event), "aria-hidden": true })] }, field.id);
|
|
734
791
|
}
|
|
735
792
|
function renderMobileCard(row) {
|
|
736
793
|
const rowId = getRowId(row);
|
|
@@ -774,24 +831,6 @@ function FilterPanel({ fields, view, onChange, onClose }) {
|
|
|
774
831
|
onChange({ ...view, filters: { ...view.filters, rules: [...view.filters.rules, { id: createRuleId(), fieldId: field.id, operator: getFieldTypeDefinition(field.type).operators[0] }] } });
|
|
775
832
|
}, children: "\uFF0B \u589E\u52A0\u689D\u4EF6" })] })] });
|
|
776
833
|
}
|
|
777
|
-
function FilterValueInput({ field, rule, onChange }) {
|
|
778
|
-
if (["isEmpty", "isNotEmpty", "isTrue", "isFalse"].includes(rule.operator))
|
|
779
|
-
return _jsx("span", { className: "hy-dv-no-value", children: "\u4E0D\u9700\u586B\u503C" });
|
|
780
|
-
if (field.options?.length)
|
|
781
|
-
return _jsxs("select", { value: String(rule.value ?? ""), onChange: (event) => onChange(event.target.value), children: [_jsx("option", { value: "", children: "\u8ACB\u9078\u64C7" }), field.options.map((option) => _jsx("option", { value: option.value, children: option.label }, option.value))] });
|
|
782
|
-
if (["number", "currency", "percentage", "autoNumber"].includes(field.type))
|
|
783
|
-
return _jsx("input", { type: "number", value: String(rule.value ?? ""), onChange: (event) => onChange(event.target.value === "" ? undefined : Number(event.target.value)) });
|
|
784
|
-
if (["date", "dateTime", "createdAt", "updatedAt"].includes(field.type))
|
|
785
|
-
return _jsx("input", { type: "date", value: String(rule.value ?? "").slice(0, 10), onChange: (event) => onChange(event.target.value) });
|
|
786
|
-
return _jsx("input", { value: String(rule.value ?? ""), onChange: (event) => onChange(event.target.value), placeholder: "\u8F38\u5165\u689D\u4EF6" });
|
|
787
|
-
}
|
|
788
|
-
function GroupPanel({ fields, view, onChange, onClose }) {
|
|
789
|
-
return _jsxs(PanelShell, { title: "\u5206\u7D44\u8A2D\u5B9A", onClose: onClose, children: [_jsx("p", { children: "\u6700\u591A\u4E09\u5C64\uFF1B\u7531\u4E0A\u5230\u4E0B\u5957\u7528\u3002" }), _jsxs("div", { className: "hy-dv-rules", children: [view.groups.map((rule, index) => _jsxs("div", { className: "hy-dv-rule", children: [_jsx("span", { children: index + 1 }), _jsx("select", { value: rule.fieldId, onChange: (event) => onChange({ ...view, groups: view.groups.map((item, i) => i === index ? { fieldId: event.target.value } : item) }), children: fields.map((field) => _jsx("option", { value: field.id, disabled: view.groups.some((item, i) => i !== index && item.fieldId === field.id), children: field.label }, field.id)) }), _jsx("button", { type: "button", onClick: () => onChange({ ...view, groups: view.groups.filter((_, i) => i !== index) }), children: "\u79FB\u9664" })] }, `${rule.fieldId}-${index}`)), _jsx("button", { type: "button", className: "hy-dv-add", disabled: view.groups.length >= Math.min(MAX_GROUP_LEVELS, fields.length), onClick: () => {
|
|
790
|
-
const field = fields.find((item) => !view.groups.some((rule) => rule.fieldId === item.id));
|
|
791
|
-
if (field)
|
|
792
|
-
onChange({ ...view, groups: [...view.groups, { fieldId: field.id }] });
|
|
793
|
-
}, children: "\uFF0B \u589E\u52A0\u5206\u7D44" })] })] });
|
|
794
|
-
}
|
|
795
834
|
function ColumnPanel({ fields, view, onChange, onClose, dragged, setDragged, drop }) {
|
|
796
835
|
const definitions = new Map(fields.map((field) => [field.id, field]));
|
|
797
836
|
const visibleCount = view.columns.filter((column) => column.visible).length;
|