simple-module-pagebuilder 0.0.1__py3-none-any.whl
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.
- pagebuilder/__init__.py +0 -0
- pagebuilder/components/DiffSummary.tsx +107 -0
- pagebuilder/components/ScheduledBadge.tsx +34 -0
- pagebuilder/components/StatusBadge.tsx +21 -0
- pagebuilder/components/blockRegistry.test.ts +56 -0
- pagebuilder/components/blockRegistry.ts +80 -0
- pagebuilder/components/blocks/Button.tsx +142 -0
- pagebuilder/components/blocks/Columns.tsx +64 -0
- pagebuilder/components/blocks/Heading.tsx +92 -0
- pagebuilder/components/blocks/Image.tsx +125 -0
- pagebuilder/components/blocks/MediaPicker.tsx +178 -0
- pagebuilder/components/blocks/Spacer.tsx +31 -0
- pagebuilder/components/blocks/Text.tsx +80 -0
- pagebuilder/components/editor/PageEditorToolbar.tsx +169 -0
- pagebuilder/components/editor/RevisionHistoryPanel.tsx +95 -0
- pagebuilder/components/editor/SchedulePanel.tsx +85 -0
- pagebuilder/components/editor/SeoSettingsPanel.tsx +119 -0
- pagebuilder/components/layoutPuckConfig.tsx +109 -0
- pagebuilder/components/media/MediaFilters.tsx +83 -0
- pagebuilder/components/media/MediaFolderSidebar.tsx +97 -0
- pagebuilder/components/media/MediaGrid.tsx +76 -0
- pagebuilder/components/media/MediaHeader.tsx +37 -0
- pagebuilder/components/media/MediaUploadQueue.tsx +103 -0
- pagebuilder/components/media/types.ts +20 -0
- pagebuilder/components/migrateContent.test.ts +146 -0
- pagebuilder/components/migrateContent.ts +135 -0
- pagebuilder/components/puckConfig.test.tsx +118 -0
- pagebuilder/components/puckConfig.tsx +121 -0
- pagebuilder/components/widgetCatalog.test.tsx +123 -0
- pagebuilder/components/widgetCatalog.ts +208 -0
- pagebuilder/components/widgets/_internal/accent-text.tsx +60 -0
- pagebuilder/components/widgets/_internal/disclosure-chevron.tsx +14 -0
- pagebuilder/components/widgets/_internal/eyebrow-split-section.tsx +71 -0
- pagebuilder/components/widgets/_internal/link-arrow.tsx +23 -0
- pagebuilder/components/widgets/_internal/rich-text.tsx +243 -0
- pagebuilder/components/widgets/_internal/solid-icons.tsx +73 -0
- pagebuilder/components/widgets/_shared/background-media.tsx +74 -0
- pagebuilder/components/widgets/_shared/content-media.tsx +43 -0
- pagebuilder/components/widgets/_shared/cta-button.tsx +69 -0
- pagebuilder/components/widgets/_shared/empty-placeholder.tsx +15 -0
- pagebuilder/components/widgets/_shared/eyebrow-text.tsx +27 -0
- pagebuilder/components/widgets/_shared/heading.tsx +88 -0
- pagebuilder/components/widgets/_shared/index.ts +39 -0
- pagebuilder/components/widgets/_shared/mask.ts +54 -0
- pagebuilder/components/widgets/_shared/section.tsx +60 -0
- pagebuilder/components/widgets/_shared/social-icon.tsx +71 -0
- pagebuilder/components/widgets/_shared/text-link.tsx +47 -0
- pagebuilder/components/widgets/accordion-widget.tsx +64 -0
- pagebuilder/components/widgets/alert-widget.tsx +47 -0
- pagebuilder/components/widgets/app-store-badges-widget.tsx +91 -0
- pagebuilder/components/widgets/article-cards-render.tsx +132 -0
- pagebuilder/components/widgets/article-cards-widget.tsx +72 -0
- pagebuilder/components/widgets/call-to-action-render.tsx +285 -0
- pagebuilder/components/widgets/call-to-action-styles.ts +28 -0
- pagebuilder/components/widgets/call-to-action-widget.tsx +83 -0
- pagebuilder/components/widgets/carousel-widget.tsx +97 -0
- pagebuilder/components/widgets/centered-hero-widget.tsx +54 -0
- pagebuilder/components/widgets/colored-action-list-widget.tsx +117 -0
- pagebuilder/components/widgets/column-widget.tsx +63 -0
- pagebuilder/components/widgets/contact-cards-widget.tsx +172 -0
- pagebuilder/components/widgets/contact-form-render.tsx +293 -0
- pagebuilder/components/widgets/contact-form-widget.tsx +69 -0
- pagebuilder/components/widgets/container-widget.tsx +93 -0
- pagebuilder/components/widgets/definition-list-widget.tsx +144 -0
- pagebuilder/components/widgets/divider-widget.tsx +56 -0
- pagebuilder/components/widgets/eyebrow-section-widget.tsx +56 -0
- pagebuilder/components/widgets/faq-render.tsx +268 -0
- pagebuilder/components/widgets/faq-widget.tsx +64 -0
- pagebuilder/components/widgets/feature-card-widget.tsx +98 -0
- pagebuilder/components/widgets/feature-cards-render.tsx +258 -0
- pagebuilder/components/widgets/feature-cards-styles.ts +107 -0
- pagebuilder/components/widgets/feature-cards-widget.tsx +133 -0
- pagebuilder/components/widgets/feature-grid-widget.tsx +118 -0
- pagebuilder/components/widgets/feature-showcase-widget.tsx +118 -0
- pagebuilder/components/widgets/gallery-widget.tsx +139 -0
- pagebuilder/components/widgets/grid-widget.tsx +59 -0
- pagebuilder/components/widgets/hero-widget.tsx +292 -0
- pagebuilder/components/widgets/html-widget.tsx +42 -0
- pagebuilder/components/widgets/iframe-widget.tsx +44 -0
- pagebuilder/components/widgets/leaderboard-widget.tsx +203 -0
- pagebuilder/components/widgets/list-widget.tsx +56 -0
- pagebuilder/components/widgets/logo-cloud-widget.tsx +127 -0
- pagebuilder/components/widgets/markdown-widget.tsx +148 -0
- pagebuilder/components/widgets/media-object-layout.ts +156 -0
- pagebuilder/components/widgets/media-object-render.tsx +284 -0
- pagebuilder/components/widgets/media-object-text.tsx +202 -0
- pagebuilder/components/widgets/media-object-widget.tsx +176 -0
- pagebuilder/components/widgets/newsletter-widget.tsx +217 -0
- pagebuilder/components/widgets/page-header-widget.tsx +118 -0
- pagebuilder/components/widgets/prose-section-widget.tsx +99 -0
- pagebuilder/components/widgets/quote-widget.tsx +51 -0
- pagebuilder/components/widgets/row-widget.tsx +74 -0
- pagebuilder/components/widgets/section-intro-widget.tsx +143 -0
- pagebuilder/components/widgets/signup-banner-widget.tsx +50 -0
- pagebuilder/components/widgets/simple-action-list-widget.tsx +91 -0
- pagebuilder/components/widgets/site-footer-widget.tsx +97 -0
- pagebuilder/components/widgets/site-header-nav.tsx +153 -0
- pagebuilder/components/widgets/site-header-widget.tsx +182 -0
- pagebuilder/components/widgets/social-banner-widget.tsx +83 -0
- pagebuilder/components/widgets/stats-widget.tsx +200 -0
- pagebuilder/components/widgets/steps-widget.tsx +211 -0
- pagebuilder/components/widgets/story-cards-widget.tsx +182 -0
- pagebuilder/components/widgets/table-widget.tsx +86 -0
- pagebuilder/components/widgets/tabs-widget.tsx +95 -0
- pagebuilder/components/widgets/tags-widget.tsx +85 -0
- pagebuilder/components/widgets/testimonial-widget.tsx +102 -0
- pagebuilder/components/widgets/timeline-layout.ts +73 -0
- pagebuilder/components/widgets/timeline-render.tsx +294 -0
- pagebuilder/components/widgets/timeline-widget.tsx +106 -0
- pagebuilder/components/widgets/under-construction-widget.tsx +40 -0
- pagebuilder/components/widgets/video-widget.tsx +103 -0
- pagebuilder/components/widgets/welcome-widget.tsx +38 -0
- pagebuilder/contracts/__init__.py +57 -0
- pagebuilder/contracts/events.py +25 -0
- pagebuilder/contracts/schemas.py +239 -0
- pagebuilder/conversion/blocks-to-html.test.ts +105 -0
- pagebuilder/conversion/blocks-to-html.ts +102 -0
- pagebuilder/conversion/index.ts +2 -0
- pagebuilder/conversion/types.ts +5 -0
- pagebuilder/deps.py +26 -0
- pagebuilder/diff.py +129 -0
- pagebuilder/endpoints/__init__.py +0 -0
- pagebuilder/endpoints/api/__init__.py +21 -0
- pagebuilder/endpoints/api/_deps.py +21 -0
- pagebuilder/endpoints/api/layout.py +78 -0
- pagebuilder/endpoints/api/pages.py +85 -0
- pagebuilder/endpoints/api/revisions.py +79 -0
- pagebuilder/endpoints/api/uploads.py +92 -0
- pagebuilder/endpoints/api/workflow.py +124 -0
- pagebuilder/endpoints/seo.py +95 -0
- pagebuilder/endpoints/views.py +228 -0
- pagebuilder/fields/checkbox-field.tsx +22 -0
- pagebuilder/fields/file-picker/file-gallery.tsx +152 -0
- pagebuilder/fields/file-picker/image-picker-field.tsx +214 -0
- pagebuilder/fields/file-picker/index.ts +6 -0
- pagebuilder/fields/file-picker/types.ts +15 -0
- pagebuilder/fields/index.ts +9 -0
- pagebuilder/fields/media-adapter.ts +47 -0
- pagebuilder/hooks/useAutosave.ts +134 -0
- pagebuilder/hooks/useEditorForm.ts +193 -0
- pagebuilder/hooks/useMediaUploads.ts +96 -0
- pagebuilder/hooks/usePageRevisions.ts +59 -0
- pagebuilder/hooks/usePageSchedule.ts +72 -0
- pagebuilder/hooks/usePageWorkflow.ts +173 -0
- pagebuilder/layout_service.py +101 -0
- pagebuilder/media_images.py +185 -0
- pagebuilder/media_queries.py +92 -0
- pagebuilder/media_service.py +222 -0
- pagebuilder/models.py +209 -0
- pagebuilder/module.py +279 -0
- pagebuilder/package.json +18 -0
- pagebuilder/pages/LayoutEditor.tsx +183 -0
- pagebuilder/pages/MediaLibrary.tsx +249 -0
- pagebuilder/pages/PageEditor.tsx +181 -0
- pagebuilder/pages/PageList.tsx +129 -0
- pagebuilder/pages/PendingReview.tsx +128 -0
- pagebuilder/pages/PublicPage.tsx +126 -0
- pagebuilder/permissions.py +44 -0
- pagebuilder/py.typed +0 -0
- pagebuilder/security.py +230 -0
- pagebuilder/service/__init__.py +141 -0
- pagebuilder/service/_common.py +24 -0
- pagebuilder/service/_revisions.py +92 -0
- pagebuilder/service/_workflow.py +207 -0
- pagebuilder/services.py +19 -0
- pagebuilder/settings.py +161 -0
- pagebuilder/static/widgets-base.css +57 -0
- pagebuilder/translation/apply.ts +54 -0
- pagebuilder/translation/extract.ts +141 -0
- pagebuilder/translation/index.ts +20 -0
- pagebuilder/translation/translatable-fields.ts +65 -0
- pagebuilder/translation/translation.test.ts +167 -0
- pagebuilder/utils/api.ts +12 -0
- pagebuilder/utils/datetime.ts +25 -0
- pagebuilder/utils/decorative-image.ts +15 -0
- pagebuilder/utils/editorSnapshot.ts +39 -0
- pagebuilder/utils/markdown.ts +160 -0
- pagebuilder/utils/mediaApi.ts +112 -0
- pagebuilder/utils/mediaFormat.ts +19 -0
- pagebuilder/utils/pagesApi.ts +111 -0
- pagebuilder/utils/parse-list.ts +7 -0
- pagebuilder/utils/request.ts +52 -0
- pagebuilder/utils/slugify.ts +9 -0
- pagebuilder/utils/types.ts +171 -0
- pagebuilder/utils/widgetUtils.ts +11 -0
- simple_module_pagebuilder-0.0.1.dist-info/METADATA +152 -0
- simple_module_pagebuilder-0.0.1.dist-info/RECORD +189 -0
- simple_module_pagebuilder-0.0.1.dist-info/WHEEL +4 -0
- simple_module_pagebuilder-0.0.1.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ComponentConfig, Slot } from '@puckeditor/core';
|
|
2
|
+
import { cn } from '../../utils/widgetUtils';
|
|
3
|
+
|
|
4
|
+
export type ColumnWidgetProps = {
|
|
5
|
+
width: '1/4' | '1/3' | '1/2' | '2/3' | '3/4' | 'full';
|
|
6
|
+
gap: 'none' | 'sm' | 'md' | 'lg';
|
|
7
|
+
column: Slot;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const WIDTH_CLASS: Record<ColumnWidgetProps['width'], string> = {
|
|
11
|
+
'1/4': 'w-full md:w-1/4',
|
|
12
|
+
'1/3': 'w-full md:w-1/3',
|
|
13
|
+
'1/2': 'w-full md:w-1/2',
|
|
14
|
+
'2/3': 'w-full md:w-2/3',
|
|
15
|
+
'3/4': 'w-full md:w-3/4',
|
|
16
|
+
full: 'w-full',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const GAP_CLASS: Record<ColumnWidgetProps['gap'], string> = {
|
|
20
|
+
none: 'gap-0',
|
|
21
|
+
sm: 'gap-2',
|
|
22
|
+
md: 'gap-4',
|
|
23
|
+
lg: 'gap-8',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const ColumnWidget: ComponentConfig<ColumnWidgetProps> = {
|
|
27
|
+
label: 'Column',
|
|
28
|
+
fields: {
|
|
29
|
+
width: {
|
|
30
|
+
type: 'select',
|
|
31
|
+
label: 'Width',
|
|
32
|
+
options: [
|
|
33
|
+
{ label: 'Quarter', value: '1/4' },
|
|
34
|
+
{ label: 'Third', value: '1/3' },
|
|
35
|
+
{ label: 'Half', value: '1/2' },
|
|
36
|
+
{ label: 'Two thirds', value: '2/3' },
|
|
37
|
+
{ label: 'Three quarters', value: '3/4' },
|
|
38
|
+
{ label: 'Full', value: 'full' },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
gap: {
|
|
42
|
+
type: 'select',
|
|
43
|
+
label: 'Gap',
|
|
44
|
+
options: [
|
|
45
|
+
{ label: 'None', value: 'none' },
|
|
46
|
+
{ label: 'Small', value: 'sm' },
|
|
47
|
+
{ label: 'Medium', value: 'md' },
|
|
48
|
+
{ label: 'Large', value: 'lg' },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
column: { type: 'slot' },
|
|
52
|
+
},
|
|
53
|
+
defaultProps: {
|
|
54
|
+
width: '1/2',
|
|
55
|
+
gap: 'md',
|
|
56
|
+
column: [],
|
|
57
|
+
},
|
|
58
|
+
render: ({ width, gap, column: Column }) => (
|
|
59
|
+
<div className={cn('flex flex-col', WIDTH_CLASS[width], GAP_CLASS[gap])}>
|
|
60
|
+
<Column />
|
|
61
|
+
</div>
|
|
62
|
+
),
|
|
63
|
+
};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type { ComponentConfig } from '@puckeditor/core';
|
|
2
|
+
import { User } from 'lucide-react';
|
|
3
|
+
import type { CSSProperties } from 'react';
|
|
4
|
+
import { createImageField, mediaLibraryAdapter } from '../../fields';
|
|
5
|
+
import { AccentText } from './_internal/accent-text';
|
|
6
|
+
import { renderRichText } from './_internal/rich-text';
|
|
7
|
+
|
|
8
|
+
export type ContactCardItem = {
|
|
9
|
+
name: string;
|
|
10
|
+
role: string;
|
|
11
|
+
email: string;
|
|
12
|
+
phone: string;
|
|
13
|
+
imageUrl: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type ContactCardsWidgetProps = {
|
|
17
|
+
eyebrow: string;
|
|
18
|
+
title: string;
|
|
19
|
+
variant: 'contact' | 'team';
|
|
20
|
+
items: ContactCardItem[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const DISPLAY_STYLE: CSSProperties = {
|
|
24
|
+
fontWeight: 'var(--pb-display-weight)' as CSSProperties['fontWeight'],
|
|
25
|
+
letterSpacing: 'var(--pb-display-tracking)',
|
|
26
|
+
fontFamily: 'var(--pb-display-font)',
|
|
27
|
+
color: 'var(--pb-heading-color)',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const ContactCardsWidget: ComponentConfig<ContactCardsWidgetProps> = {
|
|
31
|
+
label: 'Contact Cards',
|
|
32
|
+
fields: {
|
|
33
|
+
eyebrow: { type: 'text', label: 'Eyebrow' },
|
|
34
|
+
title: { type: 'text', label: 'Title' },
|
|
35
|
+
variant: {
|
|
36
|
+
type: 'select',
|
|
37
|
+
label: 'Style',
|
|
38
|
+
options: [
|
|
39
|
+
{ label: 'Contact (round avatar)', value: 'contact' },
|
|
40
|
+
{ label: 'Team (image-top card)', value: 'team' },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
items: {
|
|
44
|
+
type: 'array',
|
|
45
|
+
label: 'Contacts',
|
|
46
|
+
arrayFields: {
|
|
47
|
+
name: { type: 'text', label: 'Name' },
|
|
48
|
+
role: { type: 'text', label: 'Role' },
|
|
49
|
+
email: { type: 'text', label: 'Email' },
|
|
50
|
+
phone: { type: 'text', label: 'Phone' },
|
|
51
|
+
imageUrl: createImageField(mediaLibraryAdapter, 'Image'),
|
|
52
|
+
},
|
|
53
|
+
defaultItemProps: {
|
|
54
|
+
name: 'Jane Doe',
|
|
55
|
+
role: 'Director',
|
|
56
|
+
email: 'jane@example.com',
|
|
57
|
+
phone: '',
|
|
58
|
+
imageUrl: '',
|
|
59
|
+
},
|
|
60
|
+
min: 1,
|
|
61
|
+
max: 12,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
defaultProps: {
|
|
65
|
+
eyebrow: '',
|
|
66
|
+
title: 'Get in touch',
|
|
67
|
+
variant: 'contact',
|
|
68
|
+
items: [
|
|
69
|
+
{
|
|
70
|
+
name: 'Jane Doe',
|
|
71
|
+
role: 'Director',
|
|
72
|
+
email: 'jane@example.com',
|
|
73
|
+
phone: '',
|
|
74
|
+
imageUrl: '',
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
render: ({ eyebrow, title, variant = 'contact', items }) => {
|
|
79
|
+
const team = variant === 'team';
|
|
80
|
+
return (
|
|
81
|
+
<div className="container mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
|
82
|
+
{(eyebrow || title) && (
|
|
83
|
+
<div className="mb-8 text-center">
|
|
84
|
+
{eyebrow && (
|
|
85
|
+
<p className="mb-2 text-sm font-medium text-[#686873]">{renderRichText(eyebrow)}</p>
|
|
86
|
+
)}
|
|
87
|
+
{/* AccentText, not bare renderRichText: this is a display heading
|
|
88
|
+
on `--pb-display-weight`, where a plain <strong>'s relative
|
|
89
|
+
`bolder` can compute to no visible change. */}
|
|
90
|
+
{title && (
|
|
91
|
+
<h2 className="text-3xl sm:text-4xl" style={DISPLAY_STYLE}>
|
|
92
|
+
<AccentText text={title} />
|
|
93
|
+
</h2>
|
|
94
|
+
)}
|
|
95
|
+
</div>
|
|
96
|
+
)}
|
|
97
|
+
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
98
|
+
{items?.map((item, idx) =>
|
|
99
|
+
team ? (
|
|
100
|
+
// Image-top team card: rectangular media (dark placeholder with a
|
|
101
|
+
// person glyph when no photo), left-aligned name + affiliation.
|
|
102
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: author-ordered array with no stable id; card content repeats, so a content key collides
|
|
103
|
+
<div key={idx}>
|
|
104
|
+
{item.imageUrl ? (
|
|
105
|
+
<img
|
|
106
|
+
src={item.imageUrl}
|
|
107
|
+
alt={item.name}
|
|
108
|
+
loading="lazy"
|
|
109
|
+
className="aspect-[4/3] w-full rounded-xl object-cover"
|
|
110
|
+
/>
|
|
111
|
+
) : (
|
|
112
|
+
<div className="flex aspect-[4/3] w-full items-center justify-center rounded-xl bg-[#1a353e]">
|
|
113
|
+
<User className="size-10 text-white/35" aria-hidden="true" />
|
|
114
|
+
</div>
|
|
115
|
+
)}
|
|
116
|
+
{item.name && (
|
|
117
|
+
<h3
|
|
118
|
+
className="mt-4 text-base font-semibold"
|
|
119
|
+
style={{ color: 'var(--pb-heading-color)' }}
|
|
120
|
+
>
|
|
121
|
+
{renderRichText(item.name)}
|
|
122
|
+
</h3>
|
|
123
|
+
)}
|
|
124
|
+
{item.role && (
|
|
125
|
+
<p className="mt-1 text-sm text-[#686873]">{renderRichText(item.role)}</p>
|
|
126
|
+
)}
|
|
127
|
+
</div>
|
|
128
|
+
) : (
|
|
129
|
+
<div
|
|
130
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: author-ordered array with no stable id; card content repeats, so a content key collides
|
|
131
|
+
key={idx}
|
|
132
|
+
className="rounded-lg border bg-white dark:bg-gray-900 p-6 text-center shadow-sm"
|
|
133
|
+
>
|
|
134
|
+
{item.imageUrl ? (
|
|
135
|
+
<img
|
|
136
|
+
src={item.imageUrl}
|
|
137
|
+
alt={item.name}
|
|
138
|
+
className="size-24 rounded-full mx-auto object-cover"
|
|
139
|
+
/>
|
|
140
|
+
) : (
|
|
141
|
+
<div className="size-24 rounded-full mx-auto bg-gradient-to-br from-gray-200 to-gray-300" />
|
|
142
|
+
)}
|
|
143
|
+
<h3 className="mt-4 text-lg font-semibold">{renderRichText(item.name || '')}</h3>
|
|
144
|
+
{item.role && (
|
|
145
|
+
<p className="text-sm text-gray-500 dark:text-gray-400">
|
|
146
|
+
{renderRichText(item.role)}
|
|
147
|
+
</p>
|
|
148
|
+
)}
|
|
149
|
+
{item.email && (
|
|
150
|
+
<a
|
|
151
|
+
href={`mailto:${item.email}`}
|
|
152
|
+
className="block mt-2 text-primary-700 hover:underline text-sm break-all"
|
|
153
|
+
>
|
|
154
|
+
{item.email}
|
|
155
|
+
</a>
|
|
156
|
+
)}
|
|
157
|
+
{item.phone && (
|
|
158
|
+
<a
|
|
159
|
+
href={`tel:${item.phone}`}
|
|
160
|
+
className="block text-sm text-gray-600 dark:text-gray-300"
|
|
161
|
+
>
|
|
162
|
+
{item.phone}
|
|
163
|
+
</a>
|
|
164
|
+
)}
|
|
165
|
+
</div>
|
|
166
|
+
),
|
|
167
|
+
)}
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
);
|
|
171
|
+
},
|
|
172
|
+
};
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/** Contact form types and render — split from contact-form-widget.tsx,
|
|
2
|
+
* which keeps the field definitions, so both stay under the 300-line cap. */
|
|
3
|
+
|
|
4
|
+
import { type FormEvent, useId, useState } from 'react';
|
|
5
|
+
import { parseList } from '../../utils/widgetUtils';
|
|
6
|
+
import { AccentText } from './_internal/accent-text';
|
|
7
|
+
import { RichTextBlock, renderRichText } from './_internal/rich-text';
|
|
8
|
+
|
|
9
|
+
export type ContactFormWidgetProps = {
|
|
10
|
+
variant: 'boxed' | 'underline';
|
|
11
|
+
/**
|
|
12
|
+
* Optional CSS color for the form panel. When set, the panel renders on that
|
|
13
|
+
* color with inverse (white) labels, underline fields and an outline pill
|
|
14
|
+
* submit; blank keeps the default grey panel.
|
|
15
|
+
*/
|
|
16
|
+
surfaceColor: string;
|
|
17
|
+
title: string;
|
|
18
|
+
subtitle: string;
|
|
19
|
+
nameLabel: string;
|
|
20
|
+
namePlaceholder: string;
|
|
21
|
+
orgLabel: string;
|
|
22
|
+
orgPlaceholder: string;
|
|
23
|
+
enquiryLabel: string;
|
|
24
|
+
enquiryOptions: string;
|
|
25
|
+
emailLabel: string;
|
|
26
|
+
emailPlaceholder: string;
|
|
27
|
+
messageLabel: string;
|
|
28
|
+
messagePlaceholder: string;
|
|
29
|
+
termsLabel: string;
|
|
30
|
+
submitLabel: string;
|
|
31
|
+
successTitle: string;
|
|
32
|
+
successBody: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const HEADING_STYLE = { color: 'var(--pb-heading-color)' };
|
|
36
|
+
const BODY_STYLE = { color: 'var(--pb-body-color)' };
|
|
37
|
+
// Text on a colored (surfaceColor) panel — the shared inverse-on-color token.
|
|
38
|
+
const INVERSE_STYLE = { color: 'var(--pb-surface-contrast, #ffffff)' };
|
|
39
|
+
const INPUT_CLASS =
|
|
40
|
+
'w-full border rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500';
|
|
41
|
+
const INPUT_STYLE = {
|
|
42
|
+
borderColor: 'var(--border)',
|
|
43
|
+
color: 'var(--pb-body-color)',
|
|
44
|
+
};
|
|
45
|
+
// Boxed field, inverse (on a colored panel): translucent white fill + rule.
|
|
46
|
+
const INPUT_INVERSE_CLASS =
|
|
47
|
+
'w-full rounded-md border border-white/60 bg-white/10 px-3 py-2 placeholder:text-white/70 focus:outline-none focus:ring-2 focus:ring-white/50';
|
|
48
|
+
// Underline variant (GCA): borderless field with only a bottom rule, on a soft
|
|
49
|
+
// grey panel, label-as-placeholder.
|
|
50
|
+
const UNDERLINE_INPUT_CLASS =
|
|
51
|
+
'w-full bg-transparent border-0 border-b border-[#686873]/50 px-0 py-4 text-xl text-[var(--pb-heading-color)] placeholder:text-[#686873] focus:outline-none focus:border-[var(--pb-heading-color)]';
|
|
52
|
+
// Underline field, inverse (on a colored panel): white rule + placeholder.
|
|
53
|
+
const UNDERLINE_INPUT_INVERSE_CLASS =
|
|
54
|
+
'w-full bg-transparent border-0 border-b border-white/60 px-0 py-4 text-xl placeholder:text-white/70 focus:outline-none focus:border-white';
|
|
55
|
+
|
|
56
|
+
export function ContactFormRender({
|
|
57
|
+
variant,
|
|
58
|
+
surfaceColor,
|
|
59
|
+
title,
|
|
60
|
+
subtitle,
|
|
61
|
+
nameLabel,
|
|
62
|
+
namePlaceholder,
|
|
63
|
+
orgLabel,
|
|
64
|
+
orgPlaceholder,
|
|
65
|
+
enquiryLabel,
|
|
66
|
+
enquiryOptions,
|
|
67
|
+
emailLabel,
|
|
68
|
+
emailPlaceholder,
|
|
69
|
+
messageLabel,
|
|
70
|
+
messagePlaceholder,
|
|
71
|
+
termsLabel,
|
|
72
|
+
submitLabel,
|
|
73
|
+
successTitle,
|
|
74
|
+
successBody,
|
|
75
|
+
}: ContactFormWidgetProps) {
|
|
76
|
+
const nameId = useId();
|
|
77
|
+
const orgId = useId();
|
|
78
|
+
const enquiryId = useId();
|
|
79
|
+
const emailId = useId();
|
|
80
|
+
const messageId = useId();
|
|
81
|
+
const termsId = useId();
|
|
82
|
+
const [submitted, setSubmitted] = useState(false);
|
|
83
|
+
const options = parseList(enquiryOptions);
|
|
84
|
+
const underline = variant === 'underline';
|
|
85
|
+
// A colored panel flips fields/labels to inverse (white) styling.
|
|
86
|
+
const inverse = Boolean(surfaceColor);
|
|
87
|
+
const inputClass = inverse
|
|
88
|
+
? underline
|
|
89
|
+
? UNDERLINE_INPUT_INVERSE_CLASS
|
|
90
|
+
: INPUT_INVERSE_CLASS
|
|
91
|
+
: underline
|
|
92
|
+
? UNDERLINE_INPUT_CLASS
|
|
93
|
+
: INPUT_CLASS;
|
|
94
|
+
const inputStyle = inverse ? INVERSE_STYLE : underline ? undefined : INPUT_STYLE;
|
|
95
|
+
const labelClass = underline ? 'sr-only' : 'block text-sm font-medium mb-1';
|
|
96
|
+
const labelStyle = inverse ? INVERSE_STYLE : HEADING_STYLE;
|
|
97
|
+
const panelBodyStyle = inverse ? INVERSE_STYLE : BODY_STYLE;
|
|
98
|
+
|
|
99
|
+
function handleSubmit(event: FormEvent<HTMLFormElement>) {
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
setSubmitted(true);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<section
|
|
106
|
+
className={
|
|
107
|
+
underline
|
|
108
|
+
? 'container mx-auto px-4 py-[var(--pb-section-py,3rem)] sm:px-6 lg:px-8'
|
|
109
|
+
: 'container mx-auto py-[var(--pb-section-py,3rem)] px-4 max-w-2xl'
|
|
110
|
+
}
|
|
111
|
+
>
|
|
112
|
+
{(title || subtitle) && (
|
|
113
|
+
<div className="text-center mb-8">
|
|
114
|
+
{/* AccentText, not bare renderRichText: `font-light` (300) here makes
|
|
115
|
+
a plain <strong>'s relative `bolder` compute to 400 — no visible
|
|
116
|
+
change. */}
|
|
117
|
+
{title && (
|
|
118
|
+
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-light" style={HEADING_STYLE}>
|
|
119
|
+
<AccentText text={title} />
|
|
120
|
+
</h2>
|
|
121
|
+
)}
|
|
122
|
+
{subtitle && (
|
|
123
|
+
<RichTextBlock text={subtitle} className="mt-3 text-lg" style={BODY_STYLE} />
|
|
124
|
+
)}
|
|
125
|
+
</div>
|
|
126
|
+
)}
|
|
127
|
+
<div
|
|
128
|
+
className={
|
|
129
|
+
inverse
|
|
130
|
+
? 'mx-auto max-w-3xl rounded-[12px] px-6 py-10 sm:px-12 sm:py-12'
|
|
131
|
+
: underline
|
|
132
|
+
? 'mx-auto max-w-3xl rounded-[12px] bg-[var(--pb-surface-muted,#f3f3f4)] px-6 py-10 sm:px-12 sm:py-12'
|
|
133
|
+
: ''
|
|
134
|
+
}
|
|
135
|
+
style={inverse ? { backgroundColor: surfaceColor } : undefined}
|
|
136
|
+
>
|
|
137
|
+
{submitted ? (
|
|
138
|
+
<div
|
|
139
|
+
className="rounded-md border px-6 py-8 text-center"
|
|
140
|
+
style={inverse ? { borderColor: 'rgba(255,255,255,0.6)' } : INPUT_STYLE}
|
|
141
|
+
>
|
|
142
|
+
{successTitle && (
|
|
143
|
+
<h3 className="text-xl font-medium" style={labelStyle}>
|
|
144
|
+
{renderRichText(successTitle)}
|
|
145
|
+
</h3>
|
|
146
|
+
)}
|
|
147
|
+
{successBody && (
|
|
148
|
+
<RichTextBlock text={successBody} className="mt-2" style={panelBodyStyle} />
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
) : (
|
|
152
|
+
<form className="space-y-4" onSubmit={handleSubmit}>
|
|
153
|
+
<div>
|
|
154
|
+
<label htmlFor={nameId} className={labelClass} style={labelStyle}>
|
|
155
|
+
{renderRichText(nameLabel)}
|
|
156
|
+
</label>
|
|
157
|
+
<input
|
|
158
|
+
id={nameId}
|
|
159
|
+
type="text"
|
|
160
|
+
required
|
|
161
|
+
placeholder={namePlaceholder}
|
|
162
|
+
className={inputClass}
|
|
163
|
+
style={inputStyle}
|
|
164
|
+
/>
|
|
165
|
+
</div>
|
|
166
|
+
<div>
|
|
167
|
+
<label htmlFor={emailId} className={labelClass} style={labelStyle}>
|
|
168
|
+
{renderRichText(emailLabel)}
|
|
169
|
+
</label>
|
|
170
|
+
<input
|
|
171
|
+
id={emailId}
|
|
172
|
+
type="email"
|
|
173
|
+
required
|
|
174
|
+
placeholder={emailPlaceholder}
|
|
175
|
+
className={inputClass}
|
|
176
|
+
style={inputStyle}
|
|
177
|
+
/>
|
|
178
|
+
</div>
|
|
179
|
+
{orgLabel && (
|
|
180
|
+
<div>
|
|
181
|
+
<label htmlFor={orgId} className={labelClass} style={labelStyle}>
|
|
182
|
+
{renderRichText(orgLabel)}
|
|
183
|
+
</label>
|
|
184
|
+
<input
|
|
185
|
+
id={orgId}
|
|
186
|
+
type="text"
|
|
187
|
+
placeholder={orgPlaceholder}
|
|
188
|
+
className={inputClass}
|
|
189
|
+
style={inputStyle}
|
|
190
|
+
/>
|
|
191
|
+
</div>
|
|
192
|
+
)}
|
|
193
|
+
<div>
|
|
194
|
+
<label htmlFor={enquiryId} className={labelClass} style={labelStyle}>
|
|
195
|
+
{renderRichText(enquiryLabel)}
|
|
196
|
+
</label>
|
|
197
|
+
<select
|
|
198
|
+
id={enquiryId}
|
|
199
|
+
required
|
|
200
|
+
className={inputClass}
|
|
201
|
+
style={inputStyle}
|
|
202
|
+
defaultValue=""
|
|
203
|
+
>
|
|
204
|
+
<option value="" disabled>
|
|
205
|
+
{underline ? enquiryLabel || 'Select…' : 'Select…'}
|
|
206
|
+
</option>
|
|
207
|
+
{options.map((option) => (
|
|
208
|
+
<option key={option} value={option}>
|
|
209
|
+
{option}
|
|
210
|
+
</option>
|
|
211
|
+
))}
|
|
212
|
+
</select>
|
|
213
|
+
</div>
|
|
214
|
+
<div>
|
|
215
|
+
<label htmlFor={messageId} className={labelClass} style={labelStyle}>
|
|
216
|
+
{renderRichText(messageLabel)}
|
|
217
|
+
</label>
|
|
218
|
+
<textarea
|
|
219
|
+
id={messageId}
|
|
220
|
+
required
|
|
221
|
+
placeholder={messagePlaceholder}
|
|
222
|
+
rows={5}
|
|
223
|
+
className={inputClass}
|
|
224
|
+
style={inputStyle}
|
|
225
|
+
/>
|
|
226
|
+
</div>
|
|
227
|
+
<div className="flex items-start gap-2">
|
|
228
|
+
<input
|
|
229
|
+
id={termsId}
|
|
230
|
+
type="checkbox"
|
|
231
|
+
required
|
|
232
|
+
className="mt-1"
|
|
233
|
+
style={{
|
|
234
|
+
accentColor: inverse ? 'var(--pb-surface-contrast, #ffffff)' : 'var(--primary)',
|
|
235
|
+
}}
|
|
236
|
+
/>
|
|
237
|
+
<label htmlFor={termsId} className="text-sm" style={panelBodyStyle}>
|
|
238
|
+
{/* `*word*` segments render in the accent colour (e.g. the
|
|
239
|
+
orange "Terms of Use" link in the Figma); on a colored
|
|
240
|
+
panel they invert to the surface-contrast color, underlined.
|
|
241
|
+
The rest is ordinary inline markdown, so the terms can also
|
|
242
|
+
carry a real `[link](/page/terms)`. */}
|
|
243
|
+
{renderRichText(termsLabel, {
|
|
244
|
+
renderEm: (content, key) => (
|
|
245
|
+
<span
|
|
246
|
+
key={key}
|
|
247
|
+
className={inverse ? 'font-semibold underline' : 'font-semibold'}
|
|
248
|
+
style={{
|
|
249
|
+
color: inverse
|
|
250
|
+
? 'var(--pb-surface-contrast, #ffffff)'
|
|
251
|
+
: 'var(--pb-accent, #fa6e25)',
|
|
252
|
+
}}
|
|
253
|
+
>
|
|
254
|
+
{content}
|
|
255
|
+
</span>
|
|
256
|
+
),
|
|
257
|
+
})}
|
|
258
|
+
</label>
|
|
259
|
+
</div>
|
|
260
|
+
<button
|
|
261
|
+
type="submit"
|
|
262
|
+
className={
|
|
263
|
+
inverse
|
|
264
|
+
? 'inline-flex items-center justify-center rounded-full border px-6 py-3 font-semibold transition-colors hover:bg-white/10'
|
|
265
|
+
: underline
|
|
266
|
+
? 'inline-flex items-center justify-center rounded-full bg-[var(--primary,#1a353e)] px-6 py-3 font-semibold text-[var(--primary-foreground,#fff)] transition-opacity hover:opacity-90'
|
|
267
|
+
: 'inline-flex items-center justify-center px-5 py-2.5 rounded-md bg-primary-700 hover:bg-primary-800 text-white font-medium'
|
|
268
|
+
}
|
|
269
|
+
style={
|
|
270
|
+
inverse
|
|
271
|
+
? {
|
|
272
|
+
borderColor: 'var(--pb-surface-contrast, #ffffff)',
|
|
273
|
+
color: 'var(--pb-surface-contrast, #ffffff)',
|
|
274
|
+
backgroundColor: 'transparent',
|
|
275
|
+
}
|
|
276
|
+
: undefined
|
|
277
|
+
}
|
|
278
|
+
>
|
|
279
|
+
{renderRichText(submitLabel, { allowLinks: false })}
|
|
280
|
+
{/* Trailing arrow, off by default (`--pb-button-arrow-display`). */}
|
|
281
|
+
<span
|
|
282
|
+
aria-hidden="true"
|
|
283
|
+
className="ml-2 [display:var(--pb-button-arrow-display,none)]"
|
|
284
|
+
>
|
|
285
|
+
→
|
|
286
|
+
</span>
|
|
287
|
+
</button>
|
|
288
|
+
</form>
|
|
289
|
+
)}
|
|
290
|
+
</div>
|
|
291
|
+
</section>
|
|
292
|
+
);
|
|
293
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** Contact form widget: field definitions and config assembly.
|
|
2
|
+
* Types and render live in contact-form-render.tsx. */
|
|
3
|
+
|
|
4
|
+
import type { ComponentConfig } from '@puckeditor/core';
|
|
5
|
+
|
|
6
|
+
import { ContactFormRender, type ContactFormWidgetProps } from './contact-form-render';
|
|
7
|
+
|
|
8
|
+
export type { ContactFormWidgetProps } from './contact-form-render';
|
|
9
|
+
|
|
10
|
+
export const ContactFormWidget: ComponentConfig<ContactFormWidgetProps> = {
|
|
11
|
+
label: 'Contact form',
|
|
12
|
+
fields: {
|
|
13
|
+
variant: {
|
|
14
|
+
type: 'select',
|
|
15
|
+
label: 'Style',
|
|
16
|
+
options: [
|
|
17
|
+
{ label: 'Boxed inputs', value: 'boxed' },
|
|
18
|
+
{ label: 'Underline inputs (panel)', value: 'underline' },
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
surfaceColor: {
|
|
22
|
+
type: 'text',
|
|
23
|
+
label: 'Panel color (CSS, inverse text — optional)',
|
|
24
|
+
},
|
|
25
|
+
title: { type: 'text', label: 'Title' },
|
|
26
|
+
subtitle: { type: 'textarea', label: 'Subtitle' },
|
|
27
|
+
nameLabel: { type: 'text', label: 'Name field label' },
|
|
28
|
+
namePlaceholder: { type: 'text', label: 'Name placeholder' },
|
|
29
|
+
orgLabel: {
|
|
30
|
+
type: 'text',
|
|
31
|
+
label: 'Organisation field label (blank to hide)',
|
|
32
|
+
},
|
|
33
|
+
orgPlaceholder: { type: 'text', label: 'Organisation placeholder' },
|
|
34
|
+
enquiryLabel: { type: 'text', label: 'Enquiry type label' },
|
|
35
|
+
enquiryOptions: {
|
|
36
|
+
type: 'textarea',
|
|
37
|
+
label: 'Enquiry options (one per line)',
|
|
38
|
+
},
|
|
39
|
+
emailLabel: { type: 'text', label: 'Email field label' },
|
|
40
|
+
emailPlaceholder: { type: 'text', label: 'Email placeholder' },
|
|
41
|
+
messageLabel: { type: 'text', label: 'Message field label' },
|
|
42
|
+
messagePlaceholder: { type: 'text', label: 'Message placeholder' },
|
|
43
|
+
termsLabel: { type: 'text', label: 'Terms agreement label' },
|
|
44
|
+
submitLabel: { type: 'text', label: 'Submit button' },
|
|
45
|
+
successTitle: { type: 'text', label: 'Success title' },
|
|
46
|
+
successBody: { type: 'textarea', label: 'Success body' },
|
|
47
|
+
},
|
|
48
|
+
defaultProps: {
|
|
49
|
+
variant: 'boxed',
|
|
50
|
+
surfaceColor: '',
|
|
51
|
+
title: 'Contact us',
|
|
52
|
+
subtitle: "We'd love to hear from you.",
|
|
53
|
+
nameLabel: 'Name',
|
|
54
|
+
namePlaceholder: 'Your name',
|
|
55
|
+
orgLabel: 'Organisation name',
|
|
56
|
+
orgPlaceholder: 'Your organisation',
|
|
57
|
+
enquiryLabel: 'Enquiry type',
|
|
58
|
+
enquiryOptions: 'General\nCollaborations\nData contributions\nPress & media\nScience',
|
|
59
|
+
emailLabel: 'Email',
|
|
60
|
+
emailPlaceholder: 'you@example.com',
|
|
61
|
+
messageLabel: 'Message',
|
|
62
|
+
messagePlaceholder: 'How can we help?',
|
|
63
|
+
termsLabel: 'I have read and agree to the Terms and Conditions',
|
|
64
|
+
submitLabel: 'Send message',
|
|
65
|
+
successTitle: 'Your message has been sent',
|
|
66
|
+
successBody: 'Thank you for contacting us, a team member will be in touch with you shortly',
|
|
67
|
+
},
|
|
68
|
+
render: (props) => <ContactFormRender {...props} />,
|
|
69
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { ComponentConfig, Slot } from '@puckeditor/core';
|
|
2
|
+
import { cn } from '../../utils/widgetUtils';
|
|
3
|
+
|
|
4
|
+
export type ContainerWidgetProps = {
|
|
5
|
+
backgroundColor: string;
|
|
6
|
+
padding: 'none' | 'sm' | 'md' | 'lg';
|
|
7
|
+
maxWidth: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full';
|
|
8
|
+
rounded: 'none' | 'md' | 'lg' | 'xl';
|
|
9
|
+
container: Slot;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const PADDING_CLASS: Record<ContainerWidgetProps['padding'], string> = {
|
|
13
|
+
none: 'p-0',
|
|
14
|
+
sm: 'p-4',
|
|
15
|
+
md: 'p-8',
|
|
16
|
+
lg: 'p-12',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const MAX_WIDTH_CLASS: Record<ContainerWidgetProps['maxWidth'], string> = {
|
|
20
|
+
sm: 'max-w-screen-sm',
|
|
21
|
+
md: 'max-w-screen-md',
|
|
22
|
+
lg: 'max-w-screen-lg',
|
|
23
|
+
xl: 'max-w-screen-xl',
|
|
24
|
+
'2xl': 'max-w-screen-2xl',
|
|
25
|
+
full: 'max-w-full',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const ROUNDED_CLASS: Record<ContainerWidgetProps['rounded'], string> = {
|
|
29
|
+
none: 'rounded-none',
|
|
30
|
+
md: 'rounded-md',
|
|
31
|
+
lg: 'rounded-lg',
|
|
32
|
+
xl: 'rounded-xl',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const ContainerWidget: ComponentConfig<ContainerWidgetProps> = {
|
|
36
|
+
label: 'Container',
|
|
37
|
+
fields: {
|
|
38
|
+
backgroundColor: { type: 'text', label: 'Background color (CSS)' },
|
|
39
|
+
padding: {
|
|
40
|
+
type: 'select',
|
|
41
|
+
label: 'Padding',
|
|
42
|
+
options: [
|
|
43
|
+
{ label: 'None', value: 'none' },
|
|
44
|
+
{ label: 'Small', value: 'sm' },
|
|
45
|
+
{ label: 'Medium', value: 'md' },
|
|
46
|
+
{ label: 'Large', value: 'lg' },
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
maxWidth: {
|
|
50
|
+
type: 'select',
|
|
51
|
+
label: 'Max width',
|
|
52
|
+
options: [
|
|
53
|
+
{ label: 'Small', value: 'sm' },
|
|
54
|
+
{ label: 'Medium', value: 'md' },
|
|
55
|
+
{ label: 'Large', value: 'lg' },
|
|
56
|
+
{ label: 'Extra Large', value: 'xl' },
|
|
57
|
+
{ label: '2XL', value: '2xl' },
|
|
58
|
+
{ label: 'Full', value: 'full' },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
rounded: {
|
|
62
|
+
type: 'select',
|
|
63
|
+
label: 'Rounded',
|
|
64
|
+
options: [
|
|
65
|
+
{ label: 'None', value: 'none' },
|
|
66
|
+
{ label: 'Medium', value: 'md' },
|
|
67
|
+
{ label: 'Large', value: 'lg' },
|
|
68
|
+
{ label: 'Extra Large', value: 'xl' },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
container: { type: 'slot' },
|
|
72
|
+
},
|
|
73
|
+
defaultProps: {
|
|
74
|
+
backgroundColor: '',
|
|
75
|
+
padding: 'md',
|
|
76
|
+
maxWidth: 'xl',
|
|
77
|
+
rounded: 'none',
|
|
78
|
+
container: [],
|
|
79
|
+
},
|
|
80
|
+
render: ({ backgroundColor, padding, maxWidth, rounded, container: Container }) => (
|
|
81
|
+
<div
|
|
82
|
+
className={cn(
|
|
83
|
+
'mx-auto',
|
|
84
|
+
PADDING_CLASS[padding],
|
|
85
|
+
MAX_WIDTH_CLASS[maxWidth],
|
|
86
|
+
ROUNDED_CLASS[rounded],
|
|
87
|
+
)}
|
|
88
|
+
style={backgroundColor ? { backgroundColor } : undefined}
|
|
89
|
+
>
|
|
90
|
+
<Container />
|
|
91
|
+
</div>
|
|
92
|
+
),
|
|
93
|
+
};
|