@sincpro/mobile-ui 2.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/README.md +7 -2
  2. package/dist/Actions/Actions.Button.d.ts +2 -1
  3. package/dist/Actions/Actions.Button.js +31 -37
  4. package/dist/Actions/Actions.Dock.d.ts +6 -6
  5. package/dist/Actions/Actions.Dock.js +14 -72
  6. package/dist/Actions/Actions.FAB.d.ts +2 -1
  7. package/dist/Actions/Actions.FilterButton.js +1 -1
  8. package/dist/Actions/Actions.IconButton.d.ts +3 -4
  9. package/dist/Actions/Actions.IconButton.js +2 -2
  10. package/dist/Dialog/Confirmation.context.d.ts +5 -14
  11. package/dist/Dialog/Confirmation.context.js +11 -51
  12. package/dist/Dialog/Dialog.ActionSheet.d.ts +8 -7
  13. package/dist/Dialog/Dialog.ActionSheet.js +45 -19
  14. package/dist/Dialog/Dialog.Confirmation.d.ts +14 -11
  15. package/dist/Dialog/Dialog.Confirmation.js +38 -14
  16. package/dist/Dialog/Dialog.EditValue.d.ts +21 -16
  17. package/dist/Dialog/Dialog.EditValue.js +73 -14
  18. package/dist/Dialog/Dialog.Sheet.d.ts +8 -24
  19. package/dist/Dialog/Dialog.Sheet.js +15 -60
  20. package/dist/Dialog/index.d.ts +14 -14
  21. package/dist/Dialog/index.js +5 -3
  22. package/dist/Dialog/requestConfirmation.d.ts +3 -0
  23. package/dist/Dialog/requestConfirmation.js +13 -0
  24. package/dist/Display/Display.Accordion.js +1 -1
  25. package/dist/Display/Display.Avatar.d.ts +6 -6
  26. package/dist/Display/Display.Avatar.js +2 -2
  27. package/dist/Display/Display.Badge.js +4 -4
  28. package/dist/Display/Display.Card.js +5 -2
  29. package/dist/Display/Display.Carousel.js +1 -1
  30. package/dist/Display/Display.CircleButton.d.ts +3 -8
  31. package/dist/Display/Display.CircleButton.js +2 -2
  32. package/dist/Display/Display.CopyableText.js +1 -5
  33. package/dist/Display/Display.Date.d.ts +3 -2
  34. package/dist/Display/Display.Date.js +3 -3
  35. package/dist/Display/Display.GradientSurface.d.ts +52 -9
  36. package/dist/Display/Display.GradientSurface.js +9 -14
  37. package/dist/Display/Display.Icon.d.ts +7 -11
  38. package/dist/Display/Display.Icon.js +15 -30
  39. package/dist/Display/Display.Image.d.ts +2 -1
  40. package/dist/Display/Display.Image.js +1 -1
  41. package/dist/Display/Display.KeyValue.js +1 -1
  42. package/dist/Display/Display.ListItem.js +1 -1
  43. package/dist/Display/Display.MenuButton.js +4 -4
  44. package/dist/Display/Display.MenuCard.d.ts +3 -8
  45. package/dist/Display/Display.MenuCard.js +2 -2
  46. package/dist/Display/Display.MenuGrid.d.ts +2 -7
  47. package/dist/Display/Display.MenuGrid.js +1 -1
  48. package/dist/Display/Display.Monetary.d.ts +2 -6
  49. package/dist/Display/Display.Monetary.js +2 -2
  50. package/dist/Display/Display.PriceTag.js +1 -1
  51. package/dist/Display/Display.Rating.js +1 -1
  52. package/dist/Display/Display.SearchHistory.d.ts +2 -1
  53. package/dist/Display/Display.SearchHistory.js +1 -1
  54. package/dist/Display/Display.StatCard.d.ts +2 -1
  55. package/dist/Display/Display.StatCard.js +1 -1
  56. package/dist/Display/Display.StoryViewer.js +10 -6
  57. package/dist/Display/Display.SwipeableRow.d.ts +2 -1
  58. package/dist/Display/Display.Timeline.js +1 -1
  59. package/dist/Display/icon-map.d.ts +344 -0
  60. package/dist/Display/icon-map.js +86 -0
  61. package/dist/Display/index.d.ts +2 -0
  62. package/dist/Display/index.js +1 -0
  63. package/dist/Feedback/Feedback.Banner.js +13 -12
  64. package/dist/Feedback/Feedback.Empty.js +1 -1
  65. package/dist/Feedback/Feedback.EmptyState.d.ts +2 -1
  66. package/dist/Feedback/Feedback.EmptyState.js +1 -1
  67. package/dist/Feedback/Feedback.Error.js +1 -1
  68. package/dist/Feedback/Feedback.PermissionCard.d.ts +2 -1
  69. package/dist/Feedback/Feedback.PermissionCard.js +2 -2
  70. package/dist/Feedback/Toast.context.js +3 -3
  71. package/dist/Form/Form.ControlledDropdown.d.ts +5 -11
  72. package/dist/Form/Form.ControlledDropdown.js +5 -2
  73. package/dist/Form/Form.DatePicker.d.ts +5 -9
  74. package/dist/Form/Form.DatePicker.js +116 -21
  75. package/dist/Form/Form.Dropdown.d.ts +11 -16
  76. package/dist/Form/Form.Dropdown.js +54 -90
  77. package/dist/Form/Form.FractionalQuantityInput.js +4 -11
  78. package/dist/Form/Form.Input.d.ts +3 -3
  79. package/dist/Form/Form.KeyboardForm.d.ts +2 -2
  80. package/dist/Form/Form.KeyboardForm.js +2 -2
  81. package/dist/Form/Form.KeyboardToolbar.d.ts +2 -1
  82. package/dist/Form/Form.KeyboardToolbar.js +3 -7
  83. package/dist/Form/Form.PasswordInput.js +1 -1
  84. package/dist/Form/Form.QuantitySelector.js +1 -1
  85. package/dist/Form/Form.Select.d.ts +12 -8
  86. package/dist/Form/Form.Select.js +64 -31
  87. package/dist/Form/Form.Slider.d.ts +4 -7
  88. package/dist/Form/Form.Slider.js +11 -10
  89. package/dist/Form/Form.Switch.js +2 -1
  90. package/dist/Form/index.d.ts +6 -14
  91. package/dist/Form/index.js +0 -11
  92. package/dist/Navigation/Navigation.AppBar.d.ts +2 -1
  93. package/dist/Navigation/Navigation.AppBar.js +2 -2
  94. package/dist/Navigation/Navigation.BottomNav.d.ts +4 -8
  95. package/dist/Navigation/Navigation.BottomNav.js +2 -2
  96. package/dist/Navigation/Navigation.CollapsingHeader.js +3 -2
  97. package/dist/Navigation/Navigation.Menu.d.ts +16 -11
  98. package/dist/Navigation/Navigation.Menu.js +97 -42
  99. package/dist/Navigation/Navigation.Search.js +2 -2
  100. package/dist/Navigation/Navigation.SearchBar.js +3 -4
  101. package/dist/Navigation/Navigation.SegmentedControl.d.ts +7 -25
  102. package/dist/Navigation/Navigation.SegmentedControl.js +18 -39
  103. package/dist/Navigation/Navigation.Steps.js +1 -1
  104. package/dist/Navigation/Navigation.Tabs.d.ts +2 -1
  105. package/dist/Navigation/index.d.ts +0 -6
  106. package/dist/Navigation/index.js +0 -8
  107. package/dist/Typography/Typography.SelectableText.js +3 -7
  108. package/dist/Typography/Typography.Text.d.ts +1 -6
  109. package/dist/Typography/Typography.Text.js +3 -6
  110. package/dist/index.d.ts +1 -4
  111. package/dist/index.js +1 -4
  112. package/dist/layouts/AuthContainer.d.ts +4 -2
  113. package/dist/layouts/AuthContainer.js +3 -4
  114. package/dist/layouts/Container.d.ts +0 -4
  115. package/dist/layouts/Container.js +3 -4
  116. package/dist/layouts/ScrollContainer.d.ts +7 -3
  117. package/dist/layouts/ScrollContainer.js +5 -4
  118. package/dist/primitives/BaseButton.d.ts +2 -1
  119. package/dist/theme/build.js +0 -4
  120. package/dist/theme/css-vars.js +0 -4
  121. package/dist/theme/dark.js +2 -6
  122. package/dist/theme/default.js +1 -5
  123. package/dist/theme/index.d.ts +1 -1
  124. package/dist/theme/index.js +1 -1
  125. package/dist/theme/types.d.ts +0 -11
  126. package/dist/theme/typography.d.ts +8 -7
  127. package/dist/theme/typography.js +25 -4
  128. package/dist/utils/timezone.d.ts +0 -5
  129. package/dist/views/AuthFormView.d.ts +3 -1
  130. package/dist/views/AuthFormView.js +2 -2
  131. package/dist/views/FormViewV2.d.ts +6 -13
  132. package/dist/views/FormViewV2.js +29 -25
  133. package/dist/views/ListViewV2.context.d.ts +5 -5
  134. package/dist/views/ListViewV2.d.ts +23 -18
  135. package/dist/views/ListViewV2.js +45 -48
  136. package/dist/views/types/IListView.d.ts +5 -4
  137. package/dist/views/types/IListView.js +18 -15
  138. package/dist/widgets/CollectionOptions.d.ts +2 -2
  139. package/dist/widgets/CollectionOptions.js +92 -65
  140. package/dist/widgets/FilterBar.js +1 -1
  141. package/dist/widgets/PromoModal.js +10 -4
  142. package/dist/widgets/QuickActions.d.ts +2 -7
  143. package/dist/widgets/QuickActions.js +1 -1
  144. package/dist/widgets/ReorderableList.js +3 -3
  145. package/dist/widgets/TimeZoneSelectorModal.d.ts +1 -6
  146. package/dist/widgets/TimeZoneSelectorModal.js +17 -12
  147. package/dist/widgets/index.d.ts +0 -11
  148. package/dist/widgets/index.js +0 -7
  149. package/package.json +35 -38
  150. package/tailwind.preset.js +0 -4
  151. package/dist/Dialog/BottomSheet/BottomSheet.Root.d.ts +0 -324
  152. package/dist/Dialog/BottomSheet/BottomSheet.Root.js +0 -373
  153. package/dist/Dialog/BottomSheet/index.d.ts +0 -1
  154. package/dist/Dialog/BottomSheet/index.js +0 -1
  155. package/dist/Dialog/Dialog.NativeSheet.d.ts +0 -10
  156. package/dist/Dialog/Dialog.NativeSheet.js +0 -21
  157. package/dist/Form/Form.Button.d.ts +0 -3
  158. package/dist/Form/Form.Button.js +0 -1
  159. package/dist/Form/Form.FAB.d.ts +0 -3
  160. package/dist/Form/Form.FAB.js +0 -1
  161. package/dist/Form/Form.FilterButton.d.ts +0 -3
  162. package/dist/Form/Form.FilterButton.js +0 -1
  163. package/dist/Form/Form.IconButton.d.ts +0 -3
  164. package/dist/Form/Form.IconButton.js +0 -1
  165. package/dist/Form/Form.SearchBar.d.ts +0 -13
  166. package/dist/Form/Form.SearchBar.js +0 -26
  167. package/dist/Navigation/Navigation.Header.d.ts +0 -16
  168. package/dist/Navigation/Navigation.Header.js +0 -17
  169. package/dist/Navigation/Navigation.HeaderToolbar.d.ts +0 -15
  170. package/dist/Navigation/Navigation.HeaderToolbar.js +0 -18
  171. package/dist/Navigation/Navigation.TabIcon.d.ts +0 -18
  172. package/dist/Navigation/Navigation.TabIcon.js +0 -12
  173. package/dist/Navigation/Navigation.TabItem.d.ts +0 -19
  174. package/dist/Navigation/Navigation.TabItem.js +0 -13
  175. package/dist/icons/OdooIcon.d.ts +0 -11
  176. package/dist/icons/OdooIcon.js +0 -11
  177. package/dist/icons/PrinterIcon.d.ts +0 -9
  178. package/dist/icons/PrinterIcon.js +0 -9
  179. package/dist/layouts/TabNavigatorLayout.d.ts +0 -26
  180. package/dist/layouts/TabNavigatorLayout.js +0 -60
  181. package/dist/utils/icon.d.ts +0 -1
  182. package/dist/utils/icon.js +0 -1
  183. package/dist/widgets/CircleButton.d.ts +0 -3
  184. package/dist/widgets/CircleButton.js +0 -1
  185. package/dist/widgets/GeoPermissionCard.d.ts +0 -13
  186. package/dist/widgets/GeoPermissionCard.js +0 -24
  187. package/dist/widgets/HomeHeader.d.ts +0 -15
  188. package/dist/widgets/HomeHeader.js +0 -13
  189. package/dist/widgets/MenuCard.d.ts +0 -3
  190. package/dist/widgets/MenuCard.js +0 -1
  191. package/dist/widgets/MenuGrid.d.ts +0 -5
  192. package/dist/widgets/MenuGrid.js +0 -1
  193. package/dist/widgets/MenuSection.d.ts +0 -3
  194. package/dist/widgets/MenuSection.js +0 -1
  195. package/dist/widgets/ScreenHeader.d.ts +0 -25
  196. package/dist/widgets/ScreenHeader.js +0 -32
  197. package/dist/widgets/TimeZoneSelector.d.ts +0 -12
  198. package/dist/widgets/TimeZoneSelector.js +0 -24
