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.
Files changed (172) hide show
  1. package/README.md +10 -0
  2. package/bin/cli.js +15 -0
  3. package/bin/commands/checkApi.js +761 -0
  4. package/bin/commands/create.js +77 -5
  5. package/bin/utils/dtsProps.js +191 -0
  6. package/bin/utils/moduleExports.js +165 -0
  7. package/bin/utils/neutralise.js +64 -0
  8. package/bin/utils/templates.js +366 -27
  9. package/docs/CONTRIBUTING.md +156 -0
  10. package/docs/README.md +68 -0
  11. package/docs/arquitectura.md +164 -0
  12. package/docs/cli/README.md +542 -0
  13. package/docs/guides/README.md +33 -0
  14. package/docs/guides/app-templates.md +197 -0
  15. package/docs/guides/compatibility.md +95 -0
  16. package/docs/guides/embedding.md +284 -0
  17. package/docs/guides/first-app.md +208 -0
  18. package/docs/guides/frontend-services.md +171 -0
  19. package/docs/guides/minification-and-protection.md +260 -0
  20. package/docs/guides/mobile-and-platforms.md +439 -0
  21. package/docs/guides/online-updates.md +196 -0
  22. package/docs/guides/router.md +282 -0
  23. package/docs/guides/state.md +283 -0
  24. package/docs/guides/syntax-philosophy.md +150 -0
  25. package/docs/guides/utilities.md +241 -0
  26. package/docs/guides/widget-structure.md +163 -0
  27. package/docs/server/README.md +68 -0
  28. package/docs/server/START_HERE.md +121 -0
  29. package/docs/server/api.md +225 -0
  30. package/docs/server/data-services.md +300 -0
  31. package/docs/server/deployment.md +113 -0
  32. package/docs/server/security.md +196 -0
  33. package/docs/services/README.md +79 -0
  34. package/docs/services/RamStore.md +84 -0
  35. package/docs/services/Session.md +84 -0
  36. package/docs/services/Storage.md +88 -0
  37. package/docs/services/http.md +86 -0
  38. package/docs/tools/README.md +138 -0
  39. package/docs/tools/animation.md +87 -0
  40. package/docs/tools/async-ids-log.md +80 -0
  41. package/docs/tools/device.md +132 -0
  42. package/docs/tools/introspection.md +67 -0
  43. package/docs/tools/lists.md +96 -0
  44. package/docs/tools/markdown.md +79 -0
  45. package/docs/tools/reactivity.md +288 -0
  46. package/docs/tools/state.md +103 -0
  47. package/docs/tools/styling.md +92 -0
  48. package/docs/tools/text-and-time.md +89 -0
  49. package/docs/tools/theme.md +102 -0
  50. package/docs/tools/validation.md +75 -0
  51. package/docs/tools/visual-effects.md +84 -0
  52. package/docs/widget/Accordion.md +144 -0
  53. package/docs/widget/AdMob.md +144 -0
  54. package/docs/widget/AdSense.md +133 -0
  55. package/docs/widget/AdaptiveScaffold.md +105 -0
  56. package/docs/widget/AlertDialog.md +103 -0
  57. package/docs/widget/AnimatedBox.md +130 -0
  58. package/docs/widget/AnimatedText.md +123 -0
  59. package/docs/widget/AppBar.md +118 -0
  60. package/docs/widget/Audio.md +135 -0
  61. package/docs/widget/Avatar.md +119 -0
  62. package/docs/widget/Badge.md +128 -0
  63. package/docs/widget/BottomNavigation.md +116 -0
  64. package/docs/widget/BottomSheet.md +125 -0
  65. package/docs/widget/Button.md +137 -0
  66. package/docs/widget/COMMON_PROPS.md +150 -0
  67. package/docs/widget/Card.md +125 -0
  68. package/docs/widget/Carousel.md +151 -0
  69. package/docs/widget/Chart.md +151 -0
  70. package/docs/widget/Checkbox.md +118 -0
  71. package/docs/widget/Chip.md +126 -0
  72. package/docs/widget/CircularBar.md +168 -0
  73. package/docs/widget/CircularChart.md +164 -0
  74. package/docs/widget/CodeViewer.md +130 -0
  75. package/docs/widget/CollapsibleSideBar.md +112 -0
  76. package/docs/widget/Column.md +129 -0
  77. package/docs/widget/Container.md +136 -0
  78. package/docs/widget/DataTable.md +150 -0
  79. package/docs/widget/Divider.md +109 -0
  80. package/docs/widget/DraggBox.md +129 -0
  81. package/docs/widget/Drawer.md +135 -0
  82. package/docs/widget/DrawerItem.md +98 -0
  83. package/docs/widget/Dropdown.md +139 -0
  84. package/docs/widget/DroppBox.md +152 -0
  85. package/docs/widget/FloatingActionButton.md +120 -0
  86. package/docs/widget/GridView.md +140 -0
  87. package/docs/widget/Icon.md +117 -0
  88. package/docs/widget/Image.md +125 -0
  89. package/docs/widget/Input.md +151 -0
  90. package/docs/widget/Inspector.md +92 -0
  91. package/docs/widget/InstallButton.md +128 -0
  92. package/docs/widget/ListTile.md +132 -0
  93. package/docs/widget/ListView.md +138 -0
  94. package/docs/widget/Markdown.md +175 -0
  95. package/docs/widget/MatrixRain.md +122 -0
  96. package/docs/widget/Modal.md +129 -0
  97. package/docs/widget/Pagination.md +135 -0
  98. package/docs/widget/ParallaxBox.md +128 -0
  99. package/docs/widget/ProgressBar.md +130 -0
  100. package/docs/widget/QRCode.md +117 -0
  101. package/docs/widget/README.md +188 -0
  102. package/docs/widget/Radio.md +123 -0
  103. package/docs/widget/Rating.md +131 -0
  104. package/docs/widget/Row.md +131 -0
  105. package/docs/widget/START_HERE.md +146 -0
  106. package/docs/widget/Scaffold.md +152 -0
  107. package/docs/widget/Skeleton.md +130 -0
  108. package/docs/widget/Slider.md +146 -0
  109. package/docs/widget/SnackBar.md +102 -0
  110. package/docs/widget/Stack.md +128 -0
  111. package/docs/widget/Stepper.md +145 -0
  112. package/docs/widget/Switch.md +118 -0
  113. package/docs/widget/Tabs.md +124 -0
  114. package/docs/widget/Text.md +133 -0
  115. package/docs/widget/Tooltip.md +145 -0
  116. package/docs/widget/TreeView.md +176 -0
  117. package/docs/widget/Video.md +127 -0
  118. package/docs/widget/_template.md +61 -0
  119. package/package.json +4 -2
  120. package/src/animations/AnimatedBox.js +32 -1
  121. package/src/animations/AnimatedText.js +7 -3
  122. package/src/animations/MatrixRain.js +37 -12
  123. package/src/animations/ParallaxBox.js +16 -3
  124. package/src/animations/animate.js +187 -58
  125. package/src/animations/index.js +1 -0
  126. package/src/core/runApp.js +13 -1
  127. package/src/create-index.sh +1 -1
  128. package/src/index.d.ts +227 -91
  129. package/src/index.js +1 -0
  130. package/src/navigations/AdaptiveScaffold.js +11 -2
  131. package/src/navigations/AppBar.js +8 -2
  132. package/src/navigations/BottomNavigation.js +24 -5
  133. package/src/navigations/DrawerItem.js +16 -3
  134. package/src/navigations/Scaffold.js +8 -1
  135. package/src/navigations/Tabs.js +40 -8
  136. package/src/tools/dimensions.js +11 -3
  137. package/src/tools/useState.js +139 -101
  138. package/src/utils/getWidgetProps.js +5 -3
  139. package/src/widget-factory/assignProps.js +30 -11
  140. package/src/widget-factory/processProps.js +17 -5
  141. package/src/widget-factory/reactiveValue.js +172 -0
  142. package/src/widget-factory/translateProps.js +777 -901
  143. package/src/widget-factory/warnings.js +15 -0
  144. package/src/widget-factory/widgetFactory.js +27 -0
  145. package/src/widgets/Accordion.js +6 -5
  146. package/src/widgets/AlertDialog.js +7 -5
  147. package/src/widgets/Audio.js +30 -8
  148. package/src/widgets/BottomSheet.js +24 -1
  149. package/src/widgets/Chart.js +4 -1
  150. package/src/widgets/Checkbox.js +16 -8
  151. package/src/widgets/CircularBar.js +12 -1
  152. package/src/widgets/CircularChart.js +4 -1
  153. package/src/widgets/CodeViewer.js +26 -8
  154. package/src/widgets/DataTable.js +11 -2
  155. package/src/widgets/Dropdown.js +5 -1
  156. package/src/widgets/FloatingActionButton.js +4 -7
  157. package/src/widgets/Image.js +7 -2
  158. package/src/widgets/Input.js +119 -6
  159. package/src/widgets/Inspector.js +16 -10
  160. package/src/widgets/ListView.js +80 -30
  161. package/src/widgets/Markdown.js +53 -8
  162. package/src/widgets/Modal.js +24 -1
  163. package/src/widgets/Pagination.js +6 -3
  164. package/src/widgets/ProgressBar.js +16 -13
  165. package/src/widgets/QRCode.js +9 -2
  166. package/src/widgets/Radio.js +11 -8
  167. package/src/widgets/Slider.js +26 -4
  168. package/src/widgets/SnackBar.js +20 -2
  169. package/src/widgets/Stack.js +36 -3
  170. package/src/widgets/Stepper.js +4 -3
  171. package/src/widgets/Switch.js +8 -5
  172. package/src/widgets/TreeView.js +3 -2
