@nyaruka/temba-components 0.27.2 → 0.28.2

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.
@@ -3,9 +3,11 @@ import { css, html } from 'lit';
3
3
  import { property } from 'lit/decorators';
4
4
  import { CustomEventType } from '../interfaces';
5
5
  import { RapidElement } from '../RapidElement';
6
+ import { getClasses } from '../utils';
6
7
  export class TabPane extends RapidElement {
7
8
  constructor() {
8
9
  super(...arguments);
10
+ this.collapses = false;
9
11
  this.index = 0;
10
12
  }
11
13
  static get styles() {
@@ -21,6 +23,7 @@ export class TabPane extends RapidElement {
21
23
  }
22
24
 
23
25
  .tab {
26
+ user-select: none;
24
27
  padding: 0.5em 1em;
25
28
  margin: 0em 0em;
26
29
  cursor: pointer;
@@ -32,10 +35,39 @@ export class TabPane extends RapidElement {
32
35
  border: 0px solid rgba(0, 0, 0, 0.45);
33
36
  color: var(--color-text-dark);
34
37
  --icon-color: var(--color-text-dark);
38
+ white-space: nowrap;
39
+ }
40
+
41
+ .tab.hidden {
42
+ display: none;
35
43
  }
36
44
 
37
45
  .tab temba-icon {
46
+ }
47
+
48
+ .tab .name {
49
+ margin-left: 0.4em;
50
+ max-width: 80px;
38
51
  margin-right: 0.4em;
52
+ overflow: hidden;
53
+ transition: max-width 500ms ease-in-out;
54
+ }
55
+
56
+ .tab .badge {
57
+ margin-left: 0.4em;
58
+ }
59
+
60
+ @media (max-width: 900px) {
61
+ .collapses .tab .name {
62
+ max-width: 0px;
63
+ margin: 0;
64
+ }
65
+ }
66
+
67
+ @media (max-width: 600px) {
68
+ .collapses .tab .badge {
69
+ display: none;
70
+ }
39
71
  }
40
72
 
41
73
  .tab.selected {
@@ -60,7 +92,6 @@ export class TabPane extends RapidElement {
60
92
  }
61
93
 
62
94
  .badge {
63
- margin-left: 0.4em;
64
95
  }
65
96
 
66
97
  .count {
@@ -111,14 +142,19 @@ export class TabPane extends RapidElement {
111
142
  tabs.push(tab);
112
143
  }
113
144
  return html `
114
- <div class="tabs">
145
+ <div
146
+ class="tabs ${getClasses({ tabs: true, collapses: this.collapses })}"
147
+ >
115
148
  ${tabs.map((tab, index) => html `
116
149
  <div
117
150
  @click=${this.handleTabClick}
118
151
  data-index=${index}
119
- class="tab ${index == this.index ? 'selected' : ''} ${tab.notify
120
- ? 'notify'
121
- : ''}"
152
+ class="${getClasses({
153
+ tab: true,
154
+ selected: index == this.index,
155
+ hidden: tab.hidden,
156
+ notify: tab.notify,
157
+ })}"
122
158
  style="${tab.selectionColor && index == this.index
123
159
  ? `color:${tab.selectionColor};--icon-color:${tab.selectionColor};`
124
160
  : ''} ${tab.selectionBackground && index == this.index
@@ -126,7 +162,7 @@ export class TabPane extends RapidElement {
126
162
  : ''}"
127
163
  >
128
164
  ${tab.icon ? html `<temba-icon name=${tab.icon} />` : null}
129
- ${tab.name}
165
+ <div class="name">${tab.name}</div>
130
166
  ${tab.hasBadge()
131
167
  ? html `
132
168
  <div class="badge">
@@ -145,6 +181,9 @@ export class TabPane extends RapidElement {
145
181
  `;
146
182
  }
147
183
  }
