spiderly 19.13.0 → 19.15.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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Input, Component, EventEmitter, Output, Injectable, ViewChild, NgModule, PLATFORM_ID, Inject, HostBinding, Directive, inject, ElementRef, NgZone, input, output, LOCALE_ID, signal, computed, Injector, viewChild, effect, TemplateRef, ContentChildren, ContentChild } from '@angular/core';
2
+ import { Input, Component, EventEmitter, Output, Injectable, ViewChild, NgModule, PLATFORM_ID, Inject, HostBinding, Directive, inject, ElementRef, NgZone, signal, computed, input, output, LOCALE_ID, Injector, viewChild, untracked, effect, TemplateRef, ContentChildren, ContentChild } from '@angular/core';
3
3
  import * as i1 from '@jsverse/transloco';
4
4
  import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
5
5
  import * as i2 from '@angular/common';
@@ -4024,6 +4024,461 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
4024
4024
  }]
4025
4025
  }], ctorParameters: () => [] });
4026
4026
 
4027
+ var MatchModeCodes;
4028
+ (function (MatchModeCodes) {
4029
+ MatchModeCodes["StartsWith"] = "startsWith";
4030
+ MatchModeCodes["Contains"] = "contains";
4031
+ MatchModeCodes["Equals"] = "equals";
4032
+ MatchModeCodes["LessThan"] = "lessThan";
4033
+ MatchModeCodes["GreaterThan"] = "greaterThan";
4034
+ MatchModeCodes["In"] = "in";
4035
+ })(MatchModeCodes || (MatchModeCodes = {}));
4036
+
4037
+ /**
4038
+ * What each value type may be asked — the ONE operator table, mirroring what the generated
4039
+ * paginator implements per type (`PaginatedResultGenerator`): `In` on a string column answers
4040
+ * with InvalidMatchMode, PrimeNG's own text list adds three modes the backend 400s, and so on.
4041
+ *
4042
+ * Deliberately a LEAF module (imports nothing but the codes) so every consumer can derive from
4043
+ * it without a cycle: the store's runtime validation and offered lists (`filter-store.ts`), the
4044
+ * compile-time `FilterRule.matchMode` union (`entities/filter-rule.ts`), and the data view's
4045
+ * kind-level lists (via `operatorOptionsForKind`). These used to be hand-kept copies; edit the
4046
+ * table here and every surface follows.
4047
+ *
4048
+ * Tuple order is DISPLAY order for every operator picker (Equals, then "less/before", then
4049
+ * "more/after" — the order the header dropdowns always shipped).
4050
+ */
4051
+ const ALLOWED_OPERATORS = {
4052
+ text: [
4053
+ MatchModeCodes.StartsWith,
4054
+ MatchModeCodes.Contains,
4055
+ MatchModeCodes.Equals,
4056
+ ],
4057
+ number: [
4058
+ MatchModeCodes.Equals,
4059
+ MatchModeCodes.LessThan,
4060
+ MatchModeCodes.GreaterThan,
4061
+ MatchModeCodes.In,
4062
+ ],
4063
+ boolean: [MatchModeCodes.Equals],
4064
+ date: [
4065
+ MatchModeCodes.Equals,
4066
+ MatchModeCodes.LessThan,
4067
+ MatchModeCodes.GreaterThan,
4068
+ ],
4069
+ };
4070
+
4071
+ const OPERATOR_WORDS = {
4072
+ text: {
4073
+ [MatchModeCodes.StartsWith]: {
4074
+ pickerKey: 'StartsWith',
4075
+ phraseKey: 'FilterChipStartsWith',
4076
+ },
4077
+ [MatchModeCodes.Contains]: {
4078
+ pickerKey: 'Contains',
4079
+ phraseKey: 'FilterChipContains',
4080
+ },
4081
+ [MatchModeCodes.Equals]: {
4082
+ pickerKey: 'Equals',
4083
+ phraseKey: 'FilterChipEquals',
4084
+ },
4085
+ // No `In` for text: ALLOWED_OPERATORS.text does not carry it, so the entry was unreachable —
4086
+ // and it made `textFilter({ options })` look supported when the store throws on it.
4087
+ },
4088
+ number: {
4089
+ [MatchModeCodes.Equals]: {
4090
+ pickerKey: 'Equals',
4091
+ phraseKey: 'FilterChipEquals',
4092
+ },
4093
+ [MatchModeCodes.GreaterThan]: {
4094
+ pickerKey: 'MoreThan',
4095
+ phraseKey: 'FilterChipGreaterThan',
4096
+ },
4097
+ [MatchModeCodes.LessThan]: {
4098
+ pickerKey: 'LessThan',
4099
+ phraseKey: 'FilterChipLessThan',
4100
+ },
4101
+ // The picker never renders for a pick-list (one operator), but the CHIP always does.
4102
+ [MatchModeCodes.In]: { pickerKey: 'In', phraseKey: 'FilterChipIn' },
4103
+ },
4104
+ boolean: {
4105
+ [MatchModeCodes.Equals]: {
4106
+ pickerKey: 'Equals',
4107
+ phraseKey: 'FilterChipEquals',
4108
+ },
4109
+ },
4110
+ date: {
4111
+ [MatchModeCodes.Equals]: {
4112
+ pickerKey: 'OnDate',
4113
+ phraseKey: 'FilterChipEquals',
4114
+ },
4115
+ [MatchModeCodes.LessThan]: {
4116
+ pickerKey: 'DatesBefore',
4117
+ phraseKey: 'FilterChipBefore',
4118
+ },
4119
+ [MatchModeCodes.GreaterThan]: {
4120
+ pickerKey: 'DatesAfter',
4121
+ phraseKey: 'FilterChipAfter',
4122
+ },
4123
+ },
4124
+ };
4125
+ /**
4126
+ * `In` and options are the same fact seen from two sides: `In` needs a list of values, and the
4127
+ * editor only draws one when the filter declares options. So options mean `In` and nothing else,
4128
+ * and their absence rules `In` out — offering it on a plain number filter would hand the
4129
+ * operator a mode with no control behind it.
4130
+ *
4131
+ * Widened first: the per-kind tuples are literal, so `boolean`'s `[Equals]` makes the `In`
4132
+ * comparison a "no overlap" error rather than an empty result.
4133
+ */
4134
+ function baseOperators(kind, hasOptions) {
4135
+ const accepted = ALLOWED_OPERATORS[kind];
4136
+ return accepted.filter((value) => hasOptions ? value === MatchModeCodes.In : value !== MatchModeCodes.In);
4137
+ }
4138
+ function toOperatorOptions(kind, operators) {
4139
+ return operators.map((value) => ({
4140
+ value,
4141
+ labelKey: OPERATOR_WORDS[kind][value]?.pickerKey ?? value,
4142
+ }));
4143
+ }
4144
+ /**
4145
+ * A KIND's operator picker options, for callers with no filter behind them — today that is
4146
+ * `spiderly-data-view`, whose own filter config derives its operator lists here instead of
4147
+ * hand-keeping copies (the drift `allowed-operators.ts` exists to end). Per-FILTER narrowing
4148
+ * lives in the store (`createFilterStore` resolves it once per declaration), so this cannot be
4149
+ * handed a fake definition to satisfy — that shape shipped once and interpolated an empty
4150
+ * filter name into its own error message.
4151
+ */
4152
+ function operatorOptionsForKind(kind, hasOptions = false) {
4153
+ return toOperatorOptions(kind, baseOperators(kind, hasOptions));
4154
+ }
4155
+ /**
4156
+ * The operator applied when nobody picked one, which has to be what a person MEANS by filling in
4157
+ * the control. `Contains` for a fragment typed into a box; `GreaterThan` for a date, never
4158
+ * `Equals` — a date equality against a TIMESTAMP matches only the row written in that exact
4159
+ * second, and answers with an empty grid rather than an error (PACMS ran into this on all three
4160
+ * of its date columns, Filip 2026-08-28).
4161
+ */
4162
+ const DEFAULT_OPERATOR = {
4163
+ text: MatchModeCodes.Contains,
4164
+ number: MatchModeCodes.Equals,
4165
+ boolean: MatchModeCodes.Equals,
4166
+ date: MatchModeCodes.GreaterThan,
4167
+ };
4168
+ function textFilter(config) {
4169
+ return {
4170
+ kind: 'text',
4171
+ label: config.label,
4172
+ operators: config.operators,
4173
+ offered: config.offered,
4174
+ };
4175
+ }
4176
+ /**
4177
+ * Only a NUMBER filter accepts options, and the asymmetry is forced rather than chosen: options
4178
+ * mean `In`, and `In` is the one operator ALLOWED_OPERATORS grants to numbers alone — because the
4179
+ * generated paginator answers it with InvalidMatchMode on a string column. A text pick-list used
4180
+ * to type-check and then throw at the first `set`.
4181
+ */
4182
+ function numberFilter(config) {
4183
+ return {
4184
+ kind: 'number',
4185
+ label: config.label,
4186
+ options: config.options,
4187
+ operators: config.operators,
4188
+ offered: config.offered,
4189
+ };
4190
+ }
4191
+ function booleanFilter(config) {
4192
+ return {
4193
+ kind: 'boolean',
4194
+ label: config.label,
4195
+ operators: config.operators,
4196
+ offered: config.offered,
4197
+ };
4198
+ }
4199
+ function dateFilter(config) {
4200
+ return {
4201
+ kind: 'date',
4202
+ label: config.label,
4203
+ operators: config.operators,
4204
+ offered: config.offered,
4205
+ };
4206
+ }
4207
+ /** The shape `Date.toJSON` writes, which is the only value here JSON cannot round-trip itself. */
4208
+ const ISO_DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
4209
+ function reviveValue(value) {
4210
+ if (typeof value === 'string' && ISO_DATE.test(value))
4211
+ return new Date(value);
4212
+ if (Array.isArray(value))
4213
+ return value.map(reviveValue);
4214
+ return value;
4215
+ }
4216
+ /**
4217
+ * A blanked value is NOT a constraint. `contains ''` matches every row, so emitting one would
4218
+ * narrow nothing while the bar claims the grid is filtered.
4219
+ */
4220
+ function isBlank(value) {
4221
+ if (value === null || value === undefined)
4222
+ return true;
4223
+ if (typeof value === 'string')
4224
+ return value.trim() === '';
4225
+ if (Array.isArray(value))
4226
+ return value.length === 0;
4227
+ // A half-typed datepicker yields an Invalid Date, which JSON.stringify writes as `null`.
4228
+ if (value instanceof Date)
4229
+ return Number.isNaN(value.getTime());
4230
+ return false;
4231
+ }
4232
+ /**
4233
+ * Whether two constraint values are the same QUESTION. Identity is not it: every `set` builds a
4234
+ * fresh draft, a multiselect hands back a fresh array per change, and two Dates at one instant
4235
+ * are one constraint. This is what `commit` bails on, so a paste-over-itself or a double Apply
4236
+ * cannot spend a request — the rule PACMS's order list used to hand-roll as `lastCommittedTerm`.
4237
+ */
4238
+ function valueEquals(a, b) {
4239
+ if (a === b)
4240
+ return true;
4241
+ if (a instanceof Date && b instanceof Date)
4242
+ return a.getTime() === b.getTime();
4243
+ if (Array.isArray(a) && Array.isArray(b)) {
4244
+ return a.length === b.length && a.every((item, i) => valueEquals(item, b[i]));
4245
+ }
4246
+ return false;
4247
+ }
4248
+ function constraintEquals(a, b) {
4249
+ return a != null && a.operator === b.operator && valueEquals(a.value, b.value);
4250
+ }
4251
+ /**
4252
+ * The filter engine. It knows nothing about columns: a filter has an id, a value type and a
4253
+ * constraint, and that is the entire vocabulary.
4254
+ */
4255
+ function createFilterStore(definitions) {
4256
+ // Per-filter offered operators, resolved ONCE per declaration — so an invalid narrowing is
4257
+ // said once, not on every editor open — and re-resolved only where the inputs can actually
4258
+ // change: `setOptions`, because options' PRESENCE flips a filter between pick-list (`In`) and
4259
+ // plain operators. A per-store Map rather than the module-level WeakMap this first shipped
4260
+ // as: that memo was keyed on a definition object consumers mutate, so late-filled options
4261
+ // could freeze a filter's operators at the wrong answer.
4262
+ const resolveOffered = (id) => {
4263
+ const definition = definitions[id];
4264
+ const base = baseOperators(definition.kind, definition.options != null);
4265
+ let codes = base;
4266
+ if (definition.operators?.length) {
4267
+ const survivors = definition.operators.filter((operator) => base.includes(operator));
4268
+ // Same polarity as the column path's computeMatchModes: a bad entry is dropped and said
4269
+ // out loud, and a narrowing that leaves nothing falls back to the full list — an editor
4270
+ // with no operators would be unusable, which is worse than an unwanted one. Mostly
4271
+ // unreachable now that FilterConfig types `operators` per kind; kept for dynamically
4272
+ // built arrays.
4273
+ if (survivors.length < definition.operators.length) {
4274
+ console.error(`Filter '${definition.label}' narrows to operators its ${definition.kind} kind does not ` +
4275
+ `offer (${definition.operators.join(', ')}; offered: ${base.join(', ')}). ` +
4276
+ `Unsupported entries are ignored${survivors.length ? '' : '; falling back to the full list'}.`);
4277
+ }
4278
+ codes = survivors.length ? survivors : base;
4279
+ }
4280
+ return { codes, options: toOperatorOptions(definition.kind, codes) };
4281
+ };
4282
+ const offered = new Map();
4283
+ for (const id of Object.keys(definitions)) {
4284
+ offered.set(id, resolveOffered(id));
4285
+ }
4286
+ /**
4287
+ * The operator applied when nobody picked one. Options mean `In`, whatever the kind's default;
4288
+ * a narrowed filter defaults to its FIRST declared operator.
4289
+ */
4290
+ const defaultOperatorFor = (id) => {
4291
+ const definition = definitions[id];
4292
+ if (definition.options != null)
4293
+ return MatchModeCodes.In;
4294
+ return definition.operators?.length
4295
+ ? offered.get(id).codes[0]
4296
+ : DEFAULT_OPERATOR[definition.kind];
4297
+ };
4298
+ // Two stores, and the split is the whole point: `set` writes a DRAFT, `commit` publishes it.
4299
+ // A chip drawn off a draft would repeat the mistake the header's filter icon already shipped —
4300
+ // claiming the grid is narrowed on the first keystroke. Controls that apply on change
4301
+ // (multiselect, boolean, date) call both at once; a text box commits on Enter or blur.
4302
+ const drafts = signal(new Map());
4303
+ const writeDraft = (id, constraint) => {
4304
+ const next = new Map(drafts());
4305
+ if (constraint === null)
4306
+ next.delete(id);
4307
+ else
4308
+ next.set(id, constraint);
4309
+ drafts.set(next);
4310
+ };
4311
+ const committed = signal(new Map());
4312
+ const applied = computed(() => [...committed()].map(([id, constraint]) => ({
4313
+ id: id,
4314
+ label: definitions[id].label,
4315
+ kind: definitions[id].kind,
4316
+ operator: constraint.operator,
4317
+ operatorPhraseKey: OPERATOR_WORDS[definitions[id].kind][constraint.operator]?.phraseKey ??
4318
+ constraint.operator,
4319
+ value: constraint.value,
4320
+ })));
4321
+ const store = {
4322
+ definitions,
4323
+ applied,
4324
+ /**
4325
+ * A handle bound to one filter. This is the PLACEMENT api: it needs nothing from the component
4326
+ * tree, so the same filter can be driven from the bar, from a header cell, or from a drawer
4327
+ * rendered at app root. `value` is the DRAFT — a control shows what was typed, not what was
4328
+ * applied; `applied()` is the other question and the bar asks that one.
4329
+ */
4330
+ get(id) {
4331
+ return {
4332
+ id: id,
4333
+ label: definitions[id].label,
4334
+ kind: definitions[id].kind,
4335
+ value: computed(() => drafts().get(id)?.value),
4336
+ operator: computed(() => drafts().get(id)?.operator),
4337
+ // Getters, not snapshots: options can land AFTER a handle is taken — the bar's editor
4338
+ // holds one for the life of the popover, and PACMS fills its lookups asynchronously —
4339
+ // and a snapshot left an already-open pick-list empty until it was reopened. The
4340
+ // operator list rides along, since setOptions can flip it to `In`.
4341
+ get operators() {
4342
+ return offered.get(id).options;
4343
+ },
4344
+ get defaultOperator() {
4345
+ return defaultOperatorFor(id);
4346
+ },
4347
+ get options() {
4348
+ return definitions[id].options;
4349
+ },
4350
+ set: (constraint) => store.set(id, constraint),
4351
+ commit: () => store.commit(id),
4352
+ reset: () => store.reset(id),
4353
+ };
4354
+ },
4355
+ /**
4356
+ * The ONE seam for choices that arrive after the store is built (PACMS fills its pick-lists
4357
+ * from lookups that race a deploy). Never write `definitions[id].options` by hand: options'
4358
+ * PRESENCE is what makes a filter a pick-list, so the setter also re-resolves the offered
4359
+ * operators — a hand write would leave a late-filled filter offering `Equals` on a control
4360
+ * that emits a list.
4361
+ */
4362
+ setOptions(id, options) {
4363
+ definitions[id].options = options;
4364
+ offered.set(id, resolveOffered(id));
4365
+ },
4366
+ set(id, constraint) {
4367
+ const kind = definitions[id].kind;
4368
+ // Throws rather than logging and ignoring, which is what `computeMatchModes` does for a
4369
+ // column's OFFERED match modes. Different stakes: that one narrows a dropdown, so a bad
4370
+ // entry merely fails to show, while this one assembles a QUERY — dropping it silently would
4371
+ // leave the operator looking at unfiltered rows believing they are filtered.
4372
+ if (!ALLOWED_OPERATORS[kind].includes(constraint.operator)) {
4373
+ throw new Error(`Filter '${String(id)}' is ${kind}, which does not accept the '${constraint.operator}' ` +
4374
+ `operator (allowed: ${ALLOWED_OPERATORS[kind].join(', ')}). The generated paginator ` +
4375
+ `answers an unsupported match mode with InvalidMatchMode.`);
4376
+ }
4377
+ writeDraft(id, constraint);
4378
+ },
4379
+ /**
4380
+ * `set` + `commit` in one breath — the shape every PROGRAMMATIC write takes (a view's
4381
+ * `apply`, the sheet flow). The split API stays for controls, where draft-on-type /
4382
+ * publish-on-Apply is the point; but a bare `set` whose `commit` is forgotten (or written
4383
+ * with the wrong id — the pair repeats the string twice) fails silently: the draft is
4384
+ * written, nothing is published, and a view narrows by nothing under a labelled tab.
4385
+ */
4386
+ setAndCommit(id, constraint) {
4387
+ store.set(id, constraint);
4388
+ store.commit(id);
4389
+ },
4390
+ /**
4391
+ * Publishes the draft. A blanked draft REMOVES the constraint rather than committing an empty
4392
+ * one, so `applied()` and the payload agree: no chip, no key, no `contains ''`.
4393
+ */
4394
+ commit(id) {
4395
+ const draft = drafts().get(id);
4396
+ const current = committed();
4397
+ const shouldRemove = draft === undefined || isBlank(draft.value);
4398
+ // A new Map is a new identity even with identical contents, and everything downstream
4399
+ // reacts to identity — so a no-op commit would spend a request. The bail compares the
4400
+ // CONSTRAINT, not the draft object: every set() builds a fresh draft, and the case that
4401
+ // matters is an identical value re-committed (see valueEquals).
4402
+ if (shouldRemove ? !current.has(id) : constraintEquals(current.get(id), draft))
4403
+ return;
4404
+ const next = new Map(current);
4405
+ if (shouldRemove)
4406
+ next.delete(id);
4407
+ else
4408
+ next.set(id, draft);
4409
+ committed.set(next);
4410
+ },
4411
+ /**
4412
+ * The chip's `x`. Drops the DRAFT as well as the committed constraint: a surviving draft
4413
+ * leaves the control still showing the cleared text, and the next commit would restore a
4414
+ * filter nobody re-typed.
4415
+ */
4416
+ reset(id) {
4417
+ if (drafts().has(id))
4418
+ writeDraft(id, null);
4419
+ if (!committed().has(id))
4420
+ return;
4421
+ const next = new Map(committed());
4422
+ next.delete(id);
4423
+ committed.set(next);
4424
+ },
4425
+ /**
4426
+ * The applied set, plain enough to store. Drafts are deliberately NOT included: a half-typed
4427
+ * value is not a state anyone should come back to, and the bar's whole claim is that what it
4428
+ * shows is what narrows the grid.
4429
+ */
4430
+ snapshot() {
4431
+ const snapshot = {};
4432
+ for (const [id, constraint] of committed()) {
4433
+ snapshot[id] = {
4434
+ operator: constraint.operator,
4435
+ value: constraint.value,
4436
+ };
4437
+ }
4438
+ return snapshot;
4439
+ },
4440
+ /**
4441
+ * Replaces the applied set. The drafts are written too, so reopening a restored chip shows
4442
+ * the value it is filtering by rather than an empty control.
4443
+ *
4444
+ * Ids the store no longer declares are dropped rather than carried: a filter removed from the
4445
+ * declaration in a later release would otherwise ride every request from anyone whose storage
4446
+ * still held it, with no chip able to name it and no way to clear it from the UI.
4447
+ */
4448
+ restore(snapshot) {
4449
+ const next = new Map();
4450
+ for (const [id, constraint] of Object.entries(snapshot ?? {})) {
4451
+ if (!(id in definitions))
4452
+ continue;
4453
+ next.set(id, {
4454
+ operator: constraint.operator,
4455
+ value: reviveValue(constraint.value),
4456
+ });
4457
+ }
4458
+ drafts.set(new Map(next));
4459
+ committed.set(next);
4460
+ },
4461
+ /** The bar's "Clear all". `reset` for every filter, drafts included. */
4462
+ clear() {
4463
+ if (drafts().size > 0)
4464
+ drafts.set(new Map());
4465
+ if (committed().size > 0)
4466
+ committed.set(new Map());
4467
+ },
4468
+ /** The `filters` half of `Filter` — what the generated paginator reads. */
4469
+ toFilterPayload() {
4470
+ const payload = {};
4471
+ for (const [id, constraint] of committed()) {
4472
+ payload[id] = [
4473
+ { matchMode: constraint.operator, value: constraint.value },
4474
+ ];
4475
+ }
4476
+ return payload;
4477
+ },
4478
+ };
4479
+ return store;
4480
+ }
4481
+
4027
4482
  /**
4028
4483
  * Renders the editor for ONE filter of a `spiderly-data-table`'s bar, replacing the control the
4029
4484
  * bar would have drawn for that filter's kind. Bind it to the filter's id; filters with no
@@ -5501,7 +5956,10 @@ class SpiderlyDataTableComponent {
5501
5956
  * reading exactly the keys they already wrote.
5502
5957
  */
