@qumra/fanar-native 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 CHANGED
@@ -1,83 +1,19 @@
1
1
  # @qumra/fanar-native
2
2
 
3
- مكتبة مكوّنات قمرة لـReact Native — النظير النيتف لـ`@qumra/fanar`.
4
-
5
- نفس التوكنز، ونفس أسماء المكوّنات، ونفس أسماء الخصائص — بتنفيذ مكتوب
6
- لـReact Native لا مترجَم عنه. بلا تبعيّات وقت تشغيل.
7
-
8
- ```tsx
9
- import { FanarProvider, Screen, Card, Button, Text } from '@qumra/fanar-native'
10
-
11
- <FanarProvider mode="system" lang="ar">
12
- <Screen>
13
- <Card padded>
14
- <Text size="h3" weight="black">مرحباً</Text>
15
- <Button variant="primary" onPress={save}>احفظ</Button>
16
- </Card>
17
- </Screen>
18
- </FanarProvider>
19
- ```
20
-
21
- ## التركيب
22
-
23
3
  ```bash
24
4
  npm i @qumra/fanar-native
25
5
  ```
26
6
 
27
- نظائر مطلوبة: `react` · `react-native` · `lucide-react-native` ·
28
- `react-native-safe-area-context` · `react-native-svg`.
29
-
30
- ثمّ:
31
-
32
- 1. ركّب `<SafeAreaProvider>` عند جذر التطبيق.
33
- 2. ركّب `<FanarProvider>` تحته.
34
- 3. ضع ملفّات الأسكندرية الثمانية في `src/assets/fonts` وشغّل
35
- `npx react-native-asset`. (أو مرّر `fonts={SYSTEM_FONT}` وخذ خطّ
36
- النظام.)
37
-
38
- `DrawerMenu` **محتوى** لا مُلاحِظ: يرسم الأقسام ويصرف الاختيار، ولا
39
- يعرف شيئاً عن `@react-navigation/drawer`. مرّره إلى `drawerContent`.
40
-
41
- ## الاتجاه
42
-
43
- `FanarProvider` يضع `direction` على View جذرٍ **`flex: 1`**، فكلّ ما تحته
44
- — الخصائص المنطقية، و`flexDirection: 'row'`، ومحاذاة النصّ، والأسهم
45
- المعكوسة — يتبع `lang` فوراً، بلا إعادة تشغيل. لذلك:
46
-
47
- - ضعه **فوق** `NavigationContainer`، لتقع الشاشات كلّها تحته.
48
- - لا تستدعِ `I18nManager.forceRTL(true)` لتطبيقٍ يبدّل لغته.
49
- - هيدر `react-navigation` وإيماءة الرجوع يقرآن `I18nManager` مباشرةً:
50
- مرّر `<NavigationContainer direction={dir}>` (React Navigation 7)،
51
- و`dir` من `useLang()`.
7
+ Peers: `react` · `react-native` · `lucide-react-native` ·
8
+ `react-native-safe-area-context` · `react-native-svg`
52
9
 
53
- ## ما في الحزمة
54
-
55
- **الأساس** — `FanarProvider` · `useFanar` · `useTheme` · `useLang` ·
56
- `buildTheme` · `TOKENS` · `DARK_TOKENS` · `icon()`
57
-
58
- **النصّ والأزرار** — `Text` · `Button` · `IconButton`
59
-
60
- **العرض** — `Badge` · `Counter` · `Avatar` · `Card` · `CardHeader` ·
61
- `PageHeader` · `StatCard` · `Progress` · `ProgressSteps` · `Skeleton` ·
62
- `Divider`
63
-
64
- **الحقول** — `Field` · `Input` · `SearchInput` · `Textarea` · `Select` ·
65
- `Checkbox` · `Radio` · `RadioGroup` · `Switch` · `NumberStepper`
66
-
67
- **التنقّل** — `Tabs` · `FilterChips` · `Accordion` · `Timeline` ·
68
- `Stepper`
69
-
70
- **الطبقات** — `Sheet` · `SheetRow` · `Modal` · `Alert` · `EmptyState` ·
71
- `toast` · `ToastHost` · `Tooltip`
72
-
73
- **القشرة** — `Screen` · `AppHeader` · `BottomTabBar` · `DrawerMenu`
74
-
75
- **أسطح قمرة** — `Hero` · `PromptCard` · `ListRow` · `StepRow` ·
76
- `RuleGrid` · `Glow`
77
-
78
- **المال والتقييم** — `Money` · `MoneyProvider` · `useCurrency` ·
79
- `Stars` · `RatingInput`
80
-
81
- **اللغة** — `translate` · `phrase` · `DICT`
82
-
83
- `@qumra/fanar-native/tokens` — القيم وحدها، بلا مكوّنات.
10
+ ```tsx
11
+ import { SafeAreaProvider } from 'react-native-safe-area-context'
12
+ import { FanarProvider } from '@qumra/fanar-native'
13
+
14
+ <SafeAreaProvider>
15
+ <FanarProvider lang="ar">
16
+ <App />
17
+ </FanarProvider>
18
+ </SafeAreaProvider>
19
+ ```
package/lib/icons.d.ts CHANGED
@@ -14,5 +14,17 @@ export type IconProps = {
14
14
  flip?: boolean;
15
15
  onPress?: () => void;
16
16
  };
