/// <summary> /// Creates a duration control. /// </summary> /// <param name="value"></param> /// <param name="allowEmpty"></param> /// <param name="setup">The setup object for the duration control.</param> /// <param name="validationMethod">The validation method. Pass null if you’re only using this control for page modification.</param> public DurationControl(TimeSpan?value, bool allowEmpty, DurationControlSetup setup = null, Action <TimeSpan?, Validator> validationMethod = null) { setup = setup ?? DurationControlSetup.Create(); var textControl = new TextControl( value.HasValue ? Math.Floor(value.Value.TotalHours).ToString("0000") + ":" + value.Value.Minutes.ToString("00") : "", allowEmpty, setup: setup.IsReadOnly ? TextControlSetup.CreateReadOnly(validationPredicate: setup.ValidationPredicate, validationErrorNotifier: setup.ValidationErrorNotifier) : TextControlSetup.Create( placeholder: "h:m", action: new SpecifiedValue <FormAction>(setup.Action), valueChangedAction: setup.ValueChangedAction, pageModificationValue: setup.PageModificationValue, validationPredicate: setup.ValidationPredicate, validationErrorNotifier: setup.ValidationErrorNotifier), validationMethod: validationMethod == null ? (Action <string, Validator>)null : (postBackValue, validator) => { if (tooLongOrInvalidCharacters(postBackValue)) { validator.NoteErrorAndAddMessage("Please enter a valid duration."); setup.ValidationErrorNotifier?.Invoke(); return; } var errorHandler = new ValidationErrorHandler("duration"); var validatedValue = validator.GetNullableTimeSpan(errorHandler, parseTimeSpan(postBackValue), allowEmpty); if (errorHandler.LastResult != ErrorCondition.NoError) { setup.ValidationErrorNotifier?.Invoke(); return; } validationMethod(validatedValue, validator); }); Labeler = textControl.Labeler; PageComponent = new CustomPhrasingComponent( new DisplayableElement( context => new DisplayableElementData( setup.DisplaySetup, () => new DisplayableElementLocalData( "span", focusDependentData: new DisplayableElementFocusDependentData( includeIdAttribute: true, jsInitStatements: "{0}.blur( function() {{ {1} }} ).keypress( function( e ) {{ {2} }} ).focus( function() {{ {3} }} ).mouseup( function( e ) {{ {4} }} );" .FormatWith( getTextControlExpression(context.Id), "ApplyTimeSpanFormat( this );", "if( !NumericalOnly( e, this ) ) e.preventDefault();", "this.value = this.value.replace( ':', '' ); this.select();", "e.preventDefault();"))), classes: elementClass.Add(setup.Classes ?? ElementClassSet.Empty), children: textControl.PageComponent.ToCollection())).ToCollection()); Validation = textControl.Validation; }
/// <summary> /// Creates an HTML block container. Do not pass null for HTML. This overload is useful when you've already loaded the HTML. /// </summary> /// <param name="html"></param> /// <param name="displaySetup"></param> /// <param name="classes">The classes on the container.</param> public HtmlBlockContainer(string html, DisplaySetup displaySetup = null, ElementClassSet classes = null) { this.html = html; children = new DisplayableElement( context => new DisplayableElementData( displaySetup, () => new DisplayableElementLocalData("div"), classes: elementClass.Add(classes ?? ElementClassSet.Empty), children: new TrustedHtmlString(html).ToComponent().ToCollection())).ToCollection(); }
/// <summary> /// Creates a list error-display style. /// </summary> /// <param name="classes">The classes on the list container.</param> public ListErrorDisplayStyle(ElementClassSet classes = null) { componentGetter = (errorSources, errors, componentsFocusableOnError) => { if (!errors.Any()) { return(Enumerable.Empty <FlowComponent>().Materialize()); } return(new DisplayableElement( context => new DisplayableElementData( null, () => GetErrorFocusableElementLocalData(context, "div", componentsFocusableOnError ? errorSources : null, null), classes: containerClass.Add(classes ?? ElementClassSet.Empty), children: new StackList( from i in errors select new FontAwesomeIcon("fa-times-circle", "fa-lg").ToCollection <PhrasingComponent>() .Concat(" ".ToComponents()) .Append(i.ToComponent()) .Materialize() .ToComponentListItem()).ToCollection())).ToCollection()); }; }
private PhrasingComponent getComponent( FormValue formValue, ElementId id, string radioButtonListItemId, DisplaySetup displaySetup, bool isReadOnly, ElementClassSet classes, PageModificationValue <bool> pageModificationValue, IReadOnlyCollection <PhrasingComponent> label, FormAction action, FormAction valueChangedAction, Func <string> jsClickStatementGetter) { return(new CustomPhrasingComponent( new DisplayableElement( labelContext => new DisplayableElementData( displaySetup, () => new DisplayableElementLocalData("label"), classes: elementClass.Add(classes ?? ElementClassSet.Empty), children: new DisplayableElement( context => { if (!isReadOnly) { action?.AddToPageIfNecessary(); valueChangedAction?.AddToPageIfNecessary(); } return new DisplayableElementData( null, () => { var attributes = new List <ElementAttribute>(); var radioButtonFormValue = formValue as FormValue <ElementId>; attributes.Add(new ElementAttribute("type", radioButtonFormValue != null ? "radio" : "checkbox")); if (radioButtonFormValue != null || !isReadOnly) { attributes.Add( new ElementAttribute( "name", radioButtonFormValue != null ? ((FormValue)radioButtonFormValue).GetPostBackValueKey() : context.Id)); } if (radioButtonFormValue != null) { attributes.Add(new ElementAttribute("value", radioButtonListItemId ?? context.Id)); } if (pageModificationValue.Value) { attributes.Add(new ElementAttribute("checked")); } if (isReadOnly) { attributes.Add(new ElementAttribute("disabled")); } var jsInitStatements = StringTools.ConcatenateWithDelimiter( " ", !isReadOnly ? SubmitButton.GetImplicitSubmissionKeyPressStatements(action, false) .Surround("$( '#{0}' ).keypress( function( e ) {{ ".FormatWith(context.Id), " } );") : "", jsClickStatementGetter().Surround("$( '#{0}' ).click( function() {{ ".FormatWith(context.Id), " } );")); return new DisplayableElementLocalData( "input", new FocusabilityCondition(!isReadOnly), isFocused => { if (isFocused) { attributes.Add(new ElementAttribute("autofocus")); } return new DisplayableElementFocusDependentData( attributes: attributes, includeIdAttribute: true, jsInitStatements: jsInitStatements); }); }, classes: elementClass, clientSideIdReferences: id.ToCollection()); }, formValue: formValue).ToCollection() .Concat(label.Any() ? new GenericPhrasingContainer(label, classes: elementClass).ToCollection() : Enumerable.Empty <FlowComponent>()) .Materialize())).ToCollection())); }
/// <summary> /// Creates a date-and-time control. /// </summary> /// <param name="value"></param> /// <param name="allowEmpty"></param> /// <param name="setup">The setup object for the date-and-time control.</param> /// <param name="minValue">The earliest acceptable date.</param> /// <param name="maxValue">The latest acceptable date.</param> /// <param name="validationMethod">The validation method. Pass null if you’re only using this control for page modification.</param> public DateAndTimeControl( LocalDateTime?value, bool allowEmpty, DateAndTimeControlSetup setup = null, LocalDate?minValue = null, LocalDate?maxValue = null, Action <LocalDateTime?, Validator> validationMethod = null) { setup = setup ?? DateAndTimeControlSetup.Create(); var textControl = new TextControl( value.HasValue ? value.Value.ToDateTimeUnspecified().ToMonthDayYearString() + " " + value.Value.ToDateTimeUnspecified().ToHourAndMinuteString() : "", allowEmpty, setup: setup.IsReadOnly ? TextControlSetup.CreateReadOnly(validationPredicate: setup.ValidationPredicate, validationErrorNotifier: setup.ValidationErrorNotifier) : TextControlSetup.Create( autoFillTokens: setup.AutoFillTokens, action: new SpecifiedValue <FormAction>(setup.Action), valueChangedAction: setup.ValueChangedAction, pageModificationValue: setup.PageModificationValue, validationPredicate: setup.ValidationPredicate, validationErrorNotifier: setup.ValidationErrorNotifier), validationMethod: validationMethod == null ? (Action <string, Validator>)null : (postBackValue, validator) => { var errorHandler = new ValidationErrorHandler("date and time"); var validatedValue = validator.GetNullableDateTime( errorHandler, postBackValue.ToUpper(), TewlContrib.DateTimeTools.MonthDayYearFormats.Select(i => i + " " + TewlContrib.DateTimeTools.HourAndMinuteFormat).ToArray(), allowEmpty, minValue?.ToDateTimeUnspecified() ?? DateTime.MinValue, maxValue?.PlusDays(1).ToDateTimeUnspecified() ?? DateTime.MaxValue); if (errorHandler.LastResult != ErrorCondition.NoError) { setup.ValidationErrorNotifier?.Invoke(); return; } validationMethod( validatedValue.HasValue ? (LocalDateTime?)LocalDateTime.FromDateTime(validatedValue.Value) : null, validator); }); Labeler = textControl.Labeler; PageComponent = new CustomPhrasingComponent( new DisplayableElement( context => new DisplayableElementData( setup.DisplaySetup, () => new DisplayableElementLocalData( "span", focusDependentData: new DisplayableElementFocusDependentData( includeIdAttribute: true, jsInitStatements: "{0}.datetimepicker( {{ {1} }} );".FormatWith( getTextControlExpression(context.Id), StringTools.ConcatenateWithDelimiter( ", ", minValue.HasValue ? "minDate: {0}".FormatWith("new Date( {0}, {1} - 1, {2} )".FormatWith(minValue.Value.Year, minValue.Value.Month, minValue.Value.Day)) : "", maxValue.HasValue ? "maxDate: {0}".FormatWith("new Date( {0}, {1} - 1, {2} )".FormatWith(maxValue.Value.Year, maxValue.Value.Month, maxValue.Value.Day)) : "", "timeFormat: 'h:mmt'", "stepMinute: {0}".FormatWith(setup.MinuteInterval.Value))))), classes: elementClass.Add(setup.Classes ?? ElementClassSet.Empty), children: textControl.PageComponent.ToCollection() .Concat( setup.IsReadOnly ? Enumerable.Empty <PhrasingComponent>() : new EwfButton( new CustomButtonStyle( children: new GenericPhrasingContainer( new FontAwesomeIcon("fa-calendar-o", "fa-stack-2x").ToCollection() .Append(new FontAwesomeIcon("fa-clock-o", "fa-stack-1x")) .Materialize(), classes: new ElementClass("fa-stack")).ToCollection()), behavior: new CustomButtonBehavior(() => "{0}.datetimepicker( 'show' );".FormatWith(getTextControlExpression(context.Id))), classes: new ElementClass("icon")).ToCollection()) .Materialize())).ToCollection()); Validation = textControl.Validation; }
/// <summary> /// BasicPage.master use only. /// </summary> public Section( DisplaySetup displaySetup, SectionStyle style, ElementClassSet classes, string heading, IReadOnlyCollection <FlowComponent> postHeadingComponents, IReadOnlyCollection <FlowComponent> content, bool?expanded, bool disableStatePersistence, IReadOnlyCollection <EtherealComponent> etherealContent) { children = new DisplayableElement( context => { var hiddenFieldId = new HiddenFieldId(); var expandedPmv = heading.Any() && expanded.HasValue && !disableStatePersistence ? new PageModificationValue <string>() : null; FlowComponent getHeadingButton() { var headingComponents = new DisplayableElement( headingContext => new DisplayableElementData( null, () => new DisplayableElementLocalData("h1"), classes: headingClass, children: heading.ToComponents())).Concat(postHeadingComponents ?? Enumerable.Empty <FlowComponent>()); return(expanded.HasValue ? // We cannot use EwfButton because we have flow content. ElementActivationBehavior.GetActivatableElement( "div", ElementClassSet.Empty, Enumerable.Empty <Tuple <string, string> >().Materialize(), ElementActivationBehavior.CreateButton( buttonBehavior: new CustomButtonBehavior( () => disableStatePersistence ? "$( '#{0}' ).toggleClass( '{1}', 200 );".FormatWith( context.Id, StringTools.ConcatenateWithDelimiter( " ", style == SectionStyle.Normal ? new[] { normalClosedClass.ClassName, normalExpandedClass.ClassName } : new[] { boxClosedClass.ClassName, boxExpandedClass.ClassName })) : hiddenFieldId.GetJsValueModificationStatements( "document.getElementById( '{0}' ).value === '{2}' ? '{1}' : '{2}'".FormatWith( hiddenFieldId.ElementId.Id, bool.FalseString, bool.TrueString)))), new GenericFlowContainer( new GenericPhrasingContainer("Click to Expand".ToComponents(), classes: closeClass).ToCollection() .Append(new GenericPhrasingContainer("Click to Close".ToComponents(), classes: expandClass)) .Concat(headingComponents) .Materialize(), classes: headingClass).ToCollection(), Enumerable.Empty <EtherealComponent>().Materialize()) : new GenericFlowContainer(new GenericFlowContainer(headingComponents.Materialize(), classes: headingClass).ToCollection())); } content = content ?? Enumerable.Empty <FlowComponent>().Materialize(); return(new DisplayableElementData( displaySetup, () => new DisplayableElementLocalData( "section", focusDependentData: new DisplayableElementFocusDependentData(includeIdAttribute: heading.Any() && expanded.HasValue && disableStatePersistence)), classes: allStylesBothStatesClass .Add( style == SectionStyle.Normal ? getSectionClasses(expanded, expandedPmv, normalClosedClass, normalExpandedClass) : getSectionClasses(expanded, expandedPmv, boxClosedClass, boxExpandedClass)) .Add(classes ?? ElementClassSet.Empty), children: (heading.Any() ? getHeadingButton().ToCollection() : Enumerable.Empty <FlowComponent>()).Concat( content.Any() ? new GenericFlowContainer(content, classes: contentClass).ToCollection() : Enumerable.Empty <FlowComponent>()) .Materialize(), etherealChildren: (expandedPmv != null ? new EwfHiddenField(expanded.Value.ToString(), id: hiddenFieldId, pageModificationValue: expandedPmv).PageComponent .ToCollection() : Enumerable.Empty <EtherealComponent>()).Concat(etherealContent ?? Enumerable.Empty <EtherealComponent>()) .Materialize())); }).ToCollection(); }
/// <summary> /// Creates a time control. /// </summary> /// <param name="value"></param> /// <param name="allowEmpty"></param> /// <param name="setup">The setup object for the time control.</param> /// <param name="minValue">The earliest allowed time.</param> /// <param name="maxValue">The latest allowed time. This can be earlier than <paramref name="minValue"/> to create a range spanning midnight.</param> /// <param name="minuteInterval">Allows the user to select values only in the given increments. Be aware that other values can still be sent from the /// browser via a crafted request.</param> /// <param name="validationMethod">The validation method. Pass null if you’re only using this control for page modification.</param> public TimeControl( LocalTime?value, bool allowEmpty, TimeControlSetup setup = null, LocalTime?minValue = null, LocalTime?maxValue = null, int minuteInterval = 15, Action <LocalTime?, Validator> validationMethod = null) { setup = setup ?? TimeControlSetup.Create(); minValue = minValue ?? LocalTime.Midnight; if (minuteInterval < 30) { var textControl = new TextControl( value.HasValue ? new TimeSpan(value.Value.TickOfDay).ToTimeOfDayHourAndMinuteString() : "", allowEmpty, setup: setup.IsReadOnly ? TextControlSetup.CreateReadOnly(validationPredicate: setup.ValidationPredicate, validationErrorNotifier: setup.ValidationErrorNotifier) : TextControlSetup.Create( autoFillTokens: setup.AutoFillTokens, action: new SpecifiedValue <FormAction>(setup.Action), valueChangedAction: setup.ValueChangedAction, pageModificationValue: setup.PageModificationValue, validationPredicate: setup.ValidationPredicate, validationErrorNotifier: setup.ValidationErrorNotifier), validationMethod: validationMethod == null ? (Action <string, Validator>)null : (postBackValue, validator) => { var errorHandler = new ValidationErrorHandler("time"); var validatedValue = validator.GetNullableTimeOfDayTimeSpan( errorHandler, postBackValue.ToUpper(), TewlContrib.DateTimeTools.HourAndMinuteFormat.ToCollection().ToArray(), allowEmpty) .ToNewUnderlyingValue(v => LocalTime.FromTicksSinceMidnight(v.Ticks)); if (errorHandler.LastResult != ErrorCondition.NoError) { setup.ValidationErrorNotifier?.Invoke(); return; } var wrap = maxValue < minValue.Value; if (!wrap ? validatedValue < minValue.Value || validatedValue > maxValue : validatedValue < minValue.Value && validatedValue > maxValue) { validator.NoteErrorAndAddMessage("The time is too early or too late."); setup.ValidationErrorNotifier?.Invoke(); return; } validationMethod(validatedValue, validator); }); Labeler = textControl.Labeler; PageComponent = new DisplayableElement( context => new DisplayableElementData( setup.DisplaySetup, () => new DisplayableElementLocalData( "div", focusDependentData: new DisplayableElementFocusDependentData( includeIdAttribute: true, jsInitStatements: "{0}.timepicker( {{ {1} }} );".FormatWith( getTextControlExpression(context.Id), StringTools.ConcatenateWithDelimiter( ", ", "timeFormat: 'h:mmt'", "stepMinute: {0}".FormatWith(minuteInterval), "showButtonPanel: false")))), classes: elementClass.Add(setup.Classes ?? ElementClassSet.Empty), children: textControl.PageComponent.ToCollection() .Concat( setup.IsReadOnly ? Enumerable.Empty <PhrasingComponent>() : new EwfButton( new CustomButtonStyle(children: new FontAwesomeIcon("fa-clock-o").ToCollection()), behavior: new CustomButtonBehavior(() => "{0}.timepicker( 'show' );".FormatWith(getTextControlExpression(context.Id))), classes: new ElementClass("icon")).ToCollection()) .Materialize())); Validation = textControl.Validation; } else { var items = from time in getTimes(minValue.Value, maxValue, minuteInterval) let timeSpan = new TimeSpan(time.TickOfDay) select SelectListItem.Create <LocalTime?>(time, timeSpan.ToTimeOfDayHourAndMinuteString()); var selectList = SelectList.CreateDropDown( setup.IsReadOnly ? DropDownSetup.CreateReadOnly( items, placeholderText: "", validationPredicate: setup.ValidationPredicate, validationErrorNotifier: setup.ValidationErrorNotifier) : DropDownSetup.Create( items, placeholderText: "", autoFillTokens: setup.AutoFillTokens, action: new SpecifiedValue <FormAction>(setup.Action), selectionChangedAction: setup.ValueChangedAction, validationPredicate: setup.ValidationPredicate, validationErrorNotifier: setup.ValidationErrorNotifier), value, placeholderIsValid: allowEmpty, validationMethod: validationMethod); Labeler = selectList.Labeler; PageComponent = new GenericFlowContainer( selectList.PageComponent.ToCollection(), displaySetup: setup.DisplaySetup, classes: elementClass.Add(setup.Classes ?? ElementClassSet.Empty)); Validation = selectList.Validation; } }