184
+ __decorate([
185
+ property({ type: Boolean })
186
+ ], TabPane.prototype, "collapses", void 0);
148
187
  __decorate([
149
188
  property({ type: Number })
150
189
  ], TabPane.prototype, "index", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"TabPane.js","sourceRoot":"","sources":["../../../src/tabpane/TabPane.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators';\nimport { CustomEventType } from '../interfaces';\nimport { RapidElement } from '../RapidElement';\nimport { Tab } from './Tab';\n\nexport class TabPane extends RapidElement {\n static get styles() {\n return css`\n :host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n }\n\n .tabs {\n display: flex;\n }\n\n .tab {\n padding: 0.5em 1em;\n margin: 0em 0em;\n cursor: pointer;\n display: flex;\n align-items: center;\n border-radius: var(--curvature);\n border-bottom-right-radius: 0px;\n border-bottom-left-radius: 0px;\n border: 0px solid rgba(0, 0, 0, 0.45);\n color: var(--color-text-dark);\n --icon-color: var(--color-text-dark);\n }\n\n .tab temba-icon {\n margin-right: 0.4em;\n }\n\n .tab.selected {\n cursor: default;\n box-shadow: 2px 1px 3px 2px rgba(0, 0, 0, 0.07);\n background: #fff;\n }\n\n .pane {\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n background: #fff;\n border-radius: var(--curvature);\n box-shadow: 2px 5px 12px 2px rgba(0, 0, 0, 0.09),\n 3px 3px 2px 1px rgba(0, 0, 0, 0.05);\n min-height: 0;\n }\n\n .pane.first {\n border-top-left-radius: 0px;\n }\n\n .badge {\n margin-left: 0.4em;\n }\n\n .count {\n border-radius: 99px;\n background: rgba(0, 0, 0, 0.05);\n color: rgba(0, 0, 0, 0.5);\n font-size: 0.6em;\n font-weight: 400;\n padding: 0.1em 0.4em;\n min-width: 1em;\n text-align: center;\n }\n\n .notify .count {\n background: var(--color-alert);\n color: #fff;\n }\n `;\n }\n\n @property({ type: Number })\n index = 0;\n\n private handleTabClick(event: MouseEvent): void {\n this.index = parseInt(\n (event.currentTarget as HTMLDivElement).dataset.index\n );\n this.requestUpdate('index');\n this.fireEvent(CustomEventType.ContextChanged);\n }\n\n public updated(changedProperties: Map<string, any>) {\n super.updated(changedProperties);\n if (changedProperties.has('index')) {\n if (this.children.length > this.index) {\n for (let i = 0; i < this.children.length; i++) {\n const tab = this.children[i] as Tab;\n tab.selected = i == this.index;\n\n if (tab.selected) {\n tab.style.display = 'flex';\n } else {\n tab.style.display = 'none';\n }\n }\n }\n }\n }\n\n public getTab(index: number): Tab {\n return this.children.item(index) as Tab;\n }\n\n public render(): TemplateResult {\n const tabs: Tab[] = [];\n for (const tab of this.children) {\n tabs.push(tab as Tab);\n }\n\n return html`\n <div class=\"tabs\">\n ${tabs.map(\n (tab, index) => html`\n <div\n @click=${this.handleTabClick}\n data-index=${index}\n class=\"tab ${index == this.index ? 'selected' : ''} ${tab.notify\n ? 'notify'\n : ''}\"\n style=\"${tab.selectionColor && index == this.index\n ? `color:${tab.selectionColor};--icon-color:${tab.selectionColor};`\n : ''} ${tab.selectionBackground && index == this.index\n ? `background-color:${tab.selectionBackground};`\n : ''}\"\n >\n ${tab.icon ? html`<temba-icon name=${tab.icon} />` : null}\n ${tab.name}\n ${tab.hasBadge()\n ? html`\n <div class=\"badge\">\n ${tab.count > 0\n ? html`<div class=\"count\">${tab.count}</div>`\n : null}\n </div>\n `\n : null}\n </div>\n `\n )}\n </div>\n <div class=\"pane ${this.index === 0 ? 'first' : null}\">\n <slot></slot>\n </div>\n `;\n }\n}\n"]}
