@nyaruka/temba-components 0.93.3 → 0.94.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.
@@ -11,6 +11,8 @@ export class TabPane extends RapidElement {
11
11
  this.collapses = false;
12
12
  // are the tabs on the bottom of the pane?
13
13
  this.bottom = false;
14
+ // do we allow unselecting the current tab
15
+ this.unselect = false;
14
16
  // Only shows the name if the tab is focused
15
17
  this.focusedName = false;
16
18
  this.index = -1;
@@ -127,6 +129,14 @@ export class TabPane extends RapidElement {
127
129
  .bottom .tab.selected {
128
130
  }
129
131
 
132
+ .unselect .tab.selected {
133
+ cursor: pointer;
134
+ }
135
+
136
+ .unselect .tab.selected:hover {
137
+ background: var(--unselect-tab-color, #eee);
138
+ }
139
+
130
140
  .tab:hover {
131
141
  --icon-color: #666;
132
142
  color: #666;
@@ -217,7 +227,13 @@ export class TabPane extends RapidElement {
217
227
  `;
218
228
  }
219
229
  handleTabClick(event) {
220
- this.index = parseInt(event.currentTarget.dataset.index);
230
+ const newIndex = parseInt(event.currentTarget.dataset.index);
231
+ if (this.unselect && this.index === newIndex) {
232
+ this.index = -1;
233
+ }
234
+ else {
235
+ this.index = newIndex;
236
+ }
221
237
  event.preventDefault();
222
238
  event.stopPropagation();
223
239
  this.requestUpdate('index');
@@ -301,7 +317,8 @@ export class TabPane extends RapidElement {
301
317
  bottom: this.bottom,
302
318
  collapses: this.collapses,
303
319
  embedded: this.embedded,
304
- focusedname: this.focusedName
320
+ focusedname: this.focusedName,
321
+ unselect: this.unselect
305
322
  })}"
306
323
  >
307
324
  ${this.tabs.map((tab, index) => html `
@@ -368,6 +385,9 @@ __decorate([
368
385
  __decorate([
369
386
  property({ type: Boolean })
370
387
  ], TabPane.prototype, "bottom", void 0);
388
+ __decorate([
389
+ property({ type: Boolean })
390
+ ], TabPane.prototype, "unselect", void 0);
371
391
  __decorate([
372
392
  property({ type: Boolean })
373
393
  ], TabPane.prototype, "focusedName", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"TabPane.js","sourceRoot":"","sources":["../../../src/tabpane/TabPane.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { CustomEventType } from '../interfaces';\nimport { RapidElement } from '../RapidElement';\nimport { getClasses } from '../utils';\nimport { Tab } from './Tab';\n\nexport class TabPane extends RapidElement {\n static get styles() {\n return css`\n :host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex-grow: 1;\n }\n\n .tabs {\n display: flex;\n align-items: stretch;\n }\n\n .tab {\n user-select: none;\n padding: 0.5em 0.7em;\n margin: 0em 0em;\n cursor: pointer;\n display: flex;\n font-size: 1.01em;\n align-items: center;\n border-radius: var(--curvature);\n border-bottom-right-radius: 0px;\n border-bottom-left-radius: 0px;\n border: 0px solid rgba(0, 0, 0, 0.45);\n color: var(--color-text-dark);\n --icon-color: var(--color-text-dark);\n white-space: nowrap;\n transition: all 100ms linear;\n }\n\n .focusedname .tab .name {\n transition: all 0s linear !important;\n }\n\n .focusedname .tab.selected .name {\n transition: all 200ms linear !important;\n }\n\n .tab.hidden {\n display: none;\n }\n\n .tab temba-icon {\n }\n\n .tab .name {\n margin-left: 0.4em;\n max-width: 80px;\n overflow: hidden;\n transition: max-width 500ms ease-in-out, margin 500ms ease-in-out;\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n\n .tab .badge {\n margin-left: 0.4em;\n }\n\n @media (max-width: 900px) {\n .collapses .tab .name {\n max-width: 0px;\n margin: 0;\n }\n }\n\n @media (max-width: 600px) {\n .collapses .tab .badge {\n display: none;\n }\n }\n\n .focusedname .tab.selected {\n transform: none;\n }\n\n .focusedname .tab .name {\n max-width: 0px;\n margin: 0;\n transition: max-width 200ms linear, margin 200ms linear;\n }\n\n .focusedname .tab.selected .name {\n margin-left: 0.4em;\n max-width: 200px;\n }\n\n .tab {\n transform: scale(0.9) translate(0em, -0.05em);\n --icon-color: #aaa;\n color: #aaa;\n }\n\n .tab.selected {\n }\n\n .tab.selected,\n .tab.selected:hover {\n cursor: default;\n box-shadow: 0px -3px 3px 1px rgba(0, 0, 0, 0.02);\n background: var(--focused-tab-color, #fff);\n transform: scale(1) translateY(0em);\n --icon-color: #666;\n color: #666;\n }\n\n .bottom .tab.selected {\n }\n\n .tab:hover {\n --icon-color: #666;\n color: #666;\n background: rgba(0, 0, 0, 0.02);\n }\n\n .pane {\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n background: var(--focused-tab-color, #fff);\n border-radius: var(--curvature);\n box-shadow: var(\n --tabs-shadow,\n rgba(0, 0, 0, 0.1) 0px 1px 3px 0px,\n rgba(0, 0, 0, 0.03) 0px 1px 2px 0px\n );\n min-height: 0;\n }\n\n .pane.first {\n border-top-left-radius: 0px;\n overflow: hidden;\n }\n\n .badge {\n }\n\n .count {\n border-radius: 99px;\n background: rgba(0, 0, 0, 0.05);\n color: rgba(0, 0, 0, 0.5);\n font-size: 0.6em;\n font-weight: 400;\n padding: 0.1em 0.4em;\n min-width: 1em;\n text-align: center;\n }\n\n .notify .count {\n background: var(--color-alert);\n color: #fff;\n }\n\n .bottom.tabs .tab {\n border-radius: 0em;\n }\n\n .bottom.pane {\n border-radius: 0em;\n }\n\n .bottom.pane.first {\n border-bottom-left-radius: 0px;\n }\n\n .bottom .tab.first {\n border-bottom-left-radius: var(--curvature);\n }\n\n .embedded.pane {\n box-shadow: none;\n margin: 0;\n }\n\n .embedded.tabs {\n margin: 0;\n }\n\n .embedded .tab {\n }\n\n .embedded.tabs .tab.selected {\n box-shadow: none !important;\n }\n\n .embedded.pane {\n // padding: 0.3em;\n }\n\n .check {\n margin-left: 0.4em;\n }\n\n .pane {\n display: flex;\n }\n `;\n }\n\n @property({ type: Boolean })\n embedded = false;\n\n @property({ type: Boolean })\n collapses = false;\n\n // are the tabs on the bottom of the pane?\n @property({ type: Boolean })\n bottom = false;\n\n // Only shows the name if the tab is focused\n @property({ type: Boolean })\n focusedName = false;\n\n @property({ type: Number })\n index = -1;\n\n @property({ type: String })\n refresh = '';\n\n @property({ type: Array, attribute: false })\n tabs: Tab[] = [];\n\n private handleTabClick(event: MouseEvent): void {\n this.index = parseInt(\n (event.currentTarget as HTMLDivElement).dataset.index\n );\n event.preventDefault();\n event.stopPropagation();\n this.requestUpdate('index');\n }\n\n public handleSlotChange() {\n const tabs: Tab[] = [];\n for (const t of this.children) {\n if (t.tagName === 'TEMBA-TAB') {\n const tab = t as Tab;\n tabs.push(tab);\n }\n }\n this.tabs = tabs;\n }\n\n public firstUpdated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(changes);\n this.shadowRoot.addEventListener(\n 'slotchange',\n this.handleSlotChange.bind(this)\n );\n }\n\n public updated(changedProperties: Map<string, any>) {\n super.updated(changedProperties);\n if (changedProperties.has('index') || changedProperties.has('tabs')) {\n this.tabs.forEach((tab, index) => {\n tab.selected = index == this.index;\n });\n this.fireEvent(CustomEventType.ContextChanged);\n }\n\n // if our current tab is hidden, select the first visible one\n if (this.index > this.tabs.length) {\n const tab = this.tabs[this.index];\n if (tab && tab.hidden) {\n for (let i = 0; i < this.tabs.length; i++) {\n const other = this.tabs[i];\n if (other && !other.hidden) {\n this.index = i;\n return;\n }\n }\n }\n }\n }\n\n public setTabDetails(\n index: number,\n details: { count: number; hidden: boolean }\n ) {\n if (index < this.tabs.length) {\n const tab = this.tabs[index];\n tab.count = details.count;\n tab.hidden = details.hidden;\n this.requestUpdate();\n } else {\n // not ready yet, set the tab details later\n setTimeout(() => {\n this.setTabDetails(index, details);\n }, 100);\n }\n }\n\n public getCurrentTab(): Tab {\n return this.tabs[this.index];\n }\n\n public getTab(index: number): Tab {\n return this.tabs[index];\n }\n\n public handleTabContentChanged() {\n this.requestUpdate();\n }\n\n public render(): TemplateResult {\n return html`\n ${this.bottom\n ? html`<div\n class=\"pane ${getClasses({\n first: this.index == 0,\n embedded: this.embedded,\n bottom: this.bottom\n })}\"\n >\n <slot></slot>\n </div>`\n : null}\n\n <div\n class=\"tabs ${getClasses({\n tabs: true,\n bottom: this.bottom,\n collapses: this.collapses,\n embedded: this.embedded,\n focusedname: this.focusedName\n })}\"\n >\n ${this.tabs.map(\n (tab, index) => html`\n <div\n @click=${this.handleTabClick}\n data-index=${index}\n class=\"${getClasses({\n tab: true,\n first: index == 0,\n selected: index == this.index,\n hidden: tab.hidden,\n notify: tab.notify\n })}\"\n style=\"${tab.selectionColor && index == this.index\n ? `color:${tab.selectionColor};--icon-color:${tab.selectionColor};`\n : ''} ${tab.selectionBackground && index == this.index\n ? `background-color:${tab.selectionBackground};`\n : ''}\"\n >\n ${tab.icon ? html`<temba-icon name=${tab.icon} />` : null}\n <div class=\"name\">${tab.name}</div>\n ${tab.hasBadge()\n ? html`\n <div class=\"badge\">\n ${tab.count > 0\n ? html`<div class=\"count\">\n ${tab.count.toLocaleString()}\n </div>`\n : null}\n </div>\n `\n : null}\n ${tab.checked\n ? html`<temba-icon class=\"check\" name=\"check\"></temba-icon>`\n : null}\n </div>\n `\n )}\n\n <div style=\"flex-grow:1\"></div>\n <div style=\"display:flex; align-items:center\">\n <slot name=\"tab-right\"></slot>\n </div>\n </div>\n ${!this.bottom\n ? html`<div\n class=\"pane ${getClasses({\n first: this.index == 0,\n embedded: this.embedded,\n bottom: this.bottom\n })}\"\n >\n <slot></slot>\n </div>`\n : null}\n `;\n }\n}\n"]}
1
+ {"version":3,"file":"TabPane.js","sourceRoot":"","sources":["../../../src/tabpane/TabPane.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { CustomEventType } from '../interfaces';\nimport { RapidElement } from '../RapidElement';\nimport { getClasses } from '../utils';\nimport { Tab } from './Tab';\n\nexport class TabPane extends RapidElement {\n static get styles() {\n return css`\n :host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex-grow: 1;\n }\n\n .tabs {\n display: flex;\n align-items: stretch;\n }\n\n .tab {\n user-select: none;\n padding: 0.5em 0.7em;\n margin: 0em 0em;\n cursor: pointer;\n display: flex;\n font-size: 1.01em;\n align-items: center;\n border-radius: var(--curvature);\n border-bottom-right-radius: 0px;\n border-bottom-left-radius: 0px;\n border: 0px solid rgba(0, 0, 0, 0.45);\n color: var(--color-text-dark);\n --icon-color: var(--color-text-dark);\n white-space: nowrap;\n transition: all 100ms linear;\n }\n\n .focusedname .tab .name {\n transition: all 0s linear !important;\n }\n\n .focusedname .tab.selected .name {\n transition: all 200ms linear !important;\n }\n\n .tab.hidden {\n display: none;\n }\n\n .tab temba-icon {\n }\n\n .tab .name {\n margin-left: 0.4em;\n max-width: 80px;\n overflow: hidden;\n transition: max-width 500ms ease-in-out, margin 500ms ease-in-out;\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n\n .tab .badge {\n margin-left: 0.4em;\n }\n\n @media (max-width: 900px) {\n .collapses .tab .name {\n max-width: 0px;\n margin: 0;\n }\n }\n\n @media (max-width: 600px) {\n .collapses .tab .badge {\n display: none;\n }\n }\n\n .focusedname .tab.selected {\n transform: none;\n }\n\n .focusedname .tab .name {\n max-width: 0px;\n margin: 0;\n transition: max-width 200ms linear, margin 200ms linear;\n }\n\n .focusedname .tab.selected .name {\n margin-left: 0.4em;\n max-width: 200px;\n }\n\n .tab {\n transform: scale(0.9) translate(0em, -0.05em);\n --icon-color: #aaa;\n color: #aaa;\n }\n\n .tab.selected {\n }\n\n .tab.selected,\n .tab.selected:hover {\n cursor: default;\n box-shadow: 0px -3px 3px 1px rgba(0, 0, 0, 0.02);\n background: var(--focused-tab-color, #fff);\n transform: scale(1) translateY(0em);\n --icon-color: #666;\n color: #666;\n }\n\n .bottom .tab.selected {\n }\n\n .unselect .tab.selected {\n cursor: pointer;\n }\n\n .unselect .tab.selected:hover {\n background: var(--unselect-tab-color, #eee);\n }\n\n .tab:hover {\n --icon-color: #666;\n color: #666;\n background: rgba(0, 0, 0, 0.02);\n }\n\n .pane {\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n background: var(--focused-tab-color, #fff);\n border-radius: var(--curvature);\n box-shadow: var(\n --tabs-shadow,\n rgba(0, 0, 0, 0.1) 0px 1px 3px 0px,\n rgba(0, 0, 0, 0.03) 0px 1px 2px 0px\n );\n min-height: 0;\n }\n\n .pane.first {\n border-top-left-radius: 0px;\n overflow: hidden;\n }\n\n .badge {\n }\n\n .count {\n border-radius: 99px;\n background: rgba(0, 0, 0, 0.05);\n color: rgba(0, 0, 0, 0.5);\n font-size: 0.6em;\n font-weight: 400;\n padding: 0.1em 0.4em;\n min-width: 1em;\n text-align: center;\n }\n\n .notify .count {\n background: var(--color-alert);\n color: #fff;\n }\n\n .bottom.tabs .tab {\n border-radius: 0em;\n }\n\n .bottom.pane {\n border-radius: 0em;\n }\n\n .bottom.pane.first {\n border-bottom-left-radius: 0px;\n }\n\n .bottom .tab.first {\n border-bottom-left-radius: var(--curvature);\n }\n\n .embedded.pane {\n box-shadow: none;\n margin: 0;\n }\n\n .embedded.tabs {\n margin: 0;\n }\n\n .embedded .tab {\n }\n\n .embedded.tabs .tab.selected {\n box-shadow: none !important;\n }\n\n .embedded.pane {\n // padding: 0.3em;\n }\n\n .check {\n margin-left: 0.4em;\n }\n\n .pane {\n display: flex;\n }\n `;\n }\n\n @property({ type: Boolean })\n embedded = false;\n\n @property({ type: Boolean })\n collapses = false;\n\n // are the tabs on the bottom of the pane?\n @property({ type: Boolean })\n bottom = false;\n\n // do we allow unselecting the current tab\n @property({ type: Boolean })\n unselect = false;\n\n // Only shows the name if the tab is focused\n @property({ type: Boolean })\n focusedName = false;\n\n @property({ type: Number })\n index = -1;\n\n @property({ type: String })\n refresh = '';\n\n @property({ type: Array, attribute: false })\n tabs: Tab[] = [];\n\n private handleTabClick(event: MouseEvent): void {\n const newIndex = parseInt(\n (event.currentTarget as HTMLDivElement).dataset.index\n );\n\n if (this.unselect && this.index === newIndex) {\n this.index = -1;\n } else {\n this.index = newIndex;\n }\n\n event.preventDefault();\n event.stopPropagation();\n this.requestUpdate('index');\n }\n\n public handleSlotChange() {\n const tabs: Tab[] = [];\n for (const t of this.children) {\n if (t.tagName === 'TEMBA-TAB') {\n const tab = t as Tab;\n tabs.push(tab);\n }\n }\n this.tabs = tabs;\n }\n\n public firstUpdated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(changes);\n this.shadowRoot.addEventListener(\n 'slotchange',\n this.handleSlotChange.bind(this)\n );\n }\n\n public updated(changedProperties: Map<string, any>) {\n super.updated(changedProperties);\n if (changedProperties.has('index') || changedProperties.has('tabs')) {\n this.tabs.forEach((tab, index) => {\n tab.selected = index == this.index;\n });\n this.fireEvent(CustomEventType.ContextChanged);\n }\n\n // if our current tab is hidden, select the first visible one\n if (this.index > this.tabs.length) {\n const tab = this.tabs[this.index];\n if (tab && tab.hidden) {\n for (let i = 0; i < this.tabs.length; i++) {\n const other = this.tabs[i];\n if (other && !other.hidden) {\n this.index = i;\n return;\n }\n }\n }\n }\n }\n\n public setTabDetails(\n index: number,\n details: { count: number; hidden: boolean }\n ) {\n if (index < this.tabs.length) {\n const tab = this.tabs[index];\n tab.count = details.count;\n tab.hidden = details.hidden;\n this.requestUpdate();\n } else {\n // not ready yet, set the tab details later\n setTimeout(() => {\n this.setTabDetails(index, details);\n }, 100);\n }\n }\n\n public getCurrentTab(): Tab {\n return this.tabs[this.index];\n }\n\n public getTab(index: number): Tab {\n return this.tabs[index];\n }\n\n public handleTabContentChanged() {\n this.requestUpdate();\n }\n\n public render(): TemplateResult {\n return html`\n ${this.bottom\n ? html`<div\n class=\"pane ${getClasses({\n first: this.index == 0,\n embedded: this.embedded,\n bottom: this.bottom\n })}\"\n >\n <slot></slot>\n </div>`\n : null}\n\n <div\n class=\"tabs ${getClasses({\n tabs: true,\n bottom: this.bottom,\n collapses: this.collapses,\n embedded: this.embedded,\n focusedname: this.focusedName,\n unselect: this.unselect\n })}\"\n >\n ${this.tabs.map(\n (tab, index) => html`\n <div\n @click=${this.handleTabClick}\n data-index=${index}\n class=\"${getClasses({\n tab: true,\n first: index == 0,\n selected: index == this.index,\n hidden: tab.hidden,\n notify: tab.notify\n })}\"\n style=\"${tab.selectionColor && index == this.index\n ? `color:${tab.selectionColor};--icon-color:${tab.selectionColor};`\n : ''} ${tab.selectionBackground && index == this.index\n ? `background-color:${tab.selectionBackground};`\n : ''}\"\n >\n ${tab.icon ? html`<temba-icon name=${tab.icon} />` : null}\n <div class=\"name\">${tab.name}</div>\n ${tab.hasBadge()\n ? html`\n <div class=\"badge\">\n ${tab.count > 0\n ? html`<div class=\"count\">\n ${tab.count.toLocaleString()}\n </div>`\n : null}\n </div>\n `\n : null}\n ${tab.checked\n ? html`<temba-icon class=\"check\" name=\"check\"></temba-icon>`\n : null}\n </div>\n `\n )}\n\n <div style=\"flex-grow:1\"></div>\n <div style=\"display:flex; align-items:center\">\n <slot name=\"tab-right\"></slot>\n </div>\n </div>\n ${!this.bottom\n ? html`<div\n class=\"pane ${getClasses({\n first: this.index == 0,\n embedded: this.embedded,\n bottom: this.bottom\n })}\"\n >\n <slot></slot>\n </div>`\n : null}\n `;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyaruka/temba-components",
3
- "version": "0.93.3",
3
+ "version": "0.94.0",
4
4
  "description": "Web components to support rapidpro and related projects",
5
5
  "author": "Nyaruka <code@nyaruka.coim>",
6
6
  "main": "dist/index.js",
@@ -12,6 +12,10 @@ import { MediaPicker } from '../mediapicker/MediaPicker';
12
12
  export class Compose extends FormElement {
13
13
  static get styles() {
14
14
  return css`
15
+ :host {
16
+ --textarea-min-height: var(--textarea-min-height, 4em);
17
+ }
18
+
15
19
  .container {
16
20
  display: flex;
17
21
  flex-direction: column;
@@ -32,7 +36,7 @@ export class Compose extends FormElement {
32
36
  --compose-curvature,
33
37
  var(--curvature) var(--curvature) 0px 0px
34
38
  );
35
- --textarea-min-height: var(--textarea-min-height, 4em);
39
+
36
40
  --widget-box-shadow: none;
37
41
  padding: var(--compose-padding, 0px);
38
42
  }
@@ -461,6 +465,7 @@ export class Compose extends FormElement {
461
465
  embedded
462
466
  focusedname
463
467
  bottom
468
+ unselect
464
469
  refresh="${this.currentAttachments.length}|${this.index}|${this
465
470
  .currentQuickReplies.length}|${showOptins}|${this.currentOptin}"
466
471
  >
@@ -9,6 +9,7 @@ import { FormElement } from '../FormElement';
9
9
  import { Checkbox } from '../checkbox/Checkbox';
10
10
  import { msg } from '@lit/localize';
11
11
  import { OmniOption } from '../omnibox/Omnibox';
12
+ import { Select } from '../select/Select';
12
13
 
13
14
  const QUEIT_MILLIS = 2000;
14
15
 
@@ -193,6 +194,37 @@ export class ContactSearch extends FormElement {
193
194
  temba-alert {
194
195
  margin: 1em 0;
195
196
  }
197
+
198
+ temba-select[name='not_seen_since_days'] {
199
+ margin-bottom: 1em;
200
+ display: block;
201
+ }
202
+
203
+ .activity-select {
204
+ display: flex;
205
+ align-items: center;
206
+ padding: var(--checkbox-padding, 10px);
207
+ border-radius: var(--curvature);
208
+ cursor: pointer;
209
+ }
210
+
211
+ .activity-select:hover {
212
+ background: #f9f9f9;
213
+ }
214
+
215
+ .small-select {
216
+ --temba-select-selected-padding: 0px 0.5em;
217
+ --temba-select-selected-line-height: 1em;
218
+ --temba-select-selected-font-size: 1em;
219
+ --search-input-height: 0px !important;
220
+ min-width: 100px;
221
+ }
222
+
223
+ .filters {
224
+ padding: 1em;
225
+ border: 1px solid var(--color-borders);
226
+ border-radius: var(--curvature);
227
+ }
196
228
  `;
197
229
  }
198
230
 
@@ -368,19 +400,48 @@ export class ContactSearch extends FormElement {
368
400
  }
369
401
  }
370
402
 
403
+ private handleActivityLevelChanged(evt: any) {
404
+ const select = evt.target as Select;
405
+ const option = select.values[0];
406
+ if (option) {
407
+ if (this.exclusions['not_seen_since_days']) {
408
+ this.exclusions['not_seen_since_days'] = parseInt(option.value);
409
+ this.refresh();
410
+ }
411
+ }
412
+ }
413
+
414
+ private handleActivityLabelClicked(evt: any) {
415
+ if (
416
+ evt.target &&
417
+ evt.target.tagName !== 'TEMBA-CHECKBOX' &&
418
+ evt.target.tagName !== 'TEMBA-SELECT' &&
419
+ !evt.target.disabled
420
+ ) {
421
+ const checkbox = evt.currentTarget.querySelector('temba-checkbox');
422
+ checkbox.checked = !checkbox.checked;
423
+ }
424
+ }
425
+
371
426
  private handleExclusionChanged(evt: any) {
372
427
  if (evt.target.tagName === 'TEMBA-CHECKBOX') {
373
428
  const ex = JSON.stringify(this.exclusions);
374
429
  const checkbox = evt.target as Checkbox;
375
430
  let value = checkbox.checked as any;
376
431
 
432
+ // if we check the activity box, look inside the select for the value
433
+ if (checkbox.name === 'not_seen_since_days' && value) {
434
+ const select = checkbox.parentElement.querySelector(
435
+ 'temba-select'
436
+ ) as Select;
437
+ if (select.values[0]) {
438
+ value = parseInt(select.values[0].value);
439
+ }
440
+ }
441
+
377
442
  if (!value) {
378
443
  delete this.exclusions[checkbox.name];
379
444
  } else {
380
- if (checkbox.name === 'not_seen_since_days') {
381
- value = 90;
382
- }
383
-
384
445
  this.exclusions[checkbox.name] = value;
385
446
  }
386
447
 
@@ -447,72 +508,106 @@ export class ContactSearch extends FormElement {
447
508
  }
448
509
 
449
510
  return html`
450
- ${this.advanced
451
- ? html`<div class="query">
452
- <temba-textinput
453
- .label=${this.label}
454
- .helpText=${this.helpText}
455
- .widgetOnly=${this.widgetOnly}
456
- .errors=${this.errors}
457
- name=${this.name}
458
- .inputRoot=${this}
459
- @input=${this.handleQueryChange}
460
- placeholder=${this.placeholder}
461
- .value=${this.query}
462
- textarea
463
- autogrow
464
- >
465
- </temba-textinput>
466
- </div>`
467
- : html`<temba-omnibox
468
- placeholder="Search for contacts or groups"
469
- widget_only=""
470
- groups=""
471
- contacts=""
472
- label="Recipients"
473
- help_text="The contacts to send the message to."
474
- .errors=${this.errors}
475
- id="recipients"
476
- name="recipients"
477
- .value=${this.recipients}
478
- endpoint="/contact/omnibox/?"
479
- @change=${this.handleRecipientsChanged}
480
- >
481
- </temba-omnibox>
482
-
483
- ${this.not_seen_since_days
484
- ? html`<temba-checkbox
485
- name="not_seen_since_days"
486
- label="${msg('Skip inactive contacts')}"
487
- help_text="${msg(
488
- 'Only include contacts who have sent a message in the last 90 days.'
489
- )}"
490
- ?checked=${this.exclusions['not_seen_since_days'] === 90}
491
- @change=${this.handleExclusionChanged}
492
- ></temba-checkbox>`
493
- : null}
494
- ${this.in_a_flow
495
- ? html`<temba-checkbox
496
- name="in_a_flow"
497
- label="${msg('Skip contacts currently in a flow')}"
498
- help_text="${msg(
499
- 'Avoid interrupting a contact who is already in a flow.'
500
- )}"
501
- ?checked=${this.exclusions['in_a_flow']}
502
- @change=${this.handleExclusionChanged}
503
- ></temba-checkbox>`
504
- : null}
505
- ${this.started_previously
506
- ? html`<temba-checkbox
507
- name="started_previously"
508
- label="${msg('Skip repeat contacts')}"
509
- help_text="${msg(
510
- 'Avoid restarting a contact who has been in this flow in the last 90 days.'
511
- )}"
512
- ?checked=${this.exclusions['started_previously']}
513
- @change=${this.handleExclusionChanged}
514
- ></temba-checkbox>`
515
- : null}`}
511
+ ${
512
+ this.advanced
513
+ ? html`<div class="query">
514
+ <temba-textinput
515
+ .label=${this.label}
516
+ .helpText=${this.helpText}
517
+ .widgetOnly=${this.widgetOnly}
518
+ .errors=${this.errors}
519
+ name=${this.name}
520
+ .inputRoot=${this}
521
+ @input=${this.handleQueryChange}
522
+ placeholder=${this.placeholder}
523
+ .value=${this.query}
524
+ textarea
525
+ autogrow
526
+ >
527
+ </temba-textinput>
528
+ </div>`
529
+ : html`<temba-omnibox
530
+ placeholder="Search for contacts or groups"
531
+ widget_only=""
532
+ groups=""
533
+ contacts=""
534
+ label="Recipients"
535
+ help_text="The contacts to send the message to."
536
+ .errors=${this.errors}
537
+ id="recipients"
538
+ name="recipients"
539
+ .value=${this.recipients}
540
+ endpoint="/contact/omnibox/?"
541
+ @change=${this.handleRecipientsChanged}
542
+ >
543
+ </temba-omnibox>
544
+
545
+ <div class="filters">
546
+ <div style="display:flex;font-size:1em;margin-bottom:0.5em">
547
+ <temba-icon size="1" name="filter"></temba-icon>
548
+ <div style="margin-left:0.5em">
549
+ Only include contacts who...
550
+ </div>
551
+ </div>
552
+
553
+ ${this.not_seen_since_days
554
+ ? html`
555
+ <div
556
+ class="activity-select"
557
+ @click=${this.handleActivityLabelClicked}
558
+ >
559
+ <temba-checkbox
560
+ style="display:inline;"
561
+ name="not_seen_since_days"
562
+ @change=${this.handleExclusionChanged}
563
+ >
564
+ </temba-checkbox>
565
+
566
+ <div style="margin-left:0.5em">
567
+ ${msg('Have sent a message in the last')}
568
+ </div>
569
+
570
+ <temba-select
571
+ style="margin-left:0.5em"
572
+ class="small-select"
573
+ @change=${this.handleActivityLevelChanged}
574
+ ?disabled=${!this.exclusions['not_seen_since_days']}
575
+ >
576
+ <temba-option
577
+ name="90 days"
578
+ value="90"
579
+ ></temba-option>
580
+ <temba-option
581
+ name="180 days"
582
+ value="180"
583
+ ></temba-option>
584
+ <temba-option name="Year" value="365"></temba-option>
585
+ </temba-select>
586
+ <div></div>
587
+ </div>
588
+ `
589
+ : null}
590
+ ${this.in_a_flow
591
+ ? html`<temba-checkbox
592
+ name="in_a_flow"
593
+ label="${msg('Are not currently in a flow')}"
594
+ ?checked=${this.exclusions['in_a_flow']}
595
+ @change=${this.handleExclusionChanged}
596
+ ></temba-checkbox>`
597
+ : null}
598
+ ${this.started_previously
599
+ ? html`<temba-checkbox
600
+ name="started_previously"
601
+ label="${msg(
602
+ 'Have not started this flow in the last 90 days'
603
+ )}"
604
+ ?checked=${this.exclusions['started_previously']}
605
+ @change=${this.handleExclusionChanged}
606
+ ></temba-checkbox>`
607
+ : null}
608
+ </div>`
609
+ }
610
+ </div>
516
611
 
517
612
  <div
518
613
  class="results ${getClasses({
@@ -527,14 +622,16 @@ export class ContactSearch extends FormElement {
527
622
  <div class="summary ${this.expanded ? 'expanded' : ''}">${summary}</div>
528
623
  </div>
529
624
 
530
- ${this.summary && this.summary.warnings
531
- ? this.summary.warnings.map(
532
- (warning) =>
533
- html`<temba-alert level="warning"
534
- >${unsafeHTML(warning)}</temba-alert
535
- >`
536
- )
537
- : ``}
625
+ ${
626
+ this.summary && this.summary.warnings
627
+ ? this.summary.warnings.map(
628
+ (warning) =>
629
+ html`<temba-alert level="warning"
630
+ >${unsafeHTML(warning)}</temba-alert
631
+ >`
632
+ )
633
+ : ``
634
+ }
538
635
  `;
539
636
  }
540
637
  }
package/src/locales/es.ts CHANGED
@@ -6,10 +6,7 @@
6
6
 
7
7
  export const templates = {
8
8
  scf1453991c986b25: `Tab para completar, enter para seleccionar`,
9
- sf8653793d61d060c: `Skip inactive contacts`,
10
- sd4af861b95e8ba4a: `Only include contacts who have sent a message in the last 90 days.`,
11
- sd149dff460c8dc41: `Skip contacts currently in a flow`,
12
- sc85010c81b71421e: `Avoid interrupting a contact who is already in a flow.`,
13
- s3e3fa53e834f4fda: `Skip repeat contacts`,
14
- s95e715d82602bced: `Avoid restarting a contact who has been in this flow in the last 90 days.`
9
+ s638236250662c6b3: `Have sent a message in the last`,
10
+ s8f02e3a18ffc083a: `Are not currently in a flow`,
11
+ s4788ee206c4570c7: `Have not started this flow in the last 90 days`
15
12
  };
package/src/locales/fr.ts CHANGED
@@ -6,10 +6,7 @@
6
6
 
7
7
  export const templates = {
8
8
  scf1453991c986b25: `Tab to complete, enter to select`,
9
- sf8653793d61d060c: `Skip inactive contacts`,
10
- sd4af861b95e8ba4a: `Only include contacts who have sent a message in the last 90 days.`,
11
- sd149dff460c8dc41: `Skip contacts currently in a flow`,
12
- sc85010c81b71421e: `Avoid interrupting a contact who is already in a flow.`,
13
- s3e3fa53e834f4fda: `Skip repeat contacts`,
14
- s95e715d82602bced: `Avoid restarting a contact who has been in this flow in the last 90 days.`
9
+ s638236250662c6b3: `Have sent a message in the last`,
10
+ s8f02e3a18ffc083a: `Are not currently in a flow`,
11
+ s4788ee206c4570c7: `Have not started this flow in the last 90 days`
15
12
  };
package/src/locales/pt.ts CHANGED
@@ -6,10 +6,7 @@
6
6
 
7
7
  export const templates = {
8
8
  scf1453991c986b25: `Tab to complete, enter to select`,
9
- sf8653793d61d060c: `Skip inactive contacts`,
10
- sd4af861b95e8ba4a: `Only include contacts who have sent a message in the last 90 days.`,
11
- sd149dff460c8dc41: `Skip contacts currently in a flow`,
12
- sc85010c81b71421e: `Avoid interrupting a contact who is already in a flow.`,
13
- s3e3fa53e834f4fda: `Skip repeat contacts`,
14
- s95e715d82602bced: `Avoid restarting a contact who has been in this flow in the last 90 days.`
9
+ s638236250662c6b3: `Have sent a message in the last`,
10
+ s8f02e3a18ffc083a: `Are not currently in a flow`,
11
+ s4788ee206c4570c7: `Have not started this flow in the last 90 days`
15
12
  };
@@ -116,6 +116,14 @@ export class TabPane extends RapidElement {
116
116
  .bottom .tab.selected {
117
117
  }
118
118
 
119
+ .unselect .tab.selected {
120
+ cursor: pointer;
121
+ }
122
+
123
+ .unselect .tab.selected:hover {
124
+ background: var(--unselect-tab-color, #eee);
125
+ }
126
+
119
127
  .tab:hover {
120
128
  --icon-color: #666;
121
129
  color: #666;
@@ -216,6 +224,10 @@ export class TabPane extends RapidElement {
216
224
  @property({ type: Boolean })
217
225
  bottom = false;
218
226
 
227
+ // do we allow unselecting the current tab
228
+ @property({ type: Boolean })
229
+ unselect = false;
230
+
219
231
  // Only shows the name if the tab is focused
220
232
  @property({ type: Boolean })
221
233
  focusedName = false;
@@ -230,9 +242,16 @@ export class TabPane extends RapidElement {
230
242
  tabs: Tab[] = [];
231
243
 
232
244
  private handleTabClick(event: MouseEvent): void {
233
- this.index = parseInt(
245
+ const newIndex = parseInt(
234
246
  (event.currentTarget as HTMLDivElement).dataset.index
235
247
  );
248
+
249
+ if (this.unselect && this.index === newIndex) {
250
+ this.index = -1;
251
+ } else {
252
+ this.index = newIndex;
253
+ }
254
+
236
255
  event.preventDefault();
237
256
  event.stopPropagation();
238
257
  this.requestUpdate('index');
@@ -332,7 +351,8 @@ export class TabPane extends RapidElement {
332
351
  bottom: this.bottom,
333
352
  collapses: this.collapses,
334
353
  embedded: this.embedded,
335
- focusedname: this.focusedName
354
+ focusedname: this.focusedName,
355
+ unselect: this.unselect
336
356
  })}"
337
357
  >
338
358
  ${this.tabs.map(
package/xliff/es.xlf CHANGED
@@ -6,23 +6,14 @@
6
6
  <source>Tab to complete, enter to select</source>
7
7
  <target>Tab para completar, enter para seleccionar</target>
8
8
  </trans-unit>
9
- <trans-unit id="sf8653793d61d060c">
10
- <source>Skip inactive contacts</source>
9
+ <trans-unit id="s638236250662c6b3">
10
+ <source>Have sent a message in the last</source>
11
11
  </trans-unit>
12
- <trans-unit id="sd4af861b95e8ba4a">
13
- <source>Only include contacts who have sent a message in the last 90 days.</source>
12
+ <trans-unit id="s8f02e3a18ffc083a">
13
+ <source>Are not currently in a flow</source>
14
14
  </trans-unit>
15
- <trans-unit id="sd149dff460c8dc41">
16
- <source>Skip contacts currently in a flow</source>
17
- </trans-unit>
18
- <trans-unit id="sc85010c81b71421e">
19
- <source>Avoid interrupting a contact who is already in a flow.</source>
20
- </trans-unit>
21
- <trans-unit id="s3e3fa53e834f4fda">
22
- <source>Skip repeat contacts</source>
23
- </trans-unit>
24
- <trans-unit id="s95e715d82602bced">
25
- <source>Avoid restarting a contact who has been in this flow in the last 90 days.</source>
15
+ <trans-unit id="s4788ee206c4570c7">
16
+ <source>Have not started this flow in the last 90 days</source>
26
17
  </trans-unit>
27
18
  </body>
28
19
  </file>
package/xliff/fr.xlf CHANGED
@@ -5,23 +5,14 @@
5
5
  <trans-unit id="scf1453991c986b25">
6
6
  <source>Tab to complete, enter to select</source>
7
7
  </trans-unit>
8
- <trans-unit id="sf8653793d61d060c">
9
- <source>Skip inactive contacts</source>
8
+ <trans-unit id="s638236250662c6b3">
9
+ <source>Have sent a message in the last</source>
10
10
  </trans-unit>
11
- <trans-unit id="sd4af861b95e8ba4a">
12
- <source>Only include contacts who have sent a message in the last 90 days.</source>
11
+ <trans-unit id="s8f02e3a18ffc083a">
12
+ <source>Are not currently in a flow</source>
13
13
  </trans-unit>
14
- <trans-unit id="sd149dff460c8dc41">
15
- <source>Skip contacts currently in a flow</source>
16
- </trans-unit>
17
- <trans-unit id="sc85010c81b71421e">
18
- <source>Avoid interrupting a contact who is already in a flow.</source>
19
- </trans-unit>
20
- <trans-unit id="s3e3fa53e834f4fda">
21
- <source>Skip repeat contacts</source>
22
- </trans-unit>
23
- <trans-unit id="s95e715d82602bced">
24
- <source>Avoid restarting a contact who has been in this flow in the last 90 days.</source>
14
+ <trans-unit id="s4788ee206c4570c7">
15
+ <source>Have not started this flow in the last 90 days</source>
25
16
  </trans-unit>
26
17
  </body>
27
18
  </file>