@delmaredigital/payload-puck 0.6.26 → 0.6.28
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 +275 -0
- package/dist/components/layout/Columns.server.d.ts +48 -0
- package/dist/components/layout/Columns.server.js +156 -0
- package/dist/components/layout/Grid.js +11 -12
- package/dist/components/layout/Grid.server.js +11 -12
- 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,275 @@
|
|
|
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 { 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
|
+
if (dimensionsResult.mediaQueryCSS) mediaQueries.push(dimensionsResult.mediaQueryCSS);
|
|
225
|
+
// Self-contained CSS grid — does NOT depend on the consumer's Tailwind
|
|
226
|
+
// generating utility classes. Single column on mobile (stacked); switches to
|
|
227
|
+
// the multi-column template at >=768px via the scoped media query below.
|
|
228
|
+
const colsTemplate = resolveColumnsTemplate(safeCount, distribution);
|
|
229
|
+
const gridStyles = {
|
|
230
|
+
display: 'grid',
|
|
231
|
+
gridTemplateColumns: '1fr',
|
|
232
|
+
gap,
|
|
233
|
+
...dimensionsResult.baseStyles,
|
|
234
|
+
'--cols-template': colsTemplate
|
|
235
|
+
};
|
|
236
|
+
const slots = [
|
|
237
|
+
Column1,
|
|
238
|
+
Column2,
|
|
239
|
+
Column3,
|
|
240
|
+
Column4
|
|
241
|
+
];
|
|
242
|
+
const allMediaQueryCSS = mediaQueries.join('\n');
|
|
243
|
+
return /*#__PURE__*/ _jsxs(AnimatedWrapper, {
|
|
244
|
+
animation: animation,
|
|
245
|
+
children: [
|
|
246
|
+
allMediaQueryCSS && /*#__PURE__*/ _jsx("style", {
|
|
247
|
+
children: allMediaQueryCSS
|
|
248
|
+
}),
|
|
249
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
250
|
+
className: wrapperClass,
|
|
251
|
+
style: wrapperStyles,
|
|
252
|
+
children: [
|
|
253
|
+
/*#__PURE__*/ _jsx("div", {
|
|
254
|
+
className: contentClass,
|
|
255
|
+
style: gridStyles,
|
|
256
|
+
children: slots.slice(0, safeCount).map((Slot, i)=>{
|
|
257
|
+
const ColumnSlot = Slot;
|
|
258
|
+
return /*#__PURE__*/ _jsx(ColumnSlot, {}, i);
|
|
259
|
+
})
|
|
260
|
+
}),
|
|
261
|
+
/*#__PURE__*/ _jsx("style", {
|
|
262
|
+
children: `
|
|
263
|
+
@media (min-width: 768px) {
|
|
264
|
+
.${contentClass} {
|
|
265
|
+
grid-template-columns: var(--cols-template);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
`
|
|
269
|
+
})
|
|
270
|
+
]
|
|
271
|
+
})
|
|
272
|
+
]
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
};
|
|
@@ -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,156 @@
|
|
|
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 { 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
|
+
if (dimensionsResult.mediaQueryCSS) mediaQueries.push(dimensionsResult.mediaQueryCSS);
|
|
105
|
+
// Self-contained CSS grid — does NOT depend on the consumer's Tailwind
|
|
106
|
+
// generating utility classes. Single column on mobile (stacked); switches to
|
|
107
|
+
// the multi-column template at >=768px via the scoped media query below.
|
|
108
|
+
// grid-template-columns is dynamic, so drive it from a CSS var.
|
|
109
|
+
const colsTemplate = resolveColumnsTemplate(safeCount, distribution);
|
|
110
|
+
const gridStyles = {
|
|
111
|
+
display: 'grid',
|
|
112
|
+
gridTemplateColumns: '1fr',
|
|
113
|
+
gap,
|
|
114
|
+
...dimensionsResult.baseStyles,
|
|
115
|
+
'--cols-template': colsTemplate
|
|
116
|
+
};
|
|
117
|
+
const slots = [
|
|
118
|
+
Column1,
|
|
119
|
+
Column2,
|
|
120
|
+
Column3,
|
|
121
|
+
Column4
|
|
122
|
+
];
|
|
123
|
+
const allMediaQueryCSS = mediaQueries.join('\n');
|
|
124
|
+
return /*#__PURE__*/ _jsxs(AnimatedWrapper, {
|
|
125
|
+
animation: animation,
|
|
126
|
+
children: [
|
|
127
|
+
allMediaQueryCSS && /*#__PURE__*/ _jsx("style", {
|
|
128
|
+
children: allMediaQueryCSS
|
|
129
|
+
}),
|
|
130
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
131
|
+
className: wrapperClass,
|
|
132
|
+
style: wrapperStyles,
|
|
133
|
+
children: [
|
|
134
|
+
/*#__PURE__*/ _jsx("div", {
|
|
135
|
+
className: contentClass,
|
|
136
|
+
style: gridStyles,
|
|
137
|
+
children: slots.slice(0, safeCount).map((Slot, i)=>{
|
|
138
|
+
const ColumnSlot = Slot;
|
|
139
|
+
return /*#__PURE__*/ _jsx(ColumnSlot, {}, i);
|
|
140
|
+
})
|
|
141
|
+
}),
|
|
142
|
+
/*#__PURE__*/ _jsx("style", {
|
|
143
|
+
children: `
|
|
144
|
+
@media (min-width: 768px) {
|
|
145
|
+
.${contentClass} {
|
|
146
|
+
grid-template-columns: var(--cols-template);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
`
|
|
150
|
+
})
|
|
151
|
+
]
|
|
152
|
+
})
|
|
153
|
+
]
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
};
|
|
@@ -14,7 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
14
14
|
* - gap: Different gap at different breakpoints
|
|
15
15
|
* - visibility: Show/hide at different breakpoints
|
|
16
16
|
*/ import { useId } from 'react';
|
|
17
|
-
import {
|
|
17
|
+
import { dimensionsValueToCSS, marginValueToCSS, paddingValueToCSS, borderValueToCSS, backgroundValueToCSS, responsiveValueToCSS, visibilityValueToCSS } from '../../fields/shared.js';
|
|
18
18
|
import { AnimatedWrapper } from '../AnimatedWrapper.js';
|
|
19
19
|
import { createPaddingField } from '../../fields/PaddingField.js';
|
|
20
20
|
import { createBorderField } from '../../fields/BorderField.js';
|
|
@@ -176,19 +176,18 @@ export const GridConfig = {
|
|
|
176
176
|
if (visibilityCSS) {
|
|
177
177
|
mediaQueries.push(visibilityCSS);
|
|
178
178
|
}
|
|
179
|
-
// Tailwind classes for responsive grid: flex column on mobile, grid on md+
|
|
180
|
-
const contentClasses = cn('flex flex-col w-full', 'md:grid', contentClass);
|
|
181
|
-
// Dynamic styles that need inline (user-controlled values: gap, columns)
|
|
182
|
-
const contentStyles = {
|
|
183
|
-
gap,
|
|
184
|
-
...dimensionsResult.baseStyles
|
|
185
|
-
};
|
|
186
179
|
if (dimensionsResult.mediaQueryCSS) {
|
|
187
180
|
mediaQueries.push(dimensionsResult.mediaQueryCSS);
|
|
188
181
|
}
|
|
189
|
-
//
|
|
182
|
+
// Self-contained CSS grid — does NOT depend on the consumer's Tailwind
|
|
183
|
+
// generating utility classes. Single column on mobile (children stacked);
|
|
184
|
+
// switches to the multi-column track at >=768px via the scoped media query.
|
|
185
|
+
// grid-template-columns is dynamic, so drive the column count from a CSS var.
|
|
190
186
|
const gridStyles = {
|
|
191
|
-
|
|
187
|
+
display: 'grid',
|
|
188
|
+
gridTemplateColumns: '1fr',
|
|
189
|
+
gap,
|
|
190
|
+
...dimensionsResult.baseStyles,
|
|
192
191
|
'--grid-cols': numColumns
|
|
193
192
|
};
|
|
194
193
|
// Combine all media queries
|
|
@@ -204,13 +203,13 @@ export const GridConfig = {
|
|
|
204
203
|
style: wrapperStyles,
|
|
205
204
|
children: [
|
|
206
205
|
/*#__PURE__*/ _jsx(Content, {
|
|
207
|
-
className:
|
|
206
|
+
className: contentClass,
|
|
208
207
|
style: gridStyles
|
|
209
208
|
}),
|
|
210
209
|
/*#__PURE__*/ _jsx("style", {
|
|
211
210
|
children: `
|
|
212
211
|
@media (min-width: 768px) {
|
|
213
|
-
|
|
212
|
+
.${contentClass} {
|
|
214
213
|
grid-template-columns: repeat(var(--grid-cols), 1fr);
|
|
215
214
|
}
|
|
216
215
|
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* - margin: Different margins at different breakpoints
|
|
15
15
|
* - visibility: Show/hide at different breakpoints
|
|
16
16
|
*/ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
17
|
-
import {
|
|
17
|
+
import { dimensionsValueToCSS, marginValueToCSS, paddingValueToCSS, borderValueToCSS, backgroundValueToCSS, responsiveValueToCSS, visibilityValueToCSS } from '../../fields/shared.js';
|
|
18
18
|
import { AnimatedWrapper } from '../AnimatedWrapper.js';
|
|
19
19
|
// Simple ID generator for server-side rendering
|
|
20
20
|
let idCounter = 0;
|
|
@@ -85,19 +85,18 @@ export const GridConfig = {
|
|
|
85
85
|
if (visibilityCSS) {
|
|
86
86
|
mediaQueries.push(visibilityCSS);
|
|
87
87
|
}
|
|
88
|
-
// Tailwind classes for responsive grid: flex column on mobile, grid on md+
|
|
89
|
-
const contentClasses = cn('flex flex-col w-full', 'md:grid', contentClass);
|
|
90
|
-
// Dynamic styles that need inline (user-controlled values: gap, columns)
|
|
91
|
-
const contentStyles = {
|
|
92
|
-
gap,
|
|
93
|
-
...dimensionsResult.baseStyles
|
|
94
|
-
};
|
|
95
88
|
if (dimensionsResult.mediaQueryCSS) {
|
|
96
89
|
mediaQueries.push(dimensionsResult.mediaQueryCSS);
|
|
97
90
|
}
|
|
98
|
-
//
|
|
91
|
+
// Self-contained CSS grid — does NOT depend on the consumer's Tailwind
|
|
92
|
+
// generating utility classes. Single column on mobile (children stacked);
|
|
93
|
+
// switches to the multi-column track at >=768px via the scoped media query.
|
|
94
|
+
// grid-template-columns is dynamic, so drive the column count from a CSS var.
|
|
99
95
|
const gridStyles = {
|
|
100
|
-
|
|
96
|
+
display: 'grid',
|
|
97
|
+
gridTemplateColumns: '1fr',
|
|
98
|
+
gap,
|
|
99
|
+
...dimensionsResult.baseStyles,
|
|
101
100
|
'--grid-cols': numColumns
|
|
102
101
|
};
|
|
103
102
|
// Combine all media queries
|
|
@@ -115,13 +114,13 @@ export const GridConfig = {
|
|
|
115
114
|
style: wrapperStyles,
|
|
116
115
|
children: [
|
|
117
116
|
/*#__PURE__*/ _jsx(ContentSlot, {
|
|
118
|
-
className:
|
|
117
|
+
className: contentClass,
|
|
119
118
|
style: gridStyles
|
|
120
119
|
}),
|
|
121
120
|
/*#__PURE__*/ _jsx("style", {
|
|
122
121
|
children: `
|
|
123
122
|
@media (min-width: 768px) {
|
|
124
|
-
|
|
123
|
+
.${contentClass} {
|
|
125
124
|
grid-template-columns: repeat(var(--grid-cols), 1fr);
|
|
126
125
|
}
|
|
127
126
|
}
|
|
@@ -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.28";
|
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.28';
|