efficient-shell 0.0.0 → 4.126.3
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/LICENSE +21 -0
- package/README.md +758 -3
- package/dist/Browser-LM4RRZIL.js +478 -0
- package/dist/Browser-LM4RRZIL.js.map +1 -0
- package/dist/Calculator-HCKFTSJR.js +188 -0
- package/dist/Calculator-HCKFTSJR.js.map +1 -0
- package/dist/ConfirmDialog-LJXMB45A.js +3 -0
- package/dist/ConfirmDialog-LJXMB45A.js.map +1 -0
- package/dist/CurrencyConverter-BKKORN6K.js +232 -0
- package/dist/CurrencyConverter-BKKORN6K.js.map +1 -0
- package/dist/Documents-DD3QI5ER.js +437 -0
- package/dist/Documents-DD3QI5ER.js.map +1 -0
- package/dist/Files-BOPCPMJE.js +801 -0
- package/dist/Files-BOPCPMJE.js.map +1 -0
- package/dist/Notepad-KJOLF63O.js +406 -0
- package/dist/Notepad-KJOLF63O.js.map +1 -0
- package/dist/PdfViewer-LSP3YNFC.js +4 -0
- package/dist/PdfViewer-LSP3YNFC.js.map +1 -0
- package/dist/PomodoroTimer-SVJ62RXM.js +629 -0
- package/dist/PomodoroTimer-SVJ62RXM.js.map +1 -0
- package/dist/Preview-5UOC7ZRU.js +2955 -0
- package/dist/Preview-5UOC7ZRU.js.map +1 -0
- package/dist/Sidebar-J76PV4QF.js +290 -0
- package/dist/Sidebar-J76PV4QF.js.map +1 -0
- package/dist/Spreadsheet-J7BH7UHC.js +515 -0
- package/dist/Spreadsheet-J7BH7UHC.js.map +1 -0
- package/dist/Stock-CDUS4VCZ.js +154 -0
- package/dist/Stock-CDUS4VCZ.js.map +1 -0
- package/dist/Weather-EEDBWBPQ.js +427 -0
- package/dist/Weather-EEDBWBPQ.js.map +1 -0
- package/dist/WorldClock-FFFULVTJ.js +198 -0
- package/dist/WorldClock-FFFULVTJ.js.map +1 -0
- package/dist/apps/index.d.ts +267 -0
- package/dist/apps/index.js +147 -0
- package/dist/apps/index.js.map +1 -0
- package/dist/brand.css +2 -0
- package/dist/chunk-2VM2FRZJ.js +215 -0
- package/dist/chunk-2VM2FRZJ.js.map +1 -0
- package/dist/chunk-32W6VY2J.js +549 -0
- package/dist/chunk-32W6VY2J.js.map +1 -0
- package/dist/chunk-3CUSBZWG.js +59 -0
- package/dist/chunk-3CUSBZWG.js.map +1 -0
- package/dist/chunk-3L463T5H.js +26 -0
- package/dist/chunk-3L463T5H.js.map +1 -0
- package/dist/chunk-4TVV2BIU.js +13334 -0
- package/dist/chunk-4TVV2BIU.js.map +1 -0
- package/dist/chunk-6XQHH7D2.js +105 -0
- package/dist/chunk-6XQHH7D2.js.map +1 -0
- package/dist/chunk-7CCHEEYC.js +94 -0
- package/dist/chunk-7CCHEEYC.js.map +1 -0
- package/dist/chunk-7KIOEKHS.js +617 -0
- package/dist/chunk-7KIOEKHS.js.map +1 -0
- package/dist/chunk-AXWC3332.js +1103 -0
- package/dist/chunk-AXWC3332.js.map +1 -0
- package/dist/chunk-BFBWLCO6.js +16 -0
- package/dist/chunk-BFBWLCO6.js.map +1 -0
- package/dist/chunk-CT3HVZRZ.js +26 -0
- package/dist/chunk-CT3HVZRZ.js.map +1 -0
- package/dist/chunk-D7PYW2QS.js +265 -0
- package/dist/chunk-D7PYW2QS.js.map +1 -0
- package/dist/chunk-DMCFOWOJ.js +153 -0
- package/dist/chunk-DMCFOWOJ.js.map +1 -0
- package/dist/chunk-FQB24B42.js +416 -0
- package/dist/chunk-FQB24B42.js.map +1 -0
- package/dist/chunk-GA35NEND.js +56 -0
- package/dist/chunk-GA35NEND.js.map +1 -0
- package/dist/chunk-HNH7FOYB.js +69 -0
- package/dist/chunk-HNH7FOYB.js.map +1 -0
- package/dist/chunk-HYZ5QMVO.js +462 -0
- package/dist/chunk-HYZ5QMVO.js.map +1 -0
- package/dist/chunk-I464GYJJ.js +17 -0
- package/dist/chunk-I464GYJJ.js.map +1 -0
- package/dist/chunk-JFQGPY7W.js +819 -0
- package/dist/chunk-JFQGPY7W.js.map +1 -0
- package/dist/chunk-MGGY74RY.js +14 -0
- package/dist/chunk-MGGY74RY.js.map +1 -0
- package/dist/chunk-N3CG5HG5.js +144 -0
- package/dist/chunk-N3CG5HG5.js.map +1 -0
- package/dist/chunk-QRGFTK2X.js +79 -0
- package/dist/chunk-QRGFTK2X.js.map +1 -0
- package/dist/chunk-SZ4WMHUX.js +7 -0
- package/dist/chunk-SZ4WMHUX.js.map +1 -0
- package/dist/chunk-W6XTG23K.js +3708 -0
- package/dist/chunk-W6XTG23K.js.map +1 -0
- package/dist/chunk-YG45DAUT.js +96 -0
- package/dist/chunk-YG45DAUT.js.map +1 -0
- package/dist/chunk-YNGPTOL6.js +46 -0
- package/dist/chunk-YNGPTOL6.js.map +1 -0
- package/dist/chunk-YNHLQ24V.js +400 -0
- package/dist/chunk-YNHLQ24V.js.map +1 -0
- package/dist/chunk-YNKK7NE3.js +171 -0
- package/dist/chunk-YNKK7NE3.js.map +1 -0
- package/dist/chunk-YPO6RDMX.js +20 -0
- package/dist/chunk-YPO6RDMX.js.map +1 -0
- package/dist/file-intake/index.d.ts +87 -0
- package/dist/file-intake/index.js +3 -0
- package/dist/file-intake/index.js.map +1 -0
- package/dist/index-dQia6NpB.d.ts +5904 -0
- package/dist/index.d.ts +2495 -0
- package/dist/index.js +7460 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown/index.d.ts +118 -0
- package/dist/markdown/index.js +184 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/markup/index.d.ts +235 -0
- package/dist/markup/index.js +135 -0
- package/dist/markup/index.js.map +1 -0
- package/dist/page-tabs/index.d.ts +154 -0
- package/dist/page-tabs/index.js +706 -0
- package/dist/page-tabs/index.js.map +1 -0
- package/dist/shell.css +105 -0
- package/dist/sounds-NT4DEZGD.js +3 -0
- package/dist/sounds-NT4DEZGD.js.map +1 -0
- package/dist/styles.css +22 -0
- package/dist/themes.css +249 -0
- package/dist/types-BOinJXUf.d.ts +118 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +15 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui.css +1518 -0
- package/package.json +172 -3
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
import { useShellStrings, PopupMenu, PopupMenuItem, PopupMenuDivider } from '../chunk-7KIOEKHS.js';
|
|
2
|
+
import { confirm } from '../chunk-HYZ5QMVO.js';
|
|
3
|
+
import '../chunk-3CUSBZWG.js';
|
|
4
|
+
import * as ReactModule from 'react';
|
|
5
|
+
import { createContext, memo, useRef, useLayoutEffect, useEffect, useMemo, useId, useCallback, useReducer, useContext, useState, Fragment as Fragment$1 } from 'react';
|
|
6
|
+
import { useLocation, useNavigate } from 'react-router-dom';
|
|
7
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
var PageTabsContext = createContext(null);
|
|
10
|
+
var PageTabFrameContext = createContext(null);
|
|
11
|
+
|
|
12
|
+
// src/page-tabs/tabsModel.ts
|
|
13
|
+
var DEFAULT_MAX_TABS = 12;
|
|
14
|
+
function emptyState() {
|
|
15
|
+
return { tabs: [], activeKey: null, mounted: [], dirty: [], closing: null, clock: 0 };
|
|
16
|
+
}
|
|
17
|
+
var without = (list, key) => list.filter((k) => k !== key);
|
|
18
|
+
var withKey = (list, key) => list.includes(key) ? list : [...list, key];
|
|
19
|
+
function orderPinned(tabs, pinnedKeys) {
|
|
20
|
+
const pinned = pinnedKeys.map((key) => tabs.find((t) => t.key === key)).filter((t) => Boolean(t));
|
|
21
|
+
return [...pinned, ...tabs.filter((t) => !pinnedKeys.includes(t.key))];
|
|
22
|
+
}
|
|
23
|
+
function visit(state, entry, maxTabs = DEFAULT_MAX_TABS) {
|
|
24
|
+
const clock = state.clock + 1;
|
|
25
|
+
const existing = state.tabs.find((t) => t.key === entry.key);
|
|
26
|
+
let tabs;
|
|
27
|
+
if (existing) {
|
|
28
|
+
tabs = state.tabs.map(
|
|
29
|
+
(t) => t.key === entry.key ? {
|
|
30
|
+
...t,
|
|
31
|
+
path: entry.path,
|
|
32
|
+
title: t.titleFrom === "page" ? t.title : entry.title || t.title,
|
|
33
|
+
lastActive: clock
|
|
34
|
+
} : t
|
|
35
|
+
);
|
|
36
|
+
} else {
|
|
37
|
+
tabs = [
|
|
38
|
+
...state.tabs,
|
|
39
|
+
{ key: entry.key, path: entry.path, title: entry.title, titleFrom: "route", pinned: false, lastActive: clock }
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
let next = {
|
|
43
|
+
...state,
|
|
44
|
+
tabs,
|
|
45
|
+
activeKey: entry.key,
|
|
46
|
+
mounted: withKey(state.mounted, entry.key),
|
|
47
|
+
clock
|
|
48
|
+
};
|
|
49
|
+
if (next.closing && next.closing !== entry.key) next = removeTab(next, next.closing);
|
|
50
|
+
else if (next.closing === entry.key) next = { ...next, closing: null };
|
|
51
|
+
return evictOverflow(next, maxTabs);
|
|
52
|
+
}
|
|
53
|
+
function visitTransient(state) {
|
|
54
|
+
let next = { ...state, activeKey: null };
|
|
55
|
+
if (next.closing) next = removeTab(next, next.closing);
|
|
56
|
+
return next;
|
|
57
|
+
}
|
|
58
|
+
function evictOverflow(state, maxTabs) {
|
|
59
|
+
let next = state;
|
|
60
|
+
while (next.tabs.length > maxTabs) {
|
|
61
|
+
const candidates = next.tabs.filter((t) => !t.pinned && t.key !== next.activeKey && !next.dirty.includes(t.key) && t.key !== next.closing).sort((a, b) => a.lastActive - b.lastActive);
|
|
62
|
+
if (candidates.length === 0) break;
|
|
63
|
+
next = removeTab(next, candidates[0].key);
|
|
64
|
+
}
|
|
65
|
+
return next;
|
|
66
|
+
}
|
|
67
|
+
function removeTab(state, key) {
|
|
68
|
+
return {
|
|
69
|
+
...state,
|
|
70
|
+
tabs: state.tabs.filter((t) => t.key !== key),
|
|
71
|
+
mounted: without(state.mounted, key),
|
|
72
|
+
dirty: without(state.dirty, key),
|
|
73
|
+
activeKey: state.activeKey === key ? null : state.activeKey,
|
|
74
|
+
closing: state.closing === key ? null : state.closing
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function neighbourOf(state, key) {
|
|
78
|
+
const visible = state.tabs.filter((t) => t.key !== state.closing || t.key === key);
|
|
79
|
+
const i = visible.findIndex((t) => t.key === key);
|
|
80
|
+
if (i < 0) return null;
|
|
81
|
+
return visible[i + 1] ?? visible[i - 1] ?? null;
|
|
82
|
+
}
|
|
83
|
+
function beginClose(state, key) {
|
|
84
|
+
return { ...state, closing: key };
|
|
85
|
+
}
|
|
86
|
+
function keysToClose(state, scope, key) {
|
|
87
|
+
const closable = state.tabs.filter((t) => !t.pinned);
|
|
88
|
+
if (scope === "all") return closable.map((t) => t.key);
|
|
89
|
+
if (scope === "others") return closable.filter((t) => t.key !== key).map((t) => t.key);
|
|
90
|
+
const i = state.tabs.findIndex((t) => t.key === key);
|
|
91
|
+
if (i < 0) return [];
|
|
92
|
+
return state.tabs.slice(i + 1).filter((t) => !t.pinned).map((t) => t.key);
|
|
93
|
+
}
|
|
94
|
+
function setTitle(state, key, title) {
|
|
95
|
+
const tab = state.tabs.find((t) => t.key === key);
|
|
96
|
+
if (!tab || !title || tab.title === title && tab.titleFrom === "page") return state;
|
|
97
|
+
return {
|
|
98
|
+
...state,
|
|
99
|
+
tabs: state.tabs.map((t) => t.key === key ? { ...t, title, titleFrom: "page" } : t)
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function setDirty(state, key, dirty) {
|
|
103
|
+
const has = state.dirty.includes(key);
|
|
104
|
+
if (dirty === has) return state;
|
|
105
|
+
return { ...state, dirty: dirty ? [...state.dirty, key] : without(state.dirty, key) };
|
|
106
|
+
}
|
|
107
|
+
function ensurePinned(state, pinned) {
|
|
108
|
+
const pinnedKeys = pinned.map((p) => p.key);
|
|
109
|
+
let tabs = state.tabs.map((t) => ({ ...t, pinned: pinnedKeys.includes(t.key) }));
|
|
110
|
+
for (const p of pinned) {
|
|
111
|
+
if (!tabs.some((t) => t.key === p.key)) {
|
|
112
|
+
tabs = [...tabs, { key: p.key, path: p.path, title: p.title, titleFrom: "route", pinned: true, lastActive: 0 }];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { ...state, tabs: orderPinned(tabs, pinnedKeys) };
|
|
116
|
+
}
|
|
117
|
+
function serialize(state) {
|
|
118
|
+
const stored = {
|
|
119
|
+
v: 1,
|
|
120
|
+
tabs: state.tabs.filter((t) => t.key !== state.closing).map(({ key, path, title, titleFrom }) => ({ key, path, title, titleFrom }))
|
|
121
|
+
};
|
|
122
|
+
return JSON.stringify(stored);
|
|
123
|
+
}
|
|
124
|
+
function restore(raw, resolve) {
|
|
125
|
+
if (!raw) return [];
|
|
126
|
+
let parsed;
|
|
127
|
+
try {
|
|
128
|
+
parsed = JSON.parse(raw);
|
|
129
|
+
} catch {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
const stored = parsed;
|
|
133
|
+
if (!stored || stored.v !== 1 || !Array.isArray(stored.tabs)) return [];
|
|
134
|
+
const out = [];
|
|
135
|
+
for (const item of stored.tabs) {
|
|
136
|
+
if (!item || typeof item.path !== "string" || !item.path.startsWith("/") || item.path.startsWith("//")) continue;
|
|
137
|
+
const hashAt = item.path.indexOf("#");
|
|
138
|
+
const path = hashAt >= 0 ? item.path.slice(0, hashAt) : item.path;
|
|
139
|
+
const entry = resolve(path);
|
|
140
|
+
if (!entry || out.some((t) => t.key === entry.key)) continue;
|
|
141
|
+
const pageTitle = item.titleFrom === "page" && typeof item.title === "string" && item.title;
|
|
142
|
+
out.push({
|
|
143
|
+
key: entry.key,
|
|
144
|
+
path,
|
|
145
|
+
title: pageTitle || entry.title,
|
|
146
|
+
titleFrom: pageTitle ? "page" : "route",
|
|
147
|
+
pinned: false,
|
|
148
|
+
lastActive: 0
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
function reducer(state, action) {
|
|
154
|
+
switch (action.type) {
|
|
155
|
+
case "visit":
|
|
156
|
+
return visit(state, action.entry, action.maxTabs);
|
|
157
|
+
case "transient":
|
|
158
|
+
return visitTransient(state);
|
|
159
|
+
case "close": {
|
|
160
|
+
let next = state;
|
|
161
|
+
for (const key of action.keys) next = removeTab(next, key);
|
|
162
|
+
return action.closing ? beginClose(next, action.closing) : next;
|
|
163
|
+
}
|
|
164
|
+
case "title":
|
|
165
|
+
return setTitle(state, action.key, action.title);
|
|
166
|
+
case "dirty":
|
|
167
|
+
return setDirty(state, action.key, action.dirty);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
var pathOf = (location) => `${location.pathname}${location.search}`;
|
|
171
|
+
function parseTabPath(path) {
|
|
172
|
+
const hashAt = path.indexOf("#");
|
|
173
|
+
const hash = hashAt >= 0 ? path.slice(hashAt) : "";
|
|
174
|
+
const rest = hashAt >= 0 ? path.slice(0, hashAt) : path;
|
|
175
|
+
const searchAt = rest.indexOf("?");
|
|
176
|
+
return {
|
|
177
|
+
pathname: searchAt >= 0 ? rest.slice(0, searchAt) : rest,
|
|
178
|
+
search: searchAt >= 0 ? rest.slice(searchAt) : "",
|
|
179
|
+
hash
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function readStorage(key) {
|
|
183
|
+
if (!key) return null;
|
|
184
|
+
try {
|
|
185
|
+
return localStorage.getItem(key);
|
|
186
|
+
} catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function PageTabsProvider({
|
|
191
|
+
children,
|
|
192
|
+
resolve,
|
|
193
|
+
pinned = [],
|
|
194
|
+
storageKey,
|
|
195
|
+
maxTabs = DEFAULT_MAX_TABS,
|
|
196
|
+
enabled = true,
|
|
197
|
+
confirmDiscard
|
|
198
|
+
}) {
|
|
199
|
+
const location = useLocation();
|
|
200
|
+
const navigate = useNavigate();
|
|
201
|
+
const strings = useShellStrings().pageTabs;
|
|
202
|
+
const idPrefix = useId();
|
|
203
|
+
const resolveRef = useRef(resolve);
|
|
204
|
+
resolveRef.current = resolve;
|
|
205
|
+
const entryFor = useCallback((loc) => {
|
|
206
|
+
const route = resolveRef.current(loc);
|
|
207
|
+
if (!route) return null;
|
|
208
|
+
return { key: route.key ?? loc.pathname, path: pathOf(loc), title: route.title };
|
|
209
|
+
}, []);
|
|
210
|
+
const [state, dispatch] = useReducer(reducer, void 0, () => {
|
|
211
|
+
const restored = restore(readStorage(storageKey), (path) => entryFor(parseTabPath(path)));
|
|
212
|
+
const pinnedEntries = pinned.map((path) => entryFor(parseTabPath(path))).filter((e) => Boolean(e));
|
|
213
|
+
let initial = ensurePinned({ ...emptyState(), tabs: restored }, pinnedEntries);
|
|
214
|
+
const entry = enabled ? entryFor(location) : null;
|
|
215
|
+
initial = entry ? visit(initial, entry, maxTabs) : visitTransient(initial);
|
|
216
|
+
return initial;
|
|
217
|
+
});
|
|
218
|
+
const stateRef = useRef(state);
|
|
219
|
+
stateRef.current = state;
|
|
220
|
+
const locations = useRef(/* @__PURE__ */ new Map());
|
|
221
|
+
const current = enabled ? entryFor(location) : null;
|
|
222
|
+
const currentKey = current?.key ?? null;
|
|
223
|
+
useLayoutEffect(() => {
|
|
224
|
+
if (!enabled) return;
|
|
225
|
+
const entry = entryFor(location);
|
|
226
|
+
if (entry) {
|
|
227
|
+
locations.current.set(entry.key, location);
|
|
228
|
+
dispatch({ type: "visit", entry, maxTabs });
|
|
229
|
+
} else {
|
|
230
|
+
dispatch({ type: "transient" });
|
|
231
|
+
}
|
|
232
|
+
}, [location, enabled, maxTabs, entryFor]);
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
const open = new Set(state.tabs.map((t) => t.key));
|
|
235
|
+
for (const key of locations.current.keys()) if (!open.has(key)) locations.current.delete(key);
|
|
236
|
+
}, [state.tabs]);
|
|
237
|
+
const serialized = useMemo(() => serialize(state), [state]);
|
|
238
|
+
useEffect(() => {
|
|
239
|
+
if (!storageKey) return;
|
|
240
|
+
try {
|
|
241
|
+
localStorage.setItem(storageKey, serialized);
|
|
242
|
+
} catch {
|
|
243
|
+
}
|
|
244
|
+
}, [serialized, storageKey]);
|
|
245
|
+
const anyDirty = state.dirty.length > 0;
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
if (!anyDirty) return;
|
|
248
|
+
const hold = (e) => {
|
|
249
|
+
e.preventDefault();
|
|
250
|
+
e.returnValue = "";
|
|
251
|
+
};
|
|
252
|
+
window.addEventListener("beforeunload", hold);
|
|
253
|
+
return () => window.removeEventListener("beforeunload", hold);
|
|
254
|
+
}, [anyDirty]);
|
|
255
|
+
const ids = useRef(/* @__PURE__ */ new Map());
|
|
256
|
+
const idFor = useCallback(
|
|
257
|
+
(key, part) => {
|
|
258
|
+
let n = ids.current.get(key);
|
|
259
|
+
if (n === void 0) {
|
|
260
|
+
n = ids.current.size;
|
|
261
|
+
ids.current.set(key, n);
|
|
262
|
+
}
|
|
263
|
+
return `${idPrefix}-page-${part}-${n}`;
|
|
264
|
+
},
|
|
265
|
+
[idPrefix]
|
|
266
|
+
);
|
|
267
|
+
const askDiscard = useCallback(
|
|
268
|
+
(tabs) => {
|
|
269
|
+
if (confirmDiscard) return confirmDiscard(tabs);
|
|
270
|
+
return confirm({
|
|
271
|
+
title: strings.discardTitle,
|
|
272
|
+
message: tabs.length === 1 ? strings.discardOne : `${tabs.length} ${strings.discardMany}`,
|
|
273
|
+
confirmLabel: strings.discardConfirm,
|
|
274
|
+
cancelLabel: strings.keepEditing,
|
|
275
|
+
variant: "warning"
|
|
276
|
+
});
|
|
277
|
+
},
|
|
278
|
+
[confirmDiscard, strings]
|
|
279
|
+
);
|
|
280
|
+
const fallbackPath = useCallback(() => {
|
|
281
|
+
const s = stateRef.current;
|
|
282
|
+
return s.tabs.find((t) => t.pinned)?.path ?? "/";
|
|
283
|
+
}, []);
|
|
284
|
+
const closeNow = useCallback(
|
|
285
|
+
(keys, to, prefer) => {
|
|
286
|
+
const s = stateRef.current;
|
|
287
|
+
const active = s.activeKey;
|
|
288
|
+
if (!active || !keys.includes(active)) {
|
|
289
|
+
dispatch({ type: "close", keys, closing: null });
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
let target = to;
|
|
293
|
+
if (!target) {
|
|
294
|
+
const survivors = { ...s, tabs: s.tabs.filter((t) => t.key === active || !keys.includes(t.key)) };
|
|
295
|
+
const preferred = prefer ? s.tabs.find((t) => t.key === prefer && !keys.includes(t.key)) : void 0;
|
|
296
|
+
target = (preferred ?? neighbourOf(survivors, active))?.path ?? fallbackPath();
|
|
297
|
+
}
|
|
298
|
+
dispatch({ type: "close", keys: keys.filter((k) => k !== active), closing: active });
|
|
299
|
+
navigate(target);
|
|
300
|
+
},
|
|
301
|
+
[navigate, fallbackPath]
|
|
302
|
+
);
|
|
303
|
+
const close = useCallback(
|
|
304
|
+
async (key, options = {}) => {
|
|
305
|
+
const tab = stateRef.current.tabs.find((t) => t.key === key);
|
|
306
|
+
if (!tab || tab.pinned) {
|
|
307
|
+
if (options.to) navigate(options.to);
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
if (!options.force && stateRef.current.dirty.includes(key) && !await askDiscard([tab])) return false;
|
|
311
|
+
if (!stateRef.current.tabs.some((t) => t.key === key)) return false;
|
|
312
|
+
closeNow([key], options.to);
|
|
313
|
+
return true;
|
|
314
|
+
},
|
|
315
|
+
[askDiscard, closeNow, navigate]
|
|
316
|
+
);
|
|
317
|
+
const closeMany = useCallback(
|
|
318
|
+
async (scope, key) => {
|
|
319
|
+
let keys = keysToClose(stateRef.current, scope, key);
|
|
320
|
+
const dirty = stateRef.current.tabs.filter(
|
|
321
|
+
(t) => keys.includes(t.key) && stateRef.current.dirty.includes(t.key)
|
|
322
|
+
);
|
|
323
|
+
if (dirty.length && !await askDiscard(dirty)) {
|
|
324
|
+
keys = keys.filter((k) => !dirty.some((t) => t.key === k));
|
|
325
|
+
}
|
|
326
|
+
const open = new Set(stateRef.current.tabs.map((t) => t.key));
|
|
327
|
+
keys = keys.filter((k) => open.has(k));
|
|
328
|
+
if (keys.length) closeNow(keys, void 0, scope === "all" ? null : key);
|
|
329
|
+
},
|
|
330
|
+
[askDiscard, closeNow]
|
|
331
|
+
);
|
|
332
|
+
const activate = useCallback(
|
|
333
|
+
(key) => {
|
|
334
|
+
const tab = stateRef.current.tabs.find((t) => t.key === key);
|
|
335
|
+
if (!tab || key === stateRef.current.activeKey) return;
|
|
336
|
+
navigate(tab.path);
|
|
337
|
+
},
|
|
338
|
+
[navigate]
|
|
339
|
+
);
|
|
340
|
+
const setTitle2 = useCallback((key, title) => dispatch({ type: "title", key, title }), []);
|
|
341
|
+
const setDirty2 = useCallback((key, dirty) => dispatch({ type: "dirty", key, dirty }), []);
|
|
342
|
+
const keyFor = useCallback((loc) => entryFor(loc)?.key ?? null, [entryFor]);
|
|
343
|
+
const locationFor = useCallback((key) => locations.current.get(key), []);
|
|
344
|
+
const visibleTabs = useMemo(
|
|
345
|
+
() => enabled ? state.tabs.filter((t) => t.key !== state.closing) : [],
|
|
346
|
+
[enabled, state.tabs, state.closing]
|
|
347
|
+
);
|
|
348
|
+
const value = useMemo(
|
|
349
|
+
() => ({
|
|
350
|
+
enabled,
|
|
351
|
+
state,
|
|
352
|
+
currentKey,
|
|
353
|
+
resolve,
|
|
354
|
+
keyFor,
|
|
355
|
+
locationFor,
|
|
356
|
+
idFor,
|
|
357
|
+
activate,
|
|
358
|
+
close,
|
|
359
|
+
closeMany,
|
|
360
|
+
setTitle: setTitle2,
|
|
361
|
+
setDirty: setDirty2,
|
|
362
|
+
visibleTabs
|
|
363
|
+
}),
|
|
364
|
+
[enabled, state, currentKey, resolve, keyFor, locationFor, idFor, activate, close, closeMany, setTitle2, setDirty2, visibleTabs]
|
|
365
|
+
);
|
|
366
|
+
return /* @__PURE__ */ jsx(PageTabsContext.Provider, { value, children });
|
|
367
|
+
}
|
|
368
|
+
var Activity2 = ReactModule.Activity;
|
|
369
|
+
var PAGE_TABS_PAUSE_HIDDEN = Boolean(Activity2);
|
|
370
|
+
function KeepAlive({ active, children }) {
|
|
371
|
+
if (Activity2) return /* @__PURE__ */ jsx(Activity2, { mode: active ? "visible" : "hidden", children });
|
|
372
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
373
|
+
}
|
|
374
|
+
var sameFrame = (a, b) => a.active === b.active && a.location === b.location && a.className === b.className && a.panelId === b.panelId && a.tabId === b.tabId && (!b.active || a.render === b.render);
|
|
375
|
+
var TabFrame = memo(function TabFrame2({ tabKey, active, location, render, className, panelId, tabId }) {
|
|
376
|
+
const ref = useRef(null);
|
|
377
|
+
const activeRef = useRef(active);
|
|
378
|
+
const scrolls = useRef(/* @__PURE__ */ new Map());
|
|
379
|
+
useLayoutEffect(() => {
|
|
380
|
+
const wasActive = activeRef.current;
|
|
381
|
+
activeRef.current = active;
|
|
382
|
+
if (!active || wasActive) return;
|
|
383
|
+
for (const [el, [top, left]] of scrolls.current) {
|
|
384
|
+
if (!el.isConnected) {
|
|
385
|
+
scrolls.current.delete(el);
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
el.scrollTop = top;
|
|
389
|
+
el.scrollLeft = left;
|
|
390
|
+
}
|
|
391
|
+
}, [active]);
|
|
392
|
+
useEffect(() => {
|
|
393
|
+
const el = ref.current;
|
|
394
|
+
if (!el) return;
|
|
395
|
+
const onScroll = (e) => {
|
|
396
|
+
if (!activeRef.current) return;
|
|
397
|
+
const target = e.target;
|
|
398
|
+
if (!target || typeof target.scrollTop !== "number") return;
|
|
399
|
+
scrolls.current.set(target, [target.scrollTop, target.scrollLeft]);
|
|
400
|
+
};
|
|
401
|
+
el.addEventListener("scroll", onScroll, true);
|
|
402
|
+
return () => el.removeEventListener("scroll", onScroll, true);
|
|
403
|
+
}, []);
|
|
404
|
+
const frame = useMemo(() => ({ key: tabKey, activeRef }), [tabKey]);
|
|
405
|
+
return /* @__PURE__ */ jsx(
|
|
406
|
+
"div",
|
|
407
|
+
{
|
|
408
|
+
ref,
|
|
409
|
+
role: panelId ? "tabpanel" : void 0,
|
|
410
|
+
id: panelId,
|
|
411
|
+
"aria-labelledby": tabId,
|
|
412
|
+
hidden: !active,
|
|
413
|
+
style: active ? void 0 : { display: "none" },
|
|
414
|
+
className,
|
|
415
|
+
"data-page-tab": tabKey,
|
|
416
|
+
children: /* @__PURE__ */ jsx(PageTabFrameContext.Provider, { value: frame, children: /* @__PURE__ */ jsx(KeepAlive, { active, children: render(location) }) })
|
|
417
|
+
}
|
|
418
|
+
);
|
|
419
|
+
}, sameFrame);
|
|
420
|
+
var TRANSIENT = "\0transient";
|
|
421
|
+
function PageTabsOutlet({ children, frameClassName }) {
|
|
422
|
+
const ctx = useContext(PageTabsContext);
|
|
423
|
+
const location = useLocation();
|
|
424
|
+
if (!ctx || !ctx.enabled) {
|
|
425
|
+
return /* @__PURE__ */ jsx("div", { className: frameClassName, children: children(location) }, TRANSIENT);
|
|
426
|
+
}
|
|
427
|
+
const { state, currentKey, locationFor, idFor } = ctx;
|
|
428
|
+
const keys = state.tabs.filter((t) => state.mounted.includes(t.key)).map((t) => t.key);
|
|
429
|
+
if (currentKey && !keys.includes(currentKey)) keys.push(currentKey);
|
|
430
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
431
|
+
keys.map((key) => {
|
|
432
|
+
const active = key === currentKey;
|
|
433
|
+
const loc = active ? location : locationFor(key);
|
|
434
|
+
if (!loc) return null;
|
|
435
|
+
return /* @__PURE__ */ jsx(
|
|
436
|
+
TabFrame,
|
|
437
|
+
{
|
|
438
|
+
tabKey: key,
|
|
439
|
+
active,
|
|
440
|
+
location: loc,
|
|
441
|
+
render: children,
|
|
442
|
+
className: frameClassName,
|
|
443
|
+
panelId: idFor(key, "panel"),
|
|
444
|
+
tabId: idFor(key, "tab")
|
|
445
|
+
},
|
|
446
|
+
key
|
|
447
|
+
);
|
|
448
|
+
}),
|
|
449
|
+
!currentKey && /* @__PURE__ */ jsx(TabFrame, { tabKey: TRANSIENT, active: true, location, render: children, className: frameClassName }, TRANSIENT)
|
|
450
|
+
] });
|
|
451
|
+
}
|
|
452
|
+
var STRIP = "relative flex h-10 shrink-0 items-end gap-3 bg-gray-100 px-3";
|
|
453
|
+
var TABLIST = "flex min-w-0 flex-1 items-end overflow-x-auto overflow-y-hidden [scrollbar-width:none] [&::-webkit-scrollbar]:hidden";
|
|
454
|
+
var TAB_BASE = "group @container relative flex h-9 flex-[0_1_15rem] items-center rounded-t-md border border-b-0 text-[13px] transition-colors";
|
|
455
|
+
var TAB_FLOOR_IDLE = "min-w-[2.75rem]";
|
|
456
|
+
var TAB_FLOOR_ACTIVE = "min-w-[4.25rem]";
|
|
457
|
+
var TAB_PINNED = "group relative flex h-9 flex-none items-center justify-center rounded-t-md border border-b-0 transition-colors";
|
|
458
|
+
var TAB_ACTIVE = "z-[1] border-gray-200 bg-gray-50 text-gray-900";
|
|
459
|
+
var TAB_IDLE = "border-transparent text-gray-600 hover:bg-gray-200/70 hover:text-gray-900";
|
|
460
|
+
var TITLE = "min-w-0 flex-1 overflow-hidden whitespace-nowrap [mask-image:linear-gradient(to_right,#000_calc(100%-1rem),transparent)]";
|
|
461
|
+
var ICON_WRAP = "relative flex size-4 shrink-0 items-center justify-center text-gray-500 [&>svg]:size-4";
|
|
462
|
+
var FOCUS_RING = "outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-500";
|
|
463
|
+
function CloseIcon() {
|
|
464
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true", className: "size-3.5", children: /* @__PURE__ */ jsx("path", { d: "M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" }) });
|
|
465
|
+
}
|
|
466
|
+
function PageTabBar({ className, activeClassName, end }) {
|
|
467
|
+
const ctx = useContext(PageTabsContext);
|
|
468
|
+
const strings = useShellStrings().pageTabs;
|
|
469
|
+
const stripRef = useRef(null);
|
|
470
|
+
const [menu, setMenu] = useState(null);
|
|
471
|
+
const activeKey = ctx?.currentKey ?? null;
|
|
472
|
+
useEffect(() => {
|
|
473
|
+
if (!ctx || !activeKey) return;
|
|
474
|
+
const el = document.getElementById(ctx.idFor(activeKey, "tab"));
|
|
475
|
+
el?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
|
|
476
|
+
}, [ctx, activeKey]);
|
|
477
|
+
if (!ctx || !ctx.enabled) return null;
|
|
478
|
+
const { visibleTabs: tabs, state, idFor, activate, close, closeMany, resolve } = ctx;
|
|
479
|
+
const focusKey = tabs.some((t) => t.key === activeKey) ? activeKey : tabs[0]?.key;
|
|
480
|
+
const focusTab = (index) => {
|
|
481
|
+
const tab = tabs[(index + tabs.length) % tabs.length];
|
|
482
|
+
if (tab) document.getElementById(idFor(tab.key, "tab"))?.focus();
|
|
483
|
+
};
|
|
484
|
+
const onKeyDown = (e) => {
|
|
485
|
+
const i = tabs.findIndex((t) => document.getElementById(idFor(t.key, "tab")) === e.target);
|
|
486
|
+
if (i < 0) return;
|
|
487
|
+
if (e.key === "ArrowRight") focusTab(i + 1);
|
|
488
|
+
else if (e.key === "ArrowLeft") focusTab(i - 1);
|
|
489
|
+
else if (e.key === "Home") focusTab(0);
|
|
490
|
+
else if (e.key === "End") focusTab(tabs.length - 1);
|
|
491
|
+
else if (e.key === "Delete" && !tabs[i].pinned) void close(tabs[i].key);
|
|
492
|
+
else return;
|
|
493
|
+
e.preventDefault();
|
|
494
|
+
};
|
|
495
|
+
const onContextMenu = (e, tab) => {
|
|
496
|
+
e.preventDefault();
|
|
497
|
+
setMenu({ x: e.clientX, y: e.clientY, key: tab.key });
|
|
498
|
+
};
|
|
499
|
+
const onAuxClick = (e, tab) => {
|
|
500
|
+
if (e.button !== 1 || tab.pinned) return;
|
|
501
|
+
e.preventDefault();
|
|
502
|
+
void close(tab.key);
|
|
503
|
+
};
|
|
504
|
+
const menuTab = menu ? tabs.find((t) => t.key === menu.key) : void 0;
|
|
505
|
+
const menuIndex = menuTab ? tabs.indexOf(menuTab) : -1;
|
|
506
|
+
const closable = (list) => list.some((t) => !t.pinned);
|
|
507
|
+
const run = (fn) => () => {
|
|
508
|
+
setMenu(null);
|
|
509
|
+
void fn();
|
|
510
|
+
};
|
|
511
|
+
return /* @__PURE__ */ jsxs("div", { className: className ?? STRIP, children: [
|
|
512
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-x-0 bottom-0 h-px bg-gray-200" }),
|
|
513
|
+
/* @__PURE__ */ jsx(
|
|
514
|
+
"div",
|
|
515
|
+
{
|
|
516
|
+
ref: stripRef,
|
|
517
|
+
role: "tablist",
|
|
518
|
+
"aria-label": strings.label,
|
|
519
|
+
onKeyDown,
|
|
520
|
+
className: TABLIST,
|
|
521
|
+
children: tabs.map((tab, i) => {
|
|
522
|
+
const active = tab.key === activeKey;
|
|
523
|
+
const dirty = state.dirty.includes(tab.key);
|
|
524
|
+
const icon = resolve(parseTabPath(tab.path))?.icon;
|
|
525
|
+
const colours = active ? activeClassName ?? TAB_ACTIVE : TAB_IDLE;
|
|
526
|
+
const divider = i > 0 && !active && tabs[i - 1].key !== activeKey;
|
|
527
|
+
const tabButton = {
|
|
528
|
+
type: "button",
|
|
529
|
+
role: "tab",
|
|
530
|
+
id: idFor(tab.key, "tab"),
|
|
531
|
+
"aria-selected": active,
|
|
532
|
+
"aria-controls": state.mounted.includes(tab.key) ? idFor(tab.key, "panel") : void 0,
|
|
533
|
+
tabIndex: tab.key === focusKey ? 0 : -1,
|
|
534
|
+
title: tab.title,
|
|
535
|
+
onClick: () => activate(tab.key)
|
|
536
|
+
};
|
|
537
|
+
const handlers = {
|
|
538
|
+
onContextMenu: (e) => onContextMenu(e, tab),
|
|
539
|
+
onAuxClick: (e) => onAuxClick(e, tab),
|
|
540
|
+
// A middle-press would otherwise start the browser's autoscroll.
|
|
541
|
+
onMouseDown: (e) => {
|
|
542
|
+
if (e.button === 1) e.preventDefault();
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
const iconEl = icon && // The consumer's nav icons, whatever size they were drawn at for
|
|
546
|
+
// the sidebar, sit at the strip's size here.
|
|
547
|
+
/* @__PURE__ */ jsxs("span", { className: ICON_WRAP, "aria-hidden": "true", children: [
|
|
548
|
+
icon,
|
|
549
|
+
dirty && !active && !tab.pinned && // Unsaved work on a tab too narrow for its × slot: the dot
|
|
550
|
+
// moves onto the icon.
|
|
551
|
+
/* @__PURE__ */ jsx("span", { className: "absolute -right-1 -top-1 hidden size-2 rounded-full bg-gray-500 ring-2 ring-gray-100 @max-[6.5rem]:block @max-[6.5rem]:group-hover:hidden" })
|
|
552
|
+
] });
|
|
553
|
+
if (tab.pinned) {
|
|
554
|
+
return /* @__PURE__ */ jsx("div", { role: "presentation", className: `${TAB_PINNED} ${icon ? "w-10" : ""} ${colours}`, ...handlers, children: /* @__PURE__ */ jsxs("button", { ...tabButton, className: `flex h-full items-center justify-center rounded-t-md ${icon ? "w-full" : "px-3"} ${FOCUS_RING}`, children: [
|
|
555
|
+
iconEl,
|
|
556
|
+
/* @__PURE__ */ jsx("span", { className: icon ? "sr-only" : "whitespace-nowrap text-[13px]", children: tab.title })
|
|
557
|
+
] }) }, tab.key);
|
|
558
|
+
}
|
|
559
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
560
|
+
divider && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "mb-2.5 h-4 w-px shrink-0 self-end border-l border-gray-300" }),
|
|
561
|
+
/* @__PURE__ */ jsxs(
|
|
562
|
+
"div",
|
|
563
|
+
{
|
|
564
|
+
role: "presentation",
|
|
565
|
+
className: `${TAB_BASE} ${active ? TAB_FLOOR_ACTIVE : TAB_FLOOR_IDLE} ${colours}`,
|
|
566
|
+
...handlers,
|
|
567
|
+
children: [
|
|
568
|
+
/* @__PURE__ */ jsxs(
|
|
569
|
+
"button",
|
|
570
|
+
{
|
|
571
|
+
...tabButton,
|
|
572
|
+
className: `flex h-full min-w-0 flex-1 items-center gap-2 rounded-t-md pl-3 pr-1 text-left ${FOCUS_RING} ${active ? "@max-[5rem]:pl-2.5" : "@max-[6.5rem]:pr-3 @max-[6.5rem]:group-hover:pr-1 @max-[4.5rem]:justify-center @max-[4.5rem]:px-0 @max-[4.5rem]:group-hover:pl-1.5"}`,
|
|
573
|
+
children: [
|
|
574
|
+
iconEl,
|
|
575
|
+
/* @__PURE__ */ jsx("span", { className: `${TITLE} ${icon ? active ? "@max-[5rem]:sr-only" : "@max-[4.5rem]:sr-only" : ""}`, children: tab.title })
|
|
576
|
+
]
|
|
577
|
+
}
|
|
578
|
+
),
|
|
579
|
+
/* @__PURE__ */ jsxs(
|
|
580
|
+
"span",
|
|
581
|
+
{
|
|
582
|
+
className: `mr-1.5 flex size-5 shrink-0 items-center justify-center ${active ? "" : "@max-[6.5rem]:hidden @max-[6.5rem]:group-hover:flex"}`,
|
|
583
|
+
children: [
|
|
584
|
+
dirty && /* @__PURE__ */ jsx(
|
|
585
|
+
"span",
|
|
586
|
+
{
|
|
587
|
+
role: "img",
|
|
588
|
+
"aria-label": strings.unsaved,
|
|
589
|
+
className: "size-2 rounded-full bg-gray-500 group-hover:hidden"
|
|
590
|
+
}
|
|
591
|
+
),
|
|
592
|
+
/* @__PURE__ */ jsx(
|
|
593
|
+
"button",
|
|
594
|
+
{
|
|
595
|
+
type: "button",
|
|
596
|
+
tabIndex: -1,
|
|
597
|
+
"aria-label": `${strings.close} ${tab.title}`,
|
|
598
|
+
onClick: () => void close(tab.key),
|
|
599
|
+
className: `size-5 items-center justify-center rounded text-gray-400 hover:bg-gray-200 hover:text-gray-700 ${dirty ? "hidden group-hover:flex" : active ? "flex" : "flex opacity-0 group-hover:opacity-100 focus-visible:opacity-100"}`,
|
|
600
|
+
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
601
|
+
}
|
|
602
|
+
)
|
|
603
|
+
]
|
|
604
|
+
}
|
|
605
|
+
)
|
|
606
|
+
]
|
|
607
|
+
}
|
|
608
|
+
)
|
|
609
|
+
] }, tab.key);
|
|
610
|
+
})
|
|
611
|
+
}
|
|
612
|
+
),
|
|
613
|
+
end && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2 self-center", children: end }),
|
|
614
|
+
menu && menuTab && /* @__PURE__ */ jsxs(PopupMenu, { portal: true, style: { left: menu.x, top: menu.y }, onClose: () => setMenu(null), children: [
|
|
615
|
+
/* @__PURE__ */ jsx(PopupMenuItem, { disabled: menuTab.pinned, onClick: run(() => close(menuTab.key)), children: strings.close }),
|
|
616
|
+
/* @__PURE__ */ jsx(PopupMenuDivider, {}),
|
|
617
|
+
/* @__PURE__ */ jsx(
|
|
618
|
+
PopupMenuItem,
|
|
619
|
+
{
|
|
620
|
+
disabled: !closable(tabs.filter((t) => t.key !== menuTab.key)),
|
|
621
|
+
onClick: run(() => closeMany("others", menuTab.key)),
|
|
622
|
+
children: strings.closeOthers
|
|
623
|
+
}
|
|
624
|
+
),
|
|
625
|
+
/* @__PURE__ */ jsx(
|
|
626
|
+
PopupMenuItem,
|
|
627
|
+
{
|
|
628
|
+
disabled: !closable(tabs.slice(menuIndex + 1)),
|
|
629
|
+
onClick: run(() => closeMany("right", menuTab.key)),
|
|
630
|
+
children: strings.closeRight
|
|
631
|
+
}
|
|
632
|
+
),
|
|
633
|
+
/* @__PURE__ */ jsx(PopupMenuItem, { disabled: !closable(tabs), onClick: run(() => closeMany("all", null)), children: strings.closeAll })
|
|
634
|
+
] })
|
|
635
|
+
] });
|
|
636
|
+
}
|
|
637
|
+
var NO_TABS = {
|
|
638
|
+
enabled: false,
|
|
639
|
+
tabs: [],
|
|
640
|
+
activeKey: null,
|
|
641
|
+
activate: () => {
|
|
642
|
+
},
|
|
643
|
+
close: async () => false,
|
|
644
|
+
closeOthers: async () => {
|
|
645
|
+
},
|
|
646
|
+
closeRight: async () => {
|
|
647
|
+
},
|
|
648
|
+
closeAll: async () => {
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
function usePageTabs() {
|
|
652
|
+
const ctx = useContext(PageTabsContext);
|
|
653
|
+
if (!ctx || !ctx.enabled) return NO_TABS;
|
|
654
|
+
return {
|
|
655
|
+
enabled: true,
|
|
656
|
+
tabs: ctx.visibleTabs,
|
|
657
|
+
activeKey: ctx.currentKey,
|
|
658
|
+
activate: ctx.activate,
|
|
659
|
+
close: ctx.close,
|
|
660
|
+
closeOthers: (key) => ctx.closeMany("others", key),
|
|
661
|
+
closeRight: (key) => ctx.closeMany("right", key),
|
|
662
|
+
closeAll: () => ctx.closeMany("all", null)
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function usePageTab() {
|
|
666
|
+
const ctx = useContext(PageTabsContext);
|
|
667
|
+
const frame = useContext(PageTabFrameContext);
|
|
668
|
+
const navigate = useNavigate();
|
|
669
|
+
const key = ctx?.enabled && frame ? frame.key : null;
|
|
670
|
+
const close = useCallback(
|
|
671
|
+
async (options = {}) => {
|
|
672
|
+
if (ctx?.enabled && frame && ctx.state.tabs.some((t) => t.key === frame.key)) {
|
|
673
|
+
return ctx.close(frame.key, options);
|
|
674
|
+
}
|
|
675
|
+
if (options.to) navigate(options.to);
|
|
676
|
+
return false;
|
|
677
|
+
},
|
|
678
|
+
[ctx, frame, navigate]
|
|
679
|
+
);
|
|
680
|
+
return { key, close };
|
|
681
|
+
}
|
|
682
|
+
function usePageTabTitle(title) {
|
|
683
|
+
const ctx = useContext(PageTabsContext);
|
|
684
|
+
const frame = useContext(PageTabFrameContext);
|
|
685
|
+
const setTitle2 = ctx?.enabled ? ctx.setTitle : void 0;
|
|
686
|
+
const key = frame?.key;
|
|
687
|
+
useEffect(() => {
|
|
688
|
+
if (setTitle2 && key && title) setTitle2(key, title);
|
|
689
|
+
}, [setTitle2, key, title]);
|
|
690
|
+
}
|
|
691
|
+
function usePageTabDirty(dirty) {
|
|
692
|
+
const ctx = useContext(PageTabsContext);
|
|
693
|
+
const frame = useContext(PageTabFrameContext);
|
|
694
|
+
const setDirty2 = ctx?.enabled ? ctx.setDirty : void 0;
|
|
695
|
+
useEffect(() => {
|
|
696
|
+
if (!setDirty2 || !frame) return;
|
|
697
|
+
setDirty2(frame.key, dirty);
|
|
698
|
+
return () => {
|
|
699
|
+
if (frame.activeRef.current) setDirty2(frame.key, false);
|
|
700
|
+
};
|
|
701
|
+
}, [setDirty2, frame, dirty]);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
export { DEFAULT_MAX_TABS, PAGE_TABS_PAUSE_HIDDEN, PageTabBar, PageTabsOutlet, PageTabsProvider, parseTabPath, usePageTab, usePageTabDirty, usePageTabTitle, usePageTabs };
|
|
705
|
+
//# sourceMappingURL=index.js.map
|
|
706
|
+
//# sourceMappingURL=index.js.map
|