@cahyo-dimas/freeday 3.7.0 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +197 -0
- package/COMPONENTS.md +214 -18
- package/README.id.md +4 -4
- package/README.md +4 -3
- package/adapters/blazor/FdyDatepicker.razor +3 -1
- package/adapters/blazor/FdyDatepicker.razor.cs +36 -1
- package/adapters/blazor/FdyMonthpicker.razor +29 -0
- package/adapters/blazor/FdyMonthpicker.razor.cs +102 -0
- package/adapters/blazor/FdyTable.razor +6 -1
- package/adapters/blazor/FdyTable.razor.cs +33 -0
- package/adapters/blazor/FdyTableFooter.razor +6 -6
- package/adapters/blazor/FdyTableFooter.razor.cs +36 -0
- package/adapters/core/month-grid.d.ts +116 -0
- package/adapters/core/month-grid.js +234 -0
- package/adapters/react/components/FdyDatepicker.tsx +164 -166
- package/adapters/react/components/FdyMonthpicker.tsx +400 -0
- package/adapters/react/components/FdyTable.tsx +38 -2
- package/adapters/react/components/FdyTableFilter.tsx +13 -1
- package/adapters/react/components/FdyTableFooter.tsx +37 -6
- package/adapters/react/index.d.ts +6 -2
- package/adapters/react/index.js +1 -0
- package/adapters/react/usePopover.ts +54 -9
- package/adapters/vue/components/FdyDatepicker.vue +158 -160
- package/adapters/vue/components/FdyMonthpicker.vue +387 -0
- package/adapters/vue/components/FdyTable.vue +33 -1
- package/adapters/vue/components/FdyTableFilter.vue +13 -1
- package/adapters/vue/components/FdyTableFooter.vue +39 -6
- package/adapters/vue/index.d.ts +16 -0
- package/adapters/vue/index.js +1 -0
- package/adapters/vue/usePopover.ts +53 -10
- package/dist/freeday-datepicker.js +68 -14
- package/dist/freeday-menu.js +15 -1
- package/dist/freeday-popover.js +33 -11
- package/dist/freeday-table.js +64 -5
- package/dist/freeday-tabs.js +21 -2
- package/dist/freeday.bundle.css +50 -2
- package/dist/freeday.css +50 -2
- package/dist/freeday.js +201 -33
- package/docs/agent-onboarding.md +3 -3
- package/docs/getting-started.md +6 -6
- package/docs/integrations.md +22 -4
- package/package.json +2 -2
- package/src/components/table.css +19 -2
- package/src/components/tabs.css +31 -0
|
@@ -5,11 +5,31 @@ namespace Freeday.Blazor;
|
|
|
5
5
|
|
|
6
6
|
public partial class FdyDatepicker
|
|
7
7
|
{
|
|
8
|
-
/// <summary>Selected date as an ISO string (<c>yyyy-MM-dd</c>), or
|
|
8
|
+
/// <summary>Selected date as an ISO string (<c>yyyy-MM-dd</c>), or <c>yyyy-MM</c> when
|
|
9
|
+
/// <see cref="Mode"/> is <see cref="FdyDatepickerMode.Month"/>, or "" when unset.
|
|
9
10
|
/// Two-way bindable via <c>@bind-Value</c>.</summary>
|
|
10
11
|
[Parameter] public string? Value { get; set; }
|
|
11
12
|
[Parameter] public EventCallback<string?> ValueChanged { get; set; }
|
|
12
13
|
|
|
14
|
+
/// <summary>What the picker answers with. <see cref="FdyDatepickerMode.Day"/> (default) is the
|
|
15
|
+
/// calendar as it always was. <see cref="FdyDatepickerMode.Month"/> opens on the month grid,
|
|
16
|
+
/// stops there, and reports <c>yyyy-MM</c>.</summary>
|
|
17
|
+
[Obsolete("Use FdyMonthpicker for a month. This parameter makes Value, Min and Max carry " +
|
|
18
|
+
"yyyy-MM-dd or yyyy-MM while a different parameter decides which, so the binding " +
|
|
19
|
+
"cannot state what to store in it. Still works, unchanged, and removed in 4.0.")]
|
|
20
|
+
[Parameter] public FdyDatepickerMode Mode { get; set; } = FdyDatepickerMode.Day;
|
|
21
|
+
|
|
22
|
+
/* null rather than "day" for the default: an attribute the enhancer does not need is an
|
|
23
|
+
attribute no reader of the seed has to interpret, and the enhancer's own check is
|
|
24
|
+
`=== 'month'`, so the day path is the absence of this.
|
|
25
|
+
|
|
26
|
+
The pragma is the kit reading its OWN deprecated member, which is the one place that has to
|
|
27
|
+
keep doing so: the promise is that `Mode` keeps working until 4.0, and it cannot work if the
|
|
28
|
+
seed stops looking at it. Scoped to these two lines so every other use still warns. */
|
|
29
|
+
#pragma warning disable CS0618
|
|
30
|
+
private string? ModeAttribute => Mode == FdyDatepickerMode.Month ? "month" : null;
|
|
31
|
+
#pragma warning restore CS0618
|
|
32
|
+
|
|
13
33
|
/// <summary>Accessible label for the trigger.</summary>
|
|
14
34
|
[Parameter] public string? Label { get; set; }
|
|
15
35
|
[Parameter] public string? Placeholder { get; set; }
|
|
@@ -83,3 +103,18 @@ public partial class FdyDatepicker
|
|
|
83
103
|
/// <summary>The detail shape of the enhancer's <c>fdy-datepicker-change</c> event.</summary>
|
|
84
104
|
public sealed record ChangeDetail(string? Value, string? Date);
|
|
85
105
|
}
|
|
106
|
+
|
|
107
|
+
/// <summary>What <see cref="FdyDatepicker"/> answers with.</summary>
|
|
108
|
+
/// <remarks>The enum goes with <see cref="FdyDatepicker.Mode"/> in 4.0, so only
|
|
109
|
+
/// <see cref="Day"/> is left unmarked: it names the behaviour a picker with no
|
|
110
|
+
/// <c>Mode</c> already has, and writing it changes nothing.</remarks>
|
|
111
|
+
public enum FdyDatepickerMode
|
|
112
|
+
{
|
|
113
|
+
/// <summary>A single day, reported as <c>yyyy-MM-dd</c>.</summary>
|
|
114
|
+
Day,
|
|
115
|
+
|
|
116
|
+
/// <summary>A whole month, reported as <c>yyyy-MM</c>.</summary>
|
|
117
|
+
[Obsolete("Use FdyMonthpicker, whose Value is yyyy-MM and has no Mode to read. " +
|
|
118
|
+
"Still works, unchanged, and removed in 4.0.")]
|
|
119
|
+
Month,
|
|
120
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
@namespace Freeday.Blazor
|
|
2
|
+
@inherits FreedayComponentBase<FdyMonthpicker>
|
|
3
|
+
|
|
4
|
+
@* Seed-based wrapper over freeday-datepicker.js in its month mode: the enhancer builds the
|
|
5
|
+
input-styled trigger + calendar popover from this empty div, opens on the month grid, and emits
|
|
6
|
+
fdy-datepicker-change. The enhancer owns that DOM, so we render the seed once
|
|
7
|
+
(ShouldRender => false) and read the picked month back through @bind-Value.
|
|
8
|
+
|
|
9
|
+
data-mode="month" is written here rather than taken as a parameter. That is the difference
|
|
10
|
+
between this component and FdyDatepicker: Value has one format, yyyy-MM, because nothing the
|
|
11
|
+
caller passes can change it. *@
|
|
12
|
+
<div @ref="Root" data-fdy-datepicker
|
|
13
|
+
data-mode="month"
|
|
14
|
+
data-value="@Value"
|
|
15
|
+
data-label="@Label"
|
|
16
|
+
data-placeholder="@Placeholder"
|
|
17
|
+
data-min="@Min"
|
|
18
|
+
data-max="@Max"
|
|
19
|
+
data-id="@Id"
|
|
20
|
+
data-describedby="@Describedby"
|
|
21
|
+
data-disabled="@(Disabled ? "true" : null)"
|
|
22
|
+
data-readonly="@(Readonly ? "true" : null)"
|
|
23
|
+
data-invalid="@(Invalid ? "true" : null)"
|
|
24
|
+
data-fdy-text-prev-year="@PrevYearLabel"
|
|
25
|
+
data-fdy-text-next-year="@NextYearLabel"
|
|
26
|
+
data-fdy-text-prev-years="@PrevYearsLabel"
|
|
27
|
+
data-fdy-text-next-years="@NextYearsLabel"
|
|
28
|
+
data-fdy-text-choose-year="@ChooseYearLabel"
|
|
29
|
+
data-fdy-text-back-to-months="@BackToMonthsLabel"></div>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
using Microsoft.AspNetCore.Components;
|
|
2
|
+
using Microsoft.JSInterop;
|
|
3
|
+
|
|
4
|
+
namespace Freeday.Blazor;
|
|
5
|
+
|
|
6
|
+
/// <summary>
|
|
7
|
+
/// A picker that answers with a whole month (<c>yyyy-MM</c>).
|
|
8
|
+
/// </summary>
|
|
9
|
+
/// <remarks>
|
|
10
|
+
/// <para>This is the replacement for <c>FdyDatepicker</c> with <c>Mode="FdyDatepickerMode.Month"</c>,
|
|
11
|
+
/// which shipped in 3.8.0 and is deprecated. There, <c>Value</c> is one <c>string?</c> holding
|
|
12
|
+
/// <c>yyyy-MM-dd</c> or <c>yyyy-MM</c> and a different parameter decides which, so nothing about
|
|
13
|
+
/// the binding tells a caller what to store in it. Here it has one format and there is no
|
|
14
|
+
/// <c>Mode</c>. The deprecated spelling keeps working until 4.0.</para>
|
|
15
|
+
/// <para>It is a separate seed rather than a wrapper around <c>FdyDatepicker</c>: that component
|
|
16
|
+
/// renders once and then hands its DOM to the enhancer (<c>ShouldRender => !Hydrated</c>), so
|
|
17
|
+
/// nesting it would mean forwarding an <c>ElementReference</c> and an event subscription through a
|
|
18
|
+
/// component that owns neither. The logic the two share is the enhancer itself, which both reach
|
|
19
|
+
/// through <c>FreedayDatepicker.init</c>, and that is already one copy.</para>
|
|
20
|
+
/// <para>Month names come from the page's <c><html lang></c> through <c>Intl</c>, which is
|
|
21
|
+
/// what <c><input type="month"></c> cannot do: it names the month in the browser's language.
|
|
22
|
+
/// So an Indonesian page reads <c>Oktober 2026</c> with nothing configured.</para>
|
|
23
|
+
/// </remarks>
|
|
24
|
+
public partial class FdyMonthpicker
|
|
25
|
+
{
|
|
26
|
+
/// <summary>The chosen month as an ISO string (<c>yyyy-MM</c>), or "" when unset. One format,
|
|
27
|
+
/// always. Two-way bindable via <c>@bind-Value</c>.</summary>
|
|
28
|
+
[Parameter] public string? Value { get; set; }
|
|
29
|
+
[Parameter] public EventCallback<string?> ValueChanged { get; set; }
|
|
30
|
+
|
|
31
|
+
/// <summary>Accessible label for the trigger. Defaults to the enhancer's <c>Month</c>.</summary>
|
|
32
|
+
[Parameter] public string? Label { get; set; }
|
|
33
|
+
|
|
34
|
+
/// <summary>Shown while nothing is picked. Defaults to the enhancer's <c>Choose a month</c>.</summary>
|
|
35
|
+
[Parameter] public string? Placeholder { get; set; }
|
|
36
|
+
|
|
37
|
+
/// <summary>Earliest selectable month (ISO <c>yyyy-MM</c>; a full <c>yyyy-MM-dd</c> is read too,
|
|
38
|
+
/// as its own month). A bound that falls inside a month leaves that month pickable.</summary>
|
|
39
|
+
[Parameter] public string? Min { get; set; }
|
|
40
|
+
|
|
41
|
+
/// <summary>Latest selectable month.</summary>
|
|
42
|
+
[Parameter] public string? Max { get; set; }
|
|
43
|
+
|
|
44
|
+
/// <summary>Id for the trigger the enhancer builds, so a form's own label can point at it.</summary>
|
|
45
|
+
[Parameter] public string? Id { get; set; }
|
|
46
|
+
|
|
47
|
+
/// <summary>Id of the help or error text the trigger describes itself with.</summary>
|
|
48
|
+
[Parameter] public string? Describedby { get; set; }
|
|
49
|
+
|
|
50
|
+
/// <summary>Greyed and out of the tab order.</summary>
|
|
51
|
+
[Parameter] public bool Disabled { get; set; }
|
|
52
|
+
|
|
53
|
+
/// <summary>Locked/view mode: focusable and showing its month, but the calendar will not open.
|
|
54
|
+
/// Unlike <see cref="Disabled"/> it keeps tab order and is not greyed.</summary>
|
|
55
|
+
[Parameter] public bool Readonly { get; set; }
|
|
56
|
+
|
|
57
|
+
/// <summary>Marks the field invalid (<c>aria-invalid</c> + the error styling).</summary>
|
|
58
|
+
[Parameter] public bool Invalid { get; set; }
|
|
59
|
+
|
|
60
|
+
/* The calendar's navigation labels, and only the ones this picker shows. Month NAMES follow the
|
|
61
|
+
page's `lang` through Intl and need nothing here. The day grid's own arrows are absent along
|
|
62
|
+
with the day grid, so there is no PrevMonthLabel to pass: the arrows beside a month grid step
|
|
63
|
+
a YEAR. Leave one null and the enhancer's English default stands. */
|
|
64
|
+
[Parameter] public string? PrevYearLabel { get; set; }
|
|
65
|
+
[Parameter] public string? NextYearLabel { get; set; }
|
|
66
|
+
[Parameter] public string? PrevYearsLabel { get; set; }
|
|
67
|
+
[Parameter] public string? NextYearsLabel { get; set; }
|
|
68
|
+
|
|
69
|
+
/// <summary>The title button that drills into the year grid. <c>{label}</c> is the year shown.</summary>
|
|
70
|
+
[Parameter] public string? ChooseYearLabel { get; set; }
|
|
71
|
+
|
|
72
|
+
/// <summary>The year grid's title, back to months. <c>{start}</c> and <c>{end}</c> are the page range.</summary>
|
|
73
|
+
[Parameter] public string? BackToMonthsLabel { get; set; }
|
|
74
|
+
|
|
75
|
+
protected override ValueTask HydrateAsync() => JS.InvokeVoidAsync("FreedayDatepicker.init", Root);
|
|
76
|
+
|
|
77
|
+
protected override async ValueTask OnHydratedAsync()
|
|
78
|
+
=> await SubscribeAsync("fdy-datepicker-change", nameof(OnChange));
|
|
79
|
+
|
|
80
|
+
// The enhancer owns the built trigger + calendar; never let Blazor re-render the seed.
|
|
81
|
+
protected override bool ShouldRender() => !Hydrated;
|
|
82
|
+
|
|
83
|
+
/* Which is why the three states are pushed rather than re-rendered: the seed is written once,
|
|
84
|
+
so a page that disables this field later would otherwise be ignored in silence. */
|
|
85
|
+
protected override async Task OnParametersSetAsync()
|
|
86
|
+
{
|
|
87
|
+
if (!Hydrated) return;
|
|
88
|
+
await JS.InvokeVoidAsync("FreedayDatepicker.setState", Root,
|
|
89
|
+
new { disabled = Disabled, @readonly = Readonly, invalid = Invalid });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// <summary>Invoked by the bridge when the user picks (or clears) a month.</summary>
|
|
93
|
+
[JSInvokable]
|
|
94
|
+
public async Task OnChange(ChangeDetail detail)
|
|
95
|
+
{
|
|
96
|
+
Value = detail.Value;
|
|
97
|
+
await ValueChanged.InvokeAsync(detail.Value);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/// <summary>The detail shape of the enhancer's <c>fdy-datepicker-change</c> event.</summary>
|
|
101
|
+
public sealed record ChangeDetail(string? Value, string? Date);
|
|
102
|
+
}
|
|
@@ -146,6 +146,11 @@
|
|
|
146
146
|
|
|
147
147
|
@if (Pager)
|
|
148
148
|
{
|
|
149
|
-
|
|
149
|
+
@* The six strings go through as given, null included: every default lives in the footer, so
|
|
150
|
+
a parameter left unset here is the footer's English and not a second copy of it. *@
|
|
151
|
+
<FdyTableFooter Page="FooterPage" PageSizes="PageSizes" PageChanged="OnFooterPage"
|
|
152
|
+
InfoText="@InfoText" PageSizeLabel="@PageSizeLabel"
|
|
153
|
+
PaginationLabel="@PaginationLabel" PrevLabel="@PrevLabel"
|
|
154
|
+
NextLabel="@NextLabel" PageLabel="@PageLabel" />
|
|
150
155
|
}
|
|
151
156
|
</div>
|
|
@@ -116,6 +116,39 @@ public partial class FdyTable<TRow>
|
|
|
116
116
|
/// <summary>Bulk-bar actions (Blazor equivalent of Vue's <c>bulk-actions</c> slot).</summary>
|
|
117
117
|
[Parameter] public RenderFragment? BulkContent { get; set; }
|
|
118
118
|
|
|
119
|
+
// The footer's six strings, forwarded to the FdyTableFooter this table draws itself. 3.8.0 gave
|
|
120
|
+
// the footer those parameters and this table passed none of them on, so the hatch reached every
|
|
121
|
+
// footer except the one almost every page uses, and the documented way out was to switch the
|
|
122
|
+
// footer off and render it yourself: a door that opens only if you stop using the table. Flat
|
|
123
|
+
// parameters carrying the footer's own names, so moving between the two routes moves a
|
|
124
|
+
// parameter rather than reshaping it.
|
|
125
|
+
//
|
|
126
|
+
// Nullable, unlike SelectedText and its four neighbours above, which this component renders
|
|
127
|
+
// itself and therefore defaults itself. These six are rendered by FdyTableFooter, which already
|
|
128
|
+
// holds their English; a default repeated here would be a second copy to keep in step, and the
|
|
129
|
+
// two copies would disagree the first time one of them was edited.
|
|
130
|
+
|
|
131
|
+
/// <summary>Footer range line, <c>{from}</c> <c>{to}</c> <c>{total}</c> substituted.
|
|
132
|
+
/// Null leaves <c>FdyTableFooter</c>'s default, <c>Showing {from}–{to} of {total}</c>.</summary>
|
|
133
|
+
[Parameter] public string? InfoText { get; set; }
|
|
134
|
+
|
|
135
|
+
/// <summary>Footer: the visible word beside the rows-per-page control, which also names it.
|
|
136
|
+
/// Null leaves the default, <c>Rows</c>.</summary>
|
|
137
|
+
[Parameter] public string? PageSizeLabel { get; set; }
|
|
138
|
+
|
|
139
|
+
/// <summary>Footer: accessible name of the pager landmark. Null leaves <c>Pagination</c>.</summary>
|
|
140
|
+
[Parameter] public string? PaginationLabel { get; set; }
|
|
141
|
+
|
|
142
|
+
/// <summary>Footer: accessible name of the previous-page arrow. Null leaves <c>Previous page</c>.</summary>
|
|
143
|
+
[Parameter] public string? PrevLabel { get; set; }
|
|
144
|
+
|
|
145
|
+
/// <summary>Footer: accessible name of the next-page arrow. Null leaves <c>Next page</c>.</summary>
|
|
146
|
+
[Parameter] public string? NextLabel { get; set; }
|
|
147
|
+
|
|
148
|
+
/// <summary>Footer: accessible name of a numbered page button, <c>{n}</c> substituted. Null
|
|
149
|
+
/// leaves <c>Go to page {n}</c>.</summary>
|
|
150
|
+
[Parameter] public string? PageLabel { get; set; }
|
|
151
|
+
|
|
119
152
|
private FdySortState? _internalSort;
|
|
120
153
|
private Dictionary<string, FdyColumnFilter> _internalFilters = new();
|
|
121
154
|
private int _internalPageIndex;
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
@if (Visible)
|
|
10
10
|
{
|
|
11
11
|
<div class="fdy-table-footer">
|
|
12
|
-
<span class="fdy-table-footer__info"
|
|
12
|
+
<span class="fdy-table-footer__info">@InfoLine</span>
|
|
13
13
|
|
|
14
14
|
@if (Sizes.Count > 0)
|
|
15
15
|
{
|
|
16
16
|
<div class="fdy-table-footer__size">
|
|
17
|
-
<span id="@SizeLabelId"
|
|
17
|
+
<span id="@SizeLabelId">@(PageSizeLabel ?? "Rows")</span>
|
|
18
18
|
@* The kit's own listbox, not a native <select>: an OS menu is unthemeable, and on
|
|
19
19
|
macOS it drops a dark panel into a light page. FdyCombo's own header says so. *@
|
|
20
20
|
<FdyCombo TValue="string" Value="@Page.Size.ToString()" Options="SizeOptions"
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
|
|
25
25
|
@if (HasPager)
|
|
26
26
|
{
|
|
27
|
-
<nav class="fdy-pagination" aria-label="Pagination">
|
|
27
|
+
<nav class="fdy-pagination" aria-label="@(PaginationLabel ?? "Pagination")">
|
|
28
28
|
<ul class="fdy-pagination__list">
|
|
29
29
|
<li>
|
|
30
|
-
<button type="button" class="fdy-pagination__link" aria-label="Previous page"
|
|
30
|
+
<button type="button" class="fdy-pagination__link" aria-label="@(PrevLabel ?? "Previous page")"
|
|
31
31
|
disabled="@(CurrentPage1 == 1)" @onclick="() => GoTo(CurrentPage1 - 1)">‹</button>
|
|
32
32
|
</li>
|
|
33
33
|
@foreach (int? p in Pages)
|
|
@@ -44,13 +44,13 @@
|
|
|
44
44
|
else
|
|
45
45
|
{
|
|
46
46
|
int page = p.Value;
|
|
47
|
-
<button type="button" class="fdy-pagination__link" aria-label="@(
|
|
47
|
+
<button type="button" class="fdy-pagination__link" aria-label="@PageAriaLabel(page)"
|
|
48
48
|
@onclick="() => GoTo(page)">@(page)</button>
|
|
49
49
|
}
|
|
50
50
|
</li>
|
|
51
51
|
}
|
|
52
52
|
<li>
|
|
53
|
-
<button type="button" class="fdy-pagination__link" aria-label="Next page"
|
|
53
|
+
<button type="button" class="fdy-pagination__link" aria-label="@(NextLabel ?? "Next page")"
|
|
54
54
|
disabled="@(CurrentPage1 == TotalPages)" @onclick="() => GoTo(CurrentPage1 + 1)">›</button>
|
|
55
55
|
</li>
|
|
56
56
|
</ul>
|
|
@@ -21,6 +21,35 @@ public partial class FdyTableFooter
|
|
|
21
21
|
/// </summary>
|
|
22
22
|
[Parameter] public IReadOnlyList<int>? PageSizes { get; set; }
|
|
23
23
|
|
|
24
|
+
// The strings, six of them, one per thing this band says. The raw path has had a hatch since
|
|
25
|
+
// 1.39.0 (data-fdy-text-<key> read through textOf()) and this path had none, so an app that
|
|
26
|
+
// does not speak English could only rewrite the whole band by hand. A Blazor app mixes both
|
|
27
|
+
// paths by construction, since FreedayBlazor.initAll runs the enhancers, so these names follow
|
|
28
|
+
// the enhancer's TEXT keys where a key exists. Defaults stay English on every path, which
|
|
29
|
+
// COMPONENTS.md promises and the suite enforces.
|
|
30
|
+
|
|
31
|
+
/// <summary>
|
|
32
|
+
/// The range line. <c>{from}</c>, <c>{to}</c> and <c>{total}</c> are substituted, every
|
|
33
|
+
/// occurrence, as the enhancer's <c>textOf()</c> does. Default <c>Showing {from}–{to} of
|
|
34
|
+
/// {total}</c>, the enhancer's <c>info</c> key.
|
|
35
|
+
/// </summary>
|
|
36
|
+
[Parameter] public string? InfoText { get; set; }
|
|
37
|
+
|
|
38
|
+
/// <summary>The visible word beside the rows-per-page control, which also names it. Default <c>Rows</c>.</summary>
|
|
39
|
+
[Parameter] public string? PageSizeLabel { get; set; }
|
|
40
|
+
|
|
41
|
+
/// <summary>Accessible name of the pager landmark. Default <c>Pagination</c>.</summary>
|
|
42
|
+
[Parameter] public string? PaginationLabel { get; set; }
|
|
43
|
+
|
|
44
|
+
/// <summary>Accessible name of the previous-page arrow. Default <c>Previous page</c>.</summary>
|
|
45
|
+
[Parameter] public string? PrevLabel { get; set; }
|
|
46
|
+
|
|
47
|
+
/// <summary>Accessible name of the next-page arrow. Default <c>Next page</c>.</summary>
|
|
48
|
+
[Parameter] public string? NextLabel { get; set; }
|
|
49
|
+
|
|
50
|
+
/// <summary>Accessible name of a numbered page button. <c>{n}</c> substituted. Default <c>Go to page {n}</c>.</summary>
|
|
51
|
+
[Parameter] public string? PageLabel { get; set; }
|
|
52
|
+
|
|
24
53
|
private IReadOnlyList<int> Sizes => PageSizes ?? Array.Empty<int>();
|
|
25
54
|
|
|
26
55
|
// Just the number: the control sits beside a label reading "Rows", so "20 per page" states the
|
|
@@ -40,6 +69,13 @@ public partial class FdyTableFooter
|
|
|
40
69
|
|
|
41
70
|
private bool HasPager => Page.Size > 0 && TotalPages > 1;
|
|
42
71
|
|
|
72
|
+
private string InfoLine => (InfoText ?? "Showing {from}–{to} of {total}")
|
|
73
|
+
.Replace("{from}", RangeFrom.ToString())
|
|
74
|
+
.Replace("{to}", RangeTo.ToString())
|
|
75
|
+
.Replace("{total}", Page.Total.ToString());
|
|
76
|
+
|
|
77
|
+
private string PageAriaLabel(int page1) => (PageLabel ?? "Go to page {n}").Replace("{n}", page1.ToString());
|
|
78
|
+
|
|
43
79
|
// One page and no size control means there is nothing here to say, the table has always
|
|
44
80
|
// withheld the whole band in that case, and this is where that decision now lives.
|
|
45
81
|
private bool Visible => HasPager || Sizes.Count > 0;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Types for the framework-agnostic month and year grid logic (adapters/core/month-grid.js).
|
|
2
|
+
// Shared by FdyDatepicker and FdyMonthpicker in the Vue and React adapters.
|
|
3
|
+
|
|
4
|
+
/** A whole month as ISO `YYYY-MM`, e.g. `'2026-10'`. The format a month picker answers with. */
|
|
5
|
+
export type FdyMonth = string;
|
|
6
|
+
|
|
7
|
+
/** One cell of the twelve-cell month grid, ready to render. */
|
|
8
|
+
export interface FdyMonthCell {
|
|
9
|
+
/** 0-11, the month's index in its year. */
|
|
10
|
+
index: number;
|
|
11
|
+
/** What the cell shows, a short month name in the active locale. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** The cell's accessible name: the month and its year, spelled out in the active locale. */
|
|
14
|
+
ariaLabel: string;
|
|
15
|
+
selected: boolean;
|
|
16
|
+
today: boolean;
|
|
17
|
+
/** The whole month falls outside min/max, so it cannot be picked or navigated into. */
|
|
18
|
+
disabled: boolean;
|
|
19
|
+
/** Holds the grid's single `tabindex="0"`. */
|
|
20
|
+
focusable: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** One cell of the twelve-cell year grid. */
|
|
24
|
+
export interface FdyYearCell {
|
|
25
|
+
year: number;
|
|
26
|
+
selected: boolean;
|
|
27
|
+
today: boolean;
|
|
28
|
+
disabled: boolean;
|
|
29
|
+
focusable: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The two Intl formatters a month grid renders through. */
|
|
33
|
+
export interface FdyMonthFormatters {
|
|
34
|
+
/** Short month name, for the cell's text. */
|
|
35
|
+
cell: Intl.DateTimeFormat;
|
|
36
|
+
/** Month and year spelled out, for the cell's accessible name and the trigger's value. */
|
|
37
|
+
name: Intl.DateTimeFormat;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface FdyMonthGridView {
|
|
41
|
+
year: number;
|
|
42
|
+
selected: Date | null;
|
|
43
|
+
min: Date | null;
|
|
44
|
+
max: Date | null;
|
|
45
|
+
/** Which month index holds the roving tabindex. */
|
|
46
|
+
focusMonth: number;
|
|
47
|
+
formatters: FdyMonthFormatters;
|
|
48
|
+
/** Overridable only so a test can pin "today"; defaults to now. */
|
|
49
|
+
today?: Date;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface FdyYearGridView {
|
|
53
|
+
pageStart: number;
|
|
54
|
+
selected: Date | null;
|
|
55
|
+
min: Date | null;
|
|
56
|
+
max: Date | null;
|
|
57
|
+
focusYear: number;
|
|
58
|
+
today?: Date;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* What a key press in a grid means. `choose` is the one the two components answer differently:
|
|
63
|
+
* `FdyMonthpicker` commits the month, `FdyDatepicker` walks down into its days.
|
|
64
|
+
*/
|
|
65
|
+
export type FdyGridAction = 'focus' | 'choose' | 'close' | 'none';
|
|
66
|
+
|
|
67
|
+
export interface FdyMonthKeyIntent {
|
|
68
|
+
action: FdyGridAction;
|
|
69
|
+
/** 0-11, already wrapped inside the year on screen. */
|
|
70
|
+
index: number;
|
|
71
|
+
/** Years to step the view by, which only PageUp and PageDown set. */
|
|
72
|
+
yearDelta: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface FdyYearKeyIntent {
|
|
76
|
+
action: FdyGridAction;
|
|
77
|
+
year: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** A year page holds twelve years, so the grid matches the month grid's shape. */
|
|
81
|
+
export declare const YEARS_PER_PAGE: number;
|
|
82
|
+
|
|
83
|
+
/** A Date as `YYYY-MM`. The day is dropped. */
|
|
84
|
+
export declare function toISOMonth(d: Date): FdyMonth;
|
|
85
|
+
|
|
86
|
+
/** Read `YYYY-MM` or `YYYY-MM-DD` as the first day of the month named; null when unparseable. */
|
|
87
|
+
export declare function parseISOMonth(s: string | null | undefined): Date | null;
|
|
88
|
+
|
|
89
|
+
/** The `lang` the page declares, then `en`. Never the browser's language, which is the whole point. */
|
|
90
|
+
export declare function pageLocale(): string;
|
|
91
|
+
|
|
92
|
+
export declare function startOfDay(d: Date): Date;
|
|
93
|
+
export declare function startOfMonth(d: Date): Date;
|
|
94
|
+
export declare function addMonths(d: Date, n: number): Date;
|
|
95
|
+
|
|
96
|
+
/** True only when the WHOLE month falls outside the bounds. */
|
|
97
|
+
export declare function monthDisabled(year: number, month: number, min: Date | null, max: Date | null): boolean;
|
|
98
|
+
|
|
99
|
+
/** True only when every month in the year falls outside the bounds. */
|
|
100
|
+
export declare function yearDisabled(year: number, min: Date | null, max: Date | null): boolean;
|
|
101
|
+
|
|
102
|
+
/** Where the aligned page holding this year starts (2016 for 2026). */
|
|
103
|
+
export declare function yearPageStart(year: number): number;
|
|
104
|
+
|
|
105
|
+
/** The year grid's title, naming the page it shows. */
|
|
106
|
+
export declare function yearRangeLabel(pageStart: number): string;
|
|
107
|
+
|
|
108
|
+
export declare function monthFormatters(locale: string): FdyMonthFormatters;
|
|
109
|
+
|
|
110
|
+
export declare function monthCells(view: FdyMonthGridView): FdyMonthCell[];
|
|
111
|
+
|
|
112
|
+
export declare function yearCells(view: FdyYearGridView): FdyYearCell[];
|
|
113
|
+
|
|
114
|
+
export declare function monthKeyIntent(key: string, focusMonth: number): FdyMonthKeyIntent;
|
|
115
|
+
|
|
116
|
+
export declare function yearKeyIntent(key: string, focusYear: number): FdyYearKeyIntent;
|