@nyaruka/temba-components 0.23.0 → 0.25.1
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/CHANGELOG.md +31 -0
- package/demo/index.html +136 -97
- package/dist/a29f77aa.js +356 -0
- package/dist/index.js +2 -2
- package/dist/sw.js +1 -1
- package/dist/sw.js.map +1 -1
- package/dist/templates/components-body.html +1 -1
- package/dist/templates/components-head.html +1 -1
- package/out-tsc/src/checkbox/Checkbox.js +29 -14
- package/out-tsc/src/checkbox/Checkbox.js.map +1 -1
- package/out-tsc/src/contactsearch/ContactSearch.js +146 -72
- package/out-tsc/src/contactsearch/ContactSearch.js.map +1 -1
- package/out-tsc/src/dialog/Dialog.js +12 -3
- package/out-tsc/src/dialog/Dialog.js.map +1 -1
- package/out-tsc/src/dialog/Modax.js +12 -2
- package/out-tsc/src/dialog/Modax.js.map +1 -1
- package/out-tsc/src/interfaces.js +1 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/omnibox/Omnibox.js +7 -1
- package/out-tsc/src/omnibox/Omnibox.js.map +1 -1
- package/out-tsc/src/options/Options.js +36 -6
- package/out-tsc/src/options/Options.js.map +1 -1
- package/out-tsc/src/select/Select.js +86 -35
- package/out-tsc/src/select/Select.js.map +1 -1
- package/out-tsc/src/textinput/TextInput.js +42 -1
- package/out-tsc/src/textinput/TextInput.js.map +1 -1
- package/out-tsc/test/temba-select.test.js +2 -1
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/checkbox/checked.png +0 -0
- package/screenshots/truth/checkbox/default.png +0 -0
- package/screenshots/truth/select/disabled-multi-selection.png +0 -0
- package/screenshots/truth/select/disabled-selection.png +0 -0
- package/screenshots/truth/select/disabled.png +0 -0
- package/screenshots/truth/select/embedded.png +0 -0
- package/screenshots/truth/select/expression-selected.png +0 -0
- package/screenshots/truth/select/expressions.png +0 -0
- package/screenshots/truth/select/functions.png +0 -0
- package/screenshots/truth/select/local-options.png +0 -0
- package/screenshots/truth/select/remote-options.png +0 -0
- package/screenshots/truth/select/search-enabled.png +0 -0
- package/screenshots/truth/select/search-multi-no-matches.png +0 -0
- package/screenshots/truth/select/search-selected-focus.png +0 -0
- package/screenshots/truth/select/search-selected.png +0 -0
- package/screenshots/truth/select/search-with-selected.png +0 -0
- package/screenshots/truth/select/searching.png +0 -0
- package/screenshots/truth/select/selected-multi.png +0 -0
- package/screenshots/truth/select/selected-single.png +0 -0
- package/screenshots/truth/select/selection-clearable.png +0 -0
- package/screenshots/truth/select/with-placeholder.png +0 -0
- package/screenshots/truth/select/without-placeholder.png +0 -0
- package/src/checkbox/Checkbox.ts +31 -16
- package/src/contactsearch/ContactSearch.ts +157 -80
- package/src/dialog/Dialog.ts +13 -3
- package/src/dialog/Modax.ts +9 -2
- package/src/interfaces.ts +1 -0
- package/src/omnibox/Omnibox.ts +9 -1
- package/src/options/Options.ts +41 -10
- package/src/select/Select.ts +91 -37
- package/src/textinput/TextInput.ts +47 -1
- package/static/css/temba-components.css +1 -2
- package/test/temba-select.test.ts +3 -1
- package/test-assets/style.css +1 -1
- package/dist/28f45617.js +0 -356
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { TemplateResult, html, property, css } from 'lit-element';
|
|
2
|
-
import { getUrl,
|
|
2
|
+
import { getUrl, WebResponse } from '../utils';
|
|
3
3
|
import { TextInput } from '../textinput/TextInput';
|
|
4
4
|
import '../alert/Alert';
|
|
5
|
-
import { Contact } from '../interfaces';
|
|
5
|
+
import { Contact, CustomEventType } from '../interfaces';
|
|
6
6
|
import { styleMap } from 'lit-html/directives/style-map';
|
|
7
7
|
import { FormElement } from '../FormElement';
|
|
8
8
|
|
|
@@ -31,7 +31,7 @@ export class ContactSearch extends FormElement {
|
|
|
31
31
|
width: 160px;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
.
|
|
34
|
+
.date {
|
|
35
35
|
text-align: right;
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -40,7 +40,7 @@ export class ContactSearch extends FormElement {
|
|
|
40
40
|
color: var(--color-text-dark);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
.field-header.
|
|
43
|
+
.field-header.date {
|
|
44
44
|
text-align: right;
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -55,12 +55,6 @@ export class ContactSearch extends FormElement {
|
|
|
55
55
|
|
|
56
56
|
table {
|
|
57
57
|
width: 100%;
|
|
58
|
-
padding-top: 10px;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
.header td {
|
|
62
|
-
border-bottom: 2px solid var(--color-borders);
|
|
63
|
-
padding: 5px 3px;
|
|
64
58
|
}
|
|
65
59
|
|
|
66
60
|
.contact td {
|
|
@@ -92,6 +86,46 @@ export class ContactSearch extends FormElement {
|
|
|
92
86
|
.error {
|
|
93
87
|
margin-top: 10px;
|
|
94
88
|
}
|
|
89
|
+
|
|
90
|
+
.match-count {
|
|
91
|
+
padding: 4px;
|
|
92
|
+
margin-top: 6px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.linked {
|
|
96
|
+
color: var(--color-link-primary);
|
|
97
|
+
text-decoration: none;
|
|
98
|
+
cursor: pointer;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.header td {
|
|
102
|
+
border-bottom: 0px solid var(--color-borders);
|
|
103
|
+
padding: 5px 3px;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.expanded .header td {
|
|
107
|
+
border-bottom: 2px solid var(--color-borders);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
td.field-header,
|
|
111
|
+
tr.table-footer,
|
|
112
|
+
tr.contact {
|
|
113
|
+
display: none;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.expanded td.field-header {
|
|
117
|
+
display: table-cell;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.expanded tr.contact,
|
|
121
|
+
.expanded tr.table-footer {
|
|
122
|
+
display: table-row;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.query {
|
|
126
|
+
display: var(--contact-search-query-display);
|
|
127
|
+
margin-bottom: 10px;
|
|
128
|
+
}
|
|
95
129
|
`;
|
|
96
130
|
}
|
|
97
131
|
|
|
@@ -100,6 +134,9 @@ export class ContactSearch extends FormElement {
|
|
|
100
134
|
@property({ type: Boolean })
|
|
101
135
|
fetching: boolean;
|
|
102
136
|
|
|
137
|
+
@property({ type: Boolean })
|
|
138
|
+
expanded: boolean;
|
|
139
|
+
|
|
103
140
|
@property({ type: String })
|
|
104
141
|
endpoint: string;
|
|
105
142
|
|
|
@@ -112,8 +149,11 @@ export class ContactSearch extends FormElement {
|
|
|
112
149
|
@property({ type: String })
|
|
113
150
|
query = '';
|
|
114
151
|
|
|
115
|
-
@property({ type:
|
|
116
|
-
|
|
152
|
+
@property({ type: Number })
|
|
153
|
+
inactiveThreshold = 1000;
|
|
154
|
+
|
|
155
|
+
@property({ type: Number })
|
|
156
|
+
inactiveDays = 90;
|
|
117
157
|
|
|
118
158
|
@property({ attribute: false })
|
|
119
159
|
summary: SummaryResponse;
|
|
@@ -140,16 +180,22 @@ export class ContactSearch extends FormElement {
|
|
|
140
180
|
}
|
|
141
181
|
}
|
|
142
182
|
|
|
143
|
-
public
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
183
|
+
public executeQuery(query: string): any {
|
|
184
|
+
const url = this.endpoint + query.replace('\n', ' ');
|
|
185
|
+
getUrl(url).then((response: WebResponse) => {
|
|
186
|
+
if (response.status === 200) {
|
|
187
|
+
const summary = response.json as SummaryResponse;
|
|
188
|
+
this.fireCustomEvent(CustomEventType.FetchComplete, summary);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
148
192
|
|
|
193
|
+
public fetchSummary(query: string): any {
|
|
194
|
+
const url = this.endpoint + query.replace('\n', ' ');
|
|
149
195
|
getUrl(url).then((response: WebResponse) => {
|
|
196
|
+
this.fetching = false;
|
|
150
197
|
if (response.status === 200) {
|
|
151
198
|
this.summary = response.json as SummaryResponse;
|
|
152
|
-
this.fetching = false;
|
|
153
199
|
|
|
154
200
|
if (this.summary.error) {
|
|
155
201
|
this.errors = [this.summary.error];
|
|
@@ -157,6 +203,7 @@ export class ContactSearch extends FormElement {
|
|
|
157
203
|
this.errors = [];
|
|
158
204
|
}
|
|
159
205
|
this.requestUpdate('errors');
|
|
206
|
+
this.fireCustomEvent(CustomEventType.ContentChanged, this.summary);
|
|
160
207
|
}
|
|
161
208
|
});
|
|
162
209
|
}
|
|
@@ -177,54 +224,76 @@ export class ContactSearch extends FormElement {
|
|
|
177
224
|
|
|
178
225
|
if (!this.summary.error) {
|
|
179
226
|
const count = this.summary.total;
|
|
180
|
-
const
|
|
181
|
-
query: this.summary.query,
|
|
182
|
-
count,
|
|
183
|
-
});
|
|
227
|
+
const lastSeenOn = this.summary.query.indexOf('last_seen_on') > -1;
|
|
184
228
|
|
|
185
229
|
summary = html`
|
|
186
|
-
<
|
|
187
|
-
<
|
|
188
|
-
<
|
|
189
|
-
|
|
190
|
-
|
|
230
|
+
<div class="summary ${this.expanded ? 'expanded' : ''}">
|
|
231
|
+
<table cellspacing="0" cellpadding="0">
|
|
232
|
+
<tr class="header">
|
|
233
|
+
<td colspan="2">
|
|
234
|
+
Found
|
|
235
|
+
<a
|
|
236
|
+
class="linked"
|
|
237
|
+
target="_"
|
|
238
|
+
href="/contact/?search=${encodeURIComponent(
|
|
239
|
+
this.summary.query
|
|
240
|
+
)}"
|
|
241
|
+
>
|
|
242
|
+
${count.toLocaleString()}
|
|
243
|
+
</a>
|
|
244
|
+
contact${count !== 1 ? 's' : ''}
|
|
245
|
+
</td>
|
|
246
|
+
${fields.map(
|
|
247
|
+
field => html` <td class="field-header">${field.label}</td> `
|
|
248
|
+
)}
|
|
249
|
+
<td></td>
|
|
250
|
+
<td class="field-header date">
|
|
251
|
+
${lastSeenOn ? 'Last Seen' : 'Created'}
|
|
252
|
+
</td>
|
|
253
|
+
</tr>
|
|
254
|
+
|
|
255
|
+
${this.summary.sample.map(
|
|
256
|
+
(contact: Contact) => html`
|
|
257
|
+
<tr class="contact">
|
|
258
|
+
<td class="urn">
|
|
259
|
+
${(contact as any).primary_urn_formatted}
|
|
260
|
+
</td>
|
|
261
|
+
<td class="name">${contact.name}</td>
|
|
262
|
+
${fields.map(
|
|
263
|
+
field => html`
|
|
264
|
+
<td class="field">
|
|
265
|
+
${(
|
|
266
|
+
(contact as any).fields[field.uuid] || { text: '' }
|
|
267
|
+
).text}
|
|
268
|
+
</td>
|
|
269
|
+
`
|
|
270
|
+
)}
|
|
271
|
+
<td></td>
|
|
272
|
+
<td class="date">
|
|
273
|
+
${lastSeenOn
|
|
274
|
+
? contact.last_seen_on || '--'
|
|
275
|
+
: contact.created_on}
|
|
276
|
+
</td>
|
|
277
|
+
</tr>
|
|
278
|
+
`
|
|
191
279
|
)}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
<td></td>
|
|
210
|
-
<td class="created-on">${contact.created_on}</td>
|
|
211
|
-
</tr>
|
|
212
|
-
`
|
|
213
|
-
)}
|
|
214
|
-
|
|
215
|
-
<tr class="table-footer">
|
|
216
|
-
<td class="query-details" colspan=${fields.length + 3}>
|
|
217
|
-
${message}
|
|
218
|
-
</td>
|
|
219
|
-
<td class="more">
|
|
220
|
-
${this.summary.total > this.summary.sample.length
|
|
221
|
-
? html`
|
|
222
|
-
${this.summary.total - this.summary.sample.length} more
|
|
223
|
-
`
|
|
224
|
-
: null}
|
|
225
|
-
</td>
|
|
226
|
-
</tr>
|
|
227
|
-
</table>
|
|
280
|
+
${this.summary.total > this.summary.sample.length
|
|
281
|
+
? html`<tr class="table-footer">
|
|
282
|
+
<td class="query-details" colspan=${fields.length + 3}></td>
|
|
283
|
+
<td class="more">
|
|
284
|
+
<a
|
|
285
|
+
class="linked"
|
|
286
|
+
target="_"
|
|
287
|
+
href="/contact/?search=${encodeURIComponent(
|
|
288
|
+
this.summary.query
|
|
289
|
+
)}"
|
|
290
|
+
>more</a
|
|
291
|
+
>
|
|
292
|
+
</td>
|
|
293
|
+
</tr>`
|
|
294
|
+
: null}
|
|
295
|
+
</table>
|
|
296
|
+
</div>
|
|
228
297
|
`;
|
|
229
298
|
}
|
|
230
299
|
}
|
|
@@ -232,23 +301,31 @@ export class ContactSearch extends FormElement {
|
|
|
232
301
|
const loadingStyle = this.fetching ? { opacity: '1' } : {};
|
|
233
302
|
|
|
234
303
|
return html`
|
|
235
|
-
<
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
</
|
|
251
|
-
|
|
304
|
+
<div class="query">
|
|
305
|
+
<temba-textinput
|
|
306
|
+
.label=${this.label}
|
|
307
|
+
.helpText=${this.helpText}
|
|
308
|
+
.widgetOnly=${this.widgetOnly}
|
|
309
|
+
.errors=${this.errors}
|
|
310
|
+
name=${this.name}
|
|
311
|
+
.inputRoot=${this}
|
|
312
|
+
@input=${this.handleQueryChange}
|
|
313
|
+
placeholder=${this.placeholder}
|
|
314
|
+
.value=${this.query}
|
|
315
|
+
textarea
|
|
316
|
+
autogrow
|
|
317
|
+
>
|
|
318
|
+
</temba-textinput>
|
|
319
|
+
</div>
|
|
320
|
+
|
|
321
|
+
${this.fetching
|
|
322
|
+
? html`<temba-loading
|
|
323
|
+
units="4"
|
|
324
|
+
style=${styleMap(loadingStyle)}
|
|
325
|
+
></temba-loading>`
|
|
326
|
+
: this.summary
|
|
327
|
+
? html` <div class="summary">${summary}</div> `
|
|
328
|
+
: null}
|
|
252
329
|
`;
|
|
253
330
|
}
|
|
254
331
|
}
|
package/src/dialog/Dialog.ts
CHANGED
|
@@ -49,7 +49,7 @@ export class Dialog extends RapidElement {
|
|
|
49
49
|
position: fixed;
|
|
50
50
|
top: 0px;
|
|
51
51
|
left: 0px;
|
|
52
|
-
transition: opacity linear calc(var(--
|
|
52
|
+
transition: opacity linear calc(var(--transition-speed) / 2ms);
|
|
53
53
|
pointer-events: none;
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -68,7 +68,7 @@ export class Dialog extends RapidElement {
|
|
|
68
68
|
|
|
69
69
|
.dialog-body {
|
|
70
70
|
background: #fff;
|
|
71
|
-
max-height:
|
|
71
|
+
max-height: 75vh;
|
|
72
72
|
overflow-y: auto;
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -162,6 +162,9 @@ export class Dialog extends RapidElement {
|
|
|
162
162
|
@property({ type: Boolean })
|
|
163
163
|
destructive: boolean;
|
|
164
164
|
|
|
165
|
+
@property({ type: Boolean })
|
|
166
|
+
disabled: boolean;
|
|
167
|
+
|
|
165
168
|
@property({ type: Boolean })
|
|
166
169
|
loading: boolean;
|
|
167
170
|
|
|
@@ -293,6 +296,10 @@ export class Dialog extends RapidElement {
|
|
|
293
296
|
);
|
|
294
297
|
}
|
|
295
298
|
|
|
299
|
+
public getPrimaryButton(): Button {
|
|
300
|
+
return this.shadowRoot.querySelector(`temba-button[primary]`);
|
|
301
|
+
}
|
|
302
|
+
|
|
296
303
|
private handleKeyUp(event: KeyboardEvent) {
|
|
297
304
|
if (event.key === 'Escape') {
|
|
298
305
|
this.clickCancel();
|
|
@@ -320,7 +327,9 @@ export class Dialog extends RapidElement {
|
|
|
320
327
|
public render(): TemplateResult {
|
|
321
328
|
const height = this.getDocumentHeight();
|
|
322
329
|
|
|
323
|
-
const maskStyle = {
|
|
330
|
+
const maskStyle = {
|
|
331
|
+
height: `${height + 100}px`,
|
|
332
|
+
};
|
|
324
333
|
const dialogStyle = { width: Dialog.widths[this.size] };
|
|
325
334
|
|
|
326
335
|
const header = this.header
|
|
@@ -375,6 +384,7 @@ export class Dialog extends RapidElement {
|
|
|
375
384
|
?destructive=${this.destructive}
|
|
376
385
|
?primary=${!this.destructive}
|
|
377
386
|
?submitting=${this.submitting}
|
|
387
|
+
?disabled=${this.disabled}
|
|
378
388
|
>}</temba-button
|
|
379
389
|
>
|
|
380
390
|
`
|
package/src/dialog/Modax.ts
CHANGED
|
@@ -112,6 +112,11 @@ export class Modax extends RapidElement {
|
|
|
112
112
|
@property({ type: String })
|
|
113
113
|
body: any = this.getLoading();
|
|
114
114
|
|
|
115
|
+
@property({ type: Boolean })
|
|
116
|
+
disabled = false;
|
|
117
|
+
|
|
118
|
+
@property({ type: Boolean })
|
|
119
|
+
suspendSubmit = false;
|
|
115
120
|
// private cancelToken: CancelTokenSource;
|
|
116
121
|
|
|
117
122
|
// http promise to monitor for completeness
|
|
@@ -301,7 +306,9 @@ export class Modax extends RapidElement {
|
|
|
301
306
|
const button = evt.detail.button;
|
|
302
307
|
if (!button.disabled && !button.submitting) {
|
|
303
308
|
if (button.name === this.primaryName) {
|
|
304
|
-
this.
|
|
309
|
+
if (!this.suspendSubmit) {
|
|
310
|
+
this.submit();
|
|
311
|
+
}
|
|
305
312
|
}
|
|
306
313
|
}
|
|
307
314
|
|
|
@@ -309,7 +316,6 @@ export class Modax extends RapidElement {
|
|
|
309
316
|
this.open = false;
|
|
310
317
|
this.fetching = false;
|
|
311
318
|
this.cancelName = undefined;
|
|
312
|
-
// this.cancelToken.cancel();
|
|
313
319
|
}
|
|
314
320
|
}
|
|
315
321
|
|
|
@@ -338,6 +344,7 @@ export class Modax extends RapidElement {
|
|
|
338
344
|
?submitting=${this.submitting}
|
|
339
345
|
?destructive=${this.isDestructive()}
|
|
340
346
|
?noFocus=${true}
|
|
347
|
+
?disabled=${this.disabled}
|
|
341
348
|
@temba-button-clicked=${this.handleDialogClick.bind(this)}
|
|
342
349
|
@temba-dialog-hidden=${this.handleDialogHidden.bind(this)}
|
|
343
350
|
>
|
package/src/interfaces.ts
CHANGED
|
@@ -174,6 +174,7 @@ export enum CustomEventType {
|
|
|
174
174
|
ScrollThreshold = 'temba-scroll-threshold',
|
|
175
175
|
ContentChanged = 'temba-content-changed',
|
|
176
176
|
ContextChanged = 'temba-context-changed',
|
|
177
|
+
FetchComplete = 'temba-fetch-complete',
|
|
177
178
|
Submitted = 'temba-submitted',
|
|
178
179
|
Redirected = 'temba-redirected',
|
|
179
180
|
NoPath = 'temba-no-path',
|
package/src/omnibox/Omnibox.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TemplateResult, html, css, property } from 'lit-element';
|
|
2
2
|
import { styleMap } from 'lit-html/directives/style-map';
|
|
3
3
|
import { RapidElement } from '../RapidElement';
|
|
4
|
+
import { Select } from '../select/Select';
|
|
4
5
|
|
|
5
6
|
enum OmniType {
|
|
6
7
|
Group = 'group',
|
|
@@ -104,7 +105,9 @@ export class Omnibox extends RapidElement {
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
if (option.type === OmniType.Group) {
|
|
107
|
-
return html`
|
|
108
|
+
return html`
|
|
109
|
+
<div style=${styleMap(style)}>${option.count.toLocaleString()}</div>
|
|
110
|
+
`;
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
return null;
|
|
@@ -172,6 +175,11 @@ export class Omnibox extends RapidElement {
|
|
|
172
175
|
}
|
|
173
176
|
}
|
|
174
177
|
|
|
178
|
+
public getValues(): any[] {
|
|
179
|
+
const select = this.shadowRoot.querySelector('temba-select') as Select;
|
|
180
|
+
return select.values;
|
|
181
|
+
}
|
|
182
|
+
|
|
175
183
|
public render(): TemplateResult {
|
|
176
184
|
return html`
|
|
177
185
|
<temba-select
|
package/src/options/Options.ts
CHANGED
|
@@ -9,25 +9,26 @@ import {
|
|
|
9
9
|
throttle,
|
|
10
10
|
} from '../utils';
|
|
11
11
|
|
|
12
|
-
interface NameFunction {
|
|
13
|
-
(option: any): string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
12
|
export class Options extends RapidElement {
|
|
17
13
|
static get styles() {
|
|
18
14
|
return css`
|
|
19
15
|
.options-container {
|
|
20
|
-
visibility: hidden;
|
|
21
|
-
border-radius: var(--curvature-widget);
|
|
22
16
|
background: var(--color-widget-bg-focused);
|
|
23
17
|
user-select: none;
|
|
24
18
|
box-shadow: var(--options-shadow);
|
|
25
|
-
border: 1px solid var(--color-widget-border);
|
|
26
19
|
border-radius: var(--curvature-widget);
|
|
27
20
|
overflow: hidden;
|
|
28
21
|
margin-top: var(--options-margin-top);
|
|
29
22
|
display: flex;
|
|
30
23
|
flex-direction: column;
|
|
24
|
+
transform: scaleY(0.5) translateY(-5em);
|
|
25
|
+
transition: transform var(--transition-speed)
|
|
26
|
+
cubic-bezier(0.71, 0.18, 0.61, 1.33),
|
|
27
|
+
opacity var(--transition-speed) cubic-bezier(0.71, 0.18, 0.61, 1.33);
|
|
28
|
+
z-index: 10000;
|
|
29
|
+
pointer-events: none;
|
|
30
|
+
opacity: 0;
|
|
31
|
+
border: 1px transparent;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
.anchored {
|
|
@@ -88,12 +89,15 @@ export class Options extends RapidElement {
|
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
.show {
|
|
91
|
-
visibility: visible;
|
|
92
92
|
z-index: 10000;
|
|
93
|
+
transform: scaleY(1) translateY(0);
|
|
94
|
+
border: 1px solid var(--color-widget-border);
|
|
95
|
+
pointer-events: auto;
|
|
96
|
+
opacity: 1;
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
.option {
|
|
96
|
-
font-size:
|
|
100
|
+
font-size: var(--temba-options-font-size);
|
|
97
101
|
padding: 5px 10px;
|
|
98
102
|
border-radius: 4px;
|
|
99
103
|
margin: 0.3em;
|
|
@@ -195,6 +199,9 @@ export class Options extends RapidElement {
|
|
|
195
199
|
@property({ type: Array })
|
|
196
200
|
options: any[];
|
|
197
201
|
|
|
202
|
+
@property({ type: Array })
|
|
203
|
+
tempOptions: any[];
|
|
204
|
+
|
|
198
205
|
@property({ type: Boolean })
|
|
199
206
|
poppedTop: boolean;
|
|
200
207
|
|
|
@@ -303,6 +310,15 @@ export class Options extends RapidElement {
|
|
|
303
310
|
});
|
|
304
311
|
}
|
|
305
312
|
|
|
313
|
+
if (changedProperties.has('visible') && changedProperties.has('options')) {
|
|
314
|
+
if (!this.visible && this.options.length == 0) {
|
|
315
|
+
this.tempOptions = changedProperties.get('options');
|
|
316
|
+
window.setTimeout(() => {
|
|
317
|
+
this.tempOptions = [];
|
|
318
|
+
}, 300);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
306
322
|
if (changedProperties.has('options')) {
|
|
307
323
|
this.calculatePosition();
|
|
308
324
|
|
|
@@ -527,6 +543,12 @@ export class Options extends RapidElement {
|
|
|
527
543
|
}
|
|
528
544
|
}
|
|
529
545
|
|
|
546
|
+
// we need to swallow mouse down so we don't grab focus
|
|
547
|
+
private handleMouseDown(evt: MouseEvent) {
|
|
548
|
+
evt.preventDefault();
|
|
549
|
+
evt.stopPropagation();
|
|
550
|
+
}
|
|
551
|
+
|
|
530
552
|
private handleOptionClick(evt: MouseEvent) {
|
|
531
553
|
evt.preventDefault();
|
|
532
554
|
evt.stopPropagation();
|
|
@@ -572,7 +594,15 @@ export class Options extends RapidElement {
|
|
|
572
594
|
options: true,
|
|
573
595
|
});
|
|
574
596
|
|
|
575
|
-
|
|
597
|
+
let options = this.options || [];
|
|
598
|
+
if (
|
|
599
|
+
options.length == 0 &&
|
|
600
|
+
this.tempOptions &&
|
|
601
|
+
this.tempOptions.length > 0
|
|
602
|
+
) {
|
|
603
|
+
options = this.tempOptions;
|
|
604
|
+
}
|
|
605
|
+
|
|
576
606
|
return html`
|
|
577
607
|
<div class=${classes} style=${styleMap(containerStyle)}>
|
|
578
608
|
<div class="options-scroll" @scroll=${this.handleInnerScroll}>
|
|
@@ -582,6 +612,7 @@ export class Options extends RapidElement {
|
|
|
582
612
|
data-option-index="${index}"
|
|
583
613
|
@mousemove=${this.handleMouseMove}
|
|
584
614
|
@click=${this.handleOptionClick}
|
|
615
|
+
@mousedown=${this.handleMouseDown}
|
|
585
616
|
class="option ${index === this.cursorIndex ? 'focused' : ''}"
|
|
586
617
|
>
|
|
587
618
|
${this.resolvedRenderOption(option, index === this.cursorIndex)}
|