@nymbalhq/web-components 1.1.0 → 1.3.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.
Files changed (187) hide show
  1. package/astro/NymbalElement.astro +19 -0
  2. package/dist/base-element-CPMgAdyQ.d.ts +91 -0
  3. package/dist/chunk-2TPQNQYO.js +155 -0
  4. package/dist/chunk-2TPQNQYO.js.map +1 -0
  5. package/dist/chunk-2XXBQPDB.js +113 -0
  6. package/dist/chunk-2XXBQPDB.js.map +1 -0
  7. package/dist/chunk-42SSME5H.js +84 -0
  8. package/dist/chunk-42SSME5H.js.map +1 -0
  9. package/dist/chunk-4COVK727.js +204 -0
  10. package/dist/chunk-4COVK727.js.map +1 -0
  11. package/dist/chunk-5G3C53A2.js +666 -0
  12. package/dist/chunk-5G3C53A2.js.map +1 -0
  13. package/dist/chunk-5VWZ2U5K.js +149 -0
  14. package/dist/chunk-5VWZ2U5K.js.map +1 -0
  15. package/dist/chunk-7JAAOVCE.js +114 -0
  16. package/dist/chunk-7JAAOVCE.js.map +1 -0
  17. package/dist/chunk-B73QVSA5.js +206 -0
  18. package/dist/chunk-B73QVSA5.js.map +1 -0
  19. package/dist/chunk-DR5QB5VN.js +17 -0
  20. package/dist/chunk-DR5QB5VN.js.map +1 -0
  21. package/dist/chunk-DXWMNB5F.js +70 -0
  22. package/dist/chunk-DXWMNB5F.js.map +1 -0
  23. package/dist/chunk-EBMA7JGV.js +130 -0
  24. package/dist/chunk-EBMA7JGV.js.map +1 -0
  25. package/dist/chunk-EYDMJNNK.js +176 -0
  26. package/dist/chunk-EYDMJNNK.js.map +1 -0
  27. package/dist/chunk-FIF6GQ6U.js +141 -0
  28. package/dist/chunk-FIF6GQ6U.js.map +1 -0
  29. package/dist/chunk-GHXTJ2CF.js +109 -0
  30. package/dist/chunk-GHXTJ2CF.js.map +1 -0
  31. package/dist/chunk-H3ZBYNHV.js +153 -0
  32. package/dist/chunk-H3ZBYNHV.js.map +1 -0
  33. package/dist/chunk-HPBC3OG7.js +174 -0
  34. package/dist/chunk-HPBC3OG7.js.map +1 -0
  35. package/dist/chunk-IYT7VXFS.js +105 -0
  36. package/dist/chunk-IYT7VXFS.js.map +1 -0
  37. package/dist/chunk-LHYXNQWY.js +105 -0
  38. package/dist/chunk-LHYXNQWY.js.map +1 -0
  39. package/dist/chunk-LO2EW5YP.js +25 -0
  40. package/dist/chunk-LO2EW5YP.js.map +1 -0
  41. package/dist/chunk-LSQPUZK6.js +291 -0
  42. package/dist/chunk-LSQPUZK6.js.map +1 -0
  43. package/dist/chunk-N4Y5DI4X.js +82 -0
  44. package/dist/chunk-N4Y5DI4X.js.map +1 -0
  45. package/dist/chunk-NE6UHTOT.js +148 -0
  46. package/dist/chunk-NE6UHTOT.js.map +1 -0
  47. package/dist/chunk-NW47OJVN.js +138 -0
  48. package/dist/chunk-NW47OJVN.js.map +1 -0
  49. package/dist/chunk-O2YKCDR2.js +77 -0
  50. package/dist/chunk-O2YKCDR2.js.map +1 -0
  51. package/dist/chunk-PN6QKYE4.js +71 -0
  52. package/dist/chunk-PN6QKYE4.js.map +1 -0
  53. package/dist/chunk-RAOGZH3Z.js +188 -0
  54. package/dist/chunk-RAOGZH3Z.js.map +1 -0
  55. package/dist/chunk-RVSKX2RX.js +11 -0
  56. package/dist/chunk-RVSKX2RX.js.map +1 -0
  57. package/dist/chunk-RZVFA3N5.js +154 -0
  58. package/dist/chunk-RZVFA3N5.js.map +1 -0
  59. package/dist/chunk-SM7TASEW.js +135 -0
  60. package/dist/chunk-SM7TASEW.js.map +1 -0
  61. package/dist/chunk-U2PU4CWB.js +124 -0
  62. package/dist/chunk-U2PU4CWB.js.map +1 -0
  63. package/dist/chunk-UCEVCLQ7.js +5 -0
  64. package/dist/chunk-UCEVCLQ7.js.map +1 -0
  65. package/dist/chunk-UYSZOEOA.js +32 -0
  66. package/dist/chunk-UYSZOEOA.js.map +1 -0
  67. package/dist/chunk-V55K2CS5.js +126 -0
  68. package/dist/chunk-V55K2CS5.js.map +1 -0
  69. package/dist/chunk-VTQVGNVC.js +346 -0
  70. package/dist/chunk-VTQVGNVC.js.map +1 -0
  71. package/dist/chunk-W23P7SI4.js +239 -0
  72. package/dist/chunk-W23P7SI4.js.map +1 -0
  73. package/dist/chunk-W7D3STJH.js +279 -0
  74. package/dist/chunk-W7D3STJH.js.map +1 -0
  75. package/dist/chunk-WEEHXWM3.js +172 -0
  76. package/dist/chunk-WEEHXWM3.js.map +1 -0
  77. package/dist/chunk-WTEVTCIL.js +128 -0
  78. package/dist/chunk-WTEVTCIL.js.map +1 -0
  79. package/dist/chunk-XPFGMYBV.js +137 -0
  80. package/dist/chunk-XPFGMYBV.js.map +1 -0
  81. package/dist/elements/nymbal-add-to-cart.d.ts +14 -0
  82. package/dist/elements/nymbal-add-to-cart.js +10 -0
  83. package/dist/elements/nymbal-add-to-cart.js.map +1 -0
  84. package/dist/elements/nymbal-address-book.d.ts +30 -0
  85. package/dist/elements/nymbal-address-book.js +13 -0
  86. package/dist/elements/nymbal-address-book.js.map +1 -0
  87. package/dist/elements/nymbal-address-form.d.ts +29 -0
  88. package/dist/elements/nymbal-address-form.js +12 -0
  89. package/dist/elements/nymbal-address-form.js.map +1 -0
  90. package/dist/elements/nymbal-breadcrumb.d.ts +15 -0
  91. package/dist/elements/nymbal-breadcrumb.js +11 -0
  92. package/dist/elements/nymbal-breadcrumb.js.map +1 -0
  93. package/dist/elements/nymbal-cart-drawer.d.ts +24 -0
  94. package/dist/elements/nymbal-cart-drawer.js +11 -0
  95. package/dist/elements/nymbal-cart-drawer.js.map +1 -0
  96. package/dist/elements/nymbal-cart-page.d.ts +20 -0
  97. package/dist/elements/nymbal-cart-page.js +14 -0
  98. package/dist/elements/nymbal-cart-page.js.map +1 -0
  99. package/dist/elements/nymbal-category-nav.d.ts +15 -0
  100. package/dist/elements/nymbal-category-nav.js +11 -0
  101. package/dist/elements/nymbal-category-nav.js.map +1 -0
  102. package/dist/elements/nymbal-checkout.d.ts +77 -0
  103. package/dist/elements/nymbal-checkout.js +15 -0
  104. package/dist/elements/nymbal-checkout.js.map +1 -0
  105. package/dist/elements/nymbal-image-gallery.d.ts +19 -0
  106. package/dist/elements/nymbal-image-gallery.js +10 -0
  107. package/dist/elements/nymbal-image-gallery.js.map +1 -0
  108. package/dist/elements/nymbal-login-form.d.ts +17 -0
  109. package/dist/elements/nymbal-login-form.js +12 -0
  110. package/dist/elements/nymbal-login-form.js.map +1 -0
  111. package/dist/elements/nymbal-mini-cart.d.ts +17 -0
  112. package/dist/elements/nymbal-mini-cart.js +10 -0
  113. package/dist/elements/nymbal-mini-cart.js.map +1 -0
  114. package/dist/elements/nymbal-order-summary.d.ts +20 -0
  115. package/dist/elements/nymbal-order-summary.js +11 -0
  116. package/dist/elements/nymbal-order-summary.js.map +1 -0
  117. package/dist/elements/nymbal-payment-element.d.ts +4 -0
  118. package/dist/elements/nymbal-payment-element.js +10 -0
  119. package/dist/elements/nymbal-payment-element.js.map +1 -0
  120. package/dist/elements/nymbal-product-card.d.ts +4 -0
  121. package/dist/elements/nymbal-product-card.js +12 -0
  122. package/dist/elements/nymbal-product-card.js.map +1 -0
  123. package/dist/elements/nymbal-product-filter.d.ts +32 -0
  124. package/dist/elements/nymbal-product-filter.js +10 -0
  125. package/dist/elements/nymbal-product-filter.js.map +1 -0
  126. package/dist/elements/nymbal-product-grid.d.ts +24 -0
  127. package/dist/elements/nymbal-product-grid.js +14 -0
  128. package/dist/elements/nymbal-product-grid.js.map +1 -0
  129. package/dist/elements/nymbal-product-price.d.ts +18 -0
  130. package/dist/elements/nymbal-product-price.js +12 -0
  131. package/dist/elements/nymbal-product-price.js.map +1 -0
  132. package/dist/elements/nymbal-profile-form.d.ts +19 -0
  133. package/dist/elements/nymbal-profile-form.js +12 -0
  134. package/dist/elements/nymbal-profile-form.js.map +1 -0
  135. package/dist/elements/nymbal-quantity-selector.d.ts +20 -0
  136. package/dist/elements/nymbal-quantity-selector.js +10 -0
  137. package/dist/elements/nymbal-quantity-selector.js.map +1 -0
  138. package/dist/elements/nymbal-register-form.d.ts +17 -0
  139. package/dist/elements/nymbal-register-form.js +12 -0
  140. package/dist/elements/nymbal-register-form.js.map +1 -0
  141. package/dist/elements/nymbal-review-form.d.ts +17 -0
  142. package/dist/elements/nymbal-review-form.js +12 -0
  143. package/dist/elements/nymbal-review-form.js.map +1 -0
  144. package/dist/elements/nymbal-review-list.d.ts +14 -0
  145. package/dist/elements/nymbal-review-list.js +10 -0
  146. package/dist/elements/nymbal-review-list.js.map +1 -0
  147. package/dist/elements/nymbal-search-bar.d.ts +19 -0
  148. package/dist/elements/nymbal-search-bar.js +10 -0
  149. package/dist/elements/nymbal-search-bar.js.map +1 -0
  150. package/dist/elements/nymbal-search-results.d.ts +29 -0
  151. package/dist/elements/nymbal-search-results.js +14 -0
  152. package/dist/elements/nymbal-search-results.js.map +1 -0
  153. package/dist/elements/nymbal-site-footer.d.ts +15 -0
  154. package/dist/elements/nymbal-site-footer.js +11 -0
  155. package/dist/elements/nymbal-site-footer.js.map +1 -0
  156. package/dist/elements/nymbal-site-header.d.ts +19 -0
  157. package/dist/elements/nymbal-site-header.js +11 -0
  158. package/dist/elements/nymbal-site-header.js.map +1 -0
  159. package/dist/elements/nymbal-sort-dropdown.d.ts +28 -0
  160. package/dist/elements/nymbal-sort-dropdown.js +10 -0
  161. package/dist/elements/nymbal-sort-dropdown.js.map +1 -0
  162. package/dist/elements/nymbal-stock-badge.d.ts +16 -0
  163. package/dist/elements/nymbal-stock-badge.js +11 -0
  164. package/dist/elements/nymbal-stock-badge.js.map +1 -0
  165. package/dist/elements/nymbal-toast.d.ts +18 -0
  166. package/dist/elements/nymbal-toast.js +10 -0
  167. package/dist/elements/nymbal-toast.js.map +1 -0
  168. package/dist/elements/nymbal-variant-selector.d.ts +16 -0
  169. package/dist/elements/nymbal-variant-selector.js +10 -0
  170. package/dist/elements/nymbal-variant-selector.js.map +1 -0
  171. package/dist/form-element-C-qo3gpD.d.ts +25 -0
  172. package/dist/hydrate-support.d.ts +2 -0
  173. package/dist/hydrate-support.js +3 -0
  174. package/dist/hydrate-support.js.map +1 -0
  175. package/dist/index.d.ts +38 -157
  176. package/dist/index.js +127 -1407
  177. package/dist/index.js.map +1 -1
  178. package/dist/nymbal-payment-element-Bt8EF42T.d.ts +51 -0
  179. package/dist/nymbal-product-card-C8HkE_rG.d.ts +47 -0
  180. package/dist/register-components.d.ts +46 -0
  181. package/dist/register-components.js +48 -0
  182. package/dist/register-components.js.map +1 -0
  183. package/dist/selected-variant-Dym0shlJ.d.ts +23 -0
  184. package/dist/ssr/index.d.ts +30 -0
  185. package/dist/ssr/index.js +143 -0
  186. package/dist/ssr/index.js.map +1 -0
  187. package/package.json +33 -6
