@jskit-ai/shell-web 0.1.250 → 0.1.252
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/fixtures/adaptive-shell/src/main.js +6 -3
- package/package.json +2 -2
- package/patterns/application-shell/PATTERN.md +18 -4
- package/patterns/application-shell/example/src/pages/home/index.vue +15 -5
- package/patterns/application-shell/example/tests/e2e/adaptive-shell.spec.ts +1 -1
- package/src/architecture/inspectVueArchitecture.js +1092 -336
- package/src/client/asyncModuleRecovery/runtime.js +116 -0
- package/src/client/components/ShellLayout.vue +18 -288
- package/src/client/components/ShellMenuLinkItem.vue +3 -21
- package/src/client/components/ShellOutlet.vue +2 -24
- package/src/client/components/ShellRouteTransition.vue +16 -114
- package/src/client/components/ShellSurfaceAwareMenuLinkItem.vue +5 -100
- package/src/client/components/ShellTabLinkItem.vue +5 -101
- package/src/client/composables/useShellNavigationLink.js +85 -0
- package/src/client/composables/useShellPullToRefresh.js +260 -0
- package/src/client/error/index.js +1 -1
- package/src/client/error/inject.js +4 -1
- package/src/client/error/policy.js +4 -13
- package/src/client/error/presenters.js +4 -9
- package/src/client/error/store.js +1 -1
- package/src/client/navigation/linkResolver.js +5 -27
- package/src/client/navigation/usePaths.js +1 -21
- package/src/client/placement/index.js +2 -0
- package/src/client/placement/inject.js +4 -1
- package/src/client/placement/runtime.js +2 -4
- package/src/client/placement/surfaceContext.js +11 -8
- package/src/client/placement/validators.js +18 -65
- package/src/client/providers/ShellWebClientProvider.js +100 -356
- package/src/client/requestRecovery/runtime.js +29 -77
- package/src/client/runtime/bootstrapRuntime.js +5 -4
- package/src/client/runtime/refreshRuntime.js +122 -0
- package/src/client/support/gestureTargets.js +26 -0
- package/src/client/support/menuLinkTarget.js +35 -19
- package/src/client/support/routeParams.js +26 -0
- package/src/client/support/routeSwipeGesture.js +88 -0
- package/src/client/support/routeTransitionKey.js +1 -3
- package/src/client/support/shellLayoutClass.js +32 -0
- package/test/bootstrapRuntime.test.js +98 -0
- package/test/errorActions.test.js +2 -1
- package/test/errorRuntime.test.js +30 -0
- package/test/linkResolver.test.js +23 -1
- package/test/menuLinkTarget.test.js +77 -0
- package/test/routeSwipeGesture.test.js +49 -0
- package/test/settingsPlacementContract.test.js +16 -6
- package/test/shellGestureSupport.test.js +46 -0
- package/test/surfaceContext.test.js +53 -0
- package/test/useShellPullToRefresh.test.js +123 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createAsyncModuleRecoveryState,
|
|
3
|
+
guardedReloadApp,
|
|
4
|
+
installAsyncModuleRecoveryHandlers,
|
|
5
|
+
isDynamicImportError,
|
|
6
|
+
notifyAsyncModuleLoadError
|
|
7
|
+
} from "@jskit-ai/kernel/client/asyncModuleRecovery";
|
|
8
|
+
import { createProviderLogger as createSharedProviderLogger } from "@jskit-ai/kernel/shared/support/providerLogger";
|
|
9
|
+
import { APP_RELOAD_FAILURE_MESSAGE } from "../requestRecovery/runtime.js";
|
|
10
|
+
|
|
11
|
+
function createShellAsyncModuleRecoveryRuntime({
|
|
12
|
+
router = null,
|
|
13
|
+
errorRuntime = null,
|
|
14
|
+
logger = null
|
|
15
|
+
} = {}) {
|
|
16
|
+
const runtimeLogger = createSharedProviderLogger(logger);
|
|
17
|
+
const state = createAsyncModuleRecoveryState();
|
|
18
|
+
let installedRecovery = null;
|
|
19
|
+
|
|
20
|
+
function report(nextState = state) {
|
|
21
|
+
if (!errorRuntime || typeof errorRuntime.report !== "function") {
|
|
22
|
+
runtimeLogger.warn(
|
|
23
|
+
{
|
|
24
|
+
label: String(nextState?.label || "")
|
|
25
|
+
},
|
|
26
|
+
"Async module recovery could not report because the shell error runtime is unavailable."
|
|
27
|
+
);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
return errorRuntime.report({
|
|
33
|
+
source: "shell-web.async-module-recovery",
|
|
34
|
+
message: String(nextState?.message || "A required app module could not load."),
|
|
35
|
+
cause: nextState?.error || null,
|
|
36
|
+
intent: "app-recoverable",
|
|
37
|
+
severity: "warning",
|
|
38
|
+
dedupeKey: `shell-web.async-module-recovery.${Number(nextState?.attempt || 0)}`,
|
|
39
|
+
action: {
|
|
40
|
+
label: "Reload",
|
|
41
|
+
dismissOnRun: true,
|
|
42
|
+
handler() {
|
|
43
|
+
return reload();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
} catch (error) {
|
|
48
|
+
runtimeLogger.error(
|
|
49
|
+
{
|
|
50
|
+
label: String(nextState?.label || ""),
|
|
51
|
+
error: String(error?.message || error || "unknown error")
|
|
52
|
+
},
|
|
53
|
+
"Async module recovery could not report through the shell error runtime."
|
|
54
|
+
);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function reload() {
|
|
60
|
+
const reloaded = await guardedReloadApp({
|
|
61
|
+
state,
|
|
62
|
+
label: "App",
|
|
63
|
+
message: APP_RELOAD_FAILURE_MESSAGE
|
|
64
|
+
});
|
|
65
|
+
if (!reloaded) {
|
|
66
|
+
report(state);
|
|
67
|
+
}
|
|
68
|
+
return reloaded;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function notify(error = null, {
|
|
72
|
+
label = "App module",
|
|
73
|
+
message = "",
|
|
74
|
+
stale = isDynamicImportError(error)
|
|
75
|
+
} = {}) {
|
|
76
|
+
notifyAsyncModuleLoadError(state, error, {
|
|
77
|
+
label,
|
|
78
|
+
message,
|
|
79
|
+
retry: state.retry,
|
|
80
|
+
stale
|
|
81
|
+
});
|
|
82
|
+
return report(state);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function install() {
|
|
86
|
+
if (installedRecovery) {
|
|
87
|
+
return installedRecovery;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
installedRecovery = installAsyncModuleRecoveryHandlers({
|
|
91
|
+
state,
|
|
92
|
+
label: "App module",
|
|
93
|
+
router,
|
|
94
|
+
onNotify: report
|
|
95
|
+
});
|
|
96
|
+
return installedRecovery;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function dispose() {
|
|
100
|
+
installedRecovery?.dispose?.();
|
|
101
|
+
installedRecovery = null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return Object.freeze({
|
|
105
|
+
dispose,
|
|
106
|
+
install,
|
|
107
|
+
notify,
|
|
108
|
+
reload,
|
|
109
|
+
report,
|
|
110
|
+
state
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
createShellAsyncModuleRecoveryRuntime
|
|
116
|
+
};
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "vue";
|
|
11
11
|
import { useDisplay } from "vuetify";
|
|
12
12
|
import { useShellLayoutState } from "../composables/useShellLayoutState.js";
|
|
13
|
+
import { useShellPullToRefresh } from "../composables/useShellPullToRefresh.js";
|
|
13
14
|
import {
|
|
14
15
|
resolveShellDrawerPresentation,
|
|
15
16
|
resolveShellDrawerToggleLabel
|
|
@@ -23,6 +24,8 @@ import {
|
|
|
23
24
|
normalizeShellRailWidth,
|
|
24
25
|
resolveContentAwareDrawerWidth
|
|
25
26
|
} from "../support/drawerWidth.js";
|
|
27
|
+
import { readViewportWidth, resolveShellLayoutClass } from "../support/shellLayoutClass.js";
|
|
28
|
+
import { SHELL_WEB_REFRESH_RUNTIME_KEY } from "../runtime/refreshRuntime.js";
|
|
26
29
|
import ShellOutlet from "./ShellOutlet.vue";
|
|
27
30
|
import ShellRouteTransition from "./ShellRouteTransition.vue";
|
|
28
31
|
|
|
@@ -74,32 +77,24 @@ const {
|
|
|
74
77
|
resolvedSurfaceLabel
|
|
75
78
|
} = useShellLayoutState(props);
|
|
76
79
|
const display = useDisplay();
|
|
77
|
-
const refreshRuntime = inject(
|
|
78
|
-
const pullDistance = ref(0);
|
|
79
|
-
const pullRefreshing = ref(false);
|
|
80
|
+
const refreshRuntime = inject(SHELL_WEB_REFRESH_RUNTIME_KEY, null);
|
|
80
81
|
const wideDrawerOpen = ref(Boolean(drawerDefaultOpen.value));
|
|
81
82
|
const navigationToggle = ref(null);
|
|
82
83
|
const navigationDrawer = ref(null);
|
|
83
84
|
const measuredDrawerWidth = ref(DEFAULT_SHELL_DRAWER_WIDTH);
|
|
84
|
-
let activePull = null;
|
|
85
85
|
let drawerContentObserver = null;
|
|
86
86
|
let drawerMeasurementFrame = null;
|
|
87
87
|
let shellMounted = false;
|
|
88
88
|
|
|
89
|
-
const
|
|
90
|
-
const PULL_REFRESH_MAX_DISTANCE = 112;
|
|
91
|
-
|
|
92
|
-
const layoutClass = computed(() => {
|
|
93
|
-
const displayName = String(display?.name?.value || "").trim().toLowerCase();
|
|
94
|
-
if (displayName === "xs" || displayName === "sm") {
|
|
95
|
-
return "compact";
|
|
96
|
-
}
|
|
97
|
-
if (displayName === "md") {
|
|
98
|
-
return "medium";
|
|
99
|
-
}
|
|
100
|
-
return "expanded";
|
|
101
|
-
});
|
|
89
|
+
const layoutClass = computed(() => resolveShellLayoutClass(display?.name?.value, readViewportWidth()));
|
|
102
90
|
const isCompactLayout = computed(() => layoutClass.value === "compact");
|
|
91
|
+
const {
|
|
92
|
+
pullRefreshing,
|
|
93
|
+
pullProgress,
|
|
94
|
+
pullIndicatorVisible,
|
|
95
|
+
pullRefreshLabel,
|
|
96
|
+
pullRefreshStyle
|
|
97
|
+
} = useShellPullToRefresh({ enabled: isCompactLayout, refreshRuntime });
|
|
103
98
|
const drawerPresentation = computed(() => resolveShellDrawerPresentation({
|
|
104
99
|
compact: isCompactLayout.value,
|
|
105
100
|
open: drawerOpen.value,
|
|
@@ -109,32 +104,14 @@ const drawerToggleLabel = computed(() => resolveShellDrawerToggleLabel({
|
|
|
109
104
|
compact: isCompactLayout.value,
|
|
110
105
|
open: drawerOpen.value
|
|
111
106
|
}));
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return normalizeShellDrawerWidth(measuredDrawerWidth.value);
|
|
117
|
-
});
|
|
107
|
+
const fixedDrawerWidth = computed(() => props.drawerWidth !== null && props.drawerWidth !== undefined);
|
|
108
|
+
const resolvedDrawerWidth = computed(() =>
|
|
109
|
+
normalizeShellDrawerWidth(fixedDrawerWidth.value ? props.drawerWidth : measuredDrawerWidth.value)
|
|
110
|
+
);
|
|
118
111
|
const resolvedRailWidth = computed(() => normalizeShellRailWidth(props.railWidth));
|
|
119
112
|
const resolvedNavigationItemSpacing = computed(() =>
|
|
120
113
|
normalizeShellNavigationItemSpacing(props.navigationItemSpacing)
|
|
121
114
|
);
|
|
122
|
-
const pullProgress = computed(() =>
|
|
123
|
-
Math.min(100, Math.round((pullDistance.value / PULL_REFRESH_TRIGGER_DISTANCE) * 100))
|
|
124
|
-
);
|
|
125
|
-
const pullIndicatorVisible = computed(() =>
|
|
126
|
-
Boolean(isCompactLayout.value && (pullDistance.value > 0 || pullRefreshing.value))
|
|
127
|
-
);
|
|
128
|
-
const pullRefreshLabel = computed(() => {
|
|
129
|
-
if (pullRefreshing.value) {
|
|
130
|
-
return "Refreshing";
|
|
131
|
-
}
|
|
132
|
-
return pullProgress.value >= 100 ? "Release to refresh" : "Pull to refresh";
|
|
133
|
-
});
|
|
134
|
-
const pullRefreshStyle = computed(() => ({
|
|
135
|
-
"--shell-pull-refresh-distance": `${Math.round(Math.min(pullDistance.value, PULL_REFRESH_MAX_DISTANCE))}px`
|
|
136
|
-
}));
|
|
137
|
-
|
|
138
115
|
watch(
|
|
139
116
|
drawerOpen,
|
|
140
117
|
handleDrawerOpenChange
|
|
@@ -161,14 +138,6 @@ function attachShellListeners() {
|
|
|
161
138
|
|
|
162
139
|
window.addEventListener("keydown", handleShellKeydown);
|
|
163
140
|
window.addEventListener("resize", scheduleDrawerWidthMeasurement, { passive: true });
|
|
164
|
-
window.addEventListener("pointerdown", handlePullPointerDown, { capture: true, passive: true });
|
|
165
|
-
window.addEventListener("pointermove", handlePullPointerMove, { capture: true, passive: false });
|
|
166
|
-
window.addEventListener("pointerup", handlePullPointerEnd, { capture: true, passive: true });
|
|
167
|
-
window.addEventListener("pointercancel", handlePullPointerCancel, { capture: true, passive: true });
|
|
168
|
-
window.addEventListener("touchstart", handlePullTouchStart, { capture: true, passive: true });
|
|
169
|
-
window.addEventListener("touchmove", handlePullTouchMove, { capture: true, passive: false });
|
|
170
|
-
window.addEventListener("touchend", handlePullTouchEnd, { capture: true, passive: true });
|
|
171
|
-
window.addEventListener("touchcancel", handlePullTouchCancel, { capture: true, passive: true });
|
|
172
141
|
|
|
173
142
|
if (document?.fonts) {
|
|
174
143
|
void document.fonts.ready.then(scheduleDrawerWidthMeasurement);
|
|
@@ -185,14 +154,6 @@ function detachShellListeners() {
|
|
|
185
154
|
|
|
186
155
|
window.removeEventListener("keydown", handleShellKeydown);
|
|
187
156
|
window.removeEventListener("resize", scheduleDrawerWidthMeasurement);
|
|
188
|
-
window.removeEventListener("pointerdown", handlePullPointerDown, { capture: true });
|
|
189
|
-
window.removeEventListener("pointermove", handlePullPointerMove, { capture: true });
|
|
190
|
-
window.removeEventListener("pointerup", handlePullPointerEnd, { capture: true });
|
|
191
|
-
window.removeEventListener("pointercancel", handlePullPointerCancel, { capture: true });
|
|
192
|
-
window.removeEventListener("touchstart", handlePullTouchStart, { capture: true });
|
|
193
|
-
window.removeEventListener("touchmove", handlePullTouchMove, { capture: true });
|
|
194
|
-
window.removeEventListener("touchend", handlePullTouchEnd, { capture: true });
|
|
195
|
-
window.removeEventListener("touchcancel", handlePullTouchCancel, { capture: true });
|
|
196
157
|
document?.fonts?.removeEventListener?.("loadingdone", scheduleDrawerWidthMeasurement);
|
|
197
158
|
drawerContentObserver?.disconnect();
|
|
198
159
|
drawerContentObserver = null;
|
|
@@ -261,7 +222,7 @@ function resolveNavigationDrawerElement() {
|
|
|
261
222
|
function scheduleDrawerWidthMeasurement() {
|
|
262
223
|
if (
|
|
263
224
|
!shellMounted ||
|
|
264
|
-
|
|
225
|
+
fixedDrawerWidth.value ||
|
|
265
226
|
!drawerPresentation.value.visible ||
|
|
266
227
|
drawerPresentation.value.rail ||
|
|
267
228
|
typeof window !== "object"
|
|
@@ -323,242 +284,11 @@ function measureRenderedText(element) {
|
|
|
323
284
|
return rect;
|
|
324
285
|
}
|
|
325
286
|
|
|
326
|
-
function handlePullPointerDown(event) {
|
|
327
|
-
if (!canStartPullRefresh(event)) {
|
|
328
|
-
activePull = null;
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
activePull = {
|
|
333
|
-
pointerId: event.pointerId,
|
|
334
|
-
touchIdentifier: null,
|
|
335
|
-
startX: event.clientX,
|
|
336
|
-
startY: event.clientY,
|
|
337
|
-
pointerCancelled: false
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
|
|
341
287
|
function handleDrawerVisibilityChange(open) {
|
|
342
288
|
if (isCompactLayout.value || drawerPresentation.value.closedMode === "hidden" || open === false) {
|
|
343
289
|
setDrawerOpen(open);
|
|
344
290
|
}
|
|
345
291
|
}
|
|
346
|
-
|
|
347
|
-
function handlePullPointerMove(event) {
|
|
348
|
-
if (!activePull || event.pointerId !== activePull.pointerId) {
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
updatePullGesture(event.clientX, event.clientY, event);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function handlePullPointerEnd(event) {
|
|
356
|
-
if (!activePull || event.pointerId !== activePull.pointerId) {
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
finishPullGesture();
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
function handlePullPointerCancel(event) {
|
|
364
|
-
if (!activePull || event.pointerId !== activePull.pointerId) {
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
activePull.pointerId = null;
|
|
369
|
-
activePull.pointerCancelled = true;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
function handlePullTouchStart(event) {
|
|
373
|
-
if (activePull || !canStartTouchPullRefresh(event)) {
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
const touch = event.touches?.[0] || null;
|
|
378
|
-
if (!touch) {
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
activePull = {
|
|
383
|
-
pointerId: null,
|
|
384
|
-
touchIdentifier: touch.identifier,
|
|
385
|
-
startX: touch.clientX,
|
|
386
|
-
startY: touch.clientY,
|
|
387
|
-
pointerCancelled: false
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
function handlePullTouchMove(event) {
|
|
392
|
-
const touch = findActiveTouch(event.touches);
|
|
393
|
-
if (!activePull || !touch) {
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
updatePullGesture(touch.clientX, touch.clientY, event);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
function handlePullTouchEnd(event) {
|
|
401
|
-
if (!activePull || !touchListIncludesActiveTouch(event.changedTouches)) {
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
finishPullGesture();
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
function handlePullTouchCancel(event) {
|
|
409
|
-
if (activePull && touchListIncludesActiveTouch(event.changedTouches)) {
|
|
410
|
-
cancelPullRefresh();
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
function updatePullGesture(clientX, clientY, event) {
|
|
415
|
-
if (!activePull) {
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
const deltaX = clientX - activePull.startX;
|
|
420
|
-
const deltaY = clientY - activePull.startY;
|
|
421
|
-
const absX = Math.abs(deltaX);
|
|
422
|
-
|
|
423
|
-
if (deltaY < -4 || (absX > 24 && absX > deltaY * 1.15)) {
|
|
424
|
-
cancelPullRefresh();
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
if (deltaY <= 6 || !isAtPageTop()) {
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
if (event?.cancelable) {
|
|
433
|
-
event.preventDefault();
|
|
434
|
-
}
|
|
435
|
-
pullDistance.value = Math.min(PULL_REFRESH_MAX_DISTANCE, Math.round(deltaY * 0.55));
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
function finishPullGesture() {
|
|
439
|
-
const shouldRefresh = pullDistance.value >= PULL_REFRESH_TRIGGER_DISTANCE;
|
|
440
|
-
activePull = null;
|
|
441
|
-
|
|
442
|
-
if (!shouldRefresh) {
|
|
443
|
-
pullDistance.value = 0;
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
void refreshFromPullGesture();
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
function cancelPullRefresh() {
|
|
451
|
-
activePull = null;
|
|
452
|
-
if (!pullRefreshing.value) {
|
|
453
|
-
pullDistance.value = 0;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
async function refreshFromPullGesture() {
|
|
458
|
-
if (!refreshRuntime || typeof refreshRuntime.refresh !== "function" || pullRefreshing.value) {
|
|
459
|
-
pullDistance.value = 0;
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
pullRefreshing.value = true;
|
|
464
|
-
pullDistance.value = PULL_REFRESH_TRIGGER_DISTANCE;
|
|
465
|
-
try {
|
|
466
|
-
await refreshRuntime.refresh("pull-to-refresh");
|
|
467
|
-
} finally {
|
|
468
|
-
pullRefreshing.value = false;
|
|
469
|
-
pullDistance.value = 0;
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
function canStartPullRefresh(event) {
|
|
474
|
-
return Boolean(
|
|
475
|
-
canStartPullRefreshFromTarget(event.target) &&
|
|
476
|
-
isPrimaryTouchPointer(event)
|
|
477
|
-
);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
function canStartTouchPullRefresh(event) {
|
|
481
|
-
return Boolean(
|
|
482
|
-
event?.touches?.length === 1 &&
|
|
483
|
-
canStartPullRefreshFromTarget(event.target)
|
|
484
|
-
);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
function canStartPullRefreshFromTarget(target) {
|
|
488
|
-
return Boolean(
|
|
489
|
-
isCompactLayout.value &&
|
|
490
|
-
refreshRuntime &&
|
|
491
|
-
typeof refreshRuntime.refresh === "function" &&
|
|
492
|
-
!pullRefreshing.value &&
|
|
493
|
-
isAtPageTop() &&
|
|
494
|
-
!isPullRefreshIgnoredTarget(target)
|
|
495
|
-
);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
function isPrimaryTouchPointer(event) {
|
|
499
|
-
return event?.isPrimary !== false && event?.button === 0 && event?.pointerType !== "mouse";
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
function isAtPageTop() {
|
|
503
|
-
if (typeof window !== "object" || typeof document !== "object") {
|
|
504
|
-
return false;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
const documentScrollTop = Number(document.documentElement?.scrollTop || 0);
|
|
508
|
-
const bodyScrollTop = Number(document.body?.scrollTop || 0);
|
|
509
|
-
return Math.max(Number(window.scrollY || 0), documentScrollTop, bodyScrollTop) <= 0;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
function isPullRefreshIgnoredTarget(target) {
|
|
513
|
-
return Boolean(
|
|
514
|
-
target?.closest?.(
|
|
515
|
-
[
|
|
516
|
-
"a",
|
|
517
|
-
"button",
|
|
518
|
-
"input",
|
|
519
|
-
"select",
|
|
520
|
-
"textarea",
|
|
521
|
-
"summary",
|
|
522
|
-
"[role='button']",
|
|
523
|
-
"[role='link']",
|
|
524
|
-
"[role='slider']",
|
|
525
|
-
"[contenteditable='true']",
|
|
526
|
-
"[data-shell-pull-refresh-ignore]",
|
|
527
|
-
"[data-shell-swipe-ignore]"
|
|
528
|
-
].join(",")
|
|
529
|
-
)
|
|
530
|
-
);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
function findActiveTouch(touchList) {
|
|
534
|
-
if (!activePull || !touchList || touchList.length < 1) {
|
|
535
|
-
return null;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
if (activePull.touchIdentifier === null && touchList.length === 1) {
|
|
539
|
-
return touchList[0];
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
for (const touch of touchList) {
|
|
543
|
-
if (touch.identifier === activePull.touchIdentifier) {
|
|
544
|
-
return touch;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
return null;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
function touchListIncludesActiveTouch(touchList) {
|
|
552
|
-
if (!activePull) {
|
|
553
|
-
return false;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
if (activePull.touchIdentifier === null) {
|
|
557
|
-
return !touchList || touchList.length <= 1;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
return Boolean(findActiveTouch(touchList));
|
|
561
|
-
}
|
|
562
292
|
</script>
|
|
563
293
|
|
|
564
294
|
<template>
|
|
@@ -622,7 +352,7 @@ function touchListIncludesActiveTouch(touchList) {
|
|
|
622
352
|
data-testid="jskit-shell-drawer"
|
|
623
353
|
:data-layout="layoutClass"
|
|
624
354
|
:data-presentation="drawerPresentation.kind"
|
|
625
|
-
:data-drawer-width-mode="
|
|
355
|
+
:data-drawer-width-mode="fixedDrawerWidth ? 'fixed' : 'content'"
|
|
626
356
|
:data-drawer-width="resolvedDrawerWidth"
|
|
627
357
|
:data-navigation-item-spacing="resolvedNavigationItemSpacing"
|
|
628
358
|
:data-rail-width="resolvedRailWidth"
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed } from "vue";
|
|
3
|
-
import {
|
|
4
|
-
resolveSurfaceNavigationTargetFromPlacementContext,
|
|
5
|
-
useWebPlacementContext
|
|
6
|
-
} from "../placement/index.js";
|
|
3
|
+
import { useWebPlacementContext } from "../placement/index.js";
|
|
7
4
|
import { resolveMenuLinkIcon } from "../lib/menuIcons.js";
|
|
5
|
+
import { resolveMenuLinkHref } from "../support/menuLinkTarget.js";
|
|
8
6
|
import { activateShellNavigationLinkOnSpace } from "../support/navigationLinkKeyboard.js";
|
|
9
7
|
import ShellNavigationTooltip from "./ShellNavigationTooltip.vue";
|
|
10
8
|
|
|
@@ -33,23 +31,7 @@ const props = defineProps({
|
|
|
33
31
|
|
|
34
32
|
const { context: placementContext } = useWebPlacementContext();
|
|
35
33
|
|
|
36
|
-
const resolvedTarget = computed(() =>
|
|
37
|
-
const target = String(props.to || "").trim();
|
|
38
|
-
if (!target) {
|
|
39
|
-
return {
|
|
40
|
-
href: "",
|
|
41
|
-
sameOrigin: true
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const navigationTarget = resolveSurfaceNavigationTargetFromPlacementContext(placementContext.value, {
|
|
46
|
-
path: target
|
|
47
|
-
});
|
|
48
|
-
return {
|
|
49
|
-
href: navigationTarget.href,
|
|
50
|
-
sameOrigin: navigationTarget.sameOrigin
|
|
51
|
-
};
|
|
52
|
-
});
|
|
34
|
+
const resolvedTarget = computed(() => resolveMenuLinkHref(placementContext.value, props.to));
|
|
53
35
|
|
|
54
36
|
const resolvedIcon = computed(() =>
|
|
55
37
|
resolveMenuLinkIcon({
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
readPlacementSurfaceConfig,
|
|
14
14
|
resolveSurfaceIdFromPlacementPathname
|
|
15
15
|
} from "../placement/surfaceContext.js";
|
|
16
|
+
import { readViewportWidth, resolveShellLayoutClass } from "../support/shellLayoutClass.js";
|
|
16
17
|
|
|
17
18
|
const props = defineProps({
|
|
18
19
|
target: {
|
|
@@ -86,30 +87,7 @@ const resolvedTargetId = computed(() => {
|
|
|
86
87
|
return String(props.target || "").trim();
|
|
87
88
|
});
|
|
88
89
|
|
|
89
|
-
const resolvedLayoutClass = computed(() =>
|
|
90
|
-
const displayName = String(display?.name?.value || "").trim().toLowerCase();
|
|
91
|
-
if (displayName === "xs" || displayName === "sm") {
|
|
92
|
-
return "compact";
|
|
93
|
-
}
|
|
94
|
-
if (displayName === "md") {
|
|
95
|
-
return "medium";
|
|
96
|
-
}
|
|
97
|
-
if (displayName === "lg" || displayName === "xl" || displayName === "xxl") {
|
|
98
|
-
return "expanded";
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const viewportWidth =
|
|
102
|
-
typeof window === "object" && window?.innerWidth
|
|
103
|
-
? Number(window.innerWidth)
|
|
104
|
-
: 0;
|
|
105
|
-
if (viewportWidth > 0 && viewportWidth < 600) {
|
|
106
|
-
return "compact";
|
|
107
|
-
}
|
|
108
|
-
if (viewportWidth > 0 && viewportWidth < 1280) {
|
|
109
|
-
return "medium";
|
|
110
|
-
}
|
|
111
|
-
return "expanded";
|
|
112
|
-
});
|
|
90
|
+
const resolvedLayoutClass = computed(() => resolveShellLayoutClass(display?.name?.value, readViewportWidth()));
|
|
113
91
|
|
|
114
92
|
const placements = computed(() => {
|
|
115
93
|
void revision.value;
|