flet-box 1.0.3 → 1.0.5
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 +10 -0
- package/bin/cli.js +15 -0
- package/bin/commands/checkApi.js +761 -0
- package/bin/commands/create.js +77 -5
- package/bin/utils/dtsProps.js +191 -0
- package/bin/utils/moduleExports.js +165 -0
- package/bin/utils/neutralise.js +64 -0
- package/bin/utils/templates.js +366 -27
- package/docs/CONTRIBUTING.md +156 -0
- package/docs/README.md +68 -0
- package/docs/arquitectura.md +164 -0
- package/docs/cli/README.md +542 -0
- package/docs/guides/README.md +33 -0
- package/docs/guides/app-templates.md +197 -0
- package/docs/guides/compatibility.md +95 -0
- package/docs/guides/embedding.md +284 -0
- package/docs/guides/first-app.md +208 -0
- package/docs/guides/frontend-services.md +171 -0
- package/docs/guides/minification-and-protection.md +260 -0
- package/docs/guides/mobile-and-platforms.md +439 -0
- package/docs/guides/online-updates.md +196 -0
- package/docs/guides/router.md +282 -0
- package/docs/guides/state.md +283 -0
- package/docs/guides/syntax-philosophy.md +150 -0
- package/docs/guides/utilities.md +241 -0
- package/docs/guides/widget-structure.md +163 -0
- package/docs/server/README.md +68 -0
- package/docs/server/START_HERE.md +121 -0
- package/docs/server/api.md +225 -0
- package/docs/server/data-services.md +300 -0
- package/docs/server/deployment.md +113 -0
- package/docs/server/security.md +196 -0
- package/docs/services/README.md +79 -0
- package/docs/services/RamStore.md +84 -0
- package/docs/services/Session.md +84 -0
- package/docs/services/Storage.md +88 -0
- package/docs/services/http.md +86 -0
- package/docs/tools/README.md +138 -0
- package/docs/tools/animation.md +87 -0
- package/docs/tools/async-ids-log.md +80 -0
- package/docs/tools/device.md +132 -0
- package/docs/tools/introspection.md +67 -0
- package/docs/tools/lists.md +96 -0
- package/docs/tools/markdown.md +79 -0
- package/docs/tools/reactivity.md +288 -0
- package/docs/tools/state.md +103 -0
- package/docs/tools/styling.md +92 -0
- package/docs/tools/text-and-time.md +89 -0
- package/docs/tools/theme.md +102 -0
- package/docs/tools/validation.md +75 -0
- package/docs/tools/visual-effects.md +84 -0
- package/docs/widget/Accordion.md +144 -0
- package/docs/widget/AdMob.md +144 -0
- package/docs/widget/AdSense.md +133 -0
- package/docs/widget/AdaptiveScaffold.md +105 -0
- package/docs/widget/AlertDialog.md +103 -0
- package/docs/widget/AnimatedBox.md +130 -0
- package/docs/widget/AnimatedText.md +123 -0
- package/docs/widget/AppBar.md +118 -0
- package/docs/widget/Audio.md +135 -0
- package/docs/widget/Avatar.md +119 -0
- package/docs/widget/Badge.md +128 -0
- package/docs/widget/BottomNavigation.md +116 -0
- package/docs/widget/BottomSheet.md +125 -0
- package/docs/widget/Button.md +137 -0
- package/docs/widget/COMMON_PROPS.md +150 -0
- package/docs/widget/Card.md +125 -0
- package/docs/widget/Carousel.md +151 -0
- package/docs/widget/Chart.md +151 -0
- package/docs/widget/Checkbox.md +118 -0
- package/docs/widget/Chip.md +126 -0
- package/docs/widget/CircularBar.md +168 -0
- package/docs/widget/CircularChart.md +164 -0
- package/docs/widget/CodeViewer.md +130 -0
- package/docs/widget/CollapsibleSideBar.md +112 -0
- package/docs/widget/Column.md +129 -0
- package/docs/widget/Container.md +136 -0
- package/docs/widget/DataTable.md +150 -0
- package/docs/widget/Divider.md +109 -0
- package/docs/widget/DraggBox.md +129 -0
- package/docs/widget/Drawer.md +135 -0
- package/docs/widget/DrawerItem.md +98 -0
- package/docs/widget/Dropdown.md +139 -0
- package/docs/widget/DroppBox.md +152 -0
- package/docs/widget/FloatingActionButton.md +120 -0
- package/docs/widget/GridView.md +140 -0
- package/docs/widget/Icon.md +117 -0
- package/docs/widget/Image.md +125 -0
- package/docs/widget/Input.md +151 -0
- package/docs/widget/Inspector.md +92 -0
- package/docs/widget/InstallButton.md +128 -0
- package/docs/widget/ListTile.md +132 -0
- package/docs/widget/ListView.md +138 -0
- package/docs/widget/Markdown.md +175 -0
- package/docs/widget/MatrixRain.md +122 -0
- package/docs/widget/Modal.md +129 -0
- package/docs/widget/Pagination.md +135 -0
- package/docs/widget/ParallaxBox.md +128 -0
- package/docs/widget/ProgressBar.md +130 -0
- package/docs/widget/QRCode.md +117 -0
- package/docs/widget/README.md +188 -0
- package/docs/widget/Radio.md +123 -0
- package/docs/widget/Rating.md +131 -0
- package/docs/widget/Row.md +131 -0
- package/docs/widget/START_HERE.md +146 -0
- package/docs/widget/Scaffold.md +152 -0
- package/docs/widget/Skeleton.md +130 -0
- package/docs/widget/Slider.md +146 -0
- package/docs/widget/SnackBar.md +102 -0
- package/docs/widget/Stack.md +128 -0
- package/docs/widget/Stepper.md +145 -0
- package/docs/widget/Switch.md +118 -0
- package/docs/widget/Tabs.md +124 -0
- package/docs/widget/Text.md +133 -0
- package/docs/widget/Tooltip.md +145 -0
- package/docs/widget/TreeView.md +176 -0
- package/docs/widget/Video.md +127 -0
- package/docs/widget/_template.md +61 -0
- package/package.json +4 -2
- package/src/animations/AnimatedBox.js +32 -1
- package/src/animations/AnimatedText.js +7 -3
- package/src/animations/MatrixRain.js +37 -12
- package/src/animations/ParallaxBox.js +16 -3
- package/src/animations/animate.js +187 -58
- package/src/animations/index.js +1 -0
- package/src/core/runApp.js +13 -1
- package/src/create-index.sh +1 -1
- package/src/index.d.ts +227 -91
- package/src/index.js +1 -0
- package/src/navigations/AdaptiveScaffold.js +11 -2
- package/src/navigations/AppBar.js +8 -2
- package/src/navigations/BottomNavigation.js +24 -5
- package/src/navigations/DrawerItem.js +16 -3
- package/src/navigations/Scaffold.js +8 -1
- package/src/navigations/Tabs.js +40 -8
- package/src/tools/dimensions.js +11 -3
- package/src/tools/useState.js +139 -101
- package/src/utils/getWidgetProps.js +5 -3
- package/src/widget-factory/assignProps.js +30 -11
- package/src/widget-factory/processProps.js +17 -5
- package/src/widget-factory/reactiveValue.js +172 -0
- package/src/widget-factory/translateProps.js +777 -901
- package/src/widget-factory/warnings.js +15 -0
- package/src/widget-factory/widgetFactory.js +27 -0
- package/src/widgets/Accordion.js +6 -5
- package/src/widgets/AlertDialog.js +7 -5
- package/src/widgets/Audio.js +30 -8
- package/src/widgets/BottomSheet.js +24 -1
- package/src/widgets/Chart.js +4 -1
- package/src/widgets/Checkbox.js +16 -8
- package/src/widgets/CircularBar.js +12 -1
- package/src/widgets/CircularChart.js +4 -1
- package/src/widgets/CodeViewer.js +26 -8
- package/src/widgets/DataTable.js +11 -2
- package/src/widgets/Dropdown.js +5 -1
- package/src/widgets/FloatingActionButton.js +4 -7
- package/src/widgets/Image.js +7 -2
- package/src/widgets/Input.js +119 -6
- package/src/widgets/Inspector.js +16 -10
- package/src/widgets/ListView.js +80 -30
- package/src/widgets/Markdown.js +53 -8
- package/src/widgets/Modal.js +24 -1
- package/src/widgets/Pagination.js +6 -3
- package/src/widgets/ProgressBar.js +16 -13
- package/src/widgets/QRCode.js +9 -2
- package/src/widgets/Radio.js +11 -8
- package/src/widgets/Slider.js +26 -4
- package/src/widgets/SnackBar.js +20 -2
- package/src/widgets/Stack.js +36 -3
- package/src/widgets/Stepper.js +4 -3
- package/src/widgets/Switch.js +8 -5
- package/src/widgets/TreeView.js +3 -2
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# FletBox utilities
|
|
2
|
+
|
|
3
|
+
FletBox includes small helpers for common UI and application tasks. Import only the helpers you need from `flet-box`.
|
|
4
|
+
|
|
5
|
+
## Layout helpers
|
|
6
|
+
|
|
7
|
+
Use direct props for the simplest code:
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
Container({
|
|
11
|
+
padding: 16,
|
|
12
|
+
margin: "8px 0",
|
|
13
|
+
gap: 12,
|
|
14
|
+
});
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Use helpers when spacing or layout becomes more descriptive:
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
import { border, flex, grid, margin, padding, shadow } from "flet-box";
|
|
21
|
+
|
|
22
|
+
Container({
|
|
23
|
+
padding: padding({ horizontal: 20, vertical: 12 }),
|
|
24
|
+
margin: margin({ top: 8, bottom: 16 }),
|
|
25
|
+
border: border(1, "solid", "#e5e7eb"),
|
|
26
|
+
shadow: shadow(0, 4, 12, 0, "rgba(0,0,0,0.12)"),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const columns = grid({ columns: 3, gap: 16 });
|
|
30
|
+
const flexible = flex(1, 0, "auto");
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Colors and effects
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
import { color, gradient, rgba } from "flet-box";
|
|
37
|
+
|
|
38
|
+
const transparentBlue = rgba(37, 99, 235, 0.2);
|
|
39
|
+
const background = gradient("linear", ["#6366f1", "#8b5cf6"], 135);
|
|
40
|
+
const lighter = color("#6366f1").lighten(20);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Color helpers can lighten, darken, adjust alpha, inspect contrast, and create related colors.
|
|
44
|
+
|
|
45
|
+
Effects such as transforms and transitions return values that can be used by supported direct props:
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
import { transform, transition } from "flet-box";
|
|
49
|
+
|
|
50
|
+
Container({
|
|
51
|
+
transform: transform({ translateY: -4, scale: 1.02 }),
|
|
52
|
+
transition: transition({ property: "all", duration: 0.3, timing: "ease" }),
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Lists and mapping
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
import { createList, mapList, range, repeat } from "flet-box";
|
|
60
|
+
|
|
61
|
+
const numbers = range(0, 10, 2);
|
|
62
|
+
const repeated = repeat(3, (index) => Text({ text: `Item ${index}` }));
|
|
63
|
+
const widgets = mapList(users, (user) => Text({ text: user.name }));
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`createList` provides data helpers:
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
const page = createList.paginate(users, 2, 20);
|
|
70
|
+
const matches = createList.search(users, "ada", ["name", "email"]);
|
|
71
|
+
const grouped = createList.groupBy(users, "role");
|
|
72
|
+
const sorted = createList.sort(users, "name", "asc");
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Array helpers include `shuffle`, `reverse`, `sort`, `unique`, and `chunk`.
|
|
76
|
+
|
|
77
|
+
## Random values and ids
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
import { random, shortId, uuid } from "flet-box";
|
|
81
|
+
|
|
82
|
+
const userId = uuid();
|
|
83
|
+
const short = shortId();
|
|
84
|
+
const name = random.fullName();
|
|
85
|
+
const email = random.email();
|
|
86
|
+
const colorValue = random.hexColor();
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Use random helpers for demos and sample data. Do not use generated demo values as security credentials.
|
|
90
|
+
|
|
91
|
+
## Strings and dates
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
import {
|
|
95
|
+
capitalizeWords,
|
|
96
|
+
formatDate,
|
|
97
|
+
relativeTime,
|
|
98
|
+
truncate,
|
|
99
|
+
} from "flet-box";
|
|
100
|
+
|
|
101
|
+
capitalizeWords("flet box");
|
|
102
|
+
truncate("A very long sentence", 10, "...");
|
|
103
|
+
formatDate(new Date(), "YYYY-MM-DD");
|
|
104
|
+
relativeTime("2026-01-01");
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Delays and retries
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
import { delay, retry, withMinDelay } from "flet-box";
|
|
111
|
+
|
|
112
|
+
await delay(500);
|
|
113
|
+
await retry(fetchData, 3, 1000);
|
|
114
|
+
await withMinDelay(fetchData(), 800);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Use these helpers for loading states, demos, and recoverable network operations.
|
|
118
|
+
|
|
119
|
+
## Device and dimensions
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
import { device, dimensions, height, width } from "flet-box";
|
|
123
|
+
|
|
124
|
+
if (device.isMobile()) {
|
|
125
|
+
console.log("Mobile layout");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.log(dimensions.getWidth(), dimensions.getHeight());
|
|
129
|
+
console.log(width, height);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Subscribe to changes:
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
const removeListener = dimensions.addListener(({ width, height }) => {
|
|
136
|
+
console.log(width, height);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
removeListener();
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Themes and colors
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
import {
|
|
146
|
+
colors,
|
|
147
|
+
getColor,
|
|
148
|
+
getTheme,
|
|
149
|
+
setTheme,
|
|
150
|
+
toggleTheme,
|
|
151
|
+
} from "flet-box";
|
|
152
|
+
|
|
153
|
+
const primary = colors.primary;
|
|
154
|
+
const text = getColor("text");
|
|
155
|
+
setTheme("dark");
|
|
156
|
+
const current = getTheme();
|
|
157
|
+
toggleTheme();
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
React to theme changes:
|
|
161
|
+
|
|
162
|
+
```javascript
|
|
163
|
+
import { subscribeTheme } from "flet-box";
|
|
164
|
+
|
|
165
|
+
const unsubscribe = subscribeTheme((nextColors) => {
|
|
166
|
+
console.log(nextColors.primary);
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Clipboard
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
import { clipboard } from "flet-box";
|
|
174
|
+
|
|
175
|
+
clipboard.copy("Copied text");
|
|
176
|
+
const text = await clipboard.read();
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Browser permissions may be required for reading clipboard content.
|
|
180
|
+
|
|
181
|
+
## Text input validation
|
|
182
|
+
|
|
183
|
+
```javascript
|
|
184
|
+
import { TextInputValidator } from "flet-box";
|
|
185
|
+
|
|
186
|
+
TextInputValidator.isEmail("user@example.com");
|
|
187
|
+
TextInputValidator.onlyNumbers("abc123");
|
|
188
|
+
TextInputValidator.sanitize(userInput);
|
|
189
|
+
TextInputValidator.limitLength(userInput, 50);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The `Input` widget can use these rules through its `validation` prop.
|
|
193
|
+
|
|
194
|
+
## Markdown and code helpers
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
import {
|
|
198
|
+
escapeHtml,
|
|
199
|
+
generateHighlightedHtml,
|
|
200
|
+
markdownToWidgets,
|
|
201
|
+
} from "flet-box";
|
|
202
|
+
|
|
203
|
+
const content = markdownToWidgets("# Hello\n\nThis is **bold**");
|
|
204
|
+
const safe = escapeHtml(userText);
|
|
205
|
+
const highlighted = generateHighlightedHtml(code);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Escape untrusted text before displaying it as HTML.
|
|
209
|
+
|
|
210
|
+
## Media time helpers
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
import {
|
|
214
|
+
formatMediaProgress,
|
|
215
|
+
formatMediaTime,
|
|
216
|
+
getProgressPercent,
|
|
217
|
+
percentToSeconds,
|
|
218
|
+
} from "flet-box";
|
|
219
|
+
|
|
220
|
+
formatMediaTime(125); // 02:05
|
|
221
|
+
getProgressPercent(30, 120);
|
|
222
|
+
percentToSeconds(50, 120);
|
|
223
|
+
formatMediaProgress(30, 120);
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Choosing a utility
|
|
227
|
+
|
|
228
|
+
- Need spacing? Start with direct `padding`, `margin`, and `gap` props.
|
|
229
|
+
- Need reusable data? Use `createList`, `mapList`, or `range`.
|
|
230
|
+
- Need shared UI state? Read [State with `useState`](state.md).
|
|
231
|
+
- Need URL changes? Read [Routing](router.md).
|
|
232
|
+
- Need colors that follow the app theme? Use `colors` and `getColor`.
|
|
233
|
+
- Need browser information? Use `device`, `os`, and `dimensions`.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Continue reading
|
|
238
|
+
|
|
239
|
+
- **Previous:** [Animation helpers](../tools/animation.md) — end of the tools chapter.
|
|
240
|
+
- **Next:** [The FletBox CLI](../cli/README.md) — Chapter 11
|
|
241
|
+
- **Index:** [Guides index](README.md) · [The FletBox Book](../README.md)
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Widget structure: one shape for every widget
|
|
2
|
+
|
|
3
|
+
Every widget in FletBox is a plain function that returns an `HTMLElement`, built
|
|
4
|
+
on top of the single rendering engine: [`src/widget-factory/`](../../src/widget-factory/).
|
|
5
|
+
This page is the **canonical structure** all widgets follow — existing and future —
|
|
6
|
+
so the library stays modular, predictable, and easy to review.
|
|
7
|
+
|
|
8
|
+
Reference implementation: [`src/widgets/QRCode.js`](../../src/widgets/QRCode.js).
|
|
9
|
+
|
|
10
|
+
## The shape
|
|
11
|
+
|
|
12
|
+
```javascript
|
|
13
|
+
/**
|
|
14
|
+
* @file MyWidget.js
|
|
15
|
+
* @description One paragraph: what the widget renders and how it behaves.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { WidgetFactory } from "../widget-factory/index.js";
|
|
19
|
+
// import shared helpers (theme, composeUpdate, ...) — never a second engine.
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Module-level constants and pure helpers (no DOM, no side effects)
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
const WIDGET_PROP_KEYS = ["value", "onPress", /* ... */];
|
|
26
|
+
|
|
27
|
+
const normalize = (value) => /* pure transform */;
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Widget
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* JSDoc for every prop, its type and default.
|
|
35
|
+
* @returns {HTMLElement}
|
|
36
|
+
*/
|
|
37
|
+
export const MyWidget = (props) => {
|
|
38
|
+
// 1. Destructure with defaults; keep the rest for the factory.
|
|
39
|
+
const { value = "", onPress, size = 200, ...rest } = props;
|
|
40
|
+
|
|
41
|
+
// 2. Early guards. Never leave a half-built element.
|
|
42
|
+
if (!value) return WidgetFactory({ widgetName: "MyWidget", ...rest });
|
|
43
|
+
|
|
44
|
+
// 3. Closure state / refs (private, mutable).
|
|
45
|
+
let currentValue = value;
|
|
46
|
+
let ref = null;
|
|
47
|
+
|
|
48
|
+
// 4. Exactly ONE factory root.
|
|
49
|
+
const container = WidgetFactory({
|
|
50
|
+
widgetName: "MyWidget",
|
|
51
|
+
...defaults,
|
|
52
|
+
...rest,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// 5. Children via the factory (`child`/`children`) or `appendChild`.
|
|
56
|
+
// Raw `createElement` only for what the factory cannot express (canvas...).
|
|
57
|
+
|
|
58
|
+
// 6. Public, reactive surface. Compose — never overwrite — `container.update`.
|
|
59
|
+
container.setValue = (v) => { currentValue = v; render(); };
|
|
60
|
+
container.getValue = () => currentValue;
|
|
61
|
+
|
|
62
|
+
// 7. Resources: every listener/timer/rAF/observer/subscription goes through
|
|
63
|
+
// onUnmount, and the disposer must be idempotent.
|
|
64
|
+
let disposed = false;
|
|
65
|
+
const dispose = () => {
|
|
66
|
+
if (disposed) return;
|
|
67
|
+
disposed = true;
|
|
68
|
+
// removeEventListener / clearTimeout / disconnect ...
|
|
69
|
+
};
|
|
70
|
+
container.onUnmount(dispose);
|
|
71
|
+
|
|
72
|
+
return container;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export default MyWidget;
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## The rules
|
|
79
|
+
|
|
80
|
+
1. **One root, one engine.** A widget returns a single element created by
|
|
81
|
+
`WidgetFactory`. Never hand-roll a parallel rendering path.
|
|
82
|
+
2. **Module scope is pure.** Constants and helpers at the top of the file do no
|
|
83
|
+
DOM work and register no side effects.
|
|
84
|
+
3. **Props in, element out.** Destructure with defaults, forward `...rest` to the
|
|
85
|
+
factory, guard early. A widget with no data returns a valid element (or a
|
|
86
|
+
documented controller object — see below).
|
|
87
|
+
4. **`widgetName` labels the widget.** Pass `widgetName: "<Name>"` to the root
|
|
88
|
+
factory call so `Inspector`, `printWidgetCode`, and navigation helpers see the
|
|
89
|
+
real name instead of the capitalised HTML tag. (`name` is *not* reserved — it
|
|
90
|
+
is a public prop of `Icon`/`Avatar`.)
|
|
91
|
+
5. **Compose `update`.** Keep widget state in the closure and route updates
|
|
92
|
+
through [`composeUpdate()`](../../src/utils/composeUpdate.js). Overwriting
|
|
93
|
+
`container.update` silently disables the factory's style/event reactivity.
|
|
94
|
+
6. **No ad-hoc global styles.** Scope injected CSS with
|
|
95
|
+
`<style data-widget="<Name>">` or a per-instance adopted stylesheet
|
|
96
|
+
(`markdown-scope-N` pattern) and remove it on unmount. Never inject an
|
|
97
|
+
unscoped `<style>` tag into `document.head` yourself — `@keyframes` cannot be
|
|
98
|
+
scoped, so the only sanctioned global injection is the shared, idempotent
|
|
99
|
+
[`injectKeyframes()`](../../src/utils/styleInjector.js) helper.
|
|
100
|
+
7. **Portals clean up after themselves.** Widgets that append to
|
|
101
|
+
`document.body` (dialogs, dropdowns, tooltips) must remove their node and
|
|
102
|
+
close over the parent's lifecycle via `onUnmount`.
|
|
103
|
+
8. **`onUnmount` is the single cleanup hook.** Register disposers there; they can
|
|
104
|
+
run from more than one teardown path, so make them idempotent. Do not
|
|
105
|
+
monkey-patch `_cleanup`.
|
|
106
|
+
9. **Public API is additive.** Expose behavior through named methods
|
|
107
|
+
(`updateValue`, `getValue`, ...) and reactive accessors. Never mutate
|
|
108
|
+
`container.update` or `container._cleanup`.
|
|
109
|
+
10. **Complete the three surfaces.** Runtime file, export from
|
|
110
|
+
`src/widgets/index.js` **and** `src/index.js`, types in `src/index.d.ts`,
|
|
111
|
+
plus a doc page in `docs/widget/<Name>.md` with `basic`/`normal`/`full`
|
|
112
|
+
examples.
|
|
113
|
+
11. **Flat, aliased CSS props — never `style:`.** FletBox has no `style` object:
|
|
114
|
+
pass CSS props directly (`backgroundColor`, `padding`, `flexDirection`,
|
|
115
|
+
`whiteSpace`, …) with aliases resolved by the factory. `style: { ... }` is
|
|
116
|
+
rejected with a warning (`processProps`), so it never silently half-applies.
|
|
117
|
+
|
|
118
|
+
## Controller widgets
|
|
119
|
+
|
|
120
|
+
A few widgets manage transient UI that lives outside the tree (modals, sheets,
|
|
121
|
+
snackbars). They may return a **controller object** instead of an element, but
|
|
122
|
+
the exception is explicit and typed:
|
|
123
|
+
|
|
124
|
+
| Widget | Returns |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| `AlertDialog` | `{ open, close, modal }` |
|
|
127
|
+
| `SnackBar` | `{ close, show, getElement }` |
|
|
128
|
+
| `BottomSheet` | `{ open, close, toggle, destroy }` |
|
|
129
|
+
| `Modal` | controller instance (`open`/`close`/`updateContent`) |
|
|
130
|
+
|
|
131
|
+
Everything else returns an `HTMLElement`.
|
|
132
|
+
|
|
133
|
+
## Adding a widget — checklist
|
|
134
|
+
|
|
135
|
+
1. Copy the shape above into `src/widgets/<Name>.js`.
|
|
136
|
+
2. Implement props with defaults and JSDoc; forward `...rest`.
|
|
137
|
+
3. Pass `widgetName: "<Name>"` to the root factory call.
|
|
138
|
+
4. Register every resource with an idempotent `onUnmount` disposer.
|
|
139
|
+
5. Export from `src/widgets/index.js` and `src/index.js`.
|
|
140
|
+
6. Add types and props to `src/index.d.ts` (regenerate docs with
|
|
141
|
+
`python3 scripts/generate_widget_docs.py`).
|
|
142
|
+
7. Add `docs/widget/<Name>.md` with `basic`/`normal`/`full` examples.
|
|
143
|
+
8. Verify:
|
|
144
|
+
```bash
|
|
145
|
+
node --check src/widgets/<Name>.js
|
|
146
|
+
node scripts/check-widgets.mjs # static contract (no new violations)
|
|
147
|
+
npm run dev # then open the test harnesses:
|
|
148
|
+
# /tests/widget-contract.html — runtime contract
|
|
149
|
+
# /tests/smoke.html — instantiates every widget
|
|
150
|
+
# /tests/widget-factory.html — frozen engine regression suite
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Tooling
|
|
154
|
+
|
|
155
|
+
- `node scripts/check-widgets.mjs` — static contract checker. It compares the
|
|
156
|
+
code against `scripts/widget-contract-baseline.json`; the baseline records
|
|
157
|
+
pre-existing debt and must shrink as widgets are refactored. Adding a widget
|
|
158
|
+
to the baseline is never the fix.
|
|
159
|
+
- `node scripts/check-widgets.mjs --write-baseline` — re-snapshot after a batch
|
|
160
|
+
of fixes (only to *remove* entries in practice).
|
|
161
|
+
- `/tests/widget-contract.html` — runtime contract: every widget instantiates,
|
|
162
|
+
returns an element (or a documented controller), exposes `update()`, and
|
|
163
|
+
reports its real `_widgetName`.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# FletBox Server
|
|
2
|
+
|
|
3
|
+
FletBox Server is the backend toolkit for building APIs with Node.js. It gives you a simple route definition, automatic schemas, validation, authentication, security middleware, data tools, and generated API documentation.
|
|
4
|
+
|
|
5
|
+
It is a **separate package with its own repository** — [kuko53348/flet-box-server](https://github.com/kuko53348/flet-box-server) — because the frontend and the backend release independently. Nothing here is required to build a FletBox app; the docs live in this book so the whole stack is documented in one place.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install flet-box-server
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Import the core from the package root, and each tool from its own module. The tools are deliberately not re-exported at the root: `pdf.js` pulls puppeteer, `sms.js` pulls twilio, `csv.js` pulls xlsx, and the speech modules need optional Google Cloud packages, so a single root import would force all of them to load.
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import { Api, runServer } from "flet-box-server"; // core
|
|
15
|
+
import { SQLite } from "flet-box-server/tools/betterSqlite.js"; // one tool
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Start here
|
|
19
|
+
|
|
20
|
+
If this is your first backend, read [Start here](START_HERE.md).
|
|
21
|
+
|
|
22
|
+
## Documentation map
|
|
23
|
+
|
|
24
|
+
- [Start here](START_HERE.md): create your first API step by step.
|
|
25
|
+
- [API and routes](api.md): define endpoints, handlers, parameters, and schemas.
|
|
26
|
+
- [Authentication and security](security.md): JWT, roles, sanitization, blocking, headers, and rate limits.
|
|
27
|
+
- [Database and services](data-services.md): where data lives (browser vs server), a dedicated SQLite API server example, cache, files, email, PDF, CSV, queues, and more.
|
|
28
|
+
- [Deployment](deployment.md): environment variables, production settings, Docker, and operational checks.
|
|
29
|
+
|
|
30
|
+
## What the server does
|
|
31
|
+
|
|
32
|
+
- Defines routes with `Api()`.
|
|
33
|
+
- Runs on the native Node.js HTTP server or an Express adapter.
|
|
34
|
+
- Creates JSON responses automatically for returned objects.
|
|
35
|
+
- Generates schemas from request examples.
|
|
36
|
+
- Validates request bodies with `SchemaValidator`.
|
|
37
|
+
- Serves generated API documentation at `/docs`.
|
|
38
|
+
- Supports JWT authentication and role checks.
|
|
39
|
+
- Provides security headers, sanitization, malicious-input blocking, and rate limiting.
|
|
40
|
+
- Includes modules for SQLite, Redis, files, email, SMS, PDF, CSV/XLSX, WebSockets, queues, speech, translation, and logging.
|
|
41
|
+
|
|
42
|
+
## Important production rule
|
|
43
|
+
|
|
44
|
+
The example server in `flet-box-server/app.js` is a test server. It disables some protections and contains a development admin account. Do not copy those values into production. Use `runSecureServer`, environment variables, a real database, a strong `JWT_SECRET`, and explicit CORS origins.
|
|
45
|
+
|
|
46
|
+
## Minimal server
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
import { Api, runServer } from "flet-box-server";
|
|
50
|
+
|
|
51
|
+
const routes = Api({
|
|
52
|
+
"/": {
|
|
53
|
+
GET: () => ({ message: "Hello from FletBox Server" }),
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
runServer(routes, {
|
|
58
|
+
port: 3000,
|
|
59
|
+
docs: true,
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Then open:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
http://localhost:3000/
|
|
67
|
+
http://localhost:3000/docs
|
|
68
|
+
```
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Start here
|
|
2
|
+
|
|
3
|
+
This guide teaches the backend in small steps. You only need JavaScript and a terminal.
|
|
4
|
+
|
|
5
|
+
## 1. Install
|
|
6
|
+
|
|
7
|
+
Inside `flet-box-server`:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The server requires Node.js and uses ES modules.
|
|
14
|
+
|
|
15
|
+
## 2. Create one route
|
|
16
|
+
|
|
17
|
+
Create `server.js`:
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
import { Api, runServer } from "flet-box-server";
|
|
21
|
+
|
|
22
|
+
const routes = Api({
|
|
23
|
+
"/hello": {
|
|
24
|
+
GET: () => ({ message: "Hello" }),
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
runServer(routes, { port: 3000, docs: true });
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Start it:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
node server.js
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Test it:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
curl http://localhost:3000/hello
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 3. Understand the route
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
"/users/:id": {
|
|
47
|
+
GET: ({ id, query, req }) => ({
|
|
48
|
+
id,
|
|
49
|
+
filters: query,
|
|
50
|
+
userAgent: req.headers["user-agent"],
|
|
51
|
+
}),
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- `/users/:id` is the URL pattern.
|
|
56
|
+
- `id` comes from the URL.
|
|
57
|
+
- `query` contains values such as `?page=2`.
|
|
58
|
+
- `req` is the native request object.
|
|
59
|
+
- Returning an object sends JSON to the client.
|
|
60
|
+
|
|
61
|
+
## 4. Accept JSON data
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
const routes = Api({
|
|
65
|
+
"/tasks": {
|
|
66
|
+
POST: {
|
|
67
|
+
handler: ({ title, completed }) => ({
|
|
68
|
+
title,
|
|
69
|
+
completed,
|
|
70
|
+
}),
|
|
71
|
+
example: {
|
|
72
|
+
title: "Learn FletBox Server",
|
|
73
|
+
completed: false,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The `example` documents the expected body and can be used to generate a schema.
|
|
81
|
+
|
|
82
|
+
Request it with:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
curl -X POST http://localhost:3000/tasks \
|
|
86
|
+
-H "Content-Type: application/json" \
|
|
87
|
+
-d '{"title":"Learn FletBox Server","completed":false}'
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 5. Add protection
|
|
91
|
+
|
|
92
|
+
For a production-style default, use:
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
import { Api, runSecureServer } from "flet-box-server";
|
|
96
|
+
|
|
97
|
+
const routes = Api({
|
|
98
|
+
"/private": {
|
|
99
|
+
GET: () => ({ secret: "protected data" }),
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
runSecureServer(routes, {
|
|
104
|
+
port: 3000,
|
|
105
|
+
docs: true,
|
|
106
|
+
rateLimit: { max: 100, windowMs: 60_000 },
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Add authentication to routes that require a logged-in user. See [Authentication and security](security.md).
|
|
111
|
+
|
|
112
|
+
## 6. Learn the normal workflow
|
|
113
|
+
|
|
114
|
+
1. Define routes with `Api()`.
|
|
115
|
+
2. Add `example` objects for request bodies.
|
|
116
|
+
3. Add `responseExample` objects for documented responses.
|
|
117
|
+
4. Add middleware such as authentication or roles.
|
|
118
|
+
5. Start with `runSecureServer()`.
|
|
119
|
+
6. Test with curl or your frontend.
|
|
120
|
+
7. Open `/docs` to inspect generated documentation.
|
|
121
|
+
8. Move secrets and configuration to environment variables.
|