@leavepulse/ui 0.72.5 → 0.73.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{LpNumberFlow-Chp9pEDe.css → LpNumberFlow-P1balINP.css} +1 -1
- package/dist/chunks/{LpAppShell-DMSJg18Q.js → LpAppShell-rr5kqiK3.js} +8 -2
- package/dist/chunks/{LpCodeDisplay-AQx-3nNb.js → LpCodeDisplay-CE9TxCA4.js} +1 -1
- package/dist/chunks/{LpCommandScope-BlKcgBnK.js → LpCommandScope-CtUhJe8T.js} +1 -1
- package/dist/chunks/{LpCountdown-BxE7stYk.js → LpCountdown-DgeY3cLG.js} +1 -1
- package/dist/chunks/{LpGauge-BO1PYYqD.js → LpGauge-KZaQ_FDR.js} +1 -1
- package/dist/chunks/{LpModalNav-CE3iLnxB.js → LpModalNav-DQpV2kdn.js} +1 -1
- package/dist/chunks/{LpNumberFlow-CcxEGUy0.js → LpNumberFlow-Vqj0tP7B.js} +1 -1
- package/dist/chunks/{LpPaletteTheme-CdEEMMEF.js → LpPaletteTheme-DiLYKQiZ.js} +1 -1
- package/dist/chunks/{LpPie-Mr3EtFEd.js → LpPie-DrZepi0p.js} +1 -1
- package/dist/chunks/{LpProgress-rOlyafqp.js → LpProgress-Yr6ONG5t.js} +1 -1
- package/dist/chunks/{LpQuota-B12yijdB.js → LpQuota-Dk2MyI2Z.js} +1 -1
- package/dist/chunks/{LpSidebar-BciLMBgY.js → LpSidebar-BoNLKgh-.js} +1 -1
- package/dist/chunks/{LpSidebarNav-HYcG-ib8.js → LpSidebarNav-Bky8chrv.js} +1 -1
- package/dist/chunks/{LpSidebarRow-Di2VYz-Z.js → LpSidebarRow-BRYhNDSo.js} +1 -1
- package/dist/chunks/{LpStat-D6V6Fdfc.js → LpStat-BVf2UvEc.js} +1 -1
- package/dist/chunks/{LpTableOfContents-CraicEs4.js → LpTableOfContents-BN5zvpnx.js} +2 -2
- package/dist/chunks/{LpThemeStudio-BJDKf3_Z.js → LpThemeStudio-BpORDvH6.js} +1 -1
- package/dist/chunks/{LpThemeSwitcher-BDrIJswA.js → LpThemeSwitcher-BnEn_mvf.js} +2 -2
- package/dist/chunks/{LpUptimeBar-BtkZRwY0.js → LpUptimeBar-DqNvvMBK.js} +1 -1
- package/dist/chunks/{palette-Cvxl_1tM.js → palette-Co8tSASo.js} +1 -1
- package/dist/components/LpAppShell.vue.d.ts +20 -0
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpCodeDisplay.vue.js +1 -1
- package/dist/components/LpCommandScope.vue.js +1 -1
- package/dist/components/LpCountdown.vue.js +1 -1
- package/dist/components/LpGauge.vue.js +1 -1
- package/dist/components/LpModalNav.vue.js +1 -1
- package/dist/components/LpNumberFlow.vue.js +1 -1
- package/dist/components/LpPaletteStats.vue.js +1 -1
- package/dist/components/LpPaletteTheme.vue.js +1 -1
- package/dist/components/LpPie.vue.js +1 -1
- package/dist/components/LpProgress.vue.js +1 -1
- package/dist/components/LpQuota.vue.js +1 -1
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/LpSidebarNav.vue.js +1 -1
- package/dist/components/LpSidebarRow.vue.js +1 -1
- package/dist/components/LpStat.vue.js +1 -1
- package/dist/components/LpTableOfContents.vue.js +1 -1
- package/dist/components/LpThemeStudio.vue.js +1 -1
- package/dist/components/LpThemeSwitcher.vue.js +1 -1
- package/dist/components/LpUptimeBar.vue.js +1 -1
- package/dist/components/commandSearch.d.ts +2 -1
- package/dist/components/commandSearch.js +15 -6
- package/dist/components/format.d.ts +6 -6
- package/dist/components/quota.d.ts +1 -1
- package/dist/components/timeSpan.d.ts +1 -1
- package/dist/composables/scroll.d.ts +2 -0
- package/dist/composables/scroll.js +4 -0
- package/dist/composables/scrollMemory.d.ts +1 -0
- package/dist/composables/scrollMemory.js +111 -0
- package/dist/index.js +18 -18
- package/dist/theme/palette.d.ts +2 -1
- package/package.json +1 -1
- package/src/components/LpAppShell.vue +18 -2
- package/src/components/LpNumberFlow.vue +3 -3
- package/src/components/LpStat.vue +1 -1
- package/src/components/LpTableOfContents.vue +4 -2
- package/src/components/commandSearch.ts +17 -6
- package/src/components/format.ts +9 -9
- package/src/components/quota.ts +1 -1
- package/src/components/timeSpan.ts +1 -1
- package/src/composables/scroll.ts +5 -0
- package/src/composables/scrollMemory.ts +145 -0
- package/src/composables/useLocale.ts +1 -1
- package/src/nuxt.ts +1 -14
- package/src/theme/palette.ts +3 -3
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { watch, nextTick, onMounted, onBeforeUnmount } from "vue";
|
|
2
|
+
import { scrollMarginTop } from "./scroll.js";
|
|
3
|
+
const SETTLE_MS = 1500;
|
|
4
|
+
const DIALOG_WAIT_MS = 2e3;
|
|
5
|
+
const SETTLE_STOP = ["wheel", "touchstart", "keydown", "pointerdown"];
|
|
6
|
+
const positions = /* @__PURE__ */ new Map();
|
|
7
|
+
function addressKey() {
|
|
8
|
+
return `${location.pathname}${location.search}`;
|
|
9
|
+
}
|
|
10
|
+
function useScrollMemory(viewport, path, hash) {
|
|
11
|
+
if (typeof window === "undefined") return;
|
|
12
|
+
let current = addressKey();
|
|
13
|
+
let shown = path();
|
|
14
|
+
let travelled = false;
|
|
15
|
+
let stopHold;
|
|
16
|
+
let pending = 0;
|
|
17
|
+
const onPop = () => {
|
|
18
|
+
travelled = true;
|
|
19
|
+
};
|
|
20
|
+
window.addEventListener("popstate", onPop);
|
|
21
|
+
function anchor(value) {
|
|
22
|
+
if (!value || value === "#") return null;
|
|
23
|
+
const id = decodeURIComponent(value.replace(/^#/, ""));
|
|
24
|
+
return document.getElementById(id);
|
|
25
|
+
}
|
|
26
|
+
function reach(value, behavior) {
|
|
27
|
+
const view = viewport();
|
|
28
|
+
const target = anchor(value);
|
|
29
|
+
if (!view || !target || !view.contains(target)) return false;
|
|
30
|
+
const top = target.getBoundingClientRect().top - view.getBoundingClientRect().top + view.scrollTop - scrollMarginTop(target);
|
|
31
|
+
view.scrollTo({ top: Math.max(0, top), behavior });
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
function hold(land) {
|
|
35
|
+
stopHold?.();
|
|
36
|
+
const view = viewport();
|
|
37
|
+
const content = view?.firstElementChild;
|
|
38
|
+
land();
|
|
39
|
+
if (!content) return;
|
|
40
|
+
const watcher = new ResizeObserver(land);
|
|
41
|
+
const timer = window.setTimeout(() => stop(), SETTLE_MS);
|
|
42
|
+
const stop = () => {
|
|
43
|
+
watcher.disconnect();
|
|
44
|
+
window.clearTimeout(timer);
|
|
45
|
+
for (const name of SETTLE_STOP) window.removeEventListener(name, stop, true);
|
|
46
|
+
if (stopHold === stop) stopHold = void 0;
|
|
47
|
+
};
|
|
48
|
+
for (const name of SETTLE_STOP) window.addEventListener(name, stop, { capture: true, passive: true });
|
|
49
|
+
watcher.observe(content);
|
|
50
|
+
stopHold = stop;
|
|
51
|
+
}
|
|
52
|
+
function arrive() {
|
|
53
|
+
const back = travelled;
|
|
54
|
+
travelled = false;
|
|
55
|
+
current = addressKey();
|
|
56
|
+
shown = path();
|
|
57
|
+
const view = viewport();
|
|
58
|
+
if (!view) return;
|
|
59
|
+
const saved = positions.get(current);
|
|
60
|
+
if (back && saved !== void 0) {
|
|
61
|
+
hold(() => view.scrollTo({ top: saved, behavior: "instant" }));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (hash()) {
|
|
65
|
+
hold(() => {
|
|
66
|
+
if (!reach(hash(), "instant")) view.scrollTo({ top: 0, behavior: "instant" });
|
|
67
|
+
});
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
stopHold?.();
|
|
71
|
+
view.scrollTo({ top: 0, left: 0, behavior: "instant" });
|
|
72
|
+
}
|
|
73
|
+
function afterDialogs(run) {
|
|
74
|
+
const since = performance.now();
|
|
75
|
+
const mine = ++pending;
|
|
76
|
+
const tick = () => {
|
|
77
|
+
if (mine !== pending) return;
|
|
78
|
+
const locked = document.querySelector('[role="dialog"]') || getComputedStyle(document.body).overflow === "hidden";
|
|
79
|
+
if (locked && performance.now() - since < DIALOG_WAIT_MS) requestAnimationFrame(tick);
|
|
80
|
+
else run();
|
|
81
|
+
};
|
|
82
|
+
requestAnimationFrame(tick);
|
|
83
|
+
}
|
|
84
|
+
watch(
|
|
85
|
+
path,
|
|
86
|
+
() => {
|
|
87
|
+
const view = viewport();
|
|
88
|
+
if (view) positions.set(current, view.scrollTop);
|
|
89
|
+
void nextTick(() => requestAnimationFrame(arrive));
|
|
90
|
+
},
|
|
91
|
+
{ flush: "pre" }
|
|
92
|
+
);
|
|
93
|
+
watch(hash, (next, previous) => {
|
|
94
|
+
if (next === previous) return;
|
|
95
|
+
if (path() !== shown) return;
|
|
96
|
+
afterDialogs(() => {
|
|
97
|
+
travelled = false;
|
|
98
|
+
if (!reach(next, "smooth")) viewport()?.scrollTo({ top: 0, behavior: "smooth" });
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
onMounted(() => {
|
|
102
|
+
if (hash()) requestAnimationFrame(() => hold(() => void reach(hash(), "instant")));
|
|
103
|
+
});
|
|
104
|
+
onBeforeUnmount(() => {
|
|
105
|
+
stopHold?.();
|
|
106
|
+
window.removeEventListener("popstate", onPop);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
useScrollMemory
|
|
111
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { _ as _3 } from "./chunks/LpMapPicker-DRGgh89B.js";
|
|
|
4
4
|
import { OSM_TILES, createNominatimProvider, emptyAddress, formatAddress, project, tileUrl, unproject } from "./components/address.js";
|
|
5
5
|
import { _ as _4 } from "./chunks/LpActionBar-BP1ld7-S.js";
|
|
6
6
|
import { _ as _5 } from "./chunks/LpAlert-n5XPAZfv.js";
|
|
7
|
-
import { _ as _6 } from "./chunks/LpAppShell-
|
|
7
|
+
import { _ as _6 } from "./chunks/LpAppShell-rr5kqiK3.js";
|
|
8
8
|
import { _ as _7 } from "./chunks/LpAutocomplete-DOu7FN5P.js";
|
|
9
9
|
import { _ as _8 } from "./chunks/LpAvatar-Cg6Az3wz.js";
|
|
10
10
|
import { _ as _9 } from "./chunks/LpBadge-wUYLN4U9.js";
|
|
@@ -60,19 +60,19 @@ import { _ as _37 } from "./chunks/LpLogViewer-D25jM2_I.js";
|
|
|
60
60
|
import { countLevels, matchPositions, matchesTerm, ringWindow, stepMatch } from "./components/logSearch.js";
|
|
61
61
|
import { _ as _38 } from "./chunks/LpMediaGallery-Bkram-MO.js";
|
|
62
62
|
import { _ as _39 } from "./chunks/LpModal-BnjZ2iem.js";
|
|
63
|
-
import { _ as _40 } from "./chunks/LpModalNav-
|
|
63
|
+
import { _ as _40 } from "./chunks/LpModalNav-DQpV2kdn.js";
|
|
64
64
|
import { _ as _41 } from "./chunks/LpNotificationBell-3244LUVc.js";
|
|
65
65
|
import { _ as _42 } from "./chunks/LpNumberField-e0IBkWU1.js";
|
|
66
66
|
import { _ as _43 } from "./chunks/LpTimeField-BawSVsmt.js";
|
|
67
|
-
import { _ as _44 } from "./chunks/LpNumberFlow-
|
|
67
|
+
import { _ as _44 } from "./chunks/LpNumberFlow-Vqj0tP7B.js";
|
|
68
68
|
import { _ as _45 } from "./chunks/LpOtpInput-CeBPsM7D.js";
|
|
69
69
|
import { _ as _46 } from "./chunks/LpPagination-BIIiYHXR.js";
|
|
70
70
|
import { _ as _47 } from "./chunks/LpPasswordInput-BkHKQDhM.js";
|
|
71
|
-
import { _ as _48 } from "./chunks/LpPie-
|
|
71
|
+
import { _ as _48 } from "./chunks/LpPie-DrZepi0p.js";
|
|
72
72
|
import { _ as _49 } from "./chunks/LpPhoneInput-CETEp5W7.js";
|
|
73
73
|
import { filterCountries, flagEmoji, loadCountries, matchCountryByValue } from "./components/countries.js";
|
|
74
74
|
import { _ as _50 } from "./chunks/LpPopover-DmZ3wUtP.js";
|
|
75
|
-
import { _ as _51 } from "./chunks/LpProgress-
|
|
75
|
+
import { _ as _51 } from "./chunks/LpProgress-Yr6ONG5t.js";
|
|
76
76
|
import { _ as _52 } from "./chunks/LpRadio-CWA5Eb3M.js";
|
|
77
77
|
import { _ as _53 } from "./chunks/LpRadioGroup-ggaPL92T.js";
|
|
78
78
|
import { _ as _54 } from "./chunks/LpRollingText-B54Pje7P.js";
|
|
@@ -86,26 +86,26 @@ import { _ as _60 } from "./chunks/LpShift-CCq7dofl.js";
|
|
|
86
86
|
import { _ as _61 } from "./chunks/LpSwap-C96c0LWh.js";
|
|
87
87
|
import { _ as _62 } from "./chunks/LpHintExample-9Py0wc-L.js";
|
|
88
88
|
import { default as default11 } from "./components/LpShortcutsDialog.vue.js";
|
|
89
|
-
import { _ as _63 } from "./chunks/LpSidebar-
|
|
89
|
+
import { _ as _63 } from "./chunks/LpSidebar-BoNLKgh-.js";
|
|
90
90
|
import { _ as _64 } from "./chunks/LpSkeleton-BB6XIV-w.js";
|
|
91
91
|
import { _ as _65 } from "./chunks/LpSlider-DxYoCJJO.js";
|
|
92
92
|
import { _ as _66 } from "./chunks/LpRangeSlider-CM-0ZITZ.js";
|
|
93
93
|
import { _ as _67 } from "./chunks/LpVideoBackdrop-DS1wGv-X.js";
|
|
94
94
|
import { formatClipTime, isNativeLoop, planLoop, seamProgress, seamReached } from "./components/videoBackdrop.js";
|
|
95
95
|
import { _ as _68 } from "./chunks/LpSparkline-CExIRDb3.js";
|
|
96
|
-
import { _ as _69 } from "./chunks/LpStat-
|
|
96
|
+
import { _ as _69 } from "./chunks/LpStat-BVf2UvEc.js";
|
|
97
97
|
import { _ as _70 } from "./chunks/LpStepper-DpsocBNJ.js";
|
|
98
98
|
import { _ as _71 } from "./chunks/LpSwitch-BnXd1llc.js";
|
|
99
99
|
import { default as default12 } from "./components/LpTable.vue.js";
|
|
100
100
|
import { compareValues } from "./components/tableSort.js";
|
|
101
101
|
import { resolveRowMenu } from "./components/tableRowMenu.js";
|
|
102
|
-
import { _ as _72 } from "./chunks/LpTableOfContents-
|
|
102
|
+
import { _ as _72 } from "./chunks/LpTableOfContents-BN5zvpnx.js";
|
|
103
103
|
import { default as default13 } from "./components/LpTabs.vue.js";
|
|
104
104
|
import { _ as _73 } from "./chunks/LpBottomNav-CbNQQPuL.js";
|
|
105
105
|
import { _ as _74 } from "./chunks/LpTagsInput-C-mDG648.js";
|
|
106
106
|
import { addTags, splitTags } from "./components/tags.js";
|
|
107
107
|
import { _ as _75 } from "./chunks/LpTextarea-TE-yZv2U.js";
|
|
108
|
-
import { _ as _76 } from "./chunks/LpThemeSwitcher-
|
|
108
|
+
import { _ as _76 } from "./chunks/LpThemeSwitcher-BnEn_mvf.js";
|
|
109
109
|
import { _ as _77 } from "./chunks/LpTilt-B7KHX1zH.js";
|
|
110
110
|
import { _ as _78 } from "./chunks/LpToaster-IwTXjbbU.js";
|
|
111
111
|
import { _ as _79 } from "./chunks/LpTooltip-BcBvIx4z.js";
|
|
@@ -116,7 +116,7 @@ import { _ as _82 } from "./chunks/LpHints-Bz-BlaSl.js";
|
|
|
116
116
|
import { HINT_SIGNALS, contextMenuTip, createHintSystem, dropzoneTip, hintKey, hintSummary, hotkeyTip, localHintStorage, noteDropzone, noteHotkeyTarget, paletteTip, provideHintSystem, runningHintSystem, useHintSystem } from "./composables/useHintSystem.js";
|
|
117
117
|
import { _ as _83 } from "./chunks/LpTooltipProvider-CftjV7Xc.js";
|
|
118
118
|
import { _ as _84 } from "./chunks/LpTopologyCanvas-B1gIbS0n.js";
|
|
119
|
-
import { _ as _85 } from "./chunks/LpUptimeBar-
|
|
119
|
+
import { _ as _85 } from "./chunks/LpUptimeBar-DqNvvMBK.js";
|
|
120
120
|
import { deserializeLayout, serializeLayout } from "./layout/tree.js";
|
|
121
121
|
import { addLeaf, addTab, countBlocks, countLeaves, makeLayout, moveLeaf, removeBlock, removeLeaf, reorderTab, resizeAt, setActiveTab } from "./layout/tree.js";
|
|
122
122
|
import { blockTitle, defineBlocks } from "./layout/registry.js";
|
|
@@ -124,7 +124,7 @@ import { layoutController, useLayout } from "./layout/useLayout.js";
|
|
|
124
124
|
import { a as parseTheme, s as serializeTheme } from "./chunks/LpThemeProvider-8MG6yqU9.js";
|
|
125
125
|
import { D, _ as _86, T, b as b2, c as c2, d, e, f, g, l, h, n, i as i2, j, p, r, k, t, m, o, q, u, v } from "./chunks/LpThemeProvider-8MG6yqU9.js";
|
|
126
126
|
import { G, N, d as d2, i as i3, s } from "./chunks/backdrop-Dkf2vM81.js";
|
|
127
|
-
import { _ as _87, a as a2, b as b3, c as c3, d as d3, S, e as e2, u as u2, w } from "./chunks/LpThemeStudio-
|
|
127
|
+
import { _ as _87, a as a2, b as b3, c as c3, d as d3, S, e as e2, u as u2, w } from "./chunks/LpThemeStudio-BpORDvH6.js";
|
|
128
128
|
import { useClipboard } from "./composables/useClipboard.js";
|
|
129
129
|
import { activeShortcuts, appleModifierLabel, findHotkeyConflicts, hotkeyConflicts, hotkeySteps, isAppleKeyboard, matchesHotkey, mergeShortcutGroups, parseCombo, pendingHotkey, sequenceProgress, useAppleKeyboard, useHotkeys } from "./composables/useHotkeys.js";
|
|
130
130
|
import { anyModalOpen, topModalElement, useModalLayer } from "./components/modalDepth.js";
|
|
@@ -164,8 +164,8 @@ import { _ as _88 } from "./chunks/LpFrame-BVQCQLDI.js";
|
|
|
164
164
|
import { _ as _89 } from "./chunks/LpThemeStudioSection-B7bt8Iyg.js";
|
|
165
165
|
import { isInternalHref, resolveLink } from "./components/link.js";
|
|
166
166
|
import { _ as _90 } from "./chunks/LpRepeater-D7ZmnIYd.js";
|
|
167
|
-
import { _ as _91 } from "./chunks/LpGauge-
|
|
168
|
-
import { _ as _92 } from "./chunks/LpQuota-
|
|
167
|
+
import { _ as _91 } from "./chunks/LpGauge-KZaQ_FDR.js";
|
|
168
|
+
import { _ as _92 } from "./chunks/LpQuota-Dk2MyI2Z.js";
|
|
169
169
|
import { _ as _93 } from "./chunks/LpStatus-DglxttQ7.js";
|
|
170
170
|
import { _ as _94 } from "./chunks/LpRelativeTime-DwgCb5-8.js";
|
|
171
171
|
import { _ as _95 } from "./chunks/LpActivityLine-CJvzrNpj.js";
|
|
@@ -181,14 +181,14 @@ import { childProgress, connectorFilled, currentStepIndex, isSettled, overallSta
|
|
|
181
181
|
import { _ as _98 } from "./chunks/LpLoader-DVwkOmt9.js";
|
|
182
182
|
import { useLoadingGate } from "./composables/useLoadingGate.js";
|
|
183
183
|
import { useNow } from "./composables/useNow.js";
|
|
184
|
-
import { _ as _99 } from "./chunks/LpCountdown-
|
|
184
|
+
import { _ as _99 } from "./chunks/LpCountdown-DgeY3cLG.js";
|
|
185
185
|
import { DEFAULT_MILESTONES, DEFAULT_UNIT_LABELS, ceilToSecond, crossedMilestone, formatElapsed, formatTime, msToNextSecond, splitTime, timeParts } from "./components/timeSpan.js";
|
|
186
186
|
import { _ as _100 } from "./chunks/LpCopyButton-xF7wFVoU.js";
|
|
187
187
|
import { _ as _101 } from "./chunks/LpSecretField-BbmqM9ye.js";
|
|
188
188
|
import { _ as _102 } from "./chunks/LpDataList-Jxyl9H5w.js";
|
|
189
189
|
import { _ as _103 } from "./chunks/LpEditable-Cy6GLbFx.js";
|
|
190
190
|
import { _ as _104 } from "./chunks/LpTruncate-CflAIwiJ.js";
|
|
191
|
-
import { _ as _105 } from "./chunks/LpCodeDisplay-
|
|
191
|
+
import { _ as _105 } from "./chunks/LpCodeDisplay-CE9TxCA4.js";
|
|
192
192
|
import { formatRemaining, groupCode, middleTruncate, normalizeCode } from "./components/fieldText.js";
|
|
193
193
|
import { _ as _106 } from "./chunks/LpHoverCard-spGxyw7T.js";
|
|
194
194
|
import { _ as _107 } from "./chunks/LpIdenticon-BzdC_qz9.js";
|
|
@@ -214,10 +214,10 @@ import { _ as _116 } from "./chunks/LpSpotlight-4F3NeUMY.js";
|
|
|
214
214
|
import { _ as _117 } from "./chunks/LpGlowBorder-B8V-QzIl.js";
|
|
215
215
|
import { _ as _118 } from "./chunks/LpMagnetic-CtXKLa2Y.js";
|
|
216
216
|
import { spotlightPoint, useSpotlight } from "./composables/useSpotlight.js";
|
|
217
|
-
import { _ as _119 } from "./chunks/LpCommandScope-
|
|
217
|
+
import { _ as _119 } from "./chunks/LpCommandScope-CtUhJe8T.js";
|
|
218
218
|
import { createPaletteRegistry, mergePaletteCommands, usePaletteCommands, vPalette } from "./composables/usePaletteCommands.js";
|
|
219
|
-
import { u as u3 } from "./chunks/palette-
|
|
220
|
-
import { _ as _120 } from "./chunks/LpPaletteTheme-
|
|
219
|
+
import { u as u3 } from "./chunks/palette-Co8tSASo.js";
|
|
220
|
+
import { _ as _120 } from "./chunks/LpPaletteTheme-DiLYKQiZ.js";
|
|
221
221
|
const UI_CONFIG_VERSION = 1;
|
|
222
222
|
function serializeConfig(config) {
|
|
223
223
|
const out = {
|
package/dist/theme/palette.d.ts
CHANGED
|
@@ -3,10 +3,11 @@ import { type PaletteScope } from "../composables/usePaletteCommands";
|
|
|
3
3
|
import type { ThemeStudio } from "./studio";
|
|
4
4
|
import type { ThemeStudioPart } from "../components/LpThemeStudio.vue";
|
|
5
5
|
/** Registers the studio itself; no second list of theme options in the app. */
|
|
6
|
-
/** `hide` leaves out the same studio rows the app hides in its own studio. */
|
|
6
|
+
/** `hide` leaves out the same studio rows the app hides in its own studio; `keywords` adds the app's own search words. */
|
|
7
7
|
export declare function usePaletteTheme(studio: MaybeRefOrGetter<ThemeStudio | undefined>, options?: {
|
|
8
8
|
id?: string;
|
|
9
9
|
label?: string;
|
|
10
10
|
scope?: PaletteScope;
|
|
11
11
|
hide?: ThemeStudioPart[];
|
|
12
|
+
keywords?: string[];
|
|
12
13
|
}): void;
|
package/package.json
CHANGED
|
@@ -33,6 +33,7 @@ export default { inheritAttrs: false }
|
|
|
33
33
|
*/
|
|
34
34
|
import { computed, onMounted, ref, useId, watch } from "vue"
|
|
35
35
|
import { useLabels } from "../composables/useLpConfig"
|
|
36
|
+
import { useScrollMemory } from "../composables/scrollMemory"
|
|
36
37
|
import LpBottomNav from "./LpBottomNav.vue"
|
|
37
38
|
import LpButton from "./LpButton.vue"
|
|
38
39
|
import LpIcon from "./LpIcon.vue"
|
|
@@ -59,8 +60,18 @@ const props = withDefaults(
|
|
|
59
60
|
* The current route path. Enables the built-in prefix-match active test and
|
|
60
61
|
* the derived page title without the host wiring its own `isActive`. Pass
|
|
61
62
|
* `route.path`. Ignored when a custom `isActive` is provided.
|
|
63
|
+
*
|
|
64
|
+
* A new path also places the page in the shell's scroll region: back and
|
|
65
|
+
* forward return to where it was left, a `hash` lands on that anchor
|
|
66
|
+
* (honouring its `scroll-margin-top`), anything else starts at the top.
|
|
62
67
|
*/
|
|
63
68
|
path?: string
|
|
69
|
+
/**
|
|
70
|
+
* The current route hash (`route.hash`). With `path` it lands a page on
|
|
71
|
+
* its anchor; a new hash on the same path glides there, after a dialog
|
|
72
|
+
* that started the jump has closed.
|
|
73
|
+
*/
|
|
74
|
+
hash?: string
|
|
64
75
|
/**
|
|
65
76
|
* Custom active test (router-driven nav). Overrides `path`/`modelValue`.
|
|
66
77
|
*/
|
|
@@ -177,7 +188,8 @@ const text = useLabels(
|
|
|
177
188
|
/*
|
|
178
189
|
* The page scrolls inside the shell's own region, not the window, so the
|
|
179
190
|
* router's scroll behaviour never reaches it: without this a route change from
|
|
180
|
-
* a scrolled page landed mid-way down the next one.
|
|
191
|
+
* a scrolled page landed mid-way down the next one, and back never returned.
|
|
192
|
+
* A full-bleed page owns its scrolling, so the shell leaves it alone.
|
|
181
193
|
*/
|
|
182
194
|
const scrollArea = ref<{ viewportEl: HTMLElement | null } | null>(null)
|
|
183
195
|
const bleedId = `lp-shell-bleed-${useId()}`
|
|
@@ -195,9 +207,13 @@ watch(
|
|
|
195
207
|
() => props.path,
|
|
196
208
|
() => {
|
|
197
209
|
if (drawerOpen.value) drawerOpen.value = false
|
|
198
|
-
scrollArea.value?.viewportEl?.scrollTo({ top: 0, left: 0, behavior: "instant" })
|
|
199
210
|
},
|
|
200
211
|
)
|
|
212
|
+
useScrollMemory(
|
|
213
|
+
() => (bleeding.value ? null : scrollArea.value?.viewportEl),
|
|
214
|
+
() => props.path,
|
|
215
|
+
() => props.hash,
|
|
216
|
+
)
|
|
201
217
|
|
|
202
218
|
function onSelect(item: SidebarItem) {
|
|
203
219
|
emit("select", item)
|
|
@@ -226,7 +226,7 @@ onBeforeUnmount(stop)
|
|
|
226
226
|
|
|
227
227
|
/*
|
|
228
228
|
* The app's locale (reka's ConfigProvider) when the counter has none of its
|
|
229
|
-
* own, the same rule the dates follow: a
|
|
229
|
+
* own, the same rule the dates follow: a French app read "4,594" otherwise.
|
|
230
230
|
*/
|
|
231
231
|
const locale = useLocale(() => props.locale)
|
|
232
232
|
// The grouped and compact forms are written in the locale; the plain one always uses ".".
|
|
@@ -250,7 +250,7 @@ function formatAt(v: number, padded: boolean): string {
|
|
|
250
250
|
* The decimal count is what stays fixed: one place whenever a unit is in
|
|
251
251
|
* play, so the row's shape only changes at a rollover, not on every frame.
|
|
252
252
|
* Written in the app's locale: the cells find the fraction by that
|
|
253
|
-
* locale's own mark (fractionMark), so "1,2
|
|
253
|
+
* locale's own mark (fractionMark), so "1,2 k" keeps its columns.
|
|
254
254
|
*/
|
|
255
255
|
return compactNumber(v, { plainDecimals: 0, fixed: true, locale: locale.value, words: true })
|
|
256
256
|
}
|
|
@@ -492,7 +492,7 @@ const label = computed(
|
|
|
492
492
|
/* Faded, so a held-open column reads as an empty wheel rather than a zero the
|
|
493
493
|
number actually has. */
|
|
494
494
|
/*
|
|
495
|
-
* A space separator (
|
|
495
|
+
* A space separator (fr, pl, …) takes a whole column in a monospaced or
|
|
496
496
|
* tabular face, so "7 271" read as "7 271". It is drawn as the narrow gap
|
|
497
497
|
* the locale means.
|
|
498
498
|
*/
|
|
@@ -147,7 +147,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
147
147
|
</span>
|
|
148
148
|
</div>
|
|
149
149
|
<!-- A narrow tile moves the delta under the figure rather than breaking the
|
|
150
|
-
figure itself: "2085
|
|
150
|
+
figure itself: "2085 h" split across two lines reads as two values. -->
|
|
151
151
|
<div class="flex flex-wrap items-baseline gap-x-2">
|
|
152
152
|
<!-- The placeholder sits INSIDE the value's own type size, so its line box
|
|
153
153
|
is the real one: the tile cannot change height when the figure lands. -->
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { Motion } from "motion-v"
|
|
13
13
|
import { computed, onBeforeUnmount, onMounted, ref, useId, watch } from "vue"
|
|
14
14
|
import { useLabels } from "../composables/useLpConfig"
|
|
15
|
-
import { atScrollEnd, scrollParent } from "../composables/scroll"
|
|
15
|
+
import { atScrollEnd, scrollMarginTop, scrollParent, scrollToElement } from "../composables/scroll"
|
|
16
16
|
import { usePillTransition } from "../composables/usePillTransition"
|
|
17
17
|
|
|
18
18
|
export interface TocLink {
|
|
@@ -170,7 +170,9 @@ function onClick(id: string, ev: MouseEvent) {
|
|
|
170
170
|
ev.preventDefault()
|
|
171
171
|
activeId.value = id // mark immediately so the bar jumps to the clicked item
|
|
172
172
|
suppressSpyUntilScrollEnds(el)
|
|
173
|
-
|
|
173
|
+
// Only the nearest scrolling box moves: scrollIntoView also shifted every
|
|
174
|
+
// overflow-hidden ancestor, the shell itself among them.
|
|
175
|
+
scrollToElement(el, { block: "start", behavior: "smooth", margin: scrollMarginTop(el) })
|
|
174
176
|
// Keep the router's own state: replacing it with null broke scroll
|
|
175
177
|
// restoration and back/forward bookkeeping in vue-router apps.
|
|
176
178
|
history.replaceState(history.state, "", `#${id}`)
|
|
@@ -81,21 +81,32 @@ function rankExact<T extends SearchableCommand>(items: T[], terms: CommandSearch
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
const LATIN_KEYS = "`qwertyuiop[]asdfghjkl;'zxcvbnm,."
|
|
84
|
-
const
|
|
85
|
-
const
|
|
84
|
+
const CYRILLIC_LAYOUTS = ["ґйцукенгшщзхїфівапролджєячсмитьбю", "ёйцукенгшщзхъфывапролджэячсмитьбю"]
|
|
85
|
+
const LAYOUT_MAPS = CYRILLIC_LAYOUTS.map(keys => new Map([...LATIN_KEYS].flatMap((key, i) => [[key, keys[i]!], [keys[i]!, key]])))
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
function swapWith(value: string, keys: Map<string, string>): string {
|
|
88
88
|
return [...value].map(char => {
|
|
89
|
-
const replacement =
|
|
89
|
+
const replacement = keys.get(char.toLowerCase())
|
|
90
90
|
return replacement ? char === char.toUpperCase() ? replacement.toUpperCase() : replacement : char
|
|
91
91
|
}).join("")
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/** The query as if typed on the other layout: Latin to Ukrainian by default, `layout` 1 for the other Cyrillic one. */
|
|
95
|
+
export function swapCommandLayout(value: string, layout = 0): string {
|
|
96
|
+
return swapWith(value, LAYOUT_MAPS[layout] ?? LAYOUT_MAPS[0]!)
|
|
97
|
+
}
|
|
98
|
+
|
|
94
99
|
export function searchCommandItems<T extends SearchableCommand>(items: T[], terms: CommandSearchTerms): { items: T[]; terms: string[] } {
|
|
95
100
|
const exact = rankExact(items, terms)
|
|
96
101
|
if (exact.length || !terms.include.length) return { items: exact, terms: terms.include }
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
let fallback = terms.include
|
|
103
|
+
for (const keys of LAYOUT_MAPS) {
|
|
104
|
+
const alternate = { include: terms.include.map(term => swapWith(term, keys)), exclude: terms.exclude, groups: terms.groups }
|
|
105
|
+
const found = rankExact(items, alternate)
|
|
106
|
+
if (found.length) return { items: found, terms: alternate.include }
|
|
107
|
+
if (fallback === terms.include) fallback = alternate.include
|
|
108
|
+
}
|
|
109
|
+
return { items: [], terms: fallback }
|
|
99
110
|
}
|
|
100
111
|
|
|
101
112
|
export function rankCommandItems<T extends SearchableCommand>(items: T[], terms: CommandSearchTerms): T[] {
|
package/src/components/format.ts
CHANGED
|
@@ -35,7 +35,7 @@ export interface FormatBytesOptions {
|
|
|
35
35
|
/**
|
|
36
36
|
* A byte count in the largest unit that keeps it readable: 1 536 → "1.5 kB".
|
|
37
37
|
* Binary steps, the way file managers and storage limits show sizes. Unit
|
|
38
|
-
* names come from Intl, so a
|
|
38
|
+
* names come from Intl, so a French app reads "1,5 Mo" without a table of
|
|
39
39
|
* its own. One decimal under 10 (2.4 MB), none above (240 MB). Negative or
|
|
40
40
|
* non-finite input is "—": there is no size to show.
|
|
41
41
|
*/
|
|
@@ -54,7 +54,7 @@ export function formatBytes(bytes: number, locale?: string, options: FormatBytes
|
|
|
54
54
|
// Intl's "short" form spells the smallest unit out ("1023 byte", "1023 Byte")
|
|
55
55
|
// while every other unit is a symbol, and its "narrow" form glues English
|
|
56
56
|
// symbols to the figure ("1023B"). Narrow symbols with one no-break space
|
|
57
|
-
// read the same in every locale: "1023 B", "1.5 kB", "1,5
|
|
57
|
+
// read the same in every locale: "1023 B", "1.5 kB", "1,5 Mo".
|
|
58
58
|
const parts = numberFormat(locale, {
|
|
59
59
|
style: "unit",
|
|
60
60
|
unit: BYTE_UNITS[step],
|
|
@@ -79,8 +79,8 @@ const COMPACT_UNITS: readonly (readonly [number, string])[] = [
|
|
|
79
79
|
]
|
|
80
80
|
|
|
81
81
|
/*
|
|
82
|
-
* The unit word and the gap before it as the locale writes them: "
|
|
83
|
-
* no-break space in
|
|
82
|
+
* The unit word and the gap before it as the locale writes them: "Md" with a
|
|
83
|
+
* no-break space in French, "K" glued on in English. Taken from Intl's compact
|
|
84
84
|
* notation rather than a table, so any locale reads its own.
|
|
85
85
|
*/
|
|
86
86
|
const compactSuffixes = new Map<string, string>()
|
|
@@ -99,14 +99,14 @@ function compactSuffix(locale: string, step: number): string {
|
|
|
99
99
|
|
|
100
100
|
export interface CompactNumberOptions {
|
|
101
101
|
/**
|
|
102
|
-
* Writes the decimal separator the locale's way ("1,2k" in
|
|
102
|
+
* Writes the decimal separator the locale's way ("1,2k" in French). The
|
|
103
103
|
* k/M/B/T suffixes stay: Intl's own compact notation changes the shape of
|
|
104
|
-
* the figure per locale ("1,2
|
|
104
|
+
* the figure per locale ("1,2 k" in French), which a tick label cannot absorb.
|
|
105
105
|
* Unset keeps a plain "." whatever the browser says.
|
|
106
106
|
*/
|
|
107
107
|
locale?: string
|
|
108
108
|
/**
|
|
109
|
-
* With a locale, write the unit as that locale does: "
|
|
109
|
+
* With a locale, write the unit as that locale does: "13,1 Md" in French.
|
|
110
110
|
* For a figure read on its own (a stat tile, a counter), where "11,9k" looks
|
|
111
111
|
* foreign; not for axis ticks, where the longer word crowds the axis.
|
|
112
112
|
*/
|
|
@@ -124,7 +124,7 @@ export interface CompactNumberOptions {
|
|
|
124
124
|
|
|
125
125
|
/**
|
|
126
126
|
* A figure shortened with k/M/B/T: 1 200 → "1.2k", 3 400 000 → "3.4M"; with
|
|
127
|
-
* `words` and a locale in that locale's own words, "1
|
|
127
|
+
* `words` and a locale in that locale's own words, "13,1 Md" in French.
|
|
128
128
|
*
|
|
129
129
|
* The unit is picked by what will PRINT, not by the raw size: 999 950 at one
|
|
130
130
|
* place rounds to "1000k", which reads as the next unit up, so it becomes
|
|
@@ -163,7 +163,7 @@ export function compactNumber(value: number, options: CompactNumberOptions = {})
|
|
|
163
163
|
|
|
164
164
|
/**
|
|
165
165
|
* The character a locale writes between the whole and the fractional part:
|
|
166
|
-
* "." in English, "," in
|
|
166
|
+
* "." in English, "," in French and German. A counter that splits a grouped
|
|
167
167
|
* figure into columns has to find the fraction by this mark, not by ".",
|
|
168
168
|
* because in German "." is the thousands separator.
|
|
169
169
|
*/
|
package/src/components/quota.ts
CHANGED
|
@@ -65,7 +65,7 @@ export function quotaState(
|
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* An amount in its unit, locale-aware. Bytes go through `formatBytes`, whose
|
|
68
|
-
* unit names come from Intl, so a
|
|
68
|
+
* unit names come from Intl, so a French app reads "1,5 Go" without a
|
|
69
69
|
* translation table of its own.
|
|
70
70
|
*/
|
|
71
71
|
export function formatAmount(value: number, unit?: QuotaUnit, locale?: string): string {
|
|
@@ -19,7 +19,7 @@ export interface TimeSplit {
|
|
|
19
19
|
|
|
20
20
|
export type TimeUnit = "d" | "h" | "m" | "s"
|
|
21
21
|
|
|
22
|
-
/** Unit suffixes, so an app can write "2
|
|
22
|
+
/** Unit suffixes, so an app can write "2 j 3 h" without reformatting. */
|
|
23
23
|
export type TimeUnitLabels = Record<TimeUnit, string>
|
|
24
24
|
|
|
25
25
|
export const DEFAULT_UNIT_LABELS: TimeUnitLabels = { d: "d", h: "h", m: "m", s: "s" }
|
|
@@ -46,6 +46,11 @@ export interface ScrollToElementOptions {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/** Scroll the nearest scroller, and only it, so that `el` is in view. */
|
|
49
|
+
/** The element's own `scroll-margin-top`, the gap it asks to keep above it when scrolled to. */
|
|
50
|
+
export function scrollMarginTop(el: Element): number {
|
|
51
|
+
return Number.parseFloat(getComputedStyle(el).scrollMarginTop) || 0
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
export function scrollToElement(
|
|
50
55
|
el: Element | null | undefined,
|
|
51
56
|
{ block = "nearest", behavior = "auto", margin = 0 }: ScrollToElementOptions = {},
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { nextTick, onBeforeUnmount, onMounted, watch } from "vue"
|
|
2
|
+
import { scrollMarginTop } from "./scroll"
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Where a page lands when the shell's own scroll region changes page. The
|
|
6
|
+
* region is not the window, so a router's scrollBehavior never reaches it, and
|
|
7
|
+
* the shell has to answer the questions a browser answers for a plain site:
|
|
8
|
+
*
|
|
9
|
+
* - back and forward return to where the page was left;
|
|
10
|
+
* - an address with a hash lands on that anchor, and stays on it while the
|
|
11
|
+
* page is still growing (a lazily loaded body, images), until the reader
|
|
12
|
+
* scrolls or a moment has passed;
|
|
13
|
+
* - a new hash on the same page glides there, once a dialog that started the
|
|
14
|
+
* jump (a palette, a menu) has let go of the page;
|
|
15
|
+
* - anything else starts at the top.
|
|
16
|
+
*
|
|
17
|
+
* The router is not needed: what changed is read from the shell's `path` and
|
|
18
|
+
* `hash` props, history position from `popstate`, and positions are keyed by
|
|
19
|
+
* the address the browser shows.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const SETTLE_MS = 1500
|
|
23
|
+
const DIALOG_WAIT_MS = 2000
|
|
24
|
+
const SETTLE_STOP = ["wheel", "touchstart", "keydown", "pointerdown"] as const
|
|
25
|
+
|
|
26
|
+
const positions = new Map<string, number>()
|
|
27
|
+
|
|
28
|
+
function addressKey(): string {
|
|
29
|
+
return `${location.pathname}${location.search}`
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function useScrollMemory(
|
|
33
|
+
viewport: () => HTMLElement | null | undefined,
|
|
34
|
+
path: () => string | undefined,
|
|
35
|
+
hash: () => string | undefined,
|
|
36
|
+
): void {
|
|
37
|
+
if (typeof window === "undefined") return
|
|
38
|
+
let current = addressKey()
|
|
39
|
+
let shown = path()
|
|
40
|
+
let travelled = false
|
|
41
|
+
let stopHold: (() => void) | undefined
|
|
42
|
+
let pending = 0
|
|
43
|
+
|
|
44
|
+
const onPop = () => {
|
|
45
|
+
travelled = true
|
|
46
|
+
}
|
|
47
|
+
window.addEventListener("popstate", onPop)
|
|
48
|
+
|
|
49
|
+
function anchor(value: string | undefined): HTMLElement | null {
|
|
50
|
+
if (!value || value === "#") return null
|
|
51
|
+
const id = decodeURIComponent(value.replace(/^#/, ""))
|
|
52
|
+
return document.getElementById(id)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function reach(value: string | undefined, behavior: ScrollBehavior): boolean {
|
|
56
|
+
const view = viewport()
|
|
57
|
+
const target = anchor(value)
|
|
58
|
+
if (!view || !target || !view.contains(target)) return false
|
|
59
|
+
const top = target.getBoundingClientRect().top - view.getBoundingClientRect().top + view.scrollTop - scrollMarginTop(target)
|
|
60
|
+
view.scrollTo({ top: Math.max(0, top), behavior })
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Re-applies a landing while the page below it keeps changing size.
|
|
65
|
+
function hold(land: () => void) {
|
|
66
|
+
stopHold?.()
|
|
67
|
+
const view = viewport()
|
|
68
|
+
const content = view?.firstElementChild
|
|
69
|
+
land()
|
|
70
|
+
if (!content) return
|
|
71
|
+
const watcher = new ResizeObserver(land)
|
|
72
|
+
const timer = window.setTimeout(() => stop(), SETTLE_MS)
|
|
73
|
+
const stop = () => {
|
|
74
|
+
watcher.disconnect()
|
|
75
|
+
window.clearTimeout(timer)
|
|
76
|
+
for (const name of SETTLE_STOP) window.removeEventListener(name, stop, true)
|
|
77
|
+
if (stopHold === stop) stopHold = undefined
|
|
78
|
+
}
|
|
79
|
+
for (const name of SETTLE_STOP) window.addEventListener(name, stop, { capture: true, passive: true })
|
|
80
|
+
watcher.observe(content)
|
|
81
|
+
stopHold = stop
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function arrive() {
|
|
85
|
+
const back = travelled
|
|
86
|
+
travelled = false
|
|
87
|
+
current = addressKey()
|
|
88
|
+
shown = path()
|
|
89
|
+
const view = viewport()
|
|
90
|
+
if (!view) return
|
|
91
|
+
const saved = positions.get(current)
|
|
92
|
+
if (back && saved !== undefined) {
|
|
93
|
+
hold(() => view.scrollTo({ top: saved, behavior: "instant" }))
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
if (hash()) {
|
|
97
|
+
hold(() => {
|
|
98
|
+
if (!reach(hash(), "instant")) view.scrollTo({ top: 0, behavior: "instant" })
|
|
99
|
+
})
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
stopHold?.()
|
|
103
|
+
view.scrollTo({ top: 0, left: 0, behavior: "instant" })
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function afterDialogs(run: () => void) {
|
|
107
|
+
const since = performance.now()
|
|
108
|
+
const mine = ++pending
|
|
109
|
+
const tick = () => {
|
|
110
|
+
if (mine !== pending) return
|
|
111
|
+
const locked = document.querySelector('[role="dialog"]') || getComputedStyle(document.body).overflow === "hidden"
|
|
112
|
+
if (locked && performance.now() - since < DIALOG_WAIT_MS) requestAnimationFrame(tick)
|
|
113
|
+
else run()
|
|
114
|
+
}
|
|
115
|
+
requestAnimationFrame(tick)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
watch(
|
|
119
|
+
path,
|
|
120
|
+
() => {
|
|
121
|
+
const view = viewport()
|
|
122
|
+
if (view) positions.set(current, view.scrollTop)
|
|
123
|
+
void nextTick(() => requestAnimationFrame(arrive))
|
|
124
|
+
},
|
|
125
|
+
{ flush: "pre" },
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
watch(hash, (next, previous) => {
|
|
129
|
+
if (next === previous) return
|
|
130
|
+
if (path() !== shown) return
|
|
131
|
+
afterDialogs(() => {
|
|
132
|
+
travelled = false
|
|
133
|
+
if (!reach(next, "smooth")) viewport()?.scrollTo({ top: 0, behavior: "smooth" })
|
|
134
|
+
})
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
onMounted(() => {
|
|
138
|
+
if (hash()) requestAnimationFrame(() => hold(() => void reach(hash(), "instant")))
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
onBeforeUnmount(() => {
|
|
142
|
+
stopHold?.()
|
|
143
|
+
window.removeEventListener("popstate", onPop)
|
|
144
|
+
})
|
|
145
|
+
}
|