@nyaruka/temba-components 0.52.2 → 0.53.0
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 +9 -0
- package/dist/{8cd40058.js → 0fd963e1.js} +255 -199
- package/dist/index.js +255 -199
- package/dist/static/svg/index.svg +1 -1
- 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/button/Button.js +3 -0
- package/out-tsc/src/button/Button.js.map +1 -1
- package/out-tsc/src/dialog/Dialog.js +69 -22
- package/out-tsc/src/dialog/Dialog.js.map +1 -1
- package/out-tsc/src/dialog/Modax.js +118 -21
- package/out-tsc/src/dialog/Modax.js.map +1 -1
- package/out-tsc/src/utils/index.js +1 -0
- package/out-tsc/src/utils/index.js.map +1 -1
- package/out-tsc/src/vectoricon/index.js +2 -1
- package/out-tsc/src/vectoricon/index.js.map +1 -1
- package/out-tsc/test/temba-modax.test.js +2 -2
- package/out-tsc/test/temba-modax.test.js.map +1 -1
- package/package.json +1 -1
- package/src/button/Button.ts +3 -0
- package/src/dialog/Dialog.ts +81 -25
- package/src/dialog/Modax.ts +126 -22
- package/src/utils/index.ts +1 -0
- package/src/vectoricon/index.ts +2 -1
- package/static/svg/index.svg +1 -1
- package/static/svg/work/traced/alarm-clock.svg +1 -0
- package/static/svg/work/used/alarm-clock.svg +3 -0
- package/test/temba-modax.test.ts +3 -3
package/src/dialog/Dialog.ts
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
import { property } from 'lit/decorators.js';
|
|
2
|
-
import { TemplateResult, html, css } from 'lit';
|
|
2
|
+
import { TemplateResult, html, css, PropertyValueMap } from 'lit';
|
|
3
3
|
import { Button } from '../button/Button';
|
|
4
4
|
import { RapidElement } from '../RapidElement';
|
|
5
5
|
import { CustomEventType } from '../interfaces';
|
|
6
6
|
import { styleMap } from 'lit-html/directives/style-map.js';
|
|
7
7
|
import { getClasses } from '../utils';
|
|
8
8
|
|
|
9
|
+
export enum ButtonType {
|
|
10
|
+
PRIMARY = 'primary',
|
|
11
|
+
SECONDARY = 'secondary',
|
|
12
|
+
DESTRUCTIVE = 'destructive',
|
|
13
|
+
}
|
|
14
|
+
export class DialogButton {
|
|
15
|
+
name?: string;
|
|
16
|
+
id?: string;
|
|
17
|
+
details?: any;
|
|
18
|
+
type?: string;
|
|
19
|
+
closes?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
9
22
|
export class Dialog extends RapidElement {
|
|
10
23
|
static get widths(): { [size: string]: string } {
|
|
11
24
|
return {
|
|
@@ -24,6 +37,10 @@ export class Dialog extends RapidElement {
|
|
|
24
37
|
background: white;
|
|
25
38
|
}
|
|
26
39
|
|
|
40
|
+
.flex-grow {
|
|
41
|
+
flex-grow: 1;
|
|
42
|
+
}
|
|
43
|
+
|
|
27
44
|
.flex {
|
|
28
45
|
display: flex;
|
|
29
46
|
flex-direction: column;
|
|
@@ -99,6 +116,9 @@ export class Dialog extends RapidElement {
|
|
|
99
116
|
}
|
|
100
117
|
|
|
101
118
|
.header-text {
|
|
119
|
+
display: flex;
|
|
120
|
+
flex-direction: row;
|
|
121
|
+
align-items: center;
|
|
102
122
|
font-size: 20px;
|
|
103
123
|
padding: 12px 20px;
|
|
104
124
|
font-weight: 300;
|
|
@@ -106,11 +126,21 @@ export class Dialog extends RapidElement {
|
|
|
106
126
|
background: var(--header-bg);
|
|
107
127
|
}
|
|
108
128
|
|
|
129
|
+
.header-text .title {
|
|
130
|
+
flex-grow: 1;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.header-text .status {
|
|
134
|
+
font-size: 0.6em;
|
|
135
|
+
font-weight: bold;
|
|
136
|
+
}
|
|
137
|
+
|
|
109
138
|
.dialog-footer {
|
|
110
139
|
background: var(--color-primary-light);
|
|
111
140
|
padding: 10px;
|
|
112
141
|
display: flex;
|
|
113
|
-
flex-flow: row
|
|
142
|
+
flex-flow: row;
|
|
143
|
+
align-items: center;
|
|
114
144
|
}
|
|
115
145
|
|
|
116
146
|
temba-button {
|
|
@@ -198,6 +228,9 @@ export class Dialog extends RapidElement {
|
|
|
198
228
|
@property()
|
|
199
229
|
ready: boolean;
|
|
200
230
|
|
|
231
|
+
@property({ type: Array })
|
|
232
|
+
buttons: DialogButton[] = [];
|
|
233
|
+
|
|
201
234
|
@property({ attribute: false })
|
|
202
235
|
onButtonClicked: (button: Button) => void;
|
|
203
236
|
|
|
@@ -207,6 +240,25 @@ export class Dialog extends RapidElement {
|
|
|
207
240
|
super();
|
|
208
241
|
}
|
|
209
242
|
|
|
243
|
+
protected firstUpdated(
|
|
244
|
+
changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
|
|
245
|
+
): void {
|
|
246
|
+
if (changes.has('cancelButtonName') && this.cancelButtonName) {
|
|
247
|
+
this.buttons.push({
|
|
248
|
+
name: this.cancelButtonName,
|
|
249
|
+
type: ButtonType.SECONDARY,
|
|
250
|
+
closes: true,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (changes.has('primaryButtonName') && this.primaryButtonName) {
|
|
255
|
+
this.buttons.push({
|
|
256
|
+
name: this.primaryButtonName,
|
|
257
|
+
type: ButtonType.PRIMARY,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
210
262
|
public updated(changedProperties: Map<string, any>) {
|
|
211
263
|
if (changedProperties.has('open')) {
|
|
212
264
|
const body = document.querySelector('body');
|
|
@@ -270,8 +322,13 @@ export class Dialog extends RapidElement {
|
|
|
270
322
|
public handleClick(evt: MouseEvent) {
|
|
271
323
|
const button = evt.currentTarget as Button;
|
|
272
324
|
if (!button.disabled) {
|
|
273
|
-
|
|
274
|
-
if (button.
|
|
325
|
+
let detail: DialogButton = {};
|
|
326
|
+
if (button.index >= 0 && button.index < this.buttons.length) {
|
|
327
|
+
detail = this.buttons[button.index];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
this.fireCustomEvent(CustomEventType.ButtonClicked, { button, detail });
|
|
331
|
+
if (button.name === this.cancelButtonName || (detail && detail.closes)) {
|
|
275
332
|
this.open = false;
|
|
276
333
|
}
|
|
277
334
|
}
|
|
@@ -343,7 +400,9 @@ export class Dialog extends RapidElement {
|
|
|
343
400
|
const header = this.header
|
|
344
401
|
? html`
|
|
345
402
|
<div class="dialog-header">
|
|
346
|
-
<div class="header-text"
|
|
403
|
+
<div class="header-text">
|
|
404
|
+
<div class="title">${this.header}</div>
|
|
405
|
+
</div>
|
|
347
406
|
</div>
|
|
348
407
|
`
|
|
349
408
|
: null;
|
|
@@ -382,26 +441,23 @@ export class Dialog extends RapidElement {
|
|
|
382
441
|
</div>
|
|
383
442
|
|
|
384
443
|
<div class="dialog-footer">
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
name=${this.cancelButtonName}
|
|
403
|
-
secondary
|
|
404
|
-
></temba-button>
|
|
444
|
+
<div class="flex-grow">
|
|
445
|
+
<slot name="gutter"></slot>
|
|
446
|
+
</div>
|
|
447
|
+
${this.buttons.map(
|
|
448
|
+
(button: DialogButton, index) => html`
|
|
449
|
+
<temba-button
|
|
450
|
+
name=${button.name}
|
|
451
|
+
?destructive=${button.type == 'primary' && this.destructive}
|
|
452
|
+
?primary=${button.type == 'primary' && !this.destructive}
|
|
453
|
+
?secondary=${button.type == 'secondary'}
|
|
454
|
+
?submitting=${this.submitting}
|
|
455
|
+
?disabled=${this.disabled}
|
|
456
|
+
index=${index}
|
|
457
|
+
@click=${this.handleClick}
|
|
458
|
+
></temba-button>
|
|
459
|
+
`
|
|
460
|
+
)}
|
|
405
461
|
</div>
|
|
406
462
|
</div>
|
|
407
463
|
<div class="grow-bottom"></div>
|
package/src/dialog/Modax.ts
CHANGED
|
@@ -3,9 +3,9 @@ import { property } from 'lit/decorators.js';
|
|
|
3
3
|
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
|
4
4
|
|
|
5
5
|
import { RapidElement } from '../RapidElement';
|
|
6
|
-
import { getUrl, serialize, postUrl, WebResponse } from '../utils';
|
|
6
|
+
import { getUrl, serialize, postUrl, WebResponse, getClasses } from '../utils';
|
|
7
7
|
import { CustomEventType } from '../interfaces';
|
|
8
|
-
import { Dialog } from './Dialog';
|
|
8
|
+
import { ButtonType, Dialog, DialogButton } from './Dialog';
|
|
9
9
|
|
|
10
10
|
export class Modax extends RapidElement {
|
|
11
11
|
static get styles() {
|
|
@@ -24,6 +24,11 @@ export class Modax extends RapidElement {
|
|
|
24
24
|
display: none;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
button[type='submit'],
|
|
28
|
+
input[type='submit'] {
|
|
29
|
+
display: none;
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
.modax-body {
|
|
28
33
|
padding: 20px;
|
|
29
34
|
display: block;
|
|
@@ -67,6 +72,33 @@ export class Modax extends RapidElement {
|
|
|
67
72
|
border-radius: 6px;
|
|
68
73
|
font-weight: 300;
|
|
69
74
|
}
|
|
75
|
+
|
|
76
|
+
.step-ball {
|
|
77
|
+
background: rgba(var(--primary-rgb), 0.2);
|
|
78
|
+
width: 1.2em;
|
|
79
|
+
height: 1.2em;
|
|
80
|
+
border-radius: 100%;
|
|
81
|
+
margin-right: 0.5em;
|
|
82
|
+
border: 0.15em solid transparent;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.step-ball.complete {
|
|
86
|
+
background: rgba(var(--primary-rgb), 0.7);
|
|
87
|
+
cursor: pointer;
|
|
88
|
+
}
|
|
89
|
+
.step-ball.complete:hover {
|
|
90
|
+
background: rgba(var(--primary-rgb), 0.8);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.step-ball.active {
|
|
94
|
+
border: 0.15em solid var(--color-primary-dark);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.wizard-steps {
|
|
98
|
+
display: flex;
|
|
99
|
+
flex-direction: row;
|
|
100
|
+
margin-left: 0.6em;
|
|
101
|
+
}
|
|
70
102
|
`;
|
|
71
103
|
}
|
|
72
104
|
|
|
@@ -106,6 +138,15 @@ export class Modax extends RapidElement {
|
|
|
106
138
|
@property({ type: Boolean })
|
|
107
139
|
disabled = false;
|
|
108
140
|
|
|
141
|
+
@property({ type: Array })
|
|
142
|
+
buttons: DialogButton[] = [];
|
|
143
|
+
|
|
144
|
+
@property({ type: Number })
|
|
145
|
+
wizardStep = 0;
|
|
146
|
+
|
|
147
|
+
@property({ type: Number })
|
|
148
|
+
wizardStepCount = 0;
|
|
149
|
+
|
|
109
150
|
@property({ type: Boolean })
|
|
110
151
|
suspendSubmit = false;
|
|
111
152
|
// private cancelToken: CancelTokenSource;
|
|
@@ -149,18 +190,29 @@ export class Modax extends RapidElement {
|
|
|
149
190
|
}
|
|
150
191
|
|
|
151
192
|
public updatePrimaryButton(): void {
|
|
193
|
+
const wizard = this.shadowRoot.querySelector(
|
|
194
|
+
'#wizard-form'
|
|
195
|
+
) as HTMLDivElement;
|
|
196
|
+
if (wizard) {
|
|
197
|
+
this.wizardStep = parseInt(wizard.dataset.step);
|
|
198
|
+
this.wizardStepCount = parseInt(wizard.dataset.steps);
|
|
199
|
+
}
|
|
200
|
+
|
|
152
201
|
if (!this.noSubmit) {
|
|
153
202
|
this.updateComplete.then(() => {
|
|
154
203
|
const submitButton = this.shadowRoot.querySelector(
|
|
155
|
-
"input[type='submit']"
|
|
204
|
+
"input[type='submit'],button[type='submit']"
|
|
156
205
|
) as any;
|
|
157
206
|
|
|
158
207
|
if (submitButton) {
|
|
159
|
-
this.
|
|
160
|
-
|
|
208
|
+
this.buttons = [
|
|
209
|
+
{ type: ButtonType.SECONDARY, name: 'Cancel', closes: true },
|
|
210
|
+
{ type: ButtonType.PRIMARY, name: submitButton.value },
|
|
211
|
+
];
|
|
161
212
|
} else {
|
|
162
|
-
this.
|
|
163
|
-
|
|
213
|
+
this.buttons = [
|
|
214
|
+
{ type: ButtonType.SECONDARY, name: 'Ok', closes: true },
|
|
215
|
+
];
|
|
164
216
|
}
|
|
165
217
|
this.submitting = false;
|
|
166
218
|
});
|
|
@@ -229,22 +281,37 @@ export class Modax extends RapidElement {
|
|
|
229
281
|
this.body = this.getLoading();
|
|
230
282
|
getUrl(this.endpoint, null, this.getHeaders()).then(
|
|
231
283
|
(response: WebResponse) => {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
this.
|
|
237
|
-
|
|
284
|
+
// if it's a full page, breakout of the modal
|
|
285
|
+
if (response.body.indexOf('<!DOCTYPE HTML>') == 0) {
|
|
286
|
+
document.location = response.url;
|
|
287
|
+
} else {
|
|
288
|
+
this.setBody(response.body);
|
|
289
|
+
this.fetching = false;
|
|
290
|
+
this.updateComplete.then(() => {
|
|
291
|
+
this.updatePrimaryButton();
|
|
292
|
+
this.fireCustomEvent(CustomEventType.Loaded, {
|
|
293
|
+
body: this.getBody(),
|
|
294
|
+
});
|
|
238
295
|
});
|
|
239
|
-
}
|
|
296
|
+
}
|
|
240
297
|
}
|
|
241
298
|
);
|
|
242
299
|
}
|
|
243
300
|
|
|
244
|
-
public submit(): void {
|
|
301
|
+
public submit(extra = {}): void {
|
|
245
302
|
this.submitting = true;
|
|
246
303
|
const form = this.shadowRoot.querySelector('form');
|
|
247
|
-
|
|
304
|
+
|
|
305
|
+
let postData = form ? serialize(form) : '';
|
|
306
|
+
if (extra) {
|
|
307
|
+
Object.keys(extra).forEach(key => {
|
|
308
|
+
postData +=
|
|
309
|
+
(postData.length > 1 ? '&' : '') +
|
|
310
|
+
encodeURIComponent(key) +
|
|
311
|
+
'=' +
|
|
312
|
+
encodeURIComponent(extra[key]);
|
|
313
|
+
});
|
|
314
|
+
}
|
|
248
315
|
|
|
249
316
|
postUrl(
|
|
250
317
|
this.endpoint,
|
|
@@ -278,7 +345,9 @@ export class Modax extends RapidElement {
|
|
|
278
345
|
} else {
|
|
279
346
|
// if we set the body, update our submit button
|
|
280
347
|
if (this.setBody(response.body)) {
|
|
281
|
-
this.
|
|
348
|
+
this.updateComplete.then(() => {
|
|
349
|
+
this.updatePrimaryButton();
|
|
350
|
+
});
|
|
282
351
|
}
|
|
283
352
|
}
|
|
284
353
|
}, 1000);
|
|
@@ -290,15 +359,16 @@ export class Modax extends RapidElement {
|
|
|
290
359
|
|
|
291
360
|
private handleDialogClick(evt: CustomEvent) {
|
|
292
361
|
const button = evt.detail.button;
|
|
362
|
+
const detail = evt.detail.detail;
|
|
293
363
|
if (!button.disabled && !button.submitting) {
|
|
294
|
-
if (button.
|
|
364
|
+
if (button.primary || button.destructive) {
|
|
295
365
|
if (!this.suspendSubmit) {
|
|
296
366
|
this.submit();
|
|
297
367
|
}
|
|
298
368
|
}
|
|
299
369
|
}
|
|
300
370
|
|
|
301
|
-
if (
|
|
371
|
+
if (detail.closes) {
|
|
302
372
|
this.open = false;
|
|
303
373
|
this.fetching = false;
|
|
304
374
|
this.cancelName = undefined;
|
|
@@ -315,16 +385,47 @@ export class Modax extends RapidElement {
|
|
|
315
385
|
return this.endpoint && this.endpoint.indexOf('delete') > -1;
|
|
316
386
|
}
|
|
317
387
|
|
|
388
|
+
private handleGotoStep(evt: MouseEvent) {
|
|
389
|
+
const step = (evt.target as HTMLDivElement).dataset.gotoStep;
|
|
390
|
+
if (step) {
|
|
391
|
+
this.submit({ wizard_goto_step: step });
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
318
395
|
public getBody() {
|
|
319
396
|
return this.shadowRoot.querySelector('.modax-body');
|
|
320
397
|
}
|
|
321
398
|
|
|
322
399
|
public render(): TemplateResult {
|
|
400
|
+
const wizardStepBalls = [];
|
|
401
|
+
|
|
402
|
+
const wizard = this.shadowRoot.querySelector(
|
|
403
|
+
'#wizard-form'
|
|
404
|
+
) as HTMLDivElement;
|
|
405
|
+
if (wizard) {
|
|
406
|
+
const completed = (wizard.getAttribute('data-completed') || '')
|
|
407
|
+
.split(',')
|
|
408
|
+
.filter(step => step.length > 0);
|
|
409
|
+
|
|
410
|
+
for (let i = 0; i < this.wizardStepCount; i++) {
|
|
411
|
+
wizardStepBalls.push(
|
|
412
|
+
html`<div
|
|
413
|
+
data-goto-step=${completed[i]}
|
|
414
|
+
@click=${this.handleGotoStep.bind(this)}
|
|
415
|
+
class="${getClasses({
|
|
416
|
+
'step-ball': true,
|
|
417
|
+
active: this.wizardStep - 1 === i,
|
|
418
|
+
complete: i < completed.length,
|
|
419
|
+
})}"
|
|
420
|
+
></div>`
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
323
425
|
return html`
|
|
324
426
|
<temba-dialog
|
|
325
|
-
header=${this.header}
|
|
326
|
-
.
|
|
327
|
-
.cancelButtonName=${this.cancelName || 'Cancel'}
|
|
427
|
+
.header=${this.header}
|
|
428
|
+
.buttons=${this.buttons}
|
|
328
429
|
?open=${this.open}
|
|
329
430
|
?loading=${this.fetching}
|
|
330
431
|
?submitting=${this.submitting}
|
|
@@ -338,6 +439,9 @@ export class Modax extends RapidElement {
|
|
|
338
439
|
${this.body}
|
|
339
440
|
</div>
|
|
340
441
|
<div class="scripts"></div>
|
|
442
|
+
<div slot="gutter">
|
|
443
|
+
<div class="wizard-steps">${wizardStepBalls}</div>
|
|
444
|
+
</div>
|
|
341
445
|
</temba-dialog>
|
|
342
446
|
<div class="slot-wrapper" @click=${this.handleSlotClicked}>
|
|
343
447
|
<slot></slot>
|
package/src/utils/index.ts
CHANGED
package/src/vectoricon/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// for cache busting we dynamically generate a fingerprint, use yarn svg to update
|
|
2
|
-
export const SVG_FINGERPRINT = '
|
|
2
|
+
export const SVG_FINGERPRINT = '5d22cc3a5fb06da9ea0a632539b39983';
|
|
3
3
|
|
|
4
4
|
// only icons below are included in the sprite sheet
|
|
5
5
|
export enum Icon {
|
|
@@ -116,6 +116,7 @@ export enum Icon {
|
|
|
116
116
|
missing = 'maximize-02',
|
|
117
117
|
missed_call = 'phone-x',
|
|
118
118
|
new = 'plus',
|
|
119
|
+
next_schedule = 'alarm-clock',
|
|
119
120
|
notification = 'bell-01',
|
|
120
121
|
org_active = 'credit-card-02',
|
|
121
122
|
org_anonymous = 'glasses-01',
|