@skalfa/skalfa-component 1.0.53 → 1.0.54

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.
@@ -16,6 +16,7 @@ export interface InputDateProps extends Omit<InputHTMLAttributes<HTMLInputElemen
16
16
  }
17
17
  export declare function InputDateComponent({ label, tip, leftIcon, rightIcon, value, invalid, validations, register, unregister, onChange, className, ...props }: InputDateProps): import("@types/react").JSX.Element;
18
18
  export interface InputDatePickerProps {
19
+ value?: string;
19
20
  onChange?: (date: string) => void;
20
21
  minDate?: string;
21
22
  maxDate?: string;
@@ -27,45 +27,62 @@ function InputDateComponent({ label, tip, leftIcon, rightIcon, value, invalid, v
27
27
  const [invalidMessage] = (0, _utils_1.useValidation)(inputHandler.value, validations, invalid, inputHandler.idle);
28
28
  return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "input-container", children: [(0, jsx_runtime_1.jsxs)("label", { htmlFor: randomId, className: (0, _utils_1.cn)("input-label", props.disabled && "input-label-disabled", inputHandler.focus && "input-label-focus", !!invalidMessage && "input-label-error", (0, _utils_1.pcn)(className, "label"), props.disabled && (0, _utils_1.pcn)(className, "label", "disabled"), inputHandler.focus && (0, _utils_1.pcn)(className, "label", "focus"), !!invalidMessage && (0, _utils_1.pcn)(className, "label", "error")), children: [label, validations && _utils_1.validation.hasRules(validations, "required") && (0, jsx_runtime_1.jsx)("span", { className: "text-danger ml-1", children: "*" })] }), tip && ((0, jsx_runtime_1.jsx)("small", { className: (0, _utils_1.cn)("input-tip", props.disabled && "input-tip-disabled", (0, _utils_1.pcn)(className, "tip"), props.disabled && (0, _utils_1.pcn)(className, "tip", "disabled")), children: tip })), (0, jsx_runtime_1.jsx)(OutsideClick_component_1.OutsideClickComponent, { onOutsideClick: !isSm ? () => inputHandler.setFocus(false) : undefined, children: (0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsx)("input", { ...props, id: randomId, className: (0, _utils_1.cn)("input", leftIcon && "input-with-left-icon", rightIcon && "input-with-right-icon", (0, _utils_1.pcn)(className, "input"), !!invalidMessage && "input-error", !!invalidMessage && (0, _utils_1.pcn)(className, "input", "error")), value: inputHandler.value, onChange: (e) => {
29
29
  inputHandler.setValue(e.target.value);
30
- inputHandler.setValue(false);
30
+ inputHandler.setIdle(false);
31
31
  onChange?.(e.target.value);
32
32
  }, onFocus: (e) => {
33
33
  props.onFocus?.(e);
34
34
  inputHandler.setFocus(true);
35
35
  }, onBlur: (e) => {
36
36
  props.onBlur?.(e);
37
- }, autoComplete: "off", inputMode: isSm ? "none" : undefined }), leftIcon && ((0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { className: (0, _utils_1.cn)("input-icon", "input-icon-left", props.disabled && "input-icon-disabled", inputHandler.focus && "input-icon-focus", (0, _utils_1.pcn)(className, "icon"), props.disabled && (0, _utils_1.pcn)(className, "icon", "disabled"), inputHandler.focus && (0, _utils_1.pcn)(className, "icon", "focus")), icon: leftIcon })), rightIcon && ((0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { className: (0, _utils_1.cn)("input-icon", "input-icon-right", props.disabled && "input-icon-disabled", inputHandler.focus && "input-icon-focus", (0, _utils_1.pcn)(className, "icon"), props.disabled && (0, _utils_1.pcn)(className, "icon", "disabled"), inputHandler.focus && (0, _utils_1.pcn)(className, "icon", "focus")), icon: rightIcon })), !isSm && inputHandler.focus && ((0, jsx_runtime_1.jsx)("div", { className: "input-date-picker-popover", children: (0, jsx_runtime_1.jsx)(exports.InputDatePickerComponent, { onChange: (e) => {
37
+ }, autoComplete: "off", inputMode: isSm ? "none" : undefined }), leftIcon && ((0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { className: (0, _utils_1.cn)("input-icon", "input-icon-left", props.disabled && "input-icon-disabled", inputHandler.focus && "input-icon-focus", (0, _utils_1.pcn)(className, "icon"), props.disabled && (0, _utils_1.pcn)(className, "icon", "disabled"), inputHandler.focus && (0, _utils_1.pcn)(className, "icon", "focus")), icon: leftIcon })), rightIcon && ((0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { className: (0, _utils_1.cn)("input-icon", "input-icon-right", props.disabled && "input-icon-disabled", inputHandler.focus && "input-icon-focus", (0, _utils_1.pcn)(className, "icon"), props.disabled && (0, _utils_1.pcn)(className, "icon", "disabled"), inputHandler.focus && (0, _utils_1.pcn)(className, "icon", "focus")), icon: rightIcon })), !isSm && inputHandler.focus && ((0, jsx_runtime_1.jsx)("div", { className: "input-date-picker-popover", children: (0, jsx_runtime_1.jsx)(exports.InputDatePickerComponent, { value: inputHandler.value, onChange: (e) => {
38
38
  inputHandler.setValue(e);
39
+ inputHandler.setIdle(false);
39
40
  onChange?.(e);
40
- } }) }))] }) }), invalidMessage && ((0, jsx_runtime_1.jsx)("small", { className: (0, _utils_1.cn)("input-error-message", (0, _utils_1.pcn)(className, "error")), children: invalidMessage }))] }), isSm && ((0, jsx_runtime_1.jsx)(BottomSheet_component_1.BottomSheetComponent, { show: inputHandler.focus, onClose: () => inputHandler.setFocus(false), size: 380, footer: (0, jsx_runtime_1.jsx)("div", { className: "p-4", children: (0, jsx_runtime_1.jsx)(Button_component_1.ButtonComponent, { label: "Selesai", variant: "outline", onClick: () => inputHandler.setFocus(false), block: true }) }), children: (0, jsx_runtime_1.jsx)("div", { className: "p-4", children: (0, jsx_runtime_1.jsx)(exports.InputDatePickerComponent, { onChange: (e) => {
41
+ inputHandler.setFocus(false);
42
+ } }) }))] }) }), invalidMessage && ((0, jsx_runtime_1.jsx)("small", { className: (0, _utils_1.cn)("input-error-message", (0, _utils_1.pcn)(className, "error")), children: invalidMessage }))] }), isSm && ((0, jsx_runtime_1.jsx)(BottomSheet_component_1.BottomSheetComponent, { show: inputHandler.focus, onClose: () => inputHandler.setFocus(false), size: 380, footer: (0, jsx_runtime_1.jsx)("div", { className: "p-4", children: (0, jsx_runtime_1.jsx)(Button_component_1.ButtonComponent, { label: "Selesai", variant: "outline", onClick: () => inputHandler.setFocus(false), block: true }) }), children: (0, jsx_runtime_1.jsx)("div", { className: "p-4", children: (0, jsx_runtime_1.jsx)(exports.InputDatePickerComponent, { value: inputHandler.value, onChange: (e) => {
41
43
  inputHandler.setValue(e);
44
+ inputHandler.setIdle(false);
42
45
  onChange?.(e);
43
46
  } }) }) }))] }));
