@knowmax/genericlist-core
Version:
Knowmax Generic list with basic CRUD support without any user interface implementation.
1,119 lines (1,099 loc) • 41.6 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// src/index.ts
var index_exports = {};
__export(index_exports, {
FILTEROPERATORSDATE: () => FILTEROPERATORSDATE,
FILTEROPERATORSNUMBER: () => FILTEROPERATORSNUMBER,
FILTEROPERATORSSTRING: () => FILTEROPERATORSSTRING,
FilterOperator: () => FilterOperator,
FilterValueType: () => FilterValueType,
GenericCudList: () => GenericCudList,
GenericList: () => GenericList,
GenericListSettings: () => GenericListSettings,
GenericListSettingsProvider: () => GenericListSettingsProvider,
PARAM_DELETED: () => PARAM_DELETED,
PARAM_ORDER: () => PARAM_ORDER,
PARAM_PAGE: () => PARAM_PAGE,
deserializeListState: () => deserializeListState,
fieldIdForOperator: () => fieldIdForOperator,
getParamId: () => getParamId,
getParamValue: () => getParamValue,
hasListPrefix: () => hasListPrefix,
operatorForFilter: () => operatorForFilter,
parseOrderExpression: () => parseOrderExpression,
providerErrorMessage: () => providerErrorMessage,
serializeFilterOperator: () => serializeFilterOperator,
serializeFilterValue: () => serializeFilterValue,
useGenericListSettings: () => useGenericListSettings,
useList: () => useList,
useSimpleList: () => useSimpleList
});
module.exports = __toCommonJS(index_exports);
// src/utils/error.ts
var providerErrorMessage = (provider) => `use${provider} must be used within a ${provider}Provider.`;
// src/types/constants.ts
var PARAM_PAGE = "page";
var PARAM_ORDER = "order";
var PARAM_DELETED = "deleted";
// src/types/IFilter.ts
var FilterOperator = /* @__PURE__ */ ((FilterOperator2) => {
FilterOperator2["eq"] = "eq";
FilterOperator2["ne"] = "ne";
FilterOperator2["contains"] = "contains";
FilterOperator2["gt"] = "gt";
FilterOperator2["ge"] = "ge";
FilterOperator2["lt"] = "lt";
FilterOperator2["le"] = "le";
return FilterOperator2;
})(FilterOperator || {});
var FILTEROPERATORSSTRING = ["contains" /* contains */, "eq" /* eq */, "ne" /* ne */];
var FILTEROPERATORSNUMBER = ["eq" /* eq */, "ne" /* ne */, "gt" /* gt */, "ge" /* ge */, "lt" /* lt */, "le" /* le */];
var FILTEROPERATORSDATE = ["eq" /* eq */, "ne" /* ne */, "gt" /* gt */, "ge" /* ge */, "lt" /* lt */, "le" /* le */];
var FilterValueType = /* @__PURE__ */ ((FilterValueType2) => {
FilterValueType2[FilterValueType2["string"] = 0] = "string";
FilterValueType2[FilterValueType2["number"] = 1] = "number";
FilterValueType2[FilterValueType2["date"] = 2] = "date";
FilterValueType2[FilterValueType2["boolean"] = 3] = "boolean";
return FilterValueType2;
})(FilterValueType || {});
// src/utils/serialization.ts
var deserializeListState = (list, params) => {
const initialstate = listState(list);
let paramsupdated = false;
deserializeDeleted(list, params);
if (deserializeFilters(list, params)) {
paramsupdated = true;
}
if (deserializeOrder(list, params)) {
paramsupdated = true;
}
if (deserializePage(list, params, initialstate)) {
paramsupdated = true;
}
return paramsupdated;
};
var listState = (list) => `f=${list.filter ?? ""}&o=${list.orderExpression}&s=${list.search ?? ""}&d=${list.deleted}`;
var deserializeOrder = (list, params) => {
const orderparamid = getParamId(PARAM_ORDER, list.id);
const ordervalue = params.get(orderparamid);
if (ordervalue) {
const [field, descending] = parseOrderExpression(ordervalue);
const order = list.orderList.find((order2) => order2.field === field && (order2.descending === descending || order2.descending === void 0 && descending === false));
if (order && (!order.isAvailable || order.isAvailable(order, params, list.id))) {
list.setOrder(order);
} else {
params.delete(orderparamid);
list.setOrder(list.orderList?.find((o) => o.default === true));
return true;
}
}
return false;
};
var deserializeFilters = (list, params) => {
let paramsupdated = false;
let hassearch = false;
const filters = [];
list.filterList.forEach((filter) => {
if (filter.deserializeCustom) {
const customfilters = filter.deserializeCustom(filter, params, list.id);
if (customfilters) {
filters.push(...customfilters);
}
} else {
const paramid = getParamId(filter.id, list.id);
const value = params.get(paramid);
if (value && value !== "") {
if (filter.isAvailable && !filter.isAvailable(filter, params, list.id)) {
params.delete(paramid);
params.delete(fieldIdForOperator(paramid));
paramsupdated = true;
} else if (filter.options) {
const option = filter.options.find((o) => o.id === value);
if (option) {
if (option.expression !== void 0 && option.expression !== "") {
filters.push(option.expression);
} else if (option.value !== void 0 && (typeof option.value === "number" || typeof option.value === "boolean" || option.value !== "")) {
filters.push(`${filter.field ?? filter.id} ${serializeFilterOperator(option.operator)} ${serializeFilterValue(option.value, filter.valueType)}`);
}
}
} else if (filter.isSearch === true) {
list.setSearch(value);
hassearch = true;
} else {
filters.push(`${filter.field ?? filter.id} ${serializeFilterOperator(operatorForFilter(filter, params, list.id))} ${serializeFilterValue(value, filter.valueType)}`);
}
}
}
});
list.setFilter(
filters.length > 0 ? filters.join(" and ") : void 0
/*list.filter*/
);
if (!hassearch) {
list.setSearch(void 0);
}
return paramsupdated;
};
var deserializePage = (list, params, initialState) => {
const pageparamid = getParamId(PARAM_PAGE, list.id);
const page = params.get(pageparamid);
if (page && initialState === listState(list)) {
const p = parseInt(page, 10);
if (isNaN(p) || p === 1) {
if (list.page !== 1) {
list.setPage(1);
params.delete(pageparamid);
return true;
}
} else {
list.setPage(p);
}
} else if (list.page !== 1) {
list.setPage(1);
params.delete(pageparamid);
return true;
}
return false;
};
var deserializeDeleted = (list, params) => {
const paramid = getParamId(PARAM_DELETED, list.id);
const value = params.get(paramid);
list.setDeleted(params.has(paramid) && value !== "false");
};
var operatorForFilter = (filter, params, listId) => {
if (filter.valueType === 3 /* boolean */) {
return "eq" /* eq */;
} else {
const operatorvalue = params.get(fieldIdForOperator(getParamId(filter.id, listId)));
if (operatorvalue) {
const o = FilterOperator[operatorvalue];
if (filter.operators && filter.operators.indexOf(o) !== -1) {
return o;
}
}
return filter.operator;
}
};
// src/utils/filterutils.ts
var serializeFilterValue = (value, valueType) => {
if (typeof value === "boolean" || typeof value === "number") {
return value.toString();
} else if ((valueType === 0 /* string */ || valueType === void 0) && value.indexOf(" ") !== -1) {
return `"${value}"`;
} else {
return value;
}
};
var serializeFilterOperator = (operator) => FilterOperator[operator ?? "eq" /* eq */];
var fieldIdForOperator = (id) => `${id}-o`;
// src/utils/orderutils.ts
var parseOrderExpression = (expression) => {
const parts = expression.trim().split(/\s+/);
if (parts.length > 0) {
return [parts[0].trim(), parts.length === 2 && parts[1].trim() === "desc"];
} else {
return [expression.trim(), false];
}
};
// src/utils/params.ts
var getParamId = (id, listId) => {
if (listId !== void 0 && listId !== "") {
return `${listId}.${id}`;
} else {
return id;
}
};
var getParamValue = (id, params, listId) => {
return params.get(getParamId(id, listId));
};
var hasListPrefix = (id, listId) => {
return id.startsWith(`${listId}.`);
};
// src/list/GenericList.ts
var import_mobx2 = require("mobx");
// src/list/GenericListSettings.ts
var import_mobx = require("mobx");
var GenericListSettings = class {
constructor() {
/** Never use directly. Always use getter. */
__publicField(this, "_onFetch", defaultFetch);
__publicField(this, "_onFetchSet", false);
/** Never use directly. Always use getter. */
__publicField(this, "_onTranslate", defaultTranslate);
__publicField(this, "_onTranslateSet", false);
/** Set after required configuration of onFetch and onTranslate were made. */
__publicField(this, "ready", false);
/** Set to required bearer token for use in authentication of fetch requests. */
__publicField(this, "token");
/** Set to required language of user for use in fetch requests to get localized data from server. */
__publicField(this, "language");
/** Contains current set of values components user for state. */
__publicField(this, "params", new URLSearchParams());
/** Override to handle custom serialization of user settings like sorting, filtering and paging. */
__publicField(this, "onSerialize", defaultSerialize);
/** Override for custom implementation of header generation for fetch requests. */
__publicField(this, "onGetHeaders", defaultOnGetHeaders);
/** Override for custom implementation of error message extraction after for example fetch requests. */
__publicField(this, "onErrorMessage", defaultErrorMessage);
(0, import_mobx.makeObservable)(this, {
ready: import_mobx.observable,
setReady: import_mobx.action,
token: import_mobx.observable,
setToken: import_mobx.action,
language: import_mobx.observable,
setLanguage: import_mobx.action,
params: import_mobx.observable,
setParams: import_mobx.action
});
}
/** Internally called after configuration of required settings was done. No need to call it externally. */
setReady(value) {
this.ready = value;
}
setToken(value) {
this.token = value;
}
setLanguage(value) {
this.language = value;
}
/** Set to current set of values for components to use for state. */
setParams(value) {
this.params = value;
}
/** Internally used to serialize params. By default set to internally maintained params. Override for custom onSerialize for custom serialization. */
serialize(params) {
this.onSerialize(params, this);
}
/** Used for fetching data from server. */
get onFetch() {
return this._onFetch;
}
set onFetch(value) {
this._onFetch = value;
this._onFetchSet = true;
this.setReady(this._onFetchSet && this._onTranslateSet);
}
/** Used for translation of all language sensitive contents. */
get onTranslate() {
return this._onTranslate;
}
set onTranslate(value) {
this._onTranslate = value;
this._onTranslateSet = true;
this.setReady(this._onFetchSet && this._onTranslateSet);
}
};
var defaultFetch = (url, options) => {
return new Promise((resolve, reject) => {
console.warn("onFetch not configured");
reject("not implemented");
});
};
var defaultTranslate = (key, options) => {
console.warn("onTranslate not configured");
return key;
};
var defaultSerialize = (params, settings) => {
settings.setParams(params);
};
var defaultOnGetHeaders = (token, language, headers2) => {
const h = headers2 ?? {};
if (token) {
h["Authorization"] = "Bearer " + token;
}
if (language) {
h["Accept-Language"] = language;
}
return h;
};
var defaultErrorMessage = (error) => {
return new Promise((resolve) => {
resolve(error.message);
});
};
// src/list/GenericList.ts
var GenericList = class {
constructor(configuration) {
__publicField(this, "configuration");
__publicField(this, "_settingsUpdated", false);
__publicField(this, "_loadOptions");
/** Unique id representing this list. Will be provided to methods from settings when called to determine active list. Optional due to backward compatibility. */
__publicField(this, "id");
/** Settings for all generic lists. */
__publicField(this, "settings", new GenericListSettings());
/** Set while loading a single item */
__publicField(this, "loadingSingle", false);
/** Set after initially loading data. */
__publicField(this, "loaded", false);
/** Set while loading data. */
__publicField(this, "loading", false);
__publicField(this, "error");
/** Available options for order. */
__publicField(this, "orderList");
/** Selected order expression. Set to IOrder from orderList or string order expression. */
__publicField(this, "order");
__publicField(this, "deleted", false);
/** List with all filter definitions available for this list. */
__publicField(this, "filterList");
/** Filter to use while loading data. */
__publicField(this, "filter");
/** Query to be used while loading data. */
__publicField(this, "search");
/** Current selected page. 1-based. */
__publicField(this, "page");
/** Number of items per page. */
__publicField(this, "pageSize");
/** List containing actual page of data. */
__publicField(this, "list");
/** Total number of items available. */
__publicField(this, "totalCount");
/** Will be used as post fix for currently configured endpoint. */
__publicField(this, "endpointPostFix");
/** Hash at the moment of last update of list with server data. */
__publicField(this, "currentHash");
/** Set to selected item for specific purpose. */
__publicField(this, "selectedItem");
/** Tag to identify purpose of selected item.. */
__publicField(this, "selectedItemTag");
(0, import_mobx2.makeObservable)(this, {
ready: import_mobx2.computed,
id: import_mobx2.observable,
setId: import_mobx2.action,
settings: import_mobx2.observable,
setSettings: import_mobx2.action,
loadingSingle: import_mobx2.observable,
setLoadingSingle: import_mobx2.action,
loaded: import_mobx2.observable,
setLoaded: import_mobx2.action,
loading: import_mobx2.observable,
setLoading: import_mobx2.action,
error: import_mobx2.observable,
setError: import_mobx2.action,
orderList: import_mobx2.observable,
setOrderList: import_mobx2.action,
order: import_mobx2.observable,
setOrder: import_mobx2.action,
orderExpression: import_mobx2.computed,
deleted: import_mobx2.observable,
setDeleted: import_mobx2.action,
filterList: import_mobx2.observable,
setFilterList: import_mobx2.action,
filter: import_mobx2.observable,
setFilter: import_mobx2.action,
search: import_mobx2.observable,
setSearch: import_mobx2.action,
page: import_mobx2.observable,
setPage: import_mobx2.action,
pageSize: import_mobx2.observable,
setPageSize: import_mobx2.action,
list: import_mobx2.observable,
setList: import_mobx2.action,
totalCount: import_mobx2.observable,
setTotalCount: import_mobx2.action,
endpointPostFix: import_mobx2.observable,
setEndpointPostFix: import_mobx2.action,
hash: import_mobx2.computed,
currentHash: import_mobx2.observable,
setCurrentHash: import_mobx2.action,
selectedItem: import_mobx2.observable,
selectedItemTag: import_mobx2.observable,
setSelectedItem: import_mobx2.action,
load: import_mobx2.action,
loadSingle: import_mobx2.action
});
this.configuration = configuration;
this.id = configuration.id;
this.orderList = configuration.orderList ?? [];
this.order = configuration.orderList?.find((o) => o.default === true);
this.filterList = configuration.filterList ?? [];
this.list = [];
this.page = configuration.page ?? 1;
this.pageSize = configuration.pageSize ?? 10;
this.totalCount = 0;
}
/** Set when this generic list is ready for use. Essentially this means settings configuration is ready. */
get ready() {
return this.settings.ready;
}
setId(value) {
this.id = value;
}
/** Usually set from value provided GenericListSettinsProvider. */
setSettings(value) {
this._settingsUpdated = true;
this.settings = value;
}
setLoadingSingle(value) {
this.loadingSingle = value;
}
setLoaded(value) {
this.loaded = value;
}
setLoading(value) {
this.loading = value;
}
setError(value) {
this.error = value;
}
setOrderList(value) {
this.orderList = value;
}
setOrder(value) {
this.order = value;
}
/** Server expression based on current order setting. */
get orderExpression() {
return this.order ? typeof this.order !== "string" ? `${this.order.field}${this.order.descending === true ? " desc" : ""}` : this.order : "";
}
setDeleted(value) {
this.deleted = value;
}
setFilterList(value) {
this.filterList = value;
}
setFilter(value) {
this.filter = value;
}
setSearch(value) {
this.search = value;
}
setPage(value) {
this.page = value;
}
setPageSize(value) {
this.pageSize = value;
}
setList(value) {
this.list = value;
}
setTotalCount(value) {
this.totalCount = value;
}
/** Set to postfix for current endpoint. For example the value "abc" will result in "/abc" postfix after currently configured endpoint. */
setEndpointPostFix(value) {
this.endpointPostFix = value;
}
/** Dynamic hash representing current state of component. Use as dependecy to trigger effects */
get hash() {
return `id=${this.id}&p=${this.page}&ps=${this.pageSize}&o=${this.orderExpression}&f=${this.filter}&s=${this.search}${this.deleted ? "&d=true" : ""}&t=${this.settings.token}&epf=${this.endpointPostFix}`;
}
setCurrentHash(value) {
this.currentHash = value;
}
/** Set when hash was updated. Rather use hash as dependecy to trigger effects. */
get hashUpdated() {
return this.hash !== this.currentHash;
}
/** Selects item for specific purpose (not edit or delete). */
setSelectedItem(value, tag) {
this.selectedItem = value;
this.selectedItemTag = value ? tag : void 0;
}
/** 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.
*/
async load(reload = false, options) {
if (!this.settings.token || this.configuration.requiredEndpointPostfix === true && (!this.endpointPostFix || this.endpointPostFix.trim() === "")) {
this.setList([]);
this.setTotalCount(0);
this.setLoaded(false);
} else if (this.hashUpdated || reload) {
try {
try {
this.checkSettings();
this.setCurrentHash(this.hash);
this.setError(void 0);
this.setLoading(true);
if (reload && !options && this._loadOptions) {
options = this._loadOptions;
} else if (!reload) {
this._loadOptions = options;
}
const endpoint = this.configuration.requiredEndpointPostfix === true && this.endpointPostFix && this.endpointPostFix.trim() !== "" ? `${this.configuration.endpoint}/${this.endpointPostFix}` : this.configuration.endpoint;
const method = options?.method ?? "post";
const searchparams = Array.isArray(options?.searchParams) ? [...options.searchParams] : { ...options?.searchParams };
if (this.deleted) {
if (Array.isArray(searchparams)) {
searchparams.push(["deleted", "true"]);
} else {
searchparams.deleted = "true";
}
}
const response = await this.settings.onFetch(endpoint, {
searchParams: searchparams,
//options?.searchParams,
method,
headers: options?.headers ?? this.settings.onGetHeaders(this.settings.token, this.settings.language, void 0, this.id),
// Make sure we never post json when not post was specified as method
json: method === "post" ? options?.json ?? {
count: true,
take: this.pageSize,
skip: (this.page - 1) * this.pageSize,
orderBy: this.orderExpression,
filter: this.filter,
search: this.search
} : void 0
}, this.id);
this.setList(response.value ?? []);
this.setTotalCount(response.count ?? 0);
this.setLoaded(true);
} catch (e) {
if (e instanceof Error) {
this.setError(e);
}
this.setLoaded(false);
}
} finally {
this.setLoading(false);
}
}
}
/** 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.
*/
async loadSingle(id, options, endpoint) {
this.setError(void 0);
if (this.settings.token) {
try {
this.checkSettings();
this.setLoadingSingle(true);
try {
return await this.settings.onFetch(`${endpoint ?? this.configuration.endpointGetSingle ?? this.configuration.endpoint}/${id}`, {
searchParams: options?.searchParams,
method: options?.method ?? "get",
headers: options?.headers ?? this.settings.onGetHeaders(this.settings.token, this.settings.language, void 0, this.id)
}, this.id);
} catch (e) {
if (e instanceof Error) {
this.setError(e);
}
}
} finally {
this.setLoadingSingle(false);
}
}
}
checkSettings() {
if (!this._settingsUpdated) {
console.warn(`Settings not set for generic list with endpoint ${this.configuration.endpoint}`);
}
}
};
// src/list/GenericCudList.ts
var import_mobx3 = require("mobx");
var GenericCudList = class extends GenericList {
constructor(cudConfiguration) {
super(cudConfiguration);
__publicField(this, "cudConfiguration");
/** Set in case of Create Update Delete error. Prefer using specialized error fields per mode. */
__publicField(this, "cudError");
/** Might be set in case custom validation method returned error message. */
__publicField(this, "validationError");
/** Set to editable item when in create or update mode. */
__publicField(this, "editableItem");
/** Set to original editable item when starting update mode. */
__publicField(this, "editableItemUnmodified");
/** Set to item when in delete mode. */
__publicField(this, "deleteItem");
/** Set with data of just deleted item. */
__publicField(this, "deletedItem");
/** Set with data of newly created item. */
__publicField(this, "createdItem");
/** Set to item when in restore mode. */
__publicField(this, "restoreItem");
/** Set with data of just restored item. */
__publicField(this, "restoredItem");
/** Start create mode for item. */
__publicField(this, "create", (template) => {
this.deleteItem = void 0;
this.restoreItem = void 0;
this.editableItem = template ?? this.cudConfiguration.defaultItem();
this.editableItemUnmodified = void 0;
this.cudError = void 0;
this.validationError = void 0;
});
/** 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. */
__publicField(this, "cancel", () => {
this.editableItem = void 0;
this.editableItemUnmodified = void 0;
this.deleteItem = void 0;
this.restoreItem = void 0;
this.cudError = void 0;
this.validationError = void 0;
});
(0, import_mobx3.makeObservable)(this, {
cudError: import_mobx3.observable,
setCudError: import_mobx3.action,
validationError: import_mobx3.observable,
setValidationError: import_mobx3.action,
hasCudOrValidationError: import_mobx3.computed,
createError: import_mobx3.computed,
updateError: import_mobx3.computed,
deleteError: import_mobx3.computed,
restoreError: import_mobx3.computed,
editableItem: import_mobx3.observable,
setEditableItem: import_mobx3.action,
editableItemUnmodified: import_mobx3.observable,
setEditableItemUnmodified: import_mobx3.action,
deleteItem: import_mobx3.observable,
setDeleteItem: import_mobx3.action,
deletedItem: import_mobx3.observable,
setDeletedItem: import_mobx3.action,
createdItem: import_mobx3.observable,
setCreatedItem: import_mobx3.action,
restoreItem: import_mobx3.observable,
setRestoreItem: import_mobx3.action,
restoredItem: import_mobx3.observable,
setRestoredItem: import_mobx3.action,
create: import_mobx3.action,
update: import_mobx3.action,
delete: import_mobx3.action,
restore: import_mobx3.action,
cancel: import_mobx3.action,
isCreate: import_mobx3.computed,
isUpdate: import_mobx3.computed,
isDelete: import_mobx3.computed,
isRestore: import_mobx3.computed,
isModified: import_mobx3.computed,
isValidated: import_mobx3.computed,
save: import_mobx3.action,
deleteConfirmed: import_mobx3.action,
restoreConfirmed: import_mobx3.action
});
this.cudConfiguration = cudConfiguration;
}
setCudError(value) {
this.cudError = value;
}
setValidationError(value) {
this.validationError = value;
}
/** Set in case of CUD error or validation error (latter only when in update mode). */
get hasCudOrValidationError() {
return (this.isCreate || this.isUpdate) && this.cudError !== void 0 || this.isUpdate && this.validationError !== void 0;
}
/** Set after error in create mode. */
get createError() {
return this.isCreate && this.cudError !== void 0 ? this.cudError : void 0;
}
/** Set after error in update mode. */
get updateError() {
return this.isUpdate && this.cudError !== void 0 ? this.cudError : void 0;
}
/** Set after error in delete mode. */
get deleteError() {
return this.isDelete && this.cudError !== void 0 ? this.cudError : void 0;
}
/** Set after error in restore mode. */
get restoreError() {
return this.isRestore && this.cudError !== void 0 ? this.cudError : void 0;
}
setEditableItem(value) {
this.editableItem = value;
const validationerror = value !== void 0 && this.cudConfiguration.validateItem && this.cudConfiguration.validateItem(value);
if (typeof validationerror === "string") {
this.setValidationError(validationerror);
} else {
this.setValidationError(void 0);
}
}
setEditableItemUnmodified(value) {
this.editableItemUnmodified = value;
}
setDeleteItem(value) {
this.deleteItem = value;
}
setDeletedItem(value) {
this.deletedItem = value;
}
setCreatedItem(value) {
this.createdItem = value;
}
setRestoreItem(value) {
this.restoreItem = value;
}
setRestoredItem(value) {
this.restoredItem = value;
}
/** Start update mode for given item. */
update(entity) {
this.deleteItem = void 0;
this.restoreItem = void 0;
this.editableItem = { ...entity };
this.editableItemUnmodified = { ...entity };
this.cudError = void 0;
this.validationError = void 0;
}
/** Start delete mode for given item. */
delete(entity) {
this.editableItem = void 0;
this.editableItemUnmodified = void 0;
this.deleteItem = entity;
this.restoreItem = void 0;
this.cudError = void 0;
this.validationError = void 0;
}
/** Start restore mode for given item. */
restore(entity) {
this.editableItem = void 0;
this.editableItemUnmodified = void 0;
this.deleteItem = void 0;
this.restoreItem = entity;
this.cudError = void 0;
this.validationError = void 0;
}
/** True if in create mode. */
get isCreate() {
return this.editableItem !== void 0 && this.idForItem(this.editableItem) === void 0;
}
/** True if in update mode. */
get isUpdate() {
return this.editableItem !== void 0 && this.idForItem(this.editableItem) !== void 0;
}
/** True if in delete mode. */
get isDelete() {
return this.deleteItem !== void 0;
}
/** True if in restore mode. */
get isRestore() {
return this.restoreItem !== void 0;
}
/** True is editable data was changed while in update mode. */
get isModified() {
return this.editableItemUnmodified === void 0 || JSON.stringify(this.editableItemUnmodified) !== JSON.stringify(this.editableItem);
}
/** True if editable data contains valid data while in create or update mode. */
get isValidated() {
return this.editableItem && (!this.cudConfiguration.validateItem || this.cudConfiguration.validateItem(this.editableItem) === true);
}
/** Perform actual create or update operation of item set in create or update mode. */
async save() {
this.setCudError(void 0);
this.setCreatedItem(void 0);
if (this.settings.token && this.editableItem) {
try {
const serveritem = this.cudConfiguration.serverItem ? this.cudConfiguration.serverItem(this.editableItem) : { ...this.editableItem };
if (this.isCreate) {
const created = await this.settings.onFetch(this.cudConfiguration.endpointPost ?? this.configuration.endpoint, {
method: "post",
headers: this.settings.onGetHeaders(this.settings.token, this.settings.language, void 0, this.id),
json: serveritem
}, this.id);
this.cancel();
this.setCreatedItem(created);
return true;
} else if (this.isUpdate && this.idForItem(this.editableItem) !== void 0) {
await this.settings.onFetch(`${this.cudConfiguration.endpointPut ?? this.configuration.endpoint}/${this.idForItem(this.editableItem)}`, {
method: "put",
headers: this.settings.onGetHeaders(this.settings.token, this.settings.language, void 0, this.id),
json: serveritem
}, this.id);
this.setEditableItemUnmodified({ ...this.editableItem });
return true;
}
} catch (e) {
if (e instanceof Error) {
this.setCudError(e);
}
}
}
return false;
}
/** Perform actual delete operation of item previously set in delete mode. */
async deleteConfirmed() {
this.setCudError(void 0);
if (this.isDelete && this.deleteItem && this.settings.token) {
try {
await this.settings.onFetch(`${this.cudConfiguration.endpointDelete ?? this.configuration.endpoint}/${this.idForItem(this.deleteItem)}`, {
method: "delete",
headers: this.settings.onGetHeaders(this.settings.token, this.settings.language, void 0, this.id)
// TODO: reintroduce retry
//retry: 0
}, this.id);
this.setDeletedItem(this.deleteItem);
this.cancel();
return true;
} catch (e) {
if (e instanceof Error) {
this.setCudError(e);
}
}
}
return false;
}
/** Perform actual restore operation of item previously set in restore mode. */
async restoreConfirmed() {
this.setCudError(void 0);
if (this.isRestore && this.restoreItem && this.settings.token) {
try {
await this.settings.onFetch(`${this.cudConfiguration.endpointRestore ?? this.configuration.endpoint}/restore/${this.idForItem(this.restoreItem)}`, {
method: "post",
headers: this.settings.onGetHeaders(this.settings.token, this.settings.language, void 0, this.id)
// TODO: reintroduce retry
//retry: 0
}, this.id);
this.setRestoredItem(this.restoreItem);
this.cancel();
return true;
} catch (e) {
if (e instanceof Error) {
this.setCudError(e);
}
}
}
return false;
}
/** Return id of given item using settings provided in provided @see IGenericCudListConfiguration. */
idForItem(item) {
const idField = this.cudConfiguration.idField ?? "id";
return this.getNestedProperty(item, idField);
}
/** Return name of given item using settings provided in provided @see IGenericCudListConfiguration. */
nameForItem(item) {
const nameField = this.cudConfiguration.nameField ?? "name";
return this.getNestedProperty(item, nameField);
}
/** Helper function to get nested property values using dot notation (e.g., "user.name.surname"). */
getNestedProperty(obj, path) {
if (!obj || !path) {
return void 0;
}
const keys = path.split(".");
let current = obj;
for (const key of keys) {
if (current === null || current === void 0 || typeof current !== "object") {
return void 0;
}
current = current[key];
}
return typeof current === "string" || typeof current === "number" ? current : void 0;
}
};
// src/list/GenericListHook.tsx
var import_react2 = require("react");
// src/list/GenericListCacheProvider.tsx
var import_react = require("react");
var import_mobx_react_lite = require("mobx-react-lite");
// src/list/GenericListCache.ts
var GenericListCache = class {
constructor(maxSize) {
__publicField(this, "list", []);
__publicField(this, "maxSize");
this.maxSize = maxSize;
}
get(key) {
const list = this.list.find((item) => item.key === key)?.value;
return list ? list : void 0;
}
getOrCreate(key, creator) {
return this.list.find((item) => item.key === key)?.value ?? this.set(key, creator);
}
set(key, creator) {
const list = creator();
const index = this.list.findIndex((item) => item.key === key);
if (index !== -1) {
this.list[index].value = list;
} else {
if (this.list.length === this.maxSize) {
this.list.shift();
}
this.list.push({ key, value: list });
}
return list;
}
remove(key) {
const index = this.list.findIndex((item) => item.key === key);
if (index !== -1) {
this.list.splice(index, 1);
}
}
};
// src/list/GenericListCacheProvider.tsx
var import_jsx_runtime = require("react/jsx-runtime");
var GenericListCacheContext = (0, import_react.createContext)(null);
var useGenericListCache = () => {
const store = (0, import_react.useContext)(GenericListCacheContext);
if (!store) {
throw new Error(providerErrorMessage("GenericListCache"));
}
return store;
};
var GenericListCacheProvider = (0, import_mobx_react_lite.observer)(({ children }) => {
const genericlistcache = (0, import_react.useMemo)(() => new GenericListCache(10), []);
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GenericListCacheContext.Provider, { value: genericlistcache, children });
});
// src/list/GenericListHook.tsx
var useList = (configuration, autoLoad = true, cache = true, dependencies) => {
const listsettings = useGenericListSettings();
const listcache = useGenericListCache();
const list = (0, import_react2.useMemo)(() => {
if (cache) {
const key = configuration.id ? `${configuration.id}:${configuration.endpoint}` : configuration.endpoint;
if (dependencies) {
listcache.remove(key);
}
return listcache.getOrCreate(key, () => new GenericList(configuration));
} else {
return new GenericList(configuration);
}
}, [cache, configuration.endpoint, configuration.id, listcache, ...dependencies || []]);
(0, import_react2.useEffect)(() => {
list.setSettings(listsettings);
}, [list, listsettings]);
(0, import_react2.useEffect)(() => {
const params = new URLSearchParams(listsettings.params);
if (deserializeListState(list, params)) {
listsettings.serialize(params);
}
}, [list, listsettings.params]);
(0, import_react2.useEffect)(() => {
if (autoLoad) {
list.load();
}
}, [list.hash, list, autoLoad]);
return list;
};
// src/list/GenericListSettingsProvider.tsx
var import_react3 = require("react");
var import_mobx_react_lite2 = require("mobx-react-lite");
var import_jsx_runtime2 = require("react/jsx-runtime");
var GenericListSettingsContext = (0, import_react3.createContext)(null);
var useGenericListSettings = () => {
const genericlistsettings = (0, import_react3.useContext)(GenericListSettingsContext);
if (!genericlistsettings) {
throw new Error(providerErrorMessage("GenericListSettings"));
}
return genericlistsettings;
};
var GenericListSettingsProvider = (0, import_mobx_react_lite2.observer)(({ children }) => {
const genericlistsettings = (0, import_react3.useMemo)(() => new GenericListSettings(), []);
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GenericListSettingsContext.Provider, { value: genericlistsettings, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GenericListCacheProvider, { children }) });
});
// src/list/SimpleListHook.tsx
var import_react4 = require("react");
var import_http_utils = require("@knowmax/http-utils");
var useSimpleList = (configuration, dependencies) => {
const [result, setResult] = (0, import_react4.useState)(void 0);
const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
const [error, setError] = (0, import_react4.useState)(void 0);
const [status, setStatus] = (0, import_react4.useState)(void 0);
const fetchData = (0, import_react4.useCallback)(async (abortSignal) => {
if (configuration.token) {
try {
setIsLoading(true);
setError(void 0);
const requestBody = {
take: configuration.pageSize ?? 10,
skip: ((configuration.page ?? 1) - 1) * (configuration.pageSize ?? 10),
orderBy: configuration.order || "",
filter: configuration.filter,
search: configuration.search,
count: configuration.count ?? false
};
const requestHeaders = (0, import_http_utils.headers)().withContentTypeJson().withBearer(configuration.token);
const response = await fetch(configuration.endpoint, {
method: "post",
headers: requestHeaders.export(),
body: JSON.stringify(requestBody),
signal: abortSignal
});
setStatus(response.status);
if (!response.ok) {
const errorData = await response.json().catch(() => void 0);
throw new import_http_utils.FetchError(response, errorData);
}
const fetchedResult = await response.json();
if (!abortSignal?.aborted) {
setResult(fetchedResult);
}
} catch (err) {
if (err instanceof Error && err.name === "AbortError") {
return;
}
const errorInstance = err instanceof Error ? err : new Error("An unknown error occurred");
if (!abortSignal?.aborted) {
setError(errorInstance);
setResult(void 0);
}
} finally {
if (!abortSignal?.aborted) {
setIsLoading(false);
}
}
}
}, [configuration.endpoint, configuration.token, configuration.order, configuration.filter, configuration.page, configuration.pageSize]);
const refetch = (0, import_react4.useCallback)(async () => {
await fetchData();
}, [fetchData]);
(0, import_react4.useEffect)(() => {
const abortController = new AbortController();
fetchData(abortController.signal);
return () => {
abortController.abort();
};
}, dependencies ? [fetchData, configuration.token, ...dependencies] : [fetchData]);
return (0, import_react4.useMemo)(() => ({
result,
isLoading,
error,
status,
refetch
}), [result, isLoading, error, status, refetch]);
};
// src/index.ts
if (!new class {
constructor() {
__publicField(this, "x");
}
}().hasOwnProperty("x")) throw new Error("Transpiler is not configured correctly - required for MobX support");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
FILTEROPERATORSDATE,
FILTEROPERATORSNUMBER,
FILTEROPERATORSSTRING,
FilterOperator,
FilterValueType,
GenericCudList,
GenericList,
GenericListSettings,
GenericListSettingsProvider,
PARAM_DELETED,
PARAM_ORDER,
PARAM_PAGE,
deserializeListState,
fieldIdForOperator,
getParamId,
getParamValue,
hasListPrefix,
operatorForFilter,
parseOrderExpression,
providerErrorMessage,
serializeFilterOperator,
serializeFilterValue,
useGenericListSettings,
useList,
useSimpleList
});
//# sourceMappingURL=index.js.map