// RECYCLED ROWS HUGE DATA SOURCE - the TSX twin of the C# "Cells" fiddle.
const ITEMS = Array.from({ length: 100000 }, (_, i) => i + 1);
const FEED_PADDING = new Thickness(16, 8);
const BAR_MARGIN = new Thickness(8, 8);
// The DrawnUi cell recipe: visuals built once in the constructor, SetContent on every rebind.
// Code-behind builds ENGINE CLASSES, so it goes through Core: a bare `SkiaLabel` here is the
// JSX tag used below, not the class (same split as "drawnui-react" vs "drawnui-react/core").
class ContactCell extends SkiaDynamicDrawnCell {
initials = new Core.SkiaLabel();
title = new Core.SkiaLabel();
subtitle = new Core.SkiaLabel();
constructor(onTap: (item: number) => void) {
super();
// Absolute, not Row: a Row would hand the text column an infinite width and MaxLines
// could never truncate.
this.Type = "Absolute";
this.Padding = new Thickness(12, 10);
this.BackgroundColor = "#111827";
this.UseCache = "Image"; // one bitmap per cell, blitted while scrolling
this.AnimationTapped = "Ripple";
this.Tapped = () => onTap(this.BindingContext as number);
const avatar = new Core.SkiaShape();
avatar.Type = "Circle";
avatar.WidthRequest = 42;
avatar.LockRatio = 1;
avatar.VerticalOptions = "Center";
avatar.BackgroundColor = "#1F2937";
this.initials.FontSize = 14;
this.initials.TextColor = "#67E8F9";
this.initials.HorizontalOptions = "Center";
this.initials.VerticalOptions = "Center";
avatar.AddSubView(this.initials);
const column = new Core.SkiaLayout();
column.Type = "Column";
column.Spacing = 3;
column.VerticalOptions = "Center";
column.HorizontalOptions = "Fill";
column.Margin = new Thickness(54, 0, 0, 0); // avatar 42 + gap 12
this.title.FontSize = 15;
this.title.FontFamily = "FontTextTitle";
this.title.TextColor = "#FFFFFF";
this.subtitle.FontSize = 12;
this.subtitle.TextColor = "#94A3B8";
this.subtitle.MaxLines = 1;
column.AddSubView(this.title);
column.AddSubView(this.subtitle);
this.AddSubView(avatar);
this.AddSubView(column);
}
protected SetContent(ctx: unknown): void {
const i = ctx as number;
this.initials.Text = `${i % 100}`;
this.title.Text = `Contact ${i}`;
this.subtitle.Text = `Recycled drawn cell #${i} \u2014 scroll me fast`;
}
}
export default function App() {
const scroll = useRef(null);
const feed = useRef(null);
const [debug, setDebug] = useState("");
// Stable reference: a new function every render would rebuild the whole cell pool.
const template = useCallback(() => new ContactCell((i) => console.log(`Tapped ${i}`)), []);
const jump = (index, option = "Start") => scroll.current?.ScrollToIndex(index, true, option);
return (
<SkiaLayer VerticalOptions="Fill">
<SkiaScroll ref={scroll} Orientation="Vertical"
HorizontalOptions="Fill" VerticalOptions="Fill"
ScrollBarsVisibility="Vertical" ScrollBarThumbColor="#6694A3B8"
Scrolled={() => setDebug(feed.current?.DebugString ?? "")}>
<SkiaStack
ref={feed}
ItemsSource={ITEMS}
ItemTemplate={template}
RecyclingTemplate="Enabled"
MeasureItemsStrategy="MeasureFirst"
Spacing={8}
Padding={FEED_PADDING} />
</SkiaScroll>
{/* jump-to-index buttons */}
<SkiaStack Spacing={8} Margin={BAR_MARGIN} UseCache="Image"
HorizontalOptions="End" VerticalOptions="Center">
<SkiaButton Text="HOME" FontSize={12} BackgroundColor="#DC143C" TextColor="#FFFFFF" CornerRadius={8}
Tapped={() => jump(0)} />
<SkiaButton Text="BACKWARD" FontSize={12} BackgroundColor="#DC143C" TextColor="#FFFFFF" CornerRadius={8}
Tapped={() => jump((feed.current?.FirstVisibleIndex ?? 0) - 5)} />
<SkiaButton Text="MIDDLE" FontSize={12} BackgroundColor="#DC143C" TextColor="#FFFFFF" CornerRadius={8}
Tapped={() => jump(ITEMS.length / 2)} />
<SkiaButton Text="FORWARD" FontSize={12} BackgroundColor="#DC143C" TextColor="#FFFFFF" CornerRadius={8}
Tapped={() => jump((feed.current?.FirstVisibleIndex ?? 0) + 5)} />
<SkiaButton Text="END" FontSize={12} BackgroundColor="#DC143C" TextColor="#FFFFFF" CornerRadius={8}
Tapped={() => jump(ITEMS.length, "End")} />
</SkiaStack>
{/* what the templated stack is doing right now */}
<SkiaLabel Text={debug} FontSize={11} TextColor="#00FF00" BackgroundColor="#AA000000"
InputTransparent Padding={new Thickness(6, 3)} Margin={new Thickness(0, 8)}
MaxLines={1} HorizontalOptions="End" VerticalOptions="End" />
</SkiaLayer>
);
}