@scripso-homepad/ui 0.3.6 → 0.3.8

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 (38) hide show
  1. package/README.md +238 -79
  2. package/dist/index.cjs +1225 -85
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +324 -18
  5. package/dist/index.d.ts +324 -18
  6. package/dist/index.js +1204 -89
  7. package/dist/index.js.map +1 -1
  8. package/package.json +18 -6
  9. package/src/components/Button.stories.tsx +77 -57
  10. package/src/components/Button.tsx +154 -123
  11. package/src/components/CountryCodeSelector.stories.tsx +61 -0
  12. package/src/components/CountryCodeSelector.tsx +273 -0
  13. package/src/components/Input.stories.tsx +126 -0
  14. package/src/components/Input.tsx +221 -0
  15. package/src/components/Label.tsx +56 -0
  16. package/src/components/PhoneInput.stories.tsx +85 -0
  17. package/src/components/PhoneInput.tsx +172 -0
  18. package/src/data/countries.ts +98 -0
  19. package/src/icons/ArrowUpRightIcon.tsx +29 -0
  20. package/src/icons/ArrowUpRightIcon.web.tsx +35 -0
  21. package/src/icons/ChevronDownIcon.tsx +29 -0
  22. package/src/icons/ChevronDownIcon.web.tsx +35 -0
  23. package/src/icons/EyeIcon.tsx +36 -0
  24. package/src/icons/EyeIcon.web.tsx +42 -0
  25. package/src/icons/EyeOffIcon.tsx +29 -0
  26. package/src/icons/EyeOffIcon.web.tsx +35 -0
  27. package/src/icons/KeyIcon.tsx +36 -0
  28. package/src/icons/KeyIcon.web.tsx +42 -0
  29. package/src/icons/arrowUpRightPath.ts +2 -0
  30. package/src/icons/chevronDownPath.ts +1 -0
  31. package/src/icons/eyeIconPaths.ts +8 -0
  32. package/src/icons/keyIconPaths.ts +5 -0
  33. package/src/index.ts +42 -0
  34. package/src/theme/input.ts +154 -0
  35. package/src/theme/tokens.ts +272 -0
  36. package/src/utils/countryDropdownScrollStyles.ts +52 -0
  37. package/src/utils/countryFlag.ts +9 -0
  38. package/src/utils/useApplyWebClassName.ts +3 -2
package/README.md CHANGED
@@ -1,35 +1,68 @@
1
1
  # @scripso-homepad/ui
2
2
 
