mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-01-30 12:52:43 +08:00
refactor(dynamic-input): ts
This commit is contained in:
parent
a1b7a6c207
commit
2a9375b88a
@ -1,2 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
export { default as NDynamicInput } from './src/DynamicInput.vue'
|
2
src/dynamic-input/index.ts
Normal file
2
src/dynamic-input/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
/* istanbul ignore file */
|
||||
export { default as NDynamicInput } from './src/DynamicInput'
|
353
src/dynamic-input/src/DynamicInput.tsx
Normal file
353
src/dynamic-input/src/DynamicInput.tsx
Normal file
@ -0,0 +1,353 @@
|
||||
import {
|
||||
h,
|
||||
ref,
|
||||
toRef,
|
||||
isProxy,
|
||||
toRaw,
|
||||
computed,
|
||||
defineComponent,
|
||||
renderSlot,
|
||||
PropType,
|
||||
inject,
|
||||
CSSProperties,
|
||||
provide,
|
||||
reactive,
|
||||
VNode
|
||||
} from 'vue'
|
||||
import { useMergedState } from 'vooks'
|
||||
import { createId } from 'seemly'
|
||||
import { RemoveIcon, AddIcon } from '../../_base/icons'
|
||||
import { NBaseIcon } from '../../_base'
|
||||
import { NButton, NButtonGroup } from '../../button'
|
||||
import { useTheme, useLocale } from '../../_mixins'
|
||||
import type { ThemeProps } from '../../_mixins'
|
||||
import { warn, call, MaybeArray } from '../../_utils'
|
||||
import { dynamicInputLight } from '../styles'
|
||||
import type { DynamicInputTheme } from '../styles'
|
||||
import NDynamicInputInputPreset from './InputPreset'
|
||||
import NDynamicInputPairPreset from './PairPreset'
|
||||
import style from './styles/index.cssr'
|
||||
import { OnUpdateValue } from '../../select'
|
||||
import { DynamicInputInjection } from './interface'
|
||||
|
||||
const globalDataKeyMap = new WeakMap()
|
||||
|
||||
interface FormItemInjection {
|
||||
path?: string
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DynamicInput',
|
||||
props: {
|
||||
...(useTheme.props as ThemeProps<DynamicInputTheme>),
|
||||
max: Number,
|
||||
min: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
value: Array as PropType<any[]>,
|
||||
// TODO: make it robust for different types
|
||||
defaultValue: {
|
||||
type: Array as PropType<any[]>,
|
||||
default: () => []
|
||||
},
|
||||
preset: {
|
||||
type: String as PropType<'input' | 'pair'>,
|
||||
default: 'input'
|
||||
},
|
||||
keyField: String,
|
||||
// for preset pair
|
||||
keyPlaceholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
valuePlaceholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// for preset input
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
onCreate: Function as PropType<(index: number) => any>,
|
||||
onRemove: Function as PropType<(index: number) => void>,
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:value': [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
|
||||
onUpdateValue: [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
|
||||
// deprecated
|
||||
onClear: {
|
||||
type: Function as PropType<() => void>,
|
||||
validator: () => {
|
||||
warn(
|
||||
'dynamic-input',
|
||||
'`on-clear` is deprecated, it is out of usage anymore.'
|
||||
)
|
||||
return true
|
||||
},
|
||||
default: undefined
|
||||
},
|
||||
onInput: {
|
||||
type: Function as PropType<MaybeArray<OnUpdateValue>>,
|
||||
validator: () => {
|
||||
if (__DEV__) {
|
||||
warn(
|
||||
'dynamic-input',
|
||||
'`on-input` is deprecated, please use `on-update:value` instead.'
|
||||
)
|
||||
}
|
||||
return true
|
||||
},
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup (props, { slots }) {
|
||||
const NFormItem = inject<FormItemInjection | null>('NFormItem', null)
|
||||
const uncontrolledValueRef = ref(props.defaultValue)
|
||||
const controlledValueRef = toRef(props, 'value')
|
||||
const mergedValueRef = useMergedState(
|
||||
controlledValueRef,
|
||||
uncontrolledValueRef
|
||||
)
|
||||
const themeRef = useTheme(
|
||||
'DynamicInput',
|
||||
'DynamicInput',
|
||||
style,
|
||||
dynamicInputLight,
|
||||
props
|
||||
)
|
||||
const insertionDisabledRef = computed(() => {
|
||||
const { value: mergedValue } = mergedValueRef
|
||||
if (Array.isArray(mergedValue)) {
|
||||
const { max } = props
|
||||
return max !== undefined && mergedValue.length >= max
|
||||
}
|
||||
return false
|
||||
})
|
||||
const removeDisabledRef = computed(() => {
|
||||
const { value: mergedValue } = mergedValueRef
|
||||
if (Array.isArray(mergedValue)) return mergedValue.length <= props.min
|
||||
return true
|
||||
})
|
||||
function doUpdateValue (value: any[]): void {
|
||||
const { onInput, 'onUpdate:value': _onUpdateValue, onUpdateValue } = props
|
||||
if (onInput) call(onInput, value)
|
||||
if (_onUpdateValue) call(_onUpdateValue, value)
|
||||
if (onUpdateValue) call(onUpdateValue, value)
|
||||
uncontrolledValueRef.value = value
|
||||
}
|
||||
function ensureKey (value: any, index: number): string | number {
|
||||
if (value === undefined || value === null) return index
|
||||
if (typeof value !== 'object') return index
|
||||
const rawValue = isProxy(value) ? toRaw(value) : value
|
||||
let key = globalDataKeyMap.get(rawValue)
|
||||
if (key === undefined) {
|
||||
globalDataKeyMap.set(rawValue, (key = createId()))
|
||||
}
|
||||
return key
|
||||
}
|
||||
function handleValueChange (index: number, value: any): void {
|
||||
const { value: mergedValue } = mergedValueRef
|
||||
const newValue = Array.from(mergedValue ?? [])
|
||||
const originalItem = newValue[index]
|
||||
newValue[index] = value
|
||||
// update dataKeyMap
|
||||
if (
|
||||
originalItem &&
|
||||
value &&
|
||||
typeof originalItem === 'object' &&
|
||||
typeof value === 'object'
|
||||
) {
|
||||
const rawOriginal = isProxy(originalItem)
|
||||
? toRaw(originalItem)
|
||||
: originalItem
|
||||
const rawNew = isProxy(value) ? toRaw(value) : value
|
||||
// inherit key is value position is not change
|
||||
const originalKey = globalDataKeyMap.get(rawOriginal)
|
||||
if (originalKey !== undefined) {
|
||||
globalDataKeyMap.set(rawNew, originalKey)
|
||||
}
|
||||
}
|
||||
doUpdateValue(newValue)
|
||||
}
|
||||
function handleCreateClick (): void {
|
||||
createItem(0)
|
||||
}
|
||||
function createItem (index: number): void {
|
||||
const { value: mergedValue } = mergedValueRef
|
||||
const { onCreate } = props
|
||||
const newValue = Array.from(mergedValue ?? [])
|
||||
if (onCreate) {
|
||||
newValue.splice(index + 1, 0, onCreate(index + 1))
|
||||
doUpdateValue(newValue)
|
||||
} else if (slots.default) {
|
||||
newValue.splice(index + 1, 0, null)
|
||||
doUpdateValue(newValue)
|
||||
} else {
|
||||
switch (props.preset) {
|
||||
case 'input':
|
||||
newValue.splice(index + 1, 0, '')
|
||||
doUpdateValue(newValue)
|
||||
break
|
||||
case 'pair':
|
||||
newValue.splice(index + 1, 0, { key: '', value: '' })
|
||||
doUpdateValue(newValue)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
function remove (index: number): void {
|
||||
const { value: mergedValue } = mergedValueRef
|
||||
if (!Array.isArray(mergedValue)) return
|
||||
const { min } = props
|
||||
if (mergedValue.length <= min) return
|
||||
const newValue = Array.from(mergedValue)
|
||||
newValue.splice(index, 1)
|
||||
doUpdateValue(newValue)
|
||||
const { onRemove } = props
|
||||
if (onRemove) onRemove(index)
|
||||
}
|
||||
provide<DynamicInputInjection>(
|
||||
'NDynamicInput',
|
||||
reactive({
|
||||
mergedTheme: themeRef,
|
||||
keyPlaceholder: toRef(props, 'keyPlaceholder'),
|
||||
valuePlaceholder: toRef(props, 'valuePlaceholder'),
|
||||
placeholder: toRef(props, 'placeholder')
|
||||
})
|
||||
)
|
||||
return {
|
||||
...useLocale('DynamicInput'),
|
||||
NFormItem,
|
||||
uncontrolledValue: uncontrolledValueRef,
|
||||
mergedValue: mergedValueRef,
|
||||
insertionDisabled: insertionDisabledRef,
|
||||
removeDisabled: removeDisabledRef,
|
||||
handleCreateClick,
|
||||
ensureKey,
|
||||
handleValueChange,
|
||||
remove,
|
||||
createItem,
|
||||
mergedTheme: themeRef,
|
||||
cssVars: computed(() => {
|
||||
const {
|
||||
self: { actionMargin }
|
||||
} = themeRef.value
|
||||
return {
|
||||
'--action-margin': actionMargin
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const {
|
||||
mergedValue,
|
||||
locale,
|
||||
mergedTheme,
|
||||
keyField,
|
||||
$slots,
|
||||
preset,
|
||||
NFormItem,
|
||||
ensureKey,
|
||||
handleValueChange,
|
||||
remove,
|
||||
createItem
|
||||
} = this
|
||||
return (
|
||||
<div class="n-dynamic-input" style={this.cssVars as CSSProperties}>
|
||||
{!Array.isArray(mergedValue) || mergedValue.length === 0 ? (
|
||||
<NButton
|
||||
block
|
||||
ghost
|
||||
dashed
|
||||
unstableTheme={mergedTheme.peers.Button}
|
||||
unstableThemeOverrides={mergedTheme.overrides.Button}
|
||||
onClick={this.handleCreateClick}
|
||||
>
|
||||
{{
|
||||
default: () => locale.create,
|
||||
icon: () => (
|
||||
<NBaseIcon>{{ default: () => <AddIcon /> }}</NBaseIcon>
|
||||
)
|
||||
}}
|
||||
</NButton>
|
||||
) : (
|
||||
mergedValue.map((_, index) => (
|
||||
<div
|
||||
key={keyField ? _[keyField] : ensureKey(_, index)}
|
||||
data-key={keyField ? _[keyField] : ensureKey(_, index)}
|
||||
class="n-dynamic-input-item"
|
||||
>
|
||||
{$slots.default ? (
|
||||
renderSlot($slots, 'default', {
|
||||
value: mergedValue[index],
|
||||
index
|
||||
})
|
||||
) : preset === 'input' ? (
|
||||
<NDynamicInputInputPreset
|
||||
value={mergedValue[index]}
|
||||
parentPath={NFormItem ? NFormItem.path : undefined}
|
||||
path={
|
||||
NFormItem?.path ? `${NFormItem.path}[${index}]` : undefined
|
||||
}
|
||||
onUpdateValue={(v) => handleValueChange(index, v)}
|
||||
/>
|
||||
) : preset === 'pair' ? (
|
||||
<NDynamicInputPairPreset
|
||||
value={mergedValue[index]}
|
||||
parentPath={NFormItem ? NFormItem.path : undefined}
|
||||
path={
|
||||
NFormItem?.path ? `${NFormItem.path}[${index}]` : undefined
|
||||
}
|
||||
onUpdateValue={(v) => handleValueChange(index, v)}
|
||||
/>
|
||||
) : null}
|
||||
<div class="n-dynamic-input-item__action">
|
||||
<NButtonGroup>
|
||||
{{
|
||||
default: () =>
|
||||
[
|
||||
!this.removeDisabled ? (
|
||||
<NButton
|
||||
unstableTheme={mergedTheme.peers.Button}
|
||||
unstableThemeOverrides={
|
||||
mergedTheme.overrides.Button
|
||||
}
|
||||
circle
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
{{
|
||||
icon: () => (
|
||||
<NBaseIcon>
|
||||
{{ default: () => <RemoveIcon /> }}
|
||||
</NBaseIcon>
|
||||
)
|
||||
}}
|
||||
</NButton>
|
||||
) : null,
|
||||
<NButton
|
||||
disabled={this.insertionDisabled}
|
||||
circle
|
||||
unstableTheme={mergedTheme.peers.Button}
|
||||
unstableThemeOverrides={mergedTheme.overrides.Button}
|
||||
onClick={() => createItem(index)}
|
||||
>
|
||||
{{
|
||||
icon: () => (
|
||||
<NBaseIcon>
|
||||
{{ default: () => <AddIcon /> }}
|
||||
</NBaseIcon>
|
||||
)
|
||||
}}
|
||||
</NButton>
|
||||
] as VNode[]
|
||||
}}
|
||||
</NButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
@ -1,311 +0,0 @@
|
||||
<template>
|
||||
<div class="n-dynamic-input" :style="cssVars">
|
||||
<n-button
|
||||
v-if="!mergedValue || mergedValue.length === 0"
|
||||
block
|
||||
ghost
|
||||
dashed
|
||||
:unstable-theme="mergedTheme.peers.Button"
|
||||
:unstable-theme-overrides="mergedTheme.overrides.Button"
|
||||
@click="handleCreateClick"
|
||||
>
|
||||
<template #icon>
|
||||
<n-base-icon>
|
||||
<add-icon />
|
||||
</n-base-icon>
|
||||
</template>
|
||||
{{ locale.create }}
|
||||
</n-button>
|
||||
<div
|
||||
v-for="(_, index) in mergedValue"
|
||||
v-else
|
||||
:key="keyField ? _[keyField] : ensureKey(_, index)"
|
||||
:data-key="keyField ? _[keyField] : ensureKey(_, index)"
|
||||
class="n-dynamic-input-item"
|
||||
>
|
||||
<slot v-if="$slots.default" :value="mergedValue[index]" :index="index" />
|
||||
<n-dynamic-input-input-preset
|
||||
v-else-if="preset === 'input'"
|
||||
:value="mergedValue[index]"
|
||||
:parent-path="NFormItem && NFormItem.path"
|
||||
:path="NFormItem && NFormItem.path + '[' + index + ']'"
|
||||
@update:value="handleValueChange(index, $event)"
|
||||
/>
|
||||
<n-dynamic-input-pair-preset
|
||||
v-else-if="preset === 'pair'"
|
||||
:value="mergedValue[index]"
|
||||
:parent-path="NFormItem && NFormItem.path"
|
||||
:path="NFormItem && NFormItem.path + '[' + index + ']'"
|
||||
@update:value="handleValueChange(index, $event)"
|
||||
/>
|
||||
<div class="n-dynamic-input-item__action">
|
||||
<n-button-group>
|
||||
<n-button
|
||||
v-if="!removeDisabled"
|
||||
:unstable-theme="mergedTheme.peers.Button"
|
||||
:unstable-theme-overrides="mergedTheme.overrides.Button"
|
||||
circle
|
||||
@click="remove(index)"
|
||||
>
|
||||
<template #icon>
|
||||
<n-base-icon>
|
||||
<remove-icon />
|
||||
</n-base-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
<n-button
|
||||
:disabled="insertionDisabled"
|
||||
circle
|
||||
:unstable-theme="mergedTheme.peers.Button"
|
||||
:unstable-theme-overrides="mergedTheme.overrides.Button"
|
||||
@click="createItem(index)"
|
||||
>
|
||||
<template #icon>
|
||||
<n-base-icon>
|
||||
<add-icon />
|
||||
</n-base-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
</n-button-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, toRef, isProxy, toRaw, computed, defineComponent } from 'vue'
|
||||
import { useMergedState } from 'vooks'
|
||||
import { createId } from 'seemly'
|
||||
import { RemoveIcon, AddIcon } from '../../_base/icons'
|
||||
import { NBaseIcon } from '../../_base'
|
||||
import { NButton, NButtonGroup } from '../../button'
|
||||
import { useFormItem, useTheme, useLocale } from '../../_mixins'
|
||||
import { warn, call } from '../../_utils'
|
||||
import { dynamicInputLight } from '../styles'
|
||||
import NDynamicInputInputPreset from './InputPreset.vue'
|
||||
import NDynamicInputPairPreset from './PairPreset.vue'
|
||||
import style from './styles/index.cssr.js'
|
||||
|
||||
const globalDataKeyMap = new WeakMap()
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DynamicInput',
|
||||
components: {
|
||||
NDynamicInputInputPreset,
|
||||
NDynamicInputPairPreset,
|
||||
NButtonGroup,
|
||||
NButton,
|
||||
NBaseIcon,
|
||||
AddIcon,
|
||||
RemoveIcon
|
||||
},
|
||||
provide () {
|
||||
return {
|
||||
NDynamicInput: this
|
||||
}
|
||||
},
|
||||
props: {
|
||||
...useTheme.props,
|
||||
max: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
},
|
||||
min: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: undefined
|
||||
},
|
||||
// TODO: make it robust for different types
|
||||
defaultValue: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
preset: {
|
||||
validator (value) {
|
||||
return ['input', 'pair'].includes(value)
|
||||
},
|
||||
default: 'input'
|
||||
},
|
||||
keyField: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
// for preset pair
|
||||
keyPlaceholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
valuePlaceholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// for preset input
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
onCreate: {
|
||||
type: Function,
|
||||
default: undefined
|
||||
},
|
||||
onRemove: {
|
||||
type: Function,
|
||||
default: undefined
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:value': {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
// deprecated
|
||||
onClear: {
|
||||
validator () {
|
||||
warn(
|
||||
'dynamic-input',
|
||||
'`on-clear` is deprecated, it is out of usage anymore.'
|
||||
)
|
||||
return true
|
||||
},
|
||||
default: undefined
|
||||
},
|
||||
onInput: {
|
||||
validator () {
|
||||
if (__DEV__) {
|
||||
warn(
|
||||
'dynamic-input',
|
||||
'`on-input` is deprecated, please use `on-update:value` instead.'
|
||||
)
|
||||
}
|
||||
return true
|
||||
},
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
const uncontrolledValueRef = ref(props.defaultValue)
|
||||
const controlledValueRef = toRef(props, 'value')
|
||||
const themeRef = useTheme(
|
||||
'DynamicInput',
|
||||
'DynamicInput',
|
||||
style,
|
||||
dynamicInputLight,
|
||||
props
|
||||
)
|
||||
return {
|
||||
...useLocale('DynamicInput'),
|
||||
uncontrolledValue: uncontrolledValueRef,
|
||||
mergedValue: useMergedState(controlledValueRef, uncontrolledValueRef),
|
||||
dataKeyMap: globalDataKeyMap,
|
||||
...useFormItem(props),
|
||||
mergedTheme: themeRef,
|
||||
cssVars: computed(() => {
|
||||
const {
|
||||
self: { actionMargin }
|
||||
} = themeRef.value
|
||||
return {
|
||||
'--action-margin': actionMargin
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
insertionDisabled () {
|
||||
const { mergedValue } = this
|
||||
if (Array.isArray(mergedValue)) {
|
||||
const { max } = this
|
||||
return max !== undefined && mergedValue.length >= max
|
||||
}
|
||||
return false
|
||||
},
|
||||
removeDisabled () {
|
||||
const { mergedValue } = this
|
||||
if (Array.isArray(mergedValue)) return mergedValue.length <= this.min
|
||||
return true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doUpdateValue (value) {
|
||||
const { onInput, 'onUpdate:value': onUpdateValue } = this
|
||||
if (onInput) call(onInput, value)
|
||||
if (onUpdateValue) call(onUpdateValue, value)
|
||||
this.uncontrolledValue = value
|
||||
},
|
||||
ensureKey (value, index) {
|
||||
if (value === undefined || value === null) return index
|
||||
if (typeof value !== 'object') return index
|
||||
const { dataKeyMap } = this
|
||||
const rawValue = isProxy(value) ? toRaw(value) : value
|
||||
let key = dataKeyMap.get(rawValue)
|
||||
if (key === undefined) {
|
||||
dataKeyMap.set(rawValue, (key = createId()))
|
||||
}
|
||||
return key
|
||||
},
|
||||
handleValueChange (index, value) {
|
||||
const { mergedValue } = this
|
||||
const newValue = Array.from(mergedValue ?? [])
|
||||
const originalItem = newValue[index]
|
||||
newValue[index] = value
|
||||
const { dataKeyMap } = this
|
||||
// update dataKeyMap
|
||||
if (
|
||||
originalItem &&
|
||||
value &&
|
||||
typeof originalItem === 'object' &&
|
||||
typeof value === 'object'
|
||||
) {
|
||||
const rawOriginal = isProxy(originalItem)
|
||||
? toRaw(originalItem)
|
||||
: originalItem
|
||||
const rawNew = isProxy(value) ? toRaw(value) : value
|
||||
// inherit key is value position is not change
|
||||
const originalKey = dataKeyMap.get(rawOriginal)
|
||||
if (originalKey !== undefined) {
|
||||
dataKeyMap.set(rawNew, originalKey)
|
||||
}
|
||||
}
|
||||
|
||||
this.doUpdateValue(newValue)
|
||||
},
|
||||
handleCreateClick () {
|
||||
this.createItem(0)
|
||||
},
|
||||
createItem (index) {
|
||||
const { onCreate, mergedValue } = this
|
||||
const newValue = Array.from(mergedValue ?? [])
|
||||
if (onCreate) {
|
||||
newValue.splice(index + 1, 0, onCreate(index + 1))
|
||||
this.doUpdateValue(newValue)
|
||||
} else if (this.$slots.default) {
|
||||
newValue.splice(index + 1, 0, null)
|
||||
this.doUpdateValue(newValue)
|
||||
} else {
|
||||
switch (this.preset) {
|
||||
case 'input':
|
||||
newValue.splice(index + 1, 0, '')
|
||||
this.doUpdateValue(newValue)
|
||||
break
|
||||
case 'pair':
|
||||
newValue.splice(index + 1, 0, { key: '', value: '' })
|
||||
this.doUpdateValue(newValue)
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
remove (index) {
|
||||
const { mergedValue } = this
|
||||
if (!Array.isArray(mergedValue)) return
|
||||
const { min } = this
|
||||
if (mergedValue.length <= min) return
|
||||
const newValue = Array.from(mergedValue)
|
||||
newValue.splice(index, 1)
|
||||
this.doUpdateValue(newValue)
|
||||
const { onRemove } = this
|
||||
if (onRemove) onRemove(index)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
42
src/dynamic-input/src/InputPreset.tsx
Normal file
42
src/dynamic-input/src/InputPreset.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { h, defineComponent, inject, PropType } from 'vue'
|
||||
import { NInput } from '../../input'
|
||||
import { DynamicInputInjection } from './interface'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DynamicInputInputPreset',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
parentPath: String,
|
||||
path: String,
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
onUpdateValue: {
|
||||
type: Function as PropType<(value: string) => void>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup () {
|
||||
const NDynamicInput = inject<DynamicInputInjection>(
|
||||
'NDynamicInput'
|
||||
) as DynamicInputInjection
|
||||
return {
|
||||
NDynamicInput
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { NDynamicInput, value, onUpdateValue } = this
|
||||
return (
|
||||
<div class="n-dynamic-input-preset-input">
|
||||
<NInput
|
||||
unstableTheme={NDynamicInput.mergedTheme.peers.Input}
|
||||
unstableTheme-overrides={NDynamicInput.mergedTheme.overrides.Input}
|
||||
value={value}
|
||||
placeholder={NDynamicInput.placeholder}
|
||||
onUpdateValue={onUpdateValue}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
@ -1,52 +0,0 @@
|
||||
<template>
|
||||
<div class="n-dynamic-input-preset-input">
|
||||
<n-input
|
||||
:unstable-theme="NDynamicInput.mergedTheme.peers.Input"
|
||||
:unstable-theme-overrides="NDynamicInput.mergedTheme.overrides.Input"
|
||||
:value="value"
|
||||
:placeholder="NDynamicInput.placeholder"
|
||||
@update:value="handleInput"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
import { NInput } from '../../input'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DynamicInputInputPreset',
|
||||
components: {
|
||||
NInput
|
||||
},
|
||||
inject: {
|
||||
NDynamicInput: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
parentPath: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:value': {
|
||||
type: Function,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleInput (value) {
|
||||
this['onUpdate:value'](value)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
67
src/dynamic-input/src/PairPreset.tsx
Normal file
67
src/dynamic-input/src/PairPreset.tsx
Normal file
@ -0,0 +1,67 @@
|
||||
import { defineComponent, h, inject, PropType } from 'vue'
|
||||
import { NInput } from '../../input'
|
||||
import { DynamicInputInjection } from './interface'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DynamicInputPairPreset',
|
||||
props: {
|
||||
value: {
|
||||
type: Object as PropType<{ key: string, value: string }>,
|
||||
default: () => ({
|
||||
key: '',
|
||||
value: ''
|
||||
})
|
||||
},
|
||||
parentPath: String,
|
||||
path: String,
|
||||
onUpdateValue: {
|
||||
type: Function as PropType<
|
||||
(data: { key: string, value: string }) => void
|
||||
>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
const NDynamicInput = inject<DynamicInputInjection>(
|
||||
'NDynamicInput'
|
||||
) as DynamicInputInjection
|
||||
return {
|
||||
NDynamicInput,
|
||||
handleKeyInput (key: string) {
|
||||
props.onUpdateValue({
|
||||
key,
|
||||
value: props.value.value
|
||||
})
|
||||
},
|
||||
handleValueInput (value: string) {
|
||||
props.onUpdateValue({
|
||||
key: props.value.key,
|
||||
value
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { NDynamicInput, value } = this
|
||||
return (
|
||||
<div class="n-dynamic-input-preset-pair">
|
||||
<NInput
|
||||
unstableTheme={NDynamicInput.mergedTheme.peers.Input}
|
||||
unstableTheme-overrides={NDynamicInput.mergedTheme.overrides.Input}
|
||||
value={value.key}
|
||||
class="n-dynamic-input-pair-input"
|
||||
placeholder={NDynamicInput.keyPlaceholder}
|
||||
onUpdateValue={this.handleKeyInput}
|
||||
/>
|
||||
<NInput
|
||||
unstableTheme={NDynamicInput.mergedTheme.peers.Input}
|
||||
unstableTheme-overrides={NDynamicInput.mergedTheme.overrides.Input}
|
||||
value={value.value}
|
||||
class="n-dynamic-input-pair-input"
|
||||
placeholder={NDynamicInput.valuePlaceholder}
|
||||
onUpdateValue={this.handleValueInput}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
@ -1,73 +0,0 @@
|
||||
<template>
|
||||
<div class="n-dynamic-input-preset-pair">
|
||||
<n-input
|
||||
:unstable-theme="NDynamicInput.mergedTheme.peers.Input"
|
||||
:unstable-theme-overrides="NDynamicInput.mergedTheme.overrides.Input"
|
||||
:value="value.key"
|
||||
class="n-dynamic-input-pair-input"
|
||||
:placeholder="NDynamicInput.keyPlaceholder"
|
||||
@update:value="handleKeyInput"
|
||||
/>
|
||||
<n-input
|
||||
:unstable-theme="NDynamicInput.mergedTheme.peers.Input"
|
||||
:unstable-theme-overrides="NDynamicInput.mergedTheme.overrides.Input"
|
||||
:value="value.value"
|
||||
class="n-dynamic-input-pair-input"
|
||||
:placeholder="NDynamicInput.valuePlaceholder"
|
||||
@update:value="handleValueInput"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
import { NInput } from '../../input'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DynamicInputPairPreset',
|
||||
components: {
|
||||
NInput
|
||||
},
|
||||
inject: {
|
||||
NDynamicInput: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
key: null,
|
||||
value: null
|
||||
})
|
||||
},
|
||||
parentPath: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:value': {
|
||||
type: Function,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleKeyInput (key) {
|
||||
this['onUpdate:value']({
|
||||
key,
|
||||
value: this.value.value
|
||||
})
|
||||
},
|
||||
handleValueInput (value) {
|
||||
this['onUpdate:value']({
|
||||
key: this.value.key,
|
||||
value
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
11
src/dynamic-input/src/interface.ts
Normal file
11
src/dynamic-input/src/interface.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import type { MergedTheme } from '../../_mixins'
|
||||
import type { DynamicInputTheme } from '../styles'
|
||||
|
||||
export interface DynamicInputInjection {
|
||||
mergedTheme: MergedTheme<DynamicInputTheme>
|
||||
keyPlaceholder?: string
|
||||
valuePlaceholder?: string
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
export type OnUpdateValue = <T>(value: T[]) => void
|
@ -2,8 +2,9 @@ import { inputDark } from '../../input/styles'
|
||||
import { buttonDark } from '../../button/styles'
|
||||
import { commonDark } from '../../_styles/new-common'
|
||||
import commonVariables from './_common'
|
||||
import { DynamicInputTheme } from './light'
|
||||
|
||||
export default {
|
||||
const dynamicInputDark: DynamicInputTheme = {
|
||||
name: 'DynamicInput',
|
||||
common: commonDark,
|
||||
peers: {
|
||||
@ -11,8 +12,8 @@ export default {
|
||||
Button: buttonDark
|
||||
},
|
||||
self () {
|
||||
return {
|
||||
...commonVariables
|
||||
}
|
||||
return commonVariables
|
||||
}
|
||||
}
|
||||
|
||||
export default dynamicInputDark
|
@ -1,2 +0,0 @@
|
||||
export { default as dynamicInputDark } from './dark.js'
|
||||
export { default as dynamicInputLight } from './light.js'
|
3
src/dynamic-input/styles/index.ts
Normal file
3
src/dynamic-input/styles/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { default as dynamicInputDark } from './dark'
|
||||
export { default as dynamicInputLight } from './light'
|
||||
export type { DynamicInputTheme, DynamicInputThemeVars } from './light'
|
@ -2,17 +2,23 @@ import { inputLight } from '../../input/styles'
|
||||
import { buttonLight } from '../../button/styles'
|
||||
import { commonLight } from '../../_styles/new-common'
|
||||
import commonVariables from './_common'
|
||||
import { createTheme } from '../../_mixins'
|
||||
|
||||
export default {
|
||||
const self = () => {
|
||||
return commonVariables
|
||||
}
|
||||
|
||||
export type DynamicInputThemeVars = ReturnType<typeof self>
|
||||
|
||||
const dynamicInputLight = createTheme({
|
||||
name: 'DynamicInput',
|
||||
common: commonLight,
|
||||
peers: {
|
||||
Input: inputLight,
|
||||
Button: buttonLight
|
||||
},
|
||||
self () {
|
||||
return {
|
||||
...commonVariables
|
||||
}
|
||||
}
|
||||
}
|
||||
self
|
||||
})
|
||||
|
||||
export default dynamicInputLight
|
||||
export type DynamicInputTheme = typeof dynamicInputLight
|
Loading…
Reference in New Issue
Block a user