refactor: collapse

pull/4606/head
tangjinzhou 2021-08-30 22:20:30 +08:00
parent af182533c1
commit 0ade597b55
15 changed files with 366 additions and 450 deletions

View File

@ -5,3 +5,5 @@ tree、tree-slelct: 新增虚拟滚动、title 逻辑变动
date 相关组件: dayjs UI 变动
steps: add responsive、percent
collapse: add ghost、collapsible

View File

@ -1,87 +1,179 @@
import type { CSSProperties, ExtractPropTypes, PropType } from 'vue';
import { defineComponent, inject } from 'vue';
import animation from '../_util/openAnimation';
import { getOptionProps, getComponent, isValidElement, getSlot } from '../_util/props-util';
import {
isEmptyElement,
initDefaultProps,
flattenChildren,
isValidElement,
} from '../_util/props-util';
import { cloneElement } from '../_util/vnode';
import VcCollapse from '../vc-collapse';
import type { CollapsibleType } from './commonProps';
import { collapseProps } from './commonProps';
import { getDataAndAriaProps } from '../_util/util';
import type { ExtractPropTypes } from 'vue';
import { computed, defineComponent, ref, watch } from 'vue';
import RightOutlined from '@ant-design/icons-vue/RightOutlined';
import { defaultConfigProvider } from '../config-provider';
import PropTypes from '../_util/vue-types';
import type { VueNode } from '../_util/type';
import { tuple } from '../_util/type';
import firstNotUndefined from '../_util/firstNotUndefined';
import classNames from '../_util/classNames';
import animation from '../_util/openAnimation';
import useConfigInject from '../_util/hooks/useConfigInject';
import type { CollapsePanelProps } from './CollapsePanel';
export interface PanelProps {
isActive?: boolean;
header?: VueNode;
className?: string;
class?: string;
style?: CSSProperties;
showArrow?: boolean;
forceRender?: boolean;
disabled?: boolean;
extra?: VueNode;
type Key = number | string;
function getActiveKeysArray(activeKey: Key | Key[]) {
let currentActiveKey = activeKey;
if (!Array.isArray(currentActiveKey)) {
const activeKeyType = typeof currentActiveKey;
currentActiveKey =
activeKeyType === 'number' || activeKeyType === 'string' ? [currentActiveKey] : [];
}
return currentActiveKey.map(key => String(key));
}
type ActiveKeyType = Array<string | number> | string | number;
const collapseProps = {
prefixCls: PropTypes.string,
activeKey: { type: [Array, Number, String] as PropType<ActiveKeyType> },
defaultActiveKey: { type: [Array, Number, String] as PropType<ActiveKeyType> },
accordion: PropTypes.looseBool,
destroyInactivePanel: PropTypes.looseBool,
bordered: PropTypes.looseBool.def(true),
expandIcon: PropTypes.func,
openAnimation: PropTypes.object.def(animation),
expandIconPosition: PropTypes.oneOf(tuple('left', 'right')).def('left'),
'onUpdate:activeKey': PropTypes.func,
onChange: PropTypes.func,
};
export type CollapseProps = Partial<ExtractPropTypes<typeof collapseProps>>;
export { collapseProps };
export type CollapseProps = Partial<ExtractPropTypes<ReturnType<typeof collapseProps>>>;
export default defineComponent({
name: 'ACollapse',
inheritAttrs: false,
props: collapseProps,
setup() {
return {
configProvider: inject('configProvider', defaultConfigProvider),
};
},
methods: {
renderExpandIcon(panelProps: PanelProps = {}, prefixCls: string) {
const expandIcon = getComponent(this, 'expandIcon', panelProps);
const icon = expandIcon || <RightOutlined rotate={panelProps.isActive ? 90 : undefined} />;
props: initDefaultProps(collapseProps(), {
accordion: false,
destroyInactivePanel: false,
bordered: true,
openAnimation: animation,
expandIconPosition: 'left',
}),
slots: ['expandIcon'],
emits: ['change', 'update:activeKey'],
setup(props, { attrs, slots, emit }) {
const stateActiveKey = ref<Key[]>(
getActiveKeysArray(firstNotUndefined([props.activeKey, props.defaultActiveKey])),
);
watch(
() => props.activeKey,
() => {
stateActiveKey.value = getActiveKeysArray(props.activeKey);
},
);
const { prefixCls, direction } = useConfigInject('collapse', props);
const iconPosition = computed(() => {
const { expandIconPosition } = props;
if (expandIconPosition !== undefined) {
return expandIconPosition;
}
return direction.value === 'rtl' ? 'right' : 'left';
});
const renderExpandIcon = (panelProps: CollapsePanelProps) => {
const { expandIcon = slots.expandIcon } = props;
const icon = expandIcon ? (
expandIcon(panelProps)
) : (
<RightOutlined rotate={panelProps.isActive ? 90 : undefined} />
);
return isValidElement(Array.isArray(expandIcon) ? icon[0] : icon)
? cloneElement(icon, {
class: `${prefixCls}-arrow`,
})
? cloneElement(
icon,
{
class: `${prefixCls.value}-arrow`,
},
false,
)
: icon;
},
handleChange(activeKey: ActiveKeyType) {
this.$emit('update:activeKey', activeKey);
this.$emit('change', activeKey);
},
},
render() {
const { prefixCls: customizePrefixCls, bordered, expandIconPosition } = this;
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('collapse', customizePrefixCls);
const { class: className, ...restAttrs } = this.$attrs;
const collapseClassName = {
[className as string]: className,
[`${prefixCls}-borderless`]: !bordered,
[`${prefixCls}-icon-position-${expandIconPosition}`]: true,
};
const rcCollapeProps = {
...getOptionProps(this),
prefixCls,
expandIcon: (panelProps: PanelProps) => this.renderExpandIcon(panelProps, prefixCls),
class: collapseClassName,
...restAttrs,
onChange: this.handleChange,
const setActiveKey = (activeKey: Key[]) => {
if (props.activeKey === undefined) {
stateActiveKey.value = activeKey;
}
const newKey = props.accordion ? activeKey[0] : activeKey;
emit('update:activeKey', newKey);
emit('change', newKey);
};
const onClickItem = (key: Key) => {
let activeKey = stateActiveKey.value;
if (props.accordion) {
activeKey = activeKey[0] === key ? [] : [key];
} else {
activeKey = [...activeKey];
const index = activeKey.indexOf(key);
const isActive = index > -1;
if (isActive) {
// remove active state
activeKey.splice(index, 1);
} else {
activeKey.push(key);
}
}
setActiveKey(activeKey);
};
return <VcCollapse {...rcCollapeProps}>{getSlot(this)}</VcCollapse>;
const getNewChild = (child, index) => {
if (isEmptyElement(child)) return;
const activeKey = stateActiveKey.value;
const { accordion, destroyInactivePanel, collapsible, openAnimation } = props;
// If there is no key provide, use the panel order as default key
const key = String(child.key ?? index);
const {
header = child.children?.header?.(),
headerClass,
collapsible: childCollapsible,
disabled,
} = child.props || {};
let isActive = false;
if (accordion) {
isActive = activeKey[0] === key;
} else {
isActive = activeKey.indexOf(key) > -1;
}
let mergeCollapsible: CollapsibleType = childCollapsible ?? collapsible;
// legacy 2.x
if (disabled || disabled === '') {
mergeCollapsible = 'disabled';
}
const newProps = {
key,
panelKey: key,
header,
headerClass,
isActive,
prefixCls: prefixCls.value,
destroyInactivePanel,
openAnimation,
accordion,
onItemClick: mergeCollapsible === 'disabled' ? null : onClickItem,
expandIcon: renderExpandIcon,
collapsible: mergeCollapsible,
};
return cloneElement(child, newProps);
};
const getItems = () => {
return flattenChildren(slots.default?.()).map(getNewChild);
};
return () => {
const { accordion, bordered, ghost } = props;
const collapseClassName = classNames({
[prefixCls.value]: true,
[`${prefixCls.value}-borderless`]: !bordered,
[`${prefixCls.value}-icon-position-${iconPosition.value}`]: true,
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
[`${prefixCls.value}-ghost`]: !!ghost,
[attrs.class as string]: !!attrs.class,
});
return (
<div
class={collapseClassName}
{...getDataAndAriaProps(attrs)}
style={attrs.style}
role={accordion ? 'tablist' : null}
>
{getItems()}
</div>
);
};
},
});

View File

@ -1,54 +1,114 @@
import PanelContent from './PanelContent';
import { initDefaultProps } from '../_util/props-util';
import { collapsePanelProps } from './commonProps';
import type { ExtractPropTypes } from 'vue';
import { defineComponent, inject } from 'vue';
import { getOptionProps, getComponent, getSlot } from '../_util/props-util';
import VcCollapse from '../vc-collapse';
import { defaultConfigProvider } from '../config-provider';
import PropTypes from '../_util/vue-types';
import { defineComponent } from 'vue';
import Transition from '../_util/transition';
import classNames from '../_util/classNames';
import devWarning from '../vc-util/devWarning';
import useConfigInject from '../_util/hooks/useConfigInject';
const collapsePanelProps = {
openAnimation: PropTypes.object,
prefixCls: PropTypes.string,
header: PropTypes.VNodeChild,
headerClass: PropTypes.string,
showArrow: PropTypes.looseBool,
isActive: PropTypes.looseBool,
destroyInactivePanel: PropTypes.looseBool,
disabled: PropTypes.looseBool,
accordion: PropTypes.looseBool,
forceRender: PropTypes.looseBool,
expandIcon: PropTypes.func,
extra: PropTypes.VNodeChild,
panelKey: PropTypes.VNodeChild,
};
export type CollapsePanelProps = Partial<ExtractPropTypes<typeof collapsePanelProps>>;
export { collapsePanelProps };
export type CollapsePanelProps = Partial<ExtractPropTypes<ReturnType<typeof collapsePanelProps>>>;
export default defineComponent({
name: 'ACollapsePanel',
inheritAttrs: false,
props: collapsePanelProps,
setup() {
return {
configProvider: inject('configProvider', defaultConfigProvider),
props: initDefaultProps(collapsePanelProps(), {
showArrow: true,
isActive: false,
onItemClick() {},
headerClass: '',
forceRender: false,
}),
slots: ['expandIcon', 'extra', 'header'],
emits: ['itemClick'],
setup(props, { slots, emit }) {
devWarning(
props.disabled === undefined,
'Collapse.Panel',
'`disabled` is deprecated. Please use `collapsible="disabled"` instead.',
);
const { prefixCls } = useConfigInject('collapse', props);
const handleItemClick = () => {
emit('itemClick', props.panelKey);
};
},
render() {
const { prefixCls: customizePrefixCls, showArrow = true } = this;
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('collapse', customizePrefixCls);
const { class: className, ...restAttrs } = this.$attrs;
const collapsePanelClassName = {
[className as string]: className,
[`${prefixCls}-no-arrow`]: !showArrow,
const handleKeyPress = (e: KeyboardEvent) => {
if (e.key === 'Enter' || e.keyCode === 13 || e.which === 13) {
handleItemClick();
}
};
return () => {
const {
header = slots.header?.(),
headerClass,
isActive,
showArrow,
destroyInactivePanel,
accordion,
forceRender,
openAnimation,
expandIcon = slots.expandIcon,
extra = slots.extra?.(),
collapsible,
} = props;
const disabled = collapsible === 'disabled';
const prefixClsValue = prefixCls.value;
const headerCls = classNames(`${prefixClsValue}-header`, {
[headerClass]: headerClass,
[`${prefixClsValue}-header-collapsible-only`]: collapsible === 'header',
});
const itemCls = classNames({
[`${prefixClsValue}-item`]: true,
[`${prefixClsValue}-item-active`]: isActive,
[`${prefixClsValue}-item-disabled`]: disabled,
[`${prefixClsValue}-no-arrow`]: !showArrow,
});
const rcCollapePanelProps = {
...getOptionProps(this),
header: getComponent(this, 'header'),
prefixCls,
extra: getComponent(this, 'extra'),
class: collapsePanelClassName,
...restAttrs,
let icon = <i class="arrow" />;
if (showArrow && typeof expandIcon === 'function') {
icon = expandIcon(props);
}
const panelContent = (
<PanelContent
v-show={isActive}
prefixCls={prefixClsValue}
isActive={isActive}
forceRender={forceRender}
role={accordion ? 'tabpanel' : null}
v-slots={{ default: slots.default }}
></PanelContent>
);
const transitionProps = {
appear: true,
css: false,
...openAnimation,
};
return (
<div class={itemCls}>
<div
class={headerCls}
onClick={() => collapsible !== 'header' && handleItemClick()}
role={accordion ? 'tab' : 'button'}
tabindex={disabled ? -1 : 0}
aria-expanded={isActive}
onKeypress={handleKeyPress}
>
{showArrow && icon}
{collapsible === 'header' ? (
<span onClick={handleItemClick} class={`${prefixClsValue}-header-text`}>
{header}
</span>
) : (
header
)}
{extra && <div class={`${prefixClsValue}-extra`}>{extra}</div>}
</div>
<Transition {...transitionProps}>
{!destroyInactivePanel || isActive ? panelContent : null}
</Transition>
</div>
);
};
return <VcCollapse.Panel {...rcCollapePanelProps}>{getSlot(this)}</VcCollapse.Panel>;
},
});

View File

@ -1,10 +1,10 @@
import { defineComponent, ref, watchEffect } from 'vue';
import { panelProps } from './commonProps';
import { collapsePanelProps } from './commonProps';
import classNames from '../_util/classNames';
export default defineComponent({
name: 'PanelContent',
props: panelProps(),
props: collapsePanelProps(),
setup(props, { slots }) {
const rendered = ref(false);

View File

@ -2,8 +2,9 @@
exports[`Collapse should support remove expandIcon 1`] = `
<div class="ant-collapse ant-collapse-icon-position-left">
<div class="ant-collapse-item" role="tablist">
<div class="ant-collapse-header" role="button" tabindex="0" aria-expanded="false"><span role="img" aria-label="right" class="anticon anticon-right ant-collapse-arrow"><svg focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path></svg></span>header
<div class="ant-collapse-item">
<div class="ant-collapse-header" role="button" tabindex="0" aria-expanded="false">
<!---->header
<!---->
</div>
<!---->

View File

@ -1,31 +1,25 @@
import type { PropType } from 'vue';
import { tuple } from '../_util/type';
import PropTypes from '../_util/vue-types';
export type CollapsibleType = 'header' | 'disabled';
export type ActiveKeyType = Array<string | number> | string | number;
const collapseProps = () => ({
prefixCls: PropTypes.string,
activeKey: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
]),
defaultActiveKey: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
]),
activeKey: { type: [Array, Number, String] as PropType<ActiveKeyType> },
defaultActiveKey: { type: [Array, Number, String] as PropType<ActiveKeyType> },
accordion: PropTypes.looseBool,
destroyInactivePanel: PropTypes.looseBool,
bordered: PropTypes.looseBool,
expandIcon: PropTypes.func,
openAnimation: PropTypes.object,
expandIconPosition: PropTypes.oneOf(['left', 'right']),
onChange: PropTypes.func,
expandIconPosition: PropTypes.oneOf(tuple('left', 'right')),
collapsible: { type: String as PropType<CollapsibleType> },
ghost: PropTypes.looseBool,
});
const panelProps = () => ({
const collapsePanelProps = () => ({
openAnimation: PropTypes.object,
prefixCls: PropTypes.string,
header: PropTypes.any,
@ -33,6 +27,7 @@ const panelProps = () => ({
showArrow: PropTypes.looseBool,
isActive: PropTypes.looseBool,
destroyInactivePanel: PropTypes.looseBool,
/** @deprecated Use `collapsible="disabled"` instead */
disabled: PropTypes.looseBool,
accordion: PropTypes.looseBool,
forceRender: PropTypes.looseBool,
@ -44,4 +39,4 @@ const panelProps = () => ({
onItemClick: { type: Function as PropType<(panelKey: string | number) => void> },
});
export { collapseProps, panelProps };
export { collapseProps, collapsePanelProps };

View File

@ -1,6 +1,6 @@
import type { App, Plugin } from 'vue';
import Collapse from './Collapse';
import CollapsePanel from './CollapsePanel';
import Collapse, { collapseProps } from './Collapse';
import CollapsePanel, { collapsePanelProps } from './CollapsePanel';
export type { CollapseProps } from './Collapse';
export type { CollapsePanelProps } from './CollapsePanel';
@ -13,7 +13,7 @@ Collapse.install = function (app: App) {
return app;
};
export { CollapsePanel };
export { CollapsePanel, collapseProps, collapsePanelProps };
export default Collapse as typeof Collapse &
Plugin & {
readonly Panel: typeof CollapsePanel;

View File

@ -24,21 +24,17 @@
> .@{collapse-prefix-cls}-header {
position: relative;
padding: @collapse-header-padding;
padding-left: @collapse-header-padding-extra;
color: @heading-color;
line-height: 22px;
line-height: @line-height-base;
cursor: pointer;
transition: all 0.3s;
transition: all 0.3s, visibility 0s;
.clearfix();
.@{collapse-prefix-cls}-arrow {
.iconfont-mixin();
position: absolute;
top: 50%;
left: @padding-md;
display: inline-block;
margin-right: 12px;
font-size: @font-size-sm;
transform: translateY(-50%);
vertical-align: -1px;
& svg {
transition: transform 0.24s;
@ -54,6 +50,13 @@
}
}
.@{collapse-prefix-cls}-header-collapsible-only {
cursor: default;
.@{collapse-prefix-cls}-header-text {
cursor: pointer;
}
}
&.@{collapse-prefix-cls}-no-arrow {
> .@{collapse-prefix-cls}-header {
padding-left: 12px;
@ -69,19 +72,18 @@
padding-right: @collapse-header-padding-extra;
.@{collapse-prefix-cls}-arrow {
position: absolute;
top: 50%;
right: @padding-md;
left: auto;
margin: 0;
transform: translateY(-50%);
}
}
}
}
&-anim-active {
transition: height 0.2s @ease-out;
}
&-content {
overflow: hidden;
color: @text-color;
background-color: @collapse-content-bg;
border-top: @border-width-base @border-style-base @border-color-base;
@ -90,7 +92,7 @@
padding: @collapse-content-padding;
}
&-inactive {
&-hidden {
display: none;
}
}
@ -124,6 +126,22 @@
padding-top: 4px;
}
&-ghost {
background-color: transparent;
border: 0;
> .@{collapse-prefix-cls}-item {
border-bottom: 0;
> .@{collapse-prefix-cls}-content {
background-color: transparent;
border-top: 0;
> .@{collapse-prefix-cls}-content-box {
padding-top: 12px;
padding-bottom: 12px;
}
}
}
}
& &-item-disabled > &-header {
&,
& > .arrow {
@ -132,3 +150,5 @@
}
}
}
@import './rtl';

View File

@ -0,0 +1,42 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@collapse-prefix-cls: ~'@{ant-prefix}-collapse';
.@{collapse-prefix-cls} {
&-rtl {
direction: rtl;
}
& > &-item {
> .@{collapse-prefix-cls}-header {
.@{collapse-prefix-cls}-rtl & {
padding: @collapse-header-padding;
padding-right: @collapse-header-padding-extra;
}
.@{collapse-prefix-cls}-arrow {
& svg {
.@{collapse-prefix-cls}-rtl& {
transform: rotate(180deg);
}
}
}
.@{collapse-prefix-cls}-extra {
.@{collapse-prefix-cls}-rtl& {
float: left;
}
}
}
&.@{collapse-prefix-cls}-no-arrow {
> .@{collapse-prefix-cls}-header {
.@{collapse-prefix-cls}-rtl& {
padding-right: 12px;
padding-left: 0;
}
}
}
}
}

View File

@ -1,143 +0,0 @@
import { isEmptyElement, initDefaultProps, flattenChildren } from '../_util/props-util';
import { cloneElement } from '../_util/vnode';
import openAnimationFactory from './openAnimationFactory';
import { collapseProps, CollapsibleType } from './commonProps';
import { getDataAndAriaProps } from '../_util/util';
import { computed, defineComponent, ref, watch } from 'vue';
import firstNotUndefined from '../_util/firstNotUndefined';
import classNames from '../_util/classNames';
type Key = number | string;
function getActiveKeysArray(activeKey: Key | Key[]) {
let currentActiveKey = activeKey;
if (!Array.isArray(currentActiveKey)) {
const activeKeyType = typeof currentActiveKey;
currentActiveKey =
activeKeyType === 'number' || activeKeyType === 'string' ? [currentActiveKey] : [];
}
return currentActiveKey.map(key => String(key));
}
export default defineComponent({
name: 'Collapse',
inheritAttrs: false,
props: initDefaultProps(collapseProps(), {
prefixCls: 'rc-collapse',
accordion: false,
destroyInactivePanel: false,
}),
slots: ['expandIcon'],
emits: ['change'],
setup(props, { attrs, slots, emit }) {
const stateActiveKey = ref<Key[]>(
getActiveKeysArray(firstNotUndefined([props.activeKey, props.defaultActiveKey])),
);
watch(
() => props.activeKey,
() => {
stateActiveKey.value = getActiveKeysArray(props.activeKey);
},
);
const currentOpenAnimations = computed(
() => props.openAnimation || openAnimationFactory(props.prefixCls),
);
const setActiveKey = (activeKey: Key[]) => {
if (props.activeKey === undefined) {
stateActiveKey.value = activeKey;
}
emit('change', props.accordion ? activeKey[0] : activeKey);
};
const onClickItem = (key: Key) => {
let activeKey = stateActiveKey.value;
if (props.accordion) {
activeKey = activeKey[0] === key ? [] : [key];
} else {
activeKey = [...activeKey];
const index = activeKey.indexOf(key);
const isActive = index > -1;
if (isActive) {
// remove active state
activeKey.splice(index, 1);
} else {
activeKey.push(key);
}
}
setActiveKey(activeKey);
};
const getNewChild = (child, index) => {
if (isEmptyElement(child)) return;
const activeKey = stateActiveKey.value;
const {
prefixCls,
accordion,
destroyInactivePanel,
expandIcon = slots.expandIcon,
collapsible,
} = props;
// If there is no key provide, use the panel order as default key
const key = String(child.key ?? index);
const {
header = child.children?.header?.(),
headerClass,
collapsible: childCollapsible,
disabled,
} = child.props || {};
let isActive = false;
if (accordion) {
isActive = activeKey[0] === key;
} else {
isActive = activeKey.indexOf(key) > -1;
}
let mergeCollapsible: CollapsibleType = childCollapsible ?? collapsible;
// legacy 2.x
if (disabled || disabled === '') {
mergeCollapsible = 'disabled';
}
const newProps = {
key,
panelKey: key,
header,
headerClass,
isActive,
prefixCls,
destroyInactivePanel,
openAnimation: currentOpenAnimations.value,
accordion,
onItemClick: mergeCollapsible === 'disabled' ? null : onClickItem,
expandIcon,
collapsible: mergeCollapsible,
};
return cloneElement(child, newProps);
};
const getItems = () => {
return flattenChildren(slots.default?.()).map(getNewChild);
};
return () => {
const { prefixCls, accordion } = props;
const collapseClassName = classNames({
[prefixCls]: true,
[attrs.class as string]: !!attrs.class,
});
return (
<div
class={collapseClassName}
{...getDataAndAriaProps(attrs)}
style={attrs.style}
role={accordion ? 'tablist' : null}
>
{getItems()}
</div>
);
};
},
});

View File

@ -1,109 +0,0 @@
import PanelContent from './PanelContent';
import { initDefaultProps } from '../_util/props-util';
import { panelProps } from './commonProps';
import { defineComponent } from 'vue';
import Transition from '../_util/transition';
import classNames from '../_util/classNames';
import devWarning from '../vc-util/devWarning';
export default defineComponent({
name: 'Panel',
props: initDefaultProps(panelProps(), {
showArrow: true,
isActive: false,
onItemClick() {},
headerClass: '',
forceRender: false,
}),
slots: ['expandIcon', 'extra', 'header'],
emits: ['itemClick'],
setup(props, { slots, emit }) {
devWarning(
!('disabled' in props),
'Collapse.Panel',
'`disabled` is deprecated. Please use `collapsible="disabled"` instead.',
);
const handleItemClick = () => {
emit('itemClick', props.panelKey);
};
const handleKeyPress = (e: KeyboardEvent) => {
if (e.key === 'Enter' || e.keyCode === 13 || e.which === 13) {
handleItemClick();
}
};
return () => {
const {
prefixCls,
header = slots.header?.(),
headerClass,
isActive,
showArrow,
destroyInactivePanel,
accordion,
forceRender,
openAnimation,
expandIcon = slots.expandIcon,
extra = slots.extra?.(),
collapsible,
} = props;
const disabled = collapsible === 'disabled';
const headerCls = classNames(`${prefixCls}-header`, {
[headerClass]: headerClass,
[`${prefixCls}-header-collapsible-only`]: collapsible === 'header',
});
const itemCls = classNames({
[`${prefixCls}-item`]: true,
[`${prefixCls}-item-active`]: isActive,
[`${prefixCls}-item-disabled`]: disabled,
});
let icon = <i class="arrow" />;
if (showArrow && typeof expandIcon === 'function') {
icon = expandIcon(props);
}
const panelContent = (
<PanelContent
v-show={isActive}
prefixCls={prefixCls}
isActive={isActive}
forceRender={forceRender}
role={accordion ? 'tabpanel' : null}
v-slots={{ default: slots.default }}
></PanelContent>
);
const transitionProps = {
appear: true,
css: false,
...openAnimation,
};
return (
<div class={itemCls}>
<div
class={headerCls}
onClick={() => collapsible !== 'header' && handleItemClick()}
role={accordion ? 'tab' : 'button'}
tabindex={disabled ? -1 : 0}
aria-expanded={isActive}
onKeypress={handleKeyPress}
>
{showArrow && icon}
{collapsible === 'header' ? (
<span onClick={handleItemClick} class={`${prefixCls}-header-text`}>
{header}
</span>
) : (
header
)}
{extra && <div class={`${prefixCls}-extra`}>{extra}</div>}
</div>
<Transition {...transitionProps}>
{!destroyInactivePanel || isActive ? panelContent : null}
</Transition>
</div>
);
};
},
});

View File

@ -1,9 +0,0 @@
// based on rc-collapse 3.1.1
import CollapsePanel from './Panel';
import Collapse from './Collapse';
import { collapseProps, panelProps } from './commonProps';
Collapse.Panel = CollapsePanel;
export { collapseProps, panelProps };
export default Collapse;

View File

@ -1,35 +0,0 @@
import cssAnimation from '../_util/css-animation';
function animate(node: HTMLElement, show: boolean, transitionName: string, done: () => void) {
let height: number;
return cssAnimation(node, transitionName, {
start() {
if (!show) {
node.style.height = `${node.offsetHeight}px`;
} else {
height = node.offsetHeight;
node.style.height = '0px';
}
},
active() {
node.style.height = `${show ? height : 0}px`;
},
end() {
node.style.height = '';
done();
},
});
}
function animation(prefixCls: string) {
return {
onEnter(node: HTMLElement, done: () => void) {
return animate(node, true, `${prefixCls}-anim`, done);
},
onLeave(node: HTMLElement, done: () => void) {
return animate(node, false, `${prefixCls}-anim`, done);
},
};
}
export default animation;

2
v2-doc

@ -1 +1 @@
Subproject commit c703df2dd803ee5097b414e2a32482b8fc2225d7
Subproject commit dcd8922ce91de1a48c515d3b06b898d27fcb30f4