flet-box 1.1.0 → 1.1.2

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 (135) hide show
  1. package/README.md +16 -12
  2. package/bin/commands/checkUi.js +276 -45
  3. package/bin/commands/create.js +8 -1
  4. package/bin/commands/createBundle.js +18 -6
  5. package/bin/commands/createScreen.js +1 -1
  6. package/bin/commands/runBundle.js +1 -1
  7. package/bin/utils/bundleConfig.js +47 -3
  8. package/bin/utils/colors.js +0 -8
  9. package/bin/utils/helpers.js +0 -5
  10. package/bin/utils/images/jpeg.js +2 -3
  11. package/bin/utils/templates.js +101 -85
  12. package/docs/README.md +24 -1
  13. package/docs/cli/README.md +51 -10
  14. package/docs/guides/README.md +5 -2
  15. package/docs/guides/accessibility.md +282 -0
  16. package/docs/guides/clean-ui.md +53 -8
  17. package/docs/guides/first-app.md +6 -7
  18. package/docs/guides/minification-and-protection.md +1 -1
  19. package/docs/guides/mobile-and-platforms.md +51 -0
  20. package/docs/guides/online-updates.md +18 -6
  21. package/docs/guides/utilities.md +116 -4
  22. package/docs/guides/widget-structure.md +30 -0
  23. package/docs/tools/README.md +1 -1
  24. package/docs/tools/animation.md +134 -11
  25. package/docs/tools/device.md +75 -3
  26. package/docs/tools/text-and-time.md +116 -6
  27. package/docs/widget/Accordion.md +2 -0
  28. package/docs/widget/AnimatedBox.md +46 -20
  29. package/docs/widget/AnimatedText.md +13 -7
  30. package/docs/widget/Badge.md +1 -0
  31. package/docs/widget/COMMON_PROPS.md +26 -3
  32. package/docs/widget/Chart.md +1 -0
  33. package/docs/widget/Checkbox.md +7 -1
  34. package/docs/widget/CircularChart.md +2 -0
  35. package/docs/widget/Drawer.md +1 -0
  36. package/docs/widget/GridView.md +4 -1
  37. package/docs/widget/Input.md +2 -0
  38. package/docs/widget/InstallButton.md +1 -0
  39. package/docs/widget/Markdown.md +4 -0
  40. package/docs/widget/Rating.md +2 -0
  41. package/docs/widget/Slider.md +2 -0
  42. package/docs/widget/Stack.md +6 -5
  43. package/docs/widget/Stepper.md +2 -0
  44. package/docs/widget/Tooltip.md +3 -1
  45. package/package.json +3 -2
  46. package/skills/fletbox-design/SKILL.md +390 -14
  47. package/skills/fletbox-motion/SKILL.md +145 -47
  48. package/skills/fletbox-rules/SKILL.md +140 -1
  49. package/src/animations/AnimatedBox.js +321 -226
  50. package/src/animations/MatrixRain.js +7 -1
  51. package/src/animations/ParallaxBox.js +13 -10
  52. package/src/animations/animate.js +269 -106
  53. package/src/animations/color.js +91 -0
  54. package/src/animations/easings.js +275 -0
  55. package/src/animations/index.js +21 -0
  56. package/src/animations/motion.js +204 -0
  57. package/src/animations/transitions.js +327 -0
  58. package/src/core/runApp.js +0 -6
  59. package/src/helpers/boxEdges.js +69 -0
  60. package/src/helpers/html.js +39 -0
  61. package/src/index.d.ts +377 -25
  62. package/src/index.js +77 -0
  63. package/src/navigations/AdaptiveScaffold.js +6 -2
  64. package/src/navigations/AppBar.js +16 -7
  65. package/src/navigations/BottomNavigation.js +24 -1
  66. package/src/navigations/CollapsibleSideBar.js +0 -1
  67. package/src/navigations/Drawer.js +32 -1
  68. package/src/navigations/DrawerItem.js +1 -1
  69. package/src/navigations/Router.js +0 -1
  70. package/src/navigations/Scaffold.js +9 -3
  71. package/src/navigations/Tabs.js +104 -31
  72. package/src/services/RamStore.js +1 -2
  73. package/src/tools/breakpoints.js +113 -0
  74. package/src/tools/createList.js +0 -2
  75. package/src/tools/fonts.js +99 -0
  76. package/src/tools/index.js +74 -1
  77. package/src/tools/margin.js +5 -63
  78. package/src/tools/padding.js +5 -63
  79. package/src/tools/print.js +0 -8
  80. package/src/tools/scale.js +9 -2
  81. package/src/tools/time.js +56 -15
  82. package/src/tools/useState.js +1 -54
  83. package/src/utils/fieldFont.js +77 -0
  84. package/src/utils/focusRing.js +73 -0
  85. package/src/utils/focusScope.js +113 -0
  86. package/src/utils/markdownParser.js +7 -9
  87. package/src/utils/readableInk.js +7 -1
  88. package/src/utils/stackLayout.js +55 -0
  89. package/src/utils/styleInjector.js +25 -0
  90. package/src/utils/syntaxHighlight.js +1 -17
  91. package/src/utils/themes.js +15 -0
  92. package/src/utils/visualEffects.js +49 -47
  93. package/src/widget-factory/createWidget.js +16 -3
  94. package/src/widget-factory/effects.js +76 -3
  95. package/src/widget-factory/reactiveValue.js +0 -7
  96. package/src/widget-factory/tools.js +26 -143
  97. package/src/widget-factory/translateProps.js +5 -0
  98. package/src/widget-factory/widgetFactory.js +1 -1
  99. package/src/widgets/Accordion.js +70 -12
  100. package/src/widgets/AdMob.js +1 -1
  101. package/src/widgets/AdSense.js +1 -1
  102. package/src/widgets/AlertDialog.js +11 -9
  103. package/src/widgets/Avatar.js +10 -1
  104. package/src/widgets/Badge.js +7 -1
  105. package/src/widgets/BottomSheet.js +39 -5
  106. package/src/widgets/Chart.js +41 -9
  107. package/src/widgets/Checkbox.js +59 -9
  108. package/src/widgets/Chip.js +13 -1
  109. package/src/widgets/CircularBar.js +17 -5
  110. package/src/widgets/CircularChart.js +24 -2
  111. package/src/widgets/CodeViewer.js +5 -3
  112. package/src/widgets/Dropdown.js +223 -20
  113. package/src/widgets/GridView.js +17 -1
  114. package/src/widgets/Icon.js +21 -0
  115. package/src/widgets/Input.js +75 -44
  116. package/src/widgets/InstallButton.js +8 -2
  117. package/src/widgets/ListTile.js +6 -5
  118. package/src/widgets/ListView.js +21 -7
  119. package/src/widgets/Markdown.js +25 -44
  120. package/src/widgets/Modal.js +43 -6
  121. package/src/widgets/Pagination.js +35 -3
  122. package/src/widgets/ProgressBar.js +18 -0
  123. package/src/widgets/Radio.js +7 -0
  124. package/src/widgets/Rating.js +104 -16
  125. package/src/widgets/Skeleton.js +9 -5
  126. package/src/widgets/Slider.js +87 -7
  127. package/src/widgets/SnackBar.js +30 -1
  128. package/src/widgets/Stack.js +25 -13
  129. package/src/widgets/Stepper.js +112 -64
  130. package/src/widgets/Switch.js +10 -1
  131. package/src/widgets/Tooltip.js +93 -10
  132. package/src/widgets/TreeView.js +5 -5
  133. package/src/create-index.sh +0 -237
  134. package/src/widget-factory/pixTool.js +0 -40
  135. package/src/widget-factory/stringTool.js +0 -26
