@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
@@ -1,16 +1,39 @@
1
- // Core components
1
+ // design tokens — custom props, glass recipes, SF type ramp
2
+ export * from './tokens.js';
3
+
4
+ // foundation
2
5
  export * from './sio-icon.js';
3
6
  export * from './sio-button.js';
7
+ export * from './sio-kbd.js';
8
+ export * from './sio-chip.js';
9
+ export * from './sio-avatar.js';
10
+ export * from './sio-unread-badge.js';
11
+
12
+ // chrome & navigation
13
+ export * from './sio-glass-toolbar.js';
14
+ export * from './sio-segmented.js';
15
+ export * from './sio-tab-bar.js';
16
+ export * from './sio-channel-rail.js';
4
17
  export * from './sio-dropdown-menu.js';
5
18
 
6
- // Conversation components
19
+ // lists & feed
20
+ export * from './sio-list-row.js';
21
+ export * from './sio-inset-list.js';
22
+ export * from './sio-bucket-header.js';
23
+
24
+ // content & input
25
+ export * from './sio-ai-card.js';
26
+ export * from './sio-composer.js';
27
+ export * from './sio-cmdk.js';
28
+ export * from './sio-waveform.js';
29
+ export * from './sio-step-timeline.js';
30
+ export * from './sio-paper-thumb.js';
31
+
32
+ // embeddable chat-widget suite
33
+ export * from './sio-fab.js';
34
+ export * from './sio-combox.js';
7
35
  export * from './sio-conversation-selector.js';
8
36
  export * from './sio-conversation-view.js';
9
37
  export * from './sio-message-input.js';
10
- export * from './sio-combox.js';
11
-
12
- // Other components
13
- export * from './sio-fab.js';
14
- export * from './sio-recorder.js';
15
38
  export * from './sio-image-lightbox.js';
16
39
  export * from './sio-pdf-viewer.js';
