rankrunners-cms 0.0.1
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/CLAUDE.md +106 -0
- package/README.md +15 -0
- package/package.json +39 -0
- package/src/CaptchaBadge.tsx +72 -0
- package/src/editor/blocks/Blank/index.tsx +15 -0
- package/src/editor/blocks/Blank/styles.module.css +4 -0
- package/src/editor/blocks/Button/index.tsx +46 -0
- package/src/editor/blocks/Card/index.tsx +67 -0
- package/src/editor/blocks/Card/styles.module.css +54 -0
- package/src/editor/blocks/Container/index.tsx +36 -0
- package/src/editor/blocks/Flex/index.tsx +82 -0
- package/src/editor/blocks/Flex/styles.module.css +9 -0
- package/src/editor/blocks/Grid/index.tsx +53 -0
- package/src/editor/blocks/Grid/styles.module.css +11 -0
- package/src/editor/blocks/Heading/index.tsx +69 -0
- package/src/editor/blocks/Hero/Hero.tsx +107 -0
- package/src/editor/blocks/Hero/client.tsx +204 -0
- package/src/editor/blocks/Hero/index.tsx +2 -0
- package/src/editor/blocks/Hero/quotes.ts +46 -0
- package/src/editor/blocks/Hero/server.tsx +7 -0
- package/src/editor/blocks/Hero/styles.module.css +116 -0
- package/src/editor/blocks/Logos/index.tsx +77 -0
- package/src/editor/blocks/Logos/styles.module.css +13 -0
- package/src/editor/blocks/Paragraph/index.tsx +95 -0
- package/src/editor/blocks/Paragraph/styles.module.css +4 -0
- package/src/editor/blocks/Space/index.tsx +45 -0
- package/src/editor/blocks/Space/styles.module.css +14 -0
- package/src/editor/blocks/Stats/index.tsx +58 -0
- package/src/editor/blocks/Stats/styles.module.css +64 -0
- package/src/editor/blocks/Text/index.tsx +75 -0
- package/src/editor/blocks/Text/styles.module.css +4 -0
- package/src/editor/components/Layout/index.tsx +160 -0
- package/src/editor/components/Layout/styles.module.css +3 -0
- package/src/editor/components/Section/index.tsx +31 -0
- package/src/editor/components/Section/styles.module.css +23 -0
- package/src/editor/index.tsx +63 -0
- package/src/editor/options.ts +37 -0
- package/src/editor/types.ts +60 -0
- package/src/editor/utils/generateId.ts +2 -0
- package/src/editor/utils/getClassNameFactory.ts +63 -0
- package/src/index.css +1 -0
- package/src/index.ts +6 -0
- package/src/libs/redirect.ts +10 -0
- package/src/sitemap/handleRobotsTxt.ts +19 -0
- package/src/sitemap/handleSitemap.ts +25 -0
- package/src/sitemap/index.ts +2 -0
- package/src/styles.d.ts +4 -0
- package/tsconfig.json +42 -0
- package/tsdown.config.ts +15 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Button } from '@measured/puck'
|
|
2
|
+
import { Section } from '../../components/Section'
|
|
3
|
+
import styles from './styles.module.css'
|
|
4
|
+
import type { PuckComponent, Slot } from '@measured/puck'
|
|
5
|
+
import type { ReactNode } from 'react'
|
|
6
|
+
import getClassNameFactory from '../../utils/getClassNameFactory'
|
|
7
|
+
|
|
8
|
+
const getClassName = getClassNameFactory('Hero', styles)
|
|
9
|
+
|
|
10
|
+
export type HeroProps = {
|
|
11
|
+
quote?: { index: number; label: string }
|
|
12
|
+
title: string | ReactNode
|
|
13
|
+
description: string
|
|
14
|
+
align?: string
|
|
15
|
+
padding: string
|
|
16
|
+
image?: {
|
|
17
|
+
content?: Slot
|
|
18
|
+
mode?: 'inline' | 'background' | 'custom'
|
|
19
|
+
url?: string
|
|
20
|
+
}
|
|
21
|
+
buttons: Array<{
|
|
22
|
+
label: string
|
|
23
|
+
href: string
|
|
24
|
+
variant?: 'primary' | 'secondary'
|
|
25
|
+
}>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const Hero: PuckComponent<HeroProps> = ({
|
|
29
|
+
align,
|
|
30
|
+
title,
|
|
31
|
+
description,
|
|
32
|
+
buttons,
|
|
33
|
+
padding,
|
|
34
|
+
image,
|
|
35
|
+
puck,
|
|
36
|
+
}) => {
|
|
37
|
+
return (
|
|
38
|
+
<Section
|
|
39
|
+
className={getClassName({
|
|
40
|
+
left: align === 'left',
|
|
41
|
+
center: align === 'center',
|
|
42
|
+
hasImageBackground: image?.mode === 'background',
|
|
43
|
+
})}
|
|
44
|
+
style={{ paddingTop: padding, paddingBottom: padding }}
|
|
45
|
+
>
|
|
46
|
+
{image?.mode === 'background' && (
|
|
47
|
+
<>
|
|
48
|
+
<div
|
|
49
|
+
className={getClassName('image')}
|
|
50
|
+
style={{
|
|
51
|
+
backgroundImage: `url("${image.url}")`,
|
|
52
|
+
}}
|
|
53
|
+
></div>
|
|
54
|
+
|
|
55
|
+
<div className={getClassName('imageOverlay')}></div>
|
|
56
|
+
</>
|
|
57
|
+
)}
|
|
58
|
+
|
|
59
|
+
<div className={getClassName('inner')}>
|
|
60
|
+
<div className={getClassName('content')}>
|
|
61
|
+
<h1>{title}</h1>
|
|
62
|
+
<p className={getClassName('subtitle')}>{description}</p>
|
|
63
|
+
<div className={getClassName('actions')}>
|
|
64
|
+
{buttons.map((button, i) => (
|
|
65
|
+
<Button
|
|
66
|
+
key={i}
|
|
67
|
+
href={button.href}
|
|
68
|
+
variant={button.variant}
|
|
69
|
+
size="large"
|
|
70
|
+
tabIndex={puck.isEditing ? -1 : undefined}
|
|
71
|
+
>
|
|
72
|
+
{button.label}
|
|
73
|
+
</Button>
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
{align !== 'center' && image?.mode === 'inline' && image.url && (
|
|
79
|
+
<div
|
|
80
|
+
style={{
|
|
81
|
+
backgroundImage: `url('${image.url}')`,
|
|
82
|
+
backgroundSize: 'cover',
|
|
83
|
+
backgroundRepeat: 'no-repeat',
|
|
84
|
+
backgroundPosition: 'center',
|
|
85
|
+
borderRadius: 24,
|
|
86
|
+
height: 356,
|
|
87
|
+
marginLeft: 'auto',
|
|
88
|
+
width: '100%',
|
|
89
|
+
}}
|
|
90
|
+
/>
|
|
91
|
+
)}
|
|
92
|
+
|
|
93
|
+
{align !== 'center' && image?.mode === 'custom' && image.content && (
|
|
94
|
+
<image.content
|
|
95
|
+
style={{
|
|
96
|
+
height: 356,
|
|
97
|
+
marginLeft: 'auto',
|
|
98
|
+
width: '100%',
|
|
99
|
+
}}
|
|
100
|
+
/>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
</Section>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export default Hero
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { Link2 } from 'lucide-react'
|
|
2
|
+
import { AutoField, FieldLabel } from '@measured/puck'
|
|
3
|
+
import { quotes } from './quotes'
|
|
4
|
+
import HeroComponent from './Hero'
|
|
5
|
+
import type { ComponentConfig } from '@measured/puck'
|
|
6
|
+
import type { HeroProps } from './Hero'
|
|
7
|
+
|
|
8
|
+
export const Hero: ComponentConfig<{
|
|
9
|
+
props: HeroProps
|
|
10
|
+
fields: {
|
|
11
|
+
userField: {
|
|
12
|
+
type: 'userField'
|
|
13
|
+
option: boolean
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}> = {
|
|
17
|
+
fields: {
|
|
18
|
+
quote: {
|
|
19
|
+
type: 'external',
|
|
20
|
+
placeholder: 'Select a quote',
|
|
21
|
+
showSearch: false,
|
|
22
|
+
renderFooter: ({ items }) => {
|
|
23
|
+
return (
|
|
24
|
+
<div>
|
|
25
|
+
{items.length} result{items.length === 1 ? '' : 's'}
|
|
26
|
+
</div>
|
|
27
|
+
)
|
|
28
|
+
},
|
|
29
|
+
filterFields: {
|
|
30
|
+
author: {
|
|
31
|
+
type: 'select',
|
|
32
|
+
options: [
|
|
33
|
+
{ value: '', label: 'Select an author' },
|
|
34
|
+
{ value: 'Mark Twain', label: 'Mark Twain' },
|
|
35
|
+
{ value: 'Henry Ford', label: 'Henry Ford' },
|
|
36
|
+
{ value: 'Kurt Vonnegut', label: 'Kurt Vonnegut' },
|
|
37
|
+
{ value: 'Andrew Carnegie', label: 'Andrew Carnegie' },
|
|
38
|
+
{ value: 'C. S. Lewis', label: 'C. S. Lewis' },
|
|
39
|
+
{ value: 'Confucius', label: 'Confucius' },
|
|
40
|
+
{ value: 'Eleanor Roosevelt', label: 'Eleanor Roosevelt' },
|
|
41
|
+
{ value: 'Samuel Ullman', label: 'Samuel Ullman' },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
fetchList: async ({ query, filters }) => {
|
|
46
|
+
// Simulate delay
|
|
47
|
+
await new Promise((res) => setTimeout(res, 500))
|
|
48
|
+
|
|
49
|
+
return quotes
|
|
50
|
+
.map((quote, idx) => ({
|
|
51
|
+
index: idx,
|
|
52
|
+
title: quote.author,
|
|
53
|
+
description: quote.content,
|
|
54
|
+
}))
|
|
55
|
+
.filter((item) => {
|
|
56
|
+
if (filters.author && item.title !== filters.author) {
|
|
57
|
+
return false
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!query) return true
|
|
61
|
+
|
|
62
|
+
const queryLowercase = query.toLowerCase()
|
|
63
|
+
|
|
64
|
+
if (item.title.toLowerCase().indexOf(queryLowercase) > -1) {
|
|
65
|
+
return true
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (item.description.toLowerCase().indexOf(queryLowercase) > -1) {
|
|
69
|
+
return true
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
},
|
|
73
|
+
mapRow: (item) => ({
|
|
74
|
+
title: item.title,
|
|
75
|
+
description: <span>{item.description}</span>,
|
|
76
|
+
}),
|
|
77
|
+
mapProp: (result) => {
|
|
78
|
+
return { index: result.index, label: result.description }
|
|
79
|
+
},
|
|
80
|
+
getItemSummary: (item) => item.label,
|
|
81
|
+
},
|
|
82
|
+
title: { type: 'text', contentEditable: true },
|
|
83
|
+
description: { type: 'textarea', contentEditable: true },
|
|
84
|
+
buttons: {
|
|
85
|
+
type: 'array',
|
|
86
|
+
min: 1,
|
|
87
|
+
max: 4,
|
|
88
|
+
getItemSummary: (item) => item.label || 'Button',
|
|
89
|
+
arrayFields: {
|
|
90
|
+
label: { type: 'text', contentEditable: true },
|
|
91
|
+
href: { type: 'text' },
|
|
92
|
+
variant: {
|
|
93
|
+
type: 'select',
|
|
94
|
+
options: [
|
|
95
|
+
{ label: 'primary', value: 'primary' },
|
|
96
|
+
{ label: 'secondary', value: 'secondary' },
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
defaultItemProps: {
|
|
101
|
+
label: 'Button',
|
|
102
|
+
href: '#',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
align: {
|
|
106
|
+
type: 'radio',
|
|
107
|
+
options: [
|
|
108
|
+
{ label: 'left', value: 'left' },
|
|
109
|
+
{ label: 'center', value: 'center' },
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
image: {
|
|
113
|
+
type: 'object',
|
|
114
|
+
objectFields: {
|
|
115
|
+
content: { type: 'slot' },
|
|
116
|
+
url: {
|
|
117
|
+
type: 'custom',
|
|
118
|
+
render: ({ value, field, name, onChange, readOnly }) => (
|
|
119
|
+
<FieldLabel
|
|
120
|
+
label={field.label || name}
|
|
121
|
+
readOnly={readOnly}
|
|
122
|
+
icon={<Link2 size="16" />}
|
|
123
|
+
>
|
|
124
|
+
<AutoField
|
|
125
|
+
field={{ type: 'text' }}
|
|
126
|
+
value={value}
|
|
127
|
+
onChange={onChange}
|
|
128
|
+
readOnly={readOnly}
|
|
129
|
+
/>
|
|
130
|
+
</FieldLabel>
|
|
131
|
+
),
|
|
132
|
+
},
|
|
133
|
+
mode: {
|
|
134
|
+
type: 'radio',
|
|
135
|
+
options: [
|
|
136
|
+
{ label: 'inline', value: 'inline' },
|
|
137
|
+
{ label: 'bg', value: 'background' },
|
|
138
|
+
{ label: 'custom', value: 'custom' },
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
padding: { type: 'userField', option: true },
|
|
144
|
+
},
|
|
145
|
+
defaultProps: {
|
|
146
|
+
title: 'Hero',
|
|
147
|
+
align: 'left',
|
|
148
|
+
description: 'Description',
|
|
149
|
+
buttons: [{ label: 'Learn more', href: '#' }],
|
|
150
|
+
padding: '64px',
|
|
151
|
+
},
|
|
152
|
+
/**
|
|
153
|
+
* The resolveData method allows us to modify component data after being
|
|
154
|
+
* set by the user.
|
|
155
|
+
*
|
|
156
|
+
* It is called after the page data is changed, but before a component
|
|
157
|
+
* is rendered. This allows us to make dynamic changes to the props
|
|
158
|
+
* without storing the data in Puck.
|
|
159
|
+
*
|
|
160
|
+
* For example, requesting a third-party API for the latest content.
|
|
161
|
+
*/
|
|
162
|
+
resolveData: async ({ props }, { changed }) => {
|
|
163
|
+
if (!props.quote)
|
|
164
|
+
return { props, readOnly: { title: false, description: false } }
|
|
165
|
+
|
|
166
|
+
if (!changed.quote) {
|
|
167
|
+
return { props }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Simulate a delay
|
|
171
|
+
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
props: {
|
|
175
|
+
title: quotes[props.quote.index]?.author,
|
|
176
|
+
description: quotes[props.quote.index]?.content,
|
|
177
|
+
},
|
|
178
|
+
readOnly: { title: true, description: true },
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
resolveFields: (data, { fields }) => {
|
|
182
|
+
if (data.props.align === 'center') {
|
|
183
|
+
return {
|
|
184
|
+
...fields,
|
|
185
|
+
image: undefined,
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return fields
|
|
190
|
+
},
|
|
191
|
+
resolvePermissions: async (data, params) => {
|
|
192
|
+
if (!params.changed.quote) return params.lastPermissions
|
|
193
|
+
|
|
194
|
+
// Simulate delay
|
|
195
|
+
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
...params.permissions,
|
|
199
|
+
// Disable delete if quote 7 is selected
|
|
200
|
+
delete: data.props.quote?.index !== 7,
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
render: HeroComponent,
|
|
204
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const quotes = [
|
|
2
|
+
{
|
|
3
|
+
content:
|
|
4
|
+
"Age is an issue of mind over matter. If you don't mind, it doesn't matter.",
|
|
5
|
+
author: "Mark Twain",
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
content:
|
|
9
|
+
"Anyone who stops learning is old, whether at twenty or eighty. Anyone who keeps learning stays young. The greatest thing in life is to keep your mind young.",
|
|
10
|
+
author: "Henry Ford",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
content: "Wrinkles should merely indicate where smiles have been.",
|
|
14
|
+
author: "Mark Twain",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
content:
|
|
18
|
+
"True terror is to wake up one morning and discover that your high school class is running the country.",
|
|
19
|
+
author: "Kurt Vonnegut",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
content:
|
|
23
|
+
"As I grow older, I pay less attention to what men say. I just watch what they do.",
|
|
24
|
+
author: "Andrew Carnegie",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
content:
|
|
28
|
+
"How incessant and great are the ills with which a prolonged old age is replete.",
|
|
29
|
+
author: "C. S. Lewis",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
content:
|
|
33
|
+
"Old age, believe me, is a good and pleasant thing. It is true you are gently shouldered off the stage, but then you are given such a comfortable front stall as spectator.",
|
|
34
|
+
author: "Confucius",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
content:
|
|
38
|
+
"Old age has deformities enough of its own. It should never add to them the deformity of vice.",
|
|
39
|
+
author: "Eleanor Roosevelt",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
content:
|
|
43
|
+
"Nobody grows old merely by living a number of years. We grow old by deserting our ideals. Years may wrinkle the skin, but to give up enthusiasm wrinkles the soul.",
|
|
44
|
+
author: "Samuel Ullman",
|
|
45
|
+
},
|
|
46
|
+
];
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
.Hero {
|
|
2
|
+
background-image: linear-gradient(
|
|
3
|
+
rgba(255, 255, 255, 0),
|
|
4
|
+
rgb(247, 250, 255) 100%
|
|
5
|
+
);
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: center;
|
|
8
|
+
position: relative;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.Hero-inner {
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
position: relative;
|
|
15
|
+
gap: 48px;
|
|
16
|
+
flex-wrap: wrap;
|
|
17
|
+
z-index: 1;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@media (min-width: 1024px) {
|
|
21
|
+
.Hero-inner {
|
|
22
|
+
flex-wrap: nowrap;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.Hero h1 {
|
|
27
|
+
line-height: 1.1;
|
|
28
|
+
font-size: 48px;
|
|
29
|
+
margin: 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@media (min-width: 768px) {
|
|
33
|
+
.Hero h1 {
|
|
34
|
+
font-size: 64px;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.Hero-subtitle {
|
|
39
|
+
color: var(--puck-color-grey-05);
|
|
40
|
+
font-size: var(--puck-font-size-m);
|
|
41
|
+
line-height: 1.5;
|
|
42
|
+
margin: 0;
|
|
43
|
+
margin-bottom: 8px;
|
|
44
|
+
font-weight: 300;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.Hero--hasImageBackground .Hero-subtitle {
|
|
48
|
+
color: var(--puck-color-grey-03);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.Hero-image {
|
|
52
|
+
background-repeat: no-repeat;
|
|
53
|
+
background-size: cover;
|
|
54
|
+
background-position: center;
|
|
55
|
+
position: absolute;
|
|
56
|
+
right: 0;
|
|
57
|
+
top: 0;
|
|
58
|
+
bottom: 0;
|
|
59
|
+
left: 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.Hero-imageOverlay {
|
|
63
|
+
background-image: linear-gradient(
|
|
64
|
+
-90deg,
|
|
65
|
+
rgb(247, 250, 255, 0.7) 0%,
|
|
66
|
+
rgb(247, 250, 255, 0.7) 80%
|
|
67
|
+
);
|
|
68
|
+
position: absolute;
|
|
69
|
+
right: 0;
|
|
70
|
+
top: 0;
|
|
71
|
+
bottom: 0;
|
|
72
|
+
left: 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@media (min-width: 768px) {
|
|
76
|
+
.Hero--left .Hero-imageOverlay {
|
|
77
|
+
background-image: linear-gradient(
|
|
78
|
+
-90deg,
|
|
79
|
+
rgba(255, 255, 255, 0) 0%,
|
|
80
|
+
rgb(247, 250, 255) 70%
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.Hero-bg img {
|
|
86
|
+
height: 100%;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.Hero-content {
|
|
90
|
+
display: flex;
|
|
91
|
+
flex-direction: column;
|
|
92
|
+
gap: 16px;
|
|
93
|
+
width: 100%;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@media (min-width: 768px) {
|
|
97
|
+
.Hero--hasImageBackground .Hero-content {
|
|
98
|
+
max-width: 50%;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.Hero--center .Hero-inner {
|
|
103
|
+
justify-content: center;
|
|
104
|
+
text-align: center;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.Hero--center .Hero-content {
|
|
108
|
+
align-items: center;
|
|
109
|
+
justify-content: center;
|
|
110
|
+
max-width: 100%;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.Hero-actions {
|
|
114
|
+
display: flex;
|
|
115
|
+
gap: 16px;
|
|
116
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Section } from '../../components/Section'
|
|
2
|
+
import styles from './styles.module.css'
|
|
3
|
+
import type { ComponentConfig } from '@measured/puck'
|
|
4
|
+
import getClassNameFactory from '../../utils/getClassNameFactory'
|
|
5
|
+
|
|
6
|
+
const getClassName = getClassNameFactory('Logos', styles)
|
|
7
|
+
|
|
8
|
+
export type LogosProps = {
|
|
9
|
+
logos: Array<{
|
|
10
|
+
alt: string
|
|
11
|
+
imageUrl: string
|
|
12
|
+
}>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Logos: ComponentConfig<LogosProps> = {
|
|
16
|
+
fields: {
|
|
17
|
+
logos: {
|
|
18
|
+
type: 'array',
|
|
19
|
+
getItemSummary: (item, i) => item.alt || `Feature #${i}`,
|
|
20
|
+
defaultItemProps: {
|
|
21
|
+
alt: '',
|
|
22
|
+
imageUrl: '',
|
|
23
|
+
},
|
|
24
|
+
arrayFields: {
|
|
25
|
+
alt: { type: 'text' },
|
|
26
|
+
imageUrl: { type: 'text' },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultProps: {
|
|
31
|
+
logos: [
|
|
32
|
+
{
|
|
33
|
+
alt: 'google',
|
|
34
|
+
imageUrl:
|
|
35
|
+
'https://logolook.net/wp-content/uploads/2021/06/Google-Logo.png',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
alt: 'google',
|
|
39
|
+
imageUrl:
|
|
40
|
+
'https://logolook.net/wp-content/uploads/2021/06/Google-Logo.png',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
alt: 'google',
|
|
44
|
+
imageUrl:
|
|
45
|
+
'https://logolook.net/wp-content/uploads/2021/06/Google-Logo.png',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
alt: 'google',
|
|
49
|
+
imageUrl:
|
|
50
|
+
'https://logolook.net/wp-content/uploads/2021/06/Google-Logo.png',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
alt: 'google',
|
|
54
|
+
imageUrl:
|
|
55
|
+
'https://logolook.net/wp-content/uploads/2021/06/Google-Logo.png',
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
render: ({ logos }) => {
|
|
60
|
+
return (
|
|
61
|
+
<Section className={getClassName()}>
|
|
62
|
+
<div className={getClassName('items')}>
|
|
63
|
+
{logos.map((item, i) => (
|
|
64
|
+
<div key={i} className={getClassName('item')}>
|
|
65
|
+
<img
|
|
66
|
+
className={getClassName('image')}
|
|
67
|
+
alt={item.alt}
|
|
68
|
+
src={item.imageUrl}
|
|
69
|
+
height={64}
|
|
70
|
+
></img>
|
|
71
|
+
</div>
|
|
72
|
+
))}
|
|
73
|
+
</div>
|
|
74
|
+
</Section>
|
|
75
|
+
)
|
|
76
|
+
},
|
|
77
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { ALargeSmall, AlignLeft } from 'lucide-react'
|
|
2
|
+
|
|
3
|
+
import { Section } from '../../components/Section'
|
|
4
|
+
import { withLayout } from '../../components/Layout'
|
|
5
|
+
import type { ComponentConfig } from '@measured/puck'
|
|
6
|
+
import type { WithLayout } from '../../components/Layout'
|
|
7
|
+
|
|
8
|
+
export type ParagraphProps = WithLayout<{
|
|
9
|
+
align: 'left' | 'center' | 'right'
|
|
10
|
+
text?: string
|
|
11
|
+
padding?: string
|
|
12
|
+
size?: 's' | 'm' | 'l'
|
|
13
|
+
italic?: boolean
|
|
14
|
+
bold?: boolean
|
|
15
|
+
maxWidth?: string
|
|
16
|
+
}>
|
|
17
|
+
|
|
18
|
+
const ParagraphInner: ComponentConfig<ParagraphProps> = {
|
|
19
|
+
fields: {
|
|
20
|
+
text: {
|
|
21
|
+
label: 'Text',
|
|
22
|
+
type: 'textarea',
|
|
23
|
+
contentEditable: true,
|
|
24
|
+
},
|
|
25
|
+
size: {
|
|
26
|
+
label: 'Size',
|
|
27
|
+
type: 'select',
|
|
28
|
+
labelIcon: <ALargeSmall size={16} />,
|
|
29
|
+
options: [
|
|
30
|
+
{ label: 'S', value: 's' },
|
|
31
|
+
{ label: 'M', value: 'm' },
|
|
32
|
+
{ label: 'L', value: 'l' },
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
italic: {
|
|
36
|
+
label: 'Italic',
|
|
37
|
+
type: 'radio',
|
|
38
|
+
options: [
|
|
39
|
+
{ label: 'true', value: true },
|
|
40
|
+
{ label: 'false', value: false },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
bold: {
|
|
44
|
+
label: 'Bold',
|
|
45
|
+
type: 'radio',
|
|
46
|
+
options: [
|
|
47
|
+
{ label: 'true', value: true },
|
|
48
|
+
{ label: 'false', value: false },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
align: {
|
|
52
|
+
label: 'Text Alignment',
|
|
53
|
+
type: 'radio',
|
|
54
|
+
labelIcon: <AlignLeft size={16} />,
|
|
55
|
+
options: [
|
|
56
|
+
{ label: 'Left', value: 'left' },
|
|
57
|
+
{ label: 'Center', value: 'center' },
|
|
58
|
+
{ label: 'Right', value: 'right' },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
maxWidth: { label: 'Maximum Width', type: 'text' },
|
|
62
|
+
},
|
|
63
|
+
defaultProps: {
|
|
64
|
+
align: 'left',
|
|
65
|
+
text: 'Paragraph',
|
|
66
|
+
size: 'm',
|
|
67
|
+
},
|
|
68
|
+
render: ({ align, text, italic, bold, size, maxWidth }) => {
|
|
69
|
+
return (
|
|
70
|
+
<Section maxWidth={maxWidth}>
|
|
71
|
+
<p
|
|
72
|
+
style={{
|
|
73
|
+
display: 'flex',
|
|
74
|
+
textAlign: align,
|
|
75
|
+
width: '100%',
|
|
76
|
+
fontStyle: italic ? 'italic' : 'normal',
|
|
77
|
+
fontWeight: bold ? 700 : 400,
|
|
78
|
+
fontSize: size === 'l' ? '28px' : size === 'm' ? '20px' : '16px',
|
|
79
|
+
maxWidth,
|
|
80
|
+
justifyContent:
|
|
81
|
+
align === 'center'
|
|
82
|
+
? 'center'
|
|
83
|
+
: align === 'right'
|
|
84
|
+
? 'flex-end'
|
|
85
|
+
: 'flex-start',
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
{text}
|
|
89
|
+
</p>
|
|
90
|
+
</Section>
|
|
91
|
+
)
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const Paragraph = withLayout(ParagraphInner)
|