vira 31.21.2 → 31.22.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.
@@ -2,12 +2,14 @@ export * from './pop-up/vira-menu-item.element.js';
2
2
  export * from './pop-up/vira-menu-trigger.element.js';
3
3
  export * from './pop-up/vira-menu.element.js';
4
4
  export * from './pop-up/vira-pop-up-trigger.element.js';
5
+ export * from './vira-absolute-time.element.js';
5
6
  export * from './vira-bold-text.element.js';
6
7
  export * from './vira-button.element.js';
7
8
  export * from './vira-card.element.js';
8
9
  export * from './vira-checkbox.element.js';
9
10
  export * from './vira-collapsible-card.element.js';
10
11
  export * from './vira-collapsible-wrapper.element.js';
12
+ export * from './vira-date-input.element.js';
11
13
  export * from './vira-drawer.element.js';
12
14
  export * from './vira-dropdown.element.js';
13
15
  export * from './vira-error.element.js';
@@ -19,6 +21,7 @@ export * from './vira-json-form.element.js';
19
21
  export * from './vira-link.element.js';
20
22
  export * from './vira-modal.element.js';
21
23
  export * from './vira-progress.element.js';
24
+ export * from './vira-relative-time.element.js';
22
25
  export * from './vira-select.element.js';
23
26
  export * from './vira-tabs.element.js';
24
27
  export * from './vira-tag.element.js';
@@ -2,12 +2,14 @@ export * from './pop-up/vira-menu-item.element.js';
2
2
  export * from './pop-up/vira-menu-trigger.element.js';
3
3
  export * from './pop-up/vira-menu.element.js';
4
4
  export * from './pop-up/vira-pop-up-trigger.element.js';
5
+ export * from './vira-absolute-time.element.js';
5
6
  export * from './vira-bold-text.element.js';
6
7
  export * from './vira-button.element.js';
7
8
  export * from './vira-card.element.js';
8
9
  export * from './vira-checkbox.element.js';
9
10
  export * from './vira-collapsible-card.element.js';
10
11
  export * from './vira-collapsible-wrapper.element.js';
12
+ export * from './vira-date-input.element.js';
11
13
  export * from './vira-drawer.element.js';
12
14
  export * from './vira-dropdown.element.js';
13
15
  export * from './vira-error.element.js';
@@ -19,6 +21,7 @@ export * from './vira-json-form.element.js';
19
21
  export * from './vira-link.element.js';
20
22
  export * from './vira-modal.element.js';
21
23
  export * from './vira-progress.element.js';
24
+ export * from './vira-relative-time.element.js';
22
25
  export * from './vira-select.element.js';
23
26
  export * from './vira-tabs.element.js';
24
27
  export * from './vira-tag.element.js';
