@sincpro/mobile-ui 1.1.0 → 2.0.1

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 (129) hide show
  1. package/README.md +156 -29
  2. package/assets/brand-mesh.png +0 -0
  3. package/assets/fonts/FFL.txt +57 -0
  4. package/assets/fonts/Satoshi-Black.otf +0 -0
  5. package/assets/fonts/Satoshi-Bold.otf +0 -0
  6. package/assets/fonts/Satoshi-Medium.otf +0 -0
  7. package/assets/fonts/Satoshi-Regular.otf +0 -0
  8. package/dist/Actions/Actions.Button.d.ts +10 -12
  9. package/dist/Actions/Actions.Button.js +9 -11
  10. package/dist/Actions/Actions.Dock.d.ts +20 -0
  11. package/dist/Actions/Actions.Dock.js +89 -0
  12. package/dist/Actions/Actions.FAB.d.ts +1 -2
  13. package/dist/Actions/Actions.FAB.js +5 -7
  14. package/dist/Actions/Actions.IconButton.d.ts +1 -2
  15. package/dist/Actions/Actions.IconButton.js +2 -2
  16. package/dist/Actions/index.d.ts +4 -0
  17. package/dist/Actions/index.js +3 -0
  18. package/dist/Dialog/BottomSheet/BottomSheet.Root.js +22 -34
  19. package/dist/Dialog/Dialog.ActionSheet.js +2 -16
  20. package/dist/Dialog/Dialog.Confirmation.js +7 -5
  21. package/dist/Dialog/Dialog.EditValue.js +3 -1
  22. package/dist/Dialog/Dialog.Sheet.d.ts +3 -3
  23. package/dist/Dialog/Dialog.Sheet.js +18 -25
  24. package/dist/Display/Display.Accordion.js +7 -3
  25. package/dist/Display/Display.AmountSummary.d.ts +28 -0
  26. package/dist/Display/Display.AmountSummary.js +25 -0
  27. package/dist/Display/Display.Carousel.d.ts +27 -4
  28. package/dist/Display/Display.Carousel.js +112 -12
  29. package/dist/Display/Display.ExpoImage.d.ts +8 -0
  30. package/dist/Display/Display.ExpoImage.js +15 -0
  31. package/dist/Display/Display.Image.d.ts +10 -6
  32. package/dist/Display/Display.Image.js +39 -6
  33. package/dist/Display/Display.ListItem.d.ts +1 -2
  34. package/dist/Display/Display.ListItem.js +3 -3
  35. package/dist/Display/Display.Logo.d.ts +2 -2
  36. package/dist/Display/Display.Logo.js +3 -2
  37. package/dist/Display/Display.MediaCard.d.ts +2 -2
  38. package/dist/Display/Display.MediaEmbed.d.ts +2 -2
  39. package/dist/Display/Display.ProductCard.d.ts +2 -2
  40. package/dist/Display/Display.Story.d.ts +3 -3
  41. package/dist/Display/Display.Story.js +5 -4
  42. package/dist/Display/Display.StoryTray.d.ts +4 -4
  43. package/dist/Display/Display.StoryTray.js +3 -2
  44. package/dist/Display/Display.StoryViewer.d.ts +3 -3
  45. package/dist/Display/Display.StoryViewer.js +6 -3
  46. package/dist/Display/index.d.ts +9 -0
  47. package/dist/Display/index.js +4 -0
  48. package/dist/Feedback/Feedback.DomainSplashScreen.d.ts +13 -4
  49. package/dist/Feedback/Feedback.DomainSplashScreen.js +68 -53
  50. package/dist/Feedback/Feedback.EmptyState.d.ts +3 -4
  51. package/dist/Feedback/Feedback.EmptyState.js +4 -3
  52. package/dist/Feedback/Feedback.Error.d.ts +1 -2
  53. package/dist/Feedback/Feedback.Error.js +2 -2
  54. package/dist/Feedback/Feedback.Loading.d.ts +6 -2
  55. package/dist/Feedback/Feedback.Loading.js +2 -2
  56. package/dist/Feedback/Feedback.Progress.d.ts +6 -3
  57. package/dist/Feedback/Feedback.Progress.js +29 -19
  58. package/dist/Feedback/Feedback.ProgressCircle.d.ts +3 -2
  59. package/dist/Feedback/Feedback.ProgressCircle.js +10 -7
  60. package/dist/Feedback/Feedback.Skeleton.d.ts +4 -4
  61. package/dist/Feedback/Feedback.Skeleton.js +15 -3
  62. package/dist/Feedback/Feedback.Spinner.d.ts +3 -3
  63. package/dist/Feedback/Feedback.Spinner.js +40 -9
  64. package/dist/Feedback/Feedback.Splash.d.ts +2 -2
  65. package/dist/Feedback/Feedback.Splash.js +5 -3
  66. package/dist/Feedback/Toast.context.d.ts +64 -3
  67. package/dist/Feedback/Toast.context.js +112 -31
  68. package/dist/Feedback/index.d.ts +15 -1
  69. package/dist/Feedback/index.js +4 -1
  70. package/dist/Form/Form.Checkbox.d.ts +1 -2
  71. package/dist/Form/Form.Checkbox.js +2 -2
  72. package/dist/Form/Form.DatePicker.d.ts +1 -2
  73. package/dist/Form/Form.DatePicker.js +2 -2
  74. package/dist/Form/Form.Dropdown.d.ts +15 -3
  75. package/dist/Form/Form.Dropdown.js +21 -6
  76. package/dist/Form/Form.ErrorSummary.d.ts +23 -0
  77. package/dist/Form/Form.ErrorSummary.js +53 -0
  78. package/dist/Form/Form.Input.d.ts +1 -2
  79. package/dist/Form/Form.Input.js +2 -2
  80. package/dist/Form/Form.Select.d.ts +1 -2
  81. package/dist/Form/Form.Select.js +6 -19
  82. package/dist/Form/index.d.ts +3 -0
  83. package/dist/Form/index.js +2 -0
  84. package/dist/Navigation/Navigation.AppBar.d.ts +2 -6
  85. package/dist/Navigation/Navigation.AppBar.js +9 -7
  86. package/dist/Navigation/Navigation.BottomNav.d.ts +1 -3
  87. package/dist/Navigation/Navigation.BottomNav.js +7 -6
  88. package/dist/Navigation/Navigation.Header.d.ts +2 -2
  89. package/dist/Navigation/Navigation.Menu.js +10 -3
  90. package/dist/Navigation/Navigation.Search.d.ts +41 -0
  91. package/dist/Navigation/Navigation.Search.js +47 -0
  92. package/dist/Navigation/Navigation.SearchBar.d.ts +1 -2
  93. package/dist/Navigation/Navigation.SearchBar.js +16 -19
  94. package/dist/Navigation/Navigation.SegmentedControl.d.ts +11 -3
  95. package/dist/Navigation/Navigation.SegmentedControl.js +29 -16
  96. package/dist/Navigation/index.d.ts +11 -2
  97. package/dist/Navigation/index.js +2 -0
  98. package/dist/branding.d.ts +3 -2
  99. package/dist/primitives/BaseButton.d.ts +10 -12
  100. package/dist/primitives/BaseButton.js +9 -11
  101. package/dist/theme/typography.d.ts +1 -1
  102. package/dist/utils/image.d.ts +3 -0
  103. package/dist/utils/image.js +13 -0
  104. package/dist/utils/keys.d.ts +1 -0
  105. package/dist/utils/keys.js +7 -0
  106. package/dist/utils/progress.d.ts +1 -0
  107. package/dist/utils/progress.js +5 -0
  108. package/dist/utils/reorder.js +2 -5
  109. package/dist/utils/useReducedMotion.d.ts +1 -0
  110. package/dist/utils/useReducedMotion.js +43 -0
  111. package/dist/utils/useSheetTranslation.d.ts +5 -0
  112. package/dist/utils/useSheetTranslation.js +23 -0
  113. package/dist/views/AppSplashView.d.ts +3 -2
  114. package/dist/views/AppSplashView.js +3 -2
  115. package/dist/views/AuthFormView.d.ts +2 -2
  116. package/dist/views/FormViewV2.d.ts +2 -2
  117. package/dist/views/ListViewV2.context.d.ts +3 -1
  118. package/dist/views/ListViewV2.context.js +41 -21
  119. package/dist/views/ListViewV2.d.ts +15 -7
  120. package/dist/views/ListViewV2.js +44 -23
  121. package/dist/views/types/IListView.js +6 -5
  122. package/dist/widgets/CollectionOptions.js +9 -7
  123. package/dist/widgets/FilterBar.d.ts +1 -1
  124. package/dist/widgets/FilterBar.js +1 -1
  125. package/dist/widgets/FilterBuilder.js +7 -7
  126. package/dist/widgets/HomeHeader.d.ts +2 -2
  127. package/dist/widgets/PromoModal.js +3 -1
  128. package/dist/widgets/ScreenHeader.d.ts +2 -2
  129. package/package.json +5 -1
