@ceed/ads 1.28.2 → 1.29.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/index.cjs CHANGED
@@ -2089,6 +2089,36 @@ var CurrencyInput_default = CurrencyInput;
2089
2089
  var import_react28 = __toESM(require("react"));
2090
2090
  var import_react_virtual2 = require("@tanstack/react-virtual");
2091
2091
 
2092
+ // src/libs/text-measurer.ts
2093
+ var TextMeasurer = class {
2094
+ constructor(font) {
2095
+ const canvas = document.createElement("canvas");
2096
+ this.ctx = canvas.getContext("2d");
2097
+ if (this.ctx && font) this.ctx.font = font;
2098
+ }
2099
+ setFont(font) {
2100
+ if (this.ctx) this.ctx.font = font;
2101
+ return this;
2102
+ }
2103
+ setFontFromElement(el) {
2104
+ if (this.ctx) this.ctx.font = getComputedStyle(el).font;
2105
+ return this;
2106
+ }
2107
+ measureText(text) {
2108
+ if (!this.ctx) return 0;
2109
+ return this.ctx.measureText(text).width;
2110
+ }
2111
+ measureMaxWidth(values) {
2112
+ if (!this.ctx) return 0;
2113
+ let max = 0;
2114
+ for (let i = 0; i < values.length; i++) {
2115
+ const w = this.ctx.measureText(values[i]).width;
2116
+ if (w > max) max = w;
2117
+ }
2118
+ return max;
2119
+ }
2120
+ };
2121
+
2092
2122
  // src/components/DataTable/utils.ts
