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,176 @@
|
||||
// @ts-nocheck
|
||||
'use client';
|
||||
import * as React from 'react';
|
||||
import Combobox from '@/components/ui/combobox';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
|
||||
export default function ExampleVirtualizedCombobox() {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const virtualizerRef = React.useRef<Virtualizer | null>(null);
|
||||
|
||||
return (
|
||||
<Combobox.Root
|
||||
virtualized
|
||||
items={virtualizedItems}
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
itemToStringLabel={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 leading-5 font-bold text-neutral-950 dark:text-white">
|
||||
Search 10,000 items
|
||||
<Combobox.Input className="h-8 w-64 border border-neutral-950 bg-white dark:bg-neutral-950 px-2 text-sm any-pointer-coarse:text-base font-normal text-neutral-950 focus:outline-2 focus:-outline-offset-1 focus:outline-neutral-950 dark:focus:outline-white dark:border-white dark:text-white" />
|
||||
</label>
|
||||
|
||||
<Combobox.Portal>
|
||||
<Combobox.Positioner sideOffset={4}>
|
||||
<Combobox.Popup>
|
||||
<Combobox.Empty>
|
||||
<div className="py-3 px-2 text-sm leading-4 text-neutral-500 dark:text-neutral-400">
|
||||
No items found.
|
||||
</div>
|
||||
</Combobox.Empty>
|
||||
<Combobox.List className="p-0">
|
||||
<VirtualizedList open={open} virtualizerRef={virtualizerRef} />
|
||||
</Combobox.List>
|
||||
</Combobox.Popup>
|
||||
</Combobox.Positioner>
|
||||
</Combobox.Portal>
|
||||
</Combobox.Root>
|
||||
);
|
||||
}
|
||||
|
||||
function VirtualizedList({
|
||||
open,
|
||||
virtualizerRef,
|
||||
}: {
|
||||
open: boolean;
|
||||
virtualizerRef: React.RefObject<Virtualizer | null>;
|
||||
}) {
|
||||
const filteredItems = Combobox.useFilteredItems<VirtualizedItem>();
|
||||
|
||||
const scrollElementRef = React.useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const virtualizer = useVirtualizer({
|
||||
enabled: open,
|
||||
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 (
|
||||
<Combobox.Item
|
||||
key={virtualItem.key}
|
||||
index={virtualItem.index}
|
||||
data-index={virtualItem.index}
|
||||
ref={virtualizer.measureElement}
|
||||
value={item}
|
||||
className="grid cursor-default grid-cols-[1rem_1fr] items-center gap-2 p-2 text-sm leading-4 outline-none select-none data-highlighted:relative data-highlighted:z-0 data-highlighted:text-white data-highlighted:before:absolute data-highlighted:before:inset-0 data-highlighted:before:z-[-1] data-highlighted:before:bg-neutral-950 dark:data-highlighted:text-neutral-950 dark:data-highlighted:before:bg-white"
|
||||
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)`,
|
||||
}}
|
||||
>
|
||||
<Combobox.ItemIndicator>
|
||||
<CheckIcon />
|
||||
</Combobox.ItemIndicator>
|
||||
<span className="col-start-2">{item.name}</span>
|
||||
</Combobox.Item>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function CheckIcon(props: React.ComponentProps<'svg'>) {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
style={{ display: 'block', ...props.style }}
|
||||
>
|
||||
<path d="m2.5 8.5 4 4 7-9" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
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