UNPKG

@knowmax/genericlist-core

Version:

Knowmax Generic list with basic CRUD support without any user interface implementation.

1,070 lines (1,051 loc) 38.2 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); // 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 import { action as action2, computed, makeObservable as makeObservable2, observable as observable2 } from "mobx"; // src/list/GenericListSettings.ts import { makeObservable, observable, action } from "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); makeObservable(this, { ready: observable, setReady: action, token: observable, setToken: action, language: observable, setLanguage: action, params: observable, setParams: 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"); makeObservable2(this, { ready: computed, id: observable2, setId: action2, settings: observable2, setSettings: action2, loadingSingle: observable2, setLoadingSingle: action2, loaded: observable2, setLoaded: action2, loading: observable2, setLoading: action2, error: observable2, setError: action2, orderList: observable2, setOrderList: action2, order: observable2, setOrder: action2, orderExpression: computed, deleted: observable2, setDeleted: action2, filterList: observable2, setFilterList: action2, filter: observable2, setFilter: action2, search: observable2, setSearch: action2, page: observable2, setPage: action2, pageSize: observable2, setPageSize: action2, list: observable2, setList: action2, totalCount: observable2, setTotalCount: action2, endpointPostFix: observable2, setEndpointPostFix: action2, hash: computed, currentHash: observable2, setCurrentHash: action2, selectedItem: observable2, selectedItemTag: observable2, setSelectedItem: action2, load: action2, loadSingle: action2 }); 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 import { action as action3, computed as computed2, observable as observable3, makeObservable as makeObservable3 } from "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; }); makeObservable3(this, { cudError: observable3, setCudError: action3, validationError: observable3, setValidationError: action3, hasCudOrValidationError: computed2, createError: computed2, updateError: computed2, deleteError: computed2, restoreError: computed2, editableItem: observable3, setEditableItem: action3, editableItemUnmodified: observable3, setEditableItemUnmodified: action3, deleteItem: observable3, setDeleteItem: action3, deletedItem: observable3, setDeletedItem: action3, createdItem: observable3, setCreatedItem: action3, restoreItem: observable3, setRestoreItem: action3, restoredItem: observable3, setRestoredItem: action3, create: action3, update: action3, delete: action3, restore: action3, cancel: action3, isCreate: computed2, isUpdate: computed2, isDelete: computed2, isRestore: computed2, isModified: computed2, isValidated: computed2, save: action3, deleteConfirmed: action3, restoreConfirmed: action3 }); 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 import { useEffect, useMemo as useMemo2 } from "react"; // src/list/GenericListCacheProvider.tsx import { createContext, useContext, useMemo } from "react"; import { observer } from "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 import { jsx } from "react/jsx-runtime"; var GenericListCacheContext = createContext(null); var useGenericListCache = () => { const store = useContext(GenericListCacheContext); if (!store) { throw new Error(providerErrorMessage("GenericListCache")); } return store; }; var GenericListCacheProvider = observer(({ children }) => { const genericlistcache = useMemo(() => new GenericListCache(10), []); return /* @__PURE__ */ 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 = useMemo2(() => { 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 || []]); useEffect(() => { list.setSettings(listsettings); }, [list, listsettings]); useEffect(() => { const params = new URLSearchParams(listsettings.params); if (deserializeListState(list, params)) { listsettings.serialize(params); } }, [list, listsettings.params]); useEffect(() => { if (autoLoad) { list.load(); } }, [list.hash, list, autoLoad]); return list; }; // src/list/GenericListSettingsProvider.tsx import { createContext as createContext2, useContext as useContext2, useMemo as useMemo3 } from "react"; import { observer as observer2 } from "mobx-react-lite"; import { jsx as jsx2 } from "react/jsx-runtime"; var GenericListSettingsContext = createContext2(null); var useGenericListSettings = () => { const genericlistsettings = useContext2(GenericListSettingsContext); if (!genericlistsettings) { throw new Error(providerErrorMessage("GenericListSettings")); } return genericlistsettings; }; var GenericListSettingsProvider = observer2(({ children }) => { const genericlistsettings = useMemo3(() => new GenericListSettings(), []); return /* @__PURE__ */ jsx2(GenericListSettingsContext.Provider, { value: genericlistsettings, children: /* @__PURE__ */ jsx2(GenericListCacheProvider, { children }) }); }); // src/list/SimpleListHook.tsx import { useEffect as useEffect2, useMemo as useMemo4, useState, useCallback } from "react"; import { headers, FetchError } from "@knowmax/http-utils"; var useSimpleList = (configuration, dependencies) => { const [result, setResult] = useState(void 0); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(void 0); const [status, setStatus] = useState(void 0); const fetchData = 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 = 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 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 = useCallback(async () => { await fetchData(); }, [fetchData]); useEffect2(() => { const abortController = new AbortController(); fetchData(abortController.signal); return () => { abortController.abort(); }; }, dependencies ? [fetchData, configuration.token, ...dependencies] : [fetchData]); return useMemo4(() => ({ 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"); export { 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.mjs.map