2093
2123
  function extractFieldsFromGroupingModel(items) {
2094
2124
  const fields = /* @__PURE__ */ new Set();
@@ -2210,6 +2240,71 @@ function getTextAlign(props) {
2210
2240
  return !props.editMode && ["number", "date", "currency"].includes(props.type || "") ? "end" : "start";
2211
2241
  }
2212
2242
  var numberFormatter = (value) => "Intl" in window ? new Intl.NumberFormat().format(value) : value;
2243
+ function computeHeaderWidth(headerEl) {
2244
+ const thStyle = getComputedStyle(headerEl);
2245
+ const paddingX = parseFloat(thStyle.paddingLeft) + parseFloat(thStyle.paddingRight);
2246
+ const borderX = parseFloat(thStyle.borderLeftWidth) + parseFloat(thStyle.borderRightWidth);
2247
+ const stack = headerEl.firstElementChild;
2248
+ if (!stack) return paddingX;
2249
+ const stackStyle = getComputedStyle(stack);
2250
+ const gap = parseFloat(stackStyle.gap) || parseFloat(stackStyle.columnGap) || 0;
2251
+ let totalChildWidth = 0;
2252
+ let visibleChildCount = 0;
2253
+ for (const child of Array.from(stack.children)) {
2254
+ const el = child;
2255
+ if (!el.offsetWidth && !el.offsetHeight) continue;
2256
+ visibleChildCount++;
2257
+ const textEl = el.querySelector?.('[data-slot="header-text"]') || (el.dataset?.slot === "header-text" ? el : null);
2258
+ if (textEl) {
2259
+ const htmlEl = textEl;
2260
+ const isMultiLine = getComputedStyle(htmlEl).display === "-webkit-box";
2261
+ if (isMultiLine) {
2262
+ const measurer = new TextMeasurer();
2263
+ measurer.setFontFromElement(htmlEl);
2264
+ totalChildWidth += measurer.measureText(htmlEl.textContent || "");
2265
+ } else {
2266
+ totalChildWidth += htmlEl.scrollWidth;
2267
+ }
2268
+ } else {
2269
+ totalChildWidth += el.offsetWidth;
2270
+ }
2271
+ }
2272
+ const totalGaps = visibleChildCount > 1 ? (visibleChildCount - 1) * gap : 0;
2273
+ return totalChildWidth + totalGaps + paddingX + borderX;
2274
+ }
2275
+ function computeBodyWidth(headerEl, table, field, dataInPage) {
2276
+ const headId = headerEl.id;
2277
+ const sampleTd = headId ? table.querySelector(`tbody td[headers="${CSS.escape(headId)}"]`) : null;
2278
+ const styleSource = sampleTd || headerEl;
2279
+ const tdStyle = getComputedStyle(styleSource);
2280
+ const bodyPaddingX = parseFloat(tdStyle.paddingLeft) + parseFloat(tdStyle.paddingRight);
2281
+ const bodyBorderX = parseFloat(tdStyle.borderLeftWidth) + parseFloat(tdStyle.borderRightWidth);
2282
+ const measurer = new TextMeasurer();
2283
+ measurer.setFont(tdStyle.font);
2284
+ const texts = [];
2285
+ for (let i = 0; i < dataInPage.length; i++) {
2286
+ const val = dataInPage[i][field];
2287
+ texts.push(val == null ? "" : String(val));
2288
+ }
2289
+ const maxTextWidth = measurer.measureMaxWidth(texts);
2290
+ return maxTextWidth + bodyPaddingX + bodyBorderX;
2291
+ }
2292
+ function computeAutoFitWidth(params) {
2293
+ const { headerEl, field, dataInPage } = params;
2294
+ const table = headerEl.closest("table");
2295
+ if (!table) return null;
2296
+ const headerWidth = computeHeaderWidth(headerEl);
2297
+ const bodyWidth = computeBodyWidth(headerEl, table, field, dataInPage);
2298
+ let finalWidth = Math.ceil(Math.max(headerWidth, bodyWidth));
2299
+ const thStyle = getComputedStyle(headerEl);
2300
+ const resolvedMin = thStyle.minWidth;
2301
+ const resolvedMax = thStyle.maxWidth;
2302
+ const minPx = resolvedMin !== "none" ? parseFloat(resolvedMin) : NaN;
2303
+ const maxPx = resolvedMax !== "none" ? parseFloat(resolvedMax) : NaN;
2304
+ if (!isNaN(minPx) && minPx > 0) finalWidth = Math.max(finalWidth, minPx);
2305
+ if (!isNaN(maxPx) && maxPx > 0) finalWidth = Math.min(finalWidth, maxPx);
2306
+ return finalWidth;
2307
+ }
2213
2308
 
2214
2309
  // src/components/DataTable/styled.tsx
2215
2310
  var import_react19 = __toESM(require("react"));
@@ -2320,7 +2415,7 @@ var StyledTd = (0, import_joy25.styled)("td")(({ theme }) => ({
2320
2415
  var MotionSortIcon = (0, import_framer_motion17.motion)(import_ArrowUpwardRounded.default);
2321
2416
  var DefaultLoadingOverlay = () => /* @__PURE__ */ import_react19.default.createElement(import_joy25.LinearProgress, { value: 8, variant: "plain" });
2322
2417
  var DefaultNoRowsOverlay = () => /* @__PURE__ */ import_react19.default.createElement(import_joy25.Typography, { level: "body-sm", textColor: "text.tertiary" }, "No rows");
2323
- var Resizer = (ref, targetRef = ref, onResizeStateChange) => /* @__PURE__ */ import_react19.default.createElement(
2418
+ var Resizer = (ref, targetRef = ref, onResizeStateChange, onAutoFit) => /* @__PURE__ */ import_react19.default.createElement(
2324
2419
  Box_default,
2325
2420
  {
2326
2421
  sx: {
@@ -2328,24 +2423,67 @@ var Resizer = (ref, targetRef = ref, onResizeStateChange) => /* @__PURE__ */ imp
2328
2423
  top: 0,
2329
2424
  right: 0,
2330
2425
  bottom: 0,
2331
- width: "4px",
2332
- cursor: "col-resize"
2426
+ width: "7px",
2427
+ cursor: "col-resize",
2428
+ "&::after": {
2429
+ content: '""',
2430
+ position: "absolute",
2431
+ top: 0,
2432
+ bottom: 0,
2433
+ right: 0,
2434
+ width: "2px",
2435
+ bgcolor: "transparent",
2436
+ transition: "background-color 150ms ease"
2437
+ },
2438
+ "&:hover::after": {
2439
+ bgcolor: "primary.300"
2440
+ },
2441
+ "&[data-resizing]::after": {
2442
+ bgcolor: "primary.500"
2443
+ }
2333
2444
  },
2334
2445
  onClick: (e) => e.stopPropagation(),
2446
+ onDoubleClick: (e) => {
2447
+ e.stopPropagation();
2448
+ e.preventDefault();
2449
+ onAutoFit?.();
2450
+ },
2335
2451
  onMouseDown: (e) => {
2452
+ if (e.detail >= 2) return;
2453
+ const resizerEl = e.currentTarget;
2454
+ resizerEl.dataset.resizing = "";
2336
2455
  const initialX = e.clientX;
2337
2456
  const initialWidth = ref.current?.getBoundingClientRect().width;
2338
- onResizeStateChange?.(true);
2457
+ let activated = false;
2458
+ const DRAG_THRESHOLD = 3;
2459
+ const thStyle = ref.current ? getComputedStyle(ref.current) : null;
2460
+ const minW = thStyle ? parseFloat(thStyle.minWidth) : NaN;
2461
+ const maxW = thStyle ? parseFloat(thStyle.maxWidth) : NaN;
2339
2462
  const onMouseMove = (e2) => {
2340
- if (initialWidth && initialX) {
2341
- ref.current.style.width = `${initialWidth + (e2.clientX - initialX)}px`;
2342
- targetRef.current.style.width = `${initialWidth + (e2.clientX - initialX)}px`;
2463
+ if (!initialWidth) return;
2464
+ const delta = e2.clientX - initialX;
2465
+ if (!activated) {
2466
+ if (Math.abs(delta) < DRAG_THRESHOLD) return;
2467
+ activated = true;
2468
+ onResizeStateChange?.(true);
2469
+ }
2470
+ if (!ref.current || !targetRef.current) {
2471
+ onMouseUp();
2472
+ return;
2343
2473
  }
2474
+ let newWidth = initialWidth + delta;
2475
+ if (!isNaN(minW) && minW > 0) newWidth = Math.max(newWidth, minW);
2476
+ if (!isNaN(maxW) && maxW > 0) newWidth = Math.min(newWidth, maxW);
2477
+ ref.current.style.width = `${newWidth}px`;
2478
+ targetRef.current.style.width = `${newWidth}px`;
2344
2479
  };
2345
2480
  const onMouseUp = () => {
2481
+ resizerEl.removeAttribute("data-resizing");
2346
2482
  document.removeEventListener("mousemove", onMouseMove);
2347
2483
  document.removeEventListener("mouseup", onMouseUp);
2348
- requestAnimationFrame(() => onResizeStateChange?.(false));
2484
+ if (activated) {
2485
+ requestAnimationFrame(() => onResizeStateChange?.(false));
2486
+ }
2349
2487
  };
2350
2488
  document.addEventListener("mousemove", onMouseMove);
2351
2489
  document.addEventListener("mouseup", onMouseUp);
@@ -2918,7 +3056,11 @@ function InfoSign(props) {
2918
3056
  var InfoSign_default = InfoSign;
2919
3057
 
2920
3058
  // src/components/DataTable/components.tsx
2921
- var TextEllipsis = ({ children, lineClamp }) => {
3059
+ var TextEllipsis = ({
3060
+ children,
3061
+ lineClamp,
3062
+ ...rest
3063
+ }) => {
2922
3064
  const textRef = (0, import_react24.useRef)(null);
2923
3065
  const [showTooltip, setShowTooltip] = (0, import_react24.useState)(false);
2924
3066
  (0, import_react24.useLayoutEffect)(() => {
@@ -2933,7 +3075,7 @@ var TextEllipsis = ({ children, lineClamp }) => {
2933
3075
  ro.observe(element);
2934
3076
  return () => ro.disconnect();
2935
3077
  }, [children, lineClamp]);
2936
- return /* @__PURE__ */ import_react24.default.createElement(Tooltip_default, { title: showTooltip ? children : "", placement: "top" }, /* @__PURE__ */ import_react24.default.createElement(EllipsisDiv, { ref: textRef, lineClamp }, children));
3078
+ return /* @__PURE__ */ import_react24.default.createElement(Tooltip_default, { title: showTooltip ? children : "", placement: "top" }, /* @__PURE__ */ import_react24.default.createElement(EllipsisDiv, { ref: textRef, lineClamp, ...rest }, children));
2937
3079
  };
2938
3080
  var CellTextEllipsis = ({ children }) => {
2939
3081
  const textRef = (0, import_react24.useRef)(null);
@@ -2985,7 +3127,8 @@ var HeadCell = (props) => {
2985
3127
  headerRef,
2986
3128
  tableColRef,
2987
3129
  headerClassName,
2988
- headerLineClamp
3130
+ headerLineClamp,
3131
+ onAutoFit
2989
3132
  } = props;
2990
3133
  const theme = (0, import_joy32.useTheme)();
2991
3134
  const ref = headerRef;
@@ -3002,10 +3145,15 @@ var HeadCell = (props) => {
3002
3145
  );
3003
3146
  const isResizingRef = (0, import_react24.useRef)(false);
3004
3147
  const resizer = (0, import_react24.useMemo)(
3005
- () => resizable ?? true ? Resizer(ref, colRef, (isResizing) => {
3006
- isResizingRef.current = isResizing;
3007
- }) : null,
3008
- [resizable, ref, colRef]
3148
+ () => resizable ?? true ? Resizer(
3149
+ ref,
3150
+ colRef,
3151
+ (isResizing) => {
3152
+ isResizingRef.current = isResizing;
3153
+ },
3154
+ onAutoFit ? () => onAutoFit(field) : void 0
3155
+ ) : null,
3156
+ [resizable, ref, colRef, onAutoFit, field]
3009
3157
  );
3010
3158
  const style = (0, import_react24.useMemo)(
3011
3159
  () => ({
@@ -3093,7 +3241,7 @@ var HeadCell = (props) => {
3093
3241
  initial: "initial",
3094
3242
  className: computedHeaderClassName
3095
3243
  },
3096
- /* @__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),
3244
+ /* @__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),
3097
3245
  resizer
3098
3246
  );
3099
3247
  };
@@ -3640,6 +3788,25 @@ function useDataTableRenderer({
3640
3788
  prevRowsRef.current = _rows;
3641
3789
  }
3642
3790
  }, [_rows]);
3791
+ const handleAutoFit = (0, import_react25.useCallback)(
3792
+ (field) => {
3793
+ const colDef = visibleColumnsByField[field];
3794
+ if (!colDef?.headerRef.current) return;
3795
+ const column = allColumnsByField[field];
3796
+ const columnType = column && "type" in column ? column.type : void 0;
3797
+ if (columnType === "actions") return;
3798
+ const optimalWidth = computeAutoFitWidth({
3799
+ headerEl: colDef.headerRef.current,
3800
+ field,
3801
+ dataInPage
3802
+ });
3803
+ if (optimalWidth == null) return;
3804
+ const widthPx = `${optimalWidth}px`;
3805
+ colDef.headerRef.current.style.width = widthPx;
3806
+ if (colDef.tableColRef.current) colDef.tableColRef.current.style.width = widthPx;
3807
+ },
3808
+ [visibleColumnsByField, allColumnsByField, dataInPage]
3809
+ );
3643
3810
  return {
3644
3811
  rowCount,
3645
3812
  selectableRowCount,
@@ -3651,6 +3818,7 @@ function useDataTableRenderer({
3651
3818
  BodyRow,
3652
3819
  dataInPage,
3653
3820
  handleSortChange,
3821
+ handleAutoFit,
3654
3822
  isAllSelected,
3655
3823
  isTotalSelected,
3656
3824
  isSelectedRow: (0, import_react25.useCallback)((model) => selectedModelSet.has(model), [selectedModelSet]),
@@ -4081,6 +4249,7 @@ function Component(props, apiRef) {
4081
4249
  pageSize,
4082
4250
  onPaginationModelChange,
4083
4251
  handleSortChange,
4252
+ handleAutoFit,
4084
4253
  dataInPage,
4085
4254
  isTotalSelected,
4086
4255
  focusedRowId,
@@ -4359,6 +4528,7 @@ function Component(props, apiRef) {
4359
4528
  stickyHeader: props.stickyHeader,
4360
4529
  editMode: !!c.isCellEditable,
4361
4530
  onSortChange: handleSortChange,
4531
+ onAutoFit: handleAutoFit,
4362
4532
  ...c
4363
4533
  }
4364
4534
  )
package/dist/index.js CHANGED
@@ -1955,6 +1955,36 @@ import React25, {
1955
1955
  } from "react";
1956
1956
  import { useVirtualizer as useVirtualizer2 } from "@tanstack/react-virtual";
1957
1957
 
1958
+ // src/libs/text-measurer.ts
1959
+ var TextMeasurer = class {
1960
+ constructor(font) {
1961
+ const canvas = document.createElement("canvas");
1962
+ this.ctx = canvas.getContext("2d");
1963
+ if (this.ctx && font) this.ctx.font = font;
1964
+ }
1965
+ setFont(font) {
1966
+ if (this.ctx) this.ctx.font = font;
1967
+ return this;
1968
+ }
1969
+ setFontFromElement(el) {
1970
+ if (this.ctx) this.ctx.font = getComputedStyle(el).font;
1971
+ return this;
1972
+ }
1973
+ measureText(text) {
1974
+ if (!this.ctx) return 0;
1975
+ return this.ctx.measureText(text).width;
1976
+ }
1977
+ measureMaxWidth(values) {
1978
+ if (!this.ctx) return 0;
1979
+ let max = 0;
1980
+ for (let i = 0; i < values.length; i++) {
1981
+ const w = this.ctx.measureText(values[i]).width;
1982
+ if (w > max) max = w;
1983
+ }
1984
+ return max;
1985
+ }
1986
+ };
1987
+
1958
1988
  // src/components/DataTable/utils.ts
1959
1989
  function extractFieldsFromGroupingModel(items) {
1960
1990
  const fields = /* @__PURE__ */ new Set();
@@ -2076,6 +2106,71 @@ function getTextAlign(props) {
2076
2106
  return !props.editMode && ["number", "date", "currency"].includes(props.type || "") ? "end" : "start";
2077
2107
  }
2078
2108
  var numberFormatter = (value) => "Intl" in window ? new Intl.NumberFormat().format(value) : value;
2109
+ function computeHeaderWidth(headerEl) {
2110
+ const thStyle = getComputedStyle(headerEl);
2111
+ const paddingX = parseFloat(thStyle.paddingLeft) + parseFloat(thStyle.paddingRight);
2112
+ const borderX = parseFloat(thStyle.borderLeftWidth) + parseFloat(thStyle.borderRightWidth);
2113
+ const stack = headerEl.firstElementChild;
2114
+ if (!stack) return paddingX;
2115
+ const stackStyle = getComputedStyle(stack);
2116
+ const gap = parseFloat(stackStyle.gap) || parseFloat(stackStyle.columnGap) || 0;
2117
+ let totalChildWidth = 0;
2118
+ let visibleChildCount = 0;
2119
+ for (const child of Array.from(stack.children)) {
2120
+ const el = child;
2121
+ if (!el.offsetWidth && !el.offsetHeight) continue;
2122
+ visibleChildCount++;
2123
+ const textEl = el.querySelector?.('[data-slot="header-text"]') || (el.dataset?.slot === "header-text" ? el : null);
2124
+ if (textEl) {
2125
+ const htmlEl = textEl;
2126
+ const isMultiLine = getComputedStyle(htmlEl).display === "-webkit-box";
2127
+ if (isMultiLine) {
2128
+ const measurer = new TextMeasurer();
2129
+ measurer.setFontFromElement(htmlEl);
2130
+ totalChildWidth += measurer.measureText(htmlEl.textContent || "");
2131
+ } else {
2132
+ totalChildWidth += htmlEl.scrollWidth;
2133
+ }
2134
+ } else {
2135
+ totalChildWidth += el.offsetWidth;
2136
+ }
2137
+ }
2138
+ const totalGaps = visibleChildCount > 1 ? (visibleChildCount - 1) * gap : 0;
2139
+ return totalChildWidth + totalGaps + paddingX + borderX;
2140
+ }
2141
+ function computeBodyWidth(headerEl, table, field, dataInPage) {
2142
+ const headId = headerEl.id;
2143
+ const sampleTd = headId ? table.querySelector(`tbody td[headers="${CSS.escape(headId)}"]`) : null;
2144
+ const styleSource = sampleTd || headerEl;
2145
+ const tdStyle = getComputedStyle(styleSource);
2146
+ const bodyPaddingX = parseFloat(tdStyle.paddingLeft) + parseFloat(tdStyle.paddingRight);
2147
+ const bodyBorderX = parseFloat(tdStyle.borderLeftWidth) + parseFloat(tdStyle.borderRightWidth);
2148
+ const measurer = new TextMeasurer();
2149
+ measurer.setFont(tdStyle.font);
2150
+ const texts = [];
2151
+ for (let i = 0; i < dataInPage.length; i++) {
2152
+ const val = dataInPage[i][field];
2153
+ texts.push(val == null ? "" : String(val));
2154
+ }
2155
+ const maxTextWidth = measurer.measureMaxWidth(texts);
2156
+ return maxTextWidth + bodyPaddingX + bodyBorderX;
2157
+ }
2158
+ function computeAutoFitWidth(params) {
2159
+ const { headerEl, field, dataInPage } = params;
2160
+ const table = headerEl.closest("table");
2161
+ if (!table) return null;
2162
+ const headerWidth = computeHeaderWidth(headerEl);
2163
+ const bodyWidth = computeBodyWidth(headerEl, table, field, dataInPage);
2164
+ let finalWidth = Math.ceil(Math.max(headerWidth, bodyWidth));
2165
+ const thStyle = getComputedStyle(headerEl);
2166
+ const resolvedMin = thStyle.minWidth;
2167
+ const resolvedMax = thStyle.maxWidth;
2168
+ const minPx = resolvedMin !== "none" ? parseFloat(resolvedMin) : NaN;
2169
+ const maxPx = resolvedMax !== "none" ? parseFloat(resolvedMax) : NaN;
2170
+ if (!isNaN(minPx) && minPx > 0) finalWidth = Math.max(finalWidth, minPx);
2171
+ if (!isNaN(maxPx) && maxPx > 0) finalWidth = Math.min(finalWidth, maxPx);
2172
+ return finalWidth;
2173
+ }
2079
2174
 
2080
2175
  // src/components/DataTable/styled.tsx
2081
2176
  import React17 from "react";
@@ -2186,7 +2281,7 @@ var StyledTd = styled8("td")(({ theme }) => ({
2186
2281
  var MotionSortIcon = motion17(SortIcon);
2187
2282
  var DefaultLoadingOverlay = () => /* @__PURE__ */ React17.createElement(LinearProgress, { value: 8, variant: "plain" });
2188
2283
  var DefaultNoRowsOverlay = () => /* @__PURE__ */ React17.createElement(Typography3, { level: "body-sm", textColor: "text.tertiary" }, "No rows");
2189
- var Resizer = (ref, targetRef = ref, onResizeStateChange) => /* @__PURE__ */ React17.createElement(
2284
+ var Resizer = (ref, targetRef = ref, onResizeStateChange, onAutoFit) => /* @__PURE__ */ React17.createElement(
2190
2285
  Box_default,
2191
2286
  {
2192
2287
  sx: {
@@ -2194,24 +2289,67 @@ var Resizer = (ref, targetRef = ref, onResizeStateChange) => /* @__PURE__ */ Rea
2194
2289
  top: 0,
2195
2290
  right: 0,
2196
2291
  bottom: 0,
2197
- width: "4px",
2198
- cursor: "col-resize"
2292
+ width: "7px",
2293
+ cursor: "col-resize",
2294
+ "&::after": {
2295
+ content: '""',
2296
+ position: "absolute",
2297
+ top: 0,
2298
+ bottom: 0,
2299
+ right: 0,
2300
+ width: "2px",
2301
+ bgcolor: "transparent",
2302
+ transition: "background-color 150ms ease"
2303
+ },
2304
+ "&:hover::after": {
2305
+ bgcolor: "primary.300"
2306
+ },
2307
+ "&[data-resizing]::after": {
2308
+ bgcolor: "primary.500"
2309
+ }
2199
2310
  },
2200
2311
  onClick: (e) => e.stopPropagation(),
2312
+ onDoubleClick: (e) => {
2313
+ e.stopPropagation();
2314
+ e.preventDefault();
2315
+ onAutoFit?.();
2316
+ },
2201
2317
  onMouseDown: (e) => {
2318
+ if (e.detail >= 2) return;
2319
+ const resizerEl = e.currentTarget;
2320
+ resizerEl.dataset.resizing = "";
2202
2321
  const initialX = e.clientX;
2203
2322
  const initialWidth = ref.current?.getBoundingClientRect().width;
2204
- onResizeStateChange?.(true);
2323
+ let activated = false;
2324
+ const DRAG_THRESHOLD = 3;
2325
+ const thStyle = ref.current ? getComputedStyle(ref.current) : null;
2326
+ const minW = thStyle ? parseFloat(thStyle.minWidth) : NaN;
2327
+ const maxW = thStyle ? parseFloat(thStyle.maxWidth) : NaN;
2205
2328
  const onMouseMove = (e2) => {
2206
- if (initialWidth && initialX) {
2207
- ref.current.style.width = `${initialWidth + (e2.clientX - initialX)}px`;
2208
- targetRef.current.style.width = `${initialWidth + (e2.clientX - initialX)}px`;
2329
+ if (!initialWidth) return;
2330
+ const delta = e2.clientX - initialX;
2331
+ if (!activated) {
2332
+ if (Math.abs(delta) < DRAG_THRESHOLD) return;
2333
+ activated = true;
2334
+ onResizeStateChange?.(true);
2335
+ }
2336
+ if (!ref.current || !targetRef.current) {
2337
+ onMouseUp();
2338
+ return;
2209
2339
  }
2340
+ let newWidth = initialWidth + delta;
2341
+ if (!isNaN(minW) && minW > 0) newWidth = Math.max(newWidth, minW);
2342
+ if (!isNaN(maxW) && maxW > 0) newWidth = Math.min(newWidth, maxW);
2343
+ ref.current.style.width = `${newWidth}px`;
2344
+ targetRef.current.style.width = `${newWidth}px`;
2210
2345
  };
2211
2346
  const onMouseUp = () => {
2347
+ resizerEl.removeAttribute("data-resizing");
2212
2348
  document.removeEventListener("mousemove", onMouseMove);
2213
2349
  document.removeEventListener("mouseup", onMouseUp);
2214
- requestAnimationFrame(() => onResizeStateChange?.(false));
2350
+ if (activated) {
2351
+ requestAnimationFrame(() => onResizeStateChange?.(false));
2352
+ }
2215
2353
  };
2216
2354
  document.addEventListener("mousemove", onMouseMove);
2217
2355
  document.addEventListener("mouseup", onMouseUp);
@@ -2793,7 +2931,11 @@ function InfoSign(props) {
2793
2931
  var InfoSign_default = InfoSign;
2794
2932
 
2795
2933
  // src/components/DataTable/components.tsx
2796
- var TextEllipsis = ({ children, lineClamp }) => {
2934
+ var TextEllipsis = ({
2935
+ children,
2936
+ lineClamp,
2937
+ ...rest
2938
+ }) => {
2797
2939
  const textRef = useRef5(null);
2798
2940
  const [showTooltip, setShowTooltip] = useState8(false);
2799
2941
  useLayoutEffect(() => {
@@ -2808,7 +2950,7 @@ var TextEllipsis = ({ children, lineClamp }) => {
2808
2950
  ro.observe(element);
2809
2951
  return () => ro.disconnect();
2810
2952
  }, [children, lineClamp]);
2811
- return /* @__PURE__ */ React22.createElement(Tooltip_default, { title: showTooltip ? children : "", placement: "top" }, /* @__PURE__ */ React22.createElement(EllipsisDiv, { ref: textRef, lineClamp }, children));
2953
+ return /* @__PURE__ */ React22.createElement(Tooltip_default, { title: showTooltip ? children : "", placement: "top" }, /* @__PURE__ */ React22.createElement(EllipsisDiv, { ref: textRef, lineClamp, ...rest }, children));
2812
2954
  };
2813
2955
  var CellTextEllipsis = ({ children }) => {
2814
2956
  const textRef = useRef5(null);
@@ -2860,7 +3002,8 @@ var HeadCell = (props) => {
2860
3002
  headerRef,
2861
3003
  tableColRef,
2862
3004
  headerClassName,
2863
- headerLineClamp
3005
+ headerLineClamp,
3006
+ onAutoFit
2864
3007
  } = props;
2865
3008
  const theme = useTheme();
2866
3009
  const ref = headerRef;
@@ -2877,10 +3020,15 @@ var HeadCell = (props) => {
2877
3020
  );
2878
3021
  const isResizingRef = useRef5(false);
2879
3022
  const resizer = useMemo8(
2880
- () => resizable ?? true ? Resizer(ref, colRef, (isResizing) => {
2881
- isResizingRef.current = isResizing;
2882
- }) : null,
2883
- [resizable, ref, colRef]
3023
+ () => resizable ?? true ? Resizer(
3024
+ ref,
3025
+ colRef,
3026
+ (isResizing) => {
3027
+ isResizingRef.current = isResizing;
3028
+ },
3029
+ onAutoFit ? () => onAutoFit(field) : void 0
3030
+ ) : null,
3031
+ [resizable, ref, colRef, onAutoFit, field]
2884
3032
  );
2885
3033
  const style = useMemo8(
2886
3034
  () => ({
@@ -2968,7 +3116,7 @@ var HeadCell = (props) => {
2968
3116
  initial: "initial",
2969
3117
  className: computedHeaderClassName
2970
3118
  },
2971
- /* @__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),
3119
+ /* @__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),
2972
3120
  resizer
2973
3121
  );
2974
3122
  };
@@ -3515,6 +3663,25 @@ function useDataTableRenderer({
3515
3663
  prevRowsRef.current = _rows;
3516
3664
  }
3517
3665
  }, [_rows]);
3666
+ const handleAutoFit = useCallback10(
3667
+ (field) => {
3668
+ const colDef = visibleColumnsByField[field];
3669
+ if (!colDef?.headerRef.current) return;
3670
+ const column = allColumnsByField[field];
3671
+ const columnType = column && "type" in column ? column.type : void 0;
3672
+ if (columnType === "actions") return;
3673
+ const optimalWidth = computeAutoFitWidth({
3674
+ headerEl: colDef.headerRef.current,
3675
+ field,
3676
+ dataInPage
3677
+ });
3678
+ if (optimalWidth == null) return;
3679
+ const widthPx = `${optimalWidth}px`;
3680
+ colDef.headerRef.current.style.width = widthPx;
3681
+ if (colDef.tableColRef.current) colDef.tableColRef.current.style.width = widthPx;
3682
+ },
3683
+ [visibleColumnsByField, allColumnsByField, dataInPage]
3684
+ );
3518
3685
  return {
3519
3686
  rowCount,
3520
3687
  selectableRowCount,
@@ -3526,6 +3693,7 @@ function useDataTableRenderer({
3526
3693
  BodyRow,
3527
3694
  dataInPage,
3528
3695
  handleSortChange,
3696
+ handleAutoFit,
3529
3697
  isAllSelected,
3530
3698
  isTotalSelected,
3531
3699
  isSelectedRow: useCallback10((model) => selectedModelSet.has(model), [selectedModelSet]),
@@ -3956,6 +4124,7 @@ function Component(props, apiRef) {
3956
4124
  pageSize,
3957
4125
  onPaginationModelChange,
3958
4126
  handleSortChange,
4127
+ handleAutoFit,
3959
4128
  dataInPage,
3960
4129
  isTotalSelected,
3961
4130
  focusedRowId,
@@ -4234,6 +4403,7 @@ function Component(props, apiRef) {
4234
4403
  stickyHeader: props.stickyHeader,
4235
4404
  editMode: !!c.isCellEditable,
4236
4405
  onSortChange: handleSortChange,
4406
+ onAutoFit: handleAutoFit,
4237
4407
  ...c
4238
4408
  }
4239
4409
  )
@@ -0,0 +1,8 @@
1
+ export declare class TextMeasurer {
2
+ private ctx;
3
+ constructor(font?: string);
4
+ setFont(font: string): this;
5
+ setFontFromElement(el: Element): this;
6
+ measureText(text: string): number;
7
+ measureMaxWidth(values: string[]): number;
8
+ }