blocks-dusted 0.1.12 → 0.1.13
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/package.json +1 -1
- package/src/installers/patchRenderBlocks.js +1 -1
- package/src/registry/validateTemplateManifest.js +2 -1
- package/templates/blocks/BannerDD790432/Component.tsx +63 -0
- package/templates/blocks/BannerDD790432/config.ts +54 -0
- package/templates/blocks/BannerDD790432/manifest.json +80 -0
- package/templates/blocks/Button47/Component.tsx +124 -0
- package/templates/blocks/Button47/config.ts +75 -0
- package/templates/blocks/Button47/link.ts +22 -0
- package/templates/blocks/Button47/manifest.json +105 -0
- package/templates/blocks/Button47/styles.module.scss +75 -0
- package/templates/blocks/DD-Accordion/Component.tsx +55 -36
- package/templates/blocks/DD-Accordion/config.ts +71 -59
- package/templates/blocks/DD-AnimText/Component.tsx +50 -32
- package/templates/blocks/DD-AnimText/config.ts +296 -295
- package/templates/blocks/DD-AsideNav/Component.tsx +323 -319
- package/templates/blocks/DD-Banner/Component.tsx +54 -51
- package/templates/blocks/DD-BookCall/Component.tsx +535 -535
- package/templates/blocks/DD-ButtonPop/Component.tsx +118 -117
- package/templates/blocks/DD-Card/Component.tsx +4 -4
- package/templates/blocks/DD-CardTemplate01/Component.tsx +139 -140
- package/templates/blocks/DD-CardsWithVariantV3/Component.tsx +472 -0
- package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/index.tsx +203 -0
- package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/styles.ts +87 -0
- package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/useInjectStyle.ts +39 -0
- package/templates/blocks/DD-CardsWithVariantV3/config.ts +326 -0
- package/templates/blocks/DD-CardsWithVariantV3/manifest.json +110 -0
- package/templates/blocks/DD-Carousel-A/Component.tsx +251 -249
- package/templates/blocks/DD-Carousel-B/Component.tsx +405 -403
- package/templates/blocks/DD-ComparisonTable/Component.tsx +255 -256
- package/templates/blocks/DD-Contact/Component.tsx +433 -435
- package/templates/blocks/DD-Counter/Component.tsx +11 -6
- package/templates/blocks/DD-DeliCard/Component.tsx +303 -0
- package/templates/blocks/DD-DeliCard/config.ts +186 -0
- package/templates/blocks/DD-DeliCard/manifest.json +94 -0
- package/templates/blocks/DD-FeatCat/Component.tsx +302 -302
- package/templates/blocks/DD-FeatProd/CircularGallery.tsx +1179 -0
- package/templates/blocks/DD-FeatProd/Component.tsx +484 -0
- package/templates/blocks/DD-FeatProd/config.ts +173 -0
- package/templates/blocks/DD-FeatProd/manifest.json +93 -0
- package/templates/blocks/DD-FeatServ/Component.tsx +607 -0
- package/templates/blocks/DD-FeatServ/config.ts +213 -0
- package/templates/blocks/DD-FeatServ/manifest.json +113 -0
- package/templates/blocks/DD-FeatStrip/Component.tsx +584 -585
- package/templates/blocks/DD-FluidBtn/ButtonsEyeCatching.tsx +627 -628
- package/templates/blocks/DD-FluidBtn/Component.tsx +46 -43
- package/templates/blocks/DD-GooeyInfo/Component.tsx +150 -149
- package/templates/blocks/DD-GooeyInfo/config.ts +136 -135
- package/templates/blocks/DD-Hero/Component.tsx +296 -297
- package/templates/blocks/DD-HeroPlas/Component.tsx +50 -0
- package/templates/blocks/DD-HeroPlas/ThreeScene.tsx +109 -0
- package/templates/blocks/DD-HeroPlas/config.ts +33 -0
- package/templates/blocks/DD-HeroPlas/manifest.json +88 -0
- package/templates/blocks/DD-HorizontalScroll/Component.tsx +937 -936
- package/templates/blocks/DD-Icon/Component.tsx +46 -81
- package/templates/blocks/DD-IconButton/Component.tsx +13 -20
- package/templates/blocks/DD-IdTag/Component.tsx +22 -21
- package/templates/blocks/DD-LineAnim/Component.tsx +42 -36
- package/templates/blocks/DD-LogoMarq/Component.tsx +38 -48
- package/templates/blocks/DD-Lottie/Component.tsx +11 -3
- package/templates/blocks/DD-MasonaryMedia/Component.tsx +544 -545
- package/templates/blocks/DD-MasonaryMedia/config.ts +389 -389
- package/templates/blocks/DD-Masonry/Component.tsx +654 -644
- package/templates/blocks/DD-Masonry/config.ts +475 -475
- package/templates/blocks/DD-MaterialSwatches/Component.tsx +85 -0
- package/templates/blocks/DD-MaterialSwatches/config.ts +232 -0
- package/templates/blocks/DD-MaterialSwatches/manifest.json +84 -0
- package/templates/blocks/DD-MaterialSwatches/material.ts +107 -0
- package/templates/blocks/DD-NineCanvas/Component.tsx +129 -0
- package/templates/blocks/DD-NineCanvas/RenderBlocks.tsx +244 -0
- package/templates/blocks/DD-NineCanvas/allowedCanvasBlocks.ts +6 -0
- package/templates/blocks/DD-NineCanvas/blockComponents.tsx +8 -0
- package/templates/blocks/DD-NineCanvas/canvas/CanvasGrid.tsx +316 -0
- package/templates/blocks/DD-NineCanvas/canvas/RadialNav.tsx +432 -0
- package/templates/blocks/DD-NineCanvas/canvas/canvas-panels.css +889 -0
- package/templates/blocks/DD-NineCanvas/canvas/icons/custom-icons.tsx +364 -0
- package/templates/blocks/DD-NineCanvas/config.ts +61 -0
- package/templates/blocks/DD-NineCanvas/manifest.json +165 -0
- package/templates/blocks/DD-NineCanvas/styles.scss +152 -0
- package/templates/blocks/DD-NineCanvas/tooltip/get-strict-context.tsx +26 -0
- package/templates/blocks/DD-NineCanvas/tooltip/primitives.tsx +178 -0
- package/templates/blocks/DD-NineCanvas/tooltip/tooltip.tsx +71 -0
- package/templates/blocks/DD-NineCanvas/tooltip/use-controlled-state.tsx +33 -0
- package/templates/blocks/DD-NineCanvas/utilities/FadeOnScroll.tsx +68 -0
- package/templates/blocks/DD-NineCanvas/utilities/ParallaxWrapper.tsx +157 -0
- package/templates/blocks/DD-NineCanvas/utilities/renderCustomCSS.ts +5 -0
- package/templates/blocks/DD-NineCanvas/utilities/webglCleanup.ts +27 -0
- package/templates/blocks/DD-PayloadAIAssistant/Component.tsx +78 -0
- package/templates/blocks/DD-PayloadAIAssistant/DustedBot.tsx +590 -0
- package/templates/blocks/DD-PayloadAIAssistant/config.ts +37 -0
- package/templates/blocks/DD-PayloadAIAssistant/demo.css +125 -0
- package/templates/blocks/DD-PayloadAIAssistant/manifest.json +93 -0
- package/templates/blocks/DD-PayloadAIAssistant/source-defaults.ts +17 -0
- package/templates/blocks/DD-PayloadAIAssistant/styles.ts +306 -0
- package/templates/blocks/DD-Pricing/Component.tsx +2 -3
- package/templates/blocks/DD-Pricing/config.ts +215 -215
- package/templates/blocks/DD-Process/Component.tsx +2 -2
- package/templates/blocks/DD-ProjHeader/Component.tsx +93 -91
- package/templates/blocks/DD-ProjSlider/Component.tsx +1 -1
- package/templates/blocks/DD-Section/Component.tsx +265 -266
- package/templates/blocks/DD-Separator/Component.tsx +66 -66
- package/templates/blocks/DD-Services/Component.tsx +176 -176
- package/templates/blocks/DD-ShowcaseGrid/Component.tsx +306 -307
- package/templates/blocks/DD-Slider-A/Component.tsx +2 -2
- package/templates/blocks/DD-StackCards/Component.tsx +172 -171
- package/templates/blocks/DD-Team/Component.tsx +247 -247
- package/templates/blocks/DD-TeamCards/Component.tsx +251 -251
- package/templates/blocks/DD-TechStack/Component.tsx +173 -174
- package/templates/blocks/DD-TestMarq/Component.tsx +158 -132
- package/templates/blocks/DD-Testim/Component.tsx +247 -247
- package/templates/blocks/DD-Testim2/Component.tsx +265 -256
- package/templates/blocks/DD-Testimonials/Component.tsx +139 -139
- package/templates/blocks/DD-Thanks/Component.tsx +1 -1
- package/templates/blocks/DD-Work/Component.tsx +318 -318
- package/templates/blocks/DD-Work-B/Component.tsx +294 -294
- package/templates/blocks/DDHG-CTASection2/Component.tsx +183 -184
- package/templates/blocks/DDHG-HighlightArc/Component.tsx +6 -3
- package/templates/blocks/DDHG-LogoMarquee/Component.tsx +45 -8
- package/templates/blocks/DDHG-NavCodeGrid/Component.tsx +1 -3
- package/templates/blocks/DDHG-ServiceDetails/ServiceDetailCard.tsx +9 -22
- package/templates/blocks/DDHG-ServiceHero/Component.tsx +10 -10
- package/templates/blocks/DDHG-TeamCards/Component.tsx +6 -35
- package/templates/blocks/Form/Checkbox/index.tsx +45 -0
- package/templates/blocks/Form/Component.tsx +163 -0
- package/templates/blocks/Form/Country/index.tsx +65 -0
- package/templates/blocks/Form/Country/options.ts +982 -0
- package/templates/blocks/Form/Email/index.tsx +38 -0
- package/templates/blocks/Form/Error/index.tsx +15 -0
- package/templates/blocks/Form/Message/index.tsx +13 -0
- package/templates/blocks/Form/Number/index.tsx +36 -0
- package/templates/blocks/Form/Select/index.tsx +63 -0
- package/templates/blocks/Form/State/index.tsx +64 -0
- package/templates/blocks/Form/State/options.ts +52 -0
- package/templates/blocks/Form/Text/index.tsx +32 -0
- package/templates/blocks/Form/Textarea/index.tsx +40 -0
- package/templates/blocks/Form/Width/index.tsx +13 -0
- package/templates/blocks/Form/config.ts +51 -0
- package/templates/blocks/Form/fields.tsx +21 -0
- package/templates/blocks/Form/manifest.json +168 -0
- package/templates/blocks/MediaBlock/Component.tsx +67 -0
- package/templates/blocks/MediaBlock/config.ts +14 -0
- package/templates/blocks/MediaBlock/manifest.json +92 -0
- package/templates/blocks/TransitionsDustedShowcase/Component.tsx +9 -0
- package/templates/blocks/TransitionsDustedShowcase/config.ts +13 -0
- package/templates/blocks/TransitionsDustedShowcase/manifest.json +80 -0
- package/templates/blocks/rawHtml/Component.tsx +25 -0
- package/templates/blocks/rawHtml/config.ts +56 -0
- package/templates/blocks/rawHtml/manifest.json +80 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import { cn } from '@/utilities/ui'
|
|
3
|
+
import React, { Fragment } from 'react'
|
|
4
|
+
|
|
5
|
+
import { AnimatePresence } from 'framer-motion'
|
|
6
|
+
import { FadeOnScroll } from './utilities/FadeOnScroll'
|
|
7
|
+
import { extractCustomCSS } from './utilities/renderCustomCSS'
|
|
8
|
+
import ParallaxWrapper from './utilities/ParallaxWrapper'
|
|
9
|
+
import { blockComponents } from './blockComponents'
|
|
10
|
+
|
|
11
|
+
interface BlockProps {
|
|
12
|
+
sectionId?: string
|
|
13
|
+
disableInnerContainer?: boolean
|
|
14
|
+
|
|
15
|
+
scrollBehavior?: 'normal' | 'stickToTop' | 'stickToTopFade' | 'parallax'
|
|
16
|
+
|
|
17
|
+
sticky?: boolean
|
|
18
|
+
stickyTopOffset?: number
|
|
19
|
+
stickyZIndex?: number
|
|
20
|
+
|
|
21
|
+
fadeOnScroll?: boolean
|
|
22
|
+
useAbsoluteScroll?: boolean
|
|
23
|
+
fadeStartProgress?: number
|
|
24
|
+
fadeStartPixels?: number
|
|
25
|
+
fadeEndProgress?: number
|
|
26
|
+
fadeEndPixels?: number
|
|
27
|
+
fadeFinalOpacity?: number
|
|
28
|
+
fadeOffsetStart?: 'start start' | 'start center' | 'start end' | `start ${number}%`
|
|
29
|
+
fadeOffsetEnd?: 'start start' | 'start center' | 'start end' | `start ${number}%`
|
|
30
|
+
|
|
31
|
+
parallaxEnabled?: boolean
|
|
32
|
+
parallaxDistance?: number
|
|
33
|
+
parallaxDirection?: 'up' | 'down'
|
|
34
|
+
showScrollDebugMarkers?: boolean
|
|
35
|
+
gsapSelector?: string
|
|
36
|
+
pinTargetSelector?: string
|
|
37
|
+
pinSpacing?: number
|
|
38
|
+
scrollDuration?: number
|
|
39
|
+
scrollSpeed?: number
|
|
40
|
+
|
|
41
|
+
sectionStyle?: React.CSSProperties
|
|
42
|
+
|
|
43
|
+
initialYFadeValue?: number
|
|
44
|
+
isFadeOnce?: boolean
|
|
45
|
+
fadeStiffValue?: number
|
|
46
|
+
fadeDampValue?: number
|
|
47
|
+
exitYFadeValue?: number | string
|
|
48
|
+
|
|
49
|
+
[key: string]: any
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const toNumber = (value: unknown, fallback: number): number => {
|
|
53
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value
|
|
54
|
+
|
|
55
|
+
if (typeof value === 'string') {
|
|
56
|
+
const parsed = Number(value)
|
|
57
|
+
if (Number.isFinite(parsed)) return parsed
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return fallback
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const RenderBlocks: React.FC<{
|
|
64
|
+
blocks: Array<BlockProps & { blockType: string }>
|
|
65
|
+
}> = (props) => {
|
|
66
|
+
const { blocks } = props
|
|
67
|
+
|
|
68
|
+
if (!blocks || !Array.isArray(blocks) || blocks.length === 0) return null
|
|
69
|
+
|
|
70
|
+
const customCSS = extractCustomCSS(blocks as any)
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<>
|
|
74
|
+
{customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
|
|
75
|
+
|
|
76
|
+
{blocks.map((block, index) => {
|
|
77
|
+
const { blockType } = block
|
|
78
|
+
|
|
79
|
+
if (!blockType || !(blockType in blockComponents)) return null
|
|
80
|
+
|
|
81
|
+
const Block = blockComponents[blockType]
|
|
82
|
+
if (!Block) return null
|
|
83
|
+
|
|
84
|
+
const blockProps = block as BlockProps
|
|
85
|
+
|
|
86
|
+
const key =
|
|
87
|
+
'id' in (block as any) && (block as any).id
|
|
88
|
+
? `block-${(block as any).id}-${blockType}`
|
|
89
|
+
: `block-${index}-${blockType}`
|
|
90
|
+
|
|
91
|
+
const sectionId = blockProps.sectionId
|
|
92
|
+
const scrollSpeed = blockProps.scrollSpeed
|
|
93
|
+
|
|
94
|
+
const scrollBehavior = blockProps.scrollBehavior || 'normal'
|
|
95
|
+
|
|
96
|
+
const isProjectsSliderSticky =
|
|
97
|
+
blockType === 'projectsSlider' && blockProps.stickyToTop === true
|
|
98
|
+
|
|
99
|
+
const isSticky =
|
|
100
|
+
scrollBehavior === 'stickToTop' ||
|
|
101
|
+
scrollBehavior === 'stickToTopFade' ||
|
|
102
|
+
blockProps.sticky === true
|
|
103
|
+
|
|
104
|
+
const isStickyFade =
|
|
105
|
+
scrollBehavior === 'stickToTopFade' ||
|
|
106
|
+
(blockProps.sticky === true && blockProps.fadeOnScroll === true)
|
|
107
|
+
|
|
108
|
+
const isParallax = scrollBehavior === 'parallax' || blockProps.parallaxEnabled === true
|
|
109
|
+
|
|
110
|
+
const stickyTopOffset = toNumber(blockProps.stickyTopOffset, 0)
|
|
111
|
+
const stickyZIndex = toNumber(blockProps.stickyZIndex, 5)
|
|
112
|
+
|
|
113
|
+
const sectionStyle =
|
|
114
|
+
blockProps.sectionStyle && typeof blockProps.sectionStyle === 'object'
|
|
115
|
+
? (blockProps.sectionStyle as React.CSSProperties)
|
|
116
|
+
: undefined
|
|
117
|
+
|
|
118
|
+
const wrapperlessBlockTypes = new Set([
|
|
119
|
+
'asideNav',
|
|
120
|
+
'studioHorizontalScroll',
|
|
121
|
+
'studioStackCards',
|
|
122
|
+
])
|
|
123
|
+
|
|
124
|
+
const shouldRenderWrapperless =
|
|
125
|
+
wrapperlessBlockTypes.has(blockType) ||
|
|
126
|
+
(blockType === 'projectsSlider' && blockProps.stickyToTop === true)
|
|
127
|
+
|
|
128
|
+
if (shouldRenderWrapperless) {
|
|
129
|
+
return <Block suppressHydrationWarning key={key} {...blockProps} disableInnerContainer />
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const blockElement = <Block {...blockProps} disableInnerContainer />
|
|
133
|
+
const isNineCanvasBlock = blockType === 'nineCanvasBlock'
|
|
134
|
+
|
|
135
|
+
const sharedDataProps = {
|
|
136
|
+
suppressHydrationWarning: true,
|
|
137
|
+
id: isNineCanvasBlock ? 'ninecanvas' : sectionId,
|
|
138
|
+
'data-block-type': blockType,
|
|
139
|
+
'data-ninecanvas-gesture-owner': blockType === 'DD-Carousel-A' ? true : undefined,
|
|
140
|
+
'data-projects-slider-sticky': isProjectsSliderSticky ? 'true' : undefined,
|
|
141
|
+
'data-scroll': scrollSpeed ? true : undefined,
|
|
142
|
+
'data-scroll-speed': scrollSpeed ? String(scrollSpeed) : undefined,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (isParallax) {
|
|
146
|
+
return (
|
|
147
|
+
<AnimatePresence key={key} mode="sync">
|
|
148
|
+
<section
|
|
149
|
+
{...sharedDataProps}
|
|
150
|
+
className={cn('m-0 renderblock-container relative overflow-visible', sectionId)}
|
|
151
|
+
style={sectionStyle}
|
|
152
|
+
>
|
|
153
|
+
<ParallaxWrapper
|
|
154
|
+
sectionId={sectionId}
|
|
155
|
+
animationConfig={{
|
|
156
|
+
scrollToSelector: blockProps.scrollToSelector,
|
|
157
|
+
triggerSelector: blockProps.gsapSelector,
|
|
158
|
+
pinTargetSelector: blockProps.pinTargetSelector,
|
|
159
|
+
pinSpacing: blockProps.pinSpacing,
|
|
160
|
+
scrollDuration: blockProps.scrollDuration,
|
|
161
|
+
parallaxDistance: blockProps.parallaxDistance ?? 120,
|
|
162
|
+
parallaxDirection: blockProps.parallaxDirection ?? 'up',
|
|
163
|
+
markers: blockProps.showScrollDebugMarkers,
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
{blockElement}
|
|
167
|
+
</ParallaxWrapper>
|
|
168
|
+
</section>
|
|
169
|
+
</AnimatePresence>
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (isStickyFade) {
|
|
174
|
+
return (
|
|
175
|
+
<AnimatePresence key={key} mode="sync">
|
|
176
|
+
<section
|
|
177
|
+
{...sharedDataProps}
|
|
178
|
+
className={cn('m-0 renderblock-container sticky overflow-visible', sectionId)}
|
|
179
|
+
style={{
|
|
180
|
+
top: `${stickyTopOffset}px`,
|
|
181
|
+
zIndex: stickyZIndex,
|
|
182
|
+
...sectionStyle,
|
|
183
|
+
}}
|
|
184
|
+
>
|
|
185
|
+
<FadeOnScroll
|
|
186
|
+
startFade={
|
|
187
|
+
blockProps.useAbsoluteScroll
|
|
188
|
+
? (blockProps.fadeStartPixels ?? 1000)
|
|
189
|
+
: (blockProps.fadeStartProgress ?? 0)
|
|
190
|
+
}
|
|
191
|
+
endFade={
|
|
192
|
+
blockProps.useAbsoluteScroll
|
|
193
|
+
? (blockProps.fadeEndPixels ?? 2000)
|
|
194
|
+
: (blockProps.fadeEndProgress ?? 0.75)
|
|
195
|
+
}
|
|
196
|
+
finalOpacity={blockProps.fadeFinalOpacity ?? 0}
|
|
197
|
+
offsetStart={blockProps.fadeOffsetStart ?? 'start 50%'}
|
|
198
|
+
offsetEnd={blockProps.fadeOffsetEnd ?? 'start 10%'}
|
|
199
|
+
useAbsoluteScroll={blockProps.useAbsoluteScroll ?? false}
|
|
200
|
+
>
|
|
201
|
+
{blockElement}
|
|
202
|
+
</FadeOnScroll>
|
|
203
|
+
</section>
|
|
204
|
+
</AnimatePresence>
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (isSticky) {
|
|
209
|
+
return (
|
|
210
|
+
<AnimatePresence key={key} mode="sync">
|
|
211
|
+
<section
|
|
212
|
+
{...sharedDataProps}
|
|
213
|
+
className={cn('m-0 renderblock-container sticky overflow-visible', sectionId)}
|
|
214
|
+
style={{
|
|
215
|
+
top: `${stickyTopOffset}px`,
|
|
216
|
+
zIndex: stickyZIndex,
|
|
217
|
+
...sectionStyle,
|
|
218
|
+
}}
|
|
219
|
+
>
|
|
220
|
+
{blockElement}
|
|
221
|
+
</section>
|
|
222
|
+
</AnimatePresence>
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
<AnimatePresence key={key} mode="sync">
|
|
228
|
+
<section
|
|
229
|
+
{...sharedDataProps}
|
|
230
|
+
className={cn(
|
|
231
|
+
'm-0 renderblock-container relative overflow-visible',
|
|
232
|
+
isNineCanvasBlock && 'ninecanvas',
|
|
233
|
+
sectionId,
|
|
234
|
+
)}
|
|
235
|
+
style={sectionStyle}
|
|
236
|
+
>
|
|
237
|
+
{blockElement}
|
|
238
|
+
</section>
|
|
239
|
+
</AnimatePresence>
|
|
240
|
+
)
|
|
241
|
+
})}
|
|
242
|
+
</>
|
|
243
|
+
)
|
|
244
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Demo-only child registry. The original HZ registry is retained in source/.
|
|
2
|
+
import { Content } from '../Content/config'
|
|
3
|
+
import { DDCarouselA } from '../DD-Carousel-A/config'
|
|
4
|
+
import { DDProgress } from '../DD-Progress/config'
|
|
5
|
+
import { RawHtmlBlock } from '../rawHtml/config'
|
|
6
|
+
export const allowedCanvasBlocks = [Content, DDCarouselA, DDProgress, RawHtmlBlock]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import dynamic from 'next/dynamic'
|
|
2
|
+
import type { ComponentType } from 'react'
|
|
3
|
+
export const blockComponents: Record<string, ComponentType<any>> = {
|
|
4
|
+
content: dynamic(() => import('../Content/Component').then((m) => m.ContentBlock)),
|
|
5
|
+
'DD-Carousel-A': dynamic(() => import('../DD-Carousel-A/Component').then((m) => m.DDCarouselA)),
|
|
6
|
+
'DD-Progress': dynamic(() => import('../DD-Progress/Component').then((m) => m.DDProgress)),
|
|
7
|
+
rawHtml: dynamic(() => import('../rawHtml/Component').then((m) => m.RawHtmlBlock)),
|
|
8
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
import { getClientPerformanceMode } from '../utilities/webglCleanup'
|
|
4
|
+
|
|
5
|
+
export type CanvasPanel = {
|
|
6
|
+
id: string
|
|
7
|
+
x: number
|
|
8
|
+
y: number
|
|
9
|
+
label: string
|
|
10
|
+
content?: React.ReactNode
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type CanvasGridProps = {
|
|
14
|
+
activeCanvas: CanvasPanel
|
|
15
|
+
canvases: CanvasPanel[]
|
|
16
|
+
onCanvasChange: (canvasId: string) => void
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function CanvasGrid({ activeCanvas, canvases, onCanvasChange }: CanvasGridProps) {
|
|
20
|
+
const trackRef = React.useRef<HTMLDivElement>(null)
|
|
21
|
+
const touchStartRef = React.useRef<{
|
|
22
|
+
x: number
|
|
23
|
+
y: number
|
|
24
|
+
lastY: number
|
|
25
|
+
target: EventTarget | null
|
|
26
|
+
canvasGestureAllowed: boolean
|
|
27
|
+
scrolled?: boolean
|
|
28
|
+
} | null>(null)
|
|
29
|
+
const activeContentRef = React.useRef<HTMLDivElement | null>(null)
|
|
30
|
+
|
|
31
|
+
React.useEffect(() => {
|
|
32
|
+
const el = activeContentRef.current
|
|
33
|
+
if (!el) return
|
|
34
|
+
|
|
35
|
+
const reducedPerformance = getClientPerformanceMode() === 'reduced'
|
|
36
|
+
if (reducedPerformance) {
|
|
37
|
+
const previousOverflowY = el.style.overflowY
|
|
38
|
+
el.style.overflowY = 'auto'
|
|
39
|
+
window.dispatchEvent(new CustomEvent('locomotive:resize'))
|
|
40
|
+
return () => {
|
|
41
|
+
el.style.overflowY = previousOverflowY
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let cancelled = false
|
|
46
|
+
let scroll: { destroy: () => void } | undefined
|
|
47
|
+
let secondFrame = 0
|
|
48
|
+
|
|
49
|
+
const firstFrame = window.requestAnimationFrame(() => {
|
|
50
|
+
secondFrame = window.requestAnimationFrame(async () => {
|
|
51
|
+
const { default: LocomotiveScroll } = await import('locomotive-scroll')
|
|
52
|
+
if (cancelled) return
|
|
53
|
+
|
|
54
|
+
scroll = new LocomotiveScroll({
|
|
55
|
+
lenisOptions: { wrapper: el, content: el, eventsTarget: el, lerp: 0.08 },
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
return () => {
|
|
61
|
+
cancelled = true
|
|
62
|
+
window.cancelAnimationFrame(firstFrame)
|
|
63
|
+
window.cancelAnimationFrame(secondFrame)
|
|
64
|
+
scroll?.destroy()
|
|
65
|
+
}
|
|
66
|
+
}, [activeCanvas.id])
|
|
67
|
+
|
|
68
|
+
React.useEffect(() => {
|
|
69
|
+
const track = trackRef.current
|
|
70
|
+
if (!track) return
|
|
71
|
+
|
|
72
|
+
const getActivePanel = (target: EventTarget | null) => {
|
|
73
|
+
if (!(target instanceof Element)) return null
|
|
74
|
+
|
|
75
|
+
const panel = target.closest<HTMLElement>('.hzCanvasPanel--active')
|
|
76
|
+
return panel && track.contains(panel) ? panel : null
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const getScrollableRegions = (panel: HTMLElement, target: EventTarget | null) => {
|
|
80
|
+
const regions: HTMLElement[] = []
|
|
81
|
+
let element =
|
|
82
|
+
target instanceof HTMLElement
|
|
83
|
+
? target
|
|
84
|
+
: target instanceof Element
|
|
85
|
+
? target.parentElement
|
|
86
|
+
: null
|
|
87
|
+
|
|
88
|
+
while (element && element !== panel) {
|
|
89
|
+
const { overflowY } = window.getComputedStyle(element)
|
|
90
|
+
const canOverflow = /^(auto|scroll|overlay)$/.test(overflowY)
|
|
91
|
+
|
|
92
|
+
if (canOverflow && element.scrollHeight > element.clientHeight) {
|
|
93
|
+
regions.push(element)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
element = element.parentElement
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const canvasContent = panel.querySelector<HTMLElement>('.hzCanvasContent')
|
|
100
|
+
if (canvasContent && !regions.includes(canvasContent)) regions.push(canvasContent)
|
|
101
|
+
|
|
102
|
+
return regions
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const scrollElement = (element: HTMLElement, deltaY: number) => {
|
|
106
|
+
const maxScrollTop = element.scrollHeight - element.clientHeight
|
|
107
|
+
if (maxScrollTop <= 0) return false
|
|
108
|
+
|
|
109
|
+
const nextScrollTop = Math.min(Math.max(element.scrollTop + deltaY, 0), maxScrollTop)
|
|
110
|
+
if (nextScrollTop === element.scrollTop) return false
|
|
111
|
+
|
|
112
|
+
element.scrollTop = nextScrollTop
|
|
113
|
+
return true
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const scrollFromTarget = (panel: HTMLElement, target: EventTarget | null, deltaY: number) =>
|
|
117
|
+
getScrollableRegions(panel, target).some((region) => scrollElement(region, deltaY))
|
|
118
|
+
|
|
119
|
+
const interactiveGestureSelector = [
|
|
120
|
+
'.carousel',
|
|
121
|
+
'.carouselList',
|
|
122
|
+
'.carouselNav',
|
|
123
|
+
'[class*="carousel-"]',
|
|
124
|
+
'[class*="Carousel"]',
|
|
125
|
+
'.embla',
|
|
126
|
+
'.embla__viewport',
|
|
127
|
+
'.embla__container',
|
|
128
|
+
'.swiper',
|
|
129
|
+
'.swiper-container',
|
|
130
|
+
'.swiper-wrapper',
|
|
131
|
+
'.swiper-slide',
|
|
132
|
+
'.ultimate-embla-carousel',
|
|
133
|
+
'.ultimate-embla-viewport',
|
|
134
|
+
'.ultimate-embla-container',
|
|
135
|
+
'[data-carousel-id]',
|
|
136
|
+
'[data-projects-slider-scroll-scene]',
|
|
137
|
+
'[data-ninecanvas-gesture-owner]',
|
|
138
|
+
'button',
|
|
139
|
+
'a',
|
|
140
|
+
'input',
|
|
141
|
+
'select',
|
|
142
|
+
'textarea',
|
|
143
|
+
'[role="button"]',
|
|
144
|
+
'[role="slider"]',
|
|
145
|
+
'[contenteditable="true"]',
|
|
146
|
+
].join(',')
|
|
147
|
+
|
|
148
|
+
const canUseCanvasGesture = (panel: HTMLElement, target: EventTarget | null) => {
|
|
149
|
+
if (!(target instanceof Element)) return false
|
|
150
|
+
|
|
151
|
+
const gestureOwner = target.closest(interactiveGestureSelector)
|
|
152
|
+
return !gestureOwner || !panel.contains(gestureOwner)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const getAdjacentCanvas = (deltaX: number, deltaY: number) => {
|
|
156
|
+
const absX = Math.abs(deltaX)
|
|
157
|
+
const absY = Math.abs(deltaY)
|
|
158
|
+
const xOffset = absX > absY ? (deltaX < 0 ? 1 : -1) : 0
|
|
159
|
+
const yOffset = absY >= absX ? (deltaY > 0 ? -1 : 1) : 0
|
|
160
|
+
|
|
161
|
+
return canvases.find(
|
|
162
|
+
(canvas) => canvas.x === activeCanvas.x + xOffset && canvas.y === activeCanvas.y + yOffset,
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const changeCanvasFromTouch = (deltaX: number, deltaY: number) => {
|
|
167
|
+
const nextCanvas = getAdjacentCanvas(deltaX, deltaY)
|
|
168
|
+
if (!nextCanvas) return false
|
|
169
|
+
|
|
170
|
+
onCanvasChange(nextCanvas.id)
|
|
171
|
+
return true
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const notifyCanvasScrollDown = (deltaY: number) => {
|
|
175
|
+
if (deltaY > 0) {
|
|
176
|
+
window.dispatchEvent(new CustomEvent('hz:canvas-scroll-down'))
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const handleWheel = (event: WheelEvent) => {
|
|
181
|
+
const panel = getActivePanel(event.target)
|
|
182
|
+
if (!panel) return
|
|
183
|
+
|
|
184
|
+
const deltaMultiplier =
|
|
185
|
+
event.deltaMode === WheelEvent.DOM_DELTA_LINE
|
|
186
|
+
? 16
|
|
187
|
+
: event.deltaMode === WheelEvent.DOM_DELTA_PAGE
|
|
188
|
+
? panel.clientHeight
|
|
189
|
+
: 1
|
|
190
|
+
|
|
191
|
+
const deltaY = event.deltaY * deltaMultiplier
|
|
192
|
+
|
|
193
|
+
if (!scrollFromTarget(panel, event.target, deltaY)) return
|
|
194
|
+
|
|
195
|
+
notifyCanvasScrollDown(deltaY)
|
|
196
|
+
|
|
197
|
+
event.preventDefault()
|
|
198
|
+
event.stopPropagation()
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const handleTouchStart = (event: TouchEvent) => {
|
|
202
|
+
const panel = getActivePanel(event.target)
|
|
203
|
+
const touch = event.touches[0]
|
|
204
|
+
if (!panel || !touch) return
|
|
205
|
+
|
|
206
|
+
touchStartRef.current = {
|
|
207
|
+
x: touch.clientX,
|
|
208
|
+
y: touch.clientY,
|
|
209
|
+
lastY: touch.clientY,
|
|
210
|
+
target: event.target,
|
|
211
|
+
canvasGestureAllowed: canUseCanvasGesture(panel, event.target),
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const handleTouchMove = (event: TouchEvent) => {
|
|
216
|
+
const panel = getActivePanel(event.target)
|
|
217
|
+
const touch = event.touches[0]
|
|
218
|
+
const touchStart = touchStartRef.current
|
|
219
|
+
if (!panel || !touchStart || !touch) return
|
|
220
|
+
|
|
221
|
+
const deltaY = touchStart.lastY - touch.clientY
|
|
222
|
+
touchStart.lastY = touch.clientY
|
|
223
|
+
|
|
224
|
+
if (scrollFromTarget(panel, event.target, deltaY)) {
|
|
225
|
+
touchStart.scrolled = true
|
|
226
|
+
notifyCanvasScrollDown(deltaY)
|
|
227
|
+
|
|
228
|
+
event.preventDefault()
|
|
229
|
+
event.stopPropagation()
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const handleTouchEnd = (event: TouchEvent) => {
|
|
234
|
+
const panel =
|
|
235
|
+
getActivePanel(event.target) ?? getActivePanel(touchStartRef.current?.target ?? null)
|
|
236
|
+
const touchStart = touchStartRef.current
|
|
237
|
+
touchStartRef.current = null
|
|
238
|
+
|
|
239
|
+
if (!panel || !touchStart || !touchStart.canvasGestureAllowed) return
|
|
240
|
+
|
|
241
|
+
const touch = event.changedTouches[0]
|
|
242
|
+
if (!touch) return
|
|
243
|
+
|
|
244
|
+
const deltaX = touch.clientX - touchStart.x
|
|
245
|
+
const deltaY = touchStart.y - touch.clientY
|
|
246
|
+
const absX = Math.abs(deltaX)
|
|
247
|
+
const absY = Math.abs(deltaY)
|
|
248
|
+
const isHorizontalSwipe = absX >= 56 && absX > absY * 1.25
|
|
249
|
+
const isVerticalSwipe = absY >= 56 && absY > absX * 1.25
|
|
250
|
+
|
|
251
|
+
if (!isHorizontalSwipe && !isVerticalSwipe) return
|
|
252
|
+
// A vertical gesture consumed by nested scrolling must not also change panels.
|
|
253
|
+
if (isVerticalSwipe && touchStart.scrolled) return
|
|
254
|
+
|
|
255
|
+
if (changeCanvasFromTouch(deltaX, deltaY)) {
|
|
256
|
+
event.preventDefault()
|
|
257
|
+
event.stopPropagation()
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const handleTouchCancel = () => {
|
|
262
|
+
touchStartRef.current = null
|
|
263
|
+
}
|
|
264
|
+
track.addEventListener('wheel', handleWheel, { passive: false })
|
|
265
|
+
track.addEventListener('touchstart', handleTouchStart, { passive: true })
|
|
266
|
+
track.addEventListener('touchmove', handleTouchMove, { passive: false })
|
|
267
|
+
track.addEventListener('touchend', handleTouchEnd, { passive: false })
|
|
268
|
+
track.addEventListener('touchcancel', handleTouchCancel, { passive: true })
|
|
269
|
+
|
|
270
|
+
return () => {
|
|
271
|
+
track.removeEventListener('wheel', handleWheel)
|
|
272
|
+
track.removeEventListener('touchstart', handleTouchStart)
|
|
273
|
+
track.removeEventListener('touchmove', handleTouchMove)
|
|
274
|
+
track.removeEventListener('touchend', handleTouchEnd)
|
|
275
|
+
track.removeEventListener('touchcancel', handleTouchCancel)
|
|
276
|
+
}
|
|
277
|
+
}, [activeCanvas, canvases, onCanvasChange])
|
|
278
|
+
|
|
279
|
+
return (
|
|
280
|
+
<div ref={trackRef} className={`hzCanvasTrack hzCanvasTrack--${activeCanvas.id}`}>
|
|
281
|
+
{canvases.map((canvas) => {
|
|
282
|
+
const isActive = canvas.id === activeCanvas.id
|
|
283
|
+
|
|
284
|
+
return (
|
|
285
|
+
<section
|
|
286
|
+
aria-label={canvas.label}
|
|
287
|
+
aria-hidden={!isActive}
|
|
288
|
+
inert={!isActive}
|
|
289
|
+
className={[
|
|
290
|
+
'hzCanvasPanel',
|
|
291
|
+
isActive ? 'hzCanvasPanel--active' : '',
|
|
292
|
+
`hzCanvasPanel--x-${canvas.x === -1 ? 'neg1' : canvas.x === 1 ? 'pos1' : '0'}`,
|
|
293
|
+
`hzCanvasPanel--y-${canvas.y === -1 ? 'neg1' : canvas.y === 1 ? 'pos1' : '0'}`,
|
|
294
|
+
]
|
|
295
|
+
.filter(Boolean)
|
|
296
|
+
.join(' ')}
|
|
297
|
+
data-active={isActive ? 'true' : undefined}
|
|
298
|
+
key={canvas.id}
|
|
299
|
+
onClick={() => onCanvasChange(canvas.id)}
|
|
300
|
+
>
|
|
301
|
+
<div
|
|
302
|
+
ref={isActive ? activeContentRef : undefined}
|
|
303
|
+
className="hzCanvasContent"
|
|
304
|
+
data-canvas-content={canvas.id}
|
|
305
|
+
data-scroll-container={isActive ? true : undefined}
|
|
306
|
+
onClick={(event) => event.stopPropagation()}
|
|
307
|
+
>
|
|
308
|
+
{canvas.content ||
|
|
309
|
+
(isActive ? <p className="hzCanvasEmpty">{canvas.label}</p> : null)}
|
|
310
|
+
</div>
|
|
311
|
+
</section>
|
|
312
|
+
)
|
|
313
|
+
})}
|
|
314
|
+
</div>
|
|
315
|
+
)
|
|
316
|
+
}
|