/* * dmtools front-end entrypoint (Symfony AssetMapper / importmap). * Loaded from base.html.twig via {{ importmap('app') }}. */ import './stimulus_bootstrap.js'; import 'bootstrap/dist/css/bootstrap.min.css'; import 'datatables.net-bs5/css/dataTables.bootstrap5.min.css'; import './styles/app.css'; import * as bootstrap from 'bootstrap'; import DataTable from 'datatables.net-bs5'; window.bootstrap = bootstrap; const esc = (v) => { if (v === null || v === undefined) return ''; return String(v).replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', }[c])); }; // Build a DataTables column definition from a in the table head. // data-data key in the row object (required for server-side tables) // data-orderable "false" to disable sorting on the column // data-format "link" | "truncate" | (default: plain escaped text) // data-row-url (on the ) URL template with __ID__ for "link" columns // NB: "format" not "render" - DataTables itself consumes a data-render attribute. function columnFromTh(th, rowUrl) { const key = th.dataset.data; const col = { data: key || null, orderable: th.dataset.orderable !== 'false' }; if (th.dataset.format === 'link' && rowUrl) { col.render = (val, type, row) => { if (type !== 'display') return val ?? ''; return `${esc(val)}`; }; } else if (th.dataset.format === 'truncate') { col.render = (val, type) => { if (type !== 'display') return val ?? ''; const s = val ? String(val) : ''; const short = s.length > 70 ? s.slice(0, 70) + '…' : s; return `${esc(short)}`; }; } else { col.render = (val, type) => (type === 'display' ? esc(val) : (val ?? '')); } return col; } function initDataTables(root = document) { root.querySelectorAll('table.datatable').forEach((table) => { if (table.dataset.datatable === 'false' || DataTable.isDataTable(table)) { return; } let options = { pageLength: 10, order: [], responsive: false }; if (table.dataset.datatableOptions) { try { options = { ...options, ...JSON.parse(table.dataset.datatableOptions) }; } catch (e) { console.error('Invalid data-datatable-options on', table, e); } } // Server-side mode: the backend does the paging, sorting and searching. if (table.dataset.datatableSrc) { const ths = [...table.querySelectorAll('thead th')]; options = { ...options, serverSide: true, processing: true, ajax: { url: table.dataset.datatableSrc }, columns: ths.map((th) => columnFromTh(th, table.dataset.rowUrl)), order: options.order.length ? options.order : [[0, 'asc']], }; } new DataTable(table, options); }); } document.addEventListener('DOMContentLoaded', () => initDataTables()); document.addEventListener('turbo:load', () => initDataTables());