{"version":3,"file":"ModelViewer.mjs","sources":["../../src/ModelViewer.tsx"],"sourcesContent":["import {useState, useEffect, useCallback} from 'react';\nimport {useLoadScript} from './load-script.js';\nimport type {Model3d} from './storefront-api-types.js';\nimport type {PartialDeep} from 'type-fest';\nimport type {ModelViewerElement} from '@google/model-viewer/lib/model-viewer.js';\n\ndeclare global {\n  // eslint-disable-next-line @typescript-eslint/no-namespace\n  namespace JSX {\n    interface IntrinsicElements {\n      'model-viewer': PartialDeep<\n        ModelViewerElement,\n        {recurseIntoArrays: true}\n      >;\n    }\n  }\n}\n\ntype ModelViewerProps = Omit<\n  PartialDeep<JSX.IntrinsicElements['model-viewer'], {recurseIntoArrays: true}>,\n  'src'\n> &\n  ModelViewerBaseProps;\n\ntype ModelViewerBaseProps = {\n  /** An object with fields that correspond to the Storefront API's [Model3D object](https://shopify.dev/api/storefront/2026-01/objects/model3d). */\n  data: PartialDeep<Model3d, {recurseIntoArrays: true}>;\n  /** The callback to invoke when the 'error' event is triggered. Refer to [error in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-loading-events-error). */\n  onError?: (event: Event) => void;\n  /** The callback to invoke when the `load` event is triggered. Refer to [load in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-loading-events-load). */\n  onLoad?: (event: Event) => void;\n  /** The callback to invoke when the 'preload' event is triggered. Refer to [preload in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-loading-events-preload). */\n  onPreload?: (event: Event) => void;\n  /** The callback to invoke when the 'model-visibility' event is triggered. Refer to [model-visibility in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-loading-events-modelVisibility). */\n  onModelVisibility?: (event: Event) => void;\n  /** The callback to invoke when the 'progress' event is triggered. Refer to [progress in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-loading-events-progress). */\n  onProgress?: (event: Event) => void;\n  /** The callback to invoke when the 'ar-status' event is triggered. Refer to [ar-status in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-augmentedreality-events-arStatus). */\n  onArStatus?: (event: Event) => void;\n  /** The callback to invoke when the 'ar-tracking' event is triggered. Refer to [ar-tracking in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-augmentedreality-events-arTracking). */\n  onArTracking?: (event: Event) => void;\n  /** The callback to invoke when the 'quick-look-button-tapped' event is triggered. Refer to [quick-look-button-tapped in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-augmentedreality-events-quickLookButtonTapped). */\n  onQuickLookButtonTapped?: (event: Event) => void;\n  /** The callback to invoke when the 'camera-change' event is triggered. Refer to [camera-change in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-stagingandcameras-events-cameraChange). */\n  onCameraChange?: (event: Event) => void;\n  /** The callback to invoke when the 'environment-change' event is triggered. Refer to [environment-change in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-lightingandenv-events-environmentChange).  */\n  onEnvironmentChange?: (event: Event) => void;\n  /**  The callback to invoke when the 'play' event is triggered. Refer to [play in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-animation-events-play). */\n  onPlay?: (event: Event) => void;\n  /**  The callback to invoke when the 'pause' event is triggered. Refer to [pause in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-animation-events-pause). */\n  onPause?: (event: Event) => void;\n  /** The callback to invoke when the 'scene-graph-ready' event is triggered. Refer to [scene-graph-ready in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-scenegraph-events-sceneGraphReady). */\n  onSceneGraphReady?: (event: Event) => void;\n};\n\n/**\n * The `ModelViewer` component renders a 3D model (with the `model-viewer` custom element) for\n * the Storefront API's [Model3d object](https://shopify.dev/api/storefront/reference/products/model3d).\n *\n * The `model-viewer` custom element is lazily downloaded through a dynamically-injected `<script type=\"module\">` tag when the `<ModelViewer />` component is rendered\n *\n * ModelViewer is using version `1.21.1` of the `@google/model-viewer` library.\n */\nexport function ModelViewer(props: ModelViewerProps): JSX.Element | null {\n  const [modelViewer, setModelViewer] = useState<undefined | HTMLElement>(\n    undefined,\n  );\n  const callbackRef = useCallback((node: HTMLElement) => {\n    setModelViewer(node);\n  }, []);\n  const {data, children, className, ...passthroughProps} = props;\n\n  const modelViewerLoadedStatus = useLoadScript(\n    'https://unpkg.com/@google/model-viewer@v1.12.1/dist/model-viewer.min.js',\n    {\n      module: true,\n    },\n  );\n\n  useEffect(() => {\n    const hydrogenEventListener = {\n      error: passthroughProps.onError,\n      load: passthroughProps.onLoad,\n      preload: passthroughProps.onPreload,\n      'model-visibility': passthroughProps.onModelVisibility,\n      progress: passthroughProps.onProgress,\n      'ar-status': passthroughProps.onArStatus,\n      'ar-tracking': passthroughProps.onArTracking,\n      'quick-look-button-tapped': passthroughProps.onQuickLookButtonTapped,\n      'camera-change': passthroughProps.onCameraChange,\n      'environment-change': passthroughProps.onEnvironmentChange,\n      play: passthroughProps.onPlay,\n      pause: passthroughProps.onPause,\n      'scene-graph-ready': passthroughProps.onSceneGraphReady,\n    };\n\n    if (!modelViewer) {\n      return;\n    }\n    Object.entries(hydrogenEventListener).forEach(\n      ([eventName, callbackFunc]) => {\n        if (callbackFunc) {\n          modelViewer.addEventListener(eventName, callbackFunc);\n        }\n      },\n    );\n\n    return (): void => {\n      if (modelViewer == null) {\n        return;\n      }\n      Object.entries(hydrogenEventListener).forEach(\n        ([eventName, callbackFunc]) => {\n          if (callbackFunc) {\n            modelViewer.removeEventListener(eventName, callbackFunc);\n          }\n        },\n      );\n    };\n  }, [\n    modelViewer,\n    passthroughProps.onArStatus,\n    passthroughProps.onArTracking,\n    passthroughProps.onCameraChange,\n    passthroughProps.onEnvironmentChange,\n    passthroughProps.onError,\n    passthroughProps.onLoad,\n    passthroughProps.onModelVisibility,\n    passthroughProps.onPause,\n    passthroughProps.onPlay,\n    passthroughProps.onPreload,\n    passthroughProps.onProgress,\n    passthroughProps.onQuickLookButtonTapped,\n    passthroughProps.onSceneGraphReady,\n  ]);\n\n  if (modelViewerLoadedStatus !== 'done') {\n    // TODO: What do we want to display while the model-viewer library loads?\n    return null;\n  }\n\n  if (!data.sources?.[0]?.url) {\n    const sourcesUrlError = `<ModelViewer/> requires 'data.sources' prop to be an array, with an object that has a property 'url' on it. Rendering 'null'`;\n    if (__HYDROGEN_DEV__) {\n      throw new Error(sourcesUrlError);\n    } else {\n      console.error(sourcesUrlError);\n      return null;\n    }\n  }\n\n  if (__HYDROGEN_DEV__ && !data.alt) {\n    console.warn(\n      `<ModelViewer/> requires the 'data.alt' prop for accessibility`,\n    );\n  }\n\n  return (\n    <model-viewer\n      ref={callbackRef}\n      {...passthroughProps}\n      // @ts-expect-error src should exist\n      // @eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n      class={className}\n      id={passthroughProps.id ?? data.id}\n      src={data.sources[0].url}\n      alt={data.alt ?? null}\n      camera-controls={passthroughProps.cameraControls ?? true}\n      poster={(passthroughProps.poster || data.previewImage?.url) ?? null}\n      autoplay={passthroughProps.autoplay ?? true}\n      loading={passthroughProps.loading}\n      reveal={passthroughProps.reveal}\n      ar={passthroughProps.ar}\n      ar-modes={passthroughProps.arModes}\n      ar-scale={passthroughProps.arScale}\n      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n      ar-placement={passthroughProps.arPlacement}\n      ios-src={passthroughProps.iosSrc}\n      touch-action={passthroughProps.touchAction}\n      disable-zoom={passthroughProps.disableZoom}\n      orbit-sensitivity={passthroughProps.orbitSensitivity}\n      auto-rotate={passthroughProps.autoRotate}\n      auto-rotate-delay={passthroughProps.autoRotateDelay}\n      // @ts-expect-error rotationPerSecond should exist as a type, not sure why it doesn't. https://modelviewer.dev/docs/index.html#entrydocs-stagingandcameras-attributes-rotationPerSecond\n      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n      rotation-per-second={passthroughProps.rotationPerSecond}\n      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n      interaction-policy={(passthroughProps as any).interactionPolicy}\n      interaction-prompt={passthroughProps.interactionPrompt}\n      interaction-prompt-style={passthroughProps.interactionPromptStyle}\n      interaction-prompt-threshold={passthroughProps.interactionPromptThreshold}\n      camera-orbit={passthroughProps.cameraOrbit}\n      camera-target={passthroughProps.cameraTarget}\n      field-of-view={passthroughProps.fieldOfView}\n      max-camera-orbit={passthroughProps.maxCameraOrbit}\n      min-camera-orbit={passthroughProps.minCameraOrbit}\n      max-field-of-view={passthroughProps.maxFieldOfView}\n      min-field-of-view={passthroughProps.minFieldOfView}\n      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n      bounds={(passthroughProps as any).bounds}\n      interpolation-decay={passthroughProps.interpolationDecay ?? 100}\n      skybox-image={passthroughProps.skyboxImage}\n      environment-image={passthroughProps.environmentImage}\n      exposure={passthroughProps.exposure}\n      shadow-intensity={passthroughProps.shadowIntensity ?? 0}\n      shadow-softness={passthroughProps.shadowSoftness ?? 0}\n      animation-name={passthroughProps.animationName}\n      animation-crossfade-duration={passthroughProps.animationCrossfadeDuration}\n      variant-name={passthroughProps.variantName}\n      orientation={passthroughProps.orientation}\n      scale={passthroughProps.scale}\n    >\n      {children}\n    </model-viewer>\n  );\n}\n"],"names":[],"mappings":"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"}