@hawiyah/vue 0.0.0-stage → 0.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.
Files changed (81) hide show
  1. package/LICENSE +73 -0
  2. package/NOTICE +19 -0
  3. package/PORTAL.md +70 -0
  4. package/README.md +388 -2
  5. package/dist/calendar.d.ts +183 -0
  6. package/dist/calendar.d.ts.map +1 -0
  7. package/dist/calendar.js +448 -0
  8. package/dist/calendar.js.map +1 -0
  9. package/dist/content-navigation.d.ts +61 -0
  10. package/dist/content-navigation.d.ts.map +1 -0
  11. package/dist/content-navigation.js +31 -0
  12. package/dist/content-navigation.js.map +1 -0
  13. package/dist/content.d.ts +392 -0
  14. package/dist/content.d.ts.map +1 -0
  15. package/dist/content.js +305 -0
  16. package/dist/content.js.map +1 -0
  17. package/dist/controls.d.ts +657 -0
  18. package/dist/controls.d.ts.map +1 -0
  19. package/dist/controls.js +679 -0
  20. package/dist/controls.js.map +1 -0
  21. package/dist/display.d.ts +387 -0
  22. package/dist/display.d.ts.map +1 -0
  23. package/dist/display.js +293 -0
  24. package/dist/display.js.map +1 -0
  25. package/dist/expansion-interactive.d.ts +476 -0
  26. package/dist/expansion-interactive.d.ts.map +1 -0
  27. package/dist/expansion-interactive.js +613 -0
  28. package/dist/expansion-interactive.js.map +1 -0
  29. package/dist/file-upload-list.d.ts +54 -0
  30. package/dist/file-upload-list.d.ts.map +1 -0
  31. package/dist/file-upload-list.js +21 -0
  32. package/dist/file-upload-list.js.map +1 -0
  33. package/dist/forms.d.ts +388 -0
  34. package/dist/forms.d.ts.map +1 -0
  35. package/dist/forms.js +156 -0
  36. package/dist/forms.js.map +1 -0
  37. package/dist/index.d.ts +26 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +16 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/interactive.d.ts +240 -0
  42. package/dist/interactive.d.ts.map +1 -0
  43. package/dist/interactive.js +341 -0
  44. package/dist/interactive.js.map +1 -0
  45. package/dist/layout.d.ts +755 -0
  46. package/dist/layout.d.ts.map +1 -0
  47. package/dist/layout.js +101 -0
  48. package/dist/layout.js.map +1 -0
  49. package/dist/mobile-top-bar.d.ts +47 -0
  50. package/dist/mobile-top-bar.d.ts.map +1 -0
  51. package/dist/mobile-top-bar.js +18 -0
  52. package/dist/mobile-top-bar.js.map +1 -0
  53. package/dist/native-attributes.d.ts +38 -0
  54. package/dist/native-attributes.d.ts.map +1 -0
  55. package/dist/native-attributes.js +27 -0
  56. package/dist/native-attributes.js.map +1 -0
  57. package/dist/navigation-drawer.d.ts +87 -0
  58. package/dist/navigation-drawer.d.ts.map +1 -0
  59. package/dist/navigation-drawer.js +56 -0
  60. package/dist/navigation-drawer.js.map +1 -0
  61. package/dist/patterns.d.ts +455 -0
  62. package/dist/patterns.d.ts.map +1 -0
  63. package/dist/patterns.js +355 -0
  64. package/dist/patterns.js.map +1 -0
  65. package/dist/portal.d.ts +174 -0
  66. package/dist/portal.d.ts.map +1 -0
  67. package/dist/portal.js +36 -0
  68. package/dist/portal.js.map +1 -0
  69. package/dist/search-field.d.ts +87 -0
  70. package/dist/search-field.d.ts.map +1 -0
  71. package/dist/search-field.js +123 -0
  72. package/dist/search-field.js.map +1 -0
  73. package/dist/shared.d.ts +46 -0
  74. package/dist/shared.d.ts.map +1 -0
  75. package/dist/shared.js +40 -0
  76. package/dist/shared.js.map +1 -0
  77. package/dist/site-verification.d.ts +63 -0
  78. package/dist/site-verification.d.ts.map +1 -0
  79. package/dist/site-verification.js +34 -0
  80. package/dist/site-verification.js.map +1 -0
  81. package/package.json +53 -3
