This commit is contained in:
AntoXa PRO 2023-07-11 10:38:21 +03:00
commit 2ddefb9be3
25 changed files with 747 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
dist
node_modules
package-lock.json

9
.prettierrc Normal file
View File

@ -0,0 +1,9 @@
{
"useTabs": true,
"tabWidth": 2,
"singleQuote": true,
"semi": false,
"trailingComma": "none",
"arrowParens": "avoid",
"printWidth": 79
}

48
package.json Normal file
View File

@ -0,0 +1,48 @@
{
"name": "axp-ui",
"descriiption": "My helper ui lib",
"version": "1.5.16",
"homepage": "https://antoxahub.ru/antoxa/axp-ui",
"repository": {
"type": "git",
"url": "https://antoxahub.ru/antoxa/axp-ui.git"
},
"module": "./dist/index.js",
"exports": {
".": {
"import": "./dist/index.js"
},
"./styles/": [
"./src/css/"
],
"./tailwind.config": "./tailwind.config.ts"
},
"types": "./dist/index.d.ts",
"files": [
"dist",
"tsconfig.json",
"tailwind.config.ts",
"src/css"
],
"scripts": {
"build": "rollup -c --configPlugin rollup-plugin-typescript2",
"prepare": "npm run build"
},
"dependencies": {
"axp-ts": "^1.9.6",
"rollup-plugin-typescript": "^1.0.1",
"vue": "^3.3.4"
},
"devDependencies": {
"autoprefixer": "^10.4.14",
"postcss": "^8.4.25",
"prettier": "^2.8.8",
"rollup": "^3.26.2",
"rollup-plugin-sass": "^1.12.19",
"rollup-plugin-typescript2": "^0.34.1",
"rollup-plugin-vue": "^6.0.0",
"sass": "^1.63.6",
"tailwindcss": "^3.3.2",
"tslib": "^2.6.0"
}
}

15
rollup.config.ts Normal file
View File

@ -0,0 +1,15 @@
import { defineConfig } from 'rollup'
import ts from 'rollup-plugin-typescript2'
import vue from 'rollup-plugin-vue'
import sass from 'rollup-plugin-sass'
export default defineConfig({
input: 'src/index.ts',
output: {
format: 'esm',
dir: 'dist'
},
plugins: [ts(), vue(), sass()],
external: ['vue', 'axp-ts'],
})

13
src/components/Alert.vue Normal file
View File

@ -0,0 +1,13 @@
<script setup lang="ts">
const props = defineProps<{
type?: string,
value?: string
}>()
</script>
<template>
<div class="ui-alert" :class="props.type">
<span v-if="props.value">{{props.value}}</span>
<slot v-if="$slots.default" name="default" />
</div>
</template>

27
src/components/Btn.vue Normal file
View File

@ -0,0 +1,27 @@
<script setup lang="ts">
import { computed } from 'vue'
import UiIcon from './Icon.vue'
// Props.
const props = defineProps<{
label?: string
type?: 'button' | 'submit'
color?: string
icon?: any
}>()
// Etc.
const cssClass = computed(() => {
let res = ''
if (props.color) res = props.color
return res
})
</script>
<template>
<button class="ui-btn" :class="cssClass" :type="props.type">
<ui-icon v-if="props.icon" :name="props.icon" />
<div v-if="props.label" class="label">{{ props.label }}</div>
<slot name="default" />
</button>
</template>

84
src/components/Field.vue Normal file
View File

