Skip to content

Commit

Permalink
feat: update collapse item (#356)
Browse files Browse the repository at this point in the history
* feat: add collapse item some flexibility

* chore: eslint fix

* chore: eslint fix

* feat: another approach

* chore: prettier fix

* fix: move 'div' into computed func

* fix: move 'div' into computed func

* fix: comment fix in if statement

Co-authored-by: ViZhe <vizhe.cat@ya.ru>

* fix: no need in optional params

* fix: props types

* fix: props types

* fix: props types

* feat: storybook story + small fixes

* feat: style update 'cause of div

* feat: docs page update

* fix: define async icon comp

* fix: script type module

* fix: iframe height

* fix: docs page

* fix: docs page

---------

Co-authored-by: ViZhe <vizhe.cat@ya.ru>
  • Loading branch information
widetrace and ViZhe authored Jun 29, 2023
1 parent e3f8107 commit 101a36d
Show file tree
Hide file tree
Showing 16 changed files with 521 additions and 11 deletions.
18 changes: 17 additions & 1 deletion src/qComponents/QCollapse/src/QCollapse.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ export default defineComponent({
accordion: {
type: Boolean,
default: false
},
/**
* control your own close icon
*/
closeIcon: {
type: [Object, String] as PropType<QCollapseProps['closeIcon']>,
default: null
},
/**
* control your own open icon
*/
openIcon: {
type: [Object, String] as PropType<QCollapseProps['openIcon']>,
default: null
}
},
Expand Down Expand Up @@ -76,7 +90,9 @@ export default defineComponent({
provide<QCollapseProvider>('qCollapse', {
uniqueId,
activeNames,
updateValue
updateValue,
openIcon: props.openIcon,
closeIcon: props.closeIcon
});
}
});
Expand Down
6 changes: 5 additions & 1 deletion src/qComponents/QCollapse/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Ref } from 'vue';
import type { Component, Ref } from 'vue';

import type { Nullable } from '#/helpers';

Expand All @@ -9,12 +9,16 @@ export type QCollapsePropModelValue = Nullable<
export interface QCollapseProps {
modelValue: QCollapsePropModelValue;
accordion: Nullable<boolean>;
openIcon: Nullable<Component>;
closeIcon: Nullable<Component>;
}

export interface QCollapseProvider {
uniqueId: (prefix?: string) => string;
activeNames: Ref<(string | number)[]>;
updateValue: (name: string | number) => void;
openIcon: Nullable<Component | string>;
closeIcon: Nullable<Component | string>;
}

export type QCollapseInstance = void;
29 changes: 24 additions & 5 deletions src/qComponents/QCollapseItem/src/QCollapseItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
<slot name="title">
<div class="q-collapse-item__title">{{ title }}</div>
</slot>
<div
class="q-collapse-item__icon"
:class="icon"
<component
:is="customIcon"
:class="collapseIconClass"
/>
</button>

Expand All @@ -33,11 +33,13 @@
</template>

<script lang="ts">
import { defineComponent, inject, computed } from 'vue';
import { defineComponent, inject, computed, type Component } from 'vue';
import { randId } from '@/qComponents/helpers';
import type { QCollapseProvider } from '@/qComponents/QCollapse';
import type { ClassValue } from '#/helpers';
import QCollapseTransition from './QCollapseTransition.vue';
import type { QCollapseItemProps, QCollapseItemInstance } from './types';
Expand Down Expand Up @@ -65,22 +67,39 @@ export default defineComponent({
() =>
props.name ?? qCollapse?.uniqueId('default-collapse-name-') ?? randId()
);
const isActive = computed<boolean>(
() =>
qCollapse?.activeNames?.value.includes(preparedName.value ?? '') ??
false
);
const customIcon = computed<Component | string>(() => {
if (!qCollapse?.openIcon || !qCollapse?.closeIcon) return 'div';
return isActive.value ? qCollapse.closeIcon : qCollapse.openIcon;
});
const isCustomIcon = computed<boolean>(() => customIcon.value !== 'div');
const icon = computed<'q-icon-minus' | 'q-icon-plus'>(() =>
isActive.value ? 'q-icon-minus' : 'q-icon-plus'
);
const collapseIconClass = computed<ClassValue>(() => ({
'q-collapse-item__icon': !isCustomIcon.value,
'q-collapse-item__icon-custom': isCustomIcon.value,
[icon.value]: !isCustomIcon.value
}));
const handleHeaderClick = (): void => {
if (preparedName.value) qCollapse?.updateValue(preparedName.value);
};
return {
isActive,
icon,
customIcon,
collapseIconClass,
handleHeaderClick
};
}
Expand Down
6 changes: 6 additions & 0 deletions src/qComponents/QCollapseItem/src/q-collapse-item.scss
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@
color: var(--color-primary-blue);
}

&__icon-custom {
flex: 0 24px;
margin: 20px 16px 20px 24px;
color: var(--color-primary-blue);
}

