plain item grid

This commit is contained in:
jeffvli
2025-09-25 00:53:46 -07:00
parent 1108cb7e9a
commit 7a2af3d013
2 changed files with 215 additions and 0 deletions
@@ -0,0 +1,92 @@
.item-grid-container {
width: 100%;
height: 100%;
padding-right: var(--theme-spacing-md);
container-name: grid-list;
container-type: inline-size;
scrollbar-gutter: stable;
}
.grid-list-component {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-flow: row dense;
@container (min-width: $breakpoint-xs) {
grid-template-columns: repeat(6, 1fr);
}
@container (min-width: $breakpoint-sm) {
grid-template-columns: repeat(8, 1fr);
}
@container (min-width: $breakpoint-md) {
grid-template-columns: repeat(10, 1fr);
}
@container (min-width: $breakpoint-lg) {
grid-template-columns: repeat(12, 1fr);
}
@container (min-width: $breakpoint-xl) {
grid-template-columns: repeat(14, 1fr);
}
@container (min-width: $breakpoint-2xl) {
grid-template-columns: repeat(16, 1fr);
}
@container (min-width: $breakpoint-3xl) {
grid-template-columns: repeat(18, 1fr);
}
/* display: flex; */
/* flex-wrap: wrap; */
}
.grid-item-component {
display: flex;
grid-column: span 2;
padding: var(--theme-spacing-sm);
/* box-sizing: border-box;
display: flex;
flex: none;
align-content: stretch;
width: 50%;
padding: var(--theme-spacing-sm);
@container (min-width: $breakpoint-xs) {
width: 33.33%;
}
@container (min-width: $breakpoint-sm) {
width: 25%;
}
@container (min-width: $breakpoint-md) {
width: 20%;
}
@container (min-width: $breakpoint-lg) {
width: 14.28%;
}
@container (min-width: $breakpoint-xl) {
width: 12.5%;
}
@container (min-width: $breakpoint-2xl) {
width: 11.11%;
}
@container (min-width: $breakpoint-3xl) {
width: 10%;
} */
}
.full-width-content {
grid-column: 1 / -1;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 10%);
}
@@ -0,0 +1,123 @@
import clsx from 'clsx';
import { useOverlayScrollbars } from 'overlayscrollbars-react';
import {
CSSProperties,
forwardRef,
memo,
ReactNode,
Ref,
RefObject,
useEffect,
useRef,
useState,
} from 'react';
import { GridComponents, VirtuosoGrid, VirtuosoGridHandle } from 'react-virtuoso';
import styles from './item-grid.module.css';
import { ItemCard } from '/@/renderer/components/item-card/item-card';
const gridComponents: GridComponents<any> = {
Item: forwardRef<
HTMLDivElement,
{
children?: ReactNode;
className?: string;
context?: Record<string, unknown>;
'data-index': number;
enableExpanded?: boolean;
style?: CSSProperties;
virtuosoRef?: RefObject<VirtuosoGridHandle>;
}
>((props, ref) => {
const { children, 'data-index': index, enableExpanded, virtuosoRef } = props;
return (
<div className={clsx(styles.gridItemComponent)} ref={ref}>
{children}
</div>
);
}),
List: forwardRef<
HTMLDivElement,
{ children?: ReactNode; className?: string; style?: CSSProperties }
>((props, ref) => {
const { children, className, style, ...rest } = props;
return (
<div
className={clsx(styles.gridListComponent, className)}
ref={ref}
style={{ ...style }}
{...rest}
>
{children}
</div>
);
}),
};
interface ItemGridProps<TData> {
data: TData[];
ref: Ref<VirtuosoGridHandle>;
totalItemCount?: number;
}
export const ItemGrid = <TData,>({ data, ref, totalItemCount }: ItemGridProps<TData>) => {
const rootRef = useRef(null);
const [scroller, setScroller] = useState<HTMLElement | null>(null);
const [initialize, osInstance] = useOverlayScrollbars({
defer: true,
options: {
overflow: { x: 'hidden', y: 'scroll' },
paddingAbsolute: true,
scrollbars: {
autoHide: 'leave',
autoHideDelay: 500,
pointers: ['mouse', 'pen', 'touch'],
theme: 'feishin-os-scrollbar',
visibility: 'visible',
},
},
});
useEffect(() => {
const { current: root } = rootRef;
if (scroller && root) {
initialize({
elements: { viewport: scroller },
target: root,
});
}
return () => osInstance()?.destroy();
}, [scroller, initialize, osInstance]);
return (
<div
className={styles.itemGridContainer}
data-overlayscrollbars-initialize=""
ref={rootRef}
>
<VirtuosoGrid
components={gridComponents}
increaseViewportBy={200}
itemContent={itemContent}
ref={ref}
scrollerRef={setScroller}
totalCount={totalItemCount || data.length}
/>
</div>
);
};
const itemContent = (index: number, item: any) => {
return <InnerItem index={index} item={item} />;
};
const InnerItem = memo(({ index, item }: { index: number; item: any }) => {
return <ItemCard data={item} withControls />;
});