Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions dist/dom/FootersLxDom.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import AbstractLxDom from './AbstractLxDom';
import { LxConfigObject } from '../core/LyxeaDatatable';
declare class FooterLxDom extends AbstractLxDom {
tableRef: HTMLElement | null;
footerRef: HTMLElement | null;
config: LxConfigObject;
constructor(ref: HTMLElement, config: LxConfigObject);
appendTableFooter: () => void;
getFooterElement: () => HTMLElement | null;
build(id?: string, className?: string): Promise<HTMLElement>;
}
export default FooterLxDom;
85 changes: 49 additions & 36 deletions lib/core/Filters.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,60 @@
import FiltersLxDom from '@dom/FiltersLxDom';
import { CustomDatatableConfig } from './LyxeaDatatable';
import { LxConfigObject } from './LyxeaDatatable';
import DataTable from 'datatables.net-dt';
import FooterLxDom from '@dom/FootersLxDom';

class Filters<T> {
#domBuilder?: FiltersLxDom;
#headerLength: number;
#instance?: DataTable<T>;

constructor(config: CustomDatatableConfig<T>, instance?: DataTable<T>) {
this.#headerLength = this.#calculateHeaderLength(config);
this.#instance = instance;
tableRef: HTMLElement | null;
config: LxConfigObject;
footerUiBuilder: FooterLxDom;

constructor(ref: HTMLElement, config: LxConfigObject) {
this.tableRef = ref;
this.config = config;
this.footerUiBuilder = new FooterLxDom(this.tableRef, config);
}

#calculateHeaderLength<T>(config: CustomDatatableConfig<T>): number {
let sum = 0;
if (config.columns) {
sum += config.columns.length;
}
if (config.lxConfig && config.lxConfig.headers) {
config.lxConfig.headers.forEach((header) => {
sum += header.columns.length;
});
}

return sum;
}

init(headerEl?: HTMLElement): HTMLElement | undefined {
if (!headerEl) return;
if (!this.#instance) throw new Error('Instance is not defined');

this.#domBuilder = new FiltersLxDom(headerEl);
return this.#domBuilder.generate(
this.#headerLength,
this.#instance,
this._filterEvent
) as HTMLElement;
async build(
id = 'main_filter',
className = 'main_filter'
): Promise<HTMLElement> {
return this.footerUiBuilder.build(id, className);
}

_filterEvent(e: Event) {
const input = e.target as HTMLInputElement;
init(dtInstance: DataTable<T>, type: String) {
// @ts-ignore
this.column(input.dataset.index).search(input.value).draw();
dtInstance.columns().every(function () {
// @ts-ignore
let title = this.footer().textContent;

// Create input element
if (type == 'input') {
// Create a container div for the input and icon
let container = document.createElement('div');
container.classList.add('column_search_container');
// Create the input element
let input = document.createElement('input');
input.setAttribute('type', 'text');
input.classList.add('column_search');
input.placeholder = title;
// Create the icon element
let icon = document.createElement('i');
icon.classList.add('fa', 'fa-search');
// Append the input and icon to the container
container.appendChild(input);
container.appendChild(icon);
// Replace the footer content with the container
// @ts-ignore
this.footer().replaceChildren(container);
// Event listener for user input
input.addEventListener('keyup', () => {
// @ts-ignore
if (this.search() !== this.value) {
// @ts-ignore
this.search(input.value).draw();
}
});
}
});
}
}

Expand Down
114 changes: 101 additions & 13 deletions lib/core/LyxeaDatatable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import 'datatables.net-buttons/js/buttons.colVis.mjs';
import 'datatables.net-buttons/js/buttons.html5.mjs';
import 'datatables.net-buttons/js/buttons.print.mjs';

import DataTable, { Config, ConfigColumns } from 'datatables.net-dt';
import DataTable, {
Config,
ConfigColumns,
ConfigButtons,
ButtonConfig,
} from 'datatables.net-dt';
import AbstractLyxeaDatatable from './AbstractLyxeaDatatable';
import DtHeaders from './DtHeaders';
import Dao from '@dao/Dao';
Expand Down Expand Up @@ -40,17 +45,22 @@ export type CustomRenderer =
| Array<Function>
| Array<string | Function>;

export type ScrollYFitToScreen = {
addStaticMargin?: number;
};

export interface CustomDatatableConfig<T> extends Config {
lxConfig?: LxConfigObject;
data?: Array<T>;
}

export type LxConfigObject = {
keepFixedHeaderInDT?: boolean;
url?: string;
headers?: LxHeadersConfig;
filters?: boolean;
handleBootrapTabChange?: boolean;
scrollYFitToScreen?: boolean;
scrollYFitToScreen?: boolean | ScrollYFitToScreen;
row_action?: {
width: string;
className?: string;
Expand Down Expand Up @@ -94,7 +104,9 @@ class LyxeaDatatable<T>
#standardColumnBuilder?: DtColumns;
#customColumnBuilder?: DtHeaders;
#dtButtons?: DtButtons;
// @ts-ignore
#headerElement?: HTMLElement;
filterColumn?: Filters<T>;

constructor(ref: string, config?: CustomDatatableConfig<T>) {
super();
Expand Down Expand Up @@ -178,8 +190,10 @@ class LyxeaDatatable<T>
If columns in the standard object and in lxconfig, header generation is only based on lxconfig.
This allows you to generate the header with all the columns (and not just the columns defined in lxconfig).
*/
if (lxConfig.headers && standardColumns && standardColumns.length) {
lxConfig.headers?.unshift({ columns: [...standardColumns] });
if (standardColumns?.length) {
lxConfig.headers = lxConfig.headers
? [{ columns: [...standardColumns] }, ...lxConfig.headers]
: [{ columns: [...standardColumns] }];
}
new LxRenderer(lxConfig);
const headersBuilder = this.#customColumnBuilder
Expand All @@ -193,6 +207,11 @@ class LyxeaDatatable<T>
});
this.#headerElement = await headerUiBuilder.build();

if (lxConfig && lxConfig.filters) {
this.filterColumn = new Filters(this.refElement, lxConfig);
await this.filterColumn.build();
}

/**
* Get the data if not set
*/
Expand Down Expand Up @@ -252,7 +271,9 @@ class LyxeaDatatable<T>
if (!this.config.buttons)
this.config.buttons = this.#dtButtons.getDefaults();

this.#dtButtons.parse(this.config.buttons);
this.#dtButtons.parse(
this.config.buttons as true | ConfigButtons | (string | ButtonConfig)[]
);

jquery(`${this._ref}`).on('init.dt', (e, settings) => {
if (e.namespace !== 'dt') {
Expand All @@ -263,17 +284,28 @@ class LyxeaDatatable<T>
* Adding filter inputs
*/
if (lxConfig && lxConfig.filters) {
// @ts-ignore
this.#headerElement = new Filters(this.config, dtInstance).init(
this.#headerElement
);
this.filterColumn?.init(dtInstance, 'input');
if (this.config?.scrollX) {
jquery(`${this._ref}_wrapper .dt-scroll-foot tfoot tr th`).removeAttr(
'data-dt-column'
);
var footer = jquery(`${this._ref}_wrapper .dt-scroll-foot tfoot tr`);
jquery(`${this._ref}_wrapper .dt-scroll-head thead`).append(footer);
} else {
jquery(`${this._ref} tfoot tr th`).removeAttr('data-dt-column');
var footer = jquery(`${this._ref} tfoot tr`);
jquery(`${this._ref} thead`).append(footer);
}
}

/**
* Fit scrollY to screen
*/
if (lxConfig && lxConfig.scrollYFitToScreen) {
this.scrollYFitToScreen();
if (lxConfig?.scrollYFitToScreen) {
const scrollYConfig = this._convertToScrollYFitToScreenConfig(
lxConfig.scrollYFitToScreen
);
this._scrollYFitToScreen(scrollYConfig);
// Force redraw (FitToScreen has an effect on the draw event)
dtInstance.draw();
}
Expand All @@ -291,13 +323,56 @@ class LyxeaDatatable<T>
if (lxConfig && lxConfig.handleBootrapTabChange)
this.handleBootrapTabChange(this.instance);

if (lxConfig?.keepFixedHeaderInDT && this._ref) {
this.__keepFixedHeaderInDT();
}
return this;
}

__filterDataWithKey() {}

scrollYFitToScreen() {
__keepFixedHeaderInDT() {
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
for (const addedNode of mutation.addedNodes) {
if (!(addedNode instanceof HTMLElement)) continue;

// Si on trouve un .dtfh-floatingparent
if (addedNode.classList.contains('dtfh-floatingparent')) {
const table = document.querySelector(this._ref);
if (!table) return;

const refClean =
this._ref.startsWith('#') || this._ref.startsWith('.')
? this._ref.slice(1)
: this._ref;
const expectedAriaDescribedBy = `${refClean}_info`;

const innerTable = addedNode.querySelector('table');
if (
innerTable?.getAttribute('aria-describedby') ===
expectedAriaDescribedBy
) {
if (!table.contains(addedNode)) {
table.insertBefore(addedNode, table.firstChild);
}
}
}
}
}
});

// Observe tout le document (ou tu peux cibler un conteneur plus précis)
observer.observe(document.body, {
childList: true,
subtree: true,
});
}

_scrollYFitToScreen(config: ScrollYFitToScreen) {
const self = this;
const staticMargin =
config && config.addStaticMargin ? config.addStaticMargin : 0;
jquery(`${this._ref}`).on('draw.dt', (e, _) => {
if (e.namespace !== 'dt') {
return;
Expand Down Expand Up @@ -334,6 +409,7 @@ class LyxeaDatatable<T>
(acc, node) => acc + (node as HTMLElement).offsetHeight,
0
);
console.log('dtLayoutRowsHeight', dtLayoutRowsHeight);
const myHeight =
window.innerHeight - // La taille de la fenêtre complete
tabTop - // L'ordonnée du haut du tableau
Expand All @@ -342,7 +418,7 @@ class LyxeaDatatable<T>
dtScrollHeadHeight - // La taille du header (lorsqu'on utilise `layout`)
dtScrollFootHeight - // La taille du footer (lorsqu'on utilise `layout`)
dtLayoutRowsHeight - // La taille de toutes les rows (lorsqu'on utilise `layout`)
10; // valeur statique pour assurer une marge
staticMargin; // valeur statique pour assurer une marge
const dtScrollBody = document.querySelector(
`${self._ref}_wrapper .dt-scroll-body`
) as HTMLElement;
Expand All @@ -369,6 +445,18 @@ class LyxeaDatatable<T>
});
});
}

// Créer une fonction utilitaire pour convertir le paramètre
_convertToScrollYFitToScreenConfig(
config: boolean | ScrollYFitToScreen
): ScrollYFitToScreen {
if (typeof config === 'boolean' && config === true) {
return {
addStaticMargin: 0,
};
}
return config as ScrollYFitToScreen;
}
}

