@scripso-homepad/ui 0.3.5 → 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.
@@ -0,0 +1,272 @@
1
+ /**
2
+ * HomePad design tokens — aligned with the Figma design system
3
+ * (navy / storm gray / slate-blue palette from HomePad brand).
4
+ */
5
+
6
+ /** Storm Gray scale — Figma steps 0 → 8.5 */
7
+ export const stormGray = {
8
+ "0": "#fbfcfc",
9
+ "0.5": "#eceef0",
10
+ "1": "#dadde0",
11
+ "1.5": "#c7cdd1",
12
+ "2": "#b5bcc1",
13
+ "3": "#8f9aa3",
14
+ "4": "#6a7984",
15
+ "5": "#455765",
16
+ "6": "#374651",
17
+ "7": "#29343d",
18
+ "8": "#1c2328",
19
+ "8.5": "#151a1e",
20
+ } as const;
21
+
22
+ export type StormGrayStep = keyof typeof stormGray;
23
+
24
+ /** Navy scale — Figma steps 0 → 8.5 */
25
+ export const navy = {
26
+ "0": "#f8f9fb",
27
+ "0.5": "#e5e9ef",
28
+ "1": "#cdd3df",
29
+ "1.5": "#b4bece",
30
+ "2": "#9ca8be",
31
+ "3": "#6a7d9e",
32
+ "4": "#39527d",
33
+ "5": "#08275d",
34
+ "6": "#061f4a",
35
+ "7": "#051738",
36
+ "8": "#031025",
37
+ "8.5": "#020c1c",
38
+ } as const;
39
+
40
+ export type NavyStep = keyof typeof navy;
41
+
42
+ /** Ruby Red scale — Figma steps 0 → 8.5 */
43
+ export const rubyRed = {
44
+ "0": "#fff5f6",
45
+ "0.5": "#f7dddf",
46
+ "1": "#efc4c8",
47
+ "1.5": "#e7acb1",
48
+ "2": "#df939a",
49
+ "3": "#ce626d",
50
+ "4": "#be313f",
51
+ "5": "#ae0011",
52
+ "6": "#8b000e",
53
+ "7": "#68000a",
54
+ "8": "#460007",
55
+ "8.5": "#340005",
56
+ } as const;
57
+
58
+ export type RubyRedStep = keyof typeof rubyRed;
59
+
60
+ /** Emerald Green scale — Figma steps 0 → 8.5 */
61
+ export const emeraldGreen = {
62
+ "0": "#f0fbf5",
63
+ "0.5": "#dcf2e5",
64
+ "1": "#c8e8d5",
65
+ "1.5": "#b4dfc5",
66
+ "2": "#a0d5b5",
67
+ "3": "#77c394",
68
+ "4": "#4fb074",
69
+ "5": "#279d54",
70
+ "6": "#1f7e43",
71
+ "7": "#175e32",
72
+ "8": "#103f22",
73
+ "8.5": "#0c2f19",
74
+ } as const;
75
+
76
+ export type EmeraldGreenStep = keyof typeof emeraldGreen;
77
+
78
+ /** Figma brand palette — primary swatch per color family */
79
+ export const brand = {
80
+ slateBlue: "#182e3c",
81
+ stormGray: stormGray["5"],
82
+ navy: navy["5"],
83
+ rubyRed: rubyRed["5"],
84
+ emeraldGreen: emeraldGreen["5"],
85
+ } as const;
86
+
87
+ export const colors = {
88
+ // Brand colors
89
+ slateBlue: brand.slateBlue,
90
+
91
+ // Emerald Green — Figma scale + brand swatch
92
+ emeraldGreen0: emeraldGreen["0"],
93
+ emeraldGreen50: emeraldGreen["0.5"],
94
+ emeraldGreen100: emeraldGreen["1"],
95
+ emeraldGreen150: emeraldGreen["1.5"],
96
+ emeraldGreen200: emeraldGreen["2"],
97
+ emeraldGreen300: emeraldGreen["3"],
98
+ emeraldGreen400: emeraldGreen["4"],
99
+ emeraldGreen500: emeraldGreen["5"],
100
+ emeraldGreen600: emeraldGreen["6"],
101
+ emeraldGreen700: emeraldGreen["7"],
102
+ emeraldGreen800: emeraldGreen["8"],
103
+ emeraldGreen850: emeraldGreen["8.5"],
104
+
105
+ /** Primary brand emerald green — Figma Emerald Green 5 */
106
+ emeraldGreen: brand.emeraldGreen,
107
+
108
+ // Ruby Red — Figma scale + brand swatch
109
+ rubyRed0: rubyRed["0"],
110
+ rubyRed50: rubyRed["0.5"],
111
+ rubyRed100: rubyRed["1"],
112
+ rubyRed150: rubyRed["1.5"],
113
+ rubyRed200: rubyRed["2"],
114
+ rubyRed300: rubyRed["3"],
115
+ rubyRed400: rubyRed["4"],
116
+ rubyRed500: rubyRed["5"],
117
+ rubyRed600: rubyRed["6"],
118
+ rubyRed700: rubyRed["7"],
119
+ rubyRed800: rubyRed["8"],
120
+ rubyRed850: rubyRed["8.5"],
121
+
122
+ /** Primary brand ruby red — Figma Ruby Red 5 */
123
+ rubyRed: brand.rubyRed,
124
+
125
+ // Navy — Figma scale + brand swatch
126
+ navy0: navy["0"],
127
+ navy50: navy["0.5"],
128
+ navy100: navy["1"],
129
+ navy150: navy["1.5"],
130
+ navy200: navy["2"],
131
+ navy300: navy["3"],
132
+ navy400: navy["4"],
133
+ navy500: navy["5"],
134
+ navy600: navy["6"],
135
+ navy700: navy["7"],
136
+ navy800: navy["8"],
137
+ navy850: navy["8.5"],
138
+
139
+ /** Primary brand navy — Figma Navy 5 */
140
+ navy: brand.navy,
141
+
142
+ // Storm Gray — Figma scale + brand swatch
143
+ stormGray0: stormGray["0"],
144
+ stormGray50: stormGray["0.5"],
145
+ stormGray100: stormGray["1"],
146
+ stormGray150: stormGray["1.5"],
147
+ stormGray200: stormGray["2"],
148
+ stormGray300: stormGray["3"],
149
+ stormGray400: stormGray["4"],
150
+ stormGray500: stormGray["5"],
151
+ /** Figma brand swatch "Storm Gray" — same as `stormGray500` */
152
+ stormGrayBrand: brand.stormGray,
153
+ stormGray600: stormGray["6"],
154
+ stormGray700: stormGray["7"],
155
+ stormGray800: stormGray["8"],
156
+ stormGray850: stormGray["8.5"],
157
+
158
+ /** @deprecated Use `stormGray0` */
159
+ storm0: stormGray["0"],
160
+ /** @deprecated Use `stormGray50` */
161
+ storm50: stormGray["0.5"],
162
+ /** @deprecated Use `stormGray200` */
163
+ storm200: stormGray["2"],
164
+ /** @deprecated Use `stormGray300` */
165
+ storm300: stormGray["3"],
166
+ /** @deprecated Use `stormGray500` */
167
+ storm500: stormGray["5"],
168
+ /** @deprecated Use `stormGray700` */
169
+ storm700: stormGray["7"],
170
+ /** @deprecated Use `stormGray850` */
171
+ storm900: stormGray["8.5"],
172
+
173
+ countrySelectorSelectedBg: `${stormGray["0.5"]}99`,
174
+
175
+ white: "#ffffff",
176
+ error: "#dc2626",
177
+ errorDark: "#b3261e",
178
+ errorLight: "#fee2e2",
179
+ /** @deprecated Use `rubyRed` */
180
+ inputError: brand.rubyRed,
181
+ inputOutlineFocus: navy["0.5"],
182
+ inputOutlineError: rubyRed["0.5"],
183
+ warning: "#92400e",
184
+ warningBg: "#fef3c7",
185
+ success: "#28a745",
186
+ successMuted: "#9fd4a8",
187
+ /** @deprecated Use `emeraldGreen` */
188
+ green: brand.emeraldGreen,
189
+ transparent: "transparent",
190
+ } as const;
191
+
192
+ export const radii = {
193
+ sm: 8,
194
+ md: 12,
195
+ lg: 16,
196
+ xl: 20,
197
+ full: 9999,
198
+ } as const;
199
+
200
+ export const spacing = {
201
+ xs: 4,
202
+ sm: 8,
203
+ md: 12,
204
+ lg: 16,
205
+ xl: 24,
206
+ xxl: 32,
207
+ } as const;
208
+
209
+ export const fontSize = {
210
+ xs: 11,
211
+ sm: 12,
212
+ md: 14,
213
+ base: 16,
214
+ lg: 18,
215
+ xl: 24,
216
+ xxl: 32,
217
+ } as const;
218
+
219
+ export const fontWeight = {
220
+ regular: "400" as const,
221
+ medium: "500" as const,
222
+ semibold: "600" as const,
223
+ bold: "700" as const,
224
+ };
225
+
226
+ export const fonts = {
227
+ sans: "Noto Sans Armenian",
228
+ } as const;
229
+
230
+ /** Input label typography — matches Figma (Medium, 100% line-height). */
231
+ export const labelTypography = {
232
+ fontFamily: fonts.sans,
233
+ fontSize: fontSize.sm,
234
+ fontWeight: fontWeight.medium,
235
+ lineHeight: fontSize.sm,
236
+ letterSpacing: 0,
237
+ } as const;
238
+
239
+ /** Button label typography — matches Figma (SemiBold, 100% line-height). */
240
+ export const buttonTypography = {
241
+ lg: {
242
+ fontFamily: fonts.sans,
243
+ fontSize: 14,
244
+ fontWeight: fontWeight.semibold,
245
+ lineHeight: 14,
246
+ letterSpacing: 0,
247
+ },
248
+ sm: {
249
+ fontFamily: fonts.sans,
250
+ fontSize: 12,
251
+ fontWeight: fontWeight.semibold,
252
+ lineHeight: 12,
253
+ letterSpacing: 0,
254
+ },
255
+ } as const;
256
+
257
+ export const shadows = {
258
+ card: {
259
+ shadowColor: colors.navy,
260
+ shadowOffset: { width: 0, height: 4 },
261
+ shadowOpacity: 0.05,
262
+ shadowRadius: 20,
263
+ elevation: 2,
264
+ },
265
+ cardLg: {
266
+ shadowColor: colors.navy,
267
+ shadowOffset: { width: 0, height: 4 },
268
+ shadowOpacity: 0.1,
269
+ shadowRadius: 20,
270
+ elevation: 4,
271
+ },
272
+ } as const;
@@ -0,0 +1,52 @@
1
+ import { Platform } from "react-native";
2
+ import { colors } from "../theme/tokens";
3
+
4
+ export const COUNTRY_DROPDOWN_SCROLL_CLASS = "hp-country-dropdown-scroll";
5
+
6
+ const STYLE_ID = "hp-country-dropdown-scroll-styles";
7
+
8
+ export const countryDropdownScrollCss = `
9
+ .${COUNTRY_DROPDOWN_SCROLL_CLASS} {
10
+ scrollbar-width: thin;
11
+ scrollbar-color: ${colors.stormGray300} transparent;
12
+ overflow-y: auto;
13
+ }
14
+
15
+ .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar {
16
+ width: 4px;
17
+ }
18
+
19
+ .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar-corner {
20
+ background: transparent;
21
+ }
22
+
23
+ .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar-button,
24
+ .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar-button:single-button,
25
+ .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar-button:vertical:start:decrement,
26
+ .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar-button:vertical:end:increment {
27
+ display: none;
28
+ width: 0;
29
+ height: 0;
30
+ -webkit-appearance: none;
31
+ appearance: none;
32
+ }
33
+
34
+ .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar-track {
35
+ background: transparent;
36
+ }
37
+
38
+ .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar-thumb {
39
+ background-color: ${colors.stormGray300};
40
+ border-radius: 9999px;
41
+ }
42
+ `;
43
+
44
+ export function ensureCountryDropdownScrollStyles(): void {
45
+ if (Platform.OS !== "web" || typeof document === "undefined") return;
46
+ if (document.getElementById(STYLE_ID)) return;
47
+
48
+ const style = document.createElement("style");
49
+ style.id = STYLE_ID;
50
+ style.textContent = countryDropdownScrollCss;
51
+ document.head.appendChild(style);
52
+ }
@@ -0,0 +1,9 @@
1
+ /** ISO 3166-1 alpha-2 country code → flag emoji (cross-platform). */
2
+ export function countryCodeToFlagEmoji(code: string): string {
3
+ const upper = code.trim().toUpperCase();
4
+ if (upper.length !== 2) return "";
5
+
6
+ return String.fromCodePoint(
7
+ ...upper.split("").map((char) => 0x1f1e6 - 65 + char.charCodeAt(0)),
8
+ );
9
+ }
@@ -45,9 +45,10 @@ function resolveWebElement(ref: React.RefObject<unknown>): ClassListElement | nu
45
45
  export function useApplyWebClassName(
46
46
  ref: React.RefObject<unknown>,
47
47
  className?: string,
48
+ enabled = true,
48
49
  ): void {
49
50
  useLayoutEffect(() => {
50
- if (Platform.OS !== "web" || !className?.trim()) return;
51
+ if (!enabled || Platform.OS !== "web" || !className?.trim()) return;
51
52
 
52
53
  const element = resolveWebElement(ref);
53
54
  if (!element) return;
@@ -58,5 +59,5 @@ export function useApplyWebClassName(
58
59
  return () => {
59
60
  element.classList.remove(...classes);
60
61
  };
61
- }, [ref, className]);
62
+ }, [ref, className, enabled]);
62
63
  }