@nymbalhq/web-components 0.2.0 → 1.1.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/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { NymbalClient } from '@nymbalhq/sdk';
1
+ import { NymbalClient, PaymentConfirmContext, PaymentConfirmation } from '@nymbalhq/sdk';
2
2
 
3
3
  declare function registerClient(client: NymbalClient): void;
4
4
  declare function getClient(): NymbalClient;
5
5
 
6
+ /** `HTMLElement`, or a stand-in on a server, where the module is imported but no element is ever made. */
6
7
  declare const HTMLElementBase$1: {
7
8
  new (): HTMLElement;
8
9
  prototype: HTMLElement;
@@ -53,10 +54,8 @@ declare class NymbalMiniCart extends NymbalElement {
53
54
  }
54
55
 
55
56
  declare class NymbalVariantSelector extends NymbalElement {
56
- static get observedAttributes(): string[];
57
57
  protected setup(): void;
58
58
  protected render(): void;
59
- attributeChangedCallback(): void;
60
59
  private getOptionGroups;
61
60
  private esc;
62
61
  }
@@ -65,6 +64,7 @@ declare class NymbalQuantitySelector extends NymbalElement {
65
64
  static get observedAttributes(): string[];
66
65
  private _value;
67
66
  protected setup(): void;
67
+ private bounds;
68
68
  protected render(): void;
69
69
  attributeChangedCallback(name: string): void;
70
70
  private emitChange;
@@ -84,10 +84,17 @@ declare class NymbalSearchBar extends NymbalElement {
84
84
  private formatPrice;
85
85
  }
86
86
 
87
+ /**
88
+ * The facet filter. By default it reads the `facets` attribute and filters the product list. With `source="search"`
89
+ * it renders the search's own facets and narrows the search to one category: choosing an option selects it, choosing
90
+ * the selected option again clears it.
91
+ */
87
92
  declare class NymbalProductFilter extends NymbalElement {
88
93
  static get observedAttributes(): string[];
89
94
  protected setup(): void;
90
95
  protected render(): void;
96
+ private renderSearch;
97
+ private isSearch;
91
98
  attributeChangedCallback(): void;
92
99
  private getFacets;
93
100
  private esc;
@@ -105,6 +112,50 @@ declare class NymbalToast extends HTMLElementBase {
105
112
  static showGlobal(message: string, type?: 'success' | 'error' | 'warning' | 'info'): void;
106
113
  }
107
114
 
115
+ /** `pending` while a driver mounts; `error` when no payment can be taken, which a checkout must not submit past. */
116
+ type PaymentElementStatus = 'pending' | 'ready' | 'error';
117
+ /**
118
+ * <nymbal-payment-element client='<json>'>: renders the payments adapter's client half (GET /api/capabilities'
119
+ * `payments.client`) through the driver the SDK registry holds under its `driver` name, and confirms a payment
120
+ * through it. It emits `nymbal:payment:status` whenever `status` changes.
121
+ */
122
+ declare class NymbalPaymentElement extends HTMLElementBase$1 {
123
+ static get observedAttributes(): string[];
124
+ private _status;
125
+ private _error;
126
+ private _driver;
127
+ private _mounted;
128
+ private _rendered;
129
+ private _generation;
130
+ get status(): PaymentElementStatus;
131
+ connectedCallback(): void;
132
+ attributeChangedCallback(): void;
133
+ /**
134
+ * Completes a payment the API left `requires_action`, once the driver has mounted, handing the driver the
135
+ * checkout's `context`; the driver's answer as is.
136
+ */
137
+ confirm(clientSecret: string, context?: PaymentConfirmContext): Promise<PaymentConfirmation>;
138
+ private render;
139
+ private fail;
140
+ private setStatus;
141
+ }
142
+
143
+ /**
144
+ * <nymbal-sort-dropdown sort-keys='[…]' value default-sort>: one option per sort key the store reports, in its order,
145
+ * labelled from PRODUCT_SORT_LABELS. A `value` the keys lack shows `default-sort`. Choosing an option emits
146
+ * `nymbal:sort:changed` with `{ sort }`. A `<select>` already inside it (a server render's) is the one it fills.
147
+ */
148
+ declare class NymbalSortDropdown extends HTMLElementBase$1 {
149
+ static get observedAttributes(): string[];
150
+ private _select;
151
+ /** The key the dropdown shows. */
152
+ get value(): string;
153
+ connectedCallback(): void;
154
+ attributeChangedCallback(): void;
155
+ private selectElement;
156
+ private render;
157
+ }
158
+
108
159
  declare function registerComponents(): void;
109
160
 
110
- export { NymbalAddToCart, NymbalCartDrawer, NymbalElement, NymbalMiniCart, NymbalProductFilter, NymbalQuantitySelector, NymbalSearchBar, NymbalToast, NymbalVariantSelector, getClient, injectStyles, registerClient, registerComponents };
161
+ export { NymbalAddToCart, NymbalCartDrawer, NymbalElement, NymbalMiniCart, NymbalPaymentElement, NymbalProductFilter, NymbalQuantitySelector, NymbalSearchBar, NymbalSortDropdown, NymbalToast, NymbalVariantSelector, type PaymentElementStatus, getClient, injectStyles, registerClient, registerComponents };
package/dist/index.js CHANGED
@@ -79,9 +79,16 @@ nymbal-add-to-cart button:disabled {
79
79
  cursor: not-allowed;
80
80
  }
81
81
  `;
82
+ var MIN_QTY = 1;
83
+ var MAX_QTY = 99;
84
+ function quantityOf(attr) {
85
+ const n = attr === null || attr.trim() === "" ? Number.NaN : Number(attr);
86
+ const whole = Number.isInteger(n) ? n : MIN_QTY;
87
+ return Math.min(MAX_QTY, Math.max(MIN_QTY, whole));
88
+ }
82
89
  var NymbalAddToCart = class extends NymbalElement {
83
90
  static get observedAttributes() {
84
- return ["variant-id", "disabled"];
91
+ return ["variant-id", "qty", "disabled"];
85
92
  }
86
93
  button;
87
94
  setup() {
@@ -110,13 +117,14 @@ var NymbalAddToCart = class extends NymbalElement {
110
117
  handleClick = async () => {
111
118
  const variantId = this.getAttribute("variant-id");
112
119
  if (!variantId) return;
120
+ const qty = quantityOf(this.getAttribute("qty"));
113
121
  try {
114
- await this.client.cart.addItem(variantId);
122
+ await this.client.cart.addItem(variantId, qty);
115
123
  this.dispatchEvent(
116
124
  new CustomEvent("nymbal:cart:item-added", {
117
125
  bubbles: true,
118
126
  composed: true,
119
- detail: { variantId }
127
+ detail: { variantId, qty }
120
128
  })
121
129
  );
122
130
  } catch {
@@ -449,10 +457,10 @@ nymbal-variant-selector .nymbal-option-btn:disabled {
449
457
  cursor: not-allowed;
450
458
  }
451
459
  `;
460
+ function optionsOf(variant) {
461
+ return Array.isArray(variant.options) ? variant.options : [];
462
+ }
452
463
  var NymbalVariantSelector = class extends NymbalElement {
453
- static get observedAttributes() {
454
- return ["options"];
455
- }
456
464
  setup() {
457
465
  injectStyles("nymbal-variant-selector", STYLES4);
458
466
  this.subscribeToStore(this.client.product);
@@ -471,9 +479,7 @@ var NymbalVariantSelector = class extends NymbalElement {
471
479
  <span class="nymbal-option-label">${this.esc(group.name)}</span>
472
480
  <div class="nymbal-option-buttons">
473
481
  ${group.values.map((value) => {
474
- const isSelected = selected?.options.some(
475
- (o) => o.name === group.name && o.value === value
476
- ) ?? false;
482
+ const isSelected = selected !== null && optionsOf(selected).some((o) => o.name === group.name && o.value === value);
477
483
  return `<button
478
484
  class="nymbal-option-btn"
479
485
  data-testid="variant-option-${this.esc(group.name)}-${this.esc(value)}"
@@ -493,26 +499,14 @@ var NymbalVariantSelector = class extends NymbalElement {
493
499
  if (!optionName || !optionValue || !product) return;
494
500
  const currentSelections = /* @__PURE__ */ new Map();
495
501
  if (selected) {
496
- for (const opt of selected.options) {
502
+ for (const opt of optionsOf(selected)) {
497
503
  currentSelections.set(opt.name, opt.value);
498
504
  }
499
505
  }
500
506
  currentSelections.set(optionName, optionValue);
501
- const match = product.variants.find((v) => {
502
- const vRaw = v.options;
503
- let vOpts = [];
504
- if (typeof vRaw === "string") {
505
- try {
506
- vOpts = JSON.parse(vRaw);
507
- } catch (e) {
508
- console.warn("[variant-selector] malformed variant options JSON", e);
509
- vOpts = [];
510
- }
511
- } else if (Array.isArray(vRaw)) {
512
- vOpts = vRaw;
513
- }
514
- return vOpts.every((o) => currentSelections.get(o.name) === o.value);
515
- });
507
+ const match = product.variants.find(
508
+ (v) => optionsOf(v).every((o) => currentSelections.get(o.name) === o.value)
509
+ );
516
510
  if (match) {
517
511
  this.client.product.selectVariant(match);
518
512
  this.dispatchEvent(
@@ -526,33 +520,10 @@ var NymbalVariantSelector = class extends NymbalElement {
526
520
  });
527
521
  });
528
522
  }
529
- attributeChangedCallback() {
530
- if (this.isConnected) this.requestRender();
531
- }
532
523
  getOptionGroups(product) {
533
- const optionsAttr = this.getAttribute("options");
534
- if (optionsAttr) {
535
- try {
536
- return JSON.parse(optionsAttr);
537
- } catch (e) {
538
- console.warn("[variant-selector] malformed options attribute, falling back to variant derivation", e);
539
- }
540
- }
541
524
  const groups = /* @__PURE__ */ new Map();
542
525
  for (const variant of product.variants) {
543
- const raw = variant.options;
544
- let opts = [];
545
- if (typeof raw === "string") {
546
- try {
547
- opts = JSON.parse(raw);
548
- } catch (e) {
549
- console.warn("[variant-selector] malformed variant options in getOptionGroups", e);
550
- opts = [];
551
- }
552
- } else if (Array.isArray(raw)) {
553
- opts = raw;
554
- }
555
- for (const opt of opts) {
526
+ for (const opt of optionsOf(variant)) {
556
527
  let values = groups.get(opt.name);
557
528
  if (!values) {
558
529
  values = /* @__PURE__ */ new Set();
@@ -624,6 +595,11 @@ nymbal-quantity-selector input::-webkit-inner-spin-button {
624
595
  margin: 0;
625
596
  }
626
597
  `;
598
+ function wholeQuantity(raw, min, max) {
599
+ const n = raw === null || raw.trim() === "" ? Number.NaN : Number(raw);
600
+ if (Number.isNaN(n)) return min;
601
+ return Math.min(max, Math.max(min, Math.trunc(n)));
602
+ }
627
603
  var NymbalQuantitySelector = class extends NymbalElement {
628
604
  static get observedAttributes() {
629
605
  return ["value", "min", "max"];
@@ -631,11 +607,13 @@ var NymbalQuantitySelector = class extends NymbalElement {
631
607
  _value = 1;
632
608
  setup() {
633
609
  injectStyles("nymbal-quantity-selector", STYLES5);
634
- this._value = Number(this.getAttribute("value")) || 1;
610
+ this._value = wholeQuantity(this.getAttribute("value"), ...this.bounds());
611
+ }
612
+ bounds() {
613
+ return [Number(this.getAttribute("min")) || 1, Number(this.getAttribute("max")) || 99];
635
614
  }
636
615
  render() {
637
- const min = Number(this.getAttribute("min")) || 1;
638
- const max = Number(this.getAttribute("max")) || 99;
616
+ const [min, max] = this.bounds();
639
617
  this.innerHTML = `
640
618
  <button data-testid="qty-decrement" aria-label="Decrease quantity" ${this._value <= min ? "disabled" : ""}>&#8722;</button>
641
619
  <input data-testid="qty-input" type="number" value="${this._value}" min="${min}" max="${max}" aria-label="Quantity">
@@ -657,15 +635,14 @@ var NymbalQuantitySelector = class extends NymbalElement {
657
635
  });
658
636
  const input = this.querySelector("input");
659
637
  input?.addEventListener("change", () => {
660
- const val = Math.min(max, Math.max(min, Number(input.value) || min));
661
- this._value = val;
638
+ this._value = wholeQuantity(input.value, min, max);
662
639
  this.emitChange();
663
640
  this.render();
664
641
  });
665
642
  }
666
643
  attributeChangedCallback(name) {
667
644
  if (name === "value") {
668
- this._value = Number(this.getAttribute("value")) || 1;
645
+ this._value = wholeQuantity(this.getAttribute("value"), ...this.bounds());
669
646
  }
670
647
  if (this.isConnected) this.requestRender();
671
648
  }
@@ -885,13 +862,18 @@ nymbal-product-filter .nymbal-filter-count {
885
862
  `;
886
863
  var NymbalProductFilter = class extends NymbalElement {
887
864
  static get observedAttributes() {
888
- return ["facets"];
865
+ return ["facets", "source"];
889
866
  }
890
867
  setup() {
891
868
  injectStyles("nymbal-product-filter", STYLES7);
892
869
  this.subscribeToStore(this.client.productList);
870
+ this.subscribeToStore(this.client.search);
893
871
  }
894
872
  render() {
873
+ if (this.isSearch()) {
874
+ this.renderSearch();
875
+ return;
876
+ }
895
877
  const state = this.client.productList.getState();
896
878
  const facets = this.getFacets();
897
879
  if (facets.length === 0) {
@@ -945,6 +927,42 @@ var NymbalProductFilter = class extends NymbalElement {
945
927
  });
946
928
  });
947
929
  }
930
+ renderSearch() {
931
+ const { facets, category } = this.client.search.getState();
932
+ const shown = facets.filter((f) => f.field === "category");
933
+ this.innerHTML = shown.map(
934
+ (facet) => `
935
+ <div class="nymbal-filter-group" data-testid="filter-group-${this.esc(facet.field)}">
936
+ <span class="nymbal-filter-label">${this.esc(facet.label)}</span>
937
+ ${facet.values.map(
938
+ (v) => `
939
+ <label class="nymbal-filter-option" data-testid="filter-option-${this.esc(v.value)}">
940
+ <input type="checkbox" data-filter-value="${this.esc(v.value)}" ${v.value === category ? "checked" : ""}>
941
+ <span>${this.esc(v.label ?? v.value)}</span>
942
+ ${v.count !== void 0 ? `<span class="nymbal-filter-count">(${v.count})</span>` : ""}
943
+ </label>
944
+ `
945
+ ).join("")}
946
+ </div>
947
+ `
948
+ ).join("");
949
+ this.querySelectorAll('input[type="checkbox"]').forEach((input) => {
950
+ input.addEventListener("change", () => {
951
+ const value = input.dataset["filterValue"];
952
+ void this.client.search.setCategory(input.checked ? value : null);
953
+ this.dispatchEvent(
954
+ new CustomEvent("nymbal:filter:changed", {
955
+ bubbles: true,
956
+ composed: true,
957
+ detail: { name: "category", value: input.checked ? value : "" }
958
+ })
959
+ );
960
+ });
961
+ });
962
+ }
963
+ isSearch() {
964
+ return this.getAttribute("source") === "search";
965
+ }
948
966
  attributeChangedCallback() {
949
967
  if (this.isConnected) this.requestRender();
950
968
  }
@@ -1077,6 +1095,289 @@ var NymbalToast = class extends HTMLElementBase2 {
1077
1095
  }
1078
1096
  };
1079
1097
 
1098
+ // src/components/payment-element.ts
1099
+ import {
1100
+ getPaymentDriver
1101
+ } from "@nymbalhq/sdk";
1102
+ var STYLES9 = `
1103
+ nymbal-payment-element {
1104
+ display: block;
1105
+ font-family: var(--nymbal-font-family);
1106
+ }
1107
+ nymbal-payment-element .nymbal-payment-note {
1108
+ margin: 0;
1109
+ font-size: var(--nymbal-font-size-sm, 0.875rem);
1110
+ color: var(--nymbal-color-text-muted, #71717a);
1111
+ }
1112
+ nymbal-payment-element .nymbal-payment-card {
1113
+ padding: var(--nymbal-spacing-sm, 0.5rem) var(--nymbal-spacing-md, 1rem);
1114
+ border: 1px solid var(--nymbal-color-border, #e4e4e7);
1115
+ border-radius: var(--nymbal-radius-md, 0.5rem);
1116
+ background: var(--nymbal-color-surface, #fff);
1117
+ min-height: 40px;
1118
+ }
1119
+ nymbal-payment-element .nymbal-payment-error {
1120
+ margin: 0;
1121
+ font-size: var(--nymbal-font-size-sm, 0.875rem);
1122
+ color: var(--nymbal-color-error, #dc2626);
1123
+ }
1124
+ `;
1125
+ var NO_PAYMENT_STEP = { ok: true, confirmed: false };
1126
+ function parseClient(value) {
1127
+ if (value === null || value === "" || value === "null") return null;
1128
+ try {
1129
+ const parsed = JSON.parse(value);
1130
+ if (parsed === null) return null;
1131
+ if (typeof parsed.driver !== "string" || typeof parsed.options !== "object") return void 0;
1132
+ return { driver: parsed.driver, options: { ...parsed.options ?? {} } };
1133
+ } catch {
1134
+ return void 0;
1135
+ }
1136
+ }
1137
+ var NymbalPaymentElement = class extends HTMLElementBase {
1138
+ static get observedAttributes() {
1139
+ return ["client"];
1140
+ }
1141
+ _status = "pending";
1142
+ _error = "";
1143
+ _driver = null;
1144
+ _mounted = Promise.resolve(true);
1145
+ _rendered = void 0;
1146
+ _generation = 0;
1147
+ get status() {
1148
+ return this._status;
1149
+ }
1150
+ connectedCallback() {
1151
+ injectStyles("nymbal-payment-element", STYLES9);
1152
+ this.render();
1153
+ }
1154
+ attributeChangedCallback() {
1155
+ if (this.isConnected) this.render();
1156
+ }
1157
+ /**
1158
+ * Completes a payment the API left `requires_action`, once the driver has mounted, handing the driver the
1159
+ * checkout's `context`; the driver's answer as is.
1160
+ */
1161
+ async confirm(clientSecret, context) {
1162
+ const driver = this._driver;
1163
+ if (this._status === "error") return { ok: false, error: this._error };
1164
+ if (driver === null) return NO_PAYMENT_STEP;
1165
+ if (!await this._mounted) return { ok: false, error: this._error };
1166
+ return driver.confirm(clientSecret, context);
1167
+ }
1168
+ render() {
1169
+ const attribute = this.getAttribute("client");
1170
+ if (attribute === this._rendered) return;
1171
+ this._rendered = attribute;
1172
+ const generation = ++this._generation;
1173
+ this._driver = null;
1174
+ this._mounted = Promise.resolve(true);
1175
+ this.replaceChildren();
1176
+ const client = parseClient(attribute);
1177
+ if (client === void 0)
1178
+ return this.fail("The store sent a payment client this page cannot read.");
1179
+ if (client === null) return this.setStatus("ready");
1180
+ const driver = getPaymentDriver(client.driver);
1181
+ if (driver === void 0) {
1182
+ return this.fail(`Payment method "${client.driver}" is not available on this storefront.`);
1183
+ }
1184
+ this._driver = driver;
1185
+ this.setStatus("pending");
1186
+ this._mounted = Promise.resolve().then(() => driver.mount(this, client.options)).then(
1187
+ () => {
1188
+ if (generation === this._generation) this.setStatus("ready");
1189
+ return true;
1190
+ },
1191
+ (err) => {
1192
+ if (generation === this._generation) {
1193
+ this.fail(err instanceof Error ? err.message : "The payment form could not be loaded.");
1194
+ }
1195
+ return false;
1196
+ }
1197
+ );
1198
+ }
1199
+ fail(message) {
1200
+ this._error = message;
1201
+ const p = document.createElement("p");
1202
+ p.className = "nymbal-payment-error";
1203
+ p.setAttribute("data-testid", "payment-element-error");
1204
+ p.setAttribute("role", "alert");
1205
+ p.textContent = message;
1206
+ this.replaceChildren(p);
1207
+ this.setStatus("error");
1208
+ }
1209
+ setStatus(status) {
1210
+ if (status === this._status) return;
1211
+ this._status = status;
1212
+ this.dispatchEvent(
1213
+ new CustomEvent("nymbal:payment:status", {
1214
+ bubbles: true,
1215
+ composed: true,
1216
+ detail: { status }
1217
+ })
1218
+ );
1219
+ }
1220
+ };
1221
+
1222
+ // src/components/sort-dropdown.ts
1223
+ import { DEFAULT_PRODUCT_SORT, PRODUCT_SORT_LABELS } from "@nymbalhq/types";
1224
+ function parseKeys(value) {
1225
+ if (!value) return [];
1226
+ try {
1227
+ const parsed = JSON.parse(value);
1228
+ return Array.isArray(parsed) ? parsed.filter((k) => typeof k === "string") : [];
1229
+ } catch (err) {
1230
+ console.error("[nymbal-sort-dropdown] failed to parse sort-keys attribute", err);
1231
+ return [];
1232
+ }
1233
+ }
1234
+ var NymbalSortDropdown = class extends HTMLElementBase {
1235
+ static get observedAttributes() {
1236
+ return ["sort-keys", "value", "default-sort"];
1237
+ }
1238
+ _select = null;
1239
+ /** The key the dropdown shows. */
1240
+ get value() {
1241
+ return this._select?.value ?? "";
1242
+ }
1243
+ connectedCallback() {
1244
+ this.render();
1245
+ }
1246
+ attributeChangedCallback() {
1247
+ if (this.isConnected) this.render();
1248
+ }
1249
+ selectElement() {
1250
+ if (this._select !== null && this.contains(this._select)) return this._select;
1251
+ let select = this.querySelector("select");
1252
+ if (select === null) {
1253
+ select = document.createElement("select");
1254
+ select.setAttribute("aria-label", "Sort products");
1255
+ this.appendChild(select);
1256
+ }
1257
+ if (!select.hasAttribute("data-testid")) select.setAttribute("data-testid", "sort-dropdown");
1258
+ select.addEventListener("change", () => {
1259
+ this.dispatchEvent(
1260
+ new CustomEvent("nymbal:sort:changed", {
1261
+ bubbles: true,
1262
+ composed: true,
1263
+ detail: { sort: select.value }
1264
+ })
1265
+ );
1266
+ });
1267
+ this._select = select;
1268
+ return select;
1269
+ }
1270
+ render() {
1271
+ const keys = parseKeys(this.getAttribute("sort-keys"));
1272
+ const wanted = this.getAttribute("value") ?? "";
1273
+ const fallback = this.getAttribute("default-sort") ?? DEFAULT_PRODUCT_SORT;
1274
+ const selected = keys.includes(wanted) ? wanted : keys.includes(fallback) ? fallback : keys[0];
1275
+ const select = this.selectElement();
1276
+ select.replaceChildren(
1277
+ ...keys.map((key) => {
1278
+ const option = document.createElement("option");
1279
+ option.value = key;
1280
+ option.textContent = PRODUCT_SORT_LABELS[key] ?? key;
1281
+ return option;
1282
+ })
1283
+ );
1284
+ if (selected !== void 0) select.value = selected;
1285
+ }
1286
+ };
1287
+
1288
+ // src/index.ts
1289
+ import { getPaymentDriver as getPaymentDriver2, registerPaymentDriver } from "@nymbalhq/sdk";
1290
+
1291
+ // src/payment-drivers/test-mode.ts
1292
+ var testModeDriver = {
1293
+ mount(el) {
1294
+ el.innerHTML = '<p class="nymbal-payment-note">Test mode \u2014 payment skipped</p>';
1295
+ },
1296
+ confirm() {
1297
+ return Promise.resolve({ ok: true, confirmed: false });
1298
+ }
1299
+ };
1300
+
1301
+ // src/payment-drivers/stripe.ts
1302
+ var STRIPE_JS = "https://js.stripe.com/v3/";
1303
+ function billingDetails(context) {
1304
+ const details = {};
1305
+ if (context?.email) details.email = context.email;
1306
+ const billing = context?.billing;
1307
+ if (billing) {
1308
+ const name = `${billing.firstName} ${billing.lastName}`.trim();
1309
+ if (name) details.name = name;
1310
+ details.address = {
1311
+ line1: billing.addressLine1,
1312
+ ...billing.addressLine2 ? { line2: billing.addressLine2 } : {},
1313
+ city: billing.city,
1314
+ state: billing.region,
1315
+ postal_code: billing.postalCode,
1316
+ country: billing.country
1317
+ };
1318
+ }
1319
+ return Object.keys(details).length > 0 ? details : void 0;
1320
+ }
1321
+ var loading = null;
1322
+ function loadStripeJs() {
1323
+ const win = window;
1324
+ if (win.Stripe) return Promise.resolve(win.Stripe);
1325
+ loading ??= new Promise((resolve, reject) => {
1326
+ const script = document.createElement("script");
1327
+ script.src = STRIPE_JS;
1328
+ script.async = true;
1329
+ script.addEventListener("load", () => {
1330
+ if (win.Stripe) resolve(win.Stripe);
1331
+ else reject(new Error("Stripe.js loaded but window.Stripe is missing."));
1332
+ });
1333
+ script.addEventListener("error", () => {
1334
+ loading = null;
1335
+ script.remove();
1336
+ reject(new Error("Stripe.js could not be loaded."));
1337
+ });
1338
+ document.head.appendChild(script);
1339
+ });
1340
+ return loading;
1341
+ }
1342
+ var instances = /* @__PURE__ */ new Map();
1343
+ var mounted = [];
1344
+ var stripeDriver = {
1345
+ async mount(el, options) {
1346
+ const key = options["publishableKey"];
1347
+ if (!key)
1348
+ throw new Error("Card payments are not configured: the store has no Stripe publishable key.");
1349
+ const factory = await loadStripeJs();
1350
+ let stripe = instances.get(key);
1351
+ if (stripe === void 0) {
1352
+ stripe = factory(key);
1353
+ instances.set(key, stripe);
1354
+ }
1355
+ const target = document.createElement("div");
1356
+ target.className = "nymbal-payment-card";
1357
+ el.replaceChildren(target);
1358
+ const card = stripe.elements().create("card", {
1359
+ style: {
1360
+ base: { fontSize: "16px", color: "#1a1a1a", "::placeholder": { color: "#9ca3af" } }
1361
+ }
1362
+ });
1363
+ card.mount(target);
1364
+ mounted = [
1365
+ ...mounted.filter((m) => m.host.isConnected && m.host !== el),
1366
+ { host: el, stripe, card }
1367
+ ];
1368
+ },
1369
+ async confirm(clientSecret, context) {
1370
+ const current = mounted.findLast((m) => m.host.isConnected) ?? mounted.at(-1);
1371
+ if (current === void 0) return { ok: false, error: "The card form is not ready." };
1372
+ const billing_details = billingDetails(context);
1373
+ const result = await current.stripe.confirmCardPayment(clientSecret, {
1374
+ payment_method: { card: current.card, ...billing_details ? { billing_details } : {} }
1375
+ });
1376
+ if (result.error) return { ok: false, error: result.error.message ?? "Payment failed." };
1377
+ return { ok: true, confirmed: result.paymentIntent?.status === "succeeded" };
1378
+ }
1379
+ };
1380
+
1080
1381
  // src/index.ts
1081
1382
  function registerComponents() {
1082
1383
  const components = [
@@ -1087,8 +1388,17 @@ function registerComponents() {
1087
1388
  ["nymbal-quantity-selector", NymbalQuantitySelector],
1088
1389
  ["nymbal-search-bar", NymbalSearchBar],
1089
1390
  ["nymbal-product-filter", NymbalProductFilter],
1090
- ["nymbal-toast", NymbalToast]
1391
+ ["nymbal-toast", NymbalToast],
1392
+ ["nymbal-payment-element", NymbalPaymentElement],
1393
+ ["nymbal-sort-dropdown", NymbalSortDropdown]
1394
+ ];
1395
+ const drivers = [
1396
+ ["test-mode", testModeDriver],
1397
+ ["stripe", stripeDriver]
1091
1398
  ];
1399
+ for (const [name, driver] of drivers) {
1400
+ if (getPaymentDriver2(name) === void 0) registerPaymentDriver(name, driver);
1401
+ }
1092
1402
  for (const [name, ctor] of components) {
1093
1403
  if (!customElements.get(name)) {
1094
1404
  customElements.define(name, ctor);
@@ -1100,9 +1410,11 @@ export {
1100
1410
  NymbalCartDrawer,
1101
1411
  NymbalElement,
1102
1412
  NymbalMiniCart,
1413
+ NymbalPaymentElement,
1103
1414
  NymbalProductFilter,
1104
1415
  NymbalQuantitySelector,
1105
1416
  NymbalSearchBar,
1417
+ NymbalSortDropdown,
1106
1418
  NymbalToast,
1107
1419
  NymbalVariantSelector,
1108
1420
  getClient,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/registry.ts","../src/base-element.ts","../src/styles.ts","../src/components/add-to-cart.ts","../src/components/cart-drawer.ts","../src/components/mini-cart.ts","../src/components/variant-selector.ts","../src/components/quantity-selector.ts","../src/components/search-bar.ts","../src/components/product-filter.ts","../src/components/toast.ts","../src/index.ts"],"sourcesContent":["import type { NymbalClient } from '@nymbalhq/sdk'\n\nconst CLIENT_KEY = Symbol.for('nymbal.client')\n\nexport function registerClient(client: NymbalClient): void {\n (globalThis as Record<symbol, unknown>)[CLIENT_KEY] = client\n}\n\nexport function getClient(): NymbalClient {\n const client = (globalThis as Record<symbol, unknown>)[CLIENT_KEY] as NymbalClient | undefined\n if (!client) {\n throw new Error('NymbalClient not registered. Call registerClient() before using components.')\n }\n return client\n}\n","import type { NymbalClient } from '@nymbalhq/sdk'\nimport { getClient } from './registry.js'\n\nconst HTMLElementBase =\n typeof HTMLElement !== 'undefined' ? HTMLElement : (class {} as unknown as typeof HTMLElement)\n\nexport abstract class NymbalElement extends HTMLElementBase {\n private _unsubs: Array<() => void> = []\n private _renderQueued = false\n\n protected get client(): NymbalClient {\n return getClient()\n }\n\n protected subscribeToStore(store: { subscribe(fn: () => void): () => void }): void {\n this._unsubs.push(store.subscribe(() => this.requestRender()))\n }\n\n protected requestRender(): void {\n if (this._renderQueued) return\n this._renderQueued = true\n queueMicrotask(() => {\n this._renderQueued = false\n this.render()\n })\n }\n\n connectedCallback(): void {\n this.setup()\n this.render()\n }\n\n disconnectedCallback(): void {\n for (const fn of this._unsubs) fn()\n this._unsubs = []\n }\n\n protected abstract setup(): void\n protected abstract render(): void\n}\n","const injected = new Set<string>()\n\nexport function injectStyles(tagName: string, css: string): void {\n if (injected.has(tagName)) return\n if (typeof document === 'undefined') return\n const style = document.createElement('style')\n style.setAttribute('data-nymbal', tagName)\n style.textContent = css\n document.head.appendChild(style)\n injected.add(tagName)\n}\n","import { NymbalElement } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-add-to-cart {\n display: inline-block;\n}\nnymbal-add-to-cart button {\n font-family: var(--nymbal-font-family);\n font-size: var(--nymbal-font-size-base);\n font-weight: var(--nymbal-font-weight-medium);\n background: var(--nymbal-color-primary);\n color: var(--nymbal-color-text-inverse);\n border: none;\n border-radius: var(--nymbal-radius-md);\n padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-lg);\n cursor: pointer;\n transition: background var(--nymbal-transition-fast);\n min-width: 140px;\n}\nnymbal-add-to-cart button:hover:not(:disabled) {\n background: var(--nymbal-color-primary-hover);\n}\nnymbal-add-to-cart button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n`\n\nexport class NymbalAddToCart extends NymbalElement {\n static get observedAttributes(): string[] {\n return ['variant-id', 'disabled']\n }\n\n private button!: HTMLButtonElement\n\n protected setup(): void {\n injectStyles('nymbal-add-to-cart', STYLES)\n this.subscribeToStore(this.client.cart)\n }\n\n protected render(): void {\n const variantId = this.getAttribute('variant-id')\n const isDisabled = this.hasAttribute('disabled')\n const cartState = this.client.cart.getState()\n\n if (!this.button) {\n this.button = document.createElement('button')\n this.button.setAttribute('type', 'button')\n this.button.setAttribute('data-testid', 'add-to-cart')\n this.button.addEventListener('click', this.handleClick)\n this.innerHTML = ''\n this.appendChild(this.button)\n }\n\n this.button.disabled = isDisabled || cartState.loading || !variantId\n this.button.setAttribute('aria-busy', String(cartState.loading))\n this.button.textContent = cartState.loading ? 'Adding...' : 'Add to Cart'\n }\n\n attributeChangedCallback(): void {\n if (this.isConnected) this.requestRender()\n }\n\n private handleClick = async (): Promise<void> => {\n const variantId = this.getAttribute('variant-id')\n if (!variantId) return\n try {\n await this.client.cart.addItem(variantId)\n this.dispatchEvent(\n new CustomEvent('nymbal:cart:item-added', {\n bubbles: true,\n composed: true,\n detail: { variantId },\n }),\n )\n } catch {\n // error is stored in cart state\n }\n }\n}\n","import { NymbalElement } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-cart-drawer {\n display: block;\n}\nnymbal-cart-drawer .nymbal-drawer-overlay {\n position: fixed;\n inset: 0;\n background: var(--nymbal-color-overlay);\n z-index: var(--nymbal-z-drawer);\n opacity: 0;\n visibility: hidden;\n transition: opacity var(--nymbal-transition-normal), visibility var(--nymbal-transition-normal);\n}\nnymbal-cart-drawer[open] .nymbal-drawer-overlay {\n opacity: 1;\n visibility: visible;\n}\nnymbal-cart-drawer .nymbal-drawer-panel {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: min(400px, 90vw);\n background: var(--nymbal-color-surface);\n z-index: var(--nymbal-z-drawer);\n transform: translateX(100%);\n transition: transform var(--nymbal-transition-normal);\n display: flex;\n flex-direction: column;\n font-family: var(--nymbal-font-family);\n}\nnymbal-cart-drawer[open] .nymbal-drawer-panel {\n transform: translateX(0);\n}\nnymbal-cart-drawer .nymbal-drawer-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: var(--nymbal-spacing-md) var(--nymbal-spacing-lg);\n border-bottom: 1px solid var(--nymbal-color-border);\n}\nnymbal-cart-drawer .nymbal-drawer-header h2 {\n margin: 0;\n font-size: var(--nymbal-font-size-lg);\n font-weight: var(--nymbal-font-weight-bold);\n}\nnymbal-cart-drawer .nymbal-drawer-close {\n background: none;\n border: none;\n font-size: var(--nymbal-font-size-xl);\n cursor: pointer;\n padding: var(--nymbal-spacing-xs);\n color: var(--nymbal-color-text-muted);\n}\nnymbal-cart-drawer .nymbal-drawer-items {\n flex: 1;\n overflow-y: auto;\n padding: var(--nymbal-spacing-md) var(--nymbal-spacing-lg);\n}\nnymbal-cart-drawer .nymbal-cart-item {\n display: flex;\n gap: var(--nymbal-spacing-md);\n padding: var(--nymbal-spacing-md) 0;\n border-bottom: 1px solid var(--nymbal-color-border);\n}\nnymbal-cart-drawer .nymbal-cart-item-info {\n flex: 1;\n}\nnymbal-cart-drawer .nymbal-cart-item-name {\n font-weight: var(--nymbal-font-weight-medium);\n margin-bottom: var(--nymbal-spacing-xs);\n}\nnymbal-cart-drawer .nymbal-cart-item-variant {\n font-size: var(--nymbal-font-size-sm);\n color: var(--nymbal-color-text-muted);\n}\nnymbal-cart-drawer .nymbal-cart-item-actions {\n display: flex;\n align-items: center;\n gap: var(--nymbal-spacing-sm);\n}\nnymbal-cart-drawer .nymbal-cart-item-remove {\n background: none;\n border: none;\n color: var(--nymbal-color-error);\n cursor: pointer;\n font-size: var(--nymbal-font-size-sm);\n padding: var(--nymbal-spacing-xs);\n}\nnymbal-cart-drawer .nymbal-drawer-footer {\n padding: var(--nymbal-spacing-md) var(--nymbal-spacing-lg);\n border-top: 1px solid var(--nymbal-color-border);\n}\nnymbal-cart-drawer .nymbal-cart-subtotal {\n display: flex;\n justify-content: space-between;\n font-weight: var(--nymbal-font-weight-bold);\n margin-bottom: var(--nymbal-spacing-md);\n}\nnymbal-cart-drawer .nymbal-checkout-btn {\n width: 100%;\n font-family: var(--nymbal-font-family);\n font-size: var(--nymbal-font-size-base);\n font-weight: var(--nymbal-font-weight-medium);\n background: var(--nymbal-color-primary);\n color: var(--nymbal-color-text-inverse);\n border: none;\n border-radius: var(--nymbal-radius-md);\n padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-lg);\n cursor: pointer;\n transition: background var(--nymbal-transition-fast);\n}\nnymbal-cart-drawer .nymbal-checkout-btn:hover {\n background: var(--nymbal-color-primary-hover);\n}\nnymbal-cart-drawer .nymbal-drawer-empty {\n text-align: center;\n color: var(--nymbal-color-text-muted);\n padding: var(--nymbal-spacing-xl) 0;\n}\n`\n\nexport class NymbalCartDrawer extends NymbalElement {\n static get observedAttributes(): string[] {\n return ['open']\n }\n\n private readonly handleItemAdded = (): void => {\n this.open()\n }\n\n private readonly handleMiniCartClicked = (): void => {\n this.toggle()\n }\n\n protected setup(): void {\n injectStyles('nymbal-cart-drawer', STYLES)\n this.subscribeToStore(this.client.cart)\n document.addEventListener('nymbal:cart:item-added', this.handleItemAdded)\n document.addEventListener('nymbal:mini-cart:clicked', this.handleMiniCartClicked)\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback()\n document.removeEventListener('nymbal:cart:item-added', this.handleItemAdded)\n document.removeEventListener('nymbal:mini-cart:clicked', this.handleMiniCartClicked)\n }\n\n open(): void {\n this.setAttribute('open', '')\n window.dispatchEvent(new CustomEvent('nymbal:cart:open'))\n }\n\n close(): void {\n this.removeAttribute('open')\n window.dispatchEvent(new CustomEvent('nymbal:cart:close'))\n }\n\n toggle(): void {\n if (this.hasAttribute('open')) {\n this.close()\n } else {\n this.open()\n }\n }\n\n protected render(): void {\n const state = this.client.cart.getState()\n const currency = state.currency\n\n const itemsHtml = state.items.length === 0\n ? `<div class=\"nymbal-drawer-empty\">Your cart is empty</div>`\n : state.items.map((item) => `\n <div class=\"nymbal-cart-item\" data-testid=\"cart-item-${item.variantId}\">\n <div class=\"nymbal-cart-item-info\">\n <div class=\"nymbal-cart-item-name\">${this.esc(item.productName)}</div>\n <div class=\"nymbal-cart-item-variant\">${this.esc(item.variantName)}</div>\n <div class=\"nymbal-cart-item-qty\">Qty: ${item.qty}</div>\n </div>\n <div class=\"nymbal-cart-item-actions\">\n <span>${this.formatPrice(item.priceMinor * item.qty, currency)}</span>\n <button type=\"button\" class=\"nymbal-cart-item-remove\" data-testid=\"remove-item-${item.variantId}\" data-variant-id=\"${item.variantId}\">Remove</button>\n </div>\n </div>\n `).join('')\n\n this.innerHTML = `\n <div class=\"nymbal-drawer-overlay\" data-testid=\"cart-drawer-overlay\"></div>\n <div class=\"nymbal-drawer-panel\" role=\"dialog\" aria-label=\"Shopping cart\" aria-modal=\"true\" data-testid=\"cart-drawer\">\n <div class=\"nymbal-drawer-header\">\n <h2>Cart (${state.itemCount})</h2>\n <button type=\"button\" class=\"nymbal-drawer-close\" data-testid=\"cart-drawer-close\" aria-label=\"Close cart\">&times;</button>\n </div>\n <div class=\"nymbal-drawer-items\">${itemsHtml}</div>\n ${state.items.length > 0 ? `\n <div class=\"nymbal-drawer-footer\">\n <div class=\"nymbal-cart-subtotal\">\n <span>Subtotal</span>\n <span>${this.formatPrice(state.subtotalMinor, currency)}</span>\n </div>\n <button type=\"button\" class=\"nymbal-checkout-btn\" data-testid=\"cart-checkout-btn\">Checkout</button>\n </div>\n ` : ''}\n </div>\n `\n\n this.querySelector('.nymbal-drawer-overlay')?.addEventListener('click', () => this.close())\n this.querySelector('.nymbal-drawer-close')?.addEventListener('click', () => this.close())\n this.querySelector('.nymbal-checkout-btn')?.addEventListener('click', () => {\n this.dispatchEvent(new CustomEvent('nymbal:cart:checkout-clicked', { bubbles: true, composed: true }))\n })\n this.querySelectorAll('.nymbal-cart-item-remove').forEach((btn) => {\n btn.addEventListener('click', () => {\n const variantId = (btn as HTMLElement).dataset['variantId']\n if (variantId) this.client.cart.removeItem(variantId)\n })\n })\n }\n\n attributeChangedCallback(): void {\n if (this.isConnected) this.requestRender()\n }\n\n private esc(str: string): string {\n const div = document.createElement('div')\n div.textContent = str\n return div.innerHTML\n }\n\n private formatPrice(minor: number, currency: string): string {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency,\n }).format(minor / 100)\n }\n}\n","import { NymbalElement } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-mini-cart {\n display: inline-block;\n position: relative;\n}\nnymbal-mini-cart button {\n background: none;\n border: none;\n cursor: pointer;\n position: relative;\n padding: var(--nymbal-spacing-sm);\n font-family: var(--nymbal-font-family);\n color: var(--nymbal-color-text);\n}\nnymbal-mini-cart .nymbal-mini-cart-icon {\n width: 24px;\n height: 24px;\n}\nnymbal-mini-cart .nymbal-mini-cart-badge {\n position: absolute;\n top: 0;\n right: 0;\n background: var(--nymbal-color-accent);\n color: var(--nymbal-color-text-inverse);\n font-size: var(--nymbal-font-size-xs);\n font-weight: var(--nymbal-font-weight-bold);\n min-width: 18px;\n height: 18px;\n border-radius: var(--nymbal-radius-full);\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n}\nnymbal-mini-cart .nymbal-mini-cart-badge:empty {\n display: none;\n}\n`\n\nconst CART_SVG = `<svg class=\"nymbal-mini-cart-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"9\" cy=\"21\" r=\"1\"/><circle cx=\"20\" cy=\"21\" r=\"1\"/><path d=\"M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6\"/></svg>`\n\nexport class NymbalMiniCart extends NymbalElement {\n protected setup(): void {\n injectStyles('nymbal-mini-cart', STYLES)\n this.subscribeToStore(this.client.cart)\n }\n\n protected render(): void {\n const { itemCount } = this.client.cart.getState()\n\n this.innerHTML = `\n <button type=\"button\" data-testid=\"mini-cart\" aria-label=\"Cart (${itemCount} items)\">\n ${CART_SVG}\n <span class=\"nymbal-mini-cart-badge\" data-testid=\"mini-cart-badge\">${itemCount > 0 ? itemCount : ''}</span>\n </button>\n `\n\n this.querySelector('button')?.addEventListener('click', () => {\n this.dispatchEvent(new CustomEvent('nymbal:mini-cart:clicked', { bubbles: true, composed: true }))\n })\n }\n}\n","import type { DenormalisedVariant, DenormalisedProduct } from '@nymbalhq/sdk'\nimport type { VariantOption } from '@nymbalhq/types'\nimport { NymbalElement } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-variant-selector {\n display: block;\n font-family: var(--nymbal-font-family);\n}\nnymbal-variant-selector .nymbal-option-group {\n margin-bottom: var(--nymbal-spacing-md);\n}\nnymbal-variant-selector .nymbal-option-label {\n display: block;\n font-size: var(--nymbal-font-size-sm);\n font-weight: var(--nymbal-font-weight-medium);\n margin-bottom: var(--nymbal-spacing-sm);\n color: var(--nymbal-color-text);\n}\nnymbal-variant-selector .nymbal-option-buttons {\n display: flex;\n flex-wrap: wrap;\n gap: var(--nymbal-spacing-sm);\n}\nnymbal-variant-selector .nymbal-option-btn {\n font-family: var(--nymbal-font-family);\n font-size: var(--nymbal-font-size-sm);\n background: var(--nymbal-color-surface);\n color: var(--nymbal-color-text);\n border: 1px solid var(--nymbal-color-border);\n border-radius: var(--nymbal-radius-md);\n padding: var(--nymbal-spacing-xs) var(--nymbal-spacing-md);\n cursor: pointer;\n transition: border-color var(--nymbal-transition-fast), background var(--nymbal-transition-fast);\n}\nnymbal-variant-selector .nymbal-option-btn:hover {\n border-color: var(--nymbal-color-primary);\n}\nnymbal-variant-selector .nymbal-option-btn[aria-pressed=\"true\"] {\n background: var(--nymbal-color-primary);\n color: var(--nymbal-color-text-inverse);\n border-color: var(--nymbal-color-primary);\n}\nnymbal-variant-selector .nymbal-option-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n`\n\ninterface OptionGroup {\n name: string\n values: string[]\n}\n\nexport class NymbalVariantSelector extends NymbalElement {\n static get observedAttributes(): string[] {\n return ['options']\n }\n\n protected setup(): void {\n injectStyles('nymbal-variant-selector', STYLES)\n this.subscribeToStore(this.client.product)\n }\n\n protected render(): void {\n const state = this.client.product.getState()\n const product = state.product\n if (!product) {\n this.innerHTML = ''\n return\n }\n\n const optionGroups = this.getOptionGroups(product)\n const selected = state.selectedVariant\n\n this.innerHTML = optionGroups.map((group) => `\n <div class=\"nymbal-option-group\">\n <span class=\"nymbal-option-label\">${this.esc(group.name)}</span>\n <div class=\"nymbal-option-buttons\">\n ${group.values.map((value) => {\n const isSelected = selected?.options.some(\n (o: VariantOption) => o.name === group.name && o.value === value,\n ) ?? false\n return `<button\n class=\"nymbal-option-btn\"\n data-testid=\"variant-option-${this.esc(group.name)}-${this.esc(value)}\"\n data-option-name=\"${this.esc(group.name)}\"\n data-option-value=\"${this.esc(value)}\"\n aria-pressed=\"${isSelected}\"\n >${this.esc(value)}</button>`\n }).join('')}\n </div>\n </div>\n `).join('')\n\n this.querySelectorAll('.nymbal-option-btn').forEach((btn) => {\n btn.addEventListener('click', () => {\n const el = btn as HTMLElement\n const optionName = el.dataset['optionName']\n const optionValue = el.dataset['optionValue']\n if (!optionName || !optionValue || !product) return\n\n const currentSelections = new Map<string, string>()\n if (selected) {\n for (const opt of selected.options as VariantOption[]) {\n currentSelections.set(opt.name, opt.value)\n }\n }\n currentSelections.set(optionName, optionValue)\n\n const match = product.variants.find((v: DenormalisedVariant) => {\n const vRaw = v.options\n let vOpts: VariantOption[] = []\n if (typeof vRaw === 'string') {\n try { vOpts = JSON.parse(vRaw) } catch (e) { console.warn('[variant-selector] malformed variant options JSON', e); vOpts = [] }\n } else if (Array.isArray(vRaw)) {\n vOpts = vRaw\n }\n return vOpts.every((o) => currentSelections.get(o.name) === o.value)\n })\n\n if (match) {\n this.client.product.selectVariant(match)\n this.dispatchEvent(\n new CustomEvent('nymbal:variant:selected', {\n bubbles: true,\n composed: true,\n detail: { variant: match },\n }),\n )\n }\n })\n })\n }\n\n attributeChangedCallback(): void {\n if (this.isConnected) this.requestRender()\n }\n\n private getOptionGroups(product: DenormalisedProduct): OptionGroup[] {\n const optionsAttr = this.getAttribute('options')\n if (optionsAttr) {\n try {\n return JSON.parse(optionsAttr) as OptionGroup[]\n } catch (e) {\n console.warn('[variant-selector] malformed options attribute, falling back to variant derivation', e)\n }\n }\n\n const groups = new Map<string, Set<string>>()\n for (const variant of product.variants) {\n const raw = variant.options\n let opts: VariantOption[] = []\n if (typeof raw === 'string') {\n try { opts = JSON.parse(raw) } catch (e) { console.warn('[variant-selector] malformed variant options in getOptionGroups', e); opts = [] }\n } else if (Array.isArray(raw)) {\n opts = raw\n }\n for (const opt of opts) {\n let values = groups.get(opt.name)\n if (!values) {\n values = new Set()\n groups.set(opt.name, values)\n }\n values.add(opt.value)\n }\n }\n return Array.from(groups.entries()).map(([name, values]) => ({\n name,\n values: Array.from(values),\n }))\n }\n\n private esc(str: string): string {\n const div = document.createElement('div')\n div.textContent = str\n return div.innerHTML\n }\n}\n","import { NymbalElement } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-quantity-selector {\n display: inline-flex;\n align-items: center;\n font-family: var(--nymbal-font-family);\n}\nnymbal-quantity-selector button {\n font-family: var(--nymbal-font-family);\n font-size: var(--nymbal-font-size-base);\n background: var(--nymbal-color-surface);\n color: var(--nymbal-color-text);\n border: 1px solid var(--nymbal-color-border);\n width: 36px;\n height: 36px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background var(--nymbal-transition-fast);\n}\nnymbal-quantity-selector button:hover:not(:disabled) {\n background: var(--nymbal-color-secondary, #f4f4f5);\n}\nnymbal-quantity-selector button:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\nnymbal-quantity-selector button:first-child {\n border-radius: var(--nymbal-radius-md) 0 0 var(--nymbal-radius-md);\n}\nnymbal-quantity-selector button:last-child {\n border-radius: 0 var(--nymbal-radius-md) var(--nymbal-radius-md) 0;\n}\nnymbal-quantity-selector input {\n font-family: var(--nymbal-font-family);\n font-size: var(--nymbal-font-size-base);\n width: 48px;\n height: 36px;\n text-align: center;\n border: 1px solid var(--nymbal-color-border);\n border-left: none;\n border-right: none;\n -moz-appearance: textfield;\n}\nnymbal-quantity-selector input::-webkit-outer-spin-button,\nnymbal-quantity-selector input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n`\n\nexport class NymbalQuantitySelector extends NymbalElement {\n static get observedAttributes(): string[] {\n return ['value', 'min', 'max']\n }\n\n private _value = 1\n\n protected setup(): void {\n injectStyles('nymbal-quantity-selector', STYLES)\n this._value = Number(this.getAttribute('value')) || 1\n }\n\n protected render(): void {\n const min = Number(this.getAttribute('min')) || 1\n const max = Number(this.getAttribute('max')) || 99\n\n this.innerHTML = `\n <button data-testid=\"qty-decrement\" aria-label=\"Decrease quantity\" ${this._value <= min ? 'disabled' : ''}>&#8722;</button>\n <input data-testid=\"qty-input\" type=\"number\" value=\"${this._value}\" min=\"${min}\" max=\"${max}\" aria-label=\"Quantity\">\n <button data-testid=\"qty-increment\" aria-label=\"Increase quantity\" ${this._value >= max ? 'disabled' : ''}>&#43;</button>\n `\n\n this.querySelector('[data-testid=\"qty-decrement\"]')?.addEventListener('click', () => {\n if (this._value > min) {\n this._value--\n this.emitChange()\n this.render()\n }\n })\n\n this.querySelector('[data-testid=\"qty-increment\"]')?.addEventListener('click', () => {\n if (this._value < max) {\n this._value++\n this.emitChange()\n this.render()\n }\n })\n\n const input = this.querySelector('input')\n input?.addEventListener('change', () => {\n const val = Math.min(max, Math.max(min, Number(input.value) || min))\n this._value = val\n this.emitChange()\n this.render()\n })\n }\n\n attributeChangedCallback(name: string): void {\n if (name === 'value') {\n this._value = Number(this.getAttribute('value')) || 1\n }\n if (this.isConnected) this.requestRender()\n }\n\n private emitChange(): void {\n this.dispatchEvent(\n new CustomEvent('nymbal:quantity:changed', {\n bubbles: true,\n composed: true,\n detail: { value: this._value },\n }),\n )\n }\n}\n","import { NymbalElement } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\nimport type { DenormalisedProduct } from '@nymbalhq/sdk'\n\nconst STYLES = `\nnymbal-search-bar {\n display: block;\n position: relative;\n font-family: var(--nymbal-font-family);\n}\nnymbal-search-bar input {\n font-family: var(--nymbal-font-family);\n font-size: var(--nymbal-font-size-base);\n width: 100%;\n padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-md);\n border: 1px solid var(--nymbal-color-border);\n border-radius: var(--nymbal-radius-md);\n outline: none;\n transition: border-color var(--nymbal-transition-fast);\n box-sizing: border-box;\n}\nnymbal-search-bar input:focus {\n border-color: var(--nymbal-color-accent);\n}\nnymbal-search-bar .nymbal-search-results {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n background: var(--nymbal-color-surface);\n border: 1px solid var(--nymbal-color-border);\n border-top: none;\n border-radius: 0 0 var(--nymbal-radius-md) var(--nymbal-radius-md);\n box-shadow: var(--nymbal-shadow-md);\n z-index: var(--nymbal-z-dropdown);\n max-height: 300px;\n overflow-y: auto;\n}\nnymbal-search-bar .nymbal-search-result-item {\n padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-md);\n cursor: pointer;\n transition: background var(--nymbal-transition-fast);\n}\nnymbal-search-bar .nymbal-search-result-item:hover,\nnymbal-search-bar .nymbal-search-result-item[aria-selected=\"true\"] {\n background: var(--nymbal-color-secondary, #f4f4f5);\n}\nnymbal-search-bar .nymbal-search-result-name {\n font-weight: var(--nymbal-font-weight-medium);\n}\nnymbal-search-bar .nymbal-search-result-price {\n font-size: var(--nymbal-font-size-sm);\n color: var(--nymbal-color-text-muted);\n}\nnymbal-search-bar .nymbal-search-loading {\n padding: var(--nymbal-spacing-md);\n text-align: center;\n color: var(--nymbal-color-text-muted);\n font-size: var(--nymbal-font-size-sm);\n}\n`\n\nexport class NymbalSearchBar extends NymbalElement {\n static get observedAttributes(): string[] {\n return ['placeholder', 'debounce-ms']\n }\n\n private debounceTimer: ReturnType<typeof setTimeout> | null = null\n private selectedIndex = -1\n\n protected setup(): void {\n injectStyles('nymbal-search-bar', STYLES)\n this.subscribeToStore(this.client.search)\n }\n\n protected render(): void {\n const state = this.client.search.getState()\n const placeholder = this.getAttribute('placeholder') ?? 'Search products...'\n\n let resultsHtml = ''\n if (state.query) {\n if (state.loading) {\n resultsHtml = `<div class=\"nymbal-search-results\" data-testid=\"search-results\"><div class=\"nymbal-search-loading\">Searching...</div></div>`\n } else if (state.results.length > 0) {\n resultsHtml = `<div class=\"nymbal-search-results\" data-testid=\"search-results\">\n ${state.results\n .map(\n (product, i) => `\n <div class=\"nymbal-search-result-item\" data-testid=\"search-result-${this.esc(product.slug)}\" data-slug=\"${this.esc(product.slug)}\" aria-selected=\"${i === this.selectedIndex}\">\n <div class=\"nymbal-search-result-name\">${this.esc(product.name)}</div>\n ${product.priceMinor !== null ? `<div class=\"nymbal-search-result-price\">${this.formatPrice(product.priceMinor, product.currency ?? 'USD')}</div>` : ''}\n </div>\n `,\n )\n .join('')}\n </div>`\n }\n }\n\n this.innerHTML = `\n <input data-testid=\"search-input\" type=\"search\" placeholder=\"${this.esc(placeholder)}\" value=\"${this.esc(state.query)}\" autocomplete=\"off\" role=\"combobox\" aria-expanded=\"${state.results.length > 0}\" aria-haspopup=\"listbox\">\n ${resultsHtml}\n `\n\n this.querySelectorAll<HTMLElement>('.nymbal-search-result-item').forEach((item, i) => {\n item.addEventListener('click', () => {\n const product = state.results[i]\n if (product) this.select(product)\n })\n })\n\n const input = this.querySelector('input')!\n input.addEventListener('input', () => {\n const query = input.value\n const debounceMs = Number(this.getAttribute('debounce-ms')) || 300\n if (this.debounceTimer !== null) clearTimeout(this.debounceTimer)\n this.debounceTimer = setTimeout(() => {\n this.selectedIndex = -1\n if (query.trim()) {\n this.client.search.search(query)\n } else {\n this.client.search.clearSearch()\n }\n }, debounceMs)\n })\n\n input.addEventListener('keydown', (e) => {\n const results = state.results\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n this.selectedIndex = Math.min(this.selectedIndex + 1, results.length - 1)\n this.render()\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n this.selectedIndex = Math.max(this.selectedIndex - 1, -1)\n this.render()\n } else if (e.key === 'Enter') {\n e.preventDefault()\n const selected = this.selectedIndex >= 0 ? results[this.selectedIndex] : undefined\n if (selected) {\n this.select(selected)\n } else {\n const query = input.value.trim()\n if (query) {\n window.location.href = `/search?q=${encodeURIComponent(query)}`\n }\n }\n } else if (e.key === 'Escape') {\n this.client.search.clearSearch()\n this.selectedIndex = -1\n }\n })\n }\n\n private select(product: DenormalisedProduct): void {\n this.dispatchEvent(\n new CustomEvent('nymbal:search:selected', {\n bubbles: true,\n composed: true,\n detail: { product, query: product.name },\n }),\n )\n this.client.search.clearSearch()\n }\n\n attributeChangedCallback(): void {\n if (this.isConnected) this.requestRender()\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback()\n if (this.debounceTimer !== null) {\n clearTimeout(this.debounceTimer)\n this.debounceTimer = null\n }\n }\n\n /** Escapes a value for both text and quoted-attribute contexts. */\n private esc(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\n }\n\n private formatPrice(minor: number, currency: string): string {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(minor / 100)\n }\n}\n","import type { Facet } from '@nymbalhq/types'\nimport { NymbalElement } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-product-filter {\n display: block;\n font-family: var(--nymbal-font-family);\n}\nnymbal-product-filter .nymbal-filter-group {\n margin-bottom: var(--nymbal-spacing-lg);\n}\nnymbal-product-filter .nymbal-filter-label {\n display: block;\n font-size: var(--nymbal-font-size-sm);\n font-weight: var(--nymbal-font-weight-bold);\n margin-bottom: var(--nymbal-spacing-sm);\n color: var(--nymbal-color-text);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\nnymbal-product-filter .nymbal-filter-option {\n display: flex;\n align-items: center;\n gap: var(--nymbal-spacing-sm);\n padding: var(--nymbal-spacing-xs) 0;\n cursor: pointer;\n font-size: var(--nymbal-font-size-sm);\n color: var(--nymbal-color-text);\n}\nnymbal-product-filter .nymbal-filter-option input[type=\"checkbox\"] {\n accent-color: var(--nymbal-color-primary);\n}\nnymbal-product-filter .nymbal-filter-count {\n color: var(--nymbal-color-text-muted);\n font-size: var(--nymbal-font-size-xs);\n}\n`\n\n\nexport class NymbalProductFilter extends NymbalElement {\n static get observedAttributes(): string[] {\n return ['facets']\n }\n\n protected setup(): void {\n injectStyles('nymbal-product-filter', STYLES)\n this.subscribeToStore(this.client.productList)\n }\n\n protected render(): void {\n const state = this.client.productList.getState()\n const facets = this.getFacets()\n\n if (facets.length === 0) {\n this.innerHTML = ''\n return\n }\n\n this.innerHTML = facets.map((facet) => {\n const activeFilter = state.filters[facet.field]\n const activeValues = Array.isArray(activeFilter)\n ? activeFilter\n : (activeFilter ? [activeFilter] : [])\n\n return `\n <div class=\"nymbal-filter-group\" data-testid=\"filter-group-${facet.field}\">\n <span class=\"nymbal-filter-label\">${this.esc(facet.label)}</span>\n ${facet.values.map((v) => {\n const checked = activeValues.includes(v.value)\n return `\n <label class=\"nymbal-filter-option\" data-testid=\"filter-option-${v.value}\">\n <input type=\"checkbox\" data-filter-name=\"${facet.field}\" data-filter-value=\"${v.value}\" ${checked ? 'checked' : ''}>\n <span>${this.esc(v.label ?? v.value)}</span>\n ${v.count !== undefined ? `<span class=\"nymbal-filter-count\">(${v.count})</span>` : ''}\n </label>\n `\n }).join('')}\n </div>\n `\n }).join('')\n\n this.querySelectorAll('input[type=\"checkbox\"]').forEach((input) => {\n input.addEventListener('change', () => {\n const el = input as HTMLInputElement\n const name = el.dataset['filterName']!\n const value = el.dataset['filterValue']!\n if (el.checked) {\n const current = state.filters[name]\n const values = Array.isArray(current) ? [...current, value] : (current ? [current, value] : value)\n this.client.productList.applyFilter(name, values)\n } else {\n const current = state.filters[name]\n if (Array.isArray(current)) {\n const remaining = current.filter((v) => v !== value)\n if (remaining.length > 0) {\n this.client.productList.applyFilter(name, remaining)\n } else {\n this.client.productList.removeFilter(name)\n }\n } else {\n this.client.productList.removeFilter(name)\n }\n }\n this.dispatchEvent(\n new CustomEvent('nymbal:filter:changed', { bubbles: true, composed: true, detail: { name, value } }),\n )\n })\n })\n }\n\n attributeChangedCallback(): void {\n if (this.isConnected) this.requestRender()\n }\n\n private getFacets(): Facet[] {\n const attr = this.getAttribute('facets')\n if (!attr) return []\n try {\n return JSON.parse(attr) as Facet[]\n } catch (err) {\n console.error('[nymbal-product-filter] failed to parse facets attribute', err)\n return []\n }\n }\n\n private esc(str: string): string {\n const div = document.createElement('div')\n div.textContent = str\n return div.innerHTML\n }\n}\n","import { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-toast {\n position: fixed;\n z-index: var(--nymbal-z-toast);\n display: flex;\n flex-direction: column;\n gap: var(--nymbal-spacing-sm);\n pointer-events: none;\n font-family: var(--nymbal-font-family);\n}\nnymbal-toast[position=\"top-right\"],\nnymbal-toast:not([position]) {\n top: var(--nymbal-spacing-lg);\n right: var(--nymbal-spacing-lg);\n}\nnymbal-toast[position=\"top-left\"] {\n top: var(--nymbal-spacing-lg);\n left: var(--nymbal-spacing-lg);\n}\nnymbal-toast[position=\"bottom-right\"] {\n bottom: var(--nymbal-spacing-lg);\n right: var(--nymbal-spacing-lg);\n}\nnymbal-toast[position=\"bottom-left\"] {\n bottom: var(--nymbal-spacing-lg);\n left: var(--nymbal-spacing-lg);\n}\nnymbal-toast .nymbal-toast-item {\n pointer-events: auto;\n display: flex;\n align-items: center;\n gap: var(--nymbal-spacing-sm);\n padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-md);\n border-radius: var(--nymbal-radius-md);\n box-shadow: var(--nymbal-shadow-lg);\n font-size: var(--nymbal-font-size-sm);\n background: var(--nymbal-color-surface);\n color: var(--nymbal-color-text);\n border: 1px solid var(--nymbal-color-border);\n animation: nymbal-toast-in var(--nymbal-transition-normal) forwards;\n max-width: 360px;\n}\nnymbal-toast .nymbal-toast-item[data-type=\"success\"] {\n border-left: 3px solid var(--nymbal-color-success);\n}\nnymbal-toast .nymbal-toast-item[data-type=\"error\"] {\n border-left: 3px solid var(--nymbal-color-error);\n}\nnymbal-toast .nymbal-toast-item[data-type=\"warning\"] {\n border-left: 3px solid var(--nymbal-color-warning);\n}\nnymbal-toast .nymbal-toast-item[data-type=\"info\"] {\n border-left: 3px solid var(--nymbal-color-accent);\n}\nnymbal-toast .nymbal-toast-close {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--nymbal-color-text-muted);\n font-size: var(--nymbal-font-size-base);\n padding: 0;\n margin-left: auto;\n}\n@keyframes nymbal-toast-in {\n from { opacity: 0; transform: translateY(-8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n`\n\nconst HTMLElementBase =\n typeof HTMLElement !== 'undefined' ? HTMLElement : (class {} as unknown as typeof HTMLElement)\n\nexport class NymbalToast extends HTMLElementBase {\n static get observedAttributes(): string[] {\n return ['position', 'duration-ms']\n }\n\n connectedCallback(): void {\n injectStyles('nymbal-toast', STYLES)\n this.setAttribute('data-testid', 'toast')\n }\n\n show(message: string, type: 'success' | 'error' | 'warning' | 'info' = 'info'): void {\n const durationMs = Number(this.getAttribute('duration-ms')) || 4000\n\n const item = document.createElement('div')\n item.className = 'nymbal-toast-item'\n item.setAttribute('data-type', type)\n item.setAttribute('role', 'alert')\n\n const text = document.createElement('span')\n text.textContent = message\n\n const close = document.createElement('button')\n close.className = 'nymbal-toast-close'\n close.setAttribute('aria-label', 'Dismiss')\n close.textContent = '×'\n close.addEventListener('click', () => this.dismiss(item))\n\n item.appendChild(text)\n item.appendChild(close)\n this.appendChild(item)\n\n setTimeout(() => this.dismiss(item), durationMs)\n }\n\n private dismiss(item: HTMLElement): void {\n if (item.parentNode === this) {\n this.removeChild(item)\n }\n }\n\n static showGlobal(message: string, type: 'success' | 'error' | 'warning' | 'info' = 'info'): void {\n let toastEl = document.querySelector('nymbal-toast') as NymbalToast | null\n if (!toastEl) {\n toastEl = document.createElement('nymbal-toast') as NymbalToast\n document.body.appendChild(toastEl)\n }\n toastEl.show(message, type)\n }\n}\n","export { registerClient, getClient } from './registry.js'\nexport { NymbalElement } from './base-element.js'\nexport { injectStyles } from './styles.js'\n\nimport { NymbalAddToCart } from './components/add-to-cart.js'\nimport { NymbalCartDrawer } from './components/cart-drawer.js'\nimport { NymbalMiniCart } from './components/mini-cart.js'\nimport { NymbalVariantSelector } from './components/variant-selector.js'\nimport { NymbalQuantitySelector } from './components/quantity-selector.js'\nimport { NymbalSearchBar } from './components/search-bar.js'\nimport { NymbalProductFilter } from './components/product-filter.js'\nimport { NymbalToast } from './components/toast.js'\n\nexport {\n NymbalAddToCart,\n NymbalCartDrawer,\n NymbalMiniCart,\n NymbalVariantSelector,\n NymbalQuantitySelector,\n NymbalSearchBar,\n NymbalProductFilter,\n NymbalToast,\n}\n\nexport function registerComponents(): void {\n const components: Array<[string, CustomElementConstructor]> = [\n ['nymbal-add-to-cart', NymbalAddToCart],\n ['nymbal-cart-drawer', NymbalCartDrawer],\n ['nymbal-mini-cart', NymbalMiniCart],\n ['nymbal-variant-selector', NymbalVariantSelector],\n ['nymbal-quantity-selector', NymbalQuantitySelector],\n ['nymbal-search-bar', NymbalSearchBar],\n ['nymbal-product-filter', NymbalProductFilter],\n ['nymbal-toast', NymbalToast],\n ]\n\n for (const [name, ctor] of components) {\n if (!customElements.get(name)) {\n customElements.define(name, ctor)\n }\n }\n}\n"],"mappings":"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","names":["STYLES","STYLES","STYLES","STYLES","STYLES","STYLES","STYLES","HTMLElementBase"]}
1
+ {"version":3,"sources":["../src/registry.ts","../src/base-element.ts","../src/styles.ts","../src/components/add-to-cart.ts","../src/components/cart-drawer.ts","../src/components/mini-cart.ts","../src/components/variant-selector.ts","../src/components/quantity-selector.ts","../src/components/search-bar.ts","../src/components/product-filter.ts","../src/components/toast.ts","../src/components/payment-element.ts","../src/components/sort-dropdown.ts","../src/index.ts","../src/payment-drivers/test-mode.ts","../src/payment-drivers/stripe.ts"],"sourcesContent":["import type { NymbalClient } from '@nymbalhq/sdk'\n\nconst CLIENT_KEY = Symbol.for('nymbal.client')\n\nexport function registerClient(client: NymbalClient): void {\n (globalThis as Record<symbol, unknown>)[CLIENT_KEY] = client\n}\n\nexport function getClient(): NymbalClient {\n const client = (globalThis as Record<symbol, unknown>)[CLIENT_KEY] as NymbalClient | undefined\n if (!client) {\n throw new Error('NymbalClient not registered. Call registerClient() before using components.')\n }\n return client\n}\n","import type { NymbalClient } from '@nymbalhq/sdk'\nimport { getClient } from './registry.js'\n\n/** `HTMLElement`, or a stand-in on a server, where the module is imported but no element is ever made. */\nexport const HTMLElementBase =\n typeof HTMLElement !== 'undefined' ? HTMLElement : (class {} as unknown as typeof HTMLElement)\n\nexport abstract class NymbalElement extends HTMLElementBase {\n private _unsubs: Array<() => void> = []\n private _renderQueued = false\n\n protected get client(): NymbalClient {\n return getClient()\n }\n\n protected subscribeToStore(store: { subscribe(fn: () => void): () => void }): void {\n this._unsubs.push(store.subscribe(() => this.requestRender()))\n }\n\n protected requestRender(): void {\n if (this._renderQueued) return\n this._renderQueued = true\n queueMicrotask(() => {\n this._renderQueued = false\n this.render()\n })\n }\n\n connectedCallback(): void {\n this.setup()\n this.render()\n }\n\n disconnectedCallback(): void {\n for (const fn of this._unsubs) fn()\n this._unsubs = []\n }\n\n protected abstract setup(): void\n protected abstract render(): void\n}\n","const injected = new Set<string>()\n\nexport function injectStyles(tagName: string, css: string): void {\n if (injected.has(tagName)) return\n if (typeof document === 'undefined') return\n const style = document.createElement('style')\n style.setAttribute('data-nymbal', tagName)\n style.textContent = css\n document.head.appendChild(style)\n injected.add(tagName)\n}\n","import { NymbalElement } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-add-to-cart {\n display: inline-block;\n}\nnymbal-add-to-cart button {\n font-family: var(--nymbal-font-family);\n font-size: var(--nymbal-font-size-base);\n font-weight: var(--nymbal-font-weight-medium);\n background: var(--nymbal-color-primary);\n color: var(--nymbal-color-text-inverse);\n border: none;\n border-radius: var(--nymbal-radius-md);\n padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-lg);\n cursor: pointer;\n transition: background var(--nymbal-transition-fast);\n min-width: 140px;\n}\nnymbal-add-to-cart button:hover:not(:disabled) {\n background: var(--nymbal-color-primary-hover);\n}\nnymbal-add-to-cart button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n`\n\nconst MIN_QTY = 1\nconst MAX_QTY = 99\n\n/** The `qty` attribute as a whole number of units: a non-integer is 1, and the result is clamped to 1-99. */\nfunction quantityOf(attr: string | null): number {\n const n = attr === null || attr.trim() === '' ? Number.NaN : Number(attr)\n const whole = Number.isInteger(n) ? n : MIN_QTY\n return Math.min(MAX_QTY, Math.max(MIN_QTY, whole))\n}\n\nexport class NymbalAddToCart extends NymbalElement {\n static get observedAttributes(): string[] {\n return ['variant-id', 'qty', 'disabled']\n }\n\n private button!: HTMLButtonElement\n\n protected setup(): void {\n injectStyles('nymbal-add-to-cart', STYLES)\n this.subscribeToStore(this.client.cart)\n }\n\n protected render(): void {\n const variantId = this.getAttribute('variant-id')\n const isDisabled = this.hasAttribute('disabled')\n const cartState = this.client.cart.getState()\n\n if (!this.button) {\n this.button = document.createElement('button')\n this.button.setAttribute('type', 'button')\n this.button.setAttribute('data-testid', 'add-to-cart')\n this.button.addEventListener('click', this.handleClick)\n this.innerHTML = ''\n this.appendChild(this.button)\n }\n\n this.button.disabled = isDisabled || cartState.loading || !variantId\n this.button.setAttribute('aria-busy', String(cartState.loading))\n this.button.textContent = cartState.loading ? 'Adding...' : 'Add to Cart'\n }\n\n attributeChangedCallback(): void {\n if (this.isConnected) this.requestRender()\n }\n\n private handleClick = async (): Promise<void> => {\n const variantId = this.getAttribute('variant-id')\n if (!variantId) return\n const qty = quantityOf(this.getAttribute('qty'))\n try {\n await this.client.cart.addItem(variantId, qty)\n this.dispatchEvent(\n new CustomEvent('nymbal:cart:item-added', {\n bubbles: true,\n composed: true,\n detail: { variantId, qty },\n }),\n )\n } catch {\n // error is stored in cart state\n }\n }\n}\n","import { NymbalElement } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-cart-drawer {\n display: block;\n}\nnymbal-cart-drawer .nymbal-drawer-overlay {\n position: fixed;\n inset: 0;\n background: var(--nymbal-color-overlay);\n z-index: var(--nymbal-z-drawer);\n opacity: 0;\n visibility: hidden;\n transition: opacity var(--nymbal-transition-normal), visibility var(--nymbal-transition-normal);\n}\nnymbal-cart-drawer[open] .nymbal-drawer-overlay {\n opacity: 1;\n visibility: visible;\n}\nnymbal-cart-drawer .nymbal-drawer-panel {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: min(400px, 90vw);\n background: var(--nymbal-color-surface);\n z-index: var(--nymbal-z-drawer);\n transform: translateX(100%);\n transition: transform var(--nymbal-transition-normal);\n display: flex;\n flex-direction: column;\n font-family: var(--nymbal-font-family);\n}\nnymbal-cart-drawer[open] .nymbal-drawer-panel {\n transform: translateX(0);\n}\nnymbal-cart-drawer .nymbal-drawer-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: var(--nymbal-spacing-md) var(--nymbal-spacing-lg);\n border-bottom: 1px solid var(--nymbal-color-border);\n}\nnymbal-cart-drawer .nymbal-drawer-header h2 {\n margin: 0;\n font-size: var(--nymbal-font-size-lg);\n font-weight: var(--nymbal-font-weight-bold);\n}\nnymbal-cart-drawer .nymbal-drawer-close {\n background: none;\n border: none;\n font-size: var(--nymbal-font-size-xl);\n cursor: pointer;\n padding: var(--nymbal-spacing-xs);\n color: var(--nymbal-color-text-muted);\n}\nnymbal-cart-drawer .nymbal-drawer-items {\n flex: 1;\n overflow-y: auto;\n padding: var(--nymbal-spacing-md) var(--nymbal-spacing-lg);\n}\nnymbal-cart-drawer .nymbal-cart-item {\n display: flex;\n gap: var(--nymbal-spacing-md);\n padding: var(--nymbal-spacing-md) 0;\n border-bottom: 1px solid var(--nymbal-color-border);\n}\nnymbal-cart-drawer .nymbal-cart-item-info {\n flex: 1;\n}\nnymbal-cart-drawer .nymbal-cart-item-name {\n font-weight: var(--nymbal-font-weight-medium);\n margin-bottom: var(--nymbal-spacing-xs);\n}\nnymbal-cart-drawer .nymbal-cart-item-variant {\n font-size: var(--nymbal-font-size-sm);\n color: var(--nymbal-color-text-muted);\n}\nnymbal-cart-drawer .nymbal-cart-item-actions {\n display: flex;\n align-items: center;\n gap: var(--nymbal-spacing-sm);\n}\nnymbal-cart-drawer .nymbal-cart-item-remove {\n background: none;\n border: none;\n color: var(--nymbal-color-error);\n cursor: pointer;\n font-size: var(--nymbal-font-size-sm);\n padding: var(--nymbal-spacing-xs);\n}\nnymbal-cart-drawer .nymbal-drawer-footer {\n padding: var(--nymbal-spacing-md) var(--nymbal-spacing-lg);\n border-top: 1px solid var(--nymbal-color-border);\n}\nnymbal-cart-drawer .nymbal-cart-subtotal {\n display: flex;\n justify-content: space-between;\n font-weight: var(--nymbal-font-weight-bold);\n margin-bottom: var(--nymbal-spacing-md);\n}\nnymbal-cart-drawer .nymbal-checkout-btn {\n width: 100%;\n font-family: var(--nymbal-font-family);\n font-size: var(--nymbal-font-size-base);\n font-weight: var(--nymbal-font-weight-medium);\n background: var(--nymbal-color-primary);\n color: var(--nymbal-color-text-inverse);\n border: none;\n border-radius: var(--nymbal-radius-md);\n padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-lg);\n cursor: pointer;\n transition: background var(--nymbal-transition-fast);\n}\nnymbal-cart-drawer .nymbal-checkout-btn:hover {\n background: var(--nymbal-color-primary-hover);\n}\nnymbal-cart-drawer .nymbal-drawer-empty {\n text-align: center;\n color: var(--nymbal-color-text-muted);\n padding: var(--nymbal-spacing-xl) 0;\n}\n`\n\nexport class NymbalCartDrawer extends NymbalElement {\n static get observedAttributes(): string[] {\n return ['open']\n }\n\n private readonly handleItemAdded = (): void => {\n this.open()\n }\n\n private readonly handleMiniCartClicked = (): void => {\n this.toggle()\n }\n\n protected setup(): void {\n injectStyles('nymbal-cart-drawer', STYLES)\n this.subscribeToStore(this.client.cart)\n document.addEventListener('nymbal:cart:item-added', this.handleItemAdded)\n document.addEventListener('nymbal:mini-cart:clicked', this.handleMiniCartClicked)\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback()\n document.removeEventListener('nymbal:cart:item-added', this.handleItemAdded)\n document.removeEventListener('nymbal:mini-cart:clicked', this.handleMiniCartClicked)\n }\n\n open(): void {\n this.setAttribute('open', '')\n window.dispatchEvent(new CustomEvent('nymbal:cart:open'))\n }\n\n close(): void {\n this.removeAttribute('open')\n window.dispatchEvent(new CustomEvent('nymbal:cart:close'))\n }\n\n toggle(): void {\n if (this.hasAttribute('open')) {\n this.close()\n } else {\n this.open()\n }\n }\n\n protected render(): void {\n const state = this.client.cart.getState()\n const currency = state.currency\n\n const itemsHtml = state.items.length === 0\n ? `<div class=\"nymbal-drawer-empty\">Your cart is empty</div>`\n : state.items.map((item) => `\n <div class=\"nymbal-cart-item\" data-testid=\"cart-item-${item.variantId}\">\n <div class=\"nymbal-cart-item-info\">\n <div class=\"nymbal-cart-item-name\">${this.esc(item.productName)}</div>\n <div class=\"nymbal-cart-item-variant\">${this.esc(item.variantName)}</div>\n <div class=\"nymbal-cart-item-qty\">Qty: ${item.qty}</div>\n </div>\n <div class=\"nymbal-cart-item-actions\">\n <span>${this.formatPrice(item.priceMinor * item.qty, currency)}</span>\n <button type=\"button\" class=\"nymbal-cart-item-remove\" data-testid=\"remove-item-${item.variantId}\" data-variant-id=\"${item.variantId}\">Remove</button>\n </div>\n </div>\n `).join('')\n\n this.innerHTML = `\n <div class=\"nymbal-drawer-overlay\" data-testid=\"cart-drawer-overlay\"></div>\n <div class=\"nymbal-drawer-panel\" role=\"dialog\" aria-label=\"Shopping cart\" aria-modal=\"true\" data-testid=\"cart-drawer\">\n <div class=\"nymbal-drawer-header\">\n <h2>Cart (${state.itemCount})</h2>\n <button type=\"button\" class=\"nymbal-drawer-close\" data-testid=\"cart-drawer-close\" aria-label=\"Close cart\">&times;</button>\n </div>\n <div class=\"nymbal-drawer-items\">${itemsHtml}</div>\n ${state.items.length > 0 ? `\n <div class=\"nymbal-drawer-footer\">\n <div class=\"nymbal-cart-subtotal\">\n <span>Subtotal</span>\n <span>${this.formatPrice(state.subtotalMinor, currency)}</span>\n </div>\n <button type=\"button\" class=\"nymbal-checkout-btn\" data-testid=\"cart-checkout-btn\">Checkout</button>\n </div>\n ` : ''}\n </div>\n `\n\n this.querySelector('.nymbal-drawer-overlay')?.addEventListener('click', () => this.close())\n this.querySelector('.nymbal-drawer-close')?.addEventListener('click', () => this.close())\n this.querySelector('.nymbal-checkout-btn')?.addEventListener('click', () => {\n this.dispatchEvent(new CustomEvent('nymbal:cart:checkout-clicked', { bubbles: true, composed: true }))\n })\n this.querySelectorAll('.nymbal-cart-item-remove').forEach((btn) => {\n btn.addEventListener('click', () => {\n const variantId = (btn as HTMLElement).dataset['variantId']\n if (variantId) this.client.cart.removeItem(variantId)\n })\n })\n }\n\n attributeChangedCallback(): void {\n if (this.isConnected) this.requestRender()\n }\n\n private esc(str: string): string {\n const div = document.createElement('div')\n div.textContent = str\n return div.innerHTML\n }\n\n private formatPrice(minor: number, currency: string): string {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency,\n }).format(minor / 100)\n }\n}\n","import { NymbalElement } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-mini-cart {\n display: inline-block;\n position: relative;\n}\nnymbal-mini-cart button {\n background: none;\n border: none;\n cursor: pointer;\n position: relative;\n padding: var(--nymbal-spacing-sm);\n font-family: var(--nymbal-font-family);\n color: var(--nymbal-color-text);\n}\nnymbal-mini-cart .nymbal-mini-cart-icon {\n width: 24px;\n height: 24px;\n}\nnymbal-mini-cart .nymbal-mini-cart-badge {\n position: absolute;\n top: 0;\n right: 0;\n background: var(--nymbal-color-accent);\n color: var(--nymbal-color-text-inverse);\n font-size: var(--nymbal-font-size-xs);\n font-weight: var(--nymbal-font-weight-bold);\n min-width: 18px;\n height: 18px;\n border-radius: var(--nymbal-radius-full);\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n}\nnymbal-mini-cart .nymbal-mini-cart-badge:empty {\n display: none;\n}\n`\n\nconst CART_SVG = `<svg class=\"nymbal-mini-cart-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"9\" cy=\"21\" r=\"1\"/><circle cx=\"20\" cy=\"21\" r=\"1\"/><path d=\"M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6\"/></svg>`\n\nexport class NymbalMiniCart extends NymbalElement {\n protected setup(): void {\n injectStyles('nymbal-mini-cart', STYLES)\n this.subscribeToStore(this.client.cart)\n }\n\n protected render(): void {\n const { itemCount } = this.client.cart.getState()\n\n this.innerHTML = `\n <button type=\"button\" data-testid=\"mini-cart\" aria-label=\"Cart (${itemCount} items)\">\n ${CART_SVG}\n <span class=\"nymbal-mini-cart-badge\" data-testid=\"mini-cart-badge\">${itemCount > 0 ? itemCount : ''}</span>\n </button>\n `\n\n this.querySelector('button')?.addEventListener('click', () => {\n this.dispatchEvent(new CustomEvent('nymbal:mini-cart:clicked', { bubbles: true, composed: true }))\n })\n }\n}\n","import type { DenormalisedVariant, DenormalisedProduct } from '@nymbalhq/sdk'\nimport type { VariantOption } from '@nymbalhq/types'\nimport { NymbalElement } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-variant-selector {\n display: block;\n font-family: var(--nymbal-font-family);\n}\nnymbal-variant-selector .nymbal-option-group {\n margin-bottom: var(--nymbal-spacing-md);\n}\nnymbal-variant-selector .nymbal-option-label {\n display: block;\n font-size: var(--nymbal-font-size-sm);\n font-weight: var(--nymbal-font-weight-medium);\n margin-bottom: var(--nymbal-spacing-sm);\n color: var(--nymbal-color-text);\n}\nnymbal-variant-selector .nymbal-option-buttons {\n display: flex;\n flex-wrap: wrap;\n gap: var(--nymbal-spacing-sm);\n}\nnymbal-variant-selector .nymbal-option-btn {\n font-family: var(--nymbal-font-family);\n font-size: var(--nymbal-font-size-sm);\n background: var(--nymbal-color-surface);\n color: var(--nymbal-color-text);\n border: 1px solid var(--nymbal-color-border);\n border-radius: var(--nymbal-radius-md);\n padding: var(--nymbal-spacing-xs) var(--nymbal-spacing-md);\n cursor: pointer;\n transition: border-color var(--nymbal-transition-fast), background var(--nymbal-transition-fast);\n}\nnymbal-variant-selector .nymbal-option-btn:hover {\n border-color: var(--nymbal-color-primary);\n}\nnymbal-variant-selector .nymbal-option-btn[aria-pressed=\"true\"] {\n background: var(--nymbal-color-primary);\n color: var(--nymbal-color-text-inverse);\n border-color: var(--nymbal-color-primary);\n}\nnymbal-variant-selector .nymbal-option-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n`\n\ninterface OptionGroup {\n name: string\n values: string[]\n}\n\n/** A variant's options when they are the `VariantOption[]` the read model serves; any other shape offers none. */\nfunction optionsOf(variant: DenormalisedVariant): VariantOption[] {\n return Array.isArray(variant.options) ? variant.options : []\n}\n\nexport class NymbalVariantSelector extends NymbalElement {\n protected setup(): void {\n injectStyles('nymbal-variant-selector', STYLES)\n this.subscribeToStore(this.client.product)\n }\n\n protected render(): void {\n const state = this.client.product.getState()\n const product = state.product\n if (!product) {\n this.innerHTML = ''\n return\n }\n\n const optionGroups = this.getOptionGroups(product)\n const selected = state.selectedVariant\n\n this.innerHTML = optionGroups.map((group) => `\n <div class=\"nymbal-option-group\">\n <span class=\"nymbal-option-label\">${this.esc(group.name)}</span>\n <div class=\"nymbal-option-buttons\">\n ${group.values.map((value) => {\n const isSelected =\n selected !== null &&\n optionsOf(selected).some((o) => o.name === group.name && o.value === value)\n return `<button\n class=\"nymbal-option-btn\"\n data-testid=\"variant-option-${this.esc(group.name)}-${this.esc(value)}\"\n data-option-name=\"${this.esc(group.name)}\"\n data-option-value=\"${this.esc(value)}\"\n aria-pressed=\"${isSelected}\"\n >${this.esc(value)}</button>`\n }).join('')}\n </div>\n </div>\n `).join('')\n\n this.querySelectorAll('.nymbal-option-btn').forEach((btn) => {\n btn.addEventListener('click', () => {\n const el = btn as HTMLElement\n const optionName = el.dataset['optionName']\n const optionValue = el.dataset['optionValue']\n if (!optionName || !optionValue || !product) return\n\n const currentSelections = new Map<string, string>()\n if (selected) {\n for (const opt of optionsOf(selected)) {\n currentSelections.set(opt.name, opt.value)\n }\n }\n currentSelections.set(optionName, optionValue)\n\n const match = product.variants.find((v: DenormalisedVariant) =>\n optionsOf(v).every((o) => currentSelections.get(o.name) === o.value),\n )\n\n if (match) {\n this.client.product.selectVariant(match)\n this.dispatchEvent(\n new CustomEvent('nymbal:variant:selected', {\n bubbles: true,\n composed: true,\n detail: { variant: match },\n }),\n )\n }\n })\n })\n }\n\n private getOptionGroups(product: DenormalisedProduct): OptionGroup[] {\n const groups = new Map<string, Set<string>>()\n for (const variant of product.variants) {\n for (const opt of optionsOf(variant)) {\n let values = groups.get(opt.name)\n if (!values) {\n values = new Set()\n groups.set(opt.name, values)\n }\n values.add(opt.value)\n }\n }\n return Array.from(groups.entries()).map(([name, values]) => ({\n name,\n values: Array.from(values),\n }))\n }\n\n private esc(str: string): string {\n const div = document.createElement('div')\n div.textContent = str\n return div.innerHTML\n }\n}\n","import { NymbalElement } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-quantity-selector {\n display: inline-flex;\n align-items: center;\n font-family: var(--nymbal-font-family);\n}\nnymbal-quantity-selector button {\n font-family: var(--nymbal-font-family);\n font-size: var(--nymbal-font-size-base);\n background: var(--nymbal-color-surface);\n color: var(--nymbal-color-text);\n border: 1px solid var(--nymbal-color-border);\n width: 36px;\n height: 36px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background var(--nymbal-transition-fast);\n}\nnymbal-quantity-selector button:hover:not(:disabled) {\n background: var(--nymbal-color-secondary, #f4f4f5);\n}\nnymbal-quantity-selector button:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\nnymbal-quantity-selector button:first-child {\n border-radius: var(--nymbal-radius-md) 0 0 var(--nymbal-radius-md);\n}\nnymbal-quantity-selector button:last-child {\n border-radius: 0 var(--nymbal-radius-md) var(--nymbal-radius-md) 0;\n}\nnymbal-quantity-selector input {\n font-family: var(--nymbal-font-family);\n font-size: var(--nymbal-font-size-base);\n width: 48px;\n height: 36px;\n text-align: center;\n border: 1px solid var(--nymbal-color-border);\n border-left: none;\n border-right: none;\n -moz-appearance: textfield;\n}\nnymbal-quantity-selector input::-webkit-outer-spin-button,\nnymbal-quantity-selector input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n`\n\n/**\n * A quantity as a whole number of units in `min`-`max`: a number is truncated toward zero and clamped, anything else is\n * `min`. Add-to-cart takes only integers, so the selector shows and emits the same integer.\n */\nfunction wholeQuantity(raw: string | null, min: number, max: number): number {\n const n = raw === null || raw.trim() === '' ? Number.NaN : Number(raw)\n if (Number.isNaN(n)) return min\n return Math.min(max, Math.max(min, Math.trunc(n)))\n}\n\nexport class NymbalQuantitySelector extends NymbalElement {\n static get observedAttributes(): string[] {\n return ['value', 'min', 'max']\n }\n\n private _value = 1\n\n protected setup(): void {\n injectStyles('nymbal-quantity-selector', STYLES)\n this._value = wholeQuantity(this.getAttribute('value'), ...this.bounds())\n }\n\n private bounds(): [number, number] {\n return [Number(this.getAttribute('min')) || 1, Number(this.getAttribute('max')) || 99]\n }\n\n protected render(): void {\n const [min, max] = this.bounds()\n\n this.innerHTML = `\n <button data-testid=\"qty-decrement\" aria-label=\"Decrease quantity\" ${this._value <= min ? 'disabled' : ''}>&#8722;</button>\n <input data-testid=\"qty-input\" type=\"number\" value=\"${this._value}\" min=\"${min}\" max=\"${max}\" aria-label=\"Quantity\">\n <button data-testid=\"qty-increment\" aria-label=\"Increase quantity\" ${this._value >= max ? 'disabled' : ''}>&#43;</button>\n `\n\n this.querySelector('[data-testid=\"qty-decrement\"]')?.addEventListener('click', () => {\n if (this._value > min) {\n this._value--\n this.emitChange()\n this.render()\n }\n })\n\n this.querySelector('[data-testid=\"qty-increment\"]')?.addEventListener('click', () => {\n if (this._value < max) {\n this._value++\n this.emitChange()\n this.render()\n }\n })\n\n const input = this.querySelector('input')\n input?.addEventListener('change', () => {\n this._value = wholeQuantity(input.value, min, max)\n this.emitChange()\n this.render()\n })\n }\n\n attributeChangedCallback(name: string): void {\n if (name === 'value') {\n this._value = wholeQuantity(this.getAttribute('value'), ...this.bounds())\n }\n if (this.isConnected) this.requestRender()\n }\n\n private emitChange(): void {\n this.dispatchEvent(\n new CustomEvent('nymbal:quantity:changed', {\n bubbles: true,\n composed: true,\n detail: { value: this._value },\n }),\n )\n }\n}\n","import { NymbalElement } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\nimport type { DenormalisedProduct } from '@nymbalhq/sdk'\n\nconst STYLES = `\nnymbal-search-bar {\n display: block;\n position: relative;\n font-family: var(--nymbal-font-family);\n}\nnymbal-search-bar input {\n font-family: var(--nymbal-font-family);\n font-size: var(--nymbal-font-size-base);\n width: 100%;\n padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-md);\n border: 1px solid var(--nymbal-color-border);\n border-radius: var(--nymbal-radius-md);\n outline: none;\n transition: border-color var(--nymbal-transition-fast);\n box-sizing: border-box;\n}\nnymbal-search-bar input:focus {\n border-color: var(--nymbal-color-accent);\n}\nnymbal-search-bar .nymbal-search-results {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n background: var(--nymbal-color-surface);\n border: 1px solid var(--nymbal-color-border);\n border-top: none;\n border-radius: 0 0 var(--nymbal-radius-md) var(--nymbal-radius-md);\n box-shadow: var(--nymbal-shadow-md);\n z-index: var(--nymbal-z-dropdown);\n max-height: 300px;\n overflow-y: auto;\n}\nnymbal-search-bar .nymbal-search-result-item {\n padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-md);\n cursor: pointer;\n transition: background var(--nymbal-transition-fast);\n}\nnymbal-search-bar .nymbal-search-result-item:hover,\nnymbal-search-bar .nymbal-search-result-item[aria-selected=\"true\"] {\n background: var(--nymbal-color-secondary, #f4f4f5);\n}\nnymbal-search-bar .nymbal-search-result-name {\n font-weight: var(--nymbal-font-weight-medium);\n}\nnymbal-search-bar .nymbal-search-result-price {\n font-size: var(--nymbal-font-size-sm);\n color: var(--nymbal-color-text-muted);\n}\nnymbal-search-bar .nymbal-search-loading {\n padding: var(--nymbal-spacing-md);\n text-align: center;\n color: var(--nymbal-color-text-muted);\n font-size: var(--nymbal-font-size-sm);\n}\n`\n\nexport class NymbalSearchBar extends NymbalElement {\n static get observedAttributes(): string[] {\n return ['placeholder', 'debounce-ms']\n }\n\n private debounceTimer: ReturnType<typeof setTimeout> | null = null\n private selectedIndex = -1\n\n protected setup(): void {\n injectStyles('nymbal-search-bar', STYLES)\n this.subscribeToStore(this.client.search)\n }\n\n protected render(): void {\n const state = this.client.search.getState()\n const placeholder = this.getAttribute('placeholder') ?? 'Search products...'\n\n let resultsHtml = ''\n if (state.query) {\n if (state.loading) {\n resultsHtml = `<div class=\"nymbal-search-results\" data-testid=\"search-results\"><div class=\"nymbal-search-loading\">Searching...</div></div>`\n } else if (state.results.length > 0) {\n resultsHtml = `<div class=\"nymbal-search-results\" data-testid=\"search-results\">\n ${state.results\n .map(\n (product, i) => `\n <div class=\"nymbal-search-result-item\" data-testid=\"search-result-${this.esc(product.slug)}\" data-slug=\"${this.esc(product.slug)}\" aria-selected=\"${i === this.selectedIndex}\">\n <div class=\"nymbal-search-result-name\">${this.esc(product.name)}</div>\n ${product.priceMinor !== null ? `<div class=\"nymbal-search-result-price\">${this.formatPrice(product.priceMinor, product.currency ?? 'USD')}</div>` : ''}\n </div>\n `,\n )\n .join('')}\n </div>`\n }\n }\n\n this.innerHTML = `\n <input data-testid=\"search-input\" type=\"search\" placeholder=\"${this.esc(placeholder)}\" value=\"${this.esc(state.query)}\" autocomplete=\"off\" role=\"combobox\" aria-expanded=\"${state.results.length > 0}\" aria-haspopup=\"listbox\">\n ${resultsHtml}\n `\n\n this.querySelectorAll<HTMLElement>('.nymbal-search-result-item').forEach((item, i) => {\n item.addEventListener('click', () => {\n const product = state.results[i]\n if (product) this.select(product)\n })\n })\n\n const input = this.querySelector('input')!\n input.addEventListener('input', () => {\n const query = input.value\n const debounceMs = Number(this.getAttribute('debounce-ms')) || 300\n if (this.debounceTimer !== null) clearTimeout(this.debounceTimer)\n this.debounceTimer = setTimeout(() => {\n this.selectedIndex = -1\n if (query.trim()) {\n this.client.search.search(query)\n } else {\n this.client.search.clearSearch()\n }\n }, debounceMs)\n })\n\n input.addEventListener('keydown', (e) => {\n const results = state.results\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n this.selectedIndex = Math.min(this.selectedIndex + 1, results.length - 1)\n this.render()\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n this.selectedIndex = Math.max(this.selectedIndex - 1, -1)\n this.render()\n } else if (e.key === 'Enter') {\n e.preventDefault()\n const selected = this.selectedIndex >= 0 ? results[this.selectedIndex] : undefined\n if (selected) {\n this.select(selected)\n } else {\n const query = input.value.trim()\n if (query) {\n window.location.href = `/search?q=${encodeURIComponent(query)}`\n }\n }\n } else if (e.key === 'Escape') {\n this.client.search.clearSearch()\n this.selectedIndex = -1\n }\n })\n }\n\n private select(product: DenormalisedProduct): void {\n this.dispatchEvent(\n new CustomEvent('nymbal:search:selected', {\n bubbles: true,\n composed: true,\n detail: { product, query: product.name },\n }),\n )\n this.client.search.clearSearch()\n }\n\n attributeChangedCallback(): void {\n if (this.isConnected) this.requestRender()\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback()\n if (this.debounceTimer !== null) {\n clearTimeout(this.debounceTimer)\n this.debounceTimer = null\n }\n }\n\n /** Escapes a value for both text and quoted-attribute contexts. */\n private esc(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\n }\n\n private formatPrice(minor: number, currency: string): string {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(minor / 100)\n }\n}\n","import type { Facet } from '@nymbalhq/types'\nimport { NymbalElement } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-product-filter {\n display: block;\n font-family: var(--nymbal-font-family);\n}\nnymbal-product-filter .nymbal-filter-group {\n margin-bottom: var(--nymbal-spacing-lg);\n}\nnymbal-product-filter .nymbal-filter-label {\n display: block;\n font-size: var(--nymbal-font-size-sm);\n font-weight: var(--nymbal-font-weight-bold);\n margin-bottom: var(--nymbal-spacing-sm);\n color: var(--nymbal-color-text);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\nnymbal-product-filter .nymbal-filter-option {\n display: flex;\n align-items: center;\n gap: var(--nymbal-spacing-sm);\n padding: var(--nymbal-spacing-xs) 0;\n cursor: pointer;\n font-size: var(--nymbal-font-size-sm);\n color: var(--nymbal-color-text);\n}\nnymbal-product-filter .nymbal-filter-option input[type=\"checkbox\"] {\n accent-color: var(--nymbal-color-primary);\n}\nnymbal-product-filter .nymbal-filter-count {\n color: var(--nymbal-color-text-muted);\n font-size: var(--nymbal-font-size-xs);\n}\n`\n\n\n/**\n * The facet filter. By default it reads the `facets` attribute and filters the product list. With `source=\"search\"`\n * it renders the search's own facets and narrows the search to one category: choosing an option selects it, choosing\n * the selected option again clears it.\n */\nexport class NymbalProductFilter extends NymbalElement {\n static get observedAttributes(): string[] {\n return ['facets', 'source']\n }\n\n protected setup(): void {\n injectStyles('nymbal-product-filter', STYLES)\n // Both: a wrapper may set `source` after the element has connected.\n this.subscribeToStore(this.client.productList)\n this.subscribeToStore(this.client.search)\n }\n\n protected render(): void {\n if (this.isSearch()) {\n this.renderSearch()\n return\n }\n const state = this.client.productList.getState()\n const facets = this.getFacets()\n\n if (facets.length === 0) {\n this.innerHTML = ''\n return\n }\n\n this.innerHTML = facets.map((facet) => {\n const activeFilter = state.filters[facet.field]\n const activeValues = Array.isArray(activeFilter)\n ? activeFilter\n : (activeFilter ? [activeFilter] : [])\n\n return `\n <div class=\"nymbal-filter-group\" data-testid=\"filter-group-${facet.field}\">\n <span class=\"nymbal-filter-label\">${this.esc(facet.label)}</span>\n ${facet.values.map((v) => {\n const checked = activeValues.includes(v.value)\n return `\n <label class=\"nymbal-filter-option\" data-testid=\"filter-option-${v.value}\">\n <input type=\"checkbox\" data-filter-name=\"${facet.field}\" data-filter-value=\"${v.value}\" ${checked ? 'checked' : ''}>\n <span>${this.esc(v.label ?? v.value)}</span>\n ${v.count !== undefined ? `<span class=\"nymbal-filter-count\">(${v.count})</span>` : ''}\n </label>\n `\n }).join('')}\n </div>\n `\n }).join('')\n\n this.querySelectorAll('input[type=\"checkbox\"]').forEach((input) => {\n input.addEventListener('change', () => {\n const el = input as HTMLInputElement\n const name = el.dataset['filterName']!\n const value = el.dataset['filterValue']!\n if (el.checked) {\n const current = state.filters[name]\n const values = Array.isArray(current) ? [...current, value] : (current ? [current, value] : value)\n this.client.productList.applyFilter(name, values)\n } else {\n const current = state.filters[name]\n if (Array.isArray(current)) {\n const remaining = current.filter((v) => v !== value)\n if (remaining.length > 0) {\n this.client.productList.applyFilter(name, remaining)\n } else {\n this.client.productList.removeFilter(name)\n }\n } else {\n this.client.productList.removeFilter(name)\n }\n }\n this.dispatchEvent(\n new CustomEvent('nymbal:filter:changed', { bubbles: true, composed: true, detail: { name, value } }),\n )\n })\n })\n }\n\n private renderSearch(): void {\n const { facets, category } = this.client.search.getState()\n const shown = facets.filter((f) => f.field === 'category')\n this.innerHTML = shown\n .map(\n (facet) => `\n <div class=\"nymbal-filter-group\" data-testid=\"filter-group-${this.esc(facet.field)}\">\n <span class=\"nymbal-filter-label\">${this.esc(facet.label)}</span>\n ${facet.values\n .map(\n (v) => `\n <label class=\"nymbal-filter-option\" data-testid=\"filter-option-${this.esc(v.value)}\">\n <input type=\"checkbox\" data-filter-value=\"${this.esc(v.value)}\" ${v.value === category ? 'checked' : ''}>\n <span>${this.esc(v.label ?? v.value)}</span>\n ${v.count !== undefined ? `<span class=\"nymbal-filter-count\">(${v.count})</span>` : ''}\n </label>\n `,\n )\n .join('')}\n </div>\n `,\n )\n .join('')\n\n this.querySelectorAll<HTMLInputElement>('input[type=\"checkbox\"]').forEach((input) => {\n input.addEventListener('change', () => {\n const value = input.dataset['filterValue']!\n void this.client.search.setCategory(input.checked ? value : null)\n this.dispatchEvent(\n new CustomEvent('nymbal:filter:changed', {\n bubbles: true,\n composed: true,\n detail: { name: 'category', value: input.checked ? value : '' },\n }),\n )\n })\n })\n }\n\n private isSearch(): boolean {\n return this.getAttribute('source') === 'search'\n }\n\n attributeChangedCallback(): void {\n if (this.isConnected) this.requestRender()\n }\n\n private getFacets(): Facet[] {\n const attr = this.getAttribute('facets')\n if (!attr) return []\n try {\n return JSON.parse(attr) as Facet[]\n } catch (err) {\n console.error('[nymbal-product-filter] failed to parse facets attribute', err)\n return []\n }\n }\n\n private esc(str: string): string {\n const div = document.createElement('div')\n div.textContent = str\n return div.innerHTML\n }\n}\n","import { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-toast {\n position: fixed;\n z-index: var(--nymbal-z-toast);\n display: flex;\n flex-direction: column;\n gap: var(--nymbal-spacing-sm);\n pointer-events: none;\n font-family: var(--nymbal-font-family);\n}\nnymbal-toast[position=\"top-right\"],\nnymbal-toast:not([position]) {\n top: var(--nymbal-spacing-lg);\n right: var(--nymbal-spacing-lg);\n}\nnymbal-toast[position=\"top-left\"] {\n top: var(--nymbal-spacing-lg);\n left: var(--nymbal-spacing-lg);\n}\nnymbal-toast[position=\"bottom-right\"] {\n bottom: var(--nymbal-spacing-lg);\n right: var(--nymbal-spacing-lg);\n}\nnymbal-toast[position=\"bottom-left\"] {\n bottom: var(--nymbal-spacing-lg);\n left: var(--nymbal-spacing-lg);\n}\nnymbal-toast .nymbal-toast-item {\n pointer-events: auto;\n display: flex;\n align-items: center;\n gap: var(--nymbal-spacing-sm);\n padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-md);\n border-radius: var(--nymbal-radius-md);\n box-shadow: var(--nymbal-shadow-lg);\n font-size: var(--nymbal-font-size-sm);\n background: var(--nymbal-color-surface);\n color: var(--nymbal-color-text);\n border: 1px solid var(--nymbal-color-border);\n animation: nymbal-toast-in var(--nymbal-transition-normal) forwards;\n max-width: 360px;\n}\nnymbal-toast .nymbal-toast-item[data-type=\"success\"] {\n border-left: 3px solid var(--nymbal-color-success);\n}\nnymbal-toast .nymbal-toast-item[data-type=\"error\"] {\n border-left: 3px solid var(--nymbal-color-error);\n}\nnymbal-toast .nymbal-toast-item[data-type=\"warning\"] {\n border-left: 3px solid var(--nymbal-color-warning);\n}\nnymbal-toast .nymbal-toast-item[data-type=\"info\"] {\n border-left: 3px solid var(--nymbal-color-accent);\n}\nnymbal-toast .nymbal-toast-close {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--nymbal-color-text-muted);\n font-size: var(--nymbal-font-size-base);\n padding: 0;\n margin-left: auto;\n}\n@keyframes nymbal-toast-in {\n from { opacity: 0; transform: translateY(-8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n`\n\nconst HTMLElementBase =\n typeof HTMLElement !== 'undefined' ? HTMLElement : (class {} as unknown as typeof HTMLElement)\n\nexport class NymbalToast extends HTMLElementBase {\n static get observedAttributes(): string[] {\n return ['position', 'duration-ms']\n }\n\n connectedCallback(): void {\n injectStyles('nymbal-toast', STYLES)\n this.setAttribute('data-testid', 'toast')\n }\n\n show(message: string, type: 'success' | 'error' | 'warning' | 'info' = 'info'): void {\n const durationMs = Number(this.getAttribute('duration-ms')) || 4000\n\n const item = document.createElement('div')\n item.className = 'nymbal-toast-item'\n item.setAttribute('data-type', type)\n item.setAttribute('role', 'alert')\n\n const text = document.createElement('span')\n text.textContent = message\n\n const close = document.createElement('button')\n close.className = 'nymbal-toast-close'\n close.setAttribute('aria-label', 'Dismiss')\n close.textContent = '×'\n close.addEventListener('click', () => this.dismiss(item))\n\n item.appendChild(text)\n item.appendChild(close)\n this.appendChild(item)\n\n setTimeout(() => this.dismiss(item), durationMs)\n }\n\n private dismiss(item: HTMLElement): void {\n if (item.parentNode === this) {\n this.removeChild(item)\n }\n }\n\n static showGlobal(message: string, type: 'success' | 'error' | 'warning' | 'info' = 'info'): void {\n let toastEl = document.querySelector('nymbal-toast') as NymbalToast | null\n if (!toastEl) {\n toastEl = document.createElement('nymbal-toast') as NymbalToast\n document.body.appendChild(toastEl)\n }\n toastEl.show(message, type)\n }\n}\n","import {\n getPaymentDriver,\n type PaymentConfirmContext,\n type PaymentConfirmation,\n type PaymentDriver,\n} from '@nymbalhq/sdk'\nimport type { PaymentClient } from '@nymbalhq/types'\nimport { HTMLElementBase } from '../base-element.js'\nimport { injectStyles } from '../styles.js'\n\nconst STYLES = `\nnymbal-payment-element {\n display: block;\n font-family: var(--nymbal-font-family);\n}\nnymbal-payment-element .nymbal-payment-note {\n margin: 0;\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n color: var(--nymbal-color-text-muted, #71717a);\n}\nnymbal-payment-element .nymbal-payment-card {\n padding: var(--nymbal-spacing-sm, 0.5rem) var(--nymbal-spacing-md, 1rem);\n border: 1px solid var(--nymbal-color-border, #e4e4e7);\n border-radius: var(--nymbal-radius-md, 0.5rem);\n background: var(--nymbal-color-surface, #fff);\n min-height: 40px;\n}\nnymbal-payment-element .nymbal-payment-error {\n margin: 0;\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n color: var(--nymbal-color-error, #dc2626);\n}\n`\n\n/** `pending` while a driver mounts; `error` when no payment can be taken, which a checkout must not submit past. */\nexport type PaymentElementStatus = 'pending' | 'ready' | 'error'\n\n/** No client half: nothing to collect, so nothing is confirmed here. */\nconst NO_PAYMENT_STEP: PaymentConfirmation = { ok: true, confirmed: false }\n\nfunction parseClient(value: string | null): PaymentClient | null | undefined {\n if (value === null || value === '' || value === 'null') return null\n try {\n const parsed = JSON.parse(value) as Partial<PaymentClient> | null\n if (parsed === null) return null\n if (typeof parsed.driver !== 'string' || typeof parsed.options !== 'object') return undefined\n return { driver: parsed.driver, options: { ...(parsed.options ?? {}) } }\n } catch {\n return undefined\n }\n}\n\n/**\n * <nymbal-payment-element client='<json>'>: renders the payments adapter's client half (GET /api/capabilities'\n * `payments.client`) through the driver the SDK registry holds under its `driver` name, and confirms a payment\n * through it. It emits `nymbal:payment:status` whenever `status` changes.\n */\nexport class NymbalPaymentElement extends HTMLElementBase {\n static get observedAttributes(): string[] {\n return ['client']\n }\n\n private _status: PaymentElementStatus = 'pending'\n private _error = ''\n private _driver: PaymentDriver | null = null\n private _mounted: Promise<boolean> = Promise.resolve(true)\n private _rendered: string | null | undefined = undefined\n private _generation = 0\n\n get status(): PaymentElementStatus {\n return this._status\n }\n\n connectedCallback(): void {\n injectStyles('nymbal-payment-element', STYLES)\n this.render()\n }\n\n attributeChangedCallback(): void {\n if (this.isConnected) this.render()\n }\n\n /**\n * Completes a payment the API left `requires_action`, once the driver has mounted, handing the driver the\n * checkout's `context`; the driver's answer as is.\n */\n async confirm(clientSecret: string, context?: PaymentConfirmContext): Promise<PaymentConfirmation> {\n const driver = this._driver\n if (this._status === 'error') return { ok: false, error: this._error }\n if (driver === null) return NO_PAYMENT_STEP\n if (!(await this._mounted)) return { ok: false, error: this._error }\n return driver.confirm(clientSecret, context)\n }\n\n private render(): void {\n const attribute = this.getAttribute('client')\n // The same client half again (a reconnect, a framework re-setting the attribute) keeps what is mounted.\n if (attribute === this._rendered) return\n this._rendered = attribute\n const generation = ++this._generation\n this._driver = null\n this._mounted = Promise.resolve(true)\n this.replaceChildren()\n\n const client = parseClient(attribute)\n if (client === undefined)\n return this.fail('The store sent a payment client this page cannot read.')\n if (client === null) return this.setStatus('ready')\n const driver = getPaymentDriver(client.driver)\n if (driver === undefined) {\n return this.fail(`Payment method \"${client.driver}\" is not available on this storefront.`)\n }\n\n this._driver = driver\n this.setStatus('pending')\n this._mounted = Promise.resolve()\n .then(() => driver.mount(this, client.options))\n .then(\n () => {\n if (generation === this._generation) this.setStatus('ready')\n return true\n },\n (err: unknown) => {\n if (generation === this._generation) {\n this.fail(err instanceof Error ? err.message : 'The payment form could not be loaded.')\n }\n return false\n },\n )\n }\n\n private fail(message: string): void {\n this._error = message\n const p = document.createElement('p')\n p.className = 'nymbal-payment-error'\n p.setAttribute('data-testid', 'payment-element-error')\n p.setAttribute('role', 'alert')\n p.textContent = message\n this.replaceChildren(p)\n this.setStatus('error')\n }\n\n private setStatus(status: PaymentElementStatus): void {\n if (status === this._status) return\n this._status = status\n this.dispatchEvent(\n new CustomEvent('nymbal:payment:status', {\n bubbles: true,\n composed: true,\n detail: { status },\n }),\n )\n }\n}\n","import { DEFAULT_PRODUCT_SORT, PRODUCT_SORT_LABELS, type ProductSortKey } from '@nymbalhq/types'\nimport { HTMLElementBase } from '../base-element.js'\n\nfunction parseKeys(value: string | null): string[] {\n if (!value) return []\n try {\n const parsed: unknown = JSON.parse(value)\n return Array.isArray(parsed) ? parsed.filter((k): k is string => typeof k === 'string') : []\n } catch (err) {\n console.error('[nymbal-sort-dropdown] failed to parse sort-keys attribute', err)\n return []\n }\n}\n\n/**\n * <nymbal-sort-dropdown sort-keys='[…]' value default-sort>: one option per sort key the store reports, in its order,\n * labelled from PRODUCT_SORT_LABELS. A `value` the keys lack shows `default-sort`. Choosing an option emits\n * `nymbal:sort:changed` with `{ sort }`. A `<select>` already inside it (a server render's) is the one it fills.\n */\nexport class NymbalSortDropdown extends HTMLElementBase {\n static get observedAttributes(): string[] {\n return ['sort-keys', 'value', 'default-sort']\n }\n\n private _select: HTMLSelectElement | null = null\n\n /** The key the dropdown shows. */\n get value(): string {\n return this._select?.value ?? ''\n }\n\n connectedCallback(): void {\n this.render()\n }\n\n attributeChangedCallback(): void {\n if (this.isConnected) this.render()\n }\n\n private selectElement(): HTMLSelectElement {\n if (this._select !== null && this.contains(this._select)) return this._select\n let select = this.querySelector('select')\n if (select === null) {\n select = document.createElement('select')\n select.setAttribute('aria-label', 'Sort products')\n this.appendChild(select)\n }\n if (!select.hasAttribute('data-testid')) select.setAttribute('data-testid', 'sort-dropdown')\n select.addEventListener('change', () => {\n this.dispatchEvent(\n new CustomEvent('nymbal:sort:changed', {\n bubbles: true,\n composed: true,\n detail: { sort: select.value },\n }),\n )\n })\n this._select = select\n return select\n }\n\n private render(): void {\n const keys = parseKeys(this.getAttribute('sort-keys'))\n const wanted = this.getAttribute('value') ?? ''\n const fallback = this.getAttribute('default-sort') ?? DEFAULT_PRODUCT_SORT\n const selected = keys.includes(wanted) ? wanted : keys.includes(fallback) ? fallback : keys[0]\n\n const select = this.selectElement()\n select.replaceChildren(\n ...keys.map((key) => {\n const option = document.createElement('option')\n option.value = key\n option.textContent = PRODUCT_SORT_LABELS[key as ProductSortKey] ?? key\n return option\n }),\n )\n if (selected !== undefined) select.value = selected\n }\n}\n","export { registerClient, getClient } from './registry.js'\nexport { NymbalElement } from './base-element.js'\nexport { injectStyles } from './styles.js'\n\nimport { NymbalAddToCart } from './components/add-to-cart.js'\nimport { NymbalCartDrawer } from './components/cart-drawer.js'\nimport { NymbalMiniCart } from './components/mini-cart.js'\nimport { NymbalVariantSelector } from './components/variant-selector.js'\nimport { NymbalQuantitySelector } from './components/quantity-selector.js'\nimport { NymbalSearchBar } from './components/search-bar.js'\nimport { NymbalProductFilter } from './components/product-filter.js'\nimport { NymbalToast } from './components/toast.js'\nimport { NymbalPaymentElement } from './components/payment-element.js'\nimport { NymbalSortDropdown } from './components/sort-dropdown.js'\nimport { getPaymentDriver, registerPaymentDriver, type PaymentDriver } from '@nymbalhq/sdk'\nimport { testModeDriver } from './payment-drivers/test-mode.js'\nimport { stripeDriver } from './payment-drivers/stripe.js'\n\nexport type { PaymentElementStatus } from './components/payment-element.js'\n\nexport {\n NymbalAddToCart,\n NymbalCartDrawer,\n NymbalMiniCart,\n NymbalVariantSelector,\n NymbalQuantitySelector,\n NymbalSearchBar,\n NymbalProductFilter,\n NymbalToast,\n NymbalPaymentElement,\n NymbalSortDropdown,\n}\n\nexport function registerComponents(): void {\n const components: Array<[string, CustomElementConstructor]> = [\n ['nymbal-add-to-cart', NymbalAddToCart],\n ['nymbal-cart-drawer', NymbalCartDrawer],\n ['nymbal-mini-cart', NymbalMiniCart],\n ['nymbal-variant-selector', NymbalVariantSelector],\n ['nymbal-quantity-selector', NymbalQuantitySelector],\n ['nymbal-search-bar', NymbalSearchBar],\n ['nymbal-product-filter', NymbalProductFilter],\n ['nymbal-toast', NymbalToast],\n ['nymbal-payment-element', NymbalPaymentElement],\n ['nymbal-sort-dropdown', NymbalSortDropdown],\n ]\n // A driver the project registered under a built-in's name wins, as a project-defined tag does.\n const drivers: Array<[string, PaymentDriver]> = [\n ['test-mode', testModeDriver],\n ['stripe', stripeDriver],\n ]\n\n for (const [name, driver] of drivers) {\n if (getPaymentDriver(name) === undefined) registerPaymentDriver(name, driver)\n }\n for (const [name, ctor] of components) {\n if (!customElements.get(name)) {\n customElements.define(name, ctor)\n }\n }\n}\n","import type { PaymentDriver } from '@nymbalhq/sdk'\n\n/**\n * For a store whose payments need no shopper input (the native stub): nothing to collect, and nothing confirmed here.\n * An order the stub holds as `requires_action` stays pending until its webhook pays it.\n */\nexport const testModeDriver: PaymentDriver = {\n mount(el) {\n el.innerHTML = '<p class=\"nymbal-payment-note\">Test mode — payment skipped</p>'\n },\n confirm() {\n return Promise.resolve({ ok: true, confirmed: false })\n },\n}\n","import type { PaymentConfirmContext, PaymentConfirmation, PaymentDriver } from '@nymbalhq/sdk'\n\n// Stripe.js from Stripe's own host, as Stripe requires, so no project installs a Stripe package to take a card.\nconst STRIPE_JS = 'https://js.stripe.com/v3/'\n\ninterface StripeCard {\n mount(target: HTMLElement): void\n}\n\ninterface StripeBillingDetails {\n email?: string\n name?: string\n address?: {\n line1: string\n line2?: string\n city: string\n state: string\n postal_code: string\n country: string\n }\n}\n\ninterface StripeInstance {\n elements(): { create(type: 'card', options?: Record<string, unknown>): StripeCard }\n confirmCardPayment(\n clientSecret: string,\n data: { payment_method: { card: StripeCard; billing_details?: StripeBillingDetails } },\n ): Promise<{ paymentIntent?: { status: string }; error?: { message?: string } }>\n}\n\n/** The checkout's shopper details as Stripe's `billing_details`, which its fraud checks read; none when empty. */\nfunction billingDetails(context: PaymentConfirmContext | undefined): StripeBillingDetails | undefined {\n const details: StripeBillingDetails = {}\n if (context?.email) details.email = context.email\n const billing = context?.billing\n if (billing) {\n const name = `${billing.firstName} ${billing.lastName}`.trim()\n if (name) details.name = name\n details.address = {\n line1: billing.addressLine1,\n ...(billing.addressLine2 ? { line2: billing.addressLine2 } : {}),\n city: billing.city,\n state: billing.region,\n postal_code: billing.postalCode,\n country: billing.country,\n }\n }\n return Object.keys(details).length > 0 ? details : undefined\n}\n\ntype StripeFactory = (publishableKey: string) => StripeInstance\n\nlet loading: Promise<StripeFactory> | null = null\n\n/** `window.Stripe`, injecting Stripe.js once per page when it is not there yet. */\nfunction loadStripeJs(): Promise<StripeFactory> {\n const win = window as unknown as { Stripe?: StripeFactory }\n if (win.Stripe) return Promise.resolve(win.Stripe)\n loading ??= new Promise<StripeFactory>((resolve, reject) => {\n const script = document.createElement('script')\n script.src = STRIPE_JS\n script.async = true\n script.addEventListener('load', () => {\n if (win.Stripe) resolve(win.Stripe)\n else reject(new Error('Stripe.js loaded but window.Stripe is missing.'))\n })\n script.addEventListener('error', () => {\n loading = null\n script.remove()\n reject(new Error('Stripe.js could not be loaded.'))\n })\n document.head.appendChild(script)\n })\n return loading\n}\n\nconst instances = new Map<string, StripeInstance>()\nlet mounted: Array<{ host: HTMLElement; stripe: StripeInstance; card: StripeCard }> = []\n\n/** A card form from Stripe.js; confirm pays the intent with the card the shopper typed into. */\nexport const stripeDriver: PaymentDriver = {\n async mount(el, options) {\n const key = options['publishableKey']\n if (!key)\n throw new Error('Card payments are not configured: the store has no Stripe publishable key.')\n const factory = await loadStripeJs()\n let stripe = instances.get(key)\n if (stripe === undefined) {\n stripe = factory(key)\n instances.set(key, stripe)\n }\n const target = document.createElement('div')\n target.className = 'nymbal-payment-card'\n el.replaceChildren(target)\n const card = stripe.elements().create('card', {\n style: {\n base: { fontSize: '16px', color: '#1a1a1a', '::placeholder': { color: '#9ca3af' } },\n },\n })\n card.mount(target)\n mounted = [\n ...mounted.filter((m) => m.host.isConnected && m.host !== el),\n { host: el, stripe, card },\n ]\n },\n\n async confirm(clientSecret, context): Promise<PaymentConfirmation> {\n const current = mounted.findLast((m) => m.host.isConnected) ?? mounted.at(-1)\n if (current === undefined) return { ok: false, error: 'The card form is not ready.' }\n const billing_details = billingDetails(context)\n const result = await current.stripe.confirmCardPayment(clientSecret, {\n payment_method: { card: current.card, ...(billing_details ? { billing_details } : {}) },\n })\n if (result.error) return { ok: false, error: result.error.message ?? 'Payment failed.' }\n // `processing` is accepted but not paid yet: the webhook confirms it.\n return { ok: true, confirmed: result.paymentIntent?.status === 'succeeded' }\n },\n}\n"],"mappings":"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","names":["STYLES","STYLES","STYLES","STYLES","STYLES","STYLES","STYLES","HTMLElementBase","STYLES","getPaymentDriver","getPaymentDriver"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nymbalhq/web-components",
3
- "version": "0.2.0",
3
+ "version": "1.1.0",
4
4
  "description": "Interactive commerce UI as native Web Components",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,8 +29,8 @@
29
29
  "src/tokens/tokens.css"
30
30
  ],
31
31
  "dependencies": {
32
- "@nymbalhq/sdk": "0.2.0",
33
- "@nymbalhq/types": "0.2.0"
32
+ "@nymbalhq/sdk": "1.1.0",
33
+ "@nymbalhq/types": "1.1.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "tsup": "^8.3.5",