44
47
  }
45
48
  ;
46
- const InputDatePickerComponent = ({ onChange, minDate, maxDate, rightElement, }) => {
49
+ const InputDatePickerComponent = ({ value, onChange, minDate, maxDate, rightElement, }) => {
47
50
  const activeYearRef = (0, react_1.useRef)(null);
48
51
  const containerYearRef = (0, react_1.useRef)(null);
49
- const [currentDate, setCurrentDate] = (0, react_1.useState)((0, moment_1.default)());
50
- const [selectedDate, setSelectedDate] = (0, react_1.useState)((0, moment_1.default)());
52
+ const initialDate = (0, react_1.useMemo)(() => {
53
+ return value && (0, moment_1.default)(value).isValid() ? (0, moment_1.default)(value) : (0, moment_1.default)();
54
+ }, [value]);
55
+ const [currentDate, setCurrentDate] = (0, react_1.useState)(initialDate);
56
+ const [selectedDate, setSelectedDate] = (0, react_1.useState)(initialDate);
57
+ (0, react_1.useEffect)(() => {
58
+ if (value && (0, moment_1.default)(value).isValid()) {
59
+ const m = (0, moment_1.default)(value);
60
+ setCurrentDate(m);
61
+ setSelectedDate(m);
62
+ }
63
+ }, [value]);
51
64
  const startDate = (0, moment_1.default)(currentDate).startOf("month").startOf("week");
52
65
  const endDate = (0, moment_1.default)(currentDate).endOf("month").endOf("week");
53
- const handlePrevMonth = () => setCurrentDate((0, moment_1.default)(currentDate).subtract(1, "month"));
54
- const handleNextMonth = () => setCurrentDate((0, moment_1.default)(currentDate).add(1, "month"));
66
+ const handlePrevMonth = (e) => {
67
+ e?.preventDefault();
68
+ e?.stopPropagation();
69
+ setCurrentDate((0, moment_1.default)(currentDate).subtract(1, "month"));
70
+ };
71
+ const handleNextMonth = (e) => {
72
+ e?.preventDefault();
73
+ e?.stopPropagation();
74
+ setCurrentDate((0, moment_1.default)(currentDate).add(1, "month"));
75
+ };
55
76
  const handleDateClick = (date) => {
56
- if ((minDate && date.isBefore((0, moment_1.default)(minDate))) || (maxDate && date.isAfter((0, moment_1.default)(maxDate)))) {
77
+ if ((minDate && date.isBefore((0, moment_1.default)(minDate), "day")) || (maxDate && date.isAfter((0, moment_1.default)(maxDate), "day"))) {
57
78
  return;
58
79
  }
59
80
  setSelectedDate(date);
60
81
  onChange?.(date.format("YYYY-MM-DD"));
61
82
  };
62
83
  const renderDays = () => {
63
- const days = [];
64
- const startDay = (0, moment_1.default)(startDate);
65
- for (let i = 0; i < 7; i++) {
66
- days.push((0, jsx_runtime_1.jsx)("div", { className: "text-center font-bold", children: startDay.add(i, "days").format("dd") }, i));
67
- }
68
- return days;
84
+ const dayNames = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
85
+ return dayNames.map((d, i) => ((0, jsx_runtime_1.jsx)("div", { className: "text-center text-[11px] font-semibold text-light-foreground", children: d }, i)));
69
86
  };
70
87
  const renderCells = () => {
71
88
  const rows = [];
@@ -74,35 +91,36 @@ const InputDatePickerComponent = ({ onChange, minDate, maxDate, rightElement, })
74
91
  while (day.isBefore(endDate) || day.isSame(endDate, "day")) {
75
92
  for (let i = 0; i < 7; i++) {
76
93
  const cloneDay = (0, moment_1.default)(day);
77
- days.push((0, jsx_runtime_1.jsx)("div", { className: `w-8 aspect-square flex items-center justify-center text-center rounded-lg transition-all
78
- ${day.isSame(currentDate, "month") ? "text-foreground" : "text-light-foreground"}
79
- ${day.isSame(selectedDate, "day") ? "bg-primary text-background" : "hover:bg-light-primary"}
80
- ${day.isSame((0, moment_1.default)(), "day") ? "border !border-primary" : "hover:bg-light-primary"}
81
- ${(minDate && day.isBefore((0, moment_1.default)(minDate))) || (maxDate && day.isAfter((0, moment_1.default)(maxDate))) ? "opacity-10 cursor-not-allowed" : "cursor-pointer"}`, onClick: () => handleDateClick(cloneDay), children: day.format("D") }, day.toString()));
94
+ const isCurrentMonth = day.isSame(currentDate, "month");
95
+ const isSelected = day.isSame(selectedDate, "day");
96
+ const isToday = day.isSame((0, moment_1.default)(), "day");
97
+ const isDisabled = (minDate && day.isBefore((0, moment_1.default)(minDate), "day")) ||
98
+ (maxDate && day.isAfter((0, moment_1.default)(maxDate), "day"));
99
+ days.push((0, jsx_runtime_1.jsx)("button", { type: "button", className: (0, _utils_1.cn)("w-8 h-8 text-xs flex items-center justify-center text-center rounded-lg transition-all border-none select-none", isCurrentMonth ? "text-foreground font-medium" : "text-light-foreground/40", isSelected ? "bg-primary text-white font-bold hover:bg-primary" : (isToday ? "border border-primary font-semibold hover:bg-light-primary" : "hover:bg-light-primary"), isDisabled ? "opacity-20 cursor-not-allowed pointer-events-none" : "cursor-pointer"), onClick: () => !isDisabled && handleDateClick(cloneDay), children: day.format("D") }, day.format("YYYY-MM-DD")));
82
100
  day.add(1, "day");
83
101
  }
84
- rows.push((0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-7 gap-1", children: days }, day.toString()));
102
+ rows.push((0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-7 gap-1", children: days }, day.format("YYYY-MM-DD-row")));
85
103
  days = [];
86
104
  }
87
105
  return rows;
88
106
  };
89
107
  const years = (0, react_1.useMemo)(() => {
90
108
  const dumpYears = [];
91
- for (let i = 1945; i <= (0, moment_1.default)().year(); i++) {
109
+ for (let i = 1940; i <= (0, moment_1.default)().year() + 10; i++) {
92
110
  dumpYears.push(i);
93
111
  }
94
112
  return dumpYears;
95
113
  }, []);
96
114
  (0, react_1.useEffect)(() => {
97
115
  if (activeYearRef.current && containerYearRef.current) {
98
- containerYearRef.current.scrollTo({
99
- top: activeYearRef.current.offsetTop - containerYearRef.current.offsetTop,
100
- });
116
+ const container = containerYearRef.current;
117
+ const activeEl = activeYearRef.current;
118
+ container.scrollTop = activeEl.offsetTop - container.offsetTop - (container.clientHeight / 2) + (activeEl.clientHeight / 2);
101
119
  }
102
- }, []);
103
- return ((0, jsx_runtime_1.jsxs)("div", { className: "w-full flex gap-2 max-h-[260]", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-1/5 overflow-y-auto input-scroll pr-1", ref: containerYearRef, children: (0, jsx_runtime_1.jsx)("div", { className: "flex flex-col", children: years?.map((item) => {
120
+ }, [currentDate.year()]);
121
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "w-full flex gap-2.5 h-[260px] select-none", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-[68px] shrink-0 h-full overflow-y-auto input-scroll pr-1 border-r border-stroke", ref: containerYearRef, children: (0, jsx_runtime_1.jsx)("div", { className: "flex flex-col gap-0.5", children: years?.map((item) => {
104
122
  const isActive = currentDate?.year() === item;
105
- return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)("div", { ref: isActive ? activeYearRef : null, className: `py-1 px-2 font-semibold rounded-[6px] cursor-pointer ${isActive && "bg-light-primary"}`, onClick: () => setCurrentDate((0, moment_1.default)().set("year", item)), children: item }, item) }));
106
- }) }) }), (0, jsx_runtime_1.jsxs)("div", { className: "w-4/5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center mb-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: handlePrevMonth, className: "w-8 text-sm aspect-square rounded-full cursor-pointer", children: (0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { icon: "solid/chevron-left" }) }), (0, jsx_runtime_1.jsx)("h2", { className: "font-semibold", children: currentDate.format("MMMM") }), (0, jsx_runtime_1.jsx)("button", { onClick: handleNextMonth, className: "w-8 text-sm aspect-square rounded-full cursor-pointer", children: (0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { icon: "solid/chevron-right" }) })] }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-7 gap-1 mb-2", children: renderDays() }), (0, jsx_runtime_1.jsx)("div", { children: renderCells() })] }), rightElement && (0, jsx_runtime_1.jsx)("div", { children: rightElement })] }));
123
+ return ((0, jsx_runtime_1.jsx)("div", { ref: isActive ? activeYearRef : null, className: (0, _utils_1.cn)("py-1 px-2 font-semibold rounded-md cursor-pointer transition-colors text-center select-none", isActive ? "bg-primary text-white" : "hover:bg-light-primary text-foreground"), onClick: () => setCurrentDate((0, moment_1.default)(currentDate).set("year", item)), children: item }, item));
124
+ }) }) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 flex flex-col justify-between pl-0.5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center mb-1.5", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: handlePrevMonth, className: "w-7 h-7 flex items-center justify-center text-xs rounded-full hover:bg-light-primary transition-colors cursor-pointer border-none bg-transparent", children: (0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { icon: "solid/chevron-left", className: "w-3.5 h-3.5" }) }), (0, jsx_runtime_1.jsx)("h2", { className: "font-semibold", children: currentDate.format("MMMM YYYY") }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: handleNextMonth, className: "w-7 h-7 flex items-center justify-center text-xs rounded-full hover:bg-light-primary transition-colors cursor-pointer border-none bg-transparent", children: (0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { icon: "solid/chevron-right", className: "w-3.5 h-3.5" }) })] }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-7 gap-1 mb-1", children: renderDays() }), (0, jsx_runtime_1.jsx)("div", { className: "flex flex-col gap-1", children: renderCells() })] }), rightElement && (0, jsx_runtime_1.jsx)("div", { children: rightElement })] }));
107
125
  };
