Search code examples
typescriptvue.jscheckbox

Vue.js checkbox component is not rendered if enable id by script


I have a Vue.js component called IstTable, that consists of a table made of column and rows. All rows have the first column corresponding to a checkbox, which is a component taken by IstCheckbox. I want to introduce to IstTable component the possibility to select multiple adjacent ist-checkbox by holding shift. I achieved the result, the problem is that if there are etherogeneus checkbox inside, it doesn't work. For example: enter image description here Let's suppose this situation: I want to enable all the checkboxes from the second to the fourth one, but the fourth is already checked. The second one is not checked, so when I click it is checked, then I hold shift and I click the fourth one, that becomes unchecked. The problem is that at the end it unchecks the last one, because I physically click on it. enter image description here I changed the value of that in the script (props.rows[3].enable = true), but it is not rendered: in fact if I click the fourth visually unchecked checkbox again, still unchecked (and it put the props.rows[3].enable as false). I have to click again manually to see the checkbox checked.

This is IstCheckbox.vue:

<template>
  <div class="relative flex items-start pointer-events-none">
    <div class="flex h-5 items-center pointer-events-none">
      <input
        :id="id"
        :checked="modelValue"
        :disabled="disabled"
        type="checkbox"
        class="h-4 w-4 rounded border-gray-300 focus:ring-0 pointer-events-auto"
        :class="{ 'text-main-dark': !disabled, 'text-gray-400': disabled }"
        @change="
          $emit(
            'update:modelValue',
            ($event.target as HTMLInputElement)?.checked
          )
        "
      />
    </div>
    <div class="ml-3 text-sm">
      <label
        :for="id"
        class="font-medium"
        :class="{ 'text-main-dark': !disabled, 'text-gray-400': disabled }"
      >
        {{ label }}
      </label>
      <span
        v-if="inline"
        class="ml-1"
        :class="{ 'text-gray-400': !disabled, 'text-gray-300': disabled }"
      >
        {{ description }}
      </span>
      <p
        v-else
        :class="{ 'text-gray-400': !disabled, 'text-gray-300': disabled }"
      >
        {{ description }}
      </p>
    </div>
  </div>
</template>

<script setup lang="ts">
// Require library @tailwindcss/forms

import { computed } from "vue";

const emit = defineEmits(["update:modelValue"]);

const props = defineProps({
  // needed for v-model
  modelValue: {
    type: Boolean,
    required: true,
  },
  id: {
    type: String,
    default: Math.random().toString(20).substr(2, 6),
  },
  label: {
    type: String,
    required: false,
    default: "",
  },
  description: {
    type: String,
    required: false,
    default: "",
  },
  disabled: {
    type: Boolean,
    defaut: false,
  },
  inline: {
    type: Boolean,
    default: false,
  },
});
</script>

This is IstTable.vue:

