@unicef-polymer/etools-form-builder 3.1.2 → 3.1.4

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.
@@ -30,7 +30,24 @@ const translations = {
30
30
  NUMBER_MUST_BE_LOWER_THAN: 'Number must be lower than {0}',
31
31
  DOCUMENT_TYPE: 'Document type',
32
32
  SELECT_DOCUMENT_TYPE: 'Select Document Type',
33
- PLEASE_ANSWER: 'Important: Please provide the answer if available'
33
+ PLEASE_ANSWER: 'Important: Please provide the answer if available',
34
+ NORMAL_TEXT: 'Normal Text',
35
+ HEADING1: 'Heading 1',
36
+ HEADING2: 'Heading 2',
37
+ HEADING3: 'Heading 3',
38
+ HEADING4: 'Heading 4',
39
+ HEADING5: 'Heading 5',
40
+ HEADING6: 'Heading 6',
41
+ PARAGRAPH: 'Paragraph',
42
+ PRE_FORMATTED: 'Pre-Formatted',
43
+ FONT_SIZE: 'Font Size',
44
+ VERY_SMALL: 'Very Small',
45
+ SMALL: 'Small',
46
+ NORMAL: 'Normal',
47
+ MEDIUM_LARGE: 'Medium Large',
48
+ LARGE: 'Large',
49
+ VERY_LARGE: 'Very Large',
50
+ MAXIMUM: 'Maximum'
34
51
  },
