spiderly 19.12.1 → 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 +3 -3
- package/fesm2022/spiderly.mjs +321 -89
- package/fesm2022/spiderly.mjs.map +1 -1
- package/lib/components/spiderly-data-table/spiderly-data-table.component.d.ts +62 -19
- package/lib/filters/filter-store.d.ts +14 -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
|
@@ -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';
|
|
@@ -416,23 +416,32 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
|
|
|
416
416
|
private get viewSortStateKey();
|
|
417
417
|
/** The operator's stored sort for the active view, sanitized like any persisted sort. */
|
|
418
418
|
private persistedViewSort;
|
|
419
|
-
/**
|
|
420
|
-
|
|
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;
|
|
421
427
|
/**
|
|
422
428
|
* Records the current sort as the active view's override — called from the EXPLICIT sort
|
|
423
|
-
* gestures only (header click,
|
|
424
|
-
* inherited at init must not crystallize into an override, or a
|
|
425
|
-
* declared sort would stop flowing to operators who never
|
|
426
|
-
* flows-through rule `columnVisibilityOverrides` documents. A sort
|
|
427
|
-
* default is stored as ABSENCE for the same reason.
|
|
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.
|
|
428
434
|
*/
|
|
429
435
|
private persistActiveViewSort;
|
|
430
436
|
/**
|
|
431
|
-
* Puts the entering view's sort on the grid — stored override
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
*
|
|
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.
|
|
436
445
|
*/
|
|
437
446
|
private applyViewSort;
|
|
438
447
|
/**
|
|
@@ -538,8 +547,8 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
|
|
|
538
547
|
/**
|
|
539
548
|
* Switches to a view. Clears first, always: a view is a STATE rather than an addition, and two
|
|
540
549
|
* of them composed produce a question nobody asked. The clear runs through the store directly
|
|
541
|
-
* rather than through `clear(
|
|
542
|
-
* view
|
|
550
|
+
* rather than through `clear()`, whose requery effect would spend a fetch before the entering
|
|
551
|
+
* view's filters are in place.
|
|
543
552
|
*/
|
|
544
553
|
selectView(view: TableView): void;
|
|
545
554
|
/**
|
|
@@ -636,6 +645,35 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
|
|
|
636
645
|
* left falls back to its own name rather than vanishing.
|
|
637
646
|
*/
|
|
638
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;
|
|
639
677
|
/** This column's share, override first, then its declared width, then the per-type default. */
|
|
640
678
|
columnShare(col: Column): number;
|
|
641
679
|
/**
|
|
@@ -716,11 +754,16 @@ export declare class SpiderlyDataTableComponent implements OnInit, OnChanges, Af
|
|
|
716
754
|
actionTrackByFn(index: any, item: Action): string;
|
|
717
755
|
exportListToExcel(): void;
|
|
718
756
|
/**
|
|
719
|
-
* Clears
|
|
720
|
-
*
|
|
721
|
-
*
|
|
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.
|
|
722
765
|
*/
|
|
723
|
-
clear(
|
|
766
|
+
clear(): void;
|
|
724
767
|
setFakeIsAllSelected(): void;
|
|
725
768
|
selectAll(checked: boolean): void;
|
|
726
769
|
/**
|
|
@@ -164,12 +164,22 @@ export type FilterSnapshot = Record<string, {
|
|
|
164
164
|
value: unknown;
|
|
165
165
|
}>;
|
|
166
166
|
/**
|
|
167
|
-
* One
|
|
168
|
-
*
|
|
169
|
-
*
|
|
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).
|
|
170
170
|
*/
|
|
171
|
-
export interface
|
|
171
|
+
export interface SortPickOption {
|
|
172
|
+
field: string;
|
|
172
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 {
|
|
173
183
|
descending: boolean;
|
|
174
184
|
}
|
|
175
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
|