<!-- eslint-disable prettier/prettier -->
<template>
  <div v-if="!isLoading" class="overflow-x-auto">
    <table
      class="table-auto min-w-full w-full max-w-full mx-auto divide-y divide-gray-200 border"
    >
      <thead class="bg-gray-100">
        <tr>
          <th v-if="isBulkAction"></th>
          <th
            v-for="(column, index) in visibleColumns"
            :key="index"
            scope="col"
            class="px-6 py-3 text-left text-sm font-medium text-main-dark tracking-wider truncate dark:bg-gray-800 dark:text-main-light"
            :class="[
              column.isType(NumericColumn.TYPE) ? 'text-right' : '',
              preserveHeaderFormat ? '' : ' uppercase ',
              ,
              column.mobileHidden || column.mobileStacked
                ? 'hidden lg:table-cell'
                : '',
            ]"
            @click="sortTable(column.field)"
          >
            <div class="inline pr-3">{{ column.headerName }}</div>
            <IstIcon
              v-if="sortOrders[column.field] === 1"
              icon="fa-solid fa-arrow-down"
              class="inline"
            />
            <IstIcon
              v-if="sortOrders[column.field] === -1"
              icon="fa-solid fa-arrow-up"
              class="inline"
            />
          </th>
        </tr>
      </thead>
      <tbody class="divide-y divide-gray-200 bg-white">
        <tr
          v-for="(row, id) in sortedRows"
          :key="id"
          class="hover:cursor-pointer hover:bg-gray-100"
          :class="[id % 2 === 0 ? 'bg-white' : 'bg-gray-50']"
          @click="handleRowClicked(row)"
        >
          <td
            v-if="isBulkAction"
            class="px-3 py-3 text-sm text-main-dark align-middle md:px-5 md:py-4 pointer-events-none"
          >
            <ist-checkbox
              v-model="row.selected"
              @click.stop="toggleSelection(row)"
              @click="selectRow(row, $event)"
            ></ist-checkbox>
          </td>
          <td
            v-for="(item, index) in visibleColumns"
            :key="index"
            class="px-3 py-3 text-sm text-main-dark align-middle md:px-5 md:py-4"
            :class="[
            item.isType(NumericColumn.TYPE)
              ? 'text-right'
              : item.isType(IstComponentColumn.TYPE)
              ? (item as IstComponentColumn).tdClass
              : 'text-left',
              item.mobileHidden || item.mobileStacked ? 'hidden lg:table-cell' : '',
              item.isType(TextColumn.TYPE)?'truncate md:whitespace-nowrap':item.isType(DescriptionColumn.TYPE)?'whitespace-break-spaces':''
          ]"
          >
            <template v-if="item.isType(MultiComponentColumn.TYPE)">
              <!-- Loop through the istComponentNames array -->
              <component
                :is="getMultiComponent(componentName)"
                v-for="(componentName, componentIndex) in (item as MultiComponentColumn).istComponentNames"
                :key="componentIndex"
                v-bind="{ ...((item as MultiComponentColumn).propsRenderers[componentIndex](row[item.field])) }"
                @click="
                  handleCellClicked(
                    (item as MultiComponentColumn).propsRenderers[
                      componentIndex
                    ](row[item.field]),
                    row
                  )
                "
              ></component>
            </template>
            <component
              :is="getComponent(item)"
              v-else-if="item.isType(IstComponentColumn.TYPE)"
              v-bind="{ ...((item as IstComponentColumn).propsRenderer(row[item.field])) }"
            ></component>
            <component
              :is="getComponent(item)"
              v-else-if="item.isType(RenderedColumn.TYPE)"
            >
              {{ (item as RenderedColumn).callback(row[item.field]) }}
            </component>
            <div
              v-else-if="item.isType(HtmlRenderedColumn.TYPE)"
              v-html="(item as RenderedColumn).callback(row[item.field])"
            ></div>
            <span v-else-if="item.isType(AutoIncrementColumn.TYPE)">
              {{ id + 1 }}
            </span>
            <span v-else>
              {{ row[item.field] }}
              <dl
                v-if="item.stackChildren.length > 0"
                class="font-normal lg:hidden"
              >
                <dd
                  v-for="(stackChild, stackChildIndex) in getStackChildren(
                    item
                  )"
                  :key="stackChildIndex"
                  class="mt-1 text-helper-dark"
                  :class="getStackChildClass(stackChild)"
                >
                  {{ getStackChild(row, stackChild) }}
                </dd>
              </dl>
            </span>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script setup lang="ts">
import { defineAsyncComponent, onMounted, type PropType } from "vue";
import type { Column } from "./types/Column";
import { RenderedColumn } from "./types/RenderedColumn";
import { NumericColumn } from "./types/NumericColumn";
import { IstComponentColumn } from "./types/IstComponentColumn";
import { MultiComponentColumn } from "./types/MultiComponentColumn";
import { computed, ref } from "vue";
import IstIcon from "../../atoms/IstIcon/IstIcon.vue";
import { AsyncComponents } from "../../../asyncComponents";
import { AutoIncrementColumn } from "./types/AutoIncrementColumn";
import { TextColumn } from "./types/TextColumn";
import { HtmlRenderedColumn } from "./types/HtmlRenderedColumn";
import { DescriptionColumn } from "./types/DescriptionColumn";
import IstCheckbox from "../IstCheckbox/IstCheckbox.vue";

