spiderly 19.12.0 → 19.12.1

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,4 +1,4 @@
1
- import { AfterViewInit, ErrorHandler, EventEmitter, OnChanges, OnDestroy, OnInit, QueryList, TemplateRef } from '@angular/core';
1
+ import { AfterViewInit, ErrorHandler, EventEmitter, OnChanges, OnDestroy, OnInit, QueryList, SimpleChanges, TemplateRef } from '@angular/core';
2
2
  import { ActivatedRoute, Router } from '@angular/router';
3
3
  import { TranslocoService } from '@jsverse/transloco';
4
4
  import { SortMeta } from 'primeng/api';
@@ -106,7 +106,8 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
106
106
  /** Saved questions for this table, rendered as tabs above the bar. */
107
107
  views?: TableView[];
108
108
  /**
109
- * The view currently selected. Seeded from the first view in `ngOnInit`, never left null on a
109
+ * The view currently selected. Seeded from the first view in `ngOnInit` — then overridden by
110
+ * `restoreActiveView`, so a reload lands where the operator was — and never left null on a
110
111
  * table that has views: `viewScope` keys the layout off it, so an unseeded table writes its
111
112
  * width and order to the SAME key an unmigrated table uses — orphaned the moment the operator
112
113
  * clicks the first tab — and no tab reads as selected until they do.
@@ -229,7 +230,7 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
229
230
  * backend `.Take`s whatever it is sent.
230
231
  */
231
232
  private mergeActivePageSizesIntoOptions;
232
- ngOnChanges(): void;
233
+ ngOnChanges(changes: SimpleChanges): void;
233
234
  ngOnInit(): void;
234
235
  /**
235
236
  * User visibility choices (field → visible), holding only fields the user explicitly
@@ -297,11 +298,21 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
297
298
  * wrap, order and width alike. It undid visibility only until the header menu shipped three
298
299
  * more gestures beside it, and a layout with no way back is worse than one with no knobs.
299
300
  *
300
- * Hiding — here or from the menu — never touches filters or sort: the chip bar names every
301
- * applied filter and the sort chip names the sort, hidden column or not (decision 2b). The
302
- * legacy "hidden contributes nothing" apparatus died with the header-filter path.
301
+ * On a STORE table, hiding — here or from the menu — never touches filters or sort: the chip
302
+ * bar names every applied filter and the sort chip names the sort, hidden column or not
303
+ * (decision 2b). A storeless table keeps the sort half of the legacy invariant instead — see
304
+ * `dropHiddenColumnSort`.
303
305
  */
304
306
  resetColumnLayout(): void;
307
+ /**
308
+ * The sort half of the legacy "hidden contributes nothing" invariant, kept ONLY for storeless
309
+ * tables (the generated M2M details grids, until spiderly#407 scaffolds their stores): they
310
+ * have no sort chip, so a hidden column's sort would order the grid with nothing on screen
311
+ * naming it and no control left to clear it. The filter half needs no heir — `lazyLoad`
312
+ * overwrites the outgoing filters unconditionally. On a store table this is a no-op:
313
+ * decision 2b's chips are the visible surface, hidden column or not.
314
+ */
315
+ private dropHiddenColumnSort;
305
316
  /**
306
317
  * Per-column wrap, by `field`. A SEPARATE key from the visibility overrides on purpose: folding
307
318
  * both into one object would invalidate every layout already in an operator's localStorage, and
@@ -336,8 +347,51 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
336
347
  */
337
348
  private get filtersStateKey();
338
349
  private get filterStorage();
350
+ /**
351
+ * Where the active view id lives. Deliberately NOT view-scoped — it is the input `viewScope`
352
+ * derives from — and it follows `stateStorage` like the filters rather than the layout's
353
+ * always-local rule, because the id and the filters it scopes are one question: a durable id
354
+ * over session-scoped filters would restore a tab with nothing under it.
355
+ */
356
+ private get viewStateKey();
357
+ /** Only `selectView` writes; the `ngOnInit` seed does not — absence means "the default". */
358
+ private persistActiveView;
359
+ /**
360
+ * Puts a reload back on the view the operator was using. Every persisted key carries the view
361
+ * segment, so before this existed the seed decided what got restored: F5 landed on the first
362
+ * view and read `…:all:filters` — the tab snapped to "Sve" and the bar came back empty while
363
+ * the operator's filters sat intact under the old view's segment (Filip, on /porudzbine).
364
+ *
365
+ * An id naming no current view keeps the seed AND the stored key: "vanished by deploy" and
366
+ * "not yet arrived" are indistinguishable here (order-list's catalog-licensed "Za pakovanje"
367
+ * is literally both), so `adoptStoredView` re-reads the key on every `[views]` change and the
368
+ * key self-heals on the next explicit click.
369
+ */
370
+ private restoreActiveView;
371
+ /**
372
+ * The second half of `restoreActiveView`: a stored view that did not exist at init is adopted
373
+ * when a later `[views]` change brings it — through `selectView`, so it gets the same
374
+ * stored-wins/transient/layout/requery semantics a click would have had. There is no parked
375
+ * copy of the stored id — the key itself is re-read, and because every explicit pick
376
+ * overwrites it, the operator's own choice wins by construction. Accepted cost of adopting
377
+ * late instead of holding the first request: a brief flash of the seeded view's rows and a
378
+ * second request when the late view lands.
379
+ */
380
+ private adoptStoredView;
381
+ private viewById;
339
382
  private persistAppliedFilters;