108
126
  exports.InputDatePickerComponent = InputDatePickerComponent;
@@ -55,7 +55,7 @@ function InputDatetimeComponent({ label, tip, leftIcon, rightIcon, value, invali
55
55
  label: "Jam",
56
56
  value: 'time'
57
57
  },
58
- ], active: pickerType, onChange: (e) => setPickerType(e), className: "mb-4" }), pickerType === "date" ? ((0, jsx_runtime_1.jsx)(InputDate_component_1.InputDatePickerComponent, { onChange: (e) => {
58
+ ], active: pickerType, onChange: (e) => setPickerType(e), className: "mb-4" }), pickerType === "date" ? ((0, jsx_runtime_1.jsx)(InputDate_component_1.InputDatePickerComponent, { value: dateValue, onChange: (e) => {
59
59
  setDateValue(e);
60
60
  handleChange(e, timeValue);
61
61
  } })) : ((0, jsx_runtime_1.jsx)(InputTime_component_1.InputTimePickerComponent, { onChange: (e) => {
@@ -70,7 +70,7 @@ function InputDatetimeComponent({ label, tip, leftIcon, rightIcon, value, invali
70
70
  label: "Jam",
71
71
  value: 'time'
72
72
  },
73
- ], active: pickerType, onChange: (e) => setPickerType(e), className: "mb-4" }), pickerType === "date" ? ((0, jsx_runtime_1.jsx)(InputDate_component_1.InputDatePickerComponent, { onChange: (e) => {
73
+ ], active: pickerType, onChange: (e) => setPickerType(e), className: "mb-4" }), pickerType === "date" ? ((0, jsx_runtime_1.jsx)(InputDate_component_1.InputDatePickerComponent, { value: dateValue, onChange: (e) => {
74
74
  setDateValue(e);
75
75
  handleChange(e, timeValue);
76
76
  } })) : ((0, jsx_runtime_1.jsx)(InputTime_component_1.InputTimePickerComponent, { onChange: (e) => {
@@ -8,16 +8,18 @@ export interface InputTimeProps extends Omit<InputHTMLAttributes<HTMLInputElemen
8
8
  value?: string;
9
9
  invalid?: string;
10
10
  validations?: ValidationRules;
11
+ showSeconds?: boolean;
11
12
  onChange?: (value: string) => any;
12
13
  register?: (name: string, validations?: ValidationRules) => void;
13
14
  unregister?: (name: string) => void;
14
15
  /** Use custom class with: "label::", "tip::", "error::", "icon::". */
15
16
  className?: string;
16
17
  }
17
- export declare function InputTimeComponent({ label, tip, leftIcon, rightIcon, value, invalid, validations, register, unregister, onChange, className, ...props }: InputTimeProps): import("@types/react").JSX.Element;
18
- interface InputTimePickerProps {
18
+ export declare function InputTimeComponent({ label, tip, leftIcon, rightIcon, value, invalid, validations, showSeconds, register, unregister, onChange, className, ...props }: InputTimeProps): import("@types/react").JSX.Element;
19
+ export interface InputTimePickerProps {
20
+ value?: string;
21
+ showSeconds?: boolean;
19
22
  onChange?: (time: string) => void;
20
23
  rightElement?: ReactNode;
21
24
  }
22
25
  export declare const InputTimePickerComponent: FC<InputTimePickerProps>;
23
- export {};
@@ -10,7 +10,7 @@ const _utils_1 = require("@utils");
10
10
  const BottomSheet_component_1 = require("../modal/BottomSheet.component");
11
11
  const Button_component_1 = require("../button/Button.component");
12
12
  const OutsideClick_component_1 = require("../wrap/OutsideClick.component");
13
- function InputTimeComponent({ label, tip, leftIcon, rightIcon, value, invalid, validations, register, unregister, onChange, className = "", ...props }) {
13
+ function InputTimeComponent({ label, tip, leftIcon, rightIcon, value, invalid, validations, showSeconds, register, unregister, onChange, className = "", ...props }) {
14
14
  const { isSm } = (0, _utils_1.useResponsive)();
15
15
  // =========================>
16
16
  // ## Initial
@@ -28,48 +28,129 @@ function InputTimeComponent({ label, tip, leftIcon, rightIcon, value, invalid, v
28
28
  }, onFocus: (e) => {
29
29
  props.onFocus?.(e);
30
30
  inputHandler.setFocus(true);
31
- }, autoComplete: "off", inputMode: isSm ? "none" : undefined }), leftIcon && ((0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { className: (0, _utils_1.cn)("input-icon", "input-icon-left", props.disabled && "input-icon-disabled", inputHandler.focus && "input-icon-focus", (0, _utils_1.pcn)(className, "icon")), icon: leftIcon })), rightIcon && ((0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { className: (0, _utils_1.cn)("input-icon", "input-icon-right", props.disabled && "input-icon-disabled", inputHandler.focus && "input-icon-focus", (0, _utils_1.pcn)(className, "icon")), icon: rightIcon })), !isSm && inputHandler.focus && ((0, jsx_runtime_1.jsx)("div", { className: "input-time-picker-popover", children: (0, jsx_runtime_1.jsx)(exports.InputTimePickerComponent, { onChange: (time) => {
31
+ }, autoComplete: "off", inputMode: isSm ? "none" : undefined }), leftIcon && ((0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { className: (0, _utils_1.cn)("input-icon", "input-icon-left", props.disabled && "input-icon-disabled", inputHandler.focus && "input-icon-focus", (0, _utils_1.pcn)(className, "icon")), icon: leftIcon })), rightIcon && ((0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { className: (0, _utils_1.cn)("input-icon", "input-icon-right", props.disabled && "input-icon-disabled", inputHandler.focus && "input-icon-focus", (0, _utils_1.pcn)(className, "icon")), icon: rightIcon })), !isSm && inputHandler.focus && ((0, jsx_runtime_1.jsx)("div", { className: "input-time-picker-popover", children: (0, jsx_runtime_1.jsx)(exports.InputTimePickerComponent, { value: inputHandler.value, showSeconds: showSeconds, onChange: (time) => {
32
32
  inputHandler.setValue(time);
33
+ inputHandler.setIdle(false);
33
34
  onChange?.(time);
34
- } }) }))] }) }), invalidMessage && ((0, jsx_runtime_1.jsx)("small", { className: (0, _utils_1.cn)("input-error-message", (0, _utils_1.pcn)(className, "error")), children: invalidMessage }))] }), isSm && ((0, jsx_runtime_1.jsx)(BottomSheet_component_1.BottomSheetComponent, { show: inputHandler.focus, onClose: () => inputHandler.setFocus(false), size: 380, footer: (0, jsx_runtime_1.jsx)("div", { className: "p-4", children: (0, jsx_runtime_1.jsx)(Button_component_1.ButtonComponent, { label: "Selesai", variant: "outline", onClick: () => inputHandler.setFocus(false), block: true }) }), children: (0, jsx_runtime_1.jsx)("div", { className: "p-4", children: (0, jsx_runtime_1.jsx)(exports.InputTimePickerComponent, { onChange: (time) => {
35
+ } }) }))] }) }), invalidMessage && ((0, jsx_runtime_1.jsx)("small", { className: (0, _utils_1.cn)("input-error-message", (0, _utils_1.pcn)(className, "error")), children: invalidMessage }))] }), isSm && ((0, jsx_runtime_1.jsx)(BottomSheet_component_1.BottomSheetComponent, { show: inputHandler.focus, onClose: () => inputHandler.setFocus(false), size: 380, footer: (0, jsx_runtime_1.jsx)("div", { className: "p-4", children: (0, jsx_runtime_1.jsx)(Button_component_1.ButtonComponent, { label: "Selesai", variant: "outline", onClick: () => inputHandler.setFocus(false), block: true }) }), children: (0, jsx_runtime_1.jsx)("div", { className: "p-4", children: (0, jsx_runtime_1.jsx)(exports.InputTimePickerComponent, { value: inputHandler.value, showSeconds: showSeconds, onChange: (time) => {
35
36
  inputHandler.setValue(time);
37
+ inputHandler.setIdle(false);
36
38
  onChange?.(time);
37
39
  } }) }) }))] }));
