@ngneers/controls 0.0.1-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/fesm2022/ngneers-controls-accordion.mjs +234 -0
  2. package/fesm2022/ngneers-controls-accordion.mjs.map +1 -0
  3. package/fesm2022/ngneers-controls-api-ng.mjs +959 -0
  4. package/fesm2022/ngneers-controls-api-ng.mjs.map +1 -0
  5. package/fesm2022/ngneers-controls-api-resize.mjs +712 -0
  6. package/fesm2022/ngneers-controls-api-resize.mjs.map +1 -0
  7. package/fesm2022/ngneers-controls-api.mjs +347 -0
  8. package/fesm2022/ngneers-controls-api.mjs.map +1 -0
  9. package/fesm2022/ngneers-controls-avatar.mjs +77 -0
  10. package/fesm2022/ngneers-controls-avatar.mjs.map +1 -0
  11. package/fesm2022/ngneers-controls-base.mjs +620 -0
  12. package/fesm2022/ngneers-controls-base.mjs.map +1 -0
  13. package/fesm2022/ngneers-controls-breadcrumb.mjs +100 -0
  14. package/fesm2022/ngneers-controls-breadcrumb.mjs.map +1 -0
  15. package/fesm2022/ngneers-controls-button-group.mjs +110 -0
  16. package/fesm2022/ngneers-controls-button-group.mjs.map +1 -0
  17. package/fesm2022/ngneers-controls-button.mjs +83 -0
  18. package/fesm2022/ngneers-controls-button.mjs.map +1 -0
  19. package/fesm2022/ngneers-controls-calendar-time-CJT5AFlA.mjs +74 -0
  20. package/fesm2022/ngneers-controls-calendar-time-CJT5AFlA.mjs.map +1 -0
  21. package/fesm2022/ngneers-controls-calendar.mjs +801 -0
  22. package/fesm2022/ngneers-controls-calendar.mjs.map +1 -0
  23. package/fesm2022/ngneers-controls-checkbox.mjs +63 -0
  24. package/fesm2022/ngneers-controls-checkbox.mjs.map +1 -0
  25. package/fesm2022/ngneers-controls-chip.mjs +55 -0
  26. package/fesm2022/ngneers-controls-chip.mjs.map +1 -0
  27. package/fesm2022/ngneers-controls-default-icons.mjs +82 -0
  28. package/fesm2022/ngneers-controls-default-icons.mjs.map +1 -0
  29. package/fesm2022/ngneers-controls-defer.mjs +72 -0
  30. package/fesm2022/ngneers-controls-defer.mjs.map +1 -0
  31. package/fesm2022/ngneers-controls-dialog.mjs +407 -0
  32. package/fesm2022/ngneers-controls-dialog.mjs.map +1 -0
  33. package/fesm2022/ngneers-controls-directives.mjs +498 -0
  34. package/fesm2022/ngneers-controls-directives.mjs.map +1 -0
  35. package/fesm2022/ngneers-controls-drawer.mjs +271 -0
  36. package/fesm2022/ngneers-controls-drawer.mjs.map +1 -0
  37. package/fesm2022/ngneers-controls-edit-inplace.mjs +127 -0
  38. package/fesm2022/ngneers-controls-edit-inplace.mjs.map +1 -0
  39. package/fesm2022/ngneers-controls-errors.mjs +329 -0
  40. package/fesm2022/ngneers-controls-errors.mjs.map +1 -0
  41. package/fesm2022/ngneers-controls-filter.mjs +708 -0
  42. package/fesm2022/ngneers-controls-filter.mjs.map +1 -0
  43. package/fesm2022/ngneers-controls-hint.mjs +153 -0
  44. package/fesm2022/ngneers-controls-hint.mjs.map +1 -0
  45. package/fesm2022/ngneers-controls-i18n-translations-de.mjs +170 -0
  46. package/fesm2022/ngneers-controls-i18n-translations-de.mjs.map +1 -0
  47. package/fesm2022/ngneers-controls-i18n-translations-en.mjs +170 -0
  48. package/fesm2022/ngneers-controls-i18n-translations-en.mjs.map +1 -0
  49. package/fesm2022/ngneers-controls-i18n.mjs +57 -0
  50. package/fesm2022/ngneers-controls-i18n.mjs.map +1 -0
  51. package/fesm2022/ngneers-controls-icon.mjs +195 -0
  52. package/fesm2022/ngneers-controls-icon.mjs.map +1 -0
  53. package/fesm2022/ngneers-controls-inplace.mjs +150 -0
  54. package/fesm2022/ngneers-controls-inplace.mjs.map +1 -0
  55. package/fesm2022/ngneers-controls-input-field.mjs +165 -0
  56. package/fesm2022/ngneers-controls-input-field.mjs.map +1 -0
  57. package/fesm2022/ngneers-controls-input.mjs +72 -0
  58. package/fesm2022/ngneers-controls-input.mjs.map +1 -0
  59. package/fesm2022/ngneers-controls-item-view.mjs +473 -0
  60. package/fesm2022/ngneers-controls-item-view.mjs.map +1 -0
  61. package/fesm2022/ngneers-controls-list-box.mjs +281 -0
  62. package/fesm2022/ngneers-controls-list-box.mjs.map +1 -0
  63. package/fesm2022/ngneers-controls-mask-input.mjs +888 -0
  64. package/fesm2022/ngneers-controls-mask-input.mjs.map +1 -0
  65. package/fesm2022/ngneers-controls-menu.mjs +371 -0
  66. package/fesm2022/ngneers-controls-menu.mjs.map +1 -0
  67. package/fesm2022/ngneers-controls-message.mjs +30 -0
  68. package/fesm2022/ngneers-controls-message.mjs.map +1 -0
  69. package/fesm2022/ngneers-controls-number-input.mjs +366 -0
  70. package/fesm2022/ngneers-controls-number-input.mjs.map +1 -0
  71. package/fesm2022/ngneers-controls-paginator.mjs +129 -0
  72. package/fesm2022/ngneers-controls-paginator.mjs.map +1 -0
  73. package/fesm2022/ngneers-controls-popover.mjs +215 -0
  74. package/fesm2022/ngneers-controls-popover.mjs.map +1 -0
  75. package/fesm2022/ngneers-controls-progress.mjs +98 -0
  76. package/fesm2022/ngneers-controls-progress.mjs.map +1 -0
  77. package/fesm2022/ngneers-controls-radio.mjs +152 -0
  78. package/fesm2022/ngneers-controls-radio.mjs.map +1 -0
  79. package/fesm2022/ngneers-controls-roving-focus.mjs +323 -0
  80. package/fesm2022/ngneers-controls-roving-focus.mjs.map +1 -0
  81. package/fesm2022/ngneers-controls-scroll-shadow.mjs +65 -0
  82. package/fesm2022/ngneers-controls-scroll-shadow.mjs.map +1 -0
  83. package/fesm2022/ngneers-controls-scroller.mjs +308 -0
  84. package/fesm2022/ngneers-controls-scroller.mjs.map +1 -0
  85. package/fesm2022/ngneers-controls-select-button.mjs +58 -0
  86. package/fesm2022/ngneers-controls-select-button.mjs.map +1 -0
  87. package/fesm2022/ngneers-controls-select.mjs +417 -0
  88. package/fesm2022/ngneers-controls-select.mjs.map +1 -0
  89. package/fesm2022/ngneers-controls-slider.mjs +167 -0
  90. package/fesm2022/ngneers-controls-slider.mjs.map +1 -0
  91. package/fesm2022/ngneers-controls-snackbar.mjs +414 -0
  92. package/fesm2022/ngneers-controls-snackbar.mjs.map +1 -0
  93. package/fesm2022/ngneers-controls-spin-buttons.mjs +126 -0
  94. package/fesm2022/ngneers-controls-spin-buttons.mjs.map +1 -0
  95. package/fesm2022/ngneers-controls-spinner.mjs +184 -0
  96. package/fesm2022/ngneers-controls-spinner.mjs.map +1 -0
  97. package/fesm2022/ngneers-controls-splitter.mjs +604 -0
  98. package/fesm2022/ngneers-controls-splitter.mjs.map +1 -0
  99. package/fesm2022/ngneers-controls-state.mjs +185 -0
  100. package/fesm2022/ngneers-controls-state.mjs.map +1 -0
  101. package/fesm2022/ngneers-controls-switch.mjs +39 -0
  102. package/fesm2022/ngneers-controls-switch.mjs.map +1 -0
  103. package/fesm2022/ngneers-controls-table.mjs +2634 -0
  104. package/fesm2022/ngneers-controls-table.mjs.map +1 -0
  105. package/fesm2022/ngneers-controls-tabs.mjs +336 -0
  106. package/fesm2022/ngneers-controls-tabs.mjs.map +1 -0
  107. package/fesm2022/ngneers-controls-tag.mjs +30 -0
  108. package/fesm2022/ngneers-controls-tag.mjs.map +1 -0
  109. package/fesm2022/ngneers-controls-toast.mjs +350 -0
  110. package/fesm2022/ngneers-controls-toast.mjs.map +1 -0
  111. package/fesm2022/ngneers-controls-toggle-button.mjs +70 -0
  112. package/fesm2022/ngneers-controls-toggle-button.mjs.map +1 -0
  113. package/fesm2022/ngneers-controls-tooltip.mjs +506 -0
  114. package/fesm2022/ngneers-controls-tooltip.mjs.map +1 -0
  115. package/fesm2022/ngneers-controls-tree.mjs +599 -0
  116. package/fesm2022/ngneers-controls-tree.mjs.map +1 -0
  117. package/fesm2022/ngneers-controls-upload.mjs +377 -0
  118. package/fesm2022/ngneers-controls-upload.mjs.map +1 -0
  119. package/fesm2022/ngneers-controls-utils-ng.mjs +411 -0
  120. package/fesm2022/ngneers-controls-utils-ng.mjs.map +1 -0
  121. package/fesm2022/ngneers-controls-utils.mjs +261 -0
  122. package/fesm2022/ngneers-controls-utils.mjs.map +1 -0
  123. package/fesm2022/ngneers-controls.mjs +6 -0
  124. package/fesm2022/ngneers-controls.mjs.map +1 -0
  125. package/package.json +302 -0
  126. package/types/ngneers-controls-accordion.d.ts +139 -0
  127. package/types/ngneers-controls-api-ng.d.ts +364 -0
  128. package/types/ngneers-controls-api-resize.d.ts +318 -0
  129. package/types/ngneers-controls-api.d.ts +145 -0
  130. package/types/ngneers-controls-avatar.d.ts +54 -0
  131. package/types/ngneers-controls-base.d.ts +277 -0
  132. package/types/ngneers-controls-breadcrumb.d.ts +272 -0
  133. package/types/ngneers-controls-button-group.d.ts +46 -0
  134. package/types/ngneers-controls-button.d.ts +48 -0
  135. package/types/ngneers-controls-calendar.d.ts +466 -0
  136. package/types/ngneers-controls-checkbox.d.ts +40 -0
  137. package/types/ngneers-controls-chip.d.ts +202 -0
  138. package/types/ngneers-controls-default-icons.d.ts +5 -0
  139. package/types/ngneers-controls-defer.d.ts +48 -0
  140. package/types/ngneers-controls-dialog.d.ts +399 -0
  141. package/types/ngneers-controls-directives.d.ts +171 -0
  142. package/types/ngneers-controls-drawer.d.ts +308 -0
  143. package/types/ngneers-controls-edit-inplace.d.ts +263 -0
  144. package/types/ngneers-controls-errors.d.ts +155 -0
  145. package/types/ngneers-controls-filter.d.ts +609 -0
  146. package/types/ngneers-controls-hint.d.ts +88 -0
  147. package/types/ngneers-controls-i18n-translations-de.d.ts +164 -0
  148. package/types/ngneers-controls-i18n-translations-en.d.ts +164 -0
  149. package/types/ngneers-controls-i18n.d.ts +186 -0
  150. package/types/ngneers-controls-icon.d.ts +68 -0
  151. package/types/ngneers-controls-inplace.d.ts +98 -0
  152. package/types/ngneers-controls-input-field.d.ts +248 -0
  153. package/types/ngneers-controls-input.d.ts +30 -0
  154. package/types/ngneers-controls-item-view.d.ts +215 -0
  155. package/types/ngneers-controls-list-box.d.ts +334 -0
  156. package/types/ngneers-controls-mask-input.d.ts +434 -0
  157. package/types/ngneers-controls-menu.d.ts +192 -0
  158. package/types/ngneers-controls-message.d.ts +21 -0
  159. package/types/ngneers-controls-number-input.d.ts +146 -0
  160. package/types/ngneers-controls-paginator.d.ts +255 -0
  161. package/types/ngneers-controls-popover.d.ts +116 -0
  162. package/types/ngneers-controls-progress.d.ts +49 -0
  163. package/types/ngneers-controls-radio.d.ts +91 -0
  164. package/types/ngneers-controls-roving-focus.d.ts +107 -0
  165. package/types/ngneers-controls-scroll-shadow.d.ts +25 -0
  166. package/types/ngneers-controls-scroller.d.ts +151 -0
  167. package/types/ngneers-controls-select-button.d.ts +51 -0
  168. package/types/ngneers-controls-select.d.ts +409 -0
  169. package/types/ngneers-controls-slider.d.ts +57 -0
  170. package/types/ngneers-controls-snackbar.d.ts +330 -0
  171. package/types/ngneers-controls-spin-buttons.d.ts +69 -0
  172. package/types/ngneers-controls-spinner.d.ts +117 -0
  173. package/types/ngneers-controls-splitter.d.ts +465 -0
  174. package/types/ngneers-controls-state.d.ts +102 -0
  175. package/types/ngneers-controls-switch.d.ts +18 -0
  176. package/types/ngneers-controls-table.d.ts +3127 -0
  177. package/types/ngneers-controls-tabs.d.ts +118 -0
  178. package/types/ngneers-controls-tag.d.ts +21 -0
  179. package/types/ngneers-controls-toast.d.ts +298 -0
  180. package/types/ngneers-controls-toggle-button.d.ts +48 -0
  181. package/types/ngneers-controls-tooltip.d.ts +189 -0
  182. package/types/ngneers-controls-tree.d.ts +409 -0
  183. package/types/ngneers-controls-upload.d.ts +347 -0
  184. package/types/ngneers-controls-utils-ng.d.ts +129 -0
  185. package/types/ngneers-controls-utils.d.ts +130 -0
  186. package/types/ngneers-controls.d.ts +2 -0
