aboutsummaryrefslogtreecommitdiff
path: root/static/ts/components/date-time-input.ts
diff options
context:
space:
mode:
Diffstat (limited to 'static/ts/components/date-time-input.ts')
-rw-r--r--static/ts/components/date-time-input.ts169
1 files changed, 169 insertions, 0 deletions
diff --git a/static/ts/components/date-time-input.ts b/static/ts/components/date-time-input.ts
new file mode 100644
index 00000000..33201653
--- /dev/null
+++ b/static/ts/components/date-time-input.ts
@@ -0,0 +1,169 @@
+/**
+ * `<date-time-input />`
+ *
+ * @category Web Components
+ * @mergeTarget components
+ * @module
+ */
+
+export { DateTimeInput }
+
+import { makeElement, parseDate } from '../lib'
+
+/**
+ * The HTML component `<date-time-input />`.
+ * An element for input for date-times. Similar to
+ * @example
+ * ```html
+ * <input type="date"/>
+ * <input type="time"/>
+ * ```
+ *
+ * But as a single unit.
+ *
+ * ### Attributes
+ * - dateonly
+ *
+ */
+class DateTimeInput extends /* HTMLInputElement */ HTMLElement {
+
+ /** Our time input element */
+ readonly time: HTMLInputElement;
+ /** Our date input element */
+ readonly date: HTMLInputElement;
+
+ constructor() {
+ super();
+
+ this.date = makeElement('input', {
+ type: 'date'
+ }) as HTMLInputElement
+
+ this.time = makeElement('input', {
+ type: 'time',
+ disabled: this.dateonly
+ }) as HTMLInputElement
+ }
+
+ /**
+ We set our children first when mounted.
+
+ This can be in the constructor for chromium, but NOT firefox...
+
+ - Vivaldi 4.3.2439.63 stable
+ - Mozilla Firefox 94.0.1
+ */
+ connectedCallback() {
+ this.replaceChildren(this.date, this.time)
+ }
+
+ /**
+ Attributes which we want notifications when they are change.
+
+ Part of the Web Component API
+
+ - `dateonly`
+ */
+ static get observedAttributes() {
+ return ['dateonly']
+ }
+
+ /** Part of the Web Component API */
+ attributeChangedCallback(name: string, _: string | null, to: string | null): void {
+ switch (name) {
+ case 'dateonly':
+ if (to == null) {
+ this.time.disabled = false
+ } else {
+ if (to == '' || to == name) {
+ this.time.disabled = true;
+ } else {
+ throw new TypeError(`Invalid value for attribute dateonly: ${to}`)
+ }
+ }
+ break;
+ }
+ }
+
+ /**
+ Setting this to true disabled the time part of the input, and makes
+ any output only have date components (alternativly, the time component
+ set to zero).
+ */
+ get dateonly(): boolean {
+ return this.hasAttribute('dateonly');
+ }
+
+ /** See getter */
+ set dateonly(b: boolean) {
+ if (b) {
+ this.setAttribute('dateonly', "");
+ } else {
+ this.removeAttribute('dateonly');
+ }
+ }
+
+ /** See getter */
+ set value(date: Date) {
+ let [d, t] = date.format("~L~Y-~m-~dT~H:~M").split('T');
+ this.date.value = d;
+ this.time.value = t;
+
+ this.dateonly = date.dateonly;
+ }
+
+ /** Returns current value as a Date object. */
+ get value(): Date {
+ let dt;
+ let date = this.date.value;
+ if (this.dateonly) {
+ dt = parseDate(date);
+ dt.dateonly = true;
+ } else {
+ let time = this.time.value;
+ dt = parseDate(date + 'T' + time)
+ dt.dateonly = false;
+ }
+ return dt;
+ }
+
+ /** Returns current value as an ISO-8601 formatted string. */
+ get stringValue(): string {
+ if (this.dateonly) {
+ return this.value.format("~Y-~m-~d")
+ } else {
+ return this.value.format("~Y-~m-~dT~H:~M:~S")
+ }
+ }
+
+ /**
+ Set the selected date.
+
+ @param new_value
+ If given a date, set the input to that date.
+ If given a string, parse it as an ISO-8601 formatted datetime.
+ */
+ set stringValue(new_value: Date | string) {
+ let date, time, dateonly = false;
+ if (new_value instanceof Date) {
+ date = new_value.format("~L~Y-~m-~d");
+ time = new_value.format("~L~H:~M:~S");
+ dateonly = new_value.dateonly;
+ } else {
+ [date, time] = new_value.split('T')
+ }
+ this.dateonly = dateonly;
+ this.date.value = date;
+ this.time.value = time;
+ }
+
+ /**
+ Adds an event listener to both the date and time input.
+ */
+ addEventListener(type: string, proc: ((e: Event) => void)) {
+ if (type != 'input') throw "Only input supported";
+
+ this.date.addEventListener(type, proc);
+ this.time.addEventListener(type, proc);
+ }
+}