huspy-icons 0.1.1 → 0.1.2

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,157 +1,125 @@
1
1
  # huspy-icons
2
2
 
3
- A cross-platform icon package generated from raw SVGs. Ships separate builds for **web (React / Next.js)** and **native (Expo / React Native)** with a consistent API and first-class tree-shaking.
3
+ A cross-platform icon package generated from raw SVGs. Ships separate builds for **web (React / Next.js)** with **SVG icons** and **native (Expo / React Native)** with **font-based icons** for optimal performance.
4
4
 
5
5
  ---
6
6
 
7
7
  ## ✨ Features
8
8
 
9
9
  * **Single source of truth**: raw SVGs → codegen → typed components
10
- * **Two targets**: `huspy-icons/react` (web) and `huspy-icons/native` (expo)
11
- * **Tree-shakable** per-icon files for optimal bundle size
12
- * **Theming ready**: `color="currentColor"` by default; size tokens
13
- * **A11y** for web (`title`, `aria-hidden`) and native (`accessibilityLabel`)
14
- * **Scales** to hundreds of icons with auto-generated exports and types
10
+ * **Two rendering strategies**:
11
+ - **Web**: Inline SVG (tree-shakable, full styling control)
12
+ - **Native**: Font-based icons (better performance, smaller bundle)
13
+ * **Unified API**: Same `<Icon name="..." />` syntax for both platforms
14
+ * **TypeScript-first**: Auto-generated types with full autocomplete
15
+ * **Theming ready**: `color` and `size` props on all icons
16
+ * **A11y**: Proper accessibility labels for both platforms
17
+ * **Scales**: Hundreds of icons with auto-generated exports
15
18
 
16
19
  ---
17
20
 
18
- ## 📦 Install
21
+ ## 📦 Installation
19
22
 
20
- > Publish this package to your registry (npm/GitHub Packages) and install in each app.
21
-
22
- **Next.js (web):**
23
+ **Next.js / React (Web):**
23
24
 
24
25
  ```bash
25
26
  npm install huspy-icons
26
27
  ```
27
28
 
28
- **Expo (native):**
29
+ **Expo / React Native:**
29
30
 
30
31
  ```bash
31
- npx expo install react-native-svg
32
32
  npm install huspy-icons
33
33
  ```
34
34
 
35
- > `react` build has no extra peer deps. `native` build peers `react-native-svg`.
35
+ > **Note**: Native build uses custom fonts - no need for `react-native-svg`!
36
+ > See [FONT_SETUP.md](./FONT_SETUP.md) for font installation instructions.
36
37
 
37
38
  ---
38
39
 
39
40
  ## 🚀 Quick Start
40
41
 
41
- **Web (React / Next.js)**
42
+ ### Web (React / Next.js)
42
43
 
43
44
  ```tsx
44
- import { ArrowLeft } from 'huspy-icons/react';
45
+ import { Icon } from 'huspy-icons/react';
45
46
 
46
47
  export default function Example() {
47
48
  return (
48
- <div style={{ color: '#111' }}>
49
- <ArrowLeft size="md" />
49
+ <div>
50
+ <Icon name="arrow-left" size={24} color="#000" />
51
+ <Icon name="arrow-up-right" size={32} color="#6b7280" />
50
52
  </div>
51
53
  );
52
54
  }
53
55
  ```
54
56
 
55
- **Expo (React Native)**
56
-
57
- ```tsx
58
- import { ArrowLeft } from 'huspy-icons/native';
59
-
60
- export default function Example() {
61
- return <ArrowLeft size={20} color="#111" accessibilityLabel="Back" />;
62
- }
63
- ```
64
-
65
- ---
57
+ ### Expo / React Native
66
58
 
67
- ## 🗂️ Repo Structure (package)
59
+ **1. First, set up the font** (one-time setup):
68
60
 
69
- ```
70
- packages/huspy-icons/
71
- icons-src/ # raw .svg files (single source of truth)
72
- src/
73
- react/ # generated TSX for web
74
- native/ # generated TSX for expo
75
- shared/ # helper types/utilities (createIcon, resolveSize)
76
- scripts/ # codegen utilities (svgr, registry, typedefs)
77
- svgo.config.js
78
- package.json
79
- tsconfig.json
80
- ```
61
+ See [FONT_SETUP.md](./FONT_SETUP.md) for detailed instructions.
81
62
 
82
- ---
63
+ **2. Use the icons:**
83
64
 
84
- ## ⚙️ Scripts
65
+ ```tsx
66
+ import { Icon } from 'huspy-icons/native';
85
67
 
86
- ```json
87
- {
88
- "scripts": {
89
- "svgo": "svgo -f icons-src -o icons-src --config=svgo.config.js",
90
- "gen:react": "svgr icons-src -d src/react --ext tsx --typescript --icon",
91
- "gen:native": "svgr --native icons-src -d src/native --ext tsx --typescript --icon",
92
- "gen": "npm run svgo && npm run gen:react && npm run gen:native",
93
- "build": "tsup src/**/index.ts --format esm,cjs --dts --out-dir dist --clean",
94
- "prepare": "npm run gen && npm run build"
95
- }
68
+ export default function Example() {
69
+ return (
70
+ <>
71
+ <Icon name="arrow-left" size={24} color="#000" />
72
+ <Icon name="arrow-up-right" size={32} color="#6b7280" />
73
+ </>
74
+ );
96
75
  }
97
76
  ```
98
77
 
99
- * **`svgo`**: normalize SVGs (remove width/height, hardcoded colors).
100
- * **`gen:*`**: generate typed components for each target using SVGR.
101
- * **`build`**: bundle per-target outputs with types.
102
-
103
78
  ---
104
79
 
105
- ## 🔁 Usage Patterns
80
+ ## 🎨 API
106
81
 
107
- ### Named components (preferred)
82
+ Both platforms use the same API:
108
83
 
109
84
  ```tsx
110
- import { ArrowLeft, ArrowUpRight } from 'huspy-icons/react';
111
- <ArrowLeft size="lg" />
112
- <ArrowUpRight size={20} color="#6b7280" />
85
+ <Icon
86
+ name="arrow-left" // Icon name (TypeScript autocomplete available)
87
+ size={24} // Size in pixels (default: 16)
88
+ color="#000" // Any valid color (default: 'currentColor' for web, '#000' for native)
89
+ />
113
90
  ```
114
91
 
115
- * Best **tree-shaking**, great DX, no runtime lookup.
116
-
117
- ---
118
-
119
- ## 🎨 Props & Theming
120
-
121
- All icons share a consistent API:
92
+ ### Available Icons
122
93
 
123
- * `size`: `number | 'xs' | 'sm' | 'md' | 'lg' | 'xl'` (defaults to `lg` ≈ 20)
124
- * `color`: any CSS color (web) or string color (native), defaults to `currentColor`
125
- * Web a11y: `title?`, `aria-hidden?` (decorative by default)
126
- * Native a11y: `accessibilityLabel?`
127
-
128
- > Because paths default to `currentColor`, icons inherit text color out of the box. Style a parent `color` (web) or pass `color` directly.
94
+ TypeScript will autocomplete available icon names. Current icons:
95
+ - `arrow-left`
96
+ - `arrow-up-right`
97
+ - `icon-slot`
129
98
 
130
99
  ---
131
100
 
132
- ## ↔️ RTL & Logical Directions
133
-
134
- Icons are designed with logical naming. For RTL support:
101
+ ## 🏗️ Architecture
135
102
 
136
- ```tsx
137
- // web
138
- <div dir="rtl">
139
- <ArrowLeft />
140
- </div>
141
-
142
- // native
143
- <View style={{ transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }] }}>
144
- <ArrowLeft />
145
- </View>
146
- ```
103
+ ### Web (SVG-based)
104
+ - Individual SVG components generated from source files
105
+ - Tree-shakable - only icons you use are bundled
106
+ - Full CSS styling support
107
+ - Color inherits from parent by default (`currentColor`)
147
108
 
148
- You can also add a `rtlFlip` prop or generate RTL variants if your design system requires them.
109
+ ### Native (Font-based)
110
+ - Custom font generated from SVG files
111
+ - Single font file for all icons
112
+ - Better performance than SVG rendering
113
+ - Smaller bundle size for large icon sets
114
+ - Uses React Native `Text` component
149
115
 
150
116
  ---
151
117
 
152
- ## 🧩 Adding Icons
118
+ ## 🔧 Development
153
119
 
154
- 1. Drop raw SVGs into `icons-src/` (use 24×24 viewBox if possible).
120
+ ### Adding New Icons
121
+
122
+ 1. Drop SVG files into `icons-src/` (24×24 viewBox recommended)
155
123
 
156
124
  2. Run codegen:
157
125
 
@@ -159,53 +127,139 @@ You can also add a `rtlFlip` prop or generate RTL variants if your design system
159
127
  npm run gen
160
128
  ```