@@ -0,0 +1,708 @@
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, DestroyRef, input, booleanAttribute, numberAttribute, computed, output, viewChild, signal, effect, Component } from '@angular/core';
4
+ import { getActiveFilterConditions, executeFilter } from '@ngneers/controls/api';
5
+ import { ValueControlBase, NgnPt, provideSelf } from '@ngneers/controls/base';
6
+ import { NgnButton } from '@ngneers/controls/button';
7
+ import { NgnCalendar } from '@ngneers/controls/calendar';
8
+ import { I18n } from '@ngneers/controls/i18n';
9
+ import { NgnIcon } from '@ngneers/controls/icon';
10
+ import { NgnInput } from '@ngneers/controls/input';
11
+ import { NgnInputField } from '@ngneers/controls/input-field';
12
+ import { NgnPopover } from '@ngneers/controls/popover';
13
+ import { NgnSelect } from '@ngneers/controls/select';
14
+ import { deepMerge, NgnError } from '@ngneers/controls/utils';
15
+ import { filterControlTemplate } from '@ngneers/controls-themes/templates/filter';
16
+
17
+ function defaultOperatorsForType(dataType) {
18
+ switch (dataType) {
19
+ case 'string':
20
+ return [
21
+ { id: 'isEqual', labelKey: 'filter_operators_isEqual', requiresValue: true },
22
+ { id: 'isNotEqual', labelKey: 'filter_operators_isNotEqual', requiresValue: true },
23
+ { id: 'contains', labelKey: 'filter_operators_contains', requiresValue: true },
24
+ { id: 'startsWith', labelKey: 'filter_operators_startsWith', requiresValue: true },
25
+ { id: 'endsWith', labelKey: 'filter_operators_endsWith', requiresValue: true },
26
+ { id: 'isEmpty', labelKey: 'filter_operators_isEmpty', requiresValue: false },
27
+ { id: 'isNotEmpty', labelKey: 'filter_operators_isNotEmpty', requiresValue: false },
28
+ ];
29
+ case 'number':
30
+ return [
31
+ { id: 'isEqual', labelKey: 'filter_operators_isEqual', requiresValue: true },
32
+ { id: 'isNotEqual', labelKey: 'filter_operators_isNotEqual', requiresValue: true },
33
+ { id: 'greaterThan', labelKey: 'filter_operators_greaterThan', requiresValue: true },
34
+ {
35
+ id: 'greaterThanOrEqual',
36
+ labelKey: 'filter_operators_greaterThanOrEqual',
37
+ requiresValue: true,
38
+ },
39
+ { id: 'lessThan', labelKey: 'filter_operators_lessThan', requiresValue: true },
40
+ {
41
+ id: 'lessThanOrEqual',
42
+ labelKey: 'filter_operators_lessThanOrEqual',
43
+ requiresValue: true,
44
+ },
45
+ ];
46
+ case 'date':
47
+ case 'dateTime':
48
+ return [
49
+ { id: 'isEqual', labelKey: 'filter_operators_isEqual', requiresValue: true },
50
+ { id: 'isNotEqual', labelKey: 'filter_operators_isNotEqual', requiresValue: true },
51
+ { id: 'greaterThan', labelKey: 'filter_operators_after', requiresValue: true },
52
+ { id: 'greaterThanOrEqual', labelKey: 'filter_operators_onOrAfter', requiresValue: true },
53
+ { id: 'lessThan', labelKey: 'filter_operators_before', requiresValue: true },
54
+ { id: 'lessThanOrEqual', labelKey: 'filter_operators_onOrBefore', requiresValue: true },
55
+ ];
56
+ case 'boolean':
57
+ return [
58
+ { id: 'isTrue', labelKey: 'filter_operators_isTrue', requiresValue: false },
59
+ { id: 'isFalse', labelKey: 'filter_operators_isFalse', requiresValue: false },
60
+ ];
61
+ case 'custom':
62
+ return [
63
+ { id: 'custom', labelKey: 'filter_operators_custom', requiresValue: true },
64
+ ];
65
+ case 'list':
66
+ return [
67
+ { id: 'in', labelKey: 'filter_operators_in', requiresValue: true },
68
+ ];
69
+ }
70
+ }
71
+ /**
72
+ * @category control
73
+ */
74
+ class NgnFilter extends ValueControlBase {
75
+ theme = this.injectThemeTemplate(filterControlTemplate, 'root');
76
+ _i18n = inject(I18n);
77
+ i18n = this._i18n.translations;
78
+ unsafeI18n = this._i18n.unsafe.bind(this._i18n);
79
+ _destroyRef = inject(DestroyRef);
80
+ /**
81
+ * Data to filter.
82
+ * @default []
83
+ */
84
+ data = input([], /* @ts-ignore */
85
+ ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
86
+ /**
87
+ * Datatype of the values in {@link data}. Determines the available operators and editor UI.
88
+ * @default 'string'
89
+ */
90
+ dataType = input('string', /* @ts-ignore */
91
+ ...(ngDevMode ? [{ debugName: "dataType" }] : /* istanbul ignore next */ []));
92
+ /**
93
+ * Allows multiple conditions and a match mode (any/all).
94
+ * @default false
95
+ */
96
+ allowMultiple = input(false, { ...(ngDevMode ? { debugName: "allowMultiple" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
97
+ /**
98
+ * Options for `dataType="list"`.
99
+ * When provided, they are used as-is. When `null` or `undefined`, options are derived from {@link data}.
100
+ * @default undefined
101
+ */
102
+ listOptions = input(/* @ts-ignore */
103
+ ...(ngDevMode ? [undefined, { debugName: "listOptions" }] : /* istanbul ignore next */ []));
104
+ /**
105
+ * Controls how the filter is rendered.
106
+ * - `inline`: renders the filter rows directly
107
+ * - `input`: renders an input-like trigger that opens a popover (default)
108
+ * - `headless`: renders only the popover and can be opened programmatically via {@link show}
109
+ * @default 'input'
110
+ */
111
+ mode = input('input', /* @ts-ignore */
112
+ ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
113
+ /**
114
+ * Popover anchor used only in `headless` mode.
115
+ * In `input` mode the anchor is the internal input-field.
116
+ * @default null
117
+ */
118
+ anchor = input(null, /* @ts-ignore */
119
+ ...(ngDevMode ? [{ debugName: "anchor" }] : /* istanbul ignore next */ []));
120
+ /**
121
+ * When true, the component will filter {@link data} locally and emit the filtered result
122
+ * via {@link filterResultChange}. When false, only the filter configuration is emitted
123
+ * (useful for server-side filtering).
124
+ * @default true
125
+ */
126
+ filterLocally = input(true, { ...(ngDevMode ? { debugName: "filterLocally" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
127
+ /**
128
+ * When true, filters apply automatically as the user types/selects.
129
+ * When false, the user must click "Apply" to commit the filter.
130
+ * @default true
131
+ */
132
+ autoApply = input(true, { ...(ngDevMode ? { debugName: "autoApply" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
133
+ /**
134
+ * Debounce time in milliseconds for auto-apply mode.
135
+ * Only relevant when {@link autoApply} is `true`.
136
+ * @default 300
137
+ */
138
+ autoApplyDebounce = input(300, { ...(ngDevMode ? { debugName: "autoApplyDebounce" } : /* istanbul ignore next */ {}), transform: numberAttribute });
139
+ /**
140
+ * Options for the popover (when {@link mode} is not `inline`).
141
+ * @default {}
142
+ */
143
+ popoverOptions = input({}, /* @ts-ignore */
144
+ ...(ngDevMode ? [{ debugName: "popoverOptions" }] : /* istanbul ignore next */ []));
145
+ appliedPopoverOptions = computed(() => deepMerge({
146
+ sizeConstraints: {
147
+ width: '360px',
148
+ minHeight: '120px',
149
+ maxHeight: '500px',
150
+ },
151
+ placement: 'bottom-start',
152
+ }, this.popoverOptions()), /* @ts-ignore */
153
+ ...(ngDevMode ? [{ debugName: "appliedPopoverOptions" }] : /* istanbul ignore next */ []));
154
+ /** Emits whenever the filter configuration changes. */
155
+ filterChange = output();
156
+ /** Emits the filtered data whenever local filtering is enabled. */
157
+ filterResultChange = output();
158
+ _popover = viewChild(NgnPopover, /* @ts-ignore */
159
+ ...(ngDevMode ? [{ debugName: "_popover" }] : /* istanbul ignore next */ []));
160
+ /**
161
+ * Internal working config that the template binds to.
162
+ * In auto-apply mode, this syncs to value() via debounce.
163
+ * In manual mode, only apply() commits this to value().
164
+ */
165
+ workingConfig = signal(null, /* @ts-ignore */
166
+ ...(ngDevMode ? [{ debugName: "workingConfig" }] : /* istanbul ignore next */ []));
167
+ /** Snapshot of value() taken when popover opens in manual mode, for cancel to restore. */
168
+ _snapshotConfig = null;
169
+ /** Debounce timer handle for auto-apply. */
170
+ _debounceTimer = null;
171
+ /** Whether there is an active filter applied. */
172
+ hasActiveFilter = computed(() => {
173
+ const cfg = this.value();
174
+ if (!cfg)
175
+ return false;
176
+ if (cfg.dataType === 'list') {
177
+ const raw = cfg.conditions[0]?.rawValue;
178
+ if (!raw)
179
+ return false;
180
+ try {
181
+ const parsed = JSON.parse(raw);
182
+ return Array.isArray(parsed) && parsed.length > 0;
183
+ }
184
+ catch {
185
+ return false;
186
+ }
187
+ }
188
+ return this.activeConditionConfigs().length > 0;
189
+ }, /* @ts-ignore */
190
+ ...(ngDevMode ? [{ debugName: "hasActiveFilter" }] : /* istanbul ignore next */ []));
191
+ createCondition(operator) {
192
+ return { operator, rawValue: null };
193
+ }
194
+ defaultConfig() {
195
+ if (this.dataType() === 'list') {
196
+ return {
197
+ dataType: 'list',
198
+ matchMode: 'all',
199
+ conditions: [{ operator: 'in', rawValue: null }],
200
+ };
201
+ }
202
+ const firstOp = this.operatorDefs()[0]?.id ?? 'isEqual';
203
+ return {
204
+ dataType: this.dataType(),
205
+ matchMode: 'all',
206
+ conditions: [
207
+ {
208
+ operator: firstOp,
209
+ rawValue: null,
210
+ },
211
+ ],
212
+ };
213
+ }
214
+ /**
215
+ * Config used for UI rendering.
216
+ * Always reads from workingConfig (the live editing state).
217
+ * Falls back to a default config when null.
218
+ */
219
+ templateConfig = computed(() => (this.workingConfig() ?? this.defaultConfig()), /* @ts-ignore */
220
+ ...(ngDevMode ? [{ debugName: "templateConfig" }] : /* istanbul ignore next */ []));
221
+ templateListSelection = computed(() => {
222
+ const cfg = this.templateConfig();
223
+ if (cfg.dataType !== 'list') {
224
+ return [];
225
+ }
226
+ const raw = cfg.conditions[0]?.rawValue;
227
+ if (!raw) {
228
+ return [];
229
+ }
230
+ try {
231
+ const parsed = JSON.parse(raw);
232
+ return Array.isArray(parsed) ? parsed.map(v => String(v)) : [];
233
+ }
234
+ catch {
235
+ return [];
236
+ }
237
+ }, /* @ts-ignore */
238
+ ...(ngDevMode ? [{ debugName: "templateListSelection" }] : /* istanbul ignore next */ []));
239
+ operatorDefs = computed(() => {
240
+ const dt = this.dataType();
241
+ const defs = defaultOperatorsForType(dt);
242
+ if (dt === 'custom' && defs.length === 1 && defs[0]?.id === 'custom') {
243
+ return defs;
244
+ }
245
+ return defs;
246
+ }, /* @ts-ignore */
247
+ ...(ngDevMode ? [{ debugName: "operatorDefs" }] : /* istanbul ignore next */ []));
248
+ operatorOptions = computed(() => {
249
+ const options = this.operatorDefs().map(op => ({
250
+ label: this.i18n[op.labelKey],
251
+ value: op.id,
252
+ testId: `filter-operator-${op.id}`,
253
+ }));
254
+ return options;
255
+ }, /* @ts-ignore */
256
+ ...(ngDevMode ? [{ debugName: "operatorOptions" }] : /* istanbul ignore next */ []));
257
+ autoListOptions = computed(() => {
258
+ const seen = new Set();
259
+ const items = [];
260
+ for (const raw of this.data()) {
261
+ const s = String(raw);
262
+ if (seen.has(s)) {
263
+ continue;
264
+ }
265
+ seen.add(s);
266
+ items.push({ label: s, value: s, testId: `filter-list-${s}` });
267
+ }
268
+ return items.sort((a, b) => {
269
+ if (typeof a.label === 'string' && typeof b.label === 'string') {
270
+ return a.label.localeCompare(b.label);
271
+ }
272
+ if (typeof a.label === 'function' && typeof b.label === 'function') {
273
+ return a.label().localeCompare(b.label());
274
+ }
275
+ return 0;
276
+ });
277
+ }, /* @ts-ignore */
278
+ ...(ngDevMode ? [{ debugName: "autoListOptions" }] : /* istanbul ignore next */ []));
279
+ resolvedListOptions = computed(() => {
280
+ const listOptions = this.listOptions();
281
+ return listOptions
282
+ ? listOptions.map((opt) => ({
283
+ label: opt,
284
+ value: opt,
285
+ testId: `filter-list-${opt}`,
286
+ }))
287
+ : this.autoListOptions();
288
+ }, /* @ts-ignore */
289
+ ...(ngDevMode ? [{ debugName: "resolvedListOptions" }] : /* istanbul ignore next */ []));
290
+ matchModeOptions = [
291
+ { label: this.i18n['filter_match_all'], value: 'all', testId: 'filter-match-all' },
292
+ { label: this.i18n['filter_match_any'], value: 'any', testId: 'filter-match-any' },
293
+ ];
294
+ inputType = computed(() => {
295
+ switch (this.dataType()) {
296
+ case 'number':
297
+ return 'number';
298
+ case 'date':
299
+ return 'date';
300
+ case 'dateTime':
301
+ return 'datetime-local';
302
+ default:
303
+ return 'text';
304
+ }
305
+ }, /* @ts-ignore */
306
+ ...(ngDevMode ? [{ debugName: "inputType" }] : /* istanbul ignore next */ []));
307
+ isDateType = computed(() => this.dataType() === 'date' || this.dataType() === 'dateTime', /* @ts-ignore */
308
+ ...(ngDevMode ? [{ debugName: "isDateType" }] : /* istanbul ignore next */ []));
309
+ isShowTime = computed(() => this.dataType() === 'dateTime', /* @ts-ignore */
310
+ ...(ngDevMode ? [{ debugName: "isShowTime" }] : /* istanbul ignore next */ []));
311
+ /** Parse a rawValue string to a Date for the calendar component. */
312
+ parseRawDate(rawValue) {
313
+ if (!rawValue)
314
+ return null;
315
+ const d = new Date(rawValue);
316
+ return Number.isNaN(d.getTime()) ? null : d;
317
+ }
318
+ /** Convert a Date from the calendar to a rawValue string. */
319
+ dateToRaw(date) {
320
+ if (!date || Number.isNaN(date.getTime()))
321
+ return null;
322
+ if (this.dataType() === 'dateTime') {
323
+ // Format as YYYY-MM-DDTHH:mm for datetime-local compatibility
324
+ const pad = (n) => String(n).padStart(2, '0');
325
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
326
+ }
327
+ // Format as YYYY-MM-DD for date
328
+ const pad = (n) => String(n).padStart(2, '0');
329
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
330
+ }
331
+ summaryText = computed(() => {
332
+ const currentValue = this.value();
333
+ if (!currentValue) {
334
+ return this.i18n['filter_noFilter']();
335
+ }
336
+ if (currentValue.dataType === 'list') {
337
+ const selected = this.templateListSelection();
338
+ if (selected.length === 0) {
339
+ return this.i18n['filter_noFilter']();
340
+ }
341
+ if (selected.length === 1) {
342
+ return selected[0] ?? this.i18n['filter_noFilter']();
343
+ }
344
+ return `${selected.length} ${this.i18n['filter_selected']()}`;
345
+ }
346
+ const active = this.activeConditionConfigs();
347
+ if (active.length === 0) {
348
+ return this.i18n['filter_noFilter']();
349
+ }
350
+ if (active.length === 1) {
351
+ const c = active[0];
352
+ const def = this.operatorDefs().find(d => d.id === c?.operator);
353
+ const operatorLabel = def ? this.i18n._unsafe[def.labelKey]?.() : c?.operator;
354
+ return c?.rawValue == null || c?.rawValue === ''
355
+ ? operatorLabel
356
+ : `${operatorLabel} "${c.rawValue}"`;
357
+ }
358
+ const matchLabel = currentValue.matchMode === 'all'
359
+ ? this.i18n['filter_match_all']()
360
+ : this.i18n['filter_match_any']();
361
+ return `${active.length} ${this.i18n['filter_conditions']()} (${matchLabel})`;
362
+ }, /* @ts-ignore */
363
+ ...(ngDevMode ? [{ debugName: "summaryText" }] : /* istanbul ignore next */ []));
364
+ activeConditionConfigs = computed(() => {
365
+ const cfg = this.value();
366
+ if (!cfg || cfg.dataType === 'list') {
367
+ return [];
368
+ }
369
+ const defs = this.operatorDefs();
370
+ const defById = new Map(defs.map(d => [d.id, d]));
371
+ return cfg.conditions.filter(c => {
372
+ const def = defById.get(c.operator);
373
+ const requiresValue = def?.requiresValue ?? true;
374
+ return !requiresValue || (c.rawValue != null && c.rawValue !== '');
375
+ });
376
+ }, /* @ts-ignore */
377
+ ...(ngDevMode ? [{ debugName: "activeConditionConfigs" }] : /* istanbul ignore next */ []));
378
+ activeConditions = computed(() => {
379
+ const cfg = this.value();
380
+ return cfg ? getActiveFilterConditions(cfg) : [];
381
+ }, /* @ts-ignore */
382
+ ...(ngDevMode ? [{ debugName: "activeConditions" }] : /* istanbul ignore next */ []));
383
+ filteredData = computed(() => {
384
+ if (!this.filterLocally()) {
385
+ return this.data();
386
+ }
387
+ const cfg = this.value();
388
+ if (!cfg) {
389
+ return this.data();
390
+ }
391
+ return executeFilter(this.data(), cfg);
392
+ }, /* @ts-ignore */
393
+ ...(ngDevMode ? [{ debugName: "filteredData" }] : /* istanbul ignore next */ []));
394
+ /** The current match mode label for the condition divider */
395
+ matchModeLabel = computed(() => {
396
+ const cfg = this.templateConfig();
397
+ return cfg.matchMode === 'all'
398
+ ? this.i18n['filter_match_and']()
399
+ : this.i18n['filter_match_or']();
400
+ }, /* @ts-ignore */
401
+ ...(ngDevMode ? [{ debugName: "matchModeLabel" }] : /* istanbul ignore next */ []));
402
+ constructor() {
403
+ super();
404
+ // Initialize workingConfig from initial value.
405
+ this.workingConfig.set(this.value());
406
+ effect(() => {
407
+ this.filterChange.emit(this.value() ?? null);
408
+ });
409
+ effect(() => {
410
+ if (!this.filterLocally()) {
411
+ return;
412
+ }
413
+ this.filterResultChange.emit(this.filteredData());
414
+ });
415
+ effect(() => {
416
+ if (!this.allowMultiple()) {
417
+ const cfg = this.value();
418
+ if (!cfg || cfg.dataType === 'list') {
419
+ return;
420
+ }
421
+ if (cfg.conditions.length <= 1 && cfg.matchMode === 'all') {
422
+ return;
423
+ }
424
+ this.value.set({
425
+ ...cfg,
426
+ matchMode: 'all',
427
+ conditions: cfg.conditions.slice(0, 1),
428
+ });
429
+ }
430
+ });
431
+ effect(() => {
432
+ const defs = this.operatorDefs();
433
+ const first = defs[0];
434
+ if (!first) {
435
+ throw new NgnError('filter', 'No operators available.');
436
+ }
437
+ const defIds = new Set(defs.map(d => d.id));
438
+ const normalizeConditions = (conditions) => {
439
+ let changed = false;
440
+ const next = conditions.map(c => {
441
+ if (defIds.has(c.operator)) {
442
+ return c;
443
+ }
444
+ changed = true;
445
+ return { ...c, operator: first.id, rawValue: null };
446
+ });
447
+ return changed ? next : conditions;
448
+ };
449
+ const cfg = this.value();
450
+ if (!cfg || cfg.dataType === 'list') {
451
+ return;
452
+ }
453
+ const current = cfg.conditions;
454
+ const next = normalizeConditions(current);
455
+ if (next !== current) {
456
+ this.value.set({ ...cfg, conditions: next });
457
+ }
458
+ });
459
+ this._destroyRef.onDestroy(() => {
460
+ if (this._debounceTimer != null) {
461
+ clearTimeout(this._debounceTimer);
462
+ }
463
+ });
464
+ }
465
+ /**
466
+ * Commits the working config to the actual value, with optional debounce.
467
+ * Called after every working config change.
468
+ */
469
+ commitWorkingConfig() {
470
+ if (this.autoApply()) {
471
+ if (this._debounceTimer != null) {
472
+ clearTimeout(this._debounceTimer);
473
+ }
474
+ const debounceMs = this.autoApplyDebounce();
475
+ if (debounceMs > 0) {
476
+ this._debounceTimer = setTimeout(() => {
477
+ this._debounceTimer = null;
478
+ this.value.set(this.workingConfig());
479
+ }, debounceMs);
480
+ }
481
+ else {
482
+ this.value.set(this.workingConfig());
483
+ }
484
+ }
485
+ // In manual mode, do nothing — wait for apply()
486
+ }
487
+ /** Updates the working config and optionally auto-applies. */
488
+ updateWorkingConfig(config) {
489
+ this.workingConfig.set(config);
490
+ this.commitWorkingConfig();
491
+ }
492
+ toggleMatchMode() {
493
+ const cfg = this.templateConfig();
494
+ if (cfg.dataType === 'list' || !this.allowMultiple()) {
495
+ return;
496
+ }
497
+ const newMode = cfg.matchMode === 'all' ? 'any' : 'all';
498
+ this.updateWorkingConfig({
499
+ ...cfg,
500
+ matchMode: newMode,
501
+ });
502
+ }
503
+ setMatchMode(value) {
504
+ if (value === 'all' || value === 'any') {
505
+ const cfg = this.templateConfig();
506
+ if (cfg.dataType === 'list') {
507
+ return;
508
+ }
509
+ this.updateWorkingConfig({
510
+ ...cfg,
511
+ matchMode: this.allowMultiple() ? value : 'all',
512
+ });
513
+ }
514
+ }
515
+ operatorRequiresValue(operator) {
516
+ const def = this.operatorDefs().find(d => d.id === operator);
517
+ return def?.requiresValue ?? true;
518
+ }
519
+ setOperator(index, operator) {
520
+ if (!operator) {
521
+ return;
522
+ }
523
+ const cfg = this.templateConfig();
524
+ if (cfg.dataType === 'list') {
525
+ return;
526
+ }
527
+ const next = cfg.conditions.slice();
528
+ const existing = next[index];
529
+ if (!existing) {
530
+ return;
531
+ }
532
+ next[index] = {
533
+ ...existing,
534
+ operator,
535
+ rawValue: this.operatorRequiresValue(operator) ? existing.rawValue : null,
536
+ };
537
+ this.updateWorkingConfig({
538
+ ...cfg,
539
+ matchMode: this.allowMultiple() ? cfg.matchMode : 'all',
540
+ conditions: next,
541
+ });
542
+ }
543
+ setRawValue(index, rawValue) {
544
+ const cfg = this.templateConfig();
545
+ if (cfg.dataType === 'list') {
546
+ return;
547
+ }
548
+ const next = cfg.conditions.slice();
549
+ const existing = next[index];
550
+ if (!existing) {
551
+ return;
552
+ }
553
+ next[index] = { ...existing, rawValue };
554
+ this.updateWorkingConfig({
555
+ ...cfg,
556
+ matchMode: this.allowMultiple() ? cfg.matchMode : 'all',
557
+ conditions: next,
558
+ });
559
+ }
560
+ setDateValue(index, date) {
561
+ this.setRawValue(index, this.dateToRaw(date));
562
+ }
563
+ addCondition() {
564
+ if (!this.allowMultiple()) {
565
+ return;
566
+ }
567
+ const cfg = this.templateConfig();
568
+ if (cfg.dataType === 'list') {
569
+ return;
570
+ }
571
+ const firstOp = this.operatorDefs()[0]?.id ?? 'isEqual';
572
+ this.updateWorkingConfig({
573
+ ...cfg,
574
+ matchMode: cfg.matchMode,
575
+ conditions: [...cfg.conditions, this.createCondition(firstOp)],
576
+ });
577
+ }
578
+ removeCondition(index) {
579
+ if (!this.allowMultiple()) {
580
+ return;
581
+ }
582
+ const cfg = this.templateConfig();
583
+ if (cfg.dataType === 'list') {
584
+ return;
585
+ }
586
+ const current = cfg.conditions;
587
+ if (current.length <= 1) {
588
+ return;
589
+ }
590
+ const next = current.slice();
591
+ next.splice(index, 1);
592
+ this.updateWorkingConfig({
593
+ ...cfg,
594
+ matchMode: cfg.matchMode,
595
+ conditions: next,
596
+ });
597
+ }
598
+ setListSelection(value) {
599
+ const arr = Array.isArray(value)
600
+ ? value.map(v => String(v))
601
+ : value == null
602
+ ? []
603
+ : [String(value)];
604
+ this.updateWorkingConfig({
605
+ dataType: 'list',
606
+ matchMode: 'all',
607
+ conditions: [
608
+ {
609
+ operator: 'in',
610
+ rawValue: arr.length > 0 ? JSON.stringify(arr) : null,
611
+ },
612
+ ],
613
+ });
614
+ }
615
+ /** Applies the working config (manual mode). */
616
+ apply() {
617
+ const cfg = this.workingConfig();
618
+ this.value.set(cfg);
619
+ // Update snapshot so next cancel restores to this applied state
620
+ this._snapshotConfig = cfg ? JSON.parse(JSON.stringify(cfg)) : null;
621
+ if (this.mode() !== 'inline') {
622
+ this.hide();
623
+ }
624
+ }
625
+ /** Cancels changes and restores the snapshot (manual mode). */
626
+ cancelChanges() {
627
+ // Clear any pending debounce
628
+ if (this._debounceTimer != null) {
629
+ clearTimeout(this._debounceTimer);
630
+ this._debounceTimer = null;
631
+ }
632
+ this.workingConfig.set(this._snapshotConfig ? JSON.parse(JSON.stringify(this._snapshotConfig)) : null);
633
+ if (this.mode() !== 'inline') {
634
+ this.hide();
635
+ }
636
+ }
637
+ clear() {
638
+ this.updateWorkingConfig(null);
639
+ if (!this.autoApply()) {
640
+ // In manual mode, also commit clear immediately
641
+ this.value.set(null);
642
+ this._snapshotConfig = null;
643
+ }
644
+ if (this.mode() !== 'inline') {
645
+ this.hide();
646
+ }
647
+ }
648
+ /** Shows the filter popup. Only works when {@link mode} is not `inline`. */
649
+ show() {
650
+ if (this.mode() === 'inline') {
651
+ throw new NgnError('filter', 'Cannot open an inline filter.');
652
+ }
653
+ if (this.mode() === 'headless' && !this.anchor()) {
654
+ throw new NgnError('filter', 'Headless mode requires an [anchor] input.');
655
+ }
656
+ const popover = this._popover();
657
+ if (!popover) {
658
+ throw new NgnError('filter', 'Popover is not available.');
659
+ }
660
+ // Snapshot current value for cancel
661
+ const val = this.value();
662
+ this._snapshotConfig = val ? JSON.parse(JSON.stringify(val)) : null;
663
+ // Reset working config to current value
664
+ this.workingConfig.set(val);
665
+ popover.show();
666
+ }
667
+ /** Hides the filter popup. Only works when {@link mode} is not `inline`. */
668
+ hide() {
669
+ if (this.mode() === 'inline') {
670
+ throw new NgnError('filter', 'Cannot close an inline filter.');
671
+ }
672
+ this._popover()?.hide();
673
+ }
674
+ onKeyDown(event) {
675
+ if (event.key === 'Enter') {
676
+ if (this.mode() !== 'inline') {
677
+ this._popover()?.toggle();
678
+ event.stopPropagation();
679
+ event.preventDefault();
680
+ }
681
+ }
682
+ }
683
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnFilter, deps: [], target: i0.ɵɵFactoryTarget.Component });
684
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: NgnFilter, isStandalone: true, selector: "ngn-filter", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, dataType: { classPropertyName: "dataType", publicName: "dataType", isSignal: true, isRequired: false, transformFunction: null }, allowMultiple: { classPropertyName: "allowMultiple", publicName: "allowMultiple", isSignal: true, isRequired: false, transformFunction: null }, listOptions: { classPropertyName: "listOptions", publicName: "listOptions", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, anchor: { classPropertyName: "anchor", publicName: "anchor", isSignal: true, isRequired: false, transformFunction: null }, filterLocally: { classPropertyName: "filterLocally", publicName: "filterLocally", isSignal: true, isRequired: false, transformFunction: null }, autoApply: { classPropertyName: "autoApply", publicName: "autoApply", isSignal: true, isRequired: false, transformFunction: null }, autoApplyDebounce: { classPropertyName: "autoApplyDebounce", publicName: "autoApplyDebounce", isSignal: true, isRequired: false, transformFunction: null }, popoverOptions: { classPropertyName: "popoverOptions", publicName: "popoverOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filterChange: "filterChange", filterResultChange: "filterResultChange" }, host: { styleAttribute: "display: block;" }, providers: [provideSelf(NgnFilter)], viewQueries: [{ propertyName: "_popover", first: true, predicate: NgnPopover, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<ng-template #rowsTemplate>\n <div [ptInt]=\"this\" [ptClass]=\"'rows'\">\n @if (dataType() === 'list') {\n <div [ptInt]=\"this\" [ptClass]=\"'row'\" data-testid=\"filter-row-list\">\n <ngn-input-field\n [ptInt]=\"this\"\n [ptDep]=\"'value'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n >\n <ngn-select\n [ptInt]=\"this\"\n [ptDep]=\"'list-select'\"\n [multiple]=\"true\"\n [options]=\"resolvedListOptions()\"\n [value]=\"$any(templateListSelection())\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (valueChange)=\"setListSelection($event)\"\n />\n </ngn-input-field>\n </div>\n } @else {\n @for (c of templateConfig().conditions; track $index; let i = $index; let last = $last) {\n <div [ptInt]=\"this\" [ptClass]=\"'row'\" [attr.data-testid]=\"'filter-row-' + i\">\n <ngn-input-field\n [ptInt]=\"this\"\n [ptDep]=\"operatorRequiresValue(c.operator) ? 'operator' : 'value'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n >\n <ngn-select\n [ptInt]=\"this\"\n [ptDep]=\"'operator-select'\"\n [options]=\"operatorOptions()\"\n [label]=\"i18n['filter_operator']()\"\n [value]=\"c.operator\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (valueChange)=\"setOperator(i, $event)\"\n />\n </ngn-input-field>\n @if (operatorRequiresValue(c.operator)) {\n @if (isDateType()) {\n <ngn-input-field\n [ptInt]=\"this\"\n [ptDep]=\"'value'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n >\n <ngn-calendar\n [ptInt]=\"this\"\n [ptDep]=\"'calendar'\"\n [showTime]=\"isShowTime()\"\n [value]=\"parseRawDate(c.rawValue)\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (valueChange)=\"setDateValue(i, $event)\"\n />\n </ngn-input-field>\n } @else {\n <ngn-input-field\n [ptInt]=\"this\"\n [ptDep]=\"'value'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n >\n <input\n ngnInput\n [ptInt]=\"this\"\n [ptDep]=\"'input'\"\n [attr.aria-label]=\"i18n['filter_value']()\"\n [type]=\"inputType()\"\n [value]=\"c.rawValue || ''\"\n [disabled]=\"disabled()\"\n [readOnly]=\"readonly()\"\n (valueChange)=\"setRawValue(i, $event)\"\n />\n </ngn-input-field>\n }\n }\n\n @if (allowMultiple() && templateConfig().conditions.length > 1) {\n <button\n type=\"button\"\n ngnButton\n kind=\"icon\"\n [ptInt]=\"this\"\n [ptDep]=\"'remove-btn'\"\n [disabled]=\"disabled() || readonly()\"\n (click)=\"removeCondition(i)\"\n [attr.aria-label]=\"i18n['filter_removeCondition']() + ' ' + (i + 1)\"\n data-testid=\"filter-remove-condition\"\n >\n <ngn-icon defaultIcon=\"filter-remove\" />\n </button>\n }\n </div>\n\n @if (allowMultiple() && !last && templateConfig().conditions.length > 1) {\n <div\n [ptInt]=\"this\"\n [ptClass]=\"'condition-divider'\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"!disabled() && !readonly() && toggleMatchMode()\"\n (keydown.enter)=\"!disabled() && !readonly() && toggleMatchMode()\"\n (keydown.space)=\"\n !disabled() && !readonly() && toggleMatchMode(); $event.preventDefault()\n \"\n [attr.aria-disabled]=\"disabled() || readonly()\"\n data-testid=\"filter-match-mode\"\n >\n {{ matchModeLabel() }}\n </div>\n }\n }\n }\n </div>\n</ng-template>\n\n<ng-template #footerTemplate>\n @if (!disabled() && !readonly()) {\n <div [ptInt]=\"this\" [ptClass]=\"'footer'\">\n @if (dataType() !== 'list' && allowMultiple()) {\n <button\n type=\"button\"\n ngnButton\n kind=\"secondary\"\n [ptInt]=\"this\"\n [ptDep]=\"'add-button'\"\n (click)=\"addCondition()\"\n data-testid=\"filter-add-rule\"\n >\n <span aria-hidden=\"true\">+</span>\n {{ i18n['filter_addCondition']() }}\n </button>\n }\n <div [ptInt]=\"this\" [ptClass]=\"'footer-actions'\">\n @if (!autoApply()) {\n <button\n type=\"button\"\n ngnButton\n kind=\"secondary\"\n [ptInt]=\"this\"\n [ptDep]=\"'cancel-button'\"\n (click)=\"cancelChanges()\"\n data-testid=\"filter-cancel\"\n >\n {{ i18n['filter_cancel']() }}\n </button>\n <button\n type=\"button\"\n ngnButton\n kind=\"primary\"\n [ptInt]=\"this\"\n [ptDep]=\"'apply-button'\"\n (click)=\"apply()\"\n data-testid=\"filter-apply\"\n >\n {{ i18n['filter_apply']() }}\n </button>\n } @else {\n <button\n type=\"button\"\n ngnButton\n kind=\"secondary\"\n [ptInt]=\"this\"\n [ptDep]=\"'clear-button'\"\n (click)=\"clear()\"\n data-testid=\"filter-clear\"\n >\n {{ i18n['filter_clear']() }}\n </button>\n }\n </div>\n </div>\n }\n</ng-template>\n\n<ng-template #popoverTemplate let-anchor=\"anchor\">\n <ngn-popover\n #popover\n [ptInt]=\"this\"\n [ptDep]=\"'popover'\"\n [options]=\"appliedPopoverOptions()\"\n [anchor]=\"anchor\"\n [hasShrinkableContent]=\"true\"\n (keydown)=\"onKeyDown($event)\"\n [id]=\"inputId() + '_popover'\"\n >\n <ng-template #lazy>\n <div [ptInt]=\"this\" [ptClass]=\"'popover-content'\">\n <ng-container [ngTemplateOutlet]=\"rowsTemplate\" />\n <ng-container [ngTemplateOutlet]=\"footerTemplate\" />\n </div>\n </ng-template>\n </ngn-popover>\n</ng-template>\n\n@if (mode() === 'inline') {\n <div [ptInt]=\"this\" [ptClass]=\"'inline'\">\n <ng-container [ngTemplateOutlet]=\"rowsTemplate\" />\n <ng-container [ngTemplateOutlet]=\"footerTemplate\" />\n </div>\n} @else if (mode() === 'input') {\n <ngn-input-field\n [ptInt]=\"this\"\n [ptDep]=\"'input-field'\"\n (click)=\"!disabled() && !readonly() && show()\"\n (keydown)=\"onKeyDown($event)\"\n #field\n [label]=\"label()\"\n [inputId]=\"inputId()\"\n [invalid]=\"invalid()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n >\n <div role=\"button\" [ptInt]=\"this\" [ptClass]=\"'summary'\">{{ summaryText() }}</div>\n <ngn-icon [ptInt]=\"this\" [ptClass]=\"'icon'\" defaultIcon=\"search\" />\n </ngn-input-field>\n\n <ng-container\n [ngTemplateOutlet]=\"popoverTemplate\"\n [ngTemplateOutletContext]=\"{ anchor: field.element.nativeElement }\"\n />\n} @else {\n @if (anchor()) {\n <ng-container\n [ngTemplateOutlet]=\"popoverTemplate\"\n [ngTemplateOutletContext]=\"{ anchor: anchor() }\"\n />\n }\n}\n", dependencies: [{ kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass", "ptDep"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NgnCalendar, selector: "ngn-calendar", inputs: ["firstDayOfWeek", "showTime", "showSeconds", "inline", "format"] }, { kind: "component", type: NgnInputField, selector: "ngn-input-field", inputs: ["label", "labelledBy", "labelKind", "inputId", "showClearButton", "iconClearButton", "tabindex", "invalid", "readonly", "disabled"] }, { kind: "directive", type: NgnInput, selector: "input[ngnInput], textarea[ngnInput]", inputs: ["invalid", "value"], outputs: ["valueChange"], exportAs: ["ngnInput"] }, { kind: "component", type: NgnSelect, selector: "ngn-select", inputs: ["popoverOptions", "options", "filter", "filterText", "iconFilter", "iconDropdown", "virtual", "itemHeight", "editable", "editableAutoFilter", "multiple", "scrollToSelectedItemOnOpen", "tabindex", "checkbox"] }, { kind: "component", type: NgnPopover, selector: "ngn-popover", inputs: ["open", "anchor", "options", "hasShrinkableContent", "closeBy"], outputs: ["closed", "closing", "openChange"] }, { kind: "component", type: NgnIcon, selector: "ngn-icon", inputs: ["defaultIcon", "icon"] }, { kind: "directive", type: NgnButton, selector: "button[ngnButton], a[ngnButton]", inputs: ["ngnButtonInline"], exportAs: ["ngnButton"] }] });
685
+ }
686
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnFilter, decorators: [{
687
+ type: Component,
688
+ args: [{ selector: 'ngn-filter', imports: [
689
+ NgnPt,
690
+ NgTemplateOutlet,
691
+ NgnCalendar,
692
+ NgnInputField,
693
+ NgnInput,
694
+ NgnSelect,
695
+ NgnPopover,
696
+ NgnIcon,
697
+ NgnButton,
698
+ ], providers: [provideSelf(NgnFilter)], host: {
699
+ style: 'display: block;',
700
+ }, template: "<ng-template #rowsTemplate>\n <div [ptInt]=\"this\" [ptClass]=\"'rows'\">\n @if (dataType() === 'list') {\n <div [ptInt]=\"this\" [ptClass]=\"'row'\" data-testid=\"filter-row-list\">\n <ngn-input-field\n [ptInt]=\"this\"\n [ptDep]=\"'value'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n >\n <ngn-select\n [ptInt]=\"this\"\n [ptDep]=\"'list-select'\"\n [multiple]=\"true\"\n [options]=\"resolvedListOptions()\"\n [value]=\"$any(templateListSelection())\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (valueChange)=\"setListSelection($event)\"\n />\n </ngn-input-field>\n </div>\n } @else {\n @for (c of templateConfig().conditions; track $index; let i = $index; let last = $last) {\n <div [ptInt]=\"this\" [ptClass]=\"'row'\" [attr.data-testid]=\"'filter-row-' + i\">\n <ngn-input-field\n [ptInt]=\"this\"\n [ptDep]=\"operatorRequiresValue(c.operator) ? 'operator' : 'value'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n >\n <ngn-select\n [ptInt]=\"this\"\n [ptDep]=\"'operator-select'\"\n [options]=\"operatorOptions()\"\n [label]=\"i18n['filter_operator']()\"\n [value]=\"c.operator\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (valueChange)=\"setOperator(i, $event)\"\n />\n </ngn-input-field>\n @if (operatorRequiresValue(c.operator)) {\n @if (isDateType()) {\n <ngn-input-field\n [ptInt]=\"this\"\n [ptDep]=\"'value'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n >\n <ngn-calendar\n [ptInt]=\"this\"\n [ptDep]=\"'calendar'\"\n [showTime]=\"isShowTime()\"\n [value]=\"parseRawDate(c.rawValue)\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (valueChange)=\"setDateValue(i, $event)\"\n />\n </ngn-input-field>\n } @else {\n <ngn-input-field\n [ptInt]=\"this\"\n [ptDep]=\"'value'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n >\n <input\n ngnInput\n [ptInt]=\"this\"\n [ptDep]=\"'input'\"\n [attr.aria-label]=\"i18n['filter_value']()\"\n [type]=\"inputType()\"\n [value]=\"c.rawValue || ''\"\n [disabled]=\"disabled()\"\n [readOnly]=\"readonly()\"\n (valueChange)=\"setRawValue(i, $event)\"\n />\n </ngn-input-field>\n }\n }\n\n @if (allowMultiple() && templateConfig().conditions.length > 1) {\n <button\n type=\"button\"\n ngnButton\n kind=\"icon\"\n [ptInt]=\"this\"\n [ptDep]=\"'remove-btn'\"\n [disabled]=\"disabled() || readonly()\"\n (click)=\"removeCondition(i)\"\n [attr.aria-label]=\"i18n['filter_removeCondition']() + ' ' + (i + 1)\"\n data-testid=\"filter-remove-condition\"\n >\n <ngn-icon defaultIcon=\"filter-remove\" />\n </button>\n }\n </div>\n\n @if (allowMultiple() && !last && templateConfig().conditions.length > 1) {\n <div\n [ptInt]=\"this\"\n [ptClass]=\"'condition-divider'\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"!disabled() && !readonly() && toggleMatchMode()\"\n (keydown.enter)=\"!disabled() && !readonly() && toggleMatchMode()\"\n (keydown.space)=\"\n !disabled() && !readonly() && toggleMatchMode(); $event.preventDefault()\n \"\n [attr.aria-disabled]=\"disabled() || readonly()\"\n data-testid=\"filter-match-mode\"\n >\n {{ matchModeLabel() }}\n </div>\n }\n }\n }\n </div>\n</ng-template>\n\n<ng-template #footerTemplate>\n @if (!disabled() && !readonly()) {\n <div [ptInt]=\"this\" [ptClass]=\"'footer'\">\n @if (dataType() !== 'list' && allowMultiple()) {\n <button\n type=\"button\"\n ngnButton\n kind=\"secondary\"\n [ptInt]=\"this\"\n [ptDep]=\"'add-button'\"\n (click)=\"addCondition()\"\n data-testid=\"filter-add-rule\"\n >\n <span aria-hidden=\"true\">+</span>\n {{ i18n['filter_addCondition']() }}\n </button>\n }\n <div [ptInt]=\"this\" [ptClass]=\"'footer-actions'\">\n @if (!autoApply()) {\n <button\n type=\"button\"\n ngnButton\n kind=\"secondary\"\n [ptInt]=\"this\"\n [ptDep]=\"'cancel-button'\"\n (click)=\"cancelChanges()\"\n data-testid=\"filter-cancel\"\n >\n {{ i18n['filter_cancel']() }}\n </button>\n <button\n type=\"button\"\n ngnButton\n kind=\"primary\"\n [ptInt]=\"this\"\n [ptDep]=\"'apply-button'\"\n (click)=\"apply()\"\n data-testid=\"filter-apply\"\n >\n {{ i18n['filter_apply']() }}\n </button>\n } @else {\n <button\n type=\"button\"\n ngnButton\n kind=\"secondary\"\n [ptInt]=\"this\"\n [ptDep]=\"'clear-button'\"\n (click)=\"clear()\"\n data-testid=\"filter-clear\"\n >\n {{ i18n['filter_clear']() }}\n </button>\n }\n </div>\n </div>\n }\n</ng-template>\n\n<ng-template #popoverTemplate let-anchor=\"anchor\">\n <ngn-popover\n #popover\n [ptInt]=\"this\"\n [ptDep]=\"'popover'\"\n [options]=\"appliedPopoverOptions()\"\n [anchor]=\"anchor\"\n [hasShrinkableContent]=\"true\"\n (keydown)=\"onKeyDown($event)\"\n [id]=\"inputId() + '_popover'\"\n >\n <ng-template #lazy>\n <div [ptInt]=\"this\" [ptClass]=\"'popover-content'\">\n <ng-container [ngTemplateOutlet]=\"rowsTemplate\" />\n <ng-container [ngTemplateOutlet]=\"footerTemplate\" />\n </div>\n </ng-template>\n </ngn-popover>\n</ng-template>\n\n@if (mode() === 'inline') {\n <div [ptInt]=\"this\" [ptClass]=\"'inline'\">\n <ng-container [ngTemplateOutlet]=\"rowsTemplate\" />\n <ng-container [ngTemplateOutlet]=\"footerTemplate\" />\n </div>\n} @else if (mode() === 'input') {\n <ngn-input-field\n [ptInt]=\"this\"\n [ptDep]=\"'input-field'\"\n (click)=\"!disabled() && !readonly() && show()\"\n (keydown)=\"onKeyDown($event)\"\n #field\n [label]=\"label()\"\n [inputId]=\"inputId()\"\n [invalid]=\"invalid()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n >\n <div role=\"button\" [ptInt]=\"this\" [ptClass]=\"'summary'\">{{ summaryText() }}</div>\n <ngn-icon [ptInt]=\"this\" [ptClass]=\"'icon'\" defaultIcon=\"search\" />\n </ngn-input-field>\n\n <ng-container\n [ngTemplateOutlet]=\"popoverTemplate\"\n [ngTemplateOutletContext]=\"{ anchor: field.element.nativeElement }\"\n />\n} @else {\n @if (anchor()) {\n <ng-container\n [ngTemplateOutlet]=\"popoverTemplate\"\n [ngTemplateOutletContext]=\"{ anchor: anchor() }\"\n />\n }\n}\n" }]
701
+ }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], dataType: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataType", required: false }] }], allowMultiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowMultiple", required: false }] }], listOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "listOptions", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], anchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchor", required: false }] }], filterLocally: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterLocally", required: false }] }], autoApply: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoApply", required: false }] }], autoApplyDebounce: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoApplyDebounce", required: false }] }], popoverOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "popoverOptions", required: false }] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], filterResultChange: [{ type: i0.Output, args: ["filterResultChange"] }], _popover: [{ type: i0.ViewChild, args: [i0.forwardRef(() => NgnPopover), { isSignal: true }] }] } });
702
+
703
+ /**
704
+ * Generated bundle index. Do not edit.
705
+ */
706
+
707
+ export { NgnFilter };
708
+ //# sourceMappingURL=ngneers-controls-filter.mjs.map