fiboui 2.4.3 → 2.5.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 CHANGED
@@ -1,220 +1,271 @@
1
- # FiboUI v2.4.3
1
+ <div align="center">
2
2
 
3
- **A Flutter & SwiftUI-inspired React UI framework featuring a fluent builder API and fine-grained reactivity.**
3
+ <img src="./docs/brand/logo.svg" width="88" alt="FiboUI logo" />
4
4
 
5
- FiboUI brings chainable builders to React. Compose screens with `Column()`, `Row()`, `Button()`, and `signal()`. Builders **are** React elements — return them from functions and skip `.build()`. Prefer the fluent API (no JSX tags).
5
+ # FiboUI
6
6
 
7
- ---
7
+ **Fluent React UI.** Flutter & SwiftUI energy for the web.
8
+ There is no JSX — the function return **is** the view.
8
9
 
9
- ## Features
10
+ <br />
10
11
 
11
- - **Assembly pattern** — factories return builders for endless chaining.
12
- - **Zero-build children** — pass builders to `.children()`; they build implicitly. Typed methods (`.onPress`, `.id`, `.spacing`, `.children`) stay assignable as children.
13
- - **Fine-grained reactivity** — `signal()`, `computed()`, `createStore()`, `.bindSignal()`, `.observer()`.
14
- - **Fluent themes** — `.theme("noir" \| "web" \| …)` / `extendTheme` / `FiboThemeProvider` — CSS vars SSR-safe (Flutter / SwiftUI model). Presets: Material, Cupertino, Web, **Noir**.
15
- - **Type-safe** — TypeScript builders; `FiboChild` / `FiboChildren` describe child slots.
16
- - **Media queries** — `Box().padding(8).md({ padding: 16 }).lg({ padding: 24 })` compiles to CSS `@media`. `.responsive({ mobile, tablet, desktop })` still works (JS).
17
- - **CSS motion & scroll** — `Box().animate('fadeIn').ease('spring')`, `.onScroll('fadeIn')`, `.stagger(80)`, `.scrollProgress()`, `Presence(open).enter('fadeIn').exit('fadeOut')`. Default path is CSS (no rAF). See `docs/guide/animation.md`.
18
- - **Interaction effects** — full Hover.css–inspired catalog (2D, background sweeps, borders, shadows, bubbles, curls) as fiboui-owned CSS. Any trigger: `.hover(h => h.effect("sweepToRight"))`, `.pressEffect("shrink")`, `.hoverEffect("float")`.
12
+ [![npm](https://img.shields.io/npm/v/fiboui?style=for-the-badge&color=ff2d8a&labelColor=0a0a0a)](https://www.npmjs.com/package/fiboui)
13
+ [![downloads](https://img.shields.io/npm/dm/fiboui?style=for-the-badge&color=c8ff00&labelColor=0a0a0a)](https://www.npmjs.com/package/fiboui)
14
+ [![license](https://img.shields.io/badge/license-MIT-0a0a0a?style=for-the-badge&labelColor=0a0a0a&color=ffffff)](https://github.com/felipeoliveiradev/fiboui)
15
+ [![site](https://img.shields.io/badge/site-fiboui.vercel.app-0a0a0a?style=for-the-badge&labelColor=ff2d8a&color=0a0a0a)](https://fiboui.vercel.app)
19
16
 
20
- ---
17
+ <br />
21
18
 
22
- ## Installation
19
+ <img src="./docs/brand/banner.png" width="960" alt="FiboUI — Fluent React UI. There is no JSX." />
23
20
 
24
- ```bash
25
- npm install fiboui
26
- ```
21
+ </div>
27
22
 
28
- Import the stylesheet once in your entry file:
23
+ ---
29
24
 
30
- ```tsx
31
- import 'fiboui/style.css';
32
- ```
25
+ <div align="center">
33
26
 
34
- **Required.** The JS entry does not load CSS. ESM-only (`import`, no `require`). See [MIGRATION.md](./MIGRATION.md) if you used 1.x snapshots.
27
+ ### Live product
35
28
 
36
- Peer dependencies: React 18 or 19.
29
+ | Marketing site | Store demo |
30
+ | :---: | :---: |
31
+ | [![FiboUI site](./docs/brand/site-hero.png)](https://fiboui.vercel.app) | [![Fibo Store](./docs/brand/store-hero.png)](https://fiboui-store.vercel.app) |
32
+ | [**fiboui.vercel.app**](https://fiboui.vercel.app) | [**fiboui-store.vercel.app**](https://fiboui-store.vercel.app) |
37
33
 
38
- Next.js App Router: files that **call** builders (`Button()`, `Column()`, …) must be Client Components (`'use client'`). FiboUI uses hooks and context; it is not an RSC factory API. Import `'fiboui/style.css'` from a layout or the client entry.
34
+ Both ships are built **only** with FiboUI fluent builders — no JSX in the product UI.
39
35
 
40
- ## Releasing
36
+ </div>
41
37
 
42
- Publishing is automated. The owner must add an **`NPM_TOKEN`** automation secret in the GitHub repo settings. Release branch is **`main`**. See [CONTRIBUTING.md](./CONTRIBUTING.md).
38
+ ---
43
39
 
44
- Do not run `npm publish` locally. The `Publish` workflow runs on GitHub Release and on push to `main`: install, typecheck, test, build, then `npm publish --provenance --access public` only if that version is not already on npm. After a successful `main` publish it opens GitHub Release `vX.Y.Z` with the matching CHANGELOG section as notes.
40
+ ## Cursor / VS Code extension
45
41
 
46
- ---
42
+ Fluent trees get Flutter-style **closing labels** (`// Column`, `// List`, `// Form`), **auto-indent** on Enter, **lineage guides** on `.children([…])`, snippets (`fcol`, `frow`, …), chain autocomplete, and the **FiboUI Noir** colour theme.
47
43
 
48
- ## The assembly pattern
44
+ ```bash
45
+ ln -sfn "$(pwd)/extensions/vscode-fiboui" \
46
+ "$HOME/.cursor/extensions/felipeoliveiradev.fiboui-0.1.0"
47
+ ```
49
48
 
50
- ### Components as builders
49
+ Reload the window, then pick theme **FiboUI Noir**. Details: [`extensions/vscode-fiboui/README.md`](./extensions/vscode-fiboui/README.md).
50
+
51
+ ## Install
52
+
53
+ ```bash
54
+ npm install fiboui
55
+ ```
51
56
 
52
57
  ```tsx
53
- import { Card, Text } from 'fiboui';
58
+ import 'fiboui/style.css' // required once — JS does not load CSS
59
+ import { Button, Column, Text, mount } from 'fiboui'
54
60
 
55
- function Hello() {
56
- return Card()
57
- .className('p-8')
58
- .style({ backgroundColor: '#111' })
59
- .children([Text('Hello')]);
61
+ function App() {
62
+ return Column()
63
+ .gap(12)
64
+ .padding(24)
65
+ .children([
66
+ Text('Hello FiboUI').variant('h1'),
67
+ Button('Ship it').onPress(() => undefined),
68
+ ])
60
69
  }
70
+
71
+ mount('#root', App)
61
72
  ```
62
73
 
63
- ### No `.build()`
74
+ ESM-only · React **18 / 19** · Next App Router → `'use client'` on files that call builders.
64
75
 
65
- Builders are valid React elements (`isValidElement(Button('Go'))`). Return them from functions, nest them in `.children()`, or pass them as `React.createElement` arguments. `.build()` still exists and is deprecated.
76
+ ---
66
77
 
67
- The event API on `Button` is **`.onPress`** (not `onClick`). `FiboBuilder.onClick` still exists for raw DOM handlers on any builder.
78
+ ## JSX vs FiboUI
68
79
 
69
80
  ```tsx
70
- import { Button, Column, Text } from 'fiboui';
71
-
72
- function App() {
73
- return Column().children([
74
- Text('Title').variant('h1'),
75
- Button('Action').onPress(() => undefined),
76
- ]);
77
- }
81
+ // ── JSX ──────────────────────────────────
82
+ <div className="flex flex-col gap-3 p-6">
83
+ <h1>Hello</h1>
84
+ <button onClick={onShip}>Ship it</button>
85
+ </div>
86
+
87
+ // ── FiboUI ───────────────────────────────
88
+ Column()
89
+ .gap(12)
90
+ .padding(24)
91
+ .children([
92
+ Text('Hello').variant('h1'),
93
+ Button('Ship it').onPress(onShip),
94
+ ])
78
95
  ```
79
96
 
97
+ Same tree. Method calls instead of tags. Layout, theme, motion, and reactivity stay on the builder.
98
+
80
99
  ---
81
100
 
82
- ## Reactivity & state
101
+ ## Runtime flow
102
+
103
+ <img src="./docs/brand/flow-runtime.png" width="960" alt="Factory → fluent chain → children → React element → mount" />
83
104
 
84
- ### Signals
105
+ ```mermaid
106
+ flowchart LR
107
+ A["Column()"] --> B[".gap().padding()"]
108
+ B --> C[".children([...])"]
109
+ C --> D["React element"]
110
+ D --> E["mount / return"]
111
+
112
+ style A fill:#141414,stroke:#fff,color:#fff
113
+ style B fill:#141414,stroke:#ff2d8a,color:#ff2d8a
114
+ style C fill:#141414,stroke:#c8ff00,color:#c8ff00
115
+ style D fill:#141414,stroke:#fff,color:#fff
116
+ style E fill:#c8ff00,stroke:#c8ff00,color:#0a0a0a
117
+ ```
118
+
119
+ Builders **are** valid React elements (`isValidElement(Button('Go'))`).
120
+ Return them. Nest them. **Skip `.build()`.**
121
+
122
+ ---
85
123
 
86
- `.bindSignal(sig)` subscribes the builder and **re-assembles** when the signal changes. Read the signal inside a **function child** (or `Text(() => ...)`) so the new value is picked up. Interpolating `sig.value` into a string at construction time is a one-shot snapshot.
124
+ ## Signal flow
125
+
126
+ <img src="./docs/brand/flow-signals.png" width="960" alt="signal → bindSignal → function children → UI" />
87
127
 
88
128
  ```tsx
89
- import { Box, Button, Text, signal } from 'fiboui';
129
+ import { Box, Button, Text, signal } from 'fiboui'
90
130
 
91
- const count = signal(0);
131
+ const count = signal(0)
92
132
 
93
133
  Box()
94
134
  .bindSignal(count)
95
135
  .children(() => [
96
136
  Text(`Count: ${count.value}`),
97
137
  Button('+').onPress(() => {
98
- count.value += 1;
138
+ count.value += 1
99
139
  }),
100
- ]);
140
+ ])
101
141
  ```
102
142
 
103
- `useSignal(count)` and `.observer()` with a function child also work. Signal subscriptions unsubscribe on unmount and are Safe for React 18/19 StrictMode (`useSyncExternalStore`). Signals are in-memory and client-only — do not rely on them during SSR.
104
-
105
- `computed(() => ...)` is lazy: the first `.value` read attaches the internal effect. Read it (or `.bindSignal`) so updates notify.
143
+ `.bindSignal` re-assembles. Read `.value` inside a **function child** so updates are live. `` Text(`Total ${todos.$length}`) `` stays live without a function child. Forms and validation: [Form](docs/components/interactive-form.md).
106
144
 
107
- Two store APIs exist: `createStore()` + `useReactiveStore(store)`, and the `@Store()` decorator + `useStore(StoreClass)` (the package-root `useStore`). Builder `.state(key, initial)` is runtime-updatable via `useBuilderState(key)`.
145
+ ```mermaid
146
+ sequenceDiagram
147
+ participant UI as Builder tree
148
+ participant S as signal(count)
149
+ participant H as onPress
108
150
 
109
- ### Global store
110
-
111
- ```tsx
112
- import { Box, Text, createStore } from 'fiboui';
151
+ UI->>S: .bindSignal(count)
152
+ H->>S: count.value += 1
153
+ S-->>UI: notify subscribers
154
+ UI->>UI: re-assemble children()
155
+ ```
113
156
 
114
- const store = createStore({ theme: 'dark', user: 'John' });
157
+ ---
115
158
 
116
- Box()
117
- .observer()
118
- .children(() => Text(store.getState().user));
159
+ ## Package architecture
160
+
161
+ <img src="./docs/brand/flow-architecture.png" width="960" alt="App over layout/content/structure/theme/reactivity over core" />
162
+
163
+ ```mermaid
164
+ flowchart TB
165
+ APP["your app — screens · domain · mount"]
166
+ subgraph builders["fluent builders"]
167
+ L["layout / content<br/>Column · Row · Text · Button"]
168
+ S["structure / nav<br/>Box · Grid · Scaffold"]
169
+ T["theme / motion<br/>noir · animate · hover"]
170
+ R["reactivity<br/>signal · computed · store"]
171
+ end
172
+ CORE["core — FiboBuilder · styleManager · bindSignal"]
173
+
174
+ APP --> L & S & T & R
175
+ L & S & T & R --> CORE
176
+
177
+ style APP fill:#141414,stroke:#c8ff00,color:#c8ff00
178
+ style CORE fill:#c8ff00,stroke:#c8ff00,color:#0a0a0a
179
+ style L fill:#141414,stroke:#ff2d8a,color:#ff2d8a
180
+ style S fill:#141414,stroke:#fff,color:#fff
181
+ style T fill:#141414,stroke:#fff,color:#fff
182
+ style R fill:#141414,stroke:#fff,color:#fff
119
183
  ```
120
184
 
121
185
  ---
122
186
 
123
- ## Layout notes
187
+ ## Why FiboUI
124
188
 
125
- ### Column + Row (`spaceBetween`)
189
+ | | |
190
+ | :--- | :--- |
191
+ | **Assembly pattern** | Factories return builders for endless chaining |
192
+ | **Zero-build children** | Pass builders to `.children()` — they finalize implicitly |
193
+ | **Signals** | `signal()`, `computed()`, `.bindSignal()`, `.observer()` |
194
+ | **Themes** | `.theme("noir")` · `extendTheme` · Material / Cupertino / Web / Noir |
195
+ | **Motion** | `.animate('fadeIn')` · `.onScroll` · `.hoverEffect` · CSS-first |
196
+ | **Type-safe** | TypeScript builders · `FiboChild` / `FiboChildren` |
126
197
 
127
- Flutter `Column` defaults to `CrossAxisAlignment.center`. SwiftUI `VStack` defaults to `.center`. FiboUI **2.0 matches that: `crossAxisAlignment('center')`.**
198
+ ### Themes & motion
128
199
 
129
- **Row uses `align-self: stretch`** so `spaceBetween` still fills the Column width under a centered cross axis.
200
+ ```tsx
201
+ Box()
202
+ .theme('noir')
203
+ .animate('fadeIn')
204
+ .ease('spring')
205
+ .hover((h) => h.effect('sweepToRight'))
206
+
207
+ Box().padding(8).md({ padding: 16 }).lg({ padding: 24 })
208
+ ```
209
+
210
+ ### Layout notes
130
211
 
131
- To stretch every child (Flutter `CrossAxisAlignment.stretch`):
212
+ FiboUI **2.x** matches Flutter / SwiftUI: `Column` defaults to `crossAxisAlignment('center')`.
213
+ `Row` uses `align-self: stretch` so `spaceBetween` still fills width.
132
214
 
133
215
  ```tsx
134
- Column().crossAxisAlignment('stretch');
216
+ Column().crossAxisAlignment('stretch')
135
217
  ```
136
218
 
219
+ `Button` uses **`.onPress`** (not `onClick`).
220
+
137
221
  ---
138
222
 
139
- ## Scaffold & layout shells
223
+ ## Brand
140
224
 
141
- `Scaffold` uses `headerBar`, `footerBar`, `sideBarLeft`, and `sideBarRight`. `DashboardLayout` uses `header` and `sidebar`.
225
+ <div align="center">
142
226
 
143
- ```tsx
144
- import { Header, Scaffold, Sidebar, Text } from 'fiboui';
145
-
146
- Scaffold()
147
- .headerBar(
148
- Header()
149
- .height(80)
150
- .className('px-6 bg-white shadow-sm')
151
- .children([Text('App Title').variant('h2')])
152
- )
153
- .sideBarLeft(
154
- Sidebar().width(240).children([Text('Home'), Text('Settings')])
155
- )
156
- .children([Text('Welcome to the main content area.')]);
157
- ```
227
+ <img src="./docs/brand/logo-lockup.svg" width="280" alt="FiboUI lockup" />
228
+
229
+ <br /><br />
230
+
231
+ | Token | Hex | |
232
+ | :---: | :---: | :--- |
233
+ | **Ink** | `#0a0a0a` | background |
234
+ | **Chalk** | `#ffffff` | type |
235
+ | **Pink** | `#ff2d8a` | energy |
236
+ | **Lime** | `#c8ff00` | mark / CTA |
237
+
238
+ <img src="./docs/brand/fibonacci.svg" width="220" alt="Fibonacci spiral" />
239
+
240
+ **Tailored to each request** · **Attention to detail**
241
+
242
+ </div>
158
243
 
159
244
  ---
160
245
 
161
- ## Example: reactive product grid
246
+ ## Docs & contributing
162
247
 
163
- ```tsx
164
- import {
165
- Badge,
166
- Box,
167
- Button,
168
- Card,
169
- Grid,
170
- Row,
171
- Text,
172
- computed,
173
- createStore,
174
- } from 'fiboui';
175
-
176
- const appStore = createStore({
177
- cart: [] as { id: string; name: string; price: number; quantity: number }[],
178
- products: [
179
- { id: '1', name: 'MacBook Pro', price: 2499 },
180
- { id: '2', name: 'iPhone 15', price: 999 },
181
- ],
182
- });
183
-
184
- const cartTotal = computed(() => {
185
- const cart = appStore.getState().cart;
186
- return cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
187
- });
188
-
189
- export function ShopHeader() {
190
- return Box()
191
- .observer()
192
- .children(() => [
193
- Row().children([
194
- Text(`Total: $${cartTotal.value.toFixed(2)}`).bindSignal(cartTotal),
195
- Badge().text(String(appStore.getState().cart.length)),
196
- ]),
197
- Grid()
198
- .columns(2)
199
- .gap('md')
200
- .children(
201
- appStore.getState().products.map((product) =>
202
- Card().children([
203
- Text(product.name).weight('bold'),
204
- Button(`Add $${product.price}`).onPress(() => {
205
- appStore.setState((s) => ({
206
- cart: [...s.cart, { ...product, quantity: 1 }],
207
- }));
208
- }),
209
- ])
210
- )
211
- ),
212
- ]);
213
- }
214
- ```
248
+ | | |
249
+ | :--- | :--- |
250
+ | Migration from 1.x | [MIGRATION.md](./MIGRATION.md) |
251
+ | Contributing & release | [CONTRIBUTING.md](./CONTRIBUTING.md) |
252
+ | Animation guide | [`docs/guide/animation.md`](./docs/guide/animation.md) |
253
+ | Brand assets | [`docs/brand/`](./docs/brand/) |
254
+
255
+ Publishing is automated on **`main`** (GitHub Actions + `NPM_TOKEN`). Prefer not to `npm publish` locally.
215
256
 
216
257
  ---
217
258
 
218
- ## License
259
+ <div align="center">
260
+
261
+ <img src="./docs/brand/logo-mark.svg" width="48" alt="" />
262
+
263
+ <br /><br />
264
+
265
+ **Wear the return.**
219
266
 
220
267
  MIT © 2025–2026 FiboUI
268
+
269
+ [Site](https://fiboui.vercel.app) · [Store](https://fiboui-store.vercel.app) · [npm](https://www.npmjs.com/package/fiboui) · [GitHub](https://github.com/felipeoliveiradev/fiboui)
270
+
271
+ </div>
@@ -0,0 +1,50 @@
1
+ import { jsx as u } from "react/jsx-runtime";
2
+ import { aa as m, as as p, a9 as v } from "./client-C5fy1-Lh.js";
3
+ const k = ({
4
+ to: t,
5
+ children: c,
6
+ replace: n = !1,
7
+ state: e,
8
+ className: l,
9
+ style: f,
10
+ onClick: i
11
+ }) => {
12
+ const o = m(), a = p(), r = t.startsWith("http") || t.startsWith("//") || t.startsWith("#") ? t : `${(a == null ? void 0 : a.basename) ?? ""}${t}`;
13
+ return /* @__PURE__ */ u(
14
+ "a",
15
+ {
16
+ href: r,
17
+ onClick: (s) => {
18
+ s.ctrlKey || s.metaKey || s.shiftKey || s.button !== 0 || (s.preventDefault(), i && i(s), o(t, { replace: n, state: e }));
19
+ },
20
+ className: l,
21
+ style: f,
22
+ children: c
23
+ }
24
+ );
25
+ }, W = ({
26
+ to: t,
27
+ children: c,
28
+ className: n = "",
29
+ style: e = {},
30
+ activeClassName: l = "active",
31
+ activeStyle: f = {},
32
+ exact: i = !1,
33
+ ...o
34
+ }) => {
35
+ const { currentPath: a } = v(), r = i ? a === t : a.startsWith(t), h = r ? `${n} ${l}`.trim() : n, s = r ? { ...e, ...f } : e;
36
+ return /* @__PURE__ */ u(
37
+ k,
38
+ {
39
+ to: t,
40
+ className: h,
41
+ style: s,
42
+ ...o,
43
+ children: c
44
+ }
45
+ );
46
+ };
47
+ export {
48
+ k as L,
49
+ W as N
50
+ };
@@ -1,7 +1,6 @@
1
- "use client";
2
1
  var h = Object.defineProperty;
3
- var g = (c, t, e) => t in c ? h(c, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : c[t] = e;
4
- var s = (c, t, e) => g(c, typeof t != "symbol" ? t + "" : t, e);
2
+ var g = (a, t, e) => t in a ? h(a, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : a[t] = e;
3
+ var s = (a, t, e) => g(a, typeof t != "symbol" ? t + "" : t, e);
5
4
  const n = class n {
6
5
  constructor() {
7
6
  s(this, "services", /* @__PURE__ */ new Map());
@@ -49,8 +48,8 @@ const n = class n {
49
48
  }
50
49
  };
51
50
  s(n, "instance");
52
- let a = n;
53
- const l = a.getInstance(), i = class i {
51
+ let c = n;
52
+ const f = c.getInstance(), i = class i {
54
53
  constructor() {
55
54
  s(this, "plugins", /* @__PURE__ */ new Map());
56
55
  s(this, "routes", []);
@@ -67,7 +66,7 @@ const l = a.getInstance(), i = class i {
67
66
  return this.plugins.has(t.name) ? this : (this.plugins.set(t.name, t), t.setup(this), this);
68
67
  }
69
68
  registerService(t, e) {
70
- l.register(t, e);
69
+ f.register(t, e);
71
70
  }
72
71
  registerStore(t, e) {
73
72
  const r = e ?? `store-${this.storeSeq++}`;