0351d front end linting (#377)

* feat: disable custom script for  trial users

* after lint fix

* frontend linting

---------

Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
Favour Olayinka
2024-04-15 18:39:03 +01:00
committed by GitHub
parent 8d35fc8b1a
commit bcd45ce8a6
228 changed files with 17036 additions and 8744 deletions

View File

@@ -1,143 +1,218 @@
<template>
<div class="relative border">
<video id="webcam" autoplay playsinline :class="[{ 'hidden': !isCapturing },theme.fileInput.cameraInput]" width="1280" height="720"></video>
<canvas id="canvas" :class="{ 'hidden': !capturedImage }"></canvas>
<div v-if="cameraPermissionStatus === 'allowed'" class="absolute inset-x-0 grid place-content-center bottom-2">
<div class=" p-2 px-4 flex items-center justify-center text-xs space-x-2" v-if="isCapturing">
<span class="cursor-pointer rounded-full w-14 h-14 border-2 grid place-content-center"
@click="processCapturedImage">
<span class="cursor-pointer bg-gray-100 rounded-full w-10 h-10 grid place-content-center">
</span>
</span>
<span class="text-white cursor-pointer" @click="cancelCamera">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-8 h-8">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
</svg>
</span>
</div>
</div>
<div v-else-if="cameraPermissionStatus === 'blocked'"
class="absolute p-5 top-0 inset-x-0 flex flex-col items-center justify-center space-y-4 text-center rounded border border-gray-400/30 h-full"
@click="openCameraUpload">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round"
d="M6.827 6.175A2.31 2.31 0 0 1 5.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 0 0-1.134-.175 2.31 2.31 0 0 1-1.64-1.055l-.822-1.316a2.192 2.192 0 0 0-1.736-1.039 48.774 48.774 0 0 0-5.232 0 2.192 2.192 0 0 0-1.736 1.039l-.821 1.316Z" />
<path stroke-linecap="round" stroke-linejoin="round"
d="M16.5 12.75a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0ZM18.75 10.5h.008v.008h-.008V10.5Z" />
</svg>
<p class="text-center font-bold">
Allow Camera Permission
</p>
<p class="text-xs">You need to allow camera permission before you can take pictures. Go to browser settings to enable camera permission on this page.</p>
<button class="text-xs p-1 px-2 bg-blue-600 rounded" type="button" @click.stop="cancelCamera">Got it!</button>
</div>
<div v-else-if="cameraPermissionStatus === 'loading'"
class="absolute p-5 top-0 inset-x-0 flex flex-col items-center justify-center space-y-4 text-center rounded border border-gray-400/30 h-full"
>
<div class="w-6 h-6">
<Loader />
</div>
</div>
<div v-else
class="absolute p-5 top-0 inset-x-0 flex flex-col items-center justify-center space-y-4 text-center rounded border border-gray-400/30 h-full"
@click="openCameraUpload">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="m15.75 10.5 4.72-4.72a.75.75 0 0 1 1.28.53v11.38a.75.75 0 0 1-1.28.53l-4.72-4.72M12 18.75H4.5a2.25 2.25 0 0 1-2.25-2.25V9m12.841 9.091L16.5 19.5m-1.409-1.409c.407-.407.659-.97.659-1.591v-9a2.25 2.25 0 0 0-2.25-2.25h-9c-.621 0-1.184.252-1.591.659m12.182 12.182L2.909 5.909M1.5 4.5l1.409 1.409" />
</svg>
<p class="text-center font-bold">
Camera Device Error
</p>
<p class="text-xs">An unknown error occurred when trying to start Webcam device.</p>
<button class="text-xs p-1 px-2 bg-blue-600 rounded" type="button" @click.stop="cancelCamera">Go back</button>
</div>
<div class="relative border">
<video
id="webcam"
autoplay
playsinline
:class="[{ hidden: !isCapturing }, theme.fileInput.cameraInput]"
width="1280"
height="720"
/>
<canvas
id="canvas"
:class="{ hidden: !capturedImage }"
/>
<div
v-if="cameraPermissionStatus === 'allowed'"
class="absolute inset-x-0 grid place-content-center bottom-2"
>
<div
v-if="isCapturing"
class="p-2 px-4 flex items-center justify-center text-xs space-x-2"
>
<span
class="cursor-pointer rounded-full w-14 h-14 border-2 grid place-content-center"
@click="processCapturedImage"
>
<span
class="cursor-pointer bg-gray-100 rounded-full w-10 h-10 grid place-content-center"
/>
</span>
<span
class="text-white cursor-pointer"
@click="cancelCamera"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-8 h-8"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M6 18 18 6M6 6l12 12"
/>
</svg>
</span>
</div>
</div>
<div
v-else-if="cameraPermissionStatus === 'blocked'"
class="absolute p-5 top-0 inset-x-0 flex flex-col items-center justify-center space-y-4 text-center rounded border border-gray-400/30 h-full"
@click="openCameraUpload"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-6 h-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M6.827 6.175A2.31 2.31 0 0 1 5.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 0 0-1.134-.175 2.31 2.31 0 0 1-1.64-1.055l-.822-1.316a2.192 2.192 0 0 0-1.736-1.039 48.774 48.774 0 0 0-5.232 0 2.192 2.192 0 0 0-1.736 1.039l-.821 1.316Z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.5 12.75a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0ZM18.75 10.5h.008v.008h-.008V10.5Z"
/>
</svg>
<p class="text-center font-bold">
Allow Camera Permission
</p>
<p class="text-xs">
You need to allow camera permission before you can take pictures. Go to
browser settings to enable camera permission on this page.
</p>
<button
class="text-xs p-1 px-2 bg-blue-600 rounded"
type="button"
@click.stop="cancelCamera"
>
Got it!
</button>
</div>
<div
v-else-if="cameraPermissionStatus === 'loading'"
class="absolute p-5 top-0 inset-x-0 flex flex-col items-center justify-center space-y-4 text-center rounded border border-gray-400/30 h-full"
>
<div class="w-6 h-6">
<Loader />
</div>
</div>
<div
v-else
class="absolute p-5 top-0 inset-x-0 flex flex-col items-center justify-center space-y-4 text-center rounded border border-gray-400/30 h-full"
@click="openCameraUpload"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-6 h-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m15.75 10.5 4.72-4.72a.75.75 0 0 1 1.28.53v11.38a.75.75 0 0 1-1.28.53l-4.72-4.72M12 18.75H4.5a2.25 2.25 0 0 1-2.25-2.25V9m12.841 9.091L16.5 19.5m-1.409-1.409c.407-.407.659-.97.659-1.591v-9a2.25 2.25 0 0 0-2.25-2.25h-9c-.621 0-1.184.252-1.591.659m12.182 12.182L2.909 5.909M1.5 4.5l1.409 1.409"
/>
</svg>
<p class="text-center font-bold">
Camera Device Error
</p>
<p class="text-xs">
An unknown error occurred when trying to start Webcam device.
</p>
<button
class="text-xs p-1 px-2 bg-blue-600 rounded"
type="button"
@click.stop="cancelCamera"
>
Go back
</button>
</div>
</div>
</template>
<script>
import Webcam from 'webcam-easy';
import { themes } from '~/lib/forms/form-themes.js'
import Webcam from "webcam-easy"
import { themes } from "~/lib/forms/form-themes.js"
export default {
name: 'FileInput',
props:{
theme: { type: Object, default: () => themes.default }
name: "FileInput",
props: {
theme: { type: Object, default: () => themes.default },
},
emits: ['stopWebcam', 'uploadImage'],
data: () => ({
webcam: null,
isCapturing: false,
capturedImage: null,
cameraPermissionStatus: "loading",
}),
computed: {
videoDisplay() {
return this.isCapturing ? "" : "hidden"
},
data: () => ({
webcam: null,
isCapturing: false,
capturedImage: null,
cameraPermissionStatus: 'loading',
}),
computed: {
videoDisplay() {
return this.isCapturing ? '' : 'hidden';
},
canvasDisplay() {
return (!this.isCapturing && this.capturedImage) ? '' : 'hidden'
}
canvasDisplay() {
return !this.isCapturing && this.capturedImage ? "" : "hidden"
},
mounted() {
const webcamElement = document.getElementById('webcam');
const canvasElement = document.getElementById('canvas');
this.webcam = new Webcam(webcamElement, 'user', canvasElement);
this.openCameraUpload()
},
mounted() {
const webcamElement = document.getElementById("webcam")
const canvasElement = document.getElementById("canvas")
this.webcam = new Webcam(webcamElement, "user", canvasElement)
this.openCameraUpload()
},
methods: {
openCameraUpload() {
this.isCapturing = true
this.capturedImage = null
this.webcam
.start()
.then(() => {
this.cameraPermissionStatus = "allowed"
})
.catch((err) => {
console.error(err)
if (err.toString() === "NotAllowedError: Permission denied") {
this.cameraPermissionStatus = "blocked"
return
}
this.cameraPermissionStatus = "unknown"
})
},
cancelCamera() {
this.isCapturing = false
this.capturedImage = null
this.webcam.stop()
this.$emit("stopWebcam")
},
processCapturedImage() {
this.capturedImage = this.webcam.snap()
this.isCapturing = false
this.webcam.stop()
const byteCharacters = atob(this.capturedImage.split(",")[1])
const byteArrays = []
for (let offset = 0; offset < byteCharacters.length; offset += 512) {
const slice = byteCharacters.slice(offset, offset + 512)
methods: {
openCameraUpload() {
this.isCapturing = true;
this.capturedImage = null;
this.webcam.start()
.then(result => {
this.cameraPermissionStatus = 'allowed';
})
.catch(err => {
console.error(err)
if(err.toString() === 'NotAllowedError: Permission denied'){
this.cameraPermissionStatus = 'blocked';
return;
}
this.cameraPermissionStatus = 'unknown';
});
},
cancelCamera() {
this.isCapturing = false;
this.capturedImage = null;
this.webcam.stop()
this.$emit('stopWebcam')
},
processCapturedImage() {
this.capturedImage = this.webcam.snap();
this.isCapturing = false;
this.webcam.stop()
const byteCharacters = atob(this.capturedImage.split(',')[1]);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += 512) {
const slice = byteCharacters.slice(offset, offset + 512);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
// Create Blob from binary data
const blob = new Blob(byteArrays, { type: 'image/png' });
const filename = Date.now()
// Create a File object from the Blob
const file = new File([blob], `${filename}.png`, { type: 'image/png' });
this.$emit('uploadImage', file)
const byteNumbers = new Array(slice.length)
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i)
}
const byteArray = new Uint8Array(byteNumbers)
byteArrays.push(byteArray)
}
}
// Create Blob from binary data
const blob = new Blob(byteArrays, { type: "image/png" })
const filename = Date.now()
// Create a File object from the Blob
const file = new File([blob], `${filename}.png`, { type: "image/png" })
this.$emit("uploadImage", file)
},
},
}
</script>

View File

@@ -8,19 +8,19 @@
<span
v-if="help"
class="field-help"
v-html="help"/>
v-html="help"
/>
</slot>
</small>
<slot name="after-help">
<small class="flex-grow"/>
<small class="flex-grow" />
</slot>
</div>
</template>
<script setup>
defineProps({
helpClasses: {type: String, default: 'text-gray-400 dark:text-gray-500'},
help: {type: String, required: false}
helpClasses: { type: String, default: "text-gray-400 dark:text-gray-500" },
help: { type: String, required: false },
})
</script>

View File

@@ -19,7 +19,7 @@
<script>
export default {
name: 'InputLabel',
name: "InputLabel",
props: {
nativeFor: { type: String, default: null },

View File

@@ -50,8 +50,8 @@
</template>
<script setup>
import InputLabel from './InputLabel.vue'
import InputHelp from './InputHelp.vue'
import InputLabel from "./InputLabel.vue"
import InputHelp from "./InputHelp.vue"
defineProps({
id: { type: String, required: false },
@@ -62,7 +62,7 @@ defineProps({
wrapperClass: { type: String, required: false },
inputStyle: { type: Object, required: false },
help: { type: String, required: false },
helpPosition: { type: String, default: 'below_input' },
helpPosition: { type: String, default: "below_input" },
uppercaseLabels: { type: Boolean, default: true },
hideFieldName: { type: Boolean, default: true },
required: { type: Boolean, default: false },

View File

@@ -3,15 +3,32 @@
:class="[theme.fileInput.uploadedFile, 'overflow-hidden']"
:title="file.file.name"
>
<div v-if="file.src && !isImageHide" class="h-20 overflow-hidden flex">
<img class="block object-cover object-center w-full" :src="file.src" @error="isImageHide=true"/>
</div>
<div v-else class="h-20 flex items-center justify-center">
<svg class="w-10 h-10 text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="0.8" stroke="currentColor"
<div
v-if="file.src && !isImageHide"
class="h-20 overflow-hidden flex"
>
<img
class="block object-cover object-center w-full"
:src="file.src"
@error="isImageHide = true"
>
<path stroke-linecap="round" stroke-linejoin="round"
d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"
</div>
<div
v-else
class="h-20 flex items-center justify-center"
>
<svg
class="w-10 h-10 text-gray-500"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="0.8"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"
/>
</svg>
</div>
@@ -47,17 +64,17 @@
<script>
export default {
name: 'UploadedFile',
name: "UploadedFile",
props: {
file: { default: null },
theme: { type: Object }
file: { type:Object, default: null },
theme: { type: Object },
},
emits: ['remove'],
data: () => ({
isImageHide: false
isImageHide: false,
}),
computed: {}
computed: {},
}
</script>

View File

@@ -27,21 +27,21 @@ import {
onMounted,
ref,
watch,
} from 'vue'
} from "vue"
defineOptions({
name: 'VCheckbox',
name: "VCheckbox",
})
const props = defineProps({
id: { type: String, default: null },
name: { type: String, default: 'checkbox' },
name: { type: String, default: "checkbox" },
modelValue: { type: [Boolean, String], default: false },
disabled: { type: Boolean, default: false },
sizeClasses: { type: String, default: 'w-4 h-4' },
sizeClasses: { type: String, default: "w-4 h-4" },
})
const emit = defineEmits(['update:modelValue', 'click'])
const emit = defineEmits(["update:modelValue", "click"])
const internalValue = ref(props.modelValue)
@@ -62,18 +62,14 @@ watch(
watch(
() => internalValue.value,
(val, oldVal) => {
if (val === 0 || val === '0')
val = false
if (val === 1 || val === '1')
val = true
if (val === 0 || val === "0") val = false
if (val === 1 || val === "1") val = true
if (val !== oldVal)
emit('update:modelValue', val)
if (val !== oldVal) emit("update:modelValue", val)
},
)
onMounted(() => {
if (internalValue.value === null)
internalValue.value = false
if (internalValue.value === null) internalValue.value = false
})
</script>

View File

@@ -12,7 +12,16 @@
aria-labelledby="listbox-label"
class="cursor-pointer"
:style="inputStyle"
:class="[theme.SelectInput.input, { 'py-2': !multiple || loading, 'py-1': multiple, '!ring-red-500 !ring-2 !border-transparent': hasError, '!cursor-not-allowed !bg-gray-200': disabled }, inputClass]"
:class="[
theme.SelectInput.input,
{
'py-2': !multiple || loading,
'py-1': multiple,
'!ring-red-500 !ring-2 !border-transparent': hasError,
'!cursor-not-allowed !bg-gray-200': disabled,
},
inputClass,
]"
@click="toggleDropdown"
>
<div :class="{ 'h-6': !multiple, 'min-h-8': multiple && !loading }">
@@ -43,14 +52,17 @@
<slot name="placeholder">
<div
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 }"
>
{{ placeholder }}
</div>
</slot>
</div>
</transition>
</div>
<span class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<span
class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none"
>
<svg
class="h-5 w-5 text-gray-400"
viewBox="0 0 20 20"
@@ -67,13 +79,32 @@
</span>
</button>
</span>
<collapsible v-model="isOpen" @click-away="onClickAway"
class="absolute mt-1 rounded-md bg-white dark:bg-notion-dark-light shadow-xl z-10" :class="dropdownClass">
<ul tabindex="-1" role="listbox"
<collapsible
v-model="isOpen"
class="absolute mt-1 rounded-md bg-white dark:bg-notion-dark-light shadow-xl z-10"
:class="dropdownClass"
@click-away="onClickAway"
>
<ul
tabindex="-1"
role="listbox"
class="rounded-md text-base leading-6 shadow-xs overflow-auto focus:outline-none sm:text-sm sm:leading-5 relative"
:class="{ 'max-h-42 py-1': !isSearchable, 'max-h-48 pb-1': isSearchable }">
<div v-if="isSearchable" class="px-2 pt-2 sticky top-0 bg-white dark-bg-notion-dark-light z-10">
<text-input v-model="searchTerm" name="search" :color="color" :theme="theme" placeholder="Search..." />
:class="{
'max-h-42 py-1': !isSearchable,
'max-h-48 pb-1': isSearchable,
}"
>
<div
v-if="isSearchable"
class="px-2 pt-2 sticky top-0 bg-white dark-bg-notion-dark-light z-10"
>
<text-input
v-model="searchTerm"
name="search"
:color="color"
:theme="theme"
placeholder="Search..."
/>
</div>
<div
v-if="loading"
@@ -89,7 +120,8 @@
:style="optionStyle"
:class="{ 'px-3 pr-9': multiple, 'px-3': !multiple }"
class="text-gray-900 cursor-default select-none relative py-2 cursor-pointer group hover:text-white hover:bg-form-color focus:outline-none focus-text-white focus-nt-blue"
@click="select(item)">
@click="select(item)"
>
<slot
name="option"
:option="item"
@@ -101,7 +133,11 @@
v-else-if="!loading && !(allowCreation && searchTerm)"
class="w-full text-gray-500 text-center py-2"
>
{{ (allowCreation ? 'Type something to add an option' : 'No option available') }}.
{{
allowCreation
? "Type something to add an option"
: "No option available"
}}.
</p>
<li
v-if="allowCreation && searchTerm"
@@ -109,8 +145,12 @@
:style="optionStyle"
:class="{ 'px-3 pr-9': multiple, 'px-3': !multiple }"
class="text-gray-900 cursor-default select-none relative py-2 cursor-pointer group hover:text-white dark:text-white hover:bg-form-color focus:outline-none focus-text-white focus-nt-blue"
@click="createOption(searchTerm)">
Create <b class="px-1 bg-gray-300 rounded group-hover-text-black">{{ searchTerm }}</b>
@click="createOption(searchTerm)"
>
Create
<b class="px-1 bg-gray-300 rounded group-hover-text-black">{{
searchTerm
}}</b>
</li>
</ul>
</collapsible>
@@ -118,54 +158,54 @@
</template>
<script>
import Collapsible from '~/components/global/transitions/Collapsible.vue'
import { themes} from "~/lib/forms/form-themes.js"
import TextInput from '../TextInput.vue'
import debounce from 'lodash/debounce'
import Fuse from 'fuse.js'
import Collapsible from "~/components/global/transitions/Collapsible.vue"
import { themes } from "~/lib/forms/form-themes.js"
import TextInput from "../TextInput.vue"
import debounce from "lodash/debounce"
import Fuse from "fuse.js"
export default {
name: 'VSelect',
name: "VSelect",
components: { Collapsible, TextInput },
directives: {},
props: {
data: Array,
modelValue: { default: null, type: [String, Number, Array, Object] },
inputClass: { type: String, default: null },
dropdownClass: { type: String, default: 'w-full' },
dropdownClass: { type: String, default: "w-full" },
loading: { type: Boolean, default: false },
required: { type: Boolean, default: false },
multiple: { type: Boolean, default: false },
searchable: { type: Boolean, default: false },
hasError: { type: Boolean, default: false },
remote: { type: Function, default: null },
searchKeys: { type: Array, default: () => ['name'] },
optionKey: { type: String, default: 'id' },
searchKeys: { type: Array, default: () => ["name"] },
optionKey: { type: String, default: "id" },
emitKey: { type: String, default: null },
color: { type: String, default: '#3B82F6' },
color: { type: String, default: "#3B82F6" },
placeholder: { type: String, default: null },
uppercaseLabels: { type: Boolean, default: true },
theme: { type: Object, default: () => themes.default },
allowCreation: { type: Boolean, default: false },
disabled: { type: Boolean, default: false }
disabled: { type: Boolean, default: false },
},
emits: ['update:modelValue', 'update-options'],
emits: ["update:modelValue", "update-options"],
data() {
return {
isOpen: false,
searchTerm: '',
defaultValue: this.modelValue ?? null
searchTerm: "",
defaultValue: this.modelValue ?? null,
}
},
computed: {
optionStyle() {
return {
'--bg-form-color': this.color
"--bg-form-color": this.color,
}
},
inputStyle() {
return {
'--tw-ring-color': this.color
"--tw-ring-color": this.color,
}
},
debouncedRemote() {
@@ -176,13 +216,13 @@ export default {
},
filteredOptions() {
if (!this.data) return []
if (!this.searchable || this.remote || this.searchTerm === '') {
if (!this.searchable || this.remote || this.searchTerm === "") {
return this.data
}
// Fuse search
const fuzeOptions = {
keys: this.searchKeys
keys: this.searchKeys,
}
const fuse = new Fuse(this.data, fuzeOptions)
return fuse.search(this.searchTerm).map((res) => {
@@ -191,15 +231,19 @@ export default {
},
isSearchable() {
return this.searchable || this.remote !== null || this.allowCreation
}
},
},
watch: {
searchTerm(val) {
if (!this.debouncedRemote) return
if ((this.remote && val) || (val === '' && !this.modelValue) || (val === '' && this.isOpen)) {
if (
(this.remote && val) ||
(val === "" && !this.modelValue) ||
(val === "" && this.isOpen)
) {
return this.debouncedRemote(val)
}
}
},
},
methods: {
onClickAway(event) {
@@ -227,7 +271,7 @@ export default {
this.isOpen = !this.isOpen
}
if (!this.isOpen) {
this.searchTerm = ''
this.searchTerm = ""
}
},
select(value) {
@@ -241,25 +285,33 @@ export default {
}
if (this.multiple) {
const emitValue = Array.isArray(this.modelValue) ? [...this.modelValue] : []
const emitValue = Array.isArray(this.modelValue)
? [...this.modelValue]
: []
if (this.isSelected(value)) {
this.$emit('update:modelValue', emitValue.filter((item) => {
if (this.emitKey) {
return item !== value
}
return item[this.optionKey] !== value && item[this.optionKey] !== value[this.optionKey]
}))
this.$emit(
"update:modelValue",
emitValue.filter((item) => {
if (this.emitKey) {
return item !== value
}
return (
item[this.optionKey] !== value &&
item[this.optionKey] !== value[this.optionKey]
)
}),
)
return
}
emitValue.push(value)
this.$emit('update:modelValue', emitValue)
this.$emit("update:modelValue", emitValue)
} else {
if (this.modelValue === value) {
this.$emit('update:modelValue', this.defaultValue ?? null)
this.$emit("update:modelValue", this.defaultValue ?? null)
} else {
this.$emit('update:modelValue', value)
this.$emit("update:modelValue", value)
}
}
},
@@ -268,13 +320,13 @@ export default {
const newItem = {
name: newOption,
value: newOption,
id: newOption
id: newOption,
}
this.$emit('update-options', newItem)
this.$emit("update-options", newItem)
this.select(newItem)
this.searchTerm = ''
this.searchTerm = ""
}
}
}
},
},
}
</script>

View File

@@ -16,17 +16,16 @@
</template>
<script setup>
import { defineEmits, defineProps } from 'vue'
import { defineEmits, defineProps } from "vue"
const props = defineProps({
modelValue: { type: Boolean, default: false },
disabled: { type: Boolean, default: false },
})
const emit = defineEmits(['update:modelValue'])
const emit = defineEmits(["update:modelValue"])
function onClick() {
if (props.disabled)
return
emit('update:modelValue', !props.modelValue)
if (props.disabled) return
emit("update:modelValue", !props.modelValue)
}
</script>