@moonbase.sh/storefront 2.7.0 → 2.9.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.
package/README.md CHANGED
@@ -99,6 +99,10 @@ Moonbase.configure({
99
99
  cart: {
100
100
  quantity: 'single',
101
101
  },
102
+ pricing: {
103
+ trailingZeros: 'always',
104
+ currencyDisplay: 'code',
105
+ },
102
106
  theme: {
103
107
  colors: {
104
108
  primary: '#E5A000',
@@ -112,8 +116,92 @@ Moonbase.configure({
112
116
  })
113
117
  ```
114
118
 
119
+ `pricing.trailingZeros` controls how whole prices render. The default `'auto'`
120
+ drops the decimals — a $10.00 product shows as `$10`. Set it to `'always'` to keep
121
+ the currency's own fraction digits instead (`$10.00`, and still `¥1000` for
122
+ zero-decimal currencies like JPY). It applies to the widget's own UI and to
123
+ `data-moonbase-render` price elements on your page alike.
124
+
125
+ `pricing.currencyDisplay` picks which marker identifies the currency. Taking $10 in
126
+ USD read from a British locale, the default `'narrowSymbol'` renders the shortest
127
+ symbol (`$10`), `'symbol'` the locale's usual one (`US$10`), `'code'` the ISO 4217
128
+ code (`USD 10`), and `'name'` spells it out in the visitor's language
129
+ (`10 US dollars`). The symbol and its placement still follow the visitor's own
130
+ locale — this only chooses between the four forms.
131
+
132
+ Both `pricing` options cover the widget's UI and your `data-moonbase-render` price
133
+ elements. The checkout itself is rendered by Moonbase and formats its own prices.
134
+
135
+ `cart.offers.cartWide` covers offers keyed off the cart as a whole, such as "10% off orders
136
+ over $50", rather than off a single product. These apply themselves the moment the
137
+ cart qualifies, so there is nothing for the buyer to press. While the cart is short
138
+ of the threshold the widget shows a meter and how much further to go; once it
139
+ qualifies, the saving appears as its own row above the total.
140
+
141
+ ```ts
142
+ Moonbase.configure({
143
+ cart: {
144
+ offers: {
145
+ cartWide: {
146
+ showProgress: true,
147
+ savingLabel: 'You save',
148
+ },
149
+ },
150
+ },
151
+ })
152
+ ```
153
+
154
+ Set `showProgress` to `false` to let such an offer apply silently: the saving still
155
+ shows in the totals, the nudge does not. With no cart-wide offer configured on your
156
+ storefront, the meter never renders either way. `savingLabel` names the saving row
157
+ in the cart totals.
158
+
115
159
  You can also control where the widget mounts by providing `target`.
116
160
 
161
+ ## Promotions
162
+
163
+ When you run a sale across your catalogue, the widget surfaces it on its own — as a
164
+ banner, a popup, or both, depending on what you asked for when you set the promotion
165
+ up in Moonbase. Nothing to wire in: the sale appears while it is live and to the
166
+ shoppers it applies to, and disappears when it ends.
167
+
168
+ Once a shopper dismisses a promotion it stays dismissed in that browser, for as long
169
+ as the promotion runs. Editing the campaign doesn't bring it back — a shopper who has
170
+ turned a sale away isn't asked again. Start a new promotion to reach them again.
171
+
172
+ The two surfaces are tracked separately, so closing the popup leaves the banner up as
173
+ a reminder that the sale is still on. A popup also gets a single interaction: clicking
174
+ through to the campaign retires it for good, the same as closing it would, since it
175
+ interrupted the shopper to ask. A banner interrupted nobody, so clicking through leaves
176
+ it standing — only an explicit dismissal takes it down.
177
+
178
+ By default the banner pins itself to the top of the viewport. Give the widget a
179
+ container and it renders inline in your own layout instead — anywhere on the page:
180
+
181
+ ```html
182
+ <div data-moonbase-promotion></div>
183
+ ```
184
+
185
+ ```ts
186
+ Moonbase.configure({
187
+ promotions: {
188
+ // Set to false to render sales yourself.
189
+ enabled: true,
190
+ // 'top' | 'bottom' — only used when the page offers no container.
191
+ location: 'top',
192
+ dismissLabel: 'Dismiss',
193
+ },
194
+ })
195
+ ```
196
+
197
+ Where a sale leads, and the wording on the link, both come from the promotion itself —
198
+ only the merchant running the campaign knows what it is offering. A sale with no
199
+ destination set shows the dismiss action as its only button.
200
+
201
+ To render sales entirely yourself, turn the built-in surfaces off and listen for
202
+ `promotion-shown`, or read them from the `usePromotions()` composable in
203
+ `@moonbase.sh/vue`.
204
+
117
205
  ## Events
118
206
 
119
207
  Subscribe to widget lifecycle events with `Moonbase.on(...)`.
@@ -124,6 +212,9 @@ Available events include:
124
212
  - `signed-up`
125
213
  - `signed-out`
126
214
  - `storefront-updated`
215
+ - `promotion-shown`
216
+ - `promotion-clicked`
217
+ - `promotion-dismissed`
127
218
  - `redeemed-voucher`
128
219
  - `downloaded-product`
129
220
  - `activated-product`
@@ -9,9 +9,19 @@ import { Order } from '@moonbase.sh/vue';
9
9
  import { OwnedProduct } from '@moonbase.sh/vue';
10
10
  import { Storefront } from '@moonbase.sh/vue';
11
11
  import { StorefrontProduct } from '@moonbase.sh/vue';
12
+ import { StorefrontPromotion } from '@moonbase.sh/vue';
12
13
  import { User } from '@moonbase.sh/vue';
13
14
  import { Voucher } from '@moonbase.sh/vue';
14
15
 
16
+ /**
17
+ * Which marker identifies the currency, for `10` USD read in a British locale:
18
+ * - `'narrowSymbol'` (default) — the shortest symbol: `$10`.
19
+ * - `'symbol'` — the locale's usual symbol, disambiguated where it needs to be: `US$10`.
20
+ * - `'code'` — the ISO 4217 code: `USD 10`.
21
+ * - `'name'` — the currency spelled out in the visitor's language: `10 US dollars`.
22
+ */
23
+ declare type CurrencyDisplay = 'narrowSymbol' | 'symbol' | 'code' | 'name';
24
+
15
25
  declare type DeepPartial<T> = T extends object ? {
16
26
  [P in keyof T]?: T[P] extends HTMLElement | undefined ? T[P] : DeepPartial<T[P]>;
17
27
  } : T;
@@ -57,6 +67,9 @@ export declare enum MoonbaseEvent {
57
67
  SignedOut = "signed-out",
58
68
  RedeemedVoucher = "redeemed-voucher",
59
69
  StorefrontUpdated = "storefront-updated",
70
+ PromotionShown = "promotion-shown",
71
+ PromotionClicked = "promotion-clicked",
72
+ PromotionDismissed = "promotion-dismissed",
60
73
  DownloadedProduct = "downloaded-product",
61
74
  ActivatedProduct = "activated-product",
62
75
  AddedToCart = "added-to-cart",
@@ -84,6 +97,23 @@ export declare interface MoonbaseEventArgs {
84
97
  storefront: Storefront;
85
98
  user?: User | null;
86
99
  };
100
+ [MoonbaseEvent.PromotionShown]: {
101
+ promotion: StorefrontPromotion;
102
+ surface: PromotionSurface;
103
+ user?: User | null;
104
+ };
105
+ [MoonbaseEvent.PromotionClicked]: {
106
+ promotion: StorefrontPromotion;
107
+ surface: PromotionSurface;
108
+ /** Where the shopper is being sent — the promotion's own call-to-action URL. */
109
+ url: string;
110
+ user?: User | null;
111
+ };
112
+ [MoonbaseEvent.PromotionDismissed]: {
113
+ promotion: StorefrontPromotion;
114
+ surface: PromotionSurface;
115
+ user?: User | null;
116
+ };
87
117
  [MoonbaseEvent.DownloadedProduct]: {
88
118
  product: OwnedProduct;
89
119
  download: Download;
@@ -326,6 +356,27 @@ export declare interface MoonbaseOptions {
326
356
  };
327
357
  offers: {
328
358
  label: string;
359
+ /**
360
+ * Offers keyed off the cart as a whole, such as "10% off orders over $50", rather than off a single
361
+ * product. They apply themselves the moment the cart qualifies; there is nothing for the buyer
362
+ * to press.
363
+ */
364
+ cartWide: {
365
+ /**
366
+ * Show the eligibility meter in the cart: how close the cart is to the offer's threshold
367
+ * while it is short, and the discount confirmed once it is not.
368
+ *
369
+ * Set to `false` to let the offer apply silently: the saving still shows in the totals, the
370
+ * nudge does not. With no such offer configured the meter never renders either way.
371
+ */
372
+ showProgress: boolean;
373
+ /**
374
+ * Label for the saving row in the cart totals, above `Total`. Kept in the widget's own voice
375
+ * rather than taking the offer's name, so the totals column can't be pushed around by
376
+ * merchant-length copy.
377
+ */
378
+ savingLabel: string;
379
+ };
329
380
  };
330
381
  leadMagnets: {
331
382
  quantity: 'selectable' | 'single';
@@ -339,6 +390,26 @@ export declare interface MoonbaseOptions {
339
390
  label: string;
340
391
  };
341
392
  };
393
+ promotions: {
394
+ /**
395
+ * Whether to render the sales a merchant is running. When a promotion is live, the widget
396
+ * surfaces it as a banner and/or a popup depending on what the merchant asked for. Turn this
397
+ * off to render the campaign yourself from `Moonbase.on('promotion-shown', …)` or the
398
+ * `usePromotions()` composable in `@moonbase.sh/vue`.
399
+ */
400
+ enabled: boolean;
401
+ /**
402
+ * Where the banner sits when the host page has no `[data-moonbase-promotion]` container for
403
+ * it. Ignored when one exists — then the banner renders inline, wherever you put it.
404
+ */
405
+ location: 'top' | 'bottom';
406
+ /**
407
+ * The label on the dismiss action, and the primary action when a sale has no destination. The
408
+ * wording for a sale that *does* lead somewhere comes from the promotion itself, since only
409
+ * the merchant knows what the campaign is offering.
410
+ */
411
+ dismissLabel: string;
412
+ };
342
413
  theme: {
343
414
  dark: boolean;
344
415
  colors: {
@@ -353,6 +424,29 @@ export declare interface MoonbaseOptions {
353
424
  buttons: 'outlined' | 'light';
354
425
  cards: 'outlined' | 'shadow' | 'white';
355
426
  };
427
+ pricing: {
428
+ /**
429
+ * How to render a price that lands on a whole number.
430
+ * - `'auto'` (default) drops the fraction entirely, so `10` renders `$10`.
431
+ * - `'always'` keeps the currency's own fraction digits, so `10` renders
432
+ * `$10.00` — and still `¥1000` for zero-decimal currencies like JPY.
433
+ *
434
+ * Prices that aren't whole are unaffected; they always show their decimals.
435
+ */
436
+ trailingZeros: TrailingZeros;
437
+ /**
438
+ * Which marker identifies the currency. Taking `10` USD read in a British
439
+ * locale:
440
+ * - `'narrowSymbol'` (default) renders the shortest symbol, `$10`.
441
+ * - `'symbol'` renders the locale's usual symbol, `US$10`.
442
+ * - `'code'` renders the ISO 4217 code, `USD 10`.
443
+ * - `'name'` spells it out in the visitor's language, `10 US dollars`.
444
+ *
445
+ * The symbol and its placement always follow the visitor's own locale;
446
+ * this only picks which of the four forms to use.
447
+ */
448
+ currencyDisplay: CurrencyDisplay;
449
+ };
356
450
  target: HTMLElement | undefined;
357
451
  /**
358
452
  * When the host page declares a fixed-width mobile viewport (e.g. Wix's
@@ -373,10 +467,19 @@ export declare interface MoonbaseOptions {
373
467
  integrations: IntegrationsConfig;
374
468
  }
375
469
 
470
+ /** Which surface a promotion was rendered on. */
471
+ export declare type PromotionSurface = 'banner' | 'popup';
472
+
376
473
  export declare interface TikTokPixelConfig {
377
474
  pixelId: string;
378
475
  }
379
476
 
477
+ /**
478
+ * How a whole-number price renders: `'auto'` drops the fraction (`$10`),
479
+ * `'always'` keeps the currency's own fraction digits (`$10.00`, `¥1000`).
480
+ */
481
+ declare type TrailingZeros = 'auto' | 'always';
482
+
380
483
  export declare const urlKey: InjectionKey<string>;
381
484
 
382
485
  export { }