3
- Cross-platform UI component library for Homepad. Components use React Native primitives and work in both **React Web** (via [react-native-web](https://necolas.github.io/react-native-web/)) and **React Native** (Expo).
3
+ Cross-platform UI component library for Homepad. Built with React Native primitives works in **Expo / React Native** and **React web** (via [react-native-web](https://necolas.github.io/react-native-web/)).
4
+
5
+ ## Components
6
+
7
+ | Component | Description |
8
+ |-----------|-------------|
9
+ | `Button` | Primary actions with variants, sizes, and optional icon badge |
10
+ | `Input` | Text field with label, left/right icons, error, and hint |
11
+ | `PhoneInput` | Country code selector + phone number field |
12
+ | `CountryCodeSelector` | Standalone country dial-code dropdown |
13
+ | `Label` | Form field label |
4
14
 
5
15
  ## Installation
6
16
 
17
+ ### React Native (Expo)
18
+
7
19
  ```bash
8
- npm install @scripso-homepad/ui react react-native
20
+ npm install @scripso-homepad/ui react-native-svg
9
21
  ```
10
22
 
11
- ### React Web
23
+ Expo resolves the package from TypeScript source automatically (`react-native` export). Icons use `react-native-svg` on native.
12
24
 
13
- Also install `react-native-web`:
25
+ ### React web
14
26
 
15
27
  ```bash
16
- npm install react-native-web
28
+ npm install @scripso-homepad/ui react-native-web
17
29
  ```
18
30
 
19
- Alias `react-native` → `react-native-web` in your bundler.
31
+ Configure your bundler to alias `react-native` → `react-native-web` and resolve `.web.tsx` icon files.
20
32
 
21
- #### Vite (React)
33
+ #### Vite
22
34
 
23
35
  ```ts
24
36
  // vite.config.ts
37
+ import path from "node:path";
38
+ import { existsSync } from "node:fs";
25
39
  import { defineConfig } from "vite";
26
40
  import react from "@vitejs/plugin-react";
27
41
 
42
+ function resolveUiPackageEntry() {
43
+ const workspaceEntry = path.resolve(__dirname, "../homepad-ui/src/index.ts");
44
+ if (existsSync(workspaceEntry)) return workspaceEntry;
45
+ return path.resolve(__dirname, "node_modules/@scripso-homepad/ui/src/index.ts");
46
+ }
47
+
28
48
  export default defineConfig({
29
- plugins: [react()],
49
+ plugins: [
50
+ react({
51
+ include: [
52
+ /\.[tj]sx?$/,
53
+ /node_modules\/(@scripso-homepad\/ui|react-native|react-native-web)/,
54
+ ],
55
+ }),
56
+ ],
30
57
  resolve: {
58
+ dedupe: ["react", "react-dom", "react-native", "react-native-web"],
31
59
  alias: {
32
60
  "react-native": "react-native-web",
61
+ react: path.resolve(__dirname, "node_modules/react"),
62
+ "react-dom": path.resolve(__dirname, "node_modules/react-dom"),
63
+ "react/jsx-runtime": path.resolve(__dirname, "node_modules/react/jsx-runtime"),
64
+ "react/jsx-dev-runtime": path.resolve(__dirname, "node_modules/react/jsx-dev-runtime"),
65
+ "@scripso-homepad/ui": resolveUiPackageEntry(),
33
66
  },
34
67
  extensions: [
35
68
  ".web.tsx",
@@ -43,7 +76,20 @@ export default defineConfig({
43
76
  ],
44
77
  },
45
78
  optimizeDeps: {
46
- include: ["react-native-web", "@scripso-homepad/ui"],
79
+ include: ["react", "react-dom", "react/jsx-runtime", "react-native-web"],
80
+ exclude: ["react-native-svg", "@scripso-homepad/ui"],
81
+ esbuildOptions: {
82
+ resolveExtensions: [
83
+ ".web.js",
84
+ ".web.jsx",
85
+ ".web.ts",
86
+ ".web.tsx",
87
+ ".js",
88
+ ".jsx",
89
+ ".ts",
90
+ ".tsx",
91
+ ],
92
+ },
47
93
  },
48
94
  });
49
95
  ```
@@ -59,31 +105,64 @@ module.exports = {
59
105
  ...config.resolve.alias,
60
106
  "react-native$": "react-native-web",
61
107
  };
108
+ config.resolve.extensions = [
109
+ ".web.tsx",
110
+ ".web.ts",
111
+ ".web.jsx",
112
+ ".web.js",
113
+ ...config.resolve.extensions,
114
+ ];
62
115
  return config;
63
116
  },
64
117
  };
65
118
  ```
66
119
 
67
- ### React Native (Expo)
68
-
69
- Install from npm — no extra config needed:
70
-
71
- ```bash
72
- npm install @scripso-homepad/ui
73
- ```
74
-
75
120
  ## Usage
76
121
 
77
122
  ```tsx
78
- import { Button } from "@scripso-homepad/ui";
79
-
80
- export function Example() {
123
+ import {
124
+ Button,
125
+ Input,
126
+ PhoneInput,
127
+ CountryCodeSelector,
128
+ colors,
129
+ } from "@scripso-homepad/ui";
130
+ import { KeyIcon } from "@scripso-homepad/ui/src/icons/KeyIcon";
131
+ import { EyeIcon } from "@scripso-homepad/ui/src/icons/EyeIcon";
132
+
133
+ export function SignUpForm() {
81
134
  return (
82
- <Button
83
- title="Press me"
84
- onPress={() => console.log("pressed")}
85
- disabled={false}
86
- />
135
+ <>
136
+ <Button
137
+ title="Continue"
138
+ variant="primary"
139
+ size="lg"
140
+ showIcon
141
+ onPress={() => {}}
142
+ className="w-full"
143
+ />
144
+
145
+ <Input
146
+ label="Password"
147
+ placeholder="placeholder"
148
+ leftIcon={<KeyIcon />}
149
+ rightIcon={<EyeIcon />}
150
+ hint="Use at least 8 characters"
151
+ secureTextEntry
152
+ />
153
+
154
+ <PhoneInput
155
+ label="Phone"
156
+ placeholder="placeholder"
157
+ hint="Include your area code"
158
+ onCountryCodeChange={(code) => console.log(code)}
159
+ />
160
+
161
+ <CountryCodeSelector
162
+ value="AM"
163
+ onValueChange={(code) => console.log(code)}
164
+ />
165
+ </>
87
166
  );
88
167
  }
89
168
  ```
@@ -92,93 +171,173 @@ export function Example() {
92
171
 
93
172
  ### `Button`
94
173
 
95
- | Prop | Type | Required | Default | Description |
96
- | --------------- | ------------------------------------------------- | -------- | ----------- | -------------------------------------------------------- |
97
- | `title` | `string` | Yes | — | Button label text |
98
- | `onPress` | `() => void` | Yes | — | Press handler |
99
- | `disabled` | `boolean` | No | `false` | Disables interaction |
100
- | `variant` | `"primary" \| "secondary" \| "outline" \| "ghost"` | No | `"primary"` | Visual style preset |
101
- | `size` | `"small" \| "medium" \| "large"` | No | `"medium"` | Size preset |
102
- | `style` | `StyleProp<ViewStyle>` | No | — | Extra container styles (web + native) |
103
- | `textStyle` | `StyleProp<TextStyle>` | No | | Extra label styles (web + native) |
104
- | `className` | `string` | No | — | CSS/Tailwind classes for container (web / NativeWind) |
105
- | `textClassName` | `string` | No | — | CSS/Tailwind classes for label (web / NativeWind) |
174
+ | Prop | Type | Default | Description |
175
+ |------|------|---------|-------------|
176
+ | `title` | `string` | — | Button label (use `children` for custom content) |
177
+ | `onPress` | `(event) => void` | — | Press handler |
178
+ | `variant` | `"white" \| "primary" \| "green" \| "gray"` | `"primary"` | Visual style |
179
+ | `size` | `"lg" \| "sm"` | `"lg"` | Size preset |
180
+ | `showIcon` | `boolean` | `false` | Show icon badge |
181
+ | `icon` | `ReactNode` | — | Custom icon (defaults to arrow icon when `showIcon` is true) |
182
+ | `disabled` | `boolean` | `false` | Disable interaction |
183
+ | `style` | `StyleProp<ViewStyle>` | — | Container styles |
184
+ | `textStyle` | `StyleProp<TextStyle>` | — | Label styles |
185
+ | `className` | `string` | — | CSS classes on container (web) |
186
+ | `textClassName` | `string` | — | CSS classes on label (web) |
187
+
188
+ ```tsx
189
+ <Button title="Save" variant="green" size="sm" showIcon onPress={handleSave} />
190
+ <Button title="Custom" showIcon icon={<MyIcon />} onPress={handleSave} />
191
+ ```
192
+
193
+ ### `Input`
194
+
195
+ Extends React Native `TextInput` props.
196
+
197
+ | Prop | Type | Default | Description |
198
+ |------|------|---------|-------------|
199
+ | `label` | `string` | — | Label above the field |
200
+ | `leftIcon` | `ReactNode` | — | Icon on the left (20px, color follows state) |
201
+ | `rightIcon` | `ReactNode` | — | Icon on the right |
202
+ | `error` | `string` | — | Error message (shown instead of hint) |
203
+ | `hint` | `string` | — | Helper text below the field |
204
+ | `className` | `string` | — | Wrapper CSS classes (web) |
205
+ | `labelClassName` | `string` | — | Label CSS classes (web) |
206
+ | `inputClassName` | `string` | — | Input CSS classes (web) |
207
+ | `errorClassName` | `string` | — | Error message CSS classes (web) |
208
+ | `hintClassName` | `string` | — | Hint message CSS classes (web) |
209
+ | `containerStyle` | `StyleProp<ViewStyle>` | — | Wrapper styles |
210
+ | `style` | `StyleProp<TextStyle>` | — | Input text styles |
211
+
212
+ Field height is **52px**. Icons receive `size` and `color` automatically when they accept those props.
213
+
214
+ ### `PhoneInput`
215
+
216
+ Same props as `Input`, plus:
217
+
218
+ | Prop | Type | Description |
219
+ |------|------|-------------|
220
+ | `countryCode` | `string` | Selected country code (e.g. `"AM"`) |
221
+ | `onCountryCodeChange` | `(code: string) => void` | Country selection handler |
106
222
 
107
- #### Variants and sizes
223
+ Error and hint messages render under the phone number field (not under the country selector).
224
+
225
+ ### `CountryCodeSelector`
226
+
227
+ | Prop | Type | Default | Description |
228
+ |------|------|---------|-------------|
229
+ | `value` | `string` | `"AM"` | Selected country code |
230
+ | `onValueChange` | `(code: string) => void` | — | Selection handler |
231
+ | `options` | `Country[]` | built-in list | Country list override |
232
+ | `disabled` | `boolean` | `false` | Disable interaction |
233
+ | `style` | `StyleProp<ViewStyle>` | — | Wrapper styles |
234
+ | `className` | `string` | — | CSS classes (web) |
235
+
236
+ Also exports `countries`, `defaultCountry`, and `findCountry` for country data.
237
+
238
+ ### `Label`
239
+
240
+ | Prop | Type | Default | Description |
241
+ |------|------|---------|-------------|
242
+ | `children` | `ReactNode` | — | Label text |
243
+ | `required` | `boolean` | `false` | Show required indicator |
244
+ | `disabled` | `boolean` | `false` | Muted disabled color |
245
+ | `className` | `string` | — | CSS classes (web) |
246
+
247
+ ## Design tokens
108
248
 
109
249
  ```tsx
110
- <Button title="Save" onPress={handleSave} variant="primary" size="medium" />
111
- <Button title="Cancel" onPress={handleCancel} variant="outline" size="small" />
112
- <Button title="Delete" onPress={handleDelete} variant="secondary" size="large" />
113
- <Button title="More" onPress={handleMore} variant="ghost" />
250
+ import {
251
+ colors,
252
+ brand,
253
+ stormGray,
254
+ navy,
255
+ rubyRed,
256
+ emeraldGreen,
257
+ spacing,
258
+ radii,
259
+ fonts,
260
+ buttonTypography,
261
+ labelTypography,
262
+ } from "@scripso-homepad/ui";
114
263
  ```
115
264
 
116
- Preview all combinations in Storybook: `npm run storybook` → **AllVariants**, **AllSizes**, **VariantMatrix**.
265
+ ### Brand colors
266
+
267
+ | Name | Token | Hex |
268
+ |------|-------|-----|
269
+ | Slate Blue | `brand.slateBlue` | `#182E3C` |
270
+ | Storm Gray | `brand.stormGray` | `#455765` |
271
+ | Navy | `brand.navy` | `#08275D` |
272
+ | Ruby Red | `brand.rubyRed` | `#AE0011` |
273
+ | Emerald Green | `brand.emeraldGreen` | `#279D54` |
117
274
 
118
- #### Custom styles (React Native `style`)
275
+ ### Color scales
276
+
277
+ Each palette has steps `0`, `0.5`, `1`, `1.5`, `2` … `8`, `8.5`, exposed as flat tokens:
119
278
 
120
279
  ```tsx
121
- <Button
122
- title="Save"
123
- onPress={handleSave}
124
- style={{ backgroundColor: "#dc2626", borderRadius: 999 }}
125
- textStyle={{ fontSize: 14, textTransform: "uppercase" }}
126
- />
280
+ colors.stormGray0 // #FBFCFC
281
+ colors.stormGray50 // #ECEEF0 (Figma 0.5)
282
+ colors.stormGray500 // #455765 (brand)
283
+ colors.stormGray850 // #151A1E (Figma 8.5)
284
+
285
+ colors.navy500 // brand navy
286
+ colors.rubyRed500 // brand ruby red
287
+ colors.emeraldGreen500
127
288
  ```
128
289
 
129
- #### Tailwind classes (React web)
290
+ Or use the scale objects: `stormGray["0.5"]`, `navy["5"]`, `rubyRed["5"]`, `emeraldGreen["5"]`.
291
+
292
+ ## Styling on web
130
293
 
131
- `className` is applied to the same `TouchableOpacity` / `Text` DOM nodes as the default styles layout and padding stay consistent with native:
294
+ `className` props apply Tailwind / CSS classes to the underlying DOM node on web only. On native they are no-ops.
132
295
 
133
296
  ```tsx
134
297
  <Button
135
- title="Save"
136
- onPress={handleSave}
137
- className="!bg-violet-600 shadow-lg"
138
- textClassName="text-sm font-bold uppercase"
298
+ title="Submit"
299
+ onPress={handleSubmit}
300
+ className="w-full max-w-md"
139
301
  />
140
- ```
141
302
 
142
- Use `!` (important) on Tailwind utilities when overriding default colors, e.g. `!bg-red-500`, because react-native-web applies inline styles from `StyleSheet`.
303
+ <Input
304
+ label="Email"
305
+ className="w-full"
306
+ inputClassName="text-base"
307
+ />
308
+ ```
143
309
 
144
- Ensure Tailwind scans the package if needed:
310
+ When overriding default colors with Tailwind, use `!` (important) because react-native-web applies inline styles:
145
311
 
146
- ```js
147
- content: ["./src/**/*.{js,ts,jsx,tsx}", "./node_modules/@scripso-homepad/ui/**/*.{js,ts,jsx,tsx}"],
312
+ ```tsx
313
+ <Button title="Delete" className="!bg-red-600" onPress={handleDelete} />
148
314
  ```
149
315
 
150
316
  ## Development
151
317
 
152
318
  ```bash
153
319
  npm install
154
- npm run build
155
320
  npm run typecheck
156
321
  npm run lint
322
+ npm run build
323
+ npm run storybook # http://localhost:6006
324
+ npm run build-storybook
157
325
  ```
158
326
 
159
- ### Storybook
160
-
161
- Preview components in the browser with hot reload:
327
+ Clear Storybook cache if needed:
162
328
 
163
329
  ```bash
164
- npm run storybook
330
+ rm -rf node_modules/.cache/storybook node_modules/.vite
165
331
  ```
166
332
 
167
- Opens at [http://localhost:6006](http://localhost:6006). Components render via `react-native-web`.
168
-
169
- Build a static Storybook site:
333
+ ## Architecture
170
334
 
171
- ```bash
172
- npm run build-storybook
173
- ```
335
+ - **Shared implementation** — one component per platform, React Native primitives throughout
336
+ - **Web icons** — `.web.tsx` files use plain SVG (no `react-native-svg` on web)
337
+ - **Native icons** — `react-native-svg` (peer dependency)
338
+ - **Source resolution** — bundlers import from `src/index.ts` for correct `.web.tsx` resolution
339
+ - **Metro / Expo** — `"react-native"` package export points to TypeScript source
174
340
 
175
341
  ## Publishing
176
342
 
177
- Push to `main` — CI auto-bumps the patch version and publishes to npm. See [PUBLISHING.md](./PUBLISHING.md).
178
-
179
- ## Architecture
180
-
181
- - **Single shared implementation** — no `.web.tsx` / `.native.tsx` split
182
- - **React Native primitives** — `TouchableOpacity`, `Text`, `StyleSheet`
183
- - **Web via react-native-web** — consumers alias `react-native` at build time
184
- - **Metro source resolution** — `"react-native"` export points to TypeScript source for native bundlers
343
+ See [PUBLISHING.md](./PUBLISHING.md).