# 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:
|
| 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:
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 |
| 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 |
| noExpandedData | String | ' ' | false | Texto que aparece en caso de que la propiedad detallada en Esta propiedad se ignora si |
| headersCSV | Array | undefined | false | Arreglo con los valores de la propiedad |
| download-text | String |
| false | Determina el texto que se mostrará dentro del botón de descarga y en el tooltip del mismo.
|
| download-attrs | Object |
| false | Determina el color, el texto y el ícono del botón de descarga. |
| filters-attrs | Object |
| 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 |
| item-key | String |
| false | Nombre de la propiedad que va a utilizarse como identificador de cada item. |
| row_classes | Object |
| false | Propiedad con estructura de objeto: 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 |
| 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 |
| false | Determina el tiempo de refresco de la tabla. |
| isNewDesign | Boolean | false | false | Determina si la tabla se renderizará con otro diseño. |
| header-color | String |
| false | Determina el color del header de la tabla (cuando la propiedad |
# 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>