@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.
- package/README.md +238 -79
- package/dist/index.cjs +1225 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +324 -18
- package/dist/index.d.ts +324 -18
- package/dist/index.js +1204 -89
- package/dist/index.js.map +1 -1
- package/package.json +18 -6
- package/src/components/Button.stories.tsx +77 -57
- package/src/components/Button.tsx +154 -123
- package/src/components/CountryCodeSelector.stories.tsx +61 -0
- package/src/components/CountryCodeSelector.tsx +273 -0
- package/src/components/Input.stories.tsx +126 -0
- package/src/components/Input.tsx +221 -0
- package/src/components/Label.tsx +56 -0
- package/src/components/PhoneInput.stories.tsx +85 -0
- package/src/components/PhoneInput.tsx +172 -0
- package/src/data/countries.ts +98 -0
- package/src/icons/ArrowUpRightIcon.tsx +29 -0
- package/src/icons/ArrowUpRightIcon.web.tsx +35 -0
- package/src/icons/ChevronDownIcon.tsx +29 -0
- package/src/icons/ChevronDownIcon.web.tsx +35 -0
- package/src/icons/EyeIcon.tsx +36 -0
- package/src/icons/EyeIcon.web.tsx +42 -0
- package/src/icons/EyeOffIcon.tsx +29 -0
- package/src/icons/EyeOffIcon.web.tsx +35 -0
- package/src/icons/KeyIcon.tsx +36 -0
- package/src/icons/KeyIcon.web.tsx +42 -0
- package/src/icons/arrowUpRightPath.ts +2 -0
- package/src/icons/chevronDownPath.ts +1 -0
- package/src/icons/eyeIconPaths.ts +8 -0
- package/src/icons/keyIconPaths.ts +5 -0
- package/src/index.ts +42 -0
- package/src/theme/input.ts +154 -0
- package/src/theme/tokens.ts +272 -0
- package/src/utils/countryDropdownScrollStyles.ts +52 -0
- package/src/utils/countryFlag.ts +9 -0
- 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.
|
|
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
|
|
20
|
+
npm install @scripso-homepad/ui react-native-svg
|
|
9
21
|
```
|
|
10
22
|
|
|
11
|
-
|
|
23
|
+
Expo resolves the package from TypeScript source automatically (`react-native` export). Icons use `react-native-svg` on native.
|
|
12
24
|
|
|
13
|
-
|
|
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
|
-
|
|
31
|
+
Configure your bundler to alias `react-native` → `react-native-web` and resolve `.web.tsx` icon files.
|
|
20
32
|
|
|
21
|
-
#### Vite
|
|
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: [
|
|
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-
|
|
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 {
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
96
|
-
|
|
97
|
-
| `title`
|
|
98
|
-
| `onPress`
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
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
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
290
|
+
Or use the scale objects: `stormGray["0.5"]`, `navy["5"]`, `rubyRed["5"]`, `emeraldGreen["5"]`.
|
|
291
|
+
|
|
292
|
+
## Styling on web
|
|
130
293
|
|
|
131
|
-
`className`
|
|
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="
|
|
136
|
-
onPress={
|
|
137
|
-
className="
|
|
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
|
-
|
|
303
|
+
<Input
|
|
304
|
+
label="Email"
|
|
305
|
+
className="w-full"
|
|
306
|
+
inputClassName="text-base"
|
|
307
|
+
/>
|
|
308
|
+
```
|
|
143
309
|
|
|
144
|
-
|
|
310
|
+
When overriding default colors with Tailwind, use `!` (important) because react-native-web applies inline styles:
|
|
145
311
|
|
|
146
|
-
```
|
|
147
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
Preview components in the browser with hot reload:
|
|
327
|
+
Clear Storybook cache if needed:
|
|
162
328
|
|
|
163
329
|
```bash
|
|
164
|
-
|
|
330
|
+
rm -rf node_modules/.cache/storybook node_modules/.vite
|
|
165
331
|
```
|
|
166
332
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
Build a static Storybook site:
|
|
333
|
+
## Architecture
|
|
170
334
|
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
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).
|