feat(site): add component documentation site
Add Vite, React, MDX, and Tailwind documentation infrastructure. Document component, handbook, overview, and utility routes. Add interactive examples for every component family. Deduplicate React resolution and load the Tailwind 4 theme configuration.
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
// @ts-nocheck
|
||||
'use client';
|
||||
import * as React from 'react';
|
||||
import Autocomplete from '@/components/ui/autocomplete';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
|
||||
export default function ExampleVirtualizedAutocomplete() {
|
||||
const virtualizerRef = React.useRef<Virtualizer | null>(null);
|
||||
|
||||
return (
|
||||
<Autocomplete.Root
|
||||
virtualized
|
||||
items={virtualizedItems}
|
||||
openOnInputClick
|
||||
itemToStringValue={getItemLabel}
|
||||
onItemHighlighted={(item, { reason, index }) => {
|
||||
const virtualizer = virtualizerRef.current;
|
||||
|
||||
if (!item || !virtualizer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isStart = index === 0;
|
||||
const isEnd = index === virtualizer.options.count - 1;
|
||||
const shouldScroll = reason === 'none' || (reason === 'keyboard' && (isStart || isEnd));
|
||||
|
||||
if (shouldScroll) {
|
||||
queueMicrotask(() => {
|
||||
virtualizer.scrollToIndex(index, { align: isEnd ? 'start' : 'end' });
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<label className="flex flex-col gap-1 text-sm font-bold text-neutral-950 dark:text-white">
|
||||
Search 10,000 items
|
||||
<Autocomplete.Input />
|
||||
</label>
|
||||
|
||||
<Autocomplete.Portal>
|
||||
<Autocomplete.Positioner sideOffset={4}>
|
||||
<Autocomplete.Popup>
|
||||
<Autocomplete.Empty>
|
||||
<div className="py-3 px-2 text-sm leading-4 text-neutral-500 dark:text-neutral-400">
|
||||
No items found.
|
||||
</div>
|
||||
</Autocomplete.Empty>
|
||||
<Autocomplete.List className="p-0">
|
||||
<VirtualizedList virtualizerRef={virtualizerRef} />
|
||||
</Autocomplete.List>
|
||||
</Autocomplete.Popup>
|
||||
</Autocomplete.Positioner>
|
||||
</Autocomplete.Portal>
|
||||
</Autocomplete.Root>
|
||||
);
|
||||
}
|
||||
|
||||
function VirtualizedList({
|
||||
virtualizerRef,
|
||||
}: {
|
||||
virtualizerRef: React.RefObject<Virtualizer | null>;
|
||||
}) {
|
||||
const filteredItems = Autocomplete.useFilteredItems<VirtualizedItem>();
|
||||
|
||||
const scrollElementRef = React.useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const virtualizer = useVirtualizer({
|
||||
count: filteredItems.length,
|
||||
getScrollElement: () => scrollElementRef.current,
|
||||
estimateSize: () => 32,
|
||||
overscan: 20,
|
||||
paddingStart: 4,
|
||||
paddingEnd: 4,
|
||||
scrollPaddingEnd: 4,
|
||||
scrollPaddingStart: 4,
|
||||
});
|
||||
|
||||
React.useImperativeHandle(virtualizerRef, () => virtualizer);
|
||||
|
||||
const handleScrollElementRef = React.useCallback(
|
||||
(element: HTMLDivElement | null) => {
|
||||
scrollElementRef.current = element;
|
||||
if (element) {
|
||||
virtualizer.measure();
|
||||
}
|
||||
},
|
||||
[virtualizer],
|
||||
);
|
||||
|
||||
const totalSize = virtualizer.getTotalSize();
|
||||
|
||||
if (!filteredItems.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
role="presentation"
|
||||
ref={handleScrollElementRef}
|
||||
className="h-[min(22.5rem,var(--total-size))] max-h-[var(--available-height)] overflow-auto overscroll-contain scroll-py-1"
|
||||
style={{ '--total-size': `${totalSize}px` } as React.CSSProperties}
|
||||
>
|
||||
<div role="presentation" className="relative w-full" style={{ height: totalSize }}>
|
||||
{virtualizer.getVirtualItems().map((virtualItem) => {
|
||||
const item = filteredItems[virtualItem.index];
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Autocomplete.Item
|
||||
key={virtualItem.key}
|
||||
index={virtualItem.index}
|
||||
data-index={virtualItem.index}
|
||||
ref={virtualizer.measureElement}
|
||||
value={item}
|
||||
aria-setsize={filteredItems.length}
|
||||
aria-posinset={virtualItem.index + 1}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: virtualItem.size,
|
||||
transform: `translateY(${virtualItem.start}px)`,
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</Autocomplete.Item>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface VirtualizedItem {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
function getItemLabel(item: VirtualizedItem | null) {
|
||||
return item ? item.name : '';
|
||||
}
|
||||
|
||||
const virtualizedItems: VirtualizedItem[] = Array.from({ length: 10000 }, (_, index) => {
|
||||
const id = String(index + 1);
|
||||
const indexLabel = id.padStart(4, '0');
|
||||
return { id, name: `Item ${indexLabel}` };
|
||||
});
|
||||
|
||||
type Virtualizer = ReturnType<typeof useVirtualizer<HTMLDivElement, Element>>;
|
||||
Reference in New Issue
Block a user