const isLoading = ref(true);
const emit = defineEmits([
  "rowClicked",
  "cellClicked",
  "update:modelValue",
  "rowSelected",
]);
const props = defineProps({
  columns: {
    type: Array<Column>,
    required: true,
  },
  rows: {
    type: Array as PropType<{ [key: string]: any }[]>,
    required: true,
  },
  preserveHeaderFormat: {
    type: Boolean,
    required: false,
    default: false,
  },
  isBulkAction: {
    type: Boolean,
    default: false,
  },
  modelValue: {
    type: Boolean,
    default: false,
  },
});

const selectedRows = ref([] as any);
const lastSelectedRowIndex = ref<number | null>(null);

onMounted(() => {
  if (props.isBulkAction) {
    props.rows.forEach((row: any) => {
      row.selected = false;
    });
  }
  isLoading.value = false;
});

const getComponent = (item: Column) => {
  return AsyncComponents.getAsyncComponent(
    (item as IstComponentColumn).istComponentName
  );
};
const getMultiComponent = (item: string) => {
  return AsyncComponents.getAsyncComponent(item);
};

function toggleSelection(row: { [key: string]: any }) {
  const rowIndex = selectedRows.value.findIndex((sr) => sr.id === row.id);

  if (rowIndex > -1) {
    selectedRows.value.splice(rowIndex, 1);
  } else {
    selectedRows.value.push(row);
  }
}

const visibleColumns = computed(() => {
  let mobileStackedColumns = new Array<string>();
  props.columns.forEach((c) => {
    if (c.stackChildren) {
      mobileStackedColumns = mobileStackedColumns.concat(c.stackChildren);
    }
  });
  props.columns.forEach((c) => {
    c.mobileStacked = mobileStackedColumns.indexOf(c.field) != -1;
  });
  return props.columns.filter((c) => !c.hidden);
});

const model = computed({
  get() {
    return props.modelValue;
  },
  set(modelValue) {
    emit("update:modelValue", modelValue);
  },
});

const handleRowClicked = (row: { [key: string]: any }) => {
  emit("rowClicked", row);
};

const handleCellClicked = (cell: any, row: any) => {
  emit("cellClicked", cell, row);
};

