@undevy-org/tablewright 0.9.1 → 0.10.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.
@@ -1,14 +1,17 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
3
  import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "../ui/dropdown-menu.js";
4
- import { ChevronDown, ListFilter, EyeOff, ArrowUp, ArrowDown, Check } from "lucide-react";
4
+ import { ChevronDown, ListFilter, EyeOff, Pin, Check, PinOff, ArrowUp, ArrowDown } from "lucide-react";
5
5
  const DEFAULT_LABELS = {
6
6
  sort: "Sort",
7
7
  sortByColumn: (label) => `Sort by ${label}`,
8
8
  ascending: "Ascending",
9
9
  descending: "Descending",
10
10
  filterByColumn: "Filter by this column",
11
- hideColumn: "Hide column"
11
+ hideColumn: "Hide column",
12
+ pinLeft: "Pin left",
13
+ pinRight: "Pin right",
14
+ unpin: "Unpin"
12
15
  };
13
16
  const sectionLabelClass = "px-2 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-[0.12em] text-[var(--text-tertiary)] first:pt-1";
14
17
  const itemClass = "gap-2.5 rounded-md px-2.5 py-2";
@@ -60,6 +63,10 @@ function ColumnHeaderMenu({
60
63
  hasFilter,
61
64
  onFilterClick,
62
65
  onHide,
66
+ pinSide = false,
67
+ onPinLeft,
68
+ onPinRight,
69
+ onUnpin,
63
70
  labels: labelOverrides,
64
71
  menuWidth = "w-[200px]"
65
72
  }) {
@@ -68,7 +75,15 @@ function ColumnHeaderMenu({
68
75
  const isStacked = !!primaryLabel;
69
76
  const isSingleSortable = !!label && !!onSort;
70
77
  const isSortable = isStacked || isSingleSortable;
71
- const hasActions = hasFilter || !!onHide;
78
+ const showFilterItem = Boolean(hasFilter && onFilterClick);
79
+ const showHideItem = Boolean(onHide);
80
+ const showPinLeftItem = Boolean(onPinLeft);
81
+ const showPinRightItem = Boolean(onPinRight);
82
+ const showUnpinItem = Boolean(onUnpin && pinSide);
83
+ const hasPinSection = showPinLeftItem || showPinRightItem || showUnpinItem;
84
+ const hasFilterOrHide = showFilterItem || showHideItem;
85
+ const showSeparatorAfterSort = isSortable && (hasFilterOrHide || hasPinSection);
86
+ const showSeparatorBeforePin = hasPinSection && hasFilterOrHide;
72
87
  const labelContent = isStacked ? /* @__PURE__ */ jsxs("span", { className: "flex flex-col leading-none", children: [
73
88
  /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-[var(--text-primary)]", children: [
74
89
  primaryLabel,
@@ -127,14 +142,29 @@ function ColumnHeaderMenu({
127
142
  labels
128
143
  }
129
144
  ),
130
- isSortable && hasActions && /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-[var(--border-subtle)]" }),
131
- hasFilter && onFilterClick && /* @__PURE__ */ jsxs(DropdownMenuItem, { className: itemClass, onClick: onFilterClick, children: [
145
+ showSeparatorAfterSort && /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-[var(--border-subtle)]" }),
146
+ showFilterItem && /* @__PURE__ */ jsxs(DropdownMenuItem, { className: itemClass, onClick: onFilterClick, children: [
132
147
  /* @__PURE__ */ jsx(ListFilter, { className: iconClass }),
133
148
  labels.filterByColumn
134
149
  ] }),
135
- onHide && /* @__PURE__ */ jsxs(DropdownMenuItem, { className: itemClass, onClick: onHide, children: [
150
+ showHideItem && /* @__PURE__ */ jsxs(DropdownMenuItem, { className: itemClass, onClick: onHide, children: [
136
151
  /* @__PURE__ */ jsx(EyeOff, { className: iconClass }),
137
152
  labels.hideColumn
153
+ ] }),
154
+ showSeparatorBeforePin && /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-[var(--border-subtle)]" }),
155
+ showPinLeftItem && /* @__PURE__ */ jsxs(DropdownMenuItem, { className: itemClass, onClick: onPinLeft, children: [
156
+ /* @__PURE__ */ jsx(Pin, { className: iconClass }),
157
+ labels.pinLeft,
158
+ pinSide === "left" && /* @__PURE__ */ jsx(Check, { className: "ml-auto h-3 w-3 text-[var(--text-tertiary)]" })
159
+ ] }),
160
+ showPinRightItem && /* @__PURE__ */ jsxs(DropdownMenuItem, { className: itemClass, onClick: onPinRight, children: [
161
+ /* @__PURE__ */ jsx(Pin, { className: `${iconClass} rotate-90` }),
162
+ labels.pinRight,
163
+ pinSide === "right" && /* @__PURE__ */ jsx(Check, { className: "ml-auto h-3 w-3 text-[var(--text-tertiary)]" })
164
+ ] }),
165
+ showUnpinItem && /* @__PURE__ */ jsxs(DropdownMenuItem, { className: itemClass, onClick: onUnpin, children: [
166
+ /* @__PURE__ */ jsx(PinOff, { className: iconClass }),
167
+ labels.unpin
138
168
  ] })
139
169
  ] })
140
170
  ] });
package/dist/styles.css CHANGED
@@ -787,6 +787,10 @@
787
787
  --tw-rotate: 180deg;
788
788
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
789
789
  }
790
+ .rotate-90 {
791
+ --tw-rotate: 90deg;
792
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
793
+ }
790
794
  .cursor-col-resize {
791
795
  cursor: col-resize
792
796
  }
@@ -2682,7 +2682,10 @@ const DEFAULT_LABELS$4 = {
2682
2682
  ascending: "Ascending",
2683
2683
  descending: "Descending",
2684
2684
  filterByColumn: "Filter by this column",
2685
- hideColumn: "Hide column"
2685
+ hideColumn: "Hide column",
2686
+ pinLeft: "Pin left",
2687
+ pinRight: "Pin right",
2688
+ unpin: "Unpin"
2686
2689
  };
2687
2690
  const sectionLabelClass = "px-2 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-[0.12em] text-[var(--text-tertiary)] first:pt-1";
2688
2691
  const itemClass = "gap-2.5 rounded-md px-2.5 py-2";
@@ -2734,6 +2737,10 @@ function ColumnHeaderMenu({
2734
2737
  hasFilter,
2735
2738
  onFilterClick,
2736
2739
  onHide,
2740
+ pinSide = false,
2741
+ onPinLeft,
2742
+ onPinRight,
2743
+ onUnpin,
2737
2744
  labels: labelOverrides,
2738
2745
  menuWidth = "w-[200px]"
2739
2746
  }) {
@@ -2742,7 +2749,15 @@ function ColumnHeaderMenu({
2742
2749
  const isStacked = !!primaryLabel;
2743
2750
  const isSingleSortable = !!label && !!onSort;
2744
2751
  const isSortable = isStacked || isSingleSortable;
2745
- const hasActions = hasFilter || !!onHide;
2752
+ const showFilterItem = Boolean(hasFilter && onFilterClick);
2753
+ const showHideItem = Boolean(onHide);
2754
+ const showPinLeftItem = Boolean(onPinLeft);
2755
+ const showPinRightItem = Boolean(onPinRight);
2756
+ const showUnpinItem = Boolean(onUnpin && pinSide);
2757
+ const hasPinSection = showPinLeftItem || showPinRightItem || showUnpinItem;
2758
+ const hasFilterOrHide = showFilterItem || showHideItem;
2759
+ const showSeparatorAfterSort = isSortable && (hasFilterOrHide || hasPinSection);
2760
+ const showSeparatorBeforePin = hasPinSection && hasFilterOrHide;
2746
2761
  const labelContent = isStacked ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex flex-col leading-none", children: [
2747
2762
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-[var(--text-primary)]", children: [
2748
2763
  primaryLabel,
@@ -2801,14 +2816,29 @@ function ColumnHeaderMenu({
2801
2816
  labels
2802
2817
  }
2803
2818
  ),
2804
- isSortable && hasActions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-1 border-t border-[var(--border-subtle)]" }),
2805
- hasFilter && onFilterClick && /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { className: itemClass, onClick: onFilterClick, children: [
2819
+ showSeparatorAfterSort && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-1 border-t border-[var(--border-subtle)]" }),
2820
+ showFilterItem && /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { className: itemClass, onClick: onFilterClick, children: [
2806
2821
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ListFilter, { className: iconClass }),
2807
2822
  labels.filterByColumn
2808
2823
  ] }),
2809
- onHide && /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { className: itemClass, onClick: onHide, children: [
2824
+ showHideItem && /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { className: itemClass, onClick: onHide, children: [
2810
2825
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeOff, { className: iconClass }),
2811
2826
  labels.hideColumn
2827
+ ] }),
2828
+ showSeparatorBeforePin && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-1 border-t border-[var(--border-subtle)]" }),
2829
+ showPinLeftItem && /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { className: itemClass, onClick: onPinLeft, children: [
2830
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pin, { className: iconClass }),
2831
+ labels.pinLeft,
2832
+ pinSide === "left" && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "ml-auto h-3 w-3 text-[var(--text-tertiary)]" })
2833
+ ] }),
2834
+ showPinRightItem && /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { className: itemClass, onClick: onPinRight, children: [
2835
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pin, { className: `${iconClass} rotate-90` }),
2836
+ labels.pinRight,
2837
+ pinSide === "right" && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "ml-auto h-3 w-3 text-[var(--text-tertiary)]" })
2838
+ ] }),
2839
+ showUnpinItem && /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { className: itemClass, onClick: onUnpin, children: [
2840
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PinOff, { className: iconClass }),
2841
+ labels.unpin
2812
2842
  ] })
