@ericchen1990/pano-view 3.6.7 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/styles.css CHANGED
@@ -217,6 +217,60 @@
217
217
  gap: 0.5rem;
218
218
  }
219
219
 
220
+ .pano-webvr-portrait-prompt {
221
+ align-items: center;
222
+ background: #000;
223
+ color: #fff;
224
+ display: flex;
225
+ flex-direction: column;
226
+ gap: 0.75rem;
227
+ inset: 0;
228
+ justify-content: center;
229
+ padding: 1.5rem;
230
+ pointer-events: auto;
231
+ position: absolute;
232
+ text-align: center;
233
+ z-index: 20;
234
+ }
235
+
236
+ .pano-webvr-portrait-icon {
237
+ display: block;
238
+ flex-shrink: 0;
239
+ }
240
+
241
+ .pano-webvr-portrait-text {
242
+ font-size: 1rem;
243
+ font-weight: 700;
244
+ line-height: 1.35;
245
+ margin: 0;
246
+ max-width: 16rem;
247
+ }
248
+
249
+ .pano-iphone-fullscreen-swipe-prompt {
250
+ align-items: center;
251
+ bottom: calc(1.25rem + var(--pano-safe-bottom));
252
+ display: flex;
253
+ justify-content: center;
254
+ left: 0;
255
+ pointer-events: none;
256
+ position: absolute;
257
+ right: 0;
258
+ z-index: 20;
259
+ }
260
+
261
+ .pano-iphone-fullscreen-swipe-text {
262
+ background: rgba(0, 0, 0, 0.72);
263
+ border-radius: 999px;
264
+ color: #fff;
265
+ font-size: 0.875rem;
266
+ font-weight: 600;
267
+ line-height: 1.35;
268
+ margin: 0;
269
+ max-width: min(22rem, calc(100% - 2rem));
270
+ padding: 0.625rem 1rem;
271
+ text-align: center;
272
+ }
273
+
220
274
  .pano-webvr-hint {
221
275
  bottom: calc(1.25rem + var(--pano-safe-bottom));
222
276
  color: #fff;
@@ -398,3 +452,128 @@
398
452
  min-width: 2.125rem;
399
453
  opacity: 0.85;
400
454
  }
455
+
456
+ .pano-map,
457
+ .pano-map-overlay,
458
+ .pano-map-launcher,
459
+ .pano-map-control,
460
+ .pano-map-spot {
461
+ box-sizing: border-box;
462
+ }
463
+
464
+ .pano-map {
465
+ background: rgba(15, 23, 42, 0.72);
466
+ border: 1px solid rgba(255, 255, 255, 0.18);
467
+ border-radius: 12px;
468
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
469
+ color: #f8fafc;
470
+ overflow: hidden;
471
+ }
472
+
473
+ .pano-map-collapsed {
474
+ align-items: center;
475
+ display: flex;
476
+ justify-content: center;
477
+ min-height: 44px;
478
+ min-width: 44px;
479
+ }
480
+
481
+ .pano-map-chrome {
482
+ position: absolute;
483
+ right: 8px;
484
+ top: 8px;
485
+ z-index: 2;
486
+ }
487
+
488
+ .pano-map-controls {
489
+ display: flex;
490
+ flex-direction: column;
491
+ gap: 4px;
492
+ }
493
+
494
+ .pano-map .pano-map-control,
495
+ .pano-map-overlay .pano-map-control,
496
+ .pano-map-overlay .pano-map-launcher,
497
+ .pano-map-control,
498
+ .pano-map-launcher {
499
+ align-items: center;
500
+ background: rgba(15, 23, 42, 0.92);
501
+ border: 1px solid rgba(255, 255, 255, 0.28);
502
+ border-radius: 8px;
503
+ color: #f8fafc;
504
+ cursor: pointer;
505
+ display: inline-flex;
506
+ font: inherit;
507
+ justify-content: center;
508
+ min-height: 32px;
509
+ min-width: 32px;
510
+ padding: 0 8px;
511
+ }
512
+
513
+ .pano-map .pano-map-control:hover,
514
+ .pano-map-overlay .pano-map-control:hover,
515
+ .pano-map-overlay .pano-map-launcher:hover,
516
+ .pano-map-control:hover,
517
+ .pano-map-launcher:hover {
518
+ background: rgba(30, 41, 59, 0.98);
519
+ }
520
+
521
+ .pano-map-launcher {
522
+ height: 100%;
523
+ width: 100%;
524
+ }
525
+
526
+ .pano-map-spot:focus-visible,
527
+ .pano-map-control:focus-visible,
528
+ .pano-map-launcher:focus-visible,
529
+ .pano-map-dialog-close:focus-visible {
530
+ outline: 2px solid #38bdf8;
531
+ outline-offset: 2px;
532
+ }
533
+
534
+ .pano-map-dialog-backdrop {
535
+ background: rgb(0 0 0 / 65%);
536
+ }
537
+
538
+ .pano-map-dialog {
539
+ background: #161b22;
540
+ border: 1px solid #39424e;
541
+ border-radius: 12px;
542
+ box-shadow: 0 16px 64px rgb(0 0 0 / 45%);
543
+ color: #f4f4f4;
544
+ font: inherit;
545
+ }
546
+
547
+ .pano-map-dialog-header {
548
+ min-height: 56px;
549
+ padding: 16px 56px 12px 16px;
550
+ position: relative;
551
+ }
552
+
553
+ .pano-map-dialog-title {
554
+ font-size: 1.125rem;
555
+ font-weight: 600;
556
+ margin: 0;
557
+ }
558
+
559
+ .pano-map-dialog-close {
560
+ align-items: center;
561
+ background: transparent;
562
+ border: 1px solid currentColor;
563
+ border-radius: 6px;
564
+ color: inherit;
565
+ cursor: pointer;
566
+ display: grid;
567
+ font: inherit;
568
+ height: 36px;
569
+ justify-content: center;
570
+ padding: 0;
571
+ position: absolute;
572
+ right: 12px;
573
+ top: 12px;
574
+ width: 36px;
575
+ }
576
+
577
+ .pano-map-dialog-body {
578
+ padding: 0 16px 16px;
579
+ }
package/dist/ui.cjs CHANGED
@@ -49,7 +49,7 @@ var import_pano_overlay = require("./index.cjs");
49
49
  var import_jsx_runtime = require("react/jsx-runtime");
