@butternutbox/pawprint-native 0.0.1 → 0.1.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 (182) hide show
  1. package/.turbo/turbo-build.log +15 -15
  2. package/CHANGELOG.md +16 -0
  3. package/COMPONENT_GUIDELINES.md +111 -4
  4. package/dist/index.cjs +12370 -1455
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +1110 -11
  7. package/dist/index.d.ts +1110 -11
  8. package/dist/index.js +12324 -1455
  9. package/dist/index.js.map +1 -1
  10. package/package.json +28 -9
  11. package/src/__mocks__/asset-stub.ts +1 -0
  12. package/src/__mocks__/emotion-native.tsx +18 -0
  13. package/src/__mocks__/react-native-gesture-handler.tsx +41 -0
  14. package/src/__mocks__/react-native-reanimated.tsx +79 -0
  15. package/src/__mocks__/react-native-safe-area-context.tsx +6 -0
  16. package/src/__mocks__/react-native-svg.tsx +27 -0
  17. package/src/__mocks__/react-native-worklets.tsx +11 -0
  18. package/src/__mocks__/react-native.tsx +338 -0
  19. package/src/__mocks__/rn-primitives/avatar.tsx +24 -0
  20. package/src/__mocks__/rn-primitives/checkbox.tsx +19 -0
  21. package/src/__mocks__/rn-primitives/select.tsx +116 -0
  22. package/src/__mocks__/rn-primitives/slider.tsx +40 -0
  23. package/src/__mocks__/rn-primitives/slot.tsx +30 -0
  24. package/src/__mocks__/rn-primitives/switch.tsx +24 -0
  25. package/src/__mocks__/rn-primitives/toggle.tsx +16 -0
  26. package/src/components/atoms/Avatar/Avatar.stories.tsx +57 -49
  27. package/src/components/atoms/Avatar/Avatar.test.tsx +269 -0
  28. package/src/components/atoms/Avatar/Avatar.tsx +68 -22
  29. package/src/components/atoms/Avatar/index.ts +1 -6
  30. package/src/components/atoms/Badge/Badge.stories.tsx +5 -29
  31. package/src/components/atoms/Badge/Badge.test.tsx +90 -0
  32. package/src/components/atoms/Button/Button.test.tsx +123 -0
  33. package/src/components/atoms/Button/Button.tsx +1 -1
  34. package/src/components/atoms/CarouselControls/CarouselControls.stories.tsx +217 -0
  35. package/src/components/atoms/CarouselControls/CarouselControls.tsx +127 -0
  36. package/src/components/atoms/CarouselControls/index.ts +2 -0
  37. package/src/components/atoms/Hint/Hint.test.tsx +36 -0
  38. package/src/components/atoms/Icon/Icon.test.tsx +98 -0
  39. package/src/components/atoms/Icon/Icon.tsx +5 -1
  40. package/src/components/atoms/IconButton/IconButton.test.tsx +101 -0
  41. package/src/components/atoms/Illustration/Illustration.test.tsx +55 -0
  42. package/src/components/atoms/Input/Input.stories.tsx +129 -86
  43. package/src/components/atoms/Input/Input.test.tsx +306 -0
  44. package/src/components/atoms/Input/Input.tsx +9 -1
  45. package/src/components/atoms/Input/InputField.tsx +226 -74
  46. package/src/components/atoms/Link/Link.test.tsx +89 -0
  47. package/src/components/atoms/Logo/Logo.registry.ts +30 -5
  48. package/src/components/atoms/Logo/Logo.stories.tsx +108 -0
  49. package/src/components/atoms/Logo/Logo.test.tsx +56 -0
  50. package/src/components/atoms/Logo/assets/BCorp.tsx +113 -0
  51. package/src/components/atoms/Logo/assets/ButternutFavicon.tsx +33 -0
  52. package/src/components/atoms/Logo/assets/ButternutPrimary.tsx +294 -0
  53. package/src/components/atoms/Logo/assets/ButternutTabbedBottom.tsx +294 -0
  54. package/src/components/atoms/Logo/assets/ButternutTabbedTop.tsx +294 -0
  55. package/src/components/atoms/Logo/assets/ButternutWordmark.tsx +274 -0
  56. package/src/components/atoms/Logo/assets/PsiBufetFavicon.tsx +45 -0
  57. package/src/components/atoms/Logo/assets/PsiBufetPrimary.tsx +218 -0
  58. package/src/components/atoms/Logo/assets/PsiBufetTabbedBottom.tsx +218 -0
  59. package/src/components/atoms/Logo/assets/PsiBufetTabbedTop.tsx +218 -0
  60. package/src/components/atoms/Logo/assets/PsiBufetWordmark.tsx +195 -0
  61. package/src/components/atoms/Logo/assets/index.ts +11 -0
  62. package/src/components/atoms/NumberInput/NumberInput.stories.tsx +183 -0
  63. package/src/components/atoms/NumberInput/NumberInput.test.tsx +261 -0
  64. package/src/components/atoms/NumberInput/NumberInput.tsx +129 -0
  65. package/src/components/atoms/NumberInput/NumberInputField.tsx +77 -0
  66. package/src/components/atoms/NumberInput/index.ts +4 -0
  67. package/src/components/atoms/Spinner/Spinner.test.tsx +46 -0
  68. package/src/components/atoms/Spinner/Spinner.tsx +14 -5
  69. package/src/components/atoms/Switch/Switch.test.tsx +92 -0
  70. package/src/components/atoms/Switch/Switch.tsx +16 -13
  71. package/src/components/atoms/Tag/Tag.test.tsx +70 -0
  72. package/src/components/atoms/TextArea/TextArea.stories.tsx +303 -0
  73. package/src/components/atoms/TextArea/TextArea.test.tsx +416 -0
  74. package/src/components/atoms/TextArea/TextArea.tsx +171 -0
  75. package/src/components/atoms/TextArea/TextAreaField.tsx +304 -0
  76. package/src/components/atoms/TextArea/TextAreaLabel.tsx +103 -0
  77. package/src/components/atoms/TextArea/index.ts +6 -0
  78. package/src/components/atoms/Typography/Typography.test.tsx +94 -0
  79. package/src/components/atoms/index.ts +3 -0
  80. package/src/components/molecules/Accordion/Accordion.stories.tsx +177 -0
  81. package/src/components/molecules/Accordion/Accordion.test.tsx +185 -0
  82. package/src/components/molecules/Accordion/Accordion.tsx +284 -0
  83. package/src/components/molecules/Accordion/index.ts +6 -0
  84. package/src/components/molecules/Animated/Animated.stories.tsx +254 -0
  85. package/src/components/molecules/Animated/Animated.tsx +283 -0
  86. package/src/components/molecules/Animated/index.ts +10 -0
  87. package/src/components/molecules/ButtonDock/ButtonDock.test.tsx +83 -0
  88. package/src/components/molecules/ButtonGroup/ButtonGroup.stories.tsx +8 -14
  89. package/src/components/molecules/ButtonGroup/ButtonGroup.test.tsx +73 -0
  90. package/src/components/molecules/ButtonGroup/ButtonGroup.tsx +25 -3
  91. package/src/components/molecules/Checkbox/Checkbox.stories.tsx +72 -0
  92. package/src/components/molecules/Checkbox/Checkbox.test.tsx +117 -0
  93. package/src/components/molecules/Checkbox/Checkbox.tsx +101 -95
  94. package/src/components/molecules/CopyField/CopyField.stories.tsx +313 -0
  95. package/src/components/molecules/CopyField/CopyField.test.tsx +431 -0
  96. package/src/components/molecules/CopyField/CopyField.tsx +156 -0
  97. package/src/components/molecules/CopyField/CopyFieldInput.tsx +127 -0
  98. package/src/components/molecules/CopyField/hooks/index.ts +1 -0
  99. package/src/components/molecules/CopyField/hooks/useCopyField.ts +25 -0
  100. package/src/components/molecules/CopyField/index.ts +4 -0
  101. package/src/components/molecules/DatePicker/DatePicker.stories.tsx +298 -0
  102. package/src/components/molecules/DatePicker/DatePicker.test.tsx +201 -0
  103. package/src/components/molecules/DatePicker/DatePicker.tsx +590 -0
  104. package/src/components/molecules/DatePicker/index.ts +2 -0
  105. package/src/components/molecules/Drawer/Drawer.stories.tsx +285 -0
  106. package/src/components/molecules/Drawer/Drawer.test.tsx +180 -0
  107. package/src/components/molecules/Drawer/Drawer.tsx +187 -0
  108. package/src/components/molecules/Drawer/DrawerBody.tsx +80 -0
  109. package/src/components/molecules/Drawer/DrawerClose.tsx +76 -0
  110. package/src/components/molecules/Drawer/DrawerContent.tsx +339 -0
  111. package/src/components/molecules/Drawer/DrawerContext.ts +19 -0
  112. package/src/components/molecules/Drawer/DrawerDescription.tsx +31 -0
  113. package/src/components/molecules/Drawer/DrawerDragContext.ts +11 -0
  114. package/src/components/molecules/Drawer/DrawerFooter.tsx +49 -0
  115. package/src/components/molecules/Drawer/DrawerFooterContext.ts +6 -0
  116. package/src/components/molecules/Drawer/DrawerGrabber.tsx +62 -0
  117. package/src/components/molecules/Drawer/DrawerHeader.tsx +244 -0
  118. package/src/components/molecules/Drawer/DrawerHeaderContext.ts +13 -0
  119. package/src/components/molecules/Drawer/DrawerOverlay.tsx +53 -0
  120. package/src/components/molecules/Drawer/DrawerTitle.tsx +32 -0
  121. package/src/components/molecules/Drawer/index.ts +12 -0
  122. package/src/components/molecules/FilterTab/FilterTab.stories.tsx +210 -0
  123. package/src/components/molecules/FilterTab/FilterTab.tsx +310 -0
  124. package/src/components/molecules/FilterTab/index.ts +2 -0
  125. package/src/components/molecules/MessageCard/MessageCard.stories.tsx +169 -0
  126. package/src/components/molecules/MessageCard/MessageCard.tsx +362 -0
  127. package/src/components/molecules/MessageCard/index.ts +10 -0
  128. package/src/components/molecules/Notification/Notification.stories.tsx +219 -0
  129. package/src/components/molecules/Notification/Notification.tsx +426 -0
  130. package/src/components/molecules/Notification/index.ts +2 -0
  131. package/src/components/molecules/NumberField/NumberField.stories.tsx +231 -0
  132. package/src/components/molecules/NumberField/NumberField.tsx +186 -0
  133. package/src/components/molecules/NumberField/NumberFieldInput.tsx +287 -0
  134. package/src/components/molecules/NumberField/index.ts +2 -0
  135. package/src/components/molecules/PasswordField/PasswordField.stories.tsx +362 -0
  136. package/src/components/molecules/PasswordField/PasswordField.test.tsx +369 -0
  137. package/src/components/molecules/PasswordField/PasswordField.tsx +194 -0
  138. package/src/components/molecules/PasswordField/PasswordFieldError.tsx +52 -0
  139. package/src/components/molecules/PasswordField/PasswordFieldInput.tsx +73 -0
  140. package/src/components/molecules/PasswordField/PasswordFieldRequirements.tsx +92 -0
  141. package/src/components/molecules/PasswordField/hooks/index.ts +2 -0
  142. package/src/components/molecules/PasswordField/hooks/usePasswordField.ts +113 -0
  143. package/src/components/molecules/PasswordField/index.ts +10 -0
  144. package/src/components/molecules/PictureSelector/PictureSelector.stories.tsx +243 -0
  145. package/src/components/molecules/PictureSelector/PictureSelector.tsx +313 -0
  146. package/src/components/molecules/PictureSelector/index.ts +5 -0
  147. package/src/components/molecules/Progress/Progress.stories.tsx +145 -0
  148. package/src/components/molecules/Progress/Progress.tsx +184 -0
  149. package/src/components/molecules/Progress/index.ts +2 -0
  150. package/src/components/molecules/Radio/Radio.test.tsx +104 -0
  151. package/src/components/molecules/Radio/Radio.tsx +1 -2
  152. package/src/components/molecules/SearchField/SearchField.stories.tsx +242 -0
  153. package/src/components/molecules/SearchField/SearchField.test.tsx +318 -0
  154. package/src/components/molecules/SearchField/SearchField.tsx +143 -0
  155. package/src/components/molecules/SearchField/SearchFieldInput.tsx +63 -0
  156. package/src/components/molecules/SearchField/hooks/index.ts +1 -0
  157. package/src/components/molecules/SearchField/hooks/useSearchField.ts +56 -0
  158. package/src/components/molecules/SearchField/index.ts +4 -0
  159. package/src/components/molecules/SegmentedControl/SegmentedControl.stories.tsx +31 -8
  160. package/src/components/molecules/SegmentedControl/SegmentedControl.test.tsx +141 -0
  161. package/src/components/molecules/SegmentedControl/SegmentedControl.tsx +237 -23
  162. package/src/components/molecules/SelectField/SelectField.stories.tsx +320 -0
  163. package/src/components/molecules/SelectField/SelectField.test.tsx +254 -0
  164. package/src/components/molecules/SelectField/SelectField.tsx +236 -0
  165. package/src/components/molecules/SelectField/SelectFieldContent.tsx +85 -0
  166. package/src/components/molecules/SelectField/SelectFieldItem.tsx +133 -0
  167. package/src/components/molecules/SelectField/SelectFieldTrigger.tsx +170 -0
  168. package/src/components/molecules/SelectField/SelectFieldValue.tsx +31 -0
  169. package/src/components/molecules/SelectField/hooks/index.ts +2 -0
  170. package/src/components/molecules/SelectField/hooks/useSelectField.ts +84 -0
  171. package/src/components/molecules/SelectField/index.ts +10 -0
  172. package/src/components/molecules/Slider/Slider.test.tsx +102 -0
  173. package/src/components/molecules/Slider/Slider.tsx +293 -180
  174. package/src/components/molecules/Tooltip/Tooltip.stories.tsx +168 -0
  175. package/src/components/molecules/Tooltip/Tooltip.tsx +326 -0
  176. package/src/components/molecules/Tooltip/index.ts +2 -0
  177. package/src/components/molecules/index.ts +15 -0
  178. package/src/test-utils.tsx +20 -0
  179. package/tsconfig.json +1 -1
  180. package/tsup.config.ts +16 -2
  181. package/vitest.config.ts +114 -0
  182. package/vitest.setup.ts +16 -0
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@butternutbox/pawprint-native",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "type": "module",
5
5
  "description": "ButternutBox Pawprint Design System - React Native Components",
