create-rue 0.8.1 → 0.9.7
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/bundle.js +322 -264
- package/package.json +3 -3
- package/template/base/app/pages/Home.tsx +1 -1
- package/template/base/app/pages/TodoApp.tsx +431 -163
- package/template/base/app/pages/components/Layout.tsx +135 -29
- package/template/base/package.json +18 -19
- package/template/base/tsconfig.json +3 -4
- package/template/base/vite.config.ts +7 -14
- package/template/base/app/env.d.ts +0 -1
|
@@ -2,7 +2,7 @@ import { computed, type FC, ref, useEffect, watch } from '@rue-js/rue'
|
|
|
2
2
|
import { RouterLink, useRoute } from '@rue-js/router'
|
|
3
3
|
|
|
4
4
|
const themeStorageKey = 'rue.base.theme'
|
|
5
|
-
const
|
|
5
|
+
const headerToggleRoutes = ['/', '/report', '/todo']
|
|
6
6
|
|
|
7
7
|
const navItems = [
|
|
8
8
|
{ to: '/', label: '首页' },
|
|
@@ -80,9 +80,21 @@ const themeLabels: Record<string, string> = {
|
|
|
80
80
|
sunset: '日落',
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
const getStoredTheme = () =>
|
|
83
|
+
const getStoredTheme = () => {
|
|
84
|
+
const storedTheme = localStorage.getItem(themeStorageKey)
|
|
85
|
+
return storedTheme && themes.includes(storedTheme) ? storedTheme : 'light'
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const getHeaderStorageKey = (path: string) => {
|
|
89
|
+
if (path === '/') {
|
|
90
|
+
return 'rue.base.header.hide.home'
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return `rue.base.header.hide.${path.replace(/^\//, '')}`
|
|
94
|
+
}
|
|
84
95
|
|
|
85
|
-
const getStoredHeaderHidden = () =>
|
|
96
|
+
const getStoredHeaderHidden = (path: string) =>
|
|
97
|
+
localStorage.getItem(getHeaderStorageKey(path)) === '1'
|
|
86
98
|
|
|
87
99
|
const applyTheme = (theme: string) => {
|
|
88
100
|
document.documentElement.setAttribute('data-theme', theme)
|
|
@@ -91,20 +103,48 @@ const applyTheme = (theme: string) => {
|
|
|
91
103
|
const ThemePicker: FC = () => {
|
|
92
104
|
const theme = ref(getStoredTheme())
|
|
93
105
|
|
|
106
|
+
const applyThemeState = () => {
|
|
107
|
+
const currentTheme = theme.value
|
|
108
|
+
const themePicker = document.querySelector<HTMLSelectElement>('[data-base-theme-picker="true"]')
|
|
109
|
+
|
|
110
|
+
applyTheme(currentTheme)
|
|
111
|
+
|
|
112
|
+
if (themePicker && themePicker.value !== currentTheme) {
|
|
113
|
+
themePicker.value = currentTheme
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const syncStoredTheme = () => {
|
|
118
|
+
theme.value = getStoredTheme()
|
|
119
|
+
requestAnimationFrame(applyThemeState)
|
|
120
|
+
}
|
|
121
|
+
|
|
94
122
|
useEffect(() => {
|
|
95
|
-
|
|
96
|
-
|
|
123
|
+
localStorage.setItem(themeStorageKey, theme.value)
|
|
124
|
+
requestAnimationFrame(applyThemeState)
|
|
125
|
+
|
|
126
|
+
window.addEventListener('storage', syncStoredTheme)
|
|
127
|
+
window.addEventListener('focus', syncStoredTheme)
|
|
128
|
+
document.addEventListener('visibilitychange', syncStoredTheme)
|
|
129
|
+
|
|
130
|
+
return () => {
|
|
131
|
+
window.removeEventListener('storage', syncStoredTheme)
|
|
132
|
+
window.removeEventListener('focus', syncStoredTheme)
|
|
133
|
+
document.removeEventListener('visibilitychange', syncStoredTheme)
|
|
134
|
+
}
|
|
135
|
+
}, [])
|
|
97
136
|
|
|
98
137
|
watch(
|
|
99
138
|
() => theme.value,
|
|
100
139
|
() => {
|
|
101
|
-
applyTheme(theme.value)
|
|
102
140
|
localStorage.setItem(themeStorageKey, theme.value)
|
|
141
|
+
requestAnimationFrame(applyThemeState)
|
|
103
142
|
},
|
|
104
143
|
)
|
|
105
144
|
|
|
106
145
|
return (
|
|
107
146
|
<select
|
|
147
|
+
data-base-theme-picker="true"
|
|
108
148
|
aria-label="切换主题"
|
|
109
149
|
className="select select-bordered select-sm w-40"
|
|
110
150
|
onChange={(event: Event) => {
|
|
@@ -112,7 +152,7 @@ const ThemePicker: FC = () => {
|
|
|
112
152
|
}}
|
|
113
153
|
>
|
|
114
154
|
{themes.map((name) => (
|
|
115
|
-
<option key={name} value={name}
|
|
155
|
+
<option key={name} value={name}>
|
|
116
156
|
{themeLabels[name] || name}
|
|
117
157
|
</option>
|
|
118
158
|
))}
|
|
@@ -123,11 +163,34 @@ const ThemePicker: FC = () => {
|
|
|
123
163
|
const Header: FC = () => {
|
|
124
164
|
const route = useRoute()
|
|
125
165
|
const currentPath = computed(() => route.get()?.path || '/')
|
|
166
|
+
const navClass = (path: string) =>
|
|
167
|
+
`btn btn-sm ${currentPath.get() === path ? 'btn-primary' : 'btn-ghost'}`.trim()
|
|
168
|
+
|
|
169
|
+
const applyHeaderNavState = () => {
|
|
170
|
+
const path = currentPath.get()
|
|
171
|
+
const navLinks = document.querySelectorAll<HTMLElement>('[data-base-nav-path]')
|
|
172
|
+
|
|
173
|
+
navLinks.forEach((link) => {
|
|
174
|
+
const targetPath = link.dataset.baseNavPath || ''
|
|
175
|
+
link.className = `btn btn-sm ${path === targetPath ? 'btn-primary' : 'btn-ghost'}`.trim()
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
watch(
|
|
180
|
+
() => currentPath.get(),
|
|
181
|
+
() => {
|
|
182
|
+
requestAnimationFrame(applyHeaderNavState)
|
|
183
|
+
},
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
useEffect(() => {
|
|
187
|
+
requestAnimationFrame(applyHeaderNavState)
|
|
188
|
+
}, [])
|
|
126
189
|
|
|
127
190
|
return (
|
|
128
|
-
<header className="sticky top-0 z-40 border-b border-base-300/70 bg-base-100/
|
|
129
|
-
<div className="content-width
|
|
130
|
-
<div className="
|
|
191
|
+
<header className="sticky top-0 z-40 border-b border-base-300/70 bg-base-100/90 backdrop-blur">
|
|
192
|
+
<div className="content-width flex flex-wrap items-center gap-3 py-3">
|
|
193
|
+
<div className="min-w-0 flex-1 md:flex-none">
|
|
131
194
|
<RouterLink to="/" className="flex min-w-0 items-center gap-3">
|
|
132
195
|
<div className="flex h-10 w-10 items-center justify-center rounded-2xl bg-primary text-primary-content shadow-lg shadow-primary/20">
|
|
133
196
|
R
|
|
@@ -137,18 +200,19 @@ const Header: FC = () => {
|
|
|
137
200
|
</div>
|
|
138
201
|
</RouterLink>
|
|
139
202
|
</div>
|
|
140
|
-
<nav className="
|
|
203
|
+
<nav className="order-3 flex w-full flex-wrap gap-2 md:order-none md:w-auto md:flex-1 md:justify-center">
|
|
141
204
|
{navItems.map((item) => (
|
|
142
205
|
<RouterLink
|
|
143
206
|
key={item.to}
|
|
144
207
|
to={item.to}
|
|
145
|
-
|
|
208
|
+
data-base-nav-path={item.to}
|
|
209
|
+
className={navClass(item.to)}
|
|
146
210
|
>
|
|
147
|
-
{item.label}
|
|
211
|
+
<span>{item.label}</span>
|
|
148
212
|
</RouterLink>
|
|
149
213
|
))}
|
|
150
214
|
</nav>
|
|
151
|
-
<div className="
|
|
215
|
+
<div className="flex items-center gap-2 md:ml-auto">
|
|
152
216
|
<ThemePicker />
|
|
153
217
|
</div>
|
|
154
218
|
</div>
|
|
@@ -174,26 +238,72 @@ const Footer: FC = () => (
|
|
|
174
238
|
const HeaderToggleButton: FC<{ hidden: boolean; onToggle: () => void }> = (props) => (
|
|
175
239
|
<button
|
|
176
240
|
type="button"
|
|
177
|
-
className="btn btn-circle btn-sm fixed left-3 top-3 z-50 border-base-300 bg-base-100/
|
|
178
|
-
aria-label={props.hidden ? '
|
|
179
|
-
title={props.hidden ? '
|
|
241
|
+
className="btn btn-circle btn-sm fixed left-3 top-3 z-50 border-base-300 bg-base-100/95 text-base-content shadow-lg backdrop-blur"
|
|
242
|
+
aria-label={props.hidden ? '显示头部和底部' : '隐藏头部和底部'}
|
|
243
|
+
title={props.hidden ? '显示头部和底部' : '隐藏头部和底部'}
|
|
180
244
|
onClick={props.onToggle}
|
|
181
245
|
>
|
|
182
|
-
{props.hidden ?
|
|
246
|
+
{props.hidden ? (
|
|
247
|
+
<svg
|
|
248
|
+
viewBox="0 0 24 24"
|
|
249
|
+
width="16"
|
|
250
|
+
height="16"
|
|
251
|
+
aria-hidden="true"
|
|
252
|
+
fill="none"
|
|
253
|
+
stroke="currentColor"
|
|
254
|
+
stroke-width="2"
|
|
255
|
+
stroke-linecap="round"
|
|
256
|
+
stroke-linejoin="round"
|
|
257
|
+
>
|
|
258
|
+
<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" />
|
|
259
|
+
<circle cx="12" cy="12" r="3" />
|
|
260
|
+
</svg>
|
|
261
|
+
) : (
|
|
262
|
+
<svg
|
|
263
|
+
viewBox="0 0 24 24"
|
|
264
|
+
width="16"
|
|
265
|
+
height="16"
|
|
266
|
+
aria-hidden="true"
|
|
267
|
+
fill="none"
|
|
268
|
+
stroke="currentColor"
|
|
269
|
+
stroke-width="2"
|
|
270
|
+
stroke-linecap="round"
|
|
271
|
+
stroke-linejoin="round"
|
|
272
|
+
>
|
|
273
|
+
<path d="M3 3l18 18" />
|
|
274
|
+
<path d="M10.6 10.7a3 3 0 0 0 4.2 4.2" />
|
|
275
|
+
<path d="M9.9 5.1A10.9 10.9 0 0 1 12 5c6.5 0 10 7 10 7a21.7 21.7 0 0 1-4 5.2" />
|
|
276
|
+
<path d="M6.6 6.7C4.2 8.3 2.7 10.9 2 12c0 0 3.5 7 10 7 1.8 0 3.4-.5 4.8-1.3" />
|
|
277
|
+
</svg>
|
|
278
|
+
)}
|
|
183
279
|
</button>
|
|
184
280
|
)
|
|
185
281
|
|
|
186
282
|
const SiteLayout: FC = (props) => {
|
|
187
283
|
const route = useRoute()
|
|
188
|
-
const reportHeaderHidden = ref(getStoredHeaderHidden())
|
|
189
284
|
const currentPath = computed(() => route.get()?.path || '/')
|
|
190
|
-
const
|
|
191
|
-
const
|
|
285
|
+
const allowHeaderToggle = computed(() => headerToggleRoutes.includes(currentPath.get()))
|
|
286
|
+
const reportHeaderHidden = ref(getStoredHeaderHidden(currentPath.get()))
|
|
287
|
+
const hideHeader = computed(() => allowHeaderToggle.get() && reportHeaderHidden.value)
|
|
288
|
+
|
|
289
|
+
watch(
|
|
290
|
+
() => currentPath.get(),
|
|
291
|
+
(path) => {
|
|
292
|
+
reportHeaderHidden.value = allowHeaderToggle.get() ? getStoredHeaderHidden(path) : false
|
|
293
|
+
},
|
|
294
|
+
)
|
|
192
295
|
|
|
193
296
|
watch(
|
|
194
297
|
() => reportHeaderHidden.value,
|
|
195
298
|
() => {
|
|
196
|
-
|
|
299
|
+
if (!allowHeaderToggle.get()) {
|
|
300
|
+
return
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
localStorage.setItem(
|
|
304
|
+
getHeaderStorageKey(currentPath.get()),
|
|
305
|
+
reportHeaderHidden.value ? '1' : '0',
|
|
306
|
+
)
|
|
197
307
|
},
|
|
198
308
|
)
|
|
199
309
|
|
|
@@ -201,7 +311,7 @@ const SiteLayout: FC = (props) => {
|
|
|
201
311
|
<div className="app-shell">
|
|
202
312
|
<div className="app-surface">
|
|
203
313
|
{!hideHeader.get() && <Header />}
|
|
204
|
-
{
|
|
314
|
+
{allowHeaderToggle.get() && (
|
|
205
315
|
<HeaderToggleButton
|
|
206
316
|
hidden={hideHeader.get()}
|
|
207
317
|
onToggle={() => {
|
|
@@ -209,12 +319,8 @@ const SiteLayout: FC = (props) => {
|
|
|
209
319
|
}}
|
|
210
320
|
/>
|
|
211
321
|
)}
|
|
212
|
-
<main
|
|
213
|
-
|
|
214
|
-
>
|
|
215
|
-
{props.children}
|
|
216
|
-
</main>
|
|
217
|
-
<Footer />
|
|
322
|
+
<main className="content-width pb-10 pt-6 sm:pt-8">{props.children}</main>
|
|
323
|
+
{!hideHeader.get() && <Footer />}
|
|
218
324
|
</div>
|
|
219
325
|
</div>
|
|
220
326
|
)
|
|
@@ -6,33 +6,32 @@
|
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "vite",
|
|
8
8
|
"build": "vite build",
|
|
9
|
-
"
|
|
9
|
+
"preview": "vite preview",
|
|
10
|
+
"typecheck": "tsgo --noEmit",
|
|
10
11
|
"oxlint": "oxlint -c ./.oxlintrc.json .",
|
|
11
12
|
"oxlint-fix": "oxlint -c ./.oxlintrc.json . --fix",
|
|
12
13
|
"format": "oxfmt .",
|
|
13
14
|
"format-check": "oxfmt --check ."
|
|
14
15
|
},
|
|
15
16
|
"dependencies": {
|
|
16
|
-
"@rue-js/
|
|
17
|
-
"@rue-js/
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"@rue-js/runtime-vapor": "^0.8.1"
|
|
17
|
+
"@rue-js/router": "^0.9.7",
|
|
18
|
+
"@rue-js/rue": "^0.9.7",
|
|
19
|
+
"daisyui": "^5.7.28",
|
|
20
|
+
"oxfmt": "^0.66.0",
|
|
21
|
+
"oxlint": "^1.81.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@rue-js/jsx-dev-runtime": "^0.8.
|
|
25
|
-
"@rue-js/vite-plugin-rue": "^0.
|
|
26
|
-
"@swc/core": "
|
|
27
|
-
"@tailwindcss/typography": "^0.5.
|
|
28
|
-
"@tailwindcss/vite": "^4.
|
|
29
|
-
"@types/node": "^
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"vite": "^
|
|
35
|
-
"vite-plugin-wasm": "^3.5.0"
|
|
24
|
+
"@rue-js/jsx-dev-runtime": "^0.8.21",
|
|
25
|
+
"@rue-js/vite-plugin-rue": "^0.9.7",
|
|
26
|
+
"@swc/core": "1.15.47",
|
|
27
|
+
"@tailwindcss/typography": "^0.5.20",
|
|
28
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
29
|
+
"@types/node": "^26.4.1",
|
|
30
|
+
"@typescript/native-preview": "beta",
|
|
31
|
+
"tailwindcss": "^4.3.3",
|
|
32
|
+
"typescript": "^7.0.2",
|
|
33
|
+
"vite": "^8.2.2",
|
|
34
|
+
"vite-plugin-wasm": "^3.6.0"
|
|
36
35
|
},
|
|
37
36
|
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
|
|
38
37
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"target": "ES2020",
|
|
4
4
|
"module": "ESNext",
|
|
5
|
-
"lib": ["DOM", "DOM.Iterable", "
|
|
5
|
+
"lib": ["DOM", "DOM.Iterable", "ES2020"],
|
|
6
6
|
"strict": true,
|
|
7
7
|
"skipLibCheck": true,
|
|
8
8
|
"moduleResolution": "bundler",
|
|
@@ -11,12 +11,11 @@
|
|
|
11
11
|
"resolveJsonModule": true,
|
|
12
12
|
"isolatedModules": true,
|
|
13
13
|
"noEmit": false,
|
|
14
|
-
"types": ["
|
|
14
|
+
"types": ["vite/client", "@rue-js/rue/jsx", "node"],
|
|
15
15
|
"jsx": "react-jsx",
|
|
16
16
|
"jsxImportSource": "@rue-js",
|
|
17
17
|
"declaration": true,
|
|
18
|
-
"outDir": "./dist"
|
|
19
|
-
"baseUrl": "."
|
|
18
|
+
"outDir": "./dist"
|
|
20
19
|
},
|
|
21
20
|
"include": ["app/**/*.ts", "app/**/*.tsx", "app/**/*.d.ts", "vite.config.ts"]
|
|
22
21
|
}
|
|
@@ -2,27 +2,20 @@ import { defineConfig } from 'vite'
|
|
|
2
2
|
import VitePluginRue from '@rue-js/vite-plugin-rue'
|
|
3
3
|
import wasm from 'vite-plugin-wasm'
|
|
4
4
|
import tailwindcss from '@tailwindcss/vite'
|
|
5
|
-
import { createRequire } from 'node:module'
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
const runtimeDomEntry = require.resolve('@rue-js/runtime/src/dom.ts')
|
|
9
|
-
|
|
10
|
-
export default defineConfig({
|
|
6
|
+
export default defineConfig(({ command }) => ({
|
|
11
7
|
resolve: {
|
|
12
|
-
alias: [
|
|
13
|
-
{
|
|
14
|
-
find: '@rue-js/runtime/dom',
|
|
15
|
-
replacement: runtimeDomEntry,
|
|
16
|
-
},
|
|
17
|
-
],
|
|
18
8
|
conditions: ['import', 'module', 'browser', 'default'],
|
|
19
9
|
},
|
|
20
10
|
plugins: [
|
|
21
11
|
wasm(),
|
|
22
12
|
tailwindcss() as any,
|
|
23
13
|
VitePluginRue({
|
|
24
|
-
|
|
25
|
-
debug: true,
|
|
14
|
+
debug: command === 'serve',
|
|
26
15
|
}),
|
|
27
16
|
],
|
|
28
|
-
|
|
17
|
+
server: {
|
|
18
|
+
port: 5173,
|
|
19
|
+
strictPort: false,
|
|
20
|
+
},
|
|
21
|
+
}))
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
declare const __SSR__: boolean
|