diff --git a/src/CSSObject.cs b/src/CSSObject.cs index d179675..fce91e3 100644 --- a/src/CSSObject.cs +++ b/src/CSSObject.cs @@ -9,7 +9,7 @@ namespace CssInCSharp { public sealed partial class CSSObject { - private readonly Dictionary _styles = new (); + private readonly Dictionary _styles = new(); private readonly Dictionary _properties = new(); public Dictionary GetProperties() => _properties; public Dictionary GetStyles() => _styles; @@ -30,7 +30,7 @@ public string SerializeCss(string hashId) return Serialize(Compile(ParseStyle(true, hashId)), Stringify); } - public string ParseStyle(bool root, string hashId) + internal string ParseStyle(bool root, string hashId) { var sb = new StringBuilder(); @@ -41,6 +41,12 @@ public string ParseStyle(bool root, string hashId) foreach (var subStyle in _styles) { var nextRoot = false; + if (subStyle.Key.StartsWith("@")) + { + // if is media type, skip and insert hashId from subStyle. + root = false; + nextRoot = true; + } sb.Append($"{subStyle.Key}{{{subStyle.Value.ParseStyle(nextRoot, hashId)}}}"); } @@ -93,6 +99,23 @@ public CSSObject Merge(CSSObject[] objects) private void SetStyle(string key, CSSInterpolation value) { if (key == null) return; + /* + * If is css variable, + * eg: + * new CSSObject + * { + * ["--font-size"] = "12px" // here is string value. + * } + */ + if (value.IsT2) + { + _properties[key] = (Property)value.AsT2; // cast to Property type. + return; + } + + /* + * if is CSSObject or CSSObject[] + */ var cssObject = value.IsT0 ? value.AsT0 : new CSSObject().Merge(value.ToCssArray()); if (cssObject == null) return; if (key == MERGE_OPERATOR) diff --git a/src/Types/CSSInterpolation.cs b/src/Types/CSSInterpolation.cs index d26435c..f469a7e 100644 --- a/src/Types/CSSInterpolation.cs +++ b/src/Types/CSSInterpolation.cs @@ -7,21 +7,25 @@ public struct CSSInterpolation private readonly int _index; private readonly CSSObject _value0; private readonly CSSInterpolation[] _value1; + private readonly string _value2; public CSSObject this[string key] => _value0; - private CSSInterpolation(int index, CSSObject value0 = default, CSSInterpolation[] value1 = default) + private CSSInterpolation(int index, CSSObject value0 = default, CSSInterpolation[] value1 = default, string value2 = default) { _index = index; _value0 = value0; _value1 = value1; + _value2 = value2; } public static implicit operator CSSInterpolation(CSSObject t) => new(0, value0: t); public static implicit operator CSSInterpolation(CSSInterpolation[] t) => new(1, value1: t); + public static implicit operator CSSInterpolation(string t) => new(2, value2: t); public bool IsT0 => _index == 0; public bool IsT1 => _index == 1; + public bool IsT2 => _index == 2; public CSSObject AsT0 => _index == 0 ? @@ -31,5 +35,10 @@ private CSSInterpolation(int index, CSSObject value0 = default, CSSInterpolation _index == 1 ? _value1 : throw new InvalidOperationException($"Cannot return as T1 as result is T{_index}"); + + public string AsT2 => + _index == 2 ? + _value2 : + throw new InvalidOperationException($"Cannot return as T2 as result is T{_index}"); } } diff --git a/src/Types/Functions.cs b/src/Types/Functions.cs index bc0452e..551bdd6 100644 --- a/src/Types/Functions.cs +++ b/src/Types/Functions.cs @@ -7,18 +7,18 @@ internal static string FormatValue(string key, T value) return value switch { string v => v, - int v => Wrap(key, v), - float v => Wrap(key, v), - double v => Wrap(key, v), + int v => Wrap(key, v, v != 0), + float v => Wrap(key, v, v != 0), + double v => Wrap(key, v, v != 0), Keyframe v => v.ToString(), _ => value?.ToString() }; } - internal static string Wrap(string key, T value) + internal static string Wrap(string key, T value, bool hasValue) { // All number types need to be px-wrapped and unwanted parts of Unitless should be ignored. - if (!string.IsNullOrEmpty(key) && !Unitless.Keys.ContainsKey(key)) + if (!string.IsNullOrEmpty(key) && !Unitless.Keys.ContainsKey(key) && hasValue) { return $"{value}px"; } diff --git a/test/CssInCSharp.Tests/CSSObjectTests.cs b/test/CssInCSharp.Tests/CSSObjectTests.cs new file mode 100644 index 0000000..9b56d49 --- /dev/null +++ b/test/CssInCSharp.Tests/CSSObjectTests.cs @@ -0,0 +1,63 @@ +using Shouldly; +using Xunit; + +namespace CssInCSharp.Tests +{ + public class CSSObjectTests + { + [Fact] + public void Number_Without_Pxwrap() + { + new CSSObject + { + [".test"] = new CSSObject + { + AnimationIterationCount = 1, + AspectRatio = 1, + BorderImageOutset = 1, + BorderImageSlice = 1, + BorderImageWidth = 1, + ColumnCount = 1, + Columns = 1, + Flex = 1, + FlexGrow = 1, + MsFlexPositive = 1, + FlexShrink = 1, + FontWeight = 1, + LineHeight = 1, + Opacity = 1, + Order = 1, + Orphans = 1, + TabSize = 1, + Widows = 1, + ZIndex = 1, + Zoom = 1, + WebkitLineClamp = 1, + } + } + .ToString() + .ShouldBe(".test{animation-iteration-count:1;aspect-ratio:1;border-image-outset:1;border-image-slice:1;border-image-width:1;column-count:1;columns:1;flex:1;flex-grow:1;-ms-flex-positive:1px;flex-shrink:1;font-weight:1;line-height:1;opacity:1;order:1;orphans:1;tab-size:1;widows:1;z-index:1;zoom:1;-webkit-line-clamp:1;}"); + } + + [Fact] + public void Media() + { + var css = new CSSObject + { + ["@media (min-width: 500px)"] = new CSSObject + { + [".grid-sm"] = new CSSObject + { + Display = "none", + InsetInlineStart = "auto", + InsetInlineEnd = "auto", + MarginInlineStart = 0, + Order = 0 + } + } + }; + css.ToString().ShouldBe("@media (min-width: 500px){.grid-sm{display:none;inset-inline-start:auto;inset-inline-end:auto;margin-inline-start:0;order:0;}}"); + css.SerializeCss("1iw360o").ShouldBe("@media (min-width: 500px){:where(.1iw360o).grid-sm{display:none;inset-inline-start:auto;inset-inline-end:auto;margin-inline-start:0;order:0;}}"); + } + } +}