@nyaruka/temba-components 0.76.0 → 0.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/sw.js CHANGED
@@ -1,2 +1,2 @@
1
- if(!self.define){let e,t={};const o=(o,n)=>(o=new URL(o+".js",n).href,t[o]||new Promise((t=>{if("document"in self){const e=document.createElement("script");e.src=o,e.onload=t,document.head.appendChild(e)}else e=o,importScripts(o),t()})).then((()=>{let e=t[o];if(!e)throw new Error(`Module ${o} didn’t register its module`);return e})));self.define=(n,s)=>{const i=e||("document"in self?document.currentScript.src:"")||location.href;if(t[i])return;let r={};const d=e=>o(e,i),l={module:{uri:i},exports:r,require:d};t[i]=Promise.all(n.map((e=>l[e]||d(e)))).then((e=>(s(...e),r)))}}define(["./workbox-919adfb7"],(function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"3c275eb2.js",revision:"2846b1048f3f0f0896d088caca6cb22b"},{url:"templates/components-body.html",revision:"6a880bab9e5dc5ef32839ad3a340fd23"},{url:"templates/components-head.html",revision:"43d59176beaf04d5ed6e2d714ad0815a"}],{}),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"))),e.registerRoute("polyfills/*.js",new e.CacheFirst,"GET")}));
1
+ if(!self.define){let e,t={};const o=(o,n)=>(o=new URL(o+".js",n).href,t[o]||new Promise((t=>{if("document"in self){const e=document.createElement("script");e.src=o,e.onload=t,document.head.appendChild(e)}else e=o,importScripts(o),t()})).then((()=>{let e=t[o];if(!e)throw new Error(`Module ${o} didn’t register its module`);return e})));self.define=(n,s)=>{const i=e||("document"in self?document.currentScript.src:"")||location.href;if(t[i])return;let r={};const l=e=>o(e,i),c={module:{uri:i},exports:r,require:l};t[i]=Promise.all(n.map((e=>c[e]||l(e)))).then((e=>(s(...e),r)))}}define(["./workbox-919adfb7"],(function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"736dd89d.js",revision:"5315b93c1141129c63615e9f0c3a9785"},{url:"templates/components-body.html",revision:"7d327fa077f6518e4afec37f08908225"},{url:"templates/components-head.html",revision:"8414a36f926372f37082d7cdb07cb5b6"}],{}),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"))),e.registerRoute("polyfills/*.js",new e.CacheFirst,"GET")}));
2
2
  //# sourceMappingURL=sw.js.map
package/dist/sw.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sw.js","sources":["../../tmp/c4a636b426ba507bda2690c94adcf5c1/sw.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from '/workspaces/temba-components/node_modules/workbox-routing/registerRoute.mjs';\nimport {CacheFirst as workbox_strategies_CacheFirst} from '/workspaces/temba-components/node_modules/workbox-strategies/CacheFirst.mjs';\nimport {clientsClaim as workbox_core_clientsClaim} from '/workspaces/temba-components/node_modules/workbox-core/clientsClaim.mjs';\nimport {precacheAndRoute as workbox_precaching_precacheAndRoute} from '/workspaces/temba-components/node_modules/workbox-precaching/precacheAndRoute.mjs';\nimport {NavigationRoute as workbox_routing_NavigationRoute} from '/workspaces/temba-components/node_modules/workbox-routing/NavigationRoute.mjs';\nimport {createHandlerBoundToURL as workbox_precaching_createHandlerBoundToURL} from '/workspaces/temba-components/node_modules/workbox-precaching/createHandlerBoundToURL.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n/**\n * The precacheAndRoute() method efficiently caches and responds to\n * requests for URLs in the manifest.\n * See https://goo.gl/S9QRab\n */\nworkbox_precaching_precacheAndRoute([\n {\n \"url\": \"3c275eb2.js\",\n \"revision\": \"2846b1048f3f0f0896d088caca6cb22b\"\n },\n {\n \"url\": \"templates/components-body.html\",\n \"revision\": \"6a880bab9e5dc5ef32839ad3a340fd23\"\n },\n {\n \"url\": \"templates/components-head.html\",\n \"revision\": \"43d59176beaf04d5ed6e2d714ad0815a\"\n }\n], {});\n\nworkbox_routing_registerRoute(new workbox_routing_NavigationRoute(workbox_precaching_createHandlerBoundToURL(\"/index.html\")));\n\n\nworkbox_routing_registerRoute(\"polyfills/*.js\", new workbox_strategies_CacheFirst(), 'GET');\n\n\n\n\n"],"names":["self","skipWaiting","workbox_core_clientsClaim","workbox_precaching_precacheAndRoute","url","revision","workbox","registerRoute","workbox_routing_NavigationRoute","workbox_precaching_createHandlerBoundToURL","workbox_strategies_CacheFirst"],"mappings":"0nBAwBAA,KAAKC,cAELC,EAAAA,eAQAC,EAAAA,iBAAoC,CAClC,CACEC,IAAO,cACPC,SAAY,oCAEd,CACED,IAAO,iCACPC,SAAY,oCAEd,CACED,IAAO,iCACPC,SAAY,qCAEb,CAAE,GAEwBC,EAAAC,cAAC,IAAIC,EAAAA,gBAAgCC,EAAAA,wBAA2C,iBAGhFH,EAAAC,cAAC,iBAAkB,IAAIG,aAAiC"}
1
+ {"version":3,"file":"sw.js","sources":["../../tmp/418d797d859dfd53ace27b464065ddae/sw.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from '/workspaces/temba-components/node_modules/workbox-routing/registerRoute.mjs';\nimport {CacheFirst as workbox_strategies_CacheFirst} from '/workspaces/temba-components/node_modules/workbox-strategies/CacheFirst.mjs';\nimport {clientsClaim as workbox_core_clientsClaim} from '/workspaces/temba-components/node_modules/workbox-core/clientsClaim.mjs';\nimport {precacheAndRoute as workbox_precaching_precacheAndRoute} from '/workspaces/temba-components/node_modules/workbox-precaching/precacheAndRoute.mjs';\nimport {NavigationRoute as workbox_routing_NavigationRoute} from '/workspaces/temba-components/node_modules/workbox-routing/NavigationRoute.mjs';\nimport {createHandlerBoundToURL as workbox_precaching_createHandlerBoundToURL} from '/workspaces/temba-components/node_modules/workbox-precaching/createHandlerBoundToURL.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n/**\n * The precacheAndRoute() method efficiently caches and responds to\n * requests for URLs in the manifest.\n * See https://goo.gl/S9QRab\n */\nworkbox_precaching_precacheAndRoute([\n {\n \"url\": \"736dd89d.js\",\n \"revision\": \"5315b93c1141129c63615e9f0c3a9785\"\n },\n {\n \"url\": \"templates/components-body.html\",\n \"revision\": \"7d327fa077f6518e4afec37f08908225\"\n },\n {\n \"url\": \"templates/components-head.html\",\n \"revision\": \"8414a36f926372f37082d7cdb07cb5b6\"\n }\n], {});\n\nworkbox_routing_registerRoute(new workbox_routing_NavigationRoute(workbox_precaching_createHandlerBoundToURL(\"/index.html\")));\n\n\nworkbox_routing_registerRoute(\"polyfills/*.js\", new workbox_strategies_CacheFirst(), 'GET');\n\n\n\n\n"],"names":["self","skipWaiting","workbox_core_clientsClaim","workbox_precaching_precacheAndRoute","url","revision","workbox","registerRoute","workbox_routing_NavigationRoute","workbox_precaching_createHandlerBoundToURL","workbox_strategies_CacheFirst"],"mappings":"0nBAwBAA,KAAKC,cAELC,EAAAA,eAQAC,EAAAA,iBAAoC,CAClC,CACEC,IAAO,cACPC,SAAY,oCAEd,CACED,IAAO,iCACPC,SAAY,oCAEd,CACED,IAAO,iCACPC,SAAY,qCAEb,CAAE,GAEwBC,EAAAC,cAAC,IAAIC,EAAAA,gBAAgCC,EAAAA,wBAA2C,iBAGhFH,EAAAC,cAAC,iBAAkB,IAAIG,aAAiC"}
@@ -1 +1 @@
1
- <script type="module" src="{{STATIC_URL}}@nyaruka/temba-components/dist/3c275eb2.js"></script><script>window.TEMBA_COMPONENTS_VERSION="0.76.0"</script>
1
+ <script type="module" src="{{STATIC_URL}}@nyaruka/temba-components/dist/736dd89d.js"></script><script>window.TEMBA_COMPONENTS_VERSION="0.78.0"</script>
@@ -1 +1 @@
1
- <script src="{{STATIC_URL}}croppie/croppie.js"></script><link rel="modulepreload" href="{{STATIC_URL}}@nyaruka/temba-components/dist/3c275eb2.js" crossorigin="anonymous">
1
+ <script src="{{STATIC_URL}}croppie/croppie.js"></script><link rel="modulepreload" href="{{STATIC_URL}}@nyaruka/temba-components/dist/736dd89d.js" crossorigin="anonymous">
@@ -20,10 +20,7 @@ export class TembaDate extends RapidElement {
20
20
  static get styles() {
21
21
  return css `
22
22
  .date {
23
- display: block;
24
- overflow: hidden;
25
- text-overflow: ellipsis;
26
- white-space: nowrap;
23
+ display: inline;
27
24
  }
28
25
  `;
29
26
  }
@@ -59,24 +56,22 @@ export class TembaDate extends RapidElement {
59
56
  else if (this.display === Display.relative) {
60
57
  const minutes = Math.abs(this.datetime.diffNow().milliseconds / 1000 / 60);
61
58
  if (minutes < 1) {
62
- return html `<div
59
+ return html `<span
63
60
  class="date"
64
61
  title="${this.datetime.toLocaleString(Display.datetime)}"
65
- >
66
- just now
67
- </div>`;
62
+ >just now</span
63
+ >`;
68
64
  }
69
65
  formatted = this.store.getShortDuration(this.datetime);
70
66
  }
71
67
  else if (this.display === Display.duration) {
72
68
  const minutes = Math.abs(this.datetime.diffNow().milliseconds / 1000 / 60);
73
69
  if (minutes < 1) {
74
- return html `<div
70
+ return html `<span
75
71
  class="date"
76
72
  title="${this.datetime.toLocaleString(Display.datetime)}"
77
- >
78
- just now
79
- </div>`;
73
+ >just now</span
74
+ >`;
80
75
  }
81
76
  formatted = this.store.getShortDuration(this.datetime);
82
77
  }
@@ -86,12 +81,11 @@ export class TembaDate extends RapidElement {
86
81
  else {
87
82
  formatted = this.datetime.toLocaleString(Display[this.display]);
88
83
  }
89
- return html `<div
84
+ return html `<span
90
85
  class="date"
91
86
  title="${this.datetime.toLocaleString(Display.datetime)}"
92
- >
93
- ${formatted}
94
- </div>`;
87
+ >${formatted}</span
88
+ >`;
95
89
  }
96
90
  }
97
91
  }
@@ -1 +1 @@
1
- {"version":3,"file":"TembaDate.js","sourceRoot":"","sources":["../../../src/date/TembaDate.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { RapidElement } from '../RapidElement';\nimport { Store } from '../store/Store';\nimport { DateTime } from 'luxon';\n\nexport const Display = {\n date: DateTime.DATE_SHORT,\n datetime: DateTime.DATETIME_SHORT,\n time: DateTime.TIME_SIMPLE,\n timedate: 'timedate',\n duration: 'duration',\n relative: 'relative',\n day: 'LLL d',\n};\n\nexport class TembaDate extends RapidElement {\n static get styles() {\n return css`\n .date {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n `;\n }\n\n @property({ type: String })\n value: string;\n\n @property({ type: String })\n display = 'date';\n\n @property({ type: Object, attribute: false })\n datetime: DateTime;\n\n store: Store;\n\n protected firstUpdated(\n changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(changedProperties);\n this.store = document.querySelector('temba-store');\n }\n\n protected updated(\n changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changedProperties);\n if (changedProperties.has('value')) {\n this.datetime = DateTime.fromISO(this.value);\n }\n }\n\n public connectedCallback(): void {\n super.connectedCallback();\n }\n\n public render(): TemplateResult {\n if (this.datetime && this.store) {\n this.datetime.setLocale(this.store.getLocale());\n\n let formatted = '';\n if (this.display === Display.timedate) {\n const hours = Math.abs(\n this.datetime.diffNow().milliseconds / 1000 / 60 / 60\n );\n if (hours < 24) {\n formatted = this.datetime.toLocaleString(Display.time);\n } else if (hours < 24 * 365) {\n formatted = this.datetime.toFormat(Display.day);\n } else {\n formatted = this.datetime.toLocaleString(Display.date);\n }\n } else if (this.display === Display.relative) {\n const minutes = Math.abs(\n this.datetime.diffNow().milliseconds / 1000 / 60\n );\n if (minutes < 1) {\n return html`<div\n class=\"date\"\n title=\"${this.datetime.toLocaleString(Display.datetime)}\"\n >\n just now\n </div>`;\n }\n\n formatted = this.store.getShortDuration(this.datetime);\n } else if (this.display === Display.duration) {\n const minutes = Math.abs(\n this.datetime.diffNow().milliseconds / 1000 / 60\n );\n if (minutes < 1) {\n return html`<div\n class=\"date\"\n title=\"${this.datetime.toLocaleString(Display.datetime)}\"\n >\n just now\n </div>`;\n }\n formatted = this.store.getShortDuration(this.datetime);\n } else if (this.display === Display.day) {\n formatted = this.datetime.toLocaleString(Display.day);\n } else {\n formatted = this.datetime.toLocaleString(Display[this.display]);\n }\n return html`<div\n class=\"date\"\n title=\"${this.datetime.toLocaleString(Display.datetime)}\"\n >\n ${formatted}\n </div>`;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"TembaDate.js","sourceRoot":"","sources":["../../../src/date/TembaDate.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { RapidElement } from '../RapidElement';\nimport { Store } from '../store/Store';\nimport { DateTime } from 'luxon';\n\nexport const Display = {\n date: DateTime.DATE_SHORT,\n datetime: DateTime.DATETIME_SHORT,\n time: DateTime.TIME_SIMPLE,\n timedate: 'timedate',\n duration: 'duration',\n relative: 'relative',\n day: 'LLL d',\n};\n\nexport class TembaDate extends RapidElement {\n static get styles() {\n return css`\n .date {\n display: inline;\n }\n `;\n }\n\n @property({ type: String })\n value: string;\n\n @property({ type: String })\n display = 'date';\n\n @property({ type: Object, attribute: false })\n datetime: DateTime;\n\n store: Store;\n\n protected firstUpdated(\n changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(changedProperties);\n this.store = document.querySelector('temba-store');\n }\n\n protected updated(\n changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changedProperties);\n if (changedProperties.has('value')) {\n this.datetime = DateTime.fromISO(this.value);\n }\n }\n\n public connectedCallback(): void {\n super.connectedCallback();\n }\n\n public render(): TemplateResult {\n if (this.datetime && this.store) {\n this.datetime.setLocale(this.store.getLocale());\n\n let formatted = '';\n if (this.display === Display.timedate) {\n const hours = Math.abs(\n this.datetime.diffNow().milliseconds / 1000 / 60 / 60\n );\n if (hours < 24) {\n formatted = this.datetime.toLocaleString(Display.time);\n } else if (hours < 24 * 365) {\n formatted = this.datetime.toFormat(Display.day);\n } else {\n formatted = this.datetime.toLocaleString(Display.date);\n }\n } else if (this.display === Display.relative) {\n const minutes = Math.abs(\n this.datetime.diffNow().milliseconds / 1000 / 60\n );\n if (minutes < 1) {\n return html`<span\n class=\"date\"\n title=\"${this.datetime.toLocaleString(Display.datetime)}\"\n >just now</span\n >`;\n }\n\n formatted = this.store.getShortDuration(this.datetime);\n } else if (this.display === Display.duration) {\n const minutes = Math.abs(\n this.datetime.diffNow().milliseconds / 1000 / 60\n );\n if (minutes < 1) {\n return html`<span\n class=\"date\"\n title=\"${this.datetime.toLocaleString(Display.datetime)}\"\n >just now</span\n >`;\n }\n formatted = this.store.getShortDuration(this.datetime);\n } else if (this.display === Display.day) {\n formatted = this.datetime.toLocaleString(Display.day);\n } else {\n formatted = this.datetime.toLocaleString(Display[this.display]);\n }\n\n return html`<span\n class=\"date\"\n title=\"${this.datetime.toLocaleString(Display.datetime)}\"\n >${formatted}</span\n >`;\n }\n }\n}\n"]}
@@ -3,17 +3,10 @@ import { property } from 'lit/decorators.js';
3
3
  import { FormElement } from '../FormElement';
4
4
  import { html, css } from 'lit';
5
5
  import { CustomEventType } from '../interfaces';
6
- const KEY_HEADER = 'header';
7
- const KEY_BODY = 'body';
8
- const KEY_FOOTER = 'footer';
9
- const KEY_BUTTONS = 'button';
10
6
  export class TemplateEditor extends FormElement {
11
7
  constructor() {
12
8
  super(...arguments);
13
9
  this.lang = 'eng-US';
14
- this.buttonKeys = [];
15
- this.contentKeys = [];
16
- this.otherKeys = [];
17
10
  }
18
11
  static get styles() {
19
12
  return css `
@@ -81,9 +74,18 @@ export class TemplateEditor extends FormElement {
81
74
  border-radius: var(--curvature);
82
75
  min-height: 23px;
83
76
  display: flex;
77
+ flex-direction: row;
84
78
  align-items: center;
85
79
  margin-right: 0.5em;
86
80
  margin-top: 0.5em;
81
+ align-items: center;
82
+ }
83
+
84
+ .button .display {
85
+ margin-right: 0.5em;
86
+ background: #f9f9f9;
87
+ padding: 0.25em 1em;
88
+ border-radius: var(--curvature);
87
89
  }
88
90
 
89
91
  temba-textinput,
@@ -117,29 +119,13 @@ export class TemplateEditor extends FormElement {
117
119
  if (translation.locale === this.lang ||
118
120
  (!loc && translation.locale.split('-')[0] === lang)) {
119
121
  this.translation = translation;
120
- this.buttonKeys = [];
121
- this.contentKeys = [];
122
- this.otherKeys = [];
123
- const keys = Object.keys(translation.components);
124
- for (const key of keys) {
125
- if (key.startsWith(KEY_BUTTONS)) {
126
- this.buttonKeys.push(key);
127
- }
128
- else if (key === KEY_HEADER ||
129
- key === KEY_BODY ||
130
- key === KEY_FOOTER) {
131
- this.contentKeys.push(key);
132
- }
133
- else {
134
- this.otherKeys.push(key);
135
- }
136
- const compParams = translation.components[key].params || [];
122
+ for (const comp of translation.components) {
123
+ const compParams = comp.params || [];
137
124
  if (compParams.length > 0) {
138
125
  // create an array for the length of params
139
- newParams[key] = new Array(compParams.length).fill('');
126
+ newParams[comp.name] = new Array(compParams.length).fill('');
140
127
  }
141
128
  }
142
- this.buttonKeys.sort();
143
129
  // if we are looking at the same template copy our params on top
144
130
  if (this.template === this.selectedTemplate.uuid) {
145
131
  for (const key of Object.keys(this.params || {})) {
@@ -165,95 +151,77 @@ export class TemplateEditor extends FormElement {
165
151
  }
166
152
  handleVariableChanged(event) {
167
153
  const target = event.target;
168
- const key = target.getAttribute('key');
169
154
  const index = parseInt(target.getAttribute('index'));
170
- this.params[key][index - 1] = target.value;
155
+ this.params[target.name][index - 1] = target.value;
171
156
  this.fireCustomEvent(CustomEventType.ContentChanged, {
172
157
  template: this.selectedTemplate,
173
158
  translation: this.translation,
174
159
  params: this.params,
175
160
  });
176
161
  }
177
- renderVariables(key, component) {
162
+ renderVariables(component) {
178
163
  const parts = component.content.split(/{{(\d+)}}/g);
179
164
  if (parts.length > 0) {
180
165
  const variables = parts.map((part, index) => {
181
- const keyIndex = Math.round(index / 2);
166
+ const paramIndex = Math.round(index / 2);
182
167
  if (index % 2 === 0) {
183
168
  return html `<span class="text">${part}</span>`;
184
169
  }
185
170
  return html `<temba-completion
186
171
  class="variable"
187
172
  type="text"
188
- value=${this.params[key][keyIndex - 1]}
173
+ value=${this.params[component.name][paramIndex - 1]}
189
174
  @change=${this.handleVariableChanged}
190
- key="${key}"
191
- index="${keyIndex}}"
175
+ name="${component.name}"
176
+ index="${paramIndex}"
192
177
  placeholder="variable.."
193
178
  ></temba-completion>`;
194
179
  });
195
180
  return html `<div class="content">${variables}</div>`;
196
181
  }
197
182
  }
198
- renderComponent(key, component) {
199
- return html ` <div class="component">
200
- <div>${key}</div>
201
- ${this.renderVariables(key, component)}
202
- </div>`;
203
- }
204
- renderContent(components) {
205
- let header = null;
206
- let body = null;
207
- let footer = null;
208
- if (components[KEY_HEADER]) {
209
- header = html `<div class="header">
210
- ${this.renderVariables(KEY_HEADER, components[KEY_HEADER])}
183
+ renderComponents(components) {
184
+ const nonButtons = components
185
+ .filter(comp => !comp.type.startsWith('button/'))
186
+ .map(component => html `<div class="${component['name']}">
187
+ ${this.renderVariables(component)}
188
+ </div>`);
189
+ const buttonComponents = components.filter(comp => comp.type.startsWith('button/'));
190
+ const buttons = buttonComponents.length > 0 ? this.renderButtons(buttonComponents) : null;
191
+ return html `<div class="main">${nonButtons}</div>
192
+ <div class="buttons">
193
+ ${buttons}
194
+ <div></div>
211
195
  </div>`;
212
- }
213
- if (components[KEY_BODY]) {
214
- body = html `<div class="body">
215
- ${this.renderVariables(KEY_BODY, components[KEY_BODY])}
216
- </div>`;
217
- }
218
- if (components[KEY_FOOTER]) {
219
- footer = html `<div class="footer">
220
- ${this.renderVariables(KEY_FOOTER, components[KEY_FOOTER])}
221
- </div>`;
222
- }
223
- if (header || body || footer) {
224
- return html `<div class="content">${header}${body}${footer}</div>`;
225
- }
226
- return null;
227
196
  }
228
197
  renderButtons(components) {
229
- if (this.buttonKeys.length > 0) {
230
- const buttons = this.buttonKeys.map(key => {
231
- const component = components[key];
232
- return html `<div class="button">
233
- ${this.renderVariables(key, component)}
234
- </div>`;
235
- });
236
- return html `<div class="button-wrapper">
237
- <div class="button-header">Template Buttons</div>
238
- <div class="buttons">${buttons}</div>
239
- </div>`;
240
- }
241
- return null;
198
+ const buttons = components.map(component => {
199
+ if (component.display) {
200
+ return html `
201
+ <div class="button">
202
+ <div class="display">${component.display}</div>
203
+ ${this.renderVariables(component)}
204
+ </div>
205
+ `;
206
+ }
207
+ else {
208
+ return html `
209
+ <div class="button">${this.renderVariables(component)}</div>
210
+ `;
211
+ }
212
+ });
213
+ return html `<div class="button-wrapper">
214
+ <div class="button-header">Template Buttons</div>
215
+ <div class="buttons">${buttons}</div>
216
+ </div>`;
242
217
  }
243
218
  render() {
244
219
  let content = null;
245
- let buttons = null;
246
- let otherComponents = null;
247
220
  if (this.translation) {
248
- content = this.renderContent(this.translation.components);
249
- buttons = this.renderButtons(this.translation.components);
250
- otherComponents = this.otherKeys.map(key => {
251
- const component = this.translation.components[key];
252
- return this.renderComponent(key, component);
253
- });
221
+ content = this.renderComponents(this.translation.components);
254
222
  }
255
223
  else {
256
- otherComponents = html `<div class="error-message">
224
+ content = html `<div class="error-message">
257
225
  No approved translation was found for current language.
258
226
  </div>`;
259
227
  }
@@ -266,7 +234,7 @@ export class TemplateEditor extends FormElement {
266
234
  valuekey="uuid"
267
235
  class="picker"
268
236
  value="${this.template}"
269
- endpoint=${this.url}
237
+ endpoint="${this.url}?comps_as_list=true"
270
238
  shouldExclude=${template => template.status !== 'approved'}
271
239
  placeholder="Select a template"
272
240
  @temba-content-changed=${this.swallowEvent}
@@ -274,11 +242,7 @@ export class TemplateEditor extends FormElement {
274
242
  >
275
243
  </temba-select>
276
244
 
277
- ${this.template
278
- ? html ` <div class="template">
279
- ${content} ${buttons} ${otherComponents}
280
- </div>`
281
- : null}
245
+ ${this.template ? html ` <div class="template">${content}</div>` : null}
282
246
  </div>
283
247
  `;
284
248
  }
@@ -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 } from 'lit';\nimport { CustomEventType } from '../interfaces';\n\nconst KEY_HEADER = 'header';\nconst KEY_BODY = 'body';\nconst KEY_FOOTER = 'footer';\nconst KEY_BUTTONS = 'button';\n\ninterface Component {\n content: string;\n params: { type: string }[];\n}\n\ninterface Translation {\n locale: string;\n status: string;\n channel: { uuid: string; name: string };\n components: { [key: string]: Component };\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 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 .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 margin-top: 1em;\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 }\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 align-items: center;\n margin-right: 0.5em;\n margin-top: 0.5em;\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 }\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 // component key to array of strings for variables\n @property({ type: Object })\n params: { [key: string]: string[] };\n\n @property({ type: Object, attribute: false })\n translation: Translation;\n\n @property({ type: Boolean })\n translating: boolean;\n\n buttonKeys = [];\n contentKeys = [];\n otherKeys = [];\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 this.selectedTemplate = (event.target as any).values[0] as Template;\n const [lang, loc] = this.lang.split('-');\n\n const newParams = {};\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 this.buttonKeys = [];\n this.contentKeys = [];\n this.otherKeys = [];\n const keys = Object.keys(translation.components);\n for (const key of keys) {\n if (key.startsWith(KEY_BUTTONS)) {\n this.buttonKeys.push(key);\n } else if (\n key === KEY_HEADER ||\n key === KEY_BODY ||\n key === KEY_FOOTER\n ) {\n this.contentKeys.push(key);\n } else {\n this.otherKeys.push(key);\n }\n\n const compParams = translation.components[key].params || [];\n if (compParams.length > 0) {\n // create an array for the length of params\n newParams[key] = new Array(compParams.length).fill('');\n }\n }\n this.buttonKeys.sort();\n\n // if we are looking at the same template copy our params on top\n if (this.template === this.selectedTemplate.uuid) {\n for (const key of Object.keys(this.params || {})) {\n if (newParams[key]) {\n for (let i = 0; i < this.params[key].length; i++) {\n newParams[key][i] = this.params[key][i];\n }\n }\n }\n }\n }\n });\n } else {\n this.translation = null;\n }\n\n this.params = newParams;\n this.fireCustomEvent(CustomEventType.ContextChanged, {\n template: this.selectedTemplate,\n translation: this.translation,\n params: this.params,\n });\n }\n\n private handleVariableChanged(event: CustomEvent) {\n const target = event.target as HTMLInputElement;\n const key = target.getAttribute('key');\n const index = parseInt(target.getAttribute('index'));\n this.params[key][index - 1] = target.value;\n this.fireCustomEvent(CustomEventType.ContentChanged, {\n template: this.selectedTemplate,\n translation: this.translation,\n params: this.params,\n });\n }\n\n private renderVariables(key: string, component: Component) {\n const parts = component.content.split(/{{(\\d+)}}/g);\n if (parts.length > 0) {\n const variables = parts.map((part, index) => {\n const keyIndex = Math.round(index / 2);\n if (index % 2 === 0) {\n return html`<span class=\"text\">${part}</span>`;\n }\n return html`<temba-completion\n class=\"variable\"\n type=\"text\"\n value=${this.params[key][keyIndex - 1]}\n @change=${this.handleVariableChanged}\n key=\"${key}\"\n index=\"${keyIndex}}\"\n placeholder=\"variable..\"\n ></temba-completion>`;\n });\n return html`<div class=\"content\">${variables}</div>`;\n }\n }\n\n private renderComponent(key: string, component: Component) {\n return html` <div class=\"component\">\n <div>${key}</div>\n ${this.renderVariables(key, component)}\n </div>`;\n }\n\n public renderContent(components: {\n [key: string]: Component;\n }): TemplateResult {\n let header = null;\n let body = null;\n let footer = null;\n\n if (components[KEY_HEADER]) {\n header = html`<div class=\"header\">\n ${this.renderVariables(KEY_HEADER, components[KEY_HEADER])}\n </div>`;\n }\n\n if (components[KEY_BODY]) {\n body = html`<div class=\"body\">\n ${this.renderVariables(KEY_BODY, components[KEY_BODY])}\n </div>`;\n }\n\n if (components[KEY_FOOTER]) {\n footer = html`<div class=\"footer\">\n ${this.renderVariables(KEY_FOOTER, components[KEY_FOOTER])}\n </div>`;\n }\n\n if (header || body || footer) {\n return html`<div class=\"content\">${header}${body}${footer}</div>`;\n }\n return null;\n }\n\n public renderButtons(components): TemplateResult {\n if (this.buttonKeys.length > 0) {\n const buttons = this.buttonKeys.map(key => {\n const component = components[key];\n return html`<div class=\"button\">\n ${this.renderVariables(key, component)}\n </div>`;\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 return null;\n }\n public render(): TemplateResult {\n let content = null;\n let buttons = null;\n let otherComponents = null;\n if (this.translation) {\n content = this.renderContent(this.translation.components);\n buttons = this.renderButtons(this.translation.components);\n otherComponents = this.otherKeys.map(key => {\n const component = this.translation.components[key];\n return this.renderComponent(key, component);\n });\n } else {\n otherComponents = 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\n ? html` <div class=\"template\">\n ${content} ${buttons} ${otherComponents}\n </div>`\n : 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 } from 'lit';\nimport { CustomEventType } from '../interfaces';\n\ninterface Component {\n name: string;\n type: string;\n content: string;\n params: { type: string }[];\n}\n\ninterface Translation {\n locale: string;\n status: string;\n channel: { uuid: string; name: string };\n components: Component[];\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 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 .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 margin-top: 1em;\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 }\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 .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 }\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 // component key to array of strings for variables\n @property({ type: Object })\n params: { [key: string]: 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 this.selectedTemplate = (event.target as any).values[0] as Template;\n const [lang, loc] = this.lang.split('-');\n\n const newParams = {};\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 for (const comp of translation.components) {\n const compParams = comp.params || [];\n if (compParams.length > 0) {\n // create an array for the length of params\n newParams[comp.name] = new Array(compParams.length).fill('');\n }\n }\n\n // if we are looking at the same template copy our params on top\n if (this.template === this.selectedTemplate.uuid) {\n for (const key of Object.keys(this.params || {})) {\n if (newParams[key]) {\n for (let i = 0; i < this.params[key].length; i++) {\n newParams[key][i] = this.params[key][i];\n }\n }\n }\n }\n }\n });\n } else {\n this.translation = null;\n }\n\n this.params = newParams;\n this.fireCustomEvent(CustomEventType.ContextChanged, {\n template: this.selectedTemplate,\n translation: this.translation,\n params: this.params,\n });\n }\n\n private handleVariableChanged(event: CustomEvent) {\n const target = event.target as HTMLInputElement;\n const index = parseInt(target.getAttribute('index'));\n this.params[target.name][index - 1] = target.value;\n this.fireCustomEvent(CustomEventType.ContentChanged, {\n template: this.selectedTemplate,\n translation: this.translation,\n params: this.params,\n });\n }\n\n private renderVariables(component: Component) {\n const parts = component.content.split(/{{(\\d+)}}/g);\n if (parts.length > 0) {\n const variables = parts.map((part, index) => {\n const paramIndex = Math.round(index / 2);\n if (index % 2 === 0) {\n return html`<span class=\"text\">${part}</span>`;\n }\n return html`<temba-completion\n class=\"variable\"\n type=\"text\"\n value=${this.params[component.name][paramIndex - 1]}\n @change=${this.handleVariableChanged}\n name=\"${component.name}\"\n index=\"${paramIndex}\"\n placeholder=\"variable..\"\n ></temba-completion>`;\n });\n return html`<div class=\"content\">${variables}</div>`;\n }\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,5 +1,5 @@
1
+ import { html, fixture, expect } from '@open-wc/testing';
1
2
  import { assertScreenshot, getClip, getComponent, loadStore, mockNow, } from './utils.test';
2
- import { expect } from '@open-wc/testing';
3
3
  const TAG = 'temba-date';
4
4
  export const getDate = async (attrs = {}) => {
5
5
  attrs['width'] = 100;
@@ -34,5 +34,15 @@ describe('temba-date', () => {
34
34
  await assertScreenshot('date/datetime', getClip(date));
35
35
  expect(dateString).to.equal('11/18/1978, 9:22 AM');
36
36
  });
37
+ it('renders inline', async () => {
38
+ const el = await fixture(html `
39
+ <span
40
+ >Your birthday is
41
+ <temba-date value="1978-11-18T02:22:00.000000-07:00"></temba-date
42
+ >!</span
43
+ >
44
+ `);
45
+ await assertScreenshot('date/date-inline', getClip(el));
46
+ });
37
47
  });
38
48
  //# sourceMappingURL=temba-date.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"temba-date.test.js","sourceRoot":"","sources":["../../test/temba-date.test.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,OAAO,EACP,YAAY,EACZ,SAAS,EACT,OAAO,GACR,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,MAAM,GAAG,GAAG,YAAY,CAAC;AAEzB,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,EAAE,QAAa,EAAE,EAAE,EAAE;IAC/C,KAAK,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC;IACrB,OAAO,CAAC,MAAM,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,CAAc,CAAC;AACvD,CAAC,CAAC;AAEF,OAAO,CAAC,kCAAkC,CAAC,CAAC;AAE5C,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,UAAU,CAAC,GAAG,EAAE;QACd,SAAS,EAAE,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iBAAiB,EAAE,KAAK,IAAI,EAAE;QAC/B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,EAAE,KAAK,EAAE,kCAAkC,EAAE,CAAC,CAAC;QAC1E,MAAM,UAAU,GACd,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,OAAO,CACtC,CAAC,SAAS,CAAC;QAEZ,MAAM,gBAAgB,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACnD,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kBAAkB,EAAE,KAAK,IAAI,EAAE;QAChC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC;YACzB,KAAK,EAAE,kCAAkC;YACzC,OAAO,EAAE,UAAU;SACpB,CAAC,CAAC;QACH,MAAM,UAAU,GACd,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,OAAO,CACtC,CAAC,SAAS,CAAC;QAEZ,MAAM,gBAAgB,CAAC,eAAe,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kBAAkB,EAAE,KAAK,IAAI,EAAE;QAChC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC;YACzB,KAAK,EAAE,kCAAkC;YACzC,OAAO,EAAE,UAAU;SACpB,CAAC,CAAC;QACH,MAAM,UAAU,GACd,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,OAAO,CACtC,CAAC,SAAS,CAAC;QAEZ,MAAM,gBAAgB,CAAC,eAAe,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { TembaDate } from '../src/date/TembaDate';\nimport {\n assertScreenshot,\n getClip,\n getComponent,\n loadStore,\n mockNow,\n} from './utils.test';\nimport { expect } from '@open-wc/testing';\n\nconst TAG = 'temba-date';\n\nexport const getDate = async (attrs: any = {}) => {\n attrs['width'] = 100;\n return (await getComponent(TAG, attrs)) as TembaDate;\n};\n\nmockNow('2022-12-02T21:00:00.000000-07:00');\n\ndescribe('temba-date', () => {\n beforeEach(() => {\n loadStore();\n });\n\n it('renders default', async () => {\n const date = await getDate({ value: '1978-11-18T02:22:00.000000-07:00' });\n const dateString = (\n date.shadowRoot.querySelector('.date') as HTMLDivElement\n ).innerText;\n\n await assertScreenshot('date/date', getClip(date));\n expect(dateString).to.equal('11/18/1978');\n });\n\n it('renders duration', async () => {\n const date = await getDate({\n value: '1978-11-18T02:22:00.000000-07:00',\n display: 'duration',\n });\n const dateString = (\n date.shadowRoot.querySelector('.date') as HTMLDivElement\n ).innerText;\n\n await assertScreenshot('date/duration', getClip(date));\n expect(dateString).to.equal('44 years ago');\n });\n\n it('renders datetime', async () => {\n const date = await getDate({\n value: '1978-11-18T02:22:00.000000-07:00',\n display: 'datetime',\n });\n const dateString = (\n date.shadowRoot.querySelector('.date') as HTMLDivElement\n ).innerText;\n\n await assertScreenshot('date/datetime', getClip(date));\n expect(dateString).to.equal('11/18/1978, 9:22 AM');\n });\n});\n"]}
1
+ {"version":3,"file":"temba-date.test.js","sourceRoot":"","sources":["../../test/temba-date.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,EACL,gBAAgB,EAChB,OAAO,EACP,YAAY,EACZ,SAAS,EACT,OAAO,GACR,MAAM,cAAc,CAAC;AAEtB,MAAM,GAAG,GAAG,YAAY,CAAC;AAEzB,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,EAAE,QAAa,EAAE,EAAE,EAAE;IAC/C,KAAK,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC;IACrB,OAAO,CAAC,MAAM,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,CAAc,CAAC;AACvD,CAAC,CAAC;AAEF,OAAO,CAAC,kCAAkC,CAAC,CAAC;AAE5C,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,UAAU,CAAC,GAAG,EAAE;QACd,SAAS,EAAE,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iBAAiB,EAAE,KAAK,IAAI,EAAE;QAC/B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,EAAE,KAAK,EAAE,kCAAkC,EAAE,CAAC,CAAC;QAC1E,MAAM,UAAU,GACd,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,OAAO,CACtC,CAAC,SAAS,CAAC;QAEZ,MAAM,gBAAgB,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACnD,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kBAAkB,EAAE,KAAK,IAAI,EAAE;QAChC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC;YACzB,KAAK,EAAE,kCAAkC;YACzC,OAAO,EAAE,UAAU;SACpB,CAAC,CAAC;QACH,MAAM,UAAU,GACd,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,OAAO,CACtC,CAAC,SAAS,CAAC;QAEZ,MAAM,gBAAgB,CAAC,eAAe,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kBAAkB,EAAE,KAAK,IAAI,EAAE;QAChC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC;YACzB,KAAK,EAAE,kCAAkC;YACzC,OAAO,EAAE,UAAU;SACpB,CAAC,CAAC;QACH,MAAM,UAAU,GACd,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,OAAO,CACtC,CAAC,SAAS,CAAC;QAEZ,MAAM,gBAAgB,CAAC,eAAe,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gBAAgB,EAAE,KAAK,IAAI,EAAE;QAC9B,MAAM,EAAE,GAAgB,MAAM,OAAO,CAAC,IAAI,CAAA;;;;;;KAMzC,CAAC,CAAC;QAEH,MAAM,gBAAgB,CAAC,kBAAkB,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { html, fixture, expect } from '@open-wc/testing';\nimport { TembaDate } from '../src/date/TembaDate';\nimport {\n assertScreenshot,\n getClip,\n getComponent,\n loadStore,\n mockNow,\n} from './utils.test';\n\nconst TAG = 'temba-date';\n\nexport const getDate = async (attrs: any = {}) => {\n attrs['width'] = 100;\n return (await getComponent(TAG, attrs)) as TembaDate;\n};\n\nmockNow('2022-12-02T21:00:00.000000-07:00');\n\ndescribe('temba-date', () => {\n beforeEach(() => {\n loadStore();\n });\n\n it('renders default', async () => {\n const date = await getDate({ value: '1978-11-18T02:22:00.000000-07:00' });\n const dateString = (\n date.shadowRoot.querySelector('.date') as HTMLSpanElement\n ).innerText;\n\n await assertScreenshot('date/date', getClip(date));\n expect(dateString).to.equal('11/18/1978');\n });\n\n it('renders duration', async () => {\n const date = await getDate({\n value: '1978-11-18T02:22:00.000000-07:00',\n display: 'duration',\n });\n const dateString = (\n date.shadowRoot.querySelector('.date') as HTMLSpanElement\n ).innerText;\n\n await assertScreenshot('date/duration', getClip(date));\n expect(dateString).to.equal('44 years ago');\n });\n\n it('renders datetime', async () => {\n const date = await getDate({\n value: '1978-11-18T02:22:00.000000-07:00',\n display: 'datetime',\n });\n const dateString = (\n date.shadowRoot.querySelector('.date') as HTMLSpanElement\n ).innerText;\n\n await assertScreenshot('date/datetime', getClip(date));\n expect(dateString).to.equal('11/18/1978, 9:22 AM');\n });\n\n it('renders inline', async () => {\n const el: HTMLElement = await fixture(html`\n <span\n >Your birthday is\n <temba-date value=\"1978-11-18T02:22:00.000000-07:00\"></temba-date\n >!</span\n >\n `);\n\n await assertScreenshot('date/date-inline', getClip(el));\n });\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyaruka/temba-components",
3
- "version": "0.76.0",
3
+ "version": "0.78.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",
Binary file
Binary file
Binary file
@@ -18,10 +18,7 @@ export class TembaDate extends RapidElement {
18
18
  static get styles() {
19
19
  return css`
20
20
  .date {
21
- display: block;
22
- overflow: hidden;
23
- text-overflow: ellipsis;
24
- white-space: nowrap;
21
+ display: inline;
25
22
  }
26
23
  `;
27
24
  }
@@ -78,12 +75,11 @@ export class TembaDate extends RapidElement {
78
75
  this.datetime.diffNow().milliseconds / 1000 / 60
79
76
  );
80
77
  if (minutes < 1) {
81
- return html`<div
78
+ return html`<span
82
79
  class="date"
83
80
  title="${this.datetime.toLocaleString(Display.datetime)}"
84
- >
85
- just now
86
- </div>`;
81
+ >just now</span
82
+ >`;
87
83
  }
88
84
 
89
85
  formatted = this.store.getShortDuration(this.datetime);
@@ -92,12 +88,11 @@ export class TembaDate extends RapidElement {
92
88
  this.datetime.diffNow().milliseconds / 1000 / 60
93
89
  );
94
90
  if (minutes < 1) {
95
- return html`<div
91
+ return html`<span
96
92
  class="date"
97
93
  title="${this.datetime.toLocaleString(Display.datetime)}"
98
- >
99
- just now
100
- </div>`;
94
+ >just now</span
95
+ >`;
101
96
  }
102
97
  formatted = this.store.getShortDuration(this.datetime);
103
98
  } else if (this.display === Display.day) {
@@ -105,12 +100,12 @@ export class TembaDate extends RapidElement {
105
100
  } else {
106
101
  formatted = this.datetime.toLocaleString(Display[this.display]);
107
102
  }
108
- return html`<div
103
+
104
+ return html`<span
109
105
  class="date"
110
106
  title="${this.datetime.toLocaleString(Display.datetime)}"
111
- >
112
- ${formatted}
113
- </div>`;
107
+ >${formatted}</span
108
+ >`;
114
109
  }
115
110
  }
116
111
  }