@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,134 @@
|
|
|
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';
|
|
12
|
+
|
|
13
|
+
export type TSioChipVariant = 'channel' | 'lane' | 'status' | 'generic';
|
|
14
|
+
|
|
15
|
+
declare global {
|
|
16
|
+
interface HTMLElementTagNameMap {
|
|
17
|
+
'sio-chip': SioChip;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* variant:value → palette token.
|
|
23
|
+
* channel mail | chats | calls | letters
|
|
24
|
+
* lane feed | paper | people
|
|
25
|
+
* status now | today | later | done (unified-feed triage buckets)
|
|
26
|
+
*/
|
|
27
|
+
const chipPalette: Record<string, string> = {
|
|
28
|
+
'channel:mail': 'var(--sio-ch-mail)',
|
|
29
|
+
'channel:chats': 'var(--sio-ch-chats)',
|
|
30
|
+
'channel:calls': 'var(--sio-ch-calls)',
|
|
31
|
+
'channel:letters': 'var(--sio-ch-letters)',
|
|
32
|
+
'lane:feed': 'var(--sio-lane-feed)',
|
|
33
|
+
'lane:paper': 'var(--sio-lane-paper)',
|
|
34
|
+
'lane:people': 'var(--sio-lane-people)',
|
|
35
|
+
'status:now': 'var(--sio-bucket-now)',
|
|
36
|
+
'status:today': 'var(--sio-bucket-today)',
|
|
37
|
+
'status:later': 'var(--sio-bucket-later)',
|
|
38
|
+
'status:done': 'var(--sio-bucket-done)',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* sio-chip — uppercase 11px/600 label on a 14% color-mix tint of the
|
|
43
|
+
* palette color (v7 `.sio-lane-chip` and the channel chips in list rows).
|
|
44
|
+
*/
|
|
45
|
+
@customElement('sio-chip')
|
|
46
|
+
export class SioChip extends DeesElement {
|
|
47
|
+
public static demo = () => html`
|
|
48
|
+
<div style="display: flex; flex-direction: column; gap: 14px;">
|
|
49
|
+
<div style="display: flex; gap: 8px; flex-wrap: wrap; align-items: center;">
|
|
50
|
+
<sio-chip variant="channel" value="mail" icon="envelope">Mail</sio-chip>
|
|
51
|
+
<sio-chip variant="channel" value="chats" icon="message-circle">Chat</sio-chip>
|
|
52
|
+
<sio-chip variant="channel" value="calls" icon="phone">Call</sio-chip>
|
|
53
|
+
<sio-chip variant="channel" value="letters" icon="file-text">Letter</sio-chip>
|
|
54
|
+
</div>
|
|
55
|
+
<div style="display: flex; gap: 8px; flex-wrap: wrap; align-items: center;">
|
|
56
|
+
<sio-chip variant="lane" value="feed">Feed</sio-chip>
|
|
57
|
+
<sio-chip variant="lane" value="paper">Paper trail</sio-chip>
|
|
58
|
+
<sio-chip variant="lane" value="people">People</sio-chip>
|
|
59
|
+
</div>
|
|
60
|
+
<div style="display: flex; gap: 8px; flex-wrap: wrap; align-items: center;">
|
|
61
|
+
<sio-chip variant="status" value="now">Now</sio-chip>
|
|
62
|
+
<sio-chip variant="status" value="today">Today</sio-chip>
|
|
63
|
+
<sio-chip variant="status" value="later">Later</sio-chip>
|
|
64
|
+
<sio-chip variant="status" value="done">Done</sio-chip>
|
|
65
|
+
</div>
|
|
66
|
+
<div style="display: flex; gap: 8px; flex-wrap: wrap; align-items: center;">
|
|
67
|
+
<sio-chip icon="sparkles">AI summary</sio-chip>
|
|
68
|
+
<sio-chip color="var(--sio-ch-letters)">Suggested</sio-chip>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
@property({ type: String })
|
|
74
|
+
public accessor variant: TSioChipVariant = 'generic';
|
|
75
|
+
|
|
76
|
+
/** palette key within the variant, e.g. "mail", "feed", "now" */
|
|
77
|
+
@property({ type: String })
|
|
78
|
+
public accessor value = '';
|
|
79
|
+
|
|
80
|
+
/** custom CSS color for variant="generic"; defaults to the system tint */
|
|
81
|
+
@property({ type: String })
|
|
82
|
+
public accessor color = '';
|
|
83
|
+
|
|
84
|
+
/** optional leading glyph (sio-icon name) */
|
|
85
|
+
@property({ type: String })
|
|
86
|
+
public accessor icon = '';
|
|
87
|
+
|
|
88
|
+
public static styles = [
|
|
89
|
+
...sioElementStyles,
|
|
90
|
+
css`
|
|
91
|
+
:host {
|
|
92
|
+
display: inline-flex;
|
|
93
|
+
flex-shrink: 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* v7 .sio-lane-chip */
|
|
97
|
+
.chip {
|
|
98
|
+
display: inline-flex;
|
|
99
|
+
align-items: center;
|
|
100
|
+
gap: 6px;
|
|
101
|
+
padding: 3px 9px;
|
|
102
|
+
border-radius: var(--sio-radius-sm);
|
|
103
|
+
background: color-mix(in oklab, var(--chip-color) 14%, transparent);
|
|
104
|
+
color: var(--chip-color);
|
|
105
|
+
font-family: var(--sio-font-sf);
|
|
106
|
+
font-size: 11px;
|
|
107
|
+
font-weight: 600;
|
|
108
|
+
letter-spacing: 0.04em;
|
|
109
|
+
text-transform: uppercase;
|
|
110
|
+
white-space: nowrap;
|
|
111
|
+
user-select: none;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
sio-icon {
|
|
115
|
+
flex: 0 0 auto;
|
|
116
|
+
}
|
|
117
|
+
`,
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
private get resolvedColor(): string {
|
|
121
|
+
return (
|
|
122
|
+
chipPalette[`${this.variant}:${this.value}`] || this.color || 'var(--sio-tint)'
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public render(): TemplateResult {
|
|
127
|
+
return html`
|
|
128
|
+
<span class="chip" style="--chip-color: ${this.resolvedColor};">
|
|
129
|
+
${this.icon ? html`<sio-icon icon=${this.icon} size="10"></sio-icon>` : ''}
|
|
130
|
+
<slot></slot>
|
|
131
|
+
</span>
|
|
132
|
+
`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DeesElement,
|
|
3
|
+
html,
|
|
4
|
+
css,
|
|
5
|
+
customElement,
|
|
6
|
+
property,
|
|
7
|
+
state,
|
|
8
|
+
type TemplateResult,
|
|
9
|
+
} from '@design.estate/dees-element';
|
|
10
|
+
import { sioElementStyles } from './tokens.js';
|
|
11
|
+
import './sio-icon.js';
|
|
12
|
+
import './sio-kbd.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* sio-cmdk — v7 command palette overlay (SocialIODesktop.jsx `SIOCmdK`
|
|
16
|
+
* + socialio.css `.sio-cmdk*`).
|
|
17
|
+
*
|
|
18
|
+
* Scrim rgba(0,0,0,.32) + blur(6px), 640px panel at 14vh on --sio-surface,
|
|
19
|
+
* radius 14, deep shadow + 0.5px hairline ring. Search row with magnifier,
|
|
20
|
+
* query input and an `esc` kbd hint; grouped results with uppercase 11/600
|
|
21
|
+
* section headers; rows of 18px glyph + 14px label + right kbd chord.
|
|
22
|
+
* Active row = tint-soft wash + tint text.
|
|
23
|
+
*
|
|
24
|
+
* Keyboard: ↑/↓ move (wrapping), Enter runs the active item, Esc closes.
|
|
25
|
+
* Typing filters items across all sections; empty sections disappear.
|
|
26
|
+
*
|
|
27
|
+
* Events: `cmdk-run` { id } · `cmdk-close` (both bubble + composed).
|
|
28
|
+
* The host owns the `open` flag — the palette never hides itself.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
export interface ISioCmdkItem {
|
|
32
|
+
id: string;
|
|
33
|
+
/** lucide icon name rendered through sio-icon (18px glyph slot). */
|
|
34
|
+
icon: string;
|
|
35
|
+
label: string;
|
|
36
|
+
/** keyboard chord hint, e.g. '⇧R' or 'G I'. */
|
|
37
|
+
shortcut?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ISioCmdkSection {
|
|
41
|
+
name: string;
|
|
42
|
+
items: ISioCmdkItem[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
declare global {
|
|
46
|
+
interface HTMLElementTagNameMap {
|
|
47
|
+
'sio-cmdk': SioCmdk;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@customElement('sio-cmdk')
|
|
52
|
+
export class SioCmdk extends DeesElement {
|
|
53
|
+
public static demo = () => {
|
|
54
|
+
const demoSections: ISioCmdkSection[] = [
|
|
55
|
+
{
|
|
56
|
+
name: 'Actions',
|
|
57
|
+
items: [
|
|
58
|
+
{ id: 'action.reply', icon: 'reply', label: 'Reply', shortcut: 'R' },
|
|
59
|
+
{ id: 'action.reply-all', icon: 'reply-all', label: 'Reply all', shortcut: '⇧R' },
|
|
60
|
+
{ id: 'action.forward', icon: 'forward', label: 'Forward', shortcut: 'F' },
|
|
61
|
+
{ id: 'action.archive', icon: 'archive', label: 'Archive', shortcut: 'E' },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'Jump to',
|
|
66
|
+
items: [
|
|
67
|
+
{ id: 'goto.inbox-people', icon: 'inbox', label: 'Inbox · People', shortcut: 'G I' },
|
|
68
|
+
{ id: 'goto.starred', icon: 'star', label: 'Starred', shortcut: 'G S' },
|
|
69
|
+
{ id: 'goto.sent', icon: 'send', label: 'Sent', shortcut: 'G T' },
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'Snooze',
|
|
74
|
+
items: [
|
|
75
|
+
{ id: 'snooze.tomorrow', icon: 'sunset', label: 'Tomorrow morning', shortcut: 'H 1' },
|
|
76
|
+
{ id: 'snooze.evening', icon: 'clock', label: 'This evening', shortcut: 'H 2' },
|
|
77
|
+
{ id: 'snooze.next-week', icon: 'calendar', label: 'Next week', shortcut: 'H 3' },
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
// The wrapper's transform contains the fixed-position overlay so each
|
|
82
|
+
// frame keeps its own palette (same trick as the prototype's mac window).
|
|
83
|
+
const frameStyle = `
|
|
84
|
+
position: relative; transform: translateZ(0); overflow: hidden;
|
|
85
|
+
border-radius: 12px; background: var(--sio-bg);
|
|
86
|
+
box-shadow: 0 0 0 0.5px var(--sio-separator);
|
|
87
|
+
`;
|
|
88
|
+
const mockRow = (from: string, subject: string, time: string) => html`
|
|
89
|
+
<div style="display: flex; align-items: center; gap: 12px; padding: 12px 18px; border-bottom: 0.5px solid var(--sio-separator);">
|
|
90
|
+
<span style="width: 8px; height: 8px; border-radius: 50%; background: var(--sio-tint); flex-shrink: 0;"></span>
|
|
91
|
+
<span style="width: 140px; font-size: 14px; font-weight: 600; color: var(--sio-label); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${from}</span>
|
|
92
|
+
<span style="flex: 1; font-size: 14px; color: var(--sio-label-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${subject}</span>
|
|
93
|
+
<span style="font-size: 12px; color: var(--sio-label-3); font-variant-numeric: tabular-nums;">${time}</span>
|
|
94
|
+
</div>
|
|
95
|
+
`;
|
|
96
|
+
const mockWindow = html`
|
|
97
|
+
<div style="height: 44px; display: flex; align-items: center; gap: 8px; padding: 0 18px; border-bottom: 0.5px solid var(--sio-separator); background: var(--sio-surface);">
|
|
98
|
+
<span style="width: 12px; height: 12px; border-radius: 50%; background: #FF5F57;"></span>
|
|
99
|
+
<span style="width: 12px; height: 12px; border-radius: 50%; background: #FEBC2E;"></span>
|
|
100
|
+
<span style="width: 12px; height: 12px; border-radius: 50%; background: #28C840;"></span>
|
|
101
|
+
<span style="margin-left: 10px; font-size: 15px; font-weight: 700; letter-spacing: -0.3px; color: var(--sio-label);">People</span>
|
|
102
|
+
<span style="font-size: 12px; font-weight: 500; color: var(--sio-label-3);">3 unread</span>
|
|
103
|
+
</div>
|
|
104
|
+
${mockRow('Maya Lindqvist', 'Launch copy — final pass before Thursday', '09:41')}
|
|
105
|
+
${mockRow('Jonas Weber', 'Re: Offsite agenda + travel booking', 'Yesterday')}
|
|
106
|
+
${mockRow('Priya Nair', 'Voicemail transcript: callback re: contract', 'Tue')}
|
|
107
|
+
`;
|
|
108
|
+
return html`
|
|
109
|
+
<div style="display: flex; flex-direction: column; gap: 28px; font-family: var(--sio-font-sf);">
|
|
110
|
+
<div>
|
|
111
|
+
<div style="font-size: 12px; font-weight: 600; letter-spacing: 0.4px; text-transform: uppercase; color: var(--sio-label-3); margin: 0 0 10px 2px;">
|
|
112
|
+
⌘K over the desktop — grouped results, first row active
|
|
113
|
+
</div>
|
|
114
|
+
<div style="${frameStyle} height: 480px;">
|
|
115
|
+
<div>${mockWindow}</div>
|
|
116
|
+
<sio-cmdk
|
|
117
|
+
open
|
|
118
|
+
.sections=${demoSections}
|
|
119
|
+
@cmdk-run=${(eventArg: CustomEvent<{ id: string }>) => console.log('cmdk-run', eventArg.detail.id)}
|
|
120
|
+
@cmdk-close=${() => console.log('cmdk-close')}
|
|
121
|
+
></sio-cmdk>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
<div>
|
|
125
|
+
<div style="font-size: 12px; font-weight: 600; letter-spacing: 0.4px; text-transform: uppercase; color: var(--sio-label-3); margin: 0 0 10px 2px;">
|
|
126
|
+
Typed query "re" — sections filter down, empty groups drop out
|
|
127
|
+
</div>
|
|
128
|
+
<div style="${frameStyle} height: 340px;">
|
|
129
|
+
<div>${mockWindow}</div>
|
|
130
|
+
<sio-cmdk open query="re" .sections=${demoSections}></sio-cmdk>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
`;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// --------------------------------------------------------------------- api
|
|
138
|
+
|
|
139
|
+
/** Grouped commands; filtered live against `query`. */
|
|
140
|
+
@property({ type: Array })
|
|
141
|
+
public accessor sections: ISioCmdkSection[] = [];
|
|
142
|
+
|
|
143
|
+
/** Host-owned visibility flag (wire ⌘K / cmdk-close in the app shell). */
|
|
144
|
+
@property({ type: Boolean, reflect: true })
|
|
145
|
+
public accessor open = false;
|
|
146
|
+
|
|
147
|
+
/** Current filter text — settable to preseed, updated as the user types. */
|
|
148
|
+
@property({ type: String })
|
|
149
|
+
public accessor query = '';
|
|
150
|
+
|
|
151
|
+
@property({ type: String })
|
|
152
|
+
public accessor placeholder = 'Type a command…';
|
|
153
|
+
|
|
154
|
+
@state()
|
|
155
|
+
private accessor activeIndex = 0;
|
|
156
|
+
|
|
157
|
+
// ------------------------------------------------------------------ styles
|
|
158
|
+
|
|
159
|
+
public static styles = [
|
|
160
|
+
...sioElementStyles,
|
|
161
|
+
css`
|
|
162
|
+
:host {
|
|
163
|
+
display: none;
|
|
164
|
+
}
|
|
165
|
+
:host([open]) {
|
|
166
|
+
display: block;
|
|
167
|
+
position: fixed;
|
|
168
|
+
inset: 0;
|
|
169
|
+
z-index: 100;
|
|
170
|
+
}
|
|
171
|
+
/* scrim + shadow values are spec'd black in the v7 prototype
|
|
172
|
+
(socialio.css .sio-cmdk*) — they are not theme surfaces. */
|
|
173
|
+
.backdrop {
|
|
174
|
+
position: absolute;
|
|
175
|
+
inset: 0;
|
|
176
|
+
background: rgba(0, 0, 0, 0.32);
|
|
177
|
+
backdrop-filter: blur(6px);
|
|
178
|
+
-webkit-backdrop-filter: blur(6px);
|
|
179
|
+
display: flex;
|
|
180
|
+
justify-content: center;
|
|
181
|
+
align-items: flex-start;
|
|
182
|
+
padding-top: 14vh;
|
|
183
|
+
}
|
|
184
|
+
.panel {
|
|
185
|
+
width: 640px;
|
|
186
|
+
max-width: calc(100% - 80px);
|
|
187
|
+
background: var(--sio-surface);
|
|
188
|
+
border-radius: var(--sio-radius-lg);
|
|
189
|
+
box-shadow:
|
|
190
|
+
0 30px 80px rgba(0, 0, 0, 0.35),
|
|
191
|
+
0 0 0 var(--sio-hairline) var(--sio-separator);
|
|
192
|
+
overflow: hidden;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* search row */
|
|
196
|
+
.search-row {
|
|
197
|
+
display: flex;
|
|
198
|
+
align-items: center;
|
|
199
|
+
gap: 10px;
|
|
200
|
+
padding: 14px 16px;
|
|
201
|
+
border-bottom: var(--sio-hairline) solid var(--sio-separator);
|
|
202
|
+
}
|
|
203
|
+
.search-icon {
|
|
204
|
+
color: var(--sio-label-3);
|
|
205
|
+
flex-shrink: 0;
|
|
206
|
+
}
|
|
207
|
+
.search-input {
|
|
208
|
+
flex: 1;
|
|
209
|
+
min-width: 0;
|
|
210
|
+
padding: 0;
|
|
211
|
+
border: none;
|
|
212
|
+
outline: none;
|
|
213
|
+
background: transparent;
|
|
214
|
+
font-family: var(--sio-font-sf);
|
|
215
|
+
font-size: 15px;
|
|
216
|
+
line-height: 20px;
|
|
217
|
+
letter-spacing: -0.24px;
|
|
218
|
+
color: var(--sio-label);
|
|
219
|
+
}
|
|
220
|
+
.search-input::placeholder {
|
|
221
|
+
color: var(--sio-label-4);
|
|
222
|
+
}
|
|
223
|
+
.esc-hint {
|
|
224
|
+
flex-shrink: 0;
|
|
225
|
+
cursor: pointer;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* grouped results */
|
|
229
|
+
.results {
|
|
230
|
+
padding: 6px 0;
|
|
231
|
+
max-height: 360px;
|
|
232
|
+
overflow-y: auto;
|
|
233
|
+
overscroll-behavior: contain;
|
|
234
|
+
}
|
|
235
|
+
.section {
|
|
236
|
+
padding: 4px 0;
|
|
237
|
+
}
|
|
238
|
+
.section-header {
|
|
239
|
+
padding: 6px 18px;
|
|
240
|
+
font-size: 11px;
|
|
241
|
+
line-height: 13px;
|
|
242
|
+
font-weight: 600;
|
|
243
|
+
letter-spacing: 0.06px;
|
|
244
|
+
text-transform: uppercase;
|
|
245
|
+
color: var(--sio-label-3);
|
|
246
|
+
}
|
|
247
|
+
.row {
|
|
248
|
+
display: flex;
|
|
249
|
+
align-items: center;
|
|
250
|
+
gap: 12px;
|
|
251
|
+
padding: 8px 18px;
|
|
252
|
+
color: var(--sio-label);
|
|
253
|
+
cursor: default;
|
|
254
|
+
transition: background 0.08s ease;
|
|
255
|
+
}
|
|
256
|
+
.row.active {
|
|
257
|
+
background: var(--sio-tint-soft);
|
|
258
|
+
color: var(--sio-tint);
|
|
259
|
+
}
|
|
260
|
+
.glyph {
|
|
261
|
+
width: 18px;
|
|
262
|
+
display: inline-flex;
|
|
263
|
+
align-items: center;
|
|
264
|
+
justify-content: center;
|
|
265
|
+
flex-shrink: 0;
|
|
266
|
+
color: inherit;
|
|
267
|
+
}
|
|
268
|
+
.label {
|
|
269
|
+
flex: 1;
|
|
270
|
+
min-width: 0;
|
|
271
|
+
font-size: 14px;
|
|
272
|
+
line-height: 19px;
|
|
273
|
+
letter-spacing: -0.15px;
|
|
274
|
+
white-space: nowrap;
|
|
275
|
+
overflow: hidden;
|
|
276
|
+
text-overflow: ellipsis;
|
|
277
|
+
}
|
|
278
|
+
.row sio-kbd {
|
|
279
|
+
flex-shrink: 0;
|
|
280
|
+
}
|
|
281
|
+
.empty {
|
|
282
|
+
padding: 18px;
|
|
283
|
+
text-align: center;
|
|
284
|
+
font-size: 14px;
|
|
285
|
+
color: var(--sio-label-3);
|
|
286
|
+
}
|
|
287
|
+
`,
|
|
288
|
+
];
|
|
289
|
+
|
|
290
|
+
// --------------------------------------------------------------- filtering
|
|
291
|
+
|
|
292
|
+
private getFilteredSections(): ISioCmdkSection[] {
|
|
293
|
+
const normalizedQuery = this.query.trim().toLowerCase();
|
|
294
|
+
if (!normalizedQuery) {
|
|
295
|
+
return this.sections;
|
|
296
|
+
}
|
|
297
|
+
return this.sections
|
|
298
|
+
.map((sectionArg) => ({
|
|
299
|
+
...sectionArg,
|
|
300
|
+
items: sectionArg.items.filter((itemArg) =>
|
|
301
|
+
itemArg.label.toLowerCase().includes(normalizedQuery),
|
|
302
|
+
),
|
|
303
|
+
}))
|
|
304
|
+
.filter((sectionArg) => sectionArg.items.length > 0);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private getFlatItems(): ISioCmdkItem[] {
|
|
308
|
+
return this.getFilteredSections().flatMap((sectionArg) => sectionArg.items);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// -------------------------------------------------------------- lifecycle
|
|
312
|
+
|
|
313
|
+
public willUpdate(): void {
|
|
314
|
+
// keep the active row inside the (possibly shrunken) filtered list
|
|
315
|
+
const totalItems = this.getFlatItems().length;
|
|
316
|
+
if (this.activeIndex >= totalItems) {
|
|
317
|
+
this.activeIndex = totalItems > 0 ? totalItems - 1 : 0;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
public updated(changedPropertiesArg: Map<PropertyKey, unknown>): void {
|
|
322
|
+
if (changedPropertiesArg.has('open') && this.open) {
|
|
323
|
+
const inputElement = this.shadowRoot?.querySelector<HTMLInputElement>('.search-input');
|
|
324
|
+
inputElement?.focus({ preventScroll: true });
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ------------------------------------------------------------ interaction
|
|
329
|
+
|
|
330
|
+
private handleInput(eventArg: Event): void {
|
|
331
|
+
this.query = (eventArg.target as HTMLInputElement).value;
|
|
332
|
+
this.activeIndex = 0;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private handleKeydown(eventArg: KeyboardEvent): void {
|
|
336
|
+
const flatItems = this.getFlatItems();
|
|
337
|
+
switch (eventArg.key) {
|
|
338
|
+
case 'ArrowDown':
|
|
339
|
+
eventArg.preventDefault();
|
|
340
|
+
if (flatItems.length > 0) {
|
|
341
|
+
this.activeIndex = (this.activeIndex + 1) % flatItems.length;
|
|
342
|
+
this.scrollActiveRowIntoView();
|
|
343
|
+
}
|
|
344
|
+
break;
|
|
345
|
+
case 'ArrowUp':
|
|
346
|
+
eventArg.preventDefault();
|
|
347
|
+
if (flatItems.length > 0) {
|
|
348
|
+
this.activeIndex = (this.activeIndex - 1 + flatItems.length) % flatItems.length;
|
|
349
|
+
this.scrollActiveRowIntoView();
|
|
350
|
+
}
|
|
351
|
+
break;
|
|
352
|
+
case 'Enter':
|
|
353
|
+
eventArg.preventDefault();
|
|
354
|
+
if (flatItems[this.activeIndex]) {
|
|
355
|
+
this.runItem(flatItems[this.activeIndex]);
|
|
356
|
+
}
|
|
357
|
+
break;
|
|
358
|
+
case 'Escape':
|
|
359
|
+
eventArg.preventDefault();
|
|
360
|
+
this.close();
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
private async scrollActiveRowIntoView(): Promise<void> {
|
|
366
|
+
await this.updateComplete;
|
|
367
|
+
this.shadowRoot?.querySelector('.row.active')?.scrollIntoView({ block: 'nearest' });
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
private handleBackdropClick(eventArg: MouseEvent): void {
|
|
371
|
+
if (eventArg.target === eventArg.currentTarget) {
|
|
372
|
+
this.close();
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
private runItem(itemArg: ISioCmdkItem): void {
|
|
377
|
+
this.dispatchEvent(
|
|
378
|
+
new CustomEvent<{ id: string }>('cmdk-run', {
|
|
379
|
+
detail: { id: itemArg.id },
|
|
380
|
+
bubbles: true,
|
|
381
|
+
composed: true,
|
|
382
|
+
}),
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
private close(): void {
|
|
387
|
+
this.dispatchEvent(
|
|
388
|
+
new CustomEvent('cmdk-close', {
|
|
389
|
+
bubbles: true,
|
|
390
|
+
composed: true,
|
|
391
|
+
}),
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// ----------------------------------------------------------------- render
|
|
396
|
+
|
|
397
|
+
public render(): TemplateResult {
|
|
398
|
+
if (!this.open) {
|
|
399
|
+
return html``;
|
|
400
|
+
}
|
|
401
|
+
const filteredSections = this.getFilteredSections();
|
|
402
|
+
let runningIndex = -1;
|
|
403
|
+
return html`
|
|
404
|
+
<div class="backdrop" @click=${this.handleBackdropClick} @keydown=${this.handleKeydown}>
|
|
405
|
+
<div class="panel" role="dialog" aria-modal="true" aria-label="Command palette">
|
|
406
|
+
<div class="search-row">
|
|
407
|
+
<sio-icon class="search-icon" icon="search" size="16"></sio-icon>
|
|
408
|
+
<input
|
|
409
|
+
class="search-input"
|
|
410
|
+
type="text"
|
|
411
|
+
.value=${this.query}
|
|
412
|
+
placeholder=${this.placeholder}
|
|
413
|
+
spellcheck="false"
|
|
414
|
+
autocomplete="off"
|
|
415
|
+
aria-label="Search commands"
|
|
416
|
+
@input=${this.handleInput}
|
|
417
|
+
/>
|
|
418
|
+
<sio-kbd class="esc-hint" @click=${this.close}>esc</sio-kbd>
|
|
419
|
+
</div>
|
|
420
|
+
<div class="results" role="listbox" aria-label="Commands">
|
|
421
|
+
${filteredSections.length === 0
|
|
422
|
+
? html`<div class="empty">No matching commands</div>`
|
|
423
|
+
: filteredSections.map(
|
|
424
|
+
(sectionArg) => html`
|
|
425
|
+
<div class="section">
|
|
426
|
+
<div class="section-header">${sectionArg.name}</div>
|
|
427
|
+
${sectionArg.items.map((itemArg) => {
|
|
428
|
+
runningIndex += 1;
|
|
429
|
+
const itemIndex = runningIndex;
|
|
430
|
+
const isActive = itemIndex === this.activeIndex;
|
|
431
|
+
return html`
|
|
432
|
+
<div
|
|
433
|
+
class="row ${isActive ? 'active' : ''}"
|
|
434
|
+
role="option"
|
|
435
|
+
aria-selected=${isActive ? 'true' : 'false'}
|
|
436
|
+
@pointerenter=${() => {
|
|
437
|
+
this.activeIndex = itemIndex;
|
|
438
|
+
}}
|
|
439
|
+
@click=${() => this.runItem(itemArg)}
|
|
440
|
+
>
|
|
441
|
+
<span class="glyph">
|
|
442
|
+
${itemArg.icon
|
|
443
|
+
? html`<sio-icon icon=${itemArg.icon} size="16"></sio-icon>`
|
|
444
|
+
: html``}
|
|
445
|
+
</span>
|
|
446
|
+
<span class="label">${itemArg.label}</span>
|
|
447
|
+
${itemArg.shortcut
|
|
448
|
+
? html`<sio-kbd>${itemArg.shortcut}</sio-kbd>`
|
|
449
|
+
: html``}
|
|
450
|
+
</div>
|
|
451
|
+
`;
|
|
452
|
+
})}
|
|
453
|
+
</div>
|
|
454
|
+
`,
|
|
455
|
+
)}
|
|
456
|
+
</div>
|
|
457
|
+
</div>
|
|
458
|
+
</div>
|
|
459
|
+
`;
|
|
460
|
+
}
|
|
461
|
+
}
|