@@ -1,3 +1,4 @@
1
+ import type { ImageSource } from "./utils/image.js";
1
2
  import { type ImageSourcePropType } from "react-native";
2
3
  /**
3
4
  * Centralized brand assets. Set once at app boot via `setBranding({ ... })`. Components
@@ -14,14 +15,14 @@ import { type ImageSourcePropType } from "react-native";
14
15
  */
15
16
  export interface BrandingConfig {
16
17
  /** Brand logo (used by `Display.Logo`, `widgets.ScreenHeader`, etc.). */
17
- logo?: ImageSourcePropType;
18
+ logo?: ImageSource;
18
19
  /** Brand symbol / icon used by `Display.Pattern kind="mesh"` as the tiled texture. */
19
20
  mesh?: ImageSourcePropType;
20
21
  /** Tile size in px (overrides `theme.pattern.size` for the brand mesh). */
21
22
  meshSize?: number;
22
23
  }
23
24
  export declare function setBranding(config: BrandingConfig): void;
24
- export declare function getBrandingLogo(): ImageSourcePropType | undefined;
25
+ export declare function getBrandingLogo(): ImageSource | undefined;
25
26
  export declare function getBrandingMesh(): ImageSourcePropType | undefined;
26
27
  export declare function getBrandingMeshSize(): number | undefined;
