@moonbase.sh/storefront 2.8.0 → 2.9.1

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
@@ -132,8 +132,76 @@ locale — this only chooses between the four forms.
132
132
  Both `pricing` options cover the widget's UI and your `data-moonbase-render` price
133
133
  elements. The checkout itself is rendered by Moonbase and formats its own prices.
134
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
+
135
159
  You can also control where the widget mounts by providing `target`.
136
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
+
137
205
  ## Events
138
206
 
139
207
  Subscribe to widget lifecycle events with `Moonbase.on(...)`.
@@ -144,6 +212,9 @@ Available events include:
144
212
  - `signed-up`
145
213
  - `signed-out`
146
214
  - `storefront-updated`
215
+ - `promotion-shown`
216
+ - `promotion-clicked`
217
+ - `promotion-dismissed`
147
218
  - `redeemed-voucher`
148
219
  - `downloaded-product`
149
220
  - `activated-product`
@@ -9,6 +9,7 @@ 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
 
@@ -66,6 +67,9 @@ export declare enum MoonbaseEvent {
66
67
  SignedOut = "signed-out",
67
68
  RedeemedVoucher = "redeemed-voucher",
68
69
  StorefrontUpdated = "storefront-updated",
70
+ PromotionShown = "promotion-shown",
71
+ PromotionClicked = "promotion-clicked",
72
+ PromotionDismissed = "promotion-dismissed",
69
73
  DownloadedProduct = "downloaded-product",
70
74
  ActivatedProduct = "activated-product",
71
75
  AddedToCart = "added-to-cart",
@@ -93,6 +97,23 @@ export declare interface MoonbaseEventArgs {
93
97
  storefront: Storefront;
94
98
  user?: User | null;
95
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
+ };
96
117
  [MoonbaseEvent.DownloadedProduct]: {
97
118
  product: OwnedProduct;
98
119
  download: Download;
@@ -335,6 +356,27 @@ export declare interface MoonbaseOptions {
335
356
  };
336
357
  offers: {
337
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
+ };
338
380
  };
339
381
  leadMagnets: {
340
382
  quantity: 'selectable' | 'single';
@@ -348,6 +390,26 @@ export declare interface MoonbaseOptions {
348
390
  label: string;
349
391
  };
350
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
+ };
351
413
  theme: {
352
414
  dark: boolean;
353
415
  colors: {
@@ -405,6 +467,9 @@ export declare interface MoonbaseOptions {
405
467
  integrations: IntegrationsConfig;
406
468
  }
407
469
 
470
+ /** Which surface a promotion was rendered on. */
471
+ export declare type PromotionSurface = 'banner' | 'popup';
472
+
408
473
  export declare interface TikTokPixelConfig {
409
474
  pixelId: string;
410
475
  }