1
+ {"version":3,"file":"TabPane.js","sourceRoot":"","sources":["../../../src/tabpane/TabPane.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators';\nimport { CustomEventType } from '../interfaces';\nimport { RapidElement } from '../RapidElement';\nimport { getClasses } from '../utils';\nimport { Tab } from './Tab';\n\nexport class TabPane extends RapidElement {\n static get styles() {\n return css`\n :host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n }\n\n .tabs {\n display: flex;\n }\n\n .tab {\n user-select: none;\n padding: 0.5em 1em;\n margin: 0em 0em;\n cursor: pointer;\n display: flex;\n align-items: center;\n border-radius: var(--curvature);\n border-bottom-right-radius: 0px;\n border-bottom-left-radius: 0px;\n border: 0px solid rgba(0, 0, 0, 0.45);\n color: var(--color-text-dark);\n --icon-color: var(--color-text-dark);\n white-space: nowrap;\n }\n\n .tab.hidden {\n display: none;\n }\n\n .tab temba-icon {\n }\n\n .tab .name {\n margin-left: 0.4em;\n max-width: 80px;\n margin-right: 0.4em;\n overflow: hidden;\n transition: max-width 500ms ease-in-out;\n }\n\n .tab .badge {\n margin-left: 0.4em;\n }\n\n @media (max-width: 900px) {\n .collapses .tab .name {\n max-width: 0px;\n margin: 0;\n }\n }\n\n @media (max-width: 600px) {\n .collapses .tab .badge {\n display: none;\n }\n }\n\n .tab.selected {\n cursor: default;\n box-shadow: 2px 1px 3px 2px rgba(0, 0, 0, 0.07);\n background: #fff;\n }\n\n .pane {\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n background: #fff;\n border-radius: var(--curvature);\n box-shadow: 2px 5px 12px 2px rgba(0, 0, 0, 0.09),\n 3px 3px 2px 1px rgba(0, 0, 0, 0.05);\n min-height: 0;\n }\n\n .pane.first {\n border-top-left-radius: 0px;\n }\n\n .badge {\n }\n\n .count {\n border-radius: 99px;\n background: rgba(0, 0, 0, 0.05);\n color: rgba(0, 0, 0, 0.5);\n font-size: 0.6em;\n font-weight: 400;\n padding: 0.1em 0.4em;\n min-width: 1em;\n text-align: center;\n }\n\n .notify .count {\n background: var(--color-alert);\n color: #fff;\n }\n `;\n }\n\n @property({ type: Boolean })\n collapses = false;\n\n @property({ type: Number })\n index = 0;\n\n private handleTabClick(event: MouseEvent): void {\n this.index = parseInt(\n (event.currentTarget as HTMLDivElement).dataset.index\n );\n this.requestUpdate('index');\n this.fireEvent(CustomEventType.ContextChanged);\n }\n\n public updated(changedProperties: Map<string, any>) {\n super.updated(changedProperties);\n if (changedProperties.has('index')) {\n if (this.children.length > this.index) {\n for (let i = 0; i < this.children.length; i++) {\n const tab = this.children[i] as Tab;\n tab.selected = i == this.index;\n\n if (tab.selected) {\n tab.style.display = 'flex';\n } else {\n tab.style.display = 'none';\n }\n }\n }\n }\n }\n\n public getTab(index: number): Tab {\n return this.children.item(index) as Tab;\n }\n\n public render(): TemplateResult {\n const tabs: Tab[] = [];\n for (const tab of this.children) {\n tabs.push(tab as Tab);\n }\n\n return html`\n <div\n class=\"tabs ${getClasses({ tabs: true, collapses: this.collapses })}\"\n >\n ${tabs.map(\n (tab, index) => html`\n <div\n @click=${this.handleTabClick}\n data-index=${index}\n class=\"${getClasses({\n tab: true,\n selected: index == this.index,\n hidden: tab.hidden,\n notify: tab.notify,\n })}\"\n style=\"${tab.selectionColor && index == this.index\n ? `color:${tab.selectionColor};--icon-color:${tab.selectionColor};`\n : ''} ${tab.selectionBackground && index == this.index\n ? `background-color:${tab.selectionBackground};`\n : ''}\"\n >\n ${tab.icon ? html`<temba-icon name=${tab.icon} />` : null}\n <div class=\"name\">${tab.name}</div>\n ${tab.hasBadge()\n ? html`\n <div class=\"badge\">\n ${tab.count > 0\n ? html`<div class=\"count\">${tab.count}</div>`\n : null}\n </div>\n `\n : null}\n </div>\n `\n )}\n </div>\n <div class=\"pane ${this.index === 0 ? 'first' : null}\">\n <slot></slot>\n </div>\n `;\n }\n}\n"]}
@@ -0,0 +1,11 @@
1
+ // import { html, fixture, expect } from '@open-wc/testing';
2
+ // import { TembaSlider } from '../src/slider/TembaSlider';
3
+ // import { assertScreenshot, getClip } from './utils.test';
4
+ describe('temba-slider', () => {
5
+ // it('renders default slider', async () => {});
6
+ // it('renders a slider with visible ranges', async () => {});
7
+ // it('updates slider position on element value change', async () => {});
8
+ // it('updates slider position on when track clicked', async () => {});
9
+ // it('updates slider position on circle drag', async () => {});
10
+ });
11
+ //# sourceMappingURL=temba-slider.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temba-slider.test.js","sourceRoot":"","sources":["../../test/temba-slider.test.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,2DAA2D;AAC3D,4DAA4D;AAE5D,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,gDAAgD;IAChD,8DAA8D;IAC9D,yEAAyE;IACzE,uEAAuE;IACvE,gEAAgE;AAClE,CAAC,CAAC,CAAC","sourcesContent":["// import { html, fixture, expect } from '@open-wc/testing';\n// import { TembaSlider } from '../src/slider/TembaSlider';\n// import { assertScreenshot, getClip } from './utils.test';\n\ndescribe('temba-slider', () => {\n // it('renders default slider', async () => {});\n // it('renders a slider with visible ranges', async () => {});\n // it('updates slider position on element value change', async () => {});\n // it('updates slider position on when track clicked', async () => {});\n // it('updates slider position on circle drag', async () => {});\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyaruka/temba-components",
3
- "version": "0.27.2",
3
+ "version": "0.28.2",
4
4
  "description": "Web components to support rapidpro and related projects",
