@elasticias/ui 1.0.52 → 1.0.54
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/fesm2022/elasticias-ui-rich-text.mjs +336 -0
- package/fesm2022/elasticias-ui-rich-text.mjs.map +1 -0
- package/fesm2022/elasticias-ui.mjs +690 -112
- package/fesm2022/elasticias-ui.mjs.map +1 -1
- package/package.json +12 -2
- package/styles/_patterns.scss +51 -2
- package/types/elasticias-ui-rich-text.d.ts +151 -0
- package/types/elasticias-ui.d.ts +380 -61
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elasticias/ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.54",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/common": "^21.0.0",
|
|
6
6
|
"@angular/core": "^21.0.0",
|
|
@@ -15,7 +15,13 @@
|
|
|
15
15
|
"chart.js": "^4.5.0",
|
|
16
16
|
"primeng": "^21.0.0",
|
|
17
17
|
"rxjs": "~7.8.0",
|
|
18
|
-
"@angular/platform-browser": "21.2"
|
|
18
|
+
"@angular/platform-browser": "21.2",
|
|
19
|
+
"quill": "^2.0.3"
|
|
20
|
+
},
|
|
21
|
+
"peerDependenciesMeta": {
|
|
22
|
+
"quill": {
|
|
23
|
+
"optional": true
|
|
24
|
+
}
|
|
19
25
|
},
|
|
20
26
|
"sideEffects": false,
|
|
21
27
|
"repository": {
|
|
@@ -36,6 +42,10 @@
|
|
|
36
42
|
".": {
|
|
37
43
|
"types": "./types/elasticias-ui.d.ts",
|
|
38
44
|
"default": "./fesm2022/elasticias-ui.mjs"
|
|
45
|
+
},
|
|
46
|
+
"./rich-text": {
|
|
47
|
+
"types": "./types/elasticias-ui-rich-text.d.ts",
|
|
48
|
+
"default": "./fesm2022/elasticias-ui-rich-text.mjs"
|
|
39
49
|
}
|
|
40
50
|
},
|
|
41
51
|
"type": "module",
|
package/styles/_patterns.scss
CHANGED
|
@@ -3750,10 +3750,39 @@ input.ef-input[type='number'] {
|
|
|
3750
3750
|
outline: 0;
|
|
3751
3751
|
}
|
|
3752
3752
|
|
|
3753
|
-
|
|
3754
|
-
|
|
3753
|
+
/* Disabled reads like every other disabled comptoir field (`.ef-input[disabled]`
|
|
3754
|
+
above): flat --paper fill, the usual --rule border, --text-mute text, no
|
|
3755
|
+
fading. The root selector doubles `.p-select` / `.p-multiselect` because
|
|
3756
|
+
PrimeNG injects `.p-select.p-disabled { opacity: 1; background: <its grey> }`
|
|
3757
|
+
at runtime, after this sheet, and at a tie that later rule won, so the
|
|
3758
|
+
disabled select came out a heavier grey than its neighbours. */
|
|
3759
|
+
.es.p-select.p-disabled,
|
|
3760
|
+
.es.p-multiselect.p-disabled {
|
|
3761
|
+
opacity: 1;
|
|
3755
3762
|
cursor: not-allowed;
|
|
3756
3763
|
background: var(--paper);
|
|
3764
|
+
border-color: var(--rule);
|
|
3765
|
+
color: var(--text-mute);
|
|
3766
|
+
}
|
|
3767
|
+
|
|
3768
|
+
.es.p-select.p-disabled .p-select-label,
|
|
3769
|
+
.es.p-multiselect.p-disabled .p-multiselect-label {
|
|
3770
|
+
color: var(--text-mute);
|
|
3771
|
+
}
|
|
3772
|
+
|
|
3773
|
+
/* The caret stays as a quiet hint of what the field is, not an affordance. */
|
|
3774
|
+
.es.p-select.p-disabled .p-select-dropdown,
|
|
3775
|
+
.es.p-multiselect.p-disabled .p-multiselect-dropdown {
|
|
3776
|
+
color: var(--text-soft);
|
|
3777
|
+
}
|
|
3778
|
+
|
|
3779
|
+
/* Comptoir variant: the dense-row select. It replaced a native <select>,
|
|
3780
|
+
which had no minimum width, so it keeps none and shrinks with its cell
|
|
3781
|
+
or grid column instead of widening it (the 200px floor above would push
|
|
3782
|
+
a 390px row sideways). Height stays on --hit-field like the default. */
|
|
3783
|
+
.es--comptoir.p-select,
|
|
3784
|
+
.es--comptoir.p-multiselect {
|
|
3785
|
+
min-width: 0;
|
|
3757
3786
|
}
|
|
3758
3787
|
|
|
3759
3788
|
/* Small variant — match `.btn-sm` height */
|
|
@@ -5266,3 +5295,23 @@ input.ef-input[type='number'] {
|
|
|
5266
5295
|
color: var(--text-mute);
|
|
5267
5296
|
cursor: pointer;
|
|
5268
5297
|
}
|
|
5298
|
+
|
|
5299
|
+
/* -- Touch tier for the ef-select panel --------------------------------
|
|
5300
|
+
The trigger already grows to --hit-touch on a coarse pointer through
|
|
5301
|
+
--hit-field; the rows it opens were still ~36px and the filter 32px,
|
|
5302
|
+
too small for a thumb now that every comptoir select opens this panel
|
|
5303
|
+
instead of the OS list. A virtual-scrolled list is left alone: its rows
|
|
5304
|
+
must match `virtualScrollItemSize`, so a caller that turns it on sets
|
|
5305
|
+
that to the touch height itself. Last in the file so the base rules
|
|
5306
|
+
above do not win. */
|
|
5307
|
+
@media (pointer: coarse) {
|
|
5308
|
+
.es__panel:not(:has(.p-virtualscroller)) .p-select-option,
|
|
5309
|
+
.es__panel:not(:has(.p-virtualscroller)) .p-multiselect-option {
|
|
5310
|
+
min-block-size: var(--hit-touch);
|
|
5311
|
+
}
|
|
5312
|
+
|
|
5313
|
+
.es__panel .p-select-filter,
|
|
5314
|
+
.es__panel .p-multiselect-filter {
|
|
5315
|
+
min-block-size: var(--hit-touch);
|
|
5316
|
+
}
|
|
5317
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { DoCheck, EventEmitter, Provider } from '@angular/core';
|
|
3
|
+
import { ControlValueAccessor, NgControl } from '@angular/forms';
|
|
4
|
+
import { EditorInitEvent } from 'primeng/editor';
|
|
5
|
+
import { AbstractEfFormControl } from '@elasticias/ui';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A rich text field: PrimeNG's Editor (Quill 2) with a small toolbar (bold,
|
|
9
|
+
* italic, underline, bulleted and numbered lists, link, clear formatting) and
|
|
10
|
+
* sanitised HTML out (`sanitizeRichHtml`): the toolbar's tags only, safe links,
|
|
11
|
+
* no script, style, class or event attribute, and `''` for an empty editor.
|
|
12
|
+
*
|
|
13
|
+
* It lives in `@elasticias/ui/rich-text` so that only apps that use it bundle
|
|
14
|
+
* the editor and need `quill` (a peer dependency of this entry). PrimeNG loads
|
|
15
|
+
* Quill with a dynamic import the first time an editor renders, so a screen
|
|
16
|
+
* without one never downloads it. `ef-localized-input`'s `rich` mode renders
|
|
17
|
+
* it through `EF_RICH_TEXT`; provide that with `provideEfRichText()`.
|
|
18
|
+
*
|
|
19
|
+
* - `dir` and `lang` apply to the editing area only; the toolbar is page chrome.
|
|
20
|
+
* - Disabled by `[disabled]`, a form's `disable()` or `[efReadOnly]`; `readonly`
|
|
21
|
+
* keeps the text selectable. The toolbar is disabled with it.
|
|
22
|
+
* - The editing area is a `role="textbox"` named by the label and described
|
|
23
|
+
* by the hint, the errors and `describedByExtra`.
|
|
24
|
+
* - An incoming value equal to what it last emitted is ignored, so binding
|
|
25
|
+
* the output back never resets the caret.
|
|
26
|
+
*
|
|
27
|
+
* i18n keys the app's bundles must define (toolbar names and the link bubble,
|
|
28
|
+
* whose Quill texts are otherwise hardcoded English):
|
|
29
|
+
*
|
|
30
|
+
* | key | fr | ar | en |
|
|
31
|
+
* |---|---|---|---|
|
|
32
|
+
* | `ef_rich_text_bold` | Gras | غامق | Bold |
|
|
33
|
+
* | `ef_rich_text_italic` | Italique | مائل | Italic |
|
|
34
|
+
* | `ef_rich_text_underline` | Souligné | تسطير | Underline |
|
|
35
|
+
* | `ef_rich_text_bullet_list` | Liste à puces | قائمة نقطية | Bulleted list |
|
|
36
|
+
* | `ef_rich_text_ordered_list` | Liste numérotée | قائمة مرقمة | Numbered list |
|
|
37
|
+
* | `ef_rich_text_link` | Lien | رابط | Link |
|
|
38
|
+
* | `ef_rich_text_clean` | Effacer la mise en forme | مسح التنسيق | Clear formatting |
|
|
39
|
+
* | `ef_rich_text_toolbar` | Mise en forme | التنسيق | Formatting |
|
|
40
|
+
* | `ef_rich_text_link_enter` | Adresse du lien : | عنوان الرابط: | Link address: |
|
|
41
|
+
* | `ef_rich_text_link_visit` | Ouvrir : | فتح: | Open: |
|
|
42
|
+
* | `ef_rich_text_link_edit` | Modifier | تعديل | Edit |
|
|
43
|
+
* | `ef_rich_text_link_remove` | Retirer | إزالة | Remove |
|
|
44
|
+
* | `ef_rich_text_link_save` | Enregistrer | حفظ | Save |
|
|
45
|
+
*
|
|
46
|
+
* ```html
|
|
47
|
+
* <ef-rich-text name="description" labelKey="product_description" dir="rtl" lang="ar"
|
|
48
|
+
* [value]="html()" (valueChangeEvent)="html.set($event)" />
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
declare class EfRichTextComponent extends AbstractEfFormControl implements ControlValueAccessor, DoCheck {
|
|
52
|
+
readonly value: _angular_core.InputSignal<string | null | undefined>;
|
|
53
|
+
/** Direction of the text (`rtl` for Arabic), on the editing area only. */
|
|
54
|
+
readonly dir: _angular_core.InputSignal<"ltr" | "rtl" | "auto" | null>;
|
|
55
|
+
/** Language of the text, on the editing area only. */
|
|
56
|
+
readonly lang: _angular_core.InputSignal<string | null>;
|
|
57
|
+
/** Id(s) naming the editing area when the label is rendered elsewhere
|
|
58
|
+
* (`ef-localized-input` draws one label above its culture tabs). */
|
|
59
|
+
readonly labelledBy: _angular_core.InputSignal<string | null>;
|
|
60
|
+
/** Visible height of the editing area, in lines. */
|
|
61
|
+
readonly rows: _angular_core.InputSignal<number>;
|
|
62
|
+
/** Sanitised HTML, `''` when the editor holds no text. */
|
|
63
|
+
readonly valueChangeEvent: EventEmitter<string>;
|
|
64
|
+
readonly ngControl: NgControl | null;
|
|
65
|
+
private readonly host;
|
|
66
|
+
protected readonly formats: string[];
|
|
67
|
+
protected readonly modules: {
|
|
68
|
+
keyboard: {
|
|
69
|
+
bindings: {
|
|
70
|
+
tab: {
|
|
71
|
+
key: string;
|
|
72
|
+
handler: () => boolean;
|
|
73
|
+
};
|
|
74
|
+
'indent code-block': null;
|
|
75
|
+
'outdent code-block': null;
|
|
76
|
+
'remove tab': null;
|
|
77
|
+
indent: {
|
|
78
|
+
format: string[];
|
|
79
|
+
key: string;
|
|
80
|
+
handler: () => boolean;
|
|
81
|
+
};
|
|
82
|
+
outdent: {
|
|
83
|
+
shiftKey: boolean;
|
|
84
|
+
format: string[];
|
|
85
|
+
key: string;
|
|
86
|
+
handler: () => boolean;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
protected readonly labelId: string;
|
|
92
|
+
private readonly current;
|
|
93
|
+
private readonly quill;
|
|
94
|
+
private lastEmitted;
|
|
95
|
+
private onChange;
|
|
96
|
+
private onTouched;
|
|
97
|
+
constructor();
|
|
98
|
+
get isInvalid(): boolean;
|
|
99
|
+
get serverErrors(): string[] | null;
|
|
100
|
+
get showRequired(): boolean;
|
|
101
|
+
/** Read-only or disabled: Quill and the toolbar stop editing. */
|
|
102
|
+
protected locked(): boolean;
|
|
103
|
+
protected onInit(event: EditorInitEvent): void;
|
|
104
|
+
protected onTextChange(): void;
|
|
105
|
+
protected handleBlur(): void;
|
|
106
|
+
ngDoCheck(): void;
|
|
107
|
+
writeValue(v: string | null): void;
|
|
108
|
+
registerOnChange(fn: (v: string) => void): void;
|
|
109
|
+
registerOnTouched(fn: () => void): void;
|
|
110
|
+
setDisabledState(disabled: boolean): void;
|
|
111
|
+
/** Replaces the content unless it is our own echo or the same HTML. */
|
|
112
|
+
private apply;
|
|
113
|
+
/** Role, name, state, direction and language: everything that follows signals. */
|
|
114
|
+
private decorateFromInputs;
|
|
115
|
+
/** Validity and what describes the field, which follow the form control. */
|
|
116
|
+
private decorateFromForm;
|
|
117
|
+
/** Quill's link bubble texts are hardcoded English in its CSS; ours come from attributes. */
|
|
118
|
+
private translateBubble;
|
|
119
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfRichTextComponent, never>;
|
|
120
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfRichTextComponent, "ef-rich-text", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "dir": { "alias": "dir"; "required": false; "isSignal": true; }; "lang": { "alias": "lang"; "required": false; "isSignal": true; }; "labelledBy": { "alias": "labelledBy"; "required": false; "isSignal": true; }; "rows": { "alias": "rows"; "required": false; "isSignal": true; }; }, { "valueChangeEvent": "valueChangeEvent"; }, never, never, true, never>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Turns on `ef-localized-input`'s `rich` mode for the app. Quill still loads
|
|
125
|
+
* lazily, the first time an editor renders.
|
|
126
|
+
*
|
|
127
|
+
* ```ts
|
|
128
|
+
* providers: [provideEfRichText()]
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
declare function provideEfRichText(): Provider;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Whitelists the HTML an ef-rich-text produces: the toolbar's tags, a link's
|
|
135
|
+
* `href` when it is http(s), mailto or tel (with `rel` and `target` set by us),
|
|
136
|
+
* and no other attribute, so no style, class or event handler leaves the
|
|
137
|
+
* browser. Other tags are unwrapped, keeping their text; script, style and
|
|
138
|
+
* frames go with their content. Quill's non-breaking spaces between words
|
|
139
|
+
* become spaces. An editor holding no text returns `''`, never `<p><br></p>`.
|
|
140
|
+
*
|
|
141
|
+
* Browser only: it parses into a `<template>` and so needs `document`.
|
|
142
|
+
*
|
|
143
|
+
* Security note: `npm audit` flags quill 2.0.3 (GHSA-v3m3-f69x-jf25, XSS via
|
|
144
|
+
* HTML export; low severity, no patched release). It is mitigated here
|
|
145
|
+
* because every value ef-rich-text emits goes through this function, and
|
|
146
|
+
* incoming HTML goes through it before Quill parses it. Never render
|
|
147
|
+
* `getSemanticHTML()` without it.
|
|
148
|
+
*/
|
|
149
|
+
declare function sanitizeRichHtml(html: string | null | undefined): string;
|
|
150
|
+
|
|
151
|
+
export { EfRichTextComponent, provideEfRichText, sanitizeRichHtml };
|