6
- "main": "./dist/index.js",
7
- "module": "./dist/index.mjs",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
- "import": "./dist/index.mjs",
13
- "require": "./dist/index.js"
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
14
  }
15
15
  },
16
16
  "scripts": {
@@ -18,38 +18,57 @@
18
18
  "dev": "tsup --watch",
19
19
  "clean": "rm -rf .turbo node_modules dist",
20
20
  "lint:check": "eslint src --max-warnings 0",
21
- "type:check": "tsc --noEmit"
21
+ "type:check": "tsc --noEmit",
22
+ "test": "vitest run"
22
23
  },
23
24
  "dependencies": {
24
- "@butternutbox/pawprint-tokens": "^0.0.1",
25
+ "@butternutbox/pawprint-tokens": "^0.1.0",
25
26
  "@emotion/native": "^11.11.0",
26
27
  "@emotion/react": "^11.14.0",
27
28
  "@rn-primitives/avatar": "^1.4.0",
28
29
  "@rn-primitives/checkbox": "^1.4.0",
29
30
  "@rn-primitives/portal": "^1.0.5",
31
+ "@rn-primitives/progress": "^1.4.0",
30
32
  "@rn-primitives/slider": "^1.4.0",
31
33
  "@rn-primitives/slot": "^1.0.5",
32
34
  "@rn-primitives/switch": "^1.4.0",
33
35
  "@rn-primitives/toggle": "^1.4.0",
36
+ "@rn-primitives/tooltip": "^1.4.0",
34
37
  "@rn-primitives/types": "^1.0.5",
38
+ "date-fns": "^4.1.0",
35
39
  "expo": "^55.0.9"
36
40
  },