@@ -0,0 +1,175 @@
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
+ /**
13
+ * Accent source for the card. Empty string = system tint (default);
14
+ * a channel name colors the card with the channel palette instead
15
+ * (bg 8% / border 28% per the unified detail design).
16
+ */
17
+ export type TSioAiCardChannel = '' | 'mail' | 'chats' | 'calls' | 'letters';
18
+
19
+ declare global {
20
+ interface HTMLElementTagNameMap {
21
+ 'sio-ai-card': SioAiCard;
22
+ }
23
+ }
24
+
25
+ /**
26
+ * sio-ai-card — the shared AI surface of social.io v7.
27
+ *
28
+ * Tint-soft (or channel-color 8%) background, 0.5px hairline border at
29
+ * color-mix 25–28%, radius 12. Header row: sparkle icon + uppercase
30
+ * 11/700 tinted eyebrow. Slotted children render as bullet lines,
31
+ * each prefixed with a tinted dot (disable via `plain` for the
32
+ * single-paragraph summary style).
33
+ *
34
+ * Design sources: SocialIODesktop.jsx (thread AI summary),
35
+ * SocialIOUnified.jsx (channel-colored summary),
36
+ * SocialIOChannels.jsx (letters "translated" summary).
37
+ */
38
+ @customElement('sio-ai-card')
39
+ export class SioAiCard extends DeesElement {
40
+ public static demo = () => html`
41
+ <div
42
+ style="display: grid; gap: 14px; max-width: 640px; padding: 24px; background: var(--sio-bg); font-family: var(--sio-font-sf);"
43
+ >
44
+ <sio-ai-card label="Summary · 4 messages">
45
+ <div>Lena rewrote the hero and the "three lanes" section based on Tuesday's feedback.</div>
46
+ <div>Two open questions: (1) keep the Hey-style screener mention, (2) tighten the closing CTA.</div>
47
+ <div>She's asking for a 15-min sync before Tuesday's ship.</div>
48
+ </sio-ai-card>
49
+
50
+ <sio-ai-card label="Summary" translated-to="English" plain>
51
+ <div>
52
+ 2024 tax assessment — <b>refund of €1,284.60</b> due to you. Will be paid to your
53
+ registered bank account within 4 weeks. Objection period: 1 month.
54
+ </div>
55
+ </sio-ai-card>
56
+
57
+ <sio-ai-card label="AI summary" channel="letters" plain>
58
+ <div>Scanned at Berlin-Mitte hub. Objection deadline in 28 days.</div>
59
+ </sio-ai-card>
60
+
61
+ <sio-ai-card label="AI summary" channel="chats" plain>
62
+ <div>Lena: "bumping this — should we hold the banner for Thursday?"</div>
63
+ </sio-ai-card>
64
+ </div>
65
+ `;
66
+
67
+ /** Eyebrow text, rendered uppercase — e.g. "Summary · 4 messages". */
68
+ @property({ type: String })
69
+ public accessor label = 'Summary';
70
+
71
+ /** Channel accent; empty = system tint. */
72
+ @property({ type: String, reflect: true })
73
+ public accessor channel: TSioAiCardChannel = '';
74
+
75
+ /** Translated variant — appends "· translated to <language>" to the eyebrow. */
76
+ @property({ type: String, attribute: 'translated-to' })
77
+ public accessor translatedTo = '';
78
+
79
+ /** Single-paragraph summary style: no tinted dot in front of slotted lines. */
80
+ @property({ type: Boolean, reflect: true })
81
+ public accessor plain = false;
82
+
83
+ public static styles = [
84
+ ...sioElementStyles,
85
+ css`
86
+ :host {
87
+ display: block;
88
+ --sio-ai-accent: var(--sio-tint);
89
+ --sio-ai-bg: var(--sio-tint-soft);
90
+ --sio-ai-border: color-mix(in oklab, var(--sio-tint) 25%, transparent);
91
+ }
92
+ :host([channel='mail']) {
93
+ --sio-ai-accent: var(--sio-ch-mail);
94
+ --sio-ai-bg: color-mix(in oklab, var(--sio-ch-mail) 8%, transparent);
95
+ --sio-ai-border: color-mix(in oklab, var(--sio-ch-mail) 28%, transparent);
96
+ }
97
+ :host([channel='chats']) {
98
+ --sio-ai-accent: var(--sio-ch-chats);
99
+ --sio-ai-bg: color-mix(in oklab, var(--sio-ch-chats) 8%, transparent);
100
+ --sio-ai-border: color-mix(in oklab, var(--sio-ch-chats) 28%, transparent);
101
+ }
102
+ :host([channel='calls']) {
103
+ --sio-ai-accent: var(--sio-ch-calls);
104
+ --sio-ai-bg: color-mix(in oklab, var(--sio-ch-calls) 8%, transparent);
105
+ --sio-ai-border: color-mix(in oklab, var(--sio-ch-calls) 28%, transparent);
106
+ }
107
+ :host([channel='letters']) {
108
+ --sio-ai-accent: var(--sio-ch-letters);
109
+ --sio-ai-bg: color-mix(in oklab, var(--sio-ch-letters) 8%, transparent);
110
+ --sio-ai-border: color-mix(in oklab, var(--sio-ch-letters) 28%, transparent);
111
+ }
112
+
113
+ .card {
114
+ padding: 14px 16px;
115
+ border-radius: var(--sio-radius-md);
116
+ background: var(--sio-ai-bg);
117
+ border: var(--sio-hairline) solid var(--sio-ai-border);
118
+ }
119
+
120
+ .eyebrow {
121
+ display: flex;
122
+ align-items: center;
123
+ gap: 8px;
124
+ margin-bottom: 8px;
125
+ font-size: 11px;
126
+ line-height: 13px;
127
+ font-weight: 700;
128
+ letter-spacing: 0.06px;
129
+ text-transform: uppercase;
130
+ color: var(--sio-ai-accent);
131
+ font-variant-numeric: tabular-nums;
132
+ }
133
+
134
+ .eyebrow sio-icon {
135
+ flex: 0 0 auto;
136
+ }
137
+
138
+ ::slotted(*) {
139
+ display: flex;
140
+ gap: 8px;
141
+ margin: 0 0 3px;
142
+ font-size: 13.5px;
143
+ line-height: 1.5;
144
+ color: var(--sio-label);
145
+ }
146
+
147
+ ::slotted(*:last-child) {
148
+ margin-bottom: 0;
149
+ }
150
+
151
+ ::slotted(*)::before {
152
+ content: '·';
153
+ color: var(--sio-ai-accent);
154
+ flex: 0 0 auto;
155
+ }
156
+
157
+ :host([plain]) ::slotted(*)::before {
158
+ content: none;
159
+ }
160
+ `,
161
+ ];
162
+
163
+ public render(): TemplateResult {
164
+ const suffix = this.translatedTo ? ` · translated to ${this.translatedTo}` : '';
165
+ return html`
166
+ <div class="card" part="card">
167
+ <div class="eyebrow" part="eyebrow">
168
+ <sio-icon icon="sparkles" size="11" .strokeWidth=${2}></sio-icon>
169
+ <span>${this.label}${suffix}</span>
170
+ </div>
171
+ <slot></slot>
172
+ </div>
173
+ `;
174
+ }
175
+ }
@@ -0,0 +1,295 @@
1
+ import {
2
+ DeesElement,
3
+ html,
4
+ property,
5
+ state,
6
+ customElement,
7
+ css,
8
+ type TemplateResult,
9
+ } from '@design.estate/dees-element';
10
+
11
+ import { sioElementStyles } from './tokens.js';
12
+ import './sio-icon.js';
13
+
14
+ export type TSioAvatarPresence = 'none' | 'online';
15
+ export type TSioAvatarChannelBadge = 'none' | 'mail' | 'chats' | 'calls' | 'letters';
16
+
17
+ declare global {
18
+ interface HTMLElementTagNameMap {
19
+ 'sio-avatar': SioAvatar;
20
+ 'sio-avatar-stack': SioAvatarStack;
21
+ }
22
+ }
23
+
24
+ /**
25
+ * channel badge recipes — 18px glyph circle bottom-right of the avatar,
26
+ * solid channel color with a 2px surface ring (v7 SIOUniRow).
27
+ */
28
+ const channelBadgeRecipes: Record<
29
+ Exclude<TSioAvatarChannelBadge, 'none'>,
30
+ { icon: string; color: string }
31
+ > = {
32
+ mail: { icon: 'envelope', color: 'var(--sio-ch-mail)' },
33
+ chats: { icon: 'message-circle', color: 'var(--sio-ch-chats)' },
34
+ calls: { icon: 'phone', color: 'var(--sio-ch-calls)' },
35
+ letters: { icon: 'file-text', color: 'var(--sio-ch-letters)' },
36
+ };
37
+
38
+ /**
39
+ * sio-avatar — initials on a tinted circle (v7 `.sio-avatar`).
40
+ *
41
+ * The tint is data-driven (each person carries their own accent color),
42
+ * so it is passed in as a prop; it defaults to the system tint token.
43
+ * Optional presence dot (online) and channel badge, both ringed with
44
+ * 2px of surface color so they read on top of list rows.
45
+ */
46
+ @customElement('sio-avatar')
47
+ export class SioAvatar extends DeesElement {
48
+ public static demo = () => html`
49
+ <div style="display: flex; flex-direction: column; gap: 20px;">
50
+ <div style="display: flex; gap: 14px; align-items: center;">
51
+ <sio-avatar initials="LP" tint="#FF6B8A" size="56"></sio-avatar>
52
+ <sio-avatar initials="MV" tint="#0A84FF" size="44"></sio-avatar>
53
+ <sio-avatar initials="PS" tint="#30D158"></sio-avatar>
54
+ <sio-avatar initials="L" tint="#5E6AD2" size="34"></sio-avatar>
55
+ <sio-avatar initials="FA" tint="#A2845E" size="28"></sio-avatar>
56
+ <sio-avatar initials="JW" tint="#FF9F0A" size="20"></sio-avatar>
57
+ </div>
58
+ <div style="display: flex; gap: 14px; align-items: center;">
59
+ <sio-avatar initials="LP" tint="#FF6B8A" size="44" presence="online"></sio-avatar>
60
+ <sio-avatar initials="MV" tint="#0A84FF" size="40" channelBadge="mail"></sio-avatar>
61
+ <sio-avatar initials="LP" tint="#FF6B8A" size="40" channelBadge="chats"></sio-avatar>
62
+ <sio-avatar initials="PS" tint="#30D158" size="40" channelBadge="calls"></sio-avatar>
63
+ <sio-avatar initials="FA" tint="#A2845E" size="40" channelBadge="letters"></sio-avatar>
64
+ </div>
65
+ <div style="display: flex; gap: 24px; align-items: center;">
66
+ <sio-avatar-stack size="32">
67
+ <sio-avatar initials="LP" tint="#FF6B8A" size="32"></sio-avatar>
68
+ <sio-avatar initials="MV" tint="#0A84FF" size="32"></sio-avatar>
69
+ <sio-avatar initials="PS" tint="#30D158" size="32"></sio-avatar>
70
+ </sio-avatar-stack>
71
+ <sio-avatar-stack max="3" size="24">
72
+ <sio-avatar initials="LP" tint="#FF6B8A" size="24"></sio-avatar>
73
+ <sio-avatar initials="MV" tint="#0A84FF" size="24"></sio-avatar>
74
+ <sio-avatar initials="PS" tint="#30D158" size="24"></sio-avatar>
75
+ <sio-avatar initials="JW" tint="#FF9F0A" size="24"></sio-avatar>
76
+ <sio-avatar initials="AK" tint="#5E6AD2" size="24"></sio-avatar>
77
+ </sio-avatar-stack>
78
+ </div>
79
+ </div>
80
+ `;
81
+
82
+ @property({ type: String })
83
+ public accessor initials = '';
84
+
85
+ /** person accent color (data-driven); any CSS color, tokens welcome */
86
+ @property({ type: String })
87
+ public accessor tint = 'var(--sio-tint)';
88
+
89
+ @property({ type: Number })
90
+ public accessor size = 40;
91
+
92
+ @property({ type: String })
93
+ public accessor presence: TSioAvatarPresence = 'none';
94
+
95
+ @property({ type: String })
96
+ public accessor channelBadge: TSioAvatarChannelBadge = 'none';
97
+
98
+ public static styles = [
99
+ ...sioElementStyles,
100
+ css`
101
+ :host {
102
+ display: inline-flex;
103
+ flex-shrink: 0;
104
+ }
105
+
106
+ .wrapper {
107
+ position: relative;
108
+ flex-shrink: 0;
109
+ display: inline-flex;
110
+ }
111
+
112
+ /* v7 .sio-avatar — initials on tinted circle, white per design */
113
+ .avatar {
114
+ border-radius: 50%;
115
+ display: inline-flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ flex-shrink: 0;
119
+ font-family: var(--sio-font-sf);
120
+ font-weight: 600;
121
+ letter-spacing: -0.2px;
122
+ color: #fff; /* on-tint constant across themes (v7 .sio-avatar) */
123
+ user-select: none;
124
+ }
125
+
126
+ .presence {
127
+ position: absolute;
128
+ right: -1px;
129
+ bottom: -1px;
130
+ border-radius: 50%;
131
+ background: var(--sio-green);
132
+ border: 2px solid var(--sio-surface);
133
+ }
134
+
135
+ .channelBadge {
136
+ position: absolute;
137
+ right: -3px;
138
+ bottom: -3px;
139
+ width: 18px;
140
+ height: 18px;
141
+ border-radius: 9px;
142
+ color: #fff; /* glyph on solid channel color */
143
+ display: flex;
144
+ align-items: center;
145
+ justify-content: center;
146
+ border: 2px solid var(--sio-surface);
147
+ }
148
+ `,
149
+ ];
150
+
151
+ public render(): TemplateResult {
152
+ const fontSize = Math.round(this.size * 0.4);
153
+ const presenceSize = Math.max(8, Math.round(this.size * 0.27));
154
+ const badge = this.channelBadge !== 'none' ? channelBadgeRecipes[this.channelBadge] : null;
155
+ return html`
156
+ <div class="wrapper">
157
+ <div
158
+ class="avatar"
159
+ style="width: ${this.size}px; height: ${this.size}px; font-size: ${fontSize}px; background: ${this.tint};"
160
+ >
161
+ ${this.initials}
162
+ </div>
163
+ ${this.presence === 'online'
164
+ ? html`<div class="presence" style="width: ${presenceSize}px; height: ${presenceSize}px;"></div>`
165
+ : ''}
166
+ ${badge
167
+ ? html`
168
+ <div class="channelBadge" style="background: ${badge.color};">
169
+ <sio-icon icon=${badge.icon} size="10"></sio-icon>
170
+ </div>
171
+ `
172
+ : ''}
173
+ </div>
174
+ `;
175
+ }
176
+ }
177
+
178
+ /**
179
+ * sio-avatar-stack — overlapping avatar row (thread participants).
180
+ * Slotted sio-avatar elements overlap by -8px, each ringed with 2px of
181
+ * surface color; avatars beyond `max` collapse into a "+N" bubble.
182
+ */
183
+ @customElement('sio-avatar-stack')
184
+ export class SioAvatarStack extends DeesElement {
185
+ public static demo = () => html`
186
+ <div style="display: flex; gap: 24px; align-items: center;">
187
+ <sio-avatar-stack size="32">
188
+ <sio-avatar initials="LP" tint="#FF6B8A" size="32"></sio-avatar>
189
+ <sio-avatar initials="MV" tint="#0A84FF" size="32"></sio-avatar>
190
+ <sio-avatar initials="PS" tint="#30D158" size="32"></sio-avatar>
191
+ </sio-avatar-stack>
192
+ <sio-avatar-stack max="3" size="28">
193
+ <sio-avatar initials="LP" tint="#FF6B8A" size="28"></sio-avatar>
194
+ <sio-avatar initials="MV" tint="#0A84FF" size="28"></sio-avatar>
195
+ <sio-avatar initials="PS" tint="#30D158" size="28"></sio-avatar>
196
+ <sio-avatar initials="JW" tint="#FF9F0A" size="28"></sio-avatar>
197
+ <sio-avatar initials="AK" tint="#5E6AD2" size="28"></sio-avatar>
198
+ <sio-avatar initials="RB" tint="#A2845E" size="28"></sio-avatar>
199
+ </sio-avatar-stack>
200
+ <sio-avatar-stack max="2" size="24">
201
+ <sio-avatar initials="LP" tint="#FF6B8A" size="24"></sio-avatar>
202
+ <sio-avatar initials="MV" tint="#0A84FF" size="24"></sio-avatar>
203
+ <sio-avatar initials="PS" tint="#30D158" size="24"></sio-avatar>
204
+ <sio-avatar initials="JW" tint="#FF9F0A" size="24"></sio-avatar>
205
+ </sio-avatar-stack>
206
+ </div>
207
+ `;
208
+
209
+ /** visible avatars before collapsing into "+N" */
210
+ @property({ type: Number })
211
+ public accessor max = 4;
212
+
213
+ /** diameter of the "+N" overflow bubble — match the slotted avatar size */
214
+ @property({ type: Number })
215
+ public accessor size = 28;
216
+
217
+ @state()
218
+ private accessor overflowCount = 0;
219
+
220
+ public static styles = [
221
+ ...sioElementStyles,
222
+ css`
223
+ :host {
224
+ display: inline-flex;
225
+ align-items: center;
226
+ }
227
+
228
+ ::slotted(sio-avatar) {
229
+ border-radius: 50%;
230
+ box-shadow: 0 0 0 2px var(--sio-surface);
231
+ }
232
+
233
+ ::slotted(sio-avatar:not(:first-child)) {
234
+ margin-left: -8px;
235
+ }
236
+
237
+ ::slotted(sio-avatar[data-sio-stack-overflow]) {
238
+ display: none;
239
+ }
240
+
241
+ .overflow {
242
+ display: inline-flex;
243
+ align-items: center;
244
+ justify-content: center;
245
+ flex-shrink: 0;
246
+ margin-left: -8px;
247
+ border-radius: 50%;
248
+ background: var(--sio-surface-2);
249
+ color: var(--sio-label-2);
250
+ font-family: var(--sio-font-sf);
251
+ font-weight: 600;
252
+ font-variant-numeric: tabular-nums;
253
+ box-shadow: 0 0 0 2px var(--sio-surface);
254
+ user-select: none;
255
+ }
256
+ `,
257
+ ];
258
+
259
+ public render(): TemplateResult {
260
+ const overflowFontSize = Math.max(9, Math.round(this.size * 0.36));
261
+ return html`
262
+ <slot @slotchange=${this.recomputeStack}></slot>
263
+ ${this.overflowCount > 0
264
+ ? html`
265
+ <span
266
+ class="overflow"
267
+ style="width: ${this.size}px; height: ${this.size}px; font-size: ${overflowFontSize}px;"
268
+ >
269
+ +${this.overflowCount}
270
+ </span>
271
+ `
272
+ : ''}
273
+ `;
274
+ }
275
+
276
+ public updated(changedProperties: Map<PropertyKey, unknown>) {
277
+ if (changedProperties.has('max')) {
278
+ this.recomputeStack();
279
+ }
280
+ }
281
+
282
+ private recomputeStack = () => {
283
+ const slot = this.shadowRoot?.querySelector('slot');
284
+ if (!slot) {
285
+ return;
286
+ }
287
+ const avatars = slot
288
+ .assignedElements()
289
+ .filter((element) => element.tagName.toLowerCase() === 'sio-avatar');
290
+ avatars.forEach((element, index) => {
291
+ element.toggleAttribute('data-sio-stack-overflow', index >= this.max);
292
+ });
293
+ this.overflowCount = Math.max(0, avatars.length - this.max);
294
+ };
295
+ }
@@ -0,0 +1,157 @@
1
+ import {
2
+ DeesElement,
3
+ html,
4
+ property,
5
+ customElement,
6
+ css,
7
+ type TemplateResult,
8
+ } from '@design.estate/dees-element';
9
+ import { sioElementStyles, sioTabularNums } from './tokens.js';
10
+
11
+ /**
12
+ * Known tint keys resolve to v7 palette tokens; anything else is passed
13
+ * through verbatim so callers can hand in `var(--sio-…)` references.
14
+ */
15
+ export type TSioBucketTint =
16
+ | 'now'
17
+ | 'today'
18
+ | 'later'
19
+ | 'done'
20
+ | 'feed'
21
+ | 'paper'
22
+ | 'people'
23
+ | 'mail'
24
+ | 'chats'
25
+ | 'calls'
26
+ | 'letters'
27
+ | 'tint'
28
+ | string;
29
+
30
+ const bucketTintVars: Record<string, string> = {
31
+ now: 'var(--sio-bucket-now)',
32
+ today: 'var(--sio-bucket-today)',
33
+ later: 'var(--sio-bucket-later)',
34
+ done: 'var(--sio-bucket-done)',
35
+ feed: 'var(--sio-lane-feed)',
36
+ paper: 'var(--sio-lane-paper)',
37
+ people: 'var(--sio-lane-people)',
38
+ mail: 'var(--sio-ch-mail)',
39
+ chats: 'var(--sio-ch-chats)',
40
+ calls: 'var(--sio-ch-calls)',
41
+ letters: 'var(--sio-ch-letters)',
42
+ tint: 'var(--sio-tint)',
43
+ };
44
+
45
+ declare global {
46
+ interface HTMLElementTagNameMap {
47
+ 'sio-bucket-header': SioBucketHeader;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * sio-bucket-header — triage bucket band of the unified feed
53
+ * (design source: SIOBucketHeader in SocialIOUnified.jsx).
54
+ *
55
+ * 6px color dot + uppercase 13/700 tinted label + tabular count,
56
+ * optional right-aligned subtitle, on a faint 2% label-tint band.
57
+ * A default slot after the subtitle takes custom trailing content.
58
+ */
59
+ @customElement('sio-bucket-header')
60
+ export class SioBucketHeader extends DeesElement {
61
+ public static demo = () => html`
62
+ <div style="max-width: 420px; font-family: var(--sio-font-sf); background: var(--sio-bg); border-radius: 12px; overflow: hidden;">
63
+ <sio-bucket-header label="Now" count="2" tint="now" subtitle="needs reply today"></sio-bucket-header>
64
+ <div style="background: var(--sio-surface); height: 44px;"></div>
65
+ <sio-bucket-header label="Today" count="4" tint="today"></sio-bucket-header>
66
+ <div style="background: var(--sio-surface); height: 44px;"></div>
67
+ <sio-bucket-header label="Later" count="7" tint="later"></sio-bucket-header>
68
+ <div style="background: var(--sio-surface); height: 44px;"></div>
69
+ <sio-bucket-header label="Done" count="12" tint="done" subtitle="cleared this week"></sio-bucket-header>
70
+ <div style="background: var(--sio-surface); height: 44px;"></div>
71
+ <sio-bucket-header label="Yesterday" count="3" tint="tint"></sio-bucket-header>
72
+ <div style="background: var(--sio-surface); height: 44px;"></div>
73
+ </div>
74
+ `;
75
+
76
+ /** Bucket label — rendered uppercase, 13px / 700, tinted. */
77
+ @property({ type: String })
78
+ public accessor label = '';
79
+
80
+ /** Item count — rendered as "· n" with tabular-nums. Omit to hide. */
81
+ @property({ type: Number })
82
+ public accessor count: number | undefined = undefined;
83
+
84
+ /** Optional right-aligned subtitle on the band (e.g. "needs reply today"). */
85
+ @property({ type: String })
86
+ public accessor subtitle = '';
87
+
88
+ /**
89
+ * Bucket tint: now | today | later | done, lane keys (feed/paper/people),
90
+ * channel keys (mail/chats/calls/letters), 'tint', or any CSS color value.
91
+ */
92
+ @property({ type: String })
93
+ public accessor tint: TSioBucketTint = 'later';
94
+
95
+ public static styles = [
96
+ ...sioElementStyles,
97
+ css`
98
+ :host {
99
+ display: block;
100
+ font-family: var(--sio-font-sf);
101
+ }
102
+ .header {
103
+ padding: 16px 16px 8px;
104
+ display: flex;
105
+ align-items: baseline;
106
+ gap: 8px;
107
+ /* faint band — ~2% label ink over the grouped background */
108
+ background: color-mix(in oklab, var(--sio-label) 2%, transparent);
109
+ }
110
+ .dot {
111
+ width: 6px;
112
+ height: 6px;
113
+ border-radius: 3px;
114
+ background: var(--_bucket-tint);
115
+ flex-shrink: 0;
116
+ }
117
+ .label {
118
+ font-size: 13px;
119
+ font-weight: 700;
120
+ color: var(--_bucket-tint);
121
+ letter-spacing: 0.02em;
122
+ text-transform: uppercase;
123
+ }
124
+ .count {
125
+ font-size: 11px;
126
+ color: var(--sio-label-3);
127
+ ${sioTabularNums}
128
+ }
129
+ .spacer {
130
+ flex: 1;
131
+ }
132
+ .subtitle {
133
+ font-size: 11px;
134
+ color: var(--sio-label-3);
135
+ }
136
+ `,
137
+ ];
138
+
139
+ private get resolvedTint(): string {
140
+ return bucketTintVars[this.tint] ?? this.tint;
141
+ }
142
+
143
+ public render(): TemplateResult {
144
+ return html`
145
+ <div class="header" style="--_bucket-tint: ${this.resolvedTint}">
146
+ <div class="dot"></div>
147
+ <span class="label">${this.label}</span>
148
+ ${typeof this.count === 'number' && !Number.isNaN(this.count)
149
+ ? html`<span class="count">· ${this.count}</span>`
150
+ : html``}
151
+ <span class="spacer"></span>
152
+ ${this.subtitle ? html`<span class="subtitle">${this.subtitle}</span>` : html``}
153
+ <slot></slot>
154
+ </div>
155
+ `;
156
+ }
157
+ }