@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,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table Component (vanilla JS, Tailwind)
|
|
3
|
+
* A flexible table component for rendering tabular data with support for
|
|
4
|
+
* custom rendering and infinite scroll.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
(function (global) {
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create a table component
|
|
12
|
+
* @param {Object} options - Configuration options
|
|
13
|
+
* @param {Array} options.data - Array of data objects to display
|
|
14
|
+
* @param {Array} options.columns - Column definitions
|
|
15
|
+
* @param {boolean} options.showHeader - Show table header (default: true)
|
|
16
|
+
* @param {string} options.headerSize - Header size: 'small', 'default', 'large'
|
|
17
|
+
* @param {boolean} options.hasBorder - Show borders (default: true)
|
|
18
|
+
* @param {Function} options.onRowClick - Callback when row is clicked
|
|
19
|
+
* @param {Function} options.onFetch - Callback for infinite scroll
|
|
20
|
+
* @param {boolean} options.hasMore - Whether more data is available
|
|
21
|
+
* @param {boolean} options.isLoading - Loading state
|
|
22
|
+
* @param {string} options.emptyMessage - Message to show when no data
|
|
23
|
+
* @returns {Object} Table instance with methods
|
|
24
|
+
*/
|
|
25
|
+
function createTable(options = {}) {
|
|
26
|
+
const {
|
|
27
|
+
data = [],
|
|
28
|
+
columns = [],
|
|
29
|
+
showHeader = true,
|
|
30
|
+
headerSize = "small",
|
|
31
|
+
hasBorder = true,
|
|
32
|
+
onRowClick = null,
|
|
33
|
+
onFetch = null,
|
|
34
|
+
hasMore = false,
|
|
35
|
+
isLoading = false,
|
|
36
|
+
emptyMessage = "No data available",
|
|
37
|
+
} = options;
|
|
38
|
+
|
|
39
|
+
let currentData = [...data];
|
|
40
|
+
let container = null;
|
|
41
|
+
let tableElement = null;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get header size classes
|
|
45
|
+
*/
|
|
46
|
+
function getHeaderSizeClasses(size) {
|
|
47
|
+
const sizes = {
|
|
48
|
+
small: "h-32",
|
|
49
|
+
default: "h-44",
|
|
50
|
+
large: "h-48",
|
|
51
|
+
};
|
|
52
|
+
return sizes[size] || sizes.small;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get cell size classes
|
|
57
|
+
*/
|
|
58
|
+
function getCellSizeClasses(size) {
|
|
59
|
+
const sizes = {
|
|
60
|
+
small: "h-36",
|
|
61
|
+
default: "h-44",
|
|
62
|
+
large: "h-48",
|
|
63
|
+
};
|
|
64
|
+
return sizes[size] || sizes.default;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Handle row click
|
|
69
|
+
*/
|
|
70
|
+
function handleRowClick(rowId, event) {
|
|
71
|
+
if (onRowClick) {
|
|
72
|
+
onRowClick(rowId);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Create table header
|
|
78
|
+
*/
|
|
79
|
+
function createTableHeader() {
|
|
80
|
+
const thead = document.createElement("thead");
|
|
81
|
+
thead.className = "[&_tr]:border-0 text-typography-quaternary-text";
|
|
82
|
+
|
|
83
|
+
const tr = document.createElement("tr");
|
|
84
|
+
|
|
85
|
+
// Data columns
|
|
86
|
+
columns.forEach((column, index) => {
|
|
87
|
+
const th = document.createElement("th");
|
|
88
|
+
const isLast = index === columns.length - 1;
|
|
89
|
+
|
|
90
|
+
// Base classes from design system
|
|
91
|
+
const borderClass = hasBorder
|
|
92
|
+
? "border-1/2 border-borderColor-border-secondary"
|
|
93
|
+
: "border-b-1/2 border-b-borderColor-border-secondary";
|
|
94
|
+
|
|
95
|
+
th.className = `min-w-[150px] max-w-[500px] text-nowrap bg-fill-tertiary-fill-light-gray px-12 py-8 text-left align-middle !text-semi-10 uppercase leading-[12px] tracking-[0.5px] text-typography-tertiary-text hover:bg-fill-tertiary-fill-light-gray ${borderClass} ${getHeaderSizeClasses(
|
|
96
|
+
headerSize
|
|
97
|
+
)} ${isLast ? "w-full" : ""}`;
|
|
98
|
+
|
|
99
|
+
if (column.width) {
|
|
100
|
+
th.style.width = column.width;
|
|
101
|
+
th.style.minWidth = column.width;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const headerText = document.createElement("span");
|
|
105
|
+
headerText.textContent = column.header || "";
|
|
106
|
+
|
|
107
|
+
th.appendChild(headerText);
|
|
108
|
+
tr.appendChild(th);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
thead.appendChild(tr);
|
|
112
|
+
return thead;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Create table body
|
|
117
|
+
*/
|
|
118
|
+
function createTableBody() {
|
|
119
|
+
const tbody = document.createElement("tbody");
|
|
120
|
+
tbody.className = "[&_tr:last-child]:border-0";
|
|
121
|
+
|
|
122
|
+
if (currentData.length === 0 && !isLoading) {
|
|
123
|
+
// Empty state
|
|
124
|
+
const tr = document.createElement("tr");
|
|
125
|
+
const td = document.createElement("td");
|
|
126
|
+
td.colSpan = columns.length;
|
|
127
|
+
td.className = "text-center py-12 text-typography-secondary-text";
|
|
128
|
+
td.textContent = emptyMessage;
|
|
129
|
+
tr.appendChild(td);
|
|
130
|
+
tbody.appendChild(tr);
|
|
131
|
+
return tbody;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Data rows
|
|
135
|
+
currentData.forEach((row) => {
|
|
136
|
+
const tr = document.createElement("tr");
|
|
137
|
+
tr.className = "transition-colors hover:bg-fill-tertiary-fill-light-gray";
|
|
138
|
+
if (onRowClick) {
|
|
139
|
+
tr.className += " cursor-pointer";
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
tr.addEventListener("click", (e) => handleRowClick(row.id, e));
|
|
143
|
+
|
|
144
|
+
// Data columns
|
|
145
|
+
columns.forEach((column, index) => {
|
|
146
|
+
const td = document.createElement("td");
|
|
147
|
+
const isLast = index === columns.length - 1;
|
|
148
|
+
|
|
149
|
+
// Base classes from design system
|
|
150
|
+
const borderClass = hasBorder
|
|
151
|
+
? "border-1/2 border-borderColor-border-secondary"
|
|
152
|
+
: "border-b-1/2 border-b-borderColor-border-secondary";
|
|
153
|
+
|
|
154
|
+
td.className = `box-border w-fit min-w-[150px] max-w-[500px] text-ellipsis text-nowrap bg-fill-quarternary-fill-white px-12 py-8 align-middle !text-reg-12 text-typography-primary-text overflow-hidden truncate ${borderClass} ${getCellSizeClasses(
|
|
155
|
+
"default"
|
|
156
|
+
)} ${isLast ? "w-full" : ""}`;
|
|
157
|
+
|
|
158
|
+
if (column.width) {
|
|
159
|
+
td.style.width = column.width;
|
|
160
|
+
td.style.minWidth = column.width;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Render cell content
|
|
164
|
+
if (column.cell && typeof column.cell === "function") {
|
|
165
|
+
const cellContent = column.cell(row);
|
|
166
|
+
if (typeof cellContent === "string") {
|
|
167
|
+
td.innerHTML = cellContent;
|
|
168
|
+
} else if (cellContent instanceof HTMLElement) {
|
|
169
|
+
td.appendChild(cellContent);
|
|
170
|
+
} else {
|
|
171
|
+
td.textContent = cellContent;
|
|
172
|
+
}
|
|
173
|
+
} else if (column.accessor) {
|
|
174
|
+
const value = row[column.accessor];
|
|
175
|
+
td.textContent = value !== null && value !== undefined ? value : "";
|
|
176
|
+
} else {
|
|
177
|
+
td.textContent = "";
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
tr.appendChild(td);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
tbody.appendChild(tr);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Loading skeleton rows
|
|
187
|
+
if (isLoading) {
|
|
188
|
+
for (let i = 0; i < 3; i++) {
|
|
189
|
+
const tr = document.createElement("tr");
|
|
190
|
+
tr.className = "animate-pulse";
|
|
191
|
+
|
|
192
|
+
columns.forEach((column, index) => {
|
|
193
|
+
const td = document.createElement("td");
|
|
194
|
+
const isLast = index === columns.length - 1;
|
|
195
|
+
|
|
196
|
+
const borderClass = hasBorder
|
|
197
|
+
? "border-1/2 border-borderColor-border-secondary"
|
|
198
|
+
: "border-b-1/2 border-b-borderColor-border-secondary";
|
|
199
|
+
|
|
200
|
+
td.className = `px-12 py-8 ${borderClass} ${getCellSizeClasses("default")} ${
|
|
201
|
+
isLast ? "w-full" : ""
|
|
202
|
+
}`;
|
|
203
|
+
|
|
204
|
+
if (column.width) {
|
|
205
|
+
td.style.width = column.width;
|
|
206
|
+
td.style.minWidth = column.width;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const skeleton = document.createElement("div");
|
|
210
|
+
skeleton.className = "h-4 bg-fill-secondary-fill-lighter-gray rounded";
|
|
211
|
+
td.appendChild(skeleton);
|
|
212
|
+
tr.appendChild(td);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
tbody.appendChild(tr);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return tbody;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Handle scroll for infinite loading
|
|
224
|
+
*/
|
|
225
|
+
function handleScroll(e) {
|
|
226
|
+
if (!onFetch || isLoading || !hasMore) return;
|
|
227
|
+
|
|
228
|
+
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
|
229
|
+
|
|
230
|
+
// Check if near bottom (within 1.5x viewport height)
|
|
231
|
+
if (scrollHeight - scrollTop <= clientHeight * 1.5) {
|
|
232
|
+
onFetch();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Render the table
|
|
238
|
+
*/
|
|
239
|
+
function render() {
|
|
240
|
+
if (!container) return;
|
|
241
|
+
|
|
242
|
+
// Clear container
|
|
243
|
+
container.innerHTML = "";
|
|
244
|
+
|
|
245
|
+
// Create table wrapper
|
|
246
|
+
const wrapper = document.createElement("div");
|
|
247
|
+
wrapper.className = "relative h-full max-w-full overflow-y-auto";
|
|
248
|
+
wrapper.addEventListener("scroll", handleScroll);
|
|
249
|
+
|
|
250
|
+
// Create table
|
|
251
|
+
tableElement = document.createElement("table");
|
|
252
|
+
const tableBorderClass = hasBorder
|
|
253
|
+
? "border-1/2 border-borderColor-border-secondary"
|
|
254
|
+
: "";
|
|
255
|
+
tableElement.className = `w-full caption-bottom bg-fill-quarternary-fill-white text-reg-14 ${tableBorderClass}`;
|
|
256
|
+
|
|
257
|
+
// Add header
|
|
258
|
+
if (showHeader) {
|
|
259
|
+
tableElement.appendChild(createTableHeader());
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Add body
|
|
263
|
+
tableElement.appendChild(createTableBody());
|
|
264
|
+
|
|
265
|
+
wrapper.appendChild(tableElement);
|
|
266
|
+
container.appendChild(wrapper);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Public API
|
|
271
|
+
*/
|
|
272
|
+
return {
|
|
273
|
+
/**
|
|
274
|
+
* Mount the table to a DOM element
|
|
275
|
+
*/
|
|
276
|
+
mount(element) {
|
|
277
|
+
if (typeof element === "string") {
|
|
278
|
+
container = document.querySelector(element);
|
|
279
|
+
} else {
|
|
280
|
+
container = element;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (!container) {
|
|
284
|
+
console.error("Table: Invalid container element");
|
|
285
|
+
return this;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
render();
|
|
289
|
+
return this;
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Update table data
|
|
294
|
+
*/
|
|
295
|
+
setData(newData) {
|
|
296
|
+
currentData = [...newData];
|
|
297
|
+
render();
|
|
298
|
+
return this;
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Get current data
|
|
303
|
+
*/
|
|
304
|
+
getData() {
|
|
305
|
+
return [...currentData];
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Destroy the table
|
|
310
|
+
*/
|
|
311
|
+
destroy() {
|
|
312
|
+
if (container) {
|
|
313
|
+
container.innerHTML = "";
|
|
314
|
+
}
|
|
315
|
+
container = null;
|
|
316
|
+
tableElement = null;
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Refresh/re-render the table
|
|
321
|
+
*/
|
|
322
|
+
refresh() {
|
|
323
|
+
render();
|
|
324
|
+
return this;
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Export to global scope
|
|
330
|
+
if (typeof module !== "undefined" && module.exports) {
|
|
331
|
+
module.exports = { createTable };
|
|
332
|
+
} else {
|
|
333
|
+
global.SuperleapTable = { createTable };
|
|
334
|
+
}
|
|
335
|
+
})(typeof window !== "undefined" ? window : this);
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Textarea Component (vanilla JS)
|
|
3
|
+
* Design-system textarea with variants; matches React Textarea cva variants.
|
|
4
|
+
* Ref: React Textarea with cva variants; no React/cva dependency.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
(function (global) {
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
var TEXTAREA_CLASS = {
|
|
11
|
+
base:
|
|
12
|
+
"flex w-full rounded-4 border-1/2 border-border-primary bg-fill-quarternary-fill-white px-12 py-6 !text-reg-13 outline-none placeholder:text-typography-quaternary-text text-typography-primary-text resize-y transition-all ease-in-out",
|
|
13
|
+
default:
|
|
14
|
+
"min-h-[80px] hover:border-primary-base focus:border-primary-base",
|
|
15
|
+
borderless:
|
|
16
|
+
"min-h-[80px] border-none shadow-none rounded-0 bg-fill-quarternary-fill-white",
|
|
17
|
+
inline:
|
|
18
|
+
"border-transparent shadow-none rounded-0 bg-fill-quarternary-fill-white hover:bg-fill-tertiary-fill-light-gray focus:border-transparent focus:bg-fill-tertiary-fill-light-gray py-4",
|
|
19
|
+
error:
|
|
20
|
+
"min-h-[80px] border-error-base hover:border-error-base focus:border-error-base",
|
|
21
|
+
warning:
|
|
22
|
+
"min-h-[80px] border-warning-base hover:border-warning-base focus:border-warning-base",
|
|
23
|
+
disabled:
|
|
24
|
+
"cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function join() {
|
|
28
|
+
return Array.prototype.filter.call(arguments, Boolean).join(" ");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create a textarea component
|
|
33
|
+
* @param {Object} config
|
|
34
|
+
* @param {string} [config.variant] - 'default' | 'borderless' | 'inline' | 'error' | 'warning'
|
|
35
|
+
* @param {string} [config.placeholder]
|
|
36
|
+
* @param {string} [config.value]
|
|
37
|
+
* @param {number} [config.rows]
|
|
38
|
+
* @param {boolean} [config.disabled]
|
|
39
|
+
* @param {boolean} [config.readOnly]
|
|
40
|
+
* @param {string} [config.name]
|
|
41
|
+
* @param {string} [config.id]
|
|
42
|
+
* @param {string} [config.className] - extra class on textarea
|
|
43
|
+
* @param {number} [config.maxLength]
|
|
44
|
+
* @param {Function} [config.onChange]
|
|
45
|
+
* @param {Function} [config.onInput]
|
|
46
|
+
* @param {Function} [config.onFocus]
|
|
47
|
+
* @param {Function} [config.onBlur]
|
|
48
|
+
* @returns {HTMLTextAreaElement} textarea element (with getValue/setValue/setVariant/setDisabled attached)
|
|
49
|
+
*/
|
|
50
|
+
function create(config) {
|
|
51
|
+
var variant = config.variant || "default";
|
|
52
|
+
var disabled = !!config.disabled;
|
|
53
|
+
|
|
54
|
+
var textarea = document.createElement("textarea");
|
|
55
|
+
textarea.autocomplete = "off";
|
|
56
|
+
if (config.placeholder != null) textarea.placeholder = config.placeholder;
|
|
57
|
+
if (config.value != null) textarea.value = config.value;
|
|
58
|
+
if (config.name) textarea.name = config.name;
|
|
59
|
+
if (config.id) textarea.id = config.id;
|
|
60
|
+
if (config.rows != null) textarea.rows = config.rows;
|
|
61
|
+
if (config.maxLength != null) textarea.maxLength = config.maxLength;
|
|
62
|
+
textarea.disabled = disabled;
|
|
63
|
+
textarea.readOnly = !!config.readOnly;
|
|
64
|
+
|
|
65
|
+
textarea.className = join(
|
|
66
|
+
TEXTAREA_CLASS.base,
|
|
67
|
+
TEXTAREA_CLASS[variant] || TEXTAREA_CLASS.default,
|
|
68
|
+
disabled ? TEXTAREA_CLASS.disabled : "",
|
|
69
|
+
config.className || ""
|
|
70
|
+
);
|
|
71
|
+
textarea.setAttribute("data-textarea-variant", variant);
|
|
72
|
+
|
|
73
|
+
if (config.onChange) textarea.addEventListener("change", config.onChange);
|
|
74
|
+
if (config.onInput) textarea.addEventListener("input", config.onInput);
|
|
75
|
+
if (config.onFocus) textarea.addEventListener("focus", config.onFocus);
|
|
76
|
+
if (config.onBlur) textarea.addEventListener("blur", config.onBlur);
|
|
77
|
+
|
|
78
|
+
textarea.getInput = function () {
|
|
79
|
+
return textarea;
|
|
80
|
+
};
|
|
81
|
+
textarea.setValue = function (v) {
|
|
82
|
+
textarea.value = v;
|
|
83
|
+
};
|
|
84
|
+
textarea.getValue = function () {
|
|
85
|
+
return textarea.value;
|
|
86
|
+
};
|
|
87
|
+
textarea.setVariant = function (v) {
|
|
88
|
+
variant = v;
|
|
89
|
+
textarea.setAttribute("data-textarea-variant", v);
|
|
90
|
+
textarea.className = join(
|
|
91
|
+
TEXTAREA_CLASS.base,
|
|
92
|
+
TEXTAREA_CLASS[variant] || TEXTAREA_CLASS.default,
|
|
93
|
+
disabled ? TEXTAREA_CLASS.disabled : "",
|
|
94
|
+
config.className || ""
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
textarea.setDisabled = function (d) {
|
|
98
|
+
disabled = !!d;
|
|
99
|
+
textarea.disabled = disabled;
|
|
100
|
+
textarea.className = join(
|
|
101
|
+
TEXTAREA_CLASS.base,
|
|
102
|
+
TEXTAREA_CLASS[variant] || TEXTAREA_CLASS.default,
|
|
103
|
+
disabled ? TEXTAREA_CLASS.disabled : "",
|
|
104
|
+
config.className || ""
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
return textarea;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
global.TextareaComponent = {
|
|
112
|
+
create: create,
|
|
113
|
+
};
|
|
114
|
+
})(typeof window !== "undefined" ? window : this);
|