@dile/ui 3.19.0 → 3.19.2
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/components/floating-menu/src/DileFloatingMenu.js +1 -6
- package/components/input/src/DileInput.js +10 -0
- package/components/input/src/DileInputContact.js +2 -0
- package/components/input/src/DileInputIcon.js +2 -0
- package/components/select/src/DileSelect.js +37 -8
- package/components/select/src/DileSelectOverlay.js +41 -7
- package/components/select/src/DileSelectSearch.js +3 -1
- package/components/select/src/DileSelectSearchableBase.js +44 -0
- package/mixins/close-document-click/src/DileCloseDocumentClick.js +10 -1
- package/mixins/form/src/DileForm.js +13 -13
- package/mixins/overlay/src/DileOverlay.js +38 -10
- package/package.json +2 -2
|
@@ -243,12 +243,7 @@ export class DileFloatingMenu extends DileOverlay(DileCloseOnEscPressed(LitEleme
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
_hideOverlay() {
|
|
246
|
-
//
|
|
247
|
-
if (this._opening) {
|
|
248
|
-
this.cancelDelay();
|
|
249
|
-
this._opening = false;
|
|
250
|
-
this._overlayClass = 'opened';
|
|
251
|
-
}
|
|
246
|
+
// DileOverlay's close() also cancels an opening still in its delay.
|
|
252
247
|
super.close();
|
|
253
248
|
}
|
|
254
249
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LitElement, html, css } from 'lit';
|
|
2
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
2
3
|
import { DileEmmitChange } from '../../../mixins/form/index.js';
|
|
3
4
|
import { labelStyles } from './label-styles.js';
|
|
4
5
|
import { messageStyles } from './message-styles.js';
|
|
@@ -22,6 +23,9 @@ export class DileInput extends DileEmmitChange(LitElement) {
|
|
|
22
23
|
/** Label to the element */
|
|
23
24
|
label: { type: String },
|
|
24
25
|
|
|
26
|
+
/** Accessible name of the input, applied as aria-label only when there is no visible label */
|
|
27
|
+
accessibleLabel: { type: String },
|
|
28
|
+
|
|
25
29
|
/** Input type */
|
|
26
30
|
type: { type: String },
|
|
27
31
|
|
|
@@ -166,6 +170,7 @@ export class DileInput extends DileEmmitChange(LitElement) {
|
|
|
166
170
|
type="${this.availableType(this.type)}"
|
|
167
171
|
id="textField"
|
|
168
172
|
name="${this.name}"
|
|
173
|
+
aria-label="${ifDefined(this.inputAriaLabel)}"
|
|
169
174
|
placeholder="${this.placeholder}"
|
|
170
175
|
?disabled="${this.disabled}"
|
|
171
176
|
?readonly="${this.readonly}"
|
|
@@ -187,6 +192,11 @@ export class DileInput extends DileEmmitChange(LitElement) {
|
|
|
187
192
|
`;
|
|
188
193
|
}
|
|
189
194
|
|
|
195
|
+
/** aria-label for the inner input: accessibleLabel, only when there is no visible label */
|
|
196
|
+
get inputAriaLabel() {
|
|
197
|
+
return !this.label && this.accessibleLabel ? this.accessibleLabel : undefined;
|
|
198
|
+
}
|
|
199
|
+
|
|
190
200
|
get messageTemplate() {
|
|
191
201
|
return html`
|
|
192
202
|
${this.message
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LitElement, html, css } from 'lit';
|
|
2
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
2
3
|
import { DileInput } from '../../input/index.js';
|
|
3
4
|
|
|
4
5
|
export class DileInputContact extends DileInput {
|
|
@@ -48,6 +49,7 @@ export class DileInputContact extends DileInput {
|
|
|
48
49
|
type="${this.availableType(this.type)}"
|
|
49
50
|
id="textField"
|
|
50
51
|
name="${this.name}"
|
|
52
|
+
aria-label="${ifDefined(this.inputAriaLabel)}"
|
|
51
53
|
placeholder="${this.placeholder}"
|
|
52
54
|
?disabled="${this.disabled}"
|
|
53
55
|
?readonly="${this.readonly}"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LitElement, html, css } from 'lit';
|
|
2
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
2
3
|
import { DileInput } from '../../input/index.js';
|
|
3
4
|
import '@dile/iconlib/dile-iconlib.js';
|
|
4
5
|
import '@dile/ui/components/tooltip/tooltip.js';
|
|
@@ -100,6 +101,7 @@ export class DileInputIcon extends DileInput {
|
|
|
100
101
|
type="${this.availableType(this.type)}"
|
|
101
102
|
id="textField"
|
|
102
103
|
name="${this.name}"
|
|
104
|
+
aria-label="${ifDefined(this.inputAriaLabel)}"
|
|
103
105
|
placeholder="${this.placeholder}"
|
|
104
106
|
?disabled="${this.disabled}"
|
|
105
107
|
?readonly="${this.readonly}"
|
|
@@ -97,6 +97,8 @@ export class DileSelect extends DileEmmitChange(LitElement) {
|
|
|
97
97
|
static get properties() {
|
|
98
98
|
return {
|
|
99
99
|
label: { type: String },
|
|
100
|
+
/** Accessible name of the slotted select when there is no visible label. With label, the label text is used */
|
|
101
|
+
accessibleLabel: { type: String },
|
|
100
102
|
value: { type: String },
|
|
101
103
|
name: { type: String, reflect: true },
|
|
102
104
|
disabled: { type: Boolean },
|
|
@@ -114,7 +116,7 @@ export class DileSelect extends DileEmmitChange(LitElement) {
|
|
|
114
116
|
return html`
|
|
115
117
|
<div>
|
|
116
118
|
${this.label
|
|
117
|
-
? html`<label for="${this.selectId}">${this.label}</label>`
|
|
119
|
+
? html`<label for="${this.selectId}" @click="${this._focusSelect}">${this.label}</label>`
|
|
118
120
|
: ""
|
|
119
121
|
}
|
|
120
122
|
<div class="select-wrapper">
|
|
@@ -159,6 +161,11 @@ export class DileSelect extends DileEmmitChange(LitElement) {
|
|
|
159
161
|
if (!this.elselect.id) {
|
|
160
162
|
this.elselect.id = this.selectId;
|
|
161
163
|
}
|
|
164
|
+
// aria-label / aria-description set by the author on the slotted select always win
|
|
165
|
+
if (this._authorAriaLabel === undefined) {
|
|
166
|
+
this._authorAriaLabel = this.elselect.hasAttribute("aria-label");
|
|
167
|
+
this._authorAriaDescription = this.elselect.hasAttribute("aria-description");
|
|
168
|
+
}
|
|
162
169
|
this.elselect.addEventListener("change", this.changeHandler);
|
|
163
170
|
this.updateSelectA11y();
|
|
164
171
|
}
|
|
@@ -194,7 +201,8 @@ export class DileSelect extends DileEmmitChange(LitElement) {
|
|
|
194
201
|
this.elselect.classList.toggle("disabled", this.disabled);
|
|
195
202
|
this.elselect.setAttribute("aria-disabled", this.disabled);
|
|
196
203
|
}
|
|
197
|
-
if(changedProperties.has("errored") || changedProperties.has("message")
|
|
204
|
+
if(changedProperties.has("errored") || changedProperties.has("message")
|
|
205
|
+
|| changedProperties.has("label") || changedProperties.has("accessibleLabel")) {
|
|
198
206
|
this.updateSelectA11y();
|
|
199
207
|
}
|
|
200
208
|
}
|
|
@@ -205,18 +213,39 @@ export class DileSelect extends DileEmmitChange(LitElement) {
|
|
|
205
213
|
// Set aria-invalid when errored
|
|
206
214
|
this.elselect.setAttribute("aria-invalid", this.errored);
|
|
207
215
|
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
216
|
+
// The message lives in the shadow DOM and the select in the light DOM, so an
|
|
217
|
+
// aria-describedby id reference cannot cross the boundary: use aria-description.
|
|
218
|
+
// aria-describedby is left to the author (e.g. help text in the light DOM).
|
|
219
|
+
if (!this._authorAriaDescription) {
|
|
220
|
+
if (this.message) {
|
|
221
|
+
this.elselect.setAttribute("aria-description", this.message);
|
|
222
|
+
} else {
|
|
223
|
+
this.elselect.removeAttribute("aria-description");
|
|
224
|
+
}
|
|
214
225
|
}
|
|
215
226
|
|
|
227
|
+
// Accessible name. The <label for> lives in the shadow DOM and the select in the
|
|
228
|
+
// light DOM, so "for" cannot cross the boundary: name the select via aria-label.
|
|
229
|
+
if (!this._authorAriaLabel) {
|
|
230
|
+
const accessibleName = this.label || this.accessibleLabel;
|
|
231
|
+
if (accessibleName) {
|
|
232
|
+
this.elselect.setAttribute("aria-label", accessibleName);
|
|
233
|
+
} else {
|
|
234
|
+
this.elselect.removeAttribute("aria-label");
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
216
238
|
// Update visual state
|
|
217
239
|
this.elselect.classList.toggle("errored", this.errored);
|
|
218
240
|
}
|
|
219
241
|
|
|
242
|
+
// <label for> cannot reach the slotted select, so replicate the native label click
|
|
243
|
+
_focusSelect() {
|
|
244
|
+
if (this.elselect && !this.disabled) {
|
|
245
|
+
this.elselect.focus();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
220
249
|
firstUpdated() {
|
|
221
250
|
this.quiet = this.quietOnStart;
|
|
222
251
|
if(this.value) {
|
|
@@ -150,6 +150,11 @@ export class DileSelectOverlay extends DileOverlay(DileCloseDocumentClick(DileEm
|
|
|
150
150
|
.option[aria-selected="true"] {
|
|
151
151
|
font-weight: var(--dile-select-overlay-selected-font-weight, 600);
|
|
152
152
|
}
|
|
153
|
+
.option-content {
|
|
154
|
+
display: block;
|
|
155
|
+
flex: 1 1 auto;
|
|
156
|
+
min-width: 0;
|
|
157
|
+
}
|
|
153
158
|
@media (forced-colors: active) {
|
|
154
159
|
#trigger {
|
|
155
160
|
border-width: var(--dile-input-border-width, 1px);
|
|
@@ -172,6 +177,8 @@ export class DileSelectOverlay extends DileOverlay(DileCloseDocumentClick(DileEm
|
|
|
172
177
|
static get properties() {
|
|
173
178
|
return {
|
|
174
179
|
label: { type: String },
|
|
180
|
+
/** Accessible name of the combobox trigger and the listbox, applied as aria-label only when there is no visible label */
|
|
181
|
+
accessibleLabel: { type: String },
|
|
175
182
|
value: { type: String },
|
|
176
183
|
name: { type: String, reflect: true },
|
|
177
184
|
disabled: { type: Boolean },
|
|
@@ -185,6 +192,12 @@ export class DileSelectOverlay extends DileOverlay(DileCloseDocumentClick(DileEm
|
|
|
185
192
|
/** Suppresses the visible trigger box, letting a parent component drive open()/close()
|
|
186
193
|
* and forward keyboard events via handleExternalKeydown() while supplying its own visible control. */
|
|
187
194
|
hideTrigger: { type: Boolean, reflect: true },
|
|
195
|
+
/** Optional custom renderer for the content of each option in the popup:
|
|
196
|
+
* `(option, index) => TemplateResult | string`, where `option` is `{ value, text, icon, element }`
|
|
197
|
+
* and `element` is the native <option> (useful to read its dataset). The <li role="option">
|
|
198
|
+
* itself (ids, aria-selected, highlight, click) is still managed by the component, and the
|
|
199
|
+
* trigger, typeahead, accessible name and highlightedText keep using the plain option text. */
|
|
200
|
+
optionRenderer: { attribute: false },
|
|
188
201
|
_activeIndex: { state: true },
|
|
189
202
|
/** Display text of the currently highlighted option, readable by an external composer. */
|
|
190
203
|
highlightedText: { state: true },
|
|
@@ -218,7 +231,7 @@ export class DileSelectOverlay extends DileOverlay(DileCloseDocumentClick(DileEm
|
|
|
218
231
|
|
|
219
232
|
get options() {
|
|
220
233
|
return this.elselect
|
|
221
|
-
? Array.from(this.elselect.options).map(o => ({ value: o.value, text: o.textContent, icon: o.dataset.icon || this.elselect.dataset.icon }))
|
|
234
|
+
? Array.from(this.elselect.options).map(o => ({ value: o.value, text: o.textContent, icon: o.dataset.icon || this.elselect.dataset.icon, element: o }))
|
|
222
235
|
: [];
|
|
223
236
|
}
|
|
224
237
|
|
|
@@ -325,6 +338,11 @@ export class DileSelectOverlay extends DileOverlay(DileCloseDocumentClick(DileEm
|
|
|
325
338
|
`;
|
|
326
339
|
}
|
|
327
340
|
|
|
341
|
+
/** aria-label for the trigger and the listbox: accessibleLabel, only when there is no visible label */
|
|
342
|
+
get controlAriaLabel() {
|
|
343
|
+
return !this.label && this.accessibleLabel ? this.accessibleLabel : undefined;
|
|
344
|
+
}
|
|
345
|
+
|
|
328
346
|
get triggerTemplate() {
|
|
329
347
|
const selected = this.selectedOption;
|
|
330
348
|
return html`
|
|
@@ -332,6 +350,7 @@ export class DileSelectOverlay extends DileOverlay(DileCloseDocumentClick(DileEm
|
|
|
332
350
|
type="button"
|
|
333
351
|
id="trigger"
|
|
334
352
|
role="combobox"
|
|
353
|
+
aria-label="${ifDefined(this.controlAriaLabel)}"
|
|
335
354
|
aria-haspopup="listbox"
|
|
336
355
|
aria-expanded="${this.opened}"
|
|
337
356
|
aria-controls="overlay"
|
|
@@ -360,21 +379,36 @@ export class DileSelectOverlay extends DileOverlay(DileCloseDocumentClick(DileEm
|
|
|
360
379
|
get overlayTemplate() {
|
|
361
380
|
const options = this.options;
|
|
362
381
|
return html`
|
|
363
|
-
<ul id="overlay" class="${this._overlayClass}" role="listbox">
|
|
364
|
-
${options.map((opt, index) =>
|
|
382
|
+
<ul id="overlay" class="${this._overlayClass}" role="listbox" aria-label="${ifDefined(this.controlAriaLabel)}">
|
|
383
|
+
${options.map((opt, index) => {
|
|
384
|
+
const highlighted = index === this._activeIndex;
|
|
385
|
+
const selected = opt.value === (this.value ?? '');
|
|
386
|
+
const parts = ['option', highlighted ? 'option-highlighted' : '', selected ? 'option-selected' : ''].filter(Boolean).join(' ');
|
|
387
|
+
return html`
|
|
365
388
|
<li
|
|
366
389
|
id="${this._optionId(index)}"
|
|
367
390
|
role="option"
|
|
368
|
-
|
|
369
|
-
|
|
391
|
+
part="${parts}"
|
|
392
|
+
class="option ${highlighted ? 'highlighted' : ''}"
|
|
393
|
+
aria-selected="${selected}"
|
|
394
|
+
aria-label="${ifDefined(this.optionRenderer ? opt.text.trim() : undefined)}"
|
|
370
395
|
@click="${() => this.selectOption(index)}"
|
|
371
396
|
@mousemove="${() => this._setActiveIndex(index, false)}"
|
|
372
|
-
>${
|
|
373
|
-
|
|
397
|
+
>${this.optionContentTemplate(opt, index)}</li>
|
|
398
|
+
`;
|
|
399
|
+
})}
|
|
374
400
|
</ul>
|
|
375
401
|
`;
|
|
376
402
|
}
|
|
377
403
|
|
|
404
|
+
/** Content of each <li role="option">: optionRenderer's output when set, icon + text otherwise. */
|
|
405
|
+
optionContentTemplate(opt, index) {
|
|
406
|
+
if (typeof this.optionRenderer === 'function') {
|
|
407
|
+
return html`<span class="option-content" part="option-content">${this.optionRenderer(opt, index)}</span>`;
|
|
408
|
+
}
|
|
409
|
+
return html`${opt.icon ? html`<span class="option-icon">${this.renderIconlibIcon(opt.icon)}</span>` : ''}${opt.text}`;
|
|
410
|
+
}
|
|
411
|
+
|
|
378
412
|
renderIconlibIcon(icon) {
|
|
379
413
|
const dotIndex = icon.indexOf('.');
|
|
380
414
|
if (dotIndex === -1) {
|
|
@@ -65,6 +65,8 @@ export class DileSelectSearch extends DileSelectSearchableBase {
|
|
|
65
65
|
value: option.value,
|
|
66
66
|
text: option.textContent.trim(),
|
|
67
67
|
icon: option.dataset.icon || select.dataset.icon,
|
|
68
|
+
// The native <option>, so an optionRenderer can read its dataset.
|
|
69
|
+
element: option,
|
|
68
70
|
}));
|
|
69
71
|
}
|
|
70
72
|
|
|
@@ -91,7 +93,7 @@ export class DileSelectSearch extends DileSelectSearchableBase {
|
|
|
91
93
|
* Template method: decide whether an option matches the keyword.
|
|
92
94
|
* By default it searches the keyword anywhere in the option text, ignoring case and accents.
|
|
93
95
|
* Override it in a subclass to customize the search (e.g. also match option.value).
|
|
94
|
-
* @param {{value: string, text: string, icon: string}} option
|
|
96
|
+
* @param {{value: string, text: string, icon: string, element: HTMLOptionElement}} option
|
|
95
97
|
* @param {string} keyword
|
|
96
98
|
* @returns {boolean}
|
|
97
99
|
*/
|
|
@@ -11,6 +11,8 @@ import { labelStyles } from '../../input/src/label-styles.js';
|
|
|
11
11
|
import { messageStyles } from '../../input/src/message-styles.js';
|
|
12
12
|
|
|
13
13
|
const NAVIGATION_KEYS = ['ArrowDown', 'ArrowUp', 'Home', 'End', 'Enter', 'Escape', 'Tab'];
|
|
14
|
+
// Parts of the nested dile-select-overlay re-exported to this component's host.
|
|
15
|
+
const OPTION_PARTS = ['option', 'option-highlighted', 'option-selected', 'option-content'];
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Base class for the searchable members of the select family (dile-select-ajax-overlay,
|
|
@@ -158,6 +160,13 @@ export class DileSelectSearchableBase extends DileEmmitChange(LitElement) {
|
|
|
158
160
|
/** dile-iconlib "family.name" icon string applied to every option that has no icon of its
|
|
159
161
|
* own via iconProperty. */
|
|
160
162
|
icon: { type: String },
|
|
163
|
+
/** Optional custom renderer for the content of each result: `(item, index) => TemplateResult | string`,
|
|
164
|
+
* receiving the original data object. The displayProperty text is still used for the accessible
|
|
165
|
+
* name, the keyboard announcements and the selected text. */
|
|
166
|
+
optionRenderer: { attribute: false },
|
|
167
|
+
/** Space or comma separated extra part names used inside optionRenderer templates, re-exported
|
|
168
|
+
* from the nested dile-select-overlay so they can be styled with ::part() from outside. */
|
|
169
|
+
optionParts: { type: String },
|
|
161
170
|
delay: { type: Number },
|
|
162
171
|
keyword: {
|
|
163
172
|
type: String,
|
|
@@ -382,6 +391,8 @@ export class DileSelectSearchableBase extends DileEmmitChange(LitElement) {
|
|
|
382
391
|
name="generated-select-field"
|
|
383
392
|
placeholder="${this.placeholder}"
|
|
384
393
|
?disabled=${this.disabled}
|
|
394
|
+
exportparts="${this.exportedOptionParts}"
|
|
395
|
+
.optionRenderer=${this.nestedOptionRenderer}
|
|
385
396
|
@element-changed=${this.doSelected}
|
|
386
397
|
>
|
|
387
398
|
<select slot="select" data-icon="${ifDefined(this.icon)}">
|
|
@@ -393,6 +404,39 @@ export class DileSelectSearchableBase extends DileEmmitChange(LitElement) {
|
|
|
393
404
|
`;
|
|
394
405
|
}
|
|
395
406
|
|
|
407
|
+
/** exportparts value for the nested dile-select-overlay: built-in option parts plus optionParts. */
|
|
408
|
+
get exportedOptionParts() {
|
|
409
|
+
const extra = (this.optionParts || '').split(/[\s,]+/).filter(Boolean);
|
|
410
|
+
return [...new Set([...OPTION_PARTS, ...extra])].join(', ');
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** Adapts optionRenderer (which receives data items) to the nested overlay's (option, index) signature.
|
|
414
|
+
* Cached per optionRenderer so the nested component doesn't see a new function on every render. */
|
|
415
|
+
get nestedOptionRenderer() {
|
|
416
|
+
if (typeof this.optionRenderer !== 'function') {
|
|
417
|
+
return undefined;
|
|
418
|
+
}
|
|
419
|
+
if (this._nestedRendererFor !== this.optionRenderer) {
|
|
420
|
+
const renderer = this.optionRenderer;
|
|
421
|
+
this._nestedRendererFor = renderer;
|
|
422
|
+
this._nestedRenderer = (option, index) => {
|
|
423
|
+
const item = this.findItemForOption(option, index);
|
|
424
|
+
return item === undefined ? option.text : renderer(item, index);
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
return this._nestedRenderer;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/** Data item behind an option of the nested overlay: by index (options are generated from
|
|
431
|
+
* `data` in order), checked against idProperty, falling back to a search by idProperty. */
|
|
432
|
+
findItemForOption(option, index) {
|
|
433
|
+
const byIndex = this.data[index];
|
|
434
|
+
if (byIndex && String(byIndex[this.idProperty]) === option.value) {
|
|
435
|
+
return byIndex;
|
|
436
|
+
}
|
|
437
|
+
return this.data.find(item => String(item[this.idProperty]) === option.value);
|
|
438
|
+
}
|
|
439
|
+
|
|
396
440
|
get messageTemplate() {
|
|
397
441
|
return html`
|
|
398
442
|
${this.message
|
|
@@ -28,7 +28,7 @@ export const DileCloseDocumentClick = (SuperClass) =>
|
|
|
28
28
|
DileCloseDocumentClick.closeDocumentHandler = () => {
|
|
29
29
|
if(DileCloseDocumentClick.elements) {
|
|
30
30
|
for (let ele of DileCloseDocumentClick.elements) {
|
|
31
|
-
ele.
|
|
31
|
+
ele.closeFromDocumentClick();
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
};
|
|
@@ -70,6 +70,15 @@ export const DileCloseDocumentClick = (SuperClass) =>
|
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Close requested by a click in the document. Calls close() by default.
|
|
75
|
+
* Override it to ignore these closes in some states, as DileOverlay does
|
|
76
|
+
* while the overlay is opening.
|
|
77
|
+
*/
|
|
78
|
+
closeFromDocumentClick() {
|
|
79
|
+
this.close();
|
|
80
|
+
}
|
|
81
|
+
|
|
73
82
|
/**
|
|
74
83
|
* This method only alerts the developer to create the close() method in his component
|
|
75
84
|
*/
|
|
@@ -69,7 +69,9 @@ export const DileForm = (superclass) => class extends superclass {
|
|
|
69
69
|
node.value = value;
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
-
} else if (!isArray && adaptedData[fieldName]
|
|
72
|
+
} else if (!isArray && adaptedData[fieldName] === null) {
|
|
73
|
+
this._clearNode(node);
|
|
74
|
+
} else if (!isArray && adaptedData[fieldName] !== undefined) {
|
|
73
75
|
if (typeof node.set === "function") {
|
|
74
76
|
node.set(adaptedData[fieldName]);
|
|
75
77
|
} else {
|
|
@@ -80,13 +82,15 @@ export const DileForm = (superclass) => class extends superclass {
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
clearData() {
|
|
83
|
-
this.allNodeElements.forEach(node =>
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
85
|
+
this.allNodeElements.forEach(node => this._clearNode(node));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
_clearNode(node) {
|
|
89
|
+
if (typeof node.clear === "function") {
|
|
90
|
+
node.clear();
|
|
91
|
+
} else {
|
|
92
|
+
node.value = '';
|
|
93
|
+
}
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
get allNodeElements() {
|
|
@@ -143,11 +147,7 @@ export const DileForm = (superclass) => class extends superclass {
|
|
|
143
147
|
clearField(name) {
|
|
144
148
|
const node = this.getNodeElement(name);
|
|
145
149
|
if (node) {
|
|
146
|
-
|
|
147
|
-
node.clear();
|
|
148
|
-
} else {
|
|
149
|
-
node.value = '';
|
|
150
|
-
}
|
|
150
|
+
this._clearNode(node);
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
|
|
@@ -80,35 +80,63 @@ export const DileOverlay = function(superClass) {
|
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
82
|
* closes the overlay
|
|
83
|
+
*
|
|
84
|
+
* An explicit close() while the overlay is still opening (the 50ms delay of open())
|
|
85
|
+
* cancels the opening, so the overlay ends up closed.
|
|
83
86
|
*/
|
|
84
87
|
close() {
|
|
85
|
-
if(
|
|
88
|
+
if(this._opening) {
|
|
89
|
+
this.cancelDelay();
|
|
90
|
+
this._opening = false;
|
|
91
|
+
this._overlayClass = '';
|
|
92
|
+
this.overlay.style.display = 'none';
|
|
93
|
+
this._dispatchClosed();
|
|
94
|
+
} else if(this._overlayClass=='opened') {
|
|
86
95
|
this._overlayClass = '';
|
|
87
96
|
this._removePositionListeners();
|
|
88
97
|
this.cancelDelay();
|
|
89
98
|
this.delayId = setTimeout(() => {
|
|
90
99
|
this.overlay.style.display = 'none'
|
|
91
100
|
}, 500);
|
|
92
|
-
this.
|
|
101
|
+
this._dispatchClosed();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Close requested by a document click (DileCloseDocumentClick). It is ignored while
|
|
106
|
+
* the overlay is opening, because the same click that opens the overlay bubbles up
|
|
107
|
+
* to the document.
|
|
108
|
+
*/
|
|
109
|
+
closeFromDocumentClick() {
|
|
110
|
+
if(!this._opening) {
|
|
111
|
+
this.close();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
_dispatchClosed() {
|
|
115
|
+
this.dispatchEvent(new CustomEvent('overlay-closed', {
|
|
93
116
|
bubbles: true,
|
|
94
117
|
composed: true,
|
|
95
118
|
}));
|
|
96
|
-
}
|
|
97
119
|
}
|
|
98
120
|
/**
|
|
99
121
|
* opens the overlay
|
|
122
|
+
*
|
|
123
|
+
* overlay-opened is only dispatched when the overlay was closed, so every
|
|
124
|
+
* overlay-opened is paired with one overlay-closed.
|
|
100
125
|
*/
|
|
101
126
|
open() {
|
|
102
|
-
this.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
127
|
+
if(!this._opening && this._overlayClass != 'opened') {
|
|
128
|
+
this.dispatchEvent(new CustomEvent('overlay-opened', {
|
|
129
|
+
bubbles: true,
|
|
130
|
+
composed: true,
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
106
133
|
if(!this._opening) {
|
|
107
134
|
this._opening = true;
|
|
108
135
|
this.updatePosition();
|
|
109
|
-
if
|
|
110
|
-
|
|
111
|
-
|
|
136
|
+
// Close the other overlays, even if they are still opening.
|
|
137
|
+
if(this.closeOthers) {
|
|
138
|
+
this.closeOthers();
|
|
139
|
+
}
|
|
112
140
|
this.overlay.style.display = 'block';
|
|
113
141
|
this.cancelDelay();
|
|
114
142
|
this.delayId = setTimeout(() => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dile/ui",
|
|
3
|
-
"version": "3.19.
|
|
3
|
+
"version": "3.19.2",
|
|
4
4
|
"description": "UI Core components from dile-components.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -28,5 +28,5 @@
|
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "545b8f87465a4567904c577699562f3c5f676e68"
|
|
32
32
|
}
|