@ -0,0 +1,84 @@
<script setup lang="ts">
const props = defineProps<{
modelValue?: any
label?: string
type?: 'text' | 'number' | 'date' | 'password' | 'checkbox'
error?: string
readonly?: boolean
disabled?: boolean
tag?: 'input' | 'textarea' | 'select'
}>()
// Emits.
const emit = defineEmits<{
(e: 'input', v: any): void
(e: 'update:modelValue', v: any): void
(e: 'update:error'): void
}>()
// Handlers.
const inputHandler = (val: any) => {
emit('input', val)
emit('update:error')
if (val?.target?.value) {
let value: any = val.target.value
try {
switch (props.type) {
case 'number':
value = Number(value)
break
case 'date':
value = new Date(value)
break
case 'text':
case 'password':
case 'checkbox':
value = String(value)
break
}
} catch (ex) {}
emit('update:modelValue', value)
} else {
emit('update:modelValue', undefined)
}
}
</script>
<template>
<div
:class="{ 'ui-field': true, error: props.error, disabled: props.disabled }"
>
<div v-if="props.label" class="label">{{ props.label }}</div>
<input
v-if="!tag || tag === 'input'"
:type="props.type"
:value="props.modelValue"
:reatonly="props.readonly"
:disabled="props.disabled"
@input="inputHandler"
class="input"
/>
<textarea
v-if="tag === 'textarea'"
:value="props.modelValue"
:reatonly="props.readonly"
:disabled="props.disabled"
@input="inputHandler"
class="input"
/>
<select
v-if="tag === 'select'"
:value="props.modelValue"
:reatonly="props.readonly"
:disabled="props.disabled"
@input="inputHandler"
class="input"
>
<slot />
</select>
<div v-if="props.error" class="message">{{ props.error }}</div>
</div>
</template>

View File

@ -0,0 +1,26 @@
<script setup lang="ts">
import UiField from './Field.vue'
import { computed } from 'vue'
// Props.
const props = defineProps<{
modelValue?: boolean
}>()
// Emits.
const emit = defineEmits<{ (e: 'update:modelValue', v?: boolean): void }>()
// Value.
const valueStr = computed({
get: () => {
if (props.modelValue) return 'on'
},
set: val => {
if (val !== undefined) emit('update:modelValue', !props.modelValue)
}
})
</script>
<template>
<ui-field type="checkbox" class="ui-field-checkbox" v-model="valueStr" />
</template>

View File

@ -0,0 +1,32 @@
<script setup lang="ts">
import { computed } from 'vue'
import { cFieldsSchema, getDateCommonFormat } from 'axp-ts'
import UiField from './Field.vue'
const props = defineProps<{
modelValue?: Date
}>()
const emit = defineEmits<{ (e: 'update:modelValue', v?: Date): void }>()
const valueStr = computed({
get: () => {
try {
const value = cFieldsSchema.shape.date.parse(props.modelValue)
const format = getDateCommonFormat(value)
return format
} catch (e) {}
},
set: (val) => {
try {
const value = cFieldsSchema.shape.date.parse(val)
emit('update:modelValue', value)
} catch (e) {}
}
})
</script>
<template>
<ui-field type="date" class="ui-field-date" v-model="valueStr" />
</template>

View File

@ -0,0 +1,7 @@
<script setup lang="ts">
import UiField from './Field.vue'
</script>
<template>
<ui-field type="number" class="ui-field-number" />
</template>

View File

@ -0,0 +1,7 @@
<script setup lang="ts">
import UiField from './Field.vue'
</script>
<template>
<ui-field type="password" class="ui-field-password" />
</template>

View File

@ -0,0 +1,22 @@
<script setup lang="ts">
import { computed } from 'vue'
import { getPhoneNumberFormat, getPhoneNumberValue } from 'axp-ts'
import UiField from './Field.vue'
// Props.
const props = defineProps<{ modelValue?: number }>()
// Emits.
const emit = defineEmits<{ (e: 'update:modelValue', v?: number): void }>()
// Value string.
const valueStr = computed({
get: () => getPhoneNumberFormat(props.modelValue),
set: val => emit('update:modelValue', getPhoneNumberValue(val))
})
</script>
<template>
<ui-field v-model="valueStr" class="ui-field-phone" />
</template>

View File

