@nyaruka/temba-components 0.26.9 → 0.27.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.
Files changed (71) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/{d0cc86be.js → e7b04ba3.js} +686 -420
  3. package/dist/index.js +686 -420
  4. package/dist/static/icons/symbol-defs.svg +35 -4
  5. package/dist/sw.js +1 -1
  6. package/dist/sw.js.map +1 -1
  7. package/dist/templates/components-body.html +1 -1
  8. package/dist/templates/components-head.html +1 -1
  9. package/out-tsc/src/contacts/ContactBadges.js +97 -0
  10. package/out-tsc/src/contacts/ContactBadges.js.map +1 -0
  11. package/out-tsc/src/contacts/ContactFieldEditor.js +53 -41
  12. package/out-tsc/src/contacts/ContactFieldEditor.js.map +1 -1
  13. package/out-tsc/src/contacts/ContactFields.js +91 -33
  14. package/out-tsc/src/contacts/ContactFields.js.map +1 -1
  15. package/out-tsc/src/contacts/ContactPending.js +238 -0
  16. package/out-tsc/src/contacts/ContactPending.js.map +1 -0
  17. package/out-tsc/src/contacts/ContactStoreElement.js +3 -0
  18. package/out-tsc/src/contacts/ContactStoreElement.js.map +1 -1
  19. package/out-tsc/src/contacts/events.js +1 -1
  20. package/out-tsc/src/contacts/events.js.map +1 -1
  21. package/out-tsc/src/interfaces.js +12 -0
  22. package/out-tsc/src/interfaces.js.map +1 -1
  23. package/out-tsc/src/label/Label.js +25 -0
  24. package/out-tsc/src/label/Label.js.map +1 -1
  25. package/out-tsc/src/list/TembaMenu.js +8 -6
  26. package/out-tsc/src/list/TembaMenu.js.map +1 -1
  27. package/out-tsc/src/store/Store.js +79 -0
  28. package/out-tsc/src/store/Store.js.map +1 -1
  29. package/out-tsc/src/tabpane/Tab.js +14 -1
  30. package/out-tsc/src/tabpane/Tab.js.map +1 -1
  31. package/out-tsc/src/tabpane/TabPane.js +35 -0
  32. package/out-tsc/src/tabpane/TabPane.js.map +1 -1
  33. package/out-tsc/src/textinput/TextInput.js +2 -2
  34. package/out-tsc/src/textinput/TextInput.js.map +1 -1
  35. package/out-tsc/src/utils/index.js +3 -3
  36. package/out-tsc/src/utils/index.js.map +1 -1
  37. package/out-tsc/src/vectoricon/VectorIcon.js +13 -2
  38. package/out-tsc/src/vectoricon/VectorIcon.js.map +1 -1
  39. package/out-tsc/temba-modules.js +8 -6
  40. package/out-tsc/temba-modules.js.map +1 -1
  41. package/package.json +3 -1
  42. package/src/contacts/ContactBadges.ts +104 -0
  43. package/src/contacts/ContactFieldEditor.ts +55 -41
  44. package/src/contacts/ContactFields.ts +105 -36
  45. package/src/contacts/ContactPending.ts +236 -0
  46. package/src/contacts/ContactStoreElement.ts +4 -0
  47. package/src/contacts/events.ts +1 -1
  48. package/src/interfaces.ts +34 -1
  49. package/src/label/Label.ts +25 -0
  50. package/src/list/TembaMenu.ts +7 -5
  51. package/src/store/Store.ts +104 -1
  52. package/src/tabpane/Tab.ts +14 -1
  53. package/src/tabpane/TabPane.ts +36 -0
  54. package/src/textinput/TextInput.ts +2 -2
  55. package/src/utils/index.ts +10 -10
  56. package/src/vectoricon/VectorIcon.ts +15 -2
  57. package/static/css/temba-components.css +3 -0
  58. package/static/icons/Read Me.txt +1 -1
  59. package/static/icons/SVG/bookmark-filled.svg +5 -0
  60. package/static/icons/SVG/bookmark.svg +1 -1
  61. package/static/icons/SVG/external-link1.svg +5 -0
  62. package/static/icons/SVG/globe.svg +5 -0
  63. package/static/icons/SVG/language.svg +5 -0
  64. package/static/icons/SVG/search.svg +5 -0
  65. package/static/icons/demo-external-svg.html +218 -165
  66. package/static/icons/demo-files/demo.css +6 -3
  67. package/static/icons/demo.html +253 -169
  68. package/static/icons/selection.json +318 -184
  69. package/static/icons/style.css +4 -0
  70. package/static/icons/symbol-defs.svg +35 -4
  71. package/temba-modules.ts +9 -6
