diff --git a/src/Framework/Framework/Controls/DataPager.cs b/src/Framework/Framework/Controls/DataPager.cs index 087dd0229f..97a38af8fb 100644 --- a/src/Framework/Framework/Controls/DataPager.cs +++ b/src/Framework/Framework/Controls/DataPager.cs @@ -93,6 +93,20 @@ public ITemplate? NextPageTemplate public static readonly DotvvmProperty NextPageTemplateProperty = DotvvmProperty.Register(c => c.NextPageTemplate, null); + /// + /// Gets or sets the template of the button which moves the user to the numbered page. + /// + [MarkupOptions(AllowBinding = false, MappingMode = MappingMode.InnerElement)] + [ConstantDataContextChange(typeof(int[]), order: 0)] + [CollectionElementDataContextChange(order: 1)] + public ITemplate? PageNumberTemplate + { + get { return (ITemplate?)GetValue(PageNumberTemplateProperty); } + set { SetValue(PageNumberTemplateProperty, value); } + } + public static readonly DotvvmProperty PageNumberTemplateProperty = + DotvvmProperty.Register(c => c.PageNumberTemplate, null); + /// /// Gets or sets whether a hyperlink should be rendered for the current page number. If set to false, only a plain text is rendered. /// @@ -126,6 +140,27 @@ public bool Enabled public static readonly DotvvmProperty EnabledProperty = DotvvmPropertyWithFallback.Register(nameof(Enabled), FormControls.EnabledProperty); + /// + /// Gets or sets styles for the list item element (<li>) rendered by the component. + /// + public HtmlCapability ListItemHtmlCapability + { + get => (HtmlCapability)ListItemHtmlCapabilityProperty.GetValue(this); + set => ListItemHtmlCapabilityProperty.SetValue(this, value); + } + public static readonly DotvvmCapabilityProperty ListItemHtmlCapabilityProperty = DotvvmCapabilityProperty.RegisterCapability("ListItem"); + + /// + /// Gets or sets styles for the link buttons rendered by the component. + /// + public HtmlCapability LinkHtmlCapability + { + get => (HtmlCapability)LinkHtmlCapabilityProperty.GetValue(this); + set => LinkHtmlCapabilityProperty.SetValue(this, value); + } + public static readonly DotvvmCapabilityProperty LinkHtmlCapabilityProperty = DotvvmCapabilityProperty.RegisterCapability("Link"); + + /// /// Gets or sets the (static) command that will be triggered when the DataPager needs to load data (when navigating to different page). /// The command accepts one argument of type and should return a new or . @@ -138,14 +173,36 @@ public ICommandBinding? LoadData public static readonly DotvvmProperty LoadDataProperty = DotvvmProperty.Register(nameof(LoadData)); + /// + /// Gets or sets the CSS class to be applied to the currently active page number. + /// + [MarkupOptions(AllowBinding = false)] + public string ActiveItemCssClass + { + get { return (string)GetValue(ActiveItemCssClassProperty); } + set { SetValue(ActiveItemCssClassProperty, value); } + } + public static readonly DotvvmProperty ActiveItemCssClassProperty + = DotvvmProperty.Register(c => c.ActiveItemCssClass, "active"); + + /// + /// Gets or sets the CSS class that to be applied to the disabled items. + /// + [MarkupOptions(AllowBinding = false)] + public string DisabledItemCssClass + { + get { return (string)GetValue(DisabledItemCssClassProperty); } + set { SetValue(DisabledItemCssClassProperty, value); } + } + public static readonly DotvvmProperty DisabledItemCssClassProperty + = DotvvmProperty.Register(c => c.DisabledItemCssClass, "disabled"); + protected HtmlGenericControl? ContentWrapper { get; set; } protected HtmlGenericControl? GoToFirstPageButton { get; set; } protected HtmlGenericControl? GoToPreviousPageButton { get; set; } protected Repeater? NumberButtonsRepeater { get; set; } protected HtmlGenericControl? GoToNextPageButton { get; set; } protected HtmlGenericControl? GoToLastPageButton { get; set; } - protected virtual string ActiveItemCssClass => "active"; - protected virtual string DisabledItemCssClass => "disabled"; protected internal override void OnLoad(IDotvvmRequestContext context) { @@ -196,7 +253,7 @@ protected virtual void DataBind(Hosting.IDotvvmRequestContext context) if (pagerBindings.PageNumbers is {}) { // number fields - var liTemplate = CreatePageNumberButton(globalEnabled, pagerBindings, context); + var liTemplate = CreatePageNumberButton(globalEnabled, PageNumberTemplate, pagerBindings, context); AddItemCssClass(liTemplate, context); NumberButtonsRepeater = new Repeater() { @@ -250,22 +307,23 @@ protected virtual HtmlGenericControl CreateWrapperList() protected override void AddVisibleAttributeOrBinding(in RenderState r, IHtmlWriter writer) { } // handled by the wrapper list - protected virtual HtmlGenericControl CreatePageNumberButton(ValueOrBinding globalEnabled, DataPagerBindings pagerBindings, IDotvvmRequestContext context) + protected virtual HtmlGenericControl CreatePageNumberButton(ValueOrBinding globalEnabled, ITemplate? userDefinedContentTemplate, DataPagerBindings pagerBindings, IDotvvmRequestContext context) { - var liTemplate = new HtmlGenericControl("li"); + var liTemplate = new HtmlGenericControl("li", ListItemHtmlCapability); liTemplate.CssClasses.Add(ActiveItemCssClass, new ValueOrBinding(pagerBindings.NotNull().IsActivePage.NotNull())); - var link = new LinkButton(); + + var link = new LinkButton().SetCapability(LinkHtmlCapability); + link.SetBinding(ButtonBase.ClickProperty, pagerBindings.NotNull().GoToPage.NotNull()); - SetPageNumberButtonContent(link, pagerBindings, context); + SetPageNumberButtonContent(context, link, pagerBindings, userDefinedContentTemplate); if (!RenderLinkForCurrentPage) link.SetBinding(IncludeInPageProperty, pagerBindings.IsActivePage.NotNull().Negate()); if (!true.Equals(globalEnabled)) link.SetValue(ButtonBase.EnabledProperty, globalEnabled); liTemplate.Children.Add(link); if (!RenderLinkForCurrentPage) { - var notLink = new Literal(); - SetPageNumberSpanContent(notLink, pagerBindings, context); - notLink.RenderSpanElement = true; + var notLink = new HtmlGenericControl("span").SetCapability(LinkHtmlCapability); + SetPageNumberSpanContent(context, notLink, pagerBindings, userDefinedContentTemplate); notLink.SetBinding(IncludeInPageProperty, pagerBindings.IsActivePage); liTemplate.Children.Add(notLink); } @@ -274,8 +332,10 @@ protected virtual HtmlGenericControl CreatePageNumberButton(ValueOrBinding protected virtual HtmlGenericControl CreateNavigationButton(string defaultText, ITemplate? userDefinedContentTemplate, object enabledValue, ICommandBinding clickCommandBindingExpression,IDotvvmRequestContext context) { - var li = new HtmlGenericControl("li"); - var link = new LinkButton(); + var li = new HtmlGenericControl("li", ListItemHtmlCapability); + + var link = new LinkButton().SetCapability(LinkHtmlCapability); + SetNavigationButtonContent(context, link, defaultText, userDefinedContentTemplate); link.SetBinding(ButtonBase.ClickProperty, clickCommandBindingExpression); if (!true.Equals(enabledValue)) link.SetValue(ButtonBase.EnabledProperty, enabledValue); @@ -295,14 +355,28 @@ protected virtual void SetNavigationButtonContent(IDotvvmRequestContext context, } } - protected virtual void SetPageNumberSpanContent(Literal notLink, DataPagerBindings pagerBindings, IDotvvmRequestContext context) + protected virtual void SetPageNumberSpanContent(IDotvvmRequestContext context, HtmlGenericControl span, DataPagerBindings pagerBindings, ITemplate? userDefinedContentTemplate) { - notLink.SetBinding(Literal.TextProperty, pagerBindings.PageNumberText.NotNull()); + if (userDefinedContentTemplate != null) + { + userDefinedContentTemplate.BuildContent(context, span); + } + else + { + span.SetBinding(Literal.TextProperty, pagerBindings.PageNumberText.NotNull()); + } } - protected virtual void SetPageNumberButtonContent(LinkButton link, DataPagerBindings pagerBindings, IDotvvmRequestContext context) + protected virtual void SetPageNumberButtonContent(IDotvvmRequestContext context, LinkButton link, DataPagerBindings pagerBindings, ITemplate? userDefinedContentTemplate) { - link.SetBinding(ButtonBase.TextProperty, pagerBindings.PageNumberText.NotNull()); + if (userDefinedContentTemplate != null) + { + userDefinedContentTemplate.BuildContent(context, link); + } + else + { + link.SetBinding(ButtonBase.TextProperty, pagerBindings.PageNumberText.NotNull()); + } } protected virtual void AddItemCssClass(HtmlGenericControl item, IDotvvmRequestContext context) diff --git a/src/Samples/Common/ViewModels/ControlSamples/DataPager/DataPagerTemplatesViewModel.cs b/src/Samples/Common/ViewModels/ControlSamples/DataPager/DataPagerTemplatesViewModel.cs new file mode 100644 index 0000000000..b8f1ce2a04 --- /dev/null +++ b/src/Samples/Common/ViewModels/ControlSamples/DataPager/DataPagerTemplatesViewModel.cs @@ -0,0 +1,55 @@ +using DotVVM.Framework.Controls; +using DotVVM.Framework.ViewModel; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace DotVVM.Samples.BasicSamples.ViewModels.ControlSamples.DataPager +{ + public class DataPagerTemplatesViewModel : DotvvmViewModelBase + { + public GridViewDataSet DataSet { get; set; } + + public string[] RomanNumerals { get; set; } = new[] { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX" }; + + public override Task Init() + { + DataSet = new GridViewDataSet() + { + PagingOptions = new PagingOptions() + { + PageSize = 3 + } + }; + return base.Init(); + } + + public override Task PreRender() + { + if (DataSet.IsRefreshRequired) + { + DataSet.LoadFromQueryable(FakeDB(50)); + } + + return base.PreRender(); + } + + private IQueryable FakeDB(int itemsCreatorCounter) + { + var dbdata = new List(); + for (var i = 0; i < itemsCreatorCounter; i++) + { + dbdata.Add(new Data + { + Text = $"Item {i}" + }); + } + return dbdata.AsQueryable(); + } + + public class Data + { + public string Text { get; set; } + } + } +} diff --git a/src/Samples/Common/ViewModels/ControlSamples/DataPager/DataPagerViewModel.cs b/src/Samples/Common/ViewModels/ControlSamples/DataPager/DataPagerViewModel.cs index 72144c08a1..7c05459bbd 100644 --- a/src/Samples/Common/ViewModels/ControlSamples/DataPager/DataPagerViewModel.cs +++ b/src/Samples/Common/ViewModels/ControlSamples/DataPager/DataPagerViewModel.cs @@ -10,6 +10,8 @@ public class DataPagerViewModel : DotvvmViewModelBase { public GridViewDataSet DataSet { get; set; } + public string[] RomanNumerals { get; set; } = new[] { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX" }; + public bool Enabled { get; set; } = true; public int ItemsInDatabaseCount { get; set; } = 2; diff --git a/src/Samples/Common/Views/ControlSamples/DataPager/DataPagerTemplates.dothtml b/src/Samples/Common/Views/ControlSamples/DataPager/DataPagerTemplates.dothtml new file mode 100644 index 0000000000..65c7b6fbd6 --- /dev/null +++ b/src/Samples/Common/Views/ControlSamples/DataPager/DataPagerTemplates.dothtml @@ -0,0 +1,52 @@ +@viewModel DotVVM.Samples.BasicSamples.ViewModels.ControlSamples.DataPager.DataPagerTemplatesViewModel, DotVVM.Samples.Common + + + + + + + + + + + +

Templates in DataPager

+ + + +
  • {{value: Text}}
  • +
    +
    + + + ◀️◀️ + ◀️ + ▶️ + ▶️▶️ + + {{value: _root.RomanNumerals[_this]}} + + + + + + + diff --git a/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs b/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs index 22217bbc28..16c29ae866 100644 --- a/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs +++ b/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs @@ -75,6 +75,7 @@ public partial class SamplesRouteUrls public const string ControlSamples_ContentPlaceHolder_ContentPlaceHolderPage_ContentTest = "ControlSamples/ContentPlaceHolder/ContentPlaceHolderPage_ContentTest"; public const string ControlSamples_ContentPlaceHolder_DoubleContentPlaceHolderPage_ContentTest = "ControlSamples/ContentPlaceHolder/DoubleContentPlaceHolderPage_ContentTest"; public const string ControlSamples_DataPager_DataPager = "ControlSamples/DataPager/DataPager"; + public const string ControlSamples_DataPager_DataPagerTemplates = "ControlSamples/DataPager/DataPagerTemplates"; public const string ControlSamples_EnabledProperty_EnabledProperty = "ControlSamples/EnabledProperty/EnabledProperty"; public const string ControlSamples_EnvironmentView_EnvironmentViewTest = "ControlSamples/EnvironmentView/EnvironmentViewTest"; public const string ControlSamples_FileUpload_FileSize = "ControlSamples/FileUpload/FileSize"; diff --git a/src/Samples/Tests/Tests/Control/DataPagerTests.cs b/src/Samples/Tests/Tests/Control/DataPagerTests.cs index aadf996cbb..f5007bd97e 100644 --- a/src/Samples/Tests/Tests/Control/DataPagerTests.cs +++ b/src/Samples/Tests/Tests/Control/DataPagerTests.cs @@ -295,5 +295,27 @@ IElementWrapper GetPageIndex(int index) CheckNearPageIndexes(Enumerable.Range(11, 7)); }); } + + [Fact] + public void Control_DataPager_DataPagerTemplates() + { + RunInAllBrowsers(browser => { + browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_DataPager_DataPagerTemplates); + + var pager = browser.Single(".pagination"); + + var items = pager.FindElements(".page-item"); + items.ThrowIfDifferentCountThan(10); + + var content = new[] { "◀️◀️", "◀️", "I", "II", "III", "IV", "V", "VI", "▶️", "▶️▶️" }; + for (var i = 0; i < items.Count; i++) + { + var item = items[i]; + + var link = item.Single(".page-link"); + AssertUI.TextEquals(link, content[i]); + } + }); + } } }