@onexapis/cli 1.1.17 → 1.1.19
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/README.md +82 -16
- package/dist/cli.js +621 -294
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +618 -291
- package/dist/cli.mjs.map +1 -1
- package/dist/index.js +73 -278
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +73 -278
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/default/.env.example +1 -1
- package/templates/default/.mcp.json +8 -0
- package/templates/default/CLAUDE.md +941 -0
- package/templates/default/bundle-entry.ts +18 -0
- package/templates/default/index.ts +26 -0
- package/templates/default/package.json +34 -0
- package/templates/default/pages/about.ts +66 -0
- package/templates/default/pages/home.ts +93 -0
- package/templates/default/pages/showcase.ts +146 -0
- package/templates/default/sections/about/about-default.tsx +237 -0
- package/templates/default/sections/about/about.schema.ts +259 -0
- package/templates/default/sections/about/index.ts +15 -0
- package/templates/default/sections/cta/cta-default.tsx +180 -0
- package/templates/default/sections/cta/cta.schema.ts +210 -0
- package/templates/default/sections/cta/index.ts +11 -0
- package/templates/default/sections/features/features-default.tsx +154 -0
- package/templates/default/sections/features/features.schema.ts +330 -0
- package/templates/default/sections/features/index.ts +11 -0
- package/templates/default/sections/gallery/gallery-default.tsx +134 -0
- package/templates/default/sections/gallery/gallery.schema.ts +397 -0
- package/templates/default/sections/gallery/index.ts +11 -0
- package/templates/default/sections/hero/hero-default.tsx +212 -0
- package/templates/default/sections/hero/hero.schema.ts +273 -0
- package/templates/default/sections/hero/index.ts +15 -0
- package/templates/default/sections/stats/index.ts +11 -0
- package/templates/default/sections/stats/stats-default.tsx +103 -0
- package/templates/default/sections/stats/stats.schema.ts +266 -0
- package/templates/default/sections/testimonials/index.ts +11 -0
- package/templates/default/sections/testimonials/testimonials-default.tsx +130 -0
- package/templates/default/sections/testimonials/testimonials.schema.ts +371 -0
- package/templates/default/sections-registry.ts +32 -0
- package/templates/default/theme.config.ts +107 -0
- package/templates/default/theme.layout.ts +21 -0
- package/templates/default/tsconfig.json +16 -7
- package/templates/default/README.md.ejs +0 -129
- package/templates/default/esbuild.config.js +0 -81
- package/templates/default/package.json.ejs +0 -31
- package/templates/default/src/config.ts.ejs +0 -98
- package/templates/default/src/index.ts.ejs +0 -11
- package/templates/default/src/layout.ts +0 -23
- package/templates/default/src/manifest.ts.ejs +0 -47
- package/templates/default/src/pages/home.ts.ejs +0 -37
- package/templates/default/src/sections/footer/footer-default.tsx +0 -28
- package/templates/default/src/sections/footer/footer.schema.ts +0 -45
- package/templates/default/src/sections/footer/index.ts +0 -2
- package/templates/default/src/sections/header/header-default.tsx +0 -61
- package/templates/default/src/sections/header/header.schema.ts +0 -46
- package/templates/default/src/sections/header/index.ts +0 -2
- package/templates/default/src/sections/hero/hero-default.tsx +0 -52
- package/templates/default/src/sections/hero/hero.schema.ts +0 -52
- package/templates/default/src/sections/hero/index.ts +0 -2
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { SectionComponentProps } from "@onexapis/core/types";
|
|
4
|
+
import coreRenderers from "@onexapis/core/renderers";
|
|
5
|
+
import coreUtils from "@onexapis/core/utils";
|
|
6
|
+
|
|
7
|
+
const { ComponentRenderer, BlockRenderer } = coreRenderers;
|
|
8
|
+
const { toComponentInstance, getSectionValues, filterEnabledComponents } =
|
|
9
|
+
coreUtils;
|
|
10
|
+
|
|
11
|
+
export function FeaturesDefault({
|
|
12
|
+
section,
|
|
13
|
+
schema,
|
|
14
|
+
isEditing,
|
|
15
|
+
}: SectionComponentProps) {
|
|
16
|
+
const { settings } = getSectionValues(section, schema);
|
|
17
|
+
const { sectionTitle, sectionSubtitle, backgroundColor } = settings;
|
|
18
|
+
|
|
19
|
+
// Section-level components (title, subtitle)
|
|
20
|
+
const components = filterEnabledComponents(section.components || []);
|
|
21
|
+
const titleComp = components.find(
|
|
22
|
+
(c) => c.slot === "section-title" || c.id === "features-title"
|
|
23
|
+
);
|
|
24
|
+
const subtitleComp = components.find(
|
|
25
|
+
(c) => c.slot === "section-subtitle" || c.id === "features-subtitle"
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// Parent container block (optional — data may use container or flat structure)
|
|
29
|
+
const containerBlock = (section.blocks || []).find(
|
|
30
|
+
(b) => b.type === "features-container"
|
|
31
|
+
);
|
|
32
|
+
const containerSettings = containerBlock?.settings || {};
|
|
33
|
+
const columns = containerSettings.columns || settings.columns || "3";
|
|
34
|
+
const gap = containerSettings.gap || settings.gap || "lg";
|
|
35
|
+
|
|
36
|
+
// Support both: nested blocks inside container, or flat feature-item blocks directly on section
|
|
37
|
+
const rawBlocks = containerBlock?.blocks || section.blocks || [];
|
|
38
|
+
const featureBlocks = rawBlocks
|
|
39
|
+
.filter((b) => b.type === "feature-item" && b.enabled !== false)
|
|
40
|
+
.sort((a, b) => (a.order || 0) - (b.order || 0));
|
|
41
|
+
|
|
42
|
+
const colClass =
|
|
43
|
+
columns === "2"
|
|
44
|
+
? "md:grid-cols-2"
|
|
45
|
+
: columns === "4"
|
|
46
|
+
? "md:grid-cols-2 lg:grid-cols-4"
|
|
47
|
+
: "md:grid-cols-3";
|
|
48
|
+
|
|
49
|
+
const gapClass = gap === "sm" ? "gap-4" : gap === "md" ? "gap-6" : "gap-8";
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<section
|
|
53
|
+
className="py-16"
|
|
54
|
+
style={{ backgroundColor: String(backgroundColor || "#F9FAFB") }}
|
|
55
|
+
data-section="features"
|
|
56
|
+
data-section-id={section.id}
|
|
57
|
+
data-section-template="default"
|
|
58
|
+
data-section-name="Simple Features"
|
|
59
|
+
>
|
|
60
|
+
<div className="container mx-auto px-4 max-w-6xl">
|
|
61
|
+
{/* Section Header (section-level components) */}
|
|
62
|
+
<div className="text-center mb-12">
|
|
63
|
+
{titleComp ? (
|
|
64
|
+
<ComponentRenderer
|
|
65
|
+
key="section-title"
|
|
66
|
+
instance={toComponentInstance(titleComp)}
|
|
67
|
+
sectionId={section.id}
|
|
68
|
+
isEditing={isEditing}
|
|
69
|
+
/>
|
|
70
|
+
) : sectionTitle ? (
|
|
71
|
+
<h2
|
|
72
|
+
key="section-title-fallback"
|
|
73
|
+
className="text-3xl font-bold text-gray-900 mb-2"
|
|
74
|
+
>
|
|
75
|
+
{String(sectionTitle)}
|
|
76
|
+
</h2>
|
|
77
|
+
) : null}
|
|
78
|
+
|
|
79
|
+
{subtitleComp ? (
|
|
80
|
+
<ComponentRenderer
|
|
81
|
+
key="section-subtitle"
|
|
82
|
+
instance={toComponentInstance(subtitleComp)}
|
|
83
|
+
sectionId={section.id}
|
|
84
|
+
isEditing={isEditing}
|
|
85
|
+
/>
|
|
86
|
+
) : sectionSubtitle ? (
|
|
87
|
+
<p
|
|
88
|
+
key="section-subtitle-fallback"
|
|
89
|
+
className="text-lg text-gray-500 mb-12"
|
|
90
|
+
>
|
|
91
|
+
{String(sectionSubtitle)}
|
|
92
|
+
</p>
|
|
93
|
+
) : null}
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
{/* Features Grid (parent container block → nested feature-item blocks) */}
|
|
97
|
+
<div
|
|
98
|
+
className={`grid grid-cols-1 ${colClass} ${gapClass}`}
|
|
99
|
+
data-section-id={section.id}
|
|
100
|
+
data-block-id={containerBlock?.id || "features-container-1"}
|
|
101
|
+
data-block-type="features-container"
|
|
102
|
+
>
|
|
103
|
+
{featureBlocks.map((block) => {
|
|
104
|
+
const bs = block.settings || {};
|
|
105
|
+
const radiusMap: Record<string, string> = {
|
|
106
|
+
none: "rounded-none",
|
|
107
|
+
sm: "rounded-sm",
|
|
108
|
+
md: "rounded-md",
|
|
109
|
+
xl: "rounded-xl",
|
|
110
|
+
};
|
|
111
|
+
const paddingMap: Record<string, string> = {
|
|
112
|
+
sm: "p-4",
|
|
113
|
+
md: "p-6",
|
|
114
|
+
lg: "p-8",
|
|
115
|
+
};
|
|
116
|
+
const radius = radiusMap[bs.borderRadius as string] || "rounded-xl";
|
|
117
|
+
const padding = paddingMap[bs.padding as string] || "p-6";
|
|
118
|
+
const shadow =
|
|
119
|
+
bs.showShadow !== false ? "shadow-sm hover:shadow-md" : "";
|
|
120
|
+
return (
|
|
121
|
+
<div
|
|
122
|
+
key={block.id}
|
|
123
|
+
className={`${radius} ${padding} ${shadow} transition-shadow`}
|
|
124
|
+
style={{
|
|
125
|
+
backgroundColor: String(bs.backgroundColor || "#FFFFFF"),
|
|
126
|
+
}}
|
|
127
|
+
data-section-id={section.id}
|
|
128
|
+
data-block-id={block.id}
|
|
129
|
+
data-block-type={block.type}
|
|
130
|
+
>
|
|
131
|
+
<BlockRenderer
|
|
132
|
+
block={block}
|
|
133
|
+
sectionId={section.id}
|
|
134
|
+
isEditing={isEditing}
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
})}
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
{/* Empty state for editor */}
|
|
142
|
+
{featureBlocks.length === 0 && isEditing && (
|
|
143
|
+
<div className="text-center py-12 border-2 border-dashed border-gray-300 rounded-lg">
|
|
144
|
+
<p className="text-gray-500">
|
|
145
|
+
Add feature blocks to populate this section
|
|
146
|
+
</p>
|
|
147
|
+
</div>
|
|
148
|
+
)}
|
|
149
|
+
</div>
|
|
150
|
+
</section>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export default FeaturesDefault;
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import type { SectionSchema, FieldDefinition } from "@onexapis/core/types";
|
|
2
|
+
|
|
3
|
+
const sectionSettings: FieldDefinition[] = [
|
|
4
|
+
{
|
|
5
|
+
id: "sectionTitle",
|
|
6
|
+
type: "text",
|
|
7
|
+
label: "Section Title",
|
|
8
|
+
default: "Our Features",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
id: "sectionSubtitle",
|
|
12
|
+
type: "textarea",
|
|
13
|
+
label: "Section Subtitle",
|
|
14
|
+
default: "Everything you need to build something great",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "backgroundColor",
|
|
18
|
+
type: "color",
|
|
19
|
+
label: "Background Color",
|
|
20
|
+
default: "#F9FAFB",
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
export const featuresSchema: SectionSchema = {
|
|
25
|
+
type: "my-simple-features",
|
|
26
|
+
name: "Simple Features",
|
|
27
|
+
description: "Feature grid with icon, heading, and description blocks",
|
|
28
|
+
category: "features",
|
|
29
|
+
icon: "grid",
|
|
30
|
+
settings: sectionSettings,
|
|
31
|
+
templates: [
|
|
32
|
+
{
|
|
33
|
+
id: "default",
|
|
34
|
+
name: "Default Features",
|
|
35
|
+
description: "A grid of feature blocks with icons",
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
blocks: [
|
|
39
|
+
{
|
|
40
|
+
type: "features-container",
|
|
41
|
+
name: "Features Grid",
|
|
42
|
+
description: "Grid container for feature items",
|
|
43
|
+
icon: "grid",
|
|
44
|
+
settings: [
|
|
45
|
+
{
|
|
46
|
+
id: "columns",
|
|
47
|
+
type: "select",
|
|
48
|
+
label: "Columns",
|
|
49
|
+
default: "3",
|
|
50
|
+
options: [
|
|
51
|
+
{ label: "2 Columns", value: "2" },
|
|
52
|
+
{ label: "3 Columns", value: "3" },
|
|
53
|
+
{ label: "4 Columns", value: "4" },
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: "gap",
|
|
58
|
+
type: "select",
|
|
59
|
+
label: "Gap Size",
|
|
60
|
+
default: "lg",
|
|
61
|
+
options: [
|
|
62
|
+
{ label: "Small", value: "sm" },
|
|
63
|
+
{ label: "Medium", value: "md" },
|
|
64
|
+
{ label: "Large", value: "lg" },
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
defaults: {
|
|
69
|
+
columns: "3",
|
|
70
|
+
gap: "lg",
|
|
71
|
+
},
|
|
72
|
+
limit: 1,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
type: "feature-item",
|
|
76
|
+
name: "Feature Item",
|
|
77
|
+
description: "A single feature with icon, title, and description",
|
|
78
|
+
icon: "star",
|
|
79
|
+
settings: [
|
|
80
|
+
{
|
|
81
|
+
id: "backgroundColor",
|
|
82
|
+
type: "color",
|
|
83
|
+
label: "Background Color",
|
|
84
|
+
default: "#FFFFFF",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: "borderRadius",
|
|
88
|
+
type: "select",
|
|
89
|
+
label: "Border Radius",
|
|
90
|
+
default: "xl",
|
|
91
|
+
options: [
|
|
92
|
+
{ label: "None", value: "none" },
|
|
93
|
+
{ label: "Small", value: "sm" },
|
|
94
|
+
{ label: "Medium", value: "md" },
|
|
95
|
+
{ label: "Large", value: "xl" },
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: "padding",
|
|
100
|
+
type: "select",
|
|
101
|
+
label: "Padding",
|
|
102
|
+
default: "md",
|
|
103
|
+
options: [
|
|
104
|
+
{ label: "Small", value: "sm" },
|
|
105
|
+
{ label: "Medium", value: "md" },
|
|
106
|
+
{ label: "Large", value: "lg" },
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: "showShadow",
|
|
111
|
+
type: "checkbox",
|
|
112
|
+
label: "Show Shadow",
|
|
113
|
+
default: true,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
defaults: {
|
|
117
|
+
backgroundColor: "#FFFFFF",
|
|
118
|
+
borderRadius: "xl",
|
|
119
|
+
padding: "md",
|
|
120
|
+
showShadow: true,
|
|
121
|
+
},
|
|
122
|
+
limit: 12,
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
maxBlocks: 13,
|
|
126
|
+
defaults: {
|
|
127
|
+
settings: {
|
|
128
|
+
sectionTitle: "Our Features",
|
|
129
|
+
sectionSubtitle: "Everything you need to build something great",
|
|
130
|
+
backgroundColor: "#F9FAFB",
|
|
131
|
+
},
|
|
132
|
+
components: [
|
|
133
|
+
{
|
|
134
|
+
id: "features-title",
|
|
135
|
+
type: "heading",
|
|
136
|
+
slot: "section-title",
|
|
137
|
+
order: 0,
|
|
138
|
+
content: { text: "Our Features", tag: "h2" },
|
|
139
|
+
style: {
|
|
140
|
+
fontSize: "3xl",
|
|
141
|
+
fontWeight: "bold",
|
|
142
|
+
color: "#111827",
|
|
143
|
+
textAlign: "center",
|
|
144
|
+
marginBottom: "0.5rem",
|
|
145
|
+
},
|
|
146
|
+
styleMode: "advanced",
|
|
147
|
+
enabled: true,
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "features-subtitle",
|
|
151
|
+
type: "paragraph",
|
|
152
|
+
slot: "section-subtitle",
|
|
153
|
+
order: 1,
|
|
154
|
+
content: { text: "Everything you need to build something great" },
|
|
155
|
+
style: {
|
|
156
|
+
fontSize: "lg",
|
|
157
|
+
color: "#6B7280",
|
|
158
|
+
textAlign: "center",
|
|
159
|
+
marginBottom: "3rem",
|
|
160
|
+
},
|
|
161
|
+
styleMode: "advanced",
|
|
162
|
+
enabled: true,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
blocks: [
|
|
166
|
+
{
|
|
167
|
+
id: "features-container-1",
|
|
168
|
+
type: "features-container",
|
|
169
|
+
order: 0,
|
|
170
|
+
enabled: true,
|
|
171
|
+
settings: {
|
|
172
|
+
columns: "3",
|
|
173
|
+
gap: "lg",
|
|
174
|
+
},
|
|
175
|
+
blocks: [
|
|
176
|
+
{
|
|
177
|
+
id: "feature-1",
|
|
178
|
+
type: "feature-item",
|
|
179
|
+
order: 0,
|
|
180
|
+
enabled: true,
|
|
181
|
+
settings: {},
|
|
182
|
+
components: [
|
|
183
|
+
{
|
|
184
|
+
id: "feature-1-icon",
|
|
185
|
+
type: "icon",
|
|
186
|
+
slot: "icon",
|
|
187
|
+
order: 0,
|
|
188
|
+
content: { icon: "zap", text: "" },
|
|
189
|
+
style: { fontSize: "2xl", color: "#3B82F6" },
|
|
190
|
+
styleMode: "advanced",
|
|
191
|
+
enabled: true,
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: "feature-1-title",
|
|
195
|
+
type: "heading",
|
|
196
|
+
slot: "title",
|
|
197
|
+
order: 1,
|
|
198
|
+
content: { text: "Lightning Fast", tag: "h3" },
|
|
199
|
+
style: {
|
|
200
|
+
fontSize: "xl",
|
|
201
|
+
fontWeight: "semibold",
|
|
202
|
+
color: "#111827",
|
|
203
|
+
marginBottom: "0.5rem",
|
|
204
|
+
},
|
|
205
|
+
styleMode: "advanced",
|
|
206
|
+
enabled: true,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
id: "feature-1-desc",
|
|
210
|
+
type: "paragraph",
|
|
211
|
+
slot: "description",
|
|
212
|
+
order: 2,
|
|
213
|
+
content: {
|
|
214
|
+
text: "Built for speed with optimized performance at every level.",
|
|
215
|
+
},
|
|
216
|
+
style: {
|
|
217
|
+
fontSize: "base",
|
|
218
|
+
color: "#6B7280",
|
|
219
|
+
lineHeight: "1.6",
|
|
220
|
+
},
|
|
221
|
+
styleMode: "advanced",
|
|
222
|
+
enabled: true,
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
id: "feature-2",
|
|
228
|
+
type: "feature-item",
|
|
229
|
+
order: 1,
|
|
230
|
+
enabled: true,
|
|
231
|
+
settings: {},
|
|
232
|
+
components: [
|
|
233
|
+
{
|
|
234
|
+
id: "feature-2-icon",
|
|
235
|
+
type: "icon",
|
|
236
|
+
slot: "icon",
|
|
237
|
+
order: 0,
|
|
238
|
+
content: { icon: "shield", text: "" },
|
|
239
|
+
style: { fontSize: "2xl", color: "#3B82F6" },
|
|
240
|
+
styleMode: "advanced",
|
|
241
|
+
enabled: true,
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
id: "feature-2-title",
|
|
245
|
+
type: "heading",
|
|
246
|
+
slot: "title",
|
|
247
|
+
order: 1,
|
|
248
|
+
content: { text: "Secure by Default", tag: "h3" },
|
|
249
|
+
style: {
|
|
250
|
+
fontSize: "xl",
|
|
251
|
+
fontWeight: "semibold",
|
|
252
|
+
color: "#111827",
|
|
253
|
+
marginBottom: "0.5rem",
|
|
254
|
+
},
|
|
255
|
+
styleMode: "advanced",
|
|
256
|
+
enabled: true,
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
id: "feature-2-desc",
|
|
260
|
+
type: "paragraph",
|
|
261
|
+
slot: "description",
|
|
262
|
+
order: 2,
|
|
263
|
+
content: {
|
|
264
|
+
text: "Enterprise-grade security built into every feature.",
|
|
265
|
+
},
|
|
266
|
+
style: {
|
|
267
|
+
fontSize: "base",
|
|
268
|
+
color: "#6B7280",
|
|
269
|
+
lineHeight: "1.6",
|
|
270
|
+
},
|
|
271
|
+
styleMode: "advanced",
|
|
272
|
+
enabled: true,
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
id: "feature-3",
|
|
278
|
+
type: "feature-item",
|
|
279
|
+
order: 2,
|
|
280
|
+
enabled: true,
|
|
281
|
+
settings: {},
|
|
282
|
+
components: [
|
|
283
|
+
{
|
|
284
|
+
id: "feature-3-icon",
|
|
285
|
+
type: "icon",
|
|
286
|
+
slot: "icon",
|
|
287
|
+
order: 0,
|
|
288
|
+
content: { icon: "palette", text: "" },
|
|
289
|
+
style: { fontSize: "2xl", color: "#3B82F6" },
|
|
290
|
+
styleMode: "advanced",
|
|
291
|
+
enabled: true,
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
id: "feature-3-title",
|
|
295
|
+
type: "heading",
|
|
296
|
+
slot: "title",
|
|
297
|
+
order: 1,
|
|
298
|
+
content: { text: "Beautiful Design", tag: "h3" },
|
|
299
|
+
style: {
|
|
300
|
+
fontSize: "xl",
|
|
301
|
+
fontWeight: "semibold",
|
|
302
|
+
color: "#111827",
|
|
303
|
+
marginBottom: "0.5rem",
|
|
304
|
+
},
|
|
305
|
+
styleMode: "advanced",
|
|
306
|
+
enabled: true,
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
id: "feature-3-desc",
|
|
310
|
+
type: "paragraph",
|
|
311
|
+
slot: "description",
|
|
312
|
+
order: 2,
|
|
313
|
+
content: {
|
|
314
|
+
text: "Pixel-perfect designs that look great on any device.",
|
|
315
|
+
},
|
|
316
|
+
style: {
|
|
317
|
+
fontSize: "base",
|
|
318
|
+
color: "#6B7280",
|
|
319
|
+
lineHeight: "1.6",
|
|
320
|
+
},
|
|
321
|
+
styleMode: "advanced",
|
|
322
|
+
enabled: true,
|
|
323
|
+
},
|
|
324
|
+
],
|
|
325
|
+
},
|
|
326
|
+
],
|
|
327
|
+
},
|
|
328
|
+
],
|
|
329
|
+
},
|
|
330
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SectionComponentProps } from "@onexapis/core/types";
|
|
2
|
+
import { FeaturesDefault } from "./features-default";
|
|
3
|
+
|
|
4
|
+
export const featuresComponents: Record<
|
|
5
|
+
string,
|
|
6
|
+
React.ComponentType<SectionComponentProps>
|
|
7
|
+
> = {
|
|
8
|
+
default: FeaturesDefault,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export { featuresSchema } from "./features.schema";
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { SectionComponentProps } from "@onexapis/core/types";
|
|
4
|
+
import coreRenderers from "@onexapis/core/renderers";
|
|
5
|
+
import coreUtils from "@onexapis/core/utils";
|
|
6
|
+
|
|
7
|
+
const { ComponentRenderer, BlockRenderer } = coreRenderers;
|
|
8
|
+
const { toComponentInstance, getSectionValues, filterEnabledComponents } =
|
|
9
|
+
coreUtils;
|
|
10
|
+
|
|
11
|
+
export function GalleryDefault({
|
|
12
|
+
section,
|
|
13
|
+
schema,
|
|
14
|
+
isEditing,
|
|
15
|
+
}: SectionComponentProps) {
|
|
16
|
+
const components = filterEnabledComponents(section.components || []);
|
|
17
|
+
const titleComp = components.find(
|
|
18
|
+
(c) => c.slot === "section-title" || c.id === "gallery-title"
|
|
19
|
+
);
|
|
20
|
+
const subtitleComp = components.find(
|
|
21
|
+
(c) => c.slot === "section-subtitle" || c.id === "gallery-subtitle"
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const { settings } = getSectionValues(section, schema);
|
|
25
|
+
const { sectionTitle, sectionSubtitle, columns, gap, backgroundColor } =
|
|
26
|
+
settings;
|
|
27
|
+
|
|
28
|
+
const colClass =
|
|
29
|
+
columns === "2"
|
|
30
|
+
? "md:grid-cols-2"
|
|
31
|
+
: columns === "4"
|
|
32
|
+
? "md:grid-cols-2 lg:grid-cols-4"
|
|
33
|
+
: "md:grid-cols-3";
|
|
34
|
+
|
|
35
|
+
const gapClass = gap === "sm" ? "gap-4" : gap === "lg" ? "gap-8" : "gap-6";
|
|
36
|
+
|
|
37
|
+
const blocks = (section.blocks || [])
|
|
38
|
+
.filter((b) => b.enabled !== false)
|
|
39
|
+
.sort((a, b) => (a.order || 0) - (b.order || 0));
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<section
|
|
43
|
+
className="py-16"
|
|
44
|
+
style={{ backgroundColor: String(backgroundColor || "#FFFFFF") }}
|
|
45
|
+
data-section="gallery"
|
|
46
|
+
data-section-id={section.id}
|
|
47
|
+
data-section-template="default"
|
|
48
|
+
data-section-name="Simple Gallery"
|
|
49
|
+
>
|
|
50
|
+
<div className="container mx-auto px-4 max-w-6xl">
|
|
51
|
+
{/* Section Header */}
|
|
52
|
+
<div className="text-center mb-12">
|
|
53
|
+
{titleComp ? (
|
|
54
|
+
<ComponentRenderer
|
|
55
|
+
key="section-title"
|
|
56
|
+
instance={toComponentInstance(titleComp)}
|
|
57
|
+
sectionId={section.id}
|
|
58
|
+
isEditing={isEditing}
|
|
59
|
+
/>
|
|
60
|
+
) : sectionTitle ? (
|
|
61
|
+
<h2
|
|
62
|
+
key="section-title-fallback"
|
|
63
|
+
className="text-3xl font-bold text-gray-900 mb-2"
|
|
64
|
+
>
|
|
65
|
+
{String(sectionTitle)}
|
|
66
|
+
</h2>
|
|
67
|
+
) : null}
|
|
68
|
+
|
|
69
|
+
{subtitleComp ? (
|
|
70
|
+
<ComponentRenderer
|
|
71
|
+
key="section-subtitle"
|
|
72
|
+
instance={toComponentInstance(subtitleComp)}
|
|
73
|
+
sectionId={section.id}
|
|
74
|
+
isEditing={isEditing}
|
|
75
|
+
/>
|
|
76
|
+
) : sectionSubtitle ? (
|
|
77
|
+
<p
|
|
78
|
+
key="section-subtitle-fallback"
|
|
79
|
+
className="text-lg text-gray-500 mb-12"
|
|
80
|
+
>
|
|
81
|
+
{String(sectionSubtitle)}
|
|
82
|
+
</p>
|
|
83
|
+
) : null}
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
{/* Gallery Grid */}
|
|
87
|
+
<div className={`grid grid-cols-1 ${colClass} ${gapClass}`}>
|
|
88
|
+
{blocks.map((block) => {
|
|
89
|
+
const bs = block.settings || {};
|
|
90
|
+
const radiusMap: Record<string, string> = {
|
|
91
|
+
none: "rounded-none",
|
|
92
|
+
sm: "rounded-sm",
|
|
93
|
+
md: "rounded-md",
|
|
94
|
+
lg: "rounded-lg",
|
|
95
|
+
};
|
|
96
|
+
const radius = radiusMap[bs.borderRadius as string] || "rounded-lg";
|
|
97
|
+
const aspectMap: Record<string, string> = {
|
|
98
|
+
square: "aspect-square",
|
|
99
|
+
landscape: "aspect-[4/3]",
|
|
100
|
+
wide: "aspect-video",
|
|
101
|
+
};
|
|
102
|
+
const aspect = aspectMap[bs.aspectRatio as string] || "";
|
|
103
|
+
return (
|
|
104
|
+
<div
|
|
105
|
+
key={block.id}
|
|
106
|
+
className={`overflow-hidden ${radius} ${aspect} group`}
|
|
107
|
+
data-section-id={section.id}
|
|
108
|
+
data-block-id={block.id}
|
|
109
|
+
data-block-type={block.type}
|
|
110
|
+
>
|
|
111
|
+
<BlockRenderer
|
|
112
|
+
block={block}
|
|
113
|
+
sectionId={section.id}
|
|
114
|
+
isEditing={isEditing}
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
})}
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
{/* Empty state for editor */}
|
|
122
|
+
{blocks.length === 0 && isEditing && (
|
|
123
|
+
<div className="text-center py-12 border-2 border-dashed border-gray-300 rounded-lg">
|
|
124
|
+
<p className="text-gray-500">
|
|
125
|
+
Add gallery items to populate this section
|
|
126
|
+
</p>
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
</section>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export default GalleryDefault;
|