refactor(collapse): ts

This commit is contained in:
07akioni 2021-01-15 00:37:35 +08:00
parent b907e295d7
commit a976480bf1
10 changed files with 250 additions and 201 deletions

View File

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

View File

@ -1,27 +1,37 @@
import { computed, h, markRaw, defineComponent } from 'vue'
import {
computed,
h,
defineComponent,
PropType,
toRef,
reactive,
provide
} from 'vue'
import { intersection } from 'lodash-es'
import { useTheme } from '../../_mixins'
import { call, warn } from '../../_utils'
import { collapseLight } from '../styles'
import style from './styles/index.cssr.js'
import type { CollapseThemeVars } from '../styles'
import style from './styles/index.cssr'
export interface NCollapseInjection {
arrowPlacement: 'left' | 'right'
displayDirective: 'if' | 'show'
expandedNames: string | string[]
collectedItemNames: string[]
toggleItem(collapse: boolean, name: string, event: MouseEvent): void
}
export default defineComponent({
name: 'Collapse',
provide () {
return {
NCollapse: this
}
},
props: {
...useTheme.props,
expandedNames: {
type: [Array, String],
type: [Array, String] as PropType<string | string[]>,
default: undefined
},
arrowPlacement: {
validator (value) {
return ['left', 'right'].includes(value)
},
type: String as PropType<'left' | 'right'>,
default: 'left'
},
accordion: {
@ -29,9 +39,7 @@ export default defineComponent({
default: false
},
displayDirective: {
validator (value) {
return ['if', 'show'].includes(value)
},
type: String as PropType<'if' | 'show'>,
default: 'if'
},
onItemHeaderClick: {
@ -40,12 +48,13 @@ export default defineComponent({
},
// eslint-disable-next-line vue/prop-name-casing
'onUpdate:expandedNames': {
type: Function,
type: Function as PropType<(expandedNames: string[]) => void>,
default: undefined
},
// deprecated
onExpandedNamesChange: {
validator () {
type: Function,
validator: () => {
if (__DEV__) {
warn(
'collapse',
@ -58,15 +67,72 @@ export default defineComponent({
}
},
setup (props) {
const themeRef = useTheme(
const collectedItemNames: string[] = []
const themeRef = useTheme<CollapseThemeVars>(
'Collapse',
'Collapse',
style,
collapseLight,
props
)
function doUpdateExpandedNames (names: string[]) {
const {
'onUpdate:expandedNames': updateExpandedNames,
onExpandedNamesChange
} = props
if (updateExpandedNames) call(updateExpandedNames, names)
if (onExpandedNamesChange) call(onExpandedNamesChange, names)
}
function doItemHeaderClick (info: {
name: string
expanded: boolean
event: MouseEvent
}) {
const { onItemHeaderClick } = props
if (onItemHeaderClick) call(onItemHeaderClick, info)
}
function toggleItem (collapse: boolean, name: string, event: MouseEvent) {
const { accordion, expandedNames } = props
if (accordion) {
if (collapse) {
doUpdateExpandedNames([name])
doItemHeaderClick({ name, expanded: true, event })
} else {
doUpdateExpandedNames([])
doItemHeaderClick({ name, expanded: false, event })
}
} else {
if (!Array.isArray(expandedNames)) {
doUpdateExpandedNames([name])
doItemHeaderClick({ name, expanded: true, event })
} else {
const activeNames = intersection(expandedNames, collectedItemNames)
const index = activeNames.findIndex(
(activeName) => name === activeName
)
if (~index) {
activeNames.splice(index, 1)
doUpdateExpandedNames(activeNames)
doItemHeaderClick({ name, expanded: false, event })
} else {
activeNames.push(name)
doUpdateExpandedNames(activeNames)
doItemHeaderClick({ name, expanded: true, event })
}
}
}
}
provide<NCollapseInjection>(
'NCollapse',
reactive({
arrowPlacement: toRef(props, 'arrowPlacement'),
displayDirective: toRef(props, 'displayDirective'),
expandedNames: toRef(props, 'expandedNames'),
collectedItemNames,
toggleItem
})
)
return {
collectedItemNames: markRaw([]),
mergedTheme: themeRef,
cssVars: computed(() => {
const {
@ -94,53 +160,6 @@ export default defineComponent({
})
}
},
methods: {
doUpdateExpandedNames (...args) {
const {
'onUpdate:expandedNames': updateExpandedNames,
onExpandedNamesChange
} = this
if (updateExpandedNames) call(updateExpandedNames, ...args)
if (onExpandedNamesChange) call(onExpandedNamesChange, ...args)
},
doItemHeaderClick (...args) {
const { onItemHeaderClick } = this
if (onItemHeaderClick) call(onItemHeaderClick, ...args)
},
toggleItem (collapse, name, event) {
if (this.accordion) {
if (collapse) {
this.doUpdateExpandedNames([name])
this.doItemHeaderClick({ name, expanded: true, event })
} else {
this.doUpdateExpandedNames([])
this.doItemHeaderClick({ name, expanded: false, event })
}
} else {
if (!Array.isArray(this.expandedNames)) {
this.doUpdateExpandedNames([name])
this.doItemHeaderClick({ name, expanded: true, event })
} else {
const activeNames = intersection(
this.expandedNames,
this.collectedItemNames
)
const index = activeNames.findIndex(
(activeName) => name === activeName
)
if (~index) {
activeNames.splice(index, 1)
this.doUpdateExpandedNames(activeNames)
this.doItemHeaderClick({ name, expanded: false, event })
} else {
activeNames.push(name)
this.doUpdateExpandedNames(activeNames)
this.doItemHeaderClick({ name, expanded: true, event })
}
}
}
}
},
render () {
return h(
'div',

View File

@ -36,12 +36,13 @@
</div>
</template>
<script>
import { toRef, defineComponent } from 'vue'
<script lang="ts">
import { toRef, defineComponent, PropType, inject, computed } from 'vue'
import { ChevronRightIcon as ArrowIcon } from '../../_base/icons'
import { NBaseIcon } from '../../_base'
import { useInjectionCollection } from '../../_utils/composable'
import NCollapseItemContent from './CollapseItemContent.js'
import NCollapseItemContent from './CollapseItemContent'
import { NCollapseInjection } from './Collapse'
export default defineComponent({
name: 'CollapseItem',
@ -50,60 +51,56 @@ export default defineComponent({
NCollapseItemContent,
ArrowIcon
},
inject: {
NCollapse: {
default: null
}
},
props: {
title: {
type: String,
default: undefined
},
name: {
type: [String, Number],
type: String,
default: undefined
},
displayDirective: {
validator (value) {
return ['if', 'show'].includes(value)
},
type: String as PropType<'if' | 'show' | undefined>,
default: undefined
}
},
setup (props) {
const NCollapse = inject<NCollapseInjection>(
'NCollapse'
) as NCollapseInjection
useInjectionCollection(
'NCollapse',
'collectedItemNames',
toRef(props, 'name')
)
},
computed: {
mergedDisplayDirective () {
const { displayDirective, NCollapse } = this
const collapsedRef = computed<boolean>(() => {
const { expandedNames } = NCollapse
if (Array.isArray(expandedNames)) {
const { name } = props
return !~expandedNames.findIndex(
(expandedName) => expandedName === name
)
}
return true
})
return {
collapsed: collapsedRef,
mergedDisplayDirective: computed<'if' | 'show'>(() => {
const { displayDirective } = props
if (displayDirective) {
return displayDirective
} else {
return NCollapse.displayDirective
}
},
arrowPlacement () {
return this.NCollapse.arrowPlacement
},
collapsed () {
const { NCollapse } = this
if (NCollapse && Array.isArray(NCollapse.expandedNames)) {
const itemName = this.name
return !~NCollapse.expandedNames.findIndex((name) => name === itemName)
}
return true
}
},
methods: {
handleClick (e) {
const { NCollapse } = this
}),
arrowPlacement: computed<'left' | 'right'>(() => {
return NCollapse.arrowPlacement
}),
handleClick (e: MouseEvent) {
if (NCollapse) {
NCollapse.toggleItem(this.collapsed, this.name, e)
NCollapse.toggleItem(collapsedRef.value, props.name, e)
}
}
}
}

View File

@ -1,4 +1,10 @@
import { h, withDirectives, vShow, defineComponent } from 'vue'
import {
h,
withDirectives,
vShow,
defineComponent,
DirectiveArguments
} from 'vue'
import { NFadeInExpandTransition } from '../../_base'
export default defineComponent({
@ -22,7 +28,7 @@ export default defineComponent({
render () {
const { show, displayDirective } = this
const useVShow = displayDirective === 'show'
const directives = useVShow ? [[vShow, show]] : []
const directives: DirectiveArguments = useVShow ? [[vShow, show]] : []
return h(NFadeInExpandTransition, null, {
default: () =>
useVShow || show

View File

@ -1,91 +0,0 @@
import { c, cB, cE, cM } from '../../../_utils/cssr'
import fadeInHeightExpandTransition from '../../../_styles/transitions/fade-in-height-expand'
// vars:
// --font-size
// --bezier
// --text-color
// --divider-color
// --title-font-size
// --title-text-color
// --title-font-weight
// --arrow-color
export default cB('collapse', {
width: '100%'
}, [
cB('collapse-item', `
overflow: hidden;
font-size: var(--font-size);
transition: border-color .3s var(--bezier);
margin-top: 16px;
`, [
c('&:first-child', {
marginTop: 0
}),
c('&:first-child >', [
cE('header', {
paddingTop: 0
})
]),
cM('left-arrow-placement', [
cE('header', [
cB('collapse-item-arrow', {
marginRight: '4px'
})
])
]),
cM('right-arrow-placement', [
cE('header', [
cB('collapse-item-arrow', {
marginLeft: '4px'
})
])
]),
cB('collapse-item', {
marginLeft: '32px'
}),
cE('content-wrapper', {
overflow: 'hidden'
}, [
fadeInHeightExpandTransition({ duration: '0.15s' })
]),
cM('active', [
cE('header', [
cM('active', [
cB('collapse-item-arrow', {
transform: 'rotate(90deg)'
})
])
])
]),
c('&:not(:first-child)', {
borderTop: '1px solid var(--divider-color)'
}),
cE('header', `
font-size: var(--title-font-size);
display: flex;
flex-wrap: nowrap;
align-items: center;
font-weight: var(--title-font-weight);
transition: color .3s var(--bezier);
position: relative;
cursor: pointer;
padding: 16px 0 0 0;
color: var(--title-text-color);
`, [
cB('collapse-item-arrow', `
display: flex;
transition:
transform .15s var(--bezier),
color .3s var(--bezier);
font-size: 18px;
color: var(--arrow-color);
`)
]),
cE('content-inner', `
transition: color .3s var(--bezier);
padding-top: 16px;
color: var(--text-color);
`)
])
])

View File

@ -0,0 +1,111 @@
import { c, cB, cE, cM } from '../../../_utils/cssr'
import fadeInHeightExpandTransition from '../../../_styles/transitions/fade-in-height-expand'
// vars:
// --font-size
// --bezier
// --text-color
// --divider-color
// --title-font-size
// --title-text-color
// --title-font-weight
// --arrow-color
export default cB(
'collapse',
{
width: '100%'
},
[
cB(
'collapse-item',
`
overflow: hidden;
font-size: var(--font-size);
transition: border-color .3s var(--bezier);
margin-top: 16px;
`,
[
c('&:first-child', {
marginTop: 0
}),
c('&:first-child >', [
cE('header', {
paddingTop: 0
})
]),
cM('left-arrow-placement', [
cE('header', [
cB('collapse-item-arrow', {
marginRight: '4px'
})
])
]),
cM('right-arrow-placement', [
cE('header', [
cB('collapse-item-arrow', {
marginLeft: '4px'
})
])
]),
cB('collapse-item', {
marginLeft: '32px'
}),
cE(
'content-wrapper',
{
overflow: 'hidden'
},
[fadeInHeightExpandTransition({ duration: '0.15s' })]
),
cM('active', [
cE('header', [
cM('active', [
cB('collapse-item-arrow', {
transform: 'rotate(90deg)'
})
])
])
]),
c('&:not(:first-child)', {
borderTop: '1px solid var(--divider-color)'
}),
cE(
'header',
`
font-size: var(--title-font-size);
display: flex;
flex-wrap: nowrap;
align-items: center;
font-weight: var(--title-font-weight);
transition: color .3s var(--bezier);
position: relative;
cursor: pointer;
padding: 16px 0 0 0;
color: var(--title-text-color);
`,
[
cB(
'collapse-item-arrow',
`
display: flex;
transition:
transform .15s var(--bezier),
color .3s var(--bezier);
font-size: 18px;
color: var(--arrow-color);
`
)
]
),
cE(
'content-inner',
`
transition: color .3s var(--bezier);
padding-top: 16px;
color: var(--text-color);
`
)
]
)
]
)

View File

@ -1,9 +1,11 @@
import { commonDark } from '../../_styles/new-common'
import type { ThemeCommonVars } from '../../_styles/new-common'
import type { CollapseThemeVars } from './light'
export default {
name: 'Collapse',
common: commonDark,
self (vars) {
self (vars: ThemeCommonVars): CollapseThemeVars {
const {
fontWeight,
textColor1,

View File

@ -1,2 +0,0 @@
export { default as collapseDark } from './dark.js'
export { default as collapseLight } from './light.js'

View File

@ -0,0 +1,3 @@
export { default as collapseDark } from './dark'
export { default as collapseLight } from './light'
export type { CollapseThemeVars } from './light'

View File

@ -1,9 +1,10 @@
import { commonLight } from '../../_styles/new-common'
import type { ThemeCommonVars } from '../../_styles/new-common'
export default {
const collapseLight = {
name: 'Collapse',
common: commonLight,
self (vars) {
self (vars: ThemeCommonVars) {
const {
fontWeight,
textColor1,
@ -22,3 +23,6 @@ export default {
}
}
}
export default collapseLight
export type CollapseThemeVars = ReturnType<typeof collapseLight.self>