@qumra/fanar-native 0.1.1 → 0.1.4
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/lib/components/Surfaces.js +1 -1
- package/lib/icons.d.ts +13 -0
- package/lib/icons.js +87 -4
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/qumra/NavIcon.js +7 -5
- package/lib/qumra/QumraIcon.js +10 -4
- package/lib/qumra/data.generated.js +168 -24
- package/lib/theme/context.d.ts +2 -1
- package/lib/theme/context.js +3 -3
- package/lib/theme/theme.d.ts +1 -1
- package/lib/theme/theme.js +11 -3
- package/package.json +5 -3
|
@@ -70,7 +70,7 @@ export function StepRow({ icon, title, why, done, action, onPress, style }) {
|
|
|
70
70
|
const inner = (_jsxs(_Fragment, { children: [done ? (_jsx(View, { style: [
|
|
71
71
|
styles.stepIcon,
|
|
72
72
|
{ borderRadius: theme.radius.field, backgroundColor: theme.color.brand },
|
|
73
|
-
], children: _jsx(Check, { size: 17, strokeWidth: 3, color: "#ffffff" }) })) : brand ? (_jsx(QumraIcon, { name: brand, size: 34 })) : (_jsx(View, { style: [
|
|
73
|
+
], children: _jsx(Check, { size: 17, strokeWidth: 3, color: "#ffffff" }) })) : brand ? (_jsx(View, { style: styles.stepIcon, children: _jsx(QumraIcon, { name: brand, size: 34 }) })) : (_jsx(View, { style: [
|
|
74
74
|
styles.stepIcon,
|
|
75
75
|
{ borderRadius: theme.radius.field, backgroundColor: theme.color.soft },
|
|
76
76
|
], children: Lucide ? _jsx(Lucide, { size: 18, strokeWidth: 2, color: theme.color.brand }) : null })), _jsxs(View, { style: styles.rowText, children: [_jsx(Text, { size: 14.5, weight: "extraBold", color: done ? 'muted' : 'ink', align: "start", style: done ? styles.struck : { textAlign: 'left' }, children: title }), why ? (_jsx(Text, { size: 12.5, style: { textAlign: 'left' }, color: "muted", align: "start", leading: "relaxed", lines: 1, children: why })) : null] }), action] }));
|
package/lib/icons.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
3
3
|
import { AlertTriangle, ArrowLeft, ArrowRight, BarChart3, Bell, Bot, Check, CheckCheck, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Copy, ExternalLink, Globe, Home, Info, Lock, Menu, Minus, Plus, Search, Share2, Sparkles, Star, Undo2, X, type LucideIcon } from 'lucide-react-native';
|
|
4
4
|
export type { LucideIcon };
|
|
5
|
+
export declare function uiIconFor(name: string): string | undefined;
|
|
5
6
|
export declare function qumraIconFor(name: string): string | undefined;
|
|
6
7
|
export declare function icon(name: string): LucideIcon;
|
|
7
8
|
export declare function hasIcon(name: string): boolean;
|
|
@@ -14,5 +15,17 @@ export type IconProps = {
|
|
|
14
15
|
flip?: boolean;
|
|
15
16
|
onPress?: () => void;
|
|
16
17
|
};
|
|
18
|
+
export declare const ICON_SIZE: {
|
|
19
|
+
readonly xs: 12;
|
|
20
|
+
readonly sm: 14;
|
|
21
|
+
readonly md: 16;
|
|
22
|
+
readonly lg: 18;
|
|
23
|
+
readonly xl: 20;
|
|
24
|
+
readonly '2xl': 24;
|
|
25
|
+
readonly '3xl': 32;
|
|
26
|
+
};
|
|
27
|
+
export type IconSize = keyof typeof ICON_SIZE;
|
|
28
|
+
export declare const ICON_STROKE = 2;
|
|
29
|
+
export declare function snapIconSize(size: number): number;
|
|
17
30
|
export declare function Icon({ name, size, color, strokeWidth, style, flip, onPress }: IconProps): React.FunctionComponentElement<import("lucide-react-native").LucideProps>;
|
|
18
31
|
export { AlertTriangle, ArrowLeft, ArrowRight, BarChart3, Bell, Bot, Check, CheckCheck, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Copy, ExternalLink, Globe, Home, Info, Lock, Menu, Minus, Plus, Search, Share2, Sparkles, Star, Undo2, X, };
|
package/lib/icons.js
CHANGED
|
@@ -374,6 +374,59 @@ function qumraAs(qumraName) {
|
|
|
374
374
|
wrapped[qumraName] = Wrapped;
|
|
375
375
|
return wrapped[qumraName];
|
|
376
376
|
}
|
|
377
|
+
const stemOf = (name) => name.replace(/-(outline|sharp)$/, '');
|
|
378
|
+
const UI_FOR = {
|
|
379
|
+
add: 'plus', 'add-circle': 'plus', plus: 'plus', 'folder-plus': 'plus', 'package-plus': 'plus',
|
|
380
|
+
'person-add': 'user-plus', remove: 'close', close: 'close', 'close-circle': 'close', x: 'close',
|
|
381
|
+
checkmark: 'check', 'checkmark-circle': 'check', 'checkmark-done': 'check', check: 'check',
|
|
382
|
+
checkbox: 'check', trash: 'trash', 'trash-2': 'trash', create: 'pencil', pencil: 'pencil',
|
|
383
|
+
brush: 'pencil', 'color-wand': 'sparkle', copy: 'copy', save: 'save', download: 'save',
|
|
384
|
+
'cloud-download': 'save', 'cloud-upload': 'publish', print: 'print', send: 'send',
|
|
385
|
+
'share-social': 'share', 'share-2': 'share', search: 'search', filter: 'filter',
|
|
386
|
+
funnel: 'filter', options: 'sliders', sync: 'repeat', reload: 'repeat', refresh: 'reset',
|
|
387
|
+
'refresh-circle': 'reset', scan: 'search', stop: 'stop', 'pause-circle': 'stop', play: 'live',
|
|
388
|
+
'play-circle': 'live', 'log-out': 'logout', exit: 'logout', open: 'external',
|
|
389
|
+
'external-link': 'external', link: 'link', unlink: 'unlink', expand: 'expand',
|
|
390
|
+
contract: 'collapse', resize: 'expand',
|
|
391
|
+
'alert-circle': 'alert', 'alert-triangle': 'alert', warning: 'alert', 'help-circle': 'alert',
|
|
392
|
+
'information-circle': 'alert', 'life-buoy': 'alert', ban: 'ban', 'shield-checkmark': 'shield-check',
|
|
393
|
+
'shield-half': 'shield', 'lock-closed': 'lock', key: 'key', 'finger-print': 'key',
|
|
394
|
+
eye: 'eye', 'eye-off': 'eye-off', star: 'star', crown: 'crown', diamond: 'crown',
|
|
395
|
+
sparkles: 'sparkle', rocket: 'rocket', flash: 'bolt', flame: 'flame', bulb: 'bulb',
|
|
396
|
+
storefront: 'store', business: 'building', cube: 'box', 'package-open': 'box',
|
|
397
|
+
'bag-handle': 'bag', bag: 'bag', cart: 'cart', shoppingcart: 'cart', pricetag: 'tag',
|
|
398
|
+
pricetags: 'tag', gift: 'tag', ticket: 'tag', receipt: 'receipt', card: 'card',
|
|
399
|
+
cash: 'money', payments: 'money', wallet: 'wallet', calculator: 'sigma',
|
|
400
|
+
person: 'user', 'person-circle': 'user', user: 'user', people: 'users', customers: 'users',
|
|
401
|
+
partners: 'users', car: 'truck', barcode: 'hash', keypad: 'hash',
|
|
402
|
+
document: 'file', documents: 'file', 'document-text': 'file', reader: 'file', sheet: 'sheet',
|
|
403
|
+
folder: 'folder', library: 'layers', albums: 'layers', layers: 'layers', book: 'note',
|
|
404
|
+
journal: 'note', chatbubble: 'note', chatbubbles: 'note', text: 'text', 'code-slash': 'code',
|
|
405
|
+
terminal: 'code', image: 'image-plus', images: 'image-plus', camera: 'image-plus',
|
|
406
|
+
'camera-off': 'image-off', videocam: 'film', film: 'film', shapes: 'template',
|
|
407
|
+
'layout-grid': 'grid', grid: 'grid', apps: 'grid', widgets: 'template', list: 'rows',
|
|
408
|
+
'reorder-three': 'rows', 'drag-indicator': 'grip', archive: 'archive',
|
|
409
|
+
'bar-chart': 'chart', 'stats-chart': 'chart', 'pie-chart': 'chart', analytics: 'chart',
|
|
410
|
+
pulse: 'chart', speedometer: 'chart', 'trending-up': 'trend-up', 'trending-down': 'trend-down',
|
|
411
|
+
settings: 'gear', construct: 'gear', build: 'gear', hammer: 'gear', 'color-palette': 'palette',
|
|
412
|
+
globe: 'globe', earth: 'globe', language: 'language', server: 'database', cloud: 'database',
|
|
413
|
+
'cloud-offline': 'database', desktop: 'desktop', laptop: 'desktop', 'phone-portrait': 'mobile',
|
|
414
|
+
'tablet-portrait': 'tablet', radio: 'plug', bot: 'sparkle',
|
|
415
|
+
mail: 'mail', 'mail-open': 'mail', 'mail-unread': 'mail', call: 'phone', phone: 'phone',
|
|
416
|
+
'phone-call': 'phone', headset: 'phone', megaphone: 'megaphone', notifications: 'bell',
|
|
417
|
+
'notifications-off': 'bell', bell: 'bell', time: 'clock', clock: 'clock',
|
|
418
|
+
calendar: 'calendar', 'calendar-clear': 'calendar', location: 'pin', 'map-marker': 'pin',
|
|
419
|
+
map: 'pin', navigate: 'cursor', pin: 'pin', flag: 'thumbtack', menu: 'menu',
|
|
420
|
+
'ellipsis-vertical': 'more', 'ellipsis-horizontal': 'more', more: 'more',
|
|
421
|
+
'swap-vertical': 'sort', sun: 'live', moon: 'live',
|
|
422
|
+
'arrow-back': 'arrow-left', 'arrow-forward': 'arrow-right',
|
|
423
|
+
'arrow-undo': 'undo', 'arrow-redo': 'redo',
|
|
424
|
+
'chevron-down': 'chevron-down', 'chevron-small-down': 'chevron-down', 'chevron-up': 'chevron-up',
|
|
425
|
+
};
|
|
426
|
+
export function uiIconFor(name) {
|
|
427
|
+
const tool = UI_FOR[stemOf(name)];
|
|
428
|
+
return tool ? `ui-${tool}` : undefined;
|
|
429
|
+
}
|
|
377
430
|
export function qumraIconFor(name) {
|
|
378
431
|
return QUMRA_FOR[name];
|
|
379
432
|
}
|
|
@@ -381,18 +434,48 @@ export function icon(name) {
|
|
|
381
434
|
const brand = QUMRA_FOR[name];
|
|
382
435
|
if (brand)
|
|
383
436
|
return qumraAs(brand);
|
|
437
|
+
const tool = uiIconFor(name);
|
|
438
|
+
if (tool)
|
|
439
|
+
return qumraAs(tool);
|
|
384
440
|
return MAP[name] ?? ALIAS[name] ?? Home;
|
|
385
441
|
}
|
|
386
442
|
export function hasIcon(name) {
|
|
387
|
-
return name in QUMRA_FOR || name in MAP || name in ALIAS;
|
|
443
|
+
return name in QUMRA_FOR || !!uiIconFor(name) || name in MAP || name in ALIAS;
|
|
444
|
+
}
|
|
445
|
+
export const ICON_SIZE = {
|
|
446
|
+
xs: 12,
|
|
447
|
+
sm: 14,
|
|
448
|
+
md: 16,
|
|
449
|
+
lg: 18,
|
|
450
|
+
xl: 20,
|
|
451
|
+
'2xl': 24,
|
|
452
|
+
'3xl': 32,
|
|
453
|
+
};
|
|
454
|
+
const ICON_SCALE = [12, 14, 16, 18, 20, 22, 24, 28, 32, 36, 40, 44, 48, 56];
|
|
455
|
+
export const ICON_STROKE = 2;
|
|
456
|
+
export function snapIconSize(size) {
|
|
457
|
+
if (!Number.isFinite(size))
|
|
458
|
+
return ICON_SIZE.md;
|
|
459
|
+
if (size <= ICON_SCALE[0])
|
|
460
|
+
return ICON_SCALE[0];
|
|
461
|
+
const top = ICON_SCALE[ICON_SCALE.length - 1];
|
|
462
|
+
if (size >= top)
|
|
463
|
+
return size;
|
|
464
|
+
for (let i = 0; i < ICON_SCALE.length - 1; i++) {
|
|
465
|
+
const low = ICON_SCALE[i];
|
|
466
|
+
const high = ICON_SCALE[i + 1];
|
|
467
|
+
if (size <= high)
|
|
468
|
+
return size - low < high - size ? low : high;
|
|
469
|
+
}
|
|
470
|
+
return top;
|
|
388
471
|
}
|
|
389
|
-
export function Icon({ name, size =
|
|
472
|
+
export function Icon({ name, size = ICON_SIZE.md, color, strokeWidth, style, flip, onPress }) {
|
|
390
473
|
const Cmp = icon(name);
|
|
391
474
|
const { dir } = useFanar();
|
|
392
475
|
return React.createElement(Cmp, {
|
|
393
|
-
size,
|
|
476
|
+
size: snapIconSize(size),
|
|
394
477
|
color,
|
|
395
|
-
strokeWidth,
|
|
478
|
+
strokeWidth: strokeWidth ?? ICON_STROKE,
|
|
396
479
|
onPress,
|
|
397
480
|
style: flip && dir === 'rtl' ? [style, { transform: [{ scaleX: -1 }] }] : style,
|
|
398
481
|
});
|
package/lib/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { TONE_SOFT, TONE_SOLID, type Tone, type ToneColors } from './lib/tone';
|
|
|
5
5
|
export { toLatinDigits, toArabicDigits, groupDigits } from './lib/digits';
|
|
6
6
|
export { usePressScale, useEnter, useSkeletonPulse, EASE_OUT, EASE_IN_OUT } from './lib/motion';
|
|
7
7
|
export { hitSlopFor, HIT_SLOP_SM, TOUCH_MIN } from './lib/a11y';
|
|
8
|
-
export { icon, hasIcon, Icon, qumraIconFor, type LucideIcon, type IconProps } from './icons';
|
|
8
|
+
export { icon, hasIcon, Icon, qumraIconFor, uiIconFor, snapIconSize, ICON_SIZE, ICON_STROKE, type LucideIcon, type IconProps, type IconSize, } from './icons';
|
|
9
9
|
export { QumraIcon, hasQumraIcon, QUMRA_ICONS, type QumraIconProps, type QumraIconName, type QumraIconSpec, } from './qumra/QumraIcon';
|
|
10
10
|
export { NavIcon, type NavIconProps } from './qumra/NavIcon';
|
|
11
11
|
export { art, hasArt, BRAND, ART, type BrandName, type ArtName } from './art';
|
package/lib/index.js
CHANGED
|
@@ -5,7 +5,7 @@ export { TONE_SOFT, TONE_SOLID } from './lib/tone';
|
|
|
5
5
|
export { toLatinDigits, toArabicDigits, groupDigits } from './lib/digits';
|
|
6
6
|
export { usePressScale, useEnter, useSkeletonPulse, EASE_OUT, EASE_IN_OUT } from './lib/motion';
|
|
7
7
|
export { hitSlopFor, HIT_SLOP_SM, TOUCH_MIN } from './lib/a11y';
|
|
8
|
-
export { icon, hasIcon, Icon, qumraIconFor } from './icons';
|
|
8
|
+
export { icon, hasIcon, Icon, qumraIconFor, uiIconFor, snapIconSize, ICON_SIZE, ICON_STROKE, } from './icons';
|
|
9
9
|
export { QumraIcon, hasQumraIcon, QUMRA_ICONS, } from './qumra/QumraIcon';
|
|
10
10
|
export { NavIcon } from './qumra/NavIcon';
|
|
11
11
|
export { art, hasArt, BRAND, ART } from './art';
|
package/lib/qumra/NavIcon.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { icon as resolveLucide, qumraIconFor } from '../icons';
|
|
2
|
+
import { ICON_SIZE, ICON_STROKE, icon as resolveLucide, qumraIconFor, snapIconSize } from '../icons';
|
|
3
3
|
import { QUMRA_ICONS, QumraIcon } from './QumraIcon';
|
|
4
4
|
const brandName = (name) => qumraIconFor(name) ?? (name in QUMRA_ICONS ? name : undefined);
|
|
5
|
-
export function NavIcon({ icon, size =
|
|
5
|
+
export function NavIcon({ icon, size = ICON_SIZE.xl, active, color, activeColor, strokeWidth, style, }) {
|
|
6
|
+
const px = snapIconSize(size);
|
|
7
|
+
const stroke = strokeWidth ?? ICON_STROKE;
|
|
6
8
|
if (typeof icon === 'string') {
|
|
7
9
|
const brand = brandName(icon);
|
|
8
10
|
if (brand) {
|
|
9
|
-
return _jsx(QumraIcon, { name: brand, size:
|
|
11
|
+
return _jsx(QumraIcon, { name: brand, size: px, mono: true, active: active });
|
|
10
12
|
}
|
|
11
13
|
const Lucide = resolveLucide(icon);
|
|
12
|
-
return _jsx(Lucide, { size:
|
|
14
|
+
return _jsx(Lucide, { size: px, strokeWidth: stroke, color: active ? activeColor : color, style: style });
|
|
13
15
|
}
|
|
14
16
|
const Lucide = icon;
|
|
15
|
-
return _jsx(Lucide, { size:
|
|
17
|
+
return _jsx(Lucide, { size: px, strokeWidth: stroke, color: active ? activeColor : color, style: style });
|
|
16
18
|
}
|
|
17
19
|
export default NavIcon;
|
package/lib/qumra/QumraIcon.js
CHANGED
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import Svg, { Circle, ClipPath, Defs, Ellipse, G, LinearGradient, Path, RadialGradient, Rect, Stop, Text as SvgText, } from 'react-native-svg';
|
|
4
4
|
import { QUMRA_ICONS } from './data.generated';
|
|
5
|
+
import { useFanar } from '../theme/context';
|
|
5
6
|
const TAGS = {
|
|
6
7
|
svg: Svg,
|
|
7
8
|
g: G,
|
|
@@ -34,7 +35,8 @@ function grayscale(value) {
|
|
|
34
35
|
const c = l.toString(16).padStart(2, '0');
|
|
35
36
|
return `#${c}${c}${c}`;
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
+
const CURRENT = 'currentColor';
|
|
39
|
+
function build(node, uid, key, tint, current) {
|
|
38
40
|
const [tag, props, kids] = node;
|
|
39
41
|
const Cmp = TAGS[tag];
|
|
40
42
|
if (!Cmp)
|
|
@@ -49,7 +51,9 @@ function build(node, uid, key, tint) {
|
|
|
49
51
|
v = `${v}-${uid}`;
|
|
50
52
|
else if (typeof v === 'string' && v.indexOf('url(#') !== -1)
|
|
51
53
|
v = uniquify(v, uid);
|
|
52
|
-
if (
|
|
54
|
+
if (isPaint(k) && v === CURRENT)
|
|
55
|
+
v = current ?? v;
|
|
56
|
+
if (tint && isPaint(k) && v !== 'none' && v !== CURRENT)
|
|
53
57
|
v = flat ?? grayscale(v);
|
|
54
58
|
p[k] = v;
|
|
55
59
|
}
|
|
@@ -59,7 +63,7 @@ function build(node, uid, key, tint) {
|
|
|
59
63
|
if (p.strokeOpacity !== undefined)
|
|
60
64
|
p.strokeOpacity = '1';
|
|
61
65
|
}
|
|
62
|
-
const children = kids?.map((c, i) => build(c, uid, i, tint)).filter(Boolean);
|
|
66
|
+
const children = kids?.map((c, i) => build(c, uid, i, tint, current)).filter(Boolean);
|
|
63
67
|
return React.createElement(Cmp, { key, ...p }, children?.length ? children : undefined);
|
|
64
68
|
}
|
|
65
69
|
function badge(count) {
|
|
@@ -74,11 +78,13 @@ function badge(count) {
|
|
|
74
78
|
return (_jsxs(G, { children: [_jsx(Rect, { x: x, y: 2.2, width: w, height: 12.4, rx: 6.2, fill: "#E8A94F", stroke: "#F2F7F8", strokeWidth: 1.4 }), _jsx(SvgText, { x: x + w / 2, y: 8.4, dy: fs * 0.35, textAnchor: "middle", fill: "#FFFFFF", fontSize: fs, fontWeight: "700", children: s })] }, "qi-count"));
|
|
75
79
|
}
|
|
76
80
|
export function QumraIcon({ name, size = 32, mono, active, color, count, label, onPress, }) {
|
|
81
|
+
const { theme } = useFanar();
|
|
77
82
|
const spec = QUMRA_ICONS[name];
|
|
78
83
|
const uid = React.useId().replace(/[^a-zA-Z0-9]/g, '');
|
|
79
84
|
const dimmed = !!mono && !active;
|
|
80
85
|
const tint = dimmed ? color ?? 'gray' : undefined;
|
|
81
|
-
const
|
|
86
|
+
const current = color ?? theme.color.ink;
|
|
87
|
+
const children = React.useMemo(() => (spec ? spec.tree.map((n, i) => build(n, uid, i, tint, current)) : []), [spec, uid, tint, current]);
|
|
82
88
|
if (!spec) {
|
|
83
89
|
if (__DEV__)
|
|
84
90
|
console.warn(`[qumra-icons] لا توجد أيقونة بالاسم «${name}»`);
|