@nyaruka/temba-components 0.37.1 → 0.39.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.
@@ -4,6 +4,7 @@ import { CustomEventType } from '../interfaces';
4
4
  import { RapidElement } from '../RapidElement';
5
5
  import { fetchResults, getClasses } from '../utils';
6
6
  import { Icon } from '../vectoricon';
7
+ import ColorHash from 'color-hash';
7
8
 
8
9
  export interface MenuItem {
9
10
  id?: string;
@@ -25,6 +26,8 @@ export interface MenuItem {
25
26
  type?: string;
26
27
  on_submit?: string;
27
28
  bubble?: string;
29
+ popup?: boolean;
30
+ avatar?: string;
28
31
  }
29
32
 
30
33
  interface MenuItemState {
@@ -64,7 +67,6 @@ export class TembaMenu extends RapidElement {
64
67
  height: 8px;
65
68
  left: 14px;
66
69
  bottom: -1px;
67
- z-index: 10000;
68
70
  border-radius: 99px;
69
71
  border: 1px solid rgb(255, 255, 255);
70
72
  position: relative;
@@ -123,7 +125,8 @@ export class TembaMenu extends RapidElement {
123
125
  .submenu {
124
126
  }
125
127
 
126
- .level-0 > .item {
128
+ .level-0 > .item,
129
+ .level-0 > temba-dropdown > div[slot='toggle'] > .avatar {
127
130
  background: var(--color-primary-dark);
128
131
  --icon-color: rgba(255, 255, 255, 0.7);
129
132
  font-size: 1em;
@@ -148,10 +151,40 @@ export class TembaMenu extends RapidElement {
148
151
  background: var(--color-primary-dark);
149
152
  }
150
153
 
151
- .level-0 > .item > .details {
154
+ temba-dropdown {
155
+ z-index: 1;
156
+ }
157
+
158
+ temba-dropdown > div[slot='dropdown'] .avatar > .details {
159
+ margin-left: 0.75em;
160
+ }
161
+
162
+ .level-0 > .item > .details,
163
+ .level-0 > temba-dropdown > div[slot='toggle'] > .avatar > .details {
152
164
  display: none !important;
153
165
  }
154
166
 
167
+ .avatar {
168
+ align-items: center;
169
+ }
170
+
171
+ .avatar-circle {
172
+ box-shadow: 0 0 0px 3px rgba(0, 0, 0, 0.075);
173
+ display: flex;
174
+ margin: 0.4em 0em;
175
+ height: 2em;
176
+ width: 2em;
177
+ flex-direction: row;
178
+ align-items: center;
179
+ color: #fff;
180
+ border-radius: 100%;
181
+ font-weight: 400;
182
+ }
183
+
184
+ temba-dropdown > div[slot='dropdown'] .avatar .avatar-circle {
185
+ font-size: 0.7em;
186
+ }
187
+
155
188
  .level-0.expanding {
156
189
  }
157
190
 
@@ -179,7 +212,6 @@ export class TembaMenu extends RapidElement {
179
212
  border-radius: var(--curvature);
180
213
  display: flex;
181
214
  min-width: 12em;
182
- max-width: 12em;
183
215
  }
184
216
 
185
217
  .item > temba-icon {
@@ -399,10 +431,6 @@ export class TembaMenu extends RapidElement {
399
431
  margin-left: 0.3rem;
400
432
  }
401
433
 
402
- .fully-collapsed .level-0 {
403
- z-index: 1;
404
- }
405
-
406
434
  .fully-collapsed .level-1 {
407
435
  margin-left: -208px;
408
436
  pointer-events: none;
@@ -664,6 +692,18 @@ export class TembaMenu extends RapidElement {
664
692
  menuItem: MenuItem,
665
693
  parent: MenuItem = null
666
694
  ) {
695
+ this.fireCustomEvent(CustomEventType.ButtonClicked, {
696
+ item: menuItem,
697
+ parent,
698
+ });
699
+ if (parent && parent.popup) {
700
+ return;
701
+ }
702
+
703
+ if (menuItem.popup) {
704
+ return;
705
+ }
706
+
667
707
  if (parent && parent.inline) {
668
708
  this.handleItemClicked(null, parent);
669
709
  }
@@ -677,15 +717,6 @@ export class TembaMenu extends RapidElement {
677
717
  event.stopPropagation();
678
718
  }
679
719
 
680
- if (menuItem.type == 'modax-button') {
681
- this.fireCustomEvent(CustomEventType.ButtonClicked, {
682
- title: menuItem.name,
683
- href: menuItem.href,
684
- on_submit: menuItem.on_submit,
685
- });
686
- return;
687
- }
688
-
689
720
  if (menuItem.trigger) {
690
721
  new Function(menuItem.trigger)();
691
722
  } else {
@@ -709,7 +740,12 @@ export class TembaMenu extends RapidElement {
709
740
  }
710
741
 
711
742
  if (menuItem.endpoint) {
712
- this.loadItems(menuItem, true);
743
+ this.loadItems(menuItem, this.pending.length == 0);
744
+
745
+ // make sure change events fire for events with hrefs
746
+ if (!menuItem.href) {
747
+ return;
748
+ }
713
749
  } else {
714
750
  if (this.pending && this.pending.length > 0) {
715
751
  // auto select the next pending click
@@ -719,16 +755,24 @@ export class TembaMenu extends RapidElement {
719
755
  const nextItem = findItem(item.items, nextId).item;
720
756
  if (nextItem) {
721
757
  this.handleItemClicked(null, nextItem);
758
+ return;
722
759
  } else {
723
760
  this.fireNoPath(nextId);
761
+ this.requestUpdate('root');
762
+ return;
724
763
  }
725
764
  } else {
726
765
  this.fireNoPath(nextId);
766
+ this.requestUpdate('root');
767
+ return;
727
768
  }
728
769
  }
729
770
  this.requestUpdate('root');
730
771
  }
731
- this.dispatchEvent(new Event('change'));
772
+
773
+ if (this.pending.length == 0 || this.getMenuItem().href) {
774
+ this.dispatchEvent(new Event('change'));
775
+ }
732
776
  }
733
777
  }
734
778
 
@@ -864,6 +908,35 @@ export class TembaMenu extends RapidElement {
864
908
  return expanded;
865
909
  }
866
910
 
911
+ private renderAvatar(avatar: string) {
912
+ const hash = new ColorHash();
913
+ const color = hash.hex(avatar);
914
+
915
+ let second = avatar.indexOf(' ') + 1;
916
+ if (second < 1) {
917
+ second = avatar.length > 1 ? 1 : 0;
918
+ }
919
+ let name = avatar.substring(0, 1) + avatar.substring(second, second + 1);
920
+ name = name.toUpperCase();
921
+
922
+ return html`
923
+ <div
924
+ style="border: 0px solid red; display:flex; flex-direction: column; align-items:center;"
925
+ >
926
+ <div
927
+ class="avatar-circle"
928
+ style="border: 0px solid rgba(0,0,0,.1);background:${color}"
929
+ >
930
+ <div
931
+ style="border: 0px solid red; display:flex; flex-direction: column; align-items:center;flex-grow:1"
932
+ >
933
+ <div style="border:0px solid blue;">${name}</div>
934
+ </div>
935
+ </div>
936
+ </div>
937
+ `;
938
+ }
939
+
867
940
  private renderMenuItem = (
868
941
  menuItem: MenuItem,
869
942
  parent: MenuItem = null
@@ -893,7 +966,7 @@ export class TembaMenu extends RapidElement {
893
966
  const isChildSelected =
894
967
  isSelected && this.selection.length > menuItem.level + 1;
895
968
 
896
- const icon = menuItem.icon
969
+ let icon = menuItem.icon
897
970
  ? html`<temba-icon
898
971
  size="${menuItem.level === 0 ? '1.5' : '1'}"
899
972
  name="${menuItem.icon}"
@@ -918,13 +991,18 @@ export class TembaMenu extends RapidElement {
918
991
  ['menu-' + menuItem.id]: true,
919
992
  'child-selected': isChildSelected,
920
993
  selected: isSelected,
921
- item: true,
994
+ item: !(menuItem.avatar && menuItem.level === 0),
995
+ avatar: !!menuItem.avatar,
922
996
  inline: menuItem.inline,
923
997
  expanding: this.expanding && this.expanding === menuItem.id,
924
998
  expanded: this.isExpanded(menuItem),
925
- iconless: !icon && !collapsedIcon,
999
+ iconless: !icon && !collapsedIcon && !menuItem.avatar,
926
1000
  });
927
1001
 
1002
+ if (menuItem.avatar) {
1003
+ icon = this.renderAvatar(menuItem.avatar);
1004
+ }
1005
+
928
1006
  const item = html` <div
929
1007
  class="item-top ${isSelected ? 'selected' : null} "
930
1008
  ></div>
@@ -937,12 +1015,14 @@ export class TembaMenu extends RapidElement {
937
1015
  }}
938
1016
  >
939
1017
  ${menuItem.level === 0
940
- ? html`<temba-tip
941
- style="display:flex;"
942
- text="${menuItem.name}"
943
- position="right"
944
- >${icon}</temba-tip
945
- >`
1018
+ ? menuItem.avatar
1019
+ ? icon
1020
+ : html`<temba-tip
1021
+ style="display:flex;"
1022
+ text="${menuItem.name}"
1023
+ position="right"
1024
+ >${icon}</temba-tip
1025
+ >`
946
1026
  : html`${icon}${collapsedIcon}`}
947
1027
 
948
1028
  <div class="details" style="flex-grow:1;display:flex">
@@ -975,6 +1055,24 @@ export class TembaMenu extends RapidElement {
975
1055
 
976
1056
  <div class="item-bottom ${isSelected ? 'selected' : null}"></div>`;
977
1057
 
1058
+ if (menuItem.popup) {
1059
+ return html`
1060
+ <temba-dropdown offsetx="10" arrowoffset="8" mask>
1061
+ <div slot="toggle">${item}</div>
1062
+ <div
1063
+ slot="dropdown"
1064
+ style="width:300px;overflow:hidden;padding-bottom:0.5em"
1065
+ >
1066
+ <div style="max-height:400px;overflow-y:auto">
1067
+ ${(menuItem.items || []).map((child: MenuItem) => {
1068
+ child.level = menuItem.level + 1;
1069
+ return this.renderMenuItem(child, menuItem);
1070
+ })}
1071
+ </div>
1072
+ </div>
1073
+ </temba-dropdown>
1074
+ `;
1075
+ }
978
1076
  return item;
979
1077
  };
980
1078
 
@@ -1,21 +1,33 @@
1
1
  import { css, html, TemplateResult } from 'lit';
2
2
  import { property, customElement } from 'lit/decorators.js';
3
3
  import { RapidElement } from '../RapidElement';
4
+ import { getUrl } from '../utils';
5
+ import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
4
6
 
5
7
  @customElement('temba-remote')
6
8
  export default class Remote extends RapidElement {
7
9
  @property({ type: String })
8
10
  endpoint: string;
9
11
 
12
+ @property({ attribute: false })
13
+ body: any = html`<temba-loading></temba-loading>`;
14
+
10
15
  static get styles() {
11
16
  return css``;
12
17
  }
13
18
 
14
19
  public updated(changes: Map<string, any>) {
15
20
  super.updated(changes);
21
+
22
+ if (changes.has('endpoint')) {
23
+ getUrl(this.endpoint).then(response => {
24
+ console.log(response);
25
+ this.body = unsafeHTML(response.body);
26
+ });
27
+ }
16
28
  }
17
29
 
18
30
  public render(): TemplateResult {
19
- return html``;
31
+ return html`${this.body}`;
20
32
  }
21
33
  }
@@ -12,6 +12,7 @@ export class TabPane extends RapidElement {
12
12
  display: flex;
13
13
  flex-direction: column;
14
14
  min-height: 0;
15
+ z-index: 0;
15
16
  }
16
17
 
17
18
  .tabs {
@@ -15,6 +15,8 @@ export enum Icon {
15
15
  call_missed = 'phone-call-02',
16
16
  campaign = 'clock-refresh',
17
17
  campaigns = 'clock-refresh',
18
+ channel = 'zap',
19
+ children = 'git-branch-01',
18
20
  check = 'check',
19
21
  checkbox = 'square',
20
22
  checkbox_checked = 'check-square',
@@ -24,6 +26,7 @@ export enum Icon {
24
26
  contact_updated = 'user-edit',
25
27
  contacts = 'user-01',
26
28
  copy = 'copy-04',
29
+ dashboard = 'pie-chart-01',
27
30
  delete = 'trash-03',
28
31
  delete_small = 'x',
29
32
  download = 'download-01',
package/temba-modules.ts CHANGED
@@ -43,6 +43,7 @@ import { FieldManager } from './src/fields/FieldManager';
43
43
  import { SortableList } from './src/list/SortableList';
44
44
  import { ContentMenu } from './src/list/ContentMenu';
45
45
  import { TembaDate } from './src/date/TembaDate';
46
+ import Remote from './src/remote/Remote';
46
47
 
47
48
  export function addCustomElement(name: string, comp: any) {
48
49
  if (!window.customElements.get(name)) {
@@ -86,6 +87,7 @@ addCustomElement('temba-run-list', RunList);
86
87
  addCustomElement('temba-flow-details', FlowStoreElement);
87
88
  addCustomElement('temba-label', Label);
88
89
  addCustomElement('temba-menu', TembaMenu);
90
+ addCustomElement('temba-remote', Remote);
89
91
  addCustomElement('temba-contact-search', ContactSearch);
90
92
  addCustomElement('temba-icon', VectorIcon);
91
93
  addCustomElement('temba-dropdown', Dropdown);