@protobase/ui 0.45.15 → 0.45.17

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.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The width a column dragged `delta` pixels from `start` takes, never below `minColumnWidth`, or below `start` for a
3
+ * column the app made narrower than that, such as one of check marks.
4
+ */
5
+ const resizedWidth = (start, delta) => Math.max(Math.min(64, start), Math.round(start + delta));
6
+ /**
7
+ * `specs` with the widths a resize set them to: those columns take that width whole, even below the minimum width the
8
+ * app gave them, as `resizedWidth` keeps them usable.
9
+ */
10
+ const withResizedWidths = (specs, widths) => specs.map((spec) => spec.id in widths ? {
11
+ ...spec,
12
+ fixed: true,
13
+ width: widths[spec.id],
14
+ minWidth: void 0
15
+ } : spec);
16
+ //#endregion
17
+ export { resizedWidth, withResizedWidths };
@@ -10,6 +10,8 @@ import { useHorizontalScroll } from "./use-horizontal-scroll.js";
10
10
  import { usePinnedColumns } from "./use-pinned-columns.js";
11
11
  import { defaultPins } from "./default-pins.js";
12
12
  import { useFittedColumns } from "./use-fitted-columns.js";
13
+ import { withResizedWidths } from "./column-resize.js";
14
+ import { useColumnResize } from "./use-column-resize.js";
13
15
  import { GridHeader } from "./grid-header.js";
14
16
  import { DataRow, SkeletonRow } from "./grid-row.js";
15
17
  import { usePagedRows } from "./use-paged-rows.js";
@@ -94,7 +96,10 @@ const DataGrid = ({ columns: given, source, naturalSort, initialScrollIndex = 0,
94
96
  firstIndex,
95
97
  lastIndex
96
98
  ]);
