تُرجمت هذه الصفحة آليًا وقد تحتوي على أخطاء. اقرأ الأصل الإنجليزي

API

List

يصدّر nsh المكوّن List: قائمة افتراضية (virtualized) مبنية على عرض تمرير أصلي. لا يوجد إلا الصفوف القريبة من منفذ العرض، وتُعاد الاستفادة من خلاياها أثناء التمرير. تُحسب أصلياً كل من الصفوف الموجودة ومواضعها والأعمدة والرؤوس الثابتة وقابلية الظهور (viewability)، لذلك لا يشغّل تمرير قائمة من عشرة آلاف عنصر أي JS سوى ارتباطات الصفوف التي تتغير عند إعادة استخدام الخلية.

import { List, type ListProps, type ViewToken, type ViewabilityConfig } from "nsh";

راجع القوائم للاطلاع على الدليل، و<List> لخصائص هذا العنصر.

#List

function List<T>(props: ListProps<T>): JSX.Element;
import { List, mount, Text, View } from "nsh";

const contacts = Array.from({ length: 1000 }, (_, i) => ({ id: String(i), name: `Contact ${i}` }));

function App() {
  return (
    <List each={contacts} estimatedItemSize={48} style={{ flex: 1 }}>
      {(item, index) => (
        <View style={{ height: 48, paddingHorizontal: 16, justifyContent: "center", backgroundColor: index() % 2 ? "#f8fafc" : "#fff" }}>
          <Text>{item().name}</Text>
        </View>
      )}
    </List>
  );
}

mount(App);

#إعادة تدوير الصفوف

children دالة تعرض خلية واحدة. تعمل مرة واحدة لكل خلية، لا مرة لكل عنصر: عندما تخرج الخلية من نطاق الرؤية تُعاد الاستفادة منها لعنصر آخر، فيتغير item() وindex() (وكلاهما accessor). اقرأهما داخل تعابير JSX ({item().name}) لكي تتحدث الخلية عند إعادة استخدامها.

#الخصائص

يجمع ListProps<T> بين خصائص القائمة نفسها وخصائص التمرير المشتركة مع <Scroll> والخصائص التي يقبلها كل عنصر، وتذهب هذه الأخيرة إلى عرض التمرير الخاص بالقائمة.

#البيانات والعرض

الخاصيةالنوعالقيمة الافتراضيةالوصف
eachreadonly T[](مطلوب)العناصر. مرّر قيمة signal (each={items()}) لتتبع التغييرات؛ إدراج عناصر فوق الصفوف المرئية يُبقي تلك الصفوف في مكانها.
children(item: () => T, index: () => number) => JSX.Element(مطلوب)تعرض صفاً واحداً (انظر أعلاه).
estimatedItemSizenumber50حجم الصفوف التي لم تُقَس بعد، بالنقاط (points)، على امتداد محور القائمة. كلما كانت التقديرات أقرب، كان مؤشر التمرير وscrollToIndex أدق.
numColumnsnumber1عدد العناصر في الصف (للقوائم العمودية): شبكة.
horizontalbooleanfalseيتمرر أفقياً.
invertedbooleanfalseيرتب المحتوى من النهاية، ويكون العنصر 0 الأخير (نمط الدردشة)؛ يبدأ متمرراً إلى النهاية ويحافظ على بعده عن النهاية عند تغيّر المحتوى.
stickyHeaderIndicesnumber[]عناصر تلتصق بالبداية عند تجاوزها بالتمرير (عمود واحد، دون عكس).
headerListSlotيُعرض قبل العناصر.
footerListSlotيُعرض بعد العناصر.
emptyListSlotيظهر بدل العناصر عندما يكون each فارغاً.
separatorListSlotيُعرض بين العناصر (عمود واحد).
keyExtractor(item: T, index: number) => stringitem.key أو item.id أو الفهرسمفتاح العنصر في رموز قابلية الظهور.
initialScrollIndexnumberيتمرر إلى هذا العنصر قبل الإطار الأول.
contentStyleListContentStyleالحشو والفجوات للمحتوى: padding، paddingHorizontal، paddingVertical، paddingTop، paddingRight، paddingBottom، paddingLeft، gap، rowGap، columnGap (أرقام).
contentClassstringالحشو والفجوات للمحتوى على شكل classes (p-*، gap-*)؛ وتتغلب عليها contentStyle.
styleStyleProp<ViewStyle>نمط القائمة نفسها (حدّد لها حجماً: flex: 1 أو ارتفاعاً).
testIDstringالمعرّف الذي تعثر به اختبارات واجهة المستخدم على عرض التمرير الخاص بالقائمة (id: في nsh test، وXCUITest، وUI Automator).
refListRef | ((node: ListRef) => void)انظر دوال ref.

