@hanzo/ui 4.7.0 → 4.8.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 (272) hide show
  1. package/assets/ai-icons.tsx +207 -0
  2. package/assets/crypto.tsx +33 -0
  3. package/assets/file-type-icon.tsx +66 -0
  4. package/assets/file.tsx +45 -0
  5. package/assets/general.tsx +2318 -0
  6. package/assets/hanzo-logo.svg +9 -0
  7. package/assets/hanzo-logo.tsx +17 -0
  8. package/assets/index.ts +122 -0
  9. package/assets/index.tsx +4 -0
  10. package/assets/llm-provider.tsx +1094 -0
  11. package/blocks/auth/index.ts +6 -0
  12. package/blocks/auth/login-2fa.tsx +165 -0
  13. package/blocks/auth/login-basic.tsx +94 -0
  14. package/blocks/auth/login-social.tsx +148 -0
  15. package/blocks/auth/magic-link.tsx +129 -0
  16. package/blocks/auth/password-reset.tsx +97 -0
  17. package/blocks/auth/signup.tsx +157 -0
  18. package/blocks/components/accordian-block.tsx +48 -0
  19. package/blocks/components/block-component-props.ts +11 -0
  20. package/blocks/components/bullet-cards-block.tsx +46 -0
  21. package/blocks/components/card-block/index.tsx +171 -0
  22. package/blocks/components/card-block/link-out-button.tsx +20 -0
  23. package/blocks/components/card-block/util.ts +28 -0
  24. package/blocks/components/carte-blanche-block/index.tsx +127 -0
  25. package/blocks/components/carte-blanche-block/variant-content-left.tsx +49 -0
  26. package/blocks/components/content.tsx +70 -0
  27. package/blocks/components/cta-block.tsx +115 -0
  28. package/blocks/components/enh-heading-block.tsx +204 -0
  29. package/blocks/components/grid-block/grid-block-mutator.ts +12 -0
  30. package/blocks/components/grid-block/index.tsx +83 -0
  31. package/blocks/components/grid-block/mutator-registry.ts +10 -0
  32. package/blocks/components/grid-block/table-borders.mutator.ts +47 -0
  33. package/blocks/components/group-block.tsx +83 -0
  34. package/blocks/components/heading-block.tsx +88 -0
  35. package/blocks/components/image-block.tsx +111 -0
  36. package/blocks/components/index.ts +30 -0
  37. package/blocks/components/screenful-block/content.tsx +123 -0
  38. package/blocks/components/screenful-block/index.tsx +107 -0
  39. package/blocks/components/screenful-block/poster-background.tsx +34 -0
  40. package/blocks/components/screenful-block/video-background.tsx +45 -0
  41. package/blocks/components/space-block.tsx +66 -0
  42. package/blocks/components/video-block.tsx +138 -0
  43. package/blocks/data-display/activity-feed.tsx +242 -0
  44. package/blocks/data-display/data-table.tsx +235 -0
  45. package/blocks/data-display/stats-grid.tsx +194 -0
  46. package/blocks/def/accordian-block.ts +14 -0
  47. package/blocks/def/block.ts +7 -0
  48. package/blocks/def/bullet-cards-block.ts +22 -0
  49. package/blocks/def/card-block.ts +22 -0
  50. package/blocks/def/carte-blanche-block.ts +21 -0
  51. package/blocks/def/cta-block.ts +19 -0
  52. package/blocks/def/element-block.ts +11 -0
  53. package/blocks/def/enh-heading-block.ts +44 -0
  54. package/blocks/def/grid-block.ts +16 -0
  55. package/blocks/def/group-block.ts +11 -0
  56. package/blocks/def/heading-block.ts +15 -0
  57. package/blocks/def/image-block.ts +31 -0
  58. package/blocks/def/index.ts +35 -0
  59. package/blocks/def/screenful-block.ts +54 -0
  60. package/blocks/def/space-block.ts +64 -0
  61. package/blocks/def/video-block.ts +9 -0
  62. package/blocks/ecommerce/checkout.tsx +242 -0
  63. package/blocks/ecommerce/index.ts +7 -0
  64. package/blocks/ecommerce/product-detail.tsx +257 -0
  65. package/blocks/ecommerce/product-grid.tsx +148 -0
  66. package/blocks/ecommerce/shopping-cart.tsx +181 -0
  67. package/blocks/index.ts +2 -0
  68. package/blocks/marketing/cta-section.tsx +207 -0
  69. package/blocks/marketing/faq.tsx +159 -0
  70. package/blocks/marketing/features-grid.tsx +156 -0
  71. package/blocks/marketing/hero-section.tsx +192 -0
  72. package/blocks/marketing/index.ts +6 -0
  73. package/blocks/marketing/pricing-table.tsx +121 -0
  74. package/blocks/marketing/testimonials.tsx +196 -0
  75. package/components/index.ts +9 -0
  76. package/dist/index.js +1407 -1514
  77. package/dist/index.mjs +1363 -1472
  78. package/dist/tailwind/index.js +3 -1
  79. package/dist/tailwind/index.mjs +3 -1
  80. package/dist/util/format-text.js +51 -0
  81. package/dist/util/format-text.mjs +32 -0
  82. package/dist/util/index.js +384 -0
  83. package/dist/util/index.mjs +363 -0
  84. package/frameworks/core/index.ts +6 -0
  85. package/frameworks/core/utils/index.ts +64 -0
  86. package/frameworks/react/components/button.tsx +26 -0
  87. package/frameworks/react/components/index.ts +5 -0
  88. package/frameworks/react/hooks/index.ts +5 -0
  89. package/frameworks/react/index.ts +9 -0
  90. package/frameworks/react/package.json +8 -0
  91. package/frameworks/react/utils/index.ts +2 -0
  92. package/frameworks/react-native/index.ts +9 -0
  93. package/frameworks/react-native/package.json +8 -0
  94. package/frameworks/registry.json +371 -0
  95. package/frameworks/setup.sh +69 -0
  96. package/frameworks/svelte/index.ts +9 -0
  97. package/frameworks/svelte/package.json +8 -0
  98. package/frameworks/tracker.json +1854 -0
  99. package/frameworks/vue/index.ts +9 -0
  100. package/frameworks/vue/package.json +8 -0
  101. package/helpers/file.ts +33 -0
  102. package/helpers/memoization.ts +40 -0
  103. package/package.json +49 -11
  104. package/primitives/accordion.tsx +74 -0
  105. package/primitives/action-button.tsx +42 -0
  106. package/primitives/alert-dialog.tsx +185 -0
  107. package/primitives/alert.tsx +74 -0
  108. package/primitives/apply-typography.tsx +55 -0
  109. package/primitives/aspect-ratio.tsx +5 -0
  110. package/primitives/avatar.tsx +57 -0
  111. package/primitives/background-beams.tsx +142 -0
  112. package/primitives/badge.tsx +45 -0
  113. package/primitives/breadcrumb.tsx +130 -0
  114. package/primitives/breakpoint-indicator.tsx +19 -0
  115. package/primitives/button.tsx +72 -0
  116. package/primitives/calendar.tsx +72 -0
  117. package/primitives/card.tsx +97 -0
  118. package/primitives/carousel.tsx +238 -0
  119. package/primitives/chat/chat-input-area.tsx +88 -0
  120. package/primitives/chat/chat-input.tsx +71 -0
  121. package/primitives/chat/files-preview.tsx +331 -0
  122. package/primitives/chat/index.ts +6 -0
  123. package/primitives/chat/json-form.tsx +8 -0
  124. package/primitives/chat/message-list.tsx +308 -0
  125. package/primitives/chat/message.tsx +569 -0
  126. package/primitives/chat/sqlite-preview.tsx +215 -0
  127. package/primitives/checkbox.tsx +32 -0
  128. package/primitives/collapsible.tsx +9 -0
  129. package/primitives/combobox.tsx +239 -0
  130. package/primitives/command.tsx +151 -0
  131. package/primitives/context-menu.tsx +206 -0
  132. package/primitives/copy-to-clipboard-icon.tsx +60 -0
  133. package/primitives/dialog-video-controller.tsx +38 -0
  134. package/primitives/dialog.tsx +128 -0
  135. package/primitives/dot-pattern.tsx +57 -0
  136. package/primitives/dots-loader.tsx +13 -0
  137. package/primitives/drawer.tsx +113 -0
  138. package/primitives/dropdown-menu.tsx +199 -0
  139. package/primitives/error-message.tsx +19 -0
  140. package/primitives/file-uploader.tsx +202 -0
  141. package/primitives/form.tsx +183 -0
  142. package/primitives/hover-card.tsx +28 -0
  143. package/primitives/icons/github.tsx +14 -0
  144. package/primitives/icons/index.ts +18 -0
  145. package/primitives/icons/youtube-logo.tsx +59 -0
  146. package/primitives/index-common.ts +304 -0
  147. package/primitives/index-next.ts +4 -0
  148. package/primitives/input-otp.tsx +65 -0
  149. package/primitives/input.tsx +126 -0
  150. package/primitives/label.tsx +21 -0
  151. package/primitives/list-adaptor.ts +12 -0
  152. package/primitives/list-box.tsx +74 -0
  153. package/primitives/loading-spinner.tsx +33 -0
  154. package/primitives/markdown-preview.tsx +612 -0
  155. package/primitives/mermaid.tsx +191 -0
  156. package/primitives/navigation-menu.tsx +147 -0
  157. package/primitives/next/image.tsx +91 -0
  158. package/primitives/next/index.ts +7 -0
  159. package/primitives/next/inline-icon.tsx +36 -0
  160. package/primitives/next/link-element.tsx +109 -0
  161. package/primitives/next/mdx-link.tsx +22 -0
  162. package/primitives/next/media-stack.tsx +52 -0
  163. package/primitives/next/nav-items.tsx +45 -0
  164. package/primitives/next/youtube-embed.tsx +83 -0
  165. package/primitives/pagination.tsx +117 -0
  166. package/primitives/popover.tsx +34 -0
  167. package/primitives/pretty-json-print.tsx +28 -0
  168. package/primitives/progress.tsx +27 -0
  169. package/primitives/prompt-textarea.tsx +72 -0
  170. package/primitives/qr-code.tsx +112 -0
  171. package/primitives/radio-group.tsx +42 -0
  172. package/primitives/resizable.tsx +47 -0
  173. package/primitives/scroll-area.tsx +57 -0
  174. package/primitives/search-input.tsx +66 -0
  175. package/primitives/select.tsx +122 -0
  176. package/primitives/separator.tsx +26 -0
  177. package/primitives/sheet.tsx +139 -0
  178. package/primitives/skeleton.tsx +18 -0
  179. package/primitives/slider.tsx +63 -0
  180. package/primitives/sonner.tsx +35 -0
  181. package/primitives/step-indicator.tsx +69 -0
  182. package/primitives/stepper.tsx +272 -0
  183. package/primitives/switch.tsx +27 -0
  184. package/primitives/table.tsx +105 -0
  185. package/primitives/tabs.tsx +50 -0
  186. package/primitives/text-area.tsx +26 -0
  187. package/primitives/text-link.tsx +25 -0
  188. package/primitives/textarea.tsx +62 -0
  189. package/primitives/textfield.tsx +76 -0
  190. package/primitives/toast.tsx +30 -0
  191. package/primitives/toggle-group.tsx +63 -0
  192. package/primitives/toggle.tsx +44 -0
  193. package/primitives/tooltip.tsx +47 -0
  194. package/primitives/video-player.tsx +23 -0
  195. package/src/button.ts +1 -0
  196. package/src/hooks/index.ts +7 -0
  197. package/src/hooks/use-click-away.ts +31 -0
  198. package/src/hooks/use-combined-refs.ts +22 -0
  199. package/src/hooks/use-copy-clipboard.ts +30 -0
  200. package/src/hooks/use-debounce.ts +17 -0
  201. package/src/hooks/use-fill-ids.ts +25 -0
  202. package/src/hooks/use-map.ts +26 -0
  203. package/src/hooks/use-measure.ts +42 -0
  204. package/src/hooks/use-reverse-video-playback.ts +43 -0
  205. package/src/hooks/use-scroll-restoration.ts +50 -0
  206. package/src/index-lean.ts +87 -0
  207. package/src/index.ts +54 -0
  208. package/src/mcp/README.md +141 -0
  209. package/src/mcp/enhanced-server.ts +1208 -0
  210. package/src/mcp/index.ts +518 -0
  211. package/src/mcp/package.json +10 -0
  212. package/src/registry/api.ts +164 -0
  213. package/src/registry/index.ts +60 -0
  214. package/src/registry/package.json +10 -0
  215. package/src/utils.ts +19 -0
  216. package/tailwind/colors.tailwind.js +53 -0
  217. package/tailwind/fontFamily.tailwind.ts +7 -0
  218. package/tailwind/fontSize.tailwind.ts +13 -0
  219. package/tailwind/index.ts +7 -0
  220. package/tailwind/safelist.tailwind.js +26 -0
  221. package/tailwind/screens.tailwind.js +8 -0
  222. package/tailwind/spacing.tailwind.js +65 -0
  223. package/tailwind/tailwind.config.hanzo-preset.d.ts +5 -0
  224. package/tailwind/tailwind.config.hanzo-preset.js +915 -0
  225. package/tailwind/tw-font-desc.ts +15 -0
  226. package/tailwind/typo-plugin/get-plugin-styles.js +679 -0
  227. package/tailwind/typo-plugin/index.d.ts +9 -0
  228. package/tailwind/typo-plugin/index.js +141 -0
  229. package/tailwind/typo-plugin/utils.js +60 -0
  230. package/tailwind/typography-test.mdx +35 -0
  231. package/tailwind/z-index.tailwind.js +71 -0
  232. package/types/animation-def.ts +3 -0
  233. package/types/breakpoints.ts +11 -0
  234. package/types/bullet-item.ts +10 -0
  235. package/types/button-def.ts +39 -0
  236. package/types/dimensions.ts +8 -0
  237. package/types/grid-def.ts +56 -0
  238. package/types/image-def.ts +32 -0
  239. package/types/index.ts +30 -0
  240. package/types/link-def.ts +56 -0
  241. package/types/media-stack-def.ts +31 -0
  242. package/types/t-shirt-size.ts +5 -0
  243. package/types/tshirt-dimensions.ts +20 -0
  244. package/types/video-def.ts +25 -0
  245. package/util/blob.ts +33 -0
  246. package/util/copy-to-clipboard.ts +17 -0
  247. package/util/create-shadow-root.ts +22 -0
  248. package/util/date.ts +84 -0
  249. package/util/debounce.ts +11 -0
  250. package/util/file.ts +15 -0
  251. package/util/format-and-abbreviate-as-currency.ts +125 -0
  252. package/util/format-text.ts +34 -0
  253. package/util/format-to-max-char.ts +68 -0
  254. package/util/index-client.ts +3 -0
  255. package/util/index.ts +112 -0
  256. package/util/number-abbreviate.ts +49 -0
  257. package/util/specifier.ts +43 -0
  258. package/util/spread-to-transform.ts +25 -0
  259. package/util/step-animation.ts +90 -0
  260. package/util/timing.ts +3 -0
  261. package/util/toasts.tsx +17 -0
  262. package/util/two-way-map.ts +19 -0
  263. package/dist/index.d.mts +0 -16
  264. package/dist/index.d.ts +0 -16
  265. package/dist/lib/utils.d.mts +0 -2
  266. package/dist/lib/utils.d.ts +0 -2
  267. package/dist/src/utils.d.mts +0 -7
  268. package/dist/src/utils.d.ts +0 -7
  269. package/dist/tailwind/index.d.mts +0 -2
  270. package/dist/tailwind/index.d.ts +0 -2
  271. package/dist/types/index.d.mts +0 -12
  272. package/dist/types/index.d.ts +0 -12
@@ -0,0 +1,88 @@
1
+ import React from 'react'
2
+
3
+ import type { HeadingBlock } from '../def'
4
+ import { ApplyTypography } from '../../primitives/index-common'
5
+
6
+ import type BlockComponentProps from './block-component-props'
7
+
8
+ const HeadingBlockComponent: React.FC<BlockComponentProps> = ({
9
+ block,
10
+ className=''
11
+ }) => {
12
+
13
+ if (block.blockType !== 'heading') {
14
+ return <>heading block required</>
15
+ }
16
+ const heading = block as HeadingBlock
17
+
18
+ let Tag: React.ElementType
19
+ let BylineTag: React.ElementType | undefined = undefined
20
+
21
+ switch (heading.bylineLevel) {
22
+ case 0: {
23
+ BylineTag = 'p'
24
+ } break
25
+ case 1: {
26
+ BylineTag = 'h1'
27
+ } break
28
+ case 2: {
29
+ BylineTag = 'h2'
30
+ } break
31
+ case 3: {
32
+ BylineTag = 'h3'
33
+ } break
34
+ case 4: {
35
+ BylineTag = 'h4'
36
+ } break
37
+ case 5: {
38
+ BylineTag = 'h5'
39
+ } break
40
+ case 6: {
41
+ BylineTag = 'h6'
42
+ } break
43
+ }
44
+ // bylineLevel default is two levels below the main heading
45
+ switch (heading.level) {
46
+ case 0: {
47
+ Tag = 'p'
48
+ BylineTag = BylineTag ?? 'p'
49
+ } break
50
+ case 1: {
51
+ Tag = 'h1'
52
+ BylineTag = BylineTag ?? 'h3'
53
+ } break
54
+ case 2: {
55
+ Tag = 'h2'
56
+ BylineTag = BylineTag ?? 'h4'
57
+ } break
58
+ // 3 is default
59
+ case 4: {
60
+ Tag = 'h4'
61
+ BylineTag = BylineTag ?? 'h6'
62
+ } break
63
+ case 5: {
64
+ Tag = 'h5'
65
+ BylineTag = BylineTag ?? 'p'
66
+ } break
67
+ case 6: {
68
+ Tag = 'h6'
69
+ BylineTag = BylineTag ?? 'p'
70
+ } break
71
+ default: {
72
+ Tag = 'h3'
73
+ BylineTag = BylineTag ?? 'h5'
74
+ }
75
+ }
76
+
77
+ // Had to do this way, since tw typo plugin does support overrding typo styling wiithout .not-typography
78
+ return (
79
+ <ApplyTypography className={className}>
80
+ <Tag >{heading.heading}</Tag>
81
+ {heading.spaceBetween && <div className={'w-[1px] ' + `h-${heading.spaceBetween}`} />}
82
+ {heading.byline && (<BylineTag>{heading.byline}</BylineTag>)}
83
+ {heading.spaceAfter && <div className={'w-[1px] ' + `h-${heading.spaceAfter}`} />}
84
+ </ApplyTypography>
85
+ )
86
+ }
87
+
88
+ export default HeadingBlockComponent
@@ -0,0 +1,111 @@
1
+ import React from 'react'
2
+ import Image from 'next/image'
3
+
4
+ import type { Dimensions } from '../../types'
5
+ import type { ImageBlock } from '../def'
6
+ import { constrain, containsToken, cn } from '../../util'
7
+
8
+ import type BlockComponentProps from './block-component-props'
9
+
10
+
11
+ const ImageBlockComponent: React.FC<BlockComponentProps & {
12
+ constraintTo?: Dimensions
13
+ }> = ({
14
+ block,
15
+ className='',
16
+ agent,
17
+ constraintTo
18
+ }) => {
19
+
20
+ if (block.blockType !== 'image') {
21
+ return <>image block required</>
22
+ }
23
+
24
+ const {
25
+ src,
26
+ alt,
27
+ dim,
28
+ props,
29
+ sizes,
30
+ fullWidthOnMobile,
31
+ svgFillClass,
32
+ specifiers
33
+ } = block as ImageBlock
34
+
35
+ const specified = (s: string): boolean => (containsToken(specifiers, s))
36
+
37
+ const toSpread: any = {}
38
+ if (props?.fill === undefined) {
39
+ const resolved = constraintTo ? constrain(dim, constraintTo) : dim
40
+ toSpread.width = resolved.w
41
+ toSpread.height = resolved.h
42
+ }
43
+
44
+ let _alt: string
45
+ if (alt) {
46
+ _alt = alt
47
+ }
48
+ else {
49
+ const tokens = src.split('/')
50
+ // Something remotely meaningful
51
+ _alt = (tokens.length > 0) ? tokens[tokens.length] : src
52
+ }
53
+
54
+ const _svgFillClass = svgFillClass ?? ''
55
+
56
+ // TODO: use two elements with 'md:hidden' for 3/4 size
57
+ // https://nextjs.org/docs/app/building-your-application/optimizing/images#responsive
58
+ if (agent === 'phone' ) {
59
+ if (specified('mobile-full-width') || fullWidthOnMobile) {
60
+ const toSpread: any = {
61
+ style: {
62
+ width: '100%',
63
+ height: 'auto',
64
+ maxWidth: '420px'
65
+ },
66
+ sizes: '100vw',
67
+ }
68
+ // only for aspect ratio and to satisfy parser
69
+ toSpread.width = dim.w
70
+ toSpread.height = dim.h
71
+
72
+ return (
73
+ <div className='flex flex-col items-center w-full'>
74
+ <Image src={src} alt={_alt} {...toSpread} className={cn(_svgFillClass, className)}/>
75
+ </div>
76
+ )
77
+ }
78
+ // TODO use constraint
79
+ else if (!specified('mobile-no-scale')) {
80
+ if (props?.style?.width === 'auto' && typeof props.style.height === 'number' ) {
81
+ props.style.height = props.style.height *.75
82
+ }
83
+ else if (props?.style?.height === 'auto' && typeof props?.style?.width === 'number' ) {
84
+ props.style.width = props.style.width *.75
85
+ }
86
+ else if (props?.style && !props?.style.width) {
87
+ toSpread.width = dim.w * .75
88
+ toSpread.height = dim.h * .75
89
+ }
90
+ }
91
+ }
92
+ if (sizes) {
93
+ toSpread.sizes = sizes
94
+ }
95
+
96
+ const right = containsToken(specifiers, 'right')
97
+ const center = containsToken(specifiers, 'center')
98
+
99
+ const alignSelfClx = right ? 'self-end' : (center ? 'self-center' : 'self-start')
100
+
101
+ return (props?.fill) ? (
102
+ <div className='relative w-full h-full'>
103
+ <Image src={src} alt={_alt} {...toSpread} {...props} className={cn(_svgFillClass, 'max-w-[70vw] mx-auto', className)}/>
104
+ </div>
105
+ ) : (
106
+ <Image src={src} alt={_alt} {...toSpread} {...props} className={cn(alignSelfClx, _svgFillClass, 'max-w-[70vw] mx-auto', className)}/>
107
+ )
108
+ }
109
+
110
+ export default ImageBlockComponent
111
+
@@ -0,0 +1,30 @@
1
+ import AccordianBlock from './accordian-block'
2
+ import type BlockComponentProps from './block-component-props'
3
+ import Blocks from './content'
4
+ import CardBlock from './card-block'
5
+ import { default as ContentComponent, registerBlockType} from './content'
6
+ import CTABlock from './cta-block'
7
+ import EnhHeadingBlock from './enh-heading-block'
8
+ import GroupBlock from './group-block'
9
+ import HeadingBlock from './heading-block'
10
+ import ImageBlock from './image-block'
11
+ import SpaceBlock from './space-block'
12
+ import ScreenfulBlock from './screenful-block'
13
+ import VideoBlock from './video-block'
14
+
15
+ export {
16
+ AccordianBlock as AccordianBlockComponent,
17
+ type BlockComponentProps,
18
+ Blocks as BlocksComponent,
19
+ CardBlock as CardBlockComponent,
20
+ ContentComponent,
21
+ CTABlock as CTABlockComponent,
22
+ EnhHeadingBlock as EnhHeadingBlockComponent,
23
+ GroupBlock as GroupBlockComponent,
24
+ HeadingBlock as HeadingBlockComponent,
25
+ ImageBlock as ImageBlockComponent,
26
+ registerBlockType,
27
+ SpaceBlock as SpaceBlockComponent,
28
+ ScreenfulBlock as ScreenfulBlockComponent,
29
+ VideoBlock as VideoBlockComponent,
30
+ }
@@ -0,0 +1,123 @@
1
+ import React, { PropsWithChildren } from 'react'
2
+
3
+ import type { Block, ScreenfulBlock} from '../../def'
4
+ import { containsToken, cn } from '../../../util'
5
+ import ContentComponent from '../content'
6
+
7
+ const ContentColumn: React.FC<{
8
+ blocks: Block[]
9
+ specifiers?: string
10
+ agent?: string
11
+ className?: string
12
+ }> = ({
13
+ blocks,
14
+ specifiers,
15
+ agent,
16
+ className='',
17
+ }) => {
18
+
19
+ const specified = (s: string) => (containsToken(specifiers, s))
20
+
21
+ let modifiers = ''
22
+
23
+ if (agent !== 'phone') {
24
+ if (specified('right')) {
25
+ modifiers += 'items-end '
26
+
27
+ }
28
+ else if (specified('center')) {
29
+ modifiers += 'items-center '
30
+ }
31
+ // default to left
32
+ else {
33
+ modifiers += 'items-start '
34
+ }
35
+ }
36
+ // default to left
37
+ else {
38
+ modifiers += 'items-start '
39
+ }
40
+
41
+ if (agent !== 'phone') {
42
+ if (specified('bottom')) {
43
+ modifiers += 'justify-end '
44
+ }
45
+ else if (specified('vert-center')) {
46
+ modifiers += 'justify-center '
47
+ }
48
+ // default to top
49
+ else {
50
+ modifiers += 'justify-start '
51
+ }
52
+ // right aligned text looks shitty on mobile
53
+ if (specified('text-align-right')) {
54
+ modifiers += 'text-right '
55
+ }
56
+ else {
57
+ modifiers += 'text-left '
58
+ }
59
+ }
60
+ else {
61
+ if (specified('mobile-vert-center')) {
62
+ modifiers += 'justify-center '
63
+ }
64
+ else {
65
+ modifiers += 'justify-start '
66
+ }
67
+ }
68
+
69
+ if (agent === 'phone' && specified('mobile-center-headings')) {
70
+ modifiers += 'typography-headings:text-center '
71
+ }
72
+
73
+ /* ContentComponent's parent div needs h-full class in order for vertical alignment with flexbox to work.
74
+ * This affects specifiers: bottom, vert-center, mobile-vert-center
75
+ */
76
+ return (
77
+ <div className={cn('flex flex-col justify-center h-full', modifiers, className)} >
78
+ <ContentComponent blocks={blocks} agent={agent} />
79
+ </div>
80
+ )
81
+ }
82
+
83
+ const Content: React.FC<{
84
+ block: ScreenfulBlock
85
+ agent?: string
86
+ className?: string
87
+ }> = ({
88
+ block: b,
89
+ agent,
90
+ className='',
91
+ }) => {
92
+
93
+ // We need to set h-full when we have more than 1 column too, so the grid takes full height - Firefox and Safari fix.
94
+ const layoutClx = 'flex flex-col gap-2 sm:gap-4 h-full ' + ((agent !== 'phone') ? ('md:grid md:gap-8 ' + `md:grid-cols-${b.contentColumns.length} `) : '')
95
+
96
+ const orderclx = (columnIndex: number): string => {
97
+ const orderIndex = b.mobileOrder?.indexOf(columnIndex)
98
+ return (orderIndex && orderIndex >= 0) ? `order-${orderIndex} md:order-none` : '' // one-based in flexbox slec
99
+ }
100
+
101
+ return b.contentColumns.length == 1 ? (
102
+ <ContentColumn
103
+ blocks={b.contentColumns[0]}
104
+ specifiers={b.columnSpecifiers?.[0]}
105
+ agent={agent}
106
+ className={cn(className)}
107
+ />
108
+ ) : (
109
+ <div className={cn(layoutClx, className)}>
110
+ {b.contentColumns.map((column, index) => (
111
+ <ContentColumn
112
+ blocks={column}
113
+ specifiers={b.columnSpecifiers?.[index]}
114
+ agent={agent}
115
+ className={orderclx(index)}
116
+ key={index}
117
+ />
118
+ ))}
119
+ </div>
120
+ )
121
+ }
122
+
123
+ export default Content
@@ -0,0 +1,107 @@
1
+ 'use client'
2
+
3
+ import React from 'react'
4
+ import dynamic from 'next/dynamic'
5
+
6
+ import type { Block, ScreenfulBlock, VideoBlock } from '../../def'
7
+ import { containsToken, cn } from '../../../util'
8
+ import { ApplyTypography } from '../../../primitives/index-common'
9
+
10
+ import Poster from './poster-background'
11
+ import Content from './content'
12
+ const Video = dynamic(() => (import('./video-background')), {ssr: false, loading: () => (<></>)})
13
+
14
+ const ScreenfulComponent: React.FC<{
15
+ block: Block
16
+ agent?: string
17
+ initialInView?: boolean
18
+ snapTile?: boolean
19
+ clx?: string
20
+ contentClx?: string
21
+ bottom?: React.ReactNode
22
+ }> = ({
23
+ block,
24
+ agent,
25
+ initialInView=false,
26
+ snapTile=false,
27
+ clx='',
28
+ contentClx='',
29
+ bottom
30
+ }) => {
31
+
32
+ if (block.blockType !== 'screenful') {
33
+ return <>screenful block required</>
34
+ }
35
+ const b = block as ScreenfulBlock
36
+
37
+ const hasBannerVideo = (): boolean => (!!b.banner && (typeof b.banner === 'object'))
38
+
39
+ const tileHeight = (agent === 'desktop') ? 'h-full ' : 'h-[100svh] '
40
+
41
+ const specified = (s: string) => (containsToken(b.specifiers, s))
42
+ const narrowGutters = specified('narrow-gutters') // eg, for a table object that is large
43
+ const noGutters = specified('no-gutters')
44
+ const fullScreenWidth = specified('full-screen-width')
45
+ const vertCenter = specified('vert-center') // at the main level, it seems only useful w one column
46
+ const oneColumn = b.contentColumns.length === 1
47
+
48
+ // content wrapper clx:
49
+ // [
50
+ // positioning,
51
+ // p&m,
52
+ // p&m-modifiers
53
+ // ]
54
+ const cwclx = [
55
+ 'xl:mx-auto overflow-y-hidden h-full',
56
+ fullScreenWidth ? '' : 'max-w-screen-xl',
57
+ // desktop header: 80px / pt-20
58
+ // mobile header: 44px / pt-11
59
+ narrowGutters ?
60
+ 'px-6 lg:px-8 2xl:px-2 pb-4 lg:pb-6 xl:pb-8 ' + (snapTile ? 'pt-15 md:pt-26 lg:pt-28 ' : '') // otherwise assume there is a Main
61
+ :
62
+ noGutters ?
63
+ 'px-0 pb-0 ' + (snapTile ? 'pt-11 lg:pt-20 ' : '') // otherwise assume there is a Main
64
+ :
65
+ 'px-[8vw] xl:px-[1vw] pb-[8vh] pt-[calc(44px+4vh)] md:pt-[calc(80px+6vh)] ',
66
+
67
+ (agent && agent !== 'desktop') ? 'pt-15 sm:pt-17 pb-0 px-3 sm:px-8' : ''
68
+ ]
69
+
70
+ return (
71
+ <section {...((b.anchorId) ? {id: b.anchorId} : {})} className={cn(
72
+ snapTile ? 'snap-start snap-always h-[100vh]' : 'min-h-screen',
73
+ bottom ? 'flex flex-col' : '',
74
+ clx
75
+ )}>
76
+ <ApplyTypography className={cn(
77
+ 'w-full flex flex-row justify-center self-stretch',
78
+ snapTile ? tileHeight : '',
79
+ bottom ? 'grow' : ''
80
+ )}>
81
+ <Poster banner={b.banner}>
82
+ {hasBannerVideo() && (
83
+ <Video
84
+ block={b.banner! as VideoBlock}
85
+ className='absolute top-0 left-0 bottom-0 right-0'
86
+ initialInView={initialInView}
87
+ />
88
+ )}
89
+ <div className={cn(
90
+ ...cwclx,
91
+ snapTile ? 'absolute left-0 right-0 top-0 bottom-0 ' : 'flex min-h-screen w-full',
92
+ contentClx,
93
+ // TODO :aa py-0 breaks padding for header! Investigate why I would have done this!
94
+ vertCenter ? 'self-center ' + (oneColumn ? '!py-0' : '' ) : ''
95
+ )}
96
+ >
97
+ <Content block={b} agent={agent} className='w-full'/>
98
+ {b.footer}
99
+ </div>
100
+ </Poster>
101
+ </ApplyTypography>
102
+ {bottom}
103
+ </section>
104
+ )
105
+ }
106
+
107
+ export default ScreenfulComponent
@@ -0,0 +1,34 @@
1
+ import React, { PropsWithChildren } from 'react'
2
+
3
+ import type { VideoBlock } from '../../def'
4
+ import { cn } from '../../../util'
5
+
6
+ const Poster: React.FC<{
7
+ banner: VideoBlock | string | undefined,
8
+ className?: string
9
+ } & PropsWithChildren> = ({
10
+ children,
11
+ banner,
12
+ className=''
13
+ }) => (
14
+ banner ? (
15
+ <div
16
+ className={cn('relative', className)}
17
+ style={{
18
+ height: '100%',
19
+ width: '100%',
20
+ backgroundImage: `url(${(typeof banner === 'string') ? banner : banner.poster!})`,
21
+ backgroundSize: 'cover',
22
+ backgroundRepeat: 'no-repeat',
23
+ }}
24
+ >
25
+ {children}
26
+ </div>
27
+ ) : (
28
+ <div className={cn('bg-transparent h-full w-full relative', className)}>
29
+ {children}
30
+ </div>
31
+ )
32
+ )
33
+
34
+ export default Poster
@@ -0,0 +1,45 @@
1
+ 'use client'
2
+ import React from 'react'
3
+
4
+ import { useInView } from 'react-intersection-observer'
5
+
6
+ import type { VideoBlock } from '../../def'
7
+
8
+ const VideoBG: React.FC<{
9
+ block: VideoBlock,
10
+ className?: string,
11
+ initialInView: boolean
12
+ }> = ({
13
+ block,
14
+ className='',
15
+ initialInView
16
+ }) => {
17
+ const { ref, inView } = useInView({
18
+ threshold: 0.75,
19
+ initialInView,
20
+ })
21
+
22
+ return block ? (
23
+ <div ref={ref} className={className}>
24
+ {inView && (
25
+ <video
26
+ autoPlay
27
+ loop
28
+ muted
29
+ style={{
30
+ margin: 0,
31
+ height: '100%',
32
+ width: '100%',
33
+ objectFit: 'cover',
34
+ }}
35
+ >
36
+ {block.sources?.map((src, index) => (
37
+ <source key={index} src={src} />
38
+ ))}
39
+ </video>
40
+ )}
41
+ </div>
42
+ ) : null
43
+ }
44
+
45
+ export default VideoBG
@@ -0,0 +1,66 @@
1
+ import React from 'react'
2
+
3
+ import { ldMerge, cn } from '../../util'
4
+
5
+ import type { Breakpoint } from '../../types'
6
+ import { SPACE_DEFAULTS , type TWSpaceUnit, type HeadingLevel} from '../def/space-block'
7
+ import type SpaceBlock from '../def/space-block'
8
+ import { ApplyTypography } from '../../primitives/index-common'
9
+
10
+ import type BlockComponentProps from './block-component-props'
11
+
12
+ const TAGS = [
13
+ 'div',
14
+ 'h1',
15
+ 'h2',
16
+ 'h3',
17
+ 'h4',
18
+ 'h5',
19
+ 'h6',
20
+ ] satisfies React.ElementType[]
21
+
22
+ const SpaceBlockComponent: React.FC<BlockComponentProps> = ({
23
+ block,
24
+ className=''
25
+ }) => {
26
+
27
+ if (block && block.blockType !== 'space') {
28
+ return <>space block required</>
29
+ }
30
+
31
+ const b = block as SpaceBlock
32
+
33
+ // This code path should handle a undefined or empty sizes field.
34
+ if (!b.level) {
35
+ if (typeof b.sizes == 'number') {
36
+ return <div className={cn(`invisible w-[1px] h-${b.sizes}`, className) } />
37
+ }
38
+ const _sizes: {
39
+ [key in (Breakpoint)]?: TWSpaceUnit
40
+ } = {}
41
+ ldMerge(_sizes, SPACE_DEFAULTS, b.sizes)
42
+
43
+ let clx = ''
44
+ for (const [key, value] of Object.entries(_sizes)) {
45
+ // ts brain fart!
46
+ clx += `${key}:h-${value as TWSpaceUnit} `
47
+ }
48
+
49
+ if (b.test) {
50
+ console.log(clx)
51
+ }
52
+
53
+ return <div className={cn('invisible w-[1px] ' + clx, className)} />
54
+ }
55
+
56
+ const Tag = TAGS[b.level]
57
+ const heightClx = (b.level === (0 satisfies HeadingLevel as HeadingLevel)) ? 'h-4' : ''
58
+
59
+ return (
60
+ <ApplyTypography className={className}>
61
+ <Tag className={'invisible m-0 ' + heightClx} >&nbsp;</Tag>
62
+ </ApplyTypography>
63
+ )
64
+ }
65
+
66
+ export default SpaceBlockComponent