@marvalt/dstyler 0.1.18 → 0.1.21
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/components/AutoBlock.d.ts +8 -0
- package/dist/components/AutoBlock.d.ts.map +1 -1
- package/dist/components/AutoSection.d.ts +4 -1
- package/dist/components/AutoSection.d.ts.map +1 -1
- package/dist/components/StyledPage.d.ts +6 -0
- package/dist/components/StyledPage.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/engine/detectSections.d.ts.map +1 -1
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/renderBlock.d.ts +0 -3
- package/dist/engine/renderBlock.d.ts.map +1 -1
- package/dist/engine/selectTemplate.d.ts.map +1 -1
- package/dist/index.d.ts +159 -90
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +478 -500
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +481 -499
- package/dist/index.js.map +1 -1
- package/dist/providers/ThemeProvider.d.ts.map +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/templates/FooterMinimal.d.ts.map +1 -1
- package/dist/templates/HeroDefault.d.ts.map +1 -1
- package/dist/templates/SectionBase.d.ts.map +1 -1
- package/dist/templates/SectionWave.d.ts.map +1 -1
- package/dist/templates/blocks/BlockButton.d.ts +6 -2
- package/dist/templates/blocks/BlockButton.d.ts.map +1 -1
- package/dist/templates/blocks/BlockForm.d.ts +6 -2
- package/dist/templates/blocks/BlockForm.d.ts.map +1 -1
- package/dist/templates/blocks/BlockImage.d.ts +6 -2
- package/dist/templates/blocks/BlockImage.d.ts.map +1 -1
- package/dist/templates/blocks/BlockText.d.ts +6 -2
- package/dist/templates/blocks/BlockText.d.ts.map +1 -1
- package/dist/templates/blocks/index.d.ts.map +1 -1
- package/dist/templates/index.d.ts +1 -0
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/tokens/css-vars.d.ts.map +1 -1
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/tokens.d.ts.map +1 -1
- package/dist/tokens/wordpress-to-tokens.d.ts.map +1 -1
- package/dist/types/block.d.ts +2 -0
- package/dist/types/block.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/section.d.ts.map +1 -1
- package/dist/types/theme.d.ts.map +1 -1
- package/dist/utils/createColorMapper.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/responsive.d.ts.map +1 -1
- package/package.json +52 -69
- package/README.md +0 -76
package/dist/index.js
CHANGED
|
@@ -2,239 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
var React = require('react');
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* @license GPL-3.0-or-later
|
|
7
|
-
*
|
|
8
|
-
* This file is part of the MarVAlt Open SDK.
|
|
9
|
-
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
10
|
-
*/
|
|
11
|
-
const defaultTokens = {
|
|
12
|
-
colors: {
|
|
13
|
-
primary: '#1a73e8',
|
|
14
|
-
secondary: '#34a853',
|
|
15
|
-
background: '#ffffff',
|
|
16
|
-
text: '#222222',
|
|
17
|
-
heading: '#222222',
|
|
18
|
-
default: '#ffffff',
|
|
19
|
-
alternate: '#f5f5f5',
|
|
20
|
-
highlight: '#e8f0fe',
|
|
21
|
-
},
|
|
22
|
-
spacing: {
|
|
23
|
-
sm: 8,
|
|
24
|
-
md: 16,
|
|
25
|
-
lg: 32,
|
|
26
|
-
xl: 64,
|
|
27
|
-
},
|
|
28
|
-
typography: {
|
|
29
|
-
body: 'Inter, sans-serif',
|
|
30
|
-
heading: 'Inter, sans-serif',
|
|
31
|
-
scale: 1.2,
|
|
32
|
-
},
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* @license GPL-3.0-or-later
|
|
37
|
-
*
|
|
38
|
-
* This file is part of the MarVAlt Open SDK.
|
|
39
|
-
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
40
|
-
*/
|
|
41
|
-
/**
|
|
42
|
-
* Convert WordPress theme styles to design tokens
|
|
43
|
-
*/
|
|
44
|
-
function convertWordPressToTokens(wpStyles) {
|
|
45
|
-
if (!wpStyles) {
|
|
46
|
-
return defaultTokens;
|
|
47
|
-
}
|
|
48
|
-
const palette = wpStyles.theme_palette || {};
|
|
49
|
-
// Typography priority: Customizer (wpStyles.typography) > Theme Editor fonts (if present separately) > defaults
|
|
50
|
-
// Note: Theme Editor fonts are not explicitly separated in current schema; assume Customizer holds them if provided.
|
|
51
|
-
const tokens = {
|
|
52
|
-
...defaultTokens,
|
|
53
|
-
colors: {
|
|
54
|
-
...defaultTokens.colors,
|
|
55
|
-
...(wpStyles.colors?.primary && { primary: wpStyles.colors.primary }),
|
|
56
|
-
...(wpStyles.colors?.secondary && { secondary: wpStyles.colors.secondary }),
|
|
57
|
-
...(wpStyles.colors?.text && { text: wpStyles.colors.text }),
|
|
58
|
-
...(wpStyles.colors?.heading && { heading: wpStyles.colors.heading }),
|
|
59
|
-
...(wpStyles.colors?.background && {
|
|
60
|
-
background: wpStyles.colors.background,
|
|
61
|
-
default: wpStyles.colors.background,
|
|
62
|
-
}),
|
|
63
|
-
},
|
|
64
|
-
typography: {
|
|
65
|
-
...defaultTokens.typography,
|
|
66
|
-
...(wpStyles.typography?.heading_font_family && {
|
|
67
|
-
heading: wpStyles.typography.heading_font_family
|
|
68
|
-
}),
|
|
69
|
-
...(wpStyles.typography?.body_font_family && {
|
|
70
|
-
body: wpStyles.typography.body_font_family
|
|
71
|
-
}),
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
|
-
// Ensure heading color falls back to text if not provided
|
|
75
|
-
if (!tokens.colors.heading) {
|
|
76
|
-
tokens.colors.heading =
|
|
77
|
-
// Prefer explicit heading color if provided by Theme Editor
|
|
78
|
-
wpStyles.colors?.heading ||
|
|
79
|
-
// Then Theme Editor palette contrast (matches heading color choice in editor)
|
|
80
|
-
palette['contrast'] ||
|
|
81
|
-
// Then palette primary
|
|
82
|
-
palette['primary'] ||
|
|
83
|
-
// Then explicit primary fallback
|
|
84
|
-
wpStyles.colors?.primary ||
|
|
85
|
-
tokens.colors.text;
|
|
86
|
-
}
|
|
87
|
-
// Generate alternate and highlight colors from primary/background
|
|
88
|
-
if (tokens.colors.primary && tokens.colors.background) {
|
|
89
|
-
// Alternate: slightly darker/lighter than background
|
|
90
|
-
tokens.colors.alternate = adjustBrightness(tokens.colors.background, -0.05);
|
|
91
|
-
// Highlight: tinted with primary color
|
|
92
|
-
tokens.colors.highlight = blendColors(tokens.colors.background, tokens.colors.primary, 0.1);
|
|
93
|
-
}
|
|
94
|
-
return tokens;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Adjust brightness of a hex color
|
|
98
|
-
*/
|
|
99
|
-
function adjustBrightness(hex, amount) {
|
|
100
|
-
const num = parseInt(hex.replace('#', ''), 16);
|
|
101
|
-
const r = Math.max(0, Math.min(255, ((num >> 16) & 0xff) + (amount * 255)));
|
|
102
|
-
const g = Math.max(0, Math.min(255, ((num >> 8) & 0xff) + (amount * 255)));
|
|
103
|
-
const b = Math.max(0, Math.min(255, (num & 0xff) + (amount * 255)));
|
|
104
|
-
return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Blend two colors
|
|
108
|
-
*/
|
|
109
|
-
function blendColors(color1, color2, ratio) {
|
|
110
|
-
const c1 = hexToRgb(color1);
|
|
111
|
-
const c2 = hexToRgb(color2);
|
|
112
|
-
if (!c1 || !c2)
|
|
113
|
-
return color1;
|
|
114
|
-
const r = Math.round(c1.r + (c2.r - c1.r) * ratio);
|
|
115
|
-
const g = Math.round(c1.g + (c2.g - c1.g) * ratio);
|
|
116
|
-
const b = Math.round(c1.b + (c2.b - c1.b) * ratio);
|
|
117
|
-
return `rgb(${r}, ${g}, ${b})`;
|
|
118
|
-
}
|
|
119
|
-
function hexToRgb(hex) {
|
|
120
|
-
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
121
|
-
return result
|
|
122
|
-
? {
|
|
123
|
-
r: parseInt(result[1], 16),
|
|
124
|
-
g: parseInt(result[2], 16),
|
|
125
|
-
b: parseInt(result[3], 16),
|
|
126
|
-
}
|
|
127
|
-
: null;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* @license GPL-3.0-or-later
|
|
132
|
-
*
|
|
133
|
-
* This file is part of the MarVAlt Open SDK.
|
|
134
|
-
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
135
|
-
*/
|
|
136
|
-
const THEME_STYLE_ID = 'dstyler-theme-vars';
|
|
137
|
-
function tokensToCSS(tokens) {
|
|
138
|
-
return `
|
|
139
|
-
--dstyler-color-primary: ${tokens.colors.primary};
|
|
140
|
-
--dstyler-color-secondary: ${tokens.colors.secondary};
|
|
141
|
-
--dstyler-color-background: ${tokens.colors.background};
|
|
142
|
-
--dstyler-color-text: ${tokens.colors.text};
|
|
143
|
-
--dstyler-color-heading: ${tokens.colors.heading || tokens.colors.text};
|
|
144
|
-
--dstyler-color-default: ${tokens.colors.default};
|
|
145
|
-
--dstyler-color-alternate: ${tokens.colors.alternate};
|
|
146
|
-
--dstyler-color-highlight: ${tokens.colors.highlight};
|
|
147
|
-
--dstyler-spacing-sm: ${tokens.spacing.sm}px;
|
|
148
|
-
--dstyler-spacing-md: ${tokens.spacing.md}px;
|
|
149
|
-
--dstyler-spacing-lg: ${tokens.spacing.lg}px;
|
|
150
|
-
--dstyler-spacing-xl: ${tokens.spacing.xl}px;
|
|
151
|
-
--dstyler-font-body: ${tokens.typography.body};
|
|
152
|
-
--dstyler-font-heading: ${tokens.typography.heading};
|
|
153
|
-
--dstyler-font-scale: ${tokens.typography.scale};
|
|
154
|
-
`;
|
|
155
|
-
}
|
|
156
|
-
function ensureStyleElement() {
|
|
157
|
-
if (typeof document === 'undefined')
|
|
158
|
-
return null;
|
|
159
|
-
let el = document.getElementById(THEME_STYLE_ID);
|
|
160
|
-
if (!el) {
|
|
161
|
-
el = document.createElement('style');
|
|
162
|
-
el.id = THEME_STYLE_ID;
|
|
163
|
-
document.head.appendChild(el);
|
|
164
|
-
}
|
|
165
|
-
return el;
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Inject CSS variables from design tokens into :root
|
|
169
|
-
*/
|
|
170
|
-
function injectCSSVariables(tokens) {
|
|
171
|
-
const el = ensureStyleElement();
|
|
172
|
-
if (!el)
|
|
173
|
-
return;
|
|
174
|
-
el.textContent = `:root { ${tokensToCSS(tokens)} }`;
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Inject CSS variables for both light and dark themes.
|
|
178
|
-
* Light applies to :root, dark applies when [data-theme="dark"] is present.
|
|
179
|
-
*/
|
|
180
|
-
function injectDualThemeCSS(light, dark) {
|
|
181
|
-
const el = ensureStyleElement();
|
|
182
|
-
if (!el)
|
|
183
|
-
return;
|
|
184
|
-
el.textContent = `
|
|
185
|
-
:root { ${tokensToCSS(light)} }
|
|
186
|
-
:root[data-theme="dark"] { ${tokensToCSS(dark)} }
|
|
187
|
-
`;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* @license GPL-3.0-or-later
|
|
192
|
-
*
|
|
193
|
-
* This file is part of the MarVAlt Open SDK.
|
|
194
|
-
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
195
|
-
*/
|
|
196
|
-
/**
|
|
197
|
-
* ThemeProvider - Provides design tokens to the application
|
|
198
|
-
*
|
|
199
|
-
* Priority order:
|
|
200
|
-
* 1. customTokens (highest priority - overrides everything)
|
|
201
|
-
* 2. WordPress styles (converted to tokens)
|
|
202
|
-
* 3. Default tokens (fallback)
|
|
203
|
-
*/
|
|
204
|
-
const ThemeProvider = ({ children, wpStyles, customTokens, darkWpStyles, darkTokens, mode = 'light', }) => {
|
|
205
|
-
React.useEffect(() => {
|
|
206
|
-
// Light tokens: custom -> wpStyles -> default
|
|
207
|
-
const lightTokens = customTokens || convertWordPressToTokens(wpStyles);
|
|
208
|
-
// Dark tokens: provided -> wpStylesDark -> fallback to light
|
|
209
|
-
const resolvedDarkTokens = darkTokens || (darkWpStyles ? convertWordPressToTokens(darkWpStyles) : lightTokens);
|
|
210
|
-
if (mode === 'dark') {
|
|
211
|
-
injectDualThemeCSS(lightTokens, resolvedDarkTokens);
|
|
212
|
-
if (typeof document !== 'undefined') {
|
|
213
|
-
document.documentElement.setAttribute('data-theme', 'dark');
|
|
214
|
-
}
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
if (mode === 'system') {
|
|
218
|
-
const prefersDark = typeof window !== 'undefined' && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
219
|
-
injectDualThemeCSS(lightTokens, resolvedDarkTokens);
|
|
220
|
-
if (typeof document !== 'undefined') {
|
|
221
|
-
document.documentElement.setAttribute('data-theme', prefersDark ? 'dark' : 'light');
|
|
222
|
-
}
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
// Default to light
|
|
226
|
-
injectDualThemeCSS(lightTokens, resolvedDarkTokens);
|
|
227
|
-
if (typeof document !== 'undefined') {
|
|
228
|
-
document.documentElement.setAttribute('data-theme', 'light');
|
|
229
|
-
}
|
|
230
|
-
}, [wpStyles, customTokens, darkWpStyles, darkTokens, mode]);
|
|
231
|
-
// Return children if provided, otherwise return null (theme still applies via useEffect)
|
|
232
|
-
if (children === undefined || children === null) {
|
|
233
|
-
return null;
|
|
234
|
-
}
|
|
235
|
-
return React.createElement(React.Fragment, null, children);
|
|
236
|
-
};
|
|
237
|
-
|
|
238
5
|
var jsxRuntime = {exports: {}};
|
|
239
6
|
|
|
240
7
|
var reactJsxRuntime_production = {};
|
|
@@ -650,21 +417,80 @@ if (process.env.NODE_ENV === 'production') {
|
|
|
650
417
|
var jsxRuntimeExports = jsxRuntime.exports;
|
|
651
418
|
|
|
652
419
|
/**
|
|
653
|
-
*
|
|
654
|
-
*
|
|
655
|
-
*
|
|
656
|
-
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
420
|
+
* Detect sections from WordPress blocks
|
|
421
|
+
* Sections are defined by core/group blocks (with or without alignment)
|
|
422
|
+
* Sub-sections are core/columns blocks inside groups
|
|
657
423
|
*/
|
|
424
|
+
function detectSections(blocks) {
|
|
425
|
+
const sections = [];
|
|
426
|
+
let sectionIndex = 0;
|
|
427
|
+
for (const block of blocks) {
|
|
428
|
+
// core/group blocks are sections
|
|
429
|
+
if (block.name === 'core/group') {
|
|
430
|
+
const attrs = block.attributes || {};
|
|
431
|
+
const className = attrs.className;
|
|
432
|
+
const customId = attrs.id;
|
|
433
|
+
// Extract blocks from innerBlocks
|
|
434
|
+
const innerBlocks = block.innerBlocks || [];
|
|
435
|
+
sections.push({
|
|
436
|
+
id: `section-${sectionIndex}`,
|
|
437
|
+
type: 'section',
|
|
438
|
+
block,
|
|
439
|
+
blocks: innerBlocks,
|
|
440
|
+
index: sectionIndex,
|
|
441
|
+
customClass: className,
|
|
442
|
+
customId,
|
|
443
|
+
});
|
|
444
|
+
sectionIndex++;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return sections;
|
|
448
|
+
}
|
|
658
449
|
/**
|
|
659
|
-
*
|
|
450
|
+
* Determine section style based on alternating logic and custom classes
|
|
451
|
+
* Rules:
|
|
452
|
+
* - default is always first
|
|
453
|
+
* - after default follows alternate
|
|
454
|
+
* - highlight resets - next section is default
|
|
660
455
|
*/
|
|
661
|
-
function
|
|
662
|
-
const block = section.block;
|
|
663
|
-
const attrs = block.attributes || {};
|
|
456
|
+
function determineSectionStyle(section, previousStyle) {
|
|
664
457
|
const className = section.customClass?.toLowerCase() || '';
|
|
665
|
-
// Check for
|
|
666
|
-
if (
|
|
667
|
-
return '
|
|
458
|
+
// Check for custom class overrides first
|
|
459
|
+
if (className.includes('highlight') || className.includes('highlight-section')) {
|
|
460
|
+
return 'highlight';
|
|
461
|
+
}
|
|
462
|
+
if (className.includes('dark') || className.includes('dark-section')) {
|
|
463
|
+
return 'alternate';
|
|
464
|
+
}
|
|
465
|
+
if (className.includes('default') || className.includes('default-section')) {
|
|
466
|
+
return 'default';
|
|
467
|
+
}
|
|
468
|
+
// Alternating logic
|
|
469
|
+
if (section.index === 0) {
|
|
470
|
+
return 'default';
|
|
471
|
+
}
|
|
472
|
+
if (previousStyle === 'highlight') {
|
|
473
|
+
return 'default';
|
|
474
|
+
}
|
|
475
|
+
if (previousStyle === 'default') {
|
|
476
|
+
return 'alternate';
|
|
477
|
+
}
|
|
478
|
+
if (previousStyle === 'alternate') {
|
|
479
|
+
return 'default';
|
|
480
|
+
}
|
|
481
|
+
return 'default';
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Select template component based on section properties
|
|
486
|
+
*/
|
|
487
|
+
function selectTemplate(section) {
|
|
488
|
+
const block = section.block;
|
|
489
|
+
const attrs = block.attributes || {};
|
|
490
|
+
const className = section.customClass?.toLowerCase() || '';
|
|
491
|
+
// Check for hero/cover blocks
|
|
492
|
+
if (block.name === 'core/cover') {
|
|
493
|
+
return 'HeroDefault';
|
|
668
494
|
}
|
|
669
495
|
// Check for footer (could be detected by block name or class)
|
|
670
496
|
if (className.includes('footer') || block.name === 'core/footer') {
|
|
@@ -678,101 +504,6 @@ function selectTemplate(section) {
|
|
|
678
504
|
return 'SectionBase';
|
|
679
505
|
}
|
|
680
506
|
|
|
681
|
-
const SectionBase = ({ section, children }) => {
|
|
682
|
-
const style = section.style || 'default';
|
|
683
|
-
const bgColor = `var(--dstyler-color-${style})`;
|
|
684
|
-
const className = [
|
|
685
|
-
'dstyler-section',
|
|
686
|
-
`dstyler-section-${style}`,
|
|
687
|
-
section.customClass,
|
|
688
|
-
].filter(Boolean).join(' ');
|
|
689
|
-
return (jsxRuntimeExports.jsx("section", { className: className, id: section.customId, style: {
|
|
690
|
-
backgroundColor: bgColor,
|
|
691
|
-
padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
|
|
692
|
-
}, children: jsxRuntimeExports.jsx("div", { style: {
|
|
693
|
-
maxWidth: 'var(--dstyler-container-max-width, 1200px)',
|
|
694
|
-
margin: '0 auto',
|
|
695
|
-
}, children: children }) }));
|
|
696
|
-
};
|
|
697
|
-
|
|
698
|
-
const SectionWave = ({ section, children, waveTop = true, waveBottom = true, }) => {
|
|
699
|
-
const style = section.style || 'default';
|
|
700
|
-
const bgColor = `var(--dstyler-color-${style})`;
|
|
701
|
-
const className = [
|
|
702
|
-
'dstyler-section',
|
|
703
|
-
'dstyler-section-wave',
|
|
704
|
-
`dstyler-section-${style}`,
|
|
705
|
-
section.customClass,
|
|
706
|
-
].filter(Boolean).join(' ');
|
|
707
|
-
// Wave SVG
|
|
708
|
-
const waveSvg = (jsxRuntimeExports.jsx("svg", { viewBox: "0 0 1440 120", preserveAspectRatio: "none", style: {
|
|
709
|
-
width: '100%',
|
|
710
|
-
height: '60px',
|
|
711
|
-
display: 'block',
|
|
712
|
-
fill: bgColor,
|
|
713
|
-
}, children: jsxRuntimeExports.jsx("path", { d: "M0,60 C240,0 480,120 720,60 C960,0 1200,120 1440,60 L1440,120 L0,120 Z" }) }));
|
|
714
|
-
return (jsxRuntimeExports.jsxs("section", { className: className, id: section.customId, style: {
|
|
715
|
-
backgroundColor: bgColor,
|
|
716
|
-
position: 'relative',
|
|
717
|
-
}, children: [waveTop && (jsxRuntimeExports.jsx("div", { style: { marginTop: '-1px' }, children: waveSvg })), jsxRuntimeExports.jsx("div", { style: {
|
|
718
|
-
padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
|
|
719
|
-
}, children: jsxRuntimeExports.jsx("div", { style: {
|
|
720
|
-
maxWidth: 'var(--dstyler-container-max-width, 1200px)',
|
|
721
|
-
margin: '0 auto',
|
|
722
|
-
}, children: children }) }), waveBottom && (jsxRuntimeExports.jsx("div", { style: { marginBottom: '-1px', transform: 'rotate(180deg)' }, children: waveSvg }))] }));
|
|
723
|
-
};
|
|
724
|
-
|
|
725
|
-
const HeroDefault = ({ section, children }) => {
|
|
726
|
-
const style = section.style || 'default';
|
|
727
|
-
const bgColor = `var(--dstyler-color-${style})`;
|
|
728
|
-
const className = [
|
|
729
|
-
'dstyler-hero',
|
|
730
|
-
'dstyler-hero-default',
|
|
731
|
-
`dstyler-hero-${style}`,
|
|
732
|
-
section.customClass,
|
|
733
|
-
].filter(Boolean).join(' ');
|
|
734
|
-
return (jsxRuntimeExports.jsx("section", { className: className, id: section.customId, style: {
|
|
735
|
-
backgroundColor: bgColor,
|
|
736
|
-
padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
|
|
737
|
-
minHeight: '50vh',
|
|
738
|
-
display: 'flex',
|
|
739
|
-
alignItems: 'center',
|
|
740
|
-
justifyContent: 'center',
|
|
741
|
-
}, children: jsxRuntimeExports.jsx("div", { style: {
|
|
742
|
-
maxWidth: 'var(--dstyler-container-max-width, 1200px)',
|
|
743
|
-
margin: '0 auto',
|
|
744
|
-
textAlign: 'center',
|
|
745
|
-
}, children: children }) }));
|
|
746
|
-
};
|
|
747
|
-
|
|
748
|
-
const FooterMinimal = ({ section, children }) => {
|
|
749
|
-
const style = section.style || 'default';
|
|
750
|
-
const bgColor = `var(--dstyler-color-${style})`;
|
|
751
|
-
const className = [
|
|
752
|
-
'dstyler-footer',
|
|
753
|
-
'dstyler-footer-minimal',
|
|
754
|
-
`dstyler-footer-${style}`,
|
|
755
|
-
section.customClass,
|
|
756
|
-
].filter(Boolean).join(' ');
|
|
757
|
-
return (jsxRuntimeExports.jsx("footer", { className: className, id: section.customId, style: {
|
|
758
|
-
backgroundColor: bgColor,
|
|
759
|
-
padding: 'var(--dstyler-spacing-lg) var(--dstyler-spacing-md)',
|
|
760
|
-
borderTop: '1px solid var(--dstyler-color-alternate)',
|
|
761
|
-
}, children: jsxRuntimeExports.jsx("div", { style: {
|
|
762
|
-
maxWidth: 'var(--dstyler-container-max-width, 1200px)',
|
|
763
|
-
margin: '0 auto',
|
|
764
|
-
textAlign: 'center',
|
|
765
|
-
fontSize: '0.875rem',
|
|
766
|
-
color: 'var(--dstyler-color-text)',
|
|
767
|
-
}, children: children }) }));
|
|
768
|
-
};
|
|
769
|
-
|
|
770
|
-
/**
|
|
771
|
-
* @license GPL-3.0-or-later
|
|
772
|
-
*
|
|
773
|
-
* This file is part of the MarVAlt Open SDK.
|
|
774
|
-
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
775
|
-
*/
|
|
776
507
|
/**
|
|
777
508
|
* Select block renderer based on block type
|
|
778
509
|
*/
|
|
@@ -793,7 +524,7 @@ function selectBlockRenderer(block) {
|
|
|
793
524
|
case 'core/shortcode':
|
|
794
525
|
// Check if it's a form shortcode
|
|
795
526
|
const attrs = block.attributes || {};
|
|
796
|
-
const content = attrs.content || attrs.html || '';
|
|
527
|
+
const content = (attrs.content || attrs.html || '');
|
|
797
528
|
if (content.includes('[gravityform') || content.includes('[mauticform')) {
|
|
798
529
|
return 'BlockForm';
|
|
799
530
|
}
|
|
@@ -805,7 +536,7 @@ function selectBlockRenderer(block) {
|
|
|
805
536
|
|
|
806
537
|
const BlockText = ({ block, className }) => {
|
|
807
538
|
const attrs = block.attributes || {};
|
|
808
|
-
const content = attrs.content || attrs.text || '';
|
|
539
|
+
const content = (attrs.content || attrs.text || '');
|
|
809
540
|
const tagName = block.name === 'core/heading'
|
|
810
541
|
? `h${attrs.level || 2}`
|
|
811
542
|
: 'p';
|
|
@@ -825,40 +556,38 @@ const BlockText = ({ block, className }) => {
|
|
|
825
556
|
|
|
826
557
|
const BlockImage = ({ block, className }) => {
|
|
827
558
|
const attrs = block.attributes || {};
|
|
828
|
-
const url = attrs.url || attrs.sourceUrl || '';
|
|
829
|
-
const alt = attrs.alt || attrs.altText || '';
|
|
559
|
+
const url = (attrs.url || attrs.sourceUrl || '');
|
|
560
|
+
const alt = (attrs.alt || attrs.altText || '');
|
|
830
561
|
const width = attrs.width;
|
|
831
562
|
const height = attrs.height;
|
|
832
563
|
if (!url)
|
|
833
564
|
return null;
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
return (jsxRuntimeExports.jsx("img", { src: url, alt: alt, width: width, height: height, className: className, style: style, loading: "lazy" }));
|
|
565
|
+
return (jsxRuntimeExports.jsx("img", { src: url, alt: alt, width: width, height: height, className: className, style: {
|
|
566
|
+
maxWidth: '100%',
|
|
567
|
+
height: 'auto',
|
|
568
|
+
marginBottom: 'var(--dstyler-spacing-md)',
|
|
569
|
+
borderRadius: '8px',
|
|
570
|
+
}, loading: "lazy" }));
|
|
841
571
|
};
|
|
842
572
|
|
|
843
573
|
const BlockButton = ({ block, className }) => {
|
|
844
574
|
const attrs = block.attributes || {};
|
|
845
|
-
const text = attrs.text || attrs.label || 'Button';
|
|
846
|
-
const url = attrs.url || attrs.linkUrl || '#';
|
|
575
|
+
const text = (attrs.text || attrs.label || 'Button');
|
|
576
|
+
const url = (attrs.url || attrs.linkUrl || '#');
|
|
847
577
|
const isExternal = typeof window !== 'undefined'
|
|
848
578
|
? url.startsWith('http') && !url.includes(window.location.hostname)
|
|
849
579
|
: url.startsWith('http');
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
return (jsxRuntimeExports.jsx("a", { href: url, target: isExternal ? '_blank' : undefined, rel: isExternal ? 'noopener noreferrer' : undefined, className: className, style: style, onMouseEnter: (e) => {
|
|
580
|
+
return (jsxRuntimeExports.jsx("a", { href: url, target: isExternal ? '_blank' : undefined, rel: isExternal ? 'noopener noreferrer' : undefined, className: className, style: {
|
|
581
|
+
display: 'inline-block',
|
|
582
|
+
padding: 'var(--dstyler-spacing-md) var(--dstyler-spacing-lg)',
|
|
583
|
+
backgroundColor: 'var(--dstyler-color-primary)',
|
|
584
|
+
color: '#ffffff',
|
|
585
|
+
textDecoration: 'none',
|
|
586
|
+
borderRadius: '4px',
|
|
587
|
+
fontWeight: '500',
|
|
588
|
+
marginBottom: 'var(--dstyler-spacing-md)',
|
|
589
|
+
transition: 'opacity 0.2s',
|
|
590
|
+
}, onMouseEnter: (e) => {
|
|
862
591
|
e.currentTarget.style.opacity = '0.9';
|
|
863
592
|
}, onMouseLeave: (e) => {
|
|
864
593
|
e.currentTarget.style.opacity = '1';
|
|
@@ -867,16 +596,22 @@ const BlockButton = ({ block, className }) => {
|
|
|
867
596
|
|
|
868
597
|
const BlockForm = ({ block, className }) => {
|
|
869
598
|
const attrs = block.attributes || {};
|
|
870
|
-
const content = attrs.content || attrs.html || '';
|
|
599
|
+
const content = (attrs.content || attrs.html || '');
|
|
871
600
|
// For now, just render the HTML content
|
|
872
601
|
// In the future, this could parse and render using Gravity Forms or Mautic components
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
return (jsxRuntimeExports.jsx("div", { className: className, style: style, dangerouslySetInnerHTML: { __html: content } }));
|
|
602
|
+
return (jsxRuntimeExports.jsx("div", { className: className, style: {
|
|
603
|
+
marginBottom: 'var(--dstyler-spacing-lg)',
|
|
604
|
+
}, dangerouslySetInnerHTML: { __html: content } }));
|
|
877
605
|
};
|
|
878
606
|
|
|
879
|
-
|
|
607
|
+
/**
|
|
608
|
+
* AutoBlock - Automatically selects and renders the appropriate block renderer
|
|
609
|
+
*
|
|
610
|
+
* Priority:
|
|
611
|
+
* 1. Use registry renderer if available (integrates with wparser)
|
|
612
|
+
* 2. Use dstyler's built-in block renderers
|
|
613
|
+
*/
|
|
614
|
+
const AutoBlock = ({ block, registry, className }) => {
|
|
880
615
|
// If registry is provided, use it for rendering (integrates with wparser)
|
|
881
616
|
if (registry && registry.renderers[block.name]) {
|
|
882
617
|
const Renderer = registry.renderers[block.name];
|
|
@@ -886,19 +621,111 @@ const AutoBlock = ({ block, registry }) => {
|
|
|
886
621
|
const rendererName = selectBlockRenderer(block);
|
|
887
622
|
switch (rendererName) {
|
|
888
623
|
case 'BlockText':
|
|
889
|
-
return jsxRuntimeExports.jsx(BlockText, { block: block });
|
|
624
|
+
return jsxRuntimeExports.jsx(BlockText, { block: block, className: className });
|
|
890
625
|
case 'BlockImage':
|
|
891
|
-
return jsxRuntimeExports.jsx(BlockImage, { block: block });
|
|
626
|
+
return jsxRuntimeExports.jsx(BlockImage, { block: block, className: className });
|
|
892
627
|
case 'BlockButton':
|
|
893
|
-
return jsxRuntimeExports.jsx(BlockButton, { block: block });
|
|
628
|
+
return jsxRuntimeExports.jsx(BlockButton, { block: block, className: className });
|
|
894
629
|
case 'BlockForm':
|
|
895
|
-
return jsxRuntimeExports.jsx(BlockForm, { block: block });
|
|
630
|
+
return jsxRuntimeExports.jsx(BlockForm, { block: block, className: className });
|
|
896
631
|
default:
|
|
897
632
|
// Fallback: render block name or content
|
|
898
633
|
return (jsxRuntimeExports.jsx("div", { style: { marginBottom: 'var(--dstyler-spacing-md)' }, children: jsxRuntimeExports.jsxs("pre", { style: { fontSize: '0.875rem', color: '#666' }, children: [block.name, ": ", JSON.stringify(block.attributes, null, 2)] }) }));
|
|
899
634
|
}
|
|
900
635
|
};
|
|
901
636
|
|
|
637
|
+
const SectionBase = ({ section, children }) => {
|
|
638
|
+
const style = section.style || 'default';
|
|
639
|
+
const bgColor = `var(--dstyler-color-${style})`;
|
|
640
|
+
const className = [
|
|
641
|
+
'dstyler-section',
|
|
642
|
+
`dstyler-section-${style}`,
|
|
643
|
+
section.customClass,
|
|
644
|
+
].filter(Boolean).join(' ');
|
|
645
|
+
return (jsxRuntimeExports.jsx("section", { className: className, id: section.customId, style: {
|
|
646
|
+
backgroundColor: bgColor,
|
|
647
|
+
padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
|
|
648
|
+
}, children: jsxRuntimeExports.jsx("div", { style: {
|
|
649
|
+
maxWidth: 'var(--dstyler-container-max-width, 1200px)',
|
|
650
|
+
margin: '0 auto',
|
|
651
|
+
}, children: children }) }));
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
const SectionWave = ({ section, children, waveTop = true, waveBottom = true, }) => {
|
|
655
|
+
const style = section.style || 'default';
|
|
656
|
+
const bgColor = `var(--dstyler-color-${style})`;
|
|
657
|
+
const className = [
|
|
658
|
+
'dstyler-section',
|
|
659
|
+
'dstyler-section-wave',
|
|
660
|
+
`dstyler-section-${style}`,
|
|
661
|
+
section.customClass,
|
|
662
|
+
].filter(Boolean).join(' ');
|
|
663
|
+
// Wave SVG
|
|
664
|
+
const waveSvg = (jsxRuntimeExports.jsx("svg", { viewBox: "0 0 1440 120", preserveAspectRatio: "none", style: {
|
|
665
|
+
width: '100%',
|
|
666
|
+
height: '60px',
|
|
667
|
+
display: 'block',
|
|
668
|
+
fill: bgColor,
|
|
669
|
+
}, children: jsxRuntimeExports.jsx("path", { d: "M0,60 C240,0 480,120 720,60 C960,0 1200,120 1440,60 L1440,120 L0,120 Z" }) }));
|
|
670
|
+
return (jsxRuntimeExports.jsxs("section", { className: className, id: section.customId, style: {
|
|
671
|
+
backgroundColor: bgColor,
|
|
672
|
+
position: 'relative',
|
|
673
|
+
}, children: [waveTop && jsxRuntimeExports.jsx("div", { style: { marginTop: '-1px' }, children: waveSvg }), jsxRuntimeExports.jsx("div", { style: {
|
|
674
|
+
padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
|
|
675
|
+
}, children: jsxRuntimeExports.jsx("div", { style: {
|
|
676
|
+
maxWidth: 'var(--dstyler-container-max-width, 1200px)',
|
|
677
|
+
margin: '0 auto',
|
|
678
|
+
}, children: children }) }), waveBottom && (jsxRuntimeExports.jsx("div", { style: { marginBottom: '-1px', transform: 'rotate(180deg)' }, children: waveSvg }))] }));
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
const HeroDefault = ({ section, children }) => {
|
|
682
|
+
const style = section.style || 'default';
|
|
683
|
+
const bgColor = `var(--dstyler-color-${style})`;
|
|
684
|
+
const className = [
|
|
685
|
+
'dstyler-hero',
|
|
686
|
+
'dstyler-hero-default',
|
|
687
|
+
`dstyler-hero-${style}`,
|
|
688
|
+
section.customClass,
|
|
689
|
+
].filter(Boolean).join(' ');
|
|
690
|
+
return (jsxRuntimeExports.jsx("section", { className: className, id: section.customId, style: {
|
|
691
|
+
backgroundColor: bgColor,
|
|
692
|
+
padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
|
|
693
|
+
minHeight: '50vh',
|
|
694
|
+
display: 'flex',
|
|
695
|
+
alignItems: 'center',
|
|
696
|
+
justifyContent: 'center',
|
|
697
|
+
}, children: jsxRuntimeExports.jsx("div", { style: {
|
|
698
|
+
maxWidth: 'var(--dstyler-container-max-width, 1200px)',
|
|
699
|
+
margin: '0 auto',
|
|
700
|
+
textAlign: 'center',
|
|
701
|
+
}, children: children }) }));
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
const FooterMinimal = ({ section, children }) => {
|
|
705
|
+
const style = section.style || 'default';
|
|
706
|
+
const bgColor = `var(--dstyler-color-${style})`;
|
|
707
|
+
const className = [
|
|
708
|
+
'dstyler-footer',
|
|
709
|
+
'dstyler-footer-minimal',
|
|
710
|
+
`dstyler-footer-${style}`,
|
|
711
|
+
section.customClass,
|
|
712
|
+
].filter(Boolean).join(' ');
|
|
713
|
+
return (jsxRuntimeExports.jsx("footer", { className: className, id: section.customId, style: {
|
|
714
|
+
backgroundColor: bgColor,
|
|
715
|
+
padding: 'var(--dstyler-spacing-lg) var(--dstyler-spacing-md)',
|
|
716
|
+
borderTop: '1px solid var(--dstyler-color-alternate)',
|
|
717
|
+
}, children: jsxRuntimeExports.jsx("div", { style: {
|
|
718
|
+
maxWidth: 'var(--dstyler-container-max-width, 1200px)',
|
|
719
|
+
margin: '0 auto',
|
|
720
|
+
textAlign: 'center',
|
|
721
|
+
fontSize: '0.875rem',
|
|
722
|
+
color: 'var(--dstyler-color-text)',
|
|
723
|
+
}, children: children }) }));
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* AutoSection - Automatically selects and renders the appropriate section template
|
|
728
|
+
*/
|
|
902
729
|
const AutoSection = ({ section, registry }) => {
|
|
903
730
|
const templateName = selectTemplate(section);
|
|
904
731
|
// Render blocks using AutoBlock or registry
|
|
@@ -919,85 +746,20 @@ const AutoSection = ({ section, registry }) => {
|
|
|
919
746
|
return (jsxRuntimeExports.jsx(SectionWave, { section: section, waveTop: waveTop, waveBottom: waveBottom, children: renderBlocks() }));
|
|
920
747
|
}
|
|
921
748
|
case 'HeroDefault':
|
|
922
|
-
return
|
|
749
|
+
return jsxRuntimeExports.jsx(HeroDefault, { section: section, children: renderBlocks() });
|
|
923
750
|
case 'FooterMinimal':
|
|
924
|
-
return
|
|
751
|
+
return jsxRuntimeExports.jsx(FooterMinimal, { section: section, children: renderBlocks() });
|
|
925
752
|
default:
|
|
926
|
-
return
|
|
753
|
+
return jsxRuntimeExports.jsx(SectionBase, { section: section, children: renderBlocks() });
|
|
927
754
|
}
|
|
928
755
|
};
|
|
929
756
|
|
|
930
757
|
/**
|
|
931
|
-
*
|
|
758
|
+
* StyledPage - Main entry point for dstyler
|
|
932
759
|
*
|
|
933
|
-
*
|
|
934
|
-
*
|
|
935
|
-
*/
|
|
936
|
-
/**
|
|
937
|
-
* Detect sections from WordPress blocks
|
|
938
|
-
* Sections are defined by core/group blocks (with or without alignment)
|
|
939
|
-
* Sub-sections are core/columns blocks inside groups
|
|
760
|
+
* Detects sections from WordPress blocks and applies alternating styles
|
|
761
|
+
* Integrates with wparser's registry for block rendering
|
|
940
762
|
*/
|
|
941
|
-
function detectSections(blocks) {
|
|
942
|
-
const sections = [];
|
|
943
|
-
let sectionIndex = 0;
|
|
944
|
-
for (const block of blocks) {
|
|
945
|
-
// core/group blocks are sections
|
|
946
|
-
if (block.name === 'core/group') {
|
|
947
|
-
const attrs = block.attributes || {};
|
|
948
|
-
const className = attrs.className;
|
|
949
|
-
const customId = attrs.id;
|
|
950
|
-
// Extract blocks from innerBlocks
|
|
951
|
-
const innerBlocks = block.innerBlocks || [];
|
|
952
|
-
sections.push({
|
|
953
|
-
id: `section-${sectionIndex}`,
|
|
954
|
-
type: 'section',
|
|
955
|
-
block,
|
|
956
|
-
blocks: innerBlocks,
|
|
957
|
-
index: sectionIndex,
|
|
958
|
-
customClass: className,
|
|
959
|
-
customId,
|
|
960
|
-
});
|
|
961
|
-
sectionIndex++;
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
return sections;
|
|
965
|
-
}
|
|
966
|
-
/**
|
|
967
|
-
* Determine section style based on alternating logic and custom classes
|
|
968
|
-
* Rules:
|
|
969
|
-
* - default is always first
|
|
970
|
-
* - after default follows alternate
|
|
971
|
-
* - highlight resets - next section is default
|
|
972
|
-
*/
|
|
973
|
-
function determineSectionStyle(section, previousStyle) {
|
|
974
|
-
const className = section.customClass?.toLowerCase() || '';
|
|
975
|
-
// Check for custom class overrides first
|
|
976
|
-
if (className.includes('highlight') || className.includes('highlight-section')) {
|
|
977
|
-
return 'highlight';
|
|
978
|
-
}
|
|
979
|
-
if (className.includes('dark') || className.includes('dark-section')) {
|
|
980
|
-
return 'alternate';
|
|
981
|
-
}
|
|
982
|
-
if (className.includes('default') || className.includes('default-section')) {
|
|
983
|
-
return 'default';
|
|
984
|
-
}
|
|
985
|
-
// Alternating logic
|
|
986
|
-
if (section.index === 0) {
|
|
987
|
-
return 'default';
|
|
988
|
-
}
|
|
989
|
-
if (previousStyle === 'highlight') {
|
|
990
|
-
return 'default';
|
|
991
|
-
}
|
|
992
|
-
if (previousStyle === 'default') {
|
|
993
|
-
return 'alternate';
|
|
994
|
-
}
|
|
995
|
-
if (previousStyle === 'alternate') {
|
|
996
|
-
return 'default';
|
|
997
|
-
}
|
|
998
|
-
return 'default';
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
763
|
const StyledPage = ({ page, registry }) => {
|
|
1002
764
|
const blocks = page.blocks || [];
|
|
1003
765
|
// Detect sections from blocks
|
|
@@ -1012,25 +774,175 @@ const StyledPage = ({ page, registry }) => {
|
|
|
1012
774
|
style,
|
|
1013
775
|
};
|
|
1014
776
|
});
|
|
1015
|
-
return (jsxRuntimeExports.jsx(
|
|
777
|
+
return (jsxRuntimeExports.jsx(React.Fragment, { children: styledSections.map((section) => (jsxRuntimeExports.jsx(AutoSection, { section: section, registry: registry }, section.id))) }));
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
const defaultTokens = {
|
|
781
|
+
colors: {
|
|
782
|
+
primary: '#1a73e8',
|
|
783
|
+
secondary: '#34a853',
|
|
784
|
+
background: '#ffffff',
|
|
785
|
+
text: '#222222',
|
|
786
|
+
heading: '#222222',
|
|
787
|
+
default: '#ffffff',
|
|
788
|
+
alternate: '#f5f5f5',
|
|
789
|
+
highlight: '#e8f0fe',
|
|
790
|
+
},
|
|
791
|
+
spacing: {
|
|
792
|
+
sm: 8,
|
|
793
|
+
md: 16,
|
|
794
|
+
lg: 32,
|
|
795
|
+
xl: 64,
|
|
796
|
+
},
|
|
797
|
+
typography: {
|
|
798
|
+
body: 'Inter, sans-serif',
|
|
799
|
+
heading: 'Inter, sans-serif',
|
|
800
|
+
scale: 1.2,
|
|
801
|
+
},
|
|
1016
802
|
};
|
|
1017
803
|
|
|
1018
804
|
/**
|
|
1019
|
-
*
|
|
1020
|
-
*
|
|
1021
|
-
* This file is part of the MarVAlt Open SDK.
|
|
1022
|
-
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
805
|
+
* Adjust brightness of a hex color
|
|
1023
806
|
*/
|
|
807
|
+
function adjustBrightness(hex, amount) {
|
|
808
|
+
const num = parseInt(hex.replace('#', ''), 16);
|
|
809
|
+
const r = Math.max(0, Math.min(255, ((num >> 16) & 0xff) + (amount * 255)));
|
|
810
|
+
const g = Math.max(0, Math.min(255, ((num >> 8) & 0xff) + (amount * 255)));
|
|
811
|
+
const b = Math.max(0, Math.min(255, (num & 0xff) + (amount * 255)));
|
|
812
|
+
return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;
|
|
813
|
+
}
|
|
1024
814
|
/**
|
|
1025
|
-
*
|
|
815
|
+
* Blend two colors
|
|
1026
816
|
*/
|
|
1027
|
-
function
|
|
1028
|
-
|
|
1029
|
-
|
|
817
|
+
function blendColors(color1, color2, ratio) {
|
|
818
|
+
const c1 = hexToRgb(color1);
|
|
819
|
+
const c2 = hexToRgb(color2);
|
|
820
|
+
if (!c1 || !c2)
|
|
821
|
+
return color1;
|
|
822
|
+
const r = Math.round(c1.r + (c2.r - c1.r) * ratio);
|
|
823
|
+
const g = Math.round(c1.g + (c2.g - c1.g) * ratio);
|
|
824
|
+
const b = Math.round(c1.b + (c2.b - c1.b) * ratio);
|
|
825
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
826
|
+
}
|
|
827
|
+
function hexToRgb(hex) {
|
|
828
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
829
|
+
return result
|
|
830
|
+
? {
|
|
831
|
+
r: parseInt(result[1], 16),
|
|
832
|
+
g: parseInt(result[2], 16),
|
|
833
|
+
b: parseInt(result[3], 16),
|
|
834
|
+
}
|
|
835
|
+
: null;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Convert WordPress theme styles to design tokens
|
|
839
|
+
*/
|
|
840
|
+
function convertWordPressToTokens(wpStyles) {
|
|
841
|
+
if (!wpStyles) {
|
|
842
|
+
return defaultTokens;
|
|
1030
843
|
}
|
|
1031
|
-
|
|
1032
|
-
//
|
|
1033
|
-
|
|
844
|
+
const palette = wpStyles.theme_palette || {};
|
|
845
|
+
// Typography priority: Customizer (wpStyles.typography) > Theme Editor fonts > defaults
|
|
846
|
+
const tokens = {
|
|
847
|
+
...defaultTokens,
|
|
848
|
+
colors: {
|
|
849
|
+
...defaultTokens.colors,
|
|
850
|
+
...(wpStyles.colors?.primary && { primary: wpStyles.colors.primary }),
|
|
851
|
+
...(wpStyles.colors?.secondary && { secondary: wpStyles.colors.secondary }),
|
|
852
|
+
...(wpStyles.colors?.text && { text: wpStyles.colors.text }),
|
|
853
|
+
// heading handled below to allow palette fallback
|
|
854
|
+
...(wpStyles.colors?.background && {
|
|
855
|
+
background: wpStyles.colors.background,
|
|
856
|
+
default: wpStyles.colors.background,
|
|
857
|
+
}),
|
|
858
|
+
},
|
|
859
|
+
typography: {
|
|
860
|
+
...defaultTokens.typography,
|
|
861
|
+
...(wpStyles.typography?.heading_font_family && {
|
|
862
|
+
heading: wpStyles.typography.heading_font_family,
|
|
863
|
+
}),
|
|
864
|
+
...(wpStyles.typography?.body_font_family && {
|
|
865
|
+
body: wpStyles.typography.body_font_family,
|
|
866
|
+
}),
|
|
867
|
+
},
|
|
868
|
+
};
|
|
869
|
+
// Explicit heading override (may be undefined/empty), then fallback below
|
|
870
|
+
tokens.colors.heading = wpStyles.colors?.heading || undefined;
|
|
871
|
+
// Ensure heading color falls back to text if not provided
|
|
872
|
+
if (!tokens.colors.heading) {
|
|
873
|
+
tokens.colors.heading =
|
|
874
|
+
// Prefer explicit heading color if provided by Theme Editor
|
|
875
|
+
wpStyles.colors?.heading ||
|
|
876
|
+
// Then Theme Editor palette contrast (matches heading color choice in editor)
|
|
877
|
+
palette['contrast'] ||
|
|
878
|
+
// Then palette primary
|
|
879
|
+
palette['primary'] ||
|
|
880
|
+
// Then explicit primary fallback
|
|
881
|
+
wpStyles.colors?.primary ||
|
|
882
|
+
tokens.colors.text;
|
|
883
|
+
}
|
|
884
|
+
// Generate alternate and highlight colors from primary/background
|
|
885
|
+
if (tokens.colors.primary && tokens.colors.background) {
|
|
886
|
+
// Alternate: slightly darker/lighter than background
|
|
887
|
+
tokens.colors.alternate = adjustBrightness(tokens.colors.background, -0.05);
|
|
888
|
+
// Highlight: tinted with primary color
|
|
889
|
+
tokens.colors.highlight = blendColors(tokens.colors.background, tokens.colors.primary, 0.1);
|
|
890
|
+
}
|
|
891
|
+
return tokens;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
const THEME_STYLE_ID = 'dstyler-theme-vars';
|
|
895
|
+
function tokensToCSS(tokens) {
|
|
896
|
+
return `
|
|
897
|
+
--dstyler-color-primary: ${tokens.colors.primary};
|
|
898
|
+
--dstyler-color-secondary: ${tokens.colors.secondary};
|
|
899
|
+
--dstyler-color-background: ${tokens.colors.background};
|
|
900
|
+
--dstyler-color-text: ${tokens.colors.text};
|
|
901
|
+
--dstyler-color-heading: ${tokens.colors.heading || tokens.colors.text};
|
|
902
|
+
--dstyler-color-default: ${tokens.colors.default};
|
|
903
|
+
--dstyler-color-alternate: ${tokens.colors.alternate};
|
|
904
|
+
--dstyler-color-highlight: ${tokens.colors.highlight};
|
|
905
|
+
--dstyler-spacing-sm: ${tokens.spacing.sm}px;
|
|
906
|
+
--dstyler-spacing-md: ${tokens.spacing.md}px;
|
|
907
|
+
--dstyler-spacing-lg: ${tokens.spacing.lg}px;
|
|
908
|
+
--dstyler-spacing-xl: ${tokens.spacing.xl}px;
|
|
909
|
+
--dstyler-font-body: ${tokens.typography.body};
|
|
910
|
+
--dstyler-font-heading: ${tokens.typography.heading};
|
|
911
|
+
--dstyler-font-scale: ${tokens.typography.scale};
|
|
912
|
+
`;
|
|
913
|
+
}
|
|
914
|
+
function ensureStyleElement() {
|
|
915
|
+
if (typeof document === 'undefined')
|
|
916
|
+
return null;
|
|
917
|
+
let el = document.getElementById(THEME_STYLE_ID);
|
|
918
|
+
if (!el) {
|
|
919
|
+
el = document.createElement('style');
|
|
920
|
+
el.id = THEME_STYLE_ID;
|
|
921
|
+
document.head.appendChild(el);
|
|
922
|
+
}
|
|
923
|
+
return el;
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Inject CSS variables from design tokens into :root
|
|
927
|
+
*/
|
|
928
|
+
function injectCSSVariables(tokens) {
|
|
929
|
+
const el = ensureStyleElement();
|
|
930
|
+
if (!el)
|
|
931
|
+
return;
|
|
932
|
+
el.textContent = `:root { ${tokensToCSS(tokens)} }`;
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Inject CSS variables for both light and dark themes.
|
|
936
|
+
* Light applies to :root, dark applies when [data-theme="dark"] is present.
|
|
937
|
+
*/
|
|
938
|
+
function injectDualThemeCSS(light, dark) {
|
|
939
|
+
const el = ensureStyleElement();
|
|
940
|
+
if (!el)
|
|
941
|
+
return;
|
|
942
|
+
el.textContent = `
|
|
943
|
+
:root { ${tokensToCSS(light)} }
|
|
944
|
+
:root[data-theme="dark"] { ${tokensToCSS(dark)} }
|
|
945
|
+
`;
|
|
1034
946
|
}
|
|
1035
947
|
|
|
1036
948
|
/**
|
|
@@ -1039,6 +951,78 @@ function getResponsiveValue(mobile, tablet, desktop) {
|
|
|
1039
951
|
* This file is part of the MarVAlt Open SDK.
|
|
1040
952
|
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
1041
953
|
*/
|
|
954
|
+
/**
|
|
955
|
+
* ThemeProvider - Provides design tokens to the application
|
|
956
|
+
*
|
|
957
|
+
* Priority order:
|
|
958
|
+
* 1. customTokens (highest priority - overrides everything)
|
|
959
|
+
* 2. WordPress styles (converted to tokens)
|
|
960
|
+
* 3. Default tokens (fallback)
|
|
961
|
+
*/
|
|
962
|
+
const ThemeProvider = ({ children, wpStyles, customTokens, darkWpStyles, darkTokens, mode = 'light', }) => {
|
|
963
|
+
React.useEffect(() => {
|
|
964
|
+
// Light tokens: custom -> wpStyles -> default
|
|
965
|
+
const lightTokens = customTokens || convertWordPressToTokens(wpStyles);
|
|
966
|
+
// Dark tokens: provided -> wpStylesDark -> fallback to light
|
|
967
|
+
const resolvedDarkTokens = darkTokens || (darkWpStyles ? convertWordPressToTokens(darkWpStyles) : lightTokens);
|
|
968
|
+
if (mode === 'dark') {
|
|
969
|
+
injectDualThemeCSS(lightTokens, resolvedDarkTokens);
|
|
970
|
+
if (typeof document !== 'undefined') {
|
|
971
|
+
document.documentElement.setAttribute('data-theme', 'dark');
|
|
972
|
+
}
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
if (mode === 'system') {
|
|
976
|
+
const prefersDark = typeof window !== 'undefined' &&
|
|
977
|
+
window.matchMedia &&
|
|
978
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
979
|
+
injectDualThemeCSS(lightTokens, resolvedDarkTokens);
|
|
980
|
+
if (typeof document !== 'undefined') {
|
|
981
|
+
document.documentElement.setAttribute('data-theme', prefersDark ? 'dark' : 'light');
|
|
982
|
+
}
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
// Default to light
|
|
986
|
+
injectDualThemeCSS(lightTokens, resolvedDarkTokens);
|
|
987
|
+
if (typeof document !== 'undefined') {
|
|
988
|
+
document.documentElement.setAttribute('data-theme', 'light');
|
|
989
|
+
}
|
|
990
|
+
}, [wpStyles, customTokens, darkWpStyles, darkTokens, mode]);
|
|
991
|
+
// Return children if provided, otherwise return null (theme still applies via useEffect)
|
|
992
|
+
if (children === undefined || children === null) {
|
|
993
|
+
return null;
|
|
994
|
+
}
|
|
995
|
+
return React.createElement(React.Fragment, null, children);
|
|
996
|
+
};
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* Check if a color is dark (for determining text color)
|
|
1000
|
+
*/
|
|
1001
|
+
function isColorDark(color) {
|
|
1002
|
+
// Remove # if present
|
|
1003
|
+
const hex = color.replace('#', '');
|
|
1004
|
+
// Handle RGB/RGBA format
|
|
1005
|
+
if (color.startsWith('rgb')) {
|
|
1006
|
+
const match = color.match(/\d+/g);
|
|
1007
|
+
if (match && match.length >= 3) {
|
|
1008
|
+
const r = parseInt(match[0], 10);
|
|
1009
|
+
const g = parseInt(match[1], 10);
|
|
1010
|
+
const b = parseInt(match[2], 10);
|
|
1011
|
+
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
|
1012
|
+
return brightness < 128;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
// Handle hex format
|
|
1016
|
+
if (hex.length === 6) {
|
|
1017
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
1018
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
1019
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
1020
|
+
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
|
1021
|
+
return brightness < 128;
|
|
1022
|
+
}
|
|
1023
|
+
// Default to light background
|
|
1024
|
+
return false;
|
|
1025
|
+
}
|
|
1042
1026
|
/**
|
|
1043
1027
|
* Create a colorMapper from design tokens and WordPress theme palette
|
|
1044
1028
|
*
|
|
@@ -1065,7 +1049,9 @@ function getResponsiveValue(mobile, tablet, desktop) {
|
|
|
1065
1049
|
*/
|
|
1066
1050
|
function createColorMapperFromTokens(tokens, themePalette, overrides) {
|
|
1067
1051
|
// Debug logging
|
|
1068
|
-
if (typeof window !== 'undefined' &&
|
|
1052
|
+
if (typeof window !== 'undefined' &&
|
|
1053
|
+
typeof process !== 'undefined' &&
|
|
1054
|
+
process.env.NODE_ENV === 'development') {
|
|
1069
1055
|
console.log('🎨 createColorMapperFromTokens - themePalette:', themePalette);
|
|
1070
1056
|
console.log('🎨 createColorMapperFromTokens - tokens:', tokens);
|
|
1071
1057
|
}
|
|
@@ -1082,7 +1068,9 @@ function createColorMapperFromTokens(tokens, themePalette, overrides) {
|
|
|
1082
1068
|
const textColor = isDark ? 'text-white' : 'text-gray-900';
|
|
1083
1069
|
// Include both bg (for background mapping) and text-[color] (for text mapping), plus contrast helper
|
|
1084
1070
|
const result = `bg-[${color}] text-[${color}] ${textColor}`;
|
|
1085
|
-
if (typeof window !== 'undefined' &&
|
|
1071
|
+
if (typeof window !== 'undefined' &&
|
|
1072
|
+
typeof process !== 'undefined' &&
|
|
1073
|
+
process.env.NODE_ENV === 'development') {
|
|
1086
1074
|
console.log(`🎨 Color mapper - ${wpColorName}:`, result, '(from theme palette)');
|
|
1087
1075
|
}
|
|
1088
1076
|
return result;
|
|
@@ -1090,53 +1078,25 @@ function createColorMapperFromTokens(tokens, themePalette, overrides) {
|
|
|
1090
1078
|
// Fallback: Map WordPress theme colors to design tokens
|
|
1091
1079
|
// Using Tailwind's arbitrary value syntax for hex colors
|
|
1092
1080
|
const defaultMap = {
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1081
|
+
base: `bg-[${tokens.colors.background}] text-[${tokens.colors.text}]`,
|
|
1082
|
+
background: `bg-[${tokens.colors.background}] text-[${tokens.colors.text}]`,
|
|
1083
|
+
foreground: `text-[${tokens.colors.text}]`,
|
|
1084
|
+
contrast: `bg-[${themePalette?.contrast || tokens.colors.text}] text-[${tokens.colors.background}]`,
|
|
1085
|
+
primary: `bg-[${themePalette?.primary || tokens.colors.primary}] text-[${themePalette?.primary || tokens.colors.primary}]`,
|
|
1086
|
+
secondary: `bg-[${themePalette?.secondary || tokens.colors.secondary}] text-[${themePalette?.secondary || tokens.colors.secondary}]`,
|
|
1087
|
+
heading: `text-[${tokens.colors.heading || tokens.colors.text}]`,
|
|
1088
|
+
accent: `bg-[${tokens.colors.highlight}] text-[${tokens.colors.highlight}]`,
|
|
1101
1089
|
'accent-1': `bg-[${themePalette?.['accent-1'] || tokens.colors.primary}] text-[${themePalette?.['accent-1'] || tokens.colors.primary}]`,
|
|
1102
1090
|
'accent-2': `bg-[${themePalette?.['accent-2'] || tokens.colors.secondary}] text-[${themePalette?.['accent-2'] || tokens.colors.secondary}]`,
|
|
1103
1091
|
'accent-3': `bg-[${themePalette?.['accent-3'] || tokens.colors.highlight}] text-[${themePalette?.['accent-3'] || tokens.colors.highlight}]`,
|
|
1104
1092
|
'accent-4': `bg-[${themePalette?.['accent-4'] || tokens.colors.alternate}] text-[${themePalette?.['accent-4'] || tokens.colors.alternate}]`,
|
|
1105
1093
|
'accent-5': `bg-[${themePalette?.['accent-5'] || tokens.colors.default}] text-[${themePalette?.['accent-5'] || tokens.colors.default}]`,
|
|
1106
1094
|
'accent-6': `bg-[${themePalette?.['accent-6'] || themePalette?.primary || tokens.colors.primary}] text-[${themePalette?.['accent-6'] || themePalette?.primary || tokens.colors.primary}]`,
|
|
1107
|
-
|
|
1095
|
+
transparent: 'bg-transparent',
|
|
1108
1096
|
};
|
|
1109
1097
|
return wpColorName ? defaultMap[wpColorName] || null : null;
|
|
1110
1098
|
};
|
|
1111
1099
|
}
|
|
1112
|
-
/**
|
|
1113
|
-
* Check if a color is dark (for determining text color)
|
|
1114
|
-
*/
|
|
1115
|
-
function isColorDark(color) {
|
|
1116
|
-
// Remove # if present
|
|
1117
|
-
const hex = color.replace('#', '');
|
|
1118
|
-
// Handle RGB/RGBA format
|
|
1119
|
-
if (color.startsWith('rgb')) {
|
|
1120
|
-
const match = color.match(/\d+/g);
|
|
1121
|
-
if (match && match.length >= 3) {
|
|
1122
|
-
const r = parseInt(match[0], 10);
|
|
1123
|
-
const g = parseInt(match[1], 10);
|
|
1124
|
-
const b = parseInt(match[2], 10);
|
|
1125
|
-
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
|
1126
|
-
return brightness < 128;
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
// Handle hex format
|
|
1130
|
-
if (hex.length === 6) {
|
|
1131
|
-
const r = parseInt(hex.substring(0, 2), 16);
|
|
1132
|
-
const g = parseInt(hex.substring(2, 4), 16);
|
|
1133
|
-
const b = parseInt(hex.substring(4, 6), 16);
|
|
1134
|
-
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
|
1135
|
-
return brightness < 128;
|
|
1136
|
-
}
|
|
1137
|
-
// Default to light background
|
|
1138
|
-
return false;
|
|
1139
|
-
}
|
|
1140
1100
|
/**
|
|
1141
1101
|
* Create a colorMapper with semantic Tailwind classes
|
|
1142
1102
|
*
|
|
@@ -1164,9 +1124,9 @@ function createColorMapperWithSemanticClasses(tokens, overrides) {
|
|
|
1164
1124
|
// Map WordPress theme colors to semantic Tailwind classes
|
|
1165
1125
|
// Assumes your Tailwind config has primary, secondary, muted, etc. defined
|
|
1166
1126
|
const defaultMap = {
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1127
|
+
base: 'bg-white',
|
|
1128
|
+
contrast: 'bg-gray-900 text-white',
|
|
1129
|
+
transparent: 'bg-transparent',
|
|
1170
1130
|
'accent-1': 'bg-primary text-primary-foreground',
|
|
1171
1131
|
'accent-2': 'bg-secondary text-secondary-foreground',
|
|
1172
1132
|
'accent-3': 'bg-muted text-muted-foreground',
|
|
@@ -1178,8 +1138,30 @@ function createColorMapperWithSemanticClasses(tokens, overrides) {
|
|
|
1178
1138
|
};
|
|
1179
1139
|
}
|
|
1180
1140
|
|
|
1141
|
+
/**
|
|
1142
|
+
* @license GPL-3.0-or-later
|
|
1143
|
+
*
|
|
1144
|
+
* This file is part of the MarVAlt Open SDK.
|
|
1145
|
+
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
1146
|
+
*/
|
|
1147
|
+
/**
|
|
1148
|
+
* Responsive utility functions
|
|
1149
|
+
*/
|
|
1150
|
+
function getResponsiveValue(mobile, tablet, desktop) {
|
|
1151
|
+
if (!tablet && !desktop) {
|
|
1152
|
+
return typeof mobile === 'number' ? `${mobile}px` : mobile;
|
|
1153
|
+
}
|
|
1154
|
+
// For now, return mobile value
|
|
1155
|
+
// In the future, this could use CSS custom properties with media queries
|
|
1156
|
+
return typeof mobile === 'number' ? `${mobile}px` : mobile;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1181
1159
|
exports.AutoBlock = AutoBlock;
|
|
1182
1160
|
exports.AutoSection = AutoSection;
|
|
1161
|
+
exports.BlockButton = BlockButton;
|
|
1162
|
+
exports.BlockForm = BlockForm;
|
|
1163
|
+
exports.BlockImage = BlockImage;
|
|
1164
|
+
exports.BlockText = BlockText;
|
|
1183
1165
|
exports.FooterMinimal = FooterMinimal;
|
|
1184
1166
|
exports.HeroDefault = HeroDefault;
|
|
1185
1167
|
exports.SectionBase = SectionBase;
|