@cahyo-dimas/freeday 2.0.0 → 2.2.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 +155 -0
- package/COMPONENTS.md +298 -2
- package/README.id.md +1 -1
- package/README.md +1 -1
- package/adapters/blazor/FdyAutocomplete.razor +4 -1
- package/adapters/blazor/FdyAutocomplete.razor.cs +26 -0
- package/adapters/blazor/FdyCascade.razor +6 -1
- package/adapters/blazor/FdyCascade.razor.cs +24 -0
- package/adapters/blazor/FdyCombo.razor +1 -0
- package/adapters/blazor/FdyCombo.razor.cs +13 -0
- package/adapters/blazor/FdyDatepicker.razor +15 -1
- package/adapters/blazor/FdyDatepicker.razor.cs +46 -0
- package/adapters/core/cfl-value.d.ts +11 -0
- package/adapters/core/cfl-value.js +31 -0
- package/adapters/react/components/FdyDrawer.tsx +3 -1
- package/adapters/react/components/FdyModal.tsx +3 -1
- package/adapters/react/index.d.ts +3 -0
- package/adapters/react/index.js +1 -0
- package/adapters/vue/components/FdyDrawer.vue +4 -1
- package/adapters/vue/components/FdyModal.vue +4 -1
- package/adapters/vue/index.d.ts +3 -0
- package/adapters/vue/index.js +1 -0
- package/dist/freeday-autocomplete.js +17 -1
- package/dist/freeday-carousel.js +5 -4
- package/dist/freeday-cascade.js +58 -7
- package/dist/freeday-cfl.js +5 -4
- package/dist/freeday-chart.js +33 -3
- package/dist/freeday-datepicker.js +109 -17
- package/dist/freeday-form.js +5 -4
- package/dist/freeday-mask.js +5 -4
- package/dist/freeday-select.js +18 -1
- package/dist/freeday-stepper.js +5 -4
- package/dist/freeday-table.js +9 -6
- package/dist/freeday-timepicker.js +19 -1
- package/dist/freeday-toast.js +5 -4
- package/dist/freeday-upload.js +5 -4
- package/dist/freeday.js +298 -64
- package/docs/agent-onboarding.md +9 -6
- package/docs/getting-started.md +15 -6
- package/docs/integrations.md +2 -2
- package/package.json +2 -2
|
@@ -11,7 +11,12 @@
|
|
|
11
11
|
data-placeholder="@Placeholder"
|
|
12
12
|
data-separator="@Separator"
|
|
13
13
|
data-fdy-text-back="@BackLabel"
|
|
14
|
-
data-fdy-text-submenu="@SubmenuLabel"
|
|
14
|
+
data-fdy-text-submenu="@SubmenuLabel"
|
|
15
|
+
data-id="@Id"
|
|
16
|
+
data-describedby="@Describedby"
|
|
17
|
+
data-disabled="@(Disabled ? "true" : null)"
|
|
18
|
+
data-readonly="@(Readonly ? "true" : null)"
|
|
19
|
+
data-invalid="@(Invalid ? "true" : null)">
|
|
15
20
|
<ul>
|
|
16
21
|
@foreach (FdyCascadeNode node in Nodes)
|
|
17
22
|
{
|
|
@@ -31,8 +31,32 @@ public partial class FdyCascade
|
|
|
31
31
|
protected override async ValueTask OnHydratedAsync()
|
|
32
32
|
=> await SubscribeAsync("fdy-cascade-change", nameof(OnChange));
|
|
33
33
|
|
|
34
|
+
/// <summary>Id for the trigger the enhancer builds, so a form's label can point at it.</summary>
|
|
35
|
+
[Parameter] public string? Id { get; set; }
|
|
36
|
+
|
|
37
|
+
/// <summary>Id of the help or error text the trigger describes itself with.</summary>
|
|
38
|
+
[Parameter] public string? Describedby { get; set; }
|
|
39
|
+
|
|
40
|
+
/// <summary>Greyed and out of the tab order.</summary>
|
|
41
|
+
[Parameter] public bool Disabled { get; set; }
|
|
42
|
+
|
|
43
|
+
/// <summary>Locked/view mode: focusable and showing its value, but it will not open.</summary>
|
|
44
|
+
[Parameter] public bool Readonly { get; set; }
|
|
45
|
+
|
|
46
|
+
/// <summary>Marks the field invalid (<c>aria-invalid</c> + the error styling).</summary>
|
|
47
|
+
[Parameter] public bool Invalid { get; set; }
|
|
48
|
+
|
|
34
49
|
protected override bool ShouldRender() => !Hydrated;
|
|
35
50
|
|
|
51
|
+
/* Pushed, not re-rendered: the seed's <ul> is consumed and removed by the enhancer, so this
|
|
52
|
+
component can never render again. */
|
|
53
|
+
protected override async Task OnParametersSetAsync()
|
|
54
|
+
{
|
|
55
|
+
if (!Hydrated) return;
|
|
56
|
+
await JS.InvokeVoidAsync("FreedayCascade.setState", Root,
|
|
57
|
+
new { disabled = Disabled, @readonly = Readonly, invalid = Invalid });
|
|
58
|
+
}
|
|
59
|
+
|
|
36
60
|
/// <summary>Invoked by the bridge when the user selects a leaf.</summary>
|
|
37
61
|
[JSInvokable]
|
|
38
62
|
public async Task OnChange(ChangeDetail detail)
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<button id="@ButtonId" type="button" class="fdy-combo__button" role="combobox"
|
|
11
11
|
aria-haspopup="listbox" aria-expanded="false"
|
|
12
12
|
aria-labelledby="@AriaLabelledby"
|
|
13
|
+
aria-describedby="@Describedby"
|
|
13
14
|
aria-invalid="@(Invalid ? "true" : null)"
|
|
14
15
|
aria-readonly="@(Readonly ? "true" : null)"
|
|
15
16
|
disabled="@Disabled">
|
|
@@ -29,6 +29,10 @@ public partial class FdyCombo<TValue>
|
|
|
29
29
|
|
|
30
30
|
[Parameter] public bool Invalid { get; set; }
|
|
31
31
|
|
|
32
|
+
/// <summary>Id of the help or error text the combobox describes itself with. Vue and React
|
|
33
|
+
/// have always had this one; Blazor was the odd stack out.</summary>
|
|
34
|
+
[Parameter] public string? Describedby { get; set; }
|
|
35
|
+
|
|
32
36
|
private readonly string _autoId = $"fdy-combo-{Guid.NewGuid():N}";
|
|
33
37
|
private TValue _lastValue = default!;
|
|
34
38
|
private bool _ready;
|
|
@@ -66,6 +70,15 @@ public partial class FdyCombo<TValue>
|
|
|
66
70
|
_lastValue = Value;
|
|
67
71
|
await JS.InvokeVoidAsync("FreedayBlazor.comboSetValue", Root, KeyOf(Value));
|
|
68
72
|
}
|
|
73
|
+
|
|
74
|
+
/* The three states need the same treatment for the same reason the value did: this
|
|
75
|
+
component stops rendering once hydrated, so a page that disables the field afterwards
|
|
76
|
+
was changing a parameter nothing read. */
|
|
77
|
+
if (_ready)
|
|
78
|
+
{
|
|
79
|
+
await JS.InvokeVoidAsync("FreedayCombo.setState", Root,
|
|
80
|
+
new { disabled = Disabled, @readonly = Readonly, invalid = Invalid });
|
|
81
|
+
}
|
|
69
82
|
}
|
|
70
83
|
|
|
71
84
|
/// <summary>Invoked by the bridge when the user picks an option (enhancer's fdy-change).</summary>
|
|
@@ -10,4 +10,18 @@
|
|
|
10
10
|
data-label="@Label"
|
|
11
11
|
data-placeholder="@Placeholder"
|
|
12
12
|
data-min="@Min"
|
|
13
|
-
data-max="@Max"
|
|
13
|
+
data-max="@Max"
|
|
14
|
+
data-id="@Id"
|
|
15
|
+
data-describedby="@Describedby"
|
|
16
|
+
data-disabled="@(Disabled ? "true" : null)"
|
|
17
|
+
data-readonly="@(Readonly ? "true" : null)"
|
|
18
|
+
data-invalid="@(Invalid ? "true" : null)"
|
|
19
|
+
data-fdy-text-prev-month="@PrevMonthLabel"
|
|
20
|
+
data-fdy-text-next-month="@NextMonthLabel"
|
|
21
|
+
data-fdy-text-prev-year="@PrevYearLabel"
|
|
22
|
+
data-fdy-text-next-year="@NextYearLabel"
|
|
23
|
+
data-fdy-text-prev-years="@PrevYearsLabel"
|
|
24
|
+
data-fdy-text-next-years="@NextYearsLabel"
|
|
25
|
+
data-fdy-text-choose-month="@ChooseMonthLabel"
|
|
26
|
+
data-fdy-text-choose-year="@ChooseYearLabel"
|
|
27
|
+
data-fdy-text-back-to-months="@BackToMonthsLabel"></div>
|
|
@@ -18,6 +18,42 @@ public partial class FdyDatepicker
|
|
|
18
18
|
[Parameter] public string? Min { get; set; }
|
|
19
19
|
[Parameter] public string? Max { get; set; }
|
|
20
20
|
|
|
21
|
+
/// <summary>Id for the trigger the enhancer builds, so a form's own label can point at it.</summary>
|
|
22
|
+
[Parameter] public string? Id { get; set; }
|
|
23
|
+
|
|
24
|
+
/// <summary>Id of the help or error text the trigger describes itself with.</summary>
|
|
25
|
+
[Parameter] public string? Describedby { get; set; }
|
|
26
|
+
|
|
27
|
+
/// <summary>Greyed and out of the tab order.</summary>
|
|
28
|
+
[Parameter] public bool Disabled { get; set; }
|
|
29
|
+
|
|
30
|
+
/// <summary>Locked/view mode: focusable and showing its date, but the calendar will not open.
|
|
31
|
+
/// Unlike <see cref="Disabled"/> it keeps tab order and is not greyed.</summary>
|
|
32
|
+
[Parameter] public bool Readonly { get; set; }
|
|
33
|
+
|
|
34
|
+
/// <summary>Marks the field invalid (<c>aria-invalid</c> + the error styling).</summary>
|
|
35
|
+
[Parameter] public bool Invalid { get; set; }
|
|
36
|
+
|
|
37
|
+
/* The calendar's own navigation labels. Month and weekday NAMES follow the page's `lang`
|
|
38
|
+
through Intl and need nothing here; these are the buttons around them, which until 2.1.1
|
|
39
|
+
were literals inside the enhancer that no host could reach. Leave one null and the
|
|
40
|
+
enhancer's English default stands. */
|
|
41
|
+
[Parameter] public string? PrevMonthLabel { get; set; }
|
|
42
|
+
[Parameter] public string? NextMonthLabel { get; set; }
|
|
43
|
+
[Parameter] public string? PrevYearLabel { get; set; }
|
|
44
|
+
[Parameter] public string? NextYearLabel { get; set; }
|
|
45
|
+
[Parameter] public string? PrevYearsLabel { get; set; }
|
|
46
|
+
[Parameter] public string? NextYearsLabel { get; set; }
|
|
47
|
+
|
|
48
|
+
/// <summary>The title button that drills into the month grid. <c>{label}</c> is the period shown.</summary>
|
|
49
|
+
[Parameter] public string? ChooseMonthLabel { get; set; }
|
|
50
|
+
|
|
51
|
+
/// <summary>The title button that drills into the year grid. <c>{label}</c> is the period shown.</summary>
|
|
52
|
+
[Parameter] public string? ChooseYearLabel { get; set; }
|
|
53
|
+
|
|
54
|
+
/// <summary>The year grid's title, back to months. <c>{start}</c> and <c>{end}</c> are the page range.</summary>
|
|
55
|
+
[Parameter] public string? BackToMonthsLabel { get; set; }
|
|
56
|
+
|
|
21
57
|
protected override ValueTask HydrateAsync() => JS.InvokeVoidAsync("FreedayDatepicker.init", Root);
|
|
22
58
|
|
|
23
59
|
protected override async ValueTask OnHydratedAsync()
|
|
@@ -26,6 +62,16 @@ public partial class FdyDatepicker
|
|
|
26
62
|
// The enhancer owns the built trigger + calendar; never let Blazor re-render the seed.
|
|
27
63
|
protected override bool ShouldRender() => !Hydrated;
|
|
28
64
|
|
|
65
|
+
/* Which is exactly why the three states are pushed rather than re-rendered: the seed above is
|
|
66
|
+
written once, so a page that disables this field later would otherwise be ignored in
|
|
67
|
+
silence. */
|
|
68
|
+
protected override async Task OnParametersSetAsync()
|
|
69
|
+
{
|
|
70
|
+
if (!Hydrated) return;
|
|
71
|
+
await JS.InvokeVoidAsync("FreedayDatepicker.setState", Root,
|
|
72
|
+
new { disabled = Disabled, @readonly = Readonly, invalid = Invalid });
|
|
73
|
+
}
|
|
74
|
+
|
|
29
75
|
/// <summary>Invoked by the bridge when the user picks (or clears) a date.</summary>
|
|
30
76
|
[JSInvokable]
|
|
31
77
|
public async Task OnChange(ChangeDetail detail)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Types for the choose-from-list value helper (adapters/core/cfl-value.js).
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Narrow an `FdyCfl` value to the row a single-select can actually produce.
|
|
5
|
+
*
|
|
6
|
+
* `Row[]` is reachable only under `multiple` and `null` only under `clearable`, but the emitted
|
|
7
|
+
* type cannot say which of those props a given field sets, so every single-select handler has to
|
|
8
|
+
* prove it. Throws on an array rather than taking its first row: quietly picking one is how a
|
|
9
|
+
* `multiple` added later goes unnoticed.
|
|
10
|
+
*/
|
|
11
|
+
export declare function singleRow<Row>(value: Row | Row[] | null): Row | null;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Freeday, choose-from-list value narrowing (pure, zero dependencies).
|
|
2
|
+
//
|
|
3
|
+
// `FdyCfl` in Vue and React declares one model type, `Row | Row[] | null`, covering three prop
|
|
4
|
+
// combinations at once: the array is reachable only under `multiple`, the null only under
|
|
5
|
+
// `clearable`. A single-select screen therefore has to narrow a union that its own props make
|
|
6
|
+
// impossible, and the widening arrived across two releases that were additive from the kit's side
|
|
7
|
+
// (1.29.0 `clearable`, 1.42.0 `multiple`) and a broken build from the consumer's. Apps were each
|
|
8
|
+
// inventing the guard differently: a cast that hides the day `clearable` is added, a silent
|
|
9
|
+
// ignore, or a throw. The kit is what knows the invariant, so the guard is written once, here.
|
|
10
|
+
//
|
|
11
|
+
// Blazor needs none of this: multi-select there is a separate `Values` / `ValuesChanged` pair,
|
|
12
|
+
// because a nullable union is not a C# binding shape.
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Narrow an `FdyCfl` value to the row a single-select can actually produce.
|
|
16
|
+
*
|
|
17
|
+
* @template Row
|
|
18
|
+
* @param {Row | Row[] | null} value the value the component emitted
|
|
19
|
+
* @returns {Row | null} the picked row, or null when nothing is picked
|
|
20
|
+
* @throws {TypeError} when handed an array, which only a `multiple` CFL emits
|
|
21
|
+
*/
|
|
22
|
+
export function singleRow(value) {
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
throw new TypeError(
|
|
25
|
+
`singleRow() got an array of ${value.length} row(s). Only an FdyCfl with \`multiple\` emits ` +
|
|
26
|
+
'one, and a set of rows is not a single selection: read the array directly on that field, ' +
|
|
27
|
+
'or drop `multiple`.',
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return value == null ? null : value;
|
|
31
|
+
}
|
|
@@ -14,6 +14,8 @@ export interface FdyDrawerProps {
|
|
|
14
14
|
onClose: () => void;
|
|
15
15
|
side?: 'left' | 'right';
|
|
16
16
|
dismissible?: boolean;
|
|
17
|
+
/** aria-label for the × button. Default 'Close'. */
|
|
18
|
+
closeLabel?: string;
|
|
17
19
|
footer?: ReactNode;
|
|
18
20
|
children?: ReactNode;
|
|
19
21
|
}
|
|
@@ -47,7 +49,7 @@ export function FdyDrawer(props: FdyDrawerProps): JSX.Element {
|
|
|
47
49
|
<div className="fdy-drawer__header">
|
|
48
50
|
<h3 id={titleId} className="fdy-drawer__title">{props.title}</h3>
|
|
49
51
|
{dismissible && (
|
|
50
|
-
<button className="fdy-drawer__close" type="button" aria-label=
|
|
52
|
+
<button className="fdy-drawer__close" type="button" aria-label={props.closeLabel ?? 'Close'} onClick={props.onClose}>×</button>
|
|
51
53
|
)}
|
|
52
54
|
</div>
|
|
53
55
|
|
|
@@ -15,6 +15,8 @@ export interface FdyModalProps {
|
|
|
15
15
|
onClose: () => void;
|
|
16
16
|
size?: 'sm' | 'md' | 'lg' | 'wide';
|
|
17
17
|
dismissible?: boolean;
|
|
18
|
+
/** aria-label for the × button. Default 'Close'. */
|
|
19
|
+
closeLabel?: string;
|
|
18
20
|
footer?: ReactNode;
|
|
19
21
|
children?: ReactNode;
|
|
20
22
|
}
|
|
@@ -51,7 +53,7 @@ export function FdyModal(props: FdyModalProps): JSX.Element {
|
|
|
51
53
|
<div className="fdy-modal__header">
|
|
52
54
|
<h3 id={titleId} className="fdy-modal__title">{props.title}</h3>
|
|
53
55
|
{dismissible && (
|
|
54
|
-
<button className="fdy-modal__close" type="button" aria-label=
|
|
56
|
+
<button className="fdy-modal__close" type="button" aria-label={props.closeLabel ?? 'Close'} onClick={props.onClose}>×</button>
|
|
55
57
|
)}
|
|
56
58
|
</div>
|
|
57
59
|
|
|
@@ -34,6 +34,9 @@ export { FdyDateRange, type FdyDateRangeProps, type DateRangeValue } from './com
|
|
|
34
34
|
export { FdyAutocomplete, type FdyAutocompleteProps } from './components/FdyAutocomplete';
|
|
35
35
|
export { FdyCascade, type FdyCascadeProps, type CascadeNode } from './components/FdyCascade';
|
|
36
36
|
export { FdyCfl, type FdyCflProps, type CflColumn, type CflPage } from './components/FdyCfl';
|
|
37
|
+
/** Narrow `FdyCfl`'s `Row | Row[] | null` to the `Row | null` a single-select can produce; throws
|
|
38
|
+
* on the array only a `multiple` field emits. */
|
|
39
|
+
export { singleRow } from '../core/cfl-value';
|
|
37
40
|
export { FdyChart, type FdyChartProps, type FdyChartSeries } from './components/FdyChart';
|
|
38
41
|
export { FdyTable, type FdyTableProps } from './components/FdyTable';
|
|
39
42
|
/** The table's own footer, standalone, for a responsive list whose table is hidden at some
|
package/adapters/react/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export { FdyDateRange } from './components/FdyDateRange.tsx';
|
|
|
6
6
|
export { FdyAutocomplete } from './components/FdyAutocomplete.tsx';
|
|
7
7
|
export { FdyCascade } from './components/FdyCascade.tsx';
|
|
8
8
|
export { FdyCfl } from './components/FdyCfl.tsx';
|
|
9
|
+
export { singleRow } from '../core/cfl-value.js';
|
|
9
10
|
export { FdyChart } from './components/FdyChart.tsx';
|
|
10
11
|
export { FdyTable } from './components/FdyTable.tsx';
|
|
11
12
|
export { FdyTableFooter } from './components/FdyTableFooter.tsx';
|
|
@@ -16,6 +16,9 @@ const props = withDefaults(defineProps<{
|
|
|
16
16
|
title: string;
|
|
17
17
|
side?: 'left' | 'right';
|
|
18
18
|
dismissible?: boolean;
|
|
19
|
+
/** aria-label for the × button. Default 'Close'. Blazor has had `CloseLabel` since it
|
|
20
|
+
* shipped; these two hard-coded the string, so a non-English app could not rename it. */
|
|
21
|
+
closeLabel?: string;
|
|
19
22
|
}>(), { dismissible: true });
|
|
20
23
|
|
|
21
24
|
const emit = defineEmits<{
|
|
@@ -58,7 +61,7 @@ function onClick(e: MouseEvent): void {
|
|
|
58
61
|
<h3 :id="titleId" class="fdy-drawer__title">
|
|
59
62
|
<slot name="title">{{ title }}</slot>
|
|
60
63
|
</h3>
|
|
61
|
-
<button v-if="dismissible" class="fdy-drawer__close" type="button" aria-label="Close" @click="$emit('close')">×</button>
|
|
64
|
+
<button v-if="dismissible" class="fdy-drawer__close" type="button" :aria-label="closeLabel ?? 'Close'" @click="$emit('close')">×</button>
|
|
62
65
|
</div>
|
|
63
66
|
|
|
64
67
|
<div class="fdy-drawer__body">
|
|
@@ -18,6 +18,9 @@ const props = withDefaults(defineProps<{
|
|
|
18
18
|
title: string;
|
|
19
19
|
size?: 'sm' | 'md' | 'lg' | 'wide';
|
|
20
20
|
dismissible?: boolean;
|
|
21
|
+
/** aria-label for the × button. Default 'Close'. Blazor has had `CloseLabel` since it
|
|
22
|
+
* shipped; these two hard-coded the string, so a non-English app could not rename it. */
|
|
23
|
+
closeLabel?: string;
|
|
21
24
|
}>(), { dismissible: true });
|
|
22
25
|
|
|
23
26
|
const emit = defineEmits<{
|
|
@@ -65,7 +68,7 @@ function onClick(e: MouseEvent): void {
|
|
|
65
68
|
<h3 :id="titleId" class="fdy-modal__title">
|
|
66
69
|
<slot name="title">{{ title }}</slot>
|
|
67
70
|
</h3>
|
|
68
|
-
<button v-if="dismissible" class="fdy-modal__close" type="button" aria-label="Close" @click="$emit('close')">×</button>
|
|
71
|
+
<button v-if="dismissible" class="fdy-modal__close" type="button" :aria-label="closeLabel ?? 'Close'" @click="$emit('close')">×</button>
|
|
69
72
|
</div>
|
|
70
73
|
|
|
71
74
|
<div class="fdy-modal__body">
|
package/adapters/vue/index.d.ts
CHANGED
|
@@ -33,6 +33,9 @@ export { default as FdyAutocomplete } from './components/FdyAutocomplete.vue';
|
|
|
33
33
|
export { default as FdyCascade } from './components/FdyCascade.vue';
|
|
34
34
|
export type { CascadeNode } from './components/FdyCascade.vue';
|
|
35
35
|
export { default as FdyCfl } from './components/FdyCfl.vue';
|
|
36
|
+
/** Narrow `FdyCfl`'s `Row | Row[] | null` to the `Row | null` a single-select can produce; throws
|
|
37
|
+
* on the array only a `multiple` field emits. */
|
|
38
|
+
export { singleRow } from '../core/cfl-value';
|
|
36
39
|
export { default as FdyChart } from './components/FdyChart.vue';
|
|
37
40
|
export { default as FdyTable } from './components/FdyTable.vue';
|
|
38
41
|
/** The table's own footer, standalone, for a responsive list whose table is hidden at some
|
package/adapters/vue/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export { default as FdyDateRange } from './components/FdyDateRange.vue';
|
|
|
6
6
|
export { default as FdyAutocomplete } from './components/FdyAutocomplete.vue';
|
|
7
7
|
export { default as FdyCascade } from './components/FdyCascade.vue';
|
|
8
8
|
export { default as FdyCfl } from './components/FdyCfl.vue';
|
|
9
|
+
export { singleRow } from '../core/cfl-value.js';
|
|
9
10
|
export { default as FdyChart } from './components/FdyChart.vue';
|
|
10
11
|
export { default as FdyTable } from './components/FdyTable.vue';
|
|
11
12
|
export { default as FdyTableFooter } from './components/FdyTableFooter.vue';
|
|
@@ -40,6 +40,9 @@
|
|
|
40
40
|
var _pop = null;
|
|
41
41
|
function popCtl() { if (_pop === null && window.FreedayPopover) _pop = window.FreedayPopover.attach(listbox, input); return _pop; }
|
|
42
42
|
function open() {
|
|
43
|
+
/* The input carries these natively; a disabled one fires nothing, but a READONLY one still
|
|
44
|
+
takes focus and clicks, and the list used to open over a field nobody can edit. */
|
|
45
|
+
if (input.readOnly || input.disabled) return;
|
|
43
46
|
if (!listbox.hidden) return;
|
|
44
47
|
var p = popCtl(); if (p) p.show(); else listbox.hidden = false;
|
|
45
48
|
input.setAttribute('aria-expanded', 'true');
|
|
@@ -134,5 +137,18 @@
|
|
|
134
137
|
initAll();
|
|
135
138
|
}
|
|
136
139
|
|
|
137
|
-
|
|
140
|
+
/* Same contract as the combo's: the states live on the input natively, and a host that stops
|
|
141
|
+
re-rendering after hydration needs a way to change them afterwards. */
|
|
142
|
+
function setState(root, state) {
|
|
143
|
+
var input = root ? root.querySelector('input') : null;
|
|
144
|
+
if (!input || !state) return;
|
|
145
|
+
if (state.disabled != null) input.disabled = !!state.disabled;
|
|
146
|
+
if (state.readonly != null) input.readOnly = !!state.readonly;
|
|
147
|
+
if (state.invalid != null) {
|
|
148
|
+
if (state.invalid) input.setAttribute('aria-invalid', 'true');
|
|
149
|
+
else input.removeAttribute('aria-invalid');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
window.FreedayAutocomplete = { init: initAutocomplete, initAll: initAll, setState: setState };
|
|
138
154
|
})();
|
package/dist/freeday-carousel.js
CHANGED
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
'use strict';
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
/* User-facing strings.
|
|
22
|
-
* path, and every one overridable per element
|
|
23
|
-
*
|
|
24
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
21
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
22
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
23
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
24
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
25
|
+
* hard-coded further down. */
|
|
25
26
|
var TEXT = {
|
|
26
27
|
position: '{n} of {total}',
|
|
27
28
|
slide: 'Slide {n}'
|
package/dist/freeday-cascade.js
CHANGED
|
@@ -54,11 +54,14 @@
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
/* User-facing strings.
|
|
58
|
-
* path, and every one overridable per element
|
|
59
|
-
*
|
|
60
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
57
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
58
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
59
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
60
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
61
|
+
* hard-coded further down. */
|
|
61
62
|
var TEXT = {
|
|
63
|
+
label: 'Select',
|
|
64
|
+
placeholder: 'Select…',
|
|
62
65
|
back: 'Back one level',
|
|
63
66
|
submenu: '{label}, submenu'
|
|
64
67
|
};
|
|
@@ -88,8 +91,8 @@
|
|
|
88
91
|
var root = sourceUl ? parse(sourceUl) : [];
|
|
89
92
|
if (sourceUl) sourceUl.remove();
|
|
90
93
|
|
|
91
|
-
var label = wrap.getAttribute('data-label') || '
|
|
92
|
-
var placeholder = wrap.getAttribute('data-placeholder') || '
|
|
94
|
+
var label = wrap.getAttribute('data-label') || textOf(wrap, 'label');
|
|
95
|
+
var placeholder = wrap.getAttribute('data-placeholder') || textOf(wrap, 'placeholder');
|
|
93
96
|
var sep = wrap.getAttribute('data-separator') || ' / ';
|
|
94
97
|
|
|
95
98
|
var trigger = document.createElement('button');
|
|
@@ -204,7 +207,44 @@
|
|
|
204
207
|
|
|
205
208
|
var _pop = null;
|
|
206
209
|
function popCtl() { if (_pop === null && window.FreedayPopover) _pop = window.FreedayPopover.attach(panel, trigger); return _pop; }
|
|
210
|
+
|
|
211
|
+
/* The three field states the CSS has always styled (`:disabled`, `[aria-readonly="true"]`,
|
|
212
|
+
`[aria-invalid="true"]`) and this enhancer never set, so only the stacks that re-implement
|
|
213
|
+
the control natively had them. Read from the seed at init and settable afterwards, because
|
|
214
|
+
a host that renders once — every Blazor wrapper does, `ShouldRender => false` — cannot
|
|
215
|
+
express a later change any other way. */
|
|
216
|
+
function flagOf(name) {
|
|
217
|
+
var v = wrap.getAttribute('data-' + name);
|
|
218
|
+
return v != null && v !== 'false';
|
|
219
|
+
}
|
|
220
|
+
/* Named `state*` to match the datepicker, where `is*` collided with an older function. */
|
|
221
|
+
var stateDisabled = flagOf('disabled');
|
|
222
|
+
var stateReadonly = flagOf('readonly');
|
|
223
|
+
var stateInvalid = flagOf('invalid');
|
|
224
|
+
/* `data-id` and `data-describedby`: the trigger this enhancer BUILDS is the element a form
|
|
225
|
+
has to point its label and its error text at, and the raw path had no way to say so. */
|
|
226
|
+
if (wrap.getAttribute('data-id')) trigger.id = wrap.getAttribute('data-id');
|
|
227
|
+
if (wrap.getAttribute('data-describedby')) trigger.setAttribute('aria-describedby', wrap.getAttribute('data-describedby'));
|
|
228
|
+
|
|
229
|
+
function applyState() {
|
|
230
|
+
trigger.disabled = stateDisabled;
|
|
231
|
+
if (stateReadonly) trigger.setAttribute('aria-readonly', 'true');
|
|
232
|
+
else trigger.removeAttribute('aria-readonly');
|
|
233
|
+
if (stateInvalid) trigger.setAttribute('aria-invalid', 'true');
|
|
234
|
+
else trigger.removeAttribute('aria-invalid');
|
|
235
|
+
wrap.classList.toggle('fdy-cascade--error', stateInvalid);
|
|
236
|
+
if ((stateDisabled || stateReadonly) && !panel.hidden) close(false);
|
|
237
|
+
}
|
|
238
|
+
function setState(next) {
|
|
239
|
+
if (!next) return;
|
|
240
|
+
if (next.disabled != null) stateDisabled = !!next.disabled;
|
|
241
|
+
if (next.readonly != null) stateReadonly = !!next.readonly;
|
|
242
|
+
if (next.invalid != null) stateInvalid = !!next.invalid;
|
|
243
|
+
applyState();
|
|
244
|
+
}
|
|
245
|
+
|
|
207
246
|
function open() {
|
|
247
|
+
if (stateDisabled || stateReadonly) return;
|
|
208
248
|
if (!panel.hidden) return;
|
|
209
249
|
// Re-open at the selected leaf's level for quick re-selection.
|
|
210
250
|
var trail = selectedValue ? pathTo(root, selectedValue, []) : null;
|
|
@@ -259,8 +299,11 @@
|
|
|
259
299
|
valueSpan.classList.add('fdy-cascade__value--placeholder');
|
|
260
300
|
}
|
|
261
301
|
|
|
302
|
+
applyState();
|
|
303
|
+
|
|
262
304
|
var api = {
|
|
263
305
|
wrap: wrap,
|
|
306
|
+
setState: setState,
|
|
264
307
|
getValue: function () { return selectedValue; },
|
|
265
308
|
clear: function () { selectedValue = ''; valueSpan.textContent = placeholder; valueSpan.classList.add('fdy-cascade__value--placeholder'); }
|
|
266
309
|
};
|
|
@@ -281,5 +324,13 @@
|
|
|
281
324
|
initAll();
|
|
282
325
|
}
|
|
283
326
|
|
|
284
|
-
window.FreedayCascade = {
|
|
327
|
+
window.FreedayCascade = {
|
|
328
|
+
init: initCascade,
|
|
329
|
+
initAll: initAll,
|
|
330
|
+
/* Same reason as the datepicker's: a seed rendered once still has to be lockable later. */
|
|
331
|
+
setState: function (root, state) {
|
|
332
|
+
var api = root && root._fdyCascade ? root._fdyCascade : null;
|
|
333
|
+
if (api && api.setState) api.setState(state);
|
|
334
|
+
}
|
|
335
|
+
};
|
|
285
336
|
})();
|
package/dist/freeday-cfl.js
CHANGED
|
@@ -34,10 +34,11 @@
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
/* User-facing strings.
|
|
38
|
-
* path, and every one overridable per element
|
|
39
|
-
*
|
|
40
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
37
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
38
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
39
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
40
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
41
|
+
* hard-coded further down. */
|
|
41
42
|
var TEXT = {
|
|
42
43
|
selected: '{n} selected'
|
|
43
44
|
};
|
package/dist/freeday-chart.js
CHANGED
|
@@ -29,6 +29,28 @@
|
|
|
29
29
|
|
|
30
30
|
var NS = 'http://www.w3.org/2000/svg';
|
|
31
31
|
|
|
32
|
+
/* User-facing strings. Two of them, and both shipped wrong until 2.2.0: the legend's fallback
|
|
33
|
+
* label read `Seri 1` — Indonesian, three months after 2.0.0 turned every enhancer English —
|
|
34
|
+
* and the donut's centre caption was hard-coded, so no host could rename it. Neither was
|
|
35
|
+
* reachable by the guards: one goes into the DOM through `createTextNode`, the other through
|
|
36
|
+
* `innerHTML`, and both guards look for `textContent` / `setAttribute`. Overridable per element
|
|
37
|
+
* with `data-fdy-text-<key>`, like every other enhancer. */
|
|
38
|
+
var TEXT = {
|
|
39
|
+
series: 'Series {n}',
|
|
40
|
+
total: 'Total'
|
|
41
|
+
};
|
|
42
|
+
function textAttr(root, key) {
|
|
43
|
+
if (!root || !root.getAttribute) return null;
|
|
44
|
+
var kebab = root.getAttribute('data-fdy-text-' + key.replace(/[A-Z]/g, function (c) { return '-' + c.toLowerCase(); }));
|
|
45
|
+
return kebab != null && kebab !== '' ? kebab : root.getAttribute('data-fdy-text-' + key);
|
|
46
|
+
}
|
|
47
|
+
function textOf(root, key, vars) {
|
|
48
|
+
var custom = textAttr(root, key);
|
|
49
|
+
var s = custom != null && custom !== '' ? custom : TEXT[key];
|
|
50
|
+
if (vars) for (var k in vars) if (Object.prototype.hasOwnProperty.call(vars, k)) s = s.split('{' + k + '}').join(vars[k]);
|
|
51
|
+
return s;
|
|
52
|
+
}
|
|
53
|
+
|
|
32
54
|
// Categorical chart palette: 8 validated fixed-order slots (--chart-1..8). Series index i
|
|
33
55
|
// (0-based) -> slot i+1; series beyond the 8-slot cap reuse --chart-8 (never cycled).
|
|
34
56
|
function chartSlotVar(i) { return 'var(--chart-' + (i < 8 ? i + 1 : 8) + ')'; }
|
|
@@ -237,7 +259,7 @@
|
|
|
237
259
|
var li = document.createElement('li');
|
|
238
260
|
var sw = document.createElement('span'); sw.className = 'fdy-chart__swatch'; sw.style.background = colorFor(si);
|
|
239
261
|
li.appendChild(sw);
|
|
240
|
-
li.appendChild(document.createTextNode(s.label || (
|
|
262
|
+
li.appendChild(document.createTextNode(s.label || textOf(el, 'series', { n: si + 1 })));
|
|
241
263
|
legend.appendChild(li);
|
|
242
264
|
});
|
|
243
265
|
el.appendChild(legend);
|
|
@@ -411,8 +433,16 @@
|
|
|
411
433
|
ring.style.background = 'conic-gradient(' + stops.join(',') + ')';
|
|
412
434
|
var center = document.createElement('div'); center.className = 'fdy-donut__center';
|
|
413
435
|
var centerLabel = el.getAttribute('data-fdy-center');
|
|
414
|
-
|
|
415
|
-
|
|
436
|
+
var centerValue = document.createElement('b');
|
|
437
|
+
centerValue.textContent = centerLabel != null ? centerLabel : String(total);
|
|
438
|
+
center.appendChild(centerValue);
|
|
439
|
+
/* Built rather than assigned as innerHTML: the caption is overridable now, and an author's
|
|
440
|
+
string is not markup. */
|
|
441
|
+
if (!centerLabel) {
|
|
442
|
+
var centerCaption = document.createElement('span');
|
|
443
|
+
centerCaption.textContent = textOf(el, 'total');
|
|
444
|
+
center.appendChild(centerCaption);
|
|
445
|
+
}
|
|
416
446
|
ring.appendChild(center);
|
|
417
447
|
var svg = svgEl('svg');
|
|
418
448
|
svg.setAttribute('class', 'fdy-donut__hit');
|