@hanzo/commerce 7.3.1 → 7.3.2

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 (83) hide show
  1. package/components/Icons.tsx +34 -34
  2. package/components/add-to-cart-widget.tsx +183 -183
  3. package/components/buy/buy-card.tsx +259 -259
  4. package/components/buy/carousel-buy-card.tsx +242 -242
  5. package/components/buy/multi-family/all-variants-carousel.tsx +261 -261
  6. package/components/buy/multi-family/family-carousel/index.tsx +77 -77
  7. package/components/buy/multi-family/family-carousel/slide.tsx +83 -83
  8. package/components/buy/multi-family/family-carousel/state.ts +87 -87
  9. package/components/buy/multi-family/index.ts +2 -2
  10. package/components/buy/single-family-selector.tsx +90 -90
  11. package/components/buy/title-and-byline.tsx +25 -25
  12. package/components/cart/cart-panel/cart-line-item.tsx +76 -76
  13. package/components/cart/cart-panel/index.tsx +154 -154
  14. package/components/cart/cart-panel/promo-code.tsx +106 -106
  15. package/components/cart/cart-panel/total-area.tsx +60 -60
  16. package/components/checkout/payment-step-form/card-icon-row.tsx +26 -26
  17. package/components/checkout/payment-step-form/card-icons/amex.tsx +32 -32
  18. package/components/checkout/payment-step-form/card-icons/diners-club.tsx +13 -13
  19. package/components/checkout/payment-step-form/card-icons/discover.tsx +25 -25
  20. package/components/checkout/payment-step-form/card-icons/jcb.tsx +26 -26
  21. package/components/checkout/payment-step-form/card-icons/mastercard.tsx +27 -27
  22. package/components/checkout/payment-step-form/card-icons/visa.tsx +25 -25
  23. package/components/checkout/payment-step-form/cc-button.tsx +17 -17
  24. package/components/checkout/payment-step-form/contact-form.tsx +49 -49
  25. package/components/checkout/payment-step-form/crypto-icons/btc.tsx +11 -11
  26. package/components/checkout/payment-step-form/crypto-icons/eth.tsx +20 -20
  27. package/components/checkout/payment-step-form/crypto-icons/usdt.tsx +13 -13
  28. package/components/checkout/payment-step-form/index.tsx +122 -122
  29. package/components/checkout/payment-step-form/methods/bank-transfer.tsx +79 -79
  30. package/components/checkout/payment-step-form/methods/card.tsx +232 -232
  31. package/components/checkout/payment-step-form/methods/crypto.tsx +227 -227
  32. package/components/checkout/payment-step-form/methods/index.ts +22 -22
  33. package/components/checkout/shipping-step-form.tsx +172 -172
  34. package/components/index.ts +11 -11
  35. package/components/item/product-card.tsx +48 -48
  36. package/components/item-selector/button.tsx +188 -188
  37. package/components/item-selector/carousel/index.tsx +197 -197
  38. package/components/item-selector/carousel/slider.tsx +40 -40
  39. package/components/item-selector/index.ts +5 -5
  40. package/components/item-selector/quantity-indicator.tsx +48 -48
  41. package/components/node-tabs/index.tsx +91 -91
  42. package/components/node-tabs/node-image.tsx +31 -31
  43. package/index.ts +12 -12
  44. package/package.json +60 -60
  45. package/service/context.tsx +45 -45
  46. package/service/debug.ts +41 -41
  47. package/service/impls/standalone/actual-line-item.ts +135 -135
  48. package/service/impls/standalone/get-instance.ts +64 -64
  49. package/service/impls/standalone/index.ts +579 -579
  50. package/service/impls/standalone/order/firebase.ts +14 -14
  51. package/service/impls/standalone/order/index.ts +128 -128
  52. package/service/impls/standalone/persistence.ts +33 -33
  53. package/service/path-utils.ts +25 -25
  54. package/service/sep.ts +6 -6
  55. package/tsconfig.json +10 -10
  56. package/types/README.md +1 -1
  57. package/types/category-node.ts +49 -49
  58. package/types/checkout.ts +46 -46
  59. package/types/commerce-config.ts +13 -13
  60. package/types/commerce-service.ts +127 -127
  61. package/types/family.ts +25 -25
  62. package/types/index.ts +15 -15
  63. package/types/item-selector.ts +96 -96
  64. package/types/line-item.ts +29 -29
  65. package/types/multi-family-selector-props.ts +19 -19
  66. package/types/product.ts +20 -20
  67. package/types/promo.ts +9 -9
  68. package/types/selection-ui-specifier.ts +51 -51
  69. package/types/string-mutator.ts +13 -13
  70. package/types/token-separators.ts +7 -7
  71. package/util/analytics.ts +20 -20
  72. package/util/countries.ts +195 -195
  73. package/util/error.ts +34 -34
  74. package/util/index.ts +70 -70
  75. package/util/item-selector-options-accessor.ts +34 -34
  76. package/util/line-item-ref.ts +23 -23
  77. package/util/multi-family-selector-options-accessor.ts +14 -14
  78. package/util/obs-string-mutator.ts +22 -22
  79. package/util/product-media-accessor.ts +58 -58
  80. package/util/promo-codes.ts +108 -108
  81. package/util/selection-ui-specifiers.ts +29 -29
  82. package/util/square-payment.ts +42 -42
  83. package/util/use-sync-sku-param-w-current-item.ts +88 -88
