From b35d2956762fc66a80b94e1d0b74b369ead199b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=9C=D0=B0?= =?UTF-8?q?=D0=BB=D1=8E=D0=B3=D0=B8=D0=BD?= <d.malygin@iqdev.digital> Date: Fri, 27 Dec 2024 07:05:29 +0500 Subject: [PATCH] feat: Table in process --- src/App.vue | 9 +++ src/common/constants/icons.ts | 8 ++ src/common/interfaces/componentsProp.ts | 5 +- src/common/interfaces/componentsProps.ts | 1 + .../components/Button/Button.stories.ts | 2 +- .../components/Divider/Divider.stories.ts | 2 +- .../components/Drawer/Drawer.stories.ts | 2 +- .../components/MenuDial/MenuDial.stories.ts | 2 +- src/stories/components/Modal/Modal.stories.ts | 2 +- src/stories/components/Popup/Popup.stories.ts | 2 +- .../SelectButton/SelectButton.stories.ts | 2 +- .../components/Slider/Slider.stories.ts | 2 +- src/stories/components/Table/Table.stories.ts | 13 +++- src/stories/components/Table/Table.vue | 75 +++++++++++++++++-- .../ToggleSwitch/ToggleSwitch.stories.ts | 2 +- .../components/TreeList/TreeList.stories.ts | 2 +- src/stories/icons/Mono/SortDownIcon.vue | 30 ++++++++ src/stories/icons/Mono/SortHorizontalIcon.vue | 34 +++++++++ src/stories/icons/Mono/SortUpIcon.vue | 30 ++++++++ src/stories/icons/Mono/SortVerticalIcon.vue | 34 +++++++++ 20 files changed, 239 insertions(+), 20 deletions(-) create mode 100644 src/stories/icons/Mono/SortDownIcon.vue create mode 100644 src/stories/icons/Mono/SortHorizontalIcon.vue create mode 100644 src/stories/icons/Mono/SortUpIcon.vue create mode 100644 src/stories/icons/Mono/SortVerticalIcon.vue diff --git a/src/App.vue b/src/App.vue index f312152..12b543a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -98,6 +98,10 @@ import PlusIcon from '@stories/icons/Mono/PlusIcon.vue'; import PointerIcon from '@stories/icons/Mono/PointerIcon.vue'; import SaveIcon from '@stories/icons/Mono/SaveIcon.vue'; import SettingsIcon from '@stories/icons/Mono/SettingsIcon.vue'; +import SortHorizontalIcon from '@stories/icons/Mono/SortHorizontalIcon.vue'; +import SortDownIcon from '@stories/icons/Mono/SortDownIcon.vue'; +import SortUpIcon from '@stories/icons/Mono/SortUpIcon.vue'; +import SortVerticalIcon from '@stories/icons/Mono/SortVerticalIcon.vue'; import LineIcon from '@stories/icons/Mono/LineIcon.vue'; import TableIcon from '@stories/icons/Mono/TableIcon.vue'; import TrashIcon from '@stories/icons/Mono/TrashIcon.vue'; @@ -213,6 +217,10 @@ const gentleIcons = { PointerIcon, SaveIcon, SettingsIcon, + SortDownIcon, + SortHorizontalIcon, + SortUpIcon, + SortVerticalIcon, TableIcon, TrashIcon, TriangleIcon, @@ -420,6 +428,7 @@ const sliderValue = ref(1); display: grid; grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; gap: 10px; + margin-bottom: 20px; } .iconsItem { font-size: 14px; diff --git a/src/common/constants/icons.ts b/src/common/constants/icons.ts index 892a811..83bd219 100644 --- a/src/common/constants/icons.ts +++ b/src/common/constants/icons.ts @@ -103,6 +103,10 @@ import TableIcon from '@stories/icons/Mono/TableIcon.vue'; import TrashIcon from '@stories/icons/Mono/TrashIcon.vue'; import UserIcon from '@stories/icons/Mono/UserIcon.vue'; import TriangleIcon from '@stories/icons/Mono/TriangleIcon.vue'; +import SortHorizontalIcon from '@stories/icons/Mono/SortHorizontalIcon.vue'; +import SortDownIcon from '@stories/icons/Mono/SortDownIcon.vue'; +import SortUpIcon from '@stories/icons/Mono/SortUpIcon.vue'; +import SortVerticalIcon from '@stories/icons/Mono/SortVerticalIcon.vue'; export const iconsSet: Record<string, Component> = { Age18Icon: Age18Icon, @@ -204,6 +208,10 @@ export const iconsSet: Record<string, Component> = { PointerIcon: PointerIcon, SaveIcon: SaveIcon, SettingsIcon: SettingsIcon, + SortDownIcon, + SortHorizontalIcon, + SortUpIcon, + SortVerticalIcon, TableIcon: TableIcon, TrashIcon: TrashIcon, TriangleIcon: TriangleIcon, diff --git a/src/common/interfaces/componentsProp.ts b/src/common/interfaces/componentsProp.ts index 38144bc..7ada68d 100644 --- a/src/common/interfaces/componentsProp.ts +++ b/src/common/interfaces/componentsProp.ts @@ -3,7 +3,10 @@ import type { TDarkness, TIcons, TPosition, TTextStyle, TThemeColor } from '@int export interface ITableColumn { name: string; type?: TTableColumnType; - isFilter?: boolean; + editable?: boolean; + filterable?: boolean; + sortable?: boolean; + padding?: string; } export interface ITableItem { diff --git a/src/common/interfaces/componentsProps.ts b/src/common/interfaces/componentsProps.ts index b628fca..8f1d216 100644 --- a/src/common/interfaces/componentsProps.ts +++ b/src/common/interfaces/componentsProps.ts @@ -30,6 +30,7 @@ export interface ITableProps { fontSize?: string; showAllLines?: boolean; stripedRows?: boolean; + center?: boolean; } export interface ITLProps { diff --git a/src/stories/components/Button/Button.stories.ts b/src/stories/components/Button/Button.stories.ts index f14c35f..eaefe1f 100644 --- a/src/stories/components/Button/Button.stories.ts +++ b/src/stories/components/Button/Button.stories.ts @@ -72,7 +72,7 @@ export default meta; type Story = StoryObj<typeof meta>; -export const Primary: Story = { +export const Simple: Story = { args: {}, }; diff --git a/src/stories/components/Divider/Divider.stories.ts b/src/stories/components/Divider/Divider.stories.ts index a10ca58..aed2c56 100644 --- a/src/stories/components/Divider/Divider.stories.ts +++ b/src/stories/components/Divider/Divider.stories.ts @@ -49,7 +49,7 @@ type Story = StoryObj<typeof meta>; * See https://storybook.js.org/docs/api/csf * to learn how to use render functions. */ -export const Primary: Story = { +export const Simple: Story = { args: {}, }; diff --git a/src/stories/components/Drawer/Drawer.stories.ts b/src/stories/components/Drawer/Drawer.stories.ts index f4320b6..4ecd444 100644 --- a/src/stories/components/Drawer/Drawer.stories.ts +++ b/src/stories/components/Drawer/Drawer.stories.ts @@ -76,7 +76,7 @@ export default meta; type Story = StoryObj<typeof meta>; -export const Primary: Story = { +export const Simple: Story = { args: { visible: true, default: 'Какой-то текÑÑ‚.', diff --git a/src/stories/components/MenuDial/MenuDial.stories.ts b/src/stories/components/MenuDial/MenuDial.stories.ts index 89c4739..ba5314d 100644 --- a/src/stories/components/MenuDial/MenuDial.stories.ts +++ b/src/stories/components/MenuDial/MenuDial.stories.ts @@ -67,7 +67,7 @@ export default meta; type Story = StoryObj<typeof meta>; -export const Primary: Story = { +export const Simple: Story = { args: { items: [ { diff --git a/src/stories/components/Modal/Modal.stories.ts b/src/stories/components/Modal/Modal.stories.ts index 6ac70ab..d8e3e29 100644 --- a/src/stories/components/Modal/Modal.stories.ts +++ b/src/stories/components/Modal/Modal.stories.ts @@ -77,7 +77,7 @@ export default meta; type Story = StoryObj<typeof meta>; -export const Primary: Story = { +export const Simple: Story = { args: { visible: true, header: 'Modal', diff --git a/src/stories/components/Popup/Popup.stories.ts b/src/stories/components/Popup/Popup.stories.ts index e1e6313..29237c6 100644 --- a/src/stories/components/Popup/Popup.stories.ts +++ b/src/stories/components/Popup/Popup.stories.ts @@ -48,7 +48,7 @@ export default meta; type Story = StoryObj<typeof meta>; -export const Primary: Story = { +export const Simple: Story = { args: { default: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Amet fugiat harum maiores placeat\n soluta, vel velit voluptas. Accusamus aut, error et minima neque praesentium, ratione,\n reprehenderit repudiandae saepe ut vero! Lorem ipsum dolor sit amet, consectetur adipisicing\n elit. Amet fugiat harum maiores placeat soluta, vel velit voluptas. Accusamus aut, error et\n minima neque praesentium, ratione, reprehenderit repudiandae saepe ut vero!', diff --git a/src/stories/components/SelectButton/SelectButton.stories.ts b/src/stories/components/SelectButton/SelectButton.stories.ts index 4dc87e4..5fd64bd 100644 --- a/src/stories/components/SelectButton/SelectButton.stories.ts +++ b/src/stories/components/SelectButton/SelectButton.stories.ts @@ -94,7 +94,7 @@ export default meta; type Story = StoryObj<typeof meta>; -export const Primary: Story = { +export const Simple: Story = { args: { options: [ { diff --git a/src/stories/components/Slider/Slider.stories.ts b/src/stories/components/Slider/Slider.stories.ts index e8bfe31..24e4587 100644 --- a/src/stories/components/Slider/Slider.stories.ts +++ b/src/stories/components/Slider/Slider.stories.ts @@ -75,7 +75,7 @@ export default meta; type Story = StoryObj<typeof meta>; -export const Primary: Story = { +export const Simple: Story = { args: {}, }; diff --git a/src/stories/components/Table/Table.stories.ts b/src/stories/components/Table/Table.stories.ts index d5cd8f2..7d51e72 100644 --- a/src/stories/components/Table/Table.stories.ts +++ b/src/stories/components/Table/Table.stories.ts @@ -20,6 +20,7 @@ const meta: Meta = { gap: { control: 'text' }, showAllLines: { control: 'boolean' }, stripedRows: { control: 'boolean' }, + center: { control: 'boolean' }, darknessTheme: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] }, darknessTextColor: { control: 'select', @@ -73,7 +74,7 @@ export default meta; type Story = StoryObj<typeof meta>; -export const Primary: Story = { +export const Simple: Story = { args: { columns: [ { @@ -122,14 +123,19 @@ export const Full: Story = { { name: 'Name', type: 'text', + sortable: true, }, { name: 'Age', type: 'text', + filterable: true, }, { name: 'Hobbies', type: 'text', + padding: '30px', + filterable: true, + sortable: true, }, { name: 'Country', @@ -198,9 +204,12 @@ export const Full: Story = { fontSize: '20px', showAllLines: true, - gap: '70px', + gap: '20px', border: 'fuchsia', theme: 'black', stripedRows: true, + textColor: 'white', + darknessTextColor: '500', + center: true, }, }; diff --git a/src/stories/components/Table/Table.vue b/src/stories/components/Table/Table.vue index 06c38db..825cf7c 100644 --- a/src/stories/components/Table/Table.vue +++ b/src/stories/components/Table/Table.vue @@ -1,8 +1,12 @@ <script setup lang="ts"> import type { ITableProps } from '@interfaces/componentsProps'; -import { computed } from 'vue'; +import { computed, ref } from 'vue'; import { convertThemeToColor, convertThemeToSecondaryColor, convertThemeToTextColor } from '@helpers/common'; import type { ITableItem } from '@interfaces/componentsProp'; +import FilterIcon from '@stories/icons/Mono/FilterIcon.vue'; +import SortDownIcon from '@stories/icons/Mono/SortDownIcon.vue'; +import SortUpIcon from '@stories/icons/Mono/SortUpIcon.vue'; +import SortVerticalIcon from '@stories/icons/Mono/SortVerticalIcon.vue'; const props = withDefaults(defineProps<ITableProps>(), { gap: '5px', @@ -22,6 +26,22 @@ const color = computed(() => ); const secondaryColor = computed(() => convertThemeToSecondaryColor(props.theme, props.darknessTheme)); const darkCellColor = computed(() => convertThemeToSecondaryColor(props.theme, String(+props.darknessTheme + 300))); + +const sortState = ref<string[]>( + (() => { + const columns = props.columns; + const result = []; + for (const column of columns) { + result.push(column.sortable ? 'none' : ''); + } + return result; + })(), +); + +const changeColumnSortMode = (index: number) => { + const cur = sortState.value[index]; + sortState.value[index] = cur === 'none' ? 'down' : cur === 'down' ? 'up' : 'none'; +}; </script> <template> @@ -38,14 +58,49 @@ const darkCellColor = computed(() => convertThemeToSecondaryColor(props.theme, S :class="{ leftBorder: showAllLines, }" - v-for="column of columns" + v-for="(column, index) of columns" :key="column.name" class="columnHeader" style="padding: 5px 0 5px 5px" > - <div class="columnFlex"> - {{ column.name }} - <div></div> + <div + :style="`justify-content: ${center ? 'center' : 'start'}; padding: ${center ? `0px calc(${gap} / 2 + ${column.padding ?? '0px'} / 2)` : `0 ${column.padding ?? '0px'} 0 0`}`" + :class="[ + 'columnFlex', + { + columnGap: !center, + }, + ]" + > + <div class="columnHeader-container"> + <h3> + {{ column.name }} + </h3> + <button v-if="column.sortable" @click.prevent="changeColumnSortMode(index)"> + <SortVerticalIcon + v-show="sortState[index] === 'none'" + :color="textColor" + :size="isNaN(+fontSize.slice(0, -2)) ? fontSize.slice(0, -3) : fontSize.slice(0, -2)" + /> + <SortDownIcon + v-show="sortState[index] === 'down'" + :color="textColor" + :size="isNaN(+fontSize.slice(0, -2)) ? fontSize.slice(0, -3) : fontSize.slice(0, -2)" + /> + <SortUpIcon + v-show="sortState[index] === 'up'" + :color="textColor" + :size="isNaN(+fontSize.slice(0, -2)) ? fontSize.slice(0, -3) : fontSize.slice(0, -2)" + /> + </button> + <button v-if="column.filterable" @click.prevent=""> + <FilterIcon + :color="textColor" + :size="isNaN(+fontSize.slice(0, -2)) ? fontSize.slice(0, -3) : fontSize.slice(0, -2)" + /> + </button> + </div> + <div v-if="!center"></div> </div> </th> </tr> @@ -59,7 +114,7 @@ const darkCellColor = computed(() => convertThemeToSecondaryColor(props.theme, S }" v-for="item of row" :key="item.value" - style="padding: 5px" + :style="`padding: 5px; text-align: ${center ? 'center' : 'start'}`" > {{ item.value }} </td> @@ -90,9 +145,15 @@ tr::after { } .columnFlex { display: flex; - gap: v-bind(gap); font-weight: bold; } +.columnGap { + gap: v-bind(gap); +} +.columnHeader-container { + display: flex; + gap: 10px; +} .tableLines { border-top: 1px solid v-bind(secondaryColor); border-right: 1px solid v-bind(secondaryColor); diff --git a/src/stories/components/ToggleSwitch/ToggleSwitch.stories.ts b/src/stories/components/ToggleSwitch/ToggleSwitch.stories.ts index 362f38e..a17df1c 100644 --- a/src/stories/components/ToggleSwitch/ToggleSwitch.stories.ts +++ b/src/stories/components/ToggleSwitch/ToggleSwitch.stories.ts @@ -70,7 +70,7 @@ export default meta; type Story = StoryObj<typeof meta>; -export const Primary: Story = { +export const Simple: Story = { args: { active: false, }, diff --git a/src/stories/components/TreeList/TreeList.stories.ts b/src/stories/components/TreeList/TreeList.stories.ts index a1eb0b5..bc3e1d3 100644 --- a/src/stories/components/TreeList/TreeList.stories.ts +++ b/src/stories/components/TreeList/TreeList.stories.ts @@ -68,7 +68,7 @@ export default meta; type Story = StoryObj<typeof meta>; -export const Primary: Story = { +export const Simple: Story = { args: { items: [ { diff --git a/src/stories/icons/Mono/SortDownIcon.vue b/src/stories/icons/Mono/SortDownIcon.vue new file mode 100644 index 0000000..f23751b --- /dev/null +++ b/src/stories/icons/Mono/SortDownIcon.vue @@ -0,0 +1,30 @@ +<script setup lang="ts"> +interface Props { + color?: string; + size?: string | number; +} +defineProps<Props>(); +</script> + +<template> + <svg + :width="`${size ?? 40}px`" + :height="`${size ?? 40}px`" + viewBox="0 0 24 24" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M4 16L13 16" :stroke="color ?? '#000000'" stroke-width="1.5" stroke-linecap="round" /> + <path d="M6 11H13" :stroke="color ?? '#000000'" stroke-width="1.5" stroke-linecap="round" /> + <path d="M8 6L13 6" :stroke="color ?? '#000000'" stroke-width="1.5" stroke-linecap="round" /> + <path + d="M17 4L17 20L20 16" + :stroke="color ?? '#000000'" + stroke-width="1.5" + stroke-linecap="round" + stroke-linejoin="round" + /> + </svg> +</template> + +<style scoped></style> diff --git a/src/stories/icons/Mono/SortHorizontalIcon.vue b/src/stories/icons/Mono/SortHorizontalIcon.vue new file mode 100644 index 0000000..0d19cb5 --- /dev/null +++ b/src/stories/icons/Mono/SortHorizontalIcon.vue @@ -0,0 +1,34 @@ +<script setup lang="ts"> +interface Props { + color?: string; + size?: string | number; +} +defineProps<Props>(); +</script> + +<template> + <svg + :width="`${size ?? 40}px`" + :height="`${size ?? 40}px`" + viewBox="0 0 24 24" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M18 8L6 8M6 8L10.125 4M6 8L10.125 12" + :stroke="color ?? '#000000'" + stroke-width="1.5" + stroke-linecap="round" + stroke-linejoin="round" + /> + <path + d="M6 16L18 16M18 16L13.875 12M18 16L13.875 20" + :stroke="color ?? '#000000'" + stroke-width="1.5" + stroke-linecap="round" + stroke-linejoin="round" + /> + </svg> +</template> + +<style scoped></style> diff --git a/src/stories/icons/Mono/SortUpIcon.vue b/src/stories/icons/Mono/SortUpIcon.vue new file mode 100644 index 0000000..93e9e77 --- /dev/null +++ b/src/stories/icons/Mono/SortUpIcon.vue @@ -0,0 +1,30 @@ +<script setup lang="ts"> +interface Props { + color?: string; + size?: string | number; +} +defineProps<Props>(); +</script> + +<template> + <svg + :width="`${size ?? 40}px`" + :height="`${size ?? 40}px`" + viewBox="0 0 24 24" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M4 8H13" :stroke="color ?? '#000000'" stroke-width="1.5" stroke-linecap="round" /> + <path d="M6 13H13" :stroke="color ?? '#000000'" stroke-width="1.5" stroke-linecap="round" /> + <path d="M8 18H13" :stroke="color ?? '#000000'" stroke-width="1.5" stroke-linecap="round" /> + <path + d="M17 20V4L20 8" + :stroke="color ?? '#000000'" + stroke-width="1.5" + stroke-linecap="round" + stroke-linejoin="round" + /> + </svg> +</template> + +<style scoped></style> diff --git a/src/stories/icons/Mono/SortVerticalIcon.vue b/src/stories/icons/Mono/SortVerticalIcon.vue new file mode 100644 index 0000000..aaeb511 --- /dev/null +++ b/src/stories/icons/Mono/SortVerticalIcon.vue @@ -0,0 +1,34 @@ +<script setup lang="ts"> +interface Props { + color?: string; + size?: string | number; +} +defineProps<Props>(); +</script> + +<template> + <svg + :width="`${size ?? 40}px`" + :height="`${size ?? 40}px`" + viewBox="0 0 24 24" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M16 18L16 6M16 6L20 10.125M16 6L12 10.125" + :stroke="color ?? '#000000'" + stroke-width="1.5" + stroke-linecap="round" + stroke-linejoin="round" + /> + <path + d="M8 6L8 18M8 18L12 13.875M8 18L4 13.875" + :stroke="color ?? '#000000'" + stroke-width="1.5" + stroke-linecap="round" + stroke-linejoin="round" + /> + </svg> +</template> + +<style scoped></style> -- GitLab