@elasticias/screens 1.0.39 → 1.0.41

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elasticias/screens",
3
- "version": "1.0.39",
3
+ "version": "1.0.41",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^21.0.0",
6
6
  "@angular/core": "^21.0.0",
@@ -61,6 +61,15 @@ declare class ScreenContext extends AbstractEntity {
61
61
  entity: unknown;
62
62
  grants: string[];
63
63
  duplicateMode: boolean;
64
+ /**
65
+ * Whether the screen may write the record it shows, for its current mode.
66
+ * Set by `AbstractDetailScreenV2` to its own `canWrite` signal, so shared
67
+ * components bound to `[context]` (ef-detail-toolbar's Save and its
68
+ * `requiresWrite` custom actions) follow the same rule without each
69
+ * screen passing it again. Undefined on screens that have no write mode
70
+ * (search, report), where those components keep their default behaviour.
71
+ */
72
+ canWrite?: Signal<boolean>;
64
73
  constructor(state: string, group?: unknown, entity?: unknown, refDataProvider?: ReferenceDataProvider, bundles?: Map<string, Map<string, Map<string, string>>>, grants?: string[], screenName?: string, language?: string);
65
74
  setReferenceDataProvider(provider: ReferenceDataProvider): void;
66
75
  get ref(): {
@@ -224,6 +233,12 @@ declare const SCREEN_REF_DATA_SERVICE: InjectionToken<ScreenReferenceDataService
224
233
  declare abstract class AbstractScreenComponent extends AbstractComponent implements OnInit, AfterViewInit, OnDestroy {
225
234
  context: ScreenContext;
226
235
  connectedUser: any;
236
+ /** V1 only: the NgForm a V1 detail template declares as `#entityForm`.
237
+ * V2 templates bind `[value]` / `(valueChangeEvent)` with no NgForm, so
238
+ * this stays undefined there and everything below that walks it
239
+ * (`disableAllControls`, `setFormErrors`, `isFormValid`) does nothing.
240
+ * V2 screens use `AbstractDetailScreenV2.readOnlyMode()` bound as
241
+ * `[efReadOnly]` on the form container, and `canWrite()` for saving. */
227
242
  entityForm: NgForm;
228
243
  serverErrors: _angular_core.WritableSignal<{
229
244
  [key: string]: string[];
@@ -241,6 +256,10 @@ declare abstract class AbstractScreenComponent extends AbstractComponent impleme
241
256
  get state(): string | null;
242
257
  screenStateKey: string;
243
258
  ngOnInit(): void;
259
+ /** The `isReadOnly()` branch below is the V1 read-only path. It never
260
+ * fires usefully on a V2 screen (see `entityForm`), and its predicate
261
+ * ignores the mode: Read+Create on an existing record counts as
262
+ * writable. V2 detail screens rely on `readOnlyMode()` instead. */
244
263
  ngAfterViewInit(): void;
245
264
  processGrants(): void;
246
265
  isFormValid(): boolean;
@@ -319,6 +338,8 @@ declare abstract class AbstractScreenComponent extends AbstractComponent impleme
319
338
  /** JSON gives back ISO strings; hand back real `Date`s or nothing. */
320
339
  private static reviveDateRange;
321
340
  clearServerErrors(): void;
341
+ /** V1 only: disables the controls of `entityForm`. A no-op on V2
342
+ * screens, which use `readOnlyMode()` + `[efReadOnly]`. */
322
343
  disableAllControls(): void;
323
344
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AbstractScreenComponent, never>;
324
345
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<AbstractScreenComponent, "ng-component", never, { "context": { "alias": "context"; "required": false; }; }, {}, never, never, true, never>;
@@ -920,6 +941,14 @@ interface EfDetailToolbarAction {
920
941
  * show (subject to `visible`).
921
942
  */
922
943
  permission?: Permissions;
944
+ /**
945
+ * The action changes the record (approve, cancel, complete, ...).
946
+ * Hidden when the screen cannot write it in its current mode, read from
947
+ * the bound `ScreenContext.canWrite` (set by `AbstractDetailScreenV2`).
948
+ * Independent of `permission`: when both are set, both must pass. A
949
+ * context with no write state (no `canWrite`) leaves the action shown.
950
+ */
951
+ requiresWrite?: boolean;
923
952
  /** Click handler. */
924
953
  command?: () => void;
925
954
  }
@@ -970,6 +999,12 @@ declare abstract class AbstractDetailScreenV2<TItem extends object = any> extend
970
999
  private readonly destroyRef;
971
1000
  private readonly auditHistoryService;
972
1001
  private readonly shortcuts;
1002
+ /** The in-flight entity / history requests. A newer load drops the
1003
+ * older one, so a slow response for an id the screen has moved off
1004
+ * can never overwrite the record it now shows, and destroying the
1005
+ * screen drops both. */
1006
+ private loadSub?;
1007
+ private auditSub?;
973
1008
  /** Distinguishes this instance's registration from any other detail
974
1009
  * screen alive at once (the outgoing and incoming routed component
975
1010
  * can briefly coexist), the same convention ef-row-actions uses:
@@ -998,6 +1033,29 @@ declare abstract class AbstractDetailScreenV2<TItem extends object = any> extend
998
1033
  * the `?mode=duplicate` query param.
999
1034
  */
1000
1035
  readonly duplicateMode: _angular_core.WritableSignal<boolean>;
1036
+ /**
1037
+ * Bumped every time `processGrants()` (re)reads the user's grants.
1038
+ * `context` is a plain object, not a signal, so without this a
1039
+ * `canWrite()` read before the grants arrive would memoize `false`
1040
+ * and the route subscription re-setting `editionState(false)` to the
1041
+ * same value would never invalidate it.
1042
+ */
1043
+ private readonly grantsVersion;
1044
+ /**
1045
+ * Whether the user may write the record on screen, for the mode it is
1046
+ * in: an existing record (edit mode, not duplicating) needs Edit; a new
1047
+ * record or a duplicate needs Create. Read is not enough for either,
1048
+ * which is how a Read-only user got a fully editable, savable form.
1049
+ *
1050
+ * Bind its negation on the form container to disable every ef-* control:
1051
+ * `<div class="form" [efReadOnly]="readOnlyMode()">`. ef-detail-toolbar
1052
+ * reads it through `context.canWrite` to hide Save, so screens pass
1053
+ * nothing for that. The backend still enforces the same grants; this is
1054
+ * the UI agreeing with it.
1055
+ */
1056
+ readonly canWrite: _angular_core.Signal<boolean>;
1057
+ /** `!canWrite()`, named for the `[efReadOnly]` binding it feeds. */
1058
+ readonly readOnlyMode: _angular_core.Signal<boolean>;
1001
1059
  /** True while service.get / create / update / delete is in flight. */
1002
1060
  readonly loading: _angular_core.WritableSignal<boolean>;
1003
1061
  /** Last load / save error message — empty string when none. */
@@ -1024,6 +1082,7 @@ declare abstract class AbstractDetailScreenV2<TItem extends object = any> extend
1024
1082
  */
1025
1083
  protected getCustomActions(): EfDetailToolbarAction[];
1026
1084
  ngOnInit(): void;
1085
+ processGrants(): void;
1027
1086
  /**
1028
1087
  * Merge a partial patch into the `entity` signal — the canonical
1029
1088
  * way for form inputs to write back. Spread-immutably so OnPush
@@ -1044,6 +1103,10 @@ declare abstract class AbstractDetailScreenV2<TItem extends object = any> extend
1044
1103
  * Persist the entity. Routes to `service.create()` when in new
1045
1104
  * or duplicate mode, `service.update(id, entity)` when editing.
1046
1105
  * Subclass `beforeSave()` can return `false` to cancel.
1106
+ *
1107
+ * A no-op when `canWrite()` is false, checked before `beforeSave()` so
1108
+ * a screen's validation never toasts at a user who could not save
1109
+ * anyway.
1047
1110
  */
1048
1111
  save(): void;
1049
1112
  /**
@@ -1068,7 +1131,9 @@ declare abstract class AbstractDetailScreenV2<TItem extends object = any> extend
1068
1131
  * via `service.create()` after the user hits Save.
1069
1132
  * Without the id, the duplicate route would have nothing to
1070
1133
  * fetch — `entity` would be empty and the clone-as-template
1071
- * flow would fall back to creating a blank record. */
1134
+ * flow would fall back to creating a blank record.
1135
+ * Needs Create: a duplicate is saved through `service.create()`, so
1136
+ * opening one without Create would only lead to a read-only form. */
1072
1137
  duplicate(): void;
1073
1138
  /**
1074
1139
  * Cancel handler for the default toolbar action: