@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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +844 -0
  3. package/components/base/Button.vue +140 -0
  4. package/components/base/Container.vue +64 -0
  5. package/components/base/Image.vue +75 -0
  6. package/components/base/Spacer.vue +33 -0
  7. package/components/base/Text.vue +37 -0
  8. package/components/base/Title.vue +55 -0
  9. package/components/index.ts +20 -0
  10. package/config/baseComponents.ts +342 -0
  11. package/core/communication.ts +140 -0
  12. package/core/registry.ts +108 -0
  13. package/core/renderer.ts +217 -0
  14. package/core/types.ts +148 -0
  15. package/dist/blox.css +296 -0
  16. package/dist/components/base/Button.vue.d.ts +26 -0
  17. package/dist/components/base/Button.vue.d.ts.map +1 -0
  18. package/dist/components/base/Container.vue.d.ts +37 -0
  19. package/dist/components/base/Container.vue.d.ts.map +1 -0
  20. package/dist/components/base/Image.vue.d.ts +26 -0
  21. package/dist/components/base/Image.vue.d.ts.map +1 -0
  22. package/dist/components/base/Spacer.vue.d.ts +16 -0
  23. package/dist/components/base/Spacer.vue.d.ts.map +1 -0
  24. package/dist/components/base/Text.vue.d.ts +13 -0
  25. package/dist/components/base/Text.vue.d.ts.map +1 -0
  26. package/dist/components/base/Title.vue.d.ts +14 -0
  27. package/dist/components/base/Title.vue.d.ts.map +1 -0
  28. package/dist/components/index.d.ts +18 -0
  29. package/dist/components/index.d.ts.map +1 -0
  30. package/dist/config/baseComponents.d.ts +39 -0
  31. package/dist/config/baseComponents.d.ts.map +1 -0
  32. package/dist/core/communication.d.ts +44 -0
  33. package/dist/core/communication.d.ts.map +1 -0
  34. package/dist/core/registry.d.ts +56 -0
  35. package/dist/core/registry.d.ts.map +1 -0
  36. package/dist/core/renderer.d.ts +27 -0
  37. package/dist/core/renderer.d.ts.map +1 -0
  38. package/dist/core/types.d.ts +105 -0
  39. package/dist/core/types.d.ts.map +1 -0
  40. package/dist/index.cjs +1305 -0
  41. package/dist/index.d.ts +16 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.mjs +1260 -0
  44. package/dist/setup.d.ts +24 -0
  45. package/dist/setup.d.ts.map +1 -0
  46. package/dist/utils/normalizer.d.ts +18 -0
  47. package/dist/utils/normalizer.d.ts.map +1 -0
  48. package/dist/utils/styles.d.ts +13 -0
  49. package/dist/utils/styles.d.ts.map +1 -0
  50. package/dist/views/ExternalPreview.vue.d.ts +12 -0
  51. package/dist/views/ExternalPreview.vue.d.ts.map +1 -0
  52. package/dist/views/RenderPage.vue.d.ts +10 -0
  53. package/dist/views/RenderPage.vue.d.ts.map +1 -0
  54. package/dist/vite.config.d.ts +3 -0
  55. package/dist/vite.config.d.ts.map +1 -0
  56. package/index.ts +27 -0
  57. package/package.json +94 -0
  58. package/setup.ts +56 -0
  59. package/utils/normalizer.ts +74 -0
  60. package/utils/styles.ts +228 -0
  61. package/views/ExternalPreview.vue +420 -0
  62. package/views/RenderPage.vue +127 -0
@@ -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,3 @@
1
+ declare const _default: import('vite').UserConfigFnObject;
2
+ export default _default;
3
+ //# sourceMappingURL=vite.config.d.ts.map
@@ -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
+ }
@@ -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
+ }