27
28
  /** Test-only — reset to initial empty state. Not part of the public API. */
@@ -11,18 +11,18 @@ declare const button: import("tailwind-variants").TVReturnType<{
11
11
  outlineDanger: "bg-transparent border border-danger";
12
12
  outlineBlack: "bg-transparent border border-border-strong";
13
13
  flat: "bg-text-primary/10";
14
- link: "bg-transparent min-h-0";
14
+ link: "bg-transparent";
15
15
  transparent: "bg-transparent border border-border-light";
16
16
  };
17
17
  size: {
18
- small: "py-1.5 px-3 min-h-[36px]";
19
- medium: "py-2.5 px-4 min-h-[44px]";
18
+ small: "py-1.5 px-3 min-h-[48px]";
19
+ medium: "py-2.5 px-4 min-h-[48px]";
20
20
  large: "py-3 px-5 min-h-[48px]";
21
21
  };
22
22
  disabled: {
23
23
  true: "opacity-70";
24
24
  };
25
- }, undefined, "rounded-md items-center justify-center flex-row min-h-[44px]", {
25
+ }, undefined, "rounded-md items-center justify-center flex-row min-h-[48px]", {
26
26
  variant: {
27
27
  primary: "bg-accent";
28
28
  accent: "bg-transparent";
@@ -31,12 +31,12 @@ declare const button: import("tailwind-variants").TVReturnType<{
31
31
  outlineDanger: "bg-transparent border border-danger";
32
32
  outlineBlack: "bg-transparent border border-border-strong";
33
33
  flat: "bg-text-primary/10";
34
- link: "bg-transparent min-h-0";
34
+ link: "bg-transparent";
35
35
  transparent: "bg-transparent border border-border-light";
36
36
  };
37
37
  size: {
38
- small: "py-1.5 px-3 min-h-[36px]";
39
- medium: "py-2.5 px-4 min-h-[44px]";
38
+ small: "py-1.5 px-3 min-h-[48px]";
39
+ medium: "py-2.5 px-4 min-h-[48px]";
40
40
  large: "py-3 px-5 min-h-[48px]";
41
41
  };
42
42
  disabled: {
@@ -51,12 +51,12 @@ declare const button: import("tailwind-variants").TVReturnType<{
51
51
  outlineDanger: "bg-transparent border border-danger";
52
52
  outlineBlack: "bg-transparent border border-border-strong";
53
53
  flat: "bg-text-primary/10";
54
- link: "bg-transparent min-h-0";
54
+ link: "bg-transparent";
55
55
  transparent: "bg-transparent border border-border-light";
56
56
  };
57
57
  size: {
58
- small: "py-1.5 px-3 min-h-[36px]";
59
- medium: "py-2.5 px-4 min-h-[44px]";
58
+ small: "py-1.5 px-3 min-h-[48px]";
59
+ medium: "py-2.5 px-4 min-h-[48px]";
60
60
  large: "py-3 px-5 min-h-[48px]";
61
61
  };
62
62
  disabled: {
@@ -131,7 +131,6 @@ export interface BaseButtonProps extends VariantProps<typeof button>, Accessibil
131
131
  disabled?: boolean;
132
132
  className?: string;
133
133
  icon?: React.ReactNode;
134
- accessibleSizing?: boolean;
135
134
  testID?: string;
136
135
  }
137
136
  export declare const BaseButton: FC<BaseButtonProps>;
@@ -141,7 +140,6 @@ export interface BaseButtonIconProps extends VariantProps<typeof buttonIcon>, Ac
141
140
  loading?: boolean;
142
141
  disabled?: boolean;
143
142
  className?: string;
144
- accessibleSizing?: boolean;
145
143
  testID?: string;
146
144
  }
147
145
  export declare const BaseButtonIcon: FC<BaseButtonIconProps>;
@@ -8,7 +8,7 @@ import { ActivityIndicator, TouchableOpacity, View } from "react-native";
8
8
  // STYLE VARIANTS
9
9
  // ============================================================================
10
10
  const button = tv({
11
- base: "rounded-md items-center justify-center flex-row min-h-[44px]",
11
+ base: "rounded-md items-center justify-center flex-row min-h-[48px]",
12
12
  variants: {
13
13
  variant: {
14
14
  primary: "bg-accent",
@@ -18,12 +18,12 @@ const button = tv({
18
18
  outlineDanger: "bg-transparent border border-danger",
19
19
  outlineBlack: "bg-transparent border border-border-strong",
20
20
  flat: "bg-text-primary/10",
21
- link: "bg-transparent min-h-0",
21
+ link: "bg-transparent",
22
22
  transparent: "bg-transparent border border-border-light",
23
23
  },
24
24
  size: {
25
- small: "py-1.5 px-3 min-h-[36px]",
26
- medium: "py-2.5 px-4 min-h-[44px]",
25
+ small: "py-1.5 px-3 min-h-[48px]",
26
+ medium: "py-2.5 px-4 min-h-[48px]",
27
27
  large: "py-3 px-5 min-h-[48px]",
28
28
  },
29
29
  disabled: {
@@ -192,7 +192,7 @@ function getIconSize(size) {
192
192
  function getTextVariant(size) {
193
193
  return textVariantMap[size] ?? "button";
194
194
  }
195
- export const BaseButton = ({ title, onPress, variant = "primary", size = "medium", loading = false, disabled = false, className, icon, accessibleSizing = false, testID, accessibilityLabel, accessibilityState, ...accessibilityProps }) => {
195
+ export const BaseButton = ({ title, onPress, variant = "primary", size = "medium", loading = false, disabled = false, className, icon, testID, accessibilityLabel, accessibilityState, ...accessibilityProps }) => {
196
196
  const textColorClass = textColor({
197
197
  variant: variant ?? "primary",
198
198
  disabled,
@@ -204,17 +204,15 @@ export const BaseButton = ({ title, onPress, variant = "primary", size = "medium
204
204
  ...accessibilityState,
205
205
  disabled: disabled || loading,
206
206
  busy: loading,
207
- }, style: accessibleSizing
208
- ? { minHeight: 48, minWidth: 48, justifyContent: "center" }
209
- : undefined, testID: testID, children: loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsx(Typography.Text, { className: textColorClass, variant: textVariantValue, children: title })) }));
207
+ }, style: { minHeight: 48, minWidth: 48, justifyContent: "center" }, testID: testID, children: loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsx(Typography.Text, { allowFontScaling: true, className: textColorClass, style: { flexShrink: 1 }, variant: textVariantValue, children: title })) }));
210
208
  }
211
209
  return (_jsxs(TouchableOpacity, { activeOpacity: 0.8, className: button({ variant, size, disabled, className }), disabled: disabled || loading, onPress: onPress, ...accessibilityProps, accessibilityLabel: accessibilityLabel ?? title, accessibilityRole: "button", accessibilityState: {
212
210
  ...accessibilityState,
213
211
  disabled: disabled || loading,
214
212
  busy: loading,
215
- }, style: accessibleSizing ? { minHeight: 48, minWidth: 48 } : undefined, testID: testID, children: [icon && !loading && _jsx(View, { className: "mr-2", children: icon }), loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsx(Typography.Text, { className: textColorClass, semibold: variant === "primary", variant: textVariantValue, children: title }))] }));
213
+ }, style: { minHeight: 48, minWidth: 48 }, testID: testID, children: [icon && !loading && _jsx(View, { className: "mr-2", children: icon }), loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsx(Typography.Text, { allowFontScaling: true, className: textColorClass, semibold: variant === "primary", style: { flexShrink: 1 }, variant: textVariantValue, children: title }))] }));
216
214
  };