5
5
  "author": "Nyaruka <code@nyaruka.coim>",
6
6
  "main": "dist/index.js",
@@ -13,17 +13,16 @@ export class ContactTickets extends StoreElement {
13
13
  static get styles() {
14
14
  return css`
15
15
  :host {
16
- background: rgba(0, 0, 0, 0.6);
17
- color: #fff;
18
- --icon-color: #fff;
19
- border-radius: var(--curvature);
16
+ padding: 1em;
20
17
  }
21
18
 
22
19
  :hover {
23
- background: rgba(0, 0, 0, 0.9);
24
- border-radius: var(--curvature);
25
- transition: background 200ms ease-in-out, padding 200ms ease-in-out;
20
+ }
21
+
22
+ .ticket:hover {
26
23
  cursor: pointer;
24
+ box-shadow: 0 0 8px 1px rgba(0, 0, 0, 0.055),
25
+ 0 0 0px 2px var(--color-link-primary);
27
26
  }
28
27
 
29
28
  .tickets {
@@ -56,24 +55,17 @@ export class ContactTickets extends StoreElement {
56
55
  }
57
56
 
58
57
  .ticket > div {
59
- padding: 1em;
58
+ padding: 0.5em 1em;
59
+ pointer-events: none;
60
60
  }
61
61
 
62
- .ticket .action {
63
- background: rgba(0, 0, 0, 0.1);
64
- border-top-right-radius: var(--curvature);
65
- border-bottom-right-radius: var(--curvature);
66
- padding: 1.5em;
67
- --icon-color: rgba(0, 0, 0, 0.4);
68
- white-space: nowrap;
62
+ .status {
63
+ --icon-color: #999;
69
64
  }
70
65
 
71
- .open .action {
72
- background: rgba(0, 0, 0, 0.1);
73
- }
74
-
75
- .closed .action {
76
- background: #fff;
66
+ .ticket.closed {
67
+ background: #f9f9f9;
68
+ color: #888;
77
69
  }
78
70
  `;
79
71
  }
@@ -81,8 +73,25 @@ export class ContactTickets extends StoreElement {
81
73
  prepareData(data: any): any {
82
74
  if (data && data.length) {
83
75
  data.sort((a: Ticket, b: Ticket) => {
76
+ if (a.status == TicketStatus.Open && b.status == TicketStatus.Closed) {
77
+ return -1;
78
+ }
79
+
80
+ if (b.status == TicketStatus.Open && a.status == TicketStatus.Closed) {
81
+ return 1;
82
+ }
83
+
84
+ if (
85
+ a.status == TicketStatus.Closed &&
86
+ b.status == TicketStatus.Closed
87
+ ) {
88
+ return (
89
+ new Date(b.closed_on).getTime() - new Date(a.closed_on).getTime()
90
+ );
91
+ }
92
+
84
93
  return (
85
- new Date(a.opened_on).getTime() - new Date(b.opened_on).getTime()
94
+ new Date(b.opened_on).getTime() - new Date(a.opened_on).getTime()
86
95
  );
87
96
  });
88
97
  }
@@ -120,43 +129,24 @@ export class ContactTickets extends StoreElement {
120
129
  </temba-tip>
121
130
  </div>
122
131
 
123
- ${ticket.status === TicketStatus.Open
124
- ? html`
125
- <div class="action">
126
- <temba-icon name="check" size="1.5" clickable></temba-icon>
127
- </div>
128
- `
129
- : html`
130
- <div class="action">
131
- <temba-icon name="check" size="1.5"></temba-icon>
132
- </div>
133
- `}
132
+ ${ticket.status === TicketStatus.Closed
133
+ ? html`<div class="status">
134
+ <temba-icon name="check"></temba-icon>
135
+ </div>`
136
+ : null}
134
137
  </div>
135
138
  `;
136
139
  }
137
140
 
138
141
  public render(): TemplateResult {
139
142
  if (this.data && this.data.length > 0) {
140
- const ticket = this.data.find(
141
- (ticket: Ticket) => ticket.status == TicketStatus.Open
142
- );
143
- if (ticket) {
144
- return html`
145
- <div
146
- class="tickets"
147
- href="/ticket/all/open/${ticket.uuid}"
148
- onclick="goto(event)"
149
- >
150
- <div style="flex-grow:1"></div>
151
- <temba-icon name="agent" style="pointer-events: none;"></temba-icon>
152
- <div class="count" style="pointer-events: none;">
153
- ${this.data.filter(
154
- (ticket: Ticket) => ticket.status === TicketStatus.Open
155
- ).length}
156
- </div>
157
- </div>
158
- `;
159
- }
143
+ const tickets = this.data.map(ticket => {
144
+ return this.renderTicket(ticket);
145
+ });
146
+
147
+ return html`${tickets}`;
160
148
  }
149
+
150
+ return html`<slot name="empty"></slot>`;
161
151
  }
162
152
  }
@@ -164,6 +164,7 @@ export class TembaMenu extends RapidElement {
164
164
 
165
165
  .level-0 {
166
166
  padding: 0px;
167
+ z-index: 500;
167
168
  }
168
169
 
169
170
  .item {
@@ -261,7 +262,7 @@ export class TembaMenu extends RapidElement {
261
262
  .level-1 {
262
263
  transition: opacity 100ms linear, margin 200ms linear;
263
264
  overflow-y: scroll;
264
- z-index: 1500;
265
+ z-index: 150;
265
266
  }
266
267
 
267
268
  .level-2 {
@@ -394,9 +395,14 @@ export class TembaMenu extends RapidElement {
394
395
  }
395
396
 
396
397
  .fully-collapsed .level-1 {
397
- margin-left: -245px;
398
- z-index: 0;
398
+ margin-left: -217px;
399
+ pointer-events: none;
399
400
  border: none;
401
+ overflow: hidden;
402
+ }
403
+
404
+ .fully-collapsed .level-1 > * {
405
+ opacity: 0;
400
406
  }
401
407
 
402
408
  .fully-collapsed .level-1 .item,
@@ -12,19 +12,21 @@ export class TembaSlider extends FormElement {
12
12
  }
13
13
 
14
14
  .track {
15
- height: 0.1em;
16
- border: 12px solid #fff;
15
+ height: 2px;
16
+ border-top: 0.5em solid #fff;
17
+ border-bottom: 0.5em solid #fff;
17
18
  background: #ddd;
19
+ flex-grow: 1;
18
20
  }
19
21
 
20
22
  .circle {
21
- margin-top: 0.4em;
22
- margin-left: 1em;
23
+ margin-bottom: -1.05em;
24
+ margin-left: -0.5em;
23
25
  width: 0.75em;
24
26
  height: 0.75em;
25
27
  border: 2px solid #999;
26
28
  border-radius: 999px;
27
- position: absolute;
29
+ position: relative;
28
30
  background: #fff;
29
31
  box-shadow: 0 0 0 4px rgb(255, 255, 255);
30
32
  transition: transform 200ms ease-in-out;
@@ -39,10 +41,6 @@ export class TembaSlider extends FormElement {
39
41
  cursor: pointer;
40
42
  }
41
43
 
42
- .grabbed .circle {
43
- // border-color: #777;
44
- }
45
-
46
44
  .grabbed .circle {
47
45
  border-color: var(--color-primary-dark);
48
46
  background: #fff;
@@ -51,9 +49,24 @@ export class TembaSlider extends FormElement {
51
49
  .grabbed .circle {
52
50
  transform: scale(1.2);
53
51
  }
52
+
53
+ .wrapper {
54
+ display: flex;
55
+ align-items: center;
56
+ }
57
+
58
+ .pre,
59
+ .post {
60
+ font-size: 0.9em;
61
+ color: #999;
62
+ padding: 0em 1em;
63
+ }
54
64
  `;
55
65
  }
56
66
 
67
+ @property({ type: Boolean })
68
+ range = false;
69
+
57
70
  @property({ type: Number })
58
71
  min = 0;
59
72
 
@@ -62,32 +75,28 @@ export class TembaSlider extends FormElement {
62
75
 
63
76
  circleX = 0;
64
77
  grabbed = false;
65
- left = 0;
66
- gap = 0;
67
78
 
68
79
  public firstUpdated(changes: Map<string, any>) {
69
80
  super.firstUpdated(changes);
70
81
  this.handleMouseMove = this.handleMouseMove.bind(this);
71
82
  this.handleMouseUp = this.handleMouseUp.bind(this);
72
- this.left = Math.round(this.getBoundingClientRect().left);
73
- const circle = this.shadowRoot.querySelector('.circle').clientWidth - 4;
74
- this.left = Math.round(this.getBoundingClientRect().left + circle);
75
- this.gap = this.offsetWidth * 0.035;
76
83
  }
77
84
 
78
85
  public updated(changedProperties: Map<string, any>): void {
79
86
  if (changedProperties.has('value')) {
80
- const pct = parseInt(this.value) / this.max;
81
- const total = this.offsetWidth - this.gap;
82
- this.updateCircle(total * pct);
87
+ this.updateCircle();
83
88
  }
84
89
  }
85
90
 
86
91
  public updateValue(evt: MouseEvent) {
87
- const left = evt.pageX - this.left;
88
- const pct = left / (this.offsetWidth - this.gap);
92
+ const track = this.shadowRoot.querySelector('.track') as HTMLDivElement;
93
+ const left = evt.pageX - track.offsetLeft;
94
+ const pct = left / track.offsetWidth;
95
+
96
+ const range = this.max - this.min;
97
+ const pctAsValue = range * pct + this.min;
89
98
  this.value =
90
- '' + Math.max(this.min, Math.min(Math.round(this.max * pct), this.max));
99
+ '' + Math.max(this.min, Math.min(Math.round(pctAsValue), this.max));
91
100
  }
92
101
 
93
102
  public handleMouseMove(evt: MouseEvent) {
@@ -102,14 +111,12 @@ export class TembaSlider extends FormElement {
102
111
  document.addEventListener('mouseup', this.handleMouseUp);
103
112
  document.querySelector('html').classList.add('dragging');
104
113
  this.updateValue(evt);
105
-
106
114
  this.requestUpdate();
107
115
  }
108
116
 
109
117
  public handleMouseUp(evt: MouseEvent) {
110
118
  this.grabbed = false;
111
119
  this.updateValue(evt);
112
-
113
120
  this.requestUpdate();
114
121
 
115
122
  document.removeEventListener('mousemove', this.handleMouseMove);
@@ -117,14 +124,14 @@ export class TembaSlider extends FormElement {
117
124
  document.querySelector('html').classList.remove('dragging');
118
125
  }
119
126
 
120
- public updateCircle(x: number) {
121
- const circle = this.shadowRoot.querySelector('.circle') as HTMLDivElement;
122
- this.circleX = Math.round(
123
- Math.min(
124
- this.offsetWidth - this.gap,
125
- Math.max(x + circle.offsetWidth / 2, this.gap)
126
- )
127
- );
127
+ public updateCircle() {
128
+ const track = this.shadowRoot.querySelector('.track') as HTMLDivElement;
129
+ const pre = this.shadowRoot.querySelector('.pre') as HTMLDivElement;
130
+ const range = this.max - this.min;
131
+ const pct = (parseInt(this.value) - this.min) / range;
132
+ const pctAsPixels = track.offsetWidth * pct;
133
+
134
+ this.circleX = pctAsPixels + (pre ? pre.offsetWidth : 0);
128
135
  this.requestUpdate();
129
136
  }
130
137
 
@@ -135,7 +142,11 @@ export class TembaSlider extends FormElement {
135
142
  class="circle"
136
143
  @mousedown=${this.handleTrackDown}
137
144
  ></div>
138
- <div class="track" @mousedown=${this.handleTrackDown}></div>
145
+ <div class="wrapper">
146
+ ${this.range ? html`<div class="pre">${this.min}</div>` : null}
147
+ <div class="track" @mousedown=${this.handleTrackDown}></div>
148
+ ${this.range ? html`<div class="post">${this.max}</div>` : null}
149
+ </div>
139
150
  </div>`;
140
151
  }
141
152
  }
@@ -43,6 +43,9 @@ export class Tab extends RapidElement {
43
43
  @property({ type: Boolean })
44
44
  notify = false;
45
45
 
46
+ @property({ type: Boolean })
47
+ hidden = false;
48
+
46
49
  @property({ type: Number })
47
50
  count = 0;
48
51
 
@@ -2,6 +2,7 @@ import { css, html, TemplateResult } from 'lit';
2
2
  import { property } from 'lit/decorators';
3
3
  import { CustomEventType } from '../interfaces';
4
4
  import { RapidElement } from '../RapidElement';
5
+ import { getClasses } from '../utils';
5
6
  import { Tab } from './Tab';
6
7
 
7
8
  export class TabPane extends RapidElement {
@@ -18,6 +19,7 @@ export class TabPane extends RapidElement {
18
19
  }
19
20
 
20
21
  .tab {
22
+ user-select: none;
21
23
  padding: 0.5em 1em;
22
24
  margin: 0em 0em;
23
25
  cursor: pointer;
@@ -29,10 +31,39 @@ export class TabPane extends RapidElement {
29
31
  border: 0px solid rgba(0, 0, 0, 0.45);
30
32
  color: var(--color-text-dark);
31
33
  --icon-color: var(--color-text-dark);
34
+ white-space: nowrap;
35
+ }
36
+
37
+ .tab.hidden {
38
+ display: none;
32
39
  }
33
40
 
34
41
  .tab temba-icon {
42
+ }
43
+
44
+ .tab .name {
45
+ margin-left: 0.4em;
46
+ max-width: 80px;
35
47
  margin-right: 0.4em;
48
+ overflow: hidden;
49
+ transition: max-width 500ms ease-in-out;
50
+ }
51
+
52
+ .tab .badge {
53
+ margin-left: 0.4em;
54
+ }
55
+
56
+ @media (max-width: 900px) {
57
+ .collapses .tab .name {
58
+ max-width: 0px;
59
+ margin: 0;
60
+ }
61
+ }
62
+
63
+ @media (max-width: 600px) {
64
+ .collapses .tab .badge {
65
+ display: none;
66
+ }
36
67
  }
37
68
 
38
69
  .tab.selected {
@@ -57,7 +88,6 @@ export class TabPane extends RapidElement {
57
88
  }
58
89
 
59
90
  .badge {
60
- margin-left: 0.4em;
61
91
  }
62
92
 
63
93
  .count {
@@ -78,6 +108,9 @@ export class TabPane extends RapidElement {
78
108
  `;
79
109
  }
80
110
 
111
+ @property({ type: Boolean })
112
+ collapses = false;
113
+
81
114
  @property({ type: Number })
82
115
  index = 0;
83
116
 
@@ -118,15 +151,20 @@ export class TabPane extends RapidElement {
118
151
  }
119
152
 
120
153
  return html`
121
- <div class="tabs">
154
+ <div
155
+ class="tabs ${getClasses({ tabs: true, collapses: this.collapses })}"
156
+ >
122
157
  ${tabs.map(
123
158
  (tab, index) => html`
124
159
  <div
125
160
  @click=${this.handleTabClick}
126
161
  data-index=${index}
127
- class="tab ${index == this.index ? 'selected' : ''} ${tab.notify
128
- ? 'notify'
129
- : ''}"
162
+ class="${getClasses({
163
+ tab: true,
164
+ selected: index == this.index,
165
+ hidden: tab.hidden,
166
+ notify: tab.notify,
167
+ })}"
130
168
  style="${tab.selectionColor && index == this.index
131
169
  ? `color:${tab.selectionColor};--icon-color:${tab.selectionColor};`
132
170
  : ''} ${tab.selectionBackground && index == this.index
@@ -134,7 +172,7 @@ export class TabPane extends RapidElement {
134
172
  : ''}"
135
173
  >
136
174
  ${tab.icon ? html`<temba-icon name=${tab.icon} />` : null}
137
- ${tab.name}
175
+ <div class="name">${tab.name}</div>
138
176
  ${tab.hasBadge()
139
177
  ? html`
140
178
  <div class="badge">
@@ -1,6 +1,6 @@
1
1
  ```js script
2
2
  import { html } from '@open-wc/demoing-storybook';
3
- import '../dist/temba-checkbox.js';
3
+ import '../dist/index.js';
4
4
 
5
5
  export default {
6
6
  title: 'forms/temba-checkbox',
@@ -21,19 +21,9 @@ A component for...
21
21
 
22
22
  ## How to use
23
23
 
24
- ### Installation
25
-
26
- ```bash
27
- yarn add temba-checkbox
28
- ```
29
-
30
- ```js
31
- import 'temba-checkbox/temba-checkbox.js';
32
- ```
33
-
34
24
  ```js preview-story
35
25
  export const Default = () =>
36
- html` <temba-checkbox label="Click me for good luck"></temba-checkbox> `;
26
+ html`<temba-checkbox label="Click me for good luck"></temba-checkbox> `;
37
27
  ```
38
28
 
39
29
  ## Variations
@@ -0,0 +1,11 @@
1
+ // import { html, fixture, expect } from '@open-wc/testing';
2
+ // import { TembaSlider } from '../src/slider/TembaSlider';
3
+ // import { assertScreenshot, getClip } from './utils.test';
4
+
5
+ describe('temba-slider', () => {
6
+ // it('renders default slider', async () => {});
7
+ // it('renders a slider with visible ranges', async () => {});
8
+ // it('updates slider position on element value change', async () => {});
9
+ // it('updates slider position on when track clicked', async () => {});
10
+ // it('updates slider position on circle drag', async () => {});
11
+ });