const selectRow = (row: { [key: string]: any }, event: MouseEvent) => {
  isLoading.value = true;
  console.log(props.rows);

  const rowIndex = props.rows.findIndex(r => r.id === row.id);

  // var firstSel = true;

  // if (lastSelectedRowIndex.value) {
  //   firstSel = props.rows[lastSelectedRowIndex.value].selected;
  // }
  
  if (event.shiftKey && lastSelectedRowIndex.value !== null) {
    // Select all rows between the last and current row, inclusive
    const start = Math.min(rowIndex, lastSelectedRowIndex.value);
    const end = Math.max(rowIndex, lastSelectedRowIndex.value);

    if(props.rows[lastSelectedRowIndex.value].selected) {
      const newSelection = props.rows.slice(start, end + 1).filter(r => !isSelected(r));
      selectedRows.value = [...selectedRows.value, ...newSelection].map(row => ({
        ...row,
        selected: true
      }));
      newSelection.forEach (newRow => {
        const index = selectedRows.value.findIndex(r => r.id === newRow.id);
        if (index !== -1) {
          // selectedRows.value.splice(index, 1);
          const rowIndexInProps = props.rows.findIndex(r => r.id === newRow.id);
          updateRowSelection(rowIndexInProps, true);
          props.rows[rowIndexInProps].selected = true;
        }
      });
    } else {
      
      const newDeSelection = props.rows.slice(start, end + 1);
    //   selectedRows.value = [...selectedRows.value, ...newSelection].map(row => ({
    //     ...row,
    //     selected: false
    // }));
      // const selectedIndex = selectedRows.value.findIndex(row =>
      //   newSelection.some(newRow => newRow.id === row.id)
      // );
      // if (selectedIndex !== -1) {
      //   selectedRows.value.splice(selectedIndex, 1);
      // }

      newDeSelection.forEach(newRow => {
        const index = selectedRows.value.findIndex(r => r.id === newRow.id);
        if (index !== -1) {
          selectedRows.value.splice(index, 1);
          const rowIndexInProps = props.rows.findIndex(r => r.id === newRow.id);
          updateRowSelection(rowIndexInProps, false);
          props.rows[rowIndexInProps].selected = false;
        }
      });

      console.log("uiuhuuiihiiiuiuiu",props.rows)
    }

    // Get the subset of rows between start and end
    // if (!row.selected) {
    //   const newSelection = props.rows.slice(start, end + 1).filter(r => !isSelected(r));
    //   // Add the new selection to the existing selected rows and update the selected property to true
    //   selectedRows.value = [...selectedRows.value, ...newSelection].map(row => ({
    //     ...row,
    //     selected: true
    // }));
    // }

    // Extract the id values from selectedRows
    const selectedIds = selectedRows.value.map(row => row.id);

    // Update the selected property of each row in props.rows
    // props.rows[lastSelectedRowIndex.value].selected = !props.rows[lastSelectedRowIndex.value].selected;
    props.rows.forEach((row: any, index: number) => {
      // if(index >start && index<end){
      if(index != lastSelectedRowIndex.value && index >start && index<end){
        if (selectedIds.includes(row.id) && lastSelectedRowIndex.value != null && props.rows[lastSelectedRowIndex.value].selected) {
          row.selected = true;
        } else if (selectedIds.includes(row.id)) {
          row.selected = false;
          // selectedRows.value.pop(index);
        }
      }

      // if (selectedIds.includes(row.id) && !row.selected) {
      //   row.selected = true;
      // } else if (row.selected && index >start && index<end && lastSelectedRowIndex.value) {
      //   // row.selected = false;
      //   row.selected = props.rows[lastSelectedRowIndex.value].selected;
      //   selectedRows.value.pop(index)
      // }
    });
    lastSelectedRowIndex.value = null;
  } else {
    const selectedIds = selectedRows.value.map(row => row.id);
    props.rows.forEach((row: any) => {
      if (!selectedIds.includes(row.id)) {
        row.selected = false;
      } else {
        row.selected = true;
      }
    });
    // Update the last selected row index
    lastSelectedRowIndex.value = rowIndex;
  }

  console.log("props prova",props.rows);
  console.log("props prova2", JSON.parse(JSON.stringify(props.rows)));
  emit("rowSelected", selectedRows.value);
  console.log(selectedRows.value);
  isLoading.value = false;
};

function isSelected(row: { [key: string]: any }) {
  return selectedRows.value.some((sr) => sr.id === row.id);
}

const sortKey = ref("");
const sortOrders = ref<{ [key: string]: number }>({});

const sortTable = (key: string) => {
  console.log(sortOrders.value[key]);
  if (sortKey.value === key) {
    sortOrders.value[key] = sortOrders.value[key] * -1;
  } else {
    sortKey.value = key;
    sortOrders.value = { [key]: 1 };
  }
};

const sortedRows = computed(() => {
  const rows = props.rows.slice();
  const sort = sortKey.value;
  if (sort) {
    const order = sortOrders.value[sortKey.value];
    const colIndex = indexOfByProp(visibleColumns.value, "field", sort);
    const sortColumn = visibleColumns.value[colIndex];
    rows.sort((a, b) => sortColumn.sortFunction(a[sort], b[sort]) * order);
  }
  return rows;
});

const getStackChildren = (column: Column) => {
  const mobileHiddenColumns = visibleColumns.value.filter(
    (c) => c.mobileHidden
  );
  return column.stackChildren.filter(
    (c) => indexOfByProp(mobileHiddenColumns, "field", c) == -1
  );
};
const getStackChild = (row: any, stackChild: string) => {
  const column = visibleColumns.value.find((c) => c.field == stackChild);
  try {
    return (
      column?.headerName +
      ": " +
      (column as RenderedColumn).callback(row[stackChild])
    );
  } catch (e) {
    return column?.headerName + ": " + row[stackChild];
  }
};

