@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elasticias/ui",
3
- "version": "1.0.52",
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",
@@ -3750,10 +3750,39 @@ input.ef-input[type='number'] {
3750
3750
  outline: 0;
3751
3751
  }
3752
3752
 
3753
- .es.p-disabled {
3754
- opacity: 0.55;
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 };