feat(transfer): clsPrefix

This commit is contained in:
07akioni 2021-04-17 02:37:13 +08:00
parent 60689e81a5
commit a9a1a36fb6
8 changed files with 140 additions and 133 deletions

View File

@ -1,2 +1,2 @@
/* istanbul ignore file */
export { default as NTransfer } from './src/Transfer'
export type { TransferProps } from './src/Transfer'

View File

@ -13,10 +13,10 @@ import { depx } from 'seemly'
import { ChevronLeftIcon, ChevronRightIcon } from '../../_internal/icons'
import { NBaseIcon } from '../../_internal'
import { NButton } from '../../button'
import { useLocale, useFormItem, useTheme } from '../../_mixins'
import { useLocale, useFormItem, useTheme, useConfig } from '../../_mixins'
import type { ThemeProps } from '../../_mixins'
import { createKey } from '../../_utils/cssr'
import { warn, call } from '../../_utils'
import { warn, call, ExtractPublicPropTypes } from '../../_utils'
import type { MaybeArray } from '../../_utils'
import { transferLight } from '../styles'
import type { TransferTheme } from '../styles'
@ -30,75 +30,81 @@ import {
Option,
Filter,
OnUpdateValue,
TransferInjection
transferInjectionKey
} from './interface'
const transferProps = {
...(useTheme.props as ThemeProps<TransferTheme>),
value: Array as PropType<OptionValue[] | null>,
defaultValue: {
type: Array as PropType<OptionValue[] | null>,
default: null
},
options: {
type: Array as PropType<Option[]>,
default: () => []
},
disabled: {
type: Boolean,
default: false
},
virtualScroll: {
type: Boolean,
default: false
},
sourceTitle: String,
targetTitle: String,
filterable: {
type: Boolean,
default: false
},
sourceFilterPlaceholder: String,
targetFilterPlaceholder: String,
filter: {
type: Function as PropType<Filter>,
default: (pattern: string, option: Option) => {
if (!pattern) return true
return ~('' + option.label)
.toLowerCase()
.indexOf(('' + pattern).toLowerCase())
}
},
size: {
type: String as PropType<'small' | 'medium' | 'large' | undefined>,
default: undefined
},
// eslint-disable-next-line vue/prop-name-casing
'onUpdate:value': [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
onUpdateValue: [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
onChange: {
type: [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
validator: () => {
if (__DEV__) {
warn(
'transfer',
'`on-change` is deprecated, please use `on-update:value` instead.'
)
}
return true
},
default: undefined
}
} as const
export type TransferProps = ExtractPublicPropTypes<typeof transferProps>
export default defineComponent({
name: 'Transfer',
props: {
...(useTheme.props as ThemeProps<TransferTheme>),
value: Array as PropType<OptionValue[] | null>,
defaultValue: {
type: Array as PropType<OptionValue[] | null>,
default: null
},
options: {
type: Array as PropType<Option[]>,
default: () => []
},
disabled: {
type: Boolean,
default: false
},
virtualScroll: {
type: Boolean,
default: false
},
sourceTitle: String,
targetTitle: String,
filterable: {
type: Boolean,
default: false
},
sourceFilterPlaceholder: String,
targetFilterPlaceholder: String,
filter: {
type: Function as PropType<Filter>,
default: (pattern: string, option: Option) => {
if (!pattern) return true
return ~('' + option.label)
.toLowerCase()
.indexOf(('' + pattern).toLowerCase())
}
},
size: {
type: String as PropType<'small' | 'medium' | 'large' | undefined>,
default: undefined
},
// eslint-disable-next-line vue/prop-name-casing
'onUpdate:value': [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
onUpdateValue: [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
onChange: {
type: [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
validator: () => {
if (__DEV__) {
warn(
'transfer',
'`on-change` is deprecated, please use `on-update:value` instead.'
)
}
return true
},
default: undefined
}
},
props: transferProps,
setup (props) {
const { mergedClsPrefix } = useConfig(props)
const themeRef = useTheme(
'Transfer',
'Transfer',
style,
transferLight,
props
props,
mergedClsPrefix
)
const formItem = useFormItem(props)
const itemSizeRef = computed(() => {
@ -210,9 +216,10 @@ export default defineComponent({
)
tgtCheckedValuesRef.value = []
}
provide<TransferInjection>(
'NTransfer',
provide(
transferInjectionKey,
reactive({
cPrefix: mergedClsPrefix,
mergedSize: formItem.mergedSize,
disabled: toRef(props, 'disabled'),
mergedTheme: themeRef,
@ -229,6 +236,7 @@ export default defineComponent({
return {
...formItem,
...useLocale('Transfer'),
cPrefix: mergedClsPrefix,
itemSize: itemSizeRef,
isMounted: useIsMounted(),
isInputing: isInputingRef,
@ -305,24 +313,23 @@ export default defineComponent({
}
},
render () {
const { cPrefix } = this
return (
<div
class={[
'n-transfer',
{
'n-transfer--disabled': this.disabled,
'n-transfer--filterable': this.filterable
}
`${cPrefix}-transfer`,
this.disabled && `${cPrefix}-transfer--disabled`,
this.filterable && `${cPrefix}-transfer--filterable`
]}
style={this.cssVars as CSSProperties}
>
<div class="n-transfer-list">
<div class={`${cPrefix}-transfer-list`}>
<NTransferHeader
source
onChange={this.handleSrcHeaderCheck}
title={this.sourceTitle || this.locale.sourceTitle}
/>
<div class="n-transfer-list-body">
<div class={`${cPrefix}-transfer-list-body`}>
{this.filterable ? (
<NTransferFilter
onUpdateValue={this.handleSrcFilterUpdateValue}
@ -333,7 +340,7 @@ export default defineComponent({
onBlur={this.handleInputBlur}
/>
) : null}
<div class="n-transfer-list-flex-container">
<div class={`${cPrefix}-transfer-list-flex-container`}>
<NTransferList
source
options={this.filteredSrcOpts}
@ -345,9 +352,9 @@ export default defineComponent({
/>
</div>
</div>
<div class="n-transfer-list__border" />
<div class={`${cPrefix}-transfer-list__border`} />
</div>
<div class="n-transfer-gap">
<div class={`${cPrefix}-transfer-gap`}>
<NButton
disabled={this.toButtonDisabled || this.disabled}
theme={this.mergedTheme.peers.Button}
@ -356,7 +363,9 @@ export default defineComponent({
>
{{
icon: () => (
<NBaseIcon>{{ default: () => <ChevronRightIcon /> }}</NBaseIcon>
<NBaseIcon clsPrefix={cPrefix}>
{{ default: () => <ChevronRightIcon /> }}
</NBaseIcon>
)
}}
</NButton>
@ -368,17 +377,19 @@ export default defineComponent({
>
{{
icon: () => (
<NBaseIcon>{{ default: () => <ChevronLeftIcon /> }}</NBaseIcon>
<NBaseIcon clsPrefix={cPrefix}>
{{ default: () => <ChevronLeftIcon /> }}
</NBaseIcon>
)
}}
</NButton>
</div>
<div class="n-transfer-list">
<div class={`${cPrefix}-transfer-list`}>
<NTransferHeader
onChange={this.handleTgtHeaderCheck}
title={this.targetTitle || this.locale.targetTitle}
/>
<div class="n-transfer-list-body">
<div class={`${cPrefix}-transfer-list-body`}>
{this.filterable ? (
<NTransferFilter
onUpdateValue={this.handleTgtFilterUpdateValue}
@ -389,7 +400,7 @@ export default defineComponent({
onBlur={this.handleInputBlur}
/>
) : null}
<div class="n-transfer-list-flex-container">
<div class={`${cPrefix}-transfer-list-flex-container`}>
<NTransferList
options={this.filteredTgtOpts}
disabled={this.disabled}
@ -400,7 +411,7 @@ export default defineComponent({
/>
</div>
</div>
<div class="n-transfer-list__border" />
<div class={`${cPrefix}-transfer-list__border`} />
</div>
</div>
)

View File

@ -2,7 +2,7 @@ import { h, defineComponent, inject, PropType } from 'vue'
import { SearchIcon } from '../../_internal/icons'
import { NBaseIcon } from '../../_internal'
import { NInput } from '../../input'
import { TransferInjection } from './interface'
import { transferInjectionKey } from './interface'
export default defineComponent({
name: 'TransferFilter',
@ -24,23 +24,23 @@ export default defineComponent({
}
},
setup () {
const NTransfer = inject<TransferInjection>(
'NTransfer'
) as TransferInjection
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const NTransfer = inject(transferInjectionKey)!
return {
NTransfer
}
},
render () {
const { NTransfer } = this
const { mergedTheme, cPrefix } = NTransfer
return (
<div class="n-transfer-filter">
<div class={`${cPrefix}-transfer-filter`}>
<NInput
value={this.value}
onUpdateValue={this.onUpdateValue}
disabled={this.disabled}
theme={NTransfer.mergedTheme.peers.Input}
themeOverrides={NTransfer.mergedTheme.peerOverrides.Input}
theme={mergedTheme.peers.Input}
themeOverrides={mergedTheme.peerOverrides.Input}
clearable
size="small"
placeholder={this.placeholder}
@ -49,7 +49,7 @@ export default defineComponent({
>
{{
clear: () => (
<NBaseIcon class="n-transfer-icon">
<NBaseIcon clsPrefix={cPrefix} class={`${cPrefix}-transfer-icon`}>
{{ default: () => <SearchIcon /> }}
</NBaseIcon>
)

View File

@ -1,6 +1,6 @@
import { h, computed, defineComponent, inject, PropType } from 'vue'
import { NCheckbox } from '../../checkbox'
import { TransferInjection } from './interface'
import { transferInjectionKey } from './interface'
export default defineComponent({
name: 'TransferHeader',
@ -16,9 +16,8 @@ export default defineComponent({
title: String
},
setup (props) {
const NTransfer = inject<TransferInjection>(
'NTransfer'
) as TransferInjection
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const NTransfer = inject(transferInjectionKey)!
const checkboxPropsRef = computed(() => {
const { source } = props
if (source) {
@ -30,20 +29,23 @@ export default defineComponent({
return () => {
const { source } = props
const { value: checkboxProps } = checkboxPropsRef
const { mergedTheme, cPrefix } = NTransfer
return (
<div class="n-transfer-list-header">
<div class="n-transfer-list-header__checkbox">
<div class={`${cPrefix}-transfer-list-header`}>
<div class={`${cPrefix}-transfer-list-header__checkbox`}>
<NCheckbox
theme={NTransfer.mergedTheme.peers.Checkbox}
themeOverrides={NTransfer.mergedTheme.peerOverrides.Checkbox}
theme={mergedTheme.peers.Checkbox}
themeOverrides={mergedTheme.peerOverrides.Checkbox}
checked={checkboxProps.checked}
indeterminate={checkboxProps.indeterminate}
disabled={checkboxProps.disabled || NTransfer.disabled}
onUpdateChecked={props.onChange}
/>
</div>
<div class="n-transfer-list-header__header">{props.title}</div>
<div class="n-transfer-list-header__extra">
<div class={`${cPrefix}-transfer-list-header__header`}>
{props.title}
</div>
<div class={`${cPrefix}-transfer-list-header__extra`}>
{source
? NTransfer.srcCheckedValues.length
: NTransfer.tgtCheckedValues.length}

View File

@ -10,7 +10,7 @@ import {
import { VirtualList, VirtualListRef } from 'vueuc'
import { NEmpty } from '../../empty'
import { NScrollbar, ScrollbarInst } from '../../scrollbar'
import type { Option, TransferInjection } from './interface'
import { Option, transferInjectionKey } from './interface'
import NTransferListItem from './TransferListItem'
export default defineComponent({
@ -46,9 +46,8 @@ export default defineComponent({
}
},
setup () {
const NTransfer = inject<TransferInjection>(
'NTransfer'
) as TransferInjection
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const NTransfer = inject(transferInjectionKey)!
const scrollerInstRef = ref<ScrollbarInst | null>(null)
const vlInstRef = ref<VirtualListRef | null>(null)
function syncVLScroller (): void {
@ -77,12 +76,13 @@ export default defineComponent({
},
render () {
const { NTransfer, syncVLScroller } = this
const { mergedTheme, cPrefix } = NTransfer
return this.options.length ? (
this.virtualScroll ? (
<NScrollbar
ref="scrollerInstRef"
theme={NTransfer.mergedTheme.peers.Scrollbar}
themeOverrides={NTransfer.mergedTheme.peerOverrides.Scrollbar}
theme={mergedTheme.peers.Scrollbar}
themeOverrides={mergedTheme.peerOverrides.Scrollbar}
container={this.scrollContainer}
content={this.scrollContent}
>
@ -90,7 +90,8 @@ export default defineComponent({
default: () => (
<VirtualList
ref="srcVlInstRef"
class="n-virtual-scroller n-transfer-list-content"
style={{ height: '100%' }}
class={`${cPrefix}-transfer-list-content`}
items={this.options}
itemSize={this.itemSize}
showScrollbar={false}
@ -122,7 +123,7 @@ export default defineComponent({
>
{{
default: () => (
<div class="n-transfer-list-content">
<div class={`${cPrefix}-transfer-list-content`}>
<TransitionGroup
name="item"
appear={this.isMounted}

View File

@ -1,7 +1,7 @@
import { h, inject, defineComponent } from 'vue'
import { useMemo } from 'vooks'
import { NCheckbox } from '../../checkbox'
import type { TransferInjection } from './interface'
import { transferInjectionKey } from './interface'
export default defineComponent({
name: 'NTransferListItem',
@ -25,9 +25,8 @@ export default defineComponent({
},
setup (props) {
const { source } = props
const NTransfer = inject<TransferInjection>(
'NTransfer'
) as TransferInjection
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const NTransfer = inject(transferInjectionKey)!
const checkedRef = source
? useMemo(() => NTransfer.srcCheckedValues.includes(props.value))
: useMemo(() => NTransfer.tgtCheckedValues.includes(props.value))
@ -45,34 +44,32 @@ export default defineComponent({
return {
NTransfer,
checked: checkedRef,
className: source
? 'n-transfer-list-item--source'
: 'n-transfer-list-item--target',
handleClick
}
},
render () {
const { disabled, NTransfer, label, checked, className } = this
const { disabled, NTransfer, label, checked, source } = this
const { mergedTheme, cPrefix } = NTransfer
return (
<div
class={[
'n-transfer-list-item',
className,
{
'n-transfer-list-item--disabled': disabled
}
`${cPrefix}-transfer-list-item`,
disabled && `${cPrefix}-transfer-list-item--disabled`,
source
? `${cPrefix}-transfer-list-item--source`
: `${cPrefix}-transfer-list-item--target`
]}
onClick={this.handleClick}
>
<div class="n-transfer-list-item__checkbox">
<div class={`${cPrefix}-transfer-list-item__checkbox`}>
<NCheckbox
theme={NTransfer.mergedTheme.peers.Checkbox}
themeOverrides={NTransfer.mergedTheme.peerOverrides.Checkbox}
theme={mergedTheme.peers.Checkbox}
themeOverrides={mergedTheme.peerOverrides.Checkbox}
disabled={disabled}
checked={checked}
/>
</div>
<div class="n-transfer-list-item__label">{label}</div>
<div class={`${cPrefix}-transfer-list-item__label`}>{label}</div>
</div>
)
}

View File

@ -1,3 +1,4 @@
import { InjectionKey } from 'vue'
import type { MergedTheme } from '../../_mixins'
import type { TransferTheme } from '../styles'
@ -21,6 +22,7 @@ export type Filter = (
) => boolean
export interface TransferInjection {
cPrefix: string
mergedSize: 'small' | 'medium' | 'large'
disabled: boolean
mergedTheme: MergedTheme<TransferTheme>
@ -34,4 +36,8 @@ export interface TransferInjection {
handleTgtCheckboxClick: (checked: boolean, value: OptionValue) => void
}
export const transferInjectionKey: InjectionKey<TransferInjection> = Symbol(
'transfer'
)
export type OnUpdateValue = (value: OptionValue[]) => void

View File

@ -82,16 +82,6 @@ export default c([
border-radius: var(--border-radius);
background-color: var(--list-color);
`, [
cB('virtual-scroller', {
height: '100%',
scrollbarWidth: 'none',
'-moz-scrollbar-width': 'none'
}, [
c('&::-webkit-scrollbar', {
width: 0,
height: 0
})
]),
cE('border', `
border: 1px solid var(--border-color);
transition: border-color .3s var(--bezier);