@wire-dsl/engine 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +425 -117
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +425 -117
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1647,7 +1647,8 @@ function validateSemanticDiagnostics(ast, sourceMap) {
|
|
|
1647
1647
|
if (enumValues) {
|
|
1648
1648
|
const normalizedValue = String(propValue);
|
|
1649
1649
|
const isCustomVariantFromColors = propName === "variant" && !enumValues.includes(normalizedValue) && Object.prototype.hasOwnProperty.call(ast.colors || {}, normalizedValue);
|
|
1650
|
-
|
|
1650
|
+
const isPropReference = normalizedValue.startsWith("prop_");
|
|
1651
|
+
if (!enumValues.includes(normalizedValue) && !isCustomVariantFromColors && !isPropReference) {
|
|
1651
1652
|
emitWarning(
|
|
1652
1653
|
`Invalid value "${normalizedValue}" for property "${propName}" in component "${componentType}".`,
|
|
1653
1654
|
"COMPONENT_INVALID_PROPERTY_VALUE",
|
|
@@ -1757,7 +1758,7 @@ function validateSemanticDiagnostics(ast, sourceMap) {
|
|
|
1757
1758
|
const enumValues = rules.enumParams?.[paramName];
|
|
1758
1759
|
if (enumValues) {
|
|
1759
1760
|
const normalizedValue = String(paramValue);
|
|
1760
|
-
if (!enumValues.includes(normalizedValue)) {
|
|
1761
|
+
if (!enumValues.includes(normalizedValue) && !normalizedValue.startsWith("prop_")) {
|
|
1761
1762
|
emitWarning(
|
|
1762
1763
|
`Invalid value "${normalizedValue}" for parameter "${paramName}" in layout "${layout.layoutType}".`,
|
|
1763
1764
|
"LAYOUT_INVALID_PARAMETER_VALUE",
|
|
@@ -2643,6 +2644,20 @@ ${messages}`);
|
|
|
2643
2644
|
key
|
|
2644
2645
|
);
|
|
2645
2646
|
if (resolvedValue !== void 0) {
|
|
2647
|
+
const wasPropReference = typeof value === "string" && value.startsWith("prop_");
|
|
2648
|
+
if (wasPropReference) {
|
|
2649
|
+
const layoutMetadata = LAYOUTS2[layoutType];
|
|
2650
|
+
const property = layoutMetadata?.properties?.[key];
|
|
2651
|
+
if (property?.type === "enum" && Array.isArray(property.options)) {
|
|
2652
|
+
const normalizedValue = String(resolvedValue);
|
|
2653
|
+
if (!property.options.includes(normalizedValue)) {
|
|
2654
|
+
this.warnings.push({
|
|
2655
|
+
type: "invalid-bound-enum-value",
|
|
2656
|
+
message: `Invalid value "${normalizedValue}" for parameter "${key}" in layout "${layoutType}". Expected one of: ${property.options.join(", ")}.`
|
|
2657
|
+
});
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2646
2661
|
resolved[key] = resolvedValue;
|
|
2647
2662
|
}
|
|
2648
2663
|
}
|
|
@@ -2707,6 +2722,20 @@ ${messages}`);
|
|
|
2707
2722
|
key
|
|
2708
2723
|
);
|
|
2709
2724
|
if (resolvedValue !== void 0) {
|
|
2725
|
+
const wasPropReference = typeof value === "string" && value.startsWith("prop_");
|
|
2726
|
+
if (wasPropReference) {
|
|
2727
|
+
const metadata = COMPONENTS2[componentType];
|
|
2728
|
+
const property = metadata?.properties?.[key];
|
|
2729
|
+
if (property?.type === "enum" && Array.isArray(property.options)) {
|
|
2730
|
+
const normalizedValue = String(resolvedValue);
|
|
2731
|
+
if (!property.options.includes(normalizedValue)) {
|
|
2732
|
+
this.warnings.push({
|
|
2733
|
+
type: "invalid-bound-enum-value",
|
|
2734
|
+
message: `Invalid value "${normalizedValue}" for property "${key}" in component "${componentType}". Expected one of: ${property.options.join(", ")}.`
|
|
2735
|
+
});
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2710
2739
|
resolved[key] = resolvedValue;
|
|
2711
2740
|
}
|
|
2712
2741
|
}
|
|
@@ -4517,14 +4546,14 @@ var THEMES = {
|
|
|
4517
4546
|
primaryLight: "#EFF6FF"
|
|
4518
4547
|
},
|
|
4519
4548
|
dark: {
|
|
4520
|
-
bg: "#
|
|
4521
|
-
cardBg: "#
|
|
4522
|
-
border: "#
|
|
4523
|
-
text: "#
|
|
4524
|
-
textMuted: "#
|
|
4549
|
+
bg: "#111111",
|
|
4550
|
+
cardBg: "#1C1C1C",
|
|
4551
|
+
border: "#303030",
|
|
4552
|
+
text: "#F0F0F0",
|
|
4553
|
+
textMuted: "#808080",
|
|
4525
4554
|
primary: "#60A5FA",
|
|
4526
4555
|
primaryHover: "#3B82F6",
|
|
4527
|
-
primaryLight: "#
|
|
4556
|
+
primaryLight: "#1C2A3A"
|
|
4528
4557
|
}
|
|
4529
4558
|
};
|
|
4530
4559
|
var SVGRenderer = class {
|
|
@@ -4765,6 +4794,8 @@ var SVGRenderer = class {
|
|
|
4765
4794
|
const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
|
|
4766
4795
|
const labelOffset = this.parseBooleanProp(node.props.labelSpace, false) ? 18 : 0;
|
|
4767
4796
|
const fullWidth = this.shouldButtonFillAvailableWidth(node);
|
|
4797
|
+
const iconName = String(node.props.icon || "").trim();
|
|
4798
|
+
const iconAlign = String(node.props.iconAlign || "left").toLowerCase();
|
|
4768
4799
|
const radius = this.tokens.button.radius;
|
|
4769
4800
|
const fontSize = this.tokens.button.fontSize;
|
|
4770
4801
|
const fontWeight = this.tokens.button.fontWeight;
|
|
@@ -4772,30 +4803,62 @@ var SVGRenderer = class {
|
|
|
4772
4803
|
const controlHeight = resolveActionControlHeight(size, density);
|
|
4773
4804
|
const buttonY = pos.y + labelOffset;
|
|
4774
4805
|
const buttonHeight = Math.max(16, Math.min(controlHeight, pos.height - labelOffset));
|
|
4806
|
+
const iconSvg = iconName ? getIcon(iconName) : null;
|
|
4807
|
+
const iconSize = iconSvg ? Math.round(fontSize * 1.1) : 0;
|
|
4808
|
+
const iconGap = iconSvg ? 8 : 0;
|
|
4809
|
+
const edgePad = 12;
|
|
4810
|
+
const textPad = paddingX + extraPadding;
|
|
4775
4811
|
const idealTextWidth = this.estimateTextWidth(text, fontSize);
|
|
4776
|
-
const buttonWidth = fullWidth ? Math.max(1, pos.width) : this.clampControlWidth(Math.max(Math.ceil(idealTextWidth + (
|
|
4777
|
-
const availableTextWidth = Math.max(0, buttonWidth - (
|
|
4812
|
+
const buttonWidth = fullWidth ? Math.max(1, pos.width) : this.clampControlWidth(Math.max(Math.ceil(idealTextWidth + (iconSvg ? iconSize + iconGap : 0) + textPad * 2), 60), pos.width);
|
|
4813
|
+
const availableTextWidth = Math.max(0, buttonWidth - textPad * 2 - (iconSvg ? iconSize + iconGap : 0));
|
|
4778
4814
|
const visibleText = this.truncateTextToWidth(text, availableTextWidth, fontSize);
|
|
4779
4815
|
const semanticBase = this.getSemanticVariantColor(variant);
|
|
4780
4816
|
const hasExplicitVariantColor = semanticBase !== void 0 || this.colorResolver.hasColor(variant);
|
|
4781
4817
|
const resolvedBase = this.resolveVariantColor(variant, this.renderTheme.primary);
|
|
4782
|
-
const
|
|
4818
|
+
const isDarkMode = this.options.theme === "dark";
|
|
4819
|
+
const bgColor = hasExplicitVariantColor ? this.hexToRgba(resolvedBase, 0.85) : isDarkMode ? "rgba(48, 48, 55, 0.9)" : "rgba(226, 232, 240, 0.9)";
|
|
4783
4820
|
const textColor = hasExplicitVariantColor ? "#FFFFFF" : this.hexToRgba(this.resolveTextColor(), 0.85);
|
|
4784
|
-
const borderColor = hasExplicitVariantColor ? this.hexToRgba(resolvedBase, 0.7) : "rgba(100, 116, 139, 0.4)";
|
|
4785
|
-
|
|
4821
|
+
const borderColor = hasExplicitVariantColor ? this.hexToRgba(resolvedBase, 0.7) : isDarkMode ? "rgba(75, 75, 88, 0.8)" : "rgba(100, 116, 139, 0.4)";
|
|
4822
|
+
const iconOffsetY = buttonY + (buttonHeight - iconSize) / 2;
|
|
4823
|
+
const iconX = iconAlign === "right" ? pos.x + buttonWidth - edgePad - iconSize : pos.x + edgePad;
|
|
4824
|
+
const textAlign = String(node.props.align || "center").toLowerCase();
|
|
4825
|
+
const sidePad = textPad + 4;
|
|
4826
|
+
let textX;
|
|
4827
|
+
let textAnchor;
|
|
4828
|
+
if (textAlign === "left") {
|
|
4829
|
+
textX = iconSvg && iconAlign === "left" ? pos.x + edgePad + iconSize + iconGap : pos.x + sidePad;
|
|
4830
|
+
textAnchor = "start";
|
|
4831
|
+
} else if (textAlign === "right") {
|
|
4832
|
+
textX = iconSvg && iconAlign === "right" ? pos.x + buttonWidth - edgePad - iconSize - iconGap : pos.x + buttonWidth - sidePad;
|
|
4833
|
+
textAnchor = "end";
|
|
4834
|
+
} else {
|
|
4835
|
+
textX = pos.x + buttonWidth / 2;
|
|
4836
|
+
textAnchor = "middle";
|
|
4837
|
+
}
|
|
4838
|
+
let svg = `<g${this.getDataNodeId(node)}>
|
|
4786
4839
|
<rect x="${pos.x}" y="${buttonY}"
|
|
4787
4840
|
width="${buttonWidth}" height="${buttonHeight}"
|
|
4788
4841
|
rx="${radius}"
|
|
4789
4842
|
fill="${bgColor}"
|
|
4790
4843
|
stroke="${borderColor}"
|
|
4791
|
-
stroke-width="1"
|
|
4792
|
-
|
|
4844
|
+
stroke-width="1"/>`;
|
|
4845
|
+
if (iconSvg) {
|
|
4846
|
+
svg += `
|
|
4847
|
+
<g transform="translate(${iconX}, ${iconOffsetY})">
|
|
4848
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${textColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
4849
|
+
${this.extractSvgContent(iconSvg)}
|
|
4850
|
+
</svg>
|
|
4851
|
+
</g>`;
|
|
4852
|
+
}
|
|
4853
|
+
svg += `
|
|
4854
|
+
<text x="${textX}" y="${buttonY + buttonHeight / 2 + fontSize * 0.35}"
|
|
4793
4855
|
font-family="Arial, Helvetica, sans-serif"
|
|
4794
4856
|
font-size="${fontSize}"
|
|
4795
4857
|
font-weight="${fontWeight}"
|
|
4796
4858
|
fill="${textColor}"
|
|
4797
|
-
text-anchor="
|
|
4859
|
+
text-anchor="${textAnchor}">${this.escapeXml(visibleText)}</text>
|
|
4798
4860
|
</g>`;
|
|
4861
|
+
return svg;
|
|
4799
4862
|
}
|
|
4800
4863
|
renderLink(node, pos) {
|
|
4801
4864
|
const text = String(node.props.text || "Link");
|
|
@@ -4836,28 +4899,66 @@ var SVGRenderer = class {
|
|
|
4836
4899
|
renderInput(node, pos) {
|
|
4837
4900
|
const label = String(node.props.label || "");
|
|
4838
4901
|
const placeholder = String(node.props.placeholder || "");
|
|
4902
|
+
const iconLeftName = String(node.props.iconLeft || "").trim();
|
|
4903
|
+
const iconRightName = String(node.props.iconRight || "").trim();
|
|
4839
4904
|
const radius = this.tokens.input.radius;
|
|
4840
4905
|
const fontSize = this.tokens.input.fontSize;
|
|
4841
4906
|
const paddingX = this.tokens.input.paddingX;
|
|
4842
4907
|
const labelOffset = this.getControlLabelOffset(label);
|
|
4843
4908
|
const controlY = pos.y + labelOffset;
|
|
4844
4909
|
const controlHeight = Math.max(16, pos.height - labelOffset);
|
|
4845
|
-
|
|
4846
|
-
|
|
4910
|
+
const iconSize = 16;
|
|
4911
|
+
const iconPad = 12;
|
|
4912
|
+
const iconInnerGap = 8;
|
|
4913
|
+
const iconLeftSvg = iconLeftName ? getIcon(iconLeftName) : null;
|
|
4914
|
+
const iconRightSvg = iconRightName ? getIcon(iconRightName) : null;
|
|
4915
|
+
const leftOffset = iconLeftSvg ? iconPad + iconSize + iconInnerGap : 0;
|
|
4916
|
+
const rightOffset = iconRightSvg ? iconPad + iconSize + iconInnerGap : 0;
|
|
4917
|
+
const textX = pos.x + (iconLeftSvg ? leftOffset : paddingX);
|
|
4918
|
+
const iconColor = this.hexToRgba(this.resolveMutedColor(), 0.8);
|
|
4919
|
+
const iconCenterY = controlY + (controlHeight - iconSize) / 2;
|
|
4920
|
+
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
4921
|
+
if (label) {
|
|
4922
|
+
svg += `
|
|
4923
|
+
<text x="${pos.x + paddingX}" y="${this.getControlLabelBaselineY(pos.y)}"
|
|
4847
4924
|
font-family="Arial, Helvetica, sans-serif"
|
|
4848
4925
|
font-size="12"
|
|
4849
|
-
fill="${this.renderTheme.text}">${this.escapeXml(label)}</text
|
|
4926
|
+
fill="${this.renderTheme.text}">${this.escapeXml(label)}</text>`;
|
|
4927
|
+
}
|
|
4928
|
+
svg += `
|
|
4850
4929
|
<rect x="${pos.x}" y="${controlY}"
|
|
4851
4930
|
width="${pos.width}" height="${controlHeight}"
|
|
4852
4931
|
rx="${radius}"
|
|
4853
4932
|
fill="${this.renderTheme.cardBg}"
|
|
4854
4933
|
stroke="${this.renderTheme.border}"
|
|
4855
|
-
stroke-width="1"
|
|
4856
|
-
|
|
4934
|
+
stroke-width="1"/>`;
|
|
4935
|
+
if (iconLeftSvg) {
|
|
4936
|
+
svg += `
|
|
4937
|
+
<g transform="translate(${pos.x + iconPad}, ${iconCenterY})">
|
|
4938
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
4939
|
+
${this.extractSvgContent(iconLeftSvg)}
|
|
4940
|
+
</svg>
|
|
4941
|
+
</g>`;
|
|
4942
|
+
}
|
|
4943
|
+
if (iconRightSvg) {
|
|
4944
|
+
svg += `
|
|
4945
|
+
<g transform="translate(${pos.x + pos.width - iconPad - iconSize}, ${iconCenterY})">
|
|
4946
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
4947
|
+
${this.extractSvgContent(iconRightSvg)}
|
|
4948
|
+
</svg>
|
|
4949
|
+
</g>`;
|
|
4950
|
+
}
|
|
4951
|
+
if (placeholder) {
|
|
4952
|
+
const availPlaceholderWidth = pos.width - (iconLeftSvg ? leftOffset : paddingX) - (iconRightSvg ? rightOffset : paddingX);
|
|
4953
|
+
const visiblePlaceholder = this.truncateTextToWidth(placeholder, Math.max(0, availPlaceholderWidth), fontSize);
|
|
4954
|
+
svg += `
|
|
4955
|
+
<text x="${textX}" y="${controlY + controlHeight / 2 + 5}"
|
|
4857
4956
|
font-family="Arial, Helvetica, sans-serif"
|
|
4858
4957
|
font-size="${fontSize}"
|
|
4859
|
-
fill="${this.renderTheme.textMuted}">${this.escapeXml(
|
|
4860
|
-
|
|
4958
|
+
fill="${this.renderTheme.textMuted}">${this.escapeXml(visiblePlaceholder)}</text>`;
|
|
4959
|
+
}
|
|
4960
|
+
svg += "\n </g>";
|
|
4961
|
+
return svg;
|
|
4861
4962
|
}
|
|
4862
4963
|
renderTopbar(node, pos) {
|
|
4863
4964
|
const title = String(node.props.title || "App");
|
|
@@ -5437,30 +5538,66 @@ var SVGRenderer = class {
|
|
|
5437
5538
|
renderSelect(node, pos) {
|
|
5438
5539
|
const label = String(node.props.label || "");
|
|
5439
5540
|
const placeholder = String(node.props.placeholder || "Select...");
|
|
5541
|
+
const iconLeftName = String(node.props.iconLeft || "").trim();
|
|
5542
|
+
const iconRightName = String(node.props.iconRight || "").trim();
|
|
5440
5543
|
const labelOffset = this.getControlLabelOffset(label);
|
|
5441
5544
|
const controlY = pos.y + labelOffset;
|
|
5442
5545
|
const controlHeight = Math.max(16, pos.height - labelOffset);
|
|
5443
5546
|
const centerY = controlY + controlHeight / 2 + 5;
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
font-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5547
|
+
const iconSize = 16;
|
|
5548
|
+
const iconPad = 12;
|
|
5549
|
+
const iconInnerGap = 8;
|
|
5550
|
+
const iconLeftSvg = iconLeftName ? getIcon(iconLeftName) : null;
|
|
5551
|
+
const iconRightSvg = iconRightName ? getIcon(iconRightName) : null;
|
|
5552
|
+
const leftOffset = iconLeftSvg ? iconPad + iconSize + iconInnerGap : 0;
|
|
5553
|
+
const chevronWidth = 20;
|
|
5554
|
+
const iconColor = this.hexToRgba(this.resolveMutedColor(), 0.8);
|
|
5555
|
+
const iconCenterY = controlY + (controlHeight - iconSize) / 2;
|
|
5556
|
+
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
5557
|
+
if (label) {
|
|
5558
|
+
svg += `
|
|
5559
|
+
<text x="${pos.x}" y="${this.getControlLabelBaselineY(pos.y)}"
|
|
5560
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
5561
|
+
font-size="12"
|
|
5562
|
+
fill="${this.renderTheme.text}">${this.escapeXml(label)}</text>`;
|
|
5563
|
+
}
|
|
5564
|
+
svg += `
|
|
5565
|
+
<rect x="${pos.x}" y="${controlY}"
|
|
5566
|
+
width="${pos.width}" height="${controlHeight}"
|
|
5567
|
+
rx="6"
|
|
5568
|
+
fill="${this.renderTheme.cardBg}"
|
|
5569
|
+
stroke="${this.renderTheme.border}"
|
|
5570
|
+
stroke-width="1"/>`;
|
|
5571
|
+
if (iconLeftSvg) {
|
|
5572
|
+
svg += `
|
|
5573
|
+
<g transform="translate(${pos.x + iconPad}, ${iconCenterY})">
|
|
5574
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
5575
|
+
${this.extractSvgContent(iconLeftSvg)}
|
|
5576
|
+
</svg>
|
|
5577
|
+
</g>`;
|
|
5578
|
+
}
|
|
5579
|
+
if (iconRightSvg) {
|
|
5580
|
+
svg += `
|
|
5581
|
+
<g transform="translate(${pos.x + pos.width - chevronWidth - iconPad - iconSize}, ${iconCenterY})">
|
|
5582
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
5583
|
+
${this.extractSvgContent(iconRightSvg)}
|
|
5584
|
+
</svg>
|
|
5585
|
+
</g>`;
|
|
5586
|
+
}
|
|
5587
|
+
const textX = pos.x + (iconLeftSvg ? leftOffset : 12);
|
|
5588
|
+
const availPlaceholderWidth = pos.width - (iconLeftSvg ? leftOffset : 12) - chevronWidth - (iconRightSvg ? iconPad + iconSize + iconInnerGap : 0);
|
|
5589
|
+
const visiblePlaceholder = this.truncateTextToWidth(placeholder, Math.max(0, availPlaceholderWidth), 14);
|
|
5590
|
+
svg += `
|
|
5591
|
+
<text x="${textX}" y="${centerY}"
|
|
5592
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
5593
|
+
font-size="14"
|
|
5594
|
+
fill="${this.renderTheme.textMuted}">${this.escapeXml(visiblePlaceholder)}</text>
|
|
5595
|
+
<text x="${pos.x + pos.width - 20}" y="${centerY}"
|
|
5596
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
5597
|
+
font-size="16"
|
|
5462
5598
|
fill="${this.renderTheme.textMuted}">\u25BC</text>
|
|
5463
5599
|
</g>`;
|
|
5600
|
+
return svg;
|
|
5464
5601
|
}
|
|
5465
5602
|
renderCheckbox(node, pos) {
|
|
5466
5603
|
const label = String(node.props.label || "Checkbox");
|
|
@@ -5893,7 +6030,9 @@ var SVGRenderer = class {
|
|
|
5893
6030
|
renderImage(node, pos) {
|
|
5894
6031
|
const placeholder = String(node.props.placeholder || "landscape").toLowerCase();
|
|
5895
6032
|
const placeholderIcon = String(node.props.icon || "").trim();
|
|
6033
|
+
const variant = String(node.props.variant || "").trim();
|
|
5896
6034
|
const placeholderIconSvg = placeholder === "icon" && placeholderIcon ? getIcon(placeholderIcon) : null;
|
|
6035
|
+
const imageBg = this.options.theme === "dark" ? "#2A2A2A" : "#E8E8E8";
|
|
5897
6036
|
const aspectRatios = {
|
|
5898
6037
|
landscape: 16 / 9,
|
|
5899
6038
|
portrait: 2 / 3,
|
|
@@ -5911,30 +6050,29 @@ var SVGRenderer = class {
|
|
|
5911
6050
|
}
|
|
5912
6051
|
const offsetX = pos.x + (pos.width - iconWidth) / 2;
|
|
5913
6052
|
const offsetY = pos.y + (pos.height - iconHeight) / 2;
|
|
5914
|
-
let svg = `<g${this.getDataNodeId(node)}>
|
|
5915
|
-
<!-- Image Background -->
|
|
5916
|
-
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="#E8E8E8"/>`;
|
|
5917
6053
|
if (placeholder === "icon" && placeholderIconSvg) {
|
|
5918
|
-
const
|
|
5919
|
-
const
|
|
5920
|
-
const
|
|
5921
|
-
const
|
|
5922
|
-
const
|
|
5923
|
-
const
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
rx="${Math.max(4, badgeSize * 0.2)}"
|
|
5929
|
-
fill="rgba(255, 255, 255, 0.6)"
|
|
5930
|
-
stroke="#888"
|
|
5931
|
-
stroke-width="1"/>
|
|
6054
|
+
const semanticBase = variant ? this.getSemanticVariantColor(variant) : void 0;
|
|
6055
|
+
const hasVariant = variant.length > 0 && (semanticBase !== void 0 || this.colorResolver.hasColor(variant));
|
|
6056
|
+
const variantColor = hasVariant ? this.resolveVariantColor(variant, this.renderTheme.primary) : null;
|
|
6057
|
+
const bgColor = hasVariant ? this.hexToRgba(variantColor, 0.12) : imageBg;
|
|
6058
|
+
const iconColor = hasVariant ? variantColor : this.options.theme === "dark" ? "#888888" : "#666666";
|
|
6059
|
+
const iconSize = Math.max(16, Math.min(pos.width, pos.height) * 0.6);
|
|
6060
|
+
const iconOffsetX = pos.x + (pos.width - iconSize) / 2;
|
|
6061
|
+
const iconOffsetY = pos.y + (pos.height - iconSize) / 2;
|
|
6062
|
+
return `<g${this.getDataNodeId(node)}>
|
|
6063
|
+
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="${bgColor}" rx="4"/>
|
|
5932
6064
|
<g transform="translate(${iconOffsetX}, ${iconOffsetY})">
|
|
5933
|
-
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="
|
|
6065
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
5934
6066
|
${this.extractSvgContent(placeholderIconSvg)}
|
|
5935
6067
|
</svg>
|
|
5936
|
-
</g
|
|
5937
|
-
}
|
|
6068
|
+
</g>
|
|
6069
|
+
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="none" stroke="${this.renderTheme.border}" stroke-width="1" rx="4"/>
|
|
6070
|
+
</g>`;
|
|
6071
|
+
}
|
|
6072
|
+
let svg = `<g${this.getDataNodeId(node)}>
|
|
6073
|
+
<!-- Image Background -->
|
|
6074
|
+
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="${imageBg}"/>`;
|
|
6075
|
+
if (["landscape", "portrait", "square"].includes(placeholder)) {
|
|
5938
6076
|
const cameraCx = offsetX + iconWidth / 2;
|
|
5939
6077
|
const cameraCy = offsetY + iconHeight / 2;
|
|
5940
6078
|
const scale = Math.min(iconWidth, iconHeight) / 24;
|
|
@@ -6038,18 +6176,22 @@ var SVGRenderer = class {
|
|
|
6038
6176
|
const fontSize = 14;
|
|
6039
6177
|
const activeIndex = Number(node.props.active || 0);
|
|
6040
6178
|
const accentColor = this.resolveAccentColor();
|
|
6179
|
+
const variantProp = String(node.props.variant || "").trim();
|
|
6180
|
+
const semanticVariant = variantProp ? this.getSemanticVariantColor(variantProp) : void 0;
|
|
6181
|
+
const hasVariant = variantProp.length > 0 && (semanticVariant !== void 0 || this.colorResolver.hasColor(variantProp));
|
|
6182
|
+
const activeColor = hasVariant ? this.resolveVariantColor(variantProp, accentColor) : accentColor;
|
|
6041
6183
|
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
6042
6184
|
items.forEach((item, index) => {
|
|
6043
6185
|
const itemY = pos.y + index * itemHeight;
|
|
6044
6186
|
const isActive = index === activeIndex;
|
|
6045
|
-
const bgColor = isActive ? this.hexToRgba(
|
|
6046
|
-
const textColor = isActive ? this.hexToRgba(
|
|
6187
|
+
const bgColor = isActive ? this.hexToRgba(activeColor, 0.15) : "transparent";
|
|
6188
|
+
const textColor = isActive ? this.hexToRgba(activeColor, 0.9) : this.hexToRgba(this.resolveTextColor(), 0.75);
|
|
6047
6189
|
const fontWeight = isActive ? "500" : "400";
|
|
6048
6190
|
if (isActive) {
|
|
6049
6191
|
svg += `
|
|
6050
|
-
<rect x="${pos.x}" y="${itemY}"
|
|
6051
|
-
width="${pos.width}" height="${itemHeight}"
|
|
6052
|
-
rx="6"
|
|
6192
|
+
<rect x="${pos.x}" y="${itemY}"
|
|
6193
|
+
width="${pos.width}" height="${itemHeight}"
|
|
6194
|
+
rx="6"
|
|
6053
6195
|
fill="${bgColor}"/>`;
|
|
6054
6196
|
}
|
|
6055
6197
|
let currentX = pos.x + 12;
|
|
@@ -6058,9 +6200,10 @@ var SVGRenderer = class {
|
|
|
6058
6200
|
if (iconSvg) {
|
|
6059
6201
|
const iconSize = 16;
|
|
6060
6202
|
const iconY = itemY + (itemHeight - iconSize) / 2;
|
|
6203
|
+
const iconColor = isActive ? this.hexToRgba(activeColor, 0.9) : this.hexToRgba(this.resolveMutedColor(), 0.9);
|
|
6061
6204
|
svg += `
|
|
6062
6205
|
<g transform="translate(${currentX}, ${iconY})">
|
|
6063
|
-
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${
|
|
6206
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
6064
6207
|
${this.extractSvgContent(iconSvg)}
|
|
6065
6208
|
</svg>
|
|
6066
6209
|
</g>`;
|
|
@@ -6068,10 +6211,10 @@ var SVGRenderer = class {
|
|
|
6068
6211
|
}
|
|
6069
6212
|
}
|
|
6070
6213
|
svg += `
|
|
6071
|
-
<text x="${currentX}" y="${itemY + itemHeight / 2 + 5}"
|
|
6072
|
-
font-family="Arial, Helvetica, sans-serif"
|
|
6073
|
-
font-size="${fontSize}"
|
|
6074
|
-
font-weight="${fontWeight}"
|
|
6214
|
+
<text x="${currentX}" y="${itemY + itemHeight / 2 + 5}"
|
|
6215
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
6216
|
+
font-size="${fontSize}"
|
|
6217
|
+
font-weight="${fontWeight}"
|
|
6075
6218
|
fill="${textColor}">${this.escapeXml(item)}</text>`;
|
|
6076
6219
|
});
|
|
6077
6220
|
svg += "\n </g>";
|
|
@@ -6111,9 +6254,10 @@ var SVGRenderer = class {
|
|
|
6111
6254
|
const semanticBase = this.getSemanticVariantColor(variant);
|
|
6112
6255
|
const hasExplicitVariantColor = semanticBase !== void 0 || this.colorResolver.hasColor(variant);
|
|
6113
6256
|
const resolvedBase = this.resolveVariantColor(variant, this.renderTheme.primary);
|
|
6114
|
-
const
|
|
6257
|
+
const isDarkMode = this.options.theme === "dark";
|
|
6258
|
+
const bgColor = hasExplicitVariantColor ? this.hexToRgba(resolvedBase, 0.85) : isDarkMode ? "rgba(48, 48, 55, 0.9)" : "rgba(226, 232, 240, 0.9)";
|
|
6115
6259
|
const iconColor = hasExplicitVariantColor ? "#FFFFFF" : this.hexToRgba(this.resolveTextColor(), 0.75);
|
|
6116
|
-
const borderColor = hasExplicitVariantColor ? this.hexToRgba(resolvedBase, 0.7) : "rgba(100, 116, 139, 0.4)";
|
|
6260
|
+
const borderColor = hasExplicitVariantColor ? this.hexToRgba(resolvedBase, 0.7) : isDarkMode ? "rgba(75, 75, 88, 0.8)" : "rgba(100, 116, 139, 0.4)";
|
|
6117
6261
|
const opacity = disabled ? "0.5" : "1";
|
|
6118
6262
|
const iconSvg = getIcon(iconName);
|
|
6119
6263
|
const buttonSize = Math.max(
|
|
@@ -6171,14 +6315,37 @@ var SVGRenderer = class {
|
|
|
6171
6315
|
return this.colorResolver.resolveColor("muted", fallback);
|
|
6172
6316
|
}
|
|
6173
6317
|
getSemanticVariantColor(variant) {
|
|
6174
|
-
const
|
|
6318
|
+
const isDark = this.options.theme === "dark";
|
|
6319
|
+
const semantic = isDark ? {
|
|
6320
|
+
// Muted mid-range — readable on #111111 without being neon
|
|
6321
|
+
primary: this.renderTheme.primary,
|
|
6322
|
+
// already theme-aware (#60A5FA)
|
|
6323
|
+
secondary: "#7E8EA2",
|
|
6324
|
+
// desaturated slate
|
|
6325
|
+
success: "#22A06B",
|
|
6326
|
+
// muted emerald
|
|
6327
|
+
warning: "#B38010",
|
|
6328
|
+
// deep amber
|
|
6329
|
+
danger: "#CC4444",
|
|
6330
|
+
// muted red
|
|
6331
|
+
error: "#CC4444",
|
|
6332
|
+
info: "#2485AF"
|
|
6333
|
+
// muted sky
|
|
6334
|
+
} : {
|
|
6335
|
+
// Tailwind 500-level — works on white/light backgrounds
|
|
6175
6336
|
primary: this.renderTheme.primary,
|
|
6337
|
+
// #3B82F6
|
|
6176
6338
|
secondary: "#64748B",
|
|
6339
|
+
// Slate 500
|
|
6177
6340
|
success: "#10B981",
|
|
6341
|
+
// Emerald 500
|
|
6178
6342
|
warning: "#F59E0B",
|
|
6343
|
+
// Amber 500
|
|
6179
6344
|
danger: "#EF4444",
|
|
6345
|
+
// Red 500
|
|
6180
6346
|
error: "#EF4444",
|
|
6181
6347
|
info: "#0EA5E9"
|
|
6348
|
+
// Sky 500
|
|
6182
6349
|
};
|
|
6183
6350
|
return semantic[variant];
|
|
6184
6351
|
}
|
|
@@ -6639,6 +6806,19 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
6639
6806
|
renderLabel(node, pos) {
|
|
6640
6807
|
return this.renderTextBlock(node, pos, String(node.props.text || "Label"), 12, 1.2);
|
|
6641
6808
|
}
|
|
6809
|
+
/**
|
|
6810
|
+
* Render image as a plain skeleton rectangle — no icon, no placeholder label,
|
|
6811
|
+
* just a filled block with the correct dimensions (aspect-ratio is preserved
|
|
6812
|
+
* by the layout engine, so pos already has the right size).
|
|
6813
|
+
*/
|
|
6814
|
+
renderImage(node, pos) {
|
|
6815
|
+
return `<g${this.getDataNodeId(node)}>
|
|
6816
|
+
<rect x="${pos.x}" y="${pos.y}"
|
|
6817
|
+
width="${pos.width}" height="${pos.height}"
|
|
6818
|
+
rx="4"
|
|
6819
|
+
fill="${this.renderTheme.border}"/>
|
|
6820
|
+
</g>`;
|
|
6821
|
+
}
|
|
6642
6822
|
/**
|
|
6643
6823
|
* Render badge as shape only (no text)
|
|
6644
6824
|
*/
|
|
@@ -7313,6 +7493,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7313
7493
|
const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
|
|
7314
7494
|
const labelOffset = this.parseBooleanProp(node.props.labelSpace, false) ? 18 : 0;
|
|
7315
7495
|
const fullWidth = this.shouldButtonFillAvailableWidth(node);
|
|
7496
|
+
const iconName = String(node.props.icon || "").trim();
|
|
7497
|
+
const iconAlign = String(node.props.iconAlign || "left").toLowerCase();
|
|
7316
7498
|
const radius = this.tokens.button.radius;
|
|
7317
7499
|
const fontSize = this.tokens.button.fontSize;
|
|
7318
7500
|
const fontWeight = this.tokens.button.fontWeight;
|
|
@@ -7322,9 +7504,14 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7322
7504
|
Math.min(resolveControlHeight(size, density), pos.height - labelOffset)
|
|
7323
7505
|
);
|
|
7324
7506
|
const buttonY = pos.y + labelOffset;
|
|
7507
|
+
const iconSvg = iconName ? getIcon(iconName) : null;
|
|
7508
|
+
const iconSize = iconSvg ? Math.round(fontSize * 1.1) : 0;
|
|
7509
|
+
const iconGap = iconSvg ? 8 : 0;
|
|
7510
|
+
const edgePad = 12;
|
|
7511
|
+
const textPad = paddingX + extraPadding;
|
|
7325
7512
|
const idealTextWidth = text.length * fontSize * 0.6;
|
|
7326
|
-
const buttonWidth = fullWidth ? Math.max(1, pos.width) : this.clampControlWidth(Math.max(idealTextWidth + (
|
|
7327
|
-
const availableTextWidth = Math.max(0, buttonWidth - (
|
|
7513
|
+
const buttonWidth = fullWidth ? Math.max(1, pos.width) : this.clampControlWidth(Math.max(idealTextWidth + (iconSvg ? iconSize + iconGap : 0) + textPad * 2, 60), pos.width);
|
|
7514
|
+
const availableTextWidth = Math.max(0, buttonWidth - textPad * 2 - (iconSvg ? iconSize + iconGap : 0));
|
|
7328
7515
|
const visibleText = this.truncateTextToWidth(text, availableTextWidth, fontSize);
|
|
7329
7516
|
const semanticBase = this.getSemanticVariantColor(variant);
|
|
7330
7517
|
const hasExplicitVariantColor = semanticBase !== void 0 || this.colorResolver.hasColor(variant);
|
|
@@ -7332,21 +7519,47 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7332
7519
|
const borderColor = variantColor;
|
|
7333
7520
|
const textColor = variantColor;
|
|
7334
7521
|
const strokeWidth = 0.5;
|
|
7335
|
-
|
|
7522
|
+
const iconOffsetY = buttonY + (buttonHeight - iconSize) / 2;
|
|
7523
|
+
const iconX = iconAlign === "right" ? pos.x + buttonWidth - edgePad - iconSize : pos.x + edgePad;
|
|
7524
|
+
const textAlign = String(node.props.align || "center").toLowerCase();
|
|
7525
|
+
const sidePad = textPad + 4;
|
|
7526
|
+
let textX;
|
|
7527
|
+
let textAnchor;
|
|
7528
|
+
if (textAlign === "left") {
|
|
7529
|
+
textX = iconSvg && iconAlign === "left" ? pos.x + edgePad + iconSize + iconGap : pos.x + sidePad;
|
|
7530
|
+
textAnchor = "start";
|
|
7531
|
+
} else if (textAlign === "right") {
|
|
7532
|
+
textX = iconSvg && iconAlign === "right" ? pos.x + buttonWidth - edgePad - iconSize - iconGap : pos.x + buttonWidth - sidePad;
|
|
7533
|
+
textAnchor = "end";
|
|
7534
|
+
} else {
|
|
7535
|
+
textX = pos.x + buttonWidth / 2;
|
|
7536
|
+
textAnchor = "middle";
|
|
7537
|
+
}
|
|
7538
|
+
let svg = `<g${this.getDataNodeId(node)}>
|
|
7336
7539
|
<rect x="${pos.x}" y="${buttonY}"
|
|
7337
7540
|
width="${buttonWidth}" height="${buttonHeight}"
|
|
7338
7541
|
rx="${radius}"
|
|
7339
7542
|
fill="none"
|
|
7340
7543
|
stroke="${borderColor}"
|
|
7341
7544
|
stroke-width="${strokeWidth}"
|
|
7342
|
-
filter="url(#sketch-rough)"
|
|
7343
|
-
|
|
7545
|
+
filter="url(#sketch-rough)"/>`;
|
|
7546
|
+
if (iconSvg) {
|
|
7547
|
+
svg += `
|
|
7548
|
+
<g transform="translate(${iconX}, ${iconOffsetY})">
|
|
7549
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${textColor}" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round">
|
|
7550
|
+
${this.extractSvgContent(iconSvg)}
|
|
7551
|
+
</svg>
|
|
7552
|
+
</g>`;
|
|
7553
|
+
}
|
|
7554
|
+
svg += `
|
|
7555
|
+
<text x="${textX}" y="${buttonY + buttonHeight / 2 + fontSize * 0.35}"
|
|
7344
7556
|
font-family="${this.fontFamily}"
|
|
7345
7557
|
font-size="${fontSize}"
|
|
7346
7558
|
font-weight="${fontWeight}"
|
|
7347
7559
|
fill="${textColor}"
|
|
7348
|
-
text-anchor="
|
|
7560
|
+
text-anchor="${textAnchor}">${this.escapeXml(visibleText)}</text>
|
|
7349
7561
|
</g>`;
|
|
7562
|
+
return svg;
|
|
7350
7563
|
}
|
|
7351
7564
|
/**
|
|
7352
7565
|
* Render badge with colored border instead of fill
|
|
@@ -7471,29 +7684,67 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7471
7684
|
renderInput(node, pos) {
|
|
7472
7685
|
const label = String(node.props.label || "");
|
|
7473
7686
|
const placeholder = String(node.props.placeholder || "");
|
|
7687
|
+
const iconLeftName = String(node.props.iconLeft || "").trim();
|
|
7688
|
+
const iconRightName = String(node.props.iconRight || "").trim();
|
|
7474
7689
|
const radius = this.tokens.input.radius;
|
|
7475
7690
|
const fontSize = this.tokens.input.fontSize;
|
|
7476
7691
|
const paddingX = this.tokens.input.paddingX;
|
|
7477
7692
|
const labelOffset = this.getControlLabelOffset(label);
|
|
7478
7693
|
const controlY = pos.y + labelOffset;
|
|
7479
7694
|
const controlHeight = Math.max(16, pos.height - labelOffset);
|
|
7480
|
-
|
|
7481
|
-
|
|
7695
|
+
const iconSize = 16;
|
|
7696
|
+
const iconPad = 12;
|
|
7697
|
+
const iconInnerGap = 8;
|
|
7698
|
+
const iconLeftSvg = iconLeftName ? getIcon(iconLeftName) : null;
|
|
7699
|
+
const iconRightSvg = iconRightName ? getIcon(iconRightName) : null;
|
|
7700
|
+
const leftOffset = iconLeftSvg ? iconPad + iconSize + iconInnerGap : 0;
|
|
7701
|
+
const rightOffset = iconRightSvg ? iconPad + iconSize + iconInnerGap : 0;
|
|
7702
|
+
const textX = pos.x + (iconLeftSvg ? leftOffset : paddingX);
|
|
7703
|
+
const iconColor = "#888888";
|
|
7704
|
+
const iconCenterY = controlY + (controlHeight - iconSize) / 2;
|
|
7705
|
+
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
7706
|
+
if (label) {
|
|
7707
|
+
svg += `
|
|
7708
|
+
<text x="${pos.x}" y="${this.getControlLabelBaselineY(pos.y)}"
|
|
7482
7709
|
font-family="${this.fontFamily}"
|
|
7483
7710
|
font-size="12"
|
|
7484
|
-
fill="${this.renderTheme.text}">${this.escapeXml(label)}</text
|
|
7711
|
+
fill="${this.renderTheme.text}">${this.escapeXml(label)}</text>`;
|
|
7712
|
+
}
|
|
7713
|
+
svg += `
|
|
7485
7714
|
<rect x="${pos.x}" y="${controlY}"
|
|
7486
7715
|
width="${pos.width}" height="${controlHeight}"
|
|
7487
7716
|
rx="${radius}"
|
|
7488
7717
|
fill="${this.renderTheme.cardBg}"
|
|
7489
7718
|
stroke="#2D3748"
|
|
7490
7719
|
stroke-width="0.5"
|
|
7491
|
-
filter="url(#sketch-rough)"
|
|
7492
|
-
|
|
7720
|
+
filter="url(#sketch-rough)"/>`;
|
|
7721
|
+
if (iconLeftSvg) {
|
|
7722
|
+
svg += `
|
|
7723
|
+
<g transform="translate(${pos.x + iconPad}, ${iconCenterY})">
|
|
7724
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
7725
|
+
${this.extractSvgContent(iconLeftSvg)}
|
|
7726
|
+
</svg>
|
|
7727
|
+
</g>`;
|
|
7728
|
+
}
|
|
7729
|
+
if (iconRightSvg) {
|
|
7730
|
+
svg += `
|
|
7731
|
+
<g transform="translate(${pos.x + pos.width - iconPad - iconSize}, ${iconCenterY})">
|
|
7732
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
7733
|
+
${this.extractSvgContent(iconRightSvg)}
|
|
7734
|
+
</svg>
|
|
7735
|
+
</g>`;
|
|
7736
|
+
}
|
|
7737
|
+
if (placeholder) {
|
|
7738
|
+
const availWidth = pos.width - (iconLeftSvg ? leftOffset : paddingX) - (iconRightSvg ? rightOffset : paddingX);
|
|
7739
|
+
const visiblePh = this.truncateTextToWidth(placeholder, Math.max(0, availWidth), fontSize);
|
|
7740
|
+
svg += `
|
|
7741
|
+
<text x="${textX}" y="${controlY + controlHeight / 2 + 5}"
|
|
7493
7742
|
font-family="${this.fontFamily}"
|
|
7494
7743
|
font-size="${fontSize}"
|
|
7495
|
-
fill="${this.renderTheme.textMuted}">${this.escapeXml(
|
|
7496
|
-
|
|
7744
|
+
fill="${this.renderTheme.textMuted}">${this.escapeXml(visiblePh)}</text>`;
|
|
7745
|
+
}
|
|
7746
|
+
svg += "\n </g>";
|
|
7747
|
+
return svg;
|
|
7497
7748
|
}
|
|
7498
7749
|
/**
|
|
7499
7750
|
* Render textarea with thicker border
|
|
@@ -7747,31 +7998,67 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7747
7998
|
renderSelect(node, pos) {
|
|
7748
7999
|
const label = String(node.props.label || "");
|
|
7749
8000
|
const placeholder = String(node.props.placeholder || "Select...");
|
|
8001
|
+
const iconLeftName = String(node.props.iconLeft || "").trim();
|
|
8002
|
+
const iconRightName = String(node.props.iconRight || "").trim();
|
|
7750
8003
|
const labelOffset = this.getControlLabelOffset(label);
|
|
7751
8004
|
const controlY = pos.y + labelOffset;
|
|
7752
8005
|
const controlHeight = Math.max(16, pos.height - labelOffset);
|
|
7753
8006
|
const centerY = controlY + controlHeight / 2 + 5;
|
|
7754
|
-
|
|
7755
|
-
|
|
8007
|
+
const iconSize = 16;
|
|
8008
|
+
const iconPad = 12;
|
|
8009
|
+
const iconInnerGap = 8;
|
|
8010
|
+
const iconLeftSvg = iconLeftName ? getIcon(iconLeftName) : null;
|
|
8011
|
+
const iconRightSvg = iconRightName ? getIcon(iconRightName) : null;
|
|
8012
|
+
const leftOffset = iconLeftSvg ? iconPad + iconSize + iconInnerGap : 0;
|
|
8013
|
+
const chevronWidth = 20;
|
|
8014
|
+
const iconColor = "#888888";
|
|
8015
|
+
const iconCenterY = controlY + (controlHeight - iconSize) / 2;
|
|
8016
|
+
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
8017
|
+
if (label) {
|
|
8018
|
+
svg += `
|
|
8019
|
+
<text x="${pos.x}" y="${this.getControlLabelBaselineY(pos.y)}"
|
|
7756
8020
|
font-family="${this.fontFamily}"
|
|
7757
8021
|
font-size="12"
|
|
7758
|
-
fill="${this.renderTheme.text}">${this.escapeXml(label)}</text
|
|
8022
|
+
fill="${this.renderTheme.text}">${this.escapeXml(label)}</text>`;
|
|
8023
|
+
}
|
|
8024
|
+
svg += `
|
|
7759
8025
|
<rect x="${pos.x}" y="${controlY}"
|
|
7760
8026
|
width="${pos.width}" height="${controlHeight}"
|
|
7761
8027
|
rx="6"
|
|
7762
8028
|
fill="${this.renderTheme.cardBg}"
|
|
7763
8029
|
stroke="#2D3748"
|
|
7764
8030
|
stroke-width="0.5"
|
|
7765
|
-
filter="url(#sketch-rough)"
|
|
7766
|
-
|
|
8031
|
+
filter="url(#sketch-rough)"/>`;
|
|
8032
|
+
if (iconLeftSvg) {
|
|
8033
|
+
svg += `
|
|
8034
|
+
<g transform="translate(${pos.x + iconPad}, ${iconCenterY})">
|
|
8035
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
8036
|
+
${this.extractSvgContent(iconLeftSvg)}
|
|
8037
|
+
</svg>
|
|
8038
|
+
</g>`;
|
|
8039
|
+
}
|
|
8040
|
+
if (iconRightSvg) {
|
|
8041
|
+
svg += `
|
|
8042
|
+
<g transform="translate(${pos.x + pos.width - chevronWidth - iconPad - iconSize}, ${iconCenterY})">
|
|
8043
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
8044
|
+
${this.extractSvgContent(iconRightSvg)}
|
|
8045
|
+
</svg>
|
|
8046
|
+
</g>`;
|
|
8047
|
+
}
|
|
8048
|
+
const textX = pos.x + (iconLeftSvg ? leftOffset : 12);
|
|
8049
|
+
const availWidth = pos.width - (iconLeftSvg ? leftOffset : 12) - chevronWidth - (iconRightSvg ? iconPad + iconSize + iconInnerGap : 0);
|
|
8050
|
+
const visiblePh = this.truncateTextToWidth(placeholder, Math.max(0, availWidth), 14);
|
|
8051
|
+
svg += `
|
|
8052
|
+
<text x="${textX}" y="${centerY}"
|
|
7767
8053
|
font-family="${this.fontFamily}"
|
|
7768
8054
|
font-size="14"
|
|
7769
|
-
fill="${this.renderTheme.textMuted}">${this.escapeXml(
|
|
8055
|
+
fill="${this.renderTheme.textMuted}">${this.escapeXml(visiblePh)}</text>
|
|
7770
8056
|
<text x="${pos.x + pos.width - 20}" y="${centerY}"
|
|
7771
8057
|
font-family="${this.fontFamily}"
|
|
7772
8058
|
font-size="16"
|
|
7773
8059
|
fill="${this.renderTheme.textMuted}">\u25BC</text>
|
|
7774
8060
|
</g>`;
|
|
8061
|
+
return svg;
|
|
7775
8062
|
}
|
|
7776
8063
|
/**
|
|
7777
8064
|
* Render checkbox with sketch filter and Comic Sans
|
|
@@ -8170,44 +8457,43 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8170
8457
|
renderImage(node, pos) {
|
|
8171
8458
|
const placeholder = String(node.props.placeholder || "landscape").toLowerCase();
|
|
8172
8459
|
const iconType = String(node.props.icon || "").trim();
|
|
8460
|
+
const variant = String(node.props.variant || "").trim();
|
|
8173
8461
|
const iconSvg = placeholder === "icon" && iconType.length > 0 ? getIcon(iconType) : null;
|
|
8462
|
+
const imageBg = this.options.theme === "dark" ? "#2A2A2A" : "#E8E8E8";
|
|
8174
8463
|
if (iconSvg) {
|
|
8175
|
-
const
|
|
8176
|
-
const
|
|
8177
|
-
const
|
|
8178
|
-
const
|
|
8179
|
-
const
|
|
8180
|
-
const
|
|
8464
|
+
const semanticBase = variant ? this.getSemanticVariantColor(variant) : void 0;
|
|
8465
|
+
const hasVariant = variant.length > 0 && (semanticBase !== void 0 || this.colorResolver.hasColor(variant));
|
|
8466
|
+
const variantColor = hasVariant ? this.resolveVariantColor(variant, this.renderTheme.primary) : null;
|
|
8467
|
+
const bgColor = hasVariant ? this.hexToRgba(variantColor, 0.12) : imageBg;
|
|
8468
|
+
const iconColor = hasVariant ? variantColor : "#666666";
|
|
8469
|
+
const iconSize = Math.max(16, Math.min(pos.width, pos.height) * 0.6);
|
|
8470
|
+
const iconOffsetX = pos.x + (pos.width - iconSize) / 2;
|
|
8471
|
+
const iconOffsetY = pos.y + (pos.height - iconSize) / 2;
|
|
8181
8472
|
return `<g${this.getDataNodeId(node)}>
|
|
8182
|
-
<!-- Image Background -->
|
|
8183
8473
|
<rect x="${pos.x}" y="${pos.y}"
|
|
8184
8474
|
width="${pos.width}" height="${pos.height}"
|
|
8185
|
-
fill="
|
|
8186
|
-
stroke="#2D3748"
|
|
8187
|
-
stroke-width="0.5"
|
|
8475
|
+
fill="${bgColor}"
|
|
8188
8476
|
rx="4"
|
|
8189
8477
|
filter="url(#sketch-rough)"/>
|
|
8190
|
-
|
|
8191
|
-
<!-- Custom Icon Placeholder -->
|
|
8192
|
-
<rect x="${badgeX}" y="${badgeY}"
|
|
8193
|
-
width="${badgeSize}" height="${badgeSize}"
|
|
8194
|
-
rx="${Math.max(4, badgeSize * 0.2)}"
|
|
8195
|
-
fill="none"
|
|
8196
|
-
stroke="#2D3748"
|
|
8197
|
-
stroke-width="0.5"
|
|
8198
|
-
filter="url(#sketch-rough)"/>
|
|
8199
8478
|
<g transform="translate(${iconOffsetX}, ${iconOffsetY})">
|
|
8200
|
-
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="
|
|
8479
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
8201
8480
|
${this.extractSvgContent(iconSvg)}
|
|
8202
8481
|
</svg>
|
|
8203
8482
|
</g>
|
|
8483
|
+
<rect x="${pos.x}" y="${pos.y}"
|
|
8484
|
+
width="${pos.width}" height="${pos.height}"
|
|
8485
|
+
fill="none"
|
|
8486
|
+
stroke="#2D3748"
|
|
8487
|
+
stroke-width="0.5"
|
|
8488
|
+
rx="4"
|
|
8489
|
+
filter="url(#sketch-rough)"/>
|
|
8204
8490
|
</g>`;
|
|
8205
8491
|
}
|
|
8206
8492
|
return `<g${this.getDataNodeId(node)}>
|
|
8207
8493
|
<!-- Image Background -->
|
|
8208
8494
|
<rect x="${pos.x}" y="${pos.y}"
|
|
8209
8495
|
width="${pos.width}" height="${pos.height}"
|
|
8210
|
-
fill="
|
|
8496
|
+
fill="${imageBg}"
|
|
8211
8497
|
stroke="#2D3748"
|
|
8212
8498
|
stroke-width="0.5"
|
|
8213
8499
|
rx="4"
|
|
@@ -8262,17 +8548,23 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8262
8548
|
*/
|
|
8263
8549
|
renderSidebarMenu(node, pos) {
|
|
8264
8550
|
const itemsStr = String(node.props.items || "Item 1,Item 2,Item 3");
|
|
8551
|
+
const iconsStr = String(node.props.icons || "");
|
|
8265
8552
|
const items = itemsStr.split(",").map((s) => s.trim());
|
|
8553
|
+
const icons = iconsStr ? iconsStr.split(",").map((s) => s.trim()) : [];
|
|
8266
8554
|
const itemHeight = 40;
|
|
8267
8555
|
const fontSize = 14;
|
|
8268
8556
|
const activeIndex = Number(node.props.active || 0);
|
|
8269
8557
|
const accentColor = this.resolveAccentColor();
|
|
8558
|
+
const variantProp = String(node.props.variant || "").trim();
|
|
8559
|
+
const semanticVariant = variantProp ? this.getSemanticVariantColor(variantProp) : void 0;
|
|
8560
|
+
const hasVariant = variantProp.length > 0 && (semanticVariant !== void 0 || this.colorResolver.hasColor(variantProp));
|
|
8561
|
+
const activeColor = hasVariant ? this.resolveVariantColor(variantProp, accentColor) : accentColor;
|
|
8270
8562
|
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
8271
8563
|
items.forEach((item, index) => {
|
|
8272
8564
|
const itemY = pos.y + index * itemHeight;
|
|
8273
8565
|
const isActive = index === activeIndex;
|
|
8274
|
-
const bgColor = isActive ? this.hexToRgba(
|
|
8275
|
-
const textColor = isActive ?
|
|
8566
|
+
const bgColor = isActive ? this.hexToRgba(activeColor, 0.15) : "transparent";
|
|
8567
|
+
const textColor = isActive ? activeColor : this.resolveTextColor();
|
|
8276
8568
|
const fontWeight = isActive ? "500" : "400";
|
|
8277
8569
|
if (isActive) {
|
|
8278
8570
|
svg += `
|
|
@@ -8282,8 +8574,24 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8282
8574
|
fill="${bgColor}"
|
|
8283
8575
|
filter="url(#sketch-rough)"/>`;
|
|
8284
8576
|
}
|
|
8577
|
+
let currentX = pos.x + 12;
|
|
8578
|
+
if (icons[index]) {
|
|
8579
|
+
const iconSvg = getIcon(icons[index]);
|
|
8580
|
+
if (iconSvg) {
|
|
8581
|
+
const iconSize = 16;
|
|
8582
|
+
const iconY = itemY + (itemHeight - iconSize) / 2;
|
|
8583
|
+
const iconColor = isActive ? activeColor : this.resolveMutedColor();
|
|
8584
|
+
svg += `
|
|
8585
|
+
<g transform="translate(${currentX}, ${iconY})">
|
|
8586
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
8587
|
+
${this.extractSvgContent(iconSvg)}
|
|
8588
|
+
</svg>
|
|
8589
|
+
</g>`;
|
|
8590
|
+
currentX += iconSize + 8;
|
|
8591
|
+
}
|
|
8592
|
+
}
|
|
8285
8593
|
svg += `
|
|
8286
|
-
<text x="${
|
|
8594
|
+
<text x="${currentX}" y="${itemY + itemHeight / 2 + 5}"
|
|
8287
8595
|
font-family="${this.fontFamily}"
|
|
8288
8596
|
font-size="${fontSize}"
|
|
8289
8597
|
font-weight="${fontWeight}"
|