@nyaruka/temba-components 0.157.1 → 0.158.1

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/orca/setup.sh ADDED
@@ -0,0 +1,81 @@
1
+ #!/bin/bash
2
+
3
+ # Orca setup hook for a temba-components worktree.
4
+ # Symlinks shared utility files from nyaruka/utils, ensures the devcontainer
5
+ # is built/running (recreating it if the bind mounts are stale), and installs
6
+ # pnpm dependencies inside the worktree.
7
+ #
8
+ # Usage:
9
+ # ./orca/setup.sh # run directly
10
+ # (orca invokes this via orca.yaml's scripts.setup)
11
+
12
+ set -e
13
+
14
+ CONTAINER_NAME="temba-components"
15
+
16
+ # Orca exports ORCA_WORKTREE_PATH when running this as a hook; otherwise
17
+ # derive the worktree path from the script location.
18
+ if [ -n "${ORCA_WORKTREE_PATH:-}" ]; then
19
+ SCRIPT_DIR="$ORCA_WORKTREE_PATH"
20
+ else
21
+ SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
22
+ fi
23
+ WORKSPACE_NAME="$(basename "$SCRIPT_DIR")"
24
+
25
+ # Pin WORKTREES to the orca workspaces dir so .devcontainer/devcontainer.json
26
+ # (which reads ${localEnv:WORKTREES}) sees the orca tree, not a stale value
27
+ # inherited from the user's shell.
28
+ WORKTREES="$(dirname "$(dirname "$SCRIPT_DIR")")"
29
+ export WORKTREES
30
+
31
+ # Symlink shared files from nyaruka/utils into the worktree (gitignored).
32
+ UTILS_DIR="${UTILS_PATH:-$HOME/code/nyaruka/utils}/projects/temba-components"
33
+ if [ ! -d "$UTILS_DIR" ]; then
34
+ echo "Error: utils dir not found at $UTILS_DIR (set UTILS_PATH to override)"
35
+ exit 1
36
+ fi
37
+ for file in run.sh AGENTS.md; do
38
+ if [ -f "$UTILS_DIR/$file" ] && [ ! -e "$SCRIPT_DIR/$file" ]; then
39
+ ln -s "$UTILS_DIR/$file" "$SCRIPT_DIR/$file"
40
+ fi
41
+ done
42
+ if [ ! -e "$SCRIPT_DIR/CLAUDE.md" ]; then
43
+ ln -s "$UTILS_DIR/AGENTS.md" "$SCRIPT_DIR/CLAUDE.md"
44
+ fi
45
+
46
+ # Ensure the devcontainer is running. Bind mounts can't be edited after the
47
+ # container is created, so if an existing container's /workspaces/worktrees
48
+ # mount doesn't match $WORKTREES (e.g. a stale conductor-era container),
49
+ # recreate it to pick up the new mount.
50
+ state="$(docker inspect -f '{{.State.Status}}' "$CONTAINER_NAME" 2>/dev/null || true)"
51
+
52
+ if [ -n "$state" ]; then
53
+ current_worktrees="$(docker inspect -f '{{range .Mounts}}{{if eq .Destination "/workspaces/worktrees"}}{{.Source}}{{end}}{{end}}' "$CONTAINER_NAME" 2>/dev/null || true)"
54
+ if [ -n "$current_worktrees" ] && [ "$current_worktrees" != "$WORKTREES" ]; then
55
+ echo "Container '$CONTAINER_NAME' has a stale /workspaces/worktrees mount:"
56
+ echo " $current_worktrees (expected $WORKTREES)"
57
+ echo "Removing and recreating the container..."
58
+ docker rm -f "$CONTAINER_NAME" >/dev/null
59
+ state=""
60
+ fi
61
+ fi
62
+
63
+ case "$state" in
64
+ running) ;;
65
+ "")
66
+ echo "Container '$CONTAINER_NAME' does not exist — building devcontainer..."
67
+ devcontainer up --workspace-folder "$SCRIPT_DIR"
68
+ ;;
69
+ *)
70
+ echo "Container '$CONTAINER_NAME' is $state — starting..."
71
+ docker start "$CONTAINER_NAME"
72
+ ;;
73
+ esac
74
+
75
+ # Install dependencies inside the worktree.
76
+ docker exec "$CONTAINER_NAME" bash -c '
77
+ cd "/workspaces/worktrees/temba-components/'"$WORKSPACE_NAME"'"
78
+ pnpm install
79
+ '
80
+
81
+ echo "Worktree '$WORKSPACE_NAME' ready for development"
package/orca.yaml ADDED
@@ -0,0 +1,3 @@
1
+ scripts:
2
+ setup: |
3
+ ./orca/setup.sh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyaruka/temba-components",
3
- "version": "0.157.1",
3
+ "version": "0.158.1",
4
4
  "description": "Web components to support rapidpro and related projects",
