mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-01-06 12:17:13 +08:00
refactor(input): ts
This commit is contained in:
parent
e6568da5e4
commit
e505f6e1b9
@ -1,4 +1,4 @@
|
||||
import { h, defineComponent, PropType } from 'vue'
|
||||
import { h, defineComponent, PropType, renderSlot } from 'vue'
|
||||
import { useStyle } from '../../../_mixins'
|
||||
import { DismissCircleIcon } from '../../icons'
|
||||
import NBaseIcon from '../../icon'
|
||||
@ -43,11 +43,11 @@ export default defineComponent({
|
||||
onClick={this.onClear}
|
||||
onMousedown={this.handleMouseDown}
|
||||
>
|
||||
<DismissCircleIcon />
|
||||
{{ default: () => <DismissCircleIcon /> }}
|
||||
</NBaseIcon>
|
||||
) : (
|
||||
<div key="icon" class="n-base-clear__placeholder">
|
||||
<slot />
|
||||
{renderSlot(this.$slots, 'default')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ import createIconSwitchTransition from '../../../../_styles/transitions/icon-swi
|
||||
|
||||
// vars:
|
||||
// --bezier
|
||||
// --color
|
||||
// --size
|
||||
// --color-hover
|
||||
// --color-pressed
|
||||
// --clear-color
|
||||
// --clear-size
|
||||
// --clear-color-hover
|
||||
// --clear-color-pressed
|
||||
export default cB(
|
||||
'base-clear',
|
||||
{
|
||||
@ -22,15 +22,15 @@ export default cB(
|
||||
{
|
||||
fontSize: 'var(--size)',
|
||||
cursor: 'pointer',
|
||||
color: 'var(--color)',
|
||||
color: 'var(--clear-color)',
|
||||
transition: 'color .3s var(--bezier)'
|
||||
},
|
||||
[
|
||||
c('&:hover', {
|
||||
color: 'var(--color-hover)!important'
|
||||
color: 'var(--clear-color-hover)!important'
|
||||
}),
|
||||
c('&:active', {
|
||||
color: 'var(--color-pressed)!important'
|
||||
color: 'var(--clear-color-pressed)!important'
|
||||
})
|
||||
]
|
||||
),
|
||||
|
@ -14,7 +14,7 @@ export default defineComponent({
|
||||
},
|
||||
setup (props) {
|
||||
useStyle('BaseClose', style)
|
||||
return (
|
||||
return () => (
|
||||
<NBaseIcon
|
||||
class={[
|
||||
'n-base-close',
|
||||
@ -23,7 +23,7 @@ export default defineComponent({
|
||||
}
|
||||
]}
|
||||
>
|
||||
<CloseIcon />
|
||||
{{ default: () => <CloseIcon /> }}
|
||||
</NBaseIcon>
|
||||
)
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { computed, inject, provide, onBeforeUnmount } from 'vue'
|
||||
import { computed, inject, provide, onBeforeUnmount, ComputedRef } from 'vue'
|
||||
|
||||
type FormItemSize = 'small' | 'medium' | 'large'
|
||||
type AllowedSize = 'tiny' | 'small' | 'medium' | 'large' | 'huge'
|
||||
|
||||
interface FormItemInjection {
|
||||
size: string | undefined
|
||||
mergedSize: string
|
||||
mergedSize: FormItemSize
|
||||
restoreValidation: () => void
|
||||
handleContentBlur: () => void
|
||||
handleContentFocus: () => void
|
||||
@ -10,19 +12,27 @@ interface FormItemInjection {
|
||||
handleContentChange: () => void
|
||||
}
|
||||
|
||||
interface UseFormItemOptions {
|
||||
defaultSize?: string
|
||||
mergedSize?: (formItem: FormItemInjection | null) => string
|
||||
interface UseFormItemOptions<T> {
|
||||
defaultSize?: FormItemSize
|
||||
mergedSize?: (formItem: FormItemInjection | null) => T
|
||||
}
|
||||
|
||||
interface UseFormItemProps {
|
||||
size?: string
|
||||
interface UseFormItemProps<T> {
|
||||
size?: T
|
||||
}
|
||||
|
||||
export default function useFormItem (
|
||||
props: UseFormItemProps,
|
||||
{ defaultSize = 'medium', mergedSize }: UseFormItemOptions = {}
|
||||
) {
|
||||
interface UseFormItem<T> {
|
||||
mergedSize: ComputedRef<T>
|
||||
nTriggerFormBlur: () => void
|
||||
nTriggerFormChange: () => void
|
||||
nTriggerFormFocus: () => void
|
||||
nTriggerFormInput: () => void
|
||||
}
|
||||
|
||||
export default function useFormItem<T extends AllowedSize = FormItemSize> (
|
||||
props: UseFormItemProps<T>,
|
||||
{ defaultSize = 'medium', mergedSize }: UseFormItemOptions<T> = {}
|
||||
): UseFormItem<T> {
|
||||
const NFormItem = inject<FormItemInjection | null>('NFormItem', null)
|
||||
provide('NFormItem', null)
|
||||
const mergedSizeRef = computed(
|
||||
@ -34,10 +44,10 @@ export default function useFormItem (
|
||||
if (NFormItem) {
|
||||
const { mergedSize } = NFormItem
|
||||
if (mergedSize) {
|
||||
return mergedSize
|
||||
return (mergedSize as unknown) as T
|
||||
}
|
||||
}
|
||||
return defaultSize
|
||||
return (defaultSize as unknown) as T
|
||||
}
|
||||
)
|
||||
onBeforeUnmount(() => {
|
||||
|
@ -8,6 +8,7 @@ export {
|
||||
keysOf,
|
||||
render
|
||||
} from './vue'
|
||||
export type { MaybeArray } from './vue'
|
||||
export { warn, warnOnce } from './naive'
|
||||
export { formatLength } from './css'
|
||||
export { createKey } from './cssr'
|
||||
|
@ -1,5 +1,9 @@
|
||||
export function call<A extends any[]> (funcs: Function[] | Function, ...args: A): void {
|
||||
export function call<A extends any[]> (
|
||||
funcs: Function[] | Function,
|
||||
...args: A
|
||||
): void {
|
||||
if (Array.isArray(funcs)) funcs.forEach((func) => call(func, ...args))
|
||||
else return funcs(...args)
|
||||
}
|
||||
|
||||
export type MaybeArray<T> = T | T[]
|
||||
|
@ -6,3 +6,4 @@ export { flatten } from './flatten'
|
||||
export { call } from './call'
|
||||
export { keysOf } from './keysOf'
|
||||
export { render } from './render'
|
||||
export type { MaybeArray } from './call'
|
||||
|
@ -1,4 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
export { default as NInput } from './src/Input.vue'
|
||||
export { default as NInputGroup } from './src/InputGroup.vue'
|
||||
export { default as NInputGroupLabel } from './src/InputGroupLabel.vue'
|
4
src/input/index.ts
Normal file
4
src/input/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
/* istanbul ignore file */
|
||||
export { default as NInput } from './src/Input'
|
||||
export { default as NInputGroup } from './src/InputGroup'
|
||||
export { default as NInputGroupLabel } from './src/InputGroupLabel'
|
929
src/input/src/Input.tsx
Normal file
929
src/input/src/Input.tsx
Normal file
@ -0,0 +1,929 @@
|
||||
import {
|
||||
h,
|
||||
computed,
|
||||
defineComponent,
|
||||
nextTick,
|
||||
ref,
|
||||
toRef,
|
||||
watch,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
renderSlot,
|
||||
PropType,
|
||||
CSSProperties
|
||||
} from 'vue'
|
||||
import { useMergedState } from 'vooks'
|
||||
import NIconConfigProvider from '../../icon/src/IconConfigProvider'
|
||||
import { NBaseClear } from '../../_base'
|
||||
import { useTheme, useLocale, useFormItem, useConfig } from '../../_mixins'
|
||||
import type { ThemeProps } from '../../_mixins'
|
||||
import { call, createKey } from '../../_utils'
|
||||
import type { MaybeArray } from '../../_utils'
|
||||
import { inputLight } from '../styles'
|
||||
import type { InputTheme } from '../styles'
|
||||
import style from './styles/input.cssr'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Input',
|
||||
props: {
|
||||
...(useTheme.props as ThemeProps<InputTheme>),
|
||||
bordered: {
|
||||
type: Boolean,
|
||||
default: undefined
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text'
|
||||
},
|
||||
placeholder: {
|
||||
type: [Array, String] as PropType<undefined | string | [string, string]>,
|
||||
default: undefined
|
||||
},
|
||||
defaultValue: {
|
||||
type: [String, Array] as PropType<null | string | [string, string]>,
|
||||
default: null
|
||||
},
|
||||
value: {
|
||||
type: [String, Array] as PropType<
|
||||
null | undefined | string | [string, string]
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
size: {
|
||||
type: String as PropType<'small' | 'medium' | 'large' | undefined>,
|
||||
default: undefined
|
||||
},
|
||||
rows: {
|
||||
type: [Number, String] as PropType<number | string>,
|
||||
default: 3
|
||||
},
|
||||
round: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
minlength: {
|
||||
type: [String, Number] as PropType<number | string | undefined>,
|
||||
default: undefined
|
||||
},
|
||||
maxlength: {
|
||||
type: [String, Number] as PropType<number | string | undefined>,
|
||||
default: undefined
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
autosize: {
|
||||
type: [Boolean, Object] as PropType<
|
||||
false | { minRows?: number, maxRows?: number }
|
||||
>,
|
||||
default: false
|
||||
},
|
||||
showWordLimit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
pair: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
separator: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
readonly: {
|
||||
type: [String, Boolean],
|
||||
default: false
|
||||
},
|
||||
forceFocus: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
passivelyActivated: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
stateful: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
autofocus: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
onInput: {
|
||||
type: [Function, Array] as PropType<
|
||||
| undefined
|
||||
| MaybeArray<(value: string) => void>
|
||||
| MaybeArray<(value: [string, string]) => void>
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
onFocus: {
|
||||
type: [Function, Array] as PropType<
|
||||
undefined | MaybeArray<(e: FocusEvent) => void>
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
onBlur: {
|
||||
type: [Function, Array] as PropType<
|
||||
undefined | MaybeArray<(e: FocusEvent) => void>
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
onClick: {
|
||||
type: [Function, Array] as PropType<
|
||||
undefined | MaybeArray<(e: MouseEvent) => void>
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
onChange: {
|
||||
type: [Function, Array] as PropType<
|
||||
| undefined
|
||||
| MaybeArray<(value: string) => void>
|
||||
| MaybeArray<(value: [string, string]) => void>
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
onClear: {
|
||||
type: [Function, Array] as PropType<
|
||||
undefined | MaybeArray<(e: MouseEvent) => void>
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:value': {
|
||||
type: [Function, Array] as PropType<
|
||||
undefined | MaybeArray<(value: string) => void>
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
/** private */
|
||||
textDecoration: {
|
||||
type: [String, Array],
|
||||
default: undefined
|
||||
},
|
||||
attrSize: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
onInputBlur: {
|
||||
type: [Function, Array] as PropType<
|
||||
undefined | MaybeArray<(e: FocusEvent) => void>
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
onInputFocus: {
|
||||
type: [Function, Array] as PropType<
|
||||
undefined | MaybeArray<(e: FocusEvent) => void>
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
onDeactivate: {
|
||||
type: [Function, Array] as PropType<undefined | MaybeArray<() => void>>,
|
||||
default: undefined
|
||||
},
|
||||
onActivate: {
|
||||
type: [Function, Array] as PropType<undefined | MaybeArray<() => void>>,
|
||||
default: undefined
|
||||
},
|
||||
onWrapperFocus: {
|
||||
type: [Function, Array] as PropType<
|
||||
undefined | MaybeArray<(e: FocusEvent) => void>
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
onWrapperBlur: {
|
||||
type: [Function, Array] as PropType<
|
||||
undefined | MaybeArray<(e: FocusEvent) => void>
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
deactivateOnEnter: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
const themeRef = useTheme('Input', 'Input', style, inputLight, props)
|
||||
// dom refs
|
||||
const wrapperRef = ref<HTMLElement | null>(null)
|
||||
const textareaRef = ref<HTMLElement | null>(null)
|
||||
const textareaMirrorRef = ref<HTMLElement | null>(null)
|
||||
const inputRef = ref<HTMLElement | null>(null)
|
||||
const input2Ref = ref<HTMLElement | null>(null)
|
||||
// local
|
||||
const { locale } = useLocale('Input')
|
||||
// value
|
||||
const uncontrolledValueRef = ref(props.defaultValue)
|
||||
const controlledValueRef = toRef(props, 'value')
|
||||
const mergedValueRef = useMergedState(
|
||||
controlledValueRef,
|
||||
uncontrolledValueRef
|
||||
)
|
||||
// form-item
|
||||
const formItem = useFormItem(props)
|
||||
const { mergedSize: mergedSizeRef } = formItem
|
||||
// states
|
||||
const focusedRef = ref(false)
|
||||
const hoverRef = ref(false)
|
||||
const isComposingRef = ref(false)
|
||||
const activatedRef = ref(false)
|
||||
// placeholder
|
||||
const mergedPlaceholderRef = computed<[string, string] | [string]>(() => {
|
||||
const { placeholder, pair } = props
|
||||
if (pair) {
|
||||
if (Array.isArray(placeholder)) {
|
||||
return placeholder
|
||||
} else if (placeholder === undefined) {
|
||||
return ['', '']
|
||||
}
|
||||
return [placeholder, placeholder]
|
||||
} else if (placeholder === undefined) {
|
||||
return [locale.value.placeholder]
|
||||
} else {
|
||||
return [placeholder] as [string]
|
||||
}
|
||||
})
|
||||
const showPlaceholder1Ref = computed(() => {
|
||||
const { value: isComposing } = isComposingRef
|
||||
const { value: mergedValue } = mergedValueRef
|
||||
const { value: mergedPlaceholder } = mergedPlaceholderRef
|
||||
return (
|
||||
!isComposing &&
|
||||
(!mergedValue || (Array.isArray(mergedValue) && !mergedValue[0])) &&
|
||||
mergedPlaceholder[0]
|
||||
)
|
||||
})
|
||||
const showPlaceholder2Ref = computed(() => {
|
||||
const { value: isComposing } = isComposingRef
|
||||
const { value: mergedValue } = mergedValueRef
|
||||
const { value: mergedPlaceholder } = mergedPlaceholderRef
|
||||
return (
|
||||
!isComposing &&
|
||||
mergedPlaceholder[1] &&
|
||||
(!mergedValue || (Array.isArray(mergedValue) && !mergedValue[1]))
|
||||
)
|
||||
})
|
||||
const showTextareaPlaceholderRef = computed(() => {
|
||||
return (
|
||||
props.type === 'textarea' &&
|
||||
!isComposingRef.value &&
|
||||
!mergedValueRef.value &&
|
||||
mergedPlaceholderRef.value
|
||||
)
|
||||
})
|
||||
// clear
|
||||
const showClearButton = computed(() => {
|
||||
if (
|
||||
props.disabled ||
|
||||
!props.clearable ||
|
||||
(!mergedFocusRef.value && !hoverRef.value)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
const { value: mergedValue } = mergedValueRef
|
||||
const { value: mergedFocus } = mergedFocusRef
|
||||
if (props.pair) {
|
||||
return (
|
||||
!!(
|
||||
Array.isArray(mergedValue) &&
|
||||
(mergedValue[0] || mergedValue[1])
|
||||
) &&
|
||||
(hoverRef.value || mergedFocus)
|
||||
)
|
||||
} else {
|
||||
return !!mergedValue && (hoverRef.value || mergedFocus)
|
||||
}
|
||||
})
|
||||
// focus
|
||||
const mergedFocusRef = computed(() => {
|
||||
return props.forceFocus || focusedRef.value
|
||||
})
|
||||
// text-decoration
|
||||
const textDecorationStyleRef = computed(() => {
|
||||
const { textDecoration } = props
|
||||
if (!textDecoration) return ['', '']
|
||||
if (Array.isArray(textDecoration)) {
|
||||
return textDecoration.map((v) => ({
|
||||
textDecoration: v
|
||||
}))
|
||||
}
|
||||
return [
|
||||
{
|
||||
textDecoration
|
||||
}
|
||||
]
|
||||
})
|
||||
// textarea autosize
|
||||
const updateTextAreaStyle = (): void => {
|
||||
if (props.type === 'textarea') {
|
||||
const { autosize } = props
|
||||
if (!autosize) return
|
||||
if (!textareaRef.value) return
|
||||
const {
|
||||
paddingTop: stylePaddingTop,
|
||||
paddingBottom: stylePaddingBottom,
|
||||
lineHeight: styleLineHeight
|
||||
} = window.getComputedStyle(textareaRef.value)
|
||||
const paddingTop = Number(stylePaddingTop.slice(0, -2))
|
||||
const paddingBottom = Number(stylePaddingBottom.slice(0, -2))
|
||||
const lineHeight = Number(styleLineHeight.slice(0, -2))
|
||||
if (!textareaMirrorRef.value) return
|
||||
if (autosize.minRows) {
|
||||
const minRows = Math.max(autosize.minRows, 1)
|
||||
const styleMinHeight = `${
|
||||
paddingTop + paddingBottom + lineHeight * minRows
|
||||
}px`
|
||||
textareaMirrorRef.value.style.minHeight = styleMinHeight
|
||||
}
|
||||
if (autosize.maxRows) {
|
||||
const styleMaxHeight = `${
|
||||
paddingTop + paddingBottom + lineHeight * autosize.maxRows
|
||||
}px`
|
||||
textareaMirrorRef.value.style.maxHeight = styleMaxHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
watch([mergedValueRef, mergedSizeRef], () => {
|
||||
void nextTick(updateTextAreaStyle)
|
||||
})
|
||||
onMounted(updateTextAreaStyle)
|
||||
// other methods
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const vm = getCurrentInstance()!.proxy!
|
||||
function doInput (value: [string, string]): void
|
||||
function doInput (value: string): void
|
||||
function doInput (value: string | [string, string]): void {
|
||||
const { 'onUpdate:value': onUpdateValue, onInput } = props
|
||||
const { nTriggerFormInput } = formItem
|
||||
if (onUpdateValue) call(onUpdateValue, value)
|
||||
if (onInput) call(onInput, value)
|
||||
uncontrolledValueRef.value = value
|
||||
nTriggerFormInput()
|
||||
}
|
||||
function doChange (value: [string, string]): void
|
||||
function doChange (value: string): void
|
||||
function doChange (value: string | [string, string]): void {
|
||||
const { onChange } = props
|
||||
const { nTriggerFormChange } = formItem
|
||||
if (onChange) call(onChange, value)
|
||||
uncontrolledValueRef.value = value
|
||||
nTriggerFormChange()
|
||||
}
|
||||
function doBlur (e: FocusEvent): void {
|
||||
const { onBlur } = props
|
||||
const { nTriggerFormBlur } = formItem
|
||||
if (onBlur) call(onBlur, e)
|
||||
nTriggerFormBlur()
|
||||
}
|
||||
function doFocus (e: FocusEvent): void {
|
||||
const { onFocus } = props
|
||||
const { nTriggerFormFocus } = formItem
|
||||
if (onFocus) call(onFocus, e)
|
||||
nTriggerFormFocus()
|
||||
}
|
||||
function doClear (e: MouseEvent): void {
|
||||
const { onClear } = props
|
||||
if (onClear) call(onClear, e)
|
||||
}
|
||||
function doInputBlur (e: FocusEvent): void {
|
||||
const { onInputBlur } = props
|
||||
if (onInputBlur) call(onInputBlur, e)
|
||||
}
|
||||
function doInputFocus (e: FocusEvent): void {
|
||||
const { onInputFocus } = props
|
||||
if (onInputFocus) call(onInputFocus, e)
|
||||
}
|
||||
function doDeactivate (): void {
|
||||
const { onDeactivate } = props
|
||||
if (onDeactivate) call(onDeactivate)
|
||||
}
|
||||
function doActivate (): void {
|
||||
const { onActivate } = props
|
||||
if (onActivate) call(onActivate)
|
||||
}
|
||||
function doClick (e: MouseEvent): void {
|
||||
const { onClick } = props
|
||||
if (onClick) call(onClick, e)
|
||||
}
|
||||
function doWrapperFocus (e: FocusEvent): void {
|
||||
const { onWrapperFocus } = props
|
||||
if (onWrapperFocus) call(onWrapperFocus, e)
|
||||
}
|
||||
function doWrapperBlur (e: FocusEvent): void {
|
||||
const { onWrapperBlur } = props
|
||||
if (onWrapperBlur) call(onWrapperBlur, e)
|
||||
}
|
||||
// methods
|
||||
function handleCompositionStart (): void {
|
||||
isComposingRef.value = true
|
||||
}
|
||||
function handleCompositionEnd (e: CompositionEvent): void {
|
||||
isComposingRef.value = false
|
||||
if (e.target === input2Ref.value) {
|
||||
handleInput(e, 1)
|
||||
} else {
|
||||
handleInput(e, 0)
|
||||
}
|
||||
}
|
||||
function handleInput (
|
||||
e: InputEvent | CompositionEvent | Event,
|
||||
index: 0 | 1 = 0,
|
||||
event = 'input'
|
||||
): void {
|
||||
if (isComposingRef.value) return
|
||||
const changedValue = (e.target as HTMLInputElement).value
|
||||
if (!props.pair) {
|
||||
event === 'input' ? doInput(changedValue) : doChange(changedValue)
|
||||
} else {
|
||||
let { value } = mergedValueRef
|
||||
if (!Array.isArray(value)) {
|
||||
value = ['', '']
|
||||
} else {
|
||||
value = [...value]
|
||||
}
|
||||
value[index] = changedValue
|
||||
event === 'input' ? doInput(value) : doChange(value)
|
||||
}
|
||||
// force update to sync input's view with value
|
||||
// if not set, after input, input value won't sync with dom input value
|
||||
vm.$forceUpdate()
|
||||
}
|
||||
function handleInputBlur (e: FocusEvent): void {
|
||||
doInputBlur(e)
|
||||
if (e.relatedTarget === wrapperRef.value) {
|
||||
doDeactivate()
|
||||
}
|
||||
if (
|
||||
!(
|
||||
e.relatedTarget !== null &&
|
||||
(e.relatedTarget === inputRef.value ||
|
||||
e.relatedTarget === input2Ref.value ||
|
||||
e.relatedTarget === textareaRef.value)
|
||||
)
|
||||
) {
|
||||
activatedRef.value = false
|
||||
}
|
||||
dealWithEvent(e, 'blur')
|
||||
}
|
||||
function handleInputFocus (e: FocusEvent): void {
|
||||
doInputFocus(e)
|
||||
focusedRef.value = true
|
||||
activatedRef.value = true
|
||||
doActivate()
|
||||
dealWithEvent(e, 'focus')
|
||||
}
|
||||
function handleWrapperBlur (e: FocusEvent): void {
|
||||
if (props.passivelyActivated) {
|
||||
doWrapperBlur(e)
|
||||
dealWithEvent(e, 'blur')
|
||||
}
|
||||
}
|
||||
function handleWrapperFocus (e: FocusEvent): void {
|
||||
if (props.passivelyActivated) {
|
||||
focusedRef.value = true
|
||||
doWrapperFocus(e)
|
||||
dealWithEvent(e, 'focus')
|
||||
}
|
||||
}
|
||||
function dealWithEvent (e: FocusEvent, type: 'focus' | 'blur'): void {
|
||||
if (
|
||||
e.relatedTarget !== null &&
|
||||
(e.relatedTarget === inputRef.value ||
|
||||
e.relatedTarget === input2Ref.value ||
|
||||
e.relatedTarget === textareaRef.value ||
|
||||
e.relatedTarget === wrapperRef.value)
|
||||
) {
|
||||
/**
|
||||
* activeElement transfer inside the input, do nothing
|
||||
*/
|
||||
} else {
|
||||
if (type === 'focus') {
|
||||
doFocus(e)
|
||||
focusedRef.value = true
|
||||
} else if (type === 'blur') {
|
||||
doBlur(e)
|
||||
focusedRef.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleChange (e: Event, index?: 0 | 1): void {
|
||||
handleInput(e, index, 'change')
|
||||
}
|
||||
function handleClick (e: MouseEvent): void {
|
||||
doClick(e)
|
||||
}
|
||||
function handleClear (e: MouseEvent): void {
|
||||
doClear(e)
|
||||
if (props.pair) {
|
||||
doInput(['', ''])
|
||||
} else {
|
||||
doInput('')
|
||||
}
|
||||
}
|
||||
function handleMouseEnter (): void {
|
||||
hoverRef.value = true
|
||||
}
|
||||
function handleMouseLeave (): void {
|
||||
hoverRef.value = false
|
||||
}
|
||||
function handleWrapperKeyDown (e: KeyboardEvent): void {
|
||||
switch (e.code) {
|
||||
case 'ESC':
|
||||
handleWrapperKeyDownEsc()
|
||||
break
|
||||
case 'ENTER':
|
||||
handleWrapperKeyDownEnter(e)
|
||||
break
|
||||
}
|
||||
}
|
||||
function handleWrapperKeyDownEnter (e: KeyboardEvent): void {
|
||||
if (props.passivelyActivated) {
|
||||
const { value: focused } = activatedRef
|
||||
if (focused) {
|
||||
if (props.deactivateOnEnter) {
|
||||
handleWrapperKeyDownEsc()
|
||||
}
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
if (props.type === 'textarea') {
|
||||
textareaRef.value?.focus()
|
||||
} else {
|
||||
inputRef.value?.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleWrapperKeyDownEsc (): void {
|
||||
if (props.passivelyActivated) {
|
||||
activatedRef.value = false
|
||||
void nextTick(() => {
|
||||
wrapperRef.value?.focus()
|
||||
})
|
||||
}
|
||||
}
|
||||
function focus (): void {
|
||||
if (props.disabled) return
|
||||
if (props.passivelyActivated) {
|
||||
wrapperRef.value?.focus()
|
||||
} else {
|
||||
textareaRef.value?.focus()
|
||||
inputRef.value?.focus()
|
||||
}
|
||||
}
|
||||
function blur (): void {
|
||||
if (wrapperRef.value?.contains(document.activeElement)) {
|
||||
;(document.activeElement as HTMLElement).blur()
|
||||
}
|
||||
}
|
||||
function activate (): void {
|
||||
if (props.disabled) return
|
||||
if (textareaRef.value) textareaRef.value.focus()
|
||||
else if (inputRef.value) inputRef.value.focus()
|
||||
}
|
||||
function deactivate (): void {
|
||||
const { value: wrapperEl } = wrapperRef
|
||||
if (
|
||||
wrapperEl?.contains(document.activeElement) &&
|
||||
wrapperEl !== document.activeElement
|
||||
) {
|
||||
handleWrapperKeyDownEsc()
|
||||
}
|
||||
}
|
||||
return {
|
||||
// DOM ref
|
||||
wrapperRef,
|
||||
inputRef,
|
||||
input2Ref,
|
||||
textareaRef,
|
||||
textareaMirrorRef,
|
||||
// value
|
||||
uncontrolledValue: uncontrolledValueRef,
|
||||
mergedValue: mergedValueRef,
|
||||
mergedPlaceholder: mergedPlaceholderRef,
|
||||
showPlaceholder1: showPlaceholder1Ref,
|
||||
showPlaceholder2: showPlaceholder2Ref,
|
||||
showTextareaPlaceholder: showTextareaPlaceholderRef,
|
||||
mergedFocus: mergedFocusRef,
|
||||
isComposing: isComposingRef,
|
||||
activated: activatedRef,
|
||||
showClearButton,
|
||||
mergedSize: mergedSizeRef,
|
||||
textDecorationStyle: textDecorationStyleRef,
|
||||
// methods
|
||||
focus,
|
||||
blur,
|
||||
deactivate,
|
||||
activate,
|
||||
handleCompositionStart,
|
||||
handleCompositionEnd,
|
||||
handleInput,
|
||||
handleInputBlur,
|
||||
handleInputFocus,
|
||||
handleWrapperBlur,
|
||||
handleWrapperFocus,
|
||||
handleMouseEnter,
|
||||
handleMouseLeave,
|
||||
handleChange,
|
||||
handleClick,
|
||||
handleClear,
|
||||
handleWrapperKeyDown,
|
||||
...useConfig(props),
|
||||
mergedTheme: themeRef,
|
||||
cssVars: computed(() => {
|
||||
const { value: size } = mergedSizeRef
|
||||
const {
|
||||
common: { cubicBezierEaseInOut },
|
||||
self: {
|
||||
color,
|
||||
borderRadius,
|
||||
paddingLeft,
|
||||
paddingRight,
|
||||
textColor,
|
||||
caretColor,
|
||||
textDecorationColor,
|
||||
border,
|
||||
borderDisabled,
|
||||
borderHover,
|
||||
borderFocus,
|
||||
placeholderColor,
|
||||
placeholderColorDisabled,
|
||||
lineHeightTextarea,
|
||||
colorDisabled,
|
||||
colorFocus,
|
||||
textColorDisabled,
|
||||
boxShadowFocus,
|
||||
iconSize,
|
||||
colorFocusWarning,
|
||||
boxShadowFocusWarning,
|
||||
borderWarning,
|
||||
borderFocusWarning,
|
||||
borderHoverWarning,
|
||||
colorFocusError,
|
||||
boxShadowFocusError,
|
||||
borderError,
|
||||
borderFocusError,
|
||||
borderHoverError,
|
||||
clearSize,
|
||||
clearColor,
|
||||
clearColorHover,
|
||||
clearColorPressed,
|
||||
[createKey('fontSize', size)]: fontSize,
|
||||
[createKey('height', size)]: height
|
||||
}
|
||||
} = themeRef.value
|
||||
return {
|
||||
'--bezier': cubicBezierEaseInOut,
|
||||
'--color': color,
|
||||
'--font-size': fontSize,
|
||||
'--border-radius': borderRadius,
|
||||
'--height': height,
|
||||
'--padding-left': paddingLeft,
|
||||
'--padding-right': paddingRight,
|
||||
'--text-color': textColor,
|
||||
'--caret-color': caretColor,
|
||||
'--text-decoration-color': textDecorationColor,
|
||||
'--border': border,
|
||||
'--border-disabled': borderDisabled,
|
||||
'--border-hover': borderHover,
|
||||
'--border-focus': borderFocus,
|
||||
'--placeholder-color': placeholderColor,
|
||||
'--placeholder-color-disabled': placeholderColorDisabled,
|
||||
'--icon-size': iconSize,
|
||||
'--line-height-textarea': lineHeightTextarea,
|
||||
'--color-disabled': colorDisabled,
|
||||
'--color-focus': colorFocus,
|
||||
'--text-color-disabled': textColorDisabled,
|
||||
'--box-shadow-focus': boxShadowFocus,
|
||||
// form warning
|
||||
'--color-focus-warning': colorFocusWarning,
|
||||
'--box-shadow-focus-warning': boxShadowFocusWarning,
|
||||
'--border-warning': borderWarning,
|
||||
'--border-focus-warning': borderFocusWarning,
|
||||
'--border-hover-warning': borderHoverWarning,
|
||||
// form error
|
||||
'--color-focus-error': colorFocusError,
|
||||
'--box-shadow-focus-error': boxShadowFocusError,
|
||||
'--border-error': borderError,
|
||||
'--border-focus-error': borderFocusError,
|
||||
'--border-hover-error': borderHoverError,
|
||||
// clear-button
|
||||
'--clear-color': clearColor,
|
||||
'--clear-size': clearSize,
|
||||
'--clear-color-hover': clearColorHover,
|
||||
'--clear-color-pressed': clearColorPressed
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
render () {
|
||||
return (
|
||||
<div
|
||||
ref="wrapperRef"
|
||||
class={[
|
||||
'n-input',
|
||||
{
|
||||
'n-input--disabled': this.disabled,
|
||||
'n-input--textarea': this.type === 'textarea',
|
||||
'n-input--round': this.round && !(this.type === 'textarea'),
|
||||
'n-input--pair': this.pair,
|
||||
'n-input--focus': this.mergedFocus,
|
||||
'n-input--stateful': this.stateful
|
||||
}
|
||||
]}
|
||||
style={this.cssVars as CSSProperties}
|
||||
tabindex={
|
||||
!this.disabled && this.passivelyActivated && !this.activated
|
||||
? 0
|
||||
: undefined
|
||||
}
|
||||
onFocus={this.handleWrapperFocus}
|
||||
onBlur={this.handleWrapperBlur}
|
||||
onClick={this.handleClick}
|
||||
onMouseenter={this.handleMouseEnter}
|
||||
onMouseleave={this.handleMouseLeave}
|
||||
onCompositionstart={this.handleCompositionStart}
|
||||
onCompositionend={this.handleCompositionEnd}
|
||||
onKeydown={this.handleWrapperKeyDown}
|
||||
>
|
||||
{/* textarea mirror */}
|
||||
{this.type === 'textarea' && this.autosize ? (
|
||||
<pre ref="textareaMirrorRef" class="n-input__textarea-mirror">
|
||||
{this.mergedValue}
|
||||
<br />
|
||||
</pre>
|
||||
) : null}
|
||||
{/* textarea & basic input */}
|
||||
{this.type === 'textarea' ? (
|
||||
<textarea
|
||||
ref="textareaRef"
|
||||
class={[
|
||||
'n-input__textarea',
|
||||
{
|
||||
'n-input__textarea--autosize': this.autosize
|
||||
}
|
||||
]}
|
||||
autofocus={this.autofocus}
|
||||
rows={Number(this.rows)}
|
||||
placeholder={this.placeholder as string | undefined}
|
||||
value={this.mergedValue as string | undefined}
|
||||
disabled={this.disabled}
|
||||
maxlength={this.maxlength as any}
|
||||
minlength={this.minlength as any}
|
||||
readonly={this.readonly as any}
|
||||
tabindex={
|
||||
this.passivelyActivated && !this.activated ? -1 : undefined
|
||||
}
|
||||
style={this.textDecorationStyle[0] as any}
|
||||
onBlur={this.handleInputBlur}
|
||||
onFocus={this.handleInputFocus}
|
||||
onInput={this.handleInput}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
) : (
|
||||
<div class="n-input-wrapper">
|
||||
{this.$slots.affix || this.$slots.prefix ? (
|
||||
<NIconConfigProvider
|
||||
class="n-input__prefix"
|
||||
depth={this.disabled ? 5 : 4}
|
||||
>
|
||||
{{
|
||||
default: () =>
|
||||
renderSlot(this.$slots, 'affix', undefined, () => {
|
||||
return [renderSlot(this.$slots, 'prefix')]
|
||||
})
|
||||
}}
|
||||
</NIconConfigProvider>
|
||||
) : null}
|
||||
<div class="n-input__input">
|
||||
<input
|
||||
ref="inputRef"
|
||||
type={this.type}
|
||||
class="n-input__input-el"
|
||||
tabindex={
|
||||
this.passivelyActivated && !this.activated ? -1 : undefined
|
||||
}
|
||||
placeholder={this.mergedPlaceholder[0]}
|
||||
disabled={this.disabled}
|
||||
maxlength={this.maxlength as any}
|
||||
minlength={this.minlength as any}
|
||||
value={
|
||||
Array.isArray(this.mergedValue)
|
||||
? this.mergedValue[0]
|
||||
: (this.mergedValue as any)
|
||||
}
|
||||
readonly={this.readonly as any}
|
||||
autofocus={this.autofocus}
|
||||
size={this.attrSize}
|
||||
style={this.textDecorationStyle[0] as any}
|
||||
onBlur={this.handleInputBlur}
|
||||
onFocus={this.handleInputFocus}
|
||||
onInput={(e) => this.handleInput(e, 0)}
|
||||
onChange={(e) => this.handleChange(e, 0)}
|
||||
/>
|
||||
{this.showPlaceholder1 ? (
|
||||
<div class="n-input__placeholder">
|
||||
<span>{this.mergedPlaceholder[0]}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{!this.pair ? (
|
||||
<NIconConfigProvider
|
||||
class="n-input__suffix"
|
||||
depth={this.disabled ? 5 : 4}
|
||||
>
|
||||
{{
|
||||
default: () => [
|
||||
renderSlot(this.$slots, 'suffix'),
|
||||
this.clearable || this.$slots.clear ? (
|
||||
<NBaseClear
|
||||
show={this.showClearButton}
|
||||
onClear={this.handleClear}
|
||||
>
|
||||
{{ default: () => renderSlot(this.$slots, 'clear') }}
|
||||
</NBaseClear>
|
||||
) : null
|
||||
]
|
||||
}}
|
||||
</NIconConfigProvider>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
{/* pair input */}
|
||||
{this.pair ? (
|
||||
<span class="n-input__separator">
|
||||
{renderSlot(this.$slots, 'separator', undefined, () => [
|
||||
this.separator
|
||||
])}
|
||||
</span>
|
||||
) : null}
|
||||
{this.pair ? (
|
||||
<div v-if="pair" class="n-input-wrapper">
|
||||
<div class="n-input__input">
|
||||
<input
|
||||
ref="input2Ref"
|
||||
type={this.type}
|
||||
class="n-input__input-el"
|
||||
tabindex={
|
||||
this.passivelyActivated && !this.activated ? -1 : undefined
|
||||
}
|
||||
placeholder={this.mergedPlaceholder[1]}
|
||||
disabled={this.disabled}
|
||||
maxlength={this.maxlength as any}
|
||||
minlength={this.minlength as any}
|
||||
value={
|
||||
Array.isArray(this.mergedValue)
|
||||
? this.mergedValue[1]
|
||||
: undefined
|
||||
}
|
||||
readonly={this.readonly as any}
|
||||
style={this.textDecorationStyle[1] as any}
|
||||
onBlur={this.handleInputBlur}
|
||||
onFocus={this.handleInputFocus}
|
||||
onInput={(e) => this.handleInput(e, 1)}
|
||||
onChange={(e) => this.handleChange(e, 1)}
|
||||
/>
|
||||
{this.showPlaceholder2 ? (
|
||||
<div class="n-input__placeholder">
|
||||
<span>{this.mergedPlaceholder[1]}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<NIconConfigProvider
|
||||
class="n-input__suffix"
|
||||
depth={this.disabled ? 5 : 4}
|
||||
>
|
||||
{{
|
||||
default: () => {
|
||||
return [
|
||||
renderSlot(this.$slots, 'suffix'),
|
||||
this.clearable || this.$slots.clear ? (
|
||||
<NBaseClear
|
||||
show={this.showClearButton}
|
||||
onClear={this.handleClear}
|
||||
>
|
||||
{{ default: () => renderSlot(this.$slots, 'cleaar') }}
|
||||
</NBaseClear>
|
||||
) : null
|
||||
]
|
||||
}
|
||||
}}
|
||||
</NIconConfigProvider>
|
||||
</div>
|
||||
) : null}
|
||||
{/* textarea placeholder */}
|
||||
{this.showTextareaPlaceholder ? (
|
||||
<div class="n-input__placeholder">{this.placeholder}</div>
|
||||
) : null}
|
||||
{/* border */}
|
||||
{this.mergedBordered ? <div class="n-input__border" /> : null}
|
||||
{this.mergedBordered ? <div class="n-input__state-border" /> : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
@ -1,878 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
ref="wrapperRef"
|
||||
class="n-input"
|
||||
:class="{
|
||||
'n-input--disabled': disabled,
|
||||
'n-input--textarea': type === 'textarea',
|
||||
'n-input--round': round && !(type === 'textarea'),
|
||||
'n-input--pair': pair,
|
||||
'n-input--focus': mergedFocus,
|
||||
'n-input--stateful': stateful
|
||||
}"
|
||||
:style="cssVars"
|
||||
:tabindex="!disabled && passivelyActivated && !activated ? 0 : false"
|
||||
@focus="handleWrapperFocus"
|
||||
@blur="handleWrapperBlur"
|
||||
@keydown.enter="handleWrapperKeyDownEnter"
|
||||
@keydown.esc="handleWrapperKeyDownEsc"
|
||||
@click="handleClick"
|
||||
@mouseenter="handleMouseEnter"
|
||||
@mouseleave="handleMouseLeave"
|
||||
@compositionstart="handleCompositionStart"
|
||||
@compositionend="handleCompositionEnd"
|
||||
>
|
||||
<pre
|
||||
v-if="type === 'textarea' && autosize"
|
||||
ref="textareaMirrorRef"
|
||||
class="n-input__textarea-mirror">{{ mergedValue }}<br></pre>
|
||||
<textarea
|
||||
v-if="type === 'textarea'"
|
||||
ref="textareaRef"
|
||||
class="n-input__textarea"
|
||||
:class="{
|
||||
'n-input__textarea--autosize': autosize
|
||||
}"
|
||||
:autofocus="autofocus"
|
||||
:rows="rows"
|
||||
:placeholder="placeholder"
|
||||
:value="mergedValue"
|
||||
:disabled="disabled"
|
||||
:maxlength="maxlength"
|
||||
:minlength="minlength"
|
||||
:readonly="readonly"
|
||||
:tabindex="passivelyActivated && !activated ? -1 : false"
|
||||
:style="textDecorationStyle[0]"
|
||||
@blur="handleInputBlur"
|
||||
@focus="handleInputFocus"
|
||||
@input="handleInput"
|
||||
@change="handleChange"
|
||||
@keyup="handleKeyUp"
|
||||
/>
|
||||
<div v-else class="n-input-wrapper">
|
||||
<n-icon-config-provider
|
||||
v-if="$slots.affix || $slots.prefix"
|
||||
class="n-input__prefix"
|
||||
:depth="disabled ? 5 : 4"
|
||||
>
|
||||
<slot name="affix">
|
||||
<slot name="prefix" />
|
||||
</slot>
|
||||
</n-icon-config-provider>
|
||||
<div class="n-input__input">
|
||||
<input
|
||||
ref="inputRef"
|
||||
:type="type"
|
||||
class="n-input__input-el"
|
||||
:tabindex="passivelyActivated && !activated ? -1 : false"
|
||||
:placeholder="mergedPlaceholder[0]"
|
||||
:disabled="disabled"
|
||||
:maxlength="maxlength"
|
||||
:minlength="minlength"
|
||||
:value="pair ? mergedValue && mergedValue[0] : mergedValue"
|
||||
:readonly="readonly"
|
||||
:autofocus="autofocus"
|
||||
:size="attrSize"
|
||||
:style="textDecorationStyle[0]"
|
||||
@blur="handleInputBlur"
|
||||
@focus="handleInputFocus"
|
||||
@input="handleInput($event, 0)"
|
||||
@change="handleChange($event, 0)"
|
||||
@keyup="handleKeyUp"
|
||||
/>
|
||||
<div v-if="showPlaceholder1" class="n-input__placeholder">
|
||||
<span>{{ mergedPlaceholder[0] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<n-icon-config-provider
|
||||
v-if="!pair"
|
||||
class="n-input__suffix"
|
||||
:depth="disabled ? 5 : 4"
|
||||
>
|
||||
<slot name="suffix" />
|
||||
<n-base-clear
|
||||
v-if="clearable || $slots.clear"
|
||||
:unstable-theme="mergedTheme.peers.BaseClear"
|
||||
:unstable-theme-overrides="mergedTheme.overrides.BaseClear"
|
||||
:show="showClearButton"
|
||||
@clear="handleClear"
|
||||
>
|
||||
<slot name="clear" />
|
||||
</n-base-clear>
|
||||
</n-icon-config-provider>
|
||||
</div>
|
||||
<span v-if="pair" class="n-input__separator">
|
||||
<slot name="separator">
|
||||
{{ separator }}
|
||||
</slot>
|
||||
</span>
|
||||
<div v-if="pair" class="n-input-wrapper">
|
||||
<div class="n-input__input">
|
||||
<input
|
||||
ref="input2Ref"
|
||||
:type="type"
|
||||
class="n-input__input-el"
|
||||
:tabindex="passivelyActivated && !activated ? -1 : false"
|
||||
:placeholder="mergedPlaceholder[1]"
|
||||
:disabled="disabled"
|
||||
:maxlength="maxlength"
|
||||
:minlength="minlength"
|
||||
:value="mergedValue && mergedValue[1]"
|
||||
:readonly="readonly"
|
||||
:style="textDecorationStyle[1]"
|
||||
@blur="handleInputBlur"
|
||||
@focus="handleInputFocus"
|
||||
@input="handleInput($event, 1)"
|
||||
@change="handleChange($event, 1)"
|
||||
@keyup="handleKeyUp"
|
||||
/>
|
||||
<div v-if="showPlaceholder2" class="n-input__placeholder">
|
||||
<span>{{ mergedPlaceholder[1] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<n-icon-config-provider class="n-input__suffix" :depth="disabled ? 5 : 4">
|
||||
<slot name="suffix" />
|
||||
<n-base-clear
|
||||
v-if="clearable || $slots.clear"
|
||||
:unstable-theme="mergedTheme.peers.BaseClear"
|
||||
:unstable-theme-overrides="mergedTheme.overrides.BaseClear"
|
||||
:show="showClearButton"
|
||||
@clear="handleClear"
|
||||
>
|
||||
<slot name="clear" />
|
||||
</n-base-clear>
|
||||
</n-icon-config-provider>
|
||||
</div>
|
||||
<div v-if="showTextareaPlaceholder" class="n-input__placeholder">
|
||||
{{ placeholder }}
|
||||
</div>
|
||||
<div v-if="mergedBordered" class="n-input__border" />
|
||||
<div v-if="mergedBordered" class="n-input__state-border" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
nextTick,
|
||||
ref,
|
||||
toRef,
|
||||
watch,
|
||||
onMounted,
|
||||
getCurrentInstance
|
||||
} from 'vue'
|
||||
import { useMergedState } from 'vooks'
|
||||
import NIconConfigProvider from '../../icon/src/IconConfigProvider'
|
||||
import { NBaseClear } from '../../_base'
|
||||
import { useTheme, useLocale, useFormItem, useConfig } from '../../_mixins'
|
||||
import { call, createKey } from '../../_utils'
|
||||
import { inputLight } from '../styles'
|
||||
import style from './styles/input.cssr.js'
|
||||
|
||||
function createMethods (
|
||||
props,
|
||||
formItem,
|
||||
{
|
||||
mergedValueRef,
|
||||
uncontrolledValueRef,
|
||||
isComposingRef,
|
||||
focusedRef,
|
||||
hoverRef,
|
||||
activatedRef,
|
||||
wrapperRef,
|
||||
inputRef,
|
||||
input2Ref,
|
||||
textareaRef,
|
||||
textareaMirrorRef
|
||||
}
|
||||
) {
|
||||
const vm = getCurrentInstance().proxy
|
||||
const doInput = (value) => {
|
||||
const { 'onUpdate:value': onUpdateValue, onInput } = props
|
||||
const { nTriggerFormInput } = formItem
|
||||
if (onUpdateValue) call(onUpdateValue, value)
|
||||
if (onInput) call(onInput, value)
|
||||
uncontrolledValueRef.value = value
|
||||
nTriggerFormInput()
|
||||
}
|
||||
const doChange = (value) => {
|
||||
const { onChange } = props
|
||||
const { nTriggerFormChange } = formItem
|
||||
if (onChange) call(onChange, value)
|
||||
uncontrolledValueRef.value = value
|
||||
nTriggerFormChange()
|
||||
}
|
||||
const doBlur = (e) => {
|
||||
const { onBlur } = props
|
||||
const { nTriggerFormBlur } = formItem
|
||||
if (onBlur) call(onBlur, e)
|
||||
nTriggerFormBlur()
|
||||
}
|
||||
const doFocus = (e) => {
|
||||
const { onFocus } = props
|
||||
const { nTriggerFormFocus } = formItem
|
||||
if (onFocus) call(onFocus, e)
|
||||
nTriggerFormFocus()
|
||||
}
|
||||
const doClear = (...args) => {
|
||||
const { onClear } = props
|
||||
if (onClear) call(onClear, ...args)
|
||||
}
|
||||
const doInputBlur = (e) => {
|
||||
const { onInputBlur } = props
|
||||
if (onInputBlur) call(onInputBlur, e)
|
||||
}
|
||||
const doInputFocus = (e) => {
|
||||
const { onInputFocus } = props
|
||||
if (onInputFocus) call(onInputFocus, e)
|
||||
}
|
||||
const doDeactivate = () => {
|
||||
const { onDeactivate } = props
|
||||
if (onDeactivate) call(onDeactivate)
|
||||
}
|
||||
const doActivate = () => {
|
||||
const { onActivate } = props
|
||||
if (onActivate) call(onActivate)
|
||||
}
|
||||
const doKeyUp = (e) => {
|
||||
const { onKeyUp } = props
|
||||
if (onKeyUp) call(onKeyUp, e)
|
||||
}
|
||||
const doClick = (e) => {
|
||||
const { onClick } = props
|
||||
if (onClick) call(onClick, e)
|
||||
}
|
||||
const doWrapperFocus = (e) => {
|
||||
const { onWrapperFocus } = props
|
||||
if (onWrapperFocus) call(onWrapperFocus, e)
|
||||
}
|
||||
const doWrapperBlur = (e) => {
|
||||
const { onWrapperBlur } = props
|
||||
if (onWrapperBlur) call(onWrapperBlur, e)
|
||||
}
|
||||
// methods
|
||||
const handleCompositionStart = () => {
|
||||
isComposingRef.value = true
|
||||
}
|
||||
const handleCompositionEnd = (e) => {
|
||||
isComposingRef.value = false
|
||||
if (e.target === input2Ref.value) {
|
||||
handleInput(e, 1)
|
||||
} else {
|
||||
handleInput(e, 0)
|
||||
}
|
||||
}
|
||||
const handleInput = (e, index, event = 'input') => {
|
||||
if (isComposingRef.value) return
|
||||
const changedValue = e.target.value
|
||||
if (!props.pair) {
|
||||
event === 'input' ? doInput(changedValue) : doChange(changedValue)
|
||||
} else {
|
||||
let { value } = mergedValueRef
|
||||
if (!Array.isArray(value)) {
|
||||
value = [null, null]
|
||||
} else {
|
||||
value = [...value]
|
||||
}
|
||||
value[index] = changedValue
|
||||
event === 'input' ? doInput(value) : doChange(value)
|
||||
}
|
||||
// force update to sync input's view with value
|
||||
// if not set, after input, input value won't sync with dom input value
|
||||
vm.$forceUpdate()
|
||||
}
|
||||
const handleInputBlur = (e) => {
|
||||
doInputBlur(e)
|
||||
if (e.relatedTarget === wrapperRef.value) {
|
||||
doDeactivate()
|
||||
}
|
||||
if (
|
||||
!(
|
||||
e.relatedTarget !== null &&
|
||||
(e.relatedTarget === inputRef.value ||
|
||||
e.relatedTarget === input2Ref.value ||
|
||||
e.relatedTarget === textareaRef.value)
|
||||
)
|
||||
) {
|
||||
activatedRef.value = false
|
||||
}
|
||||
dealWithEvent(e, 'blur')
|
||||
}
|
||||
const handleInputFocus = (e) => {
|
||||
doInputFocus(e)
|
||||
focusedRef.value = true
|
||||
activatedRef.value = true
|
||||
doActivate()
|
||||
dealWithEvent(e, 'focus')
|
||||
}
|
||||
const handleWrapperBlur = (e) => {
|
||||
if (props.passivelyActivated) {
|
||||
doWrapperBlur(e)
|
||||
dealWithEvent(e, 'blur')
|
||||
}
|
||||
}
|
||||
const handleWrapperFocus = (e) => {
|
||||
if (props.passivelyActivated) {
|
||||
focusedRef.value = true
|
||||
doWrapperFocus(e)
|
||||
dealWithEvent(e, 'focus')
|
||||
}
|
||||
}
|
||||
const dealWithEvent = (e, type) => {
|
||||
if (
|
||||
e.relatedTarget !== null &&
|
||||
(e.relatedTarget === inputRef.value ||
|
||||
e.relatedTarget === input2Ref.value ||
|
||||
e.relatedTarget === textareaRef.value ||
|
||||
e.relatedTarget === wrapperRef.value)
|
||||
) {
|
||||
/**
|
||||
* activeElement transfer inside the input, do nothing
|
||||
*/
|
||||
} else {
|
||||
if (type === 'focus') {
|
||||
doFocus(e)
|
||||
focusedRef.value = true
|
||||
} else if (type === 'blur') {
|
||||
doBlur(e)
|
||||
focusedRef.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
const handleKeyUp = (e) => {
|
||||
doKeyUp(e)
|
||||
}
|
||||
const handleChange = (e, index) => handleInput(e, index, 'change')
|
||||
const handleClick = (e) => {
|
||||
doClick(e)
|
||||
}
|
||||
const handleClear = (e) => {
|
||||
doClear(e)
|
||||
if (props.pair) {
|
||||
doInput([])
|
||||
} else {
|
||||
doInput('')
|
||||
}
|
||||
}
|
||||
const handleMouseEnter = () => {
|
||||
hoverRef.value = true
|
||||
}
|
||||
const handleMouseLeave = () => {
|
||||
hoverRef.value = false
|
||||
}
|
||||
const handleWrapperKeyDownEnter = (e) => {
|
||||
if (props.passivelyActivated) {
|
||||
const { value: focused } = activatedRef
|
||||
if (focused) {
|
||||
if (props.deactivateOnEnter) {
|
||||
handleWrapperKeyDownEsc()
|
||||
}
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
if (props.type === 'textarea') {
|
||||
textareaRef.value.focus()
|
||||
} else {
|
||||
inputRef.value.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
const handleWrapperKeyDownEsc = () => {
|
||||
if (props.passivelyActivated) {
|
||||
activatedRef.value = false
|
||||
nextTick(() => {
|
||||
wrapperRef.value.focus()
|
||||
})
|
||||
}
|
||||
}
|
||||
return {
|
||||
doInput,
|
||||
doChange,
|
||||
doFocus,
|
||||
doBlur,
|
||||
doClear,
|
||||
doWrapperFocus,
|
||||
doWrapperBlur,
|
||||
doInputFocus,
|
||||
doInputBlur,
|
||||
doActivate,
|
||||
doDeactivate,
|
||||
doClick,
|
||||
doKeyUp,
|
||||
handleCompositionStart,
|
||||
handleCompositionEnd,
|
||||
handleInput,
|
||||
handleInputBlur,
|
||||
handleInputFocus,
|
||||
handleWrapperBlur,
|
||||
handleWrapperFocus,
|
||||
handleMouseEnter,
|
||||
handleMouseLeave,
|
||||
handleKeyUp,
|
||||
handleChange,
|
||||
handleClick,
|
||||
handleClear,
|
||||
handleWrapperKeyDownEnter,
|
||||
handleWrapperKeyDownEsc
|
||||
}
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Input',
|
||||
components: {
|
||||
NIconConfigProvider,
|
||||
NBaseClear
|
||||
},
|
||||
props: {
|
||||
...useTheme.props,
|
||||
bordered: {
|
||||
type: Boolean,
|
||||
default: undefined
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text'
|
||||
},
|
||||
placeholder: {
|
||||
type: [Array, String],
|
||||
default: undefined
|
||||
},
|
||||
defaultValue: {
|
||||
type: [String, Array],
|
||||
default: null
|
||||
},
|
||||
value: {
|
||||
type: [String, Array],
|
||||
default: undefined
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
size: {
|
||||
validator (value) {
|
||||
return ['small', 'medium', 'large'].includes(value)
|
||||
},
|
||||
default: undefined
|
||||
},
|
||||
rows: {
|
||||
type: [Number, String],
|
||||
default: 3
|
||||
},
|
||||
round: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
minlength: {
|
||||
type: [String, Number],
|
||||
default: undefined
|
||||
},
|
||||
maxlength: {
|
||||
type: [String, Number],
|
||||
default: undefined
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
autosize: {
|
||||
type: [Boolean, Object],
|
||||
default: false
|
||||
},
|
||||
showWordLimit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
pair: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
separator: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
readonly: {
|
||||
type: [String, Boolean],
|
||||
default: false
|
||||
},
|
||||
forceFocus: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
passivelyActivated: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
stateful: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
autofocus: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
onInput: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onFocus: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onBlur: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onClick: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onChange: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onKeyUp: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onClear: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:value': {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
/** private */
|
||||
textDecoration: {
|
||||
type: [String, Array],
|
||||
default: undefined
|
||||
},
|
||||
attrSize: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
onInputBlur: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onInputFocus: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onDeactivate: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onActivate: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onWrapperFocus: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onWrapperBlur: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
deactivateOnEnter: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
const themeRef = useTheme('Input', 'Input', style, inputLight, props)
|
||||
// dom refs
|
||||
const wrapperRef = ref(null)
|
||||
const textareaRef = ref(null)
|
||||
const textareaMirrorRef = ref(null)
|
||||
const inputRef = ref(null)
|
||||
const input2Ref = ref(null)
|
||||
// local
|
||||
const { locale } = useLocale('Input')
|
||||
// value
|
||||
const uncontrolledValueRef = ref(props.defaultValue)
|
||||
const controlledValueRef = toRef(props, 'value')
|
||||
const mergedValueRef = useMergedState(
|
||||
controlledValueRef,
|
||||
uncontrolledValueRef
|
||||
)
|
||||
// form-item
|
||||
const formItem = useFormItem(props)
|
||||
const { mergedSize: mergedSizeRef } = formItem
|
||||
// states
|
||||
const focusedRef = ref(false)
|
||||
const hoverRef = ref(false)
|
||||
const isComposingRef = ref(false)
|
||||
const activatedRef = ref(false)
|
||||
// placeholder
|
||||
const mergedPlaceholderRef = computed(() => {
|
||||
const { placeholder, pair } = props
|
||||
if (pair) {
|
||||
if (Array.isArray(placeholder)) {
|
||||
return placeholder
|
||||
} else {
|
||||
return [placeholder, placeholder]
|
||||
}
|
||||
} else if (placeholder === undefined) {
|
||||
return [locale.value.placeholder]
|
||||
} else {
|
||||
return [placeholder]
|
||||
}
|
||||
})
|
||||
const showPlaceholder1Ref = computed(() => {
|
||||
const { value: isComposing } = isComposingRef
|
||||
const { value: mergedValue } = mergedValueRef
|
||||
const { value: mergedPlaceholder } = mergedPlaceholderRef
|
||||
return (
|
||||
!isComposing &&
|
||||
(!mergedValue || (Array.isArray(mergedValue) && !mergedValue[0])) &&
|
||||
mergedPlaceholder[0]
|
||||
)
|
||||
})
|
||||
const showPlaceholder2Ref = computed(() => {
|
||||
const { value: isComposing } = isComposingRef
|
||||
const { value: mergedValue } = mergedValueRef
|
||||
const { value: mergedPlaceholder } = mergedPlaceholderRef
|
||||
return (
|
||||
!isComposing &&
|
||||
mergedPlaceholder[1] &&
|
||||
(!mergedValue || (Array.isArray(mergedValue) && !mergedValue[1]))
|
||||
)
|
||||
})
|
||||
const showTextareaPlaceholderRef = computed(() => {
|
||||
return (
|
||||
props.type === 'textarea' &&
|
||||
!isComposingRef.value &&
|
||||
!mergedValueRef.value &&
|
||||
mergedPlaceholderRef.value
|
||||
)
|
||||
})
|
||||
// clear
|
||||
const showClearButton = computed(() => {
|
||||
if (
|
||||
props.disabled ||
|
||||
!props.clearable ||
|
||||
(!mergedFocusRef.value && !hoverRef.value)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
const { value: mergedValue } = mergedValueRef
|
||||
const { value: mergedFocus } = mergedFocusRef
|
||||
if (props.pair) {
|
||||
return (
|
||||
!!(
|
||||
Array.isArray(mergedValue) &&
|
||||
(mergedValue[0] || mergedValue[1])
|
||||
) &&
|
||||
(hoverRef.value || mergedFocus)
|
||||
)
|
||||
} else {
|
||||
return !!mergedValue && (hoverRef.value || mergedFocus)
|
||||
}
|
||||
})
|
||||
// focus
|
||||
const mergedFocusRef = computed(() => {
|
||||
return props.forceFocus || focusedRef.value
|
||||
})
|
||||
// text-decoration
|
||||
const textDecorationStyleRef = computed(() => {
|
||||
const { textDecoration } = props
|
||||
if (!textDecoration) return ['', '']
|
||||
if (Array.isArray(textDecoration)) {
|
||||
return textDecoration.map((v) => ({
|
||||
textDecoration: v
|
||||
}))
|
||||
}
|
||||
return [
|
||||
{
|
||||
textDecoration
|
||||
}
|
||||
]
|
||||
})
|
||||
// textarea autosize
|
||||
const updateTextAreaStyle = () => {
|
||||
if (props.type === 'textarea') {
|
||||
const { autosize } = props
|
||||
if (!autosize) return
|
||||
const {
|
||||
paddingTop: stylePaddingTop,
|
||||
paddingBottom: stylePaddingBottom,
|
||||
lineHeight: styleLineHeight
|
||||
} = window.getComputedStyle(textareaRef.value)
|
||||
const paddingTop = Number(stylePaddingTop.slice(0, -2))
|
||||
const paddingBottom = Number(stylePaddingBottom.slice(0, -2))
|
||||
const lineHeight = Number(styleLineHeight.slice(0, -2))
|
||||
if (autosize.minRows) {
|
||||
const minRows = Math.max(autosize.minRows, 1)
|
||||
const styleMinHeight =
|
||||
paddingTop + paddingBottom + lineHeight * minRows + 'px'
|
||||
textareaMirrorRef.value.style.minHeight = styleMinHeight
|
||||
}
|
||||
if (autosize.maxRows) {
|
||||
const maxRows = Math.max(autosize.maxRows, autosize.minRows, 1)
|
||||
const styleMaxHeight =
|
||||
paddingTop + paddingBottom + lineHeight * maxRows + 'px'
|
||||
textareaMirrorRef.value.style.maxHeight = styleMaxHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
watch([mergedValueRef, mergedSizeRef], () => {
|
||||
nextTick(updateTextAreaStyle)
|
||||
})
|
||||
onMounted(updateTextAreaStyle)
|
||||
// other methods
|
||||
const methods = createMethods(props, formItem, {
|
||||
mergedValueRef,
|
||||
uncontrolledValueRef,
|
||||
isComposingRef,
|
||||
focusedRef,
|
||||
hoverRef,
|
||||
activatedRef,
|
||||
wrapperRef,
|
||||
inputRef,
|
||||
input2Ref,
|
||||
textareaRef,
|
||||
textareaMirrorRef
|
||||
})
|
||||
const focus = () => {
|
||||
if (props.disabled) return
|
||||
if (props.passivelyActivated) {
|
||||
wrapperRef.value.focus()
|
||||
} else {
|
||||
if (textareaRef.value) textareaRef.value.focus()
|
||||
else if (inputRef.value) inputRef.value.focus()
|
||||
}
|
||||
}
|
||||
const blur = () => {
|
||||
if (wrapperRef.value.contains(document.activeElement)) {
|
||||
document.activeElement.blur()
|
||||
}
|
||||
}
|
||||
const activate = () => {
|
||||
if (props.disabled) return
|
||||
if (textareaRef.value) textareaRef.value.focus()
|
||||
else if (inputRef.value) inputRef.value.focus()
|
||||
}
|
||||
const deactivate = () => {
|
||||
if (
|
||||
wrapperRef.value.contains(document.activeElement) &&
|
||||
wrapperRef.value !== document.activeElement
|
||||
) {
|
||||
methods.handleWrapperKeyDownEsc()
|
||||
}
|
||||
}
|
||||
return {
|
||||
// DOM ref
|
||||
wrapperRef,
|
||||
inputRef,
|
||||
input2Ref,
|
||||
textareaRef,
|
||||
textareaMirrorRef,
|
||||
// value
|
||||
uncontrolledValue: uncontrolledValueRef,
|
||||
mergedValue: mergedValueRef,
|
||||
mergedPlaceholder: mergedPlaceholderRef,
|
||||
showPlaceholder1: showPlaceholder1Ref,
|
||||
showPlaceholder2: showPlaceholder2Ref,
|
||||
showTextareaPlaceholder: showTextareaPlaceholderRef,
|
||||
mergedFocus: mergedFocusRef,
|
||||
isComposing: isComposingRef,
|
||||
activated: activatedRef,
|
||||
showClearButton,
|
||||
mergedSize: mergedSizeRef,
|
||||
textDecorationStyle: textDecorationStyleRef,
|
||||
// methods
|
||||
focus,
|
||||
blur,
|
||||
deactivate,
|
||||
activate,
|
||||
...methods,
|
||||
...useConfig(props),
|
||||
mergedTheme: themeRef,
|
||||
cssVars: computed(() => {
|
||||
const { value: size } = mergedSizeRef
|
||||
const {
|
||||
common: { cubicBezierEaseInOut },
|
||||
self: {
|
||||
color,
|
||||
borderRadius,
|
||||
paddingLeft,
|
||||
paddingRight,
|
||||
textColor,
|
||||
caretColor,
|
||||
textDecorationColor,
|
||||
border,
|
||||
borderDisabled,
|
||||
borderHover,
|
||||
borderFocus,
|
||||
placeholderColor,
|
||||
placeholderColorDisabled,
|
||||
lineHeightTextarea,
|
||||
colorDisabled,
|
||||
colorFocus,
|
||||
textColorDisabled,
|
||||
boxShadowFocus,
|
||||
iconSize,
|
||||
colorFocusWarning,
|
||||
boxShadowFocusWarning,
|
||||
boxShadowHoverWarning,
|
||||
borderWarning,
|
||||
borderFocusWarning,
|
||||
borderHoverWarning,
|
||||
colorFocusError,
|
||||
boxShadowFocusError,
|
||||
boxShadowHoverError,
|
||||
borderError,
|
||||
borderFocusError,
|
||||
borderHoverError,
|
||||
[createKey('fontSize', size)]: fontSize,
|
||||
[createKey('height', size)]: height
|
||||
}
|
||||
} = themeRef.value
|
||||
return {
|
||||
'--bezier': cubicBezierEaseInOut,
|
||||
'--color': color,
|
||||
'--font-size': fontSize,
|
||||
'--border-radius': borderRadius,
|
||||
'--height': height,
|
||||
'--padding-left': paddingLeft,
|
||||
'--padding-right': paddingRight,
|
||||
'--text-color': textColor,
|
||||
'--caret-color': caretColor,
|
||||
'--text-decoration-color': textDecorationColor,
|
||||
'--border': border,
|
||||
'--border-disabled': borderDisabled,
|
||||
'--border-hover': borderHover,
|
||||
'--border-focus': borderFocus,
|
||||
'--placeholder-color': placeholderColor,
|
||||
'--placeholder-color-disabled': placeholderColorDisabled,
|
||||
'--icon-size': iconSize,
|
||||
'--line-height-textarea': lineHeightTextarea,
|
||||
'--color-disabled': colorDisabled,
|
||||
'--color-focus': colorFocus,
|
||||
'--text-color-disabled': textColorDisabled,
|
||||
'--box-shadow-focus': boxShadowFocus,
|
||||
// form warning
|
||||
'--color-focus-warning': colorFocusWarning,
|
||||
'--box-shadow-focus-warning': boxShadowFocusWarning,
|
||||
'--box-shadow-hover-warning': boxShadowHoverWarning,
|
||||
'--border-warning': borderWarning,
|
||||
'--border-focus-warning': borderFocusWarning,
|
||||
'--border-hover-warning': borderHoverWarning,
|
||||
// form error
|
||||
'--color-focus-error': colorFocusError,
|
||||
'--box-shadow-focus-error': boxShadowFocusError,
|
||||
'--box-shadow-hover-error': boxShadowHoverError,
|
||||
'--border-error': borderError,
|
||||
'--border-focus-error': borderFocusError,
|
||||
'--border-hover-error': borderHoverError
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
13
src/input/src/InputGroup.tsx
Normal file
13
src/input/src/InputGroup.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { h, defineComponent, renderSlot } from 'vue'
|
||||
import { useStyle } from '../../_mixins'
|
||||
import style from './styles/input-group.cssr'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'InputGroup',
|
||||
setup () {
|
||||
useStyle('InputGroup', style)
|
||||
},
|
||||
render () {
|
||||
return <div class="n-input-group">{renderSlot(this.$slots, 'default')}</div>
|
||||
}
|
||||
})
|
@ -1,18 +0,0 @@
|
||||
<template>
|
||||
<div class="n-input-group">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
import { useStyle } from '../../_mixins'
|
||||
import style from './styles/input-group.cssr.js'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'InputGroup',
|
||||
setup (props) {
|
||||
useStyle('InputGroup', style)
|
||||
}
|
||||
})
|
||||
</script>
|
@ -1,27 +1,18 @@
|
||||
<template>
|
||||
<div
|
||||
class="n-input-group-label"
|
||||
:class="`n-input-group-label--${size}-size`"
|
||||
:style="cssVars"
|
||||
>
|
||||
<slot />
|
||||
<div class="n-input-group-label__border" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import { computed, defineComponent, renderSlot, h, PropType } from 'vue'
|
||||
import { useTheme } from '../../_mixins'
|
||||
import type { ThemeProps } from '../../_mixins'
|
||||
import { createKey } from '../../_utils'
|
||||
import { inputLight } from '../styles'
|
||||
import style from './styles/input-group-label.cssr.js'
|
||||
import type { InputTheme } from '../styles'
|
||||
import style from './styles/input-group-label.cssr'
|
||||
import type { Size } from './interface'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'InputGroupLabel',
|
||||
props: {
|
||||
...useTheme.props,
|
||||
...(useTheme.props as ThemeProps<InputTheme>),
|
||||
size: {
|
||||
type: String,
|
||||
type: String as PropType<Size>,
|
||||
default: 'medium'
|
||||
}
|
||||
},
|
||||
@ -43,8 +34,8 @@ export default defineComponent({
|
||||
borderRadius,
|
||||
textColor,
|
||||
lineHeight,
|
||||
fontSize,
|
||||
border,
|
||||
[createKey('fontSize', size)]: fontSize,
|
||||
[createKey('height', size)]: height
|
||||
}
|
||||
} = themeRef.value
|
||||
@ -60,6 +51,13 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
render () {
|
||||
return (
|
||||
<div class="n-input-group-label" style={this.cssVars as any}>
|
||||
{renderSlot(this.$slots, 'default')}
|
||||
<div class="n-input-group-label__border" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
</script>
|
1
src/input/src/interface.ts
Normal file
1
src/input/src/interface.ts
Normal file
@ -0,0 +1 @@
|
||||
export type Size = 'small' | 'medium' | 'large'
|
@ -22,7 +22,10 @@ import { cB, c, cE, cM, cNotM, insideFormItem } from '../../../_utils/cssr'
|
||||
// --color-disabled
|
||||
// --color-focus
|
||||
// --box-shadow-focus
|
||||
// --icon-size
|
||||
// --clear-color
|
||||
// --clear-size
|
||||
// --clear-color-hover
|
||||
// --clear-color-pressed
|
||||
export default c([
|
||||
cB('input', `
|
||||
line-height: 1.5;
|
||||
@ -241,7 +244,6 @@ export default c([
|
||||
}),
|
||||
c('&:hover', [
|
||||
cE('state-border', `
|
||||
box-shadow: var(--box-shadow-hover-${status});
|
||||
border: var(--border-hover-${status});
|
||||
`)
|
||||
]),
|
@ -1,5 +1,6 @@
|
||||
export default {
|
||||
paddingLeft: '14px',
|
||||
paddingRight: '8px',
|
||||
paddingIcon: '38px'
|
||||
paddingIcon: '38px',
|
||||
clearSize: '16px'
|
||||
}
|
@ -1,14 +1,11 @@
|
||||
import commonVariables from './_common'
|
||||
import { changeColor } from 'seemly'
|
||||
import { baseClearDark } from '../../_base/clear/styles'
|
||||
import { changeColor, scaleColor } from 'seemly'
|
||||
import { commonDark } from '../../_styles/new-common'
|
||||
import type { InputTheme } from './light'
|
||||
|
||||
export default {
|
||||
const inputDark: InputTheme = {
|
||||
name: 'Input',
|
||||
common: commonDark,
|
||||
peers: {
|
||||
BaseClear: baseClearDark
|
||||
},
|
||||
self (vars) {
|
||||
const {
|
||||
textColor2Overlay,
|
||||
@ -43,6 +40,7 @@ export default {
|
||||
fontSizeSmall,
|
||||
fontSizeMedium,
|
||||
fontSizeLarge,
|
||||
lineHeight,
|
||||
lineHeightTextarea: lineHeight,
|
||||
borderRadius,
|
||||
iconSize: '16px',
|
||||
@ -78,7 +76,12 @@ export default {
|
||||
boxShadowFocusError: `0 0 8px 0 ${changeColor(errorColor, {
|
||||
alpha: 0.3
|
||||
})}`,
|
||||
caretColorError: errorColor
|
||||
caretColorError: errorColor,
|
||||
clearColor: textColor4Overlay,
|
||||
clearColorHover: scaleColor(textColor4Overlay, { alpha: 1.25 }),
|
||||
clearColorPressed: scaleColor(textColor4Overlay, { alpha: 0.75 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default inputDark
|
@ -1,2 +0,0 @@
|
||||
export { default as inputDark } from './dark.js'
|
||||
export { default as inputLight } from './light.js'
|
3
src/input/styles/index.ts
Normal file
3
src/input/styles/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { default as inputDark } from './dark'
|
||||
export { default as inputLight } from './light'
|
||||
export type { InputTheme, InputThemeVars } from './light'
|
@ -1,86 +0,0 @@
|
||||
import commonVariables from './_common'
|
||||
import { changeColor } from 'seemly'
|
||||
import { baseClearLight } from '../../_base/clear/styles'
|
||||
import { commonLight } from '../../_styles/new-common'
|
||||
|
||||
export default {
|
||||
name: 'Input',
|
||||
common: commonLight,
|
||||
peers: {
|
||||
BaseClear: baseClearLight
|
||||
},
|
||||
self (vars) {
|
||||
const {
|
||||
textColor2,
|
||||
textColor4,
|
||||
textColor5,
|
||||
primaryColor,
|
||||
primaryColorHover,
|
||||
inputColor,
|
||||
inputColorDisabled,
|
||||
borderColor,
|
||||
warningColor,
|
||||
warningColorHover,
|
||||
errorColor,
|
||||
errorColorHover,
|
||||
borderRadius,
|
||||
lineHeight,
|
||||
fontSizeTiny,
|
||||
fontSizeSmall,
|
||||
fontSizeMedium,
|
||||
fontSizeLarge,
|
||||
heightTiny,
|
||||
heightSmall,
|
||||
heightMedium,
|
||||
heightLarge,
|
||||
actionColor
|
||||
} = vars
|
||||
return {
|
||||
...commonVariables,
|
||||
heightTiny,
|
||||
heightSmall,
|
||||
heightMedium,
|
||||
heightLarge,
|
||||
fontSizeTiny,
|
||||
fontSizeSmall,
|
||||
fontSizeMedium,
|
||||
fontSizeLarge,
|
||||
lineHeightTextarea: lineHeight,
|
||||
borderRadius,
|
||||
iconSize: '16px',
|
||||
groupLabelColor: actionColor,
|
||||
textColor: textColor2,
|
||||
textColorDisabled: textColor4,
|
||||
textDecorationColor: textColor2,
|
||||
caretColor: primaryColor,
|
||||
placeholderColor: textColor4,
|
||||
placeholderColorDisabled: textColor5,
|
||||
color: inputColor,
|
||||
colorDisabled: inputColorDisabled,
|
||||
colorFocus: inputColor,
|
||||
border: `1px solid ${borderColor}`,
|
||||
borderHover: `1px solid ${primaryColorHover}`,
|
||||
borderDisabled: `1px solid ${borderColor}`,
|
||||
borderFocus: `1px solid ${primaryColorHover}`,
|
||||
boxShadowFocus: `0 0 0 2px ${changeColor(primaryColor, { alpha: 0.2 })}`,
|
||||
// warning
|
||||
borderWarning: `1px solid ${warningColor}`,
|
||||
borderHoverWarning: `1px solid ${warningColorHover}`,
|
||||
colorFocusWarning: inputColor,
|
||||
borderFocusWarning: `1px solid ${warningColorHover}`,
|
||||
boxShadowFocusWarning: `0 0 0 2px ${changeColor(warningColor, {
|
||||
alpha: 0.2
|
||||
})}`,
|
||||
caretColorWarning: warningColor,
|
||||
// error
|
||||
borderError: `1px solid ${errorColor}`,
|
||||
borderHoverError: `1px solid ${errorColorHover}`,
|
||||
colorFocusError: inputColor,
|
||||
borderFocusError: `1px solid ${errorColorHover}`,
|
||||
boxShadowFocusError: `0 0 0 2px ${changeColor(errorColor, {
|
||||
alpha: 0.2
|
||||
})}`,
|
||||
caretColorError: errorColor
|
||||
}
|
||||
}
|
||||
}
|
95
src/input/styles/light.ts
Normal file
95
src/input/styles/light.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import commonVariables from './_common'
|
||||
import { changeColor, scaleColor } from 'seemly'
|
||||
import { commonLight } from '../../_styles/new-common'
|
||||
import type { ThemeCommonVars } from '../../_styles/new-common'
|
||||
import type { Theme } from '../../_mixins'
|
||||
|
||||
const self = (vars: ThemeCommonVars) => {
|
||||
const {
|
||||
textColor2,
|
||||
textColor4,
|
||||
textColor5,
|
||||
primaryColor,
|
||||
primaryColorHover,
|
||||
inputColor,
|
||||
inputColorDisabled,
|
||||
borderColor,
|
||||
warningColor,
|
||||
warningColorHover,
|
||||
errorColor,
|
||||
errorColorHover,
|
||||
borderRadius,
|
||||
lineHeight,
|
||||
fontSizeTiny,
|
||||
fontSizeSmall,
|
||||
fontSizeMedium,
|
||||
fontSizeLarge,
|
||||
heightTiny,
|
||||
heightSmall,
|
||||
heightMedium,
|
||||
heightLarge,
|
||||
actionColor
|
||||
} = vars
|
||||
return {
|
||||
...commonVariables,
|
||||
heightTiny,
|
||||
heightSmall,
|
||||
heightMedium,
|
||||
heightLarge,
|
||||
fontSizeTiny,
|
||||
fontSizeSmall,
|
||||
fontSizeMedium,
|
||||
fontSizeLarge,
|
||||
lineHeight,
|
||||
lineHeightTextarea: lineHeight,
|
||||
borderRadius,
|
||||
iconSize: '16px',
|
||||
groupLabelColor: actionColor,
|
||||
textColor: textColor2,
|
||||
textColorDisabled: textColor4,
|
||||
textDecorationColor: textColor2,
|
||||
caretColor: primaryColor,
|
||||
placeholderColor: textColor4,
|
||||
placeholderColorDisabled: textColor5,
|
||||
color: inputColor,
|
||||
colorDisabled: inputColorDisabled,
|
||||
colorFocus: inputColor,
|
||||
border: `1px solid ${borderColor}`,
|
||||
borderHover: `1px solid ${primaryColorHover}`,
|
||||
borderDisabled: `1px solid ${borderColor}`,
|
||||
borderFocus: `1px solid ${primaryColorHover}`,
|
||||
boxShadowFocus: `0 0 0 2px ${changeColor(primaryColor, { alpha: 0.2 })}`,
|
||||
// warning
|
||||
borderWarning: `1px solid ${warningColor}`,
|
||||
borderHoverWarning: `1px solid ${warningColorHover}`,
|
||||
colorFocusWarning: inputColor,
|
||||
borderFocusWarning: `1px solid ${warningColorHover}`,
|
||||
boxShadowFocusWarning: `0 0 0 2px ${changeColor(warningColor, {
|
||||
alpha: 0.2
|
||||
})}`,
|
||||
caretColorWarning: warningColor,
|
||||
// error
|
||||
borderError: `1px solid ${errorColor}`,
|
||||
borderHoverError: `1px solid ${errorColorHover}`,
|
||||
colorFocusError: inputColor,
|
||||
borderFocusError: `1px solid ${errorColorHover}`,
|
||||
boxShadowFocusError: `0 0 0 2px ${changeColor(errorColor, {
|
||||
alpha: 0.2
|
||||
})}`,
|
||||
caretColorError: errorColor,
|
||||
clearColor: textColor4,
|
||||
clearColorHover: scaleColor(textColor4, { lightness: 0.75 }),
|
||||
clearColorPressed: scaleColor(textColor4, { lightness: 0.9 })
|
||||
}
|
||||
}
|
||||
|
||||
export type InputThemeVars = ReturnType<typeof self>
|
||||
|
||||
const inputLight: Theme<InputThemeVars> = {
|
||||
name: 'Input',
|
||||
common: commonLight,
|
||||
self
|
||||
}
|
||||
|
||||
export default inputLight
|
||||
export type InputTheme = typeof inputLight
|
Loading…
Reference in New Issue
Block a user