react-native-nano-icons 0.1.8 → 0.2.1

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.
Files changed (116) hide show
  1. package/README.md +64 -2
  2. package/android/src/main/java/com/nanoicons/NanoIconsFontLoaderModule.kt +63 -0
  3. package/android/src/main/java/com/nanoicons/NanoIconsPackage.kt +19 -2
  4. package/ios/NanoIconView.h +5 -0
  5. package/ios/NanoIconView.mm +35 -9
  6. package/ios/NanoIconsFontLoader.h +11 -0
  7. package/ios/NanoIconsFontLoader.mm +110 -0
  8. package/lib/commonjs/cli/build.d.ts +3 -0
  9. package/lib/commonjs/cli/build.js +8 -4
  10. package/lib/commonjs/cli/config.d.ts +1 -0
  11. package/lib/commonjs/cli/config.js +10 -0
  12. package/lib/commonjs/cli/expoConfig.d.ts +8 -0
  13. package/lib/commonjs/cli/expoConfig.js +34 -0
  14. package/lib/commonjs/cli/index.d.ts +2 -1
  15. package/lib/commonjs/cli/index.js +4 -1
  16. package/lib/commonjs/cli/link.js +32 -22
  17. package/lib/commonjs/plugin/src/types.d.ts +9 -0
  18. package/lib/commonjs/plugin/src/withNanoIconsFontLinking.d.ts +5 -0
  19. package/lib/commonjs/plugin/src/withNanoIconsFontLinking.js +24 -10
  20. package/lib/commonjs/scripts/cli.js +23 -11
  21. package/lib/commonjs/src/core/pipeline/config.d.ts +1 -0
  22. package/lib/commonjs/src/core/pipeline/managers.js +1 -2
  23. package/lib/commonjs/src/core/pipeline/run.js +1 -0
  24. package/lib/commonjs/src/core/svg/svg_dom.d.ts +1 -0
  25. package/lib/commonjs/src/core/svg/svg_dom.js +22 -17
  26. package/lib/commonjs/src/core/svg/svg_pathops.js +30 -16
  27. package/lib/commonjs/src/core/types.d.ts +3 -0
  28. package/lib/module/const/codegenPrimitives.js +2 -0
  29. package/lib/module/const/codegenPrimitives.js.map +1 -0
  30. package/lib/module/core/font/compile.js.map +1 -1
  31. package/lib/module/core/pipeline/config.js.map +1 -1
  32. package/lib/module/core/pipeline/managers.js.map +1 -1
  33. package/lib/module/core/pipeline/run.js +4 -1
  34. package/lib/module/core/pipeline/run.js.map +1 -1
  35. package/lib/module/core/svg/layers.js.map +1 -1
  36. package/lib/module/core/svg/svg_dom.js +19 -19
  37. package/lib/module/core/svg/svg_dom.js.map +1 -1
  38. package/lib/module/core/svg/svg_pathops.js.map +1 -1
  39. package/lib/module/createNanoIconsSet.js +2 -2
  40. package/lib/module/createNanoIconsSet.js.map +1 -1
  41. package/lib/module/createNanoIconsSet.native.js +43 -16
  42. package/lib/module/createNanoIconsSet.native.js.map +1 -1
  43. package/lib/module/createNanoIconsSet.shared.js +49 -20
  44. package/lib/module/createNanoIconsSet.shared.js.map +1 -1
  45. package/lib/module/createNanoIconsSet.web.js +78 -0
  46. package/lib/module/createNanoIconsSet.web.js.map +1 -0
  47. package/lib/module/loadDynamicFont.js +118 -0
  48. package/lib/module/loadDynamicFont.js.map +1 -0
  49. package/lib/module/specs/NanoIconViewNativeComponent.ts +9 -8
  50. package/lib/module/specs/NativeNanoIconsFontLoader.js +7 -0
  51. package/lib/module/specs/NativeNanoIconsFontLoader.js.map +1 -0
  52. package/lib/module/utils/glyphRuntime.js +37 -0
  53. package/lib/module/utils/glyphRuntime.js.map +1 -0
  54. package/lib/typescript/__tests__/glyphRuntime.unit.test.d.ts +2 -0
  55. package/lib/typescript/__tests__/glyphRuntime.unit.test.d.ts.map +1 -0
  56. package/lib/typescript/__tests__/link.unit.test.d.ts +3 -0
  57. package/lib/typescript/__tests__/link.unit.test.d.ts.map +1 -0
  58. package/lib/typescript/__tests__/loadDynamicFont.unit.test.d.ts +2 -0
  59. package/lib/typescript/__tests__/loadDynamicFont.unit.test.d.ts.map +1 -0
  60. package/lib/typescript/cli/build.d.ts +3 -0
  61. package/lib/typescript/cli/build.d.ts.map +1 -1
  62. package/lib/typescript/cli/link.d.ts +12 -0
  63. package/lib/typescript/cli/link.d.ts.map +1 -0
  64. package/lib/typescript/src/const/codegenPrimitives.d.ts +3 -0
  65. package/lib/typescript/src/const/codegenPrimitives.d.ts.map +1 -0
  66. package/lib/typescript/src/core/font/compile.d.ts.map +1 -1
  67. package/lib/typescript/src/core/pipeline/config.d.ts +1 -0
  68. package/lib/typescript/src/core/pipeline/config.d.ts.map +1 -1
  69. package/lib/typescript/src/core/pipeline/managers.d.ts.map +1 -1
  70. package/lib/typescript/src/core/pipeline/run.d.ts.map +1 -1
  71. package/lib/typescript/src/core/svg/svg_dom.d.ts +1 -0
  72. package/lib/typescript/src/core/svg/svg_dom.d.ts.map +1 -1
  73. package/lib/typescript/src/core/svg/svg_pathops.d.ts.map +1 -1
  74. package/lib/typescript/src/core/types.d.ts +3 -0
  75. package/lib/typescript/src/core/types.d.ts.map +1 -1
  76. package/lib/typescript/src/createNanoIconsSet.d.ts +1 -0
  77. package/lib/typescript/src/createNanoIconsSet.d.ts.map +1 -1
  78. package/lib/typescript/src/createNanoIconsSet.native.d.ts +1 -0
  79. package/lib/typescript/src/createNanoIconsSet.native.d.ts.map +1 -1
  80. package/lib/typescript/src/createNanoIconsSet.shared.d.ts +11 -0
  81. package/lib/typescript/src/createNanoIconsSet.shared.d.ts.map +1 -1
  82. package/lib/typescript/src/createNanoIconsSet.web.d.ts +7 -0
  83. package/lib/typescript/src/createNanoIconsSet.web.d.ts.map +1 -0
  84. package/lib/typescript/src/loadDynamicFont.d.ts +28 -0
  85. package/lib/typescript/src/loadDynamicFont.d.ts.map +1 -0
  86. package/lib/typescript/src/specs/NanoIconViewNativeComponent.d.ts +9 -8
  87. package/lib/typescript/src/specs/NanoIconViewNativeComponent.d.ts.map +1 -1
  88. package/lib/typescript/src/specs/NativeNanoIconsFontLoader.d.ts +8 -0
  89. package/lib/typescript/src/specs/NativeNanoIconsFontLoader.d.ts.map +1 -0
  90. package/lib/typescript/src/types.d.ts +7 -1
  91. package/lib/typescript/src/types.d.ts.map +1 -1
  92. package/lib/typescript/src/utils/glyphRuntime.d.ts +18 -0
  93. package/lib/typescript/src/utils/glyphRuntime.d.ts.map +1 -0
  94. package/package.json +8 -5
  95. package/plugin/src/types.ts +9 -0
  96. package/plugin/src/withNanoIconsFontLinking.ts +29 -10
  97. package/react-native-nano-icons.podspec +1 -1
  98. package/scripts/cli.ts +31 -11
  99. package/src/const/codegenPrimitives.ts +14 -0
  100. package/src/core/font/compile.ts +1 -2
  101. package/src/core/pipeline/config.ts +1 -0
  102. package/src/core/pipeline/managers.ts +5 -10
  103. package/src/core/pipeline/run.ts +11 -6
  104. package/src/core/svg/layers.ts +4 -4
  105. package/src/core/svg/svg_dom.ts +26 -23
  106. package/src/core/svg/svg_pathops.ts +50 -24
  107. package/src/core/types.ts +10 -2
  108. package/src/createNanoIconsSet.native.tsx +78 -26
  109. package/src/createNanoIconsSet.shared.tsx +93 -40
  110. package/src/createNanoIconsSet.tsx +9 -1
  111. package/src/createNanoIconsSet.web.tsx +109 -0
  112. package/src/loadDynamicFont.ts +162 -0
  113. package/src/specs/NanoIconViewNativeComponent.ts +9 -8
  114. package/src/specs/NativeNanoIconsFontLoader.ts +11 -0
  115. package/src/types.ts +5 -1
  116. package/src/utils/glyphRuntime.ts +46 -0
