@social.io/catalog 1.3.0 → 2.0.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 (124) hide show
  1. package/.smartconfig.json +51 -0
  2. package/dist_bundle/bundle.js +5450 -3267
  3. package/dist_ts_web/00_commitinfo_data.js +3 -3
  4. package/dist_ts_web/elements/index.d.ts +20 -3
  5. package/dist_ts_web/elements/index.js +27 -7
  6. package/dist_ts_web/elements/sio-ai-card.d.ts +39 -0
  7. package/dist_ts_web/elements/sio-ai-card.js +229 -0
  8. package/dist_ts_web/elements/sio-avatar.d.ts +46 -0
  9. package/dist_ts_web/elements/sio-avatar.js +379 -0
  10. package/dist_ts_web/elements/sio-bucket-header.d.ts +36 -0
  11. package/dist_ts_web/elements/sio-bucket-header.js +197 -0
  12. package/dist_ts_web/elements/sio-button.d.ts +7 -3
  13. package/dist_ts_web/elements/sio-button.js +253 -152
  14. package/dist_ts_web/elements/sio-channel-rail.d.ts +37 -0
  15. package/dist_ts_web/elements/sio-channel-rail.js +275 -0
  16. package/dist_ts_web/elements/sio-chip.d.ts +25 -0
  17. package/dist_ts_web/elements/sio-chip.js +188 -0
  18. package/dist_ts_web/elements/sio-cmdk.d.ts +59 -0
  19. package/dist_ts_web/elements/sio-cmdk.js +471 -0
  20. package/dist_ts_web/elements/sio-combox.d.ts +55 -1
  21. package/dist_ts_web/elements/sio-combox.js +458 -261
  22. package/dist_ts_web/elements/sio-composer.d.ts +48 -0
  23. package/dist_ts_web/elements/sio-composer.js +357 -0
  24. package/dist_ts_web/elements/sio-conversation-selector.d.ts +41 -1
  25. package/dist_ts_web/elements/sio-conversation-selector.js +426 -238
  26. package/dist_ts_web/elements/sio-conversation-view.d.ts +34 -6
  27. package/dist_ts_web/elements/sio-conversation-view.js +738 -564
  28. package/dist_ts_web/elements/sio-dropdown-menu.d.ts +6 -6
  29. package/dist_ts_web/elements/sio-dropdown-menu.js +223 -169
  30. package/dist_ts_web/elements/sio-fab.d.ts +23 -5
  31. package/dist_ts_web/elements/sio-fab.js +259 -235
  32. package/dist_ts_web/elements/sio-glass-toolbar.d.ts +22 -0
  33. package/dist_ts_web/elements/sio-glass-toolbar.js +178 -0
  34. package/dist_ts_web/elements/sio-icon.d.ts +1 -0
  35. package/dist_ts_web/elements/sio-icon.js +254 -94
  36. package/dist_ts_web/elements/sio-image-lightbox.d.ts +3 -4
  37. package/dist_ts_web/elements/sio-image-lightbox.js +282 -191
  38. package/dist_ts_web/elements/sio-inset-list.d.ts +28 -0
  39. package/dist_ts_web/elements/sio-inset-list.js +191 -0
  40. package/dist_ts_web/elements/sio-kbd.d.ts +16 -0
  41. package/dist_ts_web/elements/sio-kbd.js +117 -0
  42. package/dist_ts_web/elements/sio-list-row.d.ts +55 -0
  43. package/dist_ts_web/elements/sio-list-row.js +462 -0
  44. package/dist_ts_web/elements/sio-message-input.d.ts +25 -4
  45. package/dist_ts_web/elements/sio-message-input.js +231 -226
  46. package/dist_ts_web/elements/sio-paper-thumb.d.ts +24 -0
  47. package/dist_ts_web/elements/sio-paper-thumb.js +293 -0
  48. package/dist_ts_web/elements/sio-pdf-viewer.d.ts +2 -0
  49. package/dist_ts_web/elements/sio-pdf-viewer.js +344 -244
  50. package/dist_ts_web/elements/sio-segmented.d.ts +32 -0
  51. package/dist_ts_web/elements/sio-segmented.js +217 -0
  52. package/dist_ts_web/elements/sio-step-timeline.d.ts +28 -0
  53. package/dist_ts_web/elements/sio-step-timeline.js +222 -0
  54. package/dist_ts_web/elements/sio-tab-bar.d.ts +36 -0
  55. package/dist_ts_web/elements/sio-tab-bar.js +232 -0
  56. package/dist_ts_web/elements/sio-unread-badge.d.ts +25 -0
  57. package/dist_ts_web/elements/sio-unread-badge.js +203 -0
  58. package/dist_ts_web/elements/sio-waveform.d.ts +34 -0
  59. package/dist_ts_web/elements/sio-waveform.js +304 -0
  60. package/dist_ts_web/elements/tokens.d.ts +70 -0
  61. package/dist_ts_web/elements/tokens.js +277 -0
  62. package/dist_ts_web/pages/firstpage.d.ts +5 -0
  63. package/dist_ts_web/pages/firstpage.js +21 -2
  64. package/dist_ts_web/pages/mainpage.d.ts +6 -0
  65. package/dist_ts_web/pages/mainpage.js +111 -39
  66. package/{license → license.md} +3 -1
  67. package/package.json +23 -29
  68. package/readme.hints.md +81 -0
  69. package/readme.md +85 -151
  70. package/ts_web/00_commitinfo_data.ts +2 -2
  71. package/ts_web/elements/index.ts +30 -7
  72. package/ts_web/elements/sio-ai-card.ts +175 -0
  73. package/ts_web/elements/sio-avatar.ts +295 -0
  74. package/ts_web/elements/sio-bucket-header.ts +157 -0
  75. package/ts_web/elements/sio-button.ts +149 -127
  76. package/ts_web/elements/sio-channel-rail.ts +243 -0
  77. package/ts_web/elements/sio-chip.ts +134 -0
  78. package/ts_web/elements/sio-cmdk.ts +461 -0
  79. package/ts_web/elements/sio-combox.ts +327 -204
  80. package/ts_web/elements/sio-composer.ts +306 -0
  81. package/ts_web/elements/sio-conversation-selector.ts +350 -228
  82. package/ts_web/elements/sio-conversation-view.ts +573 -457
  83. package/ts_web/elements/sio-dropdown-menu.ts +112 -130
  84. package/ts_web/elements/sio-fab.ts +192 -227
  85. package/ts_web/elements/sio-glass-toolbar.ts +140 -0
  86. package/ts_web/elements/sio-icon.ts +147 -25
  87. package/ts_web/elements/sio-image-lightbox.ts +62 -64
  88. package/ts_web/elements/sio-inset-list.ts +140 -0
  89. package/ts_web/elements/sio-kbd.ts +80 -0
  90. package/ts_web/elements/sio-list-row.ts +393 -0
  91. package/ts_web/elements/sio-message-input.ts +142 -221
  92. package/ts_web/elements/sio-paper-thumb.ts +244 -0
  93. package/ts_web/elements/sio-pdf-viewer.ts +58 -60
  94. package/ts_web/elements/sio-segmented.ts +183 -0
  95. package/ts_web/elements/sio-step-timeline.ts +179 -0
  96. package/ts_web/elements/sio-tab-bar.ts +195 -0
  97. package/ts_web/elements/sio-unread-badge.ts +147 -0
  98. package/ts_web/elements/sio-waveform.ts +239 -0
  99. package/ts_web/elements/tokens.ts +297 -0
  100. package/ts_web/pages/firstpage.ts +20 -1
  101. package/ts_web/pages/mainpage.ts +111 -39
  102. package/dist_bundle/bundle.js.map +0 -7
  103. package/dist_ts_web/elements/00colors.d.ts +0 -108
  104. package/dist_ts_web/elements/00colors.js +0 -137
  105. package/dist_ts_web/elements/00fonts.d.ts +0 -60
  106. package/dist_ts_web/elements/00fonts.js +0 -129
  107. package/dist_ts_web/elements/00tokens.d.ts +0 -123
  108. package/dist_ts_web/elements/00tokens.js +0 -176
  109. package/dist_ts_web/elements/sio-conversation-list.d.ts +0 -20
  110. package/dist_ts_web/elements/sio-conversation-list.js +0 -495
  111. package/dist_ts_web/elements/sio-recorder.d.ts +0 -60
  112. package/dist_ts_web/elements/sio-recorder.js +0 -173
  113. package/dist_ts_web/elements/sio-subwidget-conversations.d.ts +0 -16
  114. package/dist_ts_web/elements/sio-subwidget-conversations.js +0 -350
  115. package/dist_ts_web/elements/sio-subwidget-onboardme.d.ts +0 -7
  116. package/dist_ts_web/elements/sio-subwidget-onboardme.js +0 -243
  117. package/dist_watch/bundle.js +0 -81694
  118. package/dist_watch/bundle.js.map +0 -7
  119. package/dist_watch/index.html +0 -25
  120. package/npmextra.json +0 -19
  121. package/ts_web/elements/00colors.ts +0 -164
  122. package/ts_web/elements/00fonts.ts +0 -145
  123. package/ts_web/elements/00tokens.ts +0 -197
  124. package/ts_web/elements/sio-recorder.ts +0 -213
