@scripso-homepad/ui 0.3.6 → 0.3.7

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