@butternutbox/pawprint-native 0.0.1 → 0.2.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 (187) hide show
  1. package/.turbo/turbo-build.log +15 -15
  2. package/CHANGELOG.md +30 -0
  3. package/COMPONENT_GUIDELINES.md +111 -4
  4. package/dist/index.cjs +12413 -1459
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +1111 -13
  7. package/dist/index.d.ts +1111 -13
  8. package/dist/index.js +12365 -1457
  9. package/dist/index.js.map +1 -1
  10. package/package.json +29 -11
  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.stories.tsx +2 -2
  42. package/src/components/atoms/Illustration/Illustration.test.tsx +55 -0
  43. package/src/components/atoms/Illustration/Illustration.tsx +3 -3
  44. package/src/components/atoms/Input/Input.stories.tsx +129 -86
  45. package/src/components/atoms/Input/Input.test.tsx +306 -0
  46. package/src/components/atoms/Input/Input.tsx +9 -1
  47. package/src/components/atoms/Input/InputField.tsx +226 -74
  48. package/src/components/atoms/Link/Link.test.tsx +89 -0
  49. package/src/components/atoms/Link/Link.tsx +7 -6
  50. package/src/components/atoms/Logo/Logo.registry.ts +30 -5
  51. package/src/components/atoms/Logo/Logo.stories.tsx +108 -0
  52. package/src/components/atoms/Logo/Logo.test.tsx +56 -0
  53. package/src/components/atoms/Logo/assets/BCorp.tsx +113 -0
  54. package/src/components/atoms/Logo/assets/ButternutFavicon.tsx +33 -0
  55. package/src/components/atoms/Logo/assets/ButternutPrimary.tsx +294 -0
  56. package/src/components/atoms/Logo/assets/ButternutTabbedBottom.tsx +294 -0
  57. package/src/components/atoms/Logo/assets/ButternutTabbedTop.tsx +294 -0
  58. package/src/components/atoms/Logo/assets/ButternutWordmark.tsx +274 -0
  59. package/src/components/atoms/Logo/assets/PsiBufetFavicon.tsx +45 -0
  60. package/src/components/atoms/Logo/assets/PsiBufetPrimary.tsx +218 -0
  61. package/src/components/atoms/Logo/assets/PsiBufetTabbedBottom.tsx +218 -0
  62. package/src/components/atoms/Logo/assets/PsiBufetTabbedTop.tsx +218 -0
  63. package/src/components/atoms/Logo/assets/PsiBufetWordmark.tsx +195 -0
  64. package/src/components/atoms/Logo/assets/index.ts +11 -0
  65. package/src/components/atoms/NumberInput/NumberInput.stories.tsx +183 -0
  66. package/src/components/atoms/NumberInput/NumberInput.test.tsx +261 -0
  67. package/src/components/atoms/NumberInput/NumberInput.tsx +129 -0
  68. package/src/components/atoms/NumberInput/NumberInputField.tsx +77 -0
  69. package/src/components/atoms/NumberInput/index.ts +4 -0
  70. package/src/components/atoms/Spinner/Spinner.test.tsx +46 -0
  71. package/src/components/atoms/Spinner/Spinner.tsx +14 -5
  72. package/src/components/atoms/Switch/Switch.test.tsx +92 -0
  73. package/src/components/atoms/Switch/Switch.tsx +28 -17
  74. package/src/components/atoms/Tag/Tag.test.tsx +70 -0
  75. package/src/components/atoms/TextArea/TextArea.stories.tsx +303 -0
  76. package/src/components/atoms/TextArea/TextArea.test.tsx +416 -0
  77. package/src/components/atoms/TextArea/TextArea.tsx +171 -0
  78. package/src/components/atoms/TextArea/TextAreaField.tsx +304 -0
  79. package/src/components/atoms/TextArea/TextAreaLabel.tsx +103 -0
  80. package/src/components/atoms/TextArea/index.ts +6 -0
  81. package/src/components/atoms/Typography/Typography.test.tsx +94 -0
  82. package/src/components/atoms/index.ts +3 -0
  83. package/src/components/molecules/Accordion/Accordion.stories.tsx +177 -0
  84. package/src/components/molecules/Accordion/Accordion.test.tsx +185 -0
  85. package/src/components/molecules/Accordion/Accordion.tsx +284 -0
  86. package/src/components/molecules/Accordion/index.ts +6 -0
  87. package/src/components/molecules/Animated/Animated.stories.tsx +254 -0
  88. package/src/components/molecules/Animated/Animated.tsx +283 -0
  89. package/src/components/molecules/Animated/index.ts +10 -0
  90. package/src/components/molecules/ButtonDock/ButtonDock.stories.tsx +44 -25
  91. package/src/components/molecules/ButtonDock/ButtonDock.test.tsx +83 -0
  92. package/src/components/molecules/ButtonDock/ButtonDock.tsx +16 -13
  93. package/src/components/molecules/ButtonGroup/ButtonGroup.stories.tsx +48 -29
  94. package/src/components/molecules/ButtonGroup/ButtonGroup.test.tsx +73 -0
  95. package/src/components/molecules/ButtonGroup/ButtonGroup.tsx +25 -3
  96. package/src/components/molecules/Checkbox/Checkbox.stories.tsx +72 -0
  97. package/src/components/molecules/Checkbox/Checkbox.test.tsx +117 -0
  98. package/src/components/molecules/Checkbox/Checkbox.tsx +101 -95
  99. package/src/components/molecules/CopyField/CopyField.stories.tsx +313 -0
  100. package/src/components/molecules/CopyField/CopyField.test.tsx +431 -0
  101. package/src/components/molecules/CopyField/CopyField.tsx +156 -0
  102. package/src/components/molecules/CopyField/CopyFieldInput.tsx +127 -0
  103. package/src/components/molecules/CopyField/hooks/index.ts +1 -0
  104. package/src/components/molecules/CopyField/hooks/useCopyField.ts +25 -0
  105. package/src/components/molecules/CopyField/index.ts +4 -0
  106. package/src/components/molecules/DatePicker/DatePicker.stories.tsx +298 -0
  107. package/src/components/molecules/DatePicker/DatePicker.test.tsx +201 -0
  108. package/src/components/molecules/DatePicker/DatePicker.tsx +590 -0
  109. package/src/components/molecules/DatePicker/index.ts +2 -0
  110. package/src/components/molecules/Drawer/Drawer.stories.tsx +285 -0
  111. package/src/components/molecules/Drawer/Drawer.test.tsx +180 -0
  112. package/src/components/molecules/Drawer/Drawer.tsx +187 -0
  113. package/src/components/molecules/Drawer/DrawerBody.tsx +80 -0
  114. package/src/components/molecules/Drawer/DrawerClose.tsx +76 -0
  115. package/src/components/molecules/Drawer/DrawerContent.tsx +339 -0
  116. package/src/components/molecules/Drawer/DrawerContext.ts +19 -0
  117. package/src/components/molecules/Drawer/DrawerDescription.tsx +31 -0
  118. package/src/components/molecules/Drawer/DrawerDragContext.ts +11 -0
  119. package/src/components/molecules/Drawer/DrawerFooter.tsx +49 -0
  120. package/src/components/molecules/Drawer/DrawerFooterContext.ts +6 -0
  121. package/src/components/molecules/Drawer/DrawerGrabber.tsx +62 -0
  122. package/src/components/molecules/Drawer/DrawerHeader.tsx +244 -0
  123. package/src/components/molecules/Drawer/DrawerHeaderContext.ts +13 -0
  124. package/src/components/molecules/Drawer/DrawerOverlay.tsx +53 -0
  125. package/src/components/molecules/Drawer/DrawerTitle.tsx +32 -0
  126. package/src/components/molecules/Drawer/index.ts +12 -0
  127. package/src/components/molecules/FilterTab/FilterTab.stories.tsx +210 -0
  128. package/src/components/molecules/FilterTab/FilterTab.tsx +310 -0
  129. package/src/components/molecules/FilterTab/index.ts +2 -0
  130. package/src/components/molecules/MessageCard/MessageCard.stories.tsx +169 -0
  131. package/src/components/molecules/MessageCard/MessageCard.tsx +362 -0
  132. package/src/components/molecules/MessageCard/index.ts +10 -0
  133. package/src/components/molecules/Notification/Notification.stories.tsx +219 -0
  134. package/src/components/molecules/Notification/Notification.tsx +426 -0
  135. package/src/components/molecules/Notification/index.ts +2 -0
  136. package/src/components/molecules/NumberField/NumberField.stories.tsx +231 -0
  137. package/src/components/molecules/NumberField/NumberField.tsx +186 -0
  138. package/src/components/molecules/NumberField/NumberFieldInput.tsx +287 -0
  139. package/src/components/molecules/NumberField/index.ts +2 -0
  140. package/src/components/molecules/PasswordField/PasswordField.stories.tsx +362 -0
  141. package/src/components/molecules/PasswordField/PasswordField.test.tsx +369 -0
  142. package/src/components/molecules/PasswordField/PasswordField.tsx +194 -0
  143. package/src/components/molecules/PasswordField/PasswordFieldError.tsx +53 -0
  144. package/src/components/molecules/PasswordField/PasswordFieldInput.tsx +73 -0
  145. package/src/components/molecules/PasswordField/PasswordFieldRequirements.tsx +95 -0
  146. package/src/components/molecules/PasswordField/hooks/index.ts +2 -0
  147. package/src/components/molecules/PasswordField/hooks/usePasswordField.ts +113 -0
  148. package/src/components/molecules/PasswordField/index.ts +10 -0
  149. package/src/components/molecules/PictureSelector/PictureSelector.stories.tsx +204 -0
  150. package/src/components/molecules/PictureSelector/PictureSelector.tsx +335 -0
  151. package/src/components/molecules/PictureSelector/index.ts +5 -0
  152. package/src/components/molecules/Progress/Progress.stories.tsx +145 -0
  153. package/src/components/molecules/Progress/Progress.tsx +184 -0
  154. package/src/components/molecules/Progress/index.ts +2 -0
  155. package/src/components/molecules/Radio/Radio.test.tsx +104 -0
  156. package/src/components/molecules/Radio/Radio.tsx +1 -2
  157. package/src/components/molecules/SearchField/SearchField.stories.tsx +242 -0
  158. package/src/components/molecules/SearchField/SearchField.test.tsx +318 -0
  159. package/src/components/molecules/SearchField/SearchField.tsx +143 -0
  160. package/src/components/molecules/SearchField/SearchFieldInput.tsx +63 -0
  161. package/src/components/molecules/SearchField/hooks/index.ts +1 -0
  162. package/src/components/molecules/SearchField/hooks/useSearchField.ts +56 -0
  163. package/src/components/molecules/SearchField/index.ts +4 -0
  164. package/src/components/molecules/SegmentedControl/SegmentedControl.stories.tsx +31 -8
  165. package/src/components/molecules/SegmentedControl/SegmentedControl.test.tsx +141 -0
  166. package/src/components/molecules/SegmentedControl/SegmentedControl.tsx +237 -23
  167. package/src/components/molecules/SelectField/SelectField.stories.tsx +320 -0
  168. package/src/components/molecules/SelectField/SelectField.test.tsx +254 -0
  169. package/src/components/molecules/SelectField/SelectField.tsx +236 -0
  170. package/src/components/molecules/SelectField/SelectFieldContent.tsx +85 -0
  171. package/src/components/molecules/SelectField/SelectFieldItem.tsx +133 -0
  172. package/src/components/molecules/SelectField/SelectFieldTrigger.tsx +170 -0
  173. package/src/components/molecules/SelectField/SelectFieldValue.tsx +31 -0
  174. package/src/components/molecules/SelectField/hooks/index.ts +2 -0
  175. package/src/components/molecules/SelectField/hooks/useSelectField.ts +84 -0
  176. package/src/components/molecules/SelectField/index.ts +10 -0
  177. package/src/components/molecules/Slider/Slider.test.tsx +102 -0
  178. package/src/components/molecules/Slider/Slider.tsx +293 -180
  179. package/src/components/molecules/Tooltip/Tooltip.stories.tsx +168 -0
  180. package/src/components/molecules/Tooltip/Tooltip.tsx +326 -0
  181. package/src/components/molecules/Tooltip/index.ts +2 -0
  182. package/src/components/molecules/index.ts +15 -0
  183. package/src/test-utils.tsx +20 -0
  184. package/tsconfig.json +1 -1
  185. package/tsup.config.ts +16 -2
  186. package/vitest.config.ts +114 -0
  187. package/vitest.setup.ts +16 -0
