# Componente

# Introducción

Este componente renderizará una tabla con los datos provistos. La misma se compone de dos partes, una de renderizado y otra de logica ir-data-table-bone, esta ultima será la encargada de el manejo de los datos así sean ingresados por propiedades o por consulta a la capa de datos.

# Uso

El componente espera recibir un arreglo de items con la siguiente estructura, para el renderizado, desde la capa de datos.

[
  {
    "id": 1,
    "name": "John Doe",
    "age": 32,
    "email": "jhondoe@mail.com"
  },
  {
    "id": 2,
    "name": "Jane Doe",
    "age": 25,
    "email": "janedoe@mail.com"
  }
]

⚠️

Importante:

Si se necesita que una celda esté vacía, no debe enviarse la propiedad correspondiente del item.

Ejemplo:

[
  {
    nombre: 'ExampleName1'
  },
  {
    nombre: 'ExampleName2',
    edad: 30
  }
]

En este caso, la celda del primer item correspondiente a la columna edad, estará vacía.

# Ejemplo

# Propiedades

Propiedad Tipo Default Requerido Descripción
headers Array undefined false Arreglo de headers. Si no se utiliza esta propiedad, los headers se generán según los keys de los items. Los keys de los items que no coincidan con alguno de los headers no se mostrarán en la tabla. Tendrá el siguiente formato:
[
  {
    "text": "NOMBRE", // Texto que se va a mostrar
    "value": "name", // Key de los items al que hace referencia
    "sortable": true // Determina si la columna tiene o no la función de ordenamiento
    "align": "end", // Alineación del texto del header
    "groupable": true, // Determina si se muestra o no la opción group en le header para agrupar.
    "cellClass": "blue", // Clases ded vuetify aplicadas a las celdas pertenecientes a este header
    "filterable": false, // Determina si este header se tiene en cuenta a la hora de filtrar con la propiedad "search"
    "class": "red", // Clase aplicada a este header de la tabla
    "divider": true, // Agrega un divisor en esta columna
    "sortable": true, // Determina si se muestra la opción de ordenar por este header
    "filter-component": { // Filtros por columna, documentados en dicho apartado
      "type": "IrTextField",
      "props": {
        "label": "Filtrar por nombre",
        "value": ""
      }
    },
  },
  {
    "text": "EDAD",
    "value": "age",
    "sortable": true
  },
  {
    "text": "EMAIL",
    "value": "email",
    "sortable": false
  }
]
items Array [] false Arreglo de items.
items_fcn String undefined false Nombre de la función a ejecutarse en la DB, la respuesta se utilizará para reemplazar el arreglo de items.
args_items_fcn Object [] false Parámetros de la función que se ejecuta con el nombre proveniente de la propiedad "items_fcn" (ésta propiedad queda inutilizada si no se envía "items_fcn"). *IMPORTANTE* la función descripta en la propiedad "items_fcn" va a recibir los prámetros que incluya esta propiedad y además un parámetro extra "filtros" que va a tener como valor un array hecho string con los valores de los campos del formulario de filtro. En caso de no poseer filtros, el parámetro se envía como un string vacío.
filtros Array [] false Arreglo de campos del formulario de filtro (igual formato que la propiedad "campo" del componente "Formulario"). *IMPORTANTE* Los filtros no se mostrarán si la propiedad "items_fcn" no está setteada. Se explica más detalladamente aquí.
filtros_fcn String undefined false Nombre de la función a ejecutarse en la DB, la respuesta se utilizará para reemplazar el arreglo de filtros.
args_filtros_fcn Object {} false Parámetros de la función que se ejecuta con el nombre proveniente de la propiedad "filtros_fcn" (ésta propiedad queda inutilizada si no se envía "filtros_fcn")
show-filters-text String 'Show filters' false Determina el texto que se mostrará en el tooltip del botón para mostrar los filtros.
apply-filters-text String 'Apply filters' false Determina el texto que se mostrará en el botón para aplicar filtros.
hide-filters-text String 'Hide filters' false Determina el texto que se mostrará en el tooltip del botón para ocultar los filtros.
is-filter-visible-on-load Boolean false false Determina si los filtros se muestran al renderizarse la tabla.
comportamientos Array [] false Se explica aquí su estructura.
rowsPerPage Number 5 false Determina la cantidad de filas por página que tendrá la tabla inicialmente (puede cambiarse desde los botones de navegación de la misma tabla). En caso de valer -1 se muestran todas las filas.
downloadable Boolean false false Determina la aparición del botón de descarga en la tabla.
show_select Boolean false false Muestra un checkbox las filas y en el header de la tabla, que permiten seleccionar las filas que serán afectadas por los comportamiento de tipo:

fcn_executer modal navigator.

Si la tabla no contiene al menos un comportamiento de los nombrados anteriormente, entonces esta propiedad se ignora y las filas no podrán ser seleccionadas.

Puede ver detalles del funcionamiento de comportamientos aquí.

Si los items no tienen una propiedad id y la propiedad de tabla item-key no está setteada (o no coincide con alguna propiedad de los items) entonces la selección de filas no estará disponible.

cache_items Boolean true false

Activa un cache de los items de la tabla, el cual permitirá que al navegar por el sitio se almacenen en el equipo del usuario los items consultados por items_fcn, para mostrar temporalmente mientras se consulta la información actualizada a la DB. Esto dará una mejor performance a la tabla, ya que mostrará contenido mas rápido.

Al desactivarse la tabla no mostrará items hasta obtener la información de la DB, solo mostrará un loading.

expandedDataProp String undefined false

Determina la propiedad que se va a utilizar para mostrar en el espacio expandible debajo de la fila.

Esta propiedad se ignora si show-expand no se envía o está en false.

noExpandedData String ' ' false