5503
5958
  get viewScope() {
5504
- return this.activeViewId ? `:${this.activeViewId}` : '';
5959
+ return this.viewScopeOf(this.activeViewId);
5960
+ }
5961
+ viewScopeOf(viewId) {
5962
+ return viewId ? `:${viewId}` : '';
5505
5963
  }
5506
5964
  /**
5507
5965
  * Where the applied filters live. Follows `stateStorage` rather than the layout's always-local
@@ -5511,8 +5969,11 @@ class SpiderlyDataTableComponent {
5511
5969
  * durable preference like a column's width.
5512
5970
  */
5513
5971
  get filtersStateKey() {
5972
+ return this.filtersStateKeyOf(this.activeViewId);
5973
+ }
5974
+ filtersStateKeyOf(viewId) {
5514
5975
  return this.resolvedStateKey
5515
- ? `${this.resolvedStateKey}${this.viewScope}:filters`
5976
+ ? `${this.resolvedStateKey}${this.viewScopeOf(viewId)}:filters`
5516
5977
  : null;
5517
5978
  }
5518
5979
  get filterStorage() {
@@ -5605,17 +6066,18 @@ class SpiderlyDataTableComponent {
5605
6066
  * the empty state the operator left — they cleared it, and F5 must give the cleared state
5606
6067
  * back. On a table with no views, `view` is null and this is a plain restore of the
5607
6068
  * `persistAppliedFilters` snapshot.
6069
+ *
6070
+ * `viewFilterPayload` passes a scratch store and another view's key, to read what entering
6071
+ * that view would send.
5608
6072
  */
5609
- enterViewFilters(view, semantics) {
5610
- if (!this.filters)
6073
+ enterViewFilters(view, semantics, store = this.filters, key = this.filtersStateKey) {
6074
+ if (!store)
5611
6075
  return;
5612
- const stored = !view?.transient && this.filtersStateKey
5613
- ? readStoredJson(this.filterStorage, this.filtersStateKey)
5614
- : null;
6076
+ const stored = !view?.transient && key ? readStoredJson(this.filterStorage, key) : null;
5615
6077
  if (stored)
5616
- this.filters.restore(stored);
6078
+ store.restore(stored);
5617
6079
  else if (view?.transient || semantics === 'click')
5618
- view?.apply?.(this.filters);
6080
+ view?.apply?.(store);
5619
6081
  }
5620
6082
  get layoutStateKey() {
5621
6083
  return this.resolvedStateKey
@@ -6035,6 +6497,39 @@ class SpiderlyDataTableComponent {
6035
6497
  this.refetchFromFirstPage();
6036
6498
  }
6037
6499
  }
6500
+ /**
6501
+ * The `filters` payload a view's own list request carries — the same shape the store's
6502
+ * `toFilterPayload()` emits — so a consumer's count query can narrow each view by the bar
6503
+ * filters the operator saved on it, and a `TableView.count` stays the number of rows that tab
6504
+ * lists: on screen for the active view, after a click for the others (spiderly#439).
6505
+ *
6506
+ * - The ACTIVE view answers with the live filters. Not with what a click would enter: clicking
6507
+ * the active tab re-asks its question when the operator has cleared the bar, but the list on
6508
+ * screen, and so its count, has not.
6509
+ * - Any other view answers with what a click would enter (`enterViewFilters`): the filters
6510
+ * saved on it, else its `apply` — always `apply` for a transient view. That runs on a scratch
6511
+ * `createFilterStore` of the same declarations, so nothing switches, persists or re-queries.
6512
+ * Untracked: the scratch store's signal writes would otherwise make a read inside a
6513
+ * `computed` throw NG0600.
6514
+ *
6515
+ * `{}` on a table without `[filters]`, whose requests carry none; `null` for an id that is not
6516
+ * in `[views]`.
6517
+ */
6518
+ viewFilterPayload(viewId) {
6519
+ const view = this.viewById(viewId);
6520
+ if (!view)
6521
+ return null;
6522
+ if (!this.filters)
6523
+ return {};
6524
+ if (view.id === this.activeViewId)
6525
+ return this.filters.toFilterPayload();
6526
+ const definitions = this.filters.definitions;
6527
+ return untracked(() => {
6528
+ const scratch = createFilterStore(definitions);
6529
+ this.enterViewFilters(view, 'click', scratch, this.filtersStateKeyOf(view.id));
6530
+ return scratch.toFilterPayload();
6531
+ });
6532
+ }
6038
6533
  /**
6039
6534
  * The filter this column stands for. Defaults to the column's own `field`, because a store id
6040
6535
  * IS a backend property name — `toFilterPayload` emits it straight into `Filter.filters`. So
@@ -6945,478 +7440,23 @@ class Column {
6945
7440
  this.decimalPlaces = decimalPlaces;
6946
7441
  this.sortable = sortable;
6947
7442
  this.visible = visible;
6948
- this.lockVisible = lockVisible;
6949
- this.width = width;
6950
- this.onCellClick = onCellClick;
6951
- }
6952
- }
6953
- class RowClickEvent {
6954
- constructor({ index, id, additionalIndexes, } = {}) {
6955
- this.index = index;
6956
- this.id = id;
6957
- this.additionalIndexes = additionalIndexes;
6958
- }
6959
- }
6960
- class AllClickEvent {
6961
- constructor({ checked, additionalIndexes, } = {}) {
6962
- this.checked = checked;
6963
- this.additionalIndexes = additionalIndexes;
6964
- }
6965
- }
6966
-
6967
- var MatchModeCodes;
6968
- (function (MatchModeCodes) {
6969
- MatchModeCodes["StartsWith"] = "startsWith";
6970
- MatchModeCodes["Contains"] = "contains";
6971
- MatchModeCodes["Equals"] = "equals";
6972
- MatchModeCodes["LessThan"] = "lessThan";
6973
- MatchModeCodes["GreaterThan"] = "greaterThan";
6974
- MatchModeCodes["In"] = "in";
6975
- })(MatchModeCodes || (MatchModeCodes = {}));
6976
-
6977
- /**
6978
- * What each value type may be asked — the ONE operator table, mirroring what the generated
6979
- * paginator implements per type (`PaginatedResultGenerator`): `In` on a string column answers
6980
- * with InvalidMatchMode, PrimeNG's own text list adds three modes the backend 400s, and so on.
6981
- *
6982
- * Deliberately a LEAF module (imports nothing but the codes) so every consumer can derive from
6983
- * it without a cycle: the store's runtime validation and offered lists (`filter-store.ts`), the
6984
- * compile-time `FilterRule.matchMode` union (`entities/filter-rule.ts`), and the data view's
6985
- * kind-level lists (via `operatorOptionsForKind`). These used to be hand-kept copies; edit the
6986
- * table here and every surface follows.
6987
- *
6988
- * Tuple order is DISPLAY order for every operator picker (Equals, then "less/before", then
6989
- * "more/after" — the order the header dropdowns always shipped).
6990
- */
6991
- const ALLOWED_OPERATORS = {
6992
- text: [
6993
- MatchModeCodes.StartsWith,
6994
- MatchModeCodes.Contains,
6995
- MatchModeCodes.Equals,
6996
- ],
6997
- number: [
6998
- MatchModeCodes.Equals,
6999
- MatchModeCodes.LessThan,
7000
- MatchModeCodes.GreaterThan,
7001
- MatchModeCodes.In,
7002
- ],
7003
- boolean: [MatchModeCodes.Equals],
7004
- date: [
7005
- MatchModeCodes.Equals,
7006
- MatchModeCodes.LessThan,
7007
- MatchModeCodes.GreaterThan,
7008
- ],
7009
- };
7010
-
7011
- const OPERATOR_WORDS = {
7012
- text: {
7013
- [MatchModeCodes.StartsWith]: {
7014
- pickerKey: 'StartsWith',
7015
- phraseKey: 'FilterChipStartsWith',
7016
- },
7017
- [MatchModeCodes.Contains]: {
7018
- pickerKey: 'Contains',
7019
- phraseKey: 'FilterChipContains',
7020
- },
7021
- [MatchModeCodes.Equals]: {
7022
- pickerKey: 'Equals',
7023
- phraseKey: 'FilterChipEquals',
7024
- },
7025
- // No `In` for text: ALLOWED_OPERATORS.text does not carry it, so the entry was unreachable —
7026
- // and it made `textFilter({ options })` look supported when the store throws on it.
7027
- },
7028
- number: {
7029
- [MatchModeCodes.Equals]: {
7030
- pickerKey: 'Equals',
7031
- phraseKey: 'FilterChipEquals',
7032
- },
7033
- [MatchModeCodes.GreaterThan]: {
7034
- pickerKey: 'MoreThan',
7035
- phraseKey: 'FilterChipGreaterThan',
7036
- },
7037
- [MatchModeCodes.LessThan]: {
7038
- pickerKey: 'LessThan',
7039
- phraseKey: 'FilterChipLessThan',
7040
- },
7041
- // The picker never renders for a pick-list (one operator), but the CHIP always does.
7042
- [MatchModeCodes.In]: { pickerKey: 'In', phraseKey: 'FilterChipIn' },
7043
- },
7044
- boolean: {
7045
- [MatchModeCodes.Equals]: {
7046
- pickerKey: 'Equals',
7047
- phraseKey: 'FilterChipEquals',
7048
- },
7049
- },
7050
- date: {
7051
- [MatchModeCodes.Equals]: {
7052
- pickerKey: 'OnDate',
7053
- phraseKey: 'FilterChipEquals',
7054
- },
7055
- [MatchModeCodes.LessThan]: {
7056
- pickerKey: 'DatesBefore',
7057
- phraseKey: 'FilterChipBefore',
7058
- },
7059
- [MatchModeCodes.GreaterThan]: {
7060
- pickerKey: 'DatesAfter',
7061
- phraseKey: 'FilterChipAfter',
7062
- },
7063
- },
7064
- };
7065
- /**
7066
- * `In` and options are the same fact seen from two sides: `In` needs a list of values, and the
7067
- * editor only draws one when the filter declares options. So options mean `In` and nothing else,
7068
- * and their absence rules `In` out — offering it on a plain number filter would hand the
7069
- * operator a mode with no control behind it.
7070
- *
7071
- * Widened first: the per-kind tuples are literal, so `boolean`'s `[Equals]` makes the `In`
7072
- * comparison a "no overlap" error rather than an empty result.
7073
- */
7074
- function baseOperators(kind, hasOptions) {
7075
- const accepted = ALLOWED_OPERATORS[kind];
7076
- return accepted.filter((value) => hasOptions ? value === MatchModeCodes.In : value !== MatchModeCodes.In);
7077
- }
7078
- function toOperatorOptions(kind, operators) {
7079
- return operators.map((value) => ({
7080
- value,
7081
- labelKey: OPERATOR_WORDS[kind][value]?.pickerKey ?? value,
7082
- }));
7083
- }
7084
- /**
7085
- * A KIND's operator picker options, for callers with no filter behind them — today that is
7086
- * `spiderly-data-view`, whose own filter config derives its operator lists here instead of
7087
- * hand-keeping copies (the drift `allowed-operators.ts` exists to end). Per-FILTER narrowing
7088
- * lives in the store (`createFilterStore` resolves it once per declaration), so this cannot be
7089
- * handed a fake definition to satisfy — that shape shipped once and interpolated an empty
7090
- * filter name into its own error message.
7091
- */
7092
- function operatorOptionsForKind(kind, hasOptions = false) {
7093
- return toOperatorOptions(kind, baseOperators(kind, hasOptions));
7094
- }
7095
- /**
7096
- * The operator applied when nobody picked one, which has to be what a person MEANS by filling in
7097
- * the control. `Contains` for a fragment typed into a box; `GreaterThan` for a date, never
7098
- * `Equals` — a date equality against a TIMESTAMP matches only the row written in that exact
7099
- * second, and answers with an empty grid rather than an error (PACMS ran into this on all three
7100
- * of its date columns, Filip 2026-08-28).
7101
- */
7102
- const DEFAULT_OPERATOR = {
7103
- text: MatchModeCodes.Contains,
7104
- number: MatchModeCodes.Equals,
7105
- boolean: MatchModeCodes.Equals,
7106
- date: MatchModeCodes.GreaterThan,
7107
- };
7108
- function textFilter(config) {
7109
- return {
7110
- kind: 'text',
7111
- label: config.label,
7112
- operators: config.operators,
7113
- offered: config.offered,
7114
- };
7115
- }
7116
- /**
7117
- * Only a NUMBER filter accepts options, and the asymmetry is forced rather than chosen: options
7118
- * mean `In`, and `In` is the one operator ALLOWED_OPERATORS grants to numbers alone — because the
7119
- * generated paginator answers it with InvalidMatchMode on a string column. A text pick-list used
7120
- * to type-check and then throw at the first `set`.
7121
- */
7122
- function numberFilter(config) {
7123
- return {
7124
- kind: 'number',
7125
- label: config.label,
7126
- options: config.options,
7127
- operators: config.operators,
7128
- offered: config.offered,
7129
- };
7130
- }
7131
- function booleanFilter(config) {
7132
- return {
7133
- kind: 'boolean',
7134
- label: config.label,
7135
- operators: config.operators,
7136
- offered: config.offered,
7137
- };
7138
- }
7139
- function dateFilter(config) {
7140
- return {
7141
- kind: 'date',
7142
- label: config.label,
7143
- operators: config.operators,
7144
- offered: config.offered,
7145
- };
7146
- }
7147
- /** The shape `Date.toJSON` writes, which is the only value here JSON cannot round-trip itself. */
7148
- const ISO_DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
7149
- function reviveValue(value) {
7150
- if (typeof value === 'string' && ISO_DATE.test(value))
7151
- return new Date(value);
7152
- if (Array.isArray(value))
7153
- return value.map(reviveValue);
7154
- return value;
7155
- }
7156
- /**
7157
- * A blanked value is NOT a constraint. `contains ''` matches every row, so emitting one would
7158
- * narrow nothing while the bar claims the grid is filtered.
7159
- */
7160
- function isBlank(value) {
7161
- if (value === null || value === undefined)
7162
- return true;
7163
- if (typeof value === 'string')
7164
- return value.trim() === '';
7165
- if (Array.isArray(value))
7166
- return value.length === 0;
7167
- // A half-typed datepicker yields an Invalid Date, which JSON.stringify writes as `null`.
7168
- if (value instanceof Date)
7169
- return Number.isNaN(value.getTime());
7170
- return false;
7171
- }
7172
- /**
7173
- * Whether two constraint values are the same QUESTION. Identity is not it: every `set` builds a
7174
- * fresh draft, a multiselect hands back a fresh array per change, and two Dates at one instant
7175
- * are one constraint. This is what `commit` bails on, so a paste-over-itself or a double Apply
7176
- * cannot spend a request — the rule PACMS's order list used to hand-roll as `lastCommittedTerm`.
7177
- */
7178
- function valueEquals(a, b) {
7179
- if (a === b)
7180
- return true;
7181
- if (a instanceof Date && b instanceof Date)
7182
- return a.getTime() === b.getTime();
7183
- if (Array.isArray(a) && Array.isArray(b)) {
7184
- return a.length === b.length && a.every((item, i) => valueEquals(item, b[i]));
7443
+ this.lockVisible = lockVisible;
7444
+ this.width = width;
7445
+ this.onCellClick = onCellClick;
7185
7446
  }
7186
- return false;
7187
7447
  }
7188
- function constraintEquals(a, b) {
7189
- return a != null && a.operator === b.operator && valueEquals(a.value, b.value);
7448
+ class RowClickEvent {
7449
+ constructor({ index, id, additionalIndexes, } = {}) {
7450
+ this.index = index;
7451
+ this.id = id;
7452
+ this.additionalIndexes = additionalIndexes;
7453
+ }
7190
7454
  }
7191
- /**
7192
- * The filter engine. It knows nothing about columns: a filter has an id, a value type and a
7193
- * constraint, and that is the entire vocabulary.
7194
- */
7195
- function createFilterStore(definitions) {
7196
- // Per-filter offered operators, resolved ONCE per declaration — so an invalid narrowing is
7197
- // said once, not on every editor open — and re-resolved only where the inputs can actually
7198
- // change: `setOptions`, because options' PRESENCE flips a filter between pick-list (`In`) and
7199
- // plain operators. A per-store Map rather than the module-level WeakMap this first shipped
7200
- // as: that memo was keyed on a definition object consumers mutate, so late-filled options
7201
- // could freeze a filter's operators at the wrong answer.
7202
- const resolveOffered = (id) => {
7203
- const definition = definitions[id];
7204
- const base = baseOperators(definition.kind, definition.options != null);
7205
- let codes = base;
7206
- if (definition.operators?.length) {
7207
- const survivors = definition.operators.filter((operator) => base.includes(operator));
7208
- // Same polarity as the column path's computeMatchModes: a bad entry is dropped and said
7209
- // out loud, and a narrowing that leaves nothing falls back to the full list — an editor
7210
- // with no operators would be unusable, which is worse than an unwanted one. Mostly
7211
- // unreachable now that FilterConfig types `operators` per kind; kept for dynamically
7212
- // built arrays.
7213
- if (survivors.length < definition.operators.length) {
7214
- console.error(`Filter '${definition.label}' narrows to operators its ${definition.kind} kind does not ` +
7215
- `offer (${definition.operators.join(', ')}; offered: ${base.join(', ')}). ` +
7216
- `Unsupported entries are ignored${survivors.length ? '' : '; falling back to the full list'}.`);
7217
- }
7218
- codes = survivors.length ? survivors : base;
7219
- }
7220
- return { codes, options: toOperatorOptions(definition.kind, codes) };
7221
- };
7222
- const offered = new Map();
7223
- for (const id of Object.keys(definitions)) {
7224
- offered.set(id, resolveOffered(id));
7455
+ class AllClickEvent {
7456
+ constructor({ checked, additionalIndexes, } = {}) {
7457
+ this.checked = checked;
7458
+ this.additionalIndexes = additionalIndexes;
7225
7459
  }
7226
- /**
7227
- * The operator applied when nobody picked one. Options mean `In`, whatever the kind's default;
7228
- * a narrowed filter defaults to its FIRST declared operator.
7229
- */
7230
- const defaultOperatorFor = (id) => {
7231
- const definition = definitions[id];
7232
- if (definition.options != null)
7233
- return MatchModeCodes.In;
7234
- return definition.operators?.length
7235
- ? offered.get(id).codes[0]
7236
- : DEFAULT_OPERATOR[definition.kind];
7237
- };
7238
- // Two stores, and the split is the whole point: `set` writes a DRAFT, `commit` publishes it.
7239
- // A chip drawn off a draft would repeat the mistake the header's filter icon already shipped —
7240
- // claiming the grid is narrowed on the first keystroke. Controls that apply on change
7241
- // (multiselect, boolean, date) call both at once; a text box commits on Enter or blur.
7242
- const drafts = signal(new Map());
7243
- const writeDraft = (id, constraint) => {
7244
- const next = new Map(drafts());
7245
- if (constraint === null)
7246
- next.delete(id);
7247
- else
7248
- next.set(id, constraint);
7249
- drafts.set(next);
7250
- };
7251
- const committed = signal(new Map());
7252
- const applied = computed(() => [...committed()].map(([id, constraint]) => ({
7253
- id: id,
7254
- label: definitions[id].label,
7255
- kind: definitions[id].kind,
7256
- operator: constraint.operator,
7257
- operatorPhraseKey: OPERATOR_WORDS[definitions[id].kind][constraint.operator]?.phraseKey ??
7258
- constraint.operator,
7259
- value: constraint.value,
7260
- })));
7261
- const store = {
7262
- definitions,
7263
- applied,
7264
- /**
7265
- * A handle bound to one filter. This is the PLACEMENT api: it needs nothing from the component
7266
- * tree, so the same filter can be driven from the bar, from a header cell, or from a drawer
7267
- * rendered at app root. `value` is the DRAFT — a control shows what was typed, not what was
7268
- * applied; `applied()` is the other question and the bar asks that one.
7269
- */
7270
- get(id) {
7271
- return {
7272
- id: id,
7273
- label: definitions[id].label,
7274
- kind: definitions[id].kind,
7275
- value: computed(() => drafts().get(id)?.value),
7276
- operator: computed(() => drafts().get(id)?.operator),
7277
- // Getters, not snapshots: options can land AFTER a handle is taken — the bar's editor
7278
- // holds one for the life of the popover, and PACMS fills its lookups asynchronously —
7279
- // and a snapshot left an already-open pick-list empty until it was reopened. The
7280
- // operator list rides along, since setOptions can flip it to `In`.
7281
- get operators() {
7282
- return offered.get(id).options;
7283
- },
7284
- get defaultOperator() {
7285
- return defaultOperatorFor(id);
7286
- },
7287
- get options() {
7288
- return definitions[id].options;
7289
- },
7290
- set: (constraint) => store.set(id, constraint),
7291
- commit: () => store.commit(id),
7292
- reset: () => store.reset(id),
7293
- };
7294
- },
7295
- /**
7296
- * The ONE seam for choices that arrive after the store is built (PACMS fills its pick-lists
7297
- * from lookups that race a deploy). Never write `definitions[id].options` by hand: options'
7298
- * PRESENCE is what makes a filter a pick-list, so the setter also re-resolves the offered
7299
- * operators — a hand write would leave a late-filled filter offering `Equals` on a control
7300
- * that emits a list.
7301
- */
7302
- setOptions(id, options) {
7303
- definitions[id].options = options;
7304
- offered.set(id, resolveOffered(id));
7305
- },
7306
- set(id, constraint) {
7307
- const kind = definitions[id].kind;
7308
- // Throws rather than logging and ignoring, which is what `computeMatchModes` does for a
7309
- // column's OFFERED match modes. Different stakes: that one narrows a dropdown, so a bad
7310
- // entry merely fails to show, while this one assembles a QUERY — dropping it silently would
7311
- // leave the operator looking at unfiltered rows believing they are filtered.
7312
- if (!ALLOWED_OPERATORS[kind].includes(constraint.operator)) {
7313
- throw new Error(`Filter '${String(id)}' is ${kind}, which does not accept the '${constraint.operator}' ` +
7314
- `operator (allowed: ${ALLOWED_OPERATORS[kind].join(', ')}). The generated paginator ` +
7315
- `answers an unsupported match mode with InvalidMatchMode.`);
7316
- }
7317
- writeDraft(id, constraint);
7318
- },
7319
- /**
7320
- * `set` + `commit` in one breath — the shape every PROGRAMMATIC write takes (a view's
7321
- * `apply`, the sheet flow). The split API stays for controls, where draft-on-type /
7322
- * publish-on-Apply is the point; but a bare `set` whose `commit` is forgotten (or written
7323
- * with the wrong id — the pair repeats the string twice) fails silently: the draft is
7324
- * written, nothing is published, and a view narrows by nothing under a labelled tab.
7325
- */
7326
- setAndCommit(id, constraint) {
7327
- store.set(id, constraint);
7328
- store.commit(id);
7329
- },
7330
- /**
7331
- * Publishes the draft. A blanked draft REMOVES the constraint rather than committing an empty
7332
- * one, so `applied()` and the payload agree: no chip, no key, no `contains ''`.
7333
- */
7334
- commit(id) {
7335
- const draft = drafts().get(id);
7336
- const current = committed();
7337
- const shouldRemove = draft === undefined || isBlank(draft.value);
7338
- // A new Map is a new identity even with identical contents, and everything downstream
7339
- // reacts to identity — so a no-op commit would spend a request. The bail compares the
7340
- // CONSTRAINT, not the draft object: every set() builds a fresh draft, and the case that
7341
- // matters is an identical value re-committed (see valueEquals).
7342
- if (shouldRemove ? !current.has(id) : constraintEquals(current.get(id), draft))
7343
- return;
7344
- const next = new Map(current);
7345
- if (shouldRemove)
7346
- next.delete(id);
7347
- else
7348
- next.set(id, draft);
7349
- committed.set(next);
7350
- },
7351
- /**
7352
- * The chip's `x`. Drops the DRAFT as well as the committed constraint: a surviving draft
7353
- * leaves the control still showing the cleared text, and the next commit would restore a
7354
- * filter nobody re-typed.
7355
- */
7356
- reset(id) {
7357
- if (drafts().has(id))
7358
- writeDraft(id, null);
7359
- if (!committed().has(id))
7360
- return;
7361
- const next = new Map(committed());
7362
- next.delete(id);
7363
- committed.set(next);
7364
- },
7365
- /**
7366
- * The applied set, plain enough to store. Drafts are deliberately NOT included: a half-typed
7367
- * value is not a state anyone should come back to, and the bar's whole claim is that what it
7368
- * shows is what narrows the grid.
7369
- */
7370
- snapshot() {
7371
- const snapshot = {};
7372
- for (const [id, constraint] of committed()) {
7373
- snapshot[id] = {
7374
- operator: constraint.operator,
7375
- value: constraint.value,
7376
- };
7377
- }
7378
- return snapshot;
7379
- },
7380
- /**
7381
- * Replaces the applied set. The drafts are written too, so reopening a restored chip shows
7382
- * the value it is filtering by rather than an empty control.
7383
- *
7384
- * Ids the store no longer declares are dropped rather than carried: a filter removed from the
7385
- * declaration in a later release would otherwise ride every request from anyone whose storage
7386
- * still held it, with no chip able to name it and no way to clear it from the UI.
7387
- */
7388
- restore(snapshot) {
7389
- const next = new Map();
7390
- for (const [id, constraint] of Object.entries(snapshot ?? {})) {
7391
- if (!(id in definitions))
7392
- continue;
7393
- next.set(id, {
7394
- operator: constraint.operator,
7395
- value: reviveValue(constraint.value),
7396
- });
7397
- }
7398
- drafts.set(new Map(next));
7399
- committed.set(next);
7400
- },
7401
- /** The bar's "Clear all". `reset` for every filter, drafts included. */
7402
- clear() {
7403
- if (drafts().size > 0)
7404
- drafts.set(new Map());
7405
- if (committed().size > 0)
7406
- committed.set(new Map());
7407
- },
7408
- /** The `filters` half of `Filter` — what the generated paginator reads. */
7409
- toFilterPayload() {
7410
- const payload = {};
7411
- for (const [id, constraint] of committed()) {
7412
- payload[id] = [
7413
- { matchMode: constraint.operator, value: constraint.value },
7414
- ];
7415
- }
7416
- return payload;
7417
- },
7418
- };
7419
- return store;
7420
7460
  }
7421
7461
 
7422
7462
  class SpiderlyDataViewComponent {
@@ -8070,24 +8110,6 @@ function authInitializer(authService, platformId) {
8070
8110
  };
8071
8111
  }
8072
8112
 
8073
- class SpiderlyTranslocoLoader {
8074
- constructor(http, config) {
8075
- this.http = http;
8076
- this.config = config;
8077
- }
8078
- getTranslation(lang) {
8079
- return this.http.get(`${this.config.frontendUrl}/assets/i18n/${lang}.json`);
8080
- }
8081
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyTranslocoLoader, deps: [{ token: i1$2.HttpClient }, { token: ConfigServiceBase }], target: i0.ɵɵFactoryTarget.Injectable }); }
8082
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyTranslocoLoader, providedIn: 'root' }); }
8083
- }
8084
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyTranslocoLoader, decorators: [{
8085
- type: Injectable,
8086
- args: [{
8087
- providedIn: 'root',
8088
- }]
8089
- }], ctorParameters: () => [{ type: i1$2.HttpClient }, { type: ConfigServiceBase }] });
8090
-
8091
8113
  class SpiderlyTemplateTypeDirective {
8092
8114
  static ngTemplateContextGuard(dir, ctx) {
8093
8115
  return true;
@@ -8112,5 +8134,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
8112
8134
  * Generated bundle index. Do not edit.
8113
8135
  */
8114
8136
 
8115
- export { ALLOWED_OPERATORS, Action, AllClickEvent, ApiErrorCodes, ApiSecurityService, AppSidebarComponent, AuthCardComponent, AuthGuard, AuthResult, AuthResultWithCookies, AuthServiceBase, BaseAutocompleteControl, BaseControl, BaseDropdownControl, BaseEntity, BaseFormComponent, BaseFormService, CardSkeletonComponent, Codebook, Column, ConfigServiceBase, DEFAULT_EXTERNAL_PROVIDER_ICONS, DEFAULT_OPERATOR, ExternalLoginComponent, ExternalProvider, ExternalProviderPublic, Filter, FilterRule, FilterSortMeta, IndexCardComponent, InfoCardComponent, InitCompanyAuthDialogDetails, InitTopBarData, IsAuthorizedForSaveEvent, LastMenuIconIndexClicked, LayoutServiceBase, LazyLoadSelectedIdsResult, Login, LoginVerificationComponent, LoginVerificationToken, MatchModeCodes, MenuChangeEvent, MenuitemComponent, Namebook, NotAuthGuard, NotFoundComponent, PROPS_KEY, PaginatedResult, PanelBodyComponent, PanelFooterComponent, PanelHeaderComponent, PrimengOption, ProfileAvatarComponent, ReflectProp, RefreshTokenRequest, RequiredComponent, RowClickEvent, SecurityPermissionCodes, SendLoginVerificationEmailResult, SideMenuTopBarComponent, SidebarMenuComponent, SimpleSaveResult, SpiderlyAutocompleteComponent, SpiderlyButtonBaseComponent, SpiderlyButtonComponent, SpiderlyCalendarComponent, SpiderlyCardComponent, SpiderlyCellTemplateDirective, SpiderlyCheckboxComponent, SpiderlyColorPickerComponent, SpiderlyControlsModule, SpiderlyDataTableActionsDirective, SpiderlyDataTableComponent, SpiderlyDataViewComponent, SpiderlyDeleteConfirmationComponent, SpiderlyDropdownComponent, SpiderlyEditorComponent, SpiderlyErrorHandler, SpiderlyFileComponent, SpiderlyFileSelectEvent, SpiderlyFilterBarComponent, SpiderlyFilterTemplateDirective, SpiderlyFormArray, SpiderlyFormControl, SpiderlyFormGroup, SpiderlyLayoutComponent, SpiderlyLoginComponent, SpiderlyMarkdownComponent, SpiderlyMessageService, SpiderlyMultiAutocompleteComponent, SpiderlyMultiSelectComponent, SpiderlyNumberComponent, SpiderlyOverflowTitleDirective, SpiderlyPanelComponent, SpiderlyPanelsModule, SpiderlyPasswordComponent, SpiderlyReturnButtonComponent, SpiderlySplitButtonComponent, SpiderlyTab, SpiderlyTemplateTypeDirective, SpiderlyTextareaComponent, SpiderlyTextboxComponent, SpiderlyTranslocoLoader, TopBarComponent, UserBase, UserRole, ValidatorAbstractService, VerificationTokenRequest, VerificationTypeCodes, VerificationWrapperComponent, adjustColor, authInitializer, booleanFilter, capitalizeFirstChar, createFilterStore, dateFilter, deleteAction, exportListToExcel, firstCharToUpper, foldForSearch, getFileNameFromContentDisposition, getFilterOptions, getHtmlImgDisplayString64, getImageDimensions, getMimeTypeForFileName, getMonth, getParentUrl, getPrimengAutocompleteCodebookOptions, getPrimengAutocompleteNamebookOptions, getPrimengDropdownCodebookOptions, getPrimengDropdownNamebookOptions, getPrimengNamebookOptions, httpLoadingInterceptor, isExcelFileType, isFileImageType, isNullOrEmpty, jsonHttpInterceptor, jwtInterceptor, kebabToTitleCase, nameOf, nameof, numberFilter, operatorOptionsForKind, parseDateOnlyLocal, pushAction, saveResponseAsFile, scrollElementIntoViewIfAboveViewport, selectedTab, singleOrDefault, splitPascalCase, textFilter, toCommaSeparatedString, unauthorizedInterceptor, validatePrecisionScale };
8137
+ export { ALLOWED_OPERATORS, Action, AllClickEvent, ApiErrorCodes, ApiSecurityService, AppSidebarComponent, AuthCardComponent, AuthGuard, AuthResult, AuthResultWithCookies, AuthServiceBase, BaseAutocompleteControl, BaseControl, BaseDropdownControl, BaseEntity, BaseFormComponent, BaseFormService, CardSkeletonComponent, Codebook, Column, ConfigServiceBase, DEFAULT_EXTERNAL_PROVIDER_ICONS, DEFAULT_OPERATOR, ExternalLoginComponent, ExternalProvider, ExternalProviderPublic, Filter, FilterRule, FilterSortMeta, IndexCardComponent, InfoCardComponent, InitCompanyAuthDialogDetails, InitTopBarData, IsAuthorizedForSaveEvent, LastMenuIconIndexClicked, LayoutServiceBase, LazyLoadSelectedIdsResult, Login, LoginVerificationComponent, LoginVerificationToken, MatchModeCodes, MenuChangeEvent, MenuitemComponent, Namebook, NotAuthGuard, NotFoundComponent, PROPS_KEY, PaginatedResult, PanelBodyComponent, PanelFooterComponent, PanelHeaderComponent, PrimengOption, ProfileAvatarComponent, ReflectProp, RefreshTokenRequest, RequiredComponent, RowClickEvent, SecurityPermissionCodes, SendLoginVerificationEmailResult, SideMenuTopBarComponent, SidebarMenuComponent, SimpleSaveResult, SpiderlyAutocompleteComponent, SpiderlyButtonBaseComponent, SpiderlyButtonComponent, SpiderlyCalendarComponent, SpiderlyCardComponent, SpiderlyCellTemplateDirective, SpiderlyCheckboxComponent, SpiderlyColorPickerComponent, SpiderlyControlsModule, SpiderlyDataTableActionsDirective, SpiderlyDataTableComponent, SpiderlyDataViewComponent, SpiderlyDeleteConfirmationComponent, SpiderlyDropdownComponent, SpiderlyEditorComponent, SpiderlyErrorHandler, SpiderlyFileComponent, SpiderlyFileSelectEvent, SpiderlyFilterBarComponent, SpiderlyFilterTemplateDirective, SpiderlyFormArray, SpiderlyFormControl, SpiderlyFormGroup, SpiderlyLayoutComponent, SpiderlyLoginComponent, SpiderlyMarkdownComponent, SpiderlyMessageService, SpiderlyMultiAutocompleteComponent, SpiderlyMultiSelectComponent, SpiderlyNumberComponent, SpiderlyOverflowTitleDirective, SpiderlyPanelComponent, SpiderlyPanelsModule, SpiderlyPasswordComponent, SpiderlyReturnButtonComponent, SpiderlySplitButtonComponent, SpiderlyTab, SpiderlyTemplateTypeDirective, SpiderlyTextareaComponent, SpiderlyTextboxComponent, TopBarComponent, UserBase, UserRole, ValidatorAbstractService, VerificationTokenRequest, VerificationTypeCodes, VerificationWrapperComponent, adjustColor, authInitializer, booleanFilter, capitalizeFirstChar, createFilterStore, dateFilter, deleteAction, exportListToExcel, firstCharToUpper, foldForSearch, getFileNameFromContentDisposition, getFilterOptions, getHtmlImgDisplayString64, getImageDimensions, getMimeTypeForFileName, getMonth, getParentUrl, getPrimengAutocompleteCodebookOptions, getPrimengAutocompleteNamebookOptions, getPrimengDropdownCodebookOptions, getPrimengDropdownNamebookOptions, getPrimengNamebookOptions, httpLoadingInterceptor, isExcelFileType, isFileImageType, isNullOrEmpty, jsonHttpInterceptor, jwtInterceptor, kebabToTitleCase, nameOf, nameof, numberFilter, operatorOptionsForKind, parseDateOnlyLocal, pushAction, saveResponseAsFile, scrollElementIntoViewIfAboveViewport, selectedTab, singleOrDefault, splitPascalCase, textFilter, toCommaSeparatedString, unauthorizedInterceptor, validatePrecisionScale };
8116
8138
  //# sourceMappingURL=spiderly.mjs.map