161
129
 
162
- 3. Commit, version, and publish:
130
+ This will:
131
+ - Optimize SVGs with SVGO
132
+ - Generate React components (web)
133
+ - Generate icon font + glyph map (native)
134
+ - Generate TypeScript types
135
+
136
+ 3. Build the package:
163
137
 
164
138
  ```bash
165
- npm version minor
166
- npm publish --access public
139
+ npm run build
167
140
  ```
168
141
 
169
- > Filenames become component names: `arrow-left.svg` → `ArrowLeft.tsx`.
142
+ 4. Publish:
170
143
 
144
+ ```bash
145
+ npm version minor
146
+ npm publish
147
+ ```
171
148
 
172
- ## 🧰 Troubleshooting
173
-
174
- ### Next.js
149
+ ### Project Structure
175
150
 
176
- * If using the **prebuilt** components (recommended), you **don't** need SVGR in the app.
177
- * Ensure the package exports are ESM/CJS as shipped by `tsup`. If you see "Cannot use import statement outside a module", align your Next.js transpilation or stick to ESM imports.
151
+ ```
152
+ huspy-icons/
153
+ ├── icons-src/ # Source SVG files (single source of truth)
154
+ ├── src/
155
+ │ ├── react/ # Generated web components (SVG)
156
+ │ ├── native/ # Native Icon component (font-based)
157
+ │ └── shared/ # Shared types and utilities
158
+ ├── dist/
159
+ │ ├── fonts/ # Generated font files (TTF, WOFF, etc.)
160
+ │ ├── react/ # Built web components
161
+ │ └── native/ # Built native component
162
+ ├── scripts/
163
+ │ ├── generate-font.js # Font generation script (Fantasticon)
164
+ │ ├── generate-types.js # TypeScript type generation
165
+ │ └── add-size-prop.js # Post-processing for SVGR output
166
+ └── package.json
167
+ ```
178
168
 
179
- ### Expo
169
+ ### Available Scripts
180
170
 
181
- * Ensure `react-native-svg` is installed via `npx expo install react-native-svg`.
182
- * If tree-shaking seems off in web builds via Expo Router, prefer the `react` entry for web.
171
+ ```json
172
+ {
173
+ "svgo": "Optimize SVG files",
174
+ "gen:react": "Generate React/SVG components",
175
+ "gen:native": "Generate icon font and glyph map",
176
+ "gen:types": "Generate TypeScript types",
177
+ "gen": "Run all generation scripts",
178
+ "build": "Build TypeScript to JavaScript",
179
+ "prepare": "Full build (runs on npm install)"
180
+ }
181
+ ```
183
182
 
184
183
  ---
185
184
 
186
- ## 📦 Exports
185
+ ## 📦 Package Exports
187
186
 
188
187
  ```json
189
188
  {
190
189
  "exports": {
191
- "./react": "./dist/react/index.js",
192
- "./native": "./dist/native/index.js"
190
+ "./react": {
191
+ "import": "./dist/react/index.mjs",
192
+ "require": "./dist/react/index.js",
193
+ "types": "./dist/react/index.d.ts"
194
+ },
195
+ "./native": {
196
+ "require": "./dist/native/index.js",
197
+ "types": "./dist/native/index.d.ts"
198
+ }
193
199
  }
194
200
  }
195
201
  ```
196
202
 
197
- * Web: `import { ArrowLeft, ArrowUpRight } from 'huspy-icons/react'`
198
- * Native: `import { ArrowLeft, ArrowUpRight } from 'huspy-icons/native'`
203
+ Usage:
204
+ ```tsx
205
+ // Web
206
+ import { Icon } from 'huspy-icons/react';
207
+
208
+ // Native
209
+ import { Icon } from 'huspy-icons/native';
210
+ ```
199
211
 
200
212
  ---
201
213
 
202
214
  ## 🔒 Versioning
203
215
 
204
- * **Patch**: path tweaks, metadata fixes
205
- * **Minor**: new icons, non-breaking props
206
- * **Major**: removals/renames, breaking prop changes
216
+ * **Patch**: Path tweaks, bug fixes, metadata changes
217
+ * **Minor**: New icons, non-breaking API additions
218
+ * **Major**: Icon removals/renames, breaking API changes
219
+
220
+ ---
221
+
222
+ ## 🐛 Troubleshooting
223
+
224
+ ### Native: Font Not Loading
225
+
226
+ See [FONT_SETUP.md](./FONT_SETUP.md) for detailed setup instructions.
227
+
228
+ Common issues:
229
+ - Font file not copied to correct location
230
+ - Font not registered in app config
231
+ - App not restarted after adding font
207
232
 
208
- Use Changesets or Conventional Commits to automate changelog and release tags.
233
+ ### Web: Icons Not Showing
234
+
235
+ - Ensure you're importing from `huspy-icons/react`
236
+ - Check that the icon name is correct (TypeScript will help)
237
+ - Verify the package is installed and built
238
+
239
+ ### TypeScript Errors
240
+
241
+ If you see type errors after adding new icons:
242
+ 1. Run `npm run gen` to regenerate types
243
+ 2. Restart your TypeScript server
244
+ 3. Check that the built files in `dist/` are up to date
245
+
246
+ ---
247
+
248
+ ## 🎯 Why This Architecture?
249
+
250
+ ### Web uses SVG because:
251
+ - ✅ Tree-shakable (only bundle icons you use)
252
+ - ✅ Full CSS styling support
253
+ - ✅ Can animate individual paths
254
+ - ✅ No font loading required
255
+ - ✅ Modern browsers handle SVG efficiently
256
+
257
+ ### Native uses Fonts because:
258
+ - ✅ Better performance at scale
259
+ - ✅ Smaller bundle size for large icon sets
260
+ - ✅ Native text rendering (faster than SVG)
261
+ - ✅ Single font file vs. many SVG components
262
+ - ✅ Consistent with native icon patterns
209
263
 
210
264
  ---
211
265
 
@@ -215,9 +269,19 @@ MIT © Huspy
215
269
 
216
270
  ---
217
271
 
218
- ## 💡 Notes
272
+ ## 💡 Best Practices
273
+
274
+ * Keep `icons-src/` clean and consistent (same viewBox, stroke width)
275
+ * Use descriptive, kebab-case filenames (`arrow-left.svg`, not `arrow.svg`)
276
+ * Default to outline style; namespace filled variants if needed
277
+ * Test icons on both platforms before publishing
278
+ * Document any platform-specific differences
279
+ * Consider publishing a Storybook for visual reference
280
+
281
+ ---
219
282
 
220
- * Keep `icons-src/` clean and consistent (same stroke width family, grid).
221
- * Default to outlines; if you ship `filled` variants, namespace as `HomeIcon` / `HomeFilledIcon` or `HomeIcon` with a `variant="filled"` prop (generated).
222
- * Consider publishing a Storybook or gallery page in your web app to preview all icons.
283
+ ## 🔗 Related Docs
223
284
 