Texto que aparece en caso de que la propiedad detallada en expandedDataProp no exista para un item determinado.

Esta propiedad se ignora si show-expand no se envía o está en false.

headersCSV Array undefined false

Arreglo con los valores de la propiedad value de cada header que será visible en el CSV. Ejemplo:

// en el CSV se verán solamente las columnas "name" y "age"
headersCSV: ['name', 'age']

headers: [
  {
    "text": "NOMBRE",
    "value": "name" // el valor "name" se agregó al arreglo de headersCSV para mostrarse
  },
  {
    "text": "EDAD",
    "value": "age" // el valor "age" se agregó al arreglo de headersCSV para mostrarse
  },
  {
    "text": "EMAIL",
    "value": "email"
  }
]

download-text String

Download

false

Determina el texto que se mostrará dentro del botón de descarga y en el tooltip del mismo. Esta propiedad está deprecada. Se reemplaza por download-attrs.

download-attrs Object

{ color: 'fourth', text: '', icon: 'mdi-tray-arrow-down' }

false

Determina el color, el texto y el ícono del botón de descarga.

filters-attrs Object

{ color: 'fourth', text: '', icon: 'mdi-filter' }

false

Determina el color, el texto y el ícono del botón de "mostrar filtros".

name_csv String undefined false

Nombre del archivo descargado cuando se presione el botón de descargar csv en la tabla (funciona solo con la propiedad headersCSV en valor true). Si no se envía se usará tabla_descargada_ concatenado con la fecha de descarga del archivo. Ejemplo: tabla_descargada_2022_8_3.csv

item-key String

id

false

Nombre de la propiedad que va a utilizarse como identificador de cada item.

row_classes Object

undefined

false

Propiedad con estructura de objeto:

  {
    "conditional": "(name === 'Frozen Yogurt' || name length > 10) && (calories > 100)", // Condicionalidad determinada para la aplicación de clases
    "true_classes": "blue white--text", // clases que se aplicarán en caso de que se cumpla la condicionalidad
    "false_classes": "white red--text" // clases que se aplicarán en caso de que NO se cumpla la condicionalidad
  },

Aplica estilos personalizados a una fila que cumpla con determinadas condiciones definibles en la misma propiedad.

Podrá encontrar mas información de como utilizar esta propiedad en la sección clases de filas.

search String | Object

undefined

false Propiedad que al estar valorizada mostrará un campo de búsqueda encima de la tabla para poder filtrar las filas de la misma, sin hacer consultas a la base de datos. El valor de la propiedad puede ser un string que aparecerá como label del campo de búsqueda o también puede ser un objeto, con el cual se definirán los atributos del TextField que se muestra en el campo de búsqueda.

Si no está valorizada el campo no se mostrará.

refreshConfig Object

{ repeatTime: 0, timeout: 0, refreshingToolTip: 'Refreshing table' }

false

Determina el tiempo de refresco de la tabla. repeatTime determina cada cuanto tiempo se ejecuta items_fcn. timeout determina cuanto tiempo se estará refrescando la tabla. En caso de timeout ser -1, se refrescará infinitamente. refreshingToolTip determina el texto que se mostrará en un tooltip.

isNewDesign Boolean

false

false

Determina si la tabla se renderizará con otro diseño.

header-color String

$vuetify.theme.themes.light.primary

false

Determina el color del header de la tabla (cuando la propiedad isNewDesign esté en true). En caso de no enviarse un color, toma el color primary determinado en Vuetify.

# Ejemplo con show-expand

Código fuente

