@fluixi/core 1.0.0-alpha.53
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/LICENSE +21 -0
- package/README.md +611 -0
- package/dist/cdn/core.global.js +3 -0
- package/dist/cli/generate.cjs +1814 -0
- package/dist/cli/generate.d.ts +7 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +2580 -0
- package/dist/cli/generate.mjs +1814 -0
- package/dist/cli/index.cjs +1814 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +2580 -0
- package/dist/cli/index.mjs +1814 -0
- package/dist/cli/run.cjs +2 -0
- package/dist/cli/run.d.ts +3 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +62 -0
- package/dist/cli/run.mjs +2 -0
- package/dist/fluixi.d.ts +5 -0
- package/dist/src/cdn/chunk-PFM5O5UE.mjs +1 -0
- package/dist/src/cdn/core.cjs +1 -0
- package/dist/src/cdn/core.d.ts +16 -0
- package/dist/src/cdn/core.d.ts.map +1 -0
- package/dist/src/cdn/core.js +15 -0
- package/dist/src/cdn/core.mjs +1 -0
- package/dist/src/cdn/router-next-WNWCFVC2.mjs +1 -0
- package/dist/src/index.cjs +1 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +26 -0
- package/dist/src/index.mjs +1 -0
- package/dist/src/jsx-dev-runtime.cjs +1 -0
- package/dist/src/jsx-dev-runtime.d.ts +2 -0
- package/dist/src/jsx-dev-runtime.d.ts.map +1 -0
- package/dist/src/jsx-dev-runtime.js +1 -0
- package/dist/src/jsx-dev-runtime.mjs +1 -0
- package/dist/src/jsx-runtime.cjs +1 -0
- package/dist/src/jsx-runtime.d.ts +7 -0
- package/dist/src/jsx-runtime.d.ts.map +1 -0
- package/dist/src/jsx-runtime.js +17 -0
- package/dist/src/jsx-runtime.mjs +1 -0
- package/dist/src/lib/client/chunk-D3VTWVNJ.mjs +1 -0
- package/dist/src/lib/client/index.cjs +1 -0
- package/dist/src/lib/client/index.d.ts +54 -0
- package/dist/src/lib/client/index.d.ts.map +1 -0
- package/dist/src/lib/client/index.js +142 -0
- package/dist/src/lib/client/index.mjs +1 -0
- package/dist/src/lib/client/router-next-UYIYMRJL.mjs +1 -0
- package/dist/src/lib/context/context.cjs +1 -0
- package/dist/src/lib/context/context.d.ts +337 -0
- package/dist/src/lib/context/context.d.ts.map +1 -0
- package/dist/src/lib/context/context.js +462 -0
- package/dist/src/lib/context/context.mjs +1 -0
- package/dist/src/lib/context/index.cjs +1 -0
- package/dist/src/lib/context/index.d.ts +3 -0
- package/dist/src/lib/context/index.d.ts.map +1 -0
- package/dist/src/lib/context/index.js +5 -0
- package/dist/src/lib/context/index.mjs +1 -0
- package/dist/src/lib/control-flow.cjs +1 -0
- package/dist/src/lib/control-flow.d.ts +4 -0
- package/dist/src/lib/control-flow.d.ts.map +1 -0
- package/dist/src/lib/control-flow.js +1 -0
- package/dist/src/lib/control-flow.mjs +1 -0
- package/dist/src/lib/core.cjs +1 -0
- package/dist/src/lib/core.d.ts +90 -0
- package/dist/src/lib/core.d.ts.map +1 -0
- package/dist/src/lib/core.js +181 -0
- package/dist/src/lib/core.mjs +1 -0
- package/dist/src/lib/directives/lit.cjs +1 -0
- package/dist/src/lib/directives/lit.d.ts +2 -0
- package/dist/src/lib/directives/lit.d.ts.map +1 -0
- package/dist/src/lib/directives/lit.js +31 -0
- package/dist/src/lib/directives/lit.mjs +0 -0
- package/dist/src/lib/env/index.cjs +1 -0
- package/dist/src/lib/env/index.d.ts +39 -0
- package/dist/src/lib/env/index.d.ts.map +1 -0
- package/dist/src/lib/env/index.js +50 -0
- package/dist/src/lib/env/index.mjs +1 -0
- package/dist/src/lib/i18n/index.cjs +1 -0
- package/dist/src/lib/i18n/index.d.ts +79 -0
- package/dist/src/lib/i18n/index.d.ts.map +1 -0
- package/dist/src/lib/i18n/index.js +127 -0
- package/dist/src/lib/i18n/index.mjs +1 -0
- package/dist/src/lib/index.cjs +1 -0
- package/dist/src/lib/index.d.ts +83 -0
- package/dist/src/lib/index.d.ts.map +1 -0
- package/dist/src/lib/index.js +147 -0
- package/dist/src/lib/index.mjs +1 -0
- package/dist/src/lib/isomorphic.cjs +1 -0
- package/dist/src/lib/isomorphic.d.ts +69 -0
- package/dist/src/lib/isomorphic.d.ts.map +1 -0
- package/dist/src/lib/isomorphic.js +92 -0
- package/dist/src/lib/isomorphic.mjs +1 -0
- package/dist/src/lib/jsx-runtime/index.cjs +1 -0
- package/dist/src/lib/jsx-runtime/index.d.ts +13 -0
- package/dist/src/lib/jsx-runtime/index.d.ts.map +1 -0
- package/dist/src/lib/jsx-runtime/index.js +23 -0
- package/dist/src/lib/jsx-runtime/index.mjs +1 -0
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.cjs +1 -0
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.d.ts +2 -0
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.d.ts.map +1 -0
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.js +1 -0
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.mjs +1 -0
- package/dist/src/lib/plugins/fluixi-routes-plugin.cjs +13 -0
- package/dist/src/lib/plugins/fluixi-routes-plugin.d.ts +21 -0
- package/dist/src/lib/plugins/fluixi-routes-plugin.d.ts.map +1 -0
- package/dist/src/lib/plugins/fluixi-routes-plugin.js +102 -0
- package/dist/src/lib/plugins/fluixi-routes-plugin.mjs +13 -0
- package/dist/src/lib/plugins/index.cjs +13 -0
- package/dist/src/lib/plugins/index.d.ts +4 -0
- package/dist/src/lib/plugins/index.d.ts.map +1 -0
- package/dist/src/lib/plugins/index.js +3 -0
- package/dist/src/lib/plugins/index.mjs +13 -0
- package/dist/src/lib/plugins/route-codegen.cjs +8 -0
- package/dist/src/lib/plugins/route-codegen.d.ts +13 -0
- package/dist/src/lib/plugins/route-codegen.d.ts.map +1 -0
- package/dist/src/lib/plugins/route-codegen.js +81 -0
- package/dist/src/lib/plugins/route-codegen.mjs +8 -0
- package/dist/src/lib/plugins/route-scanner.cjs +1 -0
- package/dist/src/lib/plugins/route-scanner.d.ts +39 -0
- package/dist/src/lib/plugins/route-scanner.d.ts.map +1 -0
- package/dist/src/lib/plugins/route-scanner.js +216 -0
- package/dist/src/lib/plugins/route-scanner.mjs +1 -0
- package/dist/src/lib/plugins/vite.cjs +1 -0
- package/dist/src/lib/plugins/vite.d.ts +3 -0
- package/dist/src/lib/plugins/vite.d.ts.map +1 -0
- package/dist/src/lib/plugins/vite.js +1 -0
- package/dist/src/lib/plugins/vite.mjs +1 -0
- package/dist/src/lib/render/component.cjs +1 -0
- package/dist/src/lib/render/component.d.ts +328 -0
- package/dist/src/lib/render/component.d.ts.map +1 -0
- package/dist/src/lib/render/component.js +520 -0
- package/dist/src/lib/render/component.mjs +1 -0
- package/dist/src/lib/render/index.cjs +1 -0
- package/dist/src/lib/render/index.d.ts +113 -0
- package/dist/src/lib/render/index.d.ts.map +1 -0
- package/dist/src/lib/render/index.js +201 -0
- package/dist/src/lib/render/index.mjs +1 -0
- package/dist/src/lib/render/lazy.cjs +1 -0
- package/dist/src/lib/render/lazy.d.ts +28 -0
- package/dist/src/lib/render/lazy.d.ts.map +1 -0
- package/dist/src/lib/render/lazy.js +62 -0
- package/dist/src/lib/render/lazy.mjs +1 -0
- package/dist/src/lib/render/suspense.cjs +1 -0
- package/dist/src/lib/render/suspense.d.ts +25 -0
- package/dist/src/lib/render/suspense.d.ts.map +1 -0
- package/dist/src/lib/render/suspense.js +139 -0
- package/dist/src/lib/render/suspense.mjs +1 -0
- package/dist/src/lib/router-next/data.cjs +1 -0
- package/dist/src/lib/router-next/data.d.ts +101 -0
- package/dist/src/lib/router-next/data.d.ts.map +1 -0
- package/dist/src/lib/router-next/data.js +179 -0
- package/dist/src/lib/router-next/data.mjs +1 -0
- package/dist/src/lib/router-next/index.cjs +1 -0
- package/dist/src/lib/router-next/index.d.ts +156 -0
- package/dist/src/lib/router-next/index.d.ts.map +1 -0
- package/dist/src/lib/router-next/index.js +464 -0
- package/dist/src/lib/router-next/index.mjs +1 -0
- package/dist/src/lib/server/hydration.cjs +1 -0
- package/dist/src/lib/server/hydration.d.ts +238 -0
- package/dist/src/lib/server/hydration.d.ts.map +1 -0
- package/dist/src/lib/server/hydration.js +384 -0
- package/dist/src/lib/server/hydration.mjs +1 -0
- package/dist/src/lib/server/index.cjs +16 -0
- package/dist/src/lib/server/index.d.ts +91 -0
- package/dist/src/lib/server/index.d.ts.map +1 -0
- package/dist/src/lib/server/index.js +141 -0
- package/dist/src/lib/server/index.mjs +16 -0
- package/dist/src/lib/server/reactive.cjs +1 -0
- package/dist/src/lib/server/reactive.d.ts +298 -0
- package/dist/src/lib/server/reactive.d.ts.map +1 -0
- package/dist/src/lib/server/reactive.js +472 -0
- package/dist/src/lib/server/reactive.mjs +1 -0
- package/dist/src/lib/theme/index.cjs +1 -0
- package/dist/src/lib/theme/index.d.ts +51 -0
- package/dist/src/lib/theme/index.d.ts.map +1 -0
- package/dist/src/lib/theme/index.js +79 -0
- package/dist/src/lib/theme/index.mjs +1 -0
- package/dist/src/lib/utils/index.cjs +1 -0
- package/dist/src/lib/utils/index.d.ts +379 -0
- package/dist/src/lib/utils/index.d.ts.map +1 -0
- package/dist/src/lib/utils/index.js +527 -0
- package/dist/src/lib/utils/index.mjs +1 -0
- package/dist/src/rx.cjs +1 -0
- package/dist/src/rx.d.ts +2 -0
- package/dist/src/rx.d.ts.map +1 -0
- package/dist/src/rx.js +101 -0
- package/dist/src/rx.mjs +0 -0
- package/dist/src/server.cjs +16 -0
- package/dist/src/server.d.ts +64 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +63 -0
- package/dist/src/server.mjs +16 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +128 -0
- package/src/index.ts +31 -0
- package/types/fluixi.d.ts +5 -0
- package/types/vinxi.d.ts +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fluixi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/fluixi/assets/main/logos/128x128.png" alt="Fluixi" width="120" height="120" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# @fluixi/core
|
|
6
|
+
|
|
7
|
+
**A modern, reactive UI framework with fine-grained reactivity, routing and SSR.**
|
|
8
|
+
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+