35
52
  fr: {
36
53
  SAVE: 'Sauvegarder',
@@ -63,7 +80,24 @@ const translations = {
63
80
  NUMBER_MUST_BE_LOWER_THAN: 'Le nombre doit être inférieur à {0}',
64
81
  DOCUMENT_TYPE: 'Type de document',
65
82
  SELECT_DOCUMENT_TYPE: 'Sélectionnez le type de document',
66
- PLEASE_ANSWER: 'Important : veuillez fournir la réponse si disponible'
83
+ PLEASE_ANSWER: 'Important : veuillez fournir la réponse si disponible',
84
+ NORMAL_TEXT: 'Texte normal',
85
+ HEADING1: 'Titre 1',
86
+ HEADING2: 'Titre 2',
87
+ HEADING3: 'Titre 3',
88
+ HEADING4: 'Titre 4',
89
+ HEADING5: 'Titre 5',
90
+ HEADING6: 'Titre 6',
91
+ PARAGRAPH: 'Paragraphe',
92
+ PRE_FORMATTED: 'Pré-formaté',
93
+ FONT_SIZE: 'Taille de la police',
94
+ VERY_SMALL: 'Très petit',
95
+ SMALL: 'Petit',
96
+ NORMAL: 'Normale',
97
+ MEDIUM_LARGE: 'Moyen Grand',
98
+ LARGE: 'Grand',
99
+ VERY_LARGE: 'Très grand',
100
+ MAXIMUM: 'Maximum'
67
101
  },
68
102
  ar: {
69
103
  SAVE: 'يحفظ',
@@ -96,7 +130,24 @@ const translations = {
96
130
  NUMBER_MUST_BE_LOWER_THAN: 'يجب أن يكون الرقم أقل من {0}',
97
131
  DOCUMENT_TYPE: 'نوع الوثيقة',
98
132
  SELECT_DOCUMENT_TYPE: 'حدد نوع المستند',
99
- PLEASE_ANSWER: 'هام: يرجى تقديم الإجابة إذا كانت متوفرة'
133
+ PLEASE_ANSWER: 'هام: يرجى تقديم الإجابة إذا كانت متوفرة',
134
+ NORMAL_TEXT: 'نص عادي',
135
+ HEADING1: 'العنوان 1',
136
+ HEADING2: 'العنوان 2',
137
+ HEADING3: 'العنوان 3',
138
+ HEADING4: 'العنوان 4',
139
+ HEADING5: 'العنوان 5',
140
+ HEADING6: 'العنوان 6',
141
+ PARAGRAPH: 'فقرة',
142
+ PRE_FORMATTED: 'منسقة مسبقًا',
143
+ FONT_SIZE: 'حجم الخط',
144
+ VERY_SMALL: 'صغير جدًا',
145
+ SMALL: 'صغير',
146
+ NORMAL: 'طبيعي',
147
+ MEDIUM_LARGE: 'متوسطة كبيرة',
148
+ LARGE: 'كبير',
149
+ VERY_LARGE: 'كبير جدًا',
150
+ MAXIMUM: 'الحد الأقصى'
100
151
  },
101
152
  es: {
102
153
  SAVE: 'Guardar',
@@ -129,7 +180,24 @@ const translations = {
129
180
  NUMBER_MUST_BE_LOWER_THAN: 'El número debe ser inferior a {0}',
130
181
  DOCUMENT_TYPE: 'Tipo de Documento',
131
182
  SELECT_DOCUMENT_TYPE: 'Seleccionar tipo de documento',
132
- PLEASE_ANSWER: 'Importante: proporcione la respuesta si está disponible.'
183
+ PLEASE_ANSWER: 'Importante: proporcione la respuesta si está disponible.',
184
+ NORMAL_TEXT: 'Texto normal',
185
+ HEADING1: 'Título 1',
186
+ HEADING2: 'Título 2',
187
+ HEADING3: 'Título 3',
188
+ HEADING4: 'Título 4',
189
+ HEADING5: 'Título 5',
190
+ HEADING6: 'Título 6',
191
+ PARAGRAPH: 'Párrafo',
192
+ PRE_FORMATTED: 'Preformateado',
193
+ FONT_SIZE: 'Tamaño de fuente',
194
+ VERY_SMALL: 'Muy pequeño',
195
+ SMALL: 'Pequeño',
196
+ NORMAL: 'Normal',
197
+ MEDIUM_LARGE: 'Mediano Grande',
198
+ LARGE: 'Grande',
199
+ VERY_LARGE: 'Muy Grande',
200
+ MAXIMUM: 'Máximo'
133
201
  }
134
202
  };
135
203
  export default translations;
@@ -12,6 +12,7 @@ export declare abstract class AbstractFieldBaseClass<T> extends LitElement {
12
12
  placeholder: string;
13
13
  name: string;
14
14
  value: T | null;
15
+ showRichEditor: boolean;
15
16
  validators: FieldValidator[];
16
17
  touched: boolean;
17
18
  set defaultValue(value: any);
@@ -27,6 +27,7 @@ export class AbstractFieldBaseClass extends LitElement {
27
27
  this.placeholder = '';
28
28
  this.name = '';
29
29
  this.value = null;
30
+ this.showRichEditor = false;
30
31
  this.validators = [];
31
32
  this.touched = false;
32
33
  this._readonly = false;
@@ -97,7 +98,6 @@ export class AbstractFieldBaseClass extends LitElement {
97
98
  }
98
99
 
99
100
  :host(.wide) .question {
100
- margin-bottom: -8px;
101
101
  min-height: 0;
102
102
  }
103
103
 
@@ -197,3 +197,6 @@ __decorate([
197
197
  __decorate([
198
198
  property()
199
199
  ], AbstractFieldBaseClass.prototype, "value", void 0);
200
+ __decorate([
201
+ property({ type: Boolean })
202
+ ], AbstractFieldBaseClass.prototype, "showRichEditor", void 0);
@@ -16,7 +16,7 @@ export declare class FieldRendererComponent extends LitElement {
16
16
  defaultValue: any;
17
17
  render(): TemplateResult;
18
18
  renderField(blueprintField: BlueprintField): TemplateResult;
19
- renderStandardField({ input_type, label, help_text, required, placeholder, styling, name }: BlueprintField, isMandatory?: boolean): TemplateResult;
19
+ renderStandardField({ input_type, label, help_text, required, placeholder, styling, name }: BlueprintField, isMandatory?: boolean, isAdditionalField?: boolean): TemplateResult;
20
20
  renderTooltip(isMandatory: boolean): "" | TemplateResult<1>;
21
21
  renderRepeatableField({ input_type, label, help_text, required, placeholder, styling }: BlueprintField, isMandatory?: boolean): TemplateResult;
22
22
  renderFieldLabel(label: string, helperText: string, isMandatory?: boolean): TemplateResult;
@@ -23,7 +23,8 @@ let FieldRendererComponent = class FieldRendererComponent extends LitElement {
23
23
  return this.renderField(this.field);
24
24
  }
25
25
  renderField(blueprintField) {
26
- const additionalClass = blueprintField.styling.includes(StructureTypes.ADDITIONAL)
26
+ const isAdditionalField = blueprintField.styling.includes(StructureTypes.ADDITIONAL);
27
+ const additionalClass = isAdditionalField
27
28
  ? `additional-field ${blueprintField.name} `
28
29
  : `${blueprintField.name} `;
29
30
  const wideClass = blueprintField.styling.includes(StructureTypes.WIDE) ? 'wide-field-container ' : '';
@@ -34,11 +35,11 @@ let FieldRendererComponent = class FieldRendererComponent extends LitElement {
34
35
  <div class="${`${additionalClass}${wideClass}${mandatoryClass}finding-container`}">
35
36
  ${blueprintField.repeatable
36
37
  ? this.renderRepeatableField(blueprintField, !!mandatoryClass)
37
- : this.renderStandardField(blueprintField, !!mandatoryClass)}
38
+ : this.renderStandardField(blueprintField, !!mandatoryClass, isAdditionalField)}
38
39
  </div>
39
40
  `;
40
41
  }
41
- renderStandardField({ input_type, label, help_text, required, placeholder, styling, name }, isMandatory = false) {
42
+ renderStandardField({ input_type, label, help_text, required, placeholder, styling, name }, isMandatory = false, isAdditionalField = false) {
42
43
  var _a, _b, _c, _d;
43
44
  const isWide = styling.includes(StructureTypes.WIDE);
44
45
  switch (input_type) {
@@ -54,6 +55,7 @@ let FieldRendererComponent = class FieldRendererComponent extends LitElement {
54
55
  .validators="${this.validations}"
55
56
  .errorMessage="${this.errorMessage}"
56
57
  .defaultValue="${(_a = this.field) === null || _a === void 0 ? void 0 : _a.default_value}"
58
+ .showRichEditor="${isAdditionalField}"
57
59
  >
58
60
  ${this.renderFieldLabel(label, help_text, isMandatory)}
59
61
  </text-field>
@@ -1,8 +1,11 @@
1
- import { CSSResultArray, TemplateResult } from 'lit';
1
+ import { CSSResultArray, PropertyValues, TemplateResult } from 'lit';
2
2
  import { BaseField } from './base-field';
3
3
  import '@unicef-polymer/etools-unicef/src/etools-input/etools-textarea';
4
+ import '../../rich-editor/rich-text';
4
5
  export declare class TextField extends BaseField<string> {
6
+ protected originalValue: string | null;
5
7
  protected controlTemplate(): TemplateResult;
8
+ updated(changedProperties: PropertyValues): void;
6
9
  protected customValidation(): string | null;
7
10
  static get styles(): CSSResultArray;
8
11
  }
@@ -1,26 +1,51 @@
1
1
  import { __decorate } from "tslib";
2
2
  import { css, html } from 'lit';
3
- import { customElement } from 'lit/decorators.js';
3
+ import { customElement, property } from 'lit/decorators.js';
4
4
  import { BaseField } from './base-field';
5
5
  import '@unicef-polymer/etools-unicef/src/etools-input/etools-textarea';
6
+ import '../../rich-editor/rich-text';
6
7
  let TextField = class TextField extends BaseField {
8
+ constructor() {
9
+ super(...arguments);
10
+ this.originalValue = null;
11
+ }
7
12
  controlTemplate() {
8
- return html `
9
- <etools-textarea
10
- id="otherInfo"
11
- class="no-padding-left"
12
- no-label-float
13
- placeholder="${this.isReadonly ? '—' : this.placeholder}"
14
- .value="${this.value}"
15
- @value-changed="${({ detail }) => this.valueChanged(detail.value)}"
16
- @focus="${() => (this.touched = true)}"
17
- ?readonly="${this.isReadonly}"
18
- ?invalid="${this.errorMessage}"
19
- name="${this.name}"
20
- error-message="${this.errorMessage}"
21
- >
22
- </etools-textarea>
23
- `;
13
+ return this.showRichEditor
14
+ ? html `<div class="finding-container no-padding-left">
15
+ <rich-text
16
+ .value="${this.originalValue}"
17
+ ?readonly="${this.isReadonly}"
18
+ @editor-changed="${({ detail }) => {
19
+ if (detail.value !== this.value) {
20
+ this.valueChanged(detail.value);
21
+ }
22
+ }}"
23
+ ></rich-text>
24
+ </div>`
25
+ : html `
26
+ <etools-textarea
27
+ id="otherInfo"
28
+ class="no-padding-left"
29
+ no-label-float
30
+ placeholder="${this.isReadonly ? '—' : this.placeholder}"
31
+ .value="${this.value}"
32
+ @value-changed="${({ detail }) => this.valueChanged(detail.value)}"
33
+ @focus="${() => (this.touched = true)}"
34
+ ?readonly="${this.isReadonly}"
35
+ ?invalid="${this.errorMessage}"
36
+ name="${this.name}"
37
+ error-message="${this.errorMessage}"
38
+ >
39
+ </etools-textarea>
40
+ `;
41
+ }
42
+ updated(changedProperties) {
43
+ super.updated(changedProperties);
44
+ // set control value only at the beginning with a proxy param (originalValue),
45
+ // avoid requestUpdate when .value change because will move carret
46
+ if (changedProperties.has('value') && this.originalValue === null) {
47
+ this.originalValue = this.value;
48
+ }
24
49
  }
25
50
  customValidation() {
26
51
  return null;
@@ -42,6 +67,9 @@ let TextField = class TextField extends BaseField {
42
67
  ];
43
68
  }
44
69
  };
70
+ __decorate([
71
+ property()
72
+ ], TextField.prototype, "originalValue", void 0);
45
73
  TextField = __decorate([
46
74
  customElement('text-field')
47
75
  ], TextField);
@@ -0,0 +1,19 @@
1
+ import { LitElement } from 'lit';
2
+ import '@unicef-polymer/etools-unicef/src/etools-dropdown/etools-dropdown';
3
+ import '@unicef-polymer/etools-unicef/src/etools-icon-button/etools-icon-button';
4
+ export declare class RichAction extends LitElement {
5
+ static styles: import("lit").CSSResult;
6
+ command: string;
7
+ value?: string;
8
+ icon: string;
9
+ active: boolean;
10
+ color: string;
11
+ values: Option[];
12
+ render(): import("lit-html").TemplateResult<1>;
13
+ }
14
+ interface Option {
15
+ name: string;
16
+ value: string;
17
+ }
18
+ export declare function editorCommand(command: string, value?: string): void;
19
+ export {};
@@ -0,0 +1,106 @@
1
+ import { __decorate } from "tslib";
2
+ import { css, html, LitElement } from 'lit';
3
+ import { property, customElement } from 'lit/decorators.js';
4
+ import '@unicef-polymer/etools-unicef/src/etools-dropdown/etools-dropdown';
5
+ import '@unicef-polymer/etools-unicef/src/etools-icon-button/etools-icon-button';
6
+ let RichAction = class RichAction extends LitElement {
7
+ constructor() {
8
+ super(...arguments);
9
+ this.command = '';
10
+ this.icon = 'info';
11
+ this.active = false;
12
+ this.color = '#000000';
13
+ this.values = [];
14
+ }
15
+ render() {
16
+ const hasItems = this.values.length > 0;
17
+ return html `<section style="color:${this.color}">
18
+ ${hasItems
19
+ ? html ` <etools-dropdown
20
+ option-label="name"
21
+ option-value="value"
22
+ .options="${this.values}"
23
+ .selected="${this.values[0].value}"
24
+ dynamic-align
25
+ hide-search
26
+ trigger-value-change-event
27
+ @etools-selected-item-changed="${({ detail }) => {
28
+ if (detail === undefined || detail.selectedItem === null) {
29
+ return;
30
+ }
31
+ const selectedValue = detail.selectedItem.value;
32
+ if (selectedValue === '--') {
33
+ editorCommand('removeFormat', undefined);
34
+ }
35
+ else {
36
+ editorCommand(this.command, selectedValue);
37
+ }
38
+ }}"
39
+ >
40
+ </etools-dropdown>`
41
+ : html `<etools-icon-button
42
+ ?active="${this.active}"
43
+ name="${this.icon}"
44
+ @click=${() => {
45
+ if (this.command) {
46
+ editorCommand(this.command, this.value);
47
+ }
48
+ else {
49
+ this.dispatchEvent(new Event('action', {
50
+ bubbles: true,
51
+ composed: true
52
+ }));
53
+ }
54
+ }}
55
+ ></etools-icon-button>`}
56
+ <div><slot></slot></div>
57
+ </section>`;
58
+ }
59
+ };
60
+ RichAction.styles = css `
61
+ section {
62
+ height: 100%;
63
+ display: flex;
64
+ flex-direction: row;
65
+ flex-wrap: wrap;
66
+ align-items: center;
67
+ margin-inline-end: 4px;
68
+ }
69
+ section * {
70
+ margin: 2px;
71
+ }
72
+ etools-icon-button {
73
+ cursor: pointer;
74
+ width: 12px;
75
+ padding: 2px 4px;
76
+ }
77
+ etools-dropdown {
78
+ min-width: 140px;
79
+ margin-block-end: 2px;
80
+ }
81
+ `;
82
+ __decorate([
83
+ property({ type: String })
84
+ ], RichAction.prototype, "command", void 0);
85
+ __decorate([
86
+ property({ type: String })
87
+ ], RichAction.prototype, "value", void 0);
88
+ __decorate([
89
+ property({ type: String })
90
+ ], RichAction.prototype, "icon", void 0);
91
+ __decorate([
92
+ property({ type: Boolean })
93
+ ], RichAction.prototype, "active", void 0);
94
+ __decorate([
95
+ property({ type: String })
96
+ ], RichAction.prototype, "color", void 0);
97
+ __decorate([
98
+ property({ type: Array })
99
+ ], RichAction.prototype, "values", void 0);
100
+ RichAction = __decorate([
101
+ customElement('rich-action')
102
+ ], RichAction);
103
+ export { RichAction };
104
+ export function editorCommand(command, value) {
105
+ document.execCommand(command, true, value);
106
+ }
@@ -0,0 +1,12 @@
1
+ import { LitElement } from 'lit';
2
+ import './rich-toolbar';
3
+ import './rich-viewer';
4
+ export declare class RichText extends LitElement {
5
+ static styles: import("lit").CSSResult;
6
+ selection: Selection | null;
7
+ readonly: boolean;
8
+ node: Element;
9
+ value: string | null | undefined;
10
+ render(): import("lit-html").TemplateResult<1>;
11
+ firstUpdated(): void;
12
+ }
@@ -0,0 +1,125 @@
1
+ import { __decorate } from "tslib";
2
+ import { css, html, LitElement } from 'lit';
3
+ import { property, customElement } from 'lit/decorators.js';
4
+ import { fireEvent } from '../lib/utils/fire-custom-event';
5
+ import './rich-toolbar';
6
+ import './rich-viewer';
7
+ let RichText = class RichText extends LitElement {
8
+ constructor() {
9
+ super(...arguments);
10
+ this.selection = null;
11
+ this.readonly = false;
12
+ this.node = document.createElement('div');
13
+ }
14
+ render() {
15
+ const { selection, readonly, node } = this;
16
+ return html `<main>
17
+ <rich-toolbar
18
+ ?hidden="${this.readonly}"
19
+ .selection="${selection}"
20
+ .node="${node}"
21
+ @set-content=${(e) => {
22
+ var _a;
23
+ const event = e;
24
+ const parser = new DOMParser();
25
+ const doc = parser.parseFromString(event.detail, 'text/html');
26
+ const root = doc.querySelector('body');
27
+ this.node.innerHTML = (_a = root === null || root === void 0 ? void 0 : root.innerHTML) !== null && _a !== void 0 ? _a : '';
28
+ this.requestUpdate();
29
+ }}
30
+ ></rich-toolbar>
31
+ <rich-viewer
32
+ ?readonly="${readonly}"
33
+ .value="${this.value}"
34
+ @selection=${(e) => {
35
+ const event = e;
36
+ this.selection = event.detail.selection;
37
+ fireEvent(this, 'editor-changed', { value: event.detail.html });
38
+ }}
39
+ .node="${node}"
40
+ >
41
+ </rich-viewer>
42
+ </main>`;
43
+ }
44
+ firstUpdated() {
45
+ const children = this.children;
46
+ if ((children === null || children === void 0 ? void 0 : children.length) > 0) {
47
+ // Check if <template> is the first child
48
+ const template = children[0];
49
+ if (template.tagName === 'TEMPLATE') {
50
+ const content = template.innerHTML.trim();
51
+ if (content.length > 0) {
52
+ this.node.innerHTML = content;
53
+ this.requestUpdate();
54
+ }
55
+ }
56
+ }
57
+ }
58
+ };
59
+ RichText.styles = css `
60
+ :host {
61
+ --rich-color: black;
62
+ --rich-background: white;
63
+ --rich-action-active-color: red;
64
+ --icon-size: 24px;
65
+ border: solid 1px var(--rich-border-color, #eeeeee);
66
+ width: 100%;
67
+ }
68
+ main {
69
+ height: 100%;
70
+ width: 100%;
71
+ display: grid;
72
+ grid-template-rows: 1fr auto;
73
+ grid-template-columns: 1fr;
74
+ grid-template-areas:
75
+ 'viewer'
76
+ 'toolbar';
77
+ }
78
+
79
+ rich-toolbar {
80
+ grid-area: toolbar;
81
+ width: 100%;
82
+ background-color: var(--rich-toolbar-background, #ffffff);
83
+ color: var(--rich-color);
84
+ border-bottom: 1px solid var(--rich-toolbar-border-color, #eeeeee);
85
+ }
86
+
87
+ rich-viewer {
88
+ grid-area: viewer;
89
+ flex: 1;
90
+ width: 100%;
91
+ min-height: 40px;
92
+ overflow-y: auto;
93
+ background-color: var(--rich-background);
94
+ color: var(--rich-color);
95
+ }
96
+ rich-viewer[readonly] {
97
+ background-color: #eeeeee;
98
+ }
99
+ main {
100
+ grid-template-rows: auto 1fr;
101
+ grid-template-areas:
102
+ 'toolbar'
103
+ 'viewer';
104
+ }
105
+ rich-toolbar {
106
+ border-top: none;
107
+ border-bottom: 1px solid var(--rich-color);
108
+ }
109
+ `;
110
+ __decorate([
111
+ property({ type: Object, hasChanged: () => true })
112
+ ], RichText.prototype, "selection", void 0);
113
+ __decorate([
114
+ property({ type: Boolean })
115
+ ], RichText.prototype, "readonly", void 0);
116
+ __decorate([
117
+ property({ type: Object, hasChanged: () => true })
118
+ ], RichText.prototype, "node", void 0);
119
+ __decorate([
120
+ property({ type: String })
121
+ ], RichText.prototype, "value", void 0);
122
+ RichText = __decorate([
123
+ customElement('rich-text')
124
+ ], RichText);
125
+ export { RichText };
@@ -0,0 +1,19 @@
1
+ import { LitElement } from 'lit';
2
+ import './rich-action';
3
+ export declare class RichToolbar extends LitElement {
4
+ static styles: import("lit").CSSResult;
5
+ fgColorInput: HTMLInputElement;
6
+ bdColorInput: HTMLInputElement;
7
+ fileHandle?: any;
8
+ node: Element;
9
+ formatColor: string;
10
+ backgroundColor: string;
11
+ selection: Selection | null;
12
+ language: string;
13
+ render(): import("lit-html").TemplateResult<1>;
14
+ constructor();
15
+ connectedCallback(): void;
16
+ disconnectedCallback(): void;
17
+ handleLanguageChange(e: CustomEvent): void;
18
+ getTags(): string[];
19
+ }
@@ -0,0 +1,213 @@
1
+ import { __decorate } from "tslib";
2
+ import { css, html, LitElement } from 'lit';
3
+ import { property, customElement, query, state } from 'lit/decorators.js';
4
+ import './rich-action';
5
+ import { editorCommand } from './rich-action';
6
+ import { getTranslation } from '../lib/utils/translate';
7
+ let RichToolbar = class RichToolbar extends LitElement {
8
+ render() {
9
+ const tags = this.getTags();
10
+ return html `<header>
11
+ <rich-action icon="editor:format-clear" command="removeFormat"></rich-action>
12
+ <rich-action icon="editor:format-bold" command="bold" ?active=${tags.includes('b')}></rich-action>
13
+ <rich-action icon="editor:format-italic" command="italic" ?active=${tags.includes('i')}></rich-action>
14
+ <rich-action icon="editor:format-underlined" command="underline" ?active=${tags.includes('u')}></rich-action>
15
+ <rich-action icon="editor:format-align-left" command="justifyleft"></rich-action>
16
+ <rich-action icon="editor:format-align-center" command="justifycenter"></rich-action>
17
+ <rich-action icon="editor:format-align-right" command="justifyright"></rich-action>
18
+ <rich-action
19
+ icon="editor:format-list-numbered"
20
+ command="insertorderedlist"
21
+ ?active=${tags.includes('ol')}
22
+ ></rich-action>
23
+ <rich-action
24
+ icon="editor:format-list-bulleted"
25
+ command="insertunorderedlist"
26
+ ?active=${tags.includes('ul')}
27
+ ></rich-action>
28
+ <rich-action icon="format_quote" command="formatblock" value="blockquote"></rich-action>
29
+ <!-- <rich-action icon="format_indent_decrease" command="outdent"></rich-action>
30
+ <rich-action icon="format_indent_increase" command="indent"></rich-action> -->
31
+ <rich-action
32
+ icon="editor:add-link"
33
+ ?active=${tags.includes('a')}
34
+ @action=${() => {
35
+ const newLink = prompt('Write the URL here', 'https://');
36
+ // Check if valid url
37
+ if (newLink && newLink.match(/^(http|https):\/\/[^ "]+$/)) {
38
+ editorCommand('createlink', newLink);
39
+ }
40
+ }}
41
+ >
42
+ </rich-action>
43
+ <rich-action icon="editor:unlink" ?active=${tags.includes('a')} command="unlink"></rich-action>
44
+ <rich-action
45
+ icon="editor:format-color-text"
46
+ .color="${this.formatColor}"
47
+ @action=${() => this.fgColorInput.click()}
48
+ >
49
+ <input
50
+ type="color"
51
+ id="fg-color"
52
+ @input=${(e) => {
53
+ const input = e.target;
54
+ this.formatColor = input.value;
55
+ editorCommand('forecolor', input.value);
56
+ }}
57
+ />
58
+ </rich-action>
59
+ <rich-action
60
+ icon="editor:border-color"
61
+ .color="${this.backgroundColor}"
62
+ @action=${() => this.bdColorInput.click()}
63
+ >
64
+ <input
65
+ type="color"
66
+ id="bd-color"
67
+ @input=${(e) => {
68
+ const input = e.target;
69
+ this.backgroundColor = input.value;
70
+ editorCommand('backcolor', input.value);
71
+ }}
72
+ />
73
+ </rich-action>
74
+ <rich-action
75
+ icon="title"
76
+ command="formatblock"
77
+ .values=${[
78
+ { name: getTranslation(this.language, 'NORMAL_TEXT'), value: '--' },
79
+ { name: getTranslation(this.language, 'HEADING1'), value: 'h1' },
80
+ { name: getTranslation(this.language, 'HEADING2'), value: 'h2' },
81
+ { name: getTranslation(this.language, 'HEADING3'), value: 'h3' },
82
+ { name: getTranslation(this.language, 'HEADING4'), value: 'h4' },
83
+ { name: getTranslation(this.language, 'HEADING5'), value: 'h5' },
84
+ { name: getTranslation(this.language, 'HEADING6'), value: 'h6' },
85
+ { name: getTranslation(this.language, 'PARAGRAPH'), value: 'p' },
86
+ { name: getTranslation(this.language, 'PRE_FORMATTED'), value: 'pre' }
87
+ ]}
88
+ ></rich-action>
89
+ <rich-action
90
+ icon="editor:format-size"
91
+ command="fontsize"
92
+ .values=${[
93
+ { name: getTranslation(this.language, 'FONT_SIZE'), value: '--' },
94
+ { name: getTranslation(this.language, 'VERY_SMALL'), value: '1' },
95
+ { name: getTranslation(this.language, 'SMALL'), value: '2' },
96
+ { name: getTranslation(this.language, 'NORMAL'), value: '3' },
97
+ { name: getTranslation(this.language, 'MEDIUM_LARGE'), value: '4' },
98
+ { name: getTranslation(this.language, 'LARGE'), value: '5' },
99
+ { name: getTranslation(this.language, 'VERY_LARGE'), value: '6' },
100
+ { name: getTranslation(this.language, 'MAXIMUM'), value: '7' }
101
+ ]}
102
+ ></rich-action>
103
+ <rich-action icon="undo" command="undo"></rich-action>
104
+ <rich-action icon="redo" command="redo"></rich-action>
105
+ <!-- <rich-action icon="content_cut" command="cut"></rich-action> -->
106
+ <!-- <rich-action icon="content_copy" command="copy"></rich-action>
107
+ <rich-action icon="content_paste" command="paste"></rich-action> -->
108
+ </header>`;
109
+ }
110
+ constructor() {
111
+ super();
112
+ this.formatColor = '#000000';
113
+ this.backgroundColor = '#000000';
114
+ this.selection = null;
115
+ if (!this.language) {
116
+ this.language = window.EtoolsLanguage || 'en';
117
+ }
118
+ this.handleLanguageChange = this.handleLanguageChange.bind(this);
119
+ }
120
+ connectedCallback() {
121
+ super.connectedCallback();
122
+ document.addEventListener('language-changed', this.handleLanguageChange.bind(this));
123
+ }
124
+ disconnectedCallback() {
125
+ super.disconnectedCallback();
126
+ document.removeEventListener('language-changed', this.handleLanguageChange.bind(this));
127
+ }
128
+ handleLanguageChange(e) {
129
+ this.language = e.detail.language;
130
+ }
131
+ getTags() {
132
+ var _a;
133
+ let tags = [];
134
+ if (this.selection) {
135
+ if (this.selection.type === 'Range') {
136
+ // @ts-ignore
137
+ let parentNode = this.selection.baseNode;
138
+ if (parentNode) {
139
+ const checkNode = () => {
140
+ var _a, _b;
141
+ const tag = (_b = (_a = parentNode === null || parentNode === void 0 ? void 0 : parentNode.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === null || _b === void 0 ? void 0 : _b.trim();
142
+ if (tag)
143
+ tags.push(tag);
144
+ };
145
+ while (parentNode != null) {
146
+ checkNode();
147
+ parentNode = parentNode === null || parentNode === void 0 ? void 0 : parentNode.parentNode;
148
+ }
149
+ }
150
+ // Remove root tag
151
+ tags.pop();
152
+ }
153
+ else {
154
+ const content = ((_a = this.selection) === null || _a === void 0 ? void 0 : _a.toString()) || '';
155
+ tags = (content.match(/<[^>]+>/g) || [])
156
+ .filter((tag) => !tag.startsWith('</'))
157
+ .map((tag) => tag.replace(/<|>/g, ''));
158
+ }
159
+ }
160
+ return tags;
161
+ }
162
+ };
163
+ RichToolbar.styles = css `
164
+ header {
165
+ width: 100%;
166
+ display: flex;
167
+ flex-direction: row;
168
+ align-items: flex-end;
169
+ justify-content: flex-start;
170
+ padding-inline-start: 12px;
171
+ flex-wrap: wrap;
172
+ }
173
+ input[type='color'] {
174
+ -webkit-appearance: none;
175
+ border: none;
176
+ width: 0;
177
+ height: 0;
178
+ }
179
+ input[type='color']::-webkit-color-swatch-wrapper {
180
+ padding: 0;
181
+ }
182
+ input[type='color']::-webkit-color-swatch {
183
+ border: none;
184
+ }
185
+ `;
186
+ __decorate([
187
+ query('#fg-color')
188
+ ], RichToolbar.prototype, "fgColorInput", void 0);
189
+ __decorate([
190
+ query('#bd-color')
191
+ ], RichToolbar.prototype, "bdColorInput", void 0);
192
+ __decorate([
193
+ state()
194
+ ], RichToolbar.prototype, "fileHandle", void 0);
195
+ __decorate([
196
+ property({ type: Object, hasChanged: () => true })
197
+ ], RichToolbar.prototype, "node", void 0);
198
+ __decorate([
199
+ property({ type: String })
200
+ ], RichToolbar.prototype, "formatColor", void 0);
201
+ __decorate([
202
+ property({ type: String })
203
+ ], RichToolbar.prototype, "backgroundColor", void 0);
204
+ __decorate([
205
+ property({ type: Object, hasChanged: () => true })
206
+ ], RichToolbar.prototype, "selection", void 0);
207
+ __decorate([
208
+ property()
209
+ ], RichToolbar.prototype, "language", void 0);
210
+ RichToolbar = __decorate([
211
+ customElement('rich-toolbar')
212
+ ], RichToolbar);
213
+ export { RichToolbar };
@@ -0,0 +1,11 @@
1
+ import { LitElement } from 'lit';
2
+ export declare class RichViewer extends LitElement {
3
+ static styles: import("lit").CSSResult;
4
+ content: HTMLDivElement;
5
+ readonly: boolean;
6
+ value: string;
7
+ node: Element;
8
+ render(): import("lit-html").TemplateResult<1>;
9
+ updateSelection(): void;
10
+ firstUpdated(): void;
11
+ }
@@ -0,0 +1,80 @@
1
+ import { __decorate } from "tslib";
2
+ import { html, css, LitElement } from 'lit';
3
+ import { customElement, property, query } from 'lit/decorators.js';
4
+ import { fireEvent } from '../lib/utils/fire-custom-event';
5
+ let RichViewer = class RichViewer extends LitElement {
6
+ constructor() {
7
+ super(...arguments);
8
+ this.readonly = false;
9
+ this.value = '';
10
+ }
11
+ render() {
12
+ return html `<article
13
+ id="content"
14
+ contenteditable="${this.readonly ? 'false' : 'true'}"
15
+ .innerHTML="${this.value}"
16
+ @input=${() => this.updateSelection()}
17
+ ></article>`;
18
+ }
19
+ updateSelection() {
20
+ var _a, _b;
21
+ if (this.readonly) {
22
+ return;
23
+ }
24
+ // @ts-ignore
25
+ const shadowSelection = ((_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.getSelection)
26
+ ? // @ts-ignore
27
+ this.shadowRoot.getSelection()
28
+ : null;
29
+ const selection = shadowSelection || document.getSelection() || window.getSelection();
30
+ fireEvent(this, 'selection', { selection: selection, html: (_b = this.content) === null || _b === void 0 ? void 0 : _b.innerHTML });
31
+ }
32
+ firstUpdated() {
33
+ document.execCommand('defaultParagraphSeparator', false, 'br');
34
+ document.addEventListener('selectionchange', () => {
35
+ this.updateSelection();
36
+ });
37
+ window.addEventListener('selectionchange', () => {
38
+ this.updateSelection();
39
+ });
40
+ document.addEventListener('keydown', () => {
41
+ this.updateSelection();
42
+ });
43
+ }
44
+ };
45
+ RichViewer.styles = css `
46
+ article {
47
+ width: calc(100% - 24px);
48
+ padding: 12px;
49
+ height: auto;
50
+ min-height: 60px;
51
+ }
52
+
53
+ article[contenteditable='true'] {
54
+ border: none;
55
+ outline: none;
56
+ }
57
+ article[contenteditable='false'] {
58
+ background-color: var(--secondary-background-color);
59
+ }
60
+ blockquote {
61
+ margin-inline-start: 8px;
62
+ margin-inline-end: 8px;
63
+ }
64
+ `;
65
+ __decorate([
66
+ query('#content')
67
+ ], RichViewer.prototype, "content", void 0);
68
+ __decorate([
69
+ property({ type: Boolean, reflect: true })
70
+ ], RichViewer.prototype, "readonly", void 0);
71
+ __decorate([
72
+ property({ type: String })
73
+ ], RichViewer.prototype, "value", void 0);
74
+ __decorate([
75
+ property({ type: Object, hasChanged: () => true })
76
+ ], RichViewer.prototype, "node", void 0);
77
+ RichViewer = __decorate([
78
+ customElement('rich-viewer')
79
+ ], RichViewer);
80
+ export { RichViewer };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unicef-polymer/etools-form-builder",
3
3
  "description": "Etools FM Form Builder components",
4
- "version": "3.1.2",
4
+ "version": "3.1.4",
5
5
  "type": "module",
6
6
  "contributors": [
7
7
  "eTools Team"
@@ -32,15 +32,15 @@
32
32
  "typescript": "^4.9.5"
33
33
  },
34
34
  "peerDependencies": {
35
- "@unicef-polymer/etools-unicef": ">=1.1.3"
35
+ "@unicef-polymer/etools-unicef": ">=1.2.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@unicef-polymer/etools-unicef": "^1.1.3",
39
- "eslint": "^9.16.0",
40
38
  "@eslint/eslintrc": "^3.2.0",
41
39
  "@eslint/js": "^9.16.0",
42
40
  "@typescript-eslint/eslint-plugin": "^8.17.0",
43
41
  "@typescript-eslint/parser": "^8.17.0",
42
+ "@unicef-polymer/etools-unicef": "^1.2.0",
43
+ "eslint": "^9.16.0",
44
44
  "eslint-config-prettier": "^9.1.0",
45
45
  "eslint-plugin-html": "^8.1.2",
46
46
  "eslint-plugin-lit": "^1.15.0",