export default LyxeaDatatable;
3 changes: 3 additions & 0 deletions lib/dom/FiltersLxDom.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import AbstractLxDom from './AbstractLxDom';
import DataTable from 'datatables.net';

/**
* Deprecated
*/
class FiltersLxDom extends AbstractLxDom {
#headerElement: Node;
constructor(header: Node) {
Expand Down
62 changes: 62 additions & 0 deletions lib/dom/FootersLxDom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import AbstractLxDom from './AbstractLxDom';
import { LxConfigObject } from '@core/LyxeaDatatable';
import DomError from './DomError';

class FooterLxDom extends AbstractLxDom {
tableRef: HTMLElement | null;
footerRef: HTMLElement | null = null;
config: LxConfigObject;

constructor(ref: HTMLElement, config: LxConfigObject) {
super();
this.tableRef = ref;
this.config = config;

this.appendTableFooter();
this.getFooterElement();
}

appendTableFooter = () => {
if (!this.tableRef?.querySelector('tfoot'))
this.tableRef?.append(this.$element('tfoot', {}));
};

getFooterElement = (): HTMLElement | null => {
this.footerRef = this.tableRef?.querySelector('tfoot') ?? null;
return this.footerRef;
};

async build(
id = 'main_footer',
className = 'main_footer'
): Promise<HTMLElement> {
if (!this.footerRef) throw new DomError('Cannot select the dom footer ref');
if (!this.config.headers?.length) return this.footerRef;

const groupFooterCells: Array<HTMLElement> = [];
this.config.headers?.forEach((header) => {
header.columns?.forEach((column) => {
groupFooterCells.push(
this.$element('th', {
classList: ['colspan-border'],
attributes: {
rowspan: '1',
colspan: '1',
},
//content: column.title,
style: column.style,
})
);
});
});
const groupFooterWrapper = this.$element('tr', {
attributes: { class: className, id: id },
children: groupFooterCells,
});
this.footerRef?.append(groupFooterWrapper);

return this.footerRef;
}
}

export default FooterLxDom;
Loading