diff --git a/dist/dom/FootersLxDom.d.ts b/dist/dom/FootersLxDom.d.ts new file mode 100644 index 0000000..fa0c5af --- /dev/null +++ b/dist/dom/FootersLxDom.d.ts @@ -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; +} +export default FooterLxDom; diff --git a/lib/core/Filters.ts b/lib/core/Filters.ts index 3caff12..6e23bcf 100644 --- a/lib/core/Filters.ts +++ b/lib/core/Filters.ts @@ -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 { - #domBuilder?: FiltersLxDom; - #headerLength: number; - #instance?: DataTable; - - constructor(config: CustomDatatableConfig, instance?: DataTable) { - 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(config: CustomDatatableConfig): 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 { + return this.footerUiBuilder.build(id, className); } - _filterEvent(e: Event) { - const input = e.target as HTMLInputElement; + init(dtInstance: DataTable, 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(); + } + }); + } + }); } } diff --git a/lib/core/LyxeaDatatable.ts b/lib/core/LyxeaDatatable.ts index d8f4a2a..8504563 100644 --- a/lib/core/LyxeaDatatable.ts +++ b/lib/core/LyxeaDatatable.ts @@ -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'; @@ -40,17 +45,22 @@ export type CustomRenderer = | Array | Array; +export type ScrollYFitToScreen = { + addStaticMargin?: number; +}; + export interface CustomDatatableConfig extends Config { lxConfig?: LxConfigObject; data?: Array; } export type LxConfigObject = { + keepFixedHeaderInDT?: boolean; url?: string; headers?: LxHeadersConfig; filters?: boolean; handleBootrapTabChange?: boolean; - scrollYFitToScreen?: boolean; + scrollYFitToScreen?: boolean | ScrollYFitToScreen; row_action?: { width: string; className?: string; @@ -94,7 +104,9 @@ class LyxeaDatatable #standardColumnBuilder?: DtColumns; #customColumnBuilder?: DtHeaders; #dtButtons?: DtButtons; + // @ts-ignore #headerElement?: HTMLElement; + filterColumn?: Filters; constructor(ref: string, config?: CustomDatatableConfig) { super(); @@ -178,8 +190,10 @@ class LyxeaDatatable 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 @@ -193,6 +207,11 @@ class LyxeaDatatable }); 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 */ @@ -252,7 +271,9 @@ class LyxeaDatatable 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') { @@ -263,17 +284,28 @@ class LyxeaDatatable * 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(); } @@ -291,13 +323,56 @@ class LyxeaDatatable 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; @@ -334,6 +409,7 @@ class LyxeaDatatable (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 @@ -342,7 +418,7 @@ class LyxeaDatatable 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; @@ -369,6 +445,18 @@ class LyxeaDatatable }); }); } + + // 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; diff --git a/lib/dom/FiltersLxDom.ts b/lib/dom/FiltersLxDom.ts index 68ebd8c..06f0d11 100644 --- a/lib/dom/FiltersLxDom.ts +++ b/lib/dom/FiltersLxDom.ts @@ -1,6 +1,9 @@ import AbstractLxDom from './AbstractLxDom'; import DataTable from 'datatables.net'; +/** + * Deprecated + */ class FiltersLxDom extends AbstractLxDom { #headerElement: Node; constructor(header: Node) { diff --git a/lib/dom/FootersLxDom.ts b/lib/dom/FootersLxDom.ts new file mode 100644 index 0000000..eda6fb0 --- /dev/null +++ b/lib/dom/FootersLxDom.ts @@ -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 { + if (!this.footerRef) throw new DomError('Cannot select the dom footer ref'); + if (!this.config.headers?.length) return this.footerRef; + + const groupFooterCells: Array = []; + 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; diff --git a/lib/dto/Renderer.ts b/lib/dto/Renderer.ts index b59dc4d..4f91bcc 100644 --- a/lib/dto/Renderer.ts +++ b/lib/dto/Renderer.ts @@ -1,131 +1,156 @@ import { LxConfigObject } from '@core/LyxeaDatatable'; import '../utils/fixToFixed'; -import $ from 'jquery'; import dayjs from 'dayjs'; -export type RendederConfig = {}; - -type MultiRender = Array<(d: T, type: any, row: any, meta: any) => T>; +const CustomRenderers = { + DATE_DAY: { + render: function (data: any, type: any) { + if (type === 'display' || type === 'filter') { + return dayjs(data).format('DD/MM/YYYY'); + } + return data; + }, + }, + DATE_WITH_SECOND: { + render: function (data: any, type: any) { + if (type === 'display' || type === 'filter') { + return dayjs(data).format('DD/MM/YYYY HH:mm:ss'); + } + return data; + }, + }, + LOCAL_NUMBER: { + render: function (data: any, type: any) { + if (type === 'display' || type === 'filter') { + return data.toLocaleString(); + } + return data; + }, + }, + BOOLEAN_OUI_NON: { + render: function (data: any, type: any) { + if (type === 'display' || type === 'filter') { + return data === true ? 'Oui' : 'Non'; + } + return data; + }, + }, + NUMBER_FIXED_2: { + render: function (data: any) { + if (typeof data === 'number') { + return data.toFixed(2); + } else if (typeof data === 'string') { + const float = parseFloat(data); + if (isNaN(float)) return data; + else return float.toFixed(2); + } + }, + }, + NUMBER_2_DIGIT_MAX: { + render: function (data: any) { + if (typeof data === 'number') { + if (data % 1 === 0) return data; + else return data.toFixed(2); + } else if (typeof data === 'string') { + const float = parseFloat(data); + if (isNaN(float)) return data; + if (float % 1 === 0) return data; + else return float.toFixed(2); + } else { + return data; + } + }, + }, + PARSE_INT: { + render: function (data: any) { + return parseInt(data); + }, + }, + UPPERCASE: { + render: (data: any) => + typeof data === 'string' ? data.toUpperCase() : data, + }, + LOWERCASE: { + render: (data: any) => + typeof data === 'string' ? data.toLowerCase() : data, + }, + CUT_LONG_TEXT: { + createdCell: function (td: any, cellData: any) { + if (typeof cellData === 'string') { + if (cellData.length > 30) { + td.title = cellData; + td.innerText = cellData.substring(0, 28) + '…'; + } + } + }, + }, + CHECKBOX: { + checkboxes: { + selectRow: true, + }, + }, + _dynamic: [ + { + pattern: /^DATE_TO_FORMAT_(.+)$/, + handler: (match: any) => { + const format = match[1]; + return { + render: function (data: any, type: any) { + if (type === 'display' || type === 'filter') { + //return moment(data).format(format); + return dayjs(data).format(format); + } + return data; + }, + }; + }, + }, + ], +}; -/** Multi render function to handle array of modifiers */ -// original idea : https://datatables.net/forums/discussion/43160/multi-render-renderer -// @ts-ignore -$.fn.dataTable.render.multi = - (renderArray: MultiRender) => - (d: T, type: any, row: any, meta: any) => { - renderArray.forEach((render) => { - if (render) d = render(d, type, row, meta); - }); - return d; - }; +function resolveRenderer(nameOrObject: any) { + if (typeof nameOrObject === 'string') { + if (nameOrObject in CustomRenderers) { + // @ts-ignore + return CustomRenderers[nameOrObject]; + } + for (const dyn of CustomRenderers._dynamic || []) { + const match = nameOrObject.match(dyn.pattern); + if (match) { + return dyn.handler(match); + } + } + return {}; + } + return nameOrObject; +} class LxRenderer { - renderers: Array; constructor(config: LxConfigObject) { - this.renderers = []; config.headers && config.headers.map((headers) => { for (const config of headers.columns) { if (!config.renderer) continue; - if (!Array.isArray(config.renderer)) - config.renderer = Array(config.renderer); - - const multiRender = config.renderer.map((renderer) => { - if (typeof renderer === 'string') { - if (renderer.startsWith('DATE_TO_FORMAT_')) { - return (data: any) => { - const format = renderer.split('_').pop(); - return dayjs(data).format(format); - }; - } - - switch (renderer) { - case 'DATE_DAY': - return (data: any) => dayjs(data).format('DD/MM/YYYY'); - case 'DATE': - return (data: any) => dayjs(data).format('DD/MM/YYYY HH:mm'); - case 'DATE_WITH_SECOND': - return (data: any) => - dayjs(data).format('DD/MM/YYYY HH:mm:ss'); - case 'LOCAL_NUMBER': - return (data: any) => data.toLocaleString(); - case 'BOOLEAN_YESNO': - return this.booleanString; - case 'NUMBER_FIXED_2': - return this.numberToFixed; - case 'NUMBER_2_DIGIT_MAX': - return this.number2DigitMax; - case 'CUT_LONG_TEXT': - return this.cutLongText; - case 'PARSE_INT': - return (data: any) => parseInt(data); - case 'CHECKBOX': - // @ts-ignore - config.checkboxes = { - selectRow: true, - }; - break; - case 'UPPERCASE': - return this.uppercase; - } - } - }); - - // @ts-ignore - config.render = $.fn.dataTable.render.multi(multiRender); + if (Array.isArray(config.renderer)) { + console.warn( + 'Multiple renderers are not supported yet. Please use a single renderer.' + ); + config.renderer = config.renderer[0]; + } + const renderConf = resolveRenderer(config.renderer); + if (renderConf.render) { + config.render = renderConf.render; + } + if (renderConf.createdCell) { + config.createdCell = renderConf.createdCell; + } + if (renderConf.checkboxes) { + // @ts-ignore + config.checkboxes = renderConf.checkboxes; + } } }); } - - number2DigitMax(data: any | null) { - if (typeof data === 'number') { - if (data % 1 === 0) return data; - else return data.toFixed(2); - } else if (typeof data === 'string') { - const float = parseFloat(data); - if (isNaN(float)) return data; - if (float % 1 === 0) return data; - else return float.toFixed(2); - } else { - return data; - } - } - - booleanString = (data: boolean) => (data === true ? 'Oui' : 'Non'); - - cutLongText = (data: any) => { - if (data.length < 30) return data; - var esc = function (t: any) { - return t - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"'); - }; - - const shortened = esc(data).substr(0, 30); - const d = esc(data); - return ( - '' + - shortened + - '…' - ); - }; - - uppercase = (data: any) => - typeof data === 'string' ? data.toUpperCase() : data; - - numberToFixed = (data: any) => { - if (typeof data === 'number') { - return data.toFixed(2); - } else if (typeof data === 'string') { - const float = parseFloat(data); - if (isNaN(float)) return data; - else return float.toFixed(2); - } - }; } export default LxRenderer; diff --git a/lib/dto/Renderer.ts.bck b/lib/dto/Renderer.ts.bck new file mode 100644 index 0000000..0f53637 --- /dev/null +++ b/lib/dto/Renderer.ts.bck @@ -0,0 +1,136 @@ +import { LxConfigObject } from '@core/LyxeaDatatable'; +import '../utils/fixToFixed'; +import $ from 'jquery'; +import dayjs from 'dayjs'; + +export type RendederConfig = {}; + +type MultiRender = Array<(d: T, type: any, row: any, meta: any) => T>; + +/** Multi render function to handle array of modifiers */ +// original idea : https://datatables.net/forums/discussion/43160/multi-render-renderer +// @ts-ignore +$.fn.dataTable.render.multi = + (renderArray: MultiRender) => + (d: T, type: any, row: any, meta: any) => { + if (type !== 'display') { + // pour tri, filtrage, etc. => retourne valeur brute + return d; + } + + renderArray.forEach((render) => { + if (render) d = render(d, type, row, meta); + }); + return d; + }; + +class LxRenderer { + renderers: Array; + constructor(config: LxConfigObject) { + this.renderers = []; + config.headers && + config.headers.map((headers) => { + for (const config of headers.columns) { + if (!config.renderer) continue; + if (!Array.isArray(config.renderer)) + config.renderer = Array(config.renderer); + + const multiRender = config.renderer.map((renderer) => { + if (typeof renderer === 'string') { + if (renderer.startsWith('DATE_TO_FORMAT_')) { + return (data: any) => { + const format = renderer.split('_').pop(); + return dayjs(data).format(format); + }; + } + + switch (renderer) { + case 'DATE_DAY': + return (data: any) => dayjs(data).format('DD/MM/YYYY'); + case 'DATE': + return (data: any) => dayjs(data).format('DD/MM/YYYY HH:mm'); + case 'DATE_WITH_SECOND': + return (data: any) => + dayjs(data).format('DD/MM/YYYY HH:mm:ss'); + case 'LOCAL_NUMBER': + return (data: any) => data.toLocaleString(); + case 'BOOLEAN_YESNO': + return this.booleanString; + case 'NUMBER_FIXED_2': + return this.numberToFixed; + case 'NUMBER_2_DIGIT_MAX': + return this.number2DigitMax; + case 'CUT_LONG_TEXT': + return this.cutLongText; + case 'PARSE_INT': + return (data: any) => parseInt(data); + case 'CHECKBOX': + // @ts-ignore + config.checkboxes = { + selectRow: true, + }; + break; + case 'UPPERCASE': + return this.uppercase; + } + } + }); + + // @ts-ignore + config.render = $.fn.dataTable.render.multi(multiRender); + } + }); + } + + number2DigitMax(data: any | null) { + if (typeof data === 'number') { + if (data % 1 === 0) return data; + else return data.toFixed(2); + } else if (typeof data === 'string') { + const float = parseFloat(data); + if (isNaN(float)) return data; + if (float % 1 === 0) return data; + else return float.toFixed(2); + } else { + return data; + } + } + + booleanString = (data: boolean) => (data === true ? 'Oui' : 'Non'); + + cutLongText = (data: any) => { + if (data.length < 30) return data; + var esc = function (t: any) { + return t + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + + const shortened = esc(data).substr(0, 30); + const d = esc(data); + return ( + '' + + shortened + + '…' + ); + }; + + uppercase = (data: any) => + typeof data === 'string' ? data.toUpperCase() : data; + + numberToFixed = (data: any) => { + if (typeof data === 'number') { + return data.toFixed(2); + } else if (typeof data === 'string') { + const float = parseFloat(data); + if (isNaN(float)) return data; + else return float.toFixed(2); + } + }; +} + +export default LxRenderer; diff --git a/lib/plugins/action/Action.ts b/lib/plugins/action/Action.ts index d3b04cb..2705227 100644 --- a/lib/plugins/action/Action.ts +++ b/lib/plugins/action/Action.ts @@ -111,7 +111,7 @@ class Action { if (action.onError) action.onError(err, rowData); } }); - this.#domBuilder.defineDefaultCellStyle(td as HTMLElement); + //this.#domBuilder.defineDefaultCellStyle(td as HTMLElement); if (this.#celllClassName) (td as HTMLElement).classList.add(this.#celllClassName); td.appendChild(btn); diff --git a/lib/plugins/action/dom/ActionLxDom.ts b/lib/plugins/action/dom/ActionLxDom.ts index 8f784cc..1f41270 100644 --- a/lib/plugins/action/dom/ActionLxDom.ts +++ b/lib/plugins/action/dom/ActionLxDom.ts @@ -34,7 +34,7 @@ class ActionLxDom extends AbstractLxDom { name: name, 'aria-label': label ?? name, }, - style: { ...defaultStyle, ...style } ?? defaultStyle, + style: { ...defaultStyle, ...style }, onClick: Array.isArray(effect) ? effect : [effect], }); } diff --git a/package.json b/package.json index c41d3ee..37976f1 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "scripts": { "dev": "vite", "build": "tsc && vite build", + "watch": "tsc && vite build --watch", "preview": "vite preview", "test": "export NODE_ENV=test && jest", "coverage": "jest --coverage",