@syntrologie/adapt-product 2.27.0 → 2.28.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/cdn.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  runtime
3
- } from "./chunk-HFT5OENK.js";
4
- import "./chunk-SM35E4R3.js";
3
+ } from "./chunk-QZZVFYL6.js";
4
+ import "./chunk-SGJGJRCP.js";
5
5
 
6
6
  // src/cdn.ts
7
7
  var manifest = {
@@ -1,4 +1,5 @@
1
1
  import {
2
+ VISIBLE_FACT_KEYS,
2
3
  __privateAdd,
3
4
  __privateGet,
4
5
  __privateMethod,
@@ -7,7 +8,7 @@ import {
7
8
  comparisonSchema,
8
9
  gridSchema,
9
10
  heroSchema
10
- } from "./chunk-SM35E4R3.js";
11
+ } from "./chunk-SGJGJRCP.js";
11
12
 
12
13
  // ../../sdk-contracts/dist/mount-plumbing.js
13
14
  var MOUNT_PLUMBING_KEYS = ["instanceId", "runtime", "tileId"];
@@ -46,15 +47,13 @@ var COUNTABLE_EVENTS = [
46
47
  "ui.idle",
47
48
  "ui.scroll_thrash",
48
49
  "ui.focus_bounce",
50
+ "ui.hesitate",
51
+ "ui.rage_click",
49
52
  // Navigation
50
53
  "nav.page_view",
51
- "nav.page_leave",
52
- // Derived behavioral signals
53
- "behavior.rage_click",
54
- "behavior.hesitation",
55
- "behavior.confusion"
54
+ "nav.page_leave"
56
55
  ];
57
- var CountableEventZ = z.enum(COUNTABLE_EVENTS).describe("Event name to count. ui.* = user interactions and behavioral detectors, nav.* = page navigation, behavior.* = derived behavioral signals.");
56
+ var CountableEventZ = z.enum(COUNTABLE_EVENTS).describe("Event name to count. ui.* = user interactions and behavioral detectors (hesitate, rage_click, scroll_thrash, focus_bounce, idle, hover); nav.* = page navigation.");
58
57
  var SESSION_METRIC_KEYS = ["time_on_page", "page_views", "scroll_depth"];
59
58
  var SessionMetricKeyZ = z.enum(SESSION_METRIC_KEYS).describe("Session metric key. time_on_page = seconds on current page, page_views = pages visited this session, scroll_depth = 0-100 percentage.");
60
59
  var PageUrlConditionZ = z.object({
@@ -188,6 +187,7 @@ var NotifyZ = z.object({
188
187
 
189
188
  // src/widgets/ProductCardLit.ts
190
189
  import { html, LitElement, nothing } from "lit";
190
+ import { styleMap } from "lit/directives/style-map.js";
191
191
 
192
192
  // src/bind/dom-selector.ts
193
193
  async function resolveDomSelectorPrice(input, _signal) {
@@ -566,7 +566,8 @@ var ProductCardLit = class extends LitElement {
566
566
  return renderProductCard(
567
567
  parsed.data.product,
568
568
  parsed.data.density,
569
- this.resolved[parsed.data.product.id]
569
+ this.resolved[parsed.data.product.id],
570
+ parsed.data.visibleFacts
570
571
  );
571
572
  }
572
573
  };
@@ -601,12 +602,146 @@ ProductCardLit.properties = {
601
602
  resolved: { state: true },
602
603
  validationError: { state: true }
603
604
  };
604
- function renderProductCard(product, density, resolved) {
605
+ var AVAILABILITY_LABELS = {
606
+ in_stock: "In stock",
607
+ preorder: "Preorder",
608
+ backorder: "Backorder",
609
+ out_of_stock: "Out of stock"
610
+ };
611
+ function buildSpecGroups(product) {
612
+ const groups = /* @__PURE__ */ new Map();
613
+ for (const spec of product.specs ?? []) {
614
+ let group = groups.get(spec.section);
615
+ if (!group) {
616
+ group = { section: spec.section, rows: [] };
617
+ groups.set(spec.section, group);
618
+ }
619
+ group.rows.push({
620
+ name: spec.name,
621
+ value: spec.value,
622
+ unit: spec.unit,
623
+ emphasis: spec.emphasis ?? false
624
+ });
625
+ }
626
+ if (product.attributes && product.attributes.length > 0) {
627
+ const about = groups.get("About") ?? { section: "About", rows: [] };
628
+ for (const attr of product.attributes) {
629
+ about.rows.push({
630
+ name: attr.label,
631
+ value: attr.value,
632
+ emphasis: attr.emphasis ?? false
633
+ });
634
+ }
635
+ if (!groups.has("About")) groups.set("About", about);
636
+ }
637
+ return Array.from(groups.values());
638
+ }
639
+ function renderProductCard(product, density, resolved, visibleFacts) {
605
640
  const imgSrc = resolved?.image ?? product.image.src;
606
- const priceText = resolved?.price ?? product.price?.amount;
607
641
  const showFraming = density === "standard" && product.framing;
642
+ const specGroups = buildSpecGroups(product);
643
+ const isVisible = (key) => visibleFacts === void 0 ? true : visibleFacts.includes(key);
644
+ const factOrder = visibleFacts ?? VISIBLE_FACT_KEYS;
645
+ const hasSalePrice = product.salePrice !== void 0;
646
+ const activePriceText = resolved?.price ?? product.salePrice?.amount ?? product.price?.amount;
647
+ const activeCadence = hasSalePrice ? product.salePrice?.cadence : product.price?.cadence;
648
+ const thumbPx = density === "compact" ? 64 : 96;
649
+ const articleStyles = {
650
+ display: "flow-root"
651
+ };
652
+ const imageStyles = {
653
+ float: "left",
654
+ width: `${thumbPx}px`,
655
+ height: `${thumbPx}px`,
656
+ objectFit: "cover",
657
+ borderRadius: "6px",
658
+ marginRight: density === "compact" ? "0.5rem" : "0.75rem",
659
+ marginBottom: density === "compact" ? "0.25rem" : "0.5rem"
660
+ };
661
+ const priceBlockStyles = {
662
+ display: "flex",
663
+ flexDirection: "row",
664
+ alignItems: "baseline",
665
+ flexWrap: "wrap",
666
+ gap: "0.5rem"
667
+ };
668
+ const priceOriginalStyles = {
669
+ textDecoration: "line-through",
670
+ opacity: "0.6",
671
+ fontSize: "0.9em"
672
+ };
673
+ const priceSaleStyles = {
674
+ fontWeight: "600",
675
+ fontSize: "1.1em"
676
+ };
677
+ const priceCadenceStyles = {
678
+ fontSize: "0.85em",
679
+ opacity: "0.7"
680
+ };
681
+ const specGroupStyles = {
682
+ display: "flex",
683
+ flexDirection: "column",
684
+ gap: "0.25rem"
685
+ };
686
+ const specHeadingStyles = {
687
+ fontSize: "0.75rem",
688
+ textTransform: "uppercase",
689
+ letterSpacing: "0.05em",
690
+ opacity: "0.7",
691
+ margin: "0",
692
+ fontWeight: "600"
693
+ };
694
+ const specRowsStyles = {
695
+ listStyle: "none",
696
+ margin: "0",
697
+ padding: "0",
698
+ display: "flex",
699
+ flexDirection: "column",
700
+ gap: "0.125rem"
701
+ };
702
+ const specRowStyles = {
703
+ display: "flex",
704
+ flexDirection: "row",
705
+ alignItems: "baseline",
706
+ gap: "0.5rem",
707
+ fontSize: "0.875rem"
708
+ };
709
+ const specNameStyles = {
710
+ flex: "1",
711
+ minWidth: "0"
712
+ };
713
+ const specValueStyles = {
714
+ fontVariantNumeric: "tabular-nums",
715
+ textAlign: "right"
716
+ };
717
+ const specUnitStyles = {
718
+ fontSize: "0.85em",
719
+ opacity: "0.7",
720
+ minWidth: "2.5rem"
721
+ };
722
+ const specsContainerStyles = {
723
+ display: "flex",
724
+ flexDirection: "column",
725
+ gap: "0.5rem"
726
+ };
727
+ const availabilityStyles = {
728
+ display: "inline-block",
729
+ fontSize: "0.8rem",
730
+ opacity: "0.85"
731
+ };
732
+ const ctasStyles = {
733
+ display: "flex",
734
+ flexDirection: "row",
735
+ gap: "0.5rem",
736
+ marginTop: "0.25rem"
737
+ };
608
738
  return html`
609
- <article class="sc-product-card" data-density=${density}>
739
+ <article
740
+ class="sc-product-card"
741
+ data-density=${density}
742
+ data-labels=${product.labels ? JSON.stringify(product.labels) : nothing}
743
+ style=${styleMap(articleStyles)}
744
+ >
610
745
  <img
611
746
  class="sc-product-card__image"
612
747
  data-product-image
@@ -614,34 +749,143 @@ function renderProductCard(product, density, resolved) {
614
749
  alt=${product.image.alt}
615
750
  loading="lazy"
616
751
  @error=${onImageError}
752
+ style=${styleMap(imageStyles)}
617
753
  />
618
- <header class="sc-product-card__header">
619
- <h3 class="sc-product-card__name" data-product-name>${product.name}</h3>
620
- ${product.tagline ? html`<p class="sc-product-card__tagline" data-product-tagline>${product.tagline}</p>` : nothing}
754
+ <header
755
+ class="sc-product-card__header"
756
+ style=${styleMap({ margin: "0", display: "block" })}
757
+ >
758
+ <h3
759
+ class="sc-product-card__name"
760
+ data-product-name
761
+ style=${styleMap({ margin: "0 0 0.125rem", fontSize: "1.05rem", fontWeight: "600", lineHeight: "1.2" })}
762
+ >${product.name}</h3>
763
+ ${product.tagline && isVisible("tagline") ? html`<p
764
+ class="sc-product-card__tagline"
765
+ data-product-tagline
766
+ style=${styleMap({ margin: "0 0 0.125rem", fontSize: "0.875rem", opacity: "0.75", lineHeight: "1.3" })}
767
+ >${product.tagline}</p>` : nothing}
768
+ ${product.rating && isVisible("rating") ? html`<div
769
+ class="sc-product-card__rating"
770
+ data-product-rating
771
+ data-rating-value=${product.rating.value}
772
+ data-rating-max=${product.rating.max ?? 5}
773
+ aria-label=${`${product.rating.value} out of ${product.rating.max ?? 5}, ${product.rating.count} reviews`}
774
+ style=${styleMap({ fontSize: "0.85rem" })}
775
+ >
776
+ <span class="sc-product-card__rating-stars">★ ${product.rating.value}</span>
777
+ <span class="sc-product-card__rating-count" style=${styleMap({ opacity: "0.7", marginLeft: "0.25rem" })}>(${product.rating.count.toLocaleString()})</span>
778
+ </div>` : nothing}
621
779
  </header>
622
- ${product.price ? html`<div class="sc-product-card__price" data-product-price>
623
- <span class="sc-product-card__amount">${priceText}</span>
624
- ${product.price.cadence ? html`<span class="sc-product-card__cadence">${product.price.cadence}</span>` : nothing}
625
- </div>` : nothing}
626
- ${product.badge ? html`<span
780
+ <div
781
+ class="sc-product-card__body"
782
+ data-product-body
783
+ style=${styleMap({ display: "block", marginTop: "0.5rem" })}
784
+ >
785
+ ${factOrder.map((key) => {
786
+ if (key === "price") {
787
+ if (!isVisible("price") || !product.price && !product.salePrice) return nothing;
788
+ return html`<div
789
+ class="sc-product-card__price"
790
+ data-product-price
791
+ style=${styleMap(priceBlockStyles)}
792
+ >
793
+ ${hasSalePrice && product.price ? html`<span
794
+ class="sc-product-card__amount-original"
795
+ data-product-price-original
796
+ style=${styleMap(priceOriginalStyles)}
797
+ >${product.price.amount}</span
798
+ >` : nothing}
799
+ <span
800
+ class="sc-product-card__amount"
801
+ data-product-price-sale=${hasSalePrice ? "true" : nothing}
802
+ style=${styleMap(priceSaleStyles)}
803
+ >${activePriceText}</span
804
+ >
805
+ ${activeCadence ? html`<span
806
+ class="sc-product-card__cadence"
807
+ style=${styleMap(priceCadenceStyles)}
808
+ >${activeCadence}</span
809
+ >` : nothing}
810
+ </div>`;
811
+ }
812
+ if (key === "availability") {
813
+ if (!isVisible("availability") || !product.availability) return nothing;
814
+ return html`<span
815
+ class="sc-product-card__availability"
816
+ data-product-availability
817
+ data-availability=${product.availability}
818
+ style=${styleMap(availabilityStyles)}
819
+ >${AVAILABILITY_LABELS[product.availability]}</span
820
+ >`;
821
+ }
822
+ if (key === "badge") {
823
+ if (!isVisible("badge") || !product.badge) return nothing;
824
+ return html`<span
627
825
  class="sc-product-card__badge"
628
826
  data-product-badge
629
827
  data-tone=${product.badge.tone}
630
828
  >${product.badge.label}</span
631
- >` : nothing}
632
- ${product.attributes.length > 0 ? html`<ul class="sc-product-card__attrs">
633
- ${product.attributes.map(
634
- (a) => html`<li data-attribute-emphasis=${a.emphasis ? "true" : "false"}>
635
- <span class="sc-product-card__attr-label">${a.label}</span>
636
- <span class="sc-product-card__attr-value">${a.value}</span>
637
- </li>`
638
- )}
639
- </ul>` : nothing}
640
- ${showFraming ? html`<p
829
+ >`;
830
+ }
831
+ if (key === "specs") {
832
+ if (!isVisible("specs") || specGroups.length === 0) return nothing;
833
+ return html`<div class="sc-product-card__specs" style=${styleMap(specsContainerStyles)}>
834
+ ${specGroups.map(
835
+ (g) => html`<section
836
+ class="sc-product-card__spec-section"
837
+ data-spec-section=${g.section}
838
+ style=${styleMap(specGroupStyles)}
839
+ >
840
+ <h4
841
+ class="sc-product-card__spec-heading"
842
+ style=${styleMap(specHeadingStyles)}
843
+ >${g.section}</h4>
844
+ <ul
845
+ class="sc-product-card__spec-rows"
846
+ style=${styleMap(specRowsStyles)}
847
+ >
848
+ ${g.rows.map(
849
+ (r) => html`<li
850
+ data-spec-row
851
+ data-attribute-emphasis=${r.emphasis ? "true" : "false"}
852
+ style=${styleMap(specRowStyles)}
853
+ >
854
+ <span
855
+ class="sc-product-card__spec-name"
856
+ data-spec-name
857
+ style=${styleMap(specNameStyles)}
858
+ >${r.name}</span
859
+ >
860
+ <span
861
+ class="sc-product-card__spec-value"
862
+ data-spec-value
863
+ style=${styleMap(specValueStyles)}
864
+ >${r.value}</span
865
+ >
866
+ ${r.unit ? html`<span
867
+ class="sc-product-card__spec-unit"
868
+ data-spec-unit
869
+ style=${styleMap(specUnitStyles)}
870
+ >${r.unit}</span
871
+ >` : nothing}
872
+ </li>`
873
+ )}
874
+ </ul>
875
+ </section>`
876
+ )}
877
+ </div>`;
878
+ }
879
+ if (key === "framing") {
880
+ if (!isVisible("framing") || !showFraming) return nothing;
881
+ return html`<p
641
882
  class="sc-product-card__framing"
642
883
  .innerHTML=${sanitizeHtml(product.framing ?? "")}
643
- ></p>` : nothing}
644
- <div class="sc-product-card__ctas">
884
+ ></p>`;
885
+ }
886
+ return nothing;
887
+ })}
888
+ <div class="sc-product-card__ctas" style=${styleMap(ctasStyles)}>
645
889
  ${product.ctas.map(
646
890
  (c, i) => html`<a
647
891
  class="sc-product-card__cta"
@@ -655,6 +899,7 @@ function renderProductCard(product, density, resolved) {
655
899
  >`
656
900
  )}
657
901
  </div>
902
+ </div>
658
903
  </article>
659
904
  `;
660
905
  }
@@ -786,6 +1031,7 @@ renderRow_fn = function(product) {
786
1031
  src=${imgSrc}
787
1032
  alt=${product.image.alt}
788
1033
  loading="lazy"
1034
+ style="width:48px;height:48px;object-fit:cover;border-radius:6px;flex-shrink:0;"
789
1035
  />
790
1036
  <span class="sc-product-comparison__name">${product.name}</span>
791
1037
  ${product.price ? html2`<span class="sc-product-comparison__price"
@@ -808,7 +1054,7 @@ renderRow_fn = function(product) {
808
1054
  </span>
809
1055
  </div>
810
1056
  ${expanded ? html2`<div class="sc-product-comparison__panel" id=${regionId} role="region">
811
- ${product.attributes.length > 0 ? html2`<ul class="sc-product-comparison__attrs">
1057
+ ${product.attributes && product.attributes.length > 0 ? html2`<ul class="sc-product-comparison__attrs">
812
1058
  ${product.attributes.map(
813
1059
  (a) => html2`<li data-attribute-emphasis=${a.emphasis ? "true" : "false"}>
814
1060
  <span>${a.label}</span><span>${a.value}</span>
@@ -946,15 +1192,20 @@ var ProductHeroLit = class extends LitElement4 {
946
1192
  const resolved = this.resolved[product.id];
947
1193
  const imgSrc = resolved?.image ?? product.image.src;
948
1194
  const priceText = resolved?.price ?? product.price?.amount;
1195
+ const isTopLayout = layout === "image-top";
1196
+ const sectionStyles = isTopLayout ? "display:block;" : `display:flex;flex-direction:${layout === "image-right" ? "row-reverse" : "row"};gap:1rem;align-items:flex-start;`;
1197
+ const imageStyles = isTopLayout ? "width:100%;max-height:160px;object-fit:cover;border-radius:8px;display:block;margin-bottom:0.75rem;" : "width:128px;height:128px;object-fit:cover;border-radius:8px;flex-shrink:0;";
1198
+ const contentStyles = isTopLayout ? "" : "flex:1;min-width:0;";
949
1199
  return html4`
950
- <section class="sc-product-hero" data-hero-layout=${layout}>
1200
+ <section class="sc-product-hero" data-hero-layout=${layout} style=${sectionStyles}>
951
1201
  <img
952
1202
  class="sc-product-hero__image"
953
1203
  src=${imgSrc}
954
1204
  alt=${product.image.alt}
955
1205
  loading="lazy"
1206
+ style=${imageStyles}
956
1207
  />
957
- <div class="sc-product-hero__content">
1208
+ <div class="sc-product-hero__content" style=${contentStyles}>
958
1209
  <h2 class="sc-product-hero__name">${product.name}</h2>
959
1210
  ${product.tagline ? html4`<p class="sc-product-hero__tagline">${product.tagline}</p>` : nothing4}
960
1211
  ${product.price ? html4`<p class="sc-product-hero__price">
@@ -1098,4 +1349,4 @@ export {
1098
1349
  runtime,
1099
1350
  runtime_default
1100
1351
  };
1101
- //# sourceMappingURL=chunk-HFT5OENK.js.map
1352
+ //# sourceMappingURL=chunk-QZZVFYL6.js.map