@nyaruka/temba-components 0.19.0 → 0.23.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/.eslintrc.js +1 -0
- package/CHANGELOG.md +28 -0
- package/dist/28f45617.js +356 -0
- package/dist/index.js +356 -1
- package/dist/static/icons/symbol-defs.svg +56 -20
- 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/anchor/Anchor.js +25 -0
- package/out-tsc/src/anchor/Anchor.js.map +1 -0
- package/out-tsc/src/contacts/ContactDetails.js +9 -5
- package/out-tsc/src/contacts/ContactDetails.js.map +1 -1
- package/out-tsc/src/contacts/ContactHistory.js +1 -5
- package/out-tsc/src/contacts/ContactHistory.js.map +1 -1
- package/out-tsc/src/contacts/events.js +33 -7
- package/out-tsc/src/contacts/events.js.map +1 -1
- package/out-tsc/src/dialog/Modax.js +11 -2
- package/out-tsc/src/dialog/Modax.js.map +1 -1
- package/out-tsc/src/list/TembaMenu.js +384 -81
- package/out-tsc/src/list/TembaMenu.js.map +1 -1
- package/out-tsc/src/utils/index.js +13 -14
- package/out-tsc/src/utils/index.js.map +1 -1
- package/out-tsc/src/vectoricon/VectorIcon.js +2 -1
- package/out-tsc/src/vectoricon/VectorIcon.js.map +1 -1
- package/out-tsc/temba-modules.js +2 -0
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/temba-menu.test.js +0 -13
- package/out-tsc/test/temba-menu.test.js.map +1 -1
- package/package.json +4 -4
- package/screenshots/truth/contacts/history-expanded.png +0 -0
- package/screenshots/truth/list/items-selected.png +0 -0
- package/screenshots/truth/list/items-updated.png +0 -0
- package/screenshots/truth/list/items.png +0 -0
- package/screenshots/truth/list/menu-submenu.png +0 -0
- package/src/anchor/Anchor.ts +26 -0
- package/src/contacts/ContactDetails.ts +9 -5
- package/src/contacts/ContactHistory.ts +0 -4
- package/src/contacts/events.ts +33 -7
- package/src/dialog/Modax.ts +11 -2
- package/src/list/TembaMenu.ts +424 -93
- package/src/utils/index.ts +17 -16
- package/src/vectoricon/VectorIcon.ts +2 -1
- package/static/icons/Read Me.txt +1 -1
- package/static/icons/SVG/channel.svg +5 -0
- package/static/icons/SVG/cloud1.svg +5 -0
- package/static/icons/SVG/codepen.svg +5 -0
- package/static/icons/SVG/codesandbox.svg +5 -0
- package/static/icons/SVG/git-pull-request.svg +5 -0
- package/static/icons/SVG/grid.svg +5 -0
- package/static/icons/SVG/hard-drive.svg +5 -0
- package/static/icons/SVG/layout.svg +5 -0
- package/static/icons/SVG/list.svg +5 -0
- package/static/icons/SVG/map-pin.svg +5 -0
- package/static/icons/SVG/menu.svg +5 -0
- package/static/icons/SVG/package.svg +5 -0
- package/static/icons/SVG/zapier.svg +5 -0
- package/static/icons/demo-external-svg.html +232 -172
- package/static/icons/demo-files/demo.css +4 -4
- package/static/icons/demo.html +288 -192
- package/static/icons/selection.json +646 -345
- package/static/icons/style.css +4 -4
- package/static/icons/symbol-defs.svg +56 -20
- package/temba-modules.ts +2 -0
- package/test/temba-menu.test.ts +0 -16
- package/test-assets/style.css +1 -1
- package/dist/b10b5805.js +0 -1
- package/static/icons/SVG/zendesk1.svg +0 -5
package/src/list/TembaMenu.ts
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import { css, html, property, TemplateResult } from 'lit-element';
|
|
2
2
|
import { CustomEventType } from '../interfaces';
|
|
3
3
|
import { RapidElement } from '../RapidElement';
|
|
4
|
-
import {
|
|
5
|
-
COOKIE_KEYS,
|
|
6
|
-
fetchResults,
|
|
7
|
-
getCookieBoolean,
|
|
8
|
-
setCookie,
|
|
9
|
-
} from '../utils';
|
|
4
|
+
import { fetchResults, getClasses } from '../utils';
|
|
10
5
|
|
|
11
6
|
export interface MenuItem {
|
|
12
7
|
id?: string;
|
|
8
|
+
vanity_id?: string;
|
|
13
9
|
name?: string;
|
|
14
10
|
count?: number;
|
|
15
11
|
icon?: string;
|
|
12
|
+
collapsed_icon?: string;
|
|
16
13
|
endpoint?: string;
|
|
17
14
|
loading?: boolean;
|
|
18
15
|
bottom?: boolean;
|
|
@@ -21,8 +18,18 @@ export interface MenuItem {
|
|
|
21
18
|
href?: string;
|
|
22
19
|
items?: MenuItem[];
|
|
23
20
|
inline?: boolean;
|
|
21
|
+
type?: string;
|
|
24
22
|
}
|
|
25
23
|
|
|
24
|
+
interface MenuItemState {
|
|
25
|
+
collapsed?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const findItem = (items: MenuItem[], id: string) =>
|
|
29
|
+
(items || []).find((item: MenuItem) => {
|
|
30
|
+
return item.id == id || item.vanity_id == id;
|
|
31
|
+
});
|
|
32
|
+
|
|
26
33
|
export class TembaMenu extends RapidElement {
|
|
27
34
|
static get styles() {
|
|
28
35
|
return css`
|
|
@@ -58,6 +65,7 @@ export class TembaMenu extends RapidElement {
|
|
|
58
65
|
-webkit-user-select: none;
|
|
59
66
|
display: flex;
|
|
60
67
|
font-size: 1.15em;
|
|
68
|
+
--icon-color: var(--color-text-dark);
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
.item.selected {
|
|
@@ -106,8 +114,8 @@ export class TembaMenu extends RapidElement {
|
|
|
106
114
|
background: var(--color-primary-dark);
|
|
107
115
|
}
|
|
108
116
|
|
|
109
|
-
.level-0 > .item > .
|
|
110
|
-
display: none;
|
|
117
|
+
.level-0 > .item > .details {
|
|
118
|
+
display: none !important;
|
|
111
119
|
}
|
|
112
120
|
|
|
113
121
|
.level-0.expanding {
|
|
@@ -135,23 +143,33 @@ export class TembaMenu extends RapidElement {
|
|
|
135
143
|
margin-top: 0.1em;
|
|
136
144
|
border-radius: var(--curvature);
|
|
137
145
|
display: flex;
|
|
146
|
+
|
|
147
|
+
min-width: 12em;
|
|
148
|
+
max-width: 12em;
|
|
138
149
|
}
|
|
139
150
|
|
|
140
151
|
.item > temba-icon {
|
|
141
152
|
margin-right: 0.5em;
|
|
142
153
|
}
|
|
143
154
|
|
|
144
|
-
.item >
|
|
155
|
+
.item.inline > temba-icon {
|
|
156
|
+
// margin-right: 0em;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.item > .details > .name {
|
|
145
160
|
flex-grow: 1;
|
|
146
161
|
white-space: nowrap;
|
|
147
162
|
overflow: hidden;
|
|
148
163
|
text-overflow: ellipsis;
|
|
164
|
+
width: 0;
|
|
149
165
|
}
|
|
150
166
|
|
|
151
167
|
.level-0 > .item {
|
|
152
168
|
padding: 1em 1em;
|
|
153
169
|
margin-top: 0em;
|
|
154
170
|
border-radius: 0px;
|
|
171
|
+
min-width: inherit;
|
|
172
|
+
max-width: inherit;
|
|
155
173
|
}
|
|
156
174
|
|
|
157
175
|
.level-0 > .item > temba-icon {
|
|
@@ -199,19 +217,175 @@ export class TembaMenu extends RapidElement {
|
|
|
199
217
|
}
|
|
200
218
|
|
|
201
219
|
.inline-children {
|
|
202
|
-
|
|
220
|
+
// background: #ffffff;
|
|
221
|
+
padding: 0.5em;
|
|
222
|
+
border-bottom-right-radius: var(--curvature);
|
|
223
|
+
border-bottom-left-radius: var(--curvature);
|
|
224
|
+
font-size: 1rem;
|
|
225
|
+
margin-bottom: 0.75em;
|
|
226
|
+
border: 1px solid #f3f3f3;
|
|
227
|
+
// box-shadow: var(--shadow);
|
|
228
|
+
// margin-top: -1px;
|
|
229
|
+
z-index: 1000;
|
|
230
|
+
// margin-left: 1em;
|
|
231
|
+
border-top: none;
|
|
203
232
|
}
|
|
204
233
|
|
|
205
|
-
.
|
|
206
|
-
|
|
234
|
+
.inline-children .item {
|
|
235
|
+
max-width: 11em !important;
|
|
236
|
+
min-width: 11em !important;
|
|
237
|
+
// border: 1px solid #f1f1f1;
|
|
238
|
+
// margin-top: 0.75em;
|
|
239
|
+
// margin-right: -1em;
|
|
240
|
+
// padding-right: 0;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.item.inline {
|
|
244
|
+
border: 0px solid transparent;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.item.inline.child-selected,
|
|
248
|
+
.item.inline.selected {
|
|
249
|
+
background: #f3f3f3;
|
|
250
|
+
border: 0px solid #f1f1f1;
|
|
251
|
+
border-bottom-right-radius: 0px !important;
|
|
252
|
+
border-bottom-left-radius: 0px !important;
|
|
253
|
+
z-index: 1000;
|
|
254
|
+
color: #444;
|
|
255
|
+
--icon-color: #444;
|
|
256
|
+
// box-shadow: var(--shadow);
|
|
207
257
|
}
|
|
208
258
|
|
|
209
|
-
.
|
|
259
|
+
.level-1,
|
|
260
|
+
.level-2 {
|
|
261
|
+
border-right: 1px solid rgba(0 0 0 / 8%);
|
|
262
|
+
box-shadow: rgb(0 0 0 / 6%) 4px 0px 6px 1px;
|
|
210
263
|
}
|
|
211
264
|
|
|
212
265
|
.level-1 {
|
|
213
266
|
overflow-y: auto;
|
|
214
|
-
|
|
267
|
+
z-index: 1500;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.level-2 {
|
|
271
|
+
background: #fbfbfb;
|
|
272
|
+
overflow-y: auto;
|
|
273
|
+
z-index: 1000;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.level-2 .item .details {
|
|
277
|
+
overflow: hidden;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.level-2 .item {
|
|
281
|
+
min-width: 12em;
|
|
282
|
+
max-width: 12em;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.level-1 .item {
|
|
286
|
+
overflow: hidden;
|
|
287
|
+
max-width: 12em;
|
|
288
|
+
min-width: 12em;
|
|
289
|
+
min-height: 1.5em;
|
|
290
|
+
max-height: 1.5em;
|
|
291
|
+
transition: min-width var(--transition-speed) !important;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.level-1 .item .details {
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.collapsed .item {
|
|
298
|
+
overflow: hidden;
|
|
299
|
+
min-width: 0;
|
|
300
|
+
margin: 0;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.item .details {
|
|
304
|
+
opacity: 1;
|
|
305
|
+
min-height: 1.5em;
|
|
306
|
+
max-height: 1.5em;
|
|
307
|
+
align-items: center;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.item .details .name {
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.item temba-icon {
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.collapsed .item {
|
|
317
|
+
margin-bottom: 0.5em;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.collapsed .item .details {
|
|
321
|
+
overflow: hidden;
|
|
322
|
+
max-height: 0em;
|
|
323
|
+
max-width: 0em;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.collapsed .item .details {
|
|
327
|
+
max-height: 0em;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.collapsed .item temba-icon {
|
|
331
|
+
margin-right: 0;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.section {
|
|
335
|
+
max-width: 12em;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.collapsed .section {
|
|
339
|
+
opacity: 0;
|
|
340
|
+
max-width: 0em;
|
|
341
|
+
max-height: 0.6em;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.collapsed.level-1 {
|
|
345
|
+
overflow: hidden;
|
|
346
|
+
padding: 0.5em;
|
|
347
|
+
--icon-color: #999;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.collapsed .item .right {
|
|
351
|
+
flex-grow: 1;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.collapse-icon {
|
|
355
|
+
display: none;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.collapsed .collapse-icon {
|
|
359
|
+
--icon-color: #ccc;
|
|
360
|
+
display: block;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.collapsed .item.iconless {
|
|
364
|
+
max-height: 0em;
|
|
365
|
+
padding: 0em;
|
|
366
|
+
min-height: 0em;
|
|
367
|
+
margin-bottom: 0em;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.divider {
|
|
371
|
+
height: 1px;
|
|
372
|
+
background: #f3f3f3;
|
|
373
|
+
margin: 0.5em 0.75em;
|
|
374
|
+
min-height: 1px;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.collapsed .divider {
|
|
378
|
+
height: 0;
|
|
379
|
+
margin: 0;
|
|
380
|
+
padding: 0;
|
|
381
|
+
min-height: 0px;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.sub-section {
|
|
385
|
+
font-size: 1.1rem;
|
|
386
|
+
color: #888;
|
|
387
|
+
margin-top: 1rem;
|
|
388
|
+
margin-left: 0.3rem;
|
|
215
389
|
}
|
|
216
390
|
`;
|
|
217
391
|
}
|
|
@@ -219,12 +393,6 @@ export class TembaMenu extends RapidElement {
|
|
|
219
393
|
@property({ type: Boolean })
|
|
220
394
|
wraps = false;
|
|
221
395
|
|
|
222
|
-
@property({ type: Boolean })
|
|
223
|
-
collapsible = false;
|
|
224
|
-
|
|
225
|
-
@property({ type: Boolean })
|
|
226
|
-
collapsed: boolean;
|
|
227
|
-
|
|
228
396
|
@property({ type: Boolean })
|
|
229
397
|
wait: boolean;
|
|
230
398
|
|
|
@@ -247,10 +415,22 @@ export class TembaMenu extends RapidElement {
|
|
|
247
415
|
root: MenuItem;
|
|
248
416
|
selection: string[] = [];
|
|
249
417
|
pending: string[] = [];
|
|
418
|
+
state: { [id: string]: MenuItemState } = {};
|
|
250
419
|
|
|
251
420
|
constructor() {
|
|
252
421
|
super();
|
|
253
|
-
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
private getMenuItemState(id: string): MenuItemState {
|
|
425
|
+
let itemState = {};
|
|
426
|
+
if (id) {
|
|
427
|
+
itemState = this.state[id];
|
|
428
|
+
if (!itemState) {
|
|
429
|
+
itemState = {};
|
|
430
|
+
this.state[id] = itemState;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return itemState;
|
|
254
434
|
}
|
|
255
435
|
|
|
256
436
|
public updated(changes: Map<string, any>) {
|
|
@@ -281,19 +461,34 @@ export class TembaMenu extends RapidElement {
|
|
|
281
461
|
while (path.length > 0) {
|
|
282
462
|
const step = path.splice(0, 1)[0];
|
|
283
463
|
if (items) {
|
|
284
|
-
item = items
|
|
464
|
+
item = findItem(items, step);
|
|
285
465
|
if (item) {
|
|
286
466
|
if (item.endpoint) {
|
|
287
467
|
item.loading = true;
|
|
288
468
|
const itemToUpdate = item;
|
|
289
469
|
fetchResults(itemToUpdate.endpoint).then((updated: MenuItem[]) => {
|
|
290
|
-
// for now we only deal with updating counts
|
|
291
|
-
(itemToUpdate.items || []).forEach(
|
|
292
|
-
|
|
293
|
-
updatedItem
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
470
|
+
// for now we only deal with updating counts and names
|
|
471
|
+
(itemToUpdate.items || []).forEach(
|
|
472
|
+
(existing: MenuItem, index: number, items: []) => {
|
|
473
|
+
const updatedItem = findItem(updated, existing.id);
|
|
474
|
+
|
|
475
|
+
// we were removed!
|
|
476
|
+
if (!updatedItem) {
|
|
477
|
+
items.splice(index, 1);
|
|
478
|
+
|
|
479
|
+
if (
|
|
480
|
+
this.selection.length > 1 &&
|
|
481
|
+
this.selection[this.selection.length - 1] == existing.id
|
|
482
|
+
) {
|
|
483
|
+
this.selection.splice(this.selection.length - 1, 1);
|
|
484
|
+
this.clickItem(this.selection[this.selection.length - 1]);
|
|
485
|
+
}
|
|
486
|
+
} else {
|
|
487
|
+
existing.count = updatedItem.count;
|
|
488
|
+
existing.name = updatedItem.name;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
);
|
|
297
492
|
|
|
298
493
|
itemToUpdate.loading = false;
|
|
299
494
|
this.requestUpdate('root');
|
|
@@ -308,40 +503,72 @@ export class TembaMenu extends RapidElement {
|
|
|
308
503
|
}
|
|
309
504
|
}
|
|
310
505
|
|
|
506
|
+
private fireNoPath(missingId: string) {
|
|
507
|
+
const item = this.getMenuItem();
|
|
508
|
+
|
|
509
|
+
const details = {
|
|
510
|
+
item: item.id,
|
|
511
|
+
selection: '/' + this.selection.join('/'),
|
|
512
|
+
endpoint: item.endpoint,
|
|
513
|
+
path: missingId + '/' + this.pending.join('/') + document.location.search,
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
// remove any excess from our selection
|
|
517
|
+
const selection = this.selection.join('/');
|
|
518
|
+
selection.replace(details.path, '');
|
|
519
|
+
this.selection = selection.split('/');
|
|
520
|
+
|
|
521
|
+
this.fireCustomEvent(CustomEventType.NoPath, details);
|
|
522
|
+
this.pending = [];
|
|
523
|
+
this.requestUpdate('root');
|
|
524
|
+
}
|
|
525
|
+
|
|
311
526
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
312
|
-
private loadItems(item: MenuItem) {
|
|
527
|
+
private loadItems(item: MenuItem, selectFirst = true) {
|
|
313
528
|
if (item && item.endpoint) {
|
|
314
529
|
item.loading = true;
|
|
315
530
|
this.httpComplete = fetchResults(item.endpoint).then(
|
|
316
531
|
(items: MenuItem[]) => {
|
|
317
532
|
// update our item level
|
|
318
|
-
items.forEach(subItem =>
|
|
533
|
+
items.forEach(subItem => {
|
|
534
|
+
subItem.level = item.level + 1;
|
|
535
|
+
// if we came with preset items, set the level for them accordingly
|
|
536
|
+
if (subItem.items) {
|
|
537
|
+
subItem.items.forEach(
|
|
538
|
+
inlineItem => (inlineItem.level = item.level + 2)
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
|
|
319
543
|
item.items = items;
|
|
320
544
|
item.loading = false;
|
|
321
545
|
this.requestUpdate('root');
|
|
546
|
+
this.scrollSelectedIntoView();
|
|
322
547
|
if (this.pending && this.pending.length > 0) {
|
|
323
548
|
// auto select the next pending click
|
|
324
549
|
const nextId = this.pending.splice(0, 1)[0];
|
|
325
550
|
if (nextId && items.length > 0) {
|
|
326
|
-
const nextItem = items
|
|
551
|
+
const nextItem = findItem(items, nextId);
|
|
327
552
|
if (nextItem) {
|
|
328
553
|
this.handleItemClicked(null, nextItem);
|
|
329
554
|
} else {
|
|
330
|
-
this.
|
|
331
|
-
item: item.id,
|
|
332
|
-
endpoint: item.endpoint,
|
|
333
|
-
path: nextId + '/' + this.pending.join('/'),
|
|
334
|
-
});
|
|
555
|
+
this.fireNoPath(nextId);
|
|
335
556
|
}
|
|
336
557
|
}
|
|
337
558
|
} else {
|
|
338
559
|
// auto select the first item
|
|
339
560
|
if (
|
|
561
|
+
selectFirst &&
|
|
340
562
|
items.length > 0 &&
|
|
341
563
|
this.selection.length >= 1 &&
|
|
342
564
|
!item.inline
|
|
343
565
|
) {
|
|
344
|
-
|
|
566
|
+
for (const item of items) {
|
|
567
|
+
if (!item.type) {
|
|
568
|
+
this.handleItemClicked(null, item);
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
345
572
|
}
|
|
346
573
|
}
|
|
347
574
|
}
|
|
@@ -368,17 +595,17 @@ export class TembaMenu extends RapidElement {
|
|
|
368
595
|
|
|
369
596
|
// update our selection
|
|
370
597
|
if (menuItem.level >= this.selection.length) {
|
|
371
|
-
this.selection.push(menuItem.id);
|
|
598
|
+
this.selection.push(menuItem.vanity_id || menuItem.id);
|
|
372
599
|
} else {
|
|
373
600
|
this.selection.splice(
|
|
374
601
|
menuItem.level,
|
|
375
602
|
this.selection.length - menuItem.level,
|
|
376
|
-
menuItem.id
|
|
603
|
+
menuItem.vanity_id || menuItem.id
|
|
377
604
|
);
|
|
378
605
|
}
|
|
379
606
|
|
|
380
607
|
if (menuItem.endpoint) {
|
|
381
|
-
this.loadItems(menuItem);
|
|
608
|
+
this.loadItems(menuItem, !menuItem.href);
|
|
382
609
|
this.dispatchEvent(new Event('change'));
|
|
383
610
|
} else {
|
|
384
611
|
this.dispatchEvent(new Event('change'));
|
|
@@ -388,27 +615,43 @@ export class TembaMenu extends RapidElement {
|
|
|
388
615
|
const nextId = this.pending.splice(0, 1)[0];
|
|
389
616
|
const item = this.getMenuItem();
|
|
390
617
|
if (nextId && item && item.items && item.items.length > 0) {
|
|
391
|
-
const nextItem = item.items
|
|
618
|
+
const nextItem = findItem(item.items, nextId);
|
|
392
619
|
if (nextItem) {
|
|
393
620
|
this.handleItemClicked(null, nextItem);
|
|
394
621
|
}
|
|
622
|
+
} else {
|
|
623
|
+
this.fireNoPath(nextId);
|
|
395
624
|
}
|
|
396
625
|
}
|
|
397
|
-
|
|
398
|
-
this.pending = [];
|
|
399
626
|
this.requestUpdate('root');
|
|
400
627
|
}
|
|
401
628
|
}
|
|
402
629
|
}
|
|
403
630
|
|
|
404
|
-
public
|
|
631
|
+
public scrollSelectedIntoView() {
|
|
632
|
+
// makes sure we are scrolled into view
|
|
633
|
+
window.setTimeout(() => {
|
|
634
|
+
const eles = this.shadowRoot.querySelectorAll('.selected');
|
|
635
|
+
eles.forEach(ele => {
|
|
636
|
+
ele.scrollIntoView({ block: 'end', behavior: 'smooth' });
|
|
637
|
+
});
|
|
638
|
+
}, 0);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
public clickItem(id: string): boolean {
|
|
405
642
|
const path = [...this.selection];
|
|
406
643
|
path.splice(path.length - 1, 1, id);
|
|
407
644
|
const item = this.getMenuItemForSelection(path);
|
|
408
|
-
|
|
645
|
+
|
|
646
|
+
if (item) {
|
|
647
|
+
this.handleItemClicked(null, item);
|
|
648
|
+
this.scrollSelectedIntoView();
|
|
649
|
+
return true;
|
|
650
|
+
}
|
|
651
|
+
return false;
|
|
409
652
|
}
|
|
410
653
|
|
|
411
|
-
public getMenuItem() {
|
|
654
|
+
public getMenuItem(): MenuItem {
|
|
412
655
|
return this.getMenuItemForSelection([...this.selection]);
|
|
413
656
|
}
|
|
414
657
|
|
|
@@ -419,7 +662,7 @@ export class TembaMenu extends RapidElement {
|
|
|
419
662
|
while (path.length > 0) {
|
|
420
663
|
const step = path.splice(0, 1)[0];
|
|
421
664
|
if (items) {
|
|
422
|
-
item = items
|
|
665
|
+
item = findItem(items, step);
|
|
423
666
|
if (item) {
|
|
424
667
|
items = item.items;
|
|
425
668
|
} else {
|
|
@@ -447,19 +690,77 @@ export class TembaMenu extends RapidElement {
|
|
|
447
690
|
}
|
|
448
691
|
}
|
|
449
692
|
|
|
693
|
+
public setSelectionPath(path: string) {
|
|
694
|
+
const asPath = path.split('/').filter(step => !!step);
|
|
695
|
+
|
|
696
|
+
// first try to click in the current space
|
|
697
|
+
const clicked = this.clickItem(asPath[asPath.length - 1]);
|
|
698
|
+
|
|
699
|
+
if (!clicked) {
|
|
700
|
+
this.wait = true;
|
|
701
|
+
this.setSelection(asPath);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
public async setFocusedItem(path: string) {
|
|
706
|
+
const focusedPath = path.split('/').filter(step => !!step);
|
|
707
|
+
|
|
708
|
+
// if we don't match at the first level, we are a noop
|
|
709
|
+
if (focusedPath.length > 0) {
|
|
710
|
+
const rootItem = findItem(this.root.items, focusedPath[0]);
|
|
711
|
+
if (!rootItem) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
const newPath = [];
|
|
717
|
+
let level = this.root;
|
|
718
|
+
while (focusedPath.length > 0) {
|
|
719
|
+
const nextId = focusedPath.shift();
|
|
720
|
+
if (nextId) {
|
|
721
|
+
if (!level.items) {
|
|
722
|
+
this.loadItems(level, false);
|
|
723
|
+
await this.httpComplete;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
level = findItem(level.items, nextId);
|
|
727
|
+
if (!level) {
|
|
728
|
+
focusedPath.splice(0, focusedPath.length);
|
|
729
|
+
} else {
|
|
730
|
+
newPath.push(nextId);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
this.selection = newPath;
|
|
736
|
+
this.requestUpdate('root');
|
|
737
|
+
}
|
|
738
|
+
|
|
450
739
|
private isSelected(menuItem: MenuItem) {
|
|
451
740
|
if (menuItem.level < this.selection.length) {
|
|
452
|
-
|
|
741
|
+
const selected =
|
|
742
|
+
this.selection[menuItem.level] == (menuItem.vanity_id || menuItem.id);
|
|
743
|
+
return selected;
|
|
453
744
|
}
|
|
454
745
|
return false;
|
|
455
746
|
}
|
|
456
747
|
|
|
457
748
|
private isExpanded(menuItem: MenuItem) {
|
|
458
|
-
const expanded = !!this.selection.find(
|
|
749
|
+
const expanded = !!this.selection.find(
|
|
750
|
+
id => id === menuItem.vanity_id || menuItem.id
|
|
751
|
+
);
|
|
459
752
|
return expanded;
|
|
460
753
|
}
|
|
461
754
|
|
|
462
755
|
private renderMenuItem = (menuItem: MenuItem): TemplateResult => {
|
|
756
|
+
if (menuItem.type === 'divider') {
|
|
757
|
+
return html`<div class="divider"></div>`;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
if (menuItem.type === 'section') {
|
|
761
|
+
return html`<div class="sub-section">${menuItem.name}</div>`;
|
|
762
|
+
}
|
|
763
|
+
|
|
463
764
|
const isSelected = this.isSelected(menuItem);
|
|
464
765
|
const isChildSelected =
|
|
465
766
|
isSelected && this.selection.length > menuItem.level + 1;
|
|
@@ -471,44 +772,71 @@ export class TembaMenu extends RapidElement {
|
|
|
471
772
|
></temba-icon>`
|
|
472
773
|
: null;
|
|
473
774
|
|
|
775
|
+
const collapsedIcon = menuItem.collapsed_icon
|
|
776
|
+
? html`<temba-icon
|
|
777
|
+
size="${menuItem.level === 0 ? '1.5' : '1'}"
|
|
778
|
+
name="${menuItem.collapsed_icon}"
|
|
779
|
+
class="collapse-icon"
|
|
780
|
+
></temba-icon>`
|
|
781
|
+
: null;
|
|
782
|
+
|
|
783
|
+
const itemClasses = getClasses({
|
|
784
|
+
['menu-' + menuItem.id]: true,
|
|
785
|
+
'child-selected': isChildSelected,
|
|
786
|
+
selected: isSelected,
|
|
787
|
+
item: true,
|
|
788
|
+
inline: menuItem.inline,
|
|
789
|
+
expanding: this.expanding && this.expanding === menuItem.id,
|
|
790
|
+
expanded: this.isExpanded(menuItem),
|
|
791
|
+
iconless: !icon && !collapsedIcon,
|
|
792
|
+
});
|
|
793
|
+
|
|
474
794
|
const item = html` <div
|
|
475
|
-
class="item-top ${isSelected ? 'selected' : null}"
|
|
795
|
+
class="item-top ${isSelected ? 'selected' : null} "
|
|
476
796
|
></div>
|
|
477
797
|
|
|
478
798
|
<div
|
|
479
799
|
id="menu-${menuItem.id}"
|
|
480
|
-
class="
|
|
481
|
-
? 'child-selected'
|
|
482
|
-
: ''} ${menuItem.inline ? 'inline' : ''} ${this.expanding &&
|
|
483
|
-
this.expanding === menuItem.id
|
|
484
|
-
? 'expanding'
|
|
485
|
-
: ''} ${this.isExpanded(menuItem) ? 'expanded' : ''}"
|
|
800
|
+
class="${itemClasses}"
|
|
486
801
|
@click=${event => {
|
|
487
802
|
this.handleItemClicked(event, menuItem);
|
|
488
803
|
}}
|
|
489
804
|
>
|
|
490
|
-
${
|
|
805
|
+
${menuItem.level === 0
|
|
491
806
|
? html`<temba-tip
|
|
492
807
|
style="display:flex;"
|
|
493
808
|
text="${menuItem.name}"
|
|
494
809
|
position="right"
|
|
495
810
|
>${icon}</temba-tip
|
|
496
811
|
>`
|
|
497
|
-
: icon}
|
|
498
|
-
|
|
499
|
-
<div
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
812
|
+
: html`${icon}${collapsedIcon}`}
|
|
813
|
+
|
|
814
|
+
<div class="details" style="flex-grow:1;display:flex">
|
|
815
|
+
<div
|
|
816
|
+
class="name"
|
|
817
|
+
style="flex-grow:1; flex-shrink:0; white-space: ${this.wraps
|
|
818
|
+
? 'normal'
|
|
819
|
+
: 'nowrap'};"
|
|
820
|
+
>
|
|
821
|
+
${menuItem.name}
|
|
822
|
+
</div>
|
|
823
|
+
${menuItem.level > 0
|
|
824
|
+
? menuItem.inline
|
|
825
|
+
? html`<temba-icon
|
|
826
|
+
name="chevron-${isSelected || isChildSelected
|
|
827
|
+
? 'up'
|
|
828
|
+
: 'down'}"
|
|
829
|
+
></temba-icon>`
|
|
830
|
+
: html`${menuItem.count || menuItem.count == 0
|
|
831
|
+
? html`
|
|
832
|
+
<div class="count">
|
|
833
|
+
${menuItem.count.toLocaleString()}
|
|
834
|
+
</div>
|
|
835
|
+
`
|
|
836
|
+
: html`<div class="count"></div>`}`
|
|
837
|
+
: null}
|
|
504
838
|
</div>
|
|
505
|
-
|
|
506
|
-
? html`${menuItem.count || menuItem.count == 0
|
|
507
|
-
? html`
|
|
508
|
-
<div class="count">${menuItem.count.toLocaleString()}</div>
|
|
509
|
-
`
|
|
510
|
-
: html`<div class="count"></div>`}`
|
|
511
|
-
: null}
|
|
839
|
+
<div class="right"></div>
|
|
512
840
|
</div>
|
|
513
841
|
|
|
514
842
|
<div class="item-bottom ${isSelected ? 'selected' : null}"></div>`;
|
|
@@ -516,12 +844,6 @@ export class TembaMenu extends RapidElement {
|
|
|
516
844
|
return item;
|
|
517
845
|
};
|
|
518
846
|
|
|
519
|
-
public toggleCollapsed() {
|
|
520
|
-
this.collapsed = !this.collapsed;
|
|
521
|
-
setCookie(COOKIE_KEYS.MENU_COLLAPSED, this.collapsed);
|
|
522
|
-
this.requestUpdate('root');
|
|
523
|
-
}
|
|
524
|
-
|
|
525
847
|
public render(): TemplateResult {
|
|
526
848
|
if (!this.root || !this.root.items) {
|
|
527
849
|
return html`<temba-loading
|
|
@@ -532,7 +854,7 @@ export class TembaMenu extends RapidElement {
|
|
|
532
854
|
/>`;
|
|
533
855
|
}
|
|
534
856
|
|
|
535
|
-
let items = this.root.items;
|
|
857
|
+
let items = this.root.items || [];
|
|
536
858
|
const levels = [];
|
|
537
859
|
|
|
538
860
|
levels.push(
|
|
@@ -556,21 +878,40 @@ export class TembaMenu extends RapidElement {
|
|
|
556
878
|
);
|
|
557
879
|
|
|
558
880
|
this.selection.forEach((id, index) => {
|
|
559
|
-
const selected = (items
|
|
881
|
+
const selected = findItem(items, id);
|
|
882
|
+
|
|
883
|
+
let collapsed = false;
|
|
560
884
|
if (selected) {
|
|
561
885
|
items = selected.items;
|
|
886
|
+
const itemState = this.getMenuItemState(selected.id);
|
|
887
|
+
// users set an explicit collapse state
|
|
888
|
+
if (itemState.collapsed) {
|
|
889
|
+
collapsed = itemState.collapsed === 'collapsed';
|
|
890
|
+
}
|
|
891
|
+
// otherwise pick a default collapse state
|
|
892
|
+
else {
|
|
893
|
+
if (this.selection.length > selected.level + 2) {
|
|
894
|
+
collapsed = false;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
562
897
|
} else {
|
|
563
898
|
items = null;
|
|
564
899
|
}
|
|
565
900
|
|
|
566
901
|
if (items && items.length > 0 && !selected.inline) {
|
|
567
902
|
levels.push(
|
|
568
|
-
html`<div
|
|
569
|
-
${
|
|
903
|
+
html`<div
|
|
904
|
+
class="${getClasses({
|
|
905
|
+
level: true,
|
|
906
|
+
['level-' + (index + 1)]: true,
|
|
907
|
+
collapsed,
|
|
908
|
+
})}"
|
|
909
|
+
>
|
|
910
|
+
${!this.submenu
|
|
570
911
|
? html`<div class="section">${selected.name}</div>`
|
|
571
912
|
: null}
|
|
572
913
|
${items.map((item: MenuItem) => {
|
|
573
|
-
if (item.inline &&
|
|
914
|
+
if (item.inline && this.isSelected(item)) {
|
|
574
915
|
return html`${this.renderMenuItem(item)}
|
|
575
916
|
<div class="inline-children">
|
|
576
917
|
${item.items.map((child: MenuItem) => {
|
|
@@ -586,16 +927,6 @@ export class TembaMenu extends RapidElement {
|
|
|
586
927
|
});
|
|
587
928
|
|
|
588
929
|
const menu = html`<div class="root">${levels}</div>`;
|
|
589
|
-
|
|
590
|
-
if (this.collapsible) {
|
|
591
|
-
return html`
|
|
592
|
-
<div style="display:flex">
|
|
593
|
-
${menu}
|
|
594
|
-
<div class="collapse-toggle" @click=${this.toggleCollapsed}></div>
|
|
595
|
-
</div>
|
|
596
|
-
`;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
930
|
return html`${menu}`;
|
|
600
931
|
}
|
|
601
932
|
}
|