UNPKG

ngx-primeng-toolkit

Version:

A comprehensive TypeScript utility library for Angular component state management, PrimeNG table state management, ng-select helpers, data storage, and memoized HTTP caching. Compatible with Angular 19+ and PrimeNG 19+ (optimized for Angular 20+ and Prime

1,566 lines (1,554 loc) 60.1 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __typeError = (msg) => { throw TypeError(msg); }; 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 __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); // src/index.ts var index_exports = {}; __export(index_exports, { ComponentDataStorage: () => ComponentDataStorage, ComponentState: () => ComponentState, ManipulationType: () => ManipulationType, MemoizedDataStorage: () => MemoizedDataStorage, NgSelectHelper: () => NgSelectHelper, NgSelectPagedDataResponse: () => NgSelectPagedDataResponse, NgSelectPagedDataResponseZodSchema: () => NgSelectPagedDataResponseZodSchema, PagedDataResponseZodSchema: () => PagedDataResponseZodSchema, PrimeNgDynamicTableStateHelper: () => PrimeNgDynamicTableStateHelper, PrimeNgPagedDataTableStateHelper: () => PrimeNgPagedDataTableStateHelper, ReloadNotification: () => ReloadNotification, SkipLoadingSpinner: () => SkipLoadingSpinner, binarySearch: () => binarySearch, cleanNullishFromObject: () => cleanNullishFromObject, createBooleanColumn: () => createBooleanColumn, createBooleanSelectItems: () => createBooleanSelectItems, createDateColumn: () => createDateColumn, createDropdownColumn: () => createDropdownColumn, createKeyData: () => createKeyData, createMultiselectColumn: () => createMultiselectColumn, createNumericColumn: () => createNumericColumn, createPrimengNumberMatchModes: () => createPrimengNumberMatchModes, createPrimengStringMatchModes: () => createPrimengStringMatchModes, createSimpleColumn: () => createSimpleColumn, createStatusSelectItems: () => createStatusSelectItems, createTextColumn: () => createTextColumn, dynamicQueryResponseZodSchema: () => dynamicQueryResponseZodSchema, emptyCallback: () => emptyCallback, hasNullishInObject: () => hasNullishInObject, initNgSelect: () => initNgSelect, isApiResponse: () => isApiResponse, isDynamicQueryResponse: () => isDynamicQueryResponse, isPaginatedResponse: () => isPaginatedResponse, isSimplePagedResponse: () => isSimplePagedResponse, mergeTableHeaders: () => mergeTableHeaders, nullableKeyData: () => nullableKeyData, routeParamConcat: () => routeParamConcat }); module.exports = __toCommonJS(index_exports); // src/dynamic-table-state-helper.ts var import_http2 = require("@angular/common/http"); var import_core = require("@angular/core"); var import_signals = require("@ngrx/signals"); var import_rxjs = require("rxjs"); // src/http-context-tokens.ts var import_http = require("@angular/common/http"); var SkipLoadingSpinner = new import_http.HttpContextToken(() => false); // src/types.ts var import_zod = require("zod"); var ManipulationType = /* @__PURE__ */ ((ManipulationType2) => { ManipulationType2["Create"] = "Create"; ManipulationType2["Update"] = "Update"; ManipulationType2["CreateChild"] = "Create Child"; ManipulationType2["Delete"] = "Delete"; ManipulationType2["View"] = "View"; ManipulationType2["Save"] = "Save"; return ManipulationType2; })(ManipulationType || {}); var dynamicQueryResponseZodSchema = import_zod.z.object({ data: import_zod.z.any().array(), last_page: import_zod.z.number(), last_row: import_zod.z.number() }); var PagedDataResponseZodSchema = import_zod.z.object({ payload: import_zod.z.any().array(), totalCount: import_zod.z.number() }); function createKeyData(key, data) { return { key, data }; } function isApiResponse(response) { return typeof response === "object" && response !== null && "data" in response && "status" in response && "success" in response; } function isPaginatedResponse(response) { return typeof response === "object" && response !== null && "data" in response && Array.isArray(response.data) && "meta" in response && typeof response.meta === "object"; } function isSimplePagedResponse(response) { return typeof response === "object" && response !== null && "payload" in response && Array.isArray(response.payload) && "totalCount" in response && typeof response.totalCount === "number"; } function isDynamicQueryResponse(response) { return typeof response === "object" && response !== null && "data" in response && Array.isArray(response.data) && "last_page" in response && "last_row" in response; } // src/utils.ts function cleanNullishFromObject(o) { return Object.fromEntries(Object.entries(o).filter(([, v]) => v != null)); } function hasNullishInObject(obj) { return Object.values(obj).some((val) => val === null || val === void 0); } function routeParamConcat(baseUrl, routeParam) { if (routeParam === void 0 || routeParam === null) { throw new Error("routeParam cannot be null or undefined"); } if (baseUrl.endsWith("/")) { return baseUrl.concat(routeParam.toString()); } return baseUrl.concat(`/${routeParam.toString()}`); } function binarySearch(arr, val) { if (arr.length === 0) { return -1; } let left = 0; let right = arr.length - 1; while (left <= right) { const mid = Math.floor((left + right) / 2); if (arr[mid] === val) { return mid; } else if (arr[mid] < val) { left = mid + 1; } else { right = mid - 1; } } return -1; } function emptyCallback() { } function nullableKeyData(key, data) { if (key && data) { return { key, data }; } return null; } var ReloadNotification = class _ReloadNotification { static create() { return new _ReloadNotification(); } }; // src/dynamic-table-state-helper.ts function initialDynamicState() { return { data: [], isLoading: false, totalRecords: 0, size: 15, page: 1, filter: [], sort: [] }; } var _uniqueKey, _queryParams; var _PrimeNgDynamicTableStateHelper = class _PrimeNgDynamicTableStateHelper { constructor(url, httpClient, skipLoadingSpinner = true) { this.url = url; this.httpClient = httpClient; this.state = (0, import_signals.signalState)(initialDynamicState()); __privateAdd(this, _uniqueKey, (0, import_core.signal)("id")); this.uniqueKey = __privateGet(this, _uniqueKey).asReadonly(); __privateAdd(this, _queryParams, {}); // Public readonly signals this.totalRecords = this.state.totalRecords; this.isLoading = this.state.isLoading; this.data = this.state.data; this.urlWithOutRouteParam = url; this.skipLoadingSpinner = skipLoadingSpinner; } /** * Creates a new instance of PrimeNgDynamicTableStateHelper * @param options - Configuration options * @returns New instance of PrimeNgDynamicTableStateHelper */ static create(options) { return new _PrimeNgDynamicTableStateHelper( options.url, options.httpClient, options.skipLoadingSpinner ?? true ); } /** * Sets whether to skip the loading spinner * @param skip - Whether to skip the loading spinner * @returns This instance for method chaining */ setSkipLoadingSpinner(skip) { this.skipLoadingSpinner = skip; return this; } /** * Sets the unique key field for table rows * @param newUniqueKey - The field name to use as unique identifier * @returns This instance for method chaining */ setUniqueKey(newUniqueKey) { __privateGet(this, _uniqueKey).set(newUniqueKey); return this; } /** * Updates the API URL * @param newUrl - The new API URL * @returns This instance for method chaining */ setUrl(newUrl) { this.url = newUrl; this.urlWithOutRouteParam = newUrl; return this; } /** * Appends a route parameter to the URL * @param newRouteParam - The route parameter to append * @returns This instance for method chaining */ setRouteParam(newRouteParam) { this.url = routeParamConcat(this.urlWithOutRouteParam, newRouteParam); return this; } /** * Patches existing query parameters * @param value - Query parameters to merge * @returns This instance for method chaining */ patchQueryParams(value) { __privateSet(this, _queryParams, { ...__privateGet(this, _queryParams), ...value }); return this; } /** * Removes a specific query parameter * @param key - The key to remove * @returns This instance for method chaining */ removeQueryParam(key) { delete __privateGet(this, _queryParams)[key]; return this; } /** * Sets all query parameters (replaces existing) * @param newQueryParams - New query parameters * @returns This instance for method chaining */ setQueryParams(newQueryParams) { __privateSet(this, _queryParams, newQueryParams); return this; } /** * Handles PrimeNG table lazy load events * @param event - The lazy load event from PrimeNG table */ async onLazyLoad(event) { if (this.isLoading()) { return; } (0, import_signals.patchState)(this.state, { size: event.rows || 15, page: Math.floor((event.first || 0) / (event.rows || 15)) + 1, filter: this.filterMapper(event.filters || {}), sort: Object.keys(event.multiSortMeta || {}).length > 0 ? (event.multiSortMeta || []).map((sort) => ({ field: sort.field, dir: sort.order === 1 ? "asc" : "desc" })) : event.sortField ? [ { field: event.sortField, dir: (event.sortOrder || 1) === 1 ? "asc" : "desc" } ] : [] }); await this.fetchData(this.dtoBuilder()); } /** * Clears table data and resets to first page * @param table - Optional PrimeNG Table reference to reset */ async clearTableData(table) { if (this.isLoading()) { return; } (0, import_signals.patchState)(this.state, { data: [], totalRecords: 0, page: 1, filter: [], sort: [] }); if (table) { table.reset(); } await this.fetchData(this.dtoBuilder()); } /** * Manually triggers data refresh with current state */ async refresh() { if (this.isLoading()) { return; } await this.fetchData(this.dtoBuilder()); } /** * Fetches data from the API */ async fetchData(dto) { if (this.isLoading()) { return; } try { (0, import_signals.patchState)(this.state, { isLoading: true }); const params = new URLSearchParams(); Object.entries(__privateGet(this, _queryParams)).forEach(([key, value]) => { params.append(key, String(value)); }); const urlWithParams = params.toString() ? `${this.url}?${params.toString()}` : this.url; const response = await (0, import_rxjs.firstValueFrom)( this.httpClient.post(urlWithParams, dto, { context: new import_http2.HttpContext().set(SkipLoadingSpinner, this.skipLoadingSpinner) }) ); const validatedResponse = dynamicQueryResponseZodSchema.parse(response); (0, import_signals.patchState)(this.state, { data: validatedResponse.data, totalRecords: validatedResponse.last_row, isLoading: false }); } catch (error) { (0, import_signals.patchState)(this.state, { data: [], totalRecords: 0, isLoading: false }); throw error; } } /** * Builds the DTO for API requests */ dtoBuilder() { return { size: this.state.size(), page: this.state.page(), filter: this.state.filter(), sort: this.state.sort() }; } /** * Maps PrimeNG filters to API filter format */ filterMapper(dto) { const filters = []; Object.entries(dto).forEach(([field, filterData]) => { if (!filterData) return; const processFilter = (filter) => { if (filter.value === null || filter.value === void 0 || filter.value === "") return; const mappedType = this.evaluateInput(filter.matchMode || "contains"); if (mappedType) { filters.push({ field, value: String(filter.value), type: mappedType }); } }; if (Array.isArray(filterData)) { filterData.forEach(processFilter); } else { processFilter(filterData); } }); return filters; } /** * Maps PrimeNG filter match modes to API filter types */ evaluateInput(input) { const filterMap = { startsWith: "starts", notStartsWith: "!starts", endsWith: "ends", notEndsWith: "!ends", contains: "like", notContains: "!like", equals: "=", notEquals: "!=", greaterThan: ">", lessThan: "<", greaterThanOrEqual: ">=", lessThanOrEqual: "<=" }; return filterMap[input] || null; } }; _uniqueKey = new WeakMap(); _queryParams = new WeakMap(); var PrimeNgDynamicTableStateHelper = _PrimeNgDynamicTableStateHelper; // src/paged-table-state-helper.ts var import_http3 = require("@angular/common/http"); var import_core2 = require("@angular/core"); var import_signals2 = require("@ngrx/signals"); var import_rxjs2 = require("rxjs"); function initialPagedState() { return { data: [], isLoading: false, totalRecords: 0, limit: 15, page: 1 }; } var _state, _uniqueKey2, _queryParams2; var _PrimeNgPagedDataTableStateHelper = class _PrimeNgPagedDataTableStateHelper { constructor(url, httpClient, skipLoadingSpinner = true) { this.url = url; this.httpClient = httpClient; __privateAdd(this, _state, (0, import_signals2.signalState)(initialPagedState())); __privateAdd(this, _uniqueKey2, (0, import_core2.signal)("id")); this.uniqueKey = __privateGet(this, _uniqueKey2).asReadonly(); __privateAdd(this, _queryParams2, {}); // Public readonly signals this.totalRecords = __privateGet(this, _state).totalRecords; this.isLoading = __privateGet(this, _state).isLoading; this.data = __privateGet(this, _state).data; this.currentPage = __privateGet(this, _state).page; this.currentPageSize = __privateGet(this, _state).limit; this.urlWithOutRouteParam = url; this.skipLoadingSpinner = skipLoadingSpinner; } /** * Creates a new instance of PrimengPagedDataTableStateHelper * @param option - Configuration options * @returns New instance of PrimengPagedDataTableStateHelper */ static create(option) { return new _PrimeNgPagedDataTableStateHelper( option.url, option.httpClient, option.skipLoadingSpinner ?? true ); } /** * Creates a new instance without initial URL (can be set later) * @param option - Configuration options without URL * @returns New instance of PrimengPagedDataTableStateHelper */ static createWithBlankUrl(option) { return new _PrimeNgPagedDataTableStateHelper( "", option.httpClient, option.skipLoadingSpinner ?? true ); } /** * Sets whether to skip the loading spinner * @param skip - Whether to skip the loading spinner * @returns This instance for method chaining */ setSkipLoadingSpinner(skip) { this.skipLoadingSpinner = skip; return this; } /** * Sets the unique key field for table rows * @param newUniqueKey - The field name to use as unique identifier * @returns This instance for method chaining */ setUniqueKey(newUniqueKey) { __privateGet(this, _uniqueKey2).set(newUniqueKey); return this; } /** * Updates the API URL * @param newUrl - The new API URL * @returns This instance for method chaining */ setUrl(newUrl) { this.url = newUrl; this.urlWithOutRouteParam = newUrl; return this; } /** * Appends a route parameter to the URL * @param newRouteParam - The route parameter to append * @returns This instance for method chaining */ setRouteParam(newRouteParam) { this.url = routeParamConcat(this.urlWithOutRouteParam, newRouteParam); return this; } /** * Patches existing query parameters * @param value - Query parameters to merge * @returns This instance for method chaining */ patchQueryParams(value) { __privateSet(this, _queryParams2, { ...__privateGet(this, _queryParams2), ...value }); return this; } /** * Removes a specific query parameter * @param key - The key to remove * @returns This instance for method chaining */ removeQueryParam(key) { delete __privateGet(this, _queryParams2)[key]; return this; } /** * Removes all query parameters * @returns This instance for method chaining */ removeAllQueryParams() { __privateSet(this, _queryParams2, {}); return this; } /** * Sets all query parameters (replaces existing) * @param newQueryParams - New query parameters * @returns This instance for method chaining */ setQueryParams(newQueryParams) { __privateSet(this, _queryParams2, newQueryParams); return this; } /** * Handles PrimeNG table lazy load events * @param event - The lazy load event from PrimeNG table */ async onLazyLoad(event) { if (this.isLoading()) { return; } const newPage = Math.floor((event.first || 0) / (event.rows || 15)) + 1; const newLimit = event.rows || 15; (0, import_signals2.patchState)(__privateGet(this, _state), { limit: newLimit, page: newPage }); await this.fetchData(this.dtoBuilder()); } /** * Clears table data and resets to first page * @param table - Optional PrimeNG Table reference to reset */ async clearTableData(table) { if (this.isLoading()) { return; } (0, import_signals2.patchState)(__privateGet(this, _state), { data: [], totalRecords: 0, page: 1 }); if (table) { table.reset(); } await this.fetchData(this.dtoBuilder()); } /** * Manually triggers data refresh with current state */ async refresh() { if (this.isLoading()) { return; } await this.fetchData(this.dtoBuilder()); } /** * Fetches data from the API */ async fetchData(dto) { if (this.isLoading()) { return; } try { (0, import_signals2.patchState)(__privateGet(this, _state), { isLoading: true }); const params = new URLSearchParams(); Object.entries(__privateGet(this, _queryParams2)).forEach(([key, value]) => { params.append(key, String(value)); }); Object.entries(dto).forEach(([key, value]) => { params.append(key, String(value)); }); const urlWithParams = params.toString() ? `${this.url}?${params.toString()}` : this.url; const response = await (0, import_rxjs2.firstValueFrom)( this.httpClient.get(urlWithParams, { context: new import_http3.HttpContext().set(SkipLoadingSpinner, this.skipLoadingSpinner) }) ); const validatedResponse = PagedDataResponseZodSchema.parse(response); (0, import_signals2.patchState)(__privateGet(this, _state), { data: validatedResponse.payload, totalRecords: validatedResponse.totalCount, isLoading: false }); } catch (error) { (0, import_signals2.patchState)(__privateGet(this, _state), { data: [], totalRecords: 0, isLoading: false }); throw error; } } /** * Builds the DTO for API requests */ dtoBuilder() { return { limit: __privateGet(this, _state).limit(), page: __privateGet(this, _state).page() }; } }; _state = new WeakMap(); _uniqueKey2 = new WeakMap(); _queryParams2 = new WeakMap(); var PrimeNgPagedDataTableStateHelper = _PrimeNgPagedDataTableStateHelper; // src/table-utils.ts function createPrimengNumberMatchModes(styleClass = "p-text-capitalize", disabled = false) { return [ { label: "Equals", value: "equals", title: "Equals", styleClass, disabled }, { label: "Not Equals", value: "notEquals", title: "Not Equals", styleClass, disabled }, { label: "Greater Than", value: "greaterThan", title: "Greater Than", styleClass, disabled }, { label: "Greater Than Or Equals", value: "greaterThanOrEqual", title: "Greater Than Or Equals", styleClass, disabled }, { label: "Less Than", value: "lessThan", title: "Less Than", styleClass, disabled }, { label: "Less Than Or Equals", value: "lessThanOrEqual", title: "Less Than Or Equals", styleClass, disabled } ]; } function createPrimengStringMatchModes(styleClass = "p-text-capitalize", disabled = false) { return [ { label: "Contains", value: "contains", title: "Contains", styleClass, disabled }, { label: "Not Contains", value: "notContains", title: "Not Contains", styleClass, disabled }, { label: "Starts With", value: "startsWith", title: "Starts With", styleClass, disabled }, { label: "Not Starts With", value: "notStartsWith", title: "Not Starts With", styleClass, disabled }, { label: "Ends With", value: "endsWith", title: "Ends With", styleClass, disabled }, { label: "Not Ends With", value: "notEndsWith", title: "Not Ends With", styleClass, disabled } ]; } function createTextColumn(field, label, options = {}) { const header = { identifier: { label, field, isNested: options.isNested, hasSort: options.hasSort ?? false, styleClass: options.styleClass } }; if (options.hasFilter ?? false) { header.filter = { type: "text", placeholder: options.placeholder ?? `Search by ${label.toLowerCase()}`, matchModeOptions: options.matchModeOptions ?? createPrimengStringMatchModes(), defaultMatchMode: options.defaultMatchMode ?? "contains", ariaLabel: `Filter by ${label}`, styleClass: options.filterStyleClass }; } return header; } function createNumericColumn(field, label, options = {}) { const header = { identifier: { label, field, isNested: options.isNested, hasSort: options.hasSort ?? false, styleClass: options.styleClass } }; if (options.hasFilter ?? false) { header.filter = { type: "numeric", placeholder: options.placeholder ?? `Filter by ${label.toLowerCase()}`, matchModeOptions: options.matchModeOptions ?? createPrimengNumberMatchModes(), defaultMatchMode: options.defaultMatchMode ?? "equals", ariaLabel: `Filter by ${label}`, styleClass: options.filterStyleClass }; } return header; } function createBooleanColumn(field, label, options = {}) { const header = { identifier: { label, field, isNested: options.isNested, hasSort: options.hasSort ?? false, isBoolean: true, styleClass: options.styleClass } }; if (options.hasFilter ?? false) { header.filter = { type: "boolean", defaultMatchMode: "equals", ariaLabel: `Filter by ${label}`, styleClass: options.filterStyleClass }; } return header; } function createDateColumn(field, label, options = {}) { const header = { identifier: { label, field, isNested: options.isNested, hasSort: options.hasSort ?? false, styleClass: options.styleClass } }; if (options.hasFilter ?? false) { header.filter = { type: "date", placeholder: options.placeholder ?? `Select ${label.toLowerCase()}`, defaultMatchMode: "equals", ariaLabel: `Filter by ${label}`, styleClass: options.filterStyleClass }; } return header; } function createDropdownColumn(field, label, dropdownOptions, options = {}) { const header = { identifier: { label, field, hasSort: options.hasSort ?? false, styleClass: options.styleClass } }; if (options.hasFilter ?? false) { header.filter = { type: "dropdown", placeholder: options.placeholder ?? `Select ${label.toLowerCase()}`, matchModeOptions: dropdownOptions, defaultMatchMode: "equals", ariaLabel: `Filter by ${label}`, styleClass: options.filterStyleClass }; } return header; } function createMultiselectColumn(field, label, multiselectOptions, options = {}) { const header = { identifier: { label, field, hasSort: options.hasSort ?? false, styleClass: options.styleClass } }; if (options.hasFilter ?? false) { header.filter = { type: "multiselect", placeholder: options.placeholder ?? `Select ${label.toLowerCase()}`, matchModeOptions: multiselectOptions, defaultMatchMode: "equals", ariaLabel: `Filter by ${label}`, styleClass: options.filterStyleClass }; } return header; } function createSimpleColumn(field, label, options = {}) { return { identifier: { label, field, isNested: options.isNested, hasSort: options.hasSort ?? false, styleClass: options.styleClass } }; } function mergeTableHeaders(...headers) { return headers; } function createBooleanSelectItems(trueLabel = "Yes", falseLabel = "No") { return [ { label: trueLabel, value: true }, { label: falseLabel, value: false } ]; } function createStatusSelectItems(statusOptions) { return Object.entries(statusOptions).map(([value, label]) => ({ label, value: isNaN(Number(value)) ? value : Number(value) })); } // src/memoized-data-storage.ts var import_core3 = require("@angular/core"); var import_http4 = require("@angular/common/http"); var import_rxjs3 = require("rxjs"); var _singleData, _multipleData, _isLoading, _isMemoizationDisabledOnNextRead; var MemoizedDataStorage = class { /** * Creates a new instance of MemoizedDataStorage * @param httpClient Angular HttpClient instance for making HTTP requests * @param skipLoadingSpinner Whether to skip the loading spinner for HTTP requests */ constructor(httpClient, skipLoadingSpinner = true) { this.httpClient = httpClient; this.skipLoadingSpinner = true; __privateAdd(this, _singleData, (0, import_core3.signal)(null)); __privateAdd(this, _multipleData, (0, import_core3.signal)([])); __privateAdd(this, _isLoading, (0, import_core3.signal)(false)); // Public readonly signals for external consumption /** * Read-only signal containing single data object or null */ this.singleData = __privateGet(this, _singleData).asReadonly(); /** * Read-only signal containing array of data objects */ this.multipleData = __privateGet(this, _multipleData).asReadonly(); /** * Read-only signal indicating whether a request is currently loading */ this.isLoading = __privateGet(this, _isLoading).asReadonly(); // Private flag to control memoization behavior __privateAdd(this, _isMemoizationDisabledOnNextRead, false); this.skipLoadingSpinner = skipLoadingSpinner; } /** * Sets whether to skip the loading spinner for HTTP requests * @param skip Whether to skip the loading spinner * @returns This instance for method chaining */ setSkipLoadingSpinner(skip) { this.skipLoadingSpinner = skip; return this; } /** * Disables memoization for the next read operation and clears cached data * This forces the next loadSingleData or loadMultipleData call to fetch fresh data * * @example * ```typescript * const storage = new MemoizedDataStorage<User>(httpClient); * await storage.loadSingleData('/api/user/1'); // Fetches data * await storage.loadSingleData('/api/user/1'); // Returns cached data * * storage.disableMemoizationOnNextRead(); * await storage.loadSingleData('/api/user/1'); // Fetches fresh data * ``` */ disableMemoizationOnNextRead() { __privateSet(this, _isMemoizationDisabledOnNextRead, true); __privateGet(this, _singleData).set(null); __privateGet(this, _multipleData).set([]); } /** * Loads a single data object from the specified URL with optional query parameters * Uses memoization to avoid redundant requests unless explicitly disabled * * @param url The URL to fetch data from * @param queryParams Optional query parameters to include in the request * @returns Promise that resolves when the data is loaded * @throws Error if the HTTP request fails * * @example * ```typescript * const storage = new MemoizedDataStorage<User>(httpClient); * await storage.loadSingleData('/api/user/1', { include: 'profile' }); * const user = storage.singleData(); // User data or null * ``` */ async loadSingleData(url, queryParams = {}) { if (!__privateGet(this, _isMemoizationDisabledOnNextRead) && __privateGet(this, _singleData).call(this) !== null) { return; } try { __privateGet(this, _isLoading).set(true); const data = await (0, import_rxjs3.firstValueFrom)( this.httpClient.get(url, { params: queryParams, context: new import_http4.HttpContext().set(SkipLoadingSpinner, this.skipLoadingSpinner) }) ); __privateGet(this, _singleData).set(data); } catch (error) { __privateGet(this, _singleData).set(null); throw error; } finally { __privateGet(this, _isLoading).set(false); __privateSet(this, _isMemoizationDisabledOnNextRead, false); } } /** * Loads multiple data objects from the specified URL with optional query parameters * Uses memoization to avoid redundant requests unless explicitly disabled * * @param url The URL to fetch data from * @param queryParams Optional query parameters to include in the request * @returns Promise that resolves when the data is loaded * @throws Error if the HTTP request fails * * @example * ```typescript * const storage = new MemoizedDataStorage<User>(httpClient); * await storage.loadMultipleData('/api/users', { page: 1, limit: 10 }); * const users = storage.multipleData(); // Array of User data * ``` */ async loadMultipleData(url, queryParams = {}) { if (!__privateGet(this, _isMemoizationDisabledOnNextRead) && __privateGet(this, _multipleData).call(this).length !== 0) { return; } try { __privateGet(this, _isLoading).set(true); const context = new import_http4.HttpContext(); if (this.skipLoadingSpinner) { context.set(SkipLoadingSpinner, true); } const data = await (0, import_rxjs3.firstValueFrom)( this.httpClient.get(url, { params: queryParams, context }) ); __privateGet(this, _multipleData).set(Array.isArray(data) ? data : []); } catch (error) { __privateGet(this, _multipleData).set([]); throw error; } finally { __privateGet(this, _isLoading).set(false); __privateSet(this, _isMemoizationDisabledOnNextRead, false); } } /** * Clears all cached data and resets the storage to initial state * * @example * ```typescript * const storage = new MemoizedDataStorage<User>(httpClient); * await storage.loadSingleData('/api/user/1'); * storage.clear(); // Clears cached data * console.log(storage.singleData()); // null * console.log(storage.multipleData()); // [] * ``` */ clear() { __privateGet(this, _singleData).set(null); __privateGet(this, _multipleData).set([]); __privateSet(this, _isMemoizationDisabledOnNextRead, false); } /** * Checks if single data is currently cached * @returns true if single data is cached, false otherwise */ hasSingleData() { return __privateGet(this, _singleData).call(this) !== null; } /** * Checks if multiple data is currently cached * @returns true if multiple data is cached (non-empty array), false otherwise */ hasMultipleData() { return __privateGet(this, _multipleData).call(this).length > 0; } }; _singleData = new WeakMap(); _multipleData = new WeakMap(); _isLoading = new WeakMap(); _isMemoizationDisabledOnNextRead = new WeakMap(); // src/component-state.ts var import_core4 = require("@angular/core"); var ComponentState = class { constructor() { this.isAjaxDataIncoming = (0, import_core4.signal)(false); this.enableCheckBoxSelection = (0, import_core4.signal)(false); this.isSelectableRowEnabled = (0, import_core4.signal)(false); this.isAjaxRequestOutgoing = (0, import_core4.signal)(false); this.hasMultipleSelection = (0, import_core4.signal)(false); this.isCreateOrUpdateDialogOpen = (0, import_core4.signal)(false); this.isUpdateDialogOpen = (0, import_core4.signal)(false); this.isCreateDialogOpen = (0, import_core4.signal)(false); this.manipulationType = (0, import_core4.signal)("Create" /* Create */); this.componentTitle = (0, import_core4.signal)(""); /** * Updates the component title * @param componentTitle - The new title for the component * @returns This instance for method chaining */ this.updateComponentTitle = (componentTitle) => { this.componentTitle.set(componentTitle); return this; }; /** * Updates the multiple selection status * @param newStatus - Whether multiple selection is enabled * @returns This instance for method chaining */ this.updateMultipleSelectionStatus = (newStatus) => { this.hasMultipleSelection.set(newStatus); return this; }; /** * Updates the checkbox selection status * @param newStatus - Whether checkbox selection is enabled * @returns This instance for method chaining */ this.updateCheckBoxSelectionStatus = (newStatus) => { this.enableCheckBoxSelection.set(newStatus); return this; }; /** * Updates the selectable row status * @param newStatus - Whether row selection is enabled * @returns This instance for method chaining */ this.updateSelectableRowStatus = (newStatus) => { this.isSelectableRowEnabled.set(newStatus); return this; }; /** * Updates the manipulation type (Create, Update, Delete, View) * @param type - The manipulation type * @returns This instance for method chaining */ this.updateManipulationType = (type) => { this.manipulationType.set(type); return this; }; /** * Sets the incoming Ajax data status * @param status - Whether Ajax data is incoming * @returns This instance for method chaining */ this.setAjaxDataIncoming = (status) => { this.isAjaxDataIncoming.set(status); return this; }; /** * Sets the outgoing Ajax request status * @param status - Whether Ajax request is outgoing * @returns This instance for method chaining */ this.setAjaxRequestOutgoing = (status) => { this.isAjaxRequestOutgoing.set(status); return this; }; /** * Sets the create or update dialog open status * @param status - Whether the dialog is open * @returns This instance for method chaining */ this.setCreateOrUpdateDialogOpen = (status) => { this.isCreateOrUpdateDialogOpen.set(status); return this; }; /** * Sets the update dialog open status * @param status - Whether the update dialog is open * @returns This instance for method chaining */ this.setUpdateDialogOpen = (status) => { this.isUpdateDialogOpen.set(status); return this; }; /** * Sets the create dialog open status * @param status - Whether the create dialog is open * @returns This instance for method chaining */ this.setCreateDialogOpen = (status) => { this.isCreateDialogOpen.set(status); return this; }; /** * Computed signal that combines component title with manipulation type */ this.componentTitleWithManipulationType = (0, import_core4.computed)(() => { return this.manipulationType() + " " + this.componentTitle(); }); /** * Computed signal that indicates if component is in update state */ this.isOnUpdateState = (0, import_core4.computed)(() => { return this.manipulationType() === "Update" /* Update */; }); /** * Computed signal that indicates if component is in create state */ this.isOnCreateState = (0, import_core4.computed)(() => { return this.manipulationType() === "Create" /* Create */; }); /** * Computed signal that indicates if component is in delete state */ this.isOnDeleteState = (0, import_core4.computed)(() => { return this.manipulationType() === "Delete" /* Delete */; }); /** * Computed signal that indicates if component is in view state */ this.isOnViewState = (0, import_core4.computed)(() => { return this.manipulationType() === "View" /* View */; }); /** * Computed signal that indicates if any Ajax operation is currently running */ this.isAnyAjaxOperationRunning = (0, import_core4.computed)(() => { return this.isAjaxDataIncoming() || this.isAjaxRequestOutgoing(); }); /** * Computed signal that indicates if any dialog is open */ this.isAnyDialogOpen = (0, import_core4.computed)(() => { return this.isCreateOrUpdateDialogOpen() || this.isUpdateDialogOpen() || this.isCreateDialogOpen(); }); /** * Resets all state to default values * @returns This instance for method chaining */ this.reset = () => { this.isAjaxDataIncoming.set(false); this.enableCheckBoxSelection.set(false); this.isSelectableRowEnabled.set(false); this.isAjaxRequestOutgoing.set(false); this.hasMultipleSelection.set(false); this.isCreateOrUpdateDialogOpen.set(false); this.isUpdateDialogOpen.set(false); this.isCreateDialogOpen.set(false); this.manipulationType.set("Create" /* Create */); this.componentTitle.set(""); return this; }; } }; // src/component-data-storage.ts var import_core5 = require("@angular/core"); var ComponentDataStorage = class { constructor() { this.singleData = (0, import_core5.signal)(null); this.multipleData = (0, import_core5.signal)([]); } /** * Patches multiple data by appending new data to the existing array * @param newData - Array of new data to append * @returns This instance for method chaining * * @example * ```typescript * const storage = new ComponentDataStorage<User>(); * storage.updateMultipleData([{ id: 1, name: 'John' }]); * storage.patchMultipleData([{ id: 2, name: 'Jane' }]); * // Result: [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }] * ``` */ patchMultipleData(newData) { this.multipleData.update((prevData) => { return [...prevData, ...newData]; }); return this; } /** * Patches single data by merging new properties with existing data * If no existing data, creates new object with provided data * @param newData - Partial data to merge with existing single data * @returns This instance for method chaining * * @example * ```typescript * const storage = new ComponentDataStorage<User>(); * storage.updateSingleData({ id: 1, name: 'John', email: 'john@example.com' }); * storage.patchSingleData({ email: 'john.doe@example.com' }); * // Result: { id: 1, name: 'John', email: 'john.doe@example.com' } * ``` */ patchSingleData(newData) { this.singleData.update((prev) => prev ? { ...prev, ...newData } : { ...newData }); return this; } /** * Replaces the entire multiple data array * @param newData - New array of data to replace existing data * @returns This instance for method chaining */ updateMultipleData(newData) { this.multipleData.set(newData); return this; } /** * Replaces the single data object * @param newData - New data object or null to replace existing data * @returns This instance for method chaining */ updateSingleData(newData) { this.singleData.set(newData); return this; } /** * Adds a single item to the multiple data array * @param item - Single item to add to the array * @returns This instance for method chaining */ addToMultipleData(item) { this.multipleData.update((prevData) => [...prevData, item]); return this; } /** * Removes an item from the multiple data array based on a predicate function * @param predicate - Function that returns true for items to remove * @returns This instance for method chaining * * @example * ```typescript * storage.removeFromMultipleData(user => user.id === 1); * ``` */ removeFromMultipleData(predicate) { this.multipleData.update((prevData) => prevData.filter((item) => !predicate(item))); return this; } /** * Updates an item in the multiple data array based on a predicate function * @param predicate - Function that returns true for items to update * @param updateFn - Function that returns the updated item * @returns This instance for method chaining * * @example * ```typescript * storage.updateItemInMultipleData( * user => user.id === 1, * user => ({ ...user, name: 'Updated Name' }) * ); * ``` */ updateItemInMultipleData(predicate, updateFn) { this.multipleData.update( (prevData) => prevData.map((item) => predicate(item) ? updateFn(item) : item) ); return this; } /** * Clears all data (both single and multiple) * @returns This instance for method chaining */ clearAll() { this.singleData.set(null); this.multipleData.set([]); return this; } /** * Clears only the single data * @returns This instance for method chaining */ clearSingleData() { this.singleData.set(null); return this; } /** * Clears only the multiple data * @returns This instance for method chaining */ clearMultipleData() { this.multipleData.set([]); return this; } /** * Checks if single data exists (is not null) * @returns true if single data exists, false otherwise */ hasSingleData() { return this.singleData() !== null; } /** * Checks if multiple data has items * @returns true if multiple data array has items, false if empty */ hasMultipleData() { return this.multipleData().length > 0; } /** * Gets the count of items in multiple data * @returns Number of items in the multiple data array */ getMultipleDataCount() { return this.multipleData().length; } /** * Finds an item in the multiple data array * @param predicate - Function that returns true for the item to find * @returns The found item or undefined */ findInMultipleData(predicate) { return this.multipleData().find(predicate); } /** * Checks if an item exists in the multiple data array * @param predicate - Function that returns true for the item to check * @returns true if item exists, false otherwise */ existsInMultipleData(predicate) { return this.multipleData().some(predicate); } }; // src/ng-select-helper.ts var import_http5 = require("@angular/common/http"); var import_core6 = require("@angular/core"); var import_rxjs4 = require("rxjs"); var import_zod2 = require("zod"); var NgSelectPagedDataResponse = class { constructor(payload, totalCount) { this.payload = payload; this.totalCount = totalCount; } }; var NgSelectPagedDataResponseZodSchema = import_zod2.z.object({ payload: import_zod2.z.any().array(), totalCount: import_zod2.z.number() }); var defaultResetOpts = { resetQueryParams: false, resetBody: false, resetCache: false }; var _cache, _originalAjaxUrl, _queryParams3, _body, _initDone, _searchText, _limit, _page, _debounceTimeInSec, _totalCount, _isLastApiCallSuccessful, _limitReached, _loadMoreDataSubject, _ajaxErrorSubject, _loadedData, _isLoading2; var _NgSelectHelper = class _NgSelectHelper { constructor(ajaxUrl, httpClient, destroyRef, usePostRequest = false, limit = 50, useCache = true, skipLoadingSpinner = true) { this.ajaxUrl = ajaxUrl; this.httpClient = httpClient; this.destroyRef = destroyRef; this.usePostRequest = usePostRequest; this.useCache = useCache; this.skipLoadingSpinner = skipLoadingSpinner; __privateAdd(this, _cache, /* @__PURE__ */ new Map()); __privateAdd(this, _originalAjaxUrl); __privateAdd(this, _queryParams3, {}); __privateAdd(this, _body, {}); __privateAdd(this, _initDone, false); __privateAdd(this, _searchText, ""); __privateAdd(this, _limit); __privateAdd(this, _page, 1); __privateAdd(this, _debounceTimeInSec, 1); __privateAdd(this, _totalCount, -1); __privateAdd(this, _isLastApiCallSuccessful, true); __privateAdd(this, _limitReached, false); __privateAdd(this, _loadMoreDataSubject, new import_rxjs4.Subject()); this.inputSubject = new import_rxjs4.Subject(); __privateAdd(this, _ajaxErrorSubject, new import_rxjs4.Subject()); this.ajaxError$ = __privateGet(this, _ajaxErrorSubject).asObservable(); __privateAdd(this, _loadedData, (0, import_core6.signal)( new NgSelectPagedDataResponse([], 0) )); this.loadedData = __privateGet(this, _loadedData).asReadonly(); __privateAdd(this, _isLoading2, (0, import_core6.signal)(false)); this.isLoading = __privateGet(this, _isLoading2).asReadonly(); this.runningApiReq = null; __privateSet(this, _originalAjaxUrl, ajaxUrl); __privateSet(this, _limit, limit > 0 ? limit : 50); this.destroyRef.onDestroy(() => { __privateGet(this, _ajaxErrorSubject).complete(); this.inputSubject.complete(); __privateGet(this, _loadMoreDataSubject).complete(); __privateGet(this, _cache).clear(); if (this.runningApiReq && !this.runningApiReq.closed) { this.runningApiReq.unsubscribe(); } }); } /** * Creates a new instance of NgSelectHelper * @param options Configuration options * @returns New NgSelectHelper instance */ static create({ ajaxUrl, httpClient, destroyRef, usePostRequest, limit = 50, useCache = true, skipLoadingSpinner = true }) { return new _NgSelectHelper( ajaxUrl, httpClient, destroyRef, usePostRequest, limit, useCache, skipLoadingSpinner ); } /** * Sets whether to skip the loading spinner for HTTP requests * @param skip Whether to skip the loading spinner * @returns This instance for method chaining */ setSkipLoadingSpinner(skip) { this.skipLoadingSpinner = skip; return this; } /** * Sets the debounce time for search input in seconds * @param debounceTimeInSecond Debounce time in seconds * @returns This instance for method chaining */ setDebounceTimeInSecond(debounceTimeInSecond) { __privateSet(this, _debounceTimeInSec, debounceTimeInSecond > 0 ? debounceTimeInSecond : 1); return this; } /** * Patches the request body (only works with POST requests) * @param value Body data to merge * @returns This instance for method chaining */ patchBody(value) { if (this.usePostRequest) { this.resetAll(); __privateSet(this, _body, Object.assign(__privateGet(this, _body), value)); } return this; } /** * Sets the request body (only works with POST requests) * @param newBody New body data * @returns This instance for method chaining */ setBody(newBody) { if (this.usePostRequest) { this.resetAll(); __privateSet(this, _body, newBody); } return this; } /** * Clears the internal cache * @returns This instance for method chaining */ clearCache() { __privateGet(this, _cache).clear(); return this; } /** * Sets route parameters for the URL * @param newRouteParam Route parameter to append * @returns This instance for method chaining */ setRouteParam(newRouteParam) { const baseUrl = __privateGet(this, _originalAjaxUrl).endsWith("/") ? __privateGet(this, _originalAjaxUrl).slice(0, -1) : __privateGet(this, _originalAjaxUrl); let routeParam = newRouteParam.startsWith("/") ? newRouteParam.slice(1) : newRouteParam; this.ajaxUrl = `${baseUrl}/${routeParam}`; return this; } /** * Patches query parameters * @param value Query parameters to merge * @returns This instance for method chaining */ patchQueryParams(value) { this.resetAll(); __privateSet(this, _queryParams3, Object.assign(__privateGet(this, _queryParams3), value)); return this; } /** * Removes a query parameter * @param key Query parameter key to remove * @returns This instance for method chaining */ removeQueryParam(key) { this.resetAll(); delete __pr