@v-c/collapse 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Collapse.tsx DELETED
@@ -1,109 +0,0 @@
1
- import type { Ref } from 'vue'
2
- import type { CollapseProps, Key } from './interface'
3
- import { classNames as classnames } from '@v-c/util'
4
- import useMergedState from '@v-c/util/dist/hooks/useMergedState'
5
- import omit from '@v-c/util/dist/omit'
6
- import pickAttrs from '@v-c/util/dist/pickAttrs'
7
- import { defineComponent, ref, toRef } from 'vue'
8
- import { useItems } from './hooks/useItems'
9
-
10
- function getActiveKeysArray(activeKey: Key | Array<Key>) {
11
- let currentActiveKey = activeKey
12
- if (!Array.isArray(currentActiveKey)) {
13
- const activeKeyType = typeof currentActiveKey
14
- currentActiveKey
15
- = activeKeyType === 'number' || activeKeyType === 'string'
16
- ? [currentActiveKey]
17
- : []
18
- }
19
- return currentActiveKey.map(key => String(key))
20
- }
21
-
22
- const defaults = {
23
- prefixCls: 'vc-collapse',
24
- } as any
25
-
26
- const Collapse = defineComponent<CollapseProps>({
27
- name: 'VcCollapse',
28
- inheritAttrs: false,
29
- setup(props = defaults, { attrs, expose, slots }) {
30
- const refWrapper = ref<HTMLDivElement>()
31
-
32
- const [activeKey, setActiveKey] = useMergedState<
33
- Key | Key[],
34
- Ref<Array<Key>>
35
- >([], {
36
- value: toRef(props, 'activeKey') as Ref<Key | Key[]>,
37
- onChange: v => props.onChange?.(v as Key[]),
38
- defaultValue: props.defaultActiveKey,
39
- postState: getActiveKeysArray,
40
- })
41
-
42
- const getActiveKey = (key: Key) => {
43
- if (props.accordion) {
44
- return activeKey.value[0] === key ? [] : [key]
45
- }
46
-
47
- const index = activeKey.value.indexOf(key)
48
- const isActive = index > -1
49
- if (isActive) {
50
- return activeKey.value.filter(item => item !== key)
51
- }
52
-
53
- return [...activeKey.value, key]
54
- }
55
- const onItemClick = (key: Key) => {
56
- activeKey.value = getActiveKey(key)
57
- setActiveKey(activeKey.value)
58
- }
59
-
60
- expose({
61
- ref: refWrapper,
62
- })
63
-
64
- return () => {
65
- const {
66
- prefixCls = 'vc-collapse',
67
- openMotion,
68
- expandIcon,
69
- collapsible,
70
- accordion,
71
- classNames,
72
- styles,
73
- items,
74
- destroyOnHidden,
75
- } = props
76
-
77
- const collapseClassName = classnames(prefixCls, (attrs as any).class)
78
-
79
- const mergedProps = { ...props, ...omit(attrs, ['class', 'style']) }
80
-
81
- const mergedChildren = useItems(items, slots.default, {
82
- prefixCls,
83
- accordion,
84
- openMotion,
85
- expandIcon,
86
- collapsible,
87
- onItemClick,
88
- activeKey: activeKey.value,
89
- destroyOnHidden,
90
- classNames,
91
- styles,
92
- })
93
-
94
- return (
95
- <div
96
- ref={refWrapper}
97
- class={collapseClassName}
98
- style={(attrs as any).style}
99
- role={accordion ? 'tablist' : undefined}
100
- {...pickAttrs(mergedProps, { aria: true, data: true })}
101
- >
102
- {mergedChildren}
103
- </div>
104
- )
105
- }
106
- },
107
- })
108
-
109
- export default Collapse
package/src/Panel.tsx DELETED
@@ -1,178 +0,0 @@
1
- import type { HTMLAttributes } from 'vue'
2
- import type { CollapsePanelProps } from './interface'
3
- import { classNames as classnames } from '@v-c/util'
4
- import KeyCode from '@v-c/util/dist/KeyCode'
5
- import omit from '@v-c/util/dist/omit'
6
- import { computed, defineComponent, ref, Transition } from 'vue'
7
- import PanelContent from './PanelContent'
8
-
9
- const defaults = {
10
- showArrow: true,
11
- classNames: {},
12
- styles: {},
13
- } as any
14
-
15
- const CollapsePanel = defineComponent<CollapsePanelProps>({
16
- name: 'CollapsePanel',
17
- inheritAttrs: false,
18
- setup(props = defaults, { attrs, expose }) {
19
- const disabled = computed(() => props.collapsible === 'disabled')
20
- const refWrapper = ref()
21
- const ifExtraExist = computed(
22
- () =>
23
- props.extra !== null
24
- && props.extra !== undefined
25
- && typeof props.extra !== 'boolean',
26
- )
27
-
28
- const collapsibleProps = computed(() => {
29
- return {
30
- 'onClick': () => {
31
- props.onItemClick?.(props.panelKey!)
32
- },
33
- 'onKeydown': (e: KeyboardEvent) => {
34
- if (
35
- e.key === 'Enter'
36
- || e.keyCode === KeyCode.ENTER
37
- || e.which === KeyCode.ENTER
38
- ) {
39
- props.onItemClick?.(props.panelKey!)
40
- }
41
- },
42
- 'role': props.accordion ? 'tab' : 'button',
43
- 'aria-expanded': props.isActive,
44
- 'aria-disabled': disabled.value,
45
- 'tabIndex': disabled.value ? -1 : 0,
46
- }
47
- })
48
-
49
- expose({
50
- ref: refWrapper,
51
- })
52
-
53
- return () => {
54
- const {
55
- extra,
56
- prefixCls,
57
- isActive,
58
- class: className,
59
- expandIcon,
60
- forceRender,
61
- headerClass,
62
- collapsible,
63
- accordion,
64
- openMotion = {},
65
- onItemClick,
66
- classNames: customizeClassNames = {},
67
- showArrow = true,
68
- destroyOnHidden,
69
- styles = {},
70
- header,
71
- panelKey,
72
- children,
73
- ...restProps
74
- } = props
75
-
76
- const collapsePanelClassNames = classnames(
77
- `${prefixCls}-item`,
78
- {
79
- [`${prefixCls}-item-active`]: isActive,
80
- [`${prefixCls}-item-disabled`]: disabled.value,
81
- },
82
- className,
83
- )
84
- const headerClassName = classnames(
85
- headerClass,
86
- `${prefixCls}-header`,
87
- {
88
- [`${prefixCls}-collapsible-${collapsible}`]: !!collapsible,
89
- },
90
- customizeClassNames.header,
91
- )
92
-
93
- const headerProps: HTMLAttributes = {
94
- class: headerClassName,
95
- style: styles.header,
96
- ...(['header', 'icon'].includes(collapsible!)
97
- ? {}
98
- : collapsibleProps.value),
99
- }
100
-
101
- // ======================== Icon ========================
102
- const iconNodeInner
103
- = typeof expandIcon === 'function'
104
- ? (
105
- expandIcon(props)
106
- )
107
- : (
108
- <i class="arrow" />
109
- )
110
- const iconNode = iconNodeInner && (
111
- <div
112
- class={classnames(`${prefixCls}-expand-icon`, customizeClassNames?.icon)}
113
- style={styles?.icon}
114
- {...(['header', 'icon'].includes(collapsible!)
115
- ? collapsibleProps.value
116
- : {})}
117
- >
118
- {iconNodeInner}
119
- </div>
120
- )
121
-
122
- const panelContent = (
123
- <PanelContent
124
- v-show={isActive}
125
- prefixCls={prefixCls}
126
- classNames={customizeClassNames}
127
- styles={styles}
128
- isActive={isActive}
129
- forceRender={forceRender}
130
- role={accordion ? 'tabpanel' : undefined}
131
- v-slots={{ default: () => children }}
132
- />
133
- )
134
-
135
- const transitionProps = {
136
- 'appear': false,
137
- 'leave-to-class': `${prefixCls}-panel-hidden`,
138
- ...openMotion,
139
- }
140
-
141
- const mergedRestProps = {
142
- ...restProps,
143
- ...omit(attrs, ['class']),
144
- }
145
-
146
- return (
147
- <div
148
- {...mergedRestProps as any}
149
- ref={refWrapper}
150
- class={collapsePanelClassNames}
151
- >
152
- <div {...headerProps}>
153
- {showArrow && iconNode}
154
- <span
155
- class={classnames(
156
- `${prefixCls}-title`,
157
- customizeClassNames?.title,
158
- )}
159
- style={styles?.title}
160
- {...(collapsible === 'header' ? collapsibleProps.value : {})}
161
- >
162
- {header}
163
- </span>
164
- {ifExtraExist.value && (
165
- <div class={`${prefixCls}-extra`}>{extra}</div>
166
- )}
167
- </div>
168
-
169
- <Transition {...transitionProps}>
170
- { !destroyOnHidden || isActive ? panelContent : null}
171
- </Transition>
172
- </div>
173
- )
174
- }
175
- },
176
- })
177
-
178
- export default CollapsePanel
@@ -1,63 +0,0 @@
1
- import type { CollapsePanelProps } from './interface'
2
- import { classNames as classnames } from '@v-c/util'
3
- import { defineComponent, ref, watch } from 'vue'
4
-
5
- const PanelContent = defineComponent<CollapsePanelProps>({
6
- name: 'PanelContent',
7
- inheritAttrs: false,
8
- setup(props, { slots }) {
9
- const rendered = ref(props.isActive || props.forceRender)
10
-
11
- watch(
12
- () => [props.isActive, props.forceRender],
13
- () => {
14
- if (props.isActive || props.forceRender) {
15
- rendered.value = true
16
- }
17
- },
18
- )
19
-
20
- return () => {
21
- if (!rendered.value) {
22
- return null
23
- }
24
-
25
- const {
26
- prefixCls,
27
- isActive,
28
- style,
29
- role,
30
- class: className,
31
- classNames: customizeClassNames,
32
- styles,
33
- } = props
34
-
35
- return (
36
- <div
37
- class={classnames(
38
- `${prefixCls}-panel`,
39
- {
40
- [`${prefixCls}-panel-active`]: isActive,
41
- [`${prefixCls}-panel-inactive`]: !isActive,
42
- },
43
- className,
44
- )}
45
- style={style as any}
46
- role={role}
47
- >
48
- <div
49
- class={classnames(
50
- `${prefixCls}-body`,
51
- customizeClassNames?.body,
52
- )}
53
- style={styles?.body}
54
- >
55
- {slots.default?.()}
56
- </div>
57
- </div>
58
- )
59
- }
60
- },
61
- })
62
-
63
- export default PanelContent
@@ -1,202 +0,0 @@
1
- import type { VueNode } from '@v-c/util/dist/type'
2
- import type {
3
- RendererElement,
4
- RendererNode,
5
- VNode,
6
- VNodeNormalizedChildren,
7
- } from 'vue'
8
- import type {
9
- CollapsePanelProps,
10
- CollapseProps,
11
- ItemType,
12
- Key,
13
- } from '../interface'
14
- import { toArray } from '@v-c/util/dist/Children/toArray'
15
- import { isEmptyElement } from '@v-c/util/dist/props-util'
16
- import { cloneElement } from '@v-c/util/dist/vnode'
17
- import CollapsePanel from '../Panel'
18
-
19
- type Props = Pick<
20
- CollapsePanelProps,
21
- | 'prefixCls'
22
- | 'onItemClick'
23
- | 'openMotion'
24
- | 'expandIcon'
25
- | 'classNames'
26
- | 'styles'
27
- > &
28
- Pick<CollapseProps, 'accordion' | 'collapsible' | 'destroyOnHidden'> & {
29
- activeKey: Key[]
30
- }
31
-
32
- interface ChildrenSlots {
33
- default?: () => VueNode
34
- }
35
-
36
- function convertItemsToNodes(items: ItemType[], props: Props) {
37
- const {
38
- prefixCls,
39
- accordion,
40
- collapsible,
41
- onItemClick,
42
- activeKey,
43
- openMotion,
44
- expandIcon,
45
- destroyOnHidden,
46
- classNames: collapseClassNames,
47
- styles,
48
- } = props
49
-
50
- return items.map((item, index) => {
51
- const {
52
- label,
53
- key: rawKey,
54
- collapsible: rawCollapsible,
55
- onItemClick: rawOnItemClick,
56
- destroyOnHidden: rawDestroyOnHidden,
57
- ...restProps
58
- } = item
59
-
60
- const key = String(rawKey ?? index)
61
- const mergeCollapsible = rawCollapsible ?? collapsible
62
- const mergedDestroyOnHidden = rawDestroyOnHidden ?? destroyOnHidden
63
-
64
- const handleItemClick = (value: Key) => {
65
- if (mergeCollapsible === 'disabled')
66
- return
67
-
68
- onItemClick?.(value)
69
- rawOnItemClick?.(value)
70
- }
71
-
72
- let isActive = false
73
-
74
- if (accordion) {
75
- isActive = activeKey?.[0] === key
76
- }
77
- else {
78
- isActive = activeKey.includes(key)
79
- }
80
-
81
- return (
82
- <CollapsePanel
83
- {...restProps}
84
- classNames={collapseClassNames}
85
- styles={styles}
86
- prefixCls={prefixCls}
87
- key={key}
88
- panelKey={key}
89
- isActive={isActive}
90
- accordion={accordion}
91
- openMotion={openMotion}
92
- expandIcon={expandIcon}
93
- header={label}
94
- destroyOnHidden={mergedDestroyOnHidden}
95
- collapsible={mergeCollapsible}
96
- onItemClick={handleItemClick}
97
- />
98
- )
99
- })
100
- }
101
-
102
- /**
103
- * @deprecated The next major version will be removed
104
- */
105
- function getNewChild(
106
- child: VNode<RendererNode, RendererElement, CollapsePanelProps>,
107
- index: number,
108
- props: Props,
109
- ) {
110
- if (isEmptyElement(child) || !child)
111
- return null
112
-
113
- if (
114
- typeof child === 'boolean'
115
- || typeof child === 'number'
116
- || typeof child === 'string'
117
- ) {
118
- return child
119
- }
120
-
121
- const {
122
- prefixCls,
123
- accordion,
124
- collapsible,
125
- onItemClick,
126
- activeKey,
127
- openMotion,
128
- expandIcon,
129
- classNames: collapseClassNames,
130
- styles,
131
- destroyOnHidden,
132
- } = props
133
-
134
- const key = child.key || String(index)
135
-
136
- const {
137
- header,
138
- headerClass,
139
- collapsible: childCollapsible,
140
- onItemClick: childOnItemClick,
141
- destroyOnHidden: childDestroyOnHidden,
142
- } = child.props || {}
143
-
144
- let isActive = false
145
- if (accordion) {
146
- isActive = activeKey[0] === key
147
- }
148
- else {
149
- isActive = activeKey.includes(key as Key)
150
- }
151
-
152
- const mergeCollapsible = childCollapsible ?? collapsible
153
-
154
- const handleItemClick = (value: Key) => {
155
- if (mergeCollapsible === 'disabled')
156
- return
157
- onItemClick?.(value)
158
- childOnItemClick?.(value)
159
- }
160
-
161
- const childProps = {
162
- key,
163
- panelKey: key,
164
- header,
165
- headerClass,
166
- classNames: collapseClassNames,
167
- styles,
168
- isActive,
169
- prefixCls,
170
- destroyOnHidden: childDestroyOnHidden ?? destroyOnHidden,
171
- openMotion,
172
- accordion,
173
- children: (child.children as ChildrenSlots)?.default?.(),
174
- onItemClick: handleItemClick,
175
- expandIcon,
176
- collapsible: mergeCollapsible,
177
- }
178
-
179
- Object.keys(childProps).forEach((propName) => {
180
- if (
181
- typeof childProps[propName as keyof typeof childProps] === 'undefined'
182
- ) {
183
- delete childProps[propName as keyof typeof childProps]
184
- }
185
- })
186
-
187
- return cloneElement(child, childProps)
188
- }
189
-
190
- export function useItems(
191
- items?: ItemType[],
192
- children?: () => VNode | VNodeNormalizedChildren,
193
- props?: Props,
194
- ) {
195
- if (Array.isArray(items)) {
196
- return convertItemsToNodes(items, props!)
197
- }
198
-
199
- return toArray(children?.()).map((target, index) =>
200
- getNewChild(target, index, props!),
201
- )
202
- }
package/src/index.ts DELETED
@@ -1,8 +0,0 @@
1
- import Collapse from './Collapse'
2
- import Panel from './Panel'
3
-
4
- export type { CollapsePanelProps, CollapseProps, ItemType } from './interface'
5
-
6
- export default Collapse
7
-
8
- export { Panel }
package/src/interface.ts DELETED
@@ -1,73 +0,0 @@
1
- import type { Key, VueNode } from '@v-c/util/dist/type'
2
- import type {
3
- CSSProperties,
4
- Ref,
5
- TransitionProps,
6
- } from 'vue'
7
-
8
- export type SemanticName = 'header' | 'title' | 'body' | 'icon'
9
-
10
- export interface CollapsePanelProps {
11
- id?: string
12
- header?: VueNode
13
- prefixCls?: string
14
- headerClass?: string
15
- showArrow?: boolean
16
- class?: string
17
- classNames?: Partial<Record<SemanticName, string>>
18
- style?: object
19
- styles?: Partial<Record<SemanticName, CSSProperties>>
20
- isActive?: boolean
21
- openMotion?: TransitionProps
22
- destroyOnHidden?: boolean
23
- accordion?: boolean
24
- forceRender?: boolean
25
- extra?: VueNode
26
- onItemClick?: (panelKey: Key) => void
27
- expandIcon?: (props: object) => any
28
- panelKey?: Key
29
- role?: string
30
- collapsible?: CollapsibleType
31
- children?: VueNode | string
32
- }
33
-
34
- export type CollapsibleType = 'header' | 'icon' | 'disabled'
35
-
36
- export interface ItemType
37
- extends Omit<
38
- CollapsePanelProps,
39
- | 'header' // alias of label
40
- | 'prefixCls'
41
- | 'panelKey' // alias of key
42
- | 'isActive'
43
- | 'accordion'
44
- | 'openMotion'
45
- | 'expandIcon'
46
- > {
47
- key?: CollapsePanelProps['panelKey']
48
- label?: CollapsePanelProps['header']
49
- ref?: Ref<HTMLDivElement>
50
- }
51
-
52
- export interface CollapseProps {
53
- prefixCls?: string
54
- activeKey?: Key | Key[]
55
- defaultActiveKey?: Key | Key[]
56
- openMotion?: TransitionProps
57
- onChange?: (key: Key[]) => void
58
- accordion?: boolean
59
- destroyOnHidden?: boolean
60
- expandIcon?: (props: object) => any
61
- collapsible?: CollapsibleType
62
- /**
63
- * Collapse items content
64
- * @since 3.6.0
65
- */
66
- items?: ItemType[]
67
- classNames?: Partial<Record<SemanticName, string>>
68
- styles?: Partial<Record<SemanticName, CSSProperties>>
69
- }
70
-
71
- export type {
72
- Key,
73
- }
@@ -1,3 +0,0 @@
1
- // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
-
3
- exports[`collapse > props items > should work with nested 1`] = ``;