@@ -0,0 +1,162 @@
1
+ import { useSyncExternalStore } from 'react';
2
+ import { Image, Platform } from 'react-native';
3
+ import NanoIconsFontLoader from './specs/NativeNanoIconsFontLoader';
4
+
5
+ /**
6
+ * Runtime registration of dynamically-linked (`l:"d"`) fonts.
7
+ */
8
+
9
+ type FontStatus = 'loading' | 'ready' | 'error';
10
+
11
+ const statusByFamily = new Map<string, FontStatus>();
12
+ const inFlight = new Map<string, Promise<void>>();
13
+ const listeners = new Set<() => void>();
14
+
15
+ function emit(): void {
16
+ for (const l of listeners) l();
17
+ }
18
+
19
+ function setStatus(family: string, status: FontStatus): void {
20
+ statusByFamily.set(family, status);
21
+ emit();
22
+ }
23
+
24
+ export function getFontStatus(family: string): FontStatus | undefined {
25
+ return statusByFamily.get(family);
26
+ }
27
+
28
+ /** A font source: a require()'d module, an { uri }, or a path/uri string. */
29
+ export type FontSource = number | string | { uri: string };
30
+
31
+ export function resolveFontUri(font: unknown): string {
32
+ if (typeof font === 'number') {
33
+ const source = Image.resolveAssetSource(font);
34
+ if (!source?.uri) {
35
+ throw new Error(
36
+ '[react-native-nano-icons] Could not resolve the font asset to a uri.'
37
+ );
38
+ }
39
+ return source.uri;
40
+ }
41
+ if (typeof font === 'string') return font;
42
+ if (
43
+ font != null &&
44
+ typeof font === 'object' &&
45
+ typeof (font as { uri?: unknown }).uri === 'string'
46
+ ) {
47
+ return (font as { uri: string }).uri;
48
+ }
49
+ throw new Error(
50
+ '[react-native-nano-icons] Unsupported font source. Pass require("Foo.ttf"), { uri }, or a path string.'
51
+ );
52
+ }
53
+
54
+ async function register(family: string, uri: string): Promise<void> {
55
+ if (Platform.OS === 'web') {
56
+ // Browser FontFace API. Typed loosely so this compiles under both the RN-only
57
+ // lib config and a DOM-aware app config without depending on the DOM lib.
58
+ const g = globalThis as unknown as {
59
+ FontFace?: new (
60
+ family: string,
61
+ source: string
62
+ ) => {
63
+ load(): Promise<unknown>;
64
+ };
65
+ document?: { fonts?: { add(face: unknown): void } };
66
+ };
67
+ if (!g.FontFace || !g.document?.fonts) {
68
+ throw new Error(
69
+ '[react-native-nano-icons] FontFace API unavailable; cannot load dynamic font on this platform.'
70
+ );
71
+ }
72
+ const face = new g.FontFace(family, `url(${uri})`);
73
+ await face.load();
74
+ g.document.fonts.add(face);
75
+ return;
76
+ }
77
+
78
+ if (!NanoIconsFontLoader) {
79
+ throw new Error(
80
+ '[react-native-nano-icons] Native font loader is unavailable (Expo Go or module not built). ' +
81
+ 'Load the font yourself, or use a development/production build.'
82
+ );
83
+ }
84
+ await NanoIconsFontLoader.registerFont(family, uri);
85
+ }
86
+
87
+ /**
88
+ * Register `font` under `family`. Concurrent calls share one in-flight load.
89
+ * Once settled, a later call retries after an error or no-ops if already
90
+ * registered — unless `opts.force` re-registers (used by the explicit `loadFont`).
91
+ */
92
+ export function loadDynamicFont(
93
+ family: string,
94
+ font: unknown,
95
+ opts?: { force?: boolean }
96
+ ): Promise<void> {
97
+ // Dedupe concurrent calls.
98
+ const existing = inFlight.get(family);
99
+ if (existing) return existing;
100
+
101
+ // Already registered → skip, unless the caller forces a reload.
102
+ if (!opts?.force && statusByFamily.get(family) === 'ready') {
103
+ return Promise.resolve();
104
+ }
105
+
106
+ setStatus(family, 'loading');
107
+
108
+ const run = async (): Promise<void> => {
109
+ try {
110
+ await register(family, resolveFontUri(font));
111
+ setStatus(family, 'ready');
112
+ } catch (err) {
113
+ setStatus(family, 'error');
114
+ throw err;
115
+ }
116
+ };
117
+
118
+ const tracked = run();
119
+
120
+ // Release the slot once settled so later calls can retry/reload
121
+ inFlight.set(
122
+ family,
123
+ (async () => {
124
+ try {
125
+ await tracked;
126
+ } catch {
127
+ // status already reflects the failure
128
+ } finally {
129
+ inFlight.delete(family);
130
+ }
131
+ })()
132
+ );
133
+
134
+ return tracked;
135
+ }
136
+
137
+ /**
138
+ * Subscribe a component to a family's load state. Returns `true` while a managed
139
+ * font is still loading (so the icon should hide), `false` otherwise (ready,
140
+ * errored, or not managed by us).
141
+ */
142
+ export function useDynamicFontPending(
143
+ managed: boolean,
144
+ family: string
145
+ ): boolean {
146
+ const status = useSyncExternalStore(
147
+ (cb) => {
148
+ listeners.add(cb);
149
+ return () => listeners.delete(cb);
150
+ },
151
+ () => (managed ? statusByFamily.get(family) : undefined),
152
+ () => undefined
153
+ );
154
+ return managed && status === 'loading';
155
+ }
156
+
157
+ /** Test-only: reset module state between tests. */
158
+ export function __resetDynamicFontsForTests(): void {
159
+ statusByFamily.clear();
160
+ inFlight.clear();
161
+ listeners.clear();
162
+ }
@@ -1,15 +1,16 @@
1
1
  import { codegenNativeComponent } from 'react-native';
