@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
@@ -0,0 +1,204 @@
1
+ import '@lit-labs/ssr-client/lit-element-hydrate-support.js'
2
+ import {
3
+ NymbalElement,
4
+ baseStyles,
5
+ propertiesOf
6
+ } from "./chunk-EYDMJNNK.js";
7
+
8
+ // src/components/search-bar.ts
9
+ import { css, html, nothing } from "lit";
10
+ var SCHEMA = {
11
+ tag: "nymbal-search-bar",
12
+ reactName: "SearchBar",
13
+ attributes: [
14
+ { name: "placeholder", prop: "placeholder", type: "string" },
15
+ { name: "debounce-ms", prop: "debounceMs", type: "number" }
16
+ ],
17
+ events: [
18
+ { name: "nymbal:search:selected", prop: "onSearchSelect", detail: "{ product: unknown }" }
19
+ ],
20
+ parts: ["input", "results", "loading", "result", "result-name", "result-price"],
21
+ slots: ["loading"],
22
+ children: false
23
+ };
24
+ var NO_SEARCH = { query: "", loading: false, results: [] };
25
+ function formatPrice(minor, currency) {
26
+ return new Intl.NumberFormat(void 0, { style: "currency", currency }).format(minor / 100);
27
+ }
28
+ var NymbalSearchBar = class extends NymbalElement {
29
+ static schema = SCHEMA;
30
+ static properties = propertiesOf(SCHEMA);
31
+ static styles = [
32
+ baseStyles,
33
+ css`
34
+ :host {
35
+ display: block;
36
+ position: relative;
37
+ font-family: var(--nymbal-font-family, system-ui, -apple-system, sans-serif);
38
+ }
39
+ [part~='input'] {
40
+ font-family: var(--nymbal-font-family, system-ui, -apple-system, sans-serif);
41
+ font-size: var(--nymbal-font-size-base, 1rem);
42
+ width: 100%;
43
+ padding: var(--nymbal-spacing-sm, 0.5rem) var(--nymbal-spacing-md, 1rem);
44
+ border: 1px solid var(--nymbal-color-border, #e4e4e7);
45
+ border-radius: var(--nymbal-radius-md, 0.375rem);
46
+ outline: none;
47
+ transition: border-color var(--nymbal-transition-fast, 150ms ease);
48
+ }
49
+ [part~='input']:focus {
50
+ border-color: var(--nymbal-color-accent, #2563eb);
51
+ }
52
+ [part~='results'] {
53
+ position: absolute;
54
+ top: 100%;
55
+ left: 0;
56
+ right: 0;
57
+ background: var(--nymbal-color-surface, #ffffff);
58
+ border: 1px solid var(--nymbal-color-border, #e4e4e7);
59
+ border-top: none;
60
+ border-radius: 0 0 var(--nymbal-radius-md, 0.375rem) var(--nymbal-radius-md, 0.375rem);
61
+ box-shadow: var(--nymbal-shadow-md, 0 4px 6px rgba(0, 0, 0, 0.1));
62
+ z-index: var(--nymbal-z-dropdown, 100);
63
+ max-height: 300px;
64
+ overflow-y: auto;
65
+ }
66
+ [part~='result'] {
67
+ padding: var(--nymbal-spacing-sm, 0.5rem) var(--nymbal-spacing-md, 1rem);
68
+ cursor: pointer;
69
+ transition: background var(--nymbal-transition-fast, 150ms ease);
70
+ }
71
+ [part~='result']:hover,
72
+ [part~='result'][aria-selected='true'] {
73
+ background: var(--nymbal-color-secondary, #f4f4f5);
74
+ }
75
+ [part~='result-name'] {
76
+ font-weight: var(--nymbal-font-weight-medium, 500);
77
+ }
78
+ [part~='result-price'] {
79
+ font-size: var(--nymbal-font-size-sm, 0.875rem);
80
+ color: var(--nymbal-color-text-muted, #71717a);
81
+ }
82
+ [part~='loading'] {
83
+ padding: var(--nymbal-spacing-md, 1rem);
84
+ text-align: center;
85
+ color: var(--nymbal-color-text-muted, #71717a);
86
+ font-size: var(--nymbal-font-size-sm, 0.875rem);
87
+ }
88
+ `
89
+ ];
90
+ debounceTimer = null;
91
+ selectedIndex = -1;
92
+ stores() {
93
+ return [this.client.search];
94
+ }
95
+ render() {
96
+ const state = this.readsStores ? this.client.search.getState() : NO_SEARCH;
97
+ const placeholder = this.getAttribute("placeholder") ?? "Search products...";
98
+ let results = nothing;
99
+ if (state.query && document.querySelector("nymbal-search-results") === null) {
100
+ if (state.loading) {
101
+ results = html`<div part="results" data-testid="search-results">
102
+ <div part="loading"><slot name="loading">Searching...</slot></div>
103
+ </div>`;
104
+ } else if (state.results.length > 0) {
105
+ results = html`<div part="results" data-testid="search-results">
106
+ ${state.results.map(
107
+ (product, i) => html`
108
+ <div
109
+ part="result"
110
+ class="nymbal-search-result-item"
111
+ data-testid="search-result-${product.slug}"
112
+ data-slug=${product.slug}
113
+ aria-selected=${String(i === this.selectedIndex)}
114
+ @click=${() => this.select(product)}
115
+ >
116
+ <div part="result-name">${product.name}</div>
117
+ ${product.priceMinor !== null ? html`<div part="result-price">
118
+ ${formatPrice(product.priceMinor, product.currency ?? "USD")}
119
+ </div>` : nothing}
120
+ </div>
121
+ `
122
+ )}
123
+ </div>`;
124
+ }
125
+ }
126
+ return html`
127
+ <input
128
+ part="input"
129
+ data-testid="search-input"
130
+ type="search"
131
+ placeholder=${placeholder}
132
+ .value=${state.query}
133
+ autocomplete="off"
134
+ role="combobox"
135
+ aria-expanded=${String(results !== nothing && state.results.length > 0)}
136
+ aria-haspopup="listbox"
137
+ @input=${this.handleInput}
138
+ @keydown=${this.handleKeydown}
139
+ />
140
+ ${results}
141
+ `;
142
+ }
143
+ handleInput = (e) => {
144
+ const query = e.target.value;
145
+ const debounceMs = Number(this.getAttribute("debounce-ms")) || 300;
146
+ if (this.debounceTimer !== null) clearTimeout(this.debounceTimer);
147
+ this.debounceTimer = setTimeout(() => {
148
+ this.selectedIndex = -1;
149
+ if (query.trim()) {
150
+ void this.client.search.search(query);
151
+ } else {
152
+ this.client.search.clearSearch();
153
+ }
154
+ }, debounceMs);
155
+ };
156
+ handleKeydown = (e) => {
157
+ const { results } = this.client.search.getState();
158
+ if (e.key === "ArrowDown") {
159
+ e.preventDefault();
160
+ this.selectedIndex = Math.min(this.selectedIndex + 1, results.length - 1);
161
+ this.requestUpdate();
162
+ } else if (e.key === "ArrowUp") {
163
+ e.preventDefault();
164
+ this.selectedIndex = Math.max(this.selectedIndex - 1, -1);
165
+ this.requestUpdate();
166
+ } else if (e.key === "Enter") {
167
+ e.preventDefault();
168
+ const selected = this.selectedIndex >= 0 ? results[this.selectedIndex] : void 0;
169
+ if (selected) {
170
+ this.select(selected);
171
+ } else {
172
+ const query = e.target.value.trim();
173
+ if (query) {
174
+ window.location.href = `/search?q=${encodeURIComponent(query)}`;
175
+ }
176
+ }
177
+ } else if (e.key === "Escape") {
178
+ this.client.search.clearSearch();
179
+ this.selectedIndex = -1;
180
+ }
181
+ };
182
+ select(product) {
183
+ this.dispatchEvent(
184
+ new CustomEvent("nymbal:search:selected", {
185
+ bubbles: true,
186
+ composed: true,
187
+ detail: { product, query: product.name }
188
+ })
189
+ );
190
+ this.client.search.clearSearch();
191
+ }
192
+ disconnectedCallback() {
193
+ super.disconnectedCallback();
194
+ if (this.debounceTimer !== null) {
195
+ clearTimeout(this.debounceTimer);
196
+ this.debounceTimer = null;
197
+ }
198
+ }
199
+ };
200
+
201
+ export {
202
+ NymbalSearchBar
203
+ };
204
+ //# sourceMappingURL=chunk-4COVK727.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/search-bar.ts"],"sourcesContent":["import { css, html, nothing } from 'lit'\nimport type { DenormalisedProduct } from '@nymbalhq/sdk'\nimport { baseStyles, NymbalElement, propertiesOf, type ElementSchema } from '../base-element.js'\n\nconst SCHEMA: ElementSchema = {\n tag: 'nymbal-search-bar',\n reactName: 'SearchBar',\n attributes: [\n { name: 'placeholder', prop: 'placeholder', type: 'string' },\n { name: 'debounce-ms', prop: 'debounceMs', type: 'number' },\n ],\n events: [\n { name: 'nymbal:search:selected', prop: 'onSearchSelect', detail: '{ product: unknown }' },\n ],\n parts: ['input', 'results', 'loading', 'result', 'result-name', 'result-price'],\n slots: ['loading'],\n children: false,\n}\n\n/** What a search bar shows before any search, and on a server. */\nconst NO_SEARCH = { query: '', loading: false, results: [] as DenormalisedProduct[] }\n\nfunction formatPrice(minor: number, currency: string): string {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(minor / 100)\n}\n\nexport class NymbalSearchBar extends NymbalElement {\n static override schema = SCHEMA\n static override properties = propertiesOf(SCHEMA)\n\n static override styles = [\n baseStyles,\n css`\n :host {\n display: block;\n position: relative;\n font-family: var(--nymbal-font-family, system-ui, -apple-system, sans-serif);\n }\n [part~='input'] {\n font-family: var(--nymbal-font-family, system-ui, -apple-system, sans-serif);\n font-size: var(--nymbal-font-size-base, 1rem);\n width: 100%;\n padding: var(--nymbal-spacing-sm, 0.5rem) var(--nymbal-spacing-md, 1rem);\n border: 1px solid var(--nymbal-color-border, #e4e4e7);\n border-radius: var(--nymbal-radius-md, 0.375rem);\n outline: none;\n transition: border-color var(--nymbal-transition-fast, 150ms ease);\n }\n [part~='input']:focus {\n border-color: var(--nymbal-color-accent, #2563eb);\n }\n [part~='results'] {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n background: var(--nymbal-color-surface, #ffffff);\n border: 1px solid var(--nymbal-color-border, #e4e4e7);\n border-top: none;\n border-radius: 0 0 var(--nymbal-radius-md, 0.375rem) var(--nymbal-radius-md, 0.375rem);\n box-shadow: var(--nymbal-shadow-md, 0 4px 6px rgba(0, 0, 0, 0.1));\n z-index: var(--nymbal-z-dropdown, 100);\n max-height: 300px;\n overflow-y: auto;\n }\n [part~='result'] {\n padding: var(--nymbal-spacing-sm, 0.5rem) var(--nymbal-spacing-md, 1rem);\n cursor: pointer;\n transition: background var(--nymbal-transition-fast, 150ms ease);\n }\n [part~='result']:hover,\n [part~='result'][aria-selected='true'] {\n background: var(--nymbal-color-secondary, #f4f4f5);\n }\n [part~='result-name'] {\n font-weight: var(--nymbal-font-weight-medium, 500);\n }\n [part~='result-price'] {\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n color: var(--nymbal-color-text-muted, #71717a);\n }\n [part~='loading'] {\n padding: var(--nymbal-spacing-md, 1rem);\n text-align: center;\n color: var(--nymbal-color-text-muted, #71717a);\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n }\n `,\n ]\n\n private debounceTimer: ReturnType<typeof setTimeout> | null = null\n private selectedIndex = -1\n\n protected override stores() {\n return [this.client.search]\n }\n\n protected override render() {\n const state = this.readsStores ? this.client.search.getState() : NO_SEARCH\n const placeholder = this.getAttribute('placeholder') ?? 'Search products...'\n\n let results: unknown = nothing\n // A page that shows the search itself (`nymbal-search-results`) is not covered by the same results as suggestions.\n if (state.query && document.querySelector('nymbal-search-results') === null) {\n if (state.loading) {\n results = html`<div part=\"results\" data-testid=\"search-results\">\n <div part=\"loading\"><slot name=\"loading\">Searching...</slot></div>\n </div>`\n } else if (state.results.length > 0) {\n results = html`<div part=\"results\" data-testid=\"search-results\">\n ${state.results.map(\n (product, i) => html`\n <div\n part=\"result\"\n class=\"nymbal-search-result-item\"\n data-testid=\"search-result-${product.slug}\"\n data-slug=${product.slug}\n aria-selected=${String(i === this.selectedIndex)}\n @click=${() => this.select(product)}\n >\n <div part=\"result-name\">${product.name}</div>\n ${product.priceMinor !== null\n ? html`<div part=\"result-price\">\n ${formatPrice(product.priceMinor, product.currency ?? 'USD')}\n </div>`\n : nothing}\n </div>\n `,\n )}\n </div>`\n }\n }\n\n return html`\n <input\n part=\"input\"\n data-testid=\"search-input\"\n type=\"search\"\n placeholder=${placeholder}\n .value=${state.query}\n autocomplete=\"off\"\n role=\"combobox\"\n aria-expanded=${String(results !== nothing && state.results.length > 0)}\n aria-haspopup=\"listbox\"\n @input=${this.handleInput}\n @keydown=${this.handleKeydown}\n />\n ${results}\n `\n }\n\n private handleInput = (e: Event): void => {\n const query = (e.target as HTMLInputElement).value\n const debounceMs = Number(this.getAttribute('debounce-ms')) || 300\n if (this.debounceTimer !== null) clearTimeout(this.debounceTimer)\n this.debounceTimer = setTimeout(() => {\n this.selectedIndex = -1\n if (query.trim()) {\n void this.client.search.search(query)\n } else {\n this.client.search.clearSearch()\n }\n }, debounceMs)\n }\n\n private handleKeydown = (e: KeyboardEvent): void => {\n const { results } = this.client.search.getState()\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n this.selectedIndex = Math.min(this.selectedIndex + 1, results.length - 1)\n this.requestUpdate()\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n this.selectedIndex = Math.max(this.selectedIndex - 1, -1)\n this.requestUpdate()\n } else if (e.key === 'Enter') {\n e.preventDefault()\n const selected = this.selectedIndex >= 0 ? results[this.selectedIndex] : undefined\n if (selected) {\n this.select(selected)\n } else {\n const query = (e.target as HTMLInputElement).value.trim()\n if (query) {\n window.location.href = `/search?q=${encodeURIComponent(query)}`\n }\n }\n } else if (e.key === 'Escape') {\n this.client.search.clearSearch()\n this.selectedIndex = -1\n }\n }\n\n private select(product: DenormalisedProduct): void {\n this.dispatchEvent(\n new CustomEvent('nymbal:search:selected', {\n bubbles: true,\n composed: true,\n detail: { product, query: product.name },\n }),\n )\n this.client.search.clearSearch()\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback()\n if (this.debounceTimer !== null) {\n clearTimeout(this.debounceTimer)\n this.debounceTimer = null\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,149 @@
1
+ import '@lit-labs/ssr-client/lit-element-hydrate-support.js'
2
+ import {
3
+ NymbalProductCard,
4
+ cardProductOf
5
+ } from "./chunk-RZVFA3N5.js";
6
+ import {
7
+ buttonStyles
8
+ } from "./chunk-GHXTJ2CF.js";
9
+ import {
10
+ NymbalElement,
11
+ baseStyles,
12
+ parsedJson,
13
+ propertiesOf
14
+ } from "./chunk-EYDMJNNK.js";
15
+
16
+ // src/components/product-grid.ts
17
+ import { css, html, nothing } from "lit";
18
+ var SCHEMA = {
19
+ tag: "nymbal-product-grid",
20
+ reactName: "ProductGrid",
21
+ // `products`: the cards a server read. `source="product-list"`: the grid is a listing's, and follows the product-list
22
+ // store once it settles (a sort, a filter, load more); without it the grid is static. `has-more`: the server's page
23
+ // has a next one, so its render shows load more exactly when the store will. `lazy`: the grid sits below the fold
24
+ // (a product page's related grid), so no card is eager and its images never outrank the page's LCP image.
25
+ attributes: [
26
+ { name: "products", prop: "products", type: "json" },
27
+ { name: "source", prop: "source", type: "string" },
28
+ { name: "has-more", prop: "hasMore", type: "boolean" },
29
+ { name: "lazy", prop: "lazy", type: "boolean" }
30
+ ],
31
+ events: [],
32
+ parts: ["grid", "empty", "count", "load-more"],
33
+ slots: ["empty"],
34
+ children: false
35
+ };
36
+ var EAGER_CARDS = 4;
37
+ var NymbalProductGrid = class extends NymbalElement {
38
+ static schema = SCHEMA;
39
+ static uses = [NymbalProductCard];
40
+ static properties = propertiesOf(SCHEMA);
41
+ static styles = [
42
+ baseStyles,
43
+ buttonStyles,
44
+ css`
45
+ :host {
46
+ display: block;
47
+ font-family: var(--nymbal-font-family, system-ui, -apple-system, sans-serif);
48
+ color: var(--nymbal-color-text, #18181b);
49
+ }
50
+ [part~='count'] {
51
+ margin: 0 0 var(--nymbal-spacing-md, 1rem);
52
+ font-size: var(--nymbal-font-size-sm, 0.875rem);
53
+ color: var(--nymbal-color-text-muted, #71717a);
54
+ }
55
+ [part~='grid'] {
56
+ display: grid;
57
+ gap: var(--nymbal-spacing-md, 1rem);
58
+ grid-template-columns: 1fr;
59
+ }
60
+ @media (min-width: 641px) {
61
+ [part~='grid'] {
62
+ grid-template-columns: repeat(2, 1fr);
63
+ gap: var(--nymbal-spacing-lg, 1.5rem);
64
+ }
65
+ }
66
+ @media (min-width: 1025px) {
67
+ [part~='grid'] {
68
+ grid-template-columns: repeat(4, 1fr);
69
+ }
70
+ }
71
+ [part~='empty'] {
72
+ padding: var(--nymbal-spacing-xl, 2rem) 0;
73
+ text-align: center;
74
+ color: var(--nymbal-color-text-muted, #71717a);
75
+ }
76
+ .more {
77
+ display: flex;
78
+ justify-content: center;
79
+ margin-top: var(--nymbal-spacing-xl, 2rem);
80
+ }
81
+ `
82
+ ];
83
+ get listing() {
84
+ return this.source === "product-list";
85
+ }
86
+ stores() {
87
+ return this.listing ? [this.client.productList] : [];
88
+ }
89
+ storeSettled() {
90
+ if (this.client.productList.getState().loading) return;
91
+ this.products = void 0;
92
+ this.hasMore = void 0;
93
+ }
94
+ /** The server's cards while they stand, else (a listing with a client) the product-list store's. */
95
+ shown() {
96
+ const given = parsedJson(this, "products", this.products);
97
+ if (given === void 0 && this.listing && this.readsStores) {
98
+ const state = this.client.productList.getState();
99
+ return {
100
+ products: state.products,
101
+ hasMore: state.pagination.hasMore,
102
+ loading: state.loading
103
+ };
104
+ }
105
+ return {
106
+ products: Array.isArray(given) ? given : [],
107
+ hasMore: this.hasMore === true,
108
+ loading: false
109
+ };
110
+ }
111
+ render() {
112
+ const { products, hasMore, loading } = this.shown();
113
+ const count = products.length;
114
+ return html`
115
+ ${this.listing ? html`<p part="count">${count} product${count === 1 ? "" : "s"}</p>` : nothing}
116
+ ${count === 0 ? loading ? nothing : html`<div part="empty"><slot name="empty">No products found.</slot></div>` : html`
117
+ <div part="grid">
118
+ ${products.map(
119
+ (p, i) => html`<nymbal-product-card
120
+ product=${JSON.stringify(
121
+ cardProductOf(p)
122
+ )}
123
+ ?eager=${this.lazy !== true && i < EAGER_CARDS}
124
+ ></nymbal-product-card>`
125
+ )}
126
+ </div>
127
+ `}
128
+ ${this.listing && hasMore ? html`
129
+ <div class="more">
130
+ <button
131
+ type="button"
132
+ part="load-more"
133
+ class="btn btn-secondary"
134
+ data-testid="load-more"
135
+ ?disabled=${loading}
136
+ @click=${() => void this.client.productList.loadMore()}
137
+ >
138
+ Load more
139
+ </button>
140
+ </div>
141
+ ` : nothing}
142
+ `;
143
+ }
144
+ };
145
+
146
+ export {
147
+ NymbalProductGrid
148
+ };
149
+ //# sourceMappingURL=chunk-5VWZ2U5K.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/product-grid.ts"],"sourcesContent":["import { css, html, nothing } from 'lit'\nimport {\n baseStyles,\n NymbalElement,\n parsedJson,\n propertiesOf,\n type ElementSchema,\n} from '../base-element.js'\nimport { buttonStyles } from '../view-styles.js'\nimport { cardProductOf, NymbalProductCard } from './product-card.js'\n\nconst SCHEMA: ElementSchema = {\n tag: 'nymbal-product-grid',\n reactName: 'ProductGrid',\n // `products`: the cards a server read. `source=\"product-list\"`: the grid is a listing's, and follows the product-list\n // store once it settles (a sort, a filter, load more); without it the grid is static. `has-more`: the server's page\n // has a next one, so its render shows load more exactly when the store will. `lazy`: the grid sits below the fold\n // (a product page's related grid), so no card is eager and its images never outrank the page's LCP image.\n attributes: [\n { name: 'products', prop: 'products', type: 'json' },\n { name: 'source', prop: 'source', type: 'string' },\n { name: 'has-more', prop: 'hasMore', type: 'boolean' },\n { name: 'lazy', prop: 'lazy', type: 'boolean' },\n ],\n events: [],\n parts: ['grid', 'empty', 'count', 'load-more'],\n slots: ['empty'],\n children: false,\n}\n\n/** The first row of a wide grid: those cards' images are the page's likely LCP. */\nconst EAGER_CARDS = 4\n\ninterface Shown {\n products: unknown[]\n hasMore: boolean\n loading: boolean\n}\n\n/** A grid of `nymbal-product-card`s, a listing's (following the product-list store) or a static one. */\nexport class NymbalProductGrid extends NymbalElement {\n static override schema = SCHEMA\n static uses = [NymbalProductCard]\n static override properties = propertiesOf(SCHEMA)\n static override styles = [\n baseStyles,\n buttonStyles,\n css`\n :host {\n display: block;\n font-family: var(--nymbal-font-family, system-ui, -apple-system, sans-serif);\n color: var(--nymbal-color-text, #18181b);\n }\n [part~='count'] {\n margin: 0 0 var(--nymbal-spacing-md, 1rem);\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n color: var(--nymbal-color-text-muted, #71717a);\n }\n [part~='grid'] {\n display: grid;\n gap: var(--nymbal-spacing-md, 1rem);\n grid-template-columns: 1fr;\n }\n @media (min-width: 641px) {\n [part~='grid'] {\n grid-template-columns: repeat(2, 1fr);\n gap: var(--nymbal-spacing-lg, 1.5rem);\n }\n }\n @media (min-width: 1025px) {\n [part~='grid'] {\n grid-template-columns: repeat(4, 1fr);\n }\n }\n [part~='empty'] {\n padding: var(--nymbal-spacing-xl, 2rem) 0;\n text-align: center;\n color: var(--nymbal-color-text-muted, #71717a);\n }\n .more {\n display: flex;\n justify-content: center;\n margin-top: var(--nymbal-spacing-xl, 2rem);\n }\n `,\n ]\n\n declare products: unknown\n declare source: string | undefined\n declare hasMore: boolean | undefined\n declare lazy: boolean | undefined\n\n private get listing(): boolean {\n return this.source === 'product-list'\n }\n\n protected override stores() {\n return this.listing ? [this.client.productList] : []\n }\n\n protected override storeSettled(): void {\n // A store already settled when this grid connected may be loading this page's listing now; its last answer was\n // another listing's (a soft navigation), so the server's cards stand until this load settles.\n if (this.client.productList.getState().loading) return\n this.products = undefined\n this.hasMore = undefined\n }\n\n /** The server's cards while they stand, else (a listing with a client) the product-list store's. */\n private shown(): Shown {\n const given = parsedJson<unknown[]>(this, 'products', this.products)\n if (given === undefined && this.listing && this.readsStores) {\n const state = this.client.productList.getState()\n return {\n products: state.products,\n hasMore: state.pagination.hasMore,\n loading: state.loading,\n }\n }\n return {\n products: Array.isArray(given) ? given : [],\n hasMore: this.hasMore === true,\n loading: false,\n }\n }\n\n protected override render() {\n const { products, hasMore, loading } = this.shown()\n const count = products.length\n return html`\n ${this.listing\n ? html`<p part=\"count\">${count} product${count === 1 ? '' : 's'}</p>`\n : nothing}\n ${count === 0\n ? loading\n ? nothing\n : html`<div part=\"empty\"><slot name=\"empty\">No products found.</slot></div>`\n : html`\n <div part=\"grid\">\n ${products.map(\n (p, i) =>\n html`<nymbal-product-card\n product=${JSON.stringify(\n cardProductOf(p as Parameters<typeof cardProductOf>[0]),\n )}\n ?eager=${this.lazy !== true && i < EAGER_CARDS}\n ></nymbal-product-card>`,\n )}\n </div>\n `}\n ${this.listing && hasMore\n ? html`\n <div class=\"more\">\n <button\n type=\"button\"\n part=\"load-more\"\n class=\"btn btn-secondary\"\n data-testid=\"load-more\"\n ?disabled=${loading}\n @click=${() => void this.client.productList.loadMore()}\n >\n Load more\n </button>\n </div>\n `\n : nothing}\n `\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA,SAAS,KAAK,MAAM,eAAe;AAWnC,IAAM,SAAwB;AAAA,EAC5B,KAAK;AAAA,EACL,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA,EAKX,YAAY;AAAA,IACV,EAAE,MAAM,YAAY,MAAM,YAAY,MAAM,OAAO;AAAA,IACnD,EAAE,MAAM,UAAU,MAAM,UAAU,MAAM,SAAS;AAAA,IACjD,EAAE,MAAM,YAAY,MAAM,WAAW,MAAM,UAAU;AAAA,IACrD,EAAE,MAAM,QAAQ,MAAM,QAAQ,MAAM,UAAU;AAAA,EAChD;AAAA,EACA,QAAQ,CAAC;AAAA,EACT,OAAO,CAAC,QAAQ,SAAS,SAAS,WAAW;AAAA,EAC7C,OAAO,CAAC,OAAO;AAAA,EACf,UAAU;AACZ;AAGA,IAAM,cAAc;AASb,IAAM,oBAAN,cAAgC,cAAc;AAAA,EACnD,OAAgB,SAAS;AAAA,EACzB,OAAO,OAAO,CAAC,iBAAiB;AAAA,EAChC,OAAgB,aAAa,aAAa,MAAM;AAAA,EAChD,OAAgB,SAAS;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsCF;AAAA,EAOA,IAAY,UAAmB;AAC7B,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA,EAEmB,SAAS;AAC1B,WAAO,KAAK,UAAU,CAAC,KAAK,OAAO,WAAW,IAAI,CAAC;AAAA,EACrD;AAAA,EAEmB,eAAqB;AAGtC,QAAI,KAAK,OAAO,YAAY,SAAS,EAAE,QAAS;AAChD,SAAK,WAAW;AAChB,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGQ,QAAe;AACrB,UAAM,QAAQ,WAAsB,MAAM,YAAY,KAAK,QAAQ;AACnE,QAAI,UAAU,UAAa,KAAK,WAAW,KAAK,aAAa;AAC3D,YAAM,QAAQ,KAAK,OAAO,YAAY,SAAS;AAC/C,aAAO;AAAA,QACL,UAAU,MAAM;AAAA,QAChB,SAAS,MAAM,WAAW;AAAA,QAC1B,SAAS,MAAM;AAAA,MACjB;AAAA,IACF;AACA,WAAO;AAAA,MACL,UAAU,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;AAAA,MAC1C,SAAS,KAAK,YAAY;AAAA,MAC1B,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEmB,SAAS;AAC1B,UAAM,EAAE,UAAU,SAAS,QAAQ,IAAI,KAAK,MAAM;AAClD,UAAM,QAAQ,SAAS;AACvB,WAAO;AAAA,QACH,KAAK,UACH,uBAAuB,KAAK,WAAW,UAAU,IAAI,KAAK,GAAG,SAC7D,OAAO;AAAA,QACT,UAAU,IACR,UACE,UACA,6EACF;AAAA;AAAA,gBAEM,SAAS;AAAA,MACT,CAAC,GAAG,MACF;AAAA,8BACY,KAAK;AAAA,QACb,cAAc,CAAwC;AAAA,MACxD,CAAC;AAAA,6BACQ,KAAK,SAAS,QAAQ,IAAI,WAAW;AAAA;AAAA,IAEpD,CAAC;AAAA;AAAA,WAEJ;AAAA,QACH,KAAK,WAAW,UACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAOkB,OAAO;AAAA,yBACV,MAAM,KAAK,KAAK,OAAO,YAAY,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,cAM5D,OAAO;AAAA;AAAA,EAEf;AACF;","names":[]}