@@ -60,21 +60,21 @@ export declare const typographyVariants: {
60
60
  readonly fontFamily: string;
61
61
  readonly fontWeight: "900";
62
62
  readonly lineHeight: number;
63
- readonly letterSpacing: -0.25;
63
+ readonly letterSpacing: -1.2;
64
64
  };
65
65
  readonly h1: {
66
66
  readonly fontSize: 32;
67
67
  readonly fontFamily: string;
68
68
  readonly fontWeight: "700";
69
69
  readonly lineHeight: number;
70
- readonly letterSpacing: -0.25;
70
+ readonly letterSpacing: -0.8;
71
71
  };
72
72
  readonly h2: {
73
73
  readonly fontSize: 28;
74
74
  readonly fontFamily: string;
75
75
  readonly fontWeight: "700";
76
76
  readonly lineHeight: number;
77
- readonly letterSpacing: 0;
77
+ readonly letterSpacing: -0.4;
78
78
  };
79
79
  readonly h3: {
80
80
  readonly fontSize: 24;
@@ -219,6 +219,7 @@ export declare const typographyVariants: {
219
219
  };
220
220
  };
221
221
  export declare const variantFontRole: Record<keyof typeof typographyVariants, FontRole>;
222
+ export declare function appliedTextStyle(variant: keyof typeof typographyVariants, bold?: boolean, semibold?: boolean): TextStyle;
222
223
  export declare const createTextStyle: (variant: keyof typeof typographyVariants, overrides?: Partial<TextStyle>) => TextStyle;