@ -0,0 +1,25 @@
<script setup lang="ts">
import type { PropType } from 'vue'
import { computed } from 'vue'
import UiField from './Field.vue'
const props = defineProps<{
modelValue?: string | number,
options: { value: string, text: string }[]
}>()
const emit = defineEmits<{ (e: 'update:modelValue', v?: any): void }>()
const displayValue = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
</script>
<template>
<ui-field tag="select" class="ui-field-select" v-model="displayValue">
<option v-for="opt in props.options" :value="opt.value">
{{opt.text}}
</option>
</ui-field>
</template>

View File

@ -0,0 +1,19 @@
<script setup lang="ts">
import { GenderEnum } from 'axp-ts'
import UiFieldSelect from './FieldSelect.vue'
const options: any[] = [
{
text: 'Мужской',
value: GenderEnum.man
},
{
text: 'Женский',
value: GenderEnum.woman
}
]
</script>
<template>
<ui-field-select :options="options" class="ui-field-select-gender" />
</template>

View File

@ -0,0 +1,7 @@
<script setup lang="ts">
import UiField from './Field.vue'
</script>
<template>
<ui-field class="ui-field-text" />
</template>

View File

@ -0,0 +1,7 @@
<script setup lang="ts">
import UiField from './Field.vue'
</script>
<template>
<ui-field tag="textarea" class="ui-field-textarea" />
</template>

132
src/components/Form.vue Normal file
View File

@ -0,0 +1,132 @@
<script setup lang="ts">
import type { Ref } from 'vue'
import type { TNotificationItem, BaseFormModel } from 'axp-ts'
import { ref, computed, watch } from 'vue'
import UiBtn from './Btn.vue'
// Props.
const props = defineProps<{
modelValue: BaseFormModel<any>
title?: string
noTitle?: boolean
messages?: TNotificationItem[]
noActions?: boolean
disabled?: boolean
load?: boolean
showAll?: boolean
fn?: (obj?: any) => Promise<any>
}>()
// Emits.
const emit = defineEmits<{
(e: 'submit', v?: BaseFormModel<any>): void
(e: 'failedValid', v?: BaseFormModel<any>): void
(e: 'update:load', v: boolean): void
(e: 'fnCompleted', v?: any): void
}>()
// Controls.
const ctrls = computed(() => {
if (props.modelValue) {
if (props.showAll) {
return props.modelValue.ctrls
} else {
// @ts-ignore
return props.modelValue.ctrls.filter(e => !e.hidden)
}
}
return []
})
// Content.
const title = computed(() => {
if (!props.noTitle) return props.title || props.modelValue?.title
})
// Init data.
const load = ref(false)
watch(load, val => emit('update:load', val))
const messages: Ref<TNotificationItem[]> = ref(props.messages || [])
watch(() => props.messages, (val) => messages.value = val || [])
// Handlers.
const submitHandler = async () => {
if (props.modelValue) {
if (props.modelValue.isValid()) {
emit('submit', props.modelValue)
if (props.fn) {
load.value = true
messages.value = []
try {
const dR = await props.fn(props.modelValue.obj)
emit('fnCompleted', dR)
if (dR?.errors && Array.isArray(dR.errors)) {
for (const err of dR.errors) {
if (err.code && err.text)
if (
props.modelValue &&
// @ts-ignore
props.modelValue.ctrls.find(e => e.key === err.code)
) {
props.modelValue.setValidError(err.code, err.text)
} else {
messages.value.push({ code: 'error', text: err.text })
}
}
}
} catch (ex: any) {
messages.value.push({ code: 'error', text: ex.message })
}
load.value = false
}
} else {
emit('failedValid', props.modelValue)
}
} else {
emit('submit')
}
}
</script>
<template>
<form @submit.prevent="submitHandler" class="ui-form">
<div v-if="title" class="ui-form-header">
<h3 class="ui-form-title">{{ title }}</h3>
</div>
<div v-if="messages.length" class="ui-form-messages">
<div v-for="message in messages" :class="'text-' + message.code">
{{ message.text }}
</div>
</div>
<div class="ui-form-body">
<component
v-if="props.modelValue"
v-for="ctrl in ctrls"
:is="ctrl.component"
:label="ctrl.label"
v-model="props.modelValue.obj[ctrl.key]"
v-model:error="props.modelValue._errors[ctrl.key]"
:readonly="ctrl.readonly"
:disabled="load || props.load || props.disabled || ctrl.disabled"
:class="'ui-field-' + ctrl.key"
/>
<slot v-if="$slots.default" name="default" />
</div>
<div v-if="!props.noActions" class="ui-form-actions">
<slot v-if="$slots.actions" name="actions" />
<ui-btn
v-else
label="Отправить"
type="submit"
color="primary"
:disabled="load || props.disabled"
/>
</div>
</form>
</template>

