spiderly 19.12.0 → 19.12.2
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 +5 -5
- package/agent/docs/e2e-testing/index.md +1 -1
- package/fesm2022/spiderly.mjs +629 -138
- package/fesm2022/spiderly.mjs.map +1 -1
- package/lib/components/spiderly-data-table/spiderly-data-table.component.d.ts +208 -21
- package/lib/filters/allowed-operators.d.ts +5 -4
- package/lib/filters/filter-store.d.ts +23 -4
- package/lib/filters/spiderly-filter-bar.component.d.ts +33 -2
- package/package.json +1 -1
- package/styles/filters/spiderly-filter-bar.component.scss +37 -0
|
@@ -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';
|
|
@@ -10,7 +10,7 @@ import { Filter } from '../../entities/filter';
|
|
|
10
10
|
import { LazyLoadSelectedIdsResult } from '../../entities/lazy-load-selected-ids-result';
|
|
11
11
|
import { PaginatedResult } from '../../entities/paginated-result';
|
|
12
12
|
import { PrimengOption } from '../../entities/primeng-option';
|
|
13
|
-
import { FilterSource, SortKeyLabel } from '../../filters/filter-store';
|
|
13
|
+
import { FilterSource, SortKeyLabel, SortPickOption } from '../../filters/filter-store';
|
|
14
14
|
import { SpiderlyFilterTemplateDirective } from '../../directives/spiderly-filter-template.directive';
|
|
15
15
|
import { ConfigServiceBase } from '../../services/config.service.base';
|
|
16
16
|
import { SpiderlyMessageService } from '../../services/spiderly-message.service';
|
|
@@ -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
|
|
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
|
-
*
|
|
301
|
-
* applied filter and the sort chip names the sort, hidden column or not
|
|
302
|
-
*
|
|
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
|
-
|
|
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,83 @@ 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
|
-
|
|
354
|
-
*
|
|
355
|
-
*
|
|
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
|
+
/**
|
|
420
|
+
* THE resolution ladder, named once: the operator's stored override, else the declared
|
|
421
|
+
* default (`defaultMultiSortMeta` = view's declared sort ?? table default). Null means
|
|
422
|
+
* "unsorted" — callers append their own `?? []` / blob terms rather than re-spelling the
|
|
423
|
+
* ladder, so a future term (a per-view default refinement, decision 10's columns half)
|
|
424
|
+
* lands everywhere at once.
|
|
425
|
+
*/
|
|
426
|
+
private resolvedViewSort;
|
|
427
|
+
/**
|
|
428
|
+
* Records the current sort as the active view's override — called from the EXPLICIT sort
|
|
429
|
+
* gestures only (header click, `applySort` — the menu and both bar entry points), never
|
|
430
|
+
* from a load: a sort merely inherited at init must not crystallize into an override, or a
|
|
431
|
+
* later change to the view's declared sort would stop flowing to operators who never
|
|
432
|
+
* touched sort — the same flows-through rule `columnVisibilityOverrides` documents. A sort
|
|
433
|
+
* equal to the declared default is stored as ABSENCE for the same reason.
|
|
434
|
+
*/
|
|
435
|
+
private persistActiveViewSort;
|
|
436
|
+
/**
|
|
437
|
+
* Puts the entering view's sort on the grid — stored override, else declared sort, else the
|
|
438
|
+
* table default, else UNSORTED. A view is a COMPLETE state, sort included (2026-09-06,
|
|
439
|
+
* grilled — supersedes the "a view stating no preference keeps the ordering already there"
|
|
440
|
+
* this first shipped with): the carried-over ordering read as a bug in its first live week,
|
|
441
|
+
* when "Poslate"'s statusChangedAt rode onto "Sve", whose sorted column ships hidden —
|
|
442
|
+
* read-only chip, no header to tri-state, no way out. Polaris IndexFilters is the model:
|
|
443
|
+
* sort is part of a view's saved definition. Broadcast only — the caller owns the refetch,
|
|
444
|
+
* because which request carries the new sort differs between the store and storeless paths.
|
|
445
|
+
*/
|
|
446
|
+
private applyViewSort;
|
|
447
|
+
/**
|
|
448
|
+
* Rewrites the sort inside PrimeNG's persisted state to the one `ngOnInit` resolved. The two
|
|
449
|
+
* can disagree only on a viewed table — PrimeNG's state is table-global while the resolved
|
|
450
|
+
* sort is the view's — and PrimeNG's restoreState() applies its copy on the first [value]
|
|
451
|
+
* change, AFTER the initial request already left with ours: without this line the grid's
|
|
452
|
+
* arrows and every later request silently fall back to the other view's sort. Handed the
|
|
453
|
+
* already-parsed state: `ngOnInit` reads the blob once for the whole init pass.
|
|
454
|
+
*/
|
|
455
|
+
private alignPersistedTableStateSort;
|
|
456
|
+
/**
|
|
457
|
+
* The DECLARED default in the current context: the active view's own sort when it declares
|
|
458
|
+
* one, else the table-level `defaultSortField`. Every "fall back to the declared default"
|
|
459
|
+
* site — tri-state un-sort, Clear filters, the unsorted-load safety net — reads this, so
|
|
460
|
+
* un-sorting inside a view lands on the view's ordering, not the table's.
|
|
356
461
|
*/
|
|
357
462
|
private defaultMultiSortMeta;
|
|
358
|
-
|
|
463
|
+
/**
|
|
464
|
+
* The active view's declared sort, sanitized: a non-sortable field would answer every load
|
|
465
|
+
* with a 400 (`keepSortableMeta`'s rationale), so it is dropped rather than sent; a view
|
|
466
|
+
* whose declaration sanitizes to nothing falls through to the table default.
|
|
467
|
+
*/
|
|
468
|
+
private viewDeclaredMultiSortMeta;
|
|
469
|
+
/**
|
|
470
|
+
* The table-level declared default as PrimeNG sort meta, or null when none is declared. On a
|
|
471
|
+
* store table it applies even while its column is hidden — the sort chip names the ordering
|
|
472
|
+
* whether or not the column is on screen (decision 2b). A STORELESS table has no chip, so a
|
|
473
|
+
* hidden default would order the grid with nothing on screen naming it; there the default
|
|
474
|
+
* dies with the column, exactly as the legacy invariant had it (see `dropHiddenColumnSort`).
|
|
475
|
+
*/
|
|
476
|
+
private tableDefaultMultiSortMeta;
|
|
477
|
+
/**
|
|
478
|
+
* `keepSortableMeta` with "sanitized to nothing" surfaced as null — the rule every sort
|
|
479
|
+
* SOURCE shares ("a sort that sanitizes to nothing is no sort"), so the resolution chains
|
|
480
|
+
* can `??` past an empty answer instead of stopping on an empty array.
|
|
481
|
+
*/
|
|
482
|
+
private sanitizedSortMeta;
|
|
359
483
|
/**
|
|
360
484
|
* Drops sort meta the backend has no sort case for. Persisted state outlives the rule that
|
|
361
485
|
* produced it — a sort stored before a column became non-sortable (or before the library
|
|
@@ -367,6 +491,8 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
|
|
|
367
491
|
*/
|
|
368
492
|
private keepSortableMeta;
|
|
369
493
|
private applyDefaultSortIfUnsorted;
|
|
494
|
+
/** Puts `meta` on the grid and tells the header icons — no fetch; the caller owns that. */
|
|
495
|
+
private broadcastSort;
|
|
370
496
|
onPageChange(container: HTMLElement): void;
|
|
371
497
|
/**
|
|
372
498
|
* The one pending predicate: PrimeNG's overlay and the container's `aria-busy` must never
|
|
@@ -421,8 +547,8 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
|
|
|
421
547
|
/**
|
|
422
548
|
* Switches to a view. Clears first, always: a view is a STATE rather than an addition, and two
|
|
423
549
|
* of them composed produce a question nobody asked. The clear runs through the store directly
|
|
424
|
-
* rather than through `clear(
|
|
425
|
-
* view
|
|
550
|
+
* rather than through `clear()`, whose requery effect would spend a fetch before the entering
|
|
551
|
+
* view's filters are in place.
|
|
426
552
|
*/
|
|
427
553
|
selectView(view: TableView): void;
|
|
428
554
|
/**
|
|
@@ -519,6 +645,35 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
|
|
|
519
645
|
* left falls back to its own name rather than vanishing.
|
|
520
646
|
*/
|
|
521
647
|
private refreshSortKeys;
|
|
648
|
+
/**
|
|
649
|
+
* Gates the picker's reset row. Refreshed with `sortKeys` — same request, same reason it is a
|
|
650
|
+
* field rather than a getter. "Default" is view-aware (`defaultMultiSortMeta`), so inside a
|
|
651
|
+
* view the row offers the view's ordering back, not the table's.
|
|
652
|
+
*/
|
|
653
|
+
sortIsDefault: boolean;
|
|
654
|
+
/**
|
|
655
|
+
* What the bar's picker offers: every sortable data column, hidden ones INCLUDED — the picker
|
|
656
|
+
* exists so an ordering does not need its column on screen. Built once in `ngOnInit` (columns
|
|
657
|
+
* are a declaration, not state) and labelled here because the bar knows nothing about columns.
|
|
658
|
+
*/
|
|
659
|
+
sortPickOptions: SortPickOption[];
|
|
660
|
+
/**
|
|
661
|
+
* The one body of an explicit sort GESTURE — the thing `persistActiveViewSort`'s doc calls
|
|
662
|
+
* "explicit gestures only": page one, meta on the grid, fetch, save, selection anchor
|
|
663
|
+
* invalidated, override recorded. Shared by the header's single-click un-sort, the header
|
|
664
|
+
* menu and both bar entry points; `broadcastSort` is its deliberate no-fetch sibling. The
|
|
665
|
+
* page reset, `saveState` and anchor line mirror PrimeNG's own `sort()` (`sortMultiple()`
|
|
666
|
+
* does none of the three); the "lands on page one" / "survives a reload" specs pin why.
|
|
667
|
+
*/
|
|
668
|
+
private applySort;
|
|
669
|
+
/** The picker's ask: one sort, replacing the whole meta — the same contract as the header menu. */
|
|
670
|
+
sortFromBar(pick: SortMeta): void;
|
|
671
|
+
/**
|
|
672
|
+
* Back to the resolved default — the view's declared sort, else the table default, else
|
|
673
|
+
* unsorted. Persisting right after is what empties the override slot: a sort equal to the
|
|
674
|
+
* default is stored as absence, so the declared ordering flows through again.
|
|
675
|
+
*/
|
|
676
|
+
resetSortFromBar(): void;
|
|
522
677
|
/** This column's share, override first, then its declared width, then the per-type default. */
|
|
523
678
|
columnShare(col: Column): number;
|
|
524
679
|
/**
|
|
@@ -579,6 +734,15 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
|
|
|
579
734
|
* make every table load twice.
|
|
580
735
|
*/
|
|
581
736
|
private requeryOnAppliedFilters;
|
|
737
|
+
/**
|
|
738
|
+
* Back to page one, state saved, one fetch off FRESH metadata — the shared tail of the
|
|
739
|
+
* requery effect and `selectView`'s sort-only switch. The save is load-bearing: PrimeNG
|
|
740
|
+
* saves state only from its own interactions, so without it the pre-change page offset
|
|
741
|
+
* survives in storage and the NEXT visit replays it against a narrowed result set — the
|
|
742
|
+
* backend answers data=[] for an offset the filtered total no longer reaches, and the grid
|
|
743
|
+
* renders empty under chips claiming matches.
|
|
744
|
+
*/
|
|
745
|
+
private refetchFromFirstPage;
|
|
582
746
|
reload(): void;
|
|
583
747
|
showActions(): boolean;
|
|
584
748
|
getStyleForBodyColumn(col: Column<any>): string;
|
|
@@ -590,11 +754,16 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
|
|
|
590
754
|
actionTrackByFn(index: any, item: Action): string;
|
|
591
755
|
exportListToExcel(): void;
|
|
592
756
|
/**
|
|
593
|
-
* Clears
|
|
594
|
-
*
|
|
595
|
-
*
|
|
757
|
+
* Clears the FILTERS — and nothing else (2026-09-06, grilled). It used to also reach the
|
|
758
|
+
* sort and PrimeNG's persisted state, which made "Clear filters" a reset the label never
|
|
759
|
+
* claimed: sort is its own channel with its own affordances (the bar's picker, the header's
|
|
760
|
+
* tri-state), mirroring Polaris, where onClearAll is wired to the Filters while
|
|
761
|
+
* sortSelected never passes through it. The store's requery effect owns the refetch and the
|
|
762
|
+
* persisted-snapshot update — the button only renders with chips on the bar, so the applied
|
|
763
|
+
* set always changes — and dropping table.clear() also drops the second, unsequenced
|
|
764
|
+
* request the 2026-09-05 review flagged on every Clear.
|
|
596
765
|
*/
|
|
597
|
-
clear(
|
|
766
|
+
clear(): void;
|
|
598
767
|
setFakeIsAllSelected(): void;
|
|
599
768
|
selectAll(checked: boolean): void;
|
|
600
769
|
/**
|
|
@@ -677,6 +846,15 @@ export interface TableView<TFilters = any> {
|
|
|
677
846
|
* transience is about the QUESTION, not the furniture.
|
|
678
847
|
*/
|
|
679
848
|
transient?: boolean;
|
|
849
|
+
/**
|
|
850
|
+
* The view's declared ordering — for the view whose question includes one ("oldest shipped
|
|
851
|
+
* first" IS the work list, not a reading preference). Entering the view applies it; the
|
|
852
|
+
* tri-state un-sort and Clear filters return to it instead of the table default; an operator's
|
|
853
|
+
* own sort overrides it, persisted per view on store tables (decision 10's sort half). A view
|
|
854
|
+
* declaring none keeps whatever ordering the grid already has, exactly as before this field
|
|
855
|
+
* existed.
|
|
856
|
+
*/
|
|
857
|
+
sort?: SortMeta[];
|
|
680
858
|
}
|
|
681
859
|
export declare class Column<T = any> {
|
|
682
860
|
name?: string;
|
|
@@ -703,9 +881,17 @@ export declare class Column<T = any> {
|
|
|
703
881
|
* only so details-table cols emitted by an OLDER Spiderly.SourceGenerators (which wrote
|
|
704
882
|
* `showMatchModes: true`) still compile against this library during a mixed-version window —
|
|
705
883
|
* 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.
|
|
884
|
+
* every consumer regenerates with a generator from this release or later (spiderly#407).
|
|
707
885
|
*/
|
|
708
886
|
showMatchModes?: boolean;
|
|
887
|
+
/**
|
|
888
|
+
* @deprecated The same version-skew bridge as {@link showMatchModes}, and nothing else: older
|
|
889
|
+
* generators emit `filterField: 'xxxId'` for every dropdown-typed details-table column, so
|
|
890
|
+
* deleting it breaks the mixed window the moment a consumer adds one. NOTHING reads it —
|
|
891
|
+
* filter ids live in the store, linked from a column via {@link filterId}. Delete with
|
|
892
|
+
* `showMatchModes` (spiderly#407).
|
|
893
|
+
*/
|
|
894
|
+
filterField?: string & keyof T;
|
|
709
895
|
dropdownOrMultiselectValues?: PrimengOption[];
|
|
710
896
|
actions?: Action[];
|
|
711
897
|
editable?: boolean;
|
|
@@ -742,12 +928,13 @@ export declare class Column<T = any> {
|
|
|
742
928
|
* Not applied to editable cells — those belong to their inline input.
|
|
743
929
|
*/
|
|
744
930
|
onCellClick?: (event: CellClickEvent) => void;
|
|
745
|
-
constructor({ name, field, filterType, filterId, showMatchModes, dropdownOrMultiselectValues, actions, editable, showTime, decimalPlaces, sortable, visible, lockVisible, width, onCellClick, }?: {
|
|
931
|
+
constructor({ name, field, filterType, filterId, showMatchModes, filterField, dropdownOrMultiselectValues, actions, editable, showTime, decimalPlaces, sortable, visible, lockVisible, width, onCellClick, }?: {
|
|
746
932
|
name?: string;
|
|
747
933
|
field?: string & keyof T;
|
|
748
934
|
filterType?: 'text' | 'date' | 'multiselect' | 'boolean' | 'numeric' | 'blob';
|
|
749
935
|
filterId?: string;
|
|
750
936
|
showMatchModes?: boolean;
|
|
937
|
+
filterField?: string & keyof T;
|
|
751
938
|
dropdownOrMultiselectValues?: PrimengOption[];
|
|
752
939
|
actions?: Action[];
|
|
753
940
|
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
|
|
8
|
-
* it without a cycle: the store's runtime validation and offered lists (`filter-store.ts`),
|
|
9
|
-
*
|
|
10
|
-
* hand-kept copies; edit the
|
|
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
|
|
@@ -155,12 +164,22 @@ export type FilterSnapshot = Record<string, {
|
|
|
155
164
|
value: unknown;
|
|
156
165
|
}>;
|
|
157
166
|
/**
|
|
158
|
-
* One
|
|
159
|
-
*
|
|
160
|
-
*
|
|
167
|
+
* One column the bar's sort picker offers, labelled by whoever owns the columns — the bar knows
|
|
168
|
+
* nothing about them. Hidden sortable columns belong in the list: the picker exists precisely so
|
|
169
|
+
* an ordering does not need its column on screen (decision 2b's sort half).
|
|
161
170
|
*/
|
|
162
|
-
export interface
|
|
171
|
+
export interface SortPickOption {
|
|
172
|
+
field: string;
|
|
163
173
|
label: string;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* One sort key as the bar draws it — a pick option WITH its direction. Sort deliberately does
|
|
177
|
+
* NOT live in the filter store: `multiSortMeta` already holds it, keyed by field, and a copy
|
|
178
|
+
* here would be a second source of truth for one fact. The bar is one surface reading two
|
|
179
|
+
* owners. `field` is what lets the picker mark the active row and flip its direction; the
|
|
180
|
+
* label alone could not say which option it names once two columns share a name.
|
|
181
|
+
*/
|
|
182
|
+
export interface SortKeyLabel extends SortPickOption {
|
|
164
183
|
descending: boolean;
|
|
165
184
|
}
|
|
166
185
|
/** One applied constraint, in the shape the chip bar draws. */
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { QueryList, TemplateRef } from '@angular/core';
|
|
2
|
+
import { SortMeta } from 'primeng/api';
|
|
2
3
|
import { SpiderlyFilterTemplateDirective } from '../directives/spiderly-filter-template.directive';
|
|
3
4
|
import { MatchModeCodes } from '../enums/match-mode-enum-codes';
|
|
4
|
-
import { AppliedFilter, FilterBarSource, FilterHandle, FilterOption, SortKeyLabel } from './filter-store';
|
|
5
|
+
import { AppliedFilter, FilterBarSource, FilterHandle, FilterOption, SortKeyLabel, SortPickOption } from './filter-store';
|
|
5
6
|
import * as i0 from "@angular/core";
|
|
6
7
|
/**
|
|
7
8
|
* Lowercased and stripped of diacritics for matching. NFD decomposition handles č/ć/š/ž, but NOT
|
|
@@ -33,8 +34,20 @@ export declare class SpiderlyFilterBarComponent {
|
|
|
33
34
|
* about columns, so whoever owns the sort resolves the names.
|
|
34
35
|
*/
|
|
35
36
|
readonly sort: import("@angular/core").InputSignal<SortKeyLabel[]>;
|
|
37
|
+
/**
|
|
38
|
+
* What the picker offers, labelled by the owner — every sortable column, HIDDEN ones
|
|
39
|
+
* included (their ordering is otherwise unreachable, which is the trap the picker closes).
|
|
40
|
+
* Empty keeps the chip read-only and the picker off.
|
|
41
|
+
*/
|
|
42
|
+
readonly sortOptions: import("@angular/core").InputSignal<SortPickOption[]>;
|
|
43
|
+
/** Whether the grid already sits on its default ordering — gates the reset row, nothing else. */
|
|
44
|
+
readonly sortIsDefault: import("@angular/core").InputSignal<boolean>;
|
|
36
45
|
/** Asked for, never done here — see the button's comment. */
|
|
37
46
|
readonly clearAll: import("@angular/core").OutputEmitterRef<void>;
|
|
47
|
+
/** A single-sort ask. Multi-sort stays a header gesture (ctrl-click); the picker replaces. */
|
|
48
|
+
readonly sortPick: import("@angular/core").OutputEmitterRef<SortMeta>;
|
|
49
|
+
/** Back to the owner's default ordering — the owner knows what that resolves to, not the bar. */
|
|
50
|
+
readonly sortReset: import("@angular/core").OutputEmitterRef<void>;
|
|
38
51
|
private readonly transloco;
|
|
39
52
|
/** For the chip's date wording — the same locale mechanism the table's cells format with. */
|
|
40
53
|
private readonly locale;
|
|
@@ -45,6 +58,7 @@ export declare class SpiderlyFilterBarComponent {
|
|
|
45
58
|
*/
|
|
46
59
|
readonly controlTemplates: import("@angular/core").InputSignal<QueryList<SpiderlyFilterTemplateDirective>>;
|
|
47
60
|
readonly isAddOpen: import("@angular/core").WritableSignal<boolean>;
|
|
61
|
+
readonly isSortOpen: import("@angular/core").WritableSignal<boolean>;
|
|
48
62
|
/** Cleared when the popover closes, so reopening never starts inside someone's old query. */
|
|
49
63
|
readonly addSearch: import("@angular/core").WritableSignal<string>;
|
|
50
64
|
/** The filter whose control is open. One at a time: the bar edits, it is not a form. */
|
|
@@ -81,6 +95,23 @@ export declare class SpiderlyFilterBarComponent {
|
|
|
81
95
|
}[]>;
|
|
82
96
|
/** Opens a filter's control. Where it renders, and why not a popover: the template comment. */
|
|
83
97
|
startEditing(id: string): void;
|
|
98
|
+
/**
|
|
99
|
+
* The menu's rows: each option decorated with its ACTIVE direction's arrow (any key, not
|
|
100
|
+
* just the primary), or null. A `computed`, not a per-row method call in the binding — the
|
|
101
|
+
* popover's content is projected, so it change-detects even while closed, and a `find` per
|
|
102
|
+
* option per pass is exactly what `operatorChoices` documents against.
|
|
103
|
+
*/
|
|
104
|
+
readonly sortRows: import("@angular/core").Signal<{
|
|
105
|
+
arrow: string;
|
|
106
|
+
field: string;
|
|
107
|
+
label: string;
|
|
108
|
+
}[]>;
|
|
109
|
+
/**
|
|
110
|
+
* Ascending first; picking the already-ascending primary flips it. The direction is decided
|
|
111
|
+
* HERE so the owner applies exactly what the menu showed — an owner guessing "toggle" from its
|
|
112
|
+
* own state could disagree with a stale menu after a concurrent header click.
|
|
113
|
+
*/
|
|
114
|
+
pickSort(option: SortPickOption): void;
|
|
84
115
|
/** The raw-string controls only. Nothing reaches the bar or the query until `apply`. */
|
|
85
116
|
draft(handle: FilterHandle, event: Event): void;
|
|
86
117
|
controlTemplateFor(handle: FilterHandle): TemplateRef<unknown> | null;
|
|
@@ -128,5 +159,5 @@ export declare class SpiderlyFilterBarComponent {
|
|
|
128
159
|
*/
|
|
129
160
|
chipValue(chip: AppliedFilter): string;
|
|
130
161
|
static ɵfac: i0.ɵɵFactoryDeclaration<SpiderlyFilterBarComponent, never>;
|
|
131
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<SpiderlyFilterBarComponent, "spiderly-filter-bar", never, { "filters": { "alias": "filters"; "required": true; "isSignal": true; }; "totalRecords": { "alias": "totalRecords"; "required": false; "isSignal": true; }; "sort": { "alias": "sort"; "required": false; "isSignal": true; }; "controlTemplates": { "alias": "controlTemplates"; "required": false; "isSignal": true; }; }, { "clearAll": "clearAll"; }, never, never, true, never>;
|
|
162
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SpiderlyFilterBarComponent, "spiderly-filter-bar", never, { "filters": { "alias": "filters"; "required": true; "isSignal": true; }; "totalRecords": { "alias": "totalRecords"; "required": false; "isSignal": true; }; "sort": { "alias": "sort"; "required": false; "isSignal": true; }; "sortOptions": { "alias": "sortOptions"; "required": false; "isSignal": true; }; "sortIsDefault": { "alias": "sortIsDefault"; "required": false; "isSignal": true; }; "controlTemplates": { "alias": "controlTemplates"; "required": false; "isSignal": true; }; }, { "clearAll": "clearAll"; "sortPick": "sortPick"; "sortReset": "sortReset"; }, never, never, true, never>;
|
|
132
163
|
}
|
package/package.json
CHANGED
|
@@ -147,12 +147,49 @@
|
|
|
147
147
|
font-size: 0.875rem;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
// The picker's trigger — same chip, now a button. bare-button resets the UA chrome; the hover
|
|
151
|
+
// underline mirrors the filter chips' edit affordance so "this opens something" reads the same
|
|
152
|
+
// across the bar.
|
|
153
|
+
button.sort-chip {
|
|
154
|
+
@include bare-button();
|
|
155
|
+
cursor: pointer;
|
|
156
|
+
|
|
157
|
+
&:hover .sort-chip-key,
|
|
158
|
+
&:hover .sort-chip-label {
|
|
159
|
+
text-decoration: underline;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.sort-chip-caret {
|
|
164
|
+
font-size: 0.65rem;
|
|
165
|
+
}
|
|
166
|
+
|
|
150
167
|
.sort-chip-key {
|
|
151
168
|
// The direction arrow is the non-colour channel; the priority is the reading order, which is
|
|
152
169
|
// the thing the scattered header icons could never show.
|
|
153
170
|
color: var(--p-text-color);
|
|
154
171
|
}
|
|
155
172
|
|
|
173
|
+
// Teleported with its popover into document.body, so TOP LEVEL like the add menu above it
|
|
174
|
+
// (Angular/CLAUDE.md → overlay styling); pinned by the "sort menu styles survive the body
|
|
175
|
+
// teleport" spec. Inherits .filter-add-option for the row look.
|
|
176
|
+
.sort-menu-option {
|
|
177
|
+
display: flex;
|
|
178
|
+
justify-content: space-between;
|
|
179
|
+
align-items: center;
|
|
180
|
+
gap: 12px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.sort-menu-direction {
|
|
184
|
+
color: var(--p-primary-color);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.sort-menu-reset {
|
|
188
|
+
border-top: 1px solid var(--p-content-border-color);
|
|
189
|
+
border-radius: 0;
|
|
190
|
+
color: var(--p-text-muted-color);
|
|
191
|
+
}
|
|
192
|
+
|
|
156
193
|
.filter-bar-clear {
|
|
157
194
|
// Pushed to the far end, away from the chips: a destructive action next to the x it resembles
|
|
158
195
|
// is how a single chip gets removed by someone reaching for all of them. It is also the LAST
|