date input logic for types date and time, added story
This commit is contained in:
@@ -14,3 +14,9 @@ export interface UmbSwatchDetails {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
export interface ServertimeOffset {
|
||||
/**
|
||||
* offset in minutes relative to UTC
|
||||
*/
|
||||
offset: number;
|
||||
}
|
||||
|
||||
@@ -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 />`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>`,
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 })
|
||||
);
|
||||
}),
|
||||
];
|
||||
@@ -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,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user