react-bootstrap-front 3.10.2 → 3.11.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.
Files changed (37) hide show
  1. package/CHANGELOG +9 -0
  2. package/dist/react-bootstrap-front.js +14 -14
  3. package/doc/editor/feuille-de-route.md +36 -0
  4. package/package.json +42 -8
  5. package/src/features/advanced/DropdownWrapper.js +12 -2
  6. package/src/features/basic/Dropdown.js +112 -31
  7. package/src/features/basic/computePlacement.js +66 -0
  8. package/src/features/editor/HtmlEditor.js +681 -0
  9. package/src/features/editor/HtmlEditor.scss +189 -0
  10. package/src/features/editor/ImageDialog.js +195 -0
  11. package/src/features/editor/LinkDialog.js +148 -0
  12. package/src/features/editor/MergeMenu.js +120 -0
  13. package/src/features/editor/SourceView.js +61 -0
  14. package/src/features/editor/Toolbar.js +355 -0
  15. package/src/features/editor/Toolbar.scss +130 -0
  16. package/src/features/editor/ToolbarButton.js +59 -0
  17. package/src/features/editor/ToolbarDropdown.js +46 -0
  18. package/src/features/editor/ToolbarGroup.js +27 -0
  19. package/src/features/editor/buildExtensions.js +77 -0
  20. package/src/features/editor/defaultIcons.js +45 -0
  21. package/src/features/editor/extensions/CleanPaste.js +44 -0
  22. package/src/features/editor/extensions/FontSize.js +60 -0
  23. package/src/features/editor/extensions/LegacyImageAttrs.js +43 -0
  24. package/src/features/editor/extensions/MergeVariable.js +104 -0
  25. package/src/features/editor/extensions/index.js +4 -0
  26. package/src/features/editor/html/cleanHtml.constants.js +279 -0
  27. package/src/features/editor/html/cleanHtml.js +691 -0
  28. package/src/features/editor/html/index.js +4 -0
  29. package/src/features/editor/html/normalizeLegacyHtml.js +196 -0
  30. package/src/features/editor/html/serialize.js +130 -0
  31. package/src/features/editor/html/sourceHelpers.js +86 -0
  32. package/src/features/editor/index.js +20 -0
  33. package/src/features/editor/style.scss +2 -0
  34. package/src/features/editor/toolbarPresets.js +74 -0
  35. package/src/features/page/DefaultHeader.jsx +1 -1
  36. package/src/index.js +8 -0
  37. package/src/styles/index.scss +1 -0