37
41
  "peerDependencies": {
38
42
  "react": ">=18.0.0",
39
- "react-native": ">=0.74.0"
43
+ "react-native": ">=0.74.0",
44
+ "react-native-gesture-handler": ">=2.20.0",
45
+ "react-native-reanimated": ">=4.0.0",
46
+ "react-native-safe-area-context": ">=4.0.0",
47
+ "react-native-svg": ">=15.0.0",
48
+ "react-native-worklets": ">=0.5.0"
40
49
  },
41
50
  "devDependencies": {
42
51
  "@butternutbox/pawprint-icons": "workspace:*",
43
52
  "@butternutbox/pawprint-illustrations": "workspace:*",
44
53
  "@repo/eslint-config": "workspace:*",
45
54
  "@repo/typescript-config": "workspace:*",
55
+ "@testing-library/dom": "^10.4.1",
56
+ "@testing-library/jest-dom": "^6.6.3",
57
+ "@testing-library/react": "^16.3.0",
58
+ "@testing-library/user-event": "^14.6.1",
46
59
  "@types/react": "~19.1.10",
47
60
  "@types/react-native": "^0.73.0",
48
61
  "eslint": "^8.57.0",
62
+ "jsdom": "^26.1.0",
49
63
  "react": "19.1.0",
50
64
  "react-native": "0.81.5",
65
+ "react-native-gesture-handler": "~2.28.0",
66
+ "react-native-reanimated": "~4.1.1",
67
+ "react-native-svg": "15.12.1",
68
+ "react-native-worklets": "0.5.1",
51
69
  "tsup": "^8.5.0",
52
- "typescript": "^5.7.3"
70
+ "typescript": "^5.7.3",
71
+ "vitest": "^3.2.1"
53
72
  },
