@mintplayer/ng-spark 22.4.0 → 22.6.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/fesm2022/mintplayer-ng-spark-client-operations.mjs +12 -12
- package/fesm2022/mintplayer-ng-spark-grid.mjs +21 -21
- package/fesm2022/mintplayer-ng-spark-icon.mjs +3 -3
- package/fesm2022/mintplayer-ng-spark-models.mjs +185 -1
- package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-pipes.mjs +82 -70
- package/fesm2022/mintplayer-ng-spark-pipes.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-create.mjs +4 -4
- package/fesm2022/mintplayer-ng-spark-po-create.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-detail.mjs +3 -3
- package/fesm2022/mintplayer-ng-spark-po-edit.mjs +4 -4
- package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-form.mjs +356 -18
- package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-query-list.mjs +3 -3
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +4 -4
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-services.mjs +27 -15
- package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-shell.mjs +379 -0
- package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark.mjs +17 -1
- package/fesm2022/mintplayer-ng-spark.mjs.map +1 -1
- package/package.json +5 -1
- package/types/mintplayer-ng-spark-models.d.ts +106 -2
- package/types/mintplayer-ng-spark-pipes.d.ts +9 -1
- package/types/mintplayer-ng-spark-po-form.d.ts +132 -4
- package/types/mintplayer-ng-spark-services.d.ts +10 -0
- package/types/mintplayer-ng-spark-shell.d.ts +242 -0
- package/types/mintplayer-ng-spark.d.ts +17 -2
|
@@ -144,6 +144,13 @@ interface EntityAttributeDefinition {
|
|
|
144
144
|
referenceDisplayType?: EReferenceDisplayType;
|
|
145
145
|
/** For array AsDetail attributes: when true, rows can be drag-reordered (order = array position) */
|
|
146
146
|
isSortable?: boolean;
|
|
147
|
+
/**
|
|
148
|
+
* When true, changing this attribute's value posts the in-progress object to
|
|
149
|
+
* `/spark/po/{objectTypeId}/refresh` and applies the reshaped result as an overlay.
|
|
150
|
+
* Schema-only by design — it never travels on a PersistentObjectAttribute, so a client
|
|
151
|
+
* cannot claim a trigger the model did not declare.
|
|
152
|
+
*/
|
|
153
|
+
triggersRefresh?: boolean;
|
|
147
154
|
/** For LookupReference attributes, specifies the lookup reference type name */
|
|
148
155
|
lookupReferenceType?: string;
|
|
149
156
|
/**
|
|
@@ -234,9 +241,21 @@ interface ProgramUnit {
|
|
|
234
241
|
id: string;
|
|
235
242
|
name: TranslatedString;
|
|
236
243
|
icon?: string;
|
|
244
|
+
/**
|
|
245
|
+
* Canonical unit type, exact-cased by the server's loader: 'query' | 'persistentObject' | 'url'.
|
|
246
|
+
* Kept as string rather than a union so an older client tolerates a newer server.
|
|
247
|
+
*/
|
|
237
248
|
type: string;
|
|
238
249
|
queryId?: string;
|
|
239
250
|
persistentObjectId?: string;
|
|
251
|
+
/**
|
|
252
|
+
* For a persistentObject unit: the specific object to open — the menu entry deep-links to
|
|
253
|
+
* `/po/{type}/{objectId}`. Absent means the type's default list. For a composed page this is
|
|
254
|
+
* whatever stable string the app declared; the server's Actions class may ignore it.
|
|
255
|
+
*/
|
|
256
|
+
objectId?: string;
|
|
257
|
+
/** For a url unit: the external address, rendered as a plain anchor (never a router link). */
|
|
258
|
+
url?: string;
|
|
240
259
|
order: number;
|
|
241
260
|
alias?: string;
|
|
242
261
|
}
|
|
@@ -446,5 +465,90 @@ type SparkSelectionMode = 'none' | 'single' | 'multiple';
|
|
|
446
465
|
*/
|
|
447
466
|
declare function selectionModeFor(actions: CustomActionDefinition[]): SparkSelectionMode;
|
|
448
467
|
|
|
449
|
-
|
|
450
|
-
|
|
468
|
+
/**
|
|
469
|
+
* One selectable value, as replaced by a refresh hook. Mirrors the server's
|
|
470
|
+
* `PersistentObjectAttributeOption`.
|
|
471
|
+
*/
|
|
472
|
+
interface RefreshedOption {
|
|
473
|
+
key: string;
|
|
474
|
+
label?: Record<string, string>;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* What a refresh changed about one attribute's *presentation*.
|
|
478
|
+
*
|
|
479
|
+
* Kept separate from `EntityType` on purpose. The form's option loading hangs off a single effect
|
|
480
|
+
* keyed on `entityType` identity, and `SparkService` caches nothing — so applying a refresh by
|
|
481
|
+
* setting a new `EntityType` re-issues every reference query, every lookup fetch, a full
|
|
482
|
+
* `getEntityTypes()` and a `getPermissions()` per array-AsDetail attribute, on every refresh.
|
|
483
|
+
* Mutating the existing object instead is inert, because the rendering computed would not re-run.
|
|
484
|
+
* An overlay is the only shape that is both reactive and free.
|
|
485
|
+
*/
|
|
486
|
+
interface AttributeOverlay {
|
|
487
|
+
isRequired?: boolean;
|
|
488
|
+
isReadOnly?: boolean;
|
|
489
|
+
isVisible?: boolean;
|
|
490
|
+
rules?: ValidationRule[];
|
|
491
|
+
query?: string;
|
|
492
|
+
/** `undefined` means the hook did not touch the options; an empty array means there are none. */
|
|
493
|
+
options?: RefreshedOption[];
|
|
494
|
+
}
|
|
495
|
+
type RefreshOverlay = Record<string, AttributeOverlay>;
|
|
496
|
+
/** Applies an overlay to one attribute definition, returning a new object when anything changed. */
|
|
497
|
+
declare function applyOverlay(attr: EntityAttributeDefinition, overlay: AttributeOverlay | undefined): EntityAttributeDefinition;
|
|
498
|
+
/**
|
|
499
|
+
* Reads a refresh response into an overlay.
|
|
500
|
+
*
|
|
501
|
+
* Everything here is presentation the server owns outright, so it is taken verbatim — there is no
|
|
502
|
+
* merging to do on this half, only on values.
|
|
503
|
+
*/
|
|
504
|
+
declare function overlayFromResponse(response: PersistentObject): RefreshOverlay;
|
|
505
|
+
/**
|
|
506
|
+
* Merges a refresh response's values into the live form.
|
|
507
|
+
*
|
|
508
|
+
* The rule, and the reason for it: a refresh is not instant, and the user keeps typing during it —
|
|
509
|
+
* the form is deliberately never frozen. So for each attribute we ask whether the *server* changed
|
|
510
|
+
* it, by comparing the response against the values that were **sent**, not against what is on
|
|
511
|
+
* screen now.
|
|
512
|
+
*
|
|
513
|
+
* - server value equals what we sent → the hook did not touch it, so whatever is in the form now
|
|
514
|
+
* wins, including anything typed while the request was in flight;
|
|
515
|
+
* - server value differs → the hook deliberately changed it, and it wins over a concurrent edit.
|
|
516
|
+
*
|
|
517
|
+
* Comparing against the displayed value instead is the classic "refresh eats my typing" bug;
|
|
518
|
+
* refusing to overwrite anything the user touched is the equally wrong opposite, where a dependent
|
|
519
|
+
* field the hook computed never appears.
|
|
520
|
+
*
|
|
521
|
+
* @param sent values as they were POSTed, captured before the request left
|
|
522
|
+
* @param current values as they are now, which may have moved on
|
|
523
|
+
* @param response the reshaped object
|
|
524
|
+
*/
|
|
525
|
+
declare function mergeRefreshValues(sent: Record<string, any>, current: Record<string, any>, response: PersistentObject): Record<string, any>;
|
|
526
|
+
|
|
527
|
+
/** One rule failure, in the shape the form already renders per field. */
|
|
528
|
+
interface RuleFailure {
|
|
529
|
+
attributeName: string;
|
|
530
|
+
ruleType: string;
|
|
531
|
+
message: string;
|
|
532
|
+
}
|
|
533
|
+
interface EvaluableAttribute {
|
|
534
|
+
name: string;
|
|
535
|
+
label?: TranslatedString;
|
|
536
|
+
isRequired?: boolean;
|
|
537
|
+
rules?: ValidationRule[];
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Evaluates an attribute's rules against a value, mirroring the server's `ValidationService`.
|
|
541
|
+
*
|
|
542
|
+
* ⚠️ **Parity with the server is the point, and disagreement is worse than silence.** A client that
|
|
543
|
+
* rejects something the server would accept blocks legitimate work with no recourse; one that
|
|
544
|
+
* accepts something the server rejects merely defers the error to the round-trip, which is where it
|
|
545
|
+
* used to live anyway. So the rule set here is deliberately limited to the types the server
|
|
546
|
+
* implements, and an unrecognised rule type is ignored rather than guessed at.
|
|
547
|
+
*
|
|
548
|
+
* This exists because a refresh hook that imposes a rule needs it to bite before Save — previously
|
|
549
|
+
* `rules` was carried on the wire and never evaluated in the browser at all.
|
|
550
|
+
*/
|
|
551
|
+
declare function evaluateRules(attr: EvaluableAttribute, value: any): RuleFailure[];
|
|
552
|
+
|
|
553
|
+
export { AS_DETAIL_BREADCRUMBS_KEY, AS_DETAIL_SELF_BREADCRUMB_KEY, ELookupDisplayType, EReferenceDisplayType, ShowedOn, applyOverlay, currentLanguage, dictToNestedPo, evaluateRules, filterQueryActions, hasShowedOnFlag, mergeRefreshValues, nestedPoToDict, nestedPoToDisplayRow, overlayFromResponse, parseSelectionRule, resolveTranslation, selectionModeFor, selfBreadcrumb };
|
|
554
|
+
export type { AttributeGroup, AttributeOverlay, AttributeTab, CustomActionDefinition, EntityAttributeDefinition, EntityPermissions, EntityType, EntityTypeResolver, EvaluableAttribute, LookupReference, LookupReferenceListItem, LookupReferenceValue, PersistentObject, PersistentObjectAttribute, PersistentObjectPermissions, ProgramUnit, ProgramUnitGroup, ProgramUnitsConfiguration, QueryResult, RefreshOverlay, RefreshedOption, RetryActionPayload, RetryActionResult, RuleFailure, SparkQuery, SparkQueryRenderMode, SparkQuerySortColumn, SparkSelectionMode, StreamingErrorMessage, StreamingMessage, StreamingPatchItem, StreamingPatchMessage, StreamingSnapshotMessage, TranslatedString, ValidationError, ValidationErrorResponse, ValidationRule };
|
|
@@ -70,8 +70,16 @@ declare class ReferenceChipsPipe implements PipeTransform {
|
|
|
70
70
|
static ɵpipe: i0.ɵɵPipeDeclaration<ReferenceChipsPipe, "referenceChips", true>;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Maps a program unit to the router commands that open it. Type comparisons are exact: the
|
|
75
|
+
* server's loader canonicalizes `type` casing, so tolerating variants here would only mask a
|
|
76
|
+
* server that stopped doing so.
|
|
77
|
+
*
|
|
78
|
+
* Returns null for a `url` unit — an external address is an `<a href>`, not a router link; the
|
|
79
|
+
* component rendering the menu owns that branch.
|
|
80
|
+
*/
|
|
73
81
|
declare class RouterLinkPipe implements PipeTransform {
|
|
74
|
-
transform(unit: ProgramUnit): string[];
|
|
82
|
+
transform(unit: ProgramUnit): string[] | null;
|
|
75
83
|
static ɵfac: i0.ɵɵFactoryDeclaration<RouterLinkPipe, never>;
|
|
76
84
|
static ɵpipe: i0.ɵɵPipeDeclaration<RouterLinkPipe, "routerLink", true>;
|
|
77
85
|
}
|
|
@@ -4,10 +4,58 @@ import { CdkDragDrop } from '@angular/cdk/drag-drop';
|
|
|
4
4
|
import { Color } from '@mintplayer/ng-bootstrap';
|
|
5
5
|
import { InMemoryTreeSelectProvider, TreeNode } from '@mintplayer/ng-bootstrap/tree-select';
|
|
6
6
|
import * as _mintplayer_ng_spark_models from '@mintplayer/ng-spark/models';
|
|
7
|
-
import { EntityType, ValidationError,
|
|
7
|
+
import { PersistentObject, EntityType, ValidationError, RefreshOverlay, LookupReference, EntityAttributeDefinition, EntityPermissions, ELookupDisplayType, EReferenceDisplayType, AttributeTab, AttributeGroup, LookupReferenceValue, RuleFailure } from '@mintplayer/ng-spark/models';
|
|
8
8
|
import { DatatableSettings } from '@mintplayer/ng-bootstrap/datatable';
|
|
9
9
|
import { PaginationResponse } from '@mintplayer/pagination';
|
|
10
10
|
|
|
11
|
+
/** What the coordinator needs from its host, so it can be tested without mounting a form. */
|
|
12
|
+
interface RefreshCoordinatorHost {
|
|
13
|
+
/** POSTs the object and resolves with the reshaped one. */
|
|
14
|
+
send(triggeredBy: string): Promise<PersistentObject>;
|
|
15
|
+
/** Values as they are right now — read at dispatch time to snapshot what is being sent. */
|
|
16
|
+
currentValues(): Record<string, any>;
|
|
17
|
+
/** Applies a settled response. Not called for a superseded one. */
|
|
18
|
+
apply(response: PersistentObject, sent: Record<string, any>): void;
|
|
19
|
+
/** Surfaced so the host can show a busy affordance. Never used to disable fields. */
|
|
20
|
+
setBusy(busy: boolean): void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Serializes refreshes for **one** form instance and drops superseded ones.
|
|
24
|
+
*
|
|
25
|
+
* Per-instance rather than a service, deliberately. The retry-action modal renders its own
|
|
26
|
+
* `spark-po-form`, and a refresh can carry a retry operation — so a refresh can open a modal
|
|
27
|
+
* containing a form whose own attributes may trigger refreshes. A shared coordinator would let the
|
|
28
|
+
* nested form resolve or supersede the outer form's pending request. The same applies to the
|
|
29
|
+
* recursive `spark-po-form` used for modal AsDetail editing.
|
|
30
|
+
*
|
|
31
|
+
* Cancellation is not available: the service layer is promise-based (`firstValueFrom`), so a stale
|
|
32
|
+
* response *will* arrive. It is discarded by sequence number rather than prevented.
|
|
33
|
+
*/
|
|
34
|
+
declare class RefreshCoordinator {
|
|
35
|
+
private readonly host;
|
|
36
|
+
private queue;
|
|
37
|
+
private sequence;
|
|
38
|
+
private settled;
|
|
39
|
+
private pending;
|
|
40
|
+
constructor(host: RefreshCoordinatorHost);
|
|
41
|
+
/** Whether a refresh is in flight. */
|
|
42
|
+
get isRefreshing(): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Marks `attributeName` as needing a refresh without sending one — for free-text editors, which
|
|
45
|
+
* would otherwise issue a request per keystroke. Flushed by {@link blur} or {@link flush}.
|
|
46
|
+
*/
|
|
47
|
+
markPending(attributeName: string): void;
|
|
48
|
+
/** Sends a pending refresh for `attributeName`, if one was marked. */
|
|
49
|
+
blur(attributeName: string): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Sends every refresh still marked pending. Called before save, so a value typed and never blurred
|
|
52
|
+
* — the user tabbing straight to the save button — is still reflected before the object goes.
|
|
53
|
+
*/
|
|
54
|
+
flush(): Promise<void>;
|
|
55
|
+
/** Sends a refresh immediately — discrete editors, where every change is a committed one. */
|
|
56
|
+
trigger(attributeName: string): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
|
|
11
59
|
declare class SparkPoFormComponent {
|
|
12
60
|
private readonly sparkService;
|
|
13
61
|
private readonly translations;
|
|
@@ -21,6 +69,23 @@ declare class SparkPoFormComponent {
|
|
|
21
69
|
parentType: _angular_core.InputSignal<string | undefined>;
|
|
22
70
|
save: _angular_core.OutputEmitterRef<void>;
|
|
23
71
|
cancel: _angular_core.OutputEmitterRef<void>;
|
|
72
|
+
/**
|
|
73
|
+
* The type id to refresh against. Absent means refresh is unavailable — the form still renders and
|
|
74
|
+
* edits normally, so a host that has not opted in loses nothing.
|
|
75
|
+
*/
|
|
76
|
+
objectTypeId: _angular_core.InputSignal<string | undefined>;
|
|
77
|
+
/** The id of the object being edited; absent for a create. */
|
|
78
|
+
objectId: _angular_core.InputSignal<string | undefined>;
|
|
79
|
+
/**
|
|
80
|
+
* What the last refresh changed about each attribute's presentation, keyed by attribute name.
|
|
81
|
+
*
|
|
82
|
+
* Deliberately NOT folded back into `entityType`. All option loading hangs off one effect keyed on
|
|
83
|
+
* `entityType` identity and `SparkService` caches nothing, so re-setting it would re-issue every
|
|
84
|
+
* reference query and lookup fetch on every refresh; mutating it in place would not re-render at
|
|
85
|
+
* all.
|
|
86
|
+
*/
|
|
87
|
+
refreshOverlay: _angular_core.WritableSignal<RefreshOverlay>;
|
|
88
|
+
isRefreshing: _angular_core.WritableSignal<boolean>;
|
|
24
89
|
colors: typeof Color;
|
|
25
90
|
referenceOptions: _angular_core.WritableSignal<Record<string, PersistentObject[]>>;
|
|
26
91
|
referenceProviders: _angular_core.WritableSignal<Record<string, InMemoryTreeSelectProvider>>;
|
|
@@ -35,6 +100,15 @@ declare class SparkPoFormComponent {
|
|
|
35
100
|
asDetailReferenceOptions: _angular_core.WritableSignal<Record<string, Record<string, PersistentObject[]>>>;
|
|
36
101
|
ELookupDisplayType: typeof ELookupDisplayType;
|
|
37
102
|
EReferenceDisplayType: typeof EReferenceDisplayType;
|
|
103
|
+
/**
|
|
104
|
+
* Every attribute this form could ever need option data for — including ones the model hides,
|
|
105
|
+
* because a refresh may reveal them.
|
|
106
|
+
*
|
|
107
|
+
* Read by the option-loading effect, and deliberately independent of `refreshOverlay`: the loaders
|
|
108
|
+
* read this synchronously, so an overlay dependency here would make every refresh re-issue every
|
|
109
|
+
* reference query and lookup fetch. That is the whole reason the overlay is a separate signal.
|
|
110
|
+
*/
|
|
111
|
+
optionSourceAttributes: _angular_core.Signal<EntityAttributeDefinition[]>;
|
|
38
112
|
editableAttributes: _angular_core.Signal<EntityAttributeDefinition[]>;
|
|
39
113
|
private static readonly DEFAULT_TAB;
|
|
40
114
|
ungroupedAttributes: _angular_core.Signal<EntityAttributeDefinition[]>;
|
|
@@ -66,12 +140,66 @@ declare class SparkPoFormComponent {
|
|
|
66
140
|
/** Edit-renderer for an inline AsDetail cell (so inline editing honors `col.renderer`, not just display). */
|
|
67
141
|
getAsDetailCellEditRenderer(col: EntityAttributeDefinition): Type<any> | null;
|
|
68
142
|
getAsDetailCellEditRendererInputs(component: Type<any>, row: Record<string, any>, col: EntityAttributeDefinition): Record<string, any>;
|
|
143
|
+
/**
|
|
144
|
+
* Rules evaluated in the browser, against the *effective* metadata — so a rule a refresh hook
|
|
145
|
+
* imposed is visible before the round-trip rather than only after the server rejects the save.
|
|
146
|
+
*/
|
|
147
|
+
clientRuleFailures: _angular_core.Signal<RuleFailure[]>;
|
|
69
148
|
hasError(attrName: string): boolean;
|
|
70
149
|
private inlineErrorPath;
|
|
71
150
|
hasInlineError(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): boolean;
|
|
72
151
|
inlineErrorMessage(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): string | null;
|
|
73
|
-
|
|
74
|
-
|
|
152
|
+
/**
|
|
153
|
+
* The single funnel every scalar / boolean / inline-cell edit passes through.
|
|
154
|
+
*
|
|
155
|
+
* `attr` is optional only so the AsDetail modal's recursive form, which has no trigger context,
|
|
156
|
+
* can still call it. A caller that knows which attribute changed should always say so — without it
|
|
157
|
+
* no refresh can fire.
|
|
158
|
+
*/
|
|
159
|
+
onFieldChange(attr?: EntityAttributeDefinition): void;
|
|
160
|
+
/**
|
|
161
|
+
* A trigger inside an AsDetail row. Addressed by the same `{attr}[{index}].{col}` path the inline
|
|
162
|
+
* validation errors already use, so the server can tell which row asked without a second
|
|
163
|
+
* addressing scheme being invented for it.
|
|
164
|
+
*/
|
|
165
|
+
onInlineCellChange(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): void;
|
|
166
|
+
/** Which detail row the in-flight refresh belongs to, if any. */
|
|
167
|
+
private pendingNestedTrigger;
|
|
168
|
+
/**
|
|
169
|
+
* Applies a refresh that ran against a detail row: the row's own values, and the column metadata
|
|
170
|
+
* for the grid it lives in.
|
|
171
|
+
*
|
|
172
|
+
* The column metadata comes from `asDetailTypes` — a different signal from `entityType` — which is
|
|
173
|
+
* why a nested response cannot go through the top-level overlay.
|
|
174
|
+
*
|
|
175
|
+
* ⚠️ The row array is mutated in place rather than replaced. Rows are tracked by index, so handing
|
|
176
|
+
* the template a new array destroys and rebuilds every row's DOM and takes focus with it, mid-edit.
|
|
177
|
+
*/
|
|
178
|
+
private applyNestedResponse;
|
|
179
|
+
onInlineCellBlur(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): void;
|
|
180
|
+
/** Blur handler for free-text editors — sends the refresh their keystrokes only marked pending. */
|
|
181
|
+
onFieldBlur(attr: EntityAttributeDefinition): void;
|
|
182
|
+
private noteChange;
|
|
183
|
+
private canRefresh;
|
|
184
|
+
/**
|
|
185
|
+
* Per-instance, never a service: the retry-action modal renders its own `spark-po-form`, and a
|
|
186
|
+
* refresh may carry a retry operation — so a refresh can open a modal containing a form that
|
|
187
|
+
* refreshes. A shared coordinator would let the nested form supersede this one's request.
|
|
188
|
+
*/
|
|
189
|
+
protected readonly refreshCoordinator: RefreshCoordinator;
|
|
190
|
+
private buildRefreshPayload;
|
|
191
|
+
/**
|
|
192
|
+
* Folds replaced option lists into the signals the editors already read, so a refreshed dropdown
|
|
193
|
+
* renders through the same path as a loaded one.
|
|
194
|
+
*
|
|
195
|
+
* `undefined` means the hook did not touch this attribute's options and the loaded set stands; an
|
|
196
|
+
* empty array means it deliberately left none. Collapsing the two would blank every dropdown the
|
|
197
|
+
* hook never mentioned.
|
|
198
|
+
*/
|
|
199
|
+
private applyRefreshedOptions;
|
|
200
|
+
/** Sends anything still pending, so a typed-but-never-blurred trigger is reflected before save. */
|
|
201
|
+
flushPendingRefresh(): Promise<void>;
|
|
202
|
+
onSave(): Promise<void>;
|
|
75
203
|
onCancel(): void;
|
|
76
204
|
openAsDetailEditor(attr: EntityAttributeDefinition): void;
|
|
77
205
|
saveAsDetailObject(): void;
|
|
@@ -82,7 +210,7 @@ declare class SparkPoFormComponent {
|
|
|
82
210
|
removeArrayItem(attr: EntityAttributeDefinition, index: number): void;
|
|
83
211
|
onAsDetailReorder(attr: EntityAttributeDefinition, event: CdkDragDrop<Record<string, any>[]>): void;
|
|
84
212
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkPoFormComponent, never>;
|
|
85
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkPoFormComponent, "spark-po-form", never, { "entityType": { "alias": "entityType"; "required": false; "isSignal": true; }; "formData": { "alias": "formData"; "required": false; "isSignal": true; }; "validationErrors": { "alias": "validationErrors"; "required": false; "isSignal": true; }; "showButtons": { "alias": "showButtons"; "required": false; "isSignal": true; }; "isSaving": { "alias": "isSaving"; "required": false; "isSignal": true; }; "parentId": { "alias": "parentId"; "required": false; "isSignal": true; }; "parentType": { "alias": "parentType"; "required": false; "isSignal": true; }; }, { "formData": "formDataChange"; "save": "save"; "cancel": "cancel"; }, never, never, true, never>;
|
|
213
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkPoFormComponent, "spark-po-form", never, { "entityType": { "alias": "entityType"; "required": false; "isSignal": true; }; "formData": { "alias": "formData"; "required": false; "isSignal": true; }; "validationErrors": { "alias": "validationErrors"; "required": false; "isSignal": true; }; "showButtons": { "alias": "showButtons"; "required": false; "isSignal": true; }; "isSaving": { "alias": "isSaving"; "required": false; "isSignal": true; }; "parentId": { "alias": "parentId"; "required": false; "isSignal": true; }; "parentType": { "alias": "parentType"; "required": false; "isSignal": true; }; "objectTypeId": { "alias": "objectTypeId"; "required": false; "isSignal": true; }; "objectId": { "alias": "objectId"; "required": false; "isSignal": true; }; }, { "formData": "formDataChange"; "save": "save"; "cancel": "cancel"; }, never, never, true, never>;
|
|
86
214
|
}
|
|
87
215
|
|
|
88
216
|
/**
|
|
@@ -71,6 +71,16 @@ declare class SparkService {
|
|
|
71
71
|
get(type: string, id: string): Promise<PersistentObject>;
|
|
72
72
|
create(type: string, data: Partial<PersistentObject>): Promise<PersistentObject>;
|
|
73
73
|
update(type: string, id: string, data: Partial<PersistentObject>): Promise<PersistentObject>;
|
|
74
|
+
/**
|
|
75
|
+
* Asks the server to reshape an in-progress object after `triggeredBy`'s value changed.
|
|
76
|
+
*
|
|
77
|
+
* Writes nothing, but goes through the envelope like every other mutating call: a refresh may
|
|
78
|
+
* legitimately emit notifications, and may open the retry-action prompt.
|
|
79
|
+
*
|
|
80
|
+
* `triggeredBy` is the attribute's name. For a trigger inside an AsDetail row it is the same
|
|
81
|
+
* path form the inline validation errors use — `Jobs[2].ProfessionId`.
|
|
82
|
+
*/
|
|
83
|
+
refresh(type: string, data: Partial<PersistentObject>, triggeredBy: string): Promise<PersistentObject>;
|
|
74
84
|
delete(type: string, id: string): Promise<void>;
|
|
75
85
|
getCustomActions(objectTypeId: string): Promise<CustomActionDefinition[]>;
|
|
76
86
|
executeCustomAction(objectTypeId: string, actionName: string, parent?: PersistentObject, selectedItems?: PersistentObject[]): Promise<void>;
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { TemplateRef } from '@angular/core';
|
|
3
|
+
import { BsShellState } from '@mintplayer/ng-bootstrap/shell';
|
|
4
|
+
import { Breakpoint } from '@mintplayer/ng-bootstrap';
|
|
5
|
+
import { ShellStateChangeEventDetail } from '@mintplayer/web-components/shell';
|
|
6
|
+
import { ProgramUnitGroup } from '@mintplayer/ng-spark/models';
|
|
7
|
+
import { SparkLanguageService } from '@mintplayer/ng-spark/services';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Region slots for `<spark-shell>`.
|
|
11
|
+
*
|
|
12
|
+
* Each directive marks a template that REPLACES one region of the shell chrome (or fills an
|
|
13
|
+
* empty one). An omitted slot is not an empty slot: the shell renders its default — the toggler,
|
|
14
|
+
* the language selector, the title heading — so a host that supplies nothing still gets a
|
|
15
|
+
* complete working shell, and a host that supplies one slot leaves the rest alone.
|
|
16
|
+
*
|
|
17
|
+
* The menu itself is deliberately NOT a slot. Navigation is sourced entirely from
|
|
18
|
+
* `programUnits.json` through the rights-filtered `/spark/program-units` endpoint and re-fetched
|
|
19
|
+
* on sign-in/out; a host that finds itself writing unit anchors in a slot should be adding units
|
|
20
|
+
* to `programUnits.json` instead. Slots exist for the content AROUND the menu: an auth bar, a
|
|
21
|
+
* user chip, branding, a one-off extra link, an alert strip above the routed content.
|
|
22
|
+
*
|
|
23
|
+
* Naming follows the house convention (prefix, component, slot — see `*sparkQueryIcon` in
|
|
24
|
+
* `@mintplayer/ng-spark/grid`): `sparkShell` + region. Every slot also exists as a `TemplateRef`
|
|
25
|
+
* input on `SparkShellComponent` for hosts that cannot use content projection.
|
|
26
|
+
*
|
|
27
|
+
* ```html
|
|
28
|
+
* <spark-shell title="My App">
|
|
29
|
+
* <spark-auth-bar *sparkShellTopbarEnd />
|
|
30
|
+
* <div *sparkShellSidebarTop>
|
|
31
|
+
* <a routerLink="/github-projects" class="nav-link">GitHub projects</a>
|
|
32
|
+
* </div>
|
|
33
|
+
* <router-outlet />
|
|
34
|
+
* </spark-shell>
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
/** Topbar, leading edge. Default: a `bs-navbar-toggler` mirroring the shell's open state. */
|
|
38
|
+
declare class SparkShellTopbarStartDirective {
|
|
39
|
+
readonly templateRef: TemplateRef<unknown>;
|
|
40
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkShellTopbarStartDirective, never>;
|
|
41
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SparkShellTopbarStartDirective, "[sparkShellTopbarStart]", never, {}, {}, never, never, true, never>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Topbar, trailing edge. Default: the language selector (which hides itself when the app has
|
|
45
|
+
* one language). This is where an auth bar goes — the shell cannot ship one itself, since
|
|
46
|
+
* `@mintplayer/ng-spark` does not (and must not) depend on `@mintplayer/ng-spark-auth`.
|
|
47
|
+
*/
|
|
48
|
+
declare class SparkShellTopbarEndDirective {
|
|
49
|
+
readonly templateRef: TemplateRef<unknown>;
|
|
50
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkShellTopbarEndDirective, never>;
|
|
51
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SparkShellTopbarEndDirective, "[sparkShellTopbarEnd]", never, {}, {}, never, never, true, never>;
|
|
52
|
+
}
|
|
53
|
+
/** Sidebar, above everything. Default: `<h5>{{ title }}</h5>`. */
|
|
54
|
+
declare class SparkShellSidebarHeaderDirective {
|
|
55
|
+
readonly templateRef: TemplateRef<unknown>;
|
|
56
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkShellSidebarHeaderDirective, never>;
|
|
57
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SparkShellSidebarHeaderDirective, "[sparkShellSidebarHeader]", never, {}, {}, never, never, true, never>;
|
|
58
|
+
}
|
|
59
|
+
/** Sidebar, between the header and the program-units menu. No default. */
|
|
60
|
+
declare class SparkShellSidebarTopDirective {
|
|
61
|
+
readonly templateRef: TemplateRef<unknown>;
|
|
62
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkShellSidebarTopDirective, never>;
|
|
63
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SparkShellSidebarTopDirective, "[sparkShellSidebarTop]", never, {}, {}, never, never, true, never>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* An extra accordion tab in the sidebar menu, rendered after the generated program-unit groups
|
|
67
|
+
* and sharing their single-open behavior.
|
|
68
|
+
*
|
|
69
|
+
* A tab is contributed as DATA (header + body template), not as markup, and that is load-bearing:
|
|
70
|
+
* `bs-accordion` discovers its tabs with an Angular content query, which matches by declaration
|
|
71
|
+
* view, so a `<bs-accordion-tab>` written in a host's template and inserted into the library's
|
|
72
|
+
* accordion is never registered — it would land at index -1, get no hoisted header and no slot.
|
|
73
|
+
* Declaring a second `<bs-accordion>` instead is what puts the tab in its own exclusivity group:
|
|
74
|
+
* `mp-accordion` enforces single-open per element, over children it owns and over
|
|
75
|
+
* `<details name>`, whose grouping cannot cross a shadow root. So the tab element must be created
|
|
76
|
+
* by the menu itself, from what this directive carries.
|
|
77
|
+
*
|
|
78
|
+
* ```html
|
|
79
|
+
* <ng-container *sparkShellTab="'Component demos'; icon: 'palette'">
|
|
80
|
+
* <a routerLink="/query-slots" routerLinkActive="active" class="nav-link">Query card slots</a>
|
|
81
|
+
* </ng-container>
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* Navigation still belongs in `programUnits.json` — this is for pages the model cannot describe
|
|
85
|
+
* (client-side demos, external tools). For sidebar content that is NOT an accordion tab, use
|
|
86
|
+
* `*sparkShellSidebarTop` or `*sparkShellSidebarFooter`.
|
|
87
|
+
*/
|
|
88
|
+
declare class SparkShellTabDirective {
|
|
89
|
+
readonly templateRef: TemplateRef<unknown>;
|
|
90
|
+
/** The tab's header label. */
|
|
91
|
+
readonly header: _angular_core.InputSignal<string>;
|
|
92
|
+
/** Bootstrap icon name for the header, as in `programUnits.json`. Defaults to a folder. */
|
|
93
|
+
readonly icon: _angular_core.InputSignal<string | undefined>;
|
|
94
|
+
/** Replaces the icon+label header entirely, for a header that needs its own markup. */
|
|
95
|
+
readonly headerTemplate: _angular_core.InputSignal<TemplateRef<unknown> | null>;
|
|
96
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkShellTabDirective, never>;
|
|
97
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SparkShellTabDirective, "[sparkShellTab]", never, { "header": { "alias": "sparkShellTab"; "required": true; "isSignal": true; }; "icon": { "alias": "sparkShellTabIcon"; "required": false; "isSignal": true; }; "headerTemplate": { "alias": "sparkShellTabHeader"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* A sidebar accordion tab in the shape the menu renders it. Hosts normally contribute tabs with
|
|
101
|
+
* `*sparkShellTab`; this is the same thing as data, for a host that computes its tabs.
|
|
102
|
+
*/
|
|
103
|
+
interface SparkSidebarTab {
|
|
104
|
+
readonly header: string;
|
|
105
|
+
readonly icon?: string;
|
|
106
|
+
readonly headerTemplate?: TemplateRef<unknown> | null;
|
|
107
|
+
readonly content: TemplateRef<unknown>;
|
|
108
|
+
}
|
|
109
|
+
/** Sidebar, at the very bottom. No default. */
|
|
110
|
+
declare class SparkShellSidebarFooterDirective {
|
|
111
|
+
readonly templateRef: TemplateRef<unknown>;
|
|
112
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkShellSidebarFooterDirective, never>;
|
|
113
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SparkShellSidebarFooterDirective, "[sparkShellSidebarFooter]", never, {}, {}, never, never, true, never>;
|
|
114
|
+
}
|
|
115
|
+
/** Main region, above the projected content (the host's `<router-outlet>`). No default. */
|
|
116
|
+
declare class SparkShellMainHeaderDirective {
|
|
117
|
+
readonly templateRef: TemplateRef<unknown>;
|
|
118
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkShellMainHeaderDirective, never>;
|
|
119
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SparkShellMainHeaderDirective, "[sparkShellMainHeader]", never, {}, {}, never, never, true, never>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The application frame: topbar + sidebar + main, wrapping ng-bootstrap's `bs-shell` (whose
|
|
124
|
+
* `mp-shell` web component owns ALL responsive behavior — breakpoints, the overlay drawer,
|
|
125
|
+
* dismiss-on-navigate — in CSS; nothing here re-derives a pixel width). The sidebar renders the
|
|
126
|
+
* server-driven program-units menu; the host projects its `<router-outlet>` as the default
|
|
127
|
+
* content and customizes the chrome through the `*sparkShell*` slots (see `spark-shell-slots.ts`
|
|
128
|
+
* for the doctrine: an omitted slot renders its default, and the menu itself is never a slot).
|
|
129
|
+
*
|
|
130
|
+
* ```html
|
|
131
|
+
* <spark-shell title="My App">
|
|
132
|
+
* <spark-auth-bar *sparkShellTopbarEnd />
|
|
133
|
+
* <router-outlet />
|
|
134
|
+
* </spark-shell>
|
|
135
|
+
* ```
|
|
136
|
+
*
|
|
137
|
+
* The one piece of state the shell keeps is the toggler↔drawer mirror: the built-in hamburger is
|
|
138
|
+
* hidden (`::part(hamburger)`) in favor of a `bs-navbar-toggler` in the topbar, so the shell
|
|
139
|
+
* listens to `statechange` to keep the toggler's icon truthful in `auto` mode and only forces
|
|
140
|
+
* `show`/`hide` on explicit toggles.
|
|
141
|
+
*
|
|
142
|
+
* Theming: the chrome colors are CSS custom properties with the classic dark-sidebar defaults —
|
|
143
|
+
* `--spark-shell-topbar-bg`, `--spark-shell-sidebar-bg`, `--spark-shell-main-bg` — overridable on
|
|
144
|
+
* the `<spark-shell>` element. `sidebarTheme` flips the sidebar's `data-bs-theme` (which is what
|
|
145
|
+
* recolors the accordion internals across the shadow boundary) together with its default palette.
|
|
146
|
+
*/
|
|
147
|
+
declare class SparkShellComponent {
|
|
148
|
+
/** The sidebar heading. Ignored when a `*sparkShellSidebarHeader` slot is supplied. */
|
|
149
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
150
|
+
/** Forwarded to `bs-shell`: below it the sidebar is an overlay drawer. */
|
|
151
|
+
readonly breakpoint: _angular_core.InputSignal<Breakpoint>;
|
|
152
|
+
/**
|
|
153
|
+
* `data-bs-theme` for the sidebar — what flips the accordion's shadow-DOM internals between
|
|
154
|
+
* palettes — plus the matching default background. `null` sets no theme (inherit the page's).
|
|
155
|
+
*/
|
|
156
|
+
readonly sidebarTheme: _angular_core.InputSignal<"dark" | "light" | null>;
|
|
157
|
+
/** Forwarded to the menu: any changed value re-fetches the program units. */
|
|
158
|
+
readonly reloadToken: _angular_core.InputSignal<unknown>;
|
|
159
|
+
/** Extra sidebar tabs as data, for hosts that compute them; `*sparkShellTab` is the usual way. */
|
|
160
|
+
readonly sidebarTabs: _angular_core.InputSignal<readonly SparkSidebarTab[]>;
|
|
161
|
+
readonly topbarStartTemplate: _angular_core.InputSignal<TemplateRef<unknown> | null>;
|
|
162
|
+
readonly topbarEndTemplate: _angular_core.InputSignal<TemplateRef<unknown> | null>;
|
|
163
|
+
readonly sidebarHeaderTemplate: _angular_core.InputSignal<TemplateRef<unknown> | null>;
|
|
164
|
+
readonly sidebarTopTemplate: _angular_core.InputSignal<TemplateRef<unknown> | null>;
|
|
165
|
+
readonly sidebarFooterTemplate: _angular_core.InputSignal<TemplateRef<unknown> | null>;
|
|
166
|
+
readonly mainHeaderTemplate: _angular_core.InputSignal<TemplateRef<unknown> | null>;
|
|
167
|
+
private readonly topbarStartSlot;
|
|
168
|
+
private readonly topbarEndSlot;
|
|
169
|
+
private readonly sidebarHeaderSlot;
|
|
170
|
+
private readonly sidebarTopSlot;
|
|
171
|
+
private readonly sidebarFooterSlot;
|
|
172
|
+
private readonly mainHeaderSlot;
|
|
173
|
+
protected readonly topbarStartTpl: _angular_core.Signal<TemplateRef<unknown> | null>;
|
|
174
|
+
protected readonly topbarEndTpl: _angular_core.Signal<TemplateRef<unknown> | null>;
|
|
175
|
+
protected readonly sidebarHeaderTpl: _angular_core.Signal<TemplateRef<unknown> | null>;
|
|
176
|
+
protected readonly sidebarTopTpl: _angular_core.Signal<TemplateRef<unknown> | null>;
|
|
177
|
+
protected readonly sidebarFooterTpl: _angular_core.Signal<TemplateRef<unknown> | null>;
|
|
178
|
+
protected readonly mainHeaderTpl: _angular_core.Signal<TemplateRef<unknown> | null>;
|
|
179
|
+
/**
|
|
180
|
+
* Extra accordion tabs, forwarded to the menu so IT creates the `<bs-accordion-tab>` elements —
|
|
181
|
+
* the only way they share the generated groups' single-open behavior (see
|
|
182
|
+
* `SparkShellTabDirective`). Data-supplied tabs come first, then projected ones in declaration
|
|
183
|
+
* order.
|
|
184
|
+
*/
|
|
185
|
+
private readonly tabSlots;
|
|
186
|
+
protected readonly tabs: _angular_core.Signal<readonly SparkSidebarTab[]>;
|
|
187
|
+
protected readonly shellState: _angular_core.WritableSignal<BsShellState>;
|
|
188
|
+
protected readonly isSidebarVisible: _angular_core.WritableSignal<boolean>;
|
|
189
|
+
protected toggleSidebar(open: boolean): void;
|
|
190
|
+
protected onShellToggle(detail: ShellStateChangeEventDetail): void;
|
|
191
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkShellComponent, never>;
|
|
192
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkShellComponent, "spark-shell", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "breakpoint": { "alias": "breakpoint"; "required": false; "isSignal": true; }; "sidebarTheme": { "alias": "sidebarTheme"; "required": false; "isSignal": true; }; "reloadToken": { "alias": "reloadToken"; "required": false; "isSignal": true; }; "sidebarTabs": { "alias": "sidebarTabs"; "required": false; "isSignal": true; }; "topbarStartTemplate": { "alias": "topbarStartTemplate"; "required": false; "isSignal": true; }; "topbarEndTemplate": { "alias": "topbarEndTemplate"; "required": false; "isSignal": true; }; "sidebarHeaderTemplate": { "alias": "sidebarHeaderTemplate"; "required": false; "isSignal": true; }; "sidebarTopTemplate": { "alias": "sidebarTopTemplate"; "required": false; "isSignal": true; }; "sidebarFooterTemplate": { "alias": "sidebarFooterTemplate"; "required": false; "isSignal": true; }; "mainHeaderTemplate": { "alias": "mainHeaderTemplate"; "required": false; "isSignal": true; }; }, {}, ["topbarStartSlot", "topbarEndSlot", "sidebarHeaderSlot", "sidebarTopSlot", "sidebarFooterSlot", "mainHeaderSlot", "tabSlots"], ["*"], true, never>;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* The server-driven navigation menu: an accordion of program-unit groups fetched from
|
|
197
|
+
* `GET /spark/program-units`, which the server has already filtered to what the caller's rights
|
|
198
|
+
* allow. Hosts write ZERO router links for navigation — every group, unit, icon, label and link
|
|
199
|
+
* comes from `programUnits.json`; content around the menu belongs in `<spark-shell>`'s slots,
|
|
200
|
+
* and a host tempted to hand-write a unit anchor should add a unit to `programUnits.json`
|
|
201
|
+
* instead.
|
|
202
|
+
*
|
|
203
|
+
* Because the response is caller-scoped it must be re-fetched when the caller changes: the
|
|
204
|
+
* component tracks the optional `SPARK_AUTH_STATE` signal (supplied by ng-spark-auth's
|
|
205
|
+
* `provideSparkAuth()`, or by the app's own auth stack) and reloads on every change. Without a
|
|
206
|
+
* provider it fetches once. `reloadToken` is the manual escape hatch (any changed value triggers
|
|
207
|
+
* a reload), and `reload()` the imperative one.
|
|
208
|
+
*
|
|
209
|
+
* Usually rendered by `<spark-shell>`; exported standalone for hosts that own their own layout.
|
|
210
|
+
*/
|
|
211
|
+
declare class SparkProgramUnitsComponent {
|
|
212
|
+
private readonly sparkService;
|
|
213
|
+
private readonly authState;
|
|
214
|
+
/** Any changed value triggers a reload — for apps whose auth state isn't a provided signal. */
|
|
215
|
+
readonly reloadToken: _angular_core.InputSignal<unknown>;
|
|
216
|
+
/**
|
|
217
|
+
* Extra tabs to render after the generated groups, normally forwarded by `<spark-shell>` from
|
|
218
|
+
* its `*sparkShellTab` directives. They must be rendered by THIS template — see
|
|
219
|
+
* `SparkShellTabDirective` for why a host-declared `<bs-accordion-tab>` cannot work.
|
|
220
|
+
*/
|
|
221
|
+
readonly extraTabs: _angular_core.InputSignal<readonly SparkSidebarTab[]>;
|
|
222
|
+
protected readonly groups: _angular_core.WritableSignal<ProgramUnitGroup[]>;
|
|
223
|
+
constructor();
|
|
224
|
+
/** Re-fetches the menu. The response is already rights-filtered per caller. */
|
|
225
|
+
reload(): Promise<void>;
|
|
226
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkProgramUnitsComponent, never>;
|
|
227
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkProgramUnitsComponent, "spark-program-units", never, { "reloadToken": { "alias": "reloadToken"; "required": false; "isSignal": true; }; "extraTabs": { "alias": "extraTabs"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* The culture switcher: a `bs-select` over `SparkLanguageService`'s languages, persisting the
|
|
232
|
+
* choice. Renders nothing when the app declares one language (or none), so hosts can include it
|
|
233
|
+
* unconditionally — `<spark-shell>`'s topbar does exactly that as its trailing default.
|
|
234
|
+
*/
|
|
235
|
+
declare class SparkLanguageSelectorComponent {
|
|
236
|
+
protected readonly lang: SparkLanguageService;
|
|
237
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkLanguageSelectorComponent, never>;
|
|
238
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkLanguageSelectorComponent, "spark-language-selector", never, {}, {}, never, never, true, never>;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export { SparkLanguageSelectorComponent, SparkProgramUnitsComponent, SparkShellComponent, SparkShellMainHeaderDirective, SparkShellSidebarFooterDirective, SparkShellSidebarHeaderDirective, SparkShellSidebarTopDirective, SparkShellTabDirective, SparkShellTopbarEndDirective, SparkShellTopbarStartDirective };
|
|
242
|
+
export type { SparkSidebarTab };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InjectionToken, Provider } from '@angular/core';
|
|
1
|
+
import { InjectionToken, Signal, Provider } from '@angular/core';
|
|
2
2
|
|
|
3
3
|
interface SparkConfig {
|
|
4
4
|
baseUrl: string;
|
|
@@ -6,7 +6,22 @@ interface SparkConfig {
|
|
|
6
6
|
declare const SPARK_CONFIG: InjectionToken<SparkConfig>;
|
|
7
7
|
declare const defaultSparkConfig: SparkConfig;
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* A signal that changes whenever the authenticated user changes — the bridge that lets ng-spark
|
|
11
|
+
* components react to sign-in/out without a dependency on `@mintplayer/ng-spark-auth` (no
|
|
12
|
+
* dependency exists between the two packages, in either direction, on purpose).
|
|
13
|
+
*
|
|
14
|
+
* `@mintplayer/ng-spark-auth`'s `provideSparkAuth()` supplies it from `SparkAuthService.user`;
|
|
15
|
+
* an app with its own auth stack provides any signal that changes on sign-in/out. Consumers
|
|
16
|
+
* inject it `{ optional: true }` — absent, auth-sensitive data (the program-units menu) is
|
|
17
|
+
* fetched once and never re-fetched.
|
|
18
|
+
*
|
|
19
|
+
* The signal's VALUE is deliberately opaque (`unknown`): consumers only track it for change,
|
|
20
|
+
* never read it — what "the user" looks like belongs to the auth package.
|
|
21
|
+
*/
|
|
22
|
+
declare const SPARK_AUTH_STATE: InjectionToken<Signal<unknown>>;
|
|
23
|
+
|
|
9
24
|
declare function provideSpark(config?: Partial<SparkConfig>): Provider[];
|
|
10
25
|
|
|
11
|
-
export { SPARK_CONFIG, defaultSparkConfig, provideSpark };
|
|
26
|
+
export { SPARK_AUTH_STATE, SPARK_CONFIG, defaultSparkConfig, provideSpark };
|
|
12
27
|
export type { SparkConfig };
|