217
- export const BaseButtonIcon = ({ icon, onPress, variant = "primary", size = "medium", loading = false, disabled = false, className, accessibleSizing = false, testID, accessibilityState, ...accessibilityProps }) => {
215
+ export const BaseButtonIcon = ({ icon, onPress, variant = "primary", size = "medium", loading = false, disabled = false, className, testID, accessibilityState, ...accessibilityProps }) => {
218
216
  const iconColorValue = getIconColor(variant ?? "primary", disabled);
219
217
  const loadingColorValue = getLoadingColor(variant ?? "primary");
220
218
  const iconSizeValue = getIconSize(size ?? "medium");
@@ -222,7 +220,7 @@ export const BaseButtonIcon = ({ icon, onPress, variant = "primary", size = "med
222
220
  ...accessibilityState,
223
221
  disabled: disabled || loading,
224
222
  busy: loading,
225
- }, style: accessibleSizing ? { minHeight: 48, minWidth: 48 } : undefined, testID: testID, children: loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsx(Icon, { color: iconColorValue, name: icon, size: iconSizeValue })) }));
223
+ }, style: { minHeight: 48, minWidth: 48 }, testID: testID, children: loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsx(Icon, { color: iconColorValue, name: icon, size: iconSizeValue })) }));
226
224
  };
