Refactor add to playlist modal (#1236)

* Refactor add to playlist modal

* redesign base modal component, add ModalButton component

* improve visibility of filled button focus

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
This commit is contained in:
Kendall Garner
2025-11-02 04:57:12 +00:00
committed by GitHub
parent 829c27a5e9
commit 1a176fd118
18 changed files with 667 additions and 225 deletions
@@ -47,6 +47,11 @@
&:focus-visible {
background: darken(var(--theme-colors-primary-filled), 10%);
}
&:focus-visible {
outline: 1px solid var(--theme-colors-primary-filled);
outline-offset: 2px;
}
}
&[data-variant='state-error'] {
+5 -5
View File
@@ -1,6 +1,5 @@
import clsx from 'clsx';
import { MotionConfigProps } from 'motion/react';
import { ForwardedRef, forwardRef, type ImgHTMLAttributes } from 'react';
import { ForwardedRef, forwardRef, HTMLAttributes, type ImgHTMLAttributes, ReactNode } from 'react';
import { Img } from 'react-image';
import styles from './image.module.css';
@@ -9,9 +8,8 @@ import { Icon } from '/@/shared/components/icon/icon';
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
import { useInViewport } from '/@/shared/hooks/use-in-viewport';
interface ImageContainerProps extends MotionConfigProps {
children: React.ReactNode;
className?: string;
interface ImageContainerProps extends HTMLAttributes<HTMLDivElement> {
children: ReactNode;
enableAnimation?: boolean;
}
@@ -44,6 +42,7 @@ export function Image({
includeLoader = true,
includeUnloader = true,
src,
...props
}: ImageProps) {
const { inViewport, ref } = useInViewport();
@@ -78,6 +77,7 @@ export function Image({
</ImageContainer>
) : null
}
{...props}
/>
);
}
+19 -10
View File
@@ -1,17 +1,26 @@
.title {
font-size: var(--theme-font-size-lg);
font-weight: 700;
}
.body {
padding: var(--theme-spacing-sm) var(--theme-spacing-md);
}
.header {
display: flex;
justify-content: center;
padding: none;
background: var(--theme-colors-background);
border-bottom: none;
border-radius: 0;
}
.header h2 {
width: 100%;
font-size: var(--theme-font-size-2xl);
font-weight: 700;
user-select: none;
}
.content {
overflow: hidden;
background: var(--theme-colors-background);
border: 2px solid var(--theme-colors-border);
}
.close {
position: absolute;
top: var(--theme-spacing-md);
right: var(--theme-spacing-md);
}
+11 -2
View File
@@ -12,6 +12,7 @@ import { Button } from '/@/shared/components/button/button';
import { Flex } from '/@/shared/components/flex/flex';
import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon';
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
import { Stack } from '/@/shared/components/stack/stack';
export interface ModalProps extends Omit<MantineModalProps, 'onClose'> {
@@ -113,15 +114,23 @@ export const ModalsProvider = ({ children, ...rest }: ModalsProviderProps) => {
centered: true,
classNames: {
body: styles.body,
close: styles.close,
content: styles.content,
header: styles.header,
inner: styles.inner,
overlay: styles.overlay,
root: styles.root,
title: styles.title,
},
closeButtonProps: {
icon: <Icon icon="x" />,
icon: <Icon icon="x" size="xl" />,
},
radius: 'lg',
overlayProps: {
backgroundOpacity: 0.8,
blur: 4,
},
radius: 'xl',
scrollAreaComponent: ScrollArea,
transitionProps: {
duration: 300,
exitDuration: 300,
@@ -0,0 +1,9 @@
import { Button, ButtonProps } from '/@/shared/components/button/button';
export const ModalButton = ({ children, ...props }: ButtonProps) => {
return (
<Button px="2xl" uppercase variant="subtle" {...props}>
{children}
</Button>
);
};
@@ -0,0 +1,32 @@
.label {
font-family: var(--theme-content-font-family);
}
.label.sm {
font-size: var(--theme-font-size-sm);
}
.label.md {
font-size: var(--theme-font-size-md);
}
.label.lg {
font-size: var(--theme-font-size-lg);
}
.label.xl {
font-size: var(--theme-font-size-xl);
}
.label.xs {
font-size: var(--theme-font-size-xs);
}
.remove {
transition: color 0.1s ease-in-out;
&:hover {
color: var(--theme-colors-foreground-muted);
}
}
+29
View File
@@ -0,0 +1,29 @@
import { Pill as MantinePill, PillProps as MantinePillProps } from '@mantine/core';
import clsx from 'clsx';
import styles from './pill.module.css';
export const Pill = ({ children, size = 'md', ...props }: MantinePillProps) => {
return (
<MantinePill
classNames={{
label: clsx({
[styles.label]: true,
[styles.lg]: size === 'lg',
[styles.md]: size === 'md',
[styles.sm]: size === 'sm',
[styles.xl]: size === 'xl',
[styles.xs]: size === 'xs',
}),
remove: styles.remove,
root: styles.root,
}}
size="md"
{...props}
>
{children}
</MantinePill>
);
};
Pill.Group = MantinePill.Group;