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("이미지 파일을 읽을 수 없습니다. 다른 파일을 선택해주세요.");
};
const common = {
ref: picker,
'aria-label': "이미지 파일 선택",
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={"이미지 제거"}
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={"파일을 읽을 수 없습니다."}
/>
) : state === 'uploading' ? (
<MediaSlot
{...common}
state="uploading"
uploadLabel={"업로드 중"}
/>
) : (
<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>{"파일은 저장되지 않습니다."}</span>
</>
) : (
message
)}
</output>
</div>
);
}
Properties
Size
Shape
State
Usage
empty, uploading, error, filled를 구분합니다. 파일 선택과 업로드 동작은 앱에서 연결합니다. Playground에서는 이미지를 선택해 미리 보고, 교체하거나 제거할 수 있습니다. 선택한 파일은 브라우저 안에서만 사용하며 서버로 전송하거나 저장하지 않습니다.
TSX
'use client';
import { MediaSlot } from '@mbsw/ui';
export default function Example() {
return <MediaSlot state="empty" aria-label={"이미지 파일 선택"} />;
}
Playground의 Code 탭에서 현재 설정과 동작이 포함된 예제 전체를 복사할 수 있습니다. 공통 CSS 설치는 설치하기를 확인하세요.