@k2b/ui 0.45.0 → 0.47.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/browser/actions/Dropdown.js +8 -1
- package/dist/browser/actions/Dropdown.js.map +1 -1
- package/dist/browser/actions/FilterChip.js +11 -5
- package/dist/browser/actions/FilterChip.js.map +1 -1
- package/dist/browser/actions/ViewTabs.js +225 -93
- package/dist/browser/actions/ViewTabs.js.map +1 -1
- package/dist/browser/chat/ChatDecision.js +10 -6
- package/dist/browser/chat/ChatDecision.js.map +1 -1
- package/dist/browser/content/FileGrid.js +125 -99
- package/dist/browser/content/FileGrid.js.map +1 -1
- package/dist/browser/content/VirtualFeed.js +26 -21
- package/dist/browser/content/VirtualFeed.js.map +1 -1
- package/dist/browser/inputs/Combobox.js +14 -6
- package/dist/browser/inputs/Combobox.js.map +1 -1
- package/dist/browser/inputs/choice.js +36 -9
- package/dist/browser/inputs/choice.js.map +1 -1
- package/dist/browser/layout/AnchoredPopover.js +2 -1
- package/dist/browser/layout/AnchoredPopover.js.map +1 -1
- package/dist/browser/layout/AppWorkspace.js +348 -444
- package/dist/browser/layout/AppWorkspace.js.map +1 -1
- package/dist/browser/layout/AppWorkspaceSidebarItemStatus.js +113 -0
- package/dist/browser/layout/AppWorkspaceSidebarItemStatus.js.map +1 -0
- package/dist/browser/layout/DetailPanel.js +130 -108
- package/dist/browser/layout/DetailPanel.js.map +1 -1
- package/dist/browser/layout/DetailSections.js +1123 -0
- package/dist/browser/layout/DetailSections.js.map +1 -0
- package/dist/browser/layout/DetailsToggle.js +32 -0
- package/dist/browser/layout/DetailsToggle.js.map +1 -0
- package/dist/browser/layout/Discussion.js +3 -0
- package/dist/browser/layout/Discussion.js.map +1 -1
- package/dist/browser/layout/MobileShell.js +141 -102
- package/dist/browser/layout/MobileShell.js.map +1 -1
- package/dist/browser/layout/PageTitle.js +33 -10
- package/dist/browser/layout/PageTitle.js.map +1 -1
- package/dist/browser/layout/app-workspace-controller.js +12 -0
- package/dist/browser/layout/app-workspace-controller.js.map +1 -1
- package/dist/browser/layout/detail-sections-messages.js +127 -0
- package/dist/browser/layout/detail-sections-messages.js.map +1 -0
- package/dist/browser/layout/index.js +3 -0
- package/dist/browser/layout/index.js.map +1 -1
- package/dist/browser/surfaces/Avatar.js +3 -1
- package/dist/browser/surfaces/Avatar.js.map +1 -1
- package/dist/browser/surfaces/AvatarStack.js +95 -61
- package/dist/browser/surfaces/AvatarStack.js.map +1 -1
- package/dist/browser/surfaces/ChangeList.js +77 -0
- package/dist/browser/surfaces/ChangeList.js.map +1 -0
- package/dist/browser/surfaces/index.js +2 -1
- package/dist/browser/surfaces/index.js.map +1 -1
- package/dist/browser/text-editor/TextEditor.js +19 -8
- package/dist/browser/text-editor/TextEditor.js.map +1 -1
- package/dist/ssr/actions/Dropdown.js +6 -0
- package/dist/ssr/actions/Dropdown.js.map +1 -1
- package/dist/ssr/actions/FilterChip.js +1 -1
- package/dist/ssr/actions/FilterChip.js.map +1 -1
- package/dist/ssr/actions/ViewTabs.js +144 -37
- package/dist/ssr/actions/ViewTabs.js.map +1 -1
- package/dist/ssr/chat/ChatDecision.js +10 -6
- package/dist/ssr/chat/ChatDecision.js.map +1 -1
- package/dist/ssr/content/FileGrid.js +36 -22
- package/dist/ssr/content/FileGrid.js.map +1 -1
- package/dist/ssr/content/VirtualFeed.js +26 -21
- package/dist/ssr/content/VirtualFeed.js.map +1 -1
- package/dist/ssr/inputs/Combobox.js +6 -3
- package/dist/ssr/inputs/Combobox.js.map +1 -1
- package/dist/ssr/inputs/choice.js +36 -9
- package/dist/ssr/inputs/choice.js.map +1 -1
- package/dist/ssr/layout/AnchoredPopover.js +2 -1
- package/dist/ssr/layout/AnchoredPopover.js.map +1 -1
- package/dist/ssr/layout/AppWorkspace.js +66 -132
- package/dist/ssr/layout/AppWorkspace.js.map +1 -1
- package/dist/ssr/layout/AppWorkspaceSidebarItemStatus.js +76 -0
- package/dist/ssr/layout/AppWorkspaceSidebarItemStatus.js.map +1 -0
- package/dist/ssr/layout/DetailPanel.js +18 -1
- package/dist/ssr/layout/DetailPanel.js.map +1 -1
- package/dist/ssr/layout/DetailSections.js +838 -0
- package/dist/ssr/layout/DetailSections.js.map +1 -0
- package/dist/ssr/layout/DetailsToggle.js +29 -0
- package/dist/ssr/layout/DetailsToggle.js.map +1 -0
- package/dist/ssr/layout/Discussion.js +3 -0
- package/dist/ssr/layout/Discussion.js.map +1 -1
- package/dist/ssr/layout/MobileShell.js +60 -29
- package/dist/ssr/layout/MobileShell.js.map +1 -1
- package/dist/ssr/layout/PageTitle.js +29 -6
- package/dist/ssr/layout/PageTitle.js.map +1 -1
- package/dist/ssr/layout/app-workspace-controller.js +12 -0
- package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
- package/dist/ssr/layout/detail-sections-messages.js +127 -0
- package/dist/ssr/layout/detail-sections-messages.js.map +1 -0
- package/dist/ssr/layout/index.js +3 -0
- package/dist/ssr/layout/index.js.map +1 -1
- package/dist/ssr/surfaces/Avatar.js +3 -1
- package/dist/ssr/surfaces/Avatar.js.map +1 -1
- package/dist/ssr/surfaces/AvatarStack.js +61 -32
- package/dist/ssr/surfaces/AvatarStack.js.map +1 -1
- package/dist/ssr/surfaces/ChangeList.js +37 -0
- package/dist/ssr/surfaces/ChangeList.js.map +1 -0
- package/dist/ssr/surfaces/index.js +2 -1
- package/dist/ssr/surfaces/index.js.map +1 -1
- package/dist/ssr/text-editor/TextEditor.js +19 -8
- package/dist/ssr/text-editor/TextEditor.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/actions/FilterChip.d.ts +4 -0
- package/dist/types/actions/ViewTabs.d.ts +11 -1
- package/dist/types/chat/ChatDecision.d.ts +5 -0
- package/dist/types/content/FileGrid.d.ts +6 -0
- package/dist/types/inputs/Combobox.d.ts +2 -0
- package/dist/types/inputs/choice.d.ts +9 -3
- package/dist/types/layout/AppWorkspace.d.ts +2 -28
- package/dist/types/layout/AppWorkspaceSidebarItemStatus.d.ts +30 -0
- package/dist/types/layout/DetailPanel.d.ts +9 -0
- package/dist/types/layout/DetailSections.d.ts +186 -0
- package/dist/types/layout/DetailsToggle.d.ts +13 -0
- package/dist/types/layout/MobileShell.d.ts +12 -0
- package/dist/types/layout/PageTitle.d.ts +7 -0
- package/dist/types/layout/detail-sections-messages.d.ts +61 -0
- package/dist/types/layout/index.d.ts +5 -1
- package/dist/types/surfaces/Avatar.d.ts +3 -0
- package/dist/types/surfaces/AvatarStack.d.ts +20 -1
- package/dist/types/surfaces/ChangeList.d.ts +20 -0
- package/dist/types/surfaces/index.d.ts +5 -3
- package/package.json +3 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { memo as _$memo } from "solid-js/web";
|
|
1
2
|
import { ssrAttribute as _$ssrAttribute } from "solid-js/web";
|
|
2
3
|
import { createComponent as _$createComponent } from "solid-js/web";
|
|
3
4
|
import { ssr as _$ssr } from "solid-js/web";
|
|
@@ -5,13 +6,15 @@ import { escape as _$escape } from "solid-js/web";
|
|
|
5
6
|
var _tmpl$ = ["<span class=\"k2b-view-tabs__count\">", "</span>"],
|
|
6
7
|
_tmpl$2 = ["<span class=\"k2b-view-tabs__label k2b-truncate-fade\">", "</span>"],
|
|
7
8
|
_tmpl$3 = ["<i", " aria-hidden=\"true\"></i>"],
|
|
8
|
-
_tmpl$4 = ["<span class=\"k2b-view-
|
|
9
|
-
_tmpl$5 = ["<
|
|
10
|
-
_tmpl$6 = ["<span class=\"k2b-view-
|
|
11
|
-
_tmpl$7 = "<
|
|
12
|
-
_tmpl$8 = ["<
|
|
13
|
-
_tmpl$9 =
|
|
14
|
-
_tmpl$0 =
|
|
9
|
+
_tmpl$4 = ["<span class=\"k2b-view-tabs__badge\">", "</span>"],
|
|
10
|
+
_tmpl$5 = ["<li class=\"k2b-view-tabs__item\"", "><a class=\"k2b-view-tabs__tab\"", ">", "</a></li>"],
|
|
11
|
+
_tmpl$6 = ["<span class=\"k2b-view-tabs__label\">", "</span>"],
|
|
12
|
+
_tmpl$7 = ["<span class=\"k2b-view-tabs__more-count\" data-level=\"wide\">+", "</span>"],
|
|
13
|
+
_tmpl$8 = ["<span class=\"k2b-view-tabs__more-count\" data-level=\"compact\">+", "</span>"],
|
|
14
|
+
_tmpl$9 = "<span class=\"k2b-view-tabs__more-count\" data-level=\"fit\"></span>",
|
|
15
|
+
_tmpl$0 = "<i class=\"ti ti-chevron-down\" aria-hidden=\"true\"></i>",
|
|
16
|
+
_tmpl$1 = ["<li class=\"k2b-view-tabs__more\"", ">", "</li>"],
|
|
17
|
+
_tmpl$10 = ["<nav class=\"", "\"", "><ul class=\"k2b-view-tabs__list\" data-scroll-fade-mode=\"both\" data-scroll-fade-axis=\"horizontal\"", ">", "", "", "</ul><script>", "</script></nav>"];
|
|
15
18
|
import { createEffect, createMemo, createSignal, For, on, onCleanup, onMount, Show } from "solid-js";
|
|
16
19
|
import { useUiMessages } from "../intl/messages";
|
|
17
20
|
import { createScrollFade, scrollFadeEdges } from "../layout/scroll-fade";
|
|
@@ -20,10 +23,14 @@ import Dropdown from "./Dropdown";
|
|
|
20
23
|
export function viewTabsLayout(items, current, max = 5) {
|
|
21
24
|
// At least one slot keeps the current view visible, including for an invalid limit.
|
|
22
25
|
const limit = Number.isFinite(max) ? Math.max(1, Math.floor(max)) : 5;
|
|
26
|
+
// Pinned views never overflow, so they take no slot.
|
|
27
|
+
const flowing = items.flatMap((item, index) => item.pinned ? [] : [index]);
|
|
28
|
+
const pinned = items.flatMap((item, index) => item.pinned ? [index] : []);
|
|
23
29
|
const currentIndex = items.findIndex(item => item.id === current);
|
|
30
|
+
const currentPosition = flowing.indexOf(currentIndex);
|
|
24
31
|
const visible = size => {
|
|
25
|
-
const indices =
|
|
26
|
-
if (
|
|
32
|
+
const indices = flowing.slice(0, size);
|
|
33
|
+
if (currentPosition >= size) indices[indices.length - 1] = currentIndex;
|
|
27
34
|
return indices;
|
|
28
35
|
};
|
|
29
36
|
const wide = visible(limit);
|
|
@@ -31,14 +38,50 @@ export function viewTabsLayout(items, current, max = 5) {
|
|
|
31
38
|
return {
|
|
32
39
|
wide,
|
|
33
40
|
compact,
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
pinned,
|
|
42
|
+
wideHidden: flowing.length - wide.length,
|
|
43
|
+
compactHidden: flowing.length - compact.length
|
|
36
44
|
};
|
|
37
45
|
}
|
|
38
46
|
|
|
39
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Self-contained: the server script embeds this before hydration. A fitting strip first keeps only the views that fit
|
|
49
|
+
* its room and counts the rest in More; then the open or focused view scrolls into the strip.
|
|
50
|
+
*/
|
|
40
51
|
function settleViewTabs(list, edges, focused) {
|
|
41
52
|
if (!(list instanceof HTMLElement)) return;
|
|
53
|
+
const more = list.querySelector(":scope > .k2b-view-tabs__more");
|
|
54
|
+
const fitCount = more?.querySelector('.k2b-view-tabs__more-count[data-level="fit"]');
|
|
55
|
+
const flowing = Array.from(list.querySelectorAll(":scope > .k2b-view-tabs__item:not([data-pinned])"));
|
|
56
|
+
for (const item of flowing) item.removeAttribute("data-fit-hidden");
|
|
57
|
+
more?.removeAttribute("data-fit");
|
|
58
|
+
// The shown entries against the strip's inner room, whatever its scroll and padding.
|
|
59
|
+
const overflows = () => {
|
|
60
|
+
const boxes = Array.from(list.children).filter(child => child instanceof HTMLElement && child.tagName === "LI" && child.getClientRects().length > 0).map(child => child.getBoundingClientRect());
|
|
61
|
+
if (boxes.length === 0) return false;
|
|
62
|
+
const style = getComputedStyle(list);
|
|
63
|
+
const room = list.clientWidth - (parseFloat(style.paddingLeft) || 0) - (parseFloat(style.paddingRight) || 0);
|
|
64
|
+
return Math.max(...boxes.map(box => box.right)) - Math.min(...boxes.map(box => box.left)) > room + 0.5;
|
|
65
|
+
};
|
|
66
|
+
// The phone head row keeps its scrolling strip: its CSS turns fitting off.
|
|
67
|
+
const fits = list.hasAttribute("data-fit-room") && getComputedStyle(list).getPropertyValue("--k2b-view-tabs-fit").trim() !== "none";
|
|
68
|
+
if (fits && more && fitCount && overflows()) {
|
|
69
|
+
// Count from the level on screen: the compact strip already keeps fewer views than the wide one.
|
|
70
|
+
const compact = more.querySelector('.k2b-view-tabs__more-count[data-level="compact"]');
|
|
71
|
+
const compactShown = Boolean(compact && getComputedStyle(compact).display !== "none");
|
|
72
|
+
const behind = Number(compactShown ? more.dataset.behindCompact : more.dataset.behindWide) || 0;
|
|
73
|
+
// The last views on screen leave first; the open view stays.
|
|
74
|
+
const candidates = flowing.filter(item => item.getClientRects().length > 0 && !item.querySelector("[aria-current=page]")).reverse();
|
|
75
|
+
more.setAttribute("data-fit", "");
|
|
76
|
+
let hidden = 0;
|
|
77
|
+
for (const item of candidates) {
|
|
78
|
+
item.setAttribute("data-fit-hidden", "");
|
|
79
|
+
hidden += 1;
|
|
80
|
+
fitCount.textContent = `+${behind + hidden}`;
|
|
81
|
+
if (!overflows()) break;
|
|
82
|
+
}
|
|
83
|
+
if (hidden === 0) more.removeAttribute("data-fit");
|
|
84
|
+
}
|
|
42
85
|
const current = focused ?? list.querySelector(".k2b-view-tabs__tab[aria-current=page]");
|
|
43
86
|
if (current && current.getClientRects().length > 0 && list.scrollWidth - list.clientWidth > 1) {
|
|
44
87
|
const bounds = list.getBoundingClientRect();
|
|
@@ -63,9 +106,20 @@ function settleViewTabs(list, edges, focused) {
|
|
|
63
106
|
const fade = edges(list);
|
|
64
107
|
if (fade) list.setAttribute("data-scroll-fade", fade);else list.removeAttribute("data-scroll-fade");
|
|
65
108
|
}
|
|
66
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* Also embedded, so it must not close over module state.
|
|
111
|
+
*
|
|
112
|
+
* A fitting strip converges by construction. It fits again only when the strip renders, web fonts finish loading, or
|
|
113
|
+
* a notification finds another width of the head lead or of an entry that the last fit left shown. A fit only shows
|
|
114
|
+
* and hides entries: the lead is an inline-size container that takes no width from the strip, and every entry keeps
|
|
115
|
+
* its own width (`flex: none`), so the notifications a fit causes find the widths it ended with and stop there.
|
|
116
|
+
*/
|
|
67
117
|
function watchViewTabs(list, settle, edges) {
|
|
68
118
|
if (!(list instanceof HTMLElement)) return;
|
|
119
|
+
const fits = list.hasAttribute("data-fit-room");
|
|
120
|
+
const roomOf = () => list.closest(".k2b-app-workspace__head-lead");
|
|
121
|
+
// What a fit depends on: the lead's width and each shown entry's width, which fonts, icons and labels change.
|
|
122
|
+
const measured = () => [roomOf()?.clientWidth ?? -1, ...Array.from(list.children, child => child.getBoundingClientRect().width)].join(" ");
|
|
69
123
|
// A DOM property survives hydration: one observer and focus listener per reused list.
|
|
70
124
|
let watch = list.__k2bViewTabsWatch;
|
|
71
125
|
if (!watch) {
|
|
@@ -76,29 +130,66 @@ function watchViewTabs(list, settle, edges) {
|
|
|
76
130
|
settle(list, edges, event.target);
|
|
77
131
|
}
|
|
78
132
|
};
|
|
133
|
+
const settleNow = () => {
|
|
134
|
+
const focused = document.activeElement;
|
|
135
|
+
if (watch?.preferCurrent === false && focused instanceof HTMLElement && list.contains(focused) && focused.matches(".k2b-view-tabs__tab:focus-visible")) settle(list, edges, focused);else settle(list, edges);
|
|
136
|
+
};
|
|
137
|
+
// Head actions parse after the views; settle again after their layout, before paint, and on later resizes.
|
|
138
|
+
const connect = () => {
|
|
139
|
+
if (!watch) return;
|
|
140
|
+
watch.resize.disconnect();
|
|
141
|
+
watch.resize.observe(list);
|
|
142
|
+
for (const child of Array.from(list.children)) watch.resize.observe(child);
|
|
143
|
+
const room = roomOf();
|
|
144
|
+
if (fits && room) watch.resize.observe(room);
|
|
145
|
+
};
|
|
146
|
+
// Fitting resizes the strip and possibly the head, which other observers may watch. Inside a ResizeObserver
|
|
147
|
+
// callback that would leave their notifications undelivered, so a fit runs in the next frame, still before it paints.
|
|
148
|
+
const schedule = () => {
|
|
149
|
+
if (!watch || watch.frame) return;
|
|
150
|
+
watch.frame = requestAnimationFrame(() => {
|
|
151
|
+
if (!watch) return;
|
|
152
|
+
watch.frame = 0;
|
|
153
|
+
if (!list.isConnected) return;
|
|
154
|
+
settleNow();
|
|
155
|
+
watch.room = measured();
|
|
156
|
+
});
|
|
157
|
+
};
|
|
79
158
|
watch = {
|
|
80
159
|
resize: new ResizeObserver(() => {
|
|
81
|
-
|
|
82
|
-
if (
|
|
160
|
+
// A scrolling strip only scrolls and settles at once.
|
|
161
|
+
if (!fits) return settleNow();
|
|
162
|
+
// Heights follow the fit, and hidden entries measure 0 by its own doing: only another measure counts.
|
|
163
|
+
if (watch && measured() !== watch.room) watch.schedule();
|
|
83
164
|
}),
|
|
84
165
|
focus,
|
|
85
|
-
preferCurrent: true
|
|
166
|
+
preferCurrent: true,
|
|
167
|
+
connect,
|
|
168
|
+
schedule,
|
|
169
|
+
frame: 0,
|
|
170
|
+
room: "",
|
|
171
|
+
fonts: () => watch?.schedule()
|
|
86
172
|
};
|
|
87
173
|
Object.defineProperty(list, "__k2bViewTabsWatch", {
|
|
88
174
|
value: watch,
|
|
89
175
|
configurable: true
|
|
90
176
|
});
|
|
91
177
|
list.addEventListener("focusin", focus);
|
|
178
|
+
// A first visit can paint before the web fonts load; the server frame and the client then fit the same metrics.
|
|
179
|
+
if (fits && document.fonts) {
|
|
180
|
+
document.fonts.addEventListener("loadingdone", watch.fonts);
|
|
181
|
+
void document.fonts.ready.then(watch.fonts);
|
|
182
|
+
}
|
|
92
183
|
}
|
|
93
184
|
watch.preferCurrent = true;
|
|
94
|
-
|
|
95
|
-
resize
|
|
96
|
-
} = watch;
|
|
97
|
-
resize.disconnect();
|
|
98
|
-
// Head actions parse after the views; settle again after their layout, before paint, and on later resizes.
|
|
99
|
-
resize.observe(list);
|
|
100
|
-
for (const child of Array.from(list.children)) resize.observe(child);
|
|
185
|
+
watch.connect();
|
|
101
186
|
settle(list, edges);
|
|
187
|
+
if (fits) {
|
|
188
|
+
watch.room = measured();
|
|
189
|
+
// The head's actions parse after the views. A frame's animation callbacks run before its first layout and
|
|
190
|
+
// paint, so this fit also counts their room in the first frame.
|
|
191
|
+
watch.schedule();
|
|
192
|
+
}
|
|
102
193
|
return watch;
|
|
103
194
|
}
|
|
104
195
|
const VIEW_TABS_SCRIPT = `(${watchViewTabs})(document.currentScript&&document.currentScript.previousElementSibling,${settleViewTabs},${scrollFadeEdges})`;
|
|
@@ -125,6 +216,8 @@ export function ViewTabs(props) {
|
|
|
125
216
|
onCleanup(() => {
|
|
126
217
|
disposed = true;
|
|
127
218
|
watch?.resize.disconnect();
|
|
219
|
+
if (watch?.frame) cancelAnimationFrame(watch.frame);
|
|
220
|
+
if (watch) document.fonts?.removeEventListener("loadingdone", watch.fonts);
|
|
128
221
|
if (list && watch) {
|
|
129
222
|
list.removeEventListener("focusin", watch.focus);
|
|
130
223
|
Reflect.deleteProperty(list, "__k2bViewTabsWatch");
|
|
@@ -132,14 +225,17 @@ export function ViewTabs(props) {
|
|
|
132
225
|
});
|
|
133
226
|
});
|
|
134
227
|
// User effects run after Solid's render effects, including For and aria-current, in the same task.
|
|
135
|
-
|
|
228
|
+
// What the strip shows: another open view, view, label, count or badge can change what fits. Equal content in a new
|
|
229
|
+
// array settles nothing again.
|
|
230
|
+
const shown = createMemo(() => [props.current ?? "", layout().wide.join(","), ...props.items.map(item => `${item.id}\u0000${item.label}\u0000${item.count ?? ""}\u0000${item.badge ?? ""}`)].join("\u0001"));
|
|
231
|
+
createEffect(on(shown, () => watchViewTabs(list ?? null, settleViewTabs, scrollFadeEdges), {
|
|
136
232
|
defer: true
|
|
137
233
|
}));
|
|
138
234
|
const [open, setOpen] = createSignal(false);
|
|
139
235
|
const [menuItems, setMenuItems] = createSignal([]);
|
|
140
236
|
const onOpenChange = next => {
|
|
141
237
|
if (next) {
|
|
142
|
-
setMenuItems(props.items.filter((item, index) => item.id !== props.current && (!layout().wide.includes(index) || stripItems.get(index)?.getClientRects().length === 0)).map(item => ({
|
|
238
|
+
setMenuItems(props.items.filter((item, index) => !item.pinned && item.id !== props.current && (!layout().wide.includes(index) || stripItems.get(index)?.getClientRects().length === 0)).map(item => ({
|
|
143
239
|
href: item.href,
|
|
144
240
|
label: item.label,
|
|
145
241
|
icon: item.icon,
|
|
@@ -160,22 +256,28 @@ export function ViewTabs(props) {
|
|
|
160
256
|
get children() {
|
|
161
257
|
return _$ssr(_tmpl$, _$escape(item.count));
|
|
162
258
|
}
|
|
259
|
+
}), _$createComponent(Show, {
|
|
260
|
+
get when() {
|
|
261
|
+
return item.badge;
|
|
262
|
+
},
|
|
263
|
+
children: badge => _$ssr(_tmpl$4, _$escape(badge()))
|
|
163
264
|
})];
|
|
164
|
-
|
|
265
|
+
const stripItem = index => {
|
|
266
|
+
const item = () => props.items[index];
|
|
267
|
+
onCleanup(() => stripItems.delete(index));
|
|
268
|
+
return _$ssr(_tmpl$5, _$ssrAttribute("data-view-tab", _$escape(item().id, true), false) + _$ssrAttribute("data-pinned", item().pinned ? "" : _$escape(undefined, true), false) + _$ssrAttribute("data-compact-hidden", !item().pinned && !layout().compact.includes(index) ? "" : _$escape(undefined, true), false), _$ssrAttribute("href", _$escape(item().href, true), false) + _$ssrAttribute("aria-current", item().id === props.current ? "page" : _$escape(undefined, true), false), _$escape(tabContent(item())));
|
|
269
|
+
};
|
|
270
|
+
return _$ssr(_tmpl$10, `k2b-view-tabs ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("aria-label", _$escape(props.label, true), false), _$ssrAttribute("data-fit-room", props.fit ? "" : _$escape(undefined, true), false), _$escape(_$createComponent(For, {
|
|
165
271
|
get each() {
|
|
166
272
|
return layout().wide;
|
|
167
273
|
},
|
|
168
|
-
children:
|
|
169
|
-
const item = () => props.items[index];
|
|
170
|
-
onCleanup(() => stripItems.delete(index));
|
|
171
|
-
return _$ssr(_tmpl$0, _$ssrAttribute("data-view-tab", _$escape(item().id, true), false) + _$ssrAttribute("data-compact-hidden", !layout().compact.includes(index) ? "" : _$escape(undefined, true), false), _$ssrAttribute("href", _$escape(item().href, true), false) + _$ssrAttribute("aria-current", item().id === props.current ? "page" : _$escape(undefined, true), false), _$escape(tabContent(item())));
|
|
172
|
-
}
|
|
274
|
+
children: stripItem
|
|
173
275
|
})), _$escape(_$createComponent(Show, {
|
|
174
276
|
get when() {
|
|
175
|
-
return layout().compactHidden + layout().wideHidden > 0;
|
|
277
|
+
return layout().compactHidden + layout().wideHidden > 0 || props.fit && layout().wide.length > 1;
|
|
176
278
|
},
|
|
177
279
|
get children() {
|
|
178
|
-
return _$ssr(_tmpl$
|
|
280
|
+
return _$ssr(_tmpl$1, _$ssrAttribute("data-wide", layout().wideHidden > 0 ? "" : _$escape(undefined, true), false) + _$ssrAttribute("data-behind-wide", _$escape(layout().wideHidden, true), false) + _$ssrAttribute("data-behind-compact", _$escape(layout().compactHidden, true), false), _$escape(_$createComponent(Dropdown.Root, {
|
|
179
281
|
get items() {
|
|
180
282
|
return menuItems();
|
|
181
283
|
},
|
|
@@ -194,26 +296,31 @@ export function ViewTabs(props) {
|
|
|
194
296
|
return messages().moreViews;
|
|
195
297
|
},
|
|
196
298
|
get children() {
|
|
197
|
-
return [_$ssr(_tmpl$
|
|
299
|
+
return [_$ssr(_tmpl$6, _$escape(messages().more)), _$createComponent(Show, {
|
|
198
300
|
get when() {
|
|
199
301
|
return layout().wideHidden > 0;
|
|
200
302
|
},
|
|
201
303
|
get children() {
|
|
202
|
-
return _$ssr(_tmpl$
|
|
304
|
+
return _$ssr(_tmpl$7, _$escape(layout().wideHidden));
|
|
203
305
|
}
|
|
204
306
|
}), _$createComponent(Show, {
|
|
205
307
|
get when() {
|
|
206
308
|
return layout().compactHidden > 0;
|
|
207
309
|
},
|
|
208
310
|
get children() {
|
|
209
|
-
return _$ssr(_tmpl$
|
|
311
|
+
return _$ssr(_tmpl$8, _$escape(layout().compactHidden));
|
|
210
312
|
}
|
|
211
|
-
}), _$ssr(_tmpl$
|
|
313
|
+
}), _$ssr(_tmpl$9), _$ssr(_tmpl$0)];
|
|
212
314
|
}
|
|
213
315
|
});
|
|
214
316
|
}
|
|
215
317
|
})));
|
|
216
318
|
}
|
|
319
|
+
})), _$escape(_$createComponent(For, {
|
|
320
|
+
get each() {
|
|
321
|
+
return layout().pinned;
|
|
322
|
+
},
|
|
323
|
+
children: stripItem
|
|
217
324
|
})), VIEW_TABS_SCRIPT);
|
|
218
325
|
}
|
|
219
326
|
//# sourceMappingURL=ViewTabs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["actions/ViewTabs.tsx"],"sourcesContent":["import { createEffect, createMemo, createSignal, For, type JSX, on, onCleanup, onMount, Show } from \"solid-js\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { createScrollFade, scrollFadeEdges } from \"../layout/scroll-fade\";\nimport Dropdown, { type DropdownItem } from \"./Dropdown\";\n\nexport type ViewTab = { id: string; label: string; href: string; icon?: string; count?: number | string };\nexport type ViewTabsProps = {\n /** Accessible name of the navigation landmark, e.g. \"Views\". */\n label: string;\n items: readonly ViewTab[];\n /** Id of the open view; the server reads it from `?view=`. Unknown ids select nothing. */\n current: string | undefined;\n /** Tabs shown before More (default 5). */\n max?: number;\n class?: string;\n};\n\n/** Two deterministic strip levels; only opening the menu reads CSS visibility. */\nexport function viewTabsLayout(items: readonly ViewTab[], current: string | undefined, max = 5) {\n // At least one slot keeps the current view visible, including for an invalid limit.\n const limit = Number.isFinite(max) ? Math.max(1, Math.floor(max)) : 5;\n const currentIndex = items.findIndex((item) => item.id === current);\n const visible = (size: number) => {\n const indices = items.slice(0, size).map((_, index) => index);\n if (currentIndex >= size) indices[indices.length - 1] = currentIndex;\n return indices;\n };\n const wide = visible(limit);\n const compact = visible(Math.min(limit, 3));\n return { wide, compact, wideHidden: items.length - wide.length, compactHidden: items.length - compact.length };\n}\n\n/** Self-contained: the server script embeds this before hydration. */\nfunction settleViewTabs(list: Element | null, edges: (element: HTMLElement) => string | undefined, focused?: HTMLElement) {\n if (!(list instanceof HTMLElement)) return;\n const current = focused ?? list.querySelector(\".k2b-view-tabs__tab[aria-current=page]\");\n if (current && current.getClientRects().length > 0 && list.scrollWidth - list.clientWidth > 1) {\n const bounds = list.getBoundingClientRect();\n const item = current.getBoundingClientRect();\n const fade = edges(list);\n const style = getComputedStyle(list);\n const left = fade === \"top\" || fade === \"both\" ? parseFloat(style.scrollPaddingLeft) || 0 : 0;\n const right = fade === \"bottom\" || fade === \"both\" ? parseFloat(style.scrollPaddingRight) || 0 : 0;\n if (item.left < bounds.left + left || item.right > bounds.right - right) {\n let offset = item.left - bounds.left - (bounds.width - item.width) / 2;\n if (focused) {\n // Reserve padding at the destination edge; native clamping drops it when that end is reached.\n offset =\n item.left < bounds.left + left\n ? item.left - bounds.left - (parseFloat(style.scrollPaddingLeft) || 0)\n : item.right - bounds.right + (parseFloat(style.scrollPaddingRight) || 0);\n }\n // Only the strip moves: scrollIntoView would also scroll the page. Instant keeps the first frame free of animation.\n list.scrollTo({ left: list.scrollLeft + offset, behavior: \"instant\" });\n }\n }\n const fade = edges(list);\n if (fade) list.setAttribute(\"data-scroll-fade\", fade);\n else list.removeAttribute(\"data-scroll-fade\");\n}\n\ntype WatchedViewTabs = HTMLElement & {\n __k2bViewTabsWatch?: { resize: ResizeObserver; focus: (event: FocusEvent) => void; preferCurrent: boolean };\n};\n\n/** Also embedded, so it must not close over module state. */\nfunction watchViewTabs(list: WatchedViewTabs | null, settle: typeof settleViewTabs, edges: (element: HTMLElement) => string | undefined) {\n if (!(list instanceof HTMLElement)) return;\n // A DOM property survives hydration: one observer and focus listener per reused list.\n let watch = list.__k2bViewTabsWatch;\n if (!watch) {\n const focus = (event: FocusEvent) => {\n // Pointer focus must not move the target under the pointer.\n if (watch && event.target instanceof HTMLElement && event.target.matches(\".k2b-view-tabs__tab:focus-visible\")) {\n watch.preferCurrent = false;\n settle(list, edges, event.target);\n }\n };\n watch = {\n resize: new ResizeObserver(() => {\n const focused = document.activeElement;\n if (\n watch?.preferCurrent === false &&\n focused instanceof HTMLElement &&\n list.contains(focused) &&\n focused.matches(\".k2b-view-tabs__tab:focus-visible\")\n )\n settle(list, edges, focused);\n else settle(list, edges);\n }),\n focus,\n preferCurrent: true,\n };\n Object.defineProperty(list, \"__k2bViewTabsWatch\", { value: watch, configurable: true });\n list.addEventListener(\"focusin\", focus);\n }\n watch.preferCurrent = true;\n const { resize } = watch;\n resize.disconnect();\n // Head actions parse after the views; settle again after their layout, before paint, and on later resizes.\n resize.observe(list);\n for (const child of Array.from(list.children)) resize.observe(child);\n settle(list, edges);\n return watch;\n}\nconst VIEW_TABS_SCRIPT = `(${watchViewTabs})(document.currentScript&&document.currentScript.previousElementSibling,${settleViewTabs},${scrollFadeEdges})`;\n\n/**\n * URL-backed object views. Every view is an ordinary document link, in the strip and under More, so reload, back,\n * and forward keep the view the URL names.\n */\nexport function ViewTabs(props: ViewTabsProps): JSX.Element {\n const messages = useUiMessages();\n const layout = createMemo(() => viewTabsLayout(props.items, props.current, props.max));\n const stripItems = new Map<number, HTMLLIElement>();\n let list: HTMLUListElement | undefined;\n createScrollFade(\n () => list,\n () => true,\n );\n onMount(() => {\n const watch = watchViewTabs(list ?? null, settleViewTabs, scrollFadeEdges);\n let disposed = false;\n // Later mount effects can invalidate WebKit's scroll layout after this\n // initial settle. Finish once the mount task is complete, before its first\n // frame; ResizeObserver alone runs too late for that frame.\n queueMicrotask(() => {\n if (!disposed && watch?.preferCurrent) settleViewTabs(list ?? null, scrollFadeEdges);\n });\n onCleanup(() => {\n disposed = true;\n watch?.resize.disconnect();\n if (list && watch) {\n list.removeEventListener(\"focusin\", watch.focus);\n Reflect.deleteProperty(list, \"__k2bViewTabsWatch\");\n }\n });\n });\n // User effects run after Solid's render effects, including For and aria-current, in the same task.\n createEffect(\n on(\n () => props.current,\n () => watchViewTabs(list ?? null, settleViewTabs, scrollFadeEdges),\n { defer: true },\n ),\n );\n const [open, setOpen] = createSignal(false);\n const [menuItems, setMenuItems] = createSignal<DropdownItem[]>([]);\n const onOpenChange = (next: boolean) => {\n if (next) {\n setMenuItems(\n props.items\n .filter(\n (item, index) =>\n item.id !== props.current && (!layout().wide.includes(index) || stripItems.get(index)?.getClientRects().length === 0),\n )\n .map((item) => ({\n href: item.href,\n label: item.label,\n icon: item.icon,\n trailing: item.count !== undefined ? <span class=\"k2b-view-tabs__count\">{item.count}</span> : undefined,\n })),\n );\n }\n setOpen(next);\n };\n const tabContent = (item: ViewTab) => (\n <>\n <Show when={item.icon}>{(icon) => <i class={icon()} aria-hidden=\"true\" />}</Show>\n <span class=\"k2b-view-tabs__label k2b-truncate-fade\">{item.label}</span>\n <Show when={item.count !== undefined}>\n <span class=\"k2b-view-tabs__count\">{item.count}</span>\n </Show>\n </>\n );\n return (\n <nav class={`k2b-view-tabs ${props.class ?? \"\"}`} aria-label={props.label}>\n <ul ref={list} class=\"k2b-view-tabs__list\" data-scroll-fade-mode=\"both\" data-scroll-fade-axis=\"horizontal\">\n <For each={layout().wide}>\n {(index) => {\n const item = () => props.items[index]!;\n onCleanup(() => stripItems.delete(index));\n return (\n <li\n ref={(element) => stripItems.set(index, element)}\n class=\"k2b-view-tabs__item\"\n data-view-tab={item().id}\n data-compact-hidden={!layout().compact.includes(index) ? \"\" : undefined}\n >\n <a class=\"k2b-view-tabs__tab\" href={item().href} aria-current={item().id === props.current ? \"page\" : undefined}>\n {tabContent(item())}\n </a>\n </li>\n );\n }}\n </For>\n <Show when={layout().compactHidden + layout().wideHidden > 0}>\n <li class=\"k2b-view-tabs__more\" data-wide={layout().wideHidden > 0 ? \"\" : undefined}>\n <Dropdown.Root items={menuItems()} open={open()} onOpenChange={onOpenChange} label={messages().moreViews}>\n <Dropdown.Trigger appearance=\"plain\" class=\"k2b-view-tabs__tab k2b-view-tabs__more-trigger\" label={messages().moreViews}>\n <span class=\"k2b-view-tabs__label\">{messages().more}</span>\n <Show when={layout().wideHidden > 0}>\n <span class=\"k2b-view-tabs__more-count\" data-level=\"wide\">\n +{layout().wideHidden}\n </span>\n </Show>\n <Show when={layout().compactHidden > 0}>\n <span class=\"k2b-view-tabs__more-count\" data-level=\"compact\">\n +{layout().compactHidden}\n </span>\n </Show>\n <i class=\"ti ti-chevron-down\" aria-hidden=\"true\" />\n </Dropdown.Trigger>\n </Dropdown.Root>\n </li>\n </Show>\n </ul>\n <script innerHTML={VIEW_TABS_SCRIPT} />\n </nav>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,EAAY,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,QAAQ,UAAU;AAC9G,SAAS,aAAa,QAAQ,kBAAkB;AAChD,SAAS,gBAAgB,EAAE,eAAe,QAAQ,uBAAuB;AACzE,OAAO,QAAQ,MAA6B,YAAY;AAcxD;AACA,OAAO,SAAS,cAAc,CAAC,KAAyB,EAAE,OAA2B,EAAE,GAAG,GAAG,CAAC,EAAE;EAC9F;EACA,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;EACrE,MAAM,YAAY,GAAG,KAAK,CAAC,SAAS,CAAE,IAAI,IAAK,IAAI,CAAC,EAAE,KAAK,OAAO,CAAC;EACnE,MAAM,OAAO,GAAI,IAAY,IAAK;IAChC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,KAAK,CAAC;IAC7D,IAAI,YAAY,IAAI,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,YAAY;IACpE,OAAO,OAAO;EAChB,CAAC;EACD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC;EAC3B,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;EAC3C,OAAO;IAAE,IAAI;IAAE,OAAO;IAAE,UAAU,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM;IAAE,aAAa,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC;EAAO,CAAC;AAChH;;AAEA;AACA,SAAS,cAAc,CAAC,IAAoB,EAAE,KAAmD,EAAE,OAAqB,EAAE;EACxH,IAAI,EAAE,IAAI,YAAY,WAAW,CAAC,EAAE;EACpC,MAAM,OAAO,GAAG,OAAO,IAAI,IAAI,CAAC,aAAa,CAAC,wCAAwC,CAAC;EACvF,IAAI,OAAO,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,GAAG,CAAC,EAAE;IAC7F,MAAM,MAAM,GAAG,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACxB,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC;IAC7F,MAAM,KAAK,GAAG,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC;IAClG,IAAI,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,GAAG,KAAK,EAAE;MACvE,IAAI,MAAM,GAAG,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC;MACtE,IAAI,OAAO,EAAE;QACX;QACA,MAAM,GACJ,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,GAAG,IAAI,GAC1B,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,UAAU,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,GACpE,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;MAC/E;MACA;MACA,IAAI,CAAC,QAAQ,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC,UAAU,GAAG,MAAM;QAAE,QAAQ,EAAE;MAAU,CAAC,CAAC;IACxE;EACF;EACA,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;EACxB,IAAI,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC,KACjD,IAAI,CAAC,eAAe,CAAC,kBAAkB,CAAC;AAC/C;AAMA;AACA,SAAS,aAAa,CAAC,IAA4B,EAAE,MAA6B,EAAE,KAAmD,EAAE;EACvI,IAAI,EAAE,IAAI,YAAY,WAAW,CAAC,EAAE;EACpC;EACA,IAAI,KAAK,GAAG,IAAI,CAAC,kBAAkB;EACnC,IAAI,CAAC,KAAK,EAAE;IACV,MAAM,KAAK,GAAI,KAAiB,IAAK;MACnC;MACA,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,YAAY,WAAW,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,mCAAmC,CAAC,EAAE;QAC7G,KAAK,CAAC,aAAa,GAAG,KAAK;QAC3B,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC;MACnC;IACF,CAAC;IACD,KAAK,GAAG;MACN,MAAM,EAAE,IAAI,cAAc,CAAC,MAAM;QAC/B,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa;QACtC,IACE,KAAK,EAAE,aAAa,KAAK,KAAK,IAC9B,OAAO,YAAY,WAAW,IAC9B,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IACtB,OAAO,CAAC,OAAO,CAAC,mCAAmC,CAAC,EAEpD,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,KAC1B,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC;MAC1B,CAAC,CAAC;MACF,KAAK;MACL,aAAa,EAAE;IACjB,CAAC;IACD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,oBAAoB,EAAE;MAAE,KAAK,EAAE,KAAK;MAAE,YAAY,EAAE;IAAK,CAAC,CAAC;IACvF,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,KAAK,CAAC;EACzC;EACA,KAAK,CAAC,aAAa,GAAG,IAAI;EAC1B,MAAM;IAAE;EAAO,CAAC,GAAG,KAAK;EACxB,MAAM,CAAC,UAAU,CAAC,CAAC;EACnB;EACA,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;EACpB,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;EACpE,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC;EACnB,OAAO,KAAK;AACd;AACA,MAAM,gBAAgB,GAAG,IAAI,aAAa,2EAA2E,cAAc,IAAI,eAAe,GAAG;;AAEzJ;AACA;AACA;AACA;AACA,OAAO,SAAS,QAAQ,CAAC,KAAoB,EAAe;EAC1D,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC;EAChC,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;EACtF,MAAM,UAAU,GAAG,IAAI,GAAG,CAAwB,CAAC;EACnD,IAAI,IAAkC;EACtC,gBAAgB,CACd,MAAM,IAAI,EACV,MAAM,IACR,CAAC;EACD,OAAO,CAAC,MAAM;IACZ,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,IAAI,IAAI,EAAE,cAAc,EAAE,eAAe,CAAC;IAC1E,IAAI,QAAQ,GAAG,KAAK;IACpB;IACA;IACA;IACA,cAAc,CAAC,MAAM;MACnB,IAAI,CAAC,QAAQ,IAAI,KAAK,EAAE,aAAa,EAAE,cAAc,CAAC,IAAI,IAAI,IAAI,EAAE,eAAe,CAAC;IACtF,CAAC,CAAC;IACF,SAAS,CAAC,MAAM;MACd,QAAQ,GAAG,IAAI;MACf,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;MAC1B,IAAI,IAAI,IAAI,KAAK,EAAE;QACjB,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC;QAChD,OAAO,CAAC,cAAc,CAAC,IAAI,EAAE,oBAAoB,CAAC;MACpD;IACF,CAAC,CAAC;EACJ,CAAC,CAAC;EACF;EACA,YAAY,CACV,EAAE,CACA,MAAM,KAAK,CAAC,OAAO,EACnB,MAAM,aAAa,CAAC,IAAI,IAAI,IAAI,EAAE,cAAc,EAAE,eAAe,CAAC,EAClE;IAAE,KAAK,EAAE;EAAK,CAChB,CACF,CAAC;EACD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC;EAC3C,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,YAAY,CAAiB,EAAE,CAAC;EAClE,MAAM,YAAY,GAAI,IAAa,IAAK;IACtC,IAAI,IAAI,EAAE;MACR,YAAY,CACV,KAAK,CAAC,KAAK,CACR,MAAM,CACL,CAAC,IAAI,EAAE,KAAK,KACV,IAAI,CAAC,EAAE,KAAK,KAAK,CAAC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CACxH,CAAC,CACA,GAAG,CAAE,IAAI,KAAM;QACd,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,KAAK,KAAK,SAAS,0BAAuC,IAAI,CAAC,KAAK,KAAW;MAChG,CAAC,CAAC,CACN,CAAC;IACH;IACA,OAAO,CAAC,IAAI,CAAC;EACf,CAAC;EACD,MAAM,UAAU,GAAI,IAAa,uBAE5B,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,IAAI,CAAC,IAAI;IAAA;IAAA,UAAI,IAAI,oDAAe,IAAI,CAAC,CAAC;EAAuB,4BACnB,IAAI,CAAC,KAAK,sBAC/D,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,IAAI,CAAC,KAAK,KAAK,SAAS;IAAA;IAAA;MAAA,8BACE,IAAI,CAAC,KAAK;IAAA;EAAA,GAGnD;EACD,sBACc,0BAAiB,KAAK,CAAC,KAAK,IAAI,EAAE,SAAE,wCAAc,KAAK,CAAC,KAAK,4CAEpE,GAAG;IAAA,IAAC,IAAI;MAAA,OAAE,MAAM,CAAC,CAAC,CAAC,IAAI;IAAA;IAAA,UACpB,KAAK,IAAK;MACV,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,KAAK,CAAE;MACtC,SAAS,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;MACzC,+DAImB,IAAI,CAAC,CAAC,CAAC,EAAE,wDACH,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,YAAG,SAAS,iDAEnC,IAAI,CAAC,CAAC,CAAC,IAAI,iDAAgB,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,OAAO,GAAG,MAAM,YAAG,SAAS,0BAC5G,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAI3B;EAAC,gCAEF,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,MAAM,CAAC,CAAC,CAAC,aAAa,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC;IAAA;IAAA;MAAA,kDACf,MAAM,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,GAAG,EAAE,YAAG,SAAS,4CAChF,QAAQ,CAAC,IAAI;QAAA,IAAC,KAAK;UAAA,OAAE,SAAS,CAAC,CAAC;QAAA;QAAA,IAAE,IAAI;UAAA,OAAE,IAAI,CAAC,CAAC;QAAA;QAAE,YAAY,EAAE,YAAY;QAAA,IAAE,KAAK;UAAA,OAAE,QAAQ,CAAC,CAAC,CAAC,SAAS;QAAA;QAAA;UAAA,yBACrG,QAAQ,CAAC,OAAO;YAAC,UAAU;YAAA;YAAA,IAAgE,KAAK;cAAA,OAAE,QAAQ,CAAC,CAAC,CAAC,SAAS;YAAA;YAAA;cAAA,gCACjF,QAAQ,CAAC,CAAC,CAAC,IAAI,sBAClD,IAAI;gBAAA,IAAC,IAAI;kBAAA,OAAE,MAAM,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC;gBAAA;gBAAA;kBAAA,+BAE7B,MAAM,CAAC,CAAC,CAAC,UAAU;gBAAA;cAAA,sBAGxB,IAAI;gBAAA,IAAC,IAAI;kBAAA,OAAE,MAAM,CAAC,CAAC,CAAC,aAAa,GAAG,CAAC;gBAAA;gBAAA;kBAAA,+BAEhC,MAAM,CAAC,CAAC,CAAC,aAAa;gBAAA;cAAA;YAAA;UAAA;QAAA;MAAA;IAAA;EAAA,KASnB,gBAAgB;AAGzC","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["actions/ViewTabs.tsx"],"sourcesContent":["import { createEffect, createMemo, createSignal, For, type JSX, on, onCleanup, onMount, Show } from \"solid-js\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { createScrollFade, scrollFadeEdges } from \"../layout/scroll-fade\";\nimport Dropdown, { type DropdownItem } from \"./Dropdown\";\n\nexport type ViewTab = {\n id: string;\n label: string;\n href: string;\n icon?: string;\n count?: number | string;\n /** A short quiet label after the count, such as \"New\" for a view people have not opened yet. */\n badge?: string;\n /** Stays in the strip after More at every width, for a view of another kind than the ones that overflow. */\n pinned?: boolean;\n};\nexport type ViewTabsProps = {\n /** Accessible name of the navigation landmark, e.g. \"Views\". */\n label: string;\n items: readonly ViewTab[];\n /** Id of the open view; the server reads it from `?view=`. Unknown ids select nothing. */\n current: string | undefined;\n /** At most this many views before More (default 5). */\n max?: number;\n /**\n * The strip shows only the views that fit its room and counts the rest in More, instead of scrolling them. The open\n * view and pinned views stay. Pair it with a larger `max` so a wide head shows more views.\n */\n fit?: boolean;\n class?: string;\n};\n\n/** Two deterministic strip levels; only opening the menu reads CSS visibility. */\nexport function viewTabsLayout(items: readonly ViewTab[], current: string | undefined, max = 5) {\n // At least one slot keeps the current view visible, including for an invalid limit.\n const limit = Number.isFinite(max) ? Math.max(1, Math.floor(max)) : 5;\n // Pinned views never overflow, so they take no slot.\n const flowing = items.flatMap((item, index) => (item.pinned ? [] : [index]));\n const pinned = items.flatMap((item, index) => (item.pinned ? [index] : []));\n const currentIndex = items.findIndex((item) => item.id === current);\n const currentPosition = flowing.indexOf(currentIndex);\n const visible = (size: number) => {\n const indices = flowing.slice(0, size);\n if (currentPosition >= size) indices[indices.length - 1] = currentIndex;\n return indices;\n };\n const wide = visible(limit);\n const compact = visible(Math.min(limit, 3));\n return { wide, compact, pinned, wideHidden: flowing.length - wide.length, compactHidden: flowing.length - compact.length };\n}\n\n/**\n * Self-contained: the server script embeds this before hydration. A fitting strip first keeps only the views that fit\n * its room and counts the rest in More; then the open or focused view scrolls into the strip.\n */\nfunction settleViewTabs(list: Element | null, edges: (element: HTMLElement) => string | undefined, focused?: HTMLElement) {\n if (!(list instanceof HTMLElement)) return;\n const more = list.querySelector<HTMLElement>(\":scope > .k2b-view-tabs__more\");\n const fitCount = more?.querySelector<HTMLElement>('.k2b-view-tabs__more-count[data-level=\"fit\"]');\n const flowing = Array.from(list.querySelectorAll<HTMLElement>(\":scope > .k2b-view-tabs__item:not([data-pinned])\"));\n\n for (const item of flowing) item.removeAttribute(\"data-fit-hidden\");\n more?.removeAttribute(\"data-fit\");\n // The shown entries against the strip's inner room, whatever its scroll and padding.\n const overflows = () => {\n const boxes = Array.from(list.children)\n .filter((child) => child instanceof HTMLElement && child.tagName === \"LI\" && child.getClientRects().length > 0)\n .map((child) => child.getBoundingClientRect());\n if (boxes.length === 0) return false;\n const style = getComputedStyle(list);\n const room = list.clientWidth - (parseFloat(style.paddingLeft) || 0) - (parseFloat(style.paddingRight) || 0);\n return Math.max(...boxes.map((box) => box.right)) - Math.min(...boxes.map((box) => box.left)) > room + 0.5;\n };\n // The phone head row keeps its scrolling strip: its CSS turns fitting off.\n const fits = list.hasAttribute(\"data-fit-room\") && getComputedStyle(list).getPropertyValue(\"--k2b-view-tabs-fit\").trim() !== \"none\";\n if (fits && more && fitCount && overflows()) {\n // Count from the level on screen: the compact strip already keeps fewer views than the wide one.\n const compact = more.querySelector<HTMLElement>('.k2b-view-tabs__more-count[data-level=\"compact\"]');\n const compactShown = Boolean(compact && getComputedStyle(compact).display !== \"none\");\n const behind = Number(compactShown ? more.dataset.behindCompact : more.dataset.behindWide) || 0;\n // The last views on screen leave first; the open view stays.\n const candidates = flowing.filter((item) => item.getClientRects().length > 0 && !item.querySelector(\"[aria-current=page]\")).reverse();\n more.setAttribute(\"data-fit\", \"\");\n let hidden = 0;\n for (const item of candidates) {\n item.setAttribute(\"data-fit-hidden\", \"\");\n hidden += 1;\n fitCount.textContent = `+${behind + hidden}`;\n if (!overflows()) break;\n }\n if (hidden === 0) more.removeAttribute(\"data-fit\");\n }\n const current = focused ?? list.querySelector(\".k2b-view-tabs__tab[aria-current=page]\");\n if (current && current.getClientRects().length > 0 && list.scrollWidth - list.clientWidth > 1) {\n const bounds = list.getBoundingClientRect();\n const item = current.getBoundingClientRect();\n const fade = edges(list);\n const style = getComputedStyle(list);\n const left = fade === \"top\" || fade === \"both\" ? parseFloat(style.scrollPaddingLeft) || 0 : 0;\n const right = fade === \"bottom\" || fade === \"both\" ? parseFloat(style.scrollPaddingRight) || 0 : 0;\n if (item.left < bounds.left + left || item.right > bounds.right - right) {\n let offset = item.left - bounds.left - (bounds.width - item.width) / 2;\n if (focused) {\n // Reserve padding at the destination edge; native clamping drops it when that end is reached.\n offset =\n item.left < bounds.left + left\n ? item.left - bounds.left - (parseFloat(style.scrollPaddingLeft) || 0)\n : item.right - bounds.right + (parseFloat(style.scrollPaddingRight) || 0);\n }\n // Only the strip moves: scrollIntoView would also scroll the page. Instant keeps the first frame free of animation.\n list.scrollTo({ left: list.scrollLeft + offset, behavior: \"instant\" });\n }\n }\n const fade = edges(list);\n if (fade) list.setAttribute(\"data-scroll-fade\", fade);\n else list.removeAttribute(\"data-scroll-fade\");\n}\n\ntype WatchedViewTabs = HTMLElement & {\n __k2bViewTabsWatch?: {\n resize: ResizeObserver;\n focus: (event: FocusEvent) => void;\n preferCurrent: boolean;\n connect: () => void;\n /** Runs one fit in the next frame; calls before it runs share it. */\n schedule: () => void;\n frame: number;\n /** The head lead's width and the widths of the shown entries when the last fit ended. */\n room: string;\n /** Fits again once web fonts finish loading: their metrics change every view's width, not the lead's. */\n fonts: () => void;\n };\n};\n\n/**\n * Also embedded, so it must not close over module state.\n *\n * A fitting strip converges by construction. It fits again only when the strip renders, web fonts finish loading, or\n * a notification finds another width of the head lead or of an entry that the last fit left shown. A fit only shows\n * and hides entries: the lead is an inline-size container that takes no width from the strip, and every entry keeps\n * its own width (`flex: none`), so the notifications a fit causes find the widths it ended with and stop there.\n */\nfunction watchViewTabs(list: WatchedViewTabs | null, settle: typeof settleViewTabs, edges: (element: HTMLElement) => string | undefined) {\n if (!(list instanceof HTMLElement)) return;\n const fits = list.hasAttribute(\"data-fit-room\");\n const roomOf = () => list.closest<HTMLElement>(\".k2b-app-workspace__head-lead\");\n // What a fit depends on: the lead's width and each shown entry's width, which fonts, icons and labels change.\n const measured = () =>\n [roomOf()?.clientWidth ?? -1, ...Array.from(list.children, (child) => child.getBoundingClientRect().width)].join(\" \");\n // A DOM property survives hydration: one observer and focus listener per reused list.\n let watch = list.__k2bViewTabsWatch;\n if (!watch) {\n const focus = (event: FocusEvent) => {\n // Pointer focus must not move the target under the pointer.\n if (watch && event.target instanceof HTMLElement && event.target.matches(\".k2b-view-tabs__tab:focus-visible\")) {\n watch.preferCurrent = false;\n settle(list, edges, event.target);\n }\n };\n const settleNow = () => {\n const focused = document.activeElement;\n if (\n watch?.preferCurrent === false &&\n focused instanceof HTMLElement &&\n list.contains(focused) &&\n focused.matches(\".k2b-view-tabs__tab:focus-visible\")\n )\n settle(list, edges, focused);\n else settle(list, edges);\n };\n // Head actions parse after the views; settle again after their layout, before paint, and on later resizes.\n const connect = () => {\n if (!watch) return;\n watch.resize.disconnect();\n watch.resize.observe(list);\n for (const child of Array.from(list.children)) watch.resize.observe(child);\n const room = roomOf();\n if (fits && room) watch.resize.observe(room);\n };\n // Fitting resizes the strip and possibly the head, which other observers may watch. Inside a ResizeObserver\n // callback that would leave their notifications undelivered, so a fit runs in the next frame, still before it paints.\n const schedule = () => {\n if (!watch || watch.frame) return;\n watch.frame = requestAnimationFrame(() => {\n if (!watch) return;\n watch.frame = 0;\n if (!list.isConnected) return;\n settleNow();\n watch.room = measured();\n });\n };\n watch = {\n resize: new ResizeObserver(() => {\n // A scrolling strip only scrolls and settles at once.\n if (!fits) return settleNow();\n // Heights follow the fit, and hidden entries measure 0 by its own doing: only another measure counts.\n if (watch && measured() !== watch.room) watch.schedule();\n }),\n focus,\n preferCurrent: true,\n connect,\n schedule,\n frame: 0,\n room: \"\",\n fonts: () => watch?.schedule(),\n };\n Object.defineProperty(list, \"__k2bViewTabsWatch\", { value: watch, configurable: true });\n list.addEventListener(\"focusin\", focus);\n // A first visit can paint before the web fonts load; the server frame and the client then fit the same metrics.\n if (fits && document.fonts) {\n document.fonts.addEventListener(\"loadingdone\", watch.fonts);\n void document.fonts.ready.then(watch.fonts);\n }\n }\n watch.preferCurrent = true;\n watch.connect();\n settle(list, edges);\n if (fits) {\n watch.room = measured();\n // The head's actions parse after the views. A frame's animation callbacks run before its first layout and\n // paint, so this fit also counts their room in the first frame.\n watch.schedule();\n }\n return watch;\n}\nconst VIEW_TABS_SCRIPT = `(${watchViewTabs})(document.currentScript&&document.currentScript.previousElementSibling,${settleViewTabs},${scrollFadeEdges})`;\n\n/**\n * URL-backed object views. Every view is an ordinary document link, in the strip and under More, so reload, back,\n * and forward keep the view the URL names.\n */\nexport function ViewTabs(props: ViewTabsProps): JSX.Element {\n const messages = useUiMessages();\n const layout = createMemo(() => viewTabsLayout(props.items, props.current, props.max));\n const stripItems = new Map<number, HTMLLIElement>();\n let list: HTMLUListElement | undefined;\n createScrollFade(\n () => list,\n () => true,\n );\n onMount(() => {\n const watch = watchViewTabs(list ?? null, settleViewTabs, scrollFadeEdges);\n let disposed = false;\n // Later mount effects can invalidate WebKit's scroll layout after this\n // initial settle. Finish once the mount task is complete, before its first\n // frame; ResizeObserver alone runs too late for that frame.\n queueMicrotask(() => {\n if (!disposed && watch?.preferCurrent) settleViewTabs(list ?? null, scrollFadeEdges);\n });\n onCleanup(() => {\n disposed = true;\n watch?.resize.disconnect();\n if (watch?.frame) cancelAnimationFrame(watch.frame);\n if (watch) document.fonts?.removeEventListener(\"loadingdone\", watch.fonts);\n if (list && watch) {\n list.removeEventListener(\"focusin\", watch.focus);\n Reflect.deleteProperty(list, \"__k2bViewTabsWatch\");\n }\n });\n });\n // User effects run after Solid's render effects, including For and aria-current, in the same task.\n // What the strip shows: another open view, view, label, count or badge can change what fits. Equal content in a new\n // array settles nothing again.\n const shown = createMemo(() =>\n [\n props.current ?? \"\",\n layout().wide.join(\",\"),\n ...props.items.map((item) => `${item.id}\\u0000${item.label}\\u0000${item.count ?? \"\"}\\u0000${item.badge ?? \"\"}`),\n ].join(\"\\u0001\"),\n );\n createEffect(on(shown, () => watchViewTabs(list ?? null, settleViewTabs, scrollFadeEdges), { defer: true }));\n const [open, setOpen] = createSignal(false);\n const [menuItems, setMenuItems] = createSignal<DropdownItem[]>([]);\n const onOpenChange = (next: boolean) => {\n if (next) {\n setMenuItems(\n props.items\n .filter(\n (item, index) =>\n !item.pinned &&\n item.id !== props.current &&\n (!layout().wide.includes(index) || stripItems.get(index)?.getClientRects().length === 0),\n )\n .map((item) => ({\n href: item.href,\n label: item.label,\n icon: item.icon,\n trailing: item.count !== undefined ? <span class=\"k2b-view-tabs__count\">{item.count}</span> : undefined,\n })),\n );\n }\n setOpen(next);\n };\n const tabContent = (item: ViewTab) => (\n <>\n <Show when={item.icon}>{(icon) => <i class={icon()} aria-hidden=\"true\" />}</Show>\n <span class=\"k2b-view-tabs__label k2b-truncate-fade\">{item.label}</span>\n <Show when={item.count !== undefined}>\n <span class=\"k2b-view-tabs__count\">{item.count}</span>\n </Show>\n <Show when={item.badge}>{(badge) => <span class=\"k2b-view-tabs__badge\">{badge()}</span>}</Show>\n </>\n );\n const stripItem = (index: number) => {\n const item = () => props.items[index]!;\n onCleanup(() => stripItems.delete(index));\n return (\n <li\n ref={(element) => stripItems.set(index, element)}\n class=\"k2b-view-tabs__item\"\n data-view-tab={item().id}\n data-pinned={item().pinned ? \"\" : undefined}\n data-compact-hidden={!item().pinned && !layout().compact.includes(index) ? \"\" : undefined}\n >\n <a class=\"k2b-view-tabs__tab\" href={item().href} aria-current={item().id === props.current ? \"page\" : undefined}>\n {tabContent(item())}\n </a>\n </li>\n );\n };\n return (\n <nav class={`k2b-view-tabs ${props.class ?? \"\"}`} aria-label={props.label}>\n <ul\n ref={list}\n class=\"k2b-view-tabs__list\"\n data-scroll-fade-mode=\"both\"\n data-scroll-fade-axis=\"horizontal\"\n data-fit-room={props.fit ? \"\" : undefined}\n >\n <For each={layout().wide}>{stripItem}</For>\n {/* A fitting strip keeps More ready, hidden until a view leaves the strip. */}\n <Show when={layout().compactHidden + layout().wideHidden > 0 || (props.fit && layout().wide.length > 1)}>\n <li\n class=\"k2b-view-tabs__more\"\n data-wide={layout().wideHidden > 0 ? \"\" : undefined}\n data-behind-wide={layout().wideHidden}\n data-behind-compact={layout().compactHidden}\n >\n <Dropdown.Root items={menuItems()} open={open()} onOpenChange={onOpenChange} label={messages().moreViews}>\n <Dropdown.Trigger appearance=\"plain\" class=\"k2b-view-tabs__tab k2b-view-tabs__more-trigger\" label={messages().moreViews}>\n <span class=\"k2b-view-tabs__label\">{messages().more}</span>\n <Show when={layout().wideHidden > 0}>\n <span class=\"k2b-view-tabs__more-count\" data-level=\"wide\">\n +{layout().wideHidden}\n </span>\n </Show>\n <Show when={layout().compactHidden > 0}>\n <span class=\"k2b-view-tabs__more-count\" data-level=\"compact\">\n +{layout().compactHidden}\n </span>\n </Show>\n {/* Written by the strip's fit, never by the render, so a rerender keeps the count it measured. */}\n <span class=\"k2b-view-tabs__more-count\" data-level=\"fit\" />\n <i class=\"ti ti-chevron-down\" aria-hidden=\"true\" />\n </Dropdown.Trigger>\n </Dropdown.Root>\n </li>\n </Show>\n <For each={layout().pinned}>{stripItem}</For>\n </ul>\n <script innerHTML={VIEW_TABS_SCRIPT} />\n </nav>\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -84,7 +84,8 @@ export function ChatDecision(props) {
|
|
|
84
84
|
};
|
|
85
85
|
|
|
86
86
|
// Read once: an object literal from the host builds its JSX again on every read.
|
|
87
|
-
const reason = createMemo(() => props.unavailable ?? props.reason);
|
|
87
|
+
const reason = createMemo(() => props.waiting ?? props.unavailable ?? props.reason);
|
|
88
|
+
const confirmOff = () => Boolean(props.waiting ?? props.unavailable);
|
|
88
89
|
const open = () => props.state === "open";
|
|
89
90
|
const answer = (choice, enabled) => {
|
|
90
91
|
if (!enabled || !open() || answeredHere()) return;
|
|
@@ -142,9 +143,9 @@ export function ChatDecision(props) {
|
|
|
142
143
|
return props.confirm.variant ?? "ai";
|
|
143
144
|
},
|
|
144
145
|
get disabled() {
|
|
145
|
-
return
|
|
146
|
+
return confirmOff();
|
|
146
147
|
},
|
|
147
|
-
onClick: () => answer(props.confirm, !
|
|
148
|
+
onClick: () => answer(props.confirm, !confirmOff()),
|
|
148
149
|
get children() {
|
|
149
150
|
return [_$createComponent(Show, {
|
|
150
151
|
get when() {
|
|
@@ -157,7 +158,10 @@ export function ChatDecision(props) {
|
|
|
157
158
|
"class": "k2b-chat-decision__answer",
|
|
158
159
|
wrap: true,
|
|
159
160
|
variant: "subtle",
|
|
160
|
-
|
|
161
|
+
get disabled() {
|
|
162
|
+
return Boolean(props.waiting);
|
|
163
|
+
},
|
|
164
|
+
onClick: () => answer(props.decline, !props.waiting),
|
|
161
165
|
get children() {
|
|
162
166
|
return [_$createComponent(Show, {
|
|
163
167
|
get when() {
|
|
@@ -175,9 +179,9 @@ export function ChatDecision(props) {
|
|
|
175
179
|
wrap: true,
|
|
176
180
|
variant: "text",
|
|
177
181
|
get disabled() {
|
|
178
|
-
return
|
|
182
|
+
return confirmOff();
|
|
179
183
|
},
|
|
180
|
-
onClick: () => answer(remember(), !
|
|
184
|
+
onClick: () => answer(remember(), !confirmOff()),
|
|
181
185
|
get children() {
|
|
182
186
|
return remember().label;
|
|
183
187
|
}
|