@shaquillehinds/react-native-svg-icons 0.1.0 → 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 +347 -491
- package/bin/find-icon.js +248 -0
- package/bin/install-rules.js +146 -0
- package/package.json +7 -1
- package/rules/AGENT_RULES.md +353 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# Agent Rules — `@shaquillehinds/react-native-svg-icons`
|
|
2
|
+
|
|
3
|
+
Rules for AI coding agents writing or modifying code that uses this package.
|
|
4
|
+
Read this before rendering any icon.
|
|
5
|
+
|
|
6
|
+
The failure mode with an icon library is inventing names. This set has 997 icons
|
|
7
|
+
per variant with irregular, sometimes misspelled names. A plausible guess is
|
|
8
|
+
usually wrong, and a wrong name does not throw — it logs and renders nothing.
|
|
9
|
+
Rule 1 exists to stop that.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 0. Non-negotiables
|
|
14
|
+
|
|
15
|
+
1. **Never guess an icon name.** Verify it against the shipped type union before
|
|
16
|
+
you write it. See Rule 1.
|
|
17
|
+
2. `type` is required and selects the name union. `filled` and `outline` are not
|
|
18
|
+
fully interchangeable — 3 names differ between them.
|
|
19
|
+
3. Do not add `react-native-svg` `<Svg>` / `<Path>` around an icon. `SvgIcon`
|
|
20
|
+
renders a complete SVG.
|
|
21
|
+
4. If a prop is not listed in this file, it does not exist.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. Never guess an icon name
|
|
26
|
+
|
|
27
|
+
There is no `Search`, no `Delete`, no `Loading`, no `Bitcoin`, no `Home` outline
|
|
28
|
+
mismatch to rely on. Names come from a fixed generated union and include
|
|
29
|
+
misspellings that were baked in and must be reproduced exactly.
|
|
30
|
+
|
|
31
|
+
### Verify first
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npx rnsi-icons search # fuzzy search both variants
|
|
35
|
+
npx rnsi-icons search --type outline
|
|
36
|
+
npx rnsi-icons Trash --exact # confirm one name
|
|
37
|
+
npx rnsi-icons --list > icons.txt
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Without the CLI, grep the shipped union directly:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
grep -oE "'[A-Za-z0-9]*Search[A-Za-z0-9]*'" \
|
|
44
|
+
node_modules/@shaquillehinds/react-native-svg-icons/src/svgs/types.ts | sort -u
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
TypeScript will also reject an invalid name at compile time. Use that — do not
|
|
48
|
+
cast, do not widen to `string`, do not `as FilledIconName`. A type error here is
|
|
49
|
+
the system working.
|
|
50
|
+
|
|
51
|
+
### Names that do not exist, and what to use instead
|
|
52
|
+
|
|
53
|
+
These are the ones models reach for most:
|
|
54
|
+
|
|
55
|
+
| Guess | Reality |
|
|
56
|
+
| --------------------- | ------------------------------------------------------------------ |
|
|
57
|
+
| `Search` | `SearchNormal`, `SearchNormal1`, `SearchStatus`, `SearchZoomIn` |
|
|
58
|
+
| `Delete` | `Trash` |
|
|
59
|
+
| `Loading` / `Spinner` | `Refresh`, `RotateRight`, `RotateLeft` |
|
|
60
|
+
| `Bitcoin` | `BitcoinBtc`, `BitcoinCard`, `BitcoinConvert` |
|
|
61
|
+
| `Ethereum` | grep — crypto icons use a `NameTicker` form (`AaveAave`, `ZelZel`) |
|
|
62
|
+
| `Warning` | `Warning2` — there is no `Warning` |
|
|
63
|
+
| `Info` | `InfoCircle` or `Information` |
|
|
64
|
+
| `Close` | `CloseCircle` or `CloseSquare` — no bare `Close` |
|
|
65
|
+
| `Check` / `Checkmark` | `Check`, `TickCircle`, `TickSquare` |
|
|
66
|
+
|
|
67
|
+
### Misspellings that are load-bearing
|
|
68
|
+
|
|
69
|
+
These are the real, correct names. Spelling them properly breaks the build:
|
|
70
|
+
|
|
71
|
+
| Correct (use this) | Natural but wrong |
|
|
72
|
+
| --------------------------------------------------- | ----------------- |
|
|
73
|
+
| `MinusCirlce` | `MinusCircle` |
|
|
74
|
+
| `UserCirlceAdd` | `UserCircleAdd` |
|
|
75
|
+
| `SendSqaure2` | `SendSquare2` |
|
|
76
|
+
| `MonitorMobbile` | `MonitorMobile` |
|
|
77
|
+
| `Battery3full` | `Battery3Full` |
|
|
78
|
+
| `BrifecaseCross`, `BrifecaseTick`, `BrifecaseTimer` | `Briefcase*` |
|
|
79
|
+
|
|
80
|
+
Note `Briefcase` itself is spelled correctly while its compounds are not. Do not
|
|
81
|
+
normalise these. Do not "fix" one you encounter in existing code.
|
|
82
|
+
|
|
83
|
+
### Other naming quirks
|
|
84
|
+
|
|
85
|
+
- Numeric suffixes are variants, not sizes: `Home`, `Home1`, `Home2`,
|
|
86
|
+
`HomeHashtag` are four different icons.
|
|
87
|
+
- A few names start lowercase or with a digit: `square`, `dcube`, `dRotate`,
|
|
88
|
+
`dSquare`, `dCubeScan`, `4Support`.
|
|
89
|
+
- `4Support` is the registry key; its file is `FourSupport.tsx`. Use the key.
|
|
90
|
+
|
|
91
|
+
### Variant mismatches
|
|
92
|
+
|
|
93
|
+
994 of 997 names exist in both variants. These three do not — check before
|
|
94
|
+
swapping `type`:
|
|
95
|
+
|
|
96
|
+
| `filled` only | `outline` only |
|
|
97
|
+
| ----------------------- | ----------------------- |
|
|
98
|
+
| `ArrowPointCircleup` | `ArrowPointCircleUp` |
|
|
99
|
+
| `ArrowPointCircleRight` | `ArrowPointCircleright` |
|
|
100
|
+
| `FlashCircle` | `FlashCircle2` |
|
|
101
|
+
|
|
102
|
+
Code that flips `type` on a variable name (a focused tab bar icon, for instance)
|
|
103
|
+
must use a name outside this list.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 2. Basic usage
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { SvgIcon } from '@shaquillehinds/react-native-svg-icons';
|
|
111
|
+
|
|
112
|
+
<SvgIcon type="filled" name="Heart" size={24} color="#FF0000" />
|
|
113
|
+
<SvgIcon type="outline" name="SearchNormal" size={20} color="#8E8E93" />
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`type` and `name` are required. `size` defaults to 24, `color` defaults to
|
|
117
|
+
`#292D32` (a near-black grey — **not** pure black; set `color` explicitly if you
|
|
118
|
+
need `#000`).
|
|
119
|
+
|
|
120
|
+
### Size is normalised
|
|
121
|
+
|
|
122
|
+
`size` is passed through `normalize()` from `@shaquillehinds/react-native-essentials`,
|
|
123
|
+
which scales to device dimensions. `size={24}` is a design-scale value, not a
|
|
124
|
+
guaranteed 24 physical points. Do not compensate by hand, and do not compute an
|
|
125
|
+
icon size from `Dimensions` — pass the design value.
|
|
126
|
+
|
|
127
|
+
### `filled` vs `outline` are structurally different
|
|
128
|
+
|
|
129
|
+
- **filled** — one path, coloured with `fill`. No stroke.
|
|
130
|
+
- **outline** — usually several paths, coloured with `stroke` at
|
|
131
|
+
`strokeWidth="1.5"`, with `fill="none"` on the SVG.
|
|
132
|
+
|
|
133
|
+
This matters when overriding: `pathProps={{ fill: 'red' }}` does nothing to an
|
|
134
|
+
outline icon, and `pathProps={{ stroke: 'red' }}` does nothing to a filled one.
|
|
135
|
+
Prefer `color`, which targets the right attribute automatically.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 3. Props
|
|
140
|
+
|
|
141
|
+
| Prop | Type | Default | Notes |
|
|
142
|
+
| ----------- | ------------------------------ | ----------- | ----------------------------------------------------- |
|
|
143
|
+
| `type` | `'filled' \| 'outline'` | required | Selects the `name` union |
|
|
144
|
+
| `name` | `IconNameByType[T]` | required | Verify it — see Rule 1 |
|
|
145
|
+
| `size` | `number` | `24` | Normalised; sets width and height |
|
|
146
|
+
| `color` | `string` | `'#292D32'` | `fill` on filled, `stroke` on outline |
|
|
147
|
+
| `svgProps` | `SvgProps` | – | Spread onto `<Svg>`, after `width`/`height`/`viewBox` |
|
|
148
|
+
| `pathProps` | `PathProps` | – | Spread onto **every** `<Path>`, after `color` |
|
|
149
|
+
| `animate` | `AnimateSVGPathComponentProps` | – | See Rule 4 |
|
|
150
|
+
|
|
151
|
+
Two ordering facts that decide behaviour:
|
|
152
|
+
|
|
153
|
+
- `pathProps` is spread **after** the colour attribute, so it wins over `color`.
|
|
154
|
+
- `svgProps` is spread after `width`/`height`/`viewBox`, so `svgProps={{ width: 40 }}`
|
|
155
|
+
overrides `size`. Use `size`; do not set dimensions through `svgProps`.
|
|
156
|
+
- `pathProps` applies to _every_ path in the icon. On a multi-path outline icon
|
|
157
|
+
you cannot style one path differently through this API.
|
|
158
|
+
|
|
159
|
+
### Type-safe wrappers
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import type {
|
|
163
|
+
SvgIconProps,
|
|
164
|
+
SvgIconType,
|
|
165
|
+
} from '@shaquillehinds/react-native-svg-icons';
|
|
166
|
+
|
|
167
|
+
function Icon<T extends SvgIconType>(props: SvgIconProps<T>) {
|
|
168
|
+
return <SvgIcon {...props} />;
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Keep the generic. Writing `props: SvgIconProps` collapses the union and lets an
|
|
173
|
+
outline-only name through on `type="filled"`.
|
|
174
|
+
|
|
175
|
+
### Typing icon arrays
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
import type { OutlineIconName } from '@shaquillehinds/react-native-svg-icons';
|
|
179
|
+
|
|
180
|
+
const icons: OutlineIconName[] = [
|
|
181
|
+
'House',
|
|
182
|
+
'Airplane',
|
|
183
|
+
'Bookmark',
|
|
184
|
+
'RepeatCircle',
|
|
185
|
+
];
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Always annotate. An unannotated array widens to `string[]` and every name in it
|
|
189
|
+
stops being checked — which is exactly how invented names reach production.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## 4. Animation
|
|
194
|
+
|
|
195
|
+
Two modes, set by `animate.mode`. They do not share a config shape; do not mix
|
|
196
|
+
their keys.
|
|
197
|
+
|
|
198
|
+
### `AnimatedPathProps` — declarative
|
|
199
|
+
|
|
200
|
+
Use this by default.
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
<SvgIcon
|
|
204
|
+
type="outline"
|
|
205
|
+
name="Scanning"
|
|
206
|
+
size={100}
|
|
207
|
+
animate={{
|
|
208
|
+
mode: 'AnimatedPathProps',
|
|
209
|
+
autoStart: true,
|
|
210
|
+
loop: -1,
|
|
211
|
+
returnToStart: true,
|
|
212
|
+
isSequence: false,
|
|
213
|
+
config: { type: 'timing', duration: 2000, useNativeDriver: false },
|
|
214
|
+
animatedPathProps: [
|
|
215
|
+
{ name: 'stroke', from: 'red', to: ['green', 'blue'] },
|
|
216
|
+
{ name: 'strokeDashoffset', from: 72, to: [18, 36] },
|
|
217
|
+
],
|
|
218
|
+
}}
|
|
219
|
+
/>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Keys: `config` (singular), `animatedPathProps`, `isSequence`.
|
|
223
|
+
|
|
224
|
+
### `InterpolatePathProps` — manual
|
|
225
|
+
|
|
226
|
+
Use when you need interpolation the declarative form cannot express.
|
|
227
|
+
|
|
228
|
+
```tsx
|
|
229
|
+
animate={{
|
|
230
|
+
mode: 'InterpolatePathProps',
|
|
231
|
+
autoStart: true,
|
|
232
|
+
loop: -1,
|
|
233
|
+
returnToStart: true,
|
|
234
|
+
pathProps: (value, { inputRange }) => ({
|
|
235
|
+
stroke: value.interpolate({ inputRange, outputRange: ['red', 'green'] }),
|
|
236
|
+
strokeDasharray: '35, 35',
|
|
237
|
+
}),
|
|
238
|
+
animationConfig: [{ type: 'timing', duration: 1000, useNativeDriver: true }],
|
|
239
|
+
}}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Keys: `animationConfig` (array, one entry per stage), `pathProps` as a **function**.
|
|
243
|
+
|
|
244
|
+
Note `pathProps` means something different here from the top-level `pathProps`
|
|
245
|
+
prop. Inside `animate`, it is a function of the animated value. At the top level
|
|
246
|
+
it is a static object. Both can be present.
|
|
247
|
+
|
|
248
|
+
### Rules for both modes
|
|
249
|
+
|
|
250
|
+
- Shared keys: `autoStart` (default `false`), `loop` (`-1` for infinite,
|
|
251
|
+
default `0`), `returnToStart` (default `false`), `ref`.
|
|
252
|
+
- `useNativeDriver: false` for anything animating `stroke`, `fill`, `opacity`, or
|
|
253
|
+
dash properties — which is most icon animation. `true` only for transforms.
|
|
254
|
+
Getting this wrong fails silently or throws at runtime depending on platform.
|
|
255
|
+
- The animation applies to **every path** in the icon. Multi-path outline icons
|
|
256
|
+
animate in lockstep; there is no per-path control.
|
|
257
|
+
- Match the mode to the variant: stroke and dash animations need `type="outline"`,
|
|
258
|
+
fill animations need `type="filled"`.
|
|
259
|
+
|
|
260
|
+
### Imperative control
|
|
261
|
+
|
|
262
|
+
```tsx
|
|
263
|
+
import type { AnimateSVGComponentValueRef } from '@shaquillehinds/react-native-essentials';
|
|
264
|
+
|
|
265
|
+
const ref = useRef<AnimateSVGComponentValueRef>(null);
|
|
266
|
+
|
|
267
|
+
<SvgIcon
|
|
268
|
+
type="filled"
|
|
269
|
+
name="Play"
|
|
270
|
+
animate={{ ref, autoStart: false /* ... */ }}
|
|
271
|
+
/>;
|
|
272
|
+
|
|
273
|
+
ref.current?.start();
|
|
274
|
+
ref.current?.stop();
|
|
275
|
+
ref.current?.reset();
|
|
276
|
+
ref.current?.reverse();
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
The ref type comes from `@shaquillehinds/react-native-essentials`, not this package.
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## 5. Bundle size
|
|
284
|
+
|
|
285
|
+
`SvgIcon` resolves names through a registry that statically imports all 1,994 icon
|
|
286
|
+
modules. Importing `SvgIcon` anywhere pulls the whole set into the bundle.
|
|
287
|
+
|
|
288
|
+
- Do not claim or assume tree shaking removes unused icons. It does not.
|
|
289
|
+
- Do not build a "lighter" wrapper that maps a few names to `SvgIcon` — the
|
|
290
|
+
registry is already loaded.
|
|
291
|
+
- If a screen needs exactly one icon and bundle size is the concern, that is a
|
|
292
|
+
package-level change (deep exports), not something to work around in app code.
|
|
293
|
+
Raise it rather than inventing an import path that is not exported.
|
|
294
|
+
|
|
295
|
+
The package exports `SvgIcon` (named and default) and the name types. There are no
|
|
296
|
+
per-icon entry points.
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## 6. Missing icons fail quietly
|
|
301
|
+
|
|
302
|
+
An unknown `type` or `name` logs via `console.error` and returns `null`. Nothing
|
|
303
|
+
throws, nothing renders, layout collapses where the icon should be.
|
|
304
|
+
|
|
305
|
+
So a blank space where an icon should be is almost always a bad name, not a
|
|
306
|
+
styling problem. Check the name before adjusting layout.
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## 7. Common patterns
|
|
311
|
+
|
|
312
|
+
### Tab bar
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
function TabIcon({ focused, name }: { focused: boolean; name: IconName }) {
|
|
316
|
+
return (
|
|
317
|
+
<SvgIcon
|
|
318
|
+
type={focused ? 'filled' : 'outline'}
|
|
319
|
+
name={name}
|
|
320
|
+
size={24}
|
|
321
|
+
color={focused ? '#007AFF' : '#8E8E93'}
|
|
322
|
+
/>
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Only safe for names present in both variants — see the mismatch table in Rule 1.
|
|
328
|
+
|
|
329
|
+
### Icon button
|
|
330
|
+
|
|
331
|
+
```tsx
|
|
332
|
+
<TouchableOpacity onPress={onPress}>
|
|
333
|
+
<SvgIcon type="filled" name="Heart" size={24} color="#FF0000" />
|
|
334
|
+
</TouchableOpacity>
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Do not wrap in an extra `<View>` for sizing; `size` handles it.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## 8. Review checklist
|
|
342
|
+
|
|
343
|
+
- [ ] Every icon name verified against the type union, not guessed
|
|
344
|
+
- [ ] Misspelled names reproduced exactly (`MinusCirlce`, `SendSqaure2`, …)
|
|
345
|
+
- [ ] Icon name arrays annotated with `FilledIconName` / `OutlineIconName`
|
|
346
|
+
- [ ] No `as` cast or `string` widening on a `name`
|
|
347
|
+
- [ ] Names that flip between variants checked against the 3 mismatches
|
|
348
|
+
- [ ] `color` used rather than `pathProps` fill/stroke overrides
|
|
349
|
+
- [ ] `size` used rather than `svgProps` width/height
|
|
350
|
+
- [ ] `useNativeDriver: false` for colour, stroke, opacity and dash animation
|
|
351
|
+
- [ ] Animation mode keys not mixed (`config` vs `animationConfig`)
|
|
352
|
+
- [ ] Stroke animations on `outline`, fill animations on `filled`
|
|
353
|
+
- [ ] No claim that unused icons are tree-shaken away
|