mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-24 00:00:46 +00:00
feat(frontend): add support for datetime and time (#3256)
* feat(frontend): add support for datetime and time * feat(frontend): Time component * feat(frontend): add datetime * feat(frontend): fix default dimensions * feat(frontend): fix default dimensions * feat(frontend): add missing case * feat(frontend): fix datetime picker + improve time picker * feat(frontend): compute date validity * feat(frontend): fix placeholder * feat(frontend): adapt tooltips + add missing min/max * feat(frontend): remove console.log * Update components.ts --------- Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
|
||||
export let autofocus: boolean | null = false
|
||||
export let useDropdown: boolean = false
|
||||
export let minDate: string | undefined = undefined
|
||||
export let maxDate: string | undefined = undefined
|
||||
|
||||
let date: string | undefined = undefined
|
||||
let time: string | undefined = undefined
|
||||
@@ -53,9 +55,9 @@
|
||||
let randomId = 'datetarget-' + Math.random().toString(36).substring(7)
|
||||
</script>
|
||||
|
||||
<div class="flex flex-row gap-1 items-center w-full" id={randomId}>
|
||||
<div class="flex flex-row gap-1 items-center w-full" id={randomId} on:pointerdown on:focus>
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<input type="date" bind:value={date} {autofocus} class="!w-3/4" />
|
||||
<input type="date" bind:value={date} {autofocus} class="!w-3/4" min={minDate} max={maxDate} />
|
||||
<input type="time" bind:value={time} class="!w-1/4 min-w-[100px]" />
|
||||
<Button
|
||||
variant="border"
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<script lang="ts">
|
||||
import Markdown from 'svelte-exmarkdown'
|
||||
import type { PopoverPlacement } from './Popover.model'
|
||||
import Popover from './Popover.svelte'
|
||||
import { ExternalLink, InfoIcon } from 'lucide-svelte'
|
||||
|
||||
import { gfmPlugin } from 'svelte-exmarkdown/gfm'
|
||||
export let light = false
|
||||
export let wrapperClass = ''
|
||||
export let placement: PopoverPlacement | undefined = undefined
|
||||
export let documentationLink: string | undefined = undefined
|
||||
export let small = false
|
||||
export let markdownTooltip: string | undefined = undefined
|
||||
const plugins = [gfmPlugin()]
|
||||
</script>
|
||||
|
||||
<Popover notClickable {placement} class={wrapperClass}>
|
||||
@@ -19,7 +22,14 @@
|
||||
<InfoIcon class="{small ? 'bottom-0' : '-bottom-0.5'} absolute" size={small ? 12 : 14} />
|
||||
</div>
|
||||
<svelte:fragment slot="text">
|
||||
<slot />
|
||||
{#if markdownTooltip}
|
||||
<div class="prose-sm">
|
||||
<Markdown md={markdownTooltip} {plugins} />
|
||||
</div>
|
||||
{:else}
|
||||
<slot />
|
||||
{/if}
|
||||
|
||||
{#if documentationLink}
|
||||
<a href={documentationLink} target="_blank" class="text-blue-300 text-xs">
|
||||
<div class="flex flex-row gap-2 mt-4">
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
result: undefined as string | undefined
|
||||
})
|
||||
|
||||
$: handleDefault(resolvedConfig.defaultValue)
|
||||
$: !value && handleDefault(resolvedConfig.defaultValue)
|
||||
|
||||
function formatDate(dateString: string, formatString: string = 'dd.MM.yyyy') {
|
||||
if (formatString === '') {
|
||||
@@ -98,7 +98,7 @@
|
||||
min={resolvedConfig.minDate}
|
||||
max={resolvedConfig.maxDate}
|
||||
placeholder="Type..."
|
||||
class={twMerge(css?.input?.class, 'wm-date-input')}
|
||||
class={twMerge('windmillapp w-full py-1.5 text-sm px-2', css?.input?.class, 'wm-date-input')}
|
||||
style={css?.input?.style ?? ''}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { initConfig, initOutput } from '../../editor/appUtils'
|
||||
import type {
|
||||
AppViewerContext,
|
||||
ComponentCustomCSS,
|
||||
VerticalAlignment,
|
||||
RichConfigurations
|
||||
} from '../../types'
|
||||
import { initCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
import { components } from '../../editor/component'
|
||||
import ResolveConfig from '../helpers/ResolveConfig.svelte'
|
||||
import ResolveStyle from '../helpers/ResolveStyle.svelte'
|
||||
import DateTimeInput from '$lib/components/DateTimeInput.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { parseISO, format as formatDateFns } from 'date-fns'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
export let inputType: 'date'
|
||||
export let verticalAlignment: VerticalAlignment | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'datetimeinputcomponent'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app, worldStore, componentControl, selectedComponent } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let resolvedConfig = initConfig(
|
||||
components['datetimeinputcomponent'].initialData.configuration,
|
||||
configuration
|
||||
)
|
||||
|
||||
let value: string | undefined = undefined
|
||||
|
||||
$componentControl[id] = {
|
||||
setValue(nvalue: string) {
|
||||
value = nvalue
|
||||
}
|
||||
}
|
||||
|
||||
let outputs = initOutput($worldStore, id, {
|
||||
result: undefined as string | undefined,
|
||||
validity: true as boolean | undefined
|
||||
})
|
||||
|
||||
$: handleDefault(resolvedConfig.defaultValue)
|
||||
|
||||
function formatDate(dateString: string, formatString: string = 'dd.MM.yyyy HH:mm') {
|
||||
if (formatString === '') {
|
||||
formatString = 'dd.MM.yyyy HH:mm'
|
||||
}
|
||||
|
||||
try {
|
||||
const isoDate = parseISO(dateString)
|
||||
return formatDateFns(isoDate, formatString)
|
||||
} catch (error) {
|
||||
return 'Error formatting date:' + error.message
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
if (value) {
|
||||
outputs?.result.set(formatDate(value, resolvedConfig.outputFormat))
|
||||
const valueDate = new Date(value)
|
||||
|
||||
if (resolvedConfig.minDateTime) {
|
||||
const minDate = new Date(resolvedConfig.minDateTime)
|
||||
if (
|
||||
minDate.getDay() === valueDate.getDay() &&
|
||||
minDate.getMonth() === valueDate.getMonth() &&
|
||||
minDate.getFullYear() === valueDate.getFullYear()
|
||||
) {
|
||||
outputs?.validity.set(minDate.getTime() < valueDate.getTime())
|
||||
}
|
||||
|
||||
if (minDate.getTime() > valueDate.getTime()) {
|
||||
outputs?.validity.set(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (resolvedConfig.maxDateTime) {
|
||||
const maxDate = new Date(resolvedConfig.maxDateTime)
|
||||
|
||||
if (
|
||||
maxDate.getDay() === valueDate.getDay() &&
|
||||
maxDate.getMonth() === valueDate.getMonth() &&
|
||||
maxDate.getFullYear() === valueDate.getFullYear()
|
||||
) {
|
||||
outputs?.validity.set(maxDate.getTime() > valueDate.getTime())
|
||||
}
|
||||
|
||||
if (maxDate.getTime() < valueDate.getTime()) {
|
||||
outputs?.validity.set(false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
outputs?.result.set(undefined)
|
||||
}
|
||||
}
|
||||
|
||||
function handleDefault(defaultValue: string | undefined) {
|
||||
value = defaultValue
|
||||
}
|
||||
|
||||
let css = initCss($app.css?.datetimeinputcomponent, customCss)
|
||||
</script>
|
||||
|
||||
{#each Object.keys(components['datetimeinputcomponent'].initialData.configuration) as key (key)}
|
||||
<ResolveConfig
|
||||
{id}
|
||||
{key}
|
||||
bind:resolvedConfig={resolvedConfig[key]}
|
||||
configuration={configuration[key]}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
{#each Object.keys(css ?? {}) as key (key)}
|
||||
<ResolveStyle
|
||||
{id}
|
||||
{customCss}
|
||||
{key}
|
||||
bind:css={css[key]}
|
||||
componentStyle={$app.css?.datetimeinputcomponent}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
<div class={twMerge(css?.container?.class, 'w-full')} style={css?.container?.style}>
|
||||
{#if inputType === 'date'}
|
||||
<DateTimeInput
|
||||
bind:value
|
||||
useDropdown={resolvedConfig?.displayPresets}
|
||||
on:pointerdown={(e) => {
|
||||
e.stopPropagation()
|
||||
$selectedComponent = [id]
|
||||
}}
|
||||
minDate={resolvedConfig.minDateTime
|
||||
? formatDate(resolvedConfig.minDateTime, 'yyyy-MM-dd')
|
||||
: undefined}
|
||||
maxDate={resolvedConfig.maxDateTime
|
||||
? formatDate(resolvedConfig.maxDateTime, 'yyyy-MM-dd')
|
||||
: undefined}
|
||||
on:focus={() => ($selectedComponent = [id])}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</AlignWrapper>
|
||||
@@ -0,0 +1,122 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { initConfig, initOutput } from '../../editor/appUtils'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { initCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
import { components } from '../../editor/component'
|
||||
import ResolveConfig from '../helpers/ResolveConfig.svelte'
|
||||
import ResolveStyle from '../helpers/ResolveStyle.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'timeinputcomponent'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
const { app, worldStore, selectedComponent, componentControl } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let resolvedConfig = initConfig(
|
||||
components['timeinputcomponent'].initialData.configuration,
|
||||
configuration
|
||||
)
|
||||
|
||||
let value: string | undefined = undefined
|
||||
|
||||
$componentControl[id] = {
|
||||
setValue(nvalue: string) {
|
||||
value = nvalue
|
||||
}
|
||||
}
|
||||
|
||||
let outputs = initOutput($worldStore, id, {
|
||||
result: undefined as string | undefined,
|
||||
validity: true as boolean
|
||||
})
|
||||
|
||||
$: !value && handleDefault(resolvedConfig.defaultValue)
|
||||
|
||||
function convertToMinutes(time: string) {
|
||||
const [hours, minutes] = time.split(':').map(Number)
|
||||
return hours * 60 + minutes
|
||||
}
|
||||
|
||||
$: {
|
||||
if (value) {
|
||||
if (!resolvedConfig['24hFormat']) {
|
||||
let time = value.split(':')
|
||||
let hours = parseInt(time[0])
|
||||
let minutes = time[1]
|
||||
let ampm = hours >= 12 ? 'pm' : 'am'
|
||||
hours = hours % 12
|
||||
hours = hours ? hours : 12
|
||||
|
||||
outputs?.result.set(hours + ':' + minutes + ' ' + ampm)
|
||||
} else {
|
||||
outputs?.result.set(value)
|
||||
}
|
||||
|
||||
let currentValueInMinutes = convertToMinutes(value)
|
||||
let isValid = true
|
||||
if (resolvedConfig.minTime) {
|
||||
const minTimeInMinutes = convertToMinutes(resolvedConfig.minTime)
|
||||
if (currentValueInMinutes < minTimeInMinutes) {
|
||||
isValid = false
|
||||
}
|
||||
}
|
||||
|
||||
if (resolvedConfig.maxTime) {
|
||||
const maxTimeInMinutes = convertToMinutes(resolvedConfig.maxTime)
|
||||
if (currentValueInMinutes > maxTimeInMinutes) {
|
||||
isValid = false
|
||||
}
|
||||
}
|
||||
|
||||
// At the end, set the validity
|
||||
outputs?.validity.set(isValid)
|
||||
}
|
||||
}
|
||||
|
||||
function handleDefault(defaultValue: string | undefined) {
|
||||
value = defaultValue
|
||||
}
|
||||
let css = initCss($app.css?.timeinputcomponent, customCss)
|
||||
</script>
|
||||
|
||||
{#each Object.keys(components['timeinputcomponent'].initialData.configuration) as key (key)}
|
||||
<ResolveConfig
|
||||
{id}
|
||||
{key}
|
||||
bind:resolvedConfig={resolvedConfig[key]}
|
||||
configuration={configuration[key]}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
{#each Object.keys(css ?? {}) as key (key)}
|
||||
<ResolveStyle
|
||||
{id}
|
||||
{customCss}
|
||||
{key}
|
||||
bind:css={css[key]}
|
||||
componentStyle={$app.css?.timeinputcomponent}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
<input
|
||||
on:focus={() => ($selectedComponent = [id])}
|
||||
on:pointerdown|stopPropagation={() => ($selectedComponent = [id])}
|
||||
type="time"
|
||||
bind:value
|
||||
min={resolvedConfig.minTime}
|
||||
max={resolvedConfig.maxTime}
|
||||
placeholder="Type..."
|
||||
class={twMerge('windmillapp w-full py-1.5 text-sm px-2', css?.input?.class)}
|
||||
style={css?.input?.style ?? ''}
|
||||
/>
|
||||
</AlignWrapper>
|
||||
@@ -71,6 +71,8 @@
|
||||
import AppS3FileInput from '../../components/inputs/AppS3FileInput.svelte'
|
||||
import AppAlert from '../../components/display/AppAlert.svelte'
|
||||
import AppDateSliderInput from '../../components/inputs/AppDateSliderInput.svelte'
|
||||
import AppTimeInput from '../../components/inputs/AppTimeInput.svelte'
|
||||
import AppDateTimeInput from '../../components/inputs/AppDateTimeInput.svelte'
|
||||
|
||||
export let component: AppComponent
|
||||
export let selected: boolean
|
||||
@@ -494,6 +496,23 @@
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'timeinputcomponent'}
|
||||
<AppTimeInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'datetimeinputcomponent'}
|
||||
<AppDateTimeInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
inputType="date"
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'numberinputcomponent'}
|
||||
<AppNumberInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
|
||||
@@ -46,7 +46,9 @@ import {
|
||||
Network,
|
||||
Database,
|
||||
UploadCloud,
|
||||
AlertTriangle
|
||||
AlertTriangle,
|
||||
Clock,
|
||||
CalendarClock
|
||||
} from 'lucide-svelte'
|
||||
import type {
|
||||
Aligned,
|
||||
@@ -89,6 +91,8 @@ export type TextareaInputComponent = BaseComponent<'textareainputcomponent'>
|
||||
export type PasswordInputComponent = BaseComponent<'passwordinputcomponent'>
|
||||
export type EmailInputComponent = BaseComponent<'emailinputcomponent'>
|
||||
export type DateInputComponent = BaseComponent<'dateinputcomponent'>
|
||||
export type TimeInputComponent = BaseComponent<'timeinputcomponent'>
|
||||
export type DateTimeInputComponent = BaseComponent<'datetimeinputcomponent'>
|
||||
export type NumberInputComponent = BaseComponent<'numberinputcomponent'>
|
||||
export type CurrencyComponent = BaseComponent<'currencycomponent'>
|
||||
export type SliderComponent = BaseComponent<'slidercomponent'>
|
||||
@@ -291,6 +295,8 @@ export type TypedComponent =
|
||||
| AgChartsComponentEe
|
||||
| AlertComponent
|
||||
| DateSliderComponent
|
||||
| TimeInputComponent
|
||||
| DateTimeInputComponent
|
||||
|
||||
export type AppComponent = BaseAppComponent & TypedComponent
|
||||
|
||||
@@ -2314,12 +2320,14 @@ This is a paragraph.
|
||||
minDate: {
|
||||
type: 'static',
|
||||
value: '',
|
||||
fieldType: 'date'
|
||||
fieldType: 'date',
|
||||
tooltip: 'The minimum date that can be selected. The format is: "yyyy-MM-dd"'
|
||||
},
|
||||
maxDate: {
|
||||
type: 'static',
|
||||
value: '',
|
||||
fieldType: 'date'
|
||||
fieldType: 'date',
|
||||
tooltip: 'The maximum date that can be selected. The format is: "yyyy-MM-dd"'
|
||||
},
|
||||
defaultValue: {
|
||||
type: 'static',
|
||||
@@ -2330,8 +2338,125 @@ This is a paragraph.
|
||||
type: 'static',
|
||||
value: undefined,
|
||||
fieldType: 'text',
|
||||
tooltip: 'See date-fns format for more information',
|
||||
documentationLink: 'https://date-fns.org/v1.29.0/docs/format'
|
||||
markdownTooltip: `### Output format
|
||||
See date-fns format for more information. By default, it is 'dd.MM.yyyy'
|
||||
|
||||
| Format | Result | Description |
|
||||
| ----------- | ----------- | ----------- |
|
||||
| DD | 01, 02, ..., 31 | Day of the month |
|
||||
| D | 1, 2, ..., 31 | Day of the month |
|
||||
| MM | 01, 02, ..., 12 | Month |
|
||||
| MMM | Jan, Feb, ..., Dec | Month |
|
||||
| MMMM | January, February, ..., December | Month |
|
||||
| YYYY | 2021, 2022, ... | Year |
|
||||
`,
|
||||
|
||||
documentationLink: 'https://date-fns.org/v1.29.0/docs/format',
|
||||
placeholder: 'dd.MM.yyyy'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
datetimeinputcomponent: {
|
||||
name: 'Date & Time',
|
||||
icon: CalendarClock,
|
||||
documentationLink: `${documentationBaseUrl}/datetime_input`,
|
||||
dims: '2:1-6:2' as AppComponentDimensions,
|
||||
customCss: {
|
||||
container: { class: '', style: '' }
|
||||
},
|
||||
initialData: {
|
||||
verticalAlignment: 'center',
|
||||
componentInput: undefined,
|
||||
configuration: {
|
||||
displayPresets: {
|
||||
type: 'static',
|
||||
value: false,
|
||||
fieldType: 'boolean',
|
||||
tooltip: 'Display presets to select the date for example, in 1 week, in 1 month, etc.'
|
||||
},
|
||||
minDateTime: {
|
||||
type: 'static',
|
||||
value: '',
|
||||
fieldType: 'datetime',
|
||||
tooltip:
|
||||
'The minimum date that can be selected. The format is the ISO 8601 format: "yyyy-MM-ddTHH:mm:ss:SSSZ", for example "2021-11-06T23:39:30.000Z", or toISOString() from a Date'
|
||||
},
|
||||
maxDateTime: {
|
||||
type: 'static',
|
||||
value: '',
|
||||
fieldType: 'datetime',
|
||||
tooltip:
|
||||
'The maximum date that can be selected. The format is the ISO 8601 format: "yyyy-MM-ddTHH:mm:ss:SSSZ", for example "2021-11-06T23:39:30.000Z", or toISOString() from a Date'
|
||||
},
|
||||
outputFormat: {
|
||||
type: 'static',
|
||||
value: undefined,
|
||||
fieldType: 'text',
|
||||
documentationLink: 'https://date-fns.org/v1.29.0/docs/format',
|
||||
placeholder: 'dd.MM.yyyy HH:mm',
|
||||
markdownTooltip: `### Output format
|
||||
See date-fns format for more information. By default, it is 'dd.MM.yyyy HH:mm'
|
||||
|
||||
| Format | Result | Description |
|
||||
| ----------- | ----------- | ----------- |
|
||||
| DD | 01, 02, ..., 31 | Day of the month |
|
||||
| D | 1, 2, ..., 31 | Day of the month |
|
||||
| MM | 01, 02, ..., 12 | Month |
|
||||
| MMM | Jan, Feb, ..., Dec | Month |
|
||||
| MMMM | January, February, ..., December | Month |
|
||||
| YYYY | 2021, 2022, ... | Year |
|
||||
| HH | 00, 01, ..., 23 | Hours |
|
||||
| mm | 00, 01, ..., 59 | Minutes |
|
||||
| ss | 00, 01, ..., 59 | Seconds |
|
||||
`
|
||||
},
|
||||
defaultValue: {
|
||||
type: 'static',
|
||||
value: undefined,
|
||||
fieldType: 'datetime'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
timeinputcomponent: {
|
||||
name: 'Time',
|
||||
icon: Clock,
|
||||
documentationLink: `${documentationBaseUrl}/time_input`,
|
||||
dims: '2:1-3:1' as AppComponentDimensions,
|
||||
customCss: {
|
||||
input: { class: '', style: '' }
|
||||
},
|
||||
initialData: {
|
||||
verticalAlignment: 'center',
|
||||
componentInput: undefined,
|
||||
configuration: {
|
||||
minTime: {
|
||||
type: 'static',
|
||||
value: '',
|
||||
fieldType: 'time',
|
||||
tooltip:
|
||||
'The minimum date that can be selected. If the time provided is not valid, it will set the output "validity" to false. The format is: "HH:mm"'
|
||||
},
|
||||
maxTime: {
|
||||
type: 'static',
|
||||
value: '',
|
||||
fieldType: 'time',
|
||||
tooltip:
|
||||
'The maximum date that can be selected. If the time provided is not valid, it will set the output "validity" to false. The format is: "HH:mm"'
|
||||
},
|
||||
defaultValue: {
|
||||
type: 'static',
|
||||
value: undefined,
|
||||
fieldType: 'time'
|
||||
},
|
||||
|
||||
['24hFormat']: {
|
||||
type: 'static',
|
||||
value: true,
|
||||
fieldType: 'boolean',
|
||||
tooltip:
|
||||
'Use 24h format. Will change the format of the output of the component: HH:mm to hh:mm am/pm'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ const inputs: ComponentSet = {
|
||||
'dateslidercomponent',
|
||||
'rangecomponent',
|
||||
'dateinputcomponent',
|
||||
'timeinputcomponent',
|
||||
'datetimeinputcomponent',
|
||||
'fileinputcomponent',
|
||||
's3fileinputcomponent',
|
||||
'checkboxcomponent',
|
||||
|
||||
@@ -105,6 +105,8 @@ export function getComponentControl(type: keyof typeof components): Array<Compon
|
||||
return [clearFiles]
|
||||
case 'displaycomponent':
|
||||
case 'dateinputcomponent':
|
||||
case 'timeinputcomponent':
|
||||
case 'datetimeinputcomponent':
|
||||
case 'textinputcomponent':
|
||||
case 'numberinputcomponent':
|
||||
case 'currencycomponent':
|
||||
|
||||
@@ -696,6 +696,12 @@ export const quickStyleProperties: Record<
|
||||
dateinputcomponent: {
|
||||
input: inputDefaultProps
|
||||
},
|
||||
timeinputcomponent: {
|
||||
input: inputDefaultProps
|
||||
},
|
||||
datetimeinputcomponent: {
|
||||
container: inputDefaultProps
|
||||
},
|
||||
fileinputcomponent: {
|
||||
container: containerDefaultProps
|
||||
},
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
export let acceptSelf: boolean = false
|
||||
export let recomputeOnInputChanged = true
|
||||
export let showOnDemandOnlyToggle = true
|
||||
export let documentationLink: string | undefined = undefined
|
||||
export let markdownTooltip: string | undefined = undefined
|
||||
|
||||
const { connectingInput, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
@@ -80,8 +82,8 @@
|
||||
{#if loading}
|
||||
<Loader2 size={14} class="animate-spin ml-2" />
|
||||
{/if}
|
||||
{#if tooltip}
|
||||
<Tooltip small>
|
||||
{#if tooltip || markdownTooltip}
|
||||
<Tooltip small {documentationLink} {markdownTooltip}>
|
||||
{tooltip}
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
@@ -59,6 +59,8 @@
|
||||
placeholder={meta?.['placeholder']}
|
||||
customTitle={meta?.['customTitle']}
|
||||
loading={meta?.['loading']}
|
||||
documentationLink={meta?.['documentationLink']}
|
||||
markdownTooltip={meta?.['markdownTooltip']}
|
||||
{displayType}
|
||||
{recomputeOnInputChanged}
|
||||
{showOnDemandOnlyToggle}
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
tooltip={config?.['tooltip']}
|
||||
fileUpload={config?.['fileUpload']}
|
||||
loading={config?.['loading']}
|
||||
documentationLink={config?.['documentationLink']}
|
||||
{showOnDemandOnlyToggle}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
+5
@@ -20,6 +20,7 @@
|
||||
import AgChartWizard from '$lib/components/wizards/AgChartWizard.svelte'
|
||||
import DBExplorerWizard from '$lib/components/wizards/DBExplorerWizard.svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
import DateTimeInput from '$lib/components/DateTimeInput.svelte'
|
||||
|
||||
export let componentInput: StaticInput<any> | undefined
|
||||
export let fieldType: InputType | undefined = undefined
|
||||
@@ -42,6 +43,10 @@
|
||||
<textarea use:autosize on:keydown|stopPropagation bind:value={componentInput.value} />
|
||||
{:else if fieldType === 'date'}
|
||||
<input on:keydown|stopPropagation type="date" bind:value={componentInput.value} />
|
||||
{:else if fieldType === 'time'}
|
||||
<input on:keydown|stopPropagation type="time" bind:value={componentInput.value} />
|
||||
{:else if fieldType === 'datetime'}
|
||||
<DateTimeInput bind:value={componentInput.value} />
|
||||
{:else if fieldType === 'boolean'}
|
||||
<Toggle bind:checked={componentInput.value} size="xs" class="mt-2" />
|
||||
{:else if fieldType === 'select' && selectOptions}
|
||||
|
||||
Reference in New Issue
Block a user