@rowakit/table 0.3.0 → 0.4.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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useState, useRef, useEffect } from 'react';
2
- import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
 
4
4
  // src/column-helpers.ts
5
5
  function text(field, options) {
@@ -118,6 +118,158 @@ function getRowKey(row, rowKey) {
118
118
  function getHeaderLabel(column) {
119
119
  return column.header ?? column.id;
120
120
  }
121
+ function validateViewName(name) {
122
+ const trimmed = name.trim();
123
+ if (trimmed.length === 0) {
124
+ return { valid: false, error: "Name cannot be empty" };
125
+ }
126
+ if (trimmed.length > 40) {
127
+ return { valid: false, error: "Name cannot exceed 40 characters" };
128
+ }
129
+ const invalidChars = /[/\\?%*:|"<>\x00-\x1f\x7f]/;
130
+ if (invalidChars.test(trimmed)) {
131
+ return { valid: false, error: "Name contains invalid characters" };
132
+ }
133
+ return { valid: true };
134
+ }
135
+ function getSavedViewsIndex() {
136
+ if (typeof window === "undefined" || !window.localStorage) {
137
+ return [];
138
+ }
139
+ try {
140
+ const indexStr = localStorage.getItem("rowakit-views-index");
141
+ if (indexStr) {
142
+ const index = JSON.parse(indexStr);
143
+ if (Array.isArray(index)) {
144
+ return index;
145
+ }
146
+ }
147
+ } catch {
148
+ }
149
+ const rebuilt = [];
150
+ try {
151
+ for (let i = 0; i < localStorage.length; i++) {
152
+ const key = localStorage.key(i);
153
+ if (key?.startsWith("rowakit-view-")) {
154
+ const name = key.substring("rowakit-view-".length);
155
+ rebuilt.push({
156
+ name,
157
+ updatedAt: Date.now()
158
+ });
159
+ }
160
+ }
161
+ } catch {
162
+ }
163
+ return rebuilt;
164
+ }
165
+ function setSavedViewsIndex(index) {
166
+ if (typeof window === "undefined" || !window.localStorage) {
167
+ return;
168
+ }
169
+ try {
170
+ localStorage.setItem("rowakit-views-index", JSON.stringify(index));
171
+ } catch {
172
+ }
173
+ }
174
+ function loadSavedViewsFromStorage() {
175
+ if (typeof window === "undefined" || !window.localStorage) {
176
+ return [];
177
+ }
178
+ const index = getSavedViewsIndex();
179
+ const views = [];
180
+ for (const entry of index) {
181
+ try {
182
+ const viewStr = localStorage.getItem(`rowakit-view-${entry.name}`);
183
+ if (viewStr) {
184
+ const state = JSON.parse(viewStr);
185
+ views.push({
186
+ name: entry.name,
187
+ state
188
+ });
189
+ }
190
+ } catch {
191
+ }
192
+ }
193
+ return views;
194
+ }
195
+ function parseUrlState(params, defaultPageSize, pageSizeOptions) {
196
+ const pageStr = params.get("page");
197
+ let page = 1;
198
+ if (pageStr) {
199
+ const parsed = parseInt(pageStr, 10);
200
+ page = !isNaN(parsed) && parsed >= 1 ? parsed : 1;
201
+ }
202
+ const pageSizeStr = params.get("pageSize");
203
+ let pageSize = defaultPageSize;
204
+ if (pageSizeStr) {
205
+ const parsed = parseInt(pageSizeStr, 10);
206
+ if (!isNaN(parsed) && parsed >= 1) {
207
+ if (pageSizeOptions && pageSizeOptions.length > 0) {
208
+ pageSize = pageSizeOptions.includes(parsed) ? parsed : defaultPageSize;
209
+ } else {
210
+ pageSize = parsed;
211
+ }
212
+ }
213
+ }
214
+ const result = { page, pageSize };
215
+ const sortField = params.get("sortField");
216
+ const sortDir = params.get("sortDirection");
217
+ if (sortField && (sortDir === "asc" || sortDir === "desc")) {
218
+ result.sort = { field: sortField, direction: sortDir };
219
+ }
220
+ const filtersStr = params.get("filters");
221
+ if (filtersStr) {
222
+ try {
223
+ const parsed = JSON.parse(filtersStr);
224
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
225
+ result.filters = parsed;
226
+ }
227
+ } catch {
228
+ }
229
+ }
230
+ const widthsStr = params.get("columnWidths");
231
+ if (widthsStr) {
232
+ try {
233
+ const parsed = JSON.parse(widthsStr);
234
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
235
+ const widths = {};
236
+ for (const [key, value] of Object.entries(parsed)) {
237
+ if (typeof value === "number" && value > 0) {
238
+ widths[key] = value;
239
+ }
240
+ }
241
+ if (Object.keys(widths).length > 0) {
242
+ result.columnWidths = widths;
243
+ }
244
+ }
245
+ } catch {
246
+ }
247
+ }
248
+ return result;
249
+ }
250
+ function serializeUrlState(query, filters, columnWidths, defaultPageSize, enableColumnResizing) {
251
+ const params = new URLSearchParams();
252
+ params.set("page", String(query.page));
253
+ if (query.pageSize !== defaultPageSize) {
254
+ params.set("pageSize", String(query.pageSize));
255
+ }
256
+ if (query.sort) {
257
+ params.set("sortField", query.sort.field);
258
+ params.set("sortDirection", query.sort.direction);
259
+ }
260
+ if (filters && Object.keys(filters).length > 0) {
261
+ const nonEmptyFilters = Object.fromEntries(
262
+ Object.entries(filters).filter(([, v]) => v !== void 0)
263
+ );
264
+ if (Object.keys(nonEmptyFilters).length > 0) {
265
+ params.set("filters", JSON.stringify(nonEmptyFilters));
266
+ }
267
+ }
268
+ if (enableColumnResizing && Object.keys(columnWidths).length > 0) {
269
+ params.set("columnWidths", JSON.stringify(columnWidths));
270
+ }
271
+ return params.toString();
272
+ }
121
273
  function renderCell(column, row, isLoading, setConfirmState) {
122
274
  switch (column.kind) {
123
275
  case "text": {
@@ -234,60 +386,96 @@ function RowaKitTable({
234
386
  const resizeRafRef = useRef(null);
235
387
  const resizePendingRef = useRef(null);
236
388
  const tableRef = useRef(null);
389
+ const isResizingRef = useRef(false);
390
+ const lastResizeEndTsRef = useRef(0);
391
+ const resizingColIdRef = useRef(null);
392
+ const didHydrateUrlRef = useRef(false);
393
+ const didSkipInitialUrlSyncRef = useRef(false);
394
+ const urlSyncDebounceRef = useRef(null);
237
395
  const [savedViews, setSavedViews] = useState([]);
396
+ const [showSaveViewForm, setShowSaveViewForm] = useState(false);
397
+ const [saveViewInput, setSaveViewInput] = useState("");
398
+ const [saveViewError, setSaveViewError] = useState("");
399
+ const [overwriteConfirmName, setOverwriteConfirmName] = useState(null);
400
+ useEffect(() => {
401
+ if (!enableSavedViews) return;
402
+ const views = loadSavedViewsFromStorage();
403
+ setSavedViews(views);
404
+ }, [enableSavedViews]);
238
405
  const [confirmState, setConfirmState] = useState(null);
239
406
  const requestIdRef = useRef(0);
240
407
  useEffect(() => {
241
- if (!syncToUrl) return;
242
- const params = new URLSearchParams();
243
- params.set("page", String(query.page));
244
- params.set("pageSize", String(query.pageSize));
245
- if (query.sort) {
246
- params.set("sortField", query.sort.field);
247
- params.set("sortDirection", query.sort.direction);
408
+ if (!syncToUrl) {
409
+ didSkipInitialUrlSyncRef.current = false;
410
+ return;
248
411
  }
249
- if (query.filters && Object.keys(query.filters).length > 0) {
250
- params.set("filters", JSON.stringify(query.filters));
412
+ if (!didSkipInitialUrlSyncRef.current) {
413
+ didSkipInitialUrlSyncRef.current = true;
414
+ return;
251
415
  }
252
- if (enableColumnResizing && Object.keys(columnWidths).length > 0) {
253
- params.set("columnWidths", JSON.stringify(columnWidths));
416
+ if (urlSyncDebounceRef.current) {
417
+ clearTimeout(urlSyncDebounceRef.current);
418
+ urlSyncDebounceRef.current = null;
254
419
  }
255
- window.history.replaceState(null, "", `?${params.toString()}`);
256
- }, [query, columnWidths, syncToUrl, enableColumnResizing]);
420
+ const urlStr = serializeUrlState(query, filters, columnWidths, defaultPageSize, enableColumnResizing);
421
+ const qs = urlStr ? `?${urlStr}` : "";
422
+ window.history.replaceState(null, "", `${window.location.pathname}${qs}${window.location.hash}`);
423
+ }, [query, filters, syncToUrl, enableColumnResizing, defaultPageSize, columnWidths]);
257
424
  useEffect(() => {
258
- if (!syncToUrl) return;
259
- const params = new URLSearchParams(window.location.search);
260
- const page = parseInt(params.get("page") ?? "1", 10);
261
- const pageSize = parseInt(params.get("pageSize") ?? String(defaultPageSize), 10);
262
- const sortField = params.get("sortField");
263
- const sortDirection = params.get("sortDirection");
264
- const filtersStr = params.get("filters");
265
- const columnWidthsStr = params.get("columnWidths");
266
- const newQuery = {
267
- page: Math.max(1, page),
268
- pageSize: Math.max(1, pageSize)
269
- };
270
- if (sortField && sortDirection) {
271
- newQuery.sort = { field: sortField, direction: sortDirection };
425
+ if (!syncToUrl || !enableColumnResizing) return;
426
+ if (!didSkipInitialUrlSyncRef.current) return;
427
+ if (urlSyncDebounceRef.current) {
428
+ clearTimeout(urlSyncDebounceRef.current);
272
429
  }
273
- if (filtersStr) {
274
- try {
275
- const parsedFilters = JSON.parse(filtersStr);
276
- if (parsedFilters && typeof parsedFilters === "object") {
277
- setFilters(parsedFilters);
278
- newQuery.filters = parsedFilters;
279
- }
280
- } catch {
430
+ urlSyncDebounceRef.current = setTimeout(() => {
431
+ const urlStr = serializeUrlState(query, filters, columnWidths, defaultPageSize, enableColumnResizing);
432
+ const qs = urlStr ? `?${urlStr}` : "";
433
+ window.history.replaceState(null, "", `${window.location.pathname}${qs}${window.location.hash}`);
434
+ urlSyncDebounceRef.current = null;
435
+ }, 150);
436
+ return () => {
437
+ if (urlSyncDebounceRef.current) {
438
+ clearTimeout(urlSyncDebounceRef.current);
439
+ urlSyncDebounceRef.current = null;
281
440
  }
441
+ };
442
+ }, [columnWidths, syncToUrl, enableColumnResizing, query, filters, defaultPageSize]);
443
+ useEffect(() => {
444
+ if (!syncToUrl) {
445
+ didHydrateUrlRef.current = false;
446
+ return;
282
447
  }
283
- if (enableColumnResizing && columnWidthsStr) {
284
- try {
285
- setColumnWidths(JSON.parse(columnWidthsStr));
286
- } catch {
448
+ if (didHydrateUrlRef.current) return;
449
+ didHydrateUrlRef.current = true;
450
+ const params = new URLSearchParams(window.location.search);
451
+ const parsed = parseUrlState(params, defaultPageSize, pageSizeOptions);
452
+ setQuery({
453
+ page: parsed.page,
454
+ pageSize: parsed.pageSize,
455
+ sort: parsed.sort,
456
+ filters: parsed.filters
457
+ });
458
+ if (parsed.filters) {
459
+ setFilters(parsed.filters);
460
+ }
461
+ if (parsed.columnWidths && enableColumnResizing) {
462
+ const clamped = {};
463
+ for (const [colId, rawWidth] of Object.entries(parsed.columnWidths)) {
464
+ const widthNum = typeof rawWidth === "number" ? rawWidth : Number(rawWidth);
465
+ if (!Number.isFinite(widthNum)) continue;
466
+ const colDef = columns.find((c) => c.id === colId);
467
+ if (!colDef) continue;
468
+ const minW = colDef.minWidth ?? 80;
469
+ const maxW = colDef.maxWidth;
470
+ let finalW = Math.max(minW, widthNum);
471
+ if (maxW != null) {
472
+ finalW = Math.min(finalW, maxW);
473
+ }
474
+ clamped[colId] = finalW;
287
475
  }
476
+ setColumnWidths(clamped);
288
477
  }
289
- setQuery(newQuery);
290
- }, [syncToUrl, defaultPageSize, enableColumnResizing]);
478
+ }, [syncToUrl, defaultPageSize, enableColumnResizing, pageSizeOptions, columns]);
291
479
  useEffect(() => {
292
480
  if (!enableFilters) return;
293
481
  const activeFilters = {};
@@ -390,15 +578,52 @@ function RowaKitTable({
390
578
  if (maxWidth) {
391
579
  finalWidth = Math.min(finalWidth, maxWidth);
392
580
  }
581
+ if (columnWidths[columnId] === finalWidth) {
582
+ return;
583
+ }
393
584
  setColumnWidths((prev) => ({
394
585
  ...prev,
395
586
  [columnId]: finalWidth
396
587
  }));
397
588
  };
589
+ const autoFitColumnWidth = (columnId) => {
590
+ const tableEl = tableRef.current;
591
+ if (!tableEl) return;
592
+ const th = tableEl.querySelector(`th[data-col-id="${columnId}"]`);
593
+ if (!th) return;
594
+ const tds = Array.from(
595
+ tableEl.querySelectorAll(`td[data-col-id="${columnId}"]`)
596
+ );
597
+ const headerW = th.scrollWidth;
598
+ const cellsMaxW = tds.reduce((max, td) => Math.max(max, td.scrollWidth), 0);
599
+ const padding = 24;
600
+ const raw = Math.max(headerW, cellsMaxW) + padding;
601
+ const colDef = columns.find((c) => c.id === columnId);
602
+ const minW = colDef?.minWidth ?? 80;
603
+ const maxW = colDef?.maxWidth ?? 600;
604
+ const finalW = Math.max(minW, Math.min(raw, maxW));
605
+ setColumnWidths((prev) => ({ ...prev, [columnId]: finalW }));
606
+ };
398
607
  const startColumnResize = (e, columnId) => {
399
608
  e.preventDefault();
609
+ e.stopPropagation();
610
+ if (e.detail === 2) {
611
+ autoFitColumnWidth(columnId);
612
+ return;
613
+ }
614
+ if (e.pointerType === "mouse" && e.buttons !== 1) {
615
+ return;
616
+ }
617
+ const target = e.currentTarget;
618
+ const pointerId = e.pointerId;
619
+ try {
620
+ target.setPointerCapture(pointerId);
621
+ } catch {
622
+ }
623
+ isResizingRef.current = true;
624
+ resizingColIdRef.current = columnId;
400
625
  const startX = e.clientX;
401
- const th = e.currentTarget.parentElement;
626
+ const th = target.parentElement;
402
627
  let startWidth = columnWidths[columnId] ?? th.offsetWidth;
403
628
  const MIN_DRAG_WIDTH = 80;
404
629
  if (startWidth < MIN_DRAG_WIDTH) {
@@ -410,33 +635,38 @@ function RowaKitTable({
410
635
  }
411
636
  }
412
637
  document.body.classList.add("rowakit-resizing");
413
- const handleMouseMove = (moveEvent) => {
638
+ const handlePointerMove = (moveEvent) => {
414
639
  const delta = moveEvent.clientX - startX;
415
640
  const newWidth = startWidth + delta;
416
641
  scheduleColumnWidthUpdate(columnId, newWidth);
417
642
  };
418
- const handleMouseUp = () => {
419
- document.removeEventListener("mousemove", handleMouseMove);
420
- document.removeEventListener("mouseup", handleMouseUp);
643
+ const cleanupResize = () => {
644
+ target.removeEventListener("pointermove", handlePointerMove);
645
+ target.removeEventListener("pointerup", handlePointerUp);
646
+ target.removeEventListener("pointercancel", handlePointerCancel);
421
647
  document.body.classList.remove("rowakit-resizing");
648
+ isResizingRef.current = false;
649
+ resizingColIdRef.current = null;
650
+ lastResizeEndTsRef.current = Date.now();
651
+ try {
652
+ target.releasePointerCapture(pointerId);
653
+ } catch {
654
+ }
655
+ };
656
+ const handlePointerUp = () => {
657
+ cleanupResize();
658
+ };
659
+ const handlePointerCancel = () => {
660
+ cleanupResize();
422
661
  };
423
- document.addEventListener("mousemove", handleMouseMove);
424
- document.addEventListener("mouseup", handleMouseUp);
662
+ target.addEventListener("pointermove", handlePointerMove);
663
+ target.addEventListener("pointerup", handlePointerUp);
664
+ target.addEventListener("pointercancel", handlePointerCancel);
425
665
  };
426
- const handleColumnResizeDoubleClick = (columnId) => {
427
- const tableEl = tableRef.current;
428
- if (!tableEl) return;
429
- const th = tableEl.querySelector(`th[data-col-id="${columnId}"]`);
430
- if (!th) return;
431
- const tds = Array.from(tableEl.querySelectorAll(`td[data-col-id="${columnId}"]`));
432
- const headerW = th.scrollWidth;
433
- const cellsMaxW = tds.reduce((max, td) => Math.max(max, td.scrollWidth), 0);
434
- const padding = 24;
435
- const raw = Math.max(headerW, cellsMaxW) + padding;
436
- const minW = columns.find((c) => c.id === columnId)?.minWidth ?? 80;
437
- const maxW = columns.find((c) => c.id === columnId)?.maxWidth ?? 600;
438
- const finalW = Math.max(minW, Math.min(raw, maxW));
439
- setColumnWidths((prev) => ({ ...prev, [columnId]: finalW }));
666
+ const handleColumnResizeDoubleClick = (e, columnId) => {
667
+ e.preventDefault();
668
+ e.stopPropagation();
669
+ autoFitColumnWidth(columnId);
440
670
  };
441
671
  const saveCurrentView = (name) => {
442
672
  const viewState = {
@@ -453,6 +683,10 @@ function RowaKitTable({
453
683
  if (typeof window !== "undefined" && window.localStorage) {
454
684
  try {
455
685
  localStorage.setItem(`rowakit-view-${name}`, JSON.stringify(viewState));
686
+ const index = getSavedViewsIndex();
687
+ const filtered = index.filter((v) => v.name !== name);
688
+ filtered.push({ name, updatedAt: Date.now() });
689
+ setSavedViewsIndex(filtered);
456
690
  } catch {
457
691
  }
458
692
  }
@@ -477,6 +711,9 @@ function RowaKitTable({
477
711
  if (typeof window !== "undefined" && window.localStorage) {
478
712
  try {
479
713
  localStorage.removeItem(`rowakit-view-${name}`);
714
+ const index = getSavedViewsIndex();
715
+ const filtered = index.filter((v) => v.name !== name);
716
+ setSavedViewsIndex(filtered);
480
717
  } catch {
481
718
  }
482
719
  }
@@ -540,22 +777,127 @@ function RowaKitTable({
540
777
  const canGoPrevious = query.page > 1 && !isLoading;
541
778
  const canGoNext = query.page < totalPages && !isLoading;
542
779
  const hasActiveFilters = enableFilters && Object.values(filters).some((v) => v !== void 0);
543
- return /* @__PURE__ */ jsxs("div", { className: `rowakit-table${className ? ` ${className}` : ""}`, children: [
780
+ const containerClass = [
781
+ "rowakit-table",
782
+ enableColumnResizing ? "rowakit-layout-fixed" : "",
783
+ className
784
+ ].filter(Boolean).join(" ");
785
+ return /* @__PURE__ */ jsxs("div", { className: containerClass, children: [
544
786
  enableSavedViews && /* @__PURE__ */ jsxs("div", { className: "rowakit-saved-views-group", children: [
545
- /* @__PURE__ */ jsx(
787
+ !showSaveViewForm ? /* @__PURE__ */ jsx(
546
788
  "button",
547
789
  {
548
790
  onClick: () => {
549
- const name = typeof window !== "undefined" ? window.prompt("Enter view name:") : null;
550
- if (name) {
551
- saveCurrentView(name);
552
- }
791
+ setShowSaveViewForm(true);
792
+ setSaveViewInput("");
793
+ setSaveViewError("");
794
+ setOverwriteConfirmName(null);
553
795
  },
554
796
  className: "rowakit-saved-view-button",
555
797
  type: "button",
556
798
  children: "Save View"
557
799
  }
558
- ),
800
+ ) : /* @__PURE__ */ jsx("div", { className: "rowakit-save-view-form", children: overwriteConfirmName ? /* @__PURE__ */ jsxs("div", { className: "rowakit-save-view-confirm", children: [
801
+ /* @__PURE__ */ jsxs("p", { children: [
802
+ 'View "',
803
+ overwriteConfirmName,
804
+ '" already exists. Overwrite?'
805
+ ] }),
806
+ /* @__PURE__ */ jsx(
807
+ "button",
808
+ {
809
+ onClick: () => {
810
+ saveCurrentView(overwriteConfirmName);
811
+ setShowSaveViewForm(false);
812
+ setSaveViewInput("");
813
+ setSaveViewError("");
814
+ setOverwriteConfirmName(null);
815
+ },
816
+ className: "rowakit-saved-view-button",
817
+ type: "button",
818
+ children: "Overwrite"
819
+ }
820
+ ),
821
+ /* @__PURE__ */ jsx(
822
+ "button",
823
+ {
824
+ onClick: () => {
825
+ setOverwriteConfirmName(null);
826
+ },
827
+ className: "rowakit-saved-view-button",
828
+ type: "button",
829
+ children: "Cancel"
830
+ }
831
+ )
832
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
833
+ /* @__PURE__ */ jsx(
834
+ "input",
835
+ {
836
+ type: "text",
837
+ value: saveViewInput,
838
+ onChange: (e) => {
839
+ setSaveViewInput(e.target.value);
840
+ setSaveViewError("");
841
+ },
842
+ onKeyDown: (e) => {
843
+ if (e.key === "Enter") {
844
+ const validation = validateViewName(saveViewInput);
845
+ if (!validation.valid) {
846
+ setSaveViewError(validation.error || "Invalid name");
847
+ return;
848
+ }
849
+ if (savedViews.some((v) => v.name === saveViewInput.trim())) {
850
+ setOverwriteConfirmName(saveViewInput.trim());
851
+ } else {
852
+ saveCurrentView(saveViewInput.trim());
853
+ setShowSaveViewForm(false);
854
+ setSaveViewInput("");
855
+ setSaveViewError("");
856
+ }
857
+ }
858
+ },
859
+ placeholder: "Enter view name...",
860
+ className: "rowakit-save-view-input"
861
+ }
862
+ ),
863
+ saveViewError && /* @__PURE__ */ jsx("div", { className: "rowakit-save-view-error", children: saveViewError }),
864
+ /* @__PURE__ */ jsx(
865
+ "button",
866
+ {
867
+ onClick: () => {
868
+ const validation = validateViewName(saveViewInput);
869
+ if (!validation.valid) {
870
+ setSaveViewError(validation.error || "Invalid name");
871
+ return;
872
+ }
873
+ if (savedViews.some((v) => v.name === saveViewInput.trim())) {
874
+ setOverwriteConfirmName(saveViewInput.trim());
875
+ } else {
876
+ saveCurrentView(saveViewInput.trim());
877
+ setShowSaveViewForm(false);
878
+ setSaveViewInput("");
879
+ setSaveViewError("");
880
+ }
881
+ },
882
+ className: "rowakit-saved-view-button",
883
+ type: "button",
884
+ children: "Save"
885
+ }
886
+ ),
887
+ /* @__PURE__ */ jsx(
888
+ "button",
889
+ {
890
+ onClick: () => {
891
+ setShowSaveViewForm(false);
892
+ setSaveViewInput("");
893
+ setSaveViewError("");
894
+ },
895
+ className: "rowakit-saved-view-button",
896
+ type: "button",
897
+ children: "Cancel"
898
+ }
899
+ )
900
+ ] }) }),
559
901
  savedViews.map((view) => /* @__PURE__ */ jsxs("div", { className: "rowakit-saved-view-item", children: [
560
902
  /* @__PURE__ */ jsx(
561
903
  "button",
@@ -607,7 +949,11 @@ function RowaKitTable({
607
949
  "th",
608
950
  {
609
951
  "data-col-id": column.id,
610
- onClick: isSortable ? () => handleSort(String(field)) : void 0,
952
+ onClick: isSortable ? () => {
953
+ if (isResizingRef.current) return;
954
+ if (Date.now() - lastResizeEndTsRef.current < 150) return;
955
+ handleSort(String(field));
956
+ } : void 0,
611
957
  role: isSortable ? "button" : void 0,
612
958
  tabIndex: isSortable ? 0 : void 0,
613
959
  onKeyDown: isSortable ? (e) => {
@@ -618,11 +964,15 @@ function RowaKitTable({
618
964
  } : void 0,
619
965
  "aria-sort": isSortable && query.sort?.field === String(field) ? query.sort.direction === "asc" ? "ascending" : "descending" : void 0,
620
966
  style: {
621
- width: actualWidth ? `${actualWidth}px` : void 0,
967
+ width: actualWidth != null ? `${actualWidth}px` : void 0,
622
968
  textAlign: column.align,
623
969
  position: isResizable ? "relative" : void 0
624
970
  },
625
- className: column.truncate && !isResizable ? "rowakit-cell-truncate" : void 0,
971
+ className: [
972
+ column.truncate ? "rowakit-cell-truncate" : "",
973
+ resizingColIdRef.current === column.id ? "resizing" : ""
974
+ // PRD-01
975
+ ].filter(Boolean).join(" ") || void 0,
626
976
  children: [
627
977
  getHeaderLabel(column),
628
978
  isSortable && getSortIndicator(String(field)),
@@ -630,8 +980,8 @@ function RowaKitTable({
630
980
  "div",
631
981
  {
632
982
  className: "rowakit-column-resize-handle",
633
- onMouseDown: (e) => startColumnResize(e, column.id),
634
- onDoubleClick: () => handleColumnResizeDoubleClick(column.id),
983
+ onPointerDown: (e) => startColumnResize(e, column.id),
984
+ onDoubleClick: (e) => handleColumnResizeDoubleClick(e, column.id),
635
985
  title: "Drag to resize | Double-click to auto-fit content"
636
986
  }
637
987
  )
@@ -830,14 +1180,14 @@ function RowaKitTable({
830
1180
  column.kind === "number" ? "rowakit-cell-number" : "",
831
1181
  column.truncate ? "rowakit-cell-truncate" : ""
832
1182
  ].filter(Boolean).join(" ") || void 0;
833
- const actualWidth = columnWidths[column.id];
1183
+ const actualWidth = columnWidths[column.id] ?? column.width;
834
1184
  return /* @__PURE__ */ jsx(
835
1185
  "td",
836
1186
  {
837
1187
  "data-col-id": column.id,
838
1188
  className: cellClass,
839
1189
  style: {
840
- width: actualWidth ? `${actualWidth}px` : void 0,
1190
+ width: actualWidth != null ? `${actualWidth}px` : void 0,
841
1191
  textAlign: column.align || (column.kind === "number" ? "right" : void 0)
842
1192
  },
843
1193
  children: renderCell(column, row, isLoading, setConfirmState)
@@ -942,7 +1292,7 @@ function RowaKitTable({
942
1292
  var SmartTable = RowaKitTable;
943
1293
 
944
1294
  // src/index.ts
945
- var VERSION = "0.1.0";
1295
+ var VERSION = "0.4.0";
946
1296
 
947
1297
  export { RowaKitTable, SmartTable, VERSION, col };
948
1298
  //# sourceMappingURL=index.js.map