fix: improve date picker across app

This commit is contained in:
Ruben Fiszel
2024-02-19 17:29:57 +01:00
parent f17e8bc15d
commit 4c5d6139ce
8 changed files with 139 additions and 80 deletions
+1 -1
View File
@@ -568,7 +568,7 @@
/>
</div>
{:else if inputCat == 'date'}
<DateTimeInput {autofocus} bind:value />
<DateTimeInput useDropdown {autofocus} bind:value />
{:else if inputCat == 'sql' || inputCat == 'yaml'}
<div class="border my-1 mb-4 w-full border-primary">
<SimpleEditor
@@ -1,27 +1,94 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { Button } from './common'
export let value: string | undefined = undefined
export let autofocus: boolean = false
export let useDropdown: boolean = false
let dateFromValue: Date | undefined = value ? new Date(value) : undefined
let date: string | undefined = undefined
let time: string | undefined = undefined
function parseValue(value: string | undefined = undefined) {
let dateFromValue: Date | undefined = value ? new Date(value) : undefined
let date = isValidDate(dateFromValue) ? dateFromValue!.toISOString().split('T')[0] : undefined
let time = isValidDate(dateFromValue) ? dateFromValue!.toISOString().split('T')[1] : '00:00'
date = isValidDate(dateFromValue) ? dateFromValue!.toISOString().split('T')[0] : undefined
time = isValidDate(dateFromValue)
? `${dateFromValue!.getHours().toString().padStart(2, '0')}:${dateFromValue!
.getMinutes()
.toString()
.padStart(2, '0')}`
: undefined
}
$: {
if (date && time) {
$: parseValue(value)
let initialDate = date
let initialTime = time
function parseDateAndTime(date: string | undefined, time: string | undefined) {
if (date && time && (initialDate != date || initialTime != time)) {
let newDate = new Date(`${date}T${time}`)
value = newDate.toISOString()
dispatch('change', value)
}
}
$: parseDateAndTime(date, time)
function isValidDate(d: Date | undefined): boolean {
return d instanceof Date && !isNaN(d as any)
}
const dispatch = createEventDispatcher()
function setTimeLater(mins: number) {
let newDate = new Date()
newDate.setMinutes(newDate.getMinutes() + mins)
value = newDate.toISOString()
dispatch('change', value)
}
</script>
<div class="flex flex-row gap-1 items-center w-full">
<div class="flex flex-row gap-1 items-center w-full" id="droptarget">
<!-- svelte-ignore a11y-autofocus -->
<input type="date" bind:value={date} {autofocus} class="!w-3/4" />
<input type="time" bind:value={time} class="!w-1/4" />
<input type="time" bind:value={time} class="!w-1/4 min-w-[100px]" />
<Button
variant="border"
color="light"
size="xs"
portalTarget="#droptarget"
dropdownItems={useDropdown
? [
{
label: 'In 15 minutes',
onClick: () => {
setTimeLater(15)
}
},
{
label: 'In 1 hour',
onClick: () => {
setTimeLater(60)
}
},
{
label: 'Tomorrow',
onClick: () => {
setTimeLater(60 * 24)
}
},
{
label: 'In 1 week',
onClick: () => {
setTimeLater(7 * 60 * 24)
}
}
]
: undefined}
on:click={() => {
setTimeLater(0)
}}>now</Button
>
</div>
@@ -75,7 +75,6 @@
enabled: true,
modifierKey: 'ctrl' as 'ctrl',
onPanComplete: ({ chart }) => {
console.log(chart.scales.x.min)
dispatch('zoom', {
min: addSeconds(new Date(chart.scales.x.min), -1),
max: addSeconds(new Date(chart.scales.x.max), 1)
@@ -6,8 +6,8 @@
import Tooltip from './Tooltip.svelte'
import { userStore, workerTags } from '$lib/stores'
import { Button } from './common'
import { getToday } from '$lib/utils'
import { WorkerService } from '$lib/gen'
import DateTimeInput from './DateTimeInput.svelte'
export let runnable:
| {
@@ -50,13 +50,12 @@
<div class="flex flex-row items-end">
<div class="w-max md:w-2/3 mt-2 mb-1">
<label for="run-time" />
<input
class="inline-block"
type="datetime-local"
id="run-time"
name="run-scheduled-time"
bind:value={scheduledForStr}
min={getToday().toISOString().slice(0, 16)}
<DateTimeInput
value={scheduledForStr}
on:change={(e) => {
console.log('e.detail', e.detail)
scheduledForStr = e.detail
}}
/>
</div>
<Button
@@ -29,7 +29,7 @@
export let title: string | undefined = undefined
export let style: string = ''
export let download: string | undefined = undefined
export let portalTarget: string | undefined = undefined
export let startIcon: ButtonType.Icon | undefined = undefined
export let endIcon: ButtonType.Icon | undefined = undefined
@@ -147,7 +147,7 @@
</script>
<div
class="{dropdownItems && variant === 'contained'
class="{dropdownItems && dropdownItems.length > 0 && variant === 'contained'
? colorVariants[color].divider
: ''} {wrapperClasses} flex flex-row"
style={wrapperStyle}
@@ -226,11 +226,11 @@
</button>
{/if}
{#if dropdownItems}
{#if dropdownItems && dropdownItems.length > 0}
<div
class={twMerge(buttonClass, 'rounded-r-md rounded-l-none m-0 p-0 h-auto !w-10 border-l-0')}
>
<ButtonDropdown>
<ButtonDropdown target={portalTarget}>
<svelte:fragment slot="items">
{#each computeDropdowns() ?? [] as item}
<MenuItem on:click={item.onClick} href={item.href}>
@@ -1,36 +1,20 @@
<script lang="ts">
import { ArrowRight, Calendar } from 'lucide-svelte'
import { Calendar } from 'lucide-svelte'
import { createEventDispatcher } from 'svelte'
import { Button, Popup } from '..'
import { Popup } from '..'
import type { Placement } from '@floating-ui/core'
import DateTimeInput from '$lib/components/DateTimeInput.svelte'
export let date: string | undefined
export let label: string
const dispatch = createEventDispatcher()
let value = date
let input: HTMLInputElement
$: if (date && input) {
input.value = new Date(date).toISOString().slice(0, 16)
}
function save() {
console.log(value, date, label)
dispatch('change', value)
input.blur()
}
function bg(e: KeyboardEvent) {
if (e.key === 'Tab') {
e.stopPropagation()
}
}
export let placement: Placement = 'top-end'
</script>
<Popup floatingConfig={{ placement: placement, strategy: 'absolute' }} let:close>
<Popup floatingConfig={{ placement: placement, strategy: 'absolute' }}>
<svelte:fragment slot="button">
<button
title="Open calendar picker"
@@ -44,30 +28,17 @@
</button>
</svelte:fragment>
<!-- svelte-ignore a11y-label-has-associated-control -->
<label class="block text-primary">
<div class="pb-1 text-sm text-secondary">{label}</div>
<div class="flex w-full">
<input
type="datetime-local"
bind:value
class="!w-auto grow"
bind:this={input}
on:keydown={bg}
/>
<Button
size="xs"
color="dark"
buttonType="button"
btnClasses="!p-1 !w-[34px] !ml-1"
aria-label="Save ID"
disabled={!value}
on:click={() => {
save()
close(null)
<DateTimeInput
value={date}
on:change={(e) => {
date = e.detail
dispatch('change', date)
}}
>
<ArrowRight size={18} />
</Button>
/>
</div>
</label>
</Popup>
@@ -345,14 +345,16 @@
on:click={async () => (cancelAllJobs = true)}>Cancel All</Button
>
</div>
<div class="flex flex-row gap-1 w-full max-w-xl">
<div class="flex flex-row gap-1 w-full max-w-lg">
<div class="relative w-full">
<div class="flex gap-1 relative w-full">
<span class="text-xs absolute -top-4">Min datetime</span>
<input
type="text"
value={minTs ?? 'zoom x axis to set min (drag with ctrl)'}
value={minTs
? new Date(minTs).toLocaleString()
: 'zoom x axis to set min (drag with ctrl)'}
disabled
/>
@@ -368,7 +370,11 @@
<div class="relative w-full">
<div class="flex gap-1 relative w-full">
<span class="text-xs absolute -top-4">Max datetime</span>
<input type="text" value={maxTs ?? 'zoom x axis to set max'} disabled />
<input
type="text"
value={maxTs ? new Date(maxTs).toLocaleString() : 'zoom x axis to set max'}
disabled
/>
<CalendarPicker
date={maxTs}
label="Max datetimes"
@@ -507,14 +513,16 @@
on:click={async () => (cancelAllJobs = true)}>Cancel All</Button
>
</div>
<div class="flex flex-row gap-1 w-full max-w-xl items-center">
<div class="flex flex-row gap-1 w-full max-w-lg items-center">
<div class="relative w-full">
<div class="flex gap-1 relative w-full">
<span class="text-xs absolute -top-4">Min datetime</span>
<input
type="text"
value={minTs ?? 'zoom x axis to set min (drag with ctrl)'}
value={minTs
? new Date(minTs).toLocaleString()
: 'zoom x axis to set min (drag with ctrl)'}
disabled
/>
@@ -530,7 +538,11 @@
<div class="relative w-full">
<div class="flex gap-1 relative w-full">
<span class="text-xs absolute -top-4">Max datetime</span>
<input type="text" value={maxTs ?? 'zoom x axis to set max'} disabled />
<input
type="text"
value={maxTs ? new Date(maxTs).toLocaleString() : 'zoom x axis to set max'}
disabled
/>
<CalendarPicker
date={maxTs}
label="Max datetimes"
@@ -20,20 +20,31 @@
let scriptBuilder: ScriptBuilder | undefined = undefined
function decodeStateAndHandleError(state) {
try {
return decodeState(state)
} catch (e) {
console.error('Error decoding state', e)
return defaultScript
}
}
function defaultScript() {
return {
hash: '',
path: path ?? '',
summary: '',
content: '',
schema: schema,
is_template: false,
extra_perms: {},
language: 'deno',
kind: Script.kind.SCRIPT
}
}
let script: NewScript =
!path && initialState != undefined
? decodeState(initialState)
: {
hash: '',
path: path ?? '',
summary: '',
content: '',
schema: schema,
is_template: false,
extra_perms: {},
language: 'deno',
kind: Script.kind.SCRIPT
}
!path && initialState != undefined ? decodeStateAndHandleError(initialState) : defaultScript()
async function loadTemplate(): Promise<void> {
if (templatePath) {