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

2,747 lines 86.3 kB
"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