ListSlot إما مكوّن (Header) أو عنصر (<Header />).

#التحميل والتحديث وقابلية الظهور

الخاصيةالنوعالقيمة الافتراضيةالوصف
onLoadMore() => voidتُستدعى عندما تصبح النهاية ضمن loadMoreThreshold من أطوال منفذ العرض: حمّل المزيد.
loadMoreThresholdnumber1مدى القرب من النهاية (بأطوال منفذ العرض) الذي يُطلق عنده onLoadMore.
onLoadMoreAtStart() => voidتُستدعى عندما تصبح البداية ضمن loadMoreAtStartThreshold من أطوال منفذ العرض بعد أن حرّك المستخدم القائمة أو أمر تمرير: حمّل ما يسبق العنصر الأول.
loadMoreAtStartThresholdnumber1مدى القرب من البداية (بأطوال منفذ العرض) الذي يُطلق عنده onLoadMoreAtStart.
refreshingbooleanيظهر مؤشر السحب للتحديث.
onRefresh() => voidيفعّل السحب للتحديث؛ اضبط refreshing أثناء التحميل.
refreshIndicator{ height: number }يرسم التطبيق مؤشر السحب للتحديث الخاص به، بارتفاع height، بدل المؤشر الافتراضي.
pullValueAnimatedValueتُغذّى بمقدار سحب القائمة إلى ما بعد أعلاها، لاستخدامه في ذلك المؤشر.
onViewableItemsChange(info: { viewableItems: ViewToken<T>[]; changed: ViewToken<T>[] }) => voidالعناصر التي أصبحت قابلة للظهور أو توقفت عن ذلك.
viewabilityConfigViewabilityConfigمتى يُعدّ العنصر قابلاً للظهور.
interface ViewToken<T> {
  item: T;
  key: string;
  index: number;
  isViewable: boolean;
}

interface ViewabilityConfig {
  /** How much of the viewport an item must cover to count as viewable, from 0 to 1. */
  viewAreaCoverageThreshold?: number;
  /** How much of an item must be visible, from 0 to 1. Without either threshold, any visible pixel counts. */
  itemVisibleThreshold?: number;
  /** Milliseconds items must stay viewable before they are reported. */
  minimumViewTime?: number;
}

#التمرير

الخاصيةالنوعالقيمة الافتراضيةالوصف
scrollbarboolean | ScrollbarThemetrueيظهر مؤشر التمرير: مؤشر المنصة، أو الشريط المدمج في التطبيق؛ والكائن يمثل سمة (theme) خاصة بالعرض نفسه.
scrollablebooleantrueيستطيع المستخدم تمريره.
bouncesbooleantrueالتمرير الزائد (overscroll) إلى ما بعد الحواف.
pagingEnabledbooleanfalseيتوقف عند مضاعفات منفذ العرض.
snapToIntervalnumber0يتوقف عند مضاعفات هذا الطول (0: معطّل).
snapToOffsetsnumber[]يتوقف عند هذه الإزاحات.
snapToAlignment"start" | "center" | "end""start"أي نقطة من العنصر تتحاذى مع منفذ العرض عند استخدام snapToInterval.
disableIntervalMomentumbooleanfalseتتحرك الحركة السريعة (fling) نقطة توقف واحدة على الأكثر.
decelerationRate"normal" | "fast" | numberحسب المنصةسرعة تباطؤ الحركة السريعة (0.998 / 0.99، أو السرعة المتبقية لكل ميلي ثانية).
contentInsetPartial<Edges>مساحة حول المحتوى.
contentOffset{ x?, y? }موضع البداية.
keyboardShouldPersistTaps"never" | "always" | "handled" | boolean"never"هل تُخفي النقرة لوحة المفاتيح ("never": النقرة تخفيها فقط).
keyboardDismissMode"none" | "on-drag" | "interactive""none"هل يُخفي السحب لوحة المفاتيح.
scrollEventThrottlenumberالحد الأدنى بالميلي ثانية بين أحداث onScroll (حدث واحد على الأكثر لكل إطار في جميع الأحوال).
scrollValueAnimatedValueقيمة متحركة تُغذّى بإزاحة التمرير (المحور الرئيسي) على خيط واجهة المستخدم، للتأثيرات المرتبطة بالتمرير.

