@solidev/data 1.0.0-beta.1 → 1.0.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/AGENTS.md +475 -0
- package/README.md +95 -5
- package/fesm2022/solidev-data-richedit.mjs +82 -8
- package/fesm2022/solidev-data-richedit.mjs.map +1 -1
- package/fesm2022/solidev-data.mjs +4370 -352
- package/fesm2022/solidev-data.mjs.map +1 -1
- package/package.json +1 -1
- package/types/solidev-data-richedit.d.ts +60 -5
- package/types/solidev-data.d.ts +4125 -205
|
@@ -8,6 +8,18 @@ import * as i3 from '@angular/forms';
|
|
|
8
8
|
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
9
9
|
import { firstValueFrom } from 'rxjs';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Named toolbar presets for {@link RicheditComponent}, selectable through its
|
|
13
|
+
* `toolbar` input.
|
|
14
|
+
*
|
|
15
|
+
* - `default`: the full set — marks, code and quote, lists, headings, links,
|
|
16
|
+
* colours and alignment.
|
|
17
|
+
* - `light`: a reduced set — basic marks, lists, text colour and alignment.
|
|
18
|
+
* - `none`: empty; the component hides the menu bar entirely for this value.
|
|
19
|
+
*
|
|
20
|
+
* A `Toolbar` can also be passed directly to the component when neither preset
|
|
21
|
+
* fits.
|
|
22
|
+
*/
|
|
11
23
|
const RichEditToolbars = {
|
|
12
24
|
default: [
|
|
13
25
|
["bold", "italic"],
|
|
@@ -27,28 +39,77 @@ const RichEditToolbars = {
|
|
|
27
39
|
],
|
|
28
40
|
none: [],
|
|
29
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* Rich text editor for a model field, built on `ngx-editor`.
|
|
44
|
+
*
|
|
45
|
+
* Shipped as a separate entry point (`@solidev/data/richedit`) so that the
|
|
46
|
+
* `ngx-editor` dependency stays optional for consumers who do not need it. It
|
|
47
|
+
* follows the same `dd` / `inline` / `form` layout convention as
|
|
48
|
+
* `<data-dispedit>`, and can also stand in as the `quill` editor slot.
|
|
49
|
+
*
|
|
50
|
+
* Unlike `<data-dispedit>`, saving is explicit: the value is written back and
|
|
51
|
+
* persisted only when {@link save} runs, from the built-in save button — and
|
|
52
|
+
* only in `dd` mode. In the other modes {@link save} updates the model in memory
|
|
53
|
+
* but issues no request, leaving persistence to the surrounding form. Callers
|
|
54
|
+
* that supply their own `[fc]` can instead track {@link changed}.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```html
|
|
58
|
+
* <data-richedit [model]="thing" field="description">Description</data-richedit>
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
30
61
|
class RicheditComponent {
|
|
62
|
+
/** Model instance holding the field. */
|
|
31
63
|
model;
|
|
64
|
+
/** Name of the rich text field to edit. */
|
|
32
65
|
field;
|
|
66
|
+
/** Whether {@link toggleEdit} is allowed to enable editing. */
|
|
33
67
|
editable = true;
|
|
68
|
+
/** Whether the editor is currently enabled. Defaults to true. */
|
|
34
69
|
edit = true;
|
|
70
|
+
/**
|
|
71
|
+
* Layout, and whether {@link save} persists.
|
|
72
|
+
*
|
|
73
|
+
* `dd` renders a `<dt>`/`<dd>` block and is the only mode that saves to the
|
|
74
|
+
* API; `inline` and `form` render a label plus the editor and leave saving to
|
|
75
|
+
* the caller.
|
|
76
|
+
*/
|
|
35
77
|
mode = "dd";
|
|
36
78
|
/** Hide label (for inline forms) */
|
|
37
79
|
hideLabel = false;
|
|
38
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* Hide the built-in save button, for callers driving persistence themselves
|
|
82
|
+
* from the {@link changed} output.
|
|
83
|
+
*/
|
|
39
84
|
hideButton = false;
|
|
40
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* Form control backing the editor.
|
|
87
|
+
*
|
|
88
|
+
* When supplied, the component uses it as-is and skips its own setup — no
|
|
89
|
+
* field manager lookup, no seeding from the model, and no {@link changed}
|
|
90
|
+
* emissions. When absent, one is created and wired up from `[model]` and
|
|
91
|
+
* `[field]`.
|
|
92
|
+
*/
|
|
41
93
|
fc;
|
|
42
|
-
/** Toolbar
|
|
94
|
+
/** Toolbar preset name from {@link RichEditToolbars}, or an explicit toolbar. */
|
|
43
95
|
toolbar = "default";
|
|
96
|
+
/** Emits the editor's HTML on every change. Only wired for an internal `fc`. */
|
|
44
97
|
changed = new EventEmitter();
|
|
45
|
-
/** Field manager */
|
|
98
|
+
/** Field manager for {@link field}; only resolved when `fc` is created here. */
|
|
46
99
|
manager;
|
|
47
|
-
/**
|
|
100
|
+
/** Whether the manager declares the field required. */
|
|
48
101
|
required;
|
|
102
|
+
/** Underlying ngx-editor instance; destroyed with the component. */
|
|
49
103
|
editor;
|
|
104
|
+
/** Unused; kept for backwards compatibility. */
|
|
50
105
|
html = "";
|
|
106
|
+
/** Toolbar actually rendered, resolved from {@link toolbar}. */
|
|
51
107
|
realToolbar;
|
|
108
|
+
/**
|
|
109
|
+
* Create the editor, resolve the toolbar, and — unless a `[fc]` was given —
|
|
110
|
+
* build a control seeded from the model field, enabled per `[edit]`, and
|
|
111
|
+
* relaying changes to {@link changed}.
|
|
112
|
+
*/
|
|
52
113
|
ngOnInit() {
|
|
53
114
|
this.editor = new Editor();
|
|
54
115
|
if (this.toolbar === "light" || this.toolbar === "default") {
|
|
@@ -77,10 +138,15 @@ class RicheditComponent {
|
|
|
77
138
|
this.fc.valueChanges.subscribe((v) => this.changed.emit(v));
|
|
78
139
|
}
|
|
79
140
|
}
|
|
141
|
+
/** Destroy the ngx-editor instance to release its resources. */
|
|
80
142
|
// make sure to destory the editor
|
|
81
143
|
ngOnDestroy() {
|
|
82
144
|
this.editor.destroy();
|
|
83
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Switch between read-only and editing by enabling or disabling the control.
|
|
148
|
+
* Forces read-only when `[editable]` is false.
|
|
149
|
+
*/
|
|
84
150
|
toggleEdit() {
|
|
85
151
|
if (this.editable) {
|
|
86
152
|
if (this.edit) {
|
|
@@ -97,6 +163,14 @@ class RicheditComponent {
|
|
|
97
163
|
this.edit = false;
|
|
98
164
|
}
|
|
99
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Write the editor content back to the model field, and persist it in `dd`
|
|
168
|
+
* mode only.
|
|
169
|
+
*
|
|
170
|
+
* In `inline` / `form` modes the model is updated in memory but no request is
|
|
171
|
+
* sent, leaving the save to the surrounding form. Does nothing without both a
|
|
172
|
+
* `[model]` and a `[field]`.
|
|
173
|
+
*/
|
|
100
174
|
async save() {
|
|
101
175
|
if (this.model && this.field) {
|
|
102
176
|
this.model.setFV(this.field, this.fc.value);
|
|
@@ -105,10 +179,10 @@ class RicheditComponent {
|
|
|
105
179
|
}
|
|
106
180
|
}
|
|
107
181
|
}
|
|
108
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0
|
|
109
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0
|
|
182
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: RicheditComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
183
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: RicheditComponent, isStandalone: true, selector: "data-richedit", inputs: { model: "model", field: "field", editable: "editable", edit: "edit", mode: "mode", hideLabel: "hideLabel", hideButton: "hideButton", fc: "fc", toolbar: "toolbar" }, outputs: { changed: "changed" }, ngImport: i0, template: "\n<ng-template #editorTemplate>\n <div class=\"NgxEditor__Wrapper\">\n @if (edit && toolbar!=='none') {\n <ngx-editor-menu [editor]=\"editor\" [toolbar]=\"realToolbar\"></ngx-editor-menu>\n }\n <ngx-editor [editor]=\"editor\" [formControl]=\"fc\" [placeholder]=\"''\"></ngx-editor>\n </div>\n @if (edit && !hideButton) {\n <button class=\"btn btn-primary btn-sm w-100 mt-1\" (click)=\"save()\"><i class=\"bi bi-save me-2\"></i>Enregistrer</button>\n }\n</ng-template>\n<ng-template #titleTpl>\n <ng-content></ng-content>\n</ng-template>\n<!-- Dd display-->\n@if (mode==='dd') {\n @if (!hideLabel) {\n <dt [class.required]=\"required\"><span class=\"editable\" (click)=\"toggleEdit()\" role=\"button\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container></span></dt>\n }\n <dd [class.mb-0]=\"hideLabel\">\n <ng-container *ngTemplateOutlet=\"editorTemplate\"></ng-container>\n </dd>\n }\n <!-- Inline display-->\n @if (mode==='inline') {\n @if (!hideLabel) {\n <label [class.required]=\"required\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"editorTemplate\"></ng-container>\n }\n <!-- Form display-->\n @if (mode==='form') {\n @if (!hideLabel) {\n <label [class.required]=\"required\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"editorTemplate\"></ng-container>\n }", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: NgxEditorModule }, { kind: "component", type: i2.NgxEditorComponent, selector: "ngx-editor", inputs: ["editor", "outputFormat", "placeholder"], outputs: ["focusOut", "focusIn"] }, { kind: "component", type: i2.NgxEditorMenuComponent, selector: "ngx-editor-menu", inputs: ["toolbar", "colorPresets", "disabled", "editor", "customMenuRef", "dropdownPlacement"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
110
184
|
}
|
|
111
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0
|
|
185
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: RicheditComponent, decorators: [{
|
|
112
186
|
type: Component,
|
|
113
187
|
args: [{ selector: "data-richedit", imports: [CommonModule, NgxEditorModule, ReactiveFormsModule], changeDetection: ChangeDetectionStrategy.Eager, template: "\n<ng-template #editorTemplate>\n <div class=\"NgxEditor__Wrapper\">\n @if (edit && toolbar!=='none') {\n <ngx-editor-menu [editor]=\"editor\" [toolbar]=\"realToolbar\"></ngx-editor-menu>\n }\n <ngx-editor [editor]=\"editor\" [formControl]=\"fc\" [placeholder]=\"''\"></ngx-editor>\n </div>\n @if (edit && !hideButton) {\n <button class=\"btn btn-primary btn-sm w-100 mt-1\" (click)=\"save()\"><i class=\"bi bi-save me-2\"></i>Enregistrer</button>\n }\n</ng-template>\n<ng-template #titleTpl>\n <ng-content></ng-content>\n</ng-template>\n<!-- Dd display-->\n@if (mode==='dd') {\n @if (!hideLabel) {\n <dt [class.required]=\"required\"><span class=\"editable\" (click)=\"toggleEdit()\" role=\"button\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container></span></dt>\n }\n <dd [class.mb-0]=\"hideLabel\">\n <ng-container *ngTemplateOutlet=\"editorTemplate\"></ng-container>\n </dd>\n }\n <!-- Inline display-->\n @if (mode==='inline') {\n @if (!hideLabel) {\n <label [class.required]=\"required\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"editorTemplate\"></ng-container>\n }\n <!-- Form display-->\n @if (mode==='form') {\n @if (!hideLabel) {\n <label [class.required]=\"required\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"editorTemplate\"></ng-container>\n }" }]
|
|
114
188
|
}], propDecorators: { model: [{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"solidev-data-richedit.mjs","sources":["../../../projects/data/richedit/richedit.component.ts","../../../projects/data/richedit/richedit.component.html","../../../projects/data/richedit/solidev-data-richedit.ts"],"sourcesContent":["import {\n Component,\n EventEmitter,\n Input,\n OnDestroy,\n OnInit,\n Output,\n ChangeDetectionStrategy,\n} from \"@angular/core\";\nimport { CommonModule } from \"@angular/common\";\nimport { Editor, NgxEditorModule, Toolbar } from \"ngx-editor\";\nimport { FormControl, ReactiveFormsModule } from \"@angular/forms\";\nimport { BaseFieldManager, DataModel } from \"@solidev/data\";\nimport { firstValueFrom } from \"rxjs\";\n\nexport const RichEditToolbars: { [index: string]: Toolbar } = {\n default: [\n [\"bold\", \"italic\"],\n [\"underline\", \"strike\"],\n [\"code\", \"blockquote\"],\n [\"ordered_list\", \"bullet_list\"],\n [{ heading: [\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"] }],\n [\"link\"],\n [\"text_color\", \"background_color\"],\n [\"align_left\", \"align_center\", \"align_right\", \"align_justify\"],\n ],\n light: [\n [\"bold\", \"italic\", \"underline\"],\n [\"ordered_list\", \"bullet_list\"],\n [\"text_color\"],\n [\"align_left\", \"align_center\", \"align_right\", \"align_justify\"],\n ],\n none: [],\n};\n\n@Component({\n selector: \"data-richedit\",\n imports: [CommonModule, NgxEditorModule, ReactiveFormsModule],\n templateUrl: \"./richedit.component.html\",\n changeDetection: ChangeDetectionStrategy.Eager,\n styleUrls: [\"./richedit.component.sass\"],\n})\nexport class RicheditComponent<FT, T extends DataModel>\n implements OnInit, OnDestroy\n{\n @Input() public model?: T;\n @Input() public field?: string;\n @Input() public editable: boolean = true;\n @Input() public edit: boolean = true;\n @Input() public mode: \"dd\" | \"inline\" | \"form\" = \"dd\";\n /** Hide label (for inline forms) */\n @Input() public hideLabel: boolean = false;\n /** Hide button (for changed usage) */\n @Input() public hideButton: boolean = false;\n /** Form control, from outside or created */\n @Input() public fc!: FormControl<string | null>;\n /** Toolbar mode */\n @Input() public toolbar: \"none\" | \"default\" | \"light\" | Toolbar = \"default\";\n @Output() public changed = new EventEmitter<string | null>();\n /** Field manager */\n public manager?: BaseFieldManager<FT>;\n /** Computed required */\n public required!: boolean;\n public editor!: Editor;\n public html = \"\";\n public realToolbar!: Toolbar;\n\n ngOnInit(): void {\n this.editor = new Editor();\n if (this.toolbar === \"light\" || this.toolbar === \"default\") {\n this.realToolbar = RichEditToolbars[this.toolbar];\n } else if (this.toolbar === \"none\") {\n } else {\n this.realToolbar = this.toolbar as Toolbar;\n }\n if (!this.fc) {\n this.fc = new FormControl<string>(\"\");\n if (this.model && this.field) {\n this.manager = this.model.FM(this.field);\n this.required = this.manager?.required || false;\n this.fc.setValue((this.model as any)[this.field] || \"\", {\n emitEvent: false,\n });\n }\n if (this.edit) {\n this.fc.enable();\n } else {\n this.fc.disable();\n }\n this.fc.valueChanges.subscribe((v) => this.changed.emit(v));\n }\n }\n\n // make sure to destory the editor\n ngOnDestroy(): void {\n this.editor.destroy();\n }\n\n toggleEdit() {\n if (this.editable) {\n if (this.edit) {\n this.fc.disable();\n this.edit = false;\n } else {\n this.fc.enable();\n this.edit = true;\n }\n } else {\n this.fc.disable();\n this.edit = false;\n }\n }\n\n public async save() {\n if (this.model && this.field) {\n this.model.setFV(this.field, this.fc.value);\n if (this.mode === \"dd\") {\n await firstValueFrom(\n this.model.update([this.field], { updateModel: true }),\n );\n }\n }\n }\n}\n","\n<ng-template #editorTemplate>\n <div class=\"NgxEditor__Wrapper\">\n @if (edit && toolbar!=='none') {\n <ngx-editor-menu [editor]=\"editor\" [toolbar]=\"realToolbar\"></ngx-editor-menu>\n }\n <ngx-editor [editor]=\"editor\" [formControl]=\"fc\" [placeholder]=\"''\"></ngx-editor>\n </div>\n @if (edit && !hideButton) {\n <button class=\"btn btn-primary btn-sm w-100 mt-1\" (click)=\"save()\"><i class=\"bi bi-save me-2\"></i>Enregistrer</button>\n }\n</ng-template>\n<ng-template #titleTpl>\n <ng-content></ng-content>\n</ng-template>\n<!-- Dd display-->\n@if (mode==='dd') {\n @if (!hideLabel) {\n <dt [class.required]=\"required\"><span class=\"editable\" (click)=\"toggleEdit()\" role=\"button\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container></span></dt>\n }\n <dd [class.mb-0]=\"hideLabel\">\n <ng-container *ngTemplateOutlet=\"editorTemplate\"></ng-container>\n </dd>\n }\n <!-- Inline display-->\n @if (mode==='inline') {\n @if (!hideLabel) {\n <label [class.required]=\"required\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"editorTemplate\"></ng-container>\n }\n <!-- Form display-->\n @if (mode==='form') {\n @if (!hideLabel) {\n <label [class.required]=\"required\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"editorTemplate\"></ng-container>\n }","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"solidev-data-richedit.mjs","sources":["../../../projects/data/richedit/richedit.component.ts","../../../projects/data/richedit/richedit.component.html","../../../projects/data/richedit/solidev-data-richedit.ts"],"sourcesContent":["import {\n Component,\n EventEmitter,\n Input,\n OnDestroy,\n OnInit,\n Output,\n ChangeDetectionStrategy,\n} from \"@angular/core\";\nimport { CommonModule } from \"@angular/common\";\nimport { Editor, NgxEditorModule, Toolbar } from \"ngx-editor\";\nimport { FormControl, ReactiveFormsModule } from \"@angular/forms\";\nimport { BaseFieldManager, DataModel } from \"@solidev/data\";\nimport { firstValueFrom } from \"rxjs\";\n\n/**\n * Named toolbar presets for {@link RicheditComponent}, selectable through its\n * `toolbar` input.\n *\n * - `default`: the full set — marks, code and quote, lists, headings, links,\n * colours and alignment.\n * - `light`: a reduced set — basic marks, lists, text colour and alignment.\n * - `none`: empty; the component hides the menu bar entirely for this value.\n *\n * A `Toolbar` can also be passed directly to the component when neither preset\n * fits.\n */\nexport const RichEditToolbars: { [index: string]: Toolbar } = {\n default: [\n [\"bold\", \"italic\"],\n [\"underline\", \"strike\"],\n [\"code\", \"blockquote\"],\n [\"ordered_list\", \"bullet_list\"],\n [{ heading: [\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"] }],\n [\"link\"],\n [\"text_color\", \"background_color\"],\n [\"align_left\", \"align_center\", \"align_right\", \"align_justify\"],\n ],\n light: [\n [\"bold\", \"italic\", \"underline\"],\n [\"ordered_list\", \"bullet_list\"],\n [\"text_color\"],\n [\"align_left\", \"align_center\", \"align_right\", \"align_justify\"],\n ],\n none: [],\n};\n\n@Component({\n selector: \"data-richedit\",\n imports: [CommonModule, NgxEditorModule, ReactiveFormsModule],\n templateUrl: \"./richedit.component.html\",\n changeDetection: ChangeDetectionStrategy.Eager,\n styleUrls: [\"./richedit.component.sass\"],\n})\n/**\n * Rich text editor for a model field, built on `ngx-editor`.\n *\n * Shipped as a separate entry point (`@solidev/data/richedit`) so that the\n * `ngx-editor` dependency stays optional for consumers who do not need it. It\n * follows the same `dd` / `inline` / `form` layout convention as\n * `<data-dispedit>`, and can also stand in as the `quill` editor slot.\n *\n * Unlike `<data-dispedit>`, saving is explicit: the value is written back and\n * persisted only when {@link save} runs, from the built-in save button — and\n * only in `dd` mode. In the other modes {@link save} updates the model in memory\n * but issues no request, leaving persistence to the surrounding form. Callers\n * that supply their own `[fc]` can instead track {@link changed}.\n *\n * @example\n * ```html\n * <data-richedit [model]=\"thing\" field=\"description\">Description</data-richedit>\n * ```\n */\nexport class RicheditComponent<FT, T extends DataModel>\n implements OnInit, OnDestroy\n{\n /** Model instance holding the field. */\n @Input() public model?: T;\n /** Name of the rich text field to edit. */\n @Input() public field?: string;\n /** Whether {@link toggleEdit} is allowed to enable editing. */\n @Input() public editable: boolean = true;\n /** Whether the editor is currently enabled. Defaults to true. */\n @Input() public edit: boolean = true;\n /**\n * Layout, and whether {@link save} persists.\n *\n * `dd` renders a `<dt>`/`<dd>` block and is the only mode that saves to the\n * API; `inline` and `form` render a label plus the editor and leave saving to\n * the caller.\n */\n @Input() public mode: \"dd\" | \"inline\" | \"form\" = \"dd\";\n /** Hide label (for inline forms) */\n @Input() public hideLabel: boolean = false;\n /**\n * Hide the built-in save button, for callers driving persistence themselves\n * from the {@link changed} output.\n */\n @Input() public hideButton: boolean = false;\n /**\n * Form control backing the editor.\n *\n * When supplied, the component uses it as-is and skips its own setup — no\n * field manager lookup, no seeding from the model, and no {@link changed}\n * emissions. When absent, one is created and wired up from `[model]` and\n * `[field]`.\n */\n @Input() public fc!: FormControl<string | null>;\n /** Toolbar preset name from {@link RichEditToolbars}, or an explicit toolbar. */\n @Input() public toolbar: \"none\" | \"default\" | \"light\" | Toolbar = \"default\";\n /** Emits the editor's HTML on every change. Only wired for an internal `fc`. */\n @Output() public changed = new EventEmitter<string | null>();\n /** Field manager for {@link field}; only resolved when `fc` is created here. */\n public manager?: BaseFieldManager<FT>;\n /** Whether the manager declares the field required. */\n public required!: boolean;\n /** Underlying ngx-editor instance; destroyed with the component. */\n public editor!: Editor;\n /** Unused; kept for backwards compatibility. */\n public html = \"\";\n /** Toolbar actually rendered, resolved from {@link toolbar}. */\n public realToolbar!: Toolbar;\n\n /**\n * Create the editor, resolve the toolbar, and — unless a `[fc]` was given —\n * build a control seeded from the model field, enabled per `[edit]`, and\n * relaying changes to {@link changed}.\n */\n ngOnInit(): void {\n this.editor = new Editor();\n if (this.toolbar === \"light\" || this.toolbar === \"default\") {\n this.realToolbar = RichEditToolbars[this.toolbar];\n } else if (this.toolbar === \"none\") {\n } else {\n this.realToolbar = this.toolbar as Toolbar;\n }\n if (!this.fc) {\n this.fc = new FormControl<string>(\"\");\n if (this.model && this.field) {\n this.manager = this.model.FM(this.field);\n this.required = this.manager?.required || false;\n this.fc.setValue((this.model as any)[this.field] || \"\", {\n emitEvent: false,\n });\n }\n if (this.edit) {\n this.fc.enable();\n } else {\n this.fc.disable();\n }\n this.fc.valueChanges.subscribe((v) => this.changed.emit(v));\n }\n }\n\n /** Destroy the ngx-editor instance to release its resources. */\n // make sure to destory the editor\n ngOnDestroy(): void {\n this.editor.destroy();\n }\n\n /**\n * Switch between read-only and editing by enabling or disabling the control.\n * Forces read-only when `[editable]` is false.\n */\n toggleEdit() {\n if (this.editable) {\n if (this.edit) {\n this.fc.disable();\n this.edit = false;\n } else {\n this.fc.enable();\n this.edit = true;\n }\n } else {\n this.fc.disable();\n this.edit = false;\n }\n }\n\n /**\n * Write the editor content back to the model field, and persist it in `dd`\n * mode only.\n *\n * In `inline` / `form` modes the model is updated in memory but no request is\n * sent, leaving the save to the surrounding form. Does nothing without both a\n * `[model]` and a `[field]`.\n */\n public async save() {\n if (this.model && this.field) {\n this.model.setFV(this.field, this.fc.value);\n if (this.mode === \"dd\") {\n await firstValueFrom(\n this.model.update([this.field], { updateModel: true }),\n );\n }\n }\n }\n}\n","\n<ng-template #editorTemplate>\n <div class=\"NgxEditor__Wrapper\">\n @if (edit && toolbar!=='none') {\n <ngx-editor-menu [editor]=\"editor\" [toolbar]=\"realToolbar\"></ngx-editor-menu>\n }\n <ngx-editor [editor]=\"editor\" [formControl]=\"fc\" [placeholder]=\"''\"></ngx-editor>\n </div>\n @if (edit && !hideButton) {\n <button class=\"btn btn-primary btn-sm w-100 mt-1\" (click)=\"save()\"><i class=\"bi bi-save me-2\"></i>Enregistrer</button>\n }\n</ng-template>\n<ng-template #titleTpl>\n <ng-content></ng-content>\n</ng-template>\n<!-- Dd display-->\n@if (mode==='dd') {\n @if (!hideLabel) {\n <dt [class.required]=\"required\"><span class=\"editable\" (click)=\"toggleEdit()\" role=\"button\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container></span></dt>\n }\n <dd [class.mb-0]=\"hideLabel\">\n <ng-container *ngTemplateOutlet=\"editorTemplate\"></ng-container>\n </dd>\n }\n <!-- Inline display-->\n @if (mode==='inline') {\n @if (!hideLabel) {\n <label [class.required]=\"required\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"editorTemplate\"></ng-container>\n }\n <!-- Form display-->\n @if (mode==='form') {\n @if (!hideLabel) {\n <label [class.required]=\"required\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"editorTemplate\"></ng-container>\n }","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|