create-rue 0.3.12 → 0.7.2
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 +265 -323
- package/package.json +3 -3
- package/template/base/app/pages/Home.tsx +1 -1
- package/template/base/app/pages/TodoApp.tsx +211 -461
- package/template/base/app/pages/components/Layout.tsx +29 -135
- package/template/base/package.json +18 -21
- package/template/base/tsconfig.json +3 -2
- package/template/base/vite.config.ts +4 -7
|
@@ -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 reportHeaderStorageKey = 'rue.base.report.hideHeader'
|
|
6
6
|
|
|
7
7
|
const navItems = [
|
|
8
8
|
{ to: '/', label: '首页' },
|
|
@@ -80,21 +80,9 @@ const themeLabels: Record<string, string> = {
|
|
|
80
80
|
sunset: '日落',
|
|
81
81
|
}
|
|
82
82
|
|
|
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
|
-
}
|
|
83
|
+
const getStoredTheme = () => localStorage.getItem(themeStorageKey) || 'light'
|
|
95
84
|
|
|
96
|
-
const getStoredHeaderHidden = (
|
|
97
|
-
localStorage.getItem(getHeaderStorageKey(path)) === '1'
|
|
85
|
+
const getStoredHeaderHidden = () => localStorage.getItem(reportHeaderStorageKey) === '1'
|
|
98
86
|
|
|
99
87
|
const applyTheme = (theme: string) => {
|
|
100
88
|
document.documentElement.setAttribute('data-theme', theme)
|
|
@@ -103,48 +91,20 @@ const applyTheme = (theme: string) => {
|
|
|
103
91
|
const ThemePicker: FC = () => {
|
|
104
92
|
const theme = ref(getStoredTheme())
|
|
105
93
|
|
|
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
|
-
|
|
122
94
|
useEffect(() => {
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
}, [])
|
|
95
|
+
applyTheme(theme.value)
|
|
96
|
+
})
|
|
136
97
|
|
|
137
98
|
watch(
|
|
138
99
|
() => theme.value,
|
|
139
100
|
() => {
|
|
101
|
+
applyTheme(theme.value)
|
|
140
102
|
localStorage.setItem(themeStorageKey, theme.value)
|
|
141
|
-
requestAnimationFrame(applyThemeState)
|
|
142
103
|
},
|
|
143
104
|
)
|
|
144
105
|
|
|
145
106
|
return (
|
|
146
107
|
<select
|
|
147
|
-
data-base-theme-picker="true"
|
|
148
108
|
aria-label="切换主题"
|
|
149
109
|
className="select select-bordered select-sm w-40"
|
|
150
110
|
onChange={(event: Event) => {
|
|
@@ -152,7 +112,7 @@ const ThemePicker: FC = () => {
|
|
|
152
112
|
}}
|
|
153
113
|
>
|
|
154
114
|
{themes.map((name) => (
|
|
155
|
-
<option key={name} value={name}>
|
|
115
|
+
<option key={name} value={name} selected={theme.value === name}>
|
|
156
116
|
{themeLabels[name] || name}
|
|
157
117
|
</option>
|
|
158
118
|
))}
|
|
@@ -163,34 +123,11 @@ const ThemePicker: FC = () => {
|
|
|
163
123
|
const Header: FC = () => {
|
|
164
124
|
const route = useRoute()
|
|
165
125
|
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
|
-
}, [])
|
|
189
126
|
|
|
190
127
|
return (
|
|
191
|
-
<header className="sticky top-0 z-40 border-b border-base-300/70 bg-base-100/
|
|
192
|
-
<div className="content-width
|
|
193
|
-
<div className="min-w-0
|
|
128
|
+
<header className="sticky top-0 z-40 border-b border-base-300/70 bg-base-100/85 backdrop-blur">
|
|
129
|
+
<div className="content-width navbar min-h-0 gap-4 py-3">
|
|
130
|
+
<div className="navbar-start min-w-0">
|
|
194
131
|
<RouterLink to="/" className="flex min-w-0 items-center gap-3">
|
|
195
132
|
<div className="flex h-10 w-10 items-center justify-center rounded-2xl bg-primary text-primary-content shadow-lg shadow-primary/20">
|
|
196
133
|
R
|
|
@@ -200,19 +137,18 @@ const Header: FC = () => {
|
|
|
200
137
|
</div>
|
|
201
138
|
</RouterLink>
|
|
202
139
|
</div>
|
|
203
|
-
<nav className="
|
|
140
|
+
<nav className="navbar-center hidden flex-1 justify-center gap-2 md:flex">
|
|
204
141
|
{navItems.map((item) => (
|
|
205
142
|
<RouterLink
|
|
206
143
|
key={item.to}
|
|
207
144
|
to={item.to}
|
|
208
|
-
|
|
209
|
-
className={navClass(item.to)}
|
|
145
|
+
className={`btn btn-sm ${currentPath.get() === item.to ? 'btn-primary' : 'btn-ghost'}`}
|
|
210
146
|
>
|
|
211
|
-
|
|
147
|
+
{item.label}
|
|
212
148
|
</RouterLink>
|
|
213
149
|
))}
|
|
214
150
|
</nav>
|
|
215
|
-
<div className="flex items-center gap-2
|
|
151
|
+
<div className="navbar-end flex items-center gap-2">
|
|
216
152
|
<ThemePicker />
|
|
217
153
|
</div>
|
|
218
154
|
</div>
|
|
@@ -238,72 +174,26 @@ const Footer: FC = () => (
|
|
|
238
174
|
const HeaderToggleButton: FC<{ hidden: boolean; onToggle: () => void }> = (props) => (
|
|
239
175
|
<button
|
|
240
176
|
type="button"
|
|
241
|
-
className="btn btn-circle btn-sm fixed left-3 top-3 z-50 border-base-300 bg-base-100/
|
|
242
|
-
aria-label={props.hidden ? '
|
|
243
|
-
title={props.hidden ? '
|
|
177
|
+
className="btn btn-circle btn-sm fixed left-3 top-3 z-50 border-base-300 bg-base-100/90 shadow-lg backdrop-blur"
|
|
178
|
+
aria-label={props.hidden ? '显示顶部导航' : '隐藏顶部导航'}
|
|
179
|
+
title={props.hidden ? '显示顶部导航' : '隐藏顶部导航'}
|
|
244
180
|
onClick={props.onToggle}
|
|
245
181
|
>
|
|
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
|
-
)}
|
|
182
|
+
{props.hidden ? '显' : '隐'}
|
|
279
183
|
</button>
|
|
280
184
|
)
|
|
281
185
|
|
|
282
186
|
const SiteLayout: FC = (props) => {
|
|
283
187
|
const route = useRoute()
|
|
188
|
+
const reportHeaderHidden = ref(getStoredHeaderHidden())
|
|
284
189
|
const currentPath = computed(() => route.get()?.path || '/')
|
|
285
|
-
const
|
|
286
|
-
const
|
|
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
|
-
)
|
|
190
|
+
const isReportRoute = computed(() => currentPath.get() === '/report')
|
|
191
|
+
const hideHeader = computed(() => isReportRoute.get() && reportHeaderHidden.value)
|
|
295
192
|
|
|
296
193
|
watch(
|
|
297
194
|
() => reportHeaderHidden.value,
|
|
298
195
|
() => {
|
|
299
|
-
|
|
300
|
-
return
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
localStorage.setItem(
|
|
304
|
-
getHeaderStorageKey(currentPath.get()),
|
|
305
|
-
reportHeaderHidden.value ? '1' : '0',
|
|
306
|
-
)
|
|
196
|
+
localStorage.setItem(reportHeaderStorageKey, reportHeaderHidden.value ? '1' : '0')
|
|
307
197
|
},
|
|
308
198
|
)
|
|
309
199
|
|
|
@@ -311,7 +201,7 @@ const SiteLayout: FC = (props) => {
|
|
|
311
201
|
<div className="app-shell">
|
|
312
202
|
<div className="app-surface">
|
|
313
203
|
{!hideHeader.get() && <Header />}
|
|
314
|
-
{
|
|
204
|
+
{isReportRoute.get() && (
|
|
315
205
|
<HeaderToggleButton
|
|
316
206
|
hidden={hideHeader.get()}
|
|
317
207
|
onToggle={() => {
|
|
@@ -319,8 +209,12 @@ const SiteLayout: FC = (props) => {
|
|
|
319
209
|
}}
|
|
320
210
|
/>
|
|
321
211
|
)}
|
|
322
|
-
<main
|
|
323
|
-
|
|
212
|
+
<main
|
|
213
|
+
className={`content-width pb-10 ${hideHeader.get() ? 'pt-6 sm:pt-8' : 'pt-6 sm:pt-8'}`}
|
|
214
|
+
>
|
|
215
|
+
{props.children}
|
|
216
|
+
</main>
|
|
217
|
+
<Footer />
|
|
324
218
|
</div>
|
|
325
219
|
</div>
|
|
326
220
|
)
|
|
@@ -1,40 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rue-demo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "vite",
|
|
8
8
|
"build": "vite build",
|
|
9
|
-
"
|
|
10
|
-
"typecheck": "tsgo --noEmit",
|
|
9
|
+
"typecheck": "tsc --noEmit",
|
|
11
10
|
"oxlint": "oxlint -c ./.oxlintrc.json .",
|
|
12
11
|
"oxlint-fix": "oxlint -c ./.oxlintrc.json . --fix",
|
|
13
12
|
"format": "oxfmt .",
|
|
14
13
|
"format-check": "oxfmt --check ."
|
|
15
14
|
},
|
|
16
15
|
"dependencies": {
|
|
17
|
-
"@rue-js/
|
|
18
|
-
"@rue-js/
|
|
19
|
-
"@rue-js/
|
|
20
|
-
"@rue-js/
|
|
21
|
-
"@rue-js/runtime
|
|
22
|
-
"
|
|
23
|
-
"oxfmt": "^0.48.0",
|
|
24
|
-
"oxlint": "^1.63.0"
|
|
16
|
+
"@rue-js/design": "^0.7.2",
|
|
17
|
+
"@rue-js/jsx-runtime": "^0.7.2",
|
|
18
|
+
"@rue-js/router": "^0.7.2",
|
|
19
|
+
"@rue-js/rue": "^0.7.2",
|
|
20
|
+
"@rue-js/runtime": "^0.7.2",
|
|
21
|
+
"@rue-js/runtime-vapor": "^0.7.2"
|
|
25
22
|
},
|
|
26
23
|
"devDependencies": {
|
|
27
|
-
"@rue-js/jsx-dev-runtime": "^0.
|
|
28
|
-
"@rue-js/vite-plugin-rue": "^0.
|
|
29
|
-
"@swc/core": "^1.15.
|
|
24
|
+
"@rue-js/jsx-dev-runtime": "^0.7.2",
|
|
25
|
+
"@rue-js/vite-plugin-rue": "^0.7.2",
|
|
26
|
+
"@swc/core": "^1.15.3",
|
|
30
27
|
"@tailwindcss/typography": "^0.5.19",
|
|
31
|
-
"@tailwindcss/vite": "^4.
|
|
32
|
-
"@types/node": "^
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"typescript": "^
|
|
36
|
-
"vite": "^8.0.
|
|
37
|
-
"vite-plugin-wasm": "^3.
|
|
28
|
+
"@tailwindcss/vite": "^4.1.17",
|
|
29
|
+
"@types/node": "^24.10.1",
|
|
30
|
+
"oxfmt": "^0.42.0",
|
|
31
|
+
"oxlint": "^1.57.0",
|
|
32
|
+
"typescript": "^5.9.3",
|
|
33
|
+
"vite": "^8.0.3",
|
|
34
|
+
"vite-plugin-wasm": "^3.5.0"
|
|
38
35
|
},
|
|
39
36
|
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
|
|
40
37
|
}
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
"resolveJsonModule": true,
|
|
12
12
|
"isolatedModules": true,
|
|
13
13
|
"noEmit": false,
|
|
14
|
-
"types": ["
|
|
14
|
+
"types": ["vitest/globals"],
|
|
15
15
|
"jsx": "react-jsx",
|
|
16
16
|
"jsxImportSource": "@rue-js",
|
|
17
17
|
"declaration": true,
|
|
18
|
-
"outDir": "./dist"
|
|
18
|
+
"outDir": "./dist",
|
|
19
|
+
"baseUrl": "."
|
|
19
20
|
},
|
|
20
21
|
"include": ["app/**/*.ts", "app/**/*.tsx", "app/**/*.d.ts", "vite.config.ts"]
|
|
21
22
|
}
|
|
@@ -3,7 +3,7 @@ import VitePluginRue from '@rue-js/vite-plugin-rue'
|
|
|
3
3
|
import wasm from 'vite-plugin-wasm'
|
|
4
4
|
import tailwindcss from '@tailwindcss/vite'
|
|
5
5
|
|
|
6
|
-
export default defineConfig(
|
|
6
|
+
export default defineConfig({
|
|
7
7
|
resolve: {
|
|
8
8
|
conditions: ['import', 'module', 'browser', 'default'],
|
|
9
9
|
},
|
|
@@ -11,11 +11,8 @@ export default defineConfig(({ command }) => ({
|
|
|
11
11
|
wasm(),
|
|
12
12
|
tailwindcss() as any,
|
|
13
13
|
VitePluginRue({
|
|
14
|
-
|
|
14
|
+
include: ['/app/'],
|
|
15
|
+
debug: true,
|
|
15
16
|
}),
|
|
16
17
|
],
|
|
17
|
-
|
|
18
|
-
port: 5173,
|
|
19
|
-
strictPort: false,
|
|
20
|
-
},
|
|
21
|
-
}))
|
|
18
|
+
})
|