@encorekit/web-sdk 2.0.7 → 2.1.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 +6 -0
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/umd/encore.min.js +1 -1
- package/dist/umd/encore.min.js.map +1 -1
- package/package.json +1 -1
- package/types/api/APIClient.d.ts +18 -0
- package/types/api/APIClient.d.ts.map +1 -1
- package/types/api/generated/types.d.ts +177 -2
- package/types/api/generated/types.d.ts.map +1 -1
- package/types/api/models.d.ts +32 -0
- package/types/api/models.d.ts.map +1 -1
- package/types/core/Encore.d.ts.map +1 -1
- package/types/core/giftVariant.d.ts +67 -0
- package/types/core/giftVariant.d.ts.map +1 -0
- package/types/core/version.d.ts +1 -1
- package/types/types.d.ts +31 -3
- package/types/types.d.ts.map +1 -1
- package/types/ui/GiftHero.d.ts +75 -0
- package/types/ui/GiftHero.d.ts.map +1 -0
- package/types/ui/GiftList.d.ts +110 -0
- package/types/ui/GiftList.d.ts.map +1 -0
- package/types/ui/GiftReveal.d.ts +133 -0
- package/types/ui/GiftReveal.d.ts.map +1 -0
- package/types/ui/GiftSheet.d.ts +126 -0
- package/types/ui/GiftSheet.d.ts.map +1 -0
- package/types/ui/GiftUnwrap.d.ts +155 -0
- package/types/ui/GiftUnwrap.d.ts.map +1 -0
- package/types/ui/OfferCarousel.d.ts +10 -3
- package/types/ui/OfferCarousel.d.ts.map +1 -1
- package/types/ui/OfferModal.d.ts +7 -0
- package/types/ui/OfferModal.d.ts.map +1 -1
- package/types/ui/RewardActions.d.ts +88 -4
- package/types/ui/RewardActions.d.ts.map +1 -1
- package/types/ui/RewardCard.d.ts +16 -8
- package/types/ui/RewardCard.d.ts.map +1 -1
- package/types/ui/RewardConfetti.d.ts +23 -1
- package/types/ui/RewardConfetti.d.ts.map +1 -1
- package/types/ui/RewardLayout.d.ts +35 -0
- package/types/ui/RewardLayout.d.ts.map +1 -1
- package/types/ui/advertiserPreconnect.d.ts +90 -0
- package/types/ui/advertiserPreconnect.d.ts.map +1 -0
- package/types/ui/appearance.d.ts +27 -0
- package/types/ui/appearance.d.ts.map +1 -1
- package/types/ui/focusable.d.ts +41 -0
- package/types/ui/focusable.d.ts.map +1 -0
- package/types/ui/giftCategoryGroups.d.ts +21 -0
- package/types/ui/giftCategoryGroups.d.ts.map +1 -0
- package/types/ui/giftCopy.d.ts +215 -0
- package/types/ui/giftCopy.d.ts.map +1 -0
- package/types/ui/giftUnwrapArt.d.ts +55 -0
- package/types/ui/giftUnwrapArt.d.ts.map +1 -0
- package/types/ui/giftUnwrapCopy.d.ts +57 -0
- package/types/ui/giftUnwrapCopy.d.ts.map +1 -0
- package/types/ui/impressionGate.d.ts +63 -0
- package/types/ui/impressionGate.d.ts.map +1 -0
- package/types/ui/offerBadge.d.ts +56 -0
- package/types/ui/offerBadge.d.ts.map +1 -0
- package/types/ui/rewardSurface.d.ts +82 -0
- package/types/ui/rewardSurface.d.ts.map +1 -0
- package/types/ui/safeImageSrc.d.ts +10 -0
- package/types/ui/safeImageSrc.d.ts.map +1 -1
- package/types/ui/styles.d.ts +166 -0
- package/types/ui/styles.d.ts.map +1 -1
- package/types/ui/termsLine.d.ts +33 -0
- package/types/ui/termsLine.d.ts.map +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appearance.d.ts","sourceRoot":"","sources":["../../src/ui/appearance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAgBxC,wFAAwF;AACxF,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,cAAc,EAAE,WAAW,CAAC;CAC7B;AAED,8EAA8E;AAC9E,MAAM,MAAM,kBAAkB,GAAG,CAC/B,OAAO,EAAE,qBAAqB,KAC3B,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC;AAEpC,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC7B,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;CAC3D;AAED,qFAAqF;AACrF,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qCAAqC;AACrC,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,WAAW,GAAG,kBAAkB,CAAC;AAEzE,mFAAmF;AACnF,MAAM,MAAM,UAAU,GAAG,gBAAgB,GAAG,WAAW,GAAG,kBAAkB,CAAC;AAwF7E,yFAAyF;AACzF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,CAO9E;AAED,8EAA8E;AAC9E,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,gBAAgB,GAAG,IAAI,CASlF;AAmBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,QAAQ,GAAG,SAAS,GACzB,WAAW,CAoBb;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAE/E;AAMD,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,KAAK,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AAED,qFAAqF;AACrF,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,WAAW,CAAC;IACrB,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACtD;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"appearance.d.ts","sourceRoot":"","sources":["../../src/ui/appearance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAgBxC,wFAAwF;AACxF,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,cAAc,EAAE,WAAW,CAAC;CAC7B;AAED,8EAA8E;AAC9E,MAAM,MAAM,kBAAkB,GAAG,CAC/B,OAAO,EAAE,qBAAqB,KAC3B,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC;AAEpC,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC7B,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;CAC3D;AAED,qFAAqF;AACrF,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qCAAqC;AACrC,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,WAAW,GAAG,kBAAkB,CAAC;AAEzE,mFAAmF;AACnF,MAAM,MAAM,UAAU,GAAG,gBAAgB,GAAG,WAAW,GAAG,kBAAkB,CAAC;AAwF7E,yFAAyF;AACzF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,CAO9E;AAED,8EAA8E;AAC9E,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,gBAAgB,GAAG,IAAI,CASlF;AAmBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,QAAQ,GAAG,SAAS,GACzB,WAAW,CAoBb;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAE/E;AAMD,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,KAAK,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AAED,qFAAqF;AACrF,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,WAAW,CAAC;IACrB,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACtD;;;;;;;;;;;;;;OAcG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC;;;;;;;;;OASG;IACH,QAAQ,EAAE,MAAM,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAyKvE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What counts as a tab stop, for every focus trap in the SDK.
|
|
3
|
+
*
|
|
4
|
+
* There are two traps — `OfferModal`'s, and `GiftSheet`'s for while the sheet is open —
|
|
5
|
+
* and the sheet's swallows Tab outright (`preventDefault` plus `stopPropagation`), so
|
|
6
|
+
* the modal never sees the key and the two lists are never reconciled at runtime. A
|
|
7
|
+
* control the sheet's list omits is therefore not merely visited late, it is
|
|
8
|
+
* unreachable: the "offer terms" `<a>` `setTermsLink` renders in the sheet's footer was
|
|
9
|
+
* exactly that. One definition, used by both, is what keeps them from drifting again.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* `:not([tabindex="-1"])` on EVERY branch, not just the generic one. An element
|
|
13
|
+
* explicitly removed from the tab order is not a tab stop, and a trap must agree —
|
|
14
|
+
* otherwise a publisher's custom CTA, which `createActionSlot` neutralizes by setting
|
|
15
|
+
* `tabindex="-1"` on it and its focusable descendants, would still be matched by
|
|
16
|
+
* `button:not([disabled])` and Tab would visit the same control twice, the second time
|
|
17
|
+
* landing on a `pointer-events: none` element that does nothing.
|
|
18
|
+
*/
|
|
19
|
+
export declare const FOCUSABLE_SELECTOR: string;
|
|
20
|
+
/**
|
|
21
|
+
* The tab stops inside `root`, in document order, minus anything inside a hidden
|
|
22
|
+
* subtree.
|
|
23
|
+
*
|
|
24
|
+
* A composition may mount a panel that is present from the start and only revealed
|
|
25
|
+
* later (the gift flow's "other gifts" sheet). Its controls are real tab stops the
|
|
26
|
+
* moment it opens and must not be tab stops while it is closed, and Tab is fully
|
|
27
|
+
* trapped, so a list that ignored visibility would land Tab on a control the user
|
|
28
|
+
* cannot see or act on.
|
|
29
|
+
*
|
|
30
|
+
* `aria-hidden` as well as `hidden`, because a panel can be removed from the
|
|
31
|
+
* accessibility tree BEFORE it is removed from layout — the gift sheet marks itself
|
|
32
|
+
* `aria-hidden` the moment it starts sliding away, and stays in the DOM and
|
|
33
|
+
* un-`hidden` for the ~300ms that takes. A trap that only understood `hidden` would
|
|
34
|
+
* hand focus to a control the user is watching leave.
|
|
35
|
+
*
|
|
36
|
+
* Nothing here depends on layout — `offsetParent` would be the obvious visibility test
|
|
37
|
+
* and is uniformly null in a jsdom-like environment, which would empty the trap
|
|
38
|
+
* entirely.
|
|
39
|
+
*/
|
|
40
|
+
export declare function tabbableWithin(root: ParentNode): HTMLElement[];
|
|
41
|
+
//# sourceMappingURL=focusable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focusable.d.ts","sourceRoot":"","sources":["../../src/ui/focusable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,QAOnB,CAAC;AAEb;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,EAAE,CAI9D"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The gift sheet's tab groups, and the map from the backend's campaign category
|
|
3
|
+
* taxonomy onto them.
|
|
4
|
+
*
|
|
5
|
+
* The backend serves one of 18 taxonomy names per campaign (V167). A tab per name
|
|
6
|
+
* would put five long tabs on a nine-offer sheet, three of them holding one offer, so
|
|
7
|
+
* the sheet shows the design's short groups instead. The map is the ONLY place a
|
|
8
|
+
* taxonomy name is interpreted: a name it does not know (a renamed or newly added
|
|
9
|
+
* entry) goes to Everyday and is logged once, so a taxonomy change never hides an
|
|
10
|
+
* offer and never fails silently for a developer with debug logging on.
|
|
11
|
+
*/
|
|
12
|
+
/** The tabs, in the order the sheet shows them. */
|
|
13
|
+
export declare const GIFT_CATEGORY_GROUPS: readonly ["Watch & listen", "Money", "Everyday"];
|
|
14
|
+
export type GiftCategoryGroup = (typeof GIFT_CATEGORY_GROUPS)[number];
|
|
15
|
+
/** Where an absent, blank or unknown category is filed. */
|
|
16
|
+
export declare const DEFAULT_GIFT_CATEGORY_GROUP: GiftCategoryGroup;
|
|
17
|
+
/** Backend taxonomy name → tab. Matched case-insensitively after trimming. */
|
|
18
|
+
export declare const GIFT_CATEGORY_GROUP_BY_TAXONOMY: Readonly<Record<string, GiftCategoryGroup>>;
|
|
19
|
+
/** The tab for a served category. Absent, blank and unknown all file under Everyday. */
|
|
20
|
+
export declare function giftCategoryGroup(category: string | null | undefined): GiftCategoryGroup;
|
|
21
|
+
//# sourceMappingURL=giftCategoryGroups.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"giftCategoryGroups.d.ts","sourceRoot":"","sources":["../../src/ui/giftCategoryGroups.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,mDAAmD;AACnD,eAAO,MAAM,oBAAoB,kDAAmD,CAAC;AAErF,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,2DAA2D;AAC3D,eAAO,MAAM,2BAA2B,EAAE,iBAA8B,CAAC;AAEzE,8EAA8E;AAC9E,eAAO,MAAM,+BAA+B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAmBvF,CAAC;AAYF,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,iBAAiB,CAaxF"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gift-flow copy derivation — every sentence the gift presentation says, in one place.
|
|
3
|
+
*
|
|
4
|
+
* The same discipline as `core/hostDisplay.ts`: the screens never concatenate nouns
|
|
5
|
+
* themselves, so one sentence has exactly one definition and one test.
|
|
6
|
+
*
|
|
7
|
+
* ⚠️ THE DESIGN NAMES BRANDS; THE CONTRACT DOES NOT GUARANTEE THEM. The artifact this
|
|
8
|
+
* tracks shows "6 months Amazon Prime for FREE" and a CTA reading "Claim 6 months of
|
|
9
|
+
* Amazon Prime". Those strings are ILLUSTRATIVE. Real rows are built from whatever the
|
|
10
|
+
* offers endpoint returned, which routinely means an offer with no `perk`, no
|
|
11
|
+
* `merchantName`, no `logoUrl`, or no `advertiserUrl` at all. Every function below is
|
|
12
|
+
* total: it takes the offer as it is and returns something a human can read, rather
|
|
13
|
+
* than parsing a shape the contract never promised.
|
|
14
|
+
*
|
|
15
|
+
* In particular NOTHING here tries to extract a duration ("6 months") or a plan name
|
|
16
|
+
* ("Amazon Prime") out of free text. The perk line is remote, publisher-authored copy
|
|
17
|
+
* in an unknown language; splitting it on English prepositions would produce confident
|
|
18
|
+
* nonsense on the first non-English campaign. Where the design wants a short noun
|
|
19
|
+
* phrase and the data cannot supply one, the copy degrades to a shorter true sentence
|
|
20
|
+
* instead of a longer invented one.
|
|
21
|
+
*/
|
|
22
|
+
import type { OfferModel as Offer } from '../api/models';
|
|
23
|
+
/**
|
|
24
|
+
* Copy the gift presentation hard-codes, mirroring `RewardActions`' REWARD_COPY.
|
|
25
|
+
* Named so tests can assert by symbol rather than by literal.
|
|
26
|
+
*/
|
|
27
|
+
export declare const GIFT_COPY: {
|
|
28
|
+
/** Row / CTA fallback when the offer carries no usable name at all. */
|
|
29
|
+
readonly unnamedGift: "your gift";
|
|
30
|
+
/**
|
|
31
|
+
* The same fallback for sentences that supply their own possessive.
|
|
32
|
+
* Separate from {@link unnamedGift} because "and your your gift starts" is what
|
|
33
|
+
* one shared constant produces.
|
|
34
|
+
*/
|
|
35
|
+
readonly unnamedGiftBare: "gift";
|
|
36
|
+
/** The CTA when nothing short enough to name survived. Matches REWARD_COPY.primary. */
|
|
37
|
+
readonly claimFallback: "Claim gift";
|
|
38
|
+
/**
|
|
39
|
+
* THE ARTIFACT'S OWN COPY, and the gift surface's built-in defaults.
|
|
40
|
+
*
|
|
41
|
+
* These sit at the BOTTOM of the copy chain, below a per-call override and below
|
|
42
|
+
* whatever the backend variant resolves — a publisher who has written reward copy
|
|
43
|
+
* keeps it. What changed is what the SDK says when nobody has said anything, which
|
|
44
|
+
* used to be the carousel-era `REWARD_FALLBACK_*` strings and is now the design's.
|
|
45
|
+
*/
|
|
46
|
+
readonly headline: "Here’s a FREE gift for you";
|
|
47
|
+
readonly subline: "A free present from us. Pick one.";
|
|
48
|
+
/**
|
|
49
|
+
* Above the CTA. Replaces REWARD_COPY.trust for this surface.
|
|
50
|
+
*
|
|
51
|
+
* ⚠️ ASSERTS A FREE TRIAL, so it is only correct under an offer that IS one — the
|
|
52
|
+
* same rule, and for the same reason, as `REWARD_COPY.trust`. Its one caller gates
|
|
53
|
+
* it on `isKnownFreeTrial(offer)`; a discount, an unrecognised badge, and the common
|
|
54
|
+
* case of no `badgeLabel` at all are NOT known trials and get
|
|
55
|
+
* `REWARD_COPY.trustUnknown` instead.
|
|
56
|
+
*
|
|
57
|
+
* 🔴 NEVER A DEFAULT. Positive knowledge only — the asymmetry `ui/offerBadge.ts`
|
|
58
|
+
* is built around. An offer whose terms we were not told must not be described as
|
|
59
|
+
* free, as a trial, or as cancellable, and a constant reached when nothing is known
|
|
60
|
+
* is exactly how that claim gets made. This used to be the fallback at the foot of
|
|
61
|
+
* the "Other gifts" sheet, over a LIST that routinely mixes a free trial with a paid
|
|
62
|
+
* discount (Disney+ at $4.99/mo is live inventory).
|
|
63
|
+
*/
|
|
64
|
+
readonly finePrint: "FREE trial. You can cancel anytime.";
|
|
65
|
+
/** The sheet's CTA before anything is picked. The artifact's word, and NOT a claim
|
|
66
|
+
* label: "Claim gift" there reads as a live claim for a gift not yet chosen. */
|
|
67
|
+
readonly sheetContinue: "Continue";
|
|
68
|
+
/**
|
|
69
|
+
* Sheet heading. The CONTROL that opens it is worded by `REWARD_COPY.more`, which
|
|
70
|
+
* lives with the other secondary-button labels; one string, one home.
|
|
71
|
+
*/
|
|
72
|
+
readonly sheetTitle: "Other gifts";
|
|
73
|
+
/** Under the CTA. A middot separator, matching the artifact. */
|
|
74
|
+
readonly terms: "One gift per person · Offer terms";
|
|
75
|
+
/**
|
|
76
|
+
* The `giftList` / `giftUnwrap` terms line, in two parts because the second is the
|
|
77
|
+
* link: "By proceeding you agree to our offer terms". Rendered by `ui/termsLine.ts`,
|
|
78
|
+
* which supplies the space between them.
|
|
79
|
+
*/
|
|
80
|
+
readonly termsLead: "By proceeding you agree to our";
|
|
81
|
+
readonly termsLink: "offer terms";
|
|
82
|
+
/** The gift radio group's accessible name. Screen-reader copy is still copy. */
|
|
83
|
+
readonly chooseLabel: "Choose your gift";
|
|
84
|
+
/** The sheet's close control. */
|
|
85
|
+
readonly closeSheetLabel: "Close other gifts";
|
|
86
|
+
/** The sheet's category tab strip, for assistive tech. Only rendered with 2+ tabs. */
|
|
87
|
+
readonly sheetCategoriesLabel: "Gift categories";
|
|
88
|
+
/**
|
|
89
|
+
* `giftList`: the word beside the host app's icon above the headline, so the pair
|
|
90
|
+
* reads "{icon} Presents". The icon IS the subject; without one the row is omitted.
|
|
91
|
+
*/
|
|
92
|
+
readonly presents: "Presents";
|
|
93
|
+
/** `giftList`: the link under the rows that opens the "Other gifts" sheet. */
|
|
94
|
+
readonly showMore: "Show more gifts";
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* The advertiser as a display noun — who the user is about to be handed off to.
|
|
98
|
+
*
|
|
99
|
+
* `merchantName` is the intended source but is optional on the model, so the hostname
|
|
100
|
+
* of the destination is the fallback: an offer that can send a user somewhere can name
|
|
101
|
+
* that somewhere. `www.` is dropped because "www.audiobooks.com opens next" reads like
|
|
102
|
+
* a URL, not a brand.
|
|
103
|
+
*
|
|
104
|
+
* Returns `undefined` rather than a placeholder: callers phrase a DIFFERENT sentence
|
|
105
|
+
* when the host cannot be named, instead of one with a hole in it.
|
|
106
|
+
*/
|
|
107
|
+
export declare function giftHost(offer: Offer): string | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* WHAT the user is getting, as the offer itself describes it.
|
|
110
|
+
*
|
|
111
|
+
* The campaign's `perk` line is the field written for exactly this slot ("Enjoy 30
|
|
112
|
+
* days of Apple Music free"); `description` and `title` are the progressively less
|
|
113
|
+
* specific fallbacks. Returned verbatim — see this file's header for why it is never
|
|
114
|
+
* reshaped.
|
|
115
|
+
*/
|
|
116
|
+
export declare function giftWhat(offer: Partial<Pick<Offer, 'perk' | 'description' | 'title'>>): string | undefined;
|
|
117
|
+
/** One row's two text lines. `secondary` is omitted when it would merely repeat. */
|
|
118
|
+
export interface GiftRowText {
|
|
119
|
+
primary: string;
|
|
120
|
+
secondary?: string;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The label for one gift row.
|
|
124
|
+
*
|
|
125
|
+
* `primary` is what the gift IS; `secondary` names the brand underneath it, through
|
|
126
|
+
* `giftHost` (merchant, else the destination hostname, else the title), so every
|
|
127
|
+
* surface names the advertiser by one ladder. The brand line is dropped when the
|
|
128
|
+
* primary already contains it, which is the common case — a perk line usually names
|
|
129
|
+
* its own advertiser, and "1 month of Apple Music" above a second line reading "Apple
|
|
130
|
+
* Music" is noise, not context.
|
|
131
|
+
*
|
|
132
|
+
* Never returns an empty primary: a row the user can select must say something, and an
|
|
133
|
+
* offer that names nothing at all is still selectable.
|
|
134
|
+
*/
|
|
135
|
+
export declare function giftRowText(offer: Offer): GiftRowText;
|
|
136
|
+
/** One brand-first row's two lines: the advertiser on top, the perk underneath. */
|
|
137
|
+
export interface GiftBrandFirstText {
|
|
138
|
+
brand: string;
|
|
139
|
+
perk?: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The lines for a row that leads with the BRAND (`giftList`, `giftUnwrap`), the
|
|
143
|
+
* design's order for those surfaces. Same ladder as `giftRowText`, the other way up:
|
|
144
|
+
* the brand is `giftHost`, else what the gift is, else the shipped fallback; the perk
|
|
145
|
+
* line is dropped when it would only repeat the brand.
|
|
146
|
+
*/
|
|
147
|
+
export declare function giftBrandFirstText(offer: Offer): GiftBrandFirstText;
|
|
148
|
+
/**
|
|
149
|
+
* The CTA, which NAMES the gift where the data supports naming it.
|
|
150
|
+
*
|
|
151
|
+
* The design's whole argument for this button is that "Claim 6 months of Amazon Prime"
|
|
152
|
+
* converts better than "Claim gift" because the user reads back what they chose. That
|
|
153
|
+
* argument only holds while the name is short and true, so the ladder degrades twice:
|
|
154
|
+
*
|
|
155
|
+
* 1. a short perk line → "Claim 1 month of Apple Music"
|
|
156
|
+
* 2. an advertiser that fits, no short perk → "Claim your Apple Music gift"
|
|
157
|
+
* 3. neither → "Claim gift"
|
|
158
|
+
*
|
|
159
|
+
* Rung 2 exists because a long perk is common and a missing brand is not: an offer
|
|
160
|
+
* whose perk runs to a sentence still knows who it is from, and naming the brand keeps
|
|
161
|
+
* the button specific without letting it wrap. The brand is `giftHost`, so an offer
|
|
162
|
+
* with a destination but no merchant name is still named rather than dropping to rung 3.
|
|
163
|
+
*/
|
|
164
|
+
export declare function giftCtaLabel(offer: Offer): string;
|
|
165
|
+
/**
|
|
166
|
+
* The handoff sentence: what happens after the claim.
|
|
167
|
+
*
|
|
168
|
+
* ⚠️ STAGED, NOT YET RENDERED — deliberately, and this is the only thing in this file
|
|
169
|
+
* nothing calls. The design's post-claim card is not built: `rewardUsers` is
|
|
170
|
+
* `claimBehavior: 'immediate'`, which opens the advertiser window synchronously inside
|
|
171
|
+
* the click gesture so it survives mobile popup blockers, and an interstitial means
|
|
172
|
+
* either a blank tab held open while the user reads or moving the redirect out of that
|
|
173
|
+
* gesture. That is a money-path change to a frozen behaviour (see `core/claimBehavior`),
|
|
174
|
+
* so it is a separate decision. The sentence is derived and tested here so that
|
|
175
|
+
* decision does not also have to relitigate the copy.
|
|
176
|
+
*
|
|
177
|
+
* Two shapes, because a sentence about an unnamed destination is worse than a shorter
|
|
178
|
+
* one. Both avoid promising that anything is already active — the claim creates a
|
|
179
|
+
* transaction, and the advertiser conversion is asynchronous and only knowable later.
|
|
180
|
+
*/
|
|
181
|
+
export declare function giftHandoffLine(host: string | undefined, what: string | undefined): string;
|
|
182
|
+
/**
|
|
183
|
+
* The accessible name for a gift row.
|
|
184
|
+
*
|
|
185
|
+
* A radio whose visible label is two separate lines is announced as a run-on; naming
|
|
186
|
+
* it explicitly keeps the brand attached to the perk. Mirrors what
|
|
187
|
+
* `RewardActions.setSelectedOffer` does for the CTA.
|
|
188
|
+
*/
|
|
189
|
+
export declare function giftRowAccessibleName(offer: Offer): string;
|
|
190
|
+
/**
|
|
191
|
+
* The accessible name for a BRAND-FIRST row, in the order the row shows its text, so
|
|
192
|
+
* a voice-control user reading the visible brand off the row is saying the start of
|
|
193
|
+
* its name (WCAG 2.5.3, label in name).
|
|
194
|
+
*/
|
|
195
|
+
export declare function giftBrandFirstAccessibleName(offer: Offer): string;
|
|
196
|
+
/**
|
|
197
|
+
* Fill `el` with `text`, wrapping any occurrence of FREE in an accent span.
|
|
198
|
+
*
|
|
199
|
+
* TEXT NODES ONLY — the host's copy never goes through `innerHTML`, which is the same
|
|
200
|
+
* rule `RewardLayout` states for its own title. A publisher headline containing markup
|
|
201
|
+
* renders as the characters they wrote, not as elements.
|
|
202
|
+
*/
|
|
203
|
+
export declare function fillRowLabel(el: HTMLElement, text: string, spanClass: string): void;
|
|
204
|
+
export declare function fillWithEmphasis(el: HTMLElement, text: string, spanClass: string): void;
|
|
205
|
+
/**
|
|
206
|
+
* Fill `el` with a `giftList` perk line: the design's "10 meals for FREE" and
|
|
207
|
+
* "$4.99/month", with FREE or the amount in the accent and the rest muted.
|
|
208
|
+
*
|
|
209
|
+
* The FREE rule is `fillRowLabel`'s, unchanged, so the word is treated the same on
|
|
210
|
+
* every gift surface. Only when the line has no FREE to accent does the FIRST price
|
|
211
|
+
* amount take the accent instead — one accent per line, which is what the design does
|
|
212
|
+
* ("$4.99" accented, "/month" not). Text nodes only, like every filler here.
|
|
213
|
+
*/
|
|
214
|
+
export declare function fillPerkLine(el: HTMLElement, text: string, spanClass: string): void;
|
|
215
|
+
//# sourceMappingURL=giftCopy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"giftCopy.d.ts","sourceRoot":"","sources":["../../src/ui/giftCopy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,UAAU,IAAI,KAAK,EAAE,MAAM,eAAe,CAAC;AAGzD;;;GAGG;AACH,eAAO,MAAM,SAAS;IACpB,uEAAuE;;IAEvE;;;;OAIG;;IAEH,uFAAuF;;IAEvF;;;;;;;OAOG;;;IASH;;;;;;;;;;;;;;;OAeG;;IAEH;qFACiF;;IAEjF;;;OAGG;;IAEH,gEAAgE;;IAEhE;;;;OAIG;;;IAGH,gFAAgF;;IAEhF,iCAAiC;;IAEjC,sFAAsF;;IAEtF;;;OAGG;;IAEH,8EAA8E;;CAEtE,CAAC;AAeX;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,CAUzD;AAsBD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAItB,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,OAAO,CAAC,CAAC,GAC5D,MAAM,GAAG,SAAS,CAEpB;AAED,oFAAoF;AACpF,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,WAAW,CAKrD;AAED,mFAAmF;AACnF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,kBAAkB,CAInE;AAiBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAYjD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAO1F;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAG1D;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAGjE;AA4BD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAoBnF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAavF;AAWD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAenF"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `giftUnwrap` surface's artwork, as SDK-owned inline SVG.
|
|
3
|
+
*
|
|
4
|
+
* The design's box is a 3D raster (Figma "hypothesis 5", 1024px renders). The SDK
|
|
5
|
+
* ships no raster: an image here would be a network dependency on a decorative
|
|
6
|
+
* element and several hundred KB, so the box is redrawn as flat vector faces in a
|
|
7
|
+
* three-quarter view that matches the raster's silhouette, with the tones sampled
|
|
8
|
+
* off it (body #F7E3A8 → #F7C882, ribbon #F69300, bow #ED6A00 / #E95200; the open
|
|
9
|
+
* box is the warmer #FFE0A7 / #FFB871 with an #FF9900 ribbon).
|
|
10
|
+
*
|
|
11
|
+
* Every string here is static markup built from numbers this module owns, which is
|
|
12
|
+
* the only thing `innerHTML` is allowed to receive. No offer data reaches it: the
|
|
13
|
+
* selected offer's logo is an `<img>` element laid BETWEEN the open box's two layers
|
|
14
|
+
* by `GiftUnwrap`, never markup inside them.
|
|
15
|
+
*
|
|
16
|
+
* GRADIENT IDS ARE PER INSTANCE. The closed box is drawn twice (screen 1's hero and
|
|
17
|
+
* the first frame of screen 2's reveal) and `url(#id)` resolves to the FIRST element
|
|
18
|
+
* with that id in the document. When that first copy sits inside a `hidden` screen,
|
|
19
|
+
* Chrome and Safari paint the reference as nothing, so a shared id would draw the
|
|
20
|
+
* visible box with no fill. `nextArtId()` suffixes every id.
|
|
21
|
+
*
|
|
22
|
+
* The three icons (check circle, selected radio, back chevron) are the
|
|
23
|
+
* design's exported vectors with their path data unchanged; only the Figma layer ids
|
|
24
|
+
* were dropped, because duplicate `id` attributes are invalid in one document.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* The closed, wrapped box in a 200×200 frame: body, lid, the ribbon crossing both
|
|
28
|
+
* faces and the lid, and the bow on top. Symmetric about x=100.
|
|
29
|
+
*/
|
|
30
|
+
export declare function closedBoxSvg(): string;
|
|
31
|
+
/**
|
|
32
|
+
* The open box, BEHIND the logo tile: the lid lifted and tipped back, and the inside
|
|
33
|
+
* of the box. Pairs with {@link openBoxFrontSvg}; the tile sits between the two.
|
|
34
|
+
*/
|
|
35
|
+
export declare function openBoxBackSvg(): string;
|
|
36
|
+
/** The open box IN FRONT of the logo tile: the two front walls and their ribbon. */
|
|
37
|
+
export declare function openBoxFrontSvg(): string;
|
|
38
|
+
/**
|
|
39
|
+
* The soft blue bloom behind the open box: `bg-ellipse-glow-ready.svg`, with its
|
|
40
|
+
* layer opacity lifted OUT of the markup. The design's 0.5 is the glow track's
|
|
41
|
+
* resting value, so it lives on the element the reveal animates instead of being
|
|
42
|
+
* multiplied in twice.
|
|
43
|
+
*/
|
|
44
|
+
export declare function readyGlowSvg(): string;
|
|
45
|
+
/** `icon-check-circle.svg`, 20×20, #17171A. */
|
|
46
|
+
export declare const CHECK_CIRCLE_SVG: string;
|
|
47
|
+
/**
|
|
48
|
+
* `icon-radio-selected.svg`: the ring and the dot. The unselected state is the same
|
|
49
|
+
* ring with no dot, drawn by CSS on the same element (see the radio rules in the
|
|
50
|
+
* unwrap block of styles.ts), so this markup is only ever the selected one.
|
|
51
|
+
*/
|
|
52
|
+
export declare const RADIO_SELECTED_SVG: string;
|
|
53
|
+
/** `icon-back-chevron.svg`, 20×20. */
|
|
54
|
+
export declare const BACK_CHEVRON_SVG: string;
|
|
55
|
+
//# sourceMappingURL=giftUnwrapArt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"giftUnwrapArt.d.ts","sourceRoot":"","sources":["../../src/ui/giftUnwrapArt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAaH;;;GAGG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAyCrC;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CA8BvC;AAED,oFAAoF;AACpF,wBAAgB,eAAe,IAAI,MAAM,CAoBxC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAWrC;AAED,+CAA+C;AAC/C,eAAO,MAAM,gBAAgB,QAGnB,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAIrB,CAAC;AAEX,sCAAsC;AACtC,eAAO,MAAM,gBAAgB,QAGnB,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every sentence the `giftUnwrap` surface says that the shared gift copy does not.
|
|
3
|
+
*
|
|
4
|
+
* Its own module rather than more keys on `GIFT_COPY`, because these strings belong to
|
|
5
|
+
* one design (Figma "hypothesis 5") and nothing else renders them. The shared helpers
|
|
6
|
+
* (`giftHost`, `giftWhat`, `giftCtaLabel`, `fillRowLabel`) are still the source for
|
|
7
|
+
* anything the other gift surfaces also say, so a brand or a perk line reads the same
|
|
8
|
+
* on every one of them.
|
|
9
|
+
*
|
|
10
|
+
* 🔴 POSITIVE KNOWLEDGE ONLY, the rule `ui/offerBadge.ts` is built around. The "ready"
|
|
11
|
+
* screen lists what happens next, and the design's example bullets assert a free trial
|
|
12
|
+
* ("will ask for a card", "nothing is charged before the trial ends"). Those two are
|
|
13
|
+
* true only for an offer the backend told us is a free trial, so they are gated on
|
|
14
|
+
* `isKnownFreeTrial`, and every other offer gets a sentence that holds whatever its
|
|
15
|
+
* terms turn out to be.
|
|
16
|
+
*/
|
|
17
|
+
import type { OfferModel as Offer } from '../api/models';
|
|
18
|
+
export declare const UNWRAP_COPY: {
|
|
19
|
+
/** The sheet's heading, and the radio group inside it. */
|
|
20
|
+
readonly sheetTitle: "More gifts";
|
|
21
|
+
/** The sheet's close control, for keyboard and screen-reader users. */
|
|
22
|
+
readonly closeSheetLabel: "Close more gifts";
|
|
23
|
+
/** Screen 1's CTA, and screen 2's when nothing names the advertiser. Screen 1's is
|
|
24
|
+
* navigation to the ready screen, never a claim. */
|
|
25
|
+
readonly continue: "Continue";
|
|
26
|
+
/** Screen 2's way back to the list, with the selection intact. */
|
|
27
|
+
readonly back: "Back";
|
|
28
|
+
readonly readyTitle: "Your gift is ready";
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* The line under "Your gift is ready". The design says "our partner’s app"; this names
|
|
32
|
+
* the destination when the offer does, and on the web the destination is the
|
|
33
|
+
* advertiser's site rather than an app. With nothing to name, the sentence is
|
|
34
|
+
* rephrased around the gap instead of carrying a stand-in noun. U+2019 throughout,
|
|
35
|
+
* matching `GIFT_COPY.headline`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function unwrapReadySubline(offer: Offer): string;
|
|
38
|
+
/**
|
|
39
|
+
* Screen 2's CTA: "Continue to {advertiser}", or plain "Continue" when nothing names
|
|
40
|
+
* the destination. A sentence with a hole in it is worse than a shorter true one.
|
|
41
|
+
*/
|
|
42
|
+
export declare function unwrapClaimLabel(offer: Offer): string;
|
|
43
|
+
/**
|
|
44
|
+
* The bullets under "Your gift is ready", at most three.
|
|
45
|
+
*
|
|
46
|
+
* 1. what the gift is, in the offer's own words (`giftWhat`), whenever it has any
|
|
47
|
+
* 2. KNOWN free trial: "{advertiser} will ask for a card"
|
|
48
|
+
* 3. KNOWN free trial: "Cancel anytime, nothing is charged before the {advertiser} trial ends"
|
|
49
|
+
* otherwise, in place of 2 and 3: "You'll finish claiming on {advertiser}"
|
|
50
|
+
*
|
|
51
|
+
* Nothing here parses a duration or a price out of the perk line (see the header of
|
|
52
|
+
* `giftCopy.ts`): the first bullet is the perk verbatim, so "Free for 6 months" is
|
|
53
|
+
* what a campaign that wrote "Free for 6 months" gets, and a campaign in another
|
|
54
|
+
* language keeps its own words.
|
|
55
|
+
*/
|
|
56
|
+
export declare function unwrapReadyBullets(offer: Offer): string[];
|
|
57
|
+
//# sourceMappingURL=giftUnwrapCopy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"giftUnwrapCopy.d.ts","sourceRoot":"","sources":["../../src/ui/giftUnwrapCopy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,UAAU,IAAI,KAAK,EAAE,MAAM,eAAe,CAAC;AAIzD,eAAO,MAAM,WAAW;IACtB,0DAA0D;;IAE1D,uEAAuE;;IAEvE;yDACqD;;IAErD,kEAAkE;;;CAG1D,CAAC;AAEX;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAKvD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAGrD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,CAiBzD"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one rule for when an offer impression (`offerView` → `sdk_offer_presented`) fires.
|
|
3
|
+
*
|
|
4
|
+
* An offer element counts as SHOWN only when all of these hold at the same moment:
|
|
5
|
+
* (a) `document.visibilityState === 'visible'`;
|
|
6
|
+
* (b) the container presenting it is visible (the modal's show transition has begun,
|
|
7
|
+
* reported through `setContainerVisible`, never sniffed from a class);
|
|
8
|
+
* (c) at least half of it is on screen, per IntersectionObserver (root null, so
|
|
9
|
+
* ancestor clipping — a scroller, a sheet — is honoured). See {@link visibleShare}
|
|
10
|
+
* for what "half of it" is measured against;
|
|
11
|
+
* (d) it is actually PAINTED — not mid-entrance, not inside a container still fading
|
|
12
|
+
* in. An observer reports layout geometry and is blind to `opacity` and
|
|
13
|
+
* `visibility`, and every surface here animates in, so this is a separate check;
|
|
14
|
+
* (e) the registering surface's own condition, when it has one (the carousel's
|
|
15
|
+
* "this is the card the user is on, and the scroll is at rest").
|
|
16
|
+
* Each key (the offer id) fires at most once for the life of the gate, which is one
|
|
17
|
+
* presentation. Impressions feed billing, so every failure path under-reports rather
|
|
18
|
+
* than over-reports, and nothing here throws into the host page.
|
|
19
|
+
*/
|
|
20
|
+
/** The share of an element that must be inside the viewport for it to count as seen. */
|
|
21
|
+
export declare const IMPRESSION_VISIBLE_RATIO = 0.5;
|
|
22
|
+
/**
|
|
23
|
+
* How much of an element counts as on screen, as a share of the SMALLER of the element
|
|
24
|
+
* and the viewport.
|
|
25
|
+
*
|
|
26
|
+
* For anything that fits on screen this is exactly `intersectionRatio`. For anything
|
|
27
|
+
* larger, "half the element" is a bar it cannot clear — a 2200px card on an 844px
|
|
28
|
+
* viewport tops out at 38% — so the bar becomes "it covers half the screen" instead.
|
|
29
|
+
* Both readings answer the same question; the lesser one is the reachable one.
|
|
30
|
+
*/
|
|
31
|
+
export declare function visibleShare(entry: IntersectionObserverEntry): number;
|
|
32
|
+
export interface ImpressionRegistrationOptions {
|
|
33
|
+
/** A surface-owned condition, re-read on every evaluation and after `recheck()`. */
|
|
34
|
+
eligible?: () => boolean;
|
|
35
|
+
}
|
|
36
|
+
export declare class ImpressionGate {
|
|
37
|
+
#private;
|
|
38
|
+
constructor(options?: {
|
|
39
|
+
threshold?: number;
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Watch `element` and call `onShown` once it is shown. A key that has already fired
|
|
43
|
+
* is not watched again. Returns the disposer for this one registration.
|
|
44
|
+
*/
|
|
45
|
+
register(element: Element, key: string, onShown: () => void, options?: ImpressionRegistrationOptions): () => void;
|
|
46
|
+
/** The presenting container became visible (true) or started hiding (false). */
|
|
47
|
+
setContainerVisible(visible: boolean): void;
|
|
48
|
+
/** Re-evaluate every pending registration against the latest known state. */
|
|
49
|
+
recheck(): void;
|
|
50
|
+
hasShown(key: string): boolean;
|
|
51
|
+
destroy(): void;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A set of registrations a surface replaces together (its rows, the sheet's cards).
|
|
55
|
+
* `clear()` drops the watches without forgetting what has already been shown.
|
|
56
|
+
*/
|
|
57
|
+
export declare class ImpressionGroup {
|
|
58
|
+
#private;
|
|
59
|
+
constructor(gate: ImpressionGate);
|
|
60
|
+
register(element: Element, key: string, onShown: () => void, options?: ImpressionRegistrationOptions): void;
|
|
61
|
+
clear(): void;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=impressionGate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impressionGate.d.ts","sourceRoot":"","sources":["../../src/ui/impressionGate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,wFAAwF;AACxF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAuB5C;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,yBAAyB,GAAG,MAAM,CAWrE;AAED,MAAM,WAAW,6BAA6B;IAC5C,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC;CAC1B;AASD,qBAAa,cAAc;;gBAgBb,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO;IAIhD;;;OAGG;IACH,QAAQ,CACN,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,IAAI,EACnB,OAAO,GAAE,6BAAkC,GAC1C,MAAM,IAAI;IAwBb,gFAAgF;IAChF,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAM3C,6EAA6E;IAC7E,OAAO,IAAI,IAAI;IAKf,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI9B,OAAO,IAAI,IAAI;CAmNhB;AAED;;;GAGG;AACH,qBAAa,eAAe;;gBAId,IAAI,EAAE,cAAc;IAIhC,QAAQ,CACN,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,IAAI,EACnB,OAAO,CAAC,EAAE,6BAA6B,GACtC,IAAI;IAIP,KAAK,IAAI,IAAI;CAKd"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an offer's promo badge SAYS, and whether that offer is actually a free trial.
|
|
3
|
+
*
|
|
4
|
+
* One module for both because they are one question asked twice. The card pill and the
|
|
5
|
+
* sheet's fine print were each asserting a free trial independently and unconditionally
|
|
6
|
+
* — `REWARD_CARD_COPY.badge = 'Free trial'` and `'FREE trial. You can cancel anytime.'`
|
|
7
|
+
* — on inventory where that is not true. Disney+ at $4.99/mo is live right now.
|
|
8
|
+
*
|
|
9
|
+
* THE ONLY SIGNAL IS `badgeLabel`, a constrained enum on the campaign
|
|
10
|
+
* (`'free_trial' | 'discount' | null`). The contract says plainly that clients map it
|
|
11
|
+
* to display text AND to free-trial-vs-discount logic, so this is that mapping, in one
|
|
12
|
+
* place, rather than two components each guessing.
|
|
13
|
+
*
|
|
14
|
+
* 🔴 ABSENT MEANS "WE DO NOT KNOW", AND MUST NOT READ AS "FREE". That is the whole
|
|
15
|
+
* point of the default. `null` is by far the common case — measured on the live shelf,
|
|
16
|
+
* all five current PayRange offers have `badgeLabel: null` — so the default is what
|
|
17
|
+
* most users will actually see, and it has to be a word that promises nothing:
|
|
18
|
+
* "Exclusive" says the offer is special without claiming it is free, a trial, or
|
|
19
|
+
* discounted. Junaid's call, and the reason is exactly that we might not know.
|
|
20
|
+
*
|
|
21
|
+
* An unrecognised value lands here too. `APIClient` drops anything outside the enum,
|
|
22
|
+
* so a future badge this SDK version has never heard of degrades to "Exclusive" rather
|
|
23
|
+
* than rendering a raw wire string in a pill.
|
|
24
|
+
*/
|
|
25
|
+
import type { OfferModel } from '../api/models';
|
|
26
|
+
/**
|
|
27
|
+
* The fields this module reads: the badge, and the words shown beside it.
|
|
28
|
+
*
|
|
29
|
+
* ALL OPTIONAL, because every one of them is genuinely optional in practice —
|
|
30
|
+
* `badgeLabel` is null on eleven of twelve live offers and `perk` is null on one — and
|
|
31
|
+
* because these helpers only ever read them through `firstNonBlank`. A `Pick` of the
|
|
32
|
+
* required fields would have forced every caller to hold a whole `OfferModel` to ask
|
|
33
|
+
* what badge to show.
|
|
34
|
+
*/
|
|
35
|
+
type BadgedOffer = Partial<Pick<OfferModel, 'badgeLabel' | 'perk' | 'description' | 'title'>>;
|
|
36
|
+
/**
|
|
37
|
+
* The badge shown when the campaign carries none, or one this SDK does not recognise.
|
|
38
|
+
*
|
|
39
|
+
* Deliberately not free/trial/discount language: it is the honest thing to say about an
|
|
40
|
+
* offer whose terms we were not told.
|
|
41
|
+
*/
|
|
42
|
+
export declare const DEFAULT_BADGE = "Exclusive";
|
|
43
|
+
/** The pill text for an offer. Never empty — every card gets a badge. */
|
|
44
|
+
export declare function offerBadgeText(offer: BadgedOffer | undefined): string;
|
|
45
|
+
/**
|
|
46
|
+
* Whether this offer is a KNOWN free trial.
|
|
47
|
+
*
|
|
48
|
+
* Positive knowledge only: `discount`, absent and unrecognised are all `false`. This is
|
|
49
|
+
* what gates any copy that asserts a trial — "no charges until trial ends", "cancel
|
|
50
|
+
* anytime" — so defaulting it to `true` would put a false claim under an offer that
|
|
51
|
+
* charges on day one. The asymmetry is the point: we say "free trial" only when the
|
|
52
|
+
* backend told us so.
|
|
53
|
+
*/
|
|
54
|
+
export declare function isKnownFreeTrial(offer: BadgedOffer | undefined): boolean;
|
|
55
|
+
export {};
|
|
56
|
+
//# sourceMappingURL=offerBadge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offerBadge.d.ts","sourceRoot":"","sources":["../../src/ui/offerBadge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGhD;;;;;;;;GAQG;AACH,KAAK,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,GAAG,MAAM,GAAG,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC;AA2C9F;;;;;GAKG;AACH,eAAO,MAAM,aAAa,cAAc,CAAC;AAEzC,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,MAAM,CASrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,CAExE"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The seam between a presentation's offer SURFACE and the claim flow that drives it.
|
|
3
|
+
*
|
|
4
|
+
* `Encore.#runOfferFlow` owns the part that must never fork: creating the transaction,
|
|
5
|
+
* stamping `use_case` on the funnel events, opening the advertiser window inside the
|
|
6
|
+
* click gesture, and settling the presentation exactly once. Four surfaces feed it, and
|
|
7
|
+
* WHICH ONE is not decided by use case alone: `OfferCarousel` serves `reduceChurn` and
|
|
8
|
+
* every `rewardUsers` publisher whose served variant names no gift surface, while
|
|
9
|
+
* `GiftReveal`, `GiftList` and `GiftUnwrap` serve `rewardUsers` only when the backend
|
|
10
|
+
* serves their variant name (`rewardSurfaceKind` in `core/giftVariant.ts`). This
|
|
11
|
+
* interface is what lets the flow be written once instead of four times.
|
|
12
|
+
*
|
|
13
|
+
* 🔴 THE POINT IS THAT THERE IS ONE CLAIM PATH. A surface does not claim; it says
|
|
14
|
+
* `offerClaim` and the flow does the rest. Adding a surface that redirects, creates a
|
|
15
|
+
* transaction, or emits its own analytics would reintroduce exactly the split this
|
|
16
|
+
* seam exists to prevent.
|
|
17
|
+
*
|
|
18
|
+
* Not every member is meaningful on every surface, and that is deliberate rather than
|
|
19
|
+
* sloppy: `scroll` and `complete` describe a carousel and are simply never emitted by
|
|
20
|
+
* a surface with nothing to scroll and no last card to decline. A subscriber to an
|
|
21
|
+
* event that never fires costs nothing, where branching on the surface type at each of
|
|
22
|
+
* a dozen call sites in the claim flow would put a conditional through the money path
|
|
23
|
+
* for no behavioural gain.
|
|
24
|
+
*/
|
|
25
|
+
import type { OfferModel as Offer } from '../api/models';
|
|
26
|
+
/**
|
|
27
|
+
* A TYPE ALIAS, not `interface … extends Record<string, unknown>` — and the lint
|
|
28
|
+
* exception below is the whole reason.
|
|
29
|
+
*
|
|
30
|
+
* The `extends` form is what the older event maps in this repo use, and it adds an
|
|
31
|
+
* INDEX SIGNATURE: `keyof` widens to `string | number | symbol`, so a MISSPELLED name
|
|
32
|
+
* — `on('oferClaim')`, one `f`, and yes that is deliberate — compiles happily and its
|
|
33
|
+
* payload degrades to `unknown`. Dropping the `extends` is not
|
|
34
|
+
* an option either — an interface has no implicit index signature, so it does not
|
|
35
|
+
* satisfy `EventEmitter`'s `Record<string, unknown>` constraint at all.
|
|
36
|
+
*
|
|
37
|
+
* A type alias satisfies that constraint AND keeps `keyof` to the six literal names, so
|
|
38
|
+
* a typo in an event name stays a compile error. `consistent-type-definitions` has no
|
|
39
|
+
* way to express that distinction, so it is disabled here specifically.
|
|
40
|
+
*/
|
|
41
|
+
export type RewardSurfaceEventMap = {
|
|
42
|
+
/** The user asked to claim the given offer. The ONLY entry point to the claim flow. */
|
|
43
|
+
offerClaim: {
|
|
44
|
+
offer: Offer;
|
|
45
|
+
index: number;
|
|
46
|
+
};
|
|
47
|
+
/** An impression: this offer was seen. Grain differs per surface — see GiftReveal. */
|
|
48
|
+
offerView: {
|
|
49
|
+
offer: Offer;
|
|
50
|
+
index: number;
|
|
51
|
+
};
|
|
52
|
+
/** The selection changed. State, not analytics — it names the CTA. */
|
|
53
|
+
offerSelect: {
|
|
54
|
+
offer: Offer;
|
|
55
|
+
index: number;
|
|
56
|
+
};
|
|
57
|
+
/** Carousel only. */
|
|
58
|
+
offerDecline: {
|
|
59
|
+
index: number;
|
|
60
|
+
};
|
|
61
|
+
/** Carousel only — nothing scrolls on the gift surface. */
|
|
62
|
+
scroll: {
|
|
63
|
+
axis: 'horizontal' | 'vertical';
|
|
64
|
+
position: number;
|
|
65
|
+
};
|
|
66
|
+
/** Carousel only — the gift surface has no last card to run out of. */
|
|
67
|
+
complete: {
|
|
68
|
+
claimed: boolean;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
export interface RewardSurface {
|
|
72
|
+
on<E extends keyof RewardSurfaceEventMap>(event: E, handler: (payload: RewardSurfaceEventMap[E]) => void): () => void;
|
|
73
|
+
render(): HTMLElement;
|
|
74
|
+
destroy(): void;
|
|
75
|
+
/** Claim whatever is currently selected. Emits `offerClaim`; never claims directly. */
|
|
76
|
+
claimSelected(): void;
|
|
77
|
+
getTotalOffers(): number;
|
|
78
|
+
getCurrentIndex(): number;
|
|
79
|
+
selectOffer(index: number): void;
|
|
80
|
+
next(): void;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=rewardSurface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rewardSurface.d.ts","sourceRoot":"","sources":["../../src/ui/rewardSurface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,UAAU,IAAI,KAAK,EAAE,MAAM,eAAe,CAAC;AAEzD;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,qBAAqB,GAAG;IAClC,uFAAuF;IACvF,UAAU,EAAE;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,sFAAsF;IACtF,SAAS,EAAE;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,sEAAsE;IACtE,WAAW,EAAE;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,qBAAqB;IACrB,YAAY,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAChC,2DAA2D;IAC3D,MAAM,EAAE;QAAE,IAAI,EAAE,YAAY,GAAG,UAAU,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,uEAAuE;IACvE,QAAQ,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;CAChC,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,CAAC,SAAS,MAAM,qBAAqB,EACtC,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAC,CAAC,KAAK,IAAI,GACnD,MAAM,IAAI,CAAC;IACd,MAAM,IAAI,WAAW,CAAC;IACtB,OAAO,IAAI,IAAI,CAAC;IAChB,uFAAuF;IACvF,aAAa,IAAI,IAAI,CAAC;IACtB,cAAc,IAAI,MAAM,CAAC;IACzB,eAAe,IAAI,MAAM,CAAC;IAC1B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,IAAI,IAAI,IAAI,CAAC;CACd"}
|