285
+ - [FONT_SETUP.md](./FONT_SETUP.md) - React Native font setup guide
286
+ - [USAGE.md](./USAGE.md) - Detailed usage examples
287
+ - [PROJECT_SUMMARY.md](./PROJECT_SUMMARY.md) - Technical architecture
@@ -0,0 +1,28 @@
1
+ @font-face {
2
+ font-family: "HuspyIcons";
3
+ src: url(".//HuspyIcons.ttf?25d92884ac0b766c69d414744c2ffbc2") format("truetype"),
4
+ url(".//HuspyIcons.woff?25d92884ac0b766c69d414744c2ffbc2") format("woff"),
5
+ url(".//HuspyIcons.woff2?25d92884ac0b766c69d414744c2ffbc2") format("woff2"),
6
+ url(".//HuspyIcons.eot?25d92884ac0b766c69d414744c2ffbc2#iefix") format("embedded-opentype");
7
+ }
8
+
9
+ .icon:before {
10
+ font-family: HuspyIcons !important;
11
+ font-style: normal;
12
+ font-weight: normal !important;
13
+ font-variant: normal;
14
+ text-transform: none;
15
+ line-height: 1;
16
+ -webkit-font-smoothing: antialiased;
17
+ -moz-osx-font-smoothing: grayscale;
18
+ }
19
+
20
+ .icon.huspy-icon-icon-slot:before {
21
+ content: "\f101";
22
+ }
23
+ .icon.huspy-icon-arrow-up-right:before {
24
+ content: "\f102";
25
+ }
26
+ .icon.huspy-icon-arrow-left:before {
27
+ content: "\f103";
28
+ }
@@ -0,0 +1,9 @@
1
+ export enum HuspyIcons {
2
+ ArrowLeft = "HuspyIcons-arrow-left",
3
+ ArrowUpRight = "HuspyIcons-arrow-up-right",
4
+ IconSlot = "HuspyIcons-icon-slot"
5
+ }
6
+
7
+ export type HuspyIconsClassname = "HuspyIcons-arrow-left" | "HuspyIcons-arrow-up-right" | "HuspyIcons-icon-slot"
8
+ export type HuspyIconsIcon = "arrow-left" | "arrow-up-right" | "icon-slot"
9
+ export const HuspyIconsPrefix = "HuspyIcons-"
Binary file
@@ -0,0 +1,5 @@
1
+ {
2
+ "icon-slot": 61697,
3
+ "arrow-up-right": 61698,
4
+ "arrow-left": 61699
5
+ }
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
3
+ <svg xmlns="http://www.w3.org/2000/svg">
4
+ <defs>
5
+ <font id="HuspyIcons" horiz-adv-x="24">
6
+ <font-face font-family="HuspyIcons"
7
+ units-per-em="24" ascent="24"
8
+ descent="0" />
9
+ <missing-glyph horiz-adv-x="0" />
10
+ <glyph glyph-name="arrow-left"
11
+ unicode="&#xE000;"
12
+ horiz-adv-x="24" d="M12.7071 19.70711C13.0976 19.31658 13.0976 18.68342 12.7071 18.29289L6.41421 12L12.7071 5.7071C13.0976 5.3166 13.0976 4.6834 12.7071 4.2929C12.3166 3.9024 11.6834 3.9024 11.2929 4.2929L4.29289 11.2929C3.90237 11.6834 3.90237 12.3166 4.29289 12.7071L11.2929 19.70711C11.6834 20.09763 12.3166 20.09763 12.7071 19.70711zM4 12C4 12.5523 4.44772 13 5 13H19C19.5523 13 20 12.5523 20 12C20 11.4477 19.5523 11 19 11H5C4.44772 11 4 11.4477 4 12z" />
13
+ <glyph glyph-name="arrow-up-right"
14
+ unicode="&#xE001;"
15
+ horiz-adv-x="24" d="M6 17C6 17.55228 6.44772 18 7 18H17C17.5523 18 18 17.55228 18 17V7C18 6.4477 17.5523 6 17 6C16.4477 6 16 6.4477 16 7V16H7C6.44772 16 6 16.44772 6 17zM17.7071 17.70711C18.0976 17.31658 18.0976 16.68342 17.7071 16.29289L7.70711 6.2929C7.31658 5.9024 6.68342 5.9024 6.29289 6.2929C5.90237 6.6834 5.90237 7.3166 6.29289 7.7071L16.2929 17.70711C16.6834 18.09763 17.3166 18.09763 17.7071 17.70711z" />
16
+ <glyph glyph-name="icon-slot"
17
+ unicode="&#xE002;"
18
+ horiz-adv-x="15" d="M13.3333 7.66667C13.3333 10.98038 10.647 13.66667 7.33333 13.66667C4.01962 13.66667 1.33333 10.98038 1.33333 7.66667C1.33333 4.353 4.01962 1.6667 7.33333 1.6667C10.647 1.6667 13.3333 4.353 13.3333 7.66667zM14.6667 7.66667C14.6667 3.6166 11.3834 0.3333 7.33333 0.3333C3.28325 0.3333 0 3.6166 0 7.66667C0 11.71675 3.28325 15 7.33333 15C11.3834 15 14.6667 11.71675 14.6667 7.66667z" />
19
+ </font>
20
+ </defs>
21
+ </svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="0" height="0" style="display:none;"><symbol viewBox="0 0 24 24" id="HuspyIcons-arrow-left"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.7071 4.29289C13.0976 4.68342 13.0976 5.31658 12.7071 5.70711L6.41421 12L12.7071 18.2929C13.0976 18.6834 13.0976 19.3166 12.7071 19.7071C12.3166 20.0976 11.6834 20.0976 11.2929 19.7071L4.29289 12.7071C3.90237 12.3166 3.90237 11.6834 4.29289 11.2929L11.2929 4.29289C11.6834 3.90237 12.3166 3.90237 12.7071 4.29289Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M4 12C4 11.4477 4.44772 11 5 11H19C19.5523 11 20 11.4477 20 12C20 12.5523 19.5523 13 19 13H5C4.44772 13 4 12.5523 4 12Z" fill="currentColor"></path></symbol><symbol viewBox="0 0 24 24" id="HuspyIcons-arrow-up-right"><path fill-rule="evenodd" clip-rule="evenodd" d="M6 7C6 6.44772 6.44772 6 7 6H17C17.5523 6 18 6.44772 18 7V17C18 17.5523 17.5523 18 17 18C16.4477 18 16 17.5523 16 17V8H7C6.44772 8 6 7.55228 6 7Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M17.7071 6.29289C18.0976 6.68342 18.0976 7.31658 17.7071 7.70711L7.70711 17.7071C7.31658 18.0976 6.68342 18.0976 6.29289 17.7071C5.90237 17.3166 5.90237 16.6834 6.29289 16.2929L16.2929 6.29289C16.6834 5.90237 17.3166 5.90237 17.7071 6.29289Z" fill="currentColor"></path></symbol><symbol viewBox="0 0 15 15" id="HuspyIcons-icon-slot"><path d="M13.3333 7.33333C13.3333 4.01962 10.647 1.33333 7.33333 1.33333C4.01962 1.33333 1.33333 4.01962 1.33333 7.33333C1.33333 10.647 4.01962 13.3333 7.33333 13.3333C10.647 13.3333 13.3333 10.647 13.3333 7.33333ZM14.6667 7.33333C14.6667 11.3834 11.3834 14.6667 7.33333 14.6667C3.28325 14.6667 0 11.3834 0 7.33333C0 3.28325 3.28325 0 7.33333 0C11.3834 0 14.6667 3.28325 14.6667 7.33333Z" fill="currentColor"></path></symbol></svg>
@@ -0,0 +1,16 @@
1
+ export type HuspyIconsId =
2
+ | "icon-slot"
3
+ | "arrow-up-right"
4
+ | "arrow-left";
5
+
6
+ export enum HuspyIcons {
7
+ IconSlot = "icon-slot",
8
+ ArrowUpRight = "arrow-up-right",
9
+ ArrowLeft = "arrow-left",
10
+ }
11
+
12
+ export const HUSPY_ICONS_CODEPOINTS: { [key in HuspyIcons]: string } = {
13
+ [HuspyIcons.IconSlot]: "61697",
14
+ [HuspyIcons.ArrowUpRight]: "61698",
15
+ [HuspyIcons.ArrowLeft]: "61699",
16
+ };
Binary file
Binary file
Binary file
@@ -1,63 +1,47 @@
1
1
  import * as React from 'react';
