| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | import { VariableUiElement } from "../Base/VariableUIElement" | 
					
						
							|  |  |  | import { Store, UIEventSource } from "../../Logic/UIEventSource" | 
					
						
							|  |  |  | import Table from "../Base/Table" | 
					
						
							|  |  |  | import Combine from "../Base/Combine" | 
					
						
							|  |  |  | import { FixedUiElement } from "../Base/FixedUiElement" | 
					
						
							|  |  |  | import { Utils } from "../../Utils" | 
					
						
							|  |  |  | import BaseUIElement from "../BaseUIElement" | 
					
						
							|  |  |  | import Svg from "../../Svg" | 
					
						
							| 
									
										
										
										
											2021-06-21 03:12:43 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | export default class Histogram<T> extends VariableUiElement { | 
					
						
							|  |  |  |     private static defaultPalette = [ | 
					
						
							|  |  |  |         "#ff5858", | 
					
						
							|  |  |  |         "#ffad48", | 
					
						
							|  |  |  |         "#ffff59", | 
					
						
							|  |  |  |         "#56bd56", | 
					
						
							|  |  |  |         "#63a9ff", | 
					
						
							| 
									
										
										
										
											2022-01-16 02:00:55 +01:00
										 |  |  |         "#9d62d9", | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         "#fa61fa", | 
					
						
							| 
									
										
										
										
											2021-06-21 03:12:43 +02:00
										 |  |  |     ] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     constructor( | 
					
						
							|  |  |  |         values: Store<string[]>, | 
					
						
							|  |  |  |         title: string | BaseUIElement, | 
					
						
							|  |  |  |         countTitle: string | BaseUIElement, | 
					
						
							|  |  |  |         options?: { | 
					
						
							|  |  |  |             assignColor?: (t0: string) => string | 
					
						
							|  |  |  |             sortMode?: "name" | "name-rev" | "count" | "count-rev" | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-06-21 03:12:43 +02:00
										 |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         const sortMode = new UIEventSource<"name" | "name-rev" | "count" | "count-rev">( | 
					
						
							|  |  |  |             options?.sortMode ?? "name" | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         const sortName = new VariableUiElement( | 
					
						
							|  |  |  |             sortMode.map((m) => { | 
					
						
							|  |  |  |                 switch (m) { | 
					
						
							|  |  |  |                     case "name": | 
					
						
							|  |  |  |                         return Svg.up_svg() | 
					
						
							|  |  |  |                     case "name-rev": | 
					
						
							|  |  |  |                         return Svg.up_svg().SetStyle("transform: rotate(180deg)") | 
					
						
							|  |  |  |                     default: | 
					
						
							|  |  |  |                         return Svg.circle_svg() | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         const titleHeader = new Combine([sortName.SetClass("w-4 mr-2"), title]) | 
					
						
							|  |  |  |             .SetClass("flex") | 
					
						
							| 
									
										
										
										
											2022-01-16 02:00:55 +01:00
										 |  |  |             .onClick(() => { | 
					
						
							|  |  |  |                 if (sortMode.data === "name") { | 
					
						
							|  |  |  |                     sortMode.setData("name-rev") | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     sortMode.setData("name") | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         const sortCount = new VariableUiElement( | 
					
						
							|  |  |  |             sortMode.map((m) => { | 
					
						
							|  |  |  |                 switch (m) { | 
					
						
							|  |  |  |                     case "count": | 
					
						
							|  |  |  |                         return Svg.up_svg() | 
					
						
							|  |  |  |                     case "count-rev": | 
					
						
							|  |  |  |                         return Svg.up_svg().SetStyle("transform: rotate(180deg)") | 
					
						
							|  |  |  |                     default: | 
					
						
							|  |  |  |                         return Svg.circle_svg() | 
					
						
							| 
									
										
										
										
											2021-06-21 03:12:43 +02:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             }) | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-06-21 03:12:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         const countHeader = new Combine([sortCount.SetClass("w-4 mr-2"), countTitle]) | 
					
						
							|  |  |  |             .SetClass("flex") | 
					
						
							|  |  |  |             .onClick(() => { | 
					
						
							|  |  |  |                 if (sortMode.data === "count-rev") { | 
					
						
							|  |  |  |                     sortMode.setData("count") | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     sortMode.setData("count-rev") | 
					
						
							| 
									
										
										
										
											2022-03-29 00:20:10 +02:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const header = [titleHeader, countHeader] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         super( | 
					
						
							|  |  |  |             values.map( | 
					
						
							|  |  |  |                 (values) => { | 
					
						
							|  |  |  |                     if (values === undefined) { | 
					
						
							|  |  |  |                         return undefined | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     values = Utils.NoNull(values) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     const counts = new Map<string, number>() | 
					
						
							|  |  |  |                     for (const value of values) { | 
					
						
							|  |  |  |                         const c = counts.get(value) ?? 0 | 
					
						
							|  |  |  |                         counts.set(value, c + 1) | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     const keys = Array.from(counts.keys()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     switch (sortMode.data) { | 
					
						
							|  |  |  |                         case "name": | 
					
						
							|  |  |  |                             keys.sort() | 
					
						
							|  |  |  |                             break | 
					
						
							|  |  |  |                         case "name-rev": | 
					
						
							|  |  |  |                             keys.sort().reverse(/*Copy of array, inplace reverse if fine*/) | 
					
						
							|  |  |  |                             break | 
					
						
							|  |  |  |                         case "count": | 
					
						
							|  |  |  |                             keys.sort((k0, k1) => counts.get(k0) - counts.get(k1)) | 
					
						
							|  |  |  |                             break | 
					
						
							|  |  |  |                         case "count-rev": | 
					
						
							|  |  |  |                             keys.sort((k0, k1) => counts.get(k1) - counts.get(k0)) | 
					
						
							|  |  |  |                             break | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     const max = Math.max(...Array.from(counts.values())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     const fallbackColor = (keyValue: string) => { | 
					
						
							|  |  |  |                         const index = keys.indexOf(keyValue) | 
					
						
							|  |  |  |                         return Histogram.defaultPalette[index % Histogram.defaultPalette.length] | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     let actualAssignColor = undefined | 
					
						
							|  |  |  |                     if (options?.assignColor === undefined) { | 
					
						
							|  |  |  |                         actualAssignColor = fallbackColor | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         actualAssignColor = (keyValue: string) => { | 
					
						
							|  |  |  |                             return options.assignColor(keyValue) ?? fallbackColor(keyValue) | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     return new Table( | 
					
						
							|  |  |  |                         header, | 
					
						
							|  |  |  |                         keys.map((key) => [ | 
					
						
							|  |  |  |                             key, | 
					
						
							|  |  |  |                             new Combine([ | 
					
						
							|  |  |  |                                 new Combine([ | 
					
						
							|  |  |  |                                     new FixedUiElement("" + counts.get(key)).SetClass( | 
					
						
							|  |  |  |                                         "font-bold rounded-full block" | 
					
						
							|  |  |  |                                     ), | 
					
						
							|  |  |  |                                 ]) | 
					
						
							|  |  |  |                                     .SetClass("flex justify-center rounded border border-black") | 
					
						
							|  |  |  |                                     .SetStyle( | 
					
						
							|  |  |  |                                         `background: ${actualAssignColor(key)}; width: ${ | 
					
						
							|  |  |  |                                             (100 * counts.get(key)) / max | 
					
						
							|  |  |  |                                         }%`
 | 
					
						
							|  |  |  |                                     ), | 
					
						
							|  |  |  |                             ]).SetClass("block w-full"), | 
					
						
							|  |  |  |                         ]), | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             contentStyle: keys.map((_) => ["width: 20%"]), | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     ).SetClass("w-full zebra-table") | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |                 [sortMode] | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-06-21 03:12:43 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | } |