Media slot
Playground
TSX
'use client';
import { useState, useEffect, useRef } from 'react';
import {
Icon,
MediaSlot,
type MediaSlotSize,
type MediaSlotShape,
type MediaSlotState,
} from '@mbsw/ui';
import { Folder } from 'lucide-react';
export default function Demo() {
const settings = {
"size": "md",
"shape": "square",
"state": "empty"
};
const [message, setMessage] = useState('');
const [state, setState] = useState(settings.state as MediaSlotState);
const [file, setFile] = useState<File | null>(null);
const [imageUrl, setImageUrl] = useState('');
const input = useRef<HTMLInputElement>(null);
const picker = useRef<HTMLButtonElement>(null);
const previousSetting = useRef(settings.state);
useEffect(() => {
if (previousSetting.current === settings.state) return;
previousSetting.current = settings.state;
setState(settings.state as MediaSlotState);
if (settings.state !== 'filled') {
setFile(null);
setMessage('');
}
}, [settings.state]);
useEffect(() => {
if (!file) {
setImageUrl('');
return;
}
const url = URL.createObjectURL(file);
setImageUrl(url);
return () => URL.revokeObjectURL(url);
}, [file]);
const changeState = (next: MediaSlotState) => {
setState(next);
};
const showError = () => {
setFile(null);
changeState('error');
setMessage("Couldn't read the image file. Choose another file.");
};
const common = {
ref: picker,
'aria-label': "Choose an image file",
size: settings.size as MediaSlotSize,
shape: settings.shape as MediaSlotShape,
onClick: () => input.current?.click(),
};
return (
<div
style={{
position: 'relative',
display: 'flex',
justifyContent: 'center',
width: 280,
maxWidth: '100%',
}}
>
<input
ref={input}
type="file"
accept="image/*"
hidden
onChange={(event) => {
const selected = event.currentTarget.files?.[0];
event.currentTarget.value = '';
if (!selected) return;
if (!selected.type.startsWith('image/')) {
showError();
return;
}
setFile(selected);
changeState('filled');
setMessage('');
}}
/>
{state === 'filled' ? (
<MediaSlot
{...common}
state="filled"
removeLabel={"Remove image"}
onRemove={(event) => {
if (
event.currentTarget ===
event.currentTarget.ownerDocument.activeElement
)
picker.current?.focus();
setFile(null);
changeState('empty');
setMessage('');
}}
>
{file && imageUrl ? (
<img
src={imageUrl}
alt={file.name}
onError={showError}
style={{
display: 'block',
width: '100%',
height: '100%',
objectFit: 'cover',
objectPosition: 'center',
}}
/>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 96 96"
aria-label="Sample image"
role="img"
style={{ display: 'block', width: '100%', height: '100%' }}
>
<rect width="96" height="96" fill="#dedbff" />
<circle cx="48" cy="48" r="24" fill="#7565db" />
</svg>
)}
</MediaSlot>
) : state === 'error' ? (
<MediaSlot
{...common}
state="error"
errorMessage={"Couldn't read the file."}
/>
) : state === 'uploading' ? (
<MediaSlot
{...common}
state="uploading"
uploadLabel={"Uploading"}
/>
) : (
<MediaSlot {...common} state="empty" />
)}
<output
aria-live="polite"
style={{
position: 'absolute',
top: 'calc(100% + 12px)',
insetInline: 0,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 6,
textAlign: 'center',
fontSize: 12,
lineHeight: '18px',
color: 'var(--riido-foreground-tertiary)',
overflowWrap: 'anywhere',
}}
>
{file && state === 'filled' ? (
<>
<span
title={file.name}
style={{
display: 'inline-flex',
alignItems: 'center',
gap: 4,
maxWidth: '100%',
padding: '2px 8px',
borderRadius: 4,
background: 'var(--riido-overlay-hover)',
color: 'var(--riido-foreground-secondary)',
overflow: 'hidden',
}}
>
<Icon
icon={Folder}
size="xs"
decorative
style={{ flexShrink: 0 }}
/>
<span
style={{
minWidth: 0,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}}
>
{file.name}
</span>
</span>
<span>{"This file isn't saved."}</span>
</>
) : (
message
)}
</output>
</div>
);
}
Properties
Size
Shape
State
Usage
Use empty, uploading, error and filled to show file states. Your app handles file selection and uploading. In Playground, you can select, preview, replace and remove an image. The file stays in your browser; it is not uploaded or saved.
TSX
'use client';
import { MediaSlot } from '@mbsw/ui';
export default function Example() {
return <MediaSlot state="empty" aria-label={"Choose an image file"} />;
}
The Playground Code tab includes the full example with your current settings. See Installation for the CSS imports.