227
225
  // ============================================================================
228
226
  // COMPOUND COMPONENT
@@ -436,7 +436,7 @@ export declare const Typography: {
436
436
  readonly letterSpacing: 0;
437
437
  };
438
438
  };
439
- readonly variantFontRole: Record<"label" | "button" | "title" | "display" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "bodyReadable" | "bodyLarge" | "body" | "bodySmall" | "labelSmall" | "data" | "dataLarge" | "overline" | "caption" | "captionSmall" | "buttonSmall" | "buttonLarge" | "subtitle", FontRole>;
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
440
  readonly createTextStyle: (variant: keyof typeof typographyVariants, overrides?: Partial<TextStyle>) => TextStyle;
441
441
  readonly createCustomTextStyle: (fontSize: keyof typeof fontSizes, fontFamily: keyof typeof fontFamilies, options?: {
442
442
  lineHeight?: keyof typeof lineHeights;
@@ -0,0 +1,3 @@
1
+ import type { ImageProps } from "expo-image";
2
+ export type ImageSource = NonNullable<ImageProps["source"]>;
3
+ export declare function imageSourceKey(source: ImageProps["source"]): string | undefined;
@@ -0,0 +1,13 @@
1
+ const sharedSourceKeys = new WeakMap();
2
+ let nextSharedSourceKey = 0;
3
+ export function imageSourceKey(source) {
4
+ if (source && typeof source === "object" && "nativeRefType" in source) {
5
+ let key = sharedSourceKeys.get(source);
6
+ if (key === undefined) {
7
+ key = nextSharedSourceKey++;
8
+ sharedSourceKeys.set(source, key);
9
+ }
10
+ return `shared-image-${key}`;
11
+ }
12
+ return JSON.stringify(source);
13
+ }
@@ -0,0 +1 @@
1
+ export declare function uniqueKeys<T>(items: readonly T[], keyExtractor: (item: T, index: number) => string, component: string): string[];
@@ -0,0 +1,7 @@
1
+ export function uniqueKeys(items, keyExtractor, component) {
2
+ const keys = items.map(keyExtractor);
3
+ if (keys.some((key) => typeof key !== "string" || !key.trim()) ||
4
+ new Set(keys).size !== keys.length)
5
+ throw new Error(`${component} requires unique, non-empty string keys.`);
6
+ return keys;
7
+ }
@@ -0,0 +1 @@
1
+ export declare function normalizeProgress(value: number): number;
@@ -0,0 +1,5 @@
1
+ export function normalizeProgress(value) {
2
+ if (!Number.isFinite(value))
3
+ throw new RangeError("Progress value must be a finite number");
4
+ return Math.max(0, Math.min(1, value));
5
+ }
@@ -1,9 +1,6 @@
1
+ import { uniqueKeys } from "./keys.js";
1
2
  export function reorderKeys(items, keyExtractor) {
2
- const keys = items.map(keyExtractor);
3
- if (keys.some((key) => typeof key !== "string" || !key.trim()) ||
4
- new Set(keys).size !== keys.length)
5
- throw new Error("ReorderableList requires unique, non-empty string keys.");
6
- return keys;
3
+ return uniqueKeys(items, keyExtractor, "ReorderableList");
7
4
  }
8
5
  export function sameOrder(left, right) {
9
6
  return left.length === right.length && left.every((key, index) => key === right[index]);
@@ -0,0 +1 @@
1
+ export declare function useReducedMotion(): boolean;
@@ -0,0 +1,43 @@
1
+ import { useSyncExternalStore } from "react";
2
+ import { AccessibilityInfo } from "react-native";
3
+ const listeners = new Set();
4
+ let reducedMotion = true;
5
+ let generation = 0;
6
+ let subscription;
7
+ function publish(enabled) {
8
+ if (reducedMotion === enabled)
9
+ return;
10
+ reducedMotion = enabled;
11
+ for (const listener of [...listeners])
12
+ listener();
13
+ }
14
+ function subscribe(listener) {
15
+ listeners.add(listener);
16
+ if (listeners.size === 1) {
17
+ const request = ++generation;
18
+ let receivedChange = false;
19
+ subscription = AccessibilityInfo.addEventListener("reduceMotionChanged", (enabled) => {
20
+ receivedChange = true;
21
+ publish(enabled);
22
+ });
23
+ AccessibilityInfo.isReduceMotionEnabled().then((enabled) => {
24
+ if (request === generation && !receivedChange)
25
+ publish(enabled);
26
+ }, (error) => {
27
+ if (request === generation)
28
+ console.warn("[useReducedMotion] Could not read accessibility preference", error);
29
+ });
30
+ }
31
+ return () => {
32
+ listeners.delete(listener);
33
+ if (listeners.size === 0) {
34
+ generation += 1;
35
+ subscription?.remove();
36
+ subscription = undefined;
37
+ reducedMotion = true;
38
+ }
39
+ };
40
+ }
41
+ export function useReducedMotion() {
42
+ return useSyncExternalStore(subscribe, () => reducedMotion, () => true);
43
+ }
@@ -0,0 +1,5 @@
1
+ import { Animated } from "react-native";
2
+ export declare function useSheetTranslation(visible: boolean, hiddenOffset?: number): {
3
+ translateY: Animated.Value;
4
+ reducedMotion: boolean;
5
+ };
@@ -0,0 +1,23 @@
1
+ import { useReducedMotion } from "./useReducedMotion.js";
2
+ import { useEffect, useState } from "react";
3
+ import { Animated } from "react-native";
4
+ export function useSheetTranslation(visible, hiddenOffset = 800) {
5
+ const reducedMotion = useReducedMotion();
6
+ const [translateY] = useState(() => new Animated.Value(visible ? 0 : hiddenOffset));
7
+ useEffect(() => {
8
+ translateY.stopAnimation();
9
+ if (visible && !reducedMotion) {
10
+ const animation = Animated.spring(translateY, {
11
+ toValue: 0,
12
+ useNativeDriver: true,
13
+ damping: 20,
14
+ stiffness: 300,
15
+ });
16
+ animation.start();
17
+ return () => animation.stop();
18
+ }
19
+ translateY.setValue(visible ? 0 : hiddenOffset);
20
+ return () => translateY.stopAnimation();
21
+ }, [visible, hiddenOffset, reducedMotion, translateY]);
22
+ return { translateY, reducedMotion };
23
+ }
@@ -1,12 +1,13 @@
1
1
  import { type SpinnerVariant } from "../Feedback/Feedback.Spinner.js";
2
2
  import type { AppBarBackground } from "../Navigation/Navigation.AppBar.js";
3
+ import type { ImageSource } from "../utils/image.js";
3
4
  import type { ReactNode } from "react";
4
- import { type ImageSourcePropType, type ViewStyle } from "react-native";
5
+ import { type ViewStyle } from "react-native";
5
6
  export interface AppSplashViewProps {
6
7
  /** Gradient + pattern background. Defaults to `night-green` preset. */
7
8
  background?: AppBarBackground;
8
9
  /** App logo shown centered above the loading indicator. */
9
- logo?: ImageSourcePropType;
10
+ logo?: ImageSource;
10
11
  /** Built-in loading animation variant. Ignored when `loader` is provided. Defaults to `"dots"`. */
11
12
  loadingVariant?: SpinnerVariant;
12
13
  /** Fully custom loading element. Takes precedence over `loadingVariant`. */
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import ExpoImage from "../Display/Display.ExpoImage.js";
2
3
  import GradientSurface from "../Display/Display.GradientSurface.js";
3
4
  import { Pattern } from "../Display/Display.Pattern.js";
4
5
  import Spinner from "../Feedback/Feedback.Spinner.js";
5
- import { Image, View } from "react-native";
6
+ import { View } from "react-native";
6
7
  export function AppSplashView({ background, logo, loadingVariant = "dots", loader, style, }) {
7
- return (_jsxs(GradientSurface, { className: "flex-1", colors: background?.colors, end: background?.end, padding: "none", preset: background?.surface ?? "night-green", radius: "none", start: background?.start, style: { flex: 1, ...style }, children: [background?.pattern ? (_jsx(Pattern, { color: background.patternColor ?? "#FFFFFF", kind: background.pattern, opacity: background.patternOpacity ?? 0.18 })) : null, _jsxs(View, { style: { flex: 1, alignItems: "center", justifyContent: "center" }, children: [logo ? (_jsx(Image, { source: logo, style: { width: 140, height: 140, resizeMode: "contain", marginBottom: 40 } })) : null, loader ?? (_jsx(Spinner, { color: "rgba(255,255,255,0.9)", size: "large", variant: loadingVariant }))] })] }));
8
+ return (_jsxs(GradientSurface, { className: "flex-1", colors: background?.colors, end: background?.end, padding: "none", preset: background?.surface ?? "night-green", radius: "none", start: background?.start, style: { flex: 1, ...style }, children: [background?.pattern ? (_jsx(Pattern, { color: background.patternColor ?? "#FFFFFF", kind: background.pattern, opacity: background.patternOpacity ?? 0.18 })) : null, _jsxs(View, { style: { flex: 1, alignItems: "center", justifyContent: "center" }, children: [logo ? (_jsx(ExpoImage, { contentFit: "contain", source: logo, style: { width: 140, height: 140, marginBottom: 40 } })) : null, loader ?? (_jsx(Spinner, { color: "rgba(255,255,255,0.9)", size: "large", variant: loadingVariant }))] })] }));
8
9
  }
9
10
  export default AppSplashView;
@@ -1,11 +1,11 @@
1
1
  import type { KeyboardFormProps } from "../Form/Form.KeyboardForm.js";
2
2
  import type { AppBarBackground } from "../Navigation/Navigation.AppBar.js";
3
+ import type { ImageSource } from "../utils/image.js";
3
4
  import { ReactNode } from "react";
4
- import { type ImageSourcePropType } from "react-native";
5
5
  interface AuthFormProps {
6
6
  title?: string;
7
7
  description?: string;
8
- logoSource?: ImageSourcePropType;
8
+ logoSource?: ImageSource;
9
9
  onBack?: () => void;
10
10
  FormNode: ReactNode;
11
11
  background?: AppBarBackground;
@@ -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";
4
5
  import React, { ReactNode } from "react";
5
- import { type ImageSourcePropType } from "react-native";
6
6
  interface FormViewProps<T> {
7
7
  name: string;
8
8
  description?: string;
@@ -41,7 +41,7 @@ interface HeaderProps {
41
41
  */
42
42
  background?: AppBarBackground;
43
43
  /** @deprecated Pass `variant` prop instead. */
44
- logoSource?: ImageSourcePropType;
44
+ logoSource?: ImageSource;
45
45
  }
46
46
  declare function Header({ variant, tone, actions, children, topSpacing, background, }: HeaderProps): React.JSX.Element;
47
47
  declare function HeaderActions({ children }: {
@@ -17,8 +17,9 @@ interface ListViewContextValue<T> {
17
17
  onBack?: () => void;
18
18
  onLoadMore?: () => void;
19
19
  loadingMore?: boolean;
20
+ hasMore: boolean;
20
21
  }
21
- export declare function ListViewProvider<T>({ name, description, items, isLoading, hasError, errorMessage, readonly, onRefresh, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore, children, }: {
22
+ export declare function ListViewProvider<T>({ name, description, items, isLoading, hasError, errorMessage, readonly, onRefresh, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore, hasMore, children, }: {
22
23
  name: string;
23
24
  description?: string;
24
25
  items: T[];
@@ -34,6 +35,7 @@ export declare function ListViewProvider<T>({ name, description, items, isLoadin
34
35
  onBack?: () => void;
35
36
  onLoadMore?: () => void;
36
37
  loadingMore?: boolean;
38
+ hasMore?: boolean;
37
39
  children: ReactNode;
38
40
  }): import("react").JSX.Element;
39
41
  export declare function useListView<T>(): ListViewContextValue<T>;
@@ -1,27 +1,47 @@
1
1
  import { jsx as _jsx } from "nativewind/jsx-runtime";
2
- import { createContext, useContext, useState } from "react";
2
+ import { createContext, useContext, useMemo, useState } from "react";
3
3
  const Context = createContext(null);
4
- export function ListViewProvider({ name, description, items, isLoading = false, hasError = false, errorMessage, readonly = false, onRefresh, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore = false, children, }) {
4
+ export function ListViewProvider({ name, description, items, isLoading = false, hasError = false, errorMessage, readonly = false, onRefresh, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore = false, hasMore = true, children, }) {
5
5
  const [showRefreshing, setShowRefreshing] = useState(false);
6
- return (_jsx(Context.Provider, { value: {
7
- name,
8
- description,
9
- items,
10
- isLoading,
11
- hasError,
12
- errorMessage,
13
- readonly,
14
- showRefreshing,
15
- setShowRefreshing,
16
- onRefresh,
17
- onRetry,
18
- onPressItem,
19
- onDeleteItem,
20
- onSearch,
21
- onBack,
22
- onLoadMore,
23
- loadingMore,
24
- }, children: children }));
6
+ const value = useMemo(() => ({
7
+ name,
8
+ description,
9
+ items,
10
+ isLoading,
11
+ hasError,
12
+ errorMessage,
13
+ readonly,
14
+ showRefreshing,
15
+ setShowRefreshing,
16
+ onRefresh,
17
+ onRetry,
18
+ onPressItem,
19
+ onDeleteItem,
20
+ onSearch,
21
+ onBack,
22
+ onLoadMore,
23
+ loadingMore,
24
+ hasMore,
25
+ }), [
26
+ name,
27
+ description,
28
+ items,
29
+ isLoading,
30
+ hasError,
31
+ errorMessage,
32
+ readonly,
33
+ showRefreshing,
34
+ onRefresh,
35
+ onRetry,
36
+ onPressItem,
37
+ onDeleteItem,
38
+ onSearch,
39
+ onBack,
40
+ onLoadMore,
41
+ loadingMore,
42
+ hasMore,
43
+ ]);
44
+ return _jsx(Context.Provider, { value: value, children: children });
25
45
  }
26
46
  export function useListView() {
27
47
  const context = useContext(Context);
@@ -1,8 +1,9 @@
1
1
  import { type SearchHistoryProps } from "../Display/Display.SearchHistory.js";
2
2
  import type { AppBarBackground } from "../Navigation/Navigation.AppBar.js";
3
3
  import { type SegmentedControlProps } from "../Navigation/Navigation.SegmentedControl.js";
4
- import React, { ReactNode } from "react";
5
- import { type ScrollViewProps, StyleProp, ViewStyle } from "react-native";
4
+ import { IRowItem } from "./types/IListView.js";
5
+ import React, { ReactNode, type Ref } from "react";
6
+ import { FlatList, type FlatListProps, type ScrollViewProps, StyleProp, ViewStyle } from "react-native";
6
7
  interface ListViewProps<T> {
7
8
  name: string;
8
9
  description?: string;
@@ -22,12 +23,13 @@ interface ListViewProps<T> {
22
23
  onLoadMore?: () => void;
23
24
  /** Show a footer spinner while the next page loads. */
24
25
  loadingMore?: boolean;
26
+ hasMore?: boolean;
25
27
  backgroundColor?: string;
26
28
  withContainer?: boolean;
27
29
  children: ReactNode;
28
30
  }
29
31
  export declare function refreshListData(onRefresh: (() => Promise<void>) | undefined, onRefreshError: ((error: unknown) => void) | undefined, setShowRefreshing: (value: boolean) => void): Promise<void>;
30
- declare function ListViewRoot<T>({ name, description, items, isLoading, hasError, errorMessage, readonly, onRefresh, onRefreshError, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore, backgroundColor, withContainer, children, }: ListViewProps<T>): React.JSX.Element;
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;
31
33
  interface HeaderProps {
32
34
  /** AppBar display variant. `large` = big title (list/index). `center` = modal/detail. */
33
35
  variant?: "default" | "large" | "center";
@@ -82,14 +84,20 @@ declare function FilterChips({ children }: {
82
84
  children: ReactNode;
83
85
  }): React.JSX.Element;
84
86
  type ListScrollProps = Pick<ScrollViewProps, "keyboardShouldPersistTaps" | "keyboardDismissMode" | "testID">;
85
- declare function Content({ children, withMargin, 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>>>;
91
+ contentContainerStyle?: StyleProp<ViewStyle>;
92
+ }
93
+ declare function Content({ children, withMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, listProps, listRef, contentContainerStyle, }: {
86
94
  children?: (item: any) => ReactNode;
87
95
  withMargin?: boolean;
88
- } & ListScrollProps): React.JSX.Element;
89
- declare function ContentList({ children, contentWithMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, }: {
96
+ } & ListScrollProps & ListContentOptions): React.JSX.Element;
97
+ declare function ContentList({ children, contentWithMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, listProps, listRef, contentContainerStyle, }: {
90
98
  children?: (item: any) => ReactNode;
91
99
  contentWithMargin: boolean;
92
- } & ListScrollProps): React.JSX.Element;
100
+ } & ListScrollProps & ListContentOptions): React.JSX.Element;
93
101
  declare function Footer({ children }: {
94
102
  children: ReactNode;
95
103
  }): React.JSX.Element | null;