@opensite/ui 3.11.0 → 3.12.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.
@@ -402,6 +402,39 @@ var Section = React__default.forwardRef(
402
402
  Section.displayName = "Section";
403
403
  var IG_TILE_WIDTH = 320;
404
404
  var IG_TILE_STYLE = { width: "100%" };
405
+ var IG_TILE_RADIUS = 18;
406
+ var TILE_WRAPPER_STYLE = {
407
+ position: "relative",
408
+ borderRadius: IG_TILE_RADIUS,
409
+ boxShadow: "0 1px 2px rgba(15, 23, 42, 0.08), 0 6px 16px rgba(15, 23, 42, 0.10)"
410
+ };
411
+ var CAPTION_SCRIM_STYLE = {
412
+ position: "absolute",
413
+ left: 0,
414
+ right: 0,
415
+ bottom: 0,
416
+ padding: "28px 14px 13px",
417
+ background: "linear-gradient(180deg, rgba(8,12,24,0) 0%, rgba(8,12,24,0.55) 45%, rgba(8,12,24,0.78) 100%)",
418
+ borderBottomLeftRadius: IG_TILE_RADIUS,
419
+ borderBottomRightRadius: IG_TILE_RADIUS,
420
+ pointerEvents: "none",
421
+ // Hiding captions for now until we build in
422
+ // the dynamic behavior that clients can dictate
423
+ display: "none"
424
+ };
425
+ var CAPTION_TEXT_STYLE = {
426
+ color: "#fff",
427
+ fontSize: 13,
428
+ lineHeight: 1.4,
429
+ fontWeight: 600,
430
+ letterSpacing: "0.005em",
431
+ textShadow: "0 1px 2px rgba(0, 0, 0, 0.35)",
432
+ display: "-webkit-box",
433
+ WebkitLineClamp: 2,
434
+ WebkitBoxOrient: "vertical",
435
+ overflow: "hidden",
436
+ height: "2.8em"
437
+ };
405
438
  var TITLE_MAX = 90;
406
439
  function truncate(text, max = TITLE_MAX) {
407
440
  const trimmed = text.trim();
@@ -451,19 +484,23 @@ function likeBadge(likeCount) {
451
484
  style: {
452
485
  display: "inline-flex",
453
486
  alignItems: "center",
454
- gap: 4,
455
- padding: "3px 8px",
487
+ gap: 6,
488
+ // Breathing room from the tile's top-left corner (refinement #1).
489
+ margin: 6,
490
+ padding: "6px 11px",
456
491
  borderRadius: 999,
457
- background: "rgba(8,12,24,0.65)",
458
- backdropFilter: "blur(6px)",
459
- WebkitBackdropFilter: "blur(6px)",
492
+ background: "rgba(8, 12, 24, 0.55)",
493
+ backdropFilter: "blur(8px)",
494
+ WebkitBackdropFilter: "blur(8px)",
495
+ border: "1px solid rgba(255, 255, 255, 0.14)",
460
496
  color: "#fff",
461
- fontSize: 11,
462
- fontWeight: 700,
463
- lineHeight: 1
497
+ fontSize: 13,
498
+ fontWeight: 600,
499
+ lineHeight: 1,
500
+ letterSpacing: "0.01em"
464
501
  },
465
502
  children: [
466
- /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/heart", size: 13, "aria-hidden": "true" }),
503
+ /* @__PURE__ */ jsx(DynamicIcon, { name: "mdi/cards-heart", size: 15, "aria-hidden": "true" }),
467
504
  /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: likeCount.toLocaleString() })
468
505
  ]
469
506
  }
