mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-01-18 12:34:25 +08:00
refactor(dynamic-tags): ts
This commit is contained in:
parent
09e33ee18d
commit
78773c5701
@ -9,7 +9,7 @@ export {
|
||||
render
|
||||
} from './vue'
|
||||
export type { MaybeArray } from './vue'
|
||||
export { warn, warnOnce, throwError } from './naive'
|
||||
export { warn, warnOnce, throwError, smallerSize, largerSize } from './naive'
|
||||
export { formatLength } from './css'
|
||||
export { createKey } from './cssr'
|
||||
export * from './composable'
|
||||
|
@ -1 +1,2 @@
|
||||
export { warn, warnOnce, throwError } from './warn'
|
||||
export { smallerSize, largerSize } from './prop'
|
||||
|
29
src/_utils/naive/prop.ts
Normal file
29
src/_utils/naive/prop.ts
Normal file
@ -0,0 +1,29 @@
|
||||
export function largerSize (
|
||||
size: 'tiny' | 'small' | 'medium' | 'large'
|
||||
): 'small' | 'medium' | 'large' | 'huge' {
|
||||
switch (size) {
|
||||
case 'tiny':
|
||||
return 'small'
|
||||
case 'small':
|
||||
return 'medium'
|
||||
case 'medium':
|
||||
return 'large'
|
||||
case 'large':
|
||||
return 'huge'
|
||||
}
|
||||
}
|
||||
|
||||
export function smallerSize (
|
||||
size: 'small' | 'medium' | 'large' | 'huge'
|
||||
): 'tiny' | 'small' | 'medium' | 'large' {
|
||||
switch (size) {
|
||||
case 'small':
|
||||
return 'tiny'
|
||||
case 'medium':
|
||||
return 'small'
|
||||
case 'large':
|
||||
return 'medium'
|
||||
case 'huge':
|
||||
return 'large'
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
export { default as NDynamicTags } from './src/DynamicTags.vue'
|
2
src/dynamic-tags/index.ts
Normal file
2
src/dynamic-tags/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
/* istanbul ignore file */
|
||||
export { default as NDynamicTags } from './src/DynamicTags'
|
202
src/dynamic-tags/src/DynamicTags.tsx
Normal file
202
src/dynamic-tags/src/DynamicTags.tsx
Normal file
@ -0,0 +1,202 @@
|
||||
import {
|
||||
h,
|
||||
defineComponent,
|
||||
ref,
|
||||
PropType,
|
||||
CSSProperties,
|
||||
computed,
|
||||
nextTick
|
||||
} from 'vue'
|
||||
import commonProps from '../../tag/src/common-props'
|
||||
import { AddIcon } from '../../_base/icons'
|
||||
import { NButton } from '../../button'
|
||||
import { InputRef, NInput } from '../../input'
|
||||
import { NTag } from '../../tag'
|
||||
import { NBaseIcon } from '../../_base'
|
||||
import { useTheme, useFormItem, useLocale } from '../../_mixins'
|
||||
import type { ThemeProps } from '../../_mixins'
|
||||
import { warn, call, MaybeArray, smallerSize } from '../../_utils'
|
||||
import { dynamicTagsLight } from '../styles'
|
||||
import type { DynamicTagsTheme } from '../styles'
|
||||
|
||||
import type { OnUpdateValue } from './interface'
|
||||
import style from './styles/index.cssr'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DynamicTags',
|
||||
props: {
|
||||
...(useTheme.props as ThemeProps<DynamicTagsTheme>),
|
||||
...commonProps,
|
||||
closable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
value: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
tagStyle: {
|
||||
type: Object as PropType<CSSProperties>,
|
||||
default: () => {
|
||||
return {
|
||||
marginRight: '6px'
|
||||
}
|
||||
}
|
||||
},
|
||||
inputStyle: {
|
||||
type: Object as PropType<CSSProperties>,
|
||||
default: () => {
|
||||
return {
|
||||
width: '64px'
|
||||
}
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:value': [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
|
||||
// deprecated
|
||||
onChange: {
|
||||
type: [Function, Array] as PropType<
|
||||
MaybeArray<OnUpdateValue> | undefined
|
||||
>,
|
||||
validator: () => {
|
||||
if (__DEV__) {
|
||||
warn(
|
||||
'dynamic-tags',
|
||||
'`on-change` is deprecated, please use `on-update:value` instead.'
|
||||
)
|
||||
}
|
||||
return true
|
||||
},
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
const { locale } = useLocale('DynamicTags')
|
||||
const formItem = useFormItem(props)
|
||||
const inputValueRef = ref('')
|
||||
const showInputRef = ref(false)
|
||||
const inputForceFocusedRef = ref(true)
|
||||
const inputInstRef = ref<InputRef | null>(null)
|
||||
const themeRef = useTheme(
|
||||
'DynamicTags',
|
||||
'DynamicTags',
|
||||
style,
|
||||
dynamicTagsLight,
|
||||
props
|
||||
)
|
||||
const localizedAddRef = computed(() => {
|
||||
return locale.value.add
|
||||
})
|
||||
const inputSizeRef = computed(() => {
|
||||
return smallerSize(props.size)
|
||||
})
|
||||
function doChange (value: string[]): void {
|
||||
const { onChange, 'onUpdate:value': onUpdateValue } = props
|
||||
const { nTriggerFormInput, nTriggerFormChange } = formItem
|
||||
if (onChange) call(onChange, value)
|
||||
if (onUpdateValue) call(onUpdateValue, value)
|
||||
nTriggerFormInput()
|
||||
nTriggerFormChange()
|
||||
}
|
||||
function handleCloseClick (index: number): void {
|
||||
const tags = props.value.slice(0)
|
||||
tags.splice(index, 1)
|
||||
doChange(tags)
|
||||
}
|
||||
function handleInputKeyUp (e: KeyboardEvent): void {
|
||||
switch (e.code) {
|
||||
case 'Enter':
|
||||
handleInputConfirm()
|
||||
}
|
||||
}
|
||||
function handleInputConfirm (): void {
|
||||
if (inputValueRef.value) {
|
||||
const tags = props.value.slice(0)
|
||||
tags.push(inputValueRef.value)
|
||||
doChange(tags)
|
||||
}
|
||||
showInputRef.value = false
|
||||
inputForceFocusedRef.value = true
|
||||
inputValueRef.value = ''
|
||||
}
|
||||
function handleInputBlur (): void {
|
||||
handleInputConfirm()
|
||||
}
|
||||
function handleAddClick (): void {
|
||||
showInputRef.value = true
|
||||
void nextTick(() => {
|
||||
inputInstRef.value?.focus()
|
||||
inputForceFocusedRef.value = false
|
||||
})
|
||||
}
|
||||
return {
|
||||
inputInstRef,
|
||||
localizedAdd: localizedAddRef,
|
||||
inputSize: inputSizeRef,
|
||||
inputValue: inputValueRef,
|
||||
showInput: showInputRef,
|
||||
inputForceFocused: inputForceFocusedRef,
|
||||
handleInputKeyUp,
|
||||
handleAddClick,
|
||||
handleInputBlur,
|
||||
handleCloseClick,
|
||||
mergedTheme: themeRef
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { mergedTheme } = this
|
||||
return (
|
||||
<div class="n-dynamic-tags">
|
||||
{this.value.map((tag, index) => (
|
||||
<NTag
|
||||
key={index}
|
||||
unstableTheme={mergedTheme.peers.Tag}
|
||||
unstableThemeOverrides={mergedTheme.overrides.Tag}
|
||||
style={this.tagStyle}
|
||||
type={this.type}
|
||||
round={this.round}
|
||||
size={this.size}
|
||||
closable={this.closable}
|
||||
disabled={this.disabled}
|
||||
onClose={() => this.handleCloseClick(index)}
|
||||
>
|
||||
{{ defualt: () => tag }}
|
||||
</NTag>
|
||||
))}
|
||||
{this.showInput ? (
|
||||
<NInput
|
||||
ref="inputInstRef"
|
||||
value={this.inputValue}
|
||||
onUpdateValue={(v) => {
|
||||
this.inputValue = v
|
||||
}}
|
||||
forceFocus={this.inputForceFocused}
|
||||
unstableTheme={mergedTheme.peers.Input}
|
||||
unstableThemeOverrides={mergedTheme.overrides.Input}
|
||||
style={this.inputStyle}
|
||||
size={this.inputSize}
|
||||
placeholder=""
|
||||
onKeyup={this.handleInputKeyUp}
|
||||
onBlur={this.handleInputBlur}
|
||||
/>
|
||||
) : (
|
||||
<NButton
|
||||
dashed
|
||||
unstableTheme={this.mergedTheme.peers.Button}
|
||||
unstableThemeOverrides={this.mergedTheme.overrides.Button}
|
||||
size={this.inputSize}
|
||||
onClick={this.handleAddClick}
|
||||
>
|
||||
{{
|
||||
icon: () => (
|
||||
<NBaseIcon>{{ default: () => <AddIcon /> }}</NBaseIcon>
|
||||
)
|
||||
}}
|
||||
</NButton>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
@ -1,184 +0,0 @@
|
||||
<template>
|
||||
<div class="n-dynamic-tags">
|
||||
<n-tag
|
||||
v-for="(tag, index) in value"
|
||||
:key="index"
|
||||
:unstable-theme="mergedTheme.peers.Tag"
|
||||
:unstable-theme-overrides="mergedTheme.overrides.Tag"
|
||||
:style="tagStyle"
|
||||
:type="type"
|
||||
:round="round"
|
||||
:size="size"
|
||||
:closable="closable"
|
||||
:disabled="disabled"
|
||||
@close="handleCloseClick(index)"
|
||||
>
|
||||
{{ tag }}
|
||||
</n-tag>
|
||||
<n-input
|
||||
v-if="inputVisible"
|
||||
ref="tagInput"
|
||||
v-model:value="inputValue"
|
||||
:force-focus="inputForceFocused"
|
||||
:unstable-theme="mergedTheme.peers.Input"
|
||||
:unstable-theme-overrides="mergedTheme.overrides.Input"
|
||||
:style="inputStyle"
|
||||
:size="inputSize"
|
||||
placeholder=""
|
||||
@keyup.enter="handleInputConfirm"
|
||||
@blur="handleInputBlur"
|
||||
/>
|
||||
<n-button
|
||||
v-else
|
||||
dashed
|
||||
:unstable-theme="mergedTheme.peers.Button"
|
||||
:unstable-theme-overrides="mergedTheme.overrides.Button"
|
||||
:size="inputSize"
|
||||
@click="handleAddClick"
|
||||
>
|
||||
<template #icon>
|
||||
<n-base-icon>
|
||||
<add-icon />
|
||||
</n-base-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import commonProps from '../../tag/src/common-props'
|
||||
import { AddIcon } from '../../_base/icons'
|
||||
import { NButton } from '../../button'
|
||||
import { NTag } from '../../tag'
|
||||
import { NBaseIcon } from '../../_base'
|
||||
import { useTheme, useFormItem, useLocale } from '../../_mixins'
|
||||
import { warn, call } from '../../_utils'
|
||||
import { dynamicTagsLight } from '../styles'
|
||||
import style from './styles/index.cssr.js'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DynamicTags',
|
||||
components: {
|
||||
NTag,
|
||||
NButton,
|
||||
NBaseIcon,
|
||||
AddIcon
|
||||
},
|
||||
props: {
|
||||
...useTheme.props,
|
||||
...commonProps,
|
||||
closable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
tagStyle: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
marginRight: '6px'
|
||||
}
|
||||
}
|
||||
},
|
||||
inputStyle: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
width: '64px'
|
||||
}
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:value': {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
// deprecated
|
||||
onChange: {
|
||||
validator () {
|
||||
if (__DEV__) {
|
||||
warn(
|
||||
'dynamic-tags',
|
||||
'`on-change` is deprecated, please use `on-update:value` instead.'
|
||||
)
|
||||
}
|
||||
return true
|
||||
},
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
const themeRef = useTheme(
|
||||
'DynamicTags',
|
||||
'DynamicTags',
|
||||
style,
|
||||
dynamicTagsLight,
|
||||
props
|
||||
)
|
||||
return {
|
||||
...useLocale('DynamicTags'),
|
||||
inputValue: ref(''),
|
||||
inputVisible: ref(false),
|
||||
inputForceFocused: ref(true),
|
||||
mergedTheme: themeRef,
|
||||
...useFormItem(props)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
localizedAdd () {
|
||||
return this.locale.add
|
||||
},
|
||||
inputSize () {
|
||||
const sizes = ['small', 'medium', 'large']
|
||||
const tagSizeIndex = sizes.findIndex((size) => size === this.size)
|
||||
const inputSizeIndex = tagSizeIndex - 1 > 0 ? tagSizeIndex - 1 : 0
|
||||
return sizes[inputSizeIndex]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doChange (value) {
|
||||
const {
|
||||
onChange,
|
||||
'onUpdate:value': onUpdateValue,
|
||||
nTriggerFormInput,
|
||||
nTriggerFormChange
|
||||
} = this
|
||||
if (onChange) call(onChange, value)
|
||||
if (onUpdateValue) call(onUpdateValue, value)
|
||||
nTriggerFormInput()
|
||||
nTriggerFormChange()
|
||||
},
|
||||
handleCloseClick (index) {
|
||||
const tags = this.value.slice(0)
|
||||
tags.splice(index, 1)
|
||||
this.doChange(tags)
|
||||
},
|
||||
handleInputConfirm () {
|
||||
if (this.inputValue) {
|
||||
const tags = this.value.slice(0)
|
||||
tags.push(this.inputValue)
|
||||
this.doChange(tags)
|
||||
}
|
||||
this.inputVisible = false
|
||||
this.inputForceFocused = true
|
||||
this.inputValue = ''
|
||||
},
|
||||
handleInputBlur () {
|
||||
this.handleInputConfirm()
|
||||
},
|
||||
handleAddClick () {
|
||||
this.inputVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tagInput.focus()
|
||||
this.inputForceFocused = false
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
1
src/dynamic-tags/src/interface.ts
Normal file
1
src/dynamic-tags/src/interface.ts
Normal file
@ -0,0 +1 @@
|
||||
export type OnUpdateValue = (value: string[]) => void
|
@ -2,8 +2,9 @@ import { tagDark } from '../../tag/styles'
|
||||
import { inputDark } from '../../input/styles'
|
||||
import { buttonDark } from '../../button/styles'
|
||||
import { commonDark } from '../../_styles/new-common'
|
||||
import type { DynamicTagsTheme } from './light'
|
||||
|
||||
export default {
|
||||
const dynamicTagsDark: DynamicTagsTheme = {
|
||||
name: 'DynamicTags',
|
||||
common: commonDark,
|
||||
peers: {
|
||||
@ -12,3 +13,5 @@ export default {
|
||||
Tag: tagDark
|
||||
}
|
||||
}
|
||||
|
||||
export default dynamicTagsDark
|
@ -1,2 +0,0 @@
|
||||
export { default as dynamicTagsDark } from './dark.js'
|
||||
export { default as dynamicTagsLight } from './light.js'
|
3
src/dynamic-tags/styles/index.ts
Normal file
3
src/dynamic-tags/styles/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { default as dynamicTagsDark } from './dark'
|
||||
export { default as dynamicTagsLight } from './light'
|
||||
export type { DynamicTagsTheme, DynamicTagsThemeVars } from './light'
|
@ -2,8 +2,9 @@ import { tagLight } from '../../tag/styles'
|
||||
import { inputLight } from '../../input/styles'
|
||||
import { buttonLight } from '../../button/styles'
|
||||
import { commonLight } from '../../_styles/new-common'
|
||||
import { createTheme } from '../../_mixins'
|
||||
|
||||
export default {
|
||||
const dynamicTagsLight = createTheme({
|
||||
name: 'DynamicTags',
|
||||
common: commonLight,
|
||||
peers: {
|
||||
@ -11,4 +12,9 @@ export default {
|
||||
Button: buttonLight,
|
||||
Tag: tagLight
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default dynamicTagsLight
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface DynamicTagsThemeVars {}
|
||||
export type DynamicTagsTheme = typeof dynamicTagsLight
|
@ -1,4 +1,4 @@
|
||||
export type Size = 'small' | 'medium' | 'large'
|
||||
export type Size = 'tiny' | 'small' | 'medium' | 'large'
|
||||
|
||||
// null is for clearable
|
||||
export type OnUpdateValue = <
|
||||
|
Loading…
Reference in New Issue
Block a user