@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.
Files changed (40) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/LICENSE +21 -0
  3. package/README.md +451 -0
  4. package/components/alert.js +282 -0
  5. package/components/avatar.js +195 -0
  6. package/components/badge.js +135 -0
  7. package/components/button.js +201 -0
  8. package/components/checkbox.js +254 -0
  9. package/components/currency.js +227 -0
  10. package/components/date-time-picker/date-time-picker-utils.js +253 -0
  11. package/components/date-time-picker/date-time-picker.js +532 -0
  12. package/components/duration/duration-constants.js +46 -0
  13. package/components/duration/duration-utils.js +164 -0
  14. package/components/duration/duration.js +448 -0
  15. package/components/enum-multiselect.js +869 -0
  16. package/components/enum-select.js +831 -0
  17. package/components/enumeration.js +213 -0
  18. package/components/file-input.js +533 -0
  19. package/components/icon.js +200 -0
  20. package/components/input.js +259 -0
  21. package/components/label.js +111 -0
  22. package/components/multiselect.js +351 -0
  23. package/components/phone-input/phone-input.js +392 -0
  24. package/components/phone-input/phone-utils.js +157 -0
  25. package/components/popover.js +240 -0
  26. package/components/radio-group.js +435 -0
  27. package/components/record-multiselect.js +956 -0
  28. package/components/record-select.js +930 -0
  29. package/components/select.js +544 -0
  30. package/components/spinner.js +136 -0
  31. package/components/table.js +335 -0
  32. package/components/textarea.js +114 -0
  33. package/components/time-picker.js +357 -0
  34. package/components/toast.js +343 -0
  35. package/core/flow.js +1729 -0
  36. package/core/superleapClient.js +146 -0
  37. package/dist/output.css +2 -0
  38. package/index.d.ts +458 -0
  39. package/index.js +253 -0
  40. package/package.json +70 -0
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Duration utils: parse, format, convert between seconds and time units.
3
+ * formatType: 'seconds' | 'milliseconds' (value stored as number in that unit).
4
+ */
5
+
6
+ (function (global) {
7
+ "use strict";
8
+
9
+ var SECONDS_PER_DAY = 86400;
10
+ var SECONDS_PER_HOUR = 3600;
11
+ var SECONDS_PER_MINUTE = 60;
12
+ var SECONDS_PER_MILLISECOND = 0.001;
13
+
14
+ /**
15
+ * @param {number} seconds - total seconds (can be fractional)
16
+ * @returns {{ days: number, hours: number, minutes: number, seconds: number, milliseconds: number }}
17
+ */
18
+ function convertSecondsToTimeUnits(seconds) {
19
+ var total = Math.max(0, seconds);
20
+ var days = Math.floor(total / SECONDS_PER_DAY);
21
+ total -= days * SECONDS_PER_DAY;
22
+ var hours = Math.floor(total / SECONDS_PER_HOUR);
23
+ total -= hours * SECONDS_PER_HOUR;
24
+ var minutes = Math.floor(total / SECONDS_PER_MINUTE);
25
+ total -= minutes * SECONDS_PER_MINUTE;
26
+ var secs = Math.floor(total);
27
+ var ms = Math.round((total - secs) * 1000);
28
+ return { days: days, hours: hours, minutes: minutes, seconds: secs, milliseconds: ms };
29
+ }
30
+
31
+ /**
32
+ * @param {number} val - stored value (seconds or milliseconds)
33
+ * @param {string} formatType - 'seconds' | 'milliseconds'
34
+ * @returns {number} seconds
35
+ */
36
+ function getSecondsFromValue(val, formatType) {
37
+ if (formatType === "milliseconds") {
38
+ return (val || 0) / 1000;
39
+ }
40
+ return val || 0;
41
+ }
42
+
43
+ /**
44
+ * @param {number} seconds
45
+ * @param {string} formatType - 'seconds' | 'milliseconds'
46
+ * @returns {number}
47
+ */
48
+ function getValueFromSeconds(seconds, formatType) {
49
+ if (formatType === "milliseconds") {
50
+ return Math.round(seconds * 1000);
51
+ }
52
+ return Math.round(seconds);
53
+ }
54
+
55
+ /**
56
+ * Parse duration string: "2d1h15m30s", "4000s", "1:30:00", "90", etc.
57
+ * @param {string} input
58
+ * @param {string} formatType - 'seconds' | 'milliseconds'
59
+ * @returns {number} total seconds (can be fractional)
60
+ */
61
+ function parseDuration(input, formatType) {
62
+ if (!input || typeof input !== "string") return 0;
63
+ var cleaned = input.replace(/\s+/g, "").toLowerCase();
64
+
65
+ if (/^\d+(\.\d+)?$/.test(cleaned)) {
66
+ var num = parseFloat(cleaned);
67
+ return formatType === "milliseconds" ? num / 1000 : num;
68
+ }
69
+
70
+ var totalSeconds = 0;
71
+ var patterns = [
72
+ { regex: /(\d+(?:\.\d+)?)d/g, multiplier: SECONDS_PER_DAY },
73
+ { regex: /(\d+(?:\.\d+)?)h/g, multiplier: SECONDS_PER_HOUR },
74
+ { regex: /(\d+(?:\.\d+)?)m(?!s)/g, multiplier: SECONDS_PER_MINUTE },
75
+ { regex: /(\d+(?:\.\d+)?)s/g, multiplier: 1 },
76
+ { regex: /(\d+(?:\.\d+)?)ms/g, multiplier: SECONDS_PER_MILLISECOND },
77
+ ];
78
+
79
+ patterns.forEach(function (p) {
80
+ var regex = new RegExp(p.regex.source, p.regex.flags || "g");
81
+ var match;
82
+ while ((match = regex.exec(cleaned)) !== null) {
83
+ totalSeconds += parseFloat(match[1]) * p.multiplier;
84
+ }
85
+ });
86
+
87
+ // Time format hh:mm:ss or mm:ss or d:hh:mm:ss
88
+ if (cleaned.indexOf(":") !== -1 && !/[dhms]/.test(cleaned)) {
89
+ var parts = cleaned.split(":").map(function (p) {
90
+ return parseFloat(p) || 0;
91
+ });
92
+ if (parts.length === 2) {
93
+ totalSeconds = parts[0] * SECONDS_PER_MINUTE + parts[1];
94
+ } else if (parts.length === 3) {
95
+ totalSeconds = parts[0] * SECONDS_PER_HOUR + parts[1] * SECONDS_PER_MINUTE + parts[2];
96
+ } else if (parts.length === 4) {
97
+ totalSeconds =
98
+ parts[0] * SECONDS_PER_DAY +
99
+ parts[1] * SECONDS_PER_HOUR +
100
+ parts[2] * SECONDS_PER_MINUTE +
101
+ parts[3];
102
+ }
103
+ }
104
+
105
+ return totalSeconds;
106
+ }
107
+
108
+ /**
109
+ * @param {number} seconds
110
+ * @param {string} [placeholder]
111
+ * @returns {string}
112
+ */
113
+ function formatDuration(seconds, placeholder) {
114
+ if (seconds === 0) return placeholder || "Select duration";
115
+ var u = convertSecondsToTimeUnits(seconds);
116
+ var parts = [];
117
+ if (u.days > 0) parts.push(u.days + "d");
118
+ if (u.hours > 0) parts.push(u.hours + "h");
119
+ if (u.minutes > 0) parts.push(u.minutes + "m");
120
+ if (u.seconds > 0) parts.push(u.seconds + "s");
121
+ if (u.milliseconds > 0 && parts.length === 0) parts.push(u.milliseconds + "ms");
122
+ return parts.join(" ") || "0s";
123
+ }
124
+
125
+ /**
126
+ * @param {{ days: number, hours: number, minutes: number, seconds: number }} values
127
+ * @returns {number}
128
+ */
129
+ function wheelValuesToSeconds(values) {
130
+ return (
131
+ (values.days || 0) * SECONDS_PER_DAY +
132
+ (values.hours || 0) * SECONDS_PER_HOUR +
133
+ (values.minutes || 0) * SECONDS_PER_MINUTE +
134
+ (values.seconds || 0)
135
+ );
136
+ }
137
+
138
+ var MAX_VALUES = { days: 999, hours: 23, minutes: 59, seconds: 59 };
139
+
140
+ /**
141
+ * @param {string} unit - 'days' | 'hours' | 'minutes' | 'seconds'
142
+ * @param {number} value
143
+ * @returns {number}
144
+ */
145
+ function clampWheelValue(unit, value) {
146
+ var max = MAX_VALUES[unit];
147
+ return Math.max(0, Math.min(max, isNaN(value) ? 0 : value));
148
+ }
149
+
150
+ global.DurationUtils = {
151
+ convertSecondsToTimeUnits: convertSecondsToTimeUnits,
152
+ getSecondsFromValue: getSecondsFromValue,
153
+ getValueFromSeconds: getValueFromSeconds,
154
+ parseDuration: parseDuration,
155
+ formatDuration: formatDuration,
156
+ wheelValuesToSeconds: wheelValuesToSeconds,
157
+ clampWheelValue: clampWheelValue,
158
+ MAX_VALUES: MAX_VALUES,
159
+ SECONDS_PER_DAY: SECONDS_PER_DAY,
160
+ SECONDS_PER_HOUR: SECONDS_PER_HOUR,
161
+ SECONDS_PER_MINUTE: SECONDS_PER_MINUTE,
162
+ SECONDS_PER_MILLISECOND: SECONDS_PER_MILLISECOND,
163
+ };
164
+ })(typeof window !== "undefined" ? window : this);
@@ -0,0 +1,448 @@
1
+ /**
2
+ * Duration Component (vanilla JS)
3
+ * Popover duration picker: text input, number wheels (d/h/m/s), presets, Clear/Done.
4
+ * Ref: React DurationInput with formatType (seconds | milliseconds), variants, sizes.
5
+ * Depends: DurationUtils, DurationConstants, Popover, Button, InputComponent.
6
+ */
7
+
8
+ (function (global) {
9
+ "use strict";
10
+
11
+ function getDep(name) {
12
+ if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
13
+ var c = global.FlowUI._getComponent(name);
14
+ if (c) return c;
15
+ }
16
+ return global[name];
17
+ }
18
+
19
+ var ICON_CHEVRON_DOWN =
20
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>';
21
+ var ICON_X =
22
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" 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>';
23
+
24
+ var TRIGGER_CLASS = {
25
+ base:
26
+ "flex items-center justify-between border-1/2 rounded-4 text-typography-primary-text gap-x-8 w-full transition-all ease-in-out cursor-pointer",
27
+ default:
28
+ "border-border-primary bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
29
+ error:
30
+ "border-error-base bg-fill-quarternary-fill-white hover:border-error-base focus-within:border-error-base",
31
+ warning:
32
+ "border-warning-base bg-fill-quarternary-fill-white hover:border-warning-base focus-within:border-warning-base",
33
+ success:
34
+ "border-success-base bg-fill-quarternary-fill-white hover:border-success-base focus-within:border-success-base",
35
+ borderless:
36
+ "border-none shadow-none rounded-0 bg-fill-quarternary-fill-white",
37
+ inline:
38
+ "border-transparent shadow-none rounded-0 bg-fill-quarternary-fill-white hover:bg-fill-tertiary-fill-light-gray focus-within:border-transparent",
39
+ sizeDefault: "px-12 py-6",
40
+ sizeLarge: "px-12 py-8",
41
+ sizeSmall: "px-12 py-4",
42
+ disabled:
43
+ "cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary opacity-60",
44
+ };
45
+
46
+ function join() {
47
+ return Array.prototype.filter.call(arguments, Boolean).join(" ");
48
+ }
49
+
50
+ function createNumberWheel(unit, label, value, maxVal, onChange, onKeyDown) {
51
+ var Utils = global.DurationUtils;
52
+ if (!Utils) throw new Error("DurationUtils required");
53
+ var wrapper = document.createElement("div");
54
+ wrapper.className = "flex flex-col items-center justify-center";
55
+ var inputWrap = document.createElement("div");
56
+ inputWrap.className = "overflow-hidden rounded-4 border-1/2 border-border-primary";
57
+ var input = document.createElement("input");
58
+ input.type = "number";
59
+ input.min = 0;
60
+ input.max = maxVal;
61
+ input.value = Math.max(0, Math.min(maxVal, value));
62
+ input.className = join(
63
+ "w-full bg-inherit text-center outline-none text-reg-13 px-8 py-4 border-0",
64
+ "placeholder:text-typography-quaternary-text focus:bg-fill-tertiary-fill-light-gray",
65
+ unit === "days" ? "!w-48 !min-w-48" : "!w-40 !min-w-40"
66
+ );
67
+ input.setAttribute("placeholder", label);
68
+ inputWrap.appendChild(input);
69
+ wrapper.appendChild(inputWrap);
70
+ var labelEl = document.createElement("span");
71
+ labelEl.className = "mt-4 text-reg-10 text-typography-tertiary-text";
72
+ labelEl.textContent = label;
73
+ wrapper.appendChild(labelEl);
74
+
75
+ input.addEventListener("input", function () {
76
+ var v = parseInt(input.value, 10);
77
+ if (isNaN(v)) v = 0;
78
+ var clamped = Utils.clampWheelValue(unit, v);
79
+ input.value = clamped;
80
+ onChange(clamped);
81
+ });
82
+ input.addEventListener("change", function () {
83
+ var v = parseInt(input.value, 10);
84
+ if (isNaN(v)) v = 0;
85
+ var clamped = Utils.clampWheelValue(unit, v);
86
+ input.value = clamped;
87
+ onChange(clamped);
88
+ });
89
+ if (onKeyDown) {
90
+ input.addEventListener("keydown", onKeyDown);
91
+ }
92
+
93
+ wrapper.getInput = function () {
94
+ return input;
95
+ };
96
+ wrapper.setValue = function (v) {
97
+ var clamped = Utils.clampWheelValue(unit, v);
98
+ input.value = clamped;
99
+ };
100
+ return wrapper;
101
+ }
102
+
103
+ /**
104
+ * Create duration picker component
105
+ * @param {Object} config
106
+ * @param {number|null} [config.value] - Current value (seconds or ms depending on formatType)
107
+ * @param {string} [config.formatType='seconds'] - 'seconds' | 'milliseconds'
108
+ * @param {Function} [config.onChange] - (value: number | null) => void
109
+ * @param {string} [config.placeholder='hh:mm:ss']
110
+ * @param {string} [config.variant='default'] - default | error | warning | success | borderless | inline
111
+ * @param {string} [config.size='default'] - small | default | large
112
+ * @param {boolean} [config.disabled]
113
+ * @param {string} [config.className]
114
+ * @returns {HTMLElement} Container (trigger + popover)
115
+ */
116
+ function create(config) {
117
+ var Utils = global.DurationUtils;
118
+ var Constants = global.DurationConstants;
119
+ var Popover = getDep("Popover");
120
+ var Button = getDep("Button");
121
+ var InputComponent = getDep("InputComponent");
122
+ if (!Utils || !Constants || !Popover || !Button) {
123
+ throw new Error("Duration requires DurationUtils, DurationConstants, Popover, Button");
124
+ }
125
+
126
+ var value = config.value != null ? config.value : null;
127
+ var formatType = config.formatType === "milliseconds" ? "milliseconds" : "seconds";
128
+ var onChange = config.onChange;
129
+ var placeholder = config.placeholder != null ? config.placeholder : "hh:mm:ss";
130
+ var variant = config.variant || "default";
131
+ var size = config.size || "default";
132
+ var disabled = !!config.disabled;
133
+ var className = config.className || "";
134
+
135
+ var currentSeconds = Utils.getSecondsFromValue(value || 0, formatType);
136
+ var displayDuration = Utils.formatDuration(currentSeconds, placeholder);
137
+ var millisecondsDisplay =
138
+ currentSeconds > 0 && formatType === "milliseconds"
139
+ ? Math.round(currentSeconds * 1000) + "ms"
140
+ : null;
141
+
142
+ var wheelValues = Utils.convertSecondsToTimeUnits(currentSeconds);
143
+ var inputValue = "";
144
+
145
+ var triggerWrapper = document.createElement("div");
146
+ triggerWrapper.className = join(
147
+ TRIGGER_CLASS.base,
148
+ TRIGGER_CLASS[variant] != null ? TRIGGER_CLASS[variant] : TRIGGER_CLASS.default,
149
+ size === "large" ? TRIGGER_CLASS.sizeLarge : size === "small" ? TRIGGER_CLASS.sizeSmall : TRIGGER_CLASS.sizeDefault,
150
+ disabled ? TRIGGER_CLASS.disabled : "",
151
+ className
152
+ );
153
+ triggerWrapper.setAttribute("role", "button");
154
+ triggerWrapper.setAttribute("tabindex", disabled ? "-1" : "0");
155
+ triggerWrapper.setAttribute("aria-haspopup", "dialog");
156
+ triggerWrapper.setAttribute("aria-expanded", "false");
157
+
158
+ var triggerContent = document.createElement("div");
159
+ triggerContent.className = "flex flex-1 items-center gap-8 min-w-0";
160
+ var triggerText = document.createElement("p");
161
+ triggerText.className = "truncate text-reg-13 " + (displayDuration === placeholder ? "text-typography-quaternary-text" : "");
162
+ triggerText.textContent = displayDuration;
163
+ triggerContent.appendChild(triggerText);
164
+ if (millisecondsDisplay) {
165
+ var msSpan = document.createElement("span");
166
+ msSpan.className = "text-xs text-typography-tertiary-text truncate shrink-0";
167
+ msSpan.textContent = millisecondsDisplay;
168
+ triggerContent.appendChild(msSpan);
169
+ }
170
+ triggerWrapper.appendChild(triggerContent);
171
+
172
+ if (variant !== "inline") {
173
+ var chevronWrap = document.createElement("span");
174
+ chevronWrap.className = "size-16 text-typography-quaternary-text shrink-0 flex items-center justify-center";
175
+ chevronWrap.innerHTML = ICON_CHEVRON_DOWN;
176
+ chevronWrap.setAttribute("aria-hidden", "true");
177
+ triggerWrapper.appendChild(chevronWrap);
178
+ }
179
+
180
+ var container = document.createElement("div");
181
+ container.className = "duration-picker relative w-full";
182
+ container.appendChild(triggerWrapper);
183
+
184
+ function updateTriggerDisplay() {
185
+ currentSeconds = Utils.getSecondsFromValue(value || 0, formatType);
186
+ displayDuration = Utils.formatDuration(currentSeconds, placeholder);
187
+ triggerText.textContent = displayDuration;
188
+ triggerText.className = "truncate text-reg-13 " + (displayDuration === placeholder ? "text-typography-quaternary-text" : "");
189
+ if (msSpan) {
190
+ if (currentSeconds > 0 && formatType === "milliseconds") {
191
+ msSpan.textContent = Math.round(currentSeconds * 1000) + "ms";
192
+ msSpan.style.display = "";
193
+ } else {
194
+ msSpan.style.display = "none";
195
+ }
196
+ }
197
+ }
198
+
199
+ function applyAndClose(seconds) {
200
+ value = seconds === 0 ? null : Utils.getValueFromSeconds(seconds, formatType);
201
+ inputValue = "";
202
+ updateTriggerDisplay();
203
+ if (onChange) onChange(value);
204
+ popoverApi.hide();
205
+ }
206
+
207
+ function handleDone() {
208
+ var seconds = inputValue
209
+ ? Utils.parseDuration(inputValue, formatType)
210
+ : Utils.wheelValuesToSeconds(wheelValues);
211
+ applyAndClose(seconds);
212
+ inputValue = "";
213
+ }
214
+
215
+ function handleClear() {
216
+ value = null;
217
+ wheelValues = { days: 0, hours: 0, minutes: 0, seconds: 0 };
218
+ inputValue = "";
219
+ updateTriggerDisplay();
220
+ if (onChange) onChange(null);
221
+ popoverApi.hide();
222
+ }
223
+
224
+ var popoverContent = document.createElement("div");
225
+ popoverContent.className = "p-0";
226
+
227
+ var inputSection = document.createElement("div");
228
+ inputSection.className = "p-8";
229
+ var durationInputEl;
230
+ var clearBtnWrap;
231
+ if (InputComponent && InputComponent.create) {
232
+ var inputWrapper = document.createElement("div");
233
+ inputWrapper.className = "relative";
234
+ durationInputEl = InputComponent.create({
235
+ type: "text",
236
+ placeholder: "2d1h15m30s, 4000s, 1:30:00...",
237
+ variant: "default",
238
+ inputSize: "default",
239
+ onChange: function () {},
240
+ onInput: function () {
241
+ var inputEl = durationInputEl.getInput && durationInputEl.getInput();
242
+ if (inputEl) {
243
+ inputValue = inputEl.value;
244
+ if (inputValue) {
245
+ var parsed = Utils.parseDuration(inputValue, formatType);
246
+ wheelValues = Utils.convertSecondsToTimeUnits(parsed);
247
+ DURATION_WHEELS.forEach(function (w, i) {
248
+ if (wheelInputs[i] && wheelInputs[i].setValue) wheelInputs[i].setValue(wheelValues[w.unit]);
249
+ });
250
+ }
251
+ }
252
+ },
253
+ });
254
+ var inputEl = durationInputEl.getInput && durationInputEl.getInput();
255
+ if (inputEl) {
256
+ inputEl.addEventListener("keydown", function (e) {
257
+ if (e.key !== "Enter") return;
258
+ e.preventDefault();
259
+ var val = durationInputEl.getValue && durationInputEl.getValue();
260
+ if (val) {
261
+ var parsed = Utils.parseDuration(val, formatType);
262
+ applyAndClose(parsed);
263
+ if (durationInputEl.setValue) durationInputEl.setValue("");
264
+ }
265
+ inputValue = "";
266
+ });
267
+ }
268
+ inputWrapper.appendChild(durationInputEl);
269
+ clearBtnWrap = document.createElement("span");
270
+ clearBtnWrap.className = "absolute right-12 top-1/2 -translate-y-1/2 cursor-pointer text-typography-quaternary-text hover:text-typography-primary-text";
271
+ clearBtnWrap.innerHTML = ICON_X;
272
+ clearBtnWrap.setAttribute("role", "button");
273
+ clearBtnWrap.setAttribute("aria-label", "Clear input");
274
+ clearBtnWrap.style.display = "none";
275
+ inputWrapper.appendChild(clearBtnWrap);
276
+ clearBtnWrap.addEventListener("click", function (e) {
277
+ e.stopPropagation();
278
+ if (durationInputEl.setValue) durationInputEl.setValue("");
279
+ inputValue = "";
280
+ clearBtnWrap.style.display = "none";
281
+ });
282
+ if (inputEl) {
283
+ inputEl.addEventListener("input", function () {
284
+ clearBtnWrap.style.display = inputEl.value ? "" : "none";
285
+ });
286
+ }
287
+ inputSection.appendChild(inputWrapper);
288
+ }
289
+ popoverContent.appendChild(inputSection);
290
+
291
+ var sep1 = document.createElement("div");
292
+ sep1.className = "border-t-1/2 border-border-primary w-full";
293
+ popoverContent.appendChild(sep1);
294
+
295
+ var wheelsSection = document.createElement("div");
296
+ wheelsSection.className = "flex justify-center gap-8 p-8";
297
+ var DURATION_WHEELS = Constants.DURATION_WHEELS;
298
+ var MAX_VALUES = Utils.MAX_VALUES;
299
+ var wheelInputs = [];
300
+
301
+ function handleWheelKeyDown(e) {
302
+ if (e.key !== "Enter") return;
303
+ e.preventDefault();
304
+ handleDone();
305
+ }
306
+
307
+ DURATION_WHEELS.forEach(function (w) {
308
+ var wheelEl = createNumberWheel(
309
+ w.unit,
310
+ w.label,
311
+ wheelValues[w.unit],
312
+ MAX_VALUES[w.unit],
313
+ function (newVal) {
314
+ wheelValues[w.unit] = newVal;
315
+ inputValue = "";
316
+ if (durationInputEl && durationInputEl.setValue) durationInputEl.setValue("");
317
+ if (clearBtnWrap) clearBtnWrap.style.display = "none";
318
+ },
319
+ handleWheelKeyDown
320
+ );
321
+ wheelInputs.push(wheelEl);
322
+ wheelsSection.appendChild(wheelEl);
323
+ });
324
+ popoverContent.appendChild(wheelsSection);
325
+
326
+ var sep2 = document.createElement("div");
327
+ sep2.className = "border-t-1/2 border-border-primary w-full";
328
+ popoverContent.appendChild(sep2);
329
+
330
+ var presetsSection = document.createElement("div");
331
+ presetsSection.className = "p-8";
332
+ var PRESET_DURATIONS = Constants.PRESET_DURATIONS;
333
+ PRESET_DURATIONS.forEach(function (group) {
334
+ var groupDiv = document.createElement("div");
335
+ groupDiv.className = "space-y-2";
336
+ var labelEl = document.createElement("label");
337
+ labelEl.className = "text-reg-12 text-typography-secondary-text block";
338
+ labelEl.textContent = group.title;
339
+ groupDiv.appendChild(labelEl);
340
+ var grid = document.createElement("div");
341
+ grid.className = "grid grid-cols-4 gap-4";
342
+ group.presets.forEach(function (preset) {
343
+ var isSelected = Math.abs(currentSeconds - preset.value) < 1;
344
+ var btn = Button.create({
345
+ variant: isSelected ? "outline" : "dashed",
346
+ size: "small",
347
+ text: preset.label,
348
+ className: "text-xs",
349
+ onClick: function () {
350
+ applyAndClose(preset.value);
351
+ inputValue = "";
352
+ },
353
+ });
354
+ grid.appendChild(btn);
355
+ });
356
+ groupDiv.appendChild(grid);
357
+ presetsSection.appendChild(groupDiv);
358
+ });
359
+ popoverContent.appendChild(presetsSection);
360
+
361
+ var sep3 = document.createElement("div");
362
+ sep3.className = "border-t-1/2 border-border-primary w-full";
363
+ popoverContent.appendChild(sep3);
364
+
365
+ var footer = document.createElement("div");
366
+ footer.className = "flex justify-end gap-8 p-8";
367
+ var clearBtn = Button.create({
368
+ variant: "outline",
369
+ size: "small",
370
+ text: "Clear",
371
+ className: "text-error-text-base hover:text-error-text-hover",
372
+ onClick: handleClear,
373
+ });
374
+ if (currentSeconds <= 0) clearBtn.style.display = "none";
375
+ footer.appendChild(clearBtn);
376
+ var doneBtn = Button.create({
377
+ variant: "primary",
378
+ size: "small",
379
+ text: "Done",
380
+ onClick: handleDone,
381
+ });
382
+ footer.appendChild(doneBtn);
383
+ popoverContent.appendChild(footer);
384
+
385
+ var popoverApi = Popover.create({
386
+ trigger: triggerWrapper,
387
+ content: popoverContent,
388
+ placement: "bottom",
389
+ align: "start",
390
+ closeOnClickOutside: true,
391
+ panelClassName: "w-[275px]",
392
+ bodyClassName: "p-0",
393
+ onOpen: function () {
394
+ triggerWrapper.setAttribute("aria-expanded", "true");
395
+ if (!inputValue) {
396
+ wheelValues = Utils.convertSecondsToTimeUnits(currentSeconds);
397
+ wheelInputs.forEach(function (el, i) {
398
+ if (el.setValue) el.setValue(wheelValues[DURATION_WHEELS[i].unit]);
399
+ });
400
+ }
401
+ if (durationInputEl && durationInputEl.setValue) durationInputEl.setValue(inputValue);
402
+ if (clearBtnWrap) clearBtnWrap.style.display = inputValue ? "" : "none";
403
+ clearBtn.style.display = currentSeconds > 0 ? "" : "none";
404
+ },
405
+ onClose: function () {
406
+ triggerWrapper.setAttribute("aria-expanded", "false");
407
+ },
408
+ });
409
+
410
+ triggerWrapper.addEventListener(
411
+ "click",
412
+ function (e) {
413
+ if (disabled) {
414
+ e.preventDefault();
415
+ e.stopImmediatePropagation();
416
+ }
417
+ },
418
+ true
419
+ );
420
+ triggerWrapper.addEventListener("keydown", function (e) {
421
+ if (e.key === "Enter" || e.key === " ") {
422
+ e.preventDefault();
423
+ if (!disabled) triggerWrapper.click();
424
+ }
425
+ });
426
+
427
+ container.getValue = function () {
428
+ return value;
429
+ };
430
+ container.setValue = function (v) {
431
+ value = v;
432
+ updateTriggerDisplay();
433
+ };
434
+ container.setDisabled = function (d) {
435
+ disabled = !!d;
436
+ triggerWrapper.classList.toggle("cursor-not-allowed", disabled);
437
+ triggerWrapper.classList.toggle("opacity-60", disabled);
438
+ triggerWrapper.setAttribute("tabindex", disabled ? "-1" : "0");
439
+ if (disabled) popoverApi.hide();
440
+ };
441
+
442
+ return container;
443
+ }
444
+
445
+ global.Duration = {
446
+ create: create,
447
+ };
448
+ })(typeof window !== "undefined" ? window : this);