@opensite/ui 3.11.2 → 3.12.1

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/registry.js CHANGED
@@ -35249,7 +35249,10 @@ var CAPTION_SCRIM_STYLE = {
35249
35249
  background: "linear-gradient(180deg, rgba(8,12,24,0) 0%, rgba(8,12,24,0.55) 45%, rgba(8,12,24,0.78) 100%)",
35250
35250
  borderBottomLeftRadius: IG_TILE_RADIUS,
35251
35251
  borderBottomRightRadius: IG_TILE_RADIUS,
35252
- pointerEvents: "none"
35252
+ pointerEvents: "none",
35253
+ // Hiding captions for now until we build in
35254
+ // the dynamic behavior that clients can dictate
35255
+ display: "none"
35253
35256
  };
35254
35257
  var CAPTION_TEXT_STYLE = {
35255
35258
  color: "#fff",
@@ -35329,7 +35332,7 @@ function likeBadge(likeCount) {
35329
35332
  letterSpacing: "0.01em"
35330
35333
  },
35331
35334
  children: [
35332
- /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/heart", size: 15, "aria-hidden": "true" }),
35335
+ /* @__PURE__ */ jsx(DynamicIcon, { name: "mdi/cards-heart", size: 15, "aria-hidden": "true" }),
35333
35336
  /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: likeCount.toLocaleString() })
35334
35337
  ]
35335
35338
  }
