Better form themes (#465)
* Working on custom radius + input size * Fix date input clear vertical align * Moslty finished implementing small size * Polishing larger theme * Finish large theme * Added size/radius options in form editor * Darken help text, improve switch input help location * Slight form editor improvement * Fix styling * Polish of the form editor
This commit is contained in:
parent
a84abf9f72
commit
2ca2d97e8e
|
|
@ -31,6 +31,8 @@ abstract class UserFormRequest extends \Illuminate\Foundation\Http\FormRequest
|
||||||
// Customization
|
// Customization
|
||||||
'theme' => ['required', Rule::in(Form::THEMES)],
|
'theme' => ['required', Rule::in(Form::THEMES)],
|
||||||
'width' => ['required', Rule::in(Form::WIDTHS)],
|
'width' => ['required', Rule::in(Form::WIDTHS)],
|
||||||
|
'size' => ['required', Rule::in(Form::SIZES)],
|
||||||
|
'border_radius' => ['required', Rule::in(Form::BORDER_RADIUS)],
|
||||||
'cover_picture' => 'url|nullable',
|
'cover_picture' => 'url|nullable',
|
||||||
'logo_picture' => 'url|nullable',
|
'logo_picture' => 'url|nullable',
|
||||||
'dark_mode' => ['required', Rule::in(Form::DARK_MODE_VALUES)],
|
'dark_mode' => ['required', Rule::in(Form::DARK_MODE_VALUES)],
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,10 @@ class Form extends Model implements CachableAttributes
|
||||||
|
|
||||||
public const DARK_MODE_VALUES = ['auto', 'light', 'dark'];
|
public const DARK_MODE_VALUES = ['auto', 'light', 'dark'];
|
||||||
|
|
||||||
|
public const SIZES = ['sm','md','lg'];
|
||||||
|
|
||||||
|
public const BORDER_RADIUS = ['none','small','full'];
|
||||||
|
|
||||||
public const THEMES = ['default', 'simple', 'notion'];
|
public const THEMES = ['default', 'simple', 'notion'];
|
||||||
|
|
||||||
public const WIDTHS = ['centered', 'full'];
|
public const WIDTHS = ['centered', 'full'];
|
||||||
|
|
@ -49,6 +53,8 @@ class Form extends Model implements CachableAttributes
|
||||||
|
|
||||||
// Customization
|
// Customization
|
||||||
'custom_domain',
|
'custom_domain',
|
||||||
|
'size',
|
||||||
|
'border_radius',
|
||||||
'theme',
|
'theme',
|
||||||
'width',
|
'width',
|
||||||
'cover_picture',
|
'cover_picture',
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,16 @@
|
||||||
:disabled="disabled ? true : null"
|
:disabled="disabled ? true : null"
|
||||||
:name="name"
|
:name="name"
|
||||||
:color="color"
|
:color="color"
|
||||||
|
:theme="theme"
|
||||||
>
|
>
|
||||||
<slot name="label">
|
<slot
|
||||||
{{ label }}
|
name="label"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
:class="[
|
||||||
|
theme.SelectInput.fontSize,
|
||||||
|
]"
|
||||||
|
>{{ label }}</span>
|
||||||
<span
|
<span
|
||||||
v-if="required"
|
v-if="required"
|
||||||
class="text-red-500 required-dot"
|
class="text-red-500 required-dot"
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
<div
|
<div
|
||||||
:class="[
|
:class="[
|
||||||
theme.CodeInput.input,
|
theme.CodeInput.input,
|
||||||
|
theme.CodeInput.borderRadius,
|
||||||
{
|
{
|
||||||
'!ring-red-500 !ring-2 !border-transparent': hasError,
|
'!ring-red-500 !ring-2 !border-transparent': hasError,
|
||||||
'!cursor-not-allowed !bg-gray-200': disabled,
|
'!cursor-not-allowed !bg-gray-200': disabled,
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,18 @@
|
||||||
:popper="{ placement: 'bottom-start' }"
|
:popper="{ placement: 'bottom-start' }"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
|
ref="datepicker"
|
||||||
class="cursor-pointer overflow-hidden"
|
class="cursor-pointer overflow-hidden"
|
||||||
:class="inputClasses"
|
:class="inputClasses"
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
ref="datepicker"
|
|
||||||
>
|
>
|
||||||
<div class="flex items-center min-w-0">
|
<div class="flex items-stretch min-w-0">
|
||||||
<div
|
<div
|
||||||
class="flex-grow min-w-0 flex items-center gap-x-2"
|
class="flex-grow min-w-0 flex items-center gap-x-2"
|
||||||
:class="[
|
:class="[
|
||||||
props.theme.default.inputSpacing.vertical,
|
props.theme.DateInput.spacing.horizontal,
|
||||||
props.theme.default.inputSpacing.horizontal,
|
props.theme.DateInput.spacing.vertical,
|
||||||
{'hover:bg-gray-50 dark:hover:bg-gray-900': !props.disabled}
|
props.theme.DateInput.fontSize,
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
|
|
@ -29,16 +29,24 @@
|
||||||
class="w-4 h-4 flex-shrink-0"
|
class="w-4 h-4 flex-shrink-0"
|
||||||
dynamic
|
dynamic
|
||||||
/>
|
/>
|
||||||
<div class="flex-grow truncate overflow-hidden">
|
<div class="flex-grow truncate overflow-hidden flex items-center">
|
||||||
<p class="flex-grow truncate h-[24px]">
|
<p
|
||||||
|
v-if="formattedDatePreview"
|
||||||
|
class="flex-grow truncate"
|
||||||
|
>
|
||||||
{{ formattedDatePreview }}
|
{{ formattedDatePreview }}
|
||||||
</p>
|
</p>
|
||||||
|
<p
|
||||||
|
v-else
|
||||||
|
class="text-transparent"
|
||||||
|
>
|
||||||
|
-
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
v-if="fromDate && !props.disabled"
|
v-if="fromDate && !props.disabled"
|
||||||
class="hover:bg-gray-50 dark:hover:bg-gray-900 border-l px-2"
|
class="hover:bg-gray-50 dark:hover:bg-gray-900 border-l px-2 flex items-center"
|
||||||
:class="[props.theme.default.inputSpacing.vertical]"
|
|
||||||
@click.prevent="clear()"
|
@click.prevent="clear()"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
|
|
@ -133,7 +141,7 @@ const modeledValue = computed({
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputClasses = computed(() => {
|
const inputClasses = computed(() => {
|
||||||
const classes = [props.theme.DateInput.input, 'w-full']
|
const classes = [props.theme.DateInput.input, props.theme.DateInput.borderRadius]
|
||||||
if (props.disabled) {
|
if (props.disabled) {
|
||||||
classes.push('!cursor-not-allowed dark:!bg-gray-600 !bg-gray-200')
|
classes.push('!cursor-not-allowed dark:!bg-gray-600 !bg-gray-200')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@
|
||||||
</template>
|
</template>
|
||||||
<div
|
<div
|
||||||
v-if="cameraUpload && isInWebcam"
|
v-if="cameraUpload && isInWebcam"
|
||||||
class="hidden sm:block w-full min-h-40"
|
class="hidden sm:block w-full"
|
||||||
|
:class="[
|
||||||
|
theme.fileInput.minHeight
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<camera-upload
|
<camera-upload
|
||||||
v-if="cameraUpload"
|
v-if="cameraUpload"
|
||||||
|
|
@ -17,9 +20,17 @@
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="flex flex-col w-full items-center justify-center transition-colors duration-40"
|
class="flex flex-col w-full items-center justify-center transition-colors duration-40"
|
||||||
:class="[{'!cursor-not-allowed':disabled, 'cursor-pointer':!disabled,
|
:class="[
|
||||||
|
{'!cursor-not-allowed':disabled, 'cursor-pointer':!disabled,
|
||||||
[theme.fileInput.inputHover.light + ' dark:'+theme.fileInput.inputHover.dark]: uploadDragoverEvent,
|
[theme.fileInput.inputHover.light + ' dark:'+theme.fileInput.inputHover.dark]: uploadDragoverEvent,
|
||||||
['hover:'+theme.fileInput.inputHover.light +' dark:hover:'+theme.fileInput.inputHover.dark]: !loading}, theme.fileInput.input]"
|
['hover:'+theme.fileInput.inputHover.light +' dark:hover:'+theme.fileInput.inputHover.dark]: !loading},
|
||||||
|
theme.fileInput.input,
|
||||||
|
theme.fileInput.borderRadius,
|
||||||
|
theme.fileInput.spacing.horizontal,
|
||||||
|
theme.fileInput.spacing.vertical,
|
||||||
|
theme.fileInput.fontSize,
|
||||||
|
theme.fileInput.minHeight
|
||||||
|
]"
|
||||||
@dragover.prevent="uploadDragoverEvent=true"
|
@dragover.prevent="uploadDragoverEvent=true"
|
||||||
@dragleave.prevent="uploadDragoverEvent=false"
|
@dragleave.prevent="uploadDragoverEvent=false"
|
||||||
@drop.prevent="onUploadDropEvent"
|
@drop.prevent="onUploadDropEvent"
|
||||||
|
|
@ -66,7 +77,7 @@
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
class="w-6 h-6"
|
class="w-5 h-5"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
|
|
@ -76,7 +87,7 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="mt-2 text-sm text-gray-500 font-semibold select-none">
|
<p class="mt-2 text-sm text-gray-500 font-medium select-none">
|
||||||
Click to choose {{ multiple ? 'file(s)' : 'a file' }} or drag here
|
Click to choose {{ multiple ? 'file(s)' : 'a file' }} or drag here
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-1 text-xs text-gray-400 dark:text-gray-600 select-none">
|
<p class="mt-1 text-xs text-gray-400 dark:text-gray-600 select-none">
|
||||||
|
|
|
||||||
|
|
@ -10,41 +10,74 @@
|
||||||
class="h-6 w-6 text-nt-blue mx-auto"
|
class="h-6 w-6 text-nt-blue mx-auto"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-for="(option, index) in options"
|
|
||||||
v-else
|
v-else
|
||||||
:key="option[optionKey]"
|
class="relative overflow-hidden"
|
||||||
role="button"
|
|
||||||
:class="[
|
:class="[
|
||||||
theme.default.input,
|
theme.default.input,
|
||||||
'cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-900 flex',
|
theme.default.borderRadius,
|
||||||
{
|
{
|
||||||
'mb-2': index !== options.length,
|
'mb-2': index !== options.length,
|
||||||
'!ring-red-500 !ring-2 !border-transparent': hasError,
|
'!ring-red-500 !ring-2 !border-transparent': hasError,
|
||||||
'!cursor-not-allowed !bg-gray-200': disabled,
|
'!cursor-not-allowed !bg-gray-200': disabled,
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(option, index) in options"
|
||||||
|
v-if="options && options.length"
|
||||||
|
:key="option[optionKey]"
|
||||||
|
:role="multiple?'checkbox':'radio'"
|
||||||
|
:aria-checked="isSelected(option[optionKey])"
|
||||||
|
:class="[
|
||||||
|
theme.FlatSelectInput.spacing.vertical,
|
||||||
|
theme.FlatSelectInput.fontSize,
|
||||||
|
theme.FlatSelectInput.option,
|
||||||
|
]"
|
||||||
@click="onSelect(option[optionKey])"
|
@click="onSelect(option[optionKey])"
|
||||||
>
|
>
|
||||||
|
<template v-if="multiple">
|
||||||
|
<Icon
|
||||||
|
v-if="isSelected(option[optionKey])"
|
||||||
|
name="material-symbols:check-box"
|
||||||
|
class="text-inherit"
|
||||||
|
:color="color"
|
||||||
|
:class="[theme.FlatSelectInput.icon]"
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
v-else
|
||||||
|
name="material-symbols:check-box-outline-blank"
|
||||||
|
:class="[theme.FlatSelectInput.icon,theme.FlatSelectInput.unselectedIcon]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<Icon
|
||||||
|
v-if="isSelected(option[optionKey])"
|
||||||
|
name="material-symbols:radio-button-checked-outline"
|
||||||
|
class="text-inherit"
|
||||||
|
:color="color"
|
||||||
|
:class="[theme.FlatSelectInput.icon]"
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
v-else
|
||||||
|
name="material-symbols:radio-button-unchecked"
|
||||||
|
:class="[theme.FlatSelectInput.icon,theme.FlatSelectInput.unselectedIcon]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
<p class="flex-grow">
|
<p class="flex-grow">
|
||||||
{{ option[displayKey] }}
|
{{ option[displayKey] }}
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="isSelected(option[optionKey])"
|
v-else
|
||||||
class="flex items-center"
|
:class="[
|
||||||
|
theme.FlatSelectInput.spacing.horizontal,
|
||||||
|
theme.FlatSelectInput.spacing.vertical,
|
||||||
|
theme.FlatSelectInput.fontSize,
|
||||||
|
theme.FlatSelectInput.option,
|
||||||
|
'!text-gray-500 !cursor-not-allowed'
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<svg
|
No options available.
|
||||||
:color="color"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-5 w-5"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,24 @@
|
||||||
aria-expanded="true"
|
aria-expanded="true"
|
||||||
aria-labelledby="listbox-label"
|
aria-labelledby="listbox-label"
|
||||||
class="cursor-pointer relative w-full"
|
class="cursor-pointer relative w-full"
|
||||||
:class="[theme.default.input, { 'ring-red-500 ring-2': hasError }]"
|
:class="[
|
||||||
|
theme.default.input,
|
||||||
|
theme.default.spacing.horizontal,
|
||||||
|
theme.default.spacing.vertical,
|
||||||
|
theme.default.fontSize,
|
||||||
|
theme.default.borderRadius,
|
||||||
|
{ 'ring-red-500 ring-2': hasError }
|
||||||
|
]"
|
||||||
:style="inputStyle"
|
:style="inputStyle"
|
||||||
@click.prevent="showUploadModal = true"
|
@click.prevent="showUploadModal = true"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="currentUrl == null"
|
v-if="currentUrl == null"
|
||||||
class="h-6 text-gray-600 dark:text-gray-400"
|
class="text-gray-600 dark:text-gray-400"
|
||||||
>
|
>
|
||||||
Upload image
|
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-6 w-6 inline"
|
class="h-5 w-5 inline"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
|
|
@ -34,6 +40,7 @@
|
||||||
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
Upload image
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@
|
||||||
:id="id ? id : name"
|
:id="id ? id : name"
|
||||||
:name="name"
|
:name="name"
|
||||||
:style="inputStyle"
|
:style="inputStyle"
|
||||||
class="flex items-start"
|
class="flex items-stretch"
|
||||||
>
|
>
|
||||||
<v-select
|
<v-select
|
||||||
v-model="selectedCountryCode"
|
v-model="selectedCountryCode"
|
||||||
class="w-[130px]"
|
class="w-[130px]"
|
||||||
dropdown-class="w-[300px]"
|
dropdown-class="max-w-[300px]"
|
||||||
input-class="rounded-r-none"
|
input-class="rounded-r-none"
|
||||||
:data="countries"
|
:data="countries"
|
||||||
:disabled="disabled || countries.length === 1 ? true : null"
|
:disabled="disabled || countries.length === 1 ? true : null"
|
||||||
|
|
@ -28,13 +28,13 @@
|
||||||
@update:model-value="onChangeCountryCode"
|
@update:model-value="onChangeCountryCode"
|
||||||
>
|
>
|
||||||
<template #option="props">
|
<template #option="props">
|
||||||
<div class="flex items-center space-x-2 hover:text-white">
|
<div class="flex items-center space-x-2 max-w-full">
|
||||||
<country-flag
|
<country-flag
|
||||||
size="normal"
|
size="normal"
|
||||||
class="!-mt-[9px]"
|
class="!-mt-[9px] rounded"
|
||||||
:country="props.option.code"
|
:country="props.option.code"
|
||||||
/>
|
/>
|
||||||
<span class="grow">{{ props.option.name }}</span>
|
<span class="grow truncate">{{ props.option.name }}</span>
|
||||||
<span>{{ props.option.dial_code }}</span>
|
<span>{{ props.option.dial_code }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
>
|
>
|
||||||
<country-flag
|
<country-flag
|
||||||
size="normal"
|
size="normal"
|
||||||
class="!-mt-[9px]"
|
class="!-mt-[9px] rounded"
|
||||||
:country="props.option.code"
|
:country="props.option.code"
|
||||||
/>
|
/>
|
||||||
<span>{{ props.option.dial_code }}</span>
|
<span>{{ props.option.dial_code }}</span>
|
||||||
|
|
@ -58,6 +58,10 @@
|
||||||
:disabled="disabled ? true : null"
|
:disabled="disabled ? true : null"
|
||||||
:class="[
|
:class="[
|
||||||
theme.default.input,
|
theme.default.input,
|
||||||
|
theme.default.spacing.horizontal,
|
||||||
|
theme.default.spacing.vertical,
|
||||||
|
theme.default.fontSize,
|
||||||
|
theme.default.borderRadius,
|
||||||
{
|
{
|
||||||
'!ring-red-500 !ring-2': hasError,
|
'!ring-red-500 !ring-2': hasError,
|
||||||
'!cursor-not-allowed !bg-gray-200': disabled,
|
'!cursor-not-allowed !bg-gray-200': disabled,
|
||||||
|
|
|
||||||
|
|
@ -20,16 +20,10 @@
|
||||||
@mouseenter="onMouseHover(i)"
|
@mouseenter="onMouseHover(i)"
|
||||||
@mouseleave="hoverRating = -1"
|
@mouseleave="hoverRating = -1"
|
||||||
>
|
>
|
||||||
<svg
|
<Icon
|
||||||
class="w-8 h-8"
|
name="heroicons:star-20-solid"
|
||||||
fill="currentColor"
|
:class="theme.RatingInput.size"
|
||||||
viewBox="0 0 20 20"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"
|
|
||||||
/>
|
/>
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
'!cursor-not-allowed !bg-gray-200': disabled,
|
'!cursor-not-allowed !bg-gray-200': disabled,
|
||||||
},
|
},
|
||||||
theme.RichTextAreaInput.input,
|
theme.RichTextAreaInput.input,
|
||||||
|
theme.RichTextAreaInput.borderRadius,
|
||||||
]"
|
]"
|
||||||
:editor-toolbar="editorToolbar"
|
:editor-toolbar="editorToolbar"
|
||||||
class="rich-editor resize-y"
|
class="rich-editor resize-y"
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,10 @@
|
||||||
:class="[
|
:class="[
|
||||||
{ 'font-semibold': compVal === i },
|
{ 'font-semibold': compVal === i },
|
||||||
theme.ScaleInput.button,
|
theme.ScaleInput.button,
|
||||||
|
theme.ScaleInput.borderRadius,
|
||||||
|
theme.ScaleInput.spacing.horizontal,
|
||||||
|
theme.ScaleInput.spacing.vertical,
|
||||||
|
theme.ScaleInput.fontSize,
|
||||||
compVal !== i ? unselectedButtonClass : '',
|
compVal !== i ? unselectedButtonClass : '',
|
||||||
]"
|
]"
|
||||||
:style="btnStyle(i === compVal)"
|
:style="btnStyle(i === compVal)"
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,12 @@
|
||||||
<template #selected="{ option }">
|
<template #selected="{ option }">
|
||||||
<template v-if="multiple">
|
<template v-if="multiple">
|
||||||
<div class="flex items-center truncate mr-6">
|
<div class="flex items-center truncate mr-6">
|
||||||
<span class="truncate">
|
<span
|
||||||
|
class="truncate"
|
||||||
|
:class="[
|
||||||
|
theme.SelectInput.fontSize,
|
||||||
|
]"
|
||||||
|
>
|
||||||
{{ getOptionNames(selectedValues).join(', ') }}
|
{{ getOptionNames(selectedValues).join(', ') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -45,7 +50,13 @@
|
||||||
:option-name="getOptionName(option)"
|
:option-name="getOptionName(option)"
|
||||||
>
|
>
|
||||||
<div class="flex items-center truncate mr-6">
|
<div class="flex items-center truncate mr-6">
|
||||||
<div>{{ getOptionName(option) }}</div>
|
<div
|
||||||
|
:class="[
|
||||||
|
theme.SelectInput.fontSize,
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{ getOptionName(option) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -57,7 +68,12 @@
|
||||||
:selected="selected"
|
:selected="selected"
|
||||||
>
|
>
|
||||||
<span class="flex">
|
<span class="flex">
|
||||||
<p class="flex-grow">
|
<p
|
||||||
|
class="flex-grow"
|
||||||
|
:class="[
|
||||||
|
theme.SelectInput.fontSize,
|
||||||
|
]"
|
||||||
|
>
|
||||||
{{ option.name }}
|
{{ option.name }}
|
||||||
</p>
|
</p>
|
||||||
<span
|
<span
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,17 @@
|
||||||
<VueSignaturePad
|
<VueSignaturePad
|
||||||
ref="signaturePad"
|
ref="signaturePad"
|
||||||
:class="[
|
:class="[
|
||||||
theme.default.input,
|
theme.SignatureInput.input,
|
||||||
|
theme.SignatureInput.spacing.horizontal,
|
||||||
|
theme.SignatureInput.spacing.vertical,
|
||||||
|
theme.SignatureInput.fontSize,
|
||||||
|
theme.SignatureInput.borderRadius,
|
||||||
|
theme.SignatureInput.minHeight,
|
||||||
{
|
{
|
||||||
'!ring-red-500 !ring-2 !border-transparent': hasError,
|
'!ring-red-500 !ring-2 !border-transparent': hasError,
|
||||||
'!cursor-not-allowed !bg-gray-200': disabled,
|
'!cursor-not-allowed !bg-gray-200': disabled,
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
height="150px"
|
|
||||||
:name="name"
|
:name="name"
|
||||||
:options="{ onEnd }"
|
:options="{ onEnd }"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,10 @@
|
||||||
:disabled="disabled ? true : null"
|
:disabled="disabled ? true : null"
|
||||||
:class="[
|
:class="[
|
||||||
theme.default.input,
|
theme.default.input,
|
||||||
|
theme.default.borderRadius,
|
||||||
|
theme.default.spacing.horizontal,
|
||||||
|
theme.default.spacing.vertical,
|
||||||
|
theme.default.fontSize,
|
||||||
{
|
{
|
||||||
'!ring-red-500 !ring-2 !border-transparent': hasError,
|
'!ring-red-500 !ring-2 !border-transparent': hasError,
|
||||||
'!cursor-not-allowed !bg-gray-200': disabled,
|
'!cursor-not-allowed !bg-gray-200': disabled,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,10 @@
|
||||||
:style="inputStyle"
|
:style="inputStyle"
|
||||||
:class="[
|
:class="[
|
||||||
theme.default.input,
|
theme.default.input,
|
||||||
|
theme.default.borderRadius,
|
||||||
|
theme.default.spacing.horizontal,
|
||||||
|
theme.default.spacing.vertical,
|
||||||
|
theme.default.fontSize,
|
||||||
{
|
{
|
||||||
'!ring-red-500 !ring-2 !border-transparent': hasError,
|
'!ring-red-500 !ring-2 !border-transparent': hasError,
|
||||||
'!cursor-not-allowed !bg-gray-200': disabled,
|
'!cursor-not-allowed !bg-gray-200': disabled,
|
||||||
|
|
@ -92,10 +96,12 @@ export default {
|
||||||
...useFormInput(
|
...useFormInput(
|
||||||
props,
|
props,
|
||||||
context,
|
context,
|
||||||
props.nativeType === "file" ? "file-" : null,
|
{
|
||||||
|
formPrefixKey: props.nativeType === "file" ? "file-" : null
|
||||||
|
},
|
||||||
),
|
),
|
||||||
onEnterPress,
|
onEnterPress,
|
||||||
onChange,
|
onChange
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
||||||
|
|
@ -4,25 +4,43 @@
|
||||||
<span />
|
<span />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="flex">
|
<div class="flex space-x-2 items-center">
|
||||||
<v-switch
|
<v-switch
|
||||||
:id="id ? id : name"
|
:id="id ? id : name"
|
||||||
v-model="compVal"
|
v-model="compVal"
|
||||||
class="inline-block mr-2"
|
|
||||||
:disabled="disabled ? true : null"
|
:disabled="disabled ? true : null"
|
||||||
:color="color"
|
:color="color"
|
||||||
|
:theme="theme"
|
||||||
/>
|
/>
|
||||||
|
<div>
|
||||||
<slot name="label">
|
<slot name="label">
|
||||||
<span>{{ label }}
|
<label
|
||||||
|
:aria-label="id ? id : name"
|
||||||
|
:for="id ? id : name"
|
||||||
|
:class="theme.default.fontSize"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
<span
|
<span
|
||||||
v-if="required"
|
v-if="required"
|
||||||
class="text-red-500 required-dot"
|
class="text-red-500 required-dot"
|
||||||
>*</span></span>
|
>*</span>
|
||||||
|
</label>
|
||||||
</slot>
|
</slot>
|
||||||
|
<slot name="help">
|
||||||
|
<InputHelp
|
||||||
|
:help="help"
|
||||||
|
:help-classes="theme.default.help"
|
||||||
|
>
|
||||||
|
<template #after-help>
|
||||||
|
<slot name="bottom_after_help" />
|
||||||
|
</template>
|
||||||
|
</InputHelp>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #help>
|
<template #help>
|
||||||
<slot name="help" />
|
<span class="hidden" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #error>
|
<template #error>
|
||||||
|
|
@ -35,10 +53,12 @@
|
||||||
import {inputProps, useFormInput} from "./useFormInput.js"
|
import {inputProps, useFormInput} from "./useFormInput.js"
|
||||||
import VSwitch from "./components/VSwitch.vue"
|
import VSwitch from "./components/VSwitch.vue"
|
||||||
import InputWrapper from "./components/InputWrapper.vue"
|
import InputWrapper from "./components/InputWrapper.vue"
|
||||||
|
import InputHelp from "~/components/forms/components/InputHelp.vue"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ToggleSwitchInput",
|
name: "ToggleSwitchInput",
|
||||||
|
|
||||||
components: { InputWrapper, VSwitch },
|
components: {InputHelp, InputWrapper, VSwitch},
|
||||||
props: {
|
props: {
|
||||||
...inputProps,
|
...inputProps,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<template>
|
||||||
|
<form>
|
||||||
|
<slot />
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
/**
|
||||||
|
* Used to pass props to children input components
|
||||||
|
*/
|
||||||
|
import ThemeBuilder from "~/lib/forms/themes/ThemeBuilder.js"
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
themeName: { type: String, default: 'default' },
|
||||||
|
size: { type: String, default: "md" },
|
||||||
|
})
|
||||||
|
|
||||||
|
const theme = computed(() => (new ThemeBuilder(props.themeName, {size: props.size})).getAllComponents())
|
||||||
|
provide('theme', theme)
|
||||||
|
</script>
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
id="webcam"
|
id="webcam"
|
||||||
autoplay
|
autoplay
|
||||||
playsinline
|
playsinline
|
||||||
:class="[{ hidden: !isCapturing }, theme.fileInput.cameraInput]"
|
:class="[{ hidden: !isCapturing }, theme.fileInput.minHeight, theme.fileInput.borderRadius]"
|
||||||
width="1280"
|
width="1280"
|
||||||
height="720"
|
height="720"
|
||||||
/>
|
/>
|
||||||
|
|
@ -136,7 +136,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Webcam from "webcam-easy"
|
import Webcam from "webcam-easy"
|
||||||
import { themes } from "~/lib/forms/form-themes.js"
|
import { themes } from "~/lib/forms/themes/form-themes.js"
|
||||||
export default {
|
export default {
|
||||||
name: "FileInput",
|
name: "FileInput",
|
||||||
props: {
|
props: {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="wrapperClass"
|
:class="[ twMerge(theme.default.wrapper,wrapperClass)]"
|
||||||
:style="inputStyle"
|
:style="inputStyle"
|
||||||
>
|
>
|
||||||
<slot name="label">
|
<slot name="label">
|
||||||
|
|
@ -54,6 +54,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import InputLabel from "./InputLabel.vue"
|
import InputLabel from "./InputLabel.vue"
|
||||||
import InputHelp from "./InputHelp.vue"
|
import InputHelp from "./InputHelp.vue"
|
||||||
|
import {twMerge} from "tailwind-merge"
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
id: {type: String, required: false},
|
id: {type: String, required: false},
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
v-model="internalValue"
|
v-model="internalValue"
|
||||||
:name="name"
|
:name="name"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:class="sizeClasses"
|
class="rounded border-gray-500 w-10 h-10 cursor-pointer checkbox"
|
||||||
class="rounded border-gray-500 cursor-pointer checkbox"
|
:class="theme.CheckboxInput.size"
|
||||||
:style="{ '--accent-color': color }"
|
:style="{ '--accent-color': color }"
|
||||||
:disabled="disabled ? true : null"
|
:disabled="disabled ? true : null"
|
||||||
>
|
>
|
||||||
|
|
@ -32,7 +32,7 @@ const props = defineProps({
|
||||||
name: { type: String, default: "checkbox" },
|
name: { type: String, default: "checkbox" },
|
||||||
modelValue: { type: [Boolean, String], default: false },
|
modelValue: { type: [Boolean, String], default: false },
|
||||||
disabled: { type: Boolean, default: false },
|
disabled: { type: Boolean, default: false },
|
||||||
sizeClasses: { type: String, default: "w-4 h-4" },
|
theme: { type: Object },
|
||||||
color: { type: String, default: null },
|
color: { type: String, default: null },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,12 @@
|
||||||
<div
|
<div
|
||||||
class="inline-block w-full flex overflow-hidden"
|
class="inline-block w-full flex overflow-hidden"
|
||||||
:style="inputStyle"
|
:style="inputStyle"
|
||||||
:class="[theme.SelectInput.input, { '!ring-red-500 !ring-2 !border-transparent': hasError, '!cursor-not-allowed !bg-gray-200': disabled }, inputClass]"
|
:class="[
|
||||||
|
theme.SelectInput.input,
|
||||||
|
theme.SelectInput.borderRadius,
|
||||||
|
{ '!ring-red-500 !ring-2 !border-transparent': hasError, '!cursor-not-allowed !bg-gray-200': disabled },
|
||||||
|
inputClass
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -15,10 +20,18 @@
|
||||||
aria-expanded="true"
|
aria-expanded="true"
|
||||||
aria-labelledby="listbox-label"
|
aria-labelledby="listbox-label"
|
||||||
class="cursor-pointer w-full flex-grow relative"
|
class="cursor-pointer w-full flex-grow relative"
|
||||||
:class="[{'py-2': !multiple || loading, 'py-1': multiple},theme.default.inputSpacing.horizontal]"
|
:class="[
|
||||||
|
theme.SelectInput.spacing.horizontal,
|
||||||
|
theme.SelectInput.spacing.vertical
|
||||||
|
]"
|
||||||
@click="toggleDropdown"
|
@click="toggleDropdown"
|
||||||
>
|
>
|
||||||
<div :class="{ 'h-6': !multiple, 'min-h-8': multiple && !loading }">
|
<div
|
||||||
|
class="flex items-center"
|
||||||
|
:class="[
|
||||||
|
theme.SelectInput.minHeight
|
||||||
|
]"
|
||||||
|
>
|
||||||
<transition
|
<transition
|
||||||
name="fade"
|
name="fade"
|
||||||
mode="out-in"
|
mode="out-in"
|
||||||
|
|
@ -32,7 +45,6 @@
|
||||||
v-else-if="modelValue"
|
v-else-if="modelValue"
|
||||||
key="value"
|
key="value"
|
||||||
class="flex"
|
class="flex"
|
||||||
:class="{ 'min-h-8': multiple }"
|
|
||||||
>
|
>
|
||||||
<slot
|
<slot
|
||||||
name="selected"
|
name="selected"
|
||||||
|
|
@ -47,7 +59,10 @@
|
||||||
<slot name="placeholder">
|
<slot name="placeholder">
|
||||||
<div
|
<div
|
||||||
class="text-gray-400 dark:text-gray-500 w-full text-left truncate pr-3"
|
class="text-gray-400 dark:text-gray-500 w-full text-left truncate pr-3"
|
||||||
:class="{ 'py-1': multiple && !loading }"
|
:class="[
|
||||||
|
{ 'py-1': multiple && !loading },
|
||||||
|
theme.SelectInput.fontSize
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
{{ placeholder }}
|
{{ placeholder }}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -65,7 +80,7 @@
|
||||||
<button
|
<button
|
||||||
v-if="clearable && !isEmpty"
|
v-if="clearable && !isEmpty"
|
||||||
class="hover:bg-gray-50 dark:hover:bg-gray-900 border-l px-2"
|
class="hover:bg-gray-50 dark:hover:bg-gray-900 border-l px-2"
|
||||||
:class="[theme.default.inputSpacing.vertical]"
|
:class="[theme.SelectInput.spacing.vertical]"
|
||||||
@click.prevent="clear()"
|
@click.prevent="clear()"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
|
|
@ -78,15 +93,18 @@
|
||||||
</div>
|
</div>
|
||||||
<collapsible
|
<collapsible
|
||||||
v-model="isOpen"
|
v-model="isOpen"
|
||||||
class="absolute mt-1 bg-white overflow-auto dark:bg-notion-dark-light shadow-xl z-10"
|
class="absolute mt-1 bg-white overflow-auto dark:bg-notion-dark-light shadow-xl z-30"
|
||||||
:class="[dropdownClass,theme.SelectInput.dropdown]"
|
:class="[dropdownClass,theme.SelectInput.dropdown, theme.SelectInput.borderRadius]"
|
||||||
@click-away="onClickAway"
|
@click-away="onClickAway"
|
||||||
>
|
>
|
||||||
<ul
|
<ul
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
role="listbox"
|
role="listbox"
|
||||||
class="text-base leading-6 shadow-xs overflow-auto focus:outline-none sm:text-sm sm:leading-5 relative"
|
class="leading-6 shadow-xs overflow-auto focus:outline-none sm:text-sm sm:leading-5 relative"
|
||||||
:class="{ 'max-h-42': !isSearchable, 'max-h-48': isSearchable }"
|
:class="[
|
||||||
|
{ 'max-h-42': !isSearchable, 'max-h-48': isSearchable },
|
||||||
|
theme.SelectInput.fontSize
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="isSearchable"
|
v-if="isSearchable"
|
||||||
|
|
@ -95,15 +113,29 @@
|
||||||
<input
|
<input
|
||||||
v-model="searchTerm"
|
v-model="searchTerm"
|
||||||
type="text"
|
type="text"
|
||||||
class="flex-grow pl-3 pr-7 py-3 w-full focus:outline-none dark:text-white"
|
class="flex-grow pl-3 pr-7 py-2 w-full focus:outline-none dark:text-white"
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
>
|
>
|
||||||
<div class="flex absolute right-0 inset-y-0 items-center px-2 justify-center pointer-events-none">
|
<div
|
||||||
|
v-if="!searchTerm"
|
||||||
|
class="flex absolute right-0 inset-y-0 items-center px-2 justify-center pointer-events-none"
|
||||||
|
>
|
||||||
<Icon
|
<Icon
|
||||||
name="heroicons:magnifying-glass-solid"
|
name="heroicons:magnifying-glass-solid"
|
||||||
class="h-5 w-5 text-gray-500 dark:text-gray-400"
|
class="h-5 w-5 text-gray-500 dark:text-gray-400"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
role="button"
|
||||||
|
class="flex absolute right-0 inset-y-0 items-center px-2 justify-center"
|
||||||
|
@click="searchTerm = ''"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="heroicons:backspace"
|
||||||
|
class="h-5 w-5 text-gray-500 dark:text-gray-400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="loading"
|
v-if="loading"
|
||||||
|
|
@ -120,8 +152,14 @@
|
||||||
:key="item[optionKey]"
|
:key="item[optionKey]"
|
||||||
role="option"
|
role="option"
|
||||||
:style="optionStyle"
|
:style="optionStyle"
|
||||||
:class="[{ 'px-3 pr-9': multiple, 'px-3': !multiple },dropdownClass,theme.SelectInput.option]"
|
:class="[
|
||||||
class="text-gray-900 cursor-default select-none relative py-2 cursor-pointer group hover:bg-gray-100 dark:hover:bg-gray-900 rounded focus:outline-none"
|
dropdownClass,
|
||||||
|
theme.SelectInput.option,
|
||||||
|
theme.SelectInput.spacing.horizontal,
|
||||||
|
theme.SelectInput.spacing.vertical,
|
||||||
|
{ 'pr-9': multiple},
|
||||||
|
]"
|
||||||
|
class="text-gray-900 select-none relative cursor-pointer group hover:bg-gray-100 dark:hover:bg-gray-900 rounded focus:outline-none"
|
||||||
@click="select(item)"
|
@click="select(item)"
|
||||||
>
|
>
|
||||||
<slot
|
<slot
|
||||||
|
|
@ -145,10 +183,11 @@
|
||||||
role="option"
|
role="option"
|
||||||
:style="optionStyle"
|
:style="optionStyle"
|
||||||
:class="[{ 'px-3 pr-9': multiple, 'px-3': !multiple },dropdownClass,theme.SelectInput.option]"
|
:class="[{ 'px-3 pr-9': multiple, 'px-3': !multiple },dropdownClass,theme.SelectInput.option]"
|
||||||
class="text-gray-900 cursor-default select-none relative py-2 cursor-pointer group hover:bg-gray-100 dark:hover:bg-gray-900 rounded focus:outline-none"
|
class="text-gray-900 select-none relative py-2 cursor-pointer group hover:bg-gray-100 dark:hover:bg-gray-900 rounded focus:outline-none"
|
||||||
@click="createOption(searchTerm)"
|
@click="createOption(searchTerm)"
|
||||||
>
|
>
|
||||||
Create <span class="px-2 bg-gray-100 border border-gray-300 rounded group-hover-text-black">{{ searchTerm
|
Create <span class="px-2 bg-gray-100 border border-gray-300 rounded group-hover-text-black">{{
|
||||||
|
searchTerm
|
||||||
}}</span>
|
}}</span>
|
||||||
</li>
|
</li>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -159,7 +198,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Collapsible from '~/components/global/transitions/Collapsible.vue'
|
import Collapsible from '~/components/global/transitions/Collapsible.vue'
|
||||||
import { themes } from '../../../lib/forms/form-themes.js'
|
import {themes} from '../../../lib/forms/themes/form-themes.js'
|
||||||
import debounce from 'debounce'
|
import debounce from 'debounce'
|
||||||
import Fuse from 'fuse.js'
|
import Fuse from 'fuse.js'
|
||||||
|
|
||||||
|
|
@ -221,11 +260,11 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fuse search
|
// Fuse search
|
||||||
const fuzeOptions = {
|
const fuse = new Fuse(this.data, {
|
||||||
keys: this.searchKeys
|
keys: this.searchKeys,
|
||||||
}
|
includeScore: true
|
||||||
const fuse = new Fuse(this.data, fuzeOptions)
|
})
|
||||||
return fuse.search(this.searchTerm).map((res) => {
|
return fuse.search(this.searchTerm).filter((res) => res.score < 0.5).map((res) => {
|
||||||
return res.item
|
return res.item
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
role="button"
|
:id="id || name"
|
||||||
|
:aria-labelledby="id || name"
|
||||||
|
role="checkbox"
|
||||||
|
:aria-checked="props.modelValue"
|
||||||
|
class="flex"
|
||||||
@click.stop="onClick"
|
@click.stop="onClick"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="inline-flex items-center h-6 w-12 p-1 bg-gray-300 border rounded-full cursor-pointer focus:outline-none transition-all transform ease-in-out duration-100"
|
class="inline-flex items-center bg-gray-300 rounded-full cursor-pointer focus:outline-none transition-all transform ease-in-out duration-100"
|
||||||
:class="{ 'toggle-switch': props.modelValue }"
|
:class="[{ 'toggle-switch': props.modelValue }, theme.SwitchInput.containerSize]"
|
||||||
:style="{ '--accent-color': props.color }"
|
:style="{ '--accent-color': props.color }"
|
||||||
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="inline-block h-4 w-4 rounded-full bg-white shadow transition-all transform ease-in-out duration-150 rounded-2xl scale-100"
|
class="inline-block h-4 w-4 rounded-full bg-white transition-all transform ease-in-out duration-150 scale-100"
|
||||||
:class="{ 'translate-x-5.5': props.modelValue }"
|
:class="{ [theme.SwitchInput.translatedClass]: props.modelValue}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -21,9 +24,12 @@
|
||||||
import { defineEmits, defineProps } from "vue"
|
import { defineEmits, defineProps } from "vue"
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
id: { type: String, default: null },
|
||||||
|
name: { type: String, default: "checkbox" },
|
||||||
modelValue: { type: Boolean, default: false },
|
modelValue: { type: Boolean, default: false },
|
||||||
disabled: { type: Boolean, default: false },
|
disabled: { type: Boolean, default: false },
|
||||||
color: { type: String, default: '#3B82F6' },
|
color: { type: String, default: '#3B82F6' },
|
||||||
|
theme: { type: Object },
|
||||||
})
|
})
|
||||||
const emit = defineEmits(["update:modelValue"])
|
const emit = defineEmits(["update:modelValue"])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,23 @@
|
||||||
import {ref, computed, watch} from "vue"
|
import {ref, computed, watch} from "vue"
|
||||||
import { themes } from "~/lib/forms/form-themes.js"
|
|
||||||
import {default as _get} from "lodash/get"
|
import {default as _get} from "lodash/get"
|
||||||
import {default as _set} from "lodash/set"
|
import {default as _set} from "lodash/set"
|
||||||
import {default as _has} from "lodash/has"
|
import {default as _has} from "lodash/has"
|
||||||
|
import CachedDefaultTheme from "~/lib/forms/themes/CachedDefaultTheme.js"
|
||||||
|
|
||||||
export const inputProps = {
|
export const inputProps = {
|
||||||
id: {type: String, default: null},
|
id: {type: String, default: null},
|
||||||
name: {type: String, required: true},
|
name: {type: String, required: true},
|
||||||
label: {type: String, required: false},
|
label: {type: String, required: false},
|
||||||
form: {type: Object, required: false},
|
form: {type: Object, required: false},
|
||||||
theme: { type: Object, default: () => themes.default },
|
theme: {
|
||||||
|
type: Object, default: () => {
|
||||||
|
const theme = inject("theme", null)
|
||||||
|
if (theme) {
|
||||||
|
return theme.value
|
||||||
|
}
|
||||||
|
return CachedDefaultTheme.getInstance()
|
||||||
|
}
|
||||||
|
},
|
||||||
modelValue: {required: false},
|
modelValue: {required: false},
|
||||||
required: {type: Boolean, default: false},
|
required: {type: Boolean, default: false},
|
||||||
disabled: {type: Boolean, default: false},
|
disabled: {type: Boolean, default: false},
|
||||||
|
|
@ -20,10 +28,14 @@ export const inputProps = {
|
||||||
help: {type: String, default: null},
|
help: {type: String, default: null},
|
||||||
helpPosition: {type: String, default: "below_input"},
|
helpPosition: {type: String, default: "below_input"},
|
||||||
color: {type: String, default: "#3B82F6"},
|
color: {type: String, default: "#3B82F6"},
|
||||||
wrapperClass: { type: String, default: "relative mb-3" },
|
wrapperClass: {type: String, default: ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useFormInput(props, context, formPrefixKey = null) {
|
export function useFormInput(props, context, options = {}) {
|
||||||
|
const composableOptions = {
|
||||||
|
formPrefixKey: null,
|
||||||
|
...options
|
||||||
|
}
|
||||||
const content = ref(props.modelValue)
|
const content = ref(props.modelValue)
|
||||||
|
|
||||||
const inputStyle = computed(() => {
|
const inputStyle = computed(() => {
|
||||||
|
|
@ -47,13 +59,13 @@ export function useFormInput(props, context, formPrefixKey = null) {
|
||||||
const compVal = computed({
|
const compVal = computed({
|
||||||
get: () => {
|
get: () => {
|
||||||
if (props.form) {
|
if (props.form) {
|
||||||
return _get(props.form, (formPrefixKey || "") + props.name)
|
return _get(props.form, (composableOptions.formPrefixKey || "") + props.name)
|
||||||
}
|
}
|
||||||
return content.value
|
return content.value
|
||||||
},
|
},
|
||||||
set: (val) => {
|
set: (val) => {
|
||||||
if (props.form) {
|
if (props.form) {
|
||||||
_set(props.form, (formPrefixKey || "") + props.name, val)
|
_set(props.form, (composableOptions.formPrefixKey || "") + props.name, val)
|
||||||
} else {
|
} else {
|
||||||
content.value = val
|
content.value = val
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, defineProps, defineEmits } from "vue"
|
import { ref, defineProps, defineEmits } from "vue"
|
||||||
import OpenForm from "../forms/OpenForm.vue"
|
import OpenForm from "../forms/OpenForm.vue"
|
||||||
import { themes } from "~/lib/forms/form-themes.js"
|
import { themes } from "~/lib/forms/themes/form-themes.js"
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
show: { type: Boolean, required: true },
|
show: { type: Boolean, required: true },
|
||||||
form: { type: Object, required: true },
|
form: { type: Object, required: true },
|
||||||
|
|
|
||||||
|
|
@ -193,13 +193,12 @@
|
||||||
<script>
|
<script>
|
||||||
import OpenForm from './OpenForm.vue'
|
import OpenForm from './OpenForm.vue'
|
||||||
import OpenFormButton from './OpenFormButton.vue'
|
import OpenFormButton from './OpenFormButton.vue'
|
||||||
import { themes } from '~/lib/forms/form-themes.js'
|
|
||||||
import VButton from '~/components/global/VButton.vue'
|
import VButton from '~/components/global/VButton.vue'
|
||||||
import FormCleanings from '../../pages/forms/show/FormCleanings.vue'
|
import FormCleanings from '../../pages/forms/show/FormCleanings.vue'
|
||||||
import VTransition from '~/components/global/transitions/VTransition.vue'
|
import VTransition from '~/components/global/transitions/VTransition.vue'
|
||||||
import {pendingSubmission} from "~/composables/forms/pendingSubmission.js"
|
import {pendingSubmission} from "~/composables/forms/pendingSubmission.js"
|
||||||
import clonedeep from "clone-deep"
|
import clonedeep from "clone-deep"
|
||||||
import { default as _has } from 'lodash/has'
|
import ThemeBuilder from "~/lib/forms/themes/ThemeBuilder.js"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { VTransition, VButton, OpenFormButton, OpenForm, FormCleanings },
|
components: { VTransition, VButton, OpenFormButton, OpenForm, FormCleanings },
|
||||||
|
|
@ -227,7 +226,6 @@ export default {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
submitted: false,
|
submitted: false,
|
||||||
themes: themes,
|
|
||||||
passwordForm: useForm({
|
passwordForm: useForm({
|
||||||
password: null
|
password: null
|
||||||
}),
|
}),
|
||||||
|
|
@ -241,7 +239,10 @@ export default {
|
||||||
return import.meta.client && window.location.href.includes('popup=true')
|
return import.meta.client && window.location.href.includes('popup=true')
|
||||||
},
|
},
|
||||||
theme () {
|
theme () {
|
||||||
return this.themes[_has(this.themes, this.form.theme) ? this.form.theme : 'default']
|
return new ThemeBuilder(this.form.theme, {
|
||||||
|
size: this.form.size,
|
||||||
|
borderRadius: this.form.border_radius
|
||||||
|
}).getAllComponents()
|
||||||
},
|
},
|
||||||
isPublicFormPage () {
|
isPublicFormPage () {
|
||||||
return this.$route.name === 'forms-slug'
|
return this.$route.name === 'forms-slug'
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<button
|
<button
|
||||||
:type="nativeType"
|
:type="nativeType"
|
||||||
:disabled="loading ? true : null"
|
:disabled="loading ? true : null"
|
||||||
:class="`py-${sizes['p-y']} px-${sizes['p-x']} text-${sizes['font']} ${theme.Button.body}`"
|
:class="[`py-${sizes['p-y']} px-${sizes['p-x']} text-${sizes['font']}`,theme.Button.body,theme.Button.borderRadius]"
|
||||||
:style="buttonStyle"
|
:style="buttonStyle"
|
||||||
class="btn"
|
class="btn"
|
||||||
>
|
>
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { themes } from "~/lib/forms/form-themes.js"
|
import {themes} from "~/lib/forms/themes/form-themes.js"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "OpenFormButton",
|
name: "OpenFormButton",
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,10 @@
|
||||||
allow you to preview your form changes.
|
allow you to preview your form changes.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<VForm
|
||||||
|
size="sm"
|
||||||
|
@submit.prevent=""
|
||||||
|
>
|
||||||
<form-information />
|
<form-information />
|
||||||
<form-structure />
|
<form-structure />
|
||||||
<form-customization />
|
<form-customization />
|
||||||
|
|
@ -99,6 +103,7 @@
|
||||||
<form-security-privacy />
|
<form-security-privacy />
|
||||||
<form-custom-seo />
|
<form-custom-seo />
|
||||||
<form-custom-code />
|
<form-custom-code />
|
||||||
|
</VForm>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form-editor-preview />
|
<form-editor-preview />
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,11 @@
|
||||||
<p class="flex-grow truncate">
|
<p class="flex-grow truncate">
|
||||||
{{ field.name }}
|
{{ field.name }}
|
||||||
</p>
|
</p>
|
||||||
<v-switch
|
<ToggleSwitchInput
|
||||||
v-model="displayColumns[field.id]"
|
v-model="displayColumns[field.id]"
|
||||||
class="float-right"
|
wrapper-class="mb-0"
|
||||||
|
label=""
|
||||||
|
name="field.id"
|
||||||
@update:model-value="onChangeDisplayColumns"
|
@update:model-value="onChangeDisplayColumns"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -77,9 +79,11 @@
|
||||||
<p class="flex-grow truncate">
|
<p class="flex-grow truncate">
|
||||||
{{ field.name }}
|
{{ field.name }}
|
||||||
</p>
|
</p>
|
||||||
<v-switch
|
<ToggleSwitchInput
|
||||||
v-model="displayColumns[field.id]"
|
v-model="displayColumns[field.id]"
|
||||||
class="float-right"
|
wrapper-class="mb-0"
|
||||||
|
label=""
|
||||||
|
name="field.id"
|
||||||
@update:model-value="onChangeDisplayColumns"
|
@update:model-value="onChangeDisplayColumns"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -98,12 +102,14 @@
|
||||||
class="flex flex-wrap items-end"
|
class="flex flex-wrap items-end"
|
||||||
>
|
>
|
||||||
<div class="flex-grow">
|
<div class="flex-grow">
|
||||||
|
<VForm size="sm">
|
||||||
<text-input
|
<text-input
|
||||||
class="w-64"
|
class="w-64"
|
||||||
:form="searchForm"
|
:form="searchForm"
|
||||||
name="search"
|
name="search"
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
/>
|
/>
|
||||||
|
</VForm>
|
||||||
</div>
|
</div>
|
||||||
<div class="font-semibold flex gap-4">
|
<div class="font-semibold flex gap-4">
|
||||||
<p class="float-right text-xs uppercase mb-2">
|
<p class="float-right text-xs uppercase mb-2">
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,10 @@
|
||||||
help="Gives user a unique url to update their submission"
|
help="Gives user a unique url to update their submission"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
|
<span class="text-sm">
|
||||||
Editable submissions
|
Editable submissions
|
||||||
<pro-tag class="ml-1" />
|
</span>
|
||||||
|
<pro-tag class="-mt-1 ml-1" />
|
||||||
</template>
|
</template>
|
||||||
</toggle-switch-input>
|
</toggle-switch-input>
|
||||||
<text-input
|
<text-input
|
||||||
|
|
@ -138,7 +140,7 @@
|
||||||
help="Show a message, or redirect to a URL"
|
help="Show a message, or redirect to a URL"
|
||||||
>
|
>
|
||||||
<template #selected="{ option, optionName }">
|
<template #selected="{ option, optionName }">
|
||||||
<div class="flex items-center truncate mr-6">
|
<div class="flex items-center text-sm truncate mr-6">
|
||||||
{{ optionName }}
|
{{ optionName }}
|
||||||
<pro-tag
|
<pro-tag
|
||||||
v-if="option === 'redirect'"
|
v-if="option === 'redirect'"
|
||||||
|
|
@ -147,8 +149,8 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #option="{ option, selected }">
|
<template #option="{ option, selected }">
|
||||||
<span class="flex hover:text-white">
|
<span class="flex">
|
||||||
<p class="flex-grow hover:text-white">
|
<p class="flex-grow">
|
||||||
{{ option.name }}
|
{{ option.name }}
|
||||||
<template v-if="option.value === 'redirect'"><pro-tag /></template>
|
<template v-if="option.value === 'redirect'"><pro-tag /></template>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,34 @@
|
||||||
label="Form Theme"
|
label="Form Theme"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div class="flex space-x-4 justify-stretch">
|
||||||
|
<select-input
|
||||||
|
name="size"
|
||||||
|
class="flex-grow"
|
||||||
|
:options="[
|
||||||
|
{ name: 'Small', value: 'sm' },
|
||||||
|
{ name: 'Medium', value: 'md' },
|
||||||
|
{ name: 'Large', value: 'lg' },
|
||||||
|
]"
|
||||||
|
:form="form"
|
||||||
|
label="Input Size"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<select-input
|
||||||
|
name="border_radius"
|
||||||
|
class="flex-grow"
|
||||||
|
:options="[
|
||||||
|
{ name: 'None', value: 'none' },
|
||||||
|
{ name: 'Small', value: 'small' },
|
||||||
|
{ name: 'Full', value: 'full' },
|
||||||
|
]"
|
||||||
|
:form="form"
|
||||||
|
label="Input Roundness"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<select-input
|
<select-input
|
||||||
name="dark_mode"
|
name="dark_mode"
|
||||||
class="mt-4"
|
|
||||||
help="To see changes, save your form and open it"
|
help="To see changes, save your form and open it"
|
||||||
:options="[
|
:options="[
|
||||||
{ name: 'Auto - use Device System Preferences', value: 'auto' },
|
{ name: 'Auto - use Device System Preferences', value: 'auto' },
|
||||||
|
|
@ -68,7 +93,6 @@
|
||||||
|
|
||||||
<select-input
|
<select-input
|
||||||
name="width"
|
name="width"
|
||||||
class="mt-4"
|
|
||||||
:options="[
|
:options="[
|
||||||
{ name: 'Centered', value: 'centered' },
|
{ name: 'Centered', value: 'centered' },
|
||||||
{ name: 'Full Width', value: 'full' },
|
{ name: 'Full Width', value: 'full' },
|
||||||
|
|
@ -80,7 +104,6 @@
|
||||||
|
|
||||||
<image-input
|
<image-input
|
||||||
name="cover_picture"
|
name="cover_picture"
|
||||||
class="mt-4"
|
|
||||||
:form="form"
|
:form="form"
|
||||||
label="Cover Picture"
|
label="Cover Picture"
|
||||||
help="Not visible when form is embedded"
|
help="Not visible when form is embedded"
|
||||||
|
|
@ -89,7 +112,6 @@
|
||||||
|
|
||||||
<image-input
|
<image-input
|
||||||
name="logo_picture"
|
name="logo_picture"
|
||||||
class="mt-4"
|
|
||||||
:form="form"
|
:form="form"
|
||||||
label="Logo"
|
label="Logo"
|
||||||
help="Not visible when form is embedded"
|
help="Not visible when form is embedded"
|
||||||
|
|
@ -98,49 +120,44 @@
|
||||||
|
|
||||||
<color-input
|
<color-input
|
||||||
name="color"
|
name="color"
|
||||||
class="mt-4"
|
|
||||||
:form="form"
|
:form="form"
|
||||||
label="Color (for buttons & inputs border)"
|
label="Color (for buttons & inputs border)"
|
||||||
/>
|
/>
|
||||||
<toggle-switch-input
|
<toggle-switch-input
|
||||||
name="hide_title"
|
name="hide_title"
|
||||||
:form="form"
|
:form="form"
|
||||||
class="mt-4"
|
|
||||||
label="Hide Title"
|
label="Hide Title"
|
||||||
/>
|
/>
|
||||||
<toggle-switch-input
|
<toggle-switch-input
|
||||||
name="no_branding"
|
name="no_branding"
|
||||||
:form="form"
|
:form="form"
|
||||||
class="mt-4"
|
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
|
<span class="text-sm">
|
||||||
Remove OpnForm Branding
|
Remove OpnForm Branding
|
||||||
<pro-tag class="ml-1" />
|
</span>
|
||||||
|
<pro-tag class="-mt-1" />
|
||||||
</template>
|
</template>
|
||||||
</toggle-switch-input>
|
</toggle-switch-input>
|
||||||
<toggle-switch-input
|
<toggle-switch-input
|
||||||
name="show_progress_bar"
|
name="show_progress_bar"
|
||||||
:form="form"
|
:form="form"
|
||||||
class="mt-4"
|
|
||||||
label="Show progress bar"
|
label="Show progress bar"
|
||||||
/>
|
/>
|
||||||
<toggle-switch-input
|
<toggle-switch-input
|
||||||
name="uppercase_labels"
|
name="uppercase_labels"
|
||||||
:form="form"
|
:form="form"
|
||||||
class="mt-4"
|
|
||||||
label="Uppercase Input Labels"
|
label="Uppercase Input Labels"
|
||||||
/>
|
/>
|
||||||
<toggle-switch-input
|
<toggle-switch-input
|
||||||
name="transparent_background"
|
name="transparent_background"
|
||||||
:form="form"
|
:form="form"
|
||||||
class="mt-4"
|
|
||||||
label="Transparent Background"
|
label="Transparent Background"
|
||||||
help="Only applies when form is embedded"
|
help="Only applies when form is embedded"
|
||||||
/>
|
/>
|
||||||
<toggle-switch-input
|
<toggle-switch-input
|
||||||
name="confetti_on_submission"
|
name="confetti_on_submission"
|
||||||
:form="form"
|
:form="form"
|
||||||
class="mt-4"
|
|
||||||
label="Confetti on successful submisison"
|
label="Confetti on successful submisison"
|
||||||
@update:model-value="onChangeConfettiOnSubmission"
|
@update:model-value="onChangeConfettiOnSubmission"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<editor-right-sidebar
|
<editor-right-sidebar
|
||||||
:show="form && (showEditFieldSidebar || showAddFieldSidebar)"
|
:show="form && (showEditFieldSidebar || showAddFieldSidebar)"
|
||||||
|
>
|
||||||
|
<VForm
|
||||||
|
size="sm"
|
||||||
|
@submit.prevent=""
|
||||||
>
|
>
|
||||||
<transition mode="out-in">
|
<transition mode="out-in">
|
||||||
<form-field-edit
|
<form-field-edit
|
||||||
|
|
@ -13,6 +17,7 @@
|
||||||
v-motion-fade="'fade'"
|
v-motion-fade="'fade'"
|
||||||
/>
|
/>
|
||||||
</transition>
|
</transition>
|
||||||
|
</VForm>
|
||||||
</editor-right-sidebar>
|
</editor-right-sidebar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@
|
||||||
name="tags"
|
name="tags"
|
||||||
label="Tags"
|
label="Tags"
|
||||||
:form="form"
|
:form="form"
|
||||||
class="mt-4"
|
|
||||||
help="To organize your forms (hidden to respondents)"
|
help="To organize your forms (hidden to respondents)"
|
||||||
placeholder="Select Tag(s)"
|
placeholder="Select Tag(s)"
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
|
|
@ -47,7 +46,6 @@
|
||||||
name="visibility"
|
name="visibility"
|
||||||
label="Visibility"
|
label="Visibility"
|
||||||
:form="form"
|
:form="form"
|
||||||
class="mt-4"
|
|
||||||
help="Only public form will be accessible"
|
help="Only public form will be accessible"
|
||||||
placeholder="Select Visibility"
|
placeholder="Select Visibility"
|
||||||
:required="true"
|
:required="true"
|
||||||
|
|
@ -56,7 +54,7 @@
|
||||||
<v-button
|
<v-button
|
||||||
v-if="copyFormOptions.length > 0"
|
v-if="copyFormOptions.length > 0"
|
||||||
color="light-gray"
|
color="light-gray"
|
||||||
class="w-full mt-4"
|
class="w-full"
|
||||||
@click="showCopyFormSettingsModal = true"
|
@click="showCopyFormSettingsModal = true"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
|
@ -98,20 +96,13 @@
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
<template #title>
|
<template #title>
|
||||||
Copy Settings from another form
|
Import Settings from another form
|
||||||
</template>
|
</template>
|
||||||
<div class="p-4 min-h-[450px]">
|
<div>
|
||||||
<p class="text-gray-600">
|
|
||||||
If you already have another form that you like to use as a base for
|
|
||||||
this form, you can do that here. Select another form, confirm, and we
|
|
||||||
will copy all of the other form settings (except the form structure)
|
|
||||||
to this form.
|
|
||||||
</p>
|
|
||||||
<select-input
|
<select-input
|
||||||
v-model="copyFormId"
|
v-model="copyFormId"
|
||||||
name="copy_form_id"
|
name="copy_form_id"
|
||||||
label="Copy Settings From"
|
label="Copy Settings From"
|
||||||
class="mt-3 mb-6"
|
|
||||||
placeholder="Choose a form"
|
placeholder="Choose a form"
|
||||||
:searchable="copyFormOptions.length > 5"
|
:searchable="copyFormOptions.length > 5"
|
||||||
:options="copyFormOptions"
|
:options="copyFormOptions"
|
||||||
|
|
|
||||||
|
|
@ -31,14 +31,13 @@
|
||||||
|
|
||||||
<!-- Remember Me -->
|
<!-- Remember Me -->
|
||||||
<div class="relative flex items-center my-5">
|
<div class="relative flex items-center my-5">
|
||||||
<v-checkbox
|
<CheckboxInput
|
||||||
v-model="remember"
|
v-model="remember"
|
||||||
class="w-full md:w-1/2"
|
class="w-full md:w-1/2"
|
||||||
name="remember"
|
name="remember"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
label="Remember me"
|
||||||
Remember me
|
/>
|
||||||
</v-checkbox>
|
|
||||||
|
|
||||||
<div class="w-full md:w-1/2 text-right">
|
<div class="w-full md:w-1/2 text-right">
|
||||||
<a
|
<a
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@
|
||||||
<script>
|
<script>
|
||||||
import FormUrlPrefill from "../../../open/forms/components/FormUrlPrefill.vue"
|
import FormUrlPrefill from "../../../open/forms/components/FormUrlPrefill.vue"
|
||||||
import OpenForm from "../../../open/forms/OpenForm.vue"
|
import OpenForm from "../../../open/forms/OpenForm.vue"
|
||||||
import { themes } from "~/lib/forms/form-themes.js"
|
import { themes } from "~/lib/forms/themes/form-themes.js"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "UrlFormPrefill",
|
name: "UrlFormPrefill",
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export const initForm = (defaultValue = {}, withDefaultProperties = false) => {
|
||||||
color: "#3B82F6",
|
color: "#3B82F6",
|
||||||
hide_title: false,
|
hide_title: false,
|
||||||
no_branding: false,
|
no_branding: false,
|
||||||
uppercase_labels: true,
|
uppercase_labels: false,
|
||||||
transparent_background: false,
|
transparent_background: false,
|
||||||
closes_at: null,
|
closes_at: null,
|
||||||
closed_text:
|
closed_text:
|
||||||
|
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
/**
|
|
||||||
Input classes for each supported form themes
|
|
||||||
*/
|
|
||||||
export const themes = {
|
|
||||||
default: {
|
|
||||||
default: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
input: 'rounded-lg border-gray-300 flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full py-2 px-4 bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100',
|
|
||||||
inputSpacing: {
|
|
||||||
vertical: 'py-2',
|
|
||||||
horizontal: 'px-4'
|
|
||||||
},
|
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
|
||||||
},
|
|
||||||
Button: {
|
|
||||||
body: 'transition ease-in duration-200 text-center font-semibold shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2 rounded-lg filter hover:brightness-110'
|
|
||||||
},
|
|
||||||
DateInput: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
input: 'rounded-lg flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100',
|
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
|
||||||
},
|
|
||||||
CodeInput: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
input: 'rounded-lg border border-gray-300 dark:border-gray-600 overflow-hidden',
|
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
|
||||||
},
|
|
||||||
RichTextAreaInput: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
input: 'rounded-lg border-gray-300 flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-1 focus:ring-opacity-100 focus:border-transparent focus:ring-2',
|
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
|
||||||
},
|
|
||||||
SelectInput: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
input: 'relative w-full rounded-lg border-gray-300 flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full bg-white text-gray-700 placeholder-gray-400 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-600 shadow-sm text-base focus:outline-none focus:ring-2 focus:border-transparent',
|
|
||||||
help: 'text-gray-400 dark:text-gray-500',
|
|
||||||
dropdown: 'rounded-lg border border-gray-300 dark:border-gray-600',
|
|
||||||
option: 'rounded-md'
|
|
||||||
},
|
|
||||||
ScaleInput: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
button: 'cursor-pointer text-gray-700 inline-block rounded-lg border-gray-300 px-4 py-2 flex-grow dark:bg-notion-dark-light dark:text-gray-300 text-center',
|
|
||||||
unselectedButton: 'bg-white hover:bg-gray-50 border',
|
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
|
||||||
},
|
|
||||||
SliderInput: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
stepLabel: 'text-gray-700 dark:text-gray-300 text-center text-xs',
|
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
|
||||||
},
|
|
||||||
fileInput: {
|
|
||||||
input: 'min-h-40 border border-dashed border-gray-300 p-4 rounded-lg',
|
|
||||||
cameraInput: 'min-h-40 rounded-lg',
|
|
||||||
inputHover: {
|
|
||||||
light: 'bg-neutral-50',
|
|
||||||
dark: 'bg-notion-dark-light'
|
|
||||||
},
|
|
||||||
uploadedFile: 'border border-gray-300 dark:border-gray-600 bg-white dark:bg-notion-dark-light rounded-lg shadow-sm max-w-[10rem]'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
simple: {
|
|
||||||
default: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
input: 'flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full py-2 px-2 bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 text-base focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100',
|
|
||||||
inputSpacing: {
|
|
||||||
vertical: 'py-2',
|
|
||||||
horizontal: 'px-4'
|
|
||||||
},
|
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
|
||||||
},
|
|
||||||
Button: {
|
|
||||||
body: 'transition ease-in duration-200 text-center font-semibold focus:outline-none focus:ring-2 focus:ring-offset-2 filter hover:brightness-110'
|
|
||||||
},
|
|
||||||
DateInput: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
input: 'flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 placeholder-gray-400 text-base focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100',
|
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
|
||||||
},
|
|
||||||
SelectInput: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
input: 'relative w-full flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full bg-white text-gray-700 placeholder-gray-400 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-600 text-base focus:outline-none focus:ring-2 focus:border-transparent',
|
|
||||||
help: 'text-gray-400 dark:text-gray-500',
|
|
||||||
dropdown: 'border border-gray-300 dark:border-gray-600',
|
|
||||||
option: ''
|
|
||||||
},
|
|
||||||
CodeInput: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
input: 'border border-gray-300 dark:border-gray-600 overflow-hidden',
|
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
|
||||||
},
|
|
||||||
RichTextAreaInput: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
input: 'border-transparent flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 text-base focus:outline-none focus:ring-1 focus:ring-opacity-100 focus:border-transparent focus:ring-2',
|
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
|
||||||
},
|
|
||||||
ScaleInput: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
button: 'flex-1 appearance-none border-gray-300 dark:border-gray-600 w-full py-2 px-2 bg-gray-50 text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 text-center',
|
|
||||||
unselectedButton: 'bg-white hover:bg-gray-50 border -mx-4',
|
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
|
||||||
},
|
|
||||||
SliderInput: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
stepLabel: 'text-gray-700 dark:text-gray-300 text-center text-xs',
|
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
|
||||||
},
|
|
||||||
fileInput: {
|
|
||||||
input: 'min-h-40 border border-dashed border-gray-300 p-4',
|
|
||||||
cameraInput: 'min-h-40',
|
|
||||||
inputHover: {
|
|
||||||
light: 'bg-neutral-50',
|
|
||||||
dark: 'bg-notion-dark-light'
|
|
||||||
},
|
|
||||||
uploadedFile: 'border border-gray-300 dark:border-gray-600 bg-white dark:bg-notion-dark-light shadow-sm max-w-[10rem]'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
notion: {
|
|
||||||
default: {
|
|
||||||
label: 'text-gray-900 dark:text-gray-100 mb-2 block mt-4',
|
|
||||||
input: 'rounded border-transparent flex-1 appearance-none shadow-inner-notion w-full py-2 px-2 bg-notion-input-background dark:bg-notion-dark-light text-gray-900 dark:text-gray-100 dark:placeholder-gray-500 placeholder-gray-400 text-base focus:outline-none focus:ring-0 focus:border-transparent focus:shadow-focus-notion',
|
|
||||||
inputSpacing: {
|
|
||||||
vertical: 'py-2',
|
|
||||||
horizontal: 'px-4'
|
|
||||||
},
|
|
||||||
help: 'text-notion-input-help dark:text-gray-500'
|
|
||||||
},
|
|
||||||
Button: {
|
|
||||||
body: 'rounded-md transition ease-in duration-200 text-center font-semibold shadow shadow-inner-notion focus:outline-none focus:ring-2 focus:ring-offset-2 filter hover:brightness-110'
|
|
||||||
},
|
|
||||||
DateInput: {
|
|
||||||
label: 'text-gray-900 dark:text-gray-100 mb-2 block mt-4',
|
|
||||||
input: 'rounded shadow-inner-notion border-transparent focus:border-transparent flex-1 appearance-none w-full bg-notion-input-background dark:bg-notion-dark-light text-gray-900 dark:text-gray-100 placeholder-gray-400 text-base focus:outline-none focus:ring-0 focus:border-transparent focus:shadow-focus-notion p-[1px]',
|
|
||||||
help: 'text-notion-input-help dark:text-gray-500'
|
|
||||||
},
|
|
||||||
SelectInput: {
|
|
||||||
label: 'text-gray-900 dark:text-gray-100 mb-2 block mt-4',
|
|
||||||
input: 'rounded relative w-full border-transparent flex-1 appearance-none bg-notion-input-background shadow-inner-notion w-full px-2 text-gray-900 placeholder-gray-400 dark:bg-notion-dark-light dark:placeholder-gray-500 text-base focus:outline-none focus:ring-0 focus:border-transparent focus:shadow-focus-notion',
|
|
||||||
help: 'text-notion-input-help dark:text-gray-500',
|
|
||||||
dropdown: 'rounded border border-gray-300 dark:border-gray-600',
|
|
||||||
option: 'rounded'
|
|
||||||
},
|
|
||||||
CodeInput: {
|
|
||||||
label: 'text-gray-900 dark:text-gray-100 mb-2 block mt-4',
|
|
||||||
input: 'rounded shadow-inner-notion border border-gray-300 dark:border-gray-600 overflow-hidden',
|
|
||||||
help: 'text-notion-input-help dark:text-gray-500'
|
|
||||||
},
|
|
||||||
RichTextAreaInput: {
|
|
||||||
label: 'text-gray-900 dark:text-gray-100 mb-2 block mt-4',
|
|
||||||
input: 'rounded border-transparent flex-1 appearance-none shadow-inner-notion border border-gray-300 dark:border-gray-600 w-full text-gray-900 bg-notion-input-background dark:bg-notion-dark-light shadow-inner dark:placeholder-gray-500 placeholder-gray-400 text-base focus:outline-none focus:ring-0 focus:ring-opacity-100 focus:border-transparent focus:ring-0 focus:shadow-focus-notion',
|
|
||||||
help: 'text-notion-input-help dark:text-gray-500'
|
|
||||||
},
|
|
||||||
ScaleInput: {
|
|
||||||
label: 'text-gray-900 dark:text-gray-100 mb-2 block mt-4',
|
|
||||||
button: 'rounded border-transparent flex-1 appearance-none shadow-inner-notion w-full py-2 px-2 bg-notion-input-background dark:bg-notion-dark-light text-gray-900 dark:text-gray-100 text-center',
|
|
||||||
unselectedButton: 'bg-notion-input-background dark:bg-notion-dark-light hover:bg-gray-50 border',
|
|
||||||
help: 'text-notion-input-help dark:text-gray-500'
|
|
||||||
},
|
|
||||||
SliderInput: {
|
|
||||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
|
||||||
stepLabel: 'text-gray-700 dark:text-gray-300 text-center text-xs',
|
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
|
||||||
},
|
|
||||||
fileInput: {
|
|
||||||
input: 'min-h-40 border border-dashed border-gray-300 p-4 rounded bg-notion-input-background',
|
|
||||||
cameraInput: 'min-h-40 rounded',
|
|
||||||
inputHover: {
|
|
||||||
light: 'bg-neutral-50',
|
|
||||||
dark: 'bg-notion-dark-light'
|
|
||||||
},
|
|
||||||
uploadedFile: 'border border-gray-300 dark:border-gray-600 bg-white dark:bg-notion-dark-light rounded shadow-sm max-w-[10rem]'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import ThemeBuilder from './ThemeBuilder.js'
|
||||||
|
|
||||||
|
const CachedDefaultTheme = (function() {
|
||||||
|
let instance
|
||||||
|
|
||||||
|
function createInstance() {
|
||||||
|
const themeBuilder = new ThemeBuilder()
|
||||||
|
return themeBuilder.getAllComponents()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getInstance: function() {
|
||||||
|
if (!instance) {
|
||||||
|
instance = createInstance()
|
||||||
|
}
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
export default CachedDefaultTheme
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
import {twMerge} from 'tailwind-merge'
|
||||||
|
import {themes} from './form-themes.js'
|
||||||
|
|
||||||
|
export const sizes = ['sm', 'md', 'lg']
|
||||||
|
|
||||||
|
class ThemeBuilder {
|
||||||
|
constructor(theme = 'default', options = {}) {
|
||||||
|
this.theme = themes[theme] || themes.default
|
||||||
|
this.size = options.size || 'md'
|
||||||
|
this.borderRadius = options.borderRadius || 'small'
|
||||||
|
}
|
||||||
|
|
||||||
|
extractSizedClasses(baseConfig) {
|
||||||
|
if (typeof baseConfig === 'object' &&
|
||||||
|
sizes.every((size) => baseConfig[size])) {
|
||||||
|
return baseConfig[this.size]
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeNestedClasses(baseConfig, componentConfig) {
|
||||||
|
const mergedConfig = {}
|
||||||
|
|
||||||
|
const allKeys = new Set([
|
||||||
|
...Object.keys(baseConfig),
|
||||||
|
...Object.keys(componentConfig),
|
||||||
|
])
|
||||||
|
|
||||||
|
allKeys.forEach((key) => {
|
||||||
|
const baseValue = this.extractSizedClasses(baseConfig[key])
|
||||||
|
const componentValue = this.extractSizedClasses(componentConfig[key])
|
||||||
|
|
||||||
|
if (key === 'borderRadius') {
|
||||||
|
// Special case for border radius
|
||||||
|
const borderRadiusClass = baseConfig.borderRadius?.[this.borderRadius] || ''
|
||||||
|
mergedConfig[key] = twMerge(borderRadiusClass, componentValue)
|
||||||
|
} else if (
|
||||||
|
typeof baseValue === 'object' &&
|
||||||
|
baseValue !== null &&
|
||||||
|
!Array.isArray(baseValue)) {
|
||||||
|
mergedConfig[key] = this.mergeNestedClasses(baseValue, componentValue || {})
|
||||||
|
} else {
|
||||||
|
mergedConfig[key] = twMerge(baseValue || '', componentValue || '')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return mergedConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
getComponentTheme(componentName = 'default') {
|
||||||
|
const baseComponentConfig = this.theme.default || {}
|
||||||
|
const componentConfig = this.theme[componentName] || {}
|
||||||
|
return this.mergeNestedClasses(baseComponentConfig, componentConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all components classes for the selected theme
|
||||||
|
getAllComponents() {
|
||||||
|
const allComponents = {}
|
||||||
|
|
||||||
|
Object.keys(this.theme).forEach((componentName) => {
|
||||||
|
allComponents[componentName] = this.getComponentTheme(componentName)
|
||||||
|
})
|
||||||
|
|
||||||
|
return allComponents
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ThemeBuilder
|
||||||
|
|
@ -0,0 +1,380 @@
|
||||||
|
/**
|
||||||
|
Input classes for each supported form themes
|
||||||
|
*/
|
||||||
|
export const themes = {
|
||||||
|
default: {
|
||||||
|
default: {
|
||||||
|
wrapper: {
|
||||||
|
sm: 'relative mb-2',
|
||||||
|
md: 'relative mb-3',
|
||||||
|
lg: 'relative mb-3',
|
||||||
|
},
|
||||||
|
label: 'text-gray-700 dark:text-gray-300 font-medium',
|
||||||
|
input:
|
||||||
|
'flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100',
|
||||||
|
help: 'text-gray-500',
|
||||||
|
spacing: {
|
||||||
|
horizontal: {
|
||||||
|
sm: 'px-2',
|
||||||
|
md: 'px-4',
|
||||||
|
lg: 'px-5'
|
||||||
|
},
|
||||||
|
vertical: {
|
||||||
|
sm: 'py-1.5',
|
||||||
|
md: 'py-2',
|
||||||
|
lg: 'py-3'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fontSize: {
|
||||||
|
sm: 'text-sm',
|
||||||
|
md: 'text-base',
|
||||||
|
lg: 'text-lg'
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
none: 'rounded-none',
|
||||||
|
small: 'rounded-lg',
|
||||||
|
full: 'rounded-[20px]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ScaleInput: {
|
||||||
|
button: 'cursor-pointer text-gray-700 inline-block border-gray-300 flex-grow dark:bg-notion-dark-light dark:text-gray-300 text-center',
|
||||||
|
unselectedButton: 'bg-white border'
|
||||||
|
},
|
||||||
|
SliderInput: {
|
||||||
|
stepLabel: 'text-gray-700 dark:text-gray-300 text-center text-xs'
|
||||||
|
},
|
||||||
|
Button: {
|
||||||
|
body: 'transition ease-in duration-200 text-center font-semibold shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2 filter hover:brightness-90'
|
||||||
|
},
|
||||||
|
CodeInput: {
|
||||||
|
input: 'overflow-hidden'
|
||||||
|
},
|
||||||
|
RichTextAreaInput: {
|
||||||
|
input:
|
||||||
|
'flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-1 focus:ring-opacity-100 focus:border-transparent focus:ring-2'
|
||||||
|
},
|
||||||
|
SelectInput: {
|
||||||
|
input:
|
||||||
|
'relative w-full flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full bg-white text-gray-700 placeholder-gray-400 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-600 shadow-sm text-base focus:outline-none focus:ring-2 focus:border-transparent',
|
||||||
|
dropdown: 'border border-gray-300 dark:border-gray-600',
|
||||||
|
option: 'rounded',
|
||||||
|
minHeight: {
|
||||||
|
sm: 'min-h-[20px]',
|
||||||
|
md: 'min-h-[24px]',
|
||||||
|
lg: 'min-h-[28px]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FlatSelectInput: {
|
||||||
|
option: 'cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-900 flex items-center space-x-2 border-t first:border-t-0 px-2',
|
||||||
|
unselectedIcon: 'text-gray-300 dark:text-gray-600',
|
||||||
|
icon: {
|
||||||
|
sm: 'w-4 h-4',
|
||||||
|
md: 'w-5 h-5',
|
||||||
|
lg: 'w-6 h-6 mx-1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DateInput: {
|
||||||
|
input:
|
||||||
|
'flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100'
|
||||||
|
},
|
||||||
|
CheckboxInput:{
|
||||||
|
size: {
|
||||||
|
sm: 'w-4 h-4',
|
||||||
|
md: 'w-5 h-5',
|
||||||
|
lg: 'w-5 h-5'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SwitchInput:{
|
||||||
|
containerSize: {
|
||||||
|
sm: 'h-5 w-10 p-0.5',
|
||||||
|
md: 'h-6 w-12 p-1',
|
||||||
|
lg: 'h-6 w-12 p-1'
|
||||||
|
},
|
||||||
|
circleSize: {
|
||||||
|
sm: 'h-4 w-4',
|
||||||
|
md: 'h-4 w-4',
|
||||||
|
lg: 'h-4 w-4'
|
||||||
|
},
|
||||||
|
translatedClass: {
|
||||||
|
sm: 'translate-x-5',
|
||||||
|
md: 'translate-x-6',
|
||||||
|
lg: 'translate-x-6'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RatingInput:{
|
||||||
|
size: {
|
||||||
|
sm: 'w-6 h-6',
|
||||||
|
md: 'w-8 h-8',
|
||||||
|
lg: 'w-10 h-10'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fileInput: {
|
||||||
|
input:
|
||||||
|
'border border-dashed border-gray-300 dark:border-gray-600 p-4 shadow-none',
|
||||||
|
minHeight: {
|
||||||
|
sm: 'min-h-28',
|
||||||
|
md: 'min-h-40',
|
||||||
|
lg: 'min-h-58'
|
||||||
|
},
|
||||||
|
inputHover: {
|
||||||
|
light: 'bg-neutral-50',
|
||||||
|
dark: 'bg-notion-dark-light'
|
||||||
|
},
|
||||||
|
uploadedFile:
|
||||||
|
'border border-gray-300 dark:border-gray-600 bg-white dark:bg-notion-dark-light rounded-lg shadow-sm max-w-[10rem]'
|
||||||
|
},
|
||||||
|
SignatureInput: {
|
||||||
|
minHeight: {
|
||||||
|
sm: 'min-h-28',
|
||||||
|
md: 'min-h-40',
|
||||||
|
lg: 'min-h-48'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
simple: {
|
||||||
|
default: {
|
||||||
|
wrapper: {
|
||||||
|
sm: 'relative mb-2',
|
||||||
|
md: 'relative mb-3',
|
||||||
|
lg: 'relative mb-3',
|
||||||
|
},
|
||||||
|
label: 'text-gray-700 dark:text-gray-300 font-medium',
|
||||||
|
input:
|
||||||
|
'flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100',
|
||||||
|
help: 'text-gray-500',
|
||||||
|
spacing: {
|
||||||
|
horizontal: {
|
||||||
|
sm: 'px-2',
|
||||||
|
md: 'px-4',
|
||||||
|
lg: 'px-5'
|
||||||
|
},
|
||||||
|
vertical: {
|
||||||
|
sm: 'py-1.5',
|
||||||
|
md: 'py-2',
|
||||||
|
lg: 'py-3'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fontSize: {
|
||||||
|
sm: 'text-sm',
|
||||||
|
md: 'text-base',
|
||||||
|
lg: 'text-lg'
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
none: 'rounded-none',
|
||||||
|
small: 'rounded-lg',
|
||||||
|
full: 'rounded-[20px]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ScaleInput: {
|
||||||
|
button: 'flex-1 appearance-none border-gray-300 dark:border-gray-600 w-full bg-gray-50 text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 text-center',
|
||||||
|
unselectedButton: 'bg-white border'
|
||||||
|
},
|
||||||
|
SliderInput: {
|
||||||
|
stepLabel: 'text-gray-700 dark:text-gray-300 text-center text-xs'
|
||||||
|
},
|
||||||
|
Button: {
|
||||||
|
body: 'transition ease-in duration-200 text-center font-semibold focus:outline-none focus:ring-2 focus:ring-offset-2 filter hover:brightness-90'
|
||||||
|
},
|
||||||
|
CodeInput: {
|
||||||
|
input: 'overflow-hidden'
|
||||||
|
},
|
||||||
|
RichTextAreaInput: {
|
||||||
|
input:
|
||||||
|
'flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 text-base focus:outline-none focus:ring-1 focus:ring-opacity-100 focus:border-transparent focus:ring-2'
|
||||||
|
},
|
||||||
|
SelectInput: {
|
||||||
|
input:
|
||||||
|
'relative w-full flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full bg-white text-gray-700 placeholder-gray-400 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-600 text-base focus:outline-none focus:ring-2 focus:border-transparent',
|
||||||
|
dropdown: 'border border-gray-300 dark:border-gray-600',
|
||||||
|
option: 'rounded',
|
||||||
|
minHeight: {
|
||||||
|
sm: 'min-h-[20px]',
|
||||||
|
md: 'min-h-[24px]',
|
||||||
|
lg: 'min-h-[28px]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FlatSelectInput: {
|
||||||
|
option: 'cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-900 flex items-center space-x-2 border-t first:border-t-0 px-2',
|
||||||
|
unselectedIcon: 'text-gray-300 dark:text-gray-600',
|
||||||
|
icon: {
|
||||||
|
sm: 'w-4 h-4',
|
||||||
|
md: 'w-5 h-5',
|
||||||
|
lg: 'w-6 h-6 mx-1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DateInput: {
|
||||||
|
input: 'flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 placeholder-gray-400 text-base focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100'
|
||||||
|
},
|
||||||
|
CheckboxInput:{
|
||||||
|
size: {
|
||||||
|
sm: 'w-4 h-4',
|
||||||
|
md: 'w-5 h-5',
|
||||||
|
lg: 'w-5 h-5'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SwitchInput:{
|
||||||
|
containerSize: {
|
||||||
|
sm: 'h-5 w-10',
|
||||||
|
md: 'h-6 w-12',
|
||||||
|
lg: 'h-6 w-12'
|
||||||
|
},
|
||||||
|
circleSize: {
|
||||||
|
sm: 'h-4 w-4',
|
||||||
|
md: 'h-4 w-4',
|
||||||
|
lg: 'h-4 w-4'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RatingInput:{
|
||||||
|
size: {
|
||||||
|
sm: 'w-6 h-6',
|
||||||
|
md: 'w-8 h-8',
|
||||||
|
lg: 'w-10 h-10'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fileInput: {
|
||||||
|
input:
|
||||||
|
'border border-dashed border-gray-300 dark:border-gray-600 p-4 shadow-none',
|
||||||
|
minHeight: {
|
||||||
|
sm: 'min-h-28',
|
||||||
|
md: 'min-h-40',
|
||||||
|
lg: 'min-h-48'
|
||||||
|
},
|
||||||
|
inputHover: {
|
||||||
|
light: 'bg-neutral-50',
|
||||||
|
dark: 'bg-notion-dark-light'
|
||||||
|
},
|
||||||
|
uploadedFile:
|
||||||
|
'border border-gray-300 dark:border-gray-600 bg-white dark:bg-notion-dark-light shadow-sm max-w-[10rem]'
|
||||||
|
},
|
||||||
|
SignatureInput: {
|
||||||
|
minHeight: {
|
||||||
|
sm: 'min-h-28',
|
||||||
|
md: 'min-h-40',
|
||||||
|
lg: 'min-h-48'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
notion: {
|
||||||
|
default: {
|
||||||
|
wrapper: {
|
||||||
|
sm: 'relative mb-2',
|
||||||
|
md: 'relative mb-3',
|
||||||
|
lg: 'relative mb-3',
|
||||||
|
},
|
||||||
|
label: 'text-gray-900 dark:text-gray-100 mb-1 block mt-4',
|
||||||
|
input:
|
||||||
|
'rounded border-transparent flex-1 appearance-none shadow-inner-notion w-full bg-notion-input-background dark:bg-notion-dark-light text-gray-900 dark:text-gray-100 dark:placeholder-gray-500 placeholder-gray-400 focus:outline-none focus:ring-0 focus:border-transparent focus:shadow-focus-notion',
|
||||||
|
help: 'text-gray-500',
|
||||||
|
spacing: {
|
||||||
|
horizontal: {
|
||||||
|
sm: 'px-2',
|
||||||
|
md: 'px-4',
|
||||||
|
lg: 'px-5'
|
||||||
|
},
|
||||||
|
vertical: {
|
||||||
|
sm: 'py-1.5',
|
||||||
|
md: 'py-2',
|
||||||
|
lg: 'py-3'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fontSize: {
|
||||||
|
sm: 'text-sm',
|
||||||
|
md: 'text-base',
|
||||||
|
lg: 'text-lg'
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
none: 'rounded-none',
|
||||||
|
small: 'rounded',
|
||||||
|
full: 'rounded-[20px]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ScaleInput: {
|
||||||
|
button: 'border-transparent flex-1 appearance-none shadow-inner-notion w-full bg-notion-input-background dark:bg-notion-dark-light text-gray-900 dark:text-gray-100 text-center',
|
||||||
|
unselectedButton: 'bg-notion-input-background dark:bg-notion-dark-light border'
|
||||||
|
},
|
||||||
|
SliderInput: {
|
||||||
|
stepLabel: 'text-gray-700 dark:text-gray-300 text-center text-xs'
|
||||||
|
},
|
||||||
|
Button: {
|
||||||
|
body: 'transition ease-in duration-200 text-center font-semibold shadow shadow-inner-notion focus:outline-none focus:ring-2 focus:ring-offset-2 filter hover:brightness-90'
|
||||||
|
},
|
||||||
|
CodeInput: {
|
||||||
|
input: 'shadow-inner-notion border-transparent focus:border-transparent overflow-hidden'
|
||||||
|
},
|
||||||
|
RichTextAreaInput: {
|
||||||
|
input:
|
||||||
|
'flex-1 appearance-none shadow-inner-notion border-transparent focus:border-transparent w-full text-gray-900 bg-notion-input-background dark:bg-notion-dark-light dark:placeholder-gray-500 placeholder-gray-400 text-base focus:outline-none focus:ring-0 focus:ring-opacity-100 focus:border-transparent focus:ring-0 focus:shadow-focus-notion'
|
||||||
|
},
|
||||||
|
SelectInput: {
|
||||||
|
input:
|
||||||
|
'relative w-full border-transparent flex-1 appearance-none bg-notion-input-background shadow-inner-notion w-full text-gray-900 placeholder-gray-400 dark:bg-notion-dark-light dark:placeholder-gray-500 text-base focus:outline-none focus:ring-0 focus:border-transparent focus:shadow-focus-notion',
|
||||||
|
dropdown: 'border border-gray-300 dark:border-gray-600',
|
||||||
|
option: 'rounded',
|
||||||
|
minHeight: {
|
||||||
|
sm: 'min-h-[20px]',
|
||||||
|
md: 'min-h-[24px]',
|
||||||
|
lg: 'min-h-[28px]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FlatSelectInput: {
|
||||||
|
option: 'cursor-pointer hover:backdrop-brightness-95 flex items-center space-x-2 border-t border-neutral-300 first:border-t-0 px-2',
|
||||||
|
unselectedIcon: 'text-neutral-300 dark:text-neutral-600',
|
||||||
|
icon: {
|
||||||
|
sm: 'w-4 h-4',
|
||||||
|
md: 'w-5 h-5',
|
||||||
|
lg: 'w-6 h-6 mx-1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DateInput: {
|
||||||
|
input: 'shadow-inner-notion border-transparent focus:border-transparent flex-1 appearance-none w-full bg-notion-input-background dark:bg-notion-dark-light text-gray-900 dark:text-gray-100 placeholder-gray-400 text-base focus:outline-none focus:ring-0 focus:border-transparent focus:shadow-focus-notion p-[1px]'
|
||||||
|
},
|
||||||
|
CheckboxInput:{
|
||||||
|
size: {
|
||||||
|
sm: 'w-4 h-4',
|
||||||
|
md: 'w-5 h-5',
|
||||||
|
lg: 'w-5 h-5'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SwitchInput:{
|
||||||
|
containerSize: {
|
||||||
|
sm: 'h-5 w-10',
|
||||||
|
md: 'h-6 w-12',
|
||||||
|
lg: 'h-6 w-12'
|
||||||
|
},
|
||||||
|
circleSize: {
|
||||||
|
sm: 'h-4 w-4',
|
||||||
|
md: 'h-4 w-4',
|
||||||
|
lg: 'h-4 w-4'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RatingInput:{
|
||||||
|
size: {
|
||||||
|
sm: 'w-6 h-6',
|
||||||
|
md: 'w-8 h-8',
|
||||||
|
lg: 'w-10 h-10'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fileInput: {
|
||||||
|
input:
|
||||||
|
'p-4 rounded bg-notion-input-background dark:bg-notion-dark',
|
||||||
|
minHeight: {
|
||||||
|
sm: 'min-h-28',
|
||||||
|
md: 'min-h-40',
|
||||||
|
lg: 'min-h-48'
|
||||||
|
},
|
||||||
|
inputHover: {
|
||||||
|
light: 'bg-neutral-50',
|
||||||
|
dark: 'bg-notion-dark-light'
|
||||||
|
},
|
||||||
|
uploadedFile:
|
||||||
|
'border border-gray-300 dark:border-gray-600 bg-white dark:bg-notion-dark-light rounded shadow-sm max-w-[10rem]'
|
||||||
|
},
|
||||||
|
SignatureInput: {
|
||||||
|
minHeight: {
|
||||||
|
sm: 'min-h-28',
|
||||||
|
md: 'min-h-40',
|
||||||
|
lg: 'min-h-48'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { themes } from "~/lib/forms/form-themes.js"
|
import { themes } from "~/lib/forms/themes/form-themes.js"
|
||||||
import { default as _has } from "lodash/has"
|
import { default as _has } from "lodash/has"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
"prismjs": "^1.24.1",
|
"prismjs": "^1.24.1",
|
||||||
"qrcode": "^1.5.1",
|
"qrcode": "^1.5.1",
|
||||||
"query-builder-vue-3": "^1.0.1",
|
"query-builder-vue-3": "^1.0.1",
|
||||||
|
"tailwind-merge": "^2.3.0",
|
||||||
"tinymotion": "^0.2.0",
|
"tinymotion": "^0.2.0",
|
||||||
"v-calendar": "^3.1.2",
|
"v-calendar": "^3.1.2",
|
||||||
"vue": "^3.2.13",
|
"vue": "^3.2.13",
|
||||||
|
|
@ -13772,11 +13773,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tailwind-merge": {
|
"node_modules/tailwind-merge": {
|
||||||
"version": "2.2.2",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.3.0.tgz",
|
||||||
"integrity": "sha512-tWANXsnmJzgw6mQ07nE3aCDkCK4QdT3ThPMCzawoYA2Pws7vSTCvz3Vrjg61jVUGfFZPJzxEP+NimbcW+EdaDw==",
|
"integrity": "sha512-vkYrLpIP+lgR0tQCG6AP7zZXCTLc1Lnv/CCRT3BqJ9CZ3ui2++GPaGb1x/ILsINIMSYqqvrpqjUFsMNLlW99EA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.24.0"
|
"@babel/runtime": "^7.24.1"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
"prismjs": "^1.24.1",
|
"prismjs": "^1.24.1",
|
||||||
"qrcode": "^1.5.1",
|
"qrcode": "^1.5.1",
|
||||||
"query-builder-vue-3": "^1.0.1",
|
"query-builder-vue-3": "^1.0.1",
|
||||||
|
"tailwind-merge": "^2.3.0",
|
||||||
"tinymotion": "^0.2.0",
|
"tinymotion": "^0.2.0",
|
||||||
"v-calendar": "^3.1.2",
|
"v-calendar": "^3.1.2",
|
||||||
"vue": "^3.2.13",
|
"vue": "^3.2.13",
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ module.exports = {
|
||||||
"./plugins/**/*.{js,ts}",
|
"./plugins/**/*.{js,ts}",
|
||||||
"./app.vue",
|
"./app.vue",
|
||||||
"./error.vue",
|
"./error.vue",
|
||||||
"./lib/forms/form-themes.js",
|
"./lib/forms/themes/form-themes.js",
|
||||||
|
"./lib/forms/themes/ThemeBuilder.js",
|
||||||
],
|
],
|
||||||
safelist: [
|
safelist: [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ class FormFactory extends Factory
|
||||||
'description' => $this->faker->randomHtml(1),
|
'description' => $this->faker->randomHtml(1),
|
||||||
'visibility' => 'public',
|
'visibility' => 'public',
|
||||||
'theme' => $this->faker->randomElement(Form::THEMES),
|
'theme' => $this->faker->randomElement(Form::THEMES),
|
||||||
|
'size' => $this->faker->randomElement(Form::SIZES),
|
||||||
|
'border_radius' => $this->faker->randomElement(Form::BORDER_RADIUS),
|
||||||
'width' => $this->faker->randomElement(Form::WIDTHS),
|
'width' => $this->faker->randomElement(Form::WIDTHS),
|
||||||
'dark_mode' => $this->faker->randomElement(Form::DARK_MODE_VALUES),
|
'dark_mode' => $this->faker->randomElement(Form::DARK_MODE_VALUES),
|
||||||
'color' => '#3B82F6',
|
'color' => '#3B82F6',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class () extends Migration {
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('forms', function (Blueprint $table) {
|
||||||
|
$table->string('size')->default('md');
|
||||||
|
$table->string('border_radius')->default('small');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then for each form with "Simple" theme on, disable border radius
|
||||||
|
\App\Models\Forms\Form::whereTheme('simple')->update(['border_radius' => 'none']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('forms', function (Blueprint $table) {
|
||||||
|
$table->dropColumn(['size', 'border_radius']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue