date input logic for types date and time, added story

This commit is contained in:
Lone Iversen
2023-05-02 12:48:03 +02:00
parent 8da8a45cbe
commit 73e7e3fa46
7 changed files with 241 additions and 24 deletions

View File

@@ -14,3 +14,9 @@ export interface UmbSwatchDetails {
label: string;
value: string;
}
export interface ServertimeOffset {
/**
* offset in minutes relative to UTC
*/
offset: number;
}

View File

@@ -1,9 +1,10 @@
import { css, html } from 'lit';
import { PropertyValueMap, css, html } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement, property, state } from 'lit/decorators.js';
import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins';
import { ifDefined } from 'lit/directives/if-defined.js';
import { UUIInputEvent } from '@umbraco-ui/uui';
import { UmbConfigRepository } from '../../repositories/config/config.repository';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
@customElement('umb-date-input')
@@ -14,8 +15,23 @@ export class UmbDateInputElement extends FormControlMixin(UmbLitElement) {
return undefined;
}
/**
* Specifies the type of input that will be rendered.
* @type {'date'| 'time'| 'datetime-local'}
* @attr
* @default date
*/
@property()
type: 'date' | 'time' | 'datetime-local' = 'date';
@property({ type: String })
type = 'date';
displayValue?: string;
@property({ type: Boolean })
offsetTime = false;
@state()
private _offsetValue = 0;
@property({ type: String })
min?: string;
@@ -26,11 +42,7 @@ export class UmbDateInputElement extends FormControlMixin(UmbLitElement) {
@property({ type: Number })
step?: number;
@property({ type: Boolean })
offsetTime = false;
@state()
private _localValue?: string;
private _configRepository = new UmbConfigRepository(this);
constructor() {
super();
@@ -38,50 +50,168 @@ export class UmbDateInputElement extends FormControlMixin(UmbLitElement) {
connectedCallback(): void {
super.connectedCallback();
this.offsetTime
? (this._localValue = this.value as string)
: (this._localValue = this.#getLocal(this.value as string));
this.offsetTime ? this.#getOffset() : (this.displayValue = this.#UTCToLocal(this.value as string));
}
/*
connectedCallback(): void {
super.connectedCallback();
this.offsetTime ? this.#getOffset() : (this.displayValue = this.#getLocal(this.value as string));
} */
async #getOffset() {
const data = await this._configRepository.getServertimeOffset();
if (!data) return;
this._offsetValue = data.offset;
if (!this.value) return;
this.displayValue = this.#valueToServerOffset(this.value as string, true);
}
#localToUTC(d: string) {
if (this.type === 'time') {
return new Date(`${new Date().toJSON().slice(0, 10)} ${d}`).toISOString().slice(11, 16);
} else {
const date = new Date(d);
const isoDate = date.toISOString();
return `${isoDate.substring(0, 10)}T${isoDate.substring(11, 19)}Z`;
}
}
#UTCToLocal(d: string) {
if (this.type === 'time') {
const local = new Date(`${new Date().toJSON().slice(0, 10)} ${d}Z`)
.toLocaleTimeString(undefined, {
hourCycle: 'h23',
})
.slice(0, 5);
return local;
} else {
const timezoneReset = `${d.replace('Z', '')}Z`;
const date = new Date(timezoneReset);
const dateString = `${date.getFullYear()}-${('0' + (date.getMonth() + 1)).slice(-2)}-${(
'0' + date.getDate()
).slice(-2)}T${('0' + date.getHours()).slice(-2)}:${('0' + date.getMinutes()).slice(-2)}:${(
'0' + date.getSeconds()
).slice(-2)}`;
return this.type === 'datetime-local' ? dateString : `${dateString.substring(0, 10)}`;
}
}
#dateToString(date: Date) {
return `${date.getFullYear()}-${('0' + (date.getMonth() + 1)).slice(-2)}-${('0' + date.getDate()).slice(-2)}T${(
'0' + date.getHours()
).slice(-2)}:${('0' + date.getMinutes()).slice(-2)}:${('0' + date.getSeconds()).slice(-2)}`;
}
#valueToServerOffset(d: string, utc = false) {
if (this.type === 'time') {
return '';
} else {
const newDate = new Date(d.replace('Z', ''));
const dateOffset = new Date(
newDate.setTime(newDate.getTime() + (utc ? this._offsetValue * -1 : this._offsetValue) * 60 * 1000)
);
return this.type === 'datetime-local'
? this.#dateToString(dateOffset)
: this.#dateToString(dateOffset).slice(0, 10);
}
}
#onChange(e: UUIInputEvent) {
e.stopPropagation();
const picked = e.target.value as string;
this.value = picked ? this.#localToUTC(picked) : '';
this.displayValue = picked ? picked : '';
this.dispatchEvent(new CustomEvent('change'));
}
/*
#valueToServerOffset(date: string, utc = false) {
const newDate = new Date(date);
const dateOffset = new Date(
newDate.setTime(newDate.getTime() + (utc ? this._offsetValue * -1 : this._offsetValue) * 60 * 1000)
);
}
async #getOffset() {
const data = await this._configRepository.getServertimeOffset();
if (!data) return;
this._offsetValue = data.offset;
if (!this.value) return;
this.displayValue = this.#convertValueToServerOffset((this.value as string).replace('Z', ''), true);
}
/** Converts a Date object to string in the correct format for input field */
/*
#convertDateToString(date: Date) {
if (!date) return;
const string = `${date.getFullYear()}-${('0' + (date.getMonth() + 1)).slice(-2)}-${('0' + date.getDate()).slice(
-2
)}T${('0' + date.getHours()).slice(-2)}:${('0' + date.getMinutes()).slice(-2)}:${('0' + date.getSeconds()).slice(
-2
)}Z`;
return string;
}
*/
/*
#convertValueToServerOffset(date: string, utc?: boolean) {
if (!date) return;
const newDate = new Date(date);
// + offsetValue (minutes) * seconds * miliseconds
const dateOffset = new Date(
newDate.setTime(newDate.getTime() + (utc ? this._offsetValue * -1 : this._offsetValue) * 60 * 1000)
);
console.log(dateOffset);
return this.#convertDateToString(dateOffset);
}
*/
/*
#getUTC(timeLocal: string) {
if (!timeLocal) return;
const date = new Date(timeLocal);
const isoDate = date.toISOString();
return `${isoDate.substring(0, 10)}T${isoDate.substring(11, 19)}Z`;
}
#getLocal(timeUTC: string) {
if (!timeUTC) return;
const local = new Date(timeUTC);
const localString = `${local.getFullYear()}-${('0' + (local.getMonth() + 1)).slice(-2)}-${(
'0' + local.getDate()
).slice(-2)}T${('0' + local.getHours()).slice(-2)}:${('0' + local.getMinutes()).slice(-2)}:${(
'0' + local.getSeconds()
).slice(-2)}Z`;
return localString;
return this.#convertDateToString(local);
}
*/
/*
#onDatetimeChange(e: UUIInputEvent) {
e.stopPropagation();
const pickedTime = e.target.value as string;
this.offsetTime ? (this.value = pickedTime) : (this.value = this.#getUTC(pickedTime));
this.displayValue = pickedTime;
const val = this.offsetTime ? this.#convertValueToServerOffset(pickedTime) : this.#getUTC(pickedTime);
this.value = val ?? '';
this._localValue = pickedTime;
this.dispatchEvent(new CustomEvent('change'));
}
*/
//@change="${this.#onDatetimeChange}"
render() {
return html`<uui-input
id="datetime"
@change="${this.#onDatetimeChange}"
label="Pick a date or time"
.type="${this.type}"
@change="${this.#onChange}"
min="${ifDefined(this.min)}"
max="${ifDefined(this.max)}"
.step="${this.step}"
.value="${this._localValue?.replace('Z', '')}"></uui-input>
<br /><br />
.value="${this.displayValue?.replace('Z', '')}">
</uui-input>
<br />
UTC: ${this.value}<br />
Local ${this._localValue}<br />
offset: ${this.offsetTime}`;
Display: ${this.displayValue}<br />
Offset: ${this._offsetValue}<br />`;
}
}

View File

@@ -1,4 +1,5 @@
import { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit';
import './date-input.element';
import type { UmbDateInputElement } from './date-input.element';
@@ -17,3 +18,48 @@ export const Overview: Story = {
offsetTime: true,
},
};
export const Date: Story = {
args: {
type: 'date',
value: '2023-04-01',
offsetTime: true,
},
};
export const Time: Story = {
args: {
type: 'time',
value: '10:00',
},
};
export const DatetimelocalOffset: Story = {
args: {
type: 'datetime-local',
value: '2023-04-01T10:00:00',
offsetTime: false,
displayValue: '',
},
render: (args) =>
html`<umb-date-input
.type="${args.type}"
.value="${args.value}"
.offsetTime="${args.offsetTime}"
.displayValue="${args.displayValue}"></umb-date-input>`,
};
export const Datetimelocal: Story = {
args: {
type: 'datetime-local',
value: '2023-04-01T10:00:00',
offsetTime: false,
displayValue: '',
},
render: (args) =>
html`<umb-date-input
.type="${args.type}"
.value="${args.value}"
.offsetTime="${args.offsetTime}"
.displayValue="${args.displayValue}"></umb-date-input>`,
};

View File

@@ -0,0 +1,18 @@
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import type { ServertimeOffset } from '@umbraco-cms/backoffice/models';
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export class UmbConfigRepository {
#host;
constructor(host: UmbControllerHostElement) {
this.#host = host;
}
async getServertimeOffset() {
const resource = fetch(umbracoPath('/config/servertimeoffset')).then((res) => res.json());
const { data } = await tryExecuteAndNotify<ServertimeOffset>(this.#host, resource);
return data;
}
}

View File

@@ -29,6 +29,7 @@ import { handlers as logViewerHandlers } from './domains/log-viewer.handlers';
import { handlers as packageHandlers } from './domains/package.handlers';
import { handlers as rteEmbedHandlers } from './domains/rte-embed.handlers';
import { handlers as stylesheetHandlers } from './domains/stylesheet.handlers';
import { handlers as configHandlers } from './domains/config.handlers';
const handlers = [
serverHandlers.serverVersionHandler,
@@ -61,6 +62,7 @@ const handlers = [
...packageHandlers,
...rteEmbedHandlers,
...stylesheetHandlers,
...configHandlers,
];
switch (import.meta.env.VITE_UMBRACO_INSTALL_STATUS) {

View File

@@ -0,0 +1,13 @@
import { rest } from 'msw';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
import type { ServertimeOffset } from '@umbraco-cms/backoffice/models';
export const handlers = [
rest.get(umbracoPath('/config/servertimeoffset'), (_req, res, ctx) => {
return res(
// Respond with a 200 status code
ctx.status(200),
ctx.json<ServertimeOffset>({ offset: -120 })
);
}),
];

View File

@@ -14,6 +14,7 @@ import { handlers as healthCheckHandlers } from './domains/health-check.handlers
import { handlers as languageHandlers } from './domains/language.handlers';
import { handlers as redirectManagementHandlers } from './domains/redirect-management.handlers';
import { handlers as packageHandlers } from './domains/package.handlers';
import { handlers as configHandlers } from './domains/config.handlers';
export const handlers = [
serverHandlers.serverRunningHandler,
@@ -33,4 +34,5 @@ export const handlers = [
...languageHandlers,
...redirectManagementHandlers,
...packageHandlers,
...configHandlers,
];