@ohhwells/bridge 0.1.54-next.146 → 0.1.54-next.147

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/index.cjs CHANGED
@@ -409,36 +409,53 @@ function RatingEl({ value, ctx }) {
409
409
  }
410
410
  );
411
411
  }
412
- function PersonEl({
412
+ function AvatarDisc({
413
413
  person,
414
414
  ctx,
415
- path
415
+ path,
416
+ size
416
417
  }) {
417
418
  const avatarUrl = person.avatar ? ctx.resolveMedia(person.avatar) : null;
418
419
  const avatarAttrs = ctx.keyFor ? { "data-ohw-key": ctx.keyFor(`${path}.person.avatar`), "data-ohw-editable": "image" } : {};
420
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
421
+ "span",
422
+ {
423
+ ...avatarAttrs,
424
+ style: {
425
+ width: size,
426
+ height: size,
427
+ borderRadius: "50%",
428
+ background: "#DBEAFE",
429
+ overflow: "hidden",
430
+ flexShrink: 0,
431
+ display: "block"
432
+ },
433
+ children: (avatarUrl || ctx.keyFor) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
434
+ "img",
435
+ {
436
+ src: avatarUrl ?? TRANSPARENT_PX,
437
+ alt: "",
438
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
439
+ }
440
+ )
441
+ }
442
+ );
443
+ }
444
+ function avatarPosition(slots) {
445
+ const person = slots.person;
446
+ if (typeof person?.avatar !== "string" || !person.avatar) return null;
447
+ if (slots.mediaPosition === "left") return "left";
448
+ if (slots.mediaPosition === "bottom") return "bottom";
449
+ return "top";
450
+ }
451
+ function PersonEl({
452
+ person,
453
+ ctx,
454
+ path,
455
+ showAvatar = true
456
+ }) {
419
457
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 10, alignItems: "center" }, children: [
420
- person.avatar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
421
- "span",
422
- {
423
- ...avatarAttrs,
424
- style: {
425
- width: 56,
426
- height: 56,
427
- borderRadius: "50%",
428
- background: "#DBEAFE",
429
- overflow: "hidden",
430
- flexShrink: 0
431
- },
432
- children: (avatarUrl || ctx.keyFor) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
433
- "img",
434
- {
435
- src: avatarUrl ?? TRANSPARENT_PX,
436
- alt: "",
437
- style: { width: "100%", height: "100%", objectFit: "cover" }
438
- }
439
- )
440
- }
441
- ),
458
+ person.avatar && showAvatar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AvatarDisc, { person, ctx, path, size: 56 }),
442
459
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { flex: 1, minWidth: 0 }, children: [
443
460
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
444
461
  "span",
@@ -549,9 +566,35 @@ function TestimonialCard({ node, ctx, path }) {
549
566
  const slots = node.slots ?? {};
550
567
  const hasBg = slots.background === true;
551
568
  const person = slots.person;
569
+ const avatarPos = avatarPosition(slots);
570
+ const content = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
571
+ "div",
572
+ {
573
+ style: {
574
+ display: "flex",
575
+ flexDirection: "column",
576
+ gap: AI_TREE_TOKENS.spacing6,
577
+ minWidth: 0,
578
+ flex: avatarPos === "left" ? 1 : void 0
579
+ },
580
+ children: [
581
+ typeof slots.rating === "number" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RatingEl, { value: slots.rating, ctx }),
582
+ str(slots.quote) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
583
+ "div",
584
+ {
585
+ ...textAttrs(ctx, `${path}.quote`),
586
+ style: { ...typeStyle(AI_TREE_TOKENS.type.bodyM, ctx.brand.fonts.body), color: ctx.brand.palette.dark },
587
+ dangerouslySetInnerHTML: { __html: `"${str(slots.quote)}"` }
588
+ }
589
+ ),
590
+ person && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PersonEl, { person, ctx, path, showAvatar: avatarPos === "bottom" })
591
+ ]
592
+ }
593
+ );
552
594
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
553
595
  "div",
