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.
- package/agent/docs/angular-customization/index.md +17 -0
- package/agent/docs/frontend-localization/index.md +29 -16
- package/agent/skills/deployment/SKILL.md +11 -6
- package/fesm2022/spiderly.mjs +519 -497
- package/fesm2022/spiderly.mjs.map +1 -1
- package/lib/components/spiderly-data-table/spiderly-data-table.component.d.ts +31 -1
- package/package.json +1 -1
- package/public-api.d.ts +0 -1
- package/lib/services/spiderly-transloco-loader.d.ts +0 -12
package/fesm2022/spiderly.mjs
CHANGED
|
@@ -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,
|
|
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.
|
|
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.
|
|
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 (!
|
|
6073
|
+
enterViewFilters(view, semantics, store = this.filters, key = this.filtersStateKey) {
|
|
6074
|
+
if (!store)
|
|
5611
6075
|
return;
|
|
5612
|
-
const stored = !view?.transient && this.
|
|
5613
|
-
? readStoredJson(this.filterStorage, this.filtersStateKey)
|
|
5614
|
-
: null;
|
|
6076
|
+
const stored = !view?.transient && key ? readStoredJson(this.filterStorage, key) : null;
|
|
5615
6077
|
if (stored)
|
|
5616
|
-
|
|
6078
|
+
store.restore(stored);
|
|
5617
6079
|
else if (view?.transient || semantics === 'click')
|
|
5618
|
-
view?.apply?.(
|
|
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
|
-
|
|
7189
|
-
|
|
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
|
-
|
|
7193
|
-
|
|
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,
|
|
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
|