378 lines
9.9 KiB
TypeScript
378 lines
9.9 KiB
TypeScript
import { CompactSelection } from '@glideapps/glide-data-grid';
|
|
import { useDebouncedCallback } from '@mantine/hooks';
|
|
import React, { useEffect, useRef } from 'react';
|
|
|
|
import { useGridlerStore } from './GridlerStore';
|
|
|
|
//The computer component does not need to be recalculated on every render, so we use React.memo to prevent unnecessary re-renders.
|
|
export const Computer = React.memo(() => {
|
|
const refFirstRun = useRef(0);
|
|
const refLastSearch = useRef('');
|
|
const refLastFilters = useRef<unknown>(null);
|
|
const {
|
|
_glideref,
|
|
_gridSelectionRows,
|
|
colFilters,
|
|
colOrder,
|
|
colSize,
|
|
colSort,
|
|
columns,
|
|
getRowIndexByKey,
|
|
getState,
|
|
loadPage,
|
|
ready,
|
|
|
|
scrollToRowKey,
|
|
searchStr,
|
|
selectedRowKey,
|
|
setState,
|
|
setStateFN,
|
|
values,
|
|
} = useGridlerStore((s) => ({
|
|
_glideref: s._glideref,
|
|
_gridSelectionRows: s._gridSelectionRows,
|
|
colFilters: s.colFilters,
|
|
colOrder: s.colOrder,
|
|
colSize: s.colSize,
|
|
colSort: s.colSort,
|
|
columns: s.columns,
|
|
getRowIndexByKey: s.getRowIndexByKey,
|
|
getState: s.getState,
|
|
loadPage: s.loadPage,
|
|
ready: s.ready,
|
|
|
|
scrollToRowKey: s.scrollToRowKey,
|
|
searchStr: s.searchStr,
|
|
selectedRowKey: s.selectedRowKey,
|
|
setState: s.setState,
|
|
setStateFN: s.setStateFN,
|
|
uniqueid: s.uniqueid,
|
|
values: s.values,
|
|
}));
|
|
|
|
const debouncedDoSearch = useDebouncedCallback(
|
|
(searchStr: string) => {
|
|
loadPage(0, 'all').then(() => {
|
|
getState('refreshCells')?.();
|
|
getState('_events')?.dispatchEvent?.(
|
|
new CustomEvent('onSearched', {
|
|
detail: { search: searchStr },
|
|
})
|
|
);
|
|
});
|
|
},
|
|
{
|
|
delay: 300,
|
|
leading: false,
|
|
}
|
|
);
|
|
|
|
//When values change, update selection
|
|
useEffect(() => {
|
|
const searchSelection = async () => {
|
|
const page_data = getState('_page_data');
|
|
const pageSize = getState('pageSize');
|
|
const keyField = getState('keyField') ?? 'id';
|
|
const rowIndexes = [];
|
|
for (const vi in values as Array<Record<string, unknown>>) {
|
|
let rowIndex = -1;
|
|
const key = String(
|
|
typeof values?.[vi] === 'object'
|
|
? values?.[vi]?.[keyField]
|
|
: typeof values?.[vi] === 'string'
|
|
? values?.[vi]
|
|
: undefined
|
|
);
|
|
for (const p in page_data) {
|
|
for (const r in page_data[p]) {
|
|
const idx = Number(p) * pageSize + Number(r);
|
|
|
|
if (String(page_data[p][r]?.[keyField]) === key) {
|
|
//console.log('Found row S', idx, page_data[p][r], page_data[p][r]?.[keyField], key);
|
|
rowIndex = idx;
|
|
break;
|
|
}
|
|
}
|
|
if (rowIndex >= 0) {
|
|
rowIndexes.push(rowIndex);
|
|
break;
|
|
}
|
|
}
|
|
if (!(rowIndex >= 0)) {
|
|
const idx = await getRowIndexByKey(key);
|
|
if (idx) {
|
|
rowIndexes.push(idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
return rowIndexes;
|
|
};
|
|
|
|
if (values) {
|
|
searchSelection().then((rowIndexes) => {
|
|
let rows = CompactSelection.empty();
|
|
rowIndexes.forEach((r) => {
|
|
rows = rows.add(r);
|
|
});
|
|
|
|
setStateFN('_gridSelectionRows', () => {
|
|
return rows;
|
|
});
|
|
|
|
setStateFN('_gridSelection', (c) => ({
|
|
columns: c?.columns ?? CompactSelection.empty(),
|
|
...c,
|
|
rows,
|
|
}));
|
|
});
|
|
}
|
|
}, [values]);
|
|
|
|
//Fire onChange when selection changes
|
|
useEffect(() => {
|
|
const onChange = getState('onChange');
|
|
if (onChange && typeof onChange === 'function') {
|
|
const getGridSelectedRows = getState('getGridSelectedRows');
|
|
const buffers = getGridSelectedRows();
|
|
|
|
const _values = getState('values');
|
|
|
|
if (JSON.stringify(_values) !== JSON.stringify(buffers)) {
|
|
onChange(buffers);
|
|
}
|
|
}
|
|
}, [JSON.stringify(_gridSelectionRows), getState]);
|
|
|
|
useEffect(() => {
|
|
setState(
|
|
'renderColumns',
|
|
columns?.map((c) => ({
|
|
...c,
|
|
hasMenu: c?.hasMenu ?? true,
|
|
icon: 'sort',
|
|
}))
|
|
);
|
|
}, [columns]);
|
|
|
|
useEffect(() => {
|
|
if (searchStr === undefined || searchStr === null) {
|
|
refLastSearch.current = '';
|
|
return;
|
|
}
|
|
if (refLastSearch.current !== searchStr) {
|
|
debouncedDoSearch(searchStr);
|
|
refLastSearch.current = searchStr;
|
|
}
|
|
}, [searchStr]);
|
|
|
|
useEffect(() => {
|
|
if (!colSort) {
|
|
return;
|
|
}
|
|
|
|
setState('_gridSelection', {
|
|
columns: CompactSelection.empty(),
|
|
current: undefined,
|
|
rows: CompactSelection.empty(),
|
|
});
|
|
|
|
setState('_gridSelectionRows', CompactSelection.empty());
|
|
setStateFN('renderColumns', (cols) => {
|
|
return cols?.map((c) => ({
|
|
...c,
|
|
icon:
|
|
c.id && colSort?.find((col) => col.id === c.id)?.direction
|
|
? colSort?.find((col) => col.id === c.id)?.direction === 'asc'
|
|
? 'sortup'
|
|
: 'sortdown'
|
|
: (c.defaultIcon ?? 'sort'),
|
|
}));
|
|
}).then(() => {
|
|
loadPage(0, 'all').then(() => {
|
|
getState('refreshCells')?.();
|
|
getState('_events')?.dispatchEvent?.(
|
|
new CustomEvent('onColumnSorted', {
|
|
detail: { cols: colSort },
|
|
})
|
|
);
|
|
});
|
|
});
|
|
}, [colSort]);
|
|
|
|
useEffect(() => {
|
|
if (!colFilters) {
|
|
return;
|
|
}
|
|
|
|
if (JSON.stringify(refLastFilters.current) !== JSON.stringify(colFilters)) {
|
|
loadPage(0, 'all').then(() => {
|
|
getState('refreshCells')?.();
|
|
getState('_events')?.dispatchEvent?.(
|
|
new CustomEvent('onColumnFiltered', {
|
|
detail: { filters: colFilters },
|
|
})
|
|
);
|
|
});
|
|
refLastFilters.current = colFilters;
|
|
}
|
|
}, [colFilters]);
|
|
|
|
useEffect(() => {
|
|
if (!colSize) {
|
|
return;
|
|
}
|
|
setStateFN('renderColumns', (cols) => {
|
|
return cols?.map((c) => ({
|
|
...c,
|
|
width: c.id && colSize?.[c.id] ? colSize?.[c.id] : c.width,
|
|
}));
|
|
}).then(() => {
|
|
getState('refreshCells')?.();
|
|
});
|
|
}, [colSize]);
|
|
|
|
useEffect(() => {
|
|
if (!colOrder) {
|
|
return;
|
|
}
|
|
|
|
setStateFN('renderColumns', (cols) => {
|
|
const result = cols?.sort((a, b) => {
|
|
if (colOrder[a.id] > colOrder[b.id]) {
|
|
return 1;
|
|
}
|
|
return -1;
|
|
});
|
|
|
|
return result;
|
|
}).then(() => {
|
|
getState('refreshCells')?.();
|
|
});
|
|
}, [colOrder]);
|
|
|
|
//Initial Load
|
|
useEffect(() => {
|
|
if (!_glideref) {
|
|
return;
|
|
}
|
|
if (refFirstRun.current > 0) {
|
|
return;
|
|
}
|
|
refFirstRun.current = 1;
|
|
loadPage(0).then(() => {
|
|
getState('refreshCells')?.();
|
|
});
|
|
}, [ready, loadPage]);
|
|
|
|
//Logic to select first row on mount
|
|
useEffect(() => {
|
|
const _events = getState('_events');
|
|
const loadPage = () => {
|
|
const selectFirstRowOnMount = getState('selectFirstRowOnMount');
|
|
if (ready && selectFirstRowOnMount) {
|
|
const scrollToRowKey = getState('scrollToRowKey');
|
|
if (scrollToRowKey && scrollToRowKey >= 0) {
|
|
return;
|
|
}
|
|
const keyField = getState('keyField') ?? 'id';
|
|
const page_data = getState('_page_data');
|
|
|
|
const firstBuffer = page_data?.[0]?.[0];
|
|
const firstRow = firstBuffer?.[keyField];
|
|
const currentValues = getState('values') ?? [];
|
|
|
|
if (firstRow && firstRow > 0 && (currentValues.length ?? 0) === 0) {
|
|
const values = [firstBuffer, ...(currentValues as Array<Record<string, unknown>>)];
|
|
|
|
const onChange = getState('onChange');
|
|
//console.log('Selecting first row:', firstRow, firstBuffer, values);
|
|
if (onChange) {
|
|
onChange(values);
|
|
} else {
|
|
setState('values', values);
|
|
}
|
|
|
|
setState('scrollToRowKey', firstRow);
|
|
}
|
|
}
|
|
};
|
|
|
|
_events?.addEventListener('loadPage', loadPage);
|
|
|
|
return () => {
|
|
_events?.removeEventListener('loadPage', loadPage);
|
|
};
|
|
}, [ready]);
|
|
|
|
/// logic to apply the selected row.
|
|
// useEffect(() => {
|
|
// const ready = getState('ready');
|
|
// const ref = getState('_glideref');
|
|
// const getRowIndexByKey = getState('getRowIndexByKey');
|
|
|
|
// if (scrollToRowKey && ref && ready) {
|
|
// getRowIndexByKey?.(scrollToRowKey).then((r) => {
|
|
// if (r !== undefined) {
|
|
// //console.log('Scrolling to selected row:', scrollToRowKey, r);
|
|
// ref.scrollTo(0, r);
|
|
// getState('_events').dispatchEvent(
|
|
// new CustomEvent('scrollToRowKeyFound', {
|
|
// detail: { rowNumber: r, scrollToRowKey: scrollToRowKey },
|
|
// })
|
|
// );
|
|
// }
|
|
// });
|
|
// }
|
|
// }, [scrollToRowKey]);
|
|
|
|
useEffect(() => {
|
|
const ready = getState('ready');
|
|
const ref = getState('_glideref');
|
|
const getRowIndexByKey = getState('getRowIndexByKey');
|
|
const key = selectedRowKey ?? scrollToRowKey;
|
|
|
|
if (key && ref && ready) {
|
|
getRowIndexByKey?.(key).then((r) => {
|
|
if (r !== undefined) {
|
|
//console.log('Scrolling to selected row:', r, selectedRowKey, scrollToRowKey);
|
|
|
|
if (selectedRowKey) {
|
|
const onChange = getState('onChange');
|
|
const selected = [{ [getState('keyField') ?? 'id']: selectedRowKey }];
|
|
if (onChange) {
|
|
onChange(selected);
|
|
} else {
|
|
setState('values', selected);
|
|
}
|
|
}
|
|
|
|
ref.scrollTo(0, r);
|
|
getState('_events').dispatchEvent(
|
|
new CustomEvent('scrollToRowKeyFound', {
|
|
detail: {
|
|
rowNumber: r,
|
|
scrollToRowKey: scrollToRowKey,
|
|
selectedRowKey: selectedRowKey,
|
|
},
|
|
})
|
|
);
|
|
}
|
|
});
|
|
}
|
|
}, [scrollToRowKey, selectedRowKey]);
|
|
|
|
// console.log('Gridler:Debug:Computer', {
|
|
// colFilters,
|
|
// colOrder,
|
|
// colSize,
|
|
// colSort,
|
|
// columns,
|
|
// uniqueid
|
|
// });
|
|
|
|
return <></>;
|
|
});
|
|
|
|
Computer.displayName = 'Gridler-Computer';
|