@aster-editor/angular 0.4.2 → 0.5.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/README.md +145 -26
- package/fesm2022/aster-editor-angular.mjs +262 -83
- package/fesm2022/aster-editor-angular.mjs.map +1 -1
- package/package.json +4 -4
- package/types/aster-editor-angular.d.ts +180 -24
package/README.md
CHANGED
|
@@ -92,22 +92,26 @@ export class ArticleFormComponent {
|
|
|
92
92
|
|
|
93
93
|
## 🎛️ Inputs et outputs
|
|
94
94
|
|
|
95
|
-
| Input | Type
|
|
96
|
-
| ----------------------- |
|
|
97
|
-
| `toolbar` | `ToolbarConfig`
|
|
98
|
-
| `theme` | `AsterTheme`
|
|
99
|
-
| `locale` | `AsterLocale`
|
|
100
|
-
| `placeholder` | `string \| undefined`
|
|
101
|
-
| `readonly` | `boolean`
|
|
102
|
-
| `config` | `Partial<AsterUiOptions>`
|
|
103
|
-
| `wordPaste` | `boolean`
|
|
104
|
-
| `ariaLabel` | `string \| undefined`
|
|
105
|
-
| `ariaLabelledBy` | `string \| undefined`
|
|
106
|
-
| `variables` | `readonly AsterVariable[]`
|
|
107
|
-
| `variableValues` | `VariableValues`
|
|
108
|
-
| `variableResolver` | `VariableResolver`
|
|
109
|
-
| `allowUnknownVariables` | `boolean`
|
|
110
|
-
| `variableSuggestion` | `boolean`
|
|
95
|
+
| Input | Type | Défaut effectif | À chaud |
|
|
96
|
+
| ----------------------- | ----------------------------- | --------------- | ----------------------------------------------------------- |
|
|
97
|
+
| `toolbar` | `ToolbarConfig` | `'standard'` | Remonte l'éditeur : contenu conservé, historique perdu |
|
|
98
|
+
| `theme` | `AsterTheme` | `'light'` | Oui |
|
|
99
|
+
| `locale` | `AsterLocale` | `'fr'` | Oui, toolbar comprise |
|
|
100
|
+
| `placeholder` | `string \| undefined` | chaîne i18n | Oui |
|
|
101
|
+
| `readonly` | `boolean` | `false` | Oui, cumulé avec le `disabled` du formulaire |
|
|
102
|
+
| `config` | `Partial<AsterUiOptions>` | `{}` | Seules les clés ci-dessus sont relues ; le reste au montage |
|
|
103
|
+
| `wordPaste` | `boolean` | `true` | Oui |
|
|
104
|
+
| `ariaLabel` | `string \| undefined` | aucun | Oui : `aria-label` sur le contenteditable |
|
|
105
|
+
| `ariaLabelledBy` | `string \| undefined` | aucun | Oui : `aria-labelledby` sur le contenteditable |
|
|
106
|
+
| `variables` | `readonly AsterVariable[]` | aucune | Oui : `editor.variables.define(...)`, fusion par nom |
|
|
107
|
+
| `variableValues` | `VariableValues` | aucune | Oui : `editor.variables.setValues(...)`, fusion par nom |
|
|
108
|
+
| `variableResolver` | `VariableResolver` | aucun | Non, lu au montage |
|
|
109
|
+
| `allowUnknownVariables` | `boolean` | `true` | Non, lu au montage |
|
|
110
|
+
| `variableSuggestion` | `boolean` | `true` | Non, lu au montage |
|
|
111
|
+
| `importFidelity` | `'styled' \| 'semantic'` | `'styled'` | Lu à chaque `importDocx` |
|
|
112
|
+
| `exportInTemplate` | `boolean` | `true` | Lu à chaque `exportDocx` |
|
|
113
|
+
| `pageView` | `boolean \| PageViewOptions` | `false` | Oui : la vue page est remontée |
|
|
114
|
+
| `layout` | `DocumentLayout \| undefined` | aucune | Oui : vue page et export, voir « Fidélité Word » |
|
|
111
115
|
|
|
112
116
|
Un contenteditable ne s'associe pas à un `<label for>` : nomme la zone de saisie avec
|
|
113
117
|
`ariaLabel`, ou `ariaLabelledBy` pointant sur l'`id` de ton libellé visible. Les deux sont
|
|
@@ -127,6 +131,7 @@ dans l'éditeur.
|
|
|
127
131
|
| `focused` | `void` | Focus du contenu |
|
|
128
132
|
| `blurred` | `void` | Focus perdu vers l'extérieur de l'éditeur (pas vers la toolbar) ; marque le contrôle `touched` |
|
|
129
133
|
| `variablesChanged` | `VariableValues` | Valeurs ou déclarations de variables changées (`setValues`, `define`, `resolve`, import) |
|
|
134
|
+
| `layoutChanged` | `DocumentLayout` | Mise en page effective changée : import, input `layout`, dialogue « Mise en page » |
|
|
130
135
|
|
|
131
136
|
API publique du composant :
|
|
132
137
|
|
|
@@ -134,10 +139,17 @@ API publique du composant :
|
|
|
134
139
|
`commands.run(...)`, `tiptap`…), `null` avant le montage et après la destruction.
|
|
135
140
|
- `variableNames` : `Signal<readonly string[]>`, noms des variables présentes dans le document,
|
|
136
141
|
relus sur `change` et `variablesChange`.
|
|
142
|
+
- `importedTemplate` : `Signal<ArrayBuffer | null>`, le `.docx` du dernier import, intact.
|
|
143
|
+
- `layout` : `Signal<DocumentLayout | null>`, mise en page effective (input `layout`, sinon
|
|
144
|
+
import ou dialogue « Mise en page »).
|
|
145
|
+
- `lostObjects` : `Signal<readonly DocxLostObject[]>`, objets Word non représentables du dernier
|
|
146
|
+
import.
|
|
137
147
|
- `getHTML(options?)` : HTML du document, `{ variables: 'resolve' }` substitue les valeurs.
|
|
138
148
|
- `resolveVariables()` : appelle le résolveur pour les variables sans valeur, rend les valeurs.
|
|
139
149
|
- `importDocx(file: File): Promise<DocxImportResult>` : voir plus bas.
|
|
140
|
-
- `exportDocx(options?: DocxExportOptions): Promise<Blob>` : voir « Variables et modèles
|
|
150
|
+
- `exportDocx(options?: DocxExportOptions): Promise<Blob>` : voir « Variables et modèles » et
|
|
151
|
+
« Fidélité Word ».
|
|
152
|
+
- `compareWithTemplate(): Promise<FidelityReport | null>` : voir « Fidélité Word ».
|
|
141
153
|
|
|
142
154
|
La vue source, le plein écran et le menu contextuel de table de `@aster-editor/ui` sont montés
|
|
143
155
|
par `createAster` et donc disponibles dans le composant : via les widgets `source` /
|
|
@@ -151,7 +163,10 @@ Types réexportés pour ne pas dépendre de `core`, `ui` ou `word` : `AsterEdito
|
|
|
151
163
|
`PasteTransformer`, `ToolbarConfig`, `ToolbarPreset`, `UploadHandler`, `AsterVariable`,
|
|
152
164
|
`VariableValues`, `VariableResolver`, `VariableSource`, `VariableRenderMode`, `GetHtmlOptions`,
|
|
153
165
|
`AsterUiOptions`, `DocxImportResult`, `DocxImportWarning`, `DocxExportOptions`,
|
|
154
|
-
`DocxExportVariableOptions`, `DocxExportWarning`, `DocxVariableMode
|
|
166
|
+
`DocxExportVariableOptions`, `DocxExportWarning`, `DocxVariableMode`, et pour la fidélité Word :
|
|
167
|
+
`DocumentLayout`, `PageSetup`, `PageMargins`, `HeaderFooterSet`, `HeaderFooterContent`,
|
|
168
|
+
`PageViewOptions`, `PageViewInput`, `DocxImportFidelity`, `DocxLostObject`, `FidelityReport`,
|
|
169
|
+
`FidelityProperty`, `FidelityPropertyGroup`, `FidelityExample`.
|
|
155
170
|
|
|
156
171
|
Un input dédié laissé à `undefined` n'écrase pas la valeur venue de `config` ou de
|
|
157
172
|
`provideAster`. Passe des objets **stables** dans `config` (champ de classe, pas un littéral dans
|
|
@@ -234,10 +249,16 @@ export class ImportComponent {
|
|
|
234
249
|
}
|
|
235
250
|
```
|
|
236
251
|
|
|
237
|
-
`importDocx` charge `@aster-editor/word` par `import()` :
|
|
238
|
-
n'est téléchargé qu'au premier import. Le contenu est remplacé avec émission de `change`, un
|
|
252
|
+
`importDocx` charge `@aster-editor/word` par `import()` : le lecteur reste dans un chunk séparé
|
|
253
|
+
et n'est téléchargé qu'au premier import. Le contenu est remplacé avec émission de `change`, un
|
|
239
254
|
formulaire lié voit donc la nouvelle valeur. Le handler d'upload de l'éditeur est réutilisé pour
|
|
240
|
-
les images embarquées. Appelé avant le montage, il rejette avec une erreur explicite.
|
|
255
|
+
les images embarquées. Appelé avant le montage, il rejette avec une erreur explicite. Par défaut
|
|
256
|
+
l'import est **stylé** (`importFidelity: 'styled'`) : polices, tailles, couleurs, interlignes,
|
|
257
|
+
bordures, tableaux et listes sont conservés, et le composant garde le gabarit et la mise en page
|
|
258
|
+
du document, voir « Fidélité Word ». `importFidelity: 'semantic'` retrouve le HTML propre de
|
|
259
|
+
mammoth (comportement 0.4.x), sans mise en page. Attention à la différence avec la lib : le
|
|
260
|
+
composant choisit `styled` pour toi, alors que `importDocx` de `@aster-editor/word` appelé
|
|
261
|
+
directement reste en `semantic` en 0.5.0 (bascule prévue en 0.6).
|
|
241
262
|
|
|
242
263
|
> ⚠️ **Vite à froid** : sur `ng serve`, le tout premier `importDocx` d'une session déclenche
|
|
243
264
|
> l'optimisation de `mammoth` par Vite et **recharge la page** en plein import, sans erreur.
|
|
@@ -367,6 +388,101 @@ Pour un aperçu résolu sans export, `editor().getHTML({ variables: 'resolve' })
|
|
|
367
388
|
avec les valeurs substituées en texte brut ; `resolveVariables()` appelle d'abord le résolveur
|
|
368
389
|
pour les variables manquantes.
|
|
369
390
|
|
|
391
|
+
## 📐 Fidélité Word
|
|
392
|
+
|
|
393
|
+
Objectif : un `.docx` importé **ressort comme l'original** partout où l'utilisateur n'a rien
|
|
394
|
+
touché. Le composant enchaîne les quatre briques de `@aster-editor/word` sans que l'application
|
|
395
|
+
ait à les connaître.
|
|
396
|
+
|
|
397
|
+
### Import stylé et mise en page
|
|
398
|
+
|
|
399
|
+
`importDocx` (mode `styled` par défaut) conserve trois choses dans des signals :
|
|
400
|
+
|
|
401
|
+
- `importedTemplate()` : le `.docx` d'origine, intact (`ArrayBuffer`) ;
|
|
402
|
+
- `layout()` : la mise en page lue dans la dernière section (`DocumentLayout` : format,
|
|
403
|
+
orientation, marges, colonnes, police par défaut, langue, en-têtes et pieds en HTML stylé) ;
|
|
404
|
+
- `lostObjects()` : les objets Word que l'éditeur ne sait pas représenter (zones de texte, formes,
|
|
405
|
+
équations, commentaires, révisions…), avec leur position (`body`, `header`…).
|
|
406
|
+
|
|
407
|
+
### Vue page
|
|
408
|
+
|
|
409
|
+
```html
|
|
410
|
+
<aster-editor [pageView]="true" (layoutChanged)="layout = $event" />
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
`pageView` (`true`, ou `{ layout, pagination, showHeadersFooters }`) rend une feuille aux
|
|
414
|
+
dimensions et marges de `layout()`, avec les en-têtes et pieds `default` en lecture seule
|
|
415
|
+
au-dessus et au-dessous du corps et une pagination **approximative** (coupures « Page n sur N »
|
|
416
|
+
posées en décorations, jamais dans le HTML) ; importe `@aster-editor/ui/themes/page.css` (inclus
|
|
417
|
+
dans `aster.css`). Après un import, la mise en page lue est appliquée à la vue page active. Le
|
|
418
|
+
bouton « Mise en page » de la toolbar (widget `pageLayout`, preset `full` ; `standard` n'a que
|
|
419
|
+
l'interligne et les retraits) peut l'activer, la modifier ou la retirer : le composant relit alors
|
|
420
|
+
la mise en page, `layout()` suit et `layoutChanged` est émis. La vue page survit à un remontage
|
|
421
|
+
(`toolbar`). `config.pageView` est honoré, l'input dédié prime.
|
|
422
|
+
|
|
423
|
+
L'input `layout` impose une mise en page : il prime sur celle de l'import et sur celle du
|
|
424
|
+
dialogue, se répercute sur la vue page active et sur `exportDocx`. Comme il masque ce que le
|
|
425
|
+
dialogue pose ensuite, tiens-le à jour depuis `layoutChanged` (`[layout]="layout()"` +
|
|
426
|
+
`(layoutChanged)="layout.set($event)"`) : c'est ce que fait la page « Modèle » de la démo pour
|
|
427
|
+
ses éditeurs d'en-tête et de pied.
|
|
428
|
+
|
|
429
|
+
### Export dans le gabarit
|
|
430
|
+
|
|
431
|
+
```ts
|
|
432
|
+
const blob = await this.editor().exportDocx({ variables: { mode: 'fields' } });
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
Sans `options.template`, `exportDocx` réutilise `importedTemplate()` dès qu'il existe
|
|
436
|
+
(`exportInTemplate`, vrai par défaut) : styles, numérotation, thème, polices, en-têtes, pieds,
|
|
437
|
+
sections et propriétés du document ressortent **identiques à l'octet**, seul le corps est
|
|
438
|
+
régénéré depuis l'éditeur. La mise en page effective (`layout()`) est transmise elle aussi : avec
|
|
439
|
+
un gabarit, elle ne sert qu'à régénérer un en-tête ou un pied dont le HTML a changé (le reste
|
|
440
|
+
vient du gabarit) ; sans gabarit, elle pilote l'export neuf (format, marges, en-têtes, pieds,
|
|
441
|
+
police par défaut). `exportInTemplate: false` force l'export neuf, un `options.template`
|
|
442
|
+
explicite reste honoré.
|
|
443
|
+
|
|
444
|
+
### Rapport de fidélité
|
|
445
|
+
|
|
446
|
+
```ts
|
|
447
|
+
const report = await this.editor().compareWithTemplate();
|
|
448
|
+
if (report) console.log(report.score, report.properties, report.changedParts);
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
`compareWithTemplate()` exporte le document courant dans son gabarit (valeurs du magasin de
|
|
452
|
+
variables, mise en page effective, comme `exportDocx({ template })`) puis le compare à
|
|
453
|
+
l'original propriété par propriété (`compareDocx` de `word`, chargé à la demande) : score
|
|
454
|
+
pondéré entre 0 et 1, propriétés conservées / perdues par groupe (`text`, `paragraph-style`,
|
|
455
|
+
`paragraph-format`, `run-format`, `list`, `table`, `image`, `note`, `header-footer`, `section`,
|
|
456
|
+
`package-part`) avec jusqu'à cinq exemples, parties du paquet identiques ou non, objets perdus,
|
|
457
|
+
notes. `null` sans gabarit importé. Un aller-retour sans modification vise un score ≥ 0,98 ;
|
|
458
|
+
c'est le critère d'acceptation, pas une impression visuelle. Scores mesurés sur les fixtures et
|
|
459
|
+
le document client dans [`fixtures/docx/README.md`](../../fixtures/docx/README.md) (1 pour les
|
|
460
|
+
fixtures 11, 13 et 15 ; 0,889 pour le document client, dont les trois sections ressortent en
|
|
461
|
+
une seule).
|
|
462
|
+
|
|
463
|
+
### Limites
|
|
464
|
+
|
|
465
|
+
- **Pas de pagination exacte** : la vue page donne le format, les marges, les en-têtes et pieds,
|
|
466
|
+
les sauts de page explicites, et une pagination **approximative** (coupures visuelles, numéros
|
|
467
|
+
de page, tableaux et listes jamais coupés) qui ne garantit pas le même nombre de pages que Word.
|
|
468
|
+
- **Objets non représentables** : conservés tels quels dans le gabarit quand ils sont hors du
|
|
469
|
+
corps (en-têtes, pieds), remplacés par leur texte et listés dans `lostObjects()` quand ils sont
|
|
470
|
+
dans le corps édité.
|
|
471
|
+
- **Sections multiples** : seule la dernière section du document est lue (`layout()`) et
|
|
472
|
+
survit à l'export dans le gabarit ; un document à plusieurs formats de page ressort avec un
|
|
473
|
+
seul.
|
|
474
|
+
- Avec un gabarit, les options `page`, `pageSize`, `header`, `footer`, `defaultFont`, `lang`,
|
|
475
|
+
`headingStyles` de l'export sont ignorées, ainsi que `layout.page`, `layout.columns`,
|
|
476
|
+
`layout.titlePage`, `layout.defaultFont` et `layout.lang` : le gabarit les porte (JSDoc de
|
|
477
|
+
`template` dans `packages/word/src/export/types.ts`). `variables`, `toc`, `numberedHeadings`,
|
|
478
|
+
`title`, `creator`, `layout.headers` et `layout.footers` s'appliquent.
|
|
479
|
+
- Seuls les en-têtes et pieds `default` sont affichés par la vue page ; `first` et `even` sont
|
|
480
|
+
conservés dans le gabarit et dans `layout()`. Un en-tête ou pied édité via `[layout]` est
|
|
481
|
+
régénéré par l'export neuf et perd ce que celui-ci ne sait pas écrire (tableaux complexes,
|
|
482
|
+
images ancrées, champs autres que `PAGE` / `NUMPAGES` / fusion).
|
|
483
|
+
- Le chemin `semantic` ne renseigne ni `layout()` (objet vide) ni `lostObjects()` ; le gabarit
|
|
484
|
+
(`importedTemplate()`) est conservé sur les deux chemins.
|
|
485
|
+
|
|
370
486
|
## 📋 Collage depuis Word
|
|
371
487
|
|
|
372
488
|
Avec `wordPaste` (vrai par défaut), le composant charge `@aster-editor/word` au montage (jamais
|
|
@@ -380,15 +496,18 @@ et le collage reste brut. Passer `wordPaste` à `false` laisse le module chargé
|
|
|
380
496
|
`apps/demo` (`npm run demo`, http://localhost:4200) montre cinq pages chargées à la demande,
|
|
381
497
|
chacune avec son titre de document : `/` playground (preset, thème, langue, lecture seule,
|
|
382
498
|
compteurs, sortie HTML / JSON), `/form` reactive form avec validateur « requis » sur le contenu
|
|
383
|
-
riche, `/import` dépôt de `.docx` avec
|
|
384
|
-
|
|
385
|
-
`
|
|
499
|
+
riche, `/import` dépôt de `.docx` avec sélecteur `styled` / `semantic`, mise en page lue et
|
|
500
|
+
avertissements (exemple `13-styles-nommes.docx`), `/readonly` rendu figé en thème `night`,
|
|
501
|
+
`/template` modèle à variables (exemples `08-variables-braces.docx` et `16-sections.docx` servis
|
|
502
|
+
depuis `public/samples/`, formulaire généré, aperçu puces / résolu, vue page, en-tête et pied
|
|
503
|
+
éditables, export dans les trois modes ou dans le gabarit, rapport de fidélité, objets non
|
|
504
|
+
représentables).
|
|
386
505
|
|
|
387
506
|
## 🧪 Tests
|
|
388
507
|
|
|
389
508
|
```bash
|
|
390
|
-
npx ng test aster-angular --watch=false #
|
|
391
|
-
npx ng test demo --watch=false #
|
|
509
|
+
npx ng test aster-angular --watch=false # 49 tests du composant et de provideAster (importFidelity, exportInTemplate, pageView, layout, compareWithTemplate compris)
|
|
510
|
+
npx ng test demo --watch=false # 30 tests des pages de la démo (Import : sélecteur styled / semantic ; Modèle : vue page, en-tête et pied, rapport de fidélité, objets perdus)
|
|
392
511
|
npm run e2e -- --project=demo # tests Playwright sur la démo, page Modèle comprise (ng serve lancé par Playwright)
|
|
393
512
|
```
|
|
394
513
|
|
|
@@ -1,7 +1,68 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
2
|
import { ChangeDetectionStrategy, Component, DestroyRef, InjectionToken, afterNextRender, computed, effect, forwardRef, inject, input, makeEnvironmentProviders, output, signal, untracked, viewChild } from "@angular/core";
|
|
3
3
|
import { NG_VALUE_ACCESSOR } from "@angular/forms";
|
|
4
|
-
import {
|
|
4
|
+
import { PAGE_VIEW_CHANGE_EVENT, getActivePageView } from "@aster-editor/core";
|
|
5
|
+
import { createAster, pageViewApiOf } from "@aster-editor/ui";
|
|
6
|
+
function setOrRemoveAttribute(element, name, value) {
|
|
7
|
+
if (value === void 0 || value === "") element.removeAttribute(name);
|
|
8
|
+
else element.setAttribute(name, value);
|
|
9
|
+
}
|
|
10
|
+
function applyAriaAttributes(editor, label, labelledBy) {
|
|
11
|
+
setOrRemoveAttribute(editor.contentElement, "aria-label", label);
|
|
12
|
+
setOrRemoveAttribute(editor.contentElement, "aria-labelledby", labelledBy);
|
|
13
|
+
}
|
|
14
|
+
function applyPlaceholder(editor, placeholder) {
|
|
15
|
+
const tiptap = editor.tiptap;
|
|
16
|
+
const extension = tiptap.extensionManager.extensions.find((candidate) => candidate.name === "placeholder");
|
|
17
|
+
if (extension?.type !== "extension") return;
|
|
18
|
+
const factory = extension.config.addProseMirrorPlugins;
|
|
19
|
+
if (!factory) return;
|
|
20
|
+
const options = {
|
|
21
|
+
...extension.options,
|
|
22
|
+
placeholder: () => placeholder ?? editor.i18n.t("editor.placeholder")
|
|
23
|
+
};
|
|
24
|
+
const plugins = factory.call({
|
|
25
|
+
name: extension.name,
|
|
26
|
+
options,
|
|
27
|
+
storage: extension.storage,
|
|
28
|
+
editor: tiptap,
|
|
29
|
+
type: null,
|
|
30
|
+
parent: void 0
|
|
31
|
+
});
|
|
32
|
+
for (const plugin of plugins) {
|
|
33
|
+
tiptap.unregisterPlugin(plugin.spec.key ?? extension.name);
|
|
34
|
+
tiptap.registerPlugin(plugin);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function resolvePageViewOptions(pageView, layout) {
|
|
38
|
+
if (pageView === void 0 || pageView === false) return pageView;
|
|
39
|
+
const base = pageView === true ? {} : pageView;
|
|
40
|
+
return layout ? {
|
|
41
|
+
...base,
|
|
42
|
+
layout
|
|
43
|
+
} : base;
|
|
44
|
+
}
|
|
45
|
+
function setPageView(editor, options) {
|
|
46
|
+
const api = pageViewApiOf(editor);
|
|
47
|
+
if (!api) return;
|
|
48
|
+
if (options === void 0 || options === false) api.setPageView(false);
|
|
49
|
+
else api.setPageView(options === true ? {} : options);
|
|
50
|
+
}
|
|
51
|
+
function readPageViewLayout(editor) {
|
|
52
|
+
const active = getActivePageView(editor.contentElement);
|
|
53
|
+
if (!active) return null;
|
|
54
|
+
return active.layout ?? pageViewApiOf(editor)?.getLayout() ?? null;
|
|
55
|
+
}
|
|
56
|
+
function applyLayoutToPageView(editor, layout) {
|
|
57
|
+
const api = pageViewApiOf(editor);
|
|
58
|
+
const active = getActivePageView(editor.contentElement);
|
|
59
|
+
if (!api || !active || api.getLayout() === layout) return false;
|
|
60
|
+
api.setPageView({
|
|
61
|
+
...active,
|
|
62
|
+
layout
|
|
63
|
+
});
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
5
66
|
const ASTER_CONFIG = new InjectionToken("ASTER_CONFIG");
|
|
6
67
|
function provideAster(config) {
|
|
7
68
|
return makeEnvironmentProviders([{
|
|
@@ -10,9 +71,50 @@ function provideAster(config) {
|
|
|
10
71
|
}]);
|
|
11
72
|
}
|
|
12
73
|
const ASTER_UPLOAD_HANDLER = new InjectionToken("ASTER_UPLOAD_HANDLER");
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
|
|
74
|
+
async function importWord(file, defaults) {
|
|
75
|
+
const { importDocx } = await import("@aster-editor/word");
|
|
76
|
+
return importDocx(file, {
|
|
77
|
+
...defaults.upload ? { upload: defaults.upload } : {},
|
|
78
|
+
fidelity: defaults.fidelity
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function resolveExportOptions(editor, options, defaults) {
|
|
82
|
+
const variables = {
|
|
83
|
+
...options.variables,
|
|
84
|
+
values: options.variables?.values ?? editor.variables.getValues()
|
|
85
|
+
};
|
|
86
|
+
const template = options.template ?? (defaults.inTemplate && defaults.template ? defaults.template : void 0);
|
|
87
|
+
const layout = options.layout ?? defaults.layout ?? void 0;
|
|
88
|
+
return {
|
|
89
|
+
...options,
|
|
90
|
+
variables,
|
|
91
|
+
...template !== void 0 ? { template } : {},
|
|
92
|
+
...layout !== void 0 ? { layout } : {}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async function exportWord(editor, options) {
|
|
96
|
+
const { exportDocx } = await import("@aster-editor/word");
|
|
97
|
+
return exportDocx(editor.getHTML(), options);
|
|
98
|
+
}
|
|
99
|
+
async function compareWithTemplate(template, exported) {
|
|
100
|
+
const { compareDocx } = await import("@aster-editor/word");
|
|
101
|
+
return compareDocx(template, await exported.arrayBuffer());
|
|
102
|
+
}
|
|
103
|
+
async function loadWordPasteTransformer() {
|
|
104
|
+
const { wordPasteTransformer } = await import("@aster-editor/word");
|
|
105
|
+
return wordPasteTransformer();
|
|
106
|
+
}
|
|
107
|
+
function declareImportedVariables(editor, found, known) {
|
|
108
|
+
if (found.length === 0) return;
|
|
109
|
+
editor.variables.define(found);
|
|
110
|
+
const values = untracked(known);
|
|
111
|
+
if (!values) return;
|
|
112
|
+
const overrides = {};
|
|
113
|
+
for (const variable of found) {
|
|
114
|
+
const value = values[variable.name];
|
|
115
|
+
if (variable.value !== void 0 && value !== void 0 && value !== variable.value) overrides[variable.name] = value;
|
|
116
|
+
}
|
|
117
|
+
if (Object.keys(overrides).length > 0) editor.variables.setValues(overrides);
|
|
16
118
|
}
|
|
17
119
|
function sameNames(a, b) {
|
|
18
120
|
return a.length === b.length && a.every((name, index) => name === b[index]);
|
|
@@ -32,13 +134,27 @@ var AsterEditorComponent = class AsterEditorComponent {
|
|
|
32
134
|
variableResolver = input(void 0, ...ngDevMode ? [{ debugName: "variableResolver" }] : /* istanbul ignore next */ []);
|
|
33
135
|
allowUnknownVariables = input(void 0, ...ngDevMode ? [{ debugName: "allowUnknownVariables" }] : /* istanbul ignore next */ []);
|
|
34
136
|
variableSuggestion = input(void 0, ...ngDevMode ? [{ debugName: "variableSuggestion" }] : /* istanbul ignore next */ []);
|
|
137
|
+
importFidelity = input(void 0, ...ngDevMode ? [{ debugName: "importFidelity" }] : /* istanbul ignore next */ []);
|
|
138
|
+
exportInTemplate = input(true, ...ngDevMode ? [{ debugName: "exportInTemplate" }] : /* istanbul ignore next */ []);
|
|
139
|
+
pageView = input(void 0, ...ngDevMode ? [{ debugName: "pageView" }] : /* istanbul ignore next */ []);
|
|
140
|
+
layoutOverride = input(void 0, {
|
|
141
|
+
...ngDevMode ? { debugName: "layoutOverride" } : /* istanbul ignore next */ {},
|
|
142
|
+
alias: "layout"
|
|
143
|
+
});
|
|
35
144
|
ready = output();
|
|
36
145
|
changed = output();
|
|
37
146
|
focused = output();
|
|
38
147
|
blurred = output();
|
|
39
148
|
variablesChanged = output();
|
|
149
|
+
layoutChanged = output();
|
|
40
150
|
editorState = signal(null, ...ngDevMode ? [{ debugName: "editorState" }] : /* istanbul ignore next */ []);
|
|
41
151
|
editor = this.editorState.asReadonly();
|
|
152
|
+
importedTemplateState = signal(null, ...ngDevMode ? [{ debugName: "importedTemplateState" }] : /* istanbul ignore next */ []);
|
|
153
|
+
importedTemplate = this.importedTemplateState.asReadonly();
|
|
154
|
+
lostObjectsState = signal([], ...ngDevMode ? [{ debugName: "lostObjectsState" }] : /* istanbul ignore next */ []);
|
|
155
|
+
lostObjects = this.lostObjectsState.asReadonly();
|
|
156
|
+
layoutState = signal(null, ...ngDevMode ? [{ debugName: "layoutState" }] : /* istanbul ignore next */ []);
|
|
157
|
+
layout = computed(() => this.layoutOverride() ?? this.layoutState(), ...ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []);
|
|
42
158
|
variableNamesState = signal([], {
|
|
43
159
|
...ngDevMode ? { debugName: "variableNamesState" } : /* istanbul ignore next */ {},
|
|
44
160
|
equal: sameNames
|
|
@@ -68,8 +184,11 @@ var AsterEditorComponent = class AsterEditorComponent {
|
|
|
68
184
|
return Object.keys(source).length > 0 ? source : void 0;
|
|
69
185
|
}, ...ngDevMode ? [{ debugName: "resolvedVariableSource" }] : /* istanbul ignore next */ []);
|
|
70
186
|
resolvedVariableSuggestion = computed(() => this.variableSuggestion() ?? this.mergedConfig().variableSuggestion, ...ngDevMode ? [{ debugName: "resolvedVariableSuggestion" }] : /* istanbul ignore next */ []);
|
|
187
|
+
resolvedPageView = computed(() => this.pageView() ?? this.mergedConfig().pageView, ...ngDevMode ? [{ debugName: "resolvedPageView" }] : /* istanbul ignore next */ []);
|
|
71
188
|
instance = null;
|
|
72
189
|
mountedToolbar = null;
|
|
190
|
+
mountedPageView = void 0;
|
|
191
|
+
emittedLayout = null;
|
|
73
192
|
subscriptions = [];
|
|
74
193
|
pendingValue = null;
|
|
75
194
|
wordTransformer = null;
|
|
@@ -98,14 +217,14 @@ var AsterEditorComponent = class AsterEditorComponent {
|
|
|
98
217
|
effect(() => {
|
|
99
218
|
const placeholder = this.resolvedPlaceholder();
|
|
100
219
|
untracked(() => {
|
|
101
|
-
if (this.instance)
|
|
220
|
+
if (this.instance) applyPlaceholder(this.instance.editor, placeholder);
|
|
102
221
|
});
|
|
103
222
|
});
|
|
104
223
|
effect(() => {
|
|
105
224
|
const label = this.ariaLabel();
|
|
106
225
|
const labelledBy = this.ariaLabelledBy();
|
|
107
226
|
untracked(() => {
|
|
108
|
-
if (this.instance)
|
|
227
|
+
if (this.instance) applyAriaAttributes(this.instance.editor, label, labelledBy);
|
|
109
228
|
});
|
|
110
229
|
});
|
|
111
230
|
effect(() => {
|
|
@@ -131,30 +250,51 @@ var AsterEditorComponent = class AsterEditorComponent {
|
|
|
131
250
|
if (values && this.instance) this.instance.editor.variables.setValues(values);
|
|
132
251
|
});
|
|
133
252
|
});
|
|
253
|
+
effect(() => {
|
|
254
|
+
const pageView = this.resolvedPageView();
|
|
255
|
+
untracked(() => {
|
|
256
|
+
if (this.instance && this.mountedPageView !== pageView) {
|
|
257
|
+
this.mountedPageView = pageView;
|
|
258
|
+
setPageView(this.instance.editor, resolvePageViewOptions(pageView, this.layout()));
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
effect(() => {
|
|
263
|
+
const layout = this.layout();
|
|
264
|
+
untracked(() => {
|
|
265
|
+
if (layout && this.instance) applyLayoutToPageView(this.instance.editor, layout);
|
|
266
|
+
this.notifyLayout(layout);
|
|
267
|
+
});
|
|
268
|
+
});
|
|
134
269
|
}
|
|
135
270
|
async importDocx(file) {
|
|
136
271
|
this.requireEditor("importDocx");
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
272
|
+
const result = await importWord(file, untracked(() => ({
|
|
273
|
+
upload: this.resolvedOptions().upload,
|
|
274
|
+
fidelity: this.importFidelity() ?? "styled"
|
|
275
|
+
})));
|
|
140
276
|
const editor = this.instance?.editor;
|
|
141
277
|
if (editor && !editor.isDestroyed()) {
|
|
142
278
|
editor.setHTML(result.html, true);
|
|
143
|
-
|
|
279
|
+
declareImportedVariables(editor, result.variables, () => this.variableValues());
|
|
280
|
+
this.importedTemplateState.set(result.template);
|
|
281
|
+
this.lostObjectsState.set(result.lost);
|
|
282
|
+
this.layoutState.set(result.layout);
|
|
144
283
|
}
|
|
145
284
|
return result;
|
|
146
285
|
}
|
|
147
286
|
async exportDocx(options = {}) {
|
|
148
287
|
const editor = this.requireEditor("exportDocx");
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
288
|
+
return exportWord(editor, untracked(() => resolveExportOptions(editor, options, {
|
|
289
|
+
template: this.importedTemplate(),
|
|
290
|
+
layout: this.layout(),
|
|
291
|
+
inTemplate: this.exportInTemplate()
|
|
292
|
+
})));
|
|
293
|
+
}
|
|
294
|
+
async compareWithTemplate() {
|
|
295
|
+
const template = this.importedTemplate();
|
|
296
|
+
if (!template) return null;
|
|
297
|
+
return compareWithTemplate(template, await this.exportDocx({ template }));
|
|
158
298
|
}
|
|
159
299
|
getHTML(options) {
|
|
160
300
|
return this.instance?.editor.getHTML(options) ?? this.pendingValue ?? "";
|
|
@@ -189,28 +329,27 @@ var AsterEditorComponent = class AsterEditorComponent {
|
|
|
189
329
|
const editor = instance.editor;
|
|
190
330
|
this.instance = instance;
|
|
191
331
|
this.mountedToolbar = options.toolbar ?? "standard";
|
|
332
|
+
this.mountedPageView = untracked(() => this.resolvedPageView());
|
|
192
333
|
this.pendingValue = null;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
];
|
|
213
|
-
untracked(() => this.applyAriaAttributes(editor, this.ariaLabel(), this.ariaLabelledBy()));
|
|
334
|
+
const onPageViewChange = () => this.readLayoutFromPageView(editor);
|
|
335
|
+
editor.contentElement.addEventListener(PAGE_VIEW_CHANGE_EVENT, onPageViewChange);
|
|
336
|
+
this.subscriptions = [() => editor.contentElement.removeEventListener(PAGE_VIEW_CHANGE_EVENT, onPageViewChange)];
|
|
337
|
+
this.subscriptions.push(editor.on("ready", ({ editor: ready }) => this.ready.emit(ready)), editor.on("change", ({ editor: changed }) => {
|
|
338
|
+
const html = changed.getHTML();
|
|
339
|
+
this.onChange?.(html);
|
|
340
|
+
this.changed.emit(html);
|
|
341
|
+
this.refreshVariableNames(changed);
|
|
342
|
+
}), editor.on("variablesChange", ({ editor: source, values }) => {
|
|
343
|
+
this.refreshVariableNames(source);
|
|
344
|
+
this.variablesChanged.emit(values);
|
|
345
|
+
}), editor.on("focus", () => this.focused.emit()), editor.on("blur", ({ event }) => {
|
|
346
|
+
const related = event.relatedTarget;
|
|
347
|
+
if (related instanceof Node && instance.element.contains(related)) return;
|
|
348
|
+
this.onTouched?.();
|
|
349
|
+
this.blurred.emit();
|
|
350
|
+
}));
|
|
351
|
+
untracked(() => applyAriaAttributes(editor, this.ariaLabel(), this.ariaLabelledBy()));
|
|
352
|
+
this.readLayoutFromPageView(editor);
|
|
214
353
|
if (untracked(() => this.wordPaste())) this.loadWordPaste();
|
|
215
354
|
this.refreshVariableNames(editor);
|
|
216
355
|
this.editorState.set(editor);
|
|
@@ -221,6 +360,7 @@ var AsterEditorComponent = class AsterEditorComponent {
|
|
|
221
360
|
this.instance?.destroy();
|
|
222
361
|
this.instance = null;
|
|
223
362
|
this.mountedToolbar = null;
|
|
363
|
+
this.mountedPageView = void 0;
|
|
224
364
|
this.editorState.set(null);
|
|
225
365
|
this.variableNamesState.set([]);
|
|
226
366
|
}
|
|
@@ -261,9 +401,22 @@ var AsterEditorComponent = class AsterEditorComponent {
|
|
|
261
401
|
pasteTransformers: [this.lazyWordTransformer, ...merged.pasteTransformers ?? []],
|
|
262
402
|
upload: merged.upload ?? this.uploadHandler ?? void 0,
|
|
263
403
|
variables: variableSource,
|
|
264
|
-
variableSuggestion: this.resolvedVariableSuggestion()
|
|
404
|
+
variableSuggestion: this.resolvedVariableSuggestion(),
|
|
405
|
+
pageView: resolvePageViewOptions(this.resolvedPageView(), this.layout())
|
|
265
406
|
};
|
|
266
407
|
}
|
|
408
|
+
readLayoutFromPageView(editor) {
|
|
409
|
+
const current = readPageViewLayout(editor);
|
|
410
|
+
if (!current || current === untracked(() => this.layoutState()) || current === untracked(() => this.layoutOverride())) return;
|
|
411
|
+
this.layoutState.set(current);
|
|
412
|
+
if (untracked(() => this.layout()) !== current) this.notifyLayout(current);
|
|
413
|
+
}
|
|
414
|
+
notifyLayout(layout) {
|
|
415
|
+
if (layout && layout !== this.emittedLayout) {
|
|
416
|
+
this.emittedLayout = layout;
|
|
417
|
+
this.layoutChanged.emit(layout);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
267
420
|
requireEditor(method) {
|
|
268
421
|
const editor = this.instance?.editor;
|
|
269
422
|
if (!editor) throw new Error(`[aster-editor/angular] ${method}: editor is not mounted yet`);
|
|
@@ -272,54 +425,15 @@ var AsterEditorComponent = class AsterEditorComponent {
|
|
|
272
425
|
refreshVariableNames(editor) {
|
|
273
426
|
this.variableNamesState.set(editor.variables.getNames());
|
|
274
427
|
}
|
|
275
|
-
declareImportedVariables(editor, found) {
|
|
276
|
-
if (found.length === 0) return;
|
|
277
|
-
editor.variables.define(found);
|
|
278
|
-
const known = untracked(() => this.variableValues());
|
|
279
|
-
if (!known) return;
|
|
280
|
-
const overrides = {};
|
|
281
|
-
for (const variable of found) {
|
|
282
|
-
const value = known[variable.name];
|
|
283
|
-
if (variable.value !== void 0 && value !== void 0 && value !== variable.value) overrides[variable.name] = value;
|
|
284
|
-
}
|
|
285
|
-
if (Object.keys(overrides).length > 0) editor.variables.setValues(overrides);
|
|
286
|
-
}
|
|
287
428
|
loadWordPaste() {
|
|
288
429
|
if (this.wordTransformer || this.wordLoading) return;
|
|
289
|
-
this.wordLoading =
|
|
290
|
-
this.wordTransformer =
|
|
430
|
+
this.wordLoading = loadWordPasteTransformer().then((transformer) => {
|
|
431
|
+
this.wordTransformer = transformer;
|
|
291
432
|
}).catch((error) => {
|
|
292
433
|
this.wordLoading = null;
|
|
293
434
|
console.warn("[aster-editor/angular] @aster-editor/word unavailable, paste kept raw", error);
|
|
294
435
|
});
|
|
295
436
|
}
|
|
296
|
-
applyAriaAttributes(editor, label, labelledBy) {
|
|
297
|
-
setOrRemoveAttribute(editor.contentElement, "aria-label", label);
|
|
298
|
-
setOrRemoveAttribute(editor.contentElement, "aria-labelledby", labelledBy);
|
|
299
|
-
}
|
|
300
|
-
applyPlaceholder(editor, placeholder) {
|
|
301
|
-
const tiptap = editor.tiptap;
|
|
302
|
-
const extension = tiptap.extensionManager.extensions.find((candidate) => candidate.name === "placeholder");
|
|
303
|
-
if (extension?.type !== "extension") return;
|
|
304
|
-
const factory = extension.config.addProseMirrorPlugins;
|
|
305
|
-
if (!factory) return;
|
|
306
|
-
const options = {
|
|
307
|
-
...extension.options,
|
|
308
|
-
placeholder: () => placeholder ?? editor.i18n.t("editor.placeholder")
|
|
309
|
-
};
|
|
310
|
-
const plugins = factory.call({
|
|
311
|
-
name: extension.name,
|
|
312
|
-
options,
|
|
313
|
-
storage: extension.storage,
|
|
314
|
-
editor: tiptap,
|
|
315
|
-
type: null,
|
|
316
|
-
parent: void 0
|
|
317
|
-
});
|
|
318
|
-
for (const plugin of plugins) {
|
|
319
|
-
tiptap.unregisterPlugin(plugin.spec.key ?? extension.name);
|
|
320
|
-
tiptap.registerPlugin(plugin);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
437
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
324
438
|
minVersion: "12.0.0",
|
|
325
439
|
version: "22.2.2",
|
|
@@ -432,6 +546,34 @@ var AsterEditorComponent = class AsterEditorComponent {
|
|
|
432
546
|
isSignal: true,
|
|
433
547
|
isRequired: false,
|
|
434
548
|
transformFunction: null
|
|
549
|
+
},
|
|
550
|
+
importFidelity: {
|
|
551
|
+
classPropertyName: "importFidelity",
|
|
552
|
+
publicName: "importFidelity",
|
|
553
|
+
isSignal: true,
|
|
554
|
+
isRequired: false,
|
|
555
|
+
transformFunction: null
|
|
556
|
+
},
|
|
557
|
+
exportInTemplate: {
|
|
558
|
+
classPropertyName: "exportInTemplate",
|
|
559
|
+
publicName: "exportInTemplate",
|
|
560
|
+
isSignal: true,
|
|
561
|
+
isRequired: false,
|
|
562
|
+
transformFunction: null
|
|
563
|
+
},
|
|
564
|
+
pageView: {
|
|
565
|
+
classPropertyName: "pageView",
|
|
566
|
+
publicName: "pageView",
|
|
567
|
+
isSignal: true,
|
|
568
|
+
isRequired: false,
|
|
569
|
+
transformFunction: null
|
|
570
|
+
},
|
|
571
|
+
layoutOverride: {
|
|
572
|
+
classPropertyName: "layoutOverride",
|
|
573
|
+
publicName: "layout",
|
|
574
|
+
isSignal: true,
|
|
575
|
+
isRequired: false,
|
|
576
|
+
transformFunction: null
|
|
435
577
|
}
|
|
436
578
|
},
|
|
437
579
|
outputs: {
|
|
@@ -439,7 +581,8 @@ var AsterEditorComponent = class AsterEditorComponent {
|
|
|
439
581
|
changed: "changed",
|
|
440
582
|
focused: "focused",
|
|
441
583
|
blurred: "blurred",
|
|
442
|
-
variablesChanged: "variablesChanged"
|
|
584
|
+
variablesChanged: "variablesChanged",
|
|
585
|
+
layoutChanged: "layoutChanged"
|
|
443
586
|
},
|
|
444
587
|
providers: [{
|
|
445
588
|
provide: NG_VALUE_ACCESSOR,
|
|
@@ -593,6 +736,38 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
593
736
|
required: false
|
|
594
737
|
}]
|
|
595
738
|
}],
|
|
739
|
+
importFidelity: [{
|
|
740
|
+
type: i0.Input,
|
|
741
|
+
args: [{
|
|
742
|
+
isSignal: true,
|
|
743
|
+
alias: "importFidelity",
|
|
744
|
+
required: false
|
|
745
|
+
}]
|
|
746
|
+
}],
|
|
747
|
+
exportInTemplate: [{
|
|
748
|
+
type: i0.Input,
|
|
749
|
+
args: [{
|
|
750
|
+
isSignal: true,
|
|
751
|
+
alias: "exportInTemplate",
|
|
752
|
+
required: false
|
|
753
|
+
}]
|
|
754
|
+
}],
|
|
755
|
+
pageView: [{
|
|
756
|
+
type: i0.Input,
|
|
757
|
+
args: [{
|
|
758
|
+
isSignal: true,
|
|
759
|
+
alias: "pageView",
|
|
760
|
+
required: false
|
|
761
|
+
}]
|
|
762
|
+
}],
|
|
763
|
+
layoutOverride: [{
|
|
764
|
+
type: i0.Input,
|
|
765
|
+
args: [{
|
|
766
|
+
isSignal: true,
|
|
767
|
+
alias: "layout",
|
|
768
|
+
required: false
|
|
769
|
+
}]
|
|
770
|
+
}],
|
|
596
771
|
ready: [{
|
|
597
772
|
type: i0.Output,
|
|
598
773
|
args: ["ready"]
|
|
@@ -613,6 +788,10 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
613
788
|
type: i0.Output,
|
|
614
789
|
args: ["variablesChanged"]
|
|
615
790
|
}],
|
|
791
|
+
layoutChanged: [{
|
|
792
|
+
type: i0.Output,
|
|
793
|
+
args: ["layoutChanged"]
|
|
794
|
+
}],
|
|
616
795
|
host: [{
|
|
617
796
|
type: i0.ViewChild,
|
|
618
797
|
args: ["host", { isSignal: true }]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aster-editor-angular.mjs","names":[],"sources":["../../src/lib/provide-aster.ts","../../src/lib/tokens.ts","../../src/lib/aster-editor.component.ts"],"sourcesContent":["import { InjectionToken, makeEnvironmentProviders } from '@angular/core';\nimport type { EnvironmentProviders } from '@angular/core';\nimport type { AsterUiOptions } from '@aster-editor/ui';\n\n/**\n * Configuration globale des éditeurs d'une application (ou d'une route, si fournie\n * dans ses `providers`). Le composant la fusionne avec l'input `config`, qui prime,\n * puis avec les inputs dédiés (`toolbar`, `theme`, `locale`, `placeholder`, `readonly`).\n */\nexport const ASTER_CONFIG = new InjectionToken<Partial<AsterUiOptions>>('ASTER_CONFIG');\n\n/**\n * Déclare la configuration globale d'Aster dans `ApplicationConfig.providers` ou dans\n * les `providers` d'une route. Fusion superficielle : une clé donnée ici est remplacée\n * en bloc par la même clé passée dans l'input `config` d'un composant.\n *\n * ```ts\n * provideAster({ locale: 'en', theme: 'night', linkPolicy: { defaultTarget: '_blank' } })\n * ```\n */\nexport function provideAster(config: Partial<AsterUiOptions>): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: ASTER_CONFIG, useValue: config }]);\n}\n","import { InjectionToken } from '@angular/core';\nimport type { UploadHandler } from '@aster-editor/core';\n\n/**\n * Hook d'upload d'image fourni par l'application (service HTTP, stockage, CDN…).\n * Optionnel : s'il est présent et qu'aucun `upload` n'est passé par `provideAster`\n * ni par l'input `config`, le composant le branche sur `AsterOptions.upload`.\n * Sans handler, les images sont insérées en data URL base64.\n */\nexport const ASTER_UPLOAD_HANDLER = new InjectionToken<UploadHandler>('ASTER_UPLOAD_HANDLER');\n","import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n afterNextRender,\n computed,\n effect,\n forwardRef,\n inject,\n input,\n output,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\nimport type { ElementRef, Signal } from '@angular/core';\nimport { NG_VALUE_ACCESSOR } from '@angular/forms';\nimport type { ControlValueAccessor } from '@angular/forms';\nimport type {\n AsterDocument,\n AsterEditor,\n AsterLocale,\n AsterTheme,\n AsterVariable,\n GetHtmlOptions,\n PasteTransformer,\n ToolbarConfig,\n Unsubscribe,\n VariableResolver,\n VariableSource,\n VariableValues,\n} from '@aster-editor/core';\nimport { createAster } from '@aster-editor/ui';\nimport type { AsterInstance, AsterUiOptions } from '@aster-editor/ui';\nimport type { DocxExportOptions, DocxImportResult } from '@aster-editor/word';\nimport { ASTER_CONFIG } from './provide-aster';\nimport { ASTER_UPLOAD_HANDLER } from './tokens';\n\n/** Vue minimale des options de l'extension Tiptap `Placeholder`, pour la changer à chaud. */\ninterface PlaceholderExtensionOptions {\n placeholder?: unknown;\n}\n\ntype TiptapEditor = AsterEditor['tiptap'];\ntype ProseMirrorPlugin = Parameters<TiptapEditor['registerPlugin']>[0];\n\n/** Signature de `addProseMirrorPlugins` d'une extension Tiptap (hors nœud et marque). */\ntype ExtensionPluginFactory = (this: {\n name: string;\n options: PlaceholderExtensionOptions;\n storage: unknown;\n editor: TiptapEditor;\n type: null;\n parent: undefined;\n}) => ProseMirrorPlugin[];\n\n/** Pose l'attribut quand une valeur est fournie, le retire sinon. */\nfunction setOrRemoveAttribute(element: HTMLElement, name: string, value: string | undefined): void {\n if (value === undefined || value === '') {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value);\n }\n}\n\n/** Égalité de deux listes de noms, pour ne pas notifier `variableNames` sans changement réel. */\nfunction sameNames(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((name, index) => name === b[index]);\n}\n\n/** État du magasin de variables à conserver au travers d'un remontage. */\ninterface VariableSnapshot {\n readonly declared: readonly AsterVariable[];\n readonly values: VariableValues;\n}\n\n/**\n * Composant Angular standalone autour de `createAster` (`@aster-editor/ui`).\n *\n * - `ControlValueAccessor` : utilisable avec `formControlName`, `[formControl]` et `ngModel`.\n * La valeur du formulaire est le HTML sémantique de `getHTML()`.\n * - Zoneless et `OnPush` : rien dans le template ne dépend de l'état, les sorties sont\n * des `output()` et l'éditeur est exposé en signal.\n * - SSR-safe : l'éditeur est monté dans `afterNextRender`, jamais dans le constructeur, et\n * le module Word n'est chargé qu'à partir du montage (donc jamais côté serveur).\n *\n * Configuration, par priorité croissante : `provideAster(config)` < input `config` <\n * inputs dédiés (`toolbar`, `theme`, `locale`, `placeholder`, `readonly`). Un input dédié\n * laissé à `undefined` n'écrase donc pas la valeur venue de la configuration.\n *\n * Changements à chaud : `theme`, `locale`, `readonly`, `placeholder`, `ariaLabel`,\n * `ariaLabelledBy`, `variables` et `variableValues` sont appliqués sur l'instance vivante.\n * `toolbar` remonte l'éditeur (le contenu, les déclarations et les valeurs de variables sont\n * conservés, l'historique d'annulation est perdu, `ready` est réémis avec la nouvelle\n * instance). Les autres clés de `config` (`extensions`, `commands`, `upload`…) ne sont lues\n * qu'au montage.\n *\n * Variables (champs de fusion) : les inputs `variables`, `variableValues`, `variableResolver`,\n * `allowUnknownVariables` et `variableSuggestion` se fusionnent dans `options.variables` et\n * `variableSuggestion` de `createAster`. `variableNames` expose les noms présents dans le\n * document, `variablesChanged` relaie `variablesChange`, `exportDocx` produit le `.docx` avec\n * les valeurs du magasin.\n */\n@Component({\n selector: 'aster-editor',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: '<div #host class=\"aster-editor__host\"></div>',\n styles: ':host { display: block; }',\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => AsterEditorComponent),\n multi: true,\n },\n ],\n})\nexport class AsterEditorComponent implements ControlValueAccessor {\n /** Preset, groupes déclaratifs, ou `false` pour aucune toolbar. Défaut effectif : `standard`. */\n readonly toolbar = input<ToolbarConfig | undefined>(undefined);\n /** Thème appliqué sur `.aster[data-theme]`. `light` et `night` sont fournis par `ui`. Défaut : `light`. */\n readonly theme = input<AsterTheme | undefined>(undefined);\n /** Locale de l'éditeur et de la toolbar. Défaut effectif : `fr`. */\n readonly locale = input<AsterLocale | undefined>(undefined);\n /** Texte affiché quand le document est vide. Sans valeur, la chaîne i18n `editor.placeholder`. */\n readonly placeholder = input<string | undefined>(undefined);\n /** Lecture seule. Le `disabled` d'un formulaire s'y ajoute (l'un ou l'autre suffit). Défaut : `false`. */\n readonly readonly = input<boolean | undefined>(undefined);\n /** Tout le reste des options (`extensions`, `commands`, `linkPolicy`, `upload`, `content`…). */\n readonly config = input<Partial<AsterUiOptions>>({});\n /**\n * Nettoyage automatique du HTML collé depuis Word et Google Docs via\n * `wordPasteTransformer()` de `@aster-editor/word`, chargé dynamiquement au montage.\n */\n readonly wordPaste = input<boolean>(true);\n /**\n * Nom accessible de la zone de saisie (`aria-label` sur le contenteditable). Un\n * contenteditable n'est pas associable à un `<label for>`, c'est le seul moyen de le nommer.\n */\n readonly ariaLabel = input<string | undefined>(undefined);\n /** `id` (ou liste d'ids) de l'élément qui sert de libellé (`aria-labelledby` sur le contenteditable). */\n readonly ariaLabelledBy = input<string | undefined>(undefined);\n /**\n * Variables déclarées (libellé, description, valeur), proposées dans le menu d'insertion\n * même si absentes du document. À chaud : `editor.variables.define(...)`, fusion par nom.\n */\n readonly variables = input<readonly AsterVariable[] | undefined>(undefined);\n /**\n * Valeurs par nom. À chaud : `editor.variables.setValues(...)`, fusion avec les valeurs\n * connues (une clé absente n'efface rien, `undefined` efface).\n */\n readonly variableValues = input<VariableValues | undefined>(undefined);\n /** Résolveur asynchrone appelé par `resolveVariables()` pour les variables sans valeur. Lu au montage. */\n readonly variableResolver = input<VariableResolver | undefined>(undefined);\n /** Autoriser l'insertion d'une variable non déclarée. Défaut effectif : `true`. Lu au montage. */\n readonly allowUnknownVariables = input<boolean | undefined>(undefined);\n /** Menu de suggestion déclenché par `{{` dans le contenu. Défaut effectif : `true`. Lu au montage. */\n readonly variableSuggestion = input<boolean | undefined>(undefined);\n\n /** L'éditeur est monté et prêt. Réémis après un remontage (changement de `toolbar`). */\n readonly ready = output<AsterEditor>();\n /** Le document a changé : HTML de `getHTML()`. Pas émis sur `writeValue`. */\n readonly changed = output<string>();\n /** Le contenteditable a pris le focus. */\n readonly focused = output<void>();\n /**\n * Le contenteditable a perdu le focus au profit d'un élément extérieur à l'éditeur. Un\n * blur vers la toolbar ou un de ses menus n'est pas remonté : l'utilisateur est toujours\n * « dans » l'éditeur. C'est aussi ce qui marque le contrôle de formulaire `touched`.\n */\n readonly blurred = output<void>();\n /** Les valeurs ou déclarations de variables ont changé (`setValues`, `define`, `resolve`, import). */\n readonly variablesChanged = output<VariableValues>();\n\n private readonly editorState = signal<AsterEditor | null>(null);\n /** Éditeur courant, `null` avant le montage et après la destruction. */\n readonly editor: Signal<AsterEditor | null> = this.editorState.asReadonly();\n\n private readonly variableNamesState = signal<readonly string[]>([], { equal: sameNames });\n /**\n * Noms des variables réellement présentes dans le document (`editor.variables.getNames()`),\n * relus sur `change` et `variablesChange`. Vide avant le montage et après la destruction.\n */\n readonly variableNames: Signal<readonly string[]> = this.variableNamesState.asReadonly();\n\n private readonly host = viewChild.required<ElementRef<HTMLDivElement>>('host');\n private readonly globalConfig = inject(ASTER_CONFIG, { optional: true });\n private readonly uploadHandler = inject(ASTER_UPLOAD_HANDLER, { optional: true });\n private readonly formDisabled = signal(false);\n\n /** `ASTER_CONFIG` < `config`, fusion superficielle. */\n private readonly mergedConfig = computed<Partial<AsterUiOptions>>(() => ({\n ...(this.globalConfig ?? {}),\n ...this.config(),\n }));\n private readonly resolvedToolbar = computed<ToolbarConfig>(\n () => this.toolbar() ?? this.mergedConfig().toolbar ?? 'standard',\n );\n private readonly resolvedTheme = computed<AsterTheme>(\n () => this.theme() ?? this.mergedConfig().theme ?? 'light',\n );\n private readonly resolvedLocale = computed<AsterLocale>(\n () => this.locale() ?? this.mergedConfig().locale ?? 'fr',\n );\n private readonly resolvedPlaceholder = computed<string | undefined>(\n () => this.placeholder() ?? this.mergedConfig().placeholder,\n );\n private readonly resolvedReadOnly = computed<boolean>(\n () => (this.readonly() ?? this.mergedConfig().readOnly ?? false) || this.formDisabled(),\n );\n /** `config.variables` < inputs dédiés, clé par clé ; un input `undefined` ne masque rien. */\n private readonly resolvedVariableSource = computed<VariableSource | undefined>(() => {\n const base = this.mergedConfig().variables;\n const source: VariableSource = {\n ...base,\n ...(this.variables() !== undefined ? { variables: this.variables() } : {}),\n ...(this.variableValues() !== undefined ? { values: this.variableValues() } : {}),\n ...(this.variableResolver() !== undefined ? { resolve: this.variableResolver() } : {}),\n ...(this.allowUnknownVariables() !== undefined\n ? { allowUnknown: this.allowUnknownVariables() }\n : {}),\n };\n return Object.keys(source).length > 0 ? source : undefined;\n });\n private readonly resolvedVariableSuggestion = computed<boolean | undefined>(\n () => this.variableSuggestion() ?? this.mergedConfig().variableSuggestion,\n );\n\n private instance: AsterInstance | null = null;\n private mountedToolbar: ToolbarConfig | null = null;\n private subscriptions: Unsubscribe[] = [];\n /** Valeur reçue par `writeValue` avant le montage, appliquée comme contenu initial. */\n private pendingValue: string | null = null;\n private wordTransformer: PasteTransformer | null = null;\n private wordLoading: Promise<void> | null = null;\n private onChange: ((html: string) => void) | null = null;\n private onTouched: (() => void) | null = null;\n\n /**\n * Transformateur synchrone branché au montage ; il délègue au vrai transformateur Word\n * une fois le module chargé. Garde `@aster-editor/word` optionnel et hors du bundle\n * initial tout en respectant le contrat synchrone de `PasteTransformer`.\n */\n private readonly lazyWordTransformer: PasteTransformer = (html, context) =>\n this.wordTransformer && untracked(() => this.wordPaste())\n ? this.wordTransformer(html, context)\n : html;\n\n constructor() {\n afterNextRender(() => this.mount());\n inject(DestroyRef).onDestroy(() => this.unmount());\n\n effect(() => {\n const theme = this.resolvedTheme();\n untracked(() => this.instance?.setTheme(theme));\n });\n effect(() => {\n const locale = this.resolvedLocale();\n untracked(() => {\n const editor = this.instance?.editor;\n if (editor && editor.i18n.getLocale() !== locale) {\n editor.i18n.setLocale(locale);\n }\n });\n });\n effect(() => {\n const readOnly = this.resolvedReadOnly();\n untracked(() => this.instance?.editor.setReadOnly(readOnly));\n });\n effect(() => {\n const placeholder = this.resolvedPlaceholder();\n untracked(() => {\n if (this.instance) {\n this.applyPlaceholder(this.instance.editor, placeholder);\n }\n });\n });\n effect(() => {\n const label = this.ariaLabel();\n const labelledBy = this.ariaLabelledBy();\n untracked(() => {\n if (this.instance) {\n this.applyAriaAttributes(this.instance.editor, label, labelledBy);\n }\n });\n });\n effect(() => {\n const toolbar = this.resolvedToolbar();\n untracked(() => {\n if (this.instance && this.mountedToolbar !== toolbar) {\n this.remount();\n }\n });\n });\n // Le chargement initial du module Word part de `mount()` (jamais côté serveur) ; cet\n // effet ne couvre que le passage `false → true` d'un éditeur déjà monté.\n effect(() => {\n if (this.wordPaste()) {\n untracked(() => {\n if (this.instance) {\n this.loadWordPaste();\n }\n });\n }\n });\n // Au montage, déclarations et valeurs passent par `options.variables` ; ces deux effets\n // ne couvrent que les changements d'input sur un éditeur déjà monté.\n effect(() => {\n const declared = this.variables();\n untracked(() => {\n if (declared && this.instance) {\n this.instance.editor.variables.define(declared);\n }\n });\n });\n effect(() => {\n const values = this.variableValues();\n untracked(() => {\n if (values && this.instance) {\n this.instance.editor.variables.setValues(values);\n }\n });\n });\n }\n\n // --- API publique --------------------------------------------------------\n\n /**\n * Importe un `.docx` via `@aster-editor/word` (chargé à la demande, mammoth reste dans\n * un chunk séparé), remplace le contenu de l'éditeur en émettant `change` pour que le\n * formulaire voie la nouvelle valeur, et rend le résultat avec ses avertissements.\n * Le hook d'upload de l'éditeur est réutilisé pour les images embarquées. Si le\n * composant est détruit ou remonté pendant l'import, le résultat est rendu mais aucun\n * contenu n'est écrit : l'instance vivante est relue après chaque `await`.\n */\n async importDocx(file: File): Promise<DocxImportResult> {\n this.requireEditor('importDocx');\n const upload = untracked(() => this.resolvedOptions().upload);\n const { importDocx } = await import('@aster-editor/word');\n const result = await importDocx(file, upload ? { upload } : {});\n const editor = this.instance?.editor;\n if (editor && !editor.isDestroyed()) {\n editor.setHTML(result.html, true);\n this.declareImportedVariables(editor, result.variables);\n }\n return result;\n }\n\n /**\n * Convertit le document en `.docx` via `exportDocx` de `@aster-editor/word` (chargé à la\n * demande, la lib `docx` reste dans son chunk). Les valeurs du magasin de variables sont\n * transmises quand `options.variables.values` n'est pas fourni : `mode: 'resolve'` substitue\n * donc ce que l'utilisateur a saisi, `'fields'` produit des champs de fusion Word avec ces\n * valeurs comme résultat. Rend le `Blob`, à passer à `downloadDocx` ou à un upload.\n */\n async exportDocx(options: DocxExportOptions = {}): Promise<Blob> {\n const editor = this.requireEditor('exportDocx');\n const { exportDocx } = await import('@aster-editor/word');\n const variables = {\n ...options.variables,\n values: options.variables?.values ?? editor.variables.getValues(),\n };\n return exportDocx(editor.getHTML(), { ...options, variables });\n }\n\n /**\n * HTML du document, `{ variables: 'resolve' }` substitue les valeurs connues. Avant le\n * montage, rend la valeur reçue par `writeValue` (puces conservées) ou une chaîne vide.\n */\n getHTML(options?: GetHtmlOptions): string {\n return this.instance?.editor.getHTML(options) ?? this.pendingValue ?? '';\n }\n\n /**\n * Appelle le résolveur (`variableResolver` ou `config.variables.resolve`) pour les\n * variables présentes sans valeur, applique les valeurs obtenues et les rend toutes.\n */\n resolveVariables(): Promise<VariableValues> {\n return this.requireEditor('resolveVariables').variables.resolve();\n }\n\n // --- ControlValueAccessor ------------------------------------------------\n\n /**\n * Applique la valeur du formulaire sans émettre `change` ni entrer dans l'historique ;\n * mémorisée si l'éditeur n'est pas monté. Une valeur identique au HTML courant est\n * ignorée pour ne pas déplacer la sélection (les reactive forms réécrivent la valeur à\n * chaque `setValue`, y compris quand elle n'a pas changé).\n */\n writeValue(value: string | null | undefined): void {\n const html = value ?? '';\n const editor = this.instance?.editor;\n if (!editor) {\n this.pendingValue = html;\n return;\n }\n if (editor.getHTML() === html) {\n return;\n }\n editor.setHTML(html, false);\n }\n\n /** Enregistre le rappel appelé avec le HTML à chaque modification du document par l'utilisateur. */\n registerOnChange(fn: (html: string) => void): void {\n this.onChange = fn;\n }\n\n /** Enregistre le rappel appelé quand l'éditeur perd le focus vers l'extérieur (`touched`). */\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n /** `disabled` du formulaire → lecture seule, cumulé avec l'input `readonly`. */\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n this.instance?.editor.setReadOnly(untracked(() => this.resolvedReadOnly()));\n }\n\n // --- Cycle de vie --------------------------------------------------------\n\n /**\n * Monte l'éditeur. `content` force le contenu initial (remontage) et, dans ce cas,\n * `autofocus` est neutralisé : le focus est restauré explicitement par `remount()`.\n */\n private mount(content?: string, variables?: VariableSnapshot): void {\n if (this.instance) {\n return;\n }\n const options = untracked(() => this.resolvedOptions(content, variables));\n const instance = createAster(this.host().nativeElement, options);\n const editor = instance.editor;\n this.instance = instance;\n this.mountedToolbar = options.toolbar ?? 'standard';\n this.pendingValue = null;\n this.subscriptions = [\n editor.on('ready', ({ editor: ready }) => this.ready.emit(ready)),\n editor.on('change', ({ editor: changed }) => {\n const html = changed.getHTML();\n this.onChange?.(html);\n this.changed.emit(html);\n this.refreshVariableNames(changed);\n }),\n editor.on('variablesChange', ({ editor: source, values }) => {\n this.refreshVariableNames(source);\n this.variablesChanged.emit(values);\n }),\n editor.on('focus', () => this.focused.emit()),\n editor.on('blur', ({ event }) => {\n // Un blur vers la toolbar, un menu ou un dialogue de l'éditeur n'est pas une sortie.\n const related = event.relatedTarget;\n if (related instanceof Node && instance.element.contains(related)) {\n return;\n }\n this.onTouched?.();\n this.blurred.emit();\n }),\n ];\n untracked(() => this.applyAriaAttributes(editor, this.ariaLabel(), this.ariaLabelledBy()));\n if (untracked(() => this.wordPaste())) {\n this.loadWordPaste();\n }\n this.refreshVariableNames(editor);\n this.editorState.set(editor);\n }\n\n private unmount(): void {\n for (const unsubscribe of this.subscriptions) {\n unsubscribe();\n }\n this.subscriptions = [];\n this.instance?.destroy();\n this.instance = null;\n this.mountedToolbar = null;\n this.editorState.set(null);\n this.variableNamesState.set([]);\n }\n\n /**\n * Remonte l'éditeur en conservant le contenu, le focus, les déclarations et les valeurs\n * de variables ; l'historique est perdu.\n */\n private remount(): void {\n const current = this.instance;\n if (!current) {\n return;\n }\n const html = current.editor.getHTML();\n const focused = current.editor.isFocused();\n const variables: VariableSnapshot = {\n declared: current.editor.variables.list(),\n values: current.editor.variables.getValues(),\n };\n this.unmount();\n this.mount(html, variables);\n if (focused) {\n this.instance?.editor.focus('end');\n }\n }\n\n /**\n * Fusionne `ASTER_CONFIG` < `config` < inputs dédiés. Le transformateur Word est placé\n * en tête pour que les transformateurs de l'intégrateur reçoivent du HTML déjà nettoyé.\n * Avec `content` (remontage), `autofocus` est désactivé pour ne pas reprendre le focus et\n * `variables` réinjecte l'état du magasin précédent par-dessus la configuration.\n */\n private resolvedOptions(content?: string, variables?: VariableSnapshot): AsterUiOptions {\n const merged = this.mergedConfig();\n const initial: string | AsterDocument | undefined =\n content ?? this.pendingValue ?? merged.content;\n const source = this.resolvedVariableSource();\n const variableSource: VariableSource | undefined = variables\n ? {\n ...source,\n variables: [...(source?.variables ?? []), ...variables.declared],\n values: { ...source?.values, ...variables.values },\n }\n : source;\n return {\n ...merged,\n content: initial,\n autofocus: content === undefined ? merged.autofocus : false,\n toolbar: this.resolvedToolbar(),\n theme: this.resolvedTheme(),\n locale: this.resolvedLocale(),\n placeholder: this.resolvedPlaceholder(),\n readOnly: this.resolvedReadOnly(),\n pasteTransformers: [this.lazyWordTransformer, ...(merged.pasteTransformers ?? [])],\n upload: merged.upload ?? this.uploadHandler ?? undefined,\n variables: variableSource,\n variableSuggestion: this.resolvedVariableSuggestion(),\n };\n }\n\n /** Éditeur monté, sinon erreur explicite nommant la méthode appelée trop tôt. */\n private requireEditor(method: string): AsterEditor {\n const editor = this.instance?.editor;\n if (!editor) {\n throw new Error(`[aster-editor/angular] ${method}: editor is not mounted yet`);\n }\n return editor;\n }\n\n /** Relit `getNames()` ; le signal n'est notifié que si la liste change réellement. */\n private refreshVariableNames(editor: AsterEditor): void {\n this.variableNamesState.set(editor.variables.getNames());\n }\n\n /**\n * Après un import : déclare les variables trouvées dans le `.docx` (libellé et valeur des\n * contrôles de contenu compris), puis réapplique l'input `variableValues` pour les noms que\n * Word a lui-même valorisés, l'application restant la source de vérité des valeurs.\n */\n private declareImportedVariables(editor: AsterEditor, found: readonly AsterVariable[]): void {\n if (found.length === 0) {\n return;\n }\n editor.variables.define(found);\n const known = untracked(() => this.variableValues());\n if (!known) {\n return;\n }\n const overrides: Record<string, string> = {};\n for (const variable of found) {\n const value = known[variable.name];\n if (variable.value !== undefined && value !== undefined && value !== variable.value) {\n overrides[variable.name] = value;\n }\n }\n if (Object.keys(overrides).length > 0) {\n editor.variables.setValues(overrides);\n }\n }\n\n private loadWordPaste(): void {\n if (this.wordTransformer || this.wordLoading) {\n return;\n }\n this.wordLoading = import('@aster-editor/word')\n .then((word) => {\n this.wordTransformer = word.wordPasteTransformer();\n })\n .catch((error: unknown) => {\n this.wordLoading = null;\n console.warn(\n '[aster-editor/angular] @aster-editor/word unavailable, paste kept raw',\n error,\n );\n });\n }\n\n /** Nom accessible du contenteditable : `aria-label` et/ou `aria-labelledby`, retirés si vides. */\n private applyAriaAttributes(\n editor: AsterEditor,\n label: string | undefined,\n labelledBy: string | undefined,\n ): void {\n setOrRemoveAttribute(editor.contentElement, 'aria-label', label);\n setOrRemoveAttribute(editor.contentElement, 'aria-labelledby', labelledBy);\n }\n\n /**\n * Change le placeholder à chaud. L'extension `Placeholder` lit ses options au moment où\n * son plugin ProseMirror est créé (et `extension.options` est une copie à chaque accès),\n * donc on refabrique le plugin via `addProseMirrorPlugins` avec un `placeholder` surchargé\n * et on le substitue à l'ancien par `unregisterPlugin`/`registerPlugin`, API publiques de\n * Tiptap. Le repli sur la chaîne i18n `editor.placeholder` est conservé.\n */\n private applyPlaceholder(editor: AsterEditor, placeholder: string | undefined): void {\n const tiptap = editor.tiptap;\n const extension = tiptap.extensionManager.extensions.find(\n (candidate) => candidate.name === 'placeholder',\n );\n if (extension?.type !== 'extension') {\n return;\n }\n const factory = extension.config.addProseMirrorPlugins as ExtensionPluginFactory | undefined;\n if (!factory) {\n return;\n }\n const options: PlaceholderExtensionOptions = {\n ...extension.options,\n placeholder: (): string => placeholder ?? editor.i18n.t('editor.placeholder'),\n };\n const plugins = factory.call({\n name: extension.name,\n options,\n storage: extension.storage,\n editor: tiptap,\n type: null,\n parent: undefined,\n });\n for (const plugin of plugins) {\n tiptap.unregisterPlugin(plugin.spec.key ?? extension.name);\n tiptap.registerPlugin(plugin);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"aster-editor-angular.mjs","names":[],"sources":["../../src/lib/editor-dom.ts","../../src/lib/page-view.ts","../../src/lib/provide-aster.ts","../../src/lib/tokens.ts","../../src/lib/word.ts","../../src/lib/aster-editor.component.ts"],"sourcesContent":["import type { AsterEditor } from '@aster-editor/core';\n\n/**\n * Réglages appliqués à chaud sur l'éditeur monté (nom accessible, placeholder), hors de la\n * classe du composant pour la garder centrée sur le cycle de vie et les inputs.\n */\n\n/** Vue minimale des options de l'extension Tiptap `Placeholder`, pour la changer à chaud. */\ninterface PlaceholderExtensionOptions {\n placeholder?: unknown;\n}\n\ntype TiptapEditor = AsterEditor['tiptap'];\ntype ProseMirrorPlugin = Parameters<TiptapEditor['registerPlugin']>[0];\n\n/** Signature de `addProseMirrorPlugins` d'une extension Tiptap (hors nœud et marque). */\ntype ExtensionPluginFactory = (this: {\n name: string;\n options: PlaceholderExtensionOptions;\n storage: unknown;\n editor: TiptapEditor;\n type: null;\n parent: undefined;\n}) => ProseMirrorPlugin[];\n\n/** Pose l'attribut quand une valeur est fournie, le retire sinon. */\nfunction setOrRemoveAttribute(element: HTMLElement, name: string, value: string | undefined): void {\n if (value === undefined || value === '') {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value);\n }\n}\n\n/** Nom accessible du contenteditable : `aria-label` et/ou `aria-labelledby`, retirés si vides. */\nexport function applyAriaAttributes(\n editor: AsterEditor,\n label: string | undefined,\n labelledBy: string | undefined,\n): void {\n setOrRemoveAttribute(editor.contentElement, 'aria-label', label);\n setOrRemoveAttribute(editor.contentElement, 'aria-labelledby', labelledBy);\n}\n\n/**\n * Change le placeholder à chaud. L'extension `Placeholder` lit ses options au moment où\n * son plugin ProseMirror est créé (et `extension.options` est une copie à chaque accès),\n * donc on refabrique le plugin via `addProseMirrorPlugins` avec un `placeholder` surchargé\n * et on le substitue à l'ancien par `unregisterPlugin`/`registerPlugin`, API publiques de\n * Tiptap. Le repli sur la chaîne i18n `editor.placeholder` est conservé.\n */\nexport function applyPlaceholder(editor: AsterEditor, placeholder: string | undefined): void {\n const tiptap = editor.tiptap;\n const extension = tiptap.extensionManager.extensions.find(\n (candidate) => candidate.name === 'placeholder',\n );\n if (extension?.type !== 'extension') {\n return;\n }\n const factory = extension.config.addProseMirrorPlugins as ExtensionPluginFactory | undefined;\n if (!factory) {\n return;\n }\n const options: PlaceholderExtensionOptions = {\n ...extension.options,\n placeholder: (): string => placeholder ?? editor.i18n.t('editor.placeholder'),\n };\n const plugins = factory.call({\n name: extension.name,\n options,\n storage: extension.storage,\n editor: tiptap,\n type: null,\n parent: undefined,\n });\n for (const plugin of plugins) {\n tiptap.unregisterPlugin(plugin.spec.key ?? extension.name);\n tiptap.registerPlugin(plugin);\n }\n}\n","import { getActivePageView } from '@aster-editor/core';\nimport type { AsterEditor, DocumentLayout, PageViewOptions } from '@aster-editor/core';\nimport { pageViewApiOf } from '@aster-editor/ui';\n\n/**\n * Vue page du composant. Le core expose `setPageView` / `getLayout` hors contrat\n * (`api/editor.ts` ne les déclare pas encore) : on passe par `pageViewApiOf` de `ui`, qui\n * les détecte à l'exécution, et par `getActivePageView` du core pour relire les options\n * en vigueur (le dialogue « Mise en page » de la toolbar peut les avoir changées).\n */\n\n/** Valeur de l'input `pageView` du composant, identique à `AsterUiOptions.pageView`. */\nexport type PageViewInput = boolean | PageViewOptions | undefined;\n\n/**\n * Options de vue page à donner à `createAster` ou à `setPageView` : l'input (ou la\n * configuration) complété par la mise en page effective du composant, qui prime sur\n * `pageView.layout` quand elle existe. `undefined` et `false` passent tels quels.\n */\nexport function resolvePageViewOptions(\n pageView: PageViewInput,\n layout: DocumentLayout | null,\n): PageViewInput {\n if (pageView === undefined || pageView === false) {\n return pageView;\n }\n const base: PageViewOptions = pageView === true ? {} : pageView;\n return layout ? { ...base, layout } : base;\n}\n\n/** Active ou retire la vue page sur un éditeur monté ; sans effet si le core ne l'expose pas. */\nexport function setPageView(editor: AsterEditor, options: PageViewInput): void {\n const api = pageViewApiOf(editor);\n if (!api) {\n return;\n }\n if (options === undefined || options === false) {\n api.setPageView(false);\n } else {\n api.setPageView(options === true ? {} : options);\n }\n}\n\n/**\n * Mise en page de la vue page active, `null` si elle est inactive ou si le core ne l'expose\n * pas. L'activité est lue via `getActivePageView` : pendant la notification de retrait\n * (`setPageView` retire l'ancienne vue avant de poser la nouvelle), l'ancienne mise en page\n * est encore lisible par `getLayout` mais la vue n'est plus active, et doit être ignorée.\n */\nexport function readPageViewLayout(editor: AsterEditor): DocumentLayout | null {\n const active = getActivePageView(editor.contentElement);\n if (!active) {\n return null;\n }\n // Pendant la notification d'activation, `getLayout` ne connaît pas encore la nouvelle vue\n // (le core l'affecte au retour de `applyPageView`) : les options actives, elles, sont déjà\n // posées et portent la même référence de mise en page.\n return active.layout ?? pageViewApiOf(editor)?.getLayout() ?? null;\n}\n\n/**\n * Applique une mise en page à la vue page **si elle est active**, en conservant ses autres\n * options (pagination, affichage des en-têtes). Rien si la vue page est inactive ou si\n * elle montre déjà cette mise en page (même référence : c'est le cas juste après que le\n * dialogue « Mise en page » l'a posée). Rend vrai si la vue a été remontée.\n */\nexport function applyLayoutToPageView(editor: AsterEditor, layout: DocumentLayout): boolean {\n const api = pageViewApiOf(editor);\n const active = getActivePageView(editor.contentElement);\n if (!api || !active || api.getLayout() === layout) {\n return false;\n }\n api.setPageView({ ...active, layout });\n return true;\n}\n","import { InjectionToken, makeEnvironmentProviders } from '@angular/core';\nimport type { EnvironmentProviders } from '@angular/core';\nimport type { AsterUiOptions } from '@aster-editor/ui';\n\n/**\n * Configuration globale des éditeurs d'une application (ou d'une route, si fournie\n * dans ses `providers`). Le composant la fusionne avec l'input `config`, qui prime,\n * puis avec les inputs dédiés (`toolbar`, `theme`, `locale`, `placeholder`, `readonly`).\n */\nexport const ASTER_CONFIG = new InjectionToken<Partial<AsterUiOptions>>('ASTER_CONFIG');\n\n/**\n * Déclare la configuration globale d'Aster dans `ApplicationConfig.providers` ou dans\n * les `providers` d'une route. Fusion superficielle : une clé donnée ici est remplacée\n * en bloc par la même clé passée dans l'input `config` d'un composant.\n *\n * ```ts\n * provideAster({ locale: 'en', theme: 'night', linkPolicy: { defaultTarget: '_blank' } })\n * ```\n */\nexport function provideAster(config: Partial<AsterUiOptions>): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: ASTER_CONFIG, useValue: config }]);\n}\n","import { InjectionToken } from '@angular/core';\nimport type { UploadHandler } from '@aster-editor/core';\n\n/**\n * Hook d'upload d'image fourni par l'application (service HTTP, stockage, CDN…).\n * Optionnel : s'il est présent et qu'aucun `upload` n'est passé par `provideAster`\n * ni par l'input `config`, le composant le branche sur `AsterOptions.upload`.\n * Sans handler, les images sont insérées en data URL base64.\n */\nexport const ASTER_UPLOAD_HANDLER = new InjectionToken<UploadHandler>('ASTER_UPLOAD_HANDLER');\n","import { untracked } from '@angular/core';\nimport type {\n AsterEditor,\n AsterVariable,\n DocumentLayout,\n PasteTransformer,\n UploadHandler,\n VariableValues,\n} from '@aster-editor/core';\nimport type {\n DocxExportOptions,\n DocxImportOptions,\n DocxImportResult,\n FidelityReport,\n} from '@aster-editor/word';\n\n/**\n * Logique Word du composant, hors de la classe pour la garder lisible : chaque fonction\n * charge `@aster-editor/word` par `import()` au moment de l'appel, le paquet reste donc\n * optionnel et hors du bundle initial (mammoth, la lib `docx` et le comparateur ne sont\n * téléchargés qu'au premier usage).\n */\n\n/** Chemin de lecture d'un `.docx` : `styled` (lecteur OOXML d'Aster) ou `semantic` (mammoth). */\nexport type DocxImportFidelity = NonNullable<DocxImportOptions['fidelity']>;\n\n/** Objet Word non représentable signalé par l'import (`DocxImportResult.lost`). */\nexport type DocxLostObject = DocxImportResult['lost'][number];\n\n/** Ce que le composant sait au moment d'importer : hook d'upload et chemin de lecture. */\nexport interface WordImportDefaults {\n readonly upload: UploadHandler | undefined;\n readonly fidelity: DocxImportFidelity;\n}\n\n/** Import d'un fichier avec le hook d'upload de l'éditeur et le chemin de lecture demandé. */\nexport async function importWord(\n file: File,\n defaults: WordImportDefaults,\n): Promise<DocxImportResult> {\n const { importDocx } = await import('@aster-editor/word');\n return importDocx(file, {\n ...(defaults.upload ? { upload: defaults.upload } : {}),\n fidelity: defaults.fidelity,\n });\n}\n\n/** Ce que le composant conserve d'un import et qui complète les options d'export. */\nexport interface WordExportDefaults {\n /** Gabarit importé (`DocxImportResult.template`), `null` sans import. */\n readonly template: ArrayBuffer | null;\n /** Mise en page effective (import, input `layout` ou dialogue « Mise en page »). */\n readonly layout: DocumentLayout | null;\n /** Utiliser le gabarit importé quand `options.template` est absent. */\n readonly inTemplate: boolean;\n}\n\n/**\n * Complète les options d'export avec ce que le composant sait : les valeurs du magasin de\n * variables quand `options.variables.values` manque, le gabarit importé quand\n * `options.template` manque et que `inTemplate` l'autorise, la mise en page effective quand\n * `options.layout` manque. Avec un gabarit, `layout` ne sert qu'à régénérer un en-tête ou\n * un pied modifié (`layout.headers` / `footers` différents de ceux du gabarit) ; le reste\n * (format, marges, styles) vient du gabarit. Sans gabarit, `layout` pilote l'export neuf.\n */\nexport function resolveExportOptions(\n editor: AsterEditor,\n options: DocxExportOptions,\n defaults: WordExportDefaults,\n): DocxExportOptions {\n const variables = {\n ...options.variables,\n values: options.variables?.values ?? editor.variables.getValues(),\n };\n const template =\n options.template ?? (defaults.inTemplate && defaults.template ? defaults.template : undefined);\n const layout = options.layout ?? defaults.layout ?? undefined;\n return {\n ...options,\n variables,\n ...(template !== undefined ? { template } : {}),\n ...(layout !== undefined ? { layout } : {}),\n };\n}\n\n/** Export du document courant avec des options déjà résolues. */\nexport async function exportWord(editor: AsterEditor, options: DocxExportOptions): Promise<Blob> {\n const { exportDocx } = await import('@aster-editor/word');\n return exportDocx(editor.getHTML(), options);\n}\n\n/** Rapport de fidélité entre le gabarit importé et un export produit dans ce gabarit. */\nexport async function compareWithTemplate(\n template: ArrayBuffer,\n exported: Blob,\n): Promise<FidelityReport> {\n const { compareDocx } = await import('@aster-editor/word');\n return compareDocx(template, await exported.arrayBuffer());\n}\n\n/** Transformateur de collage Word, chargé à la demande. */\nexport async function loadWordPasteTransformer(): Promise<PasteTransformer> {\n const { wordPasteTransformer } = await import('@aster-editor/word');\n return wordPasteTransformer();\n}\n\n/**\n * Après un import : déclare les variables trouvées dans le `.docx` (libellé et valeur des\n * contrôles de contenu compris), puis réapplique les valeurs connues de l'application pour\n * les noms que Word a lui-même valorisés, l'application restant la source de vérité.\n */\nexport function declareImportedVariables(\n editor: AsterEditor,\n found: readonly AsterVariable[],\n known: () => VariableValues | undefined,\n): void {\n if (found.length === 0) {\n return;\n }\n editor.variables.define(found);\n const values = untracked(known);\n if (!values) {\n return;\n }\n const overrides: Record<string, string> = {};\n for (const variable of found) {\n const value = values[variable.name];\n if (variable.value !== undefined && value !== undefined && value !== variable.value) {\n overrides[variable.name] = value;\n }\n }\n if (Object.keys(overrides).length > 0) {\n editor.variables.setValues(overrides);\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n afterNextRender,\n computed,\n effect,\n forwardRef,\n inject,\n input,\n output,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\nimport type { ElementRef, Signal } from '@angular/core';\nimport { NG_VALUE_ACCESSOR } from '@angular/forms';\nimport type { ControlValueAccessor } from '@angular/forms';\nimport { PAGE_VIEW_CHANGE_EVENT } from '@aster-editor/core';\nimport type {\n AsterDocument,\n AsterEditor,\n AsterLocale,\n AsterTheme,\n AsterVariable,\n DocumentLayout,\n GetHtmlOptions,\n PasteTransformer,\n ToolbarConfig,\n Unsubscribe,\n VariableResolver,\n VariableSource,\n VariableValues,\n} from '@aster-editor/core';\nimport { createAster } from '@aster-editor/ui';\nimport type { AsterInstance, AsterUiOptions } from '@aster-editor/ui';\nimport type { DocxExportOptions, DocxImportResult, FidelityReport } from '@aster-editor/word';\nimport { applyAriaAttributes, applyPlaceholder } from './editor-dom';\nimport {\n applyLayoutToPageView,\n readPageViewLayout,\n resolvePageViewOptions,\n setPageView,\n} from './page-view';\nimport type { PageViewInput } from './page-view';\nimport { ASTER_CONFIG } from './provide-aster';\nimport { ASTER_UPLOAD_HANDLER } from './tokens';\nimport {\n compareWithTemplate,\n declareImportedVariables,\n exportWord,\n importWord,\n loadWordPasteTransformer,\n resolveExportOptions,\n} from './word';\nimport type { DocxImportFidelity, DocxLostObject } from './word';\n\n/** Égalité de deux listes de noms, pour ne pas notifier `variableNames` sans changement réel. */\nfunction sameNames(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((name, index) => name === b[index]);\n}\n\n/** État du magasin de variables à conserver au travers d'un remontage. */\ninterface VariableSnapshot {\n readonly declared: readonly AsterVariable[];\n readonly values: VariableValues;\n}\n\n/**\n * Composant Angular standalone autour de `createAster` (`@aster-editor/ui`).\n *\n * - `ControlValueAccessor` : utilisable avec `formControlName`, `[formControl]` et `ngModel`.\n * La valeur du formulaire est le HTML sémantique de `getHTML()`.\n * - Zoneless et `OnPush` : rien dans le template ne dépend de l'état, les sorties sont\n * des `output()` et l'éditeur est exposé en signal.\n * - SSR-safe : l'éditeur est monté dans `afterNextRender`, jamais dans le constructeur, et\n * le module Word n'est chargé qu'à partir du montage (donc jamais côté serveur).\n *\n * Configuration, par priorité croissante : `provideAster(config)` < input `config` <\n * inputs dédiés (`toolbar`, `theme`, `locale`, `placeholder`, `readonly`). Un input dédié\n * laissé à `undefined` n'écrase donc pas la valeur venue de la configuration.\n *\n * Changements à chaud : `theme`, `locale`, `readonly`, `placeholder`, `ariaLabel`,\n * `ariaLabelledBy`, `variables` et `variableValues` sont appliqués sur l'instance vivante.\n * `toolbar` remonte l'éditeur (le contenu, les déclarations et les valeurs de variables sont\n * conservés, l'historique d'annulation est perdu, `ready` est réémis avec la nouvelle\n * instance). Les autres clés de `config` (`extensions`, `commands`, `upload`…) ne sont lues\n * qu'au montage.\n *\n * Variables (champs de fusion) : les inputs `variables`, `variableValues`, `variableResolver`,\n * `allowUnknownVariables` et `variableSuggestion` se fusionnent dans `options.variables` et\n * `variableSuggestion` de `createAster`. `variableNames` expose les noms présents dans le\n * document, `variablesChanged` relaie `variablesChange`, `exportDocx` produit le `.docx` avec\n * les valeurs du magasin.\n *\n * Fidélité Word (session 3) : `importDocx` lit le `.docx` en mode stylé et conserve le\n * gabarit (`importedTemplate`), la mise en page (`layout`) et les objets non représentables\n * (`lostObjects`) ; `exportDocx` réutilise le gabarit par défaut (`exportInTemplate`) et\n * transmet la mise en page ; `pageView` active la vue page, `layout` (input) impose une mise\n * en page, `layoutChanged` signale celle que montre la vue page (dialogue « Mise en page »\n * compris) ; `compareWithTemplate` mesure la fidélité de l'export dans le gabarit.\n */\n@Component({\n selector: 'aster-editor',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: '<div #host class=\"aster-editor__host\"></div>',\n styles: ':host { display: block; }',\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => AsterEditorComponent),\n multi: true,\n },\n ],\n})\nexport class AsterEditorComponent implements ControlValueAccessor {\n /** Preset, groupes déclaratifs, ou `false` pour aucune toolbar. Défaut effectif : `standard`. */\n readonly toolbar = input<ToolbarConfig | undefined>(undefined);\n /** Thème appliqué sur `.aster[data-theme]`. `light` et `night` sont fournis par `ui`. Défaut : `light`. */\n readonly theme = input<AsterTheme | undefined>(undefined);\n /** Locale de l'éditeur et de la toolbar. Défaut effectif : `fr`. */\n readonly locale = input<AsterLocale | undefined>(undefined);\n /** Texte affiché quand le document est vide. Sans valeur, la chaîne i18n `editor.placeholder`. */\n readonly placeholder = input<string | undefined>(undefined);\n /** Lecture seule. Le `disabled` d'un formulaire s'y ajoute (l'un ou l'autre suffit). Défaut : `false`. */\n readonly readonly = input<boolean | undefined>(undefined);\n /** Tout le reste des options (`extensions`, `commands`, `linkPolicy`, `upload`, `content`…). */\n readonly config = input<Partial<AsterUiOptions>>({});\n /**\n * Nettoyage automatique du HTML collé depuis Word et Google Docs via\n * `wordPasteTransformer()` de `@aster-editor/word`, chargé dynamiquement au montage.\n */\n readonly wordPaste = input<boolean>(true);\n /**\n * Nom accessible de la zone de saisie (`aria-label` sur le contenteditable). Un\n * contenteditable n'est pas associable à un `<label for>`, c'est le seul moyen de le nommer.\n */\n readonly ariaLabel = input<string | undefined>(undefined);\n /** `id` (ou liste d'ids) de l'élément qui sert de libellé (`aria-labelledby` sur le contenteditable). */\n readonly ariaLabelledBy = input<string | undefined>(undefined);\n /**\n * Variables déclarées (libellé, description, valeur), proposées dans le menu d'insertion\n * même si absentes du document. À chaud : `editor.variables.define(...)`, fusion par nom.\n */\n readonly variables = input<readonly AsterVariable[] | undefined>(undefined);\n /**\n * Valeurs par nom. À chaud : `editor.variables.setValues(...)`, fusion avec les valeurs\n * connues (une clé absente n'efface rien, `undefined` efface).\n */\n readonly variableValues = input<VariableValues | undefined>(undefined);\n /** Résolveur asynchrone appelé par `resolveVariables()` pour les variables sans valeur. Lu au montage. */\n readonly variableResolver = input<VariableResolver | undefined>(undefined);\n /** Autoriser l'insertion d'une variable non déclarée. Défaut effectif : `true`. Lu au montage. */\n readonly allowUnknownVariables = input<boolean | undefined>(undefined);\n /** Menu de suggestion déclenché par `{{` dans le contenu. Défaut effectif : `true`. Lu au montage. */\n readonly variableSuggestion = input<boolean | undefined>(undefined);\n /**\n * Chemin de lecture d'`importDocx` : `styled` (lecteur OOXML d'Aster, HTML stylé, mise en\n * page et gabarit renseignés) ou `semantic` (mammoth, HTML sémantique, mise en page vide).\n * Défaut effectif : `styled`. Lu à chaque import.\n */\n readonly importFidelity = input<DocxImportFidelity | undefined>(undefined);\n /**\n * `exportDocx` sans `options.template` réutilise le gabarit du dernier import quand il\n * existe : tout ce qui n'est pas le corps ressort identique. `false` pour l'export neuf\n * (un `options.template` explicite reste honoré). Défaut : `true`. Lu à chaque export.\n */\n readonly exportInTemplate = input<boolean>(true);\n /**\n * Vue page (`AsterUiOptions.pageView`) : `true` pour les défauts d'Aster (A4 portrait), un\n * objet pour la mise en page, la pagination et l'affichage des en-têtes. La mise en page\n * effective du composant (`layout()`) prime sur `pageView.layout`. À chaud : la vue est\n * remontée. Le dialogue « Mise en page » de la toolbar peut aussi l'activer ou la modifier.\n */\n readonly pageView = input<PageViewInput>(undefined);\n /**\n * Mise en page imposée (input `layout`) : prime sur celle de l'import et sur celle posée\n * par le dialogue, se répercute sur la vue page active et sur `exportDocx`. Laisse\n * `undefined` pour suivre l'import ; avec une liaison, tiens-la à jour depuis\n * `layoutChanged` pour que le dialogue « Mise en page » reste pris en compte.\n */\n readonly layoutOverride = input<DocumentLayout | undefined>(undefined, { alias: 'layout' });\n\n /** L'éditeur est monté et prêt. Réémis après un remontage (changement de `toolbar`). */\n readonly ready = output<AsterEditor>();\n /** Le document a changé : HTML de `getHTML()`. Pas émis sur `writeValue`. */\n readonly changed = output<string>();\n /** Le contenteditable a pris le focus. */\n readonly focused = output<void>();\n /**\n * Le contenteditable a perdu le focus au profit d'un élément extérieur à l'éditeur. Un\n * blur vers la toolbar ou un de ses menus n'est pas remonté : l'utilisateur est toujours\n * « dans » l'éditeur. C'est aussi ce qui marque le contrôle de formulaire `touched`.\n */\n readonly blurred = output<void>();\n /** Les valeurs ou déclarations de variables ont changé (`setValues`, `define`, `resolve`, import). */\n readonly variablesChanged = output<VariableValues>();\n /**\n * La mise en page effective a changé : import, input `layout`, ou dialogue « Mise en page »\n * de la toolbar (la vue page est alors relue). Jamais émis pour `null` (vue page retirée).\n */\n readonly layoutChanged = output<DocumentLayout>();\n\n private readonly editorState = signal<AsterEditor | null>(null);\n /** Éditeur courant, `null` avant le montage et après la destruction. */\n readonly editor: Signal<AsterEditor | null> = this.editorState.asReadonly();\n\n private readonly importedTemplateState = signal<ArrayBuffer | null>(null);\n /** Le `.docx` du dernier import, intact, réutilisé comme gabarit à l'export. `null` sans import. */\n readonly importedTemplate: Signal<ArrayBuffer | null> = this.importedTemplateState.asReadonly();\n\n private readonly lostObjectsState = signal<readonly DocxLostObject[]>([]);\n /** Objets Word non représentables rencontrés par le dernier import (vide en mode `semantic`). */\n readonly lostObjects: Signal<readonly DocxLostObject[]> = this.lostObjectsState.asReadonly();\n\n /** Mise en page lue par l'import ou posée par le dialogue « Mise en page ». */\n private readonly layoutState = signal<DocumentLayout | null>(null);\n /**\n * Mise en page effective : l'input `layout` s'il est fourni, sinon celle du dernier import\n * ou du dialogue « Mise en page ». `null` avant tout import sans vue page.\n */\n readonly layout: Signal<DocumentLayout | null> = computed(\n () => this.layoutOverride() ?? this.layoutState(),\n );\n\n private readonly variableNamesState = signal<readonly string[]>([], { equal: sameNames });\n /**\n * Noms des variables réellement présentes dans le document (`editor.variables.getNames()`),\n * relus sur `change` et `variablesChange`. Vide avant le montage et après la destruction.\n */\n readonly variableNames: Signal<readonly string[]> = this.variableNamesState.asReadonly();\n\n private readonly host = viewChild.required<ElementRef<HTMLDivElement>>('host');\n private readonly globalConfig = inject(ASTER_CONFIG, { optional: true });\n private readonly uploadHandler = inject(ASTER_UPLOAD_HANDLER, { optional: true });\n private readonly formDisabled = signal(false);\n\n /** `ASTER_CONFIG` < `config`, fusion superficielle. */\n private readonly mergedConfig = computed<Partial<AsterUiOptions>>(() => ({\n ...(this.globalConfig ?? {}),\n ...this.config(),\n }));\n private readonly resolvedToolbar = computed<ToolbarConfig>(\n () => this.toolbar() ?? this.mergedConfig().toolbar ?? 'standard',\n );\n private readonly resolvedTheme = computed<AsterTheme>(\n () => this.theme() ?? this.mergedConfig().theme ?? 'light',\n );\n private readonly resolvedLocale = computed<AsterLocale>(\n () => this.locale() ?? this.mergedConfig().locale ?? 'fr',\n );\n private readonly resolvedPlaceholder = computed<string | undefined>(\n () => this.placeholder() ?? this.mergedConfig().placeholder,\n );\n private readonly resolvedReadOnly = computed<boolean>(\n () => (this.readonly() ?? this.mergedConfig().readOnly ?? false) || this.formDisabled(),\n );\n /** `config.variables` < inputs dédiés, clé par clé ; un input `undefined` ne masque rien. */\n private readonly resolvedVariableSource = computed<VariableSource | undefined>(() => {\n const base = this.mergedConfig().variables;\n const source: VariableSource = {\n ...base,\n ...(this.variables() !== undefined ? { variables: this.variables() } : {}),\n ...(this.variableValues() !== undefined ? { values: this.variableValues() } : {}),\n ...(this.variableResolver() !== undefined ? { resolve: this.variableResolver() } : {}),\n ...(this.allowUnknownVariables() !== undefined\n ? { allowUnknown: this.allowUnknownVariables() }\n : {}),\n };\n return Object.keys(source).length > 0 ? source : undefined;\n });\n private readonly resolvedVariableSuggestion = computed<boolean | undefined>(\n () => this.variableSuggestion() ?? this.mergedConfig().variableSuggestion,\n );\n private readonly resolvedPageView = computed<PageViewInput>(\n () => this.pageView() ?? this.mergedConfig().pageView,\n );\n\n private instance: AsterInstance | null = null;\n private mountedToolbar: ToolbarConfig | null = null;\n private mountedPageView: PageViewInput = undefined;\n /** Dernière mise en page émise par `layoutChanged`, pour ne pas la réémettre. */\n private emittedLayout: DocumentLayout | null = null;\n private subscriptions: Unsubscribe[] = [];\n /** Valeur reçue par `writeValue` avant le montage, appliquée comme contenu initial. */\n private pendingValue: string | null = null;\n private wordTransformer: PasteTransformer | null = null;\n private wordLoading: Promise<void> | null = null;\n private onChange: ((html: string) => void) | null = null;\n private onTouched: (() => void) | null = null;\n\n /**\n * Transformateur synchrone branché au montage ; il délègue au vrai transformateur Word\n * une fois le module chargé. Garde `@aster-editor/word` optionnel et hors du bundle\n * initial tout en respectant le contrat synchrone de `PasteTransformer`.\n */\n private readonly lazyWordTransformer: PasteTransformer = (html, context) =>\n this.wordTransformer && untracked(() => this.wordPaste())\n ? this.wordTransformer(html, context)\n : html;\n\n constructor() {\n afterNextRender(() => this.mount());\n inject(DestroyRef).onDestroy(() => this.unmount());\n\n effect(() => {\n const theme = this.resolvedTheme();\n untracked(() => this.instance?.setTheme(theme));\n });\n effect(() => {\n const locale = this.resolvedLocale();\n untracked(() => {\n const editor = this.instance?.editor;\n if (editor && editor.i18n.getLocale() !== locale) {\n editor.i18n.setLocale(locale);\n }\n });\n });\n effect(() => {\n const readOnly = this.resolvedReadOnly();\n untracked(() => this.instance?.editor.setReadOnly(readOnly));\n });\n effect(() => {\n const placeholder = this.resolvedPlaceholder();\n untracked(() => {\n if (this.instance) {\n applyPlaceholder(this.instance.editor, placeholder);\n }\n });\n });\n effect(() => {\n const label = this.ariaLabel();\n const labelledBy = this.ariaLabelledBy();\n untracked(() => {\n if (this.instance) {\n applyAriaAttributes(this.instance.editor, label, labelledBy);\n }\n });\n });\n effect(() => {\n const toolbar = this.resolvedToolbar();\n untracked(() => {\n if (this.instance && this.mountedToolbar !== toolbar) {\n this.remount();\n }\n });\n });\n // Le chargement initial du module Word part de `mount()` (jamais côté serveur) ; cet\n // effet ne couvre que le passage `false → true` d'un éditeur déjà monté.\n effect(() => {\n if (this.wordPaste()) {\n untracked(() => {\n if (this.instance) {\n this.loadWordPaste();\n }\n });\n }\n });\n // Au montage, déclarations et valeurs passent par `options.variables` ; ces deux effets\n // ne couvrent que les changements d'input sur un éditeur déjà monté.\n effect(() => {\n const declared = this.variables();\n untracked(() => {\n if (declared && this.instance) {\n this.instance.editor.variables.define(declared);\n }\n });\n });\n effect(() => {\n const values = this.variableValues();\n untracked(() => {\n if (values && this.instance) {\n this.instance.editor.variables.setValues(values);\n }\n });\n });\n // Au montage, la vue page passe par `options.pageView` ; cet effet ne couvre que les\n // changements d'input sur un éditeur déjà monté.\n effect(() => {\n const pageView = this.resolvedPageView();\n untracked(() => {\n if (this.instance && this.mountedPageView !== pageView) {\n this.mountedPageView = pageView;\n setPageView(this.instance.editor, resolvePageViewOptions(pageView, this.layout()));\n }\n });\n });\n // Mise en page effective : répercutée sur la vue page active, signalée par `layoutChanged`.\n effect(() => {\n const layout = this.layout();\n untracked(() => {\n if (layout && this.instance) {\n applyLayoutToPageView(this.instance.editor, layout);\n }\n this.notifyLayout(layout);\n });\n });\n }\n\n // --- API publique --------------------------------------------------------\n\n /**\n * Importe un `.docx` via `@aster-editor/word` (chargé à la demande, le lecteur reste dans\n * un chunk séparé), remplace le contenu de l'éditeur en émettant `change` pour que le\n * formulaire voie la nouvelle valeur, et rend le résultat avec ses avertissements.\n * Le hook d'upload de l'éditeur est réutilisé pour les images embarquées. En mode `styled`\n * (défaut, voir `importFidelity`), le gabarit, la mise en page et les objets non\n * représentables sont conservés dans `importedTemplate`, `layout` et `lostObjects`, et la\n * mise en page est appliquée à la vue page si elle est active. Si le composant est détruit\n * ou remonté pendant l'import, le résultat est rendu mais rien n'est écrit ni conservé :\n * l'instance vivante est relue après chaque `await`.\n */\n async importDocx(file: File): Promise<DocxImportResult> {\n this.requireEditor('importDocx');\n const defaults = untracked(() => ({\n upload: this.resolvedOptions().upload,\n fidelity: this.importFidelity() ?? 'styled',\n }));\n const result = await importWord(file, defaults);\n const editor = this.instance?.editor;\n if (editor && !editor.isDestroyed()) {\n editor.setHTML(result.html, true);\n declareImportedVariables(editor, result.variables, () => this.variableValues());\n this.importedTemplateState.set(result.template);\n this.lostObjectsState.set(result.lost);\n this.layoutState.set(result.layout);\n }\n return result;\n }\n\n /**\n * Convertit le document en `.docx` via `exportDocx` de `@aster-editor/word` (chargé à la\n * demande, la lib `docx` reste dans son chunk). Les valeurs du magasin de variables sont\n * transmises quand `options.variables.values` n'est pas fourni : `mode: 'resolve'` substitue\n * donc ce que l'utilisateur a saisi, `'fields'` produit des champs de fusion Word avec ces\n * valeurs comme résultat. Sans `options.template`, le gabarit du dernier import est utilisé\n * si `exportInTemplate` est vrai ; sans `options.layout`, la mise en page effective\n * (`layout()`) est transmise. Rend le `Blob`, à passer à `downloadDocx` ou à un upload.\n */\n async exportDocx(options: DocxExportOptions = {}): Promise<Blob> {\n const editor = this.requireEditor('exportDocx');\n const resolved = untracked(() =>\n resolveExportOptions(editor, options, {\n template: this.importedTemplate(),\n layout: this.layout(),\n inTemplate: this.exportInTemplate(),\n }),\n );\n return exportWord(editor, resolved);\n }\n\n /**\n * Rapport de fidélité du document courant : export dans le gabarit du dernier import\n * (valeurs du magasin de variables, mise en page effective) puis `compareDocx(gabarit,\n * export)`. `null` sans gabarit importé. Le comparateur est chargé à la demande.\n */\n async compareWithTemplate(): Promise<FidelityReport | null> {\n const template = this.importedTemplate();\n if (!template) {\n return null;\n }\n const exported = await this.exportDocx({ template });\n return compareWithTemplate(template, exported);\n }\n\n /**\n * HTML du document, `{ variables: 'resolve' }` substitue les valeurs connues. Avant le\n * montage, rend la valeur reçue par `writeValue` (puces conservées) ou une chaîne vide.\n */\n getHTML(options?: GetHtmlOptions): string {\n return this.instance?.editor.getHTML(options) ?? this.pendingValue ?? '';\n }\n\n /**\n * Appelle le résolveur (`variableResolver` ou `config.variables.resolve`) pour les\n * variables présentes sans valeur, applique les valeurs obtenues et les rend toutes.\n */\n resolveVariables(): Promise<VariableValues> {\n return this.requireEditor('resolveVariables').variables.resolve();\n }\n\n // --- ControlValueAccessor ------------------------------------------------\n\n /**\n * Applique la valeur du formulaire sans émettre `change` ni entrer dans l'historique ;\n * mémorisée si l'éditeur n'est pas monté. Une valeur identique au HTML courant est\n * ignorée pour ne pas déplacer la sélection (les reactive forms réécrivent la valeur à\n * chaque `setValue`, y compris quand elle n'a pas changé).\n */\n writeValue(value: string | null | undefined): void {\n const html = value ?? '';\n const editor = this.instance?.editor;\n if (!editor) {\n this.pendingValue = html;\n return;\n }\n if (editor.getHTML() === html) {\n return;\n }\n editor.setHTML(html, false);\n }\n\n /** Enregistre le rappel appelé avec le HTML à chaque modification du document par l'utilisateur. */\n registerOnChange(fn: (html: string) => void): void {\n this.onChange = fn;\n }\n\n /** Enregistre le rappel appelé quand l'éditeur perd le focus vers l'extérieur (`touched`). */\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n /** `disabled` du formulaire → lecture seule, cumulé avec l'input `readonly`. */\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n this.instance?.editor.setReadOnly(untracked(() => this.resolvedReadOnly()));\n }\n\n // --- Cycle de vie --------------------------------------------------------\n\n /**\n * Monte l'éditeur. `content` force le contenu initial (remontage) et, dans ce cas,\n * `autofocus` est neutralisé : le focus est restauré explicitement par `remount()`.\n */\n private mount(content?: string, variables?: VariableSnapshot): void {\n if (this.instance) {\n return;\n }\n const options = untracked(() => this.resolvedOptions(content, variables));\n const instance = createAster(this.host().nativeElement, options);\n const editor = instance.editor;\n this.instance = instance;\n this.mountedToolbar = options.toolbar ?? 'standard';\n this.mountedPageView = untracked(() => this.resolvedPageView());\n this.pendingValue = null;\n // Le dialogue « Mise en page » de la toolbar pose sa mise en page via `setPageView` sans\n // passer par le composant : le core signale chaque changement de vue page par un\n // événement DOM sur le contenteditable, on y relit la mise en page en vigueur.\n const onPageViewChange = (): void => this.readLayoutFromPageView(editor);\n editor.contentElement.addEventListener(PAGE_VIEW_CHANGE_EVENT, onPageViewChange);\n this.subscriptions = [\n () => editor.contentElement.removeEventListener(PAGE_VIEW_CHANGE_EVENT, onPageViewChange),\n ];\n this.subscriptions.push(\n editor.on('ready', ({ editor: ready }) => this.ready.emit(ready)),\n editor.on('change', ({ editor: changed }) => {\n const html = changed.getHTML();\n this.onChange?.(html);\n this.changed.emit(html);\n this.refreshVariableNames(changed);\n }),\n editor.on('variablesChange', ({ editor: source, values }) => {\n this.refreshVariableNames(source);\n this.variablesChanged.emit(values);\n }),\n editor.on('focus', () => this.focused.emit()),\n editor.on('blur', ({ event }) => {\n // Un blur vers la toolbar, un menu ou un dialogue de l'éditeur n'est pas une sortie.\n const related = event.relatedTarget;\n if (related instanceof Node && instance.element.contains(related)) {\n return;\n }\n this.onTouched?.();\n this.blurred.emit();\n }),\n );\n untracked(() => applyAriaAttributes(editor, this.ariaLabel(), this.ariaLabelledBy()));\n // Une vue page montée depuis `options.pageView` sans import : sa mise en page devient\n // celle du composant (le dialogue de la toolbar part de la même base).\n this.readLayoutFromPageView(editor);\n if (untracked(() => this.wordPaste())) {\n this.loadWordPaste();\n }\n this.refreshVariableNames(editor);\n this.editorState.set(editor);\n }\n\n private unmount(): void {\n for (const unsubscribe of this.subscriptions) {\n unsubscribe();\n }\n this.subscriptions = [];\n this.instance?.destroy();\n this.instance = null;\n this.mountedToolbar = null;\n this.mountedPageView = undefined;\n this.editorState.set(null);\n this.variableNamesState.set([]);\n }\n\n /**\n * Remonte l'éditeur en conservant le contenu, le focus, les déclarations et les valeurs\n * de variables ; l'historique est perdu.\n */\n private remount(): void {\n const current = this.instance;\n if (!current) {\n return;\n }\n const html = current.editor.getHTML();\n const focused = current.editor.isFocused();\n const variables: VariableSnapshot = {\n declared: current.editor.variables.list(),\n values: current.editor.variables.getValues(),\n };\n this.unmount();\n this.mount(html, variables);\n if (focused) {\n this.instance?.editor.focus('end');\n }\n }\n\n /**\n * Fusionne `ASTER_CONFIG` < `config` < inputs dédiés. Le transformateur Word est placé\n * en tête pour que les transformateurs de l'intégrateur reçoivent du HTML déjà nettoyé.\n * Avec `content` (remontage), `autofocus` est désactivé pour ne pas reprendre le focus et\n * `variables` réinjecte l'état du magasin précédent par-dessus la configuration.\n */\n private resolvedOptions(content?: string, variables?: VariableSnapshot): AsterUiOptions {\n const merged = this.mergedConfig();\n const initial: string | AsterDocument | undefined =\n content ?? this.pendingValue ?? merged.content;\n const source = this.resolvedVariableSource();\n const variableSource: VariableSource | undefined = variables\n ? {\n ...source,\n variables: [...(source?.variables ?? []), ...variables.declared],\n values: { ...source?.values, ...variables.values },\n }\n : source;\n return {\n ...merged,\n content: initial,\n autofocus: content === undefined ? merged.autofocus : false,\n toolbar: this.resolvedToolbar(),\n theme: this.resolvedTheme(),\n locale: this.resolvedLocale(),\n placeholder: this.resolvedPlaceholder(),\n readOnly: this.resolvedReadOnly(),\n pasteTransformers: [this.lazyWordTransformer, ...(merged.pasteTransformers ?? [])],\n upload: merged.upload ?? this.uploadHandler ?? undefined,\n variables: variableSource,\n variableSuggestion: this.resolvedVariableSuggestion(),\n pageView: resolvePageViewOptions(this.resolvedPageView(), this.layout()),\n };\n }\n\n /**\n * Relit la mise en page de la vue page active et l'adopte. Si l'input `layout` la masque,\n * `layoutChanged` est émis directement pour que l'application puisse l'adopter ; sinon\n * l'effet sur `layout()` s'en charge.\n */\n private readLayoutFromPageView(editor: AsterEditor): void {\n const current = readPageViewLayout(editor);\n // Rien à relire si la vue montre déjà l'état connu, ou l'input `layout` que le composant\n // vient lui-même de poser : l'état garde alors la mise en page de l'import, retrouvée\n // quand l'input repasse à `undefined`.\n if (\n !current ||\n current === untracked(() => this.layoutState()) ||\n current === untracked(() => this.layoutOverride())\n ) {\n return;\n }\n this.layoutState.set(current);\n if (untracked(() => this.layout()) !== current) {\n this.notifyLayout(current);\n }\n }\n\n /** Émet `layoutChanged` une seule fois par mise en page (même référence = déjà émise). */\n private notifyLayout(layout: DocumentLayout | null): void {\n if (layout && layout !== this.emittedLayout) {\n this.emittedLayout = layout;\n this.layoutChanged.emit(layout);\n }\n }\n\n /** Éditeur monté, sinon erreur explicite nommant la méthode appelée trop tôt. */\n private requireEditor(method: string): AsterEditor {\n const editor = this.instance?.editor;\n if (!editor) {\n throw new Error(`[aster-editor/angular] ${method}: editor is not mounted yet`);\n }\n return editor;\n }\n\n /** Relit `getNames()` ; le signal n'est notifié que si la liste change réellement. */\n private refreshVariableNames(editor: AsterEditor): void {\n this.variableNamesState.set(editor.variables.getNames());\n }\n\n private loadWordPaste(): void {\n if (this.wordTransformer || this.wordLoading) {\n return;\n }\n this.wordLoading = loadWordPasteTransformer()\n .then((transformer) => {\n this.wordTransformer = transformer;\n })\n .catch((error: unknown) => {\n this.wordLoading = null;\n console.warn(\n '[aster-editor/angular] @aster-editor/word unavailable, paste kept raw',\n error,\n );\n });\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aster-editor/angular",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Composant Angular standalone pour Aster Editor : reactive forms, signals, zoneless",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Joris Tremoulet",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"@angular/common": ">=20.0.0",
|
|
31
31
|
"@angular/core": ">=20.0.0",
|
|
32
32
|
"@angular/forms": ">=20.0.0",
|
|
33
|
-
"@aster-editor/core": "^0.
|
|
34
|
-
"@aster-editor/ui": "^0.
|
|
35
|
-
"@aster-editor/word": "^0.
|
|
33
|
+
"@aster-editor/core": "^0.5.0",
|
|
34
|
+
"@aster-editor/ui": "^0.5.0",
|
|
35
|
+
"@aster-editor/word": "^0.5.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependenciesMeta": {
|
|
38
38
|
"@aster-editor/word": {
|
|
@@ -1,9 +1,86 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
2
|
import { EnvironmentProviders, InjectionToken, Signal } from "@angular/core";
|
|
3
3
|
import { ControlValueAccessor } from "@angular/forms";
|
|
4
|
-
import { AsterCounts, AsterDocument, AsterEditor, AsterEditor as AsterEditor$1, AsterLocale, AsterLocale as AsterLocale$1, AsterTheme, AsterTheme as AsterTheme$1, AsterVariable, AsterVariable as AsterVariable$1, GetHtmlOptions, GetHtmlOptions as GetHtmlOptions$1, LinkPolicy, PasteTransformer, ToolbarConfig, ToolbarConfig as ToolbarConfig$1, ToolbarPreset, UploadHandler, UploadHandler as UploadHandler$1, VariableRenderMode, VariableResolver, VariableResolver as VariableResolver$1, VariableSource, VariableValues, VariableValues as VariableValues$1 } from "@aster-editor/core";
|
|
4
|
+
import { AsterCounts, AsterDocument, AsterEditor, AsterEditor as AsterEditor$1, AsterLocale, AsterLocale as AsterLocale$1, AsterTheme, AsterTheme as AsterTheme$1, AsterVariable, AsterVariable as AsterVariable$1, DocumentLayout, DocumentLayout as DocumentLayout$1, GetHtmlOptions, GetHtmlOptions as GetHtmlOptions$1, HeaderFooterContent, HeaderFooterSet, LinkPolicy, PageMargins, PageSetup, PageViewOptions, PageViewOptions as PageViewOptions$1, PasteTransformer, PasteTransformer as PasteTransformer$1, ToolbarConfig, ToolbarConfig as ToolbarConfig$1, ToolbarPreset, UploadHandler, UploadHandler as UploadHandler$1, VariableRenderMode, VariableResolver, VariableResolver as VariableResolver$1, VariableSource, VariableValues, VariableValues as VariableValues$1 } from "@aster-editor/core";
|
|
5
5
|
import { AsterUiOptions, AsterUiOptions as AsterUiOptions$1 } from "@aster-editor/ui";
|
|
6
|
-
import { DocxExportOptions, DocxExportOptions as DocxExportOptions$1, DocxExportVariableOptions, DocxExportWarning, DocxImportResult, DocxImportResult as DocxImportResult$1, DocxImportWarning, DocxVariableMode } from "@aster-editor/word";
|
|
6
|
+
import { DocxExportOptions, DocxExportOptions as DocxExportOptions$1, DocxExportVariableOptions, DocxExportWarning, DocxImportOptions, DocxImportResult, DocxImportResult as DocxImportResult$1, DocxImportWarning, DocxVariableMode, FidelityExample, FidelityProperty, FidelityPropertyGroup, FidelityReport, FidelityReport as FidelityReport$1 } from "@aster-editor/word";
|
|
7
|
+
/**
|
|
8
|
+
* Vue page du composant. Le core expose `setPageView` / `getLayout` hors contrat
|
|
9
|
+
* (`api/editor.ts` ne les déclare pas encore) : on passe par `pageViewApiOf` de `ui`, qui
|
|
10
|
+
* les détecte à l'exécution, et par `getActivePageView` du core pour relire les options
|
|
11
|
+
* en vigueur (le dialogue « Mise en page » de la toolbar peut les avoir changées).
|
|
12
|
+
*/
|
|
13
|
+
/** Valeur de l'input `pageView` du composant, identique à `AsterUiOptions.pageView`. */
|
|
14
|
+
type PageViewInput = boolean | PageViewOptions$1 | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Options de vue page à donner à `createAster` ou à `setPageView` : l'input (ou la
|
|
17
|
+
* configuration) complété par la mise en page effective du composant, qui prime sur
|
|
18
|
+
* `pageView.layout` quand elle existe. `undefined` et `false` passent tels quels.
|
|
19
|
+
*/
|
|
20
|
+
declare function resolvePageViewOptions(pageView: PageViewInput, layout: DocumentLayout$1 | null): PageViewInput;
|
|
21
|
+
/** Active ou retire la vue page sur un éditeur monté ; sans effet si le core ne l'expose pas. */
|
|
22
|
+
declare function setPageView(editor: AsterEditor$1, options: PageViewInput): void;
|
|
23
|
+
/**
|
|
24
|
+
* Mise en page de la vue page active, `null` si elle est inactive ou si le core ne l'expose
|
|
25
|
+
* pas. L'activité est lue via `getActivePageView` : pendant la notification de retrait
|
|
26
|
+
* (`setPageView` retire l'ancienne vue avant de poser la nouvelle), l'ancienne mise en page
|
|
27
|
+
* est encore lisible par `getLayout` mais la vue n'est plus active, et doit être ignorée.
|
|
28
|
+
*/
|
|
29
|
+
declare function readPageViewLayout(editor: AsterEditor$1): DocumentLayout$1 | null;
|
|
30
|
+
/**
|
|
31
|
+
* Applique une mise en page à la vue page **si elle est active**, en conservant ses autres
|
|
32
|
+
* options (pagination, affichage des en-têtes). Rien si la vue page est inactive ou si
|
|
33
|
+
* elle montre déjà cette mise en page (même référence : c'est le cas juste après que le
|
|
34
|
+
* dialogue « Mise en page » l'a posée). Rend vrai si la vue a été remontée.
|
|
35
|
+
*/
|
|
36
|
+
declare function applyLayoutToPageView(editor: AsterEditor$1, layout: DocumentLayout$1): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Logique Word du composant, hors de la classe pour la garder lisible : chaque fonction
|
|
39
|
+
* charge `@aster-editor/word` par `import()` au moment de l'appel, le paquet reste donc
|
|
40
|
+
* optionnel et hors du bundle initial (mammoth, la lib `docx` et le comparateur ne sont
|
|
41
|
+
* téléchargés qu'au premier usage).
|
|
42
|
+
*/
|
|
43
|
+
/** Chemin de lecture d'un `.docx` : `styled` (lecteur OOXML d'Aster) ou `semantic` (mammoth). */
|
|
44
|
+
type DocxImportFidelity = NonNullable<DocxImportOptions['fidelity']>;
|
|
45
|
+
/** Objet Word non représentable signalé par l'import (`DocxImportResult.lost`). */
|
|
46
|
+
type DocxLostObject = DocxImportResult$1['lost'][number];
|
|
47
|
+
/** Ce que le composant sait au moment d'importer : hook d'upload et chemin de lecture. */
|
|
48
|
+
interface WordImportDefaults {
|
|
49
|
+
readonly upload: UploadHandler$1 | undefined;
|
|
50
|
+
readonly fidelity: DocxImportFidelity;
|
|
51
|
+
}
|
|
52
|
+
/** Import d'un fichier avec le hook d'upload de l'éditeur et le chemin de lecture demandé. */
|
|
53
|
+
declare function importWord(file: File, defaults: WordImportDefaults): Promise<DocxImportResult$1>;
|
|
54
|
+
/** Ce que le composant conserve d'un import et qui complète les options d'export. */
|
|
55
|
+
interface WordExportDefaults {
|
|
56
|
+
/** Gabarit importé (`DocxImportResult.template`), `null` sans import. */
|
|
57
|
+
readonly template: ArrayBuffer | null;
|
|
58
|
+
/** Mise en page effective (import, input `layout` ou dialogue « Mise en page »). */
|
|
59
|
+
readonly layout: DocumentLayout$1 | null;
|
|
60
|
+
/** Utiliser le gabarit importé quand `options.template` est absent. */
|
|
61
|
+
readonly inTemplate: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Complète les options d'export avec ce que le composant sait : les valeurs du magasin de
|
|
65
|
+
* variables quand `options.variables.values` manque, le gabarit importé quand
|
|
66
|
+
* `options.template` manque et que `inTemplate` l'autorise, la mise en page effective quand
|
|
67
|
+
* `options.layout` manque. Avec un gabarit, `layout` ne sert qu'à régénérer un en-tête ou
|
|
68
|
+
* un pied modifié (`layout.headers` / `footers` différents de ceux du gabarit) ; le reste
|
|
69
|
+
* (format, marges, styles) vient du gabarit. Sans gabarit, `layout` pilote l'export neuf.
|
|
70
|
+
*/
|
|
71
|
+
declare function resolveExportOptions(editor: AsterEditor$1, options: DocxExportOptions$1, defaults: WordExportDefaults): DocxExportOptions$1;
|
|
72
|
+
/** Export du document courant avec des options déjà résolues. */
|
|
73
|
+
declare function exportWord(editor: AsterEditor$1, options: DocxExportOptions$1): Promise<Blob>;
|
|
74
|
+
/** Rapport de fidélité entre le gabarit importé et un export produit dans ce gabarit. */
|
|
75
|
+
declare function compareWithTemplate(template: ArrayBuffer, exported: Blob): Promise<FidelityReport$1>;
|
|
76
|
+
/** Transformateur de collage Word, chargé à la demande. */
|
|
77
|
+
declare function loadWordPasteTransformer(): Promise<PasteTransformer$1>;
|
|
78
|
+
/**
|
|
79
|
+
* Après un import : déclare les variables trouvées dans le `.docx` (libellé et valeur des
|
|
80
|
+
* contrôles de contenu compris), puis réapplique les valeurs connues de l'application pour
|
|
81
|
+
* les noms que Word a lui-même valorisés, l'application restant la source de vérité.
|
|
82
|
+
*/
|
|
83
|
+
declare function declareImportedVariables(editor: AsterEditor$1, found: readonly AsterVariable$1[], known: () => VariableValues$1 | undefined): void;
|
|
7
84
|
/**
|
|
8
85
|
* Composant Angular standalone autour de `createAster` (`@aster-editor/ui`).
|
|
9
86
|
*
|
|
@@ -30,6 +107,13 @@ import { DocxExportOptions, DocxExportOptions as DocxExportOptions$1, DocxExport
|
|
|
30
107
|
* `variableSuggestion` de `createAster`. `variableNames` expose les noms présents dans le
|
|
31
108
|
* document, `variablesChanged` relaie `variablesChange`, `exportDocx` produit le `.docx` avec
|
|
32
109
|
* les valeurs du magasin.
|
|
110
|
+
*
|
|
111
|
+
* Fidélité Word (session 3) : `importDocx` lit le `.docx` en mode stylé et conserve le
|
|
112
|
+
* gabarit (`importedTemplate`), la mise en page (`layout`) et les objets non représentables
|
|
113
|
+
* (`lostObjects`) ; `exportDocx` réutilise le gabarit par défaut (`exportInTemplate`) et
|
|
114
|
+
* transmet la mise en page ; `pageView` active la vue page, `layout` (input) impose une mise
|
|
115
|
+
* en page, `layoutChanged` signale celle que montre la vue page (dialogue « Mise en page »
|
|
116
|
+
* compris) ; `compareWithTemplate` mesure la fidélité de l'export dans le gabarit.
|
|
33
117
|
*/
|
|
34
118
|
export declare class AsterEditorComponent implements ControlValueAccessor {
|
|
35
119
|
/** Preset, groupes déclaratifs, ou `false` pour aucune toolbar. Défaut effectif : `standard`. */
|
|
@@ -72,6 +156,32 @@ export declare class AsterEditorComponent implements ControlValueAccessor {
|
|
|
72
156
|
readonly allowUnknownVariables: import("@angular/core").InputSignal<boolean | undefined>;
|
|
73
157
|
/** Menu de suggestion déclenché par `{{` dans le contenu. Défaut effectif : `true`. Lu au montage. */
|
|
74
158
|
readonly variableSuggestion: import("@angular/core").InputSignal<boolean | undefined>;
|
|
159
|
+
/**
|
|
160
|
+
* Chemin de lecture d'`importDocx` : `styled` (lecteur OOXML d'Aster, HTML stylé, mise en
|
|
161
|
+
* page et gabarit renseignés) ou `semantic` (mammoth, HTML sémantique, mise en page vide).
|
|
162
|
+
* Défaut effectif : `styled`. Lu à chaque import.
|
|
163
|
+
*/
|
|
164
|
+
readonly importFidelity: import("@angular/core").InputSignal<DocxImportFidelity | undefined>;
|
|
165
|
+
/**
|
|
166
|
+
* `exportDocx` sans `options.template` réutilise le gabarit du dernier import quand il
|
|
167
|
+
* existe : tout ce qui n'est pas le corps ressort identique. `false` pour l'export neuf
|
|
168
|
+
* (un `options.template` explicite reste honoré). Défaut : `true`. Lu à chaque export.
|
|
169
|
+
*/
|
|
170
|
+
readonly exportInTemplate: import("@angular/core").InputSignal<boolean>;
|
|
171
|
+
/**
|
|
172
|
+
* Vue page (`AsterUiOptions.pageView`) : `true` pour les défauts d'Aster (A4 portrait), un
|
|
173
|
+
* objet pour la mise en page, la pagination et l'affichage des en-têtes. La mise en page
|
|
174
|
+
* effective du composant (`layout()`) prime sur `pageView.layout`. À chaud : la vue est
|
|
175
|
+
* remontée. Le dialogue « Mise en page » de la toolbar peut aussi l'activer ou la modifier.
|
|
176
|
+
*/
|
|
177
|
+
readonly pageView: import("@angular/core").InputSignal<PageViewInput>;
|
|
178
|
+
/**
|
|
179
|
+
* Mise en page imposée (input `layout`) : prime sur celle de l'import et sur celle posée
|
|
180
|
+
* par le dialogue, se répercute sur la vue page active et sur `exportDocx`. Laisse
|
|
181
|
+
* `undefined` pour suivre l'import ; avec une liaison, tiens-la à jour depuis
|
|
182
|
+
* `layoutChanged` pour que le dialogue « Mise en page » reste pris en compte.
|
|
183
|
+
*/
|
|
184
|
+
readonly layoutOverride: import("@angular/core").InputSignal<DocumentLayout$1 | undefined>;
|
|
75
185
|
/** L'éditeur est monté et prêt. Réémis après un remontage (changement de `toolbar`). */
|
|
76
186
|
readonly ready: import("@angular/core").OutputEmitterRef<AsterEditor$1>;
|
|
77
187
|
/** Le document a changé : HTML de `getHTML()`. Pas émis sur `writeValue`. */
|
|
@@ -86,9 +196,27 @@ export declare class AsterEditorComponent implements ControlValueAccessor {
|
|
|
86
196
|
readonly blurred: import("@angular/core").OutputEmitterRef<void>;
|
|
87
197
|
/** Les valeurs ou déclarations de variables ont changé (`setValues`, `define`, `resolve`, import). */
|
|
88
198
|
readonly variablesChanged: import("@angular/core").OutputEmitterRef<Readonly<Record<string, string>>>;
|
|
199
|
+
/**
|
|
200
|
+
* La mise en page effective a changé : import, input `layout`, ou dialogue « Mise en page »
|
|
201
|
+
* de la toolbar (la vue page est alors relue). Jamais émis pour `null` (vue page retirée).
|
|
202
|
+
*/
|
|
203
|
+
readonly layoutChanged: import("@angular/core").OutputEmitterRef<DocumentLayout$1>;
|
|
89
204
|
private readonly editorState;
|
|
90
205
|
/** Éditeur courant, `null` avant le montage et après la destruction. */
|
|
91
206
|
readonly editor: Signal<AsterEditor$1 | null>;
|
|
207
|
+
private readonly importedTemplateState;
|
|
208
|
+
/** Le `.docx` du dernier import, intact, réutilisé comme gabarit à l'export. `null` sans import. */
|
|
209
|
+
readonly importedTemplate: Signal<ArrayBuffer | null>;
|
|
210
|
+
private readonly lostObjectsState;
|
|
211
|
+
/** Objets Word non représentables rencontrés par le dernier import (vide en mode `semantic`). */
|
|
212
|
+
readonly lostObjects: Signal<readonly DocxLostObject[]>;
|
|
213
|
+
/** Mise en page lue par l'import ou posée par le dialogue « Mise en page ». */
|
|
214
|
+
private readonly layoutState;
|
|
215
|
+
/**
|
|
216
|
+
* Mise en page effective : l'input `layout` s'il est fourni, sinon celle du dernier import
|
|
217
|
+
* ou du dialogue « Mise en page ». `null` avant tout import sans vue page.
|
|
218
|
+
*/
|
|
219
|
+
readonly layout: Signal<DocumentLayout$1 | null>;
|
|
92
220
|
private readonly variableNamesState;
|
|
93
221
|
/**
|
|
94
222
|
* Noms des variables réellement présentes dans le document (`editor.variables.getNames()`),
|
|
@@ -109,8 +237,12 @@ export declare class AsterEditorComponent implements ControlValueAccessor {
|
|
|
109
237
|
/** `config.variables` < inputs dédiés, clé par clé ; un input `undefined` ne masque rien. */
|
|
110
238
|
private readonly resolvedVariableSource;
|
|
111
239
|
private readonly resolvedVariableSuggestion;
|
|
240
|
+
private readonly resolvedPageView;
|
|
112
241
|
private instance;
|
|
113
242
|
private mountedToolbar;
|
|
243
|
+
private mountedPageView;
|
|
244
|
+
/** Dernière mise en page émise par `layoutChanged`, pour ne pas la réémettre. */
|
|
245
|
+
private emittedLayout;
|
|
114
246
|
private subscriptions;
|
|
115
247
|
/** Valeur reçue par `writeValue` avant le montage, appliquée comme contenu initial. */
|
|
116
248
|
private pendingValue;
|
|
@@ -126,12 +258,15 @@ export declare class AsterEditorComponent implements ControlValueAccessor {
|
|
|
126
258
|
private readonly lazyWordTransformer;
|
|
127
259
|
constructor();
|
|
128
260
|
/**
|
|
129
|
-
* Importe un `.docx` via `@aster-editor/word` (chargé à la demande,
|
|
261
|
+
* Importe un `.docx` via `@aster-editor/word` (chargé à la demande, le lecteur reste dans
|
|
130
262
|
* un chunk séparé), remplace le contenu de l'éditeur en émettant `change` pour que le
|
|
131
263
|
* formulaire voie la nouvelle valeur, et rend le résultat avec ses avertissements.
|
|
132
|
-
* Le hook d'upload de l'éditeur est réutilisé pour les images embarquées.
|
|
133
|
-
*
|
|
134
|
-
*
|
|
264
|
+
* Le hook d'upload de l'éditeur est réutilisé pour les images embarquées. En mode `styled`
|
|
265
|
+
* (défaut, voir `importFidelity`), le gabarit, la mise en page et les objets non
|
|
266
|
+
* représentables sont conservés dans `importedTemplate`, `layout` et `lostObjects`, et la
|
|
267
|
+
* mise en page est appliquée à la vue page si elle est active. Si le composant est détruit
|
|
268
|
+
* ou remonté pendant l'import, le résultat est rendu mais rien n'est écrit ni conservé :
|
|
269
|
+
* l'instance vivante est relue après chaque `await`.
|
|
135
270
|
*/
|
|
136
271
|
importDocx(file: File): Promise<DocxImportResult$1>;
|
|
137
272
|
/**
|
|
@@ -139,9 +274,17 @@ export declare class AsterEditorComponent implements ControlValueAccessor {
|
|
|
139
274
|
* demande, la lib `docx` reste dans son chunk). Les valeurs du magasin de variables sont
|
|
140
275
|
* transmises quand `options.variables.values` n'est pas fourni : `mode: 'resolve'` substitue
|
|
141
276
|
* donc ce que l'utilisateur a saisi, `'fields'` produit des champs de fusion Word avec ces
|
|
142
|
-
* valeurs comme résultat.
|
|
277
|
+
* valeurs comme résultat. Sans `options.template`, le gabarit du dernier import est utilisé
|
|
278
|
+
* si `exportInTemplate` est vrai ; sans `options.layout`, la mise en page effective
|
|
279
|
+
* (`layout()`) est transmise. Rend le `Blob`, à passer à `downloadDocx` ou à un upload.
|
|
143
280
|
*/
|
|
144
281
|
exportDocx(options?: DocxExportOptions$1): Promise<Blob>;
|
|
282
|
+
/**
|
|
283
|
+
* Rapport de fidélité du document courant : export dans le gabarit du dernier import
|
|
284
|
+
* (valeurs du magasin de variables, mise en page effective) puis `compareDocx(gabarit,
|
|
285
|
+
* export)`. `null` sans gabarit importé. Le comparateur est chargé à la demande.
|
|
286
|
+
*/
|
|
287
|
+
compareWithTemplate(): Promise<FidelityReport$1 | null>;
|
|
145
288
|
/**
|
|
146
289
|
* HTML du document, `{ variables: 'resolve' }` substitue les valeurs connues. Avant le
|
|
147
290
|
* montage, rend la valeur reçue par `writeValue` (puces conservées) ou une chaîne vide.
|
|
@@ -183,27 +326,19 @@ export declare class AsterEditorComponent implements ControlValueAccessor {
|
|
|
183
326
|
* `variables` réinjecte l'état du magasin précédent par-dessus la configuration.
|
|
184
327
|
*/
|
|
185
328
|
private resolvedOptions;
|
|
329
|
+
/**
|
|
330
|
+
* Relit la mise en page de la vue page active et l'adopte. Si l'input `layout` la masque,
|
|
331
|
+
* `layoutChanged` est émis directement pour que l'application puisse l'adopter ; sinon
|
|
332
|
+
* l'effet sur `layout()` s'en charge.
|
|
333
|
+
*/
|
|
334
|
+
private readLayoutFromPageView;
|
|
335
|
+
/** Émet `layoutChanged` une seule fois par mise en page (même référence = déjà émise). */
|
|
336
|
+
private notifyLayout;
|
|
186
337
|
/** Éditeur monté, sinon erreur explicite nommant la méthode appelée trop tôt. */
|
|
187
338
|
private requireEditor;
|
|
188
339
|
/** Relit `getNames()` ; le signal n'est notifié que si la liste change réellement. */
|
|
189
340
|
private refreshVariableNames;
|
|
190
|
-
/**
|
|
191
|
-
* Après un import : déclare les variables trouvées dans le `.docx` (libellé et valeur des
|
|
192
|
-
* contrôles de contenu compris), puis réapplique l'input `variableValues` pour les noms que
|
|
193
|
-
* Word a lui-même valorisés, l'application restant la source de vérité des valeurs.
|
|
194
|
-
*/
|
|
195
|
-
private declareImportedVariables;
|
|
196
341
|
private loadWordPaste;
|
|
197
|
-
/** Nom accessible du contenteditable : `aria-label` et/ou `aria-labelledby`, retirés si vides. */
|
|
198
|
-
private applyAriaAttributes;
|
|
199
|
-
/**
|
|
200
|
-
* Change le placeholder à chaud. L'extension `Placeholder` lit ses options au moment où
|
|
201
|
-
* son plugin ProseMirror est créé (et `extension.options` est une copie à chaque accès),
|
|
202
|
-
* donc on refabrique le plugin via `addProseMirrorPlugins` avec un `placeholder` surchargé
|
|
203
|
-
* et on le substitue à l'ancien par `unregisterPlugin`/`registerPlugin`, API publiques de
|
|
204
|
-
* Tiptap. Le repli sur la chaîne i18n `editor.placeholder` est conservé.
|
|
205
|
-
*/
|
|
206
|
-
private applyPlaceholder;
|
|
207
342
|
static ɵfac: i0.ɵɵFactoryDeclaration<AsterEditorComponent, never>;
|
|
208
343
|
static ɵcmp: i0.ɵɵComponentDeclaration<AsterEditorComponent, "aster-editor", never, {
|
|
209
344
|
"toolbar": {
|
|
@@ -276,12 +411,33 @@ export declare class AsterEditorComponent implements ControlValueAccessor {
|
|
|
276
411
|
"required": false;
|
|
277
412
|
"isSignal": true;
|
|
278
413
|
};
|
|
414
|
+
"importFidelity": {
|
|
415
|
+
"alias": "importFidelity";
|
|
416
|
+
"required": false;
|
|
417
|
+
"isSignal": true;
|
|
418
|
+
};
|
|
419
|
+
"exportInTemplate": {
|
|
420
|
+
"alias": "exportInTemplate";
|
|
421
|
+
"required": false;
|
|
422
|
+
"isSignal": true;
|
|
423
|
+
};
|
|
424
|
+
"pageView": {
|
|
425
|
+
"alias": "pageView";
|
|
426
|
+
"required": false;
|
|
427
|
+
"isSignal": true;
|
|
428
|
+
};
|
|
429
|
+
"layoutOverride": {
|
|
430
|
+
"alias": "layout";
|
|
431
|
+
"required": false;
|
|
432
|
+
"isSignal": true;
|
|
433
|
+
};
|
|
279
434
|
}, {
|
|
280
435
|
"ready": "ready";
|
|
281
436
|
"changed": "changed";
|
|
282
437
|
"focused": "focused";
|
|
283
438
|
"blurred": "blurred";
|
|
284
439
|
"variablesChanged": "variablesChanged";
|
|
440
|
+
"layoutChanged": "layoutChanged";
|
|
285
441
|
}, never, never, true, never>;
|
|
286
442
|
}
|
|
287
443
|
/**
|
|
@@ -307,4 +463,4 @@ export declare function provideAster(config: Partial<AsterUiOptions$1>): Environ
|
|
|
307
463
|
* Sans handler, les images sont insérées en data URL base64.
|
|
308
464
|
*/
|
|
309
465
|
export declare const ASTER_UPLOAD_HANDLER: InjectionToken<UploadHandler$1>;
|
|
310
|
-
export type { AsterCounts, AsterDocument, AsterEditor, AsterLocale, AsterTheme, AsterUiOptions, AsterVariable, DocxExportOptions, DocxExportVariableOptions, DocxExportWarning, DocxImportResult, DocxImportWarning, DocxVariableMode, GetHtmlOptions, LinkPolicy, PasteTransformer, ToolbarConfig, ToolbarPreset, UploadHandler, VariableRenderMode, VariableResolver, VariableSource, VariableValues };
|
|
466
|
+
export type { AsterCounts, AsterDocument, AsterEditor, AsterLocale, AsterTheme, AsterUiOptions, AsterVariable, DocumentLayout, DocxExportOptions, DocxExportVariableOptions, DocxExportWarning, DocxImportFidelity, DocxImportResult, DocxImportWarning, DocxLostObject, DocxVariableMode, FidelityExample, FidelityProperty, FidelityPropertyGroup, FidelityReport, GetHtmlOptions, HeaderFooterContent, HeaderFooterSet, LinkPolicy, PageMargins, PageSetup, PageViewInput, PageViewOptions, PasteTransformer, ToolbarConfig, ToolbarPreset, UploadHandler, VariableRenderMode, VariableResolver, VariableSource, VariableValues };
|