@luxfi/core 5.1.5 → 5.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. package/commerce/{AUTO-GEN-bullion-by-family.json → data/AUTO-GEN-bullion-by-family.json} +33 -33
  2. package/commerce/{EDIT-ME-bullion-market-prices.ts → data/EDIT-ME-bullion-market-prices.ts} +11 -11
  3. package/commerce/{assign-prices.ts → data/assign-prices.ts} +49 -49
  4. package/commerce/{assign-videos-by-family-group.ts → data/assign-videos-by-family-group.ts} +14 -14
  5. package/commerce/{bullion-price-1oz.ts → data/bullion-price-1oz.ts} +5 -5
  6. package/commerce/{index.ts → data/index.ts} +18 -18
  7. package/commerce/ui/conf.ts +13 -0
  8. package/commerce/ui/context.tsx +102 -0
  9. package/commerce/ui/store.ts +277 -0
  10. package/components/access-code-input.tsx +71 -71
  11. package/components/auth/auth-listener.tsx +29 -29
  12. package/components/auth/auth-token/clear-auth-token.tsx +12 -12
  13. package/components/auth/auth-token/set-auth-token.tsx +16 -16
  14. package/components/auth/common-auth-domains.ts +16 -16
  15. package/components/auth/login-panel.tsx +107 -104
  16. package/components/chat-widget.tsx +85 -80
  17. package/components/commerce/add-widget.tsx +20 -0
  18. package/components/commerce/bag-button.tsx +98 -98
  19. package/components/commerce/buy-button.tsx +34 -0
  20. package/components/commerce/checkout-button.tsx +129 -116
  21. package/components/commerce/checkout-panel/close-button.tsx +26 -26
  22. package/components/commerce/checkout-panel/dt-bag-carousel.tsx +36 -36
  23. package/components/commerce/checkout-panel/dt-checkout-panel.tsx +66 -66
  24. package/components/commerce/checkout-panel/index.tsx +129 -124
  25. package/components/commerce/checkout-panel/links-row.tsx +21 -21
  26. package/components/commerce/checkout-panel/mb-checkout-panel.tsx +54 -54
  27. package/components/commerce/checkout-panel/steps-indicator.tsx +39 -39
  28. package/components/commerce/checkout-panel/thank-you.tsx +18 -18
  29. package/components/commerce/checkout-widget/const.ts +13 -13
  30. package/components/commerce/checkout-widget/index.tsx +192 -86
  31. package/components/commerce/checkout-widget/obs-string-set.ts +48 -48
  32. package/components/commerce/checkout-widget/use-anim-clx-set.ts +58 -56
  33. package/components/commerce/desktop-bag-popup.tsx +78 -78
  34. package/components/commerce/drawer/index.tsx +115 -0
  35. package/components/commerce/drawer/micro.tsx +144 -0
  36. package/components/commerce/drawer/shell.tsx +83 -0
  37. package/components/commerce/mobile-bag-drawer.tsx +51 -51
  38. package/components/commerce/mobile-login-button.tsx +100 -100
  39. package/components/commerce/mobile-menu-toggle-button.tsx +35 -35
  40. package/components/commerce/mobile-nav-menu-ai.tsx +97 -97
  41. package/components/commerce/mobile-nav-menu-item.tsx +45 -45
  42. package/components/commerce/mobile-nav-menu.tsx +80 -79
  43. package/components/contact-dialog/contact-form.tsx +113 -112
  44. package/components/contact-dialog/disclaimer.tsx +13 -13
  45. package/components/contact-dialog/index.tsx +64 -64
  46. package/components/copyright.tsx +21 -21
  47. package/components/drawer-margin.tsx +25 -0
  48. package/components/footer.tsx +77 -77
  49. package/components/header/desktop.tsx +54 -54
  50. package/components/header/index.tsx +40 -47
  51. package/components/header/mobile.tsx +165 -165
  52. package/components/header/theme-toggle.tsx +26 -26
  53. package/components/icons/avatar.tsx +11 -11
  54. package/components/icons/bag-icon.tsx +10 -10
  55. package/components/icons/github.tsx +14 -14
  56. package/components/icons/index.tsx +43 -43
  57. package/components/icons/left-arrow.tsx +11 -11
  58. package/components/icons/lux-logo.tsx +10 -10
  59. package/components/icons/right-arrow.tsx +10 -10
  60. package/components/icons/search.tsx +12 -12
  61. package/components/icons/secure-delivery.tsx +13 -13
  62. package/components/icons/social-icon.tsx +35 -35
  63. package/components/icons/social-svg.css +3 -3
  64. package/components/icons/youtube-logo.tsx +59 -59
  65. package/components/index.ts +25 -27
  66. package/components/logo.tsx +81 -81
  67. package/components/main.tsx +27 -0
  68. package/components/mini-chart/index.tsx +7 -7
  69. package/components/mini-chart/mini-chart-props.ts +43 -43
  70. package/components/mini-chart/mini-chart.tsx +85 -85
  71. package/components/mini-chart/wrapper.tsx +23 -23
  72. package/components/not-found/index.tsx +28 -27
  73. package/components/not-found/not-found-content.mdx +5 -5
  74. package/components/scripts.tsx +24 -24
  75. package/conf/index.ts +52 -50
  76. package/{commerce/lux-service-options.ts → conf/lux-commerce-options.ts} +6 -6
  77. package/environment.d.ts +5 -5
  78. package/next/analytics/fpixel.ts +15 -15
  79. package/next/analytics/google-analytics.ts +13 -13
  80. package/next/analytics/index.ts +3 -3
  81. package/next/analytics/pixel-analytics.tsx +54 -54
  82. package/next/font/get-app-router-font-classes.ts +12 -12
  83. package/next/font/load-and-return-lux-next-fonts-on-import.ts +68 -68
  84. package/next/font/next-font-desc.ts +27 -27
  85. package/next/font/pages-router-font-vars.tsx +18 -18
  86. package/next/head-metadata/from-next/metadata-types.ts +158 -158
  87. package/next/head-metadata/from-next/opengraph-types.ts +267 -267
  88. package/next/head-metadata/from-next/twitter-types.ts +92 -92
  89. package/next/head-metadata/index.tsx +208 -208
  90. package/next/middleware/determine-device-mw.ts +16 -16
  91. package/package.json +80 -73
  92. package/root-layout/WHY_THIS_IS_SEPARATE.txt +1 -1
  93. package/root-layout/index.tsx +118 -121
  94. package/server-actions/firebase-app.ts +14 -14
  95. package/server-actions/index.ts +5 -5
  96. package/server-actions/store-contact.ts +51 -51
  97. package/site-def/footer/community.tsx +67 -67
  98. package/site-def/footer/company.ts +37 -37
  99. package/site-def/footer/ecosystem.ts +37 -37
  100. package/site-def/footer/index.tsx +26 -26
  101. package/site-def/footer/legal.ts +28 -28
  102. package/site-def/footer/network.ts +45 -45
  103. package/site-def/footer/svg/warpcast-logo.svg +11 -11
  104. package/site-def/index.ts +2 -2
  105. package/site-def/main-nav.tsx +292 -292
  106. package/style/cart-animation.css +29 -29
  107. package/style/checkout-animation.css +23 -23
  108. package/style/drawer-handle-overrides.css +160 -0
  109. package/style/lux-colors.css +85 -85
  110. package/style/lux-global.css +30 -30
  111. package/tailwind/fontFamily.tailwind.lux.ts +18 -18
  112. package/tailwind/index.ts +2 -2
  113. package/tailwind/lux-tw-fonts.ts +39 -39
  114. package/tailwind/tailwind.config.lux-preset.ts +10 -10
  115. package/tsconfig.json +15 -10
  116. package/types/chatbot-config.ts +6 -6
  117. package/types/chatbot-suggested-question.ts +7 -7
  118. package/types/commerce-config.ts +10 -10
  119. package/types/contact-info.ts +10 -10
  120. package/types/index.ts +5 -5
  121. package/types/site-def.ts +45 -45
  122. package/components/commerce/buy-drawer/drawer.tsx +0 -44
  123. package/components/commerce/buy-drawer/index.tsx +0 -46
  124. package/components/commerce/checkout-widget/use-lagging-item-ref.ts +0 -30
  125. package/components/header/guts.tsx +0 -27