554
596
  {
597
+ "data-ai-avatar-pos": avatarPos ?? void 0,
555
598
  style: {
556
599
  background: hasBg ? ctx.cardSurface : "transparent",
557
600
  borderRadius: hasBg ? AI_TREE_TOKENS.radiusCard : 0,
@@ -564,22 +607,15 @@ function TestimonialCard({ node, ctx, path }) {
564
607
  {
565
608
  style: {
566
609
  display: "flex",
567
- flexDirection: "column",
610
+ flexDirection: avatarPos === "left" ? "row" : "column",
611
+ alignItems: avatarPos === "left" ? "flex-start" : void 0,
568
612
  gap: AI_TREE_TOKENS.spacing6,
569
613
  padding: hasBg ? AI_TREE_TOKENS.paddingBlock : 0,
570
614
  minWidth: 0
571
615
  },
572
616
  children: [
573
- typeof slots.rating === "number" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RatingEl, { value: slots.rating, ctx }),
574
- str(slots.quote) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
575
- "div",
576
- {
577
- ...textAttrs(ctx, `${path}.quote`),
578
- style: { ...typeStyle(AI_TREE_TOKENS.type.bodyM, ctx.brand.fonts.body), color: ctx.brand.palette.dark },
579
- dangerouslySetInnerHTML: { __html: `"${str(slots.quote)}"` }
580
- }
581
- ),
582
- person && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PersonEl, { person, ctx, path })
617
+ person && (avatarPos === "top" || avatarPos === "left") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AvatarDisc, { person, ctx, path, size: 72 }),
618
+ content
583
619
  ]
584
620
  }
585
621
  )
@@ -590,37 +626,26 @@ function TeamCard({ node, ctx, path }) {
590
626
  const slots = node.slots ?? {};
591
627
  const hasBg = slots.background === true;
592
628
  const person = slots.person ?? {};
593
- const avatarUrl = person.avatar ? ctx.resolveMedia(person.avatar) : null;
594
- const avatarAttrs = ctx.keyFor ? { "data-ohw-key": ctx.keyFor(`${path}.person.avatar`), "data-ohw-editable": "image" } : {};
629
+ const avatarPos = avatarPosition(slots);
595
630
  const buttonIdx = (node.children ?? []).findIndex((c) => c.type === "button");
596
631
  const button = buttonIdx >= 0 ? (node.children ?? [])[buttonIdx] : void 0;
597
632
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
598
633
  "div",
599
634
  {
635
+ "data-ai-avatar-pos": avatarPos ?? void 0,
600
636
  style: {
601
637
  background: hasBg ? ctx.cardSurface : "transparent",
602
638
  borderRadius: hasBg ? AI_TREE_TOKENS.radiusCard : 0,
603
639
  overflow: "hidden",
604
640
  outline: slots.featured === true ? `2px solid ${ctx.brand.palette.primary}` : void 0,
605
- minWidth: 0
641
+ minWidth: 0,
642
+ display: avatarPos === "left" ? "flex" : "block",
643
+ alignItems: avatarPos === "left" ? "flex-start" : void 0
606
644
  },
607
645
  children: [
608
- person.avatar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: `${AI_TREE_TOKENS.paddingBlock}px ${AI_TREE_TOKENS.paddingBlock}px 0` }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
609
- "span",
610
- {
611
- ...avatarAttrs,
612
- style: { width: 80, height: 80, borderRadius: "50%", overflow: "hidden", display: "block" },
613
- children: (avatarUrl || ctx.keyFor) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
614
- "img",
615
- {
616
- src: avatarUrl ?? TRANSPARENT_PX,
617
- alt: "",
618
- style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
619
- }
620
- )
621
- }
622
- ) }),
623
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing4, padding: AI_TREE_TOKENS.paddingBlock }, children: [
646
+ avatarPos === "top" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: `${AI_TREE_TOKENS.paddingBlock}px ${AI_TREE_TOKENS.paddingBlock}px 0` }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AvatarDisc, { person, ctx, path, size: 80 }) }),
647
+ avatarPos === "left" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: `${AI_TREE_TOKENS.paddingBlock}px 0 ${AI_TREE_TOKENS.paddingBlock}px ${AI_TREE_TOKENS.paddingBlock}px`, flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AvatarDisc, { person, ctx, path, size: 80 }) }),
648
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing4, padding: AI_TREE_TOKENS.paddingBlock, minWidth: 0, flex: avatarPos === "left" ? 1 : void 0 }, children: [
624
649
  person.name && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
625
650
  "div",
626
651
  {
@@ -653,7 +678,8 @@ function TeamCard({ node, ctx, path }) {
653
678
  },
654
679
  i
655
680
  )) }),
656
- button && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ButtonEl, { slots: button.slots ?? {}, ctx, path: `${path}.c${buttonIdx}` }) })
681
+ button && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ButtonEl, { slots: button.slots ?? {}, ctx, path: `${path}.c${buttonIdx}` }) }),
682
+ avatarPos === "bottom" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AvatarDisc, { person, ctx, path, size: 80 })
657
683
  ] })
658
684
  ]
659
685
  }
@@ -808,56 +834,78 @@ function CardBlock({ node, ctx, path }) {
808
834
  }
809
835
  );
810
836
  }
