@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.cjs
CHANGED
|
@@ -1696,7 +1696,8 @@ function validateSemanticDiagnostics(ast, sourceMap) {
|
|
|
1696
1696
|
if (enumValues) {
|
|
1697
1697
|
const normalizedValue = String(propValue);
|
|
1698
1698
|
const isCustomVariantFromColors = propName === "variant" && !enumValues.includes(normalizedValue) && Object.prototype.hasOwnProperty.call(ast.colors || {}, normalizedValue);
|
|
1699
|
-
|
|
1699
|
+
const isPropReference = normalizedValue.startsWith("prop_");
|
|
1700
|
+
if (!enumValues.includes(normalizedValue) && !isCustomVariantFromColors && !isPropReference) {
|
|
1700
1701
|
emitWarning(
|
|
1701
1702
|
`Invalid value "${normalizedValue}" for property "${propName}" in component "${componentType}".`,
|
|
1702
1703
|
"COMPONENT_INVALID_PROPERTY_VALUE",
|
|
@@ -1806,7 +1807,7 @@ function validateSemanticDiagnostics(ast, sourceMap) {
|
|
|
1806
1807
|
const enumValues = rules.enumParams?.[paramName];
|
|
1807
1808
|
if (enumValues) {
|
|
1808
1809
|
const normalizedValue = String(paramValue);
|
|
1809
|
-
if (!enumValues.includes(normalizedValue)) {
|
|
1810
|
+
if (!enumValues.includes(normalizedValue) && !normalizedValue.startsWith("prop_")) {
|
|
1810
1811
|
emitWarning(
|
|
1811
1812
|
`Invalid value "${normalizedValue}" for parameter "${paramName}" in layout "${layout.layoutType}".`,
|
|
1812
1813
|
"LAYOUT_INVALID_PARAMETER_VALUE",
|
|
@@ -2689,6 +2690,20 @@ ${messages}`);
|
|
|
2689
2690
|
key
|
|
2690
2691
|
);
|
|
2691
2692
|
if (resolvedValue !== void 0) {
|
|
2693
|
+
const wasPropReference = typeof value === "string" && value.startsWith("prop_");
|
|
2694
|
+
if (wasPropReference) {
|
|
2695
|
+
const layoutMetadata = import_components2.LAYOUTS[layoutType];
|
|
2696
|
+
const property = layoutMetadata?.properties?.[key];
|
|
2697
|
+
if (property?.type === "enum" && Array.isArray(property.options)) {
|
|
2698
|
+
const normalizedValue = String(resolvedValue);
|
|
2699
|
+
if (!property.options.includes(normalizedValue)) {
|
|
2700
|
+
this.warnings.push({
|
|
2701
|
+
type: "invalid-bound-enum-value",
|
|
2702
|
+
message: `Invalid value "${normalizedValue}" for parameter "${key}" in layout "${layoutType}". Expected one of: ${property.options.join(", ")}.`
|
|
2703
|
+
});
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2692
2707
|
resolved[key] = resolvedValue;
|
|
2693
2708
|
}
|
|
2694
2709
|
}
|
|
@@ -2753,6 +2768,20 @@ ${messages}`);
|
|
|
2753
2768
|
key
|
|
2754
2769
|
);
|
|
2755
2770
|
if (resolvedValue !== void 0) {
|
|
2771
|
+
const wasPropReference = typeof value === "string" && value.startsWith("prop_");
|
|
2772
|
+
if (wasPropReference) {
|
|
2773
|
+
const metadata = import_components2.COMPONENTS[componentType];
|
|
2774
|
+
const property = metadata?.properties?.[key];
|
|
2775
|
+
if (property?.type === "enum" && Array.isArray(property.options)) {
|
|
2776
|
+
const normalizedValue = String(resolvedValue);
|
|
2777
|
+
if (!property.options.includes(normalizedValue)) {
|
|
2778
|
+
this.warnings.push({
|
|
2779
|
+
type: "invalid-bound-enum-value",
|
|
2780
|
+
message: `Invalid value "${normalizedValue}" for property "${key}" in component "${componentType}". Expected one of: ${property.options.join(", ")}.`
|
|
2781
|
+
});
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2756
2785
|
resolved[key] = resolvedValue;
|
|
2757
2786
|
}
|
|
2758
2787
|
}
|
|
@@ -4563,14 +4592,14 @@ var THEMES = {
|
|
|
4563
4592
|
primaryLight: "#EFF6FF"
|
|
4564
4593
|
},
|
|
4565
4594
|
dark: {
|
|
4566
|
-
bg: "#
|
|
4567
|
-
cardBg: "#
|
|
4568
|
-
border: "#
|
|
4569
|
-
text: "#
|
|
4570
|
-
textMuted: "#
|
|
4595
|
+
bg: "#111111",
|
|
4596
|
+
cardBg: "#1C1C1C",
|
|
4597
|
+
border: "#303030",
|
|
4598
|
+
text: "#F0F0F0",
|
|
4599
|
+
textMuted: "#808080",
|
|
4571
4600
|
primary: "#60A5FA",
|
|
4572
4601
|
primaryHover: "#3B82F6",
|
|
4573
|
-
primaryLight: "#
|
|
4602
|
+
primaryLight: "#1C2A3A"
|
|
4574
4603
|
}
|
|
4575
4604
|
};
|
|
4576
4605
|
var SVGRenderer = class {
|
|
@@ -4811,6 +4840,8 @@ var SVGRenderer = class {
|
|
|
4811
4840
|
const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
|
|
4812
4841
|
const labelOffset = this.parseBooleanProp(node.props.labelSpace, false) ? 18 : 0;
|
|
4813
4842
|
const fullWidth = this.shouldButtonFillAvailableWidth(node);
|
|
4843
|
+
const iconName = String(node.props.icon || "").trim();
|
|
4844
|
+
const iconAlign = String(node.props.iconAlign || "left").toLowerCase();
|
|
4814
4845
|
const radius = this.tokens.button.radius;
|
|
4815
4846
|
const fontSize = this.tokens.button.fontSize;
|
|
4816
4847
|
const fontWeight = this.tokens.button.fontWeight;
|
|
@@ -4818,30 +4849,62 @@ var SVGRenderer = class {
|
|
|
4818
4849
|
const controlHeight = resolveActionControlHeight(size, density);
|
|
4819
4850
|
const buttonY = pos.y + labelOffset;
|
|
4820
4851
|
const buttonHeight = Math.max(16, Math.min(controlHeight, pos.height - labelOffset));
|
|
4852
|
+
const iconSvg = iconName ? getIcon(iconName) : null;
|
|
4853
|
+
const iconSize = iconSvg ? Math.round(fontSize * 1.1) : 0;
|
|
4854
|
+
const iconGap = iconSvg ? 8 : 0;
|
|
4855
|
+
const edgePad = 12;
|
|
4856
|
+
const textPad = paddingX + extraPadding;
|
|
4821
4857
|
const idealTextWidth = this.estimateTextWidth(text, fontSize);
|
|
4822
|
-
const buttonWidth = fullWidth ? Math.max(1, pos.width) : this.clampControlWidth(Math.max(Math.ceil(idealTextWidth + (
|
|
4823
|
-
const availableTextWidth = Math.max(0, buttonWidth - (
|
|
4858
|
+
const buttonWidth = fullWidth ? Math.max(1, pos.width) : this.clampControlWidth(Math.max(Math.ceil(idealTextWidth + (iconSvg ? iconSize + iconGap : 0) + textPad * 2), 60), pos.width);
|
|
4859
|
+
const availableTextWidth = Math.max(0, buttonWidth - textPad * 2 - (iconSvg ? iconSize + iconGap : 0));
|
|
4824
4860
|
const visibleText = this.truncateTextToWidth(text, availableTextWidth, fontSize);
|
|
4825
4861
|
const semanticBase = this.getSemanticVariantColor(variant);
|
|
4826
4862
|
const hasExplicitVariantColor = semanticBase !== void 0 || this.colorResolver.hasColor(variant);
|
|
4827
4863
|
const resolvedBase = this.resolveVariantColor(variant, this.renderTheme.primary);
|
|
4828
|
-
const
|
|
4864
|
+
const isDarkMode = this.options.theme === "dark";
|
|
4865
|
+
const bgColor = hasExplicitVariantColor ? this.hexToRgba(resolvedBase, 0.85) : isDarkMode ? "rgba(48, 48, 55, 0.9)" : "rgba(226, 232, 240, 0.9)";
|
|
4829
4866
|
const textColor = hasExplicitVariantColor ? "#FFFFFF" : this.hexToRgba(this.resolveTextColor(), 0.85);
|
|
4830
|
-
const borderColor = hasExplicitVariantColor ? this.hexToRgba(resolvedBase, 0.7) : "rgba(100, 116, 139, 0.4)";
|
|
4831
|
-
|
|
4867
|
+
const borderColor = hasExplicitVariantColor ? this.hexToRgba(resolvedBase, 0.7) : isDarkMode ? "rgba(75, 75, 88, 0.8)" : "rgba(100, 116, 139, 0.4)";
|
|
4868
|
+
const iconOffsetY = buttonY + (buttonHeight - iconSize) / 2;
|
|
4869
|
+
const iconX = iconAlign === "right" ? pos.x + buttonWidth - edgePad - iconSize : pos.x + edgePad;
|
|
4870
|
+
const textAlign = String(node.props.align || "center").toLowerCase();
|
|
4871
|
+
const sidePad = textPad + 4;
|
|
4872
|
+
let textX;
|
|
4873
|
+
let textAnchor;
|
|
4874
|
+
if (textAlign === "left") {
|
|
4875
|
+
textX = iconSvg && iconAlign === "left" ? pos.x + edgePad + iconSize + iconGap : pos.x + sidePad;
|
|
4876
|
+
textAnchor = "start";
|
|
4877
|
+
} else if (textAlign === "right") {
|
|
4878
|
+
textX = iconSvg && iconAlign === "right" ? pos.x + buttonWidth - edgePad - iconSize - iconGap : pos.x + buttonWidth - sidePad;
|
|
4879
|
+
textAnchor = "end";
|
|
4880
|
+
} else {
|
|
4881
|
+
textX = pos.x + buttonWidth / 2;
|
|
4882
|
+
textAnchor = "middle";
|
|
4883
|
+
}
|
|
4884
|
+
let svg = `<g${this.getDataNodeId(node)}>
|
|
4832
4885
|
<rect x="${pos.x}" y="${buttonY}"
|
|
4833
4886
|
width="${buttonWidth}" height="${buttonHeight}"
|
|
4834
4887
|
rx="${radius}"
|
|
4835
4888
|
fill="${bgColor}"
|
|
4836
4889
|
stroke="${borderColor}"
|
|
4837
|
-
stroke-width="1"
|
|
4838
|
-
|
|
4890
|
+
stroke-width="1"/>`;
|
|
4891
|
+
if (iconSvg) {
|
|
4892
|
+
svg += `
|
|
4893
|
+
<g transform="translate(${iconX}, ${iconOffsetY})">
|
|
4894
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${textColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
4895
|
+
${this.extractSvgContent(iconSvg)}
|
|
4896
|
+
</svg>
|
|
4897
|
+
</g>`;
|
|
4898
|
+
}
|
|
4899
|
+
svg += `
|
|
4900
|
+
<text x="${textX}" y="${buttonY + buttonHeight / 2 + fontSize * 0.35}"
|
|
4839
4901
|
font-family="Arial, Helvetica, sans-serif"
|
|
4840
4902
|
font-size="${fontSize}"
|
|
4841
4903
|
font-weight="${fontWeight}"
|
|
4842
4904
|
fill="${textColor}"
|
|
4843
|
-
text-anchor="
|
|
4905
|
+
text-anchor="${textAnchor}">${this.escapeXml(visibleText)}</text>
|
|
4844
4906
|
</g>`;
|
|
4907
|
+
return svg;
|
|
4845
4908
|
}
|
|
4846
4909
|
renderLink(node, pos) {
|
|
4847
4910
|
const text = String(node.props.text || "Link");
|
|
@@ -4882,28 +4945,66 @@ var SVGRenderer = class {
|
|
|
4882
4945
|
renderInput(node, pos) {
|
|
4883
4946
|
const label = String(node.props.label || "");
|
|
4884
4947
|
const placeholder = String(node.props.placeholder || "");
|
|
4948
|
+
const iconLeftName = String(node.props.iconLeft || "").trim();
|
|
4949
|
+
const iconRightName = String(node.props.iconRight || "").trim();
|
|
4885
4950
|
const radius = this.tokens.input.radius;
|
|
4886
4951
|
const fontSize = this.tokens.input.fontSize;
|
|
4887
4952
|
const paddingX = this.tokens.input.paddingX;
|
|
4888
4953
|
const labelOffset = this.getControlLabelOffset(label);
|
|
4889
4954
|
const controlY = pos.y + labelOffset;
|
|
4890
4955
|
const controlHeight = Math.max(16, pos.height - labelOffset);
|
|
4891
|
-
|
|
4892
|
-
|
|
4956
|
+
const iconSize = 16;
|
|
4957
|
+
const iconPad = 12;
|
|
4958
|
+
const iconInnerGap = 8;
|
|
4959
|
+
const iconLeftSvg = iconLeftName ? getIcon(iconLeftName) : null;
|
|
4960
|
+
const iconRightSvg = iconRightName ? getIcon(iconRightName) : null;
|
|
4961
|
+
const leftOffset = iconLeftSvg ? iconPad + iconSize + iconInnerGap : 0;
|
|
4962
|
+
const rightOffset = iconRightSvg ? iconPad + iconSize + iconInnerGap : 0;
|
|
4963
|
+
const textX = pos.x + (iconLeftSvg ? leftOffset : paddingX);
|
|
4964
|
+
const iconColor = this.hexToRgba(this.resolveMutedColor(), 0.8);
|
|
4965
|
+
const iconCenterY = controlY + (controlHeight - iconSize) / 2;
|
|
4966
|
+
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
4967
|
+
if (label) {
|
|
4968
|
+
svg += `
|
|
4969
|
+
<text x="${pos.x + paddingX}" y="${this.getControlLabelBaselineY(pos.y)}"
|
|
4893
4970
|
font-family="Arial, Helvetica, sans-serif"
|
|
4894
4971
|
font-size="12"
|
|
4895
|
-
fill="${this.renderTheme.text}">${this.escapeXml(label)}</text
|
|
4972
|
+
fill="${this.renderTheme.text}">${this.escapeXml(label)}</text>`;
|
|
4973
|
+
}
|
|
4974
|
+
svg += `
|
|
4896
4975
|
<rect x="${pos.x}" y="${controlY}"
|
|
4897
4976
|
width="${pos.width}" height="${controlHeight}"
|
|
4898
4977
|
rx="${radius}"
|
|
4899
4978
|
fill="${this.renderTheme.cardBg}"
|
|
4900
4979
|
stroke="${this.renderTheme.border}"
|
|
4901
|
-
stroke-width="1"
|
|
4902
|
-
|
|
4980
|
+
stroke-width="1"/>`;
|
|
4981
|
+
if (iconLeftSvg) {
|
|
4982
|
+
svg += `
|
|
4983
|
+
<g transform="translate(${pos.x + iconPad}, ${iconCenterY})">
|
|
4984
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
4985
|
+
${this.extractSvgContent(iconLeftSvg)}
|
|
4986
|
+
</svg>
|
|
4987
|
+
</g>`;
|
|
4988
|
+
}
|
|
4989
|
+
if (iconRightSvg) {
|
|
4990
|
+
svg += `
|
|
4991
|
+
<g transform="translate(${pos.x + pos.width - iconPad - iconSize}, ${iconCenterY})">
|
|
4992
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
4993
|
+
${this.extractSvgContent(iconRightSvg)}
|
|
4994
|
+
</svg>
|
|
4995
|
+
</g>`;
|
|
4996
|
+
}
|
|
4997
|
+
if (placeholder) {
|
|
4998
|
+
const availPlaceholderWidth = pos.width - (iconLeftSvg ? leftOffset : paddingX) - (iconRightSvg ? rightOffset : paddingX);
|
|
4999
|
+
const visiblePlaceholder = this.truncateTextToWidth(placeholder, Math.max(0, availPlaceholderWidth), fontSize);
|
|
5000
|
+
svg += `
|
|
5001
|
+
<text x="${textX}" y="${controlY + controlHeight / 2 + 5}"
|
|
4903
5002
|
font-family="Arial, Helvetica, sans-serif"
|
|
4904
5003
|
font-size="${fontSize}"
|
|
4905
|
-
fill="${this.renderTheme.textMuted}">${this.escapeXml(
|
|
4906
|
-
|
|
5004
|
+
fill="${this.renderTheme.textMuted}">${this.escapeXml(visiblePlaceholder)}</text>`;
|
|
5005
|
+
}
|
|
5006
|
+
svg += "\n </g>";
|
|
5007
|
+
return svg;
|
|
4907
5008
|
}
|
|
4908
5009
|
renderTopbar(node, pos) {
|
|
4909
5010
|
const title = String(node.props.title || "App");
|
|
@@ -5483,30 +5584,66 @@ var SVGRenderer = class {
|
|
|
5483
5584
|
renderSelect(node, pos) {
|
|
5484
5585
|
const label = String(node.props.label || "");
|
|
5485
5586
|
const placeholder = String(node.props.placeholder || "Select...");
|
|
5587
|
+
const iconLeftName = String(node.props.iconLeft || "").trim();
|
|
5588
|
+
const iconRightName = String(node.props.iconRight || "").trim();
|
|
5486
5589
|
const labelOffset = this.getControlLabelOffset(label);
|
|
5487
5590
|
const controlY = pos.y + labelOffset;
|
|
5488
5591
|
const controlHeight = Math.max(16, pos.height - labelOffset);
|
|
5489
5592
|
const centerY = controlY + controlHeight / 2 + 5;
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
font-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5593
|
+
const iconSize = 16;
|
|
5594
|
+
const iconPad = 12;
|
|
5595
|
+
const iconInnerGap = 8;
|
|
5596
|
+
const iconLeftSvg = iconLeftName ? getIcon(iconLeftName) : null;
|
|
5597
|
+
const iconRightSvg = iconRightName ? getIcon(iconRightName) : null;
|
|
5598
|
+
const leftOffset = iconLeftSvg ? iconPad + iconSize + iconInnerGap : 0;
|
|
5599
|
+
const chevronWidth = 20;
|
|
5600
|
+
const iconColor = this.hexToRgba(this.resolveMutedColor(), 0.8);
|
|
5601
|
+
const iconCenterY = controlY + (controlHeight - iconSize) / 2;
|
|
5602
|
+
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
5603
|
+
if (label) {
|
|
5604
|
+
svg += `
|
|
5605
|
+
<text x="${pos.x}" y="${this.getControlLabelBaselineY(pos.y)}"
|
|
5606
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
5607
|
+
font-size="12"
|
|
5608
|
+
fill="${this.renderTheme.text}">${this.escapeXml(label)}</text>`;
|
|
5609
|
+
}
|
|
5610
|
+
svg += `
|
|
5611
|
+
<rect x="${pos.x}" y="${controlY}"
|
|
5612
|
+
width="${pos.width}" height="${controlHeight}"
|
|
5613
|
+
rx="6"
|
|
5614
|
+
fill="${this.renderTheme.cardBg}"
|
|
5615
|
+
stroke="${this.renderTheme.border}"
|
|
5616
|
+
stroke-width="1"/>`;
|
|
5617
|
+
if (iconLeftSvg) {
|
|
5618
|
+
svg += `
|
|
5619
|
+
<g transform="translate(${pos.x + iconPad}, ${iconCenterY})">
|
|
5620
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
5621
|
+
${this.extractSvgContent(iconLeftSvg)}
|
|
5622
|
+
</svg>
|
|
5623
|
+
</g>`;
|
|
5624
|
+
}
|
|
5625
|
+
if (iconRightSvg) {
|
|
5626
|
+
svg += `
|
|
5627
|
+
<g transform="translate(${pos.x + pos.width - chevronWidth - iconPad - iconSize}, ${iconCenterY})">
|
|
5628
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
5629
|
+
${this.extractSvgContent(iconRightSvg)}
|
|
5630
|
+
</svg>
|
|
5631
|
+
</g>`;
|
|
5632
|
+
}
|
|
5633
|
+
const textX = pos.x + (iconLeftSvg ? leftOffset : 12);
|
|
5634
|
+
const availPlaceholderWidth = pos.width - (iconLeftSvg ? leftOffset : 12) - chevronWidth - (iconRightSvg ? iconPad + iconSize + iconInnerGap : 0);
|
|
5635
|
+
const visiblePlaceholder = this.truncateTextToWidth(placeholder, Math.max(0, availPlaceholderWidth), 14);
|
|
5636
|
+
svg += `
|
|
5637
|
+
<text x="${textX}" y="${centerY}"
|
|
5638
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
5639
|
+
font-size="14"
|
|
5640
|
+
fill="${this.renderTheme.textMuted}">${this.escapeXml(visiblePlaceholder)}</text>
|
|
5641
|
+
<text x="${pos.x + pos.width - 20}" y="${centerY}"
|
|
5642
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
5643
|
+
font-size="16"
|
|
5508
5644
|
fill="${this.renderTheme.textMuted}">\u25BC</text>
|
|
5509
5645
|
</g>`;
|
|
5646
|
+
return svg;
|
|
5510
5647
|
}
|
|
5511
5648
|
renderCheckbox(node, pos) {
|
|
5512
5649
|
const label = String(node.props.label || "Checkbox");
|
|
@@ -5939,7 +6076,9 @@ var SVGRenderer = class {
|
|
|
5939
6076
|
renderImage(node, pos) {
|
|
5940
6077
|
const placeholder = String(node.props.placeholder || "landscape").toLowerCase();
|
|
5941
6078
|
const placeholderIcon = String(node.props.icon || "").trim();
|
|
6079
|
+
const variant = String(node.props.variant || "").trim();
|
|
5942
6080
|
const placeholderIconSvg = placeholder === "icon" && placeholderIcon ? getIcon(placeholderIcon) : null;
|
|
6081
|
+
const imageBg = this.options.theme === "dark" ? "#2A2A2A" : "#E8E8E8";
|
|
5943
6082
|
const aspectRatios = {
|
|
5944
6083
|
landscape: 16 / 9,
|
|
5945
6084
|
portrait: 2 / 3,
|
|
@@ -5957,30 +6096,29 @@ var SVGRenderer = class {
|
|
|
5957
6096
|
}
|
|
5958
6097
|
const offsetX = pos.x + (pos.width - iconWidth) / 2;
|
|
5959
6098
|
const offsetY = pos.y + (pos.height - iconHeight) / 2;
|
|
5960
|
-
let svg = `<g${this.getDataNodeId(node)}>
|
|
5961
|
-
<!-- Image Background -->
|
|
5962
|
-
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="#E8E8E8"/>`;
|
|
5963
6099
|
if (placeholder === "icon" && placeholderIconSvg) {
|
|
5964
|
-
const
|
|
5965
|
-
const
|
|
5966
|
-
const
|
|
5967
|
-
const
|
|
5968
|
-
const
|
|
5969
|
-
const
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
rx="${Math.max(4, badgeSize * 0.2)}"
|
|
5975
|
-
fill="rgba(255, 255, 255, 0.6)"
|
|
5976
|
-
stroke="#888"
|
|
5977
|
-
stroke-width="1"/>
|
|
6100
|
+
const semanticBase = variant ? this.getSemanticVariantColor(variant) : void 0;
|
|
6101
|
+
const hasVariant = variant.length > 0 && (semanticBase !== void 0 || this.colorResolver.hasColor(variant));
|
|
6102
|
+
const variantColor = hasVariant ? this.resolveVariantColor(variant, this.renderTheme.primary) : null;
|
|
6103
|
+
const bgColor = hasVariant ? this.hexToRgba(variantColor, 0.12) : imageBg;
|
|
6104
|
+
const iconColor = hasVariant ? variantColor : this.options.theme === "dark" ? "#888888" : "#666666";
|
|
6105
|
+
const iconSize = Math.max(16, Math.min(pos.width, pos.height) * 0.6);
|
|
6106
|
+
const iconOffsetX = pos.x + (pos.width - iconSize) / 2;
|
|
6107
|
+
const iconOffsetY = pos.y + (pos.height - iconSize) / 2;
|
|
6108
|
+
return `<g${this.getDataNodeId(node)}>
|
|
6109
|
+
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="${bgColor}" rx="4"/>
|
|
5978
6110
|
<g transform="translate(${iconOffsetX}, ${iconOffsetY})">
|
|
5979
|
-
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="
|
|
6111
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
5980
6112
|
${this.extractSvgContent(placeholderIconSvg)}
|
|
5981
6113
|
</svg>
|
|
5982
|
-
</g
|
|
5983
|
-
}
|
|
6114
|
+
</g>
|
|
6115
|
+
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="none" stroke="${this.renderTheme.border}" stroke-width="1" rx="4"/>
|
|
6116
|
+
</g>`;
|
|
6117
|
+
}
|
|
6118
|
+
let svg = `<g${this.getDataNodeId(node)}>
|
|
6119
|
+
<!-- Image Background -->
|
|
6120
|
+
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="${imageBg}"/>`;
|
|
6121
|
+
if (["landscape", "portrait", "square"].includes(placeholder)) {
|
|
5984
6122
|
const cameraCx = offsetX + iconWidth / 2;
|
|
5985
6123
|
const cameraCy = offsetY + iconHeight / 2;
|
|
5986
6124
|
const scale = Math.min(iconWidth, iconHeight) / 24;
|
|
@@ -6084,18 +6222,22 @@ var SVGRenderer = class {
|
|
|
6084
6222
|
const fontSize = 14;
|
|
6085
6223
|
const activeIndex = Number(node.props.active || 0);
|
|
6086
6224
|
const accentColor = this.resolveAccentColor();
|
|
6225
|
+
const variantProp = String(node.props.variant || "").trim();
|
|
6226
|
+
const semanticVariant = variantProp ? this.getSemanticVariantColor(variantProp) : void 0;
|
|
6227
|
+
const hasVariant = variantProp.length > 0 && (semanticVariant !== void 0 || this.colorResolver.hasColor(variantProp));
|
|
6228
|
+
const activeColor = hasVariant ? this.resolveVariantColor(variantProp, accentColor) : accentColor;
|
|
6087
6229
|
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
6088
6230
|
items.forEach((item, index) => {
|
|
6089
6231
|
const itemY = pos.y + index * itemHeight;
|
|
6090
6232
|
const isActive = index === activeIndex;
|
|
6091
|
-
const bgColor = isActive ? this.hexToRgba(
|
|
6092
|
-
const textColor = isActive ? this.hexToRgba(
|
|
6233
|
+
const bgColor = isActive ? this.hexToRgba(activeColor, 0.15) : "transparent";
|
|
6234
|
+
const textColor = isActive ? this.hexToRgba(activeColor, 0.9) : this.hexToRgba(this.resolveTextColor(), 0.75);
|
|
6093
6235
|
const fontWeight = isActive ? "500" : "400";
|
|
6094
6236
|
if (isActive) {
|
|
6095
6237
|
svg += `
|
|
6096
|
-
<rect x="${pos.x}" y="${itemY}"
|
|
6097
|
-
width="${pos.width}" height="${itemHeight}"
|
|
6098
|
-
rx="6"
|
|
6238
|
+
<rect x="${pos.x}" y="${itemY}"
|
|
6239
|
+
width="${pos.width}" height="${itemHeight}"
|
|
6240
|
+
rx="6"
|
|
6099
6241
|
fill="${bgColor}"/>`;
|
|
6100
6242
|
}
|
|
6101
6243
|
let currentX = pos.x + 12;
|
|
@@ -6104,9 +6246,10 @@ var SVGRenderer = class {
|
|
|
6104
6246
|
if (iconSvg) {
|
|
6105
6247
|
const iconSize = 16;
|
|
6106
6248
|
const iconY = itemY + (itemHeight - iconSize) / 2;
|
|
6249
|
+
const iconColor = isActive ? this.hexToRgba(activeColor, 0.9) : this.hexToRgba(this.resolveMutedColor(), 0.9);
|
|
6107
6250
|
svg += `
|
|
6108
6251
|
<g transform="translate(${currentX}, ${iconY})">
|
|
6109
|
-
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${
|
|
6252
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
6110
6253
|
${this.extractSvgContent(iconSvg)}
|
|
6111
6254
|
</svg>
|
|
6112
6255
|
</g>`;
|
|
@@ -6114,10 +6257,10 @@ var SVGRenderer = class {
|
|
|
6114
6257
|
}
|
|
6115
6258
|
}
|
|
6116
6259
|
svg += `
|
|
6117
|
-
<text x="${currentX}" y="${itemY + itemHeight / 2 + 5}"
|
|
6118
|
-
font-family="Arial, Helvetica, sans-serif"
|
|
6119
|
-
font-size="${fontSize}"
|
|
6120
|
-
font-weight="${fontWeight}"
|
|
6260
|
+
<text x="${currentX}" y="${itemY + itemHeight / 2 + 5}"
|
|
6261
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
6262
|
+
font-size="${fontSize}"
|
|
6263
|
+
font-weight="${fontWeight}"
|
|
6121
6264
|
fill="${textColor}">${this.escapeXml(item)}</text>`;
|
|
6122
6265
|
});
|
|
6123
6266
|
svg += "\n </g>";
|
|
@@ -6157,9 +6300,10 @@ var SVGRenderer = class {
|
|
|
6157
6300
|
const semanticBase = this.getSemanticVariantColor(variant);
|
|
6158
6301
|
const hasExplicitVariantColor = semanticBase !== void 0 || this.colorResolver.hasColor(variant);
|
|
6159
6302
|
const resolvedBase = this.resolveVariantColor(variant, this.renderTheme.primary);
|
|
6160
|
-
const
|
|
6303
|
+
const isDarkMode = this.options.theme === "dark";
|
|
6304
|
+
const bgColor = hasExplicitVariantColor ? this.hexToRgba(resolvedBase, 0.85) : isDarkMode ? "rgba(48, 48, 55, 0.9)" : "rgba(226, 232, 240, 0.9)";
|
|
6161
6305
|
const iconColor = hasExplicitVariantColor ? "#FFFFFF" : this.hexToRgba(this.resolveTextColor(), 0.75);
|
|
6162
|
-
const borderColor = hasExplicitVariantColor ? this.hexToRgba(resolvedBase, 0.7) : "rgba(100, 116, 139, 0.4)";
|
|
6306
|
+
const borderColor = hasExplicitVariantColor ? this.hexToRgba(resolvedBase, 0.7) : isDarkMode ? "rgba(75, 75, 88, 0.8)" : "rgba(100, 116, 139, 0.4)";
|
|
6163
6307
|
const opacity = disabled ? "0.5" : "1";
|
|
6164
6308
|
const iconSvg = getIcon(iconName);
|
|
6165
6309
|
const buttonSize = Math.max(
|
|
@@ -6217,14 +6361,37 @@ var SVGRenderer = class {
|
|
|
6217
6361
|
return this.colorResolver.resolveColor("muted", fallback);
|
|
6218
6362
|
}
|
|
6219
6363
|
getSemanticVariantColor(variant) {
|
|
6220
|
-
const
|
|
6364
|
+
const isDark = this.options.theme === "dark";
|
|
6365
|
+
const semantic = isDark ? {
|
|
6366
|
+
// Muted mid-range — readable on #111111 without being neon
|
|
6367
|
+
primary: this.renderTheme.primary,
|
|
6368
|
+
// already theme-aware (#60A5FA)
|
|
6369
|
+
secondary: "#7E8EA2",
|
|
6370
|
+
// desaturated slate
|
|
6371
|
+
success: "#22A06B",
|
|
6372
|
+
// muted emerald
|
|
6373
|
+
warning: "#B38010",
|
|
6374
|
+
// deep amber
|
|
6375
|
+
danger: "#CC4444",
|
|
6376
|
+
// muted red
|
|
6377
|
+
error: "#CC4444",
|
|
6378
|
+
info: "#2485AF"
|
|
6379
|
+
// muted sky
|
|
6380
|
+
} : {
|
|
6381
|
+
// Tailwind 500-level — works on white/light backgrounds
|
|
6221
6382
|
primary: this.renderTheme.primary,
|
|
6383
|
+
// #3B82F6
|
|
6222
6384
|
secondary: "#64748B",
|
|
6385
|
+
// Slate 500
|
|
6223
6386
|
success: "#10B981",
|
|
6387
|
+
// Emerald 500
|
|
6224
6388
|
warning: "#F59E0B",
|
|
6389
|
+
// Amber 500
|
|
6225
6390
|
danger: "#EF4444",
|
|
6391
|
+
// Red 500
|
|
6226
6392
|
error: "#EF4444",
|
|
6227
6393
|
info: "#0EA5E9"
|
|
6394
|
+
// Sky 500
|
|
6228
6395
|
};
|
|
6229
6396
|
return semantic[variant];
|
|
6230
6397
|
}
|
|
@@ -6685,6 +6852,19 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
|
|
|
6685
6852
|
renderLabel(node, pos) {
|
|
6686
6853
|
return this.renderTextBlock(node, pos, String(node.props.text || "Label"), 12, 1.2);
|
|
6687
6854
|
}
|
|
6855
|
+
/**
|
|
6856
|
+
* Render image as a plain skeleton rectangle — no icon, no placeholder label,
|
|
6857
|
+
* just a filled block with the correct dimensions (aspect-ratio is preserved
|
|
6858
|
+
* by the layout engine, so pos already has the right size).
|
|
6859
|
+
*/
|
|
6860
|
+
renderImage(node, pos) {
|
|
6861
|
+
return `<g${this.getDataNodeId(node)}>
|
|
6862
|
+
<rect x="${pos.x}" y="${pos.y}"
|
|
6863
|
+
width="${pos.width}" height="${pos.height}"
|
|
6864
|
+
rx="4"
|
|
6865
|
+
fill="${this.renderTheme.border}"/>
|
|
6866
|
+
</g>`;
|
|
6867
|
+
}
|
|
6688
6868
|
/**
|
|
6689
6869
|
* Render badge as shape only (no text)
|
|
6690
6870
|
*/
|
|
@@ -7359,6 +7539,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7359
7539
|
const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
|
|
7360
7540
|
const labelOffset = this.parseBooleanProp(node.props.labelSpace, false) ? 18 : 0;
|
|
7361
7541
|
const fullWidth = this.shouldButtonFillAvailableWidth(node);
|
|
7542
|
+
const iconName = String(node.props.icon || "").trim();
|
|
7543
|
+
const iconAlign = String(node.props.iconAlign || "left").toLowerCase();
|
|
7362
7544
|
const radius = this.tokens.button.radius;
|
|
7363
7545
|
const fontSize = this.tokens.button.fontSize;
|
|
7364
7546
|
const fontWeight = this.tokens.button.fontWeight;
|
|
@@ -7368,9 +7550,14 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7368
7550
|
Math.min(resolveControlHeight(size, density), pos.height - labelOffset)
|
|
7369
7551
|
);
|
|
7370
7552
|
const buttonY = pos.y + labelOffset;
|
|
7553
|
+
const iconSvg = iconName ? getIcon(iconName) : null;
|
|
7554
|
+
const iconSize = iconSvg ? Math.round(fontSize * 1.1) : 0;
|
|
7555
|
+
const iconGap = iconSvg ? 8 : 0;
|
|
7556
|
+
const edgePad = 12;
|
|
7557
|
+
const textPad = paddingX + extraPadding;
|
|
7371
7558
|
const idealTextWidth = text.length * fontSize * 0.6;
|
|
7372
|
-
const buttonWidth = fullWidth ? Math.max(1, pos.width) : this.clampControlWidth(Math.max(idealTextWidth + (
|
|
7373
|
-
const availableTextWidth = Math.max(0, buttonWidth - (
|
|
7559
|
+
const buttonWidth = fullWidth ? Math.max(1, pos.width) : this.clampControlWidth(Math.max(idealTextWidth + (iconSvg ? iconSize + iconGap : 0) + textPad * 2, 60), pos.width);
|
|
7560
|
+
const availableTextWidth = Math.max(0, buttonWidth - textPad * 2 - (iconSvg ? iconSize + iconGap : 0));
|
|
7374
7561
|
const visibleText = this.truncateTextToWidth(text, availableTextWidth, fontSize);
|
|
7375
7562
|
const semanticBase = this.getSemanticVariantColor(variant);
|
|
7376
7563
|
const hasExplicitVariantColor = semanticBase !== void 0 || this.colorResolver.hasColor(variant);
|
|
@@ -7378,21 +7565,47 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7378
7565
|
const borderColor = variantColor;
|
|
7379
7566
|
const textColor = variantColor;
|
|
7380
7567
|
const strokeWidth = 0.5;
|
|
7381
|
-
|
|
7568
|
+
const iconOffsetY = buttonY + (buttonHeight - iconSize) / 2;
|
|
7569
|
+
const iconX = iconAlign === "right" ? pos.x + buttonWidth - edgePad - iconSize : pos.x + edgePad;
|
|
7570
|
+
const textAlign = String(node.props.align || "center").toLowerCase();
|
|
7571
|
+
const sidePad = textPad + 4;
|
|
7572
|
+
let textX;
|
|
7573
|
+
let textAnchor;
|
|
7574
|
+
if (textAlign === "left") {
|
|
7575
|
+
textX = iconSvg && iconAlign === "left" ? pos.x + edgePad + iconSize + iconGap : pos.x + sidePad;
|
|
7576
|
+
textAnchor = "start";
|
|
7577
|
+
} else if (textAlign === "right") {
|
|
7578
|
+
textX = iconSvg && iconAlign === "right" ? pos.x + buttonWidth - edgePad - iconSize - iconGap : pos.x + buttonWidth - sidePad;
|
|
7579
|
+
textAnchor = "end";
|
|
7580
|
+
} else {
|
|
7581
|
+
textX = pos.x + buttonWidth / 2;
|
|
7582
|
+
textAnchor = "middle";
|
|
7583
|
+
}
|
|
7584
|
+
let svg = `<g${this.getDataNodeId(node)}>
|
|
7382
7585
|
<rect x="${pos.x}" y="${buttonY}"
|
|
7383
7586
|
width="${buttonWidth}" height="${buttonHeight}"
|
|
7384
7587
|
rx="${radius}"
|
|
7385
7588
|
fill="none"
|
|
7386
7589
|
stroke="${borderColor}"
|
|
7387
7590
|
stroke-width="${strokeWidth}"
|
|
7388
|
-
filter="url(#sketch-rough)"
|
|
7389
|
-
|
|
7591
|
+
filter="url(#sketch-rough)"/>`;
|
|
7592
|
+
if (iconSvg) {
|
|
7593
|
+
svg += `
|
|
7594
|
+
<g transform="translate(${iconX}, ${iconOffsetY})">
|
|
7595
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${textColor}" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round">
|
|
7596
|
+
${this.extractSvgContent(iconSvg)}
|
|
7597
|
+
</svg>
|
|
7598
|
+
</g>`;
|
|
7599
|
+
}
|
|
7600
|
+
svg += `
|
|
7601
|
+
<text x="${textX}" y="${buttonY + buttonHeight / 2 + fontSize * 0.35}"
|
|
7390
7602
|
font-family="${this.fontFamily}"
|
|
7391
7603
|
font-size="${fontSize}"
|
|
7392
7604
|
font-weight="${fontWeight}"
|
|
7393
7605
|
fill="${textColor}"
|
|
7394
|
-
text-anchor="
|
|
7606
|
+
text-anchor="${textAnchor}">${this.escapeXml(visibleText)}</text>
|
|
7395
7607
|
</g>`;
|
|
7608
|
+
return svg;
|
|
7396
7609
|
}
|
|
7397
7610
|
/**
|
|
7398
7611
|
* Render badge with colored border instead of fill
|
|
@@ -7517,29 +7730,67 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7517
7730
|
renderInput(node, pos) {
|
|
7518
7731
|
const label = String(node.props.label || "");
|
|
7519
7732
|
const placeholder = String(node.props.placeholder || "");
|
|
7733
|
+
const iconLeftName = String(node.props.iconLeft || "").trim();
|
|
7734
|
+
const iconRightName = String(node.props.iconRight || "").trim();
|
|
7520
7735
|
const radius = this.tokens.input.radius;
|
|
7521
7736
|
const fontSize = this.tokens.input.fontSize;
|
|
7522
7737
|
const paddingX = this.tokens.input.paddingX;
|
|
7523
7738
|
const labelOffset = this.getControlLabelOffset(label);
|
|
7524
7739
|
const controlY = pos.y + labelOffset;
|
|
7525
7740
|
const controlHeight = Math.max(16, pos.height - labelOffset);
|
|
7526
|
-
|
|
7527
|
-
|
|
7741
|
+
const iconSize = 16;
|
|
7742
|
+
const iconPad = 12;
|
|
7743
|
+
const iconInnerGap = 8;
|
|
7744
|
+
const iconLeftSvg = iconLeftName ? getIcon(iconLeftName) : null;
|
|
7745
|
+
const iconRightSvg = iconRightName ? getIcon(iconRightName) : null;
|
|
7746
|
+
const leftOffset = iconLeftSvg ? iconPad + iconSize + iconInnerGap : 0;
|
|
7747
|
+
const rightOffset = iconRightSvg ? iconPad + iconSize + iconInnerGap : 0;
|
|
7748
|
+
const textX = pos.x + (iconLeftSvg ? leftOffset : paddingX);
|
|
7749
|
+
const iconColor = "#888888";
|
|
7750
|
+
const iconCenterY = controlY + (controlHeight - iconSize) / 2;
|
|
7751
|
+
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
7752
|
+
if (label) {
|
|
7753
|
+
svg += `
|
|
7754
|
+
<text x="${pos.x}" y="${this.getControlLabelBaselineY(pos.y)}"
|
|
7528
7755
|
font-family="${this.fontFamily}"
|
|
7529
7756
|
font-size="12"
|
|
7530
|
-
fill="${this.renderTheme.text}">${this.escapeXml(label)}</text
|
|
7757
|
+
fill="${this.renderTheme.text}">${this.escapeXml(label)}</text>`;
|
|
7758
|
+
}
|
|
7759
|
+
svg += `
|
|
7531
7760
|
<rect x="${pos.x}" y="${controlY}"
|
|
7532
7761
|
width="${pos.width}" height="${controlHeight}"
|
|
7533
7762
|
rx="${radius}"
|
|
7534
7763
|
fill="${this.renderTheme.cardBg}"
|
|
7535
7764
|
stroke="#2D3748"
|
|
7536
7765
|
stroke-width="0.5"
|
|
7537
|
-
filter="url(#sketch-rough)"
|
|
7538
|
-
|
|
7766
|
+
filter="url(#sketch-rough)"/>`;
|
|
7767
|
+
if (iconLeftSvg) {
|
|
7768
|
+
svg += `
|
|
7769
|
+
<g transform="translate(${pos.x + iconPad}, ${iconCenterY})">
|
|
7770
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
7771
|
+
${this.extractSvgContent(iconLeftSvg)}
|
|
7772
|
+
</svg>
|
|
7773
|
+
</g>`;
|
|
7774
|
+
}
|
|
7775
|
+
if (iconRightSvg) {
|
|
7776
|
+
svg += `
|
|
7777
|
+
<g transform="translate(${pos.x + pos.width - iconPad - iconSize}, ${iconCenterY})">
|
|
7778
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
7779
|
+
${this.extractSvgContent(iconRightSvg)}
|
|
7780
|
+
</svg>
|
|
7781
|
+
</g>`;
|
|
7782
|
+
}
|
|
7783
|
+
if (placeholder) {
|
|
7784
|
+
const availWidth = pos.width - (iconLeftSvg ? leftOffset : paddingX) - (iconRightSvg ? rightOffset : paddingX);
|
|
7785
|
+
const visiblePh = this.truncateTextToWidth(placeholder, Math.max(0, availWidth), fontSize);
|
|
7786
|
+
svg += `
|
|
7787
|
+
<text x="${textX}" y="${controlY + controlHeight / 2 + 5}"
|
|
7539
7788
|
font-family="${this.fontFamily}"
|
|
7540
7789
|
font-size="${fontSize}"
|
|
7541
|
-
fill="${this.renderTheme.textMuted}">${this.escapeXml(
|
|
7542
|
-
|
|
7790
|
+
fill="${this.renderTheme.textMuted}">${this.escapeXml(visiblePh)}</text>`;
|
|
7791
|
+
}
|
|
7792
|
+
svg += "\n </g>";
|
|
7793
|
+
return svg;
|
|
7543
7794
|
}
|
|
7544
7795
|
/**
|
|
7545
7796
|
* Render textarea with thicker border
|
|
@@ -7793,31 +8044,67 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7793
8044
|
renderSelect(node, pos) {
|
|
7794
8045
|
const label = String(node.props.label || "");
|
|
7795
8046
|
const placeholder = String(node.props.placeholder || "Select...");
|
|
8047
|
+
const iconLeftName = String(node.props.iconLeft || "").trim();
|
|
8048
|
+
const iconRightName = String(node.props.iconRight || "").trim();
|
|
7796
8049
|
const labelOffset = this.getControlLabelOffset(label);
|
|
7797
8050
|
const controlY = pos.y + labelOffset;
|
|
7798
8051
|
const controlHeight = Math.max(16, pos.height - labelOffset);
|
|
7799
8052
|
const centerY = controlY + controlHeight / 2 + 5;
|
|
7800
|
-
|
|
7801
|
-
|
|
8053
|
+
const iconSize = 16;
|
|
8054
|
+
const iconPad = 12;
|
|
8055
|
+
const iconInnerGap = 8;
|
|
8056
|
+
const iconLeftSvg = iconLeftName ? getIcon(iconLeftName) : null;
|
|
8057
|
+
const iconRightSvg = iconRightName ? getIcon(iconRightName) : null;
|
|
8058
|
+
const leftOffset = iconLeftSvg ? iconPad + iconSize + iconInnerGap : 0;
|
|
8059
|
+
const chevronWidth = 20;
|
|
8060
|
+
const iconColor = "#888888";
|
|
8061
|
+
const iconCenterY = controlY + (controlHeight - iconSize) / 2;
|
|
8062
|
+
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
8063
|
+
if (label) {
|
|
8064
|
+
svg += `
|
|
8065
|
+
<text x="${pos.x}" y="${this.getControlLabelBaselineY(pos.y)}"
|
|
7802
8066
|
font-family="${this.fontFamily}"
|
|
7803
8067
|
font-size="12"
|
|
7804
|
-
fill="${this.renderTheme.text}">${this.escapeXml(label)}</text
|
|
8068
|
+
fill="${this.renderTheme.text}">${this.escapeXml(label)}</text>`;
|
|
8069
|
+
}
|
|
8070
|
+
svg += `
|
|
7805
8071
|
<rect x="${pos.x}" y="${controlY}"
|
|
7806
8072
|
width="${pos.width}" height="${controlHeight}"
|
|
7807
8073
|
rx="6"
|
|
7808
8074
|
fill="${this.renderTheme.cardBg}"
|
|
7809
8075
|
stroke="#2D3748"
|
|
7810
8076
|
stroke-width="0.5"
|
|
7811
|
-
filter="url(#sketch-rough)"
|
|
7812
|
-
|
|
8077
|
+
filter="url(#sketch-rough)"/>`;
|
|
8078
|
+
if (iconLeftSvg) {
|
|
8079
|
+
svg += `
|
|
8080
|
+
<g transform="translate(${pos.x + iconPad}, ${iconCenterY})">
|
|
8081
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
8082
|
+
${this.extractSvgContent(iconLeftSvg)}
|
|
8083
|
+
</svg>
|
|
8084
|
+
</g>`;
|
|
8085
|
+
}
|
|
8086
|
+
if (iconRightSvg) {
|
|
8087
|
+
svg += `
|
|
8088
|
+
<g transform="translate(${pos.x + pos.width - chevronWidth - iconPad - iconSize}, ${iconCenterY})">
|
|
8089
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
8090
|
+
${this.extractSvgContent(iconRightSvg)}
|
|
8091
|
+
</svg>
|
|
8092
|
+
</g>`;
|
|
8093
|
+
}
|
|
8094
|
+
const textX = pos.x + (iconLeftSvg ? leftOffset : 12);
|
|
8095
|
+
const availWidth = pos.width - (iconLeftSvg ? leftOffset : 12) - chevronWidth - (iconRightSvg ? iconPad + iconSize + iconInnerGap : 0);
|
|
8096
|
+
const visiblePh = this.truncateTextToWidth(placeholder, Math.max(0, availWidth), 14);
|
|
8097
|
+
svg += `
|
|
8098
|
+
<text x="${textX}" y="${centerY}"
|
|
7813
8099
|
font-family="${this.fontFamily}"
|
|
7814
8100
|
font-size="14"
|
|
7815
|
-
fill="${this.renderTheme.textMuted}">${this.escapeXml(
|
|
8101
|
+
fill="${this.renderTheme.textMuted}">${this.escapeXml(visiblePh)}</text>
|
|
7816
8102
|
<text x="${pos.x + pos.width - 20}" y="${centerY}"
|
|
7817
8103
|
font-family="${this.fontFamily}"
|
|
7818
8104
|
font-size="16"
|
|
7819
8105
|
fill="${this.renderTheme.textMuted}">\u25BC</text>
|
|
7820
8106
|
</g>`;
|
|
8107
|
+
return svg;
|
|
7821
8108
|
}
|
|
7822
8109
|
/**
|
|
7823
8110
|
* Render checkbox with sketch filter and Comic Sans
|
|
@@ -8216,44 +8503,43 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8216
8503
|
renderImage(node, pos) {
|
|
8217
8504
|
const placeholder = String(node.props.placeholder || "landscape").toLowerCase();
|
|
8218
8505
|
const iconType = String(node.props.icon || "").trim();
|
|
8506
|
+
const variant = String(node.props.variant || "").trim();
|
|
8219
8507
|
const iconSvg = placeholder === "icon" && iconType.length > 0 ? getIcon(iconType) : null;
|
|
8508
|
+
const imageBg = this.options.theme === "dark" ? "#2A2A2A" : "#E8E8E8";
|
|
8220
8509
|
if (iconSvg) {
|
|
8221
|
-
const
|
|
8222
|
-
const
|
|
8223
|
-
const
|
|
8224
|
-
const
|
|
8225
|
-
const
|
|
8226
|
-
const
|
|
8510
|
+
const semanticBase = variant ? this.getSemanticVariantColor(variant) : void 0;
|
|
8511
|
+
const hasVariant = variant.length > 0 && (semanticBase !== void 0 || this.colorResolver.hasColor(variant));
|
|
8512
|
+
const variantColor = hasVariant ? this.resolveVariantColor(variant, this.renderTheme.primary) : null;
|
|
8513
|
+
const bgColor = hasVariant ? this.hexToRgba(variantColor, 0.12) : imageBg;
|
|
8514
|
+
const iconColor = hasVariant ? variantColor : "#666666";
|
|
8515
|
+
const iconSize = Math.max(16, Math.min(pos.width, pos.height) * 0.6);
|
|
8516
|
+
const iconOffsetX = pos.x + (pos.width - iconSize) / 2;
|
|
8517
|
+
const iconOffsetY = pos.y + (pos.height - iconSize) / 2;
|
|
8227
8518
|
return `<g${this.getDataNodeId(node)}>
|
|
8228
|
-
<!-- Image Background -->
|
|
8229
8519
|
<rect x="${pos.x}" y="${pos.y}"
|
|
8230
8520
|
width="${pos.width}" height="${pos.height}"
|
|
8231
|
-
fill="
|
|
8232
|
-
stroke="#2D3748"
|
|
8233
|
-
stroke-width="0.5"
|
|
8521
|
+
fill="${bgColor}"
|
|
8234
8522
|
rx="4"
|
|
8235
8523
|
filter="url(#sketch-rough)"/>
|
|
8236
|
-
|
|
8237
|
-
<!-- Custom Icon Placeholder -->
|
|
8238
|
-
<rect x="${badgeX}" y="${badgeY}"
|
|
8239
|
-
width="${badgeSize}" height="${badgeSize}"
|
|
8240
|
-
rx="${Math.max(4, badgeSize * 0.2)}"
|
|
8241
|
-
fill="none"
|
|
8242
|
-
stroke="#2D3748"
|
|
8243
|
-
stroke-width="0.5"
|
|
8244
|
-
filter="url(#sketch-rough)"/>
|
|
8245
8524
|
<g transform="translate(${iconOffsetX}, ${iconOffsetY})">
|
|
8246
|
-
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="
|
|
8525
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
8247
8526
|
${this.extractSvgContent(iconSvg)}
|
|
8248
8527
|
</svg>
|
|
8249
8528
|
</g>
|
|
8529
|
+
<rect x="${pos.x}" y="${pos.y}"
|
|
8530
|
+
width="${pos.width}" height="${pos.height}"
|
|
8531
|
+
fill="none"
|
|
8532
|
+
stroke="#2D3748"
|
|
8533
|
+
stroke-width="0.5"
|
|
8534
|
+
rx="4"
|
|
8535
|
+
filter="url(#sketch-rough)"/>
|
|
8250
8536
|
</g>`;
|
|
8251
8537
|
}
|
|
8252
8538
|
return `<g${this.getDataNodeId(node)}>
|
|
8253
8539
|
<!-- Image Background -->
|
|
8254
8540
|
<rect x="${pos.x}" y="${pos.y}"
|
|
8255
8541
|
width="${pos.width}" height="${pos.height}"
|
|
8256
|
-
fill="
|
|
8542
|
+
fill="${imageBg}"
|
|
8257
8543
|
stroke="#2D3748"
|
|
8258
8544
|
stroke-width="0.5"
|
|
8259
8545
|
rx="4"
|
|
@@ -8308,17 +8594,23 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8308
8594
|
*/
|
|
8309
8595
|
renderSidebarMenu(node, pos) {
|
|
8310
8596
|
const itemsStr = String(node.props.items || "Item 1,Item 2,Item 3");
|
|
8597
|
+
const iconsStr = String(node.props.icons || "");
|
|
8311
8598
|
const items = itemsStr.split(",").map((s) => s.trim());
|
|
8599
|
+
const icons = iconsStr ? iconsStr.split(",").map((s) => s.trim()) : [];
|
|
8312
8600
|
const itemHeight = 40;
|
|
8313
8601
|
const fontSize = 14;
|
|
8314
8602
|
const activeIndex = Number(node.props.active || 0);
|
|
8315
8603
|
const accentColor = this.resolveAccentColor();
|
|
8604
|
+
const variantProp = String(node.props.variant || "").trim();
|
|
8605
|
+
const semanticVariant = variantProp ? this.getSemanticVariantColor(variantProp) : void 0;
|
|
8606
|
+
const hasVariant = variantProp.length > 0 && (semanticVariant !== void 0 || this.colorResolver.hasColor(variantProp));
|
|
8607
|
+
const activeColor = hasVariant ? this.resolveVariantColor(variantProp, accentColor) : accentColor;
|
|
8316
8608
|
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
8317
8609
|
items.forEach((item, index) => {
|
|
8318
8610
|
const itemY = pos.y + index * itemHeight;
|
|
8319
8611
|
const isActive = index === activeIndex;
|
|
8320
|
-
const bgColor = isActive ? this.hexToRgba(
|
|
8321
|
-
const textColor = isActive ?
|
|
8612
|
+
const bgColor = isActive ? this.hexToRgba(activeColor, 0.15) : "transparent";
|
|
8613
|
+
const textColor = isActive ? activeColor : this.resolveTextColor();
|
|
8322
8614
|
const fontWeight = isActive ? "500" : "400";
|
|
8323
8615
|
if (isActive) {
|
|
8324
8616
|
svg += `
|
|
@@ -8328,8 +8620,24 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8328
8620
|
fill="${bgColor}"
|
|
8329
8621
|
filter="url(#sketch-rough)"/>`;
|
|
8330
8622
|
}
|
|
8623
|
+
let currentX = pos.x + 12;
|
|
8624
|
+
if (icons[index]) {
|
|
8625
|
+
const iconSvg = getIcon(icons[index]);
|
|
8626
|
+
if (iconSvg) {
|
|
8627
|
+
const iconSize = 16;
|
|
8628
|
+
const iconY = itemY + (itemHeight - iconSize) / 2;
|
|
8629
|
+
const iconColor = isActive ? activeColor : this.resolveMutedColor();
|
|
8630
|
+
svg += `
|
|
8631
|
+
<g transform="translate(${currentX}, ${iconY})">
|
|
8632
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
8633
|
+
${this.extractSvgContent(iconSvg)}
|
|
8634
|
+
</svg>
|
|
8635
|
+
</g>`;
|
|
8636
|
+
currentX += iconSize + 8;
|
|
8637
|
+
}
|
|
8638
|
+
}
|
|
8331
8639
|
svg += `
|
|
8332
|
-
<text x="${
|
|
8640
|
+
<text x="${currentX}" y="${itemY + itemHeight / 2 + 5}"
|
|
8333
8641
|
font-family="${this.fontFamily}"
|
|
8334
8642
|
font-size="${fontSize}"
|
|
8335
8643
|
font-weight="${fontWeight}"
|