diff --git a/src/AlohaKit.UI.Figma/AlohaKit.UI.Figma.csproj b/src/AlohaKit.UI.Figma/AlohaKit.UI.Figma.csproj
new file mode 100644
index 0000000..c50c322
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/AlohaKit.UI.Figma.csproj
@@ -0,0 +1,61 @@
+
+
+
+ net7.0-maccatalyst
+ $(TargetFrameworks);net7.0-windows10.0.19041.0
+
+
+ Exe
+ AlohaKit.UI.Figma
+ true
+ true
+ enable
+
+
+ AlohaKit.UI.Figma
+
+
+ com.companyname.alohakit.ui.figma
+ bd33300f-7d11-47df-95ae-c835a27d86e1
+
+
+ 1.0
+ 1
+
+ 11.0
+ 13.1
+ 21.0
+ 10.0.17763.0
+ 10.0.17763.0
+ 6.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AlohaKit.UI.Figma/App.xaml b/src/AlohaKit.UI.Figma/App.xaml
new file mode 100644
index 0000000..ecc0acc
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/App.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AlohaKit.UI.Figma/App.xaml.cs b/src/AlohaKit.UI.Figma/App.xaml.cs
new file mode 100644
index 0000000..75a21de
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/App.xaml.cs
@@ -0,0 +1,12 @@
+namespace AlohaKit.UI.Figma
+{
+ public partial class App : Application
+ {
+ public App()
+ {
+ InitializeComponent();
+
+ MainPage = new AppShell();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/AppShell.xaml b/src/AlohaKit.UI.Figma/AppShell.xaml
new file mode 100644
index 0000000..4b3a7bd
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/AppShell.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/src/AlohaKit.UI.Figma/AppShell.xaml.cs b/src/AlohaKit.UI.Figma/AppShell.xaml.cs
new file mode 100644
index 0000000..d7e6dc8
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/AppShell.xaml.cs
@@ -0,0 +1,10 @@
+namespace AlohaKit.UI.Figma
+{
+ public partial class AppShell : Shell
+ {
+ public AppShell()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Converters/IsGeneratingToVisibleConverter.cs b/src/AlohaKit.UI.Figma/Converters/IsGeneratingToVisibleConverter.cs
new file mode 100644
index 0000000..92c31e8
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Converters/IsGeneratingToVisibleConverter.cs
@@ -0,0 +1,19 @@
+using System.Globalization;
+
+namespace AlohaKit.UI.Figma.Converters
+{
+ internal class IsGeneratingToVisibleConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var text = (string)value;
+
+ return !string.IsNullOrEmpty(text);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/AlohaKit.UI.Figma/Figma/Converters/ElipseConverter.cs b/src/AlohaKit.UI.Figma/Figma/Converters/ElipseConverter.cs
new file mode 100644
index 0000000..99082dd
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Figma/Converters/ElipseConverter.cs
@@ -0,0 +1,96 @@
+using FigmaSharp.Converters;
+using AlohaKit.UI.Figma.Extensions;
+using FigmaSharp.Models;
+using FigmaSharp.Services;
+using System.Globalization;
+using System.Text;
+
+namespace AlohaKit.UI.Figma.Converters
+{
+ internal class ElipseConverter : ElipseConverterBase
+ {
+ public override string ConvertToCode(CodeNode currentNode, CodeNode parentNode, ICodeRenderService rendererService)
+ {
+ if (currentNode.Node is not FigmaElipse elipseNode)
+ {
+ return string.Empty;
+ }
+
+ StringBuilder builder = new StringBuilder();
+
+ builder.AppendLine("");
+
+ if (elipseNode.HasFills)
+ {
+ var backgroundPaint = elipseNode.fills.FirstOrDefault();
+
+ if (backgroundPaint != null && backgroundPaint.visible)
+ {
+ builder.AppendLine("\n\t");
+
+ if (backgroundPaint.color != null)
+ {
+ builder.AppendLine($"\t\t");
+ }
+
+ if (backgroundPaint.gradientStops != null)
+ {
+ if (backgroundPaint.type.Equals("GRADIENT_LINEAR", StringComparison.CurrentCultureIgnoreCase))
+ builder.AppendLine($"{backgroundPaint.gradientStops.ToLinearGradientPaint()}");
+
+ if (backgroundPaint.type.Equals("GRADIENT_RADIAL", StringComparison.CurrentCultureIgnoreCase))
+ builder.AppendLine($"{backgroundPaint.gradientStops.ToRadialGradientPaint()}");
+ }
+
+ if (backgroundPaint.imageRef != null)
+ builder.AppendLine("\t\t");
+
+ builder.AppendLine("\t");
+ }
+ }
+
+ builder.AppendLine("");
+
+ return builder.ToString();
+ }
+
+ public override FigmaSharp.Views.IView ConvertToView(FigmaNode currentNode, ViewNode parent, ViewRenderService rendererService)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override Type GetControlType(FigmaNode currentNode)
+ => typeof(View);
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Figma/Converters/FrameConverter.cs b/src/AlohaKit.UI.Figma/Figma/Converters/FrameConverter.cs
new file mode 100644
index 0000000..ab0d2c4
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Figma/Converters/FrameConverter.cs
@@ -0,0 +1,102 @@
+using AlohaKit.UI.Figma.Extensions;
+using FigmaSharp.Converters;
+using FigmaSharp.Models;
+using FigmaSharp.Services;
+using System.Globalization;
+using System.Text;
+
+namespace AlohaKit.UI.Figma.Converters
+{
+ public class FrameConverter : FrameConverterBase
+ {
+ public override string ConvertToCode(CodeNode currentNode, CodeNode parentNode, ICodeRenderService rendererService)
+ {
+ if (currentNode.Node is not FigmaFrame frameNode)
+ {
+ return string.Empty;
+ }
+
+ StringBuilder builder = new StringBuilder();
+
+ builder.AppendLine("");
+
+ if (frameNode.HasFills)
+ {
+ var backgroundPaint = frameNode.fills.FirstOrDefault();
+
+ if (backgroundPaint != null && backgroundPaint.visible)
+ {
+ builder.AppendLine("\n\t");
+
+ if (backgroundPaint.color != null)
+ {
+ builder.AppendLine($"\t\t");
+ }
+
+ if (backgroundPaint.gradientStops != null)
+ {
+ if (backgroundPaint.type.Equals("GRADIENT_LINEAR", StringComparison.CurrentCultureIgnoreCase))
+ builder.AppendLine($"{backgroundPaint.gradientStops.ToLinearGradientPaint()}");
+
+ if (backgroundPaint.type.Equals("GRADIENT_RADIAL", StringComparison.CurrentCultureIgnoreCase))
+ builder.AppendLine($"{backgroundPaint.gradientStops.ToRadialGradientPaint()}");
+ }
+
+ if (backgroundPaint.imageRef != null)
+ builder.AppendLine("\t\t");
+
+ builder.AppendLine("\t");
+ }
+ }
+
+ builder.AppendLine("");
+
+ return builder.ToString();
+ }
+
+ public override FigmaSharp.Views.IView ConvertToView(FigmaNode currentNode, ViewNode parent, ViewRenderService rendererService)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override Type GetControlType(FigmaNode currentNode)
+ => typeof(View);
+
+ public override bool ScanChildren(FigmaNode currentNode)
+ => true;
+ }
+}
diff --git a/src/AlohaKit.UI.Figma/Figma/Converters/ImageConverter.cs b/src/AlohaKit.UI.Figma/Figma/Converters/ImageConverter.cs
new file mode 100644
index 0000000..7d3d963
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Figma/Converters/ImageConverter.cs
@@ -0,0 +1,111 @@
+using AlohaKit.UI.Figma.Extensions;
+using FigmaSharp.Converters;
+using FigmaSharp.Models;
+using FigmaSharp.Services;
+using System.Globalization;
+using System.Text;
+using System.Xml.Linq;
+
+namespace AlohaKit.UI.Figma.Converters
+{
+ internal class ImageConverter : NodeConverter
+ {
+ public override bool CanConvert(FigmaNode currentNode)
+ => currentNode.GetType() == typeof(FigmaVector);
+
+ public override string ConvertToCode(CodeNode currentNode, CodeNode parentNode, ICodeRenderService rendererService)
+ {
+ if (currentNode.Node is not FigmaVector figmaVector)
+ {
+ return string.Empty;
+ }
+
+ if (figmaVector.fillGeometry == null || figmaVector.fillGeometry.Length == 0)
+ {
+ return string.Empty;
+ }
+
+ StringBuilder builder = new StringBuilder();
+
+ NumberFormatInfo nfi = new NumberFormatInfo
+ {
+ NumberDecimalSeparator = "."
+ };
+
+ builder.AppendLine(" 0)
+ {
+ var geometry = figmaVector.fillGeometry[0];
+ builder.AppendLine($"\tData=\"{geometry.path}\"");
+ }
+
+ if (figmaVector.HasStrokes)
+ {
+ var strokePaint = figmaVector.strokes.FirstOrDefault();
+
+ if (strokePaint.color != null)
+ {
+ builder.AppendLine($"\tStroke=\"{strokePaint.color.ToCodeString()}\"");
+ }
+
+ if (figmaVector.strokeWeight != 0)
+ {
+ var strokeSize = figmaVector.strokeWeight;
+ builder.AppendLine($"\tStrokeThickness=\"{strokeSize}\"");
+ }
+ }
+
+ builder.Append("\t>");
+
+ if (figmaVector.HasFills)
+ {
+ var backgroundPaint = figmaVector.fills.FirstOrDefault();
+
+ if (backgroundPaint != null && backgroundPaint.visible)
+ {
+ builder.AppendLine("\n\t");
+
+ if (backgroundPaint.color != null)
+ {
+ builder.AppendLine($"\t\t");
+ }
+
+ if (backgroundPaint.gradientStops != null)
+ {
+ if (backgroundPaint.type.Equals("GRADIENT_LINEAR", StringComparison.CurrentCultureIgnoreCase))
+ builder.AppendLine($"{backgroundPaint.gradientStops.ToLinearGradientPaint()}");
+
+ if (backgroundPaint.type.Equals("GRADIENT_RADIAL", StringComparison.CurrentCultureIgnoreCase))
+ builder.AppendLine($"{backgroundPaint.gradientStops.ToRadialGradientPaint()}");
+ }
+
+ if (backgroundPaint.imageRef != null)
+ builder.AppendLine("\t\t");
+
+ builder.AppendLine("\t");
+ }
+ }
+
+ builder.AppendLine("");
+
+ return builder.ToString();
+ }
+
+ public override FigmaSharp.Views.IView ConvertToView(FigmaNode currentNode, ViewNode parent, ViewRenderService rendererService)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override Type GetControlType(FigmaNode currentNode)
+ => typeof(View);
+ }
+}
diff --git a/src/AlohaKit.UI.Figma/Figma/Converters/LineConverter.cs b/src/AlohaKit.UI.Figma/Figma/Converters/LineConverter.cs
new file mode 100644
index 0000000..1b7a4b4
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Figma/Converters/LineConverter.cs
@@ -0,0 +1,22 @@
+using FigmaSharp.Converters;
+using FigmaSharp.Models;
+using FigmaSharp.Services;
+
+namespace AlohaKit.UI.Figma.Converters
+{
+ internal class LineConverter : LineConverterBase
+ {
+ public override string ConvertToCode(CodeNode currentNode, CodeNode parentNode, ICodeRenderService rendererService)
+ {
+ return "";
+ }
+
+ public override FigmaSharp.Views.IView ConvertToView(FigmaNode currentNode, ViewNode parent, ViewRenderService rendererService)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override Type GetControlType(FigmaNode currentNode)
+ => typeof(View);
+ }
+}
diff --git a/src/AlohaKit.UI.Figma/Figma/Converters/PolygonConverter.cs b/src/AlohaKit.UI.Figma/Figma/Converters/PolygonConverter.cs
new file mode 100644
index 0000000..324304b
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Figma/Converters/PolygonConverter.cs
@@ -0,0 +1,22 @@
+using FigmaSharp.Converters;
+using FigmaSharp.Models;
+using FigmaSharp.Services;
+
+namespace AlohaKit.UI.Figma.Converters
+{
+ internal class PolygonConverter : RegularPolygonConverterBase
+ {
+ public override string ConvertToCode(CodeNode currentNode, CodeNode parentNode, ICodeRenderService rendererService)
+ {
+ return "";
+ }
+
+ public override FigmaSharp.Views.IView ConvertToView(FigmaNode currentNode, ViewNode parent, ViewRenderService rendererService)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override Type GetControlType(FigmaNode currentNode)
+ => typeof(View);
+ }
+}
diff --git a/src/AlohaKit.UI.Figma/Figma/Converters/RectangleConverter.cs b/src/AlohaKit.UI.Figma/Figma/Converters/RectangleConverter.cs
new file mode 100644
index 0000000..5d93875
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Figma/Converters/RectangleConverter.cs
@@ -0,0 +1,96 @@
+using AlohaKit.UI.Figma.Extensions;
+using FigmaSharp.Converters;
+using FigmaSharp.Models;
+using FigmaSharp.Services;
+using System.Globalization;
+using System.Text;
+
+namespace AlohaKit.UI.Figma.Converters
+{
+ internal class RectangleConverter : RectangleVectorConverterBase
+ {
+ public override string ConvertToCode(CodeNode currentNode, CodeNode parentNode, ICodeRenderService rendererService)
+ {
+ if (currentNode.Node is not RectangleVector rectangleVector)
+ {
+ return string.Empty;
+ }
+
+ StringBuilder builder = new StringBuilder();
+
+ builder.AppendLine("");
+
+ if (rectangleVector.HasFills)
+ {
+ var backgroundPaint = rectangleVector.fills.FirstOrDefault();
+
+ if (backgroundPaint != null && backgroundPaint.visible)
+ {
+ builder.AppendLine("\n\t");
+
+ if (backgroundPaint.color != null)
+ {
+ builder.AppendLine($"\t\t");
+ }
+
+ if (backgroundPaint.gradientStops != null)
+ {
+ if (backgroundPaint.type.Equals("GRADIENT_LINEAR", StringComparison.CurrentCultureIgnoreCase))
+ builder.AppendLine($"{backgroundPaint.gradientStops.ToLinearGradientPaint()}");
+
+ if (backgroundPaint.type.Equals("GRADIENT_RADIAL", StringComparison.CurrentCultureIgnoreCase))
+ builder.AppendLine($"{backgroundPaint.gradientStops.ToRadialGradientPaint()}");
+ }
+
+ if (backgroundPaint.imageRef != null)
+ builder.AppendLine("\t\t");
+
+ builder.AppendLine("\t");
+ }
+ }
+
+ builder.AppendLine("");
+
+ return builder.ToString();
+ }
+
+ public override FigmaSharp.Views.IView ConvertToView(FigmaNode currentNode, ViewNode parent, ViewRenderService rendererService)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override Type GetControlType(FigmaNode currentNode)
+ => typeof(View);
+ }
+}
diff --git a/src/AlohaKit.UI.Figma/Figma/Converters/TextConverter.cs b/src/AlohaKit.UI.Figma/Figma/Converters/TextConverter.cs
new file mode 100644
index 0000000..0b74a69
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Figma/Converters/TextConverter.cs
@@ -0,0 +1,70 @@
+using FigmaSharp.Converters;
+using AlohaKit.UI.Figma.Extensions;
+using FigmaSharp.Models;
+using FigmaSharp.Services;
+using System.Globalization;
+using System.Text;
+
+namespace AlohaKit.UI.Figma.Converters
+{
+ internal class TextConverter : TextConverterBase
+ {
+ public override string ConvertToCode(CodeNode currentNode, CodeNode parentNode, ICodeRenderService rendererService)
+ {
+ if (currentNode.Node is not FigmaText textNode)
+ {
+ return string.Empty;
+ }
+
+ StringBuilder builder = new StringBuilder();
+
+ builder.AppendLine("");
+
+ return builder.ToString();
+ }
+
+ public override FigmaSharp.Views.IView ConvertToView(FigmaNode currentNode, ViewNode parent, ViewRenderService rendererService)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override Type GetControlType(FigmaNode currentNode)
+ => typeof(View);
+ }
+}
diff --git a/src/AlohaKit.UI.Figma/Figma/Extensions/CodeGenerationExtensions.cs b/src/AlohaKit.UI.Figma/Figma/Extensions/CodeGenerationExtensions.cs
new file mode 100644
index 0000000..bd4e079
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Figma/Extensions/CodeGenerationExtensions.cs
@@ -0,0 +1,174 @@
+using AlohaKit.UI.Figma.Helpers;
+using FigmaSharp;
+using FigmaSharp.Models;
+using FigmaSharp.Services;
+using System.Text;
+
+namespace AlohaKit.UI.Figma.Extensions
+{
+ public static class CodeGenerationExtensions
+ {
+ public static string GetFullProperty(this Enum enumeration, string property)
+ {
+ return enumeration.GetType().WithProperty(property.ToCamelCase());
+ }
+
+ public static string WithProperty(this Type type, string property)
+ {
+ return string.Format("{0}.{1}", type.FullName, property);
+ }
+
+ public static string CreatePropertyName(this CodeNode sender, string propertyName)
+ {
+ return string.Format("{0}.{1}", sender.Name, propertyName);
+ }
+
+ public static string CreateChildObjectName(this CodeNode sender, string propertyName)
+ {
+ return string.Format("{0}{1}", sender.Name, propertyName);
+ }
+
+ public static string GetConstructor(this Type type, params string[] parameters)
+ {
+ string args;
+ if (parameters.Length > 0)
+ {
+ args = string.Join(", ", parameters);
+ }
+ else
+ {
+ args = string.Empty;
+ }
+ return $"new {type.FullName} ({args})";
+ }
+
+ public static string GetMethod(this Type type, string methodName, string parameters, bool inQuotes = false, bool includesSemicolon = true)
+ {
+ return CodeGenerationHelpers.GetMethod(type.FullName, methodName, parameters, inQuotes, includesSemicolon);
+ }
+
+ public static string GetFullName(this Enum myEnum)
+ {
+ return string.Format("{0}.{1}", myEnum.GetType().Name, myEnum.ToString());
+ }
+
+ public static string GetConstructor(this CodeNode sender, Type type, bool includesVar = true)
+ {
+ return GetConstructor(sender, type.FullName, includesVar);
+ }
+
+ public static string GetConstructor(this CodeNode sender, string typeFullName, bool includesVar = true)
+ {
+ return CodeGenerationHelpers.GetConstructor(sender.Name, typeFullName, includesVar);
+ }
+
+ public static string GetPropertyEquality(this CodeNode sender, string propertyName, Enum value)
+ {
+ return GetPropertyEquality(sender, propertyName, value.GetFullName());
+ }
+
+ public static string GetPropertyEquality(this CodeNode sender, string propertyName, bool value)
+ {
+ return GetPropertyEquality(sender, propertyName, value.ToString());
+ }
+
+ public static string GetPropertyEquality(this CodeNode sender, string propertyName, string value, bool inQuotes = false)
+ {
+ return CodeGenerationHelpers.GetPropertyEquality(sender.Name, propertyName, value, inQuotes);
+ }
+
+ public static string GetMethod(this CodeNode sender, string methodName, Enum parameter)
+ {
+ return GetMethod(sender, methodName, parameter.GetFullName());
+ }
+
+ public static string GetMethod(this CodeNode sender, string methodName, bool parameter)
+ {
+ return CodeGenerationHelpers.GetMethod(sender.Name, methodName, parameter);
+ }
+
+ public static string GetMethod(this CodeNode sender, string methodName, string parameters, bool inQuotes = false)
+ {
+ return CodeGenerationHelpers.GetMethod(sender.Name, methodName, parameters, inQuotes);
+ }
+
+ #region StringBuilder Code Generation
+
+ public static void WriteConstructor(this StringBuilder builder, string viewName, Type type, bool includesVar = true)
+ {
+ WriteConstructor(builder, viewName, type.FullName, includesVar);
+ }
+
+ public static void WriteConstructor(this StringBuilder builder, string viewName, string typeFullName, bool includesVar = true)
+ {
+ builder.AppendLine(CodeGenerationHelpers.GetConstructor(viewName, typeFullName, includesVar));
+ }
+
+ public static void WritePropertyEquality(this StringBuilder builder, string viewName, string propertyName, Enum value)
+ {
+ WritePropertyEquality(builder, viewName, propertyName, value.GetFullName());
+ }
+
+ public static void WriteEquality(this StringBuilder builder, string viewName, string value, bool inQuotes = false, bool instanciate = false)
+ {
+ builder.AppendLine(CodeGenerationHelpers.GetEquality(viewName, value, inQuotes, instanciate));
+ }
+
+ public static void WritePropertyEquality(this StringBuilder builder, string viewName, string propertyName, bool value)
+ {
+ WritePropertyEquality(builder, viewName, propertyName, value.ToString());
+ }
+
+ public static void WritePropertyEquality(this StringBuilder builder, string viewName, string propertyName, string value, bool inQuotes = false, bool instanciate = false)
+ {
+ builder.AppendLine(CodeGenerationHelpers.GetPropertyEquality(viewName, propertyName, value, inQuotes, instanciate));
+ }
+
+ public static void WriteTranslatedEquality(this StringBuilder builder, string viewName, string propertyName, FigmaText value, ICodeRenderService codeRenderService, bool instanciate = false)
+ {
+ WriteTranslatedEquality(builder, viewName, propertyName, value.characters, codeRenderService, instanciate, value.visible);
+ }
+
+ public static void WriteTranslatedEquality(this StringBuilder builder, string viewName, string propertyName, string value, ICodeRenderService codeRenderService, bool instanciate = false, bool textCondition = true)
+ {
+ bool needQuotes;
+ string result;
+ if (textCondition && !string.IsNullOrEmpty(value))
+ {
+ if (codeRenderService.Options != null && codeRenderService.Options.TranslateLabels)
+ {
+ result = codeRenderService.GetTranslatedText(value);
+ needQuotes = false;
+ }
+ else
+ {
+ result = value;
+ needQuotes = true;
+ }
+ }
+ else
+ {
+ result = string.Empty;
+ needQuotes = true;
+ }
+ builder.AppendLine(CodeGenerationHelpers.GetPropertyEquality(viewName, propertyName, result, inQuotes: needQuotes, instanciate: instanciate));
+ }
+
+ public static void WriteMethod(this StringBuilder builder, string viewName, string methodName, Enum parameter)
+ {
+ WriteMethod(builder, viewName, methodName, parameter.GetFullName());
+ }
+
+ public static void WriteMethod(this StringBuilder builder, string viewName, string methodName, bool parameter)
+ {
+ WriteMethod(builder, viewName, methodName, parameter.ToString());
+ }
+
+ public static void WriteMethod(this StringBuilder builder, string viewName, string methodName, string parameters, bool inQuotes = false)
+ {
+ builder.AppendLine(CodeGenerationHelpers.GetMethod(viewName, methodName, parameters, inQuotes));
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Figma/Extensions/FigmaExtensions.cs b/src/AlohaKit.UI.Figma/Figma/Extensions/FigmaExtensions.cs
new file mode 100644
index 0000000..76c7e68
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Figma/Extensions/FigmaExtensions.cs
@@ -0,0 +1,136 @@
+using FigmaSharp.Models;
+using System.Text;
+
+namespace AlohaKit.UI.Figma.Extensions
+{
+ public static class FigmaExtensions
+ {
+ public static string ToCodeString(this FigmaSharp.Color color)
+ {
+ int red = Convert.ToInt32(color.R * 255);
+ int green = Convert.ToInt32(color.G * 255);
+ int blue = Convert.ToInt32(color.B * 255);
+
+ return "#" + red.ToString("X2") + green.ToString("X2") + blue.ToString("X2");
+ }
+
+ public static string ToLinearGradientPaint(this ColorStop[] colorStops)
+ {
+ StringBuilder builder = new StringBuilder();
+
+ builder.Append("\t\t");
+ builder.AppendLine("\n\t\t\t");
+
+ foreach (var colorStop in colorStops)
+ {
+ var color = colorStop.color;
+ string hexColor = color.ToCodeString();
+
+ builder.AppendLine($"\t\t\t\t");
+ }
+
+ builder.AppendLine("\t\t\t");
+
+ builder.Append("\t\t");
+
+ return builder.ToString();
+ }
+
+ public static string ToRadialGradientPaint(this ColorStop[] colorStops)
+ {
+ StringBuilder builder = new StringBuilder();
+
+ builder.Append("\t\t");
+ builder.AppendLine("\n\t\t\t");
+
+ foreach (var colorStop in colorStops)
+ {
+ var color = colorStop.color;
+ string hexColor = color.ToCodeString();
+
+ builder.AppendLine($"\t\t\t\t");
+ }
+
+ builder.AppendLine("\t\t\t");
+
+ builder.Append("\t\t");
+
+ return builder.ToString();
+ }
+
+ public static string ToCodeString(this float[] values)
+ {
+ StringBuilder builder = new StringBuilder();
+
+ builder.Append("new float[] {");
+ var separator = ",";
+ int i = 0;
+
+ foreach (var value in values)
+ {
+ builder.Append($"{ToCodeString(value)}{(i < values.Length ? separator : string.Empty)}");
+ i++;
+ }
+
+ builder.Append("}");
+
+ return builder.ToString();
+ }
+
+ public static string ToCodeString(this float value)
+ {
+ return string.Concat(value.ToString(), "f");
+ }
+
+ public static string ToCodeString(this double value)
+ {
+ return string.Concat(value.ToString(), "f");
+ }
+
+ public static string ToCodeString(this FigmaTypeStyle style)
+ {
+ var fontFamily = style.fontPostScriptName ?? style.fontFamily;
+ var fontWeight = style.fontWeight;
+
+ string fontStyleType = "FontStyleType.Normal";
+
+ if (!string.IsNullOrEmpty(fontFamily) && fontFamily.Contains("Italic", StringComparison.CurrentCultureIgnoreCase))
+ fontStyleType = "FontStyleType.Italic";
+
+ return $"new Microsoft.Maui.Graphics.Font(\"{fontFamily}\", {fontWeight}, {fontStyleType})";
+ }
+
+ public static string ToHorizontalAignment(this string value)
+ {
+ switch(value)
+ {
+ case "LEFT":
+ return "HorizontalAlignment.Left";
+ case "CENTER":
+ return "HorizontalAlignment.Center";
+ case "RIGHT":
+ return "HorizontalAlignment.Right";
+ case "SCALE":
+ return "HorizontalAlignment.Justified";
+ default:
+ return "HorizontalAlignment.Left";
+ }
+ }
+
+ public static string ToVerticalAlignment(this string value)
+ {
+ switch (value)
+ {
+ case "TOP":
+ return "VerticalAlignment.Top";
+ case "CENTER":
+ case "SCALE":
+ return "VerticalAlignment.Center";
+ case "BOTTOM":
+ return "VerticalAlignment.Bottom";
+ default:
+ return "VerticalAlignment.Top";
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Figma/FigmaApplication.cs b/src/AlohaKit.UI.Figma/Figma/FigmaApplication.cs
new file mode 100644
index 0000000..c113f62
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Figma/FigmaApplication.cs
@@ -0,0 +1,12 @@
+namespace AlohaKit.UI.Figma
+{
+ public class FigmaApplication
+ {
+ public static void Init(string token)
+ {
+ var applicationDelegate = new FigmaDelegate();
+
+ FigmaSharp.AppContext.Current.Configuration(applicationDelegate, token);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Figma/FigmaDelegate.cs b/src/AlohaKit.UI.Figma/Figma/FigmaDelegate.cs
new file mode 100644
index 0000000..ea1f7d5
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Figma/FigmaDelegate.cs
@@ -0,0 +1,77 @@
+using AlohaKit.UI.Figma.Converters;
+using FigmaSharp;
+using FigmaSharp.Converters;
+using FigmaSharp.PropertyConfigure;
+using FigmaSharp.Views;
+using System.Reflection;
+
+namespace AlohaKit.UI.Figma
+{
+ public class FigmaDelegate : IFigmaDelegate
+ {
+ public bool IsVerticalAxisFlipped => false;
+
+ public void BeginInvoke(Action handler)
+ {
+ throw new NotImplementedException();
+ }
+
+ public FigmaSharp.Views.IView CreateEmptyView()
+ {
+ throw new NotImplementedException();
+ }
+
+ public CodePropertyConfigureBase GetCodePropertyConfigure()
+ {
+ throw new NotImplementedException();
+ }
+
+ public NodeConverter[] GetFigmaConverters()
+ {
+ return new NodeConverter[]{
+ new ElipseConverter(),
+ new FrameConverter(),
+ new ImageConverter(),
+ new LineConverter(),
+ new PolygonConverter(),
+ new RectangleConverter(),
+ new TextConverter()
+ };
+ }
+
+ public FigmaSharp.Views.IImage GetImage(string url)
+ {
+ throw new NotImplementedException();
+ }
+
+ public FigmaSharp.Views.IImage GetImageFromFilePath(string filePath)
+ {
+ throw new NotImplementedException();
+ }
+
+ public FigmaSharp.Views.IImage GetImageFromManifest(Assembly assembly, string imageRef)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IImageView GetImageView(FigmaSharp.Views.IImage image)
+ {
+ throw new NotImplementedException();
+ }
+
+ public string GetManifestResource(Assembly assembly, string file)
+ {
+ throw new NotImplementedException();
+ }
+
+ public string GetSvgData(string url)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ViewPropertyConfigureBase GetViewPropertyConfigure()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/AlohaKit.UI.Figma/Figma/Helpers/CodeGenerationHelpers.cs b/src/AlohaKit.UI.Figma/Figma/Helpers/CodeGenerationHelpers.cs
new file mode 100644
index 0000000..141f7e6
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Figma/Helpers/CodeGenerationHelpers.cs
@@ -0,0 +1,65 @@
+using AlohaKit.UI.Figma.Extensions;
+
+namespace AlohaKit.UI.Figma.Helpers
+{
+ public static class CodeGenerationHelpers
+ {
+ public static string GetConstructor(string viewName, Type type, bool includesVar = true)
+ {
+ return GetConstructor(viewName, type.FullName, includesVar);
+ }
+
+ public static string GetConstructor(string viewName, string typeFullName, bool includesVar = true)
+ {
+ return $"{(includesVar ? "var " : string.Empty)}{viewName} = new {typeFullName}();";
+ }
+
+ public static string GetPropertyEquality(string viewName, string propertyName, Enum value)
+ {
+ return GetPropertyEquality(viewName, propertyName, value.GetFullName());
+ }
+
+ public static string GetPropertyEquality(string viewName, string propertyName, bool value)
+ {
+ return GetPropertyEquality(viewName, propertyName, value.ToString());
+ }
+
+ public static string GetPropertyEquality(string viewName, string propertyName, string value, bool inQuotes = false, bool instanciate = false)
+ {
+ string fullPropertyName;
+ if (string.IsNullOrEmpty(propertyName))
+ fullPropertyName = viewName;
+ else
+ fullPropertyName = $"{viewName}.{propertyName}";
+ return GetEquality(fullPropertyName, value, inQuotes, instanciate);
+ }
+
+ public static string GetEquality(string viewName, string value, bool inQuotes = false, bool instanciate = false)
+ {
+ if (inQuotes)
+ {
+ value = string.Format("\"{0}\"", value.Replace("\n", "\\n"));
+ }
+
+ var instanciateText = instanciate ? "var " : "";
+ return $"{instanciateText}{viewName} = {value};";
+ }
+
+ public static string GetMethod(string viewName, string methodName, Enum parameter)
+ {
+ return GetMethod(viewName, methodName, parameter.GetFullName());
+ }
+
+ public static string GetMethod(string viewName, string methodName, bool value)
+ {
+ return GetMethod(viewName, methodName, value.ToString());
+ }
+
+ public static string GetMethod(string viewName, string methodName, string parameters, bool inQuotes = false, bool includesSemicolon = true)
+ {
+ parameters = inQuotes ? $"\"{parameters}\"" : parameters;
+ var semicolon = includesSemicolon ? ";" : "";
+ return $"{viewName}.{methodName} ({parameters}){semicolon}";
+ }
+ }
+}
diff --git a/src/AlohaKit.UI.Figma/Figma/PropertyConfigure/CodePropertyConfigure.cs b/src/AlohaKit.UI.Figma/Figma/PropertyConfigure/CodePropertyConfigure.cs
new file mode 100644
index 0000000..5307c4d
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Figma/PropertyConfigure/CodePropertyConfigure.cs
@@ -0,0 +1,14 @@
+using FigmaSharp.Converters;
+using FigmaSharp.PropertyConfigure;
+using FigmaSharp.Services;
+
+namespace AlohaKit.UI.Figma.PropertyConfigure
+{
+ public class CodePropertyConfigure : CodePropertyConfigureBase
+ {
+ public override string ConvertToCode(string propertyName, CodeNode currentNode, CodeNode parentNode, NodeConverter converter, CodeRenderService rendererService)
+ {
+ return string.Empty;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/MainPage.xaml b/src/AlohaKit.UI.Figma/MainPage.xaml
new file mode 100644
index 0000000..056bb40
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/MainPage.xaml
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/MainPage.xaml.cs b/src/AlohaKit.UI.Figma/MainPage.xaml.cs
new file mode 100644
index 0000000..23e20db
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/MainPage.xaml.cs
@@ -0,0 +1,10 @@
+namespace AlohaKit.UI.Figma
+{
+ public partial class MainPage : ContentPage
+ {
+ public MainPage()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/MauiProgram.cs b/src/AlohaKit.UI.Figma/MauiProgram.cs
new file mode 100644
index 0000000..7fbc343
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/MauiProgram.cs
@@ -0,0 +1,25 @@
+using Microsoft.Extensions.Logging;
+
+namespace AlohaKit.UI.Figma
+{
+ public static class MauiProgram
+ {
+ public static MauiApp CreateMauiApp()
+ {
+ var builder = MauiApp.CreateBuilder();
+ builder
+ .UseMauiApp()
+ .ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
+ });
+
+#if DEBUG
+ builder.Logging.AddDebug();
+#endif
+
+ return builder.Build();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Platforms/Android/AndroidManifest.xml b/src/AlohaKit.UI.Figma/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 0000000..e9937ad
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Platforms/Android/MainActivity.cs b/src/AlohaKit.UI.Figma/Platforms/Android/MainActivity.cs
new file mode 100644
index 0000000..5d3470f
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/Android/MainActivity.cs
@@ -0,0 +1,11 @@
+using Android.App;
+using Android.Content.PM;
+using Android.OS;
+
+namespace AlohaKit.UI.Figma
+{
+ [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
+ public class MainActivity : MauiAppCompatActivity
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Platforms/Android/MainApplication.cs b/src/AlohaKit.UI.Figma/Platforms/Android/MainApplication.cs
new file mode 100644
index 0000000..a3a398d
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/Android/MainApplication.cs
@@ -0,0 +1,16 @@
+using Android.App;
+using Android.Runtime;
+
+namespace AlohaKit.UI.Figma
+{
+ [Application]
+ public class MainApplication : MauiApplication
+ {
+ public MainApplication(IntPtr handle, JniHandleOwnership ownership)
+ : base(handle, ownership)
+ {
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Platforms/Android/Resources/values/colors.xml b/src/AlohaKit.UI.Figma/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 0000000..c04d749
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Platforms/MacCatalyst/AppDelegate.cs b/src/AlohaKit.UI.Figma/Platforms/MacCatalyst/AppDelegate.cs
new file mode 100644
index 0000000..53c3e01
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/MacCatalyst/AppDelegate.cs
@@ -0,0 +1,10 @@
+using Foundation;
+
+namespace AlohaKit.UI.Figma
+{
+ [Register("AppDelegate")]
+ public class AppDelegate : MauiUIApplicationDelegate
+ {
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Platforms/MacCatalyst/FolderPicker.cs b/src/AlohaKit.UI.Figma/Platforms/MacCatalyst/FolderPicker.cs
new file mode 100644
index 0000000..5b84224
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/MacCatalyst/FolderPicker.cs
@@ -0,0 +1,83 @@
+using AlohaKit.UI.Figma.Services;
+using Foundation;
+using UIKit;
+
+namespace AlohaKit.UI.Figma
+{
+ public class FolderPicker : IFolderPicker
+ {
+ class PickerDelegate : UIDocumentPickerDelegate
+ {
+ public Action PickHandler { get; set; }
+
+ public override void WasCancelled(UIDocumentPickerViewController controller)
+ => PickHandler?.Invoke(null);
+
+ public override void DidPickDocument(UIDocumentPickerViewController controller, NSUrl[] urls)
+ => PickHandler?.Invoke(urls);
+
+ public override void DidPickDocument(UIDocumentPickerViewController controller, NSUrl url)
+ => PickHandler?.Invoke(new NSUrl[] { url });
+ }
+
+ static void GetFileResults(NSUrl[] urls, TaskCompletionSource tcs)
+ {
+ try
+ {
+ tcs.TrySetResult(urls?[0]?.ToString() ?? "");
+ }
+ catch (Exception ex)
+ {
+ tcs.TrySetException(ex);
+ }
+ }
+
+ public async Task PickFolder()
+ {
+ var allowedTypes = new string[]
+ {
+ "public.folder"
+ };
+
+ var picker = new UIDocumentPickerViewController(allowedTypes, UIDocumentPickerMode.Open);
+ var tcs = new TaskCompletionSource();
+
+ picker.Delegate = new PickerDelegate
+ {
+ PickHandler = urls => GetFileResults(urls, tcs)
+ };
+
+ if (picker.PresentationController != null)
+ {
+ picker.PresentationController.Delegate =
+ new UIPresentationControllerDelegate(() => GetFileResults(null, tcs));
+ }
+
+ var parentController = Platform.GetCurrentUIViewController();
+
+ parentController.PresentViewController(picker, true, null);
+
+ return await tcs.Task;
+ }
+
+ internal class UIPresentationControllerDelegate : UIAdaptivePresentationControllerDelegate
+ {
+ Action dismissHandler;
+
+ internal UIPresentationControllerDelegate(Action dismissHandler)
+ => this.dismissHandler = dismissHandler;
+
+ public override void DidDismiss(UIPresentationController presentationController)
+ {
+ dismissHandler?.Invoke();
+ dismissHandler = null;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ dismissHandler?.Invoke();
+ base.Dispose(disposing);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Platforms/MacCatalyst/Info.plist b/src/AlohaKit.UI.Figma/Platforms/MacCatalyst/Info.plist
new file mode 100644
index 0000000..c96dd0a
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/MacCatalyst/Info.plist
@@ -0,0 +1,30 @@
+
+
+
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/src/AlohaKit.UI.Figma/Platforms/MacCatalyst/Program.cs b/src/AlohaKit.UI.Figma/Platforms/MacCatalyst/Program.cs
new file mode 100644
index 0000000..90beebd
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/MacCatalyst/Program.cs
@@ -0,0 +1,16 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace AlohaKit.UI.Figma
+{
+ public class Program
+ {
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Platforms/Tizen/Main.cs b/src/AlohaKit.UI.Figma/Platforms/Tizen/Main.cs
new file mode 100644
index 0000000..208fe7e
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/Tizen/Main.cs
@@ -0,0 +1,17 @@
+using Microsoft.Maui;
+using Microsoft.Maui.Hosting;
+using System;
+
+namespace AlohaKit.UI.Figma
+{
+ internal class Program : MauiApplication
+ {
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+
+ static void Main(string[] args)
+ {
+ var app = new Program();
+ app.Run(args);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Platforms/Tizen/tizen-manifest.xml b/src/AlohaKit.UI.Figma/Platforms/Tizen/tizen-manifest.xml
new file mode 100644
index 0000000..95b3dc6
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/Tizen/tizen-manifest.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ maui-appicon-placeholder
+
+
+
+
+ http://tizen.org/privilege/internet
+
+
+
+
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Platforms/Windows/App.xaml b/src/AlohaKit.UI.Figma/Platforms/Windows/App.xaml
new file mode 100644
index 0000000..fefe883
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/Windows/App.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/src/AlohaKit.UI.Figma/Platforms/Windows/App.xaml.cs b/src/AlohaKit.UI.Figma/Platforms/Windows/App.xaml.cs
new file mode 100644
index 0000000..0ad5407
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/Windows/App.xaml.cs
@@ -0,0 +1,24 @@
+using Microsoft.UI.Xaml;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace AlohaKit.UI.Figma.WinUI
+{
+ ///
+ /// Provides application-specific behavior to supplement the default Application class.
+ ///
+ public partial class App : MauiWinUIApplication
+ {
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Platforms/Windows/FolderPicker.cs b/src/AlohaKit.UI.Figma/Platforms/Windows/FolderPicker.cs
new file mode 100644
index 0000000..99c6221
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/Windows/FolderPicker.cs
@@ -0,0 +1,26 @@
+using AlohaKit.UI.Figma.Services;
+using WindowsFolderPicker = Windows.Storage.Pickers.FolderPicker;
+
+namespace AlohaKit.UI.Figma
+{
+ public class FolderPicker : IFolderPicker
+ {
+ public async Task PickFolder()
+ {
+ var folderPicker = new WindowsFolderPicker();
+
+ // Might be needed to make it work on Windows 10
+ folderPicker.FileTypeFilter.Add("*");
+
+ // Get the current window's HWND by passing in the Window object
+ var hwnd = ((MauiWinUIWindow)App.Current.Windows[0].Handler.PlatformView).WindowHandle;
+
+ // Associate the HWND with the file picker
+ WinRT.Interop.InitializeWithWindow.Initialize(folderPicker, hwnd);
+
+ var result = await folderPicker.PickSingleFolderAsync();
+
+ return result?.Path;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Platforms/Windows/Package.appxmanifest b/src/AlohaKit.UI.Figma/Platforms/Windows/Package.appxmanifest
new file mode 100644
index 0000000..2bcb11e
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/Windows/Package.appxmanifest
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+ $placeholder$
+ User Name
+ $placeholder$.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AlohaKit.UI.Figma/Platforms/Windows/app.manifest b/src/AlohaKit.UI.Figma/Platforms/Windows/app.manifest
new file mode 100644
index 0000000..93cb078
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/Windows/app.manifest
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
diff --git a/src/AlohaKit.UI.Figma/Platforms/iOS/AppDelegate.cs b/src/AlohaKit.UI.Figma/Platforms/iOS/AppDelegate.cs
new file mode 100644
index 0000000..53c3e01
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/iOS/AppDelegate.cs
@@ -0,0 +1,10 @@
+using Foundation;
+
+namespace AlohaKit.UI.Figma
+{
+ [Register("AppDelegate")]
+ public class AppDelegate : MauiUIApplicationDelegate
+ {
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Platforms/iOS/Info.plist b/src/AlohaKit.UI.Figma/Platforms/iOS/Info.plist
new file mode 100644
index 0000000..0004a4f
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/iOS/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/src/AlohaKit.UI.Figma/Platforms/iOS/Program.cs b/src/AlohaKit.UI.Figma/Platforms/iOS/Program.cs
new file mode 100644
index 0000000..90beebd
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Platforms/iOS/Program.cs
@@ -0,0 +1,16 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace AlohaKit.UI.Figma
+{
+ public class Program
+ {
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Properties/launchSettings.json b/src/AlohaKit.UI.Figma/Properties/launchSettings.json
new file mode 100644
index 0000000..edf8aad
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Windows Machine": {
+ "commandName": "MsixPackage",
+ "nativeDebugging": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Resources/AppIcon/appicon.svg b/src/AlohaKit.UI.Figma/Resources/AppIcon/appicon.svg
new file mode 100644
index 0000000..9d63b65
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Resources/AppIcon/appicon.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Resources/AppIcon/appiconfg.svg b/src/AlohaKit.UI.Figma/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 0000000..21dfb25
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Resources/Fonts/OpenSans-Regular.ttf b/src/AlohaKit.UI.Figma/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 0000000..238424f
Binary files /dev/null and b/src/AlohaKit.UI.Figma/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/src/AlohaKit.UI.Figma/Resources/Fonts/OpenSans-Semibold.ttf b/src/AlohaKit.UI.Figma/Resources/Fonts/OpenSans-Semibold.ttf
new file mode 100644
index 0000000..0fd77c6
Binary files /dev/null and b/src/AlohaKit.UI.Figma/Resources/Fonts/OpenSans-Semibold.ttf differ
diff --git a/src/AlohaKit.UI.Figma/Resources/Images/dotnet_bot.svg b/src/AlohaKit.UI.Figma/Resources/Images/dotnet_bot.svg
new file mode 100644
index 0000000..abfaff2
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Resources/Images/dotnet_bot.svg
@@ -0,0 +1,93 @@
+
diff --git a/src/AlohaKit.UI.Figma/Resources/Raw/AboutAssets.txt b/src/AlohaKit.UI.Figma/Resources/Raw/AboutAssets.txt
new file mode 100644
index 0000000..15d6244
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,15 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories). Deployment of the asset to your application
+is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
+
+
+
+These files will be deployed with you package and will be accessible using Essentials:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
diff --git a/src/AlohaKit.UI.Figma/Resources/Splash/splash.svg b/src/AlohaKit.UI.Figma/Resources/Splash/splash.svg
new file mode 100644
index 0000000..21dfb25
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Resources/Splash/splash.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Resources/Styles/Colors.xaml b/src/AlohaKit.UI.Figma/Resources/Styles/Colors.xaml
new file mode 100644
index 0000000..245758b
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Resources/Styles/Colors.xaml
@@ -0,0 +1,44 @@
+
+
+
+
+ #512BD4
+ #DFD8F7
+ #2B0B98
+ White
+ Black
+ #E1E1E1
+ #C8C8C8
+ #ACACAC
+ #919191
+ #6E6E6E
+ #404040
+ #212121
+ #141414
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #F7B548
+ #FFD590
+ #FFE5B9
+ #28C2D1
+ #7BDDEF
+ #C3F2F4
+ #3E8EED
+ #72ACF1
+ #A7CBF6
+
+
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Resources/Styles/Styles.xaml b/src/AlohaKit.UI.Figma/Resources/Styles/Styles.xaml
new file mode 100644
index 0000000..56eab87
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Resources/Styles/Styles.xaml
@@ -0,0 +1,403 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AlohaKit.UI.Figma/Services/DialogService.cs b/src/AlohaKit.UI.Figma/Services/DialogService.cs
new file mode 100644
index 0000000..3862f98
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Services/DialogService.cs
@@ -0,0 +1,28 @@
+namespace AlohaKit.UI.Figma.Services
+{
+ public class DialogService
+ {
+ static DialogService _instance;
+
+ public static DialogService Instance
+ {
+ get
+ {
+ if (_instance == null)
+ _instance = new DialogService();
+
+ return _instance;
+ }
+ }
+
+ public void DisplayAlert(string title, string message)
+ {
+ var mainPage = Application.Current.MainPage;
+
+ if (mainPage == null)
+ return;
+
+ mainPage.DisplayAlert(title, message, "Ok");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.Figma/Services/IFolderPicker.cs b/src/AlohaKit.UI.Figma/Services/IFolderPicker.cs
new file mode 100644
index 0000000..100043b
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/Services/IFolderPicker.cs
@@ -0,0 +1,7 @@
+namespace AlohaKit.UI.Figma.Services
+{
+ public interface IFolderPicker
+ {
+ Task PickFolder();
+ }
+}
diff --git a/src/AlohaKit.UI.Figma/ViewModels/MainViewModel.cs b/src/AlohaKit.UI.Figma/ViewModels/MainViewModel.cs
new file mode 100644
index 0000000..796f5eb
--- /dev/null
+++ b/src/AlohaKit.UI.Figma/ViewModels/MainViewModel.cs
@@ -0,0 +1,179 @@
+using AlohaKit.UI.Figma.PropertyConfigure;
+using AlohaKit.UI.Figma.Services;
+using FigmaSharp.Services;
+using System.Collections.ObjectModel;
+using System.Text;
+using System.Windows.Input;
+
+namespace AlohaKit.UI.Figma.ViewModels
+{
+ public class MainViewModel : BindableObject
+ {
+ string _token;
+ string _fileId;
+
+ bool _isGenerating;
+ string _code;
+ ObservableCollection _log;
+
+ public MainViewModel()
+ {
+#if DEBUG
+ // INSERT YOUR FIGMA ACCESS TOKEN
+ Token = "";
+ // INSERT THE FILE ID
+ FileId = "";
+#endif
+ Log = new ObservableCollection();
+ }
+
+ public string Token
+ {
+ get { return _token; }
+ set
+ {
+ _token = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public string FileId
+ {
+ get { return _fileId; }
+ set
+ {
+ _fileId = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public bool IsGenerating
+ {
+ get { return _isGenerating; }
+ set
+ {
+ _isGenerating = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public string Code
+ {
+ get { return _code; }
+ set
+ {
+ _code = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public ObservableCollection Log
+ {
+ get { return _log; }
+ set
+ {
+ _log = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public ICommand GenerateCommand => new Command(async () => await GenerateCodeAsync());
+ public ICommand ExportCommand => new Command(async () => await Export());
+
+ async Task GenerateCodeAsync()
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(Token))
+ {
+ var message = "In order to obtain the necessary information from Figma, it is necessary to use a Personal Access Token.";
+ Log.Add(message);
+ DialogService.Instance.DisplayAlert("Information", message);
+ return;
+ }
+
+ FigmaApplication.Init(Token);
+ if (string.IsNullOrEmpty(FileId))
+ {
+ var message = "In order to obtain the necessary information from Figma, it is necessary to use a FileId.";
+ Log.Add(message);
+ DialogService.Instance.DisplayAlert("Information", message);
+ return;
+ }
+ IsGenerating = true;
+ Log.Add("Request the data to the Figma API.");
+
+ var remoteNodeProvider = new RemoteNodeProvider();
+ await remoteNodeProvider.LoadAsync(FileId);
+
+ Log.Add($"Data obtained successfully. {remoteNodeProvider.Nodes.Count} nodes found.");
+
+ Log.Add("Initializing the code generator.");
+
+ var converters = FigmaSharp.AppContext.Current.GetFigmaConverters();
+ var codePropertyConfigure = new CodePropertyConfigure();
+ var codeRenderer = new CodeRenderService(remoteNodeProvider, converters, codePropertyConfigure);
+
+ Log.Add("Code generator initialized successfully.");
+
+ var stringBuilder = new StringBuilder();
+
+ var node = codeRenderer.NodeProvider.Nodes[0];
+
+ Log.Add($"Node {node.id} found successfully.");
+
+ Log.Add("Generating source code...");
+
+ var codeNode = new CodeNode(node);
+
+ codeRenderer.GetCode(stringBuilder, codeNode, currentRendererOptions: new CodeRenderServiceOptions
+ {
+ ShowComments = false
+ });
+
+ var code = stringBuilder.ToString();
+
+ Code = code;
+
+ Log.Add("Source Code generated successfully.");
+ }
+ catch (Exception ex)
+ {
+ Log.Add(ex.Message);
+ }
+ finally
+ {
+ IsGenerating = false;
+ }
+ }
+
+ async Task Export()
+ {
+ if (string.IsNullOrEmpty(Code))
+ {
+ string message = "The generated code is not correct.";
+ Log.Add(message);
+ DialogService.Instance.DisplayAlert("Information", message);
+ return;
+ }
+
+#if MACCATALYST || WINDOWS
+ try
+ {
+ var folderPicker = new FolderPicker();
+ string folder = await folderPicker.PickFolder();
+ string path = System.IO.Path.Combine(folder, "FigmaToMauiGraphics.txt");
+ await File.WriteAllTextAsync(path, Code);
+
+ string message = "The file has been created successfully.";
+ Log.Add(message);
+ DialogService.Instance.DisplayAlert("Information", message);
+ }
+ catch(Exception ex)
+ {
+ Log.Add(ex.Message);
+ }
+#endif
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AlohaKit.UI.sln b/src/AlohaKit.UI.sln
index c776b88..1f6707f 100644
--- a/src/AlohaKit.UI.sln
+++ b/src/AlohaKit.UI.sln
@@ -3,9 +3,17 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33015.44
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlohaKit.UI", "AlohaKit.UI\AlohaKit.UI.csproj", "{0E6FAD63-6F1A-4CA7-A16F-3F0765DC95A6}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlohaKit.UI", "AlohaKit.UI\AlohaKit.UI.csproj", "{0E6FAD63-6F1A-4CA7-A16F-3F0765DC95A6}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlohaKit.UI.Gallery", "AlohaKit.UI.Gallery\AlohaKit.UI.Gallery.csproj", "{09500699-E9A5-40B9-B72E-00E6B2CD4B38}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlohaKit.UI.Gallery", "AlohaKit.UI.Gallery\AlohaKit.UI.Gallery.csproj", "{09500699-E9A5-40B9-B72E-00E6B2CD4B38}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlohaKit.UI.Figma", "AlohaKit.UI.Figma\AlohaKit.UI.Figma.csproj", "{9BC3D8C4-A21E-4F4E-BAC1-DB24CC81CCEF}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Figma", "Figma", "{530D3C98-F65E-4631-8FF6-0E5120568BB5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FigmaSharp", "FigmaSharp\FigmaSharp.csproj", "{06D8C80F-2794-47A7-BA8A-5BEDDFC9D5AA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FigmaSharp.Views", "FigmaSharp.Views\FigmaSharp.Views.csproj", "{47B4F2A7-6F3F-4AF3-B48D-FF2AF74A4E0B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -23,10 +31,29 @@ Global
{09500699-E9A5-40B9-B72E-00E6B2CD4B38}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09500699-E9A5-40B9-B72E-00E6B2CD4B38}.Release|Any CPU.Build.0 = Release|Any CPU
{09500699-E9A5-40B9-B72E-00E6B2CD4B38}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {9BC3D8C4-A21E-4F4E-BAC1-DB24CC81CCEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9BC3D8C4-A21E-4F4E-BAC1-DB24CC81CCEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9BC3D8C4-A21E-4F4E-BAC1-DB24CC81CCEF}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {9BC3D8C4-A21E-4F4E-BAC1-DB24CC81CCEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9BC3D8C4-A21E-4F4E-BAC1-DB24CC81CCEF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9BC3D8C4-A21E-4F4E-BAC1-DB24CC81CCEF}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {06D8C80F-2794-47A7-BA8A-5BEDDFC9D5AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {06D8C80F-2794-47A7-BA8A-5BEDDFC9D5AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {06D8C80F-2794-47A7-BA8A-5BEDDFC9D5AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {06D8C80F-2794-47A7-BA8A-5BEDDFC9D5AA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {47B4F2A7-6F3F-4AF3-B48D-FF2AF74A4E0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {47B4F2A7-6F3F-4AF3-B48D-FF2AF74A4E0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {47B4F2A7-6F3F-4AF3-B48D-FF2AF74A4E0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {47B4F2A7-6F3F-4AF3-B48D-FF2AF74A4E0B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {9BC3D8C4-A21E-4F4E-BAC1-DB24CC81CCEF} = {530D3C98-F65E-4631-8FF6-0E5120568BB5}
+ {06D8C80F-2794-47A7-BA8A-5BEDDFC9D5AA} = {530D3C98-F65E-4631-8FF6-0E5120568BB5}
+ {47B4F2A7-6F3F-4AF3-B48D-FF2AF74A4E0B} = {530D3C98-F65E-4631-8FF6-0E5120568BB5}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4704EC8E-DEA6-47CF-95CE-896B3E8949D1}
EndGlobalSection
diff --git a/src/AlohaKit.UI/Controls/Element.cs b/src/AlohaKit.UI/Controls/Element.cs
index 7615922..cd9ea4a 100644
--- a/src/AlohaKit.UI/Controls/Element.cs
+++ b/src/AlohaKit.UI/Controls/Element.cs
@@ -12,7 +12,9 @@
public class Element : VisualElement, IElement
{
IElement _parent;
+ RectF _childrenBounds;
+
public Element()
{
Children = new ElementsCollection(this);
diff --git a/src/AlohaKit.UI/Controls/Path.cs b/src/AlohaKit.UI/Controls/Path.cs
index 67df750..d0b572a 100644
--- a/src/AlohaKit.UI/Controls/Path.cs
+++ b/src/AlohaKit.UI/Controls/Path.cs
@@ -18,12 +18,12 @@ namespace AlohaKit.UI
public override void Draw(ICanvas canvas, RectF bounds)
{
- canvas.SaveState();
-
base.Draw(canvas, bounds);
if (Stroke != null)
{
+ canvas.SaveState();
+
canvas.Translate(X, Y);
if (Stroke is SolidColorBrush solidColorBrush)
@@ -39,9 +39,27 @@ namespace AlohaKit.UI
var path = GetPath();
canvas.DrawPath(path);
+
+ canvas.RestoreState();
}
- canvas.RestoreState();
+ if (Fill != null)
+ {
+ canvas.SaveState();
+
+ canvas.Translate(X, Y);
+
+ if (Fill is SolidColorBrush solidColorBrush)
+ canvas.FillColor = solidColorBrush.Color;
+ else
+ canvas.SetFillPaint(Fill, new RectF(X, Y, WidthRequest, HeightRequest));
+
+ var path = GetPath();
+
+ canvas.FillPath(path);
+
+ canvas.RestoreState();
+ }
}
PathF GetPath()
diff --git a/src/FigmaSharp.Views/AnchorStyles.cs b/src/FigmaSharp.Views/AnchorStyles.cs
new file mode 100644
index 0000000..2874ada
--- /dev/null
+++ b/src/FigmaSharp.Views/AnchorStyles.cs
@@ -0,0 +1,53 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+
+namespace FigmaSharp
+{
+ [Flags]
+ public enum AnchorStyles
+ {
+ //
+ // Summary:
+ // The control is not anchored to any edges of its container.
+ None = 0,
+ //
+ // Summary:
+ // The control is anchored to the top edge of its container.
+ Top = 1,
+ //
+ // Summary:
+ // The control is anchored to the bottom edge of its container.
+ Bottom = 2,
+ //
+ // Summary:
+ // The control is anchored to the left edge of its container.
+ Left = 4,
+ //
+ // Summary:
+ // The control is anchored to the right edge of its container.
+ Right = 8
+ }
+}
diff --git a/src/FigmaSharp.Views/Color.cs b/src/FigmaSharp.Views/Color.cs
new file mode 100644
index 0000000..c5e7617
--- /dev/null
+++ b/src/FigmaSharp.Views/Color.cs
@@ -0,0 +1,59 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp
+{
+ public class Color
+ {
+ public Color () { }
+ public Color (double r, double g, double b, double a)
+ {
+ R = r;
+ G = g;
+ B = b;
+ A = a;
+ }
+
+ public Color (double r, double g, double b)
+ {
+ R = r;
+ G = g;
+ B = b;
+ A = 1;
+ }
+
+ public double R { get; set; }
+ public double G { get; set; }
+ public double B { get; set; }
+ public double A { get; set; }
+
+
+ public static Color Red => new Color (1, 0, 0, 1);
+ public static Color Green => new Color (0, 1, 0, 1);
+ public static Color Blue => new Color (0, 0, 1, 1);
+ public static Color Black => new Color (0, 0, 0, 1);
+ public static Color White => new Color (1, 1, 1, 1);
+ public static Color Transparent => new Color (0, 0, 0, 0);
+ }
+}
diff --git a/src/FigmaSharp.Views/Extensions.cs b/src/FigmaSharp.Views/Extensions.cs
new file mode 100644
index 0000000..610d0a8
--- /dev/null
+++ b/src/FigmaSharp.Views/Extensions.cs
@@ -0,0 +1,34 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System.Linq;
+
+namespace FigmaSharp.Views
+{
+ public static class Extensions
+ {
+ public static bool In (this string sender, params string[] values) =>
+ values.Any (s => s == sender);
+ }
+}
diff --git a/src/FigmaSharp.Views/FigmaColor.cs b/src/FigmaSharp.Views/FigmaColor.cs
new file mode 100644
index 0000000..7cd5023
--- /dev/null
+++ b/src/FigmaSharp.Views/FigmaColor.cs
@@ -0,0 +1,222 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+
+namespace FigmaSharp
+{
+ public class Point : IEquatable
+ {
+ public Point () { }
+ public Point (float x, float y)
+ {
+ X = x;
+ Y = y;
+ }
+
+ public float X { get; set; }
+ public float Y { get; set; }
+
+ public bool Equals (Point other)
+ {
+ if (X != other.X || Y != other.Y) {
+ return false;
+ }
+ return true;
+ }
+
+ public override bool Equals (object obj)
+ {
+ if (obj == null) return false;
+ if (!(obj is Size)) return false;
+ return Equals ((Size)obj);
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("{{{0},{1}}}", X, Y);
+ }
+
+ public static Point Zero { get; set; } = new Point ();
+
+ public Point Substract(Point center)
+ {
+ return new Point(X - center.X, Y - center.Y);
+ }
+
+ public Point UnionWith (Point center)
+ {
+ return new Point(X + center.X, Y + center.Y);
+ }
+ }
+
+ public class Size : IEquatable
+ {
+ public Size () { }
+ public Size (float width, float height)
+ {
+ Width = width;
+ Height = height;
+ }
+
+ public float Width { get; set; }
+ public float Height { get; set; }
+
+ public bool Equals (Size other)
+ {
+ if (Width != other.Width || Height != other.Height) {
+ return false;
+ }
+ return true;
+ }
+
+ public override bool Equals (object obj)
+ {
+ if (obj == null) return false;
+ if (!(obj is Size)) return false;
+ return Equals ((Size)obj);
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("{{{0},{1}}}", Width, Height);
+ }
+
+ public static Size Zero { get; set; } = new Size ();
+ }
+
+ public class Rectangle : IEquatable
+ {
+
+ readonly Size size = new Size ();
+ public Size Size {
+ get => size;
+ set {
+ size.Width = value.Width;
+ size.Height = value.Height;
+ }
+ }
+
+ readonly Point origin = new Point ();
+ public Point Origin {
+ get => origin;
+ set {
+ origin.X = value.X;
+ origin.Y = value.Y;
+ }
+ }
+
+ public float X {
+ get => origin.X;
+ set => origin.X = value;
+ }
+
+ public float Y {
+ get => origin.Y;
+ set => origin.Y = value;
+ }
+
+ public float Width {
+ get => size.Width;
+ set => size.Width = value;
+ }
+
+ public float Height {
+ get => size.Height;
+ set => size.Height = value;
+ }
+
+ public float Left => X;
+ public float Right => X + Width;
+ public float Top => Y;
+ public float Bottom => Y + Height;
+
+ public static Rectangle Zero { get; set; } = new Rectangle ();
+
+ public Rectangle ()
+ {
+
+ }
+
+ public Rectangle (Point origin, Size size)
+ {
+ this.origin = origin;
+ this.size = size;
+ }
+
+ public Rectangle (float x, float y, float width, float height)
+ {
+ this.X = x;
+ this.Y = y;
+ this.Width = width;
+ this.Height = height;
+ }
+
+ public Rectangle UnionWith (Rectangle allocation)
+ {
+ //TODO: improve
+ float xMin = Math.Min (X, allocation.X);
+ float yMin = Math.Min (Y, allocation.Y);
+ float xMax = Math.Max (X + Width, allocation.X + allocation.Width);
+ float yMax = Math.Max (Y + Height, allocation.Y + allocation.Height);
+ return new Rectangle (xMin, yMin, xMax - xMin, yMax - yMin);
+ }
+
+ public bool Equals (Rectangle other)
+ {
+ if (size.Equals (other.Size) && origin.Equals (other.Origin)) {
+ return true;
+ }
+ return false;
+ }
+
+ public override bool Equals (object obj)
+ {
+ if (obj == null) return false;
+ if (!(obj is Rectangle)) return false;
+ return Equals ((Rectangle)obj);
+ }
+
+ public bool IntersectsWith (Rectangle allocation)
+ {
+ return (Left < allocation.Right && Right > allocation.Left &&
+ Top < allocation.Bottom && Bottom > allocation.Top);
+ }
+
+ public bool IntersectsWith (Rectangle allocation, bool reversed)
+ {
+ return (Left < allocation.Right && Right > allocation.Left &&
+ Top <= allocation.Bottom && Bottom >= allocation.Top);
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("{{{0},{1},{2},{3}}}", X, Y, Width, Height);
+ }
+
+ public Point Center => new Point (Width / 2f, Height / 2f);
+
+ public Rectangle Copy() => new Rectangle(X, Y, Width, Height);
+ }
+}
diff --git a/src/FigmaSharp.Views/FigmaSharp.Views.csproj b/src/FigmaSharp.Views/FigmaSharp.Views.csproj
new file mode 100644
index 0000000..c2672dc
--- /dev/null
+++ b/src/FigmaSharp.Views/FigmaSharp.Views.csproj
@@ -0,0 +1,15 @@
+
+
+
+ net6.0;net6.0-android;net6.0-ios;net6.0-maccatalyst
+ $(TargetFrameworks);net6.0-windows10.0.19041.0
+
+
+
+
+
+
+
+
+
+
diff --git a/src/FigmaSharp.Views/FileHelper.cs b/src/FigmaSharp.Views/FileHelper.cs
new file mode 100644
index 0000000..21752f2
--- /dev/null
+++ b/src/FigmaSharp.Views/FileHelper.cs
@@ -0,0 +1,77 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.IO;
+using System.Reflection;
+
+namespace FigmaSharp.Views.Helpers
+{
+ public static class FileHelper
+ {
+ public static string GetFileDataFromBundle (string fileName, Assembly assembly = null)
+ {
+ if (assembly == null)
+ assembly = Assembly.GetEntryAssembly();
+
+ var path = System.IO.Path.GetDirectoryName(System.IO.Path.GetDirectoryName(assembly.Location));
+ path = System.IO.Path.Combine(path, "Resources", "svg", fileName);
+ var data = System.IO.File.ReadAllText(path);
+ return data;
+ }
+
+ public static string GetManifestResource(Assembly assembly, string resource)
+ {
+ if (assembly == null)
+ {
+ assembly = Assembly.GetEntryAssembly();
+ }
+ try
+ {
+ var resources = assembly.GetManifestResourceNames();
+ var fullResourceName = string.Format("{0}.{1}", assembly.GetName().Name, resource);
+
+ foreach (var item in new string[] { resource, fullResourceName })
+ {
+ //TODO: not safe
+ using (var stream = assembly.GetManifestResourceStream(item))
+ {
+ if (stream == null)
+ continue;
+ using (TextReader tr = new StreamReader(stream))
+ return tr.ReadToEnd();
+ }
+ }
+ }
+ catch (System.Exception ex)
+ {
+ Console.WriteLine("Cannot read resource '{0}' in assembly '{1}'", resource, ex);
+ return null;
+ }
+
+ Console.WriteLine("Resource '{0}' not found in assembly '{1}'", resource, assembly.FullName); ;
+ return null;
+ }
+ }
+}
diff --git a/src/FigmaSharp.Views/ITransitionButton.cs b/src/FigmaSharp.Views/ITransitionButton.cs
new file mode 100644
index 0000000..64ecdff
--- /dev/null
+++ b/src/FigmaSharp.Views/ITransitionButton.cs
@@ -0,0 +1,39 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Views
+{
+ public interface ITransitableButton : IButton, IViewTransitable
+ {
+
+ }
+
+ public interface IViewTransitable
+ {
+ string TransitionNodeID { get; set; }
+ float TransitionDuration { get; set; }
+ string TransitionEasing { get; set; }
+ }
+}
+
diff --git a/src/FigmaSharp.Views/Interfaces/IButton.cs b/src/FigmaSharp.Views/Interfaces/IButton.cs
new file mode 100644
index 0000000..701af53
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/IButton.cs
@@ -0,0 +1,41 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+
+namespace FigmaSharp.Views
+{
+ public interface IImageButton : IButton
+ {
+
+ }
+
+ public interface IButton : IView
+ {
+ bool Border { get; set; }
+ event EventHandler Clicked;
+ string Text { get; set; }
+ IImage Image { get; set; }
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/ICheckBox.cs b/src/FigmaSharp.Views/Interfaces/ICheckBox.cs
new file mode 100644
index 0000000..d05593d
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/ICheckBox.cs
@@ -0,0 +1,38 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Views
+{
+ public interface IRadioBox : IView
+ {
+ bool IsChecked { get; set; }
+ string Text { get; set; }
+ }
+
+ public interface ICheckBox : IView
+ {
+ bool IsChecked { get; set; }
+ string Text { get; set; }
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/IComboBox.cs b/src/FigmaSharp.Views/Interfaces/IComboBox.cs
new file mode 100644
index 0000000..934463a
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/IComboBox.cs
@@ -0,0 +1,32 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Views
+{
+ public interface IComboBox
+ {
+ void AddItem (string Text);
+ void RemoveItem (string Text);
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/IDeviceWindow.cs b/src/FigmaSharp.Views/Interfaces/IDeviceWindow.cs
new file mode 100644
index 0000000..2e99ce7
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/IDeviceWindow.cs
@@ -0,0 +1,38 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+
+namespace FigmaSharp.Views
+{
+ public interface IDeviceWindow : IObject, IDisposable
+ {
+ Color BackgroundColor { get; set; }
+ IView Content { get; set; }
+
+ void AddChild (IWindow window);
+ void RemoveChild (IWindow window);
+ }
+}
+
diff --git a/src/FigmaSharp.Views/Interfaces/IHorizontalBar.cs b/src/FigmaSharp.Views/Interfaces/IHorizontalBar.cs
new file mode 100644
index 0000000..5643af2
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/IHorizontalBar.cs
@@ -0,0 +1,31 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Views
+{
+ public interface IHorizontalBar : IView
+ {
+
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/IImage.cs b/src/FigmaSharp.Views/Interfaces/IImage.cs
new file mode 100644
index 0000000..dcc8c37
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/IImage.cs
@@ -0,0 +1,31 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Views
+{
+ public interface IImage : IObject
+ {
+ Size Size { get; set; }
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/IImageView.cs b/src/FigmaSharp.Views/Interfaces/IImageView.cs
new file mode 100644
index 0000000..712f043
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/IImageView.cs
@@ -0,0 +1,36 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Views
+{
+ public interface IImageView : IView
+ {
+ IImage Image { get; set; }
+ }
+
+ public interface ISvgView : IView
+ {
+ void Load(string image);
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/ILabel.cs b/src/FigmaSharp.Views/Interfaces/ILabel.cs
new file mode 100644
index 0000000..5d8c7fd
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/ILabel.cs
@@ -0,0 +1,33 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Views
+{
+ public interface ILabel : IView
+ {
+ string Text { get; set; }
+
+ Color ForegroundColor { get; set; }
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/IObject.cs b/src/FigmaSharp.Views/Interfaces/IObject.cs
new file mode 100644
index 0000000..7d97788
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/IObject.cs
@@ -0,0 +1,33 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+
+namespace FigmaSharp.Views
+{
+ public interface IObject : IDisposable
+ {
+ object NativeObject { get; }
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/IScrollView.cs b/src/FigmaSharp.Views/Interfaces/IScrollView.cs
new file mode 100644
index 0000000..0e92939
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/IScrollView.cs
@@ -0,0 +1,38 @@
+/*
+ * FigmaImageView.cs - NSImageView which stores it's associed Figma Id
+ *
+ * Author:
+ * Jose Medrano
+ *
+ * Copyright (C) 2018 Microsoft, Corp
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+namespace FigmaSharp.Views
+{
+ public interface IScrollView : IView
+ {
+ void AdjustToContent ();
+ void SetContentSize (float width, float height);
+
+ IView ContentView { get; set; }
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/ISearchBox.cs b/src/FigmaSharp.Views/Interfaces/ISearchBox.cs
new file mode 100644
index 0000000..c7711f1
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/ISearchBox.cs
@@ -0,0 +1,31 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Views
+{
+ public interface ISearchBox : ITextBox, IView
+ {
+
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/ISlider.cs b/src/FigmaSharp.Views/Interfaces/ISlider.cs
new file mode 100644
index 0000000..84160d3
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/ISlider.cs
@@ -0,0 +1,31 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Views
+{
+ public interface ISlider : IView
+ {
+
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/ISpinner.cs b/src/FigmaSharp.Views/Interfaces/ISpinner.cs
new file mode 100644
index 0000000..5a0efa5
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/ISpinner.cs
@@ -0,0 +1,44 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Views
+{
+ public interface IDisclosureTriangle : IButton
+ {
+ }
+
+ public interface IStepper : IView
+ {
+ }
+
+ public interface IProgressBar : IView
+ {
+ }
+
+ public interface ISpinner : IView
+ {
+ void Start ();
+ void Stop ();
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/IStackView.cs b/src/FigmaSharp.Views/Interfaces/IStackView.cs
new file mode 100644
index 0000000..cec0743
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/IStackView.cs
@@ -0,0 +1,37 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Views
+{
+ public enum LayoutOrientation
+ {
+ Horizontal,
+ Vertical
+ }
+
+ public interface IStackView : IView
+ {
+ LayoutOrientation Orientation { get; set; }
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/ITextBox.cs b/src/FigmaSharp.Views/Interfaces/ITextBox.cs
new file mode 100644
index 0000000..a1b811a
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/ITextBox.cs
@@ -0,0 +1,50 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Views
+{
+ public enum TextAlignment
+ {
+ /// Indicates that text will be aligned to the left or top of horizontally or vertically aligned text, respectively.
+ Start,
+ /// Indicates that text will be aligned in the middle of either horizontally or vertically aligned text.
+ Center,
+ /// Indicates that text will be aligned to the right or bottom of horizontally or vertically aligned text, respectively.
+ End
+ }
+
+ public interface ITextBox : IView
+ {
+ string Text { get; set; }
+ string PlaceHolderString { get; set; }
+
+ Color ForegroundColor { get; set; }
+ }
+
+ public interface ITextView : IView
+ {
+ //string Text { get; set; }
+ //Color ForegroundColor { get; set; }
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/ITransform.cs b/src/FigmaSharp.Views/Interfaces/ITransform.cs
new file mode 100644
index 0000000..84a39ab
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/ITransform.cs
@@ -0,0 +1,34 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Views
+{
+ public interface ITransform : IObject
+ {
+ float RotateAngle { get; set; }
+ void Rotate (float angle);
+ void Scale (float x, float y);
+ void Translate (float x, float y);
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/IView.cs b/src/FigmaSharp.Views/Interfaces/IView.cs
new file mode 100644
index 0000000..7d9ca28
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/IView.cs
@@ -0,0 +1,67 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System.Collections.Generic;
+
+namespace FigmaSharp.Views
+{
+ public interface IView : IObject
+ {
+ float Width { get; set; }
+ float Height { get; set; }
+
+ bool Hidden { get; set; }
+
+ string Identifier { get; set; }
+ string NodeName { get; set; }
+
+ float CornerRadius { get; set; }
+
+ bool MovableByWindowBackground { get; set; }
+
+ Size Size { get; set; }
+ Rectangle Allocation { get; }
+ Size IntrinsicContentSize { get; }
+
+ IView Parent { get; set; }
+ Color BackgroundColor { get; set; }
+ AnchorStyles Anchor { get; set; }
+
+ IReadOnlyList Children { get; }
+
+ void Focus ();
+ void SetPosition (float x, float y);
+ void SetAllocation (float x, float y, float width, float height);
+ void SetAlignmentRect (float x, float y, float width, float height);
+
+ void SetPosition (Point origin);
+ void SetAllocation (Point origin, Size size);
+
+ void AddChild (IView view);
+ void RemoveChild (IView view);
+ void ClearSubviews ();
+
+ void OnChangeFrameSize (Size newSize);
+ }
+}
diff --git a/src/FigmaSharp.Views/Interfaces/IWindow.cs b/src/FigmaSharp.Views/Interfaces/IWindow.cs
new file mode 100644
index 0000000..5dca56b
--- /dev/null
+++ b/src/FigmaSharp.Views/Interfaces/IWindow.cs
@@ -0,0 +1,56 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+
+namespace FigmaSharp.Views
+{
+ public interface IWindow : IObject, IDisposable
+ {
+ string Title { get; set; }
+ Color BackgroundColor { get; set; }
+ bool Borderless { get; set; }
+ bool Resizable { get; set; }
+ bool IsFullSizeContentView { get; set; }
+ bool IsClosable { get; set; }
+ Size Size { get; set; }
+
+ bool IsOpaque { get; set; }
+
+ bool MovableByWindowBackground { get; set; }
+ bool ShowCloseButton { get; set; }
+ bool ShowZoomButton { get; set; }
+ bool ShowMiniaturizeButton { get; set; }
+ bool ShowTitle { get; set; }
+
+ IView Content { get; set; }
+
+ void AddChild (IWindow window);
+ void RemoveChild (IWindow window);
+ void ShowDialog ();
+ void Show ();
+ void Center ();
+ }
+}
+
diff --git a/src/FigmaSharp.Views/Key.cs b/src/FigmaSharp.Views/Key.cs
new file mode 100644
index 0000000..7426a61
--- /dev/null
+++ b/src/FigmaSharp.Views/Key.cs
@@ -0,0 +1,151 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+
+namespace FigmaSharp
+{
+ [Flags]
+ public enum Key : ulong
+ {
+ A = 0uL,
+ S = 1uL,
+ D = 2uL,
+ F = 3uL,
+ H = 4uL,
+ G = 5uL,
+ Z = 6uL,
+ X = 7uL,
+ C = 8uL,
+ V = 9uL,
+ B = 11uL,
+ Q = 12uL,
+ W = 13uL,
+ E = 14uL,
+ R = 0xF,
+ Y = 0x10,
+ T = 17uL,
+ D1 = 18uL,
+ D2 = 19uL,
+ D3 = 20uL,
+ D4 = 21uL,
+ D6 = 22uL,
+ D5 = 23uL,
+ Equal = 24uL,
+ D9 = 25uL,
+ D7 = 26uL,
+ Minus = 27uL,
+ D8 = 28uL,
+ D0 = 29uL,
+ RightBracket = 30uL,
+ O = 0x1F,
+ U = 0x20,
+ LeftBracket = 33uL,
+ I = 34uL,
+ P = 35uL,
+ L = 37uL,
+ J = 38uL,
+ Quote = 39uL,
+ K = 40uL,
+ Semicolon = 41uL,
+ Backslash = 42uL,
+ Comma = 43uL,
+ Slash = 44uL,
+ N = 45uL,
+ M = 46uL,
+ Period = 47uL,
+ Grave = 50uL,
+ KeypadDecimal = 65uL,
+ KeypadMultiply = 67uL,
+ KeypadPlus = 69uL,
+ KeypadClear = 71uL,
+ KeypadDivide = 75uL,
+ KeypadEnter = 76uL,
+ KeypadMinus = 78uL,
+ KeypadEquals = 81uL,
+ Keypad0 = 82uL,
+ Keypad1 = 83uL,
+ Keypad2 = 84uL,
+ Keypad3 = 85uL,
+ Keypad4 = 86uL,
+ Keypad5 = 87uL,
+ Keypad6 = 88uL,
+ Keypad7 = 89uL,
+ Keypad8 = 91uL,
+ Keypad9 = 92uL,
+ Return = 36uL,
+ Tab = 48uL,
+ Space = 49uL,
+ Delete = 51uL,
+ Escape = 53uL,
+ Command = 55uL,
+ Shift = 56uL,
+ CapsLock = 57uL,
+ Option = 58uL,
+ Control = 59uL,
+ RightShift = 60uL,
+ RightOption = 61uL,
+ RightControl = 62uL,
+ Function = 0x3F,
+ VolumeUp = 72uL,
+ VolumeDown = 73uL,
+ Mute = 74uL,
+ ForwardDelete = 117uL,
+ ISOSection = 10uL,
+ JISYen = 93uL,
+ JISUnderscore = 94uL,
+ JISKeypadComma = 95uL,
+ JISEisu = 102uL,
+ JISKana = 104uL,
+ F18 = 79uL,
+ F19 = 80uL,
+ F20 = 90uL,
+ F5 = 96uL,
+ F6 = 97uL,
+ F7 = 98uL,
+ F3 = 99uL,
+ F8 = 100uL,
+ F9 = 101uL,
+ F11 = 103uL,
+ F13 = 105uL,
+ F16 = 106uL,
+ F14 = 107uL,
+ F10 = 109uL,
+ F12 = 111uL,
+ F15 = 113uL,
+ Help = 114uL,
+ Home = 115uL,
+ PageUp = 116uL,
+ F4 = 118uL,
+ End = 119uL,
+ F2 = 120uL,
+ PageDown = 121uL,
+ F1 = 122uL,
+ LeftArrow = 123uL,
+ RightArrow = 124uL,
+ DownArrow = 125uL,
+ UpArrow = 126uL
+ }
+}
+
diff --git a/src/FigmaSharp.Views/Padding.cs b/src/FigmaSharp.Views/Padding.cs
new file mode 100644
index 0000000..5920e3e
--- /dev/null
+++ b/src/FigmaSharp.Views/Padding.cs
@@ -0,0 +1,48 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp
+{
+ public class Padding
+ {
+ public Padding () { }
+
+ public Padding (float value) : this (value, value, value, value)
+ {
+
+ }
+ public Padding (float top, float left, float bottom, float right)
+ {
+ Left = left;
+ Right = right;
+ Top = top;
+ Bottom = bottom;
+ }
+
+ public float Left { get; set; }
+ public float Right { get; set; }
+ public float Top { get; set; }
+ public float Bottom { get; set; }
+ }
+}
diff --git a/src/FigmaSharp.Views/Svg/Definitions.cs b/src/FigmaSharp.Views/Svg/Definitions.cs
new file mode 100644
index 0000000..92219c5
--- /dev/null
+++ b/src/FigmaSharp.Views/Svg/Definitions.cs
@@ -0,0 +1,120 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace FigmaSharp.Views.Graphics
+{
+ [Serializable()]
+ public class Definitions : HtmlElement
+ {
+ }
+
+ [Serializable()]
+ public class StyleDefinition : Definitions
+ {
+ [XmlAttribute(attributeName: "type")]
+ public string ContentType { get; set; }
+
+ public string Content { get; set; }
+
+ [XmlText]
+ public XmlNode[] CDataContent
+ {
+ get
+ {
+ var dummy = new XmlDocument();
+ return new XmlNode[] { dummy.CreateCDataSection(Content) };
+ }
+ set
+ {
+ if (value == null)
+ {
+ Content = null;
+ return;
+ }
+
+ if (value.Length != 1)
+ {
+ throw new InvalidOperationException(
+ String.Format(
+ "Invalid array length {0}", value.Length));
+ }
+
+ Content = value[0].Value;
+ }
+ }
+ }
+
+ [Serializable()]
+ public class MarkerDefinition : Definitions
+ {
+ [XmlAttribute(attributeName: "type")]
+ public float StyleType { get; set; }
+
+ [XmlAttribute(attributeName: "markerWidth")]
+ public float MarkerWidth { get; set; }
+
+ [XmlAttribute(attributeName: "markerHeight")]
+ public float MarkerHeight { get; set; }
+
+ [XmlAttribute(attributeName: "orient")]
+ public string Orient { get; set; }
+
+ [XmlAttribute(attributeName: "viewBox")]
+ public string viewBox { get; set; }
+
+ [XmlIgnore()]
+ public ViewBoxRectangle ViewBox
+ {
+ get
+ {
+ if (!string.IsNullOrEmpty(viewBox))
+ {
+ var splitt = viewBox.Split(' ');
+ if (splitt.Length == 4)
+ {
+ var rectangle = new ViewBoxRectangle()
+ {
+ X = float.Parse(splitt[0]),
+ Y = float.Parse(splitt[1]),
+ Width = float.Parse(splitt[2]),
+ Height = float.Parse(splitt[3]),
+ };
+
+ return rectangle;
+ }
+ }
+ return null;
+ }
+ }
+
+ [XmlElement("g", Type = typeof(GPath))]
+ [XmlElement("path", Type = typeof(Path))]
+ [XmlElement("circle", Type = typeof(CirclePath))]
+ public HtmlElement[] Vectors { get; set; }
+ }
+}
diff --git a/src/FigmaSharp.Views/Svg/HtmlElements.cs b/src/FigmaSharp.Views/Svg/HtmlElements.cs
new file mode 100644
index 0000000..39b344f
--- /dev/null
+++ b/src/FigmaSharp.Views/Svg/HtmlElements.cs
@@ -0,0 +1,189 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Xml.Serialization;
+
+namespace FigmaSharp.Views.Graphics
+{
+ [Serializable()]
+ public class HtmlElement
+ {
+ [XmlAttribute(attributeName: "class")]
+ public string Class { get; set; }
+
+ [XmlAttribute(attributeName: "id")]
+ public string Id { get; set; }
+ }
+
+ [Serializable()]
+ public class Mask : HtmlElement
+ {
+ [XmlAttribute(attributeName: "x")]
+ public float X { get; set; }
+
+ [XmlAttribute(attributeName: "y")]
+ public float Y { get; set; }
+
+ [XmlAttribute(attributeName: "width")]
+ public float Width { get; set; }
+
+ [XmlAttribute(attributeName: "height")]
+ public float Height { get; set; }
+
+ [XmlAttribute(attributeName: "maskUnits")]
+ public string Units { get; set; }
+
+ [XmlAttribute(attributeName: "mask-type")]
+ public string MaskType { get; set; }
+
+ [XmlElement("g", Type = typeof(GPath))]
+ [XmlElement("path", Type = typeof(Path))]
+ [XmlElement("circle", Type = typeof(CirclePath))]
+ public HtmlElement[] Paths { get; set; }
+ }
+
+ [Serializable()]
+ public class TextPath : HtmlElement
+ {
+ [XmlAttribute(attributeName: "font-size")]
+ public float FontSize { get; set; }
+
+ [XmlAttribute(attributeName: "x")]
+ public float X { get; set; }
+
+ [XmlAttribute(attributeName: "rx")]
+ public float RX { get; set; }
+
+ [XmlAttribute(attributeName: "y")]
+ public float Y { get; set; }
+
+ [XmlAttribute(attributeName: "ry")]
+ public float RY { get; set; }
+ }
+
+ [Serializable()]
+ public class RectanglePath : LayerHtmlElement
+ {
+ [XmlAttribute(attributeName: "x")]
+ public float X { get; set; }
+
+ [XmlAttribute(attributeName: "y")]
+ public float Y { get; set; }
+
+ [XmlAttribute(attributeName: "rx")]
+ public float RX { get; set; }
+
+ [XmlAttribute(attributeName: "ry")]
+ public float RY { get; set; }
+
+ [XmlAttribute(attributeName: "width")]
+ public float Width { get; set; }
+
+ [XmlAttribute(attributeName: "height")]
+ public float Height { get; set; }
+ }
+
+ [Serializable()]
+ public class GPath : HtmlElement
+ {
+ [XmlAttribute(attributeName: "mask")]
+ public string Mask { get; set; }
+
+ [XmlElement("g", Type = typeof(GPath))]
+ [XmlElement("path", Type = typeof(Path))]
+ [XmlElement("circle", Type = typeof(CirclePath))]
+ [XmlElement("rect", Type = typeof(RectanglePath))]
+ [XmlElement("line", Type = typeof(LinePath))]
+ [XmlElement("text", Type = typeof(TextPath))]
+ public HtmlElement[] Paths { get; set; }
+ }
+
+ [Serializable()]
+ public abstract class LayerHtmlElement : HtmlElement
+ {
+ [XmlAttribute(attributeName: "fill-opacity")]
+ public float FillOpacity { get; set; }
+
+ [XmlAttribute(attributeName: "transform")]
+ public string Transform { get; set; }
+
+ [XmlAttribute(attributeName: "fill")]
+ public string Fill { get; set; }
+
+ [XmlAttribute(attributeName: "stroke")]
+ public string Stroke { get; set; }
+
+ [XmlAttribute(attributeName: "stroke-width")]
+ public float StrokeWidth { get; set; }
+
+ [XmlAttribute(attributeName: "stroke-linecap")]
+ public string StrokeLineCap { get; set; }
+
+ [XmlAttribute(attributeName: "stroke-linejoin")]
+ public string StrokeLineJoin { get; set; }
+ }
+
+ [Serializable()]
+ public class CirclePath : LayerHtmlElement
+ {
+ [XmlAttribute(attributeName: "x")]
+ public float X { get; set; }
+
+ [XmlAttribute(attributeName: "rx")]
+ public float RX { get; set; }
+
+ [XmlAttribute(attributeName: "y")]
+ public float Y { get; set; }
+
+ [XmlAttribute(attributeName: "ry")]
+ public float RY { get; set; }
+
+ [XmlAttribute(attributeName: "r")]
+ public float Radio { get; set; }
+ }
+
+ [Serializable()]
+ public class LinePath : LayerHtmlElement
+ {
+ [XmlAttribute(attributeName: "x1")]
+ public float X1 { get; set; }
+
+ [XmlAttribute(attributeName: "x2")]
+ public float X2 { get; set; }
+
+ [XmlAttribute(attributeName: "y1")]
+ public float Y1 { get; set; }
+
+ [XmlAttribute(attributeName: "y2")]
+ public float Y2 { get; set; }
+ }
+
+ [Serializable()]
+ public class Path : LayerHtmlElement
+ {
+ [XmlAttribute(attributeName: "d")]
+ public string d { get; set; }
+ }
+}
diff --git a/src/FigmaSharp.Views/Svg/Svg.cs b/src/FigmaSharp.Views/Svg/Svg.cs
new file mode 100644
index 0000000..dfe308c
--- /dev/null
+++ b/src/FigmaSharp.Views/Svg/Svg.cs
@@ -0,0 +1,120 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Xml.Linq;
+using System.Xml.Serialization;
+
+namespace FigmaSharp.Views.Graphics
+{
+ [Serializable()]
+ [XmlRoot("svg")]
+ public class Svg
+ {
+ [XmlAttribute(attributeName: "id")]
+ public string Id { get; set; }
+
+ [XmlAttribute(attributeName: "width")]
+ public float Width { get; set; }
+
+ [XmlAttribute(attributeName: "height")]
+ public float Height { get; set; }
+
+ [XmlAttribute(attributeName: "version")]
+ public float Version { get; set; }
+
+ #region Titles
+
+ [XmlElement(elementName: "title")]
+ public string Title { get; set; }
+ [XmlElement(elementName: "drawing-type")]
+ public string DrawingType { get; set; }
+
+ #endregion
+
+ [XmlElement("mask")]
+ public Mask[] Mask { get; set; }
+
+ [XmlElement("svg")]
+ public Svg[] Svgs { get; set; }
+
+ [XmlElement("g", Type = typeof(GPath))]
+ [XmlElement("path", Type = typeof(Path))]
+ [XmlElement("circle", Type = typeof(CirclePath))]
+ [XmlElement("rect", Type = typeof(RectanglePath))]
+ [XmlElement("line", Type = typeof(LinePath))]
+ [XmlElement("text", Type = typeof(TextPath))]
+ public HtmlElement[] Vectors { get; set; }
+
+ [XmlArray("defs")]
+ [XmlArrayItem("style", typeof(StyleDefinition))]
+ [XmlArrayItem("marker", typeof(MarkerDefinition))]
+ public Definitions[] Definitions { get; set; }
+
+ [XmlAttribute(attributeName: "viewBox")]
+ public string viewBox { get; set; }
+
+ [XmlIgnore()]
+ public ViewBoxRectangle ViewBox
+ {
+ get
+ {
+ if (!string.IsNullOrEmpty(viewBox))
+ {
+ var splitt = viewBox.Split(' ');
+ if (splitt.Length == 4)
+ {
+ var rectangle = new ViewBoxRectangle()
+ {
+ X = float.Parse(splitt[0]),
+ Y = float.Parse(splitt[1]),
+ Width = float.Parse(splitt[2]),
+ Height = float.Parse(splitt[3]),
+ };
+
+ return rectangle;
+ }
+ }
+ return null;
+ }
+ }
+
+ public static Svg FromData (string data)
+ {
+ data = data
+ .Replace("xmlns=\"http://www.w3.org/2000/svg\"", "")
+ .Replace("xmlns=\"http://www.w3.org/1999/xlink\"", "");
+
+ XmlSerializer serializer = new XmlSerializer(typeof(Svg));
+
+ Svg result;
+ using (TextReader r = new StringReader(data))
+ result = (Svg)serializer.Deserialize(r);
+ return result;
+ //Parse(xml.Root);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/FigmaSharp.Views/Svg/ViewBoxRectangle.cs b/src/FigmaSharp.Views/Svg/ViewBoxRectangle.cs
new file mode 100644
index 0000000..18ecb7a
--- /dev/null
+++ b/src/FigmaSharp.Views/Svg/ViewBoxRectangle.cs
@@ -0,0 +1,52 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Xml.Serialization;
+
+namespace FigmaSharp.Views.Graphics
+{
+ [Serializable()]
+ public class ViewBoxRectangle
+ {
+ [XmlAttribute(attributeName: "x")]
+ public float X { get; set; }
+
+ [XmlAttribute(attributeName: "rx")]
+ public float RX { get; set; }
+
+ [XmlAttribute(attributeName: "y")]
+ public float Y { get; set; }
+
+ [XmlAttribute(attributeName: "ry")]
+ public float RY { get; set; }
+
+ [XmlAttribute(attributeName: "width")]
+ public float Width { get; set; }
+
+ [XmlAttribute(attributeName: "height")]
+ public float Height { get; set; }
+ }
+
+}
\ No newline at end of file
diff --git a/src/FigmaSharp.Views/Transform.cs b/src/FigmaSharp.Views/Transform.cs
new file mode 100644
index 0000000..23ce2ea
--- /dev/null
+++ b/src/FigmaSharp.Views/Transform.cs
@@ -0,0 +1,33 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Views
+{
+ public class Transform
+ {
+ public float RotateAngle { get; set; }
+ public float Scale { get; set; }
+ public Point Translation { get; set; } = new Point ();
+ }
+}
diff --git a/src/FigmaSharp/AppContext.cs b/src/FigmaSharp/AppContext.cs
new file mode 100644
index 0000000..71e58d6
--- /dev/null
+++ b/src/FigmaSharp/AppContext.cs
@@ -0,0 +1,120 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Reflection;
+
+using FigmaSharp.Converters;
+using FigmaSharp.PropertyConfigure;
+using FigmaSharp.Views;
+
+namespace FigmaSharp
+{
+ ///
+ /// An AppContext contains configuration (such as the authentication token)
+ /// and code converters for the current application.
+ ///
+ public class AppContext : IFigmaDelegate
+ {
+ public bool IsApiConfigured => !string.IsNullOrEmpty (Api.Token);
+
+ IFigmaDelegate figmaDelegate;
+
+ public string Version => System.Diagnostics.FileVersionInfo.GetVersionInfo(this.GetType ().Assembly.Location).FileVersion;
+
+ AppContext()
+ {
+
+ }
+
+ public void Configuration(IFigmaDelegate currentDelegate, string token)
+ {
+ SetAccessToken(token);
+ Configuration(currentDelegate);
+ }
+
+ public void Configuration(IFigmaDelegate currentDelegate) => figmaDelegate = currentDelegate;
+
+ public void SetAccessToken(string token)
+ {
+ Api.Token = token;
+ }
+
+ public void BeginInvoke(Action handler) => figmaDelegate.BeginInvoke(handler);
+
+ public IView CreateEmptyView() => figmaDelegate.CreateEmptyView();
+
+ public NodeConverter[] GetFigmaConverters() => figmaDelegate.GetFigmaConverters();
+
+ public IImage GetImage(string url) => figmaDelegate.GetImage(url);
+ public string GetSvgData(string url) => figmaDelegate.GetSvgData(url);
+
+ public IImage GetImageFromFilePath(string filePath) =>
+ figmaDelegate.GetImageFromFilePath(filePath);
+
+ //public FigmaResponse GetFigmaResponseFromContent(string template) =>
+ // Api. FigmaApiHelper.GetFigmaResponseFromContent(template);
+
+ public IImage GetImageFromManifest(Assembly assembly, string imageRef) =>
+ figmaDelegate.GetImageFromManifest(assembly, imageRef);
+
+ public string GetManifestResource(Assembly assembly, string file) =>
+ figmaDelegate.GetManifestResource(assembly, file);
+
+ public IImageView GetImageView(IImage image)
+ {
+ return figmaDelegate.GetImageView(image);
+ }
+
+ #region Static
+
+ static AppContext current;
+
+ ///
+ /// The shared AppContext for the application.
+ ///
+ /// The current.
+ public static AppContext Current
+ {
+ get
+ {
+ if (current == null)
+ {
+ current = new AppContext();
+ }
+ return current;
+ }
+ }
+
+ public static FigmaApi Api { get; } = new FigmaApi ();
+ public bool IsVerticalAxisFlipped => figmaDelegate.IsVerticalAxisFlipped;
+
+ public ViewPropertyConfigureBase GetViewPropertyConfigure() => figmaDelegate.GetViewPropertyConfigure();
+
+ public CodePropertyConfigureBase GetCodePropertyConfigure ()
+ => figmaDelegate.GetCodePropertyConfigure();
+
+ #endregion
+ }
+}
diff --git a/src/FigmaSharp/Converters/Layers/ElipseConverterBase.cs b/src/FigmaSharp/Converters/Layers/ElipseConverterBase.cs
new file mode 100644
index 0000000..311f298
--- /dev/null
+++ b/src/FigmaSharp/Converters/Layers/ElipseConverterBase.cs
@@ -0,0 +1,47 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Converters
+{
+ public abstract class ElipseConverterBase : NodeConverter
+ {
+ public class Properties
+ {
+ public static string FillColor = "FillColor";
+ public static string StrokeColor = "StrokeColor";
+ public static string StrokeDashes = "StrokeDashes";
+ public static string LineWidth = "LineWidth";
+ public static string Opacity = "AlphaValue";
+ }
+
+ public override bool IsLayer => true;
+
+ public override bool CanConvert(FigmaNode currentNode)
+ {
+ return currentNode.GetType() == typeof(FigmaElipse);
+ }
+ }
+}
diff --git a/src/FigmaSharp/Converters/Layers/FrameConverterBase.cs b/src/FigmaSharp/Converters/Layers/FrameConverterBase.cs
new file mode 100644
index 0000000..18c45a5
--- /dev/null
+++ b/src/FigmaSharp/Converters/Layers/FrameConverterBase.cs
@@ -0,0 +1,38 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Converters
+{
+ public abstract class FrameConverterBase : NodeConverter
+ {
+ public override bool IsLayer => true;
+
+ public override bool CanConvert(FigmaNode currentNode)
+ {
+ return currentNode is FigmaFrame;
+ }
+ }
+}
diff --git a/src/FigmaSharp/Converters/Layers/LineConverterBase.cs b/src/FigmaSharp/Converters/Layers/LineConverterBase.cs
new file mode 100644
index 0000000..b3dafa6
--- /dev/null
+++ b/src/FigmaSharp/Converters/Layers/LineConverterBase.cs
@@ -0,0 +1,38 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Converters
+{
+ public abstract class LineConverterBase : NodeConverter
+ {
+ public override bool IsLayer => true;
+
+ public override bool CanConvert(FigmaNode currentNode)
+ {
+ return currentNode.GetType () == typeof (FigmaLine) || (currentNode.type == "VECTOR" && currentNode.name == "sep");
+ }
+ }
+}
diff --git a/src/FigmaSharp/Converters/Layers/PointConverterBase.cs b/src/FigmaSharp/Converters/Layers/PointConverterBase.cs
new file mode 100644
index 0000000..146b7e2
--- /dev/null
+++ b/src/FigmaSharp/Converters/Layers/PointConverterBase.cs
@@ -0,0 +1,38 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Converters
+{
+ public abstract class PointConverterBase : NodeConverter
+ {
+ public override bool IsLayer => true;
+
+ public override bool CanConvert(FigmaNode currentNode)
+ {
+ return currentNode.GetType() == typeof(FigmaPoint);
+ }
+ }
+}
diff --git a/src/FigmaSharp/Converters/Layers/RectangleVectorConverterBase.cs b/src/FigmaSharp/Converters/Layers/RectangleVectorConverterBase.cs
new file mode 100644
index 0000000..b6c5727
--- /dev/null
+++ b/src/FigmaSharp/Converters/Layers/RectangleVectorConverterBase.cs
@@ -0,0 +1,38 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Converters
+{
+ public abstract class RectangleVectorConverterBase : NodeConverter
+ {
+ public override bool IsLayer => true;
+
+ public override bool CanConvert(FigmaNode currentNode)
+ {
+ return currentNode.GetType() == typeof(RectangleVector);
+ }
+ }
+}
diff --git a/src/FigmaSharp/Converters/Layers/RegularPolygonConverterBase.cs b/src/FigmaSharp/Converters/Layers/RegularPolygonConverterBase.cs
new file mode 100644
index 0000000..8548db7
--- /dev/null
+++ b/src/FigmaSharp/Converters/Layers/RegularPolygonConverterBase.cs
@@ -0,0 +1,38 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Converters
+{
+ public abstract class RegularPolygonConverterBase : NodeConverter
+ {
+ public override bool IsLayer => true;
+
+ public override bool CanConvert(FigmaNode currentNode)
+ {
+ return currentNode.GetType() == typeof(FigmaRegularPolygon);
+ }
+ }
+}
diff --git a/src/FigmaSharp/Converters/Layers/VectorEntityConverterBase.cs b/src/FigmaSharp/Converters/Layers/VectorEntityConverterBase.cs
new file mode 100644
index 0000000..272dd3d
--- /dev/null
+++ b/src/FigmaSharp/Converters/Layers/VectorEntityConverterBase.cs
@@ -0,0 +1,39 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Converters
+{
+ public abstract class VectorConverterBase : NodeConverter
+ {
+ public override bool IsLayer => true;
+
+ public override bool CanConvert(FigmaNode currentNode)
+ {
+ return currentNode.GetType() == typeof(FigmaVector)
+ || currentNode.GetType() == typeof(FigmaStar);
+ }
+ }
+}
diff --git a/src/FigmaSharp/Converters/NodeConverter.cs b/src/FigmaSharp/Converters/NodeConverter.cs
new file mode 100644
index 0000000..efbeedc
--- /dev/null
+++ b/src/FigmaSharp/Converters/NodeConverter.cs
@@ -0,0 +1,143 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+
+using FigmaSharp.Models;
+using FigmaSharp.Services;
+using FigmaSharp.Views;
+
+namespace FigmaSharp.Converters
+{
+ public abstract class NodeConverter
+ {
+ public abstract Type GetControlType (FigmaNode currentNode);
+
+ public virtual bool IsLayer { get; }
+
+ public virtual string Name { get; } = CodeRenderService.DefaultViewName;
+
+ public virtual bool ScanChildren (FigmaNode currentNode)
+ {
+ return true;
+ //return !(currentNode is FigmaInstance);
+ }
+
+ protected T ToEnum (string value)
+ {
+ try {
+ foreach (T suit in (T[])Enum.GetValues (typeof (T))) {
+ if (suit.ToString ().ToLower ().Equals (value, StringComparison.InvariantCultureIgnoreCase)) {
+ return suit;
+ }
+ }
+ } catch (System.Exception ex) {
+ LoggingService.LogError("[FIGMA] Error", ex);
+
+ }
+ return default (T);
+ }
+
+ protected Dictionary GetKeyValues (FigmaNode currentNode)
+ {
+ Dictionary ids = new Dictionary();
+ var index = currentNode.name.IndexOf ($"type:", System.StringComparison.InvariantCultureIgnoreCase);
+ if (index > -1) {
+ var properties = currentNode.name.Split (' ');
+ foreach (var property in properties) {
+ var data = property.Split (':');
+ if (data.Length != 2) {
+ LoggingService.LogError ($"Error format in parameter: '{property}'");
+ continue;
+ }
+ ids.Add (data[0], data[1]);
+ }
+ } else {
+ ids.Add ("type", currentNode.name);
+ }
+ return ids;
+ }
+
+ string RemoveQuotes(string data, bool enable = false)
+ {
+ if (enable)
+ return data.Replace("\"", "");
+ return data;
+ }
+
+ protected string GetIdentifierValue (string data, string parameter, bool removeQuotes = false)
+ {
+ var index = data.IndexOf($"{parameter}:", System.StringComparison.InvariantCultureIgnoreCase);
+ if (index > -1)
+ {
+ var delta = data.Substring(index + $"{parameter}=".Length);
+ var endIndex = delta.IndexOf(" ", System.StringComparison.InvariantCultureIgnoreCase);
+
+ if (endIndex == -1)
+ return RemoveQuotes (delta, removeQuotes);
+ return RemoveQuotes (delta.Substring(0, endIndex), removeQuotes);
+ }
+ return null;
+ }
+
+ internal virtual bool HasWidthConstraint ()
+ {
+ return true;
+ }
+
+ internal virtual bool HasHeightConstraint()
+ {
+ return true;
+ }
+
+ internal virtual bool IsFlexibleHorizontal(FigmaNode node)
+ {
+ if (node is IConstraints constrainedNode)
+ return constrainedNode != null && constrainedNode.constraints.IsFlexibleHorizontal;
+ return false;
+ }
+
+ internal virtual bool IsFlexibleVertical(FigmaNode node)
+ {
+ if (node is IConstraints constrainedNode)
+ return constrainedNode != null && constrainedNode.constraints.IsFlexibleVertical;
+ return false;
+ }
+
+ protected bool ContainsType (FigmaNode currentNode, string name)
+ {
+ var identifier = GetIdentifierValue (currentNode.name, "type") ?? currentNode.name;
+ return identifier == name;
+ }
+
+ public abstract bool CanConvert (FigmaNode currentNode);
+
+ public virtual bool CanCodeConvert(FigmaNode currentNode) => CanConvert(currentNode);
+
+ public abstract IView ConvertToView (FigmaNode currentNode, ViewNode parent, ViewRenderService rendererService);
+
+ public abstract string ConvertToCode (CodeNode currentNode, CodeNode parentNode, ICodeRenderService rendererService);
+ }
+}
diff --git a/src/FigmaSharp/Converters/StackViewBase.cs b/src/FigmaSharp/Converters/StackViewBase.cs
new file mode 100644
index 0000000..348e0f6
--- /dev/null
+++ b/src/FigmaSharp/Converters/StackViewBase.cs
@@ -0,0 +1,42 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Converters
+{
+ public abstract class StackViewBase : NodeConverter
+ {
+ public static class Properties
+ {
+ public static string EdgeInsets = "EdgeInsets";
+ public static string Spacing = "Spacing";
+ public static string Orientation = "Orientation";
+ public static string Distribution = "Distribution";
+ }
+
+ public override bool IsLayer => true;
+ public override bool CanConvert(FigmaNode currentNode) => currentNode.IsStackView();
+ }
+}
diff --git a/src/FigmaSharp/Converters/TextConverterBase.cs b/src/FigmaSharp/Converters/TextConverterBase.cs
new file mode 100644
index 0000000..f436d08
--- /dev/null
+++ b/src/FigmaSharp/Converters/TextConverterBase.cs
@@ -0,0 +1,38 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Converters
+{
+ public abstract class TextConverterBase : NodeConverter
+ {
+ public override bool IsLayer => true;
+
+ public override bool CanConvert(FigmaNode currentNode)
+ {
+ return currentNode.GetType() == typeof(FigmaText);
+ }
+ }
+}
diff --git a/src/FigmaSharp/Extensions/CodeGenerationExtensions.cs b/src/FigmaSharp/Extensions/CodeGenerationExtensions.cs
new file mode 100644
index 0000000..81aea75
--- /dev/null
+++ b/src/FigmaSharp/Extensions/CodeGenerationExtensions.cs
@@ -0,0 +1,36 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System.Text;
+
+namespace FigmaSharp
+{
+ public static class CodeGenerationExtensions
+ {
+ public static void ReplaceDefaultNameTag (this StringBuilder builder, string value)
+ {
+ builder.Replace(Resources.Ids.Conversion.NameIdentifier, value);
+ }
+ }
+}
diff --git a/src/FigmaSharp/Extensions/FigmaStyleExtensions.cs b/src/FigmaSharp/Extensions/FigmaStyleExtensions.cs
new file mode 100644
index 0000000..db0a717
--- /dev/null
+++ b/src/FigmaSharp/Extensions/FigmaStyleExtensions.cs
@@ -0,0 +1,55 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Extensions
+{
+ public static class FigmaStyleExtensions
+ {
+ public static void FillEmptyStylePropertiesWithDefaults(this FigmaTypeStyle style, FigmaText text)
+ {
+ if (style.fills == default)
+ style.fills = text.fills;
+ if (style.fontFamily == default)
+ style.fontFamily = text.style.fontFamily;
+ if (style.fontPostScriptName == default)
+ style.fontPostScriptName = text.style.fontPostScriptName;
+ if (style.fontSize == default)
+ style.fontSize = text.style.fontSize;
+ if (style.fontWeight == default)
+ style.fontWeight = text.style.fontWeight;
+ if (style.letterSpacing == default)
+ style.letterSpacing = text.style.letterSpacing;
+ if (style.lineHeightPercent == default)
+ style.lineHeightPercent = text.style.lineHeightPercent;
+ if (style.lineHeightPx == default)
+ style.lineHeightPx = text.style.lineHeightPx;
+ if (style.textAlignHorizontal == default)
+ style.textAlignHorizontal = text.style.textAlignHorizontal;
+ if (style.textAlignVertical == default)
+ style.textAlignVertical = text.style.textAlignVertical;
+ }
+ }
+}
diff --git a/src/FigmaSharp/Extensions/NodeExtensions.cs b/src/FigmaSharp/Extensions/NodeExtensions.cs
new file mode 100644
index 0000000..23f4ec7
--- /dev/null
+++ b/src/FigmaSharp/Extensions/NodeExtensions.cs
@@ -0,0 +1,281 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp
+{
+ public static class NodeExtensions
+ {
+ public static bool TryGetAttributeValue(this FigmaNode node, string parameter, out string value)
+ {
+ //we want remove special chars
+ parameter = FigmaSharp.ServiceExtensions.FilterName(parameter);
+
+ value = node.name;
+ try
+ {
+ var index = value.IndexOf(parameter);
+ if (index > -1 && index < value.Length)
+ {
+ value = value.Substring(index + parameter.Length);
+ index = value.IndexOf("\"");
+ if (index > -1 && index < value.Length)
+ {
+ value = value.Substring(index + 1);
+ index = value.IndexOf("\"");
+ if (index > -1 && index < value.Length)
+ {
+ value = value.Substring(0, index);
+ return true;
+ }
+ }
+ }
+ }
+ catch (Exception)
+ {
+ }
+ value = null;
+ return false;
+ }
+
+ public static bool TryGetChildPropertyValue(this FigmaNode node, string property, out string value)
+ {
+ if (node is IFigmaNodeContainer container && container.children != null)
+ {
+ foreach (var item in container.children)
+ {
+ if (TryGetAttributeValue(item, property, out value))
+ {
+ return true;
+ }
+ }
+ }
+ value = null;
+ return false;
+ }
+
+ public static bool IsStackView (this FigmaNode node)
+ {
+ if (node is FigmaFrame frame)
+ {
+ if (frame.LayoutMode == FigmaLayoutMode.Horizontal ||
+ frame.LayoutMode == FigmaLayoutMode.Vertical)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static bool TryGetNodeCustomName(this FigmaNode node, out string customName)
+ {
+ customName = node.name;
+ var index = customName.IndexOf('\"');
+
+ if (index > 2 && (customName[index - 1] == ':' || customName[index - 1] == '='))
+ {
+ return false;
+ }
+
+ if (index > -1 && index < customName.Length - 1)
+ {
+ customName = customName.Substring(index + 1);
+
+ index = customName.IndexOf('\"');
+ if (index > -1 && index < customName.Length)
+ {
+ customName = customName.Substring(0, index);
+ //customName = RemoveIllegalCharacters (customName);
+ return true;
+ }
+ }
+ customName = node.name;
+ //customName = RemoveIllegalCharacters (node.name);
+ return false;
+ }
+
+ public static void CalculateBounds(this IFigmaNodeContainer figmaNodeContainer)
+ {
+ if (figmaNodeContainer is IAbsoluteBoundingBox calculatedBounds)
+ {
+ calculatedBounds.absoluteBoundingBox = Rectangle.Zero;
+ foreach (var item in figmaNodeContainer.children)
+ {
+ if (item is IAbsoluteBoundingBox itmBoundingBox)
+ {
+ calculatedBounds.absoluteBoundingBox.X = Math.Min(calculatedBounds.absoluteBoundingBox.X, itmBoundingBox.absoluteBoundingBox.X);
+ calculatedBounds.absoluteBoundingBox.Y = Math.Min(calculatedBounds.absoluteBoundingBox.Y, itmBoundingBox.absoluteBoundingBox.Y);
+
+ if (itmBoundingBox.absoluteBoundingBox.X + itmBoundingBox.absoluteBoundingBox.Width > calculatedBounds.absoluteBoundingBox.X + calculatedBounds.absoluteBoundingBox.Width)
+ {
+ calculatedBounds.absoluteBoundingBox.Width += (itmBoundingBox.absoluteBoundingBox.X + itmBoundingBox.absoluteBoundingBox.Width) - (calculatedBounds.absoluteBoundingBox.X + calculatedBounds.absoluteBoundingBox.Width);
+ }
+
+ if (itmBoundingBox.absoluteBoundingBox.Y + itmBoundingBox.absoluteBoundingBox.Height > calculatedBounds.absoluteBoundingBox.Y + calculatedBounds.absoluteBoundingBox.Height)
+ {
+ calculatedBounds.absoluteBoundingBox.Height += (itmBoundingBox.absoluteBoundingBox.Y + itmBoundingBox.absoluteBoundingBox.Height) - (calculatedBounds.absoluteBoundingBox.Y + calculatedBounds.absoluteBoundingBox.Height);
+ }
+ }
+ }
+ }
+ }
+
+ ////TODO: Change to async multithread
+ //public static async Task SaveFigmaImageFiles(this FigmaPaint[] paints, string fileId, string directoryPath, string format = ".png")
+ //{
+ // var ids = paints.Select(s => s.ID).ToArray();
+ // var query = new FigmaImageQuery(AppContext.Current.Token, fileId, ids);
+ // var images = FigmaApiHelper.GetFigmaImage(query);
+ // if (images != null)
+ // {
+ // var urls = paints.Select(s => images.images[s.ID]).ToArray();
+ // FileHelper.SaveFiles(directoryPath, format, urls);
+ // }
+ //}
+
+ public static IEnumerable FindImageNodes(this FigmaNode sender, Func condition = null)
+ {
+ if (sender is Models.IFigmaImage && (condition == null || condition(sender)))
+ {
+ yield return sender;
+ }
+
+ if (sender is IFigmaNodeContainer container)
+ {
+ foreach (var child in container.children)
+ {
+ foreach (var image in child.FindImageNodes())
+ {
+ yield return image;
+ }
+ }
+ }
+ else if (sender is FigmaDocument document)
+ {
+ foreach (var child in document.children)
+ {
+ foreach (var image in child.FindImageNodes())
+ {
+ yield return image;
+ }
+ }
+ }
+ }
+
+ public static IEnumerable FindNode(this FigmaNode sender, Func condition)
+ {
+ if (condition(sender))
+ {
+ yield return sender;
+ }
+
+ if (sender is IFigmaNodeContainer container)
+ {
+ foreach (var child in container.children)
+ {
+ foreach (var image in child.FindNode(condition))
+ {
+ yield return image;
+ }
+ }
+ }
+ else if (sender is FigmaDocument document)
+ {
+ foreach (var child in document.children)
+ {
+ foreach (var image in child.FindNode(condition))
+ {
+ yield return image;
+ }
+ }
+ }
+ }
+
+ public static Rectangle GetBoundRectangle(this IEnumerable customViews, bool includeHidden = false)
+ {
+ Rectangle point = null;
+ FigmaNode item;
+ for (int i = 0; i < customViews.Count(); i++)
+ {
+ item = customViews.ElementAt(i);
+ if (!includeHidden && !item.visible)
+ continue;
+ if (item is IAbsoluteBoundingBox boundingBox)
+ {
+ if (point == null)
+ {
+ point = boundingBox.absoluteBoundingBox.Copy();
+ }
+ else
+ {
+ if (boundingBox.absoluteBoundingBox.Left < point.X)
+ point.X = boundingBox.absoluteBoundingBox.Left;
+ if (boundingBox.absoluteBoundingBox.Top < point.Y)
+ point.Y = boundingBox.absoluteBoundingBox.Top;
+
+ if (boundingBox.absoluteBoundingBox.Right > point.Right)
+ point.Width = boundingBox.absoluteBoundingBox.Right - point.Left;
+ if (boundingBox.absoluteBoundingBox.Bottom > point.Bottom)
+ point.Height = boundingBox.absoluteBoundingBox.Bottom - point.Top;
+ }
+ }
+ }
+ return point;
+ }
+
+ public static void Recursively(this FigmaNode[] customViews, string filter, List viewsFound)
+ {
+ foreach (var item in customViews)
+ {
+ Recursively(item, filter, viewsFound);
+ }
+ }
+
+ public static void Recursively(this FigmaNode customView, string filter, List viewsFound)
+ {
+ if (customView.name == filter)
+ {
+ viewsFound.Add(customView);
+ }
+
+ if (customView is IFigmaNodeContainer container)
+ {
+ foreach (var item in container.children)
+ {
+ Recursively(item, filter, viewsFound);
+ }
+ }
+ }
+
+ public static bool IsRenderSkipped(this FigmaNode figmaNode)
+ {
+ return figmaNode.name.StartsWith("//");
+ }
+ }
+}
diff --git a/src/FigmaSharp/Extensions/ServiceExtensions.cs b/src/FigmaSharp/Extensions/ServiceExtensions.cs
new file mode 100644
index 0000000..39ff04e
--- /dev/null
+++ b/src/FigmaSharp/Extensions/ServiceExtensions.cs
@@ -0,0 +1,213 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp
+{
+ public static class ServiceExtensions
+ {
+ public static bool HasChildrenVisible (this IFigmaNodeContainer optionsNode, string layerName)
+ {
+ return optionsNode.children.FirstOrDefault(s => s.name == layerName)
+ ?.visible ?? false;
+ }
+
+ public static IEnumerable GetChildren(this FigmaNode node)
+ {
+ if (node is IFigmaNodeContainer container)
+ return container.children.OfType();
+ return Enumerable.Empty();
+ }
+
+ public static bool HasChildrenVisible(this FigmaNode figmaNode, string layerName)
+ {
+ return figmaNode is IFigmaNodeContainer nodeContainer && nodeContainer.HasChildrenVisible(layerName);
+ }
+
+ public static IEnumerable GroupByCreatedAt (this IEnumerable sender)
+ {
+ return sender.GroupBy(x => x.created_at)
+ .Select(group => group.First());
+ }
+
+ public static Rectangle GetCurrentBounds (this FigmaCanvas canvas)
+ {
+ Rectangle contentRect = Rectangle.Zero;
+ for (int i = 0; i < canvas.children.Length; i++) {
+ if (canvas.children[i] is IAbsoluteBoundingBox box) {
+ if (i == 0) {
+ contentRect = box.absoluteBoundingBox;
+ } else {
+ contentRect = contentRect.UnionWith (box.absoluteBoundingBox);
+ }
+ }
+ }
+ return contentRect;
+ }
+
+ public static FigmaCanvas GetCurrentCanvas (this FigmaNode node)
+ {
+ if (node.Parent is FigmaCanvas figmaCanvas)
+ return figmaCanvas;
+
+ if (node.Parent == null)
+ return null;
+
+ return GetCurrentCanvas (node.Parent);
+ }
+
+ public static void AppendLineIfValue (this StringBuilder sender, string value)
+ {
+ if (!string.IsNullOrEmpty (value))
+ sender.AppendLine (value);
+ }
+
+ public static bool ContainsSourceImage (this FigmaNode node)
+ {
+ FigmaPaint[] fills = null;
+ if (node is FigmaFrame frame) {
+ fills = frame.fills;
+ }
+ else if (node is FigmaVector vector)
+ {
+ fills = vector.fills;
+ }
+ if (fills != null)
+ {
+ return fills.OfType()
+ .Any(s => s.type == "IMAGE" && !string.IsNullOrEmpty(s.imageRef));
+ }
+
+ return false;
+ }
+
+ public static string FilterName(string name)
+ {
+ if (name.StartsWith("!") || name.StartsWith("#"))
+ return name.Substring(1);
+ return name;
+ }
+
+ public static string GetNodeTypeName (this FigmaNode node)
+ {
+ if (string.IsNullOrEmpty(node.name))
+ return string.Empty;
+
+ var name = FilterName (node.name);
+ var index = name.IndexOf (' ');
+ if (index > -1 && index < name.Length - 1) {
+ name = name.Substring (0,index);
+ }
+ return name;
+ }
+
+ static string RemoveIllegalCharacters (string name)
+ {
+ name = name.Replace ("-", "");
+ return name;
+ }
+
+ public static bool TryGetCodeViewName (this FigmaNode node, out string customName)
+ {
+ if (node.TryGetNodeCustomName (out customName)) {
+ customName = RemoveIllegalCharacters (customName);
+ return true;
+ };
+ customName = RemoveIllegalCharacters (node.name);
+ return false;
+ }
+
+ public static string GetClassName (this FigmaNode node)
+ {
+ var name = node.name;
+ var index = name.IndexOf ('\"');
+ if (index > -1 && index < name.Length - 1) {
+ name = name.Substring (index + 1);
+ index = name.IndexOf ('\"');
+ if (index > -1 && index < name.Length) {
+ name = name.Substring (0, index);
+
+ return name
+ .Replace (" ", string.Empty)
+ .Replace (".", string.Empty);
+ }
+ }
+
+ name = node.name;
+ //HACK: we need to fix this
+ index = name.IndexOf (' ');
+ if (index > -1 && index < name.Length - 1) {
+ name = name.Substring (index + 1);
+
+ //names cannot be only integers, we get default name
+ if (int.TryParse (name, out _))
+ name = node.name;
+ }
+
+ return name
+ .Replace (" ", string.Empty)
+ .Replace (".", string.Empty);
+ }
+
+ public static T FindNativeViewByName(this Services.ViewRenderService rendererService, string name)
+ {
+ foreach (var node in rendererService.NodesProcessed)
+ {
+ if (node.View.NativeObject is T && node.Node.name == name)
+ {
+ return (T)node.View.NativeObject;
+ }
+ }
+ return default(T);
+ }
+
+ public static IEnumerable FindNativeViewsByName(this Services.ViewRenderService rendererService, string name)
+ {
+ foreach (var node in rendererService.NodesProcessed)
+ {
+ if (node.View.NativeObject is T && node.Node.name == name)
+ {
+ yield return (T)node.View.NativeObject;
+ }
+ }
+ }
+
+ public static IEnumerable FindNativeViewsStartsWith(this Services.ViewRenderService rendererService, string name, StringComparison stringComparison = StringComparison.InvariantCultureIgnoreCase)
+ {
+ foreach (var node in rendererService.NodesProcessed)
+ {
+ if (node.View.NativeObject is T && node.Node.name.StartsWith(name, stringComparison))
+ {
+ yield return (T)node.View.NativeObject;
+ }
+ }
+ }
+ }
+}
diff --git a/src/FigmaSharp/Extensions/StringExtensions.cs b/src/FigmaSharp/Extensions/StringExtensions.cs
new file mode 100644
index 0000000..79ec596
--- /dev/null
+++ b/src/FigmaSharp/Extensions/StringExtensions.cs
@@ -0,0 +1,38 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp
+{
+ public static class StringExtensions
+ {
+ public static string ToCamelCase(this string s)
+ {
+ if (string.IsNullOrEmpty(s))
+ return string.Empty;
+ if (s.Length == 1)
+ return char.ToUpper(s[0]).ToString();
+ return char.ToUpper(s[0]) + s.Substring(1);
+ }
+ }
+}
diff --git a/src/FigmaSharp/FigmaFile/FigmaFile.cs b/src/FigmaSharp/FigmaFile/FigmaFile.cs
new file mode 100644
index 0000000..4ee68d9
--- /dev/null
+++ b/src/FigmaSharp/FigmaFile/FigmaFile.cs
@@ -0,0 +1,121 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using FigmaSharp.Converters;
+using FigmaSharp.Models;
+using FigmaSharp.PropertyConfigure;
+using FigmaSharp.Services;
+using FigmaSharp.Views;
+
+namespace FigmaSharp
+{
+ ///
+ /// Use FigmaFile to load a FigmaDocument from a .figma file bundled in your project.
+ ///
+ [Obsolete ("Not used anymore")]
+ public class FigmaFile : IFigmaFile
+ {
+ public const string FigmaPackageId = "FigmaPackageId";
+ public const string FigmaNodeCustomName = "FigmaNodeCustomName";
+
+ ///
+ /// Gets the figma images.
+ ///
+ /// The figma images.
+ public List FigmaImages { get; private set; }
+
+ ///
+ /// Gets the document.
+ ///
+ /// The document.
+ public FigmaFileResponse Document => figmaLocalFileProvider.Response;
+
+ ///
+ /// Gets the content view.
+ ///
+ /// The content view.
+ public IView ContentView { get; private set; }
+
+ readonly ViewRenderService rendererService;
+ readonly AssemblyResourceNodeProvider figmaLocalFileProvider;
+
+ string file;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// File.
+ /// Figma view converters.
+ public FigmaFile (string file, NodeConverter[] figmaViewConverters, ViewPropertyConfigureBase propertyConfigure)
+ {
+ this.file = file;
+
+ ContentView = AppContext.Current.CreateEmptyView();
+ FigmaImages = new List();
+
+ if (propertyConfigure == null)
+ propertyConfigure = AppContext.Current.GetViewPropertyConfigure();
+
+ var assembly = System.Reflection.Assembly.GetCallingAssembly();
+ figmaLocalFileProvider = new AssemblyResourceNodeProvider(assembly, file);
+ rendererService = new ViewRenderService(figmaLocalFileProvider, figmaViewConverters, propertyConfigure);
+
+ }
+
+ ///
+ /// Reload the specified includeImages.
+ ///
+ public void Reload ()
+ {
+ LoggingService.LogInfo($"Loading views..");
+ try
+ {
+ FigmaImages.Clear();
+ rendererService. Start(file, ContentView);
+
+ var mainNodes = rendererService.NodesProcessed
+ .Where(s => s.ParentView == null)
+ .ToArray();
+
+ new StoryboardLayoutManager().Run(ContentView, rendererService);
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError($"[FIGMA.FILE] Error reading resource", ex);
+ }
+ }
+
+ ///
+ /// Initializes the component.
+ ///
+ public void InitializeComponent ()
+ {
+ Reload ();
+ }
+ }
+}
diff --git a/src/FigmaSharp/FigmaFile/IFigmaFile.cs b/src/FigmaSharp/FigmaFile/IFigmaFile.cs
new file mode 100644
index 0000000..e03c6d8
--- /dev/null
+++ b/src/FigmaSharp/FigmaFile/IFigmaFile.cs
@@ -0,0 +1,41 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System.Collections.Generic;
+
+using FigmaSharp.Models;
+using FigmaSharp.Views;
+
+namespace FigmaSharp
+{
+ public interface IFigmaFile
+ {
+ List FigmaImages { get; }
+ FigmaFileResponse Document { get; }
+
+ void InitializeComponent();
+
+ void Reload ();
+ }
+}
diff --git a/src/FigmaSharp/FigmaPackage/FigmaAssemblyManifest.cs b/src/FigmaSharp/FigmaPackage/FigmaAssemblyManifest.cs
new file mode 100644
index 0000000..3b8d2fe
--- /dev/null
+++ b/src/FigmaSharp/FigmaPackage/FigmaAssemblyManifest.cs
@@ -0,0 +1,32 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp
+{
+ public class FigmaAssemblyManifest
+ {
+ public string version { get; set; }
+ public string platform { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/FigmaSharp/FigmaPackage/FigmaManifest.cs b/src/FigmaSharp/FigmaPackage/FigmaManifest.cs
new file mode 100644
index 0000000..baed8b0
--- /dev/null
+++ b/src/FigmaSharp/FigmaPackage/FigmaManifest.cs
@@ -0,0 +1,92 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+using Newtonsoft.Json;
+
+namespace FigmaSharp
+{
+ public class FigmaManifest
+ {
+ [ManifestDescription ("File Id")]
+ public string FileId { get; set; }
+
+ [ManifestDescription("Namespace")]
+ public string Namespace { get; set; }
+
+ [ManifestDescription ("Document Title")]
+ public string DocumentTitle { get; set; }
+
+ [ManifestDescription ("Document Version")]
+ public string DocumentVersion { get; set; }
+
+ [ManifestDescription("Document Last Modified")]
+ public string DocumentLastModified { get; set; }
+
+ [ManifestDescription ("Conversion Date")]
+ public DateTime Date { get; set; }
+
+ [ManifestDescription ("Remote Api Version")]
+ public string RemoteApiVersion { get; set; }
+
+ [ManifestDescription ("FigmaSharp Api Version")]
+ public string ApiVersion { get; set; }
+
+ ManifestDescription GetManifestDescription (string propertyName)
+ {
+ var attribute = this.GetType ().GetProperty (propertyName).GetCustomAttributes (true).OfType ().FirstOrDefault ();
+ return attribute;
+ }
+
+ public void ToComment (StringBuilder builder)
+ {
+ string timestamp = Date.ToString("r");
+
+ builder.AppendLine ($"// This file was auto-generated using");
+ builder.AppendLine ($"// FigmaSharp {ApiVersion} and Figma API {RemoteApiVersion} on {timestamp}");
+ builder.AppendLine ($"//");
+ builder.AppendLine ($"// Document title: {DocumentTitle}");
+ builder.AppendLine ($"// Document version: {DocumentVersion}");
+ builder.AppendLine ($"// Document URL: https://figma.com/file/{FileId}");
+ builder.AppendLine ($"// Namespace: {Namespace}");
+ builder.AppendLine ($"//");
+ builder.AppendLine ($"// Changes to this file may cause incorrect behavior");
+ builder.AppendLine ($"// and will be lost if the code is regenerated.");
+ }
+
+ public static FigmaManifest FromFilePath (string filePath)
+ {
+ return JsonConvert.DeserializeObject (File.ReadAllText (filePath));
+ }
+
+ public void Save (string filePath)
+ {
+ File.WriteAllText (filePath, JsonConvert.SerializeObject (this, Formatting.Indented));
+ }
+ }
+}
diff --git a/src/FigmaSharp/FigmaPackage/ManifestDescription.cs b/src/FigmaSharp/FigmaPackage/ManifestDescription.cs
new file mode 100644
index 0000000..908a208
--- /dev/null
+++ b/src/FigmaSharp/FigmaPackage/ManifestDescription.cs
@@ -0,0 +1,36 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp
+{
+ [System.AttributeUsage(System.AttributeTargets.Property)]
+ public class ManifestDescription : System.Attribute
+ {
+ public string Description { get; }
+ public ManifestDescription(string description)
+ {
+ this.Description = description;
+ }
+ }
+}
diff --git a/src/FigmaSharp/FigmaSharp.csproj b/src/FigmaSharp/FigmaSharp.csproj
new file mode 100644
index 0000000..5c57157
--- /dev/null
+++ b/src/FigmaSharp/FigmaSharp.csproj
@@ -0,0 +1,44 @@
+
+
+ net6.0;net6.0-android;net6.0-ios;net6.0-maccatalyst
+ $(TargetFrameworks);net6.0-windows10.0.19041.0
+
+
+
+
+
+ bin\Release\netstandard2.0\FigmaSharp.xml
+
+
+ bin\Debug\netstandard2.0\FigmaSharp.xml
+ 1701;1702;1591
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ False
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FigmaSharp/Helpers/ResourceHelper.cs b/src/FigmaSharp/Helpers/ResourceHelper.cs
new file mode 100644
index 0000000..8832645
--- /dev/null
+++ b/src/FigmaSharp/Helpers/ResourceHelper.cs
@@ -0,0 +1,40 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Helpers
+{
+ public static class ResourceHelper
+ {
+ const string specialChar = "-";
+ public static string FromLocalResourceNameToUrlResourceName (string data)
+ {
+ return data.Replace(":", specialChar);
+ }
+
+ public static string FromUrlResourceNameToLocalResourceName (string data)
+ {
+ return data.Replace("_", specialChar);
+ }
+ }
+}
diff --git a/src/FigmaSharp/Helpers/WebApiHelper.cs b/src/FigmaSharp/Helpers/WebApiHelper.cs
new file mode 100644
index 0000000..2a1b6c6
--- /dev/null
+++ b/src/FigmaSharp/Helpers/WebApiHelper.cs
@@ -0,0 +1,130 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+
+using FigmaSharp.Models;
+using FigmaSharp.Services;
+using Newtonsoft.Json;
+
+namespace FigmaSharp.Helpers
+{
+ public static class WebApiHelper
+ {
+ const string prefix = "/file/";
+ const char urlSeparatorChar = '/';
+
+ public static bool TryParseFileUrl (string link, out string fileId)
+ {
+ if (string.IsNullOrEmpty(link))
+ {
+ fileId = string.Empty;
+ return false;
+ }
+
+ if (Uri.TryCreate (link, UriKind.Absolute, out var result))
+ {
+ try
+ {
+ string path = result.AbsolutePath;
+ if (path.StartsWith(prefix))
+ {
+ string id = path.Substring(prefix.Length);
+ if (id.Contains(urlSeparatorChar))
+ id = id.Substring(0, id.IndexOf(urlSeparatorChar));
+ fileId = id;
+ return true;
+ }
+ }
+ catch
+ {
+ }
+ }
+
+ fileId = link;
+ return false;
+ }
+
+ public static string GetManifestResource (Assembly assembly, string resource)
+ {
+ if (assembly == null)
+ {
+ assembly = Assembly.GetEntryAssembly();
+ }
+ try
+ {
+ //TODO: not safe
+ var fullResourceName = string.Format("{0}.{1}", assembly.GetName().Name, resource);
+ var resources = assembly.GetManifestResourceNames();
+ using (var stream = assembly.GetManifestResourceStream(fullResourceName))
+ {
+ if (stream == null)
+ {
+ LoggingService.LogWarning("Resource '{0}' not found in assembly '{1}'", resource, assembly.FullName);
+ return null;
+ }
+ using (TextReader tr = new StreamReader(stream))
+ {
+ return tr.ReadToEnd();
+ };
+ }
+ }
+ catch (System.Exception ex)
+ {
+ LoggingService.LogError(string.Format("[FIGMA] Cannot read resource '{0}' in assembly '{1}'", resource), ex);
+ return null;
+ }
+ }
+
+ public static FigmaFileResponse GetFigmaResponseFromFileContent (string figmaContent)
+ {
+ return JsonConvert.DeserializeObject (figmaContent, new FigmaResponseConverter ());
+ }
+
+ public static FigmaFileVersionResponse GetFigmaResponseFromFileVersionContent (string figmaVersionContent)
+ {
+ return JsonConvert.DeserializeObject (figmaVersionContent);
+ }
+
+ public static string GetUrlContent(string url, string version)
+ {
+ try
+ {
+ var wc = new System.Net.WebClient();
+ byte[] raw = wc.DownloadData(url);
+
+ string webData = System.Text.Encoding.UTF8.GetString(raw);
+ return webData;
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError("[FIGMA] Error.", ex);
+ }
+ return string.Empty;
+ }
+ }
+}
diff --git a/src/FigmaSharp/IFigmaDelegate.cs b/src/FigmaSharp/IFigmaDelegate.cs
new file mode 100644
index 0000000..08341eb
--- /dev/null
+++ b/src/FigmaSharp/IFigmaDelegate.cs
@@ -0,0 +1,55 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Reflection;
+
+using FigmaSharp.Converters;
+using FigmaSharp.PropertyConfigure;
+using FigmaSharp.Views;
+
+namespace FigmaSharp
+{
+ public interface IFigmaDelegate
+ {
+ bool IsVerticalAxisFlipped { get; }
+
+ IView CreateEmptyView();
+ NodeConverter[] GetFigmaConverters();
+
+ IImage GetImage(string url);
+ string GetSvgData(string url);
+
+ IImage GetImageFromFilePath(string filePath);
+ //FigmaResponse GetFigmaResponseFromContent(string template);
+ string GetManifestResource(Assembly assembly, string file);
+
+ IImage GetImageFromManifest(Assembly assembly, string imageRef);
+ IImageView GetImageView(IImage image);
+ void BeginInvoke(Action handler);
+
+ CodePropertyConfigureBase GetCodePropertyConfigure();
+ ViewPropertyConfigureBase GetViewPropertyConfigure();
+ }
+}
diff --git a/src/FigmaSharp/Properties/AssemblyInfo.cs b/src/FigmaSharp/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..76d9c6e
--- /dev/null
+++ b/src/FigmaSharp/Properties/AssemblyInfo.cs
@@ -0,0 +1,18 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+[assembly: InternalsVisibleTo ("FigmaSharp.Controls")]
+[assembly: InternalsVisibleTo ("FigmaSharp.Controls.Cocoa")]
+[assembly: InternalsVisibleTo ("FigmaSharp.Cocoa")]
+[assembly: InternalsVisibleTo ("MonoDevelop.Figma")]
+[assembly: InternalsVisibleTo ("FigmaSharp.Tests")]
+[assembly: InternalsVisibleTo ("FigmaSharpApp")]
+
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
diff --git a/src/FigmaSharp/PropertyConfigure/CodePropertyConfigureBase.cs b/src/FigmaSharp/PropertyConfigure/CodePropertyConfigureBase.cs
new file mode 100644
index 0000000..1c8a419
--- /dev/null
+++ b/src/FigmaSharp/PropertyConfigure/CodePropertyConfigureBase.cs
@@ -0,0 +1,34 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Converters;
+using FigmaSharp.Services;
+
+namespace FigmaSharp.PropertyConfigure
+{
+ public abstract class CodePropertyConfigureBase
+ {
+ public abstract string ConvertToCode (string propertyName, CodeNode currentNode, CodeNode parentNode, NodeConverter converter, CodeRenderService rendererService);
+ }
+}
diff --git a/src/FigmaSharp/PropertyConfigure/PropertyNames.cs b/src/FigmaSharp/PropertyConfigure/PropertyNames.cs
new file mode 100644
index 0000000..58e6185
--- /dev/null
+++ b/src/FigmaSharp/PropertyConfigure/PropertyNames.cs
@@ -0,0 +1,35 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.PropertyConfigure
+{
+ public static class PropertyNames
+ {
+ public const string Size = "Size";
+ public const string Position = "Position";
+ public const string AddChild = "AddChild";
+ public const string Frame = "Frame";
+ public const string Constraints = "Constraints";
+ }
+}
diff --git a/src/FigmaSharp/PropertyConfigure/ViewPropertyConfigureBase.cs b/src/FigmaSharp/PropertyConfigure/ViewPropertyConfigureBase.cs
new file mode 100644
index 0000000..fff4d5a
--- /dev/null
+++ b/src/FigmaSharp/PropertyConfigure/ViewPropertyConfigureBase.cs
@@ -0,0 +1,34 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Converters;
+using FigmaSharp.Services;
+
+namespace FigmaSharp.PropertyConfigure
+{
+ public abstract class ViewPropertyConfigureBase
+ {
+ public abstract void Configure(string propertyName, ViewNode currentNode, ViewNode parentNode, NodeConverter converter, ViewRenderService rendererService);
+ }
+}
diff --git a/src/FigmaSharp/Resources.cs b/src/FigmaSharp/Resources.cs
new file mode 100644
index 0000000..f010640
--- /dev/null
+++ b/src/FigmaSharp/Resources.cs
@@ -0,0 +1,37 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp
+{
+ public class Resources
+ {
+ public static class Ids
+ {
+ public static class Conversion
+ {
+ public static string NameIdentifier => "[NAME]";
+ }
+ }
+ }
+}
diff --git a/src/FigmaSharp/Resources/AssemblyInfo.cs b/src/FigmaSharp/Resources/AssemblyInfo.cs
new file mode 100644
index 0000000..78e11f8
--- /dev/null
+++ b/src/FigmaSharp/Resources/AssemblyInfo.cs
@@ -0,0 +1,7 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo ("FigmaSharp.Cocoa")]
+[assembly: InternalsVisibleTo("FigmaSharp.Wpf")]
+[assembly: InternalsVisibleTo("FigmaSharp.iOS")]
+[assembly: InternalsVisibleTo("FigmaSharp.WinForms")]
+[assembly: InternalsVisibleTo("FigmaSharp.Android")]
diff --git a/src/FigmaSharp/Services/CodeRenderServiceOptions.cs b/src/FigmaSharp/Services/CodeRenderServiceOptions.cs
new file mode 100644
index 0000000..a678b96
--- /dev/null
+++ b/src/FigmaSharp/Services/CodeRenderServiceOptions.cs
@@ -0,0 +1,43 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Services
+{
+ public class RenderServiceOptions
+ {
+ public bool TranslateLabels { get; set; }
+ }
+
+ public class CodeRenderServiceOptions : RenderServiceOptions
+ {
+ public bool RendersConstructorFirstElement { get; set; }
+
+ public bool ScanChildren { get; set; } = true;
+
+ public bool ShowComments { get; set; } = true;
+ public bool ShowAddChild { get; set; } = true;
+ public bool ShowSize { get; set; } = true;
+ public bool ShowConstraints { get; set; } = true;
+ }
+}
diff --git a/src/FigmaSharp/Services/CodeRendererService.cs b/src/FigmaSharp/Services/CodeRendererService.cs
new file mode 100644
index 0000000..489812e
--- /dev/null
+++ b/src/FigmaSharp/Services/CodeRendererService.cs
@@ -0,0 +1,343 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using FigmaSharp.Converters;
+using FigmaSharp.Models;
+using FigmaSharp.PropertyConfigure;
+
+namespace FigmaSharp.Services
+{
+ public interface ICodeRenderService
+ {
+ CodeRenderServiceOptions Options { get; }
+ void Clear();
+ void GetCode(StringBuilder builder, CodeNode node, CodeNode parent = null, CodeRenderServiceOptions currentRendererOptions = null, ITranslationService translateService = null);
+ bool NodeRendersVar(CodeNode currentNode, CodeNode parentNode);
+ bool NeedsRenderConstructor(CodeNode node, CodeNode parent);
+ string GetTranslatedText(FigmaText text);
+ string GetTranslatedText(string text, bool textCondition = true);
+ INodeProvider NodeProvider { get; }
+ }
+
+ public class CodeRenderService : RenderService, ICodeRenderService
+ {
+ internal const string DefaultViewName = "view";
+
+ internal CodePropertyConfigureBase codePropertyConverter;
+
+ public CodeRenderService (INodeProvider figmaProvider, NodeConverter[] nodeConverters,
+ CodePropertyConfigureBase codePropertyConverter, ITranslationService translationService = null) : base (figmaProvider, nodeConverters, translationService)
+ {
+ this.codePropertyConverter = codePropertyConverter;
+ }
+
+ NodeConverter GetConverter (CodeNode node, List converters)
+ {
+ foreach (var customViewConverter in converters) {
+ if (customViewConverter.CanCodeConvert (node.Node)) {
+ return customViewConverter;
+ }
+ }
+ return null;
+ }
+
+ public CodeRenderServiceOptions Options
+ {
+ get => (CodeRenderServiceOptions)baseOptions;
+ internal set => baseOptions = value;
+ }
+
+ internal CodeNode ParentMainNode { get; set; }
+ internal CodeNode MainNode { get; set; }
+
+ public bool IsMainNode (FigmaNode figmaNode) => MainNode != null && figmaNode == MainNode?.Node;
+
+ readonly internal List Nodes = new List();
+
+ public virtual void Clear ()
+ {
+ SetOptions(null);
+ ParentMainNode = null;
+ MainNode = null;
+ }
+
+ public void GetCode (StringBuilder builder, CodeNode node, CodeNode parent = null, CodeRenderServiceOptions currentRendererOptions = null, ITranslationService translateService = null)
+ {
+ //in first level we clear all identifiers
+ if (parent == null) {
+ if (MainNode == null) {
+
+ //clear all nodes
+ Nodes.Clear();
+
+ identifiers.Clear ();
+ OnStartGetCode ();
+
+ //we initialize
+
+ SetOptions(currentRendererOptions ?? new CodeRenderServiceOptions());
+
+ TranslationService = translateService ?? TranslationService ?? new DefaultTranslationService();
+
+ //we store our main node
+ MainNode = node;
+ ParentMainNode = parent;
+ }
+ }
+
+ if (node != null)
+ Nodes.Add(node);
+
+ CodeNode calculatedParentNode = null;
+ NodeConverter converter = null;
+
+ var isNodeSkipped = IsNodeSkipped (node);
+
+ //on node skipped we don't render
+ if (!isNodeSkipped) {
+ //if (figmaProvider.RendersProperties (node)) {
+ converter = GetNodeConverter(node);
+
+ if (converter != null) {
+ if (!node.HasName) {
+
+ if (!TryGetCodeViewName (node, parent, converter, out string identifier)) {
+ identifier = DefaultViewName;
+ }
+
+ //we store our name to don't generate dupplicates
+ var lastIndex = GetLastInsertedIndex (identifier);
+ if (lastIndex >= 0) {
+ identifiers.Remove (identifier);
+ }
+ lastIndex++;
+
+ node.Name = identifier;
+ if (lastIndex > 0) {
+ node.Name += lastIndex;
+ }
+ identifiers.Add (identifier, lastIndex);
+ }
+
+ if (Options.ShowComments)
+ {
+ builder.AppendLine();
+ builder.AppendLine($"// View: {node.Name}");
+ builder.AppendLine($"// NodeName: {node.Node.name}");
+ builder.AppendLine($"// NodeType: {node.Node.type}");
+ builder.AppendLine($"// NodeId: {node.Node.id}");
+ }
+
+ OnPreConvertToCode (builder, node, parent, converter, codePropertyConverter);
+ //we generate our code and replace node name
+
+ var code = converter.ConvertToCode (node, parent, this);
+ builder.AppendLineIfValue (code.Replace (Resources.Ids.Conversion.NameIdentifier, node.Name));
+ OnPostConvertToCode (builder, node, parent, converter, codePropertyConverter);
+
+ //TODO: this could be removed to converters base
+ if (Options.ShowAddChild && RendersAddChild(node, parent, this))
+ {
+ builder.AppendLineIfValue(codePropertyConverter.ConvertToCode(PropertyNames.AddChild, node, parent, converter, this));
+ OnChildAdded(builder, node, parent, converter, codePropertyConverter);
+ }
+
+ if (Options.ShowSize && RendersSize(node, parent, this))
+ {
+ builder.AppendLineIfValue(codePropertyConverter.ConvertToCode(PropertyNames.Frame, node, parent, converter, this));
+ OnFrameSet(builder, node, parent, converter, codePropertyConverter);
+ }
+
+ if (Options.ShowConstraints && RendersConstraints(node, parent, this))
+ {
+ builder.AppendLineIfValue(codePropertyConverter.ConvertToCode(PropertyNames.Constraints, node, parent, converter, this));
+ }
+
+ calculatedParentNode = node;
+ } else {
+ //without a converter we don't have any view created, we need to attach to the parent view
+ calculatedParentNode = parent;
+ }
+ }
+ else
+ {
+ //if a node is skipped because is the first node we want to set as parent view
+ if (node == MainNode)
+ {
+ calculatedParentNode = node;
+ }
+ }
+
+ //without converter we scan the children automatically
+ var navigateChild = Options.ScanChildren && (converter?.ScanChildren (node.Node) ?? true);
+ if (navigateChild && HasChildrenToRender (node)) {
+ foreach (var item in GetChildrenToRender (node)) {
+ var figmaNode = new CodeNode(item, parent: node);
+ GetCode (builder, figmaNode, calculatedParentNode);
+ }
+ }
+
+ if (MainNode == node) {
+ //first loop
+ Clear ();
+ }
+ }
+
+ public NodeConverter GetNodeConverter (CodeNode node)
+ {
+ var converter = GetConverter(node, CustomConverters);
+ if (converter == null)
+ converter = GetConverter(node, DefaultConverters);
+ return converter;
+ }
+
+ protected virtual bool RendersConstraints(CodeNode node, CodeNode parent, CodeRenderService figmaCodeRendererService)
+ {
+ return !((node != null && node == MainNode) || node.Node is FigmaCanvas || node.Node.Parent is FigmaCanvas);
+ }
+
+ protected virtual bool RendersSize(CodeNode node, CodeNode parent, CodeRenderService figmaCodeRendererService)
+ {
+ return true;
+ }
+
+ protected virtual bool RendersAddChild(CodeNode node, CodeNode parent, CodeRenderService figmaCodeRendererService)
+ {
+ return true;
+ }
+
+ protected virtual void OnStartGetCode ()
+ {
+
+ }
+
+ protected virtual void OnPreConvertToCode (StringBuilder builder, CodeNode node, CodeNode parent, NodeConverter converter, CodePropertyConfigureBase codePropertyConverter)
+ {
+
+ }
+
+ public bool NodeRendersVar (CodeNode currentNode, CodeNode parentNode)
+ {
+ if (currentNode.Node.GetNodeTypeName () == "mastercontent") {
+ return false;
+ }
+
+ return !currentNode.Node.TryGetNodeCustomName(out var _);
+
+ }
+
+ protected virtual void OnPostConvertToCode (StringBuilder builder, CodeNode node, CodeNode parent, NodeConverter converter, CodePropertyConfigureBase codePropertyConverter)
+ {
+
+ }
+
+ protected virtual void OnChildAdded (StringBuilder builder, CodeNode node, CodeNode parent, NodeConverter converter, CodePropertyConfigureBase codePropertyConverter)
+ {
+
+ }
+
+ protected virtual void OnFrameSet (StringBuilder builder, CodeNode node, CodeNode parent, NodeConverter converter, CodePropertyConfigureBase codePropertyConverter)
+ {
+
+ }
+
+ const string init = "Figma";
+ const string end = "Converter";
+ const string ViewIdentifier = "View";
+
+ protected virtual bool TryGetCodeViewName (CodeNode node, CodeNode parent, NodeConverter converter, out string identifier)
+ {
+ try {
+ identifier = converter.GetType().Name;
+ if (identifier.StartsWith (init)) {
+ identifier = identifier.Substring (init.Length);
+ }
+
+ if (identifier.EndsWith (end)) {
+ identifier = identifier.Substring (0, identifier.Length - end.Length);
+ }
+
+ identifier = char.ToLower (identifier[0]) + identifier.Substring (1) + ViewIdentifier;
+
+ return true;
+ } catch (Exception) {
+ identifier = null;
+ return false;
+ }
+ }
+
+ internal int GetLastInsertedIndex (string identifier)
+ {
+ if (!identifiers.TryGetValue (identifier, out int data)) {
+ return -1;
+ }
+ return data;
+ }
+
+ #region Rendering Iteration
+
+ public virtual bool NeedsRenderConstructor (CodeNode node, CodeNode parent)
+ {
+ if (parent != null
+ && IsMainNode (parent.Node)
+ && (Options?.RendersConstructorFirstElement ?? false)
+ )
+ return false;
+ else {
+ return true;
+ }
+ }
+
+ internal virtual bool IsMainViewContainer (CodeNode node)
+ {
+ return true;
+ }
+
+ internal virtual FigmaNode[] GetChildrenToRender (CodeNode node)
+ {
+ if (node.Node is IFigmaNodeContainer nodeContainer) {
+ return nodeContainer.children;
+ }
+ return new FigmaNode[0];
+ }
+
+ internal virtual bool HasChildrenToRender (CodeNode node)
+ {
+ return node.Node is IFigmaNodeContainer;
+ }
+
+ internal virtual bool IsNodeSkipped (CodeNode node)
+ {
+ return false;
+ }
+
+ #endregion
+
+ Dictionary identifiers = new Dictionary ();
+ }
+}
diff --git a/src/FigmaSharp/Services/ITranslationService.cs b/src/FigmaSharp/Services/ITranslationService.cs
new file mode 100644
index 0000000..ed1ba29
--- /dev/null
+++ b/src/FigmaSharp/Services/ITranslationService.cs
@@ -0,0 +1,36 @@
+// Authors:
+// jmedrano
+//
+// Copyright (C) 2020 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+namespace FigmaSharp.Services
+{
+ public interface ITranslationService
+ {
+ string GetTranslatedStringText(string text);
+ }
+
+ public class DefaultTranslationService : ITranslationService
+ {
+ public string GetTranslatedStringText(string text) => text;
+ }
+}
diff --git a/src/FigmaSharp/Services/LoggingService.cs b/src/FigmaSharp/Services/LoggingService.cs
new file mode 100644
index 0000000..aca6ee6
--- /dev/null
+++ b/src/FigmaSharp/Services/LoggingService.cs
@@ -0,0 +1,135 @@
+// Authors:
+// Matt Ward
+//
+// Copyright (C) 2021 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Text;
+
+namespace FigmaSharp.Services
+{
+ public enum LogLevel
+ {
+ Fatal = 1,
+ Error = 2,
+ Warn = 4,
+ Info = 8,
+ Debug = 16,
+ }
+
+ public interface ILogger
+ {
+ void Log(LogLevel level, string message);
+ }
+
+ public static class LoggingService
+ {
+ static ILogger logger = new DefaultLogger();
+
+ public static void RegisterDefaultLogger(ILogger logger)
+ {
+ LoggingService.logger = logger;
+ }
+
+ public static void LogInfo(string message)
+ {
+ logger.Log(LogLevel.Info, message);
+ }
+
+ public static void LogInfo(string format, object arg0)
+ {
+ logger.Log(LogLevel.Info, string.Format(format, arg0));
+ }
+
+ public static void LogInfo(string format, object arg0, object arg1)
+ {
+ logger.Log(LogLevel.Info, string.Format(format, arg0, arg1));
+ }
+
+ public static void LogInfo(string format, object arg0, object arg1, object arg2)
+ {
+ logger.Log(LogLevel.Info, string.Format(format, arg0, arg1, arg2));
+ }
+
+ public static void LogInfo(string format, params object[] args)
+ {
+ logger.Log(LogLevel.Info, string.Format(format, args));
+ }
+
+ public static void LogError(string message)
+ {
+ logger.Log(LogLevel.Error, message);
+ }
+
+ public static void LogError(string format, object arg0)
+ {
+ logger.Log(LogLevel.Error, string.Format(format, arg0));
+ }
+
+ public static void LogError(string message, Exception ex)
+ {
+ if (ex == null)
+ {
+ logger.Log(LogLevel.Error, message);
+ }
+ else
+ {
+ var exceptionText = new StringBuilder();
+ exceptionText.AppendLine(message);
+ exceptionText.Append(ex);
+
+ logger.Log(LogLevel.Error, exceptionText.ToString());
+ }
+ }
+
+ public static void LogWarning(string message)
+ {
+ logger.Log(LogLevel.Warn, message);
+ }
+
+ public static void LogWarning(string format, object arg0)
+ {
+ logger.Log(LogLevel.Warn, string.Format(format, arg0));
+ }
+
+ public static void LogWarning(string format, object arg0, object arg1)
+ {
+ logger.Log(LogLevel.Warn, string.Format(format, arg0, arg1));
+ }
+
+ class DefaultLogger : ILogger
+ {
+ public void Log(LogLevel level, string message)
+ {
+ switch (level)
+ {
+ case LogLevel.Info:
+ Console.WriteLine(message);
+ break;
+ default:
+ Console.WriteLine("{0}: {1}", level.ToString().ToUpper(), message);
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/src/FigmaSharp/Services/ModuleService.cs b/src/FigmaSharp/Services/ModuleService.cs
new file mode 100644
index 0000000..b9b8726
--- /dev/null
+++ b/src/FigmaSharp/Services/ModuleService.cs
@@ -0,0 +1,262 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Reflection;
+using System.IO;
+
+using Newtonsoft.Json;
+
+using FigmaSharp.Converters;
+using FigmaSharp.PropertyConfigure;
+
+namespace FigmaSharp.Services
+{
+ public class PlatformCustomViewConverter
+ {
+ public PlatformCustomViewConverter (string platform, NodeConverter converter)
+ {
+ Platform = platform;
+ Converter = converter;
+ }
+
+ public string Platform { get; private set; }
+ public NodeConverter Converter { get; private set; }
+ }
+
+ public class PlatformFigmaCodePropertyConverter
+ {
+ public PlatformFigmaCodePropertyConverter (string platform, CodePropertyConfigureBase converter)
+ {
+ Platform = platform;
+ Converter = converter;
+ }
+
+ public string Platform { get; private set; }
+ public CodePropertyConfigureBase Converter { get; private set; }
+ }
+
+ public static class ModuleService
+ {
+ public static class Platform
+ {
+ public static string MAC = "mac";
+ public static string iOS = "ios";
+ public static string WinForms = "winforms";
+ public static string Gtk = "gtk";
+ }
+
+ public static List Converters = new List();
+ public static List CodePropertyConverters = new List ();
+
+ public static void LoadModules (string directory)
+ {
+ LoggingService.LogInfo("Loading all directory modules from {0}", directory);
+ if (!Directory.Exists(directory))
+ {
+ LoggingService.LogError("[{0}] Error. Directory not found.", directory);
+ return;
+ }
+
+ foreach (var dir in System.IO.Directory.EnumerateDirectories(directory))
+ {
+ LoadModuleDirectory(dir);
+ }
+ }
+
+ public static void LoadModuleDirectory (string directory)
+ {
+ LoggingService.LogInfo("Loading module directory: {0}", directory);
+
+ if (!Directory.Exists(directory))
+ {
+ LoggingService.LogError("[{0}] Error. Directory not found.", directory);
+ return;
+ }
+
+ var manifestFilePath = Path.Combine(directory, "figma.manifest");
+ if (!File.Exists(manifestFilePath))
+ {
+ LoggingService.LogError("Error figma.manifest not found in directory '{0}'", directory);
+ return;
+ }
+
+ LoggingService.LogInfo("Loading figma.manifest in {0} ...", manifestFilePath);
+
+ var file = File.ReadAllText (manifestFilePath);
+ var manifest = JsonConvert.DeserializeObject(file);
+
+ LoggingService.LogInfo("Version: {0}", manifest.version);
+ LoggingService.LogInfo("Platform: {0}", manifest.platform);
+
+ var enumeratedFiles = Directory.EnumerateFiles(directory, "*.dll").ToArray();
+ LoadModule(manifest.platform, enumeratedFiles);
+ }
+
+ public static void LoadModule(string platform, params string[] filePaths)
+ {
+ Dictionary instanciableTypes = new Dictionary();
+
+ LoggingService.LogInfo("Loading {0}...", string.Join(",", filePaths));
+
+ foreach (var file in filePaths)
+ {
+ if (!File.Exists (file))
+ {
+ LoggingService.LogError("[{0}] Error. File not found.", file);
+ continue;
+ }
+
+ var fileName = Path.GetFileName(file);
+ LoggingService.LogInfo("[{0}] Found.", fileName);
+ try
+ {
+ var assembly = Assembly.LoadFile(file);
+ instanciableTypes.Add(assembly, file);
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError(string.Format("[{0}] Error loading", fileName), ex);
+ }
+ }
+
+ foreach (var assemblyTypes in instanciableTypes)
+ {
+ ProcessConverters(assemblyTypes.Key, platform);
+ ProcessAddChildConverters(assemblyTypes.Key, platform);
+ ProcessCodePositionConverters (assemblyTypes.Key, platform);
+ }
+
+ LoggingService.LogInfo("Finished.");
+ }
+
+ public static void ProcessConverters (Assembly assembly, string platform)
+ {
+ try
+ {
+ //we get all the type converters from the selected assembly
+ var interfaceType = typeof(NodeConverter);
+ var types = assembly.GetTypes()
+ .Where(interfaceType.IsAssignableFrom);
+
+ foreach (var type in types)
+ {
+ if (type.GetTypeInfo().IsAbstract)
+ {
+ LoggingService.LogInfo("[{0}] Skipping {1} (abstract class).", assembly, type);
+ continue;
+ }
+ LoggingService.LogInfo("[{0}] Creating instance {1}...", assembly, type);
+ try
+ {
+ if (Activator.CreateInstance(type) is NodeConverter element)
+ Converters.Add(new PlatformCustomViewConverter(platform, element));
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError("[FIGMA] Error", ex);
+ }
+ LoggingService.LogInfo("[{0}] Loaded.", type);
+ }
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError("[FIGMA] Error", ex);
+ }
+ }
+
+ public static void ProcessAddChildConverters(Assembly assembly, string platform)
+ {
+ try
+ {
+ //we get all the type converters from the selected assembly
+ var interfaceType = typeof(CodePropertyConfigureBase);
+ var types = assembly.GetTypes()
+ .Where(interfaceType.IsAssignableFrom);
+
+ foreach (var type in types)
+ {
+ if (type.GetTypeInfo().IsAbstract)
+ {
+ LoggingService.LogInfo("[{0}] Skipping {1} (abstract class).", assembly, type);
+ continue;
+ }
+ Console.WriteLine("[{0}] Creating instance {1}...", assembly, type);
+ try
+ {
+ if (Activator.CreateInstance(type) is CodePropertyConfigureBase element)
+ CodePropertyConverters.Add(new PlatformFigmaCodePropertyConverter(platform, element));
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError("[FIGMA] Error", ex);
+ }
+ LoggingService.LogInfo("[{0}] Loaded.", type);
+ }
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError("[FIGMA] Error", ex);
+ }
+ }
+
+ public static void ProcessCodePositionConverters(Assembly assembly, string platform)
+ {
+ try
+ {
+ //we get all the type converters from the selected assembly
+ var interfaceType = typeof(CodePropertyConfigureBase);
+ var types = assembly.GetTypes()
+ .Where(interfaceType.IsAssignableFrom);
+
+ foreach (var type in types)
+ {
+ if (type.GetTypeInfo().IsAbstract)
+ {
+ LoggingService.LogInfo("[{0}] Skipping {1} (abstract class).", assembly, type);
+ continue;
+ }
+ Console.WriteLine("[{0}] Creating instance {1}...", assembly, type);
+ try
+ {
+ if (Activator.CreateInstance(type) is CodePropertyConfigureBase element)
+ CodePropertyConverters.Add(new PlatformFigmaCodePropertyConverter (platform, element));
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError("[FIGMA] Error", ex);
+ }
+ LoggingService.LogInfo("[{0}] Loaded.", type);
+ }
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError("[FIGMA] Error", ex);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/FigmaSharp/Services/Nodes/CodeNode.cs b/src/FigmaSharp/Services/Nodes/CodeNode.cs
new file mode 100644
index 0000000..0fc7bdb
--- /dev/null
+++ b/src/FigmaSharp/Services/Nodes/CodeNode.cs
@@ -0,0 +1,46 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Services
+{
+ public class CodeNode : ContainerNode
+ {
+ public CodeNode (FigmaNode node, string name = null, bool isClass = false, CodeNode parent = null) : base (node)
+ {
+ Name = name;
+ IsClass = isClass;
+ Parent = parent;
+ }
+
+ public bool HasName => !string.IsNullOrEmpty (Name);
+
+ public CodeNode Parent { get; private set; }
+
+ public string Name { get; set; }
+
+ public bool IsClass { get; private set; }
+ }
+}
diff --git a/src/FigmaSharp/Services/Nodes/ContainerNode.cs b/src/FigmaSharp/Services/Nodes/ContainerNode.cs
new file mode 100644
index 0000000..85efd99
--- /dev/null
+++ b/src/FigmaSharp/Services/Nodes/ContainerNode.cs
@@ -0,0 +1,38 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Services
+{
+ public class ContainerNode
+ {
+ public FigmaNode Node { get; private set; }
+
+ public ContainerNode(FigmaNode figmaNode)
+ {
+ Node = figmaNode;
+ }
+ }
+}
diff --git a/src/FigmaSharp/Services/Nodes/ViewNode.cs b/src/FigmaSharp/Services/Nodes/ViewNode.cs
new file mode 100644
index 0000000..46b9a58
--- /dev/null
+++ b/src/FigmaSharp/Services/Nodes/ViewNode.cs
@@ -0,0 +1,44 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Models;
+using FigmaSharp.Views;
+
+namespace FigmaSharp.Services
+{
+ public class ViewNode : ContainerNode
+ {
+ public ViewNode (FigmaNode figmaNode, IView view, ViewNode parentView = null) : base (figmaNode)
+ {
+ View = view;
+ ParentView = parentView;
+ }
+
+ public IView View { get; set; }
+ public ViewNode ParentView { get; set; }
+
+ public override string ToString() =>
+ base.Node?.ToString() ?? base.ToString();
+ }
+}
\ No newline at end of file
diff --git a/src/FigmaSharp/Services/Providers/AssemblyResourceNodeProvider.cs b/src/FigmaSharp/Services/Providers/AssemblyResourceNodeProvider.cs
new file mode 100644
index 0000000..f7a16fd
--- /dev/null
+++ b/src/FigmaSharp/Services/Providers/AssemblyResourceNodeProvider.cs
@@ -0,0 +1,70 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+
+using FigmaSharp.Helpers;
+using FigmaSharp.Views;
+
+namespace FigmaSharp.Services
+{
+ public class AssemblyResourceNodeProvider : NodeProvider
+ {
+ public Assembly Assembly { get; set; }
+
+ public AssemblyResourceNodeProvider(Assembly assembly, string file)
+ {
+ Assembly = assembly;
+ File = file;
+ }
+
+ public override string GetContentTemplate(string file)
+ {
+ return AppContext.Current.GetManifestResource(Assembly, file);
+ }
+
+ public override void OnStartImageLinkProcessing(List imageFigmaNodes)
+ {
+ LoggingService.LogInfo($"Loading images..");
+
+ if (imageFigmaNodes.Count > 0)
+ {
+ foreach (var vector in imageFigmaNodes)
+ {
+ var recoveredKey = ResourceHelper.FromLocalResourceNameToUrlResourceName(vector.Node.id);
+ var image = AppContext.Current.GetImageFromManifest(Assembly, recoveredKey);
+ if (image != null && vector.View is IImageView imageView)
+ {
+ imageView.Image = image;
+ }
+ }
+ }
+
+ LoggingService.LogInfo("Ended image link processing");
+ OnImageLinkProcessed();
+ }
+ }
+}
diff --git a/src/FigmaSharp/Services/Providers/FileNodeProvider.cs b/src/FigmaSharp/Services/Providers/FileNodeProvider.cs
new file mode 100644
index 0000000..811bfbb
--- /dev/null
+++ b/src/FigmaSharp/Services/Providers/FileNodeProvider.cs
@@ -0,0 +1,90 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using FigmaSharp.Helpers;
+using FigmaSharp.Views;
+
+namespace FigmaSharp.Services
+{
+ public class FileNodeProvider : NodeProvider
+ {
+ public FileNodeProvider(string resourcesDirectory)
+ {
+ ResourcesDirectory = resourcesDirectory;
+ }
+
+ public override string GetContentTemplate(string file)
+ {
+ return System.IO.File.ReadAllText(file);
+ }
+
+ public string ResourcesDirectory { get; set; }
+
+ public string ImageFormat { get; set; } = ".png";
+
+ public override void OnStartImageLinkProcessing(List imageFigmaNodes)
+ {
+ //not needed in local files
+ LoggingService.LogInfo($"Loading images..");
+
+ if (imageFigmaNodes.Count > 0)
+ {
+ foreach (var vector in imageFigmaNodes)
+ {
+ try
+ {
+ var recoveredKey = ResourceHelper.FromLocalResourceNameToUrlResourceName(vector.Node.id);
+ string filePath = Path.Combine(ResourcesDirectory, string.Concat(recoveredKey, ImageFormat));
+
+ if (!System.IO.File.Exists(filePath))
+ {
+ throw new FileNotFoundException(filePath);
+ }
+
+ if (vector.View is IImageView imageView)
+ {
+ var image = AppContext.Current.GetImageFromFilePath(filePath);
+ imageView.Image = image;
+ }
+ }
+ catch (FileNotFoundException ex)
+ {
+ LoggingService.LogError("[FIGMA.RENDERER] Resource '{0}' not found.", ex);
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError("[FIGMA.RENDERER] Error.", ex);
+ }
+ }
+ }
+
+ LoggingService.LogInfo("Ended image link processing");
+ OnImageLinkProcessed();
+ }
+ }
+}
diff --git a/src/FigmaSharp/Services/Providers/INodeProvider.cs b/src/FigmaSharp/Services/Providers/INodeProvider.cs
new file mode 100644
index 0000000..37818a6
--- /dev/null
+++ b/src/FigmaSharp/Services/Providers/INodeProvider.cs
@@ -0,0 +1,61 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Services
+{
+ public interface INodeProvider
+ {
+ string File { get; }
+ event EventHandler ImageLinksProcessed;
+ List Nodes { get; }
+ FigmaFileResponse Response { get; }
+
+ string GetContentTemplate(string file);
+
+ Task LoadAsync(string file);
+ void Load(string file);
+ void Save(string filePath);
+ void OnStartImageLinkProcessing(List imageVectors);
+
+ FigmaNode[] GetMainGeneratedLayers();
+
+ FigmaNode FindByFullPath(string fullPath);
+ FigmaNode FindByPath(params string[] path);
+ FigmaNode FindByName(string nodeName);
+
+ bool TryGetMainInstance (FigmaInstance figmaInstance, out FigmaInstance outInstance);
+ bool TryGetMainComponent (FigmaInstance figmaInstance, out FigmaComponentEntity outInstance);
+ bool TryGetStyle(string fillStyleValue, out FigmaStyle style);
+
+ bool RendersAsImage(FigmaNode figmaNode);
+ void SaveResourceFiles(string destinationDirectory, string format, IImageNodeRequest[] downloadImages);
+ }
+}
+
\ No newline at end of file
diff --git a/src/FigmaSharp/Services/Providers/NodeProvider.cs b/src/FigmaSharp/Services/Providers/NodeProvider.cs
new file mode 100644
index 0000000..4d59554
--- /dev/null
+++ b/src/FigmaSharp/Services/Providers/NodeProvider.cs
@@ -0,0 +1,298 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+using FigmaSharp.Helpers;
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Services
+{
+ public abstract class NodeProvider : INodeProvider
+ {
+ public event EventHandler ImageLinksProcessed;
+
+ public FigmaFileResponse Response { get; protected set; }
+ public List Nodes { get; } = new List ();
+
+ public bool ImageProcessed;
+
+ internal void OnImageLinkProcessed ()
+ {
+ ImageProcessed = true;
+ ImageLinksProcessed?.Invoke (this, new EventArgs ());
+ }
+
+ public string File { get; set; }
+
+ public Task LoadAsync(string file) => Task.Run(() => Load(file));
+
+ public void Load (string file)
+ {
+ this.File = file;
+
+ ImageProcessed = false;
+ try {
+ Nodes.Clear ();
+
+ var contentTemplate = GetContentTemplate (file);
+
+ //parse the json into a model format
+ Response = WebApiHelper.GetFigmaResponseFromFileContent (contentTemplate);
+
+ //proceses all the views recursively
+ foreach (var item in Response.document.children)
+ ProcessNodeRecursively (item, null);
+ } catch (System.Net.WebException ex) {
+ if (!AppContext.Current.IsApiConfigured)
+ LoggingService.LogError ($"Cannot connect to Figma server: TOKEN not configured.", ex);
+ else
+ LoggingService.LogError ($"Cannot connect to Figma server: wrong TOKEN?", ex);
+ } catch (Exception ex) {
+ LoggingService.LogError ($"Error reading remote resources. Ensure you added NewtonSoft nuget or cannot parse the to json?", ex);
+ }
+ }
+
+ public FigmaNode[] GetMainGeneratedLayers ()
+ {
+ return GetMainLayers (s => s.TryGetNodeCustomName (out var customName) && !s.name.StartsWith("#") && !s.name.StartsWith("//")).ToArray();
+ }
+
+ public IEnumerable GetMainLayers (Func action = null)
+ {
+ return Nodes.Where(s => s.Parent is FigmaCanvas && (action?.Invoke (s) ?? true));
+ }
+
+ public FigmaNode FindByFullPath (string fullPath)
+ {
+ return FindByPath (fullPath.Split ('/'));
+ }
+
+ public FigmaNode GetParentNode (FigmaNode node)
+ => Nodes.FirstOrDefault(s => s is IFigmaNodeContainer container && container.children.Any(c => c == node));
+
+ ///
+ /// Finds a node using the path of the views, returns null in case of no data
+ ///
+ ///
+ ///
+ public FigmaNode FindByPath (params string[] path)
+ {
+ if (path.Length == 0) {
+ return null;
+ }
+
+ FigmaNode figmaNode = null;
+ for (int i = 0; i < path.Length; i++) {
+ if (i == 0)
+ figmaNode = Nodes.FirstOrDefault (s => s.name == path[i]);
+ else
+ figmaNode = Nodes.FirstOrDefault (s => s.name == path[i] && s.Parent.id == figmaNode.id);
+
+ if (figmaNode == null)
+ return null;
+ }
+ return figmaNode;
+ }
+
+ public FigmaNode FindById(string id)
+ {
+ return Nodes.FirstOrDefault(s => s.id == id);
+ }
+
+ public FigmaNode FindByName (string name)
+ {
+ var quotedName = string.Format ("\"{0}\"", name);
+ var found = Nodes.FirstOrDefault (s => s.name.Contains (quotedName));
+ if (found != null) {
+ return found;
+ }
+ return Nodes.FirstOrDefault (s => s.name == name);
+ }
+
+ public FigmaNode FindByCustomName(string name)
+ {
+ var found = Nodes.FirstOrDefault(s => s.TryGetNodeCustomName (out var customName) && customName == name);
+ if (found != null)
+ {
+ return found;
+ }
+ return Nodes.FirstOrDefault(s => s.name == name);
+ }
+
+ void ProcessNodeRecursively (FigmaNode node, FigmaNode parent)
+ {
+ node.Parent = parent;
+ Nodes.Add (node);
+
+ if (node is FigmaInstance instance) {
+ if (Response.components.TryGetValue (instance.componentId, out var figmaComponent))
+ instance.Component = figmaComponent;
+ }
+
+ if (node is IFigmaNodeContainer nodeContainer) {
+ foreach (var item in nodeContainer.children)
+ ProcessNodeRecursively (item, node);
+ }
+ }
+
+ public abstract string GetContentTemplate (string file);
+
+ public abstract void OnStartImageLinkProcessing (List imageFigmaNodes);
+
+ public void Save (string filePath)
+ {
+ Response.Save (filePath);
+ }
+
+ public bool TryGetMainInstance(FigmaInstance nodeInstance, out FigmaInstance result)
+ {
+ //Get the instance
+ var componentNode = GetMainGeneratedLayers();
+ foreach (var item in componentNode)
+ {
+ if (item is FigmaInstance figmaInstance && figmaInstance.id == nodeInstance.componentId) {
+ result = figmaInstance;
+ return true;
+ }
+ }
+ result = null;
+ return false;
+ }
+
+ public bool TryGetMainComponent(FigmaInstance nodeInstance, out FigmaComponentEntity result)
+ {
+ //Get the instance
+ var componentNode = GetMainGeneratedLayers();
+ foreach (var item in componentNode)
+ {
+ if (item is FigmaComponentEntity figmaInstance && figmaInstance.id == nodeInstance.componentId)
+ {
+ result = figmaInstance;
+ return true;
+ }
+ }
+ result = null;
+ return false;
+ }
+
+ public bool TryGetStyle(string fillStyleValue, out FigmaStyle style)
+ {
+ return Response.styles.TryGetValue(fillStyleValue, out style);
+ }
+
+ #region Image Resources
+
+ public virtual void SaveResourceFiles(string destinationDirectory, string format, IImageNodeRequest[] downloadImages)
+ {
+ if (!Directory.Exists(destinationDirectory))
+ throw new DirectoryNotFoundException(destinationDirectory);
+
+ foreach (var downloadImage in downloadImages)
+ {
+ foreach (var imageScale in downloadImage.Scales)
+ {
+ if (string.IsNullOrEmpty(imageScale.Url))
+ continue;
+
+ string customNodeName = downloadImage.GetOutputFileName(imageScale.Scale);
+ var fileName = string.Concat(customNodeName, format);
+ var fullPath = Path.Combine(destinationDirectory, fileName);
+
+ if (System.IO.File.Exists(fullPath))
+ System.IO.File.Delete(fullPath);
+
+ try
+ {
+ using (System.Net.WebClient client = new System.Net.WebClient())
+ client.DownloadFile(new Uri(imageScale.Url), fullPath);
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError("[FIGMA] Error.", ex);
+ }
+ }
+ };
+ }
+
+ public virtual bool RendersAsImage (FigmaNode figmaNode)
+ {
+ if (figmaNode.ContainsSourceImage ())
+ return true;
+
+ if (figmaNode is IFigmaImage figmaImage && figmaImage.HasImage())
+ return true;
+
+ return false;
+ }
+
+ public virtual bool SearchImageChildren(FigmaNode figmaNode) => true;
+
+ public IEnumerable SearchImageNodes (FigmaNode mainNode)
+ {
+ if (mainNode is FigmaInstance)
+ yield break;
+
+ if (RendersAsImage (mainNode))
+ {
+ yield return mainNode;
+ yield break;
+ }
+
+ //we don't want iterate on children
+ if (!SearchImageChildren (mainNode))
+ yield break;
+
+ if (mainNode is IFigmaNodeContainer nodeContainer)
+ {
+ foreach (var item in nodeContainer.children)
+ {
+ foreach (var resultItems in SearchImageNodes (item))
+ {
+ yield return resultItems;
+ }
+ }
+ } else if (mainNode is FigmaDocument document)
+ {
+ foreach (var item in document.children)
+ {
+ foreach (var resultItems in SearchImageNodes(item))
+ {
+ yield return resultItems;
+ }
+ }
+ }
+ }
+
+ public IEnumerable SearchImageNodes() => SearchImageNodes(Response.document);
+
+ public virtual IImageNodeRequest CreateEmptyImageNodeRequest(FigmaNode node) => new ImageNodeRequest(node);
+
+ #endregion
+ }
+}
diff --git a/src/FigmaSharp/Services/Providers/RemoteNodeProvider.cs b/src/FigmaSharp/Services/Providers/RemoteNodeProvider.cs
new file mode 100644
index 0000000..59d5eb8
--- /dev/null
+++ b/src/FigmaSharp/Services/Providers/RemoteNodeProvider.cs
@@ -0,0 +1,186 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+using FigmaSharp.Models;
+using FigmaSharp.Views;
+
+namespace FigmaSharp.Services
+{
+ public class RemoteNodeProvider : NodeProvider
+ {
+ public FigmaFileVersion Version { get; set; }
+
+ public override string GetContentTemplate(string file)
+ {
+ return AppContext.Api.GetContentFileAsync(new FigmaFileQuery(file, Version)).GetAwaiter().GetResult();
+ }
+
+ public IEnumerable GetKeys(List responses, string image)
+ {
+ foreach (var item in responses)
+ {
+ foreach (var keys in item.images.Where(s => s.Value == image))
+ {
+ yield return keys.Key;
+ }
+ }
+ }
+
+ void ProcessRemoteImages(List imageFigmaNodes, ImageFormat imageFormat)
+ {
+ try
+ {
+ var totalImages = imageFigmaNodes.Count();
+ //TODO: figma url has a limited character in urls we fixed the limit to 10 ids's for each call
+ var numberLoop = (totalImages / CallNumber) + 1;
+
+ //var imageCache = new Dictionary>();
+ List>> imageCacheResponse = new List>>();
+ LoggingService.LogInfo("Detected a total of {0} possible {1} images. ", totalImages, imageFormat);
+
+ var images = new List();
+ for (int i = 0; i < numberLoop; i++)
+ {
+ var vectors = imageFigmaNodes.Skip(i * CallNumber).Take(CallNumber);
+ LoggingService.LogInfo("[{0}/{1}] Processing Images ... {2} ", i, numberLoop, vectors.Count());
+ var ids = vectors.Select(s => CreateEmptyImageNodeRequest(s.Node))
+ .ToArray();
+
+ var figmaImageResponse = AppContext.Api.GetImagesAsync(File, ids, imageFormat).GetAwaiter().GetResult();
+ if (figmaImageResponse != null)
+ {
+ foreach (var image in figmaImageResponse.images)
+ {
+ if (image.Value == null)
+ {
+ continue;
+ }
+
+ var img = imageCacheResponse.FirstOrDefault(s => image.Value == s.Item1);
+ if (img?.Item1 != null)
+ {
+ img.Item2.Add(image.Key);
+ }
+ else
+ {
+ imageCacheResponse.Add(new Tuple>(image.Value, new List() { image.Key }));
+ }
+ }
+ }
+ }
+
+ //get images not dupplicates
+ LoggingService.LogInfo("Finished image to download {0}", images.Count);
+
+ if (imageFormat == ImageFormat.svg)
+ {
+ throw new NotImplementedException("svg not implemented");
+ //with all the keys now we get the dupplicated images
+ foreach (var imageUrl in imageCacheResponse)
+ {
+ var image = Helpers.WebApiHelper.GetUrlContent(imageUrl.Item1, null);
+
+ foreach (var figmaNodeId in imageUrl.Item2)
+ {
+ var vector = imageFigmaNodes.FirstOrDefault(s => s.Node.id == figmaNodeId);
+ LoggingService.LogInfo("[{0}:{1}:{2}] {3}...", vector.Node.GetType(), vector.Node.id, vector.Node.name, imageUrl);
+
+ if (vector != null && vector.View is ISvgView imageView)
+ {
+ AppContext.Current.BeginInvoke(() =>
+ {
+ imageView.Load(image);
+ });
+ }
+ LoggingService.LogInfo("OK \n");
+ }
+ }
+ }
+ else
+ {
+ //with all the keys now we get the dupplicated images
+ foreach (var imageUrl in imageCacheResponse)
+ {
+ var Image = AppContext.Current.GetImage(imageUrl.Item1);
+ foreach (var figmaNodeId in imageUrl.Item2)
+ {
+ var vector = imageFigmaNodes.FirstOrDefault(s => s.Node.id == figmaNodeId);
+ LoggingService.LogInfo("[{0}:{1}:{2}] {3}...", vector.Node.GetType(), vector.Node.id, vector.Node.name, imageUrl);
+
+ if (vector != null)
+ {
+ AppContext.Current.BeginInvoke(() =>
+ {
+ if (vector.View is IImageView imageView)
+ {
+ imageView.Image = Image;
+ }
+ else if (vector.View is IImageButton imageButton)
+ {
+ imageButton.Image = Image;
+ }
+ else
+ {
+ LoggingService.LogInfo("[{0}:{1}:{2}] Error cannot assign the image to the current view {3}", vector.Node.GetType(), vector.Node.id, vector.Node.name, vector.View.GetType().FullName);
+ }
+ });
+ }
+ LoggingService.LogInfo("OK \n");
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError("[FIGMA] Error.", ex);
+ }
+ }
+
+ public override void OnStartImageLinkProcessing(List imageFigmaNodes)
+ {
+ if (imageFigmaNodes.Count == 0)
+ {
+ OnImageLinkProcessed();
+ return;
+ }
+
+ Task.Run(() =>
+ {
+ //var svgImages = imageFigmaNodes.Where(s => s.View is ISvgView).ToList();
+ //if (svgImages.Count > 0)
+ // ProcessRemoteImages(svgImages, ImageFormat.svg);
+ var images = imageFigmaNodes.Where(s => !(s.View is ISvgView)).ToList();
+ if (images.Count > 0)
+ ProcessRemoteImages(images, ImageFormat.png);
+ OnImageLinkProcessed();
+ });
+ }
+ const int CallNumber = 250;
+ }
+}
diff --git a/src/FigmaSharp/Services/RenderService.cs b/src/FigmaSharp/Services/RenderService.cs
new file mode 100644
index 0000000..107c31e
--- /dev/null
+++ b/src/FigmaSharp/Services/RenderService.cs
@@ -0,0 +1,159 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System.Collections.Generic;
+using System.Linq;
+
+using FigmaSharp.Converters;
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Services
+{
+ public abstract class RenderService
+ {
+ protected readonly List DefaultConverters;
+ public readonly List CustomConverters;
+
+ protected RenderServiceOptions baseOptions;
+ public ITranslationService TranslationService { get; internal set; }
+
+ protected INodeProvider nodeProvider;
+ public INodeProvider NodeProvider => nodeProvider;
+
+ protected void SetOptions (RenderServiceOptions options)
+ {
+ baseOptions = options;
+ }
+
+ public RenderService(INodeProvider nodeProvider, NodeConverter[] nodeConverters, ITranslationService translationService = null)
+ {
+ this.TranslationService = translationService ?? new DefaultTranslationService ();
+ this.nodeProvider = nodeProvider;
+ DefaultConverters = nodeConverters.Where(s => s.IsLayer).ToList();
+ CustomConverters = nodeConverters.Where(s => !s.IsLayer).ToList();
+ }
+
+ public FigmaNode FindNodeByName(string name)
+ {
+ return nodeProvider.Nodes.FirstOrDefault(s => s.name == name);
+ }
+
+ public FigmaNode FindNodeById(string id)
+ {
+ return nodeProvider.Nodes.FirstOrDefault(s => s.id == id);
+ }
+
+ FigmaNode GetRecursively(string name, FigmaNode figmaNode)
+ {
+ if (figmaNode.name == name)
+ {
+ return figmaNode;
+ }
+
+ if (figmaNode is IFigmaNodeContainer container)
+ {
+ foreach (var item in container.children)
+ {
+ var node = GetRecursively(name, item);
+ if (node != null)
+ {
+ return node;
+ }
+ }
+ }
+ return null;
+ }
+
+ const string HasConstraintsParameter = "hasConstraints";
+ internal virtual bool HasConstraints(FigmaNode currentNode, NodeConverter converter)
+ {
+ if (currentNode.TryGetAttributeValue(HasConstraintsParameter, out var value))
+ {
+ if (value == "true")
+ return true;
+ if (value == "false")
+ return false;
+ }
+ return true;
+ }
+
+ internal virtual bool HasWidthConstraint(FigmaNode currentNode, NodeConverter converter)
+ {
+ return converter.HasWidthConstraint();
+ }
+
+ internal virtual bool HasHeightConstraint(FigmaNode currentNode, NodeConverter converter)
+ {
+ return converter.HasHeightConstraint();
+ }
+
+ internal virtual bool IsFlexibleVertical(ContainerNode currentNode, NodeConverter converter)
+ {
+ return converter.IsFlexibleVertical(currentNode.Node);
+ }
+
+ internal virtual bool IsFlexibleHorizontal(ContainerNode currentNode, NodeConverter converter)
+ {
+ return converter.IsFlexibleHorizontal(currentNode.Node);
+ }
+
+ public string GetTranslatedText(FigmaText text)
+ => GetTranslatedText(text.characters, text.visible);
+
+ public string GetTranslatedText(string text, bool textCondition = true)
+ {
+ //translation false
+ if (!textCondition)
+ return string.Empty;
+
+ text = text ?? string.Empty;
+ text = text.Replace(System.Environment.NewLine, "\\n");
+ if (baseOptions.TranslateLabels && TranslationService != null)
+ {
+ return TranslationService.GetTranslatedStringText(text);
+ }
+ return text;
+ }
+
+ public NodeConverter GetConverter(FigmaNode node)
+ {
+ var converter = GetProcessedConverter(node, CustomConverters);
+ if (converter == null)
+ converter = GetProcessedConverter(node, DefaultConverters);
+ return converter;
+ }
+
+ protected NodeConverter GetProcessedConverter(FigmaNode currentNode, IEnumerable customViewConverters)
+ {
+ foreach (var customViewConverter in customViewConverters)
+ {
+ if (customViewConverter.CanConvert(currentNode))
+ {
+ return customViewConverter;
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/FigmaSharp/Services/StoryboardLayoutManager.cs b/src/FigmaSharp/Services/StoryboardLayoutManager.cs
new file mode 100644
index 0000000..aa14952
--- /dev/null
+++ b/src/FigmaSharp/Services/StoryboardLayoutManager.cs
@@ -0,0 +1,95 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System.Linq;
+
+using FigmaSharp.Models;
+using FigmaSharp.PropertyConfigure;
+using FigmaSharp.Views;
+
+namespace FigmaSharp.Services
+{
+ public class StoryboardLayoutManager
+ {
+ public bool UsesConstraints { get; set; }
+ public int HorizontalMargins { get; set; } = 64;
+ public int VerticalMargins { get; set; } = 64;
+
+ public void Run(IView contentView, ViewRenderService rendererService)
+ {
+ var mainNodes = rendererService.NodesProcessed.Where(s => s.Node.Parent is FigmaCanvas)
+ .ToArray ();
+ Run(mainNodes, contentView, rendererService);
+ }
+
+ public void Run (ViewNode[] mainViews, IView contentView, ViewRenderService rendererService)
+ {
+ var orderedNodes = mainViews
+ .OrderBy(s => ((IAbsoluteBoundingBox)s.Node).absoluteBoundingBox.Left)
+ .ThenBy(s => ((IAbsoluteBoundingBox)s.Node).absoluteBoundingBox.Top)
+ .ToArray();
+
+ //We want know the background color of the figma camvas and apply to our scrollview
+ var firstNode = orderedNodes.FirstOrDefault();
+ if (firstNode == null)
+ return;
+
+ var canvas = firstNode.ParentView?.Node as FigmaCanvas;
+ if (canvas != null) {
+ //contentView.BackgroundColor = canvas.backgroundColor;
+ if (contentView.Parent is IScrollView scrollview) {
+ //scrollview.BackgroundColor = canvas.backgroundColor;
+
+ //we need correct current initial positioning
+ var rectangle = orderedNodes
+ .Select(s => s.Node)
+ .GetBoundRectangle();
+ scrollview.SetContentSize(rectangle.Width + VerticalMargins * 2, rectangle.Height + HorizontalMargins * 2);
+ }
+ }
+
+ if (UsesConstraints) {
+ foreach (var node in orderedNodes)
+ {
+ Converters.NodeConverter converter = rendererService.GetConverter(node.Node);
+ rendererService.PropertySetter.Configure(PropertyNames.Constraints,
+ node, node.ParentView, converter, rendererService);
+ }
+ } else {
+ var rectangle = orderedNodes
+ .Select(s => s.Node)
+ .GetBoundRectangle();
+
+ foreach (var node in orderedNodes)
+ {
+ //we need correct current initial positioning
+ if (node.Node is IAbsoluteBoundingBox box)
+ {
+ node.View.SetPosition(-rectangle.X + box.absoluteBoundingBox.X + VerticalMargins, -rectangle.Y + box.absoluteBoundingBox.Y + HorizontalMargins);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/FigmaSharp/Services/ViewRenderService.cs b/src/FigmaSharp/Services/ViewRenderService.cs
new file mode 100644
index 0000000..2f7242a
--- /dev/null
+++ b/src/FigmaSharp/Services/ViewRenderService.cs
@@ -0,0 +1,499 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+using FigmaSharp.Converters;
+using FigmaSharp.Models;
+using FigmaSharp.PropertyConfigure;
+using FigmaSharp.Views;
+
+namespace FigmaSharp.Services
+{
+ public class ViewRenderService : RenderService
+ {
+ public List NodesProcessed = new List();
+ public readonly List ImageVectors = new List();
+
+ protected IView container;
+
+ protected FigmaNode firstNode;
+
+ public ViewPropertyConfigureBase PropertySetter { get; }
+
+ public ViewRenderService(INodeProvider figmaProvider, NodeConverter[] figmaViewConverters = null, ITranslationService translationService = null) : this (figmaProvider, figmaViewConverters, AppContext.Current.GetViewPropertyConfigure (), translationService)
+ {
+
+ }
+
+ public ViewRenderService(INodeProvider figmaProvider, NodeConverter[] figmaViewConverters, ViewPropertyConfigureBase propertySetter, ITranslationService translationService = null) :
+ base(figmaProvider, figmaViewConverters ?? AppContext.Current.GetFigmaConverters (), translationService)
+ {
+ this.PropertySetter = propertySetter;
+ }
+
+ protected virtual bool SkipsNode(FigmaNode currentNode, ViewNode parent, ViewRenderServiceOptions options)
+ {
+ if (options != null && options.ToIgnore != null && options.ToIgnore.Contains(currentNode))
+ return true;
+ if (currentNode.IsRenderSkipped())
+ return true;
+ return false;
+ }
+
+ protected virtual bool NodeScansChildren(FigmaNode currentNode, NodeConverter converter, ViewRenderServiceOptions options)
+ {
+ if (converter == null)
+ return false;
+
+ if (!converter.ScanChildren(currentNode))
+ {
+ return false;
+ }
+
+ if (!options.ScanChildrenFromFigmaInstances && (currentNode is FigmaInstance || currentNode is FigmaComponentEntity))
+ {
+ return false;
+ }
+
+ // This will not be used by the ViewRenderService when SkipsNode returns true. Added here
+ // in case something calls NodeScansChildren directly.
+ if (currentNode.IsRenderSkipped())
+ return false;
+
+ return true;
+ }
+
+ public void ProcessFromNode(FigmaNode figmaNode, IView View, ViewRenderServiceOptions options)
+ {
+ try
+ {
+ var processedParentView = new ViewNode(figmaNode, View);
+ NodesProcessed.Add(processedParentView);
+
+ //in canvas we want calculate the bounds size
+ if (figmaNode is FigmaCanvas canvas)
+ {
+ canvas.absoluteBoundingBox = canvas.GetCurrentBounds();
+ }
+
+ if (figmaNode is FigmaCanvas || !options.GenerateMainView)
+ {
+ if (figmaNode is IFigmaNodeContainer container)
+ {
+ foreach (var item in container.children)
+ GenerateViewsRecursively(item, processedParentView, options);
+ }
+ }
+ else
+ {
+ GenerateViewsRecursively(figmaNode, processedParentView, options);
+ }
+
+ //Images
+ if (options.AreImageProcessed)
+ {
+ foreach (var processedNode in NodesProcessed)
+ {
+ if (NodeProvider.RendersAsImage(processedNode.Node))
+ {
+ ImageVectors.Add(processedNode);
+ }
+ }
+
+ nodeProvider.ImageLinksProcessed += FileProvider_ImageLinksProcessed;
+ nodeProvider.OnStartImageLinkProcessing(ImageVectors);
+ }
+
+ LoggingService.LogInfo("View generation finished.");
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError("Error reading resource" , ex);
+ }
+ }
+
+ private void FileProvider_ImageLinksProcessed(object sender, EventArgs e)
+ {
+ LoggingService.LogInfo($"Image Links ended.");
+ }
+
+ public void Refresh(ViewRenderServiceOptions options)
+ {
+ //on refresh we want clear results
+ ImageVectors.Clear();
+ NodesProcessed.Clear();
+
+ SetOptions(options);
+
+ LoggingService.LogInfo($"Reading successfull");
+
+ FigmaCanvas canvas;
+ if (options.StartPage >= 0 && options.StartPage <= nodeProvider.Response.document.children.Length)
+ {
+ canvas = nodeProvider.Response.document.children[options.StartPage];
+ }
+ else
+ {
+ canvas = nodeProvider.Response.document.children.FirstOrDefault();
+ }
+ ProcessFromNode(canvas, container, options);
+ }
+
+ #region Rendering
+
+ public void RenderInWindow(IWindow mainWindow, ViewRenderServiceOptions options = null)
+ {
+ var allCanvas = nodeProvider.Nodes
+ .OfType()
+ .ToArray();
+ if (allCanvas.Length == 0)
+ {
+ return;
+ }
+
+ var startPage = options != null ? options.StartPage : 0;
+ var canvas = allCanvas[startPage];
+ if (canvas != null)
+ {
+ canvas.absoluteBoundingBox = canvas.GetCurrentBounds();
+ RenderInWindow(mainWindow, canvas, options);
+ }
+ }
+
+ public void RenderInWindow(IWindow mainWindow, string nodeName, ViewRenderServiceOptions options = null)
+ {
+ var node = nodeProvider.Nodes
+ .FirstOrDefault(s =>s.name == nodeName || (s.TryGetNodeCustomName(out string name) && name == nodeName));
+
+ if (node == null)
+ throw new Exception($"nodename {nodeName} not found");
+ RenderInWindow(mainWindow, node, options);
+ }
+
+ public virtual void RenderInWindow(IWindow mainWindow, FigmaNode node, ViewRenderServiceOptions options = null)
+ {
+ if (node is IAbsoluteBoundingBox bounNode) {
+ mainWindow.Size = new Size(bounNode.absoluteBoundingBox.Width, bounNode.absoluteBoundingBox.Height);
+ }
+
+ if (options == null) {
+ options = new ViewRenderServiceOptions();
+ }
+
+ SetOptions(options);
+
+ ProcessFromNode(node, mainWindow.Content, options);
+
+ var processedNode = FindProcessedNodeById(node.id);
+ RecursivelyConfigureViews(processedNode, options);
+ }
+
+ public T RenderByFullPath (IView parent, ViewRenderServiceOptions options, string path) where T : IView
+ {
+ FigmaNode node = nodeProvider.FindByPath (path);
+ if (node == null)
+ return default (T);
+ return (T)RenderByNode (node, parent, options);
+ }
+
+ public T RenderByPath (ViewRenderServiceOptions options, IView parent, params string[] path) where T : IView
+ {
+ FigmaNode node = nodeProvider.FindByPath (path);
+ if (node == null)
+ return default (T);
+ return (T)RenderByNode (node, parent, options);
+ }
+
+ public IView RenderByNode(FigmaNode node, IView parent, ViewRenderServiceOptions options = null)
+ {
+ if (options == null)
+ options = new ViewRenderServiceOptions();
+
+ firstNode = node;
+
+ ProcessFromNode(node, parent, options);
+ var processedNode = FindProcessedNodeById (node.id);
+ RecursivelyConfigureViews (processedNode, options);
+
+ firstNode = null;
+
+ return processedNode.View;
+ }
+
+ public T RenderByNode(FigmaNode node, IView parent, ViewRenderServiceOptions options = null) where T : IView
+ {
+ return (T)RenderByNode(node, parent, options);
+ }
+
+ public T RenderByName (string figmaName, IView parent, ViewRenderServiceOptions options = null) where T : IView
+ {
+ var node = FindNodeByName (figmaName);
+ if (node == null)
+ return default (T);
+ return (T)RenderByNode (node, parent, options);
+ }
+
+ #endregion
+
+ #region Find view nodes
+
+ public T FindViewStartsWith(string name, StringComparison stringComparison = StringComparison.InvariantCultureIgnoreCase) where T : IView
+ {
+ foreach (var node in NodesProcessed)
+ {
+ if (node.View is T && node.Node.name.StartsWith(name, stringComparison))
+ {
+ return (T)node.View;
+ }
+ }
+ return default(T);
+ }
+
+ public T FindViewByName(string name) where T : IView
+ {
+ foreach (var node in NodesProcessed)
+ {
+ if (node.View is T && node.Node.name == name)
+ {
+ return (T)node.View;
+ }
+ }
+ return default(T);
+ }
+
+ public T FindViewByPath(params string[] path) where T : IView
+ {
+ var node = nodeProvider.FindByPath(path);
+ if (node == null)
+ return default(T);
+ var processed = NodesProcessed.FirstOrDefault(s => s.Node == node);
+ if (processed == null)
+ return default(T);
+
+ return (T)processed.View;
+ }
+
+ public IEnumerable FindViewsStartsWith(string name, StringComparison stringComparison = StringComparison.InvariantCultureIgnoreCase)
+ {
+ foreach (var node in NodesProcessed)
+ {
+ if (node.View is T && node.Node.name.StartsWith(name, stringComparison))
+ {
+ yield return (T)node.View;
+ }
+ }
+ }
+
+ public IEnumerable FindViewsByName(string name)
+ {
+ foreach (var node in NodesProcessed)
+ {
+ if (node.View is T && node.Node.name == name)
+ {
+ yield return (T)node.View;
+ }
+ }
+ }
+
+ public IView FindViewByName(string name)
+ {
+ foreach (var node in NodesProcessed)
+ {
+ if (node.Node.name == name)
+ {
+ return node.View;
+ }
+ }
+ return null;
+ }
+
+ public ViewNode FindProcessedNodeByName(string name)
+ {
+ return NodesProcessed.FirstOrDefault(s => s.Node.name == name);
+ }
+
+ public ViewNode FindProcessedNodeById(string Id)
+ {
+ return NodesProcessed.FirstOrDefault(s => s.Node.id == Id);
+ }
+
+ #endregion
+
+ protected void RecursivelyConfigureViews (ViewNode parentNode, ViewRenderServiceOptions options)
+ {
+ var children = NodesProcessed.Where(s => s.ParentView == parentNode);
+ foreach (var child in children)
+ {
+ if (child.View == null)
+ {
+ LoggingService.LogInfo("Node {0} has no view to process... skipping", child.Node);
+ continue;
+ }
+
+ var converter = GetConverter(child.Node);
+ if (converter != null)
+ {
+ if (RendersAddChild(child, parentNode, this))
+ PropertySetter.Configure(PropertyNames.AddChild, child, parentNode, converter, this);
+
+ if (RendersSize(child, parentNode, this))
+ PropertySetter.Configure(PropertyNames.Frame, child, parentNode, converter, this);
+
+ if (RendersConstraints(child, parentNode, this))
+ PropertySetter.Configure(PropertyNames.Constraints, child, parentNode, converter, this);
+ }
+
+ RecursivelyConfigureViews (child, options);
+ }
+ }
+
+ protected virtual bool RendersAddChild (ViewNode currentNode, ViewNode parent, RenderService rendererService)
+ {
+ return true;
+ }
+
+ protected virtual bool RendersConstraints (ViewNode currentNode,ViewNode parent, RenderService rendererService)
+ {
+ return !((currentNode != null && firstNode == currentNode.Node) || (currentNode.Node is FigmaCanvas || currentNode.Node.Parent is FigmaCanvas));
+ }
+
+ protected virtual bool RendersSize (ViewNode currentNode, ViewNode parent, RenderService rendererService)
+ {
+ return true;
+ }
+
+ public async Task StartAsync (string figmaName, IView container, ViewRenderServiceOptions options = null)
+ {
+ if (options == null) {
+ options = new ViewRenderServiceOptions();
+ }
+
+ LoggingService.LogInfo("[FigmaViewRenderer] Starting process..");
+ LoggingService.LogInfo($"Reading {figmaName} from resources..");
+
+ this.container = container;
+
+ try
+ {
+ if (options.LoadFileProvider) {
+ await nodeProvider.LoadAsync(figmaName ?? nodeProvider.File);
+ }
+
+ //we generate all the processed nodes
+ Refresh(options);
+
+ //we render only if there is a canvas and GenerateViews is enabled
+ var canvas = NodesProcessed.FirstOrDefault(s => s.Node is FigmaCanvas);
+ if (canvas != null && options.ConfigureViews)
+ {
+ RecursivelyConfigureViews(canvas, options);
+ }
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError("[FIGMA] Error reading resource", ex);
+ }
+ }
+
+ public void Start(string figmaName, IView container, ViewRenderServiceOptions options = null)
+ {
+ if (options == null) {
+ options = new ViewRenderServiceOptions();
+ }
+
+ LoggingService.LogInfo("[FigmaViewRenderer] Starting process..");
+ LoggingService.LogInfo($"Reading {figmaName} from resources..");
+
+ this.container = container;
+
+ try
+ {
+ if (options.LoadFileProvider)
+ nodeProvider.Load(figmaName ?? nodeProvider.File);
+
+ //we generate all the processed nodes
+ Refresh(options);
+
+ //we render only if there is a canvas and GenerateViews is enabled
+ var canvas = NodesProcessed.FirstOrDefault(s => s.Node is FigmaCanvas);
+ if (canvas != null && options.ConfigureViews) {
+ RecursivelyConfigureViews(canvas, options);
+ }
+ }
+ catch (Exception ex)
+ {
+ LoggingService.LogError("[FIGMA] Error reading resource.", ex);
+ }
+ }
+
+ //TODO: This
+ protected void GenerateViewsRecursively(FigmaNode currentNode, ViewNode parent, ViewRenderServiceOptions options)
+ {
+ LoggingService.LogInfo("[{0}.{1}] Processing {2}..", currentNode?.id, currentNode?.name, currentNode?.GetType());
+
+ //if (currentNode.name.StartsWith ("#") || currentNode.name.StartsWith ("//")) {
+ // LoggingService.LogInfo ("[{0}.{1}] Detected skipped flag in name.. Skipping...", currentNode?.id, currentNode?.name, currentNode?.GetType ());
+ // return;
+ //}
+
+ if (SkipsNode(currentNode, parent, options))
+ return;
+
+ var converter = GetConverter(currentNode);
+
+ ViewNode currentProcessedNode = null;
+ if (converter != null)
+ {
+ var currentView = options.IsToViewProcessed ? converter.ConvertToView(currentNode, parent, this) : null;
+ currentProcessedNode = new ViewNode(currentNode, currentView, parent);
+ NodesProcessed.Add(currentProcessedNode);
+ }
+ else
+ {
+ LoggingService.LogInfo("[{1}.{2}] There is no Converter for this type: {0}", currentNode.GetType(), currentNode.id, currentNode.name);
+ }
+
+ if (NodeScansChildren(currentNode, converter, options))
+ {
+ foreach (var item in GetCurrentChildren(currentNode, parent?.Node, converter, options))
+ {
+ GenerateViewsRecursively(item, currentProcessedNode ?? parent, options);
+ }
+ }
+ }
+
+ protected virtual IEnumerable GetCurrentChildren(FigmaNode currentNode, FigmaNode parentNode, NodeConverter converter,ViewRenderServiceOptions options)
+ {
+ if (currentNode is IFigmaNodeContainer nodeContainer)
+ {
+ return nodeContainer.children;
+ }
+ return Enumerable.Empty();
+ }
+ }
+}
diff --git a/src/FigmaSharp/Services/ViewRenderServiceOptions.cs b/src/FigmaSharp/Services/ViewRenderServiceOptions.cs
new file mode 100644
index 0000000..c756138
--- /dev/null
+++ b/src/FigmaSharp/Services/ViewRenderServiceOptions.cs
@@ -0,0 +1,49 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using FigmaSharp.Models;
+
+namespace FigmaSharp.Services
+{
+ public class ViewRenderServiceOptions : RenderServiceOptions
+ {
+ public bool IsToViewProcessed { get; set; } = true;
+ public bool AreImageProcessed { get; set; } = true;
+ public int StartPage { get; set; } = 0;
+
+ public FigmaNode[] ToIgnore { get; set; }
+ public bool LoadFileProvider { get; set; } = true;
+
+ public bool ConfigureViews { get; set; } = true;
+
+ public bool GenerateMainView { get; set; } = true;
+
+ public bool FrameInMainViews { get; set; } = false;
+
+ ///
+ /// Allows configure in rederer process all children subviews from FigmaInstances (Components)
+ ///
+ public bool ScanChildrenFromFigmaInstances { get; set; } = true;
+ }
+}
diff --git a/src/FigmaSharp/WebApi/FigmaApi.cs b/src/FigmaSharp/WebApi/FigmaApi.cs
new file mode 100644
index 0000000..e5706b5
--- /dev/null
+++ b/src/FigmaSharp/WebApi/FigmaApi.cs
@@ -0,0 +1,147 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Text;
+using System.Linq;
+using FigmaSharp.Helpers;
+using FigmaSharp.Models;
+using Newtonsoft.Json;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace FigmaSharp
+{
+ public class FigmaApi
+ {
+ internal string Token { get; set; }
+
+ //TODO: right now there is no way to detect changes in API
+ public Version Version { get; } = new Version (1, 0, 1);
+
+ #region Urls
+
+ string GetFigmaFileUrl (string fileId) => string.Format ("https://api.figma.com/v1/files/{0}", fileId);
+ string GetFigmaImageUrl (string fileId, params IImageNodeRequest[] imageIds) => string.Format ("https://api.figma.com/v1/images/{0}?ids={1}", fileId, string.Join (",", imageIds.Select(s => s.ResourceId).ToArray ()));
+ string GetFigmaFileVersionsUrl (string fileId) => string.Format ("{0}/versions", GetFigmaFileUrl (fileId));
+
+ #endregion
+
+ public async Task GetContentFileAsync (FigmaFileQuery figmaQuery)
+ {
+ var result = await GetContentUrlAsync (figmaQuery,
+ (e) => {
+ var queryUrl = GetFigmaFileUrl (figmaQuery.FileId);
+ if (e.Version != null) {
+ queryUrl += string.Format ("?version={0}", figmaQuery.Version);
+ }
+ return queryUrl;
+ }
+ );
+ return result;
+ }
+
+ public async Task GetContentFileVersionAsync (FigmaFileVersionQuery figmaQuery)
+ {
+ var result = await GetContentUrlAsync (figmaQuery, (e) => GetFigmaFileVersionsUrl (e.FileId));
+ return result;
+ }
+
+ public async Task GetFile (FigmaFileQuery figmaQuery)
+ {
+ var content = await GetContentFileAsync (figmaQuery);
+ return WebApiHelper.GetFigmaResponseFromFileContent (content);
+ }
+
+ public async Task GetFileVersionsAsync (FigmaFileVersionQuery figmaQuery)
+ {
+ var content = await GetContentFileVersionAsync (figmaQuery);
+ return WebApiHelper.GetFigmaResponseFromFileVersionContent (content);
+ }
+
+ #region Images
+
+ public async Task GetImagesAsync (string fileId, IImageNodeRequest[] resourceIds, ImageFormat format = ImageFormat.png, float scale = 2)
+ {
+ var currentIds = resourceIds.Select(s => s.ResourceId).ToArray();
+ var query = new FigmaImageQuery (fileId, resourceIds);
+ query.Scale = scale;
+ query.Format = format;
+ return await GetImageAsync (query);
+ }
+
+ public async Task ProcessDownloadImagesAsync (string fileId, IImageNodeRequest[] resourceIds, ImageFormat format = ImageFormat.png, float scale = 2)
+ {
+ var response = await GetImagesAsync(fileId, resourceIds, format, scale);
+ foreach (var image in response.images)
+ {
+ var resourceId = resourceIds.FirstOrDefault(s => s.ResourceId == image.Key);
+ if (resourceId != null)
+ resourceId.Scales.Add(new ImageScale(scale, image.Value));
+ }
+ }
+
+ public async Task GetImageAsync (FigmaImageQuery figmaQuery)
+ {
+ var result = await GetContentUrlAsync (figmaQuery,
+ (e) => {
+ var figmaImageUrl = GetFigmaImageUrl (figmaQuery.FileId, figmaQuery.Ids);
+ var stringBuilder = new StringBuilder (figmaImageUrl);
+
+ stringBuilder.Append (string.Format ("&format={0}", figmaQuery.Format));
+ stringBuilder.Append (string.Format ("&scale={0}", figmaQuery.Scale));
+
+ if (figmaQuery.Version != null) {
+ stringBuilder.Append (string.Format ("&version={0}", figmaQuery.Version));
+ }
+ return stringBuilder.ToString ();
+ }
+ );
+
+ return JsonConvert.DeserializeObject (result);
+ }
+
+ async Task GetContentUrlAsync (T figmaQuery, Func handler) where T : FigmaApiBaseQuery
+ {
+ var token = string.IsNullOrEmpty (figmaQuery.PersonalAccessToken) ?
+ Token : figmaQuery.PersonalAccessToken;
+
+ var query = handler (figmaQuery);
+
+ var client = new HttpClient();
+
+ client.DefaultRequestHeaders.Add ("Accept", "application/json");
+ client.DefaultRequestHeaders.Add("x-figma-token", token);
+
+ var response = await client.GetAsync(query + "?geometry=paths");
+ var content = await response.Content.ReadAsStringAsync();
+
+ var json = Newtonsoft.Json.Linq.JObject.Parse(content);
+
+ return json.ToString();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/FigmaSharp/WebApi/FigmaFileVersion.cs b/src/FigmaSharp/WebApi/FigmaFileVersion.cs
new file mode 100644
index 0000000..5e225a8
--- /dev/null
+++ b/src/FigmaSharp/WebApi/FigmaFileVersion.cs
@@ -0,0 +1,41 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+
+namespace FigmaSharp.Models
+{
+ public class FigmaFileVersion
+ {
+ public string id { get; set; }
+ public DateTime created_at { get; set; }
+ public string label { get; set; }
+ public string description { get; set; }
+ public FigmaUser user { get; set; }
+ public string thumbnail_url { get; set; }
+
+ public string CurrentName => !string.IsNullOrEmpty(label) ? label : created_at.ToString("g");
+ public bool IsNamed => !string.IsNullOrEmpty(label);
+ }
+}
diff --git a/src/FigmaSharp/WebApi/FigmaJsonConverter.cs b/src/FigmaSharp/WebApi/FigmaJsonConverter.cs
new file mode 100644
index 0000000..a436803
--- /dev/null
+++ b/src/FigmaSharp/WebApi/FigmaJsonConverter.cs
@@ -0,0 +1,154 @@
+// Authors:
+// Jose Medrano
+//
+// Copyright (C) 2018 Microsoft, Corp
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+
+using FigmaSharp.Models;
+
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace FigmaSharp
+{
+ public class FigmaResponseConverter : JsonConverter
+ {
+ public override bool CanConvert(Type objectType)
+ {
+ return typeof(FigmaNode).IsAssignableFrom(objectType) ||
+ typeof(FigmaComponent) == objectType ||
+ typeof(FigmaPoint) == objectType;
+ }
+
+ public override object ReadJson(JsonReader reader,
+ Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ JObject jsonObject = JObject.Load(reader);
+
+ object figmaObject = null;
+
+ if (!jsonObject.ContainsKey ("type") || string.IsNullOrEmpty (jsonObject["type"].Value ()))
+ {
+ if (objectType == typeof (FigmaPoint))
+ {
+ return jsonObject.ToObject();
+ }
+ if (objectType == typeof(FigmaComponent))
+ {
+ //var obj = jsonObject.ToObject();
+ serializer.MissingMemberHandling = MissingMemberHandling.Ignore;
+ serializer.NullValueHandling = NullValueHandling.Ignore;
+ figmaObject = jsonObject.ToObject ();
+ serializer.Populate (jsonObject.CreateReader (), figmaObject);
+ return figmaObject;
+ }
+
+ throw new NotImplementedException(objectType.ToString ());
+ }
+
+ if (jsonObject["type"].Value() == "DOCUMENT")
+ {
+ figmaObject = jsonObject.ToObject();
+ }
+ else if (jsonObject["type"].Value() == "CANVAS")
+ {
+ figmaObject = jsonObject.ToObject();
+ }
+ else if (jsonObject["type"].Value() == "SLICE")
+ {
+ figmaObject = jsonObject.ToObject();
+ }
+ else if (jsonObject["type"].Value() == "FRAME")
+ {
+ figmaObject = jsonObject.ToObject();
+ }
+ else if (jsonObject["type"].Value() == "VECTOR")
+ {
+ figmaObject = jsonObject.ToObject();
+ }
+ else if (jsonObject["type"].Value() == "TEXT")
+ {
+ figmaObject = jsonObject.ToObject();
+ return figmaObject;
+ }
+ else if (jsonObject["type"].Value() == "GROUP")
+ {
+ figmaObject = jsonObject.ToObject();
+ }
+ else if (jsonObject["type"].Value() == "RECTANGLE")
+ {
+ figmaObject = jsonObject.ToObject();
+ return figmaObject;
+ }
+ else if (jsonObject["type"].Value() == "LINE")
+ {
+ figmaObject = jsonObject.ToObject();
+ return figmaObject;
+ }
+ else if (jsonObject["type"].Value() == "ELLIPSE")
+ {
+ figmaObject = jsonObject.ToObject();
+ return figmaObject;
+ }
+ else if (jsonObject["type"].Value() == "STAR")
+ {
+ figmaObject = jsonObject.ToObject();
+ return figmaObject;
+ }
+ else if (jsonObject["type"].Value() == "REGULAR_POLYGON")
+ {
+ figmaObject = jsonObject.ToObject();
+ return figmaObject;
+ }
+ else if (jsonObject["type"].Value() == "INSTANCE")
+ {
+ figmaObject = jsonObject.ToObject();
+ }
+ else if (jsonObject["type"].Value() == "COMPONENT")
+ {
+ figmaObject = jsonObject.ToObject();
+ }
+ else if (jsonObject["type"].Value() == "BOOLEAN_OPERATION")
+ {
+ figmaObject = jsonObject.ToObject();
+ }
+ else if (jsonObject["type"].Value () == "BOOLEAN_OPERATION") {
+ figmaObject = jsonObject.ToObject ();
+ } else
+ {
+ return jsonObject.ToObject