104
src/components/Icon.vue Normal file
View File

@ -0,0 +1,104 @@
<script lang="ts">
export const icons = {
account: `
<circle cx="12" cy="7" r="4" />
<path d="M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2" />
`,
doc: `
<path d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"/>
`,
vk: `
<path d="M21.579 6.855c.14-.465 0-.806-.662-.806h-2.193c-.558 0-.813.295-.953.619 0 0-1.115 2.719-2.695 4.482-.51.513-.743.675-1.021.675-.139 0-.341-.162-.341-.627V6.855c0-.558-.161-.806-.626-.806H9.642c-.348 0-.558.258-.558.504 0 .528.79.65.871 2.138v3.228c0 .707-.127.836-.407.836-.743 0-2.551-2.729-3.624-5.853-.209-.607-.42-.852-.98-.852H2.752c-.627 0-.752.295-.752.619 0 .582.743 3.462 3.461 7.271 1.812 2.601 4.363 4.011 6.687 4.011 1.393 0 1.565-.313 1.565-.853v-1.966c0-.626.133-.752.574-.752.324 0 .882.164 2.183 1.417 1.486 1.486 1.732 2.153 2.567 2.153h2.192c.626 0 .939-.313.759-.931-.197-.615-.907-1.51-1.849-2.569-.512-.604-1.277-1.254-1.51-1.579-.325-.419-.231-.604 0-.976.001.001 2.672-3.761 2.95-5.04z"/>
`,
youtube: `
<path d="M22.54 6.42a2.78 2.78 0 0 0-1.94-2C18.88 4 12 4 12 4s-6.88 0-8.6.46a2.78 2.78 0 0 0-1.94 2A29 29 0 0 0 1 11.75a29 29 0 0 0 .46 5.33A2.78 2.78 0 0 0 3.4 19c1.72.46 8.6.46 8.6.46s6.88 0 8.6-.46a2.78 2.78 0 0 0 1.94-2 29 29 0 0 0 .46-5.25 29 29 0 0 0-.46-5.33z" />
<polygon points="9.75 15.02 15.5 11.75 9.75 8.48 9.75 15.02" />
`,
video: `
<polygon points="23 7 16 12 23 17 23 7" />
<rect x="1" y="5" width="15" height="14" rx="2" ry="2" />
`,
yandex: `
<path d="m10 24v-7.786l-5.2-13.964h2.616l3.834 10.767 4.41-13.018h2.405l-5.658 16.303v7.697z"/>
`,
menu: `
<path stroke="none" d="M0 0h24v24H0z"/>
<line x1="4" y1="6" x2="20" y2="6" />
<line x1="4" y1="12" x2="20" y2="12" />
<line x1="4" y1="18" x2="20" y2="18" />
`,
close: `
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
`,
'arrow-down': `
<path stroke="none" d="M0 0h24v24H0z"/>
<polyline points="6 9 12 15 18 9" />
`,
car: `
<path stroke="none" d="M0 0h24v24H0z"/>
<circle cx="7" cy="17" r="2" />
<circle cx="17" cy="17" r="2" />
<path d="M5 17h-2v-6l2-5h9l4 5h1a2 2 0 0 1 2 2v4h-2m-4 0h-6m-6 -6h15m-6 0v-5" />
`,
review: `
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
`,
phone: `
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" />
`,
circle: `
<circle cx="12" cy="12" r="10"></circle>
<path d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
`,
dzen: `
<path d="M 11.659994,0.01953125 C 5.2952996,0.20519926 0.20563242,5.2947457 0.01953125,11.659552 4.8820049,11.598767 7.6163326,11.265345 9.4411321,9.4406121 11.265931,7.6159232 11.59921,4.8819566 11.659994,0.01953125 Z m 0.719931,0 c 0.06078,4.86242535 0.393207,7.59634815 2.218006,9.42108085 1.824686,1.8245759 4.559692,2.1581249 9.4216,2.2189399 C 23.83343,5.2948697 18.744448,0.20538037 12.379925,0.01953125 Z M 0.01953125,12.37951 C 0.20581298,18.744149 5.2954188,23.833867 11.659994,24.019531 11.59921,19.156885 11.265931,16.422327 9.4411321,14.597594 7.6163326,12.772905 4.8820049,12.440295 0.01953125,12.37951 Z m 23.99999975,0 c -4.861908,0.06081 -7.596914,0.393508 -9.4216,2.218084 -1.8248,1.824733 -2.157222,4.559291 -2.218006,9.421937 6.364403,-0.185846 11.453324,-5.275505 11.639606,-11.640021 z" />
`,
instagram: `
<path d="m 18.374816,4.4184098 c -0.80327,0 -1.405723,0.6025075 -1.405723,1.4058562 0,0.8033487 0.602453,1.4058562 1.405723,1.4058562 0.803271,0 1.405724,-0.6025075 1.405724,-1.4058562 0,-0.8033487 -0.602453,-1.4058562 -1.405724,-1.4058562 z" />
<path d="m 12.049062,6.2259403 c -3.3134909,0 -5.9241249,2.7112998 -5.9241249,5.9246867 0,3.213387 2.7110426,5.924686 5.9241249,5.924686 3.213082,0 5.924119,-2.711299 5.924119,-5.924686 0,-3.2133869 -2.610629,-5.9246867 -5.924119,-5.9246867 z m 0,9.7405887 c -2.1085854,0 -3.815535,-1.707118 -3.815535,-3.815902 0,-2.108784 1.7069496,-3.8159024 3.815535,-3.8159024 2.108585,0 3.815534,1.7071184 3.815534,3.8159024 0,2.108784 -1.706949,3.815902 -3.815534,3.815902 z" />
<path d="M 16.868685,0 H 7.3298474 C 3.2130851,0 0,3.2133868 0,7.2301222 V 16.769878 C 0,20.786613 3.2130851,24 7.2294388,24 h 9.5388362 c 4.016353,0 7.229434,-3.213387 7.229434,-7.230122 V 7.2301222 C 24.098119,3.2133868 20.885037,0 16.868685,0 Z m 4.92003,16.87029 c 0,2.7113 -2.208993,5.020926 -5.02044,5.020926 H 7.2294388 c -2.711045,0 -5.0204482,-2.209205 -5.0204482,-5.020926 V 7.3305428 c 0,-2.7112998 2.2089985,-5.0209173 5.0204482,-5.0209173 h 9.5388362 c 2.711038,0 5.02044,2.2092048 5.02044,5.0209173 z" />
`,
moon: `
<path d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
`,
sun: `
<circle cx="12" cy="12" r="5" />
<line x1="12" y1="1" x2="12" y2="3" />
<line x1="12" y1="21" x2="12" y2="23" />
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
<line x1="1" y1="12" x2="3" y2="12" />
<line x1="21" y1="12" x2="23" y2="12" />
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
`
}
</script>
<script setup lang="ts">
import { computed } from 'vue'
// Props.
const props = defineProps<{
name: keyof typeof icons
fill?: boolean
}>()
const iconName = computed(() => {
// @ts-ignore
return icons[props.name]
})
</script>
<template>
<span class="ui-icon">
<svg
viewBox="0 0 24 24"
:fill="fill ? 'currentColor' : 'none'"
stroke="currentColor"
stroke-width="1.2"
v-html="iconName"
/>
</span>
</template>