@@ -0,0 +1,183 @@
1
+ import type { PropType } from "vue";
2
+ import type { CalendarSystem } from "@hawiyah/core";
3
+ export declare const Calendar: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
4
+ readonly modelValue: StringConstructor;
5
+ readonly defaultValue: {
6
+ readonly type: StringConstructor;
7
+ readonly default: "";
8
+ };
9
+ readonly month: StringConstructor;
10
+ readonly min: StringConstructor;
11
+ readonly max: StringConstructor;
12
+ readonly locale: {
13
+ readonly type: StringConstructor;
14
+ readonly default: "ar-SA";
15
+ };
16
+ readonly calendar: {
17
+ readonly type: PropType<CalendarSystem>;
18
+ readonly default: "gregory";
19
+ };
20
+ readonly weekStartsOn: {
21
+ readonly type: NumberConstructor;
22
+ readonly default: 0;
23
+ };
24
+ readonly disabled: BooleanConstructor;
25
+ readonly readonly: BooleanConstructor;
26
+ readonly label: StringConstructor;
27
+ readonly previousLabel: StringConstructor;
28
+ readonly nextLabel: StringConstructor;
29
+ readonly unsupportedMessage: StringConstructor;
30
+ readonly isDateDisabled: PropType<(value: string) => boolean>;
31
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
32
+ [key: string]: any;
33
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
34
+ "update:modelValue": (_value: string) => true;
35
+ "update:month": (_value: string) => true;
36
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
37
+ readonly modelValue: StringConstructor;
38
+ readonly defaultValue: {
39
+ readonly type: StringConstructor;
40
+ readonly default: "";
41
+ };
42
+ readonly month: StringConstructor;
43
+ readonly min: StringConstructor;
44
+ readonly max: StringConstructor;
45
+ readonly locale: {
46
+ readonly type: StringConstructor;
47
+ readonly default: "ar-SA";
48
+ };
49
+ readonly calendar: {
50
+ readonly type: PropType<CalendarSystem>;
51
+ readonly default: "gregory";
52
+ };
53
+ readonly weekStartsOn: {
54
+ readonly type: NumberConstructor;
55
+ readonly default: 0;
56
+ };
57
+ readonly disabled: BooleanConstructor;
58
+ readonly readonly: BooleanConstructor;
59
+ readonly label: StringConstructor;
60
+ readonly previousLabel: StringConstructor;
61
+ readonly nextLabel: StringConstructor;
62
+ readonly unsupportedMessage: StringConstructor;
63
+ readonly isDateDisabled: PropType<(value: string) => boolean>;
64
+ }>> & Readonly<{
65
+ "onUpdate:modelValue"?: ((_value: string) => any) | undefined;
66
+ "onUpdate:month"?: ((_value: string) => any) | undefined;
67
+ }>, {
68
+ readonly disabled: boolean;
69
+ readonly defaultValue: string;
70
+ readonly readonly: boolean;
71
+ readonly locale: string;
72
+ readonly calendar: CalendarSystem;
73
+ readonly weekStartsOn: number;
74
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
75
+ export declare const DatePicker: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
76
+ calendar: {
77
+ type: PropType<CalendarSystem>;
78
+ default: undefined;
79
+ };
80
+ calendarSwitchable: {
81
+ type: BooleanConstructor;
82
+ default: boolean;
83
+ };
84
+ calendarSystemLabel: StringConstructor;
85
+ gregoryLabel: StringConstructor;
86
+ hijriLabel: StringConstructor;
87
+ calendarLabel: StringConstructor;
88
+ openLabel: StringConstructor;
89
+ closeLabel: StringConstructor;
90
+ invalidMessage: StringConstructor;
91
+ label: StringConstructor;
92
+ hint: StringConstructor;
93
+ error: StringConstructor;
94
+ required: {
95
+ readonly type: BooleanConstructor;
96
+ readonly default: undefined;
97
+ };
98
+ modelValue: StringConstructor;
99
+ defaultValue: {
100
+ readonly type: StringConstructor;
101
+ readonly default: "";
102
+ };
103
+ month: StringConstructor;
104
+ min: StringConstructor;
105
+ max: StringConstructor;
106
+ locale: {
107
+ readonly type: StringConstructor;
108
+ readonly default: "ar-SA";
109
+ };
110
+ weekStartsOn: {
111
+ readonly type: NumberConstructor;
112
+ readonly default: 0;
113
+ };
114
+ disabled: BooleanConstructor;
115
+ readonly: BooleanConstructor;
116
+ previousLabel: StringConstructor;
117
+ nextLabel: StringConstructor;
118
+ unsupportedMessage: StringConstructor;
119
+ isDateDisabled: PropType<(value: string) => boolean>;
120
+ }>, () => string | number | boolean | void | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
121
+ [key: string]: any;
122
+ }> | import("vue").VNodeArrayChildren | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
123
+ "update:modelValue": (_value: string) => true;
124
+ "update:calendar": (_value: CalendarSystem) => true;
125
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
126
+ calendar: {
127
+ type: PropType<CalendarSystem>;
128
+ default: undefined;
129
+ };
130
+ calendarSwitchable: {
131
+ type: BooleanConstructor;
132
+ default: boolean;
133
+ };
134
+ calendarSystemLabel: StringConstructor;
135
+ gregoryLabel: StringConstructor;
136
+ hijriLabel: StringConstructor;
137
+ calendarLabel: StringConstructor;
138
+ openLabel: StringConstructor;
139
+ closeLabel: StringConstructor;
140
+ invalidMessage: StringConstructor;
141
+ label: StringConstructor;
142
+ hint: StringConstructor;
143
+ error: StringConstructor;
144
+ required: {
145
+ readonly type: BooleanConstructor;
146
+ readonly default: undefined;
147
+ };
148
+ modelValue: StringConstructor;
149
+ defaultValue: {
150
+ readonly type: StringConstructor;
151
+ readonly default: "";
152
+ };
153
+ month: StringConstructor;
154
+ min: StringConstructor;
155
+ max: StringConstructor;
156
+ locale: {
157
+ readonly type: StringConstructor;
158
+ readonly default: "ar-SA";
159
+ };
160
+ weekStartsOn: {
161
+ readonly type: NumberConstructor;
162
+ readonly default: 0;
163
+ };
164
+ disabled: BooleanConstructor;
165
+ readonly: BooleanConstructor;
166
+ previousLabel: StringConstructor;
167
+ nextLabel: StringConstructor;
168
+ unsupportedMessage: StringConstructor;
169
+ isDateDisabled: PropType<(value: string) => boolean>;
170
+ }>> & Readonly<{
171
+ "onUpdate:modelValue"?: ((_value: string) => any) | undefined;
172
+ "onUpdate:calendar"?: ((_value: CalendarSystem) => any) | undefined;
173
+ }>, {
174
+ disabled: boolean;
175
+ required: boolean;
176
+ defaultValue: string;
177
+ readonly: boolean;
178
+ locale: string;
179
+ calendar: CalendarSystem;
180
+ weekStartsOn: number;
181
+ calendarSwitchable: boolean;
182
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
183
+ //# sourceMappingURL=calendar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../src/calendar.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAapC,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,eAAe,CAAC;AAiCjE,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;uBAVS,QAAQ,CAAC,cAAc,CAAC;;;;;;;;;;;;;6BAQxB,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;;;;kCAOhC,MAAM;6BACX,MAAM;;;;;;;;;;;;;;;uBAhBL,QAAQ,CAAC,cAAc,CAAC;;;;;;;;;;;;;6BAQxB,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;;;;;;;;;;;4EAgThE,CAAC;AACH,eAAO,MAAM,UAAU;;cAMS,QAAQ,CAAC,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAvT1B,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;;;;kCAkUhC,MAAM;gCACR,cAAc;;;cAZd,QAAQ,CAAC,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAvT1B,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;;;;;;;;;;;;;4EAyfhE,CAAC"}
@@ -0,0 +1,448 @@
1
+ import { computed, defineComponent, h, mergeProps, nextTick, ref, watch, watchEffect, } from "vue";
2
+ import { getCalendarLabels, formatCalendarDate, getCalendarMonth, getDirection, isDateInRange, isDisabled, isCalendarDateSupported, UMALQURA_MIN_DATE, UMALQURA_MAX_DATE, moveCalendarDate, } from "@hawiyah/core";
3
+ import { Button, decorate } from "./forms.js";
4
+ import { Dialog, Tooltip } from "./interactive.js";
5
+ import { decorationProps, joinIds, useField, useFormReset, useValue, } from "./shared.js";
6
+ function dateMessage(message) {
7
+ return message.split(/([0-9٠-٩]{4}-[0-9٠-٩]{2}-[0-9٠-٩]{2})/u).map(part => /^[0-9٠-٩]{4}-[0-9٠-٩]{2}-[0-9٠-٩]{2}$/u.test(part) ? h("bdi", { dir: "ltr" }, part) : part);
8
+ }
9
+ const calendarProps = {
10
+ modelValue: String,
11
+ defaultValue: { type: String, default: "" },
12
+ month: String,
13
+ min: String,
14
+ max: String,
15
+ locale: { type: String, default: "ar-SA" },
16
+ calendar: { type: String, default: "gregory" },
17
+ weekStartsOn: { type: Number, default: 0 },
18
+ disabled: Boolean,
19
+ readonly: Boolean,
20
+ label: String,
21
+ previousLabel: String,
22
+ nextLabel: String,
23
+ unsupportedMessage: String,
24
+ isDateDisabled: Function,
25
+ };
26
+ export const Calendar = defineComponent({
27
+ name: "HwCalendar",
28
+ inheritAttrs: false,
29
+ props: calendarProps,
30
+ emits: {
31
+ "update:modelValue": (_value) => true,
32
+ "update:month": (_value) => true,
33
+ },
34
+ setup(props, { attrs, emit, expose }) {
35
+ const copy = computed(() => getCalendarLabels(props.locale));
36
+ const model = useValue(() => props.modelValue, props.defaultValue, (v) => emit("update:modelValue", v));
37
+ const valid = (value) => value && isDateInRange(value) ? value : undefined;
38
+ const month = ref(valid(props.month) ?? valid(model.value.value));
39
+ const focused = ref();
40
+ const buttons = new Map();
41
+ const hidden = ref();
42
+ const blocked = (value) => !isCalendarDateSupported(value, props.calendar) ||
43
+ !isDateInRange(value, props.min, props.max) ||
44
+ Boolean(props.isDateDisabled?.(value));
45
+ const page = computed(() => getCalendarMonth({
46
+ month: month.value,
47
+ value: valid(model.value.value),
48
+ min: props.min,
49
+ max: props.max,
50
+ locale: props.locale,
51
+ calendar: props.calendar,
52
+ weekStartsOn: props.weekStartsOn,
53
+ fixedWeeks: true,
54
+ }));
55
+ const focusValue = computed(() => page.value.days.find((day) => day.value === focused.value && !blocked(day.value))?.value ??
56
+ page.value.days.find((day) => day.selected && !blocked(day.value))
57
+ ?.value ??
58
+ page.value.days.find((day) => !day.outside && !blocked(day.value))
59
+ ?.value ??
60
+ page.value.days.find((day) => !blocked(day.value))?.value);
61
+ function changeMonth(value) {
62
+ month.value = value;
63
+ emit("update:month", value);
64
+ }
65
+ function focusDate(value) {
66
+ if (props.min && value < props.min)
67
+ value = props.min;
68
+ if (props.max && value > props.max)
69
+ value = props.max;
70
+ if (blocked(value))
71
+ return;
72
+ focused.value = value;
73
+ if (!page.value.days.some((day) => day.value === value && !day.outside))
74
+ changeMonth(value);
75
+ void nextTick(() => buttons.get(value)?.focus());
76
+ }
77
+ watch(() => props.month, (value) => {
78
+ if (valid(value))
79
+ month.value = value;
80
+ });
81
+ watch(() => props.modelValue, (value) => {
82
+ if (valid(value)) {
83
+ month.value = value;
84
+ focused.value = value;
85
+ }
86
+ });
87
+ useFormReset(hidden, () => {
88
+ model.set(props.defaultValue);
89
+ month.value = valid(props.month) ?? valid(props.defaultValue);
90
+ focused.value = undefined;
91
+ });
92
+ expose({
93
+ focus: () => {
94
+ if (focusValue.value)
95
+ buttons.get(focusValue.value)?.focus();
96
+ },
97
+ });
98
+ function move(value, amount, unit) {
99
+ try {
100
+ return moveCalendarDate(value, amount, unit, props.calendar);
101
+ }
102
+ catch (error) {
103
+ if (error instanceof RangeError)
104
+ return undefined;
105
+ throw error;
106
+ }
107
+ }
108
+ function dateLabel(day) {
109
+ return isCalendarDateSupported(day.value, props.calendar)
110
+ ? formatCalendarDate(day.value, {
111
+ locale: props.locale,
112
+ calendar: props.calendar,
113
+ dateStyle: "full",
114
+ })
115
+ : day.value;
116
+ }
117
+ function keyboard(event, value, index) {
118
+ if (props.disabled)
119
+ return;
120
+ let amount = 0;
121
+ let unit = "day";
122
+ switch (event.key) {
123
+ case "ArrowRight":
124
+ amount =
125
+ getDirection(event.currentTarget) === "rtl" ? -1 : 1;
126
+ break;
127
+ case "ArrowLeft":
128
+ amount =
129
+ getDirection(event.currentTarget) === "rtl" ? 1 : -1;
130
+ break;
131
+ case "ArrowDown":
132
+ amount = 7;
133
+ break;
134
+ case "ArrowUp":
135
+ amount = -7;
136
+ break;
137
+ case "Home":
138
+ amount = -(index % 7);
139
+ break;
140
+ case "End":
141
+ amount = 6 - (index % 7);
142
+ break;
143
+ case "PageUp":
144
+ amount = -1;
145
+ unit = event.shiftKey ? "year" : "month";
146
+ break;
147
+ case "PageDown":
148
+ amount = 1;
149
+ unit = event.shiftKey ? "year" : "month";
150
+ break;
151
+ default:
152
+ return;
153
+ }
154
+ event.preventDefault();
155
+ let candidate = move(value, amount, unit);
156
+ if (!candidate)
157
+ return;
158
+ if (props.min && candidate < props.min)
159
+ candidate = props.min;
160
+ if (props.max && candidate > props.max)
161
+ candidate = props.max;
162
+ for (let attempt = 0; attempt < 366 && blocked(candidate); attempt++) {
163
+ const next = move(candidate, amount < 0 ? -1 : 1, "day");
164
+ if (!next || !isDateInRange(next, props.min, props.max))
165
+ return;
166
+ candidate = next;
167
+ }
168
+ focusDate(candidate);
169
+ }
170
+ return () => {
171
+ const monthStart = page.value.days.find((day) => !day.outside)?.value;
172
+ const previousAllowed = page.value.previous !== monthStart &&
173
+ getCalendarMonth({
174
+ month: page.value.previous,
175
+ min: props.min,
176
+ max: props.max,
177
+ calendar: props.calendar,
178
+ }).days.some((day) => !day.outside && !blocked(day.value));
179
+ const nextAllowed = page.value.next !== monthStart &&
180
+ getCalendarMonth({
181
+ month: page.value.next,
182
+ min: props.min,
183
+ max: props.max,
184
+ calendar: props.calendar,
185
+ }).days.some((day) => !day.outside && !blocked(day.value));
186
+ return h("div", mergeProps(attrs, { class: "hw-calendar" }), [
187
+ h("input", {
188
+ ref: hidden,
189
+ type: "hidden",
190
+ name: attrs.name,
191
+ form: attrs.form,
192
+ value: model.value.value,
193
+ disabled: props.disabled,
194
+ }),
195
+ h("div", { class: "hw-calendar-toolbar" }, [
196
+ h(Button, {
197
+ variant: "ghost",
198
+ "aria-label": (props.previousLabel ?? copy.value.previous),
199
+ class: "hw-calendar-prev",
200
+ disabled: props.disabled || !previousAllowed,
201
+ onClick: () => changeMonth(page.value.previous),
202
+ }, () => (props.previousLabel ?? copy.value.previous)),
203
+ h("div", { class: "hw-calendar-heading" }, [
204
+ h("span", { "aria-live": "polite", "aria-atomic": "true" }, page.value.label),
205
+ props.calendar === "islamic-umalqura" && h(Tooltip, { class: "hw-calendar-info", content: `${copy.value.range}: \u2066${UMALQURA_MIN_DATE} – ${UMALQURA_MAX_DATE}\u2069` }, () => h("span", { tabindex: 0, role: "img", class: "hw-calendar-info-icon hw-tooltip-trigger", "aria-label": copy.value.range }, "i")),
206
+ ]),
207
+ h(Button, {
208
+ variant: "ghost",
209
+ "aria-label": (props.nextLabel ?? copy.value.next),
210
+ class: "hw-calendar-next",
211
+ disabled: props.disabled || !nextAllowed,
212
+ onClick: () => changeMonth(page.value.next),
213
+ }, () => (props.nextLabel ?? copy.value.next)),
214
+ ]),
215
+ model.value.value &&
216
+ !isCalendarDateSupported(model.value.value, props.calendar) &&
217
+ h("p", { role: "status", class: "hw-sr-only" }, dateMessage((props.unsupportedMessage ?? copy.value.unsupported))),
218
+ h("table", {
219
+ role: "grid",
220
+ "aria-label": `${props.label ?? copy.value.calendar}: ${page.value.label}`,
221
+ "aria-readonly": props.readonly || undefined,
222
+ }, [
223
+ h("thead", {}, h("tr", {}, page.value.weekdays.map((day, index) => h("th", { key: index, scope: "col" }, day)))),
224
+ h("tbody", {}, Array.from({ length: 6 }, (_, week) => h("tr",
225
+ // Keep overlapping weeks keyed by date when the visible month changes.
226
+ // A positional row key can mount then remove the same date ref.
227
+ { key: page.value.days[week * 7]?.value ?? week }, page.value.days
228
+ .slice(week * 7, week * 7 + 7)
229
+ .map((day, dayIndex) => h("td", {
230
+ key: day.value,
231
+ role: "gridcell",
232
+ "aria-selected": day.selected,
233
+ }, h("button", {
234
+ ref: (element) => {
235
+ if (element)
236
+ buttons.set(day.value, element);
237
+ else
238
+ buttons.delete(day.value);
239
+ },
240
+ type: "button",
241
+ class: "hw-calendar-day",
242
+ "data-outside": day.outside ? "true" : undefined,
243
+ "data-selected": day.selected ? "true" : undefined,
244
+ "aria-current": day.today ? "date" : undefined,
245
+ "aria-label": dateLabel(day),
246
+ disabled: props.disabled ||
247
+ day.disabled ||
248
+ blocked(day.value),
249
+ tabindex: focusValue.value === day.value ? 0 : -1,
250
+ onFocus: () => {
251
+ focused.value = day.value;
252
+ },
253
+ onKeydown: (event) => keyboard(event, day.value, week * 7 + dayIndex),
254
+ onClick: () => {
255
+ if (!props.disabled &&
256
+ !props.readonly &&
257
+ !blocked(day.value)) {
258
+ model.set(day.value);
259
+ focused.value = day.value;
260
+ if (day.outside)
261
+ changeMonth(day.value);
262
+ }
263
+ },
264
+ }, day.label)))))),
265
+ ]),
266
+ ]);
267
+ };
268
+ },
269
+ });
270
+ export const DatePicker = defineComponent({
271
+ name: "HwDatePicker",
272
+ inheritAttrs: false,
273
+ props: {
274
+ ...calendarProps,
275
+ ...decorationProps,
276
+ calendar: { type: String, default: undefined },
277
+ calendarSwitchable: { type: Boolean, default: true },
278
+ calendarSystemLabel: String,
279
+ gregoryLabel: String,
280
+ hijriLabel: String,
281
+ calendarLabel: String,
282
+ openLabel: String,
283
+ closeLabel: String,
284
+ invalidMessage: String,
285
+ },
286
+ emits: {
287
+ "update:modelValue": (_value) => true,
288
+ "update:calendar": (_value) => true,
289
+ },
290
+ setup(props, { attrs, emit, expose }) {
291
+ const copy = computed(() => getCalendarLabels(props.locale));
292
+ const field = useField(props, attrs);
293
+ const element = ref();
294
+ const calendar = ref();
295
+ const open = ref(false);
296
+ const calendarSystem = useValue(() => props.calendar, "gregory", (v) => emit("update:calendar", v));
297
+ const model = useValue(() => props.modelValue, props.defaultValue, (v) => emit("update:modelValue", v));
298
+ const draft = ref(model.value.value);
299
+ const editing = ref(false);
300
+ watch(() => model.value.value, value => { draft.value = value; });
301
+ const blocked = () => props.disabled || props.readonly || !element.value || isDisabled(element.value);
302
+ const commit = (value) => {
303
+ const canonical = !value || isDateInRange(value);
304
+ draft.value = canonical && props.modelValue !== undefined ? props.modelValue : value;
305
+ if (canonical)
306
+ model.set(value);
307
+ };
308
+ const show = () => { if (!blocked()) {
309
+ open.value = true;
310
+ void nextTick(() => calendar.value?.focus());
311
+ } };
312
+ watch(() => props.disabled || props.readonly, disabled => { if (disabled)
313
+ open.value = false; });
314
+ const minimum = computed(() => calendarSystem.value.value === "islamic-umalqura" &&
315
+ (!props.min || props.min < UMALQURA_MIN_DATE)
316
+ ? UMALQURA_MIN_DATE
317
+ : props.min);
318
+ const maximum = computed(() => calendarSystem.value.value === "islamic-umalqura" &&
319
+ (!props.max || props.max > UMALQURA_MAX_DATE)
320
+ ? UMALQURA_MAX_DATE
321
+ : props.max);
322
+ const formattedDate = computed(() => {
323
+ const value = model.value.value;
324
+ return value && isCalendarDateSupported(value, calendarSystem.value.value)
325
+ ? formatCalendarDate(value, { locale: props.locale, calendar: calendarSystem.value.value, dateStyle: "full" })
326
+ : "";
327
+ });
328
+ const validationMessage = computed(() => {
329
+ const value = draft.value;
330
+ if (!value)
331
+ return "";
332
+ if (!isDateInRange(value))
333
+ return (props.invalidMessage ?? copy.value.invalid);
334
+ if (!isCalendarDateSupported(value, calendarSystem.value.value)) {
335
+ return calendarSystem.value.value === "islamic-umalqura"
336
+ ? (props.unsupportedMessage ?? copy.value.unsupported)
337
+ : (props.invalidMessage ?? copy.value.invalid);
338
+ }
339
+ return !isDateInRange(value, minimum.value, maximum.value) ||
340
+ props.isDateDisabled?.(value)
341
+ ? (props.invalidMessage ?? copy.value.invalid)
342
+ : "";
343
+ });
344
+ watchEffect(() => element.value?.setCustomValidity(validationMessage.value));
345
+ useFormReset(element, () => {
346
+ model.set(props.defaultValue);
347
+ draft.value = props.modelValue ?? props.defaultValue;
348
+ open.value = false;
349
+ });
350
+ expose({ element, focus: () => element.value?.focus() });
351
+ return () => decorate({ ...props, label: props.label ?? (attrs['aria-label'] || attrs['aria-labelledby'] ? undefined : copy.value.date) }, field.id.value, h("div", { class: "hw-date-picker" }, [
352
+ h("div", { class: "hw-date-picker-control" }, [
353
+ h("input", mergeProps(attrs, field.controlAttrs.value, {
354
+ ref: element,
355
+ type: "text",
356
+ name: undefined,
357
+ placeholder: attrs.placeholder ?? "YYYY-MM-DD",
358
+ "aria-haspopup": "dialog",
359
+ "aria-controls": `${field.id.value}-dialog`,
360
+ class: "hw-input",
361
+ value: !editing.value && draft.value && !validationMessage.value && isCalendarDateSupported(draft.value, calendarSystem.value.value) ? formatCalendarDate(draft.value, { locale: props.locale, calendar: calendarSystem.value.value, dateStyle: "medium" }) : draft.value,
362
+ min: minimum.value,
363
+ max: maximum.value,
364
+ disabled: props.disabled,
365
+ readonly: props.readonly,
366
+ "aria-invalid": validationMessage.value
367
+ ? true
368
+ : field.controlAttrs.value["aria-invalid"],
369
+ "aria-describedby": joinIds(field.controlAttrs.value["aria-describedby"], formattedDate.value && `${field.id.value}-formatted`, validationMessage.value &&
370
+ `${field.id.value}-validation-error`),
371
+ onKeydown: (event) => {
372
+ if (!event.defaultPrevented && !event.isComposing && !event.ctrlKey && !event.metaKey && !event.shiftKey && event.key === "ArrowDown" && !blocked()) {
373
+ event.preventDefault();
374
+ show();
375
+ }
376
+ },
377
+ onFocus: (event) => { if (!blocked()) {
378
+ event.target.value = draft.value;
379
+ event.target.select();
380
+ editing.value = true;
381
+ } },
382
+ onBlur: () => { editing.value = false; },
383
+ onInput: (event) => {
384
+ if (!blocked())
385
+ commit(event.target.value);
386
+ },
387
+ })),
388
+ h(Button, {
389
+ variant: "ghost",
390
+ class: "hw-date-picker-trigger",
391
+ disabled: props.disabled || props.readonly,
392
+ "aria-label": (props.openLabel ?? copy.value.open),
393
+ "aria-expanded": open.value,
394
+ "aria-haspopup": "dialog",
395
+ "aria-controls": `${field.id.value}-dialog`,
396
+ onClick: show,
397
+ }, () => h("span", { class: "hw-date-picker-icon", "aria-hidden": "true" })),
398
+ ]),
399
+ h("input", { type: "hidden", name: attrs.name, form: attrs.form, disabled: props.disabled, value: isDateInRange(model.value.value) ? model.value.value : "" }),
400
+ h("output", {
401
+ id: `${field.id.value}-formatted`,
402
+ for: field.id.value,
403
+ class: "hw-sr-only hw-date-picker-value",
404
+ "aria-live": "polite",
405
+ "aria-atomic": "true",
406
+ hidden: !formattedDate.value,
407
+ }, formattedDate.value),
408
+ validationMessage.value &&
409
+ h("p", {
410
+ id: `${field.id.value}-validation-error`,
411
+ class: "hw-error",
412
+ role: "alert",
413
+ }, dateMessage(validationMessage.value)),
414
+ h(Dialog, {
415
+ id: `${field.id.value}-dialog`,
416
+ open: open.value,
417
+ "onUpdate:open": (value) => {
418
+ open.value = value;
419
+ },
420
+ title: (props.calendarLabel ?? copy.value.choose),
421
+ closeLabel: (props.closeLabel ?? copy.value.close),
422
+ }, () => [
423
+ h("div", { class: "hw-calendar-options" }, [props.calendarSwitchable && h("div", { class: "hw-calendar-system hw-calendar-switch", role: "radiogroup", "aria-label": (props.calendarSystemLabel ?? copy.value.system), "data-calendar": calendarSystem.value.value }, ["gregory", "islamic-umalqura"].map(system => h("label", { key: system }, [
424
+ h("input", { type: "radio", name: `${field.id.value}-calendar-system`, form: "", value: system, checked: calendarSystem.value.value === system, disabled: props.disabled || props.readonly, onChange: async (event) => { const group = event.target.closest('.hw-calendar-switch'); calendarSystem.set(system); await nextTick(); group?.querySelectorAll('input[type=radio]').forEach(radio => { radio.checked = radio.value === calendarSystem.value.value; }); } }),
425
+ h("span", {}, system === "gregory" ? (props.gregoryLabel ?? copy.value.gregorian) : (props.hijriLabel ?? copy.value.hijri)),
426
+ ]))),
427
+ ]),
428
+ h(Calendar, {
429
+ ...props,
430
+ calendar: calendarSystem.value.value,
431
+ month: open.value && isDateInRange(model.value.value)
432
+ ? model.value.value
433
+ : props.month,
434
+ label: (props.calendarLabel ?? copy.value.choose),
435
+ ref: calendar,
436
+ modelValue: model.value.value,
437
+ "onUpdate:modelValue": (value) => {
438
+ if (!blocked()) {
439
+ commit(value);
440
+ open.value = false;
441
+ }
442
+ },
443
+ }),
444
+ ]),
445
+ ]));
446
+ },
447
+ });
448
+ //# sourceMappingURL=calendar.js.map