38
40
  }
39
41
  ;
40
- const InputTimePickerComponent = ({ onChange, rightElement, }) => {
41
- const [hour, setHour] = (0, react_1.useState)(0);
42
- const [minute, setMinute] = (0, react_1.useState)(0);
43
- const [second, setSecond] = (0, react_1.useState)(0);
42
+ const InputTimePickerComponent = ({ value, showSeconds = false, onChange, rightElement, }) => {
43
+ const parsed = (0, react_1.useMemo)(() => {
44
+ if (!value || typeof value !== "string") {
45
+ return { h: 0, m: 0, s: 0, hasValue: false };
46
+ }
47
+ const parts = value.split(":").map((p) => parseInt(p, 10));
48
+ return {
49
+ h: isNaN(parts[0]) ? 0 : Math.min(23, Math.max(0, parts[0])),
50
+ m: isNaN(parts[1]) ? 0 : Math.min(59, Math.max(0, parts[1])),
51
+ s: isNaN(parts[2]) ? 0 : Math.min(59, Math.max(0, parts[2])),
52
+ hasValue: true,
53
+ };
54
+ }, [value]);
55
+ const [hour, setHour] = (0, react_1.useState)(parsed.h);
56
+ const [minute, setMinute] = (0, react_1.useState)(parsed.m);
57
+ const [second, setSecond] = (0, react_1.useState)(parsed.s);
58
+ const shouldShowSeconds = showSeconds || (typeof value === "string" && value.split(":").length >= 3 && value.split(":")[2] !== "");
59
+ (0, react_1.useEffect)(() => {
60
+ if (parsed.hasValue) {
61
+ setHour(parsed.h);
62
+ setMinute(parsed.m);
63
+ setSecond(parsed.s);
64
+ }
65
+ }, [parsed]);
44
66
  const hours = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, "0"));
