Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(comp: divider): dynamic slot #104

Merged
merged 1 commit into from
Jan 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions packages/cdk/utils/__tests__/vNode.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { VNode, VNodeChild } from 'vue'

import { Comment, Fragment, Text } from 'vue'

import { getFirstValidNode, isValidElementNode } from '../vNode'

const TEMPLATE = 'template'

type FakeVNode = Partial<Pick<VNode, 'type'>> & { children?: FakeVNode[] }

const vNode: FakeVNode = {
type: Comment,
}

vNode.children = [vNode]

describe('vNode.ts', () => {
test('getFirstValidNode work', async () => {
const undefinedValue = undefined
expect(getFirstValidNode(undefinedValue)).toBeUndefined()

const arrayValue: FakeVNode[] = [vNode]
expect(getFirstValidNode(arrayValue as VNodeChild)).toBeUndefined()
expect(getFirstValidNode(vNode as VNodeChild)).toBeUndefined()

vNode.type = Fragment
expect(getFirstValidNode(arrayValue as VNodeChild, 0)).toBeUndefined()
expect(getFirstValidNode(vNode as VNodeChild, 0)).toBeUndefined()
expect(getFirstValidNode(arrayValue as VNodeChild, 1)).toBeUndefined()
expect(getFirstValidNode(vNode as VNodeChild, 1)).toBeUndefined()

vNode.type = TEMPLATE
expect(getFirstValidNode(arrayValue as VNodeChild, 0)).toBeUndefined()
expect(getFirstValidNode(vNode as VNodeChild, 0)).toBeUndefined()
expect(getFirstValidNode(arrayValue as VNodeChild, 1)).toBeUndefined()
expect(getFirstValidNode(vNode as VNodeChild, 1)).toBeUndefined()

vNode.type = Text
expect(getFirstValidNode(arrayValue as VNodeChild, 0)).toEqual(vNode)
expect(getFirstValidNode(vNode as VNodeChild, 0)).toEqual(vNode)
expect(getFirstValidNode(arrayValue as VNodeChild, 1)).toEqual(vNode)
expect(getFirstValidNode(vNode as VNodeChild, 1)).toEqual(vNode)
})

test('isValidElementNode work', async () => {
vNode.type = Comment
expect(isValidElementNode(vNode as VNodeChild)).toBeFalsy()

vNode.type = Fragment
expect(isValidElementNode(vNode as VNodeChild)).toBeFalsy()

vNode.type = Text
expect(isValidElementNode(vNode as VNodeChild)).toBeTruthy()
})
})
1 change: 1 addition & 0 deletions packages/cdk/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './convert'
export * from './propTypes'
export * from './typeof'
export * from './vNode'
41 changes: 41 additions & 0 deletions packages/cdk/utils/vNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { VNode, VNodeChild } from 'vue'

import { Comment, Fragment } from 'vue'

import { isUndefined } from './typeof'

const TEMPLATE = 'template'

export const isFragment = (node: VNodeChild) => (node as VNode).type === Fragment
export const isComment = (node: VNodeChild) => (node as VNode).type === Comment
export const isTemplate = (node: VNodeChild) => (node as VNode).type === TEMPLATE

function getChildren(node: VNode, depth: number): undefined | VNode {
if (isComment(node)) return
if (isFragment(node) || isTemplate(node)) {
return depth > 0 ? getFirstValidNode(node.children as VNodeChild, depth - 1) : undefined
}
return node
}

/**
* get first valid child node (not fragment not comment)
* @param nodes {VNode} node to be searched
* @param maxDepth {number} depth to be searched, default is 3
*/
export function getFirstValidNode(nodes?: VNodeChild, maxDepth = 3): ReturnType<typeof getChildren> {
if (isUndefined(nodes)) return
if (Array.isArray(nodes)) {
return getChildren(nodes[0] as VNode, maxDepth)
}
return getChildren(nodes as VNode, maxDepth)
}

/**
* determine whether an element is valid (not fragment not comment)
* @param node {VNode} node to be determined
* @returns {boolean}
*/
export function isValidElementNode(node: VNodeChild): boolean {
return !(isFragment(node) || isComment(node))
}
Empty file modified packages/components/divider/demo/with-text.md
100755 → 100644
Empty file.
30 changes: 20 additions & 10 deletions packages/components/divider/src/Divider.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue'
import { PropTypes } from '@idux/cdk/utils'
import { useGlobalConfig } from '@idux/components/core/config'

import type { Ref, SetupContext } from 'vue'
import type { DividerConfig } from '@idux/components/core/config'
import type { DividerProps } from './types'

import { computed, defineComponent, onUpdated, ref } from 'vue'
import { PropTypes } from '@idux/cdk/utils'
import { useGlobalConfig } from '@idux/components/core/config'
import { getFirstValidNode, isValidElementNode } from '@idux/cdk/utils'

export default defineComponent({
name: 'IxDivider',
props: {
Expand All @@ -21,15 +23,23 @@ export default defineComponent({
position: PropTypes.oneOf(['left', 'center', 'right'] as const),
type: PropTypes.oneOf(['horizontal', 'vertical'] as const),
},
setup(props: DividerProps, { slots }) {
setup(props: DividerProps, { slots }: SetupContext) {
const dividerConfig = useGlobalConfig('divider')
const className = useClassName(props, dividerConfig, !!slots.default)
let text = getFirstValidNode(slots.default?.())
const withText = ref(!!(text && isValidElementNode(text)))

onUpdated(() => {
text = getFirstValidNode(slots.default?.())
withText.value = !!(text && isValidElementNode(text))
})

const className = useClassName(props, dividerConfig, withText)

return { className }
},
})

function useClassName(props: DividerProps, config: DividerConfig, withText: boolean) {
function useClassName(props: DividerProps, config: DividerConfig, withText: Ref<boolean>) {
return computed(() => {
const position = props.position || config.position
const type = props.type || config.type
Expand All @@ -39,10 +49,10 @@ function useClassName(props: DividerProps, config: DividerConfig, withText: bool
return [
`ix-divider-${type}`,
{
'ix-divider-with-text': withText,
'ix-divider-with-text': withText.value,
'ix-divider-dashed': dashed,
'ix-divider-plain': plain && withText,
[`ix-divider-with-text-${position}`]: type === 'horizontal' && withText,
'ix-divider-plain': plain && withText.value,
[`ix-divider-with-text-${position}`]: type === 'horizontal' && withText.value,
},
]
})
Expand Down