ngx-dynamic-entity-builder 1.10.0 → 1.12.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/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "ngx-dynamic-entity-builder",
3
- "version": "1.10.0",
3
+ "version": "1.12.0",
4
4
  "description": "Angular standalone visual builder for authoring dynamic-entity EntityConfig (fields, tabs, validators, RBAC).",
5
5
  "//typings": "Local workspace resolution only: ng-packagr 21 writes types/ngx-dynamic-entity-builder.d.ts into dist/package.json. Do NOT add a 'main' field here — ng-packagr does not generate one, so it would be copied verbatim and resolve wrongly in the published package.",
6
6
  "typings": "types/ngx-dynamic-entity-builder.d.ts",
7
7
  "peerDependencies": {
8
8
  "@angular/cdk": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
9
9
  "@angular/common": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
10
- "@angular/core": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
10
+ "@angular/core": "^17.2.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
11
11
  "@angular/forms": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
12
12
  "@angular/material": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
13
- "@dynamic-entity/core": "^1.10.0",
14
- "ngx-dynamic-entity": "^1.10.0",
13
+ "@dynamic-entity/core": "^1.12.0",
14
+ "ngx-dynamic-entity": "^1.12.0",
15
15
  "rxjs": "^7.0.0"
16
16
  },
17
17
  "keywords": [
@@ -233,6 +233,15 @@ declare class BuilderStore {
233
233
  /** `derived`, or `derived_2`, `derived_3`… if another field already holds it. */
234
234
  private availableId;
235
235
  setFieldPlaceholder(id: string, language: string, value: string): void;
236
+ /**
237
+ * Help text shown under the control, per language.
238
+ *
239
+ * Separate from the placeholder for the reason the schema keeps them separate: a
240
+ * placeholder vanishes the moment the user types, so anything they need *while* filling the
241
+ * field in cannot live there. An author with only a placeholder box to write in tends to
242
+ * put it there anyway.
243
+ */
244
+ setFieldHint(id: string, language: string, value: string): void;
236
245
  toggleFlagValidator(id: string, validator: FlagValidator, on: boolean): void;
237
246
  setParamValidator(id: string, validator: ParamValidator, value: number | null): void;
238
247
  getParamValidator(field: NestedFieldConfig, validator: ParamValidator): number | null;
@@ -380,8 +389,16 @@ declare const DEFAULT_BUILDER_TEXT: {
380
389
  redo: string;
381
390
  redoTooltip: string;
382
391
  issuesTooltip: string;
392
+ issuesCount: string;
383
393
  copyJson: string;
384
394
  save: string;
395
+ saveBlockedTooltip: string;
396
+ collapsePalette: string;
397
+ expandPalette: string;
398
+ collapseInspector: string;
399
+ expandInspector: string;
400
+ paletteRailLabel: string;
401
+ inspectorRailLabel: string;
385
402
  entityName: string;
386
403
  entityNamePlaceholder: string;
387
404
  editingLanguage: string;
@@ -393,6 +410,16 @@ declare const DEFAULT_BUILDER_TEXT: {
393
410
  addField: string;
394
411
  fieldProperties: string;
395
412
  configJson: string;
413
+ paletteSearchPlaceholder: string;
414
+ paletteSearchLabel: string;
415
+ paletteSearchClear: string;
416
+ paletteNoMatch: string;
417
+ paletteGroupBasic: string;
418
+ paletteGroupChoice: string;
419
+ paletteGroupDateTime: string;
420
+ paletteGroupRich: string;
421
+ paletteGroupStructure: string;
422
+ paletteGroupOther: string;
396
423
  fieldsHeading: string;
397
424
  canvasEmpty: string;
398
425
  selectField: string;
@@ -415,8 +442,26 @@ declare const DEFAULT_BUILDER_TEXT: {
415
442
  tabMoveHint: string;
416
443
  labelInLanguage: string;
417
444
  placeholderInLanguage: string;
445
+ hintInLanguage: string;
446
+ hintHint: string;
418
447
  defaultValue: string;
419
- validation: string;
448
+ sectionBasics: string;
449
+ sectionLayout: string;
450
+ sectionValidation: string;
451
+ sectionOptions: string;
452
+ sectionDisplay: string;
453
+ sectionVisibility: string;
454
+ sectionAutomation: string;
455
+ sectionRules: string;
456
+ sectionReference: string;
457
+ width: string;
458
+ widthQuarter: string;
459
+ widthThird: string;
460
+ widthHalf: string;
461
+ widthTwoThirds: string;
462
+ widthFull: string;
463
+ widthHint: string;
464
+ widthOf: string;
420
465
  dataSource: string;
421
466
  optionsComeFrom: string;
422
467
  optionsSourceNone: string;
@@ -562,6 +607,7 @@ declare class BuilderTextService {
562
607
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<BuilderTextService>;
563
608
  }
564
609
 
610
+ type RbacAction = 'view' | 'edit' | 'delete';
565
611
  /**
566
612
  * EntityBuilderComponent — the top-level visual builder for an EntityFormConfig.
567
613
  */
@@ -574,6 +620,8 @@ declare class EntityBuilderComponent implements OnChanges {
574
620
  * copy of a config the moment it is edited — see `doSave`.
575
621
  */
576
622
  private readonly configSource;
623
+ /** Scopes the focus lookup below to this builder, not the first match in the document. */
624
+ private readonly hostRef;
577
625
  /** Existing config to edit. When omitted, the builder starts blank. */
578
626
  config?: EntityFormConfig;
579
627
  /** Languages available for label/placeholder editing. First entry is the default. */
@@ -600,7 +648,25 @@ declare class EntityBuilderComponent implements OnChanges {
600
648
  configChange: EventEmitter<EntityFormConfig>;
601
649
  /** Emitted when the user clicks Save. Carries a clean, deep-cloned config. */
602
650
  save: EventEmitter<EntityFormConfig>;
603
- protected readonly rbacActions: (keyof EntityPermissions)[];
651
+ protected readonly rbacActions: readonly RbacAction[];
652
+ readonly leftSidebarOpen: _angular_core.ModelSignal<boolean>;
653
+ readonly rightSidebarOpen: _angular_core.ModelSignal<boolean>;
654
+ toggleLeftSidebar(): void;
655
+ toggleRightSidebar(): void;
656
+ protected collapseLeft(): void;
657
+ protected expandLeft(): void;
658
+ protected collapseRight(): void;
659
+ protected expandRight(): void;
660
+ /** The replacement control does not exist until the template has caught up with the signal. */
661
+ private focusAfterRender;
662
+ /**
663
+ * Select the field an issue is about, so reading the message and acting on it are one step.
664
+ *
665
+ * A problem without a `fieldId` is about the entity itself — no tabs, a bad entity name —
666
+ * and there is nothing to select; the template disables those entries rather than silently
667
+ * doing nothing when one is clicked.
668
+ */
669
+ protected selectProblemField(problem: BuilderProblem): void;
604
670
  constructor();
605
671
  /**
606
672
  * Ctrl/Cmd+Z and Ctrl/Cmd+Shift+Z, but only when the user is not typing.
@@ -613,19 +679,53 @@ declare class EntityBuilderComponent implements OnChanges {
613
679
  protected doSave(): void;
614
680
  protected get json(): string;
615
681
  protected copyJson(): void;
616
- protected rolesFor(action: keyof EntityPermissions): readonly string[];
617
- protected setRoles(action: keyof EntityPermissions, roles: string[]): void;
682
+ protected rolesFor(action: RbacAction): readonly string[];
683
+ protected setRoles(action: RbacAction, roles: string[]): void;
618
684
  /** Parse a comma-separated role string into a trimmed, de-duplicated array. */
619
- protected setRolesFromText(action: keyof EntityPermissions, text: string): void;
685
+ protected setRolesFromText(action: RbacAction, text: string): void;
620
686
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EntityBuilderComponent, never>;
621
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EntityBuilderComponent, "ngx-entity-builder", never, { "config": { "alias": "config"; "required": false; }; "languages": { "alias": "languages"; "required": false; }; "uiLanguage": { "alias": "uiLanguage"; "required": false; }; "availableRoles": { "alias": "availableRoles"; "required": false; }; "userRoles": { "alias": "userRoles"; "required": false; }; "commonModules": { "alias": "commonModules"; "required": false; }; }, { "configChange": "configChange"; "save": "save"; }, never, ["[ngxBuilderPreview]"], true, never>;
687
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EntityBuilderComponent, "ngx-entity-builder", never, { "config": { "alias": "config"; "required": false; }; "languages": { "alias": "languages"; "required": false; }; "uiLanguage": { "alias": "uiLanguage"; "required": false; }; "availableRoles": { "alias": "availableRoles"; "required": false; }; "userRoles": { "alias": "userRoles"; "required": false; }; "commonModules": { "alias": "commonModules"; "required": false; }; "leftSidebarOpen": { "alias": "leftSidebarOpen"; "required": false; "isSignal": true; }; "rightSidebarOpen": { "alias": "rightSidebarOpen"; "required": false; "isSignal": true; }; }, { "configChange": "configChange"; "save": "save"; "leftSidebarOpen": "leftSidebarOpenChange"; "rightSidebarOpen": "rightSidebarOpenChange"; }, never, ["[ngxBuilderPreview]"], true, never>;
622
688
  }
623
689
 
624
690
  /**
625
- * FieldPaletteComponent — the list of buildable field types (Angular Material).
691
+ * Which drawer of the palette a field type belongs in.
692
+ *
693
+ * Kept here rather than on `FieldTypeMeta` in core: the catalogue is a data model shared with
694
+ * the renderer, and how a picker arranges twenty-two buttons is a decision about this panel.
695
+ * A type absent from the map — a consumer's custom type, or one added to core later — falls
696
+ * into `other`, so the palette degrades to "listed, just not filed" rather than dropping it.
697
+ */
698
+ declare const PALETTE_GROUPS: readonly ["basic", "choice", "datetime", "rich", "structure", "other"];
699
+ type PaletteGroup = (typeof PALETTE_GROUPS)[number];
700
+ interface PaletteSection {
701
+ group: PaletteGroup;
702
+ heading: string;
703
+ items: FieldTypeMeta[];
704
+ }
705
+ /**
706
+ * FieldPaletteComponent — the list of buildable field types.
707
+ *
708
+ * Two things changed the day this stopped being a flat grid of twenty-two buttons. The labels
709
+ * were clipped mid-word — "Boolean Toggl", "Entity Referen", "File Attachmen" — because the
710
+ * grid forced two equal columns and the button refused to wrap, so a third of the palette
711
+ * could not be read. And with no order to them, finding "Month & Year" meant scanning the
712
+ * whole list: the grouping and the filter are what make a palette this size navigable.
626
713
  */
627
714
  declare class FieldPaletteComponent {
715
+ /** Builder chrome, overridable via BUILDER_TEXT. */
716
+ protected readonly ui: BuilderTextService;
628
717
  readonly catalog: readonly FieldTypeMeta[];
718
+ /** The filter text. Matched against the label, the type and the description. */
719
+ protected readonly query: _angular_core.WritableSignal<string>;
720
+ /**
721
+ * The catalogue, filtered and filed.
722
+ *
723
+ * Matching the description as well as the label is what makes the filter worth having: a
724
+ * person looking for a currency input types "money", which appears in no label but is
725
+ * exactly the sort of word a description carries. An empty group is dropped rather than
726
+ * rendered as a heading with nothing under it.
727
+ */
728
+ protected readonly sections: _angular_core.Signal<PaletteSection[]>;
629
729
  pick: EventEmitter<RichFieldType>;
630
730
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<FieldPaletteComponent, never>;
631
731
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<FieldPaletteComponent, "ngx-field-palette", never, {}, { "pick": "pick"; }, never, never, true, never>;
@@ -669,10 +769,51 @@ declare class FieldInspectorComponent {
669
769
  protected readonly store: BuilderStore;
670
770
  protected readonly field: _angular_core.Signal<NestedFieldConfig>;
671
771
  protected readonly meta: _angular_core.Signal<FieldTypeMeta>;
772
+ /**
773
+ * The offered widths, as fractions of the 12-column row.
774
+ *
775
+ * Five steps rather than all twelve: a free 1–12 spinner is a layout system, and what an
776
+ * author actually wants is "these two side by side" or "this one across the row". The
777
+ * fractions that divide twelve evenly cover that, and anything finer stays authorable in
778
+ * the JSON for the rare case that needs it.
779
+ */
780
+ protected readonly spanChoices: readonly [{
781
+ readonly span: 3;
782
+ readonly textKey: "widthQuarter";
783
+ }, {
784
+ readonly span: 4;
785
+ readonly textKey: "widthThird";
786
+ }, {
787
+ readonly span: 6;
788
+ readonly textKey: "widthHalf";
789
+ }, {
790
+ readonly span: 8;
791
+ readonly textKey: "widthTwoThirds";
792
+ }, {
793
+ readonly span: 12;
794
+ readonly textKey: "widthFull";
795
+ }];
672
796
  protected lang(): string;
797
+ /**
798
+ * How many validators this field actually has, for the section's badge.
799
+ *
800
+ * `false` and an empty `custom: []` are both "not set" written the long way — a toggle
801
+ * switched off leaves the key behind — so neither counts. `0` does: `minLength: 0` is a
802
+ * value somebody typed.
803
+ */
804
+ protected validatorCount(field: NestedFieldConfig): number;
805
+ /**
806
+ * How many display flags are set away from their default.
807
+ *
808
+ * `visibility` counts when it is *false* and the rest when they are true, because the badge
809
+ * is meant to say "something here is not the default" — counting `visibility: true` would
810
+ * put a 1 on every field in the config and tell the author nothing.
811
+ */
812
+ protected displayFlagCount(field: NestedFieldConfig): number;
673
813
  protected toNum(value: unknown): number | null;
674
814
  protected labelValue(field: NestedFieldConfig): string;
675
815
  protected placeholderValue(field: NestedFieldConfig): string;
816
+ protected hintValue(field: NestedFieldConfig): string;
676
817
  protected optionLabel(option: DropdownOption): string;
677
818
  /**
678
819
  * Every tab a field can live on, sub-tabs included.