@@ -0,0 +1,28 @@
1
+ import { type PartialWithUndefined } from '@augment-vir/common';
2
+ import { type FullDate } from 'date-vir';
3
+ /**
4
+ * Displays a `FullDate` as an absolute, human readable timestamp in the user's timezone (for
5
+ * example `Jun 3, 2026 14:30 PDT`).
6
+ *
7
+ * @category Time
8
+ * @category Elements
9
+ * @see https://electrovir.github.io/vira/book/elements/vira-absolute-time
10
+ */
11
+ export declare const ViraAbsoluteTime: import("element-vir").DeclarativeElementDefinition<"vira-absolute-time", {
12
+ time: Readonly<FullDate>;
13
+ } & PartialWithUndefined<{
14
+ /**
15
+ * Show only the date part (`Jun 3, 2026`), omitting the time of day and timezone. Use this
16
+ * for values that are conceptually dates (e.g. captured via a date-only input), where the
17
+ * time of day would be meaningless noise.
18
+ */
19
+ dateOnly: boolean;
20
+ }>, {}, {}, "vira-absolute-time-", "vira-absolute-time-", readonly [], readonly []>;
21
+ /**
22
+ * Formats a `FullDate` into the same absolute string rendered by {@link ViraAbsoluteTime}.
23
+ *
24
+ * @category Time
25
+ */
26
+ export declare function formatAbsoluteTime(time: Readonly<FullDate>, options?: Readonly<PartialWithUndefined<{
27
+ dateOnly: boolean;
28
+ }>>): string;
@@ -0,0 +1,32 @@
1
+ import { createFullDateInUserTimezone, toFormattedString } from 'date-vir';
2
+ import { css } from 'element-vir';
3
+ import { defineViraElement } from '../util/define-vira-element.js';
4
+ /**
5
+ * Displays a `FullDate` as an absolute, human readable timestamp in the user's timezone (for
6
+ * example `Jun 3, 2026 14:30 PDT`).
7
+ *
8
+ * @category Time
9
+ * @category Elements
10
+ * @see https://electrovir.github.io/vira/book/elements/vira-absolute-time
11
+ */
12
+ export const ViraAbsoluteTime = defineViraElement()({
13
+ tagName: 'vira-absolute-time',
14
+ styles: css `
15
+ :host {
16
+ white-space: nowrap;
17
+ }
18
+ `,
19
+ render({ inputs }) {
20
+ return formatAbsoluteTime(inputs.time, {
21
+ dateOnly: inputs.dateOnly,
22
+ });
23
+ },
24
+ });
25
+ /**
26
+ * Formats a `FullDate` into the same absolute string rendered by {@link ViraAbsoluteTime}.
27
+ *
28
+ * @category Time
29
+ */
30
+ export function formatAbsoluteTime(time, options) {
31
+ return toFormattedString(createFullDateInUserTimezone(time), options?.dateOnly ? 'MMM d, yyyy' : 'MMM d, yyyy HH:mm ZZZZ');
32
+ }
@@ -0,0 +1,41 @@
1
+ import { type PartialWithUndefined } from '@augment-vir/common';
2
+ import { type FullDate } from 'date-vir';
3
+ /**
4
+ * A native date picker input that emits `FullDate` values. Fires `valueChange` whenever the user
5
+ * selects (or clears) a date.
6
+ *
7
+ * @category Input
8
+ * @category Elements
9
+ * @see https://electrovir.github.io/vira/book/elements/vira-date-input
10
+ */
11
+ export declare const ViraDateInput: import("element-vir").DeclarativeElementDefinition<"vira-date-input", {
12
+ value: FullDate | undefined;
13
+ } & PartialWithUndefined<{
14
+ /** Lower bound for selectable dates. Defaults to `1800-01-01` when omitted. */
15
+ min: FullDate;
16
+ /** Upper bound for selectable dates. Defaults to 10 years from now when omitted. */
17
+ max: FullDate;
18
+ /** If true, applies error styling. */
19
+ hasError: boolean;
20
+ /** A label that is shown above the input, if provided. */
21
+ label: string;
22
+ /** If true, the input is disabled and cannot be edited. */
23
+ isDisabled: boolean;
24
+ /** If true, the current value is rendered as plain text instead of an editable input. */
25
+ isReadonly: boolean;
26
+ }>, {
27
+ /** Used to couple the label and input together. Not applied when no label is provided. */
28
+ randomId: string;
29
+ }, {
30
+ /** Fires whenever the user selects or clears a date. */
31
+ valueChange: import("element-vir").DefineEvent<{
32
+ hour: import("date-vir").Hour;
33
+ minute: import("date-vir").Minute;
34
+ millisecond: number;
35
+ year: number;
36
+ month: import("date-vir").MonthNumber;
37
+ day: import("date-vir").DayOfMonth;
38
+ second: import("date-vir").Second;
39
+ timezone: "Africa/Abidjan" | "Africa/Accra" | "Africa/Addis_Ababa" | "Africa/Algiers" | "Africa/Asmara" | "Africa/Bamako" | "Africa/Bangui" | "Africa/Banjul" | "Africa/Bissau" | "Africa/Blantyre" | "Africa/Brazzaville" | "Africa/Bujumbura" | "Africa/Cairo" | "Africa/Casablanca" | "Africa/Ceuta" | "Africa/Conakry" | "Africa/Dakar" | "Africa/Dar_es_Salaam" | "Africa/Djibouti" | "Africa/Douala" | "Africa/El_Aaiun" | "Africa/Freetown" | "Africa/Gaborone" | "Africa/Harare" | "Africa/Johannesburg" | "Africa/Juba" | "Africa/Kampala" | "Africa/Khartoum" | "Africa/Kigali" | "Africa/Kinshasa" | "Africa/Lagos" | "Africa/Libreville" | "Africa/Lome" | "Africa/Luanda" | "Africa/Lubumbashi" | "Africa/Lusaka" | "Africa/Malabo" | "Africa/Maputo" | "Africa/Maseru" | "Africa/Mbabane" | "Africa/Mogadishu" | "Africa/Monrovia" | "Africa/Nairobi" | "Africa/Ndjamena" | "Africa/Niamey" | "Africa/Nouakchott" | "Africa/Ouagadougou" | "Africa/Porto-Novo" | "Africa/Sao_Tome" | "Africa/Timbuktu" | "Africa/Tripoli" | "Africa/Tunis" | "Africa/Windhoek" | "America/Adak" | "America/Anchorage" | "America/Anguilla" | "America/Antigua" | "America/Araguaina" | "America/Argentina/Buenos_Aires" | "America/Argentina/Catamarca" | "America/Argentina/ComodRivadavia" | "America/Argentina/Cordoba" | "America/Argentina/Jujuy" | "America/Argentina/La_Rioja" | "America/Argentina/Mendoza" | "America/Argentina/Rio_Gallegos" | "America/Argentina/Salta" | "America/Argentina/San_Juan" | "America/Argentina/San_Luis" | "America/Argentina/Tucuman" | "America/Argentina/Ushuaia" | "America/Aruba" | "America/Asuncion" | "America/Atikokan" | "America/Bahia" | "America/Bahia_Banderas" | "America/Barbados" | "America/Belem" | "America/Belize" | "America/Blanc-Sablon" | "America/Boa_Vista" | "America/Bogota" | "America/Boise" | "America/Cambridge_Bay" | "America/Campo_Grande" | "America/Cancun" | "America/Caracas" | "America/Cayenne" | "America/Cayman" | "America/Chicago" | "America/Chihuahua" | "America/Coral_Harbour" | "America/Costa_Rica" | "America/Creston" | "America/Cuiaba" | "America/Curacao" | "America/Danmarkshavn" | "America/Dawson" | "America/Dawson_Creek" | "America/Denver" | "America/Detroit" | "America/Dominica" | "America/Edmonton" | "America/Eirunepe" | "America/El_Salvador" | "America/Ensenada" | "America/Fort_Nelson" | "America/Fortaleza" | "America/Glace_Bay" | "America/Goose_Bay" | "America/Grand_Turk" | "America/Grenada" | "America/Guadeloupe" | "America/Guatemala" | "America/Guayaquil" | "America/Guyana" | "America/Halifax" | "America/Havana" | "America/Hermosillo" | "America/Indiana/Indianapolis" | "America/Indiana/Knox" | "America/Indiana/Marengo" | "America/Indiana/Petersburg" | "America/Indiana/Tell_City" | "America/Indiana/Vevay" | "America/Indiana/Vincennes" | "America/Indiana/Winamac" | "America/Inuvik" | "America/Iqaluit" | "America/Jamaica" | "America/Juneau" | "America/Kentucky/Louisville" | "America/Kentucky/Monticello" | "America/La_Paz" | "America/Lima" | "America/Los_Angeles" | "America/Maceio" | "America/Managua" | "America/Manaus" | "America/Martinique" | "America/Matamoros" | "America/Mazatlan" | "America/Menominee" | "America/Merida" | "America/Metlakatla" | "America/Mexico_City" | "America/Miquelon" | "America/Moncton" | "America/Monterrey" | "America/Montevideo" | "America/Montreal" | "America/Montserrat" | "America/Nassau" | "America/New_York" | "America/Nipigon" | "America/Nome" | "America/Noronha" | "America/North_Dakota/Beulah" | "America/North_Dakota/Center" | "America/North_Dakota/New_Salem" | "America/Nuuk" | "America/Ojinaga" | "America/Panama" | "America/Pangnirtung" | "America/Paramaribo" | "America/Phoenix" | "America/Port-au-Prince" | "America/Port_of_Spain" | "America/Porto_Velho" | "America/Puerto_Rico" | "America/Punta_Arenas" | "America/Rainy_River" | "America/Rankin_Inlet" | "America/Recife" | "America/Regina" | "America/Resolute" | "America/Rio_Branco" | "America/Rosario" | "America/Santarem" | "America/Santiago" | "America/Santo_Domingo" | "America/Sao_Paulo" | "America/Scoresbysund" | "America/Sitka" | "America/St_Johns" | "America/St_Kitts" | "America/St_Lucia" | "America/St_Thomas" | "America/St_Vincent" | "America/Swift_Current" | "America/Tegucigalpa" | "America/Thule" | "America/Thunder_Bay" | "America/Tijuana" | "America/Toronto" | "America/Tortola" | "America/Vancouver" | "America/Whitehorse" | "America/Winnipeg" | "America/Yakutat" | "America/Yellowknife" | "Antarctica/Casey" | "Antarctica/Davis" | "Antarctica/DumontDUrville" | "Antarctica/Macquarie" | "Antarctica/Mawson" | "Antarctica/McMurdo" | "Antarctica/Palmer" | "Antarctica/Rothera" | "Antarctica/Syowa" | "Antarctica/Troll" | "Antarctica/Vostok" | "Asia/Aden" | "Asia/Almaty" | "Asia/Amman" | "Asia/Anadyr" | "Asia/Aqtau" | "Asia/Aqtobe" | "Asia/Ashgabat" | "Asia/Atyrau" | "Asia/Baghdad" | "Asia/Bahrain" | "Asia/Baku" | "Asia/Bangkok" | "Asia/Barnaul" | "Asia/Beirut" | "Asia/Bishkek" | "Asia/Brunei" | "Asia/Chita" | "Asia/Choibalsan" | "Asia/Chongqing" | "Asia/Colombo" | "Asia/Damascus" | "Asia/Dhaka" | "Asia/Dili" | "Asia/Dubai" | "Asia/Dushanbe" | "Asia/Famagusta" | "Asia/Gaza" | "Asia/Harbin" | "Asia/Hebron" | "Asia/Ho_Chi_Minh" | "Asia/Hong_Kong" | "Asia/Hovd" | "Asia/Irkutsk" | "Asia/Jakarta" | "Asia/Jayapura" | "Asia/Jerusalem" | "Asia/Kabul" | "Asia/Kamchatka" | "Asia/Karachi" | "Asia/Kashgar" | "Asia/Kathmandu" | "Asia/Khandyga" | "Asia/Kolkata" | "Asia/Krasnoyarsk" | "Asia/Kuala_Lumpur" | "Asia/Kuching" | "Asia/Kuwait" | "Asia/Macau" | "Asia/Magadan" | "Asia/Makassar" | "Asia/Manila" | "Asia/Muscat" | "Asia/Nicosia" | "Asia/Novokuznetsk" | "Asia/Novosibirsk" | "Asia/Omsk" | "Asia/Oral" | "Asia/Phnom_Penh" | "Asia/Pontianak" | "Asia/Pyongyang" | "Asia/Qatar" | "Asia/Qostanay" | "Asia/Qyzylorda" | "Asia/Riyadh" | "Asia/Sakhalin" | "Asia/Samarkand" | "Asia/Seoul" | "Asia/Shanghai" | "Asia/Singapore" | "Asia/Srednekolymsk" | "Asia/Taipei" | "Asia/Tashkent" | "Asia/Tbilisi" | "Asia/Tehran" | "Asia/Tel_Aviv" | "Asia/Thimphu" | "Asia/Tokyo" | "Asia/Tomsk" | "Asia/Ulaanbaatar" | "Asia/Urumqi" | "Asia/Ust-Nera" | "Asia/Vientiane" | "Asia/Vladivostok" | "Asia/Yakutsk" | "Asia/Yangon" | "Asia/Yekaterinburg" | "Asia/Yerevan" | "Atlantic/Azores" | "Atlantic/Bermuda" | "Atlantic/Canary" | "Atlantic/Cape_Verde" | "Atlantic/Faroe" | "Atlantic/Jan_Mayen" | "Atlantic/Madeira" | "Atlantic/Reykjavik" | "Atlantic/South_Georgia" | "Atlantic/St_Helena" | "Atlantic/Stanley" | "Australia/Adelaide" | "Australia/Brisbane" | "Australia/Broken_Hill" | "Australia/Currie" | "Australia/Darwin" | "Australia/Eucla" | "Australia/Hobart" | "Australia/Lindeman" | "Australia/Lord_Howe" | "Australia/Melbourne" | "Australia/Perth" | "Australia/Sydney" | "CET" | "CST6CDT" | "EET" | "EST" | "EST5EDT" | "Etc/GMT+1" | "Etc/GMT+10" | "Etc/GMT+11" | "Etc/GMT+12" | "Etc/GMT+2" | "Etc/GMT+3" | "Etc/GMT+4" | "Etc/GMT+5" | "Etc/GMT+6" | "Etc/GMT+7" | "Etc/GMT+8" | "Etc/GMT+9" | "Etc/GMT-1" | "Etc/GMT-10" | "Etc/GMT-11" | "Etc/GMT-12" | "Etc/GMT-13" | "Etc/GMT-14" | "Etc/GMT-2" | "Etc/GMT-3" | "Etc/GMT-4" | "Etc/GMT-5" | "Etc/GMT-6" | "Etc/GMT-7" | "Etc/GMT-8" | "Etc/GMT-9" | "Europe/Amsterdam" | "Europe/Andorra" | "Europe/Astrakhan" | "Europe/Athens" | "Europe/Belfast" | "Europe/Belgrade" | "Europe/Berlin" | "Europe/Brussels" | "Europe/Bucharest" | "Europe/Budapest" | "Europe/Chisinau" | "Europe/Copenhagen" | "Europe/Dublin" | "Europe/Gibraltar" | "Europe/Guernsey" | "Europe/Helsinki" | "Europe/Isle_of_Man" | "Europe/Istanbul" | "Europe/Jersey" | "Europe/Kaliningrad" | "Europe/Kirov" | "Europe/Kyiv" | "Europe/Lisbon" | "Europe/Ljubljana" | "Europe/London" | "Europe/Luxembourg" | "Europe/Madrid" | "Europe/Malta" | "Europe/Minsk" | "Europe/Monaco" | "Europe/Moscow" | "Europe/Oslo" | "Europe/Paris" | "Europe/Prague" | "Europe/Riga" | "Europe/Rome" | "Europe/Samara" | "Europe/Sarajevo" | "Europe/Saratov" | "Europe/Simferopol" | "Europe/Skopje" | "Europe/Sofia" | "Europe/Stockholm" | "Europe/Tallinn" | "Europe/Tirane" | "Europe/Tiraspol" | "Europe/Ulyanovsk" | "Europe/Uzhgorod" | "Europe/Vaduz" | "Europe/Vienna" | "Europe/Vilnius" | "Europe/Volgograd" | "Europe/Warsaw" | "Europe/Zagreb" | "Europe/Zaporozhye" | "Europe/Zurich" | "HST" | "Indian/Antananarivo" | "Indian/Chagos" | "Indian/Christmas" | "Indian/Cocos" | "Indian/Comoro" | "Indian/Kerguelen" | "Indian/Mahe" | "Indian/Maldives" | "Indian/Mauritius" | "Indian/Mayotte" | "Indian/Reunion" | "MET" | "MST" | "MST7MDT" | "PST8PDT" | "Pacific/Apia" | "Pacific/Auckland" | "Pacific/Bougainville" | "Pacific/Chatham" | "Pacific/Chuuk" | "Pacific/Easter" | "Pacific/Efate" | "Pacific/Enderbury" | "Pacific/Fakaofo" | "Pacific/Fiji" | "Pacific/Funafuti" | "Pacific/Galapagos" | "Pacific/Gambier" | "Pacific/Guadalcanal" | "Pacific/Guam" | "Pacific/Honolulu" | "Pacific/Johnston" | "Pacific/Kanton" | "Pacific/Kiritimati" | "Pacific/Kosrae" | "Pacific/Kwajalein" | "Pacific/Majuro" | "Pacific/Marquesas" | "Pacific/Midway" | "Pacific/Nauru" | "Pacific/Niue" | "Pacific/Norfolk" | "Pacific/Noumea" | "Pacific/Pago_Pago" | "Pacific/Palau" | "Pacific/Pitcairn" | "Pacific/Pohnpei" | "Pacific/Port_Moresby" | "Pacific/Rarotonga" | "Pacific/Saipan" | "Pacific/Tahiti" | "Pacific/Tarawa" | "Pacific/Tongatapu" | "Pacific/Wake" | "Pacific/Wallis" | "UTC" | "WET";
40
+ } | undefined>;
41
+ }, "vira-date-input-error" | "vira-date-input-disabled", "vira-date-input-", readonly [], readonly []>;
@@ -0,0 +1,130 @@
1
+ import { randomString } from '@augment-vir/common';
2
+ import { extractEventTarget } from '@augment-vir/web';
3
+ import { calculateRelativeDate, createUtcFullDate, FullDatePart, getNowInUtcTimezone, parseInputElementValue, toHtmlInputString, userTimezone, } from 'date-vir';
4
+ import { css, defineElementEvent, html, ifDefined, listen } from 'element-vir';
5
+ import { viraDisabledStyles } from '../styles/disabled.js';
6
+ import { viraFormCssVars } from '../styles/form-styles.js';
7
+ import { defineViraElement } from '../util/define-vira-element.js';
8
+ import { formatAbsoluteTime } from './vira-absolute-time.element.js';
9
+ /**
10
+ * A native date picker input that emits `FullDate` values. Fires `valueChange` whenever the user
11
+ * selects (or clears) a date.
12
+ *
13
+ * @category Input
14
+ * @category Elements
15
+ * @see https://electrovir.github.io/vira/book/elements/vira-date-input
16
+ */
17
+ export const ViraDateInput = defineViraElement()({
18
+ tagName: 'vira-date-input',
19
+ events: {
20
+ /** Fires whenever the user selects or clears a date. */
21
+ valueChange: defineElementEvent(),
22
+ },
23
+ state() {
24
+ return {
25
+ /** Used to couple the label and input together. Not applied when no label is provided. */
26
+ randomId: randomString(32),
27
+ };
28
+ },
29
+ hostClasses: {
30
+ 'vira-date-input-error': ({ inputs }) => !!inputs.hasError,
31
+ 'vira-date-input-disabled': ({ inputs }) => !!inputs.isDisabled,
32
+ },
33
+ styles: ({ hostClasses }) => css `
34
+ :host {
35
+ display: inline-block;
36
+ width: 224px;
37
+ color: ${viraFormCssVars['vira-form-foreground-color'].value};
38
+ }
39
+
40
+ label {
41
+ display: flex;
42
+ flex-direction: column;
43
+ gap: 2px;
44
+ width: 100%;
45
+ }
46
+
47
+ .input-label {
48
+ font-weight: ${viraFormCssVars['vira-form-label-font-weight'].value};
49
+ text-align: left;
50
+ }
51
+
52
+ input {
53
+ box-sizing: border-box;
54
+ width: 100%;
55
+ padding: 4px 8px;
56
+ font-size: inherit;
57
+ border: 1px solid ${viraFormCssVars['vira-form-border-color'].value};
58
+ border-radius: ${viraFormCssVars['vira-form-radius'].value};
59
+ background-color: ${viraFormCssVars['vira-form-background-color'].value};
60
+ color: ${viraFormCssVars['vira-form-foreground-color'].value};
61
+ }
62
+
63
+ ${hostClasses['vira-date-input-error'].selector} input {
64
+ border-color: ${viraFormCssVars['vira-form-error-color'].value};
65
+ }
66
+
67
+ ${hostClasses['vira-date-input-disabled'].selector} {
68
+ cursor: not-allowed;
69
+
70
+ & input {
71
+ ${viraDisabledStyles};
72
+ }
73
+ }
74
+ `,
75
+ render({ inputs, state, dispatch, events }) {
76
+ if (inputs.isReadonly) {
77
+ const readonlyValue = inputs.value
78
+ ? formatAbsoluteTime(inputs.value, {
79
+ dateOnly: true,
80
+ })
81
+ : '';
82
+ const readonlyTemplate = html `
83
+ <span class="readonly-value">${readonlyValue}</span>
84
+ `;
85
+ if (inputs.label) {
86
+ return html `
87
+ <label>
88
+ <span class="input-label">${inputs.label}</span>
89
+ ${readonlyTemplate}
90
+ </label>
91
+ `;
92
+ }
93
+ else {
94
+ return readonlyTemplate;
95
+ }
96
+ }
97
+ const inputValue = inputs.value ? toHtmlInputString(inputs.value, FullDatePart.Date) : '';
98
+ const minDate = toHtmlInputString(inputs.min || createUtcFullDate('1800-01-01'), FullDatePart.Date);
99
+ const maxDate = toHtmlInputString(inputs.max ||
100
+ calculateRelativeDate(getNowInUtcTimezone(), {
101
+ years: 10,
102
+ }), FullDatePart.Date);
103
+ const inputTemplate = html `
104
+ <input
105
+ id=${ifDefined(inputs.label ? state.randomId : undefined)}
106
+ aria-label=${ifDefined(inputs.label || undefined)}
107
+ type="date"
108
+ min=${minDate}
109
+ max=${maxDate}
110
+ ?disabled=${inputs.isDisabled}
111
+ .value=${inputValue}
112
+ ${listen('input', (event) => {
113
+ const element = extractEventTarget(event, HTMLInputElement);
114
+ dispatch(new events.valueChange(parseInputElementValue(element, userTimezone)));
115
+ })}
116
+ />
117
+ `;
118
+ if (inputs.label) {
119
+ return html `
120
+ <label for=${state.randomId}>
121
+ <span class="input-label">${inputs.label}</span>
122
+ ${inputTemplate}
123
+ </label>
124
+ `;
125
+ }
126
+ else {
127
+ return inputTemplate;
128
+ }
129
+ },
130
+ });
@@ -4,6 +4,7 @@ import { viraFormCssVars } from '../styles/form-styles.js';
4
4
  import { defineViraElement } from '../util/define-vira-element.js';
5
5
  import { applyRequiredLabel, areFormFieldsValid, ViraFormFieldType, } from '../util/vira-form-fields.js';
6
6
  import { ViraCheckbox } from './vira-checkbox.element.js';
7
+ import { ViraDateInput } from './vira-date-input.element.js';
7
8
  import { ViraInput, ViraInputType } from './vira-input.element.js';
8
9
  import { ViraSelect } from './vira-select.element.js';
9
10
  import { ViraTextArea } from './vira-text-area.element.js';
@@ -63,7 +64,16 @@ export const ViraForm = defineViraElement()({
63
64
  width: 100%;
64
65
  vertical-align: top;
65
66
 
66
- & > ${ViraCheckbox}, & > ${ViraInput}, & > ${ViraSelect}, & > ${ViraTextArea} {
67
+ &
68
+ > ${ViraCheckbox},
69
+ &
70
+ > ${ViraDateInput},
71
+ &
72
+ > ${ViraInput},
73
+ &
74
+ > ${ViraSelect},
75
+ &
76
+ > ${ViraTextArea} {
67
77
  width: 100%;
68
78
  }
69
79
  }
@@ -243,6 +253,31 @@ export const ViraForm = defineViraElement()({
243
253
  `,
244
254
  });
245
255
  }
256
+ else if (field.type === ViraFormFieldType.Date) {
257
+ return wrapFormField({
258
+ label,
259
+ fieldTemplate: html `
260
+ <${ViraDateInput.assign({
261
+ value: field.value,
262
+ min: field.min,
263
+ max: field.max,
264
+ isDisabled,
265
+ hasError: field.hasError,
266
+ isReadonly: inputs.isReadonly,
267
+ label: childLabel,
268
+ })}
269
+ ${field.testId ? testId(field.testId) : nothing}
270
+ ${listen(ViraDateInput.events.valueChange, (event) => {
271
+ dispatch(new events.valueChange({
272
+ key,
273
+ ...field,
274
+ value: event.detail,
275
+ }));
276
+ })}
277
+ ></${ViraDateInput}>
278
+ `,
279
+ });
280
+ }
246
281
  else {
247
282
  return wrapFormField({
248
283
  label,
@@ -0,0 +1,35 @@
1
+ import { type PartialWithUndefined } from '@augment-vir/common';
2
+ import { type AtLeastOneDuration, type FullDate } from 'date-vir';
3
+ /**
4
+ * Displays a date/time as a live, relative timestamp (for example `5 minutes ago`). The relative
5
+ * string automatically updates over time. Optionally renders the absolute timestamp beneath the
6
+ * relative string.
7
+ *
8
+ * @category Time
9
+ * @category Elements
10
+ * @see https://electrovir.github.io/vira/book/elements/vira-relative-time
11
+ */
12
+ export declare const ViraRelativeTime: import("element-vir").DeclarativeElementDefinition<"vira-relative-time", {
13
+ time: Readonly<FullDate>;
14
+ } & PartialWithUndefined<{
15
+ /** When true, the absolute timestamp is rendered beneath the relative string. */
16
+ showAbsoluteTime: boolean;
17
+ /**
18
+ * How long before the relative time updates.
19
+ *
20
+ * @default {seconds: 5}
21
+ */
22
+ updateInterval: Readonly<AtLeastOneDuration>;
23
+ }>, {
24
+ now: {
25
+ hour: import("date-vir").Hour;
26
+ minute: import("date-vir").Minute;
27
+ millisecond: number;
28
+ year: number;
29
+ month: import("date-vir").MonthNumber;
30
+ day: import("date-vir").DayOfMonth;
31
+ second: import("date-vir").Second;
32
+ timezone: "UTC";
33
+ };
34
+ timeoutId: undefined | ReturnType<typeof globalThis.setTimeout>;
35
+ }, {}, "vira-relative-time-", "vira-relative-time-", readonly [], readonly []>;
@@ -0,0 +1,90 @@
1
+ import { colorCss } from '@electrovir/color';
2
+ import { convertDuration, getNowInUtcTimezone, toRelativeString, } from 'date-vir';
3
+ import { css, html, nothing } from 'element-vir';
4
+ import { viraTheme } from '../styles/vira-color-theme.js';
5
+ import { defineViraElement } from '../util/define-vira-element.js';
6
+ import { formatAbsoluteTime, ViraAbsoluteTime } from './vira-absolute-time.element.js';
7
+ /**
8
+ * Displays a date/time as a live, relative timestamp (for example `5 minutes ago`). The relative
9
+ * string automatically updates over time. Optionally renders the absolute timestamp beneath the
10
+ * relative string.
11
+ *
12
+ * @category Time
13
+ * @category Elements
14
+ * @see https://electrovir.github.io/vira/book/elements/vira-relative-time
15
+ */
16
+ export const ViraRelativeTime = defineViraElement()({
17
+ tagName: 'vira-relative-time',
18
+ styles: css `
19
+ :host {
20
+ display: inline-flex;
21
+ flex-direction: column;
22
+ white-space: nowrap;
23
+ }
24
+
25
+ ${ViraAbsoluteTime} {
26
+ font-size: 12px;
27
+ ${colorCss(viraTheme.colors['vira-grey-foreground-non-body'])};
28
+ }
29
+ `,
30
+ state() {
31
+ return {
32
+ now: getNowInUtcTimezone(),
33
+ timeoutId: undefined,
34
+ };
35
+ },
36
+ init({ inputs, updateState }) {
37
+ /**
38
+ * Reschedule with a fresh timeout after each update so the delay always reflects the
39
+ * current `updateInterval` input.
40
+ */
41
+ function scheduleNextUpdate() {
42
+ const updateInterval = inputs.updateInterval || {
43
+ seconds: 5,
44
+ };
45
+ updateState({
46
+ timeoutId: globalThis.setTimeout(() => {
47
+ updateState({
48
+ now: getNowInUtcTimezone(),
49
+ });
50
+ scheduleNextUpdate();
51
+ }, convertDuration(updateInterval, {
52
+ milliseconds: true,
53
+ }).milliseconds),
54
+ });
55
+ }
56
+ scheduleNextUpdate();
57
+ },
58
+ cleanup({ state, updateState }) {
59
+ globalThis.clearTimeout(state.timeoutId);
60
+ updateState({
61
+ timeoutId: undefined,
62
+ });
63
+ },
64
+ render({ state, inputs }) {
65
+ const relativeTime = toRelativeString({
66
+ start: state.now,
67
+ end: inputs.time,
68
+ }, {
69
+ years: true,
70
+ months: true,
71
+ days: true,
72
+ hours: true,
73
+ minutes: true,
74
+ seconds: true,
75
+ }, {
76
+ useOnlyLargestUnit: true,
77
+ decimalCount: 0,
78
+ });
79
+ return html `
80
+ <span title=${formatAbsoluteTime(inputs.time)}>${relativeTime}</span>
81
+ ${inputs.showAbsoluteTime
82
+ ? html `
83
+ <${ViraAbsoluteTime.assign({
84
+ time: inputs.time,
85
+ })}></${ViraAbsoluteTime}>
86
+ `
87
+ : nothing}
88
+ `;
89
+ },
90
+ });
@@ -1,4 +1,5 @@
1
1
  import { type PartialWithUndefined } from '@augment-vir/common';
2
+ import { type FullDate } from 'date-vir';
2
3
  import { type ViraIconSvg } from '../icons/icon-svg.js';
3
4
  import { type ViraSelectOption } from './vira-select-option.js';
4
5
  /**
@@ -18,7 +19,8 @@ export declare enum ViraFormFieldType {
18
19
  Number = "number",
19
20
  Select = "select",
20
21
  Checkbox = "checkbox",
21
- TextArea = "text-area"
22
+ TextArea = "text-area",
23
+ Date = "date"
22
24
  }
23
25
  /**
24
26
  * {@link ViraFormField} properties that are shared between all field types.
@@ -98,6 +100,14 @@ export type ViraFormField = ({
98
100
  placeholder: string;
99
101
  rows: number;
100
102
  preventResize: boolean;
103
+ }> & CommonViraFormFields) | ({
104
+ type: ViraFormFieldType.Date;
105
+ value: FullDate | undefined;
106
+ } & PartialWithUndefined<{
107
+ /** Lower bound for selectable dates. Defaults to `1800-01-01` when omitted. */
108
+ min: FullDate;
109
+ /** Upper bound for selectable dates. Defaults to 10 years from now when omitted. */
110
+ max: FullDate;
101
111
  }> & CommonViraFormFields);
102
112
  /**
103
113
  * A collection of form fields for `ViraForm`.
@@ -19,6 +19,7 @@ export var ViraFormFieldType;
19
19
  ViraFormFieldType["Select"] = "select";
20
20
  ViraFormFieldType["Checkbox"] = "checkbox";
21
21
  ViraFormFieldType["TextArea"] = "text-area";
22
+ ViraFormFieldType["Date"] = "date";
22
23
  })(ViraFormFieldType || (ViraFormFieldType = {}));
23
24
  /**
24
25
  * Appends a `'*'` to a label if it exist sand if it is required.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vira",
3
- "version": "31.21.2",
3
+ "version": "31.22.0",
4
4
  "description": "A simple and highly versatile design system using element-vir.",
5
5
  "keywords": [
6
6
  "design",
@@ -20,7 +20,7 @@
20
20
  "type": "git",
21
21
  "url": "git+https://github.com/electrovir/vira.git"
22
22
  },
23
- "license": "(MIT or CC0 1.0)",
23
+ "license": "(MIT OR CC0-1.0)",
24
24
  "author": {
25
25
  "name": "electrovir",
26
26
  "url": "https://github.com/electrovir"