package/src/index.d.ts CHANGED
@@ -6,10 +6,15 @@ declare module 'flet-box' {
6
6
  // Tipos base
7
7
  // =========================================================================
8
8
  export type Widget = HTMLElement;
9
+ // A Scaffold slot: an element, a factory it re-runs on each route change (so a
10
+ // nav can relight the active item), a boolean show/hide, or null for absent.
11
+ export type Slot = Widget | (() => Widget) | boolean | null;
9
12
  export type Color = string;
10
13
  export type Size = number | string;
11
14
  export type Padding = number | string;
12
15
  export type Margin = number | string;
16
+ /** How a single flex child answers its parent's cross-axis `alignItems`. */
17
+ export type AlignSelf = 'auto' | 'flex-start' | 'center' | 'flex-end' | 'baseline' | 'stretch';
13
18
 
14
19
  /**
15
20
  * The element returned by `WidgetFactory()`. It is a real `HTMLElement` with
@@ -77,8 +82,35 @@ declare module 'flet-box' {
77
82
  // `Size`, not `number`: for a Text this is a font size and for a Button it is
78
83
  // the `small | medium | large` preset — the widget decides which it reads.
79
84
  size?: Size;
85
+ // The engine routes these as plain style on every widget, so declaring them
86
+ // only on `Container` made `Column({ maxWidth: 720, flex: 1 })` — the layout
87
+ // recipe the design skill teaches — a false error in a JS editor.
88
+ flex?: number;
89
+ minWidth?: Size;
90
+ maxWidth?: Size;
91
+ minHeight?: Size;
92
+ maxHeight?: Size;
93
+ border?: string;
94
+ borderTop?: string;
95
+ borderBottom?: string;
96
+ borderLeft?: string;
97
+ borderRight?: string;
98
+ cursor?: string;
99
+ // The engine routes `display` untouched; `Stack` sets it internally and an
100
+ // app that overrides it is doing something the type must not forbid.
101
+ display?: string;
80
102
  padding?: Padding;
81
103
  margin?: Margin;
104
+ // The engine routes each margin side, and the `mt/mr/mb/ml` aliases, as rem
105
+ // lengths on every widget: `Text({ marginTop: 4 })` is legal at runtime.
106
+ marginTop?: Size;
107
+ marginRight?: Size;
108
+ marginBottom?: Size;
109
+ marginLeft?: Size;
110
+ mt?: Size;
111
+ mr?: Size;
112
+ mb?: Size;
113
+ ml?: Size;
82
114
  bgColor?: Color;
83
115
  // Alias kept because the engine routes it and every generated template uses
84
116
  // it: `bgColor` is the Flet-style short name for the same style.
@@ -98,6 +130,15 @@ declare module 'flet-box' {
98
130
  disabled?: boolean;
99
131
  onPress?: (widget: Widget) => void;
100
132
  onClick?: (widget: Widget) => void;
133
+ // The lowercase alias the engine also routes (`{ prop: "onclick" }` in
134
+ // translateProps); the generated AppBar reaches for it.
135
+ onclick?: (widget: Widget) => void;
136
+ // A child's own cross-axis answer to its parent's `alignItems`. It belongs on
137
+ // `CommonProps` rather than on the layouts because the widget that needs it is
138
+ // the *child* — a chip centred inside a row whose other children stretch.
139
+ alignSelf?: AlignSelf;
140
+ // Alias of `alignSelf`, matching the `justify` / `alignItems` shorthand style.
141
+ selfAlign?: AlignSelf;
101
142
  id?: string;
102
143
  className?: string;
103
144
  ref?: (widget: Widget) => void;
@@ -251,7 +292,9 @@ declare module 'flet-box' {
251
292
  direction?: 'row' | 'column';
252
293
  gap?: number | string;
253
294
  justifyContent?: 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly';
254
- alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch';
295
+ // `start`/`end`/`baseline` are CSS Box Alignment values the engine passes
296
+ // through untouched, so the type has to admit them.
297
+ alignItems?: 'flex-start' | 'center' | 'flex-end' | 'start' | 'end' | 'baseline' | 'stretch';
255
298
  wrap?: boolean | 'wrap' | 'nowrap' | 'wrap-reverse';
256
299
  child?: Widget;
257
300
  children?: Widget[];
@@ -284,6 +327,17 @@ declare module 'flet-box' {
284
327
  }
285
328
  export function Column(props: ColumnProps): Widget;
286
329
 
330
+ /**
331
+ * Layers its children in the same box.
332
+ *
333
+ * The first child stays in the flow and gives the stack its size; every child
334
+ * after it is taken out of the flow and painted over that box, so a stack with
335
+ * nothing sized and no sized first child has no room for its layers.
336
+ *
337
+ * `alignment` is the corner a layer with no edges of its own sits on
338
+ * (top-left by default). A layer that names `top`/`right`/`bottom`/`left` keeps
339
+ * those, and a child that declares its own `position` is left in the flow.
340
+ */
287
341
  export interface StackProps extends CommonProps {
288
342
  children?: Widget[];
289
343
  position?: 'relative' | 'absolute' | 'stack';
@@ -323,6 +377,12 @@ declare module 'flet-box' {
323
377
  columns?: number;
324
378
  spacing?: number;
325
379
  itemHeight?: number;
380
+ /**
381
+ * The cells themselves, laid out `columns` per row. This is the same contract
382
+ * as `data` + `renderItem` read the other way round: pass the built widgets
383
+ * here, or the items plus a builder. One of the two — `data` wins.
384
+ */
385
+ children?: Widget[] | Widget;
326
386
  }
327
387
  export function GridView(props: GridViewProps): Widget;
328
388
 
@@ -503,7 +563,9 @@ declare module 'flet-box' {
503
563
  color?: Color;
504
564
  textColor?: Color;
505
565
  borderColor?: Color;
506
- size?: 'small' | 'medium' | 'large';
566
+ // Chip reads `size` as a font size in pixels and scales its icons from it,
567
+ // so the preset union was a type the implementation never accepted.
568
+ size?: number;
507
569
  borderRadius?: number | string;
508
570
  elevation?: number;
509
571
  gap?: number;
@@ -656,7 +718,7 @@ declare module 'flet-box' {
656
718
  margin?: number | { top?: number; right?: number; bottom?: number; left?: number };
657
719
  orientation?: 'horizontal' | 'vertical';
658
720
  }
659
- export function Divider(props: DividerProps): Widget;
721
+ export function Divider(props?: DividerProps): Widget;
660
722
 
661
723
  // =========================================================================
662
724
  // Formularios e interacción
@@ -738,6 +800,12 @@ declare module 'flet-box' {
738
800
  size?: number;
739
801
  activeColor?: Color;
740
802
  label?: string;
803
+ /**
804
+ * How the checked state is painted. `outlined` (default) keeps the box hollow and
805
+ * draws the tick in `activeColor`; `filled` turns the whole box into an
806
+ * `activeColor` plate and takes the tick to whichever ink reads on it.
807
+ */
808
+ variant?: 'outlined' | 'filled';
741
809
  }
742
810
  export function Checkbox(props: CheckboxProps): Widget & {
743
811
  setChecked(checked: boolean, triggerCallback?: boolean): void;
@@ -906,16 +974,17 @@ declare module 'flet-box' {
906
974
  // Navegación
907
975
  // =========================================================================
908
976
  export interface ScaffoldProps extends CommonProps {
909
- appBar?: Widget | boolean;
977
+ appBar?: Slot;
910
978
  body?: Widget | Record<string, any>;
911
- bottomBar?: Widget | boolean;
912
- fab?: Widget | boolean;
913
- drawer?: Widget;
914
- leftNavBar?: Widget;
979
+ bottomBar?: Slot;
980
+ fab?: Slot;
981
+ // `Drawer()` returns an instance, not an element, and Scaffold unwraps it.
982
+ drawer?: Widget | DrawerInstance | null;
983
+ leftNavBar?: Slot;
915
984
  leftNavBarWidth?: number;
916
- rightNavBar?: Widget;
985
+ rightNavBar?: Slot;
917
986
  rightNavBarWidth?: number;
918
- navSideBar?: Widget;
987
+ navSideBar?: Slot;
919
988
  navSideBarWidth?: number;
920
989
  navSideBarPosition?: 'left' | 'right';
921
990
  routes?: Record<string, any>;
@@ -933,14 +1002,14 @@ declare module 'flet-box' {
933
1002
  };
934
1003
 
935
1004
  export interface AdaptiveScaffoldProps extends CommonProps {
936
- appBar?: Widget;
1005
+ appBar?: Slot;
937
1006
  body?: Widget | Record<string, any>;
938
- bottomBar?: Widget;
939
- fab?: Widget;
940
- drawer?: Widget;
941
- leftNavBar?: Widget | (() => Widget);
1007
+ bottomBar?: Slot;
1008
+ fab?: Slot;
1009
+ drawer?: Widget | DrawerInstance | null;
1010
+ leftNavBar?: Slot;
942
1011
  leftNavBarWidth?: number;
943
- rightNavBar?: Widget | (() => Widget);
1012
+ rightNavBar?: Slot;
944
1013
  rightNavBarWidth?: number;
945
1014
  routes?: Record<string, any>;
946
1015
  backgroundColor?: Color;
@@ -1139,16 +1208,20 @@ declare module 'flet-box' {
1139
1208
  effect: string;
1140
1209
  from: any;
1141
1210
  to: any;
1142
- duration?: number;
1211
+ /** Milliseconds as a number (`400`) or a CSS time as a string (`"0.4s"`). Defaults to 500. */
1212
+ duration?: number | string;
1143
1213
  reverse?: boolean;
1214
+ /** Repeats this step forever; a loop and a one-shot generate separate `@keyframes`. */
1144
1215
  loop?: boolean;
1145
- delay?: string;
1216
+ /** Milliseconds as a number (`120`) or a CSS time as a string (`"0.12s"`). Defaults to the widget's `delay`. */
1217
+ delay?: number | string;
1146
1218
  }
1147
1219
  export interface AnimatedBoxProps extends CommonProps {
1148
1220
  animations: AnimationStep[];
1149
1221
  child: Widget;
1150
1222
  timing?: string;
1151
- delay?: string;
1223
+ /** Default delay for the steps that do not name one: milliseconds or a CSS time. */
1224
+ delay?: number | string;
1152
1225
  fillMode?: string;
1153
1226
  top?: number | string;
1154
1227
  right?: number | string;
@@ -1161,6 +1234,7 @@ declare module 'flet-box' {
1161
1234
  child: Widget;
1162
1235
  animations: AnimationStep[];
1163
1236
  sameTime?: boolean;
1237
+ /** Seconds between consecutive characters (`0.05` is a 50 ms stagger). */
1164
1238
  delayBetween?: number;
1165
1239
  orientation?: 'row' | 'column';
1166
1240
  }
@@ -1203,7 +1277,7 @@ declare module 'flet-box' {
1203
1277
  from: any,
1204
1278
  to: any,
1205
1279
  duration?: number,
1206
- easing?: string
1280
+ easing?: Easing
1207
1281
  ): () => boolean;
1208
1282
  export function stopAnimation(widget: Widget, property?: string): boolean;
1209
1283
  export function animateAsync(
@@ -1212,7 +1286,7 @@ declare module 'flet-box' {
1212
1286
  from: any,
1213
1287
  to: any,
1214
1288
  duration?: number,
1215
- easing?: string
1289
+ easing?: Easing
1216
1290
  ): Promise<Widget>;
1217
1291
  export function fadeOut(widget: Widget, duration?: number): () => boolean;
1218
1292
  export function fadeIn(widget: Widget, duration?: number): () => boolean;
@@ -1221,6 +1295,123 @@ declare module 'flet-box' {
1221
1295
  export function fadeInAsync(widget: Widget, duration?: number): Promise<Widget>;
1222
1296
  export function pulseAsync(widget: Widget, duration?: number): Promise<Widget>;
1223
1297
 
1298
+ // ----- Easings, springs and the motion preference -----
1299
+ /** A curve name the imperative engine knows. */
1300
+ export type EasingName =
1301
+ | 'linear'
1302
+ | 'easeIn'
1303
+ | 'easeOut'
1304
+ | 'easeInOut'
1305
+ | 'easeInCubic'
1306
+ | 'easeOutCubic'
1307
+ | 'easeInOutCubic'
1308
+ | 'easeInQuart'
1309
+ | 'easeOutQuart'
1310
+ | 'easeInOutQuart'
1311
+ | 'easeInExpo'
1312
+ | 'easeOutExpo'
1313
+ | 'easeInOutExpo'
1314
+ | 'easeInCirc'
1315
+ | 'easeOutCirc'
1316
+ | 'easeInOutCirc'
1317
+ | 'easeInBack'
1318
+ | 'easeOutBack'
1319
+ | 'easeInOutBack'
1320
+ | 'easeInElastic'
1321
+ | 'easeOutElastic'
1322
+ | 'easeOutBounce'
1323
+ | 'standard'
1324
+ | 'emphasized'
1325
+ | 'emphasizedDecelerate'
1326
+ | 'emphasizedAccelerate';
1327
+ /** A curve name, or a curve built by `cubicBezier()` / returned by `spring()`. */
1328
+ export type Easing = EasingName | ((progress: number) => number);
1329
+ /** Every `EasingName`, in the order they are documented. */
1330
+ export const EASING_NAMES: EasingName[];
1331
+ export interface SpringOptions {
1332
+ stiffness?: number;
1333
+ damping?: number;
1334
+ mass?: number;
1335
+ initialVelocity?: number;
1336
+ epsilon?: number;
1337
+ maxDuration?: number;
1338
+ }
1339
+ export interface SpringCurve {
1340
+ easing: (progress: number) => number;
1341
+ duration: number;
1342
+ overshoot: boolean;
1343
+ }
1344
+ /** The cubic-bezier timing function as a curve, for imperative animation. */
1345
+ export function cubicBezier(
1346
+ x1: number,
1347
+ y1: number,
1348
+ x2: number,
1349
+ y2: number
1350
+ ): (progress: number) => number;
1351
+ /** A damped spring: the curve plus the duration it needs to settle. */
1352
+ export function spring(options?: SpringOptions): SpringCurve;
1353
+ export function animateSpring(
1354
+ widget: Widget,
1355
+ property: string,
1356
+ from: any,
1357
+ to: any,
1358
+ options?: SpringOptions
1359
+ ): () => boolean;
1360
+ export function animateSpringAsync(
1361
+ widget: Widget,
1362
+ property: string,
1363
+ from: any,
1364
+ to: any,
1365
+ options?: SpringOptions
1366
+ ): Promise<Widget>;
1367
+
1368
+ export interface SlideInOptions {
1369
+ direction?: 'up' | 'down' | 'left' | 'right';
1370
+ distance?: number;
1371
+ duration?: number;
1372
+ easing?: Easing;
1373
+ fade?: boolean;
1374
+ }
1375
+ export interface PopInOptions {
1376
+ from?: number;
1377
+ to?: number;
1378
+ duration?: number;
1379
+ easing?: Easing;
1380
+ fade?: boolean;
1381
+ }
1382
+ export interface ShakeOptions {
1383
+ axis?: 'x' | 'y';
1384
+ distance?: number;
1385
+ cycles?: number;
1386
+ duration?: number;
1387
+ }
1388
+ export function slideIn(widget: Widget, options?: SlideInOptions): () => boolean;
1389
+ export function slideInAsync(widget: Widget, options?: SlideInOptions): Promise<Widget>;
1390
+ export function slideOut(widget: Widget, options?: SlideInOptions): () => boolean;
1391
+ export function slideOutAsync(widget: Widget, options?: SlideInOptions): Promise<Widget>;
1392
+ export function popIn(widget: Widget, options?: PopInOptions): () => boolean;
1393
+ export function popInAsync(widget: Widget, options?: PopInOptions): Promise<Widget>;
1394
+ export function shake(widget: Widget, options?: ShakeOptions): () => boolean;
1395
+ export function shakeAsync(widget: Widget, options?: ShakeOptions): Promise<Widget>;
1396
+
1397
+ export type MotionMode = 'auto' | 'reduce' | 'full';
1398
+ export interface StaggerOptions {
1399
+ start?: number;
1400
+ step?: number;
1401
+ max?: number;
1402
+ }
1403
+ /** True when the app or the operating system asked for no movement. */
1404
+ export function prefersReducedMotion(): boolean;
1405
+ export function getMotionMode(): MotionMode;
1406
+ export function setMotionMode(mode: MotionMode): MotionMode;
1407
+ export function onMotionModeChange(
1408
+ listener: (reduced: boolean) => void
1409
+ ): () => void;
1410
+ /** The duration to actually run: 0 when motion is reduced. */
1411
+ export function motionDuration(duration: number): number;
1412
+ /** The capped delay of item `index` in an entrance. */
1413
+ export function stagger(index: number, options?: StaggerOptions): number;
1414
+
1224
1415
  // =========================================================================
1225
1416
  // Widgets adicionales (Data, Media, Utils)
1226
1417
  // =========================================================================
@@ -1555,6 +1746,7 @@ declare module 'flet-box' {
1555
1746
 
1556
1747
  export interface InstallButtonProps extends CommonProps {
1557
1748
  text?: string;
1749
+ icon?: string;
1558
1750
  variant?: 'filled' | 'outlined' | 'text';
1559
1751
  size?: 'small' | 'medium' | 'large';
1560
1752
  borderRadius?: number;
@@ -2011,10 +2203,58 @@ declare module 'flet-box' {
2011
2203
  orientation(): 'portrait' | 'landscape';
2012
2204
  isTouch(): boolean;
2013
2205
  hasGesture(): boolean;
2014
- onOrientationChange(callback: (orientation: string) => void): void;
2015
- onResize(callback: (size: { width: number; height: number; orientation: string }) => void): void;
2206
+ // Both return the unsubscribe; the runtime always did, the type hid it.
2207
+ onOrientationChange(callback: (orientation: string) => void): () => void;
2208
+ onResize(callback: (size: { width: number; height: number; orientation: string }) => void): () => void;
2016
2209
  };
2017
2210
 
2211
+ /** The five viewport widths a screen answers to, in CSS pixels. */
2212
+ export type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
2213
+
2214
+ /** `md` is 768 — the same switch `AdaptiveScaffold` uses for its drawer. */
2215
+ export const BREAKPOINTS: Record<Breakpoint, number>;
2216
+ export const BREAKPOINT_NAMES: Breakpoint[];
2217
+
2218
+ /** Which breakpoint a given width answers to (pure, no DOM). */
2219
+ export function breakpointForWidth(px: number): Breakpoint;
2220
+ /** The breakpoint the live viewport is in; `xs` where there is no window. */
2221
+ export function currentBreakpoint(): Breakpoint;
2222
+ /** Whether the viewport has reached `name`. */
2223
+ export function atLeast(name: Breakpoint): boolean;
2224
+
2225
+ /**
2226
+ * Value for the current breakpoint. Keys are breakpoint names and an unnamed
2227
+ * step inherits the nearest smaller one: `pick({ xs: 8, md: 16 })` is 8, 8, 16,
2228
+ * 16, 16.
2229
+ */
2230
+ export function pick<T>(values: Partial<Record<Breakpoint, T>>, fallback?: T): T;
2231
+
2232
+ /**
2233
+ * Subscribe to breakpoint crossings — not to every pixel of a resize.
2234
+ * @returns The unsubscribe. Call it from `onUnmount`.
2235
+ */
2236
+ export function onBreakpointChange(listener: (breakpoint: Breakpoint) => void): () => void;
2237
+
2238
+ export interface LoadFontOptions {
2239
+ /** Weights to fetch, e.g. `[400, 600]`. Google needs them or you get 400 only. */
2240
+ weights?: number[];
2241
+ /** A stylesheet to load instead of the provider URL — a self-hosted `@font-face`. */
2242
+ url?: string;
2243
+ /** CSS `font-display`, provider URLs only. Defaults to `"swap"`. */
2244
+ display?: 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
2245
+ }
2246
+
2247
+ /**
2248
+ * Load a webfont family so a `fontFamily` prop has the bytes behind it. One
2249
+ * request per family, whoever asks first. Resolves `false` when the face could
2250
+ * not be had — a webfont is a network request, and the screen keeps its
2251
+ * fallback instead of failing.
2252
+ */
2253
+ export function loadFont(family: string, options?: LoadFontOptions): Promise<boolean>;
2254
+
2255
+ /** Wait for the faces the document has already started, before a canvas paint. */
2256
+ export function fontsReady(): Promise<boolean>;
2257
+
2018
2258
  export const os: {
2019
2259
  name(): string;
2020
2260
  version(): string;
@@ -2061,8 +2301,114 @@ declare module 'flet-box' {
2061
2301
  isSafe(value: string): boolean;
2062
2302
  };
2063
2303
 
2064
- export function formatDate(date: Date | string | number, format?: string): string;
2065
- export function relativeTime(date: Date | string | number): string;
2304
+ /** Tokens `YYYY MM DD HH mm ss`; anything else passes through. Defaults to `YYYY-MM-DD`. */
2305
+ export function formatDate(date: DateInput, format?: string): string;
2306
+ export function relativeTime(date: DateInput): string;
2307
+ export function timeAgo(date: DateInput): string;
2308
+ export function timeAgoLocale(date: DateInput, locale?: "en" | "es"): string;
2309
+
2310
+ /** Anything `new Date()` accepts. */
2311
+ export type DateInput = Date | string | number;
2312
+
2313
+ /** Unit names accepted by {@link dateDiff}; a calendar month counts as 30 days and a year as 365. */
2314
+ export type DateDiffUnit =
2315
+ | "milliseconds"
2316
+ | "seconds"
2317
+ | "minutes"
2318
+ | "hours"
2319
+ | "days"
2320
+ | "weeks"
2321
+ | "months"
2322
+ | "years";
2323
+
2324
+ /** Milliseconds in `n` seconds. */
2325
+ export function seconds(n: number): number;
2326
+ /** Milliseconds in `n` minutes. */
2327
+ export function minutes(n: number): number;
2328
+ /** Milliseconds in `n` hours. */
2329
+ export function hours(n: number): number;
2330
+ /** Milliseconds in `n` days. */
2331
+ export function days(n: number): number;
2332
+ /** Milliseconds in `n` weeks. */
2333
+ export function weeks(n: number): number;
2334
+ /** Milliseconds in `n` months, a month being 30 days. */
2335
+ export function months(n: number): number;
2336
+ /** Milliseconds in `n` years, a year being 365 days. */
2337
+ export function years(n: number): number;
2338
+
2339
+ export function today(): string;
2340
+ export function yesterday(): string;
2341
+ export function tomorrow(): string;
2342
+
2343
+ /** Full English weekday name, e.g. `"Monday"`. */
2344
+ export function dayOfWeek(date: DateInput): string;
2345
+ /** Full English month name, e.g. `"January"`. */
2346
+ export function monthName(date: DateInput): string;
2347
+
2348
+ export function isToday(date: DateInput): boolean;
2349
+ export function isYesterday(date: DateInput): boolean;
2350
+ export function isTomorrow(date: DateInput): boolean;
2351
+ export function isWeekend(date: DateInput): boolean;
2352
+ export function isLeapYear(year: number): boolean;
2353
+
2354
+ /** `"Monday, January 15, 2024"` — the Intl long date for `locale`. */
2355
+ export function formatDateLong(date: DateInput, locale?: string): string;
2356
+ export function formatDateShort(date: DateInput, locale?: string): string; /** 24-hour `HH:mm:ss`. */
2357
+ export function formatTime(date: DateInput): string;
2358
+ /** 12-hour `hh:mm:ss AM`. */
2359
+ export function formatTimeAmPm(date: DateInput): string;
2360
+
2361
+ /** Token-based parse; the separators of `format` are `-`, `/`, `:` or a space. */
2362
+ export function parseDate(str: string, format?: string): Date;
2363
+
2364
+ /** Absolute difference, so the argument order does not matter. */
2365
+ export function dateDiff(date1: DateInput, date2: DateInput, unit?: DateDiffUnit): number;
2366
+
2367
+ /** New `Date` — the input is never mutated. A negative `n` subtracts. */
2368
+ export function addDays(date: DateInput, n: number): Date;
2369
+ export function addHours(date: DateInput, n: number): Date;
2370
+ export function addMinutes(date: DateInput, n: number): Date;
2371
+ export function addSeconds(date: DateInput, n: number): Date;
2372
+ export function subtractDays(date: DateInput, n: number): Date;
2373
+ export function subtractHours(date: DateInput, n: number): Date;
2374
+ export function subtractMinutes(date: DateInput, n: number): Date;
2375
+ export function subtractSeconds(date: DateInput, n: number): Date;
2376
+
2377
+ /** Alias of {@link sleep}. */
2378
+ export function waitFor(ms: number): Promise<void>;
2379
+ /** Alias of {@link sleep}. */
2380
+ export function wait(ms: number): Promise<void>;
2381
+
2382
+ /**
2383
+ * Polls `condition` until it is truthy.
2384
+ * @throws when `timeout` elapses first.
2385
+ */
2386
+ export function waitUntil(
2387
+ condition: () => boolean,
2388
+ timeout?: number,
2389
+ interval?: number,
2390
+ ): Promise<true>;
2391
+
2392
+ /**
2393
+ * Polls the DOM for `selector` until the element appears. Needs a document:
2394
+ * use it in a browser, never at module scope.
2395
+ * @throws when `timeout` elapses first.
2396
+ */
2397
+ export function waitForElement(
2398
+ selector: string,
2399
+ timeout?: number,
2400
+ interval?: number,
2401
+ ): Promise<Element>;
2402
+
2403
+ /** One-shot performance clock. `stop()` logs and returns the ms; `elapsed()` reads without logging. */
2404
+ export function timer(label: string): { stop(): number; elapsed(): number };
2405
+
2406
+ /** `setInterval` with the arguments in the order a reader expects. */
2407
+ export function every(ms: number, callback: () => void): number;
2408
+ export function everySecond(callback: () => void): number;
2409
+ export function everyMinute(callback: () => void): number;
2410
+ export function everyHour(callback: () => void): number;
2411
+ export function everyDay(callback: () => void): number;
2066
2412
  export function formatMediaTime(seconds: number): string;
2067
2413
  export function formatMediaTimeLong(seconds: number): string;
2068
2414
  export function getProgressPercent(current: number, total: number): number;
@@ -2225,6 +2571,12 @@ declare module 'flet-box' {
2225
2571
  export function upperCase(str: string): string;
2226
2572
  export function reverseString(str: string): string;
2227
2573
  export function truncate(str: string, length?: number, suffix?: string): string;
2574
+ export function startsWith(str: string, prefix: string): boolean;
2575
+ export function endsWith(str: string, suffix: string): boolean;
2576
+ export function includes(str: string, substr: string): boolean;
2577
+ export function split(str: string, separator: string): string[];
2578
+ export function join(arr: readonly any[], separator?: string): string;
2579
+ export function trim(str: string): string;
2228
2580
 
2229
2581
  export function shuffle<T>(arr: T[]): T[];
2230
2582
  export function reverse<T>(arr: T[]): T[];
package/src/index.js CHANGED
@@ -37,6 +37,12 @@ export {
37
37
  upperCase,
38
38
  reverseString,
39
39
  truncate,
40
+ startsWith,
41
+ endsWith,
42
+ includes,
43
+ split,
44
+ join,
45
+ trim,
40
46
  animation,
41
47
  border,
42
48
  gradient,
@@ -60,11 +66,63 @@ export {
60
66
  now,
61
67
  formatDate,
62
68
  relativeTime,
69
+ seconds,
70
+ minutes,
71
+ hours,
72
+ days,
73
+ weeks,
74
+ months,
75
+ years,
76
+ today,
77
+ yesterday,
78
+ tomorrow,
79
+ dayOfWeek,
80
+ monthName,
81
+ isToday,
82
+ isYesterday,
83
+ isTomorrow,
84
+ isWeekend,
85
+ isLeapYear,
86
+ formatDateLong,
87
+ formatDateShort,
88
+ formatTime,
89
+ formatTimeAmPm,
90
+ parseDate,
91
+ dateDiff,
92
+ addDays,
93
+ addHours,
94
+ addMinutes,
95
+ addSeconds,
96
+ subtractDays,
97
+ subtractHours,
98
+ subtractMinutes,
99
+ subtractSeconds,
100
+ timeAgo,
101
+ timeAgoLocale,
102
+ waitFor,
103
+ wait,
104
+ waitUntil,
105
+ waitForElement,
106
+ timer,
107
+ every,
108
+ everySecond,
109
+ everyMinute,
110
+ everyHour,
111
+ everyDay,
63
112
  device,
64
113
  os,
65
114
  dimensions,
66
115
  width,
67
116
  height,
117
+ BREAKPOINTS,
118
+ BREAKPOINT_NAMES,
119
+ breakpointForWidth,
120
+ currentBreakpoint,
121
+ atLeast,
122
+ pick,
123
+ onBreakpointChange,
124
+ loadFont,
125
+ fontsReady,
68
126
  useState,
69
127
  useWatchState,
70
128
  memo,
@@ -289,4 +347,23 @@ export {
289
347
  fadeInAsync,
290
348
  fadeOutAsync,
291
349
  pulseAsync,
350
+ animateSpring,
351
+ animateSpringAsync,
352
+ slideIn,
353
+ slideInAsync,
354
+ slideOut,
355
+ slideOutAsync,
356
+ popIn,
357
+ popInAsync,
358
+ shake,
359
+ shakeAsync,
360
+ prefersReducedMotion,
361
+ getMotionMode,
362
+ setMotionMode,
363
+ onMotionModeChange,
364
+ motionDuration,
365
+ stagger,
366
+ EASING_NAMES,
367
+ cubicBezier,
368
+ spring,
292
369
  } from "./animations/index.js";
@@ -2,6 +2,7 @@ import { Scaffold } from "./Scaffold.js";
2
2
  import { Drawer } from "./Drawer.js";
3
3
  import { CollapsibleSideBar } from "./CollapsibleSideBar.js";
4
4
  import { device } from "../tools/device.js";
5
+ import { BREAKPOINTS } from "../tools/breakpoints.js";
5
6
  import { colors } from "../utils/themes.js";
6
7
 
7
8
  /**
@@ -27,7 +28,8 @@ import { colors } from "../utils/themes.js";
27
28
  * switches navigation patterns based on screen width and device type.
28
29
  *
29
30
  * Layout rules:
30
- * - **Mobile** (`window.innerWidth < 768` or `device.isMobile()` or `forceMobile`):
31
+ * - **Mobile** (`window.innerWidth < BREAKPOINTS.md`, i.e. under 768, or
32
+ * `device.isMobile()` or `forceMobile`):
31
33
  * - `leftNavBar` is placed inside a slide-over Drawer (hidden by default).
32
34
  * - `rightNavBar` is hidden entirely.
33
35
  * - **Desktop** (all other cases):
@@ -61,9 +63,11 @@ export const AdaptiveScaffold = (props) => {
61
63
  // Determine the layout mode. forceMobile/forceDesktop props override the
62
64
  // automatic detection, which uses both the device API and the window width
63
65
  // so that resizing a desktop browser to a small width triggers mobile layout.
66
+ // The width is the framework's own `md` step rather than a private 768, so the
67
+ // scaffold and every `atLeast("md")` in the app answer to the same line.
64
68
  const isMobile =
65
69
  forceMobile ||
66
- (!forceDesktop && (device.isMobile() || window.innerWidth < 768));
70
+ (!forceDesktop && (device.isMobile() || window.innerWidth < BREAKPOINTS.md));
67
71
 
68
72
  let finalLeftNavBar = null;
69
73
  let drawerInstance = null;