UNPKG

datatables.net-react

Version:

React component for DataTables

229 lines (224 loc) 7.13 kB
'use strict'; var jsxRuntime = require('react/jsx-runtime'); var client = require('react-dom/client'); var react = require('react'); /* * List of events that DataTables can fire, so they can re-exported to React */ var dtEvents = [ 'autoFill', 'preAutoFill', 'buttons-action', 'buttons-processing', 'column-reorder', 'columns-reordered', 'childRow', 'column-sizing', 'column-visibility', 'destroy', 'draw', 'dt-error', 'info', 'init', 'length', 'options', 'order', 'page', 'preDraw', 'preInit', 'preXhr', 'processing', 'requestChild', 'search', 'stateLoaded', 'stateLoadParams', 'stateSaveParams', 'xhr', 'key', 'key-blur', 'key-focus', 'key-prefocus', 'key-refocus', 'key-return-submit', 'responsive-display', 'responsive-resize', 'rowgroup-datasrc', 'pre-row-reorder', 'row-reorder', 'row-reorder-canceled', 'row-reorder-changed', 'row-reordered', 'dtsb-inserted', 'deselect', 'select', 'select-blur', 'selectItems', 'selectStyle', 'user-select', 'stateRestore-change', ]; let DataTablesLib = null; // any here, so we can assign the `use` later - it is really a DataTableComponent though const Component = react.forwardRef(function DataTable(props, ref) { const tableEl = react.useRef(null); const table = react.useRef(null); const options = react.useRef(props.options ?? {}); const cache = react.useRef([]); // Expose the DataTables API via a reference react.useImperativeHandle(ref, () => ({ dt: () => table.current })); // Expose some of the more common settings as props if (props.data) { options.current.data = props.data; } if (props.ajax) { options.current.ajax = props.ajax; } if (props.columns) { options.current.columns = props.columns; } // If slots are defined, create `columnDefs` entries for them to apply // to their target columns. if (props.slots) { applySlots(cache.current, options.current, props.slots); } // Create the DataTable when the `<table>` is ready in the document react.useEffect(() => { if (!DataTablesLib) { throw new Error('DataTables library not set. See https://datatables.net/tn/23 for details.'); } if (tableEl.current) { const $ = DataTablesLib.use('jq'); const table$ = $(tableEl.current); // Bind to DataTable's events so they can be listened to with an `on` property dtEvents.forEach((name) => { // Create the `on*` name from the DataTables event name, which is camelCase // and an `on` prefix. const onName = 'on' + name[0].toUpperCase() + name.slice(1).replace(/-[a-z]/g, (match) => match[1].toUpperCase()); if (props[onName]) { table$.on(name + '.dt', props[onName]); } }); // Initialise the DataTable table.current = new DataTablesLib(tableEl.current, options.current); } // Unmount tidy up return () => { if (table.current) { // Unmount the created roots when this component unmounts let roots = cache.current.slice(); cache.current.length = 0; setTimeout(() => { roots.forEach((r) => { r.unmount(); }); }, 250); table.current.destroy(); table.current = null; } }; }, []); // On data change, clear and redraw react.useEffect(() => { if (props.data) { if (table.current) { table.current.clear(); table.current.rows.add(props.data).draw(false); } } }, [props.data]); return (jsxRuntime.jsx("div", { children: jsxRuntime.jsx("table", { ref: tableEl, className: props.className ?? '', id: props.id ?? '', children: props.children ?? null }) })); }); Component.use = function (lib) { DataTablesLib = lib; }; const Exporter = Component; /** * Loop over the slots defined and apply them to their columns, * targeting based on the slot name (object key). * * @param options DataTables configuration object * @param slots Props passed in */ function applySlots(cache, options, slots) { if (!options.columnDefs) { options.columnDefs = []; } Object.keys(slots).forEach((name) => { let slot = slots[name]; if (!slot) { return; } // Simple column index if (name.match(/^-?\d+$/)) { // Note that unshift is used to make sure that this property is // applied in DataTables _after_ the end user's own options, if // they've provided any. options.columnDefs.unshift({ target: parseInt(name), render: slotRenderer(cache, slot) }); } else { // Column name options.columnDefs.unshift({ target: name + ':name', render: slotRenderer(cache, slot) }); } }); } /** * Create a rendering function that will create a React component * for a cell's rendering function. * * @param slot Function to create react component or orthogonal data * @returns Rendering function */ function slotRenderer(cache, slot) { return function (data, type, row, meta) { if (slot.length === 4) { let result = slot(data, type, row, meta); return result['$$typeof'] ? renderJsx(cache, result) : result; } else if (slot.length === 3) { // The function takes three parameters so it allows for // orthogonal data - not possible to cache the response let result = slot(data, type, row, meta); return result['$$typeof'] ? renderJsx(cache, result) : result; } // Otherwise, we are expecting a JSX return from the function every // time and we can cache it. Note the `slot as any` - Typescript // doesn't appear to like the two argument option for `DataTableSlot`. return slotCache(cache, () => slot(data, row)); }; } /** * Render a slot's element and cache it */ function slotCache(cache, create) { // Execute the rendering function let result = create(); // If the result is a JSX element, we need to render and then cache it if (result['$$typeof']) { let div = renderJsx(cache, result); return div; } // Any other data just gets returned return result; } /** * Render JSX into a div which can be shown in a cell */ function renderJsx(cache, jsx) { let div = document.createElement('div'); let root = client.createRoot(div); root.render(jsx); cache.push(root); return div; } module.exports = Exporter; //# sourceMappingURL=index.js.map