public static Tuple <HTMLElement, DataGridColumnControllerResult <bool> > ForBool( ITransformationMediator listener, params GrouperDefOrAggregatorDef <bool>[] additionalGrouperOrAggr) { return(Create( listener, new [] { new FilterDef <bool>( I18n.Translate("is true"), (_, x) => x), new FilterDef <bool>( I18n.Translate("is false"), (_, x) => !x) }, new List <AggregatorDef <bool> > { new AggregatorDef <bool>(CountAggregatorLabel, x => I18n.Localize(x.Count())), new AggregatorDef <bool>(I18n.Translate("Count ☑"), x => I18n.Localize(x.Count(y => y))), new AggregatorDef <bool>(I18n.Translate("Count ☐"), x => I18n.Localize(x.Count(y => !y))), }.Concat(additionalGrouperOrAggr.Where(x => x.Aggregator != null).Select(x => x.Aggregator)), new List <GrouperDef <bool> > { new GrouperDef <bool>(GroupEverythingAsOneGroupLabel, RecordGroupingUtil.GroupAllRecordsAsOneGroup), new GrouperDef <bool>(UniqueValueGroupLabel, x => RecordGroupingUtil.GroupRecordsByKey(x, y => y, y => y.KeyData.ToString())) }.Concat(additionalGrouperOrAggr.Where(x => x.Grouper != null).Select(x => x.Grouper)), x => { var val = new InputView(""); val.Widget.Style.Visibility = Visibility.Hidden; //occupies space so filters stay aligned return val; }, default(bool), default(bool), Comparer <bool> .Default )); }
public static Tuple <HTMLElement, DataGridColumnControllerResult <DateTime?> > ForNullableDateTime( DateTimeFormat format, ITransformationMediator listener, params GrouperDefOrAggregatorDef <DateTime?>[] additionalGrouperOrAggr) { var EmptyLabel = I18n.Translate("(empty)"); var groupEverythingAsOneGroupLabel = GroupEverythingAsOneGroupLabel; return(Create( listener, new [] { new FilterDef <DateTime?>( I18n.Translate("equals"), (filterParam, x) => x.HasValue && x == filterParam), new FilterDef <DateTime?>( I18n.Translate("doesn't equal"), (filterParam, x) => x.HasValue && x != filterParam), new FilterDef <DateTime?>( I18n.Translate("is after"), (filterParam, x) => x.HasValue && x > filterParam), new FilterDef <DateTime?>( I18n.Translate("is before"), (filterParam, x) => x.HasValue && x < filterParam) }, new List <AggregatorDef <DateTime?> > { new AggregatorDef <DateTime?>(CountAggregatorLabel, x => I18n.Localize(x.Count())) }.Concat(additionalGrouperOrAggr.Where(x => x.Aggregator != null).Select(x => x.Aggregator)), new List <GrouperDef <DateTime?> > { new GrouperDef <DateTime?>(groupEverythingAsOneGroupLabel, RecordGroupingUtil.GroupAllRecordsAsOneGroup), new GrouperDef <DateTime?>(I18n.Translate("year"), x => RecordGroupingUtil.GroupRecordsByKey(x, y => ObjectUtil.MapNullAs(y, z => z.Year.ToString(), EmptyLabel), y => y.KeyData.ToString())), new GrouperDef <DateTime?>(I18n.Translate("month"), x => RecordGroupingUtil.GroupRecordsByKey(x, y => ObjectUtil.MapNullAs(y, z => I18n.Localize(z, DateTimeFormat.YM), EmptyLabel), y => y.KeyData.ToString())), new GrouperDef <DateTime?>(I18n.Translate("day"), x => RecordGroupingUtil.GroupRecordsByKey(x, y => ObjectUtil.MapNullAs(y, z => I18n.Localize(z, DateTimeFormat.DateOnly), EmptyLabel), y => y.KeyData.ToString())) }.Concat(additionalGrouperOrAggr.Where(x => x.Grouper != null).Select(x => x.Grouper)), x => { var val = new InputView(""); val.PlaceHolder = I18n.Translate("Filter value"); val.BindReadWriteAndInitialize(x, y => !y.HasValue ? "" : I18n.Localize(y.Value, format), y => string.IsNullOrEmpty(y) ? (DateTime?)null : Convert.ToDateTime(y)); return val; }, DateTimeExtensions.SmallestDate(), DateTimeExtensions.SmallestDate(), Comparer <DateTime?> .Default )); }
public static Tuple <HTMLElement, DataGridColumnControllerResult <decimal?> > ForNullableDecimal( DecimalFormat format, ITransformationMediator listener, params GrouperDefOrAggregatorDef <decimal?>[] additionalGrouperOrAggr) { var EmptyLabel = I18n.Translate("(empty)"); return(Create( listener, new [] { new FilterDef <decimal?>( I18n.Translate("equals"), (filterParam, x) => x.HasValue && x == filterParam), new FilterDef <decimal?>( I18n.Translate("doesn't equal"), (filterParam, x) => x.HasValue && x != filterParam), new FilterDef <decimal?>( I18n.Translate("is bigger than"), (filterParam, x) => x.HasValue && x > filterParam), new FilterDef <decimal?>( I18n.Translate("is smaller than"), (filterParam, x) => x.HasValue && x < filterParam) }, new List <AggregatorDef <decimal?> > { new AggregatorDef <decimal?>(SumAggregatorLabel, x => I18n.Localize(x.Sum().GetValueOrDefault(), DecimalFormat.AsNumber)), new AggregatorDef <decimal?>(CountAggregatorLabel, x => I18n.Localize(x.Count())), new AggregatorDef <decimal?>(I18n.Translate("Average"), x => I18n.Localize(x.Average().GetValueOrDefault(), DecimalFormat.AsNumber)), }.Concat(additionalGrouperOrAggr.Where(x => x.Aggregator != null).Select(x => x.Aggregator)), new List <GrouperDef <decimal?> > { new GrouperDef <decimal?>(GroupEverythingAsOneGroupLabel, RecordGroupingUtil.GroupAllRecordsAsOneGroup), new GrouperDef <decimal?>(UniqueValueGroupLabel, x => RecordGroupingUtil.GroupRecordsByKey(x, y => ObjectUtil.MapNullAs(y, (decimal z) => z.ToString(), EmptyLabel), y => y.KeyData.ToString())) }.Concat(additionalGrouperOrAggr.Where(x => x.Grouper != null).Select(x => x.Grouper)), x => { var val = new InputView(""); val.PlaceHolder = I18n.Translate("Filter value"); val.BindReadWriteAndInitialize(x, y => !y.HasValue ? "" : I18n.Localize(y.Value, format), y => string.IsNullOrEmpty(y) ? (decimal?)null : I18n.ParseDecimal(y)); return val; }, default(decimal), default(decimal), Comparer <decimal?> .Default )); }
public static Tuple <HTMLElement, DataGridColumnControllerResult <ModelT> > ForTypeTreatedAsString <ModelT>( Func <ModelT, string> textValueProvider, ITransformationMediator listener, params GrouperDefOrAggregatorDef <string>[] additionalGrouperOrAggr) { return(Create( textValueProvider, listener, new [] { new FilterDef <string>( I18n.Translate("contains"), (filterParam, x) => (x ?? "").ContainsCaseInsensitive(filterParam ?? "")), new FilterDef <string>( I18n.Translate("doesn't contain"), (filterParam, x) => !(x ?? "").ContainsCaseInsensitive(filterParam ?? "")), new FilterDef <string>( I18n.Translate("equals"), (filterParam, x) => (x ?? "").EqualsCaseInsensitive(filterParam ?? "")), new FilterDef <string>( I18n.Translate("doesn't equal"), (filterParam, x) => !(x ?? "").EqualsCaseInsensitive(filterParam ?? "")), new FilterDef <string>( I18n.Translate("begins with"), (filterParam, x) => (x ?? "").StartsWithCaseInsensitive(filterParam ?? "")), new FilterDef <string>( I18n.Translate("ends with"), (filterParam, x) => (x ?? "").EndsWithCaseInsensitive(filterParam ?? "")) }, new List <AggregatorDef <string> > { new AggregatorDef <string>(CountAggregatorLabel, x => I18n.Localize(x.Count())) }.Concat(additionalGrouperOrAggr.Where(x => x.Aggregator != null).Select(x => x.Aggregator)), new List <GrouperDef <string> > { new GrouperDef <string>(GroupEverythingAsOneGroupLabel, RecordGroupingUtil.GroupAllRecordsAsOneGroup), new GrouperDef <string>(UniqueValueGroupLabel, x => RecordGroupingUtil.GroupRecordsByKey(x, y => y, y => y.KeyData.ToString())) }.Concat(additionalGrouperOrAggr.Where(x => x.Grouper != null).Select(x => x.Grouper)), x => { var val = new InputView(""); val.PlaceHolder = I18n.Translate("Filter value"); val.BindReadWriteAndInitialize(x, y => y, y => y); return val; }, default(string), default(string), new StringCompareBasedComparer() )); }
public static Tuple <HTMLElement, DataGridColumnControllerResult <decimal> > ForDecimal( DecimalFormat format, ITransformationMediator listener, params GrouperDefOrAggregatorDef <decimal>[] additionalGrouperOrAggr) { return(Create( listener, new [] { new FilterDef <decimal>( I18n.Translate("equals"), (filterParam, x) => x == filterParam), new FilterDef <decimal>( I18n.Translate("doesn't equal"), (filterParam, x) => x != filterParam), new FilterDef <decimal>( I18n.Translate("is bigger than"), (filterParam, x) => x > filterParam), new FilterDef <decimal>( I18n.Translate("is smaller than"), (filterParam, x) => x < filterParam) }, new List <AggregatorDef <decimal> > { new AggregatorDef <decimal>(SumAggregatorLabel, x => I18n.Localize(x.Sum(), DecimalFormat.AsNumber)), new AggregatorDef <decimal>(CountAggregatorLabel, x => I18n.Localize(x.Count())), new AggregatorDef <decimal>(I18n.Translate("Average"), x => I18n.Localize(x.Average(), DecimalFormat.AsNumber)), }.Concat(additionalGrouperOrAggr.Where(x => x.Aggregator != null).Select(x => x.Aggregator)), new List <GrouperDef <decimal> > { new GrouperDef <decimal>(GroupEverythingAsOneGroupLabel, RecordGroupingUtil.GroupAllRecordsAsOneGroup), new GrouperDef <decimal>(UniqueValueGroupLabel, x => RecordGroupingUtil.GroupRecordsByKey(x, y => y, y => y.KeyData.ToString())) }.Concat(additionalGrouperOrAggr.Where(x => x.Grouper != null).Select(x => x.Grouper)), x => { var val = new InputView("", InputView.TypeText); val.PlaceHolder = I18n.Translate("Filter value"); val.BindReadWriteAndInitialize(x, y => I18n.Localize(y, format), y => Convert.ToDecimal(y)); return val; }, default(decimal), default(decimal), Comparer <decimal> .Default )); }
private static Tuple <HTMLElement, DataGridColumnControllerResult <OperT> > Create <OperT, ViewT>( ITransformationMediator listener, FilterDef <OperT>[] availableFilters, IEnumerable <AggregatorDef <OperT> > availableAggregators, IEnumerable <GrouperDef <OperT> > availableGroupers, Func <IReadWriteValue <OperT>, IReadWriteValueView <HTMLElement, ViewT> > paramEditor, OperT initialFilterValue, OperT invalidFilterValue, IComparer <OperT> sortingImpl) { return(Create <OperT, OperT, ViewT>( x => x, listener, availableFilters, availableAggregators, availableGroupers, paramEditor, initialFilterValue, invalidFilterValue, sortingImpl)); }
public static Tuple <HTMLElement, DataGridColumnControllerResult <DecimalWithPrecision> > ForDecimalWithPrecision( ITransformationMediator listener, params GrouperDefOrAggregatorDef <DecimalWithPrecision>[] additionalGrouperOrAggr) { return(Create( listener, new [] { new FilterDef <DecimalWithPrecision>( I18n.Translate("is empty"), (filterParam, x) => DecimalWithPrecision.ComparatorImpl(x, null) == 0), new FilterDef <DecimalWithPrecision>( I18n.Translate("is not empty"), (filterParam, x) => DecimalWithPrecision.ComparatorImpl(x, null) != 0), new FilterDef <DecimalWithPrecision>( I18n.Translate("equals"), (filterParam, x) => DecimalWithPrecision.ComparatorImpl(x, filterParam) == 0), new FilterDef <DecimalWithPrecision>( I18n.Translate("doesn't equal"), (filterParam, x) => DecimalWithPrecision.ComparatorImpl(x, filterParam) != 0), new FilterDef <DecimalWithPrecision>( I18n.Translate("is bigger than"), (filterParam, x) => DecimalWithPrecision.ComparatorImpl(x, filterParam) > 0), new FilterDef <DecimalWithPrecision>( I18n.Translate("is smaller than"), (filterParam, x) => DecimalWithPrecision.ComparatorImpl(x, filterParam) < 0) }, new List <AggregatorDef <DecimalWithPrecision> > { new AggregatorDef <DecimalWithPrecision>(SumAggregatorLabel, x => { var lst = x.Where(y => y != null).ToList(); var p = lst.Any() ? lst[0].Precision : 0; return I18n.Localize( lst.Aggregate(0m, (y, z) => y + z.RoundedValue), DecimalFormatExtensions.GetWithPrecision(p)); }), new AggregatorDef <DecimalWithPrecision>(CountAggregatorLabel, x => I18n.Localize(x.Count())), new AggregatorDef <DecimalWithPrecision>(I18n.Translate("Average"), x => { var lst = x.Where(y => y != null).ToList(); var p = lst.Any() ? lst[0].Precision : 0; return I18n.Localize( lst.Aggregate(0m, (y, z) => y + z.RoundedValue) / lst.Count <= 0 ? 1 : lst.Count, DecimalFormatExtensions.GetWithPrecision(p)); }), }.Concat(additionalGrouperOrAggr.Where(x => x.Aggregator != null).Select(x => x.Aggregator)), new List <GrouperDef <DecimalWithPrecision> > { new GrouperDef <DecimalWithPrecision>(GroupEverythingAsOneGroupLabel, RecordGroupingUtil.GroupAllRecordsAsOneGroup), new GrouperDef <DecimalWithPrecision>(UniqueValueGroupLabel, x => RecordGroupingUtil.GroupRecordsByKey(x, y => y, y => y.KeyData.ToString())) }.Concat(additionalGrouperOrAggr.Where(x => x.Grouper != null).Select(x => x.Grouper)), x => { var val = new InputView("", InputView.TypeText); val.PlaceHolder = I18n.Translate("Filter value"); val.BindReadWriteAndInitialize(x, y => y == null ? "" : I18n.Localize( y.Value, DecimalFormatExtensions.GetWithPrecision(y.Precision)), y => { if (string.IsNullOrWhiteSpace(y)) { return null; } var v = I18n.ParseDecimal(y); var s = I18n.Localize(v, DecimalFormat.WithFiveDecPlaces); var p = s.Length - s.IndexOf('.') - 1; return new DecimalWithPrecision(v, p); }); return val; }, null, null, new DecimalWithPrecisionDefaultComparer() )); }
//TODO refactor this monster private static Tuple <HTMLElement, DataGridColumnControllerResult <InternalT> > Create <InternalT, OperT, ViewT>( Func <InternalT, OperT> toOper, ITransformationMediator listener, FilterDef <OperT>[] availableFilters, IEnumerable <AggregatorDef <OperT> > rawAvailableAggregators, IEnumerable <GrouperDef <OperT> > rawAvailableGroupers, Func <IReadWriteValue <OperT>, IReadWriteValueView <HTMLElement, ViewT> > paramEditor, OperT initialFilterValue, OperT invalidFilterValue, IComparer <OperT> sortingImpl) { var availableAggregators = rawAvailableAggregators.ToList(); var availableGroupers = rawAvailableGroupers.ToList(); AssureUnique(availableFilters, x => x.Label); AssureUnique(availableAggregators, x => x.Label); AssureUnique(availableGroupers, x => x.Label); LocalValue <AggregatorDef <OperT> > aggregateFunc = null; LocalValue <GrouperDef <OperT> > groupingFunc = null; LocalValue <GroupOrAggregate?> groupOrAggregateChoice = null; FilterDef <OperT> currentFilterImplOrNull = null; var filterLabelToImpl = new Dictionary <string, FilterDef <OperT> >(); availableFilters.ForEach(x => filterLabelToImpl.Add(x.Label, x)); AggregatorDef <OperT> currentAggrImplOrNull = null; var aggregLabelToImpl = new Dictionary <string, AggregatorDef <OperT> >(); availableAggregators.ForEach(x => aggregLabelToImpl.Add(x.Label, x)); GrouperDef <OperT> currentGrouperImplOrNull = null; var grouperLabelToImpl = new Dictionary <string, GrouperDef <OperT> >(); availableGroupers.ForEach(x => grouperLabelToImpl.Add(x.Label, x)); var filterParam = new LocalValue <OperT>(initialFilterValue, invalidFilterValue); HTMLElement controllerElem = new HTMLSpanElement { ClassName = Magics.CssClassFilterMainContainer }; var actionContainer = new HTMLSpanElement { ClassName = Magics.CssClassFilterActionContainer }; controllerElem.AppendChild(actionContainer); var groupOrAggregateChoiceView = new RadioBasedSingleChoice(); Element GetLabelTh() { var filterTh = controllerElem.ParentElement; var filterTr = filterTh.ParentElement; var thead = filterTr.ParentElement; var iCol = filterTr.IndexOfChild(filterTh); var labelTr = thead.GetChildAtOrNull(0); var labelTh = labelTr.GetChildAtOrNull(iCol); return(labelTh); } void MarkAsGrouped(bool activated) { Logger.Debug(typeof(DataGridColumnController), "markAsGrouped({0})", activated); controllerElem.ParentElement.AddOrRemoveClass(activated, Magics.CssClassActive); GetLabelTh().AddOrRemoveClass(activated, Magics.CssClassWithGrouping); controllerElem.ParentElement.AddOrRemoveClass(activated, Magics.CssClassWithGrouping); } void MarkAsAggregated(bool activated) { Logger.Debug(typeof(DataGridColumnController), "markAsAggregated({0})", activated); controllerElem.ParentElement.AddOrRemoveClass(activated, Magics.CssClassActive); GetLabelTh().AddOrRemoveClass(activated, Magics.CssClassWithAggregation); controllerElem.ParentElement.AddOrRemoveClass(activated, Magics.CssClassWithAggregation); } void MarkAsFiltered(bool activated) { Logger.Debug(typeof(DataGridColumnController), "markAsFiltered({0})", activated); controllerElem.ParentElement.AddOrRemoveClass(activated, Magics.CssClassActive); GetLabelTh().AddOrRemoveClass(activated, Magics.CssClassWithFilter); controllerElem.ParentElement.AddOrRemoveClass(activated, Magics.CssClassWithFilter); } var removeFilter = new HTMLAnchorElement { Target = "#", Title = I18n.Translate("Remove filter"), Text = FontAwesomeSolid.IconFilter }; removeFilter.AddClasses(Magics.CssClassFilterRemove, IconFontType.FontAwesomeSolid.ToCssClassName()); actionContainer.AppendChild(removeFilter); var removeFilterActionView = new InputTypeButtonActionView(removeFilter); LocalActionBuilder.Build(removeFilterActionView, () => { currentFilterImplOrNull = null; MarkAsFiltered(false); listener.UserFilterChangedHandler(ChangeOrRemove.Removed); }); var removeGrouping = new HTMLAnchorElement { Target = "#", Title = I18n.Translate("Remove grouping"), Text = FontAwesomeSolid.IconListUl }; removeGrouping.AddClasses(Magics.CssClassGroupingRemove, IconFontType.FontAwesomeSolid.ToCssClassName()); actionContainer.AppendChild(removeGrouping); var removeGroupingActionView = new InputTypeButtonActionView(removeGrouping); LocalActionBuilder.Build(removeGroupingActionView, async() => { currentGrouperImplOrNull = null; MarkAsGrouped(false); await groupOrAggregateChoice.DoChange(null, false, null, false); listener.UserGroupingChangedHandler(ChangeOrRemove.Removed); }); var removeAggregation = new HTMLAnchorElement { ClassName = Magics.CssClassAggregationRemove, Target = "#", Title = I18n.Translate("Remove aggregation"), Text = "Σ" }; actionContainer.AppendChild(removeAggregation); var removeAggregationActionView = new InputTypeButtonActionView(removeAggregation); LocalActionBuilder.Build(removeAggregationActionView, async() => { currentAggrImplOrNull = null; await groupOrAggregateChoice.DoChange(null, false, null, false); MarkAsAggregated(false); listener.UserAggregationChangedHandler(ChangeOrRemove.Removed); }); Action <string> groupingChangedHandler = async labelOrNull => { Logger.Debug(typeof(DataGridColumnController), "Setting grouping programmatically to {0}", labelOrNull); if (labelOrNull == null) { currentGrouperImplOrNull = null; MarkAsGrouped(false); await groupOrAggregateChoice.DoChange(null, false, null, false); return; } GrouperDef <OperT> grouper; if (!grouperLabelToImpl.TryGetValue(labelOrNull, out grouper)) { Logger.Debug(typeof(DataGridColumnController), "No such grouping func when looking by label"); return; } await groupingFunc.DoChange(grouper, false, null, false); currentGrouperImplOrNull = grouper; MarkAsGrouped(true); }; Action <string> aggregationChangedHandler = async labelOrNull => { Logger.Debug(typeof(DataGridColumnController), "Setting aggregation programmatically to {0}", labelOrNull); if (labelOrNull == null) { currentAggrImplOrNull = null; MarkAsAggregated(false); await groupOrAggregateChoice.DoChange(null, false, null, false); listener.UserAggregationChangedHandler(ChangeOrRemove.Removed); return; } if (!aggregLabelToImpl.TryGetValue(labelOrNull, out var aggr)) { Logger.Debug(typeof(DataGridColumnController), "No such aggregation func when looking by label"); return; } await aggregateFunc.DoChange(aggr, false, null, false); currentAggrImplOrNull = aggr; MarkAsAggregated(true); }; listener.InitUserSide(groupingChangedHandler, aggregationChangedHandler); var filterOper = new LocalValue <FilterDef <OperT> >(availableFilters.FirstOrDefault()); var filterOperView = new DropDownSelectBox("") { PermittedValues = filterLabelToImpl.Select(x => Tuple.Create(x.Key, x.Key)) }; filterOperView.BindReadWriteAndInitialize(filterOper, x => x != null ? Tuple.Create(x.Label, x.Label) : Tuple.Create("", ""), x => !string.IsNullOrEmpty(x.Item1) ? filterLabelToImpl[x.Item1] : null); controllerElem.AppendChild(filterOperView.Widget); var val = paramEditor(filterParam); controllerElem.AppendChild(val.Widget); filterParam.Changed += (_, __, newValue, ___, isUserChange) => { if (!isUserChange) { return; } currentFilterImplOrNull = filterOper.Value; MarkAsFiltered(true); listener.UserFilterChangedHandler(ChangeOrRemove.Changed); }; filterOper.Changed += (_, __, newValue, ___, isUserChange) => { if (!isUserChange) { return; } currentFilterImplOrNull = newValue; MarkAsFiltered(true); listener.UserFilterChangedHandler(ChangeOrRemove.Changed); }; { groupOrAggregateChoice = new LocalValue <GroupOrAggregate?>(null); groupOrAggregateChoiceView.Widget.ClassList.Add(Magics.CssClassGroupOrAggregate); groupOrAggregateChoiceView.PermittedValues = EnumExtensions.GetEnumValues <GroupOrAggregate>().Select(x => Tuple.Create(((int)x).ToString(), x.GetUserFriendlyName())); groupOrAggregateChoiceView.BindReadWriteAndInitialize(groupOrAggregateChoice, x => !x.HasValue ? null : Tuple.Create(((int)x).ToString(), x.Value.GetUserFriendlyName()), x => (x == null || string.IsNullOrEmpty(x.Item1)) ? null : (GroupOrAggregate?)Convert.ToInt32(x.Item1)); groupingFunc = new LocalValue <GrouperDef <OperT> >(availableGroupers.FirstOrDefault()); var groupingFuncView = new DropDownSelectBox(I18n.Translate("group by:")); groupingFuncView.Widget.ClassList.Add(Magics.CssClassGroupingFunc); groupingFuncView.PermittedValues = grouperLabelToImpl.Select(x => Tuple.Create(x.Key, x.Key)); groupingFuncView.BindReadWriteAndInitialize(groupingFunc, x => x != null ? Tuple.Create(x.Label, x.Label) : Tuple.Create("", ""), x => !string.IsNullOrEmpty(x.Item1) ? grouperLabelToImpl[x.Item1] : null); aggregateFunc = new LocalValue <AggregatorDef <OperT> >(availableAggregators.FirstOrDefault()); var aggregateFuncView = new DropDownSelectBox(I18n.Translate("aggregate by:")); aggregateFuncView.Widget.ClassList.Add(Magics.CssClassAggregateFunc); aggregateFuncView.PermittedValues = aggregLabelToImpl.Select(x => Tuple.Create(x.Key, x.Key)); aggregateFuncView.BindReadWriteAndInitialize(aggregateFunc, x => x != null ? Tuple.Create(x.Label, x.Label) : Tuple.Create("", ""), x => !string.IsNullOrEmpty(x.Item1) ? aggregLabelToImpl[x.Item1] : null); groupingFunc.Changed += (_, __, newValue, errors, isUserChange) => { if (!isUserChange) { return; } currentGrouperImplOrNull = newValue; MarkAsGrouped(newValue != null); listener.UserGroupingChangedHandler(newValue != null ? ChangeOrRemove.Changed : ChangeOrRemove.Removed); }; aggregateFunc.Changed += (_, __, newValue, errors, isUserChange) => { if (!isUserChange) { return; } currentAggrImplOrNull = newValue; MarkAsAggregated(newValue != null); listener.UserAggregationChangedHandler(newValue != null ? ChangeOrRemove.Changed : ChangeOrRemove.Removed); }; groupOrAggregateChoice.Changed += (_, __, newValue, ___, isUserChange) => { Logger.Debug(typeof(DataGridColumnController), "groupOrAggregateChoice changed to {0} by {1}", newValue, isUserChange); //if (!isUserChange) { // return; //} if (!newValue.HasValue) { return; } switch (newValue.Value) { case GroupOrAggregate.Aggregate: currentAggrImplOrNull = aggregateFunc.Value; MarkAsAggregated(true); listener.UserAggregationChangedHandler(ChangeOrRemove.Changed); break; case GroupOrAggregate.Group: currentGrouperImplOrNull = groupingFunc.Value; MarkAsGrouped(true); listener.UserGroupingChangedHandler(ChangeOrRemove.Changed); break; default: throw new Exception("unsupported GroupOrAggregate"); } }; controllerElem.AppendChild(groupOrAggregateChoiceView.Widget); controllerElem.AppendChild(groupingFuncView.Widget); controllerElem.AppendChild(aggregateFuncView.Widget); } DocumentUtil.AddMouseClickListener(controllerElem, ev => { if (!ev.HasHtmlTarget()) { return; } //find out if clicked item is a descendant of th if (ev.HtmlTarget().IsDescendantOf(controllerElem.ParentElement)) { controllerElem.ParentElement.ClassList.Add(Magics.CssClassActive); return; } controllerElem.ParentElement?.ClassList.Remove(Magics.CssClassActive); }); return(Tuple.Create(controllerElem, new DataGridColumnControllerResult <InternalT> { FilterImpl = x => currentFilterImplOrNull == null || currentFilterImplOrNull.FilterFunc(filterParam.Value, toOper(x)), AggregationImpl = x => currentAggrImplOrNull?.AggregatorFunc(x.Select(toOper)), GroupingImpl = x => currentGrouperImplOrNull?.GroupingFunc(x.Select(toOper)), SortingImpl = new CompareImpl <InternalT>((x, y) => sortingImpl.Compare(toOper(x), toOper(y))), IsGroupingActive = () => currentGrouperImplOrNull != null })); }