@hanzo/ui 0.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/lux-site-icons/android-chrome-192x192.png +0 -0
- package/assets/lux-site-icons/android-chrome-512x512.png +0 -0
- package/assets/lux-site-icons/apple-touch-icon.png +0 -0
- package/assets/lux-site-icons/favicon-16x16.png +0 -0
- package/assets/lux-site-icons/favicon-32x32.png +0 -0
- package/assets/lux-site-icons/favicon.ico +0 -0
- package/assets/standard-docs/LUX-NFT-Terms-and-Conditions.pdf +0 -0
- package/assets/standard-docs/LUX-Privacy-Policy.pdf +0 -0
- package/blocks/components/accordian-block.tsx +48 -0
- package/blocks/components/block-component-props.ts +11 -0
- package/blocks/components/bullet-cards-block.tsx +43 -0
- package/blocks/components/card-block.tsx +213 -0
- package/blocks/components/carte-blanche-block/index.tsx +98 -0
- package/blocks/components/content.tsx +70 -0
- package/blocks/components/cta-block.tsx +98 -0
- package/blocks/components/enh-heading-block.tsx +194 -0
- package/blocks/components/grid-block/grid-block-mutator.ts +12 -0
- package/blocks/components/grid-block/index.tsx +83 -0
- package/blocks/components/grid-block/mutator-registry.ts +10 -0
- package/blocks/components/grid-block/table-borders.mutator.ts +47 -0
- package/blocks/components/group-block.tsx +83 -0
- package/blocks/components/heading-block.tsx +88 -0
- package/blocks/components/image-block.tsx +108 -0
- package/blocks/components/index.ts +30 -0
- package/blocks/components/screenful-block/content.tsx +115 -0
- package/blocks/components/screenful-block/index.tsx +77 -0
- package/blocks/components/screenful-block/poster-background.tsx +34 -0
- package/blocks/components/screenful-block/video-background.tsx +45 -0
- package/blocks/components/space-block.tsx +66 -0
- package/blocks/components/video-block.tsx +137 -0
- package/blocks/def/accordian-block.ts +14 -0
- package/blocks/def/block.ts +7 -0
- package/blocks/def/bullet-cards-block.ts +20 -0
- package/blocks/def/card-block.ts +24 -0
- package/blocks/def/carte-blanche-block.ts +20 -0
- package/blocks/def/cta-block.ts +19 -0
- package/blocks/def/element-block.ts +11 -0
- package/blocks/def/enh-heading-block.ts +45 -0
- package/blocks/def/grid-block.ts +16 -0
- package/blocks/def/group-block.ts +11 -0
- package/blocks/def/heading-block.ts +15 -0
- package/blocks/def/image-block.ts +36 -0
- package/blocks/def/index.ts +35 -0
- package/blocks/def/screenful-block.ts +51 -0
- package/blocks/def/space-block.ts +64 -0
- package/blocks/def/video-block.ts +28 -0
- package/blocks/index.ts +2 -0
- package/common/chat-widget.tsx +75 -0
- package/common/contact-dialog/contact-form.tsx +111 -0
- package/common/contact-dialog/disclaimer.tsx +13 -0
- package/common/contact-dialog/index.tsx +48 -0
- package/common/copyright.tsx +21 -0
- package/common/drawer-menu.tsx +51 -0
- package/common/footer.tsx +77 -0
- package/common/head-metadata/from-next/metadata-types.ts +158 -0
- package/common/head-metadata/from-next/opengraph-types.ts +267 -0
- package/common/head-metadata/from-next/twitter-types.ts +92 -0
- package/common/head-metadata/index.tsx +208 -0
- package/common/header/index.tsx +57 -0
- package/common/header/mobile-nav.tsx +72 -0
- package/common/header/theme-toggle.tsx +26 -0
- package/common/icons/github.tsx +14 -0
- package/common/icons/index.tsx +34 -0
- package/common/icons/lux-logo.tsx +10 -0
- package/common/icons/secure-delivery.tsx +13 -0
- package/common/icons/social-icon.tsx +35 -0
- package/common/icons/youtube-logo.tsx +59 -0
- package/common/index.ts +14 -0
- package/common/logo.tsx +71 -0
- package/common/mini-chart/index.tsx +8 -0
- package/common/mini-chart/mini-chart-props.ts +44 -0
- package/common/mini-chart/mini-chart.tsx +76 -0
- package/common/mini-chart/wrapper.tsx +23 -0
- package/context-providers/index.ts +1 -0
- package/context-providers/theme-provider.tsx +20 -0
- package/next/README.md +11 -0
- package/next/determine-device-middleware.ts +16 -0
- package/next/fonts/DrukTextWide-Bold-Trial.otf +0 -0
- package/next/fonts/DrukTextWide-Heavy-Trial.otf +0 -0
- package/next/fonts/DrukTextWide-Medium-Trial.otf +0 -0
- package/next/get-app-router-font-classes.ts +12 -0
- package/next/load-and-return-lux-next-fonts-on-import.ts +68 -0
- package/next/next-font-desc.ts +28 -0
- package/next/not-found-content.mdx +4 -0
- package/next/not-found.tsx +23 -0
- package/next/pages-router-font-vars.tsx +18 -0
- package/next/root-layout.tsx +53 -0
- package/package.json +105 -0
- package/primitives/accordion.tsx +61 -0
- package/primitives/action-button.tsx +46 -0
- package/primitives/apply-typography.tsx +55 -0
- package/primitives/avatar.tsx +49 -0
- package/primitives/badge.tsx +36 -0
- package/primitives/button.tsx +73 -0
- package/primitives/calendar.tsx +72 -0
- package/primitives/card.tsx +83 -0
- package/primitives/checkbox.tsx +32 -0
- package/primitives/command.tsx +155 -0
- package/primitives/dialog-video-controller.tsx +38 -0
- package/primitives/dialog.tsx +152 -0
- package/primitives/form.tsx +179 -0
- package/primitives/index.ts +144 -0
- package/primitives/inline-icon.tsx +37 -0
- package/primitives/input.tsx +30 -0
- package/primitives/label.tsx +28 -0
- package/primitives/link-element.tsx +104 -0
- package/primitives/main.tsx +17 -0
- package/primitives/mdx-link.tsx +22 -0
- package/primitives/nav-items.tsx +48 -0
- package/primitives/popover.tsx +35 -0
- package/primitives/progress.tsx +27 -0
- package/primitives/scroll-area.tsx +47 -0
- package/primitives/select.tsx +169 -0
- package/primitives/separator.tsx +29 -0
- package/primitives/sheet.tsx +175 -0
- package/primitives/skeleton.tsx +15 -0
- package/primitives/switch.tsx +33 -0
- package/primitives/table.tsx +117 -0
- package/primitives/tabs.tsx +60 -0
- package/primitives/tailwind-indicator.tsx +19 -0
- package/primitives/text-area.tsx +26 -0
- package/primitives/toast.tsx +129 -0
- package/primitives/toaster.tsx +37 -0
- package/primitives/use-toast.ts +192 -0
- package/primitives/video-player.tsx +26 -0
- package/primitives/youtube-embed.tsx +83 -0
- package/siteDef/footer/community.tsx +67 -0
- package/siteDef/footer/company.ts +37 -0
- package/siteDef/footer/ecosystem.ts +37 -0
- package/siteDef/footer/index.tsx +26 -0
- package/siteDef/footer/legal.ts +28 -0
- package/siteDef/footer/network.ts +33 -0
- package/siteDef/footer/svg/warpcast-logo.svg +12 -0
- package/siteDef/main-nav.ts +35 -0
- package/style/globals.css +13 -0
- package/style/hanzo-common.css +32 -0
- package/style/hanzo-default-colors.css +79 -0
- package/style/social-svg.css +3 -0
- package/tailwind/colors.tailwind.js +46 -0
- package/tailwind/fonts.tailwind.ts +31 -0
- package/tailwind/index.ts +18 -0
- package/tailwind/lux-tw-fonts.ts +32 -0
- package/tailwind/safelist.tailwind.js +26 -0
- package/tailwind/screens.tailwind.js +8 -0
- package/tailwind/spacing.tailwind.js +57 -0
- package/tailwind/tailwind.config.base.js +905 -0
- package/tailwind/tw-font-desc.ts +15 -0
- package/tailwind/typo-plugin/get-plugin-styles.js +676 -0
- package/tailwind/typo-plugin/index.d.ts +9 -0
- package/tailwind/typo-plugin/index.js +141 -0
- package/tailwind/typo-plugin/utils.js +60 -0
- package/tailwind/typography-test.mdx +36 -0
- package/types/breakpoints.ts +11 -0
- package/types/bullet-item.ts +10 -0
- package/types/button-def.ts +39 -0
- package/types/contact-info.ts +11 -0
- package/types/dimensions.ts +20 -0
- package/types/grid-def.ts +37 -0
- package/types/icon.ts +10 -0
- package/types/image-def.ts +28 -0
- package/types/index.ts +29 -0
- package/types/link-def.ts +59 -0
- package/types/site-def.ts +31 -0
- package/types/t-shirt-size.ts +5 -0
- package/util/index.ts +76 -0
- package/util/specifier.ts +43 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import React, {type ElementType} from 'react'
|
|
2
|
+
|
|
3
|
+
import type { Icon } from '../../types'
|
|
4
|
+
import type { EnhHeadingBlock } from '../def'
|
|
5
|
+
import { ApplyTypography } from '../../primitives'
|
|
6
|
+
import { cn, containsToken } from '../../util'
|
|
7
|
+
|
|
8
|
+
import type BlockComponentProps from './block-component-props'
|
|
9
|
+
import InlineIcon from '../../primitives/inline-icon'
|
|
10
|
+
|
|
11
|
+
const DEFAULTS = {
|
|
12
|
+
preheading: {
|
|
13
|
+
tag: 'h4' as ElementType,
|
|
14
|
+
mb: 2
|
|
15
|
+
},
|
|
16
|
+
heading: {
|
|
17
|
+
tag: 'h1' as ElementType,
|
|
18
|
+
mb: 2
|
|
19
|
+
},
|
|
20
|
+
byline: {
|
|
21
|
+
tag: 'h6' as ElementType,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const tagFromLevel = (level: number): ElementType => {
|
|
26
|
+
switch (level) {
|
|
27
|
+
case 1: {
|
|
28
|
+
return 'h1'
|
|
29
|
+
}
|
|
30
|
+
case 2: {
|
|
31
|
+
return 'h2'
|
|
32
|
+
}
|
|
33
|
+
case 3: {
|
|
34
|
+
return 'h3'
|
|
35
|
+
}
|
|
36
|
+
case 4: {
|
|
37
|
+
return 'h4'
|
|
38
|
+
}
|
|
39
|
+
case 5: {
|
|
40
|
+
return 'h5'
|
|
41
|
+
}
|
|
42
|
+
case 6: {
|
|
43
|
+
return 'h6'
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return 'p'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// TODO Impl icon support
|
|
50
|
+
const Element: React.FC<{
|
|
51
|
+
asTag: ElementType
|
|
52
|
+
text: string
|
|
53
|
+
icon?: Icon
|
|
54
|
+
iconLeft?: boolean
|
|
55
|
+
className?: string
|
|
56
|
+
}> = ({
|
|
57
|
+
asTag: Tag,
|
|
58
|
+
text,
|
|
59
|
+
icon,
|
|
60
|
+
iconLeft=true,
|
|
61
|
+
className : elClassName=''
|
|
62
|
+
}) => (
|
|
63
|
+
<Tag className={elClassName}>{text}</Tag>
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
const getPositionClx = (
|
|
67
|
+
specified: (s: string) => boolean,
|
|
68
|
+
agent: string | undefined
|
|
69
|
+
): {
|
|
70
|
+
preheading: string
|
|
71
|
+
heading: string
|
|
72
|
+
byline: string
|
|
73
|
+
} => {
|
|
74
|
+
|
|
75
|
+
const mobileHeadingCentered = specified('mobile-heading-centered')
|
|
76
|
+
const headingCentered = specified('center')
|
|
77
|
+
const headingRight = specified('right')
|
|
78
|
+
const bylineCentered = specified('byline-center')
|
|
79
|
+
const bylineRight = specified('byline-right')
|
|
80
|
+
|
|
81
|
+
let headerclx = ''
|
|
82
|
+
if (agent === 'phone') {
|
|
83
|
+
headerclx = (mobileHeadingCentered || headingCentered) ?
|
|
84
|
+
'self-center text-center' : (headingRight ? 'self-end text-right' : 'self-start text-left')
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const largerclx = (headingCentered) ?
|
|
88
|
+
'self-center text-center' : (headingRight ? 'self-end text-right' : 'self-start text-left')
|
|
89
|
+
|
|
90
|
+
if (mobileHeadingCentered) {
|
|
91
|
+
headerclx = 'self-center text-center md:' + largerclx.split(' ').join(' md:')
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
headerclx = largerclx
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const bylineclx = (bylineCentered) ?
|
|
99
|
+
'self-center' : (bylineRight ? 'self-end' : 'self-start')
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
preheading: headerclx,
|
|
103
|
+
heading: headerclx,
|
|
104
|
+
byline: bylineclx
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const EnhHeadingBlockComponent: React.FC<
|
|
109
|
+
BlockComponentProps & {
|
|
110
|
+
applyTypography?: boolean
|
|
111
|
+
extraSpecifiers?: string
|
|
112
|
+
}> = ({
|
|
113
|
+
block,
|
|
114
|
+
className='',
|
|
115
|
+
agent,
|
|
116
|
+
applyTypography=true,
|
|
117
|
+
extraSpecifiers=''
|
|
118
|
+
}) => {
|
|
119
|
+
|
|
120
|
+
if (block.blockType !== 'enh-heading') {
|
|
121
|
+
return <>enhance heading block required</>
|
|
122
|
+
}
|
|
123
|
+
const b = block as EnhHeadingBlock
|
|
124
|
+
const specified = (s: string) => (containsToken(b.specifiers + extraSpecifiers, s))
|
|
125
|
+
const preheadingHeadingFont = specified('preheading-heading-font')
|
|
126
|
+
const phFontClx = preheadingHeadingFont ? 'font-heading' : ''
|
|
127
|
+
|
|
128
|
+
const positionclx = getPositionClx(specified, agent)
|
|
129
|
+
|
|
130
|
+
const Inner: React.FC = () => {
|
|
131
|
+
const toRender = [
|
|
132
|
+
{
|
|
133
|
+
tag: (b.preheading) ?
|
|
134
|
+
(b.preheading.level !== undefined ? tagFromLevel(b.preheading.level) : DEFAULTS.preheading.tag)
|
|
135
|
+
:
|
|
136
|
+
undefined,
|
|
137
|
+
clx: (b.preheading) ?
|
|
138
|
+
(b.preheading.mb !== undefined ?
|
|
139
|
+
`mb-${b.preheading.mb}` : `mb-${DEFAULTS.preheading.mb}`) + ' ' + positionclx.preheading + ' ' + phFontClx
|
|
140
|
+
:
|
|
141
|
+
positionclx.preheading + ' ' + phFontClx,
|
|
142
|
+
text: (b.preheading) ? (b.preheading.text ) : undefined,
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
tag: (b.heading.level !== undefined ? tagFromLevel(b.heading.level) : DEFAULTS.heading.tag),
|
|
146
|
+
clx: (b.heading.mb !== undefined ? `mb-${b.heading.mb}` : `mb-${DEFAULTS.heading.mb}`) + ' ' + positionclx.heading,
|
|
147
|
+
text: b.heading.text,
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
tag: (b.byline) ?
|
|
151
|
+
(b.byline.level !== undefined ? tagFromLevel(b.byline.level) : DEFAULTS.byline.tag)
|
|
152
|
+
:
|
|
153
|
+
undefined,
|
|
154
|
+
clx: positionclx.byline,
|
|
155
|
+
text: (b.byline) ? (b.byline.text ) : undefined,
|
|
156
|
+
},
|
|
157
|
+
] as {
|
|
158
|
+
tag: ElementType
|
|
159
|
+
clx: string
|
|
160
|
+
text: string
|
|
161
|
+
}[]
|
|
162
|
+
|
|
163
|
+
let iconRendered = false
|
|
164
|
+
return <>
|
|
165
|
+
{toRender.map(({tag, clx, text}, index) => {
|
|
166
|
+
if (!tag) return null
|
|
167
|
+
if (b.icon && !iconRendered) {
|
|
168
|
+
iconRendered = true
|
|
169
|
+
return (
|
|
170
|
+
<div className={cn('flex flex-row items-center', clx)} key={`div-${index}`}>
|
|
171
|
+
<InlineIcon icon={b.icon} size={b.iconSize ?? 32} agent={agent}/>
|
|
172
|
+
<Element asTag={tag} text={text} />
|
|
173
|
+
</div>
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
return (
|
|
177
|
+
(<Element asTag={tag} text={text} className={clx} key={`el-${index}`}/>)
|
|
178
|
+
)
|
|
179
|
+
})}
|
|
180
|
+
</>
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return applyTypography ? (
|
|
184
|
+
<ApplyTypography className={cn('flex flex-col w-full !gap-0', className)}>
|
|
185
|
+
<Inner />
|
|
186
|
+
</ApplyTypography>
|
|
187
|
+
) : (
|
|
188
|
+
<div className={cn('flex flex-col w-full gap-0', className)}>
|
|
189
|
+
<Inner />
|
|
190
|
+
</div>
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export default EnhHeadingBlockComponent
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React, { type PropsWithChildren } from 'react'
|
|
2
|
+
|
|
3
|
+
import { cn, containsToken } from '../../../util'
|
|
4
|
+
import type { GridBlock } from '../../def'
|
|
5
|
+
import type { GridColumnSpec } from '../../../types'
|
|
6
|
+
|
|
7
|
+
import Content from '../content'
|
|
8
|
+
import type BlockComponentProps from '../block-component-props'
|
|
9
|
+
|
|
10
|
+
import getMutator from './mutator-registry'
|
|
11
|
+
|
|
12
|
+
// These must be / are safelisted in tailwind.config
|
|
13
|
+
const gridClx = (d: GridColumnSpec, prefix?: string ): string => (
|
|
14
|
+
(typeof d === 'number') ?
|
|
15
|
+
`${(prefix ?? '')}grid-cols-${d} `
|
|
16
|
+
:
|
|
17
|
+
`${(prefix ?? '')}grid-cols-${d.columns} ${(prefix ?? '')}gap-${d.gap} `
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A component that lays out the Blocks in 'cells' on a grid.
|
|
22
|
+
* If invoked directly, and children are supplied,
|
|
23
|
+
* block.cells is ignore and the children are rendered.
|
|
24
|
+
*/
|
|
25
|
+
const GridBlockComponent: React.FC<
|
|
26
|
+
BlockComponentProps & PropsWithChildren
|
|
27
|
+
> = ({
|
|
28
|
+
block,
|
|
29
|
+
className='',
|
|
30
|
+
agent,
|
|
31
|
+
children
|
|
32
|
+
}) => {
|
|
33
|
+
|
|
34
|
+
if (block.blockType !== 'grid') {
|
|
35
|
+
return <>grid block required</>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const { cells, grid, specifiers } = block as GridBlock
|
|
39
|
+
const specified = (s: string) => (containsToken(specifiers, s))
|
|
40
|
+
const mutator = specified('style-table-borders') ? getMutator('style-table-borders') : undefined
|
|
41
|
+
|
|
42
|
+
// https://tailwindcss.com/docs/content-configuration#dynamic-class-names
|
|
43
|
+
// All variants in use MUST be in style/safelist.tailwind.js
|
|
44
|
+
let clx = 'grid '
|
|
45
|
+
if (agent === 'phone') {
|
|
46
|
+
const d = (grid.mobile) ? grid.mobile : (grid.at.xs ? grid.at.xs : (grid.at.sm ?? 1))
|
|
47
|
+
clx += gridClx(d)
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
let defaultSet = false
|
|
51
|
+
for (const [key, value] of Object.entries(grid.at)) {
|
|
52
|
+
if (!defaultSet) {
|
|
53
|
+
// ts brain fart!
|
|
54
|
+
clx += gridClx(value as GridColumnSpec)
|
|
55
|
+
defaultSet = true
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// ts brain fart!
|
|
59
|
+
clx += gridClx(value as GridColumnSpec, `${key}:`)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let getMutatorCellClx = (ignore: number): string => ('')
|
|
65
|
+
if (mutator?.getCellClx) {
|
|
66
|
+
const colCount = (typeof grid.at.md! === 'number') ? grid.at.md! as number : grid.at.md!.columns
|
|
67
|
+
const cellCount = cells?.length
|
|
68
|
+
if (!colCount || !cellCount) {
|
|
69
|
+
throw new Error('GridBlockComponent: using mutator, but colCount and / or cellCount is invalid!')
|
|
70
|
+
}
|
|
71
|
+
getMutatorCellClx = (cellIndex: number) => ( mutator!.getCellClx!(cellIndex, cellCount, colCount) )
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<div className={cn('grid gap-2 md:gap-4 xl:gap-6', clx, (mutator?.gapClx ?? ''), className)}>
|
|
76
|
+
{React.Children.count(children) > 0 ? children : cells?.map((cell, index) => (
|
|
77
|
+
<Content blocks={cell} agent={agent} key={`cell-${index}`} className={getMutatorCellClx(index)}/>
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default GridBlockComponent
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type GridBlockMutator from './grid-block-mutator'
|
|
2
|
+
|
|
3
|
+
import tableBorders from './table-borders.mutator'
|
|
4
|
+
|
|
5
|
+
const map = new Map<string, GridBlockMutator>()
|
|
6
|
+
map.set('style-table-borders', tableBorders)
|
|
7
|
+
|
|
8
|
+
// get
|
|
9
|
+
export default (key: string): GridBlockMutator | undefined => (map.get(key))
|
|
10
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type GridBlockMutator from './grid-block-mutator'
|
|
2
|
+
|
|
3
|
+
const getCellClx = (
|
|
4
|
+
cellIndex: number,
|
|
5
|
+
cellCount: number,
|
|
6
|
+
colCount: number
|
|
7
|
+
): string => {
|
|
8
|
+
|
|
9
|
+
const isFirstRow = (): boolean => (cellIndex < colCount)
|
|
10
|
+
const isFirstColumn = (): boolean => (cellIndex % colCount === 0)
|
|
11
|
+
const isTLCorner = (): boolean => (cellIndex === 0)
|
|
12
|
+
const isTRCorner = (): boolean => (cellIndex === colCount -1 )
|
|
13
|
+
const isBLCorner = (): boolean => (cellIndex === cellCount - colCount)
|
|
14
|
+
const isBRCorner = (): boolean => (cellIndex === cellCount - 1)
|
|
15
|
+
|
|
16
|
+
// all get a right and bottom border and padding,
|
|
17
|
+
// other borders are a special base
|
|
18
|
+
let clx = 'border-b md:border-r p-4 md:p-8 lg:p-12 '
|
|
19
|
+
if (isFirstRow()) {
|
|
20
|
+
clx += 'border-t '
|
|
21
|
+
}
|
|
22
|
+
if (isFirstColumn()) {
|
|
23
|
+
clx += 'md:border-l '
|
|
24
|
+
}
|
|
25
|
+
// on mobile this will be the first (top) cell
|
|
26
|
+
if (isTLCorner()) {
|
|
27
|
+
clx += 'border-t md:rounded-tl-lg '
|
|
28
|
+
}
|
|
29
|
+
else if (isTRCorner()) {
|
|
30
|
+
clx += 'md:rounded-tr-lg '
|
|
31
|
+
}
|
|
32
|
+
else if (isBLCorner()) {
|
|
33
|
+
clx += 'md:rounded-bl-lg '
|
|
34
|
+
}
|
|
35
|
+
else if (isBRCorner()) {
|
|
36
|
+
clx += 'md:rounded-br-lg '
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return clx
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const gapClx = 'gap-0 md:gap-0 xl:gap-9'
|
|
43
|
+
|
|
44
|
+
export default {
|
|
45
|
+
getCellClx,
|
|
46
|
+
gapClx
|
|
47
|
+
} as GridBlockMutator
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { Breakpoints} from '../../types'
|
|
4
|
+
import { cn } from '../../util'
|
|
5
|
+
|
|
6
|
+
import type { Block, GroupBlock } from '../def'
|
|
7
|
+
import Content from './content'
|
|
8
|
+
|
|
9
|
+
// eg: 'layout-grid-2-starting-md'
|
|
10
|
+
// see comments below regarding dynamic classes and the safelist
|
|
11
|
+
const getLayoutInfo = (s: string): {
|
|
12
|
+
layout: string
|
|
13
|
+
spec: any
|
|
14
|
+
} | undefined => {
|
|
15
|
+
const tokenArray = s.split(' ')
|
|
16
|
+
const layoutToken = tokenArray.find((tok) => (tok.startsWith('layout-')))
|
|
17
|
+
if (layoutToken) {
|
|
18
|
+
const subtokens = layoutToken.split('-')
|
|
19
|
+
const layout = subtokens[1]
|
|
20
|
+
let spec: any = {}
|
|
21
|
+
switch (layout) {
|
|
22
|
+
case 'grid': {
|
|
23
|
+
const columns = parseInt(subtokens[2], 10)
|
|
24
|
+
const starting = subtokens[4]
|
|
25
|
+
if (Number.isNaN(columns) || columns < 2 || columns > 6 || !Breakpoints.includes(starting)) {
|
|
26
|
+
return undefined
|
|
27
|
+
}
|
|
28
|
+
spec = {
|
|
29
|
+
columns,
|
|
30
|
+
starting
|
|
31
|
+
}
|
|
32
|
+
} break
|
|
33
|
+
// no other types supported yet
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
layout,
|
|
37
|
+
spec
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return undefined
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const GroupBlockComponent: React.FC<{
|
|
44
|
+
block: Block
|
|
45
|
+
className?: string,
|
|
46
|
+
}> = ({
|
|
47
|
+
block,
|
|
48
|
+
className=''
|
|
49
|
+
}) => {
|
|
50
|
+
|
|
51
|
+
if (block.blockType !== 'group') {
|
|
52
|
+
return <>group block required</>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const group = block as GroupBlock
|
|
56
|
+
|
|
57
|
+
// only one supported so fat
|
|
58
|
+
if (group.specifiers?.includes('layout')) {
|
|
59
|
+
const layoutSpec = getLayoutInfo(group.specifiers)
|
|
60
|
+
if (!layoutSpec) {
|
|
61
|
+
return <>invalid or missing layout specifier in group block!</>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (layoutSpec.layout === 'grid') {
|
|
65
|
+
const { elements } = group
|
|
66
|
+
const { spec: {starting, columns} } = layoutSpec
|
|
67
|
+
|
|
68
|
+
// https://tailwindcss.com/docs/content-configuration#dynamic-class-names
|
|
69
|
+
// All variants in use MUST be in style/safelist.tailwind.js
|
|
70
|
+
const clazzName = cn('grid xs:grid-cols-1 gap-2 sm:gap-3',
|
|
71
|
+
`${starting}:grid-cols-${columns} `,
|
|
72
|
+
className)
|
|
73
|
+
return (
|
|
74
|
+
<div className={clazzName}>
|
|
75
|
+
<Content blocks={elements} />
|
|
76
|
+
</div>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default GroupBlockComponent
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import type { HeadingBlock } from '../def'
|
|
4
|
+
import { ApplyTypography } from '../../primitives'
|
|
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,108 @@
|
|
|
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
|
+
constraint?: Dimensions
|
|
13
|
+
}> = ({
|
|
14
|
+
block,
|
|
15
|
+
className='',
|
|
16
|
+
agent,
|
|
17
|
+
constraint
|
|
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
|
+
fullWidthOnMobile,
|
|
30
|
+
svgFillClass,
|
|
31
|
+
specifiers
|
|
32
|
+
} = block as ImageBlock
|
|
33
|
+
|
|
34
|
+
const specified = (s: string): boolean => (containsToken(specifiers, s))
|
|
35
|
+
|
|
36
|
+
const toSpread: any = {}
|
|
37
|
+
if (props?.fill === undefined) {
|
|
38
|
+
const dimCon = (constraint ? constrain(dim, constraint) : dim)
|
|
39
|
+
toSpread.width = dimCon.w
|
|
40
|
+
toSpread.height = dimCon.h
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let _alt: string
|
|
44
|
+
if (alt) {
|
|
45
|
+
_alt = alt
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const tokens = src.split('/')
|
|
49
|
+
// Something remotely meaningful
|
|
50
|
+
_alt = (tokens.length > 0) ? tokens[tokens.length] : src
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const _svgFillClass = svgFillClass ?? ''
|
|
54
|
+
|
|
55
|
+
// TODO: use two elements with 'md:hidden' for 3/4 size
|
|
56
|
+
// https://nextjs.org/docs/app/building-your-application/optimizing/images#responsive
|
|
57
|
+
if (agent === 'phone' ) {
|
|
58
|
+
if (specified('mobile-full-width') || fullWidthOnMobile) {
|
|
59
|
+
const toSpread: any = {
|
|
60
|
+
style: {
|
|
61
|
+
width: '100%',
|
|
62
|
+
height: 'auto',
|
|
63
|
+
maxWidth: '420px'
|
|
64
|
+
},
|
|
65
|
+
sizes: '100vw',
|
|
66
|
+
}
|
|
67
|
+
// only for aspect ratio and to satisfy parser
|
|
68
|
+
toSpread.width = dim.w
|
|
69
|
+
toSpread.height = dim.h
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div className='flex flex-col items-center w-full'>
|
|
73
|
+
<Image src={src} alt={_alt} {...toSpread} className={cn(_svgFillClass, className)}/>
|
|
74
|
+
</div>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
else if (!specified('mobile-no-scale')) {
|
|
78
|
+
if (props?.style?.width === 'auto' && typeof props.style.height === 'number' ) {
|
|
79
|
+
props.style.height = props.style.height *.75
|
|
80
|
+
}
|
|
81
|
+
else if (props?.style?.height === 'auto' && typeof props?.style?.width === 'number' ) {
|
|
82
|
+
props.style.width = props.style.width *.75
|
|
83
|
+
}
|
|
84
|
+
else if (props?.style && !props?.style.width) {
|
|
85
|
+
if (dim) {
|
|
86
|
+
toSpread.width = +dim.w * .75
|
|
87
|
+
toSpread.height = +dim.h * .75
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const right = containsToken(specifiers, 'right')
|
|
94
|
+
const center = containsToken(specifiers, 'center')
|
|
95
|
+
|
|
96
|
+
const alignSelfClx = right ? 'self-end' : (center ? 'self-center' : 'self-start')
|
|
97
|
+
|
|
98
|
+
return (props?.fill) ? (
|
|
99
|
+
<div className='relative w-full h-full'>
|
|
100
|
+
<Image src={src} alt={_alt} {...toSpread} {...props} className={cn(_svgFillClass, 'max-w-[70vw] mx-auto', className)}/>
|
|
101
|
+
</div>
|
|
102
|
+
) : (
|
|
103
|
+
<Image src={src} alt={_alt} {...toSpread} {...props} className={cn(alignSelfClx, _svgFillClass, 'max-w-[70vw] mx-auto', className)}/>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export default ImageBlockComponent
|
|
108
|
+
|
|
@@ -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
|
+
}
|