17
+ export declare const ICON_SIZE: {
18
+ readonly xs: 12;
19
+ readonly sm: 14;
20
+ readonly md: 16;
21
+ readonly lg: 18;
22
+ readonly xl: 20;
23
+ readonly '2xl': 24;
24
+ readonly '3xl': 32;
25
+ };
26
+ export type IconSize = keyof typeof ICON_SIZE;
27
+ export declare const ICON_STROKE = 2;
28
+ export declare function snapIconSize(size: number): number;
17
29
  export declare function Icon({ name, size, color, strokeWidth, style, flip, onPress }: IconProps): React.FunctionComponentElement<import("lucide-react-native").LucideProps>;
18
30
  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
@@ -386,13 +386,40 @@ export function icon(name) {
386
386
  export function hasIcon(name) {
387
387
  return name in QUMRA_FOR || name in MAP || name in ALIAS;
388
388
  }
389
- export function Icon({ name, size = 16, color, strokeWidth, style, flip, onPress }) {
389
+ export const ICON_SIZE = {
390
+ xs: 12,
391
+ sm: 14,
392
+ md: 16,
393
+ lg: 18,
394
+ xl: 20,
395
+ '2xl': 24,
396
+ '3xl': 32,
397
+ };
398
+ const ICON_SCALE = [12, 14, 16, 18, 20, 22, 24, 28, 32, 36, 40, 44, 48, 56];
399
+ export const ICON_STROKE = 2;
400
+ export function snapIconSize(size) {
401
+ if (!Number.isFinite(size))
402
+ return ICON_SIZE.md;
403
+ if (size <= ICON_SCALE[0])
404
+ return ICON_SCALE[0];
405
+ const top = ICON_SCALE[ICON_SCALE.length - 1];
406
+ if (size >= top)
407
+ return size;
408
+ for (let i = 0; i < ICON_SCALE.length - 1; i++) {
409
+ const low = ICON_SCALE[i];
410
+ const high = ICON_SCALE[i + 1];
411
+ if (size <= high)
412
+ return size - low < high - size ? low : high;
413
+ }
414
+ return top;
415
+ }
416
+ export function Icon({ name, size = ICON_SIZE.md, color, strokeWidth, style, flip, onPress }) {
390
417
  const Cmp = icon(name);
391
418
  const { dir } = useFanar();
392
419
  return React.createElement(Cmp, {
393
- size,
420
+ size: snapIconSize(size),
394
421
  color,
395
- strokeWidth,
422
+ strokeWidth: strokeWidth ?? ICON_STROKE,
396
423
  onPress,
397
424
  style: flip && dir === 'rtl' ? [style, { transform: [{ scaleX: -1 }] }] : style,
398
425
  });
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, 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, 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';
@@ -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 = 20, active, color, activeColor, strokeWidth, style, }) {
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: size, mono: true, active: active });
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: size, strokeWidth: strokeWidth, color: active ? activeColor : color, style: style });
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: size, strokeWidth: strokeWidth, color: active ? activeColor : color, style: style });
17
+ return _jsx(Lucide, { size: px, strokeWidth: stroke, color: active ? activeColor : color, style: style });
16
18
  }
17
19
  export default NavIcon;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qumra/fanar-native",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Qumra's React Native component library — the native sibling of @qumra/fanar. Token-driven, RTL-first, zero runtime dependencies.",
5
5
  "license": "UNLICENSED",
6
6
  "keywords": [
@@ -36,9 +36,10 @@
36
36
  "typecheck": "tsc -p tsconfig.check.json --noEmit",
37
37
  "check-resolution": "node scripts/check-resolution.mjs",
38
38
  "build": "node scripts/build.mjs",
39
- "verify": "node scripts/check-resolution.mjs && node scripts/sync-tokens.mjs --check && node scripts/generate-tokens.mjs --check && npm run typecheck",
39
+ "verify": "node scripts/check-resolution.mjs && node scripts/check-icon-scale.mjs && node scripts/sync-tokens.mjs --check && node scripts/generate-tokens.mjs --check && npm run typecheck",
40
40
  "prepublishOnly": "npm run verify && npm run build && npm run check-publish",
41
- "check-publish": "node scripts/check-publish.mjs"
41
+ "check-publish": "node scripts/check-publish.mjs",
42
+ "check-icon-scale": "node scripts/check-icon-scale.mjs"
42
43
  },
43
44
  "peerDependencies": {
44
45
  "lucide-react-native": ">=0.400.0",