@cimplify/sdk 0.44.12 → 0.44.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced.d.mts +1 -1
- package/dist/advanced.d.ts +1 -1
- package/dist/advanced.js +21 -21
- package/dist/advanced.mjs +2 -2
- package/dist/{chunk-6TVQQYQ5.mjs → chunk-6AZZ6AOF.mjs} +21 -1
- package/dist/{chunk-EZK2FXTM.mjs → chunk-7NML226B.mjs} +1 -1
- package/dist/{chunk-BAWKQRIB.js → chunk-CFD4J32N.js} +21 -21
- package/dist/{chunk-OMAP327U.js → chunk-MBGQP6FZ.js} +62 -61
- package/dist/{chunk-V2O5AO7I.js → chunk-MNIKXG7W.js} +26 -0
- package/dist/{chunk-3WD6U5RD.mjs → chunk-UXLQ7WXI.mjs} +4 -3
- package/dist/chunk-XL4CX75J.js +9370 -0
- package/dist/chunk-ZLEFMQC6.mjs +9318 -0
- package/dist/cli/commands/deploy.mjs +9 -1
- package/dist/{client-lvLvB1_K.d.ts → client-D6ZisKco.d.ts} +7 -0
- package/dist/{client-CkGlXZRd.d.mts → client-Dh-bHyhU.d.mts} +7 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +106 -106
- package/dist/index.mjs +3 -3
- package/dist/mock/cli.mjs +172 -23
- package/dist/mock/library.d.mts +2 -2
- package/dist/mock/library.d.ts +2 -2
- package/dist/mock/library.js +172 -23
- package/dist/mock/library.mjs +172 -23
- package/dist/mock/msw.d.mts +1 -1
- package/dist/mock/msw.d.ts +1 -1
- package/dist/mock/msw.js +172 -23
- package/dist/mock/msw.mjs +172 -23
- package/dist/react.d.mts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +80 -80
- package/dist/react.mjs +3 -3
- package/dist/server-CWpUlLOQ.d.mts +1003 -0
- package/dist/server-CWpUlLOQ.d.ts +1003 -0
- package/dist/{server-CUbuQxOV.d.ts → server-CnECAQDQ.d.mts} +28 -6
- package/dist/{server-CUbuQxOV.d.mts → server-CnECAQDQ.d.ts} +28 -6
- package/dist/server.d.mts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +5 -5
- package/dist/server.mjs +3 -3
- package/dist/testing/suite.d.mts +60 -0
- package/dist/testing/suite.d.ts +60 -0
- package/dist/testing/suite.js +169 -0
- package/dist/testing/suite.mjs +165 -0
- package/dist/testing.d.mts +1359 -0
- package/dist/testing.d.ts +1359 -0
- package/dist/testing.js +213 -0
- package/dist/testing.mjs +4 -0
- package/dist/utils.js +28 -28
- package/dist/utils.mjs +1 -1
- package/package.json +18 -3
- package/registry/account.json +15 -0
- package/registry/billing-plan-selector.json +16 -0
- package/registry/cart-drawer.json +17 -0
- package/registry/currency-selector.json +15 -0
- package/registry/customer-input-fields.json +15 -0
- package/registry/delivery-estimate.json +16 -0
- package/registry/index.json +105 -0
- package/registry/location-picker.json +15 -0
- package/registry/recently-viewed.json +16 -0
- package/registry/recommendation-carousel.json +16 -0
- package/registry/resource-picker.json +15 -0
- package/registry/session-message-banner.json +15 -0
- package/templates/storefront-bakery/AGENTS.md +12 -0
- package/templates/storefront-bakery/__tests__/brand.test.ts +4 -0
- package/templates/storefront-bakery/__tests__/cart-flow.test.ts +4 -0
- package/templates/storefront-bakery/__tests__/contract.test.ts +4 -0
- package/templates/storefront-bakery/package.json +11 -3
- package/templates/storefront-bakery/vitest.config.ts +9 -0
- package/templates/storefront-fashion/AGENTS.md +21 -1
- package/templates/storefront-fashion/__tests__/brand.test.ts +4 -0
- package/templates/storefront-fashion/__tests__/cart-flow.test.ts +4 -0
- package/templates/storefront-fashion/__tests__/contract.test.ts +4 -0
- package/templates/storefront-fashion/package.json +11 -3
- package/templates/storefront-fashion/vitest.config.ts +9 -0
- package/templates/storefront-grocery/AGENTS.md +12 -0
- package/templates/storefront-grocery/__tests__/brand.test.ts +4 -0
- package/templates/storefront-grocery/__tests__/cart-flow.test.ts +4 -0
- package/templates/storefront-grocery/__tests__/contract.test.ts +4 -0
- package/templates/storefront-grocery/package.json +11 -3
- package/templates/storefront-grocery/vitest.config.ts +9 -0
- package/templates/storefront-restaurant/AGENTS.md +12 -0
- package/templates/storefront-restaurant/__tests__/brand.test.ts +4 -0
- package/templates/storefront-restaurant/__tests__/cart-flow.test.ts +4 -0
- package/templates/storefront-restaurant/__tests__/contract.test.ts +4 -0
- package/templates/storefront-restaurant/package.json +11 -3
- package/templates/storefront-restaurant/vitest.config.ts +9 -0
- package/templates/storefront-retail/AGENTS.md +12 -0
- package/templates/storefront-retail/__tests__/brand.test.ts +4 -0
- package/templates/storefront-retail/__tests__/cart-flow.test.ts +4 -0
- package/templates/storefront-retail/__tests__/contract.test.ts +4 -0
- package/templates/storefront-retail/package.json +11 -3
- package/templates/storefront-retail/vitest.config.ts +9 -0
- package/templates/storefront-services/AGENTS.md +12 -0
- package/templates/storefront-services/__tests__/brand.test.ts +4 -0
- package/templates/storefront-services/__tests__/cart-flow.test.ts +4 -0
- package/templates/storefront-services/__tests__/contract.test.ts +4 -0
- package/templates/storefront-services/package.json +11 -3
- package/templates/storefront-services/vitest.config.ts +9 -0
package/dist/testing.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkXL4CX75J_js = require('./chunk-XL4CX75J.js');
|
|
4
|
+
require('./chunk-MBGQP6FZ.js');
|
|
5
|
+
require('./chunk-CFD4J32N.js');
|
|
6
|
+
require('./chunk-MNIKXG7W.js');
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
Object.defineProperty(exports, "AddItemPayloadSchema", {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function () { return chunkXL4CX75J_js.AddItemPayloadSchema; }
|
|
13
|
+
});
|
|
14
|
+
Object.defineProperty(exports, "AddOnDetailsSchema", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: function () { return chunkXL4CX75J_js.AddOnDetailsSchema; }
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "AuthorizationTypeSchema", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () { return chunkXL4CX75J_js.AuthorizationTypeSchema; }
|
|
21
|
+
});
|
|
22
|
+
Object.defineProperty(exports, "BrandContactSchema", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
get: function () { return chunkXL4CX75J_js.BrandContactSchema; }
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(exports, "BrandFaqSchema", {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
get: function () { return chunkXL4CX75J_js.BrandFaqSchema; }
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(exports, "BrandHeroSchema", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
get: function () { return chunkXL4CX75J_js.BrandHeroSchema; }
|
|
33
|
+
});
|
|
34
|
+
Object.defineProperty(exports, "BrandNavLinkSchema", {
|
|
35
|
+
enumerable: true,
|
|
36
|
+
get: function () { return chunkXL4CX75J_js.BrandNavLinkSchema; }
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(exports, "BrandPolicyPageSchema", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
get: function () { return chunkXL4CX75J_js.BrandPolicyPageSchema; }
|
|
41
|
+
});
|
|
42
|
+
Object.defineProperty(exports, "BrandPolicySectionSchema", {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: function () { return chunkXL4CX75J_js.BrandPolicySectionSchema; }
|
|
45
|
+
});
|
|
46
|
+
Object.defineProperty(exports, "BrandSchema", {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
get: function () { return chunkXL4CX75J_js.BrandSchema; }
|
|
49
|
+
});
|
|
50
|
+
Object.defineProperty(exports, "BrandSocialSchema", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
get: function () { return chunkXL4CX75J_js.BrandSocialSchema; }
|
|
53
|
+
});
|
|
54
|
+
Object.defineProperty(exports, "BundleResolvedSchema", {
|
|
55
|
+
enumerable: true,
|
|
56
|
+
get: function () { return chunkXL4CX75J_js.BundleResolvedSchema; }
|
|
57
|
+
});
|
|
58
|
+
Object.defineProperty(exports, "BundleSelectionInputSchema", {
|
|
59
|
+
enumerable: true,
|
|
60
|
+
get: function () { return chunkXL4CX75J_js.BundleSelectionInputSchema; }
|
|
61
|
+
});
|
|
62
|
+
Object.defineProperty(exports, "CartItemSchema", {
|
|
63
|
+
enumerable: true,
|
|
64
|
+
get: function () { return chunkXL4CX75J_js.CartItemSchema; }
|
|
65
|
+
});
|
|
66
|
+
Object.defineProperty(exports, "CartPricingSchema", {
|
|
67
|
+
enumerable: true,
|
|
68
|
+
get: function () { return chunkXL4CX75J_js.CartPricingSchema; }
|
|
69
|
+
});
|
|
70
|
+
Object.defineProperty(exports, "CartSchema", {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
get: function () { return chunkXL4CX75J_js.CartSchema; }
|
|
73
|
+
});
|
|
74
|
+
Object.defineProperty(exports, "CheckoutBodySchema", {
|
|
75
|
+
enumerable: true,
|
|
76
|
+
get: function () { return chunkXL4CX75J_js.CheckoutBodySchema; }
|
|
77
|
+
});
|
|
78
|
+
Object.defineProperty(exports, "CheckoutCustomerSchema", {
|
|
79
|
+
enumerable: true,
|
|
80
|
+
get: function () { return chunkXL4CX75J_js.CheckoutCustomerSchema; }
|
|
81
|
+
});
|
|
82
|
+
Object.defineProperty(exports, "CheckoutResponseSchema", {
|
|
83
|
+
enumerable: true,
|
|
84
|
+
get: function () { return chunkXL4CX75J_js.CheckoutResponseSchema; }
|
|
85
|
+
});
|
|
86
|
+
Object.defineProperty(exports, "ChosenPriceSchema", {
|
|
87
|
+
enumerable: true,
|
|
88
|
+
get: function () { return chunkXL4CX75J_js.ChosenPriceSchema; }
|
|
89
|
+
});
|
|
90
|
+
Object.defineProperty(exports, "CompositeResolvedSchema", {
|
|
91
|
+
enumerable: true,
|
|
92
|
+
get: function () { return chunkXL4CX75J_js.CompositeResolvedSchema; }
|
|
93
|
+
});
|
|
94
|
+
Object.defineProperty(exports, "CompositeSelectionInputSchema", {
|
|
95
|
+
enumerable: true,
|
|
96
|
+
get: function () { return chunkXL4CX75J_js.CompositeSelectionInputSchema; }
|
|
97
|
+
});
|
|
98
|
+
Object.defineProperty(exports, "CountryCodeSchema", {
|
|
99
|
+
enumerable: true,
|
|
100
|
+
get: function () { return chunkXL4CX75J_js.CountryCodeSchema; }
|
|
101
|
+
});
|
|
102
|
+
Object.defineProperty(exports, "CurrencyCodeSchema", {
|
|
103
|
+
enumerable: true,
|
|
104
|
+
get: function () { return chunkXL4CX75J_js.CurrencyCodeSchema; }
|
|
105
|
+
});
|
|
106
|
+
Object.defineProperty(exports, "CustomerInputValueSchema", {
|
|
107
|
+
enumerable: true,
|
|
108
|
+
get: function () { return chunkXL4CX75J_js.CustomerInputValueSchema; }
|
|
109
|
+
});
|
|
110
|
+
Object.defineProperty(exports, "DiscountDetailsSchema", {
|
|
111
|
+
enumerable: true,
|
|
112
|
+
get: function () { return chunkXL4CX75J_js.DiscountDetailsSchema; }
|
|
113
|
+
});
|
|
114
|
+
Object.defineProperty(exports, "IsoDateTimeSchema", {
|
|
115
|
+
enumerable: true,
|
|
116
|
+
get: function () { return chunkXL4CX75J_js.IsoDateTimeSchema; }
|
|
117
|
+
});
|
|
118
|
+
Object.defineProperty(exports, "LineTypeSchema", {
|
|
119
|
+
enumerable: true,
|
|
120
|
+
get: function () { return chunkXL4CX75J_js.LineTypeSchema; }
|
|
121
|
+
});
|
|
122
|
+
Object.defineProperty(exports, "LocaleSchema", {
|
|
123
|
+
enumerable: true,
|
|
124
|
+
get: function () { return chunkXL4CX75J_js.LocaleSchema; }
|
|
125
|
+
});
|
|
126
|
+
Object.defineProperty(exports, "MoneyLooseSchema", {
|
|
127
|
+
enumerable: true,
|
|
128
|
+
get: function () { return chunkXL4CX75J_js.MoneyLooseSchema; }
|
|
129
|
+
});
|
|
130
|
+
Object.defineProperty(exports, "MoneySchema", {
|
|
131
|
+
enumerable: true,
|
|
132
|
+
get: function () { return chunkXL4CX75J_js.MoneySchema; }
|
|
133
|
+
});
|
|
134
|
+
Object.defineProperty(exports, "NextActionSchema", {
|
|
135
|
+
enumerable: true,
|
|
136
|
+
get: function () { return chunkXL4CX75J_js.NextActionSchema; }
|
|
137
|
+
});
|
|
138
|
+
Object.defineProperty(exports, "PhoneE164Schema", {
|
|
139
|
+
enumerable: true,
|
|
140
|
+
get: function () { return chunkXL4CX75J_js.PhoneE164Schema; }
|
|
141
|
+
});
|
|
142
|
+
Object.defineProperty(exports, "ProductTypeSchema", {
|
|
143
|
+
enumerable: true,
|
|
144
|
+
get: function () { return chunkXL4CX75J_js.ProductTypeSchema; }
|
|
145
|
+
});
|
|
146
|
+
Object.defineProperty(exports, "SCHEMA_VERSION", {
|
|
147
|
+
enumerable: true,
|
|
148
|
+
get: function () { return chunkXL4CX75J_js.SCHEMA_VERSION; }
|
|
149
|
+
});
|
|
150
|
+
Object.defineProperty(exports, "SchemaViolationError", {
|
|
151
|
+
enumerable: true,
|
|
152
|
+
get: function () { return chunkXL4CX75J_js.SchemaViolationError; }
|
|
153
|
+
});
|
|
154
|
+
Object.defineProperty(exports, "SeedNameSchema", {
|
|
155
|
+
enumerable: true,
|
|
156
|
+
get: function () { return chunkXL4CX75J_js.SeedNameSchema; }
|
|
157
|
+
});
|
|
158
|
+
Object.defineProperty(exports, "ServiceStatusSchema", {
|
|
159
|
+
enumerable: true,
|
|
160
|
+
get: function () { return chunkXL4CX75J_js.ServiceStatusSchema; }
|
|
161
|
+
});
|
|
162
|
+
Object.defineProperty(exports, "TimestampSchema", {
|
|
163
|
+
enumerable: true,
|
|
164
|
+
get: function () { return chunkXL4CX75J_js.TimestampSchema; }
|
|
165
|
+
});
|
|
166
|
+
Object.defineProperty(exports, "VariantDetailsSchema", {
|
|
167
|
+
enumerable: true,
|
|
168
|
+
get: function () { return chunkXL4CX75J_js.VariantDetailsSchema; }
|
|
169
|
+
});
|
|
170
|
+
Object.defineProperty(exports, "VariantDisplayAttributeSchema", {
|
|
171
|
+
enumerable: true,
|
|
172
|
+
get: function () { return chunkXL4CX75J_js.VariantDisplayAttributeSchema; }
|
|
173
|
+
});
|
|
174
|
+
Object.defineProperty(exports, "VariantInfoSchema", {
|
|
175
|
+
enumerable: true,
|
|
176
|
+
get: function () { return chunkXL4CX75J_js.VariantInfoSchema; }
|
|
177
|
+
});
|
|
178
|
+
Object.defineProperty(exports, "assertAddItemPayload", {
|
|
179
|
+
enumerable: true,
|
|
180
|
+
get: function () { return chunkXL4CX75J_js.assertAddItemPayload; }
|
|
181
|
+
});
|
|
182
|
+
Object.defineProperty(exports, "assertBrand", {
|
|
183
|
+
enumerable: true,
|
|
184
|
+
get: function () { return chunkXL4CX75J_js.assertBrand; }
|
|
185
|
+
});
|
|
186
|
+
Object.defineProperty(exports, "assertCart", {
|
|
187
|
+
enumerable: true,
|
|
188
|
+
get: function () { return chunkXL4CX75J_js.assertCart; }
|
|
189
|
+
});
|
|
190
|
+
Object.defineProperty(exports, "assertCartItem", {
|
|
191
|
+
enumerable: true,
|
|
192
|
+
get: function () { return chunkXL4CX75J_js.assertCartItem; }
|
|
193
|
+
});
|
|
194
|
+
Object.defineProperty(exports, "assertCheckoutBody", {
|
|
195
|
+
enumerable: true,
|
|
196
|
+
get: function () { return chunkXL4CX75J_js.assertCheckoutBody; }
|
|
197
|
+
});
|
|
198
|
+
Object.defineProperty(exports, "assertCheckoutResponse", {
|
|
199
|
+
enumerable: true,
|
|
200
|
+
get: function () { return chunkXL4CX75J_js.assertCheckoutResponse; }
|
|
201
|
+
});
|
|
202
|
+
Object.defineProperty(exports, "createTestClient", {
|
|
203
|
+
enumerable: true,
|
|
204
|
+
get: function () { return chunkXL4CX75J_js.createTestClient; }
|
|
205
|
+
});
|
|
206
|
+
Object.defineProperty(exports, "fixtures", {
|
|
207
|
+
enumerable: true,
|
|
208
|
+
get: function () { return chunkXL4CX75J_js.fixtures; }
|
|
209
|
+
});
|
|
210
|
+
Object.defineProperty(exports, "makeAssertion", {
|
|
211
|
+
enumerable: true,
|
|
212
|
+
get: function () { return chunkXL4CX75J_js.makeAssertion; }
|
|
213
|
+
});
|
package/dist/testing.mjs
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { AddItemPayloadSchema, AddOnDetailsSchema, AuthorizationTypeSchema, BrandContactSchema, BrandFaqSchema, BrandHeroSchema, BrandNavLinkSchema, BrandPolicyPageSchema, BrandPolicySectionSchema, BrandSchema, BrandSocialSchema, BundleResolvedSchema, BundleSelectionInputSchema, CartItemSchema, CartPricingSchema, CartSchema, CheckoutBodySchema, CheckoutCustomerSchema, CheckoutResponseSchema, ChosenPriceSchema, CompositeResolvedSchema, CompositeSelectionInputSchema, CountryCodeSchema, CurrencyCodeSchema, CustomerInputValueSchema, DiscountDetailsSchema, IsoDateTimeSchema, LineTypeSchema, LocaleSchema, MoneyLooseSchema, MoneySchema, NextActionSchema, PhoneE164Schema, ProductTypeSchema, SCHEMA_VERSION, SchemaViolationError, SeedNameSchema, ServiceStatusSchema, TimestampSchema, VariantDetailsSchema, VariantDisplayAttributeSchema, VariantInfoSchema, assertAddItemPayload, assertBrand, assertCart, assertCartItem, assertCheckoutBody, assertCheckoutResponse, createTestClient, fixtures, makeAssertion } from './chunk-ZLEFMQC6.mjs';
|
|
2
|
+
import './chunk-UXLQ7WXI.mjs';
|
|
3
|
+
import './chunk-7NML226B.mjs';
|
|
4
|
+
import './chunk-6AZZ6AOF.mjs';
|
package/dist/utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chunkL5GKVQVJ_js = require('./chunk-L5GKVQVJ.js');
|
|
4
|
-
var
|
|
4
|
+
var chunkMNIKXG7W_js = require('./chunk-MNIKXG7W.js');
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
@@ -19,109 +19,109 @@ Object.defineProperty(exports, "resolvePhoneCountryCode", {
|
|
|
19
19
|
});
|
|
20
20
|
Object.defineProperty(exports, "CURRENCY_SYMBOLS", {
|
|
21
21
|
enumerable: true,
|
|
22
|
-
get: function () { return
|
|
22
|
+
get: function () { return chunkMNIKXG7W_js.CURRENCY_SYMBOLS; }
|
|
23
23
|
});
|
|
24
24
|
Object.defineProperty(exports, "MOBILE_MONEY_PROVIDERS", {
|
|
25
25
|
enumerable: true,
|
|
26
|
-
get: function () { return
|
|
26
|
+
get: function () { return chunkMNIKXG7W_js.MOBILE_MONEY_PROVIDERS; }
|
|
27
27
|
});
|
|
28
28
|
Object.defineProperty(exports, "categorizePaymentError", {
|
|
29
29
|
enumerable: true,
|
|
30
|
-
get: function () { return
|
|
30
|
+
get: function () { return chunkMNIKXG7W_js.categorizePaymentError; }
|
|
31
31
|
});
|
|
32
32
|
Object.defineProperty(exports, "detectMobileMoneyProvider", {
|
|
33
33
|
enumerable: true,
|
|
34
|
-
get: function () { return
|
|
34
|
+
get: function () { return chunkMNIKXG7W_js.detectMobileMoneyProvider; }
|
|
35
35
|
});
|
|
36
36
|
Object.defineProperty(exports, "formatMoney", {
|
|
37
37
|
enumerable: true,
|
|
38
|
-
get: function () { return
|
|
38
|
+
get: function () { return chunkMNIKXG7W_js.formatMoney; }
|
|
39
39
|
});
|
|
40
40
|
Object.defineProperty(exports, "formatNumberCompact", {
|
|
41
41
|
enumerable: true,
|
|
42
|
-
get: function () { return
|
|
42
|
+
get: function () { return chunkMNIKXG7W_js.formatNumberCompact; }
|
|
43
43
|
});
|
|
44
44
|
Object.defineProperty(exports, "formatPrice", {
|
|
45
45
|
enumerable: true,
|
|
46
|
-
get: function () { return
|
|
46
|
+
get: function () { return chunkMNIKXG7W_js.formatPrice; }
|
|
47
47
|
});
|
|
48
48
|
Object.defineProperty(exports, "formatPriceAdjustment", {
|
|
49
49
|
enumerable: true,
|
|
50
|
-
get: function () { return
|
|
50
|
+
get: function () { return chunkMNIKXG7W_js.formatPriceAdjustment; }
|
|
51
51
|
});
|
|
52
52
|
Object.defineProperty(exports, "formatPriceCompact", {
|
|
53
53
|
enumerable: true,
|
|
54
|
-
get: function () { return
|
|
54
|
+
get: function () { return chunkMNIKXG7W_js.formatPriceCompact; }
|
|
55
55
|
});
|
|
56
56
|
Object.defineProperty(exports, "formatPriceWithTax", {
|
|
57
57
|
enumerable: true,
|
|
58
|
-
get: function () { return
|
|
58
|
+
get: function () { return chunkMNIKXG7W_js.formatPriceWithTax; }
|
|
59
59
|
});
|
|
60
60
|
Object.defineProperty(exports, "formatProductPrice", {
|
|
61
61
|
enumerable: true,
|
|
62
|
-
get: function () { return
|
|
62
|
+
get: function () { return chunkMNIKXG7W_js.formatProductPrice; }
|
|
63
63
|
});
|
|
64
64
|
Object.defineProperty(exports, "getBasePrice", {
|
|
65
65
|
enumerable: true,
|
|
66
|
-
get: function () { return
|
|
66
|
+
get: function () { return chunkMNIKXG7W_js.getBasePrice; }
|
|
67
67
|
});
|
|
68
68
|
Object.defineProperty(exports, "getCurrencySymbol", {
|
|
69
69
|
enumerable: true,
|
|
70
|
-
get: function () { return
|
|
70
|
+
get: function () { return chunkMNIKXG7W_js.getCurrencySymbol; }
|
|
71
71
|
});
|
|
72
72
|
Object.defineProperty(exports, "getDiscountPercentage", {
|
|
73
73
|
enumerable: true,
|
|
74
|
-
get: function () { return
|
|
74
|
+
get: function () { return chunkMNIKXG7W_js.getDiscountPercentage; }
|
|
75
75
|
});
|
|
76
76
|
Object.defineProperty(exports, "getDisplayPrice", {
|
|
77
77
|
enumerable: true,
|
|
78
|
-
get: function () { return
|
|
78
|
+
get: function () { return chunkMNIKXG7W_js.getDisplayPrice; }
|
|
79
79
|
});
|
|
80
80
|
Object.defineProperty(exports, "getMarkupPercentage", {
|
|
81
81
|
enumerable: true,
|
|
82
|
-
get: function () { return
|
|
82
|
+
get: function () { return chunkMNIKXG7W_js.getMarkupPercentage; }
|
|
83
83
|
});
|
|
84
84
|
Object.defineProperty(exports, "getProductCurrency", {
|
|
85
85
|
enumerable: true,
|
|
86
|
-
get: function () { return
|
|
86
|
+
get: function () { return chunkMNIKXG7W_js.getProductCurrency; }
|
|
87
87
|
});
|
|
88
88
|
Object.defineProperty(exports, "getTaxAmount", {
|
|
89
89
|
enumerable: true,
|
|
90
|
-
get: function () { return
|
|
90
|
+
get: function () { return chunkMNIKXG7W_js.getTaxAmount; }
|
|
91
91
|
});
|
|
92
92
|
Object.defineProperty(exports, "hasTaxInfo", {
|
|
93
93
|
enumerable: true,
|
|
94
|
-
get: function () { return
|
|
94
|
+
get: function () { return chunkMNIKXG7W_js.hasTaxInfo; }
|
|
95
95
|
});
|
|
96
96
|
Object.defineProperty(exports, "isOnSale", {
|
|
97
97
|
enumerable: true,
|
|
98
|
-
get: function () { return
|
|
98
|
+
get: function () { return chunkMNIKXG7W_js.isOnSale; }
|
|
99
99
|
});
|
|
100
100
|
Object.defineProperty(exports, "isPaymentStatusFailure", {
|
|
101
101
|
enumerable: true,
|
|
102
|
-
get: function () { return
|
|
102
|
+
get: function () { return chunkMNIKXG7W_js.isPaymentStatusFailure; }
|
|
103
103
|
});
|
|
104
104
|
Object.defineProperty(exports, "isPaymentStatusRequiresAction", {
|
|
105
105
|
enumerable: true,
|
|
106
|
-
get: function () { return
|
|
106
|
+
get: function () { return chunkMNIKXG7W_js.isPaymentStatusRequiresAction; }
|
|
107
107
|
});
|
|
108
108
|
Object.defineProperty(exports, "isPaymentStatusSuccess", {
|
|
109
109
|
enumerable: true,
|
|
110
|
-
get: function () { return
|
|
110
|
+
get: function () { return chunkMNIKXG7W_js.isPaymentStatusSuccess; }
|
|
111
111
|
});
|
|
112
112
|
Object.defineProperty(exports, "isTaxInclusive", {
|
|
113
113
|
enumerable: true,
|
|
114
|
-
get: function () { return
|
|
114
|
+
get: function () { return chunkMNIKXG7W_js.isTaxInclusive; }
|
|
115
115
|
});
|
|
116
116
|
Object.defineProperty(exports, "normalizePaymentResponse", {
|
|
117
117
|
enumerable: true,
|
|
118
|
-
get: function () { return
|
|
118
|
+
get: function () { return chunkMNIKXG7W_js.normalizePaymentResponse; }
|
|
119
119
|
});
|
|
120
120
|
Object.defineProperty(exports, "normalizeStatusResponse", {
|
|
121
121
|
enumerable: true,
|
|
122
|
-
get: function () { return
|
|
122
|
+
get: function () { return chunkMNIKXG7W_js.normalizeStatusResponse; }
|
|
123
123
|
});
|
|
124
124
|
Object.defineProperty(exports, "parsePrice", {
|
|
125
125
|
enumerable: true,
|
|
126
|
-
get: function () { return
|
|
126
|
+
get: function () { return chunkMNIKXG7W_js.parsePrice; }
|
|
127
127
|
});
|
package/dist/utils.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { isValidPhoneForContext, normalizePhoneToE164, resolvePhoneCountryCode } from './chunk-YOIVZ6OA.mjs';
|
|
2
|
-
export { CURRENCY_SYMBOLS, MOBILE_MONEY_PROVIDERS, categorizePaymentError, detectMobileMoneyProvider, formatMoney, formatNumberCompact, formatPrice, formatPriceAdjustment, formatPriceCompact, formatPriceWithTax, formatProductPrice, getBasePrice, getCurrencySymbol, getDiscountPercentage, getDisplayPrice, getMarkupPercentage, getProductCurrency, getTaxAmount, hasTaxInfo, isOnSale, isPaymentStatusFailure, isPaymentStatusRequiresAction, isPaymentStatusSuccess, isTaxInclusive, normalizePaymentResponse, normalizeStatusResponse, parsePrice } from './chunk-
|
|
2
|
+
export { CURRENCY_SYMBOLS, MOBILE_MONEY_PROVIDERS, categorizePaymentError, detectMobileMoneyProvider, formatMoney, formatNumberCompact, formatPrice, formatPriceAdjustment, formatPriceCompact, formatPriceWithTax, formatProductPrice, getBasePrice, getCurrencySymbol, getDiscountPercentage, getDisplayPrice, getMarkupPercentage, getProductCurrency, getTaxAmount, hasTaxInfo, isOnSale, isPaymentStatusFailure, isPaymentStatusRequiresAction, isPaymentStatusSuccess, isTaxInclusive, normalizePaymentResponse, normalizeStatusResponse, parsePrice } from './chunk-6AZZ6AOF.mjs';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cimplify/sdk",
|
|
3
|
-
"version": "0.44.
|
|
3
|
+
"version": "0.44.16",
|
|
4
4
|
"description": "Cimplify Commerce SDK for storefronts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cimplify",
|
|
@@ -61,6 +61,16 @@
|
|
|
61
61
|
"import": "./dist/mock/msw.mjs",
|
|
62
62
|
"require": "./dist/mock/msw.js"
|
|
63
63
|
},
|
|
64
|
+
"./testing": {
|
|
65
|
+
"types": "./dist/testing.d.ts",
|
|
66
|
+
"import": "./dist/testing.mjs",
|
|
67
|
+
"require": "./dist/testing.js"
|
|
68
|
+
},
|
|
69
|
+
"./testing/suite": {
|
|
70
|
+
"types": "./dist/testing/suite.d.ts",
|
|
71
|
+
"import": "./dist/testing/suite.mjs",
|
|
72
|
+
"require": "./dist/testing/suite.js"
|
|
73
|
+
},
|
|
64
74
|
"./styles.css": "./dist/styles.css"
|
|
65
75
|
},
|
|
66
76
|
"scripts": {
|
|
@@ -81,7 +91,8 @@
|
|
|
81
91
|
"peerDependencies": {
|
|
82
92
|
"@paystack/inline-js": "^2.22.8",
|
|
83
93
|
"react": ">=17.0.0",
|
|
84
|
-
"msw": ">=2.0.0"
|
|
94
|
+
"msw": ">=2.0.0",
|
|
95
|
+
"vitest": ">=2.0.0"
|
|
85
96
|
},
|
|
86
97
|
"peerDependenciesMeta": {
|
|
87
98
|
"@paystack/inline-js": {
|
|
@@ -92,6 +103,9 @@
|
|
|
92
103
|
},
|
|
93
104
|
"msw": {
|
|
94
105
|
"optional": true
|
|
106
|
+
},
|
|
107
|
+
"vitest": {
|
|
108
|
+
"optional": true
|
|
95
109
|
}
|
|
96
110
|
},
|
|
97
111
|
"devDependencies": {
|
|
@@ -116,6 +130,7 @@
|
|
|
116
130
|
"@base-ui/react": "^1.4.1",
|
|
117
131
|
"clsx": "^2.1.1",
|
|
118
132
|
"libphonenumber-js": "1.12.41",
|
|
119
|
-
"tailwind-merge": "^3.5.0"
|
|
133
|
+
"tailwind-merge": "^3.5.0",
|
|
134
|
+
"zod": "^4.4.3"
|
|
120
135
|
}
|
|
121
136
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "account",
|
|
3
|
+
"title": "CimplifyAccount",
|
|
4
|
+
"description": "Iframe wrapper for the Cimplify account portal — sign-in, orders, addresses, settings.",
|
|
5
|
+
"type": "component",
|
|
6
|
+
"registryDependencies": [
|
|
7
|
+
"cn"
|
|
8
|
+
],
|
|
9
|
+
"files": [
|
|
10
|
+
{
|
|
11
|
+
"path": "account.tsx",
|
|
12
|
+
"content": "\"use client\";\n\nimport React, { useEffect, useRef, useState, useMemo } from \"react\";\nimport type { CimplifyClient } from \"../client\";\nimport { useOptionalCimplify } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface CimplifyAccountProps {\n /** CimplifyClient instance. Falls back to provider context. */\n client?: CimplifyClient;\n /** Override the Link base URL. */\n linkUrl?: string;\n /** Initial section to show: \"dashboard\" | \"orders\" | \"addresses\" | \"payment-methods\" | \"sessions\" | \"settings\". */\n section?: string;\n /** Appearance variables — passed to Link for theming. */\n appearance?: {\n theme?: \"light\" | \"dark\";\n variables?: {\n primaryColor?: string;\n fontFamily?: string;\n borderRadius?: string;\n };\n };\n /** Called when the user logs out from the account portal. */\n onLogout?: () => void;\n /** Additional CSS class for the container. */\n className?: string;\n}\n\nconst DEFAULT_LINK_URL = \"https://link.cimplify.io\";\n\nexport function CimplifyAccount({\n client: clientProp,\n linkUrl,\n section,\n appearance,\n onLogout,\n className,\n}: CimplifyAccountProps): React.ReactElement {\n const context = useOptionalCimplify();\n const client = clientProp ?? context?.client;\n const resolvedLinkUrl = linkUrl || DEFAULT_LINK_URL;\n\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const [height, setHeight] = useState(400);\n const [isReady, setIsReady] = useState(false);\n\n const iframeSrc = useMemo(() => {\n const path = section ? `/elements/account/${section}` : \"/elements/account\";\n const url = new URL(path, resolvedLinkUrl);\n if (client) {\n const businessId = client.getBusinessId?.() ?? \"\";\n if (businessId) url.searchParams.set(\"businessId\", businessId);\n }\n return url.toString();\n }, [resolvedLinkUrl, section, client]);\n\n // Listen for messages from the iframe\n useEffect(() => {\n function handleMessage(event: MessageEvent) {\n if (!event.data || typeof event.data !== \"object\") return;\n\n // Only accept messages from our iframe\n if (iframeRef.current?.contentWindow && event.source !== iframeRef.current.contentWindow) {\n return;\n }\n\n switch (event.data.type) {\n case \"ready\":\n setIsReady(true);\n if (typeof event.data.height === \"number\") {\n setHeight(event.data.height);\n }\n // Send init with token + appearance\n sendInit();\n break;\n\n case \"height_change\":\n if (typeof event.data.height === \"number\") {\n setHeight(event.data.height);\n }\n break;\n\n case \"logout_complete\":\n onLogout?.();\n break;\n }\n }\n\n window.addEventListener(\"message\", handleMessage);\n return () => window.removeEventListener(\"message\", handleMessage);\n }, [appearance, client, onLogout]);\n\n function sendInit() {\n const contentWindow = iframeRef.current?.contentWindow;\n if (!contentWindow) return;\n\n const token = client?.getAccessToken?.();\n contentWindow.postMessage(\n {\n type: \"init\",\n token: token ?? undefined,\n appearance: appearance ?? undefined,\n },\n resolvedLinkUrl,\n );\n }\n\n return (\n <div\n data-cimplify-account\n className={cn(\"relative overflow-hidden\", className)}\n >\n {!isReady && (\n <div className=\"flex items-center justify-center py-16\">\n <div className=\"w-6 h-6 border-2 border-border border-t-foreground rounded-full animate-spin\" />\n </div>\n )}\n <iframe\n ref={iframeRef}\n src={iframeSrc}\n style={{\n border: \"none\",\n width: \"100%\",\n height: `${height}px`,\n display: isReady ? \"block\" : \"none\",\n overflow: \"hidden\",\n background: \"transparent\",\n }}\n allow=\"geolocation\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox\"\n />\n </div>\n );\n}\n"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "billing-plan-selector",
|
|
3
|
+
"title": "BillingPlanSelector",
|
|
4
|
+
"description": "Subscription / billing-plan picker — surfaces eligible plans with pricing and trial periods.",
|
|
5
|
+
"type": "component",
|
|
6
|
+
"registryDependencies": [
|
|
7
|
+
"price",
|
|
8
|
+
"cn"
|
|
9
|
+
],
|
|
10
|
+
"files": [
|
|
11
|
+
{
|
|
12
|
+
"path": "billing-plan-selector.tsx",
|
|
13
|
+
"content": "\"use client\";\n\nimport React from \"react\";\nimport type { ProductBillingPlan, BillingFrequency } from \"@cimplify/sdk\";\nimport { useBillingPlans } from \"./hooks/use-billing-plans\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface BillingPlanSelectorClassNames {\n root?: string;\n plan?: string;\n activePlan?: string;\n frequency?: string;\n price?: string;\n setupFee?: string;\n trialBadge?: string;\n empty?: string;\n loading?: string;\n}\n\nexport interface BillingPlanSelectorProps {\n /** Product ID to fetch billing plans for. */\n productId: string;\n /** Override plans (skips fetch). For SSR, pass pre-fetched plans. */\n plans?: ProductBillingPlan[];\n /** Called when a plan is selected. */\n onPlanSelect?: (plan: ProductBillingPlan | null) => void;\n /** Currently selected plan ID. */\n selectedPlanId?: string | null;\n /** Show a \"One-time purchase\" option at the top. */\n showOneTimePurchase?: boolean;\n /** Label for the one-time purchase option. Default: \"One-time purchase\". */\n oneTimePurchaseLabel?: string;\n /** Custom plan card renderer. */\n renderPlan?: (plan: ProductBillingPlan, isActive: boolean) => React.ReactNode;\n className?: string;\n classNames?: BillingPlanSelectorClassNames;\n}\n\nconst FREQUENCY_LABELS: Record<BillingFrequency, string> = {\n weekly: \"Weekly\",\n biweekly: \"Biweekly\",\n monthly: \"Monthly\",\n quarterly: \"Quarterly\",\n annually: \"Annually\",\n};\n\nfunction formatMarkup(plan: ProductBillingPlan): string | null {\n if (!plan.markup_type || plan.markup_amount == null) return null;\n if (plan.markup_type === \"percentage\") return `+${plan.markup_amount}%`;\n return null;\n}\n\n/**\n * BillingPlanSelector — subscription/installment plan comparison and selection.\n *\n * Renders plan cards with frequency, markup, trial, and setup fee details.\n * Returns `null` when no plans are available (and one-time purchase is hidden).\n */\nexport function BillingPlanSelector({\n productId,\n plans: plansProp,\n onPlanSelect,\n selectedPlanId,\n showOneTimePurchase = false,\n oneTimePurchaseLabel = \"One-time purchase\",\n renderPlan,\n className,\n classNames,\n}: BillingPlanSelectorProps): React.ReactElement | null {\n const { plans: fetched, isLoading } = useBillingPlans(productId, {\n enabled: plansProp === undefined,\n });\n\n const plans = plansProp ?? fetched;\n\n if (isLoading && plans.length === 0) {\n return (\n <div\n data-cimplify-billing-plan-selector\n aria-busy=\"true\"\n className={cn(className, classNames?.root, classNames?.loading)}\n />\n );\n }\n\n if (plans.length === 0 && !showOneTimePurchase) {\n return null;\n }\n\n const isOneTimePurchaseSelected = selectedPlanId === null;\n\n return (\n <div data-cimplify-billing-plan-selector className={cn(className, classNames?.root)}>\n {showOneTimePurchase && (\n <button\n type=\"button\"\n onClick={() => onPlanSelect?.(null)}\n data-cimplify-billing-plan\n data-plan-type=\"one-time\"\n data-active={isOneTimePurchaseSelected || undefined}\n aria-pressed={isOneTimePurchaseSelected}\n className={cn(\n classNames?.plan,\n isOneTimePurchaseSelected && classNames?.activePlan,\n )}\n >\n <span data-cimplify-billing-frequency className={classNames?.frequency}>\n {oneTimePurchaseLabel}\n </span>\n </button>\n )}\n\n {plans.map((plan) => {\n const isActive = selectedPlanId === plan.id;\n const markup = formatMarkup(plan);\n\n return (\n <button\n key={plan.id}\n type=\"button\"\n onClick={() => onPlanSelect?.(plan)}\n data-cimplify-billing-plan\n data-plan-type={plan.plan_type}\n data-active={isActive || undefined}\n aria-pressed={isActive}\n className={cn(classNames?.plan, isActive && classNames?.activePlan)}\n >\n {renderPlan ? (\n renderPlan(plan, isActive)\n ) : (\n <>\n <span data-cimplify-billing-frequency className={classNames?.frequency}>\n {FREQUENCY_LABELS[plan.frequency]}\n </span>\n\n {markup && (\n <span data-cimplify-billing-markup className={classNames?.price}>\n {markup}\n </span>\n )}\n\n {plan.markup_type === \"fixed\" && plan.markup_amount != null && (\n <span data-cimplify-billing-markup className={classNames?.price}>\n +<Price amount={plan.markup_amount} />\n </span>\n )}\n\n {plan.trial_days > 0 && (\n <span data-cimplify-billing-trial className={classNames?.trialBadge}>\n {plan.trial_days}-day trial\n </span>\n )}\n\n {plan.setup_fee > 0 && (\n <span data-cimplify-billing-setup-fee className={classNames?.setupFee}>\n Setup fee: <Price amount={plan.setup_fee} />\n </span>\n )}\n\n {plan.plan_type === \"installment\" && plan.installment_periods != null && (\n <span data-cimplify-billing-periods>\n {plan.installment_periods} payments\n </span>\n )}\n </>\n )}\n </button>\n );\n })}\n </div>\n );\n}\n"
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cart-drawer",
|
|
3
|
+
"title": "CartDrawer",
|
|
4
|
+
"description": "Slide-in side cart drawer with provider context, free-shipping progress, animated subtotal, and empty state. Auto-opens on add-to-cart.",
|
|
5
|
+
"type": "component",
|
|
6
|
+
"registryDependencies": [
|
|
7
|
+
"cart-summary",
|
|
8
|
+
"price",
|
|
9
|
+
"cn"
|
|
10
|
+
],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "cart-drawer.tsx",
|
|
14
|
+
"content": "\"use client\";\n\nimport React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport { CartSummary } from \"./cart-summary\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { useCart } from \"@cimplify/sdk/react\";\nimport { parsePrice } from \"@cimplify/sdk\";\nimport { cn } from \"@cimplify/sdk/react\";\n\ninterface CartDrawerCtx {\n isOpen: boolean;\n open: () => void;\n close: () => void;\n toggle: () => void;\n}\n\nconst CartDrawerContext = createContext<CartDrawerCtx | null>(null);\n\nexport function useCartDrawer(): CartDrawerCtx {\n const ctx = useContext(CartDrawerContext);\n if (!ctx) {\n throw new Error(\"useCartDrawer must be used within <CartDrawerProvider>\");\n }\n return ctx;\n}\n\ninterface ProviderProps {\n children: React.ReactNode;\n /** Auto-open the drawer whenever the cart's pendingOpCount goes from 0 → >0. Default: true. */\n openOnAdd?: boolean;\n}\n\nexport function CartDrawerProvider({ children, openOnAdd = true }: ProviderProps): React.ReactElement {\n const [isOpen, setIsOpen] = useState(false);\n const cart = useCart();\n const lastPendingRef = useRef(0);\n\n const open = useCallback(() => setIsOpen(true), []);\n const close = useCallback(() => setIsOpen(false), []);\n const toggle = useCallback(() => setIsOpen((v) => !v), []);\n\n useEffect(() => {\n if (!openOnAdd) return;\n if (cart.pendingOpCount > lastPendingRef.current && cart.pendingOpCount > 0) {\n setIsOpen(true);\n }\n lastPendingRef.current = cart.pendingOpCount;\n }, [cart.pendingOpCount, openOnAdd]);\n\n const value = useMemo<CartDrawerCtx>(() => ({ isOpen, open, close, toggle }), [isOpen, open, close, toggle]);\n return <CartDrawerContext.Provider value={value}>{children}</CartDrawerContext.Provider>;\n}\n\nexport interface CartDrawerProps {\n /** Called when \"Checkout\" is clicked. Drawer auto-closes first. */\n onCheckout?: () => void;\n /** Called when \"Continue Shopping\" is clicked. Defaults to closing. */\n onContinueShopping?: () => void;\n /** Called when the empty state's CTA is clicked. */\n onShop?: () => void;\n /** Heading. */\n title?: string;\n /** Free-shipping threshold (in business currency, numeric). 0 disables the progress bar. */\n freeShippingThreshold?: number;\n /** Custom class on the panel. */\n className?: string;\n}\n\n/**\n * Animate a number toward `target` over ~250ms. Used for subtotal so it\n * feels alive when items are added/removed.\n */\nfunction useAnimatedNumber(target: number, durationMs = 250) {\n const [value, setValue] = useState(target);\n const fromRef = useRef(target);\n const startRef = useRef<number | null>(null);\n useEffect(() => {\n fromRef.current = value;\n startRef.current = null;\n let raf = 0;\n const tick = (t: number) => {\n if (startRef.current == null) startRef.current = t;\n const p = Math.min(1, (t - startRef.current) / durationMs);\n const eased = 1 - Math.pow(1 - p, 3);\n setValue(fromRef.current + (target - fromRef.current) * eased);\n if (p < 1) raf = requestAnimationFrame(tick);\n };\n raf = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(raf);\n // value intentionally omitted — only re-trigger on target change\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [target, durationMs]);\n return value;\n}\n\nexport function CartDrawer({\n onCheckout,\n onContinueShopping,\n onShop,\n title = \"Cart\",\n freeShippingThreshold = 0,\n className,\n}: CartDrawerProps): React.ReactElement {\n const { isOpen, close } = useCartDrawer();\n const cart = useCart();\n const subtotalNum = parsePrice(cart.subtotal);\n const animatedSubtotal = useAnimatedNumber(subtotalNum);\n\n // Lock body scroll + close on Escape while open.\n useEffect(() => {\n if (!isOpen) return;\n const original = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") close();\n };\n window.addEventListener(\"keydown\", onKey);\n return () => {\n document.body.style.overflow = original;\n window.removeEventListener(\"keydown\", onKey);\n };\n }, [isOpen, close]);\n\n const handleCheckout = () => {\n close();\n onCheckout?.();\n };\n\n const handleContinue = () => {\n onContinueShopping?.();\n close();\n };\n\n const handleShop = () => {\n onShop?.();\n close();\n };\n\n // Free-shipping progress\n const showShippingBar = freeShippingThreshold > 0 && !cart.isEmpty;\n const remainingForShipping = Math.max(0, freeShippingThreshold - subtotalNum);\n const shippingProgress = freeShippingThreshold > 0 ? Math.min(100, (subtotalNum / freeShippingThreshold) * 100) : 0;\n const shippingUnlocked = subtotalNum >= freeShippingThreshold && freeShippingThreshold > 0;\n\n return (\n <div\n data-cimplify-cart-drawer\n data-open={isOpen ? \"true\" : \"false\"}\n aria-hidden={!isOpen}\n className={cn(\n \"fixed inset-0 z-[200]\",\n isOpen ? \"pointer-events-auto\" : \"pointer-events-none\",\n )}\n >\n {/* Backdrop */}\n <div\n onClick={close}\n className={cn(\n \"absolute inset-0 bg-foreground/40 backdrop-blur-sm transition-opacity duration-300\",\n isOpen ? \"opacity-100\" : \"opacity-0\",\n )}\n />\n\n {/* Panel */}\n <aside\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n className={cn(\n \"absolute top-0 right-0 h-full w-full sm:max-w-[480px] bg-background shadow-2xl flex flex-col\",\n \"transition-transform duration-300\",\n isOpen ? \"translate-x-0\" : \"translate-x-full\",\n // ease-out cubic-bezier for a \"pull\" feel\n \"[transition-timing-function:cubic-bezier(0.16,1,0.3,1)]\",\n className,\n )}\n >\n {/* Header */}\n <header className=\"relative flex items-center justify-between gap-4 px-6 py-5 shrink-0\">\n <div className=\"flex items-baseline gap-2\">\n <h2 className=\"text-xl font-bold tracking-tight m-0\">{title}</h2>\n {cart.itemCount > 0 && (\n <span className=\"text-sm text-muted-foreground tabular-nums\">\n {cart.itemCount} {cart.itemCount === 1 ? \"item\" : \"items\"}\n </span>\n )}\n </div>\n <button\n type=\"button\"\n onClick={close}\n aria-label=\"Close cart\"\n className=\"grid place-items-center w-9 h-9 rounded-full hover:bg-muted text-muted-foreground hover:text-foreground transition-colors\"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\" aria-hidden>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </header>\n\n {/* Free-shipping bar */}\n {showShippingBar && (\n <div data-cimplify-cart-shipping-bar className=\"px-6 pb-4 shrink-0\">\n <div className=\"flex items-baseline justify-between gap-2 mb-1.5 text-xs\">\n {shippingUnlocked ? (\n <span className=\"font-medium text-primary\">✓ Free shipping unlocked</span>\n ) : (\n <span className=\"text-muted-foreground\">\n <Price amount={remainingForShipping} className=\"font-semibold text-foreground\" /> away from free shipping\n </span>\n )}\n </div>\n <div className=\"h-1 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full bg-primary transition-[width] duration-500 ease-out\"\n style={{ width: `${shippingProgress}%` }}\n />\n </div>\n </div>\n )}\n\n {/* Items */}\n <div className=\"flex-1 overflow-y-auto px-6\">\n {cart.isEmpty ? (\n <EmptyState onShop={handleShop} />\n ) : (\n <CartSummary showTotals={false} showCheckoutButton={false} />\n )}\n </div>\n\n {/* Footer */}\n {!cart.isEmpty && (\n <footer className=\"border-t border-border px-6 py-5 shrink-0 space-y-3 bg-background\">\n <div className=\"flex items-baseline justify-between\">\n <span className=\"text-sm text-muted-foreground\">Subtotal</span>\n <Price amount={animatedSubtotal} className=\"text-lg font-bold tabular-nums\" />\n </div>\n <p className=\"text-[11px] text-muted-foreground\">\n Tax and shipping calculated at checkout.\n </p>\n <button\n type=\"button\"\n onClick={handleCheckout}\n className=\"w-full h-12 rounded-full bg-foreground text-background font-semibold text-sm hover:bg-foreground/90 active:scale-[0.99] transition-all\"\n >\n Checkout — <Price amount={cart.subtotal} className=\"tabular-nums\" />\n </button>\n <button\n type=\"button\"\n onClick={handleContinue}\n className=\"w-full text-xs font-medium text-muted-foreground hover:text-foreground transition-colors\"\n >\n Continue shopping\n </button>\n <div className=\"flex items-center justify-center gap-1.5 text-[10px] text-muted-foreground/70 uppercase tracking-wider pt-1\">\n <svg className=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\" aria-hidden>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\" />\n </svg>\n <span>Secure checkout</span>\n </div>\n </footer>\n )}\n </aside>\n </div>\n );\n}\n\nfunction EmptyState({ onShop }: { onShop: () => void }) {\n return (\n <div data-cimplify-cart-empty className=\"h-full grid place-items-center text-center py-16\">\n <div className=\"space-y-5 max-w-[260px]\">\n <div className=\"relative w-20 h-20 mx-auto\">\n <div className=\"absolute inset-0 rounded-full bg-muted\" />\n <svg\n className=\"relative w-10 h-10 m-auto top-1/2 -translate-y-1/2 text-muted-foreground/60\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n viewBox=\"0 0 24 24\"\n aria-hidden\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M16 11V7a4 4 0 00-8 0v4M5 9h14l-1 12H6L5 9z\" />\n </svg>\n </div>\n <div>\n <p className=\"text-base font-semibold m-0\">Your cart is empty</p>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Discover something you'll love.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onShop}\n className=\"inline-flex items-center gap-1.5 h-11 px-6 rounded-full bg-foreground text-background text-sm font-semibold hover:bg-foreground/90 active:scale-[0.99] transition-all\"\n >\n Shop now\n <svg className=\"w-3.5 h-3.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" viewBox=\"0 0 24 24\" aria-hidden>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M14 5l7 7m0 0l-7 7m7-7H3\" />\n </svg>\n </button>\n </div>\n </div>\n );\n}\n"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "currency-selector",
|
|
3
|
+
"title": "CurrencySelector",
|
|
4
|
+
"description": "Multi-currency switcher backed by the FX provider — locks display currency and quote ID.",
|
|
5
|
+
"type": "component",
|
|
6
|
+
"registryDependencies": [
|
|
7
|
+
"cn"
|
|
8
|
+
],
|
|
9
|
+
"files": [
|
|
10
|
+
{
|
|
11
|
+
"path": "currency-selector.tsx",
|
|
12
|
+
"content": "\"use client\";\n\nimport React, { useState, useEffect, useRef } from \"react\";\nimport type { CurrencyCode } from \"@cimplify/sdk\";\nimport type { CimplifyError } from \"@cimplify/sdk\";\nimport type { FxRateResponse } from \"../types/fx\";\nimport { useOptionalCimplifyClient } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface CurrencySelectorClassNames {\n root?: string;\n option?: string;\n activeOption?: string;\n rate?: string;\n label?: string;\n loading?: string;\n}\n\nexport interface CurrencySelectorProps {\n /** Available currencies to display. */\n currencies: CurrencyCode[];\n /** Currently selected currency. */\n currentCurrency?: CurrencyCode;\n /** Called when a currency is selected. */\n onCurrencyChange?: (currency: CurrencyCode) => void;\n /** Show the exchange rate next to each currency. Requires `baseCurrency`. */\n showRate?: boolean;\n /** Base currency used for rate display. */\n baseCurrency?: CurrencyCode;\n /** Custom currency option renderer. */\n renderCurrency?: (\n currency: CurrencyCode,\n isActive: boolean,\n rate: FxRateResponse | null,\n ) => React.ReactNode;\n className?: string;\n classNames?: CurrencySelectorClassNames;\n}\n\n/**\n * CurrencySelector — currency switcher for international customers.\n *\n * Renders selectable currency options with optional exchange rate display.\n * Returns `null` when there is only one currency (no selector needed).\n */\nexport function CurrencySelector({\n currencies,\n currentCurrency,\n onCurrencyChange,\n showRate = false,\n baseCurrency,\n renderCurrency,\n className,\n classNames,\n}: CurrencySelectorProps): React.ReactElement | null {\n const context = useOptionalCimplifyClient();\n const client = context?.client ?? null;\n\n const [rates, setRates] = useState<Record<string, FxRateResponse>>({});\n const [isLoadingRates, setIsLoadingRates] = useState(false);\n const requestIdRef = useRef(0);\n\n useEffect(() => {\n if (!showRate || !baseCurrency || !client) return;\n\n const targets = currencies.filter((c) => c !== baseCurrency);\n if (targets.length === 0) return;\n\n const nextRequestId = ++requestIdRef.current;\n setIsLoadingRates(true);\n\n void (async () => {\n const fetched: Record<string, FxRateResponse> = {};\n\n await Promise.all(\n targets.map(async (currency) => {\n const res = await client.fx.getRate(baseCurrency, currency);\n if (res.ok) {\n fetched[currency] = res.value;\n }\n }),\n );\n\n if (nextRequestId !== requestIdRef.current) return;\n\n setRates(fetched);\n setIsLoadingRates(false);\n })();\n }, [client, showRate, baseCurrency, currencies]);\n\n if (currencies.length <= 1) {\n return null;\n }\n\n return (\n <div data-cimplify-currency-selector className={cn(className, classNames?.root)}>\n {currencies.map((currency) => {\n const isActive = currentCurrency === currency;\n const rate = rates[currency] ?? null;\n\n return (\n <button\n key={currency}\n type=\"button\"\n onClick={() => onCurrencyChange?.(currency)}\n data-cimplify-currency-option\n data-active={isActive || undefined}\n aria-pressed={isActive}\n className={cn(\n classNames?.option,\n isActive && classNames?.activeOption,\n )}\n >\n {renderCurrency ? (\n renderCurrency(currency, isActive, rate)\n ) : (\n <>\n <span data-cimplify-currency-label className={classNames?.label}>\n {currency}\n </span>\n {showRate && baseCurrency && currency !== baseCurrency && (\n <span data-cimplify-currency-rate className={classNames?.rate}>\n {isLoadingRates\n ? \"...\"\n : rate\n ? `1 ${baseCurrency} = ${rate.rate.toFixed(4)} ${currency}`\n : null}\n </span>\n )}\n </>\n )}\n </button>\n );\n })}\n </div>\n );\n}\n"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|