@@ -472,157 +509,148 @@ function likeBadge(likeCount) {
472
509
  var VIEWER_ACTIONS = [
473
510
  {
474
511
  id: "open-in-instagram",
475
- icon: /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/external-link", size: 22, "aria-hidden": "true" }),
512
+ icon: /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/instagram", size: 22, "aria-hidden": "true" }),
476
513
  label: "Instagram",
477
514
  ariaLabel: "Open in Instagram",
478
515
  onPress: (item) => openPermalink(item)
479
516
  }
480
517
  ];
481
- function RailStat({
482
- iconName,
483
- count,
484
- label,
485
- onPress
486
- }) {
487
- return /* @__PURE__ */ jsxs(
488
- "button",
518
+ function InstagramViewerRail({ item }) {
519
+ const meta = item.meta ?? {};
520
+ return /* @__PURE__ */ jsx(
521
+ "div",
489
522
  {
490
- type: "button",
491
- "aria-label": `${count.toLocaleString()} ${label}`,
492
- onClick: (e) => {
493
- e.stopPropagation();
494
- onPress();
495
- },
496
523
  style: {
524
+ position: "absolute",
525
+ right: 11,
526
+ bottom: 135,
497
527
  display: "flex",
498
528
  flexDirection: "column",
499
529
  alignItems: "center",
500
- gap: 5,
501
- padding: 0,
502
- border: "none",
503
- background: "transparent",
504
- color: "inherit",
505
- cursor: "pointer",
506
- font: "inherit"
530
+ gap: 18,
531
+ color: "var(--psmi-chrome-fg, #fff)",
532
+ zIndex: 3
507
533
  },
508
- children: [
509
- /* @__PURE__ */ jsx(
510
- "span",
511
- {
512
- style: {
513
- width: 46,
514
- height: 46,
515
- borderRadius: "50%",
516
- background: "var(--psmi-chrome-bg, rgba(255,255,255,0.14))",
517
- backdropFilter: "blur(6px)",
518
- WebkitBackdropFilter: "blur(6px)",
519
- display: "flex",
520
- alignItems: "center",
521
- justifyContent: "center"
522
- },
523
- children: /* @__PURE__ */ jsx(DynamicIcon, { name: iconName, size: 22, "aria-hidden": "true" })
524
- }
525
- ),
526
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { fontSize: 11, fontWeight: 600 }, children: count.toLocaleString() })
527
- ]
534
+ children: meta.href ? /* @__PURE__ */ jsx(
535
+ "a",
536
+ {
537
+ href: meta.href,
538
+ target: "_blank",
539
+ rel: "noopener noreferrer",
540
+ "aria-label": "Open in Instagram",
541
+ onClick: (e) => e.stopPropagation(),
542
+ style: {
543
+ display: "flex",
544
+ flexDirection: "column",
545
+ alignItems: "center",
546
+ padding: 0,
547
+ border: "none",
548
+ background: "transparent",
549
+ color: "inherit",
550
+ cursor: "pointer",
551
+ font: "inherit",
552
+ textDecoration: "none"
553
+ },
554
+ children: /* @__PURE__ */ jsx(
555
+ "span",
556
+ {
557
+ style: {
558
+ width: 46,
559
+ height: 46,
560
+ borderRadius: "50%",
561
+ background: "var(--psmi-chrome-bg, rgba(255,255,255,0.14))",
562
+ backdropFilter: "blur(6px)",
563
+ WebkitBackdropFilter: "blur(6px)",
564
+ display: "flex",
565
+ alignItems: "center",
566
+ justifyContent: "center"
567
+ },
568
+ children: /* @__PURE__ */ jsx(
569
+ DynamicIcon,
570
+ {
571
+ name: "lucide/instagram",
572
+ size: 22,
573
+ "aria-hidden": "true"
574
+ }
575
+ )
576
+ }
577
+ )
578
+ }
579
+ ) : null
528
580
  }
529
581
  );
530
582
  }
531
- function InstagramViewerRail({ item }) {
532
- const meta = item.meta ?? {};
533
- const open = () => openPermalink(item);
583
+ function profileUrl(username) {
584
+ return `https://www.instagram.com/${username.replace(/^@+/, "")}/`;
585
+ }
586
+ var USERNAME_BADGE_STYLE = {
587
+ display: "inline-flex",
588
+ alignItems: "center",
589
+ gap: 7,
590
+ marginBottom: 10,
591
+ padding: "7px 12px",
592
+ borderRadius: 999,
593
+ background: "var(--psmi-chrome-bg, rgba(255,255,255,0.16))",
594
+ backdropFilter: "blur(8px)",
595
+ WebkitBackdropFilter: "blur(8px)",
596
+ border: "1px solid rgba(255, 255, 255, 0.18)",
597
+ color: "inherit",
598
+ fontSize: 13,
599
+ fontWeight: 700,
600
+ lineHeight: 1,
601
+ letterSpacing: "0.01em",
602
+ textDecoration: "none",
603
+ pointerEvents: "auto"
604
+ };
605
+ var VIEWER_TITLE_STYLE = {
606
+ fontSize: 16,
607
+ fontWeight: 700,
608
+ lineHeight: 1.35,
609
+ textShadow: "0 1px 2px rgba(0, 0, 0, 0.4)",
610
+ display: "-webkit-box",
611
+ WebkitLineClamp: 2,
612
+ WebkitBoxOrient: "vertical",
613
+ overflow: "hidden"
614
+ };
615
+ function InstagramViewerCaption({
616
+ item,
617
+ username
618
+ }) {
619
+ const handle = username?.replace(/^@+/, "");
620
+ const badgeContent = /* @__PURE__ */ jsxs(Fragment, { children: [
621
+ /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/instagram", size: 14, "aria-hidden": "true" }),
622
+ /* @__PURE__ */ jsx("span", { children: handle ? `@${handle}` : "Instagram" })
623
+ ] });
534
624
  return /* @__PURE__ */ jsxs(
535
625
  "div",
536
626
  {
537
627
  style: {
538
628
  position: "absolute",
539
- right: 11,
540
- bottom: 135,
541
- display: "flex",
542
- flexDirection: "column",
543
- alignItems: "center",
544
- gap: 18,
629
+ left: 16,
630
+ // Mirrors the library caption card's clearance for the actions rail.
631
+ right: 78,
632
+ bottom: 34,
545
633
  color: "var(--psmi-chrome-fg, #fff)",
546
- zIndex: 3
634
+ zIndex: 2
547
635
  },
548
636
  children: [
549
- typeof meta.likeCount === "number" ? /* @__PURE__ */ jsx(
550
- RailStat,
551
- {
552
- iconName: "lucide/heart",
553
- count: meta.likeCount,
554
- label: "likes",
555
- onPress: open
556
- }
557
- ) : null,
558
- typeof meta.commentCount === "number" ? /* @__PURE__ */ jsx(
559
- RailStat,
560
- {
561
- iconName: "lucide/message-circle",
562
- count: meta.commentCount,
563
- label: "comments",
564
- onPress: open
565
- }
566
- ) : null,
567
- typeof meta.viewCount === "number" ? /* @__PURE__ */ jsx(
568
- RailStat,
569
- {
570
- iconName: "lucide/eye",
571
- count: meta.viewCount,
572
- label: "views",
573
- onPress: open
574
- }
575
- ) : null,
576
- meta.href ? /* @__PURE__ */ jsxs(
637
+ handle ? /* @__PURE__ */ jsx(
577
638
  "a",
578
639
  {
579
- href: meta.href,
640
+ href: profileUrl(handle),
580
641
  target: "_blank",
581
642
  rel: "noopener noreferrer",
582
- "aria-label": "Open in Instagram",
643
+ "aria-label": `Open @${handle} on Instagram`,
583
644
  onClick: (e) => e.stopPropagation(),
584
- style: {
585
- display: "flex",
586
- flexDirection: "column",
587
- alignItems: "center",
588
- gap: 5,
589
- padding: 0,
590
- border: "none",
591
- background: "transparent",
592
- color: "inherit",
593
- cursor: "pointer",
594
- font: "inherit",
595
- textDecoration: "none"
596
- },
597
- children: [
598
- /* @__PURE__ */ jsx(
599
- "span",
600
- {
601
- style: {
602
- width: 46,
603
- height: 46,
604
- borderRadius: "50%",
605
- background: "var(--psmi-chrome-bg, rgba(255,255,255,0.14))",
606
- backdropFilter: "blur(6px)",
607
- WebkitBackdropFilter: "blur(6px)",
608
- display: "flex",
609
- alignItems: "center",
610
- justifyContent: "center"
611
- },
612
- children: /* @__PURE__ */ jsx(
613
- DynamicIcon,
614
- {
615
- name: "lucide/external-link",
616
- size: 22,
617
- "aria-hidden": "true"
618
- }
619
- )
620
- }
621
- ),
622
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { fontSize: 11, fontWeight: 600 }, children: "Instagram" })
623
- ]
645
+ style: { ...USERNAME_BADGE_STYLE, cursor: "pointer" },
646
+ children: badgeContent
624
647
  }
625
- ) : null
648
+ ) : (
649
+ // No username supplied: never fabricate a handle — degrade to a
650
+ // non-interactive "Instagram" chip in the same position.
651
+ /* @__PURE__ */ jsx("span", { style: USERNAME_BADGE_STYLE, children: badgeContent })
652
+ ),
653
+ /* @__PURE__ */ jsx("div", { style: VIEWER_TITLE_STYLE, children: item.title })
626
654
  ]
627
655
  }
628
656
  );
@@ -632,7 +660,8 @@ function InstagramFeedGrid({
632
660
  gridClassName,
633
661
  itemClassName,
634
662
  imageClassName,
635
- optixFlowConfig
663
+ optixFlowConfig,
664
+ showLikeBadges = false
636
665
  }) {
637
666
  const { open } = useImmersiveFeed();
638
667
  const posterImgProps = useMemo(() => {
@@ -645,26 +674,39 @@ function InstagramFeedGrid({
645
674
  "div",
646
675
  {
647
676
  className: cn(
648
- "grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4",
677
+ "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4",
649
678
  gridClassName
650
679
  ),
651
680
  children: mediaItems.map((mediaItem) => {
652
681
  const likeCount = mediaItem.meta?.likeCount;
653
- return /* @__PURE__ */ jsx(
654
- ThumbnailCard,
655
- {
656
- item: mediaItem,
657
- onOpen: open,
658
- size: IG_TILE_WIDTH,
659
- style: IG_TILE_STYLE,
660
- className: itemClassName,
661
- elevated: false,
662
- showDuration: false,
663
- glyphMode: "hover",
664
- posterImgProps,
665
- badgeSlot: typeof likeCount === "number" ? likeBadge(likeCount) : void 0
666
- },
667
- mediaItem.id
682
+ return (
683
+ // The wrapper owns the tile's outer chrome (subtle boundary shadow,
684
+ // matching radius) and the block's own caption overlay — the card's
685
+ // built-in caption is hidden so 1- and 2-line captions can share a
686
+ // fixed-height, top-aligned text box (annotated refinements #2–#4).
687
+ /* @__PURE__ */ jsxs("div", { className: itemClassName, style: TILE_WRAPPER_STYLE, children: [
688
+ /* @__PURE__ */ jsx(
689
+ ThumbnailCard,
690
+ {
691
+ item: mediaItem,
692
+ onOpen: open,
693
+ size: IG_TILE_WIDTH,
694
+ style: IG_TILE_STYLE,
695
+ elevated: false,
696
+ hideCaption: true,
697
+ hideProgressHint: true,
698
+ showDuration: false,
699
+ glyphMode: "hover",
700
+ posterImgProps,
701
+ badgeSlot: showLikeBadges && typeof likeCount === "number" ? likeBadge(likeCount) : void 0
702
+ }
703
+ ),
704
+ mediaItem.title ? (
705
+ // aria-hidden: the card already announces the post title; this
706
+ // overlay is purely visual. pointer-events pass through to the card.
707
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: CAPTION_SCRIM_STYLE, children: /* @__PURE__ */ jsx("div", { style: CAPTION_TEXT_STYLE, children: mediaItem.title }) })
708
+ ) : null
709
+ ] }, mediaItem.id)
668
710
  );
669
711
  })
670
712
  }
@@ -677,7 +719,8 @@ function InstagramPostGrid({
677
719
  items,
678
720
  itemsSlot,
679
721
  className,
680
- containerClassName,
722
+ containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
723
+ spacing = "py-12 md:py-32",
681
724
  headerClassName,
682
725
  headingClassName,
683
726
  subheadingClassName,
@@ -685,15 +728,24 @@ function InstagramPostGrid({
685
728
  itemClassName,
686
729
  imageClassName,
687
730
  optixFlowConfig,
731
+ username,
732
+ showLikeBadges = false,
688
733
  background,
689
- spacing,
690
734
  pattern,
691
735
  patternOpacity,
692
736
  patternClassName
693
737
  }) {
694
738
  const mediaItems = useMemo(() => {
695
739
  if (itemsSlot || !items) return [];
696
- return items.filter((item) => Boolean(item.image)).map(toMediaItem);
740
+ const withImage = items.filter((item) => Boolean(item.image));
741
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production" && withImage.length < items.length) {
742
+ const skipped = items.filter((item) => !item.image);
743
+ const skippedIds = skipped.map((item) => item.id).join(", ");
744
+ console.warn(
745
+ `[instagram-post-grid] skipped ${skipped.length} item(s) without a resolvable image: ${skippedIds}`
746
+ );
747
+ }
748
+ return withImage.map(toMediaItem);
697
749
  }, [items, itemsSlot]);
698
750
  if (!itemsSlot && (!items || items.length === 0)) {
699
751
  return null;
@@ -715,7 +767,7 @@ function InstagramPostGrid({
715
767
  "h2",
716
768
  {
717
769
  className: cn(
718
- "text-xl font-medium tracking-tight md:text-2xl lg:text-3xl text-balance",
770
+ "text-xl font-medium tracking-tight md:text-3xl lg:text-5xl text-balance",
719
771
  headingClassName
720
772
  ),
721
773
  children: heading
@@ -725,7 +777,7 @@ function InstagramPostGrid({
725
777
  "p",
726
778
  {
727
779
  className: cn(
728
- "max-w-2xl text-balance text-muted-foreground",
780
+ "max-w-full md:max-w-2xl text-balance opacity-75",
729
781
  subheadingClassName
730
782
  ),
731
783
  children: subheading
@@ -736,7 +788,7 @@ function InstagramPostGrid({
736
788
  "div",
737
789
  {
738
790
  className: cn(
739
- "grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4",
791
+ "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4",
740
792
  gridClassName
741
793
  ),
742
794
  children: itemsSlot
@@ -749,14 +801,16 @@ function InstagramPostGrid({
749
801
  gridClassName,
750
802
  itemClassName,
751
803
  imageClassName,
752
- optixFlowConfig
804
+ optixFlowConfig,
805
+ showLikeBadges
753
806
  }
754
807
  ),
755
808
  /* @__PURE__ */ jsx(
756
809
  ImmersiveViewer,
757
810
  {
758
811
  ariaLabel: "Instagram post viewer",
759
- renderActions: ({ item }) => /* @__PURE__ */ jsx(InstagramViewerRail, { item })
812
+ renderActions: ({ item }) => /* @__PURE__ */ jsx(InstagramViewerRail, { item }),
813
+ renderCaption: (item) => /* @__PURE__ */ jsx(InstagramViewerCaption, { item, username })
760
814
  }
761
815
  )
762
816
  ] })