14
src/components/index.ts Normal file
View File

@ -0,0 +1,14 @@
export { default as UiField } from './Field.vue'
export { default as UiFieldText } from './FieldText.vue'
export { default as UiFieldPassword } from './FieldPassword.vue'
export { default as UiFieldNumber } from './FieldNumber.vue'
export { default as UiFieldPhone } from './FieldPhone.vue'
export { default as UiFieldDate } from './FieldDate.vue'
export { default as UiFieldTextArea } from './FieldTextArea.vue'
export { default as UiFieldCheckbox } from './FieldCheckbox.vue'
export { default as UiFieldSelect } from './FieldSelect.vue'
export { default as UiFieldSelectGender } from './FieldSelectGender.vue'
export { default as UiBtn } from './Btn.vue'
export { default as UiAlert } from './Alert.vue'
export { default as UiIcon } from './Icon.vue'
export { default as UiForm } from './Form.vue'

64
src/css/common.sass Normal file
View File

@ -0,0 +1,64 @@
body
@apply text-dark
*
@apply text-base
.title
@apply mb-4
h1
@apply text-xl md:text-2xl font-bold
h2
@apply text-lg md:text-xl font-bold
h3
@apply text-base md:text-lg font-bold
h4
@apply text-sm md:text-base font-bold
.ui
&-btn,
&-field .input
@apply h-[38px] text-base
&-btn
@apply px-4 border
@apply flex justify-center items-center
> *:not(:last-child)
@apply mr-1
> .ui-icon:first-child
@apply ml-[-5px]
&-field
.label
@apply mb-1
.input
@apply block w-full border px-2 py-2
outline: none
.message
@apply mt-1 text-sm
&-checkbox
@apply flex items-center justify-start
.input
@apply w-max order-first mr-4
@apply w-[24px] h-[24px] border rounded
&-textarea
.input
@apply min-h-[80px]
&-icon
@apply block w-[24px]
&-form
&-header
@apply pb-4
&-messages
@apply pb-4
> *:not(:last-child)
@apply mb-2
&-body
> *:not(:last-child)
@apply mb-4
&-actions
@apply flex items-center pt-4
> *:not(:last-child)
@apply mr-2

