@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.
Files changed (47) hide show
  1. package/fixtures/adaptive-shell/src/main.js +6 -3
  2. package/package.json +2 -2
  3. package/patterns/application-shell/PATTERN.md +18 -4
  4. package/patterns/application-shell/example/src/pages/home/index.vue +15 -5
  5. package/patterns/application-shell/example/tests/e2e/adaptive-shell.spec.ts +1 -1
  6. package/src/architecture/inspectVueArchitecture.js +1092 -336
  7. package/src/client/asyncModuleRecovery/runtime.js +116 -0
  8. package/src/client/components/ShellLayout.vue +18 -288
  9. package/src/client/components/ShellMenuLinkItem.vue +3 -21
  10. package/src/client/components/ShellOutlet.vue +2 -24
  11. package/src/client/components/ShellRouteTransition.vue +16 -114
  12. package/src/client/components/ShellSurfaceAwareMenuLinkItem.vue +5 -100
  13. package/src/client/components/ShellTabLinkItem.vue +5 -101
  14. package/src/client/composables/useShellNavigationLink.js +85 -0
  15. package/src/client/composables/useShellPullToRefresh.js +260 -0
  16. package/src/client/error/index.js +1 -1
  17. package/src/client/error/inject.js +4 -1
  18. package/src/client/error/policy.js +4 -13
  19. package/src/client/error/presenters.js +4 -9
  20. package/src/client/error/store.js +1 -1
  21. package/src/client/navigation/linkResolver.js +5 -27
  22. package/src/client/navigation/usePaths.js +1 -21
  23. package/src/client/placement/index.js +2 -0
  24. package/src/client/placement/inject.js +4 -1
  25. package/src/client/placement/runtime.js +2 -4
  26. package/src/client/placement/surfaceContext.js +11 -8
  27. package/src/client/placement/validators.js +18 -65
  28. package/src/client/providers/ShellWebClientProvider.js +100 -356
  29. package/src/client/requestRecovery/runtime.js +29 -77
  30. package/src/client/runtime/bootstrapRuntime.js +5 -4
  31. package/src/client/runtime/refreshRuntime.js +122 -0
  32. package/src/client/support/gestureTargets.js +26 -0
  33. package/src/client/support/menuLinkTarget.js +35 -19
  34. package/src/client/support/routeParams.js +26 -0
  35. package/src/client/support/routeSwipeGesture.js +88 -0
  36. package/src/client/support/routeTransitionKey.js +1 -3
  37. package/src/client/support/shellLayoutClass.js +32 -0
  38. package/test/bootstrapRuntime.test.js +98 -0
  39. package/test/errorActions.test.js +2 -1
  40. package/test/errorRuntime.test.js +30 -0
  41. package/test/linkResolver.test.js +23 -1
  42. package/test/menuLinkTarget.test.js +77 -0
  43. package/test/routeSwipeGesture.test.js +49 -0
  44. package/test/settingsPlacementContract.test.js +16 -6
  45. package/test/shellGestureSupport.test.js +46 -0
  46. package/test/surfaceContext.test.js +53 -0
  47. 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("jskit.shell-web.runtime.web-refresh.client", null);
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 PULL_REFRESH_TRIGGER_DISTANCE = 72;
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 resolvedDrawerWidth = computed(() => {
113
- if (props.drawerWidth !== null && props.drawerWidth !== undefined) {
114
- return normalizeShellDrawerWidth(props.drawerWidth);
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
- props.drawerWidth !== null && props.drawerWidth !== undefined ||
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="props.drawerWidth === null || props.drawerWidth === undefined ? 'content' : 'fixed'"
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;