@@ -0,0 +1,196 @@
1
+ import { cleanHtml } from './cleanHtml';
2
+
3
+ /**
4
+ * Compatibilité avec les contenus produits par l'ancien éditeur draft-js.
5
+ *
6
+ * L'ancien `toHtml` (features/draft/InputHtml.js) est la spécification exacte de
7
+ * ce qui se trouve en base. Ce module rattrape les tournures que le schéma de
8
+ * l'éditeur ne saurait pas relire, AVANT que le contenu ne lui soit confié :
9
+ *
10
+ * - `<cite>` : draft-js écrivait la marque CODE ainsi. Inconnue du schéma,
11
+ * elle produirait du texte nu et la mise en forme serait perdue.
12
+ * - `<span style="text-align:…">` : l'alignement était posé sur de l'inline,
13
+ * où il n'a aucun effet. On le remonte sur le bloc parent.
14
+ * - `<br>` isolé entre deux blocs : c'était un paragraphe vide. Laissé tel
15
+ * quel, il ferait fusionner les deux paragraphes voisins.
16
+ * - `<span data-mailmerge="…">` : enveloppe d'une variable de fusion. Le texte
17
+ * à l'intérieur est la variable elle-même, on garde le texte.
18
+ *
19
+ * Rien ici ne supprime de mise en forme : la fonction est appelée à chaque
20
+ * ouverture d'un contenu, elle doit être neutre pour tout ce qu'elle ne
21
+ * reconnaît pas.
22
+ */
23
+
24
+ const BLOCK_ANCESTORS = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'blockquote', 'td', 'th', 'div'];
25
+
26
+ const DEFAULTS = {
27
+ /** Ce que devient `<cite>` : 'em' (défaut), 'code' ou 'strip'. */
28
+ citeAs: 'em',
29
+ /** Passe de nettoyage minimal (profil LOAD) avant les rattrapages. */
30
+ clean: true,
31
+ /** Remonte `text-align` d'un span vers son bloc. */
32
+ liftTextAlign: true,
33
+ /** Convertit les `<br>` inter-blocs en paragraphes vides. */
34
+ brToParagraph: true,
35
+ /** Déballe les enveloppes de variables de fusion. */
36
+ unwrapMailmerge: true,
37
+ };
38
+
39
+ function parseRoot(html) {
40
+ if (typeof DOMParser === 'undefined') {
41
+ return null;
42
+ }
43
+ const doc = new DOMParser().parseFromString('<div id="rbf-legacy-root">' + html + '</div>', 'text/html');
44
+ return doc.getElementById('rbf-legacy-root');
45
+ }
46
+
47
+ function unwrap(el) {
48
+ const parent = el.parentNode;
49
+ if (!parent) {
50
+ return;
51
+ }
52
+ while (el.firstChild) {
53
+ parent.insertBefore(el.firstChild, el);
54
+ }
55
+ parent.removeChild(el);
56
+ }
57
+
58
+ function rename(el, tagName) {
59
+ const next = el.ownerDocument.createElement(tagName);
60
+ const attrs = el.attributes;
61
+ for (let i = 0; i < attrs.length; i++) {
62
+ try {
63
+ next.setAttribute(attrs[i].name, attrs[i].value);
64
+ } catch (e) {
65
+ /* nom d'attribut invalide */
66
+ }
67
+ }
68
+ while (el.firstChild) {
69
+ next.appendChild(el.firstChild);
70
+ }
71
+ if (el.parentNode) {
72
+ el.parentNode.replaceChild(next, el);
73
+ }
74
+ return next;
75
+ }
76
+
77
+ /**
78
+ * `<cite>` était la marque CODE de draft-js.
79
+ */
80
+ function handleCite(root, citeAs) {
81
+ const cites = Array.prototype.slice.call(root.getElementsByTagName('cite'));
82
+ for (let i = cites.length - 1; i >= 0; i--) {
83
+ if (citeAs === 'strip') {
84
+ unwrap(cites[i]);
85
+ } else {
86
+ rename(cites[i], citeAs === 'code' ? 'code' : 'em');
87
+ }
88
+ }
89
+ }
90
+
91
+ /**
92
+ * L'alignement posé sur un span inline n'a aucun effet : on le remonte sur le
93
+ * premier bloc ancêtre, qui est le seul endroit où l'éditeur saura le relire.
94
+ */
95
+ function liftTextAlign(root) {
96
+ const spans = Array.prototype.slice.call(root.getElementsByTagName('span'));
97
+ for (let i = 0; i < spans.length; i++) {
98
+ const span = spans[i];
99
+ const style = span.getAttribute('style');
100
+ if (!style || style.indexOf('text-align') === -1) {
101
+ continue;
102
+ }
103
+ const match = /text-align\s*:\s*([a-z]+)/i.exec(style);
104
+ if (!match) {
105
+ continue;
106
+ }
107
+ const align = match[1].toLowerCase();
108
+ span.setAttribute('style', style.replace(/text-align\s*:\s*[a-z]+\s*;?/i, '').trim());
109
+ if ((span.getAttribute('style') || '').replace(/[\s;]/g, '') === '') {
110
+ span.removeAttribute('style');
111
+ }
112
+ const block = closestBlock(span, root);
113
+ if (block && !/text-align/i.test(block.getAttribute('style') || '')) {
114
+ const existing = block.getAttribute('style');
115
+ block.setAttribute(
116
+ 'style',
117
+ existing ? existing.replace(/;?\s*$/, '; ') + 'text-align: ' + align : 'text-align: ' + align,
118
+ );
119
+ }
120
+ /* Le span n'était là que pour porter l'alignement : il n'a plus d'objet. */
121
+ if (span.attributes.length === 0) {
122
+ unwrap(span);
123
+ }
124
+ }
125
+ }
126
+
127
+ function closestBlock(el, root) {
128
+ let node = el.parentNode;
129
+ while (node && node !== root && node.nodeType === 1) {
130
+ if (BLOCK_ANCESTORS.indexOf(node.tagName.toLowerCase()) !== -1) {
131
+ return node;
132
+ }
133
+ node = node.parentNode;
134
+ }
135
+ return null;
136
+ }
137
+
138
+ /**
139
+ * draft-convert écrivait un `<br>` nu pour un paragraphe vide (blockToHTML
140
+ * retournait `<br />` sur un bloc `unstyled` sans texte). Au niveau racine, ce
141
+ * `<br>` doit redevenir un paragraphe, sinon les blocs voisins fusionnent.
142
+ */
143
+ function brToParagraph(root) {
144
+ const brs = Array.prototype.slice.call(root.childNodes);
145
+ for (let i = 0; i < brs.length; i++) {
146
+ const node = brs[i];
147
+ if (node.nodeType !== 1 || node.tagName.toLowerCase() !== 'br') {
148
+ continue;
149
+ }
150
+ const p = root.ownerDocument.createElement('p');
151
+ root.replaceChild(p, node);
152
+ }
153
+ }
154
+
155
+ /**
156
+ * `<span data-mailmerge="…">[[:code:]]</span>` : le texte intérieur est la
157
+ * variable, on le conserve tel quel et on jette l'enveloppe.
158
+ */
159
+ function unwrapMailmerge(root) {
160
+ const spans = Array.prototype.slice.call(root.querySelectorAll('span[data-mailmerge]'));
161
+ for (let i = spans.length - 1; i >= 0; i--) {
162
+ unwrap(spans[i]);
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Prépare un contenu HTML existant pour l'éditeur.
168
+ *
169
+ * @param {string} html
170
+ * @param {object} options cf. DEFAULTS
171
+ * @returns {string}
172
+ */
173
+ export function normalizeLegacyHtml(html, options) {
174
+ if (html === null || html === undefined || html === '') {
175
+ return '';
176
+ }
177
+ const opts = { ...DEFAULTS, ...(options || {}) };
178
+ const base = opts.clean ? cleanHtml(html, 'load') : String(html);
179
+ const root = parseRoot(base);
180
+ if (!root) {
181
+ return base;
182
+ }
183
+ handleCite(root, opts.citeAs);
184
+ if (opts.unwrapMailmerge) {
185
+ unwrapMailmerge(root);
186
+ }
187
+ if (opts.liftTextAlign) {
188
+ liftTextAlign(root);
189
+ }
190
+ if (opts.brToParagraph) {
191
+ brToParagraph(root);
192
+ }
193
+ return root.innerHTML;
194
+ }
195
+
196
+ export default normalizeLegacyHtml;
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Sérialisation du contenu de l'éditeur vers la chaîne stockée en base.
3
+ *
4
+ * Deux règles portent tout le fichier :
5
+ *
6
+ * - un document vide rend la chaîne vide, pas `<p></p>`. Sans cela un champ
7
+ * `required` serait toujours considéré comme rempli et la validation des
8
+ * formulaires ne se déclencherait jamais ;
9
+ * - un paragraphe vide devient `<p>&nbsp;</p>`, parce que les clients de
10
+ * messagerie effondrent les paragraphes réellement vides et que la ligne
11
+ * blanche voulue par le rédacteur disparaîtrait à l'envoi.
12
+ */
13
+
14
+ const DEFAULTS = {
15
+ /** 'nbsp' (défaut), 'br' ou 'keep' */
16
+ emptyParagraph: 'nbsp',
17
+ /** Un document sans texte ni média rend '' */
18
+ emptyDocumentAsEmptyString: true,
19
+ /** Ferme les balises orphelines en XML (`<img />`) si le moteur PHP l'exige */
20
+ selfClosing: false,
21
+ /** Ramène les couleurs en notation hexadécimale */
22
+ hexColors: true,
23
+ };
24
+
25
+ /**
26
+ * Le navigateur normalise toute couleur lue dans un style en `rgb(r, g, b)`.
27
+ * Sans cette conversion, ouvrir puis enregistrer un contenu réécrirait chaque
28
+ * couleur de la base en `rgb()` — un changement inutile, et moins bien
29
+ * supporté que l'hexadécimal par les clients de messagerie anciens.
30
+ *
31
+ * Les couleurs à transparence partielle sont laissées telles quelles :
32
+ * l'hexadécimal sur huit chiffres n'est pas lisible par ces mêmes clients.
33
+ */
34
+ function toHexColors(html) {
35
+ return html.replace(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(?:1|1\.0+)\s*)?\)/gi, function(
36
+ match,
37
+ r,
38
+ g,
39
+ b,
40
+ ) {
41
+ const channel = value => {
42
+ const hex = Number(value).toString(16);
43
+ return hex.length === 1 ? '0' + hex : hex;
44
+ };
45
+ return '#' + channel(r) + channel(g) + channel(b);
46
+ });
47
+ }
48
+
49
+ /** Balises qui portent du sens sans contenir de texte. */
50
+ const MEANINGFUL_EMPTY_TAGS = /<\s*(img|hr|br|iframe|video|audio)\b/i;
51
+
52
+ /**
53
+ * Un contenu est vide s'il ne porte ni texte visible ni média.
54
+ * Volontairement indépendante de l'éditeur : testable sans navigateur.
55
+ */
56
+ export function isEmptyHtml(html) {
57
+ if (html === null || html === undefined) {
58
+ return true;
59
+ }
60
+ const str = String(html);
61
+ if (str.trim() === '') {
62
+ return true;
63
+ }
64
+ if (MEANINGFUL_EMPTY_TAGS.test(str)) {
65
+ return false;
66
+ }
67
+ const text = str
68
+ .replace(/<[^>]*>/g, '')
69
+ .replace(/&nbsp;/gi, ' ')
70
+ .replace(/&#160;/g, ' ')
71
+ .replace(/\u00a0/g, ' ');
72
+ return text.trim() === '';
73
+ }
74
+
75
+ function applyEmptyParagraph(html, mode) {
76
+ if (mode === 'keep') {
77
+ return html;
78
+ }
79
+ const filler = mode === 'br' ? '<br>' : '&nbsp;';
80
+ /* <p></p> et <p style="…"></p>, y compris avec des blancs à l'intérieur */
81
+ return html.replace(/<p([^>]*)>(\s|&nbsp;|&#160;)*<\/p>/gi, '<p$1>' + filler + '</p>');
82
+ }
83
+
84
+ function applySelfClosing(html) {
85
+ return html.replace(/<(img|br|hr)\b([^>]*?)\s*\/?>/gi, function(match, tag, attrs) {
86
+ return '<' + tag + attrs + ' />';
87
+ });
88
+ }
89
+
90
+ /**
91
+ * Met en forme une chaîne HTML pour le stockage.
92
+ *
93
+ * @param {string} html
94
+ * @param {object} options cf. DEFAULTS
95
+ * @returns {string}
96
+ */
97
+ export function serializeHtml(html, options) {
98
+ const opts = { ...DEFAULTS, ...(options || {}) };
99
+ if (html === null || html === undefined) {
100
+ return '';
101
+ }
102
+ let out = String(html);
103
+ if (opts.emptyDocumentAsEmptyString && isEmptyHtml(out)) {
104
+ return '';
105
+ }
106
+ out = applyEmptyParagraph(out, opts.emptyParagraph);
107
+ if (opts.hexColors) {
108
+ out = toHexColors(out);
109
+ }
110
+ if (opts.selfClosing) {
111
+ out = applySelfClosing(out);
112
+ }
113
+ return out;
114
+ }
115
+
116
+ /**
117
+ * Sérialise l'état courant d'un éditeur TipTap.
118
+ *
119
+ * @param {object} editor instance @tiptap/core
120
+ * @param {object} options cf. DEFAULTS
121
+ * @returns {string}
122
+ */
123
+ export function serialize(editor, options) {
124
+ if (!editor) {
125
+ return '';
126
+ }
127
+ return serializeHtml(editor.getHTML(), options);
128
+ }
129
+
130
+ export default serialize;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Aides de la vue source.
3
+ *
4
+ * Toutes pures et sans dépendance à l'éditeur : elles servent à répondre à
5
+ * deux questions au retour vers le mode riche, et il vaut mieux pouvoir les
6
+ * tester séparément.
7
+ *
8
+ * 1. le HTML saisi était-il équilibré, ou le navigateur l'a-t-il recousu ?
9
+ * 2. le passage par le schéma de l'éditeur a-t-il perdu du contenu ?
10
+ */
11
+
12
+ const BLOCK_TAGS = 'p|div|h[1-6]|ul|ol|li|blockquote|hr|table|thead|tbody|tr|td|th|pre|br|img';
13
+
14
+ /**
15
+ * Indentation naïve : un bloc par ligne. Uniquement pour la lecture — ce n'est
16
+ * jamais ce qui est réémis vers le formulaire.
17
+ */
18
+ export function formatHtml(html) {
19
+ if (!html) {
20
+ return '';
21
+ }
22
+ const openRe = new RegExp('<(' + BLOCK_TAGS + ')\\b', 'gi');
23
+ const closeRe = new RegExp('</(' + BLOCK_TAGS + ')\\s*>', 'gi');
24
+ return String(html)
25
+ .replace(openRe, match => '\n' + match)
26
+ .replace(closeRe, match => match + '\n')
27
+ .split('\n')
28
+ .map(line => line.trim())
29
+ .filter(line => line !== '')
30
+ .join('\n');
31
+ }
32
+
33
+ /**
34
+ * Suite des balises rencontrées, ouvrantes et fermantes.
35
+ * Comparer la signature d'une saisie et celle de sa relecture par le
36
+ * navigateur dit si le HTML était équilibré.
37
+ */
38
+ export function tagSignature(html) {
39
+ const tags = [];
40
+ const re = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi;
41
+ let match = re.exec(String(html || ''));
42
+ while (match !== null) {
43
+ tags.push((match[0].charAt(1) === '/' ? '/' : '') + match[1].toLowerCase());
44
+ match = re.exec(String(html || ''));
45
+ }
46
+ return tags.join(',');
47
+ }
48
+
49
+ /**
50
+ * Texte visible, blancs normalisés. Une différence signale une perte réelle
51
+ * de contenu, pas une simple différence de balisage.
52
+ */
53
+ export function textSignature(html) {
54
+ return String(html || '')
55
+ .replace(/<[^>]*>/g, ' ')
56
+ .replace(/&nbsp;/gi, ' ')
57
+ .replace(/&#160;/g, ' ')
58
+ .replace(/\u00a0/g, ' ')
59
+ .replace(/&amp;/gi, '&')
60
+ .replace(/&lt;/gi, '<')
61
+ .replace(/&gt;/gi, '>')
62
+ .replace(/\s+/g, ' ')
63
+ .trim();
64
+ }
65
+
66
+ /**
67
+ * Relit un fragment par le navigateur et rend le HTML recousu.
68
+ * `DOMParser` ne lève jamais sur du HTML mal formé, il le répare — c'est
69
+ * précisément ce qui permet de détecter le déséquilibre par comparaison.
70
+ */
71
+ export function reserialize(html) {
72
+ if (typeof DOMParser === 'undefined') {
73
+ return String(html || '');
74
+ }
75
+ const doc = new DOMParser().parseFromString('<div id="rbf-source-root">' + html + '</div>', 'text/html');
76
+ const root = doc.getElementById('rbf-source-root');
77
+ return root ? root.innerHTML : String(html || '');
78
+ }
79
+
80
+ /**
81
+ * Vrai si le HTML saisi est équilibré, c'est-à-dire si le navigateur n'a rien
82
+ * eu à recoudre.
83
+ */
84
+ export function isBalanced(html) {
85
+ return tagSignature(html) === tagSignature(reserialize(html));
86
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Éditeur HTML (TipTap 2).
3
+ *
4
+ * `InputHtml` n'est volontairement PAS ré-exporté ici tant que
5
+ * `features/draft` existe : deux `export *` portant le même nom produisent un
6
+ * « ambiguous star export », résolu silencieusement en `undefined`.
7
+ * L'alias sera ajouté en 4.0.0, à la suppression de draft-js.
8
+ */
9
+ export { default as HtmlEditor } from './HtmlEditor';
10
+ export { default as EditorToolbar } from './Toolbar';
11
+ export { default as EditorToolbarButton } from './ToolbarButton';
12
+ export { default as EditorSourceView } from './SourceView';
13
+ export { default as EditorLinkDialog } from './LinkDialog';
14
+ export { default as EditorImageDialog } from './ImageDialog';
15
+ export { default as EditorMergeMenu, normalizeMergeVars } from './MergeMenu';
16
+ export { defaultIcons as editorDefaultIcons } from './defaultIcons';
17
+ export { buildExtensions } from './buildExtensions';
18
+ export * from './toolbarPresets';
19
+ export * from './html';
20
+ export * from './extensions';
@@ -0,0 +1,2 @@
1
+ @import './HtmlEditor';
2
+ @import './Toolbar';
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Configurations de barre d'outils.
3
+ *
4
+ * Un preset est une liste de groupes, chaque groupe une liste de clés de
5
+ * boutons. L'application peut moduler par formulaire : un champ de traduction
6
+ * ou d'alerte n'a pas besoin de la même barre qu'un gabarit de courriel.
7
+ */
8
+ export const TOOLBAR_PRESETS = {
9
+ full: [
10
+ ['undo', 'redo'],
11
+ ['paragraph', 'heading1', 'heading2', 'heading3'],
12
+ ['bold', 'italic', 'underline', 'strike', 'subscript', 'superscript'],
13
+ ['bulletList', 'orderedList', 'blockquote'],
14
+ ['alignLeft', 'alignCenter', 'alignRight', 'alignJustify'],
15
+ ['color', 'fontSize', 'fontFamily'],
16
+ ['link', 'unlink', 'image'],
17
+ ['mergeVar'],
18
+ ['clean', 'formatClear'],
19
+ ['source'],
20
+ ],
21
+ email: [
22
+ ['undo', 'redo'],
23
+ ['paragraph', 'heading1', 'heading2', 'heading3'],
24
+ ['bold', 'italic', 'underline', 'strike'],
25
+ ['bulletList', 'orderedList'],
26
+ ['alignLeft', 'alignCenter', 'alignRight'],
27
+ ['color', 'fontSize'],
28
+ ['link', 'unlink', 'image'],
29
+ ['mergeVar'],
30
+ ['clean', 'formatClear'],
31
+ ['source'],
32
+ ],
33
+ simple: [['bold', 'italic', 'underline'], ['bulletList', 'orderedList'], ['link'], ['clean']],
34
+ minimal: [['bold', 'italic'], ['clean']],
35
+ };
36
+
37
+ /** Groupes rebasculés dans le menu « … » sur petit écran. */
38
+ export const OPTIONAL_KEYS = ['color', 'fontSize', 'fontFamily', 'alignJustify', 'blockquote', 'horizontalRule'];
39
+
40
+ export const FONT_SIZES = ['8px', '10px', '11px', '12px', '14px', '16px', '18px', '24px', '30px', '36px', '48px'];
41
+
42
+ export const FONT_FAMILIES = ['Arial', 'Georgia', 'Impact', 'Tahoma', 'Times New Roman', 'Verdana'];
43
+
44
+ /**
45
+ * Palette volontairement courte : dans un courriel, une charte tient en une
46
+ * poignée de couleurs, et un nuancier complet invite à la faute de goût.
47
+ */
48
+ export const COLORS = [
49
+ '#000000',
50
+ '#434343',
51
+ '#666666',
52
+ '#999999',
53
+ '#b7b7b7',
54
+ '#ffffff',
55
+ '#980000',
56
+ '#ff0000',
57
+ '#ff9900',
58
+ '#ffff00',
59
+ '#00ff00',
60
+ '#00ffff',
61
+ '#4a86e8',
62
+ '#0000ff',
63
+ '#9900ff',
64
+ '#ff00ff',
65
+ '#83ad10',
66
+ '#235b8b',
67
+ ];
68
+
69
+ export function resolveToolbarConfig(preset, config) {
70
+ if (Array.isArray(config)) {
71
+ return config;
72
+ }
73
+ return TOOLBAR_PRESETS[preset] || TOOLBAR_PRESETS.full;
74
+ }
@@ -96,7 +96,7 @@ export class DefaultHeader extends Component {
96
96
  return (
97
97
  <div
98
98
  className={classnames(
99
- 'default-header bg-secondary pl-2 overflow-none h-100',
99
+ 'default-header pl-2 overflow-none h-100',
100
100
  'bg-' + this.props.headerBackgroundTheme
101
101
  )}
102
102
  >
package/src/index.js CHANGED
@@ -71,9 +71,17 @@ export { default as SvgNext } from "./features/advanced/SvgNext";
71
71
  export { default as SvgToday } from "./features/advanced/SvgToday";
72
72
  export { default as WidthObserver } from "./features/advanced/WidthObserver";
73
73
 
74
+ /**
75
+ *******************************************************************************
76
+ * Editor (TipTap)
77
+ *******************************************************************************
78
+ */
79
+ export * from "./features/editor/index.js";
80
+
74
81
  /**
75
82
  *******************************************************************************
76
83
  * Draft
84
+ * @deprecated remplacé par features/editor, supprimé en 4.0.0
77
85
  *******************************************************************************
78
86
  */
79
87
  export * from "./features/draft/index.js";
@@ -4,6 +4,7 @@
4
4
  @import '../features/advanced/style';
5
5
  @import '../features/basic/style';
6
6
  @import '../features/draft/style';
7
+ @import '../features/editor/style';
7
8
  @import '../features/filter/style';
8
9
  @import '../features/grid/style';
9
10
  @import '../features/inline-list/style';