From 6f7b4e5a85bd599ceda315c964f90bb87c3e7332 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: Sun, 26 Jan 2025 20:35:52 +0500
Subject: [PATCH] fix: some errors and edit 'Table'

---
 src/Playground.vue                            | 150 ++----------------
 src/common/interfaces/componentsProp.ts       |   5 -
 src/common/interfaces/componentsProps.ts      |  13 +-
 src/components/Drawer/Drawer.stories.ts       |   2 +-
 src/components/Drawer/Drawer.vue              |   2 +-
 src/components/Knob/Knob.vue                  |   2 +-
 src/components/Modal/Modal.stories.ts         |   2 +-
 src/components/Modal/Modal.vue                |   2 +-
 src/components/Select/Select.vue              |   2 +-
 src/components/Table/Table.stories.ts         | 136 +++++-----------
 src/components/Table/Table.vue                | 105 ++++++------
 src/components/Table/components/TableCell.vue | 103 ++++++++++++
 .../Table/{ => components}/TableHeader.vue    |  27 +++-
 src/components/Table/helpers.ts               |  18 +--
 14 files changed, 264 insertions(+), 305 deletions(-)
 create mode 100644 src/components/Table/components/TableCell.vue
 rename src/components/Table/{ => components}/TableHeader.vue (88%)

diff --git a/src/Playground.vue b/src/Playground.vue
index c15d980..6098fa5 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 4bb98d8..c01e4bb 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 ab0f0a3..2ca28bb 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 87b4ece..77902ce 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 7051f70..b08cf31 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 6d0ac1d..80861ad 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 3e1bef5..e06e80f 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 940c990..8b7597a 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 d916605..8a64e1a 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 6513ad2..ccc54af 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 af74f93..8f69d34 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 0000000..1c89050
--- /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 4ef4267..7873848 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 56cf7e6..10c9e93 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]));
   }
 };
 
-- 
GitLab