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