flexium 0.12.16 → 0.12.17
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 +149 -92
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,9 +11,9 @@ Flexium is a next-generation UI framework that unifies state management, async d
|
|
|
11
11
|
## Key Features
|
|
12
12
|
|
|
13
13
|
- **Unified State API** - No more `useState`, `useRecoil`, `useQuery` separation. Just `state()`.
|
|
14
|
-
- **No Virtual DOM** - Direct DOM updates
|
|
15
|
-
- **Tiny Bundle** -
|
|
16
|
-
- **Cross-Platform** - DOM, Canvas,
|
|
14
|
+
- **No Virtual DOM** - Direct DOM updates via Proxy-based fine-grained reactivity.
|
|
15
|
+
- **Tiny Bundle** - Minimal footprint with tree-shaking support.
|
|
16
|
+
- **Cross-Platform** - DOM, Canvas, Server (SSR) renderers included.
|
|
17
17
|
- **TypeScript First** - Full type inference out of the box.
|
|
18
18
|
- **Zero-Config JSX** - Works with standard tooling.
|
|
19
19
|
|
|
@@ -39,20 +39,20 @@ Flexium unifies all state concepts into one function.
|
|
|
39
39
|
### Local State
|
|
40
40
|
|
|
41
41
|
```tsx
|
|
42
|
-
import { state } from 'flexium/core'
|
|
43
|
-
import { render } from 'flexium/dom'
|
|
42
|
+
import { state } from 'flexium/core'
|
|
43
|
+
import { render } from 'flexium/dom'
|
|
44
44
|
|
|
45
45
|
function Counter() {
|
|
46
|
-
const [count, setCount] = state(0)
|
|
46
|
+
const [count, setCount] = state(0)
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
49
|
<button onclick={() => setCount(c => c + 1)}>
|
|
50
50
|
Count: {count}
|
|
51
51
|
</button>
|
|
52
|
-
)
|
|
52
|
+
)
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
render(
|
|
55
|
+
render(Counter, document.getElementById('app'))
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
### Global State
|
|
@@ -61,16 +61,17 @@ Just add a `key` to share state across components. Keys can be strings or arrays
|
|
|
61
61
|
|
|
62
62
|
```tsx
|
|
63
63
|
// Define global state with array key
|
|
64
|
-
const [theme, setTheme] = state('light', { key: ['app', 'theme'] })
|
|
64
|
+
const [theme, setTheme] = state('light', { key: ['app', 'theme'] })
|
|
65
65
|
|
|
66
66
|
function ThemeToggle() {
|
|
67
|
-
|
|
67
|
+
// Access same state anywhere with the same key
|
|
68
|
+
const [theme, setTheme] = state('light', { key: ['app', 'theme'] })
|
|
68
69
|
|
|
69
70
|
return (
|
|
70
71
|
<button onclick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
|
|
71
72
|
Theme: {theme}
|
|
72
73
|
</button>
|
|
73
|
-
)
|
|
74
|
+
)
|
|
74
75
|
}
|
|
75
76
|
```
|
|
76
77
|
|
|
@@ -81,55 +82,40 @@ Pass an async function to handle data fetching automatically.
|
|
|
81
82
|
```tsx
|
|
82
83
|
function UserProfile({ id }) {
|
|
83
84
|
const [user, control] = state(async () => {
|
|
84
|
-
const res = await fetch(`/api/users/${id}`)
|
|
85
|
-
return res.json()
|
|
86
|
-
})
|
|
85
|
+
const res = await fetch(`/api/users/${id}`)
|
|
86
|
+
return res.json()
|
|
87
|
+
})
|
|
87
88
|
|
|
88
|
-
if (control.loading) return <div>Loading...</div
|
|
89
|
-
if (control.error) return <div>Error!</div
|
|
89
|
+
if (control.loading) return <div>Loading...</div>
|
|
90
|
+
if (control.error) return <div>Error!</div>
|
|
90
91
|
|
|
91
92
|
return (
|
|
92
93
|
<div>
|
|
93
94
|
<h1>{user.name}</h1>
|
|
94
95
|
<button onclick={() => control.refetch()}>Reload</button>
|
|
95
96
|
</div>
|
|
96
|
-
)
|
|
97
|
+
)
|
|
97
98
|
}
|
|
98
99
|
```
|
|
99
100
|
|
|
100
|
-
### Derived State
|
|
101
|
+
### Computed/Derived State
|
|
101
102
|
|
|
102
103
|
```tsx
|
|
103
|
-
const [count, setCount] = state(1)
|
|
104
|
-
const [
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Array Keys & Params
|
|
108
|
-
|
|
109
|
-
```tsx
|
|
110
|
-
// Array keys for dynamic caching (like TanStack Query)
|
|
111
|
-
const [user] = state(fetchUser, { key: ['user', userId] });
|
|
112
|
-
|
|
113
|
-
// Explicit params for better DX
|
|
114
|
-
const [data] = state(
|
|
115
|
-
async ({ userId, postId }) => fetchPost(userId, postId),
|
|
116
|
-
{
|
|
117
|
-
key: ['posts', userId, postId],
|
|
118
|
-
params: { userId, postId }
|
|
119
|
-
}
|
|
120
|
-
);
|
|
104
|
+
const [count, setCount] = state(1)
|
|
105
|
+
const [doubled] = state(() => count * 2, { deps: [count] })
|
|
121
106
|
```
|
|
122
107
|
|
|
123
108
|
## Package Structure
|
|
124
109
|
|
|
125
110
|
```
|
|
126
111
|
flexium
|
|
127
|
-
├── /core
|
|
128
|
-
├── /dom
|
|
129
|
-
├── /
|
|
130
|
-
├── /
|
|
131
|
-
├── /
|
|
132
|
-
|
|
112
|
+
├── /core # Core reactivity: state(), effect(), sync(), context()
|
|
113
|
+
├── /dom # DOM renderer: render(), hydrate(), Portal, Suspense
|
|
114
|
+
├── /ref # Ref system: ref(), forwardRef()
|
|
115
|
+
├── /router # SPA routing: Routes, Route, Link, Outlet
|
|
116
|
+
├── /server # SSR: renderToString(), renderToStaticMarkup()
|
|
117
|
+
├── /canvas # Canvas 2D: Canvas, DrawRect, DrawCircle, DrawText
|
|
118
|
+
└── /interactive # Game loop: loop(), keyboard(), mouse()
|
|
133
119
|
```
|
|
134
120
|
|
|
135
121
|
## Control Flow
|
|
@@ -138,89 +124,160 @@ Use native JavaScript for control flow - no special components needed:
|
|
|
138
124
|
|
|
139
125
|
```tsx
|
|
140
126
|
// Conditional rendering
|
|
141
|
-
{isLoggedIn
|
|
127
|
+
{isLoggedIn ? <Dashboard /> : <Login />}
|
|
142
128
|
|
|
143
129
|
// Short-circuit for simple conditions
|
|
144
|
-
{isAdmin
|
|
130
|
+
{isAdmin && <AdminPanel />}
|
|
145
131
|
|
|
146
|
-
// List rendering
|
|
132
|
+
// List rendering
|
|
147
133
|
{items.map(item => <Item key={item.id} data={item} />)}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Routing
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
import { Routes, Route, Link, router } from 'flexium/router'
|
|
140
|
+
|
|
141
|
+
function App() {
|
|
142
|
+
return (
|
|
143
|
+
<Routes>
|
|
144
|
+
<nav>
|
|
145
|
+
<Link to="/">Home</Link>
|
|
146
|
+
<Link to="/about">About</Link>
|
|
147
|
+
</nav>
|
|
148
|
+
<Route path="/" component={Home} />
|
|
149
|
+
<Route path="/about" component={About} />
|
|
150
|
+
<Route path="/users/:id" component={UserProfile} />
|
|
151
|
+
</Routes>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
148
154
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
155
|
+
function UserProfile({ params }) {
|
|
156
|
+
return <h1>User: {params.id}</h1>
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Or use the router hook
|
|
160
|
+
function UserProfileHook() {
|
|
161
|
+
const r = router()
|
|
162
|
+
return <h1>User: {r.params.id}</h1>
|
|
163
|
+
}
|
|
154
164
|
```
|
|
155
165
|
|
|
156
166
|
## Canvas Rendering
|
|
157
167
|
|
|
158
168
|
```tsx
|
|
159
|
-
import { Canvas,
|
|
169
|
+
import { Canvas, DrawRect, DrawCircle, DrawText } from 'flexium/canvas'
|
|
160
170
|
|
|
161
171
|
function App() {
|
|
162
|
-
const [x, setX] = state(100)
|
|
172
|
+
const [x, setX] = state(100)
|
|
163
173
|
|
|
164
174
|
return (
|
|
165
175
|
<Canvas width={400} height={300}>
|
|
166
|
-
<
|
|
167
|
-
<
|
|
168
|
-
<
|
|
176
|
+
<DrawRect x={0} y={0} width={400} height={300} fill="#1a1a2e" />
|
|
177
|
+
<DrawCircle x={x} y={150} radius={30} fill="#e94560" />
|
|
178
|
+
<DrawText x={200} y={50} text="Hello Canvas!" fill="white" />
|
|
169
179
|
</Canvas>
|
|
170
|
-
)
|
|
180
|
+
)
|
|
171
181
|
}
|
|
172
182
|
```
|
|
173
183
|
|
|
174
|
-
##
|
|
184
|
+
## Game Development
|
|
175
185
|
|
|
176
186
|
```tsx
|
|
177
|
-
import {
|
|
187
|
+
import { state, effect } from 'flexium/core'
|
|
188
|
+
import { Canvas, DrawRect } from 'flexium/canvas'
|
|
189
|
+
import { loop, keyboard, Keys } from 'flexium/interactive'
|
|
190
|
+
|
|
191
|
+
function Game() {
|
|
192
|
+
const [x, setX] = state(100)
|
|
193
|
+
const kb = keyboard()
|
|
194
|
+
|
|
195
|
+
const gameLoop = loop({
|
|
196
|
+
fixedFps: 60,
|
|
197
|
+
onUpdate: (delta) => {
|
|
198
|
+
if (kb.isPressed(Keys.ArrowRight)) setX(x => x + 200 * delta)
|
|
199
|
+
if (kb.isPressed(Keys.ArrowLeft)) setX(x => x - 200 * delta)
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
effect(() => {
|
|
204
|
+
gameLoop.start()
|
|
205
|
+
return () => gameLoop.stop()
|
|
206
|
+
}, [])
|
|
178
207
|
|
|
179
|
-
function App() {
|
|
180
208
|
return (
|
|
181
|
-
<
|
|
182
|
-
<
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
<Text>Click me</Text>
|
|
186
|
-
</Pressable>
|
|
187
|
-
</Row>
|
|
188
|
-
</Column>
|
|
189
|
-
);
|
|
209
|
+
<Canvas width={800} height={600}>
|
|
210
|
+
<DrawRect x={x} y={300} width={50} height={50} fill="red" />
|
|
211
|
+
</Canvas>
|
|
212
|
+
)
|
|
190
213
|
}
|
|
191
214
|
```
|
|
192
215
|
|
|
193
|
-
##
|
|
216
|
+
## Server-Side Rendering
|
|
194
217
|
|
|
195
218
|
```tsx
|
|
196
|
-
import {
|
|
219
|
+
import { renderToString } from 'flexium/server'
|
|
220
|
+
import { hydrate } from 'flexium/dom'
|
|
221
|
+
|
|
222
|
+
// Server
|
|
223
|
+
const { html, state } = renderToString(App, { hydrate: true })
|
|
224
|
+
|
|
225
|
+
// Client
|
|
226
|
+
hydrate(App, document.getElementById('root'), { state })
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Built-in Components
|
|
230
|
+
|
|
231
|
+
### Portal
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
import { Portal } from 'flexium/dom'
|
|
235
|
+
|
|
236
|
+
<Portal target={document.body}>
|
|
237
|
+
<Modal />
|
|
238
|
+
</Portal>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Suspense
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
import { Suspense, lazy } from 'flexium/dom'
|
|
245
|
+
|
|
246
|
+
const LazyComponent = lazy(() => import('./Heavy'))
|
|
247
|
+
|
|
248
|
+
<Suspense fallback={<Loading />}>
|
|
249
|
+
<LazyComponent />
|
|
250
|
+
</Suspense>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### ErrorBoundary
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
import { ErrorBoundary } from 'flexium/dom'
|
|
257
|
+
|
|
258
|
+
<ErrorBoundary fallback={(error) => <Error message={error.message} />}>
|
|
259
|
+
<App />
|
|
260
|
+
</ErrorBoundary>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Context API
|
|
264
|
+
|
|
265
|
+
```tsx
|
|
266
|
+
import { createContext, context } from 'flexium/core'
|
|
267
|
+
|
|
268
|
+
const ThemeCtx = createContext('light')
|
|
197
269
|
|
|
198
270
|
function App() {
|
|
199
271
|
return (
|
|
200
|
-
<
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
</nav>
|
|
205
|
-
<Route path="/" component={Home} />
|
|
206
|
-
<Route path="/about" component={About} />
|
|
207
|
-
<Route path="/users/:id" component={UserProfile} />
|
|
208
|
-
</Routes>
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function UserProfile({ params }) {
|
|
213
|
-
// Params are passed as props to the component
|
|
214
|
-
return <h1>User: {params.id}</h1>;
|
|
272
|
+
<ThemeCtx.Provider value="dark">
|
|
273
|
+
<Child />
|
|
274
|
+
</ThemeCtx.Provider>
|
|
275
|
+
)
|
|
215
276
|
}
|
|
216
277
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
function UserProfileHook() {
|
|
221
|
-
const r = router();
|
|
222
|
-
// Access params directly from router context
|
|
223
|
-
return <h1>User: {r.params.id}</h1>;
|
|
278
|
+
function Child() {
|
|
279
|
+
const theme = context(ThemeCtx)
|
|
280
|
+
return <div>Theme: {theme}</div>
|
|
224
281
|
}
|
|
225
282
|
```
|
|
226
283
|
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var o="0.12.
|
|
1
|
+
'use strict';var o="0.12.17";exports.VERSION=o;//# sourceMappingURL=index.js.map
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["VERSION"],"mappings":"aAAO,IAAMA,CAAAA,CAAU","file":"index.js","sourcesContent":["export const VERSION = '0.12.
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["VERSION"],"mappings":"aAAO,IAAMA,CAAAA,CAAU","file":"index.js","sourcesContent":["export const VERSION = '0.12.17' // Bump version to signify rebuild\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var o="0.12.
|
|
1
|
+
var o="0.12.17";export{o as VERSION};//# sourceMappingURL=index.mjs.map
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["VERSION"],"mappings":"AAAO,IAAMA,CAAAA,CAAU","file":"index.mjs","sourcesContent":["export const VERSION = '0.12.
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["VERSION"],"mappings":"AAAO,IAAMA,CAAAA,CAAU","file":"index.mjs","sourcesContent":["export const VERSION = '0.12.17' // Bump version to signify rebuild\n"]}
|