package/dist/index.js CHANGED
@@ -1,1424 +1,144 @@
1
- // src/registry.ts
2
- var CLIENT_KEY = /* @__PURE__ */ Symbol.for("nymbal.client");
3
- function registerClient(client) {
4
- globalThis[CLIENT_KEY] = client;
5
- }
6
- function getClient() {
7
- const client = globalThis[CLIENT_KEY];
8
- if (!client) {
9
- throw new Error("NymbalClient not registered. Call registerClient() before using components.");
10
- }
11
- return client;
12
- }
13
-
14
- // src/base-element.ts
15
- var HTMLElementBase = typeof HTMLElement !== "undefined" ? HTMLElement : class {
16
- };
17
- var NymbalElement = class extends HTMLElementBase {
18
- _unsubs = [];
19
- _renderQueued = false;
20
- get client() {
21
- return getClient();
22
- }
23
- subscribeToStore(store) {
24
- this._unsubs.push(store.subscribe(() => this.requestRender()));
25
- }
26
- requestRender() {
27
- if (this._renderQueued) return;
28
- this._renderQueued = true;
29
- queueMicrotask(() => {
30
- this._renderQueued = false;
31
- this.render();
32
- });
33
- }
34
- connectedCallback() {
35
- this.setup();
36
- this.render();
37
- }
38
- disconnectedCallback() {
39
- for (const fn of this._unsubs) fn();
40
- this._unsubs = [];
41
- }
42
- };
43
-
44
- // src/styles.ts
45
- var injected = /* @__PURE__ */ new Set();
46
- function injectStyles(tagName, css) {
47
- if (injected.has(tagName)) return;
48
- if (typeof document === "undefined") return;
49
- const style = document.createElement("style");
50
- style.setAttribute("data-nymbal", tagName);
51
- style.textContent = css;
52
- document.head.appendChild(style);
53
- injected.add(tagName);
54
- }
55
-
56
- // src/components/add-to-cart.ts
57
- var STYLES = `
58
- nymbal-add-to-cart {
59
- display: inline-block;
60
- }
61
- nymbal-add-to-cart button {
62
- font-family: var(--nymbal-font-family);
63
- font-size: var(--nymbal-font-size-base);
64
- font-weight: var(--nymbal-font-weight-medium);
65
- background: var(--nymbal-color-primary);
66
- color: var(--nymbal-color-text-inverse);
67
- border: none;
68
- border-radius: var(--nymbal-radius-md);
69
- padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-lg);
70
- cursor: pointer;
71
- transition: background var(--nymbal-transition-fast);
72
- min-width: 140px;
73
- }
74
- nymbal-add-to-cart button:hover:not(:disabled) {
75
- background: var(--nymbal-color-primary-hover);
76
- }
77
- nymbal-add-to-cart button:disabled {
78
- opacity: 0.5;
79
- cursor: not-allowed;
80
- }
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
- }
89
- var NymbalAddToCart = class extends NymbalElement {
90
- static get observedAttributes() {
91
- return ["variant-id", "qty", "disabled"];
92
- }
93
- button;
94
- setup() {
95
- injectStyles("nymbal-add-to-cart", STYLES);
96
- this.subscribeToStore(this.client.cart);
97
- }
98
- render() {
99
- const variantId = this.getAttribute("variant-id");
100
- const isDisabled = this.hasAttribute("disabled");
101
- const cartState = this.client.cart.getState();
102
- if (!this.button) {
103
- this.button = document.createElement("button");
104
- this.button.setAttribute("type", "button");
105
- this.button.setAttribute("data-testid", "add-to-cart");
106
- this.button.addEventListener("click", this.handleClick);
107
- this.innerHTML = "";
108
- this.appendChild(this.button);
109
- }
110
- this.button.disabled = isDisabled || cartState.loading || !variantId;
111
- this.button.setAttribute("aria-busy", String(cartState.loading));
112
- this.button.textContent = cartState.loading ? "Adding..." : "Add to Cart";
113
- }
114
- attributeChangedCallback() {
115
- if (this.isConnected) this.requestRender();
116
- }
117
- handleClick = async () => {
118
- const variantId = this.getAttribute("variant-id");
119
- if (!variantId) return;
120
- const qty = quantityOf(this.getAttribute("qty"));
121
- try {
122
- await this.client.cart.addItem(variantId, qty);
123
- this.dispatchEvent(
124
- new CustomEvent("nymbal:cart:item-added", {
125
- bubbles: true,
126
- composed: true,
127
- detail: { variantId, qty }
128
- })
129
- );
130
- } catch {
131
- }
132
- };
133
- };
134
-
135
- // src/components/cart-drawer.ts
136
- var STYLES2 = `
137
- nymbal-cart-drawer {
138
- display: block;
139
- }
140
- nymbal-cart-drawer .nymbal-drawer-overlay {
141
- position: fixed;
142
- inset: 0;
143
- background: var(--nymbal-color-overlay);
144
- z-index: var(--nymbal-z-drawer);
145
- opacity: 0;
146
- visibility: hidden;
147
- transition: opacity var(--nymbal-transition-normal), visibility var(--nymbal-transition-normal);
148
- }
149
- nymbal-cart-drawer[open] .nymbal-drawer-overlay {
150
- opacity: 1;
151
- visibility: visible;
152
- }
153
- nymbal-cart-drawer .nymbal-drawer-panel {
154
- position: fixed;
155
- top: 0;
156
- right: 0;
157
- bottom: 0;
158
- width: min(400px, 90vw);
159
- background: var(--nymbal-color-surface);
160
- z-index: var(--nymbal-z-drawer);
161
- transform: translateX(100%);
162
- transition: transform var(--nymbal-transition-normal);
163
- display: flex;
164
- flex-direction: column;
165
- font-family: var(--nymbal-font-family);
166
- }
167
- nymbal-cart-drawer[open] .nymbal-drawer-panel {
168
- transform: translateX(0);
169
- }
170
- nymbal-cart-drawer .nymbal-drawer-header {
171
- display: flex;
172
- justify-content: space-between;
173
- align-items: center;
174
- padding: var(--nymbal-spacing-md) var(--nymbal-spacing-lg);
175
- border-bottom: 1px solid var(--nymbal-color-border);
176
- }
177
- nymbal-cart-drawer .nymbal-drawer-header h2 {
178
- margin: 0;
179
- font-size: var(--nymbal-font-size-lg);
180
- font-weight: var(--nymbal-font-weight-bold);
181
- }
182
- nymbal-cart-drawer .nymbal-drawer-close {
183
- background: none;
184
- border: none;
185
- font-size: var(--nymbal-font-size-xl);
186
- cursor: pointer;
187
- padding: var(--nymbal-spacing-xs);
188
- color: var(--nymbal-color-text-muted);
189
- }
190
- nymbal-cart-drawer .nymbal-drawer-items {
191
- flex: 1;
192
- overflow-y: auto;
193
- padding: var(--nymbal-spacing-md) var(--nymbal-spacing-lg);
194
- }
195
- nymbal-cart-drawer .nymbal-cart-item {
196
- display: flex;
197
- gap: var(--nymbal-spacing-md);
198
- padding: var(--nymbal-spacing-md) 0;
199
- border-bottom: 1px solid var(--nymbal-color-border);
200
- }
201
- nymbal-cart-drawer .nymbal-cart-item-info {
202
- flex: 1;
203
- }
204
- nymbal-cart-drawer .nymbal-cart-item-name {
205
- font-weight: var(--nymbal-font-weight-medium);
206
- margin-bottom: var(--nymbal-spacing-xs);
207
- }
208
- nymbal-cart-drawer .nymbal-cart-item-variant {
209
- font-size: var(--nymbal-font-size-sm);
210
- color: var(--nymbal-color-text-muted);
211
- }
212
- nymbal-cart-drawer .nymbal-cart-item-actions {
213
- display: flex;
214
- align-items: center;
215
- gap: var(--nymbal-spacing-sm);
216
- }
217
- nymbal-cart-drawer .nymbal-cart-item-remove {
218
- background: none;
219
- border: none;
220
- color: var(--nymbal-color-error);
221
- cursor: pointer;
222
- font-size: var(--nymbal-font-size-sm);
223
- padding: var(--nymbal-spacing-xs);
224
- }
225
- nymbal-cart-drawer .nymbal-drawer-footer {
226
- padding: var(--nymbal-spacing-md) var(--nymbal-spacing-lg);
227
- border-top: 1px solid var(--nymbal-color-border);
228
- }
229
- nymbal-cart-drawer .nymbal-cart-subtotal {
230
- display: flex;
231
- justify-content: space-between;
232
- font-weight: var(--nymbal-font-weight-bold);
233
- margin-bottom: var(--nymbal-spacing-md);
234
- }
235
- nymbal-cart-drawer .nymbal-checkout-btn {
236
- width: 100%;
237
- font-family: var(--nymbal-font-family);
238
- font-size: var(--nymbal-font-size-base);
239
- font-weight: var(--nymbal-font-weight-medium);
240
- background: var(--nymbal-color-primary);
241
- color: var(--nymbal-color-text-inverse);
242
- border: none;
243
- border-radius: var(--nymbal-radius-md);
244
- padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-lg);
245
- cursor: pointer;
246
- transition: background var(--nymbal-transition-fast);
247
- }
248
- nymbal-cart-drawer .nymbal-checkout-btn:hover {
249
- background: var(--nymbal-color-primary-hover);
250
- }
251
- nymbal-cart-drawer .nymbal-drawer-empty {
252
- text-align: center;
253
- color: var(--nymbal-color-text-muted);
254
- padding: var(--nymbal-spacing-xl) 0;
255
- }
256
- `;
257
- var NymbalCartDrawer = class extends NymbalElement {
258
- static get observedAttributes() {
259
- return ["open"];
260
- }
261
- handleItemAdded = () => {
262
- this.open();
263
- };
264
- handleMiniCartClicked = () => {
265
- this.toggle();
266
- };
267
- setup() {
268
- injectStyles("nymbal-cart-drawer", STYLES2);
269
- this.subscribeToStore(this.client.cart);
270
- document.addEventListener("nymbal:cart:item-added", this.handleItemAdded);
271
- document.addEventListener("nymbal:mini-cart:clicked", this.handleMiniCartClicked);
272
- }
273
- disconnectedCallback() {
274
- super.disconnectedCallback();
275
- document.removeEventListener("nymbal:cart:item-added", this.handleItemAdded);
276
- document.removeEventListener("nymbal:mini-cart:clicked", this.handleMiniCartClicked);
277
- }
278
- open() {
279
- this.setAttribute("open", "");
280
- window.dispatchEvent(new CustomEvent("nymbal:cart:open"));
281
- }
282
- close() {
283
- this.removeAttribute("open");
284
- window.dispatchEvent(new CustomEvent("nymbal:cart:close"));
285
- }
286
- toggle() {
287
- if (this.hasAttribute("open")) {
288
- this.close();
289
- } else {
290
- this.open();
291
- }
292
- }
293
- render() {
294
- const state = this.client.cart.getState();
295
- const currency = state.currency;
296
- const itemsHtml = state.items.length === 0 ? `<div class="nymbal-drawer-empty">Your cart is empty</div>` : state.items.map((item) => `
297
- <div class="nymbal-cart-item" data-testid="cart-item-${item.variantId}">
298
- <div class="nymbal-cart-item-info">
299
- <div class="nymbal-cart-item-name">${this.esc(item.productName)}</div>
300
- <div class="nymbal-cart-item-variant">${this.esc(item.variantName)}</div>
301
- <div class="nymbal-cart-item-qty">Qty: ${item.qty}</div>
302
- </div>
303
- <div class="nymbal-cart-item-actions">
304
- <span>${this.formatPrice(item.priceMinor * item.qty, currency)}</span>
305
- <button type="button" class="nymbal-cart-item-remove" data-testid="remove-item-${item.variantId}" data-variant-id="${item.variantId}">Remove</button>
306
- </div>
307
- </div>
308
- `).join("");
309
- this.innerHTML = `
310
- <div class="nymbal-drawer-overlay" data-testid="cart-drawer-overlay"></div>
311
- <div class="nymbal-drawer-panel" role="dialog" aria-label="Shopping cart" aria-modal="true" data-testid="cart-drawer">
312
- <div class="nymbal-drawer-header">
313
- <h2>Cart (${state.itemCount})</h2>
314
- <button type="button" class="nymbal-drawer-close" data-testid="cart-drawer-close" aria-label="Close cart">&times;</button>
315
- </div>
316
- <div class="nymbal-drawer-items">${itemsHtml}</div>
317
- ${state.items.length > 0 ? `
318
- <div class="nymbal-drawer-footer">
319
- <div class="nymbal-cart-subtotal">
320
- <span>Subtotal</span>
321
- <span>${this.formatPrice(state.subtotalMinor, currency)}</span>
322
- </div>
323
- <button type="button" class="nymbal-checkout-btn" data-testid="cart-checkout-btn">Checkout</button>
324
- </div>
325
- ` : ""}
326
- </div>
327
- `;
328
- this.querySelector(".nymbal-drawer-overlay")?.addEventListener("click", () => this.close());
329
- this.querySelector(".nymbal-drawer-close")?.addEventListener("click", () => this.close());
330
- this.querySelector(".nymbal-checkout-btn")?.addEventListener("click", () => {
331
- this.dispatchEvent(new CustomEvent("nymbal:cart:checkout-clicked", { bubbles: true, composed: true }));
332
- });
333
- this.querySelectorAll(".nymbal-cart-item-remove").forEach((btn) => {
334
- btn.addEventListener("click", () => {
335
- const variantId = btn.dataset["variantId"];
336
- if (variantId) this.client.cart.removeItem(variantId);
337
- });
338
- });
339
- }
340
- attributeChangedCallback() {
341
- if (this.isConnected) this.requestRender();
342
- }
343
- esc(str) {
344
- const div = document.createElement("div");
345
- div.textContent = str;
346
- return div.innerHTML;
347
- }
348
- formatPrice(minor, currency) {
349
- return new Intl.NumberFormat(void 0, {
350
- style: "currency",
351
- currency
352
- }).format(minor / 100);
353
- }
354
- };
355
-
356
- // src/components/mini-cart.ts
357
- var STYLES3 = `
358
- nymbal-mini-cart {
359
- display: inline-block;
360
- position: relative;
361
- }
362
- nymbal-mini-cart button {
363
- background: none;
364
- border: none;
365
- cursor: pointer;
366
- position: relative;
367
- padding: var(--nymbal-spacing-sm);
368
- font-family: var(--nymbal-font-family);
369
- color: var(--nymbal-color-text);
370
- }
371
- nymbal-mini-cart .nymbal-mini-cart-icon {
372
- width: 24px;
373
- height: 24px;
374
- }
375
- nymbal-mini-cart .nymbal-mini-cart-badge {
376
- position: absolute;
377
- top: 0;
378
- right: 0;
379
- background: var(--nymbal-color-accent);
380
- color: var(--nymbal-color-text-inverse);
381
- font-size: var(--nymbal-font-size-xs);
382
- font-weight: var(--nymbal-font-weight-bold);
383
- min-width: 18px;
384
- height: 18px;
385
- border-radius: var(--nymbal-radius-full);
386
- display: flex;
387
- align-items: center;
388
- justify-content: center;
389
- line-height: 1;
390
- }
391
- nymbal-mini-cart .nymbal-mini-cart-badge:empty {
392
- display: none;
393
- }
394
- `;
395
- var 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>`;
396
- var NymbalMiniCart = class extends NymbalElement {
397
- setup() {
398
- injectStyles("nymbal-mini-cart", STYLES3);
399
- this.subscribeToStore(this.client.cart);
400
- }
401
- render() {
402
- const { itemCount } = this.client.cart.getState();
403
- this.innerHTML = `
404
- <button type="button" data-testid="mini-cart" aria-label="Cart (${itemCount} items)">
405
- ${CART_SVG}
406
- <span class="nymbal-mini-cart-badge" data-testid="mini-cart-badge">${itemCount > 0 ? itemCount : ""}</span>
407
- </button>
408
- `;
409
- this.querySelector("button")?.addEventListener("click", () => {
410
- this.dispatchEvent(new CustomEvent("nymbal:mini-cart:clicked", { bubbles: true, composed: true }));
411
- });
412
- }
413
- };
414
-
415
- // src/components/variant-selector.ts
416
- var STYLES4 = `
417
- nymbal-variant-selector {
418
- display: block;
419
- font-family: var(--nymbal-font-family);
420
- }
421
- nymbal-variant-selector .nymbal-option-group {
422
- margin-bottom: var(--nymbal-spacing-md);
423
- }
424
- nymbal-variant-selector .nymbal-option-label {
425
- display: block;
426
- font-size: var(--nymbal-font-size-sm);
427
- font-weight: var(--nymbal-font-weight-medium);
428
- margin-bottom: var(--nymbal-spacing-sm);
429
- color: var(--nymbal-color-text);
430
- }
431
- nymbal-variant-selector .nymbal-option-buttons {
432
- display: flex;
433
- flex-wrap: wrap;
434
- gap: var(--nymbal-spacing-sm);
435
- }
436
- nymbal-variant-selector .nymbal-option-btn {
437
- font-family: var(--nymbal-font-family);
438
- font-size: var(--nymbal-font-size-sm);
439
- background: var(--nymbal-color-surface);
440
- color: var(--nymbal-color-text);
441
- border: 1px solid var(--nymbal-color-border);
442
- border-radius: var(--nymbal-radius-md);
443
- padding: var(--nymbal-spacing-xs) var(--nymbal-spacing-md);
444
- cursor: pointer;
445
- transition: border-color var(--nymbal-transition-fast), background var(--nymbal-transition-fast);
446
- }
447
- nymbal-variant-selector .nymbal-option-btn:hover {
448
- border-color: var(--nymbal-color-primary);
449
- }
450
- nymbal-variant-selector .nymbal-option-btn[aria-pressed="true"] {
451
- background: var(--nymbal-color-primary);
452
- color: var(--nymbal-color-text-inverse);
453
- border-color: var(--nymbal-color-primary);
454
- }
455
- nymbal-variant-selector .nymbal-option-btn:disabled {
456
- opacity: 0.4;
457
- cursor: not-allowed;
458
- }
459
- `;
460
- function optionsOf(variant) {
461
- return Array.isArray(variant.options) ? variant.options : [];
462
- }
463
- var NymbalVariantSelector = class extends NymbalElement {
464
- setup() {
465
- injectStyles("nymbal-variant-selector", STYLES4);
466
- this.subscribeToStore(this.client.product);
467
- }
468
- render() {
469
- const state = this.client.product.getState();
470
- const product = state.product;
471
- if (!product) {
472
- this.innerHTML = "";
473
- return;
474
- }
475
- const optionGroups = this.getOptionGroups(product);
476
- const selected = state.selectedVariant;
477
- this.innerHTML = optionGroups.map((group) => `
478
- <div class="nymbal-option-group">
479
- <span class="nymbal-option-label">${this.esc(group.name)}</span>
480
- <div class="nymbal-option-buttons">
481
- ${group.values.map((value) => {
482
- const isSelected = selected !== null && optionsOf(selected).some((o) => o.name === group.name && o.value === value);
483
- return `<button
484
- class="nymbal-option-btn"
485
- data-testid="variant-option-${this.esc(group.name)}-${this.esc(value)}"
486
- data-option-name="${this.esc(group.name)}"
487
- data-option-value="${this.esc(value)}"
488
- aria-pressed="${isSelected}"
489
- >${this.esc(value)}</button>`;
490
- }).join("")}
491
- </div>
492
- </div>
493
- `).join("");
494
- this.querySelectorAll(".nymbal-option-btn").forEach((btn) => {
495
- btn.addEventListener("click", () => {
496
- const el = btn;
497
- const optionName = el.dataset["optionName"];
498
- const optionValue = el.dataset["optionValue"];
499
- if (!optionName || !optionValue || !product) return;
500
- const currentSelections = /* @__PURE__ */ new Map();
501
- if (selected) {
502
- for (const opt of optionsOf(selected)) {
503
- currentSelections.set(opt.name, opt.value);
504
- }
505
- }
506
- currentSelections.set(optionName, optionValue);
507
- const match = product.variants.find(
508
- (v) => optionsOf(v).every((o) => currentSelections.get(o.name) === o.value)
509
- );
510
- if (match) {
511
- this.client.product.selectVariant(match);
512
- this.dispatchEvent(
513
- new CustomEvent("nymbal:variant:selected", {
514
- bubbles: true,
515
- composed: true,
516
- detail: { variant: match }
517
- })
518
- );
519
- }
520
- });
521
- });
522
- }
523
- getOptionGroups(product) {
524
- const groups = /* @__PURE__ */ new Map();
525
- for (const variant of product.variants) {
526
- for (const opt of optionsOf(variant)) {
527
- let values = groups.get(opt.name);
528
- if (!values) {
529
- values = /* @__PURE__ */ new Set();
530
- groups.set(opt.name, values);
531
- }
532
- values.add(opt.value);
533
- }
534
- }
535
- return Array.from(groups.entries()).map(([name, values]) => ({
536
- name,
537
- values: Array.from(values)
538
- }));
539
- }
540
- esc(str) {
541
- const div = document.createElement("div");
542
- div.textContent = str;
543
- return div.innerHTML;
544
- }
545
- };
546
-
547
- // src/components/quantity-selector.ts
548
- var STYLES5 = `
549
- nymbal-quantity-selector {
550
- display: inline-flex;
551
- align-items: center;
552
- font-family: var(--nymbal-font-family);
553
- }
554
- nymbal-quantity-selector button {
555
- font-family: var(--nymbal-font-family);
556
- font-size: var(--nymbal-font-size-base);
557
- background: var(--nymbal-color-surface);
558
- color: var(--nymbal-color-text);
559
- border: 1px solid var(--nymbal-color-border);
560
- width: 36px;
561
- height: 36px;
562
- cursor: pointer;
563
- display: flex;
564
- align-items: center;
565
- justify-content: center;
566
- transition: background var(--nymbal-transition-fast);
567
- }
568
- nymbal-quantity-selector button:hover:not(:disabled) {
569
- background: var(--nymbal-color-secondary, #f4f4f5);
570
- }
571
- nymbal-quantity-selector button:disabled {
572
- opacity: 0.4;
573
- cursor: not-allowed;
574
- }
575
- nymbal-quantity-selector button:first-child {
576
- border-radius: var(--nymbal-radius-md) 0 0 var(--nymbal-radius-md);
577
- }
578
- nymbal-quantity-selector button:last-child {
579
- border-radius: 0 var(--nymbal-radius-md) var(--nymbal-radius-md) 0;
580
- }
581
- nymbal-quantity-selector input {
582
- font-family: var(--nymbal-font-family);
583
- font-size: var(--nymbal-font-size-base);
584
- width: 48px;
585
- height: 36px;
586
- text-align: center;
587
- border: 1px solid var(--nymbal-color-border);
588
- border-left: none;
589
- border-right: none;
590
- -moz-appearance: textfield;
591
- }
592
- nymbal-quantity-selector input::-webkit-outer-spin-button,
593
- nymbal-quantity-selector input::-webkit-inner-spin-button {
594
- -webkit-appearance: none;
595
- margin: 0;
596
- }
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
- }
603
- var NymbalQuantitySelector = class extends NymbalElement {
604
- static get observedAttributes() {
605
- return ["value", "min", "max"];
606
- }
607
- _value = 1;
608
- setup() {
609
- injectStyles("nymbal-quantity-selector", STYLES5);
610
- this._value = wholeQuantity(this.getAttribute("value"), ...this.bounds());
611
- }
612
- bounds() {
613
- return [Number(this.getAttribute("min")) || 1, Number(this.getAttribute("max")) || 99];
614
- }
615
- render() {
616
- const [min, max] = this.bounds();
617
- this.innerHTML = `
618
- <button data-testid="qty-decrement" aria-label="Decrease quantity" ${this._value <= min ? "disabled" : ""}>&#8722;</button>
619
- <input data-testid="qty-input" type="number" value="${this._value}" min="${min}" max="${max}" aria-label="Quantity">
620
- <button data-testid="qty-increment" aria-label="Increase quantity" ${this._value >= max ? "disabled" : ""}>&#43;</button>
621
- `;
622
- this.querySelector('[data-testid="qty-decrement"]')?.addEventListener("click", () => {
623
- if (this._value > min) {
624
- this._value--;
625
- this.emitChange();
626
- this.render();
627
- }
628
- });
629
- this.querySelector('[data-testid="qty-increment"]')?.addEventListener("click", () => {
630
- if (this._value < max) {
631
- this._value++;
632
- this.emitChange();
633
- this.render();
634
- }
635
- });
636
- const input = this.querySelector("input");
637
- input?.addEventListener("change", () => {
638
- this._value = wholeQuantity(input.value, min, max);
639
- this.emitChange();
640
- this.render();
641
- });
642
- }
643
- attributeChangedCallback(name) {
644
- if (name === "value") {
645
- this._value = wholeQuantity(this.getAttribute("value"), ...this.bounds());
646
- }
647
- if (this.isConnected) this.requestRender();
648
- }
649
- emitChange() {
650
- this.dispatchEvent(
651
- new CustomEvent("nymbal:quantity:changed", {
652
- bubbles: true,
653
- composed: true,
654
- detail: { value: this._value }
655
- })
656
- );
657
- }
658
- };
659
-
660
- // src/components/search-bar.ts
661
- var STYLES6 = `
662
- nymbal-search-bar {
663
- display: block;
664
- position: relative;
665
- font-family: var(--nymbal-font-family);
666
- }
667
- nymbal-search-bar input {
668
- font-family: var(--nymbal-font-family);
669
- font-size: var(--nymbal-font-size-base);
670
- width: 100%;
671
- padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-md);
672
- border: 1px solid var(--nymbal-color-border);
673
- border-radius: var(--nymbal-radius-md);
674
- outline: none;
675
- transition: border-color var(--nymbal-transition-fast);
676
- box-sizing: border-box;
677
- }
678
- nymbal-search-bar input:focus {
679
- border-color: var(--nymbal-color-accent);
680
- }
681
- nymbal-search-bar .nymbal-search-results {
682
- position: absolute;
683
- top: 100%;
684
- left: 0;
685
- right: 0;
686
- background: var(--nymbal-color-surface);
687
- border: 1px solid var(--nymbal-color-border);
688
- border-top: none;
689
- border-radius: 0 0 var(--nymbal-radius-md) var(--nymbal-radius-md);
690
- box-shadow: var(--nymbal-shadow-md);
691
- z-index: var(--nymbal-z-dropdown);
692
- max-height: 300px;
693
- overflow-y: auto;
694
- }
695
- nymbal-search-bar .nymbal-search-result-item {
696
- padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-md);
697
- cursor: pointer;
698
- transition: background var(--nymbal-transition-fast);
699
- }
700
- nymbal-search-bar .nymbal-search-result-item:hover,
701
- nymbal-search-bar .nymbal-search-result-item[aria-selected="true"] {
702
- background: var(--nymbal-color-secondary, #f4f4f5);
703
- }
704
- nymbal-search-bar .nymbal-search-result-name {
705
- font-weight: var(--nymbal-font-weight-medium);
706
- }
707
- nymbal-search-bar .nymbal-search-result-price {
708
- font-size: var(--nymbal-font-size-sm);
709
- color: var(--nymbal-color-text-muted);
710
- }
711
- nymbal-search-bar .nymbal-search-loading {
712
- padding: var(--nymbal-spacing-md);
713
- text-align: center;
714
- color: var(--nymbal-color-text-muted);
715
- font-size: var(--nymbal-font-size-sm);
716
- }
717
- `;
718
- var NymbalSearchBar = class extends NymbalElement {
719
- static get observedAttributes() {
720
- return ["placeholder", "debounce-ms"];
721
- }
722
- debounceTimer = null;
723
- selectedIndex = -1;
724
- setup() {
725
- injectStyles("nymbal-search-bar", STYLES6);
726
- this.subscribeToStore(this.client.search);
727
- }
728
- render() {
729
- const state = this.client.search.getState();
730
- const placeholder = this.getAttribute("placeholder") ?? "Search products...";
731
- let resultsHtml = "";
732
- if (state.query) {
733
- if (state.loading) {
734
- resultsHtml = `<div class="nymbal-search-results" data-testid="search-results"><div class="nymbal-search-loading">Searching...</div></div>`;
735
- } else if (state.results.length > 0) {
736
- resultsHtml = `<div class="nymbal-search-results" data-testid="search-results">
737
- ${state.results.map(
738
- (product, i) => `
739
- <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}">
740
- <div class="nymbal-search-result-name">${this.esc(product.name)}</div>
741
- ${product.priceMinor !== null ? `<div class="nymbal-search-result-price">${this.formatPrice(product.priceMinor, product.currency ?? "USD")}</div>` : ""}
742
- </div>
743
- `
744
- ).join("")}
745
- </div>`;
746
- }
747
- }
748
- this.innerHTML = `
749
- <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">
750
- ${resultsHtml}
751
- `;
752
- this.querySelectorAll(".nymbal-search-result-item").forEach((item, i) => {
753
- item.addEventListener("click", () => {
754
- const product = state.results[i];
755
- if (product) this.select(product);
756
- });
757
- });
758
- const input = this.querySelector("input");
759
- input.addEventListener("input", () => {
760
- const query = input.value;
761
- const debounceMs = Number(this.getAttribute("debounce-ms")) || 300;
762
- if (this.debounceTimer !== null) clearTimeout(this.debounceTimer);
763
- this.debounceTimer = setTimeout(() => {
764
- this.selectedIndex = -1;
765
- if (query.trim()) {
766
- this.client.search.search(query);
767
- } else {
768
- this.client.search.clearSearch();
769
- }
770
- }, debounceMs);
771
- });
772
- input.addEventListener("keydown", (e) => {
773
- const results = state.results;
774
- if (e.key === "ArrowDown") {
775
- e.preventDefault();
776
- this.selectedIndex = Math.min(this.selectedIndex + 1, results.length - 1);
777
- this.render();
778
- } else if (e.key === "ArrowUp") {
779
- e.preventDefault();
780
- this.selectedIndex = Math.max(this.selectedIndex - 1, -1);
781
- this.render();
782
- } else if (e.key === "Enter") {
783
- e.preventDefault();
784
- const selected = this.selectedIndex >= 0 ? results[this.selectedIndex] : void 0;
785
- if (selected) {
786
- this.select(selected);
787
- } else {
788
- const query = input.value.trim();
789
- if (query) {
790
- window.location.href = `/search?q=${encodeURIComponent(query)}`;
791
- }
792
- }
793
- } else if (e.key === "Escape") {
794
- this.client.search.clearSearch();
795
- this.selectedIndex = -1;
796
- }
797
- });
798
- }
799
- select(product) {
800
- this.dispatchEvent(
801
- new CustomEvent("nymbal:search:selected", {
802
- bubbles: true,
803
- composed: true,
804
- detail: { product, query: product.name }
805
- })
806
- );
807
- this.client.search.clearSearch();
808
- }
809
- attributeChangedCallback() {
810
- if (this.isConnected) this.requestRender();
811
- }
812
- disconnectedCallback() {
813
- super.disconnectedCallback();
814
- if (this.debounceTimer !== null) {
815
- clearTimeout(this.debounceTimer);
816
- this.debounceTimer = null;
817
- }
818
- }
819
- /** Escapes a value for both text and quoted-attribute contexts. */
820
- esc(str) {
821
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
822
- }
823
- formatPrice(minor, currency) {
824
- return new Intl.NumberFormat(void 0, { style: "currency", currency }).format(minor / 100);
825
- }
826
- };
827
-
828
- // src/components/product-filter.ts
829
- var STYLES7 = `
830
- nymbal-product-filter {
831
- display: block;
832
- font-family: var(--nymbal-font-family);
833
- }
834
- nymbal-product-filter .nymbal-filter-group {
835
- margin-bottom: var(--nymbal-spacing-lg);
836
- }
837
- nymbal-product-filter .nymbal-filter-label {
838
- display: block;
839
- font-size: var(--nymbal-font-size-sm);
840
- font-weight: var(--nymbal-font-weight-bold);
841
- margin-bottom: var(--nymbal-spacing-sm);
842
- color: var(--nymbal-color-text);
843
- text-transform: uppercase;
844
- letter-spacing: 0.05em;
845
- }
846
- nymbal-product-filter .nymbal-filter-option {
847
- display: flex;
848
- align-items: center;
849
- gap: var(--nymbal-spacing-sm);
850
- padding: var(--nymbal-spacing-xs) 0;
851
- cursor: pointer;
852
- font-size: var(--nymbal-font-size-sm);
853
- color: var(--nymbal-color-text);
854
- }
855
- nymbal-product-filter .nymbal-filter-option input[type="checkbox"] {
856
- accent-color: var(--nymbal-color-primary);
857
- }
858
- nymbal-product-filter .nymbal-filter-count {
859
- color: var(--nymbal-color-text-muted);
860
- font-size: var(--nymbal-font-size-xs);
861
- }
862
- `;
863
- var NymbalProductFilter = class extends NymbalElement {
864
- static get observedAttributes() {
865
- return ["facets", "source"];
866
- }
867
- setup() {
868
- injectStyles("nymbal-product-filter", STYLES7);
869
- this.subscribeToStore(this.client.productList);
870
- this.subscribeToStore(this.client.search);
871
- }
872
- render() {
873
- if (this.isSearch()) {
874
- this.renderSearch();
875
- return;
876
- }
877
- const state = this.client.productList.getState();
878
- const facets = this.getFacets();
879
- if (facets.length === 0) {
880
- this.innerHTML = "";
881
- return;
882
- }
883
- this.innerHTML = facets.map((facet) => {
884
- const activeFilter = state.filters[facet.field];
885
- const activeValues = Array.isArray(activeFilter) ? activeFilter : activeFilter ? [activeFilter] : [];
886
- return `
887
- <div class="nymbal-filter-group" data-testid="filter-group-${facet.field}">
888
- <span class="nymbal-filter-label">${this.esc(facet.label)}</span>
889
- ${facet.values.map((v) => {
890
- const checked = activeValues.includes(v.value);
891
- return `
892
- <label class="nymbal-filter-option" data-testid="filter-option-${v.value}">
893
- <input type="checkbox" data-filter-name="${facet.field}" data-filter-value="${v.value}" ${checked ? "checked" : ""}>
894
- <span>${this.esc(v.label ?? v.value)}</span>
895
- ${v.count !== void 0 ? `<span class="nymbal-filter-count">(${v.count})</span>` : ""}
896
- </label>
897
- `;
898
- }).join("")}
899
- </div>
900
- `;
901
- }).join("");
902
- this.querySelectorAll('input[type="checkbox"]').forEach((input) => {
903
- input.addEventListener("change", () => {
904
- const el = input;
905
- const name = el.dataset["filterName"];
906
- const value = el.dataset["filterValue"];
907
- if (el.checked) {
908
- const current = state.filters[name];
909
- const values = Array.isArray(current) ? [...current, value] : current ? [current, value] : value;
910
- this.client.productList.applyFilter(name, values);
911
- } else {
912
- const current = state.filters[name];
913
- if (Array.isArray(current)) {
914
- const remaining = current.filter((v) => v !== value);
915
- if (remaining.length > 0) {
916
- this.client.productList.applyFilter(name, remaining);
917
- } else {
918
- this.client.productList.removeFilter(name);
919
- }
920
- } else {
921
- this.client.productList.removeFilter(name);
922
- }
923
- }
924
- this.dispatchEvent(
925
- new CustomEvent("nymbal:filter:changed", { bubbles: true, composed: true, detail: { name, value } })
926
- );
927
- });
928
- });
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
- }
966
- attributeChangedCallback() {
967
- if (this.isConnected) this.requestRender();
968
- }
969
- getFacets() {
970
- const attr = this.getAttribute("facets");
971
- if (!attr) return [];
972
- try {
973
- return JSON.parse(attr);
974
- } catch (err) {
975
- console.error("[nymbal-product-filter] failed to parse facets attribute", err);
976
- return [];
977
- }
978
- }
979
- esc(str) {
980
- const div = document.createElement("div");
981
- div.textContent = str;
982
- return div.innerHTML;
983
- }
984
- };
985
-
986
- // src/components/toast.ts
987
- var STYLES8 = `
988
- nymbal-toast {
989
- position: fixed;
990
- z-index: var(--nymbal-z-toast);
991
- display: flex;
992
- flex-direction: column;
993
- gap: var(--nymbal-spacing-sm);
994
- pointer-events: none;
995
- font-family: var(--nymbal-font-family);
996
- }
997
- nymbal-toast[position="top-right"],
998
- nymbal-toast:not([position]) {
999
- top: var(--nymbal-spacing-lg);
1000
- right: var(--nymbal-spacing-lg);
1001
- }
1002
- nymbal-toast[position="top-left"] {
1003
- top: var(--nymbal-spacing-lg);
1004
- left: var(--nymbal-spacing-lg);
1005
- }
1006
- nymbal-toast[position="bottom-right"] {
1007
- bottom: var(--nymbal-spacing-lg);
1008
- right: var(--nymbal-spacing-lg);
1009
- }
1010
- nymbal-toast[position="bottom-left"] {
1011
- bottom: var(--nymbal-spacing-lg);
1012
- left: var(--nymbal-spacing-lg);
1013
- }
1014
- nymbal-toast .nymbal-toast-item {
1015
- pointer-events: auto;
1016
- display: flex;
1017
- align-items: center;
1018
- gap: var(--nymbal-spacing-sm);
1019
- padding: var(--nymbal-spacing-sm) var(--nymbal-spacing-md);
1020
- border-radius: var(--nymbal-radius-md);
1021
- box-shadow: var(--nymbal-shadow-lg);
1022
- font-size: var(--nymbal-font-size-sm);
1023
- background: var(--nymbal-color-surface);
1024
- color: var(--nymbal-color-text);
1025
- border: 1px solid var(--nymbal-color-border);
1026
- animation: nymbal-toast-in var(--nymbal-transition-normal) forwards;
1027
- max-width: 360px;
1028
- }
1029
- nymbal-toast .nymbal-toast-item[data-type="success"] {
1030
- border-left: 3px solid var(--nymbal-color-success);
1031
- }
1032
- nymbal-toast .nymbal-toast-item[data-type="error"] {
1033
- border-left: 3px solid var(--nymbal-color-error);
1034
- }
1035
- nymbal-toast .nymbal-toast-item[data-type="warning"] {
1036
- border-left: 3px solid var(--nymbal-color-warning);
1037
- }
1038
- nymbal-toast .nymbal-toast-item[data-type="info"] {
1039
- border-left: 3px solid var(--nymbal-color-accent);
1040
- }
1041
- nymbal-toast .nymbal-toast-close {
1042
- background: none;
1043
- border: none;
1044
- cursor: pointer;
1045
- color: var(--nymbal-color-text-muted);
1046
- font-size: var(--nymbal-font-size-base);
1047
- padding: 0;
1048
- margin-left: auto;
1049
- }
1050
- @keyframes nymbal-toast-in {
1051
- from { opacity: 0; transform: translateY(-8px); }
1052
- to { opacity: 1; transform: translateY(0); }
1053
- }
1054
- `;
1055
- var HTMLElementBase2 = typeof HTMLElement !== "undefined" ? HTMLElement : class {
1056
- };
1057
- var NymbalToast = class extends HTMLElementBase2 {
1058
- static get observedAttributes() {
1059
- return ["position", "duration-ms"];
1060
- }
1061
- connectedCallback() {
1062
- injectStyles("nymbal-toast", STYLES8);
1063
- this.setAttribute("data-testid", "toast");
1064
- }
1065
- show(message, type = "info") {
1066
- const durationMs = Number(this.getAttribute("duration-ms")) || 4e3;
1067
- const item = document.createElement("div");
1068
- item.className = "nymbal-toast-item";
1069
- item.setAttribute("data-type", type);
1070
- item.setAttribute("role", "alert");
1071
- const text = document.createElement("span");
1072
- text.textContent = message;
1073
- const close = document.createElement("button");
1074
- close.className = "nymbal-toast-close";
1075
- close.setAttribute("aria-label", "Dismiss");
1076
- close.textContent = "\xD7";
1077
- close.addEventListener("click", () => this.dismiss(item));
1078
- item.appendChild(text);
1079
- item.appendChild(close);
1080
- this.appendChild(item);
1081
- setTimeout(() => this.dismiss(item), durationMs);
1082
- }
1083
- dismiss(item) {
1084
- if (item.parentNode === this) {
1085
- this.removeChild(item);
1086
- }
1087
- }
1088
- static showGlobal(message, type = "info") {
1089
- let toastEl = document.querySelector("nymbal-toast");
1090
- if (!toastEl) {
1091
- toastEl = document.createElement("nymbal-toast");
1092
- document.body.appendChild(toastEl);
1093
- }
1094
- toastEl.show(message, type);
1095
- }
1096
- };
1097
-
1098
- // src/components/payment-element.ts
1
+ import '@lit-labs/ssr-client/lit-element-hydrate-support.js'
1099
2
  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
-
1381
- // src/index.ts
1382
- function registerComponents() {
1383
- const components = [
1384
- ["nymbal-add-to-cart", NymbalAddToCart],
1385
- ["nymbal-cart-drawer", NymbalCartDrawer],
1386
- ["nymbal-mini-cart", NymbalMiniCart],
1387
- ["nymbal-variant-selector", NymbalVariantSelector],
1388
- ["nymbal-quantity-selector", NymbalQuantitySelector],
1389
- ["nymbal-search-bar", NymbalSearchBar],
1390
- ["nymbal-product-filter", NymbalProductFilter],
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]
1398
- ];
1399
- for (const [name, driver] of drivers) {
1400
- if (getPaymentDriver2(name) === void 0) registerPaymentDriver(name, driver);
1401
- }
1402
- for (const [name, ctor] of components) {
1403
- if (!customElements.get(name)) {
1404
- customElements.define(name, ctor);
1405
- }
1406
- }
1407
- }
3
+ registerComponents
4
+ } from "./chunk-XPFGMYBV.js";
5
+ import {
6
+ NymbalVariantSelector
7
+ } from "./chunk-H3ZBYNHV.js";
8
+ import {
9
+ NymbalReviewList
10
+ } from "./chunk-7JAAOVCE.js";
11
+ import {
12
+ NymbalSearchBar
13
+ } from "./chunk-4COVK727.js";
14
+ import {
15
+ NymbalSearchResults
16
+ } from "./chunk-RAOGZH3Z.js";
17
+ import {
18
+ NymbalSiteFooter
19
+ } from "./chunk-FIF6GQ6U.js";
20
+ import {
21
+ NymbalSiteHeader
22
+ } from "./chunk-W23P7SI4.js";
23
+ import {
24
+ NymbalSortDropdown
25
+ } from "./chunk-EBMA7JGV.js";
26
+ import {
27
+ NymbalStockBadge
28
+ } from "./chunk-O2YKCDR2.js";
29
+ import {
30
+ NymbalToast
31
+ } from "./chunk-2TPQNQYO.js";
32
+ import {
33
+ NymbalProductFilter
34
+ } from "./chunk-WEEHXWM3.js";
35
+ import {
36
+ NymbalProductGrid
37
+ } from "./chunk-5VWZ2U5K.js";
38
+ import {
39
+ NymbalProductCard
40
+ } from "./chunk-RZVFA3N5.js";
41
+ import {
42
+ NymbalProductPrice
43
+ } from "./chunk-DXWMNB5F.js";
44
+ import "./chunk-UYSZOEOA.js";
45
+ import {
46
+ NymbalProfileForm
47
+ } from "./chunk-WTEVTCIL.js";
48
+ import {
49
+ NymbalRegisterForm
50
+ } from "./chunk-NW47OJVN.js";
51
+ import {
52
+ NymbalReviewForm
53
+ } from "./chunk-NE6UHTOT.js";
54
+ import {
55
+ NymbalCartPage
56
+ } from "./chunk-VTQVGNVC.js";
57
+ import {
58
+ NymbalQuantitySelector
59
+ } from "./chunk-HPBC3OG7.js";
60
+ import {
61
+ NymbalCategoryNav
62
+ } from "./chunk-IYT7VXFS.js";
63
+ import {
64
+ NymbalCheckout
65
+ } from "./chunk-5G3C53A2.js";
66
+ import {
67
+ NymbalImageGallery
68
+ } from "./chunk-U2PU4CWB.js";
69
+ import {
70
+ NymbalLoginForm
71
+ } from "./chunk-42SSME5H.js";
72
+ import {
73
+ NymbalMiniCart
74
+ } from "./chunk-2XXBQPDB.js";
75
+ import {
76
+ NymbalOrderSummary
77
+ } from "./chunk-SM7TASEW.js";
78
+ import "./chunk-RVSKX2RX.js";
79
+ import {
80
+ NymbalPaymentElement
81
+ } from "./chunk-W7D3STJH.js";
82
+ import {
83
+ NymbalAddToCart
84
+ } from "./chunk-LHYXNQWY.js";
85
+ import {
86
+ NymbalAddressBook
87
+ } from "./chunk-B73QVSA5.js";
88
+ import {
89
+ NymbalAddressForm
90
+ } from "./chunk-V55K2CS5.js";
91
+ import "./chunk-PN6QKYE4.js";
92
+ import "./chunk-GHXTJ2CF.js";
93
+ import {
94
+ NymbalBreadcrumb
95
+ } from "./chunk-N4Y5DI4X.js";
96
+ import "./chunk-LO2EW5YP.js";
97
+ import {
98
+ NymbalCartDrawer
99
+ } from "./chunk-LSQPUZK6.js";
100
+ import "./chunk-DR5QB5VN.js";
101
+ import {
102
+ NymbalElement,
103
+ define,
104
+ getClient,
105
+ registerClient
106
+ } from "./chunk-EYDMJNNK.js";
107
+ import "./chunk-UCEVCLQ7.js";
1408
108
  export {
1409
109
  NymbalAddToCart,
110
+ NymbalAddressBook,
111
+ NymbalAddressForm,
112
+ NymbalBreadcrumb,
1410
113
  NymbalCartDrawer,
114
+ NymbalCartPage,
115
+ NymbalCategoryNav,
116
+ NymbalCheckout,
1411
117
  NymbalElement,
118
+ NymbalImageGallery,
119
+ NymbalLoginForm,
1412
120
  NymbalMiniCart,
121
+ NymbalOrderSummary,
1413
122
  NymbalPaymentElement,
123
+ NymbalProductCard,
1414
124
  NymbalProductFilter,
125
+ NymbalProductGrid,
126
+ NymbalProductPrice,
127
+ NymbalProfileForm,
1415
128
  NymbalQuantitySelector,
129
+ NymbalRegisterForm,
130
+ NymbalReviewForm,
131
+ NymbalReviewList,
1416
132
  NymbalSearchBar,
133
+ NymbalSearchResults,
134
+ NymbalSiteFooter,
135
+ NymbalSiteHeader,
1417
136
  NymbalSortDropdown,
137
+ NymbalStockBadge,
1418
138
  NymbalToast,
1419
139
  NymbalVariantSelector,
140
+ define,
1420
141
  getClient,
1421
- injectStyles,
1422
142
  registerClient,
1423
143
  registerComponents
1424
144
  };