fusee-framework 1.5.3 β 1.7.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 +30 -13
- package/app/benchmark/benchmark.js +633 -0
- package/app/benchmark/index.html +102 -0
- package/app/benchmark/styles.css +249 -0
- package/app/components/Counter.js +17 -7
- package/app/components/Counter.ts +1 -1
- package/app/components/CounterSection.js +3 -4
- package/app/main.js +29 -62
- package/app/main.ts +72 -63
- package/app/pages/404.js +16 -0
- package/app/pages/[...all].js +35 -0
- package/app/pages/[...all].ts +35 -0
- package/app/pages/_layout.js +26 -0
- package/app/pages/_layout.ts +26 -0
- package/app/pages/about.js +23 -0
- package/app/pages/about.ts +29 -0
- package/app/pages/composables.js +158 -0
- package/app/pages/composables.ts +158 -0
- package/app/pages/dynamic.js +40 -0
- package/app/pages/dynamic.ts +40 -0
- package/app/pages/index.js +117 -0
- package/app/pages/index.ts +78 -0
- package/app/pages/login.ts +21 -0
- package/app/pages/objects/[id].js +56 -0
- package/app/pages/objects/[id].ts +56 -0
- package/app/pages/posts/[category]/[slug].js +56 -0
- package/app/pages/posts/[category]/[slug].ts +56 -0
- package/app/pages/router-example.js +56 -0
- package/app/pages/router-example.ts +56 -0
- package/app/pages/test-for.js +65 -0
- package/app/pages/test-for.ts +80 -0
- package/app/pages/users/[id].js +24 -0
- package/app/pages/users/[id].ts +46 -0
- package/app/pages/users/index.js +22 -0
- package/app/pages/users/index.ts +29 -0
- package/app/pages/users.js +43 -0
- package/app/pages/users.ts +78 -0
- package/app/routes/About.js +1 -1
- package/app/routes/About.ts +1 -1
- package/app/routes/AuthRoutes.ts +41 -0
- package/app/routes/Home.js +9 -0
- package/app/routes/NotFound.js +16 -0
- package/app/routes/NotFound.ts +16 -0
- package/app/routes/RouterExample.js +56 -0
- package/app/routes/RouterExample.ts +56 -0
- package/app/routes/WildcardDemo.js +35 -0
- package/app/routes/WildcardDemo.ts +35 -0
- package/auto-imports.d.ts +1 -0
- package/bin/create-fusee-linux +0 -0
- package/bin/create-fusee-mac-arm64 +0 -0
- package/bin/create-fusee-mac-intel +0 -0
- package/bin/create-fusee-win.exe +0 -0
- package/bin/create-fusee.exe +0 -0
- package/bin/index.js +38 -0
- package/create-fusee-go/cmd/generate.go +72 -0
- package/create-fusee-go/cmd/init.go +38 -0
- package/create-fusee-go/cmd/root.go +121 -0
- package/create-fusee-go/go.mod +10 -0
- package/create-fusee-go/go.sum +10 -0
- package/create-fusee-go/internal/assets/assets.go +78 -0
- package/create-fusee-go/internal/assets/embed/framework/auto-import-preset.js +9 -0
- package/create-fusee-go/internal/assets/embed/framework/core/compiler.js +253 -0
- package/create-fusee-go/internal/assets/embed/framework/core/component.js +266 -0
- package/create-fusee-go/internal/assets/embed/framework/core/composable.js +27 -0
- package/create-fusee-go/internal/assets/embed/framework/core/directives.js +875 -0
- package/create-fusee-go/internal/assets/embed/framework/core/evaluator.js +153 -0
- package/create-fusee-go/internal/assets/embed/framework/core/event-delegation.js +191 -0
- package/create-fusee-go/internal/assets/embed/framework/core/signal.js +412 -0
- package/create-fusee-go/internal/assets/embed/framework/core/store.js +81 -0
- package/create-fusee-go/internal/assets/embed/framework/index.d.ts +3 -0
- package/create-fusee-go/internal/assets/embed/framework/index.js +10 -0
- package/create-fusee-go/internal/assets/embed/framework/router/file-router.js +177 -0
- package/create-fusee-go/internal/assets/embed/framework/router/router.js +462 -0
- package/create-fusee-go/internal/assets/embed/framework/types/compiler.d.ts +15 -0
- package/create-fusee-go/internal/assets/embed/framework/types/component.d.ts +88 -0
- package/create-fusee-go/internal/assets/embed/framework/types/composable.d.ts +14 -0
- package/create-fusee-go/internal/assets/embed/framework/types/directives.d.ts +55 -0
- package/create-fusee-go/internal/assets/embed/framework/types/evaluator.d.ts +13 -0
- package/create-fusee-go/internal/assets/embed/framework/types/event-delegation.d.ts +32 -0
- package/create-fusee-go/internal/assets/embed/framework/types/index.d.ts +22 -0
- package/create-fusee-go/internal/assets/embed/framework/types/router.d.ts +32 -0
- package/create-fusee-go/internal/assets/embed/framework/types/signal.d.ts +79 -0
- package/create-fusee-go/internal/assets/embed/framework/types/store.d.ts +8 -0
- package/create-fusee-go/internal/assets/templates/Counter.tmpl +26 -0
- package/create-fusee-go/internal/assets/templates/component.tmpl +11 -0
- package/create-fusee-go/internal/assets/templates/components/Loading.tmpl +24 -0
- package/create-fusee-go/internal/assets/templates/index.html.tmpl +15 -0
- package/create-fusee-go/internal/assets/templates/main.tmpl +14 -0
- package/create-fusee-go/internal/assets/templates/package.json.tmpl +14 -0
- package/create-fusee-go/internal/assets/templates/page.tmpl +12 -0
- package/create-fusee-go/internal/assets/templates/pages/about.tmpl +20 -0
- package/create-fusee-go/internal/assets/templates/pages/index.tmpl +22 -0
- package/create-fusee-go/internal/assets/templates/pages/layout.tmpl +18 -0
- package/create-fusee-go/internal/assets/templates/tsconfig.json.tmpl +14 -0
- package/create-fusee-go/internal/assets/templates/vite.config.js.tmpl +18 -0
- package/create-fusee-go/main.go +7 -0
- package/framework/auto-import-preset.js +1 -1
- package/framework/core/compiler.js +252 -249
- package/framework/core/component.js +265 -260
- package/framework/core/directives.js +875 -632
- package/framework/core/evaluator.js +153 -70
- package/framework/core/event-delegation.js +191 -0
- package/framework/core/signal.js +412 -404
- package/framework/index.js +10 -11
- package/framework/router/file-router.js +177 -0
- package/framework/router/router.js +462 -80
- package/framework/types/component.d.ts +0 -4
- package/framework/types/event-delegation.d.ts +32 -0
- package/framework/types/index.d.ts +3 -0
- package/framework/types/router.d.ts +32 -15
- package/framework/types/signal.d.ts +1 -0
- package/index.html +638 -563
- package/package.json +5 -3
- package/tsconfig.json +1 -0
- /package/{framework β create-fusee-go/internal/assets/embed/framework}/bin/create-fusee.js +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { currentRoute, navigate } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
export const WildcardDemo = defineComponent({
|
|
4
|
+
setup() {
|
|
5
|
+
const path = currentRoute
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
path,
|
|
9
|
+
navigate,
|
|
10
|
+
template: `
|
|
11
|
+
<div class="page">
|
|
12
|
+
<h1 style="color: #f59e0b;">π― Wildcard Match!</h1>
|
|
13
|
+
<p>This route matched via wildcard <code>path: '*'</code></p>
|
|
14
|
+
|
|
15
|
+
<div class="demo-card">
|
|
16
|
+
<h2>Current Path</h2>
|
|
17
|
+
<code style="color: #f59e0b; font-size: 1.5rem;">{{ path }}</code>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="demo-card">
|
|
21
|
+
<h2>Test Wildcard</h2>
|
|
22
|
+
<p>Any path works here:</p>
|
|
23
|
+
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
|
24
|
+
<button @click="navigate('/random-123')" class="cmp-btn">/random-123</button>
|
|
25
|
+
<button @click="navigate('/test/abc')" class="cmp-btn">/test/abc</button>
|
|
26
|
+
<button @click="navigate('/deep/nested/path')" class="cmp-btn">/deep/nested/path</button>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<button @click="navigate('/')" class="primary-btn">Back Home</button>
|
|
31
|
+
</div>
|
|
32
|
+
`
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { currentRoute, navigate } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
export const WildcardDemo = defineComponent({
|
|
4
|
+
setup() {
|
|
5
|
+
const path = currentRoute
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
path,
|
|
9
|
+
navigate,
|
|
10
|
+
template: `
|
|
11
|
+
<div class="page">
|
|
12
|
+
<h1 style="color: #f59e0b;">π― Wildcard Match!</h1>
|
|
13
|
+
<p>This route matched via wildcard <code>path: '*'</code></p>
|
|
14
|
+
|
|
15
|
+
<div class="demo-card">
|
|
16
|
+
<h2>Current Path</h2>
|
|
17
|
+
<code style="color: #f59e0b; font-size: 1.5rem;">{{ path }}</code>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="demo-card">
|
|
21
|
+
<h2>Test Wildcard</h2>
|
|
22
|
+
<p>Any path works here:</p>
|
|
23
|
+
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
|
24
|
+
<button @click="navigate('/random-123')" class="cmp-btn">/random-123</button>
|
|
25
|
+
<button @click="navigate('/test/abc')" class="cmp-btn">/test/abc</button>
|
|
26
|
+
<button @click="navigate('/deep/nested/path')" class="cmp-btn">/deep/nested/path</button>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<button @click="navigate('/')" class="primary-btn">Back Home</button>
|
|
31
|
+
</div>
|
|
32
|
+
`
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineComponent } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
export const RootLayout = defineComponent({
|
|
4
|
+
setup() {
|
|
5
|
+
return {
|
|
6
|
+
template: `
|
|
7
|
+
<div class="root-layout">
|
|
8
|
+
<nav>
|
|
9
|
+
<a href="/" f-link>Home</a>
|
|
10
|
+
<a href="/about" f-link>About</a>
|
|
11
|
+
<a href="/test-for" f-link>Loops Test</a>
|
|
12
|
+
<a href="/dynamic" f-link style="color: #ec4899;">Dynamic Components</a>
|
|
13
|
+
<a href="/composables" f-link style="color: #34d399;">Composables</a>
|
|
14
|
+
<a href="/users" f-link style="color: #f59e0b;">π₯ Users (Nested)</a>
|
|
15
|
+
<a href="/router-example" f-link style="color: #8b8bff;">π Router Example</a>
|
|
16
|
+
</nav>
|
|
17
|
+
|
|
18
|
+
<main class="content-wrap">
|
|
19
|
+
<!-- This is where all pages will be rendered -->
|
|
20
|
+
<div data-router-view></div>
|
|
21
|
+
</main>
|
|
22
|
+
</div>
|
|
23
|
+
`
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineComponent } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
export const RootLayout = defineComponent({
|
|
4
|
+
setup() {
|
|
5
|
+
return {
|
|
6
|
+
template: `
|
|
7
|
+
<div class="root-layout">
|
|
8
|
+
<nav>
|
|
9
|
+
<a href="/" f-link>Home</a>
|
|
10
|
+
<a href="/about" f-link>About</a>
|
|
11
|
+
<a href="/test-for" f-link>Loops Test</a>
|
|
12
|
+
<a href="/dynamic" f-link style="color: #ec4899;">Dynamic Components</a>
|
|
13
|
+
<a href="/composables" f-link style="color: #34d399;">Composables</a>
|
|
14
|
+
<a href="/users" f-link style="color: #f59e0b;">π₯ Users (Nested)</a>
|
|
15
|
+
<a href="/router-example" f-link style="color: #8b8bff;">π Router Example</a>
|
|
16
|
+
</nav>
|
|
17
|
+
|
|
18
|
+
<main class="content-wrap">
|
|
19
|
+
<!-- This is where all pages will be rendered -->
|
|
20
|
+
<div data-router-view></div>
|
|
21
|
+
</main>
|
|
22
|
+
</div>
|
|
23
|
+
`
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const About = defineComponent({
|
|
2
|
+
props: [],
|
|
3
|
+
setup() {
|
|
4
|
+
const loaded = signal(false)
|
|
5
|
+
inspect(loaded)
|
|
6
|
+
|
|
7
|
+
onMount(() => {
|
|
8
|
+
setTimeout(() => loaded(true), 300)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
loaded,
|
|
13
|
+
template: `
|
|
14
|
+
<div class="page">
|
|
15
|
+
<h1>About</h1>
|
|
16
|
+
<p>Framework custom β SPA prototipe</p>
|
|
17
|
+
<p><strong>Features:</strong> signals, defineComponent, History API router, lifecycle hooks.</p>
|
|
18
|
+
<p class="status">{{ loaded }}</p>
|
|
19
|
+
</div>
|
|
20
|
+
`
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Signal, EmptyProps } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
type AboutResult = {
|
|
4
|
+
loaded: Signal<boolean>
|
|
5
|
+
template: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const About = defineComponent({
|
|
9
|
+
props: [],
|
|
10
|
+
setup(): AboutResult {
|
|
11
|
+
const loaded = signal<boolean>(false)
|
|
12
|
+
|
|
13
|
+
onMount(() => {
|
|
14
|
+
setTimeout(() => loaded(true), 300)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
loaded,
|
|
19
|
+
template: `
|
|
20
|
+
<div class="page">
|
|
21
|
+
<h1>About</h1>
|
|
22
|
+
<p>Framework custom β prototip SPA.</p>
|
|
23
|
+
<p><strong>Features:</strong> signals, defineComponent, History API router, lifecycle hooks.</p>
|
|
24
|
+
<p class="status">{{ loaded }}</p>
|
|
25
|
+
</div>
|
|
26
|
+
`
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
})
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { counterLogic, mouseTracker } from '../composables/demo.js'
|
|
2
|
+
import { useFetch } from '../composables/useFetch.js'
|
|
3
|
+
import { useLocalStorage } from '../composables/useLocalStorage.js'
|
|
4
|
+
|
|
5
|
+
export const TestComposables = defineComponent({
|
|
6
|
+
setup() {
|
|
7
|
+
// ββ 1. counterLogic β pure signal composable, no context needed ββββββ
|
|
8
|
+
const counter = counterLogic(0, 1)
|
|
9
|
+
const bigCounter = counterLogic(100, 10)
|
|
10
|
+
|
|
11
|
+
// ββ 2. mouseTracker β lifecycle composable (needs setup context) βββββ
|
|
12
|
+
const mouse = mouseTracker()
|
|
13
|
+
|
|
14
|
+
// ββ 3. useLocalStorage β persists across page reloads ββββββββββββββββ
|
|
15
|
+
const theme = useLocalStorage('demo-theme', 'dark')
|
|
16
|
+
const nickname = useLocalStorage('demo-nickname', '')
|
|
17
|
+
|
|
18
|
+
const toggleTheme = () => theme(theme() === 'dark' ? 'light' : 'dark')
|
|
19
|
+
|
|
20
|
+
// ββ 4. useFetch β reactive data fetching βββββββββββββββββββββββββββββ
|
|
21
|
+
const { data: posts, loading, error } = useFetch(
|
|
22
|
+
'https://jsonplaceholder.typicode.com/posts?_limit=3'
|
|
23
|
+
)
|
|
24
|
+
// ββ 5. Custom Directives Test ββββββββββββββββββββββββββββββββββββββββ
|
|
25
|
+
const hColor = signal('#8b8bff33')
|
|
26
|
+
const colors = ['#8b8bff33', '#10b98133', '#f59e0b33', '#ef444433']
|
|
27
|
+
let colorIdx = 0
|
|
28
|
+
const nextColor = () => hColor(colors[++colorIdx % colors.length])
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
hColor,
|
|
32
|
+
nextColor,
|
|
33
|
+
|
|
34
|
+
// counter
|
|
35
|
+
count: counter.count,
|
|
36
|
+
doubled: counter.doubled,
|
|
37
|
+
isEven: counter.isEven,
|
|
38
|
+
increment: counter.increment,
|
|
39
|
+
decrement: counter.decrement,
|
|
40
|
+
reset: counter.reset,
|
|
41
|
+
|
|
42
|
+
// big counter
|
|
43
|
+
bigCount: bigCounter.count,
|
|
44
|
+
bigIncrement: bigCounter.increment,
|
|
45
|
+
bigDecrement: bigCounter.decrement,
|
|
46
|
+
|
|
47
|
+
// mouse
|
|
48
|
+
mouseX: mouse.x,
|
|
49
|
+
mouseY: mouse.y,
|
|
50
|
+
|
|
51
|
+
// localStorage
|
|
52
|
+
theme,
|
|
53
|
+
nickname,
|
|
54
|
+
toggleTheme,
|
|
55
|
+
|
|
56
|
+
// fetch
|
|
57
|
+
posts,
|
|
58
|
+
loading,
|
|
59
|
+
error,
|
|
60
|
+
|
|
61
|
+
template: `
|
|
62
|
+
<div class="page">
|
|
63
|
+
<h1>π§© Composables Demo</h1>
|
|
64
|
+
<p style="color:#888; margin-bottom: 2rem;">
|
|
65
|
+
Each section below is powered by a <code style="color:#ff3333">defineComposable()</code> function.
|
|
66
|
+
No naming rules β just reactive logic.
|
|
67
|
+
</p>
|
|
68
|
+
|
|
69
|
+
<!-- ββ 1. counterLogic βββββββββββββββββββββββββββββββββββββββ -->
|
|
70
|
+
<section class="demo-card">
|
|
71
|
+
<h2>π¦ counterLogic(0, step=1)</h2>
|
|
72
|
+
<p class="demo-desc">Pure signal composable β works without setup() context.</p>
|
|
73
|
+
<div class="counter-display">
|
|
74
|
+
<span class="big-num" :class="isEven ? 'even' : 'odd'">{{ count }}</span>
|
|
75
|
+
<span class="derived-label">Γ 2 = {{ doubled }}</span>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="btn-row">
|
|
78
|
+
<button @click="decrement" class="cmp-btn minus">β</button>
|
|
79
|
+
<button @click="reset" class="cmp-btn reset">βΊ</button>
|
|
80
|
+
<button @click="increment" class="cmp-btn plus">+</button>
|
|
81
|
+
</div>
|
|
82
|
+
<p class="badge" :class="isEven ? 'badge-blue' : 'badge-red'">{{ isEven ? 'Even' : 'Odd' }}</p>
|
|
83
|
+
</section>
|
|
84
|
+
|
|
85
|
+
<!-- ββ 1b. Second independent counter (step=10) βββββββββββββ -->
|
|
86
|
+
<section class="demo-card">
|
|
87
|
+
<h2>π¦ counterLogic(100, step=10)</h2>
|
|
88
|
+
<p class="demo-desc">Same composable, different instance. Fully isolated state.</p>
|
|
89
|
+
<div class="counter-display">
|
|
90
|
+
<span class="big-num">{{ bigCount }}</span>
|
|
91
|
+
</div>
|
|
92
|
+
<div class="btn-row">
|
|
93
|
+
<button @click="bigDecrement" class="cmp-btn minus">β10</button>
|
|
94
|
+
<button @click="bigIncrement" class="cmp-btn plus">+10</button>
|
|
95
|
+
</div>
|
|
96
|
+
</section>
|
|
97
|
+
|
|
98
|
+
<!-- ββ 2. mouseTracker ββββββββββββββββββββββββββββββββββββββ -->
|
|
99
|
+
<section class="demo-card">
|
|
100
|
+
<h2>π±οΈ mouseTracker()</h2>
|
|
101
|
+
<p class="demo-desc">Lifecycle composable β registers window listener on mount, cleans up on unmount.</p>
|
|
102
|
+
<div class="mouse-display">
|
|
103
|
+
<span>X: <strong>{{ mouseX }}</strong></span>
|
|
104
|
+
<span>Y: <strong>{{ mouseY }}</strong></span>
|
|
105
|
+
</div>
|
|
106
|
+
</section>
|
|
107
|
+
|
|
108
|
+
<!-- ββ 3. useLocalStorage βββββββββββββββββββββββββββββββββββ -->
|
|
109
|
+
<section class="demo-card">
|
|
110
|
+
<h2>πΎ useLocalStorage()</h2>
|
|
111
|
+
<p class="demo-desc">State persists across page reloads. Try changing the nickname, then refresh.</p>
|
|
112
|
+
<div style="margin-bottom: 12px;">
|
|
113
|
+
<input f-model="nickname"
|
|
114
|
+
class="cmp-input"
|
|
115
|
+
placeholder="Your nicknameβ¦" />
|
|
116
|
+
<p style="margin-top: 8px; color: #888; font-size: 0.85rem;">
|
|
117
|
+
Stored: <strong style="color:#ff3333">{{ nickname || '(empty)' }}</strong>
|
|
118
|
+
</p>
|
|
119
|
+
</div>
|
|
120
|
+
<button @click="toggleTheme" class="cmp-btn reset">
|
|
121
|
+
Theme: {{ theme }}
|
|
122
|
+
</button>
|
|
123
|
+
</section>
|
|
124
|
+
|
|
125
|
+
<!-- ββ 4. useFetch ββββββββββββββββββββββββββββββββββββββββββ -->
|
|
126
|
+
<section class="demo-card">
|
|
127
|
+
<h2>π useFetch()</h2>
|
|
128
|
+
<p class="demo-desc">Fetches from JSONPlaceholder. Loading state handled reactively.</p>
|
|
129
|
+
<div f-if="loading">
|
|
130
|
+
<p style="color: #888;">β³ Loading postsβ¦</p>
|
|
131
|
+
</div>
|
|
132
|
+
<div f-elif="error">
|
|
133
|
+
<p style="color: #ff6b6b;">β {{ error }}</p>
|
|
134
|
+
</div>
|
|
135
|
+
<div f-else>
|
|
136
|
+
<div f-for="post in posts" style="margin-bottom: 10px; padding: 10px; background:#1a1a24; border-radius:6px; border-left: 3px solid #ff3333;">
|
|
137
|
+
<strong style="color:#ff9999">{{ post.title }}</strong>
|
|
138
|
+
<p style="margin-top: 4px; font-size: 0.8rem; color: #666;">{{ post.body }}</p>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
</section>
|
|
142
|
+
|
|
143
|
+
<!-- ββ 6. Custom Directives ββββββββββββββββββββββββββββββββββββββ -->
|
|
144
|
+
<section class="demo-card">
|
|
145
|
+
<h2>π οΈ Custom Directives</h2>
|
|
146
|
+
<p class="demo-desc">Testing <code>f-focus</code> and <code>f-highlight</code>.</p>
|
|
147
|
+
|
|
148
|
+
<div f-highlight="hColor" style="padding: 15px; border-radius: 8px; margin-bottom: 10px;">
|
|
149
|
+
<input f-focus class="cmp-input" placeholder="I have f-focus!" />
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<button @click="nextColor" class="cmp-btn plus">Change Highlight Color</button>
|
|
153
|
+
</section>
|
|
154
|
+
</div>
|
|
155
|
+
`
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
})
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { defineComponent } from '../../framework/index.js'
|
|
2
|
+
import { counterLogic, mouseTracker } from '../composables/demo.js'
|
|
3
|
+
import { useFetch } from '../composables/useFetch.js'
|
|
4
|
+
import { useLocalStorage } from '../composables/useLocalStorage.js'
|
|
5
|
+
|
|
6
|
+
export const TestComposables = defineComponent({
|
|
7
|
+
setup() {
|
|
8
|
+
// ββ 1. counterLogic β pure signal composable, no context needed ββββββ
|
|
9
|
+
const counter = counterLogic(0, 1)
|
|
10
|
+
const bigCounter = counterLogic(100, 10)
|
|
11
|
+
|
|
12
|
+
// ββ 2. mouseTracker β lifecycle composable (needs setup context) βββββ
|
|
13
|
+
const mouse = mouseTracker()
|
|
14
|
+
|
|
15
|
+
// ββ 3. useLocalStorage β persists across page reloads ββββββββββββββββ
|
|
16
|
+
const theme = useLocalStorage<string>('demo-theme', 'dark')
|
|
17
|
+
const nickname = useLocalStorage<string>('demo-nickname', '')
|
|
18
|
+
|
|
19
|
+
const toggleTheme = () => theme(theme() === 'dark' ? 'light' : 'dark')
|
|
20
|
+
|
|
21
|
+
// ββ 4. useFetch β reactive data fetching βββββββββββββββββββββββββββββ
|
|
22
|
+
const { data: posts, loading, error } = useFetch(
|
|
23
|
+
'https://jsonplaceholder.typicode.com/posts?_limit=3'
|
|
24
|
+
)
|
|
25
|
+
// ββ 5. Custom Directives Test ββββββββββββββββββββββββββββββββββββββββ
|
|
26
|
+
const hColor = signal('#8b8bff33')
|
|
27
|
+
const colors = ['#8b8bff33', '#10b98133', '#f59e0b33', '#ef444433']
|
|
28
|
+
let colorIdx = 0
|
|
29
|
+
const nextColor = () => hColor(colors[++colorIdx % colors.length])
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
hColor,
|
|
33
|
+
nextColor,
|
|
34
|
+
|
|
35
|
+
// counter
|
|
36
|
+
count: counter.count,
|
|
37
|
+
doubled: counter.doubled,
|
|
38
|
+
isEven: counter.isEven,
|
|
39
|
+
increment: counter.increment,
|
|
40
|
+
decrement: counter.decrement,
|
|
41
|
+
reset: counter.reset,
|
|
42
|
+
|
|
43
|
+
// big counter
|
|
44
|
+
bigCount: bigCounter.count,
|
|
45
|
+
bigIncrement: bigCounter.increment,
|
|
46
|
+
bigDecrement: bigCounter.decrement,
|
|
47
|
+
|
|
48
|
+
// mouse
|
|
49
|
+
mouseX: mouse.x,
|
|
50
|
+
mouseY: mouse.y,
|
|
51
|
+
|
|
52
|
+
// localStorage
|
|
53
|
+
theme,
|
|
54
|
+
nickname,
|
|
55
|
+
toggleTheme,
|
|
56
|
+
|
|
57
|
+
// fetch
|
|
58
|
+
posts,
|
|
59
|
+
loading,
|
|
60
|
+
error,
|
|
61
|
+
|
|
62
|
+
template: `
|
|
63
|
+
<div class="page">
|
|
64
|
+
<h1>π§© Composables Demo</h1>
|
|
65
|
+
<p style="color:#888; margin-bottom: 2rem;">
|
|
66
|
+
Each section below is powered by a <code style="color:#ff3333">defineComposable()</code> function.
|
|
67
|
+
No naming rules β just reactive logic.
|
|
68
|
+
</p>
|
|
69
|
+
|
|
70
|
+
<!-- ββ 1. counterLogic βββββββββββββββββββββββββββββββββββββββ -->
|
|
71
|
+
<section class="demo-card">
|
|
72
|
+
<h2>π¦ counterLogic(0, step=1)</h2>
|
|
73
|
+
<p class="demo-desc">Pure signal composable β works without setup() context.</p>
|
|
74
|
+
<div class="counter-display">
|
|
75
|
+
<span class="big-num" :class="isEven ? 'even' : 'odd'">{{ count }}</span>
|
|
76
|
+
<span class="derived-label">Γ 2 = {{ doubled }}</span>
|
|
77
|
+
</div>
|
|
78
|
+
<div class="btn-row">
|
|
79
|
+
<button @click="decrement" class="cmp-btn minus">β</button>
|
|
80
|
+
<button @click="reset" class="cmp-btn reset">βΊ</button>
|
|
81
|
+
<button @click="increment" class="cmp-btn plus">+</button>
|
|
82
|
+
</div>
|
|
83
|
+
<p class="badge" :class="isEven ? 'badge-blue' : 'badge-red'">{{ isEven ? 'Even' : 'Odd' }}</p>
|
|
84
|
+
</section>
|
|
85
|
+
|
|
86
|
+
<!-- ββ 1b. Second independent counter (step=10) βββββββββββββ -->
|
|
87
|
+
<section class="demo-card">
|
|
88
|
+
<h2>π¦ counterLogic(100, step=10)</h2>
|
|
89
|
+
<p class="demo-desc">Same composable, different instance. Fully isolated state.</p>
|
|
90
|
+
<div class="counter-display">
|
|
91
|
+
<span class="big-num">{{ bigCount }}</span>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="btn-row">
|
|
94
|
+
<button @click="bigDecrement" class="cmp-btn minus">β10</button>
|
|
95
|
+
<button @click="bigIncrement" class="cmp-btn plus">+10</button>
|
|
96
|
+
</div>
|
|
97
|
+
</section>
|
|
98
|
+
|
|
99
|
+
<!-- ββ 2. mouseTracker ββββββββββββββββββββββββββββββββββββββ -->
|
|
100
|
+
<section class="demo-card">
|
|
101
|
+
<h2>π±οΈ mouseTracker()</h2>
|
|
102
|
+
<p class="demo-desc">Lifecycle composable β registers window listener on mount, cleans up on unmount.</p>
|
|
103
|
+
<div class="mouse-display">
|
|
104
|
+
<span>X: <strong>{{ mouseX }}</strong></span>
|
|
105
|
+
<span>Y: <strong>{{ mouseY }}</strong></span>
|
|
106
|
+
</div>
|
|
107
|
+
</section>
|
|
108
|
+
|
|
109
|
+
<!-- ββ 3. useLocalStorage βββββββββββββββββββββββββββββββββββ -->
|
|
110
|
+
<section class="demo-card">
|
|
111
|
+
<h2>πΎ useLocalStorage()</h2>
|
|
112
|
+
<p class="demo-desc">State persists across page reloads. Try changing the nickname, then refresh.</p>
|
|
113
|
+
<div style="margin-bottom: 12px;">
|
|
114
|
+
<input f-model="nickname"
|
|
115
|
+
class="cmp-input"
|
|
116
|
+
placeholder="Your nicknameβ¦" />
|
|
117
|
+
<p style="margin-top: 8px; color: #888; font-size: 0.85rem;">
|
|
118
|
+
Stored: <strong style="color:#ff3333">{{ nickname || '(empty)' }}</strong>
|
|
119
|
+
</p>
|
|
120
|
+
</div>
|
|
121
|
+
<button @click="toggleTheme" class="cmp-btn reset">
|
|
122
|
+
Theme: {{ theme }}
|
|
123
|
+
</button>
|
|
124
|
+
</section>
|
|
125
|
+
|
|
126
|
+
<!-- ββ 4. useFetch ββββββββββββββββββββββββββββββββββββββββββ -->
|
|
127
|
+
<section class="demo-card">
|
|
128
|
+
<h2>π useFetch()</h2>
|
|
129
|
+
<p class="demo-desc">Fetches from JSONPlaceholder. Loading state handled reactively.</p>
|
|
130
|
+
<div f-if="loading">
|
|
131
|
+
<p style="color: #888;">β³ Loading postsβ¦</p>
|
|
132
|
+
</div>
|
|
133
|
+
<div f-elif="error">
|
|
134
|
+
<p style="color: #ff6b6b;">β {{ error }}</p>
|
|
135
|
+
</div>
|
|
136
|
+
<div f-else>
|
|
137
|
+
<div f-for="post in posts" style="margin-bottom: 10px; padding: 10px; background:#1a1a24; border-radius:6px; border-left: 3px solid #ff3333;">
|
|
138
|
+
<strong style="color:#ff9999">{{ post.title }}</strong>
|
|
139
|
+
<p style="margin-top: 4px; font-size: 0.8rem; color: #666;">{{ post.body }}</p>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</section>
|
|
143
|
+
<!-- ββ 6. Custom Directives ββββββββββββββββββββββββββββββββββββββ -->
|
|
144
|
+
<section class="demo-card">
|
|
145
|
+
<h2>π οΈ Custom Directives</h2>
|
|
146
|
+
<p class="demo-desc">Testing <code>f-focus</code> and <code>f-highlight</code>.</p>
|
|
147
|
+
|
|
148
|
+
<div f-highlight="hColor" style="padding: 15px; border-radius: 8px; margin-bottom: 10px;">
|
|
149
|
+
<input f-focus class="cmp-input" placeholder="I have f-focus!" />
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<button @click="nextColor" class="cmp-btn plus">Change Highlight Color</button>
|
|
153
|
+
</section>
|
|
154
|
+
</div>
|
|
155
|
+
`
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { DynamicA } from '../components/DynamicA.js'
|
|
2
|
+
import { DynamicB } from '../components/DynamicB.js'
|
|
3
|
+
|
|
4
|
+
export const TestDynamic = defineComponent({
|
|
5
|
+
components: { DynamicA, DynamicB },
|
|
6
|
+
setup() {
|
|
7
|
+
const currentType = signal('DynamicA')
|
|
8
|
+
const sharedTitle = signal('Test Dinamic active')
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
currentType,
|
|
12
|
+
sharedTitle,
|
|
13
|
+
toggle: () => {
|
|
14
|
+
currentType(currentType() === 'DynamicA' ? 'DynamicB' : 'DynamicA')
|
|
15
|
+
},
|
|
16
|
+
template: `
|
|
17
|
+
<div class="page">
|
|
18
|
+
<h1>Dynamic Components System (Vue-style)</h1>
|
|
19
|
+
<p>Using the custom <code>f-is</code> directive.</p>
|
|
20
|
+
|
|
21
|
+
<div style="margin: 20px 0; display: flex; gap: 10px; align-items: center;">
|
|
22
|
+
<button @click="toggle" class="primary-btn">Toggle Component</button>
|
|
23
|
+
<span>Active: <strong>{{ currentType }}</strong></span>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<input f-model="sharedTitle" style="margin-bottom: 20px; padding: 8px; border-radius: 4px; border: 1px solid #444; background: #222; color: white; width: 100%;">
|
|
27
|
+
|
|
28
|
+
<hr style="border: 0; border-top: 1px solid #444; margin: 30px 0;">
|
|
29
|
+
|
|
30
|
+
<div f-is="currentType" f-keepAlive :title="sharedTitle"></div>
|
|
31
|
+
|
|
32
|
+
<p style="margin-top: 40px; color: #888; font-size: 0.9em;">
|
|
33
|
+
Note: The component above changes reactively when you click the button.
|
|
34
|
+
The <code>:title</code> prop is automatically passed to the active component.
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
`
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { DynamicA } from '../components/DynamicA.js'
|
|
2
|
+
import { DynamicB } from '../components/DynamicB.js'
|
|
3
|
+
|
|
4
|
+
export const TestDynamic = defineComponent({
|
|
5
|
+
components: { DynamicA, DynamicB },
|
|
6
|
+
setup() {
|
|
7
|
+
const currentType = signal('DynamicA')
|
|
8
|
+
const sharedTitle = signal('Dynamic Test Active')
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
currentType,
|
|
12
|
+
sharedTitle,
|
|
13
|
+
toggle: () => {
|
|
14
|
+
currentType(currentType() === 'DynamicA' ? 'DynamicB' : 'DynamicA')
|
|
15
|
+
},
|
|
16
|
+
template: `
|
|
17
|
+
<div class="page">
|
|
18
|
+
<h1>Dynamic Components System (Vue-style)</h1>
|
|
19
|
+
<p>Using the custom <code>f-is</code> directive.</p>
|
|
20
|
+
|
|
21
|
+
<div style="margin: 20px 0; display: flex; gap: 10px; align-items: center;">
|
|
22
|
+
<button @click="toggle" class="primary-btn">Toggle Component</button>
|
|
23
|
+
<span>Active: <strong>{{ currentType }}</strong></span>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<input f-model="sharedTitle" style="margin-bottom: 20px; padding: 8px; border-radius: 4px; border: 1px solid #444; background: #222; color: white; width: 100%;">
|
|
27
|
+
|
|
28
|
+
<hr style="border: 0; border-top: 1px solid #444; margin: 30px 0;">
|
|
29
|
+
|
|
30
|
+
<div f-is="currentType" :title="sharedTitle"></div>
|
|
31
|
+
|
|
32
|
+
<p style="margin-top: 40px; color: #888; font-size: 0.9em;">
|
|
33
|
+
Note: The component above changes reactively when you click the button.
|
|
34
|
+
The <code>:title</code> prop is automatically passed to the active component.
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
`
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
})
|