@superleapai/flow-ui 2.5.3 → 2.5.5
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/components/card-select.js +1 -1
- package/components/date-time-picker/date-time-picker.js +143 -11
- package/components/popover.js +31 -16
- package/components/select.js +9 -2
- package/components/time-picker.js +70 -17
- package/core/flow.js +9 -1
- package/dist/output.css +1 -1
- package/dist/superleap-flow.js +2229 -273
- package/dist/superleap-flow.js.map +1 -1
- package/dist/superleap-flow.min.js +2 -2
- package/index.d.ts +6 -0
- package/index.js +1 -0
- package/package.json +1 -1
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
var wrapper = document.createElement("div");
|
|
89
89
|
wrapper.setAttribute("role", "radiogroup");
|
|
90
90
|
wrapper.setAttribute("dir", "ltr");
|
|
91
|
-
wrapper.className = join("flex flex-col gap-
|
|
91
|
+
wrapper.className = join("flex flex-col gap-8 w-full", className);
|
|
92
92
|
|
|
93
93
|
function updateAllCards(newValue) {
|
|
94
94
|
var cards = wrapper.querySelectorAll("[data-card-value]");
|
|
@@ -35,10 +35,37 @@
|
|
|
35
35
|
return Array.prototype.filter.call(arguments, Boolean).join(" ");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// Mirror input wrapper variant + size behaviour for visual consistency
|
|
39
|
+
var TRIGGER_WRAPPER_CLASS = {
|
|
40
|
+
base:
|
|
41
|
+
"group flex items-center border-1/2 border-border-primary rounded-4 text-typography-primary-text gap-x-8 w-full transition-all ease-in-out group-has-[:disabled]:cursor-not-allowed group-has-[:disabled]:border-border-primary group-has-[:disabled]:bg-fill-tertiary-fill-light-gray group-has-[:disabled]:text-typography-quaternary-text group-has-[:disabled]:hover:border-border-primary",
|
|
42
|
+
default:
|
|
43
|
+
"bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
|
|
44
|
+
error:
|
|
45
|
+
"border-error-base bg-fill-quarternary-fill-white hover:border-error-base focus-within:border-error-base",
|
|
46
|
+
warning:
|
|
47
|
+
"border-warning-base bg-fill-quarternary-fill-white hover:border-warning-base focus-within:border-warning-base",
|
|
48
|
+
success:
|
|
49
|
+
"border-success-base bg-fill-quarternary-fill-white hover:border-success-base focus-within:border-success-base",
|
|
50
|
+
borderless:
|
|
51
|
+
"border-none shadow-none rounded-0 bg-fill-quarternary-fill-white",
|
|
52
|
+
inline:
|
|
53
|
+
"border-transparent shadow-none rounded-0 bg-fill-quarternary-fill-white hover:bg-fill-tertiary-fill-light-gray focus-within:border-transparent focus:bg-fill-tertiary-fill-light-gray focus-within:bg-fill-tertiary-fill-light-gray",
|
|
54
|
+
sizeDefault: "px-12 py-6",
|
|
55
|
+
sizeLarge: "px-12 py-8",
|
|
56
|
+
sizeSmall: "px-12 py-4",
|
|
57
|
+
disabled:
|
|
58
|
+
"cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
|
|
59
|
+
};
|
|
60
|
+
|
|
38
61
|
/**
|
|
39
62
|
* Create the calendar caption (month + year selects) and nav buttons
|
|
63
|
+
* @param {Object} [options] - Optional. nestedPopoverClass: string to add to Select/TimePicker popover wrappers so parent can ignore outside click
|
|
40
64
|
*/
|
|
41
|
-
function createCaption(displayMonth, fromDate, toDate, yearRange, onMonthChange, onPrev, onNext) {
|
|
65
|
+
function createCaption(displayMonth, fromDate, toDate, yearRange, onMonthChange, onPrev, onNext, options) {
|
|
66
|
+
options = options || {};
|
|
67
|
+
var nestedPopoverClass = options.nestedPopoverClass || "";
|
|
68
|
+
var closeTimePickerFn = null;
|
|
42
69
|
var Utils = global.DateTimePickerUtils;
|
|
43
70
|
if (!Utils) throw new Error("DateTimePickerUtils required");
|
|
44
71
|
var MONTHS = Utils.genMonths();
|
|
@@ -49,6 +76,8 @@
|
|
|
49
76
|
var captionWrap = document.createElement("div");
|
|
50
77
|
captionWrap.className = "flex justify-between relative items-center p-6";
|
|
51
78
|
|
|
79
|
+
var monthSelect = null;
|
|
80
|
+
var yearSelect = null;
|
|
52
81
|
var Select = getDep("Select");
|
|
53
82
|
if (Select && typeof Select.create === "function") {
|
|
54
83
|
var monthValue = displayMonth.getMonth();
|
|
@@ -61,22 +90,32 @@
|
|
|
61
90
|
});
|
|
62
91
|
var selectWrap = document.createElement("div");
|
|
63
92
|
selectWrap.className = "inline-flex gap-x-8";
|
|
64
|
-
|
|
93
|
+
monthSelect = Select.create({
|
|
65
94
|
options: monthOptions,
|
|
66
95
|
value: monthValue,
|
|
67
96
|
placeholder: "Month",
|
|
68
97
|
size: "small",
|
|
98
|
+
dropdownMaxHeightVh: 20,
|
|
99
|
+
popoverWrapperClassName: nestedPopoverClass,
|
|
100
|
+
onOpenCallback: function () {
|
|
101
|
+
if (closeTimePickerFn) closeTimePickerFn();
|
|
102
|
+
},
|
|
69
103
|
onChange: function (val) {
|
|
70
104
|
var d = new Date(displayMonth.getTime());
|
|
71
105
|
d.setMonth(Number(val));
|
|
72
106
|
onMonthChange(d);
|
|
73
107
|
},
|
|
74
108
|
});
|
|
75
|
-
|
|
109
|
+
yearSelect = Select.create({
|
|
76
110
|
options: yearOptions,
|
|
77
111
|
value: yearValue,
|
|
78
112
|
placeholder: "Year",
|
|
79
113
|
size: "small",
|
|
114
|
+
dropdownMaxHeightVh: 20,
|
|
115
|
+
popoverWrapperClassName: nestedPopoverClass,
|
|
116
|
+
onOpenCallback: function () {
|
|
117
|
+
if (closeTimePickerFn) closeTimePickerFn();
|
|
118
|
+
},
|
|
80
119
|
onChange: function (val) {
|
|
81
120
|
var d = new Date(displayMonth.getTime());
|
|
82
121
|
d.setFullYear(Number(val));
|
|
@@ -88,13 +127,18 @@
|
|
|
88
127
|
captionWrap.appendChild(selectWrap);
|
|
89
128
|
}
|
|
90
129
|
|
|
130
|
+
function closeDropdowns() {
|
|
131
|
+
if (monthSelect && monthSelect.popoverInstance) monthSelect.popoverInstance.hide();
|
|
132
|
+
if (yearSelect && yearSelect.popoverInstance) yearSelect.popoverInstance.hide();
|
|
133
|
+
}
|
|
134
|
+
|
|
91
135
|
var Button = getDep("Button");
|
|
92
136
|
if (!Button || typeof Button.create !== "function") {
|
|
93
137
|
var fallbackNav = document.createElement("div");
|
|
94
138
|
fallbackNav.className = "flex items-center gap-x-8";
|
|
95
139
|
fallbackNav.textContent = "Button component required";
|
|
96
140
|
captionWrap.appendChild(fallbackNav);
|
|
97
|
-
return { element: captionWrap, setMonth: function () {} };
|
|
141
|
+
return { element: captionWrap, setMonth: function () {}, closeDropdowns: function () {}, setCloseTimePicker: function () {} };
|
|
98
142
|
}
|
|
99
143
|
var nav = document.createElement("div");
|
|
100
144
|
nav.className = "flex items-center gap-x-8";
|
|
@@ -120,6 +164,36 @@
|
|
|
120
164
|
nav.appendChild(nextBtn);
|
|
121
165
|
captionWrap.appendChild(nav);
|
|
122
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Keep nav chevron disabled state in sync with current month
|
|
169
|
+
* so that when we move away from fromDate/toDate month they re-enable.
|
|
170
|
+
*/
|
|
171
|
+
function updateNavDisabled(d) {
|
|
172
|
+
var prevDisabled = Utils.isPreviousMonthDisabled(d, fromDate);
|
|
173
|
+
var nextDisabled = Utils.isNextMonthDisabled(d, toDate);
|
|
174
|
+
|
|
175
|
+
if (prevBtn) {
|
|
176
|
+
prevBtn.disabled = prevDisabled;
|
|
177
|
+
if (prevDisabled) {
|
|
178
|
+
prevBtn.setAttribute("disabled", "disabled");
|
|
179
|
+
} else {
|
|
180
|
+
prevBtn.removeAttribute("disabled");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (nextBtn) {
|
|
185
|
+
nextBtn.disabled = nextDisabled;
|
|
186
|
+
if (nextDisabled) {
|
|
187
|
+
nextBtn.setAttribute("disabled", "disabled");
|
|
188
|
+
} else {
|
|
189
|
+
nextBtn.removeAttribute("disabled");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Ensure initial state is correct
|
|
195
|
+
updateNavDisabled(displayMonth);
|
|
196
|
+
|
|
123
197
|
return {
|
|
124
198
|
element: captionWrap,
|
|
125
199
|
setMonth: function (d) {
|
|
@@ -128,6 +202,11 @@
|
|
|
128
202
|
if (selects[0].updateValue) selects[0].updateValue(d.getMonth());
|
|
129
203
|
if (selects[1].updateValue) selects[1].updateValue(d.getFullYear());
|
|
130
204
|
}
|
|
205
|
+
updateNavDisabled(d);
|
|
206
|
+
},
|
|
207
|
+
closeDropdowns: closeDropdowns,
|
|
208
|
+
setCloseTimePicker: function (fn) {
|
|
209
|
+
closeTimePickerFn = fn;
|
|
131
210
|
},
|
|
132
211
|
};
|
|
133
212
|
}
|
|
@@ -244,11 +323,19 @@
|
|
|
244
323
|
|
|
245
324
|
var triggerWrapper = document.createElement("div");
|
|
246
325
|
function getTriggerClassName(disabledState) {
|
|
326
|
+
var sizeClass =
|
|
327
|
+
size === "large"
|
|
328
|
+
? TRIGGER_WRAPPER_CLASS.sizeLarge
|
|
329
|
+
: size === "small"
|
|
330
|
+
? TRIGGER_WRAPPER_CLASS.sizeSmall
|
|
331
|
+
: TRIGGER_WRAPPER_CLASS.sizeDefault;
|
|
332
|
+
|
|
247
333
|
return join(
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
334
|
+
TRIGGER_WRAPPER_CLASS.base,
|
|
335
|
+
disabledState
|
|
336
|
+
? TRIGGER_WRAPPER_CLASS.disabled
|
|
337
|
+
: TRIGGER_WRAPPER_CLASS[variant] || TRIGGER_WRAPPER_CLASS.default,
|
|
338
|
+
sizeClass
|
|
252
339
|
);
|
|
253
340
|
}
|
|
254
341
|
triggerWrapper.className = getTriggerClassName(disabled);
|
|
@@ -289,8 +376,14 @@
|
|
|
289
376
|
var calendarEl;
|
|
290
377
|
var timePickerEl;
|
|
291
378
|
var quickRow;
|
|
379
|
+
var isTimeDropdownOpen = false;
|
|
292
380
|
|
|
293
381
|
function handleSelectDay(dayDate) {
|
|
382
|
+
// If time picker dropdown is open, close it first and ignore this click.
|
|
383
|
+
if (isTimeDropdownOpen) {
|
|
384
|
+
if (timePickerEl && timePickerEl.closeDropdown) timePickerEl.closeDropdown();
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
294
387
|
var out;
|
|
295
388
|
if (granularity === "day") {
|
|
296
389
|
out = Utils.startOfDay(dayDate);
|
|
@@ -339,7 +432,7 @@
|
|
|
339
432
|
function buildPanelContent() {
|
|
340
433
|
var fragment = document.createDocumentFragment();
|
|
341
434
|
|
|
342
|
-
// Caption (month/year dropdowns then nav chevrons)
|
|
435
|
+
// Caption (month/year dropdowns then nav chevrons); nested popover class so main popover does not close when selecting month/year/time
|
|
343
436
|
captionEl = createCaption(
|
|
344
437
|
displayMonth,
|
|
345
438
|
fromDate,
|
|
@@ -363,7 +456,8 @@
|
|
|
363
456
|
refreshCalendar();
|
|
364
457
|
if (captionEl && captionEl.setMonth) captionEl.setMonth(displayMonth);
|
|
365
458
|
}
|
|
366
|
-
}
|
|
459
|
+
},
|
|
460
|
+
{ nestedPopoverClass: "dtp-nested-popover" }
|
|
367
461
|
);
|
|
368
462
|
fragment.appendChild(captionEl.element);
|
|
369
463
|
|
|
@@ -392,6 +486,13 @@
|
|
|
392
486
|
placeholder: "Time",
|
|
393
487
|
use24Hour: hourCycle === 24,
|
|
394
488
|
size: "small",
|
|
489
|
+
popoverWrapperClassName: "dtp-nested-popover",
|
|
490
|
+
onDropdownOpen: function () {
|
|
491
|
+
isTimeDropdownOpen = true;
|
|
492
|
+
},
|
|
493
|
+
onDropdownClose: function () {
|
|
494
|
+
isTimeDropdownOpen = false;
|
|
495
|
+
},
|
|
395
496
|
onChange: function (timeStr) {
|
|
396
497
|
var base = value ? new Date(value.getTime()) : new Date();
|
|
397
498
|
Utils.setTimeFromString(base, timeStr);
|
|
@@ -402,6 +503,16 @@
|
|
|
402
503
|
},
|
|
403
504
|
});
|
|
404
505
|
timeRow.appendChild(timePickerEl);
|
|
506
|
+
// When opening time picker, close month/year selects; when opening month/year, close time picker
|
|
507
|
+
if (captionEl && captionEl.setCloseTimePicker) {
|
|
508
|
+
captionEl.setCloseTimePicker(function () {
|
|
509
|
+
if (timePickerEl && timePickerEl.closeDropdown) timePickerEl.closeDropdown();
|
|
510
|
+
isTimeDropdownOpen = false;
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
timePickerEl.addEventListener("click", function () {
|
|
514
|
+
if (captionEl && captionEl.closeDropdowns) captionEl.closeDropdowns();
|
|
515
|
+
}, true);
|
|
405
516
|
fragment.appendChild(timeRow);
|
|
406
517
|
}
|
|
407
518
|
|
|
@@ -416,6 +527,10 @@
|
|
|
416
527
|
text: "Today",
|
|
417
528
|
startIcon: ICON_CALENDAR_DOT,
|
|
418
529
|
onClick: function () {
|
|
530
|
+
if (isTimeDropdownOpen) {
|
|
531
|
+
if (timePickerEl && timePickerEl.closeDropdown) timePickerEl.closeDropdown();
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
419
534
|
var t = granularity === "day" ? Utils.startOfDay(new Date()) : new Date();
|
|
420
535
|
value = Utils.getValidDate(t, fromDate, toDate);
|
|
421
536
|
validDate = value;
|
|
@@ -437,6 +552,10 @@
|
|
|
437
552
|
text: "Tomorrow",
|
|
438
553
|
startIcon: ICON_CALENDAR_UP,
|
|
439
554
|
onClick: function () {
|
|
555
|
+
if (isTimeDropdownOpen) {
|
|
556
|
+
if (timePickerEl && timePickerEl.closeDropdown) timePickerEl.closeDropdown();
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
440
559
|
var t = Utils.addDays(new Date(), 1);
|
|
441
560
|
t = granularity === "day" ? Utils.startOfDay(t) : t;
|
|
442
561
|
value = Utils.getValidDate(t, fromDate, toDate);
|
|
@@ -458,6 +577,10 @@
|
|
|
458
577
|
text: "No date",
|
|
459
578
|
startIcon: ICON_CALENDAR_MINUS,
|
|
460
579
|
onClick: function () {
|
|
580
|
+
if (isTimeDropdownOpen) {
|
|
581
|
+
if (timePickerEl && timePickerEl.closeDropdown) timePickerEl.closeDropdown();
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
461
584
|
value = undefined;
|
|
462
585
|
validDate = undefined;
|
|
463
586
|
setMonth(new Date());
|
|
@@ -479,7 +602,10 @@
|
|
|
479
602
|
content: panelInner,
|
|
480
603
|
placement: "bottom",
|
|
481
604
|
align: align,
|
|
605
|
+
modal: true,
|
|
606
|
+
zIndex: 998,
|
|
482
607
|
closeOnClickOutside: true,
|
|
608
|
+
outsideClickIgnoreSelector: ".dtp-nested-popover",
|
|
483
609
|
onOpen: function () {
|
|
484
610
|
triggerWrapper.setAttribute("aria-expanded", "true");
|
|
485
611
|
displayMonth = validDate ? new Date(validDate.getTime()) : new Date();
|
|
@@ -490,13 +616,19 @@
|
|
|
490
616
|
},
|
|
491
617
|
});
|
|
492
618
|
|
|
493
|
-
// Popover already toggles on trigger click; we
|
|
619
|
+
// Popover already toggles on trigger click; we block when disabled, and when time dropdown is open we close it instead of toggling date popover.
|
|
494
620
|
triggerWrapper.addEventListener(
|
|
495
621
|
"click",
|
|
496
622
|
function (e) {
|
|
497
623
|
if (disabled) {
|
|
498
624
|
e.preventDefault();
|
|
499
625
|
e.stopImmediatePropagation();
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
if (isTimeDropdownOpen) {
|
|
629
|
+
e.preventDefault();
|
|
630
|
+
e.stopImmediatePropagation();
|
|
631
|
+
if (timePickerEl && timePickerEl.closeDropdown) timePickerEl.closeDropdown();
|
|
500
632
|
}
|
|
501
633
|
},
|
|
502
634
|
true
|
package/components/popover.js
CHANGED
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
* @param {string} [config.bodyClassName] - Optional class for body wrapper (overrides default padding)
|
|
21
21
|
* @param {string} [config.panelClassName] - Optional class to add to panel (e.g. for width)
|
|
22
22
|
* @param {boolean} [config.modal=false] - If true, lock body scroll and show backdrop; only popover and trigger are interactive
|
|
23
|
+
* @param {number} [config.zIndex=999] - z-index for the wrapper when modal (use lower e.g. 998 so nested popovers open above)
|
|
24
|
+
* @param {string} [config.wrapperClassName] - Optional class to add to the wrapper (e.g. for nested popovers so parent can ignore outside click)
|
|
25
|
+
* @param {string} [config.outsideClickIgnoreSelector] - If click target is inside this selector, do not close (e.g. ".dtp-nested-popover")
|
|
23
26
|
* @returns {Object} Popover API {show, hide, destroy, element}
|
|
24
27
|
*/
|
|
25
28
|
function create(config = {}) {
|
|
@@ -35,6 +38,9 @@
|
|
|
35
38
|
bodyClassName = "",
|
|
36
39
|
panelClassName = "",
|
|
37
40
|
modal = true,
|
|
41
|
+
zIndex = 999,
|
|
42
|
+
wrapperClassName = "",
|
|
43
|
+
outsideClickIgnoreSelector = "",
|
|
38
44
|
} = config;
|
|
39
45
|
|
|
40
46
|
const triggerEl =
|
|
@@ -55,7 +61,8 @@
|
|
|
55
61
|
|
|
56
62
|
const wrapper = document.createElement("div");
|
|
57
63
|
wrapper.className =
|
|
58
|
-
"fixed z-50 pointer-events-none opacity-0 invisible transition-opacity duration-150 ease-out"
|
|
64
|
+
"fixed z-50 pointer-events-none opacity-0 invisible transition-opacity duration-150 ease-out" +
|
|
65
|
+
(wrapperClassName ? " " + wrapperClassName : "");
|
|
59
66
|
wrapper.setAttribute("aria-hidden", "true");
|
|
60
67
|
|
|
61
68
|
const panel = document.createElement("div");
|
|
@@ -119,7 +126,7 @@
|
|
|
119
126
|
});
|
|
120
127
|
}
|
|
121
128
|
document.body.appendChild(backdropEl);
|
|
122
|
-
wrapper.style.zIndex =
|
|
129
|
+
wrapper.style.zIndex = String(zIndex);
|
|
123
130
|
}
|
|
124
131
|
|
|
125
132
|
function applyModalClose() {
|
|
@@ -190,15 +197,28 @@
|
|
|
190
197
|
? triggerRect.height - panelRect.height
|
|
191
198
|
: 0;
|
|
192
199
|
|
|
200
|
+
// Clear top-placement max-height when not opening above
|
|
201
|
+
if (effectivePlacement !== "top") {
|
|
202
|
+
panel.style.maxHeight = "";
|
|
203
|
+
}
|
|
204
|
+
|
|
193
205
|
switch (effectivePlacement) {
|
|
194
206
|
case "bottom":
|
|
195
207
|
top = triggerRect.bottom + gap;
|
|
196
208
|
left = triggerRect.left + alignLeft;
|
|
197
209
|
break;
|
|
198
|
-
case "top":
|
|
199
|
-
|
|
210
|
+
case "top": {
|
|
211
|
+
// Larger gap when above the trigger so the popover doesn't overlap it.
|
|
212
|
+
const topGap = 24;
|
|
213
|
+
top = triggerRect.top - panelRect.height - topGap;
|
|
200
214
|
left = triggerRect.left + alignLeft;
|
|
215
|
+
// Cap panel height so when content loads/tall it doesn't grow downward into the trigger.
|
|
216
|
+
const maxHeightAbove = triggerRect.top - topGap - 12;
|
|
217
|
+
if (maxHeightAbove > 0) {
|
|
218
|
+
panel.style.maxHeight = maxHeightAbove + "px";
|
|
219
|
+
}
|
|
201
220
|
break;
|
|
221
|
+
}
|
|
202
222
|
case "right":
|
|
203
223
|
top = triggerRect.top + alignTop;
|
|
204
224
|
left = triggerRect.right + gap;
|
|
@@ -242,6 +262,9 @@
|
|
|
242
262
|
applyModalOpen();
|
|
243
263
|
requestAnimationFrame(function () {
|
|
244
264
|
position();
|
|
265
|
+
// Set panel to open state before making wrapper visible so we never show
|
|
266
|
+
// the closed state (avoids 2–3 frame flicker of fade-out/zoom-out then open).
|
|
267
|
+
panel.setAttribute("data-state", "open");
|
|
245
268
|
wrapper.classList.remove(
|
|
246
269
|
"invisible",
|
|
247
270
|
"opacity-0",
|
|
@@ -258,11 +281,6 @@
|
|
|
258
281
|
});
|
|
259
282
|
resizeObserver.observe(panel);
|
|
260
283
|
}
|
|
261
|
-
requestAnimationFrame(function () {
|
|
262
|
-
requestAnimationFrame(function () {
|
|
263
|
-
panel.setAttribute("data-state", "open");
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
284
|
});
|
|
267
285
|
}
|
|
268
286
|
|
|
@@ -302,13 +320,10 @@
|
|
|
302
320
|
}
|
|
303
321
|
|
|
304
322
|
function outsideClick(e) {
|
|
305
|
-
if (
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
) {
|
|
310
|
-
hide();
|
|
311
|
-
}
|
|
323
|
+
if (!wrapper.classList.contains("visible")) return;
|
|
324
|
+
if (wrapper.contains(e.target) || triggerEl.contains(e.target)) return;
|
|
325
|
+
if (outsideClickIgnoreSelector && e.target.closest && e.target.closest(outsideClickIgnoreSelector)) return;
|
|
326
|
+
hide();
|
|
312
327
|
}
|
|
313
328
|
|
|
314
329
|
function handleKeyDown(e) {
|
package/components/select.js
CHANGED
|
@@ -84,6 +84,9 @@
|
|
|
84
84
|
var size = config.size || "default";
|
|
85
85
|
var canClear = !!config.canClear;
|
|
86
86
|
var onClear = config.onClear;
|
|
87
|
+
var popoverWrapperClassName = config.popoverWrapperClassName || "";
|
|
88
|
+
var onOpenCallback = config.onOpenCallback || null;
|
|
89
|
+
var dropdownMaxHeightVh = config.dropdownMaxHeightVh != null ? config.dropdownMaxHeightVh : 45;
|
|
87
90
|
|
|
88
91
|
var disabled = config.disabled === true;
|
|
89
92
|
var value =
|
|
@@ -193,11 +196,13 @@
|
|
|
193
196
|
|
|
194
197
|
var content = document.createElement("div");
|
|
195
198
|
content.setAttribute("role", "listbox");
|
|
196
|
-
content.className = "custom-select-content w-full
|
|
199
|
+
content.className = "custom-select-content w-full overflow-hidden flex flex-col";
|
|
200
|
+
content.style.maxHeight = dropdownMaxHeightVh + "vh";
|
|
197
201
|
|
|
198
202
|
var optionsList = document.createElement("div");
|
|
199
203
|
optionsList.className =
|
|
200
|
-
"overflow-y-auto
|
|
204
|
+
"overflow-y-auto p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
|
|
205
|
+
optionsList.style.maxHeight = dropdownMaxHeightVh + "vh";
|
|
201
206
|
|
|
202
207
|
if (options.length === 0) {
|
|
203
208
|
var noOpt = document.createElement("div");
|
|
@@ -257,11 +262,13 @@
|
|
|
257
262
|
closeOnClickOutside: true,
|
|
258
263
|
bodyClassName: "p-0 overflow-hidden",
|
|
259
264
|
panelClassName: "min-w-[var(--trigger-width)] max-h-[45vh] overflow-hidden",
|
|
265
|
+
wrapperClassName: popoverWrapperClassName,
|
|
260
266
|
onOpen: function () {
|
|
261
267
|
if (disabled) {
|
|
262
268
|
popover.hide();
|
|
263
269
|
return;
|
|
264
270
|
}
|
|
271
|
+
if (onOpenCallback) onOpenCallback();
|
|
265
272
|
document
|
|
266
273
|
.querySelectorAll(".custom-select, .record-select, .enum-select, .enum-multiselect, .custom-multiselect, .record-multiselect")
|
|
267
274
|
.forEach(function (other) {
|
|
@@ -47,16 +47,50 @@
|
|
|
47
47
|
return pad(h12) + " : " + pad(minute) + " " + amPm;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
|
|
50
|
+
// Mirror input wrapper variant + size for visual consistency
|
|
51
|
+
var TRIGGER_CLASS = {
|
|
52
|
+
base:
|
|
53
|
+
"group flex items-center border-1/2 border-border-primary rounded-4 text-typography-primary-text gap-x-8 w-full transition-all ease-in-out focus:outline-none min-h-0 h-full truncate hover:cursor-pointer " +
|
|
54
|
+
"group-has-[:disabled]:cursor-not-allowed group-has-[:disabled]:border-border-primary group-has-[:disabled]:bg-fill-tertiary-fill-light-gray group-has-[:disabled]:text-typography-quaternary-text group-has-[:disabled]:hover:border-border-primary",
|
|
55
|
+
default:
|
|
56
|
+
"bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
|
|
57
|
+
error:
|
|
58
|
+
"border-error-base bg-fill-quarternary-fill-white hover:border-error-base focus-within:border-error-base",
|
|
59
|
+
warning:
|
|
60
|
+
"border-warning-base bg-fill-quarternary-fill-white hover:border-warning-base focus-within:border-warning-base",
|
|
61
|
+
success:
|
|
62
|
+
"border-success-base bg-fill-quarternary-fill-white hover:border-success-base focus-within:border-success-base",
|
|
63
|
+
borderless:
|
|
64
|
+
"border-none shadow-none rounded-0 bg-fill-quarternary-fill-white",
|
|
65
|
+
inline:
|
|
66
|
+
"border-transparent shadow-none rounded-0 bg-fill-quarternary-fill-white hover:bg-fill-tertiary-fill-light-gray focus-within:border-transparent focus:bg-fill-tertiary-fill-light-gray focus-within:bg-fill-tertiary-fill-light-gray",
|
|
67
|
+
sizeDefault: "px-12 py-6 !text-reg-13",
|
|
68
|
+
sizeSmall: "px-12 py-4 !text-reg-12",
|
|
69
|
+
sizeLarge: "px-12 py-8 !text-reg-14",
|
|
70
|
+
disabled:
|
|
71
|
+
"pointer-events-none cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
function join() {
|
|
75
|
+
return Array.prototype.filter.call(arguments, Boolean).join(" ");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function triggerClasses(disabled, hasValue, size, variant) {
|
|
79
|
+
variant = variant || "default";
|
|
80
|
+
var sizeClass =
|
|
81
|
+
size === "small"
|
|
82
|
+
? TRIGGER_CLASS.sizeSmall
|
|
83
|
+
: size === "large"
|
|
84
|
+
? TRIGGER_CLASS.sizeLarge
|
|
85
|
+
: TRIGGER_CLASS.sizeDefault;
|
|
55
86
|
var valueClass = hasValue ? "text-inherit" : "text-typography-quaternary-text";
|
|
56
|
-
var
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
87
|
+
var variantClass = disabled ? TRIGGER_CLASS.disabled : (TRIGGER_CLASS[variant] || TRIGGER_CLASS.default);
|
|
88
|
+
return join(
|
|
89
|
+
TRIGGER_CLASS.base,
|
|
90
|
+
variantClass,
|
|
91
|
+
sizeClass,
|
|
92
|
+
valueClass
|
|
93
|
+
);
|
|
60
94
|
}
|
|
61
95
|
|
|
62
96
|
/**
|
|
@@ -132,7 +166,8 @@
|
|
|
132
166
|
* @param {Function} config.onChange - Change handler (value: string "HH:mm")
|
|
133
167
|
* @param {boolean} config.disabled - Whether the picker is disabled
|
|
134
168
|
* @param {boolean} config.use24Hour - Use 24-hour format (default: false, i.e. 12-hour AM/PM)
|
|
135
|
-
* @param {string} [config.size] - 'default' | 'small' (matches Input/Select sizes)
|
|
169
|
+
* @param {string} [config.size] - 'default' | 'small' | 'large' (matches Input/Select sizes)
|
|
170
|
+
* @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'success' | 'borderless' | 'inline' (matches Input trigger styles)
|
|
136
171
|
* @returns {HTMLElement} Time picker container element
|
|
137
172
|
*/
|
|
138
173
|
function create(config) {
|
|
@@ -140,9 +175,13 @@
|
|
|
140
175
|
var initialValue = config.value !== undefined ? config.value : "";
|
|
141
176
|
var placeholder = config.placeholder || "Select time";
|
|
142
177
|
var onChange = config.onChange;
|
|
178
|
+
var onDropdownOpen = typeof config.onDropdownOpen === "function" ? config.onDropdownOpen : null;
|
|
179
|
+
var onDropdownClose = typeof config.onDropdownClose === "function" ? config.onDropdownClose : null;
|
|
143
180
|
var disabled = config.disabled === true;
|
|
144
181
|
var use24Hour = config.use24Hour === true;
|
|
145
|
-
var size = config.size === "small" ? "small" : "default";
|
|
182
|
+
var size = config.size === "small" ? "small" : config.size === "large" ? "large" : "default";
|
|
183
|
+
var variant = config.variant || "default";
|
|
184
|
+
var popoverWrapperClassName = config.popoverWrapperClassName || "";
|
|
146
185
|
|
|
147
186
|
var value = typeof initialValue === "string" ? initialValue : "";
|
|
148
187
|
var parsed = parseValue(value);
|
|
@@ -161,7 +200,7 @@
|
|
|
161
200
|
trigger.setAttribute("aria-haspopup", "listbox");
|
|
162
201
|
trigger.setAttribute("aria-expanded", "false");
|
|
163
202
|
trigger.setAttribute("aria-label", placeholder);
|
|
164
|
-
trigger.className = triggerClasses(disabled, !!value, size);
|
|
203
|
+
trigger.className = triggerClasses(disabled, !!value, size, variant);
|
|
165
204
|
|
|
166
205
|
var triggerText = document.createElement("span");
|
|
167
206
|
triggerText.className =
|
|
@@ -192,6 +231,8 @@
|
|
|
192
231
|
}
|
|
193
232
|
var periodOptions = [{ value: "AM", label: "AM" }, { value: "PM", label: "PM" }];
|
|
194
233
|
|
|
234
|
+
var isOpen = false;
|
|
235
|
+
|
|
195
236
|
var hourColVal = use24Hour ? hour : hour12;
|
|
196
237
|
var hourColumn = createColumn(hourOptions, hourColVal, function (v) {
|
|
197
238
|
if (use24Hour) {
|
|
@@ -207,7 +248,7 @@
|
|
|
207
248
|
triggerText.className =
|
|
208
249
|
"flex-1 truncate text-left tabular-nums " +
|
|
209
250
|
(value ? "text-inherit" : "text-typography-quaternary-text");
|
|
210
|
-
trigger.className = triggerClasses(disabled, !!value, size);
|
|
251
|
+
trigger.className = triggerClasses(disabled, !!value, size, variant);
|
|
211
252
|
if (onChange) onChange(value);
|
|
212
253
|
}, true);
|
|
213
254
|
var minuteColumn = createColumn(minuteOptions, minute, function (v) {
|
|
@@ -219,7 +260,7 @@
|
|
|
219
260
|
triggerText.className =
|
|
220
261
|
"flex-1 truncate text-left tabular-nums " +
|
|
221
262
|
(value ? "text-inherit" : "text-typography-quaternary-text");
|
|
222
|
-
trigger.className = triggerClasses(disabled, !!value, size);
|
|
263
|
+
trigger.className = triggerClasses(disabled, !!value, size, variant);
|
|
223
264
|
if (onChange) onChange(value);
|
|
224
265
|
}, true);
|
|
225
266
|
var periodColumn = null;
|
|
@@ -234,7 +275,7 @@
|
|
|
234
275
|
triggerText.className =
|
|
235
276
|
"flex-1 truncate text-left tabular-nums " +
|
|
236
277
|
(value ? "text-inherit" : "text-typography-quaternary-text");
|
|
237
|
-
trigger.className = triggerClasses(disabled, !!value, size);
|
|
278
|
+
trigger.className = triggerClasses(disabled, !!value, size, variant);
|
|
238
279
|
if (onChange) onChange(value);
|
|
239
280
|
}, false);
|
|
240
281
|
}
|
|
@@ -310,17 +351,29 @@
|
|
|
310
351
|
closeOnClickOutside: true,
|
|
311
352
|
bodyClassName: "p-0",
|
|
312
353
|
panelClassName: "!w-auto min-w-max",
|
|
354
|
+
wrapperClassName: popoverWrapperClassName,
|
|
313
355
|
onOpen: function () {
|
|
314
356
|
trigger.setAttribute("aria-expanded", "true");
|
|
315
357
|
container.classList.add("open");
|
|
358
|
+
isOpen = true;
|
|
359
|
+
if (onDropdownOpen) onDropdownOpen();
|
|
316
360
|
syncColumnsAndScroll();
|
|
317
361
|
},
|
|
318
362
|
onClose: function () {
|
|
319
363
|
trigger.setAttribute("aria-expanded", "false");
|
|
320
364
|
container.classList.remove("open");
|
|
365
|
+
isOpen = false;
|
|
366
|
+
if (onDropdownClose) onDropdownClose();
|
|
321
367
|
},
|
|
322
368
|
});
|
|
323
369
|
hidePopover = popover.hide;
|
|
370
|
+
container.closeDropdown = function () {
|
|
371
|
+
popover.hide();
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
container.isDropdownOpen = function () {
|
|
375
|
+
return isOpen;
|
|
376
|
+
};
|
|
324
377
|
|
|
325
378
|
container.updateValue = function (newVal) {
|
|
326
379
|
value = typeof newVal === "string" ? newVal : "";
|
|
@@ -333,7 +386,7 @@
|
|
|
333
386
|
triggerText.className =
|
|
334
387
|
"flex-1 truncate text-left tabular-nums " +
|
|
335
388
|
(value ? "text-inherit" : "text-typography-quaternary-text");
|
|
336
|
-
trigger.className = triggerClasses(disabled, !!value, size);
|
|
389
|
+
trigger.className = triggerClasses(disabled, !!value, size, variant);
|
|
337
390
|
hourColumn.setSelected(use24Hour ? hour : hour12);
|
|
338
391
|
minuteColumn.setSelected(minute);
|
|
339
392
|
if (periodColumn) periodColumn.setSelected(period);
|
|
@@ -342,7 +395,7 @@
|
|
|
342
395
|
container.setDisabled = function (isDisabled) {
|
|
343
396
|
disabled = !!isDisabled;
|
|
344
397
|
trigger.disabled = disabled;
|
|
345
|
-
trigger.className = triggerClasses(disabled, !!value, size);
|
|
398
|
+
trigger.className = triggerClasses(disabled, !!value, size, variant);
|
|
346
399
|
if (disabled) popover.hide();
|
|
347
400
|
};
|
|
348
401
|
|
package/core/flow.js
CHANGED
|
@@ -386,10 +386,13 @@
|
|
|
386
386
|
* @param {Function} config.onChange - Optional change handler
|
|
387
387
|
* @param {boolean} config.disabled - Whether time picker is disabled
|
|
388
388
|
* @param {boolean} config.use24Hour - Use 24-hour format (default: false, i.e. 12-hour AM/PM)
|
|
389
|
+
* @param {string} [config.size] - 'default' | 'small' | 'large'
|
|
390
|
+
* @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'success' | 'borderless' | 'inline'
|
|
391
|
+
* @param {string} [config.helpText] - Optional help text for tooltip
|
|
389
392
|
* @returns {HTMLElement} Field element
|
|
390
393
|
*/
|
|
391
394
|
function createTimePicker(config) {
|
|
392
|
-
const { label, fieldId, value: initialValue, placeholder, required = false, onChange, disabled = false, use24Hour = false, helpText = null } = config;
|
|
395
|
+
const { label, fieldId, value: initialValue, placeholder, required = false, onChange, disabled = false, use24Hour = false, size, variant, helpText = null } = config;
|
|
393
396
|
|
|
394
397
|
const field = createFieldWrapper(label, required, helpText);
|
|
395
398
|
|
|
@@ -402,6 +405,8 @@
|
|
|
402
405
|
placeholder: placeholder || `Select ${label}`,
|
|
403
406
|
disabled,
|
|
404
407
|
use24Hour,
|
|
408
|
+
size,
|
|
409
|
+
variant,
|
|
405
410
|
onChange: (value) => {
|
|
406
411
|
set(fieldId, value);
|
|
407
412
|
if (onChange) { onChange(value); }
|
|
@@ -441,6 +446,7 @@
|
|
|
441
446
|
* @param {12|24} config.hourCycle - 12 or 24 hour format
|
|
442
447
|
* @param {string} config.granularity - 'day' | 'hour' | 'minute' | 'second'
|
|
443
448
|
* @param {string} config.size - 'small' | 'default' | 'large'
|
|
449
|
+
* @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'success' | 'borderless' | 'inline'
|
|
444
450
|
* @param {Date} config.fromDate - Min selectable date
|
|
445
451
|
* @param {Date} config.toDate - Max selectable date
|
|
446
452
|
* @param {string} config.helpText - Optional help text for tooltip
|
|
@@ -458,6 +464,7 @@
|
|
|
458
464
|
hourCycle = 12,
|
|
459
465
|
granularity = "minute",
|
|
460
466
|
size = "default",
|
|
467
|
+
variant,
|
|
461
468
|
fromDate,
|
|
462
469
|
toDate,
|
|
463
470
|
helpText = null,
|
|
@@ -488,6 +495,7 @@
|
|
|
488
495
|
hourCycle,
|
|
489
496
|
granularity,
|
|
490
497
|
size,
|
|
498
|
+
variant,
|
|
491
499
|
fromDate,
|
|
492
500
|
toDate,
|
|
493
501
|
onChange: (date) => {
|