feat(collapse): clsPrefix

This commit is contained in:
07akioni 2021-04-16 01:35:58 +08:00
parent c4487319fb
commit 18573f9157
4 changed files with 149 additions and 106 deletions

View File

@ -1,3 +1,4 @@
/* istanbul ignore file */
export { default as NCollapse } from './src/Collapse'
export { default as NCollapseItem } from './src/CollapseItem'
export type { CollapseProps } from './src/Collapse'
export type { CollapseItemProps } from './src/CollapseItem'

View File

@ -3,15 +3,17 @@ import {
h,
defineComponent,
PropType,
toRef,
reactive,
provide,
ref
ref,
InjectionKey,
Ref,
ExtractPropTypes,
CSSProperties
} from 'vue'
import { intersection } from 'lodash-es'
import { useTheme } from '../../_mixins'
import { useConfig, useTheme } from '../../_mixins'
import type { ThemeProps } from '../../_mixins'
import { call, warn } from '../../_utils'
import { call, ExtractPublicPropTypes, warn } from '../../_utils'
import type { MaybeArray } from '../../_utils'
import { collapseLight, CollapseTheme } from '../styles'
import style from './styles/index.cssr'
@ -24,10 +26,60 @@ import {
OnItemHeaderClickImpl
} from './interface'
const collapseProps = {
...(useTheme.props as ThemeProps<CollapseTheme>),
defaultExpandesNames: [Array, String] as PropType<
string | number | Array<string | number> | null
>,
expandedNames: [Array, String] as PropType<
string | number | Array<string | number> | null
>,
arrowPlacement: {
type: String as PropType<'left' | 'right'>,
default: 'left'
},
accordion: {
type: Boolean,
default: false
},
displayDirective: {
type: String as PropType<'if' | 'show'>,
default: 'if'
},
onItemHeaderClick: [Function, Array] as PropType<
MaybeArray<OnItemHeaderClick>
>,
// eslint-disable-next-line vue/prop-name-casing
'onUpdate:expandedNames': [Function, Array] as PropType<
MaybeArray<OnUpdateExpandedNames>
>,
onUpdateExpandedNames: [Function, Array] as PropType<
MaybeArray<OnUpdateExpandedNames>
>,
// deprecated
onExpandedNamesChange: {
type: [Function, Array] as PropType<
MaybeArray<OnUpdateExpandedNames> | undefined
>,
validator: () => {
if (__DEV__) {
warn(
'collapse',
'`on-expanded-names-change` is deprecated, please use `on-update:expanded-names` instead.'
)
}
return true
},
default: undefined
}
} as const
export type CollapseProps = ExtractPublicPropTypes<typeof collapseProps>
export interface NCollapseInjection {
arrowPlacement: 'left' | 'right'
displayDirective: 'if' | 'show'
expandedNames: string | number | Array<string | number> | null
props: ExtractPropTypes<typeof collapseProps>
expandedNamesRef: Ref<string | number | Array<string | number> | null>
clsPrefixRef: Ref<string>
collectedItemNames: Array<string | number>
toggleItem: (
collapse: boolean,
@ -36,61 +88,20 @@ export interface NCollapseInjection {
) => void
}
export const collapseInjectionKey: InjectionKey<NCollapseInjection> = Symbol(
'collapse'
)
export default defineComponent({
name: 'Collapse',
props: {
...(useTheme.props as ThemeProps<CollapseTheme>),
defaultExpandesNames: [Array, String] as PropType<
string | number | Array<string | number> | null
>,
expandedNames: [Array, String] as PropType<
string | number | Array<string | number> | null
>,
arrowPlacement: {
type: String as PropType<'left' | 'right'>,
default: 'left'
},
accordion: {
type: Boolean,
default: false
},
displayDirective: {
type: String as PropType<'if' | 'show'>,
default: 'if'
},
onItemHeaderClick: [Function, Array] as PropType<
MaybeArray<OnItemHeaderClick>
>,
// eslint-disable-next-line vue/prop-name-casing
'onUpdate:expandedNames': [Function, Array] as PropType<
MaybeArray<OnUpdateExpandedNames>
>,
onUpdateExpandedNames: [Function, Array] as PropType<
MaybeArray<OnUpdateExpandedNames>
>,
// deprecated
onExpandedNamesChange: {
type: [Function, Array] as PropType<
MaybeArray<OnUpdateExpandedNames> | undefined
>,
validator: () => {
if (__DEV__) {
warn(
'collapse',
'`on-expanded-names-change` is deprecated, please use `on-update:expanded-names` instead.'
)
}
return true
},
default: undefined
}
},
props: collapseProps,
setup (props) {
const { mergedClsPrefix } = useConfig(props)
const uncontrolledExpandedNamesRef = ref<
string | number | Array<string | number> | null
>(null)
const controlledExpandedNamesRef = computed(() => props.expandedNames)
const mergedExpandedNames = useMergedState(
const mergedExpandedNamesRef = useMergedState(
controlledExpandedNamesRef,
uncontrolledExpandedNamesRef
)
@ -100,7 +111,8 @@ export default defineComponent({
'Collapse',
style,
collapseLight,
props
props,
mergedClsPrefix
)
function doUpdateExpandedNames (
names: Array<string | number> | string | number
@ -135,7 +147,7 @@ export default defineComponent({
event: MouseEvent
): void {
const { accordion } = props
const { value: expandedNames } = mergedExpandedNames
const { value: expandedNames } = mergedExpandedNamesRef
if (accordion) {
if (collapse) {
doUpdateExpandedNames([name])
@ -165,18 +177,16 @@ export default defineComponent({
}
}
}
provide<NCollapseInjection>(
'NCollapse',
reactive({
arrowPlacement: toRef(props, 'arrowPlacement'),
displayDirective: toRef(props, 'displayDirective'),
expandedNames: mergedExpandedNames,
collectedItemNames,
toggleItem
})
)
provide(collapseInjectionKey, {
props,
clsPrefixRef: mergedClsPrefix,
expandedNamesRef: mergedExpandedNamesRef,
collectedItemNames,
toggleItem
})
return {
mergedTheme: themeRef,
cPrefix: mergedClsPrefix,
cssVars: computed(() => {
const {
common: { cubicBezierEaseInOut },
@ -204,13 +214,13 @@ export default defineComponent({
}
},
render () {
return h(
'div',
{
class: 'n-collapse',
style: this.cssVars
},
this.$slots
return (
<div
class={`${this.cPrefix}-collapse`}
style={this.cssVars as CSSProperties}
>
{this.$slots}
</div>
)
}
})

View File

@ -1,30 +1,45 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import { h, defineComponent, PropType, inject, computed, renderSlot } from 'vue'
import { createId } from 'seemly'
import { ChevronRightIcon as ArrowIcon } from '../../_internal/icons'
import { NBaseIcon } from '../../_internal'
import { useInjectionCollection } from '../../_utils/composable'
import NCollapseItemContent from './CollapseItemContent'
import { NCollapseInjection } from './Collapse'
import { useMemo } from 'vooks'
import { ChevronRightIcon as ArrowIcon } from '../../_internal/icons'
import { useInjectionCollection } from '../../_utils/composable'
import { NBaseIcon } from '../../_internal'
import { ExtractPublicPropTypes, throwError } from '../../_utils'
import { collapseInjectionKey } from './Collapse'
import NCollapseItemContent from './CollapseItemContent'
const collapseItemProps = {
title: String,
name: [String, Number] as PropType<string | number>,
displayDirective: String as PropType<'if' | 'show'>
} as const
export type CollapseItemProps = ExtractPublicPropTypes<typeof collapseItemProps>
export default defineComponent({
name: 'CollapseItem',
props: {
title: String,
name: [String, Number],
displayDirective: String as PropType<'if' | 'show'>
},
props: collapseItemProps,
setup (props) {
const randomName = createId()
const mergedNameRef = useMemo(() => {
return props.name ?? randomName
})
const NCollapse = inject<NCollapseInjection>(
'NCollapse'
) as NCollapseInjection
useInjectionCollection('NCollapse', 'collectedItemNames', mergedNameRef)
const NCollapse = inject(collapseInjectionKey)
if (!NCollapse) {
throwError(
'collapse-item',
'`n-collapse-item` must be placed inside `n-collapse`.'
)
}
const { expandedNamesRef, props: collapseProps, clsPrefixRef } = NCollapse
useInjectionCollection(
collapseInjectionKey,
'collectedItemNames',
mergedNameRef
)
const collapsedRef = computed<boolean>(() => {
const { expandedNames } = NCollapse
const { value: expandedNames } = expandedNamesRef
if (Array.isArray(expandedNames)) {
const { value: name } = mergedNameRef
return !~expandedNames.findIndex(
@ -35,17 +50,18 @@ export default defineComponent({
})
return {
randomName,
cPrefix: clsPrefixRef,
collapsed: collapsedRef,
mergedDisplayDirective: computed<'if' | 'show'>(() => {
const { displayDirective } = props
if (displayDirective) {
return displayDirective
} else {
return NCollapse.displayDirective
return collapseProps.displayDirective
}
}),
arrowPlacement: computed<'left' | 'right'>(() => {
return NCollapse.arrowPlacement
return collapseProps.arrowPlacement
}),
handleClick (e: MouseEvent) {
if (NCollapse) {
@ -60,33 +76,37 @@ export default defineComponent({
arrowPlacement,
collapsed,
title,
mergedDisplayDirective
mergedDisplayDirective,
cPrefix
} = this
const headerNode = renderSlot($slots, 'header', undefined, () => [title])
return (
<div
class={[
'n-collapse-item',
`n-collapse-item--${arrowPlacement}-arrow-placement`,
!collapsed && 'n-collapse-item--active'
`${cPrefix}-collapse-item`,
`${cPrefix}-collapse-item--${arrowPlacement}-arrow-placement`,
!collapsed && `${cPrefix}-collapse-item--active`
]}
>
<div
class={[
'n-collapse-item__header',
!collapsed && 'n-collapse-item__header--active'
`${cPrefix}-collapse-item__header`,
!collapsed && `${cPrefix}-collapse-item__header--active`
]}
onClick={this.handleClick}
>
{arrowPlacement === 'right' && headerNode}
<div class="n-collapse-item-arrow">
<div class={`${cPrefix}-collapse-item-arrow`}>
{renderSlot($slots, 'arrow', { collapsed: collapsed }, () => [
<NBaseIcon>{{ default: () => <ArrowIcon /> }}</NBaseIcon>
<NBaseIcon clsPrefix={cPrefix}>
{{ default: () => <ArrowIcon /> }}
</NBaseIcon>
])}
</div>
{arrowPlacement === 'left' && headerNode}
</div>
<NCollapseItemContent
clsPrefix={cPrefix}
displayDirective={mergedDisplayDirective}
show={!collapsed}
>

View File

@ -1,10 +1,20 @@
import { h, withDirectives, vShow, defineComponent, toRef } from 'vue'
import { h, withDirectives, vShow, defineComponent, toRef, PropType } from 'vue'
import { useFalseUntilTruthy } from 'vooks'
import { NFadeInExpandTransition } from '../../_internal'
export default defineComponent({
name: 'CollapseItemContent',
props: ['displayDirective', 'show'],
props: {
displayDirective: {
type: String as PropType<'if' | 'show'>,
required: true
},
show: Boolean,
clsPrefix: {
type: String,
required: true
}
},
setup (props) {
const onceTrueRef = useFalseUntilTruthy(toRef(props, 'show'))
return {
@ -16,11 +26,13 @@ export default defineComponent({
<NFadeInExpandTransition>
{{
default: () => {
const { show, displayDirective, onceTrue } = this
const { show, displayDirective, onceTrue, clsPrefix } = this
const useVShow = displayDirective === 'show' && onceTrue
const contentNode = (
<div class="n-collapse-item__content-wrapper">
<div class="n-collapse-item__content-inner">{this.$slots}</div>
<div class={`${clsPrefix}-collapse-item__content-wrapper`}>
<div class={`${clsPrefix}-collapse-item__content-inner`}>
{this.$slots}
</div>
</div>
)
return useVShow