mirror of
https://github.com/HoshinoSuzumi/rayine-ui.git
synced 2025-04-10 07:28:51 +08:00
🚧 wip(select)
This commit is contained in:
parent
d24140f673
commit
8d79696444
25
docs/content/2.components/select.md
Normal file
25
docs/content/2.components/select.md
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
description: The select component is used to create a dropdown list of options
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
::ComponentPreview
|
||||
---
|
||||
props:
|
||||
placeholder: huh?
|
||||
options: [{label: 'Option 1', value: 'option1'}, {label: 'Option 2', value: 'option2'}, {label: 'Option 3', value: 'option3', disabled: true}]
|
||||
---
|
||||
::
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
::ComponentProps
|
||||
::
|
||||
|
||||
### Theme
|
||||
|
||||
::ComponentDefaults
|
||||
::
|
156
src/runtime/components/forms/Select.vue
Normal file
156
src/runtime/components/forms/Select.vue
Normal file
@ -0,0 +1,156 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, toRef, type ComputedRef, type PropType } from 'vue'
|
||||
import { twJoin, twMerge } from 'tailwind-merge'
|
||||
import { select } from '../../themes'
|
||||
import type { DeepPartial, SelectColor, SelectSize, SelectVariant, Strategy } from '../../types'
|
||||
import { getValueByPath } from '../../utils'
|
||||
import { useRayUI } from '#build/imports'
|
||||
|
||||
const config = select
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [String, Number, Object] as PropType<string | number | object | null>,
|
||||
default: '',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
size: {
|
||||
type: String as PropType<SelectSize>,
|
||||
default: () => config.default.size,
|
||||
},
|
||||
color: {
|
||||
type: String as PropType<SelectColor>,
|
||||
default: () => config.default.color,
|
||||
},
|
||||
variant: {
|
||||
type: String as PropType<SelectVariant>,
|
||||
default: () => config.default.variant,
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
labelAttr: {
|
||||
type: String,
|
||||
default: 'label',
|
||||
},
|
||||
valueAttr: {
|
||||
type: String,
|
||||
default: 'value',
|
||||
},
|
||||
ui: {
|
||||
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
class: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue', 'change'],
|
||||
setup(props, { emit }) {
|
||||
const { ui, attrs } = useRayUI('select', toRef(props, 'ui'), config)
|
||||
|
||||
const selectClass = computed(() => {
|
||||
return twMerge(twJoin(
|
||||
ui.value.base,
|
||||
ui.value.rounded,
|
||||
ui.value.size[props.size],
|
||||
ui.value.padding[props.size],
|
||||
ui.value.variant[props.variant].replaceAll('{color}', props.color),
|
||||
), props.class)
|
||||
})
|
||||
|
||||
const normalizeOption = (option: any) => {
|
||||
if (['string', 'number', 'boolean'].includes(typeof option)) {
|
||||
return {
|
||||
[props.labelAttr]: option,
|
||||
[props.valueAttr]: option,
|
||||
}
|
||||
}
|
||||
return {
|
||||
...option,
|
||||
[props.labelAttr]: getValueByPath(option, props.labelAttr, ''),
|
||||
[props.valueAttr]: getValueByPath(option, props.valueAttr, ''),
|
||||
}
|
||||
}
|
||||
|
||||
const normalizedOptions = computed(() => {
|
||||
return props.options.map(normalizeOption)
|
||||
})
|
||||
|
||||
const normalizedPlaceholderOptions = computed(() => {
|
||||
if (props.placeholder) {
|
||||
return [
|
||||
{
|
||||
[props.labelAttr]: props.placeholder,
|
||||
[props.valueAttr]: '',
|
||||
disabled: true,
|
||||
},
|
||||
...normalizedOptions.value,
|
||||
]
|
||||
}
|
||||
|
||||
return normalizedOptions.value
|
||||
})
|
||||
|
||||
const normalizedValue = computed(() => {
|
||||
const modelValueNormalized = normalizeOption(props.modelValue)
|
||||
const matchedOption = normalizedPlaceholderOptions.value.find(option => option[props.valueAttr] === modelValueNormalized[props.valueAttr])
|
||||
if (!matchedOption) {
|
||||
return ''
|
||||
}
|
||||
return matchedOption[props.valueAttr]
|
||||
})
|
||||
|
||||
const onInput = (event: Event) => {
|
||||
emit('update:modelValue', (event.target as HTMLInputElement).value)
|
||||
}
|
||||
|
||||
const onChange = (event: Event) => {
|
||||
emit('change', (event.target as HTMLInputElement).value)
|
||||
}
|
||||
|
||||
return {
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
attrs,
|
||||
selectClass,
|
||||
normalizedOptions,
|
||||
normalizedPlaceholderOptions,
|
||||
normalizedValue,
|
||||
onInput,
|
||||
onChange,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="ui.wrapper">
|
||||
<select :class="selectClass" v-bind="attrs" @input="onInput" @change="onChange">
|
||||
<option
|
||||
v-for="(option, k) in normalizedPlaceholderOptions"
|
||||
:key="k"
|
||||
:value="option[valueAttr]"
|
||||
:disabled="option.disabled"
|
||||
:selected="option[valueAttr] === normalizedValue"
|
||||
>
|
||||
{{ option[labelAttr] }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</template>
|
5
src/runtime/themes/forms/select.ts
Normal file
5
src/runtime/themes/forms/select.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import input from './input'
|
||||
|
||||
export default {
|
||||
...input,
|
||||
}
|
@ -10,6 +10,7 @@ export { default as mark } from './elements/mark'
|
||||
export { default as input } from './forms/input'
|
||||
export { default as textarea } from './forms/textarea'
|
||||
export { default as toggle } from './forms/toggle'
|
||||
export { default as select } from './forms/select'
|
||||
|
||||
// overlays
|
||||
export { default as message } from './overlays/message'
|
||||
|
@ -5,5 +5,6 @@ export * from './textarea'
|
||||
export * from './kbd'
|
||||
export * from './toggle'
|
||||
export * from './mark'
|
||||
export * from './select'
|
||||
|
||||
export * from './utils'
|
||||
|
8
src/runtime/types/select.d.ts
vendored
Normal file
8
src/runtime/types/select.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
import type { AppConfig } from 'nuxt/schema'
|
||||
import type { select } from '../themes'
|
||||
import type { ExtractDeepKey } from './utils'
|
||||
import type colors from '#ray-colors'
|
||||
|
||||
export type SelectColor = (typeof colors)[number]
|
||||
export type SelectSize = keyof typeof select.size | ExtractDeepKey<AppConfig, ['rayui', 'select', 'size']>
|
||||
export type SelectVariant = keyof typeof select.variant | ExtractDeepKey<AppConfig, ['rayui', 'select', 'variant']>
|
Loading…
Reference in New Issue
Block a user