@marvalt/dstyler 0.1.19 → 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 -502
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +481 -501
- 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,241 +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
|
-
// heading handled below to allow palette fallback
|
|
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
|
-
// Explicit heading override (may be undefined/empty), then fallback below
|
|
75
|
-
tokens.colors.heading = wpStyles.colors?.heading || undefined;
|
|
76
|
-
// Ensure heading color falls back to text if not provided
|
|
77
|
-
if (!tokens.colors.heading) {
|
|
78
|
-
tokens.colors.heading =
|
|
79
|
-
// Prefer explicit heading color if provided by Theme Editor
|
|
80
|
-
wpStyles.colors?.heading ||
|
|
81
|
-
// Then Theme Editor palette contrast (matches heading color choice in editor)
|
|
82
|
-
palette['contrast'] ||
|
|
83
|
-
// Then palette primary
|
|
84
|
-
palette['primary'] ||
|
|
85
|
-
// Then explicit primary fallback
|
|
86
|
-
wpStyles.colors?.primary ||
|
|
87
|
-
tokens.colors.text;
|
|
88
|
-
}
|
|
89
|
-
// Generate alternate and highlight colors from primary/background
|
|
90
|
-
if (tokens.colors.primary && tokens.colors.background) {
|
|
91
|
-
// Alternate: slightly darker/lighter than background
|
|
92
|
-
tokens.colors.alternate = adjustBrightness(tokens.colors.background, -0.05);
|
|
93
|
-
// Highlight: tinted with primary color
|
|
94
|
-
tokens.colors.highlight = blendColors(tokens.colors.background, tokens.colors.primary, 0.1);
|
|
95
|
-
}
|
|
96
|
-
return tokens;
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Adjust brightness of a hex color
|
|
100
|
-
*/
|
|
101
|
-
function adjustBrightness(hex, amount) {
|
|
102
|
-
const num = parseInt(hex.replace('#', ''), 16);
|
|
103
|
-
const r = Math.max(0, Math.min(255, ((num >> 16) & 0xff) + (amount * 255)));
|
|
104
|
-
const g = Math.max(0, Math.min(255, ((num >> 8) & 0xff) + (amount * 255)));
|
|
105
|
-
const b = Math.max(0, Math.min(255, (num & 0xff) + (amount * 255)));
|
|
106
|
-
return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Blend two colors
|
|
110
|
-
*/
|
|
111
|
-
function blendColors(color1, color2, ratio) {
|
|
112
|
-
const c1 = hexToRgb(color1);
|
|
113
|
-
const c2 = hexToRgb(color2);
|
|
114
|
-
if (!c1 || !c2)
|
|
115
|
-
return color1;
|
|
116
|
-
const r = Math.round(c1.r + (c2.r - c1.r) * ratio);
|
|
117
|
-
const g = Math.round(c1.g + (c2.g - c1.g) * ratio);
|
|
118
|
-
const b = Math.round(c1.b + (c2.b - c1.b) * ratio);
|
|
119
|
-
return `rgb(${r}, ${g}, ${b})`;
|
|
120
|
-
}
|
|
121
|
-
function hexToRgb(hex) {
|
|
122
|
-
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
123
|
-
return result
|
|
124
|
-
? {
|
|
125
|
-
r: parseInt(result[1], 16),
|
|
126
|
-
g: parseInt(result[2], 16),
|
|
127
|
-
b: parseInt(result[3], 16),
|
|
128
|
-
}
|
|
129
|
-
: null;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* @license GPL-3.0-or-later
|
|
134
|
-
*
|
|
135
|
-
* This file is part of the MarVAlt Open SDK.
|
|
136
|
-
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
137
|
-
*/
|
|
138
|
-
const THEME_STYLE_ID = 'dstyler-theme-vars';
|
|
139
|
-
function tokensToCSS(tokens) {
|
|
140
|
-
return `
|
|
141
|
-
--dstyler-color-primary: ${tokens.colors.primary};
|
|
142
|
-
--dstyler-color-secondary: ${tokens.colors.secondary};
|
|
143
|
-
--dstyler-color-background: ${tokens.colors.background};
|
|
144
|
-
--dstyler-color-text: ${tokens.colors.text};
|
|
145
|
-
--dstyler-color-heading: ${tokens.colors.heading || tokens.colors.text};
|
|
146
|
-
--dstyler-color-default: ${tokens.colors.default};
|
|
147
|
-
--dstyler-color-alternate: ${tokens.colors.alternate};
|
|
148
|
-
--dstyler-color-highlight: ${tokens.colors.highlight};
|
|
149
|
-
--dstyler-spacing-sm: ${tokens.spacing.sm}px;
|
|
150
|
-
--dstyler-spacing-md: ${tokens.spacing.md}px;
|
|
151
|
-
--dstyler-spacing-lg: ${tokens.spacing.lg}px;
|
|
152
|
-
--dstyler-spacing-xl: ${tokens.spacing.xl}px;
|
|
153
|
-
--dstyler-font-body: ${tokens.typography.body};
|
|
154
|
-
--dstyler-font-heading: ${tokens.typography.heading};
|
|
155
|
-
--dstyler-font-scale: ${tokens.typography.scale};
|
|
156
|
-
`;
|
|
157
|
-
}
|
|
158
|
-
function ensureStyleElement() {
|
|
159
|
-
if (typeof document === 'undefined')
|
|
160
|
-
return null;
|
|
161
|
-
let el = document.getElementById(THEME_STYLE_ID);
|
|
162
|
-
if (!el) {
|
|
163
|
-
el = document.createElement('style');
|
|
164
|
-
el.id = THEME_STYLE_ID;
|
|
165
|
-
document.head.appendChild(el);
|
|
166
|
-
}
|
|
167
|
-
return el;
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Inject CSS variables from design tokens into :root
|
|
171
|
-
*/
|
|
172
|
-
function injectCSSVariables(tokens) {
|
|
173
|
-
const el = ensureStyleElement();
|
|
174
|
-
if (!el)
|
|
175
|
-
return;
|
|
176
|
-
el.textContent = `:root { ${tokensToCSS(tokens)} }`;
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Inject CSS variables for both light and dark themes.
|
|
180
|
-
* Light applies to :root, dark applies when [data-theme="dark"] is present.
|
|
181
|
-
*/
|
|
182
|
-
function injectDualThemeCSS(light, dark) {
|
|
183
|
-
const el = ensureStyleElement();
|
|
184
|
-
if (!el)
|
|
185
|
-
return;
|
|
186
|
-
el.textContent = `
|
|
187
|
-
:root { ${tokensToCSS(light)} }
|
|
188
|
-
:root[data-theme="dark"] { ${tokensToCSS(dark)} }
|
|
189
|
-
`;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* @license GPL-3.0-or-later
|
|
194
|
-
*
|
|
195
|
-
* This file is part of the MarVAlt Open SDK.
|
|
196
|
-
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
197
|
-
*/
|
|
198
|
-
/**
|
|
199
|
-
* ThemeProvider - Provides design tokens to the application
|
|
200
|
-
*
|
|
201
|
-
* Priority order:
|
|
202
|
-
* 1. customTokens (highest priority - overrides everything)
|
|
203
|
-
* 2. WordPress styles (converted to tokens)
|
|
204
|
-
* 3. Default tokens (fallback)
|
|
205
|
-
*/
|
|
206
|
-
const ThemeProvider = ({ children, wpStyles, customTokens, darkWpStyles, darkTokens, mode = 'light', }) => {
|
|
207
|
-
React.useEffect(() => {
|
|
208
|
-
// Light tokens: custom -> wpStyles -> default
|
|
209
|
-
const lightTokens = customTokens || convertWordPressToTokens(wpStyles);
|
|
210
|
-
// Dark tokens: provided -> wpStylesDark -> fallback to light
|
|
211
|
-
const resolvedDarkTokens = darkTokens || (darkWpStyles ? convertWordPressToTokens(darkWpStyles) : lightTokens);
|
|
212
|
-
if (mode === 'dark') {
|
|
213
|
-
injectDualThemeCSS(lightTokens, resolvedDarkTokens);
|
|
214
|
-
if (typeof document !== 'undefined') {
|
|
215
|
-
document.documentElement.setAttribute('data-theme', 'dark');
|
|
216
|
-
}
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
if (mode === 'system') {
|
|
220
|
-
const prefersDark = typeof window !== 'undefined' && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
221
|
-
injectDualThemeCSS(lightTokens, resolvedDarkTokens);
|
|
222
|
-
if (typeof document !== 'undefined') {
|
|
223
|
-
document.documentElement.setAttribute('data-theme', prefersDark ? 'dark' : 'light');
|
|
224
|
-
}
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
// Default to light
|
|
228
|
-
injectDualThemeCSS(lightTokens, resolvedDarkTokens);
|
|
229
|
-
if (typeof document !== 'undefined') {
|
|
230
|
-
document.documentElement.setAttribute('data-theme', 'light');
|
|
231
|
-
}
|
|
232
|
-
}, [wpStyles, customTokens, darkWpStyles, darkTokens, mode]);
|
|
233
|
-
// Return children if provided, otherwise return null (theme still applies via useEffect)
|
|
234
|
-
if (children === undefined || children === null) {
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
237
|
-
return React.createElement(React.Fragment, null, children);
|
|
238
|
-
};
|
|
239
|
-
|
|
240
5
|
var jsxRuntime = {exports: {}};
|
|
241
6
|
|
|
242
7
|
var reactJsxRuntime_production = {};
|
|
@@ -652,21 +417,80 @@ if (process.env.NODE_ENV === 'production') {
|
|
|
652
417
|
var jsxRuntimeExports = jsxRuntime.exports;
|
|
653
418
|
|
|
654
419
|
/**
|
|
655
|
-
*
|
|
656
|
-
*
|
|
657
|
-
*
|
|
658
|
-
* 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
|
|
659
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
|
+
}
|
|
660
449
|
/**
|
|
661
|
-
*
|
|
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
|
|
662
455
|
*/
|
|
663
|
-
function
|
|
664
|
-
const block = section.block;
|
|
665
|
-
const attrs = block.attributes || {};
|
|
456
|
+
function determineSectionStyle(section, previousStyle) {
|
|
666
457
|
const className = section.customClass?.toLowerCase() || '';
|
|
667
|
-
// Check for
|
|
668
|
-
if (
|
|
669
|
-
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';
|
|
670
494
|
}
|
|
671
495
|
// Check for footer (could be detected by block name or class)
|
|
672
496
|
if (className.includes('footer') || block.name === 'core/footer') {
|
|
@@ -680,101 +504,6 @@ function selectTemplate(section) {
|
|
|
680
504
|
return 'SectionBase';
|
|
681
505
|
}
|
|
682
506
|
|
|
683
|
-
const SectionBase = ({ section, children }) => {
|
|
684
|
-
const style = section.style || 'default';
|
|
685
|
-
const bgColor = `var(--dstyler-color-${style})`;
|
|
686
|
-
const className = [
|
|
687
|
-
'dstyler-section',
|
|
688
|
-
`dstyler-section-${style}`,
|
|
689
|
-
section.customClass,
|
|
690
|
-
].filter(Boolean).join(' ');
|
|
691
|
-
return (jsxRuntimeExports.jsx("section", { className: className, id: section.customId, style: {
|
|
692
|
-
backgroundColor: bgColor,
|
|
693
|
-
padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
|
|
694
|
-
}, children: jsxRuntimeExports.jsx("div", { style: {
|
|
695
|
-
maxWidth: 'var(--dstyler-container-max-width, 1200px)',
|
|
696
|
-
margin: '0 auto',
|
|
697
|
-
}, children: children }) }));
|
|
698
|
-
};
|
|
699
|
-
|
|
700
|
-
const SectionWave = ({ section, children, waveTop = true, waveBottom = true, }) => {
|
|
701
|
-
const style = section.style || 'default';
|
|
702
|
-
const bgColor = `var(--dstyler-color-${style})`;
|
|
703
|
-
const className = [
|
|
704
|
-
'dstyler-section',
|
|
705
|
-
'dstyler-section-wave',
|
|
706
|
-
`dstyler-section-${style}`,
|
|
707
|
-
section.customClass,
|
|
708
|
-
].filter(Boolean).join(' ');
|
|
709
|
-
// Wave SVG
|
|
710
|
-
const waveSvg = (jsxRuntimeExports.jsx("svg", { viewBox: "0 0 1440 120", preserveAspectRatio: "none", style: {
|
|
711
|
-
width: '100%',
|
|
712
|
-
height: '60px',
|
|
713
|
-
display: 'block',
|
|
714
|
-
fill: bgColor,
|
|
715
|
-
}, children: jsxRuntimeExports.jsx("path", { d: "M0,60 C240,0 480,120 720,60 C960,0 1200,120 1440,60 L1440,120 L0,120 Z" }) }));
|
|
716
|
-
return (jsxRuntimeExports.jsxs("section", { className: className, id: section.customId, style: {
|
|
717
|
-
backgroundColor: bgColor,
|
|
718
|
-
position: 'relative',
|
|
719
|
-
}, children: [waveTop && (jsxRuntimeExports.jsx("div", { style: { marginTop: '-1px' }, children: waveSvg })), jsxRuntimeExports.jsx("div", { style: {
|
|
720
|
-
padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
|
|
721
|
-
}, children: jsxRuntimeExports.jsx("div", { style: {
|
|
722
|
-
maxWidth: 'var(--dstyler-container-max-width, 1200px)',
|
|
723
|
-
margin: '0 auto',
|
|
724
|
-
}, children: children }) }), waveBottom && (jsxRuntimeExports.jsx("div", { style: { marginBottom: '-1px', transform: 'rotate(180deg)' }, children: waveSvg }))] }));
|
|
725
|
-
};
|
|
726
|
-
|
|
727
|
-
const HeroDefault = ({ section, children }) => {
|
|
728
|
-
const style = section.style || 'default';
|
|
729
|
-
const bgColor = `var(--dstyler-color-${style})`;
|
|
730
|
-
const className = [
|
|
731
|
-
'dstyler-hero',
|
|
732
|
-
'dstyler-hero-default',
|
|
733
|
-
`dstyler-hero-${style}`,
|
|
734
|
-
section.customClass,
|
|
735
|
-
].filter(Boolean).join(' ');
|
|
736
|
-
return (jsxRuntimeExports.jsx("section", { className: className, id: section.customId, style: {
|
|
737
|
-
backgroundColor: bgColor,
|
|
738
|
-
padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
|
|
739
|
-
minHeight: '50vh',
|
|
740
|
-
display: 'flex',
|
|
741
|
-
alignItems: 'center',
|
|
742
|
-
justifyContent: 'center',
|
|
743
|
-
}, children: jsxRuntimeExports.jsx("div", { style: {
|
|
744
|
-
maxWidth: 'var(--dstyler-container-max-width, 1200px)',
|
|
745
|
-
margin: '0 auto',
|
|
746
|
-
textAlign: 'center',
|
|
747
|
-
}, children: children }) }));
|
|
748
|
-
};
|
|
749
|
-
|
|
750
|
-
const FooterMinimal = ({ section, children }) => {
|
|
751
|
-
const style = section.style || 'default';
|
|
752
|
-
const bgColor = `var(--dstyler-color-${style})`;
|
|
753
|
-
const className = [
|
|
754
|
-
'dstyler-footer',
|
|
755
|
-
'dstyler-footer-minimal',
|
|
756
|
-
`dstyler-footer-${style}`,
|
|
757
|
-
section.customClass,
|
|
758
|
-
].filter(Boolean).join(' ');
|
|
759
|
-
return (jsxRuntimeExports.jsx("footer", { className: className, id: section.customId, style: {
|
|
760
|
-
backgroundColor: bgColor,
|
|
761
|
-
padding: 'var(--dstyler-spacing-lg) var(--dstyler-spacing-md)',
|
|
762
|
-
borderTop: '1px solid var(--dstyler-color-alternate)',
|
|
763
|
-
}, children: jsxRuntimeExports.jsx("div", { style: {
|
|
764
|
-
maxWidth: 'var(--dstyler-container-max-width, 1200px)',
|
|
765
|
-
margin: '0 auto',
|
|
766
|
-
textAlign: 'center',
|
|
767
|
-
fontSize: '0.875rem',
|
|
768
|
-
color: 'var(--dstyler-color-text)',
|
|
769
|
-
}, children: children }) }));
|
|
770
|
-
};
|
|
771
|
-
|
|
772
|
-
/**
|
|
773
|
-
* @license GPL-3.0-or-later
|
|
774
|
-
*
|
|
775
|
-
* This file is part of the MarVAlt Open SDK.
|
|
776
|
-
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
777
|
-
*/
|
|
778
507
|
/**
|
|
779
508
|
* Select block renderer based on block type
|
|
780
509
|
*/
|
|
@@ -795,7 +524,7 @@ function selectBlockRenderer(block) {
|
|
|
795
524
|
case 'core/shortcode':
|
|
796
525
|
// Check if it's a form shortcode
|
|
797
526
|
const attrs = block.attributes || {};
|
|
798
|
-
const content = attrs.content || attrs.html || '';
|
|
527
|
+
const content = (attrs.content || attrs.html || '');
|
|
799
528
|
if (content.includes('[gravityform') || content.includes('[mauticform')) {
|
|
800
529
|
return 'BlockForm';
|
|
801
530
|
}
|
|
@@ -807,7 +536,7 @@ function selectBlockRenderer(block) {
|
|
|
807
536
|
|
|
808
537
|
const BlockText = ({ block, className }) => {
|
|
809
538
|
const attrs = block.attributes || {};
|
|
810
|
-
const content = attrs.content || attrs.text || '';
|
|
539
|
+
const content = (attrs.content || attrs.text || '');
|
|
811
540
|
const tagName = block.name === 'core/heading'
|
|
812
541
|
? `h${attrs.level || 2}`
|
|
813
542
|
: 'p';
|
|
@@ -827,40 +556,38 @@ const BlockText = ({ block, className }) => {
|
|
|
827
556
|
|
|
828
557
|
const BlockImage = ({ block, className }) => {
|
|
829
558
|
const attrs = block.attributes || {};
|
|
830
|
-
const url = attrs.url || attrs.sourceUrl || '';
|
|
831
|
-
const alt = attrs.alt || attrs.altText || '';
|
|
559
|
+
const url = (attrs.url || attrs.sourceUrl || '');
|
|
560
|
+
const alt = (attrs.alt || attrs.altText || '');
|
|
832
561
|
const width = attrs.width;
|
|
833
562
|
const height = attrs.height;
|
|
834
563
|
if (!url)
|
|
835
564
|
return null;
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
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" }));
|
|
843
571
|
};
|
|
844
572
|
|
|
845
573
|
const BlockButton = ({ block, className }) => {
|
|
846
574
|
const attrs = block.attributes || {};
|
|
847
|
-
const text = attrs.text || attrs.label || 'Button';
|
|
848
|
-
const url = attrs.url || attrs.linkUrl || '#';
|
|
575
|
+
const text = (attrs.text || attrs.label || 'Button');
|
|
576
|
+
const url = (attrs.url || attrs.linkUrl || '#');
|
|
849
577
|
const isExternal = typeof window !== 'undefined'
|
|
850
578
|
? url.startsWith('http') && !url.includes(window.location.hostname)
|
|
851
579
|
: url.startsWith('http');
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
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) => {
|
|
864
591
|
e.currentTarget.style.opacity = '0.9';
|
|
865
592
|
}, onMouseLeave: (e) => {
|
|
866
593
|
e.currentTarget.style.opacity = '1';
|
|
@@ -869,16 +596,22 @@ const BlockButton = ({ block, className }) => {
|
|
|
869
596
|
|
|
870
597
|
const BlockForm = ({ block, className }) => {
|
|
871
598
|
const attrs = block.attributes || {};
|
|
872
|
-
const content = attrs.content || attrs.html || '';
|
|
599
|
+
const content = (attrs.content || attrs.html || '');
|
|
873
600
|
// For now, just render the HTML content
|
|
874
601
|
// In the future, this could parse and render using Gravity Forms or Mautic components
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
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 } }));
|
|
879
605
|
};
|
|
880
606
|
|
|
881
|
-
|
|
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 }) => {
|
|
882
615
|
// If registry is provided, use it for rendering (integrates with wparser)
|
|
883
616
|
if (registry && registry.renderers[block.name]) {
|
|
884
617
|
const Renderer = registry.renderers[block.name];
|
|
@@ -888,19 +621,111 @@ const AutoBlock = ({ block, registry }) => {
|
|
|
888
621
|
const rendererName = selectBlockRenderer(block);
|
|
889
622
|
switch (rendererName) {
|
|
890
623
|
case 'BlockText':
|
|
891
|
-
return jsxRuntimeExports.jsx(BlockText, { block: block });
|
|
624
|
+
return jsxRuntimeExports.jsx(BlockText, { block: block, className: className });
|
|
892
625
|
case 'BlockImage':
|
|
893
|
-
return jsxRuntimeExports.jsx(BlockImage, { block: block });
|
|
626
|
+
return jsxRuntimeExports.jsx(BlockImage, { block: block, className: className });
|
|
894
627
|
case 'BlockButton':
|
|
895
|
-
return jsxRuntimeExports.jsx(BlockButton, { block: block });
|
|
628
|
+
return jsxRuntimeExports.jsx(BlockButton, { block: block, className: className });
|
|
896
629
|
case 'BlockForm':
|
|
897
|
-
return jsxRuntimeExports.jsx(BlockForm, { block: block });
|
|
630
|
+
return jsxRuntimeExports.jsx(BlockForm, { block: block, className: className });
|
|
898
631
|
default:
|
|
899
632
|
// Fallback: render block name or content
|
|
900
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)] }) }));
|
|
901
634
|
}
|
|
902
635
|
};
|
|
903
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
|
+
*/
|
|
904
729
|
const AutoSection = ({ section, registry }) => {
|
|
905
730
|
const templateName = selectTemplate(section);
|
|
906
731
|
// Render blocks using AutoBlock or registry
|
|
@@ -921,85 +746,20 @@ const AutoSection = ({ section, registry }) => {
|
|
|
921
746
|
return (jsxRuntimeExports.jsx(SectionWave, { section: section, waveTop: waveTop, waveBottom: waveBottom, children: renderBlocks() }));
|
|
922
747
|
}
|
|
923
748
|
case 'HeroDefault':
|
|
924
|
-
return
|
|
749
|
+
return jsxRuntimeExports.jsx(HeroDefault, { section: section, children: renderBlocks() });
|
|
925
750
|
case 'FooterMinimal':
|
|
926
|
-
return
|
|
751
|
+
return jsxRuntimeExports.jsx(FooterMinimal, { section: section, children: renderBlocks() });
|
|
927
752
|
default:
|
|
928
|
-
return
|
|
753
|
+
return jsxRuntimeExports.jsx(SectionBase, { section: section, children: renderBlocks() });
|
|
929
754
|
}
|
|
930
755
|
};
|
|
931
756
|
|
|
932
757
|
/**
|
|
933
|
-
*
|
|
758
|
+
* StyledPage - Main entry point for dstyler
|
|
934
759
|
*
|
|
935
|
-
*
|
|
936
|
-
*
|
|
937
|
-
*/
|
|
938
|
-
/**
|
|
939
|
-
* Detect sections from WordPress blocks
|
|
940
|
-
* Sections are defined by core/group blocks (with or without alignment)
|
|
941
|
-
* 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
|
|
942
762
|
*/
|
|
943
|
-
function detectSections(blocks) {
|
|
944
|
-
const sections = [];
|
|
945
|
-
let sectionIndex = 0;
|
|
946
|
-
for (const block of blocks) {
|
|
947
|
-
// core/group blocks are sections
|
|
948
|
-
if (block.name === 'core/group') {
|
|
949
|
-
const attrs = block.attributes || {};
|
|
950
|
-
const className = attrs.className;
|
|
951
|
-
const customId = attrs.id;
|
|
952
|
-
// Extract blocks from innerBlocks
|
|
953
|
-
const innerBlocks = block.innerBlocks || [];
|
|
954
|
-
sections.push({
|
|
955
|
-
id: `section-${sectionIndex}`,
|
|
956
|
-
type: 'section',
|
|
957
|
-
block,
|
|
958
|
-
blocks: innerBlocks,
|
|
959
|
-
index: sectionIndex,
|
|
960
|
-
customClass: className,
|
|
961
|
-
customId,
|
|
962
|
-
});
|
|
963
|
-
sectionIndex++;
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
return sections;
|
|
967
|
-
}
|
|
968
|
-
/**
|
|
969
|
-
* Determine section style based on alternating logic and custom classes
|
|
970
|
-
* Rules:
|
|
971
|
-
* - default is always first
|
|
972
|
-
* - after default follows alternate
|
|
973
|
-
* - highlight resets - next section is default
|
|
974
|
-
*/
|
|
975
|
-
function determineSectionStyle(section, previousStyle) {
|
|
976
|
-
const className = section.customClass?.toLowerCase() || '';
|
|
977
|
-
// Check for custom class overrides first
|
|
978
|
-
if (className.includes('highlight') || className.includes('highlight-section')) {
|
|
979
|
-
return 'highlight';
|
|
980
|
-
}
|
|
981
|
-
if (className.includes('dark') || className.includes('dark-section')) {
|
|
982
|
-
return 'alternate';
|
|
983
|
-
}
|
|
984
|
-
if (className.includes('default') || className.includes('default-section')) {
|
|
985
|
-
return 'default';
|
|
986
|
-
}
|
|
987
|
-
// Alternating logic
|
|
988
|
-
if (section.index === 0) {
|
|
989
|
-
return 'default';
|
|
990
|
-
}
|
|
991
|
-
if (previousStyle === 'highlight') {
|
|
992
|
-
return 'default';
|
|
993
|
-
}
|
|
994
|
-
if (previousStyle === 'default') {
|
|
995
|
-
return 'alternate';
|
|
996
|
-
}
|
|
997
|
-
if (previousStyle === 'alternate') {
|
|
998
|
-
return 'default';
|
|
999
|
-
}
|
|
1000
|
-
return 'default';
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
763
|
const StyledPage = ({ page, registry }) => {
|
|
1004
764
|
const blocks = page.blocks || [];
|
|
1005
765
|
// Detect sections from blocks
|
|
@@ -1014,25 +774,175 @@ const StyledPage = ({ page, registry }) => {
|
|
|
1014
774
|
style,
|
|
1015
775
|
};
|
|
1016
776
|
});
|
|
1017
|
-
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
|
+
},
|
|
1018
802
|
};
|
|
1019
803
|
|
|
1020
804
|
/**
|
|
1021
|
-
*
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
805
|
+
* Adjust brightness of a hex color
|
|
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
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Blend two colors
|
|
1025
816
|
*/
|
|
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
|
+
}
|
|
1026
837
|
/**
|
|
1027
|
-
*
|
|
838
|
+
* Convert WordPress theme styles to design tokens
|
|
1028
839
|
*/
|
|
1029
|
-
function
|
|
1030
|
-
if (!
|
|
1031
|
-
return
|
|
840
|
+
function convertWordPressToTokens(wpStyles) {
|
|
841
|
+
if (!wpStyles) {
|
|
842
|
+
return defaultTokens;
|
|
1032
843
|
}
|
|
1033
|
-
|
|
1034
|
-
//
|
|
1035
|
-
|
|
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
|
+
`;
|
|
1036
946
|
}
|
|
1037
947
|
|
|
1038
948
|
/**
|
|
@@ -1041,6 +951,78 @@ function getResponsiveValue(mobile, tablet, desktop) {
|
|
|
1041
951
|
* This file is part of the MarVAlt Open SDK.
|
|
1042
952
|
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
1043
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
|
+
}
|
|
1044
1026
|
/**
|
|
1045
1027
|
* Create a colorMapper from design tokens and WordPress theme palette
|
|
1046
1028
|
*
|
|
@@ -1067,7 +1049,9 @@ function getResponsiveValue(mobile, tablet, desktop) {
|
|
|
1067
1049
|
*/
|
|
1068
1050
|
function createColorMapperFromTokens(tokens, themePalette, overrides) {
|
|
1069
1051
|
// Debug logging
|
|
1070
|
-
if (typeof window !== 'undefined' &&
|
|
1052
|
+
if (typeof window !== 'undefined' &&
|
|
1053
|
+
typeof process !== 'undefined' &&
|
|
1054
|
+
process.env.NODE_ENV === 'development') {
|
|
1071
1055
|
console.log('🎨 createColorMapperFromTokens - themePalette:', themePalette);
|
|
1072
1056
|
console.log('🎨 createColorMapperFromTokens - tokens:', tokens);
|
|
1073
1057
|
}
|
|
@@ -1084,7 +1068,9 @@ function createColorMapperFromTokens(tokens, themePalette, overrides) {
|
|
|
1084
1068
|
const textColor = isDark ? 'text-white' : 'text-gray-900';
|
|
1085
1069
|
// Include both bg (for background mapping) and text-[color] (for text mapping), plus contrast helper
|
|
1086
1070
|
const result = `bg-[${color}] text-[${color}] ${textColor}`;
|
|
1087
|
-
if (typeof window !== 'undefined' &&
|
|
1071
|
+
if (typeof window !== 'undefined' &&
|
|
1072
|
+
typeof process !== 'undefined' &&
|
|
1073
|
+
process.env.NODE_ENV === 'development') {
|
|
1088
1074
|
console.log(`🎨 Color mapper - ${wpColorName}:`, result, '(from theme palette)');
|
|
1089
1075
|
}
|
|
1090
1076
|
return result;
|
|
@@ -1092,53 +1078,25 @@ function createColorMapperFromTokens(tokens, themePalette, overrides) {
|
|
|
1092
1078
|
// Fallback: Map WordPress theme colors to design tokens
|
|
1093
1079
|
// Using Tailwind's arbitrary value syntax for hex colors
|
|
1094
1080
|
const defaultMap = {
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
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}]`,
|
|
1103
1089
|
'accent-1': `bg-[${themePalette?.['accent-1'] || tokens.colors.primary}] text-[${themePalette?.['accent-1'] || tokens.colors.primary}]`,
|
|
1104
1090
|
'accent-2': `bg-[${themePalette?.['accent-2'] || tokens.colors.secondary}] text-[${themePalette?.['accent-2'] || tokens.colors.secondary}]`,
|
|
1105
1091
|
'accent-3': `bg-[${themePalette?.['accent-3'] || tokens.colors.highlight}] text-[${themePalette?.['accent-3'] || tokens.colors.highlight}]`,
|
|
1106
1092
|
'accent-4': `bg-[${themePalette?.['accent-4'] || tokens.colors.alternate}] text-[${themePalette?.['accent-4'] || tokens.colors.alternate}]`,
|
|
1107
1093
|
'accent-5': `bg-[${themePalette?.['accent-5'] || tokens.colors.default}] text-[${themePalette?.['accent-5'] || tokens.colors.default}]`,
|
|
1108
1094
|
'accent-6': `bg-[${themePalette?.['accent-6'] || themePalette?.primary || tokens.colors.primary}] text-[${themePalette?.['accent-6'] || themePalette?.primary || tokens.colors.primary}]`,
|
|
1109
|
-
|
|
1095
|
+
transparent: 'bg-transparent',
|
|
1110
1096
|
};
|
|
1111
1097
|
return wpColorName ? defaultMap[wpColorName] || null : null;
|
|
1112
1098
|
};
|
|
1113
1099
|
}
|
|
1114
|
-
/**
|
|
1115
|
-
* Check if a color is dark (for determining text color)
|
|
1116
|
-
*/
|
|
1117
|
-
function isColorDark(color) {
|
|
1118
|
-
// Remove # if present
|
|
1119
|
-
const hex = color.replace('#', '');
|
|
1120
|
-
// Handle RGB/RGBA format
|
|
1121
|
-
if (color.startsWith('rgb')) {
|
|
1122
|
-
const match = color.match(/\d+/g);
|
|
1123
|
-
if (match && match.length >= 3) {
|
|
1124
|
-
const r = parseInt(match[0], 10);
|
|
1125
|
-
const g = parseInt(match[1], 10);
|
|
1126
|
-
const b = parseInt(match[2], 10);
|
|
1127
|
-
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
|
1128
|
-
return brightness < 128;
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
// Handle hex format
|
|
1132
|
-
if (hex.length === 6) {
|
|
1133
|
-
const r = parseInt(hex.substring(0, 2), 16);
|
|
1134
|
-
const g = parseInt(hex.substring(2, 4), 16);
|
|
1135
|
-
const b = parseInt(hex.substring(4, 6), 16);
|
|
1136
|
-
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
|
1137
|
-
return brightness < 128;
|
|
1138
|
-
}
|
|
1139
|
-
// Default to light background
|
|
1140
|
-
return false;
|
|
1141
|
-
}
|
|
1142
1100
|
/**
|
|
1143
1101
|
* Create a colorMapper with semantic Tailwind classes
|
|
1144
1102
|
*
|
|
@@ -1166,9 +1124,9 @@ function createColorMapperWithSemanticClasses(tokens, overrides) {
|
|
|
1166
1124
|
// Map WordPress theme colors to semantic Tailwind classes
|
|
1167
1125
|
// Assumes your Tailwind config has primary, secondary, muted, etc. defined
|
|
1168
1126
|
const defaultMap = {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1127
|
+
base: 'bg-white',
|
|
1128
|
+
contrast: 'bg-gray-900 text-white',
|
|
1129
|
+
transparent: 'bg-transparent',
|
|
1172
1130
|
'accent-1': 'bg-primary text-primary-foreground',
|
|
1173
1131
|
'accent-2': 'bg-secondary text-secondary-foreground',
|
|
1174
1132
|
'accent-3': 'bg-muted text-muted-foreground',
|
|
@@ -1180,8 +1138,30 @@ function createColorMapperWithSemanticClasses(tokens, overrides) {
|
|
|
1180
1138
|
};
|
|
1181
1139
|
}
|
|
1182
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
|
+
|
|
1183
1159
|
exports.AutoBlock = AutoBlock;
|
|
1184
1160
|
exports.AutoSection = AutoSection;
|
|
1161
|
+
exports.BlockButton = BlockButton;
|
|
1162
|
+
exports.BlockForm = BlockForm;
|
|
1163
|
+
exports.BlockImage = BlockImage;
|
|
1164
|
+
exports.BlockText = BlockText;
|
|
1185
1165
|
exports.FooterMinimal = FooterMinimal;
|
|
1186
1166
|
exports.HeroDefault = HeroDefault;
|
|
1187
1167
|
exports.SectionBase = SectionBase;
|