Browse Source

Merge pull request #15806 from abpframework/salihozkara/datepickertaghelper

Mvc : abpdatepicker and abpdaterangepicker add
pull/16149/head
Yunus Emre Kalkan 3 years ago
committed by GitHub
parent
commit
4729918bb2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 223
      docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md
  2. 82
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs
  3. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs
  4. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs
  5. 215
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelper.cs
  6. 712
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs
  7. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerDrops.cs
  8. 10
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerOpens.cs
  9. 41
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerOptions.cs
  10. 90
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerRange.cs
  11. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelper.cs
  12. 88
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelperService.cs
  13. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerWeekNumbers.cs
  14. 20
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDateRangePickerTagHelper.cs
  15. 132
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDateRangePickerTagHelperService.cs
  16. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/DatePickerAttribute.cs
  17. 34
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/DatePickerOptionsAttribute.cs
  18. 19
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/DateRangePickerAttribute.cs
  19. 160
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/IAbpDatePickerOptions.cs
  20. 562
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js
  21. 4
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json
  22. 4
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json

223
docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md

@ -58,49 +58,49 @@ Model:
### Attributes
You can set some of the attributes on your c# property, or directly on html tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
#### Property Attributes
- `[TextArea()]`: Converts the input into a text area.
* `[Placeholder()]`: Sets placeholder for input. You can use a localization key directly.
* `[InputInfoText()]`: Sets a small info text for input. You can use a localization key directly.
* `[FormControlSize()]`: Sets size of form-control wrapper element. Available values are
* `[Placeholder()]`: Sets the description are of the input. You can use a localization key directly.
* `[InputInfoText()]`: Sets text for the input. You can directly use a localization key.
* `[FormControlSize()]`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
* `[DisabledInput]` : Input is disabled.
* `[ReadOnlyInput]`: Input is read-only.
* `[DisabledInput]` : Sets the input as disabled.
* `[ReadOnlyInput]`: Sets the input as read-only.
#### Tag Attributes
* `info`: Sets a small info text for input. You can use a localization key directly.
* `auto-focus`: If true, browser auto focuses on the element.
* `size`: Sets size of form-control wrapper element. Available values are
* `info`: Sets text for the input. You can directly use a localization key.
* `auto-focus`: It lets the browser set focus to the element when its value is true.
* `size`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
* `disabled`: Input is disabled.
* `readonly`: Input is read-only.
* `label`: Sets the label for input.
* `display-required-symbol`: Adds the required symbol (*) to label if input is required. Default `True`.
* `disabled`: Sets the input as disabled.
* `readonly`: Sets the input as read-only.
* `label`: Sets the label of input.
* `required-symbol`: Adds the required symbol `(*)` to the label when the input is required. The default value is `True`.
`asp-format`, `name` and `value` attributes of [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-input-tag-helper) are also valid for `abp-input` tag helper.
### Label & Localization
You can set label of your input in different ways:
You can set the label of the input in several ways:
- You can use `Label` attribute and directly set the label. But it doesn't auto localize your localization key. So use it as `label="@L["{LocalizationKey}"].Value"`.
- You can set it using `[Display(name="{LocalizationKey}")]` attribute of Asp.Net Core.
- You can use the `Label` attribute to set the label directly. This property does not automatically localize the text. To localize the label, use `label="@L["{LocalizationKey}"].Value"`.
- You can set it using `[Display(name="{LocalizationKey}")]` attribute of ASP.NET Core.
- You can just let **abp** find the localization key for the property. It will try to find "DisplayName:{PropertyName}" or "{PropertyName}" localization keys, if `label` or `[DisplayName]` attributes are not set.
## abp-select
`abp-select` tag creates a Bootstrap form select for a given c# property. It uses [Asp.Net Core Select Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-select-tag-helper) in background, so every data annotation attribute of `select` tag helper of Asp.Net Core is also valid for `abp-select`.
`abp-select` tag creates a Bootstrap form select for a given c# property. It uses [ASP.NET Core Select Tag Helper](https://docs.microsoft.com/tr-tr/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-select-tag-helper) in background, so every data annotation attribute of `select` tag helper of ASP.NET Core is also valid for `abp-select`.
`abp-select` tag needs a list of `Microsoft.AspNetCore.Mvc.Rendering.SelectListItem ` to work. It can be provided by `asp-items` attriube on the tag or `[SelectItems()]` attribute on c# property. (if you are using [abp-dynamic-form](Dynamic-forms.md), c# attribute is the only way.)
@ -170,14 +170,14 @@ Model:
### Attributes
You can set some of the attributes on your c# property, or directly on html tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
#### Property Attributes
* `[SelectItems()]`: Sets the select data. Parameter should be the name of the data list. (see example above)
- `[InputInfoText()]`: Sets a small info text for input. You can use a localization key directly.
- `[FormControlSize()]`: Sets size of form-control wrapper element. Available values are
- `[InputInfoText()]`: Sets text for the input. You can directly use a localization key.
- `[FormControlSize()]`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
@ -186,21 +186,21 @@ You can set some of the attributes on your c# property, or directly on html tag.
#### Tag Attributes
- `asp-items`: Sets the select data. This Should be a list of SelectListItem.
- `info`: Sets a small info text for input. You can use a localization key directly.
- `size`: Sets size of form-control wrapper element. Available values are
- `info`: Sets text for the input. You can directly use a localization key.
- `size`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
- `label`: Sets the label for input.
- `display-required-symbol`: Adds the required symbol (*) to label if input is required. Default `True`.
- `label`: Sets the label of input.
- `required-symbol`: Adds the required symbol `(*)` to the label when the input is required. The default value is `True`.
### Label & Localization
You can set label of your input in different ways:
You can set the label of the input in several ways:
- You can use `Label` attribute and directly set the label. But it doesn't auto localize your localization key. So use it as `label="@L["{LocalizationKey}"].Value".`
- You can set it using `[Display(name="{LocalizationKey}")]` attribute of Asp.Net Core.
- You can set it using `[Display(name="{LocalizationKey}")]` attribute of ASP.NET Core.
- You can just let **abp** find the localization key for the property. It will try to find "DisplayName:{PropertyName}" or "{PropertyName}" localization keys.
Localizations of combobox values are set by `abp-select` for **Enum** property. It searches for "{EnumTypeName}.{EnumPropertyName}" or "{EnumPropertyName}" localization keys. For instance, in the example above, it will use "CarType.StationWagon" or "StationWagon" keys for localization when it localizes combobox values.
@ -251,7 +251,7 @@ Model:
### Attributes
You can set some of the attributes on your c# property, or directly on html tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
#### Property Attributes
@ -261,3 +261,172 @@ You can set some of the attributes on your c# property, or directly on html tag.
- `asp-items`: Sets the select data. This Should be a list of SelectListItem.
- `Inline`: If true, radio buttons will be in single line, next to each other. If false, they will be under each other.
## abp-date-picker & abp-date-range-picker
`abp-date-picker` and `abp-date-range-picker` tags creates a Bootstrap form date picker for a given c# property. `abp-date-picker` is for single date selection, `abp-date-range-picker` is for date range selection. It uses [datepicker](https://www.daterangepicker.com/) jQuery plugin.
Usage:
````xml
<abp-date-picker asp-for="@Model.MyModel.MyDate" />
<abp-date-range-picker asp-for-start="@Model.MyModel.MyDateRangeStart" asp-for-end="@Model.MyModel.MyDateRangeEnd" />
<abp-dynamic-form abp-model="DynamicFormExample"></abp-dynamic-form>
````
Model:
````csharp
public class FormElementsModel : PageModel
{
public SampleModel MyModel { get; set; }
public DynamicForm DynamicFormExample { get; set; }
public void OnGet()
{
MyModel = new SampleModel();
DynamicFormExample = new DynamicForm();
}
public class SampleModel
{
public DateTime MyDate { get; set; }
public DateTime MyDateRangeStart { get; set; }
public DateTime MyDateRangeEnd { get; set; }
}
public class DynamicForm
{
[DateRangePicker("MyPicker",true)]
public DateTime StartDate { get; set; }
[DateRangePicker("MyPicker",false)]
[DatePickerOptions(nameof(DatePickerOptions))]
public DateTime EndDate { get; set; }
public DateTime DateTime { get; set; }
public DynamicForm()
{
StartDate = DateTime.Now;
EndDate = DateTime.Now;
DateTime = DateTime.Now;
}
}
public AbpDatePickerOptions DatePickerOptions { get; set; }
}
````
### Attributes
You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
#### Property Attributes
* `[Placeholder()]`: Sets the description are of the input. You can use a localization key directly.
* `[InputInfoText()]`: Sets text for the input. You can directly use a localization key.
* `[FormControlSize()]`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
* `[DisabledInput]` : Sets the input as disabled.
* `[ReadOnlyInput]`: Sets the input as read-only.
- `[DatePickerOptions()]`: Sets the predefined options of the date picker. Parameter should be the name of the options property (see example above). See the available [datepicker options](https://www.daterangepicker.com/#options). You can use a localization key directly.
##### abp-date-picker
`[DatePicker]` : Sets the input as datepicker. Especially for string properties.
##### abp-date-range-picker
`[DateRangePicker()]` : Sets the picker id for the date range picker. You can set the property as a start date by setting IsStart=true or leave it as default/false to set it as an end date.
#### Tag Attributes
* `info`: Sets text for the input. You can directly use a localization key.
* `auto-focus`: It lets the browser set focus to the element when its value is true.
* `size`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
* `disabled`: Sets the input as disabled.
* `readonly`: Sets the input as read-only.
* `label`: Sets the label of input.
* `required-symbol`: Adds the required symbol `(*)` to the label when the input is required. The default value is `True`.
* `open-button`: A button to open the datepicker will be added when its `True`. The default value is `True`.
* `clear-button`: A button to clear the datepicker will be added when its `True`. The default value is `True`.
* `single-open-and-clear-button`: Shows the open and clear buttons in a single button when it's `True`. The default value is `True`.
* `is-utc`: Converts the date to UTC when its `True`. The default value is `False`.
* `is-iso`: Converts the date to ISO format when its `True`. The default value is `False`.
* `date-format`: Sets the date format of the input. The default format is the user's culture date format. You need to provide a JavaScript date format convention. Eg: `YYYY-MM-DDTHH:MM:SSZ`.
* `date-separator`: Sets a character to separate start and end dates. The default value is `-`
* Other non-mapped attributes will be automatically added to the input element as is. See the available [datepicker options](https://www.daterangepicker.com/#options). Eg: `data-start-date="2020-01-01"`
##### abp-date-picker
* `asp-date`: Sets the date value. This should be a `DateTime`, `DateTime?`, `DateTimeOffset`, `DateTimeOffset?` or `string` value.
##### abp-date-range-picker
* `asp-for-start`: Sets the start date value. This should be a `DateTime`, `DateTime?`, `DateTimeOffset`, `DateTimeOffset?` or `string` value.
* `asp-for-end`: Sets the end date value. This should be a `DateTime`, `DateTime?`, `DateTimeOffset`, `DateTimeOffset?` or `string` value.
### Label & Localization
You can set the label of the input in several ways:
- You can use the `Label` attribute to set the label directly. This property does not automatically localize the text. To localize the label, use `label="@L["{LocalizationKey}"].Value"`.
- You can set it using `[Display(name="{LocalizationKey}")]` attribute ASP.NET Core.
- You can just let **abp** find the localization key for the property. If the `label` or `[DisplayName]` attributes are not set, it tries to find the localization by convention. For example `DisplayName:{YourPropertyName}` or `{YourPropertyName}`. If your property name is FullName, it will search for `DisplayName:FullName` or `{FullName}`.
### JavaScript Usage
````javascript
var newPicker = abp.libs.bootstrapDateRangePicker.createDateRangePicker(
{
label: "New Picker",
}
);
newPicker.insertAfter($('body'));
````
````javascript
var newPicker = abp.libs.bootstrapDateRangePicker.createSinglePicker(
{
label: "New Picker",
}
);
newPicker.insertAfter($('body'));
````
#### Options
* `label`: Sets the label of the input.
* `placeholder`: Sets the placeholder of the input.
* `value`: Sets the value of the input.
* `name`: Sets the name of the input.
* `id`: Sets the id of the input.
* `required`: Sets the input as required.
* `disabled`: Sets the input as disabled.
* `readonly`: Sets the input as read-only.
* `size`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
* `openButton`: A button to open the datepicker will be added when its `True`. The default value is `True`.
* `clearButton`: A button to clear the datepicker will be added when its `True`. The default value is `True`.
* `singleOpenAndClearButton`: Shows the open and clear buttons in a single button when it's `True`. The default value is `True`.
* `isUtc`: Converts the date to UTC when its `True`. The default value is `False`.
* `isIso`: Converts the date to ISO format when its `True`. The default value is `False`.
* `dateFormat`: Sets the date format of the input. The default format is the user's culture date format. You need to provide a JavaScript date format convention. Eg: `YYYY-MM-DDTHH:MM:SSZ`.
* `dateSeparator`: Sets a character to separate start and end dates. The default value is `-`.
* `startDateName`: Sets the name of the hidden start date input.
* `endDateName`: Sets the name of the hidden end date input.
* `dateName`: Sets the name of the hidden date input.
* Other [datepicker options](https://www.daterangepicker.com/#options). Eg: `startDate: "2020-01-01"`.

82
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs

@ -14,6 +14,7 @@ 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.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
@ -144,6 +145,10 @@ public class AbpDynamicFormTagHelperService : AbpTagHelperService<AbpDynamicForm
{
await ProcessSelectGroupAsync(context, output, model);
}
else if (IsDateGroup(context, model))
{
await ProcessDateGroupAsync(context, output, model);
}
else
{
await ProcessInputGroupAsync(context, output, model);
@ -151,6 +156,64 @@ public class AbpDynamicFormTagHelperService : AbpTagHelperService<AbpDynamicForm
}
}
private async Task ProcessDateGroupAsync(TagHelperContext context, TagHelperOutput output, ModelExpression model)
{
var abpDateTagHelper = GetDateGroupTagHelper(context, output, model);
await abpDateTagHelper.RenderAsync(new TagHelperAttributeList(), context, _htmlEncoder, "div", TagMode.StartTagAndEndTag);
}
private AbpTagHelper GetDateGroupTagHelper(TagHelperContext context, TagHelperOutput output, ModelExpression model)
{
return IsDateRangeGroup(model.ModelExplorer)
? GetAbpDateRangeInputTagHelper(context, output, model)
: GetAbpDateInputTagHelper(model);
}
private AbpTagHelper GetAbpDateInputTagHelper(ModelExpression model)
{
var abpDateInputTagHelper = _serviceProvider.GetRequiredService<AbpDatePickerTagHelper>();
abpDateInputTagHelper.AspFor = model;
abpDateInputTagHelper.ViewContext = TagHelper.ViewContext;
abpDateInputTagHelper.DisplayRequiredSymbol = TagHelper.RequiredSymbols ?? true;
return abpDateInputTagHelper;
}
private AbpTagHelper GetAbpDateRangeInputTagHelper(TagHelperContext context, TagHelperOutput output, ModelExpression model)
{
var modelAttribute = model.ModelExplorer.GetAttribute<DateRangePickerAttribute>();
var pickerId = modelAttribute.PickerId;
var abpDateRangeInputTagHelper = _serviceProvider.GetRequiredService<AbpDateRangePickerTagHelper>();
abpDateRangeInputTagHelper.PickerId = pickerId;
abpDateRangeInputTagHelper.ViewContext = TagHelper.ViewContext;
if (modelAttribute.IsStart)
{
abpDateRangeInputTagHelper.AspForStart = model;
var otherModelExists = TryToGetOtherDateModel(model, pickerId, out var otherModel);
if (otherModelExists && otherModel.GetAttribute<DateRangePickerAttribute>().IsEnd)
{
abpDateRangeInputTagHelper.AspForEnd = ModelExplorerToModelExpressionConverter(otherModel);
}
}
return abpDateRangeInputTagHelper;
}
private bool TryToGetOtherDateModel(ModelExpression model, string pickerId, out ModelExplorer otherModel)
{
otherModel = TagHelper.Model.ModelExplorer.Properties.SingleOrDefault(x => x != model.ModelExplorer && x.GetAttribute<DateRangePickerAttribute>()?.PickerId == pickerId);
return otherModel != null;
}
private static bool IsDateRangeGroup(ModelExplorer modelModelExplorer)
{
return modelModelExplorer.GetAttribute<DateRangePickerAttribute>() != null;
}
protected virtual void RemoveFormGroupItemsNotInModel(TagHelperContext context, TagHelperOutput output, List<FormGroupItem> items)
{
var models = GetModels(context, output);
@ -294,6 +357,25 @@ public class AbpDynamicFormTagHelperService : AbpTagHelperService<AbpDynamicForm
return IsEnum(model.ModelExplorer) || AreSelectItemsProvided(model.ModelExplorer);
}
protected virtual bool IsDateGroup(TagHelperContext context, ModelExpression model)
{
if (model.ModelExplorer.GetAttribute<DatePickerAttribute>() != null ||
model.ModelExplorer.GetAttribute<DateRangePickerAttribute>() != null)
{
return true;
}
if (model.Metadata.ModelType == typeof(DateTime) ||
model.Metadata.ModelType == typeof(DateTime?) ||
model.Metadata.ModelType == typeof(DateTimeOffset) ||
model.Metadata.ModelType == typeof(DateTimeOffset?))
{
return true;
}
return false;
}
protected virtual bool IsEnum(ModelExplorer explorer)
{
return explorer.Metadata.IsEnum;

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs

@ -294,8 +294,10 @@ public class AbpInputTagHelperService : AbpTagHelperService<AbpInputTagHelper>
{
return "";
}
var isHaveRequiredAttribute = context.AllAttributes.Any(a => a.Name == "required");
return TagHelper.AspFor.ModelExplorer.GetAttribute<RequiredAttribute>() != null ? "<span> * </span>" : "";
return TagHelper.AspFor.ModelExplorer.GetAttribute<RequiredAttribute>() != null || isHaveRequiredAttribute ? "<span> * </span>" : "";
}
protected virtual string GetInfoAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag, bool isCheckbox)

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs

@ -197,8 +197,10 @@ public class AbpSelectTagHelperService : AbpTagHelperService<AbpSelectTagHelper>
{
return "";
}
var isHaveRequiredAttribute = context.AllAttributes.Any(a => a.Name == "required");
return TagHelper.AspFor.ModelExplorer.GetAttribute<RequiredAttribute>() != null ? "<span> * </span>" : "";
return TagHelper.AspFor.ModelExplorer.GetAttribute<RequiredAttribute>() != null || isHaveRequiredAttribute ? "<span> * </span>" : "";
}
protected virtual void AddInfoTextId(TagHelperOutput inputTagHelperOutput)

