tinybase
Version:
A reactive data store and sync engine.
1,238 lines (1,213 loc) • 32.6 kB
JavaScript
import React from 'react';
import {Fragment, jsx, jsxs} from 'react/jsx-runtime';
import {CellView, ResultCellView, ValueView} from '../ui-react/index.js';
const getTypeOf = (thing) => typeof thing;
const TINYBASE = 'tinybase';
const EMPTY_STRING = '';
const DOT = '.';
const STRING = getTypeOf(EMPTY_STRING);
const BOOLEAN = getTypeOf(true);
const NUMBER = getTypeOf(0);
const FUNCTION = getTypeOf(getTypeOf);
const LISTENER = 'Listener';
const RESULT = 'Result';
const GET = 'get';
const SET = 'set';
const ADD = 'add';
const HAS = 'Has';
const _HAS = 'has';
const IDS = 'Ids';
const TABLE = 'Table';
const ROW = 'Row';
const ROW_COUNT = ROW + 'Count';
const ROW_IDS = ROW + IDS;
const SORTED_ROW_IDS = 'Sorted' + ROW + IDS;
const CELL = 'Cell';
const CELL_IDS = CELL + IDS;
const VALUE = 'Value';
const VALUE_IDS = VALUE + IDS;
const SLICE = 'Slice';
const REMOTE_ROW_ID = 'Remote' + ROW + 'Id';
const CURRENT_TARGET = 'currentTarget';
const _VALUE = 'value';
const EXTRA = 'extra';
const strSplit = (str, separator = EMPTY_STRING, limit) =>
str.split(separator, limit);
const GLOBAL = globalThis;
const math = Math;
const mathMin = math.min;
const isFiniteNumber = isFinite;
const isUndefined = (thing) => thing == void 0;
const ifNotUndefined = (value, then, otherwise) =>
isUndefined(value) ? otherwise?.() : then(value);
const isTypeStringOrBoolean = (type) => type == STRING || type == BOOLEAN;
const isString = (thing) => getTypeOf(thing) == STRING;
const isFunction = (thing) => getTypeOf(thing) == FUNCTION;
const isArray = (thing) => Array.isArray(thing);
const size = (arrayOrString) => arrayOrString.length;
const getUndefined = () => void 0;
const arrayEvery = (array, cb) => array.every(cb);
const arrayIsEqual = (array1, array2) =>
size(array1) === size(array2) &&
arrayEvery(array1, (value1, index) => array2[index] === value1);
const arrayMap = (array, cb) => array.map(cb);
const arrayFilter = (array, cb) => array.filter(cb);
const object = Object;
const getPrototypeOf = (obj) => object.getPrototypeOf(obj);
const objEntries = object.entries;
const isObject = (obj) =>
!isUndefined(obj) &&
ifNotUndefined(
getPrototypeOf(obj),
(objPrototype) =>
objPrototype == object.prototype ||
isUndefined(getPrototypeOf(objPrototype)),
/* istanbul ignore next */
() => true,
);
const objIds = object.keys;
const objNew = (entries = []) => object.fromEntries(entries);
const objGet = (obj, id) => ifNotUndefined(obj, (obj2) => obj2[id]);
const objToArray = (obj, cb) =>
arrayMap(objEntries(obj), ([id, value]) => cb(value, id));
const objMap = (obj, cb) =>
objNew(objToArray(obj, (value, id) => [id, cb(value, id)]));
const objSize = (obj) => size(objIds(obj));
const objIsEqual = (obj1, obj2) => {
const entries1 = objEntries(obj1);
return (
size(entries1) === objSize(obj2) &&
arrayEvery(entries1, ([index, value1]) =>
isObject(value1)
? isObject(obj2[index])
? objIsEqual(obj2[index], value1)
: false
: obj2[index] === value1,
)
);
};
const {
PureComponent,
createContext,
useCallback,
useContext,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState,
useSyncExternalStore,
} = React;
const getProps = (getProps2, ...ids) =>
isUndefined(getProps2) ? {} : getProps2(...ids);
const getRelationshipsStoreTableIds = (relationships, relationshipId) => [
relationships,
relationships?.getStore(),
relationships?.getLocalTableId(relationshipId),
relationships?.getRemoteTableId(relationshipId),
];
const getIndexStoreTableId = (indexes, indexId) => [
indexes,
indexes?.getStore(),
indexes?.getTableId(indexId),
];
const TINYBASE_CONTEXT = TINYBASE + '_uirc';
const Context = GLOBAL[TINYBASE_CONTEXT]
? /* istanbul ignore next */
GLOBAL[TINYBASE_CONTEXT]
: (GLOBAL[TINYBASE_CONTEXT] = createContext([]));
const useThing = (id, offset) => {
const contextValue = useContext(Context);
return isUndefined(id)
? contextValue[offset * 2]
: isString(id)
? objGet(contextValue[offset * 2 + 1], id)
: id;
};
const useThingOrThingById = (thingOrThingId, offset) => {
const thing = useThing(thingOrThingId, offset);
return isUndefined(thingOrThingId) || isString(thingOrThingId)
? thing
: thingOrThingId;
};
const OFFSET_STORE = 0;
const OFFSET_INDEXES = 2;
const OFFSET_RELATIONSHIPS = 3;
const OFFSET_QUERIES = 4;
const EMPTY_ARRAY = [];
const DEFAULTS = [{}, [], [EMPTY_ARRAY, void 0, EMPTY_ARRAY], void 0, false, 0];
const IS_EQUALS = [
objIsEqual,
arrayIsEqual,
(
[backwardIds1, currentId1, forwardIds1],
[backwardIds2, currentId2, forwardIds2],
) =>
currentId1 === currentId2 &&
arrayIsEqual(backwardIds1, backwardIds2) &&
arrayIsEqual(forwardIds1, forwardIds2),
];
const isEqual = (thing1, thing2) => thing1 === thing2;
const addAndDelListener = (thing, listenable, ...args) => {
const listenerId = thing?.[ADD + listenable + LISTENER]?.(...args);
return () => thing?.delListener?.(listenerId);
};
const useListenable = (listenable, thing, returnType, args = EMPTY_ARRAY) => {
const lastResult = useRef(DEFAULTS[returnType]);
const getResult = useCallback(
() => {
const nextResult =
thing?.[(returnType == 4 /* Boolean */ ? _HAS : GET) + listenable]?.(
...args,
) ?? DEFAULTS[returnType];
return !(IS_EQUALS[returnType] ?? isEqual)(nextResult, lastResult.current)
? (lastResult.current = nextResult)
: lastResult.current;
},
/* eslint-disable-next-line react-hooks/exhaustive-deps */
[thing, returnType, listenable, ...args],
);
const subscribe = useCallback(
(listener) =>
addAndDelListener(
thing,
(returnType == 4 /* Boolean */ ? HAS : EMPTY_STRING) + listenable,
...args,
listener,
),
/* eslint-disable-next-line react-hooks/exhaustive-deps */
[thing, returnType, listenable, ...args],
);
return useSyncExternalStore(subscribe, getResult, getResult);
};
const useSetCallback = (
storeOrStoreId,
settable,
get,
getDeps = EMPTY_ARRAY,
then = getUndefined,
thenDeps = EMPTY_ARRAY,
...args
) => {
const store = useStoreOrStoreById(storeOrStoreId);
return useCallback(
(parameter) =>
ifNotUndefined(store, (store2) =>
ifNotUndefined(get(parameter, store2), (thing) =>
then(
store2[SET + settable](
...argsOrGetArgs(args, store2, parameter),
thing,
),
thing,
),
),
),
// eslint-disable-next-line react-hooks/exhaustive-deps
[store, settable, ...getDeps, ...thenDeps, ...nonFunctionDeps(args)],
);
};
const argsOrGetArgs = (args, store, parameter) =>
arrayMap(args, (arg) => (isFunction(arg) ? arg(parameter, store) : arg));
const nonFunctionDeps = (args) => arrayFilter(args, (arg) => !isFunction(arg));
const useSortedRowIdsImpl = (
tableId,
cellId,
descending,
offset,
limit,
storeOrStoreId,
) =>
useListenable(
SORTED_ROW_IDS,
useStoreOrStoreById(storeOrStoreId),
1 /* Array */,
[tableId, cellId, descending, offset, limit],
);
const useStoreOrStoreById = (storeOrStoreId) =>
useThingOrThingById(storeOrStoreId, OFFSET_STORE);
const useTableCellIds = (tableId, storeOrStoreId) =>
useListenable(
TABLE + CELL_IDS,
useStoreOrStoreById(storeOrStoreId),
1 /* Array */,
[tableId],
);
const useRowCount = (tableId, storeOrStoreId) =>
useListenable(
ROW_COUNT,
useStoreOrStoreById(storeOrStoreId),
5 /* Number */,
[tableId],
);
const useRowIds = (tableId, storeOrStoreId) =>
useListenable(ROW_IDS, useStoreOrStoreById(storeOrStoreId), 1 /* Array */, [
tableId,
]);
const useSortedRowIds = (
tableIdOrArgs,
cellIdOrStoreOrStoreId,
descending,
offset,
limit,
storeOrStoreId,
) =>
useSortedRowIdsImpl(
...(isObject(tableIdOrArgs)
? [
tableIdOrArgs.tableId,
tableIdOrArgs.cellId,
tableIdOrArgs.descending ?? false,
tableIdOrArgs.offset ?? 0,
tableIdOrArgs.limit,
cellIdOrStoreOrStoreId,
]
: [
tableIdOrArgs,
cellIdOrStoreOrStoreId,
descending,
offset,
limit,
storeOrStoreId,
]),
);
const useCell = (tableId, rowId, cellId, storeOrStoreId) =>
useListenable(
CELL,
useStoreOrStoreById(storeOrStoreId),
3 /* CellOrValue */,
[tableId, rowId, cellId],
);
const useValueIds = (storeOrStoreId) =>
useListenable(VALUE_IDS, useStoreOrStoreById(storeOrStoreId), 1 /* Array */);
const useValue = (valueId, storeOrStoreId) =>
useListenable(
VALUE,
useStoreOrStoreById(storeOrStoreId),
3 /* CellOrValue */,
[valueId],
);
const useSetCellCallback = (
tableId,
rowId,
cellId,
getCell,
getCellDeps,
storeOrStoreId,
then,
thenDeps,
) =>
useSetCallback(
storeOrStoreId,
CELL,
getCell,
getCellDeps,
then,
thenDeps,
tableId,
rowId,
cellId,
);
const useSetValueCallback = (
valueId,
getValue,
getValueDeps,
storeOrStoreId,
then,
thenDeps,
) =>
useSetCallback(
storeOrStoreId,
VALUE,
getValue,
getValueDeps,
then,
thenDeps,
valueId,
);
const useIndexesOrIndexesById = (indexesOrIndexesId) =>
useThingOrThingById(indexesOrIndexesId, OFFSET_INDEXES);
const useSliceRowIds = (indexId, sliceId, indexesOrIndexesId) =>
useListenable(
SLICE + ROW_IDS,
useIndexesOrIndexesById(indexesOrIndexesId),
1 /* Array */,
[indexId, sliceId],
);
const useRelationshipsOrRelationshipsById = (relationshipsOrRelationshipsId) =>
useThingOrThingById(relationshipsOrRelationshipsId, OFFSET_RELATIONSHIPS);
const useRemoteRowId = (
relationshipId,
localRowId,
relationshipsOrRelationshipsId,
) =>
useListenable(
REMOTE_ROW_ID,
useRelationshipsOrRelationshipsById(relationshipsOrRelationshipsId),
3 /* CellOrValue */,
[relationshipId, localRowId],
);
const useQueriesOrQueriesById = (queriesOrQueriesId) =>
useThingOrThingById(queriesOrQueriesId, OFFSET_QUERIES);
const useResultTableCellIds = (queryId, queriesOrQueriesId) =>
useListenable(
RESULT + TABLE + CELL_IDS,
useQueriesOrQueriesById(queriesOrQueriesId),
1 /* Array */,
[queryId],
);
const useResultRowCount = (queryId, queriesOrQueriesId) =>
useListenable(
RESULT + ROW_COUNT,
useQueriesOrQueriesById(queriesOrQueriesId),
5 /* Number */,
[queryId],
);
const useResultRowIds = (queryId, queriesOrQueriesId) =>
useListenable(
RESULT + ROW_IDS,
useQueriesOrQueriesById(queriesOrQueriesId),
1 /* Array */,
[queryId],
);
const useResultSortedRowIds = (
queryId,
cellId,
descending,
offset = 0,
limit,
queriesOrQueriesId,
) =>
useListenable(
RESULT + SORTED_ROW_IDS,
useQueriesOrQueriesById(queriesOrQueriesId),
1 /* Array */,
[queryId, cellId, descending, offset, limit],
);
const getCellOrValueType = (cellOrValue) => {
const type = getTypeOf(cellOrValue);
return isTypeStringOrBoolean(type) ||
(type == NUMBER && isFiniteNumber(cellOrValue))
? type
: void 0;
};
const getTypeCase = (type, stringCase, numberCase, booleanCase) =>
type == STRING
? stringCase
: type == NUMBER
? numberCase
: type == BOOLEAN
? booleanCase
: null;
const useStoreCellComponentProps = (store, tableId) =>
useMemo(() => ({store, tableId}), [store, tableId]);
const useQueriesCellComponentProps = (queries, queryId) =>
useMemo(() => ({queries, queryId}), [queries, queryId]);
const useCallbackOrUndefined = (callback, deps, test) => {
const returnCallback = useCallback(callback, deps);
return test ? returnCallback : void 0;
};
const useParams = (...args) =>
useMemo(
() => args,
// eslint-disable-next-line react-hooks/exhaustive-deps
args,
);
const useCells = (defaultCellIds, customCells, defaultCellComponent) =>
useMemo(() => {
const cellIds = customCells ?? defaultCellIds;
return objMap(
isArray(cellIds)
? objNew(arrayMap(cellIds, (cellId) => [cellId, cellId]))
: cellIds,
(labelOrCustomCell, cellId) => ({
...{label: cellId, component: defaultCellComponent},
...(isString(labelOrCustomCell)
? {label: labelOrCustomCell}
: labelOrCustomCell),
}),
);
}, [customCells, defaultCellComponent, defaultCellIds]);
const UP_ARROW = '\u2191';
const DOWN_ARROW = '\u2193';
const EDITABLE = 'editable';
const extraRowCells = (extraRowCells2 = [], extraRowCellProps, after = 0) =>
arrayMap(extraRowCells2, ({component: Component}, index) =>
/* @__PURE__ */ jsx(
'td',
{
className: EXTRA,
children: /* @__PURE__ */ jsx(Component, {...extraRowCellProps}),
},
extraKey(index, after),
),
);
const extraKey = (index, after) => (after ? '>' : '<') + index;
const extraHeaders = (extraCells = [], after = 0) =>
arrayMap(extraCells, ({label}, index) =>
/* @__PURE__ */ jsx(
'th',
{className: EXTRA, children: label},
extraKey(index, after),
),
);
const HtmlHeaderCell = ({
cellId,
sort: [sortCellId, sortDescending],
label = cellId ?? EMPTY_STRING,
onClick,
}) =>
/* @__PURE__ */ jsxs('th', {
onClick: useCallbackOrUndefined(
() => onClick?.(cellId),
[onClick, cellId],
onClick,
),
className:
isUndefined(sortDescending) || sortCellId != cellId
? void 0
: `sorted ${sortDescending ? 'de' : 'a'}scending`,
children: [
isUndefined(sortDescending) || sortCellId != cellId
? null
: (sortDescending ? DOWN_ARROW : UP_ARROW) + ' ',
label,
],
});
const HtmlTable = ({
className,
headerRow,
idColumn,
params: [
cells,
cellComponentProps,
rowIds,
extraCellsBefore,
extraCellsAfter,
sortAndOffset,
handleSort,
paginatorComponent,
],
}) =>
/* @__PURE__ */ jsxs('table', {
className,
children: [
paginatorComponent
? /* @__PURE__ */ jsx('caption', {children: paginatorComponent})
: null,
headerRow === false
? null
: /* @__PURE__ */ jsx('thead', {
children: /* @__PURE__ */ jsxs('tr', {
children: [
extraHeaders(extraCellsBefore),
idColumn === false
? null
: /* @__PURE__ */ jsx(HtmlHeaderCell, {
sort: sortAndOffset ?? [],
label: 'Id',
onClick: handleSort,
}),
objToArray(cells, ({label}, cellId) =>
/* @__PURE__ */ jsx(
HtmlHeaderCell,
{
cellId,
label,
sort: sortAndOffset ?? [],
onClick: handleSort,
},
cellId,
),
),
extraHeaders(extraCellsAfter, 1),
],
}),
}),
/* @__PURE__ */ jsx('tbody', {
children: arrayMap(rowIds, (rowId) => {
const rowProps = {...cellComponentProps, rowId};
return /* @__PURE__ */ jsxs(
'tr',
{
children: [
extraRowCells(extraCellsBefore, rowProps),
idColumn === false
? null
: /* @__PURE__ */ jsx('th', {title: rowId, children: rowId}),
objToArray(
cells,
({component: CellView, getComponentProps}, cellId) =>
/* @__PURE__ */ jsx(
'td',
{
children: /* @__PURE__ */ jsx(CellView, {
...getProps(getComponentProps, rowId, cellId),
...rowProps,
cellId,
}),
},
cellId,
),
),
extraRowCells(extraCellsAfter, rowProps, 1),
],
},
rowId,
);
}),
}),
],
});
const EditableThing = ({
thing,
onThingChange,
className,
hasSchema,
showType = true,
}) => {
const [thingType, setThingType] = useState();
const [currentThing, setCurrentThing] = useState();
const [stringThing, setStringThing] = useState();
const [numberThing, setNumberThing] = useState();
const [booleanThing, setBooleanThing] = useState();
if (currentThing !== thing) {
setThingType(getCellOrValueType(thing));
setCurrentThing(thing);
setStringThing(String(thing));
setNumberThing(Number(thing) || 0);
setBooleanThing(Boolean(thing));
}
const handleThingChange = useCallback(
(thing2, setTypedThing) => {
setTypedThing(thing2);
setCurrentThing(thing2);
onThingChange(thing2);
},
[onThingChange],
);
const handleTypeChange = useCallback(() => {
if (!hasSchema?.()) {
const nextType = getTypeCase(thingType, NUMBER, BOOLEAN, STRING);
const thing2 = getTypeCase(
nextType,
stringThing,
numberThing,
booleanThing,
);
setThingType(nextType);
setCurrentThing(thing2);
onThingChange(thing2);
}
}, [
hasSchema,
onThingChange,
stringThing,
numberThing,
booleanThing,
thingType,
]);
const widget = getTypeCase(
thingType,
/* @__PURE__ */ jsx(
'input',
{
value: stringThing,
onChange: useCallback(
(event) =>
handleThingChange(
String(event[CURRENT_TARGET][_VALUE]),
setStringThing,
),
[handleThingChange],
),
},
thingType,
),
/* @__PURE__ */ jsx(
'input',
{
type: 'number',
value: numberThing,
onChange: useCallback(
(event) =>
handleThingChange(
Number(event[CURRENT_TARGET][_VALUE] || 0),
setNumberThing,
),
[handleThingChange],
),
},
thingType,
),
/* @__PURE__ */ jsx(
'input',
{
type: 'checkbox',
checked: booleanThing,
onChange: useCallback(
(event) =>
handleThingChange(
Boolean(event[CURRENT_TARGET].checked),
setBooleanThing,
),
[handleThingChange],
),
},
thingType,
),
);
return /* @__PURE__ */ jsxs('div', {
className,
children: [
showType && widget
? /* @__PURE__ */ jsx('button', {
title: thingType,
className: thingType,
onClick: handleTypeChange,
children: thingType,
})
: null,
widget,
],
});
};
const EditableCellView = ({
tableId,
rowId,
cellId,
store,
className,
showType,
}) =>
/* @__PURE__ */ jsx(EditableThing, {
thing: useCell(tableId, rowId, cellId, store),
onThingChange: useSetCellCallback(
tableId,
rowId,
cellId,
(cell) => cell,
[],
store,
),
className: className ?? EDITABLE + CELL,
showType,
hasSchema: useStoreOrStoreById(store)?.hasTablesSchema,
});
const EditableValueView = ({valueId, store, className, showType}) =>
/* @__PURE__ */ jsx(EditableThing, {
thing: useValue(valueId, store),
onThingChange: useSetValueCallback(valueId, (value) => value, [], store),
className: className ?? EDITABLE + VALUE,
showType,
hasSchema: useStoreOrStoreById(store)?.hasValuesSchema,
});
const useDottedCellIds = (tableId, store) =>
arrayMap(useTableCellIds(tableId, store), (cellId) => tableId + DOT + cellId);
const RelationshipInHtmlRow = ({
localRowId,
params: [
idColumn,
cells,
localTableId,
remoteTableId,
relationshipId,
relationships,
store,
extraCellsBefore,
extraCellsAfter,
],
}) => {
const remoteRowId = useRemoteRowId(relationshipId, localRowId, relationships);
const rowProps = {
tableId: localTableId ?? '',
rowId: localRowId,
store,
};
return /* @__PURE__ */ jsxs('tr', {
children: [
extraRowCells(extraCellsBefore, rowProps),
idColumn === false
? null
: /* @__PURE__ */ jsxs(Fragment, {
children: [
/* @__PURE__ */ jsx('th', {
title: localRowId,
children: localRowId,
}),
/* @__PURE__ */ jsx('th', {
title: remoteRowId,
children: remoteRowId,
}),
],
}),
objToArray(
cells,
({component: CellView2, getComponentProps}, compoundCellId) => {
const [tableId, cellId] = strSplit(compoundCellId, DOT, 2);
const rowId =
tableId === localTableId
? localRowId
: tableId === remoteTableId
? remoteRowId
: null;
return isUndefined(rowId)
? null
: /* @__PURE__ */ jsx(
'td',
{
children: /* @__PURE__ */ jsx(CellView2, {
...getProps(getComponentProps, rowId, cellId),
store,
tableId,
rowId,
cellId,
}),
},
compoundCellId,
);
},
),
extraRowCells(extraCellsAfter, rowProps, 1),
],
});
};
const RelationshipInHtmlTable = ({
relationshipId,
relationships,
editable,
customCells,
extraCellsBefore,
extraCellsAfter,
className,
headerRow,
idColumn = true,
}) => {
const [resolvedRelationships, store, localTableId, remoteTableId] =
getRelationshipsStoreTableIds(
useRelationshipsOrRelationshipsById(relationships),
relationshipId,
);
const cells = useCells(
[
...useDottedCellIds(localTableId, store),
...useDottedCellIds(remoteTableId, store),
],
customCells,
editable ? EditableCellView : CellView,
);
const params = useParams(
idColumn,
cells,
localTableId,
remoteTableId,
relationshipId,
resolvedRelationships,
store,
extraCellsBefore,
extraCellsAfter,
);
return /* @__PURE__ */ jsxs('table', {
className,
children: [
headerRow === false
? null
: /* @__PURE__ */ jsx('thead', {
children: /* @__PURE__ */ jsxs('tr', {
children: [
extraHeaders(extraCellsBefore),
idColumn === false
? null
: /* @__PURE__ */ jsxs(Fragment, {
children: [
/* @__PURE__ */ jsxs('th', {
children: [localTableId, '.Id'],
}),
/* @__PURE__ */ jsxs('th', {
children: [remoteTableId, '.Id'],
}),
],
}),
objToArray(cells, ({label}, cellId) =>
/* @__PURE__ */ jsx('th', {children: label}, cellId),
),
extraHeaders(extraCellsAfter, 1),
],
}),
}),
/* @__PURE__ */ jsx('tbody', {
children: arrayMap(useRowIds(localTableId, store), (localRowId) =>
/* @__PURE__ */ jsx(
RelationshipInHtmlRow,
{
localRowId,
params,
},
localRowId,
),
),
}),
],
});
};
const LEFT_ARROW = '\u2190';
const RIGHT_ARROW = '\u2192';
const useSortingAndPagination = (
cellId,
descending = false,
sortOnClick,
offset = 0,
limit,
total,
paginator,
onChange,
) => {
const [[currentCellId, currentDescending, currentOffset], setState] =
useState([cellId, descending, offset]);
const setStateAndChange = useCallback(
(sortAndOffset) => {
setState(sortAndOffset);
onChange?.(sortAndOffset);
},
[onChange],
);
const handleSort = useCallbackOrUndefined(
(cellId2) =>
setStateAndChange([
cellId2,
cellId2 == currentCellId ? !currentDescending : false,
currentOffset,
]),
[setStateAndChange, currentCellId, currentDescending, currentOffset],
sortOnClick,
);
const handleChangeOffset = useCallback(
(offset2) => setStateAndChange([currentCellId, currentDescending, offset2]),
[setStateAndChange, currentCellId, currentDescending],
);
const PaginatorComponent =
paginator === true ? SortedTablePaginator : paginator;
return [
[currentCellId, currentDescending, currentOffset],
handleSort,
useMemo(
() =>
paginator === false
? null
: /* @__PURE__ */ jsx(PaginatorComponent, {
offset: currentOffset,
limit,
total,
onChange: handleChangeOffset,
}),
[
paginator,
PaginatorComponent,
currentOffset,
limit,
total,
handleChangeOffset,
],
),
];
};
const SortedTablePaginator = ({
onChange,
total,
offset = 0,
limit = total,
singular = 'row',
plural = singular + 's',
}) => {
if (offset > total || offset < 0) {
offset = 0;
onChange(0);
}
const handlePrevClick = useCallbackOrUndefined(
() => onChange(offset - limit),
[onChange, offset, limit],
offset > 0,
);
const handleNextClick = useCallbackOrUndefined(
() => onChange(offset + limit),
[onChange, offset, limit],
offset + limit < total,
);
return /* @__PURE__ */ jsxs(Fragment, {
children: [
total > limit &&
/* @__PURE__ */ jsxs(Fragment, {
children: [
/* @__PURE__ */ jsx('button', {
className: 'previous',
disabled: offset == 0,
onClick: handlePrevClick,
children: LEFT_ARROW,
}),
/* @__PURE__ */ jsx('button', {
className: 'next',
disabled: offset + limit >= total,
onClick: handleNextClick,
children: RIGHT_ARROW,
}),
offset + 1,
' to ',
mathMin(total, offset + limit),
' of ',
],
}),
total,
' ',
total != 1 ? plural : singular,
],
});
};
const ResultSortedTableInHtmlTable = ({
queryId,
cellId,
descending,
offset,
limit,
queries,
sortOnClick,
paginator = false,
customCells,
extraCellsBefore,
extraCellsAfter,
onChange,
...props
}) => {
const [sortAndOffset, handleSort, paginatorComponent] =
useSortingAndPagination(
cellId,
descending,
sortOnClick,
offset,
limit,
useResultRowCount(queryId, queries),
paginator,
onChange,
);
return /* @__PURE__ */ jsx(HtmlTable, {
...props,
params: useParams(
useCells(
useResultTableCellIds(queryId, queries),
customCells,
ResultCellView,
),
useQueriesCellComponentProps(queries, queryId),
useResultSortedRowIds(queryId, ...sortAndOffset, limit, queries),
extraCellsBefore,
extraCellsAfter,
sortAndOffset,
handleSort,
paginatorComponent,
),
});
};
const ResultTableInHtmlTable = ({
queryId,
queries,
customCells,
extraCellsBefore,
extraCellsAfter,
...props
}) =>
/* @__PURE__ */ jsx(HtmlTable, {
...props,
params: useParams(
useCells(
useResultTableCellIds(queryId, queries),
customCells,
ResultCellView,
),
useQueriesCellComponentProps(queries, queryId),
useResultRowIds(queryId, queries),
extraCellsBefore,
extraCellsAfter,
),
});
const SliceInHtmlTable = ({
indexId,
sliceId,
indexes,
editable,
customCells,
extraCellsBefore,
extraCellsAfter,
...props
}) => {
const [resolvedIndexes, store, tableId] = getIndexStoreTableId(
useIndexesOrIndexesById(indexes),
indexId,
);
return /* @__PURE__ */ jsx(HtmlTable, {
...props,
params: useParams(
useCells(
useTableCellIds(tableId, store),
customCells,
editable ? EditableCellView : CellView,
),
useStoreCellComponentProps(store, tableId),
useSliceRowIds(indexId, sliceId, resolvedIndexes),
extraCellsBefore,
extraCellsAfter,
),
});
};
const SortedTableInHtmlTable = ({
tableId,
cellId,
descending,
offset,
limit,
store,
editable,
sortOnClick,
paginator = false,
onChange,
customCells,
extraCellsBefore,
extraCellsAfter,
...props
}) => {
const [sortAndOffset, handleSort, paginatorComponent] =
useSortingAndPagination(
cellId,
descending,
sortOnClick,
offset,
limit,
useRowCount(tableId, store),
paginator,
onChange,
);
return /* @__PURE__ */ jsx(HtmlTable, {
...props,
params: useParams(
useCells(
useTableCellIds(tableId, store),
customCells,
editable ? EditableCellView : CellView,
),
useStoreCellComponentProps(store, tableId),
useSortedRowIds(tableId, ...sortAndOffset, limit, store),
extraCellsBefore,
extraCellsAfter,
sortAndOffset,
handleSort,
paginatorComponent,
),
});
};
const TableInHtmlTable = ({
tableId,
store,
editable,
customCells,
extraCellsBefore,
extraCellsAfter,
...props
}) =>
/* @__PURE__ */ jsx(HtmlTable, {
...props,
params: useParams(
useCells(
useTableCellIds(tableId, store),
customCells,
editable ? EditableCellView : CellView,
),
useStoreCellComponentProps(store, tableId),
useRowIds(tableId, store),
extraCellsBefore,
extraCellsAfter,
),
});
const extraValueCells = (
extraValueCells2 = [],
extraValueCellProps,
after = 0,
) =>
arrayMap(extraValueCells2, ({component: Component}, index) =>
/* @__PURE__ */ jsx(
'td',
{
className: EXTRA,
children: /* @__PURE__ */ jsx(Component, {...extraValueCellProps}),
},
extraKey(index, after),
),
);
const ValuesInHtmlTable = ({
store,
editable = false,
valueComponent: Value = editable ? EditableValueView : ValueView,
getValueComponentProps,
extraCellsBefore,
extraCellsAfter,
className,
headerRow,
idColumn,
}) =>
/* @__PURE__ */ jsxs('table', {
className,
children: [
headerRow === false
? null
: /* @__PURE__ */ jsx('thead', {
children: /* @__PURE__ */ jsxs('tr', {
children: [
extraHeaders(extraCellsBefore),
idColumn === false
? null
: /* @__PURE__ */ jsx('th', {children: 'Id'}),
/* @__PURE__ */ jsx('th', {children: VALUE}),
extraHeaders(extraCellsAfter, 1),
],
}),
}),
/* @__PURE__ */ jsx('tbody', {
children: arrayMap(useValueIds(store), (valueId) => {
const valueProps = {valueId, store};
return /* @__PURE__ */ jsxs(
'tr',
{
children: [
extraValueCells(extraCellsBefore, valueProps),
idColumn === false
? null
: /* @__PURE__ */ jsx('th', {
title: valueId,
children: valueId,
}),
/* @__PURE__ */ jsx('td', {
children: /* @__PURE__ */ jsx(Value, {
...getProps(getValueComponentProps, valueId),
...valueProps,
}),
}),
extraValueCells(extraCellsAfter, valueProps, 1),
],
},
valueId,
);
}),
}),
],
});
export {
EditableCellView,
EditableValueView,
RelationshipInHtmlRow,
RelationshipInHtmlTable,
ResultSortedTableInHtmlTable,
ResultTableInHtmlTable,
SliceInHtmlTable,
SortedTableInHtmlTable,
SortedTablePaginator,
TableInHtmlTable,
useSortingAndPagination,
ValuesInHtmlTable,
};