UNPKG

@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
"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