@byline/host-tanstack-start 2.2.4 → 2.2.6

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.
@@ -1,7 +1,8 @@
1
1
  :is(.aside-zO2mZZ, .byline-admin-menu-drawer-aside) {
2
- border-right: var(--border-width-thin) var(--border-style-solid) var(--gray-50);
2
+ border-right: var(--border-width-thin) var(--border-style-solid)
3
+ var(--gray-50);
3
4
  background-color: var(--canvas-25);
4
- z-index: 15;
5
+ z-index: 40;
5
6
  flex-direction: column;
6
7
  transition: all .3s ease-in-out;
7
8
  display: flex;
@@ -0,0 +1 @@
1
+ export declare function RouteProgressBar(): import("react").JSX.Element;
@@ -0,0 +1,83 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState } from "react";
4
+ import { useRouterState } from "@tanstack/react-router";
5
+ import classnames from "classnames";
6
+ import route_progress_bar_module from "./route-progress-bar.module.js";
7
+ const SHOW_DELAY_MS = 80;
8
+ const MIN_VISIBLE_MS = 250;
9
+ const FINISH_EXIT_MS = 350;
10
+ function RouteProgressBar() {
11
+ const isNavigating = useRouterState({
12
+ select: (s)=>s.isLoading || s.isTransitioning
13
+ });
14
+ const [phase, setPhase] = useState('idle');
15
+ const showTimerRef = useRef(null);
16
+ const finishTimerRef = useRef(null);
17
+ const resetTimerRef = useRef(null);
18
+ const visibleSinceRef = useRef(null);
19
+ useEffect(()=>{
20
+ const clearShow = ()=>{
21
+ if (showTimerRef.current) {
22
+ clearTimeout(showTimerRef.current);
23
+ showTimerRef.current = null;
24
+ }
25
+ };
26
+ const clearFinish = ()=>{
27
+ if (finishTimerRef.current) {
28
+ clearTimeout(finishTimerRef.current);
29
+ finishTimerRef.current = null;
30
+ }
31
+ if (resetTimerRef.current) {
32
+ clearTimeout(resetTimerRef.current);
33
+ resetTimerRef.current = null;
34
+ }
35
+ };
36
+ if (isNavigating) {
37
+ clearFinish();
38
+ if ('finishing' === phase) {
39
+ visibleSinceRef.current = performance.now();
40
+ setPhase('visible');
41
+ return;
42
+ }
43
+ if ('idle' === phase && null === showTimerRef.current) showTimerRef.current = setTimeout(()=>{
44
+ showTimerRef.current = null;
45
+ visibleSinceRef.current = performance.now();
46
+ setPhase('visible');
47
+ }, SHOW_DELAY_MS);
48
+ return;
49
+ }
50
+ if (null !== showTimerRef.current) return void clearShow();
51
+ if ('visible' === phase) {
52
+ const elapsed = performance.now() - (visibleSinceRef.current ?? 0);
53
+ const wait = Math.max(0, MIN_VISIBLE_MS - elapsed);
54
+ finishTimerRef.current = setTimeout(()=>{
55
+ finishTimerRef.current = null;
56
+ setPhase('finishing');
57
+ resetTimerRef.current = setTimeout(()=>{
58
+ resetTimerRef.current = null;
59
+ visibleSinceRef.current = null;
60
+ setPhase('idle');
61
+ }, FINISH_EXIT_MS);
62
+ }, wait);
63
+ }
64
+ }, [
65
+ isNavigating,
66
+ phase
67
+ ]);
68
+ useEffect(()=>()=>{
69
+ if (showTimerRef.current) clearTimeout(showTimerRef.current);
70
+ if (finishTimerRef.current) clearTimeout(finishTimerRef.current);
71
+ if (resetTimerRef.current) clearTimeout(resetTimerRef.current);
72
+ }, []);
73
+ return /*#__PURE__*/ jsx("div", {
74
+ "aria-hidden": "true",
75
+ className: classnames('byline-route-progress-bar', route_progress_bar_module.bar, {
76
+ [route_progress_bar_module.barVisible]: 'visible' === phase,
77
+ 'byline-route-progress-bar-visible': 'visible' === phase,
78
+ [route_progress_bar_module.barFinishing]: 'finishing' === phase,
79
+ 'byline-route-progress-bar-finishing': 'finishing' === phase
80
+ })
81
+ });
82
+ }
83
+ export { RouteProgressBar };
@@ -0,0 +1,7 @@
1
+ import "./route-progress-bar_module.css";
2
+ const route_progress_bar_module = {
3
+ bar: "bar-KT7kQq",
4
+ barVisible: "barVisible-qOhb8T",
5
+ barFinishing: "barFinishing-Ifa7gF"
6
+ };
7
+ export default route_progress_bar_module;
@@ -0,0 +1,25 @@
1
+ :is(.bar-KT7kQq, .byline-route-progress-bar) {
2
+ z-index: 100;
3
+ background-color: var(--primary-500);
4
+ opacity: 0;
5
+ pointer-events: none;
6
+ will-change: width, opacity;
7
+ width: 0%;
8
+ height: 2px;
9
+ position: fixed;
10
+ top: 0;
11
+ left: 0;
12
+ }
13
+
14
+ :is(.barVisible-qOhb8T, .byline-route-progress-bar-visible) {
15
+ opacity: 1;
16
+ width: 90%;
17
+ transition: width 8s cubic-bezier(0, .6, .1, 1), opacity .1s linear;
18
+ }
19
+
20
+ :is(.barFinishing-Ifa7gF, .byline-route-progress-bar-finishing) {
21
+ opacity: 0;
22
+ width: 100%;
23
+ transition: width .18s ease-out, opacity .25s ease-out 80ms;
24
+ }
25
+
@@ -9,6 +9,7 @@ import { DrawerToggle } from "../admin-shell/chrome/drawer-toggle.js";
9
9
  import { AdminMenuDrawer } from "../admin-shell/chrome/menu-drawer.js";
