@mandolop97/constructor-nexora 1.3.1 → 1.5.0

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.
@@ -0,0 +1,366 @@
1
+ /**
2
+ * ════════════════════════════════════════════════════════════════════════════
3
+ * NEXORA VISUAL BUILDER — OFFICIAL CONTRACT
4
+ * ════════════════════════════════════════════════════════════════════════════
5
+ *
6
+ * This file defines the **shared contract** between the Nexora Visual Builder
7
+ * and any host template that installs it via NPM.
8
+ *
9
+ * Both systems MUST use these exact types, names, and structures to ensure
10
+ * full compatibility and seamless data flow.
11
+ *
12
+ * VERSION: 1.5.0
13
+ */
14
+ import { NodeStyle, ThemeTokens, AnimationPreset, SchemaNode, Schema } from './schema';
15
+ /** All built-in node types supported by the builder */
16
+ export type BuiltInNodeType = 'Section' | 'Container' | 'Grid' | 'Stack' | 'Text' | 'Image' | 'Divider' | 'Badge' | 'Spacer' | 'Icon' | 'SocialIcons' | 'Button' | 'Card' | 'Input' | 'Accordion' | 'TabsBlock' | 'VideoEmbed' | 'FormBlock' | 'ProductCard' | 'ProductGrid' | 'CollectionGrid' | 'Navbar' | 'Footer' | 'AnnouncementBar' | 'HeroSection' | 'FeatureBar' | 'TestimonialCard' | 'NewsletterSection' | 'ImageBanner' | 'RichTextSection' | 'CTASection' | 'TestimonialSection' | 'FAQSection';
17
+ /** Extensible node type — accepts built-in types plus any custom string */
18
+ export type NodeType = BuiltInNodeType | (string & {});
19
+ /** Supported data sources for binding */
20
+ export type DataSourceType = 'products' | 'collections' | 'pages' | 'media' | 'settings' | 'custom';
21
+ /** A binding maps a node prop to a data field */
22
+ export interface DataBinding {
23
+ /** The prop key on the node (e.g., 'text', 'src', 'price') */
24
+ propKey: string;
25
+ /** The field path in the data source (e.g., 'product.title', 'product.images[0].url') */
26
+ fieldPath: string;
27
+ /** Optional transform function name (e.g., 'formatPrice', 'uppercase') */
28
+ transform?: string;
29
+ }
30
+ /** Data source configuration for a node */
31
+ export interface NodeDataSource {
32
+ /** Type of data source */
33
+ type: DataSourceType;
34
+ /** Query/filter parameters */
35
+ query?: {
36
+ collection?: string;
37
+ category?: string;
38
+ limit?: number;
39
+ sort?: 'newest' | 'oldest' | 'price-asc' | 'price-desc' | 'popular';
40
+ filter?: Record<string, any>;
41
+ };
42
+ /** Whether this node iterates over multiple items (e.g., ProductGrid) */
43
+ isCollection?: boolean;
44
+ /** Variable name for the current item in iteration context */
45
+ itemVariable?: string;
46
+ }
47
+ /** Complete binding configuration for a node */
48
+ export interface NodeBindings {
49
+ /** Data source configuration */
50
+ dataSource?: NodeDataSource;
51
+ /** Property bindings */
52
+ bindings?: DataBinding[];
53
+ /** Fallback props when no data is available */
54
+ fallbackProps?: Record<string, any>;
55
+ /** Binding mode */
56
+ mode: 'manual' | 'bound' | 'hybrid';
57
+ }
58
+ /** Base props shared by all nodes */
59
+ export interface BaseNodeProps {
60
+ /** Custom display name in layers panel */
61
+ customName?: string;
62
+ /** Scroll-triggered animation */
63
+ scrollAnimation?: AnimationPreset | 'none';
64
+ scrollAnimationDelay?: string;
65
+ scrollAnimationDuration?: string;
66
+ }
67
+ /** Text node props */
68
+ export interface TextNodeProps extends BaseNodeProps {
69
+ text?: string;
70
+ level?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span';
71
+ }
72
+ /** Image node props */
73
+ export interface ImageNodeProps extends BaseNodeProps {
74
+ src?: string;
75
+ alt?: string;
76
+ loading?: 'lazy' | 'eager';
77
+ }
78
+ /** Button node props */
79
+ export interface ButtonNodeProps extends BaseNodeProps {
80
+ text?: string;
81
+ href?: string;
82
+ variant?: 'default' | 'outline' | 'secondary' | 'ghost' | 'link';
83
+ size?: 'sm' | 'md' | 'lg';
84
+ }
85
+ /** ProductCard props (for template) */
86
+ export interface ProductCardNodeProps extends BaseNodeProps {
87
+ productId?: string;
88
+ title?: string;
89
+ price?: string;
90
+ originalPrice?: string;
91
+ image?: string;
92
+ badge?: string;
93
+ inStock?: boolean;
94
+ }
95
+ /** ProductGrid props */
96
+ export interface ProductGridNodeProps extends BaseNodeProps {
97
+ columns?: number;
98
+ limit?: number;
99
+ category?: string;
100
+ gap?: string;
101
+ collection?: string;
102
+ sort?: 'newest' | 'oldest' | 'price-asc' | 'price-desc' | 'popular';
103
+ }
104
+ /** CollectionGrid props */
105
+ export interface CollectionGridNodeProps extends BaseNodeProps {
106
+ columns?: number;
107
+ limit?: number;
108
+ gap?: string;
109
+ showTitle?: boolean;
110
+ showCount?: boolean;
111
+ }
112
+ /** HeroSection props */
113
+ export interface HeroSectionNodeProps extends BaseNodeProps {
114
+ heading?: string;
115
+ text?: string;
116
+ subtitle?: string;
117
+ ctaText?: string;
118
+ ctaHref?: string;
119
+ secondaryCtaText?: string;
120
+ secondaryCtaHref?: string;
121
+ src?: string;
122
+ overlayOpacity?: string;
123
+ overlayColor?: string;
124
+ textAlign?: 'left' | 'center' | 'right';
125
+ minHeight?: string;
126
+ }
127
+ /** ImageBanner props */
128
+ export interface ImageBannerNodeProps extends BaseNodeProps {
129
+ src?: string;
130
+ alt?: string;
131
+ href?: string;
132
+ overlayText?: string;
133
+ overlayPosition?: 'top' | 'center' | 'bottom';
134
+ }
135
+ /** RichTextSection props */
136
+ export interface RichTextSectionNodeProps extends BaseNodeProps {
137
+ content?: string;
138
+ columns?: 1 | 2;
139
+ }
140
+ /** CTASection props */
141
+ export interface CTASectionNodeProps extends BaseNodeProps {
142
+ heading?: string;
143
+ description?: string;
144
+ primaryCtaText?: string;
145
+ primaryCtaHref?: string;
146
+ secondaryCtaText?: string;
147
+ secondaryCtaHref?: string;
148
+ backgroundStyle?: 'solid' | 'gradient' | 'image';
149
+ backgroundImage?: string;
150
+ }
151
+ /** TestimonialSection props */
152
+ export interface TestimonialSectionNodeProps extends BaseNodeProps {
153
+ heading?: string;
154
+ testimonials?: Array<{
155
+ quote: string;
156
+ author: string;
157
+ role?: string;
158
+ avatar?: string;
159
+ rating?: number;
160
+ }>;
161
+ layout?: 'grid' | 'carousel' | 'stack';
162
+ }
163
+ /** FAQSection props */
164
+ export interface FAQSectionNodeProps extends BaseNodeProps {
165
+ heading?: string;
166
+ subtitle?: string;
167
+ items?: Array<{
168
+ question: string;
169
+ answer: string;
170
+ }>;
171
+ layout?: 'accordion' | 'grid';
172
+ }
173
+ /** Navbar props */
174
+ export interface NavbarNodeProps extends BaseNodeProps {
175
+ logoText?: string;
176
+ logoSrc?: string;
177
+ links?: Array<{
178
+ text: string;
179
+ href: string;
180
+ }>;
181
+ showSearch?: boolean;
182
+ showCart?: boolean;
183
+ showAccount?: boolean;
184
+ mobileMenuStyle?: 'sidebar' | 'dropdown';
185
+ announcementText?: string;
186
+ announcementHref?: string;
187
+ announcementDismissible?: boolean;
188
+ }
189
+ /** Footer props */
190
+ export interface FooterNodeProps extends BaseNodeProps {
191
+ logoText?: string;
192
+ logoSrc?: string;
193
+ copyright?: string;
194
+ links?: Array<{
195
+ text: string;
196
+ href: string;
197
+ }>;
198
+ columns?: Array<{
199
+ title: string;
200
+ links: Array<{
201
+ text: string;
202
+ href: string;
203
+ }>;
204
+ }>;
205
+ socialLinks?: Array<{
206
+ platform: string;
207
+ url: string;
208
+ }>;
209
+ showNewsletter?: boolean;
210
+ newsletterTitle?: string;
211
+ newsletterPlaceholder?: string;
212
+ }
213
+ /** Extended schema node with data binding support */
214
+ export interface BoundSchemaNode extends SchemaNode {
215
+ /** Data binding configuration */
216
+ bindings?: NodeBindings;
217
+ /** Metadata for builder/renderer */
218
+ metadata?: {
219
+ /** Node was created from a template */
220
+ fromTemplate?: string;
221
+ /** Template version */
222
+ templateVersion?: string;
223
+ /** Node is a master template that others reference */
224
+ isMasterTemplate?: boolean;
225
+ /** Last edited timestamp */
226
+ lastEdited?: string;
227
+ /** Created by (user or system) */
228
+ createdBy?: 'user' | 'system';
229
+ };
230
+ }
231
+ export type RenderMode = 'public' | 'preview' | 'edit';
232
+ export interface RenderContext {
233
+ mode: RenderMode;
234
+ /** Data available for binding */
235
+ data?: {
236
+ products?: any[];
237
+ collections?: any[];
238
+ pages?: any[];
239
+ settings?: Record<string, any>;
240
+ custom?: Record<string, any>;
241
+ };
242
+ /** Current item when rendering inside a collection */
243
+ currentItem?: any;
244
+ /** Item index when rendering inside a collection */
245
+ currentIndex?: number;
246
+ /** Theme tokens */
247
+ theme?: ThemeTokens;
248
+ /** Resolve asset URLs */
249
+ resolveAssetUrl?: (path: string) => string;
250
+ }
251
+ export type TemplateType = 'page' | 'header' | 'footer' | 'component' | 'single';
252
+ export interface PageDefinition {
253
+ slug: string;
254
+ title: string;
255
+ schema: Schema;
256
+ status?: 'published' | 'draft';
257
+ templateType?: TemplateType;
258
+ category?: string;
259
+ icon?: React.ComponentType;
260
+ canvasSize?: {
261
+ width: number;
262
+ height: number;
263
+ };
264
+ mockData?: Record<string, any>;
265
+ }
266
+ export interface InspectorFieldDef {
267
+ key: string;
268
+ label: string;
269
+ type: 'text' | 'select' | 'color' | 'number' | 'image' | 'toggle' | 'slider' | 'textarea' | 'link' | 'icon' | 'spacing' | 'group' | 'binding';
270
+ options?: {
271
+ label: string;
272
+ value: string;
273
+ }[];
274
+ min?: number;
275
+ max?: number;
276
+ step?: number;
277
+ placeholder?: string;
278
+ rows?: number;
279
+ accept?: string;
280
+ children?: InspectorFieldDef[];
281
+ defaultValue?: any;
282
+ /** For binding type: allowed data sources */
283
+ allowedDataSources?: DataSourceType[];
284
+ /** For binding type: available fields */
285
+ bindableFields?: string[];
286
+ }
287
+ export interface BlockDefinition {
288
+ type: NodeType;
289
+ label: string;
290
+ category: 'Layout' | 'Content' | 'UI' | 'Interactive' | 'Commerce' | 'Site' | 'Template' | string;
291
+ icon: React.ComponentType<{
292
+ className?: string;
293
+ }>;
294
+ canHaveChildren: boolean;
295
+ defaultProps: Record<string, any>;
296
+ defaultStyle: Partial<NodeStyle>;
297
+ allowedParents?: NodeType[];
298
+ allowedChildren?: NodeType[];
299
+ inspectorFields?: InspectorFieldDef[];
300
+ allowedTemplateTypes?: TemplateType[];
301
+ /** Factory for composite blocks */
302
+ compositeFactory?: () => {
303
+ rootId: string;
304
+ nodes: Record<string, SchemaNode>;
305
+ };
306
+ /** Whether this block supports data binding */
307
+ supportsBinding?: boolean;
308
+ /** Default binding configuration */
309
+ defaultBindings?: NodeBindings;
310
+ /** Documentation/help text */
311
+ description?: string;
312
+ }
313
+ /**
314
+ * This interface documents what the @mandolop97/constructor-nexora package exports.
315
+ * Host templates should import these items to integrate with the builder.
316
+ */
317
+ export interface NexoraExports {
318
+ NexoraBuilderApp: React.ComponentType<any>;
319
+ PageRenderer: React.ComponentType<any>;
320
+ CustomStylesInjector: React.ComponentType<any>;
321
+ createHomeSchema: () => Schema;
322
+ createProductsSchema: () => Schema;
323
+ createFAQSchema: () => Schema;
324
+ createContactSchema: () => Schema;
325
+ createHelpSchema: () => Schema;
326
+ createPrivacySchema: () => Schema;
327
+ createTermsSchema: () => Schema;
328
+ createWishlistSchema: () => Schema;
329
+ PAGE_DEFINITIONS: PageDefinition[];
330
+ getDefaultSchemaForSlug: (slug: string) => Schema | null;
331
+ blockRegistry: BlockDefinition[];
332
+ getBlockDef: (type: NodeType) => BlockDefinition | undefined;
333
+ registerBlock: (def: BlockDefinition) => void;
334
+ registerBlocks: (defs: BlockDefinition[]) => void;
335
+ getCategories: () => string[];
336
+ getBlocksByCategory: (category: string, templateType?: TemplateType) => BlockDefinition[];
337
+ createNode: (type: NodeType) => SchemaNode;
338
+ createNodeTree: (type: NodeType) => {
339
+ rootId: string;
340
+ nodes: Record<string, SchemaNode>;
341
+ };
342
+ duplicateNodeTree: (sourceId: string, nodes: Record<string, SchemaNode>) => {
343
+ newNodes: Record<string, SchemaNode>;
344
+ newRootId: string;
345
+ };
346
+ isContainerType: (type: NodeType) => boolean;
347
+ nodeStyleToCSS: (style: NodeStyle, nodeId: string) => string;
348
+ themeTokensToCSS: (tokens: ThemeTokens) => React.CSSProperties;
349
+ generatePseudoStateCSS: (style: NodeStyle, nodeId: string) => string;
350
+ generateResponsiveCSS: (style: NodeStyle, nodeId: string) => string;
351
+ validateSchema: (schema: any) => {
352
+ schema: Schema | null;
353
+ errors: string[];
354
+ };
355
+ resolveBindings: (node: BoundSchemaNode, context: RenderContext) => Record<string, any>;
356
+ hydrateTemplate: (template: Schema, data: any) => Schema;
357
+ EDITOR_VERSION: string;
358
+ }
359
+ /** Union of all node prop types */
360
+ export type AllNodeProps = TextNodeProps | ImageNodeProps | ButtonNodeProps | ProductCardNodeProps | ProductGridNodeProps | CollectionGridNodeProps | HeroSectionNodeProps | ImageBannerNodeProps | RichTextSectionNodeProps | CTASectionNodeProps | TestimonialSectionNodeProps | FAQSectionNodeProps | NavbarNodeProps | FooterNodeProps | BaseNodeProps;
361
+ /** Type guard for checking if a node supports binding */
362
+ export declare function supportsDataBinding(nodeType: NodeType): boolean;
363
+ /** Type guard for checking if a node is a commerce block */
364
+ export declare function isCommerceBlock(nodeType: NodeType): boolean;
365
+ /** Type guard for checking if a node is a template block */
366
+ export declare function isTemplateBlock(nodeType: NodeType): boolean;
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- export type BuiltInNodeType = 'Section' | 'Container' | 'Grid' | 'Stack' | 'Text' | 'Image' | 'Divider' | 'Badge' | 'Button' | 'Card' | 'Input' | 'ProductCard' | 'Navbar' | 'Footer' | 'AnnouncementBar' | 'FeatureBar' | 'TestimonialCard' | 'NewsletterSection' | 'HeroSection' | 'Accordion' | 'TabsBlock' | 'VideoEmbed' | 'Spacer' | 'Icon' | 'SocialIcons' | 'FormBlock';
2
+ export type BuiltInNodeType = 'Section' | 'Container' | 'Grid' | 'Stack' | 'Text' | 'Image' | 'Divider' | 'Badge' | 'Button' | 'Card' | 'Input' | 'ProductCard' | 'ProductGrid' | 'CollectionGrid' | 'Navbar' | 'Footer' | 'AnnouncementBar' | 'FeatureBar' | 'TestimonialCard' | 'NewsletterSection' | 'HeroSection' | 'ImageBanner' | 'RichTextSection' | 'CTASection' | 'TestimonialSection' | 'FAQSection' | 'Accordion' | 'TabsBlock' | 'VideoEmbed' | 'Spacer' | 'Icon' | 'SocialIcons' | 'FormBlock';
3
3
  /** Extensible node type — accepts all built-in types plus any custom string. */
