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
2,747 lines • 86.3 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
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);
// src/index.ts
var index_exports = {};
__export(index_exports, {
ComponentDataStorage: () => ComponentDataStorage,
ComponentState: () => ComponentState,
ManipulationType: () => ManipulationType,
MemoizedDataStorage: () => MemoizedDataStorage,
NgSelectHelper: () => NgSelectHelper,
NgSelectPagedDataResponse: () => NgSelectPagedDataResponse,
NullableApiResponseSchema: () => NullableApiResponseSchema,
NumberStringKeyDataSchema: () => NumberStringKeyDataSchema,
OffsetPaginatedNgSelectNetworkRequest: () => OffsetPaginatedNgSelectNetworkRequest,
OffsetPaginatedPrimeNgTableStateNetworkRequest: () => OffsetPaginatedPrimeNgTableStateNetworkRequest,
PagedDataResponseZodSchema: () => PagedDataResponseZodSchema,
PostRequestBody: () => PostRequestBody,
PrimeNgDynamicTableStateHelper: () => PrimeNgDynamicTableStateHelper,
PrimeNgPagedDataTableStateHelper: () => PrimeNgPagedDataTableStateHelper,
ReloadNotification: () => ReloadNotification,
SkipLoadingSpinner: () => SkipLoadingSpinner,
UrlBasedDynamicQuery: () => UrlBasedDynamicQuery,
binarySearch: () => binarySearch,
calculatePrimengTablePagination: () => calculatePrimengTablePagination,
cleanNullishFromObject: () => cleanNullishFromObject,
createBooleanColumn: () => createBooleanColumn,
createBooleanSelectItems: () => createBooleanSelectItems,
createDateColumn: () => createDateColumn,
createDateTimeColumn: () => createDateTimeColumn,
createDropdownColumn: () => createDropdownColumn,
createHierarchicalTree: () => createHierarchicalTree,
createKeyData: () => createKeyData,
createMultiselectColumn: () => createMultiselectColumn,
createNgSelectHelper: () => createNgSelectHelper,
createNumericColumn: () => createNumericColumn,
createPrimengNumberMatchModes: () => createPrimengNumberMatchModes,
createPrimengStringMatchModes: () => createPrimengStringMatchModes,
createSimpleColumn: () => createSimpleColumn,
createStatusSelectItems: () => createStatusSelectItems,
createTextColumn: () => createTextColumn,
createTimeColumn: () => createTimeColumn,
dynamicQueryResponseZodSchema: () => dynamicQueryResponseZodSchema,
emptyCallback: () => emptyCallback,
hasNullishInObject: () => hasNullishInObject,
initNgSelect: () => initNgSelect,
initNgSelectHelper: () => initNgSelectHelper,
isApiResponse: () => isApiResponse,
isDynamicQueryResponse: () => isDynamicQueryResponse,
isPaginatedResponse: () => isPaginatedResponse,
isSimplePagedResponse: () => isSimplePagedResponse,
mergeTableHeaders: () => mergeTableHeaders,
nullableKeyData: () => nullableKeyData,
offsetPaginatedNgSelectState: () => offsetPaginatedNgSelectState,
offsetPaginatedPrimeNgTableState: () => offsetPaginatedPrimeNgTableState,
provideOffsetPaginatedNgSelectStateConfig: () => provideOffsetPaginatedNgSelectStateConfig,
provideOffsetPaginatedPrimeNgTableStateConfig: () => provideOffsetPaginatedPrimeNgTableStateConfig,
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()
});
var NumberStringKeyDataSchema = import_zod.z.object({
key: import_zod.z.union([import_zod.z.number(), import_zod.z.string()]),
data: import_zod.z.string()
});
var NullableApiResponseSchema = (payloadSchema) => import_zod.z.object({
payload: payloadSchema.nullable()
});
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(obj) {
if (obj === void 0) {
return {};
}
return Object.fromEntries(Object.entries(obj).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();
}
};
function createHierarchicalTree(data, idKey, parentIdKey, expanded = false) {
if (!Array.isArray(data)) {
throw new Error("data must be an array");
}
const categoryMap = new Map(
data.map((item) => {
if (!Object.hasOwn(item, idKey) || !Object.hasOwn(item, parentIdKey)) {
throw new Error("idKey or parentIdKey is missing", { cause: item });
}
return [
item[idKey],
{
data: item,
children: [],
expanded
}
];
})
);
const rootNodes = [];
data.forEach((item) => {
const parentId = item[parentIdKey];
if (parentId != null && categoryMap.has(parentId)) {
const parent = categoryMap.get(parentId);
parent.children.push(categoryMap.get(item[idKey]));
} else {
rootNodes.push(categoryMap.get(item[idKey]));
}
});
return rootNodes;
}
// src/dynamic-table-state-helper.ts
function initialDynamicState() {
return {
data: [],
isLoading: false,
totalRecords: 0,
size: 15,
page: 1,
filter: [],
sort: []
};
}
function calculatePrimengTablePagination(event, defaultValue = { page: 1, limit: 15 }) {
if (!event) {
return defaultValue;
}
const page = event.first && event.rows ? Math.floor(event.first / event.rows) : 0;
const limit = event.rows ?? 15;
return { page: page + 1, limit };
}
var PrimeNgDynamicTableStateHelper = class _PrimeNgDynamicTableStateHelper {
constructor(url, httpClient, skipLoadingSpinner = true) {
this.url = url;
this.httpClient = httpClient;
this.urlWithOutRouteParam = url;
this.skipLoadingSpinner = skipLoadingSpinner;
}
state = (0, import_signals.signalState)(
initialDynamicState()
);
urlWithOutRouteParam;
skipLoadingSpinner;
#uniqueKey = (0, import_core.signal)("id");
uniqueKey = this.#uniqueKey.asReadonly();
#queryParams = {};
// Public readonly signals
totalRecords = this.state.totalRecords;
isLoading = this.state.isLoading;
data = this.state.data;
/**
* 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) {
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) {
this.#queryParams = { ...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 this.#queryParams[key];
return this;
}
/**
* Sets all query parameters (replaces existing)
* @param newQueryParams - New query parameters
* @returns This instance for method chaining
*/
setQueryParams(newQueryParams) {
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(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 = (filter3) => {
if (filter3.value === null || filter3.value === void 0 || filter3.value === "")
return;
const mappedType = this.evaluateInput(filter3.matchMode || "contains");
if (mappedType) {
filters.push({
field,
value: String(filter3.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;
}
};
// 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 PrimeNgPagedDataTableStateHelper = class _PrimeNgPagedDataTableStateHelper {
constructor(url, httpClient, skipLoadingSpinner = true) {
this.url = url;
this.httpClient = httpClient;
this.urlWithOutRouteParam = url;
this.skipLoadingSpinner = skipLoadingSpinner;
}
#state = (0, import_signals2.signalState)(initialPagedState());
urlWithOutRouteParam;
skipLoadingSpinner;
#uniqueKey = (0, import_core2.signal)("id");
uniqueKey = this.#uniqueKey.asReadonly();
#queryParams = {};
// Public readonly signals
totalRecords = this.#state.totalRecords;
isLoading = this.#state.isLoading;
data = this.#state.data;
currentPage = this.#state.page;
currentPageSize = this.#state.limit;
/**
* 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) {
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) {
this.#queryParams = { ...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 this.#queryParams[key];
return this;
}
/**
* Removes all query parameters
* @returns This instance for method chaining
*/
removeAllQueryParams() {
this.#queryParams = {};
return this;
}
/**
* Sets all query parameters (replaces existing)
* @param newQueryParams - New query parameters
* @returns This instance for method chaining
*/
setQueryParams(newQueryParams) {
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;
}
const newPage = Math.floor((event.first || 0) / (event.rows || 15)) + 1;
const newLimit = event.rows || 15;
(0, import_signals2.patchState)(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)(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)(this.#state, { isLoading: true });
const params = new URLSearchParams();
Object.entries(this.#queryParams).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)(this.#state, {
data: validatedResponse.payload,
totalRecords: validatedResponse.totalCount,
isLoading: false
});
} catch (error) {
(0, import_signals2.patchState)(this.#state, {
data: [],
totalRecords: 0,
isLoading: false
});
throw error;
}
}
/**
* Builds the DTO for API requests
*/
dtoBuilder() {
return {
limit: this.#state.limit(),
page: this.#state.page()
};
}
};
// 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,
isDate: true,
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 createDateTimeColumn(field, label, options = {}) {
const header = {
identifier: {
label,
field,
isNested: options.isNested,
hasSort: options.hasSort ?? false,
isDateTime: true,
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 createTimeColumn(field, label, options = {}) {
const header = {
identifier: {
label,
field,
isNested: options.isNested,
hasSort: options.hasSort ?? false,
isTimeOnly: true,
styleClass: options.styleClass
}
};
if (options.hasFilter ?? false) {
header.filter = {
type: "text",
placeholder: options.placeholder ?? `Filter by ${label.toLowerCase()}`,
defaultMatchMode: "contains",
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 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 = skipLoadingSpinner;
}
skipLoadingSpinner = true;
/**
* 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;
}
#singleData = (0, import_core3.signal)(null);
#multipleData = (0, import_core3.signal)([]);
#isLoading = (0, import_core3.signal)(false);
// Public readonly signals for external consumption
/**
* Read-only signal containing single data object or null
*/
singleData = this.#singleData.asReadonly();
/**
* Read-only signal containing array of data objects
*/
multipleData = this.#multipleData.asReadonly();
/**
* Read-only signal indicating whether a request is currently loading
*/
isLoading = this.#isLoading.asReadonly();
// Private flag to control memoization behavior
#isMemoizationDisabledOnNextRead = false;
/**
* 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() {
this.#isMemoizationDisabledOnNextRead = true;
this.#singleData.set(null);
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 (!this.#isMemoizationDisabledOnNextRead && this.#singleData() !== null) {
return;
}
try {
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)
})
);
this.#singleData.set(data);
} catch (error) {
this.#singleData.set(null);
throw error;
} finally {
this.#isLoading.set(false);
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 (!this.#isMemoizationDisabledOnNextRead && this.#multipleData().length !== 0) {
return;
}
try {
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
})
);
this.#multipleData.set(Array.isArray(data) ? data : []);
} catch (error) {
this.#multipleData.set([]);
throw error;
} finally {
this.#isLoading.set(false);
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() {
this.#singleData.set(null);
this.#multipleData.set([]);
this.#isMemoizationDisabledOnNextRead = false;
}
/**
* Checks if single data is currently cached
* @returns true if single data is cached, false otherwise
*/
hasSingleData() {
return this.#singleData() !== null;
}
/**
* Checks if multiple data is currently cached
* @returns true if multiple data is cached (non-empty array), false otherwise
*/
hasMultipleData() {
return this.#multipleData().length > 0;
}
};
// src/component-state.ts
var import_core4 = require("@angular/core");
var ComponentState = class {
isAjaxDataIncoming = (0, import_core4.signal)(false);
enableCheckBoxSelection = (0, import_core4.signal)(false);
isSelectableRowEnabled = (0, import_core4.signal)(false);
isAjaxRequestOutgoing = (0, import_core4.signal)(false);
hasMultipleSelection = (0, import_core4.signal)(false);
isCreateOrUpdateDialogOpen = (0, import_core4.signal)(false);
isUpdateDialogOpen = (0, import_core4.signal)(false);
isCreateDialogOpen = (0, import_core4.signal)(false);
manipulationType = (0, import_core4.signal)("create" /* Create */);
componentTitle = (0, import_core4.signal)("");
isDataManipulationPageOpen = (0, import_core4.signal)(false);
isCreateOrUpdatePageOpen = (0, import_core4.signal)(false);
isUpdatePageOpen = (0, import_core4.signal)(false);
isCreatePageOpen = (0, import_core4.signal)(false);
manipulationTypeLabel = (0, import_core4.computed)(() => {
switch (this.manipulationType()) {
case "create" /* Create */:
return "Create";
case "update" /* Update */:
return "Update";
case "create-child" /* CreateChild */:
return "Create Child";
case "delete" /* Delete */:
return "Delete";
case "view" /* View */:
return "View";
case "save" /* Save */:
return "Save";
default:
return "";
}
});
/**
* Updates the component title
* @param componentTitle - The new title for the component
* @returns This instance for method chaining
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
setCreateDialogOpen = (status) => {
this.isCreateDialogOpen.set(status);
return this;
};
/**
* Computed signal that combines component title with manipulation type
*/
componentTitleWithManipulationType = (0, import_core4.computed)(() => {
return this.manipulationTypeLabel() + " " + this.componentTitle();
});
/**
* Computed signal that indicates if component is in update state
*/
isOnUpdateState = (0, import_core4.computed)(() => {
return this.manipulationType() === "update" /* Update */;
});
/**
* Computed signal that indicates if component is in create state
*/
isOnCreateState = (0, import_core4.computed)(() => {
return this.manipulationType() === "create" /* Create */;
});
/**
* Computed signal that indicates if component is in delete state
*/
isOnDeleteState = (0, import_core4.computed)(() => {
return this.manipulationType() === "delete" /* Delete */;
});
/**
* Computed signal that indicates if component is in view state
*/
isOnViewState = (0, import_core4.computed)(() => {
return this.manipulationType() === "view" /* View */;
});
/**
* Computed signal that indicates if any Ajax operation is currently running
*/
isAnyAjaxOperationRunning = (0, import_core4.computed)(() => {
return this.isAjaxDataIncoming() || this.isAjaxRequestOutgoing();
});
/**
* Computed signal that indicates if any dialog is open
*/
isAnyDialogOpen = (0, import_core4.computed)(() => {
return this.isCreateOrUpdateDialogOpen() || this.isUpdateDialogOpen() || this.isCreateDialogOpen();
});
/**
* Resets all state to default values
* @returns This instance for method chaining
*/
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 {
singleData = (0, import_core5.signal)(null);
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 = (0, import_core5.computed)(() => {
return this.singleData() !== null;
});
/**
* Checks if multiple data has items
* @returns true if multiple data array has items, false if empty
*/
hasMultipleData = (0, import_core5.computed)(() => {
return !Array.isArray(this.multipleData()) ? false : this.multipleData().length > 0;
});
/**
* Gets the count of items in multiple data
* @returns Number of items in the multiple data array
*/
getMultipleDataCount = (0, import_core5.computed)(() => {
return !this.hasMultipleData() ? 0 : 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 NgSelectPagedDataResponse = class {
constructor(payload, totalCount) {
this.payload = payload;
this.totalCount = totalCount;
}
};
var defaultResetOpts = {
resetQueryParams: false,
resetBody: false,
resetCache: false
};
var NgSelectHelper = class _NgSelectHelper {
constructor(ajaxUrl, httpClient, destroyRef, usePostRequest = false, limit = 50, useCache = true, skipLoadingSpinner = true, initialSearchText = "") {
this.ajaxUrl = ajaxUrl;
this.httpClient = httpClient;
this.destroyRef = destroyRef;
this.usePostRequest = usePostRequest;
this.useCache = useCache;
this.skipLoadingSpinner = skipLoadingSpinner;
this.#searchText = initialSearchText;
this.#originalAjaxUrl = ajaxUrl;
this.#limit = limit > 0 ? limit : 50;
this.destroyRef.onDestroy(() => {
this.#ajaxErrorSubject.complete();
this.inputSubject.complete();
this.#loadMoreDataSubject.complete();
this.#cache.clear();
if (this.runningApiReq && !this.runningApiReq.closed) {
this.runningApiReq.unsubscribe();
}
});
}
#cache = /* @__PURE__ */ new Map();
#originalAjaxUrl;
#queryParams = {};
#body = {};
#initDone = false;
#searchText = "";
#limit;
#page = 1;
#debounceTimeInSec = 1;
#totalCount = -1;
#isLastApiCallSuccessful = true;
#limitReached = false;
#loadMoreDataSubject = new import_rxjs4.Subject();
inputSubject = new import_rxjs4.Subject();
#ajaxErrorSubject = new import_rxjs4.Subject();
ajaxError$ = this.#ajaxErrorSubject.asObservable();
#loadedData = (0, import_core6.signal)(
new NgSelectPagedDataResponse([], 0)
);
loadedData = this.#loadedData.asReadonly();
#isLoading = (0, import_core6.signal)(false);
isLoading = this.#isLoading.asReadonly();
runningApiReq = null;
/**
* Creates a new instance of NgSelectHelper
* @param options Configuration options
* @returns New NgSelectHelper instance
*/
static create({
ajaxUrl,
httpClient,
destroyRef,
usePostRequest = false,
limit = 50,
useCache = true,
skipLoadingSpinner = true,
initialSearchText = ""
}) {
return new _NgSelectHelper(
ajaxUrl,
httpClient,
destroyRef,
usePostRequest,
limit,
useCache,
skipLoadingSpinner,
initialSearchText
);
}
/**
* 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) {
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();
this.#body = Object.assign(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();
this.#body = newBody;
}
return this;
}
/**
* Clears the internal cache
* @returns This instance for method chaining
*/
clearCache() {
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 = this.#originalAjaxUrl.endsWith("/") ? this.#originalAjaxUrl.slice(0, -1) : 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();
this.#queryParams = Object.assign(this.#queryParams, 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 this.#queryParams[key];
return this;
}
/**
* Sets query parameters
* @param newQueryParams New query parameters
* @returns This instance for method chaining
*/
setQueryParams(newQueryParams) {
this.resetAll();
this.#queryParams = newQueryParams;
return this;
}
/**
* Handler for ng-select blur event
*/
onBlur() {
this.resetAll();
}
/**
* Handler for ng-select close event
*/
onClose() {
this.resetSearchText();
if (this.runningApiReq && !this.runningApiReq.closed) {
this.runningApiReq.unsubscribe();
this.runningApiReq = null;
}
}
/**
* Handler for ng-select clear event
*/
async onClear() {
this.resetAll();
}
/**
* Handler for ng-select open event
*/
onOpen() {
this.#loadMoreDataSubject.next();
}
/**
* Handler for ng-select scroll to end event
*/
onScrollToEnd() {
if (this.#isLoading()) {
return;
}
if (this.isLastApiCallSuccessful && !this.limitReached) {
this.#page += 1;
}
this.#loadMoreDataSubject.next();
}
/**
* Gets whether the last API call was successful
*/
get isLastApiCallSuccessful() {
return this.#isLastApiCallSuccessful;
}
/**
* Gets whether the limit has been reached
*/
get limitReached() {
return this.#limitReached;
}
/**
* Gets current query parameters
*/
get queryParams() {
return this.#queryParams;
}
/**
* Gets current request body
*/
get body() {
return this.#body;
}
/**
* Gets current page number
*/
get page() {
return this.#page;
}
/**
* Gets total count of available items
*/
get totalCount() {
return this.#totalCount;
}
/**
* Gets whether initialization is complete
*/
get isInitDone() {
return this.#initDone;
}
/**
* Initializes the NgSelectHelper with event handlers
* Should be called in ngOnInit or similar lifecycle method
*/
init() {
if (this.#initDone) {
return;
}
this.#initDone = true;
this.inputSubject.pipe(
(0, import_rxjs4.debounceTime)(this.#debounceTimeInSec * 500),
(0, import_rxjs4.switchMap)((term) => {
this.resetAll();
this.#searchText = term;
return this.loadDataFromApi(this.page, this.#limit, term).pipe(
(0, import_rxjs4.catchError)(() => (0, import_rxjs4.of)(null))
);
})
).subscribe({
next: (res) => {
if (res) {
this.updateStateOnSuccessfulInitialApiCall(res);
} else {
this.updateStateOnFailedApiCall();
}
}
});
if (this.#searchText !== "") {
this.inputSubject.next(this.#searchText);
}
this.#loadMoreDataSubject.pipe(
(0, import_rxjs4.switchMap)(() => {
if (this.#isLoading()) {
return (0, import_rxjs4.of)(null);
}
this.runLimitReachedCheck();
if (this.limitReached) {
return (0, import_rxjs4.of)(null);
}
return this.loadDataFromApi(this.page, this.#limit, this.#searchText).pipe(
(0, import_rxjs4.catchError)(() => (0, import_rxjs4.of)(null))
);
})
).subscribe({
next: (res) => {
if (res) {
this.updateStateOnSuccessfulSubsequentApiCall(res);
} else {
this.updateStateOnFailedApiCall();
}
}
});
}
/**
* Loads data from the API
* @param page Page number
* @param limit Items per page
* @param searchText Search term
* @returns Observable of paged data response
*/
loadDataFromApi(page, limit, searchText) {
const queryParams = { page, limit };
if (searchText) {
queryParams["searchText"] = searchText;
}
const key = {
ajaxUrl: this.ajaxUrl,
page,
limit,
searchText: searchText ?? "",
queryParams: { ...queryParams, ...this.#queryParams },
body: this.#body
};
let strKey = null;
if (this.useCache) {
strKey = JSON.stringify(key);
const cacheData = this.#cache.get(strKey);
if (cacheData !== void 0) {
return (0, import_rxjs4.of)(cacheData);
}
}
this.#isLoading.set(true);
let req;
if (this.usePostRequest) {
req = this.httpClient.post(key.ajaxUrl, key.body, {
params: key.queryParams,
context: new import_http5.HttpContext().set(SkipLoadingSpinner, this.skipLoadingSpinner)
});
} else {
req = this.httpClient.get(key.ajaxUrl, {
params: key.queryParams,
context: new import_http5.HttpContext().set(SkipLoadingSpinner, this.skipLoadingSpinner)
});
}
return req.pipe(
(0, import_rxjs4.first)(),
(0, import_rxjs4.mergeMap)((val) => {
if (!val) {
throw new Error("Invalid response body for ng-select");
}
if (!Object.hasOwn(val, "payload") || !Object.hasOwn(val, "totalCount")) {
throw new Error("Invalid response body for ng-select");
}
if (!Array.isArray(Reflect.get(val, "payload"))) {
throw new Error("Invalid response body for ng-select");
}
return (0, import_rxjs4.of)(val);
}),
(0, import_rxjs4.tap)((val) => {
if (this.useCache && strKey && val) {
this.#cache.set(strKey, val);
}
}),
(0, import_rxjs4.catchError)((error) => {
this.#ajaxErrorSubject.next(error);
return (0, import_rxjs4.of)(null);
}),
(0, import_rxjs4.finalize)(() => this.#isLoading.set(false))
);
}
/**
/**
* Checks if the limit has been reached
*/
runLimitReachedCheck() {
if (this.totalCount !== -1) {
this.#limitReached = this.#loadedData().payload.length >= this.totalCount;
}
}
/**
* Updates state after successful initial API call
* @param data Response data
*/
updateStateOnSuccessfulInitialApiCall(data) {
this.#loadedData.set(new NgSelectPagedDataResponse(data.payload, data.totalCount));
this.#totalCount = data.totalCount;
this.#isLastApiCallSuccessful = true;
}
/**
* Updates state after successful subsequent API call
* @param data Response data
*/
updateStateOnSuccessfulSubsequentApiCall(data) {
this.#loadedData.update((val) => {
return new NgSelectPagedDataResponse(
[...val.payload, ...data.payload],
data.totalCount
);
});
this.#totalCount = data.totalCount;
this.#isLastApiCallSuccessful = true;
}
/**
* Updates state after failed API call
*/
updateStateOnFailedApiCall() {
this.#isLastApiCallSuccessful = false;
}
/**
* Resets all state to initial values
* @param opts Reset options
*/
resetAll(opts = defaultResetOpts) {
if (this.runningApiReq && !this.runningApiReq.closed) {
this.runningApiReq.unsubscribe();
this.runningApiReq = null;
}
this.#isLastApiCallSuccessful = true;
this.#page = 1;
this.#totalCount = -1;
this.#limitReached = false;
this.#searchText = "";
if (opts.resetCache) {
this.#cache.clear();
}
this.#loadedData.set(new NgSelectPagedDataResponse([], 0));
this.#isLoading.set(false);
if (opts.resetQueryParams) {
this.#queryParams = {};
}
if (opts.resetBody) {
this.#body = {};
}
}
/**
* Resets search text
*/
resetSearchText() {
this.#searchText = "";
}
};
function createNgSelectHelper(options) {
!options.injector && (0, import_core6.assertInInjectionContext)(createNgSelectHelper);
const assertedInjector = options.injector ?? (0, import_core6.inject)(import_core6.Injector);
return (0, import_core6.runInInjectionContext)(assertedInjector, () => {
const httpClient = (0, import_core6.inject)(import_http5.HttpClient);
const destroyRef = (0, import_core6.inject)(import_core6.DestroyRef);
return NgSelectHelper.create({
...options,
httpClient,
destroyRef
});
});
}
// src/offset-paginated-ng-select-state.ts
var import_http6 = require("@angular/common/http");
var import_core7 = require("@angular/core");
var import_rxjs_interop = require("@angular/core/rxjs-interop");
var import_rxjs5 = require("rxjs");
var optionsWithDefaultValue = {
searchQueryParamKey: "searchText",
pageQueryParamKey: "page",
limitQueryParamKey: "limit",
dataArrayKey: "payload",
totalDataCountKey: "totalCount",
debounceTimeMs: 500,
searchOnlyMode: false,
requestMethod: "GET",
queryParams: {},
onCloseReset: { reset: false },
postRequestBody: null,
dataLimitPerRequest: 20,
useCache: false,
cacheTtlSec: 60,
disableCacheExpiration: false
};
var OFFSET_PAGINATED_NG_SELECT_STATE_CONFIG = new import_core7.InjectionToken(
"OFFSET_PAGINATED_NG_SELECT_STATE_CONFIG",
{
providedIn: "root",
factory: () => optionsWithDefaultValue
}
);
function provideOffsetPaginatedNgSelectStateConfig(configFactory) {
return {
provide: OFFSET_PAGINATED_NG_SELECT_STATE_CONFIG,
useFactory: () => {
const config = configFactory();
return { ...optionsWithDefaultValue, ...config };
}
};
}
var OffsetPaginatedNgSelectNetworkRequest = new import_http6.HttpContextToken(() => false);
function isValidData(dataArrayKey, dataCountKey, rawData) {
if (typeof rawData !== "object" || rawData === null || Array.isArray(rawData)) {
return false;
}
const hasData = dataArrayKey in rawData || "payload" in rawData || "data" in rawData;
if (!hasData || !(dataCountKey in rawData)) {
return false;
}
const dataArray = rawData[dataArrayKey] ?? rawData["payload"] ?? rawData["data"];
if (!Array.isArray(dataArray)) {
return false;
}
if (typeof rawData[dataCountKey] !== "number") {
return false;
}
return true;
}
function defaultData() {
return {
payload: [],
totalCount: 0
};
}
var defaultResetOpts2 = {
resetQueryParams: false,
resetBody: false,
resetCache: false
};
function httpParamsToObject(params) {
const result = {};
params.keys().forEach((key) => {
const values = params.getAll(key);
if (values === null) {
return;
}
result[key] = values.length > 1 ? values : values[0];
});
return result;
}
function removeBlacklistedParams(params, blacklistedQueryKeys) {
let current = params;
params.keys().forEach((key) => {
if (blacklistedQueryKeys.includes(key.toLowerCase())) {
current = current.delete(key);
}
});
return current;
}
function offsetPaginatedNgSelectState(url, options) {
if ((0, import_core7.isDevMode)() && !options?.injector) {
(0, import_core7.assertInInjectionContext)(offsetPaginatedNgSelectState);
}
const assertedInjector = options?.injector ?? (0, import_core7.inject)(import_core7.Injector);
return (0, import_core7.runInInjectionContext)(assertedInjector, () => {
const destroyRef = (0, import_core7.inject)(import_core7.DestroyRef);
const http = (0, import_core7.inject)(import_http6.HttpClient);
const configFromDi = (0, import_core7.inject)(OFFSET_PAGINATED_NG_SELECT_STATE_CONFIG);
const typeaheadSubject = new import_rxjs5.Subject();
const typeAhead$ = typeaheadSubject.asObservable();
const mergedOptions = {
...configFromDi,
...cleanNullishFromObject(options),
httpContext: options?.httpContext ?? new import_http6.HttpContext(),
onError: options?.onError ?? configFromDi.onError
};
const blackListedQueryKeys = [
mergedOptions.searchQueryParamKey,
mergedOptions.pageQueryParamKey,
mergedOptions.limitQueryParamKey
].map((elem) => elem.toLowerCase());
const sanitizedMergedQueryParams = removeBlacklistedParams(
new import_http6.HttpParams({ fromObject: mergedOptions.queryParams }),
blackListedQueryKeys
);
const internalState = Object.seal({
apiCallNotification: new import_rxjs5.Subject(),
apiCallAbortNotifier: new import_rxjs5.Subject(),
currentPage: (0, import_core7.signal)(1),
isCurrentPageSuccessful: (0, import_core7.signal)(false),
isAllDataLoaded: (0, import_core7.signal)(false),
isLoading: (0, import_core7.signal)(false),
searchTermFromSearchEvent: (0, import_core7.signal)(null),
isSelectPanelOpen: (0, import_core7.signal)(false),
loadedData: (0, import_core7.signal)(defaultData()),
searchText: (0, import_core7.signal)(null),
cache: /* @__PURE__ */ new Map(),
postRequestBody: (0, import_core7.signal)(mergedOptions.postRequestBody),
queryParamsFromConsumer: (0, import_core7.signal)(sanitizedMergedQueryParams)
});
const finalQueryParams = (0, import_core7.computed)(() => {
let val = internalState.queryParamsFromConsumer().set(mergedOptions.pageQueryParamKey, String(internalState.currentPage())).set(mergedOptions.limitQueryParamKey, String(mergedOptions.dataLimitPerRequest));
const searchText = internalState.searchText();
if (searchText !== null) {
val = val.set(mergedOptions.searchQueryParamKey, searchText);
} else {
val = val.delete(mergedOptions.searchQueryParamKey);
}
return val;
});
destroyRef.onDestroy(() => {
if (!typeaheadSubject.closed) {
typeaheadSubject.complete();
}
if (!internalState.apiCallAbortNotifier.closed) {
internalState.apiCallAbortNotifier.next();
internalState.apiCallAbortNotifier.complete();
}
if (!internalState.apiCallNotification.closed) {
internalState.apiCallNotification.complete();
}
internalState.cache.clear();
});
if (mergedOptions.useCache && !mergedOptions.disableCacheExpiration) {
(0, import_rxjs5.interval)(mergedOptions.cacheTtlSec * 1e3).pipe((0, import_rxjs_interop.takeUntilDestroyed)(destroyRef)).subscribe(() => {
internalState.cache.clear();
});
}
function reset(options2 = defaultResetOpts2) {
internalState.apiCallAbortNotifier.next();
internalState.currentPage.set(1);
internalState.isCurrentPageSuccessful.set(false);
internalState.searchText.set(null);
internalState.loadedData.set(defaultData());
internalState.isAllDataLoaded.set(false);
internalState.isLoading.set(false);
if (options2.resetBody) {
internalState.postRequestBody.set(mergedOptions.postRequestBody);
}
if (options2.resetQueryParams) {
internalState.queryParamsFromConsumer.set(sanitizedMergedQueryParams);
}
if (options2.resetCache) {
internalState.cache.clear();
}
}
const notifyApiCall = () => {
internalState.apiCallNotification.next();
};
const handleApiResponse = (newData) => {
internalState.isCurrentPageSuccessful.set(true);
if (newData.payload.length > 0) {
internalState.loadedData.update((val) => {
return {
payload: [...val.payload, ...newData.payload],
totalCount: newData.totalCount
};
});
}
internalState.isAllDataLoaded.set(
internalState.loadedData().payload.length === newData.totalCount
);
};
const loadDataFromApi = () => {
const finalUrl = (0, import_core7.isSignal)(url) ? url() : url;
if (finalUrl === null) {
return (0, import_rxjs5.of)(null);
}
const key = {
url: finalUrl,
body: internalState.postRequestBody(),
queryParams: finalQueryParams().toString()
};
if (mergedOptions.requestMethod !== "POST") {
delete key["body"];
}
let strKey = null;
if (mergedOptions.useCache) {
strKey = JSON.stringify(key);
const cacheData = internalState.cache.get(strKey);
if (cacheData !== void 0) {
return (0, import_rxjs5.of)(cacheData);
}
}
let req;
switch (mergedOptions.requestMethod) {
case "GET":
req = (0, import_rxjs5.defer)(() => {
internalState.isLoading.set(true);
return http.get(finalUrl, {
params: finalQueryParams(),
context: mergedOptions.httpContext.set(OffsetPaginatedNgSelectNetworkRequest, true)
});
});
break;
case "POST":
req = (0, import_rxjs5.defer)(() => {
internalState.isLoading.set(true);
return http.post(finalUrl, internalState.postRequestBody(), {
params: finalQueryParams(),
context: mergedOptions.httpContext.set(OffsetPaginatedNgSelectNetworkRequest, true)
});
});
break;
default:
throw new Error("Invalid/unsupported request method");
}
return req.pipe(
(0, import_rxjs5.switchMap)((rawData) => {
if (!isValidData(mergedOptions.dataArrayKey, mergedOptions.totalDataCountKey, rawData)) {
throw new Error(
`The response body must be an object. Valid example: { ${mergedOptions.dataArrayKey}: [], ${mergedOptions.totalDataCountKey}: 0 } or { payload: [], ${mergedOptions.totalDataCountKey}: 0 } or { data: [], ${mergedOptions.totalDataCountKey}: 0 }`
);
}
const dataArray = rawData[mergedOptions.dataArrayKey] ?? rawData["payload"] ?? rawData["data"] ?? [];
const dataInContainer = {
payload: dataArray,
totalCount: rawData[mergedOptions.totalDataCountKey] ?? 0
};
return (0, import_rxjs5.of)(dataInContainer);
}),
(0, import_rxjs5.tap)((data) => {
if (mergedOptions.useCache && strKey && data) {
internalState.cache.set(strKey, data);
}
}),
(0, import_rxjs5.catchError)((error) => {
if (mergedOptions.onError) {
try {
mergedOptions.onError(error);
} catch (errorFromHandler) {
console.warn(
"Exception in onError is handled internally to keep observable running.",
errorFromHandler
);
}
}
console.error(error);
return (0, import_rxjs5.of)(null);
}),
(0, import_rxjs5.finalize)(() => internalState.isLoading.set(false)),
(0, import_rxjs5.takeUntil)(internalState.apiCallAbortNotifier)
);
};
internalState.apiCallNotification.pipe(
(0, import_rxjs5.switchMap)(
() => internalState.isLoading() || internalState.isAllDataLoaded() || !internalState.isSelectPanelOpen() ? (0, import_rxjs5.of)(null) : loadDataFromApi()
),
(0, import_rxjs5.filter)((val) => val !== null),
(0, import_rxjs_interop.takeUntilDestroyed)(destroyRef)
).subscribe((data) => {
handleApiResponse(data);
});
typeAhead$.pipe(
(0, import_rxjs5.combineLatestWith)((0, import_rxjs_interop.toObservable)(internalState.searchTermFromSearchEvent)),
(0, import_rxjs5.debounceTime)(mergedOptions.debounceTimeMs),
(0, import_rxjs5.switchMap)(([typeaheadValue, searchEventValue]) => {
if (typeaheadValue === searchEventValue) {
return (0, import_rxjs5.of)(typeaheadValue.trim());
}
if (typeaheadValue !== searchEventValue && searchEventValue === "") {
return (0, import_rxjs5.of)("");
}
return (0, import_rxjs5.of)(null);
}),
(0, import_rxjs5.filter)((val) => val !== null),
(0, import_rxjs_interop.takeUntilDestroyed)(destroyRef)
).subscribe((finalValue) => {
reset();
if (mergedOptions.searchOnlyMode && finalValue === "") {
return;
}
internalState.searchText.set(finalValue);
notifyApiCall();
});
return Object.seal({
// data state
reset,
queryParams: (0, import_core7.computed)(() => {
return httpParamsToObject(finalQueryParams());
}),
postRequestBody: internalState.postRequestBody.asReadonly(),
typeaheadSubject,
data: (0, import_core7.computed)(() => {
return internalState.loadedData().payload;
}),
isLoading: internalState.isLoading.asReadonly(),
// event handlers
clearCache() {
internalState.cache.clear();
return this;
},
onOpen() {
internalState.isSelectPanelOpen.set(true);
if (!mergedOptions.searchOnlyMode) {
notifyApiCall();
}
},
onClose() {
internalState.isSelectPanelOpen.set(false);
if (mergedOptions.onCloseReset.reset) {
reset(mergedOptions.onCloseReset.resetOpts);
}
},
onClear() {
reset();
},
onSearch(event) {
if (Object.hasOwn(event, "term")) {
internalState.searchTermFromSearchEvent.set(event.term);
}
},
onScrollToEnd() {
if (internalState.isAllDataLoaded() || internalState.isLoading() || !internalState.isSelectPanelOpen()) {
return;
}
if (internalState.isCurrentPageSuccessful()) {
internalState.currentPage.update((currentValue) => {
return currentValue + 1;
});
}
notifyApiCall();
},
// chainable methods
setBody(value) {
if (mergedOptions.requestMethod === "POST") {
reset();
internalState.postRequestBody.set(value);
}
return this;
},
clearBody() {
if (mergedOptions.requestMethod === "POST") {
reset();
internalState.postRequestBody.set(null);
}
return this;
},
patchQueryParams(value) {
const newValue = { ...value };
Object.keys(newValue).forEach((key) => {
if (blackListedQueryKeys.includes(key.toLowerCase())) {
delete newValue[key];
}
});
const keys = Object.keys(newValue);
if (keys.length === 0) {
return this;
}
reset();
internalState.queryParamsFromConsumer.update((current) => {
let next = current;
keys.forEach((key) => {
next = next.delete(key);
});
return next.appendAll(newValue);
});
return this;
},
removeQueryParam(key) {
reset();
internalState.queryParamsFromConsumer.update((currentValue) => {
return currentValue.delete(key);
});
return this;
},
removeAllQueryParams() {
reset();
internalState.queryParamsFromConsumer.set(new import_http6.HttpParams());
return this;
}
});
});
}
// src/ng-select-utils.ts
var import_core8 = require("@angular/core");
var import_rxjs_interop2 = require("@angular/core/rxjs-interop");
function initNgSelect(helpers$, destroyRef, onAjaxError) {
helpers$.pipe((0, import_rxjs_interop2.takeUntilDestroyed)(destroyRef)).subscribe({
next: (helpers) => {
helpers.filter((elem) => elem instanceof NgSelectHelper && !elem.isInitDone).forEach((elem) => {
elem.init();
elem.ajaxError$.pipe((0, import_rxjs_interop2.takeUntilDestroyed)(destroyRef)).subscribe(onAjaxError);
});
}
});
}
function initNgSelectHelper(items, options = {}) {
!options.injector && (0, import_core8.assertInInjectionContext)(initNgSelectHelper);
const assertedInjector = options.injector ?? (0, import_core8.inject)(import_core8.Injector);
(0, import_core8.runInInjectionContext)(assertedInjector, () => {
const destroyRef = (0, import_core8.inject)(import_core8.DestroyRef);
(0, import_rxjs_interop2.toObservable)(items).pipe((0, import_rxjs_interop2.takeUntilDestroyed)(destroyRef)).subscribe((elem) => {
elem.filter((elem2) => elem2 instanceof NgSelectHelper && !elem2.isInitDone).forEach((elem2) => {
elem2.init();
if (!options.onAjaxError) {
return;
}
elem2.ajaxError$.pipe((0, import_rxjs_interop2.takeUntilDestroyed)(destroyRef)).subscribe((payload) => {
if (options.onAjaxError) {
options.onAjaxError(payload);
}
});
});
});
});
}
// src/offset-paginated-prime-ng-table-state.ts
var import_http7 = require("@angular/common/http");
var import_core9 = require("@angular/core");
var import_rxjs_interop3 = require("@angular/core/rxjs-interop");
var import_rxjs6 = require("rxjs");
var OffsetPaginatedPrimeNgTableStateNetworkRequest = new import_http7.HttpContextToken(() => false);
var PostRequestBody = class {
constructor(payload) {
this.payload = payload;
}
};
var optionsWithDefaultValue2 = {
pageQueryParamKey: "page",
limitQueryParamKey: "limit",
dataArrayKey: "payload",
totalDataCountKey: "totalCount",
requestMethod: "GET",
queryParams: {}
};
var OFFSET_PAGINATED_PRIME_NG_TABLE_STATE_CONFIG = new import_core9.InjectionToken(
"OFFSET_PAGINATED_PRIME_NG_TABLE_STATE_CONFIG",
{
providedIn: "root",
factory: () => optionsWithDefaultValue2
}
);
function provideOffsetPaginatedPrimeNgTableStateConfig(configFactory) {
return {
provide: OFFSET_PAGINATED_PRIME_NG_TABLE_STATE_CONFIG,
useFactory: () => {
const config = configFactory();
return { ...optionsWithDefaultValue2, ...config };
}
};
}
function isValidData2(dataArrayKey, dataCountKey, rawData) {
if (typeof rawData !== "object" || rawData === null || Array.isArray(rawData)) {
return false;
}
if (!(dataArrayKey in rawData) || !(dataCountKey in rawData)) {
return false;
}
if (!Array.isArray(rawData[dataArrayKey])) {
return false;
}
if (typeof rawData[dataCountKey] !== "number") {
return false;
}
return true;
}
function defaultData2() {
return {
payload: [],
totalCount: 0
};
}
function offsetPaginatedPrimeNgTableState(url, options) {
if ((0, import_core9.isDevMode)() && !options?.injector) {
(0, import_core9.assertInInjectionContext)(offsetPaginatedPrimeNgTableState);
}
const assertedInjector = options?.injector ?? (0, import_core9.inject)(import_core9.Injector);
return (0, import_core9.runInInjectionContext)(assertedInjector, () => {
const http = (0, import_core9.inject)(import_http7.HttpClient);
const destroyRef = (0, import_core9.inject)(import_core9.DestroyRef);
const configFromDi = (0, import_core9.inject)(OFFSET_PAGINATED_PRIME_NG_TABLE_STATE_CONFIG);
const mergedOptions = {
...configFromDi,
...cleanNullishFromObject(options),
httpContext: options?.httpContext ?? new import_http7.HttpContext(),
onError: options?.onError ?? configFromDi.onError
};
const blackListedQueryKeys = [
mergedOptions.pageQueryParamKey,
mergedOptions.limitQueryParamKey
].map((elem) => elem.toLowerCase());
Object.keys(mergedOptions.queryParams).forEach((key) => {
if (blackListedQueryKeys.includes(key.toLowerCase())) {
delete mergedOptions.queryParams[key];
}
});
const internalState = Object.seal({
apiCallNotification: new import_rxjs6.Subject(),
apiCallAbortNotifier: new import_rxjs6.Subject(),
isLoading: (0, import_core9.signal)(false),
currentPage: (0, import_core9.signal)(1),
currentLimit: (0, import_core9.signal)(15),
totalRecords: (0, import_core9.signal)(0),
loadedData: (0, import_core9.signal)(defaultData2()),
predicate: mergedOptions.predicate ?? (0, import_core9.signal)(true),
queryParamsFromUser: (0, import_core9.signal)(
mergedOptions.queryParams
),
postRequestBodyFromLazyLoad: (0, import_core9.signal)(null),
queryParamsFromLazyLoad: (0, import_core9.signal)(null)
});
const finalQueryParams = (0, import_core9.computed)(() => {
const val = {
[mergedOptions.pageQueryParamKey]: internalState.currentPage(),
[mergedOptions.limitQueryParamKey]: internalState.currentLimit(),
...cleanNullishFromObject(internalState.queryParamsFromUser()),
...cleanNullishFromObject(internalState.queryParamsFromLazyLoad() ?? {})
};
return val;
});
(0, import_core9.effect)(() => {
const predicate = internalState.predicate();
(0, import_core9.untracked)(() => {
if (!predicate) {
internalState.apiCallAbortNotifier.next();
}
});
});
destroyRef.onDestroy(() => {
if (!internalState.apiCallNotification.closed) {
internalState.apiCallNotification.complete();
}
if (!internalState.apiCallAbortNotifier.closed) {
internalState.apiCallAbortNotifier.next();
internalState.apiCallAbortNotifier.complete();
}
});
const loadDataFromApi = () => {
const apiUrl = (0, import_core9.isSignal)(url) ? url() : url;
let req;
switch (mergedOptions.requestMethod) {
case "GET":
req = (0, import_rxjs6.defer)(() => {
internalState.isLoading.set(true);
return http.get(apiUrl, {
params: { ...finalQueryParams() },
context: mergedOptions.httpContext.set(
OffsetPaginatedPrimeNgTableStateNetworkRequest,
true
)
});
});
break;
case "POST":
req = (0, import_rxjs6.defer)(() => {
internalState.isLoading.set(true);
return http.post(apiUrl, internalState.postRequestBodyFromLazyLoad(), {
params: { ...finalQueryParams() },
context: mergedOptions.httpContext.set(
OffsetPaginatedPrimeNgTableStateNetworkRequest,
true
)
});
});
break;
default:
throw new Error("Invalid/unsupported request method");
}
return req.pipe(
(0, import_rxjs6.switchMap)((rawData) => {
if (!isValidData2(mergedOptions.dataArrayKey, mergedOptions.totalDataCountKey, rawData)) {
throw new Error(
`The response body must be an object. Valid example: { ${mergedOptions.dataArrayKey}: [], ${mergedOptions.totalDataCountKey}: 0 }`
);
}
const dataInContainer = {
payload: rawData[mergedOptions.dataArrayKey] ?? [],
totalCount: rawData[mergedOptions.totalDataCountKey] ?? 0
};
return (0, import_rxjs6.of)(dataInContainer);
}),
(0, import_rxjs6.catchError)((error) => {
if (mergedOptions.onError) {
try {
mergedOptions.onError(error);
} catch (errorFromHandler) {
console.warn(
"Exception in onError is handled internally to keep observable running.",
errorFromHandler
);
}
}
console.error(error);
return (0, import_rxjs6.of)(null);
}),
(0, import_rxjs6.finalize)(() => internalState.isLoading.set(false)),
(0, import_rxjs6.takeUntil)(internalState.apiCallAbortNotifier)
);
};
const handleApiResponse = (newData) => {
internalState.loadedData.set({
payload: newData.payload,
totalCount: newData.totalCount
});
internalState.totalRecords.set(newData.totalCount);
};
internalState.apiCallNotification.pipe(
(0, import_rxjs6.switchMap)(() => {
if (internalState.isLoading()) {
return (0, import_rxjs6.of)(null);
} else if (!internalState.predicate()) {
return (0, import_rxjs6.of)(defaultData2());
} else {
return loadDataFromApi();
}
}),
(0, import_rxjs6.filter)((val) => val !== null),
(0, import_rxjs_interop3.takeUntilDestroyed)(destroyRef)
).subscribe((data) => {
handleApiResponse(data);
});
return Object.seal({
queryParams: finalQueryParams,
predicate: internalState.predicate,
totalRecords: internalState.totalRecords.asReadonly(),
data: (0, import_core9.computed)(() => {
return internalState.loadedData().payload;
}),
isLoading: internalState.isLoading.asReadonly(),
onLazyLoad(event) {
const offset = event.first || 0;
const limit = event.rows || 15;
const currentPage = Math.floor(offset / limit) + 1;
internalState.currentPage.set(currentPage);
internalState.currentLimit.set(limit);
if (mergedOptions.filterAndSortHandler) {
const handledResult = mergedOptions.filterAndSortHandler(event);
if (handledResult instanceof import_http7.HttpParams) {
internalState.queryParamsFromLazyLoad.set(handledResult);
} else if (handledResult instanceof PostRequestBody) {
internalState.postRequestBodyFromLazyLoad.set(handledResult.payload);
}
}
internalState.apiCallNotification.next();
},
patchQueryParams(value) {
const newValue = { ...value };
Object.keys(newValue).forEach((key) => {
if (blackListedQueryKeys.includes(key.toLowerCase())) {
delete newValue[key];
}
});
if (Object.keys(newValue).length > 0) {
internalState.queryParamsFromUser.update((currentValue) => {
return {
...currentValue,
...newValue
};
});
}
return this;
},
removeQueryParam(key) {
internalState.queryParamsFromUser.update((currentValue) => {
const { [key]: _, ...rest } = currentValue;
return rest;
});
return this;
},
removeAllQueryParams() {
internalState.queryParamsFromUser.set({});
return this;
}
});
});
}
var UrlBasedDynamicQuery = {
Contains: "con",
NotContains: "ntcon",
StartsWith: "sw",
NotStartsWith: "ntsw",
EndsWith: "ew",
NotEndsWith: "ntew",
Equal: "eq",
NotEquals: "nteq",
GreaterThan: "gt",
GreaterThanEqual: "gte",
LessThan: "lt",
LessThanEqual: "lte"
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ComponentDataStorage,
ComponentState,
ManipulationType,
MemoizedDataStorage,
NgSelectHelper,
NgSelectPagedDataResponse,
NullableApiResponseSchema,
NumberStringKeyDataSchema,
OffsetPaginatedNgSelectNetworkRequest,
OffsetPaginatedPrimeNgTableStateNetworkRequest,
PagedDataResponseZodSchema,
PostRequestBody,
PrimeNgDynamicTableStateHelper,
PrimeNgPagedDataTableStateHelper,
ReloadNotification,
SkipLoadingSpinner,
UrlBasedDynamicQuery,
binarySearch,
calculatePrimengTablePagination,
cleanNullishFromObject,
createBooleanColumn,
createBooleanSelectItems,
createDateColumn,
createDateTimeColumn,
createDropdownColumn,
createHierarchicalTree,
createKeyData,
createMultiselectColumn,
createNgSelectHelper,
createNumericColumn,
createPrimengNumberMatchModes,
createPrimengStringMatchModes,
createSimpleColumn,
createStatusSelectItems,
createTextColumn,
createTimeColumn,
dynamicQueryResponseZodSchema,
emptyCallback,
hasNullishInObject,
initNgSelect,
initNgSelectHelper,
isApiResponse,
isDynamicQueryResponse,
isPaginatedResponse,
isSimplePagedResponse,
mergeTableHeaders,
nullableKeyData,
offsetPaginatedNgSelectState,
offsetPaginatedPrimeNgTableState,
provideOffsetPaginatedNgSelectStateConfig,
provideOffsetPaginatedPrimeNgTableStateConfig,
routeParamConcat
});
//# sourceMappingURL=index.js.map