223
224
  export declare const createCustomTextStyle: (fontSize: keyof typeof fontSizes, fontFamily: keyof typeof fontFamilies, options?: {
224
225
  lineHeight?: keyof typeof lineHeights;
@@ -278,21 +279,21 @@ export declare const Typography: {
278
279
  readonly fontFamily: string;
279
280
  readonly fontWeight: "900";
280
281
  readonly lineHeight: number;
281
- readonly letterSpacing: -0.25;
282
+ readonly letterSpacing: -1.2;
282
283
  };
283
284
  readonly h1: {
284
285
  readonly fontSize: 32;
285
286
  readonly fontFamily: string;
286
287
  readonly fontWeight: "700";
287
288
  readonly lineHeight: number;
288
- readonly letterSpacing: -0.25;
289
+ readonly letterSpacing: -0.8;
289
290
  };
290
291
  readonly h2: {
291
292
  readonly fontSize: 28;
292
293
  readonly fontFamily: string;
293
294
  readonly fontWeight: "700";
294
295
  readonly lineHeight: number;
295
- readonly letterSpacing: 0;
296
+ readonly letterSpacing: -0.4;
296
297
  };
297
298
  readonly h3: {
298
299
  readonly fontSize: 24;
@@ -436,7 +437,7 @@ export declare const Typography: {
436
437
  readonly letterSpacing: 0;
437
438
  };
438
439
  };
439
- readonly variantFontRole: Record<"display" | "label" | "button" | "title" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "bodyReadable" | "bodyLarge" | "body" | "bodySmall" | "labelSmall" | "data" | "dataLarge" | "overline" | "caption" | "captionSmall" | "buttonSmall" | "buttonLarge" | "subtitle", FontRole>;
440
+ readonly variantFontRole: Record<"display" | "button" | "label" | "title" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "bodyReadable" | "bodyLarge" | "body" | "bodySmall" | "labelSmall" | "data" | "dataLarge" | "overline" | "caption" | "captionSmall" | "buttonSmall" | "buttonLarge" | "subtitle", FontRole>;
440
441
  readonly createTextStyle: (variant: keyof typeof typographyVariants, overrides?: Partial<TextStyle>) => TextStyle;
441
442
  readonly createCustomTextStyle: (fontSize: keyof typeof fontSizes, fontFamily: keyof typeof fontFamilies, options?: {
442
443
  lineHeight?: keyof typeof lineHeights;
@@ -91,7 +91,7 @@ export const typographyVariants = {
91
91
  fontFamily: fontFamilies.titleBlack,
92
92
  fontWeight: fontWeights.titleBlack,
93
93
  lineHeight: fontSizes["7xl"] * lineHeights.tight,
94
- letterSpacing: letterSpacing.tight,
94
+ letterSpacing: -1.2,
95
95
  },
96
96
  // Headings (title role)
97
97
  h1: {
@@ -99,14 +99,14 @@ export const typographyVariants = {
99
99
  fontFamily: fontFamilies.title,
100
100
  fontWeight: fontWeights.title,
101
101
  lineHeight: fontSizes["5xl"] * lineHeights.tight,
102
- letterSpacing: letterSpacing.tight,
102
+ letterSpacing: -0.8,
103
103
  },
104
104
  h2: {
105
105
  fontSize: fontSizes["4xl"],
106
106
  fontFamily: fontFamilies.title,
107
107
  fontWeight: fontWeights.title,
108
108
  lineHeight: fontSizes["4xl"] * lineHeights.snug,
109
- letterSpacing: letterSpacing.normal,
109
+ letterSpacing: -0.4,
110
110
  },
111
111
  h3: {
112
112
  fontSize: fontSizes["3xl"],
@@ -286,9 +286,30 @@ export const variantFontRole = {
286
286
  title: "semiBold",
287
287
  subtitle: "medium",
288
288
  };
289
+ /* The shipped files stop at Bold 700. Asking iOS/Android for 800 or 900 on
290
+ * that file synthesizes a faux face and drops the loaded family. Rendering
291
+ * uses the weight of the file that was actually registered. */
292
+ const loadedFaceWeight = {
293
+ IBMPlexMono_400Regular: "400",
294
+ IBMPlexMono_500Medium: "500",
295
+ IBMPlexSans_300Light: "300",
296
+ IBMPlexSans_400Regular: "400",
297
+ IBMPlexSans_500Medium: "500",
298
+ IBMPlexSans_600SemiBold: "600",
299
+ IBMPlexSans_700Bold: "700",
300
+ };
301
+ export function appliedTextStyle(variant, bold = false, semibold = false) {
302
+ const fontFamily = resolveTextFontFamily(variant, bold, semibold);
303
+ const { fontWeight, ...metrics } = typographyVariants[variant];
304
+ return {
305
+ ...metrics,
306
+ fontFamily,
307
+ fontWeight: loadedFaceWeight[fontFamily] ?? fontWeight,
308
+ };
309
+ }
289
310
  /* -------------------- Utility Functions -------------------- */
290
311
  export const createTextStyle = (variant, overrides) => ({
291
- ...typographyVariants[variant],
312
+ ...appliedTextStyle(variant),
292
313
  ...overrides,
293
314
  });
294
315
  export const createCustomTextStyle = (fontSize, fontFamily, options) => {
@@ -1,8 +1,3 @@
1
- /**
2
- * @deprecated Region-coupled (LatAm locale data); relocated to the core alongside the
3
- * TimeZone widgets. Import from `@sincpro/mobile/ui/components/molecules` instead. Kept
4
- * here for backwards compatibility and removed in the next major.
5
- */
6
1
  export interface TimezoneLocale {
7
2
  timezone: string;
8
3
  locale: string;
@@ -2,6 +2,7 @@ import type { KeyboardFormProps } from "../Form/Form.KeyboardForm.js";
2
2
  import type { AppBarBackground } from "../Navigation/Navigation.AppBar.js";
3
3
  import type { ImageSource } from "../utils/image.js";
4
4
  import { ReactNode } from "react";
5
+ import type { Edge } from "react-native-safe-area-context";
5
6
  interface AuthFormProps {
6
7
  title?: string;
7
8
  description?: string;
@@ -9,8 +10,9 @@ interface AuthFormProps {
9
10
  onBack?: () => void;
10
11
  FormNode: ReactNode;
11
12
  background?: AppBarBackground;
13
+ edges?: Edge[];
12
14
  keyboardToolbar?: boolean;
13
15
  keyboardFormProps?: Omit<KeyboardFormProps, "children" | "toolbar">;
14
16
  }
15
- declare function AuthFormView({ title, description, logoSource, onBack, FormNode, background, keyboardToolbar, keyboardFormProps, }: AuthFormProps): import("react").JSX.Element;
17
+ declare function AuthFormView({ title, description, logoSource, onBack, FormNode, background, edges, keyboardToolbar, keyboardFormProps, }: AuthFormProps): import("react").JSX.Element;
16
18
  export default AuthFormView;
@@ -5,7 +5,7 @@ import { Typography } from "../Typography/index.js";
5
5
  import { View } from "react-native";
6
6
  const Logo = Display.Logo;
7
7
  const Text = Typography.Text;
8
- function AuthFormView({ title, description, logoSource, onBack, FormNode, background, keyboardToolbar, keyboardFormProps, }) {
8
+ function AuthFormView({ title, description, logoSource, onBack, FormNode, background, edges, keyboardToolbar, keyboardFormProps, }) {
9
9
  const renderTitle = () => {
10
10
  if (!title)
11
11
  return null;
@@ -16,6 +16,6 @@ function AuthFormView({ title, description, logoSource, onBack, FormNode, backgr
16
16
  return null;
17
17
  return _jsx(Text, { variant: "body", children: description });
18
18
  };
19
- return (_jsx(AuthContainer, { background: background, header: _jsx(Logo, { size: "large", source: logoSource }), keyboardFormProps: keyboardFormProps, keyboardToolbar: keyboardToolbar, onBackPress: onBack, children: _jsxs(View, { className: "m-4", children: [_jsxs(View, { className: "mb-5", children: [renderTitle(), renderDescription()] }), FormNode] }) }));
19
+ return (_jsx(AuthContainer, { background: background, edges: edges, header: _jsx(Logo, { size: "large", source: logoSource }), keyboardFormProps: keyboardFormProps, keyboardToolbar: keyboardToolbar, onBackPress: onBack, children: _jsxs(View, { className: "m-4", children: [_jsxs(View, { className: "mb-5", children: [renderTitle(), renderDescription()] }), FormNode] }) }));
20
20
  }
21
21
  export default AuthFormView;
@@ -1,8 +1,8 @@
1
1
  import { type KeyboardFormProps } from "../Form/Form.KeyboardForm.js";
2
2
  import type { AppBarBackground } from "../Navigation/Navigation.AppBar.js";
3
3
  import { type SegmentedControlProps } from "../Navigation/Navigation.SegmentedControl.js";
4
- import type { ImageSource } from "../utils/image.js";
5
4
  import React, { ReactNode } from "react";
5
+ import type { Edge } from "react-native-safe-area-context";
6
6
  interface FormViewProps<T> {
7
7
  name: string;
8
8
  description?: string;
@@ -17,11 +17,14 @@ interface FormViewProps<T> {
17
17
  onRetry?: () => void;
18
18
  onBack?: () => void;
19
19
  withContainer?: boolean;
20
+ edges?: Edge[];
20
21
  children: ReactNode;
21
22
  }
22
23
  export declare function refreshFormData(onRefresh: (() => Promise<void>) | undefined, onRefreshError: ((error: unknown) => void) | undefined, setShowRefreshing: (value: boolean) => void): Promise<void>;
23
- declare function FormViewRoot<T>({ name, description, item, isLoading, isEmpty, hasError, errorMessage, readonly, onRefresh, onRefreshError, onRetry, onBack, withContainer, children, }: FormViewProps<T>): React.JSX.Element;
24
+ declare function FormViewRoot<T>({ name, description, item, isLoading, isEmpty, hasError, errorMessage, readonly, onRefresh, onRefreshError, onRetry, onBack, withContainer, edges, children, }: FormViewProps<T>): React.JSX.Element;
24
25
  interface HeaderProps {
26
+ /** Reserve the top inset; disable when the host already handles it. */
27
+ safeArea?: boolean;
25
28
  /** AppBar display variant. `large` = big title (list/index screens). `center` = modal/detail. */
26
29
  variant?: "default" | "large" | "center";
27
30
  /** Surface tone. `dark` = always-dark hero. `gradient` = brand accent gradient. */
@@ -40,10 +43,8 @@ interface HeaderProps {
40
43
  * See `AppBarBackground` for the full shape.
41
44
  */
42
45
  background?: AppBarBackground;
43
- /** @deprecated Pass `variant` prop instead. */
44
- logoSource?: ImageSource;
45
46
  }
46
- declare function Header({ variant, tone, actions, children, topSpacing, background, }: HeaderProps): React.JSX.Element;
47
+ declare function Header({ safeArea, variant, tone, actions, children, topSpacing, background, }: HeaderProps): React.JSX.Element;
47
48
  declare function HeaderActions({ children }: {
48
49
  children: ReactNode;
49
50
  }): React.JSX.Element;
@@ -62,13 +63,6 @@ declare function Content({ withMargin, keyboardToolbar, keyboardFormProps, child
62
63
  keyboardFormProps?: Omit<KeyboardFormProps, "children" | "toolbar">;
63
64
  children: ReactNode;
64
65
  }): React.JSX.Element;
65
- /**
66
- * @deprecated Content already handles isEmpty/!item by rendering Feedback.Empty internally.
67
- * Use `<FormViewV2.Content>` directly — this wrapper is redundant.
68
- */
69
- declare function Form({ children }: {
70
- children: ReactNode;
71
- }): React.JSX.Element;
72
66
  /** Layout wrapper for a set of Group cards. Content already handles isEmpty/loading states. */
73
67
  declare function Groups({ children }: {
74
68
  children: ReactNode;
@@ -148,7 +142,6 @@ export declare const FormViewV2: {
148
142
  Segmented: typeof HeaderSegmented;
149
143
  };
150
144
  Content: typeof Content & {
151
- Form: typeof Form;
152
145
  Groups: typeof Groups;
153
146
  Group: typeof Group;
154
147
  Section: typeof Section;
@@ -10,8 +10,17 @@ import { SegmentedControl, } from "../Navigation/Navigation.SegmentedControl.js"
10
10
  import { useTheme } from "../theme/index.js";
11
11
  import { FormViewProvider, useFormView } from "./FormViewV2.context.js";
12
12
  import React from "react";
13
- import { RefreshControl, View } from "react-native";
13
+ import { RefreshControl, useWindowDimensions, View } from "react-native";
14
+ import { KeyboardStickyView } from "react-native-keyboard-controller";
14
15
  const RefreshErrorContext = React.createContext(undefined);
16
+ const FooterHeightContext = React.createContext(null);
17
+ function useFooterHeight() {
18
+ const context = React.useContext(FooterHeightContext);
19
+ if (!context) {
20
+ throw new Error("FormViewV2.Content and Footer must be used within FormViewV2.Root");
21
+ }
22
+ return context;
23
+ }
15
24
  export async function refreshFormData(onRefresh, onRefreshError, setShowRefreshing) {
16
25
  setShowRefreshing(true);
17
26
  try {
@@ -30,16 +39,16 @@ export async function refreshFormData(onRefresh, onRefreshError, setShowRefreshi
30
39
  setShowRefreshing(false);
31
40
  }
32
41
  }
33
- function FormViewRoot({ name, description, item, isLoading = false, isEmpty = false, hasError = false, errorMessage, readonly = false, onRefresh, onRefreshError, onRetry, onBack, withContainer = true, children, }) {
42
+ function FormViewRoot({ name, description, item, isLoading = false, isEmpty = false, hasError = false, errorMessage, readonly = false, onRefresh, onRefreshError, onRetry, onBack, withContainer = true, edges = ["left", "right"], children, }) {
43
+ const [footerHeight, setFooterHeight] = React.useState(0);
44
+ const footerContext = React.useMemo(() => ({ height: footerHeight, setHeight: setFooterHeight }), [footerHeight]);
34
45
  const Wrapper = withContainer ? Container : View;
35
- const wrapperProps = withContainer
36
- ? { edges: ["bottom", "left", "right"] }
37
- : { style: { flex: 1 } };
38
- return (_jsx(FormViewProvider, { description: description, errorMessage: errorMessage, hasError: hasError, isEmpty: isEmpty, isLoading: isLoading, item: item, name: name, onBack: onBack, onRefresh: onRefresh, onRetry: onRetry, readonly: readonly, children: _jsx(RefreshErrorContext.Provider, { value: onRefreshError, children: _jsx(ErrorBoundary, { children: _jsx(Wrapper, { ...wrapperProps, children: children }) }) }) }));
46
+ const wrapperProps = withContainer ? { edges } : { style: { flex: 1 } };
47
+ return (_jsx(FormViewProvider, { description: description, errorMessage: errorMessage, hasError: hasError, isEmpty: isEmpty, isLoading: isLoading, item: item, name: name, onBack: onBack, onRefresh: onRefresh, onRetry: onRetry, readonly: readonly, children: _jsx(RefreshErrorContext.Provider, { value: onRefreshError, children: _jsx(ErrorBoundary, { children: _jsx(FooterHeightContext.Provider, { value: footerContext, children: _jsx(Wrapper, { ...wrapperProps, children: children }) }) }) }) }));
39
48
  }
40
- function Header({ variant = "default", tone, actions, children, topSpacing, background, }) {
49
+ function Header({ safeArea = true, variant = "default", tone, actions, children, topSpacing, background, }) {
41
50
  const { name, description, onBack } = useFormView();
42
- return (_jsx(AppBar, { actions: actions, background: background, onBack: onBack, safeArea: false, subheader: children, subtitle: description ?? undefined, title: name, tone: tone, topSpacing: topSpacing, variant: variant }));
51
+ return (_jsx(AppBar, { actions: actions, background: background, onBack: onBack, safeArea: safeArea, subheader: children, subtitle: description ?? undefined, title: name, tone: tone, topSpacing: topSpacing, variant: variant }));
43
52
  }
44
53
  function HeaderActions({ children }) {
45
54
  return (_jsx(View, { className: "flex-row px-4 my-2 items-center justify-between flex-1 gap-2", children: children }));
@@ -55,9 +64,11 @@ function HeaderActionsBar({ children }) {
55
64
  function HeaderSegmented(props) {
56
65
  return _jsx(SegmentedControl, { ...props });
57
66
  }
58
- function Content({ withMargin = false, keyboardToolbar = false, keyboardFormProps, children, }) {
67
+ function Content({ withMargin = false, keyboardToolbar = true, keyboardFormProps, children, }) {
59
68
  const { name, isLoading, isEmpty, hasError, errorMessage, item, onRefresh, onRetry, onBack, showRefreshing, setShowRefreshing, } = useFormView();
60
69
  const bottomInset = useBottomInset();
70
+ const { height: measuredFooterHeight } = useFooterHeight();
71
+ const stickyFooterHeight = isLoading || isEmpty ? 0 : measuredFooterHeight;
61
72
  const theme = useTheme();
62
73
  const onRefreshError = React.useContext(RefreshErrorContext);
63
74
  const handleRefresh = React.useCallback(async () => {
@@ -72,18 +83,7 @@ function Content({ withMargin = false, keyboardToolbar = false, keyboardFormProp
72
83
  paddingBottom: bottomInset,
73
84
  },
74
85
  keyboardFormProps?.contentContainerStyle,
75
- ], keyboardShouldPersistTaps: keyboardFormProps?.keyboardShouldPersistTaps ?? "handled", refreshControl: keyboardFormProps?.refreshControl ?? (_jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing })), style: [{ flex: 1, backgroundColor: theme.bg.page }, keyboardFormProps?.style], toolbar: keyboardToolbar, children: isCenteredContent ? (_jsx(_Fragment, { children: isLoading ? (_jsx(Feedback.Loading, { message: `Cargando ${name}...` })) : hasError ? (_jsx(Feedback.Error, { message: errorMessage, onBack: onBack, onRetry: onRetry })) : (_jsx(Feedback.Empty, {})) })) : (_jsx(View, { style: withMargin ? { paddingHorizontal: 16 } : undefined, children: children })) }));
76
- }
77
- /**
78
- * @deprecated Content already handles isEmpty/!item by rendering Feedback.Empty internally.
79
- * Use `<FormViewV2.Content>` directly — this wrapper is redundant.
80
- */
81
- function Form({ children }) {
82
- const { isEmpty, item } = useFormView();
83
- if (isEmpty || !item) {
84
- return _jsx(Feedback.Empty, {});
85
- }
86
- return _jsx(_Fragment, { children: children });
86
+ ], keyboardShouldPersistTaps: keyboardFormProps?.keyboardShouldPersistTaps ?? "handled", refreshControl: keyboardFormProps?.refreshControl ?? (_jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing })), stickyFooterHeight: stickyFooterHeight, style: [{ flex: 1, backgroundColor: theme.bg.page }, keyboardFormProps?.style], toolbar: keyboardToolbar, children: isCenteredContent ? (_jsx(_Fragment, { children: isLoading ? (_jsx(Feedback.Loading, { message: `Cargando ${name}...` })) : hasError ? (_jsx(Feedback.Error, { message: errorMessage, onBack: onBack, onRetry: onRetry })) : (_jsx(Feedback.Empty, {})) })) : (_jsx(View, { style: withMargin ? { paddingHorizontal: 16 } : undefined, children: children })) }));
87
87
  }
88
88
  /** Layout wrapper for a set of Group cards. Content already handles isEmpty/loading states. */
89
89
  function Groups({ children }) {
@@ -122,12 +122,17 @@ function Section({ title, footer, children, }) {
122
122
  }
123
123
  function Footer({ children }) {
124
124
  const { isEmpty, isLoading } = useFormView();
125
+ const { setHeight } = useFooterHeight();
126
+ const frame = React.useRef(null);
127
+ const { height: windowHeight } = useWindowDimensions();
128
+ const [bottomGap, setBottomGap] = React.useState(0);
129
+ React.useEffect(() => () => setHeight(0), [setHeight]);
125
130
  if (isEmpty || isLoading) {
126
131
  return null;
127
132
  }
128
- // Sticky bottom bar (lives outside the scroll). No shadow — a top hairline integrates it
129
- // with the form instead of reading as a detached floating card.
130
- return (_jsx(View, { className: "bg-bg-card px-4 py-3 border-t border-border-light", children: children }));
133
+ return (_jsx(View, { onLayout: () => {
134
+ frame.current?.measureInWindow((_x, y, _width, height) => setBottomGap(Math.max(0, windowHeight - y - height)));
135
+ }, ref: frame, children: _jsx(KeyboardStickyView, { offset: { closed: 0, opened: bottomGap }, children: _jsx(View, { className: "bg-bg-card px-4 py-3 border-t border-border-light", onLayout: (event) => setHeight(event.nativeEvent.layout.height), children: children }) }) }));
131
136
  }
132
137
  // Nested Header components
133
138
  const HeaderWithSubComponents = Object.assign(Header, {
@@ -137,7 +142,6 @@ const HeaderWithSubComponents = Object.assign(Header, {
137
142
  });
138
143
  // Nested Content components
139
144
  const ContentWithSubComponents = Object.assign(Content, {
140
- Form,
141
145
  Groups,
142
146
  Group,
143
147
  Section,
@@ -2,7 +2,7 @@ import { ReactNode } from "react";
2
2
  interface ListViewContextValue<T> {
3
3
  name: string;
4
4
  description?: string;
5
- items: T[];
5
+ items: readonly T[];
6
6
  isLoading: boolean;
7
7
  hasError: boolean;
8
8
  errorMessage?: string;
@@ -11,8 +11,8 @@ interface ListViewContextValue<T> {
11
11
  setShowRefreshing: (value: boolean) => void;
12
12
  onRefresh?: () => Promise<void>;
13
13
  onRetry?: () => void;
14
- onPressItem?: (item: T) => void;
15
- onDeleteItem?: (item: T) => void;
14
+ onPressItem?(item: T): void;
15
+ onDeleteItem?(item: T): void;
16
16
  onSearch?: (query: string) => void;
17
17
  onBack?: () => void;
18
18
  onLoadMore?: () => void;
@@ -22,7 +22,7 @@ interface ListViewContextValue<T> {
22
22
  export declare function ListViewProvider<T>({ name, description, items, isLoading, hasError, errorMessage, readonly, onRefresh, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore, hasMore, children, }: {
23
23
  name: string;
24
24
  description?: string;
25
- items: T[];
25
+ items: readonly T[];
26
26
  isLoading?: boolean;
27
27
  hasError?: boolean;
28
28
  errorMessage?: string;
@@ -38,5 +38,5 @@ export declare function ListViewProvider<T>({ name, description, items, isLoadin
38
38
  hasMore?: boolean;
39
39
  children: ReactNode;
40
40
  }): import("react").JSX.Element;
41
- export declare function useListView<T>(): ListViewContextValue<T>;
41
+ export declare function useListView<T = unknown>(): ListViewContextValue<T>;
42
42
  export {};
@@ -1,13 +1,15 @@
1
+ import { type FlashListProps, type FlashListRef } from "@shopify/flash-list";
1
2
  import { type SearchHistoryProps } from "../Display/Display.SearchHistory.js";
2
3
  import type { AppBarBackground } from "../Navigation/Navigation.AppBar.js";
3
4
  import { type SegmentedControlProps } from "../Navigation/Navigation.SegmentedControl.js";
4
- import { IRowItem } from "./types/IListView.js";
5
+ import { type IRowItem } from "./types/IListView.js";
5
6
  import React, { ReactNode, type Ref } from "react";
6
- import { FlatList, type FlatListProps, type ScrollViewProps, StyleProp, ViewStyle } from "react-native";
7
+ import { type ScrollViewProps, StyleProp, ViewStyle } from "react-native";
8
+ import type { Edge } from "react-native-safe-area-context";
7
9
  interface ListViewProps<T> {
8
10
  name: string;
9
11
  description?: string;
10
- items: T[];
12
+ items: readonly T[];
11
13
  isLoading?: boolean;
12
14
  hasError?: boolean;
13
15
  errorMessage?: string;
@@ -26,11 +28,14 @@ interface ListViewProps<T> {
26
28
  hasMore?: boolean;
27
29
  backgroundColor?: string;
28
30
  withContainer?: boolean;
31
+ edges?: Edge[];
29
32
  children: ReactNode;
30
33
  }
31
34
  export declare function refreshListData(onRefresh: (() => Promise<void>) | undefined, onRefreshError: ((error: unknown) => void) | undefined, setShowRefreshing: (value: boolean) => void): Promise<void>;
32
- declare function ListViewRoot<T>({ name, description, items, isLoading, hasError, errorMessage, readonly, onRefresh, onRefreshError, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore, hasMore, backgroundColor, withContainer, children, }: ListViewProps<T>): React.JSX.Element;
35
+ declare function ListViewRoot<T>({ name, description, items, isLoading, hasError, errorMessage, readonly, onRefresh, onRefreshError, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore, hasMore, backgroundColor, withContainer, edges, children, }: ListViewProps<T>): React.JSX.Element;
33
36
  interface HeaderProps {
37
+ /** Reserve the top inset; disable when the host already handles it. */
38
+ safeArea?: boolean;
34
39
  /** AppBar display variant. `large` = big title (list/index). `center` = modal/detail. */
35
40
  variant?: "default" | "large" | "center";
36
41
  /** Surface tone. `dark` = always-dark hero. `gradient` = brand accent gradient. */
@@ -50,7 +55,7 @@ interface HeaderProps {
50
55
  */
51
56
  topSpacing?: number;
52
57
  }
53
- declare function Header({ variant, tone, actions, children, background, topSpacing, }: HeaderProps): React.JSX.Element;
58
+ declare function Header({ safeArea, variant, tone, actions, children, background, topSpacing, }: HeaderProps): React.JSX.Element;
54
59
  declare function HeaderSearch({ searchQuery }: {
55
60
  searchQuery?: string;
56
61
  }): React.JSX.Element | null;
@@ -84,20 +89,20 @@ declare function FilterChips({ children }: {
84
89
  children: ReactNode;
85
90
  }): React.JSX.Element;
86
91
  type ListScrollProps = Pick<ScrollViewProps, "keyboardShouldPersistTaps" | "keyboardDismissMode" | "testID">;
87
- export type ListViewVirtualizationProps = Pick<FlatListProps<IRowItem<unknown>>, "initialNumToRender" | "maxToRenderPerBatch" | "windowSize" | "updateCellsBatchingPeriod" | "removeClippedSubviews" | "extraData" | "getItemLayout" | "onViewableItemsChanged" | "viewabilityConfig" | "onEndReachedThreshold" | "maintainVisibleContentPosition">;
88
- interface ListContentOptions {
89
- listProps?: ListViewVirtualizationProps;
90
- listRef?: Ref<FlatList<IRowItem<unknown>>>;
92
+ export type ListViewVirtualizationProps<T = unknown> = Pick<FlashListProps<IRowItem<T>>, "drawDistance" | "maxItemsInRecyclePool" | "getItemType" | "extraData" | "initialScrollIndex" | "initialScrollIndexParams" | "onViewableItemsChanged" | "viewabilityConfig" | "viewabilityConfigCallbackPairs" | "onEndReachedThreshold" | "maintainVisibleContentPosition">;
93
+ interface ListContentOptions<T> {
94
+ listProps?: ListViewVirtualizationProps<T>;
95
+ listRef?: Ref<FlashListRef<IRowItem<T>>>;
91
96
  contentContainerStyle?: StyleProp<ViewStyle>;
92
97
  }
93
- declare function Content({ children, withMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, listProps, listRef, contentContainerStyle, }: {
94
- children?: (item: any) => ReactNode;
95
- withMargin?: boolean;
96
- } & ListScrollProps & ListContentOptions): React.JSX.Element;
97
- declare function ContentList({ children, contentWithMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, listProps, listRef, contentContainerStyle, }: {
98
- children?: (item: any) => ReactNode;
98
+ declare function ContentList<T = unknown>({ children, contentWithMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, listProps, listRef, contentContainerStyle, }: {
99
+ children?: (item: T, row: IRowItem<T>) => ReactNode;
99
100
  contentWithMargin: boolean;
100
- } & ListScrollProps & ListContentOptions): React.JSX.Element;
101
+ } & ListScrollProps & ListContentOptions<T>): React.JSX.Element;
102
+ declare function Content<T = unknown>({ children, withMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, listProps, listRef, contentContainerStyle, }: {
103
+ children?: (item: T, row: IRowItem<T>) => ReactNode;
104
+ withMargin?: boolean;
105
+ } & ListScrollProps & ListContentOptions<T>): React.JSX.Element;
101
106
  declare function Footer({ children }: {
102
107
  children: ReactNode;
103
108
  }): React.JSX.Element | null;
@@ -153,7 +158,7 @@ interface RowActionButtonProps {
153
158
  declare function RowActionButton({ icon, onPress }: RowActionButtonProps): React.JSX.Element;
154
159
  /**
155
160
  * Screen container for a collection (list / index / search results).
156
- * Owns safe-area, the FlatList (pagination + pull-to-refresh), record count, and the
161
+ * Owns safe-area, the FlashList (pagination + pull-to-refresh), record count, and the
157
162
  * loading/empty/error states.
158
163
  *
159
164
  * API map:
@@ -164,7 +169,7 @@ declare function RowActionButton({ icon, onPress }: RowActionButtonProps): React
164
169
  * .Header.Segmented — tab-like switch in the subheader (Activos / Archivados)
165
170
  * .Header.Filters(.Chips/.Chip) — horizontal filter pills (SIBLING below the header)
166
171
  * .Header.ActionsBar — SIBLING bar below the header (bulk select, etc.)
167
- * ListViewV2.Content — FlatList body; `children` is an optional custom row renderer
172
+ * ListViewV2.Content — FlashList body; `children` is an optional custom row renderer
168
173
  * .Content.Row(.Avatar/.Content/.Title/.Subtitle/.Footer/.Actions/.ActionButton)
169
174
  * ListViewV2.Footer — pinned footer (hidden when empty)
170
175
  * ListViewV2.FloatingButton — absolute FAB overlay
@@ -1,4 +1,5 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "nativewind/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import { FlashList, } from "@shopify/flash-list";
2
3
  import { Display } from "../Display/index.js";
3
4
  import SearchHistory from "../Display/Display.SearchHistory.js";
4
5
  import { ErrorBoundary, Feedback } from "../Feedback/index.js";
@@ -12,7 +13,7 @@ import { Typography } from "../Typography/index.js";
12
13
  import { ListViewProvider, useListView } from "./ListViewV2.context.js";
13
14
  import { toRowItems } from "./types/IListView.js";
14
15
  import React, { createContext, useCallback, useContext, useEffect, useMemo, useState, } from "react";
15
- import { FlatList, RefreshControl, ScrollView, TouchableOpacity, View, } from "react-native";
16
+ import { RefreshControl, ScrollView, TouchableOpacity, View, } from "react-native";
16
17
  const RefreshErrorContext = createContext(undefined);
17
18
  export async function refreshListData(onRefresh, onRefreshError, setShowRefreshing) {
18
19
  setShowRefreshing(true);
@@ -32,17 +33,13 @@ export async function refreshListData(onRefresh, onRefreshError, setShowRefreshi
32
33
  setShowRefreshing(false);
33
34
  }
34
35
  }
35
- function ListViewRoot({ name, description, items, isLoading = false, hasError = false, errorMessage, readonly = false, onRefresh, onRefreshError, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore = false, hasMore = true, backgroundColor, withContainer = true, children, }) {
36
- const Wrapper = withContainer ? Container : View;
37
- const wrapperProps = withContainer
38
- ? { edges: ["bottom", "left", "right"] }
39
- : { className: "flex-1 bg-bg-page" };
40
- // Propagate contentWithMargin to ContentList via context
41
- return (_jsx(ListViewProvider, { description: description, errorMessage: errorMessage, hasError: hasError, hasMore: hasMore, isLoading: isLoading, items: items, loadingMore: loadingMore, name: name, onBack: onBack, onDeleteItem: onDeleteItem, onLoadMore: onLoadMore, onPressItem: onPressItem, onRefresh: onRefresh, onRetry: onRetry, onSearch: onSearch, readonly: readonly, children: _jsx(RefreshErrorContext.Provider, { value: onRefreshError, children: _jsx(ErrorBoundary, { children: _jsx(Wrapper, { ...wrapperProps, children: children }) }) }) }));
36
+ function ListViewRoot({ name, description, items, isLoading = false, hasError = false, errorMessage, readonly = false, onRefresh, onRefreshError, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore = false, hasMore = true, backgroundColor, withContainer = true, edges, children, }) {
37
+ const style = backgroundColor ? { backgroundColor } : undefined;
38
+ return (_jsx(ListViewProvider, { description: description, errorMessage: errorMessage, hasError: hasError, hasMore: hasMore, isLoading: isLoading, items: items, loadingMore: loadingMore, name: name, onBack: onBack, onDeleteItem: onDeleteItem, onLoadMore: onLoadMore, onPressItem: onPressItem, onRefresh: onRefresh, onRetry: onRetry, onSearch: onSearch, readonly: readonly, children: _jsx(RefreshErrorContext.Provider, { value: onRefreshError, children: _jsx(ErrorBoundary, { children: withContainer ? (_jsx(Container, { edges: edges, style: style, children: children })) : (_jsx(View, { className: "flex-1 bg-bg-page", style: style, children: children })) }) }) }));
42
39
  }
43
- function Header({ variant = "large", tone, actions, children, background, topSpacing, }) {
40
+ function Header({ safeArea = true, variant = "large", tone, actions, children, background, topSpacing, }) {
44
41
  const { name, description, onBack } = useListView();
45
- return (_jsx(Navigation.AppBar, { actions: actions, background: background, onBack: onBack, safeArea: false, subheader: children, subtitle: description ?? undefined, title: name, tone: tone, topSpacing: topSpacing, variant: variant }));
42
+ return (_jsx(Navigation.AppBar, { actions: actions, background: background, onBack: onBack, safeArea: safeArea, subheader: children, subtitle: description ?? undefined, title: name, tone: tone, topSpacing: topSpacing, variant: variant }));
46
43
  }
47
44
  function HeaderSearch({ searchQuery = "" }) {
48
45
  const { name, onSearch } = useListView();
@@ -118,8 +115,41 @@ function FilterChip({ label, active = false, onPress }) {
118
115
  function FilterChips({ children }) {
119
116
  return (_jsx(ScrollView, { contentContainerStyle: { paddingHorizontal: 16, gap: 8 }, horizontal: true, showsHorizontalScrollIndicator: false, style: { flexGrow: 0 }, children: children }));
120
117
  }
121
- function rowKey(item) {
122
- return String(item.id ?? item.index);
118
+ function rowKey(row) {
119
+ return String(row.id);
120
+ }
121
+ function ContentList({ children, contentWithMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, listProps, listRef, contentContainerStyle, }) {
122
+ const { items, onRefresh, showRefreshing, setShowRefreshing, onPressItem, onDeleteItem, readonly, onLoadMore, loadingMore, hasMore, } = useListView();
123
+ const bottomInset = useBottomInset();
124
+ const theme = useTheme();
125
+ const rowItems = useMemo(() => toRowItems(items), [items]);
126
+ const onRefreshError = useContext(RefreshErrorContext);
127
+ const handleRefresh = useCallback(async () => {
128
+ await refreshListData(onRefresh, onRefreshError, setShowRefreshing);
129
+ }, [onRefresh, onRefreshError, setShowRefreshing]);
130
+ const renderItem = useCallback(({ item: row }) => {
131
+ if (children) {
132
+ return _jsx(_Fragment, { children: children(row.item, row) });
133
+ }
134
+ const onPress = () => onPressItem?.(row.item);
135
+ const onDelete = () => onDeleteItem?.(row.item);
136
+ const isReadOnly = row.readonly || readonly || false;
137
+ return (_jsx(Navigation.RowItem, { item: row, onDelete: onDelete, onPress: onPress, readonly: isReadOnly }));
138
+ }, [children, onPressItem, onDeleteItem, readonly]);
139
+ return (_jsx(View, { style: {
140
+ flex: 1,
141
+ marginTop: 2,
142
+ paddingHorizontal: contentWithMargin ? 4 : 0,
143
+ backgroundColor: theme.bg.page,
144
+ }, children: _jsx(FlashList, { ...listProps, contentContainerStyle: [
145
+ bottomInset ? { paddingBottom: bottomInset } : undefined,
146
+ contentContainerStyle,
147
+ ], data: rowItems, keyboardDismissMode: keyboardDismissMode, keyboardShouldPersistTaps: keyboardShouldPersistTaps, keyExtractor: rowKey, ListFooterComponent: loadingMore ? (_jsx(View, { className: "py-4", children: _jsx(Feedback.Spinner, { size: "small" }) })) : undefined, maintainVisibleContentPosition: listProps?.maintainVisibleContentPosition ?? { disabled: true }, onEndReached: onLoadMore
148
+ ? () => {
149
+ if (hasMore && !loadingMore && !showRefreshing)
150
+ onLoadMore();
151
+ }
152
+ : undefined, onEndReachedThreshold: listProps?.onEndReachedThreshold ?? 0.5, ref: listRef, refreshControl: _jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing }), renderItem: renderItem, showsVerticalScrollIndicator: false, style: { flex: 1 }, testID: testID }) }));
123
153
  }
124
154
  function Content({ children, withMargin = true, keyboardShouldPersistTaps = "handled", keyboardDismissMode = "none", testID, listProps, listRef, contentContainerStyle, }) {
125
155
  const { name, items, isLoading, hasError, errorMessage, onRefresh, onRetry, onBack, showRefreshing, setShowRefreshing, } = useListView();
@@ -156,39 +186,6 @@ function Content({ children, withMargin = true, keyboardShouldPersistTaps = "han
156
186
  }
157
187
  return (_jsxs(View, { style: { flex: 1, backgroundColor: theme.bg.page }, children: [_jsx(Display.CountRecords, { count: items.length || 0, name: name }), _jsx(ContentList, { contentContainerStyle: contentContainerStyle, contentWithMargin: withMargin, keyboardDismissMode: keyboardDismissMode, keyboardShouldPersistTaps: keyboardShouldPersistTaps, listProps: listProps, listRef: listRef, testID: testID, children: children })] }));
158
188
  }
159
- function ContentList({ children, contentWithMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, listProps, listRef, contentContainerStyle, }) {
160
- const { items, onRefresh, showRefreshing, setShowRefreshing, onPressItem, onDeleteItem, readonly, onLoadMore, loadingMore, hasMore, } = useListView();
161
- const bottomInset = useBottomInset();
162
- const theme = useTheme();
163
- const rowItems = useMemo(() => toRowItems(items), [items]);
164
- const onRefreshError = useContext(RefreshErrorContext);
165
- const handleRefresh = useCallback(async () => {
166
- await refreshListData(onRefresh, onRefreshError, setShowRefreshing);
167
- }, [onRefresh, onRefreshError, setShowRefreshing]);
168
- const renderItem = useCallback(({ item }) => {
169
- if (children) {
170
- return _jsx(_Fragment, { children: children(item) });
171
- }
172
- const onPress = () => onPressItem?.(item);
173
- const onDelete = () => onDeleteItem?.(item);
174
- const isReadOnly = item.readonly || readonly || false;
175
- return (_jsx(Navigation.RowItem, { item: { ...item }, onDelete: onDelete, onPress: onPress, readonly: isReadOnly }));
176
- }, [children, onPressItem, onDeleteItem, readonly]);
177
- return (_jsx(View, { style: {
178
- flex: 1,
179
- marginTop: 2,
180
- paddingHorizontal: contentWithMargin ? 4 : 0,
181
- backgroundColor: theme.bg.page,
182
- }, children: _jsx(FlatList, { ...listProps, contentContainerStyle: [
183
- bottomInset ? { paddingBottom: bottomInset } : undefined,
184
- contentContainerStyle,
185
- ], data: rowItems, keyboardDismissMode: keyboardDismissMode, keyboardShouldPersistTaps: keyboardShouldPersistTaps, keyExtractor: rowKey, ListFooterComponent: loadingMore ? (_jsx(View, { className: "py-4", children: _jsx(Feedback.Spinner, { size: "small" }) })) : undefined, onEndReached: onLoadMore
186
- ? () => {
187
- if (hasMore && !loadingMore && !showRefreshing)
188
- onLoadMore();
189
- }
190
- : undefined, onEndReachedThreshold: listProps?.onEndReachedThreshold ?? 0.5, ref: listRef, refreshControl: _jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing }), renderItem: renderItem, scrollEnabled: true, showsVerticalScrollIndicator: false, testID: testID }) }));
191
- }
192
189
  function Footer({ children }) {
193
190
  const { items } = useListView();
194
191
  if (items.length === 0) {
@@ -259,7 +256,7 @@ const ContentWithSubComponents = Object.assign(Content, {
259
256
  });
260
257
  /**
261
258
  * Screen container for a collection (list / index / search results).
262
- * Owns safe-area, the FlatList (pagination + pull-to-refresh), record count, and the
259
+ * Owns safe-area, the FlashList (pagination + pull-to-refresh), record count, and the
263
260
  * loading/empty/error states.
264
261
  *
265
262
  * API map:
@@ -270,7 +267,7 @@ const ContentWithSubComponents = Object.assign(Content, {
270
267
  * .Header.Segmented — tab-like switch in the subheader (Activos / Archivados)
271
268
  * .Header.Filters(.Chips/.Chip) — horizontal filter pills (SIBLING below the header)
272
269
  * .Header.ActionsBar — SIBLING bar below the header (bulk select, etc.)
273
- * ListViewV2.Content — FlatList body; `children` is an optional custom row renderer
270
+ * ListViewV2.Content — FlashList body; `children` is an optional custom row renderer
274
271
  * .Content.Row(.Avatar/.Content/.Title/.Subtitle/.Footer/.Actions/.ActionButton)
275
272
  * ListViewV2.Footer — pinned footer (hidden when empty)
276
273
  * ListViewV2.FloatingButton — absolute FAB overlay
@@ -1,18 +1,19 @@
1
1
  import { UICollectionItem, UIItem } from "./UIItem.js";
2
2
  import { ReactNode } from "react";
3
- export type IRowItem<T> = T & UIItem & {
3
+ export type IRowItem<T> = UIItem & {
4
+ item: T;
4
5
  id: string | number;
5
6
  index: number;
6
7
  rowTitle: string;
7
8
  rowSubtitle?: string;
8
9
  };
9
10
  export interface IRowItemProps<T> extends UIItem {
10
- item: IRowItem<T> | T;
11
+ item: IRowItem<T>;
11
12
  }
12
13
  export interface IListView<T> extends UICollectionItem<T> {
13
14
  name: string;
14
15
  description?: string;
15
- items: T[];
16
+ items: readonly T[];
16
17
  isLoading?: boolean;
17
18
  onRefresh?: () => void | Promise<void>;
18
19
  onBack?: () => void | Promise<void>;
@@ -31,4 +32,4 @@ export type RowKeys<T> = Partial<{
31
32
  readOnly?: keyof T;
32
33
  }>;
33
34
  export declare function toRowItem<T>(item: T, index: number, mapKeys?: RowKeys<T>): IRowItem<T>;
34
- export declare function toRowItems<T>(items: T[], mapKeys?: RowKeys<T>): IRowItem<T>[];
35
+ export declare function toRowItems<T>(items: readonly T[], mapKeys?: RowKeys<T>): IRowItem<T>[];