@@ -1,34 +1,34 @@
1
- [
2
- {
3
- "id": "LXM-AG-B",
4
- "title": "Minted Bar",
5
- "parentTitle": "Lux Silver",
6
- "desc": "Get unprecedented access to silver with 1:1 asset-backed Lux Silver NFTs, sovereign ownership of physical silver without management fees, and mine-direct discount pricing.",
7
- "img": {
8
- "src": "/assets/commerce/silver/product/silver-bar-pt-800x800.png",
9
- "dim": {
10
- "w": 800,
11
- "h": 800
12
- }
13
- },
14
- "products": [
15
- {
16
- "id": "fce17569-a86c-4f69-bc61-ab435ed0c46f",
17
- "sku": "LXM-AG-B-1_OZ",
18
- "fullTitle": "Lux Silver, 1oz Minted Bar",
19
- "optionLabel": "1oz",
20
- "familyTitle": "Minted Bar",
21
- "familyId": "LXM-AG-B",
22
- "desc": "Get unprecedented access to silver with 1:1 asset-backed Lux Silver NFTs, sovereign ownership of physical silver without management fees, and mine-direct discount pricing.",
23
- "price": 0,
24
- "img": {
25
- "src": "/assets/commerce/silver/product/silver-bar-pt-800x800.png",
26
- "dim": {
27
- "w": 800,
28
- "h": 800
29
- }
30
- }
31
- }
32
- ]
33
- }
1
+ [
2
+ {
3
+ "id": "LXM-AG-B",
4
+ "title": "Minted Bar",
5
+ "parentTitle": "Lux Silver",
6
+ "desc": "Get unprecedented access to silver with 1:1 asset-backed Lux Silver NFTs, sovereign ownership of physical silver without management fees, and mine-direct discount pricing.",
7
+ "img": {
8
+ "src": "/assets/commerce/silver/product/silver-bar-pt-800x800.png",
9
+ "dim": {
10
+ "w": 800,
11
+ "h": 800
12
+ }
13
+ },
14
+ "products": [
15
+ {
16
+ "id": "fce17569-a86c-4f69-bc61-ab435ed0c46f",
17
+ "sku": "LXM-AG-B-1_OZ",
18
+ "fullTitle": "Lux Silver, 1oz Minted Bar",
19
+ "optionLabel": "1oz",
20
+ "familyTitle": "Minted Bar",
21
+ "familyId": "LXM-AG-B",
22
+ "desc": "Get unprecedented access to silver with 1:1 asset-backed Lux Silver NFTs, sovereign ownership of physical silver without management fees, and mine-direct discount pricing.",
23
+ "price": 0,
24
+ "img": {
25
+ "src": "/assets/commerce/silver/product/silver-bar-pt-800x800.png",
26
+ "dim": {
27
+ "w": 800,
28
+ "h": 800
29
+ }
30
+ }
31
+ }
32
+ ]
33
+ }
34
34
  ]
@@ -1,12 +1,12 @@
1
- // updated manually apr 29, 2024
2
-
3
- export default {
4
- au: {
5
- market1oz: 2336.50,
6
- discount: 0.01
7
- },
8
- ag: {
9
- market1oz: 27.143,
10
- discount: 0.10
11
- },
1
+ // updated manually apr 29, 2024
2
+
3
+ export default {
4
+ au: {
5
+ market1oz: 2336.50,
6
+ discount: 0.01
7
+ },
8
+ ag: {
9
+ market1oz: 27.143,
10
+ discount: 0.10
11
+ },
12
12
  }
@@ -1,50 +1,50 @@
1
- import type { Family } from '@hanzo/commerce/types'
2
-
3
- import prices from './EDIT-ME-bullion-market-prices'
4
-
5
- const sep = {
6
- tok: '-',
7
- subTok: '_',
8
- decimal: '.'
9
- }
10
-
11
- const GRAMS_PER_OZ = 28.3495
12
-
13
- const tree: any = {}
14
- for (let key in prices) {
15
- const values = prices[key as keyof typeof prices]
16
- tree[key] = {
17
- oz: values.market1oz * (1 - values.discount),
18
- g: values.market1oz * (1 - values.discount) / GRAMS_PER_OZ
19
- }
20
- }
21
-
22
- // LXB-AU-B-1_OZ, LXB-AU-B-2.5_g
23
- const priceFromSKU = (
24
- sku: string
25
- ) => {
26
- const tokens = sku.split(sep.tok)
27
- const type_ = tokens[1].toLowerCase()
28
-
29
- const quanAndUnit = tokens[tokens.length - 1]
30
- const quanAndUnitToks = quanAndUnit.split(sep.subTok)
31
- let quantity = quanAndUnitToks[0].includes(sep.decimal) ? parseFloat(quanAndUnitToks[0].split(sep.decimal).join('.')) : parseInt(quanAndUnitToks[0])
32
- let unit = quanAndUnitToks[1].toLowerCase()
33
- if (unit === 'kg') {
34
- quantity *= 1000
35
- unit = 'g'
36
- }
37
- else if (unit === 'lb') {
38
- quantity *= 16
39
- unit = 'oz'
40
- }
41
-
42
- return tree[type_][unit] * quantity
43
- }
44
-
45
- export default (c: Family): Family => {
46
- for (let prod of c.products) {
47
- prod.price = priceFromSKU(prod.sku)
48
- }
49
- return c
1
+ import type { Family } from '@hanzo/commerce/types'
2
+
3
+ import prices from './EDIT-ME-bullion-market-prices'
4
+
5
+ const sep = {
6
+ tok: '-',
7
+ subTok: '_',
8
+ decimal: '.'
9
+ }
10
+
11
+ const GRAMS_PER_OZ = 28.3495
12
+
13
+ const tree: any = {}
14
+ for (let key in prices) {
15
+ const values = prices[key as keyof typeof prices]
16
+ tree[key] = {
17
+ oz: values.market1oz * (1 - values.discount),
18
+ g: values.market1oz * (1 - values.discount) / GRAMS_PER_OZ
19
+ }
20
+ }
21
+
22
+ // LXB-AU-B-1_OZ, LXB-AU-B-2.5_g
23
+ const priceFromSKU = (
24
+ sku: string
25
+ ) => {
26
+ const tokens = sku.split(sep.tok)
27
+ const type_ = tokens[1].toLowerCase()
28
+
29
+ const quanAndUnit = tokens[tokens.length - 1]
30
+ const quanAndUnitToks = quanAndUnit.split(sep.subTok)
31
+ let quantity = quanAndUnitToks[0].includes(sep.decimal) ? parseFloat(quanAndUnitToks[0].split(sep.decimal).join('.')) : parseInt(quanAndUnitToks[0])
32
+ let unit = quanAndUnitToks[1].toLowerCase()
33
+ if (unit === 'kg') {
34
+ quantity *= 1000
35
+ unit = 'g'
36
+ }
37
+ else if (unit === 'lb') {
38
+ quantity *= 16
39
+ unit = 'oz'
40
+ }
41
+
42
+ return tree[type_][unit] * quantity
43
+ }
44
+
45
+ export default (c: Family): Family => {
46
+ for (let prod of c.products) {
47
+ prod.price = priceFromSKU(prod.sku)
48
+ }
49
+ return c
50
50
  }
@@ -1,14 +1,14 @@
1
- import type { VideoDef } from '@hanzo/ui/types'
2
- import type { Family } from '@hanzo/commerce/types'
3
-
4
- export default (fam: Family, map: Map<string, VideoDef>): Family => {
5
- if (fam.parentTitle) {
6
- for (let prod of fam.products) {
7
- const video = map.get(fam.parentTitle)
8
- if (video) {
9
- prod.video = video
10
- }
11
- }
12
- }
13
- return fam
14
- }
1
+ import type { VideoDef } from '@hanzo/ui/types'
2
+ import type { Family } from '@hanzo/commerce/types'
3
+
4
+ export default (fam: Family, map: Map<string, VideoDef>): Family => {
5
+ if (fam.parentTitle) {
6
+ for (let prod of fam.products) {
7
+ const video = map.get(fam.parentTitle)
8
+ if (video) {
9
+ prod.video = video
10
+ }
11
+ }
12
+ }
13
+ return fam
14
+ }
@@ -1,5 +1,5 @@
1
- import PRICES from './EDIT-ME-bullion-market-prices'
2
-
3
- export default (type_: keyof typeof PRICES): number => (
4
- PRICES[type_].market1oz * (1 - PRICES[type_].discount)
5
- )
1
+ import PRICES from './EDIT-ME-bullion-market-prices'
2
+
3
+ export default (type_: keyof typeof PRICES): number => (
4
+ PRICES[type_].market1oz * (1 - PRICES[type_].discount)
5
+ )
@@ -1,18 +1,18 @@
1
- import type { VideoDef } from '@hanzo/ui/types'
2
-
3
- import bullionByFamily from './AUTO-GEN-bullion-by-family.json'
4
-
5
- import assignPrices from './assign-prices'
6
- import assignVideosByFamilyGroup from './assign-videos-by-family-group'
7
-
8
- export const getBullionFamilies = (videoMap: Map<string, VideoDef>) => (
9
- bullionByFamily.map(
10
- (fam) => (assignPrices(fam))).map(
11
- (fam) => (assignVideosByFamilyGroup(fam, videoMap)
12
- )
13
- )
14
- )
15
-
16
- export { default as serviceOptions } from './lux-service-options'
17
- export { default as bullionPrice1oz } from './bullion-price-1oz'
18
-
1
+ import type { VideoDef } from '@hanzo/ui/types'
2
+
3
+ import bullionByFamily from './AUTO-GEN-bullion-by-family.json'
4
+
5
+ import assignPrices from './assign-prices'
6
+ import assignVideosByFamilyGroup from './assign-videos-by-family-group'
7
+
8
+ export const getBullionFamilies = (videoMap: Map<string, VideoDef>) => (
9
+ bullionByFamily.map(
10
+ (fam) => (assignPrices(fam))).map(
11
+ (fam) => (assignVideosByFamilyGroup(fam, videoMap)
12
+ )
13
+ )
14
+ )
15
+
16
+ export { default as serviceOptions } from '../../conf/lux-commerce-options'
17
+ export { default as bullionPrice1oz } from './bullion-price-1oz'
18
+
@@ -0,0 +1,13 @@
1
+ import type { SnapPointsConfig } from './store'
2
+
3
+ export default {
4
+ mb: {
5
+ micro: '62px',
6
+ full: '550px'
7
+ },
8
+
9
+ dt: {
10
+ micro: '74px',
11
+ full: 0.75
12
+ }
13
+ } satisfies SnapPointsConfig
@@ -0,0 +1,102 @@
1
+ 'use client'
2
+ import React, {
3
+ createContext,
4
+ useContext,
5
+ useRef,
6
+ type PropsWithChildren,
7
+ useEffect,
8
+ useLayoutEffect
9
+ } from 'react'
10
+ import { enableStaticRendering } from 'mobx-react-lite'
11
+ import { usePathname } from 'next/navigation'
12
+ import { useDebounceCallback } from 'usehooks-ts'
13
+
14
+
15
+ import { preset as twConfig } from '@hanzo/ui/tailwind'
16
+ import { useCommerce } from '@hanzo/commerce'
17
+
18
+ import type { CommerceDrawer, SelectAndBuy, RecentActivity } from './store'
19
+ import { CommerceUIStore } from './store'
20
+ import conf from './conf'
21
+
22
+ // https://dev.to/ivandotv/mobx-server-side-rendering-with-next-js-4m18
23
+ enableStaticRendering(typeof window === "undefined")
24
+
25
+ const CommerceUIContext = createContext<CommerceUIStore | undefined>(undefined)
26
+
27
+ const useCommerceDrawer = (): CommerceDrawer => {
28
+ return useContext(CommerceUIContext) as CommerceDrawer
29
+ }
30
+
31
+ const useSelectAndBuy = (): SelectAndBuy => {
32
+ return useContext(CommerceUIContext) as SelectAndBuy
33
+ }
34
+
35
+ const useRecentActivity = (): RecentActivity => {
36
+ return useContext(CommerceUIContext) as RecentActivity
37
+ }
38
+
39
+ const CommerceUIProvider: React.FC<PropsWithChildren> = ({
40
+ children,
41
+ }) => {
42
+
43
+ const cmmc = useCommerce()
44
+ const pathName = usePathname()
45
+ const isCheckout = pathName === '/checkout'
46
+ const ref = useRef<CommerceUIStore>(new CommerceUIStore(cmmc, conf))
47
+
48
+ if (ref.current.checkingOut != isCheckout) {
49
+ ref.current.setCheckingOut(isCheckout)
50
+ }
51
+
52
+ const onResize = () => {
53
+ const width = window.innerWidth
54
+ let desktopMin = 0
55
+ if (twConfig.theme?.screens) {
56
+ // expected form: { md: '768px' }
57
+ if ('md' in twConfig.theme?.screens && typeof twConfig.theme?.screens.md === 'string') {
58
+ desktopMin = parseInt(twConfig.theme?.screens.md)
59
+ }
60
+ if (width < desktopMin) {
61
+ if (!ref.current.isMobile) {
62
+ ref.current.setMobile(true)
63
+ }
64
+ }
65
+ else if (ref.current.isMobile) {
66
+ ref.current.setMobile(false)
67
+ }
68
+ }
69
+ ref.current.setViewportHeight(window.innerHeight)
70
+ }
71
+
72
+ const onResize_debounced = useDebounceCallback(onResize, 500)
73
+
74
+ useLayoutEffect(() => {
75
+ ref.current.initialize()
76
+ onResize()
77
+ window.addEventListener('resize', onResize_debounced);
78
+ return () => {
79
+ window.removeEventListener('resize', onResize_debounced)
80
+ ref.current.dispose()
81
+ }
82
+ }, [])
83
+
84
+ useEffect(() => {
85
+ ref.current.reset()
86
+ }, [pathName])
87
+
88
+
89
+ return (
90
+ <CommerceUIContext.Provider value={ref.current}>
91
+ {children}
92
+ </CommerceUIContext.Provider>
93
+ )
94
+ }
95
+
96
+ export {
97
+ useCommerceDrawer,
98
+ useSelectAndBuy,
99
+ useRecentActivity,
100
+ CommerceUIProvider
101
+ }
102
+
@@ -0,0 +1,277 @@
1
+ import {
2
+ action,
3
+ computed,
4
+ makeObservable,
5
+ observable,
6
+ reaction,
7
+ type IReactionDisposer,
8
+ } from 'mobx'
9
+
10
+ import type { CommerceService, LineItem, ObsLineItemRef } from '@hanzo/commerce/types'
11
+
12
+ const logOn = false
13
+ const log = (s: string) => {
14
+ if (logOn) {
15
+ console.log('COMMERCE_UI ' + s)
16
+ }
17
+ }
18
+
19
+ type SnapPoint = number | string
20
+
21
+ type SnapPoints = {
22
+ full: SnapPoint
23
+ micro: SnapPoint
24
+ }
25
+
26
+ type SnapPointsConfig = {
27
+ mb: SnapPoints
28
+ dt: SnapPoints
29
+ }
30
+
31
+ type DrawerState = 'closed' | 'micro' | 'full'
32
+
33
+ interface RecentActivity extends ObsLineItemRef {
34
+ quantityChanged(sku: string, val: number, prevVal: number): void
35
+ }
36
+
37
+ interface SelectAndBuy {
38
+ showVariants: (skuPath: string) => void
39
+ hideVariants: () => void
40
+ get currentSkuPath(): string | undefined
41
+ }
42
+
43
+ interface CommerceDrawer {
44
+
45
+ get open(): boolean
46
+ get state(): DrawerState
47
+ get closedByUser(): boolean
48
+ setClosedByUser(b: boolean): void
49
+ get modal(): boolean
50
+ get points(): SnapPoint[]
51
+ get activePoint(): SnapPoint | null
52
+ // Called by UI Gesture
53
+ onActivePointChanged: (p: SnapPoint | null) => void
54
+ get showCheckout(): boolean
55
+ get showAdded(): boolean
56
+ get showBuy(): boolean
57
+
58
+ get microHeight(): SnapPoint
59
+ get isMobile(): boolean
60
+ get snapPointPx(): number
61
+ }
62
+
63
+ class CommerceUIStore implements
64
+ RecentActivity,
65
+ SelectAndBuy,
66
+ CommerceDrawer
67
+ {
68
+ _vHeight: number = 0
69
+
70
+ _currentSkuPath: string | undefined = undefined
71
+ _closedByUser: boolean = false
72
+ _checkingOut: boolean = false
73
+ _ignoreStateChange: boolean = false
74
+ _activePoint: SnapPoint | null = null
75
+
76
+ _activeItem: LineItem | undefined = undefined
77
+ _reactionDisposers: IReactionDisposer[] = []
78
+ _service: CommerceService
79
+ _pointsConfig: SnapPointsConfig
80
+ _points: SnapPoints // points to either this._pointsConfig.md or this._pointsConfig.dt
81
+
82
+ constructor(s: CommerceService, conf: SnapPointsConfig) {
83
+ this._service = s
84
+ this._pointsConfig = conf
85
+ this._points = this._pointsConfig.dt
86
+
87
+ makeObservable(this, {
88
+ _currentSkuPath: observable,
89
+ _activeItem: observable.ref,
90
+ _closedByUser: observable,
91
+ _checkingOut: observable,
92
+ _activePoint: observable,
93
+ _points: observable.ref,
94
+ _vHeight: observable,
95
+
96
+ showVariants: action,
97
+ hideVariants: action,
98
+ quantityChanged: action,
99
+ setClosedByUser: action,
100
+ setCheckingOut: action,
101
+ setActivePoint: action,
102
+ setMobile: action,
103
+ setViewportHeight: action,
104
+ clearActiveItem: action,
105
+
106
+ activePoint: computed,
107
+ checkingOut: computed,
108
+ closedByUser: computed,
109
+ currentSkuPath: computed,
110
+ item: computed,
111
+ microHeight: computed,
112
+ modal: computed,
113
+ points: computed,
114
+ showAdded: computed,
115
+ showBuy: computed,
116
+ showCheckout: computed,
117
+ snapPointPx: computed,
118
+ state: computed,
119
+ })
120
+ }
121
+
122
+ initialize = (): void => {
123
+
124
+ this._reactionDisposers.push(reaction(
125
+ () => ( this.state ),
126
+ (s) => {
127
+ if (this.ignoreStateChange) {
128
+ log(`STATE CHANGE to "${s}" (IGNORED)`) // ===========
129
+ this.setIgnoreStateChange(false)
130
+ return
131
+ }
132
+ else {
133
+ log(`STATE CHANGE to "${s}" (UI REACTED)`) // ===========
134
+ this._syncUIToState(s)
135
+ }
136
+ }
137
+ ))
138
+ }
139
+
140
+ reset = () => {
141
+ this.hideVariants()
142
+ this.setClosedByUser(false)
143
+ this.clearActiveItem()
144
+ // DO NOT reset _checkingOut!
145
+ }
146
+
147
+ onActivePointChanged = (pt: SnapPoint | null): void => {
148
+ log("ON onActivePointChanged: " + pt) // ===========
149
+ if (pt === this._points.micro && this.activePoint === this._points.full) {
150
+ this.setIgnoreStateChange(true)
151
+ this.hideVariants()
152
+ }
153
+ else if (pt === this._points.full && this.activePoint === this._points.micro) {
154
+ this.setIgnoreStateChange(true)
155
+ this.showVariants(this.item?.sku ?? '')
156
+ }
157
+ this.setActivePoint(pt)
158
+ }
159
+
160
+ showVariants = (skuPath: string): void => {
161
+ this._service.setCurrentItem(undefined)
162
+ this._currentSkuPath = skuPath
163
+ this._closedByUser = false
164
+ }
165
+
166
+ hideVariants = (): void => { this._currentSkuPath = undefined }
167
+
168
+ get currentSkuPath(): string | undefined { return this._currentSkuPath }
169
+
170
+ quantityChanged = (sku: string, val: number, oldVal: number): void => {
171
+
172
+ if (val === 0) {
173
+ if (this._activeItem?.sku === sku) {
174
+ this._activeItem = undefined
175
+ }
176
+ // otherwise ignore
177
+ }
178
+ else {
179
+ if (!this._activeItem || this._activeItem.sku !== sku) {
180
+ this._activeItem = this._service.getItemBySku(sku)
181
+ }
182
+ }
183
+ }
184
+
185
+ get item(): LineItem | undefined { return this._activeItem }
186
+ clearActiveItem = (): void => { this._activeItem = undefined }
187
+
188
+ get closedByUser(): boolean { return this._closedByUser }
189
+ setClosedByUser = (b: boolean): void => { this._closedByUser = b}
190
+
191
+ get ignoreStateChange(): boolean { return this._ignoreStateChange }
192
+ setIgnoreStateChange = (b: boolean): void => { this._ignoreStateChange = b}
193
+
194
+ get checkingOut(): boolean { return this._checkingOut }
195
+ setCheckingOut = (b: boolean): void => { this._checkingOut = b }
196
+
197
+ get activePoint(): SnapPoint | null { return this._activePoint }
198
+ setActivePoint = (pt: SnapPoint | null): void => { this._activePoint = pt}
199
+
200
+ get points(): SnapPoint[] {
201
+ if (this.showBuy && !(this.showAdded || this.showCheckout)) {
202
+ return [this._points.full]
203
+ }
204
+ else if (!this.showBuy && !this.showAdded && this.showCheckout) {
205
+ return [this._points.micro]
206
+ }
207
+ return [this._points.micro, this._points.full]
208
+ }
209
+
210
+ _syncUIToState = (s: DrawerState) => {
211
+ log("_syncUIToState: " + s) // ===========
212
+ if (s === 'micro') {
213
+ this.setActivePoint(this.points[0])
214
+ }
215
+ else if (s === 'full') {
216
+ this.setActivePoint(this.points[this.points.length - 1])
217
+ }
218
+ }
219
+
220
+ get open(): boolean {
221
+ return (
222
+ !this.closedByUser
223
+ &&
224
+ (this.showCheckout || this.showAdded || this.showBuy)
225
+ )
226
+ }
227
+
228
+ get state(): DrawerState {
229
+ if (this.showBuy) {
230
+ return 'full'
231
+ }
232
+ else if (!this.closedByUser && (this.showAdded || this.showCheckout)) {
233
+ return 'micro'
234
+ }
235
+ return 'closed'
236
+ }
237
+
238
+ get showBuy(): boolean {return !!this.currentSkuPath}
239
+ get showAdded(): boolean { return !this._checkingOut && !!this.item}
240
+ get showCheckout(): boolean { return !this._checkingOut && !this._service.cartEmpty}
241
+ get modal(): boolean { return this.state !== 'micro'}
242
+
243
+ get microHeight(): SnapPoint {
244
+ return this._points.micro
245
+ }
246
+
247
+ get isMobile(): boolean { return this._pointsConfig.mb === this._points }
248
+ setMobile = (b: boolean): void => {
249
+ log("setMobile: " + b) // ===========
250
+ this._points = b ? this._pointsConfig.mb : this._pointsConfig.dt
251
+ }
252
+
253
+ setViewportHeight = (v: number) => {this._vHeight = v}
254
+ get snapPointPx(): number {
255
+ if (!this._activePoint || this._vHeight === 0) return 0
256
+
257
+ if (typeof this._activePoint === 'string') {
258
+ return parseInt(this._activePoint)
259
+ }
260
+
261
+ return this._vHeight * this._activePoint as number
262
+ }
263
+
264
+ dispose = () => {
265
+ this._reactionDisposers.forEach((d) => {d()})
266
+ }
267
+ }
268
+
269
+ export {
270
+ CommerceUIStore,
271
+ type CommerceDrawer,
272
+ type RecentActivity,
273
+ type SelectAndBuy,
274
+ type SnapPointsConfig,
275
+ type SnapPoints,
276
+ type SnapPoint
277
+ }