@ceed/cds 1.27.1 → 1.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/DataTable/components.d.ts +3 -2
- package/dist/components/DataTable/hooks.d.ts +2 -0
- package/dist/components/DataTable/styled.d.ts +1 -1
- package/dist/components/DataTable/utils.d.ts +5 -0
- package/dist/index.browser.js +4 -4
- package/dist/index.browser.js.map +4 -4
- package/dist/index.cjs +191 -17
- package/dist/index.js +191 -17
- package/dist/libs/text-measurer.d.ts +8 -0
- package/framer/index.js +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -2091,6 +2091,36 @@ var CurrencyInput_default = CurrencyInput;
|
|
|
2091
2091
|
var import_react28 = __toESM(require("react"));
|
|
2092
2092
|
var import_react_virtual2 = require("@tanstack/react-virtual");
|
|
2093
2093
|
|
|
2094
|
+
// src/libs/text-measurer.ts
|
|
2095
|
+
var TextMeasurer = class {
|
|
2096
|
+
constructor(font) {
|
|
2097
|
+
const canvas = document.createElement("canvas");
|
|
2098
|
+
this.ctx = canvas.getContext("2d");
|
|
2099
|
+
if (this.ctx && font) this.ctx.font = font;
|
|
2100
|
+
}
|
|
2101
|
+
setFont(font) {
|
|
2102
|
+
if (this.ctx) this.ctx.font = font;
|
|
2103
|
+
return this;
|
|
2104
|
+
}
|
|
2105
|
+
setFontFromElement(el) {
|
|
2106
|
+
if (this.ctx) this.ctx.font = getComputedStyle(el).font;
|
|
2107
|
+
return this;
|
|
2108
|
+
}
|
|
2109
|
+
measureText(text) {
|
|
2110
|
+
if (!this.ctx) return 0;
|
|
2111
|
+
return this.ctx.measureText(text).width;
|
|
2112
|
+
}
|
|
2113
|
+
measureMaxWidth(values) {
|
|
2114
|
+
if (!this.ctx) return 0;
|
|
2115
|
+
let max = 0;
|
|
2116
|
+
for (let i = 0; i < values.length; i++) {
|
|
2117
|
+
const w = this.ctx.measureText(values[i]).width;
|
|
2118
|
+
if (w > max) max = w;
|
|
2119
|
+
}
|
|
2120
|
+
return max;
|
|
2121
|
+
}
|
|
2122
|
+
};
|
|
2123
|
+
|
|
2094
2124
|
// src/components/DataTable/utils.ts
|
|
2095
2125
|
function extractFieldsFromGroupingModel(items) {
|
|
2096
2126
|
const fields = /* @__PURE__ */ new Set();
|
|
@@ -2212,6 +2242,71 @@ function getTextAlign(props) {
|
|
|
2212
2242
|
return !props.editMode && ["number", "date", "currency"].includes(props.type || "") ? "end" : "start";
|
|
2213
2243
|
}
|
|
2214
2244
|
var numberFormatter = (value) => "Intl" in window ? new Intl.NumberFormat().format(value) : value;
|
|
2245
|
+
function computeHeaderWidth(headerEl) {
|
|
2246
|
+
const thStyle = getComputedStyle(headerEl);
|
|
2247
|
+
const paddingX = parseFloat(thStyle.paddingLeft) + parseFloat(thStyle.paddingRight);
|
|
2248
|
+
const borderX = parseFloat(thStyle.borderLeftWidth) + parseFloat(thStyle.borderRightWidth);
|
|
2249
|
+
const stack = headerEl.firstElementChild;
|
|
2250
|
+
if (!stack) return paddingX;
|
|
2251
|
+
const stackStyle = getComputedStyle(stack);
|
|
2252
|
+
const gap = parseFloat(stackStyle.gap) || parseFloat(stackStyle.columnGap) || 0;
|
|
2253
|
+
let totalChildWidth = 0;
|
|
2254
|
+
let visibleChildCount = 0;
|
|
2255
|
+
for (const child of Array.from(stack.children)) {
|
|
2256
|
+
const el = child;
|
|
2257
|
+
if (!el.offsetWidth && !el.offsetHeight) continue;
|
|
2258
|
+
visibleChildCount++;
|
|
2259
|
+
const textEl = el.querySelector?.('[data-slot="header-text"]') || (el.dataset?.slot === "header-text" ? el : null);
|
|
2260
|
+
if (textEl) {
|
|
2261
|
+
const htmlEl = textEl;
|
|
2262
|
+
const isMultiLine = getComputedStyle(htmlEl).display === "-webkit-box";
|
|
2263
|
+
if (isMultiLine) {
|
|
2264
|
+
const measurer = new TextMeasurer();
|
|
2265
|
+
measurer.setFontFromElement(htmlEl);
|
|
2266
|
+
totalChildWidth += measurer.measureText(htmlEl.textContent || "");
|
|
2267
|
+
} else {
|
|
2268
|
+
totalChildWidth += htmlEl.scrollWidth;
|
|
2269
|
+
}
|
|
2270
|
+
} else {
|
|
2271
|
+
totalChildWidth += el.offsetWidth;
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
const totalGaps = visibleChildCount > 1 ? (visibleChildCount - 1) * gap : 0;
|
|
2275
|
+
return totalChildWidth + totalGaps + paddingX + borderX;
|
|
2276
|
+
}
|
|
2277
|
+
function computeBodyWidth(headerEl, table, field, dataInPage) {
|
|
2278
|
+
const headId = headerEl.id;
|
|
2279
|
+
const sampleTd = headId ? table.querySelector(`tbody td[headers="${CSS.escape(headId)}"]`) : null;
|
|
2280
|
+
const styleSource = sampleTd || headerEl;
|
|
2281
|
+
const tdStyle = getComputedStyle(styleSource);
|
|
2282
|
+
const bodyPaddingX = parseFloat(tdStyle.paddingLeft) + parseFloat(tdStyle.paddingRight);
|
|
2283
|
+
const bodyBorderX = parseFloat(tdStyle.borderLeftWidth) + parseFloat(tdStyle.borderRightWidth);
|
|
2284
|
+
const measurer = new TextMeasurer();
|
|
2285
|
+
measurer.setFont(tdStyle.font);
|
|
2286
|
+
const texts = [];
|
|
2287
|
+
for (let i = 0; i < dataInPage.length; i++) {
|
|
2288
|
+
const val = dataInPage[i][field];
|
|
2289
|
+
texts.push(val == null ? "" : String(val));
|
|
2290
|
+
}
|
|
2291
|
+
const maxTextWidth = measurer.measureMaxWidth(texts);
|
|
2292
|
+
return maxTextWidth + bodyPaddingX + bodyBorderX;
|
|
2293
|
+
}
|
|
2294
|
+
function computeAutoFitWidth(params) {
|
|
2295
|
+
const { headerEl, field, dataInPage } = params;
|
|
2296
|
+
const table = headerEl.closest("table");
|
|
2297
|
+
if (!table) return null;
|
|
2298
|
+
const headerWidth = computeHeaderWidth(headerEl);
|
|
2299
|
+
const bodyWidth = computeBodyWidth(headerEl, table, field, dataInPage);
|
|
2300
|
+
let finalWidth = Math.ceil(Math.max(headerWidth, bodyWidth));
|
|
2301
|
+
const thStyle = getComputedStyle(headerEl);
|
|
2302
|
+
const resolvedMin = thStyle.minWidth;
|
|
2303
|
+
const resolvedMax = thStyle.maxWidth;
|
|
2304
|
+
const minPx = resolvedMin !== "none" ? parseFloat(resolvedMin) : NaN;
|
|
2305
|
+
const maxPx = resolvedMax !== "none" ? parseFloat(resolvedMax) : NaN;
|
|
2306
|
+
if (!isNaN(minPx) && minPx > 0) finalWidth = Math.max(finalWidth, minPx);
|
|
2307
|
+
if (!isNaN(maxPx) && maxPx > 0) finalWidth = Math.min(finalWidth, maxPx);
|
|
2308
|
+
return finalWidth;
|
|
2309
|
+
}
|
|
2215
2310
|
|
|
2216
2311
|
// src/components/DataTable/styled.tsx
|
|
2217
2312
|
var import_react19 = __toESM(require("react"));
|
|
@@ -2322,7 +2417,7 @@ var StyledTd = (0, import_joy25.styled)("td")(({ theme }) => ({
|
|
|
2322
2417
|
var MotionSortIcon = (0, import_framer_motion17.motion)(import_ArrowUpwardRounded.default);
|
|
2323
2418
|
var DefaultLoadingOverlay = () => /* @__PURE__ */ import_react19.default.createElement(import_joy25.LinearProgress, { value: 8, variant: "plain" });
|
|
2324
2419
|
var DefaultNoRowsOverlay = () => /* @__PURE__ */ import_react19.default.createElement(import_joy25.Typography, { level: "body-sm", textColor: "text.tertiary" }, "No rows");
|
|
2325
|
-
var Resizer = (ref, targetRef = ref, onResizeStateChange) => /* @__PURE__ */ import_react19.default.createElement(
|
|
2420
|
+
var Resizer = (ref, targetRef = ref, onResizeStateChange, onAutoFit) => /* @__PURE__ */ import_react19.default.createElement(
|
|
2326
2421
|
Box_default,
|
|
2327
2422
|
{
|
|
2328
2423
|
sx: {
|
|
@@ -2330,24 +2425,67 @@ var Resizer = (ref, targetRef = ref, onResizeStateChange) => /* @__PURE__ */ imp
|
|
|
2330
2425
|
top: 0,
|
|
2331
2426
|
right: 0,
|
|
2332
2427
|
bottom: 0,
|
|
2333
|
-
width: "
|
|
2334
|
-
cursor: "col-resize"
|
|
2428
|
+
width: "7px",
|
|
2429
|
+
cursor: "col-resize",
|
|
2430
|
+
"&::after": {
|
|
2431
|
+
content: '""',
|
|
2432
|
+
position: "absolute",
|
|
2433
|
+
top: 0,
|
|
2434
|
+
bottom: 0,
|
|
2435
|
+
right: 0,
|
|
2436
|
+
width: "2px",
|
|
2437
|
+
bgcolor: "transparent",
|
|
2438
|
+
transition: "background-color 150ms ease"
|
|
2439
|
+
},
|
|
2440
|
+
"&:hover::after": {
|
|
2441
|
+
bgcolor: "primary.300"
|
|
2442
|
+
},
|
|
2443
|
+
"&[data-resizing]::after": {
|
|
2444
|
+
bgcolor: "primary.500"
|
|
2445
|
+
}
|
|
2335
2446
|
},
|
|
2336
2447
|
onClick: (e) => e.stopPropagation(),
|
|
2448
|
+
onDoubleClick: (e) => {
|
|
2449
|
+
e.stopPropagation();
|
|
2450
|
+
e.preventDefault();
|
|
2451
|
+
onAutoFit?.();
|
|
2452
|
+
},
|
|
2337
2453
|
onMouseDown: (e) => {
|
|
2454
|
+
if (e.detail >= 2) return;
|
|
2455
|
+
const resizerEl = e.currentTarget;
|
|
2456
|
+
resizerEl.dataset.resizing = "";
|
|
2338
2457
|
const initialX = e.clientX;
|
|
2339
2458
|
const initialWidth = ref.current?.getBoundingClientRect().width;
|
|
2340
|
-
|
|
2459
|
+
let activated = false;
|
|
2460
|
+
const DRAG_THRESHOLD = 3;
|
|
2461
|
+
const thStyle = ref.current ? getComputedStyle(ref.current) : null;
|
|
2462
|
+
const minW = thStyle ? parseFloat(thStyle.minWidth) : NaN;
|
|
2463
|
+
const maxW = thStyle ? parseFloat(thStyle.maxWidth) : NaN;
|
|
2341
2464
|
const onMouseMove = (e2) => {
|
|
2342
|
-
if (initialWidth
|
|
2343
|
-
|
|
2344
|
-
|
|
2465
|
+
if (!initialWidth) return;
|
|
2466
|
+
const delta = e2.clientX - initialX;
|
|
2467
|
+
if (!activated) {
|
|
2468
|
+
if (Math.abs(delta) < DRAG_THRESHOLD) return;
|
|
2469
|
+
activated = true;
|
|
2470
|
+
onResizeStateChange?.(true);
|
|
2345
2471
|
}
|
|
2472
|
+
if (!ref.current || !targetRef.current) {
|
|
2473
|
+
onMouseUp();
|
|
2474
|
+
return;
|
|
2475
|
+
}
|
|
2476
|
+
let newWidth = initialWidth + delta;
|
|
2477
|
+
if (!isNaN(minW) && minW > 0) newWidth = Math.max(newWidth, minW);
|
|
2478
|
+
if (!isNaN(maxW) && maxW > 0) newWidth = Math.min(newWidth, maxW);
|
|
2479
|
+
ref.current.style.width = `${newWidth}px`;
|
|
2480
|
+
targetRef.current.style.width = `${newWidth}px`;
|
|
2346
2481
|
};
|
|
2347
2482
|
const onMouseUp = () => {
|
|
2483
|
+
resizerEl.removeAttribute("data-resizing");
|
|
2348
2484
|
document.removeEventListener("mousemove", onMouseMove);
|
|
2349
2485
|
document.removeEventListener("mouseup", onMouseUp);
|
|
2350
|
-
|
|
2486
|
+
if (activated) {
|
|
2487
|
+
requestAnimationFrame(() => onResizeStateChange?.(false));
|
|
2488
|
+
}
|
|
2351
2489
|
};
|
|
2352
2490
|
document.addEventListener("mousemove", onMouseMove);
|
|
2353
2491
|
document.addEventListener("mouseup", onMouseUp);
|
|
@@ -2920,7 +3058,11 @@ function InfoSign(props) {
|
|
|
2920
3058
|
var InfoSign_default = InfoSign;
|
|
2921
3059
|
|
|
2922
3060
|
// src/components/DataTable/components.tsx
|
|
2923
|
-
var TextEllipsis = ({
|
|
3061
|
+
var TextEllipsis = ({
|
|
3062
|
+
children,
|
|
3063
|
+
lineClamp,
|
|
3064
|
+
...rest
|
|
3065
|
+
}) => {
|
|
2924
3066
|
const textRef = (0, import_react24.useRef)(null);
|
|
2925
3067
|
const [showTooltip, setShowTooltip] = (0, import_react24.useState)(false);
|
|
2926
3068
|
(0, import_react24.useLayoutEffect)(() => {
|
|
@@ -2935,7 +3077,7 @@ var TextEllipsis = ({ children, lineClamp }) => {
|
|
|
2935
3077
|
ro.observe(element);
|
|
2936
3078
|
return () => ro.disconnect();
|
|
2937
3079
|
}, [children, lineClamp]);
|
|
2938
|
-
return /* @__PURE__ */ import_react24.default.createElement(Tooltip_default, { title: showTooltip ? children : "", placement: "top" }, /* @__PURE__ */ import_react24.default.createElement(EllipsisDiv, { ref: textRef, lineClamp }, children));
|
|
3080
|
+
return /* @__PURE__ */ import_react24.default.createElement(Tooltip_default, { title: showTooltip ? children : "", placement: "top" }, /* @__PURE__ */ import_react24.default.createElement(EllipsisDiv, { ref: textRef, lineClamp, ...rest }, children));
|
|
2939
3081
|
};
|
|
2940
3082
|
var CellTextEllipsis = ({ children }) => {
|
|
2941
3083
|
const textRef = (0, import_react24.useRef)(null);
|
|
@@ -2987,11 +3129,16 @@ var HeadCell = (props) => {
|
|
|
2987
3129
|
headerRef,
|
|
2988
3130
|
tableColRef,
|
|
2989
3131
|
headerClassName,
|
|
2990
|
-
headerLineClamp
|
|
3132
|
+
headerLineClamp,
|
|
3133
|
+
onAutoFit
|
|
2991
3134
|
} = props;
|
|
2992
3135
|
const theme = (0, import_joy32.useTheme)();
|
|
2993
3136
|
const ref = headerRef;
|
|
2994
3137
|
const colRef = tableColRef;
|
|
3138
|
+
const localRef = (0, import_react24.useRef)(null);
|
|
3139
|
+
(0, import_react24.useLayoutEffect)(() => {
|
|
3140
|
+
ref.current = localRef.current;
|
|
3141
|
+
}, [ref]);
|
|
2995
3142
|
const [isHovered, setIsHovered] = (0, import_react24.useState)(false);
|
|
2996
3143
|
const sortable = type === "actions" ? false : _sortable;
|
|
2997
3144
|
const headId = (0, import_react24.useMemo)(
|
|
@@ -3000,10 +3147,15 @@ var HeadCell = (props) => {
|
|
|
3000
3147
|
);
|
|
3001
3148
|
const isResizingRef = (0, import_react24.useRef)(false);
|
|
3002
3149
|
const resizer = (0, import_react24.useMemo)(
|
|
3003
|
-
() => resizable ?? true ? Resizer(
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3150
|
+
() => resizable ?? true ? Resizer(
|
|
3151
|
+
ref,
|
|
3152
|
+
colRef,
|
|
3153
|
+
(isResizing) => {
|
|
3154
|
+
isResizingRef.current = isResizing;
|
|
3155
|
+
},
|
|
3156
|
+
onAutoFit ? () => onAutoFit(field) : void 0
|
|
3157
|
+
) : null,
|
|
3158
|
+
[resizable, ref, colRef, onAutoFit, field]
|
|
3007
3159
|
);
|
|
3008
3160
|
const style = (0, import_react24.useMemo)(
|
|
3009
3161
|
() => ({
|
|
@@ -3074,7 +3226,7 @@ var HeadCell = (props) => {
|
|
|
3074
3226
|
id: headId,
|
|
3075
3227
|
"aria-label": headerName ?? field,
|
|
3076
3228
|
"aria-sort": sort ? { asc: "ascending", desc: "descending" }[sort] : "none",
|
|
3077
|
-
ref,
|
|
3229
|
+
ref: localRef,
|
|
3078
3230
|
key: field,
|
|
3079
3231
|
style,
|
|
3080
3232
|
onClick: (0, import_react24.useCallback)(
|
|
@@ -3091,7 +3243,7 @@ var HeadCell = (props) => {
|
|
|
3091
3243
|
initial: "initial",
|
|
3092
3244
|
className: computedHeaderClassName
|
|
3093
3245
|
},
|
|
3094
|
-
/* @__PURE__ */ import_react24.default.createElement(Stack_default, { gap: 1, direction: "row", justifyContent: textAlign, alignItems: "center", sx: { minWidth: 0 } }, textAlign === "end" && sortIcon, textAlign === "end" && infoSign, /* @__PURE__ */ import_react24.default.createElement(TextEllipsis, { lineClamp: headerLineClamp }, headerName ?? field, editMode && required && /* @__PURE__ */ import_react24.default.createElement(Asterisk, null, "*")), textAlign === "start" && infoSign, textAlign === "start" && sortIcon),
|
|
3246
|
+
/* @__PURE__ */ import_react24.default.createElement(Stack_default, { gap: 1, direction: "row", justifyContent: textAlign, alignItems: "center", sx: { minWidth: 0 } }, textAlign === "end" && sortIcon, textAlign === "end" && infoSign, /* @__PURE__ */ import_react24.default.createElement(TextEllipsis, { lineClamp: headerLineClamp, "data-slot": "header-text" }, headerName ?? field, editMode && required && /* @__PURE__ */ import_react24.default.createElement(Asterisk, null, "*")), textAlign === "start" && infoSign, textAlign === "start" && sortIcon),
|
|
3095
3247
|
resizer
|
|
3096
3248
|
);
|
|
3097
3249
|
};
|
|
@@ -3638,6 +3790,25 @@ function useDataTableRenderer({
|
|
|
3638
3790
|
prevRowsRef.current = _rows;
|
|
3639
3791
|
}
|
|
3640
3792
|
}, [_rows]);
|
|
3793
|
+
const handleAutoFit = (0, import_react25.useCallback)(
|
|
3794
|
+
(field) => {
|
|
3795
|
+
const colDef = visibleColumnsByField[field];
|
|
3796
|
+
if (!colDef?.headerRef.current) return;
|
|
3797
|
+
const column = allColumnsByField[field];
|
|
3798
|
+
const columnType = column && "type" in column ? column.type : void 0;
|
|
3799
|
+
if (columnType === "actions") return;
|
|
3800
|
+
const optimalWidth = computeAutoFitWidth({
|
|
3801
|
+
headerEl: colDef.headerRef.current,
|
|
3802
|
+
field,
|
|
3803
|
+
dataInPage
|
|
3804
|
+
});
|
|
3805
|
+
if (optimalWidth == null) return;
|
|
3806
|
+
const widthPx = `${optimalWidth}px`;
|
|
3807
|
+
colDef.headerRef.current.style.width = widthPx;
|
|
3808
|
+
if (colDef.tableColRef.current) colDef.tableColRef.current.style.width = widthPx;
|
|
3809
|
+
},
|
|
3810
|
+
[visibleColumnsByField, allColumnsByField, dataInPage]
|
|
3811
|
+
);
|
|
3641
3812
|
return {
|
|
3642
3813
|
rowCount,
|
|
3643
3814
|
selectableRowCount,
|
|
@@ -3649,6 +3820,7 @@ function useDataTableRenderer({
|
|
|
3649
3820
|
BodyRow,
|
|
3650
3821
|
dataInPage,
|
|
3651
3822
|
handleSortChange,
|
|
3823
|
+
handleAutoFit,
|
|
3652
3824
|
isAllSelected,
|
|
3653
3825
|
isTotalSelected,
|
|
3654
3826
|
isSelectedRow: (0, import_react25.useCallback)((model) => selectedModelSet.has(model), [selectedModelSet]),
|
|
@@ -4079,6 +4251,7 @@ function Component(props, apiRef) {
|
|
|
4079
4251
|
pageSize,
|
|
4080
4252
|
onPaginationModelChange,
|
|
4081
4253
|
handleSortChange,
|
|
4254
|
+
handleAutoFit,
|
|
4082
4255
|
dataInPage,
|
|
4083
4256
|
isTotalSelected,
|
|
4084
4257
|
focusedRowId,
|
|
@@ -4357,6 +4530,7 @@ function Component(props, apiRef) {
|
|
|
4357
4530
|
stickyHeader: props.stickyHeader,
|
|
4358
4531
|
editMode: !!c.isCellEditable,
|
|
4359
4532
|
onSortChange: handleSortChange,
|
|
4533
|
+
onAutoFit: handleAutoFit,
|
|
4360
4534
|
...c
|
|
4361
4535
|
}
|
|
4362
4536
|
)
|
package/dist/index.js
CHANGED
|
@@ -1965,6 +1965,36 @@ import React25, {
|
|
|
1965
1965
|
} from "react";
|
|
1966
1966
|
import { useVirtualizer as useVirtualizer2 } from "@tanstack/react-virtual";
|
|
1967
1967
|
|
|
1968
|
+
// src/libs/text-measurer.ts
|
|
1969
|
+
var TextMeasurer = class {
|
|
1970
|
+
constructor(font) {
|
|
1971
|
+
const canvas = document.createElement("canvas");
|
|
1972
|
+
this.ctx = canvas.getContext("2d");
|
|
1973
|
+
if (this.ctx && font) this.ctx.font = font;
|
|
1974
|
+
}
|
|
1975
|
+
setFont(font) {
|
|
1976
|
+
if (this.ctx) this.ctx.font = font;
|
|
1977
|
+
return this;
|
|
1978
|
+
}
|
|
1979
|
+
setFontFromElement(el) {
|
|
1980
|
+
if (this.ctx) this.ctx.font = getComputedStyle(el).font;
|
|
1981
|
+
return this;
|
|
1982
|
+
}
|
|
1983
|
+
measureText(text) {
|
|
1984
|
+
if (!this.ctx) return 0;
|
|
1985
|
+
return this.ctx.measureText(text).width;
|
|
1986
|
+
}
|
|
1987
|
+
measureMaxWidth(values) {
|
|
1988
|
+
if (!this.ctx) return 0;
|
|
1989
|
+
let max = 0;
|
|
1990
|
+
for (let i = 0; i < values.length; i++) {
|
|
1991
|
+
const w = this.ctx.measureText(values[i]).width;
|
|
1992
|
+
if (w > max) max = w;
|
|
1993
|
+
}
|
|
1994
|
+
return max;
|
|
1995
|
+
}
|
|
1996
|
+
};
|
|
1997
|
+
|
|
1968
1998
|
// src/components/DataTable/utils.ts
|
|
1969
1999
|
function extractFieldsFromGroupingModel(items) {
|
|
1970
2000
|
const fields = /* @__PURE__ */ new Set();
|
|
@@ -2086,6 +2116,71 @@ function getTextAlign(props) {
|
|
|
2086
2116
|
return !props.editMode && ["number", "date", "currency"].includes(props.type || "") ? "end" : "start";
|
|
2087
2117
|
}
|
|
2088
2118
|
var numberFormatter = (value) => "Intl" in window ? new Intl.NumberFormat().format(value) : value;
|
|
2119
|
+
function computeHeaderWidth(headerEl) {
|
|
2120
|
+
const thStyle = getComputedStyle(headerEl);
|
|
2121
|
+
const paddingX = parseFloat(thStyle.paddingLeft) + parseFloat(thStyle.paddingRight);
|
|
2122
|
+
const borderX = parseFloat(thStyle.borderLeftWidth) + parseFloat(thStyle.borderRightWidth);
|
|
2123
|
+
const stack = headerEl.firstElementChild;
|
|
2124
|
+
if (!stack) return paddingX;
|
|
2125
|
+
const stackStyle = getComputedStyle(stack);
|
|
2126
|
+
const gap = parseFloat(stackStyle.gap) || parseFloat(stackStyle.columnGap) || 0;
|
|
2127
|
+
let totalChildWidth = 0;
|
|
2128
|
+
let visibleChildCount = 0;
|
|
2129
|
+
for (const child of Array.from(stack.children)) {
|
|
2130
|
+
const el = child;
|
|
2131
|
+
if (!el.offsetWidth && !el.offsetHeight) continue;
|
|
2132
|
+
visibleChildCount++;
|
|
2133
|
+
const textEl = el.querySelector?.('[data-slot="header-text"]') || (el.dataset?.slot === "header-text" ? el : null);
|
|
2134
|
+
if (textEl) {
|
|
2135
|
+
const htmlEl = textEl;
|
|
2136
|
+
const isMultiLine = getComputedStyle(htmlEl).display === "-webkit-box";
|
|
2137
|
+
if (isMultiLine) {
|
|
2138
|
+
const measurer = new TextMeasurer();
|
|
2139
|
+
measurer.setFontFromElement(htmlEl);
|
|
2140
|
+
totalChildWidth += measurer.measureText(htmlEl.textContent || "");
|
|
2141
|
+
} else {
|
|
2142
|
+
totalChildWidth += htmlEl.scrollWidth;
|
|
2143
|
+
}
|
|
2144
|
+
} else {
|
|
2145
|
+
totalChildWidth += el.offsetWidth;
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
const totalGaps = visibleChildCount > 1 ? (visibleChildCount - 1) * gap : 0;
|
|
2149
|
+
return totalChildWidth + totalGaps + paddingX + borderX;
|
|
2150
|
+
}
|
|
2151
|
+
function computeBodyWidth(headerEl, table, field, dataInPage) {
|
|
2152
|
+
const headId = headerEl.id;
|
|
2153
|
+
const sampleTd = headId ? table.querySelector(`tbody td[headers="${CSS.escape(headId)}"]`) : null;
|
|
2154
|
+
const styleSource = sampleTd || headerEl;
|
|
2155
|
+
const tdStyle = getComputedStyle(styleSource);
|
|
2156
|
+
const bodyPaddingX = parseFloat(tdStyle.paddingLeft) + parseFloat(tdStyle.paddingRight);
|
|
2157
|
+
const bodyBorderX = parseFloat(tdStyle.borderLeftWidth) + parseFloat(tdStyle.borderRightWidth);
|
|
2158
|
+
const measurer = new TextMeasurer();
|
|
2159
|
+
measurer.setFont(tdStyle.font);
|
|
2160
|
+
const texts = [];
|
|
2161
|
+
for (let i = 0; i < dataInPage.length; i++) {
|
|
2162
|
+
const val = dataInPage[i][field];
|
|
2163
|
+
texts.push(val == null ? "" : String(val));
|
|
2164
|
+
}
|
|
2165
|
+
const maxTextWidth = measurer.measureMaxWidth(texts);
|
|
2166
|
+
return maxTextWidth + bodyPaddingX + bodyBorderX;
|
|
2167
|
+
}
|
|
2168
|
+
function computeAutoFitWidth(params) {
|
|
2169
|
+
const { headerEl, field, dataInPage } = params;
|
|
2170
|
+
const table = headerEl.closest("table");
|
|
2171
|
+
if (!table) return null;
|
|
2172
|
+
const headerWidth = computeHeaderWidth(headerEl);
|
|
2173
|
+
const bodyWidth = computeBodyWidth(headerEl, table, field, dataInPage);
|
|
2174
|
+
let finalWidth = Math.ceil(Math.max(headerWidth, bodyWidth));
|
|
2175
|
+
const thStyle = getComputedStyle(headerEl);
|
|
2176
|
+
const resolvedMin = thStyle.minWidth;
|
|
2177
|
+
const resolvedMax = thStyle.maxWidth;
|
|
2178
|
+
const minPx = resolvedMin !== "none" ? parseFloat(resolvedMin) : NaN;
|
|
2179
|
+
const maxPx = resolvedMax !== "none" ? parseFloat(resolvedMax) : NaN;
|
|
2180
|
+
if (!isNaN(minPx) && minPx > 0) finalWidth = Math.max(finalWidth, minPx);
|
|
2181
|
+
if (!isNaN(maxPx) && maxPx > 0) finalWidth = Math.min(finalWidth, maxPx);
|
|
2182
|
+
return finalWidth;
|
|
2183
|
+
}
|
|
2089
2184
|
|
|
2090
2185
|
// src/components/DataTable/styled.tsx
|
|
2091
2186
|
import React17 from "react";
|
|
@@ -2196,7 +2291,7 @@ var StyledTd = styled8("td")(({ theme }) => ({
|
|
|
2196
2291
|
var MotionSortIcon = motion17(SortIcon);
|
|
2197
2292
|
var DefaultLoadingOverlay = () => /* @__PURE__ */ React17.createElement(LinearProgress, { value: 8, variant: "plain" });
|
|
2198
2293
|
var DefaultNoRowsOverlay = () => /* @__PURE__ */ React17.createElement(Typography3, { level: "body-sm", textColor: "text.tertiary" }, "No rows");
|
|
2199
|
-
var Resizer = (ref, targetRef = ref, onResizeStateChange) => /* @__PURE__ */ React17.createElement(
|
|
2294
|
+
var Resizer = (ref, targetRef = ref, onResizeStateChange, onAutoFit) => /* @__PURE__ */ React17.createElement(
|
|
2200
2295
|
Box_default,
|
|
2201
2296
|
{
|
|
2202
2297
|
sx: {
|
|
@@ -2204,24 +2299,67 @@ var Resizer = (ref, targetRef = ref, onResizeStateChange) => /* @__PURE__ */ Rea
|
|
|
2204
2299
|
top: 0,
|
|
2205
2300
|
right: 0,
|
|
2206
2301
|
bottom: 0,
|
|
2207
|
-
width: "
|
|
2208
|
-
cursor: "col-resize"
|
|
2302
|
+
width: "7px",
|
|
2303
|
+
cursor: "col-resize",
|
|
2304
|
+
"&::after": {
|
|
2305
|
+
content: '""',
|
|
2306
|
+
position: "absolute",
|
|
2307
|
+
top: 0,
|
|
2308
|
+
bottom: 0,
|
|
2309
|
+
right: 0,
|
|
2310
|
+
width: "2px",
|
|
2311
|
+
bgcolor: "transparent",
|
|
2312
|
+
transition: "background-color 150ms ease"
|
|
2313
|
+
},
|
|
2314
|
+
"&:hover::after": {
|
|
2315
|
+
bgcolor: "primary.300"
|
|
2316
|
+
},
|
|
2317
|
+
"&[data-resizing]::after": {
|
|
2318
|
+
bgcolor: "primary.500"
|
|
2319
|
+
}
|
|
2209
2320
|
},
|
|
2210
2321
|
onClick: (e) => e.stopPropagation(),
|
|
2322
|
+
onDoubleClick: (e) => {
|
|
2323
|
+
e.stopPropagation();
|
|
2324
|
+
e.preventDefault();
|
|
2325
|
+
onAutoFit?.();
|
|
2326
|
+
},
|
|
2211
2327
|
onMouseDown: (e) => {
|
|
2328
|
+
if (e.detail >= 2) return;
|
|
2329
|
+
const resizerEl = e.currentTarget;
|
|
2330
|
+
resizerEl.dataset.resizing = "";
|
|
2212
2331
|
const initialX = e.clientX;
|
|
2213
2332
|
const initialWidth = ref.current?.getBoundingClientRect().width;
|
|
2214
|
-
|
|
2333
|
+
let activated = false;
|
|
2334
|
+
const DRAG_THRESHOLD = 3;
|
|
2335
|
+
const thStyle = ref.current ? getComputedStyle(ref.current) : null;
|
|
2336
|
+
const minW = thStyle ? parseFloat(thStyle.minWidth) : NaN;
|
|
2337
|
+
const maxW = thStyle ? parseFloat(thStyle.maxWidth) : NaN;
|
|
2215
2338
|
const onMouseMove = (e2) => {
|
|
2216
|
-
if (initialWidth
|
|
2217
|
-
|
|
2218
|
-
|
|
2339
|
+
if (!initialWidth) return;
|
|
2340
|
+
const delta = e2.clientX - initialX;
|
|
2341
|
+
if (!activated) {
|
|
2342
|
+
if (Math.abs(delta) < DRAG_THRESHOLD) return;
|
|
2343
|
+
activated = true;
|
|
2344
|
+
onResizeStateChange?.(true);
|
|
2219
2345
|
}
|
|
2346
|
+
if (!ref.current || !targetRef.current) {
|
|
2347
|
+
onMouseUp();
|
|
2348
|
+
return;
|
|
2349
|
+
}
|
|
2350
|
+
let newWidth = initialWidth + delta;
|
|
2351
|
+
if (!isNaN(minW) && minW > 0) newWidth = Math.max(newWidth, minW);
|
|
2352
|
+
if (!isNaN(maxW) && maxW > 0) newWidth = Math.min(newWidth, maxW);
|
|
2353
|
+
ref.current.style.width = `${newWidth}px`;
|
|
2354
|
+
targetRef.current.style.width = `${newWidth}px`;
|
|
2220
2355
|
};
|
|
2221
2356
|
const onMouseUp = () => {
|
|
2357
|
+
resizerEl.removeAttribute("data-resizing");
|
|
2222
2358
|
document.removeEventListener("mousemove", onMouseMove);
|
|
2223
2359
|
document.removeEventListener("mouseup", onMouseUp);
|
|
2224
|
-
|
|
2360
|
+
if (activated) {
|
|
2361
|
+
requestAnimationFrame(() => onResizeStateChange?.(false));
|
|
2362
|
+
}
|
|
2225
2363
|
};
|
|
2226
2364
|
document.addEventListener("mousemove", onMouseMove);
|
|
2227
2365
|
document.addEventListener("mouseup", onMouseUp);
|
|
@@ -2803,7 +2941,11 @@ function InfoSign(props) {
|
|
|
2803
2941
|
var InfoSign_default = InfoSign;
|
|
2804
2942
|
|
|
2805
2943
|
// src/components/DataTable/components.tsx
|
|
2806
|
-
var TextEllipsis = ({
|
|
2944
|
+
var TextEllipsis = ({
|
|
2945
|
+
children,
|
|
2946
|
+
lineClamp,
|
|
2947
|
+
...rest
|
|
2948
|
+
}) => {
|
|
2807
2949
|
const textRef = useRef5(null);
|
|
2808
2950
|
const [showTooltip, setShowTooltip] = useState8(false);
|
|
2809
2951
|
useLayoutEffect(() => {
|
|
@@ -2818,7 +2960,7 @@ var TextEllipsis = ({ children, lineClamp }) => {
|
|
|
2818
2960
|
ro.observe(element);
|
|
2819
2961
|
return () => ro.disconnect();
|
|
2820
2962
|
}, [children, lineClamp]);
|
|
2821
|
-
return /* @__PURE__ */ React22.createElement(Tooltip_default, { title: showTooltip ? children : "", placement: "top" }, /* @__PURE__ */ React22.createElement(EllipsisDiv, { ref: textRef, lineClamp }, children));
|
|
2963
|
+
return /* @__PURE__ */ React22.createElement(Tooltip_default, { title: showTooltip ? children : "", placement: "top" }, /* @__PURE__ */ React22.createElement(EllipsisDiv, { ref: textRef, lineClamp, ...rest }, children));
|
|
2822
2964
|
};
|
|
2823
2965
|
var CellTextEllipsis = ({ children }) => {
|
|
2824
2966
|
const textRef = useRef5(null);
|
|
@@ -2870,11 +3012,16 @@ var HeadCell = (props) => {
|
|
|
2870
3012
|
headerRef,
|
|
2871
3013
|
tableColRef,
|
|
2872
3014
|
headerClassName,
|
|
2873
|
-
headerLineClamp
|
|
3015
|
+
headerLineClamp,
|
|
3016
|
+
onAutoFit
|
|
2874
3017
|
} = props;
|
|
2875
3018
|
const theme = useTheme();
|
|
2876
3019
|
const ref = headerRef;
|
|
2877
3020
|
const colRef = tableColRef;
|
|
3021
|
+
const localRef = useRef5(null);
|
|
3022
|
+
useLayoutEffect(() => {
|
|
3023
|
+
ref.current = localRef.current;
|
|
3024
|
+
}, [ref]);
|
|
2878
3025
|
const [isHovered, setIsHovered] = useState8(false);
|
|
2879
3026
|
const sortable = type === "actions" ? false : _sortable;
|
|
2880
3027
|
const headId = useMemo8(
|
|
@@ -2883,10 +3030,15 @@ var HeadCell = (props) => {
|
|
|
2883
3030
|
);
|
|
2884
3031
|
const isResizingRef = useRef5(false);
|
|
2885
3032
|
const resizer = useMemo8(
|
|
2886
|
-
() => resizable ?? true ? Resizer(
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
3033
|
+
() => resizable ?? true ? Resizer(
|
|
3034
|
+
ref,
|
|
3035
|
+
colRef,
|
|
3036
|
+
(isResizing) => {
|
|
3037
|
+
isResizingRef.current = isResizing;
|
|
3038
|
+
},
|
|
3039
|
+
onAutoFit ? () => onAutoFit(field) : void 0
|
|
3040
|
+
) : null,
|
|
3041
|
+
[resizable, ref, colRef, onAutoFit, field]
|
|
2890
3042
|
);
|
|
2891
3043
|
const style = useMemo8(
|
|
2892
3044
|
() => ({
|
|
@@ -2957,7 +3109,7 @@ var HeadCell = (props) => {
|
|
|
2957
3109
|
id: headId,
|
|
2958
3110
|
"aria-label": headerName ?? field,
|
|
2959
3111
|
"aria-sort": sort ? { asc: "ascending", desc: "descending" }[sort] : "none",
|
|
2960
|
-
ref,
|
|
3112
|
+
ref: localRef,
|
|
2961
3113
|
key: field,
|
|
2962
3114
|
style,
|
|
2963
3115
|
onClick: useCallback9(
|
|
@@ -2974,7 +3126,7 @@ var HeadCell = (props) => {
|
|
|
2974
3126
|
initial: "initial",
|
|
2975
3127
|
className: computedHeaderClassName
|
|
2976
3128
|
},
|
|
2977
|
-
/* @__PURE__ */ React22.createElement(Stack_default, { gap: 1, direction: "row", justifyContent: textAlign, alignItems: "center", sx: { minWidth: 0 } }, textAlign === "end" && sortIcon, textAlign === "end" && infoSign, /* @__PURE__ */ React22.createElement(TextEllipsis, { lineClamp: headerLineClamp }, headerName ?? field, editMode && required && /* @__PURE__ */ React22.createElement(Asterisk, null, "*")), textAlign === "start" && infoSign, textAlign === "start" && sortIcon),
|
|
3129
|
+
/* @__PURE__ */ React22.createElement(Stack_default, { gap: 1, direction: "row", justifyContent: textAlign, alignItems: "center", sx: { minWidth: 0 } }, textAlign === "end" && sortIcon, textAlign === "end" && infoSign, /* @__PURE__ */ React22.createElement(TextEllipsis, { lineClamp: headerLineClamp, "data-slot": "header-text" }, headerName ?? field, editMode && required && /* @__PURE__ */ React22.createElement(Asterisk, null, "*")), textAlign === "start" && infoSign, textAlign === "start" && sortIcon),
|
|
2978
3130
|
resizer
|
|
2979
3131
|
);
|
|
2980
3132
|
};
|
|
@@ -3521,6 +3673,25 @@ function useDataTableRenderer({
|
|
|
3521
3673
|
prevRowsRef.current = _rows;
|
|
3522
3674
|
}
|
|
3523
3675
|
}, [_rows]);
|
|
3676
|
+
const handleAutoFit = useCallback10(
|
|
3677
|
+
(field) => {
|
|
3678
|
+
const colDef = visibleColumnsByField[field];
|
|
3679
|
+
if (!colDef?.headerRef.current) return;
|
|
3680
|
+
const column = allColumnsByField[field];
|
|
3681
|
+
const columnType = column && "type" in column ? column.type : void 0;
|
|
3682
|
+
if (columnType === "actions") return;
|
|
3683
|
+
const optimalWidth = computeAutoFitWidth({
|
|
3684
|
+
headerEl: colDef.headerRef.current,
|
|
3685
|
+
field,
|
|
3686
|
+
dataInPage
|
|
3687
|
+
});
|
|
3688
|
+
if (optimalWidth == null) return;
|
|
3689
|
+
const widthPx = `${optimalWidth}px`;
|
|
3690
|
+
colDef.headerRef.current.style.width = widthPx;
|
|
3691
|
+
if (colDef.tableColRef.current) colDef.tableColRef.current.style.width = widthPx;
|
|
3692
|
+
},
|
|
3693
|
+
[visibleColumnsByField, allColumnsByField, dataInPage]
|
|
3694
|
+
);
|
|
3524
3695
|
return {
|
|
3525
3696
|
rowCount,
|
|
3526
3697
|
selectableRowCount,
|
|
@@ -3532,6 +3703,7 @@ function useDataTableRenderer({
|
|
|
3532
3703
|
BodyRow,
|
|
3533
3704
|
dataInPage,
|
|
3534
3705
|
handleSortChange,
|
|
3706
|
+
handleAutoFit,
|
|
3535
3707
|
isAllSelected,
|
|
3536
3708
|
isTotalSelected,
|
|
3537
3709
|
isSelectedRow: useCallback10((model) => selectedModelSet.has(model), [selectedModelSet]),
|
|
@@ -3962,6 +4134,7 @@ function Component(props, apiRef) {
|
|
|
3962
4134
|
pageSize,
|
|
3963
4135
|
onPaginationModelChange,
|
|
3964
4136
|
handleSortChange,
|
|
4137
|
+
handleAutoFit,
|
|
3965
4138
|
dataInPage,
|
|
3966
4139
|
isTotalSelected,
|
|
3967
4140
|
focusedRowId,
|
|
@@ -4240,6 +4413,7 @@ function Component(props, apiRef) {
|
|
|
4240
4413
|
stickyHeader: props.stickyHeader,
|
|
4241
4414
|
editMode: !!c.isCellEditable,
|
|
4242
4415
|
onSortChange: handleSortChange,
|
|
4416
|
+
onAutoFit: handleAutoFit,
|
|
4243
4417
|
...c
|
|
4244
4418
|
}
|
|
4245
4419
|
)
|