@vaadin/hilla-react-crud
Version:
Hilla CRUD utils for React
200 lines • 6.74 kB
JavaScript
import { useMemo, useState } from 'react';
import Direction from './types/org/springframework/data/domain/Sort/Direction.js';
import NullHandling from './types/org/springframework/data/domain/Sort/NullHandling.js';
function createSort(params) {
return {
orders: params.sortOrders
.filter((order) => order.direction != null)
.map((order) => ({
property: order.path,
direction: order.direction === 'asc' ? Direction.ASC : Direction.DESC,
ignoreCase: false,
nullHandling: NullHandling.NATIVE,
})),
};
}
export function isCountService(service) {
return !!service.count;
}
export class DataProvider {
service;
loadTotalCount;
afterLoadCallback;
filter;
totalCount;
filteredCount;
constructor(service, options = {}) {
this.service = service;
this.filter = options.initialFilter;
this.loadTotalCount = options.loadTotalCount;
this.afterLoadCallback = options.afterLoad;
this.load = this.load.bind(this);
}
reset() {
this.totalCount = undefined;
this.filteredCount = undefined;
}
setFilter(filter) {
this.reset();
this.filter = filter;
}
async load(params, callback) {
const page = await this.fetchPage(params);
this.filteredCount = await this.fetchFilteredCount(page);
if (this.loadTotalCount) {
this.totalCount = await this.fetchTotalCount(page);
}
callback(page.items, this.filteredCount);
if (this.afterLoadCallback) {
this.afterLoadCallback({
totalCount: this.totalCount,
filteredCount: this.filteredCount,
});
}
}
async fetchPage(params) {
const sort = createSort(params);
const pageNumber = params.page;
const { pageSize } = params;
const pageRequest = {
pageNumber,
pageSize,
sort,
};
const items = await this.service.list(pageRequest, this.filter);
return { items, pageRequest };
}
}
export class AbstractComboBoxDataProvider {
list;
loadTotalCount;
sort;
totalCount;
filteredCount;
constructor(list, sort) {
this.list = list;
this.sort = sort;
}
reset() {
this.totalCount = undefined;
this.filteredCount = undefined;
}
load(params, callback) {
this.fetchPage(params)
.then(async (page) => {
this.filteredCount = await this.fetchFilteredCount(page);
if (this.loadTotalCount) {
this.totalCount = await this.fetchTotalCount(page);
}
callback(page.items, this.filteredCount);
})
.catch((error) => {
throw error;
});
}
async fetchPage(params) {
const pageNumber = params.page;
const { pageSize } = params;
const pageRequest = {
pageNumber,
pageSize,
sort: this.sort ?? { orders: [] },
};
const items = await this.list(pageRequest, params.filter);
return { items, pageRequest };
}
}
function determineInfiniteScrollingSize(page, lastKnownSize) {
const { items, pageRequest } = page;
const { pageNumber, pageSize } = pageRequest;
let infiniteScrollingSize;
if (items.length === pageSize) {
infiniteScrollingSize = (pageNumber + 1) * pageSize + 1;
if (lastKnownSize !== undefined && infiniteScrollingSize < lastKnownSize) {
infiniteScrollingSize = lastKnownSize;
}
}
else {
infiniteScrollingSize = pageNumber * pageSize + items.length;
}
return infiniteScrollingSize;
}
export class InfiniteDataProvider extends DataProvider {
fetchTotalCount() {
return undefined;
}
fetchFilteredCount(page) {
return determineInfiniteScrollingSize(page, this.filteredCount);
}
}
export class InfiniteComboBoxDataProvider extends AbstractComboBoxDataProvider {
fetchTotalCount() {
return undefined;
}
fetchFilteredCount(page) {
return determineInfiniteScrollingSize(page, this.filteredCount);
}
}
export class FixedSizeDataProvider extends DataProvider {
constructor(service, options = {}) {
if (!isCountService(service)) {
throw new Error('The provided service does not implement the CountService interface.');
}
super(service, options);
}
async fetchTotalCount() {
if (this.totalCount !== undefined) {
return this.totalCount;
}
return this.service.count(undefined);
}
async fetchFilteredCount() {
if (this.filteredCount !== undefined) {
return this.filteredCount;
}
return this.service.count(this.filter);
}
}
export function createDataProvider(service, options = {}) {
if (isCountService(service)) {
return new FixedSizeDataProvider(service, options);
}
return new InfiniteDataProvider(service, options);
}
export function useDataProvider(service, filter) {
const [refreshCounter, setRefreshCounter] = useState(0);
const dataProvider = useMemo(() => createDataProvider(service, { initialFilter: filter }), [service]);
dataProvider.setFilter(filter);
const dataProviderFn = useMemo(() => dataProvider.load.bind(dataProvider), [dataProvider, filter, refreshCounter]);
return {
dataProvider: dataProviderFn,
refresh: () => {
dataProvider.reset();
setRefreshCounter(refreshCounter + 1);
},
};
}
export function useGridDataProvider(fetch, dependencies) {
const result = useDataProvider(useMemo(() => ({
list: async (pageable) => fetch(pageable),
}), dependencies ?? []));
const dataProvider = result.dataProvider;
dataProvider.refresh = result.refresh;
return dataProvider;
}
function createComboBoxDataProvider(list, sort) {
return new InfiniteComboBoxDataProvider(list, sort);
}
export function useComboBoxDataProvider(fetch, options, dependencies) {
const [refreshCounter, setRefreshCounter] = useState(0);
const dataProvider = useMemo(() => createComboBoxDataProvider(fetch, options?.sort), [options?.sort, ...(dependencies ?? [])]);
return useMemo(() => {
const dataProviderWithRefresh = (...args) => dataProvider.load(...args);
dataProviderWithRefresh.refresh = () => {
dataProvider.reset();
setRefreshCounter(refreshCounter + 1);
};
return dataProviderWithRefresh;
}, [dataProvider, refreshCounter]);
}
//# sourceMappingURL=data-provider.js.map