45
67
  const minutes = Array.from({ length: 60 }, (_, i) => String(i).padStart(2, "0"));
46
68
  const seconds = Array.from({ length: 60 }, (_, i) => String(i).padStart(2, "0"));
47
- const handleSelect = (type, val) => {
48
- if (type === "h")
49
- setHour(Number(val));
50
- if (type === "m")
51
- setMinute(Number(val));
52
- if (type === "s")
53
- setSecond(Number(val));
69
+ const presetContainerRef = (0, react_1.useRef)(null);
70
+ const activePresetRef = (0, react_1.useRef)(null);
71
+ const hourContainerRef = (0, react_1.useRef)(null);
72
+ const activeHourRef = (0, react_1.useRef)(null);
73
+ const minuteContainerRef = (0, react_1.useRef)(null);
74
+ const activeMinuteRef = (0, react_1.useRef)(null);
75
+ const secondContainerRef = (0, react_1.useRef)(null);
76
+ const activeSecondRef = (0, react_1.useRef)(null);
77
+ const scrollToActive = (container, active) => {
78
+ if (container && active) {
79
+ container.scrollTop = active.offsetTop - container.offsetTop - (container.clientHeight / 2) + (active.clientHeight / 2);
80
+ }
54
81
  };
55
82
  (0, react_1.useEffect)(() => {
56
- const formatted = `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}:${String(second).padStart(2, "0")}`;
83
+ scrollToActive(hourContainerRef.current, activeHourRef.current);
84
+ scrollToActive(minuteContainerRef.current, activeMinuteRef.current);
85
+ if (shouldShowSeconds) {
86
+ scrollToActive(secondContainerRef.current, activeSecondRef.current);
87
+ }
88
+ scrollToActive(presetContainerRef.current, activePresetRef.current);
89
+ }, []);
90
+ const emitChange = (newH, newM, newS) => {
91
+ const hh = String(newH).padStart(2, "0");
92
+ const mm = String(newM).padStart(2, "0");
93
+ const ss = String(newS).padStart(2, "0");
94
+ const formatted = shouldShowSeconds ? `${hh}:${mm}:${ss}` : `${hh}:${mm}`;
57
95
  onChange?.(formatted);
58
- }, [hour, minute, second]);
59
- const renderColumn = (items, type, activeValue) => ((0, jsx_runtime_1.jsx)("div", { className: "flex-1 overflow-y-auto text-center input-scroll", children: items.map((item) => {
60
- const active = Number(item) === activeValue;
61
- return ((0, jsx_runtime_1.jsx)("div", { onClick: () => handleSelect(type, item), className: (0, _utils_1.cn)("p-2 cursor-pointer rounded text-sm", active ? "bg-primary text-background font-semibold" : "hover:bg-light-primary"), children: item }, item));
62
- }) }));
96
+ };
97
+ const handleSelect = (type, val) => {
98
+ let newH = hour;
99
+ let newM = minute;
100
+ let newS = second;
101
+ if (type === "h") {
102
+ newH = val;
103
+ setHour(val);
104
+ }
105
+ else if (type === "m") {
106
+ newM = val;
107
+ setMinute(val);
108
+ }
109
+ else if (type === "s") {
110
+ newS = val;
111
+ setSecond(val);
112
+ }
113
+ emitChange(newH, newM, newS);
114
+ };
115
+ const handlePresetSelect = (presetStr) => {
116
+ const parts = presetStr.split(":").map(Number);
117
+ const newH = parts[0] ?? 0;
118
+ const newM = parts[1] ?? 0;
119
+ const newS = 0;
120
+ setHour(newH);
121
+ setMinute(newM);
122
+ setSecond(newS);
123
+ emitChange(newH, newM, newS);
124
+ };
63
125
  const timeSlots = (0, react_1.useMemo)(() => {
64
- const newTimeSlots = [];
65
- for (let i = 0; i <= 24 * 60; i += 30) {
66
- const hours = Math.floor(i / 60);
67
- const minutes = i % 60;
68
- const label = `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
69
- newTimeSlots.push(label);
126
+ const slots = [];
127
+ for (let i = 0; i < 24 * 60; i += 30) {
128
+ const h = Math.floor(i / 60);
129
+ const m = i % 60;
130
+ slots.push(`${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`);
70
131
  }
71
- return newTimeSlots;
132
+ return slots;
72
133
  }, []);
73
- return ((0, jsx_runtime_1.jsxs)("div", { className: "w-full max-h-[260] flex gap-2", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-1/3 overflow-y-auto bg-background rounded-[6px] input-scroll", children: timeSlots.map((time) => ((0, jsx_runtime_1.jsx)("div", { className: "p-2 text-sm rounded cursor-pointer hover:bg-light-primary text-center", onClick: () => onChange?.(time), children: time }, time))) }), (0, jsx_runtime_1.jsxs)("div", { className: "w-2/3 flex gap-2", children: [renderColumn(hours, "h", hour), renderColumn(minutes, "m", minute), renderColumn(seconds, "s", second)] }), rightElement && (0, jsx_runtime_1.jsx)("div", { children: rightElement })] }));
134
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "w-full flex gap-2 h-[260px] select-none text-xs", children: [(0, jsx_runtime_1.jsxs)("div", { className: "w-20 shrink-0 h-full flex flex-col border-r border-stroke pr-1.5", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-[11px] font-semibold text-light-foreground text-center py-1 mb-1 border-b border-stroke", children: "Preset" }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 overflow-y-auto input-scroll flex flex-col gap-0.5", ref: presetContainerRef, children: timeSlots.map((time) => {
135
+ const isPresetActive = `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}` === time;
136
+ return ((0, jsx_runtime_1.jsx)("div", { ref: isPresetActive ? activePresetRef : null, className: (0, _utils_1.cn)("py-1.5 px-1 text-center font-medium rounded-md cursor-pointer transition-colors", isPresetActive
137
+ ? "bg-primary text-white font-semibold"
138
+ : "hover:bg-light-primary text-foreground"), onClick: () => handlePresetSelect(time), children: time }, time));
139
+ }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 h-full flex flex-col", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-[11px] font-semibold text-light-foreground text-center py-1 mb-1 border-b border-stroke", children: "Jam" }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 overflow-y-auto input-scroll flex flex-col gap-0.5", ref: hourContainerRef, children: hours.map((item) => {
140
+ const active = Number(item) === hour;
141
+ return ((0, jsx_runtime_1.jsx)("div", { ref: active ? activeHourRef : null, onClick: () => handleSelect("h", Number(item)), className: (0, _utils_1.cn)("py-1.5 text-center font-medium rounded-md cursor-pointer transition-colors", active
142
+ ? "bg-primary text-white font-semibold"
143
+ : "hover:bg-light-primary text-foreground"), children: item }, item));
144
+ }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 h-full flex flex-col", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-[11px] font-semibold text-light-foreground text-center py-1 mb-1 border-b border-stroke", children: "Menit" }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 overflow-y-auto input-scroll flex flex-col gap-0.5", ref: minuteContainerRef, children: minutes.map((item) => {
145
+ const active = Number(item) === minute;
146
+ return ((0, jsx_runtime_1.jsx)("div", { ref: active ? activeMinuteRef : null, onClick: () => handleSelect("m", Number(item)), className: (0, _utils_1.cn)("py-1.5 text-center font-medium rounded-md cursor-pointer transition-colors", active
147
+ ? "bg-primary text-white font-semibold"
148
+ : "hover:bg-light-primary text-foreground"), children: item }, item));
149
+ }) })] }), shouldShowSeconds && ((0, jsx_runtime_1.jsxs)("div", { className: "flex-1 h-full flex flex-col", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-[11px] font-semibold text-light-foreground text-center py-1 mb-1 border-b border-stroke", children: "Detik" }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 overflow-y-auto input-scroll flex flex-col gap-0.5", ref: secondContainerRef, children: seconds.map((item) => {
150
+ const active = Number(item) === second;
151
+ return ((0, jsx_runtime_1.jsx)("div", { ref: active ? activeSecondRef : null, onClick: () => handleSelect("s", Number(item)), className: (0, _utils_1.cn)("py-1.5 text-center font-medium rounded-md cursor-pointer transition-colors", active
152
+ ? "bg-primary text-white font-semibold"
153
+ : "hover:bg-light-primary text-foreground"), children: item }, item));
154
+ }) })] })), rightElement && (0, jsx_runtime_1.jsx)("div", { children: rightElement })] }));
74
155
  };
75
156
  exports.InputTimePickerComponent = InputTimePickerComponent;
@@ -5,6 +5,8 @@ export interface SelectOptionProps {
5
5
  value: string | number;
6
6
  searchable?: string[];
7
7
  customLabel?: ReactNode;
8
+ customOption?: ((option: any) => ReactNode) | ReactNode;
9
+ [key: string]: any;
8
10
  }
9
11
  export interface SelectProps {
10
12
  name: string;
@@ -23,7 +25,8 @@ export interface SelectProps {
23
25
  options?: SelectOptionProps[];
24
26
  searchable?: boolean;
25
27
  serverOptionControl?: ApiType & {
26
- cacheName?: string | false;
28
+ cacheName?: string | boolean;
29
+ selectableOption?: string[];
27
30
  };
28
31
  idbOptionControl?: {
29
32
  store: string;
@@ -38,6 +41,8 @@ export interface SelectProps {
38
41
  maxShowOption?: number;
39
42
  creatable?: boolean | string;
40
43
  creatableLabel?: string;
44
+ customOption?: (option: any) => ReactNode;
45
+ selectableOption?: string[];
41
46
  onChange?: (value: any, data?: any) => any;
42
47
  register?: (name: string, validations?: ValidationRules) => void;
43
48
  unregister?: (name: string) => void;
@@ -46,4 +51,4 @@ export interface SelectProps {
46
51
  /** Use custom class with: "label::", "tip::", "error::", "icon::", "suggest::", "suggest-item::". */
47
52
  className?: string;
48
53
  }
49
- export declare function SelectComponent({ name, label, placeholder, tip, leftIcon, rightIcon, value, invalid, disabled, validations, multiple, autoFocus, clearable, options, searchable, serverOptionControl, idbOptionControl, serverSearchable, includedOptions, exceptOptions, tempOptions, newOption, maxShowOption, creatable, creatableLabel, register, unregister, onChange, onFocus, onBlur, className, }: SelectProps): import("@types/react").JSX.Element;
54
+ export declare function SelectComponent({ name, label, placeholder, tip, leftIcon, rightIcon, value, invalid, disabled, validations, multiple, autoFocus, clearable, options, searchable, serverOptionControl, idbOptionControl, serverSearchable, includedOptions, exceptOptions, tempOptions, newOption, maxShowOption, creatable, creatableLabel, customOption, selectableOption, register, unregister, onChange, onFocus, onBlur, className, }: SelectProps): import("@types/react").JSX.Element;