<template>
  <ir-data-table-bone
    v-bind="{ ...attrs }"
    v-model="selectedRows"
    :column-filters-values="columnFiltersValues"
    @cerrar-dialog="cerrarDialog"
    @upload-refreshing-time="updateRefreshing"
    @set-show-column-filters="setShowColumnFilters"
  >
    <template
      #default="{
        headers,
        items: itemsBone,
        rowsPerPage,
        manageGroupalHeader,
        filtros,
        submitForm,
        expandedDataProp,
        noExpandedData,
        buttonsTableOutside,
        buttonsTableInside,
        showSelect,
        generateCSV,
        downloadable,
        id_resolver_dlg,
        id_resolver,
        loading,
        isThereImageTypes,
        handleRowClick,
        handleTableClick,
        availableTableTypes,
        availableRowTypes,
        image_row,
        checkType,
        nameCSV,
        continueAction,
        dialogAttrs,
        row_classes,
        groupColor,
        search,
        refreshParent,
        repeatTime,
        refreshingToolTip,
        columnFiltersTooltip,
        sortSelected,
        headerOver,
        sortDesc,
        selectedHeadersValues,
        mouseOver,
        toggleSelectHeaderSort,
        indexSelected,
        setNewPage,
        currentPage,
        getPages,
        selectAllItems,
        toggleItemSelection,
        getCheckboxValue,
        multiSort,
        comportamientosRowGroups,
        checkComportamientosGrouped,
        columnFiltersIcon,
        columnFiltersColor,
        columnFiltersLabel,
        isLoadingComportamiento
      }"
    >
      <ir-dialog
        v-model="showDialog"
        v-bind="dialogAttrs"
        :json_resolver="{
          id_resolver: id_resolver_dlg,
          args_components_fcn: { filtros: filtros }
        }"
        @confirm="continueAction"
        @refresh-parent="refreshParent"
      />
      <IrImagePreviewer
        v-if="isThereImageTypes"
        v-model="imagenSeleccionada"
      />
      <style>
        .v-data-table.{{ getGroupClassName(groupColor) }} th button span.groupBtn {
        color: {{ groupColor && groupColor !== '' ? groupColor : '#FF0000' }} !important;
        }
        .v-data-table.{{ getGroupClassName(groupColor) }} th span:last-child {
        color: {{ groupColor && groupColor !== '' ? groupColor : '#FF0000' }} !important;
        }
        .v-data-table.{{ getGroupClassName(groupColor) }} th.sortable span:nth-child(2) {
        color: inherit !important;
        }
        .v-data-table.{{ getGroupClassName(groupColor) }} th.sortable span.v-data-table-header__sort-badge {
        color: inherit !important;
        }
        .v-data-table.{{ getGroupClassName(groupColor) }} th span:first-child {
        color: inherit !important;
        }
      </style>
      <div>
        <v-overlay
          :value="isLoadingComportamiento"
          absolute
          z-index="99"
        >
          <v-progress-circular
            size="50"
            indeterminate
          />
        </v-overlay>
        <v-data-table
          ref="irDataTableDOM"
          v-model="selectedRows"
          :item-class="row_classes"
          :hide-default-header="isNewDesign || hideDefaultHeader"
          :hide-default-footer="isNewDesign || hideDefaultFooter"
          :items-per-page="rowsPerPage"
          :headers="manageGroupalHeader"
          :items="itemsBone"
          :show-select="showSelect"
          :search="searchValue"
          v-bind="getFilteredProps"
          :page="currentPage"
          :group-by="groupedBy"
          :sort-desc="sortDesc"
          :sort-by="selectedHeadersValues"
          :class="resolveTableClasses(groupColor)"
          style="margin-top: 20px"
          :loading="isLoading(loading, otherProps.loading)"
        >
          <template
            v-if="isNewDesign"
            #header
          >
            <th
              :style="resolveHeaderStyle"
              class="d-none d-sm-table-row"
            >
              <td
                v-if="showSelect"
                style="width: 80px !important"
                class="px-sm-4"
              >
                <!-- Estos dos ir-checkbox se hacen porque la propiedad on-icon no es reactiva, intenté hacer una condicional en la prop pero no toma el cambio -->
                <ir-checkbox
                  v-show="selectedRows.length === itemsBone.length"
                  v-model="allSelected"
                  is-new-design
                  dense
                  dark
                  height="5px"
                  @change="(value) => selectAllItems(value, itemsBone)"
                />
                <ir-checkbox
                  v-show="selectedRows.length !== itemsBone.length"
                  v-model="allSelected"
                  is-new-design
                  dense
                  dark
                  on-icon="mdi-minus"
                  height="5px"
                  @change="(value) => selectAllItems(value, itemsBone)"
                />
              </td>
              <template v-for="(header, i) in headers">
                <td
                  :key="'tr-' + header.value + i + '-header'"
                  class="py-0 px-4"
                  @mouseenter="(event) => mouseOver(event, i)"
                  @mouseleave="(event) => mouseOver(event, i)"
                >
                  <div
                    class="d-flex align-center"
                    :class="
                      header.align
                        ? `ir-data-table-header-cell-${header.align}`
                        : ''
                    "
                  >
                    <p class="white--text ma-0 header-font">
                      {{
                        header.text.charAt(0).toUpperCase() +
                        header.text.slice(1).toLowerCase()
                      }}
                    </p>
                    <v-icon
                      v-if="header.sortable"
                      :height="5"
                      color="white"
                      :style="
                        resolveStyle(headerOver, indexSelected, i, sortSelected)
                      "
                      @click="toggleSelectHeaderSort(i, header)"
                    >
                      mdi-arrow-down-thin
                    </v-icon>
                    <span
                      v-if="indexSelected(i) !== -1 && multiSort"
                      class="white--text sort-index-header"
                    >
                      {{ indexSelected(i) + 1 }}
                    </span>
                    <button
                      v-if="header.groupable"
                      :class="getGroupClassName(groupColor)"
                      @click="groupBy(header)"
                    >
                      <span class="groupBtn">Group</span>
                    </button>
                  </div>
                </td>
              </template>
              <td v-if="comportamientosRowGroups.length > 0" />
            </th>
            <tr :class="progressLinearLoadingClasses(loading)">
              <td :colspan="getColSpan(manageGroupalHeader, showSelect)">
                <v-progress-linear indeterminate />
              </td>
            </tr>
          </template>
          <template #top>
            <ir-form
              v-if="filtros.length !== 0 && isFiltros"
              :campos="filtros"
              data-cy="form-filtro"
              flat
              style="margin-bottom: 40px"
            >
              <template #footer>
                <v-btn
                  dark
                  data-cy="aplicar-filtro"
                  v-on="submitForm"
                >
                  {{ applyFiltersText }}
                </v-btn>
              </template>
            </ir-form>
            <div class="d-flex justify-end top__container">
              <!-- En la siguiente linea se intenta quitar los paréntesis pero rompe al hacerlo, no puedo desactivar el warning en el template -->
              <v-text-field
                v-if="!isUndefined(search)"
                v-model="searchValue"
                v-bind="searchAtrrs(search)"
                class="searchField px-4 py-3"
              />
              <template v-if="isNewDesign">
                <template v-for="(button, i) in buttonsTableOutside">
                  <v-tooltip
                    v-if="
                      typeof button.visible !== 'undefined'
                        ? button.visible
                        : true
                    "
                    :key="i"
                    bottom
                    :disabled="!button.tooltip_text"
                  >
                    <template #activator="{ on }">
                      <ir-link-to
                        v-bind="{
                          ...button,
                          text: button.component_text,
                          color: button.color ? button.color : 'fourth'
                        }"
                        v-on="on"
                        @click="
                          slotsClickManage(
                            checkType,
                            availableTableTypes,
                            availableRowTypes,
                            button,
                            handleTableClick
                          )
                        "
                      />
                    </template>
                    <span>{{ button.tooltip_text }}</span>
                  </v-tooltip>
                </template>
                <DownloadCSV
                  v-if="downloadable && itemsBone.length !== 0"
                  :generate-csv="generateCSV"
                  :file-name="nameCSV"
                  :download-text="downloadText"
                  class="d-flex"
                  @exporting-error="addSnack('error', errorDownloading)"
                  @export-finished="addSnack('success', successDownloading)"
                >
                  <template #default="{ on, attrs }">
                    <ir-link-to
                      v-bind="{ ...attrs, ...downloadAttrsProps }"
                      v-on="on"
                    />
                  </template>
                </DownloadCSV>
                <v-tooltip
                  v-if="isActiveFilters(headers)"
                  bottom
                  :disabled="!columnFiltersTooltip"
                >
                  <template #activator="{ on, attrs }">
                    <ir-link-to
                      v-bind="{ ...attrs }"
                      :icon="columnFiltersIcon"
                      :color="columnFiltersColor"
                      :text="columnFiltersLabel"
                      v-on="on"
                      @click="areColFiltersVisible = !areColFiltersVisible"
                    />
                  </template>
                  <span>{{ columnFiltersTooltip }}</span>
                </v-tooltip>
                <v-tooltip
                  v-if="filtros.length !== 0 && visibleFields(filtros)"
                  bottom
                >
                  <template #activator="{ on, attrs }">
                    <ir-link-to
                      v-bind="{ ...attrs, ...filterButtonAttrs }"
                      v-on="on"
                      @click="mostrarFiltros"
                    />
                  </template>
                  <span>
                    {{ isFiltros ? hideFiltersText : showFiltersText }}
                  </span>
                </v-tooltip>
                <v-menu
                  v-if="buttonsTableInside.length > 0"
                  offset-y
                  :left="$vuetify.breakpoint.smAndDown"
                >
                  <template #activator="{ on, attrs }">
                    <ir-link-to
                      icon="mdi-chevron-down"
                      :color="dropDownButtonColor"
                      v-bind="attrs"
                      v-on="on"
                    />
                  </template>
                  <v-list>
                    <template v-for="(button, i) in buttonsTableInside">
                      <v-tooltip
                        v-if="
                          typeof button.visible !== 'undefined'
                            ? button.visible
                            : true
                        "
                        :key="i"
                        bottom
                        :disabled="!button.tooltip_text"
                      >
                        <template #activator="{ on }">
                          <v-list-item
                            :data-cy="'tableButton-' + i"
                            v-on="on"
                            @click="
                              slotsClickManage(
                                checkType,
                                availableTableTypes,
                                availableRowTypes,
                                button,
                                handleTableClick
                              )
                            "
                          >
                            <v-list-item-title
                              v-if="
                                !!button.component_text || !!button.tooltip_text
                              "
                            >
                              {{
                                button.component_text
                                  ? button.component_text
                                  : button.tooltip_text
                              }}
                            </v-list-item-title>
                            <v-icon
                              v-bind="
                                button['icon-attrs']
                                  ? button['icon-attrs']
                                  : { color: 'primary' }
                              "
                            >
                              {{ button.icon }}
                            </v-icon>
                          </v-list-item>
                        </template>
                        <span>{{ button.tooltip_text }}</span>
                      </v-tooltip>
                    </template>
                  </v-list>
                </v-menu>
              </template>
            </div>
            <div style="position: relative">
              <div class="d-flex flex-row actionPosition pr-2 pr-md-0">
                <template v-if="!isNewDesign">
                  <template v-for="(button, i) in buttonsTableOutside">
                    <v-tooltip
                      v-if="
                        typeof button.visible !== 'undefined'
                          ? button.visible
                          : true
                      "
                      :key="i"
                      bottom
                      :disabled="!button.tooltip_text"
                    >
                      <template #activator="{ on }">
                        <v-btn
                          :data-cy="'tableButton-' + i"
                          class="d-flex align-center"
                          :fab="resolveAttr(button.attrs.fab)"
                          :small="resolveAttr(button.attrs.small)"
                          v-bind="button.attrs"
                          v-on="on"
                          @click="
                            slotsClickManage(
                              checkType,
                              availableTableTypes,
                              availableRowTypes,
                              button,
                              handleTableClick
                            )
                          "
                        >
                          <p v-if="!!button.component_text">
                            {{ button.component_text }}
                          </p>
                          <v-icon
                            v-if="button.icon"
                            v-bind="
                              button['icon-attrs']
                                ? button['icon-attrs']
                                : { color: 'white' }
                            "
                          >
                            {{ button.icon }}
                          </v-icon>
                        </v-btn>
                      </template>
                      <span>{{ button.tooltip_text }}</span>
                    </v-tooltip>
                  </template>
                  <v-menu
                    v-if="buttonsTableInside.length > 0"
                    offset-y
                    :left="$vuetify.breakpoint.smAndDown"
                  >
                    <template #activator="{ on, attrs }">
                      <v-btn
                        data-cy="dropDownButton"
                        color="primary"
                        dark
                        fab
                        small
                        v-bind="attrs"
                        v-on="on"
                      >
                        <v-icon> mdi-chevron-down </v-icon>
                      </v-btn>
                    </template>
                    <v-list>
                      <template v-for="(button, i) in buttonsTableInside">
                        <v-tooltip
                          v-if="
                            typeof button.visible !== 'undefined'
                              ? button.visible
                              : true
                          "
                          :key="i"
                          bottom
                          :disabled="!button.tooltip_text"
                        >
                          <template #activator="{ on }">
                            <v-list-item
                              :data-cy="'tableButton-' + i"
                              v-on="on"
                              @click="
                                slotsClickManage(
                                  checkType,
                                  availableTableTypes,
                                  availableRowTypes,
                                  button,
                                  handleTableClick
                                )
                              "
                            >
                              <v-list-item-title
                                v-if="
                                  !!button.component_text || !!button.tooltip_text
                                "
                              >
                                {{
                                  button.component_text
                                    ? button.component_text
                                    : button.tooltip_text
                                }}
                              </v-list-item-title>
                              <v-icon
                                v-bind="
                                  button['icon-attrs']
                                    ? button['icon-attrs']
                                    : { color: 'primary' }
                                "
                              >
                                {{ button.icon }}
                              </v-icon>
                            </v-list-item>
                          </template>
                          <span>{{ button.tooltip_text }}</span>
                        </v-tooltip>
                      </template>
                    </v-list>
                  </v-menu>
                  <DownloadCSV
                    v-if="downloadable && itemsBone.length !== 0"
                    :generate-csv="generateCSV"
                    :file-name="nameCSV"
                    :download-text="downloadText"
                  />
                  <v-tooltip
                    v-if="isActiveFilters(headers)"
                    bottom
                    :disabled="!columnFiltersTooltip"
                  >
                    <template #activator="{ on, attrs }">
                      <v-btn
                        v-bind="attrs"
                        fab
                        small
                        dark
                        @click="areColFiltersVisible = !areColFiltersVisible"
                        v-on="on"
                      >
                        <div
                          class="hideFiltersBtn"
                          :class="!areColFiltersVisible ? 'hidden' : 'showed'"
                        />
                        <v-icon> mdi-table-search </v-icon>
                      </v-btn>
                    </template>
                    <span>{{ columnFiltersTooltip }}</span>
                  </v-tooltip>
                  <v-tooltip
                    v-if="filtros.length !== 0 && visibleFields(filtros)"
                    bottom
                  >
                    <template #activator="{ on }">
                      <v-btn
                        data-cy="form-filtro-show"
                        class="d-inline-flex"
                        fab
                        small
                        dark
                        v-on="on"
                        @click="mostrarFiltros"
                      >
                        <v-icon dark>
                          {{ isFiltros ? 'mdi-filter-off' : 'mdi-filter' }}
                        </v-icon>
                      </v-btn>
                    </template>
                    <span>
                      {{ isFiltros ? hideFiltersText : showFiltersText }}
                    </span>
                  </v-tooltip>
                </template>
                <v-tooltip
                  v-if="countingDown !== null"
                  bottom
                  :disabled="refreshingToolTip === ''"
                >
                  <template #activator="{ on }">
                    <v-btn
                      style="cursor: default"
                      class="d-inline-flex elevation-0"
                      fab
                      small
                      color="#f0f0f0"
                      v-on="on"
                    >
                      <div class="number-container">
                        <span class="number">{{ countingDown }}</span>
                        <v-progress-circular
                          :value="getPercentage(countingDown, repeatTime)"
                          color="#000000"
                          width="2"
                          height="2"
                        />
                      </div>
                    </v-btn>
                  </template>
                  <span>
                    {{ refreshingToolTip }}
                  </span>
                </v-tooltip>
              </div>
            </div>
          </template>
          <template #expanded-item="{ headers: headerExpanded, item }">
            <td :colspan="headerExpanded.length">
              {{
                typeof item[expandedDataProp] !== 'undefined'
                  ? item[expandedDataProp]
                  : noExpandedData
              }}
            </td>
          </template>
          <template
            v-if="isNewDesign"
            #item="{ item, select, index }"
          >
            <v-row
              :style="{
                padding: rowPadding,
                height: rowHeight,
                'background-color': index % 2 !== 0 ? '#F5F5F5' : '' // Aplica color solo a filas impares
              }"
              class="d-flex flex-column d-sm-table-row"
            >
              <td
                v-if="showSelect"
                style="vertical-align: middle"
                class="pt-2 pb-4 pt-sm-0 pb-sm-0 px-sm-4 pl-8 pr-5 d-flex justify-end d-sm-table-cell"
              >
                <ir-checkbox
                  v-if="hasCheckbox(item[selectableKey])"
                  is-new-design
                  :value="getCheckboxValue(item)"
                  dense
                  height="5px"
                  @change="(value) => toggleItemSelection(value, select, item)"
                />
              </td>
              <template v-for="(header, i) in headers">
                <td
                  :key="'tr-' + header.value + i"
                  style="vertical-align: middle"
                  class="py-1 py-sm-0 px-sm-4 px-8 d-flex justify-space-between align-center d-sm-table-cell"
                >
                  <p
                    class="d-block d-sm-none my-0 font-weight-bold"
                    style="font-size: 14px"
                  >
                    {{ header.text }}
                  </p>
                  <IrDataTableCell
                    v-if="
                      typeof header.visible !== 'undefined'
                        ? header.visible
                        : true
                    "
                    :key="`${currentPage}-${item[header.value]}`"
                    :tr-index="i + 1"
                    :td-index="i"
                    :cell-value="item[header.value]"
                    :header="header"
                    is-new-design
                    :item="item"
                    :check-white-color="checkWhiteColor"
                    :image_row="image_row"
                    @preview-image="seleccionarImagen"
                    @excecute="
                      slotsClickManage(
                        checkType,
                        availableTableTypes,
                        availableRowTypes,
                        header,
                        handleRowClick,
                        item
                      )
                    "
                  />
                </td>
              </template>
              <td
                v-if="comportamientosRowGroups.length > 0"
                style="vertical-align: middle"
                class="py-1 py-sm-0 px-sm-4 px-8 d-flex justify-end d-sm-table-cell"
              >
                <ir-row-grouped-comps
                  :data-cy="'grouped-comps-' + index"
                  :comportamientos="checkComportamientosGrouped(item)"
                  @comp-clicked="
                    (button) =>
                      slotsClickManage(
                        checkType,
                        availableTableTypes,
                        availableRowTypes,
                        button,
                        handleRowClick,
                        item
                      )
                  "
                />
              </td>
            </v-row>
          </template>
          <template
            v-for="(header, i) in comportamientosRowGroups.length > 0
              ? [...headers, { name: 'comportamientos-grouped' }]
              : headers"
            #[`item.${header.value}`]="{ item, index: itemIndex }"
          >
            <slot
              :name="'tr-' + (header.value ? header.value : header.name)"
              :value="item[header.value]"
            >
              <IrDataTableCell
                v-if="
                  header.name !== 'comportamientos-grouped' &&
                  typeof header.visible !== 'undefined'
                    ? header.visible
                    : true
                "
                :key="`${currentPage}-${item[header.value]}`"
                :tr-index="i + 1"
                :td-index="i"
                :cell-value="item[header.value]"
                :header="header"
                :item="item"
                :check-white-color="checkWhiteColor"
                :image_row="image_row"
                @preview-image="seleccionarImagen"
                @excecute="
                  slotsClickManage(
                    checkType,
                    availableTableTypes,
                    availableRowTypes,
                    header,
                    handleRowClick,
                    item
                  )
                "
              />
              <ir-row-grouped-comps
                v-if="header.name === 'comportamientos-grouped'"
                :key="i + 1"
                :data-cy="'grouped-comps-' + itemIndex"
                :comportamientos="checkComportamientosGrouped(item)"
                @comp-clicked="
                  (button) =>
                    slotsClickManage(
                      checkType,
                      availableTableTypes,
                      availableRowTypes,
                      button,
                      handleRowClick,
                      item
                    )
                "
              />
            </slot>
          </template>
          <template #footer>
            <div
              v-if="isNewDesign"
              class="pagination-container"
              :class="paginationClasses(loading)"
            >
              <div class="pagination">
                <button
                  :style="currentPage <= 1 ? 'pointer-events: none;' : ''"
                  @click="setNewPage(currentPage - 1)"
                >
                  <v-icon> mdi-chevron-left </v-icon>
                </button>
                <template v-for="(page, i) in getPages(rowsPerPage, itemsBone)">
                  <button
                    :key="i"
                    class="page-button"
                    :style="isNaN(page) ? 'pointer-events: none;' : ''"
                    @click="setNewPage(page)"
                  >
                    <div
                      style="
                        height: 100%;
                        width: 30%;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        position: relative;
                      "
                    >
                      <span
                        style="
                          font-weight: 400;
                          font-size: 14px;
                          line-height: 24px;
                          margin: 0px;
                        "
                      >
                        {{ page }}
                      </span>
                      <div
                        style="
                          position: absolute;
                          bottom: 0%;
                          left: 0%;
                          width: 100%;
                          height: 4px;
                          background-color: #000000;
                          transition: all 0.2s ease;
                        "
                        :style="currentPage === page ? '' : 'opacity: 0%;'"
                      />
                    </div>
                  </button>
                </template>
                <button
                  :style="
                    resolvePageButtonStyle(
                      currentPage,
                      getPages(rowsPerPage, itemsBone).length
                    )
                  "
                  @click="setNewPage(currentPage + 1)"
                >
                  <v-icon> mdi-chevron-right </v-icon>
                </button>
              </div>
            </div>
          </template>
          <template
            v-if="isActiveFilters(headers)"
            #body.prepend
          >
            <v-fade-transition>
              <tr v-show="areColFiltersVisible">
                <td v-if="showSelect" />
                <td
                  v-for="{
                    text,
                    value,
                    'filter-component': filterComponent
                  } in headers"
                  :key="text"
                  class="pt-2 pb-2 filters-td"
                >
                  <component
                    :is="filterComponent.type"
                    v-if="
                      typeof filterComponent !== 'undefined' &&
                      typeof filterComponent.type !== 'undefined'
                    "
                    v-bind="filterComponent.props"
                    v-model="columnFiltersValues[value]"
                    :id_resolver="id_resolver"
                  />
                </td>
              </tr>
            </v-fade-transition>
          </template>
        </v-data-table>
      </div>
    </template>
  </ir-data-table-bone>
</template>

<script>
import Vue from 'vue'
import { ref, onMounted, computed, getCurrentInstance } from 'vue'
import { modal_row, modal, tableComponentName } from '../../constants'
import IrImagePreviewer from '../IrImagePreviewer/IrImagePreviewer.vue'
import IrDataTableCell from './IrDataTableCell.vue'
import DownloadCSV from './DownloadCSV.vue'
import IrDataTableBone from './IrDataTableBone.vue'
import IrDialog from '../IrDialog/IrDialog.vue'
import focusAndScrollMixin from '../../mixins/focusAndScroll'
import IrForm from '../formularios/IrForm/IrForm.vue'
import IrCheckbox from '../formularios/IrCheckbox/IrCheckbox.vue'
import IrLinkTo from '../formularios/IrLinkTo/IrLinkTo.vue'
import IrRowGroupedComps from './IrRowGroupedComps.vue'
import { filterProps } from '../../helpers/filterProps'
export default {
  name: tableComponentName,
  components: {
    IrDataTableBone,
    IrLinkTo,
    IrDialog,
    IrForm,
    IrCheckbox,
    DownloadCSV,
    IrDataTableCell,
    IrImagePreviewer,
    IrRowGroupedComps
  },
  inheritAttrs: false,
  props: {
    dropDownButtonColor: {
      type: String,
      default: 'fourth'
    },
    isFilterVisibleOnLoad: {
      type: Boolean,
      default: false
    },
    errorDownloading: {
      type: String,
      default: 'Error downloading'
    },
    successDownloading: {
      type: String,
      default: 'Downloaded successfully'
    },
    applyFiltersText: {
      type: String,
      default: 'Apply filters'
    },
    showFiltersText: {
      type: String,
      default: 'Show filters'
    },
    downloadAttrs: {
      type: Object,
      default: () => ({
        color: 'fourth',
        icon: 'mdi-tray-arrow-down',
        text: ''
      })
    },
    filtersAttrs: {
      type: Object,
      default: () => ({
        color: 'fourth',
        icon: '',
        text: ''
      })
    },
    hideFiltersText: {
      type: String,
      default: 'Hide filters'
    },
    selectableKey: {
      type: String,
      default: 'isSelectable'
    },
    downloadText: {
      type: String,
      default: 'Download'
    },
    isNewDesign: {
      type: Boolean,
      default: false
    },
    hideDefaultHeader: {
      type: Boolean,
      default: false
    },
    hideDefaultFooter: {
      type: Boolean,
      default: false
    },
    headerColor: {
      type: String,
      required: false,
      default: ''
    },
    isFocused: {
      type: Boolean,
      default: false
    }
  },
  setup(props, { attrs }) {
    const vueInstance = getCurrentInstance()
    const vuetify = vueInstance.proxy.$vuetify
    const iclstore = Vue.prototype.$iclstore
    const logErrorComponent = Vue.prototype.$logErrorComponent
    const iclRouter = Vue.prototype.$iclRouter
    const irDataTableDOM = ref(null)
    const otherProps = ref({})
    const columnFiltersValues = ref({})
    const areColFiltersVisible = ref(false)
    const imagenSeleccionada = ref({})
    const showDialog = ref(false)
    const selectedRows = ref([])
    const isFiltros = ref(false)
    const searchValue = ref('')
    const countingDown = ref(null)
    const allSelected = ref(false)
    const groupedBy = ref(undefined)
    const rowHeight = computed(() => {
      return vuetify.breakpoint.smAndUp ? '48px !important' : 'auto'
    })
    const rowPadding = computed(() => {
      return vuetify.breakpoint.smAndUp ? '0px' : '20px 0px !important'
    })
    const getFilteredProps = computed(() => {
      return filterProps(irDataTableDOM.value, otherProps.value, ['data-cy'])
    })
    const resolveHeaderStyle = computed(() => {
      let stylesAux = ''
      if (props.headerColor && props.headerColor !== '')
        stylesAux = props.headerColor
      else stylesAux = vuetify.theme.themes.light.primary
      return `height: 40px !important; background-color: ${stylesAux}`
    })
    const generatedDownloadText = computed(() => {
      return props.downloadAttrs.text
        ? props.downloadAttrs.text
        : props.downloadText
    })
    const downloadAttrsProps = computed(() => {
      return {
        ...props.downloadAttrs,
        text: generatedDownloadText.value
      }
    })
    const filterButtonAttrs = computed(() => {
      let icon
      if (props.filtersAttrs.icon) icon = props.filtersAttrs.icon
      else if (isFiltros.value) icon = 'mdi-filter-off'
      else icon = 'mdi-filter'
      const text = props.filtersAttrs.text ? props.filtersAttrs.text : ''
      return { ...props.filtersAttrs, icon, text }
    })
    const progressLinearLoadingClasses = (loading) => {
      return isLoading(loading, otherProps.value.loading) ? '' : 'opacity-0'
    }
    const paginationClasses = (loading) => {
      return props.isNewDesign && !isLoading(loading, otherProps.value.loading)
        ? ''
        : 'opacity-0'
    }
    const groupBy = (header) => {
      groupedBy.value = header.value == groupedBy.value ? undefined : header.value
    }
    const isUndefined = (item) => {
      return typeof item == 'undefined'
    }
    const hasCheckbox = (key) => {
      return typeof key === 'undefined' || key
    }
    const getColSpan = (headers, showSelect) => {
      return headers.length + (showSelect ? 1 : 0)
    }
    const addSnack = (color, text) => {
      iclstore.dispatch('addSnack', [{ color, text }])
    }
    const resolvePageButtonStyle = (currentPage, pages) => {
      return currentPage >= pages ? 'pointer-events: none;' : ''
    }
    const resolveStyle = (headerOver, indexSelected, i, sortSelected) => {
      if (i === headerOver || indexSelected(i) !== -1) {
        if (
          sortSelected[indexSelected(i)] &&
          !sortSelected[indexSelected(i)].sortDesc
        ) {
          return 'transform: rotate(180deg);'
        } else {
          return ''
        }
      } else {
        return 'opacity: 0%; pointer-events: none;'
      }
    }
    const resolveTableClasses = (groupColor) => {
      const color = groupColor && groupColor !== '' ? groupColor : '#FF0000'
      return (
        (props.isNewDesign ? '' : 'elevation-1') +
        ' ' +
        getGroupClass(color)
      )
    }
    const searchAtrrs = (search) => {
      if (typeof search === 'string') return { label: search }
      else return search
    }
    const setShowColumnFilters = (e) => {
      areColFiltersVisible.value = e
    }
    const isActiveFilters = (headers) => {
      return (
        headers.findIndex((header) => {
          return (
            typeof header['filter-component'] !== 'undefined' &&
            typeof header['filter-component'].type !== 'undefined'
          )
        }) > -1
      )
    }
    const getPercentage = (countingDown, repeatTime) => {
      let totalPercentage = (countingDown * 100) / Math.floor(repeatTime / 1000)
      return 100 - totalPercentage
    }
    const updateRefreshing = (newCountingDown) => {
      countingDown.value = newCountingDown
    }
    const visibleFields = (filtros) => {
      let filtrosAux = filtros.filter((filtro) => filtro.props.visible)
      return filtrosAux.length > 0
    }
    const getGroupClass = (color) => {
      return color ? `${getGroupClassName(color)}` : ''
    }
    const mostrarFiltros = () => {
      isFiltros.value = !isFiltros.value
    }
    const resolveAttr = (attr) => {
      return typeof attr !== 'undefined' ? attr : true
    }
    const isLoading = (loading, propsLoading) => {
      return (
        (loading && typeof propsLoading === 'undefined') ||
        (loading && typeof propsLoading !== 'undefined' && propsLoading)
      )
    }
    const cerrarDialog = () => {
      showDialog.value = false
    }
    const getGroupClassName = (color) => {
      const localColor = color && color !== '' ? color : '#FF0000'
      return 'table-group-color-' + (localColor[0] === '#' ? localColor.slice(1) : localColor)
    }
    const checkWhiteColor = (obj) => {
      let white =
        typeof obj.attrs !== 'undefined' &&
        typeof obj.attrs.color !== 'undefined' &&
        obj.attrs.color === 'white'
      return white ? 'black' : 'white'
    }
    const slotsClickManage = (
      checkType,
      availableTableTypes,
      availableRowTypes,
      action,
      clickHandler,
      item
    ) => {
      let { type, confirmation } = action
      const isValidType = typeof type !== 'undefined' && type !== ''
      let rowClick = false
      let tableClick = false
      if (isValidType) {
        rowClick = checkType(availableRowTypes, type)
        if (!rowClick) tableClick = checkType(availableTableTypes, type)
      } else
        logErrorComponent(
          tableComponentName,
          'Hubo un error. No se pudo detectar el comportamiento correspondiente.',
          iclRouter
        )
      if (rowClick) {
        if (type === modal_row || confirmation) showDialog.value = true
        clickHandler(action, item)
      } else if (tableClick) {
        if (type === modal || confirmation) showDialog.value = true
        clickHandler(action)
      } else
        logErrorComponent(
          tableComponentName,
          'Hubo un error. Se detectó un comportamiento inválido. Nombre: ' +
            type +
            '.',
          iclRouter
        )
    }
    const seleccionarImagen = (imagen) => {
      imagenSeleccionada.value = imagen
    }
    const focusAndScroll = focusAndScrollMixin.methods.focusAndScroll
    onMounted(() => {
      if (props.isFocused) {
      let element = irDataTableDOM.value.$el.querySelector('table')
      focusAndScroll(element)
    }
      isFiltros.value = props.isFilterVisibleOnLoad
      let aux = { ...attrs }
      delete aux.headers
      delete aux.items
      delete aux.fcn
      delete aux.args
      delete aux.show_select
      Object.assign(otherProps.value, aux)
    })
    return {
      attrs,
      irDataTableDOM,
      otherProps,
      columnFiltersValues,
      areColFiltersVisible,
      imagenSeleccionada,
      showDialog,
      selectedRows,
      isFiltros,
      searchValue,
      countingDown,
      rowHeight,
      rowPadding,
      getFilteredProps,
      resolveHeaderStyle,
      downloadAttrsProps,
      filterButtonAttrs,
      progressLinearLoadingClasses,
      paginationClasses,
      isUndefined,
      hasCheckbox,
      getColSpan,
      addSnack,
      resolvePageButtonStyle,
      resolveStyle,
      resolveTableClasses,
      searchAtrrs,
      setShowColumnFilters,
      isActiveFilters,
      getPercentage,
      updateRefreshing,
      visibleFields,
      getGroupClass,
      mostrarFiltros,
      resolveAttr,
      isLoading,
      cerrarDialog,
      getGroupClassName,
      checkWhiteColor,
      groupBy,
      groupedBy,
      slotsClickManage,
      seleccionarImagen,
      allSelected
    }
  }
}
</script>