@@ -1,83 +1,83 @@
1
- 'use client'
2
- import React from 'react'
3
-
4
- import {
5
- ApplyTypography,
6
- MediaStack
7
- } from '@hanzo/ui/primitives'
8
- import type { Dimensions } from '@hanzo/ui/types'
9
-
10
- import type { ItemSelectorOptions, Family, LineItem, ItemSelector } from '../../../../types'
11
- import { ButtonItemSelector } from '../../..'
12
- import { cn } from '@hanzo/ui/util'
13
-
14
- const FamilySlide: React.FC<Omit<ItemSelector, 'items'> & {
15
- family: Family
16
- mediaConstraint: Dimensions
17
- clx?: string
18
- mobile?: boolean,
19
- options?: ItemSelectorOptions
20
- }> = ({
21
- family,
22
- selectedItemRef,
23
- selectSku,
24
- mediaConstraint: cnst = {w: 200, h: 200},
25
- clx='',
26
- mobile=false,
27
- options={}
28
- }) => {
29
-
30
- const titleSpec = 'title' in options ? options.title : 'short'
31
- const showByline = 'showByline' in options ? options.showByline : true
32
- const title = titleSpec === 'long' ?
33
- family.title
34
- :
35
- (
36
- titleSpec === 'short' ?
37
- (family.titleShort ? family.titleShort : family.title )
38
- :
39
- undefined
40
- )
41
-
42
- const byline = (showByline && family.byline) ? family.byline : undefined
43
- /*
44
- let byline: string | undefined = undefined
45
- if (showByline) {
46
- if (family.byline) {
47
- // if byline names another field thats a function, call it
48
- if (family.byline in family && typeof (family as any)[family.byline] === 'function') {
49
- byline = (family as any)[family.byline]() as string
50
- }
51
- else {
52
- byline = family.byline
53
- }
54
- }
55
- }
56
- */
57
-
58
- return (
59
- <div className={cn('flex flex-col items-center gap-4', clx)} data-vaul-no-drag >
60
- {title && (
61
- <ApplyTypography className='flex flex-col items-center !gap-1 [&>*]:!m-0' data-vaul-no-drag >
62
- <h6 className='font-bold text-lg'>{title}</h6>
63
- {byline && (<p className={''}>{byline}</p>)}
64
- </ApplyTypography>
65
- )}
66
- <MediaStack
67
- media={selectedItemRef.item!}
68
- constrainTo={cnst}
69
- />
70
- <ButtonItemSelector
71
- items={family.products as LineItem[]}
72
- selectedItemRef={selectedItemRef}
73
- selectSku={selectSku}
74
- scrollable={false}
75
- mobile={mobile}
76
- options={options}
77
- itemClx='text-sm'
78
- />
79
- </div>
80
- )
81
- }
82
-
83
- export default FamilySlide
1
+ 'use client'
2
+ import React from 'react'
3
+
4
+ import {
5
+ ApplyTypography,
6
+ MediaStack
7
+ } from '@hanzo/ui/primitives'
8
+ import type { Dimensions } from '@hanzo/ui/types'
9
+
10
+ import type { ItemSelectorOptions, Family, LineItem, ItemSelector } from '../../../../types'
11
+ import { ButtonItemSelector } from '../../..'
12
+ import { cn } from '@hanzo/ui/util'
13
+
14
+ const FamilySlide: React.FC<Omit<ItemSelector, 'items'> & {
15
+ family: Family
16
+ mediaConstraint: Dimensions
17
+ clx?: string
18
+ mobile?: boolean,
19
+ options?: ItemSelectorOptions
20
+ }> = ({
21
+ family,
22
+ selectedItemRef,
23
+ selectSku,
24
+ mediaConstraint: cnst = {w: 200, h: 200},
25
+ clx='',
26
+ mobile=false,
27
+ options={}
28
+ }) => {
29
+
30
+ const titleSpec = 'title' in options ? options.title : 'short'
31
+ const showByline = 'showByline' in options ? options.showByline : true
32
+ const title = titleSpec === 'long' ?
33
+ family.title
34
+ :
35
+ (
36
+ titleSpec === 'short' ?
37
+ (family.titleShort ? family.titleShort : family.title )
38
+ :
39
+ undefined
40
+ )
41
+
42
+ const byline = (showByline && family.byline) ? family.byline : undefined
43
+ /*
44
+ let byline: string | undefined = undefined
45
+ if (showByline) {
46
+ if (family.byline) {
47
+ // if byline names another field thats a function, call it
48
+ if (family.byline in family && typeof (family as any)[family.byline] === 'function') {
49
+ byline = (family as any)[family.byline]() as string
50
+ }
51
+ else {
52
+ byline = family.byline
53
+ }
54
+ }
55
+ }
56
+ */
57
+
58
+ return (
59
+ <div className={cn('flex flex-col items-center gap-4', clx)} data-vaul-no-drag >
60
+ {title && (
61
+ <ApplyTypography className='flex flex-col items-center !gap-1 [&>*]:!m-0' data-vaul-no-drag >
62
+ <h6 className='font-bold text-lg'>{title}</h6>
63
+ {byline && (<p className={''}>{byline}</p>)}
64
+ </ApplyTypography>
65
+ )}
66
+ <MediaStack
67
+ media={selectedItemRef.item!}
68
+ constrainTo={cnst}
69
+ />
70
+ <ButtonItemSelector
71
+ items={family.products as LineItem[]}
72
+ selectedItemRef={selectedItemRef}
73
+ selectSku={selectSku}
74
+ scrollable={false}
75
+ mobile={mobile}
76
+ options={options}
77
+ itemClx='text-sm'
78
+ />
79
+ </div>
80
+ )
81
+ }
82
+
83
+ export default FamilySlide
@@ -1,87 +1,87 @@
1
- import type { Family, LineItem, ObsLineItemRef, Product } from '../../../../types'
2
- import { observable, action, computed, makeObservable } from 'mobx'
3
-
4
- class SlideState implements ObsLineItemRef {
5
-
6
- _fam: Family
7
- _item: Product
8
- _syncSku: (sku: string) => void
9
- _selected: boolean
10
-
11
- constructor(fam: Family, initiallySelected: boolean, sync: (sku: string) => void) {
12
- this._fam = fam
13
- this._item = fam.products[0]
14
- this._selected = initiallySelected
15
- this._syncSku = sync
16
-
17
- makeObservable(this, {
18
- _item: observable,
19
- item: computed,
20
- selectSku: action
21
- })
22
- }
23
-
24
- selectSku = (sku: string) => {
25
- if (this._item.sku === sku) return;
26
- const found = this._fam.products.find((i) => (i.sku === sku))
27
- if (found) {
28
- this._item = found
29
- if (this._selected) {
30
- this._syncSku(sku)
31
- }
32
- }
33
- else {
34
- throw new Error('SlideState.selectSku(): no item found!')
35
- }
36
- }
37
-
38
- setSelected = (b: boolean) => {this._selected = b}
39
-
40
- get id(): string {return (this._fam.id)}
41
-
42
- get item(): LineItem | undefined {
43
- return this._item as LineItem
44
- }
45
- }
46
-
47
- class FamilyCarouselState {
48
-
49
- _map = new Map<string, SlideState>()
50
- _syncSku: (sku: string) => void
51
-
52
- constructor(
53
- fams: Family[],
54
- // initialFamilyId: string,
55
- syncSkuToCurrentSlide: (sku: string) => void
56
- ) {
57
-
58
- fams.forEach((f, index) => {this._map.set(f.id, new SlideState(
59
- f,
60
- (index === 0),
61
- syncSkuToCurrentSlide
62
- ))})
63
- this._syncSku = syncSkuToCurrentSlide
64
- }
65
-
66
- setCurrentFamily = (id: string) => {
67
-
68
- this._map.forEach((s) => {
69
- s.setSelected(s.id === id)
70
- })
71
-
72
- const slideState = this._map.get(id)
73
- if (slideState) {
74
- this._syncSku(slideState.item!.sku)
75
- }
76
- else {
77
- throw new Error(`FamilyCarouselState.setCurrentFamily: no state object for id '${id}'!`)
78
- }
79
- }
80
-
81
- getSlideState = (familyId: string): SlideState | undefined => (this._map.get(familyId))
82
- }
83
-
84
- export {
85
- SlideState,
86
- FamilyCarouselState
87
- }
1
+ import type { Family, LineItem, ObsLineItemRef, Product } from '../../../../types'
2
+ import { observable, action, computed, makeObservable } from 'mobx'
3
+
4
+ class SlideState implements ObsLineItemRef {
5
+
6
+ _fam: Family
7
+ _item: Product
8
+ _syncSku: (sku: string) => void
9
+ _selected: boolean
10
+
11
+ constructor(fam: Family, initiallySelected: boolean, sync: (sku: string) => void) {
12
+ this._fam = fam
13
+ this._item = fam.products[0]
14
+ this._selected = initiallySelected
15
+ this._syncSku = sync
16
+
17
+ makeObservable(this, {
18
+ _item: observable,
19
+ item: computed,
20
+ selectSku: action
21
+ })
22
+ }
23
+
24
+ selectSku = (sku: string) => {
25
+ if (this._item.sku === sku) return;
26
+ const found = this._fam.products.find((i) => (i.sku === sku))
27
+ if (found) {
28
+ this._item = found
29
+ if (this._selected) {
30
+ this._syncSku(sku)
31
+ }
32
+ }
33
+ else {
34
+ throw new Error('SlideState.selectSku(): no item found!')
35
+ }
36
+ }
37
+
38
+ setSelected = (b: boolean) => {this._selected = b}
39
+
40
+ get id(): string {return (this._fam.id)}
41
+
42
+ get item(): LineItem | undefined {
43
+ return this._item as LineItem
44
+ }
45
+ }
46
+
47
+ class FamilyCarouselState {
48
+
49
+ _map = new Map<string, SlideState>()
50
+ _syncSku: (sku: string) => void
51
+
52
+ constructor(
53
+ fams: Family[],
54
+ // initialFamilyId: string,
55
+ syncSkuToCurrentSlide: (sku: string) => void
56
+ ) {
57
+
58
+ fams.forEach((f, index) => {this._map.set(f.id, new SlideState(
59
+ f,
60
+ (index === 0),
61
+ syncSkuToCurrentSlide
62
+ ))})
63
+ this._syncSku = syncSkuToCurrentSlide
64
+ }
65
+
66
+ setCurrentFamily = (id: string) => {
67
+
68
+ this._map.forEach((s) => {
69
+ s.setSelected(s.id === id)
70
+ })
71
+
72
+ const slideState = this._map.get(id)
73
+ if (slideState) {
74
+ this._syncSku(slideState.item!.sku)
75
+ }
76
+ else {
77
+ throw new Error(`FamilyCarouselState.setCurrentFamily: no state object for id '${id}'!`)
78
+ }
79
+ }
80
+
81
+ getSlideState = (familyId: string): SlideState | undefined => (this._map.get(familyId))
82
+ }
83
+
84
+ export {
85
+ SlideState,
86
+ FamilyCarouselState
87
+ }
@@ -1,2 +1,2 @@
1
- export { default as FamilyCarousel } from './family-carousel'
2
- export { default as AllVariantsCarousel } from './all-variants-carousel'
1
+ export { default as FamilyCarousel } from './family-carousel'
2
+ export { default as AllVariantsCarousel } from './all-variants-carousel'
@@ -1,90 +1,90 @@
1
- import React from 'react'
2
- import { observer } from 'mobx-react-lite'
3
-
4
- import { MediaStack, Skeleton } from '@hanzo/ui/primitives'
5
- import type { Dimensions } from '@hanzo/ui/types'
6
-
7
- import { useCommerce } from '../..'
8
-
9
- import type { ItemSelectorOptions, ItemSelectorProps, LineItem } from '../../types'
10
- import TitleAndByline from './title-and-byline'
11
- import { accessItemOptions } from '../../util'
12
- import { cn } from '@hanzo/ui/util'
13
-
14
-
15
- const DEF_MEDIA_CONSTRAINT = {w: 200, h: 200}
16
-
17
- const SingleFamilySelector: React.FC<{
18
- items: LineItem[] // these may be sorted, so are we can't just use currentFamily.products
19
- Selector: React.ComponentType<ItemSelectorProps>
20
- selOptions: ItemSelectorOptions | undefined
21
- scrollable?: boolean
22
- showItemMedia?: boolean
23
- mediaConstraint?: Dimensions
24
- mobile?: boolean
25
- clx?: string
26
- titleAreaClx?: string
27
- }> = ({
28
- items,
29
- Selector,
30
- selOptions,
31
- scrollable=false,
32
- showItemMedia=true,
33
- mediaConstraint=DEF_MEDIA_CONSTRAINT,
34
- mobile=false,
35
- clx='',
36
- titleAreaClx=''
37
- }) => {
38
-
39
- const cmmc = useCommerce()
40
-
41
-
42
- const MyTitleAndByline: React.FC<{clx?: string}> = observer(({
43
- clx=''
44
- }) => {
45
- const { familyTitle, showFamilyByline } = accessItemOptions(selOptions)
46
- const title = (familyTitle === 'none' ?
47
- undefined
48
- :
49
- (familyTitle === 'long' ?
50
- cmmc.currentFamily?.title
51
- :
52
- (cmmc.currentFamily?.titleShort ?? cmmc.currentFamily?.title)
53
- )
54
- )
55
- const byline = (familyTitle !== 'none') && showFamilyByline ? cmmc.currentFamily?.byline : undefined
56
- return ( <TitleAndByline clx={clx} title={title} byline={byline}/> )
57
- })
58
-
59
- const ItemMedia: React.FC = observer(() => (
60
- cmmc.currentItem ? (
61
- <MediaStack
62
- media={cmmc.currentItem!}
63
- constrainTo={mediaConstraint}
64
- clx={'mb-2 ' + (scrollable ? 'shrink-0' : '')}
65
- />
66
- ) : (
67
- <Skeleton className={'w-[200px] h-[200px] my-2 ' + (scrollable ? 'shrink-0' : '')}/>
68
- )
69
- ))
70
-
71
- return (
72
- <div className={cn(clx, 'flex flex-col items-center', (scrollable ? 'shrink' : ''))}>
73
- <MyTitleAndByline clx={cn(titleAreaClx, (scrollable ? 'shrink-0' : ''))} />
74
- {showItemMedia && (<ItemMedia />)}
75
- {items && (
76
- <Selector
77
- items={items}
78
- selectedItemRef={cmmc}
79
- selectSku={cmmc.setCurrentItem.bind(cmmc)}
80
- scrollable={scrollable}
81
- mobile={mobile}
82
- options={selOptions}
83
- clx={(scrollable ? 'shrink' : '')}
84
- />
85
- )}
86
- </div>
87
- )
88
- }
89
-
90
- export default SingleFamilySelector
1
+ import React from 'react'
2
+ import { observer } from 'mobx-react-lite'
3
+
4
+ import { MediaStack, Skeleton } from '@hanzo/ui/primitives'
5
+ import type { Dimensions } from '@hanzo/ui/types'
6
+
7
+ import { useCommerce } from '../..'
8
+
9
+ import type { ItemSelectorOptions, ItemSelectorProps, LineItem } from '../../types'
10
+ import TitleAndByline from './title-and-byline'
11
+ import { accessItemOptions } from '../../util'
12
+ import { cn } from '@hanzo/ui/util'
13
+
14
+
15
+ const DEF_MEDIA_CONSTRAINT = {w: 200, h: 200}
16
+
17
+ const SingleFamilySelector: React.FC<{
18
+ items: LineItem[] // these may be sorted, so are we can't just use currentFamily.products
19
+ Selector: React.ComponentType<ItemSelectorProps>
20
+ selOptions: ItemSelectorOptions | undefined
21
+ scrollable?: boolean
22
+ showItemMedia?: boolean
23
+ mediaConstraint?: Dimensions
24
+ mobile?: boolean
25
+ clx?: string
26
+ titleAreaClx?: string
27
+ }> = ({
28
+ items,
29
+ Selector,
30
+ selOptions,
31
+ scrollable=false,
32
+ showItemMedia=true,
33
+ mediaConstraint=DEF_MEDIA_CONSTRAINT,
34
+ mobile=false,
35
+ clx='',
36
+ titleAreaClx=''
37
+ }) => {
38
+
39
+ const cmmc = useCommerce()
40
+
41
+
42
+ const MyTitleAndByline: React.FC<{clx?: string}> = observer(({
43
+ clx=''
44
+ }) => {
45
+ const { familyTitle, showFamilyByline } = accessItemOptions(selOptions)
46
+ const title = (familyTitle === 'none' ?
47
+ undefined
48
+ :
49
+ (familyTitle === 'long' ?
50
+ cmmc.currentFamily?.title
51
+ :
52
+ (cmmc.currentFamily?.titleShort ?? cmmc.currentFamily?.title)
53
+ )
54
+ )
55
+ const byline = (familyTitle !== 'none') && showFamilyByline ? cmmc.currentFamily?.byline : undefined
56
+ return ( <TitleAndByline clx={clx} title={title} byline={byline}/> )
57
+ })
58
+
59
+ const ItemMedia: React.FC = observer(() => (
60
+ cmmc.currentItem ? (
61
+ <MediaStack
62
+ media={cmmc.currentItem!}
63
+ constrainTo={mediaConstraint}
64
+ clx={'mb-2 ' + (scrollable ? 'shrink-0' : '')}
65
+ />
66
+ ) : (
67
+ <Skeleton className={'w-[200px] h-[200px] my-2 ' + (scrollable ? 'shrink-0' : '')}/>
68
+ )
69
+ ))
70
+
71
+ return (
72
+ <div className={cn(clx, 'flex flex-col items-center', (scrollable ? 'shrink' : ''))}>
73
+ <MyTitleAndByline clx={cn(titleAreaClx, (scrollable ? 'shrink-0' : ''))} />
74
+ {showItemMedia && (<ItemMedia />)}
75
+ {items && (
76
+ <Selector
77
+ items={items}
78
+ selectedItemRef={cmmc}
79
+ selectSku={cmmc.setCurrentItem.bind(cmmc)}
80
+ scrollable={scrollable}
81
+ mobile={mobile}
82
+ options={selOptions}
83
+ clx={(scrollable ? 'shrink' : '')}
84
+ />
85
+ )}
86
+ </div>
87
+ )
88
+ }
89
+
90
+ export default SingleFamilySelector
@@ -1,25 +1,25 @@
1
- import React from 'react'
2
-
3
- import { ApplyTypography } from '@hanzo/ui/primitives'
4
- import { cn } from '@hanzo/ui/util'
5
-
6
- const TitleAndByline: React.FC<{
7
- title: string | undefined
8
- byline?: string | undefined
9
- clx?: string
10
- bylineClx?: string
11
- }> = ({
12
- title,
13
- byline,
14
- clx='',
15
- bylineClx=''
16
- }) => ( (title || byline) ? (
17
- <ApplyTypography className={cn('flex flex-col items-center !gap-0 [&>*]:!m-0 ', clx)} >
18
- <h4>{title}</h4>
19
- {byline && (<h6 className={bylineClx}>{byline}</h6>)}
20
- </ApplyTypography>
21
- ) : (null)
22
- )
23
-
24
- export default TitleAndByline
25
-
1
+ import React from 'react'
2
+
3
+ import { ApplyTypography } from '@hanzo/ui/primitives'
4
+ import { cn } from '@hanzo/ui/util'
5
+
6
+ const TitleAndByline: React.FC<{
7
+ title: string | undefined
8
+ byline?: string | undefined
9
+ clx?: string
10
+ bylineClx?: string
11
+ }> = ({
12
+ title,
13
+ byline,
14
+ clx='',
15
+ bylineClx=''
16
+ }) => ( (title || byline) ? (
17
+ <ApplyTypography className={cn('flex flex-col items-center !gap-0 [&>*]:!m-0 ', clx)} >
18
+ <h4>{title}</h4>
19
+ {byline && (<h6 className={bylineClx}>{byline}</h6>)}
20
+ </ApplyTypography>
21
+ ) : (null)
22
+ )
23
+
24
+ export default TitleAndByline
25
+