helfy-create 0.0.6 → 0.1.0
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/README.md +49 -38
- package/bin/create-helfy.js +8 -0
- package/package.json +31 -31
- package/template/declaration.d.ts +10 -8
- package/template/package-lock.json +6981 -7000
- package/template/package.json +29 -30
- package/template/rspack.config.js +76 -76
- package/template/src/App.hf +20 -0
- package/template/src/App.ts +5 -0
- package/template/src/context/ThemeContext.ts +18 -0
- package/template/src/pages/AboutPage.hf +12 -0
- package/template/src/pages/AboutPage.ts +5 -0
- package/template/src/pages/CounterPage.hf +13 -0
- package/template/src/pages/CounterPage.ts +15 -0
- package/template/src/pages/HomePage.hf +17 -0
- package/template/src/pages/HomePage.ts +5 -0
- package/template/src/pages/_router.hf +21 -0
- package/template/src/pages/_router.ts +5 -0
- package/template/src/pages/{_routes.tsx → _routes.ts} +10 -10
- package/template/src/pages/layouts/MainLayout.hf +24 -0
- package/template/src/pages/layouts/MainLayout.ts +5 -0
- package/template/src/widgets/Footer/Footer.hf +5 -0
- package/template/src/widgets/Footer/Footer.ts +5 -0
- package/template/src/widgets/Header/Header.hf +15 -0
- package/template/src/widgets/Header/Header.ts +11 -0
- package/template/src/widgets/Navbar/Navbar.hf +14 -0
- package/template/src/widgets/Navbar/Navbar.ts +11 -0
- package/template/src/widgets/NotFound/NotFound.hf +14 -0
- package/template/src/widgets/NotFound/NotFound.ts +12 -0
- package/template/src/widgets/ThemeToggle/ThemeToggle.hf +16 -0
- package/template/src/widgets/ThemeToggle/ThemeToggle.ts +12 -0
- package/template/src/widgets/navLinks.ts +5 -0
- package/template/tsconfig.json +0 -2
- package/template/src/App.tsx +0 -26
- package/template/src/context/ThemeContext.tsx +0 -60
- package/template/src/pages/AboutPage.tsx +0 -18
- package/template/src/pages/CounterPage.tsx +0 -17
- package/template/src/pages/HomePage.tsx +0 -22
- package/template/src/pages/_router.tsx +0 -15
- package/template/src/pages/layouts/MainLayout.tsx +0 -29
- package/template/src/widgets/Footer/Footer.tsx +0 -8
- package/template/src/widgets/Header/Header.tsx +0 -34
- package/template/src/widgets/Navbar/Navbar.tsx +0 -29
- package/template/src/widgets/NotFound/NotFound.tsx +0 -31
package/template/package.json
CHANGED
|
@@ -1,30 +1,29 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{PROJECT_NAME}}",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"scripts": {
|
|
6
|
-
"dev": "cross-env NODE_ENV=development rspack serve",
|
|
7
|
-
"build": "cross-env NODE_ENV=production rspack build"
|
|
8
|
-
},
|
|
9
|
-
"dependencies": {
|
|
10
|
-
"@helfy/helfy": "^0.
|
|
11
|
-
},
|
|
12
|
-
"devDependencies": {
|
|
13
|
-
"@babel/core": "^7.20.0",
|
|
14
|
-
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
|
15
|
-
"@babel/plugin-proposal-decorators": "^7.21.0",
|
|
16
|
-
"@babel/plugin-proposal-private-methods": "^7.18.6",
|
|
17
|
-
"@babel/plugin-proposal-private-property-in-object": "^7.21.0",
|
|
18
|
-
"@babel/preset-env": "^7.20.0",
|
|
19
|
-
"@babel/preset-react": "^7.18.6",
|
|
20
|
-
"@babel/preset-typescript": "^7.21.0",
|
|
21
|
-
"@
|
|
22
|
-
"
|
|
23
|
-
"babel-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PROJECT_NAME}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "cross-env NODE_ENV=development rspack serve",
|
|
7
|
+
"build": "cross-env NODE_ENV=production rspack build"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@helfy/helfy": "^0.1.2"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@babel/core": "^7.20.0",
|
|
14
|
+
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
|
15
|
+
"@babel/plugin-proposal-decorators": "^7.21.0",
|
|
16
|
+
"@babel/plugin-proposal-private-methods": "^7.18.6",
|
|
17
|
+
"@babel/plugin-proposal-private-property-in-object": "^7.21.0",
|
|
18
|
+
"@babel/preset-env": "^7.20.0",
|
|
19
|
+
"@babel/preset-react": "^7.18.6",
|
|
20
|
+
"@babel/preset-typescript": "^7.21.0",
|
|
21
|
+
"@rspack/cli": "^1.3.0",
|
|
22
|
+
"@rspack/core": "^1.3.0",
|
|
23
|
+
"babel-loader": "^9.1.2",
|
|
24
|
+
"babel-plugin-transform-typescript-metadata": "^0.4.0",
|
|
25
|
+
"cross-env": "^7.0.3",
|
|
26
|
+
"css-loader": "^6.7.3",
|
|
27
|
+
"typescript": "^5.9.3"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const { rspack } = require('@rspack/core');
|
|
3
|
-
|
|
4
|
-
const isDev = process.env.NODE_ENV === 'development';
|
|
5
|
-
|
|
6
|
-
const paths = {
|
|
7
|
-
src: path.resolve(__dirname, 'src'),
|
|
8
|
-
dist: path.resolve(__dirname, 'dist'),
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
module.exports = {
|
|
12
|
-
context: paths.src,
|
|
13
|
-
mode: isDev ? 'development' : 'production',
|
|
14
|
-
entry: './index.ts',
|
|
15
|
-
watchOptions: isDev
|
|
16
|
-
? { ignored: ['**/node_modules/**', '**/.helfy/**'] }
|
|
17
|
-
: {},
|
|
18
|
-
devServer: {
|
|
19
|
-
hot: true,
|
|
20
|
-
port: 3000,
|
|
21
|
-
static: { directory: paths.src },
|
|
22
|
-
compress: true,
|
|
23
|
-
historyApiFallback: true,
|
|
24
|
-
},
|
|
25
|
-
output: {
|
|
26
|
-
filename: 'bundle.js',
|
|
27
|
-
path: paths.dist,
|
|
28
|
-
publicPath: '/',
|
|
29
|
-
clean: true,
|
|
30
|
-
},
|
|
31
|
-
resolve: {
|
|
32
|
-
modules: ['src', 'node_modules'],
|
|
33
|
-
extensions: ['.ts', '.js', '.
|
|
34
|
-
alias: {
|
|
35
|
-
'@': path.resolve(__dirname, 'src'),
|
|
36
|
-
'@context': path.resolve(__dirname, 'src/context'),
|
|
37
|
-
'@pages': path.resolve(__dirname, 'src/pages'),
|
|
38
|
-
'@widgets': path.resolve(__dirname, 'src/widgets'),
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
plugins: [
|
|
42
|
-
new rspack.HtmlRspackPlugin({
|
|
43
|
-
template: './index.html',
|
|
44
|
-
minify: !isDev
|
|
45
|
-
}),
|
|
46
|
-
new rspack.CssExtractRspackPlugin({
|
|
47
|
-
filename: `css/${isDev ? '[name]' : '[contenthash:8]_[id]'}.css`,
|
|
48
|
-
ignoreOrder: true,
|
|
49
|
-
}),
|
|
50
|
-
],
|
|
51
|
-
module: {
|
|
52
|
-
rules: [
|
|
53
|
-
{
|
|
54
|
-
test: /\.css$/,
|
|
55
|
-
type: 'javascript/auto',
|
|
56
|
-
use: [
|
|
57
|
-
rspack.CssExtractRspackPlugin.loader,
|
|
58
|
-
{ loader: 'css-loader', options: { importLoaders: 1 } },
|
|
59
|
-
],
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
test: /\.
|
|
63
|
-
exclude: /node_modules/,
|
|
64
|
-
use: [
|
|
65
|
-
'babel-loader',
|
|
66
|
-
require.resolve('@helfy/helfy/compiler/
|
|
67
|
-
],
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
test: /\.(ts|js)$/,
|
|
71
|
-
exclude: /node_modules/,
|
|
72
|
-
use: 'babel-loader',
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
},
|
|
76
|
-
};
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const { rspack } = require('@rspack/core');
|
|
3
|
+
|
|
4
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
5
|
+
|
|
6
|
+
const paths = {
|
|
7
|
+
src: path.resolve(__dirname, 'src'),
|
|
8
|
+
dist: path.resolve(__dirname, 'dist'),
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
context: paths.src,
|
|
13
|
+
mode: isDev ? 'development' : 'production',
|
|
14
|
+
entry: './index.ts',
|
|
15
|
+
watchOptions: isDev
|
|
16
|
+
? { ignored: ['**/node_modules/**', '**/.helfy/**'] }
|
|
17
|
+
: {},
|
|
18
|
+
devServer: {
|
|
19
|
+
hot: true,
|
|
20
|
+
port: 3000,
|
|
21
|
+
static: { directory: paths.src },
|
|
22
|
+
compress: true,
|
|
23
|
+
historyApiFallback: true,
|
|
24
|
+
},
|
|
25
|
+
output: {
|
|
26
|
+
filename: 'bundle.js',
|
|
27
|
+
path: paths.dist,
|
|
28
|
+
publicPath: '/',
|
|
29
|
+
clean: true,
|
|
30
|
+
},
|
|
31
|
+
resolve: {
|
|
32
|
+
modules: ['src', 'node_modules'],
|
|
33
|
+
extensions: ['.ts', '.js', '.hf', '.css'],
|
|
34
|
+
alias: {
|
|
35
|
+
'@': path.resolve(__dirname, 'src'),
|
|
36
|
+
'@context': path.resolve(__dirname, 'src/context'),
|
|
37
|
+
'@pages': path.resolve(__dirname, 'src/pages'),
|
|
38
|
+
'@widgets': path.resolve(__dirname, 'src/widgets'),
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
plugins: [
|
|
42
|
+
new rspack.HtmlRspackPlugin({
|
|
43
|
+
template: './index.html',
|
|
44
|
+
minify: !isDev,
|
|
45
|
+
}),
|
|
46
|
+
new rspack.CssExtractRspackPlugin({
|
|
47
|
+
filename: `css/${isDev ? '[name]' : '[contenthash:8]_[id]'}.css`,
|
|
48
|
+
ignoreOrder: true,
|
|
49
|
+
}),
|
|
50
|
+
],
|
|
51
|
+
module: {
|
|
52
|
+
rules: [
|
|
53
|
+
{
|
|
54
|
+
test: /\.css$/,
|
|
55
|
+
type: 'javascript/auto',
|
|
56
|
+
use: [
|
|
57
|
+
rspack.CssExtractRspackPlugin.loader,
|
|
58
|
+
{ loader: 'css-loader', options: { importLoaders: 1 } },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
test: /\.hf$/,
|
|
63
|
+
exclude: /node_modules/,
|
|
64
|
+
use: [
|
|
65
|
+
'babel-loader',
|
|
66
|
+
require.resolve('@helfy/helfy/compiler/hf-loader'),
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
test: /\.(ts|js)$/,
|
|
71
|
+
exclude: /node_modules/,
|
|
72
|
+
use: 'babel-loader',
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { App } from "./App";
|
|
2
|
+
import { ThemeContext } from "@context/ThemeContext";
|
|
3
|
+
import { MainLayout } from "@pages/layouts/MainLayout";
|
|
4
|
+
import { Header } from "@widgets/Header/Header";
|
|
5
|
+
import { NavBar } from "@widgets/Navbar/Navbar";
|
|
6
|
+
import { Footer } from "@widgets/Footer/Footer";
|
|
7
|
+
import { AppRouter } from "@pages/_router";
|
|
8
|
+
|
|
9
|
+
@template<App>() {
|
|
10
|
+
<ThemeContext>
|
|
11
|
+
<div class="app">
|
|
12
|
+
<MainLayout>
|
|
13
|
+
@slot.header -> <Header />
|
|
14
|
+
@slot.sidebar -> <NavBar />
|
|
15
|
+
@slot.content -> <AppRouter />
|
|
16
|
+
@slot.footer -> <Footer />
|
|
17
|
+
</MainLayout>
|
|
18
|
+
</div>
|
|
19
|
+
</ThemeContext>
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Context, state, effect } from "@helfy/helfy";
|
|
2
|
+
|
|
3
|
+
export type TThemeMode = "light" | "dark";
|
|
4
|
+
|
|
5
|
+
@Context
|
|
6
|
+
export class ThemeContext {
|
|
7
|
+
@state mode: TThemeMode = "dark";
|
|
8
|
+
|
|
9
|
+
toggle = () => {
|
|
10
|
+
this.mode = this.mode === "dark" ? "light" : "dark";
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** Keeps class="dark" on <html> in sync with the mode. */
|
|
14
|
+
@effect
|
|
15
|
+
syncToDom() {
|
|
16
|
+
document.documentElement.classList.toggle("dark", this.mode === "dark");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AboutPage } from "./AboutPage";
|
|
2
|
+
|
|
3
|
+
@template<AboutPage>() {
|
|
4
|
+
<div class="page">
|
|
5
|
+
<h1>About</h1>
|
|
6
|
+
<p>This is a Helfy application with routing, slots, a theme context and a lazily loaded page.</p>
|
|
7
|
+
<p>
|
|
8
|
+
This page is a separate chunk loaded on the first visit — see <code>lazy()</code> in
|
|
9
|
+
<code>src/pages/_routes.ts</code>.
|
|
10
|
+
</p>
|
|
11
|
+
</div>
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CounterPage } from "./CounterPage";
|
|
2
|
+
|
|
3
|
+
@template<CounterPage>() {
|
|
4
|
+
<div class="page">
|
|
5
|
+
<h1>Counter</h1>
|
|
6
|
+
<p>Count: <strong>{this.count}</strong> ({this.parity})</p>
|
|
7
|
+
<button class="btn" onclick={this.increment}>Increment</button>
|
|
8
|
+
<button class="btn" onclick={this.decrement}>Decrement</button>
|
|
9
|
+
@if (this.count >= 10) {
|
|
10
|
+
<p>That's a lot of clicks!</p>
|
|
11
|
+
}
|
|
12
|
+
</div>
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { View, state, computed } from "@helfy/helfy";
|
|
2
|
+
import template from "./CounterPage.hf";
|
|
3
|
+
|
|
4
|
+
@View({ template })
|
|
5
|
+
export class CounterPage {
|
|
6
|
+
@state count = 0;
|
|
7
|
+
|
|
8
|
+
@computed
|
|
9
|
+
get parity() {
|
|
10
|
+
return this.count % 2 === 0 ? "even" : "odd";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
increment = () => this.count++;
|
|
14
|
+
decrement = () => this.count--;
|
|
15
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HomePage } from "./HomePage";
|
|
2
|
+
|
|
3
|
+
@template<HomePage>() {
|
|
4
|
+
<div class="page">
|
|
5
|
+
<h1>Welcome to Helfy</h1>
|
|
6
|
+
<p>Helfy is a TypeScript UI framework with decorators, .hf templates and fine-grained reactivity.</p>
|
|
7
|
+
<p>
|
|
8
|
+
Edit <code>src/pages/HomePage.hf</code> and save the file — changes will appear automatically.
|
|
9
|
+
</p>
|
|
10
|
+
<h2>Quick start</h2>
|
|
11
|
+
<p>
|
|
12
|
+
A component is a class with <code>@View</code> (<code>HomePage.ts</code>), its markup lives in the
|
|
13
|
+
<code>.hf</code> template next to it. Open the Counter page to see reactivity in action and toggle the theme in
|
|
14
|
+
the top-right corner.
|
|
15
|
+
</p>
|
|
16
|
+
</div>
|
|
17
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AppRouter } from "./_router";
|
|
2
|
+
import { RouterView } from "@helfy/helfy";
|
|
3
|
+
import { NotFound } from "@widgets/NotFound/NotFound";
|
|
4
|
+
|
|
5
|
+
@template<AppRouter>() {
|
|
6
|
+
@safe {
|
|
7
|
+
<RouterView>
|
|
8
|
+
@slot.notFound({ pathname }) {
|
|
9
|
+
<NotFound pathname={pathname} />
|
|
10
|
+
}
|
|
11
|
+
</RouterView>
|
|
12
|
+
} @wait {
|
|
13
|
+
<p class="page">Loading…</p>
|
|
14
|
+
} @fail (error, retry) {
|
|
15
|
+
<section class="page" role="alert">
|
|
16
|
+
<h2>Something went wrong</h2>
|
|
17
|
+
<p>{error instanceof Error ? error.message : String(error)}</p>
|
|
18
|
+
<button type="button" class="btn" onclick={retry}>Try again</button>
|
|
19
|
+
</section>
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { HomePage } from "./HomePage";
|
|
3
|
-
import { CounterPage } from "./CounterPage";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
{ path: "/", component:
|
|
8
|
-
|
|
9
|
-
{ path: "/about", component: AboutPage },
|
|
10
|
-
];
|
|
1
|
+
import { lazy, type RouteConfig } from "@helfy/helfy";
|
|
2
|
+
import { HomePage } from "./HomePage";
|
|
3
|
+
import { CounterPage } from "./CounterPage";
|
|
4
|
+
|
|
5
|
+
export const routes: RouteConfig[] = [
|
|
6
|
+
{ path: "/", component: HomePage },
|
|
7
|
+
{ path: "/counter", component: CounterPage },
|
|
8
|
+
// A separate chunk, loaded on the first visit.
|
|
9
|
+
{ path: "/about", component: lazy(() => import("./AboutPage").then((m) => m.AboutPage)) },
|
|
10
|
+
];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { MainLayout } from "./MainLayout";
|
|
2
|
+
|
|
3
|
+
@template<MainLayout>() {
|
|
4
|
+
<div class="layout">
|
|
5
|
+
<header class="header">
|
|
6
|
+
@slot:header()
|
|
7
|
+
</header>
|
|
8
|
+
<div class="layout__body">
|
|
9
|
+
<aside class="sidebar">
|
|
10
|
+
@slot:sidebar()
|
|
11
|
+
</aside>
|
|
12
|
+
<main class="main">
|
|
13
|
+
<div class="main__content">
|
|
14
|
+
@slot:content()
|
|
15
|
+
</div>
|
|
16
|
+
</main>
|
|
17
|
+
</div>
|
|
18
|
+
<footer class="footer">
|
|
19
|
+
@slot:footer() fallback {
|
|
20
|
+
<span>© 2026</span>
|
|
21
|
+
}
|
|
22
|
+
</footer>
|
|
23
|
+
</div>
|
|
24
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Header } from "./Header";
|
|
2
|
+
import { Link } from "@helfy/helfy";
|
|
3
|
+
import { ThemeToggle } from "@widgets/ThemeToggle/ThemeToggle";
|
|
4
|
+
|
|
5
|
+
@template<Header>() {
|
|
6
|
+
<div class="header__inner">
|
|
7
|
+
<Link to="/" label="Helfy" class="header__logo" />
|
|
8
|
+
<nav class="nav">
|
|
9
|
+
@for (link of this.links; track link.to) {
|
|
10
|
+
<Link to={link.to} label={link.label} class={this.pathname === link.to ? "nav-link--active" : ""} />
|
|
11
|
+
}
|
|
12
|
+
</nav>
|
|
13
|
+
<ThemeToggle />
|
|
14
|
+
</div>
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { NavBar } from "./Navbar";
|
|
2
|
+
import { Link } from "@helfy/helfy";
|
|
3
|
+
|
|
4
|
+
@template<NavBar>() {
|
|
5
|
+
<nav class="sidebar-nav">
|
|
6
|
+
@for (link of this.links; track link.to) {
|
|
7
|
+
<Link
|
|
8
|
+
to={link.to}
|
|
9
|
+
label={link.label}
|
|
10
|
+
class={this.pathname === link.to ? "nav-link nav-link--active" : "nav-link"}
|
|
11
|
+
/>
|
|
12
|
+
}
|
|
13
|
+
</nav>
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { NotFound } from "./NotFound";
|
|
2
|
+
import { Link } from "@helfy/helfy";
|
|
3
|
+
|
|
4
|
+
@template<NotFound>() {
|
|
5
|
+
<section class="not-found">
|
|
6
|
+
<div class="not-found__code">404</div>
|
|
7
|
+
<h1 class="not-found__title">Oops! Page not found</h1>
|
|
8
|
+
<p class="not-found__text">The requested address does not exist or has been moved.</p>
|
|
9
|
+
@if (this.props.pathname) {
|
|
10
|
+
<p class="not-found__path">{this.props.pathname}</p>
|
|
11
|
+
}
|
|
12
|
+
<Link to="/" label="Back to home" class="btn btn--primary" />
|
|
13
|
+
</section>
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { View } from "@helfy/helfy";
|
|
2
|
+
import template from "./NotFound.hf";
|
|
3
|
+
|
|
4
|
+
export interface Props {
|
|
5
|
+
pathname?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Custom 404 page — overrides the RouterView fallback. */
|
|
9
|
+
@View({ template })
|
|
10
|
+
export class NotFound {
|
|
11
|
+
constructor(readonly props: Props = {}) {}
|
|
12
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ThemeToggle } from "./ThemeToggle";
|
|
2
|
+
|
|
3
|
+
@template<ThemeToggle>() {
|
|
4
|
+
<button
|
|
5
|
+
type="button"
|
|
6
|
+
class="theme-toggle"
|
|
7
|
+
onclick={() => this.toggle()}
|
|
8
|
+
aria-label={this.mode === "dark" ? "Switch to light theme" : "Switch to dark theme"}
|
|
9
|
+
>
|
|
10
|
+
@if (this.mode === "dark") {
|
|
11
|
+
<span>Light</span>
|
|
12
|
+
} @else {
|
|
13
|
+
<span>Dark</span>
|
|
14
|
+
}
|
|
15
|
+
</button>
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { View, useCtx } from "@helfy/helfy";
|
|
2
|
+
import { ThemeContext } from "@context/ThemeContext";
|
|
3
|
+
import template from "./ThemeToggle.hf";
|
|
4
|
+
|
|
5
|
+
@View({ template })
|
|
6
|
+
export class ThemeToggle {
|
|
7
|
+
@useCtx(ThemeContext, "toggle")
|
|
8
|
+
toggle!: ThemeContext["toggle"];
|
|
9
|
+
|
|
10
|
+
@useCtx(ThemeContext, "mode")
|
|
11
|
+
mode!: ThemeContext["mode"];
|
|
12
|
+
}
|
package/template/tsconfig.json
CHANGED
|
@@ -7,10 +7,8 @@
|
|
|
7
7
|
"noEmit": true,
|
|
8
8
|
"resolveJsonModule": true,
|
|
9
9
|
"skipLibCheck": true,
|
|
10
|
-
"jsx": "preserve",
|
|
11
10
|
"experimentalDecorators": true,
|
|
12
11
|
"emitDecoratorMetadata": true,
|
|
13
|
-
"plugins": [{ "name": "@helfy/helfy-ts-plugin" }],
|
|
14
12
|
"paths": {
|
|
15
13
|
"@context/*": ["./context/*"],
|
|
16
14
|
"@pages/*": ["./pages/*"],
|
package/template/src/App.tsx
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { View } from "@helfy/helfy";
|
|
2
|
-
import { ThemeContext, ThemeSync } from "@context/ThemeContext";
|
|
3
|
-
import { MainLayout } from "@pages/layouts/MainLayout";
|
|
4
|
-
import { Header } from "@widgets/Header/Header";
|
|
5
|
-
import { NavBar } from "@widgets/Navbar/Navbar";
|
|
6
|
-
import { Footer } from "@widgets/Footer/Footer";
|
|
7
|
-
import { AppRouter } from "@pages/_router";
|
|
8
|
-
|
|
9
|
-
@View
|
|
10
|
-
export class App {
|
|
11
|
-
render() {
|
|
12
|
-
return (
|
|
13
|
-
<ThemeContext>
|
|
14
|
-
<ThemeSync />
|
|
15
|
-
<div class="app">
|
|
16
|
-
<MainLayout>
|
|
17
|
-
@slot.header() {<Header />}
|
|
18
|
-
@slot.sidebar() {<NavBar />}
|
|
19
|
-
@slot.content() {<AppRouter />}
|
|
20
|
-
@slot.footer() {<Footer />}
|
|
21
|
-
</MainLayout>
|
|
22
|
-
</div>
|
|
23
|
-
</ThemeContext>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
}
|