mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2024-12-21 04:50:14 +08:00
refactor(collapse): ts
This commit is contained in:
parent
b907e295d7
commit
a976480bf1
@ -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'
|
@ -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',
|
@ -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
|
||||
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)
|
||||
const collapsedRef = computed<boolean>(() => {
|
||||
const { expandedNames } = NCollapse
|
||||
if (Array.isArray(expandedNames)) {
|
||||
const { name } = props
|
||||
return !~expandedNames.findIndex(
|
||||
(expandedName) => expandedName === name
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick (e) {
|
||||
const { NCollapse } = this
|
||||
if (NCollapse) {
|
||||
NCollapse.toggleItem(this.collapsed, this.name, e)
|
||||
})
|
||||
return {
|
||||
collapsed: collapsedRef,
|
||||
mergedDisplayDirective: computed<'if' | 'show'>(() => {
|
||||
const { displayDirective } = props
|
||||
if (displayDirective) {
|
||||
return displayDirective
|
||||
} else {
|
||||
return NCollapse.displayDirective
|
||||
}
|
||||
}),
|
||||
arrowPlacement: computed<'left' | 'right'>(() => {
|
||||
return NCollapse.arrowPlacement
|
||||
}),
|
||||
handleClick (e: MouseEvent) {
|
||||
if (NCollapse) {
|
||||
NCollapse.toggleItem(collapsedRef.value, props.name, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
@ -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);
|
||||
`)
|
||||
])
|
||||
])
|
111
src/collapse/src/styles/index.cssr.ts
Normal file
111
src/collapse/src/styles/index.cssr.ts
Normal 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);
|
||||
`
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
@ -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,
|
@ -1,2 +0,0 @@
|
||||
export { default as collapseDark } from './dark.js'
|
||||
export { default as collapseLight } from './light.js'
|
3
src/collapse/styles/index.ts
Normal file
3
src/collapse/styles/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { default as collapseDark } from './dark'
|
||||
export { default as collapseLight } from './light'
|
||||
export type { CollapseThemeVars } from './light'
|
@ -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>
|
Loading…
Reference in New Issue
Block a user