5
5
  "author": "Nyaruka <code@nyaruka.coim>",
6
6
  "main": "dist/index.js",
@@ -205,13 +205,22 @@ export class Dropdown extends RapidElement {
205
205
  return;
206
206
  }
207
207
 
208
+ // Anchor the dropdown to the toggle's viewport coordinates.
209
+ // The dropdown is `position: fixed`, so viewport coords are
210
+ // the right reference frame. Without an explicit anchor the
211
+ // browser resolves `top: auto; left: auto` from the element's
212
+ // in-flow position, which mis-places the dropdown when the
213
+ // page is scrolled. Always setting top/left from
214
+ // getBoundingClientRect makes positioning scroll-invariant.
208
215
  const dropdownStyle = {
209
216
  border: '1px solid rgba(0,0,0,0.1)',
210
- marginTop: '0.5em'
217
+ marginTop: '0.5em',
218
+ top: toggleBounds.bottom + 'px',
219
+ left: toggleBounds.left + 'px'
211
220
  };
212
221
 
213
222
  // if off the the right, bump it left
214
- if (dropdownBounds.right > window.innerWidth) {
223
+ if (toggleBounds.left + dropdownBounds.width > window.innerWidth) {
215
224
  dropdownStyle['left'] =
216
225
  toggleBounds.right - dropdownBounds.width + 'px';
217
226
  delete dropdownStyle['right'];
@@ -219,7 +228,7 @@ export class Dropdown extends RapidElement {
219
228
  }
220
229
 
221
230
  // if off to the bottom, bump it up
222
- if (dropdownBounds.bottom > window.innerHeight) {
231
+ if (toggleBounds.bottom + dropdownBounds.height > window.innerHeight) {
223
232
  dropdownStyle['top'] = toggleBounds.top - dropdownBounds.height + 'px';
224
233
  dropdownStyle['marginTop'] = '-0.5em';
225
234
  bumpedUp = true;
@@ -237,9 +246,12 @@ export class Dropdown extends RapidElement {
237
246
  // anchored to far-left toggles (e.g. rail items) don't rub against
238
247
  // the window edge. Shift the dropdown right and slide the arrow
239
248
  // back the same amount so it still points at the toggle.
249
+ // Check against the intended `left` (toggleBounds.left) rather
250
+ // than the dropdown's currently-rendered bounds, since the new
251
+ // left is what we're about to set.
240
252
  const MIN_LEFT = 8;
241
- if (dropdownBounds.left < MIN_LEFT && !bumpedLeft) {
242
- const shift = MIN_LEFT - dropdownBounds.left;
253
+ if (toggleBounds.left < MIN_LEFT && !bumpedLeft) {
254
+ const shift = MIN_LEFT - toggleBounds.left;
243
255
  dropdownStyle['left'] = MIN_LEFT + 'px';
244
256
  arrowLeft -= shift;
245
257
  }
@@ -13,6 +13,7 @@ import {
13
13
  UpdateFieldEvent,
14
14
  URNsChangedEvent
15
15
  } from '../events';
16
+ import { getLanguageName } from '../languages';
16
17
  import { oxfordFn } from '../utils';
17
18
 
18
19
  export enum Events {
@@ -291,9 +292,7 @@ export const renderTicketAssigneeChanged = (
291
292
  // consistently interactive across open / close / reopen / assigned
292
293
  // rows (the contact-history page can show events from any of the
293
294
  // contact's tickets, so the jump-to-ticket affordance is useful).
294
- const ticketLink = html`<a href="/ticket/all/open/${ticketUUID}/"
295
- >ticket</a
296
- >`;
295
+ const ticketLink = html`<a href="/ticket/all/open/${ticketUUID}/">ticket</a>`;
297
296
  const ticketLinkCapitalized = html`<a href="/ticket/all/open/${ticketUUID}/"
298
297
  >This ticket</a
299
298
  >`;
@@ -356,11 +355,7 @@ export const renderContactGroupsEvent = (
356
355
  if (event.groups_added) {
357
356
  return renderInfoList('Added to', 'Added to', event.groups_added);
358
357
  } else if (event.groups_removed) {
359
- return renderInfoList(
360
- 'Removed from',
361
- 'Removed from',
362
- event.groups_removed
363
- );
358
+ return renderInfoList('Removed from', 'Removed from', event.groups_removed);
364
359
  }
365
360
  };
366
361
 
@@ -382,7 +377,7 @@ export const renderContactLanguageChangedEvent = (
382
377
  return html`<div style=${eventLineStyle}>Cleared language</div>`;
383
378
  }
384
379
  return html`<div style=${eventLineStyle}>
385
- Language updated to ${valueText(event.language)}
380
+ Language updated to ${valueText(getLanguageName(event.language))}
386
381
  </div>`;
387
382
  };
388
383
 
@@ -219,13 +219,18 @@ export class CanvasNode extends RapidElement {
219
219
  background: repeating-linear-gradient(120deg, tomato, tomato 6px, #ff7056 0, #ff7056 18px) !important;
220
220
  }
221
221
 
222
- /* Disable links on actions/nodes with issues */
222
+ /* Disable links on actions/nodes with issues so clicks fall through
223
+ to open the editor instead of navigating. */
223
224
  .action-content.has-issues .linked-name div,
224
225
  .node.has-issues > .router .linked-name div {
225
226
  text-decoration: none !important;
226
227
  cursor: default !important;
227
228
  pointer-events: none;
228
229
  }
230
+ .action-content.has-issues .linked-pill,
231
+ .node.has-issues > .router .linked-pill {
232
+ pointer-events: none;
233
+ }
229
234
 
230
235
  .action.sortable {
231
236
  display: flex;
@@ -1094,13 +1099,16 @@ export class CanvasNode extends RapidElement {
1094
1099
  }
1095
1100
 
1096
1101
  /**
1097
- * Returns true if the click target is inside a `.linked-name` that is
1098
- * still active (i.e. the containing action/node has no issues).
1099
- * When an action/node has issues, links are visually disabled and clicks
1100
- * should fall through to open the editor instead.
1102
+ * Returns true if the click target is inside a `.linked-name` or
1103
+ * `.linked-pill` whose containing action/node has no issues. Active
1104
+ * links handle their own navigation, so click-vs-drag and node-edit
1105
+ * handlers bail out on them. When the action/node has issues, links are
1106
+ * visually disabled (see CSS) and clicks fall through to open the
1107
+ * editor instead.
1101
1108
  */
1102
1109
  private isActiveLink(target: HTMLElement, action?: Action): boolean {
1103
- if (!target.closest('.linked-name')) return false;
1110
+ if (!target.closest('.linked-name') && !target.closest('.linked-pill'))
1111
+ return false;
1104
1112
  if (action) return !this.issuesByAction?.has(action.uuid);
1105
1113
  return !(
1106
1114
  this.issuesByNode?.has(this.node.uuid) ||
@@ -127,7 +127,8 @@ export class DragManager {
127
127
  if (
128
128
  target.classList.contains('exit') ||
129
129
  target.closest('.exit') ||
130
- target.closest('.linked-name')
130
+ target.closest('.linked-name') ||
131
+ target.closest('.linked-pill')
131
132
  ) {
132
133
  return;
133
134
  }
@@ -186,7 +187,8 @@ export class DragManager {
186
187
  if (
187
188
  target.classList.contains('exit') ||
188
189
  target.closest('.exit') ||
189
- target.closest('.linked-name')
190
+ target.closest('.linked-name') ||
191
+ target.closest('.linked-pill')
190
192
  ) {
191
193
  return;
192
194
  }
package/src/flow/utils.ts CHANGED
@@ -386,6 +386,7 @@ const renderLinkedObject = (
386
386
 
387
387
  const pillType = iconToPillType(icon);
388
388
  return html`<temba-label
389
+ class="linked-pill"
389
390
  icon=${icon || ''}
390
391
  type=${pillType || 'neutral'}
391
392
  clickable
@@ -19,6 +19,7 @@ export class DatePicker extends FieldElement {
19
19
  .container {
20
20
  border-radius: var(--curvature);
21
21
  border: 1px solid var(--color-widget-border);
22
+ background: var(--color-widget-bg);
22
23
  display: flex;
23
24
  cursor: pointer;
24
25
  box-shadow: var(--widget-box-shadow);
@@ -59,7 +60,7 @@ export class DatePicker extends FieldElement {
59
60
  }
60
61
 
61
62
  .tz-wrapper {
62
- background: #efefef;
63
+ background: var(--sunken);
63
64
  display: flex;
64
65
  flex-direction: row;
65
66
  align-items: center;
package/src/interfaces.ts CHANGED
@@ -323,5 +323,8 @@ export enum CustomEventType {
323
323
  RevisionViewed = 'temba-revision-viewed',
324
324
  RevisionCancelled = 'temba-revision-cancelled',
325
325
  RevisionReverted = 'temba-revision-reverted',
326
- RevisionsClosed = 'temba-revisions-closed'
326
+ RevisionsClosed = 'temba-revisions-closed',
327
+ RowClick = 'temba-row-click',
328
+ SelectionChange = 'temba-selection-change',
329
+ BulkAction = 'temba-bulk-action'
327
330
  }
package/src/layout/Tab.ts CHANGED
@@ -27,24 +27,9 @@ export class Tab extends RapidElement {
27
27
  @property({ type: String })
28
28
  icon: string;
29
29
 
30
- @property({ type: String })
31
- selectionColor: string;
32
-
33
- @property({ type: String })
34
- selectionBackground: string;
35
-
36
- @property({ type: String })
37
- borderColor: string = 'var(--color-widget-border)';
38
-
39
- @property({ type: String })
40
- activityColor: string = `var(--color-link-primary)`;
41
-
42
30
  @property({ type: Boolean })
43
31
  selected = false;
44
32
 
45
- @property({ type: Boolean })
46
- notify = false;
47
-
48
33
  @property({ type: Boolean })
49
34
  alert = false;
50
35
 
@@ -18,43 +18,48 @@ export class TabPane extends RapidElement {
18
18
  .options {
19
19
  display: flex;
20
20
  align-items: stretch;
21
- padding: var(--temba-tabs-options-padding, 0);
22
- border-bottom: none;
21
+ gap: 4px;
22
+ border-bottom: 1px solid var(--border);
23
23
  }
24
24
 
25
25
  .option {
26
26
  user-select: none;
27
- padding: 0.5em 0.7em;
28
- margin: 0em 0em;
29
- cursor: pointer;
30
27
  display: flex;
31
- font-size: 1.1em;
32
28
  align-items: center;
33
- border: 1px inset transparent;
34
- border-bottom: 0px;
35
- border-radius: var(--curvature);
36
- border-bottom-right-radius: 0px;
37
- border-bottom-left-radius: 0px;
38
-
39
- color: var(--color-text-dark);
40
- --icon-color: var(--color-text-dark);
29
+ cursor: pointer;
30
+ padding: 8px 14px 10px;
31
+ margin-bottom: -1px;
32
+ background: transparent;
33
+ color: var(--text-2);
34
+ --icon-color: var(--text-2);
35
+ font-size: 13px;
36
+ font-weight: var(--w-medium);
37
+ border-bottom: 2px solid transparent;
41
38
  white-space: nowrap;
42
- transition: all 100ms linear;
39
+ transition:
40
+ color 100ms linear,
41
+ border-color 100ms linear;
43
42
  }
44
43
 
45
- .focusedname .option .name {
46
- transition: all 0s linear !important;
44
+ .option:hover {
45
+ color: var(--text-1);
46
+ --icon-color: var(--text-1);
47
47
  }
48
48
 
49
- .focusedname .option.selected .name {
50
- transition: all 200ms linear !important;
49
+ .option.selected,
50
+ .option.selected:hover {
51
+ cursor: default;
52
+ color: var(--accent-700);
53
+ --icon-color: var(--accent-700);
54
+ border-bottom-color: var(--accent-600);
51
55
  }
52
56
 
53
- .option.hidden {
54
- display: none;
57
+ .unselect .option.selected {
58
+ cursor: pointer;
55
59
  }
56
60
 
57
- .option temba-icon {
61
+ .option.hidden {
62
+ display: none;
58
63
  }
59
64
 
60
65
  .option .name {
@@ -70,6 +75,9 @@ export class TabPane extends RapidElement {
70
75
 
71
76
  .option .badge {
72
77
  margin-left: 0.4em;
78
+ margin-right: -6px;
79
+ display: inline-flex;
80
+ align-items: center;
73
81
  }
74
82
 
75
83
  @media (max-width: 900px) {
@@ -85,9 +93,6 @@ export class TabPane extends RapidElement {
85
93
  }
86
94
  }
87
95
 
88
- .focusedname .option.selected {
89
- }
90
-
91
96
  .focusedname .option .name {
92
97
  max-width: 0px;
93
98
  margin: 0;
@@ -101,137 +106,81 @@ export class TabPane extends RapidElement {
101
106
  max-width: 200px;
102
107
  }
103
108
 
104
- .option {
105
- transform: scale(0.9) translateY(0em);
106
- --icon-color: rgba(0, 0, 0, 0.5);
107
- color: rgba(0, 0, 0, 0.5);
108
- }
109
-
110
- .option.selected {
111
- }
112
-
113
- .option.selected,
114
- .option.selected:hover {
115
- cursor: default;
116
- box-shadow: 0px -3px 3px 1px rgba(0, 0, 0, 0.02);
117
-
118
- background: var(--focused-tab-color, #fff);
119
- transform: scale(1) translateY(1px);
120
- --icon-color: #666;
121
- color: #666;
122
- border: 1px inset rgba(0, 0, 0, 0.15);
123
- border-bottom: 0px;
124
- }
125
-
126
- .option.selected .dot {
127
- display: none;
128
- }
129
-
130
- .unselect .option.selected {
131
- cursor: pointer;
109
+ .focusedname .option .name {
110
+ transition: all 0s linear !important;
132
111
  }
133
112
 
134
- .unselect .option.selected:hover {
135
- background: var(--unselect-tab-color, #eee);
113
+ .focusedname .option.selected .name {
114
+ transition: all 200ms linear !important;
136
115
  }
137
116
 
138
- .option:hover {
139
- --icon-color: #666;
140
- color: #666;
141
- background: rgba(0, 0, 0, 0.02);
117
+ .option.dirty {
118
+ font-weight: var(--w-semibold);
142
119
  }
143
120
 
144
- .option.dirty {
145
- font-weight: 500;
121
+ .option.alert {
122
+ color: var(--danger);
123
+ --icon-color: var(--danger);
146
124
  }
147
125
 
148
126
  .pane {
149
127
  display: flex;
150
128
  flex-direction: column;
151
129
  flex-grow: 1;
152
- background: var(--focused-tab-color, #fff);
153
- border-bottom-left-radius: var(--curvature);
154
- border-bottom-right-radius: var(--curvature);
155
- overflow: hidden;
156
-
157
- box-shadow: var(
158
- --tabs-shadow,
159
- rgba(0, 0, 0, 0.1) 0px 1px 3px 0px,
160
- rgba(0, 0, 0, 0.03) 0px 1px 2px 0px
161
- );
162
130
  min-height: 0;
163
- }
164
-
165
- .pane.first {
166
- border-top-left-radius: 0px;
167
131
  overflow: hidden;
168
132
  }
169
133
 
170
134
  .count {
171
- border-radius: 99px;
172
- background: rgba(0, 0, 0, 0.1);
173
- color: rgba(0, 0, 0, 0.5);
174
- font-size: 0.7em;
175
- font-weight: 500;
176
- min-width: 1.5em;
177
- text-align: center;
178
- }
179
-
180
- .dot {
181
- height: 0.5em;
182
- width: 0.5em;
183
- margin-left: 0.2em;
184
- background: var(--color-primary-dark);
185
- border-radius: 99px;
186
- }
187
-
188
- .notify .count {
189
- background: var(--color-alert);
190
- color: #fff;
135
+ display: inline-flex;
136
+ align-items: center;
137
+ justify-content: center;
138
+ height: 16px;
139
+ padding: 0 2px;
140
+ color: inherit;
141
+ opacity: 0.6;
142
+ font-size: 11px;
143
+ font-weight: var(--w-medium);
144
+ font-variant-numeric: tabular-nums;
191
145
  }
192
146
 
193
- .alert {
194
- color: var(--color-alert);
195
- --icon-color: var(--color-alert);
147
+ .option.selected .count,
148
+ .option.alert .count {
149
+ min-width: 16px;
150
+ padding: 0 4px;
151
+ border-radius: 999px;
152
+ opacity: 1;
196
153
  }
197
154
 
198
- .embedded.pane {
199
- box-shadow: none;
200
- margin: 0;
201
- border-left: none !important;
202
- border-right: none !important;
203
- border-bottom: none !important;
155
+ .option.selected .count {
156
+ background: var(--accent-100);
157
+ color: var(--accent-700);
158
+ font-weight: var(--w-semibold);
204
159
  }
205
160
 
206
- .embedded .option {
207
- border-bottom: none !important;
208
- border-radius: 0em;
209
- border-top: none !important;
161
+ .option.alert .count {
162
+ background: var(--danger-bg);
163
+ color: var(--danger);
210
164
  }
211
165
 
212
- .embedded .option.first {
213
- margin-left: 0em;
214
- border-top: none !important;
215
- border-left: none;
166
+ .dot {
167
+ height: 0.5em;
168
+ width: 0.5em;
169
+ margin-left: 0.2em;
170
+ background: var(--accent-600);
171
+ border-radius: 99px;
216
172
  }
217
173
 
218
- .embedded.options .option.selected {
219
- box-shadow: none !important;
174
+ .option.selected .dot {
175
+ display: none;
220
176
  }
221
177
 
222
178
  .check {
223
179
  margin-left: 0.4em;
224
180
  }
225
-
226
- .pane {
227
- display: flex;
228
- }
229
181
  `;
230
182
  }
231
183
 
232
- @property({ type: Boolean })
233
- embedded = false;
234
-
235
184
  @property({ type: Boolean })
236
185
  collapses = false;
237
186
 
@@ -277,7 +226,9 @@ export class TabPane extends RapidElement {
277
226
  }
278
227
  }
279
228
  this.options = tabs;
280
- this.index = 0;
229
+ if (this.index < 0 || this.index >= tabs.length) {
230
+ this.index = 0;
231
+ }
281
232
  }
282
233
 
283
234
  public firstUpdated(
@@ -367,13 +318,11 @@ export class TabPane extends RapidElement {
367
318
  }
368
319
 
369
320
  public render(): TemplateResult {
370
- const activeTab = this.options[this.index];
371
321
  return html`
372
322
  <div
373
323
  class="${getClasses({
374
324
  options: true,
375
325
  collapses: this.collapses,
376
- embedded: this.embedded,
377
326
  focusedname: this.focusedName,
378
327
  unselect: this.unselect
379
328
  })}"
@@ -388,15 +337,9 @@ export class TabPane extends RapidElement {
388
337
  first: index == 0,
389
338
  selected: index == this.index,
390
339
  hidden: tab.hidden,
391
- notify: tab.notify,
392
340
  alert: tab.alert,
393
341
  dirty: tab.dirty
394
342
  })}"
395
- style="${tab.selectionColor && index == this.index
396
- ? `color:${tab.selectionColor};--icon-color:${tab.selectionColor};`
397
- : ''} ${tab.selectionBackground && index == this.index
398
- ? `background-color:${tab.selectionBackground};`
399
- : ''}"
400
343
  >
401
344
  ${tab.icon ? html`<temba-icon name=${tab.icon} />` : null}
402
345
  <div class="name">${tab.name} ${tab.dirty ? ` *` : ``}</div>
@@ -405,14 +348,11 @@ export class TabPane extends RapidElement {
405
348
  <div class="badge">
406
349
  ${tab.count > 0 && !tab.activity
407
350
  ? html`<div class="count">
408
- ${tab.activity ? '' : tab.count.toLocaleString()}
351
+ ${tab.count.toLocaleString()}
409
352
  </div>`
410
353
  : null}
411
354
  ${tab.activity && tab.count > 0 && !tab.dirty
412
- ? html`<div
413
- class="dot"
414
- style="background:${tab.activityColor}"
415
- ></div>`
355
+ ? html`<div class="dot"></div>`
416
356
  : null}
417
357
  </div>
418
358
  `
@@ -429,35 +369,7 @@ export class TabPane extends RapidElement {
429
369
  <slot name="tab-right"></slot>
430
370
  </div>
431
371
  </div>
432
- <div
433
- @temba-details-changed=${this.handleTabDetailsChanged}
434
- style="${activeTab?.borderColor
435
- ? `
436
- border-top: var(--temba-tabs-border-top, 1px solid ${
437
- activeTab?.borderColor || 'var(--color-widget-border)'
438
- });
439
-
440
- border-left: var(--temba-tabs-border-left, 1px solid ${
441
- activeTab?.borderColor || 'var(--color-widget-border)'
442
- });
443
-
444
- border-bottom: var(--temba-tabs-border-bottom, 1px solid ${
445
- activeTab?.borderColor || 'var(--color-widget-border)'
446
- });
447
-
448
- border-right: var(--temba-tabs-border-right, 1px solid ${
449
- activeTab?.borderColor || 'var(--color-widget-border)'
450
- });
451
-
452
- `
453
- : ''} ${activeTab?.selectionBackground
454
- ? `background:${activeTab?.selectionBackground};`
455
- : ``}"
456
- class="pane ${getClasses({
457
- first: this.index == 0,
458
- embedded: this.embedded
459
- })}"
460
- >
372
+ <div @temba-details-changed=${this.handleTabDetailsChanged} class="pane">
461
373
  <slot></slot>
462
374
  <slot name="pane-bottom"></slot>
463
375
  </div>