340
- private restoreAppliedFilters;
383
+ /**
384
+ * Applies the right filters for entering `view` — the transient rule's one home. A TRANSIENT
385
+ * view's `apply` is a function of NOW, so it always re-derives; restoring would put
386
+ * yesterday's date under a tab claiming "today" (`persistAppliedFilters` has no transient
387
+ * guard, so storage holds the LAST answer). A non-transient view's own stored answer wins
388
+ * over its `apply`. The semantics argument is the one deliberate difference between the
389
+ * callers: a 'click' re-asks the view's question when nothing is stored, a 'refresh' returns
390
+ * the empty state the operator left — they cleared it, and F5 must give the cleared state
391
+ * back. On a table with no views, `view` is null and this is a plain restore of the
392
+ * `persistAppliedFilters` snapshot.
393
+ */
394
+ private enterViewFilters;
341
395
  private get layoutStateKey();
342
396
  /** One key for the whole layout — wrap now, widths next (decision 5). */
343
397
  private persistColumnLayout;
@@ -349,13 +403,74 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
349
403
  * because we need it before PrimeNG's restoreState() runs on the first [value] change.
350
404
  */
351
405
  private persistedTableState;
352
- /**
353
- * The declared default as PrimeNG sort meta, or null when none is declared. Applies even
354
- * while its column is hidden: the sort chip names the ordering whether or not the column
355
- * is on screen (decision 2b).
406
+ private activeView;
407
+ /**
408
+ * Where the operator's per-view sort override lives. Null on a table without views — those
409
+ * keep the pure PrimeNG sort persistence they always had — and null on a STORELESS table:
410
+ * there "hidden contributes nothing" retires a hidden column's sort (`dropHiddenColumnSort`)
411
+ * with no persist call at the hide site, so an override restored blind at the next init
412
+ * would re-impose a sort no chip can name. Store tables — where every viewed consumer
413
+ * lives — have the chip. Follows `stateStorage` like the filters and the view id: the sort
414
+ * is part of the same question they scope.
415
+ */
416
+ private get viewSortStateKey();
417
+ /** The operator's stored sort for the active view, sanitized like any persisted sort. */
418
+ private persistedViewSort;
419
+ /** The sort this view asks for: the operator's stored override, else its declared sort. */
420
+ private viewSort;
421
+ /**
422
+ * Records the current sort as the active view's override — called from the EXPLICIT sort
423
+ * gestures only (header click, menu sort, Clear filters), never from a load: a sort merely
424
+ * inherited at init must not crystallize into an override, or a later change to the view's
425
+ * declared sort would stop flowing to operators who never touched sort — the same
426
+ * flows-through rule `columnVisibilityOverrides` documents. A sort equal to the declared
427
+ * default is stored as ABSENCE for the same reason.
428
+ */
429
+ private persistActiveViewSort;
430
+ /**
431
+ * Puts the entering view's sort on the grid — stored override first, then the declared sort;
432
+ * a view stating no preference keeps the ordering already there (the pre-decision-10-sort
433
+ * behavior, and the least surprising for the tab an operator never sorted). Broadcast only —
434
+ * the caller owns the refetch, because which request carries the new sort differs between the
435
+ * store and storeless paths.
436
+ */
437
+ private applyViewSort;
438
+ /**
439
+ * Rewrites the sort inside PrimeNG's persisted state to the one `ngOnInit` resolved. The two
440
+ * can disagree only on a viewed table — PrimeNG's state is table-global while the resolved
441
+ * sort is the view's — and PrimeNG's restoreState() applies its copy on the first [value]
442
+ * change, AFTER the initial request already left with ours: without this line the grid's
443
+ * arrows and every later request silently fall back to the other view's sort. Handed the
444
+ * already-parsed state: `ngOnInit` reads the blob once for the whole init pass.
445
+ */
446
+ private alignPersistedTableStateSort;
447
+ /**
448
+ * The DECLARED default in the current context: the active view's own sort when it declares
449
+ * one, else the table-level `defaultSortField`. Every "fall back to the declared default"
450
+ * site — tri-state un-sort, Clear filters, the unsorted-load safety net — reads this, so
451
+ * un-sorting inside a view lands on the view's ordering, not the table's.
356
452
  */
