feat: rewrite to use app config and rework docs (#143)

Co-authored-by: Daniel Roe <daniel@roe.dev>
Co-authored-by: Sébastien Chopin <seb@nuxt.com>
This commit is contained in:
Benjamin Canac
2023-05-04 14:49:19 +02:00
committed by GitHub
parent 56230ea915
commit 6da0db0113
144 changed files with 10470 additions and 8109 deletions

View File

@@ -1,5 +1,5 @@
<template>
<div :class="wrapperClass">
<div :class="ui.wrapper">
<select
:id="name"
:name="name"
@@ -17,7 +17,7 @@
:label="option[textAttribute]"
>
<option
v-for="(childOption, index2) in option.children"
v-for="(childOption, index2) in (option.children as any[])"
:key="`${childOption[valueAttribute]}-${index}-${index2}`"
:value="childOption[valueAttribute]"
:selected="childOption[valueAttribute] === normalizedValue"
@@ -36,169 +36,197 @@
</template>
</select>
<div v-if="icon" :class="iconWrapperClass">
<div v-if="icon" :class="leadingIconClass">
<Icon :name="icon" :class="iconClass" />
</div>
<span :class="trailingIconClass">
<Icon name="i-heroicons-chevron-down-20-solid" :class="iconClass" aria-hidden="true" />
</span>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { get } from 'lodash-es'
import { classNames } from '../../utils'
import Icon from '../elements/Icon.vue'
import $ui from '#build/ui'
const props = defineProps({
modelValue: {
type: [String, Number, Object],
default: ''
},
name: {
type: String,
required: true
},
placeholder: {
type: String,
default: null
},
required: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
options: {
type: Array,
default: () => []
},
size: {
type: String,
default: 'md',
validator (value: string) {
return Object.keys($ui.select.size).includes(value)
}
},
wrapperClass: {
type: String,
default: () => $ui.select.wrapper
},
baseClass: {
type: String,
default: () => $ui.select.base
},
iconBaseClass: {
type: String,
default: () => $ui.select.icon.base
},
customClass: {
type: String,
default: null
},
appearance: {
type: String,
default: 'default',
validator (value: string) {
return Object.keys($ui.select.appearance).includes(value)
}
},
textAttribute: {
type: String,
default: 'text'
},
valueAttribute: {
type: String,
default: 'value'
},
icon: {
type: String,
default: null
}
})
const emit = defineEmits(['update:modelValue'])
const onInput = (value: string) => {
emit('update:modelValue', value)
}
const guessOptionValue = (option: any) => {
return get(option, props.valueAttribute, get(option, props.textAttribute))
}
const guessOptionText = (option: any) => {
return get(option, props.textAttribute, get(option, props.valueAttribute))
}
const normalizeOption = (option: any) => {
if (['string', 'number', 'boolean'].includes(typeof option)) {
return {
[props.valueAttribute]: option,
[props.textAttribute]: option
}
}
return {
...option,
[props.valueAttribute]: guessOptionValue(option),
[props.textAttribute]: guessOptionText(option)
}
}
const normalizedOptions = computed(() => {
return props.options.map(option => normalizeOption(option))
})
const normalizedOptionsWithPlaceholder = computed(() => {
if (!props.placeholder) {
return normalizedOptions.value
}
return [
{
[props.valueAttribute]: '',
[props.textAttribute]: props.placeholder,
disabled: true
},
...normalizedOptions.value
]
})
const normalizedValue = computed(() => {
const normalizeModelValue = normalizeOption(props.modelValue)
const foundOption = normalizedOptionsWithPlaceholder.value.find(option => option[props.valueAttribute] === normalizeModelValue[props.valueAttribute])
if (!foundOption) {
return ''
}
return foundOption[props.valueAttribute]
})
const selectClass = computed(() => {
return classNames(
props.baseClass,
$ui.select.size[props.size],
$ui.select.spacing[props.size],
$ui.select.appearance[props.appearance],
!!props.icon && $ui.select.leading.spacing[props.size],
$ui.select.trailing.spacing[props.size],
props.customClass
)
})
const iconClass = computed(() => {
return classNames(
props.iconBaseClass,
$ui.select.icon.size[props.size],
!!props.icon && $ui.select.icon.leading.spacing[props.size]
)
})
const iconWrapperClass = $ui.select.icon.leading.wrapper
</script>
<script lang="ts">
export default { name: 'USelect' }
import { computed, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { get } from 'lodash-es'
import { defu } from 'defu'
import Icon from '../elements/Icon.vue'
import { classNames } from '../../utils'
import { useAppConfig } from '#imports'
// TODO: Remove
// @ts-expect-error
import appConfig from '#build/app.config'
// const appConfig = useAppConfig()
export default defineComponent({
components: {
Icon
},
props: {
modelValue: {
type: [String, Number, Object],
default: ''
},
name: {
type: String,
required: true
},
placeholder: {
type: String,
default: null
},
required: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
icon: {
type: String,
default: null
},
options: {
type: Array,
default: () => []
},
size: {
type: String,
default: () => appConfig.ui.select.default.size,
validator (value: string) {
return Object.keys(appConfig.ui.select.size).includes(value)
}
},
appearance: {
type: String,
default: () => appConfig.ui.select.default.appearance,
validator (value: string) {
return Object.keys(appConfig.ui.select.appearance).includes(value)
}
},
textAttribute: {
type: String,
default: 'text'
},
valueAttribute: {
type: String,
default: 'value'
},
ui: {
type: Object as PropType<Partial<typeof appConfig.ui.select>>,
default: () => appConfig.ui.select
}
},
emits: ['update:modelValue', 'focus', 'blur'],
setup (props, { emit }) {
// TODO: Remove
const appConfig = useAppConfig()
const ui = computed<Partial<typeof appConfig.ui.select>>(() => defu({}, props.ui, appConfig.ui.select))
const onInput = (value: string) => {
emit('update:modelValue', value)
}
const guessOptionValue = (option: any) => {
return get(option, props.valueAttribute, get(option, props.textAttribute))
}
const guessOptionText = (option: any) => {
return get(option, props.textAttribute, get(option, props.valueAttribute))
}
const normalizeOption = (option: any) => {
if (['string', 'number', 'boolean'].includes(typeof option)) {
return {
[props.valueAttribute]: option,
[props.textAttribute]: option
}
}
return {
...option,
[props.valueAttribute]: guessOptionValue(option),
[props.textAttribute]: guessOptionText(option)
}
}
const normalizedOptions = computed(() => {
return props.options.map(option => normalizeOption(option))
})
const normalizedOptionsWithPlaceholder = computed(() => {
if (!props.placeholder) {
return normalizedOptions.value
}
return [
{
[props.valueAttribute]: '',
[props.textAttribute]: props.placeholder,
disabled: true
},
...normalizedOptions.value
]
})
const normalizedValue = computed(() => {
const normalizeModelValue = normalizeOption(props.modelValue)
const foundOption = normalizedOptionsWithPlaceholder.value.find(option => option[props.valueAttribute] === normalizeModelValue[props.valueAttribute])
if (!foundOption) {
return ''
}
return foundOption[props.valueAttribute]
})
const selectClass = computed(() => {
return classNames(
ui.value.base,
ui.value.size[props.size],
ui.value.spacing[props.size],
ui.value.appearance[props.appearance],
!!props.icon && ui.value.leading.spacing[props.size],
ui.value.trailing.spacing[props.size],
ui.value.custom
)
})
const iconClass = computed(() => {
return classNames(
ui.value.icon.base,
ui.value.icon.size[props.size]
)
})
const leadingIconClass = computed(() => {
return classNames(
ui.value.icon.leading.wrapper,
ui.value.icon.leading.spacing[props.size]
)
})
const trailingIconClass = computed(() => {
return classNames(
ui.value.icon.trailing.wrapper,
ui.value.icon.trailing.spacing[props.size]
)
})
return {
// eslint-disable-next-line vue/no-dupe-keys
ui,
normalizedOptionsWithPlaceholder,
normalizedValue,
selectClass,
iconClass,
leadingIconClass,
trailingIconClass,
onInput
}
}
})
</script>