2
+ import { TextProps } from 'react-native';
2
3
 
3
- type IconSize = number | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
4
4
  /**
5
- * Icon size presets
5
+ * Available icon names in the HuspyIcons font
6
6
  */
7
- declare const ICON_SIZES: {
8
- readonly xs: 8;
9
- readonly sm: 12;
10
- readonly md: 16;
11
- readonly lg: 20;
12
- readonly xl: 24;
13
- };
7
+ type IconName = 'icon-slot' | 'arrow-up-right' | 'arrow-left';
14
8
  /**
15
- * Icon size token type
9
+ * Mapping of icon names to unicode codepoints
10
+ * Used by the Icon component to render the correct glyph
16
11
  */
17
- type IconSizeToken = keyof typeof ICON_SIZES;
12
+ declare const glyphMap: Record<IconName, number>;
18
13
  /**
19
- * Props for React Native icons
14
+ * Font family name for React Native
20
15
  */
21
- interface NativeIconProps {
22
- size?: IconSize;
23
- width?: number;
24
- height?: number;
25
- color?: string;
26
- style?: any;
27
- }
28
- /**
29
- * Resolves an icon size to a numeric value
30
- * @param size - Size value or token
31
- * @returns Numeric size in pixels
32
- */
33
- declare function resolveSize(size?: IconSize): number;
34
-
35
- declare const SvgArrowLeft: ({ size, ...props }: NativeIconProps) => React.JSX.Element;
36
-
37
- declare const SvgArrowUpRight: ({ size, ...props }: NativeIconProps) => React.JSX.Element;
16
+ declare const fontFamily = "HuspyIcons";
38
17
 
39
- declare const SvgIconSlot: ({ size, ...props }: NativeIconProps) => React.JSX.Element;
40
-
41
- /**
42
- * Icon imports - using dynamic imports for tree-shaking
43
- * Auto-generated - do not edit manually
44
- */
45
- /**
46
- * Available icon names
47
- */
48
- type IconName = 'arrow-left' | 'arrow-up-right' | 'icon-slot';
49
18
  /**
50
- * Props for the unified Icon component (React Native)
19
+ * Props for the Icon component (React Native)
51
20
  */
52
- interface IconProps extends Omit<NativeIconProps, 'size'> {
21
+ interface IconProps extends Omit<TextProps, 'children'> {
22
+ /**
23
+ * Name of the icon to display
24
+ */
53
25
  name: IconName;
54
- size?: NativeIconProps['size'];
26
+ /**
27
+ * Size of the icon (default: 16)
28
+ */
29
+ size?: number;
30
+ /**
31
+ * Color of the icon (default: inherits from parent or 'black')
32
+ */
55
33
  color?: string;
56
34
  }
57
35
  /**
58
- * Unified Icon component that renders icons by name (React Native)
59
- * Uses dynamic imports for tree-shaking support
36
+ * Icon component for React Native
37
+ *
38
+ * Renders icons using a custom font (HuspyIcons)
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * <Icon name="arrow-left" size={24} color="#000" />
43
+ * ```
60
44
  */
61
- declare const Icon: ({ name, size, color, ...props }: IconProps) => React.JSX.Element | null;
45
+ declare const Icon: ({ name, size, color, style, ...props }: IconProps) => React.JSX.Element | null;
62
46
 
63
- export { SvgArrowLeft as ArrowLeft, SvgArrowUpRight as ArrowUpRight, ICON_SIZES, Icon, type IconName, type IconProps, type IconSize, type IconSizeToken, SvgIconSlot as IconSlot, type NativeIconProps, resolveSize };
47
+ export { Icon, type IconName, type IconProps, fontFamily, glyphMap };
@@ -5,9 +5,6 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __esm = (fn, res) => function __init() {
9
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
- };
11
8
  var __export = (target, all) => {
12
9
  for (var name in all)
13
10
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -30,189 +27,64 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
27
  ));
31
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
29
 
33
- // src/shared/types.ts
34
- function resolveSize(size = "lg") {
35
- if (typeof size === "number") {
36
- return size;
37
- }
38
- return ICON_SIZES[size] ?? ICON_SIZES.lg;
39
- }
40
- var ICON_SIZES;
41
- var init_types = __esm({
42
- "src/shared/types.ts"() {
43
- "use strict";
44
- ICON_SIZES = {
45
- xs: 8,
46
- sm: 12,
47
- md: 16,
48
- lg: 20,
49
- xl: 24
50
- };
51
- }
52
- });
53
-
54
- // src/native/ArrowLeft.tsx
55
- var ArrowLeft_exports = {};
56
- __export(ArrowLeft_exports, {
57
- default: () => ArrowLeft_default
58
- });
59
- var React, import_react_native_svg, SvgArrowLeft, ArrowLeft_default;
60
- var init_ArrowLeft = __esm({
61
- "src/native/ArrowLeft.tsx"() {
62
- "use strict";
63
- React = __toESM(require("react"));
64
- import_react_native_svg = __toESM(require("react-native-svg"));
65
- init_types();
66
- SvgArrowLeft = ({ size = 16, ...props }) => {
67
- const sizeValue = resolveSize(size);
68
- return /* @__PURE__ */ React.createElement(import_react_native_svg.default, { width: sizeValue, height: sizeValue, viewBox: "0 0 24 24", fill: "none", ...props }, /* @__PURE__ */ React.createElement(
69
- import_react_native_svg.Path,
70
- {
71
- fillRule: "evenodd",
72
- clipRule: "evenodd",
73
- d: "M12.7071 4.29289C13.0976 4.68342 13.0976 5.31658 12.7071 5.70711L6.41421 12L12.7071 18.2929C13.0976 18.6834 13.0976 19.3166 12.7071 19.7071C12.3166 20.0976 11.6834 20.0976 11.2929 19.7071L4.29289 12.7071C3.90237 12.3166 3.90237 11.6834 4.29289 11.2929L11.2929 4.29289C11.6834 3.90237 12.3166 3.90237 12.7071 4.29289Z",
74
- fill: "currentColor"
75
- }
76
- ), /* @__PURE__ */ React.createElement(
77
- import_react_native_svg.Path,
78
- {
79
- fillRule: "evenodd",
80
- clipRule: "evenodd",
81
- d: "M4 12C4 11.4477 4.44772 11 5 11H19C19.5523 11 20 11.4477 20 12C20 12.5523 19.5523 13 19 13H5C4.44772 13 4 12.5523 4 12Z",
82
- fill: "currentColor"
83
- }
84
- ));
85
- };
86
- ArrowLeft_default = SvgArrowLeft;
87
- }
88
- });
89
-
90
- // src/native/ArrowUpRight.tsx
91
- var ArrowUpRight_exports = {};
92
- __export(ArrowUpRight_exports, {
93
- default: () => ArrowUpRight_default
94
- });
95
- var React2, import_react_native_svg2, SvgArrowUpRight, ArrowUpRight_default;
96
- var init_ArrowUpRight = __esm({
97
- "src/native/ArrowUpRight.tsx"() {
98
- "use strict";
99
- React2 = __toESM(require("react"));
100
- import_react_native_svg2 = __toESM(require("react-native-svg"));
101
- init_types();
102
- SvgArrowUpRight = ({ size = 16, ...props }) => {
103
- const sizeValue = resolveSize(size);
104
- return /* @__PURE__ */ React2.createElement(import_react_native_svg2.default, { width: sizeValue, height: sizeValue, viewBox: "0 0 24 24", fill: "none", ...props }, /* @__PURE__ */ React2.createElement(
105
- import_react_native_svg2.Path,
106
- {
107
- fillRule: "evenodd",
108
- clipRule: "evenodd",
109
- d: "M6 7C6 6.44772 6.44772 6 7 6H17C17.5523 6 18 6.44772 18 7V17C18 17.5523 17.5523 18 17 18C16.4477 18 16 17.5523 16 17V8H7C6.44772 8 6 7.55228 6 7Z",
110
- fill: "currentColor"
111
- }
112
- ), /* @__PURE__ */ React2.createElement(
113
- import_react_native_svg2.Path,
114
- {
115
- fillRule: "evenodd",
116
- clipRule: "evenodd",
117
- d: "M17.7071 6.29289C18.0976 6.68342 18.0976 7.31658 17.7071 7.70711L7.70711 17.7071C7.31658 18.0976 6.68342 18.0976 6.29289 17.7071C5.90237 17.3166 5.90237 16.6834 6.29289 16.2929L16.2929 6.29289C16.6834 5.90237 17.3166 5.90237 17.7071 6.29289Z",
118
- fill: "currentColor"
119
- }
120
- ));
121
- };
122
- ArrowUpRight_default = SvgArrowUpRight;
123
- }
124
- });
125
-
126
- // src/native/IconSlot.tsx
127
- var IconSlot_exports = {};
128
- __export(IconSlot_exports, {
129
- default: () => IconSlot_default
130
- });
131
- var React3, import_react_native_svg3, SvgIconSlot, IconSlot_default;
132
- var init_IconSlot = __esm({
133
- "src/native/IconSlot.tsx"() {
134
- "use strict";
135
- React3 = __toESM(require("react"));
136
- import_react_native_svg3 = __toESM(require("react-native-svg"));
137
- init_types();
138
- SvgIconSlot = ({ size = 16, ...props }) => {
139
- const sizeValue = resolveSize(size);
140
- return /* @__PURE__ */ React3.createElement(import_react_native_svg3.default, { width: sizeValue, height: sizeValue, viewBox: "0 0 15 15", fill: "none", ...props }, /* @__PURE__ */ React3.createElement(
141
- import_react_native_svg3.Path,
142
- {
143
- d: "M13.3333 7.33333C13.3333 4.01962 10.647 1.33333 7.33333 1.33333C4.01962 1.33333 1.33333 4.01962 1.33333 7.33333C1.33333 10.647 4.01962 13.3333 7.33333 13.3333C10.647 13.3333 13.3333 10.647 13.3333 7.33333ZM14.6667 7.33333C14.6667 11.3834 11.3834 14.6667 7.33333 14.6667C3.28325 14.6667 0 11.3834 0 7.33333C0 3.28325 3.28325 0 7.33333 0C11.3834 0 14.6667 3.28325 14.6667 7.33333Z",
144
- fill: "currentColor"
145
- }
146
- ));
147
- };
148
- IconSlot_default = SvgIconSlot;
149
- }
150
- });
151
-
152
30
  // src/native/index.ts