50
50
  function PanoDialogRoot({ open, onOpenChange, children }) {
51
51
  (0, import_pano_overlay.usePanoModal)(open);
52
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogPrimitive.Root, { open, onOpenChange, children });
52
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogPrimitive.Root, { modal: false, open, onOpenChange, children });
53
53
  }
54
54
  var PanoDialogBackdrop = (0, import_react.forwardRef)(
55
55
  function PanoDialogBackdrop2({ style, className, ...props }, ref) {
package/dist/ui.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ui.ts","../src/ui/dialog.tsx","../src/ui/project-popup-dialog.tsx"],"sourcesContent":["export {\n PanoDialog,\n PanoDialogRoot,\n PanoDialogBackdrop,\n PanoDialogContent,\n PanoDialogTitle,\n PanoDialogDescription,\n PanoDialogClose,\n} from \"./ui/dialog\";\nexport type {\n PanoDialogProps,\n PanoDialogRootProps,\n PanoDialogBackdropProps,\n PanoDialogContentProps,\n PanoDialogTitleProps,\n PanoDialogDescriptionProps,\n PanoDialogCloseProps,\n} from \"./ui/dialog\";\nexport { ProjectPopupDialog } from \"./ui/project-popup-dialog\";\nexport type { ProjectPopupDialogProps } from \"./ui/project-popup-dialog\";\n","import * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { forwardRef, useId, useRef, useState, type ComponentPropsWithoutRef, type CSSProperties, type ReactNode } from \"react\";\nimport { PanoOverlayPortalProvider, usePanoModal, usePanoOverlay } from \"../overlay/pano-overlay\";\n\nexport type PanoDialogRootProps = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n children: ReactNode;\n};\n\nexport function PanoDialogRoot({ open, onOpenChange, children }: PanoDialogRootProps) {\n usePanoModal(open);\n return <DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>{children}</DialogPrimitive.Root>;\n}\n\nexport type PanoDialogBackdropProps = Omit<ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>, \"forceMount\">;\n\nexport const PanoDialogBackdrop = forwardRef<HTMLDivElement, PanoDialogBackdropProps>(\n function PanoDialogBackdrop({ style, className, ...props }, ref) {\n const { portalContainer } = usePanoOverlay();\n if (!portalContainer) return null;\n return (\n <DialogPrimitive.Portal container={portalContainer}>\n <DialogPrimitive.Overlay\n {...props}\n ref={ref}\n data-pano-dialog-backdrop=\"\"\n className={[\"pano-dialog-backdrop\", className].filter(Boolean).join(\" \")}\n style={{ position: \"absolute\", inset: 0, pointerEvents: \"auto\", ...style }}\n />\n </DialogPrimitive.Portal>\n );\n },\n);\n\nexport type PanoDialogContentProps = Omit<ComponentPropsWithoutRef<typeof DialogPrimitive.Content>, \"forceMount\" | \"asChild\">;\n\nexport const PanoDialogContent = forwardRef<HTMLDivElement, PanoDialogContentProps>(\n function PanoDialogContent({ children, style, className, onOpenAutoFocus, onCloseAutoFocus, ...props }, forwardedRef) {\n const { portalContainer, visibleRect } = usePanoOverlay();\n const [nestedContainer, setNestedContainer] = useState<HTMLDivElement | null>(null);\n const returnFocus = useRef<HTMLElement | null>(null);\n if (!portalContainer) return null;\n const layout: CSSProperties & Record<`--pano-dialog-${string}`, string> = {\n \"--pano-dialog-available-width\": `${Math.max(0, visibleRect.width - 32)}px`,\n \"--pano-dialog-available-height\": `${Math.max(0, visibleRect.height - 32)}px`,\n position: \"absolute\",\n top: visibleRect.top + visibleRect.height / 2,\n left: visibleRect.left + visibleRect.width / 2,\n transform: \"translate(-50%, -50%)\",\n width: \"min(var(--pano-dialog-width, 640px), var(--pano-dialog-available-width))\",\n maxHeight: \"var(--pano-dialog-available-height)\",\n boxSizing: \"border-box\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"auto\",\n pointerEvents: \"auto\",\n zIndex: 1,\n ...style,\n };\n return (\n <DialogPrimitive.Portal container={portalContainer}>\n <DialogPrimitive.Content\n {...props}\n ref={forwardedRef}\n data-pano-dialog=\"\"\n className={[\"pano-dialog-content\", className].filter(Boolean).join(\" \")}\n style={layout}\n onOpenAutoFocus={(event) => {\n const active = portalContainer.ownerDocument.activeElement;\n returnFocus.current = active instanceof HTMLElement ? active : null;\n onOpenAutoFocus?.(event);\n }}\n onCloseAutoFocus={(event) => {\n onCloseAutoFocus?.(event);\n if (event.defaultPrevented) return;\n event.preventDefault();\n const previous = returnFocus.current;\n const canvas = portalContainer.closest(\"[data-pano-viewer]\")?.querySelector(\"canvas\");\n const target = previous?.isConnected && previous !== previous.ownerDocument.body && !previous.closest(\"[inert]\")\n ? previous : canvas;\n if (target && !target.closest(\"[inert]\")) target.focus({ preventScroll: true });\n }}\n >\n <PanoOverlayPortalProvider container={nestedContainer}>\n {children}\n </PanoOverlayPortalProvider>\n <div\n data-pano-dialog-portals=\"\"\n ref={setNestedContainer}\n style={{ position: \"absolute\", inset: 0, pointerEvents: \"none\", zIndex: 2 }}\n />\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n );\n },\n);\n\nexport const PanoDialogTitle = DialogPrimitive.Title;\nexport const PanoDialogDescription = DialogPrimitive.Description;\nexport const PanoDialogClose = DialogPrimitive.Close;\nexport type PanoDialogTitleProps = ComponentPropsWithoutRef<typeof PanoDialogTitle>;\nexport type PanoDialogDescriptionProps = ComponentPropsWithoutRef<typeof PanoDialogDescription>;\nexport type PanoDialogCloseProps = ComponentPropsWithoutRef<typeof PanoDialogClose>;\n\nexport type PanoDialogProps = PanoDialogRootProps & {\n title: ReactNode;\n description?: ReactNode;\n closeLabel?: string;\n className?: string;\n style?: CSSProperties;\n};\n\n/** A ready-to-use modal; compose the exported parts for a different layout. */\nexport function PanoDialog({ open, onOpenChange, title, description, closeLabel = \"Close\", children, className, style }: PanoDialogProps) {\n const descriptionId = useId();\n return (\n <PanoDialogRoot open={open} onOpenChange={onOpenChange}>\n <PanoDialogBackdrop />\n <PanoDialogContent\n className={className}\n style={{ overflow: \"hidden\", ...style }}\n aria-describedby={description == null ? undefined : descriptionId}\n >\n <header className=\"pano-dialog-header\" style={{ flexShrink: 0 }}>\n <div>\n <PanoDialogTitle className=\"pano-dialog-title\">{title}</PanoDialogTitle>\n {description == null ? null : (\n <PanoDialogDescription id={descriptionId} className=\"pano-dialog-description\">{description}</PanoDialogDescription>\n )}\n </div>\n </header>\n <PanoDialogClose className=\"pano-dialog-close\" aria-label={closeLabel} type=\"button\">\n <svg aria-hidden=\"true\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\">\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </svg>\n </PanoDialogClose>\n <div className=\"pano-dialog-body\" style={{ overflow: \"auto\", minHeight: 0 }}>{children}</div>\n </PanoDialogContent>\n </PanoDialogRoot>\n );\n}\n","import { useEffect, useRef } from \"react\";\nimport type { ProjectPopupBlock, ProjectPopupDefinition } from \"../data\";\nimport { PanoDialog } from \"./dialog\";\n\nexport type ProjectPopupDialogProps = {\n popup: ProjectPopupDefinition | null;\n onOpenChange: (open: boolean) => void;\n closeLabel?: string;\n onMediaError?: (block: ProjectPopupBlock) => void;\n};\n\nfunction PopupMedia({ block, label, onError }: {\n block: Extract<ProjectPopupBlock, { type: \"audio\" | \"video\" }>;\n label: string;\n onError?: () => void;\n}) {\n const ref = useRef<HTMLMediaElement | null>(null);\n useEffect(() => {\n const media = ref.current;\n // Strict Mode replays cleanup/setup on the same DOM node.\n if (media && media.getAttribute(\"src\") !== block.src) {\n media.src = block.src;\n media.load();\n }\n return () => {\n if (!media) return;\n media.pause();\n media.removeAttribute(\"src\");\n media.load();\n };\n }, [block.src]);\n const props = { controls: true, autoPlay: false, preload: \"metadata\", src: block.src, onError } as const;\n return block.type === \"video\"\n ? <video {...props} ref={(node) => { ref.current = node; }} playsInline poster={block.poster} aria-label={label} />\n : <audio {...props} ref={(node) => { ref.current = node; }} aria-label={label} />;\n}\n\n/** Render in PanoViewer.renderOverlay; the host owns selection and may replace this UI. */\nexport function ProjectPopupDialog({ popup, onOpenChange, closeLabel, onMediaError }: ProjectPopupDialogProps) {\n return <PanoDialog open={popup !== null} onOpenChange={onOpenChange} title={popup?.title || popup?.id || \"\"} closeLabel={closeLabel}>\n {popup ? <div className=\"pano-project-popup\" key={popup.id}>\n {popup.blocks.map((block) => block.type === \"text\"\n ? <p key={block.id} style={{ whiteSpace: \"pre-wrap\", overflowWrap: \"anywhere\" }}>{block.text}</p>\n : block.type === \"image\"\n ? <img key={`${block.id}:${block.src}`} src={block.src} alt={block.alt ?? \"\"} onError={() => onMediaError?.(block)} />\n : <PopupMedia key={`${block.id}:${block.src}`} block={block} label={popup.title} onError={() => onMediaError?.(block)} />)}\n </div> : null}\n </PanoDialog>;\n}\n"],"mappings":"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","names":["PanoDialogBackdrop","PanoDialogContent","import_react","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/ui.ts","../src/ui/dialog.tsx","../src/ui/project-popup-dialog.tsx"],"sourcesContent":["export {\n PanoDialog,\n PanoDialogRoot,\n PanoDialogBackdrop,\n PanoDialogContent,\n PanoDialogTitle,\n PanoDialogDescription,\n PanoDialogClose,\n} from \"./ui/dialog\";\nexport type {\n PanoDialogProps,\n PanoDialogRootProps,\n PanoDialogBackdropProps,\n PanoDialogContentProps,\n PanoDialogTitleProps,\n PanoDialogDescriptionProps,\n PanoDialogCloseProps,\n} from \"./ui/dialog\";\nexport { ProjectPopupDialog } from \"./ui/project-popup-dialog\";\nexport type { ProjectPopupDialogProps } from \"./ui/project-popup-dialog\";\n","import * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { forwardRef, useId, useRef, useState, type ComponentPropsWithoutRef, type CSSProperties, type ReactNode } from \"react\";\nimport { PanoOverlayPortalProvider, usePanoModal, usePanoOverlay } from \"../overlay/pano-overlay\";\n\nexport type PanoDialogRootProps = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n children: ReactNode;\n};\n\nexport function PanoDialogRoot({ open, onOpenChange, children }: PanoDialogRootProps) {\n usePanoModal(open);\n // Keep modality inside the viewer overlay. Radix modal mode would lock page\n // scroll and disable the host document; viewer input is already gated by usePanoModal.\n return (\n <DialogPrimitive.Root modal={false} open={open} onOpenChange={onOpenChange}>\n {children}\n </DialogPrimitive.Root>\n );\n}\n\nexport type PanoDialogBackdropProps = Omit<ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>, \"forceMount\">;\n\nexport const PanoDialogBackdrop = forwardRef<HTMLDivElement, PanoDialogBackdropProps>(\n function PanoDialogBackdrop({ style, className, ...props }, ref) {\n const { portalContainer } = usePanoOverlay();\n if (!portalContainer) return null;\n return (\n <DialogPrimitive.Portal container={portalContainer}>\n <DialogPrimitive.Overlay\n {...props}\n ref={ref}\n data-pano-dialog-backdrop=\"\"\n className={[\"pano-dialog-backdrop\", className].filter(Boolean).join(\" \")}\n style={{ position: \"absolute\", inset: 0, pointerEvents: \"auto\", ...style }}\n />\n </DialogPrimitive.Portal>\n );\n },\n);\n\nexport type PanoDialogContentProps = Omit<ComponentPropsWithoutRef<typeof DialogPrimitive.Content>, \"forceMount\" | \"asChild\">;\n\nexport const PanoDialogContent = forwardRef<HTMLDivElement, PanoDialogContentProps>(\n function PanoDialogContent({ children, style, className, onOpenAutoFocus, onCloseAutoFocus, ...props }, forwardedRef) {\n const { portalContainer, visibleRect } = usePanoOverlay();\n const [nestedContainer, setNestedContainer] = useState<HTMLDivElement | null>(null);\n const returnFocus = useRef<HTMLElement | null>(null);\n if (!portalContainer) return null;\n const layout: CSSProperties & Record<`--pano-dialog-${string}`, string> = {\n \"--pano-dialog-available-width\": `${Math.max(0, visibleRect.width - 32)}px`,\n \"--pano-dialog-available-height\": `${Math.max(0, visibleRect.height - 32)}px`,\n position: \"absolute\",\n top: visibleRect.top + visibleRect.height / 2,\n left: visibleRect.left + visibleRect.width / 2,\n transform: \"translate(-50%, -50%)\",\n width: \"min(var(--pano-dialog-width, 640px), var(--pano-dialog-available-width))\",\n maxHeight: \"var(--pano-dialog-available-height)\",\n boxSizing: \"border-box\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"auto\",\n pointerEvents: \"auto\",\n zIndex: 1,\n ...style,\n };\n return (\n <DialogPrimitive.Portal container={portalContainer}>\n <DialogPrimitive.Content\n {...props}\n ref={forwardedRef}\n data-pano-dialog=\"\"\n className={[\"pano-dialog-content\", className].filter(Boolean).join(\" \")}\n style={layout}\n onOpenAutoFocus={(event) => {\n const active = portalContainer.ownerDocument.activeElement;\n returnFocus.current = active instanceof HTMLElement ? active : null;\n onOpenAutoFocus?.(event);\n }}\n onCloseAutoFocus={(event) => {\n onCloseAutoFocus?.(event);\n if (event.defaultPrevented) return;\n event.preventDefault();\n const previous = returnFocus.current;\n const canvas = portalContainer.closest(\"[data-pano-viewer]\")?.querySelector(\"canvas\");\n const target = previous?.isConnected && previous !== previous.ownerDocument.body && !previous.closest(\"[inert]\")\n ? previous : canvas;\n if (target && !target.closest(\"[inert]\")) target.focus({ preventScroll: true });\n }}\n >\n <PanoOverlayPortalProvider container={nestedContainer}>\n {children}\n </PanoOverlayPortalProvider>\n <div\n data-pano-dialog-portals=\"\"\n ref={setNestedContainer}\n style={{ position: \"absolute\", inset: 0, pointerEvents: \"none\", zIndex: 2 }}\n />\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n );\n },\n);\n\nexport const PanoDialogTitle = DialogPrimitive.Title;\nexport const PanoDialogDescription = DialogPrimitive.Description;\nexport const PanoDialogClose = DialogPrimitive.Close;\nexport type PanoDialogTitleProps = ComponentPropsWithoutRef<typeof PanoDialogTitle>;\nexport type PanoDialogDescriptionProps = ComponentPropsWithoutRef<typeof PanoDialogDescription>;\nexport type PanoDialogCloseProps = ComponentPropsWithoutRef<typeof PanoDialogClose>;\n\nexport type PanoDialogProps = PanoDialogRootProps & {\n title: ReactNode;\n description?: ReactNode;\n closeLabel?: string;\n className?: string;\n style?: CSSProperties;\n};\n\n/** A ready-to-use modal; compose the exported parts for a different layout. */\nexport function PanoDialog({ open, onOpenChange, title, description, closeLabel = \"Close\", children, className, style }: PanoDialogProps) {\n const descriptionId = useId();\n return (\n <PanoDialogRoot open={open} onOpenChange={onOpenChange}>\n <PanoDialogBackdrop />\n <PanoDialogContent\n className={className}\n style={{ overflow: \"hidden\", ...style }}\n aria-describedby={description == null ? undefined : descriptionId}\n >\n <header className=\"pano-dialog-header\" style={{ flexShrink: 0 }}>\n <div>\n <PanoDialogTitle className=\"pano-dialog-title\">{title}</PanoDialogTitle>\n {description == null ? null : (\n <PanoDialogDescription id={descriptionId} className=\"pano-dialog-description\">{description}</PanoDialogDescription>\n )}\n </div>\n </header>\n <PanoDialogClose className=\"pano-dialog-close\" aria-label={closeLabel} type=\"button\">\n <svg aria-hidden=\"true\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\">\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </svg>\n </PanoDialogClose>\n <div className=\"pano-dialog-body\" style={{ overflow: \"auto\", minHeight: 0 }}>{children}</div>\n </PanoDialogContent>\n </PanoDialogRoot>\n );\n}\n","import { useEffect, useRef } from \"react\";\nimport type { ProjectPopupBlock, ProjectPopupDefinition } from \"../data\";\nimport { PanoDialog } from \"./dialog\";\n\nexport type ProjectPopupDialogProps = {\n popup: ProjectPopupDefinition | null;\n onOpenChange: (open: boolean) => void;\n closeLabel?: string;\n onMediaError?: (block: ProjectPopupBlock) => void;\n};\n\nfunction PopupMedia({ block, label, onError }: {\n block: Extract<ProjectPopupBlock, { type: \"audio\" | \"video\" }>;\n label: string;\n onError?: () => void;\n}) {\n const ref = useRef<HTMLMediaElement | null>(null);\n useEffect(() => {\n const media = ref.current;\n // Strict Mode replays cleanup/setup on the same DOM node.\n if (media && media.getAttribute(\"src\") !== block.src) {\n media.src = block.src;\n media.load();\n }\n return () => {\n if (!media) return;\n media.pause();\n media.removeAttribute(\"src\");\n media.load();\n };\n }, [block.src]);\n const props = { controls: true, autoPlay: false, preload: \"metadata\", src: block.src, onError } as const;\n return block.type === \"video\"\n ? <video {...props} ref={(node) => { ref.current = node; }} playsInline poster={block.poster} aria-label={label} />\n : <audio {...props} ref={(node) => { ref.current = node; }} aria-label={label} />;\n}\n\n/** Render in PanoViewer.renderOverlay; the host owns selection and may replace this UI. */\nexport function ProjectPopupDialog({ popup, onOpenChange, closeLabel, onMediaError }: ProjectPopupDialogProps) {\n return <PanoDialog open={popup !== null} onOpenChange={onOpenChange} title={popup?.title || popup?.id || \"\"} closeLabel={closeLabel}>\n {popup ? <div className=\"pano-project-popup\" key={popup.id}>\n {popup.blocks.map((block) => block.type === \"text\"\n ? <p key={block.id} style={{ whiteSpace: \"pre-wrap\", overflowWrap: \"anywhere\" }}>{block.text}</p>\n : block.type === \"image\"\n ? <img key={`${block.id}:${block.src}`} src={block.src} alt={block.alt ?? \"\"} onError={() => onMediaError?.(block)} />\n : <PopupMedia key={`${block.id}:${block.src}`} block={block} label={popup.title} onError={() => onMediaError?.(block)} />)}\n </div> : null}\n </PanoDialog>;\n}\n"],"mappings":"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","names":["PanoDialogBackdrop","PanoDialogContent","import_react","import_jsx_runtime"]}
package/dist/ui.mjs CHANGED
@@ -7,7 +7,7 @@ import { PanoOverlayPortalProvider, usePanoModal, usePanoOverlay } from "./index
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  function PanoDialogRoot({ open, onOpenChange, children }) {
9
9
  usePanoModal(open);
10
- return /* @__PURE__ */ jsx(DialogPrimitive.Root, { open, onOpenChange, children });
10
+ return /* @__PURE__ */ jsx(DialogPrimitive.Root, { modal: false, open, onOpenChange, children });
11
11
  }
