@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.
- package/.smartconfig.json +51 -0
- package/dist_bundle/bundle.js +5450 -3267
- package/dist_ts_web/00_commitinfo_data.js +3 -3
- package/dist_ts_web/elements/index.d.ts +20 -3
- package/dist_ts_web/elements/index.js +27 -7
- package/dist_ts_web/elements/sio-ai-card.d.ts +39 -0
- package/dist_ts_web/elements/sio-ai-card.js +229 -0
- package/dist_ts_web/elements/sio-avatar.d.ts +46 -0
- package/dist_ts_web/elements/sio-avatar.js +379 -0
- package/dist_ts_web/elements/sio-bucket-header.d.ts +36 -0
- package/dist_ts_web/elements/sio-bucket-header.js +197 -0
- package/dist_ts_web/elements/sio-button.d.ts +7 -3
- package/dist_ts_web/elements/sio-button.js +253 -152
- package/dist_ts_web/elements/sio-channel-rail.d.ts +37 -0
- package/dist_ts_web/elements/sio-channel-rail.js +275 -0
- package/dist_ts_web/elements/sio-chip.d.ts +25 -0
- package/dist_ts_web/elements/sio-chip.js +188 -0
- package/dist_ts_web/elements/sio-cmdk.d.ts +59 -0
- package/dist_ts_web/elements/sio-cmdk.js +471 -0
- package/dist_ts_web/elements/sio-combox.d.ts +55 -1
- package/dist_ts_web/elements/sio-combox.js +458 -261
- package/dist_ts_web/elements/sio-composer.d.ts +48 -0
- package/dist_ts_web/elements/sio-composer.js +357 -0
- package/dist_ts_web/elements/sio-conversation-selector.d.ts +41 -1
- package/dist_ts_web/elements/sio-conversation-selector.js +426 -238
- package/dist_ts_web/elements/sio-conversation-view.d.ts +34 -6
- package/dist_ts_web/elements/sio-conversation-view.js +738 -564
- package/dist_ts_web/elements/sio-dropdown-menu.d.ts +6 -6
- package/dist_ts_web/elements/sio-dropdown-menu.js +223 -169
- package/dist_ts_web/elements/sio-fab.d.ts +23 -5
- package/dist_ts_web/elements/sio-fab.js +259 -235
- package/dist_ts_web/elements/sio-glass-toolbar.d.ts +22 -0
- package/dist_ts_web/elements/sio-glass-toolbar.js +178 -0
- package/dist_ts_web/elements/sio-icon.d.ts +1 -0
- package/dist_ts_web/elements/sio-icon.js +254 -94
- package/dist_ts_web/elements/sio-image-lightbox.d.ts +3 -4
- package/dist_ts_web/elements/sio-image-lightbox.js +282 -191
- package/dist_ts_web/elements/sio-inset-list.d.ts +28 -0
- package/dist_ts_web/elements/sio-inset-list.js +191 -0
- package/dist_ts_web/elements/sio-kbd.d.ts +16 -0
- package/dist_ts_web/elements/sio-kbd.js +117 -0
- package/dist_ts_web/elements/sio-list-row.d.ts +55 -0
- package/dist_ts_web/elements/sio-list-row.js +462 -0
- package/dist_ts_web/elements/sio-message-input.d.ts +25 -4
- package/dist_ts_web/elements/sio-message-input.js +231 -226
- package/dist_ts_web/elements/sio-paper-thumb.d.ts +24 -0
- package/dist_ts_web/elements/sio-paper-thumb.js +293 -0
- package/dist_ts_web/elements/sio-pdf-viewer.d.ts +2 -0
- package/dist_ts_web/elements/sio-pdf-viewer.js +344 -244
- package/dist_ts_web/elements/sio-segmented.d.ts +32 -0
- package/dist_ts_web/elements/sio-segmented.js +217 -0
- package/dist_ts_web/elements/sio-step-timeline.d.ts +28 -0
- package/dist_ts_web/elements/sio-step-timeline.js +222 -0
- package/dist_ts_web/elements/sio-tab-bar.d.ts +36 -0
- package/dist_ts_web/elements/sio-tab-bar.js +232 -0
- package/dist_ts_web/elements/sio-unread-badge.d.ts +25 -0
- package/dist_ts_web/elements/sio-unread-badge.js +203 -0
- package/dist_ts_web/elements/sio-waveform.d.ts +34 -0
- package/dist_ts_web/elements/sio-waveform.js +304 -0
- package/dist_ts_web/elements/tokens.d.ts +70 -0
- package/dist_ts_web/elements/tokens.js +277 -0
- package/dist_ts_web/pages/firstpage.d.ts +5 -0
- package/dist_ts_web/pages/firstpage.js +21 -2
- package/dist_ts_web/pages/mainpage.d.ts +6 -0
- package/dist_ts_web/pages/mainpage.js +111 -39
- package/{license → license.md} +3 -1
- package/package.json +23 -29
- package/readme.hints.md +81 -0
- package/readme.md +85 -151
- package/ts_web/00_commitinfo_data.ts +2 -2
- package/ts_web/elements/index.ts +30 -7
- package/ts_web/elements/sio-ai-card.ts +175 -0
- package/ts_web/elements/sio-avatar.ts +295 -0
- package/ts_web/elements/sio-bucket-header.ts +157 -0
- package/ts_web/elements/sio-button.ts +149 -127
- package/ts_web/elements/sio-channel-rail.ts +243 -0
- package/ts_web/elements/sio-chip.ts +134 -0
- package/ts_web/elements/sio-cmdk.ts +461 -0
- package/ts_web/elements/sio-combox.ts +327 -204
- package/ts_web/elements/sio-composer.ts +306 -0
- package/ts_web/elements/sio-conversation-selector.ts +350 -228
- package/ts_web/elements/sio-conversation-view.ts +573 -457
- package/ts_web/elements/sio-dropdown-menu.ts +112 -130
- package/ts_web/elements/sio-fab.ts +192 -227
- package/ts_web/elements/sio-glass-toolbar.ts +140 -0
- package/ts_web/elements/sio-icon.ts +147 -25
- package/ts_web/elements/sio-image-lightbox.ts +62 -64
- package/ts_web/elements/sio-inset-list.ts +140 -0
- package/ts_web/elements/sio-kbd.ts +80 -0
- package/ts_web/elements/sio-list-row.ts +393 -0
- package/ts_web/elements/sio-message-input.ts +142 -221
- package/ts_web/elements/sio-paper-thumb.ts +244 -0
- package/ts_web/elements/sio-pdf-viewer.ts +58 -60
- package/ts_web/elements/sio-segmented.ts +183 -0
- package/ts_web/elements/sio-step-timeline.ts +179 -0
- package/ts_web/elements/sio-tab-bar.ts +195 -0
- package/ts_web/elements/sio-unread-badge.ts +147 -0
- package/ts_web/elements/sio-waveform.ts +239 -0
- package/ts_web/elements/tokens.ts +297 -0
- package/ts_web/pages/firstpage.ts +20 -1
- package/ts_web/pages/mainpage.ts +111 -39
- package/dist_bundle/bundle.js.map +0 -7
- package/dist_ts_web/elements/00colors.d.ts +0 -108
- package/dist_ts_web/elements/00colors.js +0 -137
- package/dist_ts_web/elements/00fonts.d.ts +0 -60
- package/dist_ts_web/elements/00fonts.js +0 -129
- package/dist_ts_web/elements/00tokens.d.ts +0 -123
- package/dist_ts_web/elements/00tokens.js +0 -176
- package/dist_ts_web/elements/sio-conversation-list.d.ts +0 -20
- package/dist_ts_web/elements/sio-conversation-list.js +0 -495
- package/dist_ts_web/elements/sio-recorder.d.ts +0 -60
- package/dist_ts_web/elements/sio-recorder.js +0 -173
- package/dist_ts_web/elements/sio-subwidget-conversations.d.ts +0 -16
- package/dist_ts_web/elements/sio-subwidget-conversations.js +0 -350
- package/dist_ts_web/elements/sio-subwidget-onboardme.d.ts +0 -7
- package/dist_ts_web/elements/sio-subwidget-onboardme.js +0 -243
- package/dist_watch/bundle.js +0 -81694
- package/dist_watch/bundle.js.map +0 -7
- package/dist_watch/index.html +0 -25
- package/npmextra.json +0 -19
- package/ts_web/elements/00colors.ts +0 -164
- package/ts_web/elements/00fonts.ts +0 -145
- package/ts_web/elements/00tokens.ts +0 -197
- package/ts_web/elements/sio-recorder.ts +0 -213
|
@@ -0,0 +1,195 @@
|
|
|
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-tab-bar': SioTabBar;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ISioTabBarChannel {
|
|
19
|
+
id: string;
|
|
20
|
+
label: string;
|
|
21
|
+
color: string;
|
|
22
|
+
icon: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ISioTabChangeDetail {
|
|
26
|
+
id: string;
|
|
27
|
+
channel: ISioTabBarChannel;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The v7 channel set — All / Mail / Chats / Calls / Letters
|
|
32
|
+
* (prototype: SIO_CHANNELS in SocialIOData.jsx).
|
|
33
|
+
*/
|
|
34
|
+
const defaultChannels: ISioTabBarChannel[] = [
|
|
35
|
+
{ id: 'all', label: 'All', color: 'var(--sio-blue)', icon: 'grid-2x2' },
|
|
36
|
+
{ id: 'mail', label: 'Mail', color: 'var(--sio-ch-mail)', icon: 'mail' },
|
|
37
|
+
{ id: 'chats', label: 'Chats', color: 'var(--sio-ch-chats)', icon: 'message-circle' },
|
|
38
|
+
{ id: 'calls', label: 'Calls', color: 'var(--sio-ch-calls)', icon: 'phone' },
|
|
39
|
+
{ id: 'letters', label: 'Letters', color: 'var(--sio-ch-letters)', icon: 'file-text' },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const calendarChannel: ISioTabBarChannel = {
|
|
43
|
+
id: 'calendar',
|
|
44
|
+
label: 'Calendar',
|
|
45
|
+
color: 'var(--sio-blue)',
|
|
46
|
+
icon: 'calendar',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* sio-tab-bar — the mobile bottom channel switcher (v7 prototype:
|
|
51
|
+
* SIOTabBar in SocialIOChannels.jsx).
|
|
52
|
+
*
|
|
53
|
+
* A liquid-glass bar (blur 28px saturate 180%, 0.5px top hairline) with one
|
|
54
|
+
* flex-equal tab per channel: 22px icon over a 10.5px label. The active tab
|
|
55
|
+
* lights up in its channel color; 30px bottom padding clears the iOS home
|
|
56
|
+
* indicator. Selecting a tab dispatches `tab-change` with `{ id, channel }`
|
|
57
|
+
* (bubbles, composed).
|
|
58
|
+
*/
|
|
59
|
+
@customElement('sio-tab-bar')
|
|
60
|
+
export class SioTabBar extends DeesElement {
|
|
61
|
+
public static demo = () => html`
|
|
62
|
+
<div style="display: flex; flex-direction: column; gap: 24px; max-width: 390px; font-family: var(--sio-font-sf);">
|
|
63
|
+
<!-- Mail active -->
|
|
64
|
+
<div
|
|
65
|
+
style="border-radius: 20px; overflow: hidden; border: var(--sio-hairline) solid var(--sio-separator); background: var(--sio-bg);"
|
|
66
|
+
>
|
|
67
|
+
<div style="padding: 18px 16px 26px;">
|
|
68
|
+
<div style="font-size: 28px; font-weight: 700; letter-spacing: 0.36px; color: var(--sio-label);">Mail</div>
|
|
69
|
+
<div style="margin-top: 3px; font-size: 13px; color: var(--sio-label-3);">
|
|
70
|
+
4 unread · Feed 12 · Paper 4 · People 3
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
<sio-tab-bar active="mail"></sio-tab-bar>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<!-- Chats active, with the optional Calendar tab -->
|
|
77
|
+
<div
|
|
78
|
+
style="border-radius: 20px; overflow: hidden; border: var(--sio-hairline) solid var(--sio-separator); background: var(--sio-bg);"
|
|
79
|
+
>
|
|
80
|
+
<div style="padding: 18px 16px 26px;">
|
|
81
|
+
<div style="font-size: 28px; font-weight: 700; letter-spacing: 0.36px; color: var(--sio-label);">Chats</div>
|
|
82
|
+
<div style="margin-top: 3px; font-size: 13px; color: var(--sio-label-3);">
|
|
83
|
+
3 new · Lena is typing…
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
<sio-tab-bar active="chats" .withCalendar=${true}></sio-tab-bar>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
`;
|
|
90
|
+
|
|
91
|
+
@property({ type: Array })
|
|
92
|
+
public accessor channels: ISioTabBarChannel[] = defaultChannels;
|
|
93
|
+
|
|
94
|
+
@property({ type: String })
|
|
95
|
+
public accessor active = 'mail';
|
|
96
|
+
|
|
97
|
+
@property({ type: Boolean })
|
|
98
|
+
public accessor withCalendar = false;
|
|
99
|
+
|
|
100
|
+
public static styles = [
|
|
101
|
+
...sioElementStyles,
|
|
102
|
+
css`
|
|
103
|
+
:host {
|
|
104
|
+
display: block;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* toolbar glass, hairline on the top edge (the bar sits at the bottom) */
|
|
108
|
+
.bar {
|
|
109
|
+
display: flex;
|
|
110
|
+
padding: 8px 0 30px; /* 30px clears the iOS home indicator */
|
|
111
|
+
background: var(--sio-toolbar-glass-bg);
|
|
112
|
+
backdrop-filter: blur(28px) saturate(180%);
|
|
113
|
+
-webkit-backdrop-filter: blur(28px) saturate(180%);
|
|
114
|
+
border-top: var(--sio-hairline) solid var(--sio-separator);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.tab {
|
|
118
|
+
appearance: none;
|
|
119
|
+
border: none;
|
|
120
|
+
margin: 0;
|
|
121
|
+
padding: 0;
|
|
122
|
+
background: transparent;
|
|
123
|
+
flex: 1;
|
|
124
|
+
min-width: 0;
|
|
125
|
+
display: flex;
|
|
126
|
+
flex-direction: column;
|
|
127
|
+
align-items: center;
|
|
128
|
+
gap: 2px;
|
|
129
|
+
font-family: var(--sio-font-sf);
|
|
130
|
+
color: var(--sio-label-3);
|
|
131
|
+
cursor: pointer;
|
|
132
|
+
-webkit-tap-highlight-color: transparent;
|
|
133
|
+
transition: color 120ms ease;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.tab.active {
|
|
137
|
+
color: var(--_channel-color, var(--sio-tint));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.tab:focus-visible {
|
|
141
|
+
outline: 2px solid color-mix(in oklab, var(--sio-tint) 40%, transparent);
|
|
142
|
+
outline-offset: -2px;
|
|
143
|
+
border-radius: var(--sio-radius-sm);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.label {
|
|
147
|
+
font-size: 10.5px;
|
|
148
|
+
font-weight: 500;
|
|
149
|
+
letter-spacing: 0.02px;
|
|
150
|
+
line-height: 1.25;
|
|
151
|
+
white-space: nowrap;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.tab.active .label {
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
}
|
|
157
|
+
`,
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
public render(): TemplateResult {
|
|
161
|
+
const tabs = this.withCalendar ? [...this.channels, calendarChannel] : this.channels;
|
|
162
|
+
return html`
|
|
163
|
+
<nav class="bar" part="bar">
|
|
164
|
+
${tabs.map(
|
|
165
|
+
(channel) => html`
|
|
166
|
+
<button
|
|
167
|
+
type="button"
|
|
168
|
+
class="tab ${channel.id === this.active ? 'active' : ''}"
|
|
169
|
+
style="--_channel-color: ${channel.color}"
|
|
170
|
+
aria-current=${channel.id === this.active ? 'page' : 'false'}
|
|
171
|
+
@click=${() => this.handleSelect(channel)}
|
|
172
|
+
>
|
|
173
|
+
<sio-icon icon=${channel.icon} size="22" .strokeWidth=${1.7}></sio-icon>
|
|
174
|
+
<span class="label">${channel.label}</span>
|
|
175
|
+
</button>
|
|
176
|
+
`,
|
|
177
|
+
)}
|
|
178
|
+
</nav>
|
|
179
|
+
`;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private handleSelect(channel: ISioTabBarChannel): void {
|
|
183
|
+
if (channel.id === this.active) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
this.active = channel.id;
|
|
187
|
+
this.dispatchEvent(
|
|
188
|
+
new CustomEvent<ISioTabChangeDetail>('tab-change', {
|
|
189
|
+
detail: { id: channel.id, channel },
|
|
190
|
+
bubbles: true,
|
|
191
|
+
composed: true,
|
|
192
|
+
}),
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DeesElement,
|
|
3
|
+
html,
|
|
4
|
+
property,
|
|
5
|
+
customElement,
|
|
6
|
+
css,
|
|
7
|
+
type TemplateResult,
|
|
8
|
+
} from '@design.estate/dees-element';
|
|
9
|
+
|
|
10
|
+
import { sioElementStyles } from './tokens.js';
|
|
11
|
+
import './sio-icon.js'; // used by the static demo
|
|
12
|
+
|
|
13
|
+
export type TSioUnreadBadgeChannel = 'tint' | 'mail' | 'chats' | 'calls' | 'letters' | 'alert';
|
|
14
|
+
export type TSioUnreadBadgeSize = 'md' | 'sm';
|
|
15
|
+
|
|
16
|
+
declare global {
|
|
17
|
+
interface HTMLElementTagNameMap {
|
|
18
|
+
'sio-unread-badge': SioUnreadBadge;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const badgeColors: Record<TSioUnreadBadgeChannel, string> = {
|
|
23
|
+
tint: 'var(--sio-tint)',
|
|
24
|
+
mail: 'var(--sio-ch-mail)',
|
|
25
|
+
chats: 'var(--sio-ch-chats)',
|
|
26
|
+
calls: 'var(--sio-ch-calls)',
|
|
27
|
+
letters: 'var(--sio-ch-letters)',
|
|
28
|
+
alert: 'var(--sio-red)',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* sio-unread-badge — count pill on solid channel color (v7 chat list /
|
|
33
|
+
* channel rail / widget launcher). md: min-width 20, height 20, radius 10,
|
|
34
|
+
* 11.5/700 tabular white. sm (rails): 16px with 10px numerals.
|
|
35
|
+
* `ring` adds the 2px surface ring used when the pill sits on an icon.
|
|
36
|
+
*/
|
|
37
|
+
@customElement('sio-unread-badge')
|
|
38
|
+
export class SioUnreadBadge extends DeesElement {
|
|
39
|
+
public static demo = () => html`
|
|
40
|
+
<div style="display: flex; flex-direction: column; gap: 16px;">
|
|
41
|
+
<div style="display: flex; gap: 10px; align-items: center;">
|
|
42
|
+
<sio-unread-badge count="3" channel="chats"></sio-unread-badge>
|
|
43
|
+
<sio-unread-badge count="12" channel="mail"></sio-unread-badge>
|
|
44
|
+
<sio-unread-badge count="1" channel="calls"></sio-unread-badge>
|
|
45
|
+
<sio-unread-badge count="2" channel="letters"></sio-unread-badge>
|
|
46
|
+
<sio-unread-badge count="4"></sio-unread-badge>
|
|
47
|
+
<sio-unread-badge count="1" channel="alert"></sio-unread-badge>
|
|
48
|
+
</div>
|
|
49
|
+
<div style="display: flex; gap: 10px; align-items: center;">
|
|
50
|
+
<sio-unread-badge count="3" channel="chats" size="sm"></sio-unread-badge>
|
|
51
|
+
<sio-unread-badge count="12" channel="mail" size="sm"></sio-unread-badge>
|
|
52
|
+
<sio-unread-badge count="5" channel="calls" size="sm" ring></sio-unread-badge>
|
|
53
|
+
</div>
|
|
54
|
+
<div style="display: flex; gap: 22px; align-items: center;">
|
|
55
|
+
<span style="position: relative; display: inline-flex;">
|
|
56
|
+
<sio-icon icon="envelope" size="20" style="color: var(--sio-ch-mail);"></sio-icon>
|
|
57
|
+
<sio-unread-badge
|
|
58
|
+
count="3"
|
|
59
|
+
channel="mail"
|
|
60
|
+
size="sm"
|
|
61
|
+
ring
|
|
62
|
+
style="position: absolute; top: -5px; right: -9px;"
|
|
63
|
+
></sio-unread-badge>
|
|
64
|
+
</span>
|
|
65
|
+
<span style="position: relative; display: inline-flex;">
|
|
66
|
+
<sio-icon icon="message-circle" size="20" style="color: var(--sio-ch-chats);"></sio-icon>
|
|
67
|
+
<sio-unread-badge
|
|
68
|
+
count="7"
|
|
69
|
+
channel="chats"
|
|
70
|
+
size="sm"
|
|
71
|
+
ring
|
|
72
|
+
style="position: absolute; top: -5px; right: -9px;"
|
|
73
|
+
></sio-unread-badge>
|
|
74
|
+
</span>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
`;
|
|
78
|
+
|
|
79
|
+
@property({ type: Number })
|
|
80
|
+
public accessor count = 0;
|
|
81
|
+
|
|
82
|
+
@property({ type: String })
|
|
83
|
+
public accessor channel: TSioUnreadBadgeChannel = 'tint';
|
|
84
|
+
|
|
85
|
+
@property({ type: String })
|
|
86
|
+
public accessor size: TSioUnreadBadgeSize = 'md';
|
|
87
|
+
|
|
88
|
+
/** 2px surface ring — for pills sitting on rail icons */
|
|
89
|
+
@property({ type: Boolean })
|
|
90
|
+
public accessor ring = false;
|
|
91
|
+
|
|
92
|
+
public static styles = [
|
|
93
|
+
...sioElementStyles,
|
|
94
|
+
css`
|
|
95
|
+
:host {
|
|
96
|
+
display: inline-flex;
|
|
97
|
+
flex-shrink: 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.badge {
|
|
101
|
+
box-sizing: border-box;
|
|
102
|
+
display: inline-flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
justify-content: center;
|
|
105
|
+
min-width: 20px;
|
|
106
|
+
height: 20px;
|
|
107
|
+
padding: 0 6px;
|
|
108
|
+
border-radius: 10px;
|
|
109
|
+
background: var(--badge-color);
|
|
110
|
+
color: #fff; /* white numerals on solid channel color per v7 */
|
|
111
|
+
font-family: var(--sio-font-sf);
|
|
112
|
+
font-size: 11.5px;
|
|
113
|
+
font-weight: 700;
|
|
114
|
+
font-variant-numeric: tabular-nums;
|
|
115
|
+
line-height: 1;
|
|
116
|
+
white-space: nowrap;
|
|
117
|
+
user-select: none;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.badge.sm {
|
|
121
|
+
min-width: 16px;
|
|
122
|
+
height: 16px;
|
|
123
|
+
padding: 0 5px;
|
|
124
|
+
border-radius: 8px;
|
|
125
|
+
font-size: 10px;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.badge.ring {
|
|
129
|
+
box-shadow: 0 0 0 2px var(--sio-surface);
|
|
130
|
+
}
|
|
131
|
+
`,
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
public render(): TemplateResult {
|
|
135
|
+
if (this.count <= 0) {
|
|
136
|
+
return html``;
|
|
137
|
+
}
|
|
138
|
+
return html`
|
|
139
|
+
<span
|
|
140
|
+
class="badge ${this.size === 'sm' ? 'sm' : ''} ${this.ring ? 'ring' : ''}"
|
|
141
|
+
style="--badge-color: ${badgeColors[this.channel] || badgeColors.tint};"
|
|
142
|
+
>
|
|
143
|
+
${this.count}
|
|
144
|
+
</span>
|
|
145
|
+
`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
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
|
+
|
|
11
|
+
declare global {
|
|
12
|
+
interface HTMLElementTagNameMap {
|
|
13
|
+
'sio-waveform': SioWaveform;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* sio-waveform — v7 voicemail / voice-message player.
|
|
19
|
+
*
|
|
20
|
+
* A surface card with a round play/pause control, a deterministic bar-chart
|
|
21
|
+
* waveform (played bars in the channel color, the rest in systemGray @ 45%
|
|
22
|
+
* per the v7 spec) and tabular elapsed / -remaining counters.
|
|
23
|
+
*
|
|
24
|
+
* Clicking the waveform dispatches `seek` ({ progress }); the play control
|
|
25
|
+
* toggles and dispatches `play` / `pause` ({ progress }).
|
|
26
|
+
*/
|
|
27
|
+
@customElement('sio-waveform')
|
|
28
|
+
export class SioWaveform extends DeesElement {
|
|
29
|
+
public static demo = () => html`
|
|
30
|
+
<div
|
|
31
|
+
style="display: flex; flex-direction: column; gap: 20px; max-width: 420px; padding: 20px; background: var(--sio-bg); border-radius: 18px; font-family: var(--sio-font-sf);"
|
|
32
|
+
>
|
|
33
|
+
<div>
|
|
34
|
+
<div style="font-size: 22px; font-weight: 700; letter-spacing: -0.4px; color: var(--sio-label);">
|
|
35
|
+
Maren Vogel
|
|
36
|
+
</div>
|
|
37
|
+
<div style="font-size: 13px; color: var(--sio-label-3); margin-top: 2px;">
|
|
38
|
+
Today 09:41 · 1:47 · Voicemail
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
<sio-waveform progress="0.28" elapsed="0:28" remaining="-1:19"></sio-waveform>
|
|
42
|
+
<sio-waveform
|
|
43
|
+
progress="0.62"
|
|
44
|
+
playing
|
|
45
|
+
elapsed="1:06"
|
|
46
|
+
remaining="-0:41"
|
|
47
|
+
color="var(--sio-ch-chats)"
|
|
48
|
+
bars="64"
|
|
49
|
+
></sio-waveform>
|
|
50
|
+
<sio-waveform progress="0" elapsed="0:00" remaining="-1:47" color="var(--sio-tint)"></sio-waveform>
|
|
51
|
+
</div>
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
/** Number of waveform bars (the v7 voicemail player draws 50). */
|
|
55
|
+
@property({ type: Number })
|
|
56
|
+
public accessor bars: number = 50;
|
|
57
|
+
|
|
58
|
+
/** Playback position, 0–1. Played bars take the channel color. */
|
|
59
|
+
@property({ type: Number })
|
|
60
|
+
public accessor progress: number = 0;
|
|
61
|
+
|
|
62
|
+
/** Accent for played bars + play control. Any CSS color, defaults to the calls channel. */
|
|
63
|
+
@property({ type: String })
|
|
64
|
+
public accessor color: string = 'var(--sio-ch-calls)';
|
|
65
|
+
|
|
66
|
+
/** Elapsed counter, e.g. "0:28". */
|
|
67
|
+
@property({ type: String })
|
|
68
|
+
public accessor elapsed: string = '0:00';
|
|
69
|
+
|
|
70
|
+
/** Remaining counter, e.g. "-1:19". */
|
|
71
|
+
@property({ type: String })
|
|
72
|
+
public accessor remaining: string = '-0:00';
|
|
73
|
+
|
|
74
|
+
@property({ type: Boolean, reflect: true })
|
|
75
|
+
public accessor playing = false;
|
|
76
|
+
|
|
77
|
+
public static styles = [
|
|
78
|
+
...sioElementStyles,
|
|
79
|
+
css`
|
|
80
|
+
:host {
|
|
81
|
+
display: block;
|
|
82
|
+
|
|
83
|
+
/* systemGray (#8E8E93) @ 45% — unplayed bars per the v7 spec */
|
|
84
|
+
--sio-waveform-rest: color-mix(in oklab, var(--sio-bucket-later) 45%, transparent);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.player {
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
gap: 12px;
|
|
91
|
+
padding: 14px 16px;
|
|
92
|
+
background: var(--sio-surface);
|
|
93
|
+
border: var(--sio-hairline) solid var(--sio-separator);
|
|
94
|
+
border-radius: 16px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.playButton {
|
|
98
|
+
width: 44px;
|
|
99
|
+
height: 44px;
|
|
100
|
+
flex-shrink: 0;
|
|
101
|
+
display: flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
justify-content: center;
|
|
104
|
+
border: none;
|
|
105
|
+
border-radius: 22px;
|
|
106
|
+
padding: 0;
|
|
107
|
+
cursor: pointer;
|
|
108
|
+
font: inherit;
|
|
109
|
+
transition: transform 80ms ease, filter 120ms ease;
|
|
110
|
+
}
|
|
111
|
+
.playButton:hover {
|
|
112
|
+
filter: brightness(1.06);
|
|
113
|
+
}
|
|
114
|
+
.playButton:active {
|
|
115
|
+
transform: scale(0.94);
|
|
116
|
+
}
|
|
117
|
+
.playButton:focus-visible {
|
|
118
|
+
outline: 2px solid color-mix(in oklab, var(--sio-tint) 60%, transparent);
|
|
119
|
+
outline-offset: 2px;
|
|
120
|
+
}
|
|
121
|
+
.playButton svg {
|
|
122
|
+
display: block;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.track {
|
|
126
|
+
flex: 1;
|
|
127
|
+
min-width: 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.wave {
|
|
131
|
+
display: block;
|
|
132
|
+
width: 100%;
|
|
133
|
+
height: 28px;
|
|
134
|
+
cursor: pointer;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.counters {
|
|
138
|
+
display: flex;
|
|
139
|
+
justify-content: space-between;
|
|
140
|
+
margin-top: 2px;
|
|
141
|
+
font-size: 11px;
|
|
142
|
+
line-height: 13px;
|
|
143
|
+
color: var(--sio-label-3);
|
|
144
|
+
font-variant-numeric: tabular-nums;
|
|
145
|
+
}
|
|
146
|
+
`,
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
public render(): TemplateResult {
|
|
150
|
+
const barCount = Math.max(1, Math.floor(this.bars));
|
|
151
|
+
const clamped = Math.min(1, Math.max(0, this.progress));
|
|
152
|
+
const playedCount = Math.round(barCount * clamped);
|
|
153
|
+
const barTemplates: TemplateResult[] = [];
|
|
154
|
+
for (let i = 0; i < barCount; i++) {
|
|
155
|
+
// deterministic pseudo-random heights — transcribed from the v7 prototype
|
|
156
|
+
const h = 4 + Math.abs(Math.sin(i * 0.55) * 11) + Math.abs(Math.cos(i * 1.3) * 6);
|
|
157
|
+
barTemplates.push(html`
|
|
158
|
+
<rect
|
|
159
|
+
x=${i * 4}
|
|
160
|
+
y=${14 - h / 2}
|
|
161
|
+
width="2.2"
|
|
162
|
+
height=${h}
|
|
163
|
+
rx="1.1"
|
|
164
|
+
fill=${i < playedCount ? this.color : 'var(--sio-waveform-rest)'}
|
|
165
|
+
></rect>
|
|
166
|
+
`);
|
|
167
|
+
}
|
|
168
|
+
return html`
|
|
169
|
+
<div class="player" part="player">
|
|
170
|
+
<button
|
|
171
|
+
class="playButton"
|
|
172
|
+
style="background: ${this.color};"
|
|
173
|
+
aria-label=${this.playing ? 'Pause' : 'Play'}
|
|
174
|
+
@click=${this.handlePlayToggle}
|
|
175
|
+
>
|
|
176
|
+
${this.playing
|
|
177
|
+
? html`
|
|
178
|
+
<svg width="18" height="18" viewBox="0 0 24 24" aria-hidden="true">
|
|
179
|
+
<rect x="6" y="4.5" width="4.4" height="15" rx="1.6" fill="var(--sio-surface)"></rect>
|
|
180
|
+
<rect x="13.6" y="4.5" width="4.4" height="15" rx="1.6" fill="var(--sio-surface)"></rect>
|
|
181
|
+
</svg>
|
|
182
|
+
`
|
|
183
|
+
: html`
|
|
184
|
+
<svg width="18" height="18" viewBox="0 0 24 24" aria-hidden="true">
|
|
185
|
+
<path d="M7 5l12 7-12 7V5z" fill="var(--sio-surface)"></path>
|
|
186
|
+
</svg>
|
|
187
|
+
`}
|
|
188
|
+
</button>
|
|
189
|
+
<div class="track">
|
|
190
|
+
<svg
|
|
191
|
+
class="wave"
|
|
192
|
+
viewBox="0 0 ${barCount * 4} 28"
|
|
193
|
+
preserveAspectRatio="none"
|
|
194
|
+
role="slider"
|
|
195
|
+
aria-label="Playback position"
|
|
196
|
+
aria-valuemin="0"
|
|
197
|
+
aria-valuemax="1"
|
|
198
|
+
aria-valuenow=${clamped.toFixed(2)}
|
|
199
|
+
@click=${this.handleSeek}
|
|
200
|
+
>
|
|
201
|
+
${barTemplates}
|
|
202
|
+
</svg>
|
|
203
|
+
<div class="counters">
|
|
204
|
+
<span>${this.elapsed}</span>
|
|
205
|
+
<span>${this.remaining}</span>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private handlePlayToggle() {
|
|
213
|
+
this.playing = !this.playing;
|
|
214
|
+
this.dispatchEvent(
|
|
215
|
+
new CustomEvent(this.playing ? 'play' : 'pause', {
|
|
216
|
+
detail: { progress: Math.min(1, Math.max(0, this.progress)) },
|
|
217
|
+
bubbles: true,
|
|
218
|
+
composed: true,
|
|
219
|
+
}),
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private handleSeek(event: MouseEvent) {
|
|
224
|
+
const wave = event.currentTarget as SVGSVGElement;
|
|
225
|
+
const rect = wave.getBoundingClientRect();
|
|
226
|
+
if (!rect.width) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const progress = Math.min(1, Math.max(0, (event.clientX - rect.left) / rect.width));
|
|
230
|
+
this.progress = progress;
|
|
231
|
+
this.dispatchEvent(
|
|
232
|
+
new CustomEvent('seek', {
|
|
233
|
+
detail: { progress },
|
|
234
|
+
bubbles: true,
|
|
235
|
+
composed: true,
|
|
236
|
+
}),
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|