@delmaredigital/payload-puck 0.6.26 → 0.6.27
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/dist/ai/presets/componentAiDefaults.js +17 -0
- package/dist/components/exports.d.ts +2 -0
- package/dist/components/exports.js +2 -0
- package/dist/components/layout/Columns.d.ts +40 -0
- package/dist/components/layout/Columns.js +274 -0
- package/dist/components/layout/Columns.server.d.ts +48 -0
- package/dist/components/layout/Columns.server.js +157 -0
- package/dist/config/config.editor.d.ts +1 -0
- package/dist/config/config.editor.js +3 -0
- package/dist/config/index.js +3 -0
- package/dist/config/presets.d.ts +1 -0
- package/dist/config/presets.js +3 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -153,6 +153,23 @@
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
},
|
|
156
|
+
Columns: {
|
|
157
|
+
ai: {
|
|
158
|
+
instructions: 'Use when content must be targeted into specific columns (each column is its own drop zone), e.g. a text column beside an image column. For content that just flows across equal cells, prefer Grid.'
|
|
159
|
+
},
|
|
160
|
+
fields: {
|
|
161
|
+
count: {
|
|
162
|
+
ai: {
|
|
163
|
+
instructions: 'Number of columns, 2-4.'
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
distribution: {
|
|
167
|
+
ai: {
|
|
168
|
+
instructions: "Column width split. 'equal' for even columns, or an fr ratio like '2:1' / '1:2' / '1:1:2' for asymmetric layouts (must match the column count)."
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
156
173
|
// Content Components
|
|
157
174
|
Card: {
|
|
158
175
|
ai: {
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
export { ContainerConfig } from './layout/Container.js';
|
|
26
26
|
export { FlexConfig } from './layout/Flex.js';
|
|
27
27
|
export { GridConfig } from './layout/Grid.js';
|
|
28
|
+
export { ColumnsConfig } from './layout/Columns.js';
|
|
28
29
|
export { SectionConfig } from './layout/Section.js';
|
|
29
30
|
export { SpacerConfig } from './layout/Spacer.js';
|
|
30
31
|
export { TemplateConfig } from './layout/Template.js';
|
|
@@ -40,6 +41,7 @@ export { AccordionConfig } from './interactive/Accordion.js';
|
|
|
40
41
|
export { ContainerConfig as ContainerServerConfig } from './layout/Container.server.js';
|
|
41
42
|
export { FlexConfig as FlexServerConfig } from './layout/Flex.server.js';
|
|
42
43
|
export { GridConfig as GridServerConfig } from './layout/Grid.server.js';
|
|
44
|
+
export { ColumnsConfig as ColumnsServerConfig } from './layout/Columns.server.js';
|
|
43
45
|
export { SectionConfig as SectionServerConfig } from './layout/Section.server.js';
|
|
44
46
|
export { SpacerConfig as SpacerServerConfig } from './layout/Spacer.server.js';
|
|
45
47
|
export { TemplateServerConfig } from './layout/Template.server.js';
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
export { ContainerConfig } from './layout/Container.js';
|
|
30
30
|
export { FlexConfig } from './layout/Flex.js';
|
|
31
31
|
export { GridConfig } from './layout/Grid.js';
|
|
32
|
+
export { ColumnsConfig } from './layout/Columns.js';
|
|
32
33
|
export { SectionConfig } from './layout/Section.js';
|
|
33
34
|
export { SpacerConfig } from './layout/Spacer.js';
|
|
34
35
|
export { TemplateConfig } from './layout/Template.js';
|
|
@@ -53,6 +54,7 @@ export { AccordionConfig } from './interactive/Accordion.js';
|
|
|
53
54
|
export { ContainerConfig as ContainerServerConfig } from './layout/Container.server.js';
|
|
54
55
|
export { FlexConfig as FlexServerConfig } from './layout/Flex.server.js';
|
|
55
56
|
export { GridConfig as GridServerConfig } from './layout/Grid.server.js';
|
|
57
|
+
export { ColumnsConfig as ColumnsServerConfig } from './layout/Columns.server.js';
|
|
56
58
|
export { SectionConfig as SectionServerConfig } from './layout/Section.server.js';
|
|
57
59
|
export { SpacerConfig as SpacerServerConfig } from './layout/Spacer.server.js';
|
|
58
60
|
export { TemplateServerConfig } from './layout/Template.server.js';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Columns Component - Puck Configuration (editor)
|
|
3
|
+
*
|
|
4
|
+
* Unlike Grid (a single CSS-grid slot where children auto-place into cells),
|
|
5
|
+
* Columns exposes one independent drop zone PER column — components can be
|
|
6
|
+
* dropped into a specific column. Puck slots are static named fields, so a fixed
|
|
7
|
+
* maximum set of column slots (column1..columnN) is declared and only the first
|
|
8
|
+
* `count` are rendered.
|
|
9
|
+
*
|
|
10
|
+
* Responsive: stacks vertically on mobile, switches to a CSS grid at md+ (≥768px).
|
|
11
|
+
*
|
|
12
|
+
* Width distribution supports equal columns or fr ratios (e.g. 2:1, 1:2, 1:1:2)
|
|
13
|
+
* for asymmetric layouts.
|
|
14
|
+
*/
|
|
15
|
+
import type { ComponentConfig } from '@puckeditor/core';
|
|
16
|
+
import { type PaddingValue, type BorderValue, type DimensionsValue, type BackgroundValue, type AnimationValue, type ResponsiveValue, type VisibilityValue } from '../../fields/shared.js';
|
|
17
|
+
export declare const MAX_COLUMNS = 4;
|
|
18
|
+
export type ColumnCount = 2 | 3 | 4;
|
|
19
|
+
export interface ColumnsProps {
|
|
20
|
+
column1: unknown;
|
|
21
|
+
column2: unknown;
|
|
22
|
+
column3: unknown;
|
|
23
|
+
column4: unknown;
|
|
24
|
+
count: ColumnCount;
|
|
25
|
+
distribution: string;
|
|
26
|
+
gap: number;
|
|
27
|
+
background: BackgroundValue | null;
|
|
28
|
+
customPadding: ResponsiveValue<PaddingValue> | PaddingValue | null;
|
|
29
|
+
dimensions: ResponsiveValue<DimensionsValue> | DimensionsValue | null;
|
|
30
|
+
border: BorderValue | null;
|
|
31
|
+
margin: ResponsiveValue<PaddingValue> | PaddingValue | null;
|
|
32
|
+
animation: AnimationValue | null;
|
|
33
|
+
visibility: VisibilityValue | null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve `grid-template-columns` from the column count and distribution preset.
|
|
37
|
+
* Falls back to equal widths when the ratio's segment count doesn't match.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveColumnsTemplate(count: number, distribution: string): string;
|
|
40
|
+
export declare const ColumnsConfig: ComponentConfig;
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Columns Component - Puck Configuration (editor)
|
|
4
|
+
*
|
|
5
|
+
* Unlike Grid (a single CSS-grid slot where children auto-place into cells),
|
|
6
|
+
* Columns exposes one independent drop zone PER column — components can be
|
|
7
|
+
* dropped into a specific column. Puck slots are static named fields, so a fixed
|
|
8
|
+
* maximum set of column slots (column1..columnN) is declared and only the first
|
|
9
|
+
* `count` are rendered.
|
|
10
|
+
*
|
|
11
|
+
* Responsive: stacks vertically on mobile, switches to a CSS grid at md+ (≥768px).
|
|
12
|
+
*
|
|
13
|
+
* Width distribution supports equal columns or fr ratios (e.g. 2:1, 1:2, 1:1:2)
|
|
14
|
+
* for asymmetric layouts.
|
|
15
|
+
*/ import { useId } from 'react';
|
|
16
|
+
import { cn, dimensionsValueToCSS, marginValueToCSS, paddingValueToCSS, borderValueToCSS, backgroundValueToCSS, responsiveValueToCSS, visibilityValueToCSS } from '../../fields/shared.js';
|
|
17
|
+
import { AnimatedWrapper } from '../AnimatedWrapper.js';
|
|
18
|
+
import { createPaddingField } from '../../fields/PaddingField.js';
|
|
19
|
+
import { createBorderField } from '../../fields/BorderField.js';
|
|
20
|
+
import { createDimensionsField } from '../../fields/DimensionsField.js';
|
|
21
|
+
import { createMarginField } from '../../fields/MarginField.js';
|
|
22
|
+
import { createResetField } from '../../fields/ResetField.js';
|
|
23
|
+
import { createBackgroundField } from '../../fields/BackgroundField.js';
|
|
24
|
+
import { createAnimationField } from '../../fields/AnimationField.js';
|
|
25
|
+
import { createResponsiveField } from '../../fields/ResponsiveField.js';
|
|
26
|
+
import { createResponsiveVisibilityField } from '../../fields/ResponsiveVisibilityField.js';
|
|
27
|
+
export const MAX_COLUMNS = 4;
|
|
28
|
+
const DEFAULT_PADDING = {
|
|
29
|
+
top: 0,
|
|
30
|
+
right: 0,
|
|
31
|
+
bottom: 0,
|
|
32
|
+
left: 0,
|
|
33
|
+
unit: 'px',
|
|
34
|
+
linked: true
|
|
35
|
+
};
|
|
36
|
+
const DEFAULT_DIMENSIONS = {
|
|
37
|
+
mode: 'full',
|
|
38
|
+
alignment: 'center',
|
|
39
|
+
maxWidth: {
|
|
40
|
+
value: 100,
|
|
41
|
+
unit: '%',
|
|
42
|
+
enabled: true
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const defaultProps = {
|
|
46
|
+
column1: [],
|
|
47
|
+
column2: [],
|
|
48
|
+
column3: [],
|
|
49
|
+
column4: [],
|
|
50
|
+
count: 2,
|
|
51
|
+
distribution: 'equal',
|
|
52
|
+
gap: 24,
|
|
53
|
+
background: null,
|
|
54
|
+
customPadding: null,
|
|
55
|
+
dimensions: null,
|
|
56
|
+
border: null,
|
|
57
|
+
margin: null,
|
|
58
|
+
animation: null,
|
|
59
|
+
visibility: null
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Resolve `grid-template-columns` from the column count and distribution preset.
|
|
63
|
+
* Falls back to equal widths when the ratio's segment count doesn't match.
|
|
64
|
+
*/ export function resolveColumnsTemplate(count, distribution) {
|
|
65
|
+
const safeCount = Math.min(Math.max(Math.round(count) || 2, 1), MAX_COLUMNS);
|
|
66
|
+
if (distribution && distribution !== 'equal') {
|
|
67
|
+
const parts = distribution.split(':').map((p)=>p.trim());
|
|
68
|
+
if (parts.length === safeCount && parts.every((p)=>/^\d+(\.\d+)?$/.test(p))) {
|
|
69
|
+
return parts.map((p)=>`${p}fr`).join(' ');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return `repeat(${safeCount}, minmax(0, 1fr))`;
|
|
73
|
+
}
|
|
74
|
+
export const ColumnsConfig = {
|
|
75
|
+
label: 'Columns',
|
|
76
|
+
fields: {
|
|
77
|
+
_reset: createResetField({
|
|
78
|
+
defaultProps
|
|
79
|
+
}),
|
|
80
|
+
// One independent drop zone per column. Disallow Section (full-width) inside
|
|
81
|
+
// a column, matching Grid — it would break the column layout.
|
|
82
|
+
column1: {
|
|
83
|
+
type: 'slot',
|
|
84
|
+
disallow: [
|
|
85
|
+
'Section'
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
column2: {
|
|
89
|
+
type: 'slot',
|
|
90
|
+
disallow: [
|
|
91
|
+
'Section'
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
column3: {
|
|
95
|
+
type: 'slot',
|
|
96
|
+
disallow: [
|
|
97
|
+
'Section'
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
column4: {
|
|
101
|
+
type: 'slot',
|
|
102
|
+
disallow: [
|
|
103
|
+
'Section'
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
// Responsive visibility control
|
|
107
|
+
visibility: createResponsiveVisibilityField({
|
|
108
|
+
label: 'Visibility'
|
|
109
|
+
}),
|
|
110
|
+
count: {
|
|
111
|
+
type: 'select',
|
|
112
|
+
label: 'Number of Columns',
|
|
113
|
+
options: [
|
|
114
|
+
{
|
|
115
|
+
label: '2 Columns',
|
|
116
|
+
value: 2
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
label: '3 Columns',
|
|
120
|
+
value: 3
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
label: '4 Columns',
|
|
124
|
+
value: 4
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
distribution: {
|
|
129
|
+
type: 'select',
|
|
130
|
+
label: 'Column Widths',
|
|
131
|
+
options: [
|
|
132
|
+
{
|
|
133
|
+
label: 'Equal',
|
|
134
|
+
value: 'equal'
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
label: '2 : 1 (wide left)',
|
|
138
|
+
value: '2:1'
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
label: '1 : 2 (wide right)',
|
|
142
|
+
value: '1:2'
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
label: '3 : 1',
|
|
146
|
+
value: '3:1'
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
label: '1 : 3',
|
|
150
|
+
value: '1:3'
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
label: '2 : 1 : 1',
|
|
154
|
+
value: '2:1:1'
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
label: '1 : 2 : 1 (wide center)',
|
|
158
|
+
value: '1:2:1'
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
label: '1 : 1 : 2',
|
|
162
|
+
value: '1:1:2'
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
},
|
|
166
|
+
gap: {
|
|
167
|
+
type: 'number',
|
|
168
|
+
label: 'Gap (px)',
|
|
169
|
+
min: 0
|
|
170
|
+
},
|
|
171
|
+
background: createBackgroundField({
|
|
172
|
+
label: 'Background'
|
|
173
|
+
}),
|
|
174
|
+
border: createBorderField({
|
|
175
|
+
label: 'Border'
|
|
176
|
+
}),
|
|
177
|
+
dimensions: createResponsiveField({
|
|
178
|
+
label: 'Dimensions (Responsive)',
|
|
179
|
+
innerField: (config)=>createDimensionsField(config),
|
|
180
|
+
defaultValue: DEFAULT_DIMENSIONS
|
|
181
|
+
}),
|
|
182
|
+
animation: createAnimationField({
|
|
183
|
+
label: 'Animation'
|
|
184
|
+
}),
|
|
185
|
+
margin: createResponsiveField({
|
|
186
|
+
label: 'Margin (Responsive)',
|
|
187
|
+
innerField: (config)=>createMarginField(config),
|
|
188
|
+
defaultValue: DEFAULT_PADDING
|
|
189
|
+
}),
|
|
190
|
+
customPadding: createResponsiveField({
|
|
191
|
+
label: 'Padding (Responsive)',
|
|
192
|
+
innerField: (config)=>createPaddingField(config),
|
|
193
|
+
defaultValue: DEFAULT_PADDING
|
|
194
|
+
})
|
|
195
|
+
},
|
|
196
|
+
defaultProps,
|
|
197
|
+
render: ({ column1: Column1, column2: Column2, column3: Column3, column4: Column4, count = 2, distribution = 'equal', gap, background, customPadding, dimensions, border, margin, animation, visibility })=>{
|
|
198
|
+
const safeCount = Math.min(Math.max(Math.round(count) || 2, 1), MAX_COLUMNS);
|
|
199
|
+
// Generate unique IDs for CSS targeting
|
|
200
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
201
|
+
const uniqueId = useId().replace(/:/g, '');
|
|
202
|
+
const wrapperClass = `puck-columns-${uniqueId}`;
|
|
203
|
+
const contentClass = `puck-columns-content-${uniqueId}`;
|
|
204
|
+
const mediaQueries = [];
|
|
205
|
+
const backgroundStyles = backgroundValueToCSS(background);
|
|
206
|
+
const wrapperStyles = {
|
|
207
|
+
...backgroundStyles
|
|
208
|
+
};
|
|
209
|
+
const paddingResult = responsiveValueToCSS(customPadding, (v)=>({
|
|
210
|
+
padding: paddingValueToCSS(v)
|
|
211
|
+
}), wrapperClass);
|
|
212
|
+
Object.assign(wrapperStyles, paddingResult.baseStyles);
|
|
213
|
+
if (paddingResult.mediaQueryCSS) mediaQueries.push(paddingResult.mediaQueryCSS);
|
|
214
|
+
const borderStyles = borderValueToCSS(border);
|
|
215
|
+
if (borderStyles) Object.assign(wrapperStyles, borderStyles);
|
|
216
|
+
const marginResult = responsiveValueToCSS(margin, (v)=>({
|
|
217
|
+
margin: marginValueToCSS(v)
|
|
218
|
+
}), wrapperClass);
|
|
219
|
+
Object.assign(wrapperStyles, marginResult.baseStyles);
|
|
220
|
+
if (marginResult.mediaQueryCSS) mediaQueries.push(marginResult.mediaQueryCSS);
|
|
221
|
+
const dimensionsResult = responsiveValueToCSS(dimensions, dimensionsValueToCSS, contentClass);
|
|
222
|
+
const visibilityCSS = visibilityValueToCSS(visibility, wrapperClass);
|
|
223
|
+
if (visibilityCSS) mediaQueries.push(visibilityCSS);
|
|
224
|
+
const contentClasses = cn('flex flex-col w-full', 'md:grid', contentClass);
|
|
225
|
+
const contentStyles = {
|
|
226
|
+
gap,
|
|
227
|
+
...dimensionsResult.baseStyles
|
|
228
|
+
};
|
|
229
|
+
if (dimensionsResult.mediaQueryCSS) mediaQueries.push(dimensionsResult.mediaQueryCSS);
|
|
230
|
+
const colsTemplate = resolveColumnsTemplate(safeCount, distribution);
|
|
231
|
+
const gridStyles = {
|
|
232
|
+
...contentStyles,
|
|
233
|
+
'--cols-template': colsTemplate
|
|
234
|
+
};
|
|
235
|
+
const slots = [
|
|
236
|
+
Column1,
|
|
237
|
+
Column2,
|
|
238
|
+
Column3,
|
|
239
|
+
Column4
|
|
240
|
+
];
|
|
241
|
+
const allMediaQueryCSS = mediaQueries.join('\n');
|
|
242
|
+
return /*#__PURE__*/ _jsxs(AnimatedWrapper, {
|
|
243
|
+
animation: animation,
|
|
244
|
+
children: [
|
|
245
|
+
allMediaQueryCSS && /*#__PURE__*/ _jsx("style", {
|
|
246
|
+
children: allMediaQueryCSS
|
|
247
|
+
}),
|
|
248
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
249
|
+
className: wrapperClass,
|
|
250
|
+
style: wrapperStyles,
|
|
251
|
+
children: [
|
|
252
|
+
/*#__PURE__*/ _jsx("div", {
|
|
253
|
+
className: contentClasses,
|
|
254
|
+
style: gridStyles,
|
|
255
|
+
children: slots.slice(0, safeCount).map((Slot, i)=>{
|
|
256
|
+
const ColumnSlot = Slot;
|
|
257
|
+
return /*#__PURE__*/ _jsx(ColumnSlot, {}, i);
|
|
258
|
+
})
|
|
259
|
+
}),
|
|
260
|
+
/*#__PURE__*/ _jsx("style", {
|
|
261
|
+
children: `
|
|
262
|
+
@media (min-width: 768px) {
|
|
263
|
+
.${contentClass} {
|
|
264
|
+
grid-template-columns: var(--cols-template);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
`
|
|
268
|
+
})
|
|
269
|
+
]
|
|
270
|
+
})
|
|
271
|
+
]
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Columns Component - Server-safe Puck Configuration
|
|
3
|
+
*
|
|
4
|
+
* Unlike Grid (a single slot styled as CSS grid, where children auto-place into
|
|
5
|
+
* cells), Columns exposes one independent drop zone PER column. Each column is
|
|
6
|
+
* its own slot, so components can be targeted into a specific column.
|
|
7
|
+
*
|
|
8
|
+
* Puck slots are static named fields, so we declare a fixed maximum set of
|
|
9
|
+
* column slots (column1..columnN) and render only the first `count`.
|
|
10
|
+
*
|
|
11
|
+
* Responsive: stacks vertically on mobile (flex column), switches to a CSS grid
|
|
12
|
+
* at md+ (≥768px), matching the Grid component's behavior.
|
|
13
|
+
*
|
|
14
|
+
* This is the server-safe variant (slots only, no editor fields). For the full
|
|
15
|
+
* editor version with fields, use Columns.tsx
|
|
16
|
+
*/
|
|
17
|
+
import type { ComponentConfig } from '@puckeditor/core';
|
|
18
|
+
import { type PaddingValue, type BorderValue, type DimensionsValue, type BackgroundValue, type AnimationValue, type ResponsiveValue, type VisibilityValue } from '../../fields/shared.js';
|
|
19
|
+
/** Maximum number of column slots the component declares. */
|
|
20
|
+
export declare const MAX_COLUMNS = 4;
|
|
21
|
+
export type ColumnCount = 2 | 3 | 4;
|
|
22
|
+
export interface ColumnsProps {
|
|
23
|
+
column1: unknown;
|
|
24
|
+
column2: unknown;
|
|
25
|
+
column3: unknown;
|
|
26
|
+
column4: unknown;
|
|
27
|
+
count: ColumnCount;
|
|
28
|
+
/**
|
|
29
|
+
* Column width distribution. 'equal' splits evenly; otherwise a colon-separated
|
|
30
|
+
* fr ratio (e.g. '2:1', '1:2', '1:1:2'). Ratios whose segment count doesn't
|
|
31
|
+
* match `count` fall back to equal.
|
|
32
|
+
*/
|
|
33
|
+
distribution: string;
|
|
34
|
+
gap: number;
|
|
35
|
+
background: BackgroundValue | null;
|
|
36
|
+
customPadding: ResponsiveValue<PaddingValue> | PaddingValue | null;
|
|
37
|
+
dimensions: ResponsiveValue<DimensionsValue> | DimensionsValue | null;
|
|
38
|
+
border: BorderValue | null;
|
|
39
|
+
margin: ResponsiveValue<PaddingValue> | PaddingValue | null;
|
|
40
|
+
animation: AnimationValue | null;
|
|
41
|
+
visibility: VisibilityValue | null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolve the `grid-template-columns` value from the column count and the
|
|
45
|
+
* distribution preset. Falls back to equal widths when the ratio doesn't match.
|
|
46
|
+
*/
|
|
47
|
+
export declare function resolveColumnsTemplate(count: number, distribution: string): string;
|
|
48
|
+
export declare const ColumnsConfig: ComponentConfig;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Columns Component - Server-safe Puck Configuration
|
|
3
|
+
*
|
|
4
|
+
* Unlike Grid (a single slot styled as CSS grid, where children auto-place into
|
|
5
|
+
* cells), Columns exposes one independent drop zone PER column. Each column is
|
|
6
|
+
* its own slot, so components can be targeted into a specific column.
|
|
7
|
+
*
|
|
8
|
+
* Puck slots are static named fields, so we declare a fixed maximum set of
|
|
9
|
+
* column slots (column1..columnN) and render only the first `count`.
|
|
10
|
+
*
|
|
11
|
+
* Responsive: stacks vertically on mobile (flex column), switches to a CSS grid
|
|
12
|
+
* at md+ (≥768px), matching the Grid component's behavior.
|
|
13
|
+
*
|
|
14
|
+
* This is the server-safe variant (slots only, no editor fields). For the full
|
|
15
|
+
* editor version with fields, use Columns.tsx
|
|
16
|
+
*/ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
17
|
+
import { cn, dimensionsValueToCSS, marginValueToCSS, paddingValueToCSS, borderValueToCSS, backgroundValueToCSS, responsiveValueToCSS, visibilityValueToCSS } from '../../fields/shared.js';
|
|
18
|
+
import { AnimatedWrapper } from '../AnimatedWrapper.js';
|
|
19
|
+
/** Maximum number of column slots the component declares. */ export const MAX_COLUMNS = 4;
|
|
20
|
+
const defaultProps = {
|
|
21
|
+
column1: [],
|
|
22
|
+
column2: [],
|
|
23
|
+
column3: [],
|
|
24
|
+
column4: [],
|
|
25
|
+
count: 2,
|
|
26
|
+
distribution: 'equal',
|
|
27
|
+
gap: 24,
|
|
28
|
+
background: null,
|
|
29
|
+
customPadding: null,
|
|
30
|
+
dimensions: null,
|
|
31
|
+
border: null,
|
|
32
|
+
margin: null,
|
|
33
|
+
animation: null,
|
|
34
|
+
visibility: null
|
|
35
|
+
};
|
|
36
|
+
// Simple ID generator for server-side rendering
|
|
37
|
+
let idCounter = 0;
|
|
38
|
+
function generateUniqueId() {
|
|
39
|
+
return `c${(++idCounter).toString(36)}${Math.random().toString(36).slice(2, 6)}`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the `grid-template-columns` value from the column count and the
|
|
43
|
+
* distribution preset. Falls back to equal widths when the ratio doesn't match.
|
|
44
|
+
*/ export function resolveColumnsTemplate(count, distribution) {
|
|
45
|
+
const safeCount = Math.min(Math.max(Math.round(count) || 2, 1), MAX_COLUMNS);
|
|
46
|
+
if (distribution && distribution !== 'equal') {
|
|
47
|
+
const parts = distribution.split(':').map((p)=>p.trim());
|
|
48
|
+
if (parts.length === safeCount && parts.every((p)=>/^\d+(\.\d+)?$/.test(p))) {
|
|
49
|
+
return parts.map((p)=>`${p}fr`).join(' ');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return `repeat(${safeCount}, minmax(0, 1fr))`;
|
|
53
|
+
}
|
|
54
|
+
export const ColumnsConfig = {
|
|
55
|
+
label: 'Columns',
|
|
56
|
+
fields: {
|
|
57
|
+
column1: {
|
|
58
|
+
type: 'slot'
|
|
59
|
+
},
|
|
60
|
+
column2: {
|
|
61
|
+
type: 'slot'
|
|
62
|
+
},
|
|
63
|
+
column3: {
|
|
64
|
+
type: 'slot'
|
|
65
|
+
},
|
|
66
|
+
column4: {
|
|
67
|
+
type: 'slot'
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
defaultProps,
|
|
71
|
+
render: ({ column1: Column1, column2: Column2, column3: Column3, column4: Column4, count = 2, distribution = 'equal', gap, background, customPadding, dimensions, border, margin, animation, visibility })=>{
|
|
72
|
+
const safeCount = Math.min(Math.max(Math.round(count) || 2, 1), MAX_COLUMNS);
|
|
73
|
+
// Generate unique IDs for CSS targeting (server-safe)
|
|
74
|
+
const uniqueId = generateUniqueId();
|
|
75
|
+
const wrapperClass = `puck-columns-${uniqueId}`;
|
|
76
|
+
const contentClass = `puck-columns-content-${uniqueId}`;
|
|
77
|
+
// Collect all media query CSS
|
|
78
|
+
const mediaQueries = [];
|
|
79
|
+
// Generate styles from BackgroundValue
|
|
80
|
+
const backgroundStyles = backgroundValueToCSS(background);
|
|
81
|
+
const wrapperStyles = {
|
|
82
|
+
...backgroundStyles
|
|
83
|
+
};
|
|
84
|
+
// Padding (responsive)
|
|
85
|
+
const paddingResult = responsiveValueToCSS(customPadding, (v)=>({
|
|
86
|
+
padding: paddingValueToCSS(v)
|
|
87
|
+
}), wrapperClass);
|
|
88
|
+
Object.assign(wrapperStyles, paddingResult.baseStyles);
|
|
89
|
+
if (paddingResult.mediaQueryCSS) mediaQueries.push(paddingResult.mediaQueryCSS);
|
|
90
|
+
// Border
|
|
91
|
+
const borderStyles = borderValueToCSS(border);
|
|
92
|
+
if (borderStyles) Object.assign(wrapperStyles, borderStyles);
|
|
93
|
+
// Margin (responsive)
|
|
94
|
+
const marginResult = responsiveValueToCSS(margin, (v)=>({
|
|
95
|
+
margin: marginValueToCSS(v)
|
|
96
|
+
}), wrapperClass);
|
|
97
|
+
Object.assign(wrapperStyles, marginResult.baseStyles);
|
|
98
|
+
if (marginResult.mediaQueryCSS) mediaQueries.push(marginResult.mediaQueryCSS);
|
|
99
|
+
// Dimensions (responsive)
|
|
100
|
+
const dimensionsResult = responsiveValueToCSS(dimensions, dimensionsValueToCSS, contentClass);
|
|
101
|
+
// Visibility media queries
|
|
102
|
+
const visibilityCSS = visibilityValueToCSS(visibility, wrapperClass);
|
|
103
|
+
if (visibilityCSS) mediaQueries.push(visibilityCSS);
|
|
104
|
+
// Tailwind: flex column on mobile, grid on md+
|
|
105
|
+
const contentClasses = cn('flex flex-col w-full', 'md:grid', contentClass);
|
|
106
|
+
const contentStyles = {
|
|
107
|
+
gap,
|
|
108
|
+
...dimensionsResult.baseStyles
|
|
109
|
+
};
|
|
110
|
+
if (dimensionsResult.mediaQueryCSS) mediaQueries.push(dimensionsResult.mediaQueryCSS);
|
|
111
|
+
// grid-template-columns is dynamic, so drive it from a CSS var and scope the
|
|
112
|
+
// media-query rule to this instance's content class (no cross-instance leak).
|
|
113
|
+
const colsTemplate = resolveColumnsTemplate(safeCount, distribution);
|
|
114
|
+
const gridStyles = {
|
|
115
|
+
...contentStyles,
|
|
116
|
+
'--cols-template': colsTemplate
|
|
117
|
+
};
|
|
118
|
+
const slots = [
|
|
119
|
+
Column1,
|
|
120
|
+
Column2,
|
|
121
|
+
Column3,
|
|
122
|
+
Column4
|
|
123
|
+
];
|
|
124
|
+
const allMediaQueryCSS = mediaQueries.join('\n');
|
|
125
|
+
return /*#__PURE__*/ _jsxs(AnimatedWrapper, {
|
|
126
|
+
animation: animation,
|
|
127
|
+
children: [
|
|
128
|
+
allMediaQueryCSS && /*#__PURE__*/ _jsx("style", {
|
|
129
|
+
children: allMediaQueryCSS
|
|
130
|
+
}),
|
|
131
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
132
|
+
className: wrapperClass,
|
|
133
|
+
style: wrapperStyles,
|
|
134
|
+
children: [
|
|
135
|
+
/*#__PURE__*/ _jsx("div", {
|
|
136
|
+
className: contentClasses,
|
|
137
|
+
style: gridStyles,
|
|
138
|
+
children: slots.slice(0, safeCount).map((Slot, i)=>{
|
|
139
|
+
const ColumnSlot = Slot;
|
|
140
|
+
return /*#__PURE__*/ _jsx(ColumnSlot, {}, i);
|
|
141
|
+
})
|
|
142
|
+
}),
|
|
143
|
+
/*#__PURE__*/ _jsx("style", {
|
|
144
|
+
children: `
|
|
145
|
+
@media (min-width: 768px) {
|
|
146
|
+
.${contentClass} {
|
|
147
|
+
grid-template-columns: var(--cols-template);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
`
|
|
151
|
+
})
|
|
152
|
+
]
|
|
153
|
+
})
|
|
154
|
+
]
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
};
|
|
@@ -94,6 +94,7 @@ export declare const editorConfig: {
|
|
|
94
94
|
Container: ComponentConfig<any>;
|
|
95
95
|
Flex: ComponentConfig<any>;
|
|
96
96
|
Grid: ComponentConfig<any>;
|
|
97
|
+
Columns: ComponentConfig<any>;
|
|
97
98
|
Section: ComponentConfig<any>;
|
|
98
99
|
Spacer: ComponentConfig<any>;
|
|
99
100
|
Template: ComponentConfig<any>;
|
|
@@ -6,6 +6,7 @@ import { lockedSlugField, lockedHomepageField } from '../fields/LockedField.js';
|
|
|
6
6
|
import { ContainerConfig } from '../components/layout/Container.js';
|
|
7
7
|
import { FlexConfig } from '../components/layout/Flex.js';
|
|
8
8
|
import { GridConfig } from '../components/layout/Grid.js';
|
|
9
|
+
import { ColumnsConfig } from '../components/layout/Columns.js';
|
|
9
10
|
import { SectionConfig } from '../components/layout/Section.js';
|
|
10
11
|
import { SpacerConfig } from '../components/layout/Spacer.js';
|
|
11
12
|
import { TemplateConfig } from '../components/layout/Template.js';
|
|
@@ -146,6 +147,7 @@ export const editorConfig = {
|
|
|
146
147
|
'Container',
|
|
147
148
|
'Flex',
|
|
148
149
|
'Grid',
|
|
150
|
+
'Columns',
|
|
149
151
|
'Section',
|
|
150
152
|
'Spacer',
|
|
151
153
|
'Template'
|
|
@@ -181,6 +183,7 @@ export const editorConfig = {
|
|
|
181
183
|
Container: ContainerConfig,
|
|
182
184
|
Flex: FlexConfig,
|
|
183
185
|
Grid: GridConfig,
|
|
186
|
+
Columns: ColumnsConfig,
|
|
184
187
|
Section: SectionConfig,
|
|
185
188
|
Spacer: SpacerConfig,
|
|
186
189
|
Template: TemplateConfig,
|
package/dist/config/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { DEFAULT_LAYOUTS, layoutsToOptions } from '../layouts/index.js';
|
|
|
9
9
|
import { ContainerConfig } from '../components/layout/Container.server.js';
|
|
10
10
|
import { FlexConfig } from '../components/layout/Flex.server.js';
|
|
11
11
|
import { GridConfig } from '../components/layout/Grid.server.js';
|
|
12
|
+
import { ColumnsConfig } from '../components/layout/Columns.server.js';
|
|
12
13
|
import { SectionConfig } from '../components/layout/Section.server.js';
|
|
13
14
|
import { SpacerConfig } from '../components/layout/Spacer.server.js';
|
|
14
15
|
import { TemplateServerConfig } from '../components/layout/Template.server.js';
|
|
@@ -75,6 +76,7 @@ import { AccordionConfig } from '../components/interactive/Accordion.server.js';
|
|
|
75
76
|
'Container',
|
|
76
77
|
'Flex',
|
|
77
78
|
'Grid',
|
|
79
|
+
'Columns',
|
|
78
80
|
'Section',
|
|
79
81
|
'Spacer',
|
|
80
82
|
'Template'
|
|
@@ -110,6 +112,7 @@ import { AccordionConfig } from '../components/interactive/Accordion.server.js';
|
|
|
110
112
|
Container: ContainerConfig,
|
|
111
113
|
Flex: FlexConfig,
|
|
112
114
|
Grid: GridConfig,
|
|
115
|
+
Columns: ColumnsConfig,
|
|
113
116
|
Section: SectionConfig,
|
|
114
117
|
Spacer: SpacerConfig,
|
|
115
118
|
Template: TemplateServerConfig,
|
package/dist/config/presets.d.ts
CHANGED
|
@@ -244,6 +244,7 @@ export declare const fullConfig: {
|
|
|
244
244
|
Container: ComponentConfig<any>;
|
|
245
245
|
Flex: ComponentConfig<any>;
|
|
246
246
|
Grid: ComponentConfig<any>;
|
|
247
|
+
Columns: ComponentConfig<any>;
|
|
247
248
|
Section: ComponentConfig<any>;
|
|
248
249
|
Spacer: ComponentConfig<any>;
|
|
249
250
|
Template: ComponentConfig<any>;
|
package/dist/config/presets.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
// Import component configs
|
|
4
|
-
import { ContainerConfig, FlexConfig, GridConfig, SectionConfig, SpacerConfig, TemplateConfig, HeadingConfig, TextConfig, RichTextEditorConfig, ImageConfig, ButtonConfig, CardConfig, DividerConfig, AccordionConfig } from '../components/exports.js';
|
|
4
|
+
import { ContainerConfig, FlexConfig, GridConfig, ColumnsConfig, SectionConfig, SpacerConfig, TemplateConfig, HeadingConfig, TextConfig, RichTextEditorConfig, ImageConfig, ButtonConfig, CardConfig, DividerConfig, AccordionConfig } from '../components/exports.js';
|
|
5
5
|
// Import field factories for root config
|
|
6
6
|
import { createBackgroundField } from '../fields/BackgroundField.js';
|
|
7
7
|
import { lockedSlugField, lockedHomepageField } from '../fields/LockedField.js';
|
|
@@ -278,6 +278,7 @@ import { createSlugPreviewField } from '../fields/SlugPreviewField.js';
|
|
|
278
278
|
'Container',
|
|
279
279
|
'Flex',
|
|
280
280
|
'Grid',
|
|
281
|
+
'Columns',
|
|
281
282
|
'Section',
|
|
282
283
|
'Spacer',
|
|
283
284
|
'Template'
|
|
@@ -313,6 +314,7 @@ import { createSlugPreviewField } from '../fields/SlugPreviewField.js';
|
|
|
313
314
|
Container: ContainerConfig,
|
|
314
315
|
Flex: FlexConfig,
|
|
315
316
|
Grid: GridConfig,
|
|
317
|
+
Columns: ColumnsConfig,
|
|
316
318
|
Section: SectionConfig,
|
|
317
319
|
Spacer: SpacerConfig,
|
|
318
320
|
Template: TemplateConfig,
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.6.
|
|
1
|
+
export declare const VERSION = "0.6.27";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by scripts/generate-version.js - do not edit manually
|
|
2
|
-
export const VERSION = '0.6.
|
|
2
|
+
export const VERSION = '0.6.27';
|