|
|
11
|
+

|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
A modern, reactive framework for building web applications with [lit-html](https://lit.dev) and fine-grained reactivity, inspired by [SolidJS](https://www.solidjs.com/).
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- 🚀 **Fine-grained Reactivity** - Powered by `@fluixi/reactive` for efficient, surgical updates
|
|
21
|
+
- 🎯 **lit-html Integration** - Leverage the power and simplicity of lit-html templates
|
|
22
|
+
- 🔄 **Control Flow Components** - Show, For, Switch, Portal, and Dynamic components
|
|
23
|
+
- 🛣️ **Built-in Router** - Client-side routing with nested routes and lazy loading
|
|
24
|
+
- 📡 **Resource Management** - Async data fetching with automatic loading states
|
|
25
|
+
- 🎭 **Context API** - Share state across component trees without prop drilling
|
|
26
|
+
- 🏗️ **TypeScript First** - Full type safety and excellent IDE support
|
|
27
|
+
- 🛠️ **CLI Tools** - Project and component generators for rapid development
|
|
28
|
+
- 📦 **Zero Runtime Overhead** - Compile-time optimizations for minimal bundle size
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install @fluixi/core @fluixi/reactive lit
|
|
34
|
+
# or
|
|
35
|
+
yarn add @fluixi/core @fluixi/reactive lit
|
|
36
|
+
# or
|
|
37
|
+
pnpm add @fluixi/core @fluixi/reactive lit
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
### Creating a New Project
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Using the CLI
|
|
46
|
+
npx @fluixi/core new my-app
|
|
47
|
+
|
|
48
|
+
# Or install globally
|
|
49
|
+
npm install -g @fluixi/core
|
|
50
|
+
fluixi new my-app
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Basic Example
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { createSignal, render, html } from '@fluixi/core';
|
|
57
|
+
|
|
58
|
+
const Counter = () => {
|
|
59
|
+
const [count, setCount] = createSignal(0);
|
|
60
|
+
|
|
61
|
+
return html`
|
|
62
|
+
<div>
|
|
63
|
+
<h1>Count: ${count()}</h1>
|
|
64
|
+
<button @click=${() => setCount(count() + 1)}>Increment</button>
|
|
65
|
+
<button @click=${() => setCount(count() - 1)}>Decrement</button>
|
|
66
|
+
<button @click=${() => setCount(0)}>Reset</button>
|
|
67
|
+
</div>
|
|
68
|
+
`;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
render(Counter, document.getElementById('app')!);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Core Concepts
|
|
75
|
+
|
|
76
|
+
### Signals
|
|
77
|
+
|
|
78
|
+
Signals are the foundation of reactivity in Fluixi. They create reactive state that automatically updates the UI when changed.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { createSignal, createEffect } from '@fluixi/core';
|
|
82
|
+
|
|
83
|
+
const [count, setCount] = createSignal(0);
|
|
84
|
+
const [name, setName] = createSignal('Alice');
|
|
85
|
+
|
|
86
|
+
// Effects run automatically when dependencies change
|
|
87
|
+
createEffect(() => {
|
|
88
|
+
console.log(`${name()} has counted to ${count()}`);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
setCount(5); // Logs: "Alice has counted to 5"
|
|
92
|
+
setName('Bob'); // Logs: "Bob has counted to 5"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Components
|
|
96
|
+
|
|
97
|
+
Components are pure functions that return lit-html templates. They can use reactive primitives to manage state.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { html, createSignal, Component, ComponentProps } from '@fluixi/core';
|
|
101
|
+
|
|
102
|
+
interface GreetingProps extends ComponentProps {
|
|
103
|
+
initialName?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const Greeting: Component<GreetingProps> = (props) => {
|
|
107
|
+
const [name, setName] = createSignal(props.initialName || 'World');
|
|
108
|
+
|
|
109
|
+
return html`
|
|
110
|
+
<div>
|
|
111
|
+
<h1>Hello, ${name()}!</h1>
|
|
112
|
+
<input
|
|
113
|
+
type="text"
|
|
114
|
+
.value=${name()}
|
|
115
|
+
@input=${(e: Event) => setName((e.target as HTMLInputElement).value)}
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
`;
|
|
119
|
+
};
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Control Flow
|
|
123
|
+
|
|
124
|
+
Fluixi provides control flow components for conditional rendering and lists.
|
|
125
|
+
|
|
126
|
+
#### Show - Conditional Rendering
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { Show, createSignal, html } from '@fluixi/core';
|
|
130
|
+
|
|
131
|
+
const UserProfile = () => {
|
|
132
|
+
const [user, setUser] = createSignal<User | null>(null);
|
|
133
|
+
const [loading, setLoading] = createSignal(true);
|
|
134
|
+
|
|
135
|
+
return html`
|
|
136
|
+
${Show({
|
|
137
|
+
when: loading,
|
|
138
|
+
fallback: Show({
|
|
139
|
+
when: user,
|
|
140
|
+
fallback: html`<p>No user found</p>`,
|
|
141
|
+
children: (u) => html`<p>Welcome, ${u.name}!</p>`
|
|
142
|
+
}),
|
|
143
|
+
children: html`<p>Loading...</p>`
|
|
144
|
+
})}
|
|
145
|
+
`;
|
|
146
|
+
};
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### For - List Rendering
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { For, createSignal, html } from '@fluixi/core';
|
|
153
|
+
|
|
154
|
+
const TodoList = () => {
|
|
155
|
+
const [todos, setTodos] = createSignal([
|
|
156
|
+
{ id: 1, text: 'Learn Fluixi', done: false },
|
|
157
|
+
{ id: 2, text: 'Build an app', done: false },
|
|
158
|
+
]);
|
|
159
|
+
|
|
160
|
+
return html`
|
|
161
|
+
<ul>
|
|
162
|
+
${For({
|
|
163
|
+
each: todos,
|
|
164
|
+
children: (todo, index) => html`
|
|
165
|
+
<li>
|
|
166
|
+
<input
|
|
167
|
+
type="checkbox"
|
|
168
|
+
.checked=${todo.done}
|
|
169
|
+
@change=${(e) => {
|
|
170
|
+
const newTodos = [...todos()];
|
|
171
|
+
newTodos[index].done = e.target.checked;
|
|
172
|
+
setTodos(newTodos);
|
|
173
|
+
}}
|
|
174
|
+
/>
|
|
175
|
+
${todo.text}
|
|
176
|
+
</li>
|
|
177
|
+
`
|
|
178
|
+
})}
|
|
179
|
+
</ul>
|
|
180
|
+
`;
|
|
181
|
+
};
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### Switch/Match - Multiple Conditions
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import { Switch, Match, createSignal, html } from '@fluixi/core';
|
|
188
|
+
|
|
189
|
+
const StatusView = () => {
|
|
190
|
+
const [status, setStatus] = createSignal<'loading' | 'success' | 'error'>('loading');
|
|
191
|
+
|
|
192
|
+
return html`
|
|
193
|
+
${Switch({
|
|
194
|
+
children: [
|
|
195
|
+
Match({ when: () => status() === 'loading', children: html`<p>Loading...</p>` }),
|
|
196
|
+
Match({ when: () => status() === 'success', children: html`<p>Success!</p>` }),
|
|
197
|
+
Match({ when: () => status() === 'error', children: html`<p>Error!</p>` })
|
|
198
|
+
]
|
|
199
|
+
})}
|
|
200
|
+
`;
|
|
201
|
+
};
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### Portal - Render Outside Hierarchy
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { Portal, createSignal, Show, html } from '@fluixi/core';
|
|
208
|
+
|
|
209
|
+
const Modal = () => {
|
|
210
|
+
const [isOpen, setIsOpen] = createSignal(false);
|
|
211
|
+
|
|
212
|
+
return html`
|
|
213
|
+
<button @click=${() => setIsOpen(true)}>Open Modal</button>
|
|
214
|
+
|
|
215
|
+
${Show({
|
|
216
|
+
when: isOpen,
|
|
217
|
+
children: Portal({
|
|
218
|
+
children: html`
|
|
219
|
+
<div class="modal-overlay">
|
|
220
|
+
<div class="modal-content">
|
|
221
|
+
<h2>Modal Title</h2>
|
|
222
|
+
<p>Modal content goes here</p>
|
|
223
|
+
<button @click=${() => setIsOpen(false)}>Close</button>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
`
|
|
227
|
+
})
|
|
228
|
+
})}
|
|
229
|
+
`;
|
|
230
|
+
};
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Resources
|
|
234
|
+
|
|
235
|
+
Resources handle async data fetching with built-in loading and error states.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import { createResource, Show, html } from '@fluixi/core';
|
|
239
|
+
|
|
240
|
+
interface User {
|
|
241
|
+
id: number;
|
|
242
|
+
name: string;
|
|
243
|
+
email: string;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const fetchUser = async (id: number): Promise<User> => {
|
|
247
|
+
const response = await fetch(`/api/users/${id}`);
|
|
248
|
+
return response.json();
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const UserDetail = () => {
|
|
252
|
+
const [userId, setUserId] = createSignal(1);
|
|
253
|
+
const [user, { refetch }] = createResource(() => userId(), fetchUser);
|
|
254
|
+
|
|
255
|
+
return html`
|
|
256
|
+
${Show({
|
|
257
|
+
when: user.loading,
|
|
258
|
+
fallback: Show({
|
|
259
|
+
when: user.error,
|
|
260
|
+
fallback: Show({
|
|
261
|
+
when: user,
|
|
262
|
+
children: (u) => html`
|
|
263
|
+
<div>
|
|
264
|
+
<h2>${u.name}</h2>
|
|
265
|
+
<p>${u.email}</p>
|
|
266
|
+
<button @click=${refetch}>Refresh</button>
|
|
267
|
+
</div>
|
|
268
|
+
`
|
|
269
|
+
}),
|
|
270
|
+
children: html`<p>Error: ${user.error?.message}</p>`
|
|
271
|
+
}),
|
|
272
|
+
children: html`<p>Loading user...</p>`
|
|
273
|
+
})}
|
|
274
|
+
`;
|
|
275
|
+
};
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Context API
|
|
279
|
+
|
|
280
|
+
Share state across component trees without prop drilling.
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
import { createContext, useContext, Provider, html } from '@fluixi/core';
|
|
284
|
+
|
|
285
|
+
interface Theme {
|
|
286
|
+
primaryColor: string;
|
|
287
|
+
secondaryColor: string;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const ThemeContext = createContext<Theme>({
|
|
291
|
+
primaryColor: '#007bff',
|
|
292
|
+
secondaryColor: '#6c757d'
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const ThemedButton = () => {
|
|
296
|
+
const theme = useContext(ThemeContext);
|
|
297
|
+
|
|
298
|
+
return html`
|
|
299
|
+
<button style="background: ${theme.primaryColor}">
|
|
300
|
+
Themed Button
|
|
301
|
+
</button>
|
|
302
|
+
`;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const App = () => {
|
|
306
|
+
const theme = { primaryColor: '#28a745', secondaryColor: '#dc3545' };
|
|
307
|
+
|
|
308
|
+
return Provider({
|
|
309
|
+
context: ThemeContext,
|
|
310
|
+
value: theme,
|
|
311
|
+
children: html`
|
|
312
|
+
<div>
|
|
313
|
+
<h1>My App</h1>
|
|
314
|
+
${ThemedButton()}
|
|
315
|
+
</div>
|
|
316
|
+
`
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Router
|
|
322
|
+
|
|
323
|
+
Built-in client-side routing with nested routes and lazy loading.
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
import { createRouter, Router, Link, useParams, html } from '@fluixi/core';
|
|
327
|
+
|
|
328
|
+
const Home = () => html`<h1>Home Page</h1>`;
|
|
329
|
+
|
|
330
|
+
const UserDetail = () => {
|
|
331
|
+
const params = useParams();
|
|
332
|
+
return html`<h1>User ${params.id}</h1>`;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const routes = [
|
|
336
|
+
{ path: '/', component: Home },
|
|
337
|
+
{ path: '/users/:id', component: UserDetail },
|
|
338
|
+
{ path: '/about', component: () => html`<h1>About</h1>` }
|
|
339
|
+
];
|
|
340
|
+
|
|
341
|
+
const App = () => {
|
|
342
|
+
const router = createRouter(routes);
|
|
343
|
+
|
|
344
|
+
return Router({
|
|
345
|
+
router,
|
|
346
|
+
children: html`
|
|
347
|
+
<div>
|
|
348
|
+
<nav>
|
|
349
|
+
${Link({ href: '/', children: 'Home' })}
|
|
350
|
+
${Link({ href: '/about', children: 'About' })}
|
|
351
|
+
</nav>
|
|
352
|
+
<main id="content"></main>
|
|
353
|
+
</div>
|
|
354
|
+
`
|
|
355
|
+
});
|
|
356
|
+
};
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## CLI Commands
|
|
360
|
+
|
|
361
|
+
### Create a New Project
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
# Interactive mode
|
|
365
|
+
fluixi new
|
|
366
|
+
|
|
367
|
+
# With options
|
|
368
|
+
fluixi new my-app --template full
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Generate a Component
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
# Interactive mode
|
|
375
|
+
fluixi generate component
|
|
376
|
+
|
|
377
|
+
# With options
|
|
378
|
+
fluixi g c MyComponent --type with-state --path components/MyComponent/MyComponent.ts
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Component types:
|
|
382
|
+
- `basic` - Simple component
|
|
383
|
+
- `with-state` - Component with reactive state
|
|
384
|
+
- `with-resource` - Component with async data fetching
|
|
385
|
+
- `with-router` - Component with router integration
|
|
386
|
+
- `full` - Full-featured component with all features
|
|
387
|
+
|
|
388
|
+
## Lifecycle Hooks
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
import { onMount, onUnmount, createSignal, html } from '@fluixi/core';
|
|
392
|
+
|
|
393
|
+
const MyComponent = () => {
|
|
394
|
+
const [count, setCount] = createSignal(0);
|
|
395
|
+
|
|
396
|
+
onMount(() => {
|
|
397
|
+
console.log('Component mounted');
|
|
398
|
+
const interval = setInterval(() => setCount(c => c + 1), 1000);
|
|
399
|
+
|
|
400
|
+
// Cleanup function
|
|
401
|
+
return () => {
|
|
402
|
+
console.log('Component unmounting');
|
|
403
|
+
clearInterval(interval);
|
|
404
|
+
};
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
onUnmount(() => {
|
|
408
|
+
console.log('Additional cleanup');
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
return html`<div>Count: ${count()}</div>`;
|
|
412
|
+
};
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
## Advanced Patterns
|
|
416
|
+
|
|
417
|
+
### Derived State (Memos)
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import { createSignal, createMemo, html } from '@fluixi/core';
|
|
421
|
+
|
|
422
|
+
const ExpensiveCalculation = () => {
|
|
423
|
+
const [a, setA] = createSignal(1);
|
|
424
|
+
const [b, setB] = createSignal(2);
|
|
425
|
+
|
|
426
|
+
// Memo caches the result and only recalculates when dependencies change
|
|
427
|
+
const sum = createMemo(() => {
|
|
428
|
+
console.log('Calculating sum...');
|
|
429
|
+
return a() + b();
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
return html`
|
|
433
|
+
<div>
|
|
434
|
+
<p>Sum: ${sum()}</p>
|
|
435
|
+
<button @click=${() => setA(a() + 1)}>Increment A</button>
|
|
436
|
+
<button @click=${() => setB(b() + 1)}>Increment B</button>
|
|
437
|
+
</div>
|
|
438
|
+
`;
|
|
439
|
+
};
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Batched Updates
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
import { createSignal, batch, html } from '@fluixi/core';
|
|
446
|
+
|
|
447
|
+
const BatchExample = () => {
|
|
448
|
+
const [firstName, setFirstName] = createSignal('John');
|
|
449
|
+
const [lastName, setLastName] = createSignal('Doe');
|
|
450
|
+
|
|
451
|
+
const updateName = () => {
|
|
452
|
+
batch(() => {
|
|
453
|
+
setFirstName('Jane');
|
|
454
|
+
setLastName('Smith');
|
|
455
|
+
}); // UI updates only once
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
return html`
|
|
459
|
+
<div>
|
|
460
|
+
<p>${firstName()} ${lastName()}</p>
|
|
461
|
+
<button @click=${updateName}>Update Name</button>
|
|
462
|
+
</div>
|
|
463
|
+
`;
|
|
464
|
+
};
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Error Boundaries
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
import { createSignal, Show, html } from '@fluixi/core';
|
|
471
|
+
|
|
472
|
+
const ErrorBoundary = (props: { children: any }) => {
|
|
473
|
+
const [error, setError] = createSignal<Error | null>(null);
|
|
474
|
+
|
|
475
|
+
try {
|
|
476
|
+
return html`
|
|
477
|
+
${Show({
|
|
478
|
+
when: () => !error(),
|
|
479
|
+
fallback: html`<div>Error: ${error()?.message}</div>`,
|
|
480
|
+
children: props.children
|
|
481
|
+
})}
|
|
482
|
+
`;
|
|
483
|
+
} catch (e) {
|
|
484
|
+
setError(e as Error);
|
|
485
|
+
return html`<div>Error: ${(e as Error).message}</div>`;
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
## API Reference
|
|
491
|
+
|
|
492
|
+
### Core
|
|
493
|
+
|
|
494
|
+
- `createSignal(initialValue, options?)` - Create a reactive signal
|
|
495
|
+
- `createEffect(fn)` - Run side effects when dependencies change
|
|
496
|
+
- `createMemo(fn)` - Create derived reactive state
|
|
497
|
+
- `createComponent(fn)` - Create a component with lifecycle management
|
|
498
|
+
- `render(component, container)` - Render a component into the DOM
|
|
499
|
+
- `onMount(fn)` - Run code when component mounts
|
|
500
|
+
- `onUnmount(fn)` - Run code when component unmounts
|
|
501
|
+
- `batch(fn)` - Batch multiple updates together
|
|
502
|
+
- `untrack(fn)` - Run code without tracking dependencies
|
|
503
|
+
|
|
504
|
+
### Control Flow
|
|
505
|
+
|
|
506
|
+
- `Show({ when, fallback?, children })` - Conditional rendering
|
|
507
|
+
- `For({ each, fallback?, children })` - List rendering
|
|
508
|
+
- `Index({ each, fallback?, children })` - Non-keyed list rendering
|
|
509
|
+
- `Switch({ fallback?, children })` - Multiple condition branches
|
|
510
|
+
- `Match({ when, children })` - Condition branch in Switch
|
|
511
|
+
- `Portal({ mount?, children })` - Render outside component tree
|
|
512
|
+
- `Dynamic({ component, props?, children? })` - Render dynamic components
|
|
513
|
+
|
|
514
|
+
### Context
|
|
515
|
+
|
|
516
|
+
- `createContext(defaultValue, name?)` - Create a context
|
|
517
|
+
- `useContext(context)` - Access context value
|
|
518
|
+
- `Provider({ context, value, children })` - Provide context value
|
|
519
|
+
|
|
520
|
+
### Resources
|
|
521
|
+
|
|
522
|
+
- `createResource(source, fetcher, options?)` - Create async resource
|
|
523
|
+
- `createResources([...])` - Create multiple resources
|
|
524
|
+
- `createLazyResource(source, fetcher, options?)` - Create lazy resource
|
|
525
|
+
|
|
526
|
+
### Router
|
|
527
|
+
|
|
528
|
+
- `createRouter(routes, options?)` - Create router instance
|
|
529
|
+
- `Router({ router, children })` - Router provider component
|
|
530
|
+
- `Link({ href, children })` - Navigation link
|
|
531
|
+
- `useRouter()` - Access router instance
|
|
532
|
+
- `useLocation()` - Access current location
|
|
533
|
+
- `useNavigate()` - Get navigation function
|
|
534
|
+
- `useParams()` - Get route parameters
|
|
535
|
+
- `useQuery()` - Get query parameters
|
|
536
|
+
|
|
537
|
+
## TypeScript Support
|
|
538
|
+
|
|
539
|
+
Fluixi is written in TypeScript and provides full type safety:
|
|
540
|
+
|
|
541
|
+
```typescript
|
|
542
|
+
import { Component, ComponentProps, createSignal } from '@fluixi/core';
|
|
543
|
+
|
|
544
|
+
interface UserProps extends ComponentProps {
|
|
545
|
+
userId: number;
|
|
546
|
+
onUserLoad?: (user: User) => void;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
interface User {
|
|
550
|
+
id: number;
|
|
551
|
+
name: string;
|
|
552
|
+
email: string;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const UserComponent: Component<UserProps> = (props) => {
|
|
556
|
+
const [user, setUser] = createSignal<User | null>(null);
|
|
557
|
+
|
|
558
|
+
// TypeScript knows the types of all signals and props
|
|
559
|
+
return html`<div>${user()?.name}</div>`;
|
|
560
|
+
};
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
## Comparison with Other Frameworks
|
|
564
|
+
|
|
565
|
+
### vs React
|
|
566
|
+
|
|
567
|
+
- ✅ No virtual DOM - direct DOM updates for better performance
|
|
568
|
+
- ✅ True fine-grained reactivity without re-renders
|
|
569
|
+
- ✅ Simpler mental model - no hooks rules or dependency arrays
|
|
570
|
+
- ✅ Smaller bundle size
|
|
571
|
+
- ⚠️ Smaller ecosystem (but growing!)
|
|
572
|
+
|
|
573
|
+
### vs SolidJS
|
|
574
|
+
|
|
575
|
+
- ✅ Similar reactivity model and API
|
|
576
|
+
- ✅ Uses lit-html for familiar template syntax
|
|
577
|
+
- ✅ Integrated with @fluixi/reactive library
|
|
578
|
+
- ⚠️ Different template syntax (lit-html vs JSX)
|
|
579
|
+
|
|
580
|
+
### vs Vue
|
|
581
|
+
|
|
582
|
+
- ✅ More explicit reactivity with signals
|
|
583
|
+
- ✅ Better TypeScript inference
|
|
584
|
+
- ✅ Lighter weight
|
|
585
|
+
- ⚠️ No single-file components
|
|
586
|
+
|
|
587
|
+
## Contributing
|
|
588
|
+
|
|
589
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
590
|
+
|
|
591
|
+
## License
|
|
592
|
+
|
|
593
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
594
|
+
|
|
595
|
+
## Credits
|
|
596
|
+
|
|
597
|
+
Fluixi is inspired by:
|
|
598
|
+
- [SolidJS](https://www.solidjs.com/) - For the reactivity model and API design
|
|
599
|
+
- [Lit](https://lit.dev/) - For the template system
|
|
600
|
+
- The [TC39 Signals Proposal](https://github.com/tc39/proposal-signals)
|
|
601
|
+
|
|
602
|
+
## Links
|
|
603
|
+
|
|
604
|
+
- [Documentation](https://github.com/your-org/fluixi/docs)
|
|
605
|
+
- [Examples](https://github.com/your-org/fluixi/examples)
|
|
606
|
+
- [GitHub](https://github.com/your-org/fluixi)
|
|
607
|
+
- [npm](https://www.npmjs.com/package/@fluixi/core)
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
Made with ☕ by the Adafri team
|