mirror of https://github.com/abpframework/abp.git
committed by
GitHub
22 changed files with 2413 additions and 31 deletions
@ -0,0 +1,215 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Microsoft.AspNetCore.Razor.TagHelpers; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
public abstract class |
|||
AbpDatePickerBaseTagHelper<TTagHelper> : AbpTagHelper<TTagHelper, AbpDatePickerBaseTagHelperService<TTagHelper>>, IAbpDatePickerOptions |
|||
where TTagHelper : AbpDatePickerBaseTagHelper<TTagHelper> |
|||
|
|||
{ |
|||
private readonly IAbpDatePickerOptions _abpDatePickerOptionsImplementation; |
|||
|
|||
public string Label { get; set; } |
|||
|
|||
public string LabelTooltip { get; set; } |
|||
|
|||
public string LabelTooltipIcon { get; set; } = "bi-info-circle"; |
|||
|
|||
public string LabelTooltipPlacement { get; set; } = "right"; |
|||
|
|||
public bool LabelTooltipHtml { get; set; } = false; |
|||
|
|||
[HtmlAttributeName("info")] |
|||
public string InfoText { get; set; } |
|||
|
|||
[HtmlAttributeName("disabled")] |
|||
public bool IsDisabled { get; set; } = false; |
|||
|
|||
[HtmlAttributeName("readonly")] |
|||
public bool? IsReadonly { get; set; } = false; |
|||
|
|||
public bool AutoFocus { get; set; } |
|||
|
|||
public AbpFormControlSize Size { get; set; } = AbpFormControlSize.Default; |
|||
|
|||
[HtmlAttributeName("required-symbol")] |
|||
public bool DisplayRequiredSymbol { get; set; } = true; |
|||
|
|||
public string Name { get; set; } |
|||
|
|||
public string Value { get; set; } |
|||
|
|||
public bool SuppressLabel { get; set; } |
|||
|
|||
protected AbpDatePickerBaseTagHelper(AbpDatePickerBaseTagHelperService<TTagHelper> service) : base(service) |
|||
{ |
|||
_abpDatePickerOptionsImplementation = new AbpDatePickerOptions(); |
|||
} |
|||
|
|||
public string PickerId { |
|||
get => _abpDatePickerOptionsImplementation.PickerId; |
|||
set => _abpDatePickerOptionsImplementation.PickerId = value; |
|||
} |
|||
|
|||
public DateTime? MinDate { |
|||
get => _abpDatePickerOptionsImplementation.MinDate; |
|||
set => _abpDatePickerOptionsImplementation.MinDate = value; |
|||
} |
|||
|
|||
public DateTime? MaxDate { |
|||
get => _abpDatePickerOptionsImplementation.MaxDate; |
|||
set => _abpDatePickerOptionsImplementation.MaxDate = value; |
|||
} |
|||
|
|||
public object MaxSpan { |
|||
get => _abpDatePickerOptionsImplementation.MaxSpan; |
|||
set => _abpDatePickerOptionsImplementation.MaxSpan = value; |
|||
} |
|||
|
|||
public bool? ShowDropdowns { |
|||
get => _abpDatePickerOptionsImplementation.ShowDropdowns; |
|||
set => _abpDatePickerOptionsImplementation.ShowDropdowns = value; |
|||
} |
|||
|
|||
public int? MinYear { |
|||
get => _abpDatePickerOptionsImplementation.MinYear; |
|||
set => _abpDatePickerOptionsImplementation.MinYear = value; |
|||
} |
|||
|
|||
public int? MaxYear { |
|||
get => _abpDatePickerOptionsImplementation.MaxYear; |
|||
set => _abpDatePickerOptionsImplementation.MaxYear = value; |
|||
} |
|||
|
|||
public AbpDatePickerWeekNumbers WeekNumbers { |
|||
get => _abpDatePickerOptionsImplementation.WeekNumbers; |
|||
set => _abpDatePickerOptionsImplementation.WeekNumbers = value; |
|||
} |
|||
|
|||
public bool? TimePicker { |
|||
get => _abpDatePickerOptionsImplementation.TimePicker; |
|||
set => _abpDatePickerOptionsImplementation.TimePicker = value; |
|||
} |
|||
|
|||
public int? TimePickerIncrement { |
|||
get => _abpDatePickerOptionsImplementation.TimePickerIncrement; |
|||
set => _abpDatePickerOptionsImplementation.TimePickerIncrement = value; |
|||
} |
|||
|
|||
public bool? TimePicker24Hour { |
|||
get => _abpDatePickerOptionsImplementation.TimePicker24Hour; |
|||
set => _abpDatePickerOptionsImplementation.TimePicker24Hour = value; |
|||
} |
|||
|
|||
public bool? TimePickerSeconds { |
|||
get => _abpDatePickerOptionsImplementation.TimePickerSeconds; |
|||
set => _abpDatePickerOptionsImplementation.TimePickerSeconds = value; |
|||
} |
|||
|
|||
public List<AbpDatePickerRange> Ranges { |
|||
get => _abpDatePickerOptionsImplementation.Ranges; |
|||
set => _abpDatePickerOptionsImplementation.Ranges = value; |
|||
} |
|||
|
|||
public bool? ShowCustomRangeLabel { |
|||
get => _abpDatePickerOptionsImplementation.ShowCustomRangeLabel; |
|||
set => _abpDatePickerOptionsImplementation.ShowCustomRangeLabel = value; |
|||
} |
|||
|
|||
public bool? AlwaysShowCalendars { |
|||
get => _abpDatePickerOptionsImplementation.AlwaysShowCalendars; |
|||
set => _abpDatePickerOptionsImplementation.AlwaysShowCalendars = value; |
|||
} |
|||
|
|||
public AbpDatePickerOpens Opens { |
|||
get => _abpDatePickerOptionsImplementation.Opens; |
|||
set => _abpDatePickerOptionsImplementation.Opens = value; |
|||
} |
|||
|
|||
public AbpDatePickerDrops Drops { |
|||
get => _abpDatePickerOptionsImplementation.Drops; |
|||
set => _abpDatePickerOptionsImplementation.Drops = value; |
|||
} |
|||
|
|||
public string ButtonClasses { |
|||
get => _abpDatePickerOptionsImplementation.ButtonClasses; |
|||
set => _abpDatePickerOptionsImplementation.ButtonClasses = value; |
|||
} |
|||
|
|||
public string TodayButtonClasses { |
|||
get => _abpDatePickerOptionsImplementation.TodayButtonClasses; |
|||
set => _abpDatePickerOptionsImplementation.TodayButtonClasses = value; |
|||
} |
|||
|
|||
public string ApplyButtonClasses { |
|||
get => _abpDatePickerOptionsImplementation.ApplyButtonClasses; |
|||
set => _abpDatePickerOptionsImplementation.ApplyButtonClasses = value; |
|||
} |
|||
|
|||
public string ClearButtonClasses { |
|||
get => _abpDatePickerOptionsImplementation.ClearButtonClasses; |
|||
set => _abpDatePickerOptionsImplementation.ClearButtonClasses = value; |
|||
} |
|||
|
|||
public object Locale { |
|||
get => _abpDatePickerOptionsImplementation.Locale; |
|||
set => _abpDatePickerOptionsImplementation.Locale = value; |
|||
} |
|||
|
|||
public bool? AutoApply { |
|||
get => _abpDatePickerOptionsImplementation.AutoApply; |
|||
set => _abpDatePickerOptionsImplementation.AutoApply = value; |
|||
} |
|||
|
|||
public bool? LinkedCalendars { |
|||
get => _abpDatePickerOptionsImplementation.LinkedCalendars; |
|||
set => _abpDatePickerOptionsImplementation.LinkedCalendars = value; |
|||
} |
|||
|
|||
public bool? AutoUpdateInput { |
|||
get => _abpDatePickerOptionsImplementation.AutoUpdateInput; |
|||
set => _abpDatePickerOptionsImplementation.AutoUpdateInput = value; |
|||
} |
|||
|
|||
public string ParentEl { |
|||
get => _abpDatePickerOptionsImplementation.ParentEl; |
|||
set => _abpDatePickerOptionsImplementation.ParentEl = value; |
|||
} |
|||
|
|||
public string DateFormat { |
|||
get => _abpDatePickerOptionsImplementation.DateFormat; |
|||
set => _abpDatePickerOptionsImplementation.DateFormat = value; |
|||
} |
|||
|
|||
public bool OpenButton { |
|||
get => _abpDatePickerOptionsImplementation.OpenButton; |
|||
set => _abpDatePickerOptionsImplementation.OpenButton = value; |
|||
} |
|||
|
|||
public bool ClearButton { |
|||
get => _abpDatePickerOptionsImplementation.ClearButton; |
|||
set => _abpDatePickerOptionsImplementation.ClearButton = value; |
|||
} |
|||
|
|||
public bool SingleOpenAndClearButton { |
|||
get => _abpDatePickerOptionsImplementation.SingleOpenAndClearButton; |
|||
set => _abpDatePickerOptionsImplementation.SingleOpenAndClearButton = value; |
|||
} |
|||
|
|||
public bool? IsUtc { |
|||
get => _abpDatePickerOptionsImplementation.IsUtc; |
|||
set => _abpDatePickerOptionsImplementation.IsUtc = value; |
|||
} |
|||
|
|||
public bool? IsIso { |
|||
get => _abpDatePickerOptionsImplementation.IsIso; |
|||
set => _abpDatePickerOptionsImplementation.IsIso = value; |
|||
} |
|||
|
|||
public object Options { |
|||
get => _abpDatePickerOptionsImplementation.Options; |
|||
set => _abpDatePickerOptionsImplementation.Options = value; |
|||
} |
|||
} |
|||
@ -0,0 +1,712 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using System.Linq; |
|||
using System.Text.Encodings.Web; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Localization.Resources.AbpUi; |
|||
using Microsoft.AspNetCore.Mvc.Rendering; |
|||
using Microsoft.AspNetCore.Mvc.TagHelpers; |
|||
using Microsoft.AspNetCore.Mvc.ViewFeatures; |
|||
using Microsoft.AspNetCore.Razor.TagHelpers; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Localization; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
public abstract class AbpDatePickerBaseTagHelperService<TTagHelper> : AbpTagHelperService<TTagHelper> |
|||
where TTagHelper : AbpDatePickerBaseTagHelper<TTagHelper> |
|||
{ |
|||
protected readonly Dictionary<Type,Func<object,string>> SupportedInputTypes = new() { |
|||
{typeof(string), o => DateTime.Parse((string)o).ToString("O")}, |
|||
{typeof(DateTime), o => ((DateTime) o).ToString("O")}, |
|||
{typeof(DateTime?), o => ((DateTime?) o)?.ToString("O")}, |
|||
{typeof(DateTimeOffset), o => ((DateTimeOffset) o).ToString("O")}, |
|||
{typeof(DateTimeOffset?), o => ((DateTimeOffset?) o)?.ToString("O")} |
|||
}; |
|||
|
|||
protected readonly IJsonSerializer JsonSerializer; |
|||
protected readonly IHtmlGenerator Generator; |
|||
protected readonly HtmlEncoder Encoder; |
|||
protected readonly IServiceProvider ServiceProvider; |
|||
protected readonly IAbpTagHelperLocalizer TagHelperLocalizer; |
|||
protected virtual string TagName { get; set; } = "abp-date-picker"; |
|||
protected IStringLocalizer<AbpUiResource> L { get; } |
|||
protected InputTagHelper InputTagHelper { get; set; } |
|||
protected abstract TagHelperOutput TagHelperOutput { get; set; } |
|||
|
|||
protected AbpDatePickerBaseTagHelperService(IJsonSerializer jsonSerializer, IHtmlGenerator generator, |
|||
HtmlEncoder encoder, IServiceProvider serviceProvider, IStringLocalizer<AbpUiResource> l, |
|||
IAbpTagHelperLocalizer tagHelperLocalizer) |
|||
{ |
|||
JsonSerializer = jsonSerializer; |
|||
Generator = generator; |
|||
Encoder = encoder; |
|||
ServiceProvider = serviceProvider; |
|||
L = l; |
|||
TagHelperLocalizer = tagHelperLocalizer; |
|||
|
|||
InputTagHelper = new InputTagHelper(Generator) { InputTypeName = "text" }; |
|||
} |
|||
|
|||
protected virtual T GetAttribute<T>() where T : Attribute |
|||
{ |
|||
return GetAttributeAndModelExpression<T>(out _); |
|||
} |
|||
|
|||
protected abstract T GetAttributeAndModelExpression<T>(out ModelExpression modelExpression) where T : Attribute; |
|||
|
|||
|
|||
public async override Task ProcessAsync(TagHelperContext context, TagHelperOutput output) |
|||
{ |
|||
TagHelperOutput = new TagHelperOutput("input", GetInputAttributes(context, output), (_, _) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent())); |
|||
|
|||
InputTagHelper.ViewContext = TagHelper.ViewContext; |
|||
|
|||
if (!TagHelper.Name.IsNullOrEmpty()) |
|||
{ |
|||
InputTagHelper.Name = TagHelper.Name; |
|||
} |
|||
|
|||
if (!TagHelper.Value.IsNullOrEmpty()) |
|||
{ |
|||
InputTagHelper.Value = TagHelper.Value; |
|||
} |
|||
|
|||
AddDisabledAttribute(TagHelperOutput); |
|||
AddAutoFocusAttribute(TagHelperOutput); |
|||
AddFormControls(context, output, TagHelperOutput); |
|||
AddReadOnlyAttribute(TagHelperOutput); |
|||
AddPlaceholderAttribute(TagHelperOutput); |
|||
AddInfoTextId(TagHelperOutput); |
|||
|
|||
// Open and close button
|
|||
var openButtonContent = TagHelper.OpenButton |
|||
? await ProcessButtonAndGetContentAsync(context, output, "calendar", "open") |
|||
: ""; |
|||
var clearButtonContent = TagHelper.ClearButton |
|||
? await ProcessButtonAndGetContentAsync(context, output, "times", "clear", visible:!TagHelper.SingleOpenAndClearButton) |
|||
: ""; |
|||
|
|||
var labelContent = await GetLabelAsHtmlAsync(context, output, TagHelperOutput); |
|||
var infoContent = GetInfoAsHtml(context, output, TagHelperOutput); |
|||
var validationContent = await GetValidationAsHtmlAsync(context, output); |
|||
|
|||
var inputGroup = new TagHelperOutput("div", |
|||
new TagHelperAttributeList(new[] { new TagHelperAttribute("class", "input-group") }), |
|||
(_, _) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent())); |
|||
inputGroup.Content.AppendHtml( |
|||
TagHelperOutput.Render(Encoder) + openButtonContent + clearButtonContent |
|||
); |
|||
|
|||
var abpDatePickerTag = new TagHelperOutput(TagName, GetBaseTagAttributes(context, output, TagHelper), |
|||
(_, _) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent())); |
|||
abpDatePickerTag.Content.AppendHtml(inputGroup.Render(Encoder)); |
|||
abpDatePickerTag.Content.AppendHtml(validationContent); |
|||
abpDatePickerTag.Content.AppendHtml(GetExtraInputHtml(context, output)); |
|||
|
|||
var innerHtml = labelContent + abpDatePickerTag.Render(Encoder) + infoContent; |
|||
|
|||
var order = GetOrder(); |
|||
|
|||
AddGroupToFormGroupContents( |
|||
context, |
|||
GetPropertyName(), |
|||
SurroundInnerHtmlAndGet(context, output, innerHtml), |
|||
order |
|||
); |
|||
|
|||
|
|||
output.TagMode = TagMode.StartTagAndEndTag; |
|||
output.TagName = "div"; |
|||
LeaveOnlyGroupAttributes(context, output); |
|||
output.Attributes.AddClass("mb-3"); |
|||
|
|||
output.Content.AppendHtml(innerHtml); |
|||
} |
|||
|
|||
protected virtual void AddReadOnlyAttribute(TagHelperOutput inputTagHelperOutput) |
|||
{ |
|||
if (inputTagHelperOutput.Attributes.ContainsName("readonly") == false && |
|||
(TagHelper.IsReadonly != false || GetAttribute<ReadOnlyInput>() != null)) |
|||
{ |
|||
inputTagHelperOutput.Attributes.Add("readonly", ""); |
|||
} |
|||
} |
|||
|
|||
protected virtual TagHelperAttributeList GetInputAttributes(TagHelperContext context, TagHelperOutput output) |
|||
{ |
|||
var groupPrefix = "group-"; |
|||
|
|||
var tagHelperAttributes = output.Attributes.Where(a => !a.Name.StartsWith(groupPrefix)).ToList(); |
|||
|
|||
var attrList = new TagHelperAttributeList(); |
|||
|
|||
foreach (var tagHelperAttribute in tagHelperAttributes) |
|||
{ |
|||
attrList.Add(tagHelperAttribute); |
|||
} |
|||
|
|||
attrList.Add("type", "text"); |
|||
|
|||
if (attrList.ContainsName("value")) |
|||
{ |
|||
attrList.Remove(attrList.First(a => a.Name == "value")); |
|||
} |
|||
|
|||
if (!TagHelper.Name.IsNullOrEmpty() && !attrList.ContainsName("name")) |
|||
{ |
|||
attrList.Add("name", TagHelper.Name); |
|||
} |
|||
|
|||
if (!attrList.ContainsName("autocomplete")) |
|||
{ |
|||
attrList.Add("autocomplete", "off"); |
|||
} |
|||
|
|||
return attrList; |
|||
} |
|||
protected virtual void LeaveOnlyGroupAttributes(TagHelperContext context, TagHelperOutput output) |
|||
{ |
|||
var groupPrefix = "group-"; |
|||
var tagHelperAttributes = output.Attributes.Where(a => a.Name.StartsWith(groupPrefix)).ToList(); |
|||
|
|||
output.Attributes.Clear(); |
|||
|
|||
foreach (var tagHelperAttribute in tagHelperAttributes) |
|||
{ |
|||
var nameWithoutPrefix = tagHelperAttribute.Name.Substring(groupPrefix.Length); |
|||
var newAttribute = new TagHelperAttribute(nameWithoutPrefix, tagHelperAttribute.Value); |
|||
output.Attributes.Add(newAttribute); |
|||
} |
|||
} |
|||
|
|||
protected virtual string SurroundInnerHtmlAndGet(TagHelperContext context, TagHelperOutput output, string innerHtml) |
|||
{ |
|||
return "<div class=\"mb-3\">" + |
|||
Environment.NewLine + innerHtml + Environment.NewLine + |
|||
"</div>"; |
|||
} |
|||
|
|||
protected abstract string GetPropertyName(); |
|||
|
|||
protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, |
|||
int order) |
|||
{ |
|||
var list = context.GetValue<List<FormGroupItem>>(FormGroupContents) ?? new List<FormGroupItem>(); |
|||
|
|||
if (!list.Any(igc => igc.HtmlContent.Contains("id=\"" + propertyName.Replace('.', '_') + "\""))) |
|||
{ |
|||
list.Add(new FormGroupItem { HtmlContent = html, Order = order, PropertyName = propertyName }); |
|||
} |
|||
} |
|||
|
|||
protected abstract int GetOrder(); |
|||
protected abstract void AddBaseTagAttributes(TagHelperAttributeList attributes); |
|||
|
|||
protected virtual string GetExtraInputHtml(TagHelperContext context, TagHelperOutput output) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
protected TagHelperAttributeList ConvertDatePickerOptionsToAttributeList(IAbpDatePickerOptions options) |
|||
{ |
|||
var attrList = new TagHelperAttributeList(); |
|||
|
|||
if(options == null) |
|||
{ |
|||
return attrList; |
|||
} |
|||
|
|||
if (options.Locale != null) |
|||
{ |
|||
attrList.Add("data-locale", JsonSerializer.Serialize(options.Locale)); |
|||
} |
|||
|
|||
if (options.MinDate != null) |
|||
{ |
|||
attrList.Add("data-min-date", options.MinDate); |
|||
} |
|||
|
|||
if (options.MaxDate != null) |
|||
{ |
|||
attrList.Add("data-max-date", options.MaxDate); |
|||
} |
|||
|
|||
if (options.MaxSpan != null) |
|||
{ |
|||
attrList.Add("data-max-span", JsonSerializer.Serialize(options.MaxSpan)); |
|||
} |
|||
|
|||
if (options.ShowDropdowns == false) |
|||
{ |
|||
attrList.Add("data-show-dropdowns", options.ShowDropdowns.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
if (options.MinYear != null) |
|||
{ |
|||
attrList.Add("data-min-year", options.MinYear); |
|||
} |
|||
|
|||
if (options.MaxYear != null) |
|||
{ |
|||
attrList.Add("data-max-year", options.MaxYear); |
|||
} |
|||
|
|||
switch (options.WeekNumbers) |
|||
{ |
|||
case AbpDatePickerWeekNumbers.Normal: |
|||
attrList.Add("data-show-week-numbers", "true"); |
|||
break; |
|||
case AbpDatePickerWeekNumbers.Iso: |
|||
attrList.Add("data-show-i-s-o-week-numbers", "true"); |
|||
break; |
|||
} |
|||
|
|||
if (options.TimePicker != null) |
|||
{ |
|||
attrList.Add("data-time-picker", options.TimePicker.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
if (options.TimePickerIncrement != null) |
|||
{ |
|||
attrList.Add("data-time-picker-increment", options.TimePickerIncrement); |
|||
} |
|||
|
|||
if (options.TimePicker24Hour != null) |
|||
{ |
|||
attrList.Add("data-time-picker24-hour", options.TimePicker24Hour.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
if (options.TimePickerSeconds != null) |
|||
{ |
|||
attrList.Add("data-time-picker-seconds", options.TimePickerSeconds.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
if (options.Opens != AbpDatePickerOpens.Center) |
|||
{ |
|||
attrList.Add("data-opens", options.Opens.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
if (options.Drops != AbpDatePickerDrops.Down) |
|||
{ |
|||
attrList.Add("data-drops", options.Drops.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
if (!options.ButtonClasses.IsNullOrEmpty()) |
|||
{ |
|||
attrList.Add("data-button-classes", options.ButtonClasses); |
|||
} |
|||
|
|||
if (!options.ApplyButtonClasses.IsNullOrEmpty()) |
|||
{ |
|||
attrList.Add("data-apply-button-classes", options.ApplyButtonClasses); |
|||
} |
|||
|
|||
if (!options.ClearButtonClasses.IsNullOrEmpty()) |
|||
{ |
|||
attrList.Add("data-clear-button-classes", options.ClearButtonClasses); |
|||
} |
|||
|
|||
if (!options.TodayButtonClasses.IsNullOrEmpty()) |
|||
{ |
|||
attrList.Add("data-today-button-classes", options.TodayButtonClasses); |
|||
} |
|||
|
|||
if (options.AutoApply != null) |
|||
{ |
|||
attrList.Add("data-auto-apply", options.AutoApply.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
if (options.LinkedCalendars != null) |
|||
{ |
|||
attrList.Add("data-linked-calendars", options.LinkedCalendars.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
if (options.AutoUpdateInput != null) |
|||
{ |
|||
attrList.Add("data-auto-update-input", options.AutoUpdateInput.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
if (!options.ParentEl.IsNullOrEmpty()) |
|||
{ |
|||
attrList.Add("data-parent-el", options.ParentEl); |
|||
} |
|||
|
|||
if (!options.DateFormat.IsNullOrEmpty()) |
|||
{ |
|||
attrList.Add("data-date-format", options.DateFormat); |
|||
} |
|||
|
|||
if(options.Ranges != null && options.Ranges.Any()) |
|||
{ |
|||
var ranges = options.Ranges.ToDictionary(r => r.Label, r => r.Dates); |
|||
|
|||
attrList.Add("data-ranges", JsonSerializer.Serialize(ranges)); |
|||
} |
|||
|
|||
if(options.AlwaysShowCalendars != null) |
|||
{ |
|||
attrList.Add("data-always-show-calendars", options.AlwaysShowCalendars.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
if(options.ShowCustomRangeLabel == false) |
|||
{ |
|||
attrList.Add("data-show-custom-range-label", options.ShowCustomRangeLabel.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
if(options.Options != null) |
|||
{ |
|||
attrList.Add("data-options", JsonSerializer.Serialize(options.Options)); |
|||
} |
|||
|
|||
if (options.IsUtc != null) |
|||
{ |
|||
attrList.Add("data-is-utc", options.IsUtc.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
if (options.IsIso != null) |
|||
{ |
|||
attrList.Add("data-is-iso", options.IsIso.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
if (!options.PickerId.IsNullOrWhiteSpace()) |
|||
{ |
|||
attrList.Add("id", options.PickerId); |
|||
} |
|||
|
|||
if(!options.SingleOpenAndClearButton) |
|||
{ |
|||
attrList.Add("data-single-open-and-clear-button", options.SingleOpenAndClearButton.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
return attrList; |
|||
} |
|||
|
|||
protected TagHelperAttributeList GetBaseTagAttributes(TagHelperContext context, TagHelperOutput output, IAbpDatePickerOptions options) |
|||
{ |
|||
var groupPrefix = "group-"; |
|||
|
|||
var tagHelperAttributes = output.Attributes.Where(a => !a.Name.StartsWith(groupPrefix)).ToList(); |
|||
|
|||
var attrList = new TagHelperAttributeList(); |
|||
|
|||
foreach (var tagHelperAttribute in tagHelperAttributes) |
|||
{ |
|||
attrList.Add(tagHelperAttribute); |
|||
} |
|||
|
|||
if (attrList.ContainsName("type")) |
|||
{ |
|||
attrList.Remove(attrList.First(a => a.Name == "type")); |
|||
} |
|||
|
|||
if (attrList.ContainsName("name")) |
|||
{ |
|||
attrList.Remove(attrList.First(a => a.Name == "name")); |
|||
} |
|||
|
|||
if (attrList.ContainsName("id")) |
|||
{ |
|||
attrList.Remove(attrList.First(a => a.Name == "id")); |
|||
} |
|||
|
|||
if (attrList.ContainsName("value")) |
|||
{ |
|||
attrList.Remove(attrList.First(a => a.Name == "value")); |
|||
} |
|||
|
|||
foreach (var attr in ConvertDatePickerOptionsToAttributeList(options)) |
|||
{ |
|||
attrList.Add(attr); |
|||
} |
|||
|
|||
var optionsAttribute = GetAttributeAndModelExpression<DatePickerOptionsAttribute>(out var modelExpression); |
|||
if (optionsAttribute != null) |
|||
{ |
|||
foreach (var attr in ConvertDatePickerOptionsToAttributeList(optionsAttribute.GetDatePickerOptions(modelExpression.ModelExplorer))) |
|||
{ |
|||
attrList.Add(attr); |
|||
} |
|||
} |
|||
|
|||
AddBaseTagAttributes(attrList); |
|||
|
|||
return attrList; |
|||
} |
|||
|
|||
protected virtual bool IsOutputHidden(TagHelperOutput inputTag) |
|||
{ |
|||
return inputTag.Attributes.Any(a => |
|||
a.Name.ToLowerInvariant() == "type" && a.Value?.ToString()?.ToLowerInvariant() == "hidden"); |
|||
} |
|||
|
|||
protected virtual string GetInfoAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag) |
|||
{ |
|||
if (IsOutputHidden(inputTag)) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
string text; |
|||
ModelExplorer modelExplorer = null; |
|||
|
|||
if (!string.IsNullOrEmpty(TagHelper.InfoText)) |
|||
{ |
|||
text = TagHelper.InfoText; |
|||
} |
|||
else |
|||
{ |
|||
var infoAttribute = GetAttributeAndModelExpression<InputInfoText>(out var modelExpression); |
|||
if (infoAttribute != null) |
|||
{ |
|||
modelExplorer = modelExpression.ModelExplorer; |
|||
text = infoAttribute.Text; |
|||
} |
|||
else |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
} |
|||
|
|||
var idAttr = inputTag.Attributes.FirstOrDefault(a => a.Name == "id"); |
|||
modelExplorer ??= GetModelExpression().ModelExplorer; |
|||
var localizedText = TagHelperLocalizer.GetLocalizedText(text, modelExplorer); |
|||
|
|||
var div = new TagBuilder("div"); |
|||
div.Attributes.Add("id", idAttr?.Value + "InfoText"); |
|||
div.AddCssClass("form-text"); |
|||
div.InnerHtml.Append(localizedText); |
|||
|
|||
inputTag.Attributes.Add("aria-describedby", idAttr?.Value + "InfoText"); |
|||
|
|||
return div.ToHtmlString(); |
|||
} |
|||
|
|||
protected virtual async Task<string> GetLabelAsHtmlAsync(TagHelperContext context, TagHelperOutput output, |
|||
TagHelperOutput inputTag) |
|||
{ |
|||
if (IsOutputHidden(inputTag) || TagHelper.SuppressLabel) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
if (string.IsNullOrEmpty(TagHelper.Label)) |
|||
{ |
|||
return await GetLabelAsHtmlUsingTagHelperAsync(context, output) + GetRequiredSymbol(context, output); |
|||
} |
|||
|
|||
var label = new TagBuilder("label"); |
|||
label.Attributes.Add("for", GetIdAttributeValue(inputTag)); |
|||
label.InnerHtml.AppendHtml(TagHelper.Label); |
|||
|
|||
label.AddCssClass("form-label"); |
|||
|
|||
if (!TagHelper.LabelTooltip.IsNullOrEmpty()) |
|||
{ |
|||
label.Attributes.Add("data-bs-toggle", "tooltip"); |
|||
label.Attributes.Add("data-bs-placement", TagHelper.LabelTooltipPlacement); |
|||
if (TagHelper.LabelTooltipHtml) |
|||
{ |
|||
label.Attributes.Add("data-bs-html", "true"); |
|||
} |
|||
|
|||
label.Attributes.Add("title", TagHelper.LabelTooltip); |
|||
label.InnerHtml.AppendHtml($" <i class=\"bi {TagHelper.LabelTooltipIcon}\"></i>"); |
|||
} |
|||
|
|||
return label.ToHtmlString(); |
|||
} |
|||
|
|||
protected virtual string GetIdAttributeValue(TagHelperOutput inputTag) |
|||
{ |
|||
var idAttr = inputTag.Attributes.FirstOrDefault(a => a.Name == "id"); |
|||
|
|||
return idAttr != null ? idAttr.Value.ToString() : string.Empty; |
|||
} |
|||
|
|||
protected virtual string GetRequiredSymbol(TagHelperContext context, TagHelperOutput output) |
|||
{ |
|||
if (!TagHelper.DisplayRequiredSymbol) |
|||
{ |
|||
return ""; |
|||
} |
|||
|
|||
var isHaveRequiredAttribute = context.AllAttributes.Any(a => a.Name == "required"); |
|||
|
|||
return GetAttribute<RequiredAttribute>() != null || isHaveRequiredAttribute ? "<span> * </span>" : ""; |
|||
} |
|||
|
|||
protected abstract ModelExpression GetModelExpression(); |
|||
|
|||
protected virtual async Task<string> GetLabelAsHtmlUsingTagHelperAsync(TagHelperContext context, |
|||
TagHelperOutput output) |
|||
{ |
|||
var modelExpression = GetModelExpression(); |
|||
if (modelExpression == null) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
var labelTagHelper = new LabelTagHelper(Generator) { |
|||
ViewContext = TagHelper.ViewContext, |
|||
For = modelExpression |
|||
}; |
|||
|
|||
var attributeList = new TagHelperAttributeList(); |
|||
|
|||
attributeList.AddClass("form-label"); |
|||
|
|||
if (!TagHelper.LabelTooltip.IsNullOrEmpty()) |
|||
{ |
|||
attributeList.Add("data-bs-toggle", "tooltip"); |
|||
attributeList.Add("data-bs-placement", TagHelper.LabelTooltipPlacement); |
|||
if (TagHelper.LabelTooltipHtml) |
|||
{ |
|||
attributeList.Add("data-bs-html", "true"); |
|||
} |
|||
|
|||
attributeList.Add("title", TagHelper.LabelTooltip); |
|||
} |
|||
|
|||
var innerOutput = |
|||
await labelTagHelper.ProcessAndGetOutputAsync(attributeList, context, "label", TagMode.StartTagAndEndTag); |
|||
if (!TagHelper.LabelTooltip.IsNullOrEmpty()) |
|||
{ |
|||
innerOutput.Content.AppendHtml($" <i class=\"bi {TagHelper.LabelTooltipIcon}\"></i>"); |
|||
} |
|||
|
|||
return innerOutput.Render(Encoder); |
|||
} |
|||
|
|||
protected virtual async Task<string> ProcessButtonAndGetContentAsync(TagHelperContext context, |
|||
TagHelperOutput output, string icon, string type, bool visible = true) |
|||
{ |
|||
var abpButtonTagHelper = ServiceProvider.GetRequiredService<AbpButtonTagHelper>(); |
|||
var attributes = |
|||
new TagHelperAttributeList { new("type", "button"), new("tabindex", "-1"), new("data-type", type) }; |
|||
abpButtonTagHelper.ButtonType = AbpButtonType.Outline_Secondary; |
|||
abpButtonTagHelper.Icon = icon; |
|||
|
|||
abpButtonTagHelper.Disabled = TagHelper.IsDisabled; |
|||
|
|||
if (!visible) |
|||
{ |
|||
attributes.AddClass("d-none"); |
|||
} |
|||
|
|||
return await abpButtonTagHelper.RenderAsync(attributes, context, Encoder, "button", TagMode.StartTagAndEndTag); |
|||
} |
|||
|
|||
protected virtual void AddInfoTextId(TagHelperOutput inputTagHelperOutput) |
|||
{ |
|||
if (GetAttribute<InputInfoText>() == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var idAttr = inputTagHelperOutput.Attributes.FirstOrDefault(a => a.Name == "id"); |
|||
|
|||
if (idAttr == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
inputTagHelperOutput.Attributes.Add("aria-describedby", GetInfoText()); |
|||
} |
|||
|
|||
public virtual string GetInfoText() |
|||
{ |
|||
var infoAttribute = GetAttributeAndModelExpression<InputInfoText>(out var modelExpression); |
|||
|
|||
if (infoAttribute != null) |
|||
{ |
|||
return TagHelperLocalizer.GetLocalizedText(infoAttribute.Text, modelExpression.ModelExplorer); |
|||
} |
|||
|
|||
return string.Empty; |
|||
} |
|||
|
|||
protected virtual void AddPlaceholderAttribute(TagHelperOutput inputTagHelperOutput) |
|||
{ |
|||
if (inputTagHelperOutput.Attributes.ContainsName("placeholder")) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var attribute = GetAttributeAndModelExpression<Placeholder>(out var modelExpression); |
|||
|
|||
if (attribute != null) |
|||
{ |
|||
var placeholderLocalized = |
|||
TagHelperLocalizer.GetLocalizedText(attribute.Value, modelExpression.ModelExplorer); |
|||
|
|||
inputTagHelperOutput.Attributes.Add("placeholder", placeholderLocalized); |
|||
} |
|||
} |
|||
|
|||
protected virtual void AddFormControls(TagHelperContext context, TagHelperOutput output, |
|||
TagHelperOutput inputTagHelperOutput) |
|||
{ |
|||
inputTagHelperOutput.Attributes.AddClass("form-control"); |
|||
var size = GetSize(context, output); |
|||
if (!size.IsNullOrEmpty()) |
|||
{ |
|||
inputTagHelperOutput.Attributes.AddClass(size); |
|||
} |
|||
} |
|||
|
|||
protected virtual void AddAutoFocusAttribute(TagHelperOutput inputTagHelperOutput) |
|||
{ |
|||
if (TagHelper.AutoFocus && !inputTagHelperOutput.Attributes.ContainsName("data-auto-focus")) |
|||
{ |
|||
inputTagHelperOutput.Attributes.Add("data-auto-focus", "true"); |
|||
} |
|||
} |
|||
|
|||
protected virtual void AddDisabledAttribute(TagHelperOutput inputTagHelperOutput) |
|||
{ |
|||
if (inputTagHelperOutput.Attributes.ContainsName("disabled") == false && |
|||
(TagHelper.IsDisabled || GetAttribute<DisabledInput>() != null)) |
|||
{ |
|||
inputTagHelperOutput.Attributes.Add("disabled", ""); |
|||
} |
|||
} |
|||
|
|||
|
|||
protected virtual string GetSize(TagHelperContext context, TagHelperOutput output) |
|||
{ |
|||
// TODO: Test this method
|
|||
var attribute = GetAttribute<FormControlSize>(); |
|||
|
|||
if (attribute != null) |
|||
{ |
|||
TagHelper.Size = attribute.Size; |
|||
} |
|||
|
|||
return TagHelper.Size switch { |
|||
AbpFormControlSize.Small => "form-control-sm", |
|||
AbpFormControlSize.Medium => "form-control-md", |
|||
AbpFormControlSize.Large => "form-control-lg", |
|||
_ => "" |
|||
}; |
|||
} |
|||
|
|||
protected abstract Task<string> GetValidationAsHtmlAsync(TagHelperContext context, TagHelperOutput output); |
|||
|
|||
protected virtual async Task<string> GetValidationAsHtmlByInputAsync(TagHelperContext context, |
|||
TagHelperOutput output, |
|||
[NotNull]InputTagHelper inputTag) |
|||
{ |
|||
var validationMessageTagHelper = |
|||
new ValidationMessageTagHelper(Generator) { For = inputTag.For, ViewContext = TagHelper.ViewContext }; |
|||
|
|||
var attributeList = new TagHelperAttributeList { { "class", "text-danger col-auto" } }; |
|||
|
|||
return await validationMessageTagHelper.RenderAsync(attributeList, context, Encoder, "span", |
|||
TagMode.StartTagAndEndTag); |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
public enum AbpDatePickerDrops : byte |
|||
{ |
|||
Down = 1, |
|||
Up = 2, |
|||
Auto = 3 |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
public enum AbpDatePickerOpens : byte |
|||
{ |
|||
Left = 1, |
|||
Right = 2, |
|||
Center = 3, |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
public class AbpDatePickerOptions : IAbpDatePickerOptions |
|||
{ |
|||
public string PickerId { get; set; } |
|||
public DateTime? MinDate { get; set; } |
|||
public DateTime? MaxDate { get; set; } |
|||
public object MaxSpan { get; set; } |
|||
public bool? ShowDropdowns { get; set; } |
|||
public int? MinYear { get; set; } |
|||
public int? MaxYear { get; set; } |
|||
public AbpDatePickerWeekNumbers WeekNumbers { get; set; } = AbpDatePickerWeekNumbers.None; |
|||
public bool? TimePicker { get; set; } |
|||
public int? TimePickerIncrement { get; set; } |
|||
public bool? TimePicker24Hour { get; set; } |
|||
public bool? TimePickerSeconds { get; set; } |
|||
public List<AbpDatePickerRange> Ranges { get; set; } |
|||
public bool? ShowCustomRangeLabel { get; set; } |
|||
public bool? AlwaysShowCalendars { get; set; } |
|||
public AbpDatePickerOpens Opens { get; set; } = AbpDatePickerOpens.Center; |
|||
public AbpDatePickerDrops Drops { get; set; } = AbpDatePickerDrops.Down; |
|||
public string ButtonClasses { get; set; } |
|||
public string TodayButtonClasses { get; set; } |
|||
public string ApplyButtonClasses { get; set; } |
|||
public string ClearButtonClasses { get; set; } |
|||
public object Locale { get; set; } |
|||
public bool? AutoApply { get; set; } |
|||
public bool? LinkedCalendars { get; set; } |
|||
public bool? AutoUpdateInput { get; set; } |
|||
public string ParentEl { get; set; } |
|||
public string DateFormat { get; set; } |
|||
public bool OpenButton { get; set; } = true; |
|||
public bool ClearButton { get; set; } = true; |
|||
public bool SingleOpenAndClearButton { get; set; } = true; |
|||
public bool? IsUtc { get; set; } |
|||
public bool? IsIso { get; set; } |
|||
public object Options { get; set; } |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
public class AbpDatePickerRange |
|||
{ |
|||
private readonly List<string> _dates = new List<string>(); |
|||
public string Label { get; set; } |
|||
public IReadOnlyList<string> Dates => _dates; |
|||
|
|||
public AbpDatePickerRange() |
|||
{ |
|||
|
|||
} |
|||
public AbpDatePickerRange(string label, DateTime start, DateTime end) |
|||
{ |
|||
Label = label; |
|||
AddDate(start); |
|||
AddDate(end); |
|||
} |
|||
|
|||
public AbpDatePickerRange(string label, DateTime date) |
|||
{ |
|||
Label = label; |
|||
AddDate(date); |
|||
} |
|||
|
|||
public AbpDatePickerRange(string label, DateTimeOffset start, DateTimeOffset end) |
|||
{ |
|||
Label = label; |
|||
AddDate(start); |
|||
AddDate(end); |
|||
} |
|||
|
|||
public AbpDatePickerRange(string label, DateTimeOffset date) |
|||
{ |
|||
Label = label; |
|||
AddDate(date); |
|||
} |
|||
|
|||
public AbpDatePickerRange(string label, string start, string end) |
|||
{ |
|||
Label = label; |
|||
AddDate(start); |
|||
AddDate(end); |
|||
} |
|||
|
|||
public AbpDatePickerRange(string label, string date) |
|||
{ |
|||
Label = label; |
|||
AddDate(date); |
|||
} |
|||
|
|||
public void AddDate(string date) |
|||
{ |
|||
_dates.Add(DateTime.Parse(date).ToString("O")); |
|||
} |
|||
|
|||
public void AddDate(DateTime date) |
|||
{ |
|||
_dates.Add(date.ToString("O")); |
|||
} |
|||
|
|||
public void AddDate(DateTimeOffset date) |
|||
{ |
|||
_dates.Add(date.ToString("O")); |
|||
} |
|||
|
|||
public void AddDate(DateTime? date) |
|||
{ |
|||
if (date.HasValue) |
|||
{ |
|||
_dates.Add(date.Value.ToString("O")); |
|||
} |
|||
} |
|||
|
|||
public void AddDate(DateTimeOffset? date) |
|||
{ |
|||
if (date.HasValue) |
|||
{ |
|||
_dates.Add(date.Value.ToString("O")); |
|||
} |
|||
} |
|||
|
|||
public void AddDate(string date, string format) |
|||
{ |
|||
_dates.Add(DateTime.ParseExact(date, format, null).ToString("O")); |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using JetBrains.Annotations; |
|||
using Microsoft.AspNetCore.Mvc.ViewFeatures; |
|||
using Microsoft.AspNetCore.Razor.TagHelpers; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
[HtmlTargetElement("abp-date-picker", TagStructure = TagStructure.NormalOrSelfClosing)] |
|||
public class AbpDatePickerTagHelper : AbpDatePickerBaseTagHelper<AbpDatePickerTagHelper> |
|||
{ |
|||
[CanBeNull] |
|||
public ModelExpression AspFor { get; set; } |
|||
|
|||
public AbpDatePickerTagHelper(AbpDatePickerTagHelperService service) : base(service) |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
using System; |
|||
using System.Text.Encodings.Web; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Localization.Resources.AbpUi; |
|||
using Microsoft.AspNetCore.Mvc.TagHelpers; |
|||
using Microsoft.AspNetCore.Mvc.ViewFeatures; |
|||
using Microsoft.AspNetCore.Razor.TagHelpers; |
|||
using Microsoft.Extensions.Localization; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
public class AbpDatePickerTagHelperService : AbpDatePickerBaseTagHelperService<AbpDatePickerTagHelper> |
|||
{ |
|||
public AbpDatePickerTagHelperService(IJsonSerializer jsonSerializer, IHtmlGenerator generator, HtmlEncoder encoder, IServiceProvider serviceProvider, IStringLocalizer<AbpUiResource> l, IAbpTagHelperLocalizer tagHelperLocalizer) : base(jsonSerializer, generator, encoder, serviceProvider, l, tagHelperLocalizer) |
|||
{ |
|||
|
|||
} |
|||
|
|||
protected override TagHelperOutput TagHelperOutput { get; set; } |
|||
|
|||
[CanBeNull] |
|||
protected virtual InputTagHelper DateTagHelper { get; set; } |
|||
|
|||
[CanBeNull] |
|||
protected virtual TagHelperOutput DateTagHelperOutput { get; set; } |
|||
protected override string GetPropertyName() |
|||
{ |
|||
return TagHelper.AspFor?.Name ?? string.Empty; |
|||
} |
|||
|
|||
protected override T GetAttributeAndModelExpression<T>(out ModelExpression modelExpression) |
|||
{ |
|||
modelExpression = TagHelper.AspFor; |
|||
return modelExpression?.ModelExplorer.GetAttribute<T>(); |
|||
} |
|||
|
|||
public async override Task ProcessAsync(TagHelperContext context, TagHelperOutput output) |
|||
{ |
|||
if (TagHelper.AspFor != null) |
|||
{ |
|||
DateTagHelper = new InputTagHelper(Generator) |
|||
{ |
|||
InputTypeName = "hidden", |
|||
ViewContext = TagHelper.ViewContext, |
|||
For = TagHelper.AspFor, |
|||
}; |
|||
|
|||
var attributes = new TagHelperAttributeList { { "data-date", "true" }, { "type", "hidden" } }; |
|||
DateTagHelperOutput = await DateTagHelper.ProcessAndGetOutputAsync(attributes, context, "input"); |
|||
} |
|||
|
|||
await base.ProcessAsync(context, output); |
|||
} |
|||
|
|||
|
|||
protected override int GetOrder() |
|||
{ |
|||
return TagHelper.AspFor?.Metadata.Order ?? 0; |
|||
} |
|||
|
|||
protected override void AddBaseTagAttributes(TagHelperAttributeList attributes) |
|||
{ |
|||
if (TagHelper.AspFor != null && |
|||
TagHelper.AspFor.Model != null && |
|||
SupportedInputTypes.TryGetValue(TagHelper.AspFor.Metadata.ModelType, out var convertFunc)) |
|||
{ |
|||
attributes.Add("data-date", convertFunc(TagHelper.AspFor.Model)); |
|||
} |
|||
} |
|||
|
|||
protected override ModelExpression GetModelExpression() |
|||
{ |
|||
return TagHelper.AspFor; |
|||
} |
|||
|
|||
protected async override Task<string> GetValidationAsHtmlAsync(TagHelperContext context, TagHelperOutput output) |
|||
{ |
|||
return DateTagHelper != null ? await GetValidationAsHtmlByInputAsync(context, output, DateTagHelper) : string.Empty; |
|||
} |
|||
|
|||
protected override string GetExtraInputHtml(TagHelperContext context, TagHelperOutput output) |
|||
{ |
|||
return DateTagHelperOutput?.Render(Encoder); |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
public enum AbpDatePickerWeekNumbers : byte |
|||
{ |
|||
None = 0, |
|||
Normal = 1, |
|||
Iso = 2 |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using JetBrains.Annotations; |
|||
using Microsoft.AspNetCore.Mvc.ViewFeatures; |
|||
using Microsoft.AspNetCore.Razor.TagHelpers; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
[HtmlTargetElement("abp-date-range-picker", TagStructure = TagStructure.NormalOrSelfClosing)] |
|||
public class AbpDateRangePickerTagHelper : AbpDatePickerBaseTagHelper<AbpDateRangePickerTagHelper> |
|||
{ |
|||
[CanBeNull] |
|||
public ModelExpression AspForStart { get; set; } |
|||
|
|||
[CanBeNull] |
|||
public ModelExpression AspForEnd { get; set; } |
|||
|
|||
public AbpDateRangePickerTagHelper(AbpDateRangePickerTagHelperService tagHelperService) : |
|||
base(tagHelperService) |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,132 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Text.Encodings.Web; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Localization.Resources.AbpUi; |
|||
using Microsoft.AspNetCore.Mvc.TagHelpers; |
|||
using Microsoft.AspNetCore.Mvc.ViewFeatures; |
|||
using Microsoft.AspNetCore.Razor.TagHelpers; |
|||
using Microsoft.Extensions.Localization; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
public class AbpDateRangePickerTagHelperService : AbpDatePickerBaseTagHelperService<AbpDateRangePickerTagHelper> |
|||
{ |
|||
public AbpDateRangePickerTagHelperService(IJsonSerializer jsonSerializer, IHtmlGenerator generator, |
|||
HtmlEncoder encoder, IServiceProvider serviceProvider, IStringLocalizer<AbpUiResource> l, |
|||
IAbpTagHelperLocalizer tagHelperLocalizer) : |
|||
base(jsonSerializer, generator, encoder, serviceProvider, l, |
|||
tagHelperLocalizer) |
|||
{ |
|||
} |
|||
|
|||
protected override string TagName { get; set; } = "abp-date-range-picker"; |
|||
|
|||
protected override T GetAttributeAndModelExpression<T>(out ModelExpression modelExpression) |
|||
{ |
|||
modelExpression = new[] { TagHelper.AspForStart, TagHelper.AspForEnd }.FirstOrDefault(x => x?.ModelExplorer?.GetAttribute<T>() != null); |
|||
return modelExpression?.ModelExplorer.GetAttribute<T>(); |
|||
} |
|||
|
|||
public async override Task ProcessAsync(TagHelperContext context, TagHelperOutput output) |
|||
{ |
|||
if (TagHelper.AspForStart != null) |
|||
{ |
|||
var startDateAttributes = new TagHelperAttributeList { { "data-start-date", "true" }, { "type", "hidden" } }; |
|||
StartDateTagHelper = new InputTagHelper(Generator) |
|||
{ |
|||
ViewContext = TagHelper.ViewContext, |
|||
For = TagHelper.AspForStart, |
|||
InputTypeName = "hidden" |
|||
}; |
|||
|
|||
StartDateTagHelperOutput = await StartDateTagHelper.ProcessAndGetOutputAsync(startDateAttributes, context, "input"); |
|||
} |
|||
|
|||
if (TagHelper.AspForEnd != null) |
|||
{ |
|||
var endDateAttributes = new TagHelperAttributeList { { "data-end-date", "true" }, { "type", "hidden" } }; |
|||
EndDateTagHelper = new InputTagHelper(Generator) |
|||
{ |
|||
ViewContext = TagHelper.ViewContext, |
|||
For = TagHelper.AspForEnd, |
|||
InputTypeName = "hidden" |
|||
}; |
|||
|
|||
EndDateTagHelperOutput = await EndDateTagHelper.ProcessAndGetOutputAsync(endDateAttributes, context, "input"); |
|||
} |
|||
|
|||
await base.ProcessAsync(context, output); |
|||
} |
|||
|
|||
protected override TagHelperOutput TagHelperOutput { get; set; } |
|||
|
|||
[CanBeNull] |
|||
protected virtual InputTagHelper StartDateTagHelper { get; set; } |
|||
|
|||
[CanBeNull] |
|||
protected virtual TagHelperOutput StartDateTagHelperOutput { get; set; } |
|||
|
|||
[CanBeNull] |
|||
protected virtual InputTagHelper EndDateTagHelper { get; set; } |
|||
|
|||
[CanBeNull] |
|||
protected virtual TagHelperOutput EndDateTagHelperOutput { get; set; } |
|||
|
|||
protected override string GetPropertyName() |
|||
{ |
|||
return TagHelper.AspForStart?.Name ?? string.Empty; |
|||
} |
|||
|
|||
protected override int GetOrder() |
|||
{ |
|||
return TagHelper.Order; |
|||
} |
|||
|
|||
protected override void AddBaseTagAttributes(TagHelperAttributeList attributes) |
|||
{ |
|||
if (TagHelper.AspForStart != null && |
|||
TagHelper.AspForStart.Model != null && |
|||
SupportedInputTypes.TryGetValue(TagHelper.AspForStart.Metadata.ModelType, out var convertFuncStart)) |
|||
{ |
|||
attributes.Add("data-start-date", convertFuncStart(TagHelper.AspForStart.Model)); |
|||
} |
|||
|
|||
if (TagHelper.AspForEnd != null && |
|||
TagHelper.AspForEnd.Model != null && |
|||
SupportedInputTypes.TryGetValue(TagHelper.AspForEnd.Metadata.ModelType, out var convertFuncEnd)) |
|||
{ |
|||
attributes.Add("data-end-date", convertFuncEnd(TagHelper.AspForEnd.Model)); |
|||
} |
|||
} |
|||
|
|||
protected override string GetExtraInputHtml(TagHelperContext context, TagHelperOutput output) |
|||
{ |
|||
return StartDateTagHelperOutput?.Render(Encoder) + EndDateTagHelperOutput?.Render(Encoder); |
|||
} |
|||
|
|||
protected override ModelExpression GetModelExpression() |
|||
{ |
|||
return TagHelper.AspForStart; |
|||
} |
|||
|
|||
protected async override Task<string> GetValidationAsHtmlAsync(TagHelperContext context, TagHelperOutput output) |
|||
{ |
|||
var validationHtml = string.Empty; |
|||
|
|||
if (StartDateTagHelper != null) |
|||
{ |
|||
validationHtml += await GetValidationAsHtmlByInputAsync(context, output, StartDateTagHelper); |
|||
} |
|||
|
|||
if (EndDateTagHelper != null) |
|||
{ |
|||
validationHtml += await GetValidationAsHtmlByInputAsync(context, output, EndDateTagHelper); |
|||
} |
|||
|
|||
return validationHtml; |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
[AttributeUsage(AttributeTargets.Property)] |
|||
public class DatePickerAttribute : Attribute |
|||
{ |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using Microsoft.AspNetCore.Mvc.ViewFeatures; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
[AttributeUsage(AttributeTargets.Property)] |
|||
public class DatePickerOptionsAttribute : Attribute |
|||
{ |
|||
public string DatePickerOptionsPropertyName { get; set; } |
|||
|
|||
public DatePickerOptionsAttribute(string datePickerOptionsPropertyName) |
|||
{ |
|||
DatePickerOptionsPropertyName = datePickerOptionsPropertyName; |
|||
} |
|||
|
|||
public IAbpDatePickerOptions GetDatePickerOptions(ModelExplorer explorer) |
|||
{ |
|||
var properties = explorer.Container.Properties.Where(p => p.Metadata.PropertyName != null && p.Metadata.PropertyName.Equals(DatePickerOptionsPropertyName)).ToList(); |
|||
|
|||
while (properties.Count == 0) |
|||
{ |
|||
explorer = explorer.Container; |
|||
if(explorer.Container == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
properties = explorer.Container.Properties.Where(p => p.Metadata.PropertyName != null && p.Metadata.PropertyName.Equals(DatePickerOptionsPropertyName)).ToList(); |
|||
} |
|||
|
|||
return properties.FirstOrDefault(p=> p.Model is IAbpDatePickerOptions)?.Model as IAbpDatePickerOptions; |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
[AttributeUsage(AttributeTargets.Property)] |
|||
public class DateRangePickerAttribute : Attribute |
|||
{ |
|||
public string PickerId { get; set; } |
|||
|
|||
public bool IsStart { get; set; } |
|||
|
|||
public bool IsEnd => !IsStart; |
|||
|
|||
public DateRangePickerAttribute(string pickerId, bool isStart = false) |
|||
{ |
|||
PickerId = pickerId; |
|||
IsStart = isStart; |
|||
} |
|||
} |
|||
@ -0,0 +1,160 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; |
|||
|
|||
public interface IAbpDatePickerOptions |
|||
{ |
|||
public string PickerId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Min date allowed
|
|||
/// </summary>
|
|||
DateTime? MinDate { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Max date allowed
|
|||
/// </summary>
|
|||
DateTime? MaxDate { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The maximum span between the selected start and end dates. Can have any property you can add to a moment object (i.e. days, months)
|
|||
/// </summary>
|
|||
object MaxSpan { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Show year and month select boxes above calendars to jump to a specific month and year.
|
|||
/// </summary>
|
|||
bool? ShowDropdowns { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The minimum year shown in the dropdowns when showDropdowns is set to true.
|
|||
/// </summary>
|
|||
int? MinYear { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The maximum year shown in the dropdowns when showDropdowns is set to true.
|
|||
/// </summary>
|
|||
int? MaxYear { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Show week numbers at the start of each week on the calendars.
|
|||
/// </summary>
|
|||
AbpDatePickerWeekNumbers WeekNumbers { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Adds select boxes to choose times in addition to dates.
|
|||
/// </summary>
|
|||
bool? TimePicker { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Increment of the minutes selection list for times (i.e. 30 to allow only selection of times ending in 0 or 30).
|
|||
/// </summary>
|
|||
int? TimePickerIncrement { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Use 24-hour instead of 12-hour times, removing the AM/PM selection.
|
|||
/// </summary>
|
|||
bool? TimePicker24Hour { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Show seconds in the timePicker.
|
|||
/// </summary>
|
|||
bool? TimePickerSeconds { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Set predefined date ranges the user can select from. Each key is the label for the range, and its value an array with two dates representing the bounds of the range.
|
|||
/// </summary>
|
|||
List<AbpDatePickerRange> Ranges { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Displays "Custom Range" at the end of the list of predefined ranges, when the ranges option is used. This option will be highlighted whenever the current date range selection does not match one of the predefined ranges. Clicking it will display the calendars to select a new range.
|
|||
/// </summary>
|
|||
bool? ShowCustomRangeLabel { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Normally, if you use the ranges option to specify pre-defined date ranges, calendars for choosing a custom date range are not shown until the user clicks "Custom Range". When this option is set to true, the calendars for choosing a custom date range are always shown instead.
|
|||
/// </summary>
|
|||
bool? AlwaysShowCalendars { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Whether the picker appears aligned to the left, to the right, or centered under the HTML element it's attached to.
|
|||
/// </summary>
|
|||
AbpDatePickerOpens Opens { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Whether the picker appears below (default) or above the HTML element it's attached to.
|
|||
/// </summary>
|
|||
AbpDatePickerDrops Drops { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// CSS class names that will be added to both the today, clear, and apply buttons.
|
|||
/// </summary>
|
|||
[CanBeNull] |
|||
string ButtonClasses { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// CSS class names that will be added only to the today button.
|
|||
/// </summary>
|
|||
[CanBeNull] |
|||
string TodayButtonClasses { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// CSS class names that will be added only to the apply button.
|
|||
/// </summary>
|
|||
[CanBeNull] |
|||
string ApplyButtonClasses { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// CSS class names that will be added only to the clear button.
|
|||
/// </summary>
|
|||
[CanBeNull] |
|||
string ClearButtonClasses { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Allows you to provide localized strings for buttons and labels, customize the date format, and change the first day of week for the calendars.
|
|||
/// </summary>
|
|||
[CanBeNull] |
|||
object Locale { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Hide the apply button, and automatically apply a new date range as soon as two dates are clicked.
|
|||
/// </summary>
|
|||
bool? AutoApply { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// When enabled, the two calendars displayed will always be for two sequential months (i.e. January and February), and both will be advanced when clicking the left or right arrows above the calendars. When disabled, the two calendars can be individually advanced and display any month/year.
|
|||
/// </summary>
|
|||
bool? LinkedCalendars { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Indicates whether the date range picker should automatically update the value of the input element it's attached to at initialization and when the selected dates change.
|
|||
/// </summary>
|
|||
bool? AutoUpdateInput { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// jQuery selector of the parent element that the date range picker will be added to, if not provided this will be 'body'
|
|||
/// </summary>
|
|||
[CanBeNull] |
|||
string ParentEl { get; set; } |
|||
|
|||
[CanBeNull] |
|||
string DateFormat { get; set; } |
|||
|
|||
bool OpenButton { get; set; } |
|||
|
|||
bool ClearButton { get; set; } |
|||
|
|||
bool SingleOpenAndClearButton { get; set; } |
|||
|
|||
bool? IsUtc { get; set; } |
|||
|
|||
bool? IsIso { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Other non-mapped attributes will be automatically added to the input element as is.
|
|||
/// </summary>
|
|||
[CanBeNull] |
|||
object Options { get; set; } |
|||
} |
|||
Loading…
Reference in new issue