@occluze/tether 0.6.8 → 0.7.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.
Files changed (215) hide show
  1. package/README.md +28 -28
  2. package/dist/esm/astro.js +2 -2
  3. package/dist/esm/chunks/account-CXMA5RPZ.js +1 -0
  4. package/dist/esm/chunks/account-pages-IZO7FEOZ.js +1 -0
  5. package/dist/esm/chunks/analytics-PG5FPRDN.js +1 -0
  6. package/dist/esm/chunks/chunk-4HD4J5JC.js +1 -0
  7. package/dist/esm/chunks/chunk-6TFMLT4A.js +4 -0
  8. package/dist/esm/chunks/chunk-BBVUVPMO.js +1 -0
  9. package/dist/esm/chunks/chunk-E5YO6274.js +1 -0
  10. package/dist/esm/chunks/chunk-EW5SYO6L.js +1 -0
  11. package/dist/esm/chunks/chunk-HJY2JSNB.js +1 -0
  12. package/dist/esm/chunks/chunk-MQYB6BH4.js +1 -0
  13. package/dist/esm/chunks/chunk-N3V45JPS.js +1 -0
  14. package/dist/esm/chunks/chunk-RPMBCAGS.js +1 -0
  15. package/dist/esm/chunks/chunk-VFNHIGCS.js +1 -0
  16. package/dist/esm/chunks/chunk-WT7RGGJU.js +1 -0
  17. package/dist/esm/chunks/chunk-YN6LEBEN.js +1 -0
  18. package/dist/esm/chunks/chunk-Z6HS3H7K.js +1 -0
  19. package/dist/esm/chunks/diagnose-XCEBQKBO.js +14 -0
  20. package/dist/esm/chunks/extras-4HU3VSBQ.js +1 -0
  21. package/dist/esm/chunks/markets-ui-M4IQ62CT.js +1 -0
  22. package/dist/esm/chunks/search-A5DS2IOS.js +1 -0
  23. package/dist/esm/index.js +1 -1
  24. package/dist/esm/server.js +2 -2
  25. package/dist/presets/barba.esm.js +19 -23
  26. package/dist/tether.account-pages.js +332 -367
  27. package/dist/tether.account-pages.min.js +1 -1
  28. package/dist/tether.account.js +253 -492
  29. package/dist/tether.account.min.js +1 -1
  30. package/dist/tether.analytics.js +82 -89
  31. package/dist/tether.analytics.min.js +1 -1
  32. package/dist/tether.diagnose.js +52 -34
  33. package/dist/tether.diagnose.min.js +2 -2
  34. package/dist/tether.extras.js +42 -37
  35. package/dist/tether.extras.min.js +1 -1
  36. package/dist/tether.js +1361 -935
  37. package/dist/tether.markets.js +89 -91
  38. package/dist/tether.markets.min.js +1 -1
  39. package/dist/tether.min.js +0 -0
  40. package/dist/tether.min.js.map +4 -4
  41. package/dist/tether.search.js +60 -69
  42. package/dist/tether.search.min.js +1 -1
  43. package/dist/types/server/src/shared/account-orders.d.ts +44 -0
  44. package/dist/types/server/src/shared/account-routes.d.ts +153 -0
  45. package/dist/types/server/src/shared/api-versions.d.ts +13 -0
  46. package/dist/types/server/src/shared/cart-id.d.ts +2 -0
  47. package/dist/types/server/src/shared/error-message.d.ts +2 -0
  48. package/dist/types/server/src/shared/json.d.ts +25 -0
  49. package/dist/types/server/src/shared/market-codes.d.ts +16 -0
  50. package/dist/types/src/account/addresses-view.d.ts +5 -0
  51. package/dist/types/src/account/answers.d.ts +40 -0
  52. package/dist/types/src/account/markup.d.ts +19 -0
  53. package/dist/types/src/account/operations.d.ts +41 -0
  54. package/dist/types/src/account/order-lines.d.ts +13 -0
  55. package/dist/types/src/account/order-list.d.ts +12 -0
  56. package/dist/types/src/account/order-page.d.ts +6 -0
  57. package/dist/types/src/account/page-answers.d.ts +182 -0
  58. package/dist/types/src/account/page-events.d.ts +2 -0
  59. package/dist/types/src/account/server-client.d.ts +60 -0
  60. package/dist/types/src/account/subscriptions-view.d.ts +2 -0
  61. package/dist/types/src/account-api.d.ts +18 -0
  62. package/dist/types/src/account-marks.d.ts +15 -0
  63. package/dist/types/src/account-pages.d.ts +8 -0
  64. package/dist/types/src/account.d.ts +64 -0
  65. package/dist/types/src/amounts.d.ts +12 -0
  66. package/dist/types/src/analytics-shopify.d.ts +41 -0
  67. package/dist/types/src/analytics.d.ts +2 -0
  68. package/dist/types/src/api-version.d.ts +28 -0
  69. package/dist/types/src/astro.d.ts +42 -0
  70. package/dist/types/src/cart/cart-shape.d.ts +43 -0
  71. package/dist/types/src/cart/checkout.d.ts +16 -0
  72. package/dist/types/src/cart/drawer.d.ts +18 -0
  73. package/dist/types/src/cart/mutations.d.ts +85 -0
  74. package/dist/types/src/cart/render.d.ts +17 -0
  75. package/dist/types/src/cart/store.d.ts +185 -0
  76. package/dist/types/src/cart.d.ts +106 -0
  77. package/dist/types/src/client.d.ts +134 -0
  78. package/dist/types/src/collection.d.ts +25 -0
  79. package/dist/types/src/config-sections.d.ts +40 -0
  80. package/dist/types/src/config.d.ts +190 -0
  81. package/dist/types/src/controls/cart-controls.d.ts +2 -0
  82. package/dist/types/src/controls/discount.d.ts +2 -0
  83. package/dist/types/src/controls/extras.d.ts +5 -0
  84. package/dist/types/src/controls/navigation.d.ts +12 -0
  85. package/dist/types/src/diagnose-markup.d.ts +19 -0
  86. package/dist/types/src/diagnose.d.ts +60 -0
  87. package/dist/types/src/dom.d.ts +75 -0
  88. package/dist/types/src/extras.d.ts +21 -0
  89. package/dist/types/src/fragments.d.ts +158 -0
  90. package/dist/types/src/globals.d.ts +15 -0
  91. package/dist/types/src/images.d.ts +20 -0
  92. package/dist/types/src/index.d.ts +167 -0
  93. package/dist/types/src/initial.d.ts +56 -0
  94. package/dist/types/src/lazy.d.ts +28 -0
  95. package/dist/types/src/loader.d.ts +84 -0
  96. package/dist/types/src/market-codes.d.ts +6 -0
  97. package/dist/types/src/markets-ui.d.ts +32 -0
  98. package/dist/types/src/markets.d.ts +72 -0
  99. package/dist/types/src/markup.d.ts +131 -0
  100. package/dist/types/src/mounting.d.ts +46 -0
  101. package/dist/types/src/page-market.d.ts +27 -0
  102. package/dist/types/src/presets/barba.d.ts +56 -0
  103. package/dist/types/src/product-events.d.ts +2 -0
  104. package/dist/types/src/product-render.d.ts +17 -0
  105. package/dist/types/src/product-shape.d.ts +90 -0
  106. package/dist/types/src/product-state.d.ts +110 -0
  107. package/dist/types/src/product.d.ts +66 -0
  108. package/dist/types/src/repeaters.d.ts +16 -0
  109. package/dist/types/src/search.d.ts +33 -0
  110. package/dist/types/src/selling-plans.d.ts +64 -0
  111. package/dist/types/src/server.d.ts +88 -0
  112. package/dist/types/src/shopify-values.d.ts +46 -0
  113. package/dist/types/src/status.d.ts +33 -0
  114. package/dist/types/src/storage.d.ts +12 -0
  115. package/dist/types/src/store-html.d.ts +39 -0
  116. package/dist/types/src/storefront/catalog.d.ts +38 -0
  117. package/dist/types/src/storefront/json-ld.d.ts +20 -0
  118. package/dist/types/src/variant-render.d.ts +14 -0
  119. package/package.json +24 -24
  120. package/dist/esm/chunks/account-C3NP6HFB.js +0 -1
  121. package/dist/esm/chunks/account-pages-WLK374FO.js +0 -1
  122. package/dist/esm/chunks/analytics-G3SAWMNL.js +0 -1
  123. package/dist/esm/chunks/chunk-27PJJZGC.js +0 -1
  124. package/dist/esm/chunks/chunk-2FSXS55U.js +0 -1
  125. package/dist/esm/chunks/chunk-FCSWQDVV.js +0 -1
  126. package/dist/esm/chunks/chunk-FOMSMIAM.js +0 -1
  127. package/dist/esm/chunks/chunk-GZJTCLLW.js +0 -1
  128. package/dist/esm/chunks/chunk-HEQILZVR.js +0 -1
  129. package/dist/esm/chunks/chunk-JOOEXYF3.js +0 -4
  130. package/dist/esm/chunks/chunk-MPTQYLSM.js +0 -1
  131. package/dist/esm/chunks/chunk-NEHZ37TU.js +0 -3
  132. package/dist/esm/chunks/chunk-V74KGEJF.js +0 -1
  133. package/dist/esm/chunks/chunk-VT4CDUJJ.js +0 -1
  134. package/dist/esm/chunks/chunk-YJLWX2HK.js +0 -1
  135. package/dist/esm/chunks/diagnose-6VVGZNTY.js +0 -14
  136. package/dist/esm/chunks/extras-GJVRDFM3.js +0 -1
  137. package/dist/esm/chunks/markets-ui-UNB4PTA7.js +0 -1
  138. package/dist/esm/chunks/search-OU75RNQL.js +0 -1
  139. package/src/account/addresses-view.js +0 -92
  140. package/src/account/browser-session.js +0 -359
  141. package/src/account/markup.js +0 -48
  142. package/src/account/operations.js +0 -180
  143. package/src/account/order-lines.js +0 -96
  144. package/src/account/order-list.js +0 -91
  145. package/src/account/order-page.js +0 -192
  146. package/src/account/page-events.js +0 -288
  147. package/src/account/server-client.js +0 -172
  148. package/src/account/shared.js +0 -111
  149. package/src/account/shopify-auth.js +0 -150
  150. package/src/account/subscriptions-view.js +0 -158
  151. package/src/account-api.js +0 -118
  152. package/src/account-links.js +0 -35
  153. package/src/account-pages.js +0 -89
  154. package/src/account.js +0 -342
  155. package/src/amounts.js +0 -36
  156. package/src/analytics-shopify.js +0 -241
  157. package/src/analytics.js +0 -352
  158. package/src/api-version.js +0 -85
  159. package/src/astro.js +0 -266
  160. package/src/cart/checkout.js +0 -97
  161. package/src/cart/drawer.js +0 -138
  162. package/src/cart/mutations.js +0 -234
  163. package/src/cart/render.js +0 -294
  164. package/src/cart/store.js +0 -326
  165. package/src/cart.js +0 -391
  166. package/src/client.js +0 -267
  167. package/src/collection.js +0 -183
  168. package/src/config.js +0 -349
  169. package/src/controls/cart-controls.js +0 -155
  170. package/src/controls/discount.js +0 -96
  171. package/src/controls/extras.js +0 -63
  172. package/src/controls/navigation.js +0 -83
  173. package/src/diagnose-markup.js +0 -333
  174. package/src/diagnose.js +0 -295
  175. package/src/dom.js +0 -287
  176. package/src/extras.js +0 -145
  177. package/src/fragments.js +0 -237
  178. package/src/images.js +0 -104
  179. package/src/index.js +0 -353
  180. package/src/initial.js +0 -87
  181. package/src/lazy-iife.js +0 -116
  182. package/src/lazy.js +0 -39
  183. package/src/loader.js +0 -337
  184. package/src/market-codes.js +0 -35
  185. package/src/markets-ui.js +0 -204
  186. package/src/markets.js +0 -334
  187. package/src/markup.js +0 -64
  188. package/src/mounting.js +0 -277
  189. package/src/page-market.js +0 -116
  190. package/src/parts/account-pages.js +0 -6
  191. package/src/parts/account.js +0 -6
  192. package/src/parts/analytics.js +0 -6
  193. package/src/parts/diagnose.js +0 -6
  194. package/src/parts/extras.js +0 -6
  195. package/src/parts/markets.js +0 -6
  196. package/src/parts/search.js +0 -6
  197. package/src/presets/barba.js +0 -190
  198. package/src/product-events.js +0 -130
  199. package/src/product-render.js +0 -257
  200. package/src/product-state.js +0 -318
  201. package/src/product.js +0 -344
  202. package/src/repeaters.js +0 -173
  203. package/src/search.js +0 -323
  204. package/src/selling-plans.js +0 -154
  205. package/src/server.js +0 -215
  206. package/src/shopify-values.js +0 -89
  207. package/src/status.js +0 -126
  208. package/src/store-html.js +0 -0
  209. package/src/storefront/catalog.js +0 -199
  210. package/src/storefront/json-ld.js +0 -167
  211. package/src/variant-render.js +0 -165
  212. package/types/astro.d.ts +0 -28
  213. package/types/barba.d.ts +0 -16
  214. package/types/index.d.ts +0 -577
  215. package/types/server.d.ts +0 -182