@@ -1,5 +1,5 @@
1
1
  import React from "react"
2
- import { View, PressableProps } from "react-native"
2
+ import { View, Pressable, PressableProps } from "react-native"
3
3
  import styled from "@emotion/native"
4
4
  import { useTheme } from "@emotion/react"
5
5
  import * as CheckboxPrimitive from "@rn-primitives/checkbox"
@@ -27,7 +27,7 @@ export type CheckboxProps = CheckboxOwnProps &
27
27
 
28
28
  const parseTokenValue = (value: string): number => parseFloat(value)
29
29
 
30
- const StyledRoot = styled(CheckboxPrimitive.Root)<{
30
+ const StyledRootPressable = styled(Pressable)<{
31
31
  rootFlexAlign: string
32
32
  rootGap: number
33
33
  rootOpacity: number
@@ -169,109 +169,115 @@ export const Checkbox = React.forwardRef<View, CheckboxProps>(
169
169
  const controlSize = parseTokenValue(checkbox.size.control.default)
170
170
 
171
171
  return (
172
- <StyledRoot
173
- ref={ref}
172
+ <CheckboxPrimitive.Root
174
173
  checked={isChecked}
175
174
  onCheckedChange={handleCheckedChange}
176
175
  disabled={disabled}
177
- rootFlexAlign={isTile ? "center" : "flex-start"}
178
- rootGap={parseTokenValue(
179
- isTile ? checkbox.checkboxTile.spacing.gap : checkbox.spacing.gap
180
- )}
181
- rootOpacity={
182
- disabled ? parseFloat(theme.tokens.primitives.opacity["40"]) : 1
183
- }
184
- rootPaddingVertical={
185
- isTile
186
- ? parseTokenValue(checkbox.checkboxTile.spacing.verticalPadding)
187
- : undefined
188
- }
189
- rootPaddingHorizontal={
190
- isTile
191
- ? parseTokenValue(checkbox.checkboxTile.spacing.horizontalPadding)
192
- : undefined
193
- }
194
- rootMaxWidth={
195
- isTile
196
- ? parseTokenValue(checkbox.checkboxTile.size.maxWidth)
197
- : undefined
198
- }
199
- rootBgColor={
200
- isTile
201
- ? isChecked
202
- ? checkbox.checkboxTile.colour.background.selected
203
- : checkbox.checkboxTile.colour.background.default
204
- : undefined
205
- }
206
- rootBorderWidth={
207
- isTile
208
- ? parseTokenValue(checkbox.checkboxTile.borderWidth.default)
209
- : undefined
210
- }
211
- rootBorderColor={
212
- isTile
213
- ? isChecked
214
- ? checkbox.checkboxTile.colour.border.selected
215
- : checkbox.checkboxTile.colour.border.default
216
- : undefined
217
- }
218
- rootBorderRadius={
219
- isTile
220
- ? parseTokenValue(checkbox.checkboxTile.borderRadius.default)
221
- : undefined
222
- }
223
- style={typeof style === "function" ? undefined : style}
224
- {...rest}
176
+ asChild
225
177
  >
226
- <StyledControl
227
- controlSize={controlSize}
228
- controlBorderWidth={parseTokenValue(checkbox.control.border.default)}
229
- controlBorderColor={
230
- isChecked
231
- ? checkbox.colour.background.selected
232
- : checkbox.colour.border.default
178
+ <StyledRootPressable
179
+ ref={ref}
180
+ rootFlexAlign={isTile ? "center" : "flex-start"}
181
+ rootGap={parseTokenValue(
182
+ isTile ? checkbox.checkboxTile.spacing.gap : checkbox.spacing.gap
183
+ )}
184
+ rootOpacity={
185
+ disabled ? parseFloat(theme.tokens.primitives.opacity["40"]) : 1
186
+ }
187
+ rootPaddingVertical={
188
+ isTile
189
+ ? parseTokenValue(checkbox.checkboxTile.spacing.verticalPadding)
190
+ : undefined
191
+ }
192
+ rootPaddingHorizontal={
193
+ isTile
194
+ ? parseTokenValue(checkbox.checkboxTile.spacing.horizontalPadding)
195
+ : undefined
196
+ }
197
+ rootMaxWidth={
198
+ isTile
199
+ ? parseTokenValue(checkbox.checkboxTile.size.maxWidth)
200
+ : undefined
201
+ }
202
+ rootBgColor={
203
+ isTile
204
+ ? isChecked
205
+ ? checkbox.checkboxTile.colour.background.selected
206
+ : checkbox.checkboxTile.colour.background.default
207
+ : undefined
233
208
  }
234
- controlBorderRadius={parseTokenValue(dimensions.borderRadius.xs)}
235
- controlBgColor={
236
- isChecked ? checkbox.colour.background.selected : "transparent"
209
+ rootBorderWidth={
210
+ isTile
211
+ ? parseTokenValue(checkbox.checkboxTile.borderWidth.default)
212
+ : undefined
237
213
  }
214
+ rootBorderColor={
215
+ isTile
216
+ ? isChecked
217
+ ? checkbox.checkboxTile.colour.border.selected
218
+ : checkbox.checkboxTile.colour.border.default
219
+ : undefined
220
+ }
221
+ rootBorderRadius={
222
+ isTile
223
+ ? parseTokenValue(checkbox.checkboxTile.borderRadius.default)
224
+ : undefined
225
+ }
226
+ style={typeof style === "function" ? undefined : style}
227
+ {...rest}
238
228
  >
239
- <CheckboxPrimitive.Indicator>
240
- <Icon icon={Check} size="xs" colour="alt" />
241
- </CheckboxPrimitive.Indicator>
242
- </StyledControl>
229
+ <StyledControl
230
+ controlSize={controlSize}
231
+ controlBorderWidth={parseTokenValue(
232
+ checkbox.control.border.default
233
+ )}
234
+ controlBorderColor={
235
+ isChecked
236
+ ? checkbox.colour.background.selected
237
+ : checkbox.colour.border.default
238
+ }
239
+ controlBorderRadius={parseTokenValue(dimensions.borderRadius.xs)}
240
+ controlBgColor={
241
+ isChecked ? checkbox.colour.background.selected : "transparent"
242
+ }
243
+ >
244
+ <CheckboxPrimitive.Indicator>
245
+ <Icon icon={Check} size="xs" colour="alt" />
246
+ </CheckboxPrimitive.Indicator>
247
+ </StyledControl>
243
248
 
244
- <StyledContent
245
- contentGap={parseTokenValue(checkbox.spacing.content.gap)}
246
- >
247
- {label && (
248
- <Typography
249
- token={checkbox.typography.label}
250
- color={checkbox.colour.text.title}
251
- >
252
- {label}
253
- </Typography>
254
- )}
255
- {subText && (
256
- <Typography
257
- token={checkbox.typography.subText}
258
- color={checkbox.colour.text.subtext}
249
+ <StyledContent
250
+ contentGap={parseTokenValue(checkbox.spacing.content.gap)}
251
+ >
252
+ {label && (
253
+ <Typography
254
+ token={checkbox.typography.label}
255
+ color={checkbox.colour.text.title}
256
+ >
257
+ {label}
258
+ </Typography>
259
+ )}
260
+ {subText && (
261
+ <Typography
262
+ token={checkbox.typography.subText}
263
+ color={checkbox.colour.text.subtext}
264
+ >
265
+ {subText}
266
+ </Typography>
267
+ )}
268
+ </StyledContent>
269
+
270
+ {isTile && illustration && (
271
+ <StyledIllustration
272
+ illustrationSize={parseTokenValue(
273
+ checkbox.checkboxTile.size.illustration.default
274
+ )}
259
275
  >
260
- {subText}
261
- </Typography>
276
+ {illustration}
277
+ </StyledIllustration>
262
278
  )}
263
- </StyledContent>
264
-
265
- {isTile && illustration && (
266
- <StyledIllustration
267
- illustrationSize={parseTokenValue(
268
- checkbox.checkboxTile.size.illustration.default
269
- )}
270
- >
271
- {illustration}
272
- </StyledIllustration>
273
- )}
274
- </StyledRoot>
279
+ </StyledRootPressable>
280
+ </CheckboxPrimitive.Root>
275
281
  )
276
282
  }
277
283
  )
@@ -0,0 +1,313 @@
1
+ import React, { useState } from "react"
2
+ import { View, StyleSheet } from "react-native"
3
+ import { CopyField } from "./CopyField"
4
+ import { Typography } from "../../atoms/Typography"
5
+ import { useCopyField } from "./hooks/useCopyField"
6
+ import type { InputState } from "../../atoms/Input/InputField"
7
+ import { Icon } from "../../atoms/Icon"
8
+ import { Lock } from "@butternutbox/pawprint-icons/core"
9
+
10
+ export default {
11
+ title: "Molecules/CopyField",
12
+ component: CopyField,
13
+ parameters: {
14
+ docs: {
15
+ description: {
16
+ component:
17
+ "Copy field component with a copy button that copies the input value to clipboard. Supports both simple props API and compound component API."
18
+ }
19
+ }
20
+ },
21
+ argTypes: {
22
+ label: {
23
+ control: { type: "text" },
24
+ description: "Label text"
25
+ },
26
+ placeholder: {
27
+ control: { type: "text" },
28
+ description: "Placeholder text"
29
+ },
30
+ description: {
31
+ control: { type: "text" },
32
+ description: "Help text below input"
33
+ },
34
+ error: {
35
+ control: { type: "text" },
36
+ description: "Error message"
37
+ },
38
+ state: {
39
+ control: { type: "select" },
40
+ options: ["default", "error", "success"],
41
+ description: "Visual state of the input"
42
+ },
43
+ optionalText: {
44
+ control: { type: "text" },
45
+ description: "Optional text to display next to label"
46
+ },
47
+ copyButtonLabel: {
48
+ control: { type: "text" },
49
+ description: "Label for the copy button"
50
+ },
51
+ editable: {
52
+ control: { type: "boolean" },
53
+ description: "Controls whether the input is editable"
54
+ }
55
+ }
56
+ }
57
+
58
+ export const Default = () => {
59
+ const copyProps = useCopyField({
60
+ onCopy: (value) => console.log("Copied:", value) // eslint-disable-line no-console
61
+ })
62
+
63
+ return (
64
+ <View style={styles.column}>
65
+ <View style={styles.section}>
66
+ <Typography size="sm" weight="semiBold" color="tertiary">
67
+ Using useCopyField Hook
68
+ </Typography>
69
+ <CopyField
70
+ {...copyProps}
71
+ label="API Key"
72
+ placeholder="Enter your API key..."
73
+ description="Your unique API key for authentication"
74
+ leadingIcon={<Icon icon={Lock} size="md" />}
75
+ />
76
+ </View>
77
+ </View>
78
+ )
79
+ }
80
+
81
+ export const States = () => {
82
+ const defaultProps = useCopyField()
83
+ const filledProps = useCopyField({ initialValue: "sk-1234567890abcdef" })
84
+ const errorProps = useCopyField({ initialValue: "invalid-key" })
85
+ const successProps = useCopyField({ initialValue: "sk-1234567890abcdef" })
86
+
87
+ return (
88
+ <View style={styles.column}>
89
+ <View style={styles.section}>
90
+ <Typography size="sm" weight="semiBold" color="tertiary">
91
+ Default State
92
+ </Typography>
93
+ <CopyField
94
+ {...defaultProps}
95
+ label="Default"
96
+ placeholder="Enter API key..."
97
+ description="Help text"
98
+ />
99
+ </View>
100
+ <View style={styles.section}>
101
+ <Typography size="sm" weight="semiBold" color="tertiary">
102
+ Disabled
103
+ </Typography>
104
+ <CopyField
105
+ label="Disabled"
106
+ placeholder="Enter API key..."
107
+ description="Help text"
108
+ editable={false}
109
+ />
110
+ </View>
111
+ <View style={styles.section}>
112
+ <Typography size="sm" weight="semiBold" color="tertiary">
113
+ With Value
114
+ </Typography>
115
+ <CopyField
116
+ {...filledProps}
117
+ label="With Value"
118
+ placeholder="Enter API key..."
119
+ description="Copy button enabled when field has text"
120
+ />
121
+ </View>
122
+ <View style={styles.section}>
123
+ <Typography size="sm" weight="semiBold" color="tertiary">
124
+ Success State
125
+ </Typography>
126
+ <CopyField
127
+ {...successProps}
128
+ label="Success State"
129
+ placeholder="Enter API key..."
130
+ state="success"
131
+ description="API key verified"
132
+ />
133
+ </View>
134
+ <View style={styles.section}>
135
+ <Typography size="sm" weight="semiBold" color="tertiary">
136
+ Error State
137
+ </Typography>
138
+ <CopyField
139
+ {...errorProps}
140
+ label="Error State"
141
+ placeholder="Enter API key..."
142
+ state="error"
143
+ error="Invalid API key format"
144
+ description="Help text"
145
+ />
146
+ </View>
147
+ </View>
148
+ )
149
+ }
150
+
151
+ export const ManualValidation = () => {
152
+ const [apiKey, setApiKey] = useState("")
153
+ const [apiKeyState, setApiKeyState] = useState<InputState>("default")
154
+
155
+ const handleChange = (newValue: string) => {
156
+ setApiKey(newValue)
157
+
158
+ if (!newValue) {
159
+ setApiKeyState("default")
160
+ return
161
+ }
162
+
163
+ const hasMinLength = newValue.length >= 10
164
+ const hasPrefix = newValue.startsWith("sk-")
165
+ const hasValidChars = /^sk-[a-z0-9]+$/.test(newValue)
166
+
167
+ const allRequirementsMet = hasMinLength && hasPrefix && hasValidChars
168
+
169
+ setApiKeyState(allRequirementsMet ? "success" : "error")
170
+ }
171
+
172
+ return (
173
+ <View style={styles.column}>
174
+ <View style={styles.section}>
175
+ <Typography size="sm" weight="semiBold" color="tertiary">
176
+ Manual Validation with Success State
177
+ </Typography>
178
+ <CopyField
179
+ label="API Key"
180
+ placeholder="sk-xxxxxxxxxxxxxxxx"
181
+ value={apiKey}
182
+ onValueChange={handleChange}
183
+ onCopy={(value) => console.log("Copied:", value)} // eslint-disable-line no-console
184
+ state={apiKeyState}
185
+ description="Must start with 'sk-', at least 10 chars, lowercase letters and numbers only"
186
+ error="Invalid API key format"
187
+ />
188
+ </View>
189
+ </View>
190
+ )
191
+ }
192
+
193
+ export const CompoundComponentAPI = () => {
194
+ const [apiKey, setApiKey] = useState("")
195
+ const [apiKeyState, setApiKeyState] = useState<InputState>("default")
196
+
197
+ const handleChange = (newValue: string) => {
198
+ setApiKey(newValue)
199
+
200
+ if (!newValue) {
201
+ setApiKeyState("default")
202
+ return
203
+ }
204
+
205
+ const hasMinLength = newValue.length >= 10
206
+ const hasPrefix = newValue.startsWith("sk-")
207
+ const hasValidChars = /^sk-[a-z0-9]+$/.test(newValue)
208
+
209
+ const allRequirementsMet = hasMinLength && hasPrefix && hasValidChars
210
+
211
+ setApiKeyState(allRequirementsMet ? "success" : "error")
212
+ }
213
+
214
+ return (
215
+ <View style={styles.column}>
216
+ <View style={styles.section}>
217
+ <Typography size="sm" weight="semiBold" color="tertiary">
218
+ Compound Component API
219
+ </Typography>
220
+ <CopyField.Root>
221
+ <CopyField.Label state={apiKeyState}>API Key</CopyField.Label>
222
+ <CopyField.Field
223
+ placeholder="sk-xxxxxxxxxxxxxxxx"
224
+ value={apiKey}
225
+ onValueChange={handleChange}
226
+ onCopy={(value) => console.log("Copied:", value)} // eslint-disable-line no-console
227
+ state={apiKeyState}
228
+ />
229
+ <CopyField.Description state={apiKeyState}>
230
+ Must start with 'sk-' and be at least 10 characters
231
+ </CopyField.Description>
232
+ {apiKeyState === "error" && apiKey.length > 0 && (
233
+ <CopyField.Error>Invalid API key format</CopyField.Error>
234
+ )}
235
+ </CopyField.Root>
236
+ </View>
237
+ </View>
238
+ )
239
+ }
240
+
241
+ export const WithOptionalText = () => (
242
+ <View style={styles.column}>
243
+ <View style={styles.section}>
244
+ <Typography size="sm" weight="semiBold" color="tertiary">
245
+ With Optional Text
246
+ </Typography>
247
+ <CopyField
248
+ label="API Key"
249
+ optionalText="(optional)"
250
+ placeholder="Enter API key..."
251
+ description="Optional API key field"
252
+ />
253
+ </View>
254
+ </View>
255
+ )
256
+
257
+ export const Controlled = () => {
258
+ const copyProps = useCopyField({
259
+ onCopy: (value) => console.log("Copied to clipboard:", value) // eslint-disable-line no-console
260
+ })
261
+
262
+ return (
263
+ <View style={styles.column}>
264
+ <View style={styles.section}>
265
+ <Typography size="sm" weight="semiBold" color="tertiary">
266
+ Controlled: {copyProps.value || "(empty)"}
267
+ </Typography>
268
+ <CopyField
269
+ {...copyProps}
270
+ label="API Key"
271
+ placeholder="Enter API key..."
272
+ description={`Current value: "${copyProps.value || "empty"}"`}
273
+ />
274
+ </View>
275
+ </View>
276
+ )
277
+ }
278
+
279
+ export const CustomButtonLabel = () => {
280
+ const copyProps = useCopyField({ initialValue: "sk-1234567890abcdef" })
281
+
282
+ return (
283
+ <View style={styles.column}>
284
+ <View style={styles.section}>
285
+ <Typography size="sm" weight="semiBold" color="tertiary">
286
+ Custom Copy Button Label
287
+ </Typography>
288
+ <CopyField
289
+ {...copyProps}
290
+ label="API Key"
291
+ placeholder="Enter API key..."
292
+ copyButtonLabel="Copy Key"
293
+ description="Custom button label example"
294
+ />
295
+ </View>
296
+ </View>
297
+ )
298
+ }
299
+
300
+ const styles = StyleSheet.create({
301
+ container: {
302
+ width: 320
303
+ },
304
+ column: {
305
+ flexDirection: "column",
306
+ gap: 24,
307
+ width: 320
308
+ },
309
+ section: {
310
+ flexDirection: "column",
311
+ gap: 8
312
+ }
313
+ })