@cntrl-site/sdk-nextjs 0.12.8 → 0.13.0
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/lib/components/Item.js +15 -4
- package/lib/components/Section.js +6 -1
- package/lib/components/items/CustomItem.js +18 -1
- package/lib/components/items/ImageItem.js +20 -2
- package/lib/components/items/RectangleItem.js +13 -6
- package/lib/components/items/RichTextItem.js +16 -2
- package/lib/components/items/VideoItem.js +21 -3
- package/lib/components/items/VimeoEmbed.js +48 -15
- package/lib/components/items/YoutubeEmbed.js +15 -1
- package/lib/components/useItemPosition.js +3 -17
- package/lib/components/useSectionColor.js +4 -2
- package/lib/utils/HoverStyles/HoverStyles.js +67 -0
- package/lib/utils/RichTextConverter/RichTextConverter.js +10 -0
- package/lib/utils/getItemTopStyle.js +18 -0
- package/package.json +4 -2
- package/src/components/Item.tsx +15 -5
- package/src/components/Section.tsx +6 -1
- package/src/components/items/CustomItem.tsx +20 -2
- package/src/components/items/ImageItem.tsx +23 -4
- package/src/components/items/RectangleItem.tsx +15 -8
- package/src/components/items/RichTextItem.tsx +18 -4
- package/src/components/items/VideoItem.tsx +30 -11
- package/src/components/items/VimeoEmbed.tsx +62 -29
- package/src/components/items/YoutubeEmbed.tsx +15 -1
- package/src/components/useItemPosition.ts +1 -13
- package/src/components/useSectionColor.ts +7 -3
- package/src/utils/HoverStyles/HoverStyles.ts +74 -0
- package/src/utils/RichTextConverter/RichTextConverter.tsx +10 -0
- package/src/utils/getItemTopStyle.ts +14 -0
package/src/components/Item.tsx
CHANGED
|
@@ -18,12 +18,14 @@ import { VimeoEmbedItem } from './items/VimeoEmbed';
|
|
|
18
18
|
import { YoutubeEmbedItem } from './items/YoutubeEmbed';
|
|
19
19
|
import { CustomItem } from './items/CustomItem';
|
|
20
20
|
import { useCntrlContext } from '../provider/useCntrlContext';
|
|
21
|
-
import {
|
|
21
|
+
import { useItemPosition } from './useItemPosition';
|
|
22
22
|
import { useItemDimensions } from './useItemDimensions';
|
|
23
23
|
import { useCurrentLayout } from '../common/useCurrentLayout';
|
|
24
24
|
import { useItemScale } from './useItemScale';
|
|
25
25
|
import { ScaleAnchorMap } from '../utils/ScaleAnchorMap';
|
|
26
26
|
import { useSectionHeightData } from './useSectionHeightMap';
|
|
27
|
+
import { getHoverStyles, getTransitions } from '../utils/HoverStyles/HoverStyles';
|
|
28
|
+
import { getItemTopStyle } from '../utils/getItemTopStyle';
|
|
27
29
|
|
|
28
30
|
export interface ItemProps<I extends TArticleItemAny> {
|
|
29
31
|
item: I;
|
|
@@ -52,7 +54,7 @@ export const Item: FC<ItemProps<TArticleItemAny>> = ({ item, sectionId}) => {
|
|
|
52
54
|
const sectionHeight = useSectionHeightData(sectionId);
|
|
53
55
|
const layout = useCurrentLayout();
|
|
54
56
|
const { width, height } = useItemDimensions(item, sectionId);
|
|
55
|
-
const layoutValues: Record<string, any>[] = [item.area, item.hidden];
|
|
57
|
+
const layoutValues: Record<string, any>[] = [item.area, item.hidden, item.state.hover];
|
|
56
58
|
const isInitialRef = useRef(true);
|
|
57
59
|
layoutValues.push(item.sticky);
|
|
58
60
|
layoutValues.push(sectionHeight);
|
|
@@ -100,12 +102,11 @@ export const Item: FC<ItemProps<TArticleItemAny>> = ({ item, sectionId}) => {
|
|
|
100
102
|
<ItemComponent item={item} sectionId={sectionId} onResize={handleItemResize} />
|
|
101
103
|
</div>
|
|
102
104
|
<JSXStyle id={id}>{`
|
|
103
|
-
${getLayoutStyles(layouts, layoutValues, ([area, hidden, sticky, sectionHeight, layoutParams]) => {
|
|
105
|
+
${getLayoutStyles(layouts, layoutValues, ([area, hidden, hoverParams, sticky, sectionHeight, layoutParams]) => {
|
|
104
106
|
const sizingAxis = parseSizing(layoutParams.sizing);
|
|
105
|
-
|
|
106
107
|
return (`
|
|
107
108
|
.item-${item.id} {
|
|
108
|
-
position: ${sticky ? 'sticky' : 'absolute'};
|
|
109
|
+
position: ${sticky ? 'sticky' : 'absolute'};
|
|
109
110
|
width: ${sizingAxis.x === SizingType.Manual ? `${area.width * 100}vw` : 'max-content'};
|
|
110
111
|
height: ${sizingAxis.y === SizingType.Manual ? `w${area.height * 100}vw` : 'unset'};
|
|
111
112
|
transform: scale(${scale});
|
|
@@ -113,7 +114,9 @@ export const Item: FC<ItemProps<TArticleItemAny>> = ({ item, sectionId}) => {
|
|
|
113
114
|
transform-origin: ${ScaleAnchorMap[scaleAnchor]};
|
|
114
115
|
pointer-events: auto;
|
|
115
116
|
--webkit-backface-visibility: hidden;
|
|
117
|
+
cursor: ${hoverParams ? 'pointer' : 'default'};
|
|
116
118
|
visibility: ${hidden ? 'hidden' : 'visible'};
|
|
119
|
+
transition: ${getTransitions(['width', 'height', 'scale'], hoverParams)};
|
|
117
120
|
}
|
|
118
121
|
.item-wrapper-${item.id} {
|
|
119
122
|
position: absolute;
|
|
@@ -124,6 +127,13 @@ export const Item: FC<ItemProps<TArticleItemAny>> = ({ item, sectionId}) => {
|
|
|
124
127
|
top: ${getItemTopStyle(area.top, area.anchorSide)};
|
|
125
128
|
left: ${area.left * 100}vw;
|
|
126
129
|
height: ${sticky ? `${getStickyItemWrapperHeight(sticky, area.height) * 100}vw` : 'unset'};
|
|
130
|
+
transition: ${getTransitions(['left', 'top'], hoverParams)};
|
|
131
|
+
}
|
|
132
|
+
.item-${item.id}:hover {
|
|
133
|
+
${getHoverStyles(['width', 'height', 'scale'], hoverParams)}
|
|
134
|
+
}
|
|
135
|
+
.item-wrapper-${item.id}:hover {
|
|
136
|
+
${getHoverStyles(['left', 'top'], hoverParams, area.anchorSide)}
|
|
127
137
|
}
|
|
128
138
|
`);
|
|
129
139
|
})}
|
|
@@ -49,7 +49,7 @@ export const Section: FC<Props> = ({ section, data, children }) => {
|
|
|
49
49
|
className={`section-${section.id}`}
|
|
50
50
|
id={section.name}
|
|
51
51
|
style={{
|
|
52
|
-
backgroundColor: backgroundColor
|
|
52
|
+
backgroundColor: backgroundColor.toCss()
|
|
53
53
|
}}
|
|
54
54
|
ref={sectionRef}
|
|
55
55
|
>
|
|
@@ -65,6 +65,11 @@ export const Section: FC<Props> = ({ section, data, children }) => {
|
|
|
65
65
|
))
|
|
66
66
|
}
|
|
67
67
|
${getSectionVisibilityStyles()}
|
|
68
|
+
@supports not (color: oklch(42% 0.3 90 / 1)) {
|
|
69
|
+
.section-${section.id} {
|
|
70
|
+
background-color: ${backgroundColor.fmt('rgba')};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
68
73
|
`}</JSXStyle>
|
|
69
74
|
</>
|
|
70
75
|
);
|
|
@@ -1,11 +1,29 @@
|
|
|
1
|
-
import { TCustomItem } from '@cntrl-site/sdk';
|
|
1
|
+
import { ArticleItemType, getLayoutStyles, TCustomItem } from '@cntrl-site/sdk';
|
|
2
2
|
import { FC } from 'react';
|
|
3
3
|
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
4
4
|
import { ItemProps } from '../Item';
|
|
5
|
+
import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
|
|
6
|
+
import JSXStyle from 'styled-jsx/style';
|
|
5
7
|
|
|
6
8
|
export const CustomItem: FC<ItemProps<TCustomItem>> = ({ item }) => {
|
|
7
9
|
const sdk = useCntrlContext();
|
|
10
|
+
const { layouts } = useCntrlContext();
|
|
8
11
|
const component = sdk.customItems.get(item.commonParams.name);
|
|
9
12
|
if (!component) return null;
|
|
10
|
-
return
|
|
13
|
+
return (
|
|
14
|
+
<>
|
|
15
|
+
<div className={`custom-component-${item.id}`}>{component({})}</div>
|
|
16
|
+
<JSXStyle id={item.id}>
|
|
17
|
+
{`${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
|
|
18
|
+
return (`
|
|
19
|
+
.custom-component-${item.id} {
|
|
20
|
+
transition: ${getTransitions<ArticleItemType.Custom>(['angle'], hoverParams)};
|
|
21
|
+
}
|
|
22
|
+
.custom-component-${item.id}:hover {
|
|
23
|
+
${getHoverStyles<ArticleItemType.Custom>(['angle'], hoverParams)}
|
|
24
|
+
}
|
|
25
|
+
`);
|
|
26
|
+
})}`}
|
|
27
|
+
</JSXStyle>
|
|
28
|
+
</>);
|
|
11
29
|
};
|
|
@@ -1,31 +1,40 @@
|
|
|
1
1
|
import { FC, useId, useMemo } from 'react';
|
|
2
2
|
import JSXStyle from 'styled-jsx/style';
|
|
3
|
-
import { CntrlColor, TImageItem } from '@cntrl-site/sdk';
|
|
3
|
+
import { ArticleItemType, CntrlColor, getLayoutStyles, TImageItem } from '@cntrl-site/sdk';
|
|
4
4
|
import { ItemProps } from '../Item';
|
|
5
5
|
import { LinkWrapper } from '../LinkWrapper';
|
|
6
6
|
import { useFileItem } from './useFileItem';
|
|
7
7
|
import { useItemAngle } from '../useItemAngle';
|
|
8
|
+
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
9
|
+
import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
|
|
8
10
|
|
|
9
11
|
export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId }) => {
|
|
10
12
|
const id = useId();
|
|
13
|
+
const { layouts } = useCntrlContext();
|
|
11
14
|
const { radius, strokeWidth, opacity, strokeColor } = useFileItem(item, sectionId);
|
|
12
15
|
const angle = useItemAngle(item, sectionId);
|
|
13
|
-
const borderColor = useMemo(() => CntrlColor.parse(strokeColor)
|
|
16
|
+
const borderColor = useMemo(() => CntrlColor.parse(strokeColor), [strokeColor]);
|
|
14
17
|
return (
|
|
15
18
|
<LinkWrapper url={item.link?.url}>
|
|
16
19
|
<>
|
|
17
|
-
<div
|
|
20
|
+
<div
|
|
21
|
+
className={`image-wrapper-${item.id}`}
|
|
18
22
|
style={{
|
|
19
23
|
borderRadius: `${radius * 100}vw`,
|
|
20
24
|
borderWidth: `${strokeWidth * 100}vw`,
|
|
21
25
|
opacity: `${opacity}`,
|
|
22
|
-
borderColor: `${borderColor}`,
|
|
26
|
+
borderColor: `${borderColor.toCss()}`,
|
|
23
27
|
transform: `rotate(${angle}deg)`
|
|
24
28
|
}}
|
|
25
29
|
>
|
|
26
30
|
<img className="image" src={item.commonParams.url} />
|
|
27
31
|
</div>
|
|
28
32
|
<JSXStyle id={id}>{`
|
|
33
|
+
@supports not (color: oklch(42% 0.3 90 / 1)) {
|
|
34
|
+
.image-wrapper-${item.id} {
|
|
35
|
+
border-color: ${borderColor.fmt('rgba')};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
29
38
|
.image-wrapper-${item.id} {
|
|
30
39
|
position: absolute;
|
|
31
40
|
overflow: hidden;
|
|
@@ -42,6 +51,16 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId }) => {
|
|
|
42
51
|
object-fit: cover;
|
|
43
52
|
pointer-events: none;
|
|
44
53
|
}
|
|
54
|
+
${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
|
|
55
|
+
return (`
|
|
56
|
+
.image-wrapper-${item.id} {
|
|
57
|
+
transition: ${getTransitions<ArticleItemType.Image>(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity'], hoverParams)};
|
|
58
|
+
}
|
|
59
|
+
.image-wrapper-${item.id}:hover {
|
|
60
|
+
${getHoverStyles<ArticleItemType.Image>(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity'], hoverParams)}
|
|
61
|
+
}
|
|
62
|
+
`);
|
|
63
|
+
})}
|
|
45
64
|
`}</JSXStyle>
|
|
46
65
|
</>
|
|
47
66
|
</LinkWrapper>
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { FC, useId, useMemo } from 'react';
|
|
2
2
|
import JSXStyle from 'styled-jsx/style';
|
|
3
|
-
import { TRectangleItem, CntrlColor } from '@cntrl-site/sdk';
|
|
3
|
+
import { TRectangleItem, CntrlColor, getLayoutStyles, ArticleItemType } from '@cntrl-site/sdk';
|
|
4
4
|
import { ItemProps } from '../Item';
|
|
5
5
|
import { LinkWrapper } from '../LinkWrapper';
|
|
6
6
|
import { useRectangleItem } from './useRectangleItem';
|
|
7
7
|
import { useItemAngle } from '../useItemAngle';
|
|
8
|
+
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
9
|
+
import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
|
|
8
10
|
|
|
9
11
|
export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId }) => {
|
|
10
12
|
const id = useId();
|
|
13
|
+
const { layouts } = useCntrlContext();
|
|
11
14
|
const { fillColor, radius, strokeWidth, strokeColor } = useRectangleItem(item, sectionId);
|
|
12
15
|
const angle = useItemAngle(item, sectionId);
|
|
13
16
|
const backgroundColor = useMemo(() => CntrlColor.parse(fillColor), [fillColor]);
|
|
@@ -16,7 +19,7 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId }
|
|
|
16
19
|
return (
|
|
17
20
|
<LinkWrapper url={item.link?.url}>
|
|
18
21
|
<>
|
|
19
|
-
<div
|
|
22
|
+
<div className={`rectangle-${item.id}`}
|
|
20
23
|
style={{
|
|
21
24
|
backgroundColor: `${backgroundColor.toCss()}`,
|
|
22
25
|
borderRadius: `${radius * 100}vw`,
|
|
@@ -26,12 +29,6 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId }
|
|
|
26
29
|
}}
|
|
27
30
|
/>
|
|
28
31
|
<JSXStyle id={id}>{`
|
|
29
|
-
@supports (color: oklch(42% 0.3 90 / 1)) {
|
|
30
|
-
.rectangle-${item.id} {
|
|
31
|
-
background-color: ${backgroundColor.fmt('oklch')};
|
|
32
|
-
border-color: ${borderColor.fmt('oklch')};
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
32
|
@supports not (color: oklch(42% 0.3 90 / 1)) {
|
|
36
33
|
.rectangle-${item.id} {
|
|
37
34
|
background-color: ${backgroundColor.fmt('rgba')};
|
|
@@ -45,6 +42,16 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId }
|
|
|
45
42
|
border-style: solid;
|
|
46
43
|
box-sizing: border-box;
|
|
47
44
|
}
|
|
45
|
+
${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
|
|
46
|
+
return (`
|
|
47
|
+
.rectangle-${item.id} {
|
|
48
|
+
transition: ${getTransitions<ArticleItemType.Rectangle>(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor'], hoverParams)};
|
|
49
|
+
}
|
|
50
|
+
.rectangle-${item.id}:hover {
|
|
51
|
+
${getHoverStyles<ArticleItemType.Rectangle>(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor'], hoverParams)}
|
|
52
|
+
}
|
|
53
|
+
`);
|
|
54
|
+
})}
|
|
48
55
|
`}</JSXStyle>
|
|
49
56
|
</>
|
|
50
57
|
</LinkWrapper>
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import { FC, useEffect, useState } from 'react';
|
|
2
|
-
import { TRichTextItem } from '@cntrl-site/sdk';
|
|
1
|
+
import { FC, useEffect, useId, useState } from 'react';
|
|
2
|
+
import { ArticleItemType, getLayoutStyles, TRichTextItem } from '@cntrl-site/sdk';
|
|
3
3
|
import JSXStyle from 'styled-jsx/style';
|
|
4
4
|
import { ItemProps } from '../Item';
|
|
5
5
|
import { useRichTextItem } from './useRichTextItem';
|
|
6
6
|
import { useItemAngle } from '../useItemAngle';
|
|
7
|
+
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
8
|
+
import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
|
|
7
9
|
|
|
8
10
|
export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, onResize }) => {
|
|
9
11
|
const [content, styles, preset] = useRichTextItem(item);
|
|
12
|
+
const id = useId();
|
|
10
13
|
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
11
14
|
const angle = useItemAngle(item, sectionId);
|
|
15
|
+
const { layouts } = useCntrlContext();
|
|
12
16
|
const className = preset ? `cntrl-preset-${preset.id}` : undefined;
|
|
13
17
|
|
|
14
18
|
useEffect(() => {
|
|
@@ -27,15 +31,25 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
|
|
|
27
31
|
<>
|
|
28
32
|
<div
|
|
29
33
|
ref={setRef}
|
|
30
|
-
className={className}
|
|
34
|
+
className={`${className} rich-text-wrapper-${item.id}`}
|
|
31
35
|
style={{
|
|
32
36
|
transform: `rotate(${angle}deg)`
|
|
33
37
|
}}
|
|
34
38
|
>
|
|
35
39
|
{content}
|
|
36
40
|
</div>
|
|
37
|
-
<JSXStyle id={
|
|
41
|
+
<JSXStyle id={id}>
|
|
38
42
|
{styles}
|
|
43
|
+
{`${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
|
|
44
|
+
return (`
|
|
45
|
+
.rich-text-wrapper-${item.id} {
|
|
46
|
+
transition: ${getTransitions<ArticleItemType.RichText>(['angle'], hoverParams)};
|
|
47
|
+
}
|
|
48
|
+
.rich-text-wrapper-${item.id}:hover {
|
|
49
|
+
${getHoverStyles<ArticleItemType.RichText>(['angle'], hoverParams)}
|
|
50
|
+
}
|
|
51
|
+
`);
|
|
52
|
+
})}`}
|
|
39
53
|
</JSXStyle>
|
|
40
54
|
</>
|
|
41
55
|
);
|
|
@@ -1,33 +1,42 @@
|
|
|
1
1
|
import { FC, useId, useMemo } from 'react';
|
|
2
2
|
import JSXStyle from 'styled-jsx/style';
|
|
3
|
-
import { CntrlColor, TVideoItem } from '@cntrl-site/sdk';
|
|
3
|
+
import { ArticleItemType, CntrlColor, getLayoutStyles, TVideoItem } from '@cntrl-site/sdk';
|
|
4
4
|
import { ItemProps } from '../Item';
|
|
5
5
|
import { LinkWrapper } from '../LinkWrapper';
|
|
6
6
|
import { useFileItem } from './useFileItem';
|
|
7
7
|
import { useItemAngle } from '../useItemAngle';
|
|
8
|
+
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
9
|
+
import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
|
|
8
10
|
|
|
9
11
|
export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId }) => {
|
|
10
12
|
const id = useId();
|
|
13
|
+
const { layouts } = useCntrlContext();
|
|
11
14
|
const { radius, strokeWidth, strokeColor, opacity } = useFileItem(item, sectionId);
|
|
12
15
|
const angle = useItemAngle(item, sectionId);
|
|
13
|
-
const borderColor = useMemo(() => CntrlColor.parse(strokeColor)
|
|
16
|
+
const borderColor = useMemo(() => CntrlColor.parse(strokeColor), [strokeColor]);
|
|
14
17
|
return (
|
|
15
18
|
<LinkWrapper url={item.link?.url}>
|
|
16
|
-
<div
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
<div
|
|
20
|
+
className={`video-wrapper-${item.id}`}
|
|
21
|
+
style={{
|
|
22
|
+
borderRadius: `${radius * 100}vw`,
|
|
23
|
+
borderWidth: `${strokeWidth * 100}vw`,
|
|
24
|
+
opacity: `${opacity}`,
|
|
25
|
+
borderColor: `${borderColor.toCss()}`,
|
|
26
|
+
transform: `rotate(${angle}deg)`
|
|
27
|
+
}}
|
|
24
28
|
>
|
|
25
29
|
<video autoPlay muted loop playsInline className="video">
|
|
26
30
|
<source src={item.commonParams.url} />
|
|
27
31
|
</video>
|
|
28
32
|
</div>
|
|
29
33
|
<JSXStyle id={id}>{`
|
|
30
|
-
|
|
34
|
+
@supports not (color: oklch(42% 0.3 90 / 1)) {
|
|
35
|
+
.video-wrapper-${item.id} {
|
|
36
|
+
border-color: ${borderColor.fmt('rgba')};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
.video-wrapper-${item.id} {
|
|
31
40
|
position: absolute;
|
|
32
41
|
overflow: hidden;
|
|
33
42
|
width: 100%;
|
|
@@ -44,6 +53,16 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId }) => {
|
|
|
44
53
|
object-fit: cover;
|
|
45
54
|
pointer-events: none;
|
|
46
55
|
}
|
|
56
|
+
${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
|
|
57
|
+
return (`
|
|
58
|
+
.video-wrapper-${item.id} {
|
|
59
|
+
transition: ${getTransitions<ArticleItemType.Video>(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity'], hoverParams)};
|
|
60
|
+
}
|
|
61
|
+
.video-wrapper-${item.id}:hover {
|
|
62
|
+
${getHoverStyles<ArticleItemType.Video>(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity'], hoverParams)}
|
|
63
|
+
}
|
|
64
|
+
`);
|
|
65
|
+
})}
|
|
47
66
|
`}</JSXStyle>
|
|
48
67
|
</LinkWrapper>
|
|
49
68
|
);
|
|
@@ -1,20 +1,34 @@
|
|
|
1
|
-
import { FC, useId } from 'react';
|
|
1
|
+
import { FC, useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import Player from '@vimeo/player';
|
|
2
3
|
import JSXStyle from 'styled-jsx/style';
|
|
3
4
|
import { TVimeoEmbedItem } from '@cntrl-site/core';
|
|
4
5
|
import { ItemProps } from '../Item';
|
|
5
6
|
import { LinkWrapper } from '../LinkWrapper';
|
|
6
7
|
import { useEmbedVideoItem } from './useEmbedVideoItem';
|
|
7
8
|
import { useItemAngle } from '../useItemAngle';
|
|
9
|
+
import { ArticleItemType, getLayoutStyles } from '@cntrl-site/sdk';
|
|
10
|
+
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
11
|
+
import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
|
|
12
|
+
import { useCurrentLayout } from '../../common/useCurrentLayout';
|
|
8
13
|
|
|
9
14
|
export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId }) => {
|
|
10
15
|
const id = useId();
|
|
16
|
+
const { layouts } = useCntrlContext();
|
|
17
|
+
const layout = useCurrentLayout()
|
|
11
18
|
const { radius } = useEmbedVideoItem(item, sectionId);
|
|
19
|
+
const [iframeRef, setIframeRef] = useState<HTMLIFrameElement | null>(null);
|
|
20
|
+
const vimeoPlayer = useMemo(() => iframeRef ? new Player(iframeRef) : undefined, [iframeRef]);
|
|
21
|
+
const isAutoPlayOnHover = useMemo(() => {
|
|
22
|
+
const layoutHoverStates = item.state.hover[layout];
|
|
23
|
+
if (!layoutHoverStates) return false;
|
|
24
|
+
return layoutHoverStates.autoplay === true;
|
|
25
|
+
}, [layout, item]);
|
|
12
26
|
const angle = useItemAngle(item, sectionId);
|
|
13
27
|
const { autoplay, controls, loop, muted, pictureInPicture, url } = item.commonParams;
|
|
14
28
|
const getValidVimeoUrl = (url: string): string => {
|
|
15
29
|
const validURL = new URL(url);
|
|
16
30
|
validURL.searchParams.append('controls', String(controls));
|
|
17
|
-
validURL.searchParams.append('autoplay',
|
|
31
|
+
validURL.searchParams.append('autoplay', String(autoplay));
|
|
18
32
|
validURL.searchParams.append('muted', String(muted));
|
|
19
33
|
validURL.searchParams.append('loop', String(loop));
|
|
20
34
|
validURL.searchParams.append('pip', String(pictureInPicture));
|
|
@@ -29,33 +43,52 @@ export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId
|
|
|
29
43
|
|
|
30
44
|
return (
|
|
31
45
|
<LinkWrapper url={item.link?.url}>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
46
|
+
<div className={`embed-video-wrapper-${item.id}`}
|
|
47
|
+
style={{
|
|
48
|
+
borderRadius: `${radius * 100}vw`,
|
|
49
|
+
transform: `rotate(${angle}deg)`
|
|
50
|
+
}}
|
|
51
|
+
onMouseEnter={() => {
|
|
52
|
+
if (!vimeoPlayer || !isAutoPlayOnHover) return;
|
|
53
|
+
vimeoPlayer.play();
|
|
54
|
+
}}
|
|
55
|
+
onMouseLeave={() =>{
|
|
56
|
+
if (!vimeoPlayer || !isAutoPlayOnHover) return;
|
|
57
|
+
vimeoPlayer.pause();
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
<iframe
|
|
61
|
+
ref={setIframeRef}
|
|
62
|
+
className="embedVideo"
|
|
63
|
+
src={validUrl || ''}
|
|
64
|
+
allow="autoplay; fullscreen; picture-in-picture;"
|
|
65
|
+
allowFullScreen
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
<JSXStyle id={id}>{`
|
|
69
|
+
.embed-video-wrapper-${item.id} {
|
|
70
|
+
position: absolute;
|
|
71
|
+
overflow: hidden;
|
|
72
|
+
width: 100%;
|
|
73
|
+
height: 100%;
|
|
74
|
+
}
|
|
75
|
+
.embedVideo {
|
|
76
|
+
width: 100%;
|
|
77
|
+
height: 100%;
|
|
78
|
+
z-index: 1;
|
|
79
|
+
border: none;
|
|
80
|
+
}
|
|
81
|
+
${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
|
|
82
|
+
return (`
|
|
83
|
+
.embed-video-wrapper-${item.id} {
|
|
84
|
+
transition: ${getTransitions<ArticleItemType.VimeoEmbed>(['angle', 'radius'], hoverParams)};
|
|
85
|
+
}
|
|
86
|
+
.embed-video-wrapper-${item.id}:hover {
|
|
87
|
+
${getHoverStyles<ArticleItemType.VimeoEmbed>(['angle', 'radius'], hoverParams)}
|
|
88
|
+
}
|
|
89
|
+
`);
|
|
90
|
+
})}
|
|
91
|
+
`}</JSXStyle>
|
|
59
92
|
</LinkWrapper>
|
|
60
93
|
);
|
|
61
94
|
};
|
|
@@ -6,9 +6,13 @@ import { LinkWrapper } from '../LinkWrapper';
|
|
|
6
6
|
import { getYoutubeId } from '../../utils/getValidYoutubeUrl';
|
|
7
7
|
import { useEmbedVideoItem } from './useEmbedVideoItem';
|
|
8
8
|
import { useItemAngle } from '../useItemAngle';
|
|
9
|
+
import { ArticleItemType, getLayoutStyles } from '@cntrl-site/sdk';
|
|
10
|
+
import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
|
|
11
|
+
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
9
12
|
|
|
10
13
|
export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, sectionId }) => {
|
|
11
14
|
const id = useId();
|
|
15
|
+
const { layouts } = useCntrlContext();
|
|
12
16
|
const { autoplay, controls, url } = item.commonParams;
|
|
13
17
|
const { radius } = useEmbedVideoItem(item, sectionId);
|
|
14
18
|
const angle = useItemAngle(item, sectionId);
|
|
@@ -18,7 +22,7 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
|
|
|
18
22
|
const id = getYoutubeId(newUrl);
|
|
19
23
|
const validUrl = new URL(`https://www.youtube.com/embed/${id}`);
|
|
20
24
|
validUrl.searchParams.append('controls', `${ Number(controls) }`);
|
|
21
|
-
validUrl.searchParams.append('autoplay',
|
|
25
|
+
validUrl.searchParams.append('autoplay', `${ Number(autoplay) }`);
|
|
22
26
|
validUrl.searchParams.append('mute', `${ Number(autoplay) }`);
|
|
23
27
|
|
|
24
28
|
return validUrl.href;
|
|
@@ -53,6 +57,16 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
|
|
|
53
57
|
z-index: 1;
|
|
54
58
|
border: none;
|
|
55
59
|
}
|
|
60
|
+
${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
|
|
61
|
+
return (`
|
|
62
|
+
.embed-youtube-video-wrapper-${item.id} {
|
|
63
|
+
transition: ${getTransitions<ArticleItemType.YoutubeEmbed>(['angle', 'radius'], hoverParams)};
|
|
64
|
+
}
|
|
65
|
+
.embed-youtube-video-wrapper-${item.id}:hover {
|
|
66
|
+
${getHoverStyles<ArticleItemType.YoutubeEmbed>(['angle', 'radius'], hoverParams)}
|
|
67
|
+
}
|
|
68
|
+
`);
|
|
69
|
+
})}
|
|
56
70
|
`}</JSXStyle>
|
|
57
71
|
</LinkWrapper>
|
|
58
72
|
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AnchorSide, TArticleItemAny } from '@cntrl-site/sdk';
|
|
2
2
|
import { useKeyframeValue } from '../common/useKeyframeValue';
|
|
3
3
|
import { useCurrentLayout } from '../common/useCurrentLayout';
|
|
4
|
+
import { getItemTopStyle } from '../utils/getItemTopStyle';
|
|
4
5
|
|
|
5
6
|
export const useItemPosition = (item: TArticleItemAny, sectionId: string) => {
|
|
6
7
|
const layoutId = useCurrentLayout();
|
|
@@ -17,17 +18,4 @@ export const useItemPosition = (item: TArticleItemAny, sectionId: string) => {
|
|
|
17
18
|
};
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
export function getItemTopStyle(top: number, anchorSide?: AnchorSide) {
|
|
21
|
-
const defaultValue = `${top * 100}vw`;
|
|
22
|
-
if (!anchorSide) return defaultValue;
|
|
23
|
-
switch (anchorSide) {
|
|
24
|
-
case AnchorSide.Top:
|
|
25
|
-
return defaultValue;
|
|
26
|
-
case AnchorSide.Center:
|
|
27
|
-
return `calc(50% + ${top * 100}vw)`;
|
|
28
|
-
case AnchorSide.Bottom:
|
|
29
|
-
return `calc(100% + ${top * 100}vw)`;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
21
|
|
|
@@ -3,12 +3,16 @@ import { useMemo } from 'react';
|
|
|
3
3
|
import { CntrlColor } from '@cntrl-site/sdk';
|
|
4
4
|
|
|
5
5
|
type LayoutIdentifier = string;
|
|
6
|
-
const DEFAULT_COLOR = '
|
|
6
|
+
const DEFAULT_COLOR = 'rgba(0, 0, 0, 0)';
|
|
7
7
|
|
|
8
|
-
export const useSectionColor = (colorData: Record<LayoutIdentifier, string | null>):
|
|
8
|
+
export const useSectionColor = (colorData: Record<LayoutIdentifier, string | null>): CntrlColor => {
|
|
9
9
|
const layoutId = useCurrentLayout();
|
|
10
10
|
return useMemo(() => {
|
|
11
11
|
const layoutColor = colorData[layoutId];
|
|
12
|
-
return
|
|
12
|
+
return CntrlColor.parse(
|
|
13
|
+
!layoutColor
|
|
14
|
+
? DEFAULT_COLOR
|
|
15
|
+
: layoutColor
|
|
16
|
+
);
|
|
13
17
|
}, []);
|
|
14
18
|
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { THoverParams, ArticleItemType, CntrlColor, AnchorSide, TItemHoverState } from '@cntrl-site/sdk';
|
|
2
|
+
import { getItemTopStyle } from '../getItemTopStyle';
|
|
3
|
+
|
|
4
|
+
type UnionToIntersection<U> = (U extends any ? (arg: U) => void : never) extends (arg: infer I) => void ? I : never;
|
|
5
|
+
type HoverParamsGetter = (value: any, anchorSide?: AnchorSide) => string;
|
|
6
|
+
type ItemHoverParams = Omit<UnionToIntersection<TItemHoverState>, 'autoplay'>;
|
|
7
|
+
|
|
8
|
+
const hoverTransformationMap: Record<keyof ItemHoverParams, HoverParamsGetter> = {
|
|
9
|
+
'width': (width: number) => `width: ${width * 100}vw !important;`,
|
|
10
|
+
'height': (height: number) => `height: ${height * 100}vw !important;`,
|
|
11
|
+
'top': (top: number, anchorSide?: AnchorSide) => `top: ${getItemTopStyle(top, anchorSide)} !important;`,
|
|
12
|
+
'left': (left: number) => `left: ${left * 100}vw !important;`,
|
|
13
|
+
'scale': (scale: number) => `transform: scale(${scale}) !important;`,
|
|
14
|
+
'angle': (angle: number) => `transform: rotate(${angle}deg) !important;`,
|
|
15
|
+
'opacity': (opacity: number) => `opacity: ${opacity} !important;`,
|
|
16
|
+
'radius': (radius: number) => `border-radius: ${radius * 100}vw !important;`,
|
|
17
|
+
'strokeWidth': (strokeWidth: number) => `border-width: ${strokeWidth * 100}vw !important;`,
|
|
18
|
+
'strokeColor': (strokeColor: string) => `border-color: ${CntrlColor.parse(strokeColor).toCss()} !important;`,
|
|
19
|
+
'fillColor': (fillColor: string) => `background-color: ${CntrlColor.parse(fillColor).toCss()} !important;`,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const CSSPropertyNameMap: Record<keyof ItemHoverParams, string> = {
|
|
23
|
+
'width': 'width',
|
|
24
|
+
'height': 'height',
|
|
25
|
+
'top': 'top',
|
|
26
|
+
'left': 'left',
|
|
27
|
+
'scale': 'transform',
|
|
28
|
+
'angle': 'transform',
|
|
29
|
+
'opacity': 'opacity',
|
|
30
|
+
'radius': 'border-radius',
|
|
31
|
+
'strokeWidth': 'border-width',
|
|
32
|
+
'strokeColor': 'border-color',
|
|
33
|
+
'fillColor': 'background-color'
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function getTransitions<T extends ArticleItemType>(
|
|
37
|
+
values: Array<keyof ItemHoverParams>,
|
|
38
|
+
hover?: ItemHoverParams
|
|
39
|
+
): string {
|
|
40
|
+
if (!hover) return 'unset';
|
|
41
|
+
const transitionValues = values.reduce<string[]>((acc, valueName) => {
|
|
42
|
+
if (valueName in hover && hover[valueName] !== undefined) {
|
|
43
|
+
const hoverProperties = hover[valueName] as THoverParams<string | number>;
|
|
44
|
+
return [
|
|
45
|
+
...acc,
|
|
46
|
+
`${CSSPropertyNameMap[valueName]} ${hoverProperties!.duration}ms ${hoverProperties!.timing} ${hoverProperties!.delay}ms`
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
return acc;
|
|
50
|
+
}, []);
|
|
51
|
+
if (!transitionValues.length) return 'unset';
|
|
52
|
+
return transitionValues.join(', ');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function getHoverStyles<T extends ArticleItemType>(
|
|
56
|
+
values: Array<keyof ItemHoverParams>,
|
|
57
|
+
hover?: ItemHoverParams,
|
|
58
|
+
anchorSide?: AnchorSide
|
|
59
|
+
): string {
|
|
60
|
+
if (!hover) return '';
|
|
61
|
+
const hoverValues = values.reduce<string[]>((acc, valueName) => {
|
|
62
|
+
if (valueName in hover && hover[valueName] !== undefined) {
|
|
63
|
+
const hoverProperties = hover[valueName] as THoverParams<string | number>;
|
|
64
|
+
return [
|
|
65
|
+
...acc,
|
|
66
|
+
hoverTransformationMap[valueName](hoverProperties.value, anchorSide)
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
return acc;
|
|
70
|
+
}, []);
|
|
71
|
+
if (!hoverValues.length) return '';
|
|
72
|
+
return hoverValues.join('\n');
|
|
73
|
+
}
|
|
74
|
+
|
|
@@ -134,6 +134,7 @@ export class RichTextConverter {
|
|
|
134
134
|
if (!entitiesGroup.stylesGroup) continue;
|
|
135
135
|
for (const styleGroup of entitiesGroup.stylesGroup) {
|
|
136
136
|
const lineHeight = styleGroup.styles.find(s => s.name === 'LINEHEIGHT');
|
|
137
|
+
const color = styleGroup.styles.find(s => s.name === 'COLOR');
|
|
137
138
|
if (lineHeight?.value) {
|
|
138
139
|
currentLineHeight[item.layout] = lineHeight.value;
|
|
139
140
|
}
|
|
@@ -142,6 +143,15 @@ export class RichTextConverter {
|
|
|
142
143
|
${styleGroup.styles.map(s => RichTextConverter.fromDraftToInline(s)).join('\n')}
|
|
143
144
|
}
|
|
144
145
|
`);
|
|
146
|
+
if (color) {
|
|
147
|
+
styleRules[item.layout].push(`
|
|
148
|
+
@supports not (color: oklch(42% 0.3 90 / 1)) {
|
|
149
|
+
.${blockClass} .s-${styleGroup.start}-${styleGroup.end} {
|
|
150
|
+
color: ${CntrlColor.parse(getResolvedValue(color.value, 'COLOR')!).fmt('rgba')};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
`);
|
|
154
|
+
}
|
|
145
155
|
}
|
|
146
156
|
}
|
|
147
157
|
}
|