357
453
  private defaultMultiSortMeta;
358
- private persistedMultiSortMeta;
454
+ /**
455
+ * The active view's declared sort, sanitized: a non-sortable field would answer every load
456
+ * with a 400 (`keepSortableMeta`'s rationale), so it is dropped rather than sent; a view
457
+ * whose declaration sanitizes to nothing falls through to the table default.
458
+ */
459
+ private viewDeclaredMultiSortMeta;
460
+ /**
461
+ * The table-level declared default as PrimeNG sort meta, or null when none is declared. On a
462
+ * store table it applies even while its column is hidden — the sort chip names the ordering
463
+ * whether or not the column is on screen (decision 2b). A STORELESS table has no chip, so a
464
+ * hidden default would order the grid with nothing on screen naming it; there the default
465
+ * dies with the column, exactly as the legacy invariant had it (see `dropHiddenColumnSort`).
466
+ */
467
+ private tableDefaultMultiSortMeta;
468
+ /**
469
+ * `keepSortableMeta` with "sanitized to nothing" surfaced as null — the rule every sort
470
+ * SOURCE shares ("a sort that sanitizes to nothing is no sort"), so the resolution chains
471
+ * can `??` past an empty answer instead of stopping on an empty array.
472
+ */
473
+ private sanitizedSortMeta;
359
474
  /**
360
475
  * Drops sort meta the backend has no sort case for. Persisted state outlives the rule that
361
476
  * produced it — a sort stored before a column became non-sortable (or before the library
@@ -367,6 +482,8 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
367
482
  */
368
483
  private keepSortableMeta;
369
484
  private applyDefaultSortIfUnsorted;
485
+ /** Puts `meta` on the grid and tells the header icons — no fetch; the caller owns that. */
486
+ private broadcastSort;
370
487
  onPageChange(container: HTMLElement): void;
371
488
  /**
372
489
  * The one pending predicate: PrimeNG's overlay and the container's `aria-busy` must never
@@ -579,6 +696,15 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
579
696
  * make every table load twice.
580
697
  */
581
698
  private requeryOnAppliedFilters;
699
+ /**
700
+ * Back to page one, state saved, one fetch off FRESH metadata — the shared tail of the
701
+ * requery effect and `selectView`'s sort-only switch. The save is load-bearing: PrimeNG
702
+ * saves state only from its own interactions, so without it the pre-change page offset
703
+ * survives in storage and the NEXT visit replays it against a narrowed result set — the
704
+ * backend answers data=[] for an offset the filtered total no longer reaches, and the grid
705
+ * renders empty under chips claiming matches.
706
+ */
707
+ private refetchFromFirstPage;
582
708
  reload(): void;
583
709
  showActions(): boolean;
584
710
  getStyleForBodyColumn(col: Column<any>): string;
@@ -677,6 +803,15 @@ export interface TableView<TFilters = any> {
677
803
  * transience is about the QUESTION, not the furniture.
678
804
  */
679
805
  transient?: boolean;
806
+ /**
807
+ * The view's declared ordering — for the view whose question includes one ("oldest shipped
808
+ * first" IS the work list, not a reading preference). Entering the view applies it; the
809
+ * tri-state un-sort and Clear filters return to it instead of the table default; an operator's
810
+ * own sort overrides it, persisted per view on store tables (decision 10's sort half). A view
811
+ * declaring none keeps whatever ordering the grid already has, exactly as before this field
812
+ * existed.
813
+ */
814
+ sort?: SortMeta[];
680
815
  }