@@ -7,9 +7,15 @@ import {
7
7
  CompletionSchema,
8
8
  KeyedAssets,
9
9
  CustomEventType,
10
+ Workspace,
10
11
  } from '../interfaces';
11
12
  import { RapidElement } from '../RapidElement';
12
13
  import Lru from 'tiny-lru';
14
+ import {
15
+ HumanizeDurationLanguage,
16
+ HumanizeDuration,
17
+ } from 'humanize-duration-ts';
18
+ import { DateTime } from 'luxon';
13
19
 
14
20
  export class Store extends RapidElement {
15
21
  @property({ type: Number })
@@ -30,6 +36,12 @@ export class Store extends RapidElement {
30
36
  @property({ type: String, attribute: 'globals' })
31
37
  globalsEndpoint: string;
32
38
 
39
+ @property({ type: String, attribute: 'languages' })
40
+ languagesEndpoint: string;
41
+
42
+ @property({ type: String, attribute: 'workspace' })
43
+ workspaceEndpoint: string;
44
+
33
45
  @property({ type: Object, attribute: false })
34
46
  private schema: CompletionSchema;
35
47
 
@@ -39,11 +51,17 @@ export class Store extends RapidElement {
39
51
  @property({ type: Object, attribute: false })
40
52
  private keyedAssets: KeyedAssets = {};
41
53
 
54
+ private locale = [...navigator.languages];
55
+
42
56
  private fields: { [key: string]: ContactField } = {};
43
57
  private groups: { [uuid: string]: ContactGroup } = {};
44
-
58
+ private languages: any = {};
59
+ private workspace: Workspace;
45
60
  private pinnedFields: ContactField[] = [];
46
61
 
62
+ private langService = new HumanizeDurationLanguage();
63
+ private humanizer = new HumanizeDuration(this.langService);
64
+
47
65
  // http promise to monitor for completeness
48
66
  public httpComplete: Promise<void | WebResponse[]>;
49
67
 
@@ -52,6 +70,21 @@ export class Store extends RapidElement {
52
70
  public firstUpdated() {
53
71
  this.cache = Lru(this.max, this.ttl);
54
72
 
73
+ /*
74
+ // This will create a shorthand unit
75
+ this.humanizer.addLanguage("en", {
76
+ y: () => "y",
77
+ mo: () => "mo",
78
+ w: () => "w",
79
+ d: () => "d",
80
+ h: () => "h",
81
+ m: () => "m",
82
+ s: () => "s",
83
+ ms: () => "ms",
84
+ decimal: ".",
85
+ });
86
+ */
87
+
55
88
  const fetches = [];
56
89
  if (this.completionEndpoint) {
57
90
  fetches.push(
@@ -91,6 +124,22 @@ export class Store extends RapidElement {
91
124
  );
92
125
  }
93
126
 
127
+ if (this.languagesEndpoint) {
128
+ fetches.push(
129
+ getAssets(this.languagesEndpoint).then((results: any[]) => {
130
+ // convert array of objects to lookup
131
+ this.languages = results.reduce(function (
132
+ languages: any,
133
+ result: any
134
+ ) {
135
+ languages[result.value] = result.name;
136
+ return languages;
137
+ },
138
+ {});
139
+ })
140
+ );
141
+ }
142
+
94
143
  if (this.groupsEndpoint) {
95
144
  fetches.push(
96
145
  getAssets(this.groupsEndpoint).then((groups: any[]) => {
@@ -101,9 +150,40 @@ export class Store extends RapidElement {
101
150
  );
102
151
  }
103
152
 
153
+ if (this.workspaceEndpoint) {
154
+ fetches.push(
155
+ getUrl(this.workspaceEndpoint).then((response: WebResponse) => {
156
+ this.workspace = response.json;
157
+ const lang = response.headers.get('content-language');
158
+ if (lang) {
159
+ this.locale = [lang, ...this.locale];
160
+ }
161
+ })
162
+ );
163
+ }
164
+
104
165
  this.httpComplete = Promise.all(fetches);
105
166
  }
106
167
 
168
+ public getLanguageCode() {
169
+ if (this.locale.length > 0) {
170
+ return this.locale[0].split('-')[0];
171
+ }
172
+ return 'en';
173
+ }
174
+
175
+ public getShortDuration(isoDate: string) {
176
+ const scheduled = DateTime.fromISO(isoDate);
177
+ const now = DateTime.now();
178
+
179
+ const duration = scheduled.diff(now).valueOf();
180
+ return this.humanizer.humanize(duration, {
181
+ language: this.getLanguageCode(),
182
+ largest: 1,
183
+ round: false,
184
+ });
185
+ }
186
+
107
187
  public setKeyedAssets(name: string, values: string[]): void {
108
188
  this.keyedAssets[name] = values;
109
189
  }
@@ -132,6 +212,10 @@ export class Store extends RapidElement {
132
212
  return this.pinnedFields;
133
213
  }
134
214
 
215
+ public getLanguageName(iso: string) {
216
+ return this.languages[iso];
217
+ }
218
+
135
219
  public isDynamicGroup(uuid: string): boolean {
136
220
  const group = this.groups[uuid];
137
221
  if (group && group.query) {
@@ -140,6 +224,20 @@ export class Store extends RapidElement {
140
224
  return false;
141
225
  }
142
226
 
227
+ public getWorkspace(): Workspace {
228
+ return this.workspace;
229
+ }
230
+
231
+ public formatDate(dateString: string) {
232
+ return new Date(dateString).toLocaleString(this.locale, {
233
+ year: 'numeric',
234
+ month: 'long',
235
+ day: 'numeric',
236
+ hour: 'numeric',
237
+ minute: 'numeric',
238
+ });
239
+ }
240
+
143
241
  public getUrl(
144
242
  url: string,
145
243
  options?: {
@@ -208,6 +306,11 @@ export class Store extends RapidElement {
208
306
 
209
307
  public fetching: { [url: string]: number } = {};
210
308
 
309
+ public updateCache(url: string, data: any) {
310
+ this.cache.set(url, data);
311
+ this.fireCustomEvent(CustomEventType.StoreUpdated, { url, data });
312
+ }
313
+
211
314
  public makeRequest(
212
315
  url: string,
213
316
  options?: { force?: boolean; prepareData?: (data: any) => any }
@@ -31,12 +31,25 @@ export class Tab extends RapidElement {
31
31
  @property({ type: String })
32
32
  icon: string;
33
33
 
34
+ @property({ type: String })
35
+ selectionColor: string;
36
+
37
+ @property({ type: String })
38
+ selectionBackground: string;
39
+
34
40
  @property({ type: Boolean })
35
41
  selected = false;
36
42
 
43
+ @property({ type: Number })
44
+ count = 0;
45
+
46
+ public hasBadge() {
47
+ return this.count > 0;
48
+ }
49
+
37
50
  public render(): TemplateResult {
38
51
  return html`<slot
39
52
  class="${getClasses({ selected: this.selected })}"
40
- ></slot>`;
53
+ ></slot> `;
41
54
  }
42
55
  }
@@ -1,5 +1,6 @@
1
1
  import { css, html, TemplateResult } from 'lit';
2
2
  import { property } from 'lit/decorators';
3
+ import { CustomEventType } from '../interfaces';
3
4
  import { RapidElement } from '../RapidElement';
4
5
  import { Tab } from './Tab';
5
6
 
@@ -21,6 +22,7 @@ export class TabPane extends RapidElement {
21
22
  margin: 0em 0em;
22
23
  cursor: pointer;
23
24
  display: flex;
25
+ align-items: center;
24
26
  border-radius: var(--curvature);
25
27
  border-bottom-right-radius: 0px;
26
28
  border-bottom-left-radius: 0px;
@@ -53,6 +55,21 @@ export class TabPane extends RapidElement {
53
55
  .pane.first {
54
56
  border-top-left-radius: 0px;
55
57
  }
58
+
59
+ .badge {
60
+ margin-left: 0.4em;
61
+ }
62
+
63
+ .count {
64
+ border-radius: 99px;
65
+ background: rgba(0, 0, 0, 0.05);
66
+ color: rgba(0, 0, 0, 0.5);
67
+ font-size: 0.7em;
68
+ font-weight: 400;
69
+ padding: 0.1em 0.4em;
70
+ min-width: 1em;
71
+ text-align: center;
72
+ }
56
73
  `;
57
74
  }
58
75
 
@@ -64,6 +81,7 @@ export class TabPane extends RapidElement {
64
81
  (event.currentTarget as HTMLDivElement).dataset.index
65
82
  );
66
83
  this.requestUpdate('index');
84
+ this.fireEvent(CustomEventType.ContextChanged);
67
85
  }
68
86
 
69
87
  public updated(changedProperties: Map<string, any>) {
@@ -84,6 +102,10 @@ export class TabPane extends RapidElement {
84
102
  }
85
103
  }
86
104
 
105
+ public getTab(index: number): Tab {
106
+ return this.children.item(index) as Tab;
107
+ }
108
+
87
109
  public render(): TemplateResult {
88
110
  const tabs: Tab[] = [];
89
111
  for (const tab of this.children) {
@@ -98,9 +120,23 @@ export class TabPane extends RapidElement {
98
120
  @click=${this.handleTabClick}
99
121
  data-index=${index}
100
122
  class="tab ${index == this.index ? 'selected' : ''}"
123
+ style="${tab.selectionColor && index == this.index
124
+ ? `color:${tab.selectionColor};--icon-color:${tab.selectionColor};`
125
+ : ''} ${tab.selectionBackground && index == this.index
126
+ ? `background-color:${tab.selectionBackground};`
127
+ : ''}"
101
128
  >
102
129
  ${tab.icon ? html`<temba-icon name=${tab.icon} />` : null}
103
130
  ${tab.name}
131
+ ${tab.hasBadge()
132
+ ? html`
133
+ <div class="badge">
134
+ ${tab.count > 0
135
+ ? html`<div class="count">${tab.count}</div>`
136
+ : null}
137
+ </div>
138
+ `
139
+ : null}
104
140
  </div>
105
141
  `
106
142
  )}
@@ -20,9 +20,7 @@ export class TextInput extends FormElement {
20
20
  display: flex;
21
21
  flex-direction: row;
22
22
  align-items: stretch;
23
-
24
23
  box-shadow: var(--widget-box-shadow);
25
-
26
24
  caret-color: var(--input-caret);
27
25
  }
28
26
 
@@ -47,6 +45,8 @@ export class TextInput extends FormElement {
47
45
  border-color: var(--color-focus);
48
46
  background: var(--color-widget-bg-focused);
49
47
  box-shadow: var(--widget-box-shadow-focused);
48
+ z-index: 10000;
49
+ position: relative;
50
50
  }
51
51
 
52
52
  .input-container:hover {
@@ -6,7 +6,8 @@ import { ContactField, Ticket } from '../interfaces';
6
6
 
7
7
  export type Asset = KeyedAsset & Ticket & ContactField;
8
8
 
9
- export const DATE_FORMAT = /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/;
9
+ export const DATE_FORMAT =
10
+ /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/;
10
11
 
11
12
  interface KeyedAsset {
12
13
  key?: string;
@@ -77,11 +78,11 @@ export const getUrl = (
77
78
  // eslint-disable-next-line no-empty
78
79
  } catch (err) {}
79
80
  resolve({
81
+ controller,
80
82
  body,
81
83
  json,
82
- status: response.status,
83
84
  headers: response.headers,
84
- controller,
85
+ status: response.status,
85
86
  });
86
87
  });
87
88
  })
@@ -210,8 +211,8 @@ export const postUrl = (
210
211
  resolve({
211
212
  body,
212
213
  json,
213
- status: response.status,
214
214
  headers: response.headers,
215
+ status: response.status,
215
216
  });
216
217
  });
217
218
  })
@@ -260,12 +261,11 @@ export const postForm = (
260
261
 
261
262
  /**
262
263
  */
263
- export const renderIf = (predicate: boolean | any) => (
264
- then: () => TemplateResult,
265
- otherwise?: () => TemplateResult
266
- ) => {
267
- return predicate ? then() : otherwise ? otherwise() : html``;
268
- };
264
+ export const renderIf =
265
+ (predicate: boolean | any) =>
266
+ (then: () => TemplateResult, otherwise?: () => TemplateResult) => {
267
+ return predicate ? then() : otherwise ? otherwise() : html``;
268
+ };
269
269
 
270
270
  export const hexToRgb = (hex: string): { r: number; g: number; b: number } => {
271
271
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
@@ -4,7 +4,7 @@ import { property } from 'lit/decorators';
4
4
  import { getClasses } from '../utils';
5
5
 
6
6
  // for cache busting, increase whenever the icon set changes
7
- const ICON_VERSION = 8;
7
+ const ICON_VERSION = 12;
8
8
 
9
9
  export class VectorIcon extends LitElement {
10
10
  @property({ type: String })
@@ -29,6 +29,9 @@ export class VectorIcon extends LitElement {
29
29
  @property({ type: String })
30
30
  animateChange: string;
31
31
 
32
+ @property({ type: String })
33
+ animateClick: string;
34
+
32
35
  @property({ type: Number })
33
36
  animationDuration = 200;
34
37
 
@@ -151,6 +154,12 @@ export class VectorIcon extends LitElement {
151
154
  }
152
155
  }
153
156
 
157
+ public handleClicked() {
158
+ if (this.animateClick) {
159
+ this.animationStep = 1;
160
+ }
161
+ }
162
+
154
163
  public updated(changes: Map<string, any>) {
155
164
  super.updated(changes);
156
165
 
@@ -183,10 +192,11 @@ export class VectorIcon extends LitElement {
183
192
  public render(): TemplateResult {
184
193
  return html`
185
194
  <div
195
+ @click=${this.handleClicked}
186
196
  class="wrapper ${getClasses({
187
197
  clickable: this.clickable,
188
198
  circled: this.circled,
189
- animate: !!this.animateChange,
199
+ animate: !!this.animateChange || !!this.animateClick,
190
200
  })}"
191
201
  >
192
202
  <svg
@@ -199,6 +209,9 @@ export class VectorIcon extends LitElement {
199
209
  [this.animateChange]: !!this.animateChange,
200
210
  [this.animateChange + '-' + this.animationStep]:
201
211
  this.animationStep > 0,
212
+ [this.animateClick]: !!this.animateClick,
213
+ [this.animateClick + '-' + this.animationStep]:
214
+ this.animationStep > 0,
202
215
  })}"
203
216
  >
204
217
  <use
@@ -84,6 +84,9 @@ html input {
84
84
  --color-label-primary-text: var(--color-text-light);
85
85
  --color-label-secondary: rgba(0, 0, 0, 0.2);
86
86
  --color-label-secondary-text: rgba(255, 255, 255, 0.9);
87
+ --color-label-tertiary: var(--color-tertiary);
88
+ --color-label-tertiary-text: var(--color-text-light);
89
+
87
90
 
88
91
  --color-nav-unselected: #fff;
89
92
  --color-nav-selected-bg: #fff;
@@ -4,7 +4,7 @@ If you prefer using PNGs, PDFs, or CSS sprites, refer to the Preferences panel o
4
4
 
5
5
  *demo.html* lists the icons that you selected. To insert your icons as inline SVGs (with the <use> element), copy the <svg> element (that contains symbol definitions) from the source of the demo.html file, below your own HTML's <body> tag. After copying this SVG, you can reference your glyphs like the following:
6
6
 
7
- <svg class="icon icon-calendar"><use xlink:href="#icon-calendar"></use></svg>
7
+ <svg class="icon icon-language"><use xlink:href="#icon-language"></use></svg>
8
8
 
9
9
  You can get this code from the SVG tab of the IcoMoon app, or by referring to the source of the demo.html file. To see how you can change the color/size of your icons using CSS, refer to the example provided in the *style.css* file.
10
10
 
@@ -0,0 +1,5 @@
1
+ <!-- Generated by IcoMoon.io -->
2
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
3
+ <title>bookmark-filled</title>
4
+ <path d="M17.016 3q0.797 0 1.383 0.609t0.586 1.406v15.984l-6.984-3-6.984 3v-15.984q0-0.797 0.586-1.406t1.383-0.609h10.031z"></path>
5
+ </svg>
@@ -1,5 +1,5 @@
1
1
  <!-- Generated by IcoMoon.io -->
2
2
  <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
3
3
  <title>bookmark</title>
4
- <path d="M18.419 21.814c0.161 0.116 0.363 0.186 0.581 0.186 0.552 0 1-0.448 1-1v-16c0-0.828-0.337-1.58-0.879-2.121s-1.293-0.879-2.121-0.879h-10c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v16c-0.001 0.199 0.060 0.404 0.186 0.581 0.321 0.449 0.946 0.554 1.395 0.232l6.419-4.584zM18 19.057l-5.419-3.871c-0.355-0.254-0.819-0.242-1.162 0l-5.419 3.871v-14.057c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293h10c0.276 0 0.525 0.111 0.707 0.293s0.293 0.431 0.293 0.707z"></path>
4
+ <path d="M17.016 18v-12.984h-10.031v12.984l5.016-2.203zM17.016 3q0.797 0 1.383 0.609t0.586 1.406v15.984l-6.984-3-6.984 3v-15.984q0-0.797 0.586-1.406t1.383-0.609h10.031z"></path>
5
5
  </svg>
@@ -0,0 +1,5 @@
1
+ <!-- Generated by IcoMoon.io -->
2
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
3
+ <title>external-link1</title>
4
+ <path d="M17 13v6c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293h-11c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-11c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293h6c0.552 0 1-0.448 1-1s-0.448-1-1-1h-6c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v11c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h11c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121v-6c0-0.552-0.448-1-1-1s-1 0.448-1 1zM10.707 14.707l9.293-9.293v3.586c0 0.552 0.448 1 1 1s1-0.448 1-1v-6c0-0.136-0.027-0.265-0.076-0.383s-0.121-0.228-0.216-0.323c-0.001-0.001-0.001-0.001-0.002-0.002-0.092-0.092-0.202-0.166-0.323-0.216-0.118-0.049-0.247-0.076-0.383-0.076h-6c-0.552 0-1 0.448-1 1s0.448 1 1 1h3.586l-9.293 9.293c-0.391 0.391-0.391 1.024 0 1.414s1.024 0.391 1.414 0z"></path>
5
+ </svg>
@@ -0,0 +1,5 @@
1
+ <!-- Generated by IcoMoon.io -->
2
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
3
+ <title>globe</title>
4
+ <path d="M16.951 11c-0.214-2.69-1.102-5.353-2.674-7.71 1.57 0.409 2.973 1.232 4.087 2.346 1.408 1.408 2.351 3.278 2.581 5.364zM14.279 20.709c1.483-2.226 2.437-4.853 2.669-7.709h3.997c-0.23 2.086-1.173 3.956-2.581 5.364-1.113 1.113-2.516 1.936-4.085 2.345zM7.049 13c0.214 2.69 1.102 5.353 2.674 7.71-1.57-0.409-2.973-1.232-4.087-2.346-1.408-1.408-2.351-3.278-2.581-5.364zM9.721 3.291c-1.482 2.226-2.436 4.853-2.669 7.709h-3.997c0.23-2.086 1.173-3.956 2.581-5.364 1.114-1.113 2.516-1.936 4.085-2.345zM12.004 1c0 0 0 0 0 0-3.044 0.001-5.794 1.233-7.782 3.222-1.99 1.989-3.222 4.741-3.222 7.778s1.232 5.789 3.222 7.778c1.988 1.989 4.738 3.221 7.774 3.222 0 0 0 0 0 0 3.044-0.001 5.793-1.233 7.782-3.222 1.99-1.989 3.222-4.741 3.222-7.778s-1.232-5.789-3.222-7.778c-1.988-1.989-4.738-3.221-7.774-3.222zM14.946 13c-0.252 2.788-1.316 5.36-2.945 7.451-1.729-2.221-2.706-4.818-2.945-7.451zM11.999 3.549c1.729 2.221 2.706 4.818 2.945 7.451h-5.89c0.252-2.788 1.316-5.36 2.945-7.451z"></path>
5
+ </svg>
@@ -0,0 +1,5 @@
1
+ <!-- Generated by IcoMoon.io -->
2
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
3
+ <title>language</title>
4
+ <path d="M19.753 10.909c-0.624-1.707-2.366-2.726-4.661-2.726-0.090 0-0.176 0.002-0.262 0.006l-0.016-2.063c0 0 3.41-0.588 3.525-0.607s0.133-0.119 0.109-0.231c-0.023-0.111-0.167-0.883-0.188-0.976-0.027-0.131-0.102-0.127-0.207-0.109s-3.25 0.461-3.25 0.461-0.012-1.953-0.013-2.078c-0.001-0.125-0.069-0.158-0.194-0.156s-0.92 0.014-1.025 0.016c-0.105 0.002-0.164 0.049-0.162 0.148s0.033 2.307 0.033 2.307-3.061 0.527-3.144 0.543c-0.084 0.014-0.17 0.053-0.151 0.143s0.19 1.094 0.208 1.172c0.018 0.080 0.072 0.129 0.188 0.107 0.115-0.019 2.924-0.504 2.924-0.504l0.035 2.018c-1.077 0.281-1.801 0.824-2.256 1.303-0.768 0.807-1.207 1.887-1.207 2.963 0 1.586 0.971 2.529 2.328 2.695 3.162 0.387 5.119-3.060 5.769-4.715 1.097 1.506 0.256 4.354-2.094 5.98-0.043 0.029-0.098 0.129-0.033 0.207s0.541 0.662 0.619 0.756c0.080 0.096 0.206 0.059 0.256 0.023 2.51-1.73 3.661-4.515 2.869-6.683zM12.367 14.097c-0.966-0.121-0.944-0.914-0.944-1.453 0-0.773 0.327-1.58 0.876-2.156 0.335-0.354 0.75-0.621 1.229-0.799l0.082 4.277c-0.385 0.131-0.799 0.185-1.243 0.131zM14.794 13.544l0.046-4.109c0.084-0.004 0.166-0.010 0.252-0.010 0.773 0 1.494 0.145 1.885 0.361s-1.023 2.713-2.183 3.758zM5.844 5.876c-0.030-0.094-0.103-0.145-0.196-0.145h-1.95c-0.093 0-0.165 0.051-0.194 0.144-0.412 1.299-3.48 10.99-3.496 11.041s-0.011 0.076 0.062 0.076h1.733c0.075 0 0.099-0.023 0.114-0.072 0.015-0.051 1.008-3.318 1.008-3.318h3.496c0 0 0.992 3.268 1.008 3.318s0.039 0.072 0.113 0.072h1.734c0.072 0 0.078-0.025 0.062-0.076-0.014-0.050-3.083-9.741-3.494-11.040zM3.226 12.194l1.447-5.25 1.447 5.25h-2.894z"></path>
5
+ </svg>
@@ -0,0 +1,5 @@
1
+ <!-- Generated by IcoMoon.io -->
2
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
3
+ <title>search</title>
4
+ <path d="M16.041 15.856c-0.034 0.026-0.067 0.055-0.099 0.087s-0.060 0.064-0.087 0.099c-1.258 1.213-2.969 1.958-4.855 1.958-1.933 0-3.682-0.782-4.95-2.050s-2.050-3.017-2.050-4.95 0.782-3.682 2.050-4.95 3.017-2.050 4.95-2.050 3.682 0.782 4.95 2.050 2.050 3.017 2.050 4.95c0 1.886-0.745 3.597-1.959 4.856zM21.707 20.293l-3.675-3.675c1.231-1.54 1.968-3.493 1.968-5.618 0-2.485-1.008-4.736-2.636-6.364s-3.879-2.636-6.364-2.636-4.736 1.008-6.364 2.636-2.636 3.879-2.636 6.364 1.008 4.736 2.636 6.364 3.879 2.636 6.364 2.636c2.125 0 4.078-0.737 5.618-1.968l3.675 3.675c0.391 0.391 1.024 0.391 1.414 0s0.391-1.024 0-1.414z"></path>
5
+ </svg>