54
73
  "publishConfig": {
55
74
  "access": "public"
@@ -0,0 +1 @@
1
+ export default "asset-stub"
@@ -0,0 +1,18 @@
1
+ import React from "react"
2
+
3
+ /**
4
+ * Mock of @emotion/native's styled function.
5
+ * Wraps the component without applying styles — sufficient for unit tests
6
+ * that verify structure, content, and behaviour rather than visual styles.
7
+ */
8
+ const styled = (Component: any) => {
9
+ return (..._styleFns: any[]) => {
10
+ const Styled = React.forwardRef((props: any, ref: any) => (
11
+ <Component ref={ref} {...props} />
12
+ ))
13
+ Styled.displayName = `Styled(${Component.displayName || Component.name || "Component"})`
14
+ return Styled
15
+ }
16
+ }
17
+
18
+ export default styled
@@ -0,0 +1,41 @@
1
+ import React from "react"
2
+
3
+ type GestureChainable = {
4
+ onBegin: (_cb: unknown) => GestureChainable
5
+ onUpdate: (_cb: unknown) => GestureChainable
6
+ onChange: (_cb: unknown) => GestureChainable
7
+ onEnd: (_cb: unknown) => GestureChainable
8
+ onFinalize: (_cb: unknown) => GestureChainable
9
+ enabled: (_value: boolean) => GestureChainable
10
+ minDistance: (_value: number) => GestureChainable
11
+ }
12
+
13
+ const makeChainable = (): GestureChainable => {
14
+ const chain: GestureChainable = {
15
+ onBegin: () => chain,
16
+ onUpdate: () => chain,
17
+ onChange: () => chain,
18
+ onEnd: () => chain,
19
+ onFinalize: () => chain,
20
+ enabled: () => chain,
21
+ minDistance: () => chain
22
+ }
23
+ return chain
24
+ }
25
+
26
+ export const Gesture = {
27
+ Pan: () => makeChainable(),
28
+ Tap: () => makeChainable()
29
+ }
30
+
31
+ export const GestureDetector = ({
32
+ children
33
+ }: {
34
+ children: React.ReactNode
35
+ }) => <>{children}</>
36
+
37
+ export const GestureHandlerRootView = ({
38
+ children
39
+ }: {
40
+ children: React.ReactNode
41
+ }) => <>{children}</>
@@ -0,0 +1,79 @@
1
+ import React from "react"
2
+
3
+ const AnimatedView = React.forwardRef<HTMLDivElement, Record<string, unknown>>(
4
+ ({ children, style, ..._rest }, ref) => {
5
+ const flatStyle = Array.isArray(style)
6
+ ? Object.assign({}, ...style.filter(Boolean))
7
+ : style
8
+ return (
9
+ <div
10
+ ref={ref}
11
+ style={
12
+ typeof flatStyle === "object" ? (flatStyle as object) : undefined
13
+ }
14
+ >
15
+ {children as React.ReactNode}
16
+ </div>
17
+ )
18
+ }
19
+ )
20
+ AnimatedView.displayName = "Animated.View"
21
+
22
+ const Animated = {
23
+ View: AnimatedView
24
+ }
25
+
26
+ export default Animated
27
+
28
+ export const useSharedValue = <T,>(initial: T) => ({ value: initial })
29
+
30
+ export const useAnimatedStyle = (_fn: () => object) => ({})
31
+
32
+ export const runOnJS =
33
+ <Args extends unknown[], R>(fn: (..._args: Args) => R) =>
34
+ (...args: Args) =>
35
+ fn(...args)
36
+
37
+ export const withTiming = <T,>(value: T) => value
38
+
39
+ // Animation builder stub — supports chaining .duration() / .easing() / .delay()
40
+ const createBuilder = () => {
41
+ const b: Record<string, unknown> = {}
42
+ b.duration = () => b
43
+ b.easing = () => b
44
+ b.delay = () => b
45
+ return b
46
+ }
47
+
48
+ export const Easing = {
49
+ out: (_fn: unknown) => _fn,
50
+ inOut: (_fn: unknown) => _fn,
51
+ quad: (t: number) => t * t
52
+ }
53
+
54
+ export const FadeIn = createBuilder()
55
+ export const FadeOut = createBuilder()
56
+ export const FadeInUp = createBuilder()
57
+ export const FadeOutUp = createBuilder()
58
+ export const FadeInDown = createBuilder()
59
+ export const FadeOutDown = createBuilder()
60
+ export const FadeInLeft = createBuilder()
61
+ export const FadeOutLeft = createBuilder()
62
+ export const FadeInRight = createBuilder()
63
+ export const FadeOutRight = createBuilder()
64
+ export const ZoomIn = createBuilder()
65
+ export const ZoomOut = createBuilder()
66
+ export class ComplexAnimationBuilder {
67
+ static duration() {
68
+ return new ComplexAnimationBuilder()
69
+ }
70
+ duration() {
71
+ return this
72
+ }
73
+ easing() {
74
+ return this
75
+ }
76
+ delay() {
77
+ return this
78
+ }
79
+ }
@@ -0,0 +1,6 @@
1
+ export const useSafeAreaInsets = () => ({
2
+ top: 0,
3
+ bottom: 0,
4
+ left: 0,
5
+ right: 0
6
+ })
@@ -0,0 +1,27 @@
1
+ import React from "react"
2
+
3
+ type SvgProps = {
4
+ children?: React.ReactNode
5
+ style?: unknown
6
+ width?: number | string
7
+ height?: number | string
8
+ preserveAspectRatio?: string
9
+ pointerEvents?: string
10
+ }
11
+
12
+ const passThrough =
13
+ (name: string) =>
14
+ ({ children }: { children?: React.ReactNode }) =>
15
+ React.createElement("div", { "data-mock": name }, children)
16
+
17
+ const Svg = ({ children }: SvgProps) =>
18
+ React.createElement("div", { "data-mock": "Svg" }, children)
19
+
20
+ export default Svg
21
+ export const Defs = passThrough("Defs")
22
+ export const LinearGradient = passThrough("LinearGradient")
23
+ export const Rect = passThrough("Rect")
24
+ export const Stop = passThrough("Stop")
25
+ export const G = passThrough("G")
26
+ export const Path = passThrough("Path")
27
+ export const ClipPath = passThrough("ClipPath")
@@ -0,0 +1,11 @@
1
+ export const scheduleOnRN = <Args extends unknown[], R>(
2
+ fn: (..._args: Args) => R,
3
+ ...args: Args
4
+ ): void => {
5
+ fn(...args)
6
+ }
7
+
8
+ export const runOnUI =
9
+ <Args extends unknown[], R>(fn: (..._args: Args) => R) =>
10
+ (...args: Args) =>
11
+ fn(...args)
@@ -0,0 +1,338 @@
1
+ import React from "react"
2
+
3
+ // ─── View ────────────────────────────────────────────────────────────────
4
+ export const View = React.forwardRef<HTMLDivElement, any>(
5
+ (
6
+ {
7
+ testID,
8
+ accessibilityRole,
9
+ accessibilityLabel,
10
+ accessibilityState,
11
+ accessibilityValue,
12
+ accessible: _accessible,
13
+ children,
14
+ style,
15
+ onLayout: _onLayout,
16
+ ..._rest
17
+ },
18
+ ref
19
+ ) => {
20
+ const domProps: Record<string, any> = {}
21
+ if (testID) domProps["data-testid"] = testID
22
+ if (accessibilityRole) {
23
+ domProps.role =
24
+ accessibilityRole === "adjustable" ? "slider" : accessibilityRole
25
+ }
26
+ if (accessibilityLabel) domProps["aria-label"] = accessibilityLabel
27
+ if (accessibilityState?.disabled) domProps["aria-disabled"] = true
28
+ if (accessibilityState?.selected) domProps["aria-selected"] = true
29
+ if (accessibilityState?.checked !== undefined)
30
+ domProps["aria-checked"] = accessibilityState.checked
31
+ if (accessibilityState?.busy) domProps["aria-busy"] = true
32
+ if (accessibilityValue) {
33
+ if (accessibilityValue.now !== undefined)
34
+ domProps["aria-valuenow"] = String(accessibilityValue.now)
35
+ if (accessibilityValue.min !== undefined)
36
+ domProps["aria-valuemin"] = String(accessibilityValue.min)
37
+ if (accessibilityValue.max !== undefined)
38
+ domProps["aria-valuemax"] = String(accessibilityValue.max)
39
+ }
40
+ return (
41
+ <div ref={ref} style={style} {...domProps}>
42
+ {children}
43
+ </div>
44
+ )
45
+ }
46
+ )
47
+ View.displayName = "View"
48
+
49
+ // ─── Text ────────────────────────────────────────────────────────────────
50
+ export const Text = React.forwardRef<HTMLSpanElement, any>(
51
+ (
52
+ {
53
+ children,
54
+ style,
55
+ testID,
56
+ numberOfLines: _numberOfLines,
57
+ ellipsizeMode: _ellipsizeMode,
58
+ ..._rest
59
+ },
60
+ ref
61
+ ) => (
62
+ <span ref={ref} style={style} data-testid={testID}>
63
+ {children}
64
+ </span>
65
+ )
66
+ )
67
+ Text.displayName = "Text"
68
+
69
+ // ─── Pressable ───────────────────────────────────────────────────────────
70
+ export const Pressable = React.forwardRef<HTMLButtonElement, any>(
71
+ (
72
+ {
73
+ testID,
74
+ disabled,
75
+ accessibilityRole,
76
+ accessibilityLabel,
77
+ accessibilityState,
78
+ onPress,
79
+ onPressIn: _onPressIn,
80
+ onPressOut: _onPressOut,
81
+ children,
82
+ style: _style,
83
+ ..._rest
84
+ },
85
+ ref
86
+ ) => {
87
+ const domProps: Record<string, any> = {}
88
+ if (testID) domProps["data-testid"] = testID
89
+ if (accessibilityRole) domProps.role = accessibilityRole
90
+ if (accessibilityLabel) domProps["aria-label"] = accessibilityLabel
91
+ if (accessibilityState?.disabled) domProps["aria-disabled"] = true
92
+ if (accessibilityState?.selected) domProps["aria-selected"] = true
93
+ if (accessibilityState?.expanded !== undefined)
94
+ domProps["aria-expanded"] = accessibilityState.expanded
95
+ if (accessibilityState?.busy) domProps["aria-busy"] = true
96
+
97
+ const content =
98
+ typeof children === "function" ? children({ pressed: false }) : children
99
+
100
+ return (
101
+ <button
102
+ ref={ref as any}
103
+ disabled={disabled}
104
+ onClick={(e) => !disabled && onPress?.(e)}
105
+ {...domProps}
106
+ >
107
+ {content}
108
+ </button>
109
+ )
110
+ }
111
+ )
112
+ Pressable.displayName = "Pressable"
113
+
114
+ // ─── TextInput ───────────────────────────────────────────────────────────
115
+ export const TextInput = React.forwardRef<HTMLInputElement, any>(
116
+ (
117
+ {
118
+ testID,
119
+ placeholderTextColor: _placeholderTextColor,
120
+ onChangeText,
121
+ onChange,
122
+ value,
123
+ defaultValue,
124
+ placeholder,
125
+ editable,
126
+ keyboardType: _keyboardType,
127
+ accessibilityState,
128
+ ..._rest
129
+ },
130
+ ref
131
+ ) => {
132
+ return (
133
+ <input
134
+ ref={ref}
135
+ data-testid={testID}
136
+ value={value}
137
+ defaultValue={defaultValue}
138
+ placeholder={placeholder}
139
+ disabled={editable === false}
140
+ aria-disabled={accessibilityState?.disabled}
141
+ onChange={(e) => {
142
+ onChangeText?.(e.target.value)
143
+ onChange?.(e)
144
+ }}
145
+ />
146
+ )
147
+ }
148
+ )
149
+ TextInput.displayName = "TextInput"
150
+
151
+ // ─── ScrollView ──────────────────────────────────────────────────────────
152
+ export const ScrollView = React.forwardRef<HTMLDivElement, any>(
153
+ (
154
+ {
155
+ children,
156
+ contentContainerStyle,
157
+ horizontal: _horizontal,
158
+ showsHorizontalScrollIndicator: _showsHorizontalScrollIndicator,
159
+ showsVerticalScrollIndicator: _showsVerticalScrollIndicator,
160
+ nestedScrollEnabled: _nestedScrollEnabled,
161
+ ..._rest
162
+ },
163
+ ref
164
+ ) => (
165
+ <div ref={ref} style={contentContainerStyle}>
166
+ {children}
167
+ </div>
168
+ )
169
+ )
170
+ ScrollView.displayName = "ScrollView"
171
+
172
+ // ─── Modal ───────────────────────────────────────────────────────────────
173
+ export const Modal = React.forwardRef<HTMLDivElement, any>(
174
+ ({ children, visible, onRequestClose: _onRequestClose, ..._rest }, ref) => {
175
+ if (!visible) return null
176
+ return (
177
+ <div ref={ref} role="dialog">
178
+ {children}
179
+ </div>
180
+ )
181
+ }
182
+ )
183
+ Modal.displayName = "Modal"
184
+
185
+ // ─── Image ───────────────────────────────────────────────────────────────
186
+ export const Image = React.forwardRef<HTMLImageElement, any>(
187
+ ({ source, style, ..._rest }, ref) => (
188
+ <img ref={ref} src={source?.uri ?? source} style={style} alt="" />
189
+ )
190
+ )
191
+ Image.displayName = "Image"
192
+
193
+ // ─── Animated ────────────────────────────────────────────────────────────
194
+ class AnimatedValue {
195
+ _value: number
196
+ _listeners: Record<string, (val: { value: number }) => void> = {}
197
+ _listenerId = 0
198
+
199
+ constructor(value: number) {
200
+ this._value = value
201
+ }
202
+ setValue(value: number) {
203
+ this._value = value
204
+ }
205
+ stopAnimation() {}
206
+ stop() {}
207
+ interpolate({ outputRange }: { inputRange: number[]; outputRange: any[] }) {
208
+ return outputRange[0]
209
+ }
210
+ addListener(cb: (val: { value: number }) => void): string {
211
+ const id = String(++this._listenerId)
212
+ this._listeners[id] = cb
213
+ return id
214
+ }
215
+ removeListener(id: string) {
216
+ delete this._listeners[id]
217
+ }
218
+ }
219
+
220
+ const mockAnim = (cb?: (result: { finished: boolean }) => void) => {
221
+ if (cb) setTimeout(() => cb({ finished: true }), 0)
222
+ }
223
+
224
+ export const Animated = {
225
+ View: React.forwardRef<HTMLDivElement, any>(
226
+ (
227
+ {
228
+ children,
229
+ style,
230
+ accessibilityRole,
231
+ accessibilityLabel,
232
+ accessibilityState,
233
+ ..._rest
234
+ },
235
+ ref
236
+ ) => {
237
+ const domProps: Record<string, any> = {}
238
+ if (accessibilityRole) domProps.role = accessibilityRole
239
+ if (accessibilityLabel) domProps["aria-label"] = accessibilityLabel
240
+ if (accessibilityState?.disabled) domProps["aria-disabled"] = true
241
+ const flatStyle = Array.isArray(style)
242
+ ? Object.assign({}, ...style.filter(Boolean))
243
+ : style
244
+ return (
245
+ <div
246
+ ref={ref}
247
+ style={typeof flatStyle === "object" ? flatStyle : undefined}
248
+ {...domProps}
249
+ >
250
+ {children}
251
+ </div>
252
+ )
253
+ }
254
+ ),
255
+ Value: AnimatedValue,
256
+ timing: (_value: any, _config: any) => ({
257
+ start: mockAnim,
258
+ stop: () => {}
259
+ }),
260
+ spring: (_value: any, _config: any) => ({
261
+ start: mockAnim,
262
+ stop: () => {}
263
+ }),
264
+ parallel: (animations: any[]) => ({
265
+ start: (cb?: (result: { finished: boolean }) => void) => {
266
+ animations.forEach((anim) => anim.start())
267
+ if (cb) setTimeout(() => cb({ finished: true }), 0)
268
+ },
269
+ stop: () => {
270
+ animations.forEach((anim) => anim.stop?.())
271
+ }
272
+ }),
273
+ loop: (animation: any) => ({
274
+ start: () => animation.start(),
275
+ stop: () => {}
276
+ })
277
+ }
278
+ Animated.View.displayName = "Animated.View"
279
+
280
+ // ─── Dimensions ──────────────────────────────────────────────────────────
281
+ export const Dimensions = {
282
+ get: (_dim: "window" | "screen") => ({ width: 375, height: 812 })
283
+ }
284
+
285
+ // ─── useWindowDimensions ─────────────────────────────────────────────────
286
+ export const useWindowDimensions = () => ({ width: 375, height: 812 })
287
+
288
+ // ─── StyleSheet ──────────────────────────────────────────────────────────
289
+ export const StyleSheet = {
290
+ create: <T extends Record<string, any>>(styles: T): T => styles,
291
+ flatten: (style: any) => style,
292
+ absoluteFillObject: {
293
+ position: "absolute" as const,
294
+ top: 0,
295
+ left: 0,
296
+ bottom: 0,
297
+ right: 0
298
+ }
299
+ }
300
+
301
+ // ─── Linking ─────────────────────────────────────────────────────────────
302
+ export const Linking = {
303
+ openURL: (() => {}) as any
304
+ }
305
+
306
+ // ─── PanResponder ────────────────────────────────────────────────────────
307
+ export const PanResponder = {
308
+ create: (_config: any) => ({
309
+ panHandlers: {}
310
+ })
311
+ }
312
+
313
+ // ─── Easing ──────────────────────────────────────────────────────────────
314
+ export const Easing = {
315
+ linear: (t: number) => t,
316
+ ease: (t: number) => t,
317
+ bezier: (_x1: number, _y1: number, _x2: number, _y2: number) => (t: number) =>
318
+ t
319
+ }
320
+
321
+ // ─── Platform ────────────────────────────────────────────────────────────
322
+ export const Platform = {
323
+ OS: "ios" as const,
324
+ select: (obj: any) => obj.ios ?? obj.default
325
+ }
326
+
327
+ // ─── Type exports (re-exported as empty for TS compat) ───────────────────
328
+ export type ViewProps = any
329
+ export type TextProps = any
330
+ export type PressableProps = any
331
+ export type TextInputProps = any
332
+ export type ViewStyle = any
333
+ export type TextStyle = any
334
+ export type LayoutChangeEvent = any
335
+ export type ImageSourcePropType = any
336
+ export type ScrollViewProps = any
337
+ export type GestureResponderEvent = any
338
+ export type PanResponderInstance = { panHandlers: Record<string, any> }
@@ -0,0 +1,24 @@
1
+ import React from "react"
2
+
3
+ export const Root = React.forwardRef<any, any>(
4
+ ({ children, alt, ..._rest }, ref) => (
5
+ <div ref={ref} role="img" aria-label={alt}>
6
+ {children}
7
+ </div>
8
+ )
9
+ )
10
+ Root.displayName = "AvatarPrimitive.Root"
11
+
12
+ export const Image = React.forwardRef<any, any>(({ source, ..._rest }, ref) => (
13
+ <img ref={ref} src={source?.uri} alt="" />
14
+ ))
15
+ Image.displayName = "AvatarPrimitive.Image"
16
+
17
+ export const Fallback = React.forwardRef<any, any>(
18
+ ({ children, ..._rest }, ref) => (
19
+ <div ref={ref} data-testid="avatar-fallback">
20
+ {children}
21
+ </div>
22
+ )
23
+ )
24
+ Fallback.displayName = "AvatarPrimitive.Fallback"
@@ -0,0 +1,19 @@
1
+ import React from "react"
2
+
3
+ export const Root = React.forwardRef<any, any>(
4
+ ({ checked, onCheckedChange, disabled, children, ..._rest }, ref) => (
5
+ <button
6
+ ref={ref}
7
+ role="checkbox"
8
+ aria-checked={checked}
9
+ disabled={disabled}
10
+ onClick={() => !disabled && onCheckedChange?.(!checked)}
11
+ >
12
+ {children}
13
+ </button>
14
+ )
15
+ )
16
+ Root.displayName = "CheckboxPrimitive.Root"
17
+
18
+ export const Indicator = ({ children }: any) => <>{children}</>
19
+ Indicator.displayName = "CheckboxPrimitive.Indicator"