Update base components

This commit is contained in:
jeffvli
2022-10-24 21:56:58 -07:00
parent dfdf53f6ee
commit df3418120a
23 changed files with 614 additions and 328 deletions
@@ -7,12 +7,10 @@ import {
} from 'react';
import ReactPlayer, { ReactPlayerProps } from 'react-player';
import {
CrossfadeStyle,
PlaybackStyle,
PlayerStatus,
Song,
} from '../../../types';
import { crossfadeHandler, gaplessHandler } from './utils/listenHandlers';
crossfadeHandler,
gaplessHandler,
} from '@/renderer/components/audio-player/utils/list-handlers';
import { CrossfadeStyle, PlaybackStyle, PlayerStatus, Song } from '@/types';
interface AudioPlayerProps extends ReactPlayerProps {
crossfadeDuration: number;
@@ -125,13 +123,13 @@ export const AudioPlayer = forwardRef(
return gaplessHandler({
currentTime: e.playedSeconds,
duration: getDuration(player1Ref),
isFlac: player1?.suffix === 'flac',
isFlac: player1?.container === 'flac',
isTransitioning,
nextPlayerRef: player2Ref,
setIsTransitioning,
});
},
[isTransitioning, player1?.suffix]
[isTransitioning, player1?.container]
);
const handleGapless2 = useCallback(
@@ -139,13 +137,13 @@ export const AudioPlayer = forwardRef(
return gaplessHandler({
currentTime: e.playedSeconds,
duration: getDuration(player2Ref),
isFlac: player2?.suffix === 'flac',
isFlac: player2?.container === 'flac',
isTransitioning,
nextPlayerRef: player1Ref,
setIsTransitioning,
});
},
[isTransitioning, player2?.suffix]
[isTransitioning, player2?.container]
);
return (
-10
View File
@@ -1,10 +0,0 @@
import { ReactNode } from 'react';
import { Button as MantineButton } from '@mantine/core';
interface ButtonProps {
icon?: ReactNode;
}
export const Button = ({ icon }: ButtonProps) => {
return <MantineButton>Button</MantineButton>;
};
+114
View File
@@ -0,0 +1,114 @@
import React, { forwardRef, Ref } from 'react';
import styled from '@emotion/styled';
import {
Button as MantineButton,
ButtonProps as MantineButtonProps,
createPolymorphicComponent,
TooltipProps,
} from '@mantine/core';
import { Tooltip } from '@/renderer/components/tooltip';
interface ButtonProps extends MantineButtonProps {
children: React.ReactNode;
onClick?: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
tooltip?: Omit<TooltipProps, 'children'>;
}
interface StyledButtonProps extends ButtonProps {
ref: Ref<HTMLButtonElement>;
}
const StyledButton = styled(MantineButton)<StyledButtonProps>`
color: ${(props) => {
switch (props.variant) {
case 'default':
return 'var(--btn-default-fg)';
case 'filled':
return 'var(--btn-primary-fg)';
case 'subtle':
return 'var(--btn-subtle-fg)';
default:
return '';
}
}};
background-color: ${(props) => {
switch (props.variant) {
case 'default':
return 'var(--btn-default-bg)';
case 'filled':
return 'var(--btn-primary-bg)';
case 'subtle':
return 'var(--btn-subtle-bg)';
default:
return '';
}
}};
&:hover {
color: ${(props) => {
switch (props.variant) {
case 'default':
return 'var(--btn-default-fg-hover)';
case 'filled':
return 'var(--btn-primary-fg-hover)';
case 'subtle':
return 'var(--btn-subtle-fg-hover)';
default:
return '';
}
}};
background-color: ${(props) => {
switch (props.variant) {
case 'default':
return 'var(--btn-default-bg-hover)';
case 'filled':
return 'var(--btn-primary-bg-hover)';
case 'subtle':
return 'var(--btn-subtle-bg-hover)';
default:
return '';
}
}};
}
&:active {
transform: scale(0.98);
}
&:focus-visible {
border: 1px var(--primary-color) solid;
}
&.mantine-Button-root &:focus-visible {
outline: --var-primary;
}
`;
const pButton = forwardRef<HTMLButtonElement, ButtonProps>(
({ children, tooltip, ...props }: ButtonProps, ref) => {
if (tooltip) {
return (
<Tooltip {...tooltip}>
<StyledButton ref={ref} {...props}>
{children}
</StyledButton>
</Tooltip>
);
}
return (
<StyledButton ref={ref} {...props}>
{children}
</StyledButton>
);
}
);
export const Button = createPolymorphicComponent<'button', ButtonProps>(
pButton
);
pButton.defaultProps = {
onClick: undefined,
tooltip: undefined,
};
@@ -1,14 +0,0 @@
import { useDraggable } from '@dnd-kit/core';
export const Draggable = ({ element, id, children, key }: any) => {
const Element = element || 'div';
const { attributes, listeners, setNodeRef } = useDraggable({
id,
});
return (
<Element key={key} ref={setNodeRef} {...listeners} {...attributes}>
{children}
</Element>
);
};
@@ -0,0 +1,69 @@
import styled from '@emotion/styled';
import {
Menu as MantineMenu,
MenuProps as MantineMenuProps,
MenuItemProps as MantineMenuItemProps,
MenuLabelProps as MantineMenuLabelProps,
MenuDividerProps as MantineMenuDividerProps,
MenuDropdownProps as MantineMenuDropdownProps,
createPolymorphicComponent,
} from '@mantine/core';
type MenuProps = MantineMenuProps;
type MenuLabelProps = MantineMenuLabelProps;
type MenuItemProps = MantineMenuItemProps;
type MenuDividerProps = MantineMenuDividerProps;
type MenuDropdownProps = MantineMenuDropdownProps;
const StyledMenu = styled(MantineMenu)<MenuProps>``;
const StyledMenuLabel = styled(MantineMenu.Label)`
font-family: var(--label-font-family);
`;
const StyledMenuItem = styled(MantineMenu.Item)`
padding: 0.5rem;
font-size: 0.9em;
font-family: var(--label-font-family);
background-color: var(--dropdown-menu-bg);
& .mantine-Menu-itemIcon {
margin-right: 0.5rem;
}
`;
const StyledMenuDropdown = styled(MantineMenu.Dropdown)`
background: var(--dropdown-menu-bg);
`;
const StyledMenuDivider = styled(MantineMenu.Divider)`
margin: 0.3rem 0;
`;
export const DropdownMenu = ({ children, ...props }: MenuProps) => {
return <StyledMenu {...props}>{children}</StyledMenu>;
};
const MenuLabel = ({ children, ...props }: MenuLabelProps) => {
return <StyledMenuLabel {...props}>{children}</StyledMenuLabel>;
};
const pMenuItem = ({ children, ...props }: MenuItemProps) => {
return <StyledMenuItem {...props}>{children}</StyledMenuItem>;
};
const MenuDropdown = ({ children, ...props }: MenuDropdownProps) => {
return <StyledMenuDropdown {...props}>{children}</StyledMenuDropdown>;
};
const MenuItem = createPolymorphicComponent<'button', MenuItemProps>(pMenuItem);
const MenuDivider = ({ ...props }: MenuDividerProps) => {
return <StyledMenuDivider {...props} />;
};
DropdownMenu.Label = MenuLabel;
DropdownMenu.Item = MenuItem;
DropdownMenu.Target = MantineMenu.Target;
DropdownMenu.Dropdown = MenuDropdown;
DropdownMenu.Divider = MenuDivider;
@@ -1,34 +0,0 @@
import { ComponentPropsWithoutRef, ReactNode } from 'react';
import { ActionIcon, ActionIconProps, TooltipProps } from '@mantine/core';
import { Tooltip } from '../tooltip/Tooltip';
type MantineIconButtonProps = ActionIconProps &
ComponentPropsWithoutRef<'button'>;
interface IconButtonProps extends MantineIconButtonProps {
active?: boolean;
icon: ReactNode;
tooltip?: Omit<TooltipProps, 'children'>;
}
export const IconButton = ({
active,
tooltip,
icon,
...rest
}: IconButtonProps) => {
if (tooltip) {
return (
<Tooltip {...tooltip}>
<ActionIcon {...rest}>{icon}</ActionIcon>
</Tooltip>
);
}
return <ActionIcon {...rest}>{icon}</ActionIcon>;
};
IconButton.defaultProps = {
active: false,
tooltip: undefined,
};
+10 -4
View File
@@ -1,4 +1,10 @@
export * from './tooltip/Tooltip';
export * from './audio-player/AudioPlayer';
export * from './icon-button/IconButton';
export * from './text/Text';
export * from './tooltip';
export * from './audio-player';
export * from './text';
export * from './button';
export * from './virtual-grid';
export * from './modal';
export * from './input';
export * from './segmented-control';
export * from './dropdown-menu';
export * from './toast';
+79
View File
@@ -0,0 +1,79 @@
import React, { forwardRef } from 'react';
import styled from '@emotion/styled';
import {
TextInput as MantineTextInput,
TextInputProps as MantineTextInputProps,
PasswordInput as MantinePasswordInput,
PasswordInputProps as MantinePasswordInputProps,
} from '@mantine/core';
interface TextInputProps extends MantineTextInputProps {
children: React.ReactNode;
}
interface PasswordInputProps extends MantinePasswordInputProps {
children: React.ReactNode;
}
const StyledTextInput = styled(MantineTextInput)<TextInputProps>`
&:focus,
&:focus-within {
border-color: var(--primary-color);
}
& .mantine-TextInput-wrapper {
border-color: var(--primary-color);
}
& .mantine-TextInput-input {
&:focus,
&:focus-within {
border-color: var(--primary-color);
}
}
& .mantine-TextInput-required {
color: var(--secondary-color);
}
& .mantine-TextInput-label {
font-family: var(--label-font-faimly);
}
`;
const StyledPasswordInput = styled(MantinePasswordInput)<PasswordInputProps>`
& .mantine-PasswordInput-input {
&:focus,
&:focus-within {
border-color: var(--primary-color);
}
}
& .mantine-PasswordInput-required {
color: var(--secondary-color);
}
& .mantine-PasswordInput-label {
font-family: var(--label-font-faimly);
}
`;
export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
({ children, ...props }: TextInputProps, ref) => {
return (
<StyledTextInput ref={ref} spellCheck={false} {...props}>
{children}
</StyledTextInput>
);
}
);
export const PasswordInput = forwardRef<HTMLInputElement, PasswordInputProps>(
({ children, ...props }: PasswordInputProps, ref) => {
return (
<StyledPasswordInput ref={ref} spellCheck={false} {...props}>
{children}
</StyledPasswordInput>
);
}
);
-27
View File
@@ -1,27 +0,0 @@
import {
Modal as MantineModal,
ModalProps as MantineModalProps,
} from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
interface ModalProps extends MantineModalProps {
condition: boolean;
}
export const Modal = ({ condition, children, ...rest }: ModalProps) => {
const [opened, handlers] = useDisclosure(false);
return (
<>
{condition && (
<MantineModal
{...rest}
opened={opened}
onClose={() => handlers.close()}
>
{children}
</MantineModal>
)}
</>
);
};
+31
View File
@@ -0,0 +1,31 @@
import React from 'react';
import {
Modal as MantineModal,
ModalProps as MantineModalProps,
} from '@mantine/core';
export interface ModalProps extends Omit<MantineModalProps, 'onClose'> {
children?: React.ReactNode;
handlers: {
close: () => void;
open: () => void;
toggle: () => void;
};
}
export const Modal = ({ children, handlers, ...rest }: ModalProps) => {
return (
<MantineModal
overlayBlur={2}
overlayOpacity={0.2}
{...rest}
onClose={handlers.close}
>
{children}
</MantineModal>
);
};
Modal.defaultProps = {
children: undefined,
};
@@ -0,0 +1,29 @@
import { forwardRef } from 'react';
import styled from '@emotion/styled';
import {
SegmentedControl as MantineSegmentedControl,
SegmentedControlProps as MantineSegmentedControlProps,
} from '@mantine/core';
type SegmentedControlProps = MantineSegmentedControlProps;
const StyledSegmentedControl = styled(
MantineSegmentedControl
)<MantineSegmentedControlProps>`
&:focus,
&:focus-within,
&:active {
outline: 1px var(--primary-color) solid;
}
& .mantine-SegmentedControl-label {
font-family: var(--label-font-family);
}
`;
export const SegmentedControl = forwardRef<
HTMLDivElement,
SegmentedControlProps
>(({ ...props }: SegmentedControlProps, ref) => {
return <StyledSegmentedControl ref={ref} {...props} />;
});
@@ -3,14 +3,13 @@ import {
ComponentPropsWithRef,
ReactNode,
} from 'react';
import styled from '@emotion/styled';
import {
Text as MantineText,
TextProps as MantineTextProps,
} from '@mantine/core';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import { Font } from '../../styles';
import { textEllipsis } from '../../styles/mixins';
import { Font, textEllipsis } from '@/renderer/styles';
type MantineTextDivProps = MantineTextProps & ComponentPropsWithoutRef<'div'>;
type MantineTextLinkProps = MantineTextProps & ComponentPropsWithRef<'link'>;
@@ -42,7 +41,7 @@ const BaseText = styled(MantineText)<any>`
? 'var(--playerbar-text-secondary-color)'
: 'var(--playerbar-text-primary-color)'};
font-family: ${(props) => props.font || Font.GOTHAM};
cursor: ${(props) => (props.link ? 'cursor' : 'default')};
/* cursor: ${(props) => (props.link ? 'cursor' : 'default')}; */
user-select: ${(props) => (props.$noSelect ? 'none' : 'auto')};
${(props) => props.overflow === 'hidden' && textEllipsis}
`;
@@ -63,7 +62,7 @@ export const Text = ({
}: TextProps) => {
if (link) {
return (
<StyledLinkText<typeof Link>
<StyledLinkText
$noSelect={noSelect}
$secondary={secondary}
component={Link}
+67
View File
@@ -0,0 +1,67 @@
import {
showNotification,
NotificationProps as MantineNotificationProps,
updateNotification,
hideNotification,
cleanNotifications,
cleanNotificationsQueue,
} from '@mantine/notifications';
interface NotificationProps extends MantineNotificationProps {
type?: 'success' | 'error' | 'warning' | 'info';
}
const showToast = ({ type, ...props }: NotificationProps) => {
const color =
type === 'success'
? 'var(--success-color)'
: type === 'warning'
? 'var(--warning-color)'
: type === 'error'
? 'var(--danger-color)'
: 'var(--primary-color)';
const defaultTitle =
type === 'success'
? 'Success'
: type === 'warning'
? 'Warning'
: type === 'error'
? 'Error'
: 'Info';
const defaultDuration = type === 'error' ? 3500 : 2000;
return showNotification({
autoClose: defaultDuration,
disallowClose: true,
styles: () => ({
closeButton: {},
description: {
color: 'var(--toast-description-fg)',
fontSize: '.9em',
},
loader: {
margin: '1rem',
},
root: {
'&::before': { backgroundColor: color },
background: 'var(--toast-bg)',
},
title: {
color: 'var(--toast-title-fg)',
fontSize: '1em',
},
}),
title: defaultTitle,
...props,
});
};
export const toast = {
clean: cleanNotifications,
cleanQueue: cleanNotificationsQueue,
hide: hideNotification,
show: showToast,
update: updateNotification,
};
@@ -1,9 +0,0 @@
.body {
padding: 5px;
color: var(--tooltip-text-color);
background: var(--tooltip-bg);
}
.arrow {
background: var(--tooltip-bg);
}
@@ -1,31 +0,0 @@
import { Tooltip as MantineTooltip, TooltipProps } from '@mantine/core';
export const Tooltip = ({ children, ...rest }: TooltipProps) => {
return (
<MantineTooltip
radius="xs"
styles={{
arrow: {
background: 'var(--tooltip-bg)',
},
root: {
background: 'var(--tooltip-bg)',
color: 'var(--tooltip-text-color)',
padding: '5px',
},
}}
{...rest}
>
{children}
</MantineTooltip>
);
};
Tooltip.defaultProps = {
openDelay: 0,
position: 'top',
transition: 'fade',
transitionDuration: 250,
withArrow: true,
withinPortal: true,
};
+31
View File
@@ -0,0 +1,31 @@
import styled from '@emotion/styled';
import { Tooltip as MantineTooltip, TooltipProps } from '@mantine/core';
const StyledTooltip = styled(MantineTooltip)`
& .mantine-Tooltip-arrow {
background: var(--tooltip-bg);
}
& .mantine-Tooltip-tooltip {
margin: 20px;
color: var(--tooltip-text-color);
background: var(--tooltip-bg);
}
`;
export const Tooltip = ({ children, ...rest }: TooltipProps) => {
return (
<StyledTooltip pl={10} pr={10} py={5} radius="xs" {...rest}>
{children}
</StyledTooltip>
);
};
Tooltip.defaultProps = {
openDelay: 0,
position: 'top',
transition: 'fade',
transitionDuration: 250,
withArrow: true,
withinPortal: true,
};
@@ -1,171 +0,0 @@
import React, { useState } from 'react';
import { DragOverlay, useDndMonitor } from '@dnd-kit/core';
import { snapCenterToCursor } from '@dnd-kit/modifiers';
import { Card, Skeleton } from '@mantine/core';
import { motion } from 'framer-motion';
import { createPortal } from 'react-dom';
import styled from 'styled-components';
import { CardRow } from '../../types';
import { Draggable } from '../drag-drop/Draggable';
import { Text } from '../text/Text';
import { GridCardControls } from './GridCardControls';
const CardWrapper = styled(motion.div)<{
itemGap: number;
itemHeight: number;
itemWidth: number;
}>`
flex: ${({ itemWidth }) => `0 0 ${itemWidth}px`};
width: ${({ itemWidth }) => `${itemWidth}px`};
height: ${({ itemHeight }) => `${itemHeight}px`};
margin: ${({ itemGap }) => `0 ${itemGap / 2}px`};
border-radius: 3px;
filter: drop-shadow(0 4px 4px #000);
user-select: none;
pointer-events: auto; // https://github.com/bvaughn/react-window/issues/128#issuecomment-460166682
&:focus-visible {
outline: 1px solid #fff;
}
`;
const StyledCard = styled(Card)`
display: flex;
flex-direction: column;
gap: 0.5rem;
width: 100%;
height: 100%;
padding: 0;
background-color: rgb(50, 50, 50, 50%);
border-radius: 3px;
transition: background-color 0.2s ease-in-out;
&:hover {
background-color: rgb(50, 50, 50, 60%);
}
`;
const ImageSection = styled.div`
width: 100%;
height: 100%;
`;
interface ImageProps {
height: number;
src: string;
}
const Image = styled(motion.div).attrs((props: ImageProps) => ({
style: {
background: `url(${props.src})`,
},
}))<ImageProps>`
height: ${({ height }) => `${height}px`};
background-position: center;
background-size: cover;
border: 0;
`;
const ControlsContainer = styled.div`
display: none;
width: 100%;
height: 100%;
${StyledCard}:hover & {
display: block;
}
`;
const DetailSection = styled.div`
display: flex;
flex-direction: column;
`;
const Row = styled.div`
height: 25px;
padding: 0 0.2rem;
`;
export const GridCard = ({ data, index, style }: any) => {
const {
itemHeight,
itemWidth,
columnCount,
itemGap,
itemCount,
cardControls,
handlePlayQueueAdd,
cardRows,
itemData,
} = data;
const startIndex = index * columnCount;
const stopIndex = Math.min(itemCount - 1, startIndex + columnCount - 1);
const cards = [];
const [isDragging, setIsDragging] = useState(false);
useDndMonitor({
onDragCancel: () => setIsDragging(false),
onDragEnd: () => setIsDragging(false),
onDragStart: () => setIsDragging(true),
});
for (let i = startIndex; i <= stopIndex; i += 1) {
cards.push(
<React.Fragment key={`card-${i}-${index}`}>
<Draggable id={`${i}-${index}`}>
<CardWrapper
itemGap={itemGap}
itemHeight={itemHeight}
itemWidth={itemWidth}
>
<Skeleton visible={!itemData[i]}>
<StyledCard>
<ImageSection>
<Image height={itemWidth} src={itemData[i]?.image}>
<ControlsContainer>
<GridCardControls
cardControls={cardControls}
handlePlayQueueAdd={handlePlayQueueAdd}
itemData={itemData[i]}
/>
</ControlsContainer>
</Image>
</ImageSection>
<DetailSection>
{cardRows.map((row: CardRow) => (
<Row key={`row-${row.prop}`}>
<Text overflow="hidden" weight={500}>
{itemData[i] && itemData[i][row.prop]}
</Text>
</Row>
))}
</DetailSection>
</StyledCard>
</Skeleton>
</CardWrapper>
</Draggable>
{createPortal(
<DragOverlay dropAnimation={null} modifiers={[snapCenterToCursor]}>
{isDragging ? <div>OVERLAY</div> : null}
</DragOverlay>,
document.body
)}
</React.Fragment>
);
}
return (
<div
style={{
...style,
alignItems: 'center',
display: 'flex',
justifyContent: 'start',
}}
>
{cards}
</div>
);
};
@@ -1,8 +1,8 @@
import React from 'react';
import styled from '@emotion/styled';
import { Button, UnstyledButton, UnstyledButtonProps } from '@mantine/core';
import { motion } from 'framer-motion';
import { RiPlayFill } from 'react-icons/ri';
import styled from 'styled-components';
import { Play } from '../../../types';
type PlayButtonType = UnstyledButtonProps &
@@ -0,0 +1,152 @@
import React from 'react';
import styled from '@emotion/styled';
import { Skeleton } from '@mantine/core';
import { motion } from 'framer-motion';
import { CardRow } from '../../types';
import { Text } from '../text';
import { GridCardControls } from './grid-card-controls';
const CardWrapper = styled(motion.div)<{
itemGap: number;
itemHeight: number;
itemWidth: number;
}>`
flex: ${({ itemWidth }) => `0 0 ${itemWidth}px`};
width: ${({ itemWidth }) => `${itemWidth}px`};
height: ${({ itemHeight }) => `${itemHeight}px`};
margin: ${({ itemGap }) => `0 ${itemGap / 2}px`};
filter: drop-shadow(0 4px 4px #000);
transition: border 0.2s ease-in-out;
user-select: none;
pointer-events: auto; // https://github.com/bvaughn/react-window/issues/128#issuecomment-460166682
&:focus-visible {
outline: 1px solid #fff;
}
`;
const StyledCard = styled.div`
display: flex;
flex-direction: column;
gap: 0.5rem;
width: 100%;
height: 100%;
padding: 0;
background: var(--grid-card-bg);
border-radius: 3px;
`;
const ImageSection = styled.div`
width: 100%;
height: 100%;
`;
interface ImageProps {
height: number;
src: string;
}
// const Image = styled(motion.div).attrs((props: ImageProps) => ({
// style: {
// background: `url(${props.src})`,
// backgroundPosition: 'center',
// backgroundSize: 'cover',
// },
// }))<ImageProps>`
// height: ${({ height }) => `${height}px`};
// background-position: center;
// background-size: cover;
// border: 0;
// `;
const Image = styled(motion.div)<ImageProps>`
height: ${({ height }) => `${height}px`};
background: ${({ src }) => `url(${src})`};
background-position: center;
background-size: cover;
border: 0;
`;
const ControlsContainer = styled.div`
display: block;
width: 100%;
height: 100%;
`;
const DetailSection = styled.div`
display: flex;
flex-direction: column;
`;
const Row = styled.div`
height: 25px;
padding: 0 0.2rem;
`;
export const GridCard = ({ data, index, style }: any) => {
const {
itemHeight,
itemWidth,
columnCount,
itemGap,
itemCount,
cardControls,
handlePlayQueueAdd,
cardRows,
itemData,
} = data;
const startIndex = index * columnCount;
const stopIndex = Math.min(itemCount - 1, startIndex + columnCount - 1);
const cards = [];
for (let i = startIndex; i <= stopIndex; i += 1) {
cards.push(
<React.Fragment key={`card-${i}-${index}`}>
<CardWrapper
itemGap={itemGap}
itemHeight={itemHeight}
itemWidth={itemWidth}
>
<Skeleton visible={!itemData[i]}>
<StyledCard>
<ImageSection>
<Image height={itemWidth} src={itemData[i]?.imageUrl}>
<ControlsContainer>
<GridCardControls
cardControls={cardControls}
handlePlayQueueAdd={handlePlayQueueAdd}
itemData={itemData[i]}
/>
</ControlsContainer>
</Image>
</ImageSection>
<DetailSection>
{cardRows.map((row: CardRow) => (
<Row key={`row-${row.prop}`}>
<Text overflow="hidden" weight={500}>
{itemData[i] && itemData[i][row.prop]}
</Text>
</Row>
))}
</DetailSection>
</StyledCard>
</Skeleton>
</CardWrapper>
</React.Fragment>
);
}
return (
<div
style={{
...style,
alignItems: 'center',
display: 'flex',
justifyContent: 'start',
}}
>
{cards}
</div>
);
};
@@ -0,0 +1,2 @@
export * from './virtual-grid-wrapper';
export * from './virtual-infinite-grid';
@@ -1,8 +1,8 @@
import { Ref, useMemo } from 'react';
import { FixedSizeList, FixedSizeListProps } from 'react-window';
import { usePlayQueueHandler } from '../../features/player/hooks/usePlayQueueHandler';
import { CardRow } from '../../types';
import { GridCard } from './GridCard';
import { GridCard } from '@/renderer/components/virtual-grid/grid-card';
import { usePlayQueueHandler } from '@/renderer/features/player/hooks/usePlayQueueHandler';
import { CardRow } from '@/renderer/types';
export const VirtualGridWrapper = ({
refInstance,
@@ -2,8 +2,8 @@ import { useState, useEffect, useRef, useMemo } from 'react';
import debounce from 'lodash/debounce';
import { FixedSizeListProps } from 'react-window';
import InfiniteLoader from 'react-window-infinite-loader';
import { CardRow } from '../../types';
import { VirtualGridWrapper } from './VirtualGridWrapper';
import { VirtualGridWrapper } from '@/renderer/components/virtual-grid/virtual-grid-wrapper';
import { CardRow } from '@/renderer/types';
interface VirtualGridProps
extends Omit<FixedSizeListProps, 'children' | 'itemSize'> {
@@ -12,8 +12,9 @@ interface VirtualGridProps
itemGap?: number;
itemSize: number;
minimumBatchSize?: number;
query: (props: any) => Promise<any>;
query: ({ serverId }: { serverId: string }, props: any) => Promise<any>;
queryParams?: Record<string, any>;
serverId: string;
}
export const VirtualInfiniteGrid = ({
@@ -26,6 +27,7 @@ export const VirtualInfiniteGrid = ({
query,
queryParams,
height,
serverId,
width,
}: VirtualGridProps) => {
const [itemData, setItemData] = useState<any[]>([]);
@@ -58,11 +60,14 @@ export const VirtualInfiniteGrid = ({
const start = startIndex * columnCount;
const end = stopIndex * columnCount + columnCount;
const t = await query({
skip: start,
take: end - start,
...queryParams,
});
const t = await query(
{ serverId },
{
skip: start,
take: end - start,
...queryParams,
}
);
const newData: any[] = [...itemData];
@@ -109,7 +114,7 @@ export const VirtualInfiniteGrid = ({
itemCount={itemCount || 0}
itemData={itemData}
itemGap={itemGap!}
itemHeight={itemHeight!}
itemHeight={itemHeight! + itemGap! / 2}
itemWidth={itemSize}
refInstance={(list) => {
infiniteLoaderRef(list);