837
+ function toggleAccordionItem(button) {
838
+ const wasOpen = button.getAttribute("aria-expanded") === "true";
839
+ const list = button.closest("[data-ai-accordion]");
840
+ for (const other of list?.querySelectorAll("button[aria-expanded]") ?? []) {
841
+ other.setAttribute("aria-expanded", "false");
842
+ const panel = other.nextElementSibling;
843
+ if (panel) panel.style.display = "none";
844
+ const chevron = other.querySelector("[data-ai-chevron]");
845
+ if (chevron) chevron.style.transform = "";
846
+ }
847
+ if (!wasOpen) {
848
+ button.setAttribute("aria-expanded", "true");
849
+ const panel = button.nextElementSibling;
850
+ if (panel) panel.style.display = "block";
851
+ const chevron = button.querySelector("[data-ai-chevron]");
852
+ if (chevron) chevron.style.transform = "rotate(180deg)";
853
+ }
854
+ }
811
855
  function AccordionBlock({ node, ctx, path }) {
812
- const [open, setOpen] = import_react.default.useState(null);
813
856
  const rule = `1px solid ${ctx.brand.palette.dark}33`;
814
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { borderTop: rule }, children: (node.items ?? []).map((item, i) => {
815
- const isOpen = open === i;
816
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { borderBottom: rule }, children: [
817
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
818
- "button",
819
- {
820
- type: "button",
821
- "aria-expanded": isOpen,
822
- onClick: () => setOpen(isOpen ? null : i),
823
- style: {
824
- ...typeStyle(AI_TREE_TOKENS.type.bodyMBold, ctx.brand.fonts.body),
825
- color: ctx.brand.palette.dark,
826
- display: "flex",
827
- justifyContent: "space-between",
828
- alignItems: "center",
829
- width: "100%",
830
- padding: "14px 0",
831
- background: "transparent",
832
- border: "none",
833
- cursor: "pointer",
834
- textAlign: "left",
835
- gap: AI_TREE_TOKENS.spacing3
836
- },
837
- children: [
838
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.items.${i}.question`), children: item.question }),
839
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
840
- import_lucide_react.ChevronDown,
841
- {
842
- size: 20,
843
- strokeWidth: 1.5,
844
- "aria-hidden": true,
845
- style: { flexShrink: 0, transform: isOpen ? "rotate(180deg)" : void 0, transition: "transform 0.2s" }
846
- }
847
- )
848
- ]
849
- }
850
- ),
851
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: isOpen ? "block" : "none", paddingBottom: 14 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
852
- "div",
853
- {
854
- ...textAttrs(ctx, `${path}.items.${i}.answer`),
855
- style: { ...typeStyle(AI_TREE_TOKENS.type.bodyM, ctx.brand.fonts.body), color: ctx.brand.palette.accent },
856
- dangerouslySetInnerHTML: { __html: item.answer }
857
- }
858
- ) })
859
- ] }, i);
860
- }) });
857
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-ai-accordion": "", style: { borderTop: rule }, children: (node.items ?? []).map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { borderBottom: rule }, children: [
858
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
859
+ "button",
860
+ {
861
+ type: "button",
862
+ "aria-expanded": false,
863
+ onClick: (e) => toggleAccordionItem(e.currentTarget),
864
+ style: {
865
+ ...typeStyle(AI_TREE_TOKENS.type.bodyMBold, ctx.brand.fonts.body),
866
+ color: ctx.brand.palette.dark,
867
+ display: "flex",
868
+ justifyContent: "space-between",
869
+ alignItems: "center",
870
+ width: "100%",
871
+ padding: "14px 0",
872
+ background: "transparent",
873
+ border: "none",
874
+ cursor: "pointer",
875
+ textAlign: "left",
876
+ gap: AI_TREE_TOKENS.spacing3
877
+ },
878
+ children: [
879
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
880
+ "span",
881
+ {
882
+ ...textAttrs(ctx, `${path}.items.${i}.question`),
883
+ style: { flex: 1, minWidth: 0 },
884
+ dangerouslySetInnerHTML: { __html: item.question }
885
+ }
886
+ ),
887
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
888
+ import_lucide_react.ChevronDown,
889
+ {
890
+ "data-ai-chevron": "",
891
+ size: 20,
892
+ strokeWidth: 1.5,
893
+ "aria-hidden": true,
894
+ style: { flexShrink: 0, transition: "transform 0.2s" }
895
+ }
896
+ )
897
+ ]
898
+ }
899
+ ),
900
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "none", paddingBottom: 14 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
901
+ "div",
902
+ {
903
+ ...textAttrs(ctx, `${path}.items.${i}.answer`),
904
+ style: { ...typeStyle(AI_TREE_TOKENS.type.bodyM, ctx.brand.fonts.body), color: ctx.brand.palette.accent },
905
+ dangerouslySetInnerHTML: { __html: item.answer }
906
+ }
907
+ ) })
908
+ ] }, i)) });
861
909
  }
862
910
  function Carousel({ items, itemsPerRow, ctx }) {
863
911
  const [page, setPage] = import_react.default.useState(0);