@nyaruka/temba-components 0.91.5 → 0.91.6

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.
@@ -18,6 +18,7 @@ export var TicketStatus;
18
18
  export var CustomEventType;
19
19
  (function (CustomEventType) {
20
20
  CustomEventType["Loaded"] = "temba-loaded";
21
+ CustomEventType["Loading"] = "temba-loading";
21
22
  CustomEventType["Canceled"] = "temba-canceled";
22
23
  CustomEventType["CursorChanged"] = "temba-cursor-changed";
23
24
  CustomEventType["Refreshed"] = "temba-refreshed";
@@ -1 +1 @@
1
- {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../src/interfaces.ts"],"names":[],"mappings":"AAwBA,MAAM,CAAN,IAAY,SAIX;AAJD,WAAY,SAAS;IACnB,mCAAsB,CAAA;IACtB,uCAA0B,CAAA;IAC1B,qCAAwB,CAAA;AAC1B,CAAC,EAJW,SAAS,KAAT,SAAS,QAIpB;AAED,MAAM,CAAN,IAAY,kBAIX;AAJD,WAAY,kBAAkB;IAC5B,sDAAgC,CAAA;IAChC,gEAA0C,CAAA;IAC1C,4DAAsC,CAAA;AACxC,CAAC,EAJW,kBAAkB,KAAlB,kBAAkB,QAI7B;AAED,MAAM,CAAN,IAAY,YAGX;AAHD,WAAY,YAAY;IACtB,6BAAa,CAAA;IACb,iCAAiB,CAAA;AACnB,CAAC,EAHW,YAAY,KAAZ,YAAY,QAGvB;AAyMD,MAAM,CAAN,IAAY,eAsBX;AAtBD,WAAY,eAAe;IACzB,0CAAuB,CAAA;IACvB,8CAA2B,CAAA;IAC3B,yDAAsC,CAAA;IACtC,gDAA6B,CAAA;IAC7B,gDAA6B,CAAA;IAC7B,yDAAsC,CAAA;IACtC,uDAAoC,CAAA;IACpC,6DAA0C,CAAA;IAC1C,2DAAwC,CAAA;IACxC,2DAAwC,CAAA;IACxC,yDAAsC,CAAA;IACtC,qDAAkC,CAAA;IAClC,gDAA6B,CAAA;IAC7B,kDAA+B,CAAA;IAC/B,2CAAwB,CAAA;IACxB,uDAAoC,CAAA;IACpC,wCAAqB,CAAA;IACrB,uDAAoC,CAAA;IACpC,iDAA8B,CAAA;IAC9B,+CAA4B,CAAA;IAC5B,4CAAyB,CAAA;AAC3B,CAAC,EAtBW,eAAe,KAAf,eAAe,QAsB1B","sourcesContent":["export interface Workspace {\n uuid: string;\n name: string;\n country: string;\n languages: string[];\n timezone: string;\n date_style: DateStyle;\n anon: boolean;\n}\n\nexport interface Language {\n iso: string;\n name: string;\n}\n\nexport interface Attachment {\n uuid: string;\n content_type: string;\n url: string;\n filename: string;\n size: number;\n error: string;\n}\n\nexport enum DateStyle {\n DayFirst = 'day_first',\n MonthFirst = 'month_first',\n YearFirst = 'year_first'\n}\n\nexport enum ScheduledEventType {\n CampaignEvent = 'campaign_event',\n ScheduledBroadcast = 'scheduled_broadcast',\n ScheduledTrigger = 'scheduled_trigger'\n}\n\nexport enum TicketStatus {\n Open = 'open',\n Closed = 'closed'\n}\n\nexport interface ScheduledEvent {\n type: ScheduledEventType;\n scheduled: string;\n repeat_period: string;\n campaign?: ObjectReference;\n flow?: ObjectReference;\n message?: string;\n}\n\nexport interface User {\n id?: number;\n first_name?: string;\n last_name?: string;\n email?: string;\n role?: string;\n created_on?: string;\n avatar?: string;\n}\n\nexport interface Ticket {\n uuid: string;\n subject: string;\n body?: string;\n closed_on: string;\n opened_on: string;\n status: string;\n contact: ObjectReference;\n topic: ObjectReference;\n assignee?: { email: string; name: string; avatar?: string };\n}\n\nexport interface FlowResult {\n key: string;\n name: string;\n categories: string[];\n node_uuids: string[];\n}\n\nexport interface FlowDetails {\n name: string;\n results: FlowResult[];\n modified_on: string;\n runs: {\n active: number;\n completed: number;\n expired: number;\n interrupted: number;\n };\n}\n\nexport interface Msg {\n text: string;\n status: string;\n channel: ObjectReference;\n quick_replies: string[];\n urn: string;\n id: number;\n direction: string;\n type: string;\n attachments: string[];\n}\n\nexport interface ObjectReference {\n uuid: string;\n name: string;\n}\n\nexport interface ContactField {\n key: string;\n label: string;\n value_type: string;\n featured: boolean;\n priority: number;\n agent_access: string;\n type: string;\n usages: { campaign_events: number; flows: number; groups: number };\n}\n\nexport interface ContactGroup {\n uuid: string;\n count: number;\n name: string;\n query?: string;\n status: string;\n}\n\nexport interface URN {\n scheme: string;\n path: string;\n}\n\nexport interface Group {\n name: string;\n uuid: string;\n is_dynamic?: boolean;\n}\n\nexport interface ContactTicket {\n name: string;\n uuid: string;\n status: string;\n\n contact: {\n uuid: string;\n name: string;\n created_on: Date;\n last_seen_on: Date;\n };\n}\n\nexport interface Contact {\n name: string;\n uuid: string;\n stopped: boolean;\n blocked: boolean;\n urns: string[];\n language?: string;\n fields: { [key: string]: string };\n groups: Group[];\n modified_on: string;\n created_on: string;\n last_seen_on: string;\n status: string;\n\n anon_display?: string;\n flow?: ObjectReference;\n last_msg?: Msg;\n direction?: string;\n ticket: {\n uuid: string;\n subject: string;\n closed_on?: string;\n last_activity_on: string;\n assignee?: User;\n topic?: ObjectReference;\n };\n}\n\nexport interface FeatureProperties {\n name: string;\n osm_id: string;\n level: number;\n children?: FeatureProperties[];\n has_children?: boolean;\n aliases?: string;\n parent_osm_id?: string;\n id?: number;\n path?: string;\n}\n\nexport interface Position {\n top: number;\n left: number;\n}\n\nexport interface FunctionExample {\n template: string;\n output: string;\n}\n\nexport interface CompletionOption {\n name?: string;\n summary: string;\n\n // functions\n signature?: string;\n detail?: string;\n examples?: FunctionExample[];\n}\n\nexport interface CompletionResult {\n anchorPosition: Position;\n query: string;\n options: CompletionOption[];\n currentFunction: CompletionOption;\n}\n\nexport interface CompletionProperty {\n key: string;\n help: string;\n type: string;\n}\n\nexport interface CompletionType {\n name: string;\n\n key_source?: string;\n property_template?: CompletionProperty;\n properties?: CompletionProperty[];\n}\n\nexport interface CompletionSchema {\n types: CompletionType[];\n root: CompletionProperty[];\n root_no_session: CompletionProperty[];\n}\n\nexport type KeyedAssets = { [assetType: string]: string[] };\n\nexport enum CustomEventType {\n Loaded = 'temba-loaded',\n Canceled = 'temba-canceled',\n CursorChanged = 'temba-cursor-changed',\n Refreshed = 'temba-refreshed',\n Selection = 'temba-selection',\n ButtonClicked = 'temba-button-clicked',\n DialogHidden = 'temba-dialog-hidden',\n ScrollThreshold = 'temba-scroll-threshold',\n ContentChanged = 'temba-content-changed',\n ContextChanged = 'temba-context-changed',\n FetchComplete = 'temba-fetch-complete',\n MessageSent = 'temba-message-sent',\n Submitted = 'temba-submitted',\n Redirected = 'temba-redirected',\n NoPath = 'temba-no-path',\n StoreUpdated = 'temba-store-updated',\n Ready = 'temba-ready',\n OrderChanged = 'temba-order-changed',\n DragStart = 'temba-drag-start',\n DragStop = 'temba-drag-stop',\n Resized = 'temba-resized'\n}\n"]}
1
+ {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../src/interfaces.ts"],"names":[],"mappings":"AAwBA,MAAM,CAAN,IAAY,SAIX;AAJD,WAAY,SAAS;IACnB,mCAAsB,CAAA;IACtB,uCAA0B,CAAA;IAC1B,qCAAwB,CAAA;AAC1B,CAAC,EAJW,SAAS,KAAT,SAAS,QAIpB;AAED,MAAM,CAAN,IAAY,kBAIX;AAJD,WAAY,kBAAkB;IAC5B,sDAAgC,CAAA;IAChC,gEAA0C,CAAA;IAC1C,4DAAsC,CAAA;AACxC,CAAC,EAJW,kBAAkB,KAAlB,kBAAkB,QAI7B;AAED,MAAM,CAAN,IAAY,YAGX;AAHD,WAAY,YAAY;IACtB,6BAAa,CAAA;IACb,iCAAiB,CAAA;AACnB,CAAC,EAHW,YAAY,KAAZ,YAAY,QAGvB;AAyMD,MAAM,CAAN,IAAY,eAuBX;AAvBD,WAAY,eAAe;IACzB,0CAAuB,CAAA;IACvB,4CAAyB,CAAA;IACzB,8CAA2B,CAAA;IAC3B,yDAAsC,CAAA;IACtC,gDAA6B,CAAA;IAC7B,gDAA6B,CAAA;IAC7B,yDAAsC,CAAA;IACtC,uDAAoC,CAAA;IACpC,6DAA0C,CAAA;IAC1C,2DAAwC,CAAA;IACxC,2DAAwC,CAAA;IACxC,yDAAsC,CAAA;IACtC,qDAAkC,CAAA;IAClC,gDAA6B,CAAA;IAC7B,kDAA+B,CAAA;IAC/B,2CAAwB,CAAA;IACxB,uDAAoC,CAAA;IACpC,wCAAqB,CAAA;IACrB,uDAAoC,CAAA;IACpC,iDAA8B,CAAA;IAC9B,+CAA4B,CAAA;IAC5B,4CAAyB,CAAA;AAC3B,CAAC,EAvBW,eAAe,KAAf,eAAe,QAuB1B","sourcesContent":["export interface Workspace {\n uuid: string;\n name: string;\n country: string;\n languages: string[];\n timezone: string;\n date_style: DateStyle;\n anon: boolean;\n}\n\nexport interface Language {\n iso: string;\n name: string;\n}\n\nexport interface Attachment {\n uuid: string;\n content_type: string;\n url: string;\n filename: string;\n size: number;\n error: string;\n}\n\nexport enum DateStyle {\n DayFirst = 'day_first',\n MonthFirst = 'month_first',\n YearFirst = 'year_first'\n}\n\nexport enum ScheduledEventType {\n CampaignEvent = 'campaign_event',\n ScheduledBroadcast = 'scheduled_broadcast',\n ScheduledTrigger = 'scheduled_trigger'\n}\n\nexport enum TicketStatus {\n Open = 'open',\n Closed = 'closed'\n}\n\nexport interface ScheduledEvent {\n type: ScheduledEventType;\n scheduled: string;\n repeat_period: string;\n campaign?: ObjectReference;\n flow?: ObjectReference;\n message?: string;\n}\n\nexport interface User {\n id?: number;\n first_name?: string;\n last_name?: string;\n email?: string;\n role?: string;\n created_on?: string;\n avatar?: string;\n}\n\nexport interface Ticket {\n uuid: string;\n subject: string;\n body?: string;\n closed_on: string;\n opened_on: string;\n status: string;\n contact: ObjectReference;\n topic: ObjectReference;\n assignee?: { email: string; name: string; avatar?: string };\n}\n\nexport interface FlowResult {\n key: string;\n name: string;\n categories: string[];\n node_uuids: string[];\n}\n\nexport interface FlowDetails {\n name: string;\n results: FlowResult[];\n modified_on: string;\n runs: {\n active: number;\n completed: number;\n expired: number;\n interrupted: number;\n };\n}\n\nexport interface Msg {\n text: string;\n status: string;\n channel: ObjectReference;\n quick_replies: string[];\n urn: string;\n id: number;\n direction: string;\n type: string;\n attachments: string[];\n}\n\nexport interface ObjectReference {\n uuid: string;\n name: string;\n}\n\nexport interface ContactField {\n key: string;\n label: string;\n value_type: string;\n featured: boolean;\n priority: number;\n agent_access: string;\n type: string;\n usages: { campaign_events: number; flows: number; groups: number };\n}\n\nexport interface ContactGroup {\n uuid: string;\n count: number;\n name: string;\n query?: string;\n status: string;\n}\n\nexport interface URN {\n scheme: string;\n path: string;\n}\n\nexport interface Group {\n name: string;\n uuid: string;\n is_dynamic?: boolean;\n}\n\nexport interface ContactTicket {\n name: string;\n uuid: string;\n status: string;\n\n contact: {\n uuid: string;\n name: string;\n created_on: Date;\n last_seen_on: Date;\n };\n}\n\nexport interface Contact {\n name: string;\n uuid: string;\n stopped: boolean;\n blocked: boolean;\n urns: string[];\n language?: string;\n fields: { [key: string]: string };\n groups: Group[];\n modified_on: string;\n created_on: string;\n last_seen_on: string;\n status: string;\n\n anon_display?: string;\n flow?: ObjectReference;\n last_msg?: Msg;\n direction?: string;\n ticket: {\n uuid: string;\n subject: string;\n closed_on?: string;\n last_activity_on: string;\n assignee?: User;\n topic?: ObjectReference;\n };\n}\n\nexport interface FeatureProperties {\n name: string;\n osm_id: string;\n level: number;\n children?: FeatureProperties[];\n has_children?: boolean;\n aliases?: string;\n parent_osm_id?: string;\n id?: number;\n path?: string;\n}\n\nexport interface Position {\n top: number;\n left: number;\n}\n\nexport interface FunctionExample {\n template: string;\n output: string;\n}\n\nexport interface CompletionOption {\n name?: string;\n summary: string;\n\n // functions\n signature?: string;\n detail?: string;\n examples?: FunctionExample[];\n}\n\nexport interface CompletionResult {\n anchorPosition: Position;\n query: string;\n options: CompletionOption[];\n currentFunction: CompletionOption;\n}\n\nexport interface CompletionProperty {\n key: string;\n help: string;\n type: string;\n}\n\nexport interface CompletionType {\n name: string;\n\n key_source?: string;\n property_template?: CompletionProperty;\n properties?: CompletionProperty[];\n}\n\nexport interface CompletionSchema {\n types: CompletionType[];\n root: CompletionProperty[];\n root_no_session: CompletionProperty[];\n}\n\nexport type KeyedAssets = { [assetType: string]: string[] };\n\nexport enum CustomEventType {\n Loaded = 'temba-loaded',\n Loading = 'temba-loading',\n Canceled = 'temba-canceled',\n CursorChanged = 'temba-cursor-changed',\n Refreshed = 'temba-refreshed',\n Selection = 'temba-selection',\n ButtonClicked = 'temba-button-clicked',\n DialogHidden = 'temba-dialog-hidden',\n ScrollThreshold = 'temba-scroll-threshold',\n ContentChanged = 'temba-content-changed',\n ContextChanged = 'temba-context-changed',\n FetchComplete = 'temba-fetch-complete',\n MessageSent = 'temba-message-sent',\n Submitted = 'temba-submitted',\n Redirected = 'temba-redirected',\n NoPath = 'temba-no-path',\n StoreUpdated = 'temba-store-updated',\n Ready = 'temba-ready',\n OrderChanged = 'temba-order-changed',\n DragStart = 'temba-drag-start',\n DragStop = 'temba-drag-stop',\n Resized = 'temba-resized'\n}\n"]}
@@ -2,6 +2,7 @@ import { __decorate } from "tslib";
2
2
  import { css, html } from 'lit';
3
3
  import { RapidElement } from '../RapidElement';
4
4
  import { property } from 'lit/decorators.js';
5
+ import { CustomEventType } from '../interfaces';
5
6
  import { Icon } from '../vectoricon';
6
7
  import { DEFAULT_MEDIA_ENDPOINT, getClasses, isImageAttachment, postFormData } from '../utils';
7
8
  const verifyAccept = (type, accept) => {
@@ -125,6 +126,11 @@ export class MediaPicker extends RapidElement {
125
126
  this.dispatchEvent(new Event('change'));
126
127
  }, 0);
127
128
  }
129
+ if (changes.has('uploading')) {
130
+ this.dispatchEvent(new CustomEvent(CustomEventType.Loading, {
131
+ detail: { loading: this.uploading }
132
+ }));
133
+ }
128
134
  }
129
135
  getAcceptableFiles(evt) {
130
136
  const dt = evt.dataTransfer;
@@ -1 +1 @@
1
- {"version":3,"file":"MediaPicker.js","sourceRoot":"","sources":["../../../src/mediapicker/MediaPicker.ts"],"names":[],"mappings":"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","sourcesContent":["import { TemplateResult, css, html } from 'lit';\nimport { RapidElement } from '../RapidElement';\nimport { property } from 'lit/decorators.js';\nimport { Attachment } from '../interfaces';\nimport { Icon } from '../vectoricon';\nimport {\n DEFAULT_MEDIA_ENDPOINT,\n WebResponse,\n getClasses,\n isImageAttachment,\n postFormData\n} from '../utils';\n\nconst verifyAccept = (type: string, accept: string): boolean => {\n if (accept) {\n const allowed = accept.split(',').map((x) => x.trim());\n return (\n allowed.includes(type) || allowed.includes(type.split('/')[0] + '/*')\n );\n }\n return true;\n};\n\nexport class MediaPicker extends RapidElement {\n static get styles() {\n return css`\n .drop-mask {\n border-radius: var(--curvature-widget);\n transition: opacity ease-in-out var(--transition-speed);\n }\n\n .highlight .drop-mask {\n background: rgba(210, 243, 184, 0.8);\n }\n\n .drop-mask > div {\n margin: auto;\n border-radius: var(--curvature-widget);\n font-weight: 400;\n color: rgba(0, 0, 0, 0.5);\n }\n\n .attachments {\n }\n\n .attachments-list {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n padding: 0.2em;\n }\n\n .attachment-item {\n padding: 0.4em;\n padding-top: 1em;\n }\n\n .attachment-item.error {\n background: #fff;\n color: rgba(250, 0, 0, 0.75);\n padding: 0.2em;\n margin: 0.3em 0.5em;\n border-radius: var(--curvature);\n display: block;\n }\n\n .remove-item {\n --icon-color: #ccc;\n background: #fff;\n border-radius: 99%;\n transition: transform 200ms linear;\n transform: scale(0);\n display: block;\n margin-bottom: -24px;\n margin-left: 10px;\n width: 1em;\n height: 1em;\n }\n\n .attachment-item:hover .remove-item {\n transform: scale(1);\n }\n\n .remove-item:hover {\n --icon-color: #333;\n cursor: pointer;\n }\n\n .attachment-name {\n align-self: center;\n font-size: 12px;\n padding: 2px 8px;\n }\n\n #upload-input {\n display: none;\n }\n\n .upload-label {\n display: flex;\n align-items: center;\n }\n\n .upload-icon {\n color: rgb(102, 102, 102);\n }\n\n .add-attachment {\n padding: 1em;\n background-color: rgba(0, 0, 0, 0.05);\n border-radius: var(--curvature);\n color: #aaa;\n margin: 0.5em;\n }\n\n .add-attachment:hover {\n background-color: rgba(0, 0, 0, 0.07);\n cursor: pointer;\n }\n `;\n }\n\n @property({ type: String, attribute: false })\n endpoint = DEFAULT_MEDIA_ENDPOINT;\n\n @property({ type: Boolean })\n pendingDrop: boolean;\n\n @property({ type: String })\n icon = Icon.add;\n\n @property({ type: String })\n accept = ''; //e.g. \".xls,.xlsx\"\n\n @property({ type: Number })\n max = 3;\n\n @property({ type: Array })\n attachments: Attachment[] = [];\n\n @property({ type: Boolean, attribute: false })\n uploading: boolean;\n\n public updated(changes: Map<string, any>): void {\n super.updated(changes);\n if (changes.has('attachments')) {\n // wait one cycle to fire change for tests\n setTimeout(() => {\n this.dispatchEvent(new Event('change'));\n }, 0);\n }\n }\n\n private getAcceptableFiles(evt: DragEvent): File[] {\n const dt = evt.dataTransfer;\n if (dt) {\n const files = [...dt.files];\n return files.filter((file) => verifyAccept(file.type, this.accept));\n }\n }\n\n private handleDragEnter(evt: DragEvent): void {\n this.highlight(evt);\n }\n\n private handleDragOver(evt: DragEvent): void {\n this.highlight(evt);\n }\n\n private handleDragLeave(evt: DragEvent): void {\n this.unhighlight(evt);\n }\n\n private handleDrop(evt: DragEvent): void {\n this.unhighlight(evt);\n if (this.canAcceptAttachments()) {\n this.uploadFiles(this.getAcceptableFiles(evt));\n }\n }\n\n public canAcceptAttachments() {\n return this.attachments.length < this.max;\n }\n\n private highlight(evt: DragEvent): void {\n evt.preventDefault();\n evt.stopPropagation();\n if (this.canAcceptAttachments()) {\n this.pendingDrop = true;\n }\n }\n\n private unhighlight(evt: DragEvent): void {\n evt.preventDefault();\n evt.stopPropagation();\n this.pendingDrop = false;\n }\n\n private addCurrentAttachment(attachmentToAdd: any) {\n this.attachments.push(attachmentToAdd);\n this.requestUpdate('attachments');\n }\n\n private removeCurrentAttachment(attachmentToRemove: any) {\n this.attachments = this.attachments.filter(\n (currentAttachment) => currentAttachment !== attachmentToRemove\n );\n this.requestUpdate('attachments');\n }\n\n private handleRemoveFileClicked(evt: Event): void {\n const target = evt.target as HTMLDivElement;\n const currentAttachmentToRemove = this.attachments.find(\n ({ url }) => url === target.id\n );\n if (currentAttachmentToRemove) {\n this.removeCurrentAttachment(currentAttachmentToRemove);\n }\n }\n\n private handleUploadFileInputChanged(evt: Event): void {\n const target = evt.target as HTMLInputElement;\n const files = target.files;\n this.uploadFiles([...files]);\n }\n\n public uploadFiles(files: File[]): void {\n let filesToUpload = [];\n\n //remove duplicate files that have already been uploaded\n filesToUpload = files.filter((file) => {\n // check our file type against accepts\n if (this.accept) {\n if (!verifyAccept(file.type, this.accept)) {\n return false;\n }\n }\n\n const index = this.attachments.findIndex(\n (value) => value.filename === file.name && value.size === file.size\n );\n if (index === -1) {\n return file;\n }\n });\n\n filesToUpload.map((fileToUpload) => {\n this.uploadFile(fileToUpload);\n });\n }\n\n private uploadFile(file: File): void {\n this.uploading = true;\n\n const url = this.endpoint;\n const payload = new FormData();\n payload.append('file', file);\n postFormData(url, payload)\n .then((response: WebResponse) => {\n if (this.attachments.length < this.max) {\n const attachment = response.json as Attachment;\n if (attachment) {\n this.addCurrentAttachment(attachment);\n }\n }\n })\n .catch((error: WebResponse) => {\n let uploadError = '';\n if (error.status === 400) {\n uploadError = error.json.file[0];\n } else {\n uploadError = 'Server failure';\n }\n console.error(uploadError);\n })\n .finally(() => {\n this.uploading = false;\n });\n }\n\n private renderUploader(): TemplateResult {\n if (this.uploading) {\n return html`<temba-loading units=\"3\" size=\"12\"></temba-loading>`;\n } else {\n return this.attachments.length < this.max\n ? html`<input\n type=\"file\"\n id=\"upload-input\"\n ?multiple=${this.max > 1}\n accept=\"${this.accept}\"\n @change=\"${this.handleUploadFileInputChanged}\"\n />\n <label\n id=\"upload-label\"\n class=\"actions-left upload-label\"\n for=\"upload-input\"\n >\n <div class=\"add-attachment\">\n <temba-icon name=\"${this.icon}\" size=\"1.5\"></temba-icon>\n </div>\n </label>`\n : null;\n }\n }\n\n public render(): TemplateResult {\n return html` <div\n class=${getClasses({ container: true, highlight: this.pendingDrop })}\n @dragenter=\"${this.handleDragEnter}\"\n @dragover=\"${this.handleDragOver}\"\n @dragleave=\"${this.handleDragLeave}\"\n @drop=\"${this.handleDrop}\"\n >\n <div class=\"drop-mask\">\n <div class=\"attachments-list\">\n ${this.attachments.map((validAttachment) => {\n return html`<div class=\"attachment-item\">\n <temba-icon\n class=\"remove-item\"\n @click=\"${this.handleRemoveFileClicked}\"\n id=\"${validAttachment.url}\"\n name=\"${Icon.delete_small}\"\n ></temba-icon>\n ${isImageAttachment(validAttachment)\n ? html`<temba-thumbnail\n url=\"${validAttachment.url}\"\n ></temba-thumbnail>`\n : html`<temba-thumbnail\n label=\"${validAttachment.content_type.split('/')[1]}\"\n ></temba-thumbnail>`}\n </div>`;\n })}\n ${this.renderUploader()}\n </div>\n </div>\n </div>`;\n }\n}\n"]}
1
+ {"version":3,"file":"MediaPicker.js","sourceRoot":"","sources":["../../../src/mediapicker/MediaPicker.ts"],"names":[],"mappings":"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","sourcesContent":["import { TemplateResult, css, html } from 'lit';\nimport { RapidElement } from '../RapidElement';\nimport { property } from 'lit/decorators.js';\nimport { Attachment, CustomEventType } from '../interfaces';\nimport { Icon } from '../vectoricon';\nimport {\n DEFAULT_MEDIA_ENDPOINT,\n WebResponse,\n getClasses,\n isImageAttachment,\n postFormData\n} from '../utils';\n\nconst verifyAccept = (type: string, accept: string): boolean => {\n if (accept) {\n const allowed = accept.split(',').map((x) => x.trim());\n return (\n allowed.includes(type) || allowed.includes(type.split('/')[0] + '/*')\n );\n }\n return true;\n};\n\nexport class MediaPicker extends RapidElement {\n static get styles() {\n return css`\n .drop-mask {\n border-radius: var(--curvature-widget);\n transition: opacity ease-in-out var(--transition-speed);\n }\n\n .highlight .drop-mask {\n background: rgba(210, 243, 184, 0.8);\n }\n\n .drop-mask > div {\n margin: auto;\n border-radius: var(--curvature-widget);\n font-weight: 400;\n color: rgba(0, 0, 0, 0.5);\n }\n\n .attachments {\n }\n\n .attachments-list {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n padding: 0.2em;\n }\n\n .attachment-item {\n padding: 0.4em;\n padding-top: 1em;\n }\n\n .attachment-item.error {\n background: #fff;\n color: rgba(250, 0, 0, 0.75);\n padding: 0.2em;\n margin: 0.3em 0.5em;\n border-radius: var(--curvature);\n display: block;\n }\n\n .remove-item {\n --icon-color: #ccc;\n background: #fff;\n border-radius: 99%;\n transition: transform 200ms linear;\n transform: scale(0);\n display: block;\n margin-bottom: -24px;\n margin-left: 10px;\n width: 1em;\n height: 1em;\n }\n\n .attachment-item:hover .remove-item {\n transform: scale(1);\n }\n\n .remove-item:hover {\n --icon-color: #333;\n cursor: pointer;\n }\n\n .attachment-name {\n align-self: center;\n font-size: 12px;\n padding: 2px 8px;\n }\n\n #upload-input {\n display: none;\n }\n\n .upload-label {\n display: flex;\n align-items: center;\n }\n\n .upload-icon {\n color: rgb(102, 102, 102);\n }\n\n .add-attachment {\n padding: 1em;\n background-color: rgba(0, 0, 0, 0.05);\n border-radius: var(--curvature);\n color: #aaa;\n margin: 0.5em;\n }\n\n .add-attachment:hover {\n background-color: rgba(0, 0, 0, 0.07);\n cursor: pointer;\n }\n `;\n }\n\n @property({ type: String, attribute: false })\n endpoint = DEFAULT_MEDIA_ENDPOINT;\n\n @property({ type: Boolean })\n pendingDrop: boolean;\n\n @property({ type: String })\n icon = Icon.add;\n\n @property({ type: String })\n accept = ''; //e.g. \".xls,.xlsx\"\n\n @property({ type: Number })\n max = 3;\n\n @property({ type: Array })\n attachments: Attachment[] = [];\n\n @property({ type: Boolean, attribute: false })\n uploading: boolean;\n\n public updated(changes: Map<string, any>): void {\n super.updated(changes);\n if (changes.has('attachments')) {\n // wait one cycle to fire change for tests\n setTimeout(() => {\n this.dispatchEvent(new Event('change'));\n }, 0);\n }\n\n if (changes.has('uploading')) {\n this.dispatchEvent(\n new CustomEvent(CustomEventType.Loading, {\n detail: { loading: this.uploading }\n })\n );\n }\n }\n\n private getAcceptableFiles(evt: DragEvent): File[] {\n const dt = evt.dataTransfer;\n if (dt) {\n const files = [...dt.files];\n return files.filter((file) => verifyAccept(file.type, this.accept));\n }\n }\n\n private handleDragEnter(evt: DragEvent): void {\n this.highlight(evt);\n }\n\n private handleDragOver(evt: DragEvent): void {\n this.highlight(evt);\n }\n\n private handleDragLeave(evt: DragEvent): void {\n this.unhighlight(evt);\n }\n\n private handleDrop(evt: DragEvent): void {\n this.unhighlight(evt);\n if (this.canAcceptAttachments()) {\n this.uploadFiles(this.getAcceptableFiles(evt));\n }\n }\n\n public canAcceptAttachments() {\n return this.attachments.length < this.max;\n }\n\n private highlight(evt: DragEvent): void {\n evt.preventDefault();\n evt.stopPropagation();\n if (this.canAcceptAttachments()) {\n this.pendingDrop = true;\n }\n }\n\n private unhighlight(evt: DragEvent): void {\n evt.preventDefault();\n evt.stopPropagation();\n this.pendingDrop = false;\n }\n\n private addCurrentAttachment(attachmentToAdd: any) {\n this.attachments.push(attachmentToAdd);\n this.requestUpdate('attachments');\n }\n\n private removeCurrentAttachment(attachmentToRemove: any) {\n this.attachments = this.attachments.filter(\n (currentAttachment) => currentAttachment !== attachmentToRemove\n );\n this.requestUpdate('attachments');\n }\n\n private handleRemoveFileClicked(evt: Event): void {\n const target = evt.target as HTMLDivElement;\n const currentAttachmentToRemove = this.attachments.find(\n ({ url }) => url === target.id\n );\n if (currentAttachmentToRemove) {\n this.removeCurrentAttachment(currentAttachmentToRemove);\n }\n }\n\n private handleUploadFileInputChanged(evt: Event): void {\n const target = evt.target as HTMLInputElement;\n const files = target.files;\n this.uploadFiles([...files]);\n }\n\n public uploadFiles(files: File[]): void {\n let filesToUpload = [];\n\n //remove duplicate files that have already been uploaded\n filesToUpload = files.filter((file) => {\n // check our file type against accepts\n if (this.accept) {\n if (!verifyAccept(file.type, this.accept)) {\n return false;\n }\n }\n\n const index = this.attachments.findIndex(\n (value) => value.filename === file.name && value.size === file.size\n );\n if (index === -1) {\n return file;\n }\n });\n\n filesToUpload.map((fileToUpload) => {\n this.uploadFile(fileToUpload);\n });\n }\n\n private uploadFile(file: File): void {\n this.uploading = true;\n\n const url = this.endpoint;\n const payload = new FormData();\n payload.append('file', file);\n postFormData(url, payload)\n .then((response: WebResponse) => {\n if (this.attachments.length < this.max) {\n const attachment = response.json as Attachment;\n if (attachment) {\n this.addCurrentAttachment(attachment);\n }\n }\n })\n .catch((error: WebResponse) => {\n let uploadError = '';\n if (error.status === 400) {\n uploadError = error.json.file[0];\n } else {\n uploadError = 'Server failure';\n }\n console.error(uploadError);\n })\n .finally(() => {\n this.uploading = false;\n });\n }\n\n private renderUploader(): TemplateResult {\n if (this.uploading) {\n return html`<temba-loading units=\"3\" size=\"12\"></temba-loading>`;\n } else {\n return this.attachments.length < this.max\n ? html`<input\n type=\"file\"\n id=\"upload-input\"\n ?multiple=${this.max > 1}\n accept=\"${this.accept}\"\n @change=\"${this.handleUploadFileInputChanged}\"\n />\n <label\n id=\"upload-label\"\n class=\"actions-left upload-label\"\n for=\"upload-input\"\n >\n <div class=\"add-attachment\">\n <temba-icon name=\"${this.icon}\" size=\"1.5\"></temba-icon>\n </div>\n </label>`\n : null;\n }\n }\n\n public render(): TemplateResult {\n return html` <div\n class=${getClasses({ container: true, highlight: this.pendingDrop })}\n @dragenter=\"${this.handleDragEnter}\"\n @dragover=\"${this.handleDragOver}\"\n @dragleave=\"${this.handleDragLeave}\"\n @drop=\"${this.handleDrop}\"\n >\n <div class=\"drop-mask\">\n <div class=\"attachments-list\">\n ${this.attachments.map((validAttachment) => {\n return html`<div class=\"attachment-item\">\n <temba-icon\n class=\"remove-item\"\n @click=\"${this.handleRemoveFileClicked}\"\n id=\"${validAttachment.url}\"\n name=\"${Icon.delete_small}\"\n ></temba-icon>\n ${isImageAttachment(validAttachment)\n ? html`<temba-thumbnail\n url=\"${validAttachment.url}\"\n ></temba-thumbnail>`\n : html`<temba-thumbnail\n label=\"${validAttachment.content_type.split('/')[1]}\"\n ></temba-thumbnail>`}\n </div>`;\n })}\n ${this.renderUploader()}\n </div>\n </div>\n </div>`;\n }\n}\n"]}
@@ -3,10 +3,12 @@ import { property } from 'lit/decorators.js';
3
3
  import { FormElement } from '../FormElement';
4
4
  import { html, css, LitElement } from 'lit';
5
5
  import { CustomEventType } from '../interfaces';
6
+ import { getClasses } from '../utils';
6
7
  export class TemplateEditor extends FormElement {
7
8
  constructor() {
8
9
  super(...arguments);
9
10
  this.lang = 'eng-US';
11
+ this.pickersLoading = {};
10
12
  }
11
13
  static get styles() {
12
14
  return css `
@@ -155,6 +157,12 @@ export class TemplateEditor extends FormElement {
155
157
  variables: this.variables
156
158
  });
157
159
  }
160
+ handleAttachmentLoading(event) {
161
+ const media = event.target;
162
+ const index = parseInt(media.getAttribute('index'));
163
+ this.pickersLoading[index] = event.detail.loading;
164
+ this.requestUpdate();
165
+ }
158
166
  handleAttachmentsChanged(event) {
159
167
  const media = event.target;
160
168
  const index = parseInt(media.getAttribute('index'));
@@ -222,15 +230,19 @@ export class TemplateEditor extends FormElement {
222
230
  variableSpec.type === 'video') {
223
231
  let attachments = [];
224
232
  if (this.variables[variableIndex]) {
225
- const parts = this.variables[variableIndex].split(':', 2);
226
- attachments = [{ url: parts[1], content_type: parts[0] }];
233
+ const parts = this.variables[variableIndex].split(':');
234
+ const content_type = parts[0];
235
+ const url = parts.slice(1).join(':');
236
+ attachments = [{ url, content_type }];
227
237
  }
238
+ const loading = this.pickersLoading[variableIndex];
228
239
  return html `<div
240
+ class=${getClasses({ loading })}
229
241
  style="
230
242
  display: flex;
231
243
  align-items: center;
232
244
  border-radius: var(--curvature);
233
- ${attachments.length === 0
245
+ ${attachments.length === 0 && !loading
234
246
  ? `background-color:rgba(255,0,0,.07);`
235
247
  : ``}
236
248
  "
@@ -243,10 +255,11 @@ export class TemplateEditor extends FormElement {
243
255
  index=${variableIndex}
244
256
  icon="attachment_${variableSpec.type}"
245
257
  attachments=${JSON.stringify(attachments)}
258
+ @temba-loading=${this.handleAttachmentLoading.bind(this)}
246
259
  @change=${this.handleAttachmentsChanged.bind(this)}
247
260
  ></temba-media-picker>
248
261
  <div>
249
- ${attachments.length == 0
262
+ ${attachments.length == 0 && !loading
250
263
  ? html `Attach ${variableSpec.type} to continue`
251
264
  : ''}
252
265
  </div>
@@ -310,7 +323,7 @@ export class TemplateEditor extends FormElement {
310
323
  valuekey="uuid"
311
324
  class="picker"
312
325
  value="${this.template}"
313
- endpoint="${this.url}?comps_as_list=true"
326
+ endpoint="${this.url}"
314
327
  shouldExclude=${(template) => template.status !== 'approved'}
315
328
  placeholder="Select a template"
316
329
  @temba-content-changed=${this.swallowEvent}
@@ -1 +1 @@
1
- {"version":3,"file":"TemplateEditor.js","sourceRoot":"","sources":["../../../src/templates/TemplateEditor.ts"],"names":[],"mappings":"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","sourcesContent":["import { property } from 'lit/decorators.js';\nimport { FormElement } from '../FormElement';\nimport { TemplateResult, html, css, PropertyValueMap, LitElement } from 'lit';\nimport { CustomEventType } from '../interfaces';\nimport { MediaPicker } from '../mediapicker/MediaPicker';\n\ninterface Component {\n name: string;\n type: string;\n content: string;\n variables: { [key: string]: number };\n}\n\ninterface Translation {\n locale: string;\n status: string;\n channel: { uuid: string; name: string };\n components: Component[];\n variables: { type: string }[];\n}\n\ninterface Template {\n created_on: string;\n modified_on: string;\n name: string;\n translations: Translation[];\n uuid: string;\n}\n\nexport class TemplateEditor extends FormElement {\n static shadowRootOptions = {\n ...LitElement.shadowRootOptions,\n delegatesFocus: true\n };\n\n static get styles() {\n return css`\n .component {\n background: #fff;\n border: 1px solid var(--color-widget-border);\n border-radius: var(--curvature);\n padding: 1em;\n margin-top: 1em;\n }\n\n .content {\n margin-bottom: 1em;\n }\n\n .picker {\n margin-bottom: 0.5em;\n display: block;\n }\n .param {\n display: flex;\n margin-bottom: 0.5em;\n align-items: center;\n }\n label {\n margin-right: 0.5em;\n }\n\n .content span {\n margin-right: 0.25em;\n }\n\n .error-message {\n padding-left: 0.5em;\n }\n\n .variable {\n display: inline-block;\n margin: 0.25em 0em;\n margin-right: 0.25em;\n }\n\n .button-wrapper {\n background: #f9f9f9;\n border-radius: var(--curvature);\n padding: 0.5em;\n display: flex;\n flex-direction: column;\n }\n\n .button-header {\n font-weight: normal;\n margin-left: 0.25em;\n margin-bottom: -0.5em;\n font-size: 0.9em;\n color: #777;\n }\n\n .buttons {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n margin-bottom: 1em;\n }\n\n .button {\n background: #fff;\n padding: 0.3em 1em;\n border: 1px solid #e6e6e6;\n border-radius: var(--curvature);\n min-height: 23px;\n display: flex;\n flex-direction: row;\n align-items: center;\n margin-right: 0.5em;\n margin-top: 0.5em;\n align-items: center;\n }\n\n .button .content {\n margin-bottom: 0;\n }\n\n .button .display {\n margin-right: 0.5em;\n background: #f9f9f9;\n padding: 0.25em 1em;\n border-radius: var(--curvature);\n }\n\n temba-textinput,\n temba-completion {\n --temba-textinput-padding: 5px 5px;\n --temba-textinput-font-size: 0.9em;\n line-height: initial;\n }\n\n .template {\n background: #fff;\n border-radius: var(--curvature);\n border: 1px solid var(--color-widget-border);\n padding: 1em;\n line-height: 2.2em;\n max-height: 50vh;\n overflow-y: auto;\n overflow-x: hidden;\n padding-bottom: 0;\n }\n `;\n }\n\n @property({ type: String })\n url: string;\n\n // initial template uuid\n @property({ type: String })\n template: string;\n\n @property({ type: Object })\n selectedTemplate: Template;\n\n @property({ type: String })\n lang = 'eng-US';\n\n @property({ type: Array })\n variables: string[];\n\n @property({ type: Object, attribute: false })\n translation: Translation;\n\n @property({ type: Boolean })\n translating: boolean;\n\n public firstUpdated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(changes);\n }\n\n public updated(changedProperties: Map<string, any>): void {\n super.updated(changedProperties);\n }\n\n private handleTemplateChanged(event: CustomEvent) {\n const prev = this.selectedTemplate;\n this.selectedTemplate = (event.target as any).values[0] as Template;\n const [lang, loc] = this.lang.split('-');\n if (this.selectedTemplate) {\n this.selectedTemplate.translations.forEach((translation) => {\n if (\n translation.locale === this.lang ||\n (!loc && translation.locale.split('-')[0] === lang)\n ) {\n this.translation = translation;\n // initialize our variables array\n const newVariables = new Array(\n (translation.variables || []).length\n ).fill('');\n\n if (!prev) {\n // copy our previous variables into newVariables\n if (this.variables) {\n this.variables.forEach((variable, index) => {\n newVariables[index] = variable;\n });\n }\n }\n this.variables = newVariables;\n }\n });\n } else {\n this.translation = null;\n }\n\n this.fireCustomEvent(CustomEventType.ContextChanged, {\n template: this.selectedTemplate,\n translation: this.translation,\n variables: this.variables\n });\n }\n\n private handleAttachmentsChanged(event: CustomEvent) {\n const media = event.target as MediaPicker;\n const index = parseInt(media.getAttribute('index'));\n\n if (media.attachments.length === 0) {\n this.variables[index] = '';\n } else {\n const attachment = media.attachments[0];\n if (attachment.url && attachment.content_type) {\n this.variables[index] = `${attachment.content_type}:${attachment.url}`;\n } else {\n this.variables[index] = ``;\n }\n }\n this.fireContentChange();\n }\n\n private handleVariableChanged(event: CustomEvent) {\n const target = event.target as HTMLInputElement;\n const variableIndex = parseInt(target.getAttribute('index'));\n this.variables[variableIndex] = target.value;\n this.fireContentChange();\n }\n\n private fireContentChange() {\n this.fireCustomEvent(CustomEventType.ContentChanged, {\n template: this.selectedTemplate,\n translation: this.translation,\n variables: this.variables\n });\n }\n\n private renderVariables(component: Component) {\n // create a regex match based on the variable names\n const variableRegex = new RegExp(\n `{{(${Object.keys(component.variables || []).join('|')})}}`,\n 'g'\n );\n\n let variables = null;\n\n let parts = [];\n if (component.content && component.content.trim().length > 0) {\n parts = component.content?.split(variableRegex) || [];\n }\n\n if (parts.length > 0) {\n variables = parts.map((part, index) => {\n if (index % 2 === 0) {\n return html`<span class=\"text\">${part}</span>`;\n }\n const variableIndex = component.variables[part];\n return html`<temba-completion\n class=\"variable\"\n type=\"text\"\n value=${variableIndex < this.variables.length\n ? this.variables[variableIndex]\n : null}\n @keyup=${this.handleVariableChanged}\n name=\"${component.name}\"\n index=\"${variableIndex}\"\n placeholder=\"{{${part}}}\"\n ></temba-completion>`;\n });\n } else {\n variables = Object.values(component.variables).map((variableIndex) => {\n const variableSpec = this.translation.variables[variableIndex];\n if (\n variableSpec.type === 'image' ||\n variableSpec.type === 'document' ||\n variableSpec.type === 'audio' ||\n variableSpec.type === 'video'\n ) {\n let attachments = [];\n if (this.variables[variableIndex]) {\n const parts = this.variables[variableIndex].split(':', 2);\n attachments = [{ url: parts[1], content_type: parts[0] }];\n }\n\n return html`<div\n style=\"\n display: flex; \n align-items: center; \n border-radius: var(--curvature);\n ${attachments.length === 0\n ? `background-color:rgba(255,0,0,.07);`\n : ``}\n \"\n >\n <temba-media-picker\n accept=\"${variableSpec.type === 'document'\n ? 'application/pdf'\n : variableSpec.type + '/*'}\"\n max=\"1\"\n index=${variableIndex}\n icon=\"attachment_${variableSpec.type}\"\n attachments=${JSON.stringify(attachments)}\n @change=${this.handleAttachmentsChanged.bind(this)}\n ></temba-media-picker>\n <div>\n ${attachments.length == 0\n ? html`Attach ${variableSpec.type} to continue`\n : ''}\n </div>\n </div>`;\n }\n });\n }\n\n return html`<div class=\"content\">${variables}</div> `;\n }\n\n public renderComponents(components: Component[]): TemplateResult {\n const nonButtons = components\n .filter((comp) => !comp.type.startsWith('button/'))\n .map(\n (component) =>\n html`<div class=\"${component['name']}\">\n ${this.renderVariables(component)}\n </div>`\n );\n const buttonComponents = components.filter((comp) =>\n comp.type.startsWith('button/')\n );\n const buttons =\n buttonComponents.length > 0 ? this.renderButtons(buttonComponents) : null;\n return html`<div class=\"main\">${nonButtons}</div>\n <div class=\"buttons\">\n ${buttons}\n <div></div>\n </div>`;\n }\n\n public renderButtons(components): TemplateResult {\n const buttons = components.map((component) => {\n if (component.display) {\n return html`\n <div class=\"button\">\n <div class=\"display\">${component.display}</div>\n ${this.renderVariables(component)}\n </div>\n `;\n } else {\n return html`\n <div class=\"button\">${this.renderVariables(component)}</div>\n `;\n }\n });\n return html`<div class=\"button-wrapper\">\n <div class=\"button-header\">Template Buttons</div>\n <div class=\"buttons\">${buttons}</div>\n </div>`;\n }\n\n public render(): TemplateResult {\n let content = null;\n if (this.translation) {\n content = this.renderComponents(this.translation.components);\n } else {\n content = html`<div class=\"error-message\">\n No approved translation was found for current language.\n </div>`;\n }\n\n return html`\n <div>\n <temba-select\n searchable\n ?clearable=${!this.translating}\n ?disabled=${this.translating}\n valuekey=\"uuid\"\n class=\"picker\"\n value=\"${this.template}\"\n endpoint=\"${this.url}?comps_as_list=true\"\n shouldExclude=${(template) => template.status !== 'approved'}\n placeholder=\"Select a template\"\n @temba-content-changed=${this.swallowEvent}\n @change=${this.handleTemplateChanged}\n >\n </temba-select>\n\n ${this.template ? html` <div class=\"template\">${content}</div>` : null}\n </div>\n `;\n }\n}\n"]}
1
+ {"version":3,"file":"TemplateEditor.js","sourceRoot":"","sources":["../../../src/templates/TemplateEditor.ts"],"names":[],"mappings":"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","sourcesContent":["import { property } from 'lit/decorators.js';\nimport { FormElement } from '../FormElement';\nimport { TemplateResult, html, css, PropertyValueMap, LitElement } from 'lit';\nimport { CustomEventType } from '../interfaces';\nimport { MediaPicker } from '../mediapicker/MediaPicker';\nimport { getClasses } from '../utils';\n\ninterface Component {\n name: string;\n type: string;\n content: string;\n variables: { [key: string]: number };\n}\n\ninterface Translation {\n locale: string;\n status: string;\n channel: { uuid: string; name: string };\n components: Component[];\n variables: { type: string }[];\n}\n\ninterface Template {\n created_on: string;\n modified_on: string;\n name: string;\n translations: Translation[];\n uuid: string;\n}\n\nexport class TemplateEditor extends FormElement {\n static shadowRootOptions = {\n ...LitElement.shadowRootOptions,\n delegatesFocus: true\n };\n\n static get styles() {\n return css`\n .component {\n background: #fff;\n border: 1px solid var(--color-widget-border);\n border-radius: var(--curvature);\n padding: 1em;\n margin-top: 1em;\n }\n\n .content {\n margin-bottom: 1em;\n }\n\n .picker {\n margin-bottom: 0.5em;\n display: block;\n }\n .param {\n display: flex;\n margin-bottom: 0.5em;\n align-items: center;\n }\n label {\n margin-right: 0.5em;\n }\n\n .content span {\n margin-right: 0.25em;\n }\n\n .error-message {\n padding-left: 0.5em;\n }\n\n .variable {\n display: inline-block;\n margin: 0.25em 0em;\n margin-right: 0.25em;\n }\n\n .button-wrapper {\n background: #f9f9f9;\n border-radius: var(--curvature);\n padding: 0.5em;\n display: flex;\n flex-direction: column;\n }\n\n .button-header {\n font-weight: normal;\n margin-left: 0.25em;\n margin-bottom: -0.5em;\n font-size: 0.9em;\n color: #777;\n }\n\n .buttons {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n margin-bottom: 1em;\n }\n\n .button {\n background: #fff;\n padding: 0.3em 1em;\n border: 1px solid #e6e6e6;\n border-radius: var(--curvature);\n min-height: 23px;\n display: flex;\n flex-direction: row;\n align-items: center;\n margin-right: 0.5em;\n margin-top: 0.5em;\n align-items: center;\n }\n\n .button .content {\n margin-bottom: 0;\n }\n\n .button .display {\n margin-right: 0.5em;\n background: #f9f9f9;\n padding: 0.25em 1em;\n border-radius: var(--curvature);\n }\n\n temba-textinput,\n temba-completion {\n --temba-textinput-padding: 5px 5px;\n --temba-textinput-font-size: 0.9em;\n line-height: initial;\n }\n\n .template {\n background: #fff;\n border-radius: var(--curvature);\n border: 1px solid var(--color-widget-border);\n padding: 1em;\n line-height: 2.2em;\n max-height: 50vh;\n overflow-y: auto;\n overflow-x: hidden;\n padding-bottom: 0;\n }\n `;\n }\n\n @property({ type: String })\n url: string;\n\n // initial template uuid\n @property({ type: String })\n template: string;\n\n @property({ type: Object })\n selectedTemplate: Template;\n\n @property({ type: String })\n lang = 'eng-US';\n\n @property({ type: Array })\n variables: string[];\n\n @property({ type: Object, attribute: false })\n translation: Translation;\n\n @property({ type: Boolean })\n translating: boolean;\n\n pickersLoading: { [key: number]: boolean } = {};\n\n public firstUpdated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(changes);\n }\n\n public updated(changedProperties: Map<string, any>): void {\n super.updated(changedProperties);\n }\n\n private handleTemplateChanged(event: CustomEvent) {\n const prev = this.selectedTemplate;\n this.selectedTemplate = (event.target as any).values[0] as Template;\n const [lang, loc] = this.lang.split('-');\n if (this.selectedTemplate) {\n this.selectedTemplate.translations.forEach((translation) => {\n if (\n translation.locale === this.lang ||\n (!loc && translation.locale.split('-')[0] === lang)\n ) {\n this.translation = translation;\n // initialize our variables array\n const newVariables = new Array(\n (translation.variables || []).length\n ).fill('');\n\n if (!prev) {\n // copy our previous variables into newVariables\n if (this.variables) {\n this.variables.forEach((variable, index) => {\n newVariables[index] = variable;\n });\n }\n }\n this.variables = newVariables;\n }\n });\n } else {\n this.translation = null;\n }\n\n this.fireCustomEvent(CustomEventType.ContextChanged, {\n template: this.selectedTemplate,\n translation: this.translation,\n variables: this.variables\n });\n }\n\n private handleAttachmentLoading(event: CustomEvent) {\n const media = event.target as MediaPicker;\n const index = parseInt(media.getAttribute('index'));\n this.pickersLoading[index] = event.detail.loading;\n this.requestUpdate();\n }\n\n private handleAttachmentsChanged(event: CustomEvent) {\n const media = event.target as MediaPicker;\n const index = parseInt(media.getAttribute('index'));\n\n if (media.attachments.length === 0) {\n this.variables[index] = '';\n } else {\n const attachment = media.attachments[0];\n if (attachment.url && attachment.content_type) {\n this.variables[index] = `${attachment.content_type}:${attachment.url}`;\n } else {\n this.variables[index] = ``;\n }\n }\n this.fireContentChange();\n }\n\n private handleVariableChanged(event: CustomEvent) {\n const target = event.target as HTMLInputElement;\n const variableIndex = parseInt(target.getAttribute('index'));\n this.variables[variableIndex] = target.value;\n this.fireContentChange();\n }\n\n private fireContentChange() {\n this.fireCustomEvent(CustomEventType.ContentChanged, {\n template: this.selectedTemplate,\n translation: this.translation,\n variables: this.variables\n });\n }\n\n private renderVariables(component: Component) {\n // create a regex match based on the variable names\n const variableRegex = new RegExp(\n `{{(${Object.keys(component.variables || []).join('|')})}}`,\n 'g'\n );\n\n let variables = null;\n\n let parts = [];\n if (component.content && component.content.trim().length > 0) {\n parts = component.content?.split(variableRegex) || [];\n }\n\n if (parts.length > 0) {\n variables = parts.map((part, index) => {\n if (index % 2 === 0) {\n return html`<span class=\"text\">${part}</span>`;\n }\n const variableIndex = component.variables[part];\n return html`<temba-completion\n class=\"variable\"\n type=\"text\"\n value=${variableIndex < this.variables.length\n ? this.variables[variableIndex]\n : null}\n @keyup=${this.handleVariableChanged}\n name=\"${component.name}\"\n index=\"${variableIndex}\"\n placeholder=\"{{${part}}}\"\n ></temba-completion>`;\n });\n } else {\n variables = Object.values(component.variables).map((variableIndex) => {\n const variableSpec = this.translation.variables[variableIndex];\n if (\n variableSpec.type === 'image' ||\n variableSpec.type === 'document' ||\n variableSpec.type === 'audio' ||\n variableSpec.type === 'video'\n ) {\n let attachments = [];\n if (this.variables[variableIndex]) {\n const parts = this.variables[variableIndex].split(':');\n const content_type = parts[0];\n const url = parts.slice(1).join(':');\n attachments = [{ url, content_type }];\n }\n\n const loading = this.pickersLoading[variableIndex];\n\n return html`<div\n class=${getClasses({ loading })}\n style=\"\n display: flex; \n align-items: center; \n border-radius: var(--curvature);\n ${attachments.length === 0 && !loading\n ? `background-color:rgba(255,0,0,.07);`\n : ``}\n \"\n >\n <temba-media-picker\n accept=\"${variableSpec.type === 'document'\n ? 'application/pdf'\n : variableSpec.type + '/*'}\"\n max=\"1\"\n index=${variableIndex}\n icon=\"attachment_${variableSpec.type}\"\n attachments=${JSON.stringify(attachments)}\n @temba-loading=${this.handleAttachmentLoading.bind(this)}\n @change=${this.handleAttachmentsChanged.bind(this)}\n ></temba-media-picker>\n <div>\n ${attachments.length == 0 && !loading\n ? html`Attach ${variableSpec.type} to continue`\n : ''}\n </div>\n </div>`;\n }\n });\n }\n\n return html`<div class=\"content\">${variables}</div> `;\n }\n\n public renderComponents(components: Component[]): TemplateResult {\n const nonButtons = components\n .filter((comp) => !comp.type.startsWith('button/'))\n .map(\n (component) =>\n html`<div class=\"${component['name']}\">\n ${this.renderVariables(component)}\n </div>`\n );\n const buttonComponents = components.filter((comp) =>\n comp.type.startsWith('button/')\n );\n const buttons =\n buttonComponents.length > 0 ? this.renderButtons(buttonComponents) : null;\n return html`<div class=\"main\">${nonButtons}</div>\n <div class=\"buttons\">\n ${buttons}\n <div></div>\n </div>`;\n }\n\n public renderButtons(components): TemplateResult {\n const buttons = components.map((component) => {\n if (component.display) {\n return html`\n <div class=\"button\">\n <div class=\"display\">${component.display}</div>\n ${this.renderVariables(component)}\n </div>\n `;\n } else {\n return html`\n <div class=\"button\">${this.renderVariables(component)}</div>\n `;\n }\n });\n return html`<div class=\"button-wrapper\">\n <div class=\"button-header\">Template Buttons</div>\n <div class=\"buttons\">${buttons}</div>\n </div>`;\n }\n\n public render(): TemplateResult {\n let content = null;\n if (this.translation) {\n content = this.renderComponents(this.translation.components);\n } else {\n content = html`<div class=\"error-message\">\n No approved translation was found for current language.\n </div>`;\n }\n\n return html`\n <div>\n <temba-select\n searchable\n ?clearable=${!this.translating}\n ?disabled=${this.translating}\n valuekey=\"uuid\"\n class=\"picker\"\n value=\"${this.template}\"\n endpoint=\"${this.url}\"\n shouldExclude=${(template) => template.status !== 'approved'}\n placeholder=\"Select a template\"\n @temba-content-changed=${this.swallowEvent}\n @change=${this.handleTemplateChanged}\n >\n </temba-select>\n\n ${this.template ? html` <div class=\"template\">${content}</div>` : null}\n </div>\n `;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyaruka/temba-components",
3
- "version": "0.91.5",
3
+ "version": "0.91.6",
4
4
  "description": "Web components to support rapidpro and related projects",
5
5
  "author": "Nyaruka <code@nyaruka.coim>",
6
6
  "main": "dist/index.js",
package/src/interfaces.ts CHANGED
@@ -240,6 +240,7 @@ export type KeyedAssets = { [assetType: string]: string[] };
240
240
 
241
241
  export enum CustomEventType {
242
242
  Loaded = 'temba-loaded',
243
+ Loading = 'temba-loading',
243
244
  Canceled = 'temba-canceled',
244
245
  CursorChanged = 'temba-cursor-changed',
245
246
  Refreshed = 'temba-refreshed',
@@ -1,7 +1,7 @@
1
1
  import { TemplateResult, css, html } from 'lit';
2
2
  import { RapidElement } from '../RapidElement';
3
3
  import { property } from 'lit/decorators.js';
4
- import { Attachment } from '../interfaces';
4
+ import { Attachment, CustomEventType } from '../interfaces';
5
5
  import { Icon } from '../vectoricon';
6
6
  import {
7
7
  DEFAULT_MEDIA_ENDPOINT,
@@ -149,6 +149,14 @@ export class MediaPicker extends RapidElement {
149
149
  this.dispatchEvent(new Event('change'));
150
150
  }, 0);
151
151
  }
152
+
153
+ if (changes.has('uploading')) {
154
+ this.dispatchEvent(
155
+ new CustomEvent(CustomEventType.Loading, {
156
+ detail: { loading: this.uploading }
157
+ })
158
+ );
159
+ }
152
160
  }
153
161
 
154
162
  private getAcceptableFiles(evt: DragEvent): File[] {
@@ -3,6 +3,7 @@ import { FormElement } from '../FormElement';
3
3
  import { TemplateResult, html, css, PropertyValueMap, LitElement } from 'lit';
4
4
  import { CustomEventType } from '../interfaces';
5
5
  import { MediaPicker } from '../mediapicker/MediaPicker';
6
+ import { getClasses } from '../utils';
6
7
 
7
8
  interface Component {
8
9
  name: string;
@@ -165,6 +166,8 @@ export class TemplateEditor extends FormElement {
165
166
  @property({ type: Boolean })
166
167
  translating: boolean;
167
168
 
169
+ pickersLoading: { [key: number]: boolean } = {};
170
+
168
171
  public firstUpdated(
169
172
  changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
170
173
  ): void {
@@ -213,6 +216,13 @@ export class TemplateEditor extends FormElement {
213
216
  });
214
217
  }
215
218
 
219
+ private handleAttachmentLoading(event: CustomEvent) {
220
+ const media = event.target as MediaPicker;
221
+ const index = parseInt(media.getAttribute('index'));
222
+ this.pickersLoading[index] = event.detail.loading;
223
+ this.requestUpdate();
224
+ }
225
+
216
226
  private handleAttachmentsChanged(event: CustomEvent) {
217
227
  const media = event.target as MediaPicker;
218
228
  const index = parseInt(media.getAttribute('index'));
@@ -288,16 +298,21 @@ export class TemplateEditor extends FormElement {
288
298
  ) {
289
299
  let attachments = [];
290
300
  if (this.variables[variableIndex]) {
291
- const parts = this.variables[variableIndex].split(':', 2);
292
- attachments = [{ url: parts[1], content_type: parts[0] }];
301
+ const parts = this.variables[variableIndex].split(':');
302
+ const content_type = parts[0];
303
+ const url = parts.slice(1).join(':');
304
+ attachments = [{ url, content_type }];
293
305
  }
294
306
 
307
+ const loading = this.pickersLoading[variableIndex];
308
+
295
309
  return html`<div
310
+ class=${getClasses({ loading })}
296
311
  style="
297
312
  display: flex;
298
313
  align-items: center;
299
314
  border-radius: var(--curvature);
300
- ${attachments.length === 0
315
+ ${attachments.length === 0 && !loading
301
316
  ? `background-color:rgba(255,0,0,.07);`
302
317
  : ``}
303
318
  "
@@ -310,10 +325,11 @@ export class TemplateEditor extends FormElement {
310
325
  index=${variableIndex}
311
326
  icon="attachment_${variableSpec.type}"
312
327
  attachments=${JSON.stringify(attachments)}
328
+ @temba-loading=${this.handleAttachmentLoading.bind(this)}
313
329
  @change=${this.handleAttachmentsChanged.bind(this)}
314
330
  ></temba-media-picker>
315
331
  <div>
316
- ${attachments.length == 0
332
+ ${attachments.length == 0 && !loading
317
333
  ? html`Attach ${variableSpec.type} to continue`
318
334
  : ''}
319
335
  </div>
@@ -386,7 +402,7 @@ export class TemplateEditor extends FormElement {
386
402
  valuekey="uuid"
387
403
  class="picker"
388
404
  value="${this.template}"
389
- endpoint="${this.url}?comps_as_list=true"
405
+ endpoint="${this.url}"
390
406
  shouldExclude=${(template) => template.status !== 'approved'}
391
407
  placeholder="Select a template"
392
408
  @temba-content-changed=${this.swallowEvent}