&__body {
margin-top: 1px;
background-color: var(--color-tertiary-gray-light);
Expand Down
7 changes: 4 additions & 3 deletions src/qComponents/QCollapseItem/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ComputedRef } from 'vue';
import type { Component, ComputedRef } from 'vue';

import type { Nullable } from '#/helpers';
import type { Nullable, ClassValue } from '#/helpers';

export interface QCollapseItemProps {
title: Nullable<string>;
Expand All @@ -9,7 +9,8 @@ export interface QCollapseItemProps {

export interface QCollapseItemInstance {
isActive: ComputedRef<boolean>;
icon: ComputedRef<'q-icon-minus' | 'q-icon-plus'>;
customIcon: ComputedRef<Component | string>;
collapseIconClass: ComputedRef<ClassValue>;
handleHeaderClick: () => void;
}

Expand Down
104 changes: 104 additions & 0 deletions stories/components/QCollapse/CustomIcon.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import type { Meta, Story } from '@storybook/vue3';
import { defineAsyncComponent, defineComponent, ref } from 'vue';

import { QCollapse, type QCollapseProps } from '@/qComponents/QCollapse';
import { QCollapseItem } from '@/qComponents/QCollapseItem';

type QCollapseStoryWithExtraControl = QCollapseProps & { customIcon: boolean };

const storyMetadata: Meta = {
title: 'Components/QCollapse',
component: QCollapse,
subcomponents: { QCollapseItem },
argTypes: {
customIcon: {
control: { type: 'boolean' },
description:
'Control display of the custom icon (demonstration purpose only)'
},
openIcon: { control: { type: 'none' } },
closeIcon: { control: { type: 'none' } }
}
};

const QCollapseStory: Story<QCollapseStoryWithExtraControl> = args =>
defineComponent({
components: { QCollapse, QCollapseItem },
setup() {
const activeNames = ref<string[]>(['1']);
const iconUp = defineAsyncComponent(
() => import('./icons/ChevronUp.vue')
);
const iconDown = defineAsyncComponent(
() => import('./icons/ChevronDown.vue')
);

return { args, activeNames, iconUp, iconDown };
},
template: `
<q-collapse
v-model="activeNames"
:close-icon="args.customIcon ? iconDown : null"
:open-icon="args.customIcon ? iconUp : null"
:accordion="args.accordion"
style="max-width:732px"
>
<q-collapse-item
title="Consistency"
name="1"
>
<div>
Consistent with real life: in line with the process and logic of real
life, and comply with languages and habits that the users are used to;
</div>
<div>
Consistent within interface: all elements should be consistent, such as:
design style, icons and texts, position of elements, etc.
</div>
</q-collapse-item>
<q-collapse-item
title="Controlled consequences: users should be granted the freedom to operate, including canceling, aborting or reflect current state by updat"
>
<div>
Operation feedback: enable the users to clearly perceive their
operations by style updates and interactive effects;
</div>
<div>
Visual feedback: reflect current state by updating or rearranging
elements of the page.
</div>
</q-collapse-item>
<q-collapse-item title="Efficiency">
<div>
Simplify the process: keep operating process simple and intuitive;
</div>
<div>
Definite and clear: enunciate your intentions clearly so that the users
can quickly understand and make decisions;
</div>
<div>
Easy to identify: the interface should be straightforward, which helps
the users to identify and frees them from memorizing and recalling.
</div>
</q-collapse-item>
<q-collapse-item
title="Controllability"
name="4"
>
<div>
Decision making: giving advices about operations is acceptable, but do
not make decisions for the users;
</div>
<div>
Controlled consequences: users should be granted the freedom to operate,
including canceling, aborting or terminating current operation.
</div>
</q-collapse-item>
</q-collapse>
`
});

export const CustomIcon = QCollapseStory.bind({
customIcon: false
});
export default storyMetadata;
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ const storyMetadata: Meta = {
component: QCollapse,
subcomponents: { QCollapseItem },
argTypes: {
modelValue: { control: { type: 'none' } }
modelValue: { control: { type: 'none' } },
openIcon: { control: { type: 'none' } },
closeIcon: { control: { type: 'none' } }
}
};

Expand Down
22 changes: 22 additions & 0 deletions stories/components/QCollapse/icons/ChevronDown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.0002 15.713L18.0102 9.70299L16.5972 8.28799L12.0002 12.888L7.40423 8.28799L5.99023 9.70199L12.0002 15.713Z"
fill="currentColor"
/>
</svg>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'IconChevronDown'
});
</script>
22 changes: 22 additions & 0 deletions stories/components/QCollapse/icons/ChevronUp.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.0002 8.288L5.99023 14.298L7.40423 15.713L12.0042 11.113L16.6042 15.713L18.0112 14.298L12.0002 8.288Z"
fill="currentColor"
/>
</svg>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'IconChevronUp'
});
</script>
Loading

0 comments on commit 101a36d

Please sign in to comment.