@superleapai/flow-ui 1.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/CHANGELOG.md +65 -0
- package/LICENSE +21 -0
- package/README.md +451 -0
- package/components/alert.js +282 -0
- package/components/avatar.js +195 -0
- package/components/badge.js +135 -0
- package/components/button.js +201 -0
- package/components/checkbox.js +254 -0
- package/components/currency.js +227 -0
- package/components/date-time-picker/date-time-picker-utils.js +253 -0
- package/components/date-time-picker/date-time-picker.js +532 -0
- package/components/duration/duration-constants.js +46 -0
- package/components/duration/duration-utils.js +164 -0
- package/components/duration/duration.js +448 -0
- package/components/enum-multiselect.js +869 -0
- package/components/enum-select.js +831 -0
- package/components/enumeration.js +213 -0
- package/components/file-input.js +533 -0
- package/components/icon.js +200 -0
- package/components/input.js +259 -0
- package/components/label.js +111 -0
- package/components/multiselect.js +351 -0
- package/components/phone-input/phone-input.js +392 -0
- package/components/phone-input/phone-utils.js +157 -0
- package/components/popover.js +240 -0
- package/components/radio-group.js +435 -0
- package/components/record-multiselect.js +956 -0
- package/components/record-select.js +930 -0
- package/components/select.js +544 -0
- package/components/spinner.js +136 -0
- package/components/table.js +335 -0
- package/components/textarea.js +114 -0
- package/components/time-picker.js +357 -0
- package/components/toast.js +343 -0
- package/core/flow.js +1729 -0
- package/core/superleapClient.js +146 -0
- package/dist/output.css +2 -0
- package/index.d.ts +458 -0
- package/index.js +253 -0
- package/package.json +70 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alert Component (vanilla JS, Tailwind)
|
|
3
|
+
* Alert notification component with multiple variants.
|
|
4
|
+
* Displays contextual feedback messages with optional icons and actions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
(function (global) {
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
// SVG Icons
|
|
11
|
+
const ICONS = {
|
|
12
|
+
alertCircle: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>`,
|
|
13
|
+
circleCheck: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>`,
|
|
14
|
+
infoCircle: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>`,
|
|
15
|
+
playstationX: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>`,
|
|
16
|
+
x: `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>`,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Variant to icon mapping
|
|
20
|
+
const VARIANT_ICONS = {
|
|
21
|
+
default: null,
|
|
22
|
+
destructive: "playstationX",
|
|
23
|
+
warning: "alertCircle",
|
|
24
|
+
error: "playstationX",
|
|
25
|
+
success: "circleCheck",
|
|
26
|
+
info: "infoCircle",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get variant-specific Tailwind classes
|
|
31
|
+
*/
|
|
32
|
+
function getVariantClasses(variant) {
|
|
33
|
+
const variants = {
|
|
34
|
+
default: {
|
|
35
|
+
container: "bg-fill-quarternary-fill-white border-borderColor-border-primary",
|
|
36
|
+
title: "text-typography-primary-text",
|
|
37
|
+
description: "text-typography-secondary-text",
|
|
38
|
+
},
|
|
39
|
+
destructive: {
|
|
40
|
+
container: "bg-fill-quarternary-fill-white border-error-border",
|
|
41
|
+
title: "text-error-text-base",
|
|
42
|
+
description: "text-error-text-base",
|
|
43
|
+
},
|
|
44
|
+
warning: {
|
|
45
|
+
container: "bg-warning-surface border-warning-border",
|
|
46
|
+
title: "text-warning-text-base",
|
|
47
|
+
description: "text-warning-text-base",
|
|
48
|
+
},
|
|
49
|
+
error: {
|
|
50
|
+
container: "bg-error-surface border-error-border",
|
|
51
|
+
title: "text-error-text-base",
|
|
52
|
+
description: "text-error-text-base",
|
|
53
|
+
},
|
|
54
|
+
success: {
|
|
55
|
+
container: "bg-success-surface border-success-border",
|
|
56
|
+
title: "text-success-text-base",
|
|
57
|
+
description: "text-success-text-base",
|
|
58
|
+
},
|
|
59
|
+
info: {
|
|
60
|
+
container: "bg-info-surface border-info-border",
|
|
61
|
+
title: "text-info-text-base",
|
|
62
|
+
description: "text-info-text-base",
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
return variants[variant] || variants.default;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Create an alert element
|
|
70
|
+
* @param {Object} config - Configuration object
|
|
71
|
+
* @param {string} config.title - Alert title (optional)
|
|
72
|
+
* @param {string} config.description - Alert description/message
|
|
73
|
+
* @param {string} [config.variant='default'] - 'default' | 'destructive' | 'warning' | 'error' | 'success' | 'info'
|
|
74
|
+
* @param {HTMLElement|string} [config.icon] - Custom icon element or HTML string (optional)
|
|
75
|
+
* @param {HTMLElement|string} [config.action] - Action element or HTML string (optional, e.g., close button)
|
|
76
|
+
* @param {string} [config.className] - Additional CSS classes
|
|
77
|
+
* @returns {HTMLElement} Alert element
|
|
78
|
+
*/
|
|
79
|
+
function create(config = {}) {
|
|
80
|
+
const {
|
|
81
|
+
title = "",
|
|
82
|
+
description = "",
|
|
83
|
+
variant = "default",
|
|
84
|
+
icon = null,
|
|
85
|
+
action = null,
|
|
86
|
+
className = "",
|
|
87
|
+
} = config;
|
|
88
|
+
|
|
89
|
+
if (!description && !title) {
|
|
90
|
+
console.warn("[Alert] No title or description provided");
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const variantClasses = getVariantClasses(variant);
|
|
95
|
+
|
|
96
|
+
// Create alert root element
|
|
97
|
+
const alert = document.createElement("div");
|
|
98
|
+
alert.setAttribute("data-slot", "alert");
|
|
99
|
+
alert.setAttribute("role", "alert");
|
|
100
|
+
|
|
101
|
+
// Base classes - using flex layout for better control
|
|
102
|
+
const baseClasses = "flex gap-8 rounded-4 border-1/2 p-8 text-left w-full";
|
|
103
|
+
|
|
104
|
+
alert.className = `${baseClasses} ${variantClasses.container} ${className}`.trim();
|
|
105
|
+
|
|
106
|
+
// Add icon if provided or if variant has default icon
|
|
107
|
+
const iconKey = VARIANT_ICONS[variant];
|
|
108
|
+
if (icon !== null || iconKey) {
|
|
109
|
+
const iconWrapper = document.createElement("div");
|
|
110
|
+
iconWrapper.className = "flex-shrink-0 flex items-start pt-2";
|
|
111
|
+
iconWrapper.style.cssText = "width: 16px; height: 16px;";
|
|
112
|
+
|
|
113
|
+
if (icon !== null) {
|
|
114
|
+
if (typeof icon === "string") {
|
|
115
|
+
iconWrapper.innerHTML = icon;
|
|
116
|
+
} else if (icon instanceof HTMLElement) {
|
|
117
|
+
iconWrapper.appendChild(icon);
|
|
118
|
+
}
|
|
119
|
+
} else if (iconKey) {
|
|
120
|
+
iconWrapper.innerHTML = ICONS[iconKey];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
alert.appendChild(iconWrapper);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Create content wrapper for title and description
|
|
127
|
+
const contentWrapper = document.createElement("div");
|
|
128
|
+
contentWrapper.className = "flex-1 flex flex-col gap-4 min-w-0";
|
|
129
|
+
|
|
130
|
+
// Create title if provided
|
|
131
|
+
if (title) {
|
|
132
|
+
const titleEl = document.createElement("div");
|
|
133
|
+
titleEl.setAttribute("data-slot", "alert-title");
|
|
134
|
+
titleEl.className = `text-med-12 ${variantClasses.title}`;
|
|
135
|
+
|
|
136
|
+
if (typeof title === "string") {
|
|
137
|
+
titleEl.textContent = title;
|
|
138
|
+
} else if (title instanceof HTMLElement) {
|
|
139
|
+
titleEl.appendChild(title);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
contentWrapper.appendChild(titleEl);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Create description if provided
|
|
146
|
+
if (description) {
|
|
147
|
+
const descEl = document.createElement("div");
|
|
148
|
+
descEl.setAttribute("data-slot", "alert-description");
|
|
149
|
+
descEl.className = `text-reg-12 ${variantClasses.description}`;
|
|
150
|
+
|
|
151
|
+
if (typeof description === "string") {
|
|
152
|
+
descEl.textContent = description;
|
|
153
|
+
} else if (description instanceof HTMLElement) {
|
|
154
|
+
descEl.appendChild(description);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
contentWrapper.appendChild(descEl);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
alert.appendChild(contentWrapper);
|
|
161
|
+
|
|
162
|
+
// Add action if provided
|
|
163
|
+
if (action !== null) {
|
|
164
|
+
const actionWrapper = document.createElement("div");
|
|
165
|
+
actionWrapper.setAttribute("data-slot", "alert-action");
|
|
166
|
+
actionWrapper.className = "flex-shrink-0 flex items-start pt-2";
|
|
167
|
+
actionWrapper.style.cssText = "margin-left: auto;";
|
|
168
|
+
|
|
169
|
+
if (typeof action === "string") {
|
|
170
|
+
actionWrapper.innerHTML = action;
|
|
171
|
+
} else if (action instanceof HTMLElement) {
|
|
172
|
+
actionWrapper.appendChild(action);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
alert.appendChild(actionWrapper);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return alert;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Create a simple alert with default styling
|
|
183
|
+
* @param {string} message - Alert message
|
|
184
|
+
* @param {string} [variant='default'] - Alert variant
|
|
185
|
+
* @param {Object} [options] - Additional options
|
|
186
|
+
* @returns {HTMLElement} Alert element
|
|
187
|
+
*/
|
|
188
|
+
function simple(message, variant = "default", options = {}) {
|
|
189
|
+
return create({
|
|
190
|
+
description: message,
|
|
191
|
+
variant: variant,
|
|
192
|
+
...options,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Create a success alert
|
|
198
|
+
* @param {string} message - Alert message
|
|
199
|
+
* @param {Object} [options] - Additional options
|
|
200
|
+
* @returns {HTMLElement} Alert element
|
|
201
|
+
*/
|
|
202
|
+
function success(message, options = {}) {
|
|
203
|
+
return simple(message, "success", options);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Create an error alert
|
|
208
|
+
* @param {string} message - Alert message
|
|
209
|
+
* @param {Object} [options] - Additional options
|
|
210
|
+
* @returns {HTMLElement} Alert element
|
|
211
|
+
*/
|
|
212
|
+
function error(message, options = {}) {
|
|
213
|
+
return simple(message, "error", options);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Create a warning alert
|
|
218
|
+
* @param {string} message - Alert message
|
|
219
|
+
* @param {Object} [options] - Additional options
|
|
220
|
+
* @returns {HTMLElement} Alert element
|
|
221
|
+
*/
|
|
222
|
+
function warning(message, options = {}) {
|
|
223
|
+
return simple(message, "warning", options);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Create an info alert
|
|
228
|
+
* @param {string} message - Alert message
|
|
229
|
+
* @param {Object} [options] - Additional options
|
|
230
|
+
* @returns {HTMLElement} Alert element
|
|
231
|
+
*/
|
|
232
|
+
function info(message, options = {}) {
|
|
233
|
+
return simple(message, "info", options);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Create a destructive alert
|
|
238
|
+
* @param {string} message - Alert message
|
|
239
|
+
* @param {Object} [options] - Additional options
|
|
240
|
+
* @returns {HTMLElement} Alert element
|
|
241
|
+
*/
|
|
242
|
+
function destructive(message, options = {}) {
|
|
243
|
+
return simple(message, "destructive", options);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Create a close button for use in alert actions
|
|
248
|
+
* @param {Function} onClick - Click handler
|
|
249
|
+
* @param {string} [variant='default'] - Alert variant to match button color
|
|
250
|
+
* @returns {HTMLElement} Close button element
|
|
251
|
+
*/
|
|
252
|
+
function createCloseButton(onClick, variant = 'default') {
|
|
253
|
+
const button = document.createElement("button");
|
|
254
|
+
button.type = "button";
|
|
255
|
+
|
|
256
|
+
// Get the appropriate text color class based on variant
|
|
257
|
+
const variantClasses = getVariantClasses(variant);
|
|
258
|
+
const colorClass = variantClasses.title; // Use title color for consistency
|
|
259
|
+
|
|
260
|
+
button.className = `flex items-center justify-center rounded-4 transition-opacity opacity-70 hover:opacity-100 focus:opacity-100 focus:outline-none ${colorClass}`;
|
|
261
|
+
button.style.cssText = "flex-shrink: 0; width: 20px; height: 20px; border: none; background: transparent; cursor: pointer; padding: 2px;";
|
|
262
|
+
button.innerHTML = ICONS.x;
|
|
263
|
+
|
|
264
|
+
if (onClick) {
|
|
265
|
+
button.addEventListener("click", onClick);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return button;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Export to global namespace
|
|
272
|
+
global.Alert = {
|
|
273
|
+
create,
|
|
274
|
+
simple,
|
|
275
|
+
success,
|
|
276
|
+
error,
|
|
277
|
+
warning,
|
|
278
|
+
info,
|
|
279
|
+
destructive,
|
|
280
|
+
createCloseButton,
|
|
281
|
+
};
|
|
282
|
+
})(typeof window !== "undefined" ? window : this);
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Avatar Component (vanilla JS)
|
|
3
|
+
* Avatar, VividAvatar (name-based color), AvatarGroup, VividAvatarGroup.
|
|
4
|
+
* Uses stringToColor for deterministic background/text colors from a string (e.g. user name).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
(function (global) {
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
var AVATAR_BASE =
|
|
11
|
+
"flex shrink-0 overflow-hidden items-center justify-center bg-primary-base !text-typography-invert-text object-cover";
|
|
12
|
+
|
|
13
|
+
var AVATAR_SIZE = {
|
|
14
|
+
custom: "size-full",
|
|
15
|
+
large: "size-40 text-reg-14",
|
|
16
|
+
default: "size-32 text-reg-12",
|
|
17
|
+
small: "size-20 text-reg-12",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
var AVATAR_SHAPE = {
|
|
21
|
+
circle: "rounded-128",
|
|
22
|
+
square: "rounded-4",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
var GROUP_SPACING = {
|
|
26
|
+
custom: "-space-x-12",
|
|
27
|
+
large: "-space-x-12",
|
|
28
|
+
default: "-space-x-4",
|
|
29
|
+
small: "-space-x-8",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
var VIVID_COLORS = [
|
|
33
|
+
{ backgroundColor: "#f8cfcd", textColor: "#550b15" },
|
|
34
|
+
{ backgroundColor: "#fde09b", textColor: "#5f1a05" },
|
|
35
|
+
{ backgroundColor: "#a6eb84", textColor: "#004433" },
|
|
36
|
+
{ backgroundColor: "#c7dcfc", textColor: "#0c2c5b" },
|
|
37
|
+
{ backgroundColor: "#a5dbe2", textColor: "#002a2e" },
|
|
38
|
+
{ backgroundColor: "#82ffd3", textColor: "#00916b" },
|
|
39
|
+
{ backgroundColor: "#8eeee2", textColor: "#016f66" },
|
|
40
|
+
{ backgroundColor: "#f8d5d8", textColor: "#5c0d21" },
|
|
41
|
+
{ backgroundColor: "#fef7be", textColor: "#4a4410" },
|
|
42
|
+
{ backgroundColor: "#f1d4fc", textColor: "#4c0e5f" },
|
|
43
|
+
{ backgroundColor: "#f8d4eb", textColor: "#570e45" },
|
|
44
|
+
{ backgroundColor: "#d9d8fc", textColor: "#2b0c7b" },
|
|
45
|
+
{ backgroundColor: "#d8dcd2", textColor: "#233400" },
|
|
46
|
+
{ backgroundColor: "#dbd6e1", textColor: "#371c51" },
|
|
47
|
+
{ backgroundColor: "#bcfb94", textColor: "#213b0d" },
|
|
48
|
+
{ backgroundColor: "#d4dcfc", textColor: "#190d8a" },
|
|
49
|
+
{ backgroundColor: "#ffd2b1", textColor: "#4a2400" },
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
function join() {
|
|
53
|
+
return Array.prototype.filter.call(arguments, Boolean).join(" ");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Map a string to a deterministic background and text color pair.
|
|
58
|
+
* @param {string} str - e.g. user name
|
|
59
|
+
* @returns {{ backgroundColor: string, textColor: string }}
|
|
60
|
+
*/
|
|
61
|
+
function stringToColor(str) {
|
|
62
|
+
if (!str || typeof str !== "string") {
|
|
63
|
+
return { backgroundColor: "#f4f6f6", textColor: "#292929" };
|
|
64
|
+
}
|
|
65
|
+
var s = str.toLowerCase();
|
|
66
|
+
var hash = 0;
|
|
67
|
+
for (var i = 0; i < s.length; i += 1) {
|
|
68
|
+
hash = s.charCodeAt(i) + ((hash << 5) - hash);
|
|
69
|
+
}
|
|
70
|
+
var idx = Math.abs(hash % VIVID_COLORS.length);
|
|
71
|
+
return VIVID_COLORS[idx];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Create a single Avatar element (no vivid colors).
|
|
76
|
+
* @param {Object} config
|
|
77
|
+
* @param {string} [config.name] - Used for fallback initial (first letter)
|
|
78
|
+
* @param {string} [config.image] - Image URL
|
|
79
|
+
* @param {'small'|'default'|'large'|'custom'} [config.size]
|
|
80
|
+
* @param {'circle'|'square'} [config.shape]
|
|
81
|
+
* @param {string} [config.className]
|
|
82
|
+
* @returns {HTMLElement}
|
|
83
|
+
*/
|
|
84
|
+
function create(config) {
|
|
85
|
+
var size = (config && config.size) || "default";
|
|
86
|
+
var shape = (config && config.shape) || "circle";
|
|
87
|
+
var name = (config && config.name) || "";
|
|
88
|
+
var image = config && config.image;
|
|
89
|
+
var className = (config && config.className) || "";
|
|
90
|
+
|
|
91
|
+
var root = document.createElement("div");
|
|
92
|
+
root.className = join(AVATAR_BASE, AVATAR_SIZE[size], AVATAR_SHAPE[shape], className);
|
|
93
|
+
|
|
94
|
+
var fallback = document.createElement("span");
|
|
95
|
+
fallback.className = "flex h-full w-full items-center justify-center capitalize";
|
|
96
|
+
fallback.textContent = name ? name.charAt(0).toUpperCase() : "?";
|
|
97
|
+
root.appendChild(fallback);
|
|
98
|
+
|
|
99
|
+
if (image) {
|
|
100
|
+
var img = document.createElement("img");
|
|
101
|
+
img.src = image;
|
|
102
|
+
img.alt = name || "Avatar";
|
|
103
|
+
img.className = "h-full w-full object-cover";
|
|
104
|
+
img.setAttribute("referrerpolicy", "no-referrer");
|
|
105
|
+
img.onerror = function () {
|
|
106
|
+
img.style.display = "none";
|
|
107
|
+
fallback.style.display = "flex";
|
|
108
|
+
};
|
|
109
|
+
img.onload = function () {
|
|
110
|
+
fallback.style.display = "none";
|
|
111
|
+
};
|
|
112
|
+
fallback.style.display = "none";
|
|
113
|
+
root.appendChild(img);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return root;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Create a VividAvatar: same as Avatar but background and text color come from stringToColor(name).
|
|
121
|
+
* @param {Object} config - Same as create(), name is used for color and fallback initial
|
|
122
|
+
* @returns {HTMLElement}
|
|
123
|
+
*/
|
|
124
|
+
function createVivid(config) {
|
|
125
|
+
var name = (config && config.name) || "";
|
|
126
|
+
var el = create(config);
|
|
127
|
+
var colors = stringToColor(name);
|
|
128
|
+
el.style.backgroundColor = colors.backgroundColor;
|
|
129
|
+
var fallbackSpan = el.querySelector("span");
|
|
130
|
+
if (fallbackSpan) fallbackSpan.style.color = colors.textColor;
|
|
131
|
+
return el;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Create an AvatarGroup (overlapping avatars, max 3 shown + remainder count).
|
|
136
|
+
* @param {Object} config
|
|
137
|
+
* @param {{ id: number, name: string, image?: string }[]} config.users
|
|
138
|
+
* @param {'small'|'default'|'large'|'custom'} [config.size]
|
|
139
|
+
* @param {string} [config.className]
|
|
140
|
+
* @returns {HTMLElement}
|
|
141
|
+
*/
|
|
142
|
+
function createGroup(config) {
|
|
143
|
+
var users = (config && config.users) || [];
|
|
144
|
+
var size = (config && config.size) || "default";
|
|
145
|
+
var className = (config && config.className) || "";
|
|
146
|
+
var vivid = !!(config && config.vivid);
|
|
147
|
+
|
|
148
|
+
var wrap = document.createElement("div");
|
|
149
|
+
wrap.className = join("flex", GROUP_SPACING[size], className);
|
|
150
|
+
|
|
151
|
+
var displayUsers = users.slice(0, 3);
|
|
152
|
+
var remaining = users.length - 3;
|
|
153
|
+
|
|
154
|
+
displayUsers.forEach(function (user) {
|
|
155
|
+
var avatar = vivid
|
|
156
|
+
? createVivid({ name: user.name, image: user.image, size: size, shape: "circle" })
|
|
157
|
+
: create({ name: user.name, image: user.image, size: size, shape: "circle" });
|
|
158
|
+
wrap.appendChild(avatar);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (remaining > 0) {
|
|
162
|
+
var extra = create({
|
|
163
|
+
name: "",
|
|
164
|
+
size: size,
|
|
165
|
+
shape: "circle",
|
|
166
|
+
});
|
|
167
|
+
var fallback = extra.querySelector("span");
|
|
168
|
+
if (fallback) {
|
|
169
|
+
fallback.textContent = "+" + remaining;
|
|
170
|
+
}
|
|
171
|
+
wrap.appendChild(extra);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return wrap;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Create a VividAvatarGroup (each avatar uses name-based color).
|
|
179
|
+
* @param {Object} config - Same as createGroup()
|
|
180
|
+
* @returns {HTMLElement}
|
|
181
|
+
*/
|
|
182
|
+
function createVividGroup(config) {
|
|
183
|
+
return createGroup(Object.assign({}, config, { vivid: true }));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
global.Avatar = {
|
|
187
|
+
stringToColor: stringToColor,
|
|
188
|
+
create: create,
|
|
189
|
+
createVivid: createVivid,
|
|
190
|
+
createGroup: createGroup,
|
|
191
|
+
createVividGroup: createVividGroup,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
console.log("[Avatar] Module loaded successfully");
|
|
195
|
+
})(typeof window !== "undefined" ? window : this);
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Badge Component (vanilla JS)
|
|
3
|
+
* Mirrors React Badge with cva-style variants: variant, color, size, startIcon, endIcon, icon.
|
|
4
|
+
* Use border-border-primary (flow convention) instead of border-borderColor-border-primary.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
(function (global) {
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
var BADGE_BASE =
|
|
11
|
+
"inline-flex items-center gap-4 rounded-4 border-1/2 !text-med-12 transition-colors focus:outline-none";
|
|
12
|
+
|
|
13
|
+
var BADGE_COLOR = {
|
|
14
|
+
default: "bg-fill-tertiary-fill-light-gray text-typography-primary-text hover:text-typography-secondary-text border-border-primary ",
|
|
15
|
+
info: "text-info-text-base bg-info-surface-hover border-info-border-base",
|
|
16
|
+
warning: "text-warning-text-base bg-warning-surface-hover border-warning-border-base",
|
|
17
|
+
error: "text-error-text-base bg-error-surface-hover border-error-border-base",
|
|
18
|
+
success: "text-success-text-base bg-success-surface-hover border-success-border-base",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
var BADGE_SIZE = {
|
|
22
|
+
small: "px-8 py-2",
|
|
23
|
+
medium: "px-8 py-6",
|
|
24
|
+
large: "px-8 py-6",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
var ICON_BADGE_BASE =
|
|
28
|
+
"flex items-center justify-center p-4 text-typography-primary-text bg-fill-default-fill-gray rounded-4 border-1/2 border-border-primary !text-med-12 transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2";
|
|
29
|
+
|
|
30
|
+
var ICON_BADGE_VARIANT = {
|
|
31
|
+
default: "bg-fill-default-fill-gray",
|
|
32
|
+
primary: "border-none",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
var ICON_BADGE_COLOR = {
|
|
36
|
+
default: "bg-fill-default-fill-gray",
|
|
37
|
+
info: "text-info-text-base bg-info-surface-hover border-info-surface-hover",
|
|
38
|
+
warning: "text-warning-text-base bg-warning-surface-hover border-warning-surface-hover",
|
|
39
|
+
error: "text-error-text-base bg-error-surface-hover border-error-surface-hover",
|
|
40
|
+
success: "text-success-text-base bg-success-surface-hover border-success-surface-hover",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
var ICON_WRAPPER_CLASS = "flex size-16 items-center justify-center";
|
|
44
|
+
|
|
45
|
+
function join() {
|
|
46
|
+
return Array.prototype.filter.call(arguments, Boolean).join(" ");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Create a Badge element
|
|
51
|
+
* @param {Object} config
|
|
52
|
+
* @param {'default'|'primary'} [config.variant]
|
|
53
|
+
* @param {'default'|'info'|'warning'|'error'|'success'} [config.color]
|
|
54
|
+
* @param {'small'|'medium'|'large'} [config.size] - default 'large' (React default)
|
|
55
|
+
* @param {string|HTMLElement} [config.startIcon] - HTML string or element before content
|
|
56
|
+
* @param {string|HTMLElement} [config.endIcon] - HTML string or element after content
|
|
57
|
+
* @param {string|HTMLElement} [config.icon] - if set, render icon-only badge (iconBadgeVariants)
|
|
58
|
+
* @param {string|HTMLElement} [config.content] - main content (text or node); use for non–icon-only badges
|
|
59
|
+
* @param {string} [config.className]
|
|
60
|
+
* @returns {HTMLElement}
|
|
61
|
+
*/
|
|
62
|
+
function create(config) {
|
|
63
|
+
var variant = config.variant || "default";
|
|
64
|
+
var color = config.color || "default";
|
|
65
|
+
var size = config.size || "large";
|
|
66
|
+
var className = config.className || "";
|
|
67
|
+
|
|
68
|
+
if (config.icon) {
|
|
69
|
+
var iconBadge = document.createElement("div");
|
|
70
|
+
iconBadge.className = join(
|
|
71
|
+
ICON_BADGE_BASE,
|
|
72
|
+
ICON_BADGE_VARIANT[variant] || ICON_BADGE_VARIANT.default,
|
|
73
|
+
ICON_BADGE_COLOR[color] || ICON_BADGE_COLOR.default,
|
|
74
|
+
className
|
|
75
|
+
);
|
|
76
|
+
var iconContent = config.icon;
|
|
77
|
+
if (typeof iconContent === "string") {
|
|
78
|
+
iconBadge.innerHTML = iconContent;
|
|
79
|
+
} else if (iconContent && iconContent.nodeType === 1) {
|
|
80
|
+
iconBadge.appendChild(iconContent);
|
|
81
|
+
}
|
|
82
|
+
return iconBadge;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
var badge = document.createElement("div");
|
|
86
|
+
badge.className = join(
|
|
87
|
+
BADGE_BASE,
|
|
88
|
+
BADGE_COLOR[color] || BADGE_COLOR.default,
|
|
89
|
+
BADGE_SIZE[size] || BADGE_SIZE.large,
|
|
90
|
+
className
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
if (config.startIcon) {
|
|
94
|
+
var startWrap = document.createElement("span");
|
|
95
|
+
startWrap.className = ICON_WRAPPER_CLASS;
|
|
96
|
+
if (typeof config.startIcon === "string") {
|
|
97
|
+
startWrap.innerHTML = config.startIcon;
|
|
98
|
+
} else if (config.startIcon && config.startIcon.nodeType === 1) {
|
|
99
|
+
startWrap.appendChild(config.startIcon);
|
|
100
|
+
}
|
|
101
|
+
badge.appendChild(startWrap);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (config.content !== undefined && config.content !== null) {
|
|
105
|
+
var content = config.content;
|
|
106
|
+
if (typeof content === "string") {
|
|
107
|
+
badge.appendChild(document.createTextNode(content));
|
|
108
|
+
} else if (content && content.nodeType === 1) {
|
|
109
|
+
badge.appendChild(content);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (config.endIcon) {
|
|
114
|
+
var endWrap = document.createElement("span");
|
|
115
|
+
endWrap.className =
|
|
116
|
+
typeof config.endIcon === "string"
|
|
117
|
+
? ICON_WRAPPER_CLASS
|
|
118
|
+
: "flex items-center justify-center shrink-0";
|
|
119
|
+
if (typeof config.endIcon === "string") {
|
|
120
|
+
endWrap.innerHTML = config.endIcon;
|
|
121
|
+
} else if (config.endIcon && config.endIcon.nodeType === 1) {
|
|
122
|
+
endWrap.appendChild(config.endIcon);
|
|
123
|
+
}
|
|
124
|
+
badge.appendChild(endWrap);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return badge;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
global.Badge = {
|
|
131
|
+
create: create,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
console.log("[Badge] Module loaded successfully");
|
|
135
|
+
})(typeof window !== "undefined" ? window : this);
|