diff --git a/Signum.Engine.Extensions/Chart/ChartColorLogic.cs b/Signum.Engine.Extensions/Chart/ChartColorLogic.cs index 21a5d8591b..dc6fc7dbef 100644 --- a/Signum.Engine.Extensions/Chart/ChartColorLogic.cs +++ b/Signum.Engine.Extensions/Chart/ChartColorLogic.cs @@ -4,9 +4,9 @@ namespace Signum.Engine.Chart; -public static class ChartColorLogic +public static class ColorPaletteLogic { - public static ResetLazy>> Colors = null!; + public static ResetLazy> ColorPaletteCache = null!; public static readonly int Limit = 360; @@ -14,161 +14,27 @@ internal static void Start(SchemaBuilder sb) { if (sb.NotDefined(MethodInfo.GetCurrentMethod())) { - sb.Include() + sb.Include() + .WithSave(ColorPaletteOperation.Save) + .WithDelete(ColorPaletteOperation.Delete) .WithQuery(() => cc => new { Entity = cc, - cc.Related, - cc.Color, + cc.Id, + cc.Type, + cc.CategoryName, + cc.Seed, }); - Colors = sb.GlobalLazy(() => - Database.Query() - .Select(cc => new { cc.Related.EntityType, cc.Related.Id, cc.Color }) - .AgGroupToDictionary(a => a.EntityType!, gr => gr.ToDictionary(a => a.Id, a => a.Color)), - new InvalidateWith(typeof(ChartColorEntity))); + ColorPaletteCache = sb.GlobalLazy(() => + Database.Query() + .ToDictionaryEx(cc => cc.Type.ToType()), + new InvalidateWith(typeof(ColorPaletteEntity))); } } - public static Dictionary Palettes = new Dictionary(){ - {"Category10", "#1f77b4 #ff7f0e #2ca02c #d62728 #9467bd #8c564b #e377c2 #7f7f7f #bcbd22 #17becf"}, - {"Category20", "#1f77b4 #aec7e8 #ff7f0e #ffbb78 #2ca02c #98df8a #d62728 #ff9896 #9467bd #c5b0d5 #8c564b #c49c94 #e377c2 #f7b6d2 #7f7f7f #c7c7c7 #bcbd22 #dbdb8d #17becf #9edae5"}, - {"Category20b", "#393b79 #5254a3 #6b6ecf #9c9ede #637939 #8ca252 #b5cf6b #cedb9c #8c6d31 #bd9e39 #e7ba52 #e7cb94 #843c39 #ad494a #d6616b #e7969c #7b4173 #a55194 #ce6dbd #de9ed6"}, - {"Category20c", "#3182bd #6baed6 #9ecae1 #c6dbef #e6550d #fd8d3c #fdae6b #fdd0a2 #31a354 #74c476 #a1d99b #c7e9c0 #756bb1 #9e9ac8 #bcbddc #dadaeb #636363 #969696 #bdbdbd #d9d9d9"}, - }; - - public static void CreateNewPalette(Type type, string palette) - { - AssertFewEntities(type); - - var dic = Database.RetrieveAllLite(type).Select(l => new ChartColorEntity { Related = (Lite)l }).ToDictionary(a => a.Related); - - dic.SetRange(Database.Query().Where(c => c.Related.EntityType == type).ToDictionary(a => a.Related)); - - var list = dic.Values.ToList(); - - var cats = Palettes.GetOrThrow(palette).Split(' '); - - for (int i = 0; i < list.Count; i++) - { - list[i].Color = cats[i % cats.Length]; - } - - list.SaveList(); - } - - public static void AssertFewEntities(Type type) - { - int count = giCount.GetInvoker(type)(); - - if (count > Limit) - throw new ApplicationException("Too many {0} ({1}), maximum is {2}".FormatWith(type.NicePluralName(), count, Limit)); - } - - public static bool HasTooManyEntities(Type type, out int count) - { - count = giCount.GetInvoker(type)(); - - return count > Limit; - } - - public static void SavePalette(ChartPaletteModel model) - { - using (var tr = new Transaction()) - { - Type type = TypeLogic.GetType(model.TypeName); - - giDeleteColors.GetInvoker(type)(); - - model.Colors.Where(a => a.Color != null).SaveList(); - tr.Commit(); - } - } - - - static readonly GenericInvoker> giCount = new(() => Count()); - static int Count() where T : Entity - { - return Database.Query().Count(); - } - - static readonly GenericInvoker> giDeleteColors = new(() => DeleteColors()); - static int DeleteColors() where T : Entity - { - return (from t in Database.Query() // To filter by type conditions - join cc in Database.Query() on t.ToLite() equals cc.Related - select cc).UnsafeDelete(); - } - - public static ChartPaletteModel? GetPalette(Type type, bool allEntities) - { - var dic = ChartColorLogic.Colors.Value.TryGetC(type); - - if (allEntities) - { - AssertFewEntities(type); - - return new ChartPaletteModel - { - TypeName = TypeLogic.GetCleanName(type), - Colors = Database.RetrieveAllLite(type).Select(l => new ChartColorEntity - { - Related = (Lite)l, - Color = dic?.TryGetC(l.Id)! - }).ToMList() - }; - } - else - { - if (dic == null) - return null; - - if (EnumEntity.IsEnumEntity(type)) - { - var lites = EnumEntity.GetEntities(EnumEntity.Extract(type)!).ToDictionary(a => a.Id, a => a.ToLite()); - - return new ChartPaletteModel - { - TypeName = type.ToTypeEntity().CleanName, - Colors = dic.Select(kvp => new ChartColorEntity - { - Related = lites.GetOrThrow(kvp.Key), - Color = kvp.Value - }).ToMList() - }; - } - else - { - return new ChartPaletteModel - { - TypeName = type.ToTypeEntity().CleanName, - Colors = dic.Select(kvp => new ChartColorEntity - { - Related = Lite.Create(type, kvp.Key), - Color = kvp.Value - }).ToMList() - }; - } - } - } - - public static string? ColorFor(Type type, PrimaryKey id) - { - return Colors.Value.TryGetC(type)?.TryGetC(id); - } - - public static string? ColorFor(Lite lite) - { - return ColorFor(lite.EntityType, lite.Id); - } - - public static string? ColorFor(Entity ident) - { - return ColorFor(ident.GetType(), ident.Id); - } - - public static void DeletePalette(Type type) + public static string? ColorFor(Entity entity) { - Database.Query().Where(c => c.Related.EntityType == type).UnsafeDelete(); + return ColorPaletteCache.Value.TryGetC(entity.GetType())?.SpecificColors.SingleEx(a => a.Entity.Is(entity))?.Color; } } diff --git a/Signum.Engine.Extensions/Chart/ChartLogic.cs b/Signum.Engine.Extensions/Chart/ChartLogic.cs index ea955edd60..e772bf88c5 100644 --- a/Signum.Engine.Extensions/Chart/ChartLogic.cs +++ b/Signum.Engine.Extensions/Chart/ChartLogic.cs @@ -14,7 +14,7 @@ public static void Start(SchemaBuilder sb, bool googleMapsChartScripts, string[] PermissionAuthLogic.RegisterTypes(typeof(ChartPermission)); - ChartColorLogic.Start(sb); + ColorPaletteLogic.Start(sb); ChartScriptLogic.Start(sb, googleMapsChartScripts, svgMapUrls); UserChartLogic.Start(sb); } diff --git a/Signum.Engine.Extensions/Chart/Scripts/Bars.cs b/Signum.Engine.Extensions/Chart/Scripts/Bars.cs index 9d5c0a5323..8ba179b805 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/Bars.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/Bars.cs @@ -31,8 +31,7 @@ public BarsChartScript(): base(D3ChartScript.Bars) }, new ChartScriptParameterGroup("Color Category") { - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("category10|accent|dark2|paired|pastel1|pastel2|set1|set2|set3|BrBG[K]|PRGn[K]|PiYG[K]|PuOr[K]|RdBu[K]|RdGy[K]|RdYlBu[K]|RdYlGn[K]|Spectral[K]|Blues[K]|Greys[K]|Oranges[K]|Purples[K]|Reds[K]|BuGn[K]|BuPu[K]|OrRd[K]|PuBuGn[K]|PuBu[K]|PuRd[K]|RdPu[K]|YlGnBu[K]|YlGn[K]|YlOrBr[K]|YlOrRd[K]") }, - new ChartScriptParameter("ColorCategorySteps", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("3|4|5|6|7|8|9|10|11") } + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory) }, } }; } diff --git a/Signum.Engine.Extensions/Chart/Scripts/BubblePack.cs b/Signum.Engine.Extensions/Chart/Scripts/BubblePack.cs index eeccdde1c2..79654d0762 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/BubblePack.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/BubblePack.cs @@ -39,12 +39,11 @@ public BubblePackChartScript() : base(D3ChartScript.BubblePack) new ChartScriptParameterGroup("Color Scale") { new ChartScriptParameter("ColorScale", ChartParameterType.Enum) { ColumnIndex = 3, ValueDefinition = EnumValueList.Parse("ZeroMax|MinMax|Sqrt|Log") }, - new ChartScriptParameter("ColorInterpolate", ChartParameterType.Enum) { ColumnIndex = 3, ValueDefinition = EnumValueList.Parse("YlGn|YlGnBu|GnBu|BuGn|PuBuGn|PuBu|BuPu|RdPu|PuRd|OrRd|YlOrRd|YlOrBr|Purples|Blues|Greens|Oranges|Reds|Greys|PuOr|BrBG|PRGn|PiYG|RdBu|RdGy|RdYlBu|Spectral|RdYlGn") }, + new ChartScriptParameter("ColorInterpolate", ChartParameterType.Special) { ColumnIndex = 3, ValueDefinition = new SpecialParameter(SpecialParameterType.ColorInterpolate) }, }, new ChartScriptParameterGroup("Color Category") { - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ColumnIndex = 4, ValueDefinition = EnumValueList.Parse("category10|accent|dark2|paired|pastel1|pastel2|set1|set2|set3|BrBG[K]|PRGn[K]|PiYG[K]|PuOr[K]|RdBu[K]|RdGy[K]|RdYlBu[K]|RdYlGn[K]|Spectral[K]|Blues[K]|Greys[K]|Oranges[K]|Purples[K]|Reds[K]|BuGn[K]|BuPu[K]|OrRd[K]|PuBuGn[K]|PuBu[K]|PuRd[K]|RdPu[K]|YlGnBu[K]|YlGn[K]|YlOrBr[K]|YlOrRd[K]") }, - new ChartScriptParameter("ColorCategorySteps", ChartParameterType.Enum) { ColumnIndex = 4, ValueDefinition = EnumValueList.Parse("3|4|5|6|7|8|9|10|11") }, + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ColumnIndex = 4, ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory) }, }, }; } diff --git a/Signum.Engine.Extensions/Chart/Scripts/Bubbleplot.cs b/Signum.Engine.Extensions/Chart/Scripts/Bubbleplot.cs index e09e7e20ff..b4c8b81c6c 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/Bubbleplot.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/Bubbleplot.cs @@ -37,12 +37,11 @@ public BubbleplotChartScript() : base(D3ChartScript.Bubbleplot) new ChartScriptParameterGroup("Color Scale") { new ChartScriptParameter("ColorScale", ChartParameterType.Enum) { ColumnIndex = 0, ValueDefinition = EnumValueList.Parse("Ordinal|ZeroMax|MinMax|Sqrt|Log") }, - new ChartScriptParameter("ColorInterpolate", ChartParameterType.Enum) { ColumnIndex = 0, ValueDefinition = EnumValueList.Parse("YlGn|YlGnBu|GnBu|BuGn|PuBuGn|PuBu|BuPu|RdPu|PuRd|OrRd|YlOrRd|YlOrBr|Purples|Blues|Greens|Oranges|Reds|Greys|PuOr|BrBG|PRGn|PiYG|RdBu|RdGy|RdYlBu|Spectral|RdYlGn") }, + new ChartScriptParameter("ColorInterpolate", ChartParameterType.Special) { ColumnIndex = 0, ValueDefinition = new SpecialParameter(SpecialParameterType.ColorInterpolate) }, }, new ChartScriptParameterGroup("Color Category") { - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ColumnIndex = 0, ValueDefinition = EnumValueList.Parse("category10|accent|dark2|paired|pastel1|pastel2|set1|set2|set3|BrBG[K]|PRGn[K]|PiYG[K]|PuOr[K]|RdBu[K]|RdGy[K]|RdYlBu[K]|RdYlGn[K]|Spectral[K]|Blues[K]|Greys[K]|Oranges[K]|Purples[K]|Reds[K]|BuGn[K]|BuPu[K]|OrRd[K]|PuBuGn[K]|PuBu[K]|PuRd[K]|RdPu[K]|YlGnBu[K]|YlGn[K]|YlOrBr[K]|YlOrRd[K]") }, - new ChartScriptParameter("ColorCategorySteps", ChartParameterType.Enum) { ColumnIndex = 0, ValueDefinition = EnumValueList.Parse("3|4|5|6|7|8|9|10|11") }, + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ColumnIndex = 0, ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory) }, } }; } diff --git a/Signum.Engine.Extensions/Chart/Scripts/CalendarStream.cs b/Signum.Engine.Extensions/Chart/Scripts/CalendarStream.cs index a2eb4b44bf..119441f7c9 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/CalendarStream.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/CalendarStream.cs @@ -18,7 +18,7 @@ public CalendarStreamChartScript() : base(D3ChartScript.CalendarStream) { new ChartScriptParameter("StartDate", ChartParameterType.Enum) { ColumnIndex = 0, ValueDefinition = EnumValueList.Parse("Monday|Sunday") }, new ChartScriptParameter("ColorScale", ChartParameterType.Enum) { ColumnIndex = 1, ValueDefinition = EnumValueList.Parse("ZeroMax|MinMax|Sqrt|Log") }, - new ChartScriptParameter("ColorInterpolate", ChartParameterType.Enum) { ColumnIndex = 1, ValueDefinition = EnumValueList.Parse("YlGn|YlGnBu|GnBu|BuGn|PuBuGn|PuBu|BuPu|RdPu|PuRd|OrRd|YlOrRd|YlOrBr|Purples|Blues|Greens|Oranges|Reds|Greys|PuOr|BrBG|PRGn|PiYG|RdBu|RdGy|RdYlBu|Spectral|RdYlGn") }, + new ChartScriptParameter("ColorInterpolate", ChartParameterType.Special) { ColumnIndex = 1, ValueDefinition = new SpecialParameter(SpecialParameterType.ColorInterpolate) }, } }; } diff --git a/Signum.Engine.Extensions/Chart/Scripts/Columns.cs b/Signum.Engine.Extensions/Chart/Scripts/Columns.cs index 082bff5e68..5cfc54fb13 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/Columns.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/Columns.cs @@ -32,8 +32,7 @@ public ColumnsChartScript() : base(D3ChartScript.Columns) }, new ChartScriptParameterGroup("Color") { - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("category10|accent|dark2|paired|pastel1|pastel2|set1|set2|set3|BrBG[K]|PRGn[K]|PiYG[K]|PuOr[K]|RdBu[K]|RdGy[K]|RdYlBu[K]|RdYlGn[K]|Spectral[K]|Blues[K]|Greys[K]|Oranges[K]|Purples[K]|Reds[K]|BuGn[K]|BuPu[K]|OrRd[K]|PuBuGn[K]|PuBu[K]|PuRd[K]|RdPu[K]|YlGnBu[K]|YlGn[K]|YlOrBr[K]|YlOrRd[K]") }, - new ChartScriptParameter("ColorCategorySteps", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("3|4|5|6|7|8|9|10|11") }, + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory) }, new ChartScriptParameter("ForceColor", ChartParameterType.String) { ValueDefinition = new StringValue("") }, } }; diff --git a/Signum.Engine.Extensions/Chart/Scripts/Heatmap.cs b/Signum.Engine.Extensions/Chart/Scripts/Heatmap.cs index 012d7d7a87..1e3950c468 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/Heatmap.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/Heatmap.cs @@ -27,7 +27,7 @@ public HeatmapChartScript() : base(GoogleMapsChartScript.Heatmap) }, new ChartScriptParameterGroup("Color Gradient") { - new ChartScriptParameter("ColorGradient", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("Default|Blue-Red|Purple-Blue|Orange-Red|Fire|Emerald|Cobalt|Purples|Greys") }, + new ChartScriptParameter("ColorInterpolate", ChartParameterType.Special) { ValueDefinition = new SpecialParameter(SpecialParameterType.ColorInterpolate) }, } }; } diff --git a/Signum.Engine.Extensions/Chart/Scripts/Markermap.cs b/Signum.Engine.Extensions/Chart/Scripts/Markermap.cs index 9351c8480a..3a41404735 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/Markermap.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/Markermap.cs @@ -38,8 +38,8 @@ public MarkermapChartScript(): base(GoogleMapsChartScript.Markermap) new ChartScriptParameterGroup() { new ChartScriptParameter("ColorScale", ChartParameterType.Enum) { ColumnIndex = 6, ValueDefinition = EnumValueList.Parse("ZeroMax|MinMax|Sqrt|Log") }, - new ChartScriptParameter("ColorSet", ChartParameterType.Enum) { ColumnIndex = 6, ValueDefinition = EnumValueList.Parse("YlGn|YlGnBu|GnBu|BuGn|PuBuGn|PuBu|BuPu|RdPu|PuRd|OrRd|YlOrRd|YlOrBr|Purples|Blues|Greens|Oranges|Reds|Greys|PuOr|BrBG|PRGn|PiYG|RdBu|RdGy|RdYlBu|Spectral|RdYlGn") }, - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ColumnIndex = 7, ValueDefinition = EnumValueList.Parse("category10|category20|category20b|category20c|accent|paired|pastel1|pastel2|set1|set2|set3") } + new ChartScriptParameter("ColorInterpolation", ChartParameterType.Special) { ColumnIndex = 6, ValueDefinition = new SpecialParameter(SpecialParameterType.ColorInterpolate) }, + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ColumnIndex = 7, ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory)} }, new ChartScriptParameterGroup("Zoom") { diff --git a/Signum.Engine.Extensions/Chart/Scripts/MultiBars.cs b/Signum.Engine.Extensions/Chart/Scripts/MultiBars.cs index fd2388b7e3..ebe3af1346 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/MultiBars.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/MultiBars.cs @@ -35,8 +35,7 @@ public MultiBarsChartScript() : base(D3ChartScript.MultiBars) }, new ChartScriptParameterGroup("Color") { - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("category10|accent|dark2|paired|pastel1|pastel2|set1|set2|set3|BrBG[K]|PRGn[K]|PiYG[K]|PuOr[K]|RdBu[K]|RdGy[K]|RdYlBu[K]|RdYlGn[K]|Spectral[K]|Blues[K]|Greys[K]|Oranges[K]|Purples[K]|Reds[K]|BuGn[K]|BuPu[K]|OrRd[K]|PuBuGn[K]|PuBu[K]|PuRd[K]|RdPu[K]|YlGnBu[K]|YlGn[K]|YlOrBr[K]|YlOrRd[K]") }, - new ChartScriptParameter("ColorCategorySteps", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("3|4|5|6|7|8|9|10|11") } + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory) }, } }; } diff --git a/Signum.Engine.Extensions/Chart/Scripts/MultiColumns.cs b/Signum.Engine.Extensions/Chart/Scripts/MultiColumns.cs index 59410e5e5e..5685b45f3e 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/MultiColumns.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/MultiColumns.cs @@ -35,8 +35,7 @@ public MultiColumnsChartScript() : base(D3ChartScript.MultiColumns) }, new ChartScriptParameterGroup("Color Category") { - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("category10|accent|dark2|paired|pastel1|pastel2|set1|set2|set3|BrBG[K]|PRGn[K]|PiYG[K]|PuOr[K]|RdBu[K]|RdGy[K]|RdYlBu[K]|RdYlGn[K]|Spectral[K]|Blues[K]|Greys[K]|Oranges[K]|Purples[K]|Reds[K]|BuGn[K]|BuPu[K]|OrRd[K]|PuBuGn[K]|PuBu[K]|PuRd[K]|RdPu[K]|YlGnBu[K]|YlGn[K]|YlOrBr[K]|YlOrRd[K]") }, - new ChartScriptParameter("ColorCategorySteps", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("3|4|5|6|7|8|9|10|11") } + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory) }, } }; } diff --git a/Signum.Engine.Extensions/Chart/Scripts/MultiLines.cs b/Signum.Engine.Extensions/Chart/Scripts/MultiLines.cs index 4abf5e147d..3069139a43 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/MultiLines.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/MultiLines.cs @@ -43,8 +43,7 @@ public MultiLinesChartScript(): base(D3ChartScript.MultiLines) }, new ChartScriptParameterGroup("Color Category") { - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("category10|accent|dark2|paired|pastel1|pastel2|set1|set2|set3|BrBG[K]|PRGn[K]|PiYG[K]|PuOr[K]|RdBu[K]|RdGy[K]|RdYlBu[K]|RdYlGn[K]|Spectral[K]|Blues[K]|Greys[K]|Oranges[K]|Purples[K]|Reds[K]|BuGn[K]|BuPu[K]|OrRd[K]|PuBuGn[K]|PuBu[K]|PuRd[K]|RdPu[K]|YlGnBu[K]|YlGn[K]|YlOrBr[K]|YlOrRd[K]") }, - new ChartScriptParameter("ColorCategorySteps", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("3|4|5|6|7|8|9|10|11") } + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory) }, }, new ChartScriptParameterGroup("Form") { diff --git a/Signum.Engine.Extensions/Chart/Scripts/ParallelCordiantes.cs b/Signum.Engine.Extensions/Chart/Scripts/ParallelCordiantes.cs index f618a929e7..666ca93cf8 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/ParallelCordiantes.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/ParallelCordiantes.cs @@ -35,7 +35,7 @@ public ParallelCoordiantesChartScript(): base(D3ChartScript.ParallelCoordinates) }, new ChartScriptParameterGroup("Color") { - new ChartScriptParameter("ColorInterpolate", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("YlGn|YlGnBu|GnBu|BuGn|PuBuGn|PuBu|BuPu|RdPu|PuRd|OrRd|YlOrRd|YlOrBr|Purples|Blues|Greens|Oranges|Reds|Greys|PuOr|BrBG|PRGn|PiYG|RdBu|RdGy|RdYlBu|Spectral|RdYlGn") }, + new ChartScriptParameter("ColorInterpolate", ChartParameterType.Special) { ValueDefinition = new SpecialParameter(SpecialParameterType.ColorInterpolate) }, }, new ChartScriptParameterGroup("Form") { diff --git a/Signum.Engine.Extensions/Chart/Scripts/Pie.cs b/Signum.Engine.Extensions/Chart/Scripts/Pie.cs index 97a5d038ea..fd2fdd2277 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/Pie.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/Pie.cs @@ -24,8 +24,7 @@ public PieChartScript(): base(D3ChartScript.Pie) }, new ChartScriptParameterGroup("Color Category") { - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("category10|accent|dark2|paired|pastel1|pastel2|set1|set2|set3|BrBG[K]|PRGn[K]|PiYG[K]|PuOr[K]|RdBu[K]|RdGy[K]|RdYlBu[K]|RdYlGn[K]|Spectral[K]|Blues[K]|Greys[K]|Oranges[K]|Purples[K]|Reds[K]|BuGn[K]|BuPu[K]|OrRd[K]|PuBuGn[K]|PuBu[K]|PuRd[K]|RdPu[K]|YlGnBu[K]|YlGn[K]|YlOrBr[K]|YlOrRd[K]") }, - new ChartScriptParameter("ColorCategorySteps", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("3|4|5|6|7|8|9|10|11") } + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory) }, }, new ChartScriptParameterGroup("ShowPercent") { diff --git a/Signum.Engine.Extensions/Chart/Scripts/Punchcard.cs b/Signum.Engine.Extensions/Chart/Scripts/Punchcard.cs index e9d7578006..cd3dd0adcc 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/Punchcard.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/Punchcard.cs @@ -52,7 +52,7 @@ public PunchcardChartScript() : base(D3ChartScript.Punchcard) new ChartScriptParameterGroup("Fill Color") { new ChartScriptParameter("ColorScale", ChartParameterType.Enum) { ColumnIndex = 3, ValueDefinition = EnumValueList.Parse("ZeroMax|MinMax|Sqrt|Log") }, - new ChartScriptParameter("ColorInterpolate", ChartParameterType.Enum) { ColumnIndex = 3, ValueDefinition = EnumValueList.Parse("YlGn|YlGnBu|GnBu|BuGn|PuBuGn|PuBu|BuPu|RdPu|PuRd|OrRd|YlOrRd|YlOrBr|Purples|Blues|Greens|Oranges|Reds|Greys|PuOr|BrBG|PRGn|PiYG|RdBu|RdGy|RdYlBu|Spectral|RdYlGn") }, + new ChartScriptParameter("ColorInterpolate", ChartParameterType.Special) { ColumnIndex = 3, ValueDefinition = new SpecialParameter(SpecialParameterType.ColorInterpolate) }, new ChartScriptParameter("FillColor", ChartParameterType.String) { ValueDefinition = new StringValue("gray") }, }, new ChartScriptParameterGroup("Stroke") diff --git a/Signum.Engine.Extensions/Chart/Scripts/Scatterplot.cs b/Signum.Engine.Extensions/Chart/Scripts/Scatterplot.cs index 276c7feed5..090dee5d90 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/Scatterplot.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/Scatterplot.cs @@ -34,12 +34,11 @@ public ScatterplotChartScript() : base(D3ChartScript.Scatterplot) new ChartScriptParameterGroup("Color Scale") { new ChartScriptParameter("ColorScale", ChartParameterType.Enum) { ColumnIndex = 0, ValueDefinition = EnumValueList.Parse("Ordinal|ZeroMax|MinMax|Sqrt|Log") }, - new ChartScriptParameter("ColorInterpolate", ChartParameterType.Enum) { ColumnIndex = 0, ValueDefinition = EnumValueList.Parse("YlGn|YlGnBu|GnBu|BuGn|PuBuGn|PuBu|BuPu|RdPu|PuRd|OrRd|YlOrRd|YlOrBr|Purples|Blues|Greens|Oranges|Reds|Greys|PuOr|BrBG|PRGn|PiYG|RdBu|RdGy|RdYlBu|Spectral|RdYlGn") }, + new ChartScriptParameter("ColorInterpolate", ChartParameterType.Special) { ColumnIndex = 0, ValueDefinition = new SpecialParameter(SpecialParameterType.ColorInterpolate) }, }, new ChartScriptParameterGroup("Color Category") { - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ColumnIndex = 0, ValueDefinition = EnumValueList.Parse("category10|accent|dark2|paired|pastel1|pastel2|set1|set2|set3|BrBG[K]|PRGn[K]|PiYG[K]|PuOr[K]|RdBu[K]|RdGy[K]|RdYlBu[K]|RdYlGn[K]|Spectral[K]|Blues[K]|Greys[K]|Oranges[K]|Purples[K]|Reds[K]|BuGn[K]|BuPu[K]|OrRd[K]|PuBuGn[K]|PuBu[K]|PuRd[K]|RdPu[K]|YlGnBu[K]|YlGn[K]|YlOrBr[K]|YlOrRd[K]") }, - new ChartScriptParameter("ColorCategorySteps", ChartParameterType.Enum) { ColumnIndex = 0, ValueDefinition = EnumValueList.Parse("3|4|5|6|7|8|9|10|11") } + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ColumnIndex = 0, ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory) } }, }; diff --git a/Signum.Engine.Extensions/Chart/Scripts/StackedBars.cs b/Signum.Engine.Extensions/Chart/Scripts/StackedBars.cs index 91af842c2d..9f5c0af4ed 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/StackedBars.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/StackedBars.cs @@ -35,8 +35,7 @@ public StackedBarsChartScript() : base(D3ChartScript.StackedBars) }, new ChartScriptParameterGroup("Color Category") { - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("category10|accent|dark2|paired|pastel1|pastel2|set1|set2|set3|BrBG[K]|PRGn[K]|PiYG[K]|PuOr[K]|RdBu[K]|RdGy[K]|RdYlBu[K]|RdYlGn[K]|Spectral[K]|Blues[K]|Greys[K]|Oranges[K]|Purples[K]|Reds[K]|BuGn[K]|BuPu[K]|OrRd[K]|PuBuGn[K]|PuBu[K]|PuRd[K]|RdPu[K]|YlGnBu[K]|YlGn[K]|YlOrBr[K]|YlOrRd[K]") }, - new ChartScriptParameter("ColorCategorySteps", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("3|4|5|6|7|8|9|10|11") } + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory) }, }, new ChartScriptParameterGroup("Form") { diff --git a/Signum.Engine.Extensions/Chart/Scripts/StackedColumns.cs b/Signum.Engine.Extensions/Chart/Scripts/StackedColumns.cs index 73ea08bfc4..8091e21a97 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/StackedColumns.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/StackedColumns.cs @@ -36,8 +36,7 @@ public StackedColumnsChartScript() : base(D3ChartScript.StackedColumns) }, new ChartScriptParameterGroup("Color Category") { - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("category10|accent|dark2|paired|pastel1|pastel2|set1|set2|set3|BrBG[K]|PRGn[K]|PiYG[K]|PuOr[K]|RdBu[K]|RdGy[K]|RdYlBu[K]|RdYlGn[K]|Spectral[K]|Blues[K]|Greys[K]|Oranges[K]|Purples[K]|Reds[K]|BuGn[K]|BuPu[K]|OrRd[K]|PuBuGn[K]|PuBu[K]|PuRd[K]|RdPu[K]|YlGnBu[K]|YlGn[K]|YlOrBr[K]|YlOrRd[K]") }, - new ChartScriptParameter("ColorCategorySteps", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("3|4|5|6|7|8|9|10|11") } + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory) }, }, new ChartScriptParameterGroup("Form") { diff --git a/Signum.Engine.Extensions/Chart/Scripts/StackedLines.cs b/Signum.Engine.Extensions/Chart/Scripts/StackedLines.cs index 3f8eda8b8f..08b37d2dd7 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/StackedLines.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/StackedLines.cs @@ -35,8 +35,7 @@ public StackedLinesChartScript() : base(D3ChartScript.StackedLines) }, new ChartScriptParameterGroup("Color Category") { - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("category10|accent|dark2|paired|pastel1|pastel2|set1|set2|set3|BrBG[K]|PRGn[K]|PiYG[K]|PuOr[K]|RdBu[K]|RdGy[K]|RdYlBu[K]|RdYlGn[K]|Spectral[K]|Blues[K]|Greys[K]|Oranges[K]|Purples[K]|Reds[K]|BuGn[K]|BuPu[K]|OrRd[K]|PuBuGn[K]|PuBu[K]|PuRd[K]|RdPu[K]|YlGnBu[K]|YlGn[K]|YlOrBr[K]|YlOrRd[K]") }, - new ChartScriptParameter("ColorCategorySteps", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse("3|4|5|6|7|8|9|10|11") } + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory) }, }, new ChartScriptParameterGroup("Form") { diff --git a/Signum.Engine.Extensions/Chart/Scripts/SvgMap.cs b/Signum.Engine.Extensions/Chart/Scripts/SvgMap.cs index 23b64525d9..a41291947e 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/SvgMap.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/SvgMap.cs @@ -1,56 +1,55 @@ -using Signum.Entities.Chart; - -namespace Signum.Engine.Chart.Scripts; - -public class SvgMapScript : ChartScript -{ - public SvgMapScript(string[] svgMaps) : base(SvgMapsChartScript.SvgMap) - { - this.Icon = ChartScriptLogic.LoadIcon("svgmap.png"); - this.Columns = new List - { - new ChartScriptColumn("LocationCode", ChartColumnType.String), - new ChartScriptColumn("Location", ChartColumnType.Groupable) { IsOptional = true }, - new ChartScriptColumn("Color Scale", ChartColumnType.Magnitude) { IsOptional = true }, - new ChartScriptColumn("Color Category", ChartColumnType.Groupable) { IsOptional = true }, - new ChartScriptColumn("Opacity", ChartColumnType.Magnitude) { IsOptional = true }, - }; - this.ParameterGroups = new List - { - new ChartScriptParameterGroup("Url") - { - new ChartScriptParameter("SvgUrl", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse(svgMaps.ToString("|")) }, - }, - - new ChartScriptParameterGroup("Location") - { - new ChartScriptParameter("LocationSelector", ChartParameterType.String) { ColumnIndex = 0, ValueDefinition = new StringValue("path[id]") }, - new ChartScriptParameter("LocationAttribute", ChartParameterType.String) { ColumnIndex = 0, ValueDefinition = new StringValue("id") }, - new ChartScriptParameter("LocationMatch", ChartParameterType.Enum) { ColumnIndex = 0, ValueDefinition = EnumValueList.Parse("Exact|Prefix") }, - }, - - new ChartScriptParameterGroup("Stroke") - { - new ChartScriptParameter("StrokeColor", ChartParameterType.String) { ValueDefinition = new StringValue("") }, - new ChartScriptParameter("StrokeWidth", ChartParameterType.String) { ValueDefinition = new StringValue("") }, +using Signum.Entities.Chart; + +namespace Signum.Engine.Chart.Scripts; + +public class SvgMapScript : ChartScript +{ + public SvgMapScript(string[] svgMaps) : base(SvgMapsChartScript.SvgMap) + { + this.Icon = ChartScriptLogic.LoadIcon("svgmap.png"); + this.Columns = new List + { + new ChartScriptColumn("LocationCode", ChartColumnType.String), + new ChartScriptColumn("Location", ChartColumnType.Groupable) { IsOptional = true }, + new ChartScriptColumn("Color Scale", ChartColumnType.Magnitude) { IsOptional = true }, + new ChartScriptColumn("Color Category", ChartColumnType.Groupable) { IsOptional = true }, + new ChartScriptColumn("Opacity", ChartColumnType.Magnitude) { IsOptional = true }, + }; + this.ParameterGroups = new List + { + new ChartScriptParameterGroup("Url") + { + new ChartScriptParameter("SvgUrl", ChartParameterType.Enum) { ValueDefinition = EnumValueList.Parse(svgMaps.ToString("|")) }, }, - new ChartScriptParameterGroup("Fill") - { - new ChartScriptParameter("NoDataColor", ChartParameterType.String) { ValueDefinition = new StringValue("#aaa") }, - }, - - new ChartScriptParameterGroup("Color Scale") - { - new ChartScriptParameter("ColorScale", ChartParameterType.Enum) { ColumnIndex = 2, ValueDefinition = EnumValueList.Parse("ZeroMax|MinMax|Sqrt|Log") }, - new ChartScriptParameter("ColorInterpolate", ChartParameterType.Enum) { ColumnIndex = 2, ValueDefinition = EnumValueList.Parse("YlGn|YlGnBu|GnBu|BuGn|PuBuGn|PuBu|BuPu|RdPu|PuRd|OrRd|YlOrRd|YlOrBr|Purples|Blues|Greens|Oranges|Reds|Greys|PuOr|BrBG|PRGn|PiYG|RdBu|RdGy|RdYlBu|Spectral|RdYlGn") }, - new ChartScriptParameter("ColorScaleMaxValue", ChartParameterType.Number) { ColumnIndex = 2, ValueDefinition = new NumberInterval{ DefaultValue = null} }, - }, - new ChartScriptParameterGroup("Color Category") - { - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ColumnIndex = 3, ValueDefinition = EnumValueList.Parse("category10|accent|dark2|paired|pastel1|pastel2|set1|set2|set3|BrBG[K]|PRGn[K]|PiYG[K]|PuOr[K]|RdBu[K]|RdGy[K]|RdYlBu[K]|RdYlGn[K]|Spectral[K]|Blues[K]|Greys[K]|Oranges[K]|Purples[K]|Reds[K]|BuGn[K]|BuPu[K]|OrRd[K]|PuBuGn[K]|PuBu[K]|PuRd[K]|RdPu[K]|YlGnBu[K]|YlGn[K]|YlOrBr[K]|YlOrRd[K]") }, - new ChartScriptParameter("ColorCategorySteps", ChartParameterType.Enum) { ColumnIndex = 3, ValueDefinition = EnumValueList.Parse("3|4|5|6|7|8|9|10|11") }, - } - }; - } -} + new ChartScriptParameterGroup("Location") + { + new ChartScriptParameter("LocationSelector", ChartParameterType.String) { ColumnIndex = 0, ValueDefinition = new StringValue("path[id]") }, + new ChartScriptParameter("LocationAttribute", ChartParameterType.String) { ColumnIndex = 0, ValueDefinition = new StringValue("id") }, + new ChartScriptParameter("LocationMatch", ChartParameterType.Enum) { ColumnIndex = 0, ValueDefinition = EnumValueList.Parse("Exact|Prefix") }, + }, + + new ChartScriptParameterGroup("Stroke") + { + new ChartScriptParameter("StrokeColor", ChartParameterType.String) { ValueDefinition = new StringValue("") }, + new ChartScriptParameter("StrokeWidth", ChartParameterType.String) { ValueDefinition = new StringValue("") }, + }, + + new ChartScriptParameterGroup("Fill") + { + new ChartScriptParameter("NoDataColor", ChartParameterType.String) { ValueDefinition = new StringValue("#aaa") }, + }, + + new ChartScriptParameterGroup("Color Scale") + { + new ChartScriptParameter("ColorScale", ChartParameterType.Enum) { ColumnIndex = 2, ValueDefinition = EnumValueList.Parse("ZeroMax|MinMax|Sqrt|Log") }, + new ChartScriptParameter("ColorInterpolate", ChartParameterType.Special) { ColumnIndex = 2, ValueDefinition = new SpecialParameter(SpecialParameterType.ColorInterpolate) }, + new ChartScriptParameter("ColorScaleMaxValue", ChartParameterType.Number) { ColumnIndex = 2, ValueDefinition = new NumberInterval{ DefaultValue = null} }, + }, + new ChartScriptParameterGroup("Color Category") + { + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ColumnIndex = 3, ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory) }, + } + }; + } +} diff --git a/Signum.Engine.Extensions/Chart/Scripts/TreeMap.cs b/Signum.Engine.Extensions/Chart/Scripts/TreeMap.cs index 588daedd03..ea34180205 100644 --- a/Signum.Engine.Extensions/Chart/Scripts/TreeMap.cs +++ b/Signum.Engine.Extensions/Chart/Scripts/TreeMap.cs @@ -34,12 +34,11 @@ public TreeMapChartScript() : base(D3ChartScript.Treemap) new ChartScriptParameterGroup("Color Scale") { new ChartScriptParameter("ColorScale", ChartParameterType.Enum) { ColumnIndex = 3, ValueDefinition = EnumValueList.Parse("ZeroMax|MinMax|Sqrt|Log") }, - new ChartScriptParameter("ColorInterpolate", ChartParameterType.Enum) { ColumnIndex = 3, ValueDefinition = EnumValueList.Parse("YlGn|YlGnBu|GnBu|BuGn|PuBuGn|PuBu|BuPu|RdPu|PuRd|OrRd|YlOrRd|YlOrBr|Purples|Blues|Greens|Oranges|Reds|Greys|PuOr|BrBG|PRGn|PiYG|RdBu|RdGy|RdYlBu|Spectral|RdYlGn") }, + new ChartScriptParameter("ColorInterpolate", ChartParameterType.Special) { ColumnIndex = 3, ValueDefinition = new SpecialParameter(SpecialParameterType.ColorInterpolate) }, }, new ChartScriptParameterGroup("Color Category") { - new ChartScriptParameter("ColorCategory", ChartParameterType.Enum) { ColumnIndex = 4, ValueDefinition = EnumValueList.Parse("category10|accent|dark2|paired|pastel1|pastel2|set1|set2|set3|BrBG[K]|PRGn[K]|PiYG[K]|PuOr[K]|RdBu[K]|RdGy[K]|RdYlBu[K]|RdYlGn[K]|Spectral[K]|Blues[K]|Greys[K]|Oranges[K]|Purples[K]|Reds[K]|BuGn[K]|BuPu[K]|OrRd[K]|PuBuGn[K]|PuBu[K]|PuRd[K]|RdPu[K]|YlGnBu[K]|YlGn[K]|YlOrBr[K]|YlOrRd[K]") }, - new ChartScriptParameter("ColorCategorySteps", ChartParameterType.Enum) { ColumnIndex = 4, ValueDefinition = EnumValueList.Parse("3|4|5|6|7|8|9|10|11") }, + new ChartScriptParameter("ColorCategory", ChartParameterType.Special) { ColumnIndex = 4, ValueDefinition = new SpecialParameter(SpecialParameterType.ColorCategory) }, } }; } diff --git a/Signum.Entities.Extensions/Chart/ChartColor.cs b/Signum.Entities.Extensions/Chart/ChartColor.cs deleted file mode 100644 index 4315c2c84e..0000000000 --- a/Signum.Entities.Extensions/Chart/ChartColor.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Signum.Entities.Basics; - -namespace Signum.Entities.Chart; - -[EntityKind(EntityKind.System, EntityData.Master), TicksColumn(false)] -public class ChartColorEntity : Entity -{ - [ImplementedByAll, UniqueIndex] - public Lite Related { get; set; } - - [StringLengthValidator(Max = 100), NotNullValidator(DisabledInModelBinder = true)] - public string Color { get; set; } - - public override string ToString() - { - if (Related == null) - return " -> {0}".FormatWith(Color); - - - return "{0} {1} -> {2}".FormatWith(Related.GetType().NiceName(), Related.Id, Color); - } -} - -public class ChartPaletteModel : ModelEntity -{ - public string TypeName { get; set; } - - public MList Colors { get; set; } = new MList(); - - public override string ToString() - { - var type = TypeEntity.TryGetType(TypeName); - - return ChartMessage.ColorsFor0.NiceToString().FormatWith(type?.NiceName() ?? TypeName); - } -} diff --git a/Signum.Entities.Extensions/Chart/ChartScriptParameter.cs b/Signum.Entities.Extensions/Chart/ChartScriptParameter.cs index 6cafc1a8b7..b1f3681d0e 100644 --- a/Signum.Entities.Extensions/Chart/ChartScriptParameter.cs +++ b/Signum.Entities.Extensions/Chart/ChartScriptParameter.cs @@ -48,6 +48,8 @@ public override void Write(Utf8JsonWriter writer, ChartScriptParameterGroup valu } } + + public class ChartScriptParameter { public ChartScriptParameter(string name, ChartParameterType type) @@ -147,6 +149,33 @@ string IChartParameterValueDefinition.DefaultValue(QueryToken? token) } } +[InTypeScript(true)] +public enum SpecialParameterType +{ + ColorCategory, + ColorInterpolate, +} + +public class SpecialParameter : IChartParameterValueDefinition +{ + public SpecialParameterType SpecialParameterType { get; } + + public SpecialParameter(SpecialParameterType specialParameterType) + { + SpecialParameterType = specialParameterType; + } + + public string DefaultValue(QueryToken? token) + { + return ""; + } + + public string? Validate(string? parameter, QueryToken? token) + { + return null; + } +} + public class EnumValueList : List, IChartParameterValueDefinition { public static string? TryParse(string valueDefinition, out EnumValueList list) @@ -253,14 +282,6 @@ public bool CompatibleWith(QueryToken? token) { return TypeFilter == null || token != null && ChartUtils.IsChartColumnType(token, TypeFilter.Value); } - - internal string ToCode() - { - if (this.TypeFilter.HasValue) - return $@"new EnumValue(""{ this.Name }"", ChartColumnType.{this.TypeFilter.Value})"; - else - return $@"new EnumValue(""{ this.Name }"")"; - } } [InTypeScript(true)] @@ -269,6 +290,7 @@ public enum ChartParameterType Enum, Number, String, + Special, } public class StringValue : IChartParameterValueDefinition diff --git a/Signum.Entities.Extensions/Chart/ChartUtils.cs b/Signum.Entities.Extensions/Chart/ChartUtils.cs index c5a5d5edc4..4c1e0325cf 100644 --- a/Signum.Entities.Extensions/Chart/ChartUtils.cs +++ b/Signum.Entities.Extensions/Chart/ChartUtils.cs @@ -131,9 +131,8 @@ public static bool SynchronizeColumns(this ChartScript chartScript, IChartBase c Name = sp.Name, parentChart = chart, ScriptParameter = sp, + Value = sp.ValueDefinition.DefaultValue(sp.GetToken(chart)) }; - - cp.Value = sp.ValueDefinition.DefaultValue(sp.GetToken(chart)); } chart.Parameters.Add(cp); diff --git a/Signum.Entities.Extensions/Chart/ColorPalette.cs b/Signum.Entities.Extensions/Chart/ColorPalette.cs new file mode 100644 index 0000000000..eb4d8542c4 --- /dev/null +++ b/Signum.Entities.Extensions/Chart/ColorPalette.cs @@ -0,0 +1,64 @@ +using Signum.Entities.Basics; +using System.ComponentModel; + +namespace Signum.Entities.Chart; + +[EntityKind(EntityKind.Main, EntityData.Master)] +public class ColorPaletteEntity : Entity +{ + public ColorPaletteEntity() + { + this.BindParent(); + } + + + [UniqueIndex] + public TypeEntity Type { get; set; } + + [StringLengthValidator(Max = 100)] + public string CategoryName { get; set; } + + public int Seed { get; set; } + + [PreserveOrder, NoRepeatValidator, BindParent] + public MList SpecificColors { get; set; } = new MList(); + + [AutoExpressionField] + public override string ToString() => As.Expression(() => IsNew ? GetType().NewNiceName() : GetType().NiceName() + " " + Type.ToString()); +} + +public class SpecificColorEmbedded : EmbeddedEntity +{ + [ImplementedByAll, UniqueIndex] + public Lite Entity { get; set; } + + [StringLengthValidator(Max = 100)] + public string Color { get; set; } + + protected override string? PropertyValidation(PropertyInfo pi) + { + if(pi.Name == nameof(Entity)) + { + var cp = this.GetParentEntity(); + + if (cp.SpecificColors.Any(a => a != this && a.Entity.Is(this.Entity))) + return ValidationMessage._0IsRepeated.NiceToString(this.Entity); + } + + return base.PropertyValidation(pi); + } +} + +[AutoInit] +public static class ColorPaletteOperation +{ + public static readonly ExecuteSymbol Save; + public static readonly DeleteSymbol Delete; +} + +public enum ColorPaletteMessage +{ + FillAutomatically, + [Description("Select {0} only if you want to override the automatic color")] + Select0OnlyIfYouWantToOverrideTheAutomaticColor, +} diff --git a/Signum.Entities/Basics/Type.cs b/Signum.Entities/Basics/Type.cs index 14d683d504..2454156bf7 100644 --- a/Signum.Entities/Basics/Type.cs +++ b/Signum.Entities/Basics/Type.cs @@ -2,7 +2,7 @@ namespace Signum.Entities.Basics; #pragma warning disable CS8618 // Non-nullable field is uninitialized. -[EntityKind(EntityKind.System, EntityData.Master), TicksColumn(false)] +[EntityKind(EntityKind.SystemString, EntityData.Master), TicksColumn(false)] public class TypeEntity : Entity { [StringLengthValidator(Max = 200), UniqueIndex] diff --git a/Signum.Entities/EnumEntity.cs b/Signum.Entities/EnumEntity.cs index 9251c66819..2fbb99424e 100644 --- a/Signum.Entities/EnumEntity.cs +++ b/Signum.Entities/EnumEntity.cs @@ -80,6 +80,13 @@ public static Enum ToEnum(Entity ident) return (Enum)Enum.ToObject(enumType, ident.id!.Value.Object); } + public static Enum ToEnum(Lite lite) + { + Type enumType = Extract(lite.EntityType)!; + + return (Enum)Enum.ToObject(enumType, lite.Id.Object); + } + public static bool IsEnumEntity(this Type type) { return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(EnumEntity<>); diff --git a/Signum.React.Extensions/Basics/Templates/ColorTypeahead.tsx b/Signum.React.Extensions/Basics/Templates/ColorTypeahead.tsx index 154ef694a9..a12a9b14fb 100644 --- a/Signum.React.Extensions/Basics/Templates/ColorTypeahead.tsx +++ b/Signum.React.Extensions/Basics/Templates/ColorTypeahead.tsx @@ -10,7 +10,12 @@ import { useForceUpdate } from '@framework/Hooks' import { TypeaheadOptions } from '@framework/Components/Typeahead' -export function ColorTypeaheadLine(p: { ctx: TypeContext; onChange?: () => void, inputAttrs?: React.InputHTMLAttributes; }){ +export function ColorTypeaheadLine(p: { + ctx: TypeContext; + onChange?: () => void, + inputAttrs?: React.InputHTMLAttributes; + extraButtons?: () => React.ReactNode; +}) { const forceUpdate = useForceUpdate(); @@ -19,28 +24,44 @@ export function ColorTypeaheadLine(p: { ctx: TypeContext - + { +
+ {input} + + {p.extraButtons && p.extraButtons()} +
)} + /> + } ); } + interface ColorTypeaheadProps { color: string | null | undefined; onChange: (newColor: string | null | undefined) => void; formControlClass: string | undefined; placeholder?: string; inputAttrs?: React.InputHTMLAttributes; + renderInput?: (input: React.ReactElement) => React.ReactElement } export function ColorTypeahead(p : ColorTypeaheadProps){ @@ -89,6 +110,7 @@ export function ColorTypeahead(p : ColorTypeaheadProps){ onChange={handleSelect} renderItem={handleRenderItem} minLength={0} + renderInput={p.renderInput} /> ); } diff --git a/Signum.React.Extensions/Chart/ChartClient.tsx b/Signum.React.Extensions/Chart/ChartClient.tsx index 5543d47d2d..aac374c13f 100644 --- a/Signum.React.Extensions/Chart/ChartClient.tsx +++ b/Signum.React.Extensions/Chart/ChartClient.tsx @@ -12,13 +12,13 @@ import { import * as AuthClient from '../Authorization/AuthClient' import { UserChartEntity, ChartPermission, ChartColumnEmbedded, ChartParameterEmbedded, ChartRequestModel, - IChartBase, ChartColumnType, ChartParameterType, ChartScriptSymbol, D3ChartScript, GoogleMapsChartScript, HtmlChartScript, SvgMapsChartScript + IChartBase, ChartColumnType, ChartParameterType, ChartScriptSymbol, D3ChartScript, GoogleMapsChartScript, HtmlChartScript, SvgMapsChartScript, SpecialParameterType } from './Signum.Entities.Chart' import { QueryTokenEmbedded } from '../UserAssets/Signum.Entities.UserAssets' import ChartButton from './ChartButton' import ChartRequestView, { ChartRequestViewHandle } from './Templates/ChartRequestView' import * as UserChartClient from './UserChart/UserChartClient' -import * as ChartPaletteClient from './ChartPalette/ChartPaletteClient' +import * as ColorPaletteClient from './ColorPalette/ColorPaletteClient' import { ImportRoute } from "@framework/AsyncImport"; import { ColumnRequest } from '@framework/FindOptions'; import { toLuxonFormat } from '@framework/Reflection'; @@ -27,10 +27,12 @@ import { toFilterRequests, toFilterOptions } from '@framework/Finder'; import { QueryString } from '@framework/QueryString'; import { MemoRepository } from './D3Scripts/Components/ReactChart'; import { DashboardFilter } from '../Dashboard/View/DashboardFilterController'; -import { softCast } from '../../Signum.React/Scripts/Globals'; +import { Dic, softCast } from '../../Signum.React/Scripts/Globals'; +import { colorInterpolators, colorSchemes } from './ColorPalette/ColorUtils'; +import { getColorInterpolation } from './D3Scripts/Components/ChartUtils'; export function start(options: { routes: JSX.Element[], googleMapsApiKey?: string, svgMap?: boolean }) { - + options.routes.push( import("./Templates/ChartRequestPage")} />); AppContext.clearSettingsActions.push(ButtonBarChart.clearOnButtonBarElements); @@ -45,7 +47,7 @@ export function start(options: { routes: JSX.Element[], googleMapsApiKey?: strin }); UserChartClient.start({ routes: options.routes }); - ChartPaletteClient.start({ routes: options.routes }); + ColorPaletteClient.start({ routes: options.routes }); registerChartScriptComponent(D3ChartScript.Bars, () => import("./D3Scripts/Bars")); registerChartScriptComponent(D3ChartScript.BubblePack, () => import("./D3Scripts/BubblePack")); @@ -151,7 +153,7 @@ export interface ChartScriptParameter { name: string; columnIndex?: number; type: ChartParameterType; - valueDefinition: NumberInterval | EnumValueList | StringValue | null; + valueDefinition: NumberInterval | EnumValueList | StringValue | SpecialParameter | null; } export interface NumberInterval { @@ -160,6 +162,10 @@ export interface NumberInterval { maxValue: number | null; } +export interface SpecialParameter { + specialParameterType: SpecialParameterType; +} + export interface EnumValueList extends Array { } @@ -249,12 +255,14 @@ export function isChartColumnType(token: QueryToken | undefined, ct: ChartColumn "DateOnly", "String", "Lite", - "Enum"].contains(type); + "Enum" + ].contains(type); case "Magnitude": return [ "Integer", "Real", - "RealGroupable"].contains(type); + "RealGroupable" + ].contains(type); case "Positionable": return [ "Integer", @@ -262,10 +270,10 @@ export function isChartColumnType(token: QueryToken | undefined, ct: ChartColumn "RealGroupable", "DateOnly", "DateTime", - "Time"].contains(type); + "Time" + ].contains(type); } - return false; } @@ -331,12 +339,21 @@ export function synchronizeColumns(chart: IChartBase, chartScript: ChartScript) } -function isValidParameterValue(value: string | null | undefined, scriptParameter: ChartScriptParameter, relatedColumn: QueryToken | null | undefined) { +function isValidParameterValue(value: string | null | undefined, scriptParameter: ChartScriptParameter, relatedColumn: QueryToken | null | undefined) : boolean{ switch (scriptParameter.type) { case "Enum": return (scriptParameter.valueDefinition as EnumValueList).filter(a => a.typeFilter == undefined || relatedColumn == undefined || isChartColumnType(relatedColumn, a.typeFilter)).some(a => a.name == value); case "Number": return !isNaN(parseFloat(value!)); case "String": return true; + case "Special": { + const specialParameterType = (scriptParameter.valueDefinition as SpecialParameter).specialParameterType; + switch (specialParameterType) { + case "ColorCategory": return value != null && colorSchemes[value] != null; + case "ColorInterpolate": return value != null && getColorInterpolation(value) != null; + default: throw new Error("Unexpected parameter type " + specialParameterType); + } + + } default: throw new Error("Unexpected parameter type"); } @@ -347,6 +364,14 @@ export function defaultParameterValue(scriptParameter: ChartScriptParameter, rel case "Enum": return (scriptParameter.valueDefinition as EnumValueList).filter(a => a.typeFilter == undefined || relatedColumn == undefined || isChartColumnType(relatedColumn, a.typeFilter)).first().name; case "Number": return (scriptParameter.valueDefinition as NumberInterval).defaultValue?.toString(); case "String": return (scriptParameter.valueDefinition as StringValue).defaultValue?.toString(); + case "Special": { + const specialParameterType = (scriptParameter.valueDefinition as SpecialParameter).specialParameterType; + switch (specialParameterType) { + case "ColorCategory": return Dic.getKeys(colorSchemes)[0]; + case "ColorInterpolate": return Dic.getKeys(colorInterpolators)[0]; + default: throw new Error("Unexpected parameter type " + specialParameterType); + } + } default: throw new Error("Unexpected parameter type"); } @@ -615,7 +640,7 @@ export module API { return v => String(v); } - export function getColor(token: QueryToken, palettes: { [type: string] : ChartPaletteClient.ColorPalette | null }): ((val: unknown) => string | null) { + export function getColor(token: QueryToken, palettes: { [type: string]: ColorPaletteClient.ColorPalette | null }): ((val: unknown) => string | null) { var tis = tryGetTypeInfos(token.type); @@ -626,7 +651,7 @@ export module API { return "#555"; var cp = palettes[typeName]; - return cp && cp[v as string] || null; + return cp && cp.getColor(v as string) || null; } } @@ -635,8 +660,10 @@ export module API { if (v == null) return "#555"; - var cp = palettes[(v as Lite).EntityType]; - return cp && cp[(v as Lite).id!] || null; + var lite = (v as Lite); + + var cp = palettes[lite.EntityType]; + return cp && cp.getColor(lite.id!.toString()) || null; }; } @@ -705,7 +732,7 @@ export module API { return request.parameters.toObject(a => a.element.name!, a => a.element.value ?? defaultValues[a.element.name!]) } - export function toChartResult(request: ChartRequestModel, rt: ResultTable, chartScript: ChartScript, palettes: { [type: string]: ChartPaletteClient.ColorPalette | null }): ExecuteChartResult { + export function toChartResult(request: ChartRequestModel, rt: ResultTable, chartScript: ChartScript, palettes: { [type: string]: ColorPaletteClient.ColorPalette | null }): ExecuteChartResult { var cols = request.columns.map((mle, i) => { const token = mle.element.token && mle.element.token.token; @@ -811,7 +838,7 @@ export module API { .then(rt => palettesPromise.then(palettes => toChartResult(request, rt, chartScript, palettes))); } - export function getPalletes(request: ChartRequestModel): Promise<{ [type: string]: ChartPaletteClient.ColorPalette | null }> { + export function getPalletes(request: ChartRequestModel): Promise<{ [type: string]: ColorPaletteClient.ColorPalette | null }> { var allTypes = request.columns .map(c => c.element.token) .notNull() @@ -821,7 +848,7 @@ export module API { .notNull() .distinctBy(a => a.name); - var palettesPromise = Promise.all(allTypes.map(ti => ChartPaletteClient.getColorPalette(ti).then(cp => ({ type: ti.name, palette: cp })))) + var palettesPromise = Promise.all(allTypes.map(ti => ColorPaletteClient.getColorPalette(ti).then(cp => ({ type: ti.name, palette: cp })))) .then(list => list.toObject(a => a.type, a => a.palette)); return palettesPromise; diff --git a/Signum.React.Extensions/Chart/ChartPalette/ChartPaletteClient.tsx b/Signum.React.Extensions/Chart/ChartPalette/ChartPaletteClient.tsx deleted file mode 100644 index ba0da54ba2..0000000000 --- a/Signum.React.Extensions/Chart/ChartPalette/ChartPaletteClient.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import * as React from 'react' -import { ajaxPost, ajaxGet } from '@framework/Services'; -import { EntitySettings } from '@framework/Navigator' -import * as Navigator from '@framework/Navigator' -import * as Finder from '@framework/Finder' -import { Entity, getToString, Lite, liteKey } from '@framework/Signum.Entities' -import * as QuickLinks from '@framework/QuickLinks' -import { OrderOptionParsed } from '@framework/FindOptions' -import * as AuthClient from '../../Authorization/AuthClient' -import { UserChartEntity, ChartPermission, ChartMessage, ChartRequestModel, ChartParameterEmbedded, ChartColumnEmbedded, ChartPaletteModel } from '../Signum.Entities.Chart' -import { QueryFilterEmbedded, QueryOrderEmbedded } from '../../UserQueries/Signum.Entities.UserQueries' -import { QueryTokenEmbedded } from '../../UserAssets/Signum.Entities.UserAssets' -import * as ChartClient from '../ChartClient' -import * as UserAssetsClient from '../../UserAssets/UserAssetClient' -import { ImportRoute } from "@framework/AsyncImport"; -import { OrderRequest } from '@framework/FindOptions'; -import { toFilterRequests } from '@framework/Finder'; -import { PseudoType, getTypeName, getTypeInfo, tryGetTypeInfo } from '@framework/Reflection'; -import { asFieldFunction } from '../../Dynamic/View/NodeUtils'; -import MessageModal from '@framework/Modals/MessageModal'; -import { notifySuccess } from '../../../Signum.React/Scripts/Operations'; - -export function start(options: { routes: JSX.Element[] }) { - Navigator.addSettings(new EntitySettings(ChartPaletteModel, e => import('./ChartPaletteControl'))); -} - -export function navigatePalette(type: PseudoType): Promise { - return API.fetchColorPalette(getTypeName(type), true) - .then(cp => { - if (cp == null) - return MessageModal.showError(ChartMessage.Type0NotFoundInTheDatabase.niceToString(getTypeName(type)), ChartMessage.TypeNotFound.niceToString()); - - return Navigator.view(cp,).then(pal => { - if (pal) { - API.saveColorPalette(pal).then(() => notifySuccess()); - } - }); - }); -} - -export let colorPalettesTypes: string[]; -export function getColorPaletteTypes(): Promise { - if (colorPalettesTypes) - return Promise.resolve(colorPalettesTypes); - - return API.fetchColorPalettes().then(cs => colorPalettesTypes = cs); -} - -export interface ColorPalette { - [id: string]: string; -} - -export let colorPalette: { [typeName: string]: ColorPalette | null } = {}; -export function getColorPalette(type: PseudoType): Promise { - - const typeName = getTypeName(type); - - if (colorPalette[typeName] !== undefined) - return Promise.resolve(colorPalette[typeName]); - - return API.fetchColorPalette(typeName, false) - .then(cs => colorPalette[typeName] = (cs && toColorPalete(cs)) ?? null); -} - -export function toColorPalete(model: ChartPaletteModel): ColorPalette { - - var ti = tryGetTypeInfo(model.typeName); - - var byId = model.colors.filter(a => a.element.color != null) - .toObject(a => a.element.related.id as string, a => a.element.color); - - if (ti == null || ti.kind == "Enum") { - var byName = model.colors.filter(a => a.element.color != null) - .toObject(a => getToString(a.element.related)!, a => a.element.color); - return { ...byId, ...byName }; - } - - return byId; -} - -export function setColorPalette(model: ChartPaletteModel) { - - if (model.colors.some(c => c.element.color != null)) { - colorPalette[model.typeName] = toColorPalete(model); - if (!colorPalettesTypes.contains(model.typeName)) - colorPalettesTypes.push(model.typeName); - } else { - delete colorPalette[model.typeName]; - if (colorPalettesTypes.contains(model.typeName)) - colorPalettesTypes.remove(model.typeName); - } -} - -export module API { - export function fetchColorPalettes(): Promise { - return ajaxGet({ url: "~/api/chart/colorPalette" }); - } - - export function fetchColorPalette(typeName: string, allEntities: boolean): Promise { - return ajaxGet({ url: `~/api/chart/colorPalette/${typeName}?allEntities=${allEntities}` }); - } - - export function saveColorPalette(model: ChartPaletteModel): Promise { - return ajaxPost({ url: `~/api/chart/colorPalette/${model.typeName}/save`, }, model); - } - - export function deleteColorPalette(typeName: string): Promise { - return ajaxPost({ url: `~/api/chart/colorPalette/${typeName}/delete` }, undefined); - } - - export function newColorPalette(typeName: string, paletteType: string): Promise { - return ajaxPost({ url: `~/api/chart/colorPalette/${typeName}/new/${paletteType}`, }, undefined); - } -} diff --git a/Signum.React.Extensions/Chart/ChartPalette/ChartPaletteControl.tsx b/Signum.React.Extensions/Chart/ChartPalette/ChartPaletteControl.tsx deleted file mode 100644 index 0e0f2ecac5..0000000000 --- a/Signum.React.Extensions/Chart/ChartPalette/ChartPaletteControl.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import * as React from 'react' -import { Button } from 'react-bootstrap' -import { notifySuccess } from '@framework/Operations' -import { TypeContext, ButtonsContext, IRenderButtons } from '@framework/TypeContext' -import { EntityLine, ValueLine } from '@framework/Lines' - -import { useForceUpdate } from '@framework/Hooks' -import { ChartPaletteModel, ChartMessage, ChartColorEntity } from '../Signum.Entities.Chart' -import * as ChartPaletteClient from './ChartPaletteClient' -import SelectorModal from '@framework/SelectorModal' -import { AuthAdminMessage } from '../../Authorization/Signum.Entities.Authorization' -import { EntityLink } from '@framework/Search' -import { ColorTypeaheadLine } from '../../Basics/Templates/ColorTypeahead' -import { Color } from '../../Basics/Color' - -export default React.forwardRef(function ChartPaletteControl(p: { ctx: TypeContext }, ref: React.Ref) { - - - function reload(bc: ButtonsContext) { - const pal = (bc.pack.entity as ChartPaletteModel); - ChartPaletteClient.API.fetchColorPalette(pal.typeName, true) - .then(newPack => { - ChartPaletteClient.setColorPalette(newPack!); - bc.frame.onReload({ entity: newPack!, canExecute: {} }); - }); - } - - function handleSaveClick(bc: ButtonsContext) { - const pal = (bc.pack.entity as ChartPaletteModel); - ChartPaletteClient.API.saveColorPalette(pal) - .then(newPack => { - notifySuccess(); - reload(bc); - }); - } - - function handleDeleteClick(bc: ButtonsContext) { - const pal = (bc.pack.entity as ChartPaletteModel); - ChartPaletteClient.API.deleteColorPalette(pal.typeName) - .then(newPack => { - notifySuccess(); - reload(bc); - }); - } - - async function handleNewPaletteClick(bc: ButtonsContext) { - const pal = (bc.pack.entity as ChartPaletteModel); - const palette = await SelectorModal.chooseElement(["Category10", "Category20", "Category20b", "Category20c"], { title: ChartMessage.ChooseABasePalette.niceToString() }); - if (palette) { - await ChartPaletteClient.API.newColorPalette(pal.typeName, palette); - notifySuccess(); - reload(bc); - } - } - - function renderButtons(bc: ButtonsContext) { - return [ - { button: }, - { button: }, - { button: }, - ]; - } - - React.useImperativeHandle(ref, () => ({ renderButtons }), [p.ctx.value]) - - let ctx = p.ctx; - - return ( -
-
- f.typeName)} readOnly /> -
- - - - - - - - - {ctx.mlistItemCtxs(a => a.colors).map((c, i) => )} - -
- {ChartColorEntity.nicePropertyName(a => a.related)} - - {ChartColorEntity.nicePropertyName(a => a.color)} -
- -
- ); -}); - - -export function ChartColorRow(p: { ctx: TypeContext }) { - - const forceUpdate = useForceUpdate(); - - return ( - - - - - - t.color, { formGroupStyle: "SrOnly" })} onChange={() => forceUpdate()} /> - - - ); -} - diff --git a/Signum.React.Extensions/Chart/ChartPalette/ChartPaletteController.cs b/Signum.React.Extensions/Chart/ChartPalette/ChartPaletteController.cs deleted file mode 100644 index b653c179b4..0000000000 --- a/Signum.React.Extensions/Chart/ChartPalette/ChartPaletteController.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Signum.Entities.Chart; -using Signum.Engine.Chart; -using System.ComponentModel.DataAnnotations; - -namespace Signum.React.Chart; - -public class ChartPaletteController : ControllerBase -{ - [HttpGet("api/chart/colorPalette")] - public List ColorPelettes() - { - return ChartColorLogic.Colors.Value.Keys.Select(t => TypeLogic.GetCleanName(t)).ToList(); - } - - [HttpGet("api/chart/colorPalette/{typeName}")] - public ChartPaletteModel? ColorPelette(string typeName, bool allEntities) - { - var type = TypeLogic.TryGetType(typeName); - if (type == null) - return null; - - return ChartColorLogic.GetPalette(type, allEntities); - } - - [HttpPost("api/chart/colorPalette/{typeName}/new/{paletteName}")] - public void NewColorPelette(string typeName, string paletteName) - { - ChartColorLogic.CreateNewPalette(TypeLogic.GetType(typeName), paletteName); - } - - [HttpPost("api/chart/colorPalette/{typeName}/delete")] - public void DeleteColorPalete(string typeName) - { - ChartColorLogic.DeletePalette(TypeLogic.GetType(typeName)); - } - - [HttpPost("api/chart/colorPalette/{typeName}/save")] - public void SaveColorPalete(string typeName, [Required, FromBody]ChartPaletteModel paletteModel) - { - ChartColorLogic.SavePalette(paletteModel); - } -} diff --git a/Signum.React.Extensions/Chart/ColorPalette/ColorPalette.tsx b/Signum.React.Extensions/Chart/ColorPalette/ColorPalette.tsx new file mode 100644 index 0000000000..3c13f8d756 --- /dev/null +++ b/Signum.React.Extensions/Chart/ColorPalette/ColorPalette.tsx @@ -0,0 +1,213 @@ + +import * as React from 'react' +import { Binding, EntityBaseController, EntityCombo, EntityLine, EntityTable, ValueLine } from '@framework/Lines' +import { TypeContext } from '@framework/TypeContext' +import { ColorPaletteEntity, ColorPaletteMessage, SpecificColorEmbedded } from '../Signum.Entities.Chart'; +import { colorSchemes } from './ColorUtils'; +import { classes, Dic } from '@framework/Globals'; +import * as Navigator from '@framework/Navigator'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import * as Finder from '@framework/Finder'; +import { useAPI, useForceUpdate } from '@framework/Hooks'; +import { ColorTypeaheadLine } from '../../Basics/Templates/ColorTypeahead'; +import { getTypeInfo, IBinding, tryGetTypeInfo } from '@framework/Reflection'; +import { Entity, EntityControlMessage, Lite, newMListElement, toLite } from '@framework/Signum.Entities'; +import { UserEntity } from '../../Authorization/Signum.Entities.Authorization'; +import { EntityLink } from '@framework/Search'; +import { calculateColor, ColorScheme } from './ColorPaletteClient'; + +export default function ColorPalette(p: { ctx: TypeContext }) { + const ctx = p.ctx; + const forceUpdate = useForceUpdate(); + const ctx4 = ctx.subCtx({ formGroupStyle: "Basic" }); + + const ti = ctx4.value.type && tryGetTypeInfo(ctx4.value.type.cleanName); + + const enumConverter = useAPI(() => ti?.kind == "Enum" ? Navigator.API.getEnumEntities(ti.name) : Promise.resolve(null), [ti?.name]); + + //const count = useAPI(() => ti?.queryDefined ? Finder.getQueryValue(ti, []) : Promise.resolve(null), [ti?.name]); + + function withConverter(ctx: TypeContext>): TypeContext { + return new TypeContext(ctx, undefined, ctx.propertyRoute, new ConvertBinding(ctx.binding, enumConverter!)); + } + + const colors = ctx4.value.categoryName ? colorSchemes[ctx4.value.categoryName] : null; + + + async function handleMagicWand(e: React.MouseEvent) { + + e.preventDefault(); + + if (ti == null) + return; + + var fewEntities: Lite[] | null | undefined = + ti.kind == "Enum" ? Dic.getValues(enumConverter!.enumToEntity).map(a => toLite(a)) : + ti.isLowPopulation || await Finder.getQueryValue(ti.name, []) < 20 ? await Finder.API.fetchAllLites({ types: ti.name }) : + null; + + if (fewEntities != null) { + + var step = fewEntities.length == 0 ? 1 : Math.floor(colors!.length / fewEntities.length); + if (step == 0) + step = 1; + + + ctx.value.specificColors = [...fewEntities.map((e, i) => newMListElement(SpecificColorEmbedded.New({ + entity: e, + color: colors![i * step % colors!.length] + })))]; + + forceUpdate(); + + } + else { + + var chooseEntities = await Finder.findMany({ queryName: ti.name }, { + message: ColorPaletteMessage.Select0OnlyIfYouWantToOverrideTheAutomaticColor.niceToString().formatHtml({ti.nicePluralName}), + searchControlProps: { + entityFormatter: new Finder.EntityFormatter((row, cols, sc) => !row.entity || !Navigator.isViewable(row.entity.EntityType, { isSearch: true }) ? undefined : + +
+ + {EntityBaseController.viewIcon} +
+
) + } + }); + + if (chooseEntities != null) { + + ctx.value.specificColors = [...chooseEntities.map(e => newMListElement(SpecificColorEmbedded.New({ + entity: e, + color: !colors ? undefined : calculateColor(e.id!.toString(), colors!, ctx.value.seed ?? 0) + })))]; + + forceUpdate(); + } + } + } + + return ( +
+ +
+
+ n.type)} readOnly={!ctx.value.isNew || ctx.value.specificColors.length > 0} onChange={forceUpdate} /> +
+
+ n.categoryName)} onChange={forceUpdate} + valueLineType="DropDownList" + optionItems={Dic.getKeys(colorSchemes)} + onRenderDropDownListItem={oi =>
+ + {oi.label} +
} /> +
+
+ n.seed)} /> +
+
+ + {ti != null && (ti.kind != "Enum" || enumConverter != null) && + p.specificColors)} + extraButtonsAfter={() => + + } + columns={EntityTable.typedColumns([ + { + property: p => p.entity, + template: (ectx) => + ti.kind == "Enum" ? p.entity))} /> : + ti?.isLowPopulation ? p.entity)} type={{ name: ctx4.value.type.cleanName, isLite: true }} /> : + p.entity)} type={{ name: ctx4.value.type.cleanName, isLite: true }} />, + headerHtmlAttributes: { style: { width: "40%" } }, + }, + { + property: p => p.color, + template: (ectx) => a.color)} colors={colors as (string[] | null)} />, + headerHtmlAttributes: { style: { width: "40%" } }, + }, + ])} + /> + } +
+ ); +} + +function ColorSelector(p: { ctx: TypeContext, colors: string[] | null }) { + + const [custom, setCustom] = React.useState(false); + + React.useEffect(() => { + setCustom(p.colors == null || p.ctx.value != null && !p.colors.contains(p.ctx.value)); + }, [p.colors]) + + if (custom || p.colors == null) + return getSwitchModelButton()} /> + + return + + {oi.label} + + } + extraButtons={vl => getSwitchModelButton()} + />; + + function getSwitchModelButton(): React.ReactElement { + return ( + { + e.preventDefault(); + setCustom(!custom); + }}> + + + ); + } +} + + + +class ConvertBinding implements IBinding { + + parent: IBinding | null>; + converter: Navigator.EnumConverter; + + constructor(binding: IBinding>, enumEntities: Navigator.EnumConverter) { + this.parent = binding; + this.suffix = this.parent.suffix; + this.converter = enumEntities; + } + + getValue(): string | null { + var val = this.parent.getValue(); + return val && this.converter.idToEnum[val.id!]; + } + + setValue(val: string): void { + this.parent.setValue(val == null ? null : toLite(this.converter.enumToEntity[val])); + } + suffix: string; + getIsReadonly(): boolean { + return this.parent.getIsReadonly(); + } + getError(): string | undefined { + return this.parent.getError() + } + setError(value: string | undefined): void { + return this.parent.setError(value); + } +} + + + diff --git a/Signum.React.Extensions/Chart/ColorPalette/ColorPaletteClient.tsx b/Signum.React.Extensions/Chart/ColorPalette/ColorPaletteClient.tsx new file mode 100644 index 0000000000..508c341764 --- /dev/null +++ b/Signum.React.Extensions/Chart/ColorPalette/ColorPaletteClient.tsx @@ -0,0 +1,119 @@ +import { ajaxPost, ajaxGet } from '@framework/Services'; +import { EntitySettings } from '@framework/Navigator' +import * as React from 'react'; +import * as Navigator from '@framework/Navigator' +import * as AppContext from '@framework/AppContext' +import { ChartMessage, ColorPaletteEntity } from '../Signum.Entities.Chart' +import * as ColorUtils from './ColorUtils' +import { PseudoType, getTypeName, tryGetTypeInfo } from '@framework/Reflection'; +import { Lite } from '@framework/Signum.Entities'; +import * as Constructor from '@framework/Constructor'; +import * as Finder from '@framework/Finder'; +import { Dic } from '@framework/Globals'; +import { TypeEntity } from '@framework/Signum.Entities.Basics'; +import { getColorInterpolation } from './ColorUtils'; + +export function start(options: { routes: JSX.Element[] }) { + Navigator.addSettings(new EntitySettings(ColorPaletteEntity, e => import('./ColorPalette'))); + + Finder.registerPropertyFormatter(ColorPaletteEntity.tryPropertyRoute(a => a.categoryName), + new Finder.CellFormatter(cat => cat && {cat}, true)); + + Constructor.registerConstructor(ColorPaletteEntity, props => ColorPaletteEntity.New({ seed: 0, categoryName: Dic.getKeys(ColorUtils.colorSchemes).first(), ...props })); + + Navigator.registerEntityChanged(ColorPaletteEntity, () => Dic.clear(colorPalette)); + + AppContext.clearSettingsActions.push(() => Dic.clear(colorPalette)); +} + +export interface ColorPalette { + lite: Lite; + typeName: string; + categoryName: string; + seed: number; + specificColors: { [key: string]: string }; + + cachedColors: { [key: string]: string }; + palette: ReadonlyArray; + getColor(key: string): string; +} + +export let colorPalette: { [typeName: string]: Promise } = {}; +export function getColorPalette(type: PseudoType): Promise { + + const typeName = getTypeName(type); + + if (colorPalette[typeName] !== undefined) + return colorPalette[typeName]; + + return colorPalette[typeName] = API.colorPalette(typeName).then(pal => { + if (pal == null) + return pal; + + pal.cachedColors = {}; + pal.palette = ColorUtils.colorSchemes[pal.categoryName]; + + if (pal.palette == null) + throw new Error("Inavlid ColorPaletter categoryName: " + pal.categoryName); + + pal.getColor = getColor; + return pal; + }); +} + +function getColor(this: ColorPalette, key: string) { + + let color = this.cachedColors[key]; + if (color != null) + return color; + + color = this.specificColors[key]; + if (color != null) { + return this.cachedColors[key] = color; + } + + color = calculateColor(key, this.palette, this.seed) + + return this.cachedColors[key] = color; +} + +export function calculateColor(key: string, palette: readonly string[], seed: number) : string { + var hc = hashCode(key); + + if (hc < 0) + hc = -hc; + + return palette[(hc + seed) % palette.length]; +} + + + +function hashCode(s: string): number { + var h = 0; + for (let i = 0; i < s.length; i++) + h = Math.imul(31, h) + s.charCodeAt(i) | 0; + + return h; +} + +export module API { + + export function colorPalette(typeName: string): Promise { + return ajaxGet({ url: `~/api/colorPalette/${typeName}`, }); + } +} + +export function ColorScheme(p: { colorScheme: string }) { + return (
+ {ColorUtils.colorSchemes[p.colorScheme]?.map(c =>
)} +
); +} + +export function ColorInterpolate(p: { colorInterpolator: string }) { + + const inter = getColorInterpolation(p.colorInterpolator); + + return (
+ {inter && Array.range(0, 10).map(i =>
)} +
); +} diff --git a/Signum.React.Extensions/Chart/ColorPalette/ColorPaletteController.cs b/Signum.React.Extensions/Chart/ColorPalette/ColorPaletteController.cs new file mode 100644 index 0000000000..7f947df6d4 --- /dev/null +++ b/Signum.React.Extensions/Chart/ColorPalette/ColorPaletteController.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Mvc; +using Signum.Entities.Chart; +using Signum.Engine.Chart; +using System.ComponentModel.DataAnnotations; +using Signum.Engine.Authorization; +using HtmlAgilityPack; +using Signum.Entities.Basics; + +namespace Signum.React.Chart; + +public class ColorPaletteController : ControllerBase +{ + [HttpGet("api/colorPalette/{typeName}")] + public ColorPaletteTS? ColorPelette(string typeName) + { + Type type = TypeLogic.GetType(typeName); + + Schema.Current.AssertAllowed(type, true); + + var palette = ColorPaletteLogic.ColorPaletteCache.Value.TryGetC(type); + if (palette == null) + return null; + + return new ColorPaletteTS + { + Lite = palette.ToLite(), + TypeName = TypeLogic.GetCleanName(type), + CategoryName = palette.CategoryName, + Seed = palette.Seed, + SpecificColors = type.IsEnum ? + palette.SpecificColors.ToDictionary(a => EnumEntity.ToEnum(a.Entity).ToString(), a => a.Color) : + palette.SpecificColors.ToDictionary(a => a.Entity.Id.ToString(), a => a.Color) + }; + } +} + +public class ColorPaletteTS +{ + public required Lite Lite { get; set; } + public required string TypeName { get; set; } + public required string CategoryName { get; set; } + public required int Seed { get; set; } + public required Dictionary SpecificColors { get; set; } +} diff --git a/Signum.React.Extensions/Chart/ColorPalette/ColorUtils.ts b/Signum.React.Extensions/Chart/ColorPalette/ColorUtils.ts new file mode 100644 index 0000000000..6f037d055a --- /dev/null +++ b/Signum.React.Extensions/Chart/ColorPalette/ColorUtils.ts @@ -0,0 +1,194 @@ +import a from "bpmn-js/lib/features/search"; +import * as d3 from "d3" +import * as d3sc from "d3-scale-chromatic"; +import { MemoRepository } from "../D3Scripts/Components/ReactChart"; + + +interface Interpolator { + categoryName: string; + name: string; + interpolate: (value: number) => string; +} + +//https://github.com/d3/d3-scale-chromatic +export const colorInterpolators: { [name: string]: Interpolator } = [ + { categoryName: "Diverging", name: "BrBG", interpolate: d3sc.interpolateBrBG }, + { categoryName: "Diverging", name: "PRGn", interpolate: d3sc.interpolatePRGn }, + { categoryName: "Diverging", name: "PiYG", interpolate: d3sc.interpolatePiYG }, + { categoryName: "Diverging", name: "PuOr", interpolate: d3sc.interpolatePuOr }, + { categoryName: "Diverging", name: "RdBu", interpolate: d3sc.interpolateRdBu }, + { categoryName: "Diverging", name: "RdGy", interpolate: d3sc.interpolateRdGy }, + { categoryName: "Diverging", name: "RdYlBu", interpolate: d3sc.interpolateRdYlBu }, + { categoryName: "Diverging", name: "RdYlGn", interpolate: d3sc.interpolateRdYlGn }, + { categoryName: "Diverging", name: "Spectral", interpolate: d3sc.interpolateSpectral }, + + { categoryName: "Single Hue", name: "Blues", interpolate: d3sc.interpolateBlues }, + { categoryName: "Single Hue", name: "Greens", interpolate: d3sc.interpolateGreens }, + { categoryName: "Single Hue", name: "Greys", interpolate: d3sc.interpolateGreys }, + { categoryName: "Single Hue", name: "Oranges", interpolate: d3sc.interpolateOranges }, + { categoryName: "Single Hue", name: "Purples", interpolate: d3sc.interpolatePurples }, + { categoryName: "Single Hue", name: "Reds", interpolate: d3sc.interpolateReds }, + + { categoryName: "Multi-Hue", name: "Turbo", interpolate: d3sc.interpolateTurbo }, + { categoryName: "Multi-Hue", name: "Viridis", interpolate: d3sc.interpolateViridis }, + { categoryName: "Multi-Hue", name: "Inferno", interpolate: d3sc.interpolateInferno }, + { categoryName: "Multi-Hue", name: "Magma", interpolate: d3sc.interpolateMagma }, + { categoryName: "Multi-Hue", name: "Plasma", interpolate: d3sc.interpolatePlasma }, + { categoryName: "Multi-Hue", name: "Cividis", interpolate: d3sc.interpolateCividis }, + { categoryName: "Multi-Hue", name: "Warm", interpolate: d3sc.interpolateWarm }, + { categoryName: "Multi-Hue", name: "Cool", interpolate: d3sc.interpolateCool }, + { categoryName: "Multi-Hue", name: "CubehelixDefault", interpolate: d3sc.interpolateCubehelixDefault }, + + { categoryName: "Multi-Hue", name: "BuGn", interpolate: d3sc.interpolateBuGn }, + { categoryName: "Multi-Hue", name: "BuPu", interpolate: d3sc.interpolateBuPu }, + { categoryName: "Multi-Hue", name: "GnBu", interpolate: d3sc.interpolateGnBu }, + { categoryName: "Multi-Hue", name: "OrRd", interpolate: d3sc.interpolateOrRd }, + { categoryName: "Multi-Hue", name: "PuBuGn", interpolate: d3sc.interpolatePuBuGn }, + { categoryName: "Multi-Hue", name: "PuBu", interpolate: d3sc.interpolatePuBu }, + { categoryName: "Multi-Hue", name: "PuRd", interpolate: d3sc.interpolatePuRd }, + { categoryName: "Multi-Hue", name: "RdPu", interpolate: d3sc.interpolateRdPu }, + { categoryName: "Multi-Hue", name: "YlGnBu", interpolate: d3sc.interpolateYlGnBu }, + { categoryName: "Multi-Hue", name: "YlGn", interpolate: d3sc.interpolateTurbo }, + { categoryName: "Multi-Hue", name: "YlOrBr", interpolate: d3sc.interpolateYlOrBr }, + { categoryName: "Multi-Hue", name: "YlOrRd", interpolate: d3sc.interpolateYlOrRd }, + + { categoryName: "Cyclical", name: "Rainbow", interpolate: d3sc.interpolateRainbow }, + { categoryName: "Cyclical", name: "Sidebow", interpolate: d3sc.interpolateSinebow }, +].toObject(a => a.name); + + +export function getColorInterpolation(interpolationName: string | undefined | null): ((value: number) => string) | undefined { + + if (interpolationName == null) + return undefined; + + var inver = interpolationName.startsWith("-"); + + if (inver) + interpolationName = interpolationName.after("-"); + + var interp = colorInterpolators[interpolationName]?.interpolate; + + if (inver) + return val => interp(1 - val); + + return interp; +} + + + + + +//https://fluentcolors.com/ +export const fluentColors: ReadonlyArray = [ + "#FFB900", + "#FF8C00", + "#F7630C", + "#CA5010", + "#DA3B01", + "#EF6950", + "#D13438", + "#FF4343", + "#E74856", + "#E81123", + "#EA005E", + "#C30052", + "#E3008C", + "#BF0077", + "#C239B3", + "#9A0089", + "#0078D7", + "#0063B1", + "#8E8CD8", + "#6B69D6", + "#8764B8", + "#744DA9", + "#B146C2", + "#881798", + "#0099BC", + "#2D7D9A", + "#00B7C3", + "#038387", + "#00B294", + "#018574", + "#00CC6A", + "#10893E", + "#7A7574", + "#5D5A58", + "#68768A", + "#515C6B", + "#567C73", + "#486860", + "#498205", + "#107C10", + "#767676", + "#4C4A48", + "#69797E", + "#4A5459", + "#647C64", + "#525E54", + "#847545", + "#7E735F", +]; + +//https://materialui.co/flatuicolors/ +export const flatUIColors: ReadonlyArray = [ + + "#F1C40F", + "#F39C12", + "#E67E22", + "#D35400", + "#E74C3C", + "#C0392B", + + "#9B59B6", + "#8E44AD", + + "#3498DB", + "#2980B9", + + "#1ABC9C", + "#16A085", + "#2ECC71", + "#27AE60", + + "#ECF0F1", + "#BDC3C7", + "#34495E", + "#2C3E50", + "#95A5A6", + "#7F8C8D" +]; + +//https://materialui.co/metrocolors/ +export const metroColors: ReadonlyArray = [ + + "#A4C400", "#60A917", "#008A00", "#00ABA9", "#1BA1E2", "#0050EF", "#6A00FF", "#AA00FF", "#F472D0", "#D80073", "#A20025", "#E51400", "#FA6800", "#F0A30A", + "#E3C800", "#825A2C", "#6D8764", "#647687", "#76608A", "#A0522D", +]; + + +export const materialColors: ReadonlyArray = [ + + "#E53935", "#D81B60", "#8E24AA", "#5E35B1", "#3949AB", "#1E88E5", "#039BE5", "#00ACC1", "#00897B", "#43A047", "#7CB342", "#C0CA33", "#FDD835", "#FFB300", + "#FB8C00", "#F4511E", "#6D4C41", "#757575", "#546E7A" +]; + + +export const colorSchemes: { [name: string]: ReadonlyArray } = { + "fluent": fluentColors, + "flatui": flatUIColors, + "metro": metroColors, + "material": materialColors, + "category10": d3.schemeCategory10, + "accent": d3sc.schemeAccent, + "dark2": d3sc.schemeDark2, + "paired": d3sc.schemePaired, + "pastel1": d3sc.schemePastel1, + "pastel2": d3sc.schemePastel2, + "set1": d3sc.schemeSet1, + "set2": d3sc.schemeSet2, + "set3": d3sc.schemeSet3, + "tableau10": d3sc.schemeTableau10, +}; + diff --git a/Signum.React.Extensions/Chart/D3Scripts/Components/ChartUtils.ts b/Signum.React.Extensions/Chart/D3Scripts/Components/ChartUtils.ts index 0f74fe3005..d4af5c89b5 100644 --- a/Signum.React.Extensions/Chart/D3Scripts/Components/ChartUtils.ts +++ b/Signum.React.Extensions/Chart/D3Scripts/Components/ChartUtils.ts @@ -1,6 +1,5 @@ import { DateTime, DurationUnit, Duration, DateTimeUnit } from "luxon" import * as d3 from "d3" -import * as d3sc from "d3-scale-chromatic"; import { ChartTable, ChartColumn, ChartRow } from "../../ChartClient" import { parseLite } from "@framework/Signum.Entities" import * as Navigator from '@framework/Navigator' @@ -9,6 +8,8 @@ import { tryGetTypeInfo } from "@framework/Reflection"; import { ChartRequestModel } from "../../Signum.Entities.Chart"; import { isFilterGroupOption, isFilterGroupOptionParsed, FilterConditionOptionParsed, FilterOptionParsed, QueryToken, FilterConditionOption } from "@framework/FindOptions"; import { MemoRepository } from "./ReactChart"; +import * as ColorUtils from "../../ColorPalette/ColorUtils" +import { colorInterpolators } from "../../ColorPalette/ColorUtils" @@ -426,87 +427,6 @@ export function getCurveByName(curveName: string): d3.CurveFactoryLineOnly | und return undefined; } -export function getColorInterpolation(interpolationName: string | undefined | null): ((value: number) => string) | undefined { - switch (interpolationName) { - case "YlGn": return d3sc.interpolateYlGn; - case "YlGnBu": return d3sc.interpolateYlGnBu; - case "GnBu": return d3sc.interpolateGnBu; - case "BuGn": return d3sc.interpolateBuGn; - case "PuBuGn": return d3sc.interpolatePuBuGn; - case "PuBu": return d3sc.interpolatePuBu; - case "BuPu": return d3sc.interpolateBuPu; - case "RdPu": return d3sc.interpolateRdPu; - case "PuRd": return d3sc.interpolatePuRd; - case "OrRd": return d3sc.interpolateOrRd; - case "YlOrRd": return d3sc.interpolateYlOrRd; - case "YlOrBr": return d3sc.interpolateYlOrBr; - case "Purples": return d3sc.interpolatePurples; - case "Blues": return d3sc.interpolateBlues; - case "Greens": return d3sc.interpolateGreens; - case "Oranges": return d3sc.interpolateOranges; - case "Reds": return d3sc.interpolateReds; - case "Greys": return d3sc.interpolateGreys; - case "PuOr": return d3sc.interpolatePuOr; - case "BrBG": return d3sc.interpolateBrBG; - case "PRGn": return d3sc.interpolatePRGn; - case "PiYG": return d3sc.interpolatePiYG; - case "RdBu": return d3sc.interpolateRdBu; - case "RdGy": return d3sc.interpolateRdGy; - case "RdYlBu": return d3sc.interpolateRdYlBu; - case "Spectral": return d3sc.interpolateSpectral; - case "RdYlGn": return d3sc.interpolateRdYlGn; - } - - return undefined; -} - -export function getColorScheme(schemeName: string | null | undefined, k: number | undefined = 11): ReadonlyArray | undefined { - switch (schemeName) { - case "category10": return d3.schemeCategory10; - case "accent": return d3sc.schemeAccent; - case "dark2": return d3sc.schemeDark2; - case "paired": return d3sc.schemePaired; - case "pastel1": return d3sc.schemePastel1; - case "pastel2": return d3sc.schemePastel2; - case "set1": return d3sc.schemeSet1; - case "set2": return d3sc.schemeSet2; - case "set3": return d3sc.schemeSet3; - case "BrBG[K]": return d3sc.schemeBrBG[k]; - case "PRGn[K]": return d3sc.schemePRGn[k]; - case "PiYG[K]": return d3sc.schemePiYG[k]; - case "PuOr[K]": return d3sc.schemePuOr[k]; - case "RdBu[K]": return d3sc.schemeRdBu[k]; - case "RdGy[K]": return d3sc.schemeRdGy[k]; - case "RdYlBu[K]": return d3sc.schemeRdYlBu[k]; - case "RdYlGn[K]": return d3sc.schemeRdYlGn[k]; - case "Spectral[K]": return d3sc.schemeSpectral[k]; - case "Blues[K]": return d3sc.schemeBlues[k]; - case "Greys[K]": return d3sc.schemeGreys[k]; - case "Oranges[K]": return d3sc.schemeOranges[k]; - case "Purples[K]": return d3sc.schemePurples[k]; - case "Reds[K]": return d3sc.schemeReds[k]; - case "BuGn[K]": return d3sc.schemeBuGn[k]; - case "BuPu[K]": return d3sc.schemeBuPu[k]; - case "OrRd[K]": return d3sc.schemeOrRd[k]; - case "PuBuGn[K]": return d3sc.schemePuBuGn[k]; - case "PuBu[K]": return d3sc.schemePuBu[k]; - case "PuRd[K]": return d3sc.schemePuRd[k]; - case "RdPu[K]": return d3sc.schemeRdPu[k]; - case "YlGnBu[K]": return d3sc.schemeYlGnBu[k]; - case "YlGn[K]": return d3sc.schemeYlGn[k]; - case "YlOrBr[K]": return d3sc.schemeYlOrBr[k]; - case "YlOrRd[K]": return d3sc.schemeYlOrRd[k]; - } - - return undefined; -} - -interface CachedColorOrdinal { - category: string; - categorySteps: number; - scale: d3.ScaleOrdinal; -} - export function colorCategory(parameters: { [name: string]: string }, domain: string[], memo: MemoRepository, memoKey?: string, deps?: []): d3.ScaleOrdinal { var category = parameters["ColorCategory"]; @@ -514,13 +434,18 @@ export function colorCategory(parameters: { [name: string]: string }, domain: st return memo.memo>(memoKey ?? "colorCategory", [category, categorySteps, ...(deps ?? [])], () => { - var scheme = getColorScheme(category, categorySteps); + var scheme = ColorUtils.colorSchemes[category]; var scale = d3.scaleOrdinal(scheme); domain.forEach(a => scale(a)); return scale; }); } +export function getColorInterpolation(interpolationName: string | undefined | null): ((value: number) => string) | undefined { + + return ColorUtils.getColorInterpolation(interpolationName); +} + diff --git a/Signum.React.Extensions/Chart/GoogleMapScripts/Markermap.tsx b/Signum.React.Extensions/Chart/GoogleMapScripts/Markermap.tsx index 2dd7ed9dd3..74218e6cac 100644 --- a/Signum.React.Extensions/Chart/GoogleMapScripts/Markermap.tsx +++ b/Signum.React.Extensions/Chart/GoogleMapScripts/Markermap.tsx @@ -14,7 +14,7 @@ export default function renderMarkermapChart(p: ChartClient.ChartScriptProps) { return } -export function MarkermapChartImp({ data, parameters, onDrillDown }: ChartClient.ChartScriptProps) { +export function MarkermapChartImp({ data, parameters, onDrillDown, memo }: ChartClient.ChartScriptProps) { const divElement = React.useRef(null); @@ -104,8 +104,7 @@ export function MarkermapChartImp({ data, parameters, onDrillDown }: ChartClient color = r => colorInterpolator!(scaleFunc(colorScaleColumn!.getValue(r))!); } else if (colorSchemeColumn != null) { - var scheme = ChartUtils.getColorScheme(parameters["ColorCategory"])!; - var categoryColor = d3.scaleOrdinal(scheme).domain(data.rows.map(colorSchemeColumn.getValueKey)); + var categoryColor = ChartUtils.colorCategory(parameters, data.rows.map(colorSchemeColumn.getValueKey), memo); color = r => colorSchemeColumn!.getValueColor(r) ?? categoryColor(colorSchemeColumn!.getValueKey(r)); } diff --git a/Signum.React.Extensions/Chart/Signum.Entities.Chart.ts b/Signum.React.Extensions/Chart/Signum.Entities.Chart.ts index 7d0edcc957..3c74dc73e0 100644 --- a/Signum.React.Extensions/Chart/Signum.Entities.Chart.ts +++ b/Signum.React.Extensions/Chart/Signum.Entities.Chart.ts @@ -27,13 +27,6 @@ export interface ChartScriptParameterEmbedded { export type IChartBase = ChartRequestModel | UserChartEntity; -export const ChartColorEntity = new Type("ChartColor"); -export interface ChartColorEntity extends Entities.Entity { - Type: "ChartColor"; - related: Entities.Lite; - color: string; -} - export const ChartColumnEmbedded = new Type("ChartColumnEmbedded"); export interface ChartColumnEmbedded extends Entities.EmbeddedEntity { Type: "ChartColumnEmbedded"; @@ -103,13 +96,6 @@ export module ChartMessage { export const QueryResultReachedMaxRows0 = new MessageKey("ChartMessage", "QueryResultReachedMaxRows0"); } -export const ChartPaletteModel = new Type("ChartPaletteModel"); -export interface ChartPaletteModel extends Entities.ModelEntity { - Type: "ChartPaletteModel"; - typeName: string; - colors: Entities.MList; -} - export const ChartParameterEmbedded = new Type("ChartParameterEmbedded"); export interface ChartParameterEmbedded extends Entities.EmbeddedEntity { Type: "ChartParameterEmbedded"; @@ -121,7 +107,8 @@ export const ChartParameterType = new EnumType("ChartParamet export type ChartParameterType = "Enum" | "Number" | - "String"; + "String" | + "Special"; export module ChartPermission { export const ViewCharting : Authorization.PermissionSymbol = registerSymbol("Permission", "ChartPermission.ViewCharting"); @@ -141,6 +128,25 @@ export interface ChartScriptSymbol extends Entities.Symbol { Type: "ChartScript"; } +export const ColorPaletteEntity = new Type("ColorPalette"); +export interface ColorPaletteEntity extends Entities.Entity { + Type: "ColorPalette"; + type: Basics.TypeEntity; + categoryName: string; + seed: number; + specificColors: Entities.MList; +} + +export module ColorPaletteMessage { + export const FillAutomatically = new MessageKey("ColorPaletteMessage", "FillAutomatically"); + export const Select0OnlyIfYouWantToOverrideTheAutomaticColor = new MessageKey("ColorPaletteMessage", "Select0OnlyIfYouWantToOverrideTheAutomaticColor"); +} + +export module ColorPaletteOperation { + export const Save : Entities.ExecuteSymbol = registerSymbol("Operation", "ColorPaletteOperation.Save"); + export const Delete : Entities.DeleteSymbol = registerSymbol("Operation", "ColorPaletteOperation.Delete"); +} + export module D3ChartScript { export const Bars : ChartScriptSymbol = registerSymbol("ChartScript", "D3ChartScript.Bars"); export const Columns : ChartScriptSymbol = registerSymbol("ChartScript", "D3ChartScript.Columns"); @@ -170,6 +176,18 @@ export module HtmlChartScript { export const PivotTable : ChartScriptSymbol = registerSymbol("ChartScript", "HtmlChartScript.PivotTable"); } +export const SpecialParameterType = new EnumType("SpecialParameterType"); +export type SpecialParameterType = + "ColorCategory" | + "ColorInterpolate"; + +export const SpecificColorEmbedded = new Type("SpecificColorEmbedded"); +export interface SpecificColorEmbedded extends Entities.EmbeddedEntity { + Type: "SpecificColorEmbedded"; + entity: Entities.Lite; + color: string; +} + export module SvgMapsChartScript { export const SvgMap : ChartScriptSymbol = registerSymbol("ChartScript", "SvgMapsChartScript.SvgMap"); } diff --git a/Signum.React.Extensions/Chart/Templates/ChartBuilder.tsx b/Signum.React.Extensions/Chart/Templates/ChartBuilder.tsx index 26d8a6b661..0512b4c5bd 100644 --- a/Signum.React.Extensions/Chart/Templates/ChartBuilder.tsx +++ b/Signum.React.Extensions/Chart/Templates/ChartBuilder.tsx @@ -5,11 +5,14 @@ import { is } from '@framework/Signum.Entities' import { ValueLine, ValueLineProps, OptionItem } from '@framework/Lines' import { ChartColumnEmbedded, IChartBase, ChartMessage, ChartParameterEmbedded, ChartRequestModel } from '../Signum.Entities.Chart' import * as ChartClient from '../ChartClient' -import * as ChartPaletteClient from '../ChartPalette/ChartPaletteClient' import { ChartScript, ChartScriptParameter, EnumValueList } from '../ChartClient' import { ChartColumn } from './ChartColumn' +import * as ColorPaletteClient from '../ColorPalette/ColorPaletteClient' +import { ColorInterpolate, ColorScheme } from '../ColorPalette/ColorPaletteClient' import { useForceUpdate, useAPI } from '@framework/Hooks' import { UserState } from '../../Authorization/Signum.Entities.Authorization' +import { colorInterpolators, colorSchemes } from '../ColorPalette/ColorUtils' +import { Dic } from '@framework/Globals' export interface ChartBuilderProps { ctx: TypeContext; /*IChart*/ @@ -24,7 +27,6 @@ export interface ChartBuilderProps { export default function ChartBuilder(p: ChartBuilderProps) { const forceUpdate = useForceUpdate(); - const colorPalettes = useAPI(signal => ChartPaletteClient.getColorPaletteTypes(), []); const chartScripts = useAPI(signal => ChartClient.getChartScripts(), []); function chartTypeImgClass(script: ChartScript): string { @@ -115,11 +117,11 @@ export default function ChartBuilder(p: ChartBuilderProps) { - {chartScript && colorPalettes && mlistItemContext(p.ctx.subCtx(c => c.columns, { formSize: "xs" })).map((ctx, i) => + {chartScript && mlistItemContext(p.ctx.subCtx(c => c.columns, { formSize: "xs" })).map((ctx, i) => handleTokenChange(ctx.value)} onRedraw={handleOnRedraw} - onOrderChanged={handleOrderChart} colorPalettes={colorPalettes!} columnIndex={i} parameterDic={parameterDic} />) + onOrderChanged={handleOrderChart} columnIndex={i} parameterDic={parameterDic} />) } @@ -176,11 +178,48 @@ export function Parameters(props: { function ParameterValueLine({ ctx, scriptParameter, chart, onRedraw }: { ctx: TypeContext, scriptParameter: ChartScriptParameter, onRedraw?: () => void, chart: IChartBase }) { - const forceUpdate = useForceUpdate(); + + if (scriptParameter.type == "Special") { + var sp = scriptParameter.valueDefinition as ChartClient.SpecialParameter; + + if (sp.specialParameterType == "ColorCategory") { + return a.value)} label={scriptParameter.name} onChange={onRedraw} + valueLineType="DropDownList" + optionItems={Dic.getKeys(colorSchemes)} + onRenderDropDownListItem={oi =>
+ + {oi.label} +
} /> + } + + if (sp.specialParameterType == "ColorInterpolate") { + return a.value)} label={scriptParameter.name} onChange={onRedraw} + valueLineType="DropDownList" + optionItems={Dic.getKeys(colorInterpolators).map(a => (ctx.value.value?.startsWith("-") ? "-" : "") + a)} + onRenderDropDownListItem={oi =>
+ + {oi.label} +
} + helpText={} + /> + } + } + const token = scriptParameter.columnIndex == undefined ? undefined : chart.columns[scriptParameter.columnIndex].element.token?.token; + const vl: ValueLineProps = { ctx: ctx.subCtx(a => a.value), label: scriptParameter.name!, diff --git a/Signum.React.Extensions/Chart/Templates/ChartColumn.tsx b/Signum.React.Extensions/Chart/Templates/ChartColumn.tsx index a5fe4d6d1b..35a4ef7051 100644 --- a/Signum.React.Extensions/Chart/Templates/ChartColumn.tsx +++ b/Signum.React.Extensions/Chart/Templates/ChartColumn.tsx @@ -5,13 +5,13 @@ import { TypeContext, StyleContext } from '@framework/TypeContext' import { tryGetTypeInfos, TypeInfo, isTypeEnum } from '@framework/Reflection' import * as Navigator from '@framework/Navigator' import { ValueLine, FormGroup } from '@framework/Lines' -import { ChartColumnEmbedded, IChartBase, ChartMessage, ChartColorEntity, ChartColumnType, ChartParameterEmbedded } from '../Signum.Entities.Chart' +import { ChartColumnEmbedded, IChartBase, ChartMessage, ChartColumnType, ChartParameterEmbedded, ColorPaletteEntity } from '../Signum.Entities.Chart' import * as ChartClient from '../ChartClient' import { ChartScriptColumn, ChartScript } from '../ChartClient' -import * as ChartPaletteClient from '../ChartPalette/ChartPaletteClient' +import * as ColorPaletteClient from '../ColorPalette/ColorPaletteClient' import QueryTokenEntityBuilder from '../../UserAssets/Templates/QueryTokenEmbeddedBuilder' -import { External } from '@framework/Signum.Entities'; -import { useForceUpdate } from '@framework/Hooks' +import { External, JavascriptMessage, toLite } from '@framework/Signum.Entities'; +import { useAPI, useForceUpdate } from '@framework/Hooks' import { Parameters } from './ChartBuilder' export interface ChartColumnProps { @@ -21,7 +21,6 @@ export interface ChartColumnProps { chartScript: ChartScript; chartBase: IChartBase; queryKey: string; - colorPalettes: string[]; onRedraw: () => void; parameterDic: { [name: string]: TypeContext }, onOrderChanged: (chartColumn: ChartColumnEmbedded, e: React.MouseEvent) => void; @@ -93,7 +92,7 @@ export function ChartColumn(p: ChartColumnProps) { const t = token?.token!.type; - if (t == undefined || Navigator.isReadOnly(ChartColorEntity, { ignoreTypeIsReadonly: true })) + if (t == undefined || Navigator.isReadOnly(ColorPaletteEntity)) return []; if (!t.isLite && !isTypeEnum(t.name)) @@ -165,7 +164,7 @@ export function ChartColumn(p: ChartColumnProps) {
{getColorPalettes().map((t, i) =>
- {t && } + {t && }
) }
@@ -189,22 +188,38 @@ function getTitle(ct: ChartColumnType): ChartColumnType[] { export interface ChartPaletteLinkProps { type: TypeInfo; - currentPalettes: string[]; refresh: () => void; ctx: StyleContext; } -export const ChartPaletteLink = (props: ChartPaletteLinkProps) => - - { - e.preventDefault(); - ChartPaletteClient.navigatePalette(props.type) - .then(() => props.refresh()); - }}> - {props.currentPalettes.contains(props.type.name) ? ChartMessage.ViewPalette.niceToString() : ChartMessage.CreatePalette.niceToString()} - - ; +export function ChartPaletteLink(p: ChartPaletteLinkProps) { + + const palette = useAPI(() => ColorPaletteClient.getColorPalette(p.type.name), [p.type.name]); + + return ( + + {palette === undefined ? + + {JavascriptMessage.loading.niceToString()} + : + { + e.preventDefault(); + if (palette) + Navigator.view(palette.lite); + else { + Navigator.API.getType(p.type.name) + .then(t => Navigator.view(ColorPaletteEntity.New({ + type: t! + }))); + } + }}> + {palette ? ChartMessage.ViewPalette.niceToString() : ChartMessage.CreatePalette.niceToString()} + + } + + ); +} diff --git a/Signum.React.Extensions/Map/OperationMAp.cs b/Signum.React.Extensions/Map/OperationMAp.cs index 5ad99e9f3e..fec6b55c1f 100644 --- a/Signum.React.Extensions/Map/OperationMAp.cs +++ b/Signum.React.Extensions/Map/OperationMAp.cs @@ -43,7 +43,7 @@ from e in Enum.GetValues(t.UnNullify()).Cast() key = e.ToString(), niceName = e.NiceToString(), isSpecial = t == typeof(DefaultState), - color = Engine.Chart.ChartColorLogic.ColorFor(EnumEntity.FromEnumUntyped(e)), + color = Engine.Chart.ColorPaletteLogic.ColorFor(EnumEntity.FromEnumUntyped(e)), token = tokens.GetOrThrow(e.GetType()), }).ToList(), diff --git a/Signum.React.Extensions/Signum.React.Extensions.csproj b/Signum.React.Extensions/Signum.React.Extensions.csproj index 096bd8de0d..cd6393753c 100644 --- a/Signum.React.Extensions/Signum.React.Extensions.csproj +++ b/Signum.React.Extensions/Signum.React.Extensions.csproj @@ -77,8 +77,6 @@ - - diff --git a/Signum.React/ApiControllers/ReflectionController.cs b/Signum.React/ApiControllers/ReflectionController.cs index 1638bd26fd..92099408ff 100644 --- a/Signum.React/ApiControllers/ReflectionController.cs +++ b/Signum.React/ApiControllers/ReflectionController.cs @@ -32,6 +32,14 @@ public ActionResult> Types() return TypeLogic.TryGetType(typeName)?.ToTypeEntity(); } + [HttpGet("api/reflection/enumEntities/{typeName}")] + public Dictionary GetEnumEntities(string typeName) + { + var type = EnumEntity.Extract(TypeLogic.GetType(typeName))!; + + return EnumEntity.GetValues(type).ToDictionary(a => a.ToString(), a => EnumEntity.FromEnumUntyped(a)); + } + [HttpPost("api/registerClientError"), ValidateModelFilter, SignumAllowAnonymous] public void ClientError([Required, FromBody] ClientErrorModel error) diff --git a/Signum.React/Scripts/Lines/ValueLine.tsx b/Signum.React/Scripts/Lines/ValueLine.tsx index 7455a15027..ba6b51fcce 100644 --- a/Signum.React/Scripts/Lines/ValueLine.tsx +++ b/Signum.React/Scripts/Lines/ValueLine.tsx @@ -344,7 +344,10 @@ function internalDropDownList(vl: ValueLineController) { vl.setValue(e.value); }; - var oi = optionItems.single(a => a.value == s.ctx.value); + var oi = optionItems.singleOrNull(a => a.value == s.ctx.value) ?? { + value: s.ctx.value, + label: s.ctx.value, + }; return ( diff --git a/Signum.React/Scripts/Navigator.tsx b/Signum.React/Scripts/Navigator.tsx index b886c87037..58056d60c9 100644 --- a/Signum.React/Scripts/Navigator.tsx +++ b/Signum.React/Scripts/Navigator.tsx @@ -1,9 +1,9 @@ import * as React from "react" import { Dic, classes, softCast, } from './Globals'; import { ajaxGet, ajaxPost, clearContextHeaders } from './Services'; -import { Lite, Entity, ModifiableEntity, EntityPack, isEntity, isLite, isEntityPack, toLite, liteKey, FrameMessage, ModelEntity, getToString, isModifiableEntity } from './Signum.Entities'; +import { Lite, Entity, ModifiableEntity, EntityPack, isEntity, isLite, isEntityPack, toLite, liteKey, FrameMessage, ModelEntity, getToString, isModifiableEntity, EnumEntity } from './Signum.Entities'; import { IUserEntity, TypeEntity, ExceptionEntity } from './Signum.Entities.Basics'; -import { PropertyRoute, PseudoType, Type, getTypeInfo, tryGetTypeInfos, getTypeName, isTypeModel, OperationType, TypeReference, IsByAll, isTypeEntity, tryGetTypeInfo, getTypeInfos, newLite, TypeInfo } from './Reflection'; +import { PropertyRoute, PseudoType, Type, getTypeInfo, tryGetTypeInfos, getTypeName, isTypeModel, OperationType, TypeReference, IsByAll, isTypeEntity, tryGetTypeInfo, getTypeInfos, newLite, TypeInfo, EnumType } from './Reflection'; import { TypeContext } from './TypeContext'; import * as AppContext from './AppContext'; import * as Finder from './Finder'; @@ -24,6 +24,7 @@ import CopyLiteButton from "./Components/CopyLiteButton"; import { Typeahead } from "./Components"; import { TypeaheadOptions } from "./Components/Typeahead"; import CopyLinkButton from "./Components/CopyLinkButton"; +import { object } from "prop-types"; if (!window.__allowNavigatorWithoutUser && (currentUser == null || getToString(currentUser) == "Anonymous")) throw new Error("To improve intial performance, no dependency to any module that depends on Navigator should be taken for anonymous user. Review your dependencies or write var __allowNavigatorWithoutUser = true in Index.cshtml to disable this check."); @@ -884,6 +885,24 @@ export module API { return ajaxGet({ url: `~/api/reflection/typeEntity/${typeName}` }); } + + export function getEnumEntities(type: EnumType): Promise>; + export function getEnumEntities(typeName: string): Promise>; + export function getEnumEntities(type: string | EnumType): Promise> { + + var typeName = typeof type == "string" ? type : type.type; + + return ajaxGet<{ [enumValue: string]: Entity }>({ url: `~/api/reflection/enumEntities/${typeName}` }) + .then(enumToEntity => softCast>({ + enumToEntity: enumToEntity, + idToEnum: Object.entries(enumToEntity).toObject(a => a[1].id!.toString(), a => a[0]) + })); + } +} + +export interface EnumConverter { + enumToEntity: { [enumValue: string]: EnumEntity }; + idToEnum: { [id: string]: T }; }