153
31
  var native_exports = {};
154
32
  __export(native_exports, {
155
- ArrowLeft: () => ArrowLeft_default,
156
- ArrowUpRight: () => ArrowUpRight_default,
157
- ICON_SIZES: () => ICON_SIZES,
158
33
  Icon: () => Icon_default,
159
- IconSlot: () => IconSlot_default,
160
- resolveSize: () => resolveSize
34
+ fontFamily: () => fontFamily,
35
+ glyphMap: () => glyphMap
161
36
  });
162
37
  module.exports = __toCommonJS(native_exports);
163
- init_ArrowLeft();
164
- init_ArrowUpRight();
165
- init_IconSlot();
166
38
 
167
39
  // src/native/Icon.tsx
168
- var React4 = __toESM(require("react"));
169
- function loadIcon(name) {
170
- switch (name) {
171
- case "arrow-left":
172
- return Promise.resolve().then(() => (init_ArrowLeft(), ArrowLeft_exports)).then((m) => m.default);
173
- case "arrow-up-right":
174
- return Promise.resolve().then(() => (init_ArrowUpRight(), ArrowUpRight_exports)).then((m) => m.default);
175
- case "icon-slot":
176
- return Promise.resolve().then(() => (init_IconSlot(), IconSlot_exports)).then((m) => m.default);
177
- default:
178
- return Promise.reject(new Error(`Icon "${name}" not found`));
179
- }
180
- }
181
- var Icon = ({ name, size = 16, color, ...props }) => {
182
- const [IconComponent, setIconComponent] = React4.useState(null);
183
- const [loading, setLoading] = React4.useState(true);
184
- const [error, setError] = React4.useState(null);
185
- React4.useEffect(() => {
186
- setLoading(true);
187
- setError(null);
188
- loadIcon(name).then((Component) => {
189
- setIconComponent(() => Component);
190
- setLoading(false);
191
- }).catch((err) => {
192
- console.warn(err.message);
193
- setError(err.message);
194
- setLoading(false);
195
- });
196
- }, [name]);
197
- if (loading) {
198
- return null;
199
- }
200
- if (error || !IconComponent) {
40
+ var React = __toESM(require("react"));
41
+ var import_react_native = require("react-native");
42
+
43
+ // src/native/glyphMap.ts
44
+ var glyphMap = {
45
+ "icon-slot": 61697,
46
+ "arrow-up-right": 61698,
47
+ "arrow-left": 61699
48
+ };
49
+ var fontFamily = "HuspyIcons";
50
+
51
+ // src/native/Icon.tsx
52
+ var Icon = ({ name, size = 16, color = "#000", style, ...props }) => {
53
+ const codepoint = glyphMap[name];
54
+ if (!codepoint) {
55
+ if (__DEV__) {
56
+ console.warn(`Icon "${name}" not found in HuspyIcons font`);
57
+ }
201
58
  return null;
202
59
  }
203
- return /* @__PURE__ */ React4.createElement(IconComponent, { size, color, ...props });
60
+ const glyph = String.fromCharCode(codepoint);
61
+ return /* @__PURE__ */ React.createElement(
62
+ import_react_native.Text,
63
+ {
64
+ ...props,
65
+ style: [
66
+ {
67
+ fontFamily,
68
+ fontSize: size,
69
+ color,
70
+ // Ensure icon doesn't inherit text styles
71
+ fontWeight: "normal",
72
+ fontStyle: "normal"
73
+ },
74
+ style
75
+ ],
76
+ accessible: true,
77
+ accessibilityLabel: props.accessibilityLabel || name,
78
+ accessibilityRole: "image"
79
+ },
80
+ glyph
81
+ );
204
82
  };
205
83
  var Icon_default = Icon;
206
-
207
- // src/native/index.ts
208
- init_types();
209
84
  // Annotate the CommonJS export names for ESM import in node:
210
85
  0 && (module.exports = {
211
- ArrowLeft,
212
- ArrowUpRight,
213
- ICON_SIZES,
214
86
  Icon,
215
- IconSlot,
216
- resolveSize
87
+ fontFamily,
88
+ glyphMap
217
89
  });
218
90
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/shared/types.ts","../../src/native/ArrowLeft.tsx","../../src/native/ArrowUpRight.tsx","../../src/native/IconSlot.tsx","../../src/native/index.ts","../../src/native/Icon.tsx"],"sourcesContent":["export type IconSize = number | 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n\n/**\n * Icon size presets\n */\nexport const ICON_SIZES = {\n xs: 8,\n sm: 12,\n md: 16,\n lg: 20,\n xl: 24,\n} as const;\n\n/**\n * Icon size token type\n */\nexport type IconSizeToken = keyof typeof ICON_SIZES;\n\n/**\n * Props for React (web) icons\n */\nexport interface ReactIconProps extends React.SVGProps<SVGSVGElement> {\n size?: IconSize;\n}\n\n/**\n * Props for React Native icons\n */\nexport interface NativeIconProps {\n size?: IconSize;\n width?: number;\n height?: number;\n color?: string;\n style?: any;\n}\n\n/**\n * Resolves an icon size to a numeric value\n * @param size - Size value or token\n * @returns Numeric size in pixels\n */\nexport function resolveSize(size: IconSize = 'lg'): number {\n if (typeof size === 'number') {\n return size;\n }\n \n return ICON_SIZES[size] ?? ICON_SIZES.lg;\n}\n","import * as React from 'react';\nimport Svg, { Path } from 'react-native-svg';\nimport type { NativeIconProps } from '../shared/types';\nimport { resolveSize } from '../shared/types';\n\nconst SvgArrowLeft = ({ size = 16, ...props }: NativeIconProps) => {\n const sizeValue = resolveSize(size);\n \n return (\n <Svg width={sizeValue} height={sizeValue} viewBox=\"0 0 24 24\" fill=\"none\" {...props}>\n <Path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M12.7071 4.29289C13.0976 4.68342 13.0976 5.31658 12.7071 5.70711L6.41421 12L12.7071 18.2929C13.0976 18.6834 13.0976 19.3166 12.7071 19.7071C12.3166 20.0976 11.6834 20.0976 11.2929 19.7071L4.29289 12.7071C3.90237 12.3166 3.90237 11.6834 4.29289 11.2929L11.2929 4.29289C11.6834 3.90237 12.3166 3.90237 12.7071 4.29289Z\"\n fill=\"currentColor\"\n />\n <Path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M4 12C4 11.4477 4.44772 11 5 11H19C19.5523 11 20 11.4477 20 12C20 12.5523 19.5523 13 19 13H5C4.44772 13 4 12.5523 4 12Z\"\n fill=\"currentColor\"\n />\n </Svg>\n );\n};\n\nexport default SvgArrowLeft;\n","import * as React from 'react';\nimport Svg, { Path } from 'react-native-svg';\nimport type { NativeIconProps } from '../shared/types';\nimport { resolveSize } from '../shared/types';\n\nconst SvgArrowUpRight = ({ size = 16, ...props }: NativeIconProps) => {\n const sizeValue = resolveSize(size);\n \n return (\n <Svg width={sizeValue} height={sizeValue} viewBox=\"0 0 24 24\" fill=\"none\" {...props}>\n <Path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M6 7C6 6.44772 6.44772 6 7 6H17C17.5523 6 18 6.44772 18 7V17C18 17.5523 17.5523 18 17 18C16.4477 18 16 17.5523 16 17V8H7C6.44772 8 6 7.55228 6 7Z\"\n fill=\"currentColor\"\n />\n <Path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M17.7071 6.29289C18.0976 6.68342 18.0976 7.31658 17.7071 7.70711L7.70711 17.7071C7.31658 18.0976 6.68342 18.0976 6.29289 17.7071C5.90237 17.3166 5.90237 16.6834 6.29289 16.2929L16.2929 6.29289C16.6834 5.90237 17.3166 5.90237 17.7071 6.29289Z\"\n fill=\"currentColor\"\n />\n </Svg>\n );\n};\n\nexport default SvgArrowUpRight;\n","import * as React from 'react';\nimport Svg, { Path } from 'react-native-svg';\nimport type { NativeIconProps } from '../shared/types';\nimport { resolveSize } from '../shared/types';\n\nconst SvgIconSlot = ({ size = 16, ...props }: NativeIconProps) => {\n const sizeValue = resolveSize(size);\n \n return (\n <Svg width={sizeValue} height={sizeValue} viewBox=\"0 0 15 15\" fill=\"none\" {...props}>\n <Path\n d=\"M13.3333 7.33333C13.3333 4.01962 10.647 1.33333 7.33333 1.33333C4.01962 1.33333 1.33333 4.01962 1.33333 7.33333C1.33333 10.647 4.01962 13.3333 7.33333 13.3333C10.647 13.3333 13.3333 10.647 13.3333 7.33333ZM14.6667 7.33333C14.6667 11.3834 11.3834 14.6667 7.33333 14.6667C3.28325 14.6667 0 11.3834 0 7.33333C0 3.28325 3.28325 0 7.33333 0C11.3834 0 14.6667 3.28325 14.6667 7.33333Z\"\n fill=\"currentColor\"\n />\n </Svg>\n );\n};\n\nexport default SvgIconSlot;\n","// Auto-generated exports\nexport { default as ArrowLeft } from './ArrowLeft';\nexport { default as ArrowUpRight } from './ArrowUpRight';\nexport { default as IconSlot } from './IconSlot';\n\n// Unified Icon component\nexport { default as Icon } from './Icon';\nexport type { IconName, IconProps } from './Icon';\n\n// Export types\nexport type {\n IconSize,\n IconSizeToken,\n NativeIconProps,\n} from '../shared/types';\n\nexport { ICON_SIZES, resolveSize } from '../shared/types';\n","import * as React from 'react';\nimport type { NativeIconProps } from '../shared/types';\n\n/**\n * Icon imports - using dynamic imports for tree-shaking\n * Auto-generated - do not edit manually\n */\n// Icon: arrow-left\n// Icon: arrow-up-right\n// Icon: icon-slot\n\n/**\n * Available icon names\n */\nexport type IconName = 'arrow-left' | 'arrow-up-right' | 'icon-slot';\n\n/**\n * Props for the unified Icon component (React Native)\n */\nexport interface IconProps extends Omit<NativeIconProps, 'size'> {\n name: IconName;\n size?: NativeIconProps['size'];\n color?: string;\n}\n\n/**\n * Loads an icon component dynamically\n * This pattern allows bundlers to tree-shake unused icons\n * Note: React Native bundlers may handle this differently\n */\nfunction loadIcon(name: IconName): Promise<React.ComponentType<any>> {\n switch (name) {\n case 'arrow-left':\n return import('./ArrowLeft').then(m => m.default);\n case 'arrow-up-right':\n return import('./ArrowUpRight').then(m => m.default);\n case 'icon-slot':\n return import('./IconSlot').then(m => m.default);\n default:\n return Promise.reject(new Error(`Icon \"${name}\" not found`));\n }\n}\n\n/**\n * Unified Icon component that renders icons by name (React Native)\n * Uses dynamic imports for tree-shaking support\n */\nconst Icon = ({ name, size = 16, color, ...props }: IconProps) => {\n const [IconComponent, setIconComponent] = React.useState<React.ComponentType<any> | null>(null);\n const [loading, setLoading] = React.useState(true);\n const [error, setError] = React.useState<string | null>(null);\n\n React.useEffect(() => {\n setLoading(true);\n setError(null);\n \n loadIcon(name)\n .then((Component) => {\n setIconComponent(() => Component);\n setLoading(false);\n })\n .catch((err) => {\n console.warn(err.message);\n setError(err.message);\n setLoading(false);\n });\n }, [name]);\n\n if (loading) {\n return null;\n }\n\n if (error || !IconComponent) {\n return null;\n }\n\n return <IconComponent size={size} color={color} {...props} />;\n};\n\nexport default Icon;"],"mappings":"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","names":["Svg","React","import_react_native_svg","Svg","React","import_react_native_svg","Svg","React"]}
1
+ {"version":3,"sources":["../../src/native/index.ts","../../src/native/Icon.tsx","../../src/native/glyphMap.ts"],"sourcesContent":["/**\n * Huspy Icons - React Native (Font-based)\n * \n * This package provides icon components for React Native using a custom font.\n * \n * @example\n * ```tsx\n * import { Icon } from 'huspy-icons/native';\n * \n * function MyComponent() {\n * return <Icon name=\"arrow-left\" size={24} color=\"#000\" />;\n * }\n * ```\n */\n\nexport { default as Icon } from './Icon';\nexport type { IconProps, IconName } from './Icon';\nexport { glyphMap, fontFamily } from './glyphMap';\n","import * as React from 'react';\nimport { Text, TextProps, Platform } from 'react-native';\nimport { glyphMap, fontFamily, IconName } from './glyphMap';\n\n/**\n * Props for the Icon component (React Native)\n */\nexport interface IconProps extends Omit<TextProps, 'children'> {\n /**\n * Name of the icon to display\n */\n name: IconName;\n \n /**\n * Size of the icon (default: 16)\n */\n size?: number;\n \n /**\n * Color of the icon (default: inherits from parent or 'black')\n */\n color?: string;\n}\n\n/**\n * Icon component for React Native\n * \n * Renders icons using a custom font (HuspyIcons)\n * \n * @example\n * ```tsx\n * <Icon name=\"arrow-left\" size={24} color=\"#000\" />\n * ```\n */\nconst Icon = ({ name, size = 16, color = '#000', style, ...props }: IconProps) => {\n const codepoint = glyphMap[name];\n \n if (!codepoint) {\n if (__DEV__) {\n console.warn(`Icon \"${name}\" not found in HuspyIcons font`);\n }\n return null;\n }\n \n // Convert codepoint to character\n const glyph = String.fromCharCode(codepoint);\n \n return (\n <Text\n {...props}\n style={[\n {\n fontFamily: fontFamily,\n fontSize: size,\n color: color,\n // Ensure icon doesn't inherit text styles\n fontWeight: 'normal',\n fontStyle: 'normal',\n },\n style,\n ]}\n // Accessibility\n accessible\n accessibilityLabel={props.accessibilityLabel || name}\n accessibilityRole=\"image\"\n >\n {glyph}\n </Text>\n );\n};\n\nexport default Icon;\nexport type { IconName };\n","// Auto-generated by generate-font.js - do not edit manually\n// Source: icons-src/*.svg dist/fonts/HuspyIcons.*\n\n/**\n * Available icon names in the HuspyIcons font\n */\nexport type IconName = 'icon-slot' | 'arrow-up-right' | 'arrow-left';\n\n/**\n * Mapping of icon names to unicode codepoints\n * Used by the Icon component to render the correct glyph\n */\nexport const glyphMap: Record<IconName, number> = {\n \"icon-slot\": 61697,\n \"arrow-up-right\": 61698,\n \"arrow-left\": 61699\n};\n\n/**\n * Font family name for React Native\n */\nexport const fontFamily = 'HuspyIcons';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,YAAuB;AACvB,0BAA0C;;;ACWnC,IAAM,WAAqC;AAAA,EAChD,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,cAAc;AAChB;AAKO,IAAM,aAAa;;;ADa1B,IAAM,OAAO,CAAC,EAAE,MAAM,OAAO,IAAI,QAAQ,QAAQ,OAAO,GAAG,MAAM,MAAiB;AAChF,QAAM,YAAY,SAAS,IAAI;AAE/B,MAAI,CAAC,WAAW;AACd,QAAI,SAAS;AACX,cAAQ,KAAK,SAAS,IAAI,gCAAgC;AAAA,IAC5D;AACA,WAAO;AAAA,EACT;AAGA,QAAM,QAAQ,OAAO,aAAa,SAAS;AAE3C,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,OAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,UAAU;AAAA,UACV;AAAA;AAAA,UAEA,YAAY;AAAA,UACZ,WAAW;AAAA,QACb;AAAA,QACA;AAAA,MACF;AAAA,MAEA,YAAU;AAAA,MACV,oBAAoB,MAAM,sBAAsB;AAAA,MAChD,mBAAkB;AAAA;AAAA,IAEjB;AAAA,EACH;AAEJ;AAEA,IAAO,eAAQ;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huspy-icons",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Cross-platform icon package for Huspy - React and React Native compatible",
5
5
  "author": "Huspy",
6
6
  "license": "MIT",
@@ -32,12 +32,14 @@
32
32
  },
33
33
  "files": [
34
34
  "dist",
35
- "src"
35
+ "src",
36
+ "README.md",
37
+ "LICENSE"
36
38
  ],
37
39
  "scripts": {
38
40
  "svgo": "svgo -f icons-src -o icons-src --config=svgo.config.js",
39
41
  "gen:react": "svgr icons-src -d src/react --ext tsx --typescript --icon && node scripts/add-size-prop.js --react",
40
- "gen:native": "svgr --native icons-src -d src/native --ext tsx --typescript --icon && node scripts/add-size-prop.js --native",
42
+ "gen:native": "node scripts/generate-font.js",
41
43
  "gen:types": "node scripts/generate-types.js",
42
44
  "gen": "npm run svgo && npm run gen:react && npm run gen:native && npm run gen:types",
43
45
  "build": "tsup",
@@ -51,21 +53,18 @@
51
53
  "@types/react": "^18.2.48",
52
54
  "@types/react-native": "^0.73.0",
53
55
  "esbuild": "^0.19.11",
56
+ "fantasticon": "^3.0.0",
54
57
  "svgo": "^3.3.2",
55
58
  "tsup": "^8.0.1",
56
59
  "typescript": "^5.3.3"
57
60
  },
58
61
  "peerDependencies": {
59
62
  "react": ">=16.8.0",
60
- "react-native": ">=0.73.0",
61
- "react-native-svg": "^15.14.0"
63
+ "react-native": ">=0.73.0"
62
64
  },
63
65
  "peerDependenciesMeta": {
64
66
  "react-native": {
65
67
  "optional": true
66
- },
67
- "react-native-svg": {
68
- "optional": true
69
68
  }
70
69
  },
71
70
  "publishConfig": {
@@ -1,80 +1,73 @@
1
1
  import * as React from 'react';
2
- import type { NativeIconProps } from '../shared/types';
2
+ import { Text, TextProps, Platform } from 'react-native';
3
+ import { glyphMap, fontFamily, IconName } from './glyphMap';
3
4
 
4
5
  /**
5
- * Icon imports - using dynamic imports for tree-shaking
6
- * Auto-generated - do not edit manually
6
+ * Props for the Icon component (React Native)
7
7
  */
8
- // Icon: arrow-left
9
- // Icon: arrow-up-right
10
- // Icon: icon-slot
11
-
12
- /**
13
- * Available icon names
14
- */
15
- export type IconName = 'arrow-left' | 'arrow-up-right' | 'icon-slot';
16
-
17
- /**
18
- * Props for the unified Icon component (React Native)
19
- */
20
- export interface IconProps extends Omit<NativeIconProps, 'size'> {
8
+ export interface IconProps extends Omit<TextProps, 'children'> {
9
+ /**
10
+ * Name of the icon to display
11
+ */
21
12
  name: IconName;
22
- size?: NativeIconProps['size'];
13
+
14
+ /**
15
+ * Size of the icon (default: 16)
16
+ */
17
+ size?: number;
18
+
19
+ /**
20
+ * Color of the icon (default: inherits from parent or 'black')
21
+ */
23
22
  color?: string;
24
23
  }
25
24
 
26
25
  /**
27
- * Loads an icon component dynamically
28
- * This pattern allows bundlers to tree-shake unused icons
29
- * Note: React Native bundlers may handle this differently
26
+ * Icon component for React Native
27
+ *
28
+ * Renders icons using a custom font (HuspyIcons)
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * <Icon name="arrow-left" size={24} color="#000" />
33
+ * ```
30
34
  */
31
- function loadIcon(name: IconName): Promise<React.ComponentType<any>> {
32
- switch (name) {
33
- case 'arrow-left':
34
- return import('./ArrowLeft').then(m => m.default);
35
- case 'arrow-up-right':
36
- return import('./ArrowUpRight').then(m => m.default);
37
- case 'icon-slot':
38
- return import('./IconSlot').then(m => m.default);
39
- default:
40
- return Promise.reject(new Error(`Icon "${name}" not found`));
41
- }
42
- }
43
-
44
- /**
45
- * Unified Icon component that renders icons by name (React Native)
46
- * Uses dynamic imports for tree-shaking support
47
- */
48
- const Icon = ({ name, size = 16, color, ...props }: IconProps) => {
49
- const [IconComponent, setIconComponent] = React.useState<React.ComponentType<any> | null>(null);
50
- const [loading, setLoading] = React.useState(true);
51
- const [error, setError] = React.useState<string | null>(null);
52
-
53
- React.useEffect(() => {
54
- setLoading(true);
55
- setError(null);
56
-
57
- loadIcon(name)
58
- .then((Component) => {
59
- setIconComponent(() => Component);
60
- setLoading(false);
61
- })
62
- .catch((err) => {
63
- console.warn(err.message);
64
- setError(err.message);
65
- setLoading(false);
66
- });
67
- }, [name]);
68
-
69
- if (loading) {
35
+ const Icon = ({ name, size = 16, color = '#000', style, ...props }: IconProps) => {
36
+ const codepoint = glyphMap[name];
37
+
38
+ if (!codepoint) {
39
+ if (__DEV__) {
40
+ console.warn(`Icon "${name}" not found in HuspyIcons font`);
41
+ }
70
42
  return null;
71
43
  }
72
-
73
- if (error || !IconComponent) {
74
- return null;
75
- }
76
-
77
- return <IconComponent size={size} color={color} {...props} />;
44
+
45
+ // Convert codepoint to character
46
+ const glyph = String.fromCharCode(codepoint);
47
+
48
+ return (
49
+ <Text
50
+ {...props}
51
+ style={[
52
+ {
53
+ fontFamily: fontFamily,
54
+ fontSize: size,
55
+ color: color,
56
+ // Ensure icon doesn't inherit text styles
57
+ fontWeight: 'normal',
58
+ fontStyle: 'normal',
59
+ },
60
+ style,
61
+ ]}
62
+ // Accessibility
63
+ accessible
64
+ accessibilityLabel={props.accessibilityLabel || name}
65
+ accessibilityRole="image"
66
+ >
67
+ {glyph}
68
+ </Text>
69
+ );
78
70
  };
79
71
 
80
- export default Icon;
72
+ export default Icon;
73
+ export type { IconName };
@@ -0,0 +1,22 @@
1
+ // Auto-generated by generate-font.js - do not edit manually
2
+ // Source: icons-src/*.svg → dist/fonts/HuspyIcons.*
3
+
4
+ /**
5
+ * Available icon names in the HuspyIcons font
6
+ */
7
+ export type IconName = 'icon-slot' | 'arrow-up-right' | 'arrow-left';
8
+
9
+ /**
10
+ * Mapping of icon names to unicode codepoints
11
+ * Used by the Icon component to render the correct glyph
12
+ */
13
+ export const glyphMap: Record<IconName, number> = {
14
+ "icon-slot": 61697,
15
+ "arrow-up-right": 61698,
16
+ "arrow-left": 61699
17
+ };
18
+
19
+ /**
20
+ * Font family name for React Native
21
+ */
22
+ export const fontFamily = 'HuspyIcons';
@@ -1,17 +1,18 @@
1
- // Auto-generated exports
2
- export { default as ArrowLeft } from './ArrowLeft';
3
- export { default as ArrowUpRight } from './ArrowUpRight';
4
- export { default as IconSlot } from './IconSlot';
1
+ /**
2
+ * Huspy Icons - React Native (Font-based)
3
+ *
4
+ * This package provides icon components for React Native using a custom font.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * import { Icon } from 'huspy-icons/native';
9
+ *
10
+ * function MyComponent() {
11
+ * return <Icon name="arrow-left" size={24} color="#000" />;
12
+ * }
13
+ * ```
14
+ */
5
15
 
6
- // Unified Icon component
7
16
  export { default as Icon } from './Icon';
8
- export type { IconName, IconProps } from './Icon';
9
-
10
- // Export types
11
- export type {
12
- IconSize,
13
- IconSizeToken,
14
- NativeIconProps,
15
- } from '../shared/types';
16
-
17
- export { ICON_SIZES, resolveSize } from '../shared/types';
17
+ export type { IconProps, IconName } from './Icon';
18
+ export { glyphMap, fontFamily } from './glyphMap';
@@ -1,27 +0,0 @@
1
- import * as React from 'react';
2
- import Svg, { Path } from 'react-native-svg';
3
- import type { NativeIconProps } from '../shared/types';
4
- import { resolveSize } from '../shared/types';
5
-
6
- const SvgArrowLeft = ({ size = 16, ...props }: NativeIconProps) => {
7
- const sizeValue = resolveSize(size);
8
-
9
- return (
10
- <Svg width={sizeValue} height={sizeValue} viewBox="0 0 24 24" fill="none" {...props}>
11
- <Path
12
- fillRule="evenodd"
13
- clipRule="evenodd"
14
- d="M12.7071 4.29289C13.0976 4.68342 13.0976 5.31658 12.7071 5.70711L6.41421 12L12.7071 18.2929C13.0976 18.6834 13.0976 19.3166 12.7071 19.7071C12.3166 20.0976 11.6834 20.0976 11.2929 19.7071L4.29289 12.7071C3.90237 12.3166 3.90237 11.6834 4.29289 11.2929L11.2929 4.29289C11.6834 3.90237 12.3166 3.90237 12.7071 4.29289Z"
15
- fill="currentColor"
16
- />
17
- <Path
18
- fillRule="evenodd"
19
- clipRule="evenodd"
20
- d="M4 12C4 11.4477 4.44772 11 5 11H19C19.5523 11 20 11.4477 20 12C20 12.5523 19.5523 13 19 13H5C4.44772 13 4 12.5523 4 12Z"
21
- fill="currentColor"
22
- />
23
- </Svg>
24
- );
25
- };
26
-
27
- export default SvgArrowLeft;
@@ -1,27 +0,0 @@
1
- import * as React from 'react';
2
- import Svg, { Path } from 'react-native-svg';
3
- import type { NativeIconProps } from '../shared/types';
4
- import { resolveSize } from '../shared/types';
5
-
6
- const SvgArrowUpRight = ({ size = 16, ...props }: NativeIconProps) => {
7
- const sizeValue = resolveSize(size);
8
-
9
- return (
10
- <Svg width={sizeValue} height={sizeValue} viewBox="0 0 24 24" fill="none" {...props}>
11
- <Path
12
- fillRule="evenodd"
13
- clipRule="evenodd"
14
- d="M6 7C6 6.44772 6.44772 6 7 6H17C17.5523 6 18 6.44772 18 7V17C18 17.5523 17.5523 18 17 18C16.4477 18 16 17.5523 16 17V8H7C6.44772 8 6 7.55228 6 7Z"
15
- fill="currentColor"
16
- />
17
- <Path
18
- fillRule="evenodd"
19
- clipRule="evenodd"
20
- d="M17.7071 6.29289C18.0976 6.68342 18.0976 7.31658 17.7071 7.70711L7.70711 17.7071C7.31658 18.0976 6.68342 18.0976 6.29289 17.7071C5.90237 17.3166 5.90237 16.6834 6.29289 16.2929L16.2929 6.29289C16.6834 5.90237 17.3166 5.90237 17.7071 6.29289Z"
21
- fill="currentColor"
22
- />
23
- </Svg>
24
- );
25
- };
26
-
27
- export default SvgArrowUpRight;
@@ -1,19 +0,0 @@
1
- import * as React from 'react';
2
- import Svg, { Path } from 'react-native-svg';
3
- import type { NativeIconProps } from '../shared/types';
4
- import { resolveSize } from '../shared/types';
5
-
6
- const SvgIconSlot = ({ size = 16, ...props }: NativeIconProps) => {
7
- const sizeValue = resolveSize(size);
8
-
9
- return (
10
- <Svg width={sizeValue} height={sizeValue} viewBox="0 0 15 15" fill="none" {...props}>
11
- <Path
12
- d="M13.3333 7.33333C13.3333 4.01962 10.647 1.33333 7.33333 1.33333C4.01962 1.33333 1.33333 4.01962 1.33333 7.33333C1.33333 10.647 4.01962 13.3333 7.33333 13.3333C10.647 13.3333 13.3333 10.647 13.3333 7.33333ZM14.6667 7.33333C14.6667 11.3834 11.3834 14.6667 7.33333 14.6667C3.28325 14.6667 0 11.3834 0 7.33333C0 3.28325 3.28325 0 7.33333 0C11.3834 0 14.6667 3.28325 14.6667 7.33333Z"
13
- fill="currentColor"
14
- />
15
- </Svg>
16
- );
17
- };
18
-
19
- export default SvgIconSlot;
@@ -1,3 +0,0 @@
1
- export { default as ArrowLeft } from './ArrowLeft';
2
- export { default as ArrowUpRight } from './ArrowUpRight';
3
- export { default as IconSlot } from './IconSlot';