2
- import type { CodegenTypes as CT, ViewProps } from 'react-native';
2
+ import type { ViewProps } from 'react-native';
3
+ import type { Float, Int32 } from '../const/codegenPrimitives';
3
4
 
4
5
  export interface NativeProps extends ViewProps {
5
6
  fontFamily: string;
6
- codepoints: ReadonlyArray<CT.Int32>;
7
- colors: ReadonlyArray<CT.Int32>;
8
- fontSize: CT.Float;
9
- advanceWidth: CT.Int32;
10
- unitsPerEm: CT.Int32;
11
- iconWidth: CT.Float;
12
- iconHeight: CT.Float;
7
+ codepoints: ReadonlyArray<Int32>;
8
+ colors: ReadonlyArray<Int32>;
9
+ fontSize: Float;
10
+ advanceWidth: Int32;
11
+ unitsPerEm: Int32;
12
+ iconWidth: Float;
13
+ iconHeight: Float;
13
14
  }
14
15
 
15
16
  export default codegenNativeComponent<NativeProps>('NanoIconView');
@@ -0,0 +1,11 @@
1
+ import type { TurboModule } from 'react-native';
2
+ import { TurboModuleRegistry } from 'react-native';
3
+
4
+ export interface Spec extends TurboModule {
5
+ /** Pass glyphMap.m.f as `family`. On iOS it must also match the TTF's embedded PostScript/full name. */
6
+ registerFont(family: string, uri: string): Promise<boolean>;
7
+ }
8
+
9
+ // `get` (not `getEnforcing`) so the absence of the native module (web, Expo Go,
10
+ // module not built) returns null and the caller can degrade gracefully.
11
+ export default TurboModuleRegistry.get<Spec>('NanoIconsFontLoader');
package/src/types.ts CHANGED
@@ -18,10 +18,14 @@ export type IconProps<Name> = {
18
18
  accessible?: boolean;
19
19
  accessibilityLabel?: string;
20
20
  accessibilityRole?: AccessibilityRole;
21
+ accessibilityElementsHidden?: boolean; // iOS
22
+ importantForAccessibility?: 'auto' | 'yes' | 'no' | 'no-hide-descendants'; // Android
21
23
  ref?: Ref<ViewRef>;
22
24
  testID?: string;
23
25
  };