@@ -35338,157 +35341,148 @@ function likeBadge(likeCount) {
35338
35341
  var VIEWER_ACTIONS = [
35339
35342
  {
35340
35343
  id: "open-in-instagram",
35341
- icon: /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/external-link", size: 22, "aria-hidden": "true" }),
35344
+ icon: /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/instagram", size: 22, "aria-hidden": "true" }),
35342
35345
  label: "Instagram",
35343
35346
  ariaLabel: "Open in Instagram",
35344
35347
  onPress: (item) => openPermalink(item)
35345
35348
  }
35346
35349
  ];
35347
- function RailStat({
35348
- iconName,
35349
- count,
35350
- label,
35351
- onPress
35352
- }) {
35353
- return /* @__PURE__ */ jsxs(
35354
- "button",
35350
+ function InstagramViewerRail({ item }) {
35351
+ const meta = item.meta ?? {};
35352
+ return /* @__PURE__ */ jsx(
35353
+ "div",
35355
35354
  {
35356
- type: "button",
35357
- "aria-label": `${count.toLocaleString()} ${label}`,
35358
- onClick: (e) => {
35359
- e.stopPropagation();
35360
- onPress();
35361
- },
35362
35355
  style: {
35356
+ position: "absolute",
35357
+ right: 11,
35358
+ // Bottom-aligned with the caption card (see InstagramViewerCaption).
35359
+ bottom: 34,
35363
35360
  display: "flex",
35364
35361
  flexDirection: "column",
35365
35362
  alignItems: "center",
35366
- gap: 5,
35367
- padding: 0,
35368
- border: "none",
35369
- background: "transparent",
35370
- color: "inherit",
35371
- cursor: "pointer",
35372
- font: "inherit"
35363
+ color: "var(--psmi-chrome-fg, #fff)",
35364
+ zIndex: 3
35373
35365
  },
35374
- children: [
35375
- /* @__PURE__ */ jsx(
35376
- "span",
35377
- {
35378
- style: {
35379
- width: 46,
35380
- height: 46,
35381
- borderRadius: "50%",
35382
- background: "var(--psmi-chrome-bg, rgba(255,255,255,0.14))",
35383
- backdropFilter: "blur(6px)",
35384
- WebkitBackdropFilter: "blur(6px)",
35385
- display: "flex",
35386
- alignItems: "center",
35387
- justifyContent: "center"
35388
- },
35389
- children: /* @__PURE__ */ jsx(DynamicIcon, { name: iconName, size: 22, "aria-hidden": "true" })
35390
- }
35391
- ),
35392
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { fontSize: 11, fontWeight: 600 }, children: count.toLocaleString() })
35393
- ]
35366
+ children: meta.href ? /* @__PURE__ */ jsx(
35367
+ "a",
35368
+ {
35369
+ href: meta.href,
35370
+ target: "_blank",
35371
+ rel: "noopener noreferrer",
35372
+ "aria-label": "Open in Instagram",
35373
+ onClick: (e) => e.stopPropagation(),
35374
+ style: {
35375
+ display: "flex",
35376
+ flexDirection: "column",
35377
+ alignItems: "center",
35378
+ padding: 0,
35379
+ border: "none",
35380
+ background: "transparent",
35381
+ color: "inherit",
35382
+ cursor: "pointer",
35383
+ font: "inherit",
35384
+ textDecoration: "none"
35385
+ },
35386
+ children: /* @__PURE__ */ jsx(
35387
+ "span",
35388
+ {
35389
+ style: {
35390
+ width: 46,
35391
+ height: 46,
35392
+ borderRadius: "50%",
35393
+ background: "var(--psmi-chrome-bg, rgba(255,255,255,0.14))",
35394
+ backdropFilter: "blur(6px)",
35395
+ WebkitBackdropFilter: "blur(6px)",
35396
+ display: "flex",
35397
+ alignItems: "center",
35398
+ justifyContent: "center"
35399
+ },
35400
+ children: /* @__PURE__ */ jsx(
35401
+ DynamicIcon,
35402
+ {
35403
+ name: "lucide/instagram",
35404
+ size: 22,
35405
+ "aria-hidden": "true"
35406
+ }
35407
+ )
35408
+ }
35409
+ )
35410
+ }
35411
+ ) : null
35394
35412
  }
35395
35413
  );
35396
35414
  }
35397
- function InstagramViewerRail({ item }) {
35398
- const meta = item.meta ?? {};
35399
- const open = () => openPermalink(item);
35415
+ function profileUrl(username) {
35416
+ return `https://www.instagram.com/${username.replace(/^@+/, "")}/`;
35417
+ }
35418
+ var USERNAME_BADGE_STYLE = {
35419
+ display: "inline-flex",
35420
+ alignItems: "center",
35421
+ gap: 7,
35422
+ marginBottom: 10,
35423
+ padding: "7px 12px",
35424
+ borderRadius: 999,
35425
+ background: "var(--psmi-chrome-bg, rgba(255,255,255,0.16))",
35426
+ backdropFilter: "blur(8px)",
35427
+ WebkitBackdropFilter: "blur(8px)",
35428
+ border: "1px solid rgba(255, 255, 255, 0.18)",
35429
+ color: "inherit",
35430
+ fontSize: 13,
35431
+ fontWeight: 700,
35432
+ lineHeight: 1,
35433
+ letterSpacing: "0.01em",
35434
+ textDecoration: "none",
35435
+ pointerEvents: "auto"
35436
+ };
35437
+ var VIEWER_TITLE_STYLE = {
35438
+ fontSize: 16,
35439
+ fontWeight: 700,
35440
+ lineHeight: 1.35,
35441
+ textShadow: "0 1px 2px rgba(0, 0, 0, 0.4)",
35442
+ display: "-webkit-box",
35443
+ WebkitLineClamp: 2,
35444
+ WebkitBoxOrient: "vertical",
35445
+ overflow: "hidden"
35446
+ };
35447
+ function InstagramViewerCaption({
35448
+ item,
35449
+ username
35450
+ }) {
35451
+ const handle = username?.replace(/^@+/, "");
35452
+ const badgeContent = /* @__PURE__ */ jsxs(Fragment, { children: [
35453
+ /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/instagram", size: 14, "aria-hidden": "true" }),
35454
+ /* @__PURE__ */ jsx("span", { children: handle ? `@${handle}` : "Instagram" })
35455
+ ] });
35400
35456
  return /* @__PURE__ */ jsxs(
35401
35457
  "div",
35402
35458
  {
35403
35459
  style: {
35404
35460
  position: "absolute",
35405
- right: 11,
35406
- bottom: 135,
35407
- display: "flex",
35408
- flexDirection: "column",
35409
- alignItems: "center",
35410
- gap: 18,
35461
+ left: 16,
35462
+ // Mirrors the library caption card's clearance for the actions rail.
35463
+ right: 78,
35464
+ bottom: 34,
35411
35465
  color: "var(--psmi-chrome-fg, #fff)",
35412
- zIndex: 3
35466
+ zIndex: 2
35413
35467
  },
35414
35468
  children: [
35415
- typeof meta.likeCount === "number" ? /* @__PURE__ */ jsx(
35416
- RailStat,
35417
- {
35418
- iconName: "lucide/heart",
35419
- count: meta.likeCount,
35420
- label: "likes",
35421
- onPress: open
35422
- }
35423
- ) : null,
35424
- typeof meta.commentCount === "number" ? /* @__PURE__ */ jsx(
35425
- RailStat,
35426
- {
35427
- iconName: "lucide/message-circle",
35428
- count: meta.commentCount,
35429
- label: "comments",
35430
- onPress: open
35431
- }
35432
- ) : null,
35433
- typeof meta.viewCount === "number" ? /* @__PURE__ */ jsx(
35434
- RailStat,
35435
- {
35436
- iconName: "lucide/eye",
35437
- count: meta.viewCount,
35438
- label: "views",
35439
- onPress: open
35440
- }
35441
- ) : null,
35442
- meta.href ? /* @__PURE__ */ jsxs(
35469
+ handle ? /* @__PURE__ */ jsx(
35443
35470
  "a",
35444
35471
  {
35445
- href: meta.href,
35472
+ href: profileUrl(handle),
35446
35473
  target: "_blank",
35447
35474
  rel: "noopener noreferrer",
35448
- "aria-label": "Open in Instagram",
35475
+ "aria-label": `Open @${handle} on Instagram`,
35449
35476
  onClick: (e) => e.stopPropagation(),
35450
- style: {
35451
- display: "flex",
35452
- flexDirection: "column",
35453
- alignItems: "center",
35454
- gap: 5,
35455
- padding: 0,
35456
- border: "none",
35457
- background: "transparent",
35458
- color: "inherit",
35459
- cursor: "pointer",
35460
- font: "inherit",
35461
- textDecoration: "none"
35462
- },
35463
- children: [
35464
- /* @__PURE__ */ jsx(
35465
- "span",
35466
- {
35467
- style: {
35468
- width: 46,
35469
- height: 46,
35470
- borderRadius: "50%",
35471
- background: "var(--psmi-chrome-bg, rgba(255,255,255,0.14))",
35472
- backdropFilter: "blur(6px)",
35473
- WebkitBackdropFilter: "blur(6px)",
35474
- display: "flex",
35475
- alignItems: "center",
35476
- justifyContent: "center"
35477
- },
35478
- children: /* @__PURE__ */ jsx(
35479
- DynamicIcon,
35480
- {
35481
- name: "lucide/external-link",
35482
- size: 22,
35483
- "aria-hidden": "true"
35484
- }
35485
- )
35486
- }
35487
- ),
35488
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { fontSize: 11, fontWeight: 600 }, children: "Instagram" })
35489
- ]
35477
+ style: { ...USERNAME_BADGE_STYLE, cursor: "pointer" },
35478
+ children: badgeContent
35490
35479
  }
35491
- ) : null
35480
+ ) : (
35481
+ // No username supplied: never fabricate a handle — degrade to a
35482
+ // non-interactive "Instagram" chip in the same position.
35483
+ /* @__PURE__ */ jsx("span", { style: USERNAME_BADGE_STYLE, children: badgeContent })
35484
+ ),
35485
+ /* @__PURE__ */ jsx("div", { style: VIEWER_TITLE_STYLE, children: item.title })
35492
35486
  ]
35493
35487
  }
35494
35488
  );
@@ -35498,7 +35492,8 @@ function InstagramFeedGrid({
35498
35492
  gridClassName,
35499
35493
  itemClassName,
35500
35494
  imageClassName,
35501
- optixFlowConfig
35495
+ optixFlowConfig,
35496
+ showLikeBadges = false
35502
35497
  }) {
35503
35498
  const { open } = useImmersiveFeed();
35504
35499
  const posterImgProps = useMemo(() => {
@@ -35535,7 +35530,7 @@ function InstagramFeedGrid({
35535
35530
  showDuration: false,
35536
35531
  glyphMode: "hover",
35537
35532
  posterImgProps,
35538
- badgeSlot: typeof likeCount === "number" ? likeBadge(likeCount) : void 0
35533
+ badgeSlot: showLikeBadges && typeof likeCount === "number" ? likeBadge(likeCount) : void 0
35539
35534
  }
35540
35535
  ),
35541
35536
  mediaItem.title ? (
@@ -35565,6 +35560,8 @@ function InstagramPostGrid({
35565
35560
  itemClassName,
35566
35561
  imageClassName,
35567
35562
  optixFlowConfig,
35563
+ username,
35564
+ showLikeBadges = false,
35568
35565
  background,
35569
35566
  pattern,
35570
35567
  patternOpacity,
@@ -35573,7 +35570,7 @@ function InstagramPostGrid({
35573
35570
  const mediaItems = useMemo(() => {
35574
35571
  if (itemsSlot || !items) return [];
35575
35572
  const withImage = items.filter((item) => Boolean(item.image));
35576
- if (process.env.NODE_ENV !== "production" && withImage.length < items.length) {
35573
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production" && withImage.length < items.length) {
35577
35574
  const skipped = items.filter((item) => !item.image);
35578
35575
  const skippedIds = skipped.map((item) => item.id).join(", ");
35579
35576
  console.warn(
@@ -35602,7 +35599,7 @@ function InstagramPostGrid({
35602
35599
  "h2",
35603
35600
  {
35604
35601
  className: cn(
35605
- "text-xl font-medium tracking-tight md:text-2xl lg:text-3xl text-balance",
35602
+ "text-xl font-medium tracking-tight md:text-3xl lg:text-5xl text-balance",
35606
35603
  headingClassName
35607
35604
  ),
35608
35605
  children: heading
@@ -35612,7 +35609,7 @@ function InstagramPostGrid({
35612
35609
  "p",
35613
35610
  {
35614
35611
  className: cn(
35615
- "max-w-2xl text-balance text-muted-foreground",
35612
+ "max-w-full md:max-w-2xl text-balance opacity-75",
35616
35613
  subheadingClassName
35617
35614
  ),
35618
35615
  children: subheading
@@ -35636,14 +35633,16 @@ function InstagramPostGrid({
35636
35633
  gridClassName,
35637
35634
  itemClassName,
35638
35635
  imageClassName,
35639
- optixFlowConfig
35636
+ optixFlowConfig,
35637
+ showLikeBadges
35640
35638
  }
35641
35639
  ),
35642
35640
  /* @__PURE__ */ jsx(
35643
35641
  ImmersiveViewer,
35644
35642
  {
35645
35643
  ariaLabel: "Instagram post viewer",
35646
- renderActions: ({ item }) => /* @__PURE__ */ jsx(InstagramViewerRail, { item })
35644
+ renderActions: ({ item }) => /* @__PURE__ */ jsx(InstagramViewerRail, { item }),
35645
+ renderCaption: (item) => /* @__PURE__ */ jsx(InstagramViewerCaption, { item, username })
35647
35646
  }
35648
35647
  )
35649
35648
  ] })
@@ -111527,8 +111526,8 @@ var interiorCarousel = {
111527
111526
  }
111528
111527
  };
111529
111528
  var instagramPostGrid = {
111530
- exampleUsage: "Display a website's Instagram feed as a responsive grid of vertical (9:16) tiles that open a fullscreen, swipeable immersive viewer (TikTok/Reels-style). Photos and reels are both first-class media \u2014 reels play a muted preview and open with playback; photos open as a full-bleed still. Each tile shows a like-count pill top-left (when available), no duration timestamp, and a center glyph on hover only (a play triangle for reels, an expand icon for photos). The fullscreen viewer carries the caption plus a read-only engagement rail (likes/comments/views) and an explicit 'Open in Instagram' link (a real anchor opening in a new tab). Data is hydrated from the toastability Instagram feed (dataSource type 'instagram_feed', bindTo 'items').",
111531
- importantUsageNotes: `${GALLERY_MEDIA_NOTE} This is a DYNAMIC feed block: its 'items' are hydrated at routing-build time from the connected Instagram profile \u2014 do NOT hand-author post content. 'items[].image' and 'items[].videoUrl' MUST be the re-hosted MediaRecord CDN URLs served by the feed; expiring Instagram CDN URLs must never be used. Tiles are VERTICAL (9:16) and render both photos and reels; a post is treated as a reel only when 'isVideo' is true AND 'videoUrl' is present. Engagement counts ('likeCount', 'commentCount', 'viewCount') render only when the value is present \u2014 never fabricate a zero for a missing metric; 'likeCount' surfaces as the tile's top-left pill and, with comments/views, in the viewer rail. Tapping a tile OPENS THE FULLSCREEN VIEWER (the whole-tile link-out was replaced); the instagram.com permalink ('items[].href') opens from the viewer's 'Open in Instagram' egress, which is rendered as a REAL anchor (target=_blank, rel=noopener noreferrer) \u2014 a crawlable, affordance-correct link, not a scripted window.open button. 'items[].caption' should be a plain STRING when rendered immersively \u2014 it flows through as the tile/viewer title text; a rich ReactNode caption cannot be threaded through the immersive card/viewer and falls back to 'items[].imageAlt' (then a generic label). Consumer image styling is honored: 'imageClassName' and 'optixFlowConfig' are forwarded onto each tile's poster image. Items without an 'image' are skipped; an empty or missing 'items' array renders nothing. Requires the site to have a connected, fully-imported Instagram profile ('instagram_media' capability).`,
111529
+ exampleUsage: "Display a website's Instagram feed as a responsive grid of vertical (9:16) tiles that open a fullscreen, swipeable immersive viewer (TikTok/Reels-style). Photos and reels are both first-class media \u2014 reels play a muted preview and open with playback; photos open as a full-bleed still. Tiles show no duration timestamp and a center glyph on hover only (a play triangle for reels, an expand icon for photos); like-count pills are hidden by default (opt back in via 'showLikeBadges' once per-client dynamic visibility ships). The fullscreen viewer shows a prominent clickable '@username' profile badge (set the 'username' prop) above the post title and a single Instagram-glyph egress to the post's permalink (a real anchor opening in a new tab). Data is hydrated from the toastability Instagram feed (dataSource type 'instagram_feed', bindTo 'items').",
111530
+ importantUsageNotes: `${GALLERY_MEDIA_NOTE} This is a DYNAMIC feed block: its 'items' are hydrated at routing-build time from the connected Instagram profile \u2014 do NOT hand-author post content. 'items[].image' and 'items[].videoUrl' MUST be the re-hosted MediaRecord CDN URLs served by the feed; expiring Instagram CDN URLs must never be used. Tiles are VERTICAL (9:16) and render both photos and reels; a post is treated as a reel only when 'isVideo' is true AND 'videoUrl' is present. Engagement counts ('likeCount', 'commentCount', 'viewCount') are carried in the data but NOT currently displayed: the tile like-count pill is hidden by default ('showLikeBadges' opts back in; never fabricate a zero for a missing metric) and the viewer's engagement rail was removed per design review. Set 'username' to the account's handle (leading '@' tolerated) \u2014 it renders as the viewer caption's prominent clickable '@username' badge linking to the profile in a new tab; when omitted the badge degrades to a non-clickable 'Instagram' chip (a handle is never fabricated). Tapping a tile OPENS THE FULLSCREEN VIEWER (the whole-tile link-out was replaced); the instagram.com permalink ('items[].href') opens from the viewer's single Instagram-glyph egress, which is rendered as a REAL anchor (target=_blank, rel=noopener noreferrer) \u2014 a crawlable, affordance-correct link, not a scripted window.open button. 'items[].caption' should be a plain STRING when rendered immersively \u2014 it flows through as the tile/viewer title text; a rich ReactNode caption cannot be threaded through the immersive card/viewer and falls back to 'items[].imageAlt' (then a generic label). Consumer image styling is honored: 'imageClassName' and 'optixFlowConfig' are forwarded onto each tile's poster image. Items without an 'image' are skipped; an empty or missing 'items' array renders nothing. Requires the site to have a connected, fully-imported Instagram profile ('instagram_media' capability).`,
111532
111531
  usageRequirements: {
111533
111532
  requiredProps: ["items"],
111534
111533
  requiresSiteCapabilities: galleryCapabilities(
@@ -111560,6 +111559,7 @@ var instagramPostGrid = {
111560
111559
  exampleProps: {
111561
111560
  heading: "Follow us on Instagram",
111562
111561
  subheading: "The latest from our feed.",
111562
+ username: "acmecafe",
111563
111563
  background: "white",
111564
111564
  spacing: "lg",
111565
111565
  items: [
@@ -125070,7 +125070,7 @@ var BLOCK_REGISTRY = {
125070
125070
  "instagram-post-grid": {
125071
125071
  id: "instagram-post-grid",
125072
125072
  name: "Instagram Post Grid",
125073
- description: "A responsive grid of vertical (9:16) tiles rendering a website's Instagram feed. Tapping a tile opens a fullscreen, swipeable immersive viewer (TikTok/Reels-style) with the caption, a read-only engagement rail (likes/comments/views), and an 'Open in Instagram' action. Photos and reels are both first-class media; each tile shows a like-count pill and a hover-only center glyph (play for reels, expand for photos). Content is hydrated dynamically from the connected Instagram profile. Ideal for social proof sections, footers, and community pages that showcase a live Instagram presence.",
125073
+ description: "A responsive grid of vertical (9:16) tiles rendering a website's Instagram feed. Tapping a tile opens a fullscreen, swipeable immersive viewer (TikTok/Reels-style) with a clickable @username profile badge, the post title, and a single Instagram-glyph link to the post. Photos and reels are both first-class media; tiles use a hover-only center glyph (play for reels, expand for photos), with like-count pills hidden by default. Content is hydrated dynamically from the connected Instagram profile. Ideal for social proof sections, footers, and community pages that showcase a live Instagram presence.",
125074
125074
  semanticTags: [
125075
125075
  "gallery",
125076
125076
  "instagram",
@@ -77,6 +77,6 @@ interface SocialLinkIconProps extends Omit<PressableProps, "children">, SocialLi
77
77
  * />
78
78
  * ```
79
79
  */
80
- declare const SocialLinkIcon: React.ForwardRefExoticComponent<SocialLinkIconProps & React.RefAttributes<HTMLButtonElement | HTMLAnchorElement | HTMLSpanElement>>;
80
+ declare const SocialLinkIcon: React.ForwardRefExoticComponent<SocialLinkIconProps & React.RefAttributes<HTMLAnchorElement | HTMLButtonElement | HTMLSpanElement>>;
81
81
 
82
82
  export { SocialLinkIcon, type SocialLinkIconDynamicIconProps, type SocialLinkIconProps };
@@ -77,6 +77,6 @@ interface SocialLinkIconProps extends Omit<PressableProps, "children">, SocialLi
77
77
  * />
78
78
  * ```
79
79
  */
80
- declare const SocialLinkIcon: React.ForwardRefExoticComponent<SocialLinkIconProps & React.RefAttributes<HTMLButtonElement | HTMLAnchorElement | HTMLSpanElement>>;
80
+ declare const SocialLinkIcon: React.ForwardRefExoticComponent<SocialLinkIconProps & React.RefAttributes<HTMLAnchorElement | HTMLButtonElement | HTMLSpanElement>>;
81
81
 
82
82
  export { SocialLinkIcon, type SocialLinkIconDynamicIconProps, type SocialLinkIconProps };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opensite/ui",
3
- "version": "3.11.2",
3
+ "version": "3.12.1",
4
4
  "description": "Foundational UI component library for OpenSite Semantic Site Builder with tree-shakable exports and abstract styling",
5
5
  "keywords": [
6
6
  "react",