flet-box 1.1.2 → 1.1.3
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/AGENTS_FOR_CODE_GEN.md +284 -0
- package/CONTRIBUTING.md +238 -0
- package/README.md +2 -2
- package/bin/cli.js +88 -1
- package/bin/commands/auditFix.js +181 -27
- package/bin/commands/checkApi.js +14 -3
- package/bin/commands/checkStructure.js +368 -0
- package/bin/commands/checkSyntax.js +293 -0
- package/bin/commands/checkUi.js +666 -14
- package/bin/commands/create.js +14 -0
- package/bin/commands/createBundle.js +2 -2
- package/bin/commands/metrics.js +448 -0
- package/bin/commands/release.js +18 -0
- package/bin/commands/report.js +849 -0
- package/bin/commands/tools.js +76 -0
- package/bin/commands/unpack.js +39 -11
- package/bin/commands/verify.js +879 -0
- package/bin/mcp.js +280 -0
- package/bin/utils/contract.js +27 -0
- package/bin/utils/dtsProps.js +5 -1
- package/bin/utils/migrationGuide.js +146 -0
- package/bin/utils/moduleExports.js +9 -5
- package/bin/utils/templates.js +225 -18
- package/bin/utils/toolkit.js +191 -0
- package/docs/README.md +5 -1
- package/docs/arquitectura.md +1 -1
- package/docs/cli/README.md +168 -2
- package/docs/guides/README.md +6 -0
- package/docs/guides/accessibility.md +2 -1
- package/docs/guides/ai-toolkit.md +166 -0
- package/docs/guides/compatibility.md +1 -1
- package/docs/guides/migration.md +92 -0
- package/docs/guides/minification-and-protection.md +2 -1
- package/docs/guides/mobile-and-platforms.md +1 -1
- package/docs/guides/online-updates.md +2 -0
- package/docs/server/deployment.md +1 -1
- package/docs/tools/styling.md +2 -2
- package/docs/tools/theme.md +139 -4
- package/docs/widget/Accordion.md +74 -8
- package/docs/widget/AdMob.md +68 -3
- package/docs/widget/AdSense.md +67 -2
- package/docs/widget/AlertDialog.md +1 -1
- package/docs/widget/AnimatedBox.md +80 -39
- package/docs/widget/AnimatedText.md +74 -18
- package/docs/widget/Audio.md +73 -14
- package/docs/widget/Avatar.md +69 -4
- package/docs/widget/Badge.md +68 -3
- package/docs/widget/BottomSheet.md +2 -2
- package/docs/widget/Button.md +69 -3
- package/docs/widget/Card.md +69 -4
- package/docs/widget/Carousel.md +67 -2
- package/docs/widget/Chart.md +67 -2
- package/docs/widget/Checkbox.md +74 -8
- package/docs/widget/Chip.md +68 -3
- package/docs/widget/CircularBar.md +74 -15
- package/docs/widget/CircularChart.md +71 -10
- package/docs/widget/CodeViewer.md +67 -2
- package/docs/widget/Column.md +68 -3
- package/docs/widget/Container.md +68 -3
- package/docs/widget/DataTable.md +67 -2
- package/docs/widget/Divider.md +67 -2
- package/docs/widget/DraggBox.md +67 -2
- package/docs/widget/Dropdown.md +76 -10
- package/docs/widget/DroppBox.md +67 -2
- package/docs/widget/FloatingActionButton.md +68 -3
- package/docs/widget/GridView.md +67 -2
- package/docs/widget/Icon.md +71 -5
- package/docs/widget/Image.md +68 -3
- package/docs/widget/Input.md +68 -3
- package/docs/widget/Inspector.md +2 -2
- package/docs/widget/InstallButton.md +73 -13
- package/docs/widget/ListTile.md +69 -3
- package/docs/widget/ListView.md +67 -2
- package/docs/widget/Maps.md +68 -3
- package/docs/widget/Markdown.md +74 -15
- package/docs/widget/MatrixRain.md +72 -11
- package/docs/widget/Menu.md +209 -0
- package/docs/widget/Modal.md +3 -3
- package/docs/widget/Pagination.md +68 -3
- package/docs/widget/ParallaxBox.md +72 -12
- package/docs/widget/ProgressBar.md +69 -4
- package/docs/widget/QRCode.md +67 -2
- package/docs/widget/README.md +54 -50
- package/docs/widget/Radio.md +76 -10
- package/docs/widget/Rating.md +70 -6
- package/docs/widget/Row.md +68 -3
- package/docs/widget/Skeleton.md +68 -3
- package/docs/widget/Slider.md +75 -9
- package/docs/widget/SnackBar.md +2 -2
- package/docs/widget/Stack.md +68 -3
- package/docs/widget/Stepper.md +68 -3
- package/docs/widget/Switch.md +74 -8
- package/docs/widget/Text.md +71 -4
- package/docs/widget/ThemePicker.md +197 -0
- package/docs/widget/Tooltip.md +72 -7
- package/docs/widget/TreeView.md +70 -10
- package/docs/widget/Video.md +71 -10
- package/package.json +10 -3
- package/skills/fletbox-design/SKILL.md +89 -5
- package/skills/fletbox-motion/SKILL.md +1 -1
- package/skills/fletbox-rules/SKILL.md +56 -4
- package/src/core/runApp.js +12 -0
- package/src/index.d.ts +374 -101
- package/src/index.js +9 -0
- package/src/navigations/AdaptiveScaffold.js +2 -2
- package/src/navigations/BottomNavigation.js +1 -1
- package/src/navigations/Scaffold.js +18 -6
- package/src/tools/useState.js +11 -4
- package/src/utils/colors.js +275 -0
- package/src/utils/errorBoundary.js +288 -0
- package/src/utils/index.js +10 -2
- package/src/utils/stackPosition.js +39 -24
- package/src/utils/syntaxHighlight.js +4 -1
- package/src/utils/themes.js +279 -3
- package/src/widget-factory/addChildren.js +17 -10
- package/src/widget-factory/assignProps.js +18 -6
- package/src/widget-factory/createWidget.js +7 -0
- package/src/widget-factory/deprecations.js +77 -0
- package/src/widget-factory/translateProps.js +11 -1
- package/src/widget-factory/warnings.js +1 -27
- package/src/widget-factory/widgetFactory.js +59 -17
- package/src/widgets/Accordion.js +21 -7
- package/src/widgets/Badge.js +11 -2
- package/src/widgets/Button.js +15 -3
- package/src/widgets/Carousel.js +12 -4
- package/src/widgets/Chart.js +7 -2
- package/src/widgets/Checkbox.js +1 -2
- package/src/widgets/Chip.js +24 -10
- package/src/widgets/CircularBar.js +9 -3
- package/src/widgets/CircularChart.js +5 -3
- package/src/widgets/Container.js +6 -0
- package/src/widgets/Divider.js +3 -1
- package/src/widgets/DraggBox.js +9 -0
- package/src/widgets/Dropdown.js +3 -3
- package/src/widgets/DroppBox.js +5 -0
- package/src/widgets/FloatingActionButton.js +1 -1
- package/src/widgets/Icon.js +5 -1
- package/src/widgets/InstallButton.js +4 -2
- package/src/widgets/Markdown.js +171 -103
- package/src/widgets/Menu.js +340 -0
- package/src/widgets/Modal.js +7 -1
- package/src/widgets/Pagination.js +10 -5
- package/src/widgets/ProgressBar.js +5 -4
- package/src/widgets/QRCode.js +4 -0
- package/src/widgets/Radio.js +1 -2
- package/src/widgets/Rating.js +9 -0
- package/src/widgets/Slider.js +4 -1
- package/src/widgets/SnackBar.js +2 -2
- package/src/widgets/Switch.js +1 -2
- package/src/widgets/Text.js +9 -5
- package/src/widgets/ThemePicker.js +234 -0
- package/src/widgets/TreeView.js +2 -1
- package/src/widgets/index.js +2 -0
- package/docs/CONTRIBUTING.md +0 -156
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file ThemePicker.js
|
|
3
|
+
* @description A grid of two-tone swatches that picks the screen's accent color.
|
|
4
|
+
*
|
|
5
|
+
* One control, twelve choices, no color theory. Each swatch is a square split
|
|
6
|
+
* down its diagonal: the upper-left half is the family's accent in the light
|
|
7
|
+
* theme, the lower-right half is the same family in the dark one. That is the
|
|
8
|
+
* iOS Settings shape, and it is the only honest way to show a *pair* — a FletBox
|
|
9
|
+
* accent is not one color but the two stops one hue wears in the two themes, and
|
|
10
|
+
* a reader who sees both knows what the app will look like before tapping.
|
|
11
|
+
*
|
|
12
|
+
* Tapping calls `setAccent`, which repaints every widget already on screen and
|
|
13
|
+
* writes the choice to `localStorage`, so the next visit opens in the same color.
|
|
14
|
+
* With `surfaces: true` the tap lays the family over the page as well — background,
|
|
15
|
+
* panels, cards and hairlines all read off the same ramp — so the picker chooses a
|
|
16
|
+
* *theme* and not only a brand color. The picker subscribes to the theme, so a
|
|
17
|
+
* toggle somewhere else on the screen moves the selection here without this widget
|
|
18
|
+
* being asked.
|
|
19
|
+
*/
|
|
20
|
+
import { WidgetFactory } from "../widget-factory/index.js";
|
|
21
|
+
import { Text } from "./Text.js";
|
|
22
|
+
import { Icon } from "./Icon.js";
|
|
23
|
+
import {
|
|
24
|
+
colors,
|
|
25
|
+
getAccent,
|
|
26
|
+
getAccentSurfaces,
|
|
27
|
+
setAccent,
|
|
28
|
+
listAccents,
|
|
29
|
+
subscribeTheme,
|
|
30
|
+
} from "../utils/themes.js";
|
|
31
|
+
import { readableInk } from "../utils/readableInk.js";
|
|
32
|
+
import { composeUpdate } from "../utils/composeUpdate.js";
|
|
33
|
+
import { TAP_MINIMUM, LABEL_TEXT, PILL } from "../tools/scale.js";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {Object} props
|
|
37
|
+
* @returns {HTMLElement} The picker's root row.
|
|
38
|
+
*/
|
|
39
|
+
export const ThemePicker = (props = {}) => {
|
|
40
|
+
const {
|
|
41
|
+
accents = listAccents(),
|
|
42
|
+
size = TAP_MINIMUM,
|
|
43
|
+
showLabels = false,
|
|
44
|
+
label = "Accent color",
|
|
45
|
+
disabled = false,
|
|
46
|
+
surfaces = false,
|
|
47
|
+
onChange,
|
|
48
|
+
...rest
|
|
49
|
+
} = props;
|
|
50
|
+
|
|
51
|
+
// What the cells are built from, and what a rebuild replaces. Reading these from
|
|
52
|
+
// the closure instead of the destructured props is what lets `update()` change
|
|
53
|
+
// the list: a cell built at construction time would keep painting the family it
|
|
54
|
+
// was born with.
|
|
55
|
+
let choices = accents;
|
|
56
|
+
let labelled = showLabels;
|
|
57
|
+
let off = disabled;
|
|
58
|
+
// Whether a tap lays the family over the page too, or only over the brand.
|
|
59
|
+
let tinted = surfaces;
|
|
60
|
+
|
|
61
|
+
// The diagonal split. `135deg` runs from the top-left corner, so the first stop
|
|
62
|
+
// owns the half a reader's eye reaches first — which is the light theme, the one
|
|
63
|
+
// a picker is usually opened under.
|
|
64
|
+
const swatchPaint = (accent) =>
|
|
65
|
+
`linear-gradient(135deg, ${accent.light} 0 50%, ${accent.dark} 50% 100%)`;
|
|
66
|
+
|
|
67
|
+
// The rim is the only thing that says "this one is on", so it is the one thing
|
|
68
|
+
// that must not be a color nobody can see: `colors.border` measures 1.3:1 on
|
|
69
|
+
// white and 1.2:1 on a dark card. An unselected swatch wears `gray500` — 3:1 on
|
|
70
|
+
// both surfaces, the same choice `Checkbox` makes for an empty box — and the
|
|
71
|
+
// selected one wears the page's own ink, the strongest value the theme has. The
|
|
72
|
+
// tick badge is ink too, and `readableInk` picks whichever of black and white
|
|
73
|
+
// reads on it, so the selected state is unmistakable without spending the accent
|
|
74
|
+
// on it — which rule 28 forbids anyway.
|
|
75
|
+
const rimFor = (selected) =>
|
|
76
|
+
selected ? colors.text : off ? colors.border : colors.gray500;
|
|
77
|
+
|
|
78
|
+
const cells = [];
|
|
79
|
+
|
|
80
|
+
const paintCell = (cell, selected) => {
|
|
81
|
+
cell.swatch.style.border = `2px solid ${rimFor(selected)}`;
|
|
82
|
+
cell.badge.style.display = selected ? "flex" : "none";
|
|
83
|
+
cell.root.setAttribute("aria-checked", String(selected));
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const paintAll = () => {
|
|
87
|
+
const active = getAccent();
|
|
88
|
+
cells.forEach((cell) => paintCell(cell, cell.accent.id === active));
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const buildCell = (accent) => {
|
|
92
|
+
const tickSize = Math.max(14, Math.round(size * 0.42));
|
|
93
|
+
|
|
94
|
+
// The tick only ever paints on its own badge, so the pair it has to clear is
|
|
95
|
+
// badge ink on badge plate — a value this file controls, not a user's color.
|
|
96
|
+
const badge = WidgetFactory({
|
|
97
|
+
tag: "span",
|
|
98
|
+
display: "none",
|
|
99
|
+
alignItems: "center",
|
|
100
|
+
justifyContent: "center",
|
|
101
|
+
width: tickSize,
|
|
102
|
+
height: tickSize,
|
|
103
|
+
borderRadius: PILL,
|
|
104
|
+
backgroundColor: colors.text,
|
|
105
|
+
child: Icon({
|
|
106
|
+
name: "check",
|
|
107
|
+
size: Math.max(10, Math.round(size * 0.28)),
|
|
108
|
+
color: readableInk(colors.text, colors.onPrimary),
|
|
109
|
+
}),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const swatch = WidgetFactory({
|
|
113
|
+
tag: "span",
|
|
114
|
+
width: size,
|
|
115
|
+
height: size,
|
|
116
|
+
gradient: swatchPaint(accent),
|
|
117
|
+
border: `2px solid ${rimFor(false)}`,
|
|
118
|
+
borderRadius: 8,
|
|
119
|
+
boxSizing: "border-box",
|
|
120
|
+
display: "flex",
|
|
121
|
+
alignItems: "center",
|
|
122
|
+
justifyContent: "center",
|
|
123
|
+
flexShrink: 0,
|
|
124
|
+
transition: "border-color 0.2s ease",
|
|
125
|
+
child: badge,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const root = WidgetFactory({
|
|
129
|
+
// The square is a `span` inside a row, so nothing here is a radio unless the
|
|
130
|
+
// widget says so. `role` reaches the factory before `applyEffects`, which is
|
|
131
|
+
// what keeps the engine from labelling the control `button`.
|
|
132
|
+
role: "radio",
|
|
133
|
+
"aria-checked": "false",
|
|
134
|
+
"aria-label": accent.label,
|
|
135
|
+
widgetName: "ThemePicker",
|
|
136
|
+
display: "inline-flex",
|
|
137
|
+
flexDirection: "column",
|
|
138
|
+
alignItems: "center",
|
|
139
|
+
gap: labelled ? 4 : undefined,
|
|
140
|
+
minWidth: size,
|
|
141
|
+
minHeight: size,
|
|
142
|
+
padding: labelled ? 4 : 0,
|
|
143
|
+
cursor: off ? "not-allowed" : "pointer",
|
|
144
|
+
opacity: off ? 0.5 : 1,
|
|
145
|
+
// Pressing a caption must not scale the caption: the root is the hit box and
|
|
146
|
+
// the square is the thing that reads as selected.
|
|
147
|
+
disableTransform: labelled || undefined,
|
|
148
|
+
child: labelled
|
|
149
|
+
? [
|
|
150
|
+
swatch,
|
|
151
|
+
Text({
|
|
152
|
+
text: accent.label,
|
|
153
|
+
size: LABEL_TEXT,
|
|
154
|
+
color: off ? colors.textDisabled : colors.textSecondary,
|
|
155
|
+
}),
|
|
156
|
+
]
|
|
157
|
+
: swatch,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const cell = { accent, root, swatch, badge };
|
|
161
|
+
|
|
162
|
+
if (off) {
|
|
163
|
+
root.setAttribute("aria-disabled", "true");
|
|
164
|
+
} else {
|
|
165
|
+
// Routed through the factory rather than assigned to `onclick`: `applyEffects`
|
|
166
|
+
// is what gives this cell Enter, Space and a Tab stop, and it only sees
|
|
167
|
+
// handlers that arrive as props.
|
|
168
|
+
root.update({
|
|
169
|
+
onPress: () => {
|
|
170
|
+
// Re-tapping the ticked cell still has to work when the cell changed
|
|
171
|
+
// meaning: the same id paints brand-only or a whole theme depending on
|
|
172
|
+
// `surfaces`, and that flag can arrive through `update()`.
|
|
173
|
+
if (getAccent() === accent.id && getAccentSurfaces() === tinted) return;
|
|
174
|
+
setAccent(accent.id, { surfaces: tinted });
|
|
175
|
+
if (onChange) onChange(accent.id);
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return cell;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const rebuild = () => {
|
|
184
|
+
cells.forEach((cell) => cell.root.remove());
|
|
185
|
+
cells.length = 0;
|
|
186
|
+
choices.forEach((accent) => {
|
|
187
|
+
const cell = buildCell(accent);
|
|
188
|
+
cells.push(cell);
|
|
189
|
+
group.appendChild(cell.root);
|
|
190
|
+
});
|
|
191
|
+
paintAll();
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const group = WidgetFactory({
|
|
195
|
+
widgetName: "ThemePicker",
|
|
196
|
+
// A row of colored squares is not a control. Named, it is one, and the twelve
|
|
197
|
+
// radios inside it are read as options of a single question.
|
|
198
|
+
role: "radiogroup",
|
|
199
|
+
"aria-label": label,
|
|
200
|
+
display: "flex",
|
|
201
|
+
flexWrap: "wrap",
|
|
202
|
+
alignItems: "flex-start",
|
|
203
|
+
gap: 8,
|
|
204
|
+
...rest,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
rebuild();
|
|
208
|
+
|
|
209
|
+
// The selection is the theme's state, not this widget's. A toggle in the app bar,
|
|
210
|
+
// an OS flip, or a `setAccent` from a settings screen all move what is checked
|
|
211
|
+
// here, and none of them can be reached by a prop.
|
|
212
|
+
const unsubscribe = subscribeTheme(paintAll);
|
|
213
|
+
group.onUnmount(() => {
|
|
214
|
+
unsubscribe();
|
|
215
|
+
cells.length = 0;
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
composeUpdate(
|
|
219
|
+
group,
|
|
220
|
+
["accents", "showLabels", "label", "disabled", "surfaces"],
|
|
221
|
+
(next) => {
|
|
222
|
+
if (next.label !== undefined) group.setAttribute("aria-label", next.label);
|
|
223
|
+
if (next.accents !== undefined) choices = next.accents;
|
|
224
|
+
if (next.showLabels !== undefined) labelled = next.showLabels;
|
|
225
|
+
if (next.disabled !== undefined) off = next.disabled;
|
|
226
|
+
if (next.surfaces !== undefined) tinted = next.surfaces;
|
|
227
|
+
if (next.accents || next.showLabels || next.disabled || next.surfaces) rebuild();
|
|
228
|
+
},
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
return group;
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
export default ThemePicker;
|
package/src/widgets/TreeView.js
CHANGED
|
@@ -13,7 +13,8 @@ export const TreeView = (props) => {
|
|
|
13
13
|
nodes = [],
|
|
14
14
|
onSelect,
|
|
15
15
|
onToggle,
|
|
16
|
-
|
|
16
|
+
// The tree's own name for the selection callback. `onPress` is the
|
|
17
|
+
// framework-wide spelling and works here too — it receives the node.
|
|
17
18
|
onPress: onPressProp,
|
|
18
19
|
expandedNodes = [],
|
|
19
20
|
indent = 16,
|
package/src/widgets/index.js
CHANGED
|
@@ -37,9 +37,11 @@ export { DroppBox } from "./DroppBox.js";
|
|
|
37
37
|
export { DataTable } from "./DataTable.js";
|
|
38
38
|
export { Carousel } from "./Carousel.js";
|
|
39
39
|
export { Tooltip } from "./Tooltip.js";
|
|
40
|
+
export { Menu } from "./Menu.js";
|
|
40
41
|
export { Pagination } from "./Pagination.js";
|
|
41
42
|
export { TreeView } from "./TreeView.js";
|
|
42
43
|
export { Stepper } from "./Stepper.js";
|
|
44
|
+
export { ThemePicker } from "./ThemePicker.js";
|
|
43
45
|
export { Skeleton } from "./Skeleton.js";
|
|
44
46
|
export { Chart } from "./Chart.js";
|
|
45
47
|
export { QRCode } from "./QRCode.js";
|
package/docs/CONTRIBUTING.md
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
# Contributing to FletBox
|
|
2
|
-
|
|
3
|
-
Thank you for helping improve FletBox. The project is designed to stay friendly to developers, so contributions should keep the API readable, predictable, and practical.
|
|
4
|
-
|
|
5
|
-
## Project areas
|
|
6
|
-
|
|
7
|
-
```text
|
|
8
|
-
src/core/ Runtime, mounting, PWA, and HMR
|
|
9
|
-
src/widgets/ UI widgets
|
|
10
|
-
src/widget-factory/Widget creation and prop processing
|
|
11
|
-
src/navigations/ Scaffold, drawer, tabs, and router
|
|
12
|
-
src/services/ Browser storage and HTTP helpers
|
|
13
|
-
src/tools/ State and general-purpose tools
|
|
14
|
-
src/utils/ Themes, parsing, effects, units, and validation
|
|
15
|
-
bin/ CLI and project generators
|
|
16
|
-
docs/ User and API documentation
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Before changing code
|
|
20
|
-
|
|
21
|
-
1. Read the owning module and its nearest tests or examples.
|
|
22
|
-
2. Check `src/index.js` to understand the public export.
|
|
23
|
-
3. Check `src/index.d.ts` when changing public types.
|
|
24
|
-
4. Search for existing aliases and related widgets.
|
|
25
|
-
5. Keep unrelated refactors out of the change.
|
|
26
|
-
|
|
27
|
-
## Adding a widget
|
|
28
|
-
|
|
29
|
-
A widget should normally include:
|
|
30
|
-
|
|
31
|
-
- A file under `src/widgets/`.
|
|
32
|
-
- An export in `src/widgets/index.js`.
|
|
33
|
-
- An export in `src/index.js`.
|
|
34
|
-
- A prop interface and function declaration in `src/index.d.ts`.
|
|
35
|
-
- Basic, everyday, and full examples.
|
|
36
|
-
- A documentation page under `docs/widget/`.
|
|
37
|
-
- Accessibility notes when it is interactive.
|
|
38
|
-
- Cleanup for timers, listeners, observers, and subscriptions.
|
|
39
|
-
|
|
40
|
-
Use the existing `WidgetFactory` patterns instead of creating a second rendering system.
|
|
41
|
-
|
|
42
|
-
## Adding a prop
|
|
43
|
-
|
|
44
|
-
When adding a public prop:
|
|
45
|
-
|
|
46
|
-
1. Implement it in the widget.
|
|
47
|
-
2. Add its TypeScript type.
|
|
48
|
-
3. Add its default value and behavior documentation.
|
|
49
|
-
4. Add an example that proves the behavior.
|
|
50
|
-
5. Check whether it should have a friendly alias.
|
|
51
|
-
6. Test both the preferred name and aliases when aliases are intentional.
|
|
52
|
-
|
|
53
|
-
FletBox supports developer-friendly aliases, but each alias must have one clear meaning.
|
|
54
|
-
|
|
55
|
-
## Documentation pattern
|
|
56
|
-
|
|
57
|
-
Widget pages follow this structure:
|
|
58
|
-
|
|
59
|
-
1. Overview.
|
|
60
|
-
2. Learn it in one minute.
|
|
61
|
-
3. When to use.
|
|
62
|
-
4. Common props.
|
|
63
|
-
5. Full prop list.
|
|
64
|
-
6. How props work.
|
|
65
|
-
7. Example usage.
|
|
66
|
-
8. Basic, everyday, and full examples.
|
|
67
|
-
9. Common layout and styling examples.
|
|
68
|
-
10. Beginner tips.
|
|
69
|
-
11. Common mistakes.
|
|
70
|
-
12. Behavior notes.
|
|
71
|
-
13. Accessibility.
|
|
72
|
-
14. Related widgets.
|
|
73
|
-
|
|
74
|
-
Regenerate widget documentation with:
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
python3 scripts/generate_widget_docs.py
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
The generator uses `src/index.d.ts` and the supplied snippet library as references.
|
|
81
|
-
|
|
82
|
-
## Runtime and types must agree
|
|
83
|
-
|
|
84
|
-
A public API is not complete until these three surfaces agree:
|
|
85
|
-
|
|
86
|
-
```text
|
|
87
|
-
runtime implementation
|
|
88
|
-
src/index.js exports
|
|
89
|
-
src/index.d.ts declarations
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
If a function is declared but not exported, or exported but not typed, do not ship it as stable: fix the source of truth.
|
|
93
|
-
|
|
94
|
-
`npm test` enforces this with `scripts/check-api-surface.mjs`. It imports `src/index.js` for real, so it fails when a runtime export has no declaration, when a declared value does not exist at runtime, when an `interface`/`type` is not `export`ed (a consumer could not `import type` it), or when the barrel re-exports a name that no longer exists. It also fails when the package stops being importable in Node, so never touch `window` / `document` at module scope — guard with `typeof window !== "undefined"`.
|
|
95
|
-
|
|
96
|
-
## Cleanup rules
|
|
97
|
-
|
|
98
|
-
Components that register resources must release them:
|
|
99
|
-
|
|
100
|
-
- `window` and `document` listeners.
|
|
101
|
-
- Timers and animation frames.
|
|
102
|
-
- `ResizeObserver` and `MutationObserver`.
|
|
103
|
-
- Router subscriptions.
|
|
104
|
-
- Storage subscriptions.
|
|
105
|
-
- WebSocket connections.
|
|
106
|
-
|
|
107
|
-
Use the widget cleanup lifecycle when available.
|
|
108
|
-
|
|
109
|
-
## Validation checklist
|
|
110
|
-
|
|
111
|
-
Before opening a pull request:
|
|
112
|
-
|
|
113
|
-
```bash
|
|
114
|
-
npm test
|
|
115
|
-
node --check path/to/changed-file.js
|
|
116
|
-
python3 -m py_compile scripts/generate_widget_docs.py
|
|
117
|
-
python3 scripts/generate_widget_docs.py
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
`npm test` runs the two static checkers described above — the widget-structure contract (`scripts/check-widgets.mjs`) and the API surface coupling (`scripts/check-api-surface.mjs`) — and must pass before a pull request.
|
|
121
|
-
|
|
122
|
-
The behavioral tests are dependency-free browser harnesses in `tests/`. Serve the repo with the dev server (`npm run dev` or `node bin/cli.js run-spa`), then open:
|
|
123
|
-
|
|
124
|
-
- `/tests/index.html` — widget-factory regression contract
|
|
125
|
-
- `/tests/smoke.html` — instantiates every widget
|
|
126
|
-
- `/tests/doccheck.html` — chapters 1-2 documented behavior
|
|
127
|
-
- `/tests/doccheck-all.html` — chapters 3-7 documented behavior
|
|
128
|
-
- `/tests/propscan.html` — custom-prop scanner
|
|
129
|
-
|
|
130
|
-
Each harness writes a global (`window.__TESTS`, `window.__SMOKE`, `window.__DOCCHECK`) and must pass with 0 failures.
|
|
131
|
-
|
|
132
|
-
Also verify the feature in a real browser when it changes DOM behavior. For build changes, run:
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
npm run build
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
For CLI changes, test:
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
node bin/cli.js --help
|
|
142
|
-
node bin/cli.js --version
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
## Pull request guidance
|
|
146
|
-
|
|
147
|
-
A good pull request explains:
|
|
148
|
-
|
|
149
|
-
- What user problem it solves.
|
|
150
|
-
- Which files changed.
|
|
151
|
-
- Which public API changed.
|
|
152
|
-
- Whether types and docs were updated.
|
|
153
|
-
- Which commands or tests were run.
|
|
154
|
-
- Any remaining compatibility or migration concern.
|
|
155
|
-
|
|
156
|
-
Avoid committing generated output, secrets, local databases, APKs, logs, or credentials.
|