mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-01-12 12:25:16 +08:00
refactor(tree): ts
This commit is contained in:
parent
73c9d3f9c1
commit
d963e2f2d6
@ -65,7 +65,7 @@ export default defineComponent({
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
...useTheme.props,
|
||||
...useTheme.createProps<AlertThemeVars>(),
|
||||
title: {
|
||||
type: String,
|
||||
default: undefined
|
||||
@ -107,13 +107,7 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
const themeRef = useTheme<AlertThemeVars>(
|
||||
'Alert',
|
||||
'Alert',
|
||||
style,
|
||||
alertLight,
|
||||
props
|
||||
)
|
||||
const themeRef = useTheme('Alert', 'Alert', style, alertLight, props)
|
||||
const cssVars = computed(() => {
|
||||
const {
|
||||
common: { cubicBezierEaseInOut },
|
||||
|
@ -7,7 +7,7 @@ import style from './styles/index.cssr'
|
||||
export default defineComponent({
|
||||
name: 'Code',
|
||||
props: {
|
||||
...useTheme.props,
|
||||
...useTheme.createProps<CodeThemeVars>(),
|
||||
language: {
|
||||
type: String,
|
||||
default: undefined
|
||||
@ -56,13 +56,7 @@ export default defineComponent({
|
||||
watch(toRef(props, 'language'), setCode)
|
||||
watch(toRef(props, 'code'), setCode)
|
||||
watch(hljsRef, setCode)
|
||||
const themeRef = useTheme<CodeThemeVars>(
|
||||
'Code',
|
||||
'Code',
|
||||
style,
|
||||
codeLight,
|
||||
props
|
||||
)
|
||||
const themeRef = useTheme('Code', 'Code', style, codeLight, props)
|
||||
return {
|
||||
codeRef,
|
||||
cssVars: computed(() => {
|
||||
|
@ -114,7 +114,9 @@ export default defineComponent({
|
||||
{
|
||||
class: 'n-menu-item-content__arrow'
|
||||
},
|
||||
[h(ChevronDownFilledIcon)]
|
||||
{
|
||||
default: () => h(ChevronDownFilledIcon)
|
||||
}
|
||||
)
|
||||
: null
|
||||
]
|
||||
|
@ -15,6 +15,7 @@ import { useMergedState, useCompitable, useIsMounted, useMemo } from 'vooks'
|
||||
import { call, keep, warn } from '../../_utils'
|
||||
import { useTheme } from '../../_mixins'
|
||||
import NPopoverBody, { popoverBodyProps } from './PopoverBody'
|
||||
import type { PopoverThemeVars } from '../styles'
|
||||
|
||||
const bodyPropKeys = Object.keys(
|
||||
popoverBodyProps
|
||||
@ -73,7 +74,7 @@ export interface PopoverInjection {
|
||||
}
|
||||
|
||||
export const popoverProps = {
|
||||
...useTheme.props,
|
||||
...useTheme.createProps<PopoverThemeVars>(),
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: undefined
|
||||
|
@ -24,7 +24,7 @@ import { PopoverThemeVars } from '../styles/light'
|
||||
import { PopoverInjection } from './Popover'
|
||||
|
||||
export const popoverBodyProps = {
|
||||
...useTheme.props,
|
||||
...useTheme.createProps<PopoverThemeVars>(),
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: undefined
|
||||
@ -106,13 +106,7 @@ export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
props: popoverBodyProps,
|
||||
setup (props) {
|
||||
const themeRef = useTheme<PopoverThemeVars>(
|
||||
'Popover',
|
||||
'Popover',
|
||||
style,
|
||||
popoverLight,
|
||||
props
|
||||
)
|
||||
const themeRef = useTheme('Popover', 'Popover', style, popoverLight, props)
|
||||
const followerRef = ref<FollowerRef | null>(null)
|
||||
const NPopover = inject<PopoverInjection>('NPopover') as PopoverInjection
|
||||
const followerEnabledRef = ref(props.show)
|
||||
@ -125,7 +119,9 @@ export default defineComponent({
|
||||
if (trigger === 'hover') {
|
||||
directives.push([mousemoveoutside, handleMouseMoveOutside])
|
||||
}
|
||||
if (props.displayDirective === 'show') { directives.push([vShow, props.show]) }
|
||||
if (props.displayDirective === 'show') {
|
||||
directives.push([vShow, props.show])
|
||||
}
|
||||
return directives as DirectiveArguments
|
||||
})
|
||||
const styleRef = computed(() => {
|
||||
|
@ -1,2 +1,3 @@
|
||||
export { default as popoverDark } from './dark'
|
||||
export { default as popoverLight } from './light'
|
||||
export type { PopoverThemeVars } from './light'
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { commonDark } from '../_styles/new-common'
|
||||
import { alertDark } from '../alert/styles'
|
||||
import { affixDark } from '../affix/styles'
|
||||
import { anchorDark } from '../anchor/styles'
|
||||
import { autoCompleteDark } from '../auto-complete/styles'
|
||||
import { avatarDark } from '../avatar/styles'
|
||||
@ -68,7 +67,6 @@ import { selectDark } from '../select/styles'
|
||||
export const darkTheme = {
|
||||
common: commonDark,
|
||||
Alert: alertDark,
|
||||
Affix: affixDark,
|
||||
Anchor: anchorDark,
|
||||
AutoComplete: autoCompleteDark,
|
||||
Avatar: avatarDark,
|
@ -1,6 +1,5 @@
|
||||
import { commonLight } from '../_styles/new-common'
|
||||
import { alertLight } from '../alert/styles'
|
||||
import { affixLight } from '../affix/styles'
|
||||
import { anchorLight } from '../anchor/styles'
|
||||
import { autoCompleteLight } from '../auto-complete/styles'
|
||||
import { avatarLight } from '../avatar/styles'
|
||||
@ -68,7 +67,6 @@ import { selectLight } from '../select/styles'
|
||||
export const lightTheme = {
|
||||
common: commonLight,
|
||||
Alert: alertLight,
|
||||
Affix: affixLight,
|
||||
Anchor: anchorLight,
|
||||
AutoComplete: autoCompleteLight,
|
||||
Avatar: avatarLight,
|
@ -1,465 +0,0 @@
|
||||
import { h, ref, toRef, computed, defineComponent } from 'vue'
|
||||
import { createTreeMate } from 'treemate'
|
||||
import { useMergedState } from 'vooks'
|
||||
import { useTheme } from '../../_mixins'
|
||||
import { call, warn } from '../../_utils'
|
||||
import { treeLight } from '../styles'
|
||||
import NTreeNode from './TreeNode'
|
||||
import { keysWithFilter } from './utils'
|
||||
import style from './styles/index.cssr.js'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Tree',
|
||||
provide () {
|
||||
return { NTree: this }
|
||||
},
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
defaultExpandAll: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
expandOnDragenter: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
cancelable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
checkable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
draggable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
blockNode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
checkedKeys: {
|
||||
type: Array,
|
||||
default: undefined
|
||||
},
|
||||
defaultCheckedKeys: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
expandedKeys: {
|
||||
type: Array,
|
||||
default: undefined
|
||||
},
|
||||
defaultExpandedKeys: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
selectedKeys: {
|
||||
type: Array,
|
||||
default: undefined
|
||||
},
|
||||
defaultSelectedKeys: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
remote: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
pattern: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
filter: {
|
||||
type: Function,
|
||||
default: (pattern, node) => {
|
||||
if (!pattern) return true
|
||||
return ~node.label.toLowerCase().indexOf(pattern.toLowerCase())
|
||||
}
|
||||
},
|
||||
onLoad: {
|
||||
type: Function,
|
||||
default: undefined
|
||||
},
|
||||
cascade: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
selectable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
onDragEnter: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onDragLeave: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onDragEnd: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onDragStart: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
onDrop: {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:expandedKeys': {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:checkedKeys': {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:selectedKeys': {
|
||||
type: [Function, Array],
|
||||
default: undefined
|
||||
},
|
||||
// deprecated
|
||||
onExpandedKeysChange: {
|
||||
validator () {
|
||||
warn(
|
||||
'tree',
|
||||
'`on-expanded-keys-change` is deprecated, please use `on-update:expanded-keys` instead.'
|
||||
)
|
||||
return true
|
||||
},
|
||||
default: undefined
|
||||
},
|
||||
onCheckedKeysChange: {
|
||||
validator () {
|
||||
warn(
|
||||
'tree',
|
||||
'`on-checked-keys-change` is deprecated, please use `on-update:expanded-keys` instead.'
|
||||
)
|
||||
return true
|
||||
},
|
||||
default: undefined
|
||||
},
|
||||
onSelectedKeysChange: {
|
||||
validator () {
|
||||
warn(
|
||||
'tree',
|
||||
'`on-selected-keys-change` is deprecated, please use `on-update:selected-keys` instead.'
|
||||
)
|
||||
return true
|
||||
},
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
const themeRef = useTheme('Tree', 'Tree', style, treeLight, props)
|
||||
const treeMateRef = computed(() => createTreeMate(props.data))
|
||||
const uncontrolledCheckedKeysRef = ref(
|
||||
props.defaultCheckedKeys || props.checkedKeys
|
||||
)
|
||||
const controlledCheckedKeysRef = toRef(props, 'checkedKeys')
|
||||
const mergedCheckedKeysRef = useMergedState(
|
||||
controlledCheckedKeysRef,
|
||||
uncontrolledCheckedKeysRef
|
||||
)
|
||||
const checkedStatusRef = computed(() => {
|
||||
return treeMateRef.value.getCheckedKeys(mergedCheckedKeysRef.value, {
|
||||
cascade: props.cascade
|
||||
})
|
||||
})
|
||||
const displayedCheckedKeysRef = computed(() => {
|
||||
return checkedStatusRef.value.checkedKeys
|
||||
})
|
||||
const displayedIndeterminateKeysRef = computed(() => {
|
||||
return checkedStatusRef.value.indeterminateKeys
|
||||
})
|
||||
const uncontrolledSelectedKeysRef = ref(
|
||||
props.defaultSelectedKeys || props.selectedKeys
|
||||
)
|
||||
const controlledSelectedKeysRef = toRef(props, 'selectedKeys')
|
||||
const mergedSelectedKeysRef = useMergedState(
|
||||
controlledSelectedKeysRef,
|
||||
uncontrolledSelectedKeysRef
|
||||
)
|
||||
const uncontrolledExpandedKeysRef = ref(
|
||||
props.defaultExpandAll
|
||||
? treeMateRef.value.getNonLeafKeys()
|
||||
: props.defaultExpandedKeys || props.expandedKeys
|
||||
)
|
||||
const controlledExpandedKeysRef = toRef(props, 'selectedKeys')
|
||||
const mergedExpandedKeysRef = useMergedState(
|
||||
controlledExpandedKeysRef,
|
||||
uncontrolledExpandedKeysRef
|
||||
)
|
||||
return {
|
||||
treeMate: treeMateRef,
|
||||
tmNodes: computed(() => treeMateRef.value.treeNodes),
|
||||
uncontrolledCheckedKeys: uncontrolledCheckedKeysRef,
|
||||
displayedCheckedKeys: displayedCheckedKeysRef,
|
||||
displayedIndeterminateKeys: displayedIndeterminateKeysRef,
|
||||
uncontrolledSelectedKeys: uncontrolledSelectedKeysRef,
|
||||
mergedSelectedKeys: mergedSelectedKeysRef,
|
||||
uncontrolledExpandedKeys: uncontrolledExpandedKeysRef,
|
||||
mergedExpandedKeys: mergedExpandedKeysRef,
|
||||
highlightKeys: ref([]),
|
||||
loadingKeys: ref([]),
|
||||
mergedTheme: themeRef,
|
||||
cssVars: computed(() => {
|
||||
const {
|
||||
common: { cubicBezierEaseInOut },
|
||||
self: {
|
||||
fontSize,
|
||||
nodeBorderRadius,
|
||||
nodeColorHover,
|
||||
nodeColorPressed,
|
||||
nodeColorActive,
|
||||
arrowColor,
|
||||
loadingColor,
|
||||
nodeTextColor,
|
||||
nodeTextColorDisabled
|
||||
}
|
||||
} = themeRef.value
|
||||
return {
|
||||
'--arrow-color': arrowColor,
|
||||
'--loading-color': loadingColor,
|
||||
'--bezier': cubicBezierEaseInOut,
|
||||
'--font-size': fontSize,
|
||||
'--node-border-radius': nodeBorderRadius,
|
||||
'--node-color-active': nodeColorActive,
|
||||
'--node-color-hover': nodeColorHover,
|
||||
'--node-color-pressed': nodeColorPressed,
|
||||
'--node-text-color': nodeTextColor,
|
||||
'--node-text-color-disabled': nodeTextColorDisabled
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
draggingNodeKey: null,
|
||||
draggingNode: null,
|
||||
droppingNodeKey: null,
|
||||
expandTimerId: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
data () {
|
||||
this.loadingKeys = []
|
||||
this.expandTimerId = null
|
||||
},
|
||||
pattern (value) {
|
||||
if (value) {
|
||||
const [expandedKeysAfterChange, highlightKeys] = keysWithFilter(
|
||||
this.data,
|
||||
this.pattern,
|
||||
this.filter
|
||||
)
|
||||
this.highlightKeys = highlightKeys
|
||||
this.doExpandedKeysChange(expandedKeysAfterChange)
|
||||
} else {
|
||||
this.highlightKeys = []
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doExpandedKeysChange (value) {
|
||||
const {
|
||||
'onUpdate:expandedKeys': onUpdateExpandedKeys,
|
||||
onExpandedKeysChange
|
||||
} = this
|
||||
this.uncontrolledExpandedKeys = value
|
||||
if (onUpdateExpandedKeys) call(onUpdateExpandedKeys, value)
|
||||
if (onExpandedKeysChange) call(onExpandedKeysChange, value)
|
||||
},
|
||||
doCheckedKeysChange (value) {
|
||||
const {
|
||||
'onUpdate:checkedKeys': onUpdateCheckedKeys,
|
||||
onCheckedKeysChange
|
||||
} = this
|
||||
this.uncontrolledCheckedKeys = value
|
||||
if (onUpdateCheckedKeys) call(onUpdateCheckedKeys, value)
|
||||
if (onCheckedKeysChange) call(onCheckedKeysChange, value)
|
||||
},
|
||||
doSelectedKeysChange (value) {
|
||||
const {
|
||||
'onUpdate:selectedKeys': onUpdateSelectedKeys,
|
||||
onSelectedKeysChange
|
||||
} = this
|
||||
this.uncontrolledSelectedKeys = value
|
||||
if (onUpdateSelectedKeys) call(onUpdateSelectedKeys, value)
|
||||
if (onSelectedKeysChange) call(onSelectedKeysChange, value)
|
||||
},
|
||||
doDragEnter (...args) {
|
||||
const { onDragEnter } = this
|
||||
if (onDragEnter) call(onDragEnter, ...args)
|
||||
},
|
||||
doDragLeave (...args) {
|
||||
const { onDragLeave } = this
|
||||
if (onDragLeave) call(onDragLeave, ...args)
|
||||
},
|
||||
doDragEnd (...args) {
|
||||
const { onDragEnd } = this
|
||||
if (onDragEnd) call(onDragEnd, ...args)
|
||||
},
|
||||
doDragStart (...args) {
|
||||
const { onDragStart } = this
|
||||
if (onDragStart) call(onDragStart, ...args)
|
||||
},
|
||||
doDrop (...args) {
|
||||
const { onDrop } = this
|
||||
if (onDrop) call(onDrop, ...args)
|
||||
},
|
||||
resetDragStatus () {
|
||||
this.draggingNodeKey = null
|
||||
this.draggingNode = null
|
||||
this.droppingNodeKey = null
|
||||
},
|
||||
handleCheck (node, checked) {
|
||||
if (this.disabled || node.disabled) return
|
||||
const { checkedKeys } = this.treeMate[checked ? 'check' : 'uncheck'](
|
||||
node.key,
|
||||
this.displayedCheckedKeys,
|
||||
{
|
||||
cascade: this.cascade
|
||||
}
|
||||
)
|
||||
this.doCheckedKeysChange(checkedKeys)
|
||||
},
|
||||
toggleExpand (node) {
|
||||
if (this.disabled) return
|
||||
const { mergedExpandedKeys } = this
|
||||
const index = mergedExpandedKeys.findIndex(
|
||||
(expandNodeId) => expandNodeId === node.key
|
||||
)
|
||||
if (~index) {
|
||||
const expandedKeysAfterChange = Array.from(mergedExpandedKeys)
|
||||
expandedKeysAfterChange.splice(index, 1)
|
||||
this.doExpandedKeysChange(expandedKeysAfterChange)
|
||||
} else {
|
||||
this.doExpandedKeysChange(mergedExpandedKeys.concat(node.key))
|
||||
}
|
||||
},
|
||||
handleSwitcherClick (node) {
|
||||
if (this.disabled || node.disabled) return
|
||||
this.toggleExpand(node)
|
||||
},
|
||||
handleSelect (node) {
|
||||
if (this.disabled || node.disabled || !this.selectable) return
|
||||
if (this.multiple) {
|
||||
const selectedKeys = this.mergedSelectedKeys
|
||||
const index = selectedKeys.findIndex((key) => key === node.key)
|
||||
if (~index) {
|
||||
if (this.cancelable) {
|
||||
selectedKeys.splice(index, 1)
|
||||
}
|
||||
} else if (!~index) {
|
||||
selectedKeys.push(node.key)
|
||||
}
|
||||
this.doSelectedKeysChange(selectedKeys)
|
||||
} else {
|
||||
const selectedKeys = this.mergedSelectedKeys
|
||||
if (selectedKeys.includes(node.key)) {
|
||||
if (this.cancelable) {
|
||||
this.doSelectedKeysChange([])
|
||||
}
|
||||
} else {
|
||||
this.doSelectedKeysChange([node.key])
|
||||
}
|
||||
}
|
||||
},
|
||||
handleDragEnter ({ event, node }) {
|
||||
// node should be a tmNode
|
||||
if (!this.draggable || this.disabled || node.disabled) return
|
||||
this.doDragEnter({ event, node })
|
||||
if (!this.expandOnDragenter) return
|
||||
this.droppingNodeKey = node.key
|
||||
if (node.key === this.draggingNodeKey) return
|
||||
if (!this.mergedExpandedKeys.includes(node.key) && !node.isLeaf) {
|
||||
window.clearTimeout(this.expandTimerId)
|
||||
const expand = () => {
|
||||
if (
|
||||
this.droppingNodeKey === node.key &&
|
||||
!this.mergedExpandedKeys.includes(node.key)
|
||||
) {
|
||||
this.doExpandedKeysChange(this.mergedExpandedKeys.concat(node.key))
|
||||
}
|
||||
}
|
||||
if (!node.isShallowLoaded) {
|
||||
if (!this.loadingKeys.includes(node.key)) {
|
||||
this.loadingKeys.push(node.key)
|
||||
}
|
||||
this.onLoad(node).then(() => {
|
||||
this.loadingKeys.splice(
|
||||
this.loadingKeys.find((key) => key === node.key),
|
||||
1
|
||||
)
|
||||
expand()
|
||||
})
|
||||
return
|
||||
}
|
||||
this.expandTimerId = window.setTimeout(() => {
|
||||
expand()
|
||||
this.expandTimerId = null
|
||||
}, 800)
|
||||
}
|
||||
},
|
||||
handleDragLeave ({ event, node }) {
|
||||
if (!this.draggable || this.disabled || node.disabled) return
|
||||
this.droppingNodeKey = null
|
||||
this.doDragLeave({ event, node })
|
||||
},
|
||||
handleDragEnd ({ event, node }) {
|
||||
if (!this.draggable || this.disabled || node.disabled) return
|
||||
this.doDragEnd({ event, node })
|
||||
this.resetDragStatus()
|
||||
},
|
||||
handleDragStart ({ event, node }) {
|
||||
if (!this.draggable || this.disabled || node.disabled) return
|
||||
this.draggingNodeKey = node.key
|
||||
this.draggingNode = node
|
||||
this.doDragStart({ event, node })
|
||||
},
|
||||
handleDrop ({ event, node, dropPosition }) {
|
||||
if (!this.draggable || this.disabled || node.disabled) return
|
||||
this.doDrop('drop', {
|
||||
event,
|
||||
node,
|
||||
dragNode: this.draggingNode,
|
||||
dropPosition
|
||||
})
|
||||
this.resetDragStatus()
|
||||
}
|
||||
},
|
||||
render () {
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
class: 'n-tree',
|
||||
style: this.cssVars
|
||||
},
|
||||
this.tmNodes.map((tmNode) =>
|
||||
h(NTreeNode, {
|
||||
tmNode,
|
||||
key: tmNode.key
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
547
src/tree/src/Tree.ts
Normal file
547
src/tree/src/Tree.ts
Normal file
@ -0,0 +1,547 @@
|
||||
import {
|
||||
h,
|
||||
ref,
|
||||
toRef,
|
||||
computed,
|
||||
defineComponent,
|
||||
provide,
|
||||
PropType,
|
||||
watch,
|
||||
reactive
|
||||
} from 'vue'
|
||||
import { createTreeMate, Key, KeyedRawNode, TreeNode } from 'treemate'
|
||||
import { useMergedState } from 'vooks'
|
||||
import { useTheme } from '../../_mixins'
|
||||
import type { MergedTheme } from '../../_mixins/use-theme'
|
||||
import { call, warn } from '../../_utils'
|
||||
import { treeLight } from '../styles'
|
||||
import type { TreeThemeVars } from '../styles'
|
||||
import NTreeNode from './TreeNode'
|
||||
import { keysWithFilter } from './utils'
|
||||
import style from './styles/index.cssr'
|
||||
|
||||
export interface DragInfo {
|
||||
event: DragEvent
|
||||
node: KeyedRawNode
|
||||
}
|
||||
export interface DropInfo {
|
||||
event: DragEvent
|
||||
node: KeyedRawNode
|
||||
dropPosition: 'top' | 'center' | 'bottom'
|
||||
}
|
||||
|
||||
export interface TreeInjection {
|
||||
loadingKeys: Key[]
|
||||
highlightKeys: Key[]
|
||||
displayedCheckedKeys: Key[]
|
||||
displayedIndeterminateKeys: Key[]
|
||||
mergedSelectedKeys: Key[]
|
||||
mergedExpandedKeys: Key[]
|
||||
remote: boolean
|
||||
draggable: boolean
|
||||
checkable: boolean
|
||||
blockNode: boolean
|
||||
onLoad: (node: KeyedRawNode) => Promise<void>
|
||||
handleSwitcherClick: (node: TreeNode) => void
|
||||
handleSelect: (node: TreeNode) => void
|
||||
handleCheck: (node: TreeNode, checked: boolean) => void
|
||||
handleDragStart: (info: DragInfo) => void
|
||||
handleDragEnter: (info: DragInfo) => void
|
||||
handleDragLeave: (info: DragInfo) => void
|
||||
handleDragEnd: (info: DragInfo) => void
|
||||
handleDrop: (info: DropInfo) => void
|
||||
mergedTheme: MergedTheme<TreeThemeVars>
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Tree',
|
||||
props: {
|
||||
...useTheme.createProps<TreeThemeVars>(),
|
||||
data: {
|
||||
type: Array as PropType<KeyedRawNode[]>,
|
||||
required: true
|
||||
},
|
||||
defaultExpandAll: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
expandOnDragenter: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
cancelable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
checkable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
draggable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
blockNode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
checkedKeys: {
|
||||
type: Array as PropType<Key[]>,
|
||||
default: undefined
|
||||
},
|
||||
defaultCheckedKeys: {
|
||||
type: Array as PropType<Key[]>,
|
||||
default: () => []
|
||||
},
|
||||
expandedKeys: {
|
||||
type: Array as PropType<Key[]>,
|
||||
default: undefined
|
||||
},
|
||||
defaultExpandedKeys: {
|
||||
type: Array as PropType<Key[]>,
|
||||
default: () => []
|
||||
},
|
||||
selectedKeys: {
|
||||
type: Array as PropType<Key[]>,
|
||||
default: undefined
|
||||
},
|
||||
defaultSelectedKeys: {
|
||||
type: Array as PropType<Key[]>,
|
||||
default: () => []
|
||||
},
|
||||
remote: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
pattern: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
filter: {
|
||||
type: Function as PropType<
|
||||
(pattern: string, node: KeyedRawNode) => boolean
|
||||
>,
|
||||
default: (pattern: string, node: KeyedRawNode) => {
|
||||
if (!pattern) return true
|
||||
return ~node.label.toLowerCase().indexOf(pattern.toLowerCase())
|
||||
}
|
||||
},
|
||||
onLoad: {
|
||||
type: Function as PropType<(node: KeyedRawNode) => Promise<void>>,
|
||||
default: undefined
|
||||
},
|
||||
cascade: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
selectable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
onDragEnter: {
|
||||
type: [Function, Array] as PropType<
|
||||
(e: DragEvent) => void | ((e: DragEvent) => void)[]
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
onDragLeave: {
|
||||
type: [Function, Array] as PropType<
|
||||
(e: DragEvent) => void | ((e: DragEvent) => void)[]
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
onDragEnd: {
|
||||
type: [Function, Array] as PropType<
|
||||
(e: DragEvent) => void | ((e: DragEvent) => void)[]
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
onDragStart: {
|
||||
type: [Function, Array] as PropType<
|
||||
(e: DragEvent) => void | ((e: DragEvent) => void)[]
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
onDrop: {
|
||||
type: [Function, Array] as PropType<
|
||||
(e: DragEvent) => void | ((e: DragEvent) => void)[]
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:expandedKeys': {
|
||||
type: [Function, Array] as PropType<
|
||||
(value: Key[]) => void | ((value: Key[]) => void)[]
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:checkedKeys': {
|
||||
type: [Function, Array] as PropType<
|
||||
(value: Key[]) => void | ((value: Key[]) => void)[]
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:selectedKeys': {
|
||||
type: [Function, Array] as PropType<
|
||||
(value: Key[]) => void | ((value: Key[]) => void)[]
|
||||
>,
|
||||
default: undefined
|
||||
},
|
||||
// deprecated
|
||||
onExpandedKeysChange: {
|
||||
type: [Function, Array] as PropType<
|
||||
(value: Key[]) => void | ((value: Key[]) => void)[]
|
||||
>,
|
||||
validator: () => {
|
||||
warn(
|
||||
'tree',
|
||||
'`on-expanded-keys-change` is deprecated, please use `on-update:expanded-keys` instead.'
|
||||
)
|
||||
return true
|
||||
},
|
||||
default: undefined
|
||||
},
|
||||
onCheckedKeysChange: {
|
||||
type: [Function, Array] as PropType<
|
||||
(value: Key[]) => void | ((value: Key[]) => void)[]
|
||||
>,
|
||||
validator: () => {
|
||||
warn(
|
||||
'tree',
|
||||
'`on-checked-keys-change` is deprecated, please use `on-update:expanded-keys` instead.'
|
||||
)
|
||||
return true
|
||||
},
|
||||
default: undefined
|
||||
},
|
||||
onSelectedKeysChange: {
|
||||
type: [Function, Array] as PropType<
|
||||
(value: Key[]) => void | ((value: Key[]) => void)[]
|
||||
>,
|
||||
validator: () => {
|
||||
warn(
|
||||
'tree',
|
||||
'`on-selected-keys-change` is deprecated, please use `on-update:selected-keys` instead.'
|
||||
)
|
||||
return true
|
||||
},
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
const themeRef = useTheme('Tree', 'Tree', style, treeLight, props)
|
||||
const treeMateRef = computed(() => createTreeMate(props.data))
|
||||
const uncontrolledCheckedKeysRef = ref(
|
||||
props.defaultCheckedKeys || props.checkedKeys
|
||||
)
|
||||
const controlledCheckedKeysRef = toRef(props, 'checkedKeys')
|
||||
const mergedCheckedKeysRef = useMergedState(
|
||||
controlledCheckedKeysRef,
|
||||
uncontrolledCheckedKeysRef
|
||||
)
|
||||
const checkedStatusRef = computed(() => {
|
||||
return treeMateRef.value.getCheckedKeys(mergedCheckedKeysRef.value, {
|
||||
cascade: props.cascade
|
||||
})
|
||||
})
|
||||
const displayedCheckedKeysRef = computed(() => {
|
||||
return checkedStatusRef.value.checkedKeys
|
||||
})
|
||||
const displayedIndeterminateKeysRef = computed(() => {
|
||||
return checkedStatusRef.value.indeterminateKeys
|
||||
})
|
||||
const uncontrolledSelectedKeysRef = ref(
|
||||
props.defaultSelectedKeys || props.selectedKeys
|
||||
)
|
||||
const controlledSelectedKeysRef = toRef(props, 'selectedKeys')
|
||||
const mergedSelectedKeysRef = useMergedState(
|
||||
controlledSelectedKeysRef,
|
||||
uncontrolledSelectedKeysRef
|
||||
)
|
||||
const uncontrolledExpandedKeysRef = ref(
|
||||
props.defaultExpandAll
|
||||
? treeMateRef.value.getNonLeafKeys()
|
||||
: props.defaultExpandedKeys || props.expandedKeys
|
||||
)
|
||||
const controlledExpandedKeysRef = toRef(props, 'selectedKeys')
|
||||
const mergedExpandedKeysRef = useMergedState(
|
||||
controlledExpandedKeysRef,
|
||||
uncontrolledExpandedKeysRef
|
||||
)
|
||||
|
||||
const draggingNodeKeyRef = ref<Key | null>(null)
|
||||
const draggingNodeRef = ref<KeyedRawNode | null>(null)
|
||||
const droppingNodeKeyRef = ref<Key | null>(null)
|
||||
const expandTimerIdRef = ref<number | undefined>(undefined)
|
||||
const highlightKeysRef = ref<Key[]>([])
|
||||
const loadingKeysRef = ref<Key[]>([])
|
||||
|
||||
watch(toRef(props, 'data'), () => {
|
||||
loadingKeysRef.value = []
|
||||
expandTimerIdRef.value = undefined
|
||||
})
|
||||
watch(toRef(props, 'pattern'), (value) => {
|
||||
if (value) {
|
||||
const [expandedKeysAfterChange, highlightKeys] = keysWithFilter(
|
||||
props.data,
|
||||
props.pattern,
|
||||
props.filter
|
||||
)
|
||||
highlightKeysRef.value = highlightKeys
|
||||
doExpandedKeysChange(expandedKeysAfterChange)
|
||||
} else {
|
||||
highlightKeysRef.value = []
|
||||
}
|
||||
})
|
||||
|
||||
function doExpandedKeysChange (value: Key[]) {
|
||||
const {
|
||||
'onUpdate:expandedKeys': onUpdateExpandedKeys,
|
||||
onExpandedKeysChange
|
||||
} = props
|
||||
uncontrolledExpandedKeysRef.value = value
|
||||
if (onUpdateExpandedKeys) call(onUpdateExpandedKeys, value)
|
||||
if (onExpandedKeysChange) call(onExpandedKeysChange, value)
|
||||
}
|
||||
function doCheckedKeysChange (value: Key[]) {
|
||||
const {
|
||||
'onUpdate:checkedKeys': onUpdateCheckedKeys,
|
||||
onCheckedKeysChange
|
||||
} = props
|
||||
uncontrolledCheckedKeysRef.value = value
|
||||
if (onUpdateCheckedKeys) call(onUpdateCheckedKeys, value)
|
||||
if (onCheckedKeysChange) call(onCheckedKeysChange, value)
|
||||
}
|
||||
function doSelectedKeysChange (value: Key[]) {
|
||||
const {
|
||||
'onUpdate:selectedKeys': onUpdateSelectedKeys,
|
||||
onSelectedKeysChange
|
||||
} = props
|
||||
uncontrolledSelectedKeysRef.value = value
|
||||
if (onUpdateSelectedKeys) call(onUpdateSelectedKeys, value)
|
||||
if (onSelectedKeysChange) call(onSelectedKeysChange, value)
|
||||
}
|
||||
// Drag & Drop
|
||||
function doDragEnter (info: DragInfo) {
|
||||
const { onDragEnter } = props
|
||||
if (onDragEnter) call(onDragEnter, info)
|
||||
}
|
||||
function doDragLeave (info: DragInfo) {
|
||||
const { onDragLeave } = props
|
||||
if (onDragLeave) call(onDragLeave, info)
|
||||
}
|
||||
function doDragEnd (info: DragInfo) {
|
||||
const { onDragEnd } = props
|
||||
if (onDragEnd) call(onDragEnd, info)
|
||||
}
|
||||
function doDragStart (info: DragInfo) {
|
||||
const { onDragStart } = props
|
||||
if (onDragStart) call(onDragStart, info)
|
||||
}
|
||||
function doDrop (info: DropInfo & { dragNode: TreeNode }) {
|
||||
const { onDrop } = props
|
||||
if (onDrop) call(onDrop, info)
|
||||
}
|
||||
function resetDragStatus () {
|
||||
draggingNodeKeyRef.value = null
|
||||
draggingNodeRef.value = null
|
||||
droppingNodeKeyRef.value = null
|
||||
}
|
||||
function handleCheck (node: TreeNode, checked: boolean) {
|
||||
if (props.disabled || node.disabled) return
|
||||
const { checkedKeys } = treeMateRef.value[checked ? 'check' : 'uncheck'](
|
||||
node.key,
|
||||
displayedCheckedKeysRef.value,
|
||||
{
|
||||
cascade: props.cascade
|
||||
}
|
||||
)
|
||||
doCheckedKeysChange(checkedKeys)
|
||||
}
|
||||
function toggleExpand (node: TreeNode) {
|
||||
if (props.disabled) return
|
||||
const { value: mergedExpandedKeys } = mergedExpandedKeysRef
|
||||
const index = mergedExpandedKeys.findIndex(
|
||||
(expandNodeId) => expandNodeId === node.key
|
||||
)
|
||||
if (~index) {
|
||||
const expandedKeysAfterChange = Array.from(mergedExpandedKeys)
|
||||
expandedKeysAfterChange.splice(index, 1)
|
||||
doExpandedKeysChange(expandedKeysAfterChange)
|
||||
} else {
|
||||
doExpandedKeysChange(mergedExpandedKeys.concat(node.key))
|
||||
}
|
||||
}
|
||||
function handleSwitcherClick (node: TreeNode) {
|
||||
if (props.disabled || node.disabled) return
|
||||
toggleExpand(node)
|
||||
}
|
||||
function handleSelect (node: TreeNode) {
|
||||
if (props.disabled || node.disabled || !props.selectable) return
|
||||
if (props.multiple) {
|
||||
const selectedKeys = mergedSelectedKeysRef.value
|
||||
const index = selectedKeys.findIndex((key) => key === node.key)
|
||||
if (~index) {
|
||||
if (props.cancelable) {
|
||||
selectedKeys.splice(index, 1)
|
||||
}
|
||||
} else if (!~index) {
|
||||
selectedKeys.push(node.key)
|
||||
}
|
||||
doSelectedKeysChange(selectedKeys)
|
||||
} else {
|
||||
const selectedKeys = mergedSelectedKeysRef.value
|
||||
if (selectedKeys.includes(node.key)) {
|
||||
if (props.cancelable) {
|
||||
doSelectedKeysChange([])
|
||||
}
|
||||
} else {
|
||||
doSelectedKeysChange([node.key])
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleDragEnter ({ event, node }: DragInfo) {
|
||||
// node should be a tmNode
|
||||
if (!props.draggable || props.disabled || node.disabled) return
|
||||
doDragEnter({ event, node })
|
||||
if (!props.expandOnDragenter) return
|
||||
droppingNodeKeyRef.value = node.key
|
||||
if (node.key === draggingNodeKeyRef.value) return
|
||||
if (!mergedExpandedKeysRef.value.includes(node.key) && !node.isLeaf) {
|
||||
window.clearTimeout(expandTimerIdRef.value)
|
||||
const expand = () => {
|
||||
if (
|
||||
droppingNodeKeyRef.value === node.key &&
|
||||
!mergedExpandedKeysRef.value.includes(node.key)
|
||||
) {
|
||||
doExpandedKeysChange(mergedExpandedKeysRef.value.concat(node.key))
|
||||
}
|
||||
}
|
||||
if (!node.isShallowLoaded) {
|
||||
if (!loadingKeysRef.value.includes(node.key)) {
|
||||
loadingKeysRef.value.push(node.key)
|
||||
}
|
||||
props.onLoad(node).then(() => {
|
||||
loadingKeysRef.value.splice(
|
||||
loadingKeysRef.value.findIndex((key) => key === node.key),
|
||||
1
|
||||
)
|
||||
expand()
|
||||
})
|
||||
return
|
||||
}
|
||||
expandTimerIdRef.value = window.setTimeout(() => {
|
||||
expand()
|
||||
expandTimerIdRef.value = undefined
|
||||
}, 800)
|
||||
}
|
||||
}
|
||||
function handleDragLeave ({ event, node }: DragInfo) {
|
||||
if (!props.draggable || props.disabled || node.disabled) return
|
||||
droppingNodeKeyRef.value = null
|
||||
doDragLeave({ event, node })
|
||||
}
|
||||
function handleDragEnd ({ event, node }: DragInfo) {
|
||||
if (!props.draggable || props.disabled || node.disabled) return
|
||||
doDragEnd({ event, node })
|
||||
resetDragStatus()
|
||||
}
|
||||
function handleDragStart ({ event, node }: DragInfo) {
|
||||
if (!props.draggable || props.disabled || node.disabled) return
|
||||
draggingNodeKeyRef.value = node.key
|
||||
draggingNodeRef.value = node
|
||||
doDragStart({ event, node })
|
||||
}
|
||||
function handleDrop ({ event, node, dropPosition }: DropInfo) {
|
||||
if (!props.draggable || props.disabled || node.disabled) return
|
||||
doDrop({
|
||||
event,
|
||||
node,
|
||||
dragNode: draggingNodeRef.value as TreeNode,
|
||||
dropPosition
|
||||
})
|
||||
resetDragStatus()
|
||||
}
|
||||
provide<TreeInjection>(
|
||||
'NTree',
|
||||
reactive({
|
||||
loadingKeys: loadingKeysRef,
|
||||
highlightKeys: highlightKeysRef,
|
||||
displayedCheckedKeys: displayedCheckedKeysRef,
|
||||
displayedIndeterminateKeys: displayedIndeterminateKeysRef,
|
||||
mergedSelectedKeys: mergedSelectedKeysRef,
|
||||
mergedExpandedKeys: mergedExpandedKeysRef,
|
||||
remote: toRef(props, 'remote'),
|
||||
onLoad: toRef(props, 'onLoad'),
|
||||
draggable: toRef(props, 'draggable'),
|
||||
checkable: toRef(props, 'checkable'),
|
||||
blockNode: toRef(props, 'blockNode'),
|
||||
handleSwitcherClick,
|
||||
handleDragEnd,
|
||||
handleDragEnter,
|
||||
handleDragLeave,
|
||||
handleDragStart,
|
||||
handleDrop,
|
||||
handleSelect,
|
||||
handleCheck,
|
||||
mergedTheme: themeRef
|
||||
})
|
||||
)
|
||||
return {
|
||||
tmNodes: computed(() => treeMateRef.value.treeNodes),
|
||||
cssVars: computed(() => {
|
||||
const {
|
||||
common: { cubicBezierEaseInOut },
|
||||
self: {
|
||||
fontSize,
|
||||
nodeBorderRadius,
|
||||
nodeColorHover,
|
||||
nodeColorPressed,
|
||||
nodeColorActive,
|
||||
arrowColor,
|
||||
loadingColor,
|
||||
nodeTextColor,
|
||||
nodeTextColorDisabled
|
||||
}
|
||||
} = themeRef.value
|
||||
return {
|
||||
'--arrow-color': arrowColor,
|
||||
'--loading-color': loadingColor,
|
||||
'--bezier': cubicBezierEaseInOut,
|
||||
'--font-size': fontSize,
|
||||
'--node-border-radius': nodeBorderRadius,
|
||||
'--node-color-active': nodeColorActive,
|
||||
'--node-color-hover': nodeColorHover,
|
||||
'--node-color-pressed': nodeColorPressed,
|
||||
'--node-text-color': nodeTextColor,
|
||||
'--node-text-color-disabled': nodeTextColorDisabled
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
render () {
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
class: 'n-tree',
|
||||
style: this.cssVars
|
||||
},
|
||||
this.tmNodes.map((tmNode) =>
|
||||
h(NTreeNode, {
|
||||
tmNode,
|
||||
key: tmNode.key
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
@ -1,26 +1,82 @@
|
||||
import { h, inject, computed, defineComponent } from 'vue'
|
||||
import { h, inject, computed, defineComponent, PropType } from 'vue'
|
||||
import { useMemo } from 'vooks'
|
||||
import { TreeNode, KeyedRawNode } from 'treemate'
|
||||
import NTreeNodeSwitcher from './TreeNodeSwitcher.vue'
|
||||
import NTreeNodeCheckbox from './TreeNodeCheckbox.vue'
|
||||
import NTreeNodeContent from './TreeNodeContent.vue'
|
||||
import { NFadeInExpandTransition } from '../../_base'
|
||||
import type { TreeInjection } from './Tree'
|
||||
|
||||
const TreeNode = defineComponent({
|
||||
name: 'NTreeNode',
|
||||
inject: {
|
||||
NTree: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
props: {
|
||||
tmNode: {
|
||||
type: Object,
|
||||
type: Object as PropType<TreeNode>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
const NTree = inject('NTree')
|
||||
const NTree = inject<TreeInjection>('NTree') as TreeInjection
|
||||
function handleSwitcherClick () {
|
||||
const { tmNode } = props
|
||||
if (NTree.remote && !tmNode.isLeaf && !tmNode.isShallowLoaded) {
|
||||
if (!NTree.loadingKeys.includes(tmNode.key)) {
|
||||
NTree.loadingKeys.push(tmNode.key)
|
||||
}
|
||||
NTree.onLoad &&
|
||||
NTree.onLoad(tmNode.rawNode as KeyedRawNode).then(() => {
|
||||
NTree.loadingKeys.splice(
|
||||
NTree.loadingKeys.findIndex((key) => key === tmNode.key),
|
||||
1
|
||||
)
|
||||
NTree.handleSwitcherClick(tmNode)
|
||||
})
|
||||
} else {
|
||||
NTree.handleSwitcherClick(tmNode)
|
||||
}
|
||||
}
|
||||
function handleContentClick () {
|
||||
NTree.handleSelect(props.tmNode)
|
||||
}
|
||||
function handleDragEnter (e: DragEvent) {
|
||||
NTree.handleDragEnter({
|
||||
event: e,
|
||||
node: props.tmNode.rawNode as KeyedRawNode
|
||||
})
|
||||
}
|
||||
function handleDragStart (e: DragEvent) {
|
||||
NTree.handleDragStart({
|
||||
event: e,
|
||||
node: props.tmNode.rawNode as KeyedRawNode
|
||||
})
|
||||
}
|
||||
function handleDragLeave (e: DragEvent) {
|
||||
NTree.handleDragLeave({
|
||||
event: e,
|
||||
node: props.tmNode.rawNode as KeyedRawNode
|
||||
})
|
||||
}
|
||||
function handleDragEnd (e: DragEvent) {
|
||||
NTree.handleDragEnd({
|
||||
event: e,
|
||||
node: props.tmNode.rawNode as KeyedRawNode
|
||||
})
|
||||
}
|
||||
function handleDrop (
|
||||
e: DragEvent,
|
||||
dropPosition: 'top' | 'center' | 'bottom'
|
||||
) {
|
||||
NTree.handleDrop({
|
||||
event: e,
|
||||
node: props.tmNode.rawNode as KeyedRawNode,
|
||||
dropPosition
|
||||
})
|
||||
}
|
||||
function handleCheck (checked: boolean) {
|
||||
NTree.handleCheck(props.tmNode, checked)
|
||||
}
|
||||
return {
|
||||
NTree,
|
||||
loading: useMemo(() => NTree.loadingKeys.includes(props.tmNode.key)),
|
||||
highlight: useMemo(() => NTree.highlightKeys.includes(props.tmNode.key)),
|
||||
checked: useMemo(() =>
|
||||
@ -35,109 +91,15 @@ const TreeNode = defineComponent({
|
||||
expanded: useMemo(() =>
|
||||
NTree.mergedExpandedKeys.includes(props.tmNode.key)
|
||||
),
|
||||
icon: computed(() => props.tmNode.rawNode.icon)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doDragStart (...args) {
|
||||
const {
|
||||
NTree: { handleDragStart }
|
||||
} = this
|
||||
if (handleDragStart) handleDragStart(...args)
|
||||
},
|
||||
doDragEnter (...args) {
|
||||
const {
|
||||
NTree: { handleDragEnter }
|
||||
} = this
|
||||
if (handleDragEnter) handleDragEnter(...args)
|
||||
},
|
||||
doDragEnd (...args) {
|
||||
const {
|
||||
NTree: { handleDragEnd }
|
||||
} = this
|
||||
if (handleDragEnd) handleDragEnd(...args)
|
||||
},
|
||||
doDragLeave (...args) {
|
||||
const {
|
||||
NTree: { handleDragLeave }
|
||||
} = this
|
||||
if (handleDragLeave) handleDragLeave(...args)
|
||||
},
|
||||
doDragOver (...args) {
|
||||
const {
|
||||
NTree: { handleDragOver }
|
||||
} = this
|
||||
if (handleDragOver) handleDragOver(...args)
|
||||
},
|
||||
doDrop (...args) {
|
||||
const {
|
||||
NTree: { handleDrop }
|
||||
} = this
|
||||
if (handleDrop) handleDrop(...args)
|
||||
},
|
||||
doSwitcherClick (...args) {
|
||||
const {
|
||||
NTree: { handleSwitcherClick }
|
||||
} = this
|
||||
if (handleSwitcherClick) handleSwitcherClick(...args)
|
||||
},
|
||||
doCheck (...args) {
|
||||
const {
|
||||
NTree: { handleCheck }
|
||||
} = this
|
||||
if (handleCheck) handleCheck(...args)
|
||||
},
|
||||
doSelect (...args) {
|
||||
const {
|
||||
NTree: { handleSelect }
|
||||
} = this
|
||||
if (handleSelect) handleSelect(...args)
|
||||
},
|
||||
handleSwitcherClick () {
|
||||
const { NTree, tmNode } = this
|
||||
if (NTree.remote && !tmNode.isLeaf && !tmNode.isShallowLoaded) {
|
||||
if (!NTree.loadingKeys.includes(tmNode.key)) {
|
||||
NTree.loadingKeys.push(tmNode.key)
|
||||
}
|
||||
NTree.onLoad &&
|
||||
NTree.onLoad(tmNode.rawNode).then(() => {
|
||||
NTree.loadingKeys.splice(
|
||||
NTree.loadingKeys.find((key) => key === tmNode.key),
|
||||
1
|
||||
)
|
||||
this.doSwitcherClick(tmNode.rawNode)
|
||||
})
|
||||
} else {
|
||||
this.doSwitcherClick(tmNode.rawNode)
|
||||
}
|
||||
},
|
||||
handleContentClick () {
|
||||
this.doSelect(this.tmNode.rawNode)
|
||||
},
|
||||
handleDragOver (e) {
|
||||
this.doDragOver({ event: e, node: this.tmNode.rawNode })
|
||||
},
|
||||
handleDragEnter (e) {
|
||||
this.doDragEnter({ event: e, node: this.tmNode.rawNode })
|
||||
},
|
||||
handleDragStart (e) {
|
||||
this.doDragStart({ event: e, node: this.tmNode.rawNode })
|
||||
},
|
||||
handleDragLeave (e) {
|
||||
this.doDragLeave({ event: e, node: this.tmNode.rawNode })
|
||||
},
|
||||
handleDragEnd (e) {
|
||||
this.doDragEnd({ event: e, node: this.tmNode.rawNode })
|
||||
},
|
||||
handleDrop (e, dropPosition) {
|
||||
this.doDrop({
|
||||
event: e,
|
||||
node: this.tmNode.rawNode,
|
||||
dropPosition
|
||||
})
|
||||
},
|
||||
handleCheck (checked) {
|
||||
this.doCheck(this.tmNode.rawNode, checked)
|
||||
icon: computed(() => props.tmNode.rawNode.icon),
|
||||
handleCheck,
|
||||
handleDrop,
|
||||
handleDragStart,
|
||||
handleDragEnter,
|
||||
handleDragEnd,
|
||||
handleDragLeave,
|
||||
handleContentClick,
|
||||
handleSwitcherClick
|
||||
}
|
||||
},
|
||||
render () {
|
||||
@ -170,10 +132,10 @@ const TreeNode = defineComponent({
|
||||
highlight: this.highlight,
|
||||
draggable: this.NTree.draggable,
|
||||
onClick: this.handleContentClick,
|
||||
onDragover: this.handleDragOver,
|
||||
onDragenter: this.handleDragEnter,
|
||||
onDragstart: this.handleDragStart,
|
||||
onDragleave: this.handleDragLeave,
|
||||
onDragEnd: this.handleDragEnd,
|
||||
onDrop: this.handleDrop
|
||||
},
|
||||
{
|
@ -10,20 +10,16 @@
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
<script lang="ts">
|
||||
import { defineComponent, inject } from 'vue'
|
||||
import { NCheckbox } from '../../checkbox'
|
||||
import type { TreeInjection } from './Tree'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NTreeNodeCheckbox',
|
||||
components: {
|
||||
NCheckbox
|
||||
},
|
||||
inject: {
|
||||
NTree: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
props: {
|
||||
checked: {
|
||||
type: Boolean,
|
||||
@ -38,18 +34,23 @@ export default defineComponent({
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doCheck (value) {
|
||||
const { onCheck } = this
|
||||
setup (props) {
|
||||
const NTree = inject<TreeInjection>('NTree')
|
||||
function doCheck (value: boolean) {
|
||||
const { onCheck } = props
|
||||
if (onCheck) return onCheck(value)
|
||||
},
|
||||
handleUpdateValue (value) {
|
||||
if (this.indeterminate) {
|
||||
this.doCheck(false)
|
||||
}
|
||||
function handleUpdateValue (value: boolean) {
|
||||
if (props.indeterminate) {
|
||||
doCheck(false)
|
||||
} else {
|
||||
this.doCheck(value)
|
||||
doCheck(value)
|
||||
}
|
||||
}
|
||||
return {
|
||||
handleUpdateValue,
|
||||
NTree
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<span
|
||||
ref="selfRef"
|
||||
class="n-tree-node-content"
|
||||
:class="{
|
||||
'n-tree-node-content--pending': pending,
|
||||
@ -26,8 +27,8 @@
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NTreeNodeContent',
|
||||
@ -81,96 +82,110 @@ export default defineComponent({
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
pending: false,
|
||||
pendingPosition: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doClick () {
|
||||
const { onClick } = this
|
||||
setup (props) {
|
||||
const pendingRef = ref(false)
|
||||
const pendingPositionRef = ref<'top' | 'center' | 'bottom' | null>(null)
|
||||
const selfRef = ref<HTMLElement | null>(null)
|
||||
function doClick () {
|
||||
const { onClick } = props
|
||||
if (onClick) onClick()
|
||||
},
|
||||
doDragStart (e) {
|
||||
const { onDragStart } = this
|
||||
}
|
||||
function doDragStart (e: DragEvent) {
|
||||
const { onDragStart } = props
|
||||
if (onDragStart) onDragStart(e)
|
||||
},
|
||||
doDragEnter (e) {
|
||||
const { onDragEnter } = this
|
||||
}
|
||||
function doDragEnter (e: DragEvent) {
|
||||
const { onDragEnter } = props
|
||||
if (onDragEnter) onDragEnter(e)
|
||||
},
|
||||
doDragEnd (e) {
|
||||
const { onDragEnd } = this
|
||||
}
|
||||
function doDragEnd (e: DragEvent) {
|
||||
const { onDragEnd } = props
|
||||
if (onDragEnd) onDragEnd(e)
|
||||
},
|
||||
doDragLeave (e) {
|
||||
const { onDragLeave } = this
|
||||
}
|
||||
function doDragLeave (e: DragEvent) {
|
||||
const { onDragLeave } = props
|
||||
if (onDragLeave) onDragLeave(e)
|
||||
},
|
||||
doDragOver (e) {
|
||||
const { onDragOver } = this
|
||||
if (onDragOver) onDragOver(e)
|
||||
},
|
||||
doDrop (e, dropPosition) {
|
||||
const { onDrop } = this
|
||||
}
|
||||
// function doDragOver (e: DragEvent) {
|
||||
// const { onDragOver } = props
|
||||
// if (onDragOver) onDragOver(e)
|
||||
// }
|
||||
function doDrop (e: DragEvent, dropPosition: 'top' | 'bottom' | 'center') {
|
||||
const { onDrop } = props
|
||||
if (onDrop) onDrop(e, dropPosition)
|
||||
},
|
||||
handleClick () {
|
||||
this.doClick()
|
||||
},
|
||||
handleContentDragStart (e) {
|
||||
this.doDragStart(e)
|
||||
},
|
||||
handleContentDragEnter (e) {
|
||||
}
|
||||
function handleClick () {
|
||||
doClick()
|
||||
}
|
||||
function handleContentDragStart (e: DragEvent) {
|
||||
doDragStart(e)
|
||||
}
|
||||
function handleContentDragEnter (e: DragEvent) {
|
||||
if (
|
||||
e.currentTarget &&
|
||||
e.relatedTarget &&
|
||||
e.currentTarget.contains(e.relatedTarget)
|
||||
(e.currentTarget as HTMLElement).contains(
|
||||
e.relatedTarget as HTMLElement
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
this.doDragEnter(e)
|
||||
},
|
||||
handleDragOverContent (e) {
|
||||
doDragEnter(e)
|
||||
}
|
||||
function handleDragOverContent (e: DragEvent) {
|
||||
e.preventDefault()
|
||||
const el = this.$el
|
||||
this.pending = true
|
||||
const el = selfRef.value as HTMLElement
|
||||
pendingRef.value = true
|
||||
const elOffsetHeight = el.offsetHeight
|
||||
const elClientTop = el.getBoundingClientRect().top
|
||||
const eventOffsetY = e.clientY - elClientTop
|
||||
if (eventOffsetY <= 8) {
|
||||
this.pendingPosition = 'top'
|
||||
pendingPositionRef.value = 'top'
|
||||
} else if (eventOffsetY >= elOffsetHeight - 8) {
|
||||
this.pendingPosition = 'bottom'
|
||||
pendingPositionRef.value = 'bottom'
|
||||
} else {
|
||||
this.pendingPosition = 'body'
|
||||
pendingPositionRef.value = 'center'
|
||||
}
|
||||
this.doDragOver(e)
|
||||
},
|
||||
handleContentDragEnd (e) {
|
||||
this.doDragEnd(e)
|
||||
},
|
||||
handleContentDragLeave (e) {
|
||||
}
|
||||
function handleContentDragEnd (e: DragEvent) {
|
||||
doDragEnd(e)
|
||||
}
|
||||
function handleContentDragLeave (e: DragEvent) {
|
||||
if (
|
||||
e.currentTarget &&
|
||||
e.relatedTarget &&
|
||||
e.currentTarget.contains(e.relatedTarget)
|
||||
(e.currentTarget as HTMLElement).contains(
|
||||
e.relatedTarget as HTMLElement
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
this.pending = false
|
||||
this.doDragLeave(e)
|
||||
},
|
||||
handleContentDrop (e) {
|
||||
pendingRef.value = false
|
||||
doDragLeave(e)
|
||||
}
|
||||
function handleContentDrop (e: DragEvent) {
|
||||
e.preventDefault()
|
||||
this.pending = false
|
||||
const dropPosition = {
|
||||
top: 'top',
|
||||
bottom: 'bottom',
|
||||
body: 'center'
|
||||
}[this.pendingPosition]
|
||||
this.doDrop(e, dropPosition)
|
||||
pendingRef.value = false
|
||||
if (pendingPositionRef.value !== null) {
|
||||
const dropPosition = {
|
||||
top: 'top',
|
||||
bottom: 'bottom',
|
||||
center: 'center'
|
||||
}[pendingPositionRef.value]
|
||||
doDrop(e, dropPosition as 'top' | 'bottom' | 'center')
|
||||
}
|
||||
}
|
||||
return {
|
||||
selfRef,
|
||||
pending: pendingRef,
|
||||
pendingPosition: pendingPositionRef,
|
||||
handleContentDragLeave,
|
||||
handleContentDragStart,
|
||||
handleDragOverContent,
|
||||
handleContentDragEnd,
|
||||
handleContentDragEnter,
|
||||
handleContentDrop,
|
||||
handleClick
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -18,7 +18,7 @@
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { SwitcherIcon } from '../../_base/icons'
|
||||
import { NIconSwitchTransition, NBaseLoading, NBaseIcon } from '../../_base'
|
||||
@ -54,13 +54,15 @@ export default defineComponent({
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doClick () {
|
||||
const { onClick } = this
|
||||
setup (props) {
|
||||
function doClick () {
|
||||
const { onClick } = props
|
||||
if (onClick) onClick()
|
||||
},
|
||||
handleClick () {
|
||||
this.doClick()
|
||||
}
|
||||
return {
|
||||
handleClick () {
|
||||
doClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1,161 +0,0 @@
|
||||
import { c, cB, cE, cM } from '../../../_utils/cssr'
|
||||
import fadeInHeightExpandTransition from '../../../_styles/transitions/fade-in-height-expand'
|
||||
import iconSwitchTransition from '../../../_styles/transitions/icon-switch'
|
||||
|
||||
// vars:
|
||||
// --arrow-color
|
||||
// --bezier
|
||||
// --font-size
|
||||
// --node-border-radius
|
||||
// --node-color-active
|
||||
// --node-color-hover
|
||||
// --node-color-pressed
|
||||
// --node-text-color
|
||||
// --node-text-color-disabled
|
||||
export default cB('tree', {
|
||||
fontSize: 'var(--font-size)'
|
||||
}, [
|
||||
c('ul, li', `
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
`),
|
||||
c('>', [
|
||||
cB('tree-node', [
|
||||
c('&:first-child', {
|
||||
paddingTop: 0
|
||||
})
|
||||
])
|
||||
]),
|
||||
cB('tree-children-wrapper', {
|
||||
marginLeft: '16px'
|
||||
}, [
|
||||
fadeInHeightExpandTransition({ duration: '0.15s' })
|
||||
]),
|
||||
cB('tree-node', {
|
||||
padding: '6px 0 0 0'
|
||||
}),
|
||||
cB('tree-node-switcher', `
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: transform .15s var(--bezier);
|
||||
vertical-align: bottom;
|
||||
`, [
|
||||
cE('icon', `
|
||||
position: relative;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
display: flex;
|
||||
color: var(--arrow-color);
|
||||
transition: color .3s var(--bezier);
|
||||
font-size: 14px;
|
||||
`, [
|
||||
cB('icon', [
|
||||
iconSwitchTransition()
|
||||
]),
|
||||
cB('base-loading', `
|
||||
color: var(--loading-color);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
`, [
|
||||
iconSwitchTransition()
|
||||
])
|
||||
]),
|
||||
cM('hide', {
|
||||
visibility: 'hidden'
|
||||
}),
|
||||
cM('expanded', {
|
||||
transform: 'rotate(90deg)'
|
||||
})
|
||||
]),
|
||||
cB('tree-node-checkbox', `
|
||||
display: inline-flex;
|
||||
height: 24px;
|
||||
width: 16px;
|
||||
vertical-align: bottom;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 4px;
|
||||
`),
|
||||
cB('tree-node-content', `
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
height: 24px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 3px solid transparent;
|
||||
border-top: 3px solid transparent;
|
||||
line-height: 24px;
|
||||
align-items: center;
|
||||
vertical-align: bottom;
|
||||
padding: 0 6px;
|
||||
cursor: pointer;
|
||||
border-radius: var(--node-border-radius);
|
||||
text-decoration-color: transparent;
|
||||
text-decoration-line: underline;
|
||||
color: var(--node-text-color);
|
||||
transition:
|
||||
color .3s var(--bezier),
|
||||
text-decoration-color .3s var(--bezier),
|
||||
background-color .3s var(--bezier),
|
||||
border-color .3s var(--bezier);
|
||||
`, [
|
||||
c('&:last-child', {
|
||||
marginBottom: 0
|
||||
}),
|
||||
cE('padding-box', `
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: transparent;
|
||||
`),
|
||||
cE('text', `
|
||||
line-height: 1.25;
|
||||
border-bottom: 1px solid transparent;
|
||||
transition: border-color .3s var(--bezier);
|
||||
`),
|
||||
cM('block', {
|
||||
width: 'calc(100% - 24px)'
|
||||
}, [
|
||||
cM('checkable', {
|
||||
width: 'calc(100% - 48px)'
|
||||
})
|
||||
]),
|
||||
c('&:hover', {
|
||||
backgroundColor: 'var(--node-color-hover)'
|
||||
}),
|
||||
c('&:active', {
|
||||
backgroundColor: 'var(--node-color-pressed)'
|
||||
}),
|
||||
cM('hightlight', [
|
||||
cE('text', {
|
||||
borderBottomColor: 'var(--node-text-color-disabled)'
|
||||
})
|
||||
]),
|
||||
cM('pending', [
|
||||
c('&:hover', {
|
||||
backgroundColor: 'transparent'
|
||||
}),
|
||||
cM('pending-bottom', {
|
||||
borderBottom: '3px solid var(--node-color-hover)'
|
||||
}),
|
||||
cM('pending-top', {
|
||||
borderTop: '3px solid var(--node-color-hover)'
|
||||
}),
|
||||
cM('pending-body', {
|
||||
backgroundColor: 'var(--node-color-hover)'
|
||||
})
|
||||
]),
|
||||
cM('selected', {
|
||||
backgroundColor: 'var(--node-color-active)'
|
||||
})
|
||||
])
|
||||
])
|
195
src/tree/src/styles/index.cssr.ts
Normal file
195
src/tree/src/styles/index.cssr.ts
Normal file
@ -0,0 +1,195 @@
|
||||
import { c, cB, cE, cM } from '../../../_utils/cssr'
|
||||
import fadeInHeightExpandTransition from '../../../_styles/transitions/fade-in-height-expand'
|
||||
import iconSwitchTransition from '../../../_styles/transitions/icon-switch'
|
||||
|
||||
// vars:
|
||||
// --arrow-color
|
||||
// --bezier
|
||||
// --font-size
|
||||
// --node-border-radius
|
||||
// --node-color-active
|
||||
// --node-color-hover
|
||||
// --node-color-pressed
|
||||
// --node-text-color
|
||||
// --node-text-color-disabled
|
||||
export default cB(
|
||||
'tree',
|
||||
{
|
||||
fontSize: 'var(--font-size)'
|
||||
},
|
||||
[
|
||||
c(
|
||||
'ul, li',
|
||||
`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
`
|
||||
),
|
||||
c('>', [
|
||||
cB('tree-node', [
|
||||
c('&:first-child', {
|
||||
paddingTop: 0
|
||||
})
|
||||
])
|
||||
]),
|
||||
cB(
|
||||
'tree-children-wrapper',
|
||||
{
|
||||
marginLeft: '16px'
|
||||
},
|
||||
[fadeInHeightExpandTransition({ duration: '0.15s' })]
|
||||
),
|
||||
cB('tree-node', {
|
||||
padding: '6px 0 0 0'
|
||||
}),
|
||||
cB(
|
||||
'tree-node-switcher',
|
||||
`
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: transform .15s var(--bezier);
|
||||
vertical-align: bottom;
|
||||
`,
|
||||
[
|
||||
cE(
|
||||
'icon',
|
||||
`
|
||||
position: relative;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
display: flex;
|
||||
color: var(--arrow-color);
|
||||
transition: color .3s var(--bezier);
|
||||
font-size: 14px;
|
||||
`,
|
||||
[
|
||||
cB('icon', [iconSwitchTransition()]),
|
||||
cB(
|
||||
'base-loading',
|
||||
`
|
||||
color: var(--loading-color);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
`,
|
||||
[iconSwitchTransition()]
|
||||
)
|
||||
]
|
||||
),
|
||||
cM('hide', {
|
||||
visibility: 'hidden'
|
||||
}),
|
||||
cM('expanded', {
|
||||
transform: 'rotate(90deg)'
|
||||
})
|
||||
]
|
||||
),
|
||||
cB(
|
||||
'tree-node-checkbox',
|
||||
`
|
||||
display: inline-flex;
|
||||
height: 24px;
|
||||
width: 16px;
|
||||
vertical-align: bottom;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 4px;
|
||||
`
|
||||
),
|
||||
cB(
|
||||
'tree-node-content',
|
||||
`
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
height: 24px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 3px solid transparent;
|
||||
border-top: 3px solid transparent;
|
||||
line-height: 24px;
|
||||
align-items: center;
|
||||
vertical-align: bottom;
|
||||
padding: 0 6px;
|
||||
cursor: pointer;
|
||||
border-radius: var(--node-border-radius);
|
||||
text-decoration-color: transparent;
|
||||
text-decoration-line: underline;
|
||||
color: var(--node-text-color);
|
||||
transition:
|
||||
color .3s var(--bezier),
|
||||
text-decoration-color .3s var(--bezier),
|
||||
background-color .3s var(--bezier),
|
||||
border-color .3s var(--bezier);
|
||||
`,
|
||||
[
|
||||
c('&:last-child', {
|
||||
marginBottom: 0
|
||||
}),
|
||||
cE(
|
||||
'padding-box',
|
||||
`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: transparent;
|
||||
`
|
||||
),
|
||||
cE(
|
||||
'text',
|
||||
`
|
||||
line-height: 1.25;
|
||||
border-bottom: 1px solid transparent;
|
||||
transition: border-color .3s var(--bezier);
|
||||
`
|
||||
),
|
||||
cM(
|
||||
'block',
|
||||
{
|
||||
width: 'calc(100% - 24px)'
|
||||
},
|
||||
[
|
||||
cM('checkable', {
|
||||
width: 'calc(100% - 48px)'
|
||||
})
|
||||
]
|
||||
),
|
||||
c('&:hover', {
|
||||
backgroundColor: 'var(--node-color-hover)'
|
||||
}),
|
||||
c('&:active', {
|
||||
backgroundColor: 'var(--node-color-pressed)'
|
||||
}),
|
||||
cM('hightlight', [
|
||||
cE('text', {
|
||||
borderBottomColor: 'var(--node-text-color-disabled)'
|
||||
})
|
||||
]),
|
||||
cM('pending', [
|
||||
c('&:hover', {
|
||||
backgroundColor: 'transparent'
|
||||
}),
|
||||
cM('pending-bottom', {
|
||||
borderBottom: '3px solid var(--node-color-hover)'
|
||||
}),
|
||||
cM('pending-top', {
|
||||
borderTop: '3px solid var(--node-color-hover)'
|
||||
}),
|
||||
cM('pending-body', {
|
||||
backgroundColor: 'var(--node-color-hover)'
|
||||
})
|
||||
]),
|
||||
cM('selected', {
|
||||
backgroundColor: 'var(--node-color-active)'
|
||||
})
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
@ -1,34 +0,0 @@
|
||||
function traverse (nodes, callback, callbackAfter) {
|
||||
nodes &&
|
||||
nodes.forEach((node) => {
|
||||
callback(node)
|
||||
traverse(node.children, callback, callbackAfter)
|
||||
callbackAfter && callbackAfter(node)
|
||||
})
|
||||
}
|
||||
|
||||
export function keysWithFilter (nodes, pattern, filter) {
|
||||
const keys = new Set()
|
||||
const highlightKeys = new Set()
|
||||
const path = []
|
||||
traverse(
|
||||
nodes,
|
||||
(node) => {
|
||||
path.push(node)
|
||||
if (filter(pattern, node)) {
|
||||
highlightKeys.add(node.key)
|
||||
for (let i = path.length - 2; i >= 0; --i) {
|
||||
if (!keys.has(path[i].key)) {
|
||||
keys.add(path[i].key)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
() => {
|
||||
path.pop()
|
||||
}
|
||||
)
|
||||
return [Array.from(keys), Array.from(highlightKeys)]
|
||||
}
|
44
src/tree/src/utils.ts
Normal file
44
src/tree/src/utils.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { Key, KeyedRawNode } from 'treemate'
|
||||
|
||||
function traverse (
|
||||
nodes: KeyedRawNode[] | undefined,
|
||||
callback: (node: KeyedRawNode) => void,
|
||||
callbackAfter: (node: KeyedRawNode) => void
|
||||
) {
|
||||
nodes &&
|
||||
nodes.forEach((node) => {
|
||||
callback(node)
|
||||
traverse(node.children, callback, callbackAfter)
|
||||
callbackAfter && callbackAfter(node)
|
||||
})
|
||||
}
|
||||
|
||||
export function keysWithFilter (
|
||||
nodes: KeyedRawNode[],
|
||||
pattern: string,
|
||||
filter: (pattern: string, node: KeyedRawNode) => boolean
|
||||
): [Key[], Key[]] {
|
||||
const keys = new Set<Key>()
|
||||
const highlightKeys = new Set<Key>()
|
||||
const path: KeyedRawNode[] = []
|
||||
traverse(
|
||||
nodes,
|
||||
(node) => {
|
||||
path.push(node)
|
||||
if (filter(pattern, node)) {
|
||||
highlightKeys.add(node.key as Key)
|
||||
for (let i = path.length - 2; i >= 0; --i) {
|
||||
if (!keys.has(path[i].key as Key)) {
|
||||
keys.add(path[i].key as Key)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
() => {
|
||||
path.pop()
|
||||
}
|
||||
)
|
||||
return [Array.from(keys), Array.from(highlightKeys)]
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
import { changeColor } from 'seemly'
|
||||
import { checkboxDark } from '../../checkbox/styles'
|
||||
import { commonDark } from '../../_styles/new-common'
|
||||
import type { ThemeCommonVars } from '../../_styles/new-common'
|
||||
import type { TreeThemeVars } from './light'
|
||||
|
||||
export default {
|
||||
name: 'Tree',
|
||||
@ -8,7 +10,7 @@ export default {
|
||||
peers: {
|
||||
Checkbox: checkboxDark
|
||||
},
|
||||
self (vars) {
|
||||
self (vars: ThemeCommonVars): TreeThemeVars {
|
||||
const {
|
||||
borderRadiusSmall,
|
||||
hoverColorOverlay,
|
@ -1,2 +0,0 @@
|
||||
export { default as treeDark } from './dark.js'
|
||||
export { default as treeLight } from './light.js'
|
3
src/tree/styles/index.ts
Normal file
3
src/tree/styles/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { default as treeDark } from './dark'
|
||||
export { default as treeLight } from './light'
|
||||
export type { TreeThemeVars } from './light'
|
@ -1,14 +1,15 @@
|
||||
import { changeColor } from 'seemly'
|
||||
import { checkboxLight } from '../../checkbox/styles'
|
||||
import { commonLight } from '../../_styles/new-common'
|
||||
import type { ThemeCommonVars } from '../../_styles/new-common'
|
||||
|
||||
export default {
|
||||
const treeLight = {
|
||||
name: 'Tree',
|
||||
common: commonLight,
|
||||
peers: {
|
||||
Checkbox: checkboxLight
|
||||
},
|
||||
self (vars) {
|
||||
self (vars: ThemeCommonVars) {
|
||||
const {
|
||||
borderRadiusSmall,
|
||||
hoverColorOverlay,
|
||||
@ -32,3 +33,6 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default treeLight
|
||||
export type TreeThemeVars = ReturnType<typeof treeLight.self>
|
Loading…
Reference in New Issue
Block a user