ngx-dynamic-entity 1.5.0 → 1.7.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,6 +1,6 @@
1
1
  {
2
2
  "name": "ngx-dynamic-entity",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Angular standalone UI library for rendering dynamic entity forms.",
5
5
  "//typings": "Local workspace resolution only: ng-packagr 21 writes types/ngx-dynamic-entity.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.d.ts",
@@ -8,7 +8,7 @@
8
8
  "@angular/common": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
9
9
  "@angular/core": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
10
10
  "@angular/forms": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
11
- "@dynamic-entity/core": "^1.5.0",
11
+ "@dynamic-entity/core": "^1.7.0",
12
12
  "rxjs": "^7.0.0"
13
13
  },
14
14
  "keywords": [
package/styles.css CHANGED
@@ -577,3 +577,50 @@ textarea.ngx-field__input {
577
577
  grid-column: 1 / -1 !important;
578
578
  }
579
579
  }
580
+
581
+ /* ─── Markdown field ────────────────────────────────────────────────────────
582
+ With no MARKDOWN_RENDERER the field shows its own source, so the line breaks
583
+ the author typed are the only structure it has: pre-wrap is what preserves
584
+ them. Without this rule the read-only view collapses a document to one line. */
585
+ .ngx-field__markdown--source {
586
+ white-space: pre-wrap;
587
+ }
588
+
589
+ /* Rendered output is consumer HTML. Keep it inside the field's column rather
590
+ than letting a wide table or long code line stretch the grid. */
591
+ .ngx-field__markdown {
592
+ overflow-x: auto;
593
+ max-width: 100%;
594
+ }
595
+
596
+ .ngx-field__markdown > :first-child { margin-top: 0; }
597
+ .ngx-field__markdown > :last-child { margin-bottom: 0; }
598
+
599
+ .ngx-field__markdown-tabs {
600
+ display: flex;
601
+ gap: 4px;
602
+ margin-bottom: 6px;
603
+ }
604
+
605
+ .ngx-field__markdown-tab {
606
+ font: inherit;
607
+ padding: 2px 10px;
608
+ cursor: pointer;
609
+ border: 1px solid var(--ngx-border, #d1d5db);
610
+ border-radius: 4px;
611
+ background: var(--ngx-surface, #fff);
612
+ }
613
+
614
+ .ngx-field__markdown-tab[aria-pressed='true'] {
615
+ background: var(--ngx-accent-soft, #eef2ff);
616
+ border-color: var(--ngx-accent, #6366f1);
617
+ }
618
+
619
+ /* The preview replaces the textarea, so give it a comparable height — otherwise
620
+ switching tabs makes the whole form jump. */
621
+ .ngx-field__markdown--preview {
622
+ min-height: 8.5rem;
623
+ padding: 8px 10px;
624
+ border: 1px solid var(--ngx-border, #d1d5db);
625
+ border-radius: 4px;
626
+ }
@@ -163,6 +163,25 @@ type ConfigSourceHandler = (entityKey: string) => any;
163
163
  * Enables cross-entity field referencing (`isReferenced`, `referencedEntityKey`, `referencedFieldId`, `hasDrift`).
164
164
  */
165
165
  declare const CONFIG_SOURCE: InjectionToken<ConfigSourceHandler>;
166
+ /**
167
+ * Turns the markdown a `markdown` field stores into HTML for display.
168
+ *
169
+ * Optional, and deliberately not bundled: these packages declare no runtime dependencies
170
+ * beyond `tslib`, and a markdown parser is a large one to force on every consumer. Without
171
+ * a renderer the field still works — it stores and shows the source text, with line breaks
172
+ * preserved and nothing interpreted.
173
+ *
174
+ * The returned HTML is bound through `[innerHTML]`, so Angular's sanitizer strips scripts
175
+ * and event handlers before it reaches the DOM. That is a backstop, not a licence: a
176
+ * renderer should still be configured to escape raw HTML in its input, because sanitizing
177
+ * removes the dangerous parts silently rather than telling the author their content was
178
+ * altered.
179
+ *
180
+ * @example
181
+ * import { marked } from 'marked';
182
+ * { provide: MARKDOWN_RENDERER, useValue: (src: string) => marked.parse(src) as string }
183
+ */
184
+ declare const MARKDOWN_RENDERER: InjectionToken<(source: string) => string>;
166
185
 
167
186
  interface NgxDynamicEntityConfig {
168
187
  /** Roles that see masked field values as XXXXXXXXX */
@@ -1011,6 +1030,7 @@ declare class DynamicRecordFormComponent implements OnChanges {
1011
1030
  }>;
1012
1031
  dynamicFormComp?: DynamicFormComponent;
1013
1032
  private readonly rulesEvaluation;
1033
+ private readonly rbacService;
1014
1034
  private readonly host;
1015
1035
  private readonly injector;
1016
1036
  readonly currentData: i0.WritableSignal<Record<string, any>>;
@@ -1035,6 +1055,16 @@ declare class DynamicRecordFormComponent implements OnChanges {
1035
1055
  message: string;
1036
1056
  }[];
1037
1057
  dismissInfoBanner(fieldId: string): void;
1058
+ /**
1059
+ * Whether the current roles may edit this record.
1060
+ *
1061
+ * `userRoles` was declared as an input here and then read by nothing at all — this
1062
+ * component computed no permissions of any kind, so the record view was fully editable
1063
+ * for every role while the plain form at least hid its Save button. Cached on the same
1064
+ * terms as the sibling component: it depends only on `config` and `userRoles`.
1065
+ */
1066
+ private permissionsCache;
1067
+ get permissions(): ReturnType<RbacService['getPermissions']>;
1038
1068
  /** True when nothing in the record may be edited. */
1039
1069
  get recordReadOnly(): boolean;
1040
1070
  /**
@@ -1325,6 +1355,60 @@ declare class TextareaFieldComponent {
1325
1355
  static ɵcmp: i0.ɵɵComponentDeclaration<TextareaFieldComponent, "ngx-textarea-field", never, { "field": { "alias": "field"; "required": false; }; "control": { "alias": "control"; "required": false; }; "language": { "alias": "language"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "masked": { "alias": "masked"; "required": false; }; }, {}, never, never, true, never>;
1326
1356
  }
1327
1357
 
1358
+ /**
1359
+ * Markdown field: a long-form input that stores **markdown source**, never HTML.
1360
+ *
1361
+ * Storing the source rather than rendered HTML is the whole design. The record stays plain
1362
+ * text — diffable, portable, safe to log, and impossible to turn into stored XSS by writing
1363
+ * it into a database. Rendering happens at display time and only when a consumer asks for
1364
+ * it, via `MARKDOWN_RENDERER`.
1365
+ *
1366
+ * Without a renderer this degrades to exactly what `textarea` does, plus preserved line
1367
+ * breaks: the source is shown as text with nothing interpreted. That is the default because
1368
+ * these packages declare no runtime dependencies beyond `tslib`, and a markdown parser is a
1369
+ * large thing to force on a consumer who wanted a form library.
1370
+ *
1371
+ * With a renderer, its HTML is bound through `[innerHTML]`, which Angular sanitizes — see
1372
+ * the token's own note on why that is a backstop rather than a licence.
1373
+ */
1374
+ declare class MarkdownFieldComponent implements OnChanges {
1375
+ field: NestedFieldConfig;
1376
+ control: AbstractControl;
1377
+ language: string;
1378
+ readonly: boolean;
1379
+ masked: boolean;
1380
+ /** Optional — the field is fully usable without one. */
1381
+ private readonly render;
1382
+ private readonly cdr;
1383
+ private readonly destroyRef;
1384
+ protected readonly previewing: i0.WritableSignal<boolean>;
1385
+ private valueSub?;
1386
+ /**
1387
+ * Re-render when the value changes from outside this component.
1388
+ *
1389
+ * `rendered()` reads `control.value`, which under OnPush is neither an input nor a
1390
+ * template event: typing in the textarea updates through `formControl`, but a host
1391
+ * calling `patchValue`, a rule, or an `autoPatch` mapping does not mark this component
1392
+ * dirty. The preview and the read-only view would keep showing the previous document
1393
+ * while the record already held a different one.
1394
+ */
1395
+ ngOnChanges(changes: SimpleChanges): void;
1396
+ /** A Preview tab that can only ever show the source back is worse than no tab. */
1397
+ protected get canPreview(): boolean;
1398
+ get label(): string;
1399
+ get placeholder(): string;
1400
+ /**
1401
+ * Rendered HTML, or null when there is no renderer or nothing to render.
1402
+ *
1403
+ * A renderer is consumer code and may throw on input it does not like. Letting that
1404
+ * escape would take down the whole form over one malformed field, so it falls back to the
1405
+ * source text — which is what the field would have shown with no renderer at all.
1406
+ */
1407
+ protected rendered(): string | null;
1408
+ static ɵfac: i0.ɵɵFactoryDeclaration<MarkdownFieldComponent, never>;
1409
+ static ɵcmp: i0.ɵɵComponentDeclaration<MarkdownFieldComponent, "ngx-markdown-field", never, { "field": { "alias": "field"; "required": false; }; "control": { "alias": "control"; "required": false; }; "language": { "alias": "language"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "masked": { "alias": "masked"; "required": false; }; }, {}, never, never, true, never>;
1410
+ }
1411
+
1328
1412
  declare class NumberFieldComponent {
1329
1413
  private readonly messages;
1330
1414
  field: NestedFieldConfig;
@@ -1726,5 +1810,5 @@ declare class FileFieldComponent {
1726
1810
  static ɵcmp: i0.ɵɵComponentDeclaration<FileFieldComponent, "ngx-file-field", never, { "field": { "alias": "field"; "required": false; }; "control": { "alias": "control"; "required": false; }; "language": { "alias": "language"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "masked": { "alias": "masked"; "required": false; }; }, {}, never, never, true, never>;
1727
1811
  }
1728
1812
 
1729
- export { ASYNC_VALIDATOR_REGISTRY, ArrayFieldComponent, BooleanFieldComponent, COMMON_MODULES_REGISTRY, CONFIG_SOURCE, CascadeDataService, CheckboxFieldComponent, ConfigSourceService, CurrencyFieldComponent, DEFAULT_VALIDATION_MESSAGES, DateFieldComponent, DateTimeFieldComponent, DropdownFieldComponent, DynamicFieldComponent, DynamicFormComponent, DynamicRecordFormComponent, ENTITY_REF_CACHE_STORE, ENTITY_REF_REGISTRY, EmailFieldComponent, EntityRefFieldComponent, EntityRefRegistryService, EntityRefSelectionService, EntityReferenceService, FIELD_TYPE_REGISTRY, FIELD_TYPE_SETS, FieldRegistryService, FileFieldComponent, GroupFieldComponent, HOOK_REGISTRY, HookRegistryService, ImageFieldComponent, InMemoryEntityRefCacheStore, LOOKUP_REGISTRY, LookupRegistryService, MASKED_ROLES, MonthYearFieldComponent, MultiSelectFieldComponent, NumberFieldComponent, PasswordFieldComponent, RECORD_MIGRATIONS, RadioFieldComponent, RbacService, RulesEvaluationService, SYSTEM_DEFAULT_CAN_EDIT, TextFieldComponent, TextareaFieldComponent, TimeFieldComponent, UPLOAD_HANDLER, VALIDATION_MESSAGES, VALIDATOR_REGISTRY, ValidationMessagesService, ValidatorRegistryService, builtInFieldTypes, provideBuiltInFieldTypes, provideFieldTypes, provideNgxDynamicEntity, refreshChoiceOptions };
1813
+ export { ASYNC_VALIDATOR_REGISTRY, ArrayFieldComponent, BooleanFieldComponent, COMMON_MODULES_REGISTRY, CONFIG_SOURCE, CascadeDataService, CheckboxFieldComponent, ConfigSourceService, CurrencyFieldComponent, DEFAULT_VALIDATION_MESSAGES, DateFieldComponent, DateTimeFieldComponent, DropdownFieldComponent, DynamicFieldComponent, DynamicFormComponent, DynamicRecordFormComponent, ENTITY_REF_CACHE_STORE, ENTITY_REF_REGISTRY, EmailFieldComponent, EntityRefFieldComponent, EntityRefRegistryService, EntityRefSelectionService, EntityReferenceService, FIELD_TYPE_REGISTRY, FIELD_TYPE_SETS, FieldRegistryService, FileFieldComponent, GroupFieldComponent, HOOK_REGISTRY, HookRegistryService, ImageFieldComponent, InMemoryEntityRefCacheStore, LOOKUP_REGISTRY, LookupRegistryService, MARKDOWN_RENDERER, MASKED_ROLES, MarkdownFieldComponent, MonthYearFieldComponent, MultiSelectFieldComponent, NumberFieldComponent, PasswordFieldComponent, RECORD_MIGRATIONS, RadioFieldComponent, RbacService, RulesEvaluationService, SYSTEM_DEFAULT_CAN_EDIT, TextFieldComponent, TextareaFieldComponent, TimeFieldComponent, UPLOAD_HANDLER, VALIDATION_MESSAGES, VALIDATOR_REGISTRY, ValidationMessagesService, ValidatorRegistryService, builtInFieldTypes, provideBuiltInFieldTypes, provideFieldTypes, provideNgxDynamicEntity, refreshChoiceOptions };
1730
1814
  export type { ChoiceOptionHost, ConfigSourceHandler, DynamicFieldComponentContract, EntityRefCacheStore, EntityRefSelection, HookFn, NgxDynamicEntityConfig, ValidationMessage };