import React, { useMemo } from 'react' import { ComponentStory, ComponentMeta } from '@storybook/react' import { EmoteWithBlobs, IPreviewController, PreviewCamera, PreviewEmote, PreviewProjection, WearableWithBlobs } from '@dcl/schemas/dist/dapps/preview' import { BodyShape, EmoteCategory, Metrics } from '@dcl/schemas/dist/platform/item' import { WearableCategory } from '@dcl/schemas' import { Button } from '../Button/Button' import { Navbar } from '../Navbar/Navbar' import { Tabs } from '../Tabs/Tabs' import { Page } from '../Page/Page' import { Hero } from '../Hero/Hero' import { Container } from '../Container/Container' import { Header } from '../Header/Header' import { Footer } from '../Footer/Footer' import { Row } from '../Row/Row' import { Radio } from '../Radio/Radio' import { Center } from '../Center/Center' import { SliderField } from '../SliderField/SliderField' import { WearablePreview } from './WearablePreview' import { EmoteControls } from './EmoteControls' import { ZoomControls } from './ZoomControls' import { TranslationControls, VerticalPosition } from './TranslationControls' import './WearablePreview.stories.css' const getRandomHex = () => { return '#' + Math.random().toString(16).slice(2, 8) } const getRandomProfile = () => { return `default${Math.floor(Math.random() * 160) + 1}` } const RandomConfigProvider = (props: { children: (hair: string, skin: string, profile: string) => React.ReactElement }) => { const [hair, setHair] = React.useState(getRandomHex()) const [skin, setSkin] = React.useState(getRandomHex()) const [profile, setProfile] = React.useState(getRandomProfile()) React.useEffect(() => { setInterval(() => { setHair(getRandomHex()) setSkin(getRandomHex()) setProfile(getRandomProfile()) }, 5000) }, []) return props.children(hair, skin, profile) } function toWearableWithBlobs(file: File): WearableWithBlobs { return { id: 'some-id', name: '', description: '', image: '', thumbnail: '', i18n: [], data: { category: WearableCategory.HAT, hides: [], replaces: [], tags: [], representations: [ { bodyShapes: [BodyShape.MALE, BodyShape.FEMALE], mainFile: 'model.glb', contents: [ { key: 'model.glb', blob: file } ], overrideHides: [], overrideReplaces: [] } ] } } } function toEmoteWithBlobs(file: File): EmoteWithBlobs { return { id: 'some-id', name: '', description: '', image: '', thumbnail: '', i18n: [], emoteDataADR74: { category: EmoteCategory.DANCE, tags: [], representations: [ { bodyShapes: [BodyShape.MALE, BodyShape.FEMALE], mainFile: 'model.glb', contents: [ { key: 'model.glb', blob: file } ] } ], loop: false } } } export default { title: 'WearablePreview', component: WearablePreview, parameters: { layout: 'fullscreen' } } as ComponentMeta export const PreviewAnItem: ComponentStory = () => (
) PreviewAnItem.storyName = 'Preview an item' export const PreviewAToken: ComponentStory = () => (
) PreviewAToken.storyName = 'Preview a token' export const PreviewATokenFromMumbai: ComponentStory< typeof WearablePreview > = () => (
) PreviewATokenFromMumbai.storyName = 'Preview a token from mumbai' export const PreviewATextureWearable: ComponentStory< typeof WearablePreview > = () => (
) PreviewATextureWearable.storyName = 'Preview a texture wearable' export const UsingCustomSkin: ComponentStory = () => (
) UsingCustomSkin.storyName = 'Using custom skin' export const UsingCustomHair: ComponentStory = () => (
) UsingCustomHair.storyName = 'Using custom hair' export const UsingCustomShape: ComponentStory = () => (
) UsingCustomShape.storyName = 'Using custom shape' export const UsingAProfile: ComponentStory = () => (
) UsingAProfile.storyName = 'Using a profile' export const UsingAProfileEmote: ComponentStory< typeof WearablePreview > = () => (
) UsingAProfileEmote.storyName = 'Using a profile + emote' export const UsingAWearablePreviewProfileEmote: ComponentStory< typeof WearablePreview > = () => (
) UsingAWearablePreviewProfileEmote.storyName = 'Using a wearable + profile + emote' export const UsingStaticCamera: ComponentStory = () => (
) UsingStaticCamera.storyName = 'Using static camera' export const UsingOnLoadCallback: ComponentStory< typeof WearablePreview > = () => (
console.log('loaded!')} />
) UsingOnLoadCallback.storyName = 'Using onLoad callback' export const UsingOnErrorCallback: ComponentStory< typeof WearablePreview > = () => (
console.error(error.message)} />
) UsingOnErrorCallback.storyName = 'Using onError callback' export const UseAsHero: ComponentStory = () => (
Atlas Market My Assets
Hello Wolrd

This page has a hero

) UseAsHero.storyName = 'Use as hero' export const WithHotReload: ComponentStory = () => (
{(hair, skin, profile) => ( console.log('loaded!')} /> )}
) WithHotReload.storyName = 'With hot reload' export const FromUrl: ComponentStory = () => (
) FromUrl.storyName = 'From URL' export const FromBase64: ComponentStory = () => (
= () => (
) WithoutAutoRotation.storyName = 'Without auto Rotation' export const WithoutBackgroundOrTransparentBackground: ComponentStory< typeof WearablePreview > = () => (
) WithoutBackgroundOrTransparentBackground.storyName = 'Without background or transparent background' export const WithCustomBackgroundColor: ComponentStory< typeof WearablePreview > = () => (
) WithCustomBackgroundColor.storyName = 'With custom background color' export const TakeScreenshotAndMetrics: ComponentStory< typeof WearablePreview > = () => { const [screenshot, setScreenshot] = React.useState('') const [metrics, setMetrics] = React.useState(null) const ref = React.useRef(null) const onLoad = React.useCallback(() => { ref.current = ref.current ?? WearablePreview.createController('some-id') }, []) return (
{screenshot && } {metrics &&

{JSON.stringify(metrics)}

}
) } TakeScreenshotAndMetrics.storyName = 'Take screenshot and metrics' export const WithTranslationControls: ComponentStory< typeof WearablePreview > = () => { const [verticalPosition, setVerticalPosition] = React.useState( VerticalPosition.LEFT ) const togglePosition = verticalPosition === VerticalPosition.LEFT ? VerticalPosition.RIGHT : VerticalPosition.LEFT return (
setVerticalPosition(togglePosition)} style={{ textTransform: 'capitalize' }} />
) } WithTranslationControls.storyName = 'With translation controls' export const WithZoomControls: ComponentStory = () => { return (
) } WithZoomControls.storyName = 'With zoom controls' export const WithEmoteControls: ComponentStory = () => { return (
) } WithEmoteControls.storyName = 'With emote controls' export const WithSound: ComponentStory = () => { return (
) } WithSound.storyName = 'With sound' export const EmoteEvents: ComponentStory = () => { const [goTo, setGoTo] = React.useState('0') const [screenshot, setScreenshot] = React.useState('') const [length, setLength] = React.useState('') const ref = React.useRef(null) const onLoad = React.useCallback(() => { ref.current = ref.current ?? WearablePreview.createController('some-id') }, []) return (
setGoTo(e.target.value)} /> {screenshot && } {!!length &&

Length: {length} seconds

}
) } EmoteEvents.storyName = 'Emote events' export const PreviewFromAFile: ComponentStory = () => { const inputRef = React.useRef() const [file, setFile] = React.useState(null) return (
{file ? ( ) : (
setFile(inputRef.current.files[0])} />
)}
) } PreviewFromAFile.storyName = 'Preview from a file' export const EmoteThumbnailPicker: ComponentStory< typeof WearablePreview > = () => { const inputRef = React.useRef() const [file, setFile] = React.useState(null) const [screenshot, setScreenshot] = React.useState('') const [length, setLength] = React.useState(0) const ref = React.useRef(null) const onLoad = React.useCallback(async () => { ref.current = ref.current ?? WearablePreview.createController('thumbnail-picker') setLength(await ref.current.emote.getLength()) }, []) return (
{file ? ( <> {screenshot && } {length > 0 && ( { await ref.current.emote.pause() await ref.current.emote.goTo(value / 100) }} /> )} ) : (
setFile(inputRef.current.files[0])} />
)}
) } EmoteThumbnailPicker.storyName = 'Emote thumbnail picker' export const WithOffset: ComponentStory = () => (
) WithOffset.storyName = 'With offset' export const CameraFromTheFront: ComponentStory< typeof WearablePreview > = () => (
) CameraFromTheFront.storyName = 'Camera from the front' export const CameraFromTheTop: ComponentStory = () => (
) CameraFromTheTop.storyName = 'Camera from the top' export const DifferentProjections: ComponentStory< typeof WearablePreview > = () => (
) DifferentProjections.storyName = 'Different projections' export const CustomPeerUrl: ComponentStory = () => (
) CustomPeerUrl.storyName = 'Custom peer url' export const WithoutFadeEffect: ComponentStory = () => (
) WithoutFadeEffect.storyName = 'Without fade effect' export const WithoutPanning: ComponentStory = () => (
) WithoutPanning.storyName = 'Without panning' export const WithLockedAlpha: ComponentStory = () => (
) WithLockedAlpha.storyName = 'With locked alpha' export const WithLockedBeta: ComponentStory = () => (
) WithLockedBeta.storyName = 'With locked beta' export const WithLockedRadius: ComponentStory = () => (
) WithLockedRadius.storyName = 'With locked radius' export const UnityModes: ComponentStory = () => { const [unityMode, setUnityMode] = React.useState< 'marketplace' | 'profile' | 'authentication' | 'builder' | 'configurator' >('marketplace') const previewProps = useMemo(() => { switch (unityMode) { case 'marketplace': case 'builder': return { contractAddress: '0xee8ae4c668edd43b34b98934d6d2ff82e41e6488', itemId: '5' } case 'profile': return { profile: getRandomProfile() } case 'authentication': return { profile: getRandomProfile() } case 'configurator': return { username: 'DclUsername' } default: return {} } }, [unityMode]) return (
console.log(`WearablePreview loaded with mode: ${unityMode}`) } onError={(error) => console.error(`WearablePreview error with mode ${unityMode}:`, error) } />
Debug: Check browser console for generated URL
) } UnityModes.storyName = 'Unity modes'