chore(module): lint

This commit is contained in:
Benjamin Canac
2024-04-12 14:02:23 +02:00
parent 74a640ceca
commit abb7580f71
22 changed files with 112 additions and 116 deletions

View File

@@ -21,7 +21,7 @@ export default defineNuxtModule<ModuleOptions>({
prefix: 'U',
colors: undefined
},
async setup (options, nuxt) {
async setup(options, nuxt) {
const { resolve } = createResolver(import.meta.url)
options.colors = options.colors || ['primary', 'red', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'violet', 'purple', 'fuchia', 'pink', 'rose']

View File

@@ -52,7 +52,7 @@ const inputId = _inputId.value ?? useId()
const modelValue = defineModel<boolean | undefined>({
default: undefined,
set (value) {
set(value) {
return value
}
})
@@ -60,10 +60,10 @@ const modelValue = defineModel<boolean | undefined>({
const indeterminate = computed(() => (modelValue.value === undefined && props.indeterminate))
const checked = computed({
get () {
get() {
return indeterminate.value ? 'indeterminate' : modelValue.value
},
set (value) {
set(value) {
modelValue.value = value === 'indeterminate' ? undefined : value
}
})
@@ -71,7 +71,7 @@ const checked = computed({
// FIXME: I think there's a race condition between this and the v-model event.
// This must be triggered after the value updates, otherwise the form validates
// the previous value.
function onChecked () {
function onChecked() {
emitFormChange()
}

View File

@@ -14,7 +14,7 @@ export interface FormProps<T extends object> {
id?: string | number
schema?: FormSchema<T>
state: Partial<T>
validate?: (state: Partial<T>) => Promise<FormError[] | void>
validate?: (state: Partial<T>) => Promise<FormError[]>
validateOn?: FormInputEvents[]
disabled?: boolean
validateOnInputDelay?: number
@@ -38,7 +38,7 @@ import { useId } from '#imports'
import { getYupErrors, isYupSchema, getValibotError, isValibotSchema, getZodErrors, isZodSchema, getJoiErrors, isJoiSchema } from '#ui/utils/form'
const props = withDefaults(defineProps<FormProps<T>>(), {
validateOn () {
validateOn() {
return ['input', 'blur', 'change'] as FormInputEvents[]
},
validateOnInputDelay: 300
@@ -55,7 +55,6 @@ const parentBus = inject<UseEventBusReturn<FormEvent, string> | undefined>(
)
provide('form-events', bus)
const nestedForms = ref<Map<string | number, { validate: () => any }>>(new Map())
onMounted(async () => {
@@ -98,14 +97,14 @@ provide('form-errors', errors)
const inputs = ref<Record<string, string>>({})
provide('form-inputs', inputs)
function resolveErrorIds (errs: FormError[]): FormErrorWithId[] {
return errs.map((err) => ({
function resolveErrorIds(errs: FormError[]): FormErrorWithId[] {
return errs.map(err => ({
...err,
id: inputs.value[err.name]
}))
}
async function getErrors (): Promise<FormErrorWithId[]> {
async function getErrors(): Promise<FormErrorWithId[]> {
let errs = props.validate ? (await props.validate(props.state)) ?? [] : []
if (props.schema) {
@@ -125,26 +124,23 @@ async function getErrors (): Promise<FormErrorWithId[]> {
return resolveErrorIds(errs)
}
async function _validate (
opts: { name?: string | string[], silent?: boolean, nested?: boolean } = { silent: false, nested: true }
): Promise<T | false> {
async function _validate(opts: { name?: string | string[], silent?: boolean, nested?: boolean } = { silent: false, nested: true }): Promise<T | false> {
const names = opts.name && !Array.isArray(opts.name) ? [opts.name] : opts.name
const nestedValidatePromises = !names && opts.nested ? Array.from(nestedForms.value.values()).map(
({ validate }) => validate().then(() => undefined).catch((error: Error) => {
if (!(error instanceof FormValidationException)) {
throw error
}
return error
})
) : []
const nestedValidatePromises = !names && opts.nested
? Array.from(nestedForms.value.values()).map(
({ validate }) => validate().then(() => undefined).catch((error: Error) => {
if (!(error instanceof FormValidationException)) {
throw error
}
return error
})
)
: []
if (names) {
const otherErrors = errors.value.filter(
(error) => !names!.includes(error.name)
)
const pathErrors = (await getErrors()).filter((error) =>
names!.includes(error.name)
const otherErrors = errors.value.filter(error => !names!.includes(error.name))
const pathErrors = (await getErrors()).filter(error => names!.includes(error.name)
)
errors.value = otherErrors.concat(pathErrors)
} else {
@@ -160,7 +156,7 @@ async function _validate (
return props.state as T
}
async function onSubmit (payload: Event) {
async function onSubmit(payload: Event) {
const event = payload as SubmitEvent
try {
@@ -170,7 +166,6 @@ async function onSubmit (payload: Event) {
data: props.state
}
emit('submit', submitEvent)
} catch (error) {
if (!(error instanceof FormValidationException)) {
throw error
@@ -190,30 +185,30 @@ defineExpose<Form<T>>({
validate: _validate,
errors,
setErrors (errs: FormError[], name?: string) {
setErrors(errs: FormError[], name?: string) {
if (name) {
errors.value = errors.value
.filter((error) => error.name !== name)
.filter(error => error.name !== name)
.concat(resolveErrorIds(errs))
} else {
errors.value = resolveErrorIds(errs)
}
},
async submit () {
async submit() {
await onSubmit(new Event('submit'))
},
getErrors (name?: string) {
getErrors(name?: string) {
if (name) {
return errors.value.filter((err) => err.name === name)
return errors.value.filter(err => err.name === name)
}
return errors.value
},
clear (name?: string) {
clear(name?: string) {
if (name) {
errors.value = errors.value.filter((err) => err.name !== name)
errors.value = errors.value.filter(err => err.name !== name)
} else {
errors.value = []
}

View File

@@ -52,10 +52,10 @@ const ui = computed(() => tv({ extend: formField, slots: props.ui })({
const formErrors = inject<Ref<FormError[]> | null>('form-errors', null)
const error = computed(() => {
return (props.error && typeof props.error === 'string') ||
typeof props.error === 'boolean'
return (props.error && typeof props.error === 'string')
|| typeof props.error === 'boolean'
? props.error
: formErrors?.value?.find((error) => error.name === props.name)?.message
: formErrors?.value?.find(error => error.name === props.name)?.message
})
const inputId = ref(useId())

View File

@@ -73,14 +73,14 @@ const ui = computed(() => tv({ extend: input, slots: props.ui })({
const inputRef = ref<HTMLInputElement | null>(null)
function autoFocus () {
function autoFocus() {
if (props.autofocus) {
inputRef.value?.focus()
}
}
// Custom function to handle the v-model properties
function updateInput (value: string) {
function updateInput(value: string) {
if (modelModifiers.trim) {
value = value.trim()
}
@@ -93,13 +93,13 @@ function updateInput (value: string) {
emitFormInput()
}
function onInput (event: Event) {
function onInput(event: Event) {
if (!modelModifiers.lazy) {
updateInput((event.target as HTMLInputElement).value)
}
}
function onChange (event: Event) {
function onChange(event: Event) {
const value = (event.target as HTMLInputElement).value
if (modelModifiers.lazy) {
@@ -112,7 +112,7 @@ function onChange (event: Event) {
}
}
function onBlur (event: FocusEvent) {
function onBlur(event: FocusEvent) {
emitFormBlur()
emit('blur', event)
}

View File

@@ -55,7 +55,7 @@ const ui = computed(() => tv({
}
}))
function isLinkActive (slotProps: any) {
function isLinkActive(slotProps: any) {
if (props.active !== undefined) {
return props.active
}
@@ -78,7 +78,7 @@ function isLinkActive (slotProps: any) {
return false
}
function resolveLinkClass (slotProps: any) {
function resolveLinkClass(slotProps: any) {
const active = isLinkActive(slotProps)
if (props.raw) {

View File

@@ -23,7 +23,7 @@ const props = withDefaults(defineProps<LinkBaseProps>(), {
type: 'button'
})
function onClick (e: MouseEvent) {
function onClick(e: MouseEvent) {
if (props.disabled) {
e.stopPropagation()
e.preventDefault()
@@ -43,10 +43,10 @@ function onClick (e: MouseEvent) {
<template>
<Primitive
v-bind="href ? {
as: 'a',
href: disabled ? undefined : href,
'as': 'a',
'href': disabled ? undefined : href,
'aria-disabled': disabled ? 'true' : undefined,
role: disabled ? 'link' : undefined
'role': disabled ? 'link' : undefined
} : as === 'button' ? {
as,
type,

View File

@@ -58,8 +58,8 @@ const contentProps = toRef(() => props.content)
const contentEvents = computed(() => {
if (props.preventClose) {
return {
'pointerDownOutside': (e: Event) => e.preventDefault(),
'interactOutside': (e: Event) => e.preventDefault()
pointerDownOutside: (e: Event) => e.preventDefault(),
interactOutside: (e: Event) => e.preventDefault()
}
}

View File

@@ -9,7 +9,7 @@ const appConfig = _appConfig as AppConfig & { ui: { popover: Partial<typeof them
const popover = tv({ extend: tv(theme), ...(appConfig.ui?.popover || {}) })
export interface PopoverProps extends PopoverRootProps, Pick<HoverCardRootProps, 'openDelay' | 'closeDelay'>{
export interface PopoverProps extends PopoverRootProps, Pick<HoverCardRootProps, 'openDelay' | 'closeDelay'> {
/**
* The mode of the popover.
* @defaultValue "click"

View File

@@ -35,7 +35,7 @@ export type RadioGroupEmits = {
export interface RadioGroupSlots<T> {
legend(): any
label(props: { option: RadioGroupOption<T> }): any
description(props: { option: RadioGroupOption<T>}): any
description(props: { option: RadioGroupOption<T> }): any
}
</script>
@@ -61,7 +61,7 @@ const ui = computed(() => tv({ extend: radioGroup, slots: props.ui })({
required: props.required
}))
function normalizeOption (option: any) {
function normalizeOption(option: any) {
if (['string', 'number', 'boolean'].includes(typeof option)) {
return {
id: `${inputId}:${option}`,
@@ -82,7 +82,7 @@ const normalizedOptions = computed(() => {
})
const modelValue = defineModel<T>({
set (value) {
set(value) {
emits('change', value)
return value
}
@@ -91,7 +91,7 @@ const modelValue = defineModel<T>({
// FIXME: I think there's a race condition between this and the v-model event.
// This must be triggered after the value updates, otherwise the form validates
// the previous value.
function onUpdate () {
function onUpdate() {
emitFormChange()
}
</script>

View File

@@ -61,8 +61,8 @@ const contentProps = toRef(() => props.content)
const contentEvents = computed(() => {
if (props.preventClose) {
return {
'pointerDownOutside': (e: Event) => e.preventDefault(),
'interactOutside': (e: Event) => e.preventDefault()
pointerDownOutside: (e: Event) => e.preventDefault(),
interactOutside: (e: Event) => e.preventDefault()
}
}

View File

@@ -51,7 +51,7 @@ const ui = computed(() => tv({ extend: switchTv, slots: props.ui })({
// FIXME: I think there's a race condition between this and the v-model event.
// This must be triggered after the value updates, otherwise the form validates
// the previous value.
async function onChecked () {
async function onChecked() {
emitFormChange()
}
</script>

View File

@@ -65,14 +65,14 @@ const ui = computed(() => tv({ extend: textarea, slots: props.ui })({
const textareaRef = ref<HTMLTextAreaElement | null>(null)
function autoFocus () {
function autoFocus() {
if (props.autofocus) {
textareaRef.value?.focus()
}
}
// Custom function to handle the v-model properties
function updateInput (value: string) {
function updateInput(value: string) {
if (modelModifiers.trim) {
value = value.trim()
}
@@ -85,7 +85,7 @@ function updateInput (value: string) {
emitFormInput()
}
function onInput (event: Event) {
function onInput(event: Event) {
autoResize()
if (!modelModifiers.lazy) {
@@ -93,7 +93,7 @@ function onInput (event: Event) {
}
}
function onChange (event: Event) {
function onChange(event: Event) {
const value = (event.target as HTMLInputElement).value
if (modelModifiers.lazy) {
@@ -106,7 +106,7 @@ function onChange (event: Event) {
}
}
function onBlur (event: FocusEvent) {
function onBlur(event: FocusEvent) {
emitFormBlur()
emit('blur', event)
}
@@ -117,9 +117,8 @@ onMounted(() => {
}, props.autofocusDelay)
})
function autoResize () {
function autoResize() {
if (props.autoresize) {
if (!textareaRef.value) {
return
}
@@ -127,10 +126,10 @@ function autoResize () {
textareaRef.value.rows = props.rows
const styles = window.getComputedStyle(textareaRef.value)
const paddingTop = parseInt(styles.paddingTop)
const paddingBottom = parseInt(styles.paddingBottom)
const paddingTop = Number.parseInt(styles.paddingTop)
const paddingBottom = Number.parseInt(styles.paddingBottom)
const padding = paddingTop + paddingBottom
const lineHeight = parseInt(styles.lineHeight)
const lineHeight = Number.parseInt(styles.lineHeight)
const { scrollHeight } = textareaRef.value
const newRows = (scrollHeight - padding) / lineHeight

View File

@@ -40,16 +40,16 @@ const { toasts, remove } = useToast()
const swipeDirection = computed(() => {
switch (props.position) {
case 'top-center':
return 'up'
case 'top-right':
case 'bottom-right':
return 'right'
case 'bottom-center':
return 'down'
case 'top-left':
case 'bottom-left':
return 'left'
case 'top-center':
return 'up'
case 'top-right':
case 'bottom-right':
return 'right'
case 'bottom-center':
return 'down'
case 'top-left':
case 'bottom-left':
return 'left'
}
return 'right'
})
@@ -59,7 +59,7 @@ const ui = computed(() => tv({ extend: toaster, slots: props.ui })({
swipeDirection: swipeDirection.value
}))
function onUpdateOpen (value: boolean, id: string | number) {
function onUpdateOpen(value: boolean, id: string | number) {
if (value) {
return
}
@@ -75,7 +75,7 @@ const refs = ref<{ height: number }[]>([])
const height = computed(() => refs.value.reduce((acc, { height }) => acc + height + 16, 0))
const frontHeight = computed(() => refs.value[refs.value.length - 1]?.height || 0)
function getOffset (index: number) {
function getOffset(index: number) {
return refs.value.slice(index + 1).reduce((acc, { height }) => acc + height + 16, 0)
}

View File

@@ -12,7 +12,7 @@ export interface UseComponentIconsProps {
loadingIcon?: IconProps['name']
}
export function useComponentIcons (props: UseComponentIconsProps) {
export function useComponentIcons(props: UseComponentIconsProps) {
const appConfig = useAppConfig()
const isLeading = computed(() => (props.icon && props.leading) || (props.icon && !props.trailing) || (props.loading && !props.trailing && !props.trailingIcon) || !!props.leadingIcon)

View File

@@ -5,16 +5,16 @@ import type { FormEvent, FormInputEvents, FormFieldInjectedOptions, FormInjected
type Props<T> = {
id?: string
name?: string
// @ts-ignore FIXME: TS doesn't like this
// @ts-expect-error FIXME: TS doesn't like this
size?: T['size']
// @ts-ignore FIXME: TS doesn't like this
// @ts-expect-error FIXME: TS doesn't like this
color?: T['color']
eagerValidation?: boolean
legend?: string
disabled?: boolean
}
export function useFormField <T> (inputProps?: Props<T>) {
export function useFormField<T>(inputProps?: Props<T>) {
const formOptions = inject<FormInjectedOptions | undefined>('form-options', undefined)
const formBus = inject<UseEventBusReturn<FormEvent, string> | undefined>('form-events', undefined)
const formField = inject<FormFieldInjectedOptions<T> | undefined>('form-field', undefined)
@@ -33,18 +33,18 @@ export function useFormField <T> (inputProps?: Props<T>) {
const blurred = ref(false)
function emitFormEvent (type: FormInputEvents, name: string) {
function emitFormEvent(type: FormInputEvents, name: string) {
if (formBus && formField) {
formBus.emit({ type, name })
}
}
function emitFormBlur () {
function emitFormBlur() {
emitFormEvent('blur', formField?.name.value as string)
blurred.value = true
}
function emitFormChange () {
function emitFormChange() {
emitFormEvent('change', formField?.name.value as string)
}

View File

@@ -6,10 +6,10 @@ export interface Toast extends Omit<ToastProps, 'defaultOpen'> {
click?: (toast: Toast) => void
}
export function useToast () {
export function useToast() {
const toasts = useState<Toast[]>('toasts', () => [])
function add (toast: Partial<Toast>): Toast {
function add(toast: Partial<Toast>): Toast {
const body = {
id: new Date().getTime().toString(),
open: true,
@@ -26,7 +26,7 @@ export function useToast () {
return body
}
function update (id: string | number, toast: Partial<Toast>) {
function update(id: string | number, toast: Partial<Toast>) {
const index = toasts.value.findIndex((t: Toast) => t.id === id)
if (index !== -1) {
toasts.value[index] = {
@@ -36,7 +36,7 @@ export function useToast () {
}
}
function remove (id: string | number) {
function remove(id: string | number) {
const index = toasts.value.findIndex((t: Toast) => t.id === id)
if (index !== -1) {
toasts.value[index] = {

View File

@@ -1,5 +1,6 @@
declare module '#build/app.config' {
import type { AppConfig } from '@nuxt/schema'
const _default: AppConfig
export default _default
}

View File

@@ -71,7 +71,7 @@ export interface FormInjectedOptions {
export interface FormFieldInjectedOptions<T> {
inputId: Ref<string | undefined>
name: ComputedRef<string | undefined>
// @ts-ignore FIXME: TS doesn't like this
// @ts-expect-error FIXME: TS doesn't like this
size: ComputedRef<T['size']>
error: ComputedRef<string | boolean | undefined>
eagerValidation: ComputedRef<boolean | undefined>
@@ -83,7 +83,7 @@ export class FormValidationException extends Error {
errors: FormErrorWithId[]
childrens: FormValidationException[]
constructor (formId: string | number, errors: FormErrorWithId[], childErrors: FormValidationException[]) {
constructor(formId: string | number, errors: FormErrorWithId[], childErrors: FormValidationException[]) {
super('Form validation exception')
this.formId = formId
this.errors = errors

View File

@@ -4,21 +4,21 @@ import type { ObjectSchema as YupObjectSchema, ValidationError as YupError } fro
import type { ObjectSchemaAsync as ValibotObjectSchema } from 'valibot'
import type { FormError } from '#ui/types/form'
export function isYupSchema (schema: any): schema is YupObjectSchema<any> {
export function isYupSchema(schema: any): schema is YupObjectSchema<any> {
return schema.validate && schema.__isYupSchema__
}
export function isYupError (error: any): error is YupError {
export function isYupError(error: any): error is YupError {
return error.inner !== undefined
}
export async function getYupErrors (state: any, schema: YupObjectSchema<any>): Promise<FormError[]> {
export async function getYupErrors(state: any, schema: YupObjectSchema<any>): Promise<FormError[]> {
try {
await schema.validate(state, { abortEarly: false })
return []
} catch (error) {
if (isYupError(error)) {
return error.inner.map((issue) => ({
return error.inner.map(issue => ({
name: issue.path ?? '',
message: issue.message
}))
@@ -28,14 +28,14 @@ export async function getYupErrors (state: any, schema: YupObjectSchema<any>): P
}
}
export function isZodSchema (schema: any): schema is ZodSchema {
export function isZodSchema(schema: any): schema is ZodSchema {
return schema.parse !== undefined
}
export async function getZodErrors (state: any, schema: ZodSchema): Promise<FormError[]> {
export async function getZodErrors(state: any, schema: ZodSchema): Promise<FormError[]> {
const result = await schema.safeParseAsync(state)
if (result.success === false) {
return result.error.issues.map((issue) => ({
return result.error.issues.map(issue => ({
name: issue.path.join('.'),
message: issue.message
}))
@@ -43,21 +43,21 @@ export async function getZodErrors (state: any, schema: ZodSchema): Promise<Form
return []
}
export function isJoiSchema (schema: any): schema is JoiSchema {
export function isJoiSchema(schema: any): schema is JoiSchema {
return schema.validateAsync !== undefined && schema.id !== undefined
}
export function isJoiError (error: any): error is JoiError {
export function isJoiError(error: any): error is JoiError {
return error.isJoi === true
}
export async function getJoiErrors (state: any, schema: JoiSchema): Promise<FormError[]> {
export async function getJoiErrors(state: any, schema: JoiSchema): Promise<FormError[]> {
try {
await schema.validateAsync(state, { abortEarly: false })
return []
} catch (error) {
if (isJoiError(error)) {
return error.details.map((detail) => ({
return error.details.map(detail => ({
name: detail.path.join('.'),
message: detail.message
}))
@@ -67,15 +67,15 @@ export async function getJoiErrors (state: any, schema: JoiSchema): Promise<Form
}
}
export function isValibotSchema (schema: any): schema is ValibotObjectSchema<any> {
export function isValibotSchema(schema: any): schema is ValibotObjectSchema<any> {
return schema._parse !== undefined
}
export async function getValibotError (state: any, schema: ValibotObjectSchema<any>): Promise<FormError[]> {
export async function getValibotError(state: any, schema: ValibotObjectSchema<any>): Promise<FormError[]> {
const result = await schema._parse(state)
if (result.issues) {
return result.issues.map((issue) => ({
name: issue.path?.map((p) => p.key).join('.') || '',
return result.issues.map(issue => ({
name: issue.path?.map(p => p.key).join('.') || '',
message: issue.message
}))
}

View File

@@ -1,4 +1,4 @@
export function pick<Data extends object, Keys extends keyof Data> (data: Data, keys: Keys[]): Pick<Data, Keys> {
export function pick<Data extends object, Keys extends keyof Data>(data: Data, keys: Keys[]): Pick<Data, Keys> {
const result = {} as Pick<Data, Keys>
for (const key of keys) {
@@ -8,17 +8,18 @@ export function pick<Data extends object, Keys extends keyof Data> (data: Data,
return result
}
export function omit<Data extends object, Keys extends keyof Data> (data: Data, keys: Keys[]): Omit<Data, Keys> {
export function omit<Data extends object, Keys extends keyof Data>(data: Data, keys: Keys[]): Omit<Data, Keys> {
const result = { ...data }
for (const key of keys) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete result[key]
}
return result as Omit<Data, Keys>
}
export function looseToNumber (val: any): any {
const n = parseFloat(val)
return isNaN(n) ? val : n
export function looseToNumber(val: any): any {
const n = Number.parseFloat(val)
return Number.isNaN(n) ? val : n
}

View File

@@ -4,7 +4,7 @@ import type { Nuxt } from '@nuxt/schema'
import type { ModuleOptions } from './module'
import * as theme from './theme'
export function addTemplates (options: ModuleOptions, nuxt: Nuxt) {
export function addTemplates(options: ModuleOptions, nuxt: Nuxt) {
const shades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]
const template = addTemplate({