30
src/css/theme.sass Normal file
View File

@ -0,0 +1,30 @@
.ui
&-field
&.disabled
.input
@apply bg-light/10 text-dark/60
&.error
.label,
.input,
.message
@apply text-error border-error
&-btn
&.primary
@apply bg-primary text-white
&.accent
@apply bg-accent text-white
&.dark
@apply bg-dark text-white
&.light
@apply bg-light text-white
&.error
@apply bg-error text-white
&.success
@apply bg-success text-white
&:disabled
@apply text-white bg-light border-light
&-btn,
&-field .input
@apply rounded
&-field .input
@apply bg-white

1
src/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './components'

5
src/types/vueshim.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}

31
tailwind.config.ts Normal file
View File

@ -0,0 +1,31 @@
import type { Config } from 'tailwindcss'
const config: Config = {
content: ['./src/components/**/*.vue'],
theme: {
extend: {
colors: {
primary: '#3c87a4',
accent: '#f00000',
dark: '#363636',
light: '#cdcdcd',
error: '#f00000',
success: '#07b859'
},
container: {
center: true,
padding: '1rem'
},
fontSize: {
sm: ['.8rem', '1'],
base: ['1rem', '1'],
lg: ['1.3rem', '1'],
xl: ['1.8rem', '1'],
'2xl': ['2.2rem', '1']
}
}
},
plugins: []
}
export default config

15
tsconfig.json Normal file
View File

@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"strict": true,
"moduleResolution": "Node",
"isolatedModules": true,
"esModuleInterop": true,
"useDefineForClassFields": true,
"declaration": true
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue"]
}