12
12
  var PanoDialogBackdrop = forwardRef(
13
13
  function PanoDialogBackdrop2({ style, className, ...props }, ref) {
package/dist/ui.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ui/dialog.tsx","../src/ui/project-popup-dialog.tsx"],"sourcesContent":["import * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { forwardRef, useId, useRef, useState, type ComponentPropsWithoutRef, type CSSProperties, type ReactNode } from \"react\";\nimport { PanoOverlayPortalProvider, usePanoModal, usePanoOverlay } from \"../overlay/pano-overlay\";\n\nexport type PanoDialogRootProps = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n children: ReactNode;\n};\n\nexport function PanoDialogRoot({ open, onOpenChange, children }: PanoDialogRootProps) {\n usePanoModal(open);\n return <DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>{children}</DialogPrimitive.Root>;\n}\n\nexport type PanoDialogBackdropProps = Omit<ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>, \"forceMount\">;\n\nexport const PanoDialogBackdrop = forwardRef<HTMLDivElement, PanoDialogBackdropProps>(\n function PanoDialogBackdrop({ style, className, ...props }, ref) {\n const { portalContainer } = usePanoOverlay();\n if (!portalContainer) return null;\n return (\n <DialogPrimitive.Portal container={portalContainer}>\n <DialogPrimitive.Overlay\n {...props}\n ref={ref}\n data-pano-dialog-backdrop=\"\"\n className={[\"pano-dialog-backdrop\", className].filter(Boolean).join(\" \")}\n style={{ position: \"absolute\", inset: 0, pointerEvents: \"auto\", ...style }}\n />\n </DialogPrimitive.Portal>\n );\n },\n);\n\nexport type PanoDialogContentProps = Omit<ComponentPropsWithoutRef<typeof DialogPrimitive.Content>, \"forceMount\" | \"asChild\">;\n\nexport const PanoDialogContent = forwardRef<HTMLDivElement, PanoDialogContentProps>(\n function PanoDialogContent({ children, style, className, onOpenAutoFocus, onCloseAutoFocus, ...props }, forwardedRef) {\n const { portalContainer, visibleRect } = usePanoOverlay();\n const [nestedContainer, setNestedContainer] = useState<HTMLDivElement | null>(null);\n const returnFocus = useRef<HTMLElement | null>(null);\n if (!portalContainer) return null;\n const layout: CSSProperties & Record<`--pano-dialog-${string}`, string> = {\n \"--pano-dialog-available-width\": `${Math.max(0, visibleRect.width - 32)}px`,\n \"--pano-dialog-available-height\": `${Math.max(0, visibleRect.height - 32)}px`,\n position: \"absolute\",\n top: visibleRect.top + visibleRect.height / 2,\n left: visibleRect.left + visibleRect.width / 2,\n transform: \"translate(-50%, -50%)\",\n width: \"min(var(--pano-dialog-width, 640px), var(--pano-dialog-available-width))\",\n maxHeight: \"var(--pano-dialog-available-height)\",\n boxSizing: \"border-box\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"auto\",\n pointerEvents: \"auto\",\n zIndex: 1,\n ...style,\n };\n return (\n <DialogPrimitive.Portal container={portalContainer}>\n <DialogPrimitive.Content\n {...props}\n ref={forwardedRef}\n data-pano-dialog=\"\"\n className={[\"pano-dialog-content\", className].filter(Boolean).join(\" \")}\n style={layout}\n onOpenAutoFocus={(event) => {\n const active = portalContainer.ownerDocument.activeElement;\n returnFocus.current = active instanceof HTMLElement ? active : null;\n onOpenAutoFocus?.(event);\n }}\n onCloseAutoFocus={(event) => {\n onCloseAutoFocus?.(event);\n if (event.defaultPrevented) return;\n event.preventDefault();\n const previous = returnFocus.current;\n const canvas = portalContainer.closest(\"[data-pano-viewer]\")?.querySelector(\"canvas\");\n const target = previous?.isConnected && previous !== previous.ownerDocument.body && !previous.closest(\"[inert]\")\n ? previous : canvas;\n if (target && !target.closest(\"[inert]\")) target.focus({ preventScroll: true });\n }}\n >\n <PanoOverlayPortalProvider container={nestedContainer}>\n {children}\n </PanoOverlayPortalProvider>\n <div\n data-pano-dialog-portals=\"\"\n ref={setNestedContainer}\n style={{ position: \"absolute\", inset: 0, pointerEvents: \"none\", zIndex: 2 }}\n />\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n );\n },\n);\n\nexport const PanoDialogTitle = DialogPrimitive.Title;\nexport const PanoDialogDescription = DialogPrimitive.Description;\nexport const PanoDialogClose = DialogPrimitive.Close;\nexport type PanoDialogTitleProps = ComponentPropsWithoutRef<typeof PanoDialogTitle>;\nexport type PanoDialogDescriptionProps = ComponentPropsWithoutRef<typeof PanoDialogDescription>;\nexport type PanoDialogCloseProps = ComponentPropsWithoutRef<typeof PanoDialogClose>;\n\nexport type PanoDialogProps = PanoDialogRootProps & {\n title: ReactNode;\n description?: ReactNode;\n closeLabel?: string;\n className?: string;\n style?: CSSProperties;\n};\n\n/** A ready-to-use modal; compose the exported parts for a different layout. */\nexport function PanoDialog({ open, onOpenChange, title, description, closeLabel = \"Close\", children, className, style }: PanoDialogProps) {\n const descriptionId = useId();\n return (\n <PanoDialogRoot open={open} onOpenChange={onOpenChange}>\n <PanoDialogBackdrop />\n <PanoDialogContent\n className={className}\n style={{ overflow: \"hidden\", ...style }}\n aria-describedby={description == null ? undefined : descriptionId}\n >\n <header className=\"pano-dialog-header\" style={{ flexShrink: 0 }}>\n <div>\n <PanoDialogTitle className=\"pano-dialog-title\">{title}</PanoDialogTitle>\n {description == null ? null : (\n <PanoDialogDescription id={descriptionId} className=\"pano-dialog-description\">{description}</PanoDialogDescription>\n )}\n </div>\n </header>\n <PanoDialogClose className=\"pano-dialog-close\" aria-label={closeLabel} type=\"button\">\n <svg aria-hidden=\"true\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\">\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </svg>\n </PanoDialogClose>\n <div className=\"pano-dialog-body\" style={{ overflow: \"auto\", minHeight: 0 }}>{children}</div>\n </PanoDialogContent>\n </PanoDialogRoot>\n );\n}\n","import { useEffect, useRef } from \"react\";\nimport type { ProjectPopupBlock, ProjectPopupDefinition } from \"../data\";\nimport { PanoDialog } from \"./dialog\";\n\nexport type ProjectPopupDialogProps = {\n popup: ProjectPopupDefinition | null;\n onOpenChange: (open: boolean) => void;\n closeLabel?: string;\n onMediaError?: (block: ProjectPopupBlock) => void;\n};\n\nfunction PopupMedia({ block, label, onError }: {\n block: Extract<ProjectPopupBlock, { type: \"audio\" | \"video\" }>;\n label: string;\n onError?: () => void;\n}) {\n const ref = useRef<HTMLMediaElement | null>(null);\n useEffect(() => {\n const media = ref.current;\n // Strict Mode replays cleanup/setup on the same DOM node.\n if (media && media.getAttribute(\"src\") !== block.src) {\n media.src = block.src;\n media.load();\n }\n return () => {\n if (!media) return;\n media.pause();\n media.removeAttribute(\"src\");\n media.load();\n };\n }, [block.src]);\n const props = { controls: true, autoPlay: false, preload: \"metadata\", src: block.src, onError } as const;\n return block.type === \"video\"\n ? <video {...props} ref={(node) => { ref.current = node; }} playsInline poster={block.poster} aria-label={label} />\n : <audio {...props} ref={(node) => { ref.current = node; }} aria-label={label} />;\n}\n\n/** Render in PanoViewer.renderOverlay; the host owns selection and may replace this UI. */\nexport function ProjectPopupDialog({ popup, onOpenChange, closeLabel, onMediaError }: ProjectPopupDialogProps) {\n return <PanoDialog open={popup !== null} onOpenChange={onOpenChange} title={popup?.title || popup?.id || \"\"} closeLabel={closeLabel}>\n {popup ? <div className=\"pano-project-popup\" key={popup.id}>\n {popup.blocks.map((block) => block.type === \"text\"\n ? <p key={block.id} style={{ whiteSpace: \"pre-wrap\", overflowWrap: \"anywhere\" }}>{block.text}</p>\n : block.type === \"image\"\n ? <img key={`${block.id}:${block.src}`} src={block.src} alt={block.alt ?? \"\"} onError={() => onMediaError?.(block)} />\n : <PopupMedia key={`${block.id}:${block.src}`} block={block} label={popup.title} onError={() => onMediaError?.(block)} />)}\n </div> : null}\n </PanoDialog>;\n}\n"],"mappings":"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","names":["PanoDialogBackdrop","PanoDialogContent","useRef","jsx","useRef"]}
1
+ {"version":3,"sources":["../src/ui/dialog.tsx","../src/ui/project-popup-dialog.tsx"],"sourcesContent":["import * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { forwardRef, useId, useRef, useState, type ComponentPropsWithoutRef, type CSSProperties, type ReactNode } from \"react\";\nimport { PanoOverlayPortalProvider, usePanoModal, usePanoOverlay } from \"../overlay/pano-overlay\";\n\nexport type PanoDialogRootProps = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n children: ReactNode;\n};\n\nexport function PanoDialogRoot({ open, onOpenChange, children }: PanoDialogRootProps) {\n usePanoModal(open);\n // Keep modality inside the viewer overlay. Radix modal mode would lock page\n // scroll and disable the host document; viewer input is already gated by usePanoModal.\n return (\n <DialogPrimitive.Root modal={false} open={open} onOpenChange={onOpenChange}>\n {children}\n </DialogPrimitive.Root>\n );\n}\n\nexport type PanoDialogBackdropProps = Omit<ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>, \"forceMount\">;\n\nexport const PanoDialogBackdrop = forwardRef<HTMLDivElement, PanoDialogBackdropProps>(\n function PanoDialogBackdrop({ style, className, ...props }, ref) {\n const { portalContainer } = usePanoOverlay();\n if (!portalContainer) return null;\n return (\n <DialogPrimitive.Portal container={portalContainer}>\n <DialogPrimitive.Overlay\n {...props}\n ref={ref}\n data-pano-dialog-backdrop=\"\"\n className={[\"pano-dialog-backdrop\", className].filter(Boolean).join(\" \")}\n style={{ position: \"absolute\", inset: 0, pointerEvents: \"auto\", ...style }}\n />\n </DialogPrimitive.Portal>\n );\n },\n);\n\nexport type PanoDialogContentProps = Omit<ComponentPropsWithoutRef<typeof DialogPrimitive.Content>, \"forceMount\" | \"asChild\">;\n\nexport const PanoDialogContent = forwardRef<HTMLDivElement, PanoDialogContentProps>(\n function PanoDialogContent({ children, style, className, onOpenAutoFocus, onCloseAutoFocus, ...props }, forwardedRef) {\n const { portalContainer, visibleRect } = usePanoOverlay();\n const [nestedContainer, setNestedContainer] = useState<HTMLDivElement | null>(null);\n const returnFocus = useRef<HTMLElement | null>(null);\n if (!portalContainer) return null;\n const layout: CSSProperties & Record<`--pano-dialog-${string}`, string> = {\n \"--pano-dialog-available-width\": `${Math.max(0, visibleRect.width - 32)}px`,\n \"--pano-dialog-available-height\": `${Math.max(0, visibleRect.height - 32)}px`,\n position: \"absolute\",\n top: visibleRect.top + visibleRect.height / 2,\n left: visibleRect.left + visibleRect.width / 2,\n transform: \"translate(-50%, -50%)\",\n width: \"min(var(--pano-dialog-width, 640px), var(--pano-dialog-available-width))\",\n maxHeight: \"var(--pano-dialog-available-height)\",\n boxSizing: \"border-box\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"auto\",\n pointerEvents: \"auto\",\n zIndex: 1,\n ...style,\n };\n return (\n <DialogPrimitive.Portal container={portalContainer}>\n <DialogPrimitive.Content\n {...props}\n ref={forwardedRef}\n data-pano-dialog=\"\"\n className={[\"pano-dialog-content\", className].filter(Boolean).join(\" \")}\n style={layout}\n onOpenAutoFocus={(event) => {\n const active = portalContainer.ownerDocument.activeElement;\n returnFocus.current = active instanceof HTMLElement ? active : null;\n onOpenAutoFocus?.(event);\n }}\n onCloseAutoFocus={(event) => {\n onCloseAutoFocus?.(event);\n if (event.defaultPrevented) return;\n event.preventDefault();\n const previous = returnFocus.current;\n const canvas = portalContainer.closest(\"[data-pano-viewer]\")?.querySelector(\"canvas\");\n const target = previous?.isConnected && previous !== previous.ownerDocument.body && !previous.closest(\"[inert]\")\n ? previous : canvas;\n if (target && !target.closest(\"[inert]\")) target.focus({ preventScroll: true });\n }}\n >\n <PanoOverlayPortalProvider container={nestedContainer}>\n {children}\n </PanoOverlayPortalProvider>\n <div\n data-pano-dialog-portals=\"\"\n ref={setNestedContainer}\n style={{ position: \"absolute\", inset: 0, pointerEvents: \"none\", zIndex: 2 }}\n />\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n );\n },\n);\n\nexport const PanoDialogTitle = DialogPrimitive.Title;\nexport const PanoDialogDescription = DialogPrimitive.Description;\nexport const PanoDialogClose = DialogPrimitive.Close;\nexport type PanoDialogTitleProps = ComponentPropsWithoutRef<typeof PanoDialogTitle>;\nexport type PanoDialogDescriptionProps = ComponentPropsWithoutRef<typeof PanoDialogDescription>;\nexport type PanoDialogCloseProps = ComponentPropsWithoutRef<typeof PanoDialogClose>;\n\nexport type PanoDialogProps = PanoDialogRootProps & {\n title: ReactNode;\n description?: ReactNode;\n closeLabel?: string;\n className?: string;\n style?: CSSProperties;\n};\n\n/** A ready-to-use modal; compose the exported parts for a different layout. */\nexport function PanoDialog({ open, onOpenChange, title, description, closeLabel = \"Close\", children, className, style }: PanoDialogProps) {\n const descriptionId = useId();\n return (\n <PanoDialogRoot open={open} onOpenChange={onOpenChange}>\n <PanoDialogBackdrop />\n <PanoDialogContent\n className={className}\n style={{ overflow: \"hidden\", ...style }}\n aria-describedby={description == null ? undefined : descriptionId}\n >\n <header className=\"pano-dialog-header\" style={{ flexShrink: 0 }}>\n <div>\n <PanoDialogTitle className=\"pano-dialog-title\">{title}</PanoDialogTitle>\n {description == null ? null : (\n <PanoDialogDescription id={descriptionId} className=\"pano-dialog-description\">{description}</PanoDialogDescription>\n )}\n </div>\n </header>\n <PanoDialogClose className=\"pano-dialog-close\" aria-label={closeLabel} type=\"button\">\n <svg aria-hidden=\"true\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\">\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </svg>\n </PanoDialogClose>\n <div className=\"pano-dialog-body\" style={{ overflow: \"auto\", minHeight: 0 }}>{children}</div>\n </PanoDialogContent>\n </PanoDialogRoot>\n );\n}\n","import { useEffect, useRef } from \"react\";\nimport type { ProjectPopupBlock, ProjectPopupDefinition } from \"../data\";\nimport { PanoDialog } from \"./dialog\";\n\nexport type ProjectPopupDialogProps = {\n popup: ProjectPopupDefinition | null;\n onOpenChange: (open: boolean) => void;\n closeLabel?: string;\n onMediaError?: (block: ProjectPopupBlock) => void;\n};\n\nfunction PopupMedia({ block, label, onError }: {\n block: Extract<ProjectPopupBlock, { type: \"audio\" | \"video\" }>;\n label: string;\n onError?: () => void;\n}) {\n const ref = useRef<HTMLMediaElement | null>(null);\n useEffect(() => {\n const media = ref.current;\n // Strict Mode replays cleanup/setup on the same DOM node.\n if (media && media.getAttribute(\"src\") !== block.src) {\n media.src = block.src;\n media.load();\n }\n return () => {\n if (!media) return;\n media.pause();\n media.removeAttribute(\"src\");\n media.load();\n };\n }, [block.src]);\n const props = { controls: true, autoPlay: false, preload: \"metadata\", src: block.src, onError } as const;\n return block.type === \"video\"\n ? <video {...props} ref={(node) => { ref.current = node; }} playsInline poster={block.poster} aria-label={label} />\n : <audio {...props} ref={(node) => { ref.current = node; }} aria-label={label} />;\n}\n\n/** Render in PanoViewer.renderOverlay; the host owns selection and may replace this UI. */\nexport function ProjectPopupDialog({ popup, onOpenChange, closeLabel, onMediaError }: ProjectPopupDialogProps) {\n return <PanoDialog open={popup !== null} onOpenChange={onOpenChange} title={popup?.title || popup?.id || \"\"} closeLabel={closeLabel}>\n {popup ? <div className=\"pano-project-popup\" key={popup.id}>\n {popup.blocks.map((block) => block.type === \"text\"\n ? <p key={block.id} style={{ whiteSpace: \"pre-wrap\", overflowWrap: \"anywhere\" }}>{block.text}</p>\n : block.type === \"image\"\n ? <img key={`${block.id}:${block.src}`} src={block.src} alt={block.alt ?? \"\"} onError={() => onMediaError?.(block)} />\n : <PopupMedia key={`${block.id}:${block.src}`} block={block} label={popup.title} onError={() => onMediaError?.(block)} />)}\n </div> : null}\n </PanoDialog>;\n}\n"],"mappings":"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","names":["PanoDialogBackdrop","PanoDialogContent","useRef","jsx","useRef"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ericchen1990/pano-view",
3
- "version": "3.6.7",
3
+ "version": "3.8.0",
4
4
  "description": "React components for panoramic viewing experiences.",
5
5
  "author": "Eric Chen",
6
6
  "license": "MIT",