@@ -0,0 +1,145 @@
1
+ # Tooltip
2
+
3
+ Wraps a child widget and shows a labeled overlay bubble on hover or focus. The bubble is rendered as a portal on document.body so it is never clipped.
4
+
5
+ ## When to use it
6
+
7
+ Use it to explain icons, buttons, or dense fields that cannot fit a visible label.
8
+
9
+ ## Quick start
10
+
11
+ ```javascript
12
+ import { Button, Tooltip, clipboard } from "flet-box";
13
+
14
+ const tip = Tooltip({
15
+ text: "Copy to clipboard",
16
+ child: Button({ text: "Copy" }),
17
+ });
18
+ ```
19
+
20
+ > The prop table is generated from `src/index.d.ts`; the examples use only documented props.
21
+
22
+ ## Props
23
+
24
+ | Prop | Type | Description |
25
+ | --- | --- | --- |
26
+ | `text` | `string` | Visible text content. |
27
+ | `child` | `Widget` | A single child widget. Alias: `children` for a list. |
28
+ | `position` | `'top' \| 'bottom' \| 'left' \| 'right'` | CSS `position` value, or `stack` for centered stacking. |
29
+ | `delay` | `number` | Delay before the animation starts, in milliseconds. |
30
+ | `bgColor` | `Color` | Background color. Alias: `backgroundColor`. |
31
+ | `textColor` | `Color` | Color of the button label. |
32
+ | `fontSize` | `number` | Size of the font. |
33
+ | `padding` | `Padding` | Space inside the widget, between its content and its border. |
34
+ | `borderRadius` | `number` | Rounds the corners of the widget. |
35
+ | `offset` | `number` | The `offset` value for the widget. |
36
+ | `showArrow` | `boolean` | Controls whether the arrow is shown. |
37
+ | `disabled` | `boolean` | Disables interaction and shows the non-interactive state. |
38
+ | `maxWidth` | `number` | Maximum width the widget may grow to. |
39
+ | `textAlign` | `'left' \| 'center' \| 'right'` | The `textAlign` value for the widget. |
40
+ | `zIndex` | `number` | Index used for the z. |
41
+ | `animationDuration` | `number` | Duration for the animation, in milliseconds. |
42
+ | `arrowSize` | `number` | Size of the arrow. |
43
+ | `borderColor` | `Color` | Color used for the border. |
44
+ | `borderWidth` | `number` | Width of the border. |
45
+ | `shadow` | `string` | Raw CSS `box-shadow` value, for a custom shadow. |
46
+
47
+ ### Common props
48
+
49
+ Every widget also accepts these shared props — see [common props](COMMON_PROPS.md) for the full rules and aliases.
50
+
51
+ | Prop | Type | Description |
52
+ | --- | --- | --- |
53
+ | `width` | `Size` | Width of the widget. A number is pixels; a string uses any CSS unit. |
54
+ | `height` | `Size` | Height of the widget. A number is pixels; a string uses any CSS unit. |
55
+ | `size` | `Size` | Overall size preset or pixel value, depending on the widget. |
56
+ | `padding` | `Padding` | Space inside the widget, between its content and its border. |
57
+ | `margin` | `Margin` | Space outside the widget, between it and its neighbors. |
58
+ | `bgColor` | `Color` | Background color. Alias: `backgroundColor`. |
59
+ | `color` | `Color` | Foreground color, usually the text or icon color. |
60
+ | `borderRadius` | `number \| string` | Rounds the corners of the widget. |
61
+ | `elevation` | `number` | Shadow depth. Higher values lift the widget off the page. |
62
+ | `shadow` | `string` | Raw CSS `box-shadow` value, for a custom shadow. |
63
+ | `opacity` | `number` | Opacity from 0 (invisible) to 1 (fully opaque). |
64
+ | `visible` | `boolean` | Whether the widget is rendered and visible. |
65
+ | `disabled` | `boolean` | Disables interaction and shows the non-interactive state. |
66
+ | `onPress` | `(widget: Widget) => void` | Callback fired when the widget is pressed. Alias: `onClick`. |
67
+ | `onClick` | `(widget: Widget) => void` | Callback fired when the widget is clicked. Alias: `onPress`. |
68
+ | `id` | `string` | DOM `id` for the rendered element. |
69
+ | `className` | `string` | CSS class names applied to the rendered element. |
70
+ | `ref` | `(widget: Widget) => void` | Callback that receives the underlying DOM node. |
71
+ | `disableTransform` | `boolean` | Disables the default press/scale transform animation. |
72
+
73
+ ## Examples
74
+
75
+ ### Everyday
76
+
77
+ ```javascript
78
+ import { Icon, Tooltip } from "flet-box";
79
+
80
+ const tip = Tooltip({
81
+ text: "Adds the item to your cart",
82
+ position: "bottom",
83
+ delay: 150,
84
+ child: Icon({ name: "shopping_cart" }),
85
+ });
86
+ ```
87
+
88
+ ### Full
89
+
90
+ ```javascript
91
+ import { Button, Tooltip, rgba } from "flet-box";
92
+
93
+ const tip = Tooltip({
94
+ text: "Only visible to workspace admins",
95
+ child: Button({ text: "Share", variant: "outlined" }),
96
+ position: "right",
97
+ delay: 300,
98
+ offset: 12,
99
+ maxWidth: 240,
100
+ bgColor: "#111827",
101
+ textColor: "#f9fafb",
102
+ showArrow: true,
103
+ textAlign: "left",
104
+ borderRadius: 8,
105
+ shadow: "0 4px 12px rgba(0,0,0,0.25)",
106
+ animationDuration: 180,
107
+ });
108
+ ```
109
+
110
+ ## Tips
111
+
112
+ - Attach it to any widget via child; the tooltip is a separate portal, so overflow
113
+ - hidden ancestors do not cut it off.
114
+ - delay controls how long the pointer must rest before the tooltip appears; lower it
115
+ - for dense toolbars, raise it for error-prone hints.
116
+ - The bubble is clamped to the viewport edges, so top/left placements can sit slightly
117
+ - inside the window bounds.
118
+
119
+ ## Accessibility
120
+
121
+ - The wrapped child keeps its focus handlers, so keyboard users tabbing onto it get
122
+ - the same tooltip as mouse hover.
123
+
124
+ ## Behavior
125
+
126
+ - The tooltip hides itself on window scroll or resize and removes its body portal plus
127
+ - listeners on unmount.
128
+ - The wrapped child gains showTooltip, hideTooltip, updateContent, and updatePosition
129
+ - helpers for imperative control.
130
+
131
+ ## Related widgets
132
+
133
+ - [Button](Button.md)
134
+ - [Icon](Icon.md)
135
+ - [Text](Text.md)
136
+
137
+ ---
138
+
139
+ ## Continue reading
140
+
141
+ - **Previous:** [SnackBar](SnackBar.md)
142
+ - **Next:** [ProgressBar](ProgressBar.md)
143
+ - **Index:** [Widget index](README.md) · [Start here](START_HERE.md)
144
+
145
+ You are reading **Chapter 4 · Feedback and overlays** (5 of 10).
@@ -0,0 +1,176 @@
1
+ # TreeView
2
+
3
+ TreeView renders a collapsible hierarchy from nodes, each with an id, label, optional icon, badge and children. It supports expand/collapse, selection and nested navigation.
4
+
5
+ ## When to use it
6
+
7
+ Use it for file explorers, category menus or any nested set of items the user drills into.
8
+
9
+ ## Quick start
10
+
11
+ ```javascript
12
+ import { TreeView } from "flet-box";
13
+
14
+ const tree = TreeView({
15
+ nodes: [
16
+ { id: "src", label: "src" },
17
+ ],
18
+ });
19
+ ```
20
+
21
+ > The prop table is generated from `src/index.d.ts`; the examples use only documented props.
22
+
23
+ ## Props
24
+
25
+ | Prop | Type | Description |
26
+ | --- | --- | --- |
27
+ | `nodes` | `Array<{` | Tree nodes, each with `id`, `label`, and optional `children`. |
28
+ | `id` | `string` | DOM `id` for the rendered element. |
29
+ | `label` | `string` | Label or caption shown near the control. |
30
+ | `icon` | `string` | Name of the icon to render. |
31
+ | `iconColor` | `Color` | Color of the field icons. |
32
+ | `children` | `any[]` | An array of child widgets. Alias: `child` for a single child. |
33
+ | `badge` | `string \| number` | The `badge` value for the widget. |
34
+ | `onSelect` | `(node: any) => void` | Fired when the widget or an option is selected. |
35
+ | `onToggle` | `(nodeId: string, expanded: boolean) => void` | Fired with the new toggled state. |
36
+ | `expandedNodes` | `string[]` | Ids of nodes that start expanded. |
37
+ | `indent` | `number` | Indentation depth used when printing nested structures. |
38
+ | `showIcons` | `boolean` | Controls whether the icons is shown. |
39
+ | `folderIcon` | `string` | Icon for the folder. |
40
+ | `folderOpenIcon` | `string` | Icon for the folder open. |
41
+ | `fileIcon` | `string` | Icon for the file. |
42
+ | `expandIcon` | `string` | Icon for the expand. |
43
+ | `collapseIcon` | `string` | Icon for the collapse. |
44
+ | `defaultExpanded` | `boolean` | Expands top-level nodes by default. |
45
+ | `selectable` | `boolean` | Lets the user select a tree node. |
46
+ | `selectedNodeId` | `string \| null` | Id of the currently selected node. |
47
+ | `bgColor` | `Color` | Background color. Alias: `backgroundColor`. |
48
+ | `hoverBgColor` | `Color` | Background color for the hover. |
49
+ | `selectedBgColor` | `Color` | Background color for the selected. |
50
+ | `textColor` | `Color` | Color of the button label. |
51
+ | `selectedTextColor` | `Color` | Color used for the selected text. |
52
+ | `iconColor` | `Color` | Color of the field icons. |
53
+ | `folderIconColor` | `Color` | Color used for the folder icon. |
54
+ | `borderColor` | `Color` | Color used for the border. |
55
+ | `nodePadding` | `Padding` | Padding for the node. |
56
+ | `nodeGap` | `number` | Space for the node. |
57
+ | `childrenGap` | `number` | Space for the children. |
58
+ | `borderRadius` | `number` | Rounds the corners of the widget. |
59
+ | `fontSize` | `number` | Size of the font. |
60
+ | `iconSize` | `number` | Size of the field icons. |
61
+ | `transitionDuration` | `string` | Duration for the transition, in milliseconds. |
62
+
63
+ ### Common props
64
+
65
+ Every widget also accepts these shared props — see [common props](COMMON_PROPS.md) for the full rules and aliases.
66
+
67
+ | Prop | Type | Description |
68
+ | --- | --- | --- |
69
+ | `width` | `Size` | Width of the widget. A number is pixels; a string uses any CSS unit. |
70
+ | `height` | `Size` | Height of the widget. A number is pixels; a string uses any CSS unit. |
71
+ | `size` | `Size` | Overall size preset or pixel value, depending on the widget. |
72
+ | `padding` | `Padding` | Space inside the widget, between its content and its border. |
73
+ | `margin` | `Margin` | Space outside the widget, between it and its neighbors. |
74
+ | `bgColor` | `Color` | Background color. Alias: `backgroundColor`. |
75
+ | `color` | `Color` | Foreground color, usually the text or icon color. |
76
+ | `borderRadius` | `number \| string` | Rounds the corners of the widget. |
77
+ | `elevation` | `number` | Shadow depth. Higher values lift the widget off the page. |
78
+ | `shadow` | `string` | Raw CSS `box-shadow` value, for a custom shadow. |
79
+ | `opacity` | `number` | Opacity from 0 (invisible) to 1 (fully opaque). |
80
+ | `visible` | `boolean` | Whether the widget is rendered and visible. |
81
+ | `disabled` | `boolean` | Disables interaction and shows the non-interactive state. |
82
+ | `onPress` | `(widget: Widget) => void` | Callback fired when the widget is pressed. Alias: `onClick`. |
83
+ | `onClick` | `(widget: Widget) => void` | Callback fired when the widget is clicked. Alias: `onPress`. |
84
+ | `id` | `string` | DOM `id` for the rendered element. |
85
+ | `className` | `string` | CSS class names applied to the rendered element. |
86
+ | `ref` | `(widget: Widget) => void` | Callback that receives the underlying DOM node. |
87
+ | `disableTransform` | `boolean` | Disables the default press/scale transform animation. |
88
+
89
+ ## Examples
90
+
91
+ ### Everyday
92
+
93
+ ```javascript
94
+ import { TreeView } from "flet-box";
95
+
96
+ const tree = TreeView({
97
+ nodes: [
98
+ {
99
+ id: "src",
100
+ label: "src",
101
+ children: [
102
+ { id: "app", label: "app.js" },
103
+ { id: "router", label: "Router.js" },
104
+ ],
105
+ },
106
+ ],
107
+ showIcons: true,
108
+ onSelect: (node) => console.log(node.id),
109
+ });
110
+ ```
111
+
112
+ ### Full
113
+
114
+ ```javascript
115
+ import { TreeView } from "flet-box";
116
+
117
+ const tree = TreeView({
118
+ nodes: [
119
+ {
120
+ id: "src",
121
+ label: "src",
122
+ icon: "folder",
123
+ children: [
124
+ { id: "app", label: "app.js", icon: "description" },
125
+ { id: "router", label: "Router.js", badge: 2 },
126
+ ],
127
+ },
128
+ ],
129
+ expandedNodes: ["src"],
130
+ defaultExpanded: true,
131
+ selectable: true,
132
+ showIcons: true,
133
+ selectedNodeId: "app",
134
+ indent: 20,
135
+ nodeGap: 4,
136
+ childrenGap: 4,
137
+ fontSize: 14,
138
+ transitionDuration: "200ms",
139
+ hoverBgColor: "#f1f5f9",
140
+ selectedBgColor: "#dbeafe",
141
+ onToggle: (nodeId, expanded) => console.log(nodeId, expanded),
142
+ onSelect: (node) => console.log(node.id),
143
+ });
144
+ ```
145
+
146
+ ## Tips
147
+
148
+ - Give every node a stable unique id; expandedNodes and selectedNodeId reference those ids.
149
+ - Set selectable to false for navigation trees where tapping should only toggle groups.
150
+ - Use defaultExpanded to open first-level groups on first render.
151
+
152
+ ## Accessibility
153
+
154
+ - Expose meaningful label text and do not rely only on icons to describe a node.
155
+ - Keep onSelect behavior predictable so keyboard users understand what activation does.
156
+
157
+ ## Behavior
158
+
159
+ - Nodes toggle locally and report expansion through onToggle with the nodeId and expanded flag.
160
+ - Selection is reported through onSelect as the node object and can be controlled with selectedNodeId.
161
+
162
+ ## Related widgets
163
+
164
+ - [ListView](ListView.md)
165
+ - [ListTile](ListTile.md)
166
+ - [Accordion](Accordion.md)
167
+
168
+ ---
169
+
170
+ ## Continue reading
171
+
172
+ - **Previous:** [Accordion](Accordion.md)
173
+ - **Next:** [Carousel](Carousel.md)
174
+ - **Index:** [Widget index](README.md) · [Start here](START_HERE.md)
175
+
176
+ You are reading **Chapter 5 · Navigation and flows** (3 of 5).
@@ -0,0 +1,127 @@
1
+ # Video
2
+
3
+ Renders a native <video> element with proper media attributes and a convenient playback API. Helpers for play, pause, stop, restart, and volume.
4
+
5
+ ## When to use it
6
+
7
+ Use it to embed a clip, trailer, ambient background video, or lecture recording.
8
+
9
+ ## Quick start
10
+
11
+ ```javascript
12
+ import { Video } from "flet-box";
13
+
14
+ const clip = Video({
15
+ src: "/media/intro.mp4",
16
+ });
17
+ ```
18
+
19
+ > The prop table is generated from `src/index.d.ts`; the examples use only documented props.
20
+
21
+ ## Props
22
+
23
+ | Prop | Type | Description |
24
+ | --- | --- | --- |
25
+ | `src` | `string` | Resource URL for the image, audio, or video. |
26
+ | `width` | `number \| string` | Width of the widget. A number is pixels; a string uses any CSS unit. |
27
+ | `height` | `number \| string` | Height of the widget. A number is pixels; a string uses any CSS unit. |
28
+ | `autoplay` | `boolean` | Starts playback automatically. |
29
+ | `controls` | `boolean` | Shows the native media playback controls. |
30
+ | `loop` | `boolean` | Restarts playback when it reaches the end. |
31
+ | `muted` | `boolean` | Starts playback with the audio muted. |
32
+ | `poster` | `string` | Image shown before a video starts playing. |
33
+ | `ref` | `(video: HTMLVideoElement) => void` | Callback that receives the underlying DOM node. |
34
+
35
+ ### Common props
36
+
37
+ Every widget also accepts these shared props — see [common props](COMMON_PROPS.md) for the full rules and aliases.
38
+
39
+ | Prop | Type | Description |
40
+ | --- | --- | --- |
41
+ | `width` | `Size` | Width of the widget. A number is pixels; a string uses any CSS unit. |
42
+ | `height` | `Size` | Height of the widget. A number is pixels; a string uses any CSS unit. |
43
+ | `size` | `Size` | Overall size preset or pixel value, depending on the widget. |
44
+ | `padding` | `Padding` | Space inside the widget, between its content and its border. |
45
+ | `margin` | `Margin` | Space outside the widget, between it and its neighbors. |
46
+ | `bgColor` | `Color` | Background color. Alias: `backgroundColor`. |
47
+ | `color` | `Color` | Foreground color, usually the text or icon color. |
48
+ | `borderRadius` | `number \| string` | Rounds the corners of the widget. |
49
+ | `elevation` | `number` | Shadow depth. Higher values lift the widget off the page. |
50
+ | `shadow` | `string` | Raw CSS `box-shadow` value, for a custom shadow. |
51
+ | `opacity` | `number` | Opacity from 0 (invisible) to 1 (fully opaque). |
52
+ | `visible` | `boolean` | Whether the widget is rendered and visible. |
53
+ | `disabled` | `boolean` | Disables interaction and shows the non-interactive state. |
54
+ | `onPress` | `(widget: Widget) => void` | Callback fired when the widget is pressed. Alias: `onClick`. |
55
+ | `onClick` | `(widget: Widget) => void` | Callback fired when the widget is clicked. Alias: `onPress`. |
56
+ | `id` | `string` | DOM `id` for the rendered element. |
57
+ | `className` | `string` | CSS class names applied to the rendered element. |
58
+ | `ref` | `(widget: Widget) => void` | Callback that receives the underlying DOM node. |
59
+ | `disableTransform` | `boolean` | Disables the default press/scale transform animation. |
60
+
61
+ ## Examples
62
+
63
+ ### Everyday
64
+
65
+ ```javascript
66
+ import { Video } from "flet-box";
67
+
68
+ const clip = Video({
69
+ src: "/media/intro.mp4",
70
+ controls: true,
71
+ width: "100%",
72
+ height: 360,
73
+ poster: "/media/intro-poster.jpg",
74
+ });
75
+ ```
76
+
77
+ ### Full
78
+
79
+ ```javascript
80
+ import { Video } from "flet-box";
81
+
82
+ const clip = Video({
83
+ src: "/media/demo.mp4",
84
+ width: "100%",
85
+ height: 480,
86
+ controls: true,
87
+ autoplay: false,
88
+ loop: true,
89
+ muted: true,
90
+ poster: "/media/demo-poster.jpg",
91
+ ref: (video) => video.playVideo(),
92
+ });
93
+ ```
94
+
95
+ ## Tips
96
+
97
+ - controls, loop, muted, and autoplay are set as element properties, not CSS, so they
98
+ - work even though they look like styling props.
99
+ - Browsers block autoplay with sound; pair autoplay: true with muted: true for a
100
+ - reliable ambient loop.
101
+ - The ref callback receives the element extended with playVideo, pauseVideo, stop,
102
+ - restart, volumeUp, and muteVideo helpers.
103
+
104
+ ## Accessibility
105
+
106
+ - Keep controls: true so keyboard and screen-reader users can operate playback.
107
+
108
+ ## Behavior
109
+
110
+ - playVideo swallows the browser's autoplay rejection instead of throwing, so it is
111
+ - safe to call from a click handler.
112
+
113
+ ## Related widgets
114
+
115
+ - [Audio](Audio.md)
116
+ - [Image](Image.md)
117
+ - [Card](Card.md)
118
+
119
+ ---
120
+
121
+ ## Continue reading
122
+
123
+ - **Previous:** [Audio](Audio.md)
124
+ - **Next:** [DraggBox](DraggBox.md)
125
+ - **Index:** [Widget index](README.md) · [Start here](START_HERE.md)
126
+
127
+ You are reading **Chapter 7 · Media and drag & drop** (2 of 4).
@@ -0,0 +1,61 @@
1
+ # WidgetName
2
+
3
+ ## Overview
4
+
5
+ Explain what the widget is for in one or two sentences.
6
+
7
+ ## When to use it
8
+
9
+ Describe the situation in which you reach for this widget.
10
+
11
+ ## Quick start
12
+
13
+ ```javascript
14
+ import { WidgetName } from "flet-box";
15
+
16
+ const example = WidgetName({
17
+ // the smallest useful call
18
+ });
19
+ ```
20
+
21
+ ## Props
22
+
23
+ | Prop | Type | Description |
24
+ | --- | --- | --- |
25
+ | `propName` | `Type` | What the prop does. |
26
+
27
+ ## Examples
28
+
29
+ ### Everyday
30
+
31
+ Show the most common combination of props.
32
+
33
+ ### Full
34
+
35
+ Show advanced layout, styling and events.
36
+
37
+ ## Tips
38
+
39
+ - Practical, widget-specific advice.
40
+
41
+ ## Accessibility
42
+
43
+ - Keyboard and screen-reader considerations.
44
+
45
+ ## Behavior
46
+
47
+ - Layout, interaction and re-render notes.
48
+
49
+ ## Related widgets
50
+
51
+ - [Container](Container.md)
52
+
53
+ ---
54
+
55
+ ## Continue reading
56
+
57
+ - **Previous:** ...
58
+ - **Next:** ...
59
+ - **Index:** [Widget index](README.md) · [Start here](START_HERE.md)
60
+
61
+ You are reading **Chapter N · Title** (i of m).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flet-box",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Declarative, zero-dependency UI framework in vanilla JS — ship web, PWA, Android and iOS from one codebase",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -19,7 +19,8 @@
19
19
  "dev": "node bin/cli.js run --hot",