const getStackChildClass = (stackChildName: string) => {
  const column = visibleColumns.value.find((c) => c.field == stackChildName);
  return column?.isType(TextColumn.TYPE)
    ? "truncate"
    : column?.isType(DescriptionColumn.TYPE)
    ? "whitespace-break-spaces"
    : "";
};

const indexOfByProp = (
  array: Array<any>,
  prop: string,
  value: string | number
) => {
  return (array as Array<any>).map((e) => e[prop]).indexOf(value);
};

function updateRowSelection(index: number, value: boolean) {
  if (index < 0 || index >= props.rows.length) return;

  const newRow = {
    ...props.rows[index],
    selected: value,
  };

  // Creating a new array to trigger reactivity
  const newRows = [
    ...props.rows.slice(0, index),
    newRow,
    ...props.rows.slice(index + 1),
  ];

  emit("update:modelValue", newRows); // Assuming you want to emit the change
}

// function indexOfByProp(array: any[], prop: string, value: any): number {
//   for (let i = 0; i < array.length; i++) {
//     if (array[i][prop] == value) {
//       return i;
//     }
//   }
//   return -1;
// }

</script>

As you can see, I have the selectRow method with the event.Shiftkey, triggered when I hold shift. At the end of the method I have a strange behaviour: console.log("props prova",props.rows); console.log("props prova2", JSON.parse(JSON.stringify(props.rows))); the first one represents in console all, with the fourth element with enable = false, the second one with enable = true. But if I enable debug, so I let the code going slows, both are true. But the problem is still present: also if props.rows is updated, it is not rendered on view.