215
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelper.cs

@ -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;
}
}

712
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs

@ -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);
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerDrops.cs

@ -0,0 +1,8 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
public enum AbpDatePickerDrops : byte
{
Down = 1,
Up = 2,
Auto = 3
}

10
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerOpens.cs

@ -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,
}

41
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerOptions.cs

@ -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; }
}

90
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerRange.cs

@ -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"));
}
}

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelper.cs

@ -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)
{
}
}

88
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelperService.cs

@ -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);
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerWeekNumbers.cs

@ -0,0 +1,8 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
public enum AbpDatePickerWeekNumbers : byte
{
None = 0,
Normal = 1,
Iso = 2
}

20
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDateRangePickerTagHelper.cs

@ -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)
{
}
}

132
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDateRangePickerTagHelperService.cs

@ -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;
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/DatePickerAttribute.cs

@ -0,0 +1,8 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
[AttributeUsage(AttributeTargets.Property)]
public class DatePickerAttribute : Attribute
{
}

34
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/DatePickerOptionsAttribute.cs

@ -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;
}
}

19
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/DateRangePickerAttribute.cs

@ -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;
}
}

160
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/IAbpDatePickerOptions.cs

@ -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; }
}

562
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js

@ -193,6 +193,567 @@
});
}
abp.libs.bootstrapDateRangePicker = {
packageName: "bootstrap-daterangepicker",
createDateRangePicker: function (options) {
options = options || {};
options.singleDatePicker = false;
return this.createDatePicker(options);
},
createSinglePicker: function (options) {
options = options || {};
options.singleDatePicker = true;
return this.createDatePicker(options);
},
createDatePicker: function (options) {
var $container = $('<div class="mb-3"></div>')
var label = $('<label class="form-label"></label>')
if (options.label) {
label.text(options.label)
}
$container.append(label)
var $datePicker = options.singleDatePicker ? $('<abp-date-picker></abp-date-picker>') : $('<abp-date-range-picker></abp-date-range-picker>');
$container.append($datePicker)
var $inputGroup = $('<div class="input-group"></div>');
var $dateInput = $('<input type="text" autocomplete="off" class="form-control" />');
if (options.placeholder) {
$dateInput.attr('placeholder', options.placeholder)
}
if (options.value) {
$dateInput.val(options.value)
}
if (options.name) {
$dateInput.attr('name', options.name)
}
if (options.id) {
$dateInput.attr('id', options.id)
}
if (options.required) {
$dateInput.attr('required', true)
}
if (options.disabled) {
$dateInput.attr('disabled', true)
}
if (options.readonly) {
$dateInput.attr('readonly', true)
}
if(options.placeholder) {
$dateInput.attr('placeholder', options.placeholder)
}
if (options.size) {
switch (options.size) {
case 'Small':
$dateInput.addClass('form-control-sm')
break;
case 'Medium':
$dateInput.addClass('form-control-md')
break;
case 'Large':
$dateInput.addClass('form-control-lg')
break;
default:
break;
}
}
$inputGroup.append($dateInput);
if (options.openButton !== false) {
var $openButton = $('<button type="button" class="btn btn-outline-secondary" tabindex="-1" data-type="open"><i class="fa fa-calendar"></i></button>');
$inputGroup.append($openButton);
if(options.disabled) {
$openButton.attr('disabled', 'disabled')
}
}
if (options.clearButton !== false) {
var $clearButton = $('<button type="button" class="btn btn-outline-secondary" tabindex="-1" data-type="clear"><i class="fa fa-times"></i></button>');
$inputGroup.append($clearButton);
if(options.disabled) {
$clearButton.attr('disabled', 'disabled')
}
}
$datePicker.append($inputGroup);
if (options.startDateName) {
var $hiddenStartDateElement = $('<input type="hidden" data-start-date="true" name="' + options.startDateName + '" />');
$datePicker.append($hiddenStartDateElement);
}
if (options.endDateName) {
var $hiddenEndDateElement = $('<input type="hidden" data-end-date="true" name="' + options.endDateName + '" />');
$datePicker.append($hiddenEndDateElement);
}
if (options.dateName) {
var $hiddenDateElement = $('<input type="hidden" data-date="true" name="' + options.dateName + '" />');
$datePicker.append($hiddenDateElement);
}
$datePicker.data('options', options);
abp.dom.initializers.initializeDateRangePickers($datePicker);
$container[0].datePicker = $datePicker[0];
return $container;
}
};
abp.dom.initializers.initializeDateRangePickers = function ($rootElement) {
function setOptions (options, $datePickerElement, singleDatePicker) {
options.singleDatePicker = singleDatePicker;
var defaultOptions = {
showDropdowns: true,
opens: "center",
drops: "down",
autoApply: true,
autoUpdateInput: false,
showTodayButton: true,
showClearButton: true,
minYear: parseInt(moment().subtract(100, 'year').locale('en').format('YYYY')),
maxYear: parseInt(moment().add(100, 'year').locale('en').format('YYYY')),
locale: {
direction: abp.localization.currentCulture.isRightToLeft ? 'rtl' : 'ltr',
todayLabel: abp.localization.localize('Today', 'AbpUi'),
clearLabel: abp.localization.localize('Clear', 'AbpUi'),
applyLabel: abp.localization.localize('Apply', 'AbpUi'),
},
singleOpenAndClearButton: true
};
var locale = defaultOptions.locale;
$.extend(options, defaultOptions);
$.extend(options, $datePickerElement.data());
if ($.isEmptyObject(options.locale)) {
options.locale = locale;
} else {
locale = options.locale;
}
$.extend(options, $datePickerElement.data("options"));
if ($.isEmptyObject(options.locale)) {
options.locale = locale;
}
if (options.timePicker && options.timePicker24Hour === undefined) {
options.timePicker24Hour = moment.localeData().longDateFormat('LT').indexOf('A') < 1;
}
if (options.dateFormat) {
options.locale = options.locale || {};
options.locale.format = options.dateFormat;
}
if (options.separator) {
options.locale = options.locale || {};
options.locale.separator = options.separator;
}
if (options.ranges) {
$.each(options.ranges, function (key, value) {
let start = value[0];
let end;
if (value.length > 1) {
end = value[1];
} else {
end = value[0];
}
options.ranges[key] = [getMoment(start, options), getMoment(end, options)];
});
}
if (typeof options.template !== 'string' && !(options.template instanceof $))
options.template =
'<div class="daterangepicker">' +
'<div class="ranges"></div>' +
'<div class="drp-calendar left">' +
'<div class="calendar-table"></div>' +
'<div class="calendar-time"></div>' +
'</div>' +
'<div class="drp-calendar right">' +
'<div class="calendar-table"></div>' +
'<div class="calendar-time"></div>' +
'</div>' +
'<div class="drp-buttons">' +
'<button class="applyBtn" disabled="disabled" type="button"></button> ' +
'</div>' +
'</div>';
}
function replaceOpenButton (hasDate,singleOpenAndClearButton, $openButton, $clearButton) {
var hiddenClass = 'd-none';
if (singleOpenAndClearButton) {
if (hasDate) {
$openButton.addClass(hiddenClass);
$clearButton.removeClass(hiddenClass);
$clearButton.insertAfter($openButton);
} else {
$openButton.removeClass(hiddenClass);
$clearButton.addClass(hiddenClass);
$openButton.insertAfter($clearButton);
}
}
}
function getMoment (date, options, dateFormat) {
var isUtc = options.isUtc;
dateFormat = dateFormat || options.dateFormat;
if (!date) {
return isUtc ? moment.utc() : moment();
}
if (isUtc) {
return moment.utc(date, dateFormat);
} else {
return moment(date, dateFormat);
}
}
function getStartDate(options, startDate){
startDate = startDate ? startDate : options.startDate;
startDate = startDate ? getMoment(startDate, options) : null;
if (options.singleDatePicker && !startDate) {
startDate = options.date ? getMoment(options.date, options) : null;
}
if(startDate && startDate.isValid()){
return startDate;
}
return null;
}
function getEndDate(options, endDate){
if (options.singleDatePicker) {
return null;
}
endDate = endDate ? endDate : options.endDate;
endDate = endDate ? getMoment(endDate, options) : null;
if(endDate && endDate.isValid()){
return endDate;
}
return null;
}
function getTodayButton(options, $input){
if (options.showTodayButton) {
var $todayBtn = $('<button type="button" class="btn btn-sm today-btn" data-action="today">' + options.locale.todayLabel + '</button>');
if(options.todayButtonClasses){
$todayBtn.addClass(options.todayButtonClasses);
}else{
$todayBtn.addClass('btn-default');
}
$todayBtn.on('click', function () {
var today = getMoment(undefined, options);
$input.data('daterangepicker').setStartDate(today);
$input.data('daterangepicker').setEndDate(today);
$input.data('daterangepicker').clickApply();
});
return $todayBtn;
}
return null;
}
function getClearButton(options, $input, $dateRangePicker){
if (options.showClearButton) {
var $clearBtn = $('<button type="button" class="btn btn-sm clear-btn" data-action="clear">' + options.locale.clearLabel + '</button>');
if(options.clearButtonClasses){
$clearBtn.addClass(options.clearButtonClasses);
}else{
$clearBtn.addClass('btn-default');
}
$clearBtn.on('click', function () {
$input.val('').trigger('change');
$dateRangePicker.hide();
});
return $clearBtn;
}
return null;
}
function addExtraButtons(options, $dateRangePicker, $input) {
var extraButtons = [getTodayButton(options, $input), getClearButton(options, $input, $dateRangePicker)];
if ($dateRangePicker.container.hasClass('auto-apply')) {
var $div = $('<div class="drp-buttons extra-buttons"></div>');
$div.css('display', 'block');
$.each(extraButtons, function (index, value) {
$div.prepend(value);
});
$div.insertAfter($dateRangePicker.container.find('.drp-buttons'));
} else {
$.each(extraButtons, function (index, value) {
$dateRangePicker.container.find('.drp-buttons').prepend(value);
});
}
}
function addOpenButtonClick($openButton, $dateRangePicker){
if(!$openButton){
return;
}
$dateRangePicker.outsideClick = function (e) {
var target = $(e.target);
// if the page is clicked anywhere except within the daterangerpicker/button itself then call this.hide()
if (
// ie modal dialog fix
e.type == "focusin" ||
target.closest(this.element).length ||
target.closest(this.container).length ||
target.closest('.calendar-table').length ||
target.closest($openButton).length
) {
return;
}
this.hide();
this.element.trigger('outsideClick.daterangepicker', this);
};
$openButton.on('click', function () {
$dateRangePicker.toggle();
});
}
function extendDateFormat (format,options) {
if (options.timePicker) {
if (options.timePicker24Hour) {
if (options.timePickerSeconds) {
format += " HH:mm:ss";
} else {
format += " HH:mm";
}
} else {
if (options.timePickerSeconds) {
format += ' ' + " hh:mm:ss A"
} else {
format += " hh:mm A";
}
}
}
return format;
}
function fillInput($input, startDate, endDate, options) {
if (options.singleDatePicker) {
if (startDate) {
$input.val(startDate.format(options.dateFormat));
}
} else {
if (startDate && endDate) {
$input.val(startDate.format(options.dateFormat) + options.separator + endDate.format(options.dateFormat));
}
}
}
$rootElement
.findWithSelf('abp-date-picker,abp-date-range-picker')
.each(function () {
var $this = $(this);
var $input = $this.find('.input-group input[type="text"]')
if ($input.data('daterangepicker')) {
return;
}
var $openButton = $this.find('button[data-type="open"]')
var $clearButton = $this.find('button[data-type="clear"]')
var singleDatePicker = $this.is('abp-date-picker')
var options = {};
setOptions(options, $this, singleDatePicker);
var isIso = options.isIso;
var dateFormat = options.dateFormat;
var separator = options.separator;
var defaultDateFormat = extendDateFormat("YYYY-MM-DD", options);
var singleOpenAndClearButton = options.singleOpenAndClearButton && $clearButton.length > 0 && $openButton.length > 0;
var startDate = getStartDate(options);
var endDate = getEndDate(options);
if (startDate) {
options.startDate = startDate;
}
if (endDate) {
options.endDate = endDate;
}
$input.daterangepicker(options);
var $dateRangePicker = $input.data('daterangepicker');
addExtraButtons(options, $dateRangePicker, $input);
addOpenButtonClick($openButton, $dateRangePicker);
if (!dateFormat) {
dateFormat = extendDateFormat(moment.localeData().longDateFormat('L'), options);
options.dateFormat = dateFormat;
}
if (!separator) {
separator = $dateRangePicker.locale.separator;
options.separator = separator;
}
if(options.autoUpdateInput){
fillInput($input, startDate, endDate, options);
}
$input.on('apply.daterangepicker', function (ev, picker) {
if (singleDatePicker) {
$(this).val(picker.startDate.format(dateFormat));
} else {
$(this).val(picker.startDate.format(dateFormat) + separator + picker.endDate.format(dateFormat));
}
$(this).trigger('change');
});
$input.on('cancel.daterangepicker', function (ev, picker) {
$(this).val('');
$(this).trigger('change');
});
$input.on('show.daterangepicker', function (ev, picker) {
var momentStartDate = getMoment(startDate, options);
var momentEndDate = getMoment(endDate, options);
if (momentStartDate.isValid()) {
picker.setStartDate(momentStartDate);
}
if (momentEndDate.isValid()) {
picker.setEndDate(momentEndDate);
}
});
$clearButton.on('click', function () {
$input.val('');
$input.trigger('change');
});
var firstTrigger = true;
$input.on('change', function () {
if ($(this).val() !== '') {
replaceOpenButton(true, singleOpenAndClearButton, $openButton, $clearButton);
} else {
replaceOpenButton(false, singleOpenAndClearButton, $openButton, $clearButton);
}
var dates = $(this).val().split(separator);
if (dates.length === 2) {
startDate = formatDate(getStartDate(options, dates[0]));
endDate = formatDate(getEndDate(options, dates[1]));
} else {
if (dates[0]) {
startDate = formatDate(getStartDate(options, dates[0]));
}
else {
if(!firstTrigger){
startDate = null;
}
}
if(!firstTrigger){
endDate = null;
}
}
if (!startDate) {
replaceOpenButton(false, singleOpenAndClearButton, $openButton, $clearButton);
$(this).val('');
}
if (!singleDatePicker) {
var input1 = $this.find("input[data-start-date]")
input1.val(startDate);
var input2 = $this.find("input[data-end-date]")
input2.val(endDate);
} else {
var input = $this.find("input[data-date]")
input.val(startDate);
}
if (singleDatePicker) {
$this.data('date', startDate);
$input.data('date', startDate);
} else {
$this.data('startDate', startDate);
$this.data('endDate', endDate);
$input.data('startDate', startDate);
$input.data('endDate', endDate);
}
firstTrigger = false;
});
function formatDate(date) {
if (date) {
if (isIso) {
return date.locale('en').format();
}
return date.locale('en').format(defaultDateFormat)
}
return null;
}
function getFormattedStartDate() {
if (startDate) {
return getMoment(startDate, options).format(dateFormat);
}
return null;
}
function getFormattedEndDate() {
if (endDate) {
return getMoment(endDate, options).format(dateFormat);
}
return null;
}
if (singleDatePicker) {
$this[0].getFormattedDate = getFormattedStartDate;
$input[0].getFormattedDate = getFormattedStartDate;
$dateRangePicker.getFormattedDate = getFormattedStartDate;
} else {
$dateRangePicker.getFormattedStartDate = getFormattedStartDate;
$dateRangePicker.getFormattedEndDate = getFormattedEndDate;
$this[0].getFormattedStartDate = getFormattedStartDate;
$this[0].getFormattedEndDate = getFormattedEndDate;
$input[0].getFormattedStartDate = getFormattedStartDate;
$input[0].getFormattedEndDate = getFormattedEndDate;
}
$input.trigger('change');
});
}
abp.dom.onNodeAdded(function (args) {
abp.dom.initializers.initializeToolTips(args.$el.findWithSelf('[data-toggle="tooltip"]'));
abp.dom.initializers.initializePopovers(args.$el.findWithSelf('[data-toggle="popover"]'));
@ -217,6 +778,7 @@
abp.dom.initializers.initializePopovers($('[data-toggle="popover"]'));
abp.dom.initializers.initializeTimeAgos($('.timeago'));
abp.dom.initializers.initializeDatepickers($(document));
abp.dom.initializers.initializeDateRangePickers($(document));
abp.dom.initializers.initializeForms($('form'));
abp.dom.initializers.initializeAutocompleteSelects($('.auto-complete-select'));
$('[data-auto-focus="true"]').first().findWithSelf('input,select').focus();

4
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json

@ -48,6 +48,8 @@
"Search": "Search",
"ItemWillBeDeletedMessageWithFormat": "{0} will be deleted!",
"ItemWillBeDeletedMessage": "This item will be deleted!",
"ManageYourAccount": "Manage your account"
"ManageYourAccount": "Manage your account",
"Today": "Today",
"Apply": "Apply"
}
}

4
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json

@ -48,6 +48,8 @@
"Search": "Arama",
"ItemWillBeDeletedMessageWithFormat": "{0} silinecektir!",
"ItemWillBeDeletedMessage": "Bu nesne silinecektir!",
"ManageYourAccount": "Hesap yönetimi"
"ManageYourAccount": "Hesap yönetimi",
"Today": "Bugün",
"Apply": "Uygula"
}
}

Loading…
Cancel
Save