chordia-ui 3.2.3 → 3.2.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chordia-ui",
3
- "version": "3.2.3",
3
+ "version": "3.2.4",
4
4
  "description": "Chordia Design System - UI components, tokens, and Tailwind preset",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs.js",
@@ -54,6 +54,10 @@
54
54
  "import": "./dist/components/login.es.js",
55
55
  "require": "./dist/components/login.cjs.js"
56
56
  },
57
+ "./components/onboarding": {
58
+ "import": "./dist/components/onboarding.es.js",
59
+ "require": "./dist/components/onboarding.cjs.js"
60
+ },
57
61
  "./pages/interactionDetails": {
58
62
  "import": "./dist/pages/interactionDetails.es.js",
59
63
  "require": "./dist/pages/interactionDetails.cjs.js"
@@ -47,5 +47,8 @@ export { NotificationPanel, NotificationItem } from './notifications';
47
47
 
48
48
  export { OverlayPanel } from './models';
49
49
 
50
+ // Onboarding
51
+ export { UploadEvaluate, GettingStarted, UploadInteraction, ConnectData, AddTeammates } from './onboarding';
52
+
50
53
  // Login
51
54
  export { LoginPage } from './login';
@@ -0,0 +1,278 @@
1
+ import { useState } from 'react';
2
+ import { Search } from 'lucide-react';
3
+
4
+ const FF = 'var(--font-sans)';
5
+
6
+ // ─── Styles ───
7
+
8
+ const containerStyle = {
9
+ fontFamily: FF,
10
+ };
11
+
12
+ const sectionTitleStyle = {
13
+ fontSize: 20,
14
+ fontWeight: 600,
15
+ fontStyle: 'normal',
16
+ fontFamily: FF,
17
+ color: 'var(--grey-strong)',
18
+ margin: 0,
19
+ lineHeight: 'normal',
20
+ };
21
+
22
+ const sectionSubtitleStyle = {
23
+ fontSize: 13,
24
+ fontWeight: 400,
25
+ fontStyle: 'normal',
26
+ color: 'var(--color-text-secondary)',
27
+ fontFamily: FF,
28
+ margin: '4px 0 0',
29
+ lineHeight: '140%',
30
+ };
31
+
32
+ const searchRowStyle = {
33
+ display: 'flex',
34
+ alignItems: 'center',
35
+ gap: 12,
36
+ marginTop: 24,
37
+ };
38
+
39
+ const searchInputWrapStyle = {
40
+ display: 'flex',
41
+ alignItems: 'center',
42
+ flex: 1,
43
+ height: 44,
44
+ padding: '0 14px',
45
+ borderRadius: 10,
46
+ border: '1px solid var(--color-input-border)',
47
+ background: 'var(--grey-white)',
48
+ boxSizing: 'border-box',
49
+ gap: 8,
50
+ };
51
+
52
+ const searchIconStyle = {
53
+ flexShrink: 0,
54
+ color: 'var(--color-text-secondary)',
55
+ };
56
+
57
+ const emailTagStyle = {
58
+ display: 'inline-flex',
59
+ alignItems: 'center',
60
+ gap: 4,
61
+ padding: '4px 10px',
62
+ borderRadius: 6,
63
+ background: 'var(--hover-warm)',
64
+ fontSize: 14,
65
+ fontWeight: 500,
66
+ fontFamily: FF,
67
+ color: 'var(--grey-strong)',
68
+ whiteSpace: 'nowrap',
69
+ };
70
+
71
+ const emailTagRemoveStyle = {
72
+ cursor: 'pointer',
73
+ fontSize: 16,
74
+ lineHeight: 1,
75
+ color: 'var(--color-text-secondary)',
76
+ marginLeft: 2,
77
+ };
78
+
79
+ const searchInputStyle = {
80
+ flex: 1,
81
+ border: 'none',
82
+ outline: 'none',
83
+ fontSize: 14,
84
+ fontWeight: 400,
85
+ fontFamily: FF,
86
+ color: 'var(--grey-strong)',
87
+ background: 'transparent',
88
+ height: '100%',
89
+ };
90
+
91
+ const inviteBtnStyle = {
92
+ display: 'flex',
93
+ height: 44,
94
+ padding: '0 24px',
95
+ justifyContent: 'center',
96
+ alignItems: 'center',
97
+ borderRadius: 10,
98
+ background: 'var(--grey-strong)',
99
+ fontSize: 14,
100
+ fontWeight: 600,
101
+ fontFamily: FF,
102
+ color: 'var(--grey-white)',
103
+ border: 'none',
104
+ cursor: 'pointer',
105
+ transition: 'var(--transition-fast)',
106
+ outline: 'none',
107
+ flexShrink: 0,
108
+ };
109
+
110
+ const suggestionsListStyle = {
111
+ marginTop: 8,
112
+ border: '1px solid var(--border)',
113
+ borderRadius: 10,
114
+ overflow: 'hidden',
115
+ background: 'var(--grey-white)',
116
+ };
117
+
118
+ const suggestionItemStyle = {
119
+ display: 'flex',
120
+ alignItems: 'center',
121
+ gap: 12,
122
+ padding: '14px 16px',
123
+ cursor: 'pointer',
124
+ transition: 'var(--transition-fast)',
125
+ borderBottom: '1px solid var(--border-subtle)',
126
+ };
127
+
128
+ const avatarStyle = (color) => ({
129
+ width: 40,
130
+ height: 40,
131
+ borderRadius: 9999,
132
+ background: color || 'var(--hover-warm)',
133
+ display: 'flex',
134
+ alignItems: 'center',
135
+ justifyContent: 'center',
136
+ fontSize: 14,
137
+ fontWeight: 600,
138
+ fontFamily: FF,
139
+ color: 'var(--color-green)',
140
+ flexShrink: 0,
141
+ });
142
+
143
+ const suggestionNameStyle = {
144
+ fontSize: 14,
145
+ fontWeight: 600,
146
+ fontFamily: FF,
147
+ color: 'var(--grey-strong)',
148
+ margin: 0,
149
+ lineHeight: '120%',
150
+ };
151
+
152
+ const suggestionEmailStyle = {
153
+ fontSize: 13,
154
+ fontWeight: 400,
155
+ fontFamily: FF,
156
+ color: 'var(--color-text-secondary)',
157
+ margin: '2px 0 0',
158
+ lineHeight: '140%',
159
+ };
160
+
161
+ // ─── Default Suggestions ───
162
+
163
+ const DEFAULT_SUGGESTIONS = [
164
+ { name: 'Alex Rivera', email: 'alex.rivera@company.com', initials: 'AR' },
165
+ { name: 'Alexandra Smith', email: 'a.smith@design.co', initials: 'AS' },
166
+ ];
167
+
168
+ // ─── Component ───
169
+
170
+ const AddTeammates = ({ suggestions = DEFAULT_SUGGESTIONS, onInvite }) => {
171
+ const [inputValue, setInputValue] = useState('');
172
+ const [emails, setEmails] = useState([]);
173
+ const [focused, setFocused] = useState(false);
174
+
175
+ const filteredSuggestions = suggestions.filter(
176
+ (s) =>
177
+ !emails.includes(s.email) &&
178
+ (s.name.toLowerCase().includes(inputValue.toLowerCase()) ||
179
+ s.email.toLowerCase().includes(inputValue.toLowerCase()))
180
+ );
181
+
182
+ const addEmail = (email) => {
183
+ if (email && !emails.includes(email)) {
184
+ setEmails([...emails, email]);
185
+ setInputValue('');
186
+ }
187
+ };
188
+
189
+ const removeEmail = (email) => {
190
+ setEmails(emails.filter((e) => e !== email));
191
+ };
192
+
193
+ const handleKeyDown = (e) => {
194
+ if (e.key === 'Enter' && inputValue.includes('@')) {
195
+ e.preventDefault();
196
+ addEmail(inputValue.trim());
197
+ }
198
+ if (e.key === 'Backspace' && !inputValue && emails.length) {
199
+ removeEmail(emails[emails.length - 1]);
200
+ }
201
+ };
202
+
203
+ const handleInvite = () => {
204
+ if (emails.length) {
205
+ onInvite?.(emails);
206
+ }
207
+ };
208
+
209
+ return (
210
+ <div style={containerStyle}>
211
+ <h2 style={sectionTitleStyle}>Add Teammates</h2>
212
+ <p style={sectionSubtitleStyle}>Collaborate with your team to accelerate your workflow.</p>
213
+
214
+ <div style={searchRowStyle}>
215
+ <div
216
+ style={{
217
+ ...searchInputWrapStyle,
218
+ borderColor: focused ? 'var(--color-green)' : 'var(--color-input-border)',
219
+ boxShadow: focused ? '0 0 0 3px var(--color-green-ring)' : 'none',
220
+ }}
221
+ >
222
+ <Search size={18} style={searchIconStyle} />
223
+ {emails.map((email) => (
224
+ <span key={email} style={emailTagStyle}>
225
+ {email}
226
+ <span style={emailTagRemoveStyle} onClick={() => removeEmail(email)}>×</span>
227
+ </span>
228
+ ))}
229
+ <input
230
+ type="text"
231
+ placeholder={emails.length ? '' : 'Search by name or email...'}
232
+ value={inputValue}
233
+ onChange={(e) => setInputValue(e.target.value)}
234
+ onFocus={() => setFocused(true)}
235
+ onBlur={() => setTimeout(() => setFocused(false), 150)}
236
+ onKeyDown={handleKeyDown}
237
+ style={searchInputStyle}
238
+ />
239
+ </div>
240
+ <button
241
+ style={inviteBtnStyle}
242
+ onClick={handleInvite}
243
+ onMouseEnter={(e) => { e.currentTarget.style.opacity = '0.85'; }}
244
+ onMouseLeave={(e) => { e.currentTarget.style.opacity = '1'; }}
245
+ >
246
+ Invite
247
+ </button>
248
+ </div>
249
+
250
+ {focused && filteredSuggestions.length > 0 && (
251
+ <div style={suggestionsListStyle}>
252
+ {filteredSuggestions.map((suggestion, idx) => (
253
+ <div
254
+ key={suggestion.email}
255
+ style={{
256
+ ...suggestionItemStyle,
257
+ ...(idx === filteredSuggestions.length - 1 ? { borderBottom: 'none' } : {}),
258
+ }}
259
+ onMouseDown={() => addEmail(suggestion.email)}
260
+ onMouseEnter={(e) => { e.currentTarget.style.background = 'var(--hover-warm-subtle)'; }}
261
+ onMouseLeave={(e) => { e.currentTarget.style.background = 'var(--grey-white)'; }}
262
+ >
263
+ <div style={avatarStyle()}>
264
+ {suggestion.initials}
265
+ </div>
266
+ <div>
267
+ <p style={suggestionNameStyle}>{suggestion.name}</p>
268
+ <p style={suggestionEmailStyle}>{suggestion.email}</p>
269
+ </div>
270
+ </div>
271
+ ))}
272
+ </div>
273
+ )}
274
+ </div>
275
+ );
276
+ };
277
+
278
+ export default AddTeammates;
@@ -2,6 +2,7 @@ import { useState } from 'react';
2
2
  import { MessageSquare, Database, Users, BookOpen, SlidersHorizontal, Briefcase } from 'lucide-react';
3
3
  import UploadInteraction from './UploadInteraction';
4
4
  import ConnectData from './ConnectData';
5
+ import AddTeammates from './AddTeammates';
5
6
 
6
7
  const FF = 'var(--font-sans)';
7
8
 
@@ -511,6 +512,9 @@ const GettingStarted = ({
511
512
  {activeStepId === 'connect' && (
512
513
  <ConnectData />
513
514
  )}
515
+ {activeStepId === 'invite' && (
516
+ <AddTeammates />
517
+ )}
514
518
  </div>
515
519
  </div>
516
520
  </div>
@@ -0,0 +1,5 @@
1
+ export { default as UploadEvaluate } from './UploadEvaluate';
2
+ export { default as GettingStarted } from './GettingStarted';
3
+ export { default as UploadInteraction } from './UploadInteraction';
4
+ export { default as ConnectData } from './ConnectData';
5
+ export { default as AddTeammates } from './AddTeammates';