@leavepulse/ui 0.90.0 → 0.92.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/{LpCommandPalette-CO_5TFOQ.css → LpCommandPalette-D9ZuNQmc.css} +20 -20
- package/dist/assets/{LpTable-DAVNb-x2.css → LpTable-B3hNJQVl.css} +48 -25
- package/dist/components/LpCommandPalette.vue.d.ts +11 -3
- package/dist/components/LpCommandPalette.vue.js +2 -2
- package/dist/components/LpTable.vue.d.ts +15 -0
- package/dist/components/LpTable.vue.js +36 -25
- package/dist/components/commandSearch.d.ts +2 -0
- package/dist/components/commandSearch.js +9 -1
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/LpCommandPalette.vue +13 -5
- package/src/components/LpTable.vue +58 -6
- package/src/components/commandSearch.ts +23 -1
- package/src/index.ts +1 -1
|
@@ -5,28 +5,28 @@
|
|
|
5
5
|
* pane from its right edge as it grows. Animating the columns themselves made
|
|
6
6
|
* every line of the list and the pane re-wrap on every frame.
|
|
7
7
|
*/
|
|
8
|
-
.lp-cmd-dialog[data-v-
|
|
8
|
+
.lp-cmd-dialog[data-v-d51dc98f] {
|
|
9
9
|
--lp-cmd-list: min(92vw, 40rem);
|
|
10
10
|
--lp-cmd-pane: max(0px, min(26rem, 96vw - var(--lp-cmd-list)));
|
|
11
11
|
width: var(--lp-cmd-list);
|
|
12
12
|
}
|
|
13
|
-
.lp-cmd-split[data-v-
|
|
13
|
+
.lp-cmd-split[data-v-d51dc98f] {
|
|
14
14
|
transition:
|
|
15
15
|
width 300ms var(--ease-emphasized, ease),
|
|
16
16
|
min-height 300ms var(--ease-emphasized, ease);
|
|
17
17
|
}
|
|
18
18
|
@media (min-width: 768px) {
|
|
19
|
-
.lp-cmd-split .lp-cmd-list[data-v-
|
|
19
|
+
.lp-cmd-split .lp-cmd-list[data-v-d51dc98f] {
|
|
20
20
|
flex: 0 0 auto;
|
|
21
21
|
/* the dialog's 1px border on each side */
|
|
22
22
|
width: calc(var(--lp-cmd-list) - 2px);
|
|
23
23
|
}
|
|
24
|
-
.lp-cmd-dialog[data-pane="open"][data-v-
|
|
24
|
+
.lp-cmd-dialog[data-pane="open"][data-v-d51dc98f] {
|
|
25
25
|
width: calc(var(--lp-cmd-list) + var(--lp-cmd-pane));
|
|
26
26
|
min-height: min(76vh, 36rem);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
.lp-cmd-pane[data-v-
|
|
29
|
+
.lp-cmd-pane[data-v-d51dc98f] {
|
|
30
30
|
width: var(--lp-cmd-pane);
|
|
31
31
|
}
|
|
32
32
|
/*
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
* desktop window keeps the card.
|
|
37
37
|
*/
|
|
38
38
|
@media (hover: none) and (pointer: coarse) and (max-width: 639px) {
|
|
39
|
-
.lp-cmd-dialog[data-v-
|
|
39
|
+
.lp-cmd-dialog[data-v-d51dc98f] {
|
|
40
40
|
inset: var(--lp-cmd-vy, 0) auto auto var(--lp-cmd-vx, 0);
|
|
41
41
|
translate: none;
|
|
42
42
|
width: var(--lp-cmd-vw, 100%);
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
padding-bottom: env(safe-area-inset-bottom);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
.lp-cmd-dialog[data-editing] .lp-cmd-pane[data-v-
|
|
51
|
+
.lp-cmd-dialog[data-editing] .lp-cmd-pane[data-v-d51dc98f] {
|
|
52
52
|
width: 100%;
|
|
53
53
|
}
|
|
54
|
-
.lp-cmd-dialog[data-editing][data-v-
|
|
54
|
+
.lp-cmd-dialog[data-editing][data-v-d51dc98f] {
|
|
55
55
|
width: var(--lp-cmd-list);
|
|
56
56
|
min-height: 0;
|
|
57
57
|
}
|
|
@@ -59,43 +59,43 @@
|
|
|
59
59
|
* The pane rides in from the right edge while the dialog opens up to it, and
|
|
60
60
|
* leaves the same way, for as long as the dialog takes to close over it.
|
|
61
61
|
*/
|
|
62
|
-
.lp-cmd-pane-enter-active[data-v-
|
|
63
|
-
.lp-cmd-pane-leave-active[data-v-
|
|
62
|
+
.lp-cmd-pane-enter-active[data-v-d51dc98f],
|
|
63
|
+
.lp-cmd-pane-leave-active[data-v-d51dc98f] {
|
|
64
64
|
transition:
|
|
65
65
|
transform 300ms var(--ease-emphasized, ease),
|
|
66
66
|
opacity 300ms ease;
|
|
67
67
|
}
|
|
68
|
-
.lp-cmd-pane-enter-from[data-v-
|
|
69
|
-
.lp-cmd-pane-leave-to[data-v-
|
|
68
|
+
.lp-cmd-pane-enter-from[data-v-d51dc98f],
|
|
69
|
+
.lp-cmd-pane-leave-to[data-v-d51dc98f] {
|
|
70
70
|
opacity: 0;
|
|
71
71
|
transform: translateX(40%);
|
|
72
72
|
}
|
|
73
|
-
.lp-cmd-para[data-v-
|
|
73
|
+
.lp-cmd-para[data-v-d51dc98f] {
|
|
74
74
|
--rise: 4px;
|
|
75
75
|
animation: rise-in 260ms ease-out both;
|
|
76
76
|
}
|
|
77
77
|
@media (prefers-reduced-motion: reduce) {
|
|
78
|
-
.lp-cmd-split[data-v-
|
|
79
|
-
.lp-cmd-pane-enter-active[data-v-
|
|
80
|
-
.lp-cmd-pane-leave-active[data-v-
|
|
78
|
+
.lp-cmd-split[data-v-d51dc98f],
|
|
79
|
+
.lp-cmd-pane-enter-active[data-v-d51dc98f],
|
|
80
|
+
.lp-cmd-pane-leave-active[data-v-d51dc98f] {
|
|
81
81
|
transition: none;
|
|
82
82
|
}
|
|
83
|
-
.lp-cmd-para[data-v-
|
|
83
|
+
.lp-cmd-para[data-v-d51dc98f] {
|
|
84
84
|
animation: none;
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
-
.lp-cmd-excerpt[data-v-
|
|
87
|
+
.lp-cmd-excerpt[data-v-d51dc98f] {
|
|
88
88
|
display: -webkit-box;
|
|
89
89
|
-webkit-box-orient: vertical;
|
|
90
90
|
overflow: hidden;
|
|
91
91
|
}
|
|
92
92
|
/* Each result eases in; honours reduced motion. */
|
|
93
|
-
.lp-cmd-item[data-v-
|
|
93
|
+
.lp-cmd-item[data-v-d51dc98f] {
|
|
94
94
|
--rise: 3px;
|
|
95
95
|
animation: rise-in 140ms ease-out both;
|
|
96
96
|
}
|
|
97
97
|
@media (prefers-reduced-motion: reduce) {
|
|
98
|
-
.lp-cmd-item[data-v-
|
|
98
|
+
.lp-cmd-item[data-v-d51dc98f] {
|
|
99
99
|
animation: none;
|
|
100
100
|
}
|
|
101
101
|
}
|
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
|
|
2
|
+
/*
|
|
3
|
+
* A list line's facts, one dot between each two that are there. Every field
|
|
4
|
+
* carries its own dot before it; the row sits one dot to the left inside a
|
|
5
|
+
* box that clips, so the dot opening the line, or any wrapped line, falls
|
|
6
|
+
* outside and only the dots between facts are seen. A cell whose slot drew
|
|
7
|
+
* nothing (a v-if that failed, an empty value) is left out, dot and all.
|
|
8
|
+
*/
|
|
9
|
+
.lp-line[data-v-62285817] {
|
|
10
|
+
--lp-line-sep: 1rem;
|
|
11
|
+
}
|
|
12
|
+
.lp-line-row[data-v-62285817] {
|
|
13
|
+
margin-left: calc(-1 * var(--lp-line-sep));
|
|
14
|
+
}
|
|
15
|
+
.lp-line-field[data-v-62285817]::before {
|
|
16
|
+
content: "·" / "";
|
|
17
|
+
display: inline-block;
|
|
18
|
+
width: var(--lp-line-sep);
|
|
19
|
+
text-align: center;
|
|
20
|
+
}
|
|
21
|
+
.lp-line-field[data-v-62285817]:has(> .lp-line-value:empty) {
|
|
22
|
+
display: none;
|
|
23
|
+
}
|
|
24
|
+
|
|
2
25
|
/*
|
|
3
26
|
* Rows arriving, leaving and moving as the filter changes.
|
|
4
27
|
*
|
|
@@ -11,17 +34,17 @@
|
|
|
11
34
|
* fade before closing the gap and the whole list lurches at the end instead of
|
|
12
35
|
* gliding.
|
|
13
36
|
*/
|
|
14
|
-
.lp-row-enter-active[data-v-
|
|
15
|
-
.lp-row-leave-active[data-v-
|
|
37
|
+
.lp-row-enter-active[data-v-62285817],
|
|
38
|
+
.lp-row-leave-active[data-v-62285817] {
|
|
16
39
|
transition:
|
|
17
40
|
opacity var(--duration-fast) var(--ease-emphasized),
|
|
18
41
|
transform var(--duration-fast) var(--ease-emphasized);
|
|
19
42
|
}
|
|
20
|
-
.lp-row-move[data-v-
|
|
43
|
+
.lp-row-move[data-v-62285817] {
|
|
21
44
|
transition: transform var(--duration-medium) var(--ease-emphasized);
|
|
22
45
|
}
|
|
23
|
-
.lp-row-enter-from[data-v-
|
|
24
|
-
.lp-row-leave-to[data-v-
|
|
46
|
+
.lp-row-enter-from[data-v-62285817],
|
|
47
|
+
.lp-row-leave-to[data-v-62285817] {
|
|
25
48
|
opacity: 0;
|
|
26
49
|
transform: translateX(-0.5rem);
|
|
27
50
|
}
|
|
@@ -35,7 +58,7 @@
|
|
|
35
58
|
* drops out of the table layout entirely. Nothing sits below this row to be
|
|
36
59
|
* dragged around, so simply not animating its exit costs nothing.
|
|
37
60
|
*/
|
|
38
|
-
.lp-row-empty.lp-row-leave-active[data-v-
|
|
61
|
+
.lp-row-empty.lp-row-leave-active[data-v-62285817] {
|
|
39
62
|
display: none;
|
|
40
63
|
}
|
|
41
64
|
|
|
@@ -54,7 +77,7 @@
|
|
|
54
77
|
* once anything is selected. An earlier version collapsed the column to nothing
|
|
55
78
|
* and floated the hover box over the first cell, where it sat on the text.
|
|
56
79
|
*/
|
|
57
|
-
.lp-tick[data-v-
|
|
80
|
+
.lp-tick[data-v-62285817] {
|
|
58
81
|
display: flex;
|
|
59
82
|
align-items: center;
|
|
60
83
|
max-width: 0;
|
|
@@ -66,17 +89,17 @@
|
|
|
66
89
|
padding var(--duration-medium) var(--ease-emphasized),
|
|
67
90
|
opacity var(--duration-fast) ease;
|
|
68
91
|
}
|
|
69
|
-
.lp-tick-open[data-v-
|
|
92
|
+
.lp-tick-open[data-v-62285817] {
|
|
70
93
|
max-width: 3.25rem;
|
|
71
94
|
padding-inline: 1rem;
|
|
72
95
|
opacity: 1;
|
|
73
96
|
}
|
|
74
|
-
.lp-tick-hidden[data-v-
|
|
75
|
-
.lp-tick-cue[data-v-
|
|
97
|
+
.lp-tick-hidden[data-v-62285817],
|
|
98
|
+
.lp-tick-cue[data-v-62285817] {
|
|
76
99
|
opacity: 0;
|
|
77
100
|
}
|
|
78
|
-
tr:hover .lp-tick-cue[data-v-
|
|
79
|
-
.lp-tick-cue[data-v-
|
|
101
|
+
tr:hover .lp-tick-cue[data-v-62285817],
|
|
102
|
+
.lp-tick-cue[data-v-62285817]:focus-within {
|
|
80
103
|
opacity: 1;
|
|
81
104
|
}
|
|
82
105
|
|
|
@@ -84,15 +107,15 @@ tr:hover .lp-tick-cue[data-v-f0750892],
|
|
|
84
107
|
turned down the rows change without animating rather than not changing. */
|
|
85
108
|
/* Only where there is a hover to reveal it; a finger long-presses instead. */
|
|
86
109
|
@media (hover: none) {
|
|
87
|
-
tr:hover .lp-tick-cue[data-v-
|
|
110
|
+
tr:hover .lp-tick-cue[data-v-62285817] {
|
|
88
111
|
opacity: 0;
|
|
89
112
|
}
|
|
90
113
|
|
|
91
114
|
/* Nor is there anything for the column's width to hold until then: on a
|
|
92
115
|
touch screen it stays closed until a long press starts selecting, and
|
|
93
116
|
eases open like the rest of the column. */
|
|
94
|
-
.lp-tick-hidden[data-v-
|
|
95
|
-
.lp-tick-cue[data-v-
|
|
117
|
+
.lp-tick-hidden[data-v-62285817],
|
|
118
|
+
.lp-tick-cue[data-v-62285817] {
|
|
96
119
|
max-width: 0;
|
|
97
120
|
padding-inline: 0;
|
|
98
121
|
}
|
|
@@ -102,7 +125,7 @@ tr:hover .lp-tick-cue[data-v-f0750892] {
|
|
|
102
125
|
* A list line's tick opens from nothing, as the table's column does, so lines
|
|
103
126
|
* do not carry an empty gap until selection starts.
|
|
104
127
|
*/
|
|
105
|
-
.lp-tick-line[data-v-
|
|
128
|
+
.lp-tick-line[data-v-62285817] {
|
|
106
129
|
max-width: 2rem;
|
|
107
130
|
opacity: 1;
|
|
108
131
|
}
|
|
@@ -112,28 +135,28 @@ tr:hover .lp-tick-cue[data-v-f0750892] {
|
|
|
112
135
|
* open; a cue shows it only while
|
|
113
136
|
* the card is pointed at or holds focus, the same rule as a row's.
|
|
114
137
|
*/
|
|
115
|
-
.lp-tick-card[data-v-
|
|
138
|
+
.lp-tick-card[data-v-62285817] {
|
|
116
139
|
max-width: none;
|
|
117
140
|
padding-inline: 0;
|
|
118
141
|
opacity: 1;
|
|
119
142
|
}
|
|
120
|
-
.lp-tick[data-cue][data-v-
|
|
143
|
+
.lp-tick[data-cue][data-v-62285817] {
|
|
121
144
|
opacity: 0;
|
|
122
145
|
}
|
|
123
|
-
.lp-item:hover .lp-tick[data-cue][data-v-
|
|
124
|
-
.lp-tick[data-cue][data-v-
|
|
146
|
+
.lp-item:hover .lp-tick[data-cue][data-v-62285817],
|
|
147
|
+
.lp-tick[data-cue][data-v-62285817]:focus-within {
|
|
125
148
|
opacity: 1;
|
|
126
149
|
}
|
|
127
150
|
@media (hover: none) {
|
|
128
|
-
.lp-item:hover .lp-tick[data-cue][data-v-
|
|
151
|
+
.lp-item:hover .lp-tick[data-cue][data-v-62285817] {
|
|
129
152
|
opacity: 0;
|
|
130
153
|
}
|
|
131
154
|
}
|
|
132
155
|
@media (prefers-reduced-motion: reduce) {
|
|
133
|
-
.lp-row-enter-active[data-v-
|
|
134
|
-
.lp-row-leave-active[data-v-
|
|
135
|
-
.lp-row-move[data-v-
|
|
136
|
-
.lp-tick[data-v-
|
|
156
|
+
.lp-row-enter-active[data-v-62285817],
|
|
157
|
+
.lp-row-leave-active[data-v-62285817],
|
|
158
|
+
.lp-row-move[data-v-62285817],
|
|
159
|
+
.lp-tick[data-v-62285817] {
|
|
137
160
|
transition: none;
|
|
138
161
|
}
|
|
139
162
|
}
|
|
@@ -53,11 +53,19 @@ export interface Command {
|
|
|
53
53
|
group?: string;
|
|
54
54
|
disabled?: boolean;
|
|
55
55
|
/**
|
|
56
|
-
* Hidden from the default (empty-query) list
|
|
57
|
-
*
|
|
58
|
-
*
|
|
56
|
+
* Hidden from the default (empty-query) list and surfaced only when the query
|
|
57
|
+
* names it: every typed word starts a word of its label or keywords. A word
|
|
58
|
+
* inside another word, the description or a near miss does not reveal it.
|
|
59
|
+
* For rarely-used or destructive actions (prune, decommission, …) that
|
|
60
|
+
* shouldn't clutter the palette but stay reachable.
|
|
59
61
|
*/
|
|
60
62
|
hidden?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* For a hidden command: the words that reveal it, instead of its label and
|
|
65
|
+
* keywords. A variant whose label repeats its parent's word ("Vacation: sick
|
|
66
|
+
* leave…") then stays out of a search for the parent and shows up for "sick".
|
|
67
|
+
*/
|
|
68
|
+
revealOn?: string[];
|
|
61
69
|
/**
|
|
62
70
|
* Picking this opens a mode instead of closing: a nested list, either given
|
|
63
71
|
* outright or fetched from the query typed inside it. This is what keeps a
|
|
@@ -26,7 +26,7 @@ import { parsePaletteQuery, parseCommandQuery, searchCommandItems, applyQueryFil
|
|
|
26
26
|
import { PALETTE_MODES, modeChipLabel, settleFetched, fallbackRows, readRecent, resolveRecentItems, recentVisible, commandHasPreview, matchModePrefix, isSafeHref, absoluteHref, commandKey, recentKey, pushRecent, moreRowId, toRecentItem } from "./commandPalette.js";
|
|
27
27
|
import { useView, usePortalTarget, useOutsideGuard, viewWindow } from "../composables/useView.js";
|
|
28
28
|
import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
|
|
29
|
-
import '../assets/LpCommandPalette-
|
|
29
|
+
import '../assets/LpCommandPalette-D9ZuNQmc.css';const _hoisted_1 = { class: "relative flex shrink-0 items-center gap-2.5 border-b border-line px-4" };
|
|
30
30
|
const _hoisted_2 = ["title", "onClick"];
|
|
31
31
|
const _hoisted_3 = ["readonly", "aria-label", "aria-expanded", "aria-controls", "aria-activedescendant", "placeholder", "onKeydown"];
|
|
32
32
|
const _hoisted_4 = { class: "shrink-0 rounded-small border border-line px-1.5 py-0.5 text-[10px] text-muted [@media(hover:none)_and_(pointer:coarse)]:hidden" };
|
|
@@ -1400,7 +1400,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1400
1400
|
};
|
|
1401
1401
|
}
|
|
1402
1402
|
});
|
|
1403
|
-
const LpCommandPalette = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
1403
|
+
const LpCommandPalette = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-d51dc98f"]]);
|
|
1404
1404
|
export {
|
|
1405
1405
|
LpCommandPalette as default
|
|
1406
1406
|
};
|
|
@@ -2,6 +2,8 @@ import { type ColumnPin, type ColumnPreset, type TableLayout } from "./tableFeat
|
|
|
2
2
|
import { type CsvOptions } from "./exportData";
|
|
3
3
|
import { type TableFilters } from "./tableFilter";
|
|
4
4
|
import { type TableRowMenuResult } from "./tableRowMenu";
|
|
5
|
+
/** How a column reads in a list line: alone, after its label, or not at all. */
|
|
6
|
+
export type TableListMode = boolean | "label";
|
|
5
7
|
export interface TableColumn<Row> {
|
|
6
8
|
key: string;
|
|
7
9
|
label: string;
|
|
@@ -58,6 +60,19 @@ export interface TableColumn<Row> {
|
|
|
58
60
|
* user's own show/hide applies as usual.
|
|
59
61
|
*/
|
|
60
62
|
hideBelow?: number;
|
|
63
|
+
/**
|
|
64
|
+
* How the column reads in a list line (the `list` layout, and a table drawn
|
|
65
|
+
* as a list below `layoutBelow`), where there are no headers to name it.
|
|
66
|
+
* `true` (default) puts the value alone in the line; `"label"` prefixes its
|
|
67
|
+
* label, for a figure that means nothing on its own ("Cores 28"); `false`
|
|
68
|
+
* leaves it out: an actions column (the row menu holds the same actions),
|
|
69
|
+
* or a fact the line already says. A function decides per row, for a fact
|
|
70
|
+
* worth a place only when it holds something ("needs attention", a stack
|
|
71
|
+
* that is set): a cell slot cannot say "nothing" itself, since an empty
|
|
72
|
+
* slot falls back to the raw value. Cards always show labels and are not
|
|
73
|
+
* affected.
|
|
74
|
+
*/
|
|
75
|
+
list?: TableListMode | ((row: Row) => TableListMode);
|
|
61
76
|
/** Stick to this edge while the table scrolls sideways. Pinned columns move to that edge. */
|
|
62
77
|
pin?: ColumnPin;
|
|
63
78
|
/** Bounds in px for `resizableColumns`. */
|
|
@@ -34,7 +34,7 @@ import { toCsv, downloadFile, fileStem } from "./exportData.js";
|
|
|
34
34
|
import { toHiddenSets, sameHidden, summarizeShown, parseTableState, mergeRestored, serializeTableState, fromHiddenSets } from "./tableFilter.js";
|
|
35
35
|
import { resolveRowMenu } from "./tableRowMenu.js";
|
|
36
36
|
import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
|
|
37
|
-
import '../assets/LpTable-
|
|
37
|
+
import '../assets/LpTable-B3hNJQVl.css';const _hoisted_1 = {
|
|
38
38
|
key: 0,
|
|
39
39
|
class: "flex min-h-8 flex-wrap items-center gap-x-3 gap-y-2 pb-3 text-xs"
|
|
40
40
|
};
|
|
@@ -132,13 +132,15 @@ const _hoisted_44 = {
|
|
|
132
132
|
};
|
|
133
133
|
const _hoisted_45 = {
|
|
134
134
|
key: 1,
|
|
135
|
-
class: "
|
|
135
|
+
class: "lp-line min-w-0 overflow-hidden text-xs text-muted"
|
|
136
136
|
};
|
|
137
|
-
const _hoisted_46 = {
|
|
137
|
+
const _hoisted_46 = { class: "lp-line-row flex flex-wrap items-center gap-y-0.5" };
|
|
138
|
+
const _hoisted_47 = ["title"];
|
|
139
|
+
const _hoisted_48 = {
|
|
138
140
|
key: 0,
|
|
139
|
-
|
|
141
|
+
class: "lp-line-label"
|
|
140
142
|
};
|
|
141
|
-
const
|
|
143
|
+
const _hoisted_49 = { class: "lp-line-value" };
|
|
142
144
|
const __default__ = { inheritAttrs: false };
|
|
143
145
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
144
146
|
...__default__,
|
|
@@ -936,6 +938,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
936
938
|
);
|
|
937
939
|
const titleColumn = computed(() => cardColumns.value[0]);
|
|
938
940
|
const fieldColumns = computed(() => cardColumns.value.slice(1).filter((col) => col.label));
|
|
941
|
+
const lineColumns = computed(() => fieldColumns.value.filter((col) => col.list !== false));
|
|
942
|
+
function lineMode(col, row) {
|
|
943
|
+
return (typeof col.list === "function" ? col.list(row) : col.list) ?? true;
|
|
944
|
+
}
|
|
939
945
|
watch(chosenLayout, (layout) => {
|
|
940
946
|
if (storageKey.value && restored.value) store.set(`${storageKey.value}:layout`, layout);
|
|
941
947
|
});
|
|
@@ -1840,26 +1846,31 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1840
1846
|
createTextVNode(toDisplayString(entry.row[titleColumn.value.key]), 1)
|
|
1841
1847
|
], true)
|
|
1842
1848
|
])) : createCommentVNode("", true),
|
|
1843
|
-
|
|
1844
|
-
(
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
ci > 0 ? (openBlock(), createElementBlock("span", _hoisted_46, "·")) : createCommentVNode("", true),
|
|
1849
|
-
createElementVNode("span", {
|
|
1850
|
-
class: "min-w-0 truncate",
|
|
1851
|
-
title: col.label
|
|
1849
|
+
lineColumns.value.length ? (openBlock(), createElementBlock("div", _hoisted_45, [
|
|
1850
|
+
createElementVNode("div", _hoisted_46, [
|
|
1851
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(lineColumns.value, (col) => {
|
|
1852
|
+
return openBlock(), createElementBlock(Fragment, {
|
|
1853
|
+
key: col.key
|
|
1852
1854
|
}, [
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
},
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1855
|
+
lineMode(col, entry.row) !== false ? (openBlock(), createElementBlock("span", {
|
|
1856
|
+
key: 0,
|
|
1857
|
+
class: "lp-line-field min-w-0 truncate",
|
|
1858
|
+
title: col.label
|
|
1859
|
+
}, [
|
|
1860
|
+
lineMode(col, entry.row) === "label" ? (openBlock(), createElementBlock("span", _hoisted_48, toDisplayString(`${col.label} `), 1)) : createCommentVNode("", true),
|
|
1861
|
+
createElementVNode("span", _hoisted_49, [
|
|
1862
|
+
renderSlot(_ctx.$slots, `cell-${col.key}`, {
|
|
1863
|
+
row: entry.row,
|
|
1864
|
+
value: entry.row[col.key],
|
|
1865
|
+
openMenu: openRowMenu
|
|
1866
|
+
}, () => [
|
|
1867
|
+
createTextVNode(toDisplayString(entry.row[col.key]), 1)
|
|
1868
|
+
], true)
|
|
1869
|
+
])
|
|
1870
|
+
], 8, _hoisted_47)) : createCommentVNode("", true)
|
|
1871
|
+
], 64);
|
|
1872
|
+
}), 128))
|
|
1873
|
+
])
|
|
1863
1874
|
])) : createCommentVNode("", true)
|
|
1864
1875
|
], true)
|
|
1865
1876
|
]))
|
|
@@ -1887,7 +1898,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1887
1898
|
};
|
|
1888
1899
|
}
|
|
1889
1900
|
});
|
|
1890
|
-
const LpTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
1901
|
+
const LpTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-62285817"]]);
|
|
1891
1902
|
export {
|
|
1892
1903
|
LpTable as default
|
|
1893
1904
|
};
|
|
@@ -26,9 +26,17 @@ function nearWord(term, label) {
|
|
|
26
26
|
if (term.length < 5) return false;
|
|
27
27
|
return label.split(/[^\p{L}\p{N}]+/u).some((word) => withinOne(term, word) || word.length > term.length && withinOne(term, word.slice(0, term.length)));
|
|
28
28
|
}
|
|
29
|
+
function wordStarts(text, term) {
|
|
30
|
+
return text.split(/[^\p{L}\p{N}]+/u).some((word) => word.startsWith(term));
|
|
31
|
+
}
|
|
32
|
+
function named(command, terms) {
|
|
33
|
+
const words = command.revealOn ? command.revealOn.map((word) => word.toLowerCase()) : [command.label.toLowerCase(), ...(command.keywords ?? []).map((keyword) => keyword.toLowerCase())];
|
|
34
|
+
return terms.include.length > 0 && terms.include.every((term) => words.some((text) => wordStarts(text, term)));
|
|
35
|
+
}
|
|
29
36
|
function score(command, terms) {
|
|
37
|
+
if (command.hidden && !named(command, terms)) return 0;
|
|
30
38
|
const label = command.label.toLowerCase();
|
|
31
|
-
const keywords =
|
|
39
|
+
const keywords = [...command.keywords ?? [], ...command.revealOn ?? []].map((keyword) => keyword.toLowerCase());
|
|
32
40
|
const description = command.description?.toLowerCase() ?? "";
|
|
33
41
|
const group = command.group?.toLowerCase() ?? "";
|
|
34
42
|
if (terms.exclude.some((term) => matches(command, term)) || terms.groups.some((term) => !group.includes(term))) return 0;
|
package/dist/index.d.ts
CHANGED
|
@@ -191,7 +191,7 @@ export { default as LpStepper } from "./components/LpStepper.vue";
|
|
|
191
191
|
export type { Step } from "./components/LpStepper.vue";
|
|
192
192
|
export { default as LpSwitch } from "./components/LpSwitch.vue";
|
|
193
193
|
export { default as LpTable } from "./components/LpTable.vue";
|
|
194
|
-
export type { SortState, TableColumn, TablePage, TableQuery } from "./components/LpTable.vue";
|
|
194
|
+
export type { SortState, TableColumn, TableListMode, TablePage, TableQuery } from "./components/LpTable.vue";
|
|
195
195
|
export type { TableLabels } from "./components/LpTable.vue";
|
|
196
196
|
export type { TableCellEdit, TableExportOptions, TableSelectionToken } from "./components/LpTable.vue";
|
|
197
197
|
export type { ColumnPin, ColumnPreset, TableLayout } from "./components/tableFeatures";
|
package/package.json
CHANGED
|
@@ -143,11 +143,19 @@ export interface Command {
|
|
|
143
143
|
group?: string
|
|
144
144
|
disabled?: boolean
|
|
145
145
|
/**
|
|
146
|
-
* Hidden from the default (empty-query) list
|
|
147
|
-
*
|
|
148
|
-
*
|
|
146
|
+
* Hidden from the default (empty-query) list and surfaced only when the query
|
|
147
|
+
* names it: every typed word starts a word of its label or keywords. A word
|
|
148
|
+
* inside another word, the description or a near miss does not reveal it.
|
|
149
|
+
* For rarely-used or destructive actions (prune, decommission, …) that
|
|
150
|
+
* shouldn't clutter the palette but stay reachable.
|
|
149
151
|
*/
|
|
150
152
|
hidden?: boolean
|
|
153
|
+
/**
|
|
154
|
+
* For a hidden command: the words that reveal it, instead of its label and
|
|
155
|
+
* keywords. A variant whose label repeats its parent's word ("Vacation: sick
|
|
156
|
+
* leave…") then stays out of a search for the parent and shows up for "sick".
|
|
157
|
+
*/
|
|
158
|
+
revealOn?: string[]
|
|
151
159
|
/**
|
|
152
160
|
* Picking this opens a mode instead of closing: a nested list, either given
|
|
153
161
|
* outright or fetched from the query typed inside it. This is what keeps a
|
|
@@ -902,8 +910,8 @@ const local = computed<{ items: Command[]; terms: string[] }>(() => {
|
|
|
902
910
|
const filtered = query.value.trim()
|
|
903
911
|
? source.value.filter((c) => !c.fallback)
|
|
904
912
|
: source.value.filter((c) => !c.hidden && !c.fallback)
|
|
905
|
-
// Empty query: show everything EXCEPT hidden commands
|
|
906
|
-
//
|
|
913
|
+
// Empty query: show everything EXCEPT hidden commands. With a query, a hidden
|
|
914
|
+
// command ranks only when the query names it (see commandSearch).
|
|
907
915
|
if (!terms.include.length && !terms.exclude.length && !terms.groups.length) return { items: filtered, terms: [] }
|
|
908
916
|
return searchCommandItems(filtered, terms)
|
|
909
917
|
})
|
|
@@ -62,6 +62,9 @@ import {
|
|
|
62
62
|
} from "./tableFilter"
|
|
63
63
|
import { resolveRowMenu, type TableRowMenuResult } from "./tableRowMenu"
|
|
64
64
|
|
|
65
|
+
/** How a column reads in a list line: alone, after its label, or not at all. */
|
|
66
|
+
export type TableListMode = boolean | "label"
|
|
67
|
+
|
|
65
68
|
export interface TableColumn<Row> {
|
|
66
69
|
key: string
|
|
67
70
|
label: string
|
|
@@ -118,6 +121,19 @@ export interface TableColumn<Row> {
|
|
|
118
121
|
* user's own show/hide applies as usual.
|
|
119
122
|
*/
|
|
120
123
|
hideBelow?: number
|
|
124
|
+
/**
|
|
125
|
+
* How the column reads in a list line (the `list` layout, and a table drawn
|
|
126
|
+
* as a list below `layoutBelow`), where there are no headers to name it.
|
|
127
|
+
* `true` (default) puts the value alone in the line; `"label"` prefixes its
|
|
128
|
+
* label, for a figure that means nothing on its own ("Cores 28"); `false`
|
|
129
|
+
* leaves it out: an actions column (the row menu holds the same actions),
|
|
130
|
+
* or a fact the line already says. A function decides per row, for a fact
|
|
131
|
+
* worth a place only when it holds something ("needs attention", a stack
|
|
132
|
+
* that is set): a cell slot cannot say "nothing" itself, since an empty
|
|
133
|
+
* slot falls back to the raw value. Cards always show labels and are not
|
|
134
|
+
* affected.
|
|
135
|
+
*/
|
|
136
|
+
list?: TableListMode | ((row: Row) => TableListMode)
|
|
121
137
|
/** Stick to this edge while the table scrolls sideways. Pinned columns move to that edge. */
|
|
122
138
|
pin?: ColumnPin
|
|
123
139
|
/** Bounds in px for `resizableColumns`. */
|
|
@@ -1545,6 +1561,10 @@ const cardColumns = computed(() =>
|
|
|
1545
1561
|
)
|
|
1546
1562
|
const titleColumn = computed(() => cardColumns.value[0])
|
|
1547
1563
|
const fieldColumns = computed(() => cardColumns.value.slice(1).filter((col) => col.label))
|
|
1564
|
+
const lineColumns = computed(() => fieldColumns.value.filter((col) => col.list !== false))
|
|
1565
|
+
function lineMode(col: TableColumn<T>, row: T): TableListMode {
|
|
1566
|
+
return (typeof col.list === "function" ? col.list(row) : col.list) ?? true
|
|
1567
|
+
}
|
|
1548
1568
|
|
|
1549
1569
|
watch(chosenLayout, (layout) => {
|
|
1550
1570
|
if (storageKey.value && restored.value) store.set(`${storageKey.value}:layout`, layout)
|
|
@@ -2377,13 +2397,19 @@ usePaletteCommands(() => {
|
|
|
2377
2397
|
<div v-if="titleColumn" class="min-w-0 truncate font-medium text-ink">
|
|
2378
2398
|
<slot :name="`cell-${titleColumn.key}`" :row="entry.row" :value="entry.row[titleColumn.key]" :open-menu="openRowMenu">{{ entry.row[titleColumn.key] }}</slot>
|
|
2379
2399
|
</div>
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2400
|
+
<!-- Separators belong between the facts that are there: a cell
|
|
2401
|
+
that drew nothing takes no dot, and the dot that would open
|
|
2402
|
+
a wrapped line is clipped off (the inner row starts one
|
|
2403
|
+
separator to the left of the clip box). -->
|
|
2404
|
+
<div v-if="lineColumns.length" class="lp-line min-w-0 overflow-hidden text-xs text-muted">
|
|
2405
|
+
<div class="lp-line-row flex flex-wrap items-center gap-y-0.5">
|
|
2406
|
+
<template v-for="col in lineColumns" :key="col.key">
|
|
2407
|
+
<span v-if="lineMode(col, entry.row) !== false" class="lp-line-field min-w-0 truncate" :title="col.label">
|
|
2408
|
+
<span v-if="lineMode(col, entry.row) === 'label'" class="lp-line-label">{{ `${col.label} ` }}</span>
|
|
2409
|
+
<span class="lp-line-value"><slot :name="`cell-${col.key}`" :row="entry.row" :value="entry.row[col.key]" :open-menu="openRowMenu">{{ entry.row[col.key] }}</slot></span>
|
|
2385
2410
|
</span>
|
|
2386
|
-
|
|
2411
|
+
</template>
|
|
2412
|
+
</div>
|
|
2387
2413
|
</div>
|
|
2388
2414
|
</slot>
|
|
2389
2415
|
</div>
|
|
@@ -2403,6 +2429,32 @@ usePaletteCommands(() => {
|
|
|
2403
2429
|
</template>
|
|
2404
2430
|
|
|
2405
2431
|
<style scoped>
|
|
2432
|
+
/*
|
|
2433
|
+
* A list line's facts, one dot between each two that are there. Every field
|
|
2434
|
+
* carries its own dot before it; the row sits one dot to the left inside a
|
|
2435
|
+
* box that clips, so the dot opening the line, or any wrapped line, falls
|
|
2436
|
+
* outside and only the dots between facts are seen. A cell whose slot drew
|
|
2437
|
+
* nothing (a v-if that failed, an empty value) is left out, dot and all.
|
|
2438
|
+
*/
|
|
2439
|
+
.lp-line {
|
|
2440
|
+
--lp-line-sep: 1rem;
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
.lp-line-row {
|
|
2444
|
+
margin-left: calc(-1 * var(--lp-line-sep));
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
.lp-line-field::before {
|
|
2448
|
+
content: "·" / "";
|
|
2449
|
+
display: inline-block;
|
|
2450
|
+
width: var(--lp-line-sep);
|
|
2451
|
+
text-align: center;
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
.lp-line-field:has(> .lp-line-value:empty) {
|
|
2455
|
+
display: none;
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2406
2458
|
/*
|
|
2407
2459
|
* Rows arriving, leaving and moving as the filter changes.
|
|
2408
2460
|
*
|
|
@@ -3,6 +3,8 @@ export interface SearchableCommand {
|
|
|
3
3
|
description?: string
|
|
4
4
|
keywords?: string[]
|
|
5
5
|
group?: string
|
|
6
|
+
hidden?: boolean
|
|
7
|
+
revealOn?: string[]
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
export interface CommandSearchTerms {
|
|
@@ -50,9 +52,29 @@ function nearWord(term: string, label: string): boolean {
|
|
|
50
52
|
.some((word) => withinOne(term, word) || (word.length > term.length && withinOne(term, word.slice(0, term.length))))
|
|
51
53
|
}
|
|
52
54
|
|
|
55
|
+
function wordStarts(text: string, term: string): boolean {
|
|
56
|
+
return text.split(/[^\p{L}\p{N}]+/u).some((word) => word.startsWith(term))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/*
|
|
60
|
+
* A hidden command is there for whoever names it, not for whoever types a word
|
|
61
|
+
* it happens to contain. Every term has to start a word of its label or of a
|
|
62
|
+
* keyword (or of `revealOn`, which then replaces both): "пт" no longer digs a
|
|
63
|
+
* settings key out of "Транскрипты", and "vacation" no longer spills every
|
|
64
|
+
* hidden variant of it. Descriptions, mid-word hits and near misses do not
|
|
65
|
+
* reveal; the visible list keeps all of those.
|
|
66
|
+
*/
|
|
67
|
+
function named(command: SearchableCommand, terms: CommandSearchTerms): boolean {
|
|
68
|
+
const words = command.revealOn
|
|
69
|
+
? command.revealOn.map((word) => word.toLowerCase())
|
|
70
|
+
: [command.label.toLowerCase(), ...(command.keywords ?? []).map((keyword) => keyword.toLowerCase())]
|
|
71
|
+
return terms.include.length > 0 && terms.include.every((term) => words.some((text) => wordStarts(text, term)))
|
|
72
|
+
}
|
|
73
|
+
|
|
53
74
|
function score(command: SearchableCommand, terms: CommandSearchTerms): number {
|
|
75
|
+
if (command.hidden && !named(command, terms)) return 0
|
|
54
76
|
const label = command.label.toLowerCase()
|
|
55
|
-
const keywords = (command.keywords ?? []).map((keyword) => keyword.toLowerCase())
|
|
77
|
+
const keywords = [...(command.keywords ?? []), ...(command.revealOn ?? [])].map((keyword) => keyword.toLowerCase())
|
|
56
78
|
const description = command.description?.toLowerCase() ?? ""
|
|
57
79
|
const group = command.group?.toLowerCase() ?? ""
|
|
58
80
|
if (terms.exclude.some((term) => matches(command, term)) || terms.groups.some((term) => !group.includes(term))) return 0
|
package/src/index.ts
CHANGED
|
@@ -267,7 +267,7 @@ export { default as LpStepper } from "./components/LpStepper.vue"
|
|
|
267
267
|
export type { Step } from "./components/LpStepper.vue"
|
|
268
268
|
export { default as LpSwitch } from "./components/LpSwitch.vue"
|
|
269
269
|
export { default as LpTable } from "./components/LpTable.vue"
|
|
270
|
-
export type { SortState, TableColumn, TablePage, TableQuery } from "./components/LpTable.vue"
|
|
270
|
+
export type { SortState, TableColumn, TableListMode, TablePage, TableQuery } from "./components/LpTable.vue"
|
|
271
271
|
export type { TableLabels } from "./components/LpTable.vue"
|
|
272
272
|
export type { TableCellEdit, TableExportOptions, TableSelectionToken } from "./components/LpTable.vue"
|
|
273
273
|
export type { ColumnPin, ColumnPreset, TableLayout } from "./components/tableFeatures"
|