97
- const sized = useFittedColumns(columns, data);
99
+ const headerRef = useRef(null);
100
+ const resize = useColumnResize(headerRef);
101
+ const fitted = useFittedColumns(columns, data);
102
+ const sized = useMemo(() => withResizedWidths(fitted, resize.widths), [fitted, resize.widths]);
98
103
  const table = useTable({
99
104
  features,
100
105
  columns: tableColumns,
@@ -117,7 +122,6 @@ const DataGrid = ({ columns: given, source, naturalSort, initialScrollIndex = 0,
117
122
  const trailing = rowActions && rowActions.length > 0 ? 40 : 0;
118
123
  const minScale = narrow ? 1 : desktopMinScale;
119
124
  const horizontal = useHorizontalScroll();
120
- const headerRef = useRef(null);
121
125
  const onBodyScroll = () => {
122
126
  horizontal.onScroll();
123
127
  if (headerRef.current && horizontal.ref.current) headerRef.current.scrollLeft = horizontal.ref.current.scrollLeft;
@@ -169,6 +173,8 @@ const DataGrid = ({ columns: given, source, naturalSort, initialScrollIndex = 0,
169
173
  lastPinned,
170
174
  scrolled: horizontal.scrolled,
171
175
  onTogglePin: togglePin,
176
+ resize,
177
+ widths: resize.widths,
172
178
  selectable,
173
179
  selectedCount,
174
180
  onClearSelection: () => setRowSelection({})
@@ -4,6 +4,7 @@ import { text } from "./messages.js";
4
4
  import { gridTemplate } from "./grid-template.js";
5
5
  import { isRightAligned } from "./column-spec.js";
6
6
  import { HeaderMenu, unsortableText } from "./header-menu.js";
7
+ import { ResizeHandle } from "./resize-handle.js";
7
8
  import { ArrowDown, ArrowUp, ChevronsUpDown } from "lucide-react";
8
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
10
  //#region src/data-grid/grid-header.tsx
@@ -19,7 +20,7 @@ const HeaderLabel = ({ spec }) => /* @__PURE__ */ jsxs(Fragment, { children: [/*
19
20
  className: "rounded bg-primary px-1 text-[10px] font-semibold uppercase leading-4 text-primary-foreground",
20
21
  children: text("new")
21
22
  })] });
22
- const GridHeader = ({ cells, specs, gutter, selectedCount, selectable, trailing, minScale, pins = {}, lastPinned, scrolled, onTogglePin, onClearSelection }) => /* @__PURE__ */ jsxs("div", {
23
+ const GridHeader = ({ cells, specs, gutter, selectedCount, selectable, trailing, minScale, pins = {}, lastPinned, scrolled, onTogglePin, resize, widths = {}, onClearSelection }) => /* @__PURE__ */ jsxs("div", {
23
24
  className: "grid h-8 shrink-0 items-center border-b bg-surface text-body font-medium text-muted-foreground",
24
25
  style: { gridTemplateColumns: gridTemplate(specs, gutter, trailing, minScale) },
25
26
  role: "row",
@@ -44,25 +45,34 @@ const GridHeader = ({ cells, specs, gutter, selectedCount, selectable, trailing,
44
45
  return /* @__PURE__ */ jsxs("div", {
45
46
  "data-col-id": spec.id,
46
47
  style: pinned ? { left: pins[spec.id] } : void 0,
47
- className: cn("group/cell flex h-full min-w-0 items-center", pinned && "sticky z-10 bg-inherit", pinned && scrolled && spec.id === lastPinned && "shadow-[1px_0_0_var(--border),4px_0_6px_-2px_rgb(0_0_0/0.12)]", spec.highlight && "bg-primary-soft text-primary-text"),
48
- children: [spec.sortable ? /* @__PURE__ */ jsxs("button", {
49
- type: "button",
50
- onClick: onSort,
51
- className: cn("group/head flex h-full min-w-0 flex-1 items-center gap-1 pl-3 pr-1 text-left", isRightAligned(spec.kind) && "justify-end", "hover:text-foreground", sorted && "text-foreground"),
52
- children: [/* @__PURE__ */ jsx(HeaderLabel, { spec }), /* @__PURE__ */ jsx(SortIcon, { sorted })]
53
- }) : /* @__PURE__ */ jsx("div", {
54
- title: unsortableText(),
55
- className: cn("flex h-full min-w-0 flex-1 items-center gap-1 pl-3 pr-1 text-left", isRightAligned(spec.kind) && "justify-end"),
56
- children: /* @__PURE__ */ jsx(HeaderLabel, { spec })
57
- }), /* @__PURE__ */ jsx("div", {
58
- className: "pr-1.5",
59
- children: /* @__PURE__ */ jsx(HeaderMenu, {
48
+ className: cn("group/cell flex h-full min-w-0 items-center", pinned ? "sticky z-10 bg-inherit" : "relative", pinned && scrolled && spec.id === lastPinned && "shadow-[1px_0_0_var(--border),4px_0_6px_-2px_rgb(0_0_0/0.12)]", spec.highlight && "bg-primary-soft text-primary-text"),
49
+ children: [
50
+ spec.sortable ? /* @__PURE__ */ jsxs("button", {
51
+ type: "button",
52
+ onClick: onSort,
53
+ className: cn("group/head flex h-full min-w-0 flex-1 items-center gap-1 pl-3 pr-1 text-left", isRightAligned(spec.kind) && "justify-end", "hover:text-foreground", sorted && "text-foreground"),
54
+ children: [/* @__PURE__ */ jsx(HeaderLabel, { spec }), /* @__PURE__ */ jsx(SortIcon, { sorted })]
55
+ }) : /* @__PURE__ */ jsx("div", {
56
+ title: unsortableText(),
57
+ className: cn("flex h-full min-w-0 flex-1 items-center gap-1 pl-3 pr-1 text-left", isRightAligned(spec.kind) && "justify-end"),
58
+ children: /* @__PURE__ */ jsx(HeaderLabel, { spec })
59
+ }),
60
+ /* @__PURE__ */ jsx("div", {
61
+ className: "pr-1.5",
62
+ children: /* @__PURE__ */ jsx(HeaderMenu, {
63
+ header: spec.header,
64
+ pinned,
65
+ sortable: Boolean(spec.sortable),
66
+ onTogglePin: () => onTogglePin(spec.id)
67
+ })
68
+ }),
69
+ resize && /* @__PURE__ */ jsx(ResizeHandle, {
70
+ id: spec.id,
60
71
  header: spec.header,
61
- pinned,
62
- sortable: Boolean(spec.sortable),
63
- onTogglePin: () => onTogglePin(spec.id)
72
+ width: widths[spec.id],
73
+ resize
64
74
  })
65
- })]
75
+ ]
66
76
  }, spec.id);
67
77
  }),
68
78
  trailing ? /* @__PURE__ */ jsx("div", { className: "sticky right-0 z-10 h-full bg-inherit" }) : null
@@ -6,6 +6,7 @@ const text = defineMessages({
6
6
  columnMenu: (header) => `${header} column menu`,
7
7
  unpin: "Unpin",
8
8
  pinToLeft: "Pin to left",
9
+ resizeColumn: (header) => `Resize ${header} column`,
9
10
  selected: (count) => `${count} selected`,
10
11
  no: "No",
11
12
  shownIn: (zone) => `Shown in ${zone}`,
@@ -28,6 +29,7 @@ const text = defineMessages({
28
29
  columnMenu: (header) => `Kolommenu ${header}`,
29
30
  unpin: "Losmaken",
30
31
  pinToLeft: "Links vastzetten",
32
+ resizeColumn: (header) => `Breedte kolom ${header}`,
31
33
  selected: (count) => `${count} geselecteerd`,
32
34
  no: "Nee",
33
35
  shownIn: (zone) => `Getoond in ${zone}`,
@@ -0,0 +1,21 @@
1
+ import { text } from "./messages.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+ //#region src/data-grid/resize-handle.tsx
4
+ /**
5
+ * The right edge of a header cell, dragged to make its column wider or narrower. It sits beside the sort button rather
6
+ * than in it, so a drag never sorts.
7
+ */
8
+ const ResizeHandle = ({ id, header, width, resize }) => /* @__PURE__ */ jsx("div", {
9
+ role: "separator",
10
+ "aria-orientation": "vertical",
11
+ "aria-label": text("resizeColumn", header),
12
+ "aria-valuemin": 64,
13
+ "aria-valuenow": width,
14
+ tabIndex: 0,
15
+ onPointerDown: (event) => resize.onPointerDown(id, event),
16
+ onKeyDown: (event) => resize.onKeyDown(id, event),
17
+ className: "group/resize absolute inset-y-0 right-0 z-10 flex w-2 cursor-col-resize touch-none justify-end outline-none",
18
+ children: /* @__PURE__ */ jsx("div", { className: "h-full w-0.5 bg-transparent group-hover/resize:bg-primary group-focus-visible/resize:bg-primary group-active/resize:bg-primary" })
19
+ });
20
+ //#endregion
21
+ export { ResizeHandle };
@@ -0,0 +1,51 @@
1
+ import { resizedWidth } from "./column-resize.js";
2
+ import { useState } from "react";
3
+ //#region src/data-grid/use-column-resize.ts
4
+ /** The widths every header cell has now, by column id, so that resizing one column leaves the others as they are. */
5
+ const measuredWidths = (header) => Object.fromEntries([...header.querySelectorAll("[data-col-id]")].map((cell) => [cell.dataset.colId, cell.offsetWidth]));
6
+ /**
7
+ * Column widths the user sets by dragging a header cell's edge, or with the arrow keys on it, by column id. They live
8
+ * as long as the grid does: a reload or another page shows the app's widths again.
9
+ */
10
+ const useColumnResize = (header) => {
11
+ const [widths, setWidths] = useState({});
12
+ const startWidths = () => ({
13
+ ...header.current ? measuredWidths(header.current) : {},
14
+ ...widths
15
+ });
16
+ const onPointerDown = (id, event) => {
17
+ if (event.button !== 0) return;
18
+ event.preventDefault();
19
+ const start = startWidths();
20
+ const startX = event.clientX;
21
+ const onMove = (move) => setWidths({
22
+ ...start,
23
+ [id]: resizedWidth(start[id] ?? 0, move.clientX - startX)
24
+ });
25
+ const onEnd = () => {
26
+ window.removeEventListener("pointermove", onMove);
27
+ window.removeEventListener("pointerup", onEnd);
28
+ window.removeEventListener("pointercancel", onEnd);
29
+ };
30
+ window.addEventListener("pointermove", onMove);
31
+ window.addEventListener("pointerup", onEnd);
32
+ window.addEventListener("pointercancel", onEnd);
33
+ };
34
+ const onKeyDown = (id, event) => {
35
+ const delta = event.key === "ArrowRight" ? 16 : event.key === "ArrowLeft" ? -16 : 0;
36
+ if (delta === 0) return;
37
+ event.preventDefault();
38
+ const start = startWidths();
39
+ setWidths({
40
+ ...start,
41
+ [id]: resizedWidth(start[id] ?? 0, delta)
42
+ });
43
+ };
44
+ return {
45
+ widths,
46
+ onPointerDown,
47
+ onKeyDown
48
+ };
49
+ };
50
+ //#endregion
51
+ export { useColumnResize };
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.45.15";
2
+ var version = "0.45.17";
3
3
  var protobase = { "pluginApi": 1 };
4
4
  //#endregion
5
5
  export { protobase, version };