Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion public/library/images/icons/all.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/components/library/atoms/CharCount/CharCount.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.count {
display: block;
margin-top: 2px;
font-size: 12px;
line-height: 1.3;
color: var(--black-transparent-300);
}

.over {
color: var(--red-600);
}
27 changes: 27 additions & 0 deletions src/components/library/atoms/CharCount/CharCount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import classNames from 'classnames';
import React, { JSX } from 'react';

import styles from './CharCount.module.scss';

interface CharCountProps {
current: number;
max: number;
className?: string;
}

export function CharCount({
current,
max,
className,
}: CharCountProps): JSX.Element {
return (
<span
className={classNames(styles.count, className, {
[styles.over]: current > max,
})}
aria-hidden="true"
>
{current}/{max}
</span>
);
}
1 change: 1 addition & 0 deletions src/components/library/atoms/CharCount/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './CharCount';
10 changes: 6 additions & 4 deletions src/components/library/molecules/AddShelfModal/AddShelfModal.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import classNames from 'classnames';
import React, { JSX, useState } from 'react';

import { shelfCardData } from '@constants/library/common';
import {
SHELF_NAME_MAX_LENGTH,
shelfCardData,
} from '@constants/library/common';

import { CharCount } from '@components/library/atoms/CharCount';
import { Loader } from '@components/library/atoms/Loader';
import { Text, TypographyVariant } from '@components/library/atoms/Text';

Expand All @@ -13,9 +17,6 @@ import type { AddShelfModalProps, ShelfType } from './AddShelfModal.types';

import styles from './AddShelfModal.module.scss';

// Matches the single-shelf `name` constraint (`maxLength: 50`) in the backend schema.
const SHELF_NAME_MAX_LENGTH = 50;

export function AddShelfModal(props: AddShelfModalProps): JSX.Element {
const { onClose, onAddShelf, existingNames = [] } = props;
const { closeRef, close } = useModalClose(onClose);
Expand Down Expand Up @@ -78,6 +79,7 @@ export function AddShelfModal(props: AddShelfModalProps): JSX.Element {
ariaLabel="Shelf name"
maxLength={SHELF_NAME_MAX_LENGTH}
/>
<CharCount current={name.length} max={SHELF_NAME_MAX_LENGTH} />
{error && <p className={styles.error}>{error}</p>}
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@
width: 100%;
height: 100%;
object-fit: cover;
// Fade the album art in once it decodes so it no longer pops in over the
// vinyl-sleeve placeholder; `.coverImageLoaded` is added on load.
opacity: 0;
transition: opacity 0.45s ease;
}

.coverImageLoaded {
opacity: 1;
}

.tags {
Expand Down
18 changes: 16 additions & 2 deletions src/components/library/molecules/AudioCard/AudioCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { resolveStrapiUrl } from '@utils/library/resolveStrapiUrl';
import classNames from 'classnames';
import Image from 'next/image';
import React, { JSX } from 'react';
import React, { JSX, useCallback, useState } from 'react';

import { SelectToggle } from '@components/library/molecules/SelectToggle';

Expand All @@ -25,6 +25,16 @@ export function AudioCard({
const tags = attributes.tags?.data ?? [];
const title = attributes.title;

const [coverLoaded, setCoverLoaded] = useState(false);

// A cached image can finish decoding before React attaches `onLoad`, leaving
// it stuck at opacity 0. Catch that case via the ref's `complete` flag.
const coverRef = useCallback((node: HTMLImageElement | null) => {
if (node?.complete) {
setCoverLoaded(true);
}
}, []);

const handleActivate = () => onClick?.(object);

const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
Expand Down Expand Up @@ -61,11 +71,15 @@ export function AudioCard({
<div className={styles.cover}>
{coverUrl && (
<Image
ref={coverRef}
src={coverUrl}
alt={title}
fill
sizes="190px"
className={styles.coverImage}
className={classNames(styles.coverImage, {
[styles.coverImageLoaded]: coverLoaded,
})}
onLoad={() => setCoverLoaded(true)}
/>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
align-items: center;
gap: 6px;

&.current .card {
&.current .card:not(.placeholder) {
border-color: var(--brown);
box-shadow: 0 0 0 2px var(--brown);
}
Expand All @@ -29,7 +29,6 @@
background: var(--white);
border: 1px solid var(--brown-border);
border-radius: 4px;
padding: 8px;
cursor: grab;
user-select: none;
touch-action: none;
Expand All @@ -38,6 +37,24 @@
cursor: grabbing;
}

// The slot left behind by the card being dragged. The solid clone rides the
// cursor in the DragOverlay; this dashed outline marks where it will land.
&.placeholder {
background: transparent;
border: 1px dashed #cebda1;
box-shadow: none;

.cover {
opacity: 0;
}
}

// The clone under the cursor — lift it off the grid so it reads as picked up.
&.overlayCard {
cursor: grabbing;
box-shadow: 0 8px 20px rgb(0 0 0 / 18%);
}

.cover {
width: 100%;
border-radius: 4px;
Expand Down
Loading
Loading