@@ -3,16 +3,22 @@ import {
3
3
  html,
4
4
  property,
5
5
  customElement,
6
- cssManager,
7
6
  css,
8
- unsafeCSS,
9
7
  type TemplateResult,
10
8
  } from '@design.estate/dees-element';
11
9
 
12
- // Import design tokens
13
- import { colors, bdTheme } from './00colors.js';
14
- import { spacing, radius, shadows, transitions } from './00tokens.js';
15
- import { fontFamilies, typography } from './00fonts.js';
10
+ import { sioElementStyles } from './tokens.js';
11
+ import './sio-icon.js';
12
+
13
+ export type TSioButtonType =
14
+ | 'default'
15
+ | 'primary'
16
+ | 'secondary'
17
+ | 'destructive'
18
+ | 'outline'
19
+ | 'ghost';
20
+ export type TSioButtonSize = 'sm' | 'default' | 'lg';
21
+ export type TSioButtonStatus = 'normal' | 'pending' | 'success' | 'error';
16
22
 
17
23
  declare global {
18
24
  interface HTMLElementTagNameMap {
@@ -23,15 +29,45 @@ declare global {
23
29
  @customElement('sio-button')
24
30
  export class SioButton extends DeesElement {
25
31
  public static demo = () => html`
26
- <div style="display: flex; gap: 16px; flex-wrap: wrap; align-items: center;">
27
- <sio-button>Default</sio-button>
28
- <sio-button type="primary">Primary</sio-button>
29
- <sio-button type="destructive">Delete</sio-button>
30
- <sio-button type="outline">Outline</sio-button>
31
- <sio-button type="ghost">Ghost</sio-button>
32
- <sio-button size="sm">Small</sio-button>
33
- <sio-button size="lg">Large</sio-button>
34
- <sio-button disabled>Disabled</sio-button>
32
+ <div
33
+ style="display: flex; flex-direction: column; gap: 20px; padding: 24px; background: var(--sio-bg); border-radius: 14px;"
34
+ >
35
+ <div style="display: flex; gap: 10px; flex-wrap: wrap; align-items: center;">
36
+ <sio-button type="primary">
37
+ <sio-icon icon="pencil" size="15"></sio-icon>
38
+ Compose
39
+ </sio-button>
40
+ <sio-button type="secondary">
41
+ <sio-icon icon="archive" size="15"></sio-icon>
42
+ Archive
43
+ </sio-button>
44
+ <sio-button type="outline">
45
+ <sio-icon icon="clock" size="15"></sio-icon>
46
+ Move to Later
47
+ </sio-button>
48
+ <sio-button type="ghost">Dismiss</sio-button>
49
+ <sio-button type="destructive">
50
+ <sio-icon icon="trash" size="15"></sio-icon>
51
+ Delete thread
52
+ </sio-button>
53
+ </div>
54
+ <div style="display: flex; gap: 10px; flex-wrap: wrap; align-items: center;">
55
+ <sio-button type="primary" size="sm">Reply</sio-button>
56
+ <sio-button type="secondary" size="sm">Forward</sio-button>
57
+ <sio-button type="primary">Mark as Done</sio-button>
58
+ <sio-button type="primary" size="lg">
59
+ <sio-icon icon="send" size="17"></sio-icon>
60
+ Send
61
+ </sio-button>
62
+ <sio-button type="outline" size="lg">Save draft</sio-button>
63
+ </div>
64
+ <div style="display: flex; gap: 10px; flex-wrap: wrap; align-items: center;">
65
+ <sio-button type="primary" status="pending">Sending…</sio-button>
66
+ <sio-button type="primary" status="success">Sent</sio-button>
67
+ <sio-button type="destructive" status="error">Failed</sio-button>
68
+ <sio-button type="primary" disabled>Compose</sio-button>
69
+ <sio-button type="secondary" disabled>Archive</sio-button>
70
+ </div>
35
71
  </div>
36
72
  `;
37
73
 
@@ -39,23 +75,22 @@ export class SioButton extends DeesElement {
39
75
  public accessor text: string = '';
40
76
 
41
77
  @property({ type: String })
42
- public accessor type: 'default' | 'primary' | 'secondary' | 'destructive' | 'outline' | 'ghost' = 'default';
78
+ public accessor type: TSioButtonType = 'default';
43
79
 
44
80
  @property({ type: String })
45
- public accessor size: 'sm' | 'default' | 'lg' = 'default';
81
+ public accessor size: TSioButtonSize = 'default';
46
82
 
47
83
  @property({ type: Boolean, reflect: true })
48
84
  public accessor disabled: boolean = false;
49
85
 
50
86
  @property({ type: String })
51
- public accessor status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
87
+ public accessor status: TSioButtonStatus = 'normal';
52
88
 
53
89
  public static styles = [
54
- cssManager.defaultStyles,
90
+ ...sioElementStyles,
55
91
  css`
56
92
  :host {
57
93
  display: inline-block;
58
- font-family: ${unsafeCSS(fontFamilies.sans)};
59
94
  }
60
95
 
61
96
  :host([disabled]) {
@@ -68,210 +103,199 @@ export class SioButton extends DeesElement {
68
103
  align-items: center;
69
104
  justify-content: center;
70
105
  white-space: nowrap;
71
- border-radius: 6px;
72
- font-weight: 500;
73
- font-size: 14px;
106
+ border: var(--sio-hairline) solid transparent;
107
+ border-radius: var(--sio-radius);
108
+ font-family: var(--sio-font-sf);
109
+ font-weight: 600;
74
110
  line-height: 1;
75
- letter-spacing: -0.01em;
76
- transition: all 120ms ease;
111
+ transition:
112
+ background 120ms ease,
113
+ color 120ms ease,
114
+ border-color 120ms ease,
115
+ transform 80ms ease;
77
116
  cursor: pointer;
78
117
  user-select: none;
79
118
  outline: none;
80
- border: none;
81
119
  gap: 6px;
82
120
  }
83
121
 
84
- /* Size variants */
122
+ .button:active:not(.disabled) {
123
+ transform: scale(0.98);
124
+ }
125
+
126
+ .button:focus-visible {
127
+ outline: 2px solid color-mix(in oklab, var(--sio-tint) 40%, transparent);
128
+ outline-offset: 2px;
129
+ }
130
+
131
+ /* Size ramp — SF footnote/subheadline tracking */
85
132
  .button.size-sm {
86
- height: 32px;
87
- padding: 0 12px;
88
- font-size: 13px;
133
+ height: 28px;
134
+ padding: 0 10px;
135
+ font-size: 12px;
136
+ letter-spacing: 0;
137
+ gap: 5px;
89
138
  }
90
139
 
91
140
  .button.size-default {
92
141
  height: 36px;
93
- padding: 0 16px;
94
- font-size: 14px;
142
+ padding: 0 14px;
143
+ font-size: 13px;
144
+ letter-spacing: -0.08px;
95
145
  }
96
146
 
97
147
  .button.size-lg {
98
- height: 42px;
99
- padding: 0 24px;
148
+ height: 44px;
149
+ padding: 0 20px;
100
150
  font-size: 15px;
151
+ letter-spacing: -0.24px;
101
152
  }
102
153
 
103
- /* Type variants */
104
- .button.default {
105
- background: ${bdTheme('hsl(0 0% 95%)', 'hsl(0 0% 15%)')};
106
- color: ${bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 95%)')};
154
+ /* default falls back to the secondary look (iOS gray fill) */
155
+ .button.default,
156
+ .button.secondary {
157
+ background: var(--sio-surface-2);
158
+ color: var(--sio-label);
107
159
  }
108
160
 
109
- .button.default:hover:not(.disabled) {
110
- background: ${bdTheme('hsl(0 0% 91%)', 'hsl(0 0% 20%)')};
161
+ .button.default:hover:not(.disabled),
162
+ .button.secondary:hover:not(.disabled) {
163
+ background: color-mix(in oklab, var(--sio-label) 10%, transparent);
111
164
  }
112
165
 
113
- .button.default:active:not(.disabled) {
114
- background: ${bdTheme('hsl(0 0% 87%)', 'hsl(0 0% 18%)')};
166
+ .button.default:active:not(.disabled),
167
+ .button.secondary:active:not(.disabled) {
168
+ background: color-mix(in oklab, var(--sio-label) 14%, transparent);
115
169
  }
116
170
 
171
+ /* primary — solid system tint. The on-tint label is white in both
172
+ themes per the v7 spec (solid tint/red fills always carry white). */
117
173
  .button.primary {
118
- background: ${bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 95%)')};
119
- color: ${bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 0%)')};
174
+ background: var(--sio-tint);
175
+ color: #ffffff;
120
176
  }
121
177
 
122
178
  .button.primary:hover:not(.disabled) {
123
- background: ${bdTheme('hsl(0 0% 25%)', 'hsl(0 0% 100%)')};
179
+ background: var(--sio-tint-hover);
124
180
  }
125
181
 
126
182
  .button.primary:active:not(.disabled) {
127
- background: ${bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 90%)')};
128
- }
129
-
130
- /* Secondary variant */
131
- .button.secondary {
132
- background: transparent;
133
- color: ${bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 95%)')};
134
- box-shadow: inset 0 0 0 1px ${bdTheme('hsl(0 0% 85%)', 'hsl(0 0% 25%)')};
135
- }
136
-
137
- .button.secondary:hover:not(.disabled) {
138
- background: ${bdTheme('hsl(0 0% 96%)', 'hsl(0 0% 15%)')};
183
+ background: color-mix(in oklab, var(--sio-tint) 88%, var(--sio-label));
139
184
  }
140
185
 
141
- .button.secondary:active:not(.disabled) {
142
- background: ${bdTheme('hsl(0 0% 92%)', 'hsl(0 0% 12%)')};
143
- }
144
-
145
- /* Destructive variant */
186
+ /* destructive — solid system red */
146
187
  .button.destructive {
147
- background: ${bdTheme('hsl(0 100% 95%)', 'hsl(0 50% 20%)')};
148
- color: ${bdTheme('hsl(0 100% 45%)', 'hsl(0 100% 75%)')};
188
+ background: var(--sio-red);
189
+ color: #ffffff;
149
190
  }
150
191
 
151
192
  .button.destructive:hover:not(.disabled) {
152
- background: ${bdTheme('hsl(0 100% 45%)', 'hsl(0 100% 50%)')};
153
- color: white;
193
+ background: color-mix(in oklab, var(--sio-red) 88%, var(--sio-label));
154
194
  }
155
195
 
156
196
  .button.destructive:active:not(.disabled) {
157
- background: ${bdTheme('hsl(0 100% 40%)', 'hsl(0 100% 45%)')};
158
- color: white;
197
+ background: color-mix(in oklab, var(--sio-red) 80%, var(--sio-label));
159
198
  }
160
199
 
200
+ /* outline — hairline ring, quiet label */
161
201
  .button.outline {
162
202
  background: transparent;
163
- color: ${bdTheme('hsl(0 0% 40%)', 'hsl(0 0% 70%)')};
164
- box-shadow: inset 0 0 0 1.5px ${bdTheme('hsl(0 0% 90%)', 'hsl(0 0% 30%)')};
203
+ color: var(--sio-label);
204
+ border-color: var(--sio-separator-opaque);
165
205
  }
166
206
 
167
207
  .button.outline:hover:not(.disabled) {
168
- color: ${bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 95%)')};
169
- box-shadow: inset 0 0 0 1.5px ${bdTheme('hsl(0 0% 70%)', 'hsl(0 0% 50%)')};
208
+ background: var(--sio-surface-2);
170
209
  }
171
210
 
172
211
  .button.outline:active:not(.disabled) {
173
- background: ${bdTheme('hsl(0 0% 95%)', 'hsl(0 0% 15%)')};
212
+ background: color-mix(in oklab, var(--sio-label) 10%, transparent);
174
213
  }
175
214
 
215
+ /* ghost — bare label, fill on hover */
176
216
  .button.ghost {
177
217
  background: transparent;
178
- color: ${bdTheme('hsl(0 0% 40%)', 'hsl(0 0% 70%)')};
218
+ color: var(--sio-label-2);
179
219
  }
180
220
 
181
221
  .button.ghost:hover:not(.disabled) {
182
- color: ${bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 95%)')};
183
- background: ${bdTheme('hsl(0 0% 0% / 0.05)', 'hsl(0 0% 100% / 0.05)')};
222
+ color: var(--sio-label);
223
+ background: var(--sio-surface-2);
184
224
  }
185
225
 
186
226
  .button.ghost:active:not(.disabled) {
187
- background: ${bdTheme('hsl(0 0% 0% / 0.1)', 'hsl(0 0% 100% / 0.1)')};
227
+ background: color-mix(in oklab, var(--sio-label) 10%, transparent);
188
228
  }
189
229
 
190
230
  /* Status states */
191
231
  .button.pending {
192
232
  pointer-events: none;
193
- opacity: 0.7;
233
+ opacity: 0.75;
194
234
  }
195
235
 
196
236
  .spinner {
197
- position: absolute;
198
- left: ${unsafeCSS(spacing["3"])};
199
- animation: spin 1s linear infinite;
237
+ animation: spin 900ms linear infinite;
200
238
  }
201
239
 
202
240
  @keyframes spin {
203
- from { transform: rotate(0deg); }
204
- to { transform: rotate(360deg); }
241
+ from {
242
+ transform: rotate(0deg);
243
+ }
244
+ to {
245
+ transform: rotate(360deg);
246
+ }
205
247
  }
206
248
 
207
249
  .button.success {
208
- background: ${bdTheme('success')};
209
- color: ${bdTheme('successForeground')};
250
+ background: var(--sio-green);
251
+ color: #ffffff;
210
252
  pointer-events: none;
211
253
  }
212
254
 
213
255
  .button.error {
214
- background: ${bdTheme('destructive')};
215
- color: ${bdTheme('destructiveForeground')};
256
+ background: var(--sio-red);
257
+ color: #ffffff;
216
258
  pointer-events: none;
217
259
  }
218
260
 
219
261
  /* Disabled state */
220
262
  .button.disabled {
221
- opacity: 0.5;
263
+ opacity: 0.45;
222
264
  cursor: not-allowed;
223
265
  transform: none !important;
224
266
  }
225
267
 
226
- /* Focus state */
227
- .button:focus-visible {
228
- outline: 2px solid ${bdTheme('hsl(0 0% 15% / 0.2)', 'hsl(0 0% 95% / 0.2)')};
229
- outline-offset: 2px;
230
- }
231
-
232
268
  /* Icon sizing within buttons */
233
269
  .button sio-icon {
234
- width: 16px;
235
- height: 16px;
236
270
  flex-shrink: 0;
237
271
  }
238
-
239
- .button.size-sm sio-icon {
240
- width: 14px;
241
- height: 14px;
242
- }
243
-
244
- .button.size-lg sio-icon {
245
- width: 18px;
246
- height: 18px;
247
- }
248
272
  `,
249
273
  ];
250
274
 
251
275
  public render(): TemplateResult {
252
276
  const buttonClasses = [
253
277
  'button',
254
- this.type === 'primary' ? 'primary' : this.type,
278
+ this.type,
255
279
  `size-${this.size}`,
256
280
  this.disabled ? 'disabled' : '',
257
281
  this.status,
258
- ].filter(Boolean).join(' ');
282
+ ]
283
+ .filter(Boolean)
284
+ .join(' ');
285
+
286
+ const statusIconSize = this.size === 'sm' ? 13 : this.size === 'lg' ? 17 : 15;
259
287
 
260
288
  return html`
261
- <button
262
- class="${buttonClasses}"
263
- ?disabled=${this.disabled}
264
- @click=${this.handleClick}
265
- >
266
- ${this.status === 'pending' ? html`
267
- <sio-icon class="spinner" icon="loader" size="16"></sio-icon>
268
- ` : ''}
269
- ${this.status === 'success' ? html`
270
- <sio-icon icon="check" size="16"></sio-icon>
271
- ` : ''}
272
- ${this.status === 'error' ? html`
273
- <sio-icon icon="x" size="16"></sio-icon>
274
- ` : ''}
289
+ <button class="${buttonClasses}" ?disabled=${this.disabled} @click=${this.handleClick}>
290
+ ${this.status === 'pending'
291
+ ? html`<sio-icon class="spinner" icon="loader" size=${statusIconSize}></sio-icon>`
292
+ : ''}
293
+ ${this.status === 'success'
294
+ ? html`<sio-icon icon="check" size=${statusIconSize}></sio-icon>`
295
+ : ''}
296
+ ${this.status === 'error'
297
+ ? html`<sio-icon icon="x" size=${statusIconSize}></sio-icon>`
298
+ : ''}
275
299
  <slot>${this.text}</slot>
276
300
  </button>
277
301
  `;
@@ -283,9 +307,7 @@ export class SioButton extends DeesElement {
283
307
  event.stopPropagation();
284
308
  return;
285
309
  }
286
-
287
-
288
- // Let the native click bubble normally
289
- // Don't dispatch a custom event to avoid double-triggering
310
+ // Let the native click bubble normally — no synthetic event, so
311
+ // consumers never see a double trigger.
290
312
  }
291
- }
313
+ }
@@ -0,0 +1,243 @@
1
+ import {
2
+ DeesElement,
3
+ html,
4
+ property,
5
+ customElement,
6
+ css,
7
+ type TemplateResult,
8
+ } from '@design.estate/dees-element';
9
+ import { sioElementStyles } from './tokens.js';
10
+ import './sio-icon.js';
11
+
12
+ declare global {
13
+ interface HTMLElementTagNameMap {
14
+ 'sio-channel-rail': SioChannelRail;
15
+ }
16
+ }
17
+
18
+ export interface ISioChannelRailItem {
19
+ id: string;
20
+ label: string;
21
+ color: string;
22
+ icon: string;
23
+ unread?: number;
24
+ }
25
+
26
+ export interface ISioChannelSelectDetail {
27
+ id: string;
28
+ channel: ISioChannelRailItem;
29
+ }
30
+
31
+ /**
32
+ * The v7 channel set — All / Mail / Chats / Calls / Letters
33
+ * (prototype: SIO_CHANNELS in SocialIOData.jsx).
34
+ */
35
+ const defaultChannels: ISioChannelRailItem[] = [
36
+ { id: 'all', label: 'All', color: 'var(--sio-blue)', icon: 'grid-2x2' },
37
+ { id: 'mail', label: 'Mail', color: 'var(--sio-ch-mail)', icon: 'mail' },
38
+ { id: 'chats', label: 'Chats', color: 'var(--sio-ch-chats)', icon: 'message-circle' },
39
+ { id: 'calls', label: 'Calls', color: 'var(--sio-ch-calls)', icon: 'phone' },
40
+ { id: 'letters', label: 'Letters', color: 'var(--sio-ch-letters)', icon: 'file-text' },
41
+ ];
42
+
43
+ /**
44
+ * sio-channel-rail — the 64px channel column at the very left of the
45
+ * desktop window (v7 prototype: SIOChannelRail in
46
+ * SocialIODesktopChannels.jsx).
47
+ *
48
+ * Each item stacks an 18px icon over a 10px label; the active item gets a
49
+ * rounded-10 chip and its channel color, and every item can carry an unread
50
+ * badge (channel-colored pill at top -5 / right -9 with a 2px ring cut out
51
+ * of the rail). Selecting a channel dispatches `channel-select` with
52
+ * `{ id, channel }` (bubbles, composed).
53
+ */
54
+ @customElement('sio-channel-rail')
55
+ export class SioChannelRail extends DeesElement {
56
+ public static demo = () => html`
57
+ <div style="display: flex; gap: 24px; font-family: var(--sio-font-sf);">
58
+ <!-- busy morning: unreads on every channel -->
59
+ <div
60
+ style="display: flex; height: 340px; border-radius: 12px; overflow: hidden; border: var(--sio-hairline) solid var(--sio-separator);"
61
+ >
62
+ <sio-channel-rail
63
+ active="mail"
64
+ .channels=${[
65
+ { id: 'all', label: 'All', color: 'var(--sio-blue)', icon: 'grid-2x2', unread: 3 },
66
+ { id: 'mail', label: 'Mail', color: 'var(--sio-ch-mail)', icon: 'mail', unread: 4 },
67
+ { id: 'chats', label: 'Chats', color: 'var(--sio-ch-chats)', icon: 'message-circle', unread: 7 },
68
+ { id: 'calls', label: 'Calls', color: 'var(--sio-ch-calls)', icon: 'phone', unread: 1 },
69
+ { id: 'letters', label: 'Letters', color: 'var(--sio-ch-letters)', icon: 'file-text', unread: 2 },
70
+ ]}
71
+ ></sio-channel-rail>
72
+ <div style="width: 210px; background: var(--sio-surface); padding: 14px 16px;">
73
+ <div style="font-size: 13px; font-weight: 600; color: var(--sio-label);">Inbox</div>
74
+ <div style="margin-top: 2px; font-size: 11px; color: var(--sio-label-3);">
75
+ 4 unread · Feed 12 · Paper 4
76
+ </div>
77
+ </div>
78
+ </div>
79
+
80
+ <!-- Letters active, all clear -->
81
+ <div
82
+ style="display: flex; height: 340px; border-radius: 12px; overflow: hidden; border: var(--sio-hairline) solid var(--sio-separator);"
83
+ >
84
+ <sio-channel-rail active="letters"></sio-channel-rail>
85
+ <div style="width: 210px; background: var(--sio-surface); padding: 14px 16px;">
86
+ <div style="font-size: 13px; font-weight: 600; color: var(--sio-label);">Letters</div>
87
+ <div style="margin-top: 2px; font-size: 11px; color: var(--sio-label-3);">
88
+ Scan station online · 1 queued
89
+ </div>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ `;
94
+
95
+ @property({ type: Array })
96
+ public accessor channels: ISioChannelRailItem[] = defaultChannels;
97
+
98
+ @property({ type: String })
99
+ public accessor active = 'mail';
100
+
101
+ public static styles = [
102
+ ...sioElementStyles,
103
+ css`
104
+ :host {
105
+ display: block;
106
+ height: 100%;
107
+ }
108
+
109
+ /* Rail chrome: the systemGray fill composited over the canvas —
110
+ #E8E8EC light / #1C1C1F dark, matching the design's #EDEDEE/#17171A
111
+ from tokens alone. */
112
+ .rail {
113
+ width: 64px;
114
+ height: 100%;
115
+ box-sizing: border-box;
116
+ display: flex;
117
+ flex-direction: column;
118
+ align-items: stretch;
119
+ gap: 2px;
120
+ padding: 10px 8px;
121
+ background-color: var(--sio-bg);
122
+ background-image: linear-gradient(var(--sio-surface-2), var(--sio-surface-2));
123
+ border-right: var(--sio-hairline) solid var(--sio-separator);
124
+ }
125
+
126
+ .item {
127
+ appearance: none;
128
+ border: none;
129
+ margin: 0;
130
+ position: relative;
131
+ padding: 8px 0;
132
+ border-radius: var(--sio-radius);
133
+ background: transparent;
134
+ display: flex;
135
+ flex-direction: column;
136
+ align-items: center;
137
+ gap: 3px;
138
+ font-family: var(--sio-font-sf);
139
+ cursor: pointer;
140
+ -webkit-tap-highlight-color: transparent;
141
+ transition: background 120ms ease, color 120ms ease;
142
+ }
143
+
144
+ .item.active {
145
+ background: color-mix(in oklab, var(--sio-label) 5%, transparent);
146
+ }
147
+
148
+ .item:focus-visible {
149
+ outline: 2px solid color-mix(in oklab, var(--sio-tint) 40%, transparent);
150
+ outline-offset: -2px;
151
+ }
152
+
153
+ .glyph {
154
+ position: relative;
155
+ display: flex;
156
+ align-items: center;
157
+ justify-content: center;
158
+ color: var(--sio-label-3);
159
+ }
160
+
161
+ .item.active .glyph {
162
+ color: var(--_channel-color, var(--sio-tint));
163
+ }
164
+
165
+ .badge {
166
+ position: absolute;
167
+ top: -5px;
168
+ right: -9px;
169
+ min-width: 15px;
170
+ height: 15px;
171
+ padding: 0 4px;
172
+ box-sizing: border-box;
173
+ border-radius: 8px;
174
+ background: var(--_channel-color, var(--sio-tint));
175
+ /* fixed foreground on the theme-stable channel palette */
176
+ color: #ffffff;
177
+ font-size: 10px;
178
+ font-weight: 700;
179
+ font-variant-numeric: tabular-nums;
180
+ display: flex;
181
+ align-items: center;
182
+ justify-content: center;
183
+ line-height: 1;
184
+ /* 2px ring in the rail's own chrome tone */
185
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--sio-label) 8%, var(--sio-bg));
186
+ }
187
+
188
+ .label {
189
+ font-size: 10px;
190
+ font-weight: 500;
191
+ letter-spacing: 0.02px;
192
+ line-height: 1.25;
193
+ color: var(--sio-label-2);
194
+ white-space: nowrap;
195
+ }
196
+
197
+ .item.active .label {
198
+ font-weight: 600;
199
+ color: var(--sio-label);
200
+ }
201
+ `,
202
+ ];
203
+
204
+ public render(): TemplateResult {
205
+ return html`
206
+ <nav class="rail" part="rail">
207
+ ${this.channels.map(
208
+ (channel) => html`
209
+ <button
210
+ type="button"
211
+ class="item ${channel.id === this.active ? 'active' : ''}"
212
+ style="--_channel-color: ${channel.color}"
213
+ aria-current=${channel.id === this.active ? 'page' : 'false'}
214
+ @click=${() => this.handleSelect(channel)}
215
+ >
216
+ <span class="glyph">
217
+ <sio-icon icon=${channel.icon} size="18" .strokeWidth=${1.7}></sio-icon>
218
+ ${channel.unread
219
+ ? html`<span class="badge">${channel.unread}</span>`
220
+ : html``}
221
+ </span>
222
+ <span class="label">${channel.label}</span>
223
+ </button>
224
+ `,
225
+ )}
226
+ </nav>
227
+ `;
228
+ }
229
+
230
+ private handleSelect(channel: ISioChannelRailItem): void {
231
+ if (channel.id === this.active) {
232
+ return;
233
+ }
234
+ this.active = channel.id;
235
+ this.dispatchEvent(
236
+ new CustomEvent<ISioChannelSelectDetail>('channel-select', {
237
+ detail: { id: channel.id, channel },
238
+ bubbles: true,
239
+ composed: true,
240
+ }),
241
+ );
242
+ }
243
+ }