681
816
  export declare class Column<T = any> {
682
817
  name?: string;
@@ -703,9 +838,17 @@ export declare class Column<T = any> {
703
838
  * only so details-table cols emitted by an OLDER Spiderly.SourceGenerators (which wrote
704
839
  * `showMatchModes: true`) still compile against this library during a mixed-version window —
705
840
  * a consumer on published NuGet generators plus an npm-linked library build. Delete it once
706
- * every consumer regenerates with a generator from this release or later.
841
+ * every consumer regenerates with a generator from this release or later (spiderly#407).
707
842
  */
708
843
  showMatchModes?: boolean;
844
+ /**
845
+ * @deprecated The same version-skew bridge as {@link showMatchModes}, and nothing else: older
846
+ * generators emit `filterField: 'xxxId'` for every dropdown-typed details-table column, so
847
+ * deleting it breaks the mixed window the moment a consumer adds one. NOTHING reads it —
848
+ * filter ids live in the store, linked from a column via {@link filterId}. Delete with
849
+ * `showMatchModes` (spiderly#407).
850
+ */
851
+ filterField?: string & keyof T;
709
852
  dropdownOrMultiselectValues?: PrimengOption[];
710
853
  actions?: Action[];
711
854
  editable?: boolean;
@@ -742,12 +885,13 @@ export declare class Column<T = any> {
742
885
  * Not applied to editable cells — those belong to their inline input.
743
886
  */
744
887
  onCellClick?: (event: CellClickEvent) => void;
745
- constructor({ name, field, filterType, filterId, showMatchModes, dropdownOrMultiselectValues, actions, editable, showTime, decimalPlaces, sortable, visible, lockVisible, width, onCellClick, }?: {
888
+ constructor({ name, field, filterType, filterId, showMatchModes, filterField, dropdownOrMultiselectValues, actions, editable, showTime, decimalPlaces, sortable, visible, lockVisible, width, onCellClick, }?: {
746
889
  name?: string;
747
890
  field?: string & keyof T;
748
891
  filterType?: 'text' | 'date' | 'multiselect' | 'boolean' | 'numeric' | 'blob';
749
892
  filterId?: string;
750
893
  showMatchModes?: boolean;
894
+ filterField?: string & keyof T;
751
895
  dropdownOrMultiselectValues?: PrimengOption[];
752
896
  actions?: Action[];
753
897
  editable?: boolean;
@@ -4,10 +4,11 @@ import { MatchModeCodes } from '../enums/match-mode-enum-codes';
4
4
  * paginator implements per type (`PaginatedResultGenerator`): `In` on a string column answers
5
5
  * with InvalidMatchMode, PrimeNG's own text list adds three modes the backend 400s, and so on.
6
6
  *
7
- * Deliberately a LEAF module (imports nothing but the codes) so both consumers can derive from
8
- * it without a cycle: the store's runtime validation and offered lists (`filter-store.ts`), and
9
- * the compile-time `FilterRule.matchMode` union (`entities/filter-rule.ts`). These used to be
10
- * hand-kept copies; edit the table here and every surface follows.
7
+ * Deliberately a LEAF module (imports nothing but the codes) so every consumer can derive from
8
+ * it without a cycle: the store's runtime validation and offered lists (`filter-store.ts`), the
9
+ * compile-time `FilterRule.matchMode` union (`entities/filter-rule.ts`), and the data view's
10
+ * kind-level lists (via `operatorOptionsForKind`). These used to be hand-kept copies; edit the
11
+ * table here and every surface follows.
11
12
  *
12
13
  * Tuple order is DISPLAY order for every operator picker (Equals, then "less/before", then
13
14
  * "more/after" — the order the header dropdowns always shipped).
@@ -32,6 +32,15 @@ export interface OperatorOption {
32
32
  /** Transloco key, resolved by the control — the store holds no translations. */
33
33
  labelKey: string;
34
34
  }
35
+ /**
36
+ * A KIND's operator picker options, for callers with no filter behind them — today that is
37
+ * `spiderly-data-view`, whose own filter config derives its operator lists here instead of
38
+ * hand-keeping copies (the drift `allowed-operators.ts` exists to end). Per-FILTER narrowing
39
+ * lives in the store (`createFilterStore` resolves it once per declaration), so this cannot be
40
+ * handed a fake definition to satisfy — that shape shipped once and interpolated an empty
41
+ * filter name into its own error message.
42
+ */
43
+ export declare function operatorOptionsForKind(kind: FilterValueKind, hasOptions?: boolean): OperatorOption[];
35
44
  /**
36
45
  * The operator applied when nobody picked one, which has to be what a person MEANS by filling in
37
46
  * the control. `Contains` for a fragment typed into a box; `GreaterThan` for a date, never
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spiderly",
3
- "version": "19.12.0",
3
+ "version": "19.12.1",
4
4
  "author": "Filip Trivan",
5
5
  "license": "MIT",
6
6
  "description": "Spiderly Angular Library to use in combination with Spiderly.",