4
4
  export type NodeType = BuiltInNodeType | (string & {});
5
5
  export interface NodeStyle {
@@ -160,6 +160,10 @@ export interface NodeProps {
160
160
  videoUrl?: string;
161
161
  autoplay?: boolean;
162
162
  muted?: boolean;
163
+ /** Scroll animation */
164
+ scrollAnimation?: 'fadeIn' | 'slideUp' | 'slideLeft' | 'slideRight' | 'scaleIn' | 'none';
165
+ scrollAnimationDelay?: string;
166
+ scrollAnimationDuration?: string;
163
167
  /** Allow arbitrary extra props from custom blocks */
164
168
  [key: string]: any;
165
169
  }
@@ -174,6 +178,8 @@ export interface SchemaNode {
174
178
  customName?: string;
175
179
  /** Raw CSS applied to this specific widget */
176
180
  customCSS?: string;
181
+ /** IDs of global styles applied to this node */
182
+ appliedGlobalStyles?: string[];
177
183
  }
178
184
  export interface ThemeTokens {
179
185
  colors: {
@@ -203,6 +209,12 @@ export interface ThemeTokens {
203
209
  xl: string;
204
210
  };
205
211
  gradient?: string;
212
+ /** Default ProductCard layout used across the site */
213
+ defaultCardLayout?: 'vertical' | 'horizontal' | 'minimal' | 'overlay';
214
+ }
215
+ export interface GlobalStyleDef {
216
+ label: string;
217
+ style: Partial<NodeStyle>;
206
218
  }
207
219
  export interface Schema {
208
220
  id: string;
@@ -211,6 +223,8 @@ export interface Schema {
211
223
  themeTokens: ThemeTokens;
212
224
  rootNodeId: string;
213
225
  nodes: Record<string, SchemaNode>;
226
+ /** Reusable global style classes */
227
+ globalStyles?: Record<string, GlobalStyleDef>;
214
228
  }
215
229
  export interface Page {
216
230
  id: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mandolop97/constructor-nexora",
3
3
  "private": false,
4
- "version": "1.3.1",
4
+ "version": "1.5.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",