Solution

  • Fixed. The problem was about the physical click to the second checkbox clicked (the forth in the example). And this click is automatically triggered and change the status unchecking the checkbox. My idea is to save the status of the first checkbox clicked while I hold shift and check if that status is the same to the last checkbox clicked holding shift. If yes, then disable the second checkbox beacuse it is in the correct state.

    So I saved this state in lastIsSelected constant and I set to null in the case the second checkbox is clicked holding shift.

    Here my updated code on IstTable.vue:

    <!-- eslint-disable prettier/prettier -->
    <template>
      <div v-if="!isLoading" class="overflow-x-auto">
        <table
          class="table-auto min-w-full w-full max-w-full mx-auto divide-y divide-gray-200 border"
        >
          <thead class="bg-gray-100">
            <tr>
              <th v-if="isBulkAction"></th>
              <th
                v-for="(column, index) in visibleColumns"
                :key="index"
                scope="col"
                class="px-6 py-3 text-left text-sm font-medium text-main-dark tracking-wider truncate dark:bg-gray-800 dark:text-main-light"
                :class="[
                  column.isType(NumericColumn.TYPE) ? 'text-right' : '',
                  preserveHeaderFormat ? '' : ' uppercase ',
                  ,
                  column.mobileHidden || column.mobileStacked
                    ? 'hidden lg:table-cell'
                    : '',
                ]"
                @click="sortTable(column.field)"
              >
                <div class="inline pr-3">{{ column.headerName }}</div>
                <IstIcon
                  v-if="sortOrders[column.field] === 1"
                  icon="fa-solid fa-arrow-down"
                  class="inline"
                />
                <IstIcon
                  v-if="sortOrders[column.field] === -1"
                  icon="fa-solid fa-arrow-up"
                  class="inline"
                />
              </th>
            </tr>
          </thead>
          <tbody class="divide-y divide-gray-200 bg-white">
            <tr
              v-for="(row, id) in sortedRows"
              :key="id"
              class="hover:cursor-pointer hover:bg-gray-100"
              :class="[id % 2 === 0 ? 'bg-white' : 'bg-gray-50']"
              @click="handleRowClicked(row)"
            >
              <td
                v-if="isBulkAction"
                class="px-3 py-3 text-sm text-main-dark align-middle md:px-5 md:py-4 pointer-events-none"
              >
                <ist-checkbox
                  v-if="lastIsSelected == row.selected"
                  disable = "true"
                  v-model="row.selected"
                  @click.stop="toggleSelection(row)"
                  @click="selectRow(row, $event)"
                ></ist-checkbox>
                <ist-checkbox
                  v-else
                  v-model="row.selected"
                  @click.stop="toggleSelection(row)"
                  @click="selectRow(row, $event)"
                ></ist-checkbox>
              </td>
              <td
                v-for="(item, index) in visibleColumns"
                :key="index"
                class="px-3 py-3 text-sm text-main-dark align-middle md:px-5 md:py-4"
                :class="[
                item.isType(NumericColumn.TYPE)
                  ? 'text-right'
                  : item.isType(IstComponentColumn.TYPE)
                  ? (item as IstComponentColumn).tdClass
                  : 'text-left',
                  item.mobileHidden || item.mobileStacked ? 'hidden lg:table-cell' : '',
                  item.isType(TextColumn.TYPE)?'truncate md:whitespace-nowrap':item.isType(DescriptionColumn.TYPE)?'whitespace-break-spaces':''
              ]"
              >
                <template v-if="item.isType(MultiComponentColumn.TYPE)">
                  <!-- Loop through the istComponentNames array -->
                  <component
                    :is="getMultiComponent(componentName)"
                    v-for="(componentName, componentIndex) in (item as MultiComponentColumn).istComponentNames"
                    :key="componentIndex"
                    v-bind="{ ...((item as MultiComponentColumn).propsRenderers[componentIndex](row[item.field])) }"
                    @click="
                      handleCellClicked(
                        (item as MultiComponentColumn).propsRenderers[
                          componentIndex
                        ](row[item.field]),
                        row
                      )
                    "
                  ></component>
                </template>
                <component
                  :is="getComponent(item)"
                  v-else-if="item.isType(IstComponentColumn.TYPE)"
                  v-bind="{ ...((item as IstComponentColumn).propsRenderer(row[item.field])) }"
                ></component>
                <component
                  :is="getComponent(item)"
                  v-else-if="item.isType(RenderedColumn.TYPE)"
                >
                  {{ (item as RenderedColumn).callback(row[item.field]) }}
                </component>
                <div
                  v-else-if="item.isType(HtmlRenderedColumn.TYPE)"
                  v-html="(item as RenderedColumn).callback(row[item.field])"
                ></div>
                <span v-else-if="item.isType(AutoIncrementColumn.TYPE)">
                  {{ id + 1 }}
                </span>
                <span v-else>
                  {{ row[item.field] }}
                  <dl
                    v-if="item.stackChildren.length > 0"
                    class="font-normal lg:hidden"
                  >
                    <dd
                      v-for="(stackChild, stackChildIndex) in getStackChildren(
                        item
                      )"
                      :key="stackChildIndex"
                      class="mt-1 text-helper-dark"
                      :class="getStackChildClass(stackChild)"
                    >
                      {{ getStackChild(row, stackChild) }}
                    </dd>
                  </dl>
                </span>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </template>
    
    <script setup lang="ts">
    import { defineAsyncComponent, onMounted, type PropType } from "vue";
    import type { Column } from "./types/Column";
    import { RenderedColumn } from "./types/RenderedColumn";
    import { NumericColumn } from "./types/NumericColumn";
    import { IstComponentColumn } from "./types/IstComponentColumn";
    import { MultiComponentColumn } from "./types/MultiComponentColumn";
    import { computed, ref } from "vue";
    import IstIcon from "../../atoms/IstIcon/IstIcon.vue";
    import { AsyncComponents } from "../../../asyncComponents";
    import { AutoIncrementColumn } from "./types/AutoIncrementColumn";
    import { TextColumn } from "./types/TextColumn";
    import { HtmlRenderedColumn } from "./types/HtmlRenderedColumn";
    import { DescriptionColumn } from "./types/DescriptionColumn";
    import IstCheckbox from "../IstCheckbox/IstCheckbox.vue";
    
    const isLoading = ref(true);
    const emit = defineEmits([
      "rowClicked",
      "cellClicked",
      "update:modelValue",
      "rowSelected",
    ]);
    const props = defineProps({
      columns: {
        type: Array<Column>,
        required: true,
      },
      rows: {
        type: Array as PropType<{ [key: string]: any }[]>,
        required: true,
      },
      preserveHeaderFormat: {
        type: Boolean,
        required: false,
        default: false,
      },
      isBulkAction: {
        type: Boolean,
        default: false,
      },
      modelValue: {
        type: Boolean,
        default: false,
      },
    });
    
    const selectedRows = ref([] as any);
    const lastSelectedRowIndex = ref<number | null>(null);
    const lastIsSelected = ref<boolean | null>(null);
    
    
    onMounted(() => {
      if (props.isBulkAction) {
        props.rows.forEach((row: any) => {
          row.selected = false;
        });
      }
      isLoading.value = false;
    });
    
    const getComponent = (item: Column) => {
      return AsyncComponents.getAsyncComponent(
        (item as IstComponentColumn).istComponentName
      );
    };
    const getMultiComponent = (item: string) => {
      return AsyncComponents.getAsyncComponent(item);
    };
    
    function toggleSelection(row: { [key: string]: any }) {
      const rowIndex = selectedRows.value.findIndex((sr) => sr.id === row.id);
    
      if (rowIndex > -1) {
        selectedRows.value.splice(rowIndex, 1);
      } else {
        selectedRows.value.push(row);
      }
    }
    
    const visibleColumns = computed(() => {
      let mobileStackedColumns = new Array<string>();
      props.columns.forEach((c) => {
        if (c.stackChildren) {
          mobileStackedColumns = mobileStackedColumns.concat(c.stackChildren);
        }
      });
      props.columns.forEach((c) => {
        c.mobileStacked = mobileStackedColumns.indexOf(c.field) != -1;
      });
      return props.columns.filter((c) => !c.hidden);
    });
    
    const model = computed({
      get() {
        return props.modelValue;
      },
      set(modelValue) {
        emit("update:modelValue", modelValue);
      },
    });
    
    const handleRowClicked = (row: { [key: string]: any }) => {
      emit("rowClicked", row);
    };
    
    const handleCellClicked = (cell: any, row: any) => {
      emit("cellClicked", cell, row);
    };
    
    const selectRow = (row: { [key: string]: any }, event: MouseEvent) => {
      isLoading.value = true;
      console.log(props.rows);
    
      const rowIndex = props.rows.findIndex(r => r.id === row.id);
      
      if (event.shiftKey && lastSelectedRowIndex.value !== null) {
        // Select all rows between the last and current row, inclusive
        const start = Math.min(rowIndex, lastSelectedRowIndex.value);
        const end = Math.max(rowIndex, lastSelectedRowIndex.value);
    
        if(props.rows[lastSelectedRowIndex.value].selected) {
          const newSelection = props.rows.slice(start, end + 1).filter(r => !isSelected(r));
          selectedRows.value = [...selectedRows.value, ...newSelection].map(row => ({
            ...row,
            selected: true
          }));
          newSelection.forEach (newRow => {
            const index = selectedRows.value.findIndex(r => r.id === newRow.id);
            if (index !== -1) {
              // selectedRows.value.splice(index, 1);
              const rowIndexInProps = props.rows.findIndex(r => r.id === newRow.id);
              updateRowSelection(rowIndexInProps, true);
              props.rows[rowIndexInProps].selected = true;
            }
          });
        } else {
          
          const newDeSelection = props.rows.slice(start, end + 1);
    
          newDeSelection.forEach(newRow => {
            const index = selectedRows.value.findIndex(r => r.id === newRow.id);
            if (index !== -1) {
              selectedRows.value.splice(index, 1);
              const rowIndexInProps = props.rows.findIndex(r => r.id === newRow.id);
              updateRowSelection(rowIndexInProps, false);
              props.rows[rowIndexInProps].selected = false;
            }
          });
    
          console.log("uiuhuuiihiiiuiuiu",props.rows)
        }
    
    
    
        // Extract the id values from selectedRows
        const selectedIds = selectedRows.value.map(row => row.id);
    
        // Update the selected property of each row in props.rows
        props.rows.forEach((row: any, index: number) => {
          // if(index >start && index<end){
          if(index != lastSelectedRowIndex.value && index >start && index<end){
            if (selectedIds.includes(row.id) && lastSelectedRowIndex.value != null && props.rows[lastSelectedRowIndex.value].selected) {
              row.selected = true;
            } else if (selectedIds.includes(row.id)) {
              row.selected = false;
            }
          }
    
        });
        lastSelectedRowIndex.value = null;
        lastIsSelected.value = null;
      } else {
        const selectedIds = selectedRows.value.map(row => row.id);
        props.rows.forEach((row: any) => {
          if (!selectedIds.includes(row.id)) {
            row.selected = false;
          } else {
            row.selected = true;
          }
        });
        // Update the last selected row index
        lastSelectedRowIndex.value = rowIndex;
        lastIsSelected.value = props.rows[rowIndex].selected;
      }
    
      console.log("props prova",props.rows);
      console.log("props prova2", JSON.parse(JSON.stringify(props.rows)));
      emit("rowSelected", selectedRows.value);
      console.log(selectedRows.value);
      isLoading.value = false;
    };
    
    function isSelected(row: { [key: string]: any }) {
      return selectedRows.value.some((sr) => sr.id === row.id);
    }
    
    const sortKey = ref("");
    const sortOrders = ref<{ [key: string]: number }>({});
    
    const sortTable = (key: string) => {
      console.log(sortOrders.value[key]);
      if (sortKey.value === key) {
        sortOrders.value[key] = sortOrders.value[key] * -1;
      } else {
        sortKey.value = key;
        sortOrders.value = { [key]: 1 };
      }
    };
    
    const sortedRows = computed(() => {
      const rows = props.rows.slice();
      const sort = sortKey.value;
      if (sort) {
        const order = sortOrders.value[sortKey.value];
        const colIndex = indexOfByProp(visibleColumns.value, "field", sort);
        const sortColumn = visibleColumns.value[colIndex];
        rows.sort((a, b) => sortColumn.sortFunction(a[sort], b[sort]) * order);
      }
      return rows;
    });
    
    const getStackChildren = (column: Column) => {
      const mobileHiddenColumns = visibleColumns.value.filter(
        (c) => c.mobileHidden
      );
      return column.stackChildren.filter(
        (c) => indexOfByProp(mobileHiddenColumns, "field", c) == -1
      );
    };
    const getStackChild = (row: any, stackChild: string) => {
      const column = visibleColumns.value.find((c) => c.field == stackChild);
      try {
        return (
          column?.headerName +
          ": " +
          (column as RenderedColumn).callback(row[stackChild])
        );
      } catch (e) {
        return column?.headerName + ": " + row[stackChild];
      }
    };
    
    const getStackChildClass = (stackChildName: string) => {
      const column = visibleColumns.value.find((c) => c.field == stackChildName);
      return column?.isType(TextColumn.TYPE)
        ? "truncate"
        : column?.isType(DescriptionColumn.TYPE)
        ? "whitespace-break-spaces"
        : "";
    };
    
    const indexOfByProp = (
      array: Array<any>,
      prop: string,
      value: string | number
    ) => {
      return (array as Array<any>).map((e) => e[prop]).indexOf(value);
    };
    
    function updateRowSelection(index: number, value: boolean) {
      if (index < 0 || index >= props.rows.length) return;
    
      const newRow = {
        ...props.rows[index],
        selected: value,
      };
    
      // Creating a new array to trigger reactivity
      const newRows = [
        ...props.rows.slice(0, index),
        newRow,
        ...props.rows.slice(index + 1),
      ];
    
      emit("update:modelValue", newRows); // Assuming you want to emit the change
    }
    
    </script>