2813
2843
  ] })
2814
2844
  ] });
@@ -635,6 +635,10 @@
635
635
  --tw-rotate: 180deg;
636
636
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
637
637
  }
638
+ .rotate-90 {
639
+ --tw-rotate: 90deg;
640
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
641
+ }
638
642
  .cursor-col-resize {
639
643
  cursor: col-resize
640
644
  }
@@ -6,6 +6,9 @@ export interface ColumnHeaderMenuLabels {
6
6
  descending: string;
7
7
  filterByColumn: string;
8
8
  hideColumn: string;
9
+ pinLeft: string;
10
+ pinRight: string;
11
+ unpin: string;
9
12
  }
10
13
  interface ColumnHeaderMenuProps {
11
14
  label?: string;
@@ -20,8 +23,12 @@ interface ColumnHeaderMenuProps {
20
23
  hasFilter?: boolean;
21
24
  onFilterClick?: () => void;
22
25
  onHide?: () => void;
26
+ pinSide?: "left" | "right" | false;
27
+ onPinLeft?: () => void;
28
+ onPinRight?: () => void;
29
+ onUnpin?: () => void;
23
30
  labels?: Partial<ColumnHeaderMenuLabels>;
24
31
  menuWidth?: string;
25
32
  }
26
- export declare function ColumnHeaderMenu({ label, primaryLabel, secondaryLabel, sorted, onSort, primarySorted, onPrimarySort, secondarySorted, onSecondarySort, hasFilter, onFilterClick, onHide, labels: labelOverrides, menuWidth, }: ColumnHeaderMenuProps): import("react").JSX.Element;
33
+ export declare function ColumnHeaderMenu({ label, primaryLabel, secondaryLabel, sorted, onSort, primarySorted, onPrimarySort, secondarySorted, onSecondarySort, hasFilter, onFilterClick, onHide, pinSide, onPinLeft, onPinRight, onUnpin, labels: labelOverrides, menuWidth, }: ColumnHeaderMenuProps): import("react").JSX.Element;
27
34
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@undevy-org/tablewright",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "React data-table and application-shell components: filters, drawers, row controls, sortable and resizable columns, built on Radix UI and TanStack Table.",
5
5
  "license": "MIT",
6
6
  "repository": {