@bagelink/blox 1.5.3
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/LICENSE +21 -0
- package/README.md +844 -0
- package/components/base/Button.vue +140 -0
- package/components/base/Container.vue +64 -0
- package/components/base/Image.vue +75 -0
- package/components/base/Spacer.vue +33 -0
- package/components/base/Text.vue +37 -0
- package/components/base/Title.vue +55 -0
- package/components/index.ts +20 -0
- package/config/baseComponents.ts +342 -0
- package/core/communication.ts +140 -0
- package/core/registry.ts +108 -0
- package/core/renderer.ts +217 -0
- package/core/types.ts +148 -0
- package/dist/blox.css +296 -0
- package/dist/components/base/Button.vue.d.ts +26 -0
- package/dist/components/base/Button.vue.d.ts.map +1 -0
- package/dist/components/base/Container.vue.d.ts +37 -0
- package/dist/components/base/Container.vue.d.ts.map +1 -0
- package/dist/components/base/Image.vue.d.ts +26 -0
- package/dist/components/base/Image.vue.d.ts.map +1 -0
- package/dist/components/base/Spacer.vue.d.ts +16 -0
- package/dist/components/base/Spacer.vue.d.ts.map +1 -0
- package/dist/components/base/Text.vue.d.ts +13 -0
- package/dist/components/base/Text.vue.d.ts.map +1 -0
- package/dist/components/base/Title.vue.d.ts +14 -0
- package/dist/components/base/Title.vue.d.ts.map +1 -0
- package/dist/components/index.d.ts +18 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/config/baseComponents.d.ts +39 -0
- package/dist/config/baseComponents.d.ts.map +1 -0
- package/dist/core/communication.d.ts +44 -0
- package/dist/core/communication.d.ts.map +1 -0
- package/dist/core/registry.d.ts +56 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/renderer.d.ts +27 -0
- package/dist/core/renderer.d.ts.map +1 -0
- package/dist/core/types.d.ts +105 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/index.cjs +1305 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +1260 -0
- package/dist/setup.d.ts +24 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/utils/normalizer.d.ts +18 -0
- package/dist/utils/normalizer.d.ts.map +1 -0
- package/dist/utils/styles.d.ts +13 -0
- package/dist/utils/styles.d.ts.map +1 -0
- package/dist/views/ExternalPreview.vue.d.ts +12 -0
- package/dist/views/ExternalPreview.vue.d.ts.map +1 -0
- package/dist/views/RenderPage.vue.d.ts +10 -0
- package/dist/views/RenderPage.vue.d.ts.map +1 -0
- package/dist/vite.config.d.ts +3 -0
- package/dist/vite.config.d.ts.map +1 -0
- package/index.ts +27 -0
- package/package.json +94 -0
- package/setup.ts +56 -0
- package/utils/normalizer.ts +74 -0
- package/utils/styles.ts +228 -0
- package/views/ExternalPreview.vue +420 -0
- package/views/RenderPage.vue +127 -0
package/dist/setup.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ComponentConfig } from './config/baseComponents';
|
|
2
|
+
/**
|
|
3
|
+
* Register all base components from the library
|
|
4
|
+
* Call this in your project's setup to make base components available
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerBaseComponents(): void;
|
|
7
|
+
/**
|
|
8
|
+
* Get all component configurations (base + custom)
|
|
9
|
+
* This returns both the Vue components and their editor schemas
|
|
10
|
+
*/
|
|
11
|
+
export declare function getAllComponentConfigs(customConfigs?: ComponentConfig[]): ComponentConfig[];
|
|
12
|
+
/**
|
|
13
|
+
* Helper to create a custom component configuration
|
|
14
|
+
*/
|
|
15
|
+
export declare function createComponentConfig(config: ComponentConfig): ComponentConfig;
|
|
16
|
+
/**
|
|
17
|
+
* Register a single custom component
|
|
18
|
+
*/
|
|
19
|
+
export declare function registerCustomComponent(config: ComponentConfig): void;
|
|
20
|
+
/**
|
|
21
|
+
* Register multiple custom components
|
|
22
|
+
*/
|
|
23
|
+
export declare function registerCustomComponents(configs: ComponentConfig[]): void;
|
|
24
|
+
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../setup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAGpF;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAS7C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,GAAE,eAAe,EAAO,GAAG,eAAe,EAAE,CAE/F;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAE9E;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAGrE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,IAAI,CAIzE"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Normalization Utilities
|
|
3
|
+
*
|
|
4
|
+
* Normalizes component data for proper rendering
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Convert string values to proper types for component props
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalizeComponentData(data: Record<string, any>): Record<string, any>;
|
|
10
|
+
/**
|
|
11
|
+
* Deep clone an object
|
|
12
|
+
*/
|
|
13
|
+
export declare function deepClone<T>(obj: T): T;
|
|
14
|
+
/**
|
|
15
|
+
* Merge objects deeply
|
|
16
|
+
*/
|
|
17
|
+
export declare function deepMerge(target: any, source: any): any;
|
|
18
|
+
//# sourceMappingURL=normalizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../../utils/normalizer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAwCrF;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAEtC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,GAAG,CAYvD"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate CSS styles from block data
|
|
3
|
+
*/
|
|
4
|
+
export declare function generateBlockStyles(data: Record<string, any>, isMobile?: boolean): Record<string, string>;
|
|
5
|
+
/**
|
|
6
|
+
* Generate responsive CSS classes
|
|
7
|
+
*/
|
|
8
|
+
export declare function getResponsiveClasses(data: Record<string, any>): string[];
|
|
9
|
+
/**
|
|
10
|
+
* Generate the responsive CSS to be injected into the page
|
|
11
|
+
*/
|
|
12
|
+
export declare function getResponsiveCSS(): string;
|
|
13
|
+
//# sourceMappingURL=styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../utils/styles.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,QAAQ,GAAE,OAAe,GACvB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAqKxB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE,CAgBxE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CA8BzC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PageData } from '../core/types';
|
|
2
|
+
interface Props {
|
|
3
|
+
origin?: string;
|
|
4
|
+
initialPageData?: PageData;
|
|
5
|
+
componentConfigs?: any[];
|
|
6
|
+
}
|
|
7
|
+
declare const _default: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<Props> & Readonly<{}>, {
|
|
8
|
+
origin: string;
|
|
9
|
+
componentConfigs: any[];
|
|
10
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
|
|
11
|
+
export default _default;
|
|
12
|
+
//# sourceMappingURL=ExternalPreview.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExternalPreview.vue.d.ts","sourceRoot":"","sources":["../../views/ExternalPreview.vue"],"names":[],"mappings":"AAAA,OA8aO,KAAK,EAAiB,QAAQ,EAAE,MAAM,eAAe,CAAA;AAQ5D,UAAU,KAAK;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,QAAQ,CAAA;IAC1B,gBAAgB,CAAC,EAAE,GAAG,EAAE,CAAA;CACxB;;YAHS,MAAM;sBAEI,GAAG,EAAE;;AAoXzB,wBAQG"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { PageData } from '../core/types';
|
|
2
|
+
interface Props {
|
|
3
|
+
pageData: PageData;
|
|
4
|
+
mobileBreakpoint?: number;
|
|
5
|
+
}
|
|
6
|
+
declare const _default: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<Props> & Readonly<{}>, {
|
|
7
|
+
mobileBreakpoint: number;
|
|
8
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
|
|
9
|
+
export default _default;
|
|
10
|
+
//# sourceMappingURL=RenderPage.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RenderPage.vue.d.ts","sourceRoot":"","sources":["../../views/RenderPage.vue"],"names":[],"mappings":"AAAA,OAwIO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAO7C,UAAU,KAAK;IACd,QAAQ,EAAE,QAAQ,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CACzB;;sBADmB,MAAM;;AAsI1B,wBAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.config.d.ts","sourceRoot":"","sources":["../vite.config.ts"],"names":[],"mappings":";AASA,wBAoCG"}
|
package/index.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blox External Preview Library
|
|
3
|
+
* Main entry point
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Component exports
|
|
7
|
+
export * from './components'
|
|
8
|
+
// Configuration exports
|
|
9
|
+
export * from './config/baseComponents'
|
|
10
|
+
export * from './core/communication'
|
|
11
|
+
export * from './core/registry'
|
|
12
|
+
|
|
13
|
+
export * from './core/renderer'
|
|
14
|
+
// Core exports
|
|
15
|
+
export * from './core/types'
|
|
16
|
+
|
|
17
|
+
// Auto-registration helper
|
|
18
|
+
export { getAllComponentConfigs, registerBaseComponents } from './setup'
|
|
19
|
+
|
|
20
|
+
export * from './utils/normalizer'
|
|
21
|
+
|
|
22
|
+
// Utility exports
|
|
23
|
+
export * from './utils/styles'
|
|
24
|
+
// View exports
|
|
25
|
+
export { default as ExternalPreview } from './views/ExternalPreview.vue'
|
|
26
|
+
|
|
27
|
+
export { default as RenderPage } from './views/RenderPage.vue'
|
package/package.json
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bagelink/blox",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.5.3",
|
|
5
|
+
"description": "Blox page builder library for drag-and-drop page building and static data management",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Bagel Studio",
|
|
8
|
+
"email": "info@bagelstudio.co.il",
|
|
9
|
+
"url": "https://bagelstudio.co.il"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"homepage": "https://github.com/bageldb/bagelink/tree/master/packages/blox#readme",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/bageldb/bagelink.git",
|
|
16
|
+
"directory": "packages/blox"
|
|
17
|
+
},
|
|
18
|
+
"bugs": "https://github.com/bageldb/bagelink/issues",
|
|
19
|
+
"keywords": [
|
|
20
|
+
"blox",
|
|
21
|
+
"page-builder",
|
|
22
|
+
"preview",
|
|
23
|
+
"external-preview",
|
|
24
|
+
"cms",
|
|
25
|
+
"vue",
|
|
26
|
+
"component-library",
|
|
27
|
+
"drag-and-drop",
|
|
28
|
+
"static-pages"
|
|
29
|
+
],
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"exports": {
|
|
32
|
+
"./package.json": "./package.json",
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"require": "./dist/index.cjs",
|
|
36
|
+
"import": "./dist/index.mjs"
|
|
37
|
+
},
|
|
38
|
+
"./components": {
|
|
39
|
+
"types": "./dist/components/index.d.ts",
|
|
40
|
+
"import": "./dist/components/index.mjs"
|
|
41
|
+
},
|
|
42
|
+
"./core": {
|
|
43
|
+
"types": "./dist/core/types.d.ts",
|
|
44
|
+
"import": "./dist/core/types.mjs"
|
|
45
|
+
},
|
|
46
|
+
"./utils": {
|
|
47
|
+
"types": "./dist/utils/styles.d.ts",
|
|
48
|
+
"import": "./dist/utils/styles.mjs"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"main": "./dist/index.cjs",
|
|
52
|
+
"module": "./dist/index.mjs",
|
|
53
|
+
"types": "./dist/index.d.ts",
|
|
54
|
+
"typesVersions": {
|
|
55
|
+
"*": {
|
|
56
|
+
"*": [
|
|
57
|
+
"./dist/*",
|
|
58
|
+
"./dist/index.d.ts"
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"files": [
|
|
63
|
+
"dist",
|
|
64
|
+
"core",
|
|
65
|
+
"components",
|
|
66
|
+
"config",
|
|
67
|
+
"utils",
|
|
68
|
+
"views",
|
|
69
|
+
"setup.ts",
|
|
70
|
+
"index.ts",
|
|
71
|
+
"README.md"
|
|
72
|
+
],
|
|
73
|
+
"publishConfig": {
|
|
74
|
+
"access": "public"
|
|
75
|
+
},
|
|
76
|
+
"peerDependencies": {
|
|
77
|
+
"vue": "^3.3.0"
|
|
78
|
+
},
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"@vitejs/plugin-vue": "^5.0.0",
|
|
81
|
+
"@vue-macros/reactivity-transform": "^1.1.6",
|
|
82
|
+
"typescript": "^5.8.3",
|
|
83
|
+
"vite": "^5.0.0",
|
|
84
|
+
"vite-plugin-dts": "^4.0.0",
|
|
85
|
+
"vite-tsconfig-paths": "^5.0.0",
|
|
86
|
+
"vue": "^3.5.16",
|
|
87
|
+
"vue-tsc": "^2.0.0"
|
|
88
|
+
},
|
|
89
|
+
"scripts": {
|
|
90
|
+
"dev": "vite build --watch",
|
|
91
|
+
"build": "vite build",
|
|
92
|
+
"typecheck": "vue-tsc --noEmit"
|
|
93
|
+
}
|
|
94
|
+
}
|
package/setup.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blox Setup and Registration
|
|
3
|
+
*
|
|
4
|
+
* Handles automatic registration of base components and provides
|
|
5
|
+
* configuration helpers for external projects.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { baseComponentConfigs, type ComponentConfig } from './config/baseComponents'
|
|
9
|
+
import { registerComponent, registerComponents } from './core/registry'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Register all base components from the library
|
|
13
|
+
* Call this in your project's setup to make base components available
|
|
14
|
+
*/
|
|
15
|
+
export function registerBaseComponents(): void {
|
|
16
|
+
const componentMap: Record<string, any> = {}
|
|
17
|
+
|
|
18
|
+
baseComponentConfigs.forEach((config) => {
|
|
19
|
+
componentMap[config.id] = config.component
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
registerComponents(componentMap)
|
|
23
|
+
console.log('✅ Registered base components:', Object.keys(componentMap))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get all component configurations (base + custom)
|
|
28
|
+
* This returns both the Vue components and their editor schemas
|
|
29
|
+
*/
|
|
30
|
+
export function getAllComponentConfigs(customConfigs: ComponentConfig[] = []): ComponentConfig[] {
|
|
31
|
+
return [...baseComponentConfigs, ...customConfigs]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Helper to create a custom component configuration
|
|
36
|
+
*/
|
|
37
|
+
export function createComponentConfig(config: ComponentConfig): ComponentConfig {
|
|
38
|
+
return config
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Register a single custom component
|
|
43
|
+
*/
|
|
44
|
+
export function registerCustomComponent(config: ComponentConfig): void {
|
|
45
|
+
registerComponent(config.id, config.component)
|
|
46
|
+
console.log(`✅ Registered custom component: ${config.id}`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Register multiple custom components
|
|
51
|
+
*/
|
|
52
|
+
export function registerCustomComponents(configs: ComponentConfig[]): void {
|
|
53
|
+
configs.forEach((config) => {
|
|
54
|
+
registerCustomComponent(config)
|
|
55
|
+
})
|
|
56
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Normalization Utilities
|
|
3
|
+
*
|
|
4
|
+
* Normalizes component data for proper rendering
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Convert string values to proper types for component props
|
|
9
|
+
*/
|
|
10
|
+
export function normalizeComponentData(data: Record<string, any>): Record<string, any> {
|
|
11
|
+
const normalized: Record<string, any> = {}
|
|
12
|
+
|
|
13
|
+
// Fields that should remain as strings even if they look like numbers
|
|
14
|
+
const stringFields = ['title', 'subTitle', 'btnTxt', 'tag', 'customId', 'height', 'url', 'href']
|
|
15
|
+
|
|
16
|
+
for (const [key, value] of Object.entries(data)) {
|
|
17
|
+
if (typeof value === 'string') {
|
|
18
|
+
// Convert string booleans to actual booleans
|
|
19
|
+
if (value === 'true') {
|
|
20
|
+
normalized[key] = true
|
|
21
|
+
} else if (value === 'false') {
|
|
22
|
+
normalized[key] = false
|
|
23
|
+
} else if (!Number.isNaN(Number(value)) && value !== '' && !stringFields.includes(key)) {
|
|
24
|
+
// Convert string numbers to numbers (but not for text fields)
|
|
25
|
+
normalized[key] = Number(value)
|
|
26
|
+
} else {
|
|
27
|
+
normalized[key] = value
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
normalized[key] = value
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Provide default props to avoid Vue warnings for missing required props
|
|
35
|
+
if (!normalized.items) {
|
|
36
|
+
normalized.items = []
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Ensure height is always a string if it exists
|
|
40
|
+
if (normalized.height !== undefined && typeof normalized.height === 'number') {
|
|
41
|
+
normalized.height = String(normalized.height)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (normalized.height === 'auto') {
|
|
45
|
+
// Convert auto height strings to valid numbers for components that expect numbers
|
|
46
|
+
delete normalized.height
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return normalized
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Deep clone an object
|
|
54
|
+
*/
|
|
55
|
+
export function deepClone<T>(obj: T): T {
|
|
56
|
+
return JSON.parse(JSON.stringify(obj))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Merge objects deeply
|
|
61
|
+
*/
|
|
62
|
+
export function deepMerge(target: any, source: any): any {
|
|
63
|
+
const result = { ...target }
|
|
64
|
+
|
|
65
|
+
for (const key in source) {
|
|
66
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
67
|
+
result[key] = deepMerge(result[key] || {}, source[key])
|
|
68
|
+
} else {
|
|
69
|
+
result[key] = source[key]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return result
|
|
74
|
+
}
|
package/utils/styles.ts
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate CSS styles from block data
|
|
3
|
+
*/
|
|
4
|
+
export function generateBlockStyles(
|
|
5
|
+
data: Record<string, any>,
|
|
6
|
+
isMobile: boolean = false
|
|
7
|
+
): Record<string, string> {
|
|
8
|
+
const styles: Record<string, string> = {}
|
|
9
|
+
|
|
10
|
+
// Helper to coerce numeric-like values
|
|
11
|
+
function toNumber(v: any): number | null {
|
|
12
|
+
if (v === undefined || v === null || v === '') return null
|
|
13
|
+
const n = Number(v)
|
|
14
|
+
return Number.isNaN(n) ? null : n
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Helper to get value with mobile override
|
|
18
|
+
function getValue(desktopKey: string, mobileKey: string) {
|
|
19
|
+
if (isMobile && data[mobileKey] !== undefined && data[mobileKey] !== null) {
|
|
20
|
+
return data[mobileKey]
|
|
21
|
+
}
|
|
22
|
+
return data[desktopKey]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Margin and padding (use rem units) with mobile overrides
|
|
26
|
+
const mTop = toNumber(getValue('marginTop', 'marginTopMobile'))
|
|
27
|
+
const mBottom = toNumber(getValue('marginBottom', 'marginBottomMobile'))
|
|
28
|
+
const pad = toNumber(getValue('padding', 'paddingMobile'))
|
|
29
|
+
|
|
30
|
+
// Set margin values
|
|
31
|
+
if (mTop !== null) {
|
|
32
|
+
styles['margin-top'] = `${mTop}rem`
|
|
33
|
+
styles['--margin-top'] = `${mTop}rem`
|
|
34
|
+
} else {
|
|
35
|
+
styles['--margin-top'] = '0rem'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (mBottom !== null) {
|
|
39
|
+
styles['margin-bottom'] = `${mBottom}rem`
|
|
40
|
+
styles['--margin-bottom'] = `${mBottom}rem`
|
|
41
|
+
} else {
|
|
42
|
+
styles['--margin-bottom'] = '0rem'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (pad !== null) {
|
|
46
|
+
styles.padding = `${pad}rem`
|
|
47
|
+
styles['--block-padding'] = `${pad}rem`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Width behavior
|
|
51
|
+
const wDesktop = toNumber(data.width)
|
|
52
|
+
const wMobile = toNumber(data.widthMobile)
|
|
53
|
+
const wPercentDesktop = toNumber(data.widthPercent) || 96
|
|
54
|
+
const wPercentMobile = toNumber(data.widthPercentMobile) || wPercentDesktop
|
|
55
|
+
const fullWidthDesktop = data.fullWidth
|
|
56
|
+
|
|
57
|
+
// Set CSS variables for responsive width
|
|
58
|
+
styles['--max-width-desktop'] = wDesktop !== null ? `${wDesktop}px` : '800px'
|
|
59
|
+
styles['--max-width-mobile'] = wMobile !== null ? `${wMobile}px` : wDesktop !== null ? `${wDesktop}px` : '800px'
|
|
60
|
+
styles['--width-percent-desktop'] = `${wPercentDesktop}%`
|
|
61
|
+
styles['--width-percent-mobile'] = `${wPercentMobile}%`
|
|
62
|
+
|
|
63
|
+
// Set margin centering for non-full-width blocks
|
|
64
|
+
if (!(fullWidthDesktop === true || fullWidthDesktop === 'true') && wDesktop !== null) {
|
|
65
|
+
styles['margin-left'] = 'auto'
|
|
66
|
+
styles['margin-right'] = 'auto'
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Border styles
|
|
70
|
+
const borderW = toNumber(getValue('borderWidth', 'borderWidthMobile'))
|
|
71
|
+
if (borderW !== null && borderW > 0) {
|
|
72
|
+
styles['border-width'] = `${borderW}px`
|
|
73
|
+
styles['border-style'] = getValue('borderStyle', 'borderStyleMobile') || 'solid'
|
|
74
|
+
const borderColor = getValue('borderColor', 'borderColorMobile')
|
|
75
|
+
if (borderColor) {
|
|
76
|
+
styles['border-color'] = borderColor
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Center alignment
|
|
81
|
+
const isCenter = getValue('center', 'centerMobile')
|
|
82
|
+
if (isCenter === true || isCenter === 'true') {
|
|
83
|
+
styles['text-align'] = 'center'
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Border radius
|
|
87
|
+
const borderR = toNumber(getValue('borderRadius', 'borderRadiusMobile'))
|
|
88
|
+
if (borderR !== null) {
|
|
89
|
+
styles['border-radius'] = `${borderR}px`
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Colors with CSS variables
|
|
93
|
+
const bgColorDesktop = data.backgroundColor
|
|
94
|
+
const bgColorMobile = data.backgroundColorMobile
|
|
95
|
+
const textColorDesktop = data.textColor
|
|
96
|
+
const { textColorMobile } = data
|
|
97
|
+
|
|
98
|
+
if (bgColorDesktop) {
|
|
99
|
+
styles['--bg-color-desktop'] = bgColorDesktop
|
|
100
|
+
}
|
|
101
|
+
if (bgColorMobile) {
|
|
102
|
+
styles['--bg-color-mobile'] = bgColorMobile
|
|
103
|
+
}
|
|
104
|
+
if (textColorDesktop) {
|
|
105
|
+
styles['--text-color-desktop'] = textColorDesktop
|
|
106
|
+
}
|
|
107
|
+
if (textColorMobile) {
|
|
108
|
+
styles['--text-color-mobile'] = textColorMobile
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Shadow types
|
|
112
|
+
const shadowMap = {
|
|
113
|
+
none: 'none',
|
|
114
|
+
sm: '0 0 10px 0 rgba(0, 0, 0, 0.05)',
|
|
115
|
+
md: '0 0 20px 0 rgba(0, 0, 0, 0.15)',
|
|
116
|
+
lg: '0 0 30px 0 rgba(0, 0, 0, 0.15)',
|
|
117
|
+
xl: '0 0 40px 0 rgba(0, 0, 0, 0.5)',
|
|
118
|
+
custom: '0 4px 6px -1px rgba(0, 0, 0, 0.15)',
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const shadowType = getValue('shadowType', 'shadowTypeMobile')
|
|
122
|
+
if (shadowType && shadowType !== 'none') {
|
|
123
|
+
styles['box-shadow'] = shadowMap[shadowType as keyof typeof shadowMap] || shadowMap.md
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Z-index
|
|
127
|
+
const z = toNumber(getValue('zIndex', 'zIndexMobile'))
|
|
128
|
+
if (z !== null && z > 0) {
|
|
129
|
+
styles.position = 'relative'
|
|
130
|
+
styles['z-index'] = z.toString()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Visibility
|
|
134
|
+
if (!isMobile && data.showDesktop === false) {
|
|
135
|
+
styles.display = 'none'
|
|
136
|
+
} else if (isMobile && data.showMobile === false) {
|
|
137
|
+
styles.display = 'none'
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Font family
|
|
141
|
+
const fontFamilyDesktop = data.fontFamily
|
|
142
|
+
const { fontFamilyMobile } = data
|
|
143
|
+
|
|
144
|
+
if (isMobile && fontFamilyMobile && fontFamilyMobile.trim() !== '') {
|
|
145
|
+
styles['font-family'] = `"${fontFamilyMobile}", sans-serif`
|
|
146
|
+
} else if (fontFamilyDesktop && fontFamilyDesktop.trim() !== '') {
|
|
147
|
+
styles['font-family'] = `"${fontFamilyDesktop}", sans-serif`
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Custom CSS
|
|
151
|
+
if (data.customCSS) {
|
|
152
|
+
try {
|
|
153
|
+
const customRules = data.customCSS
|
|
154
|
+
.split(';')
|
|
155
|
+
.filter((rule: string) => rule.trim())
|
|
156
|
+
.map((rule: string) => rule.trim().split(':'))
|
|
157
|
+
.filter((parts: string[]) => parts.length === 2)
|
|
158
|
+
|
|
159
|
+
customRules.forEach(([property, value]: [string, string]) => {
|
|
160
|
+
const prop = property.trim()
|
|
161
|
+
const val = value.trim()
|
|
162
|
+
if (prop && val) {
|
|
163
|
+
styles[prop] = val
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
} catch (e) {
|
|
167
|
+
console.warn('Invalid custom CSS:', data.customCSS)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return styles
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Generate responsive CSS classes
|
|
176
|
+
*/
|
|
177
|
+
export function getResponsiveClasses(data: Record<string, any>): string[] {
|
|
178
|
+
const classes = ['blox-block', 'responsive-colors']
|
|
179
|
+
|
|
180
|
+
const effectiveDesktopFullWidth = Boolean(data.fullWidth)
|
|
181
|
+
const effectiveMobileFullWidth
|
|
182
|
+
= data.fullWidthMobile !== null ? Boolean(data.fullWidthMobile) : effectiveDesktopFullWidth
|
|
183
|
+
|
|
184
|
+
if (effectiveDesktopFullWidth) {
|
|
185
|
+
classes.push('full-width-desktop')
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (effectiveMobileFullWidth) {
|
|
189
|
+
classes.push('full-width-mobile')
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return classes
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Generate the responsive CSS to be injected into the page
|
|
197
|
+
*/
|
|
198
|
+
export function getResponsiveCSS(): string {
|
|
199
|
+
return `
|
|
200
|
+
/* Blox Responsive System */
|
|
201
|
+
.responsive-colors {
|
|
202
|
+
color: var(--text-color-desktop, inherit);
|
|
203
|
+
background-color: var(--bg-color-desktop, transparent);
|
|
204
|
+
margin-inline: auto;
|
|
205
|
+
max-width: var(--max-width-desktop, 800px);
|
|
206
|
+
width: var(--width-percent-desktop, 96%);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.responsive-colors.full-width-desktop {
|
|
210
|
+
width: 100% !important;
|
|
211
|
+
max-width: none !important;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
@media (max-width: 910px) {
|
|
215
|
+
.responsive-colors {
|
|
216
|
+
color: var(--text-color-mobile, var(--text-color-desktop, inherit));
|
|
217
|
+
background-color: var(--bg-color-mobile, var(--bg-color-desktop, transparent));
|
|
218
|
+
max-width: var(--max-width-mobile, var(--max-width-desktop, 800px));
|
|
219
|
+
width: var(--width-percent-mobile, var(--width-percent-desktop, 96%));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.responsive-colors.full-width-mobile {
|
|
223
|
+
width: 100% !important;
|
|
224
|
+
max-width: none !important;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
`
|
|
228
|
+
}
|