20
20
  "build": "node bin/cli.js createBundle",
21
21
  "release": "node bin/cli.js release",
22
- "test": "node scripts/check-widgets.mjs"
22
+ "test": "node scripts/check-widgets.mjs && node scripts/check-prop-registry.mjs && node scripts/check-docs-props.mjs && node scripts/check-api-surface.mjs && node scripts/check-router-base.mjs && python3 tools/snippets-creator-flet-box/build_snippets.py --check && node scripts/check-snippets.mjs",
23
+ "test:browser": "node scripts/run-browser-tests.mjs"
23
24
  },
24
25
  "keywords": [
25
26
  "fletbox",
@@ -51,6 +52,7 @@
51
52
  "files": [
52
53
  "bin/",
53
54
  "src/",
55
+ "docs/",
54
56
  "README.md",
55
57
  "LICENSE"
56
58
  ],
@@ -334,6 +334,24 @@ const interpolateGradient = (gradient1, gradient2, t) => {
334
334
  * @param {string} keyframes - The keyframe body (everything inside `@keyframes name { … }`).
335
335
  * @returns {void}
336
336
  */
337
+ /**
338
+ * Stable hash of a keyframe body, used as the generated animation name so that
339
+ * identical keyframes are injected only once.
340
+ *
341
+ * @param {string} keyframes
342
+ * @returns {string}
343
+ */
344
+ const hashKeyframes = (keyframes) => {
345
+ let h1 = 0x811c9dc5;
346
+ let h2 = 0x01000193;
347
+ for (let i = 0; i < keyframes.length; i++) {
348
+ const c = keyframes.charCodeAt(i);
349
+ h1 = Math.imul(h1 ^ c, 0x01000193);
350
+ h2 = Math.imul(h2 + c, 0x85ebca6b) ^ (h2 >>> 13);
351
+ }
352
+ return (h1 >>> 0).toString(36) + (h2 >>> 0).toString(36);
353
+ };
354
+
337
355
  const injectKeyframes = (name, keyframes) => {
338
356
  if (injectedKeyframes.has(name)) return;
339
357
  const style = document.createElement("style");
@@ -365,6 +383,15 @@ const generateKeyframes = (animations) => {
365
383
  const effect = effectMap[anim.effect] || anim.effect;
366
384
  const { from, to, reverse = true } = anim;
367
385
 
386
+ // A descriptor without `from`/`to` is malformed. Skip it with a warning
387
+ // rather than throwing a TypeError out of the caller's entire render.
388
+ if (from === undefined || to === undefined) {
389
+ console.warn(
390
+ `⚠️ [AnimatedBox] Skipping animation "${anim.effect}": "from" and "to" are required.`,
391
+ );
392
+ return;
393
+ }
394
+
368
395
  let value;
369
396
  let color;
370
397
 
@@ -589,8 +616,12 @@ export const AnimatedBox = ({
589
616
  child.style.background = firstAnim.from;
590
617
  }
591
618
 
592
- const name = `anim-${Date.now()}-${animationCounter++}`;
593
619
  const keyframes = generateKeyframes(animations);
620
+ // The name is derived from the keyframe body so identical animations reuse a
621
+ // single <style>. A per-call unique name (`Date.now()`) made the dedupe check
622
+ // in `injectKeyframes` dead code: every call appended a permanent <style> to
623
+ // document.head, and AnimatedText calls this once per letter.
624
+ const name = `anim-${hashKeyframes(keyframes)}`;
594
625
  injectKeyframes(name, keyframes);
595
626
 
596
627
  const duration = animations[0]?.duration || 500;
@@ -39,9 +39,13 @@ export const AnimatedText = ({
39
39
  if (!child) return null;
40
40
  if (!animations || animations.length === 0) return child;
41
41
 
42
- // Extract text content and props from the original Text widget
43
- const originalText = child._props?.text || child.textContent || "";
44
- const originalProps = { ...(child._props || {}) };
42
+ // Extract text content and props from the original Text widget. The factory
43
+ // stores props in `_originalProps`; `_props` is never assigned by anything, so
44
+ // reading it here silently dropped every style (font, size, colour) and the
45
+ // documented per-letter inheritance never happened.
46
+ const sourceProps = child._originalProps || child._props || {};
47
+ const originalText = sourceProps.text || child.textContent || "";
48
+ const originalProps = { ...sourceProps };
45
49
  // Ensure no background animation styles bleed into individual letter widgets
46
50
  delete originalProps.animations;
47
51
 
@@ -52,6 +52,11 @@ export const MatrixRain = (props = {}) => {
52
52
  let drops = [];
53
53
  let columns = 0;
54
54
  let resizeObserver = null;
55
+ let observedParent = null;
56
+ // The canvas is returned to the caller and appended afterwards, so it is NOT
57
+ // connected when the animation starts. `wasConnected` tracks that first mount
58
+ // so the loop can stop when the canvas is later removed from the document.
59
+ let wasConnected = false;
55
60
 
56
61
  /**
57
62
  * Determines the canvas dimensions based on positioning mode.
@@ -84,14 +89,17 @@ export const MatrixRain = (props = {}) => {
84
89
  */
85
90
  const resizeCanvas = () => {
86
91
  if (!canvas) return;
87
- if (
88
- position === "absolute" &&
89
- !resizeObserver &&
90
- typeof ResizeObserver !== "undefined" &&
91
- canvas.parentElement
92
- ) {
93
- resizeObserver = new ResizeObserver(resizeCanvas);
94
- resizeObserver.observe(canvas.parentElement);
92
+ // The canvas is returned to the caller and appended afterwards, so on the
93
+ // first call there is no parent yet. Create the observer once and (re)attach
94
+ // it whenever the canvas ends up living in a different parent.
95
+ if (position === "absolute" && typeof ResizeObserver !== "undefined") {
96
+ if (!resizeObserver) resizeObserver = new ResizeObserver(resizeCanvas);
97
+ const parent = canvas.parentElement;
98
+ if (parent && observedParent !== parent) {
99
+ if (observedParent) resizeObserver.unobserve(observedParent);
100
+ resizeObserver.observe(parent);
101
+ observedParent = parent;
102
+ }
95
103
  }
96
104
  const { width, height } = getCanvasSize();
97
105
  canvas.width = width;
@@ -154,6 +162,25 @@ export const MatrixRain = (props = {}) => {
154
162
  const draw = () => {
155
163
  if (!ctx || !canvas) return;
156
164
 
165
+ // The canvas is handed back to the caller and appended afterwards, so it is
166
+ // disconnected while the animation starts. Once it HAS been mounted, a
167
+ // disconnect means it was removed from the document — stop there.
168
+ //
169
+ // Nothing else covers that path: `runApp`'s teardownTree and Scaffold's
170
+ // observer call `_cleanup()`, but the lifecycle observer in
171
+ // `src/widget-factory/lifecycle.js` only fires `onUnmount`, and a raw canvas
172
+ // has no such hook. Without this guard the loop would keep re-arming at
173
+ // 60fps painting into a detached canvas, forever.
174
+ if (canvas.isConnected) {
175
+ if (!wasConnected) {
176
+ wasConnected = true;
177
+ resizeCanvas(); // size to the real parent and attach the observer
178
+ }
179
+ } else if (wasConnected) {
180
+ cleanup();
181
+ return;
182
+ }
183
+
157
184
  // Semi-transparent black overlay produces the trailing-fade effect
158
185
  ctx.fillStyle = `rgba(0, 0, 0, ${fadeAmount})`;
159
186
  ctx.fillRect(0, 0, canvas.width, canvas.height);
@@ -188,10 +215,8 @@ export const MatrixRain = (props = {}) => {
188
215
  };
189
216
 
190
217
  window.addEventListener("resize", handleResize);
191
- if (position === "absolute" && typeof ResizeObserver !== "undefined") {
192
- resizeObserver = new ResizeObserver(resizeCanvas);
193
- if (canvas.parentElement) resizeObserver.observe(canvas.parentElement);
194
- }
218
+ // NOTE: the parent ResizeObserver is attached from `resizeCanvas()` (see
219
+ // above), because the canvas has no parent until the caller appends it.
195
220
 
196
221
  /**
197
222
  * Stops the animation loop, removes event listeners, disconnects the