mirror of
https://github.com/directus/directus.git
synced 2026-01-29 14:07:57 -05:00
32829
api/package-lock.json
generated
Normal file
32829
api/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -79,6 +79,7 @@
|
||||
"commander": "^5.1.0",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"cors": "^2.8.5",
|
||||
"date-fns": "^2.16.1",
|
||||
"dotenv": "^8.2.0",
|
||||
"eventemitter2": "^6.4.3",
|
||||
"execa": "^4.0.3",
|
||||
|
||||
@@ -29,7 +29,7 @@ export class FieldsService {
|
||||
this.payloadService = new PayloadService('directus_fields');
|
||||
}
|
||||
|
||||
async readAll(collection?: string) {
|
||||
async readAll(collection?: string): Promise<Field[]> {
|
||||
let fields: FieldMeta[];
|
||||
const nonAuthorizedItemsService = new ItemsService('directus_fields', { knex: this.knex });
|
||||
|
||||
@@ -347,6 +347,8 @@ export class FieldsService {
|
||||
column = table[type](field.field /* precision, scale */);
|
||||
} else if (field.type === 'csv') {
|
||||
column = table.string(field.field);
|
||||
} else if (field.type === 'dateTime') {
|
||||
column = table.dateTime(field.field, { useTz: false });
|
||||
} else {
|
||||
column = table[field.type](field.field);
|
||||
}
|
||||
@@ -355,7 +357,7 @@ export class FieldsService {
|
||||
column.defaultTo(field.schema.default_value);
|
||||
}
|
||||
|
||||
if (field.schema.is_nullable !== undefined && field.schema.is_nullable === false) {
|
||||
if (field.schema?.is_nullable !== undefined && field.schema.is_nullable === false) {
|
||||
column.notNullable();
|
||||
} else {
|
||||
column.nullable();
|
||||
|
||||
@@ -12,6 +12,9 @@ import { ItemsService } from './items';
|
||||
import { URL } from 'url';
|
||||
import Knex from 'knex';
|
||||
import env from '../env';
|
||||
import SchemaInspector from 'knex-schema-inspector';
|
||||
import getLocalType from '../utils/get-local-type';
|
||||
import { format, formatISO } from 'date-fns';
|
||||
|
||||
type Action = 'create' | 'read' | 'update';
|
||||
|
||||
@@ -138,7 +141,7 @@ export class PayloadService {
|
||||
action: Action,
|
||||
payload: Partial<Item> | Partial<Item>[]
|
||||
): Promise<Partial<Item> | Partial<Item>[]> {
|
||||
const processedPayload = (Array.isArray(payload) ? payload : [payload]) as Partial<Item>[];
|
||||
let processedPayload = (Array.isArray(payload) ? payload : [payload]) as Partial<Item>[];
|
||||
|
||||
if (processedPayload.length === 0) return [];
|
||||
|
||||
@@ -172,6 +175,10 @@ export class PayloadService {
|
||||
})
|
||||
);
|
||||
|
||||
if (action === 'read') {
|
||||
await this.processDates(processedPayload);
|
||||
}
|
||||
|
||||
if (['create', 'update'].includes(action)) {
|
||||
processedPayload.forEach((record) => {
|
||||
for (const [key, value] of Object.entries(record)) {
|
||||
@@ -214,6 +221,51 @@ export class PayloadService {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Knex returns `datetime` and `date` columns as Date.. This is wrong for date / datetime, as those
|
||||
* shouldn't return with time / timezone info respectively
|
||||
*/
|
||||
async processDates(payloads: Partial<Record<string, any>>[]) {
|
||||
const schemaInspector = SchemaInspector(this.knex);
|
||||
const columnsInCollection = await schemaInspector.columnInfo(this.collection);
|
||||
|
||||
const columnsWithType = columnsInCollection.map((column) => ({
|
||||
name: column.name,
|
||||
type: getLocalType(column.type),
|
||||
}));
|
||||
|
||||
const dateColumns = columnsWithType.filter((column) => ['dateTime', 'date', 'timestamp'].includes(column.type));
|
||||
|
||||
if (dateColumns.length === 0) return payloads;
|
||||
|
||||
for (const dateColumn of dateColumns) {
|
||||
for (const payload of payloads) {
|
||||
const value: Date = payload[dateColumn.name];
|
||||
|
||||
if (value) {
|
||||
if (dateColumn.type === 'timestamp') {
|
||||
const newValue = formatISO(value);
|
||||
payload[dateColumn.name] = newValue;
|
||||
}
|
||||
|
||||
if (dateColumn.type === 'dateTime') {
|
||||
// Strip off the Z at the end of a non-timezone datetime value
|
||||
const newValue = format(value, "yyyy-MM-dd'T'HH:mm:ss");
|
||||
payload[dateColumn.name] = newValue;
|
||||
}
|
||||
|
||||
if (dateColumn.type === 'date') {
|
||||
// Strip off the time / timezone information from a date-only value
|
||||
const newValue = format(value, 'yyyy-MM-dd');
|
||||
payload[dateColumn.name] = newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return payloads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively save/update all nested related m2o items
|
||||
*/
|
||||
|
||||
@@ -41,6 +41,6 @@ export type Field = {
|
||||
collection: string;
|
||||
field: string;
|
||||
type: typeof types[number];
|
||||
schema: Column;
|
||||
schema: Column | null;
|
||||
meta: FieldMeta | null;
|
||||
};
|
||||
|
||||
@@ -28,6 +28,7 @@ const localTypeMap: Record<string, { type: typeof types[number]; useTimezone?: b
|
||||
char: { type: 'string' },
|
||||
date: { type: 'date' },
|
||||
datetime: { type: 'dateTime' },
|
||||
dateTime: { type: 'dateTime' },
|
||||
timestamp: { type: 'timestamp' },
|
||||
time: { type: 'time' },
|
||||
float: { type: 'float' },
|
||||
@@ -70,7 +71,7 @@ const localTypeMap: Record<string, { type: typeof types[number]; useTimezone?: b
|
||||
bpchar: { type: 'string' },
|
||||
timestamptz: { type: 'timestamp' },
|
||||
'timestamp with time zone': { type: 'timestamp', useTimezone: true },
|
||||
'timestamp without time zone': { type: 'timestamp' },
|
||||
'timestamp without time zone': { type: 'dateTime' },
|
||||
timetz: { type: 'time' },
|
||||
'time with time zone': { type: 'time', useTimezone: true },
|
||||
'time without time zone': { type: 'time' },
|
||||
|
||||
17
app/package-lock.json
generated
17
app/package-lock.json
generated
@@ -16288,17 +16288,6 @@
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"js-yaml-loader": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml-loader/-/js-yaml-loader-1.2.2.tgz",
|
||||
"integrity": "sha512-H+NeuNrG6uOs/WMjna2SjkaCw13rMWiT/D7l9+9x5n8aq88BDsh2sRmdfxckWPIHtViYHWRG6XiCKYvS1dfyLg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"js-yaml": "^3.13.1",
|
||||
"loader-utils": "^1.2.3",
|
||||
"un-eval": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
@@ -25401,12 +25390,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"un-eval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/un-eval/-/un-eval-1.2.0.tgz",
|
||||
"integrity": "sha512-Wlj/pum6dQtGTPD/lclDtoVPkSfpjPfy1dwnnKw/sZP5DpBH9fLhBgQfsqNhe5/gS1D+vkZUuB771NRMUPA5CA==",
|
||||
"dev": true
|
||||
},
|
||||
"underscore": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch, PropType } from '@vue/composition-api';
|
||||
import { defineComponent, ref, watch, PropType, computed } from '@vue/composition-api';
|
||||
import localizedFormat from '@/utils/localized-format';
|
||||
import localizedFormatDistance from '@/utils/localized-format-distance';
|
||||
import i18n from '@/lang';
|
||||
import parseISO from 'date-fns/parseISO';
|
||||
import { parseISO, parse } from 'date-fns';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -26,20 +26,34 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const localValue = computed(() => {
|
||||
if (!props.value) return null;
|
||||
|
||||
if (props.type === 'timestamp') {
|
||||
return parseISO(props.value);
|
||||
} else if (props.type === 'dateTime') {
|
||||
return parse(props.value, "yyyy-MM-dd'T'HH:mm:ss", new Date());
|
||||
} else if (props.type === 'date') {
|
||||
return parse(props.value, 'yyyy-MM-dd', new Date());
|
||||
} else if (props.type === 'time') {
|
||||
return parse(props.value, 'HH:mm:ss', new Date());
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
const displayValue = ref<string | null>(null);
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
localValue,
|
||||
async (newValue) => {
|
||||
if (newValue === null) {
|
||||
displayValue.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const date = parseISO(props.value);
|
||||
|
||||
if (props.relative) {
|
||||
displayValue.value = await localizedFormatDistance(date, new Date(), {
|
||||
displayValue.value = await localizedFormatDistance(newValue, new Date(), {
|
||||
addSuffix: true,
|
||||
});
|
||||
} else {
|
||||
@@ -47,7 +61,7 @@ export default defineComponent({
|
||||
if (props.type === 'date') format = String(i18n.t('date-fns_date'));
|
||||
if (props.type === 'time') format = String(i18n.t('date-fns_time'));
|
||||
|
||||
displayValue.value = await localizedFormat(date, format);
|
||||
displayValue.value = await localizedFormat(newValue, format);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import withPadding from '../../../.storybook/decorators/with-padding';
|
||||
import { defineComponent, ref, watch } from '@vue/composition-api';
|
||||
import { withKnobs, select } from '@storybook/addon-knobs';
|
||||
import readme from './readme.md';
|
||||
import i18n from '@/lang';
|
||||
import RawValue from '../../../.storybook/raw-value.vue';
|
||||
|
||||
export default {
|
||||
title: 'Interfaces / DateTime',
|
||||
decorators: [withPadding, withKnobs],
|
||||
parameters: {
|
||||
notes: readme,
|
||||
},
|
||||
};
|
||||
|
||||
export const basic = () =>
|
||||
defineComponent({
|
||||
i18n,
|
||||
props: {
|
||||
type: {
|
||||
default: select('Type', ['datetime', 'date', 'time'], 'datetime'),
|
||||
},
|
||||
},
|
||||
components: { RawValue },
|
||||
setup(props) {
|
||||
const value = ref('2020-04-28 14:40:00');
|
||||
|
||||
watch(
|
||||
() => props.type,
|
||||
(newType: string) => {
|
||||
if (newType === 'datetime') {
|
||||
value.value = '2020-04-28 14:40:00';
|
||||
} else if (newType === 'time') {
|
||||
value.value = '14:40:00';
|
||||
} else {
|
||||
// date
|
||||
value.value = '2020-04-28';
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return { value };
|
||||
},
|
||||
template: `
|
||||
<div style="max-width: 300px;">
|
||||
<interface-datetime
|
||||
v-model="value"
|
||||
:type="type"
|
||||
/>
|
||||
<portal-target multiple name="outlet" />
|
||||
<raw-value>{{ value }}</raw-value>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
@@ -17,13 +17,13 @@
|
||||
|
||||
<div class="date-selects" v-if="type === 'timestamp' || type === 'dateTime' || type === 'date'">
|
||||
<div class="month">
|
||||
<v-select :placeholder="$t('month')" :items="months" v-model="localValue.month" />
|
||||
<v-select :placeholder="$t('month')" :items="monthItems" v-model="month" />
|
||||
</div>
|
||||
<div class="date">
|
||||
<v-select :placeholder="$t('date')" :items="dates" v-model="localValue.date" />
|
||||
<v-select :placeholder="$t('date')" :items="dateItems" v-model="date" />
|
||||
</div>
|
||||
<div class="year">
|
||||
<v-select :placeholder="$t('year')" :items="years" v-model="localValue.year" allow-other />
|
||||
<v-select :placeholder="$t('year')" :items="yearItems" v-model="year" allow-other />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -32,19 +32,19 @@
|
||||
<div
|
||||
class="time-selects"
|
||||
v-if="type === 'timestamp' || type === 'dateTime' || type === 'time'"
|
||||
:class="{ seconds: includeSeconds }"
|
||||
:class="{ seconds: includeSeconds, 'use-24': use24 }"
|
||||
>
|
||||
<div class="hour">
|
||||
<v-select :items="hours" v-model="localValue.hours" />
|
||||
<v-select :items="hourItems" v-model="hours" />
|
||||
</div>
|
||||
<div class="minutes">
|
||||
<v-select :items="minutesSeconds" v-model="localValue.minutes" />
|
||||
<v-select :items="minutesSecondItems" v-model="minutes" />
|
||||
</div>
|
||||
<div v-if="includeSeconds" class="seconds">
|
||||
<v-select :items="minutesSeconds" v-model="localValue.seconds" />
|
||||
<v-select :items="minutesSecondItems" v-model="seconds" />
|
||||
</div>
|
||||
<div class="period">
|
||||
<v-select :items="['am', 'pm']" v-model="localValue.period" />
|
||||
<div class="period" v-if="use24 === false">
|
||||
<v-select :items="['am', 'pm']" v-model="period" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
import { defineComponent, ref, watch, computed, reactive, PropType } from '@vue/composition-api';
|
||||
import formatLocalized from '@/utils/localized-format';
|
||||
import { i18n } from '@/lang';
|
||||
import { formatISO, parseISO } from 'date-fns';
|
||||
import { formatISO, parseISO, format, parse } from 'date-fns';
|
||||
|
||||
type LocalValue = {
|
||||
month: null | number;
|
||||
@@ -67,7 +67,6 @@ type LocalValue = {
|
||||
hours: null | number;
|
||||
minutes: null | number;
|
||||
seconds: null | number;
|
||||
period: 'am' | 'pm';
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
@@ -89,121 +88,209 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
use24: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const valueAsDate = computed(() => {
|
||||
if (props.value === null) return null;
|
||||
return parseISO(props.value);
|
||||
});
|
||||
|
||||
const displayValue = ref<string | null>(null);
|
||||
|
||||
syncDisplayValue();
|
||||
|
||||
const localValue = reactive({
|
||||
month: null,
|
||||
date: null,
|
||||
year: null,
|
||||
hours: 9,
|
||||
minutes: 0,
|
||||
seconds: 0,
|
||||
period: 'am',
|
||||
} as LocalValue);
|
||||
|
||||
syncLocalValue();
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newValue, oldValue) => {
|
||||
if (newValue !== oldValue && newValue !== null && newValue.length !== 0) {
|
||||
syncLocalValue();
|
||||
syncDisplayValue();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => localValue,
|
||||
(newValue) => {
|
||||
if (
|
||||
newValue.year !== null &&
|
||||
String(newValue.year).length === 4 &&
|
||||
newValue.month !== null &&
|
||||
newValue.date !== null &&
|
||||
newValue.hours !== null &&
|
||||
newValue.minutes !== null &&
|
||||
newValue.seconds !== null
|
||||
) {
|
||||
const { year, month, date, hours, minutes, seconds, period } = newValue;
|
||||
|
||||
const asDate = new Date(year, month, date, period === 'am' ? hours : hours + 12, minutes, seconds);
|
||||
|
||||
if(valueAsDate.value?.getTime() != asDate.getTime())
|
||||
emit('input', formatISO(asDate));
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
const { months, dates, years, hours, minutesSeconds } = useOptions();
|
||||
const { _value, year, month, date, hours, minutes, seconds, period } = useLocalValue();
|
||||
const { yearItems, monthItems, dateItems, hourItems, minutesSecondItems } = useOptions();
|
||||
const { displayValue } = useDisplayValue();
|
||||
|
||||
return {
|
||||
displayValue,
|
||||
months,
|
||||
dates,
|
||||
years,
|
||||
year,
|
||||
month,
|
||||
date,
|
||||
hours,
|
||||
minutesSeconds,
|
||||
minutes,
|
||||
seconds,
|
||||
period,
|
||||
setToNow,
|
||||
localValue,
|
||||
onAMPMInput,
|
||||
yearItems,
|
||||
monthItems,
|
||||
dateItems,
|
||||
hourItems,
|
||||
minutesSecondItems,
|
||||
displayValue,
|
||||
};
|
||||
|
||||
function useLocalValue() {
|
||||
const _value = computed({
|
||||
get() {
|
||||
if (!props.value) return null;
|
||||
|
||||
if (props.type === 'timestamp') {
|
||||
return parseISO(props.value);
|
||||
} else if (props.type === 'dateTime') {
|
||||
return parse(props.value, "yyyy-MM-dd'T'HH:mm:ss", new Date());
|
||||
} else if (props.type === 'date') {
|
||||
return parse(props.value, 'yyyy-MM-dd', new Date());
|
||||
} else if (props.type === 'time') {
|
||||
return parse(props.value, 'HH:mm:ss', new Date());
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
set(newValue: Date | null) {
|
||||
if (newValue === null) return emit('input', null);
|
||||
|
||||
if (props.type === 'timestamp') {
|
||||
emit('input', formatISO(newValue));
|
||||
} else if (props.type === 'dateTime') {
|
||||
emit('input', format(newValue, "yyyy-MM-dd'T'HH:mm:ss"));
|
||||
} else if (props.type === 'date') {
|
||||
emit('input', format(newValue, 'yyyy-MM-dd'));
|
||||
} else if (props.type === 'time') {
|
||||
emit('input', format(newValue, 'HH:mm:ss'));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const year = computed({
|
||||
get() {
|
||||
if (!_value.value) return null;
|
||||
return _value.value.getFullYear();
|
||||
},
|
||||
set(newYear: number | null) {
|
||||
const newValue = _value.value ? new Date(_value.value) : new Date();
|
||||
newValue.setFullYear(newYear || 0);
|
||||
_value.value = newValue;
|
||||
},
|
||||
});
|
||||
|
||||
const month = computed({
|
||||
get() {
|
||||
if (!_value.value) return null;
|
||||
return _value.value.getMonth();
|
||||
},
|
||||
set(newMonth: number | null) {
|
||||
const newValue = _value.value ? new Date(_value.value) : new Date();
|
||||
newValue.setMonth(newMonth || 0);
|
||||
_value.value = newValue;
|
||||
},
|
||||
});
|
||||
|
||||
const date = computed({
|
||||
get() {
|
||||
if (!_value.value) return null;
|
||||
return _value.value.getDate();
|
||||
},
|
||||
set(newDate: number | null) {
|
||||
const newValue = _value.value ? new Date(_value.value) : new Date();
|
||||
newValue.setDate(newDate || 1);
|
||||
_value.value = newValue;
|
||||
},
|
||||
});
|
||||
|
||||
const hours = computed({
|
||||
get() {
|
||||
if (!_value.value) return null;
|
||||
const hours = _value.value.getHours();
|
||||
|
||||
if (props.use24 === false) {
|
||||
return hours % 12;
|
||||
}
|
||||
|
||||
return hours;
|
||||
},
|
||||
set(newHours: number | null) {
|
||||
const newValue = _value.value ? new Date(_value.value) : new Date();
|
||||
newValue.setHours(newHours || 0);
|
||||
_value.value = newValue;
|
||||
},
|
||||
});
|
||||
|
||||
const minutes = computed({
|
||||
get() {
|
||||
if (!_value.value) return null;
|
||||
return _value.value.getMinutes();
|
||||
},
|
||||
set(newMinutes: number | null) {
|
||||
const newValue = _value.value ? new Date(_value.value) : new Date();
|
||||
newValue.setMinutes(newMinutes || 0);
|
||||
_value.value = newValue;
|
||||
},
|
||||
});
|
||||
|
||||
const seconds = computed({
|
||||
get() {
|
||||
if (!_value.value) return null;
|
||||
return _value.value.getSeconds();
|
||||
},
|
||||
set(newSeconds: number | null) {
|
||||
const newValue = _value.value ? new Date(_value.value) : new Date();
|
||||
newValue.setSeconds(newSeconds || 0);
|
||||
_value.value = newValue;
|
||||
},
|
||||
});
|
||||
|
||||
const period = computed({
|
||||
get() {
|
||||
if (!_value.value) return null;
|
||||
return _value.value.getHours() >= 12 ? 'pm' : 'am';
|
||||
},
|
||||
set(newAMPM: 'am' | 'pm' | null) {
|
||||
const newValue = _value.value ? new Date(_value.value) : new Date();
|
||||
const current = newValue.getHours() >= 12 ? 'pm' : 'am';
|
||||
|
||||
if (current !== newAMPM) {
|
||||
if (newAMPM === 'am') {
|
||||
newValue.setHours(newValue.getHours() - 12);
|
||||
} else {
|
||||
newValue.setHours(newValue.getHours() + 12);
|
||||
}
|
||||
}
|
||||
|
||||
_value.value = newValue;
|
||||
},
|
||||
});
|
||||
|
||||
return { _value, year, month, date, hours, minutes, seconds, period };
|
||||
}
|
||||
|
||||
function setToNow() {
|
||||
const date = new Date();
|
||||
|
||||
localValue.month = date.getMonth();
|
||||
localValue.date = date.getDate();
|
||||
localValue.year = date.getFullYear();
|
||||
localValue.hours = date.getHours();
|
||||
localValue.minutes = date.getMinutes();
|
||||
localValue.seconds = date.getSeconds();
|
||||
_value.value = new Date();
|
||||
}
|
||||
|
||||
function syncLocalValue() {
|
||||
if (!valueAsDate.value) return;
|
||||
localValue.month = valueAsDate.value.getMonth();
|
||||
localValue.date = valueAsDate.value.getDate();
|
||||
localValue.year = valueAsDate.value?.getFullYear();
|
||||
localValue.hours = valueAsDate.value?.getHours() % 12;
|
||||
localValue.minutes = valueAsDate.value?.getMinutes();
|
||||
localValue.seconds = valueAsDate.value?.getSeconds();
|
||||
}
|
||||
function useDisplayValue() {
|
||||
const displayValue = ref<string | null>(null);
|
||||
|
||||
async function syncDisplayValue() {
|
||||
if (valueAsDate.value === null) return null;
|
||||
let format = `${i18n.t('date-fns_date')} ${i18n.t('date-fns_time')}`;
|
||||
watch(_value, setDisplayValue);
|
||||
|
||||
if (props.type === 'date') format = String(i18n.t('date-fns_date'));
|
||||
if (props.type === 'time') format = String(i18n.t('date-fns_time'));
|
||||
return { displayValue };
|
||||
|
||||
displayValue.value = await formatLocalized(valueAsDate.value as Date, format);
|
||||
}
|
||||
async function setDisplayValue() {
|
||||
if (!props.value || !_value.value) {
|
||||
displayValue.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
function onAMPMInput(newValue: 'PM' | 'AM') {
|
||||
if (!localValue.hours) return;
|
||||
let format = `${i18n.t('date-fns_date')} ${i18n.t('date-fns_time')}`;
|
||||
|
||||
if (newValue === 'AM') {
|
||||
localValue.hours = localValue.hours - 12;
|
||||
} else {
|
||||
localValue.hours = localValue.hours + 12;
|
||||
if (props.type === 'date') format = String(i18n.t('date-fns_date'));
|
||||
if (props.type === 'time') format = String(i18n.t('date-fns_time'));
|
||||
|
||||
displayValue.value = await formatLocalized(_value.value, format);
|
||||
}
|
||||
}
|
||||
|
||||
function useOptions() {
|
||||
const months = computed(() =>
|
||||
const yearItems = computed(() => {
|
||||
const current = _value.value?.getFullYear() || new Date().getFullYear();
|
||||
const years = [];
|
||||
|
||||
for (let i = current - 5; i <= current + 5; i++) {
|
||||
years.push({
|
||||
text: String(i),
|
||||
value: i,
|
||||
});
|
||||
}
|
||||
|
||||
return years;
|
||||
});
|
||||
|
||||
const monthItems = computed(() =>
|
||||
[
|
||||
i18n.t('months.january'),
|
||||
i18n.t('months.february'),
|
||||
@@ -223,7 +310,7 @@ export default defineComponent({
|
||||
}))
|
||||
);
|
||||
|
||||
const dates = computed(() => {
|
||||
const dateItems = computed(() => {
|
||||
const dates = [];
|
||||
|
||||
for (let i = 1; i <= 31; i++) {
|
||||
@@ -233,24 +320,15 @@ export default defineComponent({
|
||||
return dates;
|
||||
});
|
||||
|
||||
const years = computed(() => {
|
||||
const current = valueAsDate.value?.getFullYear() || new Date().getFullYear();
|
||||
const years = [];
|
||||
for (let i = current - 5; i <= current + 5; i++) {
|
||||
years.push({
|
||||
text: String(i),
|
||||
value: i,
|
||||
});
|
||||
}
|
||||
return years;
|
||||
});
|
||||
|
||||
const hours = computed(() => {
|
||||
const hourItems = computed(() => {
|
||||
const hours = [];
|
||||
|
||||
for (let i = 1; i <= 12; i++) {
|
||||
const hoursInADay = props.use24 ? 24 : 12;
|
||||
|
||||
for (let i = 1; i <= hoursInADay; i++) {
|
||||
let hour = String(i);
|
||||
if (hour.length === 1) hour = '0' + hour;
|
||||
|
||||
hours.push({
|
||||
text: hour,
|
||||
value: i,
|
||||
@@ -260,7 +338,7 @@ export default defineComponent({
|
||||
return hours;
|
||||
});
|
||||
|
||||
const minutesSeconds = computed(() => {
|
||||
const minutesSecondItems = computed(() => {
|
||||
const values = [];
|
||||
|
||||
for (let i = 0; i < 60; i++) {
|
||||
@@ -275,7 +353,7 @@ export default defineComponent({
|
||||
return values;
|
||||
});
|
||||
|
||||
return { dates, years, months, hours, minutesSeconds };
|
||||
return { yearItems, monthItems, dateItems, hourItems, minutesSecondItems };
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -300,6 +378,14 @@ export default defineComponent({
|
||||
&.seconds {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
&.use-24 {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
|
||||
&.seconds {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.month {
|
||||
|
||||
@@ -21,6 +21,18 @@ export default defineInterface(({ i18n }) => ({
|
||||
default_value: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'use24',
|
||||
name: i18n.t('interfaces.datetime.use_24'),
|
||||
type: 'boolean',
|
||||
meta: {
|
||||
width: 'half',
|
||||
interface: 'toggle',
|
||||
},
|
||||
schema: {
|
||||
default_value: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
recommendedDisplays: ['datetime'],
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user