<style lang="scss" scoped>
.page-button {
  height: 48px;
  width: 48px;
  display: flex;
  justify-content: center;
}
.pagination {
  height: 48px;
  display: flex;
  flex-direction: row;
  align-items: center;
}
.pagination-container {
  height: 72px;
  width: 100%;
  display: flex;
  align-items: end;
  justify-content: center;
}
.opacity-0 {
  opacity: 0;
}
.sort-index-header {
  font-size: 12px;
  opacity: 0.85;
}
.header-font {
  font-weight: 400;
  font-size: 14px;
  line-height: 24px;
}
.actionPosition {
  gap: 10px;
  position: absolute;
  right: -27px;
  top: -35px;
  z-index: 7;
  @media only screen and (max-width: 960px) {
    right: 0px;
    top: -27px;
  }
}
.number-container {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.loading-text__container {
  width: 100%;
  margin-top: 8px;
}
.number {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
}
.hideFiltersBtn {
  width: 4px;
  height: 30px;
  rotate: -45deg;
  background: white;
  border-right: 2px solid #383838;
  display: block;
  z-index: 100;
  border-radius: 4px;
  position: absolute;
  transition: opacity 0.3s linear;
  content: '';
}

.searchField {
  width: 60%;
}

.hidden {
  opacity: 0;
}
.showed {
  opacity: 1;
}

.top__container {
  width: 100%;
  margin-bottom: 6px;
  gap: 24px;
}

::v-deep .filters-td div div[class*='--outlined'].v-input--dense fieldset {
  height: 35px;
}
::v-deep .filters-td div div[class*='--outlined'].v-input--dense {
  & div[class*='__slot'] {
    height: 30px;
    min-height: 30px !important;
    & input {
      height: 30px;
    }
    & .v-input__append-inner {
      margin-top: 2px;
    }
    & label {
      top: 5px;
    }
  }
  & .v-select__slot .v-input__icon {
    min-width: 5px !important;
    width: 5px !important;
  }
}

.ir-data-table-header-cell {
  &-start {
    justify-content: start;
  }
  &-end {
    flex-direction: row-reverse;
    justify-content: end;
  }
  &-center {
    justify-content: center;
  }
}
</style>
Last Updated: 4/5/2024, 4:52:19 PM