@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
@@ -0,0 +1,239 @@
1
+ import '@lit-labs/ssr-client/lit-element-hydrate-support.js'
2
+ import {
3
+ navigateOnClick
4
+ } from "./chunk-LO2EW5YP.js";
5
+ import {
6
+ NymbalElement,
7
+ baseStyles,
8
+ parsedJson,
9
+ propertiesOf
10
+ } from "./chunk-EYDMJNNK.js";
11
+
12
+ // src/components/site-header.ts
13
+ import { css, html, nothing } from "lit";
14
+ var SCHEMA = {
15
+ tag: "nymbal-site-header",
16
+ reactName: "SiteHeader",
17
+ attributes: [
18
+ { name: "links", prop: "links", type: "json" },
19
+ { name: "logo", prop: "logo", type: "string" },
20
+ { name: "account-href", prop: "accountHref", type: "string" }
21
+ ],
22
+ events: [],
23
+ parts: ["header", "logo", "nav", "link", "account", "toggle", "actions"],
24
+ // The page's own search bar and mini-cart, light-DOM children with `slot="search"` and `slot="cart"`.
25
+ slots: ["search", "cart"],
26
+ children: true
27
+ };
28
+ var NymbalSiteHeader = 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: sticky;
37
+ top: 0;
38
+ z-index: var(--nymbal-z-dropdown, 100);
39
+ font-family: var(--nymbal-font-family, system-ui, -apple-system, sans-serif);
40
+ }
41
+ [part~='header'] {
42
+ background-color: var(--nymbal-color-surface, #ffffff);
43
+ border-bottom: 1px solid var(--nymbal-color-border, #e4e4e7);
44
+ }
45
+ .inner {
46
+ display: flex;
47
+ flex-wrap: wrap;
48
+ align-items: center;
49
+ gap: var(--nymbal-spacing-md, 1rem);
50
+ max-width: 72rem;
51
+ margin-inline: auto;
52
+ padding: var(--nymbal-spacing-sm, 0.5rem) var(--nymbal-spacing-md, 1rem);
53
+ }
54
+ [part~='logo'] {
55
+ flex-shrink: 0;
56
+ font-size: var(--nymbal-font-size-lg, 1.125rem);
57
+ font-weight: var(--nymbal-font-weight-bold, 700);
58
+ letter-spacing: -0.02em;
59
+ color: var(--nymbal-color-primary, #18181b);
60
+ text-decoration: none;
61
+ }
62
+ [part~='logo']:hover {
63
+ color: var(--nymbal-color-accent, #2563eb);
64
+ }
65
+ .toggle-input {
66
+ position: absolute;
67
+ width: 1px;
68
+ height: 1px;
69
+ overflow: hidden;
70
+ clip: rect(0 0 0 0);
71
+ white-space: nowrap;
72
+ }
73
+ [part~='toggle'] {
74
+ display: flex;
75
+ flex-direction: column;
76
+ justify-content: center;
77
+ gap: 4px;
78
+ width: 28px;
79
+ height: 28px;
80
+ margin-left: auto;
81
+ padding: 2px;
82
+ cursor: pointer;
83
+ }
84
+ .line {
85
+ display: block;
86
+ width: 100%;
87
+ height: 2px;
88
+ border-radius: 1px;
89
+ background-color: var(--nymbal-color-primary, #18181b);
90
+ transition:
91
+ transform var(--nymbal-transition-fast, 150ms ease),
92
+ opacity var(--nymbal-transition-fast, 150ms ease);
93
+ }
94
+ .toggle-input:checked ~ [part~='toggle'] .line:nth-child(1) {
95
+ transform: translateY(6px) rotate(45deg);
96
+ }
97
+ .toggle-input:checked ~ [part~='toggle'] .line:nth-child(2) {
98
+ opacity: 0;
99
+ }
100
+ .toggle-input:checked ~ [part~='toggle'] .line:nth-child(3) {
101
+ transform: translateY(-6px) rotate(-45deg);
102
+ }
103
+ .menu {
104
+ display: none;
105
+ flex-direction: column;
106
+ gap: var(--nymbal-spacing-md, 1rem);
107
+ width: 100%;
108
+ padding-block: var(--nymbal-spacing-sm, 0.5rem);
109
+ }
110
+ .toggle-input:checked ~ .menu {
111
+ display: flex;
112
+ }
113
+ [part~='nav'] {
114
+ display: flex;
115
+ flex-direction: column;
116
+ gap: var(--nymbal-spacing-xs, 0.25rem);
117
+ }
118
+ [part~='link'] {
119
+ display: block;
120
+ padding: var(--nymbal-spacing-xs, 0.25rem) var(--nymbal-spacing-sm, 0.5rem);
121
+ font-size: var(--nymbal-font-size-sm, 0.875rem);
122
+ font-weight: var(--nymbal-font-weight-medium, 500);
123
+ color: var(--nymbal-color-text, #18181b);
124
+ text-decoration: none;
125
+ border-radius: var(--nymbal-radius-sm, 0.25rem);
126
+ transition:
127
+ background-color var(--nymbal-transition-fast, 150ms ease),
128
+ color var(--nymbal-transition-fast, 150ms ease);
129
+ }
130
+ [part~='link']:hover,
131
+ [part~='account']:hover {
132
+ background-color: var(--nymbal-color-border, #e4e4e7);
133
+ color: var(--nymbal-color-accent, #2563eb);
134
+ }
135
+ [part~='actions'] {
136
+ display: flex;
137
+ align-items: center;
138
+ gap: var(--nymbal-spacing-sm, 0.5rem);
139
+ }
140
+ [part~='account'] {
141
+ display: flex;
142
+ align-items: center;
143
+ justify-content: center;
144
+ width: 36px;
145
+ height: 36px;
146
+ color: var(--nymbal-color-text, #18181b);
147
+ text-decoration: none;
148
+ border-radius: var(--nymbal-radius-full, 9999px);
149
+ }
150
+ @media (min-width: 641px) {
151
+ .inner {
152
+ padding-inline: var(--nymbal-spacing-lg, 1.5rem);
153
+ }
154
+ }
155
+ @media (min-width: 769px) {
156
+ [part~='toggle'] {
157
+ display: none;
158
+ }
159
+ .inner {
160
+ flex-wrap: nowrap;
161
+ padding-block: var(--nymbal-spacing-md, 1rem);
162
+ }
163
+ .menu {
164
+ display: flex;
165
+ flex: 1;
166
+ flex-direction: row;
167
+ align-items: center;
168
+ justify-content: space-between;
169
+ width: auto;
170
+ gap: var(--nymbal-spacing-lg, 1.5rem);
171
+ padding-block: 0;
172
+ }
173
+ [part~='nav'] {
174
+ flex-direction: row;
175
+ }
176
+ }
177
+ `
178
+ ];
179
+ render() {
180
+ const links = parsedJson(this, "links", this.links);
181
+ return html`
182
+ <header part="header" data-testid="site-header">
183
+ <div class="inner">
184
+ ${this.logo === void 0 ? nothing : html`<a part="logo" href="/" @click=${navigateOnClick}>${this.logo}</a>`}
185
+ <input
186
+ type="checkbox"
187
+ id="nav-toggle"
188
+ class="toggle-input"
189
+ aria-label="Toggle navigation"
190
+ />
191
+ <label part="toggle" for="nav-toggle" aria-hidden="true">
192
+ <span class="line"></span>
193
+ <span class="line"></span>
194
+ <span class="line"></span>
195
+ </label>
196
+ <div class="menu">
197
+ <nav part="nav" aria-label="Main navigation">
198
+ ${(Array.isArray(links) ? links : []).map(
199
+ (link) => html`<a part="link" href=${link.href} @click=${navigateOnClick}
200
+ >${link.label}</a
201
+ >`
202
+ )}
203
+ </nav>
204
+ <div part="actions">
205
+ <slot name="search"></slot>
206
+ ${this.accountHref === void 0 ? nothing : html`<a
207
+ part="account"
208
+ href=${this.accountHref}
209
+ aria-label="Account"
210
+ @click=${navigateOnClick}
211
+ >
212
+ <svg
213
+ width="20"
214
+ height="20"
215
+ viewBox="0 0 24 24"
216
+ fill="none"
217
+ stroke="currentColor"
218
+ stroke-width="2"
219
+ stroke-linecap="round"
220
+ stroke-linejoin="round"
221
+ aria-hidden="true"
222
+ >
223
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
224
+ <circle cx="12" cy="7" r="4" />
225
+ </svg>
226
+ </a>`}
227
+ <slot name="cart"></slot>
228
+ </div>
229
+ </div>
230
+ </div>
231
+ </header>
232
+ `;
233
+ }
234
+ };
235
+
236
+ export {
237
+ NymbalSiteHeader
238
+ };
239
+ //# sourceMappingURL=chunk-W23P7SI4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/site-header.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 { navigateOnClick } from '../navigate.js'\n\nconst SCHEMA: ElementSchema = {\n tag: 'nymbal-site-header',\n reactName: 'SiteHeader',\n attributes: [\n { name: 'links', prop: 'links', type: 'json' },\n { name: 'logo', prop: 'logo', type: 'string' },\n { name: 'account-href', prop: 'accountHref', type: 'string' },\n ],\n events: [],\n parts: ['header', 'logo', 'nav', 'link', 'account', 'toggle', 'actions'],\n // The page's own search bar and mini-cart, light-DOM children with `slot=\"search\"` and `slot=\"cart\"`.\n slots: ['search', 'cart'],\n children: true,\n}\n\ninterface Link {\n label: string\n href: string\n}\n\n/**\n * The store's header: the logo, the navigation (behind a toggle on a narrow screen, with no script needed), the\n * account link, and the page's search bar and mini-cart in its `search` and `cart` slots.\n */\nexport class NymbalSiteHeader extends NymbalElement {\n static override schema = SCHEMA\n static override properties = propertiesOf(SCHEMA)\n static override styles = [\n baseStyles,\n css`\n :host {\n display: block;\n position: sticky;\n top: 0;\n z-index: var(--nymbal-z-dropdown, 100);\n font-family: var(--nymbal-font-family, system-ui, -apple-system, sans-serif);\n }\n [part~='header'] {\n background-color: var(--nymbal-color-surface, #ffffff);\n border-bottom: 1px solid var(--nymbal-color-border, #e4e4e7);\n }\n .inner {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--nymbal-spacing-md, 1rem);\n max-width: 72rem;\n margin-inline: auto;\n padding: var(--nymbal-spacing-sm, 0.5rem) var(--nymbal-spacing-md, 1rem);\n }\n [part~='logo'] {\n flex-shrink: 0;\n font-size: var(--nymbal-font-size-lg, 1.125rem);\n font-weight: var(--nymbal-font-weight-bold, 700);\n letter-spacing: -0.02em;\n color: var(--nymbal-color-primary, #18181b);\n text-decoration: none;\n }\n [part~='logo']:hover {\n color: var(--nymbal-color-accent, #2563eb);\n }\n .toggle-input {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip: rect(0 0 0 0);\n white-space: nowrap;\n }\n [part~='toggle'] {\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 4px;\n width: 28px;\n height: 28px;\n margin-left: auto;\n padding: 2px;\n cursor: pointer;\n }\n .line {\n display: block;\n width: 100%;\n height: 2px;\n border-radius: 1px;\n background-color: var(--nymbal-color-primary, #18181b);\n transition:\n transform var(--nymbal-transition-fast, 150ms ease),\n opacity var(--nymbal-transition-fast, 150ms ease);\n }\n .toggle-input:checked ~ [part~='toggle'] .line:nth-child(1) {\n transform: translateY(6px) rotate(45deg);\n }\n .toggle-input:checked ~ [part~='toggle'] .line:nth-child(2) {\n opacity: 0;\n }\n .toggle-input:checked ~ [part~='toggle'] .line:nth-child(3) {\n transform: translateY(-6px) rotate(-45deg);\n }\n .menu {\n display: none;\n flex-direction: column;\n gap: var(--nymbal-spacing-md, 1rem);\n width: 100%;\n padding-block: var(--nymbal-spacing-sm, 0.5rem);\n }\n .toggle-input:checked ~ .menu {\n display: flex;\n }\n [part~='nav'] {\n display: flex;\n flex-direction: column;\n gap: var(--nymbal-spacing-xs, 0.25rem);\n }\n [part~='link'] {\n display: block;\n padding: var(--nymbal-spacing-xs, 0.25rem) var(--nymbal-spacing-sm, 0.5rem);\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n font-weight: var(--nymbal-font-weight-medium, 500);\n color: var(--nymbal-color-text, #18181b);\n text-decoration: none;\n border-radius: var(--nymbal-radius-sm, 0.25rem);\n transition:\n background-color var(--nymbal-transition-fast, 150ms ease),\n color var(--nymbal-transition-fast, 150ms ease);\n }\n [part~='link']:hover,\n [part~='account']:hover {\n background-color: var(--nymbal-color-border, #e4e4e7);\n color: var(--nymbal-color-accent, #2563eb);\n }\n [part~='actions'] {\n display: flex;\n align-items: center;\n gap: var(--nymbal-spacing-sm, 0.5rem);\n }\n [part~='account'] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n color: var(--nymbal-color-text, #18181b);\n text-decoration: none;\n border-radius: var(--nymbal-radius-full, 9999px);\n }\n @media (min-width: 641px) {\n .inner {\n padding-inline: var(--nymbal-spacing-lg, 1.5rem);\n }\n }\n @media (min-width: 769px) {\n [part~='toggle'] {\n display: none;\n }\n .inner {\n flex-wrap: nowrap;\n padding-block: var(--nymbal-spacing-md, 1rem);\n }\n .menu {\n display: flex;\n flex: 1;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n width: auto;\n gap: var(--nymbal-spacing-lg, 1.5rem);\n padding-block: 0;\n }\n [part~='nav'] {\n flex-direction: row;\n }\n }\n `,\n ]\n\n declare links: unknown\n declare logo: string | undefined\n declare accountHref: string | undefined\n\n protected override render() {\n const links = parsedJson<Link[]>(this, 'links', this.links)\n return html`\n <header part=\"header\" data-testid=\"site-header\">\n <div class=\"inner\">\n ${this.logo === undefined\n ? nothing\n : html`<a part=\"logo\" href=\"/\" @click=${navigateOnClick}>${this.logo}</a>`}\n <input\n type=\"checkbox\"\n id=\"nav-toggle\"\n class=\"toggle-input\"\n aria-label=\"Toggle navigation\"\n />\n <label part=\"toggle\" for=\"nav-toggle\" aria-hidden=\"true\">\n <span class=\"line\"></span>\n <span class=\"line\"></span>\n <span class=\"line\"></span>\n </label>\n <div class=\"menu\">\n <nav part=\"nav\" aria-label=\"Main navigation\">\n ${(Array.isArray(links) ? links : []).map(\n (link) =>\n html`<a part=\"link\" href=${link.href} @click=${navigateOnClick}\n >${link.label}</a\n >`,\n )}\n </nav>\n <div part=\"actions\">\n <slot name=\"search\"></slot>\n ${this.accountHref === undefined\n ? nothing\n : html`<a\n part=\"account\"\n href=${this.accountHref}\n aria-label=\"Account\"\n @click=${navigateOnClick}\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2\" />\n <circle cx=\"12\" cy=\"7\" r=\"4\" />\n </svg>\n </a>`}\n <slot name=\"cart\"></slot>\n </div>\n </div>\n </div>\n </header>\n `\n }\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,KAAK,MAAM,eAAe;AAUnC,IAAM,SAAwB;AAAA,EAC5B,KAAK;AAAA,EACL,WAAW;AAAA,EACX,YAAY;AAAA,IACV,EAAE,MAAM,SAAS,MAAM,SAAS,MAAM,OAAO;AAAA,IAC7C,EAAE,MAAM,QAAQ,MAAM,QAAQ,MAAM,SAAS;AAAA,IAC7C,EAAE,MAAM,gBAAgB,MAAM,eAAe,MAAM,SAAS;AAAA,EAC9D;AAAA,EACA,QAAQ,CAAC;AAAA,EACT,OAAO,CAAC,UAAU,QAAQ,OAAO,QAAQ,WAAW,UAAU,SAAS;AAAA;AAAA,EAEvE,OAAO,CAAC,UAAU,MAAM;AAAA,EACxB,UAAU;AACZ;AAWO,IAAM,mBAAN,cAA+B,cAAc;AAAA,EAClD,OAAgB,SAAS;AAAA,EACzB,OAAgB,aAAa,aAAa,MAAM;AAAA,EAChD,OAAgB,SAAS;AAAA,IACvB;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;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;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;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,EAiJF;AAAA,EAMmB,SAAS;AAC1B,UAAM,QAAQ,WAAmB,MAAM,SAAS,KAAK,KAAK;AAC1D,WAAO;AAAA;AAAA;AAAA,YAGC,KAAK,SAAS,SACZ,UACA,sCAAsC,eAAe,IAAI,KAAK,IAAI,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAcrE,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,GAAG;AAAA,MACpC,CAAC,SACC,2BAA2B,KAAK,IAAI,WAAW,eAAe;AAAA,uBACzD,KAAK,KAAK;AAAA;AAAA,IAEnB,CAAC;AAAA;AAAA;AAAA;AAAA,gBAIC,KAAK,gBAAgB,SACnB,UACA;AAAA;AAAA,2BAES,KAAK,WAAW;AAAA;AAAA,6BAEd,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAgBrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrB;AACF;","names":[]}
@@ -0,0 +1,279 @@
1
+ import '@lit-labs/ssr-client/lit-element-hydrate-support.js'
2
+ import {
3
+ NymbalElement,
4
+ propertiesOf
5
+ } from "./chunk-EYDMJNNK.js";
6
+
7
+ // src/components/payment-element.ts
8
+ import {
9
+ getPaymentDriver,
10
+ registerPaymentDriver
11
+ } from "@nymbalhq/sdk";
12
+ import { css, nothing } from "lit";
13
+
14
+ // src/payment-drivers/stripe.ts
15
+ var STRIPE_JS = "https://js.stripe.com/v3/";
16
+ function billingDetails(context) {
17
+ const details = {};
18
+ if (context?.email) details.email = context.email;
19
+ const billing = context?.billing;
20
+ if (billing) {
21
+ const name = `${billing.firstName} ${billing.lastName}`.trim();
22
+ if (name) details.name = name;
23
+ details.address = {
24
+ line1: billing.addressLine1,
25
+ ...billing.addressLine2 ? { line2: billing.addressLine2 } : {},
26
+ city: billing.city,
27
+ state: billing.region,
28
+ postal_code: billing.postalCode,
29
+ country: billing.country
30
+ };
31
+ }
32
+ return Object.keys(details).length > 0 ? details : void 0;
33
+ }
34
+ var loading = null;
35
+ function loadStripeJs() {
36
+ const win = window;
37
+ if (win.Stripe) return Promise.resolve(win.Stripe);
38
+ loading ??= new Promise((resolve, reject) => {
39
+ const script = document.createElement("script");
40
+ script.src = STRIPE_JS;
41
+ script.async = true;
42
+ script.addEventListener("load", () => {
43
+ if (win.Stripe) resolve(win.Stripe);
44
+ else reject(new Error("Stripe.js loaded but window.Stripe is missing."));
45
+ });
46
+ script.addEventListener("error", () => {
47
+ loading = null;
48
+ script.remove();
49
+ reject(new Error("Stripe.js could not be loaded."));
50
+ });
51
+ document.head.appendChild(script);
52
+ });
53
+ return loading;
54
+ }
55
+ var instances = /* @__PURE__ */ new Map();
56
+ var mounted = [];
57
+ var stripeDriver = {
58
+ async mount(el, options) {
59
+ const key = options["publishableKey"];
60
+ if (!key)
61
+ throw new Error("Card payments are not configured: the store has no Stripe publishable key.");
62
+ const factory = await loadStripeJs();
63
+ let stripe = instances.get(key);
64
+ if (stripe === void 0) {
65
+ stripe = factory(key);
66
+ instances.set(key, stripe);
67
+ }
68
+ const target = document.createElement("div");
69
+ target.className = "nymbal-payment-card";
70
+ el.replaceChildren(target);
71
+ const card = stripe.elements().create("card", {
72
+ style: {
73
+ base: { fontSize: "16px", color: "#1a1a1a", "::placeholder": { color: "#9ca3af" } }
74
+ }
75
+ });
76
+ card.mount(target);
77
+ mounted = [
78
+ ...mounted.filter((m) => m.host.isConnected && m.host !== el),
79
+ { host: el, stripe, card }
80
+ ];
81
+ },
82
+ async confirm(clientSecret, context) {
83
+ const current = mounted.findLast((m) => m.host.isConnected) ?? mounted.at(-1);
84
+ if (current === void 0) return { ok: false, error: "The card form is not ready." };
85
+ const billing_details = billingDetails(context);
86
+ const result = await current.stripe.confirmCardPayment(clientSecret, {
87
+ payment_method: { card: current.card, ...billing_details ? { billing_details } : {} }
88
+ });
89
+ if (result.error) return { ok: false, error: result.error.message ?? "Payment failed." };
90
+ return { ok: true, confirmed: result.paymentIntent?.status === "succeeded" };
91
+ }
92
+ };
93
+
94
+ // src/payment-drivers/test-mode.ts
95
+ var testModeDriver = {
96
+ mount(el) {
97
+ el.innerHTML = '<p class="nymbal-payment-note">Test mode \u2014 payment skipped</p>';
98
+ },
99
+ confirm() {
100
+ return Promise.resolve({ ok: true, confirmed: false });
101
+ }
102
+ };
103
+
104
+ // src/components/payment-element.ts
105
+ var SCHEMA = {
106
+ tag: "nymbal-payment-element",
107
+ reactName: "PaymentElement",
108
+ attributes: [{ name: "client", prop: "client", type: "json" }],
109
+ events: [
110
+ {
111
+ name: "nymbal:payment:status",
112
+ prop: "onStatusChange",
113
+ detail: "{ status: 'pending' | 'ready' | 'error' }"
114
+ }
115
+ ],
116
+ parts: ["error"],
117
+ slots: [],
118
+ children: false
119
+ };
120
+ var NO_PAYMENT_STEP = { ok: true, confirmed: false };
121
+ function parseClient(value) {
122
+ if (value === null || value === "" || value === "null") return null;
123
+ try {
124
+ const parsed = JSON.parse(value);
125
+ if (parsed === null) return null;
126
+ if (typeof parsed.driver !== "string" || typeof parsed.options !== "object") return void 0;
127
+ return { driver: parsed.driver, options: { ...parsed.options ?? {} } };
128
+ } catch {
129
+ return void 0;
130
+ }
131
+ }
132
+ var NymbalPaymentElement = class _NymbalPaymentElement extends NymbalElement {
133
+ static schema = SCHEMA;
134
+ /** The built-in drivers, except a name the project registered first: the project's driver wins, as its tag does. */
135
+ static beforeDefine() {
136
+ const drivers = [
137
+ ["test-mode", testModeDriver],
138
+ ["stripe", stripeDriver]
139
+ ];
140
+ for (const [name, driver] of drivers) {
141
+ if (getPaymentDriver(name) === void 0) registerPaymentDriver(name, driver);
142
+ }
143
+ }
144
+ static properties = propertiesOf(SCHEMA);
145
+ static styles = css`
146
+ nymbal-payment-element {
147
+ display: block;
148
+ font-family: var(--nymbal-font-family, system-ui, -apple-system, sans-serif);
149
+ }
150
+ nymbal-payment-element .nymbal-payment-note {
151
+ margin: 0;
152
+ font-size: var(--nymbal-font-size-sm, 0.875rem);
153
+ color: var(--nymbal-color-text-muted, #71717a);
154
+ }
155
+ nymbal-payment-element .nymbal-payment-card {
156
+ padding: var(--nymbal-spacing-sm, 0.5rem) var(--nymbal-spacing-md, 1rem);
157
+ border: 1px solid var(--nymbal-color-border, #e4e4e7);
158
+ border-radius: var(--nymbal-radius-md, 0.375rem);
159
+ background: var(--nymbal-color-surface, #ffffff);
160
+ min-height: 40px;
161
+ }
162
+ nymbal-payment-element [part~='error'] {
163
+ margin: 0;
164
+ font-size: var(--nymbal-font-size-sm, 0.875rem);
165
+ color: var(--nymbal-color-error, #dc2626);
166
+ }
167
+ `;
168
+ _status = "pending";
169
+ _error = "";
170
+ _driver = null;
171
+ _mounted = Promise.resolve(true);
172
+ _rendered = void 0;
173
+ _generation = 0;
174
+ get status() {
175
+ return this._status;
176
+ }
177
+ /**
178
+ * The light DOM, so the payment driver's UI (Stripe's iframe) and the checkout form share one tree. Its styles join
179
+ * the sheets of the tree it is in, where a shadow root would have held them.
180
+ */
181
+ createRenderRoot() {
182
+ const root = this.getRootNode();
183
+ const sheet = _NymbalPaymentElement.styles.styleSheet;
184
+ if (sheet !== void 0 && "adoptedStyleSheets" in root && !root.adoptedStyleSheets.includes(sheet)) {
185
+ root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet];
186
+ }
187
+ return this;
188
+ }
189
+ connectedCallback() {
190
+ super.connectedCallback();
191
+ this.mountClient();
192
+ }
193
+ attributeChangedCallback(name, old, value) {
194
+ super.attributeChangedCallback(name, old, value);
195
+ if (this.isConnected) this.mountClient();
196
+ }
197
+ /** The children are the driver's, mounted imperatively, so Lit renders none of its own. */
198
+ render() {
199
+ return nothing;
200
+ }
201
+ /**
202
+ * Completes a payment the API left `requires_action`, once the driver has mounted, handing the driver the
203
+ * checkout's `context`; the driver's answer as is.
204
+ */
205
+ async confirm(clientSecret, context) {
206
+ const driver = this._driver;
207
+ if (this._status === "error") return { ok: false, error: this._error };
208
+ if (driver === null) return NO_PAYMENT_STEP;
209
+ if (!await this._mounted) return { ok: false, error: this._error };
210
+ return driver.confirm(clientSecret, context);
211
+ }
212
+ mountClient() {
213
+ const attribute = this.getAttribute("client");
214
+ if (attribute === this._rendered) return;
215
+ this._rendered = attribute;
216
+ const generation = ++this._generation;
217
+ this._driver = null;
218
+ this._mounted = Promise.resolve(true);
219
+ this.clear();
220
+ const client = parseClient(attribute);
221
+ if (client === void 0)
222
+ return this.fail("The store sent a payment client this page cannot read.");
223
+ if (client === null) return this.setStatus("ready");
224
+ const driver = getPaymentDriver(client.driver);
225
+ if (driver === void 0) {
226
+ return this.fail(`Payment method "${client.driver}" is not available on this storefront.`);
227
+ }
228
+ this._driver = driver;
229
+ this.setStatus("pending");
230
+ this._mounted = Promise.resolve().then(() => driver.mount(this, client.options)).then(
231
+ () => {
232
+ if (generation === this._generation) this.setStatus("ready");
233
+ return true;
234
+ },
235
+ (err) => {
236
+ if (generation === this._generation) {
237
+ this.fail(err instanceof Error ? err.message : "The payment form could not be loaded.");
238
+ }
239
+ return false;
240
+ }
241
+ );
242
+ }
243
+ fail(message) {
244
+ this._error = message;
245
+ const p = document.createElement("p");
246
+ p.setAttribute("part", "error");
247
+ p.setAttribute("data-testid", "payment-element-error");
248
+ p.setAttribute("role", "alert");
249
+ p.textContent = message;
250
+ this.clear(p);
251
+ this.setStatus("error");
252
+ }
253
+ /**
254
+ * Replaces what a driver or an error rendered. Lit's marker comment stays: its next update (a new `client`) writes
255
+ * at it, and throws once it is gone.
256
+ */
257
+ clear(...nodes) {
258
+ for (const child of [...this.childNodes]) {
259
+ if (child.nodeType !== Node.COMMENT_NODE) child.remove();
260
+ }
261
+ this.append(...nodes);
262
+ }
263
+ setStatus(status) {
264
+ if (status === this._status) return;
265
+ this._status = status;
266
+ this.dispatchEvent(
267
+ new CustomEvent("nymbal:payment:status", {
268
+ bubbles: true,
269
+ composed: true,
270
+ detail: { status }
271
+ })
272
+ );
273
+ }
274
+ };
275
+
276
+ export {
277
+ NymbalPaymentElement
278
+ };
279
+ //# sourceMappingURL=chunk-W7D3STJH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/payment-element.ts","../src/payment-drivers/stripe.ts","../src/payment-drivers/test-mode.ts"],"sourcesContent":["import {\n getPaymentDriver,\n registerPaymentDriver,\n type PaymentConfirmContext,\n type PaymentConfirmation,\n type PaymentDriver,\n} from '@nymbalhq/sdk'\nimport type { PaymentClient } from '@nymbalhq/types'\nimport { css, nothing, type CSSResult } from 'lit'\nimport { NymbalElement, propertiesOf, type ElementSchema } from '../base-element.js'\nimport { stripeDriver } from '../payment-drivers/stripe.js'\nimport { testModeDriver } from '../payment-drivers/test-mode.js'\n\nconst SCHEMA: ElementSchema = {\n tag: 'nymbal-payment-element',\n reactName: 'PaymentElement',\n attributes: [{ name: 'client', prop: 'client', type: 'json' }],\n events: [\n {\n name: 'nymbal:payment:status',\n prop: 'onStatusChange',\n detail: \"{ status: 'pending' | 'ready' | 'error' }\",\n },\n ],\n parts: ['error'],\n slots: [],\n children: false,\n}\n\n/** `pending` while a driver mounts; `error` when no payment can be taken, which a checkout must not submit past. */\nexport type PaymentElementStatus = 'pending' | 'ready' | 'error'\n\n/** No client half: nothing to collect, so nothing is confirmed here. */\nconst NO_PAYMENT_STEP: PaymentConfirmation = { ok: true, confirmed: false }\n\nfunction parseClient(value: string | null): PaymentClient | null | undefined {\n if (value === null || value === '' || value === 'null') return null\n try {\n const parsed = JSON.parse(value) as Partial<PaymentClient> | null\n if (parsed === null) return null\n if (typeof parsed.driver !== 'string' || typeof parsed.options !== 'object') return undefined\n return { driver: parsed.driver, options: { ...(parsed.options ?? {}) } }\n } catch {\n return undefined\n }\n}\n\n/**\n * <nymbal-payment-element client='<json>'>: renders the payments adapter's client half (GET /api/capabilities'\n * `payments.client`) through the driver the SDK registry holds under its `driver` name, and confirms a payment\n * through it. It emits `nymbal:payment:status` whenever `status` changes.\n */\nexport class NymbalPaymentElement extends NymbalElement {\n static override schema = SCHEMA\n\n /** The built-in drivers, except a name the project registered first: the project's driver wins, as its tag does. */\n static beforeDefine(): void {\n const drivers: Array<[string, PaymentDriver]> = [\n ['test-mode', testModeDriver],\n ['stripe', stripeDriver],\n ]\n for (const [name, driver] of drivers) {\n if (getPaymentDriver(name) === undefined) registerPaymentDriver(name, driver)\n }\n }\n static override properties = propertiesOf(SCHEMA)\n\n static override styles = css`\n nymbal-payment-element {\n display: block;\n font-family: var(--nymbal-font-family, system-ui, -apple-system, sans-serif);\n }\n nymbal-payment-element .nymbal-payment-note {\n margin: 0;\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n color: var(--nymbal-color-text-muted, #71717a);\n }\n nymbal-payment-element .nymbal-payment-card {\n padding: var(--nymbal-spacing-sm, 0.5rem) var(--nymbal-spacing-md, 1rem);\n border: 1px solid var(--nymbal-color-border, #e4e4e7);\n border-radius: var(--nymbal-radius-md, 0.375rem);\n background: var(--nymbal-color-surface, #ffffff);\n min-height: 40px;\n }\n nymbal-payment-element [part~='error'] {\n margin: 0;\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n color: var(--nymbal-color-error, #dc2626);\n }\n `\n\n private _status: PaymentElementStatus = 'pending'\n private _error = ''\n private _driver: PaymentDriver | null = null\n private _mounted: Promise<boolean> = Promise.resolve(true)\n private _rendered: string | null | undefined = undefined\n private _generation = 0\n\n get status(): PaymentElementStatus {\n return this._status\n }\n\n /**\n * The light DOM, so the payment driver's UI (Stripe's iframe) and the checkout form share one tree. Its styles join\n * the sheets of the tree it is in, where a shadow root would have held them.\n */\n protected override createRenderRoot(): HTMLElement {\n const root = this.getRootNode() as Document | ShadowRoot\n const sheet = (NymbalPaymentElement.styles as CSSResult).styleSheet\n if (sheet !== undefined && 'adoptedStyleSheets' in root && !root.adoptedStyleSheets.includes(sheet)) {\n root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet]\n }\n return this\n }\n\n override connectedCallback(): void {\n super.connectedCallback()\n this.mountClient()\n }\n\n override attributeChangedCallback(name: string, old: string | null, value: string | null): void {\n super.attributeChangedCallback(name, old, value)\n if (this.isConnected) this.mountClient()\n }\n\n /** The children are the driver's, mounted imperatively, so Lit renders none of its own. */\n protected override render() {\n return nothing\n }\n\n /**\n * Completes a payment the API left `requires_action`, once the driver has mounted, handing the driver the\n * checkout's `context`; the driver's answer as is.\n */\n async confirm(clientSecret: string, context?: PaymentConfirmContext): Promise<PaymentConfirmation> {\n const driver = this._driver\n if (this._status === 'error') return { ok: false, error: this._error }\n if (driver === null) return NO_PAYMENT_STEP\n if (!(await this._mounted)) return { ok: false, error: this._error }\n return driver.confirm(clientSecret, context)\n }\n\n private mountClient(): void {\n const attribute = this.getAttribute('client')\n // The same client half again (a reconnect, a framework re-setting the attribute) keeps what is mounted.\n if (attribute === this._rendered) return\n this._rendered = attribute\n const generation = ++this._generation\n this._driver = null\n this._mounted = Promise.resolve(true)\n this.clear()\n\n const client = parseClient(attribute)\n if (client === undefined)\n return this.fail('The store sent a payment client this page cannot read.')\n if (client === null) return this.setStatus('ready')\n const driver = getPaymentDriver(client.driver)\n if (driver === undefined) {\n return this.fail(`Payment method \"${client.driver}\" is not available on this storefront.`)\n }\n\n this._driver = driver\n this.setStatus('pending')\n this._mounted = Promise.resolve()\n .then(() => driver.mount(this, client.options))\n .then(\n () => {\n if (generation === this._generation) this.setStatus('ready')\n return true\n },\n (err: unknown) => {\n if (generation === this._generation) {\n this.fail(err instanceof Error ? err.message : 'The payment form could not be loaded.')\n }\n return false\n },\n )\n }\n\n private fail(message: string): void {\n this._error = message\n const p = document.createElement('p')\n p.setAttribute('part', 'error')\n p.setAttribute('data-testid', 'payment-element-error')\n p.setAttribute('role', 'alert')\n p.textContent = message\n this.clear(p)\n this.setStatus('error')\n }\n\n /**\n * Replaces what a driver or an error rendered. Lit's marker comment stays: its next update (a new `client`) writes\n * at it, and throws once it is gone.\n */\n private clear(...nodes: Node[]): void {\n for (const child of [...this.childNodes]) {\n if (child.nodeType !== Node.COMMENT_NODE) child.remove()\n }\n this.append(...nodes)\n }\n\n private setStatus(status: PaymentElementStatus): void {\n if (status === this._status) return\n this._status = status\n this.dispatchEvent(\n new CustomEvent('nymbal:payment:status', {\n bubbles: true,\n composed: true,\n detail: { status },\n }),\n )\n }\n}\n","import type { PaymentConfirmContext, PaymentConfirmation, PaymentDriver } from '@nymbalhq/sdk'\n\n// Stripe.js from Stripe's own host, as Stripe requires, so no project installs a Stripe package to take a card.\nconst STRIPE_JS = 'https://js.stripe.com/v3/'\n\ninterface StripeCard {\n mount(target: HTMLElement): void\n}\n\ninterface StripeBillingDetails {\n email?: string\n name?: string\n address?: {\n line1: string\n line2?: string\n city: string\n state: string\n postal_code: string\n country: string\n }\n}\n\ninterface StripeInstance {\n elements(): { create(type: 'card', options?: Record<string, unknown>): StripeCard }\n confirmCardPayment(\n clientSecret: string,\n data: { payment_method: { card: StripeCard; billing_details?: StripeBillingDetails } },\n ): Promise<{ paymentIntent?: { status: string }; error?: { message?: string } }>\n}\n\n/** The checkout's shopper details as Stripe's `billing_details`, which its fraud checks read; none when empty. */\nfunction billingDetails(context: PaymentConfirmContext | undefined): StripeBillingDetails | undefined {\n const details: StripeBillingDetails = {}\n if (context?.email) details.email = context.email\n const billing = context?.billing\n if (billing) {\n const name = `${billing.firstName} ${billing.lastName}`.trim()\n if (name) details.name = name\n details.address = {\n line1: billing.addressLine1,\n ...(billing.addressLine2 ? { line2: billing.addressLine2 } : {}),\n city: billing.city,\n state: billing.region,\n postal_code: billing.postalCode,\n country: billing.country,\n }\n }\n return Object.keys(details).length > 0 ? details : undefined\n}\n\ntype StripeFactory = (publishableKey: string) => StripeInstance\n\nlet loading: Promise<StripeFactory> | null = null\n\n/** `window.Stripe`, injecting Stripe.js once per page when it is not there yet. */\nfunction loadStripeJs(): Promise<StripeFactory> {\n const win = window as unknown as { Stripe?: StripeFactory }\n if (win.Stripe) return Promise.resolve(win.Stripe)\n loading ??= new Promise<StripeFactory>((resolve, reject) => {\n const script = document.createElement('script')\n script.src = STRIPE_JS\n script.async = true\n script.addEventListener('load', () => {\n if (win.Stripe) resolve(win.Stripe)\n else reject(new Error('Stripe.js loaded but window.Stripe is missing.'))\n })\n script.addEventListener('error', () => {\n loading = null\n script.remove()\n reject(new Error('Stripe.js could not be loaded.'))\n })\n document.head.appendChild(script)\n })\n return loading\n}\n\nconst instances = new Map<string, StripeInstance>()\nlet mounted: Array<{ host: HTMLElement; stripe: StripeInstance; card: StripeCard }> = []\n\n/** A card form from Stripe.js; confirm pays the intent with the card the shopper typed into. */\nexport const stripeDriver: PaymentDriver = {\n async mount(el, options) {\n const key = options['publishableKey']\n if (!key)\n throw new Error('Card payments are not configured: the store has no Stripe publishable key.')\n const factory = await loadStripeJs()\n let stripe = instances.get(key)\n if (stripe === undefined) {\n stripe = factory(key)\n instances.set(key, stripe)\n }\n const target = document.createElement('div')\n target.className = 'nymbal-payment-card'\n el.replaceChildren(target)\n const card = stripe.elements().create('card', {\n style: {\n base: { fontSize: '16px', color: '#1a1a1a', '::placeholder': { color: '#9ca3af' } },\n },\n })\n card.mount(target)\n mounted = [\n ...mounted.filter((m) => m.host.isConnected && m.host !== el),\n { host: el, stripe, card },\n ]\n },\n\n async confirm(clientSecret, context): Promise<PaymentConfirmation> {\n const current = mounted.findLast((m) => m.host.isConnected) ?? mounted.at(-1)\n if (current === undefined) return { ok: false, error: 'The card form is not ready.' }\n const billing_details = billingDetails(context)\n const result = await current.stripe.confirmCardPayment(clientSecret, {\n payment_method: { card: current.card, ...(billing_details ? { billing_details } : {}) },\n })\n if (result.error) return { ok: false, error: result.error.message ?? 'Payment failed.' }\n // `processing` is accepted but not paid yet: the webhook confirms it.\n return { ok: true, confirmed: result.paymentIntent?.status === 'succeeded' }\n },\n}\n","import type { PaymentDriver } from '@nymbalhq/sdk'\n\n/**\n * For a store whose payments need no shopper input (the native stub): nothing to collect, and nothing confirmed here.\n * An order the stub holds as `requires_action` stays pending until its webhook pays it.\n */\nexport const testModeDriver: PaymentDriver = {\n mount(el) {\n el.innerHTML = '<p class=\"nymbal-payment-note\">Test mode — payment skipped</p>'\n },\n confirm() {\n return Promise.resolve({ ok: true, confirmed: false })\n },\n}\n"],"mappings":"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","names":[]}