oranguru/src/Gridler/components/Computer.tsx

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';