@knowmax/genericlist-core
Version:
Knowmax Generic list with basic CRUD support without any user interface implementation.
493 lines (474 loc) • 23.1 kB
TypeScript
import { DependencyList, FunctionComponent } from 'react';
import { FetchError } from '@knowmax/http-utils';
/** Returns error message display when calling usePROVIDER at location outside PROVIDER context. */
declare const providerErrorMessage: (provider: string) => string;
declare const PARAM_PAGE = "page";
declare const PARAM_ORDER = "order";
declare const PARAM_DELETED = "deleted";
declare enum FilterOperator {
eq = "eq",
ne = "ne",
contains = "contains",
gt = "gt",
ge = "ge",
lt = "lt",
le = "le"
}
declare const FILTEROPERATORSSTRING: FilterOperator[];
declare const FILTEROPERATORSNUMBER: FilterOperator[];
declare const FILTEROPERATORSDATE: FilterOperator[];
declare enum FilterValueType {
string = 0,
number = 1,
date = 2,
boolean = 3
}
/** Call back method, called from within custom OnRenderCustomType */
type OnSelectCustomValue = (value?: string, listId?: string) => void;
/** Type for rendering custom filter */
type OnRenderCustomType = (filter: IFilter, value?: string, onChange?: OnSelectCustomValue, listId?: string) => JSX.Element;
/** Type for custom handling of deserialization of filter from given params. Should return filter to add to all filter and-wise. */
type OnDeserializeCustom = (filter: IFilter, params: URLSearchParams, listId?: string) => string[] | undefined;
interface IFilter {
/** Friendly display name fo filter. */
name: string;
/** Id used to represent selection in url. */
id: string;
/** Optional name of field for query in case it differs from @see id. */
field?: string;
/** Array of options for closed domain filters. If set filter will be presented as a closed domain, otherwise an open input value. */
options?: IFilterOption[];
/** Text to show as placeholder in input value. */
placeholderText?: string;
/** Selected operator to use for comparison. Defaults to eq. */
operator?: FilterOperator;
/** Available operators for comparison. */
operators?: FilterOperator[];
/** Optional value type. Needed for proper serialization to server and to determine type of input control to render. Defaults to string. */
valueType?: FilterValueType;
/** Hidden filter will be deserialized from params but are not visible. */
hidden?: boolean;
/** Set to true to make sure filter is initially collapsed (currently only supported for filters with @see options set and no value selected) */
collapsed?: boolean;
/** Set to true if this filter is to be used as search field in request to server. Only for fields that render as open input value. */
isSearch?: boolean;
/** Callback method to determine if filter is available. */
isAvailable?: (filter: IFilter, params: URLSearchParams, listId?: string) => boolean;
/** Callback method to handle filter in a custom way including rendering. */
renderCustom?: OnRenderCustomType;
/** Callback method to handle custom filter deserialization. */
deserializeCustom?: OnDeserializeCustom;
}
interface IFilterOption {
/** Unique identification. */
id: string;
/** Friendly name. */
name?: string;
/** Value associated with option. Used in combination with @see operator. */
value?: string | number | boolean;
/** Operator to be used with @see value. Defaults to equal (eq). */
operator?: FilterOperator;
/** Expression to override @see value and @see operator. */
expression?: string;
}
interface IOrder {
field: string;
name: string;
descending?: boolean;
default?: boolean;
/** The value type for this field. */
fieldType?: FilterValueType;
/** Callback method to determine if order is available. */
isAvailable?: (order: IOrder, params: URLSearchParams, listId?: string) => boolean;
}
interface IListResponse<T> {
count?: number;
value: T[];
}
interface IListRequest {
count: boolean;
take: number;
skip: number;
orderBy: string;
filter?: string;
search?: string;
}
/** Http methods for fetch. */
type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete';
/** Simplified search params type to specify optional fetch search params. */
type FetchSearchParams = Record<string, string | number | boolean> | Array<Array<string | number | boolean>>;
/** Optional parameters to override defaults used for fetch requests. */
type FetchOptions = {
method?: HttpMethod;
json?: unknown;
headers?: any;
searchParams?: FetchSearchParams;
};
type TranslationValues = Record<string, string | number>;
type OnGetHeadersType = (token?: string, language?: string, headers?: any, listId?: string) => any;
type OnFetchType = <T>(url: string, options?: FetchOptions, listId?: string) => Promise<T>;
type OnErrorMessageType = (error: Error) => Promise<string>;
type OnSerializeType = (params: URLSearchParams, settings: GenericListSettings, listId?: string) => void;
type OnTranslateType = (key: string, values?: TranslationValues, listId?: string) => string;
/** Responsible for all system settings in use for all generic lists. Allows system specific implementation of generic list components. */
declare class GenericListSettings {
/** Never use directly. Always use getter. */
private _onFetch;
private _onFetchSet;
/** Never use directly. Always use getter. */
private _onTranslate;
private _onTranslateSet;
constructor();
/** Set after required configuration of onFetch and onTranslate were made. */
ready: boolean;
/** Internally called after configuration of required settings was done. No need to call it externally. */
setReady(value: boolean): void;
/** Set to required bearer token for use in authentication of fetch requests. */
token?: string;
setToken(value?: string): void;
/** Set to required language of user for use in fetch requests to get localized data from server. */
language?: string;
setLanguage(value?: string): void;
/** Contains current set of values components user for state. */
params: URLSearchParams;
/** Set to current set of values for components to use for state. */
setParams(value: URLSearchParams): void;
/** Internally used to serialize params. By default set to internally maintained params. Override for custom onSerialize for custom serialization. */
serialize(params: URLSearchParams): void;
/** Used for fetching data from server. */
get onFetch(): OnFetchType;
set onFetch(value: OnFetchType);
/** Used for translation of all language sensitive contents. */
get onTranslate(): OnTranslateType;
set onTranslate(value: OnTranslateType);
/** Override to handle custom serialization of user settings like sorting, filtering and paging. */
onSerialize: OnSerializeType;
/** Override for custom implementation of header generation for fetch requests. */
onGetHeaders: OnGetHeadersType;
/** Override for custom implementation of error message extraction after for example fetch requests. */
onErrorMessage: OnErrorMessageType;
}
interface IGenericListConfiguration {
/** Optional unique id for this list. */
id?: string;
/** Endpoint to use for loading list of data. */
endpoint: string;
/** Endpoint to use for loading a single item. */
endpointGetSingle?: string;
/** Set in case this list required configuration for endpoint postfix. */
requiredEndpointPostfix?: boolean;
/** List with order options. */
orderList?: IOrder[];
/** List with filter options. */
filterList?: IFilter[];
/** Page number for pagination. */
page?: number;
/** Page size for pagination. */
pageSize?: number;
}
/** Generic implementation to support lists from different endpoints supporting Knowmax LinqUtility ListRequest.
* Can be inherited or used in combination with GenericListHook, @see useList. */
declare class GenericList<T = unknown> {
configuration: IGenericListConfiguration;
private _settingsUpdated;
private _loadOptions?;
constructor(configuration: IGenericListConfiguration);
/** Set when this generic list is ready for use. Essentially this means settings configuration is ready. */
get ready(): boolean;
/** Unique id representing this list. Will be provided to methods from settings when called to determine active list. Optional due to backward compatibility. */
id: string | undefined;
setId(value?: string): void;
/** Settings for all generic lists. */
settings: GenericListSettings;
/** Usually set from value provided GenericListSettinsProvider. */
setSettings(value: GenericListSettings): void;
/** Set while loading a single item */
loadingSingle: boolean;
setLoadingSingle(value: boolean): void;
/** Set after initially loading data. */
loaded: boolean;
setLoaded(value: boolean): void;
/** Set while loading data. */
loading: boolean;
setLoading(value: boolean): void;
error?: Error | string;
setError(value?: Error | string): void;
/** Available options for order. */
orderList: IOrder[];
setOrderList(value: IOrder[]): void;
/** Selected order expression. Set to IOrder from orderList or string order expression. */
order?: IOrder | string;
setOrder(value?: IOrder | string): void;
/** Server expression based on current order setting. */
get orderExpression(): string;
deleted: boolean;
setDeleted(value: boolean): void;
/** List with all filter definitions available for this list. */
filterList: IFilter[];
setFilterList(value: IFilter[]): void;
/** Filter to use while loading data. */
filter?: string;
setFilter(value?: string): void;
/** Query to be used while loading data. */
search?: string;
setSearch(value?: string): void;
/** Current selected page. 1-based. */
page: number;
setPage(value: number): void;
/** Number of items per page. */
pageSize: number;
setPageSize(value: number): void;
/** List containing actual page of data. */
list: T[];
setList(value: T[]): void;
/** Total number of items available. */
totalCount: number;
setTotalCount(value: number): void;
/** Will be used as post fix for currently configured endpoint. */
endpointPostFix?: string;
/** Set to postfix for current endpoint. For example the value "abc" will result in "/abc" postfix after currently configured endpoint. */
setEndpointPostFix(value?: string): void;
/** Dynamic hash representing current state of component. Use as dependecy to trigger effects */
get hash(): string;
/** Hash at the moment of last update of list with server data. */
currentHash?: string;
setCurrentHash(value: string): void;
/** Set when hash was updated. Rather use hash as dependecy to trigger effects. */
get hashUpdated(): boolean;
/** Set to selected item for specific purpose. */
selectedItem?: T;
/** Tag to identify purpose of selected item.. */
selectedItemTag?: string;
/** Selects item for specific purpose (not edit or delete). */
setSelectedItem(value?: T, tag?: string): void;
/** Load current page of items.
* @param reload If true, will reload current page of items using original options used on inital load.
* @param options Options to use while loading data. If not provided, will use options used on inital load only in case of reload.
*/
load(reload?: boolean, options?: FetchOptions): Promise<void>;
/** Load single item identified by id.
* @param id Id of item to load.
* @param options Options to use while loading data.
* @param endpoint Endpoint to use while loading data. If not provided, will use endpointGetSingle or endpoint from configuration.
*/
loadSingle(id: string | number, options?: FetchOptions, endpoint?: string): Promise<T | undefined>;
private checkSettings;
}
interface IGenericCudListConfiguration<ET> extends IGenericListConfiguration {
idField?: string;
nameField?: string;
/** Returns default item to use for new entries. */
defaultItem: () => ET;
/** Validate given item. Return boolean true for valid item or string error. */
validateItem?: (item: ET) => boolean | string;
/** Transform given item into item suitable for serialization to server. */
serverItem?: (item: ET) => unknown;
endpointPost?: string;
endpointPut?: string;
endpointDelete?: string;
endpointRestore?: string;
}
declare class GenericCudList<T, ET> extends GenericList<T> {
cudConfiguration: IGenericCudListConfiguration<ET>;
constructor(cudConfiguration: IGenericCudListConfiguration<ET>);
/** Set in case of Create Update Delete error. Prefer using specialized error fields per mode. */
cudError?: Error | string;
setCudError(value?: Error | string): void;
/** Might be set in case custom validation method returned error message. */
validationError?: string;
setValidationError(value?: string): void;
/** Set in case of CUD error or validation error (latter only when in update mode). */
get hasCudOrValidationError(): boolean;
/** Set after error in create mode. */
get createError(): string | Error | undefined;
/** Set after error in update mode. */
get updateError(): string | Error | undefined;
/** Set after error in delete mode. */
get deleteError(): string | Error | undefined;
/** Set after error in restore mode. */
get restoreError(): string | Error | undefined;
/** Set to editable item when in create or update mode. */
editableItem?: ET;
setEditableItem(value?: ET): void;
/** Set to original editable item when starting update mode. */
editableItemUnmodified?: ET;
setEditableItemUnmodified(value?: ET): void;
/** Set to item when in delete mode. */
deleteItem?: T;
setDeleteItem(value?: T): void;
/** Set with data of just deleted item. */
deletedItem?: T;
setDeletedItem(value?: T): void;
/** Set with data of newly created item. */
createdItem?: T;
setCreatedItem(value?: T): void;
/** Set to item when in restore mode. */
restoreItem?: T;
setRestoreItem(value?: T): void;
/** Set with data of just restored item. */
restoredItem?: T;
setRestoredItem(value?: T): void;
/** Start create mode for item. */
create: (template?: ET) => void;
/** Start update mode for given item. */
update(entity: ET | T): void;
/** Start delete mode for given item. */
delete(entity: T): void;
/** Start restore mode for given item. */
restore(entity: T): void;
/** Cancel any mode we are currently in (create, update, delete, restore). Using lambda syntax here so we can directly hook this method to onClick/onChange like events of user interface. */
cancel: () => void;
/** True if in create mode. */
get isCreate(): boolean;
/** True if in update mode. */
get isUpdate(): boolean;
/** True if in delete mode. */
get isDelete(): boolean;
/** True if in restore mode. */
get isRestore(): boolean;
/** True is editable data was changed while in update mode. */
get isModified(): boolean;
/** True if editable data contains valid data while in create or update mode. */
get isValidated(): boolean | undefined;
/** Perform actual create or update operation of item set in create or update mode. */
save(): Promise<boolean>;
/** Perform actual delete operation of item previously set in delete mode. */
deleteConfirmed(): Promise<boolean>;
/** Perform actual restore operation of item previously set in restore mode. */
restoreConfirmed(): Promise<boolean>;
/** Return id of given item using settings provided in provided @see IGenericCudListConfiguration. */
idForItem(item: T | ET): string | number | undefined;
/** Return name of given item using settings provided in provided @see IGenericCudListConfiguration. */
nameForItem(item: T | ET): string | number | undefined;
/** Helper function to get nested property values using dot notation (e.g., "user.name.surname"). */
private getNestedProperty;
}
/**
* Generic hook for specified type and configuration. Attempts to cache lists using endpoint as key.
* Automatically loads data, updates token and selected application.
*
* @param configuration - List configuration including endpoint and other settings
* @param autoLoad - Whether to automatically load data when the list is created or dependencies change
* @param cache - Whether to use caching. When true, lists are cached by endpoint/id key
* @param dependencies - Optional custom dependencies. When any of these change, the cached list (if any)
* will be removed and a fresh list instance will be created
*/
declare const useList: <T extends object>(configuration: IGenericListConfiguration, autoLoad?: boolean, cache?: boolean, dependencies?: DependencyList) => GenericList<T>;
declare const useGenericListSettings: () => GenericListSettings;
/** Wrap this around your application to be able to configure and use generic lists.
* Also includes GenericListCacheProvider, so no need to wrap that around your application. */
declare const GenericListSettingsProvider: FunctionComponent<{
children?: React.ReactNode;
}>;
/**
* Configuration object for the useSimpleList hook.
*/
type TSimpleListConfiguration = {
/** The API endpoint URL to fetch data from */
endpoint: string;
/** Bearer token for authentication */
token: string;
/** Optional ordering specification (e.g., "name ASC", "date DESC") */
order?: string;
/** Optional filter criteria for the query */
filter?: string;
/** Whether to include total count in the response */
count?: boolean;
/** Current page number (1-based, defaults to 1) */
page?: number;
/** Number of items per page (defaults to 10) */
pageSize?: number;
/** Optional search string for full-text search */
search?: string;
};
/**
* Return type for the useSimpleList hook.
*/
interface ISimpleListHookResult<T> {
/** The fetched list response containing data and metadata */
result?: IListResponse<T>;
/** Loading state indicator */
isLoading: boolean;
/** Error object if the request failed */
error?: FetchError | Error;
/** Http status code */
status?: number;
/** Function to manually refetch the data */
refetch: () => Promise<void>;
}
/**
* A simplified React hook for fetching paginated list data from an API endpoint.
*
* This hook is designed as a lightweight alternative to the more complex `useList` hook,
* providing essential list functionality without advanced features like caching,
* state management, or CRUD operations.
*
* **Key Features:**
* - Simple pagination support
* - Automatic request abortion on unmount/dependency changes
* - Built-in loading and error states
* - TypeScript support with generic type parameter
* - Manual refetch capability
*
* **Use Cases:**
* - Read-only data lists
* - Simple pagination scenarios
* - When you don't need advanced caching or state management
* - Lightweight components with minimal list requirements
*
* @template T - The type of objects in the list
* @param configuration - Configuration object containing endpoint, authentication, and query parameters
* @param dependencies - Optional React dependencies array to trigger refetch when values change
* @returns An object containing the list result, loading state, error state, and refetch function
*
* @example
* ```tsx
* // Basic usage
* const { result, isLoading, error, refetch } = useSimpleList<User>({
* endpoint: '/api/users',
* token: 'your-auth-token',
* pageSize: 20,
* page: 1
* });
*
* // With filtering and ordering
* const { result, isLoading, error } = useSimpleList<Product>({
* endpoint: '/api/products',
* token: authToken,
* filter: 'category eq "electronics"',
* order: 'name ASC',
* count: true
* });
*
* // With dependencies
* const [searchTerm, setSearchTerm] = useState('');
* const { result, isLoading } = useSimpleList<Article>({
* endpoint: '/api/articles',
* token: authToken,
* filter: searchTerm ? `title contains "${searchTerm}"` : undefined
* }, [searchTerm]);
* ```
*
* @see {@link useList} for the full-featured alternative with caching and CRUD operations
*/
declare const useSimpleList: <T extends Record<string, any>>(configuration: TSimpleListConfiguration, dependencies?: DependencyList) => ISimpleListHookResult<T>;
/** Deserialize GenericList settings from given URLSearchParams.
* @returns true if params have been updated. Caller is reposible to update params in state.
*/
declare const deserializeListState: (list: GenericList, params: URLSearchParams) => boolean;
declare const operatorForFilter: (filter: IFilter, params: URLSearchParams, listId?: string) => FilterOperator | undefined;
/** Serialize value for filter by adding quotes in case value type is string and value contains spaces. */
declare const serializeFilterValue: (value: string | boolean | number, valueType?: FilterValueType) => string;
/** Serialize operator for filter, default to eq. */
declare const serializeFilterOperator: (operator?: FilterOperator) => FilterOperator;
/** Determine name for field to store operator for field with given id in (url) state. */
declare const fieldIdForOperator: (id: string) => string;
/** Parse order expression by splitting order field name and optional sort indicator. */
declare const parseOrderExpression: (expression: string) => [string, boolean];
/** Returns given param id optionally prefixed with id of given list. */
declare const getParamId: (id: string, listId?: string) => string;
/** Get value for param with id optionally prefixed with id of given list. */
declare const getParamValue: (id: string, params: URLSearchParams, listId?: string) => string | null;
/** Returns true if given id starts with given listId, false otherwise. */
declare const hasListPrefix: (id: string, listId: string) => boolean;
export { FILTEROPERATORSDATE, FILTEROPERATORSNUMBER, FILTEROPERATORSSTRING, type FetchOptions, type FetchSearchParams, FilterOperator, FilterValueType, GenericCudList, GenericList, GenericListSettings, GenericListSettingsProvider, type HttpMethod, type IFilter, type IFilterOption, type IGenericCudListConfiguration, type IGenericListConfiguration, type IListRequest, type IListResponse, type IOrder, type ISimpleListHookResult, type OnDeserializeCustom, type OnErrorMessageType, type OnFetchType, type OnGetHeadersType, type OnRenderCustomType, type OnSelectCustomValue, type OnSerializeType, type OnTranslateType, PARAM_DELETED, PARAM_ORDER, PARAM_PAGE, type TranslationValues, deserializeListState, fieldIdForOperator, getParamId, getParamValue, hasListPrefix, operatorForFilter, parseOrderExpression, providerErrorMessage, serializeFilterOperator, serializeFilterValue, useGenericListSettings, useList, useSimpleList };