10
10
  import { AdminMenuProvider } from "../admin-shell/chrome/menu-provider.js";
11
11
  import { RouteError, RouteNotFound } from "../admin-shell/chrome/route-error.js";
12
+ import { RouteProgressBar } from "../admin-shell/chrome/route-progress-bar.js";
12
13
  import { bylineAdminServices } from "../integrations/byline-admin-services.js";
13
14
  import { BylineAiAdminProvider } from "../integrations/byline-ai.js";
14
15
  import { bylineFieldServices } from "../integrations/byline-field-services.js";
@@ -40,6 +41,7 @@ function createAdminLayoutRoute(path, opts = {}) {
40
41
  children: /*#__PURE__*/ jsx(BylineAiAdminProvider, {
41
42
  children: /*#__PURE__*/ jsxs(AdminMenuProvider, {
42
43
  children: [
44
+ /*#__PURE__*/ jsx(RouteProgressBar, {}),
43
45
  /*#__PURE__*/ jsx(AdminAppBar, {
44
46
  user: user
45
47
  }),
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "private": false,
4
4
  "type": "module",
5
5
  "license": "MPL-2.0",
6
- "version": "2.2.4",
6
+ "version": "2.2.6",
7
7
  "engines": {
8
8
  "node": ">=20.9.0"
9
9
  },
@@ -107,12 +107,12 @@
107
107
  "react-swipeable": "^7.0.2",
108
108
  "uuid": "^14.0.0",
109
109
  "zod": "^4.4.3",
110
- "@byline/admin": "2.2.4",
111
- "@byline/ai": "2.2.4",
112
- "@byline/auth": "2.2.4",
113
- "@byline/client": "2.2.4",
114
- "@byline/core": "2.2.4",
115
- "@byline/ui": "2.2.4"
110
+ "@byline/auth": "2.2.6",
111
+ "@byline/client": "2.2.6",
112
+ "@byline/core": "2.2.6",
113
+ "@byline/ui": "2.2.6",
114
+ "@byline/ai": "2.2.6",
115
+ "@byline/admin": "2.2.6"
116
116
  },
117
117
  "peerDependencies": {
118
118
  "@tanstack/react-router": "^1.167.0",
@@ -16,14 +16,16 @@
16
16
  :global(.byline-admin-menu-drawer-aside) {
17
17
  display: flex;
18
18
  flex-direction: column;
19
- border-right: var(--border-width-thin) var(--border-style-solid) var(--gray-50);
19
+ border-right: var(--border-width-thin) var(--border-style-solid)
20
+ var(--gray-50);
20
21
  background-color: var(--canvas-25);
21
- z-index: 15;
22
+ z-index: 40;
22
23
  transition: all 300ms ease-in-out;
23
24
  }
24
25
 
25
26
  :is([data-theme="dark"], :global(.dark)) .aside,
26
- :is([data-theme="dark"], :global(.dark)) :global(.byline-admin-menu-drawer-aside) {
27
+ :is([data-theme="dark"], :global(.dark))
28
+ :global(.byline-admin-menu-drawer-aside) {
27
29
  border-right-color: var(--canvas-700);
28
30
  background-color: var(--canvas-800);
29
31
  }
@@ -132,18 +134,27 @@
132
134
  }
133
135
 
134
136
  .nav ul :global(li.menu-item) :is(a, button):disabled,
135
- :global(.byline-admin-menu-drawer) ul :global(li.menu-item) :is(a, button):disabled {
137
+ :global(.byline-admin-menu-drawer)
138
+ ul
139
+ :global(li.menu-item)
140
+ :is(a, button):disabled {
136
141
  cursor: not-allowed;
137
142
  opacity: 0.6;
138
143
  }
139
144
 
140
145
  .nav ul :global(li.menu-item.compact) :is(a, button),
141
- :global(.byline-admin-menu-drawer) ul :global(li.menu-item.compact) :is(a, button) {
146
+ :global(.byline-admin-menu-drawer)
147
+ ul
148
+ :global(li.menu-item.compact)
149
+ :is(a, button) {
142
150
  justify-content: center;
143
151
  }
144
152
 
145
153
  .nav ul :global(li.menu-item.active) :is(a, button),
146
- :global(.byline-admin-menu-drawer) ul :global(li.menu-item.active) :is(a, button) {
154
+ :global(.byline-admin-menu-drawer)
155
+ ul
156
+ :global(li.menu-item.active)
157
+ :is(a, button) {
147
158
  color: #111111;
148
159
  text-decoration: none;
149
160
  }
@@ -156,7 +167,11 @@
156
167
  background-color: #dddddd;
157
168
  }
158
169
 
159
- :is([data-theme="dark"], :global(.dark)) .nav ul :global(li.menu-item) :is(a, button),
170
+ :is([data-theme="dark"], :global(.dark))
171
+ .nav
172
+ ul
173
+ :global(li.menu-item)
174
+ :is(a, button),
160
175
  :is([data-theme="dark"], :global(.dark))
161
176
  :global(.byline-admin-menu-drawer)
162
177
  ul
@@ -165,7 +180,11 @@
165
180
  color: #dddddd;
166
181
  }
167
182
 
168
- :is([data-theme="dark"], :global(.dark)) .nav ul :global(li.menu-item.active) :is(a, button),
183
+ :is([data-theme="dark"], :global(.dark))
184
+ .nav
185
+ ul
186
+ :global(li.menu-item.active)
187
+ :is(a, button),
169
188
  :is([data-theme="dark"], :global(.dark))
170
189
  :global(.byline-admin-menu-drawer)
171
190
  ul
@@ -0,0 +1,41 @@
1
+ /**
2
+ * RouteProgressBar — top-of-viewport indeterminate progress indicator for
3
+ * admin route transitions.
4
+ *
5
+ * Override handles:
6
+ * .byline-route-progress-bar — base bar element
7
+ * .byline-route-progress-bar-visible — bar is growing (navigation in flight)
8
+ * .byline-route-progress-bar-finishing — bar is completing + fading out
9
+ */
10
+
11
+ .bar,
12
+ :global(.byline-route-progress-bar) {
13
+ position: fixed;
14
+ top: 0;
15
+ left: 0;
16
+ z-index: 100;
17
+ height: 2px;
18
+ width: 0%;
19
+ background-color: var(--primary-500);
20
+ opacity: 0;
21
+ pointer-events: none;
22
+ will-change: width, opacity;
23
+ }
24
+
25
+ .barVisible,
26
+ :global(.byline-route-progress-bar-visible) {
27
+ width: 90%;
28
+ opacity: 1;
29
+ transition:
30
+ width 8s cubic-bezier(0, 0.6, 0.1, 1),
31
+ opacity 100ms linear;
32
+ }
33
+
34
+ .barFinishing,
35
+ :global(.byline-route-progress-bar-finishing) {
36
+ width: 100%;
37
+ opacity: 0;
38
+ transition:
39
+ width 180ms ease-out,
40
+ opacity 250ms ease-out 80ms;
41
+ }
@@ -0,0 +1,128 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * This Source Code is subject to the terms of the Mozilla Public
5
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
6
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
+ *
8
+ * Copyright (c) Infonomic Company Limited
9
+ */
10
+
11
+ /**
12
+ * RouteProgressBar — global navigation progress indicator for the admin shell.
13
+ *
14
+ * Driven by TanStack Router's reactive state via `useRouterState`. The bar
15
+ * appears across the top of the viewport whenever a route transition or
16
+ * loader is in flight.
17
+ *
18
+ * Timing:
19
+ * - SHOW_DELAY_MS — debounce before the bar appears. Sub-debounce
20
+ * navigations (typical instant client-side transitions)
21
+ * never show the bar at all.
22
+ * - MIN_VISIBLE_MS — once shown, the bar remains visible for at least this
23
+ * long before the completion animation, so it never
24
+ * flickers as a single-frame artefact.
25
+ */
26
+
27
+ import { useEffect, useRef, useState } from 'react'
28
+ import { useRouterState } from '@tanstack/react-router'
29
+
30
+ import cx from 'classnames'
31
+
32
+ import styles from './route-progress-bar.module.css'
33
+
34
+ const SHOW_DELAY_MS = 80
35
+ const MIN_VISIBLE_MS = 250
36
+ const FINISH_EXIT_MS = 350
37
+
38
+ type Phase = 'idle' | 'visible' | 'finishing'
39
+
40
+ export function RouteProgressBar() {
41
+ const isNavigating = useRouterState({
42
+ select: (s) => s.isLoading || s.isTransitioning,
43
+ })
44
+ const [phase, setPhase] = useState<Phase>('idle')
45
+ const showTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
46
+ const finishTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
47
+ const resetTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
48
+ const visibleSinceRef = useRef<number | null>(null)
49
+
50
+ useEffect(() => {
51
+ const clearShow = () => {
52
+ if (showTimerRef.current) {
53
+ clearTimeout(showTimerRef.current)
54
+ showTimerRef.current = null
55
+ }
56
+ }
57
+ const clearFinish = () => {
58
+ if (finishTimerRef.current) {
59
+ clearTimeout(finishTimerRef.current)
60
+ finishTimerRef.current = null
61
+ }
62
+ if (resetTimerRef.current) {
63
+ clearTimeout(resetTimerRef.current)
64
+ resetTimerRef.current = null
65
+ }
66
+ }
67
+
68
+ if (isNavigating) {
69
+ // A new navigation started — abort any pending exit animation.
70
+ clearFinish()
71
+
72
+ if (phase === 'finishing') {
73
+ // Re-grab the bar mid-fade.
74
+ visibleSinceRef.current = performance.now()
75
+ setPhase('visible')
76
+ return
77
+ }
78
+ if (phase === 'idle' && showTimerRef.current === null) {
79
+ showTimerRef.current = setTimeout(() => {
80
+ showTimerRef.current = null
81
+ visibleSinceRef.current = performance.now()
82
+ setPhase('visible')
83
+ }, SHOW_DELAY_MS)
84
+ }
85
+ return
86
+ }
87
+
88
+ // Navigation finished.
89
+ if (showTimerRef.current !== null) {
90
+ // Still inside the debounce window — bar never appeared. No-op.
91
+ clearShow()
92
+ return
93
+ }
94
+ if (phase === 'visible') {
95
+ const elapsed = performance.now() - (visibleSinceRef.current ?? 0)
96
+ const wait = Math.max(0, MIN_VISIBLE_MS - elapsed)
97
+ finishTimerRef.current = setTimeout(() => {
98
+ finishTimerRef.current = null
99
+ setPhase('finishing')
100
+ resetTimerRef.current = setTimeout(() => {
101
+ resetTimerRef.current = null
102
+ visibleSinceRef.current = null
103
+ setPhase('idle')
104
+ }, FINISH_EXIT_MS)
105
+ }, wait)
106
+ }
107
+ }, [isNavigating, phase])
108
+
109
+ useEffect(() => {
110
+ return () => {
111
+ if (showTimerRef.current) clearTimeout(showTimerRef.current)
112
+ if (finishTimerRef.current) clearTimeout(finishTimerRef.current)
113
+ if (resetTimerRef.current) clearTimeout(resetTimerRef.current)
114
+ }
115
+ }, [])
116
+
117
+ return (
118
+ <div
119
+ aria-hidden="true"
120
+ className={cx('byline-route-progress-bar', styles.bar, {
121
+ [styles.barVisible]: phase === 'visible',
122
+ 'byline-route-progress-bar-visible': phase === 'visible',
123
+ [styles.barFinishing]: phase === 'finishing',
124
+ 'byline-route-progress-bar-finishing': phase === 'finishing',
125
+ })}
126
+ />
127
+ )
128
+ }
@@ -31,6 +31,7 @@ import { DrawerToggle } from '../admin-shell/chrome/drawer-toggle.js'
31
31
  import { AdminMenuDrawer } from '../admin-shell/chrome/menu-drawer.js'
32
32
  import { AdminMenuProvider } from '../admin-shell/chrome/menu-provider.js'
33
33
  import { RouteError, RouteNotFound } from '../admin-shell/chrome/route-error.js'
34
+ import { RouteProgressBar } from '../admin-shell/chrome/route-progress-bar.js'
34
35
  import { bylineAdminServices } from '../integrations/byline-admin-services.js'
35
36
  import { BylineAiAdminProvider } from '../integrations/byline-ai.js'
36
37
  import { bylineFieldServices } from '../integrations/byline-field-services.js'
@@ -70,6 +71,7 @@ export function createAdminLayoutRoute(path: string, opts: AdminLayoutOpts = {})
70
71
  <BylineFieldServicesProvider services={bylineFieldServices}>
71
72
  <BylineAiAdminProvider>
72
73
  <AdminMenuProvider>
74
+ <RouteProgressBar />
73
75
  <AdminAppBar user={user} />
74
76
  <main className={cx('byline-admin-layout-main', layoutStyles.main)}>
75
77
  <DrawerToggle />