@mari-ui/native 0.1.0 → 0.2.0
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 +54 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -22
- package/dist/index.d.ts +14 -22
- package/dist/index.js.map +1 -1
- package/package.json +3 -7
package/README.md
CHANGED
|
@@ -1,144 +1,89 @@
|
|
|
1
1
|
# @mari-ui/native
|
|
2
2
|
|
|
3
|
-
mari-ui
|
|
3
|
+
[mari-ui](https://github.com/aykutyilmaz37/mari-ui) tasarım sisteminin React Native component'leri. Web'deki `@mari-ui/components` ile aynı prop adları, aynı tema ve renk sözleşmesi; ölçüler ise dokunmatik ekrana göre (en az 44 pt dokunma hedefi, platform metin ölçeği).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- **~45 component** — form, gezinme, veri gösterimi, katmanlar (Modal, BottomSheet, Toast), görsel galerisi.
|
|
6
|
+
- **Sıfır kurulum** — `StyleSheet` + `@mari-ui/tokens`; NativeWind, babel ya da metro eklentisi gerekmez.
|
|
7
|
+
- **Açık / koyu tema** otomatik (işletim sistemi tercihi), WCAG AA kontrastı token'lardan.
|
|
8
|
+
- Her component'te **iskelet** (`skeleton`), erişilebilirlik (rol, ad, durum) ve hareket azaltma desteği.
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
**Typography (Dalga 1):** Ölçek Apple HIG metin stilleri: gövde 17/22; `Title` 1–6 = Large Title 34 · Title 1 28 · Title 2 22 · Title 3 20 · Headline 17 · Subheadline 15. İç içe `Text` boyutu ebeveynden alır (RN stil mirası), yalnız renk/ağırlık değiştirir. `ellipsis` → `numberOfLines`. `Paragraph` iskeleti metin verilmişse satır sayısını metnin gerçek yüksekliğinden çıkarır (her genişlikte kaymasız). Ölçek `NativeConfigProvider` `typography` ile ezilir.
|
|
10
|
-
|
|
11
|
-
**Dalga 1a gösterim bileşenleri:** Ölçüler `nativeConfig` içinde (`avatar` 32/40/56, `badge` 16/20, `tag` 13/18 → 24pt), `NativeConfigProvider` ile ezilir. Divider düz çizgisi platform saç teli kalınlığında. Tag kapatma düğmesi `hitSlop` ile 44pt. Etiket/rozet gibi içeriği kadar olan öğeler `alignSelf: "flex-start"` kullanmaz (yatay satırda ebeveynin ortalamasını ezer) — `lib/Hug` deseni. Genel `Skeleton` satırları Typography ölçeğinde; `rows` beklenen satır sayısıyla eşleşmeli.
|
|
12
|
-
|
|
13
|
-
**Dalga 1b seçim bileşenleri (Checkbox, Radio/RadioGroup, Switch):** Kutu/daire M3 ölçüsünde (sm/md/lg 18/22/26, 2pt kenar), satırın tamamı dokunulabilir ve en az 44pt; Switch iOS UISwitch ölçüsü (md 51×31). Renk kuralları web ile aynı (`lib/selectionColors`): dinlenirken nötr `input` kenarı, `color` verilirse doğrulama rengi; Switch kapalıyken her renkte gri, topuz her zaman beyaz. `onChange` olay nesnesi değil değer verir (`boolean` ya da grup değeri). `RadioGroup` native'de varsayılan dikey. Durum hem `accessibilityState` hem `aria-*` ile verilir (react-native-web eski prop'u okumuyor).
|
|
14
|
-
|
|
15
|
-
**Dalga 1c giriş bileşenleri (Textarea, SearchInput, OtpInput, Field, Segmented):** Textarea ve SearchInput Input'un kutu/renk/odak kurallarını kullanır. OtpInput arkada TEK gerçek alan tutar (iOS `oneTimeCode`, Android `sms-otp` otomatik doldurma, yapıştırma); kutular görseldir, dar ekranda en az 32pt'ye daralır. `Field` içindeki kontrole etiketi erişilebilir ad, hatayı/ipucunu erişilebilir ipucu olarak verir; devre dışı, hata rengi ve iskelet de Field'dan gelir (Input, Textarea, OtpInput, Segmented, Checkbox, RadioGroup, Switch). Segmented iOS UISegmentedControl gibi (md 32pt, kayan seçim yüzeyi).
|
|
16
|
-
|
|
17
|
-
**Dalga 2a geri bildirim (Alert, Progress, Loading, Empty):** Alert error/warning'i ekran okuyucuda hemen, diğerlerini sırası gelince duyurur. Progress çizgi 4/6/10 (4 = iOS/M3), daire 48/80/120; değer metni durumu söyler ("%100, tamamlandı"). Loading belirsiz göstergede platformun `ActivityIndicator`'ını kullanır; satır içi / sarmalama (içerik yerinde, soluk, dokunmaya ve ekran okuyucuya kapalı) / tam ekran (`Modal`). Gizlenen her öğe hem `accessibilityElementsHidden` hem `aria-hidden` alır — react-native-web eskisini aria'ya çevirmiyor.
|
|
18
|
-
|
|
19
|
-
**Dalga 2b katmanlar (Modal, Dialog, BottomSheet, ActionSheet):** Modal web Modal'ının karşılığı; Dialog iOS uyarısı ölçülerinde kısa onay (kendi çizimimiz). BottomSheet ek paket olmadan (Animated + PanResponder) sürüklenir ve `snapPoints` duraklarında durur; panelin GÖRÜNEN YÜKSEKLİĞİ canlandırılır (kaydırma değil), böylece alt bölüm her durakta görünür ve içerik görünen alanda kayar. Durak verilmezse yükseklik başlık + içeriğin tam yüksekliği (`onContentSizeChange`) + alt bölümden hesaplanır. ActionSheet BottomSheet üzerine; seçilen eylem panel kapandıktan sonra (`onAfterClose`) çalışır — iOS, kapanmakta olan Modal varken yenisini göstermez. Karartma rengi `nativeColor` ile RN'nin anladığı `rgba()` biçimine çevrilir; pencere yüzeyleri koyu temada ince kenarlıkla ayrışır (`lib/overlay`).
|
|
20
|
-
|
|
21
|
-
**Dalga 2c Toast:** `ToastProvider` (uygulama kökünde) + `useToast()` → `showToast` / `dismiss` / `update`, web ile aynı API. Telefonda konum `top` / `bottom` (web köşe değerleri en yakın kenara eşlenir), kenar başına en fazla 3. Parmak üstündeyken sayaç durur, yana kaydırınca kapanır; ekran okuyucu açıkken eylemli Toast kendiliğinden kapanmaz, iOS'ta `announceForAccessibility` ile duyurulur. RN `Modal` değil, dokunuşu engellemeyen bir katman — açık bir Modal/BottomSheet'in altında kalır. Kenar payları `insets` ile (varsayılan iOS 54/34, Android durum çubuğu/16).
|
|
22
|
-
|
|
23
|
-
**Dalga 3a (Rate, Slider, Statistic, Steps):** Rate dokunarak ya da parmağı yıldızlar üzerinde kaydırarak puanlanır; `onHoverChange` sürükleme önizlemesini verir. Slider'ın tutamacı rayın içinde kalır (iOS UISlider, md tutamaç 28), dokunma alanı en az 44pt, değer baloncuğu sürüklerken görünür. Rate ve Slider sürüklemesi kaydırılan bir ScrollView'a kaptırılmaz; ekran okuyucuda ikisi de `adjustable` (yukarı/aşağı kaydırma bir adım). Statistic ile Countdown, Clock ve Stopwatch aynı kabuğu kullanır. Statistic ve Clock ekran okuyucuda tek parça okunur; sayaçlar her saniye konuşmaz (süre dolunca ya da durdurulunca `announceForAccessibility`). Dar kartta değer sığana kadar küçülür (`adjustsFontSizeToFit`; react-native-web kısaltır). Steps'te `responsive` (varsayılan 520) kapsayıcı genişliğine bakar: telefonda yatay Steps dikey listeye, `responsiveLayout="compact"` ile yalnız göstergeler + "Adım 2/4 · Başlık" düzenine geçer. Ölçüler `nativeConfig.slider` / `statistic` / `steps` içinde.
|
|
24
|
-
|
|
25
|
-
**Dalga 3b (Accordion, Tabs, Select):** Accordion başlık satırının her yerine dokunmak açar (en az 44pt); açılma yükseklik animasyonuyla, kapalı gövde ekran okuyucudan gizli; RN'de başlık seviyesi olmadığı için `headingLevel` yok sayılır. Tabs web'deki birleşik API ile (`Tabs` / `TabsList` / `TabsTrigger` / `TabsContent`); taşan sekmeler yatay kayar ve aktif sekme görünür alana kaydırılır, gösterge kayarak geçer, dikey Tabs `responsive` (640) altında yataya döner; `icon` fonksiyon olarak etiket rengini alabilir. Select kapalıyken Input'la aynı kutu, seçenekler **BottomSheet**'te açılır: tekli seçimde dokunulan seçenek seçilip panel kapanır, çoklu seçimde panel açık kalır ("Tamam"); `showSearch` Türkçe/aksansız arar. Ölçüler `nativeConfig.accordion` / `tabs` / `select`.
|
|
26
|
-
|
|
27
|
-
**Klavye + ScrollView (BottomSheet, Select, Modal):** RN dokunuşu native pencereye değil React ağacına göre dağıtır — Modal'da açılan panel de onu açan ekranın ScrollView'unun çocuğu sayılır. O ScrollView'a `keyboardShouldPersistTaps="handled"` verin; varsayılan (`"never"`) klavye açıkken her dokunuşu yakalayıp yalnız klavyeyi kapatır, paneldeki düğme/seçenek ilk dokunuşta çalışmaz. Arama türündeki alanlarda (`type="search"`, SearchInput) otomatik düzeltme ve büyük harf kapalıdır — iOS düzeltmesi klavye kapanırken "mugla"yı "Mughal" yapıp süzülen listeyi boşaltıyordu.
|
|
28
|
-
|
|
29
|
-
**List (Faz 16.3):** Native `List`, `ListItem` ve `ListItemMeta`, React Native `FlatList` üzerine kuruludur. `data`, `keyExtractor`, `ListHeaderComponent`, `ListFooterComponent`, `ListEmptyComponent`, `ItemSeparatorComponent`, `onEndReached`, `refreshing` ve `onRefresh` native adları korunur; `renderItem(item, index)` web'deki kullanım biçimini sürdürür. `infinite={{ hasMore, loading, onLoadMore, endMessage }}` web'deki sonsuz akış davranışını native `onEndReached` ile sağlar; kayıtları `onLoadMore` içinde `data`ya ekleyin. Alt bölüm yükleme göstergesi, erişilebilir bir “Daha fazla yükle” düğmesi ve isteğe bağlı bitiş mesajı sunar. Eşiği `onEndReachedThreshold` belirler (sonsuz akışta varsayılan `0.5`). `split` ayırıcıyı açıp kapatır, `skeleton` ilk yükleme yer tutucularını gösterir. Büyük listelerde sanallaştırma ve kaydırma davranışı `FlatList`'ten gelir.
|
|
30
|
-
|
|
31
|
-
## Native ölçü ölçeği
|
|
32
|
-
|
|
33
|
-
Web ile ortak kalan şey özellik ve renk/tema sözleşmesidir. Ölçüler platforma göre seçilir: buton ile alanlarda native kontrol yüksekliği ve dokunma hedefi, Card'da okunabilir yazı ve içerik yoğunluğu esas alınır. Örneğin Card `sm/md/lg` iç boşluğu 12/16/20dp; gövde `fontSize` değerleri native ölçeğinden 14/16/17'dir (React Native metin ölçeği kullanıcı erişilebilirlik ayarını izler). Bu değerler web Card'ının 12/24/32 dolgu ölçeğinden bağımsızdır. Varsayılan native ölçekler `src/nativeConfig.tsx` içindedir ve `NativeConfigProvider` ile uygulama düzeyinde özelleştirilebilir.
|
|
34
|
-
|
|
35
|
-
```tsx
|
|
36
|
-
import { NativeConfigProvider } from "@mari-ui/native";
|
|
37
|
-
|
|
38
|
-
<NativeConfigProvider config={{
|
|
39
|
-
sizes: { md: { height: 48, fontSize: 17 } },
|
|
40
|
-
card: { md: { padding: 18 } },
|
|
41
|
-
list: { md: { rowPaddingVertical: 14, rowMinHeight: 52 } },
|
|
42
|
-
}}>
|
|
43
|
-
<App />
|
|
44
|
-
</NativeConfigProvider>
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Yalnızca verilen alanlar ezilir; diğer varsayılan ölçüler korunur. `<ThemeProvider>` ve native config birbirinden bağımsızdır.
|
|
48
|
-
|
|
49
|
-
Card'ın web'deki `cover`/`title`/`extra`/`footer`/`actions`, `bordered`/`inset`/`size`, `skeleton` özellikleri, `CardMeta`, `slotStyles`, `onPress`/`onLongPress`, devre dışı ve erişilebilirlik adları vardır. Kartın içindeki `actions` kendi düğmeleridir; kartın tamamı basılacaksa `onPress` kullanın, ikisini tek etkileşimde iç içe geçirmeyin. `hoverable` native'de basılma görünümünü açıp kapatır. `loading` geriye uyumlu eski addır.
|
|
50
|
-
|
|
51
|
-
## Storybook ve kategori düzeni
|
|
52
|
-
|
|
53
|
-
Native component story'leri web Storybook kenar çubuğunda `Native/Genel`, `Native/Veri Girişi`, `Native/Veri Gösterimi` gibi gruplara ayrılır. `native-studio` aynı dosyalardan kategorili bir component listesi üretir. Yeni story dosyaları component'in yanında kalır; kategori story başlığındaki `Native/<Kategori>/<Component>` yolundan gelir. Böylece web ve Expo önizlemesi aynı örnekleri ve grupları gösterir.
|
|
54
|
-
|
|
55
|
-
## Kurulum (tüketici tarafında)
|
|
10
|
+
## Kurulum
|
|
56
11
|
|
|
57
12
|
```bash
|
|
58
|
-
pnpm add @mari-ui/native react-native
|
|
13
|
+
pnpm add @mari-ui/native @mari-ui/tokens react-native-svg
|
|
59
14
|
```
|
|
60
15
|
|
|
61
|
-
|
|
16
|
+
Peer bağımlılıklar: `react` ≥ 18.2, `react-native` ≥ 0.76, `react-native-svg` ≥ 15. Expo ile `npx expo install react-native-svg` kullanın.
|
|
62
17
|
|
|
63
|
-
|
|
64
|
-
import { Button } from "@mari-ui/native";
|
|
65
|
-
|
|
66
|
-
<Button color="primary" onPress={() => {}}>Kaydet</Button>
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
`<ThemeProvider>` sarmalanmazsa `useTheme()` işletim sisteminin `useColorScheme()`ine göre `@mari-ui/tokens`'ın `lightTheme`/`darkTheme`'i arasında otomatik seçer — web'deki `prefers-color-scheme` davranışının native karşılığı. Sarmalamak sadece şu durumlarda gerekir: sistemi geçersiz kılmak (örn. "her zaman koyu tema" ayarı) veya tüketicinin kendi ürettiği bir 3. temayı (bkz. plan backlog madde 10 — light/dark ötesinde özel tema) uygulamak için:
|
|
18
|
+
## Kullanım
|
|
70
19
|
|
|
71
20
|
```tsx
|
|
72
|
-
import {
|
|
73
|
-
import { darkTheme } from "@mari-ui/tokens";
|
|
21
|
+
import { Button, Input, Field } from "@mari-ui/native";
|
|
74
22
|
|
|
75
|
-
<
|
|
76
|
-
<
|
|
77
|
-
</
|
|
23
|
+
<Field label="E-posta">
|
|
24
|
+
<Input type="email" placeholder="ornek@mari-ui.com" />
|
|
25
|
+
</Field>
|
|
26
|
+
<Button color="primary" onPress={save}>Kaydet</Button>
|
|
78
27
|
```
|
|
79
28
|
|
|
80
|
-
|
|
29
|
+
`ThemeProvider` isteğe bağlıdır; verilmezse tema cihazın açık/koyu ayarını izler.
|
|
81
30
|
|
|
82
|
-
|
|
31
|
+
## Tema
|
|
83
32
|
|
|
84
33
|
```tsx
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
Elle `StyleSheet.create` objesi kurulmaz; `color`/`variant` kombinasyonu `@mari-ui/core`'un `resolveColorVariant()`ından gelen SOYUT token referanslarına, `size` `componentSize` tablosuna göre çözülür — web'in Tailwind class'larıyla YAPTIĞI İŞİN AYNISINI burada `StyleSheet` nesneleri yapar, ikisi de aynı `@mari-ui/core`/`@mari-ui/tokens` kaynağından beslenir.
|
|
34
|
+
import { ThemeProvider } from "@mari-ui/native";
|
|
35
|
+
import { darkTheme, resolveThemeConfig } from "@mari-ui/tokens";
|
|
89
36
|
|
|
90
|
-
|
|
37
|
+
// Her zaman koyu:
|
|
38
|
+
<ThemeProvider theme={darkTheme}><App /></ThemeProvider>
|
|
91
39
|
|
|
92
|
-
|
|
40
|
+
// Projenin mari-ui.config.json'ı (marka rengi, kişilik, ek temalar):
|
|
41
|
+
const { light, dark } = resolveThemeConfig(config);
|
|
42
|
+
<ThemeProvider theme={scheme === "dark" ? dark : light}><App /></ThemeProvider>
|
|
43
|
+
```
|
|
93
44
|
|
|
94
|
-
|
|
95
|
-
// apps/mobile/metro.config.js (örnek — bu repoda henüz böyle bir app yok)
|
|
96
|
-
const { getDefaultConfig } = require("expo/metro-config");
|
|
97
|
-
const path = require("path");
|
|
45
|
+
`npx @mari-ui/cli theme` aynı config'ten `mari-ui-theme.ts` üretir; içindeki temalar doğrudan `ThemeProvider`a verilebilir.
|
|
98
46
|
|
|
99
|
-
|
|
100
|
-
const workspaceRoot = path.resolve(projectRoot, "../..");
|
|
47
|
+
### Fontlar
|
|
101
48
|
|
|
102
|
-
|
|
49
|
+
Varsayılan `fonts="system"`: iOS'ta SF, Android'de Roboto — dosya yüklemek gerekmez. mari-ui'ın kendi aileleri için `fonts="brand"` verin ve fontları aynı adlarla yükleyin:
|
|
103
50
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
config.watchFolders = [workspaceRoot];
|
|
51
|
+
```tsx
|
|
52
|
+
import { useFonts } from "expo-font";
|
|
107
53
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
54
|
+
const [loaded] = useFonts({
|
|
55
|
+
Geist: require("@mari-ui/tokens/fonts/Geist.ttf"),
|
|
56
|
+
Manrope: require("@mari-ui/tokens/fonts/Manrope.ttf"),
|
|
57
|
+
"Geist Mono": require("@mari-ui/tokens/fonts/GeistMono.ttf"),
|
|
58
|
+
});
|
|
113
59
|
|
|
114
|
-
|
|
60
|
+
<ThemeProvider theme={theme} fonts="brand">…</ThemeProvider>
|
|
115
61
|
```
|
|
116
62
|
|
|
117
|
-
|
|
63
|
+
Tasarım kişilikleri renk, köşe, gölge ve hareketi uygular; native'de font dosyası getirmez.
|
|
118
64
|
|
|
119
|
-
|
|
65
|
+
### Güvenli alan ve ölçüler
|
|
120
66
|
|
|
121
|
-
|
|
67
|
+
```tsx
|
|
68
|
+
import { NativeConfigProvider, ThemeProvider } from "@mari-ui/native";
|
|
69
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
70
|
+
|
|
71
|
+
<ThemeProvider theme={theme} insets={useSafeAreaInsets()}>
|
|
72
|
+
<NativeConfigProvider config={{ sizes: { md: { height: 48 } } }}>
|
|
73
|
+
<App />
|
|
74
|
+
</NativeConfigProvider>
|
|
75
|
+
</ThemeProvider>
|
|
76
|
+
```
|
|
122
77
|
|
|
123
|
-
|
|
78
|
+
`insets`, ekran kenarına değen bileşenlere (AppBar, BottomNavigation, BottomSheet, Toast…) çentik ve ev çubuğu payını verir. `NativeConfigProvider` varsayılan ölçüleri (yükseklik, dolgu, yazı boyutu) uygulama genelinde ezer; yalnız verilen alanlar değişir.
|
|
124
79
|
|
|
125
|
-
|
|
126
|
-
- **Test edilMEyenler**: `react-native`'i ÇALIŞMA ZAMANINDA import eden dosyalar — `useTheme.ts` (`useColorScheme`), `useStyles.ts` (`StyleSheet.create`), ve üç component'in kendisi (`Button`/`Input`/`Card`, `View`/`Pressable`/`TextInput` importları). Bunlar `tsc`/`eslint` ile ve gerçek bir RN ortamında (yukarıdaki doğrulanmamış madde) doğrulanıyor. Gerçek bir component-render testi isteniyorsa `jest` + `jest-expo`/`@testing-library/react-native` gerekir — bu, vitest'in yerini almayan, monorepo'ya YENİ bir test koşucusu eklemek anlamına gelir; bu fazın kapsamı dışında bırakıldı, ileride gerçek bir Expo doğrulamasıyla birlikte ele alınabilir.
|
|
80
|
+
## İpucu: klavye ve kaydırma
|
|
127
81
|
|
|
128
|
-
|
|
82
|
+
Select / BottomSheet / Modal açan ekranın `ScrollView`una `keyboardShouldPersistTaps="handled"` verin; yoksa klavye açıkken paneldeki ilk dokunuş yalnız klavyeyi kapatır.
|
|
129
83
|
|
|
130
|
-
|
|
131
|
-
renk verilmediğinde nötr ve en az 3:1 kontrastlı `canvas.input` sınırını,
|
|
132
|
-
odakta iris rengini kullanır. `color="error"` gibi açık seçimler korunur.
|
|
84
|
+
## Bağlantılar
|
|
133
85
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
`@mari-ui/tokens/fonts/Geist.ttf` ve `@mari-ui/tokens/fonts/Manrope.ttf`
|
|
137
|
-
export'larında bulunur. Font yüklemesi kütüphanenin yan etkisi değildir;
|
|
138
|
-
Expo veya native projenizin font yükleme/kayıt yöntemini kullanın ve
|
|
139
|
-
kayıt tamamlanınca arayüzü gösterin. Ağırlık eşlemelerini cihazda doğrulayın.
|
|
86
|
+
- Component dokümanları ve örnekler: [mari-ui deposu](https://github.com/aykutyilmaz37/mari-ui#readme) (Storybook: `pnpm storybook`, "Native" grubu)
|
|
87
|
+
- Web component'leri: [`@mari-ui/components`](https://www.npmjs.com/package/@mari-ui/components)
|
|
140
88
|
|
|
141
|
-
|
|
142
|
-
`fontFamily.heading` alanlarını `"system-ui"` yapın. Native resolver bu değerde
|
|
143
|
-
`fontFamily` özelliğini boş bırakarak işletim sisteminin fontunu kullanır.
|
|
144
|
-
Özel kayıtlı font isimleri de desteklenir; CSS fallback listeleri RN'e taşınmaz.
|
|
89
|
+
Lisans: MIT.
|