@@ -1,116 +0,0 @@
1
- /**
2
- * The market the page uses: a page built for one market (`<meta name="tether-page-country">`) wins, then the visitor's
3
- * saved or detected market, then the configured one. Init applies it to `cfg` once. Astro's ClientRouter keeps the
4
- * script running across pages, so each arrival asks again: the next page may be built for another market, or for none.
5
- */
6
- import { cfg, log, reportError } from './config.js';
7
- import { loadPart } from './lazy.js';
8
- import { toCountry, toLanguage } from './market-codes.js';
9
- import { readSavedMarket } from './markets.js';
10
- /** @import { TetherOptions } from './config.js' */
11
- /** @import { Market, DetectedMarket } from './markets.js' */
12
- /** @import { SetCountryOptions } from './markets-ui.js' */
13
- /** @import { ShopifyCart } from './cart/store.js' */
14
-
15
- /**
16
- * @typedef {object} ConfiguredMarket The market of the config, before a page's or visitor's market applied.
17
- * @property {string | undefined} country
18
- * @property {string | undefined} language
19
- * @property {string | undefined} locale
20
- */
21
-
22
- /** @type {ConfiguredMarket} */
23
- let configuredMarket = { country: undefined, language: undefined, locale: undefined };
24
-
25
- /** Whether the page arrived at last was built for its own market, which the next page without one switches back from. */
26
- let lastPageHadOwnMarket = false;
27
-
28
- /**
29
- * `<meta name="tether-page-country" content="DE">`, with `tether-page-language` and `tether-page-locale`. Never
30
- * remembered: it belongs to the page, not to the visitor.
31
- *
32
- * @returns {Market | null} `null` when the page has no valid country of its own.
33
- */
34
- function pageMarket() {
35
- /** @param {string} name */
36
- const meta = (name) =>
37
- document.querySelector(`meta[name="${cfg.prefix}-page-${name}"]`)?.getAttribute('content') || undefined;
38
- const country = toCountry(meta('country'));
39
- if (!country) return null;
40
- return { country, language: toLanguage(meta('language')) ?? undefined, locale: meta('locale') };
41
- }
42
-
43
- /**
44
- * Without `markets.detect` only the visitor's saved choice applies, read without loading the markets part.
45
- *
46
- * @returns {Promise<Market | DetectedMarket | null>}
47
- */
48
- async function visitorMarket() {
49
- const markets = cfg.markets ?? {};
50
- if (markets.detect === false) return null;
51
- if (!markets.detect) return markets.remember === false ? null : readSavedMarket();
52
- try {
53
- return (await loadPart('markets')).detectMarket(markets);
54
- } catch (error) {
55
- reportError(error, 'markets');
56
- return null;
57
- }
58
- }
59
-
60
- /**
61
- * Sets `cfg`'s market for the first page, over the configured one.
62
- *
63
- * @param {TetherOptions} overrides What `init()` was called with: a `locale` there wins over the visitor's.
64
- * @returns {Promise<void>}
65
- */
66
- export async function applyStartMarket(overrides) {
67
- configuredMarket = { country: cfg.country, language: cfg.language, locale: cfg.locale };
68
- const page = pageMarket();
69
- lastPageHadOwnMarket = page !== null;
70
- if (page) {
71
- cfg.country = page.country;
72
- cfg.language = page.language;
73
- if (page.locale) cfg.locale = page.locale;
74
- return;
75
- }
76
- const market = await visitorMarket();
77
- if (!market) return;
78
- if (market.country) cfg.country = market.country;
79
- if (market.language) cfg.language = market.language;
80
- if (market.locale && !overrides.locale) cfg.locale = market.locale;
81
- log('market', market);
82
- }
83
-
84
- /**
85
- * @param {string | undefined} a
86
- * @param {string | undefined} b
87
- */
88
- const sameCode = (a, b) => (a ?? '').toUpperCase() === (b ?? '').toUpperCase();
89
-
90
- /**
91
- * The market to switch to on arriving at the current page. A page built for a market switches to it; the first page
92
- * without one after such a page switches back to the visitor's own choice, else to the configured market.
93
- *
94
- * @returns {{ country: string, language: string | undefined, locale: string | undefined } | null} `null` when the page
95
- * stays in the market in use.
96
- */
97
- export function marketForArrival() {
98
- const page = pageMarket();
99
- const leftOwnMarket = page === null && lastPageHadOwnMarket;
100
- lastPageHadOwnMarket = page !== null;
101
- const target = page ?? (leftOwnMarket ? (readSavedMarket() ?? configuredMarket) : null);
102
- if (!target?.country) return null;
103
- if (sameCode(target.country, cfg.country) && sameCode(target.language, cfg.language)) return null;
104
- return { country: target.country, language: target.language, locale: target.locale };
105
- }
106
-
107
- /**
108
- * `Tether.setCountry('DE')`: loads the markets part on first use.
109
- *
110
- * @param {string} code
111
- * @param {SetCountryOptions} [options]
112
- * @returns {Promise<ShopifyCart | null>} The cart once everything is in the new market.
113
- */
114
- export function setCountry(code, options) {
115
- return loadPart('markets').then((part) => part.setCountry(code, options));
116
- }
@@ -1,6 +0,0 @@
1
- // The <script> build's tether.account-pages.js: registers the part where the core script's loader (lazy-iife.js) finds it.
2
- import * as part from '../account-pages.js';
3
-
4
- const { Tether } = /** @type {Window & { Tether?: { __parts: Record<string, unknown> } }} */ (window);
5
- if (!Tether) throw new Error('tether.account-pages.js runs after the Tether script, which loads it');
6
- Tether.__parts['account-pages'] = part;
@@ -1,6 +0,0 @@
1
- // The <script> build's tether.account.js: registers the part where the core script's loader (lazy-iife.js) finds it.
2
- import * as part from '../account.js';
3
-
4
- const { Tether } = /** @type {Window & { Tether?: { __parts: Record<string, unknown> } }} */ (window);
5
- if (!Tether) throw new Error('tether.account.js runs after the Tether script, which loads it');
6
- Tether.__parts['account'] = part;
@@ -1,6 +0,0 @@
1
- // The <script> build's tether.analytics.js: registers the part where the core script's loader (lazy-iife.js) finds it.
2
- import * as part from '../analytics.js';
3
-
4
- const { Tether } = /** @type {Window & { Tether?: { __parts: Record<string, unknown> } }} */ (window);
5
- if (!Tether) throw new Error('tether.analytics.js runs after the Tether script, which loads it');
6
- Tether.__parts['analytics'] = part;
@@ -1,6 +0,0 @@
1
- // The <script> build's tether.diagnose.js: registers the part where the core script's loader (lazy-iife.js) finds it.
2
- import * as part from '../diagnose.js';
3
-
4
- const { Tether } = /** @type {Window & { Tether?: { __parts: Record<string, unknown> } }} */ (window);
5
- if (!Tether) throw new Error('tether.diagnose.js runs after the Tether script, which loads it');
6
- Tether.__parts['diagnose'] = part;
@@ -1,6 +0,0 @@
1
- // The <script> build's tether.extras.js: registers the part where the core script's loader (lazy-iife.js) finds it.
2
- import * as part from '../extras.js';
3
-
4
- const { Tether } = /** @type {Window & { Tether?: { __parts: Record<string, unknown> } }} */ (window);
5
- if (!Tether) throw new Error('tether.extras.js runs after the Tether script, which loads it');
6
- Tether.__parts['extras'] = part;
@@ -1,6 +0,0 @@
1
- // The <script> build's tether.markets.js: registers the part where the core script's loader (lazy-iife.js) finds it.
2
- import * as part from '../markets-ui.js';
3
-
4
- const { Tether } = /** @type {Window & { Tether?: { __parts: Record<string, unknown> } }} */ (window);
5
- if (!Tether) throw new Error('tether.markets.js runs after the Tether script, which loads it');
6
- Tether.__parts['markets'] = part;
@@ -1,6 +0,0 @@
1
- // The <script> build's tether.search.js: registers the part where the core script's loader (lazy-iife.js) finds it.
2
- import * as part from '../search.js';
3
-
4
- const { Tether } = /** @type {Window & { Tether?: { __parts: Record<string, unknown> } }} */ (window);
5
- if (!Tether) throw new Error('tether.search.js runs after the Tether script, which loads it');
6
- Tether.__parts['search'] = part;
@@ -1,190 +0,0 @@
1
- /**
2
- * Preset for sites with page transitions (Barba, Swup), smooth scroll (Lenis) and GSAP. Nothing is read from globals
3
- * except GSAP and ScrollTrigger by default: pass in what your site has.
4
- *
5
- * import Tether from "@occluze/tether";
6
- * import { barbaPreset } from "@occluze/tether/presets/barba";
7
- *
8
- * Tether.init({
9
- * ...window.TETHER_CONFIG,
10
- * ...barbaPreset({
11
- * lenis: () => myLenis, // resized after async renders
12
- * lockScroll: (drawer) => myLock.lock(drawer), // when the cart opens
13
- * unlockScroll: () => myLock.unlock(), // when the cart closes
14
- * onValueChange: (el, previous) => {}, // e.g. a text scramble
15
- * }),
16
- * });
17
- *
18
- * // once per page container, e.g. in Barba's beforeEnter AND once():
19
- * Tether.mount(container);
20
- * // when leaving a page:
21
- * Tether.unmount();
22
- */
23
- /** @import { TetherConfig } from '../config.js' */
24
-
25
- /**
26
- * GSAP isn't a dependency, so the two calls this preset makes are described here.
27
- *
28
- * @typedef {Record<string, string | number | (() => void)>} TweenVars
29
- * @typedef {object} Gsap
30
- * @property {(target: HTMLElement, from: TweenVars, to: TweenVars) => unknown} fromTo
31
- * @property {(target: HTMLElement, to: TweenVars) => unknown} to
32
- */
33
-
34
- /**
35
- * @typedef {object} BarbaPresetOptions Each getter may also be the value itself.
36
- * @property {() => unknown} [gsap] Default `window.gsap`.
37
- * @property {(() => unknown) | null} [lenis] Resized after async renders.
38
- * @property {() => unknown} [scrollTrigger] Default `window.ScrollTrigger`, refreshed after async renders.
39
- * @property {((drawer: HTMLElement) => void) | null} [lockScroll] When the cart opens.
40
- * @property {(() => void) | null} [unlockScroll] When the cart closes.
41
- * @property {(() => void) | null} [onResize] After a cart line animated in or out.
42
- * @property {((el: HTMLElement, previousText: string) => void) | null} [onValueChange]
43
- * @property {boolean} [animateLines] Animate cart lines in and out (default true).
44
- * @property {number} [lineDuration] Seconds.
45
- */
46
-
47
- /**
48
- * @typedef {Pick<TetherConfig, 'autoMount' | 'onLayoutChange' | 'onCartOpen' | 'onCartClose' | 'onLineEnter'
49
- * | 'onLineLeave' | 'onValueChange'>} BarbaPresetConfig
50
- */
51
-
52
- const LINE_DURATION_SECONDS = 0.3;
53
- /** @returns {TweenVars} A new object each time: GSAP writes into the vars it is given. */
54
- const collapsed = () => ({
55
- height: 0,
56
- paddingTop: 0,
57
- paddingBottom: 0,
58
- borderBottomWidth: 0,
59
- opacity: 0,
60
- overflow: 'hidden',
61
- });
62
- const ANIMATED_PROPS = 'height,paddingTop,paddingBottom,borderBottomWidth,opacity,overflow';
63
-
64
- const reducedMotion = () =>
65
- typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
66
-
67
- /**
68
- * @param {unknown} option
69
- * @returns {unknown} What a getter option returns, or the option itself when it is the value.
70
- */
71
- function current(option) {
72
- if (typeof option !== 'function') return option;
73
- // Every function option of this preset is a getter that takes no arguments.
74
- const getter = /** @type {() => unknown} */ (option);
75
- return getter();
76
- }
77
-
78
- /**
79
- * Calls `target[method]()` when it has one: Lenis and ScrollTrigger are optional and versions differ.
80
- *
81
- * @param {unknown} target
82
- * @param {string} method
83
- */
84
- function callIfPresent(target, method) {
85
- if (typeof target !== 'object' || target === null) return;
86
- /** @type {unknown} */
87
- const fn = Reflect.get(target, method);
88
- // A method of `target`, called on it.
89
- if (typeof fn === 'function') /** @type {(this: unknown) => unknown} */ (fn).call(target);
90
- }
91
-
92
- /**
93
- * @param {unknown} value
94
- * @returns {value is Gsap}
95
- */
96
- const isGsap = (value) =>
97
- typeof value === 'object' &&
98
- value !== null &&
99
- typeof Reflect.get(value, 'fromTo') === 'function' &&
100
- typeof Reflect.get(value, 'to') === 'function';
101
-
102
- /**
103
- * Hooks for sites with page transitions, smooth scroll and GSAP: spread into `Tether.init()`.
104
- *
105
- * @param {BarbaPresetOptions} [options]
106
- * @returns {BarbaPresetConfig}
107
- */
108
- export function barbaPreset(options = {}) {
109
- const {
110
- gsap = () => /** @type {unknown} */ (Reflect.get(window, 'gsap')),
111
- lenis = null,
112
- scrollTrigger = () => /** @type {unknown} */ (Reflect.get(window, 'ScrollTrigger')),
113
- lockScroll = null,
114
- unlockScroll = null,
115
- onResize = null,
116
- onValueChange = null,
117
- animateLines = true,
118
- lineDuration = LINE_DURATION_SECONDS,
119
- } = options;
120
-
121
- /** Whether this preset holds the scroll lock, so a modal's own lock stays intact. */
122
- let locked = false;
123
-
124
- /**
125
- * @param {HTMLElement} node
126
- * @returns {Gsap | null} GSAP, when this line should animate.
127
- */
128
- const animator = (node) => {
129
- if (!animateLines || reducedMotion() || node.offsetHeight <= 0) return null;
130
- const animation = current(gsap);
131
- return isGsap(animation) ? animation : null;
132
- };
133
-
134
- return {
135
- autoMount: false,
136
-
137
- onLayoutChange() {
138
- callIfPresent(current(lenis), 'resize');
139
- callIfPresent(current(scrollTrigger), 'refresh');
140
- },
141
-
142
- onCartOpen(drawers) {
143
- const drawer = drawers[0];
144
- if (locked || typeof lockScroll !== 'function' || !drawer) return;
145
- lockScroll(drawer);
146
- locked = true;
147
- },
148
-
149
- onCartClose() {
150
- if (locked && typeof unlockScroll === 'function') unlockScroll();
151
- locked = false;
152
- },
153
-
154
- onLineEnter(node) {
155
- const animation = animator(node);
156
- if (!animation) return;
157
- const style = getComputedStyle(node);
158
- animation.fromTo(node, collapsed(), {
159
- height: node.offsetHeight,
160
- paddingTop: style.paddingTop,
161
- paddingBottom: style.paddingBottom,
162
- borderBottomWidth: style.borderBottomWidth,
163
- opacity: 1,
164
- duration: lineDuration,
165
- clearProps: ANIMATED_PROPS,
166
- onComplete: () => onResize?.(),
167
- });
168
- },
169
-
170
- onLineLeave(node, done) {
171
- const animation = animator(node);
172
- if (!animation) {
173
- done();
174
- return;
175
- }
176
- animation.to(node, {
177
- ...collapsed(),
178
- duration: lineDuration,
179
- onComplete: () => {
180
- done();
181
- onResize?.();
182
- },
183
- });
184
- },
185
-
186
- onValueChange: typeof onValueChange === 'function' ? onValueChange : null,
187
- };
188
- }
189
-
190
- export default barbaPreset;
@@ -1,130 +0,0 @@
1
- /**
2
- * A product scope's clicks and changes: option and plan buttons, purchase toggles, quantity steppers, the selects and
3
- * add to cart. One delegated listener of each kind on the root, so cards a list re-renders need no new listeners.
4
- */
5
- import { reportError } from './config.js';
6
- import { sel, get, emit, setText, busy } from './dom.js';
7
- import { addButtonLabel } from './variant-render.js';
8
- /** @import { ProductScope } from './product.js' */
9
-
10
- const productSelector = () => sel('product');
11
-
12
- /**
13
- * @param {Element} el
14
- * @returns {el is HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement}
15
- */
16
- const isField = (el) => el.tagName === 'INPUT' || el.tagName === 'SELECT' || el.tagName === 'TEXTAREA';
17
-
18
- /**
19
- * @param {EventTarget | null} target
20
- * @returns {target is Element}
21
- */
22
- const isElement = (target) => target instanceof window.Element;
23
-
24
- /**
25
- * @param {ProductScope} scope
26
- * @param {Element} target
27
- * @param {string} selector
28
- * @returns {HTMLElement | null} The nearest `selector` around `target` that belongs to the scope, not to a nested one.
29
- * Typed as an HTML element, as `qsa` types its matches.
30
- */
31
- function closestOwn(scope, target, selector) {
32
- const el = /** @type {HTMLElement | null} */ (target.closest(selector));
33
- return el?.closest(productSelector()) === scope.root ? el : null;
34
- }
35
-
36
- /**
37
- * Adds to the cart with `aria-busy` on the button and `tether-adding-text` as its label meanwhile.
38
- *
39
- * @param {ProductScope} scope
40
- * @param {HTMLElement} button
41
- */
42
- function onAddClick(scope, button) {
43
- const { label, original } = addButtonLabel(button);
44
- const addingText = get(button, 'adding-text');
45
- void busy(button, async () => {
46
- if (addingText) setText(label, addingText);
47
- try {
48
- await scope.addToCart();
49
- } catch (error) {
50
- reportError(error, 'add to cart failed');
51
- emit('error', { error, context: 'add-to-cart' }, scope.root);
52
- } finally {
53
- if (addingText) setText(label, original);
54
- }
55
- }).then(() => {
56
- // The mode-dependent labels (tether-subscribe-text, tether-soldout-text) wait for aria-busy to clear.
57
- scope.renderVariant();
58
- });
59
- }
60
-
61
- /**
62
- * @param {ProductScope} scope
63
- * @param {Event} event
64
- * @param {Element} target
65
- */
66
- function onClick(scope, event, target) {
67
- const optionValue = closestOwn(scope, target, sel('option-value'));
68
- const optionList = optionValue?.closest(sel('option-list'));
69
- if (optionValue && optionList) {
70
- event.preventDefault();
71
- scope.select(scope.optName(get(optionList, 'option-list')), get(optionValue, 'option-value') ?? '');
72
- return;
73
- }
74
- const plan = closestOwn(scope, target, sel('plan-value'));
75
- if (plan) {
76
- event.preventDefault();
77
- scope.selectPlan(get(plan, 'plan-value'));
78
- return;
79
- }
80
- const purchase = closestOwn(scope, target, `${sel('purchase')}:not(select)`);
81
- if (purchase) {
82
- // A radio or checkbox input keeps its own click, so it shows checked.
83
- if (purchase.tagName !== 'INPUT') event.preventDefault();
84
- scope.setPurchase(get(purchase, 'purchase'));
85
- return;
86
- }
87
- const plus = closestOwn(scope, target, sel('qty-plus'));
88
- const minus = plus ? null : closestOwn(scope, target, sel('qty-minus'));
89
- if (plus || minus) {
90
- event.preventDefault();
91
- scope.setQuantity(scope.quantity + (plus ? 1 : -1));
92
- return;
93
- }
94
- const add = closestOwn(scope, target, sel('add-to-cart'));
95
- if (add) {
96
- event.preventDefault();
97
- onAddClick(scope, add);
98
- }
99
- }
100
-
101
- /**
102
- * @param {ProductScope} scope
103
- * @param {Element} target A form field in the scope.
104
- */
105
- function onChange(scope, target) {
106
- const value = isField(target) ? target.value : '';
107
- if (target.matches(`select${sel('option')}`)) {
108
- scope.select(scope.optName(get(target, 'option')), value);
109
- } else if (target.matches(`select${sel('variant-select')}`)) {
110
- scope.selectVariant(value);
111
- } else if (target.matches(`select${sel('plan')}`)) {
112
- if (value) scope.selectPlan(value);
113
- else scope.setPurchase('one-time');
114
- } else if (target.matches(`select${sel('purchase')}`)) {
115
- scope.setPurchase(value);
116
- } else if (target.matches(sel('qty'))) {
117
- scope.setQuantity(value);
118
- }
119
- }
120
-
121
- /** @param {ProductScope} scope */
122
- export function bindProductEvents(scope) {
123
- scope.root.addEventListener('click', (event) => {
124
- if (isElement(event.target)) onClick(scope, event, event.target);
125
- });
126
- scope.root.addEventListener('change', (event) => {
127
- const { target } = event;
128
- if (isElement(target) && target.closest(productSelector()) === scope.root) onChange(scope, target);
129
- });
130
- }