@skalfa/skalfa-component 1.0.52 → 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) => {
@@ -17,6 +17,8 @@ export declare const InputImageComponent: React.FC<InputImageProps>;
17
17
  interface CropperProps {
18
18
  src: string;
19
19
  aspect: number;
20
+ fileName?: string;
21
+ fileType?: string;
20
22
  onDone?: (file: File) => void;
21
23
  }
22
24
  export declare const CanvasCropper: React.FC<CropperProps>;
@@ -16,6 +16,7 @@ const InputImageComponent = ({ name, label, tip, value, disabled, aspect, invali
16
16
  const [preview, setPreview] = (0, react_1.useState)("");
17
17
  const [drag, setDrag] = (0, react_1.useState)(false);
18
18
  const [cropSrc, setCropSrc] = (0, react_1.useState)(null);
19
+ const [cropFile, setCropFile] = (0, react_1.useState)(null);
19
20
  const [openCrop, setOpenCrop] = (0, react_1.useState)(false);
20
21
  const inputHandler = (0, _utils_1.useInputHandler)(name, value, validations, register, true, unregister);
21
22
  const [invalidMessage, setInvalidMessage] = (0, _utils_1.useValidation)(inputHandler.value, validations, invalid, inputHandler.idle);
@@ -35,6 +36,7 @@ const InputImageComponent = ({ name, label, tip, value, disabled, aspect, invali
35
36
  }
36
37
  }, [value]);
37
38
  const openCropper = (file) => {
39
+ setCropFile(file);
38
40
  const reader = new FileReader();
39
41
  reader.onload = () => {
40
42
  setCropSrc(reader.result);
@@ -68,10 +70,14 @@ const InputImageComponent = ({ name, label, tip, value, disabled, aspect, invali
68
70
  };
69
71
  const onCropCancel = () => {
70
72
  setOpenCrop(false);
73
+ setCropFile(null);
74
+ setCropSrc(null);
71
75
  inputRef.current.value = "";
72
76
  };
73
77
  const remove = () => {
74
78
  setPreview("");
79
+ setCropFile(null);
80
+ setCropSrc(null);
75
81
  onChange?.(null);
76
82
  inputRef.current && (inputRef.current.value = "");
77
83
  };
@@ -99,12 +105,11 @@ const InputImageComponent = ({ name, label, tip, value, disabled, aspect, invali
99
105
  }, onDragOver: (e) => e.preventDefault(), onDragEnter: (e) => {
100
106
  e.preventDefault();
101
107
  setDrag(true);
102
- }, onDragLeave: () => setDrag(false), onDrop: onDropFile, children: [preview && !containerAspect ? ((0, jsx_runtime_1.jsxs)("div", { className: "relative group w-full h-full flex items-center justify-center", children: [(0, jsx_runtime_1.jsx)("img", { src: preview, alt: "Preview", className: "w-full h-auto max-h-[400px] object-contain rounded-lg" }), (0, jsx_runtime_1.jsxs)("div", { className: "absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity duration-200 flex flex-col items-center justify-center text-white rounded-lg", children: [(0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { icon: "solid/images", className: "text-3xl mb-2" }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-center", children: "Klik atau seret untuk ganti Gambar" })] })] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "input-image-dropzone-content", children: [(0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { icon: drag ? "solid/hand-holding" : "solid/images", className: "text-3xl" }), (0, jsx_runtime_1.jsx)("p", { className: "input-image-dropzone-text", children: drag ? "Letakkan di sini" : preview ? "Klik atau seret untuk ganti Gambar" : "Klik atau seret gambar" })] })), (0, jsx_runtime_1.jsx)("input", { id: randomId, ref: inputRef, type: "file", accept: "image/*", className: "hidden", disabled: disabled, onChange: onUpload })] }) }), preview && !disabled && ((0, jsx_runtime_1.jsx)(Button_component_1.ButtonComponent, { icon: "solid/times", paint: "danger", size: "sm", className: "absolute top-10 right-4", onClick: remove })), 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)(Modal_component_1.ModalComponent, { show: openCrop, onClose: onCropCancel, title: "Sesuaikan Gambar", className: "w-fit max-w-[90vw]", children: cropSrc && ((0, jsx_runtime_1.jsx)("div", { className: "p-4", children: (0, jsx_runtime_1.jsx)(exports.CanvasCropper, { src: cropSrc, aspect: eval(currentAspect.replace(":", "/")), onDone: onCropDone }) })) })) : ((0, jsx_runtime_1.jsx)(BottomSheet_component_1.BottomSheetComponent, { show: openCrop, onClose: onCropCancel, size: 400, children: cropSrc && ((0, jsx_runtime_1.jsx)("div", { className: "p-4", children: (0, jsx_runtime_1.jsx)(exports.CanvasCropper, { src: cropSrc, aspect: eval(currentAspect.replace(":", "/")), onDone: onCropDone }) })) }))] }));
108
+ }, onDragLeave: () => setDrag(false), onDrop: onDropFile, children: [preview && !containerAspect ? ((0, jsx_runtime_1.jsxs)("div", { className: "relative group w-full h-full flex items-center justify-center", children: [(0, jsx_runtime_1.jsx)("img", { src: preview, alt: "Preview", className: "w-full h-auto max-h-[400px] object-contain rounded-lg" }), (0, jsx_runtime_1.jsxs)("div", { className: "absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity duration-200 flex flex-col items-center justify-center text-white rounded-lg", children: [(0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { icon: "solid/images", className: "text-3xl mb-2" }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-center", children: "Klik atau seret untuk ganti Gambar" })] })] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "input-image-dropzone-content", children: [(0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { icon: drag ? "solid/hand-holding" : "solid/images", className: "text-3xl" }), (0, jsx_runtime_1.jsx)("p", { className: "input-image-dropzone-text", children: drag ? "Letakkan di sini" : preview ? "Klik atau seret untuk ganti Gambar" : "Klik atau seret gambar" })] })), (0, jsx_runtime_1.jsx)("input", { id: randomId, ref: inputRef, type: "file", accept: "image/*", className: "hidden", disabled: disabled, onChange: onUpload })] }) }), preview && !disabled && ((0, jsx_runtime_1.jsx)(Button_component_1.ButtonComponent, { icon: "solid/times", paint: "danger", size: "sm", className: "absolute top-10 right-4", onClick: remove })), 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)(Modal_component_1.ModalComponent, { show: openCrop, onClose: onCropCancel, title: "Sesuaikan Gambar", className: "w-fit max-w-[90vw]", children: cropSrc && ((0, jsx_runtime_1.jsx)("div", { className: "p-4", children: (0, jsx_runtime_1.jsx)(exports.CanvasCropper, { src: cropSrc, aspect: eval(currentAspect.replace(":", "/")), fileName: cropFile?.name, fileType: cropFile?.type, onDone: onCropDone }) })) })) : ((0, jsx_runtime_1.jsx)(BottomSheet_component_1.BottomSheetComponent, { show: openCrop, onClose: onCropCancel, size: 400, children: cropSrc && ((0, jsx_runtime_1.jsx)("div", { className: "p-4", children: (0, jsx_runtime_1.jsx)(exports.CanvasCropper, { src: cropSrc, aspect: eval(currentAspect.replace(":", "/")), fileName: cropFile?.name, fileType: cropFile?.type, onDone: onCropDone }) })) }))] }));
103
109
  };
104
110
  exports.InputImageComponent = InputImageComponent;
105
- const CanvasCropper = ({ src, aspect, onDone, }) => {
111
+ const CanvasCropper = ({ src, aspect, fileName, fileType, onDone, }) => {
106
112
  const canvasRef = (0, react_1.useRef)(null);
107
- const previewRef = (0, react_1.useRef)(null);
108
113
  const pinchDistRef = (0, react_1.useRef)(0);
109
114
  const pinchStartZoomRef = (0, react_1.useRef)(1);
110
115
  const [img, setImg] = (0, react_1.useState)(null);
@@ -130,8 +135,8 @@ const CanvasCropper = ({ src, aspect, onDone, }) => {
130
135
  canvas.width = CROP_W;
131
136
  canvas.height = CROP_SIZE;
132
137
  ctx.clearRect(0, 0, canvas.width, canvas.height);
133
- const scaledW = img.width * zoom;
134
- const scaledH = img.height * zoom;
138
+ const scaledW = img.naturalWidth * zoom;
139
+ const scaledH = img.naturalHeight * zoom;
135
140
  const x = pos.x - scaledW / 2 + CROP_W / 2;
136
141
  const y = pos.y - scaledH / 2 + CROP_SIZE / 2;
137
142
  ctx.drawImage(img, x, y, scaledW, scaledH);
@@ -178,19 +183,50 @@ const CanvasCropper = ({ src, aspect, onDone, }) => {
178
183
  const performCrop = () => {
179
184
  if (!img)
180
185
  return;
181
- const preview = previewRef.current;
182
- const pctx = preview.getContext("2d");
183
- preview.width = CROP_W;
184
- preview.height = CROP_SIZE;
185
- const scaledW = img.width * zoom;
186
- const scaledH = img.height * zoom;
187
- const x = pos.x - scaledW / 2 + CROP_W / 2;
188
- const y = pos.y - scaledH / 2 + CROP_SIZE / 2;
189
- pctx.drawImage(img, x, y, scaledW, scaledH);
190
- preview.toBlob((blob) => {
191
- const file = new File([blob], "cropped.png", { type: "image/png" });
186
+ const scaleFactor = 1 / zoom;
187
+ const scaledW = img.naturalWidth * zoom;
188
+ const scaledH = img.naturalHeight * zoom;
189
+ const cropDisplayX = (scaledW - CROP_W) / 2 - pos.x;
190
+ const cropDisplayY = (scaledH - CROP_SIZE) / 2 - pos.y;
191
+ const sourceX = cropDisplayX * scaleFactor;
192
+ const sourceY = cropDisplayY * scaleFactor;
193
+ const sourceW = CROP_W * scaleFactor;
194
+ const sourceH = CROP_SIZE * scaleFactor;
195
+ const sx = Math.max(0, Math.min(img.naturalWidth - 1, sourceX));
196
+ const sy = Math.max(0, Math.min(img.naturalHeight - 1, sourceY));
197
+ const sw = Math.min(sourceW, img.naturalWidth - sx);
198
+ const sh = Math.min(sourceH, img.naturalHeight - sy);
199
+ let targetW = Math.round(sw);
200
+ let targetH = Math.round(sh);
201
+ const MAX_DIMENSION = 2560;
202
+ if (targetW > MAX_DIMENSION || targetH > MAX_DIMENSION) {
203
+ if (targetW >= targetH) {
204
+ targetH = Math.round((targetH * MAX_DIMENSION) / targetW);
205
+ targetW = MAX_DIMENSION;
206
+ }
207
+ else {
208
+ targetW = Math.round((targetW * MAX_DIMENSION) / targetH);
209
+ targetH = MAX_DIMENSION;
210
+ }
211
+ }
212
+ const outputCanvas = document.createElement("canvas");
213
+ outputCanvas.width = targetW;
214
+ outputCanvas.height = targetH;
215
+ const octx = outputCanvas.getContext("2d");
216
+ if (!octx)
217
+ return;
218
+ octx.imageSmoothingEnabled = true;
219
+ octx.imageSmoothingQuality = "high";
220
+ octx.drawImage(img, sx, sy, sw, sh, 0, 0, targetW, targetH);
221
+ const type = fileType || "image/jpeg";
222
+ const name = fileName || (type === "image/png" ? "cropped.png" : "cropped.jpg");
223
+ const quality = type === "image/png" ? undefined : 0.92;
224
+ outputCanvas.toBlob((blob) => {
225
+ if (!blob)
226
+ return;
227
+ const file = new File([blob], name, { type, lastModified: Date.now() });
192
228
  onDone?.(file);
193
- });
229
+ }, type, quality);
194
230
  };
195
231
  const handleWheel = (e) => {
196
232
  e.preventDefault();
@@ -276,6 +312,6 @@ const CanvasCropper = ({ src, aspect, onDone, }) => {
276
312
  setDragging(false);
277
313
  pinchDistRef.current = 0;
278
314
  };
279
- return ((0, jsx_runtime_1.jsxs)("div", { style: { touchAction: "none" }, children: [(0, jsx_runtime_1.jsxs)("div", { className: "input-image-cropper-wrapper", style: { width: CROP_W, height: CROP_SIZE }, onMouseMove: handleMove, onMouseUp: handleUp, onMouseLeave: handleUp, onWheel: handleWheel, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, children: [(0, jsx_runtime_1.jsx)("canvas", { ref: canvasRef, width: CROP_W, height: CROP_SIZE, onMouseDown: handleDown, className: "cursor-move" }), (0, jsx_runtime_1.jsxs)("div", { className: "input-image-cropper-grid-container", style: { width: CROP_W, height: CROP_SIZE }, children: [(0, jsx_runtime_1.jsx)("div", { className: "input-image-cropper-grid-border" }), (0, jsx_runtime_1.jsxs)("div", { className: "absolute inset-0", children: [(0, jsx_runtime_1.jsx)("div", { className: "input-image-cropper-grid-line-h input-image-cropper-grid-line-h-1" }), (0, jsx_runtime_1.jsx)("div", { className: "input-image-cropper-grid-line-h input-image-cropper-grid-line-h-2" }), (0, jsx_runtime_1.jsx)("div", { className: "input-image-cropper-grid-line-v input-image-cropper-grid-line-v-1" }), (0, jsx_runtime_1.jsx)("div", { className: "input-image-cropper-grid-line-v input-image-cropper-grid-line-v-2" })] })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-4 pb-2", children: (0, jsx_runtime_1.jsx)(Button_component_1.ButtonComponent, { label: "Selesai", variant: "outline", onClick: performCrop, block: true }) }), (0, jsx_runtime_1.jsx)("canvas", { ref: previewRef, className: "hidden" })] }));
315
+ return ((0, jsx_runtime_1.jsxs)("div", { style: { touchAction: "none" }, children: [(0, jsx_runtime_1.jsxs)("div", { className: "input-image-cropper-wrapper", style: { width: CROP_W, height: CROP_SIZE }, onMouseMove: handleMove, onMouseUp: handleUp, onMouseLeave: handleUp, onWheel: handleWheel, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, children: [(0, jsx_runtime_1.jsx)("canvas", { ref: canvasRef, width: CROP_W, height: CROP_SIZE, onMouseDown: handleDown, className: "cursor-move" }), (0, jsx_runtime_1.jsxs)("div", { className: "input-image-cropper-grid-container", style: { width: CROP_W, height: CROP_SIZE }, children: [(0, jsx_runtime_1.jsx)("div", { className: "input-image-cropper-grid-border" }), (0, jsx_runtime_1.jsxs)("div", { className: "absolute inset-0", children: [(0, jsx_runtime_1.jsx)("div", { className: "input-image-cropper-grid-line-h input-image-cropper-grid-line-h-1" }), (0, jsx_runtime_1.jsx)("div", { className: "input-image-cropper-grid-line-h input-image-cropper-grid-line-h-2" }), (0, jsx_runtime_1.jsx)("div", { className: "input-image-cropper-grid-line-v input-image-cropper-grid-line-v-1" }), (0, jsx_runtime_1.jsx)("div", { className: "input-image-cropper-grid-line-v input-image-cropper-grid-line-v-2" })] })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-4 pb-2", children: (0, jsx_runtime_1.jsx)(Button_component_1.ButtonComponent, { label: "Selesai", variant: "outline", onClick: performCrop, block: true }) })] }));
280
316
  };
281
317
  exports.CanvasCropper = CanvasCropper;
@@ -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;