ngx-dynamic-entity 1.6.0 → 1.8.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.6.0",
3
+ "version": "1.8.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.6.0",
11
+ "@dynamic-entity/core": "^1.8.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 */
@@ -797,6 +816,15 @@ declare class DynamicFormComponent implements OnInit, OnChanges, OnDestroy {
797
816
  get submitBlocked(): boolean;
798
817
  ngOnInit(): void;
799
818
  ngOnChanges(changes: SimpleChanges): void;
819
+ /**
820
+ * Whether an `initialData` change represents loading a different record.
821
+ *
822
+ * Not every change does. A template binding like `[initialData]="record() || {}"` yields a
823
+ * fresh object literal on each evaluation, so `ngOnChanges` fires with two empty objects
824
+ * and nothing has actually been loaded — resetting there would drag someone back to the
825
+ * first tab while they were filling in a new record on the third.
826
+ */
827
+ private isRecordSwap;
800
828
  ngOnDestroy(): void;
801
829
  /**
802
830
  * `focusPanel: false` is for a caller that is about to focus something more specific.
@@ -1336,6 +1364,60 @@ declare class TextareaFieldComponent {
1336
1364
  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>;
1337
1365
  }
1338
1366
 
1367
+ /**
1368
+ * Markdown field: a long-form input that stores **markdown source**, never HTML.
1369
+ *
1370
+ * Storing the source rather than rendered HTML is the whole design. The record stays plain
1371
+ * text — diffable, portable, safe to log, and impossible to turn into stored XSS by writing
1372
+ * it into a database. Rendering happens at display time and only when a consumer asks for
1373
+ * it, via `MARKDOWN_RENDERER`.
1374
+ *
1375
+ * Without a renderer this degrades to exactly what `textarea` does, plus preserved line
1376
+ * breaks: the source is shown as text with nothing interpreted. That is the default because
1377
+ * these packages declare no runtime dependencies beyond `tslib`, and a markdown parser is a
1378
+ * large thing to force on a consumer who wanted a form library.
1379
+ *
1380
+ * With a renderer, its HTML is bound through `[innerHTML]`, which Angular sanitizes — see
1381
+ * the token's own note on why that is a backstop rather than a licence.
1382
+ */
1383
+ declare class MarkdownFieldComponent implements OnChanges {
1384
+ field: NestedFieldConfig;
1385
+ control: AbstractControl;
1386
+ language: string;
1387
+ readonly: boolean;
1388
+ masked: boolean;
1389
+ /** Optional — the field is fully usable without one. */
1390
+ private readonly render;
1391
+ private readonly cdr;
1392
+ private readonly destroyRef;
1393
+ protected readonly previewing: i0.WritableSignal<boolean>;
1394
+ private valueSub?;
1395
+ /**
1396
+ * Re-render when the value changes from outside this component.
1397
+ *
1398
+ * `rendered()` reads `control.value`, which under OnPush is neither an input nor a
1399
+ * template event: typing in the textarea updates through `formControl`, but a host
1400
+ * calling `patchValue`, a rule, or an `autoPatch` mapping does not mark this component
1401
+ * dirty. The preview and the read-only view would keep showing the previous document
1402
+ * while the record already held a different one.
1403
+ */
1404
+ ngOnChanges(changes: SimpleChanges): void;
1405
+ /** A Preview tab that can only ever show the source back is worse than no tab. */
1406
+ protected get canPreview(): boolean;
1407
+ get label(): string;
1408
+ get placeholder(): string;
1409
+ /**
1410
+ * Rendered HTML, or null when there is no renderer or nothing to render.
1411
+ *
1412
+ * A renderer is consumer code and may throw on input it does not like. Letting that
1413
+ * escape would take down the whole form over one malformed field, so it falls back to the
1414
+ * source text — which is what the field would have shown with no renderer at all.
1415
+ */
1416
+ protected rendered(): string | null;
1417
+ static ɵfac: i0.ɵɵFactoryDeclaration<MarkdownFieldComponent, never>;
1418
+ 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>;
1419
+ }
1420
+
1339
1421
  declare class NumberFieldComponent {
1340
1422
  private readonly messages;
1341
1423
  field: NestedFieldConfig;
@@ -1737,5 +1819,5 @@ declare class FileFieldComponent {
1737
1819
  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>;
1738
1820
  }
1739
1821
 
1740
- 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 };
1822
+ 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 };
1741
1823
  export type { ChoiceOptionHost, ConfigSourceHandler, DynamicFieldComponentContract, EntityRefCacheStore, EntityRefSelection, HookFn, NgxDynamicEntityConfig, ValidationMessage };