24
26
 
25
27
  export type IconComponent<GM extends NanoGlyphMapInput> = React.FC<
26
28
  IconProps<keyof GM['i']>
27
- >;
29
+ > & {
30
+ loadFont: (font?: number | string | { uri: string }) => Promise<void>;
31
+ };
@@ -0,0 +1,46 @@
1
+ import type { ColorValue } from 'react-native';
2
+ import type { NanoGlyphMapInput, GlyphEntry } from '../core/types';
3
+
4
+ /** Default icon size (px) shared by every platform renderer. */
5
+ export const DEFAULT_ICON_SIZE = 12;
6
+
7
+ /**
8
+ * Look up a glyph entry by name, falling back to a single '?' layer
9
+ * (codepoint 63) sized to the font's em when the name is unknown.
10
+ */
11
+ export function resolveGlyphEntry<GM extends NanoGlyphMapInput>(
12
+ glyphMap: GM,
13
+ name: keyof GM['i']
14
+ ): GlyphEntry {
15
+ return (glyphMap.i[name as string] ?? [
16
+ glyphMap.m.u,
17
+ [[63, 'black']],
18
+ ]) as GlyphEntry;
19
+ }
20
+
21
+ /** A memoized codepoint -> string converter, scoped per icon set. */
22
+ export function createCharCache(): (codepoint: number) => string {
23
+ const cache = new Map<number, string>();
24
+ return (codepoint) => {
25
+ let ch = cache.get(codepoint);
26
+ if (ch === undefined) {
27
+ ch = String.fromCodePoint(codepoint);
28
+ cache.set(codepoint, ch);
29
+ }
30
+ return ch;
31
+ };
32
+ }
33
+
34
+ /**
35
+ * Build a per-layer color resolver for one render: an explicit per-index
36
+ * color wins, else the last supplied palette color spills onto remaining
37
+ * layers, else the glyph's own source color, else black.
38
+ */
39
+ export function createLayerColorResolver(
40
+ color: ColorValue | ColorValue[] | undefined
41
+ ): (index: number, srcColor: string | undefined) => ColorValue {
42
+ const colorArray = Array.isArray(color) ? color : [color];
43
+ const lastPaletteColor = colorArray[colorArray.length - 1];
44
+ return (index, srcColor) =>
45
+ colorArray[index] ?? lastPaletteColor ?? srcColor ?? 'black';
46
+ }