@leavepulse/ui 0.49.1 → 0.50.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/assets/{LpTable-xWwI4KPZ.css → LpTable-BAMDVi1u.css} +17 -17
- package/dist/chunks/{LpTabs-29iW1XOx.js → LpTabs-UM0FQmPA.js} +18 -3
- package/dist/components/LpTable.vue.d.ts +7 -0
- package/dist/components/LpTable.vue.js +45 -24
- package/dist/components/LpTabs.vue.d.ts +4 -4
- package/dist/components/LpTabs.vue.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/components/LpTable.vue +32 -2
- package/src/components/LpTabs.vue +21 -3
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
* fade before closing the gap and the whole list lurches at the end instead of
|
|
12
12
|
* gliding.
|
|
13
13
|
*/
|
|
14
|
-
.lp-row-enter-active[data-v-
|
|
15
|
-
.lp-row-leave-active[data-v-
|
|
14
|
+
.lp-row-enter-active[data-v-5877be26],
|
|
15
|
+
.lp-row-leave-active[data-v-5877be26] {
|
|
16
16
|
transition:
|
|
17
17
|
opacity var(--duration-fast) var(--ease-emphasized),
|
|
18
18
|
transform var(--duration-fast) var(--ease-emphasized);
|
|
19
19
|
}
|
|
20
|
-
.lp-row-move[data-v-
|
|
20
|
+
.lp-row-move[data-v-5877be26] {
|
|
21
21
|
transition: transform var(--duration-medium) var(--ease-emphasized);
|
|
22
22
|
}
|
|
23
|
-
.lp-row-enter-from[data-v-
|
|
24
|
-
.lp-row-leave-to[data-v-
|
|
23
|
+
.lp-row-enter-from[data-v-5877be26],
|
|
24
|
+
.lp-row-leave-to[data-v-5877be26] {
|
|
25
25
|
opacity: 0;
|
|
26
26
|
transform: translateX(-0.5rem);
|
|
27
27
|
}
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* drops out of the table layout entirely. Nothing sits below this row to be
|
|
36
36
|
* dragged around, so simply not animating its exit costs nothing.
|
|
37
37
|
*/
|
|
38
|
-
.lp-row-empty.lp-row-leave-active[data-v-
|
|
38
|
+
.lp-row-empty.lp-row-leave-active[data-v-5877be26] {
|
|
39
39
|
display: none;
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
* once anything is selected. An earlier version collapsed the column to nothing
|
|
55
55
|
* and floated the hover box over the first cell, where it sat on the text.
|
|
56
56
|
*/
|
|
57
|
-
.lp-tick[data-v-
|
|
57
|
+
.lp-tick[data-v-5877be26] {
|
|
58
58
|
display: flex;
|
|
59
59
|
align-items: center;
|
|
60
60
|
max-width: 0;
|
|
@@ -66,17 +66,17 @@
|
|
|
66
66
|
padding var(--duration-medium) var(--ease-emphasized),
|
|
67
67
|
opacity var(--duration-fast) ease;
|
|
68
68
|
}
|
|
69
|
-
.lp-tick-open[data-v-
|
|
69
|
+
.lp-tick-open[data-v-5877be26] {
|
|
70
70
|
max-width: 3.25rem;
|
|
71
71
|
padding-inline: 1rem;
|
|
72
72
|
opacity: 1;
|
|
73
73
|
}
|
|
74
|
-
.lp-tick-hidden[data-v-
|
|
75
|
-
.lp-tick-cue[data-v-
|
|
74
|
+
.lp-tick-hidden[data-v-5877be26],
|
|
75
|
+
.lp-tick-cue[data-v-5877be26] {
|
|
76
76
|
opacity: 0;
|
|
77
77
|
}
|
|
78
|
-
tr:hover .lp-tick-cue[data-v-
|
|
79
|
-
.lp-tick-cue[data-v-
|
|
78
|
+
tr:hover .lp-tick-cue[data-v-5877be26],
|
|
79
|
+
.lp-tick-cue[data-v-5877be26]:focus-within {
|
|
80
80
|
opacity: 1;
|
|
81
81
|
}
|
|
82
82
|
|
|
@@ -84,15 +84,15 @@ tr:hover .lp-tick-cue[data-v-4fa70e89],
|
|
|
84
84
|
turned down the rows change without animating rather than not changing. */
|
|
85
85
|
/* Only where there is a hover to reveal it; a finger long-presses instead. */
|
|
86
86
|
@media (hover: none) {
|
|
87
|
-
tr:hover .lp-tick-cue[data-v-
|
|
87
|
+
tr:hover .lp-tick-cue[data-v-5877be26] {
|
|
88
88
|
opacity: 0;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
@media (prefers-reduced-motion: reduce) {
|
|
92
|
-
.lp-row-enter-active[data-v-
|
|
93
|
-
.lp-row-leave-active[data-v-
|
|
94
|
-
.lp-row-move[data-v-
|
|
95
|
-
.lp-tick[data-v-
|
|
92
|
+
.lp-row-enter-active[data-v-5877be26],
|
|
93
|
+
.lp-row-leave-active[data-v-5877be26],
|
|
94
|
+
.lp-row-move[data-v-5877be26],
|
|
95
|
+
.lp-tick[data-v-5877be26] {
|
|
96
96
|
transition: none;
|
|
97
97
|
}
|
|
98
98
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineComponent, ref, watch, useId, computed, openBlock, createBlock, unref, withCtx, createVNode, normalizeClass, createElementBlock, Fragment, renderList, createCommentVNode, createElementVNode, toDisplayString, renderSlot } from "vue";
|
|
1
|
+
import { defineComponent, ref, watch, nextTick, useId, computed, openBlock, createBlock, unref, withCtx, createVNode, normalizeClass, createElementBlock, Fragment, renderList, createCommentVNode, createElementVNode, toDisplayString, renderSlot } from "vue";
|
|
2
2
|
import { useReducedMotion, Motion } from "motion-v";
|
|
3
3
|
import { TabsRoot, TabsList, TabsTrigger, TabsContent } from "reka-ui";
|
|
4
4
|
import { usePillTransition } from "../composables/usePillTransition.js";
|
|
@@ -25,6 +25,19 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
25
25
|
if (value !== void 0) current.value = value;
|
|
26
26
|
}
|
|
27
27
|
);
|
|
28
|
+
const list = ref(null);
|
|
29
|
+
watch(current, () => {
|
|
30
|
+
void nextTick(() => {
|
|
31
|
+
const row = list.value?.$el;
|
|
32
|
+
const active = row?.querySelector('[data-state="active"]');
|
|
33
|
+
if (!row || !active || row.scrollWidth <= row.clientWidth) return;
|
|
34
|
+
const start = active.offsetLeft - row.offsetLeft;
|
|
35
|
+
const end = start + active.offsetWidth;
|
|
36
|
+
if (start >= row.scrollLeft && end <= row.scrollLeft + row.clientWidth) return;
|
|
37
|
+
const left = start < row.scrollLeft ? start : end - row.clientWidth;
|
|
38
|
+
row.scrollTo({ left, behavior: reduceMotion.value ? "auto" : "smooth" });
|
|
39
|
+
});
|
|
40
|
+
});
|
|
28
41
|
function select(value) {
|
|
29
42
|
if (props.modelValue === void 0) current.value = value;
|
|
30
43
|
emit("update:modelValue", value);
|
|
@@ -50,7 +63,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
50
63
|
}, {
|
|
51
64
|
default: withCtx(() => [
|
|
52
65
|
createVNode(unref(TabsList), {
|
|
53
|
-
|
|
66
|
+
ref_key: "list",
|
|
67
|
+
ref: list,
|
|
68
|
+
class: normalizeClass(["max-w-full gap-1 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", [
|
|
54
69
|
__props.block ? "flex w-full" : "inline-flex",
|
|
55
70
|
__props.variant === "contained" ? "rounded-control border border-line bg-surface-soft p-1" : "",
|
|
56
71
|
__props.variant === "underline" ? "border-b border-line" : ""
|
|
@@ -62,7 +77,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
62
77
|
return openBlock(), createBlock(unref(TabsTrigger), {
|
|
63
78
|
key: item.value,
|
|
64
79
|
value: item.value,
|
|
65
|
-
class: normalizeClass(["relative inline-flex items-center justify-center gap-1.5 text-sm outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring", [
|
|
80
|
+
class: normalizeClass(["relative inline-flex shrink-0 items-center justify-center gap-1.5 whitespace-nowrap text-sm outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring", [
|
|
66
81
|
__props.block ? "flex-1" : "",
|
|
67
82
|
__props.variant === "underline" ? "rounded-t-md px-3 pt-1.5 pb-2" : "rounded-md px-3 py-1.5",
|
|
68
83
|
__props.accent ? "data-[state=active]:text-brand" : "data-[state=active]:text-ink",
|
|
@@ -34,6 +34,13 @@ export interface TableColumn<Row> {
|
|
|
34
34
|
/** Keep this column out of the show/hide list — an actions column has no
|
|
35
35
|
* name to offer and nothing to gain from being hidden. */
|
|
36
36
|
alwaysVisible?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Leave the column out while the table is narrower than this many pixels
|
|
39
|
+
* (a phone, a narrow card), so the columns that matter keep their room
|
|
40
|
+
* instead of the whole table scrolling sideways. Wider than that, the
|
|
41
|
+
* user's own show/hide applies as usual.
|
|
42
|
+
*/
|
|
43
|
+
hideBelow?: number;
|
|
37
44
|
}
|
|
38
45
|
export interface TableLabels {
|
|
39
46
|
[key: string]: string;
|
|
@@ -18,7 +18,7 @@ import { _ as _sfc_main$1 } from "../chunks/LpButton-CKSwC6Mu.js";
|
|
|
18
18
|
import { toCsv, downloadFile, fileStem } from "./exportData.js";
|
|
19
19
|
import { summarizeShown, parseTableState, serializeTableState, countValues } from "./tableFilter.js";
|
|
20
20
|
import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
|
|
21
|
-
import '../assets/LpTable-
|
|
21
|
+
import '../assets/LpTable-BAMDVi1u.css';const _hoisted_1 = {
|
|
22
22
|
key: 0,
|
|
23
23
|
class: "flex justify-end"
|
|
24
24
|
};
|
|
@@ -43,19 +43,18 @@ const _hoisted_9 = {
|
|
|
43
43
|
class: "flex items-center gap-2"
|
|
44
44
|
};
|
|
45
45
|
const _hoisted_10 = { class: "hidden sm:inline" };
|
|
46
|
-
const _hoisted_11 = { class: "
|
|
47
|
-
const _hoisted_12 =
|
|
48
|
-
const _hoisted_13 = ["
|
|
49
|
-
const _hoisted_14 = ["
|
|
50
|
-
const _hoisted_15 = ["
|
|
51
|
-
const _hoisted_16 =
|
|
52
|
-
const _hoisted_17 = {
|
|
46
|
+
const _hoisted_11 = { class: "border-b border-line bg-surface-soft" };
|
|
47
|
+
const _hoisted_12 = ["inert"];
|
|
48
|
+
const _hoisted_13 = ["aria-sort"];
|
|
49
|
+
const _hoisted_14 = ["onClick"];
|
|
50
|
+
const _hoisted_15 = ["aria-label", "data-lp-filter"];
|
|
51
|
+
const _hoisted_16 = {
|
|
53
52
|
key: "lp-table-empty",
|
|
54
53
|
class: "lp-row-empty"
|
|
55
54
|
};
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
55
|
+
const _hoisted_17 = ["colspan"];
|
|
56
|
+
const _hoisted_18 = ["onContextmenu", "data-paint-key", "onClick", "onPointerdown"];
|
|
57
|
+
const _hoisted_19 = ["onPointerdown"];
|
|
59
58
|
const __default__ = { inheritAttrs: false };
|
|
60
59
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
61
60
|
...__default__,
|
|
@@ -203,8 +202,25 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
203
202
|
change();
|
|
204
203
|
}
|
|
205
204
|
const hiddenColumns = ref(/* @__PURE__ */ new Set());
|
|
205
|
+
const tableEl = ref(null);
|
|
206
|
+
const tableWidth = ref(Number.POSITIVE_INFINITY);
|
|
207
|
+
let widthObserver;
|
|
208
|
+
watch(
|
|
209
|
+
tableEl,
|
|
210
|
+
(table) => {
|
|
211
|
+
widthObserver?.disconnect();
|
|
212
|
+
const el = table?.closest("[data-reka-scroll-area-viewport]") ?? table?.parentElement;
|
|
213
|
+
if (!el || typeof ResizeObserver === "undefined") return;
|
|
214
|
+
widthObserver = new ResizeObserver(() => tableWidth.value = el.clientWidth);
|
|
215
|
+
widthObserver.observe(el);
|
|
216
|
+
tableWidth.value = el.clientWidth;
|
|
217
|
+
},
|
|
218
|
+
{ flush: "post" }
|
|
219
|
+
);
|
|
206
220
|
const visibleColumns = computed(
|
|
207
|
-
() => props.columns.filter(
|
|
221
|
+
() => props.columns.filter(
|
|
222
|
+
(c) => !hiddenColumns.value.has(c.key) && !(c.hideBelow && tableWidth.value < c.hideBelow)
|
|
223
|
+
)
|
|
208
224
|
);
|
|
209
225
|
function valuesOf(col) {
|
|
210
226
|
if (props.load && facets.value[col.key]) return facets.value[col.key];
|
|
@@ -606,6 +622,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
606
622
|
);
|
|
607
623
|
onBeforeUnmount(() => {
|
|
608
624
|
ro?.disconnect();
|
|
625
|
+
widthObserver?.disconnect();
|
|
609
626
|
if (motionTimer) clearTimeout(motionTimer);
|
|
610
627
|
});
|
|
611
628
|
const barInsetTop = computed(
|
|
@@ -738,13 +755,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
738
755
|
always: ""
|
|
739
756
|
}, {
|
|
740
757
|
default: withCtx(() => [
|
|
741
|
-
createElementVNode("table",
|
|
758
|
+
createElementVNode("table", {
|
|
759
|
+
ref_key: "tableEl",
|
|
760
|
+
ref: tableEl,
|
|
761
|
+
class: "w-full border-collapse text-sm"
|
|
762
|
+
}, [
|
|
742
763
|
createElementVNode("thead", {
|
|
743
764
|
ref_key: "headEl",
|
|
744
765
|
ref: headEl,
|
|
745
766
|
class: normalizeClass(__props.stickyHeader ? "sticky top-0 z-10 bg-surface-soft" : "")
|
|
746
767
|
}, [
|
|
747
|
-
createElementVNode("tr",
|
|
768
|
+
createElementVNode("tr", _hoisted_11, [
|
|
748
769
|
__props.selectable ? (openBlock(), createElementBlock("th", {
|
|
749
770
|
key: 0,
|
|
750
771
|
class: normalizeClass(["w-px p-0", cellY.value])
|
|
@@ -758,7 +779,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
758
779
|
indeterminate: someChecked.value,
|
|
759
780
|
"onUpdate:modelValue": toggleAll
|
|
760
781
|
}, null, 8, ["model-value", "indeterminate"])
|
|
761
|
-
], 10,
|
|
782
|
+
], 10, _hoisted_12)
|
|
762
783
|
], 2)) : createCommentVNode("", true),
|
|
763
784
|
(openBlock(true), createElementBlock(Fragment, null, renderList(visibleColumns.value, (col) => {
|
|
764
785
|
return openBlock(), createElementBlock("th", {
|
|
@@ -790,7 +811,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
790
811
|
activeSort.value?.key === col.key ? "opacity-100" : "opacity-40 group-hover/sort:opacity-70"
|
|
791
812
|
])
|
|
792
813
|
}, null, 8, ["name", "class"])) : createCommentVNode("", true)
|
|
793
|
-
], 10,
|
|
814
|
+
], 10, _hoisted_14)
|
|
794
815
|
]),
|
|
795
816
|
_: 2
|
|
796
817
|
}, 1032, ["items"])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
|
|
@@ -813,7 +834,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
813
834
|
name: hiddenValues.value[col.key]?.size ? "lucide:filter-x" : "lucide:filter",
|
|
814
835
|
size: 12
|
|
815
836
|
}, null, 8, ["name"])
|
|
816
|
-
], 10,
|
|
837
|
+
], 10, _hoisted_15)
|
|
817
838
|
]),
|
|
818
839
|
default: withCtx(() => [
|
|
819
840
|
createVNode(_sfc_main$9, {
|
|
@@ -825,7 +846,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
825
846
|
]),
|
|
826
847
|
_: 2
|
|
827
848
|
}, 1032, ["aria-label"])) : createCommentVNode("", true)
|
|
828
|
-
], 14,
|
|
849
|
+
], 14, _hoisted_13);
|
|
829
850
|
}), 128))
|
|
830
851
|
])
|
|
831
852
|
], 2),
|
|
@@ -837,7 +858,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
837
858
|
ref: (el) => unref(roving).setContainer(el?.$el ?? null)
|
|
838
859
|
}, {
|
|
839
860
|
default: withCtx(() => [
|
|
840
|
-
displayRows.value.length === 0 ? (openBlock(), createElementBlock("tr",
|
|
861
|
+
displayRows.value.length === 0 ? (openBlock(), createElementBlock("tr", _hoisted_16, [
|
|
841
862
|
createElementVNode("td", {
|
|
842
863
|
colspan: colSpan.value,
|
|
843
864
|
class: "px-4 py-10 text-center text-muted"
|
|
@@ -860,7 +881,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
860
881
|
]),
|
|
861
882
|
_: 1
|
|
862
883
|
})) : createCommentVNode("", true)
|
|
863
|
-
], 8,
|
|
884
|
+
], 8, _hoisted_17)
|
|
864
885
|
])) : createCommentVNode("", true),
|
|
865
886
|
(openBlock(true), createElementBlock(Fragment, null, renderList(displayRows.value, (row, index) => {
|
|
866
887
|
return openBlock(), createElementBlock("tr", mergeProps({
|
|
@@ -888,7 +909,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
888
909
|
"onUpdate:modelValue": (v) => toggleRow(keyFor(row, index), v)
|
|
889
910
|
}, null, 8, ["model-value", "onUpdate:modelValue"])
|
|
890
911
|
], 2)
|
|
891
|
-
], 42,
|
|
912
|
+
], 42, _hoisted_19)) : createCommentVNode("", true),
|
|
892
913
|
(openBlock(true), createElementBlock(Fragment, null, renderList(visibleColumns.value, (col, ci) => {
|
|
893
914
|
return openBlock(), createElementBlock("td", {
|
|
894
915
|
key: col.key,
|
|
@@ -902,12 +923,12 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
902
923
|
], true)
|
|
903
924
|
], 2);
|
|
904
925
|
}), 128))
|
|
905
|
-
], 16,
|
|
926
|
+
], 16, _hoisted_18);
|
|
906
927
|
}), 128))
|
|
907
928
|
]),
|
|
908
929
|
_: 3
|
|
909
930
|
}, 8, ["css", "class"])
|
|
910
|
-
])
|
|
931
|
+
], 512)
|
|
911
932
|
]),
|
|
912
933
|
_: 3
|
|
913
934
|
}, 8, ["items"])
|
|
@@ -926,7 +947,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
926
947
|
};
|
|
927
948
|
}
|
|
928
949
|
});
|
|
929
|
-
const LpTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
950
|
+
const LpTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-5877be26"]]);
|
|
930
951
|
export {
|
|
931
952
|
LpTable as default
|
|
932
953
|
};
|
|
@@ -22,13 +22,13 @@ type __VLS_Props = {
|
|
|
22
22
|
/** Stretch the bar to full width with equal-share triggers. */
|
|
23
23
|
block?: boolean;
|
|
24
24
|
};
|
|
25
|
-
declare var
|
|
25
|
+
declare var __VLS_51: {
|
|
26
26
|
value: string;
|
|
27
|
-
},
|
|
27
|
+
}, __VLS_53: {};
|
|
28
28
|
type __VLS_Slots = {} & {
|
|
29
|
-
panel?: (props: typeof
|
|
29
|
+
panel?: (props: typeof __VLS_51) => any;
|
|
30
30
|
} & {
|
|
31
|
-
default?: (props: typeof
|
|
31
|
+
default?: (props: typeof __VLS_53) => any;
|
|
32
32
|
};
|
|
33
33
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
34
34
|
"update:modelValue": (value: string) => any;
|
package/dist/index.js
CHANGED
|
@@ -96,7 +96,7 @@ import { _ as _69 } from "./chunks/LpSwitch-OrcOJX9m.js";
|
|
|
96
96
|
import { default as default12 } from "./components/LpTable.vue.js";
|
|
97
97
|
import { compareValues } from "./components/tableSort.js";
|
|
98
98
|
import { _ as _70 } from "./chunks/LpTableOfContents-BUcAfGPQ.js";
|
|
99
|
-
import { _ as _71 } from "./chunks/LpTabs-
|
|
99
|
+
import { _ as _71 } from "./chunks/LpTabs-UM0FQmPA.js";
|
|
100
100
|
import { _ as _72 } from "./chunks/LpTagsInput-D1SuqbhB.js";
|
|
101
101
|
import { addTags, splitTags } from "./components/tags.js";
|
|
102
102
|
import { _ as _73 } from "./chunks/LpTextarea-uuScWo6-.js";
|
package/package.json
CHANGED
|
@@ -59,6 +59,13 @@ export interface TableColumn<Row> {
|
|
|
59
59
|
/** Keep this column out of the show/hide list — an actions column has no
|
|
60
60
|
* name to offer and nothing to gain from being hidden. */
|
|
61
61
|
alwaysVisible?: boolean
|
|
62
|
+
/**
|
|
63
|
+
* Leave the column out while the table is narrower than this many pixels
|
|
64
|
+
* (a phone, a narrow card), so the columns that matter keep their room
|
|
65
|
+
* instead of the whole table scrolling sideways. Wider than that, the
|
|
66
|
+
* user's own show/hide applies as usual.
|
|
67
|
+
*/
|
|
68
|
+
hideBelow?: number
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
export interface TableLabels {
|
|
@@ -430,8 +437,30 @@ function withMotion(change: () => void) {
|
|
|
430
437
|
}
|
|
431
438
|
const hiddenColumns = ref<Set<string>>(new Set())
|
|
432
439
|
|
|
440
|
+
// The table's own width, for `hideBelow`: what decides is the room the table
|
|
441
|
+
// has, not the window, which a sidebar or a card can take most of.
|
|
442
|
+
// Measured on the scroll viewport around the table: that is the room it has,
|
|
443
|
+
// while the table itself widens with whatever it overflows by.
|
|
444
|
+
const tableEl = ref<HTMLElement | null>(null)
|
|
445
|
+
const tableWidth = ref(Number.POSITIVE_INFINITY)
|
|
446
|
+
let widthObserver: ResizeObserver | undefined
|
|
447
|
+
watch(
|
|
448
|
+
tableEl,
|
|
449
|
+
(table) => {
|
|
450
|
+
widthObserver?.disconnect()
|
|
451
|
+
const el = table?.closest<HTMLElement>("[data-reka-scroll-area-viewport]") ?? table?.parentElement
|
|
452
|
+
if (!el || typeof ResizeObserver === "undefined") return
|
|
453
|
+
widthObserver = new ResizeObserver(() => (tableWidth.value = el.clientWidth))
|
|
454
|
+
widthObserver.observe(el)
|
|
455
|
+
tableWidth.value = el.clientWidth
|
|
456
|
+
},
|
|
457
|
+
{ flush: "post" },
|
|
458
|
+
)
|
|
459
|
+
|
|
433
460
|
const visibleColumns = computed(() =>
|
|
434
|
-
props.columns.filter(
|
|
461
|
+
props.columns.filter(
|
|
462
|
+
(c) => !hiddenColumns.value.has(c.key) && !(c.hideBelow && tableWidth.value < c.hideBelow),
|
|
463
|
+
),
|
|
435
464
|
)
|
|
436
465
|
|
|
437
466
|
/** The distinct values a filterable column holds, in first-seen order. */
|
|
@@ -997,6 +1026,7 @@ watch(
|
|
|
997
1026
|
)
|
|
998
1027
|
onBeforeUnmount(() => {
|
|
999
1028
|
ro?.disconnect()
|
|
1029
|
+
widthObserver?.disconnect()
|
|
1000
1030
|
if (motionTimer) clearTimeout(motionTimer)
|
|
1001
1031
|
})
|
|
1002
1032
|
|
|
@@ -1111,7 +1141,7 @@ const barInsetTop = computed(() =>
|
|
|
1111
1141
|
reaches: rows and headers carry their own and take the event first, so
|
|
1112
1142
|
this is the fallback rather than a competitor. -->
|
|
1113
1143
|
<LpContextMenu :items="contextMenu" always>
|
|
1114
|
-
<table class="w-full border-collapse text-sm">
|
|
1144
|
+
<table ref="tableEl" class="w-full border-collapse text-sm">
|
|
1115
1145
|
<!-- The background belongs on `thead` itself, not only on its row:
|
|
1116
1146
|
`border-collapse` collapses the borders, and with a sticky header the
|
|
1117
1147
|
rows show through that seam — a band of body text rides across the
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { Motion, useReducedMotion } from "motion-v"
|
|
3
3
|
import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "reka-ui"
|
|
4
|
-
import { computed, ref, useId, watch } from "vue"
|
|
4
|
+
import { computed, nextTick, ref, useId, watch } from "vue"
|
|
5
5
|
import { usePillTransition } from "../composables/usePillTransition"
|
|
6
6
|
import LpIcon from "./LpIcon.vue"
|
|
7
7
|
|
|
@@ -45,6 +45,23 @@ watch(
|
|
|
45
45
|
},
|
|
46
46
|
)
|
|
47
47
|
|
|
48
|
+
// A row wider than its container scrolls sideways (a phone, a narrow card)
|
|
49
|
+
// instead of wrapping each label onto two lines; the active tab is brought
|
|
50
|
+
// into view whenever it changes, including from outside.
|
|
51
|
+
const list = ref<{ $el: HTMLElement } | null>(null)
|
|
52
|
+
watch(current, () => {
|
|
53
|
+
void nextTick(() => {
|
|
54
|
+
const row = list.value?.$el
|
|
55
|
+
const active = row?.querySelector<HTMLElement>('[data-state="active"]')
|
|
56
|
+
if (!row || !active || row.scrollWidth <= row.clientWidth) return
|
|
57
|
+
const start = active.offsetLeft - row.offsetLeft
|
|
58
|
+
const end = start + active.offsetWidth
|
|
59
|
+
if (start >= row.scrollLeft && end <= row.scrollLeft + row.clientWidth) return
|
|
60
|
+
const left = start < row.scrollLeft ? start : end - row.clientWidth
|
|
61
|
+
row.scrollTo({ left, behavior: reduceMotion.value ? "auto" : "smooth" })
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
48
65
|
function select(value: string) {
|
|
49
66
|
if (props.modelValue === undefined) current.value = value
|
|
50
67
|
emit("update:modelValue", value)
|
|
@@ -101,7 +118,8 @@ function releaseFocusAfterPointer(event: PointerEvent) {
|
|
|
101
118
|
@update:model-value="(v) => select(v as string)"
|
|
102
119
|
>
|
|
103
120
|
<TabsList
|
|
104
|
-
|
|
121
|
+
ref="list"
|
|
122
|
+
class="max-w-full gap-1 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
|
105
123
|
:class="[
|
|
106
124
|
block ? 'flex w-full' : 'inline-flex',
|
|
107
125
|
variant === 'contained' ? 'rounded-control border border-line bg-surface-soft p-1' : '',
|
|
@@ -113,7 +131,7 @@ function releaseFocusAfterPointer(event: PointerEvent) {
|
|
|
113
131
|
v-for="item in items"
|
|
114
132
|
:key="item.value"
|
|
115
133
|
:value="item.value"
|
|
116
|
-
class="relative inline-flex items-center justify-center gap-1.5 text-sm outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring"
|
|
134
|
+
class="relative inline-flex shrink-0 items-center justify-center gap-1.5 whitespace-nowrap text-sm outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring"
|
|
117
135
|
:class="[
|
|
118
136
|
block ? 'flex-1' : '',
|
|
119
137
|
variant === 'underline' ? 'rounded-t-md px-3 pt-1.5 pb-2' : 'rounded-md px-3 py-1.5',
|