#أحداث التمرير

الحدثالبيانات المرسلةمتى
onScrollScrollEvent: { offset, contentSize, size, zoomScale }أثناء التمرير.
onScrollDragStart{ offset }يبدأ السحب.
onScrollDragEnd{ offset }يُرفع الإصبع.
onMomentumStart{ offset }تبدأ حركة انسيابية بعد السحب.
onMomentumEnd{ offset }تنتهي الحركة الانسيابية (وكذلك عندما يوقفها سحب جديد).
onScrollEnd{ offset }استقر التمرير.

تقبل القوائم أيضاً الخصائص التي يقبلها كل عنصر (الخصائص المشتركة).

#دوال ref

يتلقى ref الخاص بالقائمة ListRef، مع الدوال التالية إضافة إلى دوال ViewRef:

الدالةالوصف
scrollToIndex({ index, animated?, viewPosition?, viewOffset? })تمرّر العنصر index إلى نطاق الرؤية. يحدد viewPosition موضعه: 0 عند بداية منفذ العرض (الافتراضي)، 0.5 في المنتصف، 1 عند النهاية؛ ويزيحه viewOffset أكثر. تُحسم أصلياً باستخدام أحجام الصفوف المقيسة (أو المقدّرة).
scrollToOffset({ offset, animated? })تمرّر إلى إزاحة في المحتوى على امتداد محور القائمة.
scrollToEnd({ animated? })تمرّر إلى نهاية المحتوى (بداية القائمة المعكوسة).
scrollTo({ x?, y?, animated? })تمرّر إلى إزاحة.
import { List, type ListRef, Text, View } from "nsh";

let list!: ListRef;

<View style={{ flex: 1 }}>
  <Text onPress={() => list.scrollToIndex({ index: 60, viewPosition: 0.5 })}>Jump to 60</Text>
  <List ref={list} each={items} numColumns={3} estimatedItemSize={90} style={{ flex: 1 }}
        contentStyle={{ padding: 12, gap: 8 }}>
    {(item) => <View style={{ height: 90, backgroundColor: "#e0e7ff" }}><Text>{item().title}</Text></View>}
  </List>
</View>

#أمثلة

دردشة: أحدث رسالة في الأسفل، وتُحمَّل الرسائل الأقدم عند التمرير للأعلى.

<List
  each={messages()}
  inverted
  estimatedItemSize={44}
  style={{ flex: 1 }}
  contentStyle={{ padding: 12, gap: 6 }}
  keyboardDismissMode="interactive"
  onLoadMore={loadOlder}
  loadMoreThreshold={0.5}
>
  {(m) => (
    <View style={{ alignSelf: m().mine ? "flex-end" : "flex-start", maxWidth: "75%", padding: 10, borderRadius: 14, backgroundColor: m().mine ? "#6366f1" : "#e2e8f0" }}>
      <Text style={{ color: m().mine ? "#fff" : "#0f172a" }}>{m().text}</Text>
    </View>
  )}
</List>

السحب للتحديث:

const refreshing = signal(false);
const refresh = async () => {
  refreshing.set(true);
  items.set(await load());
  refreshing.set(false);
};

<List each={items()} refreshing={refreshing()} onRefresh={refresh} style={{ flex: 1 }}>
  {(item) => <Text style={{ padding: 16 }}>{item().title}</Text>}
</List>

#ListItemsProps

type ListItemsProps<T> = Pick<ListProps<T>,
  "each" | "children" | "keyExtractor" | "stickyHeaderIndices" | "initialScrollIndex"
  | "onViewableItemsChange" | "viewabilityConfig" | "refreshing" | "onRefresh"
  | "onLoadMore" | "loadMoreThreshold"
  | "header" | "footer" | "empty">;

هو نصف ListProps المعني بالبيانات والصفوف: ما تمرره إلى مكوّن خاص بك يرتب القائمة بنفسه، مثل قائمة إعدادات تغلّف List بحشوها وفواصلها الخاصة.