diff --git a/src/Playground.vue b/src/Playground.vue index c15d980971ded46b12153164f659d4ef2725366e..6098fa53b48be6fb4218973c9ea4ef9e065b8024 100644 --- a/src/Playground.vue +++ b/src/Playground.vue @@ -20,6 +20,7 @@ import AtIcon from '@icons/Mono/AtIcon.vue'; import Knob from '@components/Knob/Knob.vue'; import Rating from '@components/Rating/Rating.vue'; import HomeIcon from '@icons/Mono/HomeIcon.vue'; +import ProgressBar from '@components/ProgressBar/ProgressBar.vue'; const visibleDrawer = ref(false); const sliderOptions: ISliderOptions[] = [ @@ -121,7 +122,7 @@ const tableColumns: ITableColumn[] = [ type: 'select', options: { options: [{ value: 'Married' }, { value: 'Oh no...(s)he is dead' }], - theme: 'sky', + theme: 'black', }, }, { @@ -140,138 +141,18 @@ const tableColumns: ITableColumn[] = [ size: 'small', }, }, + { + name: 'Strength', + type: 'knob', + options: {}, + }, ]; const tableData = ref([ - [ - { - value: 'Pete', - }, - { - value: '30', - }, - { - value: 'Chess', - }, - { - value: 'USA', - }, - { - value: false, - }, - { - value: 'Married', - }, - { - value: 0, - }, - { - value: 30, - }, - ], - [ - { - value: 'John', - }, - { - value: '7', - }, - { - value: 'Football', - }, - { - value: 'Canada', - }, - { - value: true, - }, - { - value: 'Married', - }, - { - value: 0, - }, - { - value: 30, - }, - ], - [ - { - value: 'Дима', - }, - { - value: '22', - }, - { - value: 'Frontend', - }, - { - value: 'Russia', - }, - { - value: false, - }, - { - value: 'Married', - }, - { - value: 0, - }, - { - value: 30, - }, - ], - [ - { - value: 'КÑюша', - }, - { - value: '32', - }, - { - value: 'Frontend', - }, - { - value: 'Russia', - }, - { - value: false, - }, - { - value: 'Married', - }, - { - value: 0, - }, - { - value: 30, - }, - ], - [ - { - value: 'КÑюша', - }, - { - value: '32', - }, - { - value: 'Backend', - }, - { - value: 'Russia', - }, - { - value: false, - }, - { - value: 'Married', - }, - { - value: 0, - }, - { - value: 30, - }, - ], + ['Pete', '30', 'Chess', 'USA', false, 'Married', 0, 30, 2], + ['John', '7', 'Football', 'Canada', true, 'Married', 0, 30, 2], + ['Дима', '22', 'Frontend', 'Russia', false, 'Married', 0, 30, 2], + ['КÑюша', '32', 'Frontend', 'Russia', false, 'Married', 0, 30, 2], + ['КÑюша', '32', 'Backend', 'Russia', false, 'Married', 0, 30, 2], ]); const activeCheckbox = ref(); const selectOptions = [ @@ -283,6 +164,7 @@ const selectOptions = [ }, ]; const knob = ref(0); +const pbValue = ref(0); </script> <template> @@ -309,6 +191,7 @@ const knob = ref(0); <Checkbox v-model="activeCheckbox" /> <Checkbox v-model="activeCheckbox" size="large" /> <Checkbox v-model="activeCheckbox" size="huge" /> + <ProgressBar v-model="pbValue" /> {{ tableData[1] }} <Table center @@ -316,10 +199,9 @@ const knob = ref(0); :columns="tableColumns" darknessTextColor="500" v-model="tableData" - size="large" - font-size="36px" theme="black" stripedRows + editable ></Table> <button class="testButton" @click="visibleDrawer = !visibleDrawer">Ðажми менÑ</button> <div class="hui" style="width: 500px; height: 500px; background-color: gray"></div> @@ -402,7 +284,7 @@ const knob = ref(0); </template> </SelectButton> <ToggleSwitch /> - <Drawer v-model:visible="visibleDrawer" theme="sky" :dismissible="false" closeIcon="CropIcon"> + <Drawer v-model:visible="visibleDrawer" theme="sky" :dismissible="false" closeIcon="Crop"> <template #header>Ðто - Drawer</template> <p> pizdwertyuki lokl,kmjhgfwewesrdty ukilo,kmjngeartyukikdhgfgjhklj.,kga Lorem ipsum dolor sit amet, consectetur diff --git a/src/common/interfaces/componentsProp.ts b/src/common/interfaces/componentsProp.ts index 4bb98d8f71eb5cb25339e6d8750a73916f5f2002..c01e4bb32fbb60ea87995ddb177ddde0604b07c1 100644 --- a/src/common/interfaces/componentsProp.ts +++ b/src/common/interfaces/componentsProp.ts @@ -18,11 +18,6 @@ export interface ITableColumn { padding?: string; } -export interface ITableItem { - value: string | number | boolean; - editable?: boolean; -} - export interface ITableColumnOptions extends ICheckboxProps, ISelectProps, diff --git a/src/common/interfaces/componentsProps.ts b/src/common/interfaces/componentsProps.ts index ab0f0a38ea284735e5a25d781b816494da80079a..2ca28bbe074ed87ed25fb5e5d25602bd9e8ad32e 100644 --- a/src/common/interfaces/componentsProps.ts +++ b/src/common/interfaces/componentsProps.ts @@ -18,12 +18,11 @@ import type { ISelectOption, ISliderOptions, ITableColumn, - ITableItem, ITreeItem, } from '@interfaces/componentsProp'; export interface ITableProps { - data?: ITableItem[][]; + data?: unknown[][]; columns: ITableColumn[]; multipleSort?: boolean; gap?: string; @@ -36,6 +35,12 @@ export interface ITableProps { textColor?: TThemeColor; darknessTheme?: TDarkness; darknessTextColor?: TDarkness; + editable?: boolean; + noEditingSettings?: { + columns: number[]; + rows: number[]; + cells: [number, number][]; + }; } export interface ITLProps { @@ -80,10 +85,12 @@ export interface IMDProps { } export interface IKnobProps { + value?: number; min?: number; max?: number; step?: number; size?: TSize; + width?: string; theme?: TThemeColor; colorGaps?: IColorGap[]; negativeTheme?: TThemeColor; @@ -217,6 +224,7 @@ export interface IProgressBarProps { inactiveTheme?: TThemeColor; darknessTheme?: TDarkness; darknessInactiveTheme?: TDarkness; + gradient?: string[]; showLabel?: boolean; labelBefore?: string; labelAfter?: string; @@ -224,6 +232,7 @@ export interface IProgressBarProps { } export interface IRatingProps { + value?: number; count?: number; size?: TSize; gap?: string; diff --git a/src/components/Drawer/Drawer.stories.ts b/src/components/Drawer/Drawer.stories.ts index 87b4ece322890fa98550a8d3884d614e457f056f..77902ce71d86e72e40423229730c5c296ee22ca0 100644 --- a/src/components/Drawer/Drawer.stories.ts +++ b/src/components/Drawer/Drawer.stories.ts @@ -88,7 +88,7 @@ export const BlackFull: Story = { visible: true, width: 200, theme: 'black', - closeIcon: 'CrossIcon', + closeIcon: 'Cross', header: 'Drawer', footer: 'The end.', headerDivider: true, diff --git a/src/components/Drawer/Drawer.vue b/src/components/Drawer/Drawer.vue index 7051f7023ffd1090cd99645bf555b6ec79f274e2..b08cf312bca5060a94682760d8ca98a698eb7c5b 100644 --- a/src/components/Drawer/Drawer.vue +++ b/src/components/Drawer/Drawer.vue @@ -12,7 +12,7 @@ const props = withDefaults(defineProps<IDrawerProps>(), { dismissible: true, theme: 'white', darknessTheme: '500', - closeIcon: 'CrossIcon', + closeIcon: 'Cross', headerDivider: false, footerDivider: false, }); diff --git a/src/components/Knob/Knob.vue b/src/components/Knob/Knob.vue index 6d0ac1d4b0236a69b9001532ce37bc042ba64fab..80861ad2779bbe7eed9a03f1876ecd4fd742006d 100644 --- a/src/components/Knob/Knob.vue +++ b/src/components/Knob/Knob.vue @@ -39,7 +39,7 @@ const degreesTotal = computed(() => 360 - 90); const length = computed(() => props.max - props.min); const center = computed(() => calcCenter(container.value)); const start = computed(() => calcStart(container.value)); -const containerSize = computed(() => calcContainerSize(props.size)); +const containerSize = computed(() => props.width ?? calcContainerSize(props.size)); const buttonSize = computed(() => { const size = props.size; return size === 'normal' || size === 'small' ? 'small' : size === 'large' ? 'large' : 'huge'; diff --git a/src/components/Modal/Modal.stories.ts b/src/components/Modal/Modal.stories.ts index 3e1bef5a5158f2f07c53792ef3ce69eba7818810..e06e80f4ebf1679e6174a821edadb73fbc3c3450 100644 --- a/src/components/Modal/Modal.stories.ts +++ b/src/components/Modal/Modal.stories.ts @@ -98,7 +98,7 @@ export const Full: Story = { theme: 'black', position: 'topRight', dismissible: true, - closeIcon: 'CodeIcon', + closeIcon: 'Code', headerDivider: true, }, }; diff --git a/src/components/Modal/Modal.vue b/src/components/Modal/Modal.vue index 940c99095477a2ed572e676a38030cbb536e0098..8b7597a8d8af42cbc595ac303792e1c29acdad7a 100644 --- a/src/components/Modal/Modal.vue +++ b/src/components/Modal/Modal.vue @@ -14,7 +14,7 @@ const props = withDefaults(defineProps<IModalProps>(), { width: '30%', height: '30%', headerDivider: false, - closeIcon: 'CrossIcon', + closeIcon: 'Cross', }); const body = document.querySelector('body')!; const emit = defineEmits(['onClose']); diff --git a/src/components/Select/Select.vue b/src/components/Select/Select.vue index d91660592bbad5c50ac4a8358c31067dac318fa3..8a64e1a1a433118957eb068cb26d0216a199d4ea 100644 --- a/src/components/Select/Select.vue +++ b/src/components/Select/Select.vue @@ -9,7 +9,7 @@ import SearchIcon from '@icons/Mono/SearchIcon.vue'; import { calcFontSize, calcPadding, getOptionsGroups } from '@components/Select/helpers'; const props = withDefaults(defineProps<ISelectProps>(), { - options: [{ value: 'One' }, { value: 'Two' }], + options: () => [{ value: 'One' }, { value: 'Two' }], size: 'normal', width: '200px', theme: 'white', diff --git a/src/components/Table/Table.stories.ts b/src/components/Table/Table.stories.ts index 6513ad2649b0f4b86336cc99efd4804014a6fa4f..ccc54afdf14a6cf80b76e0002f8e1e69752158c8 100644 --- a/src/components/Table/Table.stories.ts +++ b/src/components/Table/Table.stories.ts @@ -23,6 +23,8 @@ const meta: Meta = { stripedRows: { control: 'boolean' }, center: { control: 'boolean' }, multipleSort: { control: 'boolean' }, + editable: { control: 'boolean' }, + noEditingSettings: { control: 'object' }, darknessTheme: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] }, darknessTextColor: { control: 'select', @@ -125,14 +127,12 @@ export const Full: Story = { { name: 'Name', type: 'text', - sortable: true, - initSort: 'none', }, { name: 'Age', type: 'number', - sortable: true, filterable: true, + sortable: true, }, { name: 'Hobbies', @@ -140,107 +140,58 @@ export const Full: Story = { padding: '30px', filterable: true, sortable: true, - initSort: 'none', }, { name: 'Country', type: 'text', }, { - name: 'Is gay', + name: 'Is gay?', type: 'checkbox', - sortable: true, - initSort: 'up', }, - ], - - data: [ - [ - { - value: 'Pete', - }, - { - value: '30', - }, - { - value: 'Chess', - }, - { - value: 'USA', - }, - { - value: false, - }, - ], - [ - { - value: 'John', - }, - { - value: '7', - }, - { - value: 'Football', - }, - { - value: 'USA', - }, - { - value: true, - }, - ], - [ - { - value: 'Дима', - }, - { - value: '22', - }, - { - value: 'Frontend', - }, - { - value: 'Russia', - }, - { - value: false, - }, - ], - [ - { - value: 'КÑюша', - }, - { - value: '32', - }, - { - value: 'Frontend', - }, - { - value: 'Russia', - }, - { - value: false, - }, - ], - [ - { - value: 'КÑюша', - }, - { - value: '32', - }, - { - value: 'Backend', + { + name: 'Status', + type: 'select', + options: { + options: [{ value: 'Married' }, { value: 'Oh no...(s)he is dead' }], + theme: 'sky', }, - { - value: 'Russia', + }, + { + name: 'Children', + type: 'rating', + options: { + theme: 'yellow', }, - { - value: false, + }, + { + name: 'Job progress', + type: 'progressBar', + options: { + theme: 'red', + width: '150px', + size: 'small', }, - ], + }, + { + name: 'Strength', + type: 'knob', + options: {}, + }, ], + + data: [ + ['Pete', '30', 'Chess', 'USA', false, 'Married', 0, 30, 2], + ['John', '7', 'Football', 'Canada', true, 'Married', 0, 30, 2], + ['Дима', '22', 'Frontend', 'Russia', false, 'Married', 0, 30, 2], + ['КÑюша', '32', 'Frontend', 'Russia', false, 'Married', 0, 30, 2], + ['КÑюша', '32', 'Backend', 'Russia', false, 'Married', 0, 30, 2], + ], + noEditingSettings: { + columns: [5], + rows: [3], + cells: [[0, 6]], + }, fontSize: '24px', showAllLines: true, border: 'fuchsia', @@ -249,5 +200,6 @@ export const Full: Story = { darknessTextColor: '500', center: true, size: 'large', + editable: true, }, }; diff --git a/src/components/Table/Table.vue b/src/components/Table/Table.vue index af74f9355b6338b31d2c7311241e752e4c497416..8f69d34645e87e51c68a00c945164b14070866bb 100644 --- a/src/components/Table/Table.vue +++ b/src/components/Table/Table.vue @@ -1,29 +1,22 @@ <script setup lang="ts"> import type { ITableProps } from '@interfaces/componentsProps'; -import { computed, ref } from 'vue'; +import { computed, type Ref, ref } from 'vue'; import { convertThemeToColor, convertThemeToSecondaryColor, convertThemeToTextColor } from '@helpers/common'; -import type { ITableItem } from '@interfaces/componentsProp'; -import { - calcAdditionalHeight, - calcGap, - calcRows, - filterCheckboxProps, - filterSelectProps, -} from '@components/Table/helpers'; -import TableHeader from '@components/Table/TableHeader.vue'; -import Checkbox from '@components/Checkbox/Checkbox.vue'; -import Select from '@components/Select/Select.vue'; -import Rating from '@components/Rating/Rating.vue'; -import ProgressBar from '@components/ProgressBar/ProgressBar.vue'; +import { calcAdditionalHeight, calcGap, calcRows } from '@components/Table/helpers'; +import TableHeader from '@components/Table/components/TableHeader.vue'; +import TableCell from '@components/Table/components/TableCell.vue'; const props = withDefaults(defineProps<ITableProps>(), { size: 'normal', theme: 'white', darknessTheme: '500', fontSize: '16px', + editable: false, }); -const data = defineModel<ITableItem[][]>(); +const data = defineModel<unknown[][]>(); +const emit = defineEmits(['updateData']); +const table = ref(); const columns = ref(props.columns); const sortStateActive = ref<[number, string] | []>([]); const indexColumnToFilter = ref<number>(0); @@ -31,6 +24,9 @@ const isFilterPopup = ref<boolean>(false); const filterValue = ref<string>(''); const isRegisterSensitive = ref<boolean>(false); +if (props.data) { + data.value = props.data; +} if (props.columns) { columns.value = props.columns; } @@ -40,7 +36,6 @@ if (~columnToSortIndex) sortStateActive.value = [columnToSortIndex, props.column const initGap = computed(() => calcGap(props.gap ?? '5px', props.fontSize)); const additionalHeightFromSize = computed(() => calcAdditionalHeight(props.size, props.fontSize)); -const types = computed(() => props.columns.map((column) => column.type)); // ['', 'up', 'none', '', 'none', ...] const sortState = computed<string[]>(() => { const result = []; @@ -49,9 +44,9 @@ const sortState = computed<string[]>(() => { } return result; }); -const rows = computed<ITableItem[][]>(() => +const rows = computed<unknown[][]>(() => calcRows( - data.value ?? props.data, + data.value, sortStateActive.value, props.multipleSort, indexColumnToFilter.value, @@ -60,6 +55,7 @@ const rows = computed<ITableItem[][]>(() => isRegisterSensitive.value, ), ); +const types = computed(() => props.columns.map((column) => column.type)); const themeColor = computed(() => convertThemeToColor(props.theme, props.darknessTheme)); const color = computed(() => @@ -69,6 +65,11 @@ const color = computed(() => ); const secondaryColor = computed<string>(() => convertThemeToSecondaryColor(props.theme, props.darknessTheme)); const darkCellColor = computed(() => convertThemeToSecondaryColor(props.theme, String(+props.darknessTheme + 300))); +const knobWidth = computed(() => { + if (!isNaN(+props.fontSize[props.fontSize.length - 3])) + return +props.fontSize.slice(0, -2) * 2.5 + props.fontSize.slice(-2); + return +props.fontSize.slice(0, -3) * 2.5 + props.fontSize.slice(-3); +}); const changeColumnSortMode = (index: number) => { const cur = sortState.value[index]; @@ -95,6 +96,12 @@ const cancelFilter = () => { filterValue.value = ''; isFilterPopup.value = false; }; +const updateData = (newValue: Ref<unknown>, rowIndex: number, columnIndex: number) => { + if (data.value?.[rowIndex]?.[columnIndex] !== undefined) { + data.value[rowIndex][columnIndex] = newValue.value ?? newValue; + emit('updateData', data.value); + } +}; </script> <template> @@ -104,16 +111,19 @@ const cancelFilter = () => { tableLines: showAllLines, }" :style="`background-color: ${themeColor}; color: ${color}`" - id="table" + class="table" + ref="table" > <thead> <TableHeader v-model:filterValue="filterValue" v-model:isFilterPopup="isFilterPopup" v-model:isRegisterSensitive="isRegisterSensitive" + :table="table" :columns="columns" :sortState="sortState" :indexColumnToFilter="indexColumnToFilter" + :types="types" :initGap="initGap" :additionalHeightFromSize="additionalHeightFromSize" :theme="theme" @@ -129,40 +139,38 @@ const cancelFilter = () => { /> </thead> <tbody> - <tr v-for="(row, rowIndex) of rows" :key="rowIndex"> + <tr + v-for="(row, rowIndex) of rows" + :key="rowIndex" + :class="{ + noEdit: + !editable || + (noEditingSettings?.rows && noEditingSettings?.rows.find((i) => data?.[i]?.join('') === row.join(''))), + }" + > <td + v-for="(item, columnIndex) of row" + :key="columnIndex" :class="{ leftBorder: showAllLines, darkRow: stripedRows && rowIndex % 2, + noEdit: !editable || (noEditingSettings?.columns && ~noEditingSettings.columns?.indexOf(columnIndex)), }" - v-for="(item, columnIndex) of row" - :key="String(item.value)" :style="`padding: calc(${initGap} / 2 + ${additionalHeightFromSize}) ${initGap}`" > - <div :class="['cell', { cellCenter: center }]"> - <span v-if="~['text', 'number'].indexOf(types[columnIndex] ?? '')">{{ item.value }}</span> - <Checkbox - v-else-if="types[columnIndex] === 'checkbox'" - v-bind="filterCheckboxProps(columns[columnIndex].options)" - v-model="item.value as boolean" - /> - <Select - v-else-if="types[columnIndex] === 'select'" - v-bind="filterSelectProps(columns[columnIndex].options)" - v-model="item.value" - width="100px" - /> - <Rating - v-else-if="types[columnIndex] === 'rating'" - v-bind="columns[columnIndex].options" - v-model="item.value" - /> - <ProgressBar - v-else-if="types[columnIndex] === 'progressBar'" - v-bind="columns[columnIndex].options" - :value="item.value as number" - /> - </div> + <TableCell + :item="item" + :types="types" + :columns="columns" + :rowIndex="rowIndex" + :columnIndex="columnIndex" + :center="center" + :editable="editable" + :noEditingSettings="noEditingSettings?.cells" + :fontSize="fontSize" + :knobWidth="knobWidth" + @updateData="updateData" + /> </td> </tr> </tbody> @@ -171,7 +179,7 @@ const cancelFilter = () => { </template> <style scoped> -table { +.table { border-collapse: collapse; } table * { @@ -208,4 +216,7 @@ tr::after { .darkRow { background-color: v-bind(darkCellColor); } +.noEdit { + pointer-events: none; +} </style> diff --git a/src/components/Table/components/TableCell.vue b/src/components/Table/components/TableCell.vue new file mode 100644 index 0000000000000000000000000000000000000000..1c89050e5edd591bd37d57e60edf6320784929eb --- /dev/null +++ b/src/components/Table/components/TableCell.vue @@ -0,0 +1,103 @@ +<script setup lang="ts"> +import { filterCheckboxProps, filterSelectProps } from '@components/Table/helpers'; +import type { ITableColumn, TTableColumnType } from '@interfaces/componentsProp'; +import Checkbox from '@components/Checkbox/Checkbox.vue'; +import Select from '@components/Select/Select.vue'; +import Rating from '@components/Rating/Rating.vue'; +import ProgressBar from '@components/ProgressBar/ProgressBar.vue'; +import Knob from '@components/Knob/Knob.vue'; + +interface IProps { + item: unknown; + types: (TTableColumnType | undefined)[]; + columns: ITableColumn[]; + rowIndex: number; + columnIndex: number; + center: boolean | undefined; + editable: boolean; + noEditingSettings: [number, number][] | undefined; + fontSize: string; + knobWidth: string; +} +defineProps<IProps>(); +defineEmits(['updateData']); +</script> + +<template> + <div + :class="[ + 'cell', + { + cellCenter: center, + noEdit: + !editable || + (noEditingSettings && + noEditingSettings.find((i: [number, number]) => i[0] === rowIndex && i[1] === columnIndex)), + }, + ]" + > + <input + v-if="~['text', 'number'].indexOf(types[columnIndex] ?? '')" + :value="item" + @input="(event) => $emit('updateData', event.target, rowIndex, columnIndex)" + :id="`${rowIndex}-${columnIndex}`" + :type="types[columnIndex]" + :style="`display: inline; width: 100%; text-align: ${center ? 'center' : 'auto'}`" + /> + <Checkbox + v-else-if="types[columnIndex] === 'checkbox'" + v-bind="filterCheckboxProps(columns[columnIndex].options)" + :active="item as boolean" + @update="$emit('updateData', $event, rowIndex, columnIndex)" + /> + <Select + v-else-if="types[columnIndex] === 'select'" + v-bind="filterSelectProps(columns[columnIndex].options)" + width="100px" + :selected="item as string" + @update="$emit('updateData', $event, rowIndex, columnIndex)" + /> + <Rating + v-else-if="types[columnIndex] === 'rating'" + v-bind="columns[columnIndex].options" + :value="item as number" + @update="$emit('updateData', $event, rowIndex, columnIndex)" + /> + <ProgressBar + v-else-if="types[columnIndex] === 'progressBar'" + v-bind="columns[columnIndex].options" + :value="item as number" + @update="$emit('updateData', $event, rowIndex, columnIndex)" + /> + <Knob + v-else-if="types[columnIndex] === 'knob'" + v-bind="columns[columnIndex].options" + :value="item as number" + :width="knobWidth" + :fontSize="fontSize" + @update="$emit('updateData', $event, rowIndex, columnIndex)" + /> + </div> +</template> + +<style scoped> +input { + border: 1px solid transparent; + transition: border-color 0.2s ease; +} +input:hover { + border-color: white; + border-radius: 5px; +} +input:active { + border-radius: 5px; +} +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} +input[type='number'] { + -moz-appearance: textfield; +} +</style> diff --git a/src/components/Table/TableHeader.vue b/src/components/Table/components/TableHeader.vue similarity index 88% rename from src/components/Table/TableHeader.vue rename to src/components/Table/components/TableHeader.vue index 4ef4267d86d7839abe4dcb5f3953a14efac590b1..7873848bcc6f3b82548acb29ca0ce292ce77934d 100644 --- a/src/components/Table/TableHeader.vue +++ b/src/components/Table/components/TableHeader.vue @@ -9,13 +9,15 @@ import Button from '@components/Button/Button.vue'; import CheckMarkIcon from '@icons/Mono/CheckMarkIcon.vue'; import CrossIcon from '@icons/Mono/CrossIcon.vue'; import type { TThemeColor } from '@interfaces/common'; -import type { ITableColumn } from '@interfaces/componentsProp'; +import type { ITableColumn, TTableColumnType } from '@interfaces/componentsProp'; import { computed } from 'vue'; interface Props { + table: Element; columns: ITableColumn[]; sortState: string[]; indexColumnToFilter: number; + types: (TTableColumnType | undefined)[]; initGap: string; additionalHeightFromSize: string; theme: TThemeColor; @@ -40,9 +42,8 @@ const iconSize = computed(() => { const calcLeft = (selector: string) => { const el = document.querySelector(selector); - const table = document.querySelector('#table')!; if (!el) return 0; - return el.getBoundingClientRect().left - table.getBoundingClientRect().left + +iconSize.value; + return el.getBoundingClientRect().left - props.table.getBoundingClientRect().left + +iconSize.value; }; const isColumnTypeText = computed(() => props.columns[props.indexColumnToFilter].type === 'text'); </script> @@ -50,12 +51,16 @@ const isColumnTypeText = computed(() => props.columns[props.indexColumnToFilter] <template> <tr> <th - :class="{ - leftBorder: showAllLines, - }" v-for="(column, index) of columns" :key="column.name" - :style="`padding: calc(${initGap} / 2 + ${additionalHeightFromSize}) ${initGap}`" + :style="`position: relative; padding: calc(${initGap} / 2 + ${additionalHeightFromSize}) ${initGap}`" + :class="[ + `column`, + { + leftBorder: showAllLines, + textMinWidth: types[index] === 'text', + }, + ]" > <div :style="`justify-content: ${center ? 'center' : 'start'}; gap: ${center ? '0' : initGap}; padding: ${calcColumnPadding(column, center, initGap)}`" @@ -132,7 +137,12 @@ const isColumnTypeText = computed(() => props.columns[props.indexColumnToFilter] .columnHeader-container { display: flex; align-items: center; + padding: 5px 0; gap: 10px; + > h3 { + user-select: none; + white-space: nowrap; + } } .filterInput { width: 150px; @@ -148,4 +158,7 @@ const isColumnTypeText = computed(() => props.columns[props.indexColumnToFilter] .leftBorder { border-left: 1px solid v-bind(secondaryColor); } +.textMinWidth { + min-width: 230px; +} </style> diff --git a/src/components/Table/helpers.ts b/src/components/Table/helpers.ts index 56cf7e6bcdf066e705f3b416c01aa3e96ec56fa4..10c9e937d76a61ab7acb677e301cd68bd6e1305c 100644 --- a/src/components/Table/helpers.ts +++ b/src/components/Table/helpers.ts @@ -1,9 +1,9 @@ -import type { ITableColumn, ITableItem, TTableColumnType } from '@interfaces/componentsProp'; +import type { ITableColumn, TTableColumnType } from '@interfaces/componentsProp'; import type { TSize } from '@interfaces/common'; import type { ICheckboxProps, ISelectProps } from '@interfaces/componentsProps'; export const calcRows = ( - initRows: ITableItem[][] | undefined, + initRows: unknown[][] | undefined, sortStateActive: [number, string] | [], multipleSort: boolean, indexColumnToFilter: number, @@ -16,9 +16,7 @@ export const calcRows = ( let rows = [...initRows]; if (filterValue) { rows = rows.filter((row) => { - const item = isRegisterSensitive - ? row[indexColumnToFilter].value - : (row[indexColumnToFilter].value as string).toLowerCase(); + const item = isRegisterSensitive ? row[indexColumnToFilter] : (row[indexColumnToFilter] as string).toLowerCase(); return (item as string).startsWith(isRegisterSensitive ? filterValue : filterValue.toLowerCase()); }); } @@ -47,16 +45,12 @@ export const calcRows = ( const value = sortStateActive[1]; if (~['text', 'select'].indexOf(columnToSortType)) return rows.sort((a, b) => { - if (typeof a[index].value === 'string' && typeof b[index].value === 'string') - return value === 'down' - ? a[index].value.localeCompare(b[index].value) - : b[index].value.localeCompare(a[index].value); + if (typeof a[index] === 'string' && typeof b[index] === 'string') + return value === 'down' ? a[index].localeCompare(b[index]) : b[index].localeCompare(a[index]); return 0; }); // 'number', 'checkbox', 'rating', 'progressBar', 'knob' - return rows.sort((a, b) => - value === 'down' ? +a[index].value - +b[index].value : +b[index].value - +a[index].value, - ); + return rows.sort((a, b) => (value === 'down' ? +a[index] - +b[index] : +b[index] - +a[index])); } };