{"version":3,"file":"ModelViewer.mjs","names":[],"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-04/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 * @publicDocs\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"],"mappings":";;;;;;;;;;;;;AAgEA,SAAgB,YAAY,OAA6C;CACvE,MAAM,CAAC,aAAa,kBAAkB,SACpC,KAAA,EACD;CACD,MAAM,cAAc,aAAa,SAAsB;AACrD,iBAAe,KAAK;IACnB,EAAE,CAAC;CACN,MAAM,EAAC,MAAM,UAAU,WAAW,GAAG,qBAAoB;CAEzD,MAAM,0BAA0B,cAC9B,2EACA,EACE,QAAQ,MACT,CACF;AAED,iBAAgB;EACd,MAAM,wBAAwB;GAC5B,OAAO,iBAAiB;GACxB,MAAM,iBAAiB;GACvB,SAAS,iBAAiB;GAC1B,oBAAoB,iBAAiB;GACrC,UAAU,iBAAiB;GAC3B,aAAa,iBAAiB;GAC9B,eAAe,iBAAiB;GAChC,4BAA4B,iBAAiB;GAC7C,iBAAiB,iBAAiB;GAClC,sBAAsB,iBAAiB;GACvC,MAAM,iBAAiB;GACvB,OAAO,iBAAiB;GACxB,qBAAqB,iBAAiB;GACvC;AAED,MAAI,CAAC,YACH;AAEF,SAAO,QAAQ,sBAAsB,CAAC,SACnC,CAAC,WAAW,kBAAkB;AAC7B,OAAI,aACF,aAAY,iBAAiB,WAAW,aAAa;IAG1D;AAED,eAAmB;AACjB,OAAI,eAAe,KACjB;AAEF,UAAO,QAAQ,sBAAsB,CAAC,SACnC,CAAC,WAAW,kBAAkB;AAC7B,QAAI,aACF,aAAY,oBAAoB,WAAW,aAAa;KAG7D;;IAEF;EACD;EACA,iBAAiB;EACjB,iBAAiB;EACjB,iBAAiB;EACjB,iBAAiB;EACjB,iBAAiB;EACjB,iBAAiB;EACjB,iBAAiB;EACjB,iBAAiB;EACjB,iBAAiB;EACjB,iBAAiB;EACjB,iBAAiB;EACjB,iBAAiB;EACjB,iBAAiB;EAClB,CAAC;AAEF,KAAI,4BAA4B,OAE9B,QAAO;AAGT,KAAI,CAAC,KAAK,UAAU,IAAI,KAAK;AAKzB,UAAQ,MAJc,+HAIQ;AAC9B,SAAO;;AAUX,QACE,oBAAC,gBAAD;EACE,KAAK;EACL,GAAI;EAGJ,OAAO;EACP,IAAI,iBAAiB,MAAM,KAAK;EAChC,KAAK,KAAK,QAAQ,GAAG;EACrB,KAAK,KAAK,OAAO;EACjB,mBAAiB,iBAAiB,kBAAkB;EACpD,SAAS,iBAAiB,UAAU,KAAK,cAAc,QAAQ;EAC/D,UAAU,iBAAiB,YAAY;EACvC,SAAS,iBAAiB;EAC1B,QAAQ,iBAAiB;EACzB,IAAI,iBAAiB;EACrB,YAAU,iBAAiB;EAC3B,YAAU,iBAAiB;EAE3B,gBAAc,iBAAiB;EAC/B,WAAS,iBAAiB;EAC1B,gBAAc,iBAAiB;EAC/B,gBAAc,iBAAiB;EAC/B,qBAAmB,iBAAiB;EACpC,eAAa,iBAAiB;EAC9B,qBAAmB,iBAAiB;EAGpC,uBAAqB,iBAAiB;EAEtC,sBAAqB,iBAAyB;EAC9C,sBAAoB,iBAAiB;EACrC,4BAA0B,iBAAiB;EAC3C,gCAA8B,iBAAiB;EAC/C,gBAAc,iBAAiB;EAC/B,iBAAe,iBAAiB;EAChC,iBAAe,iBAAiB;EAChC,oBAAkB,iBAAiB;EACnC,oBAAkB,iBAAiB;EACnC,qBAAmB,iBAAiB;EACpC,qBAAmB,iBAAiB;EAEpC,QAAS,iBAAyB;EAClC,uBAAqB,iBAAiB,sBAAsB;EAC5D,gBAAc,iBAAiB;EAC/B,qBAAmB,iBAAiB;EACpC,UAAU,iBAAiB;EAC3B,oBAAkB,iBAAiB,mBAAmB;EACtD,mBAAiB,iBAAiB,kBAAkB;EACpD,kBAAgB,iBAAiB;EACjC,gCAA8B,iBAAiB;EAC/C,gBAAc,iBAAiB;EAC/B,aAAa,iBAAiB;EAC9B,OAAO,iBAAiB;EAEvB;EACY,CAAA"}