@skalfa/skalfa-component 1.0.34 → 1.0.36
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/dist/input/InputContent.component.js +63 -1
- package/dist/input/InputImage.component.js +26 -6
- package/dist/supervision/TableSupervision.component.d.ts +1 -1
- package/package.json +1 -1
- package/src/input/InputContent.component.tsx +78 -0
- package/src/input/InputImage.component.tsx +44 -11
- package/src/supervision/TableSupervision.component.tsx +1 -1
|
@@ -458,6 +458,68 @@ function InputContentComponent({ label, tip, className = "", value, invalid, val
|
|
|
458
458
|
}
|
|
459
459
|
}
|
|
460
460
|
}, [handleBold, handleItalic, handleUnderline, handleStrikethrough, handleLink, handleEditorChange, updateActiveStates]);
|
|
461
|
+
const handlePaste = (0, react_1.useCallback)((e) => {
|
|
462
|
+
const pastedText = e.clipboardData.getData("text/plain");
|
|
463
|
+
if (!pastedText)
|
|
464
|
+
return;
|
|
465
|
+
const urlRegex = /(\b(?:https?:\/\/|www\.)[^\s()<>]+(?:\([\w\d]+\)|[^.,?;:"')\s]))/gi;
|
|
466
|
+
if (urlRegex.test(pastedText)) {
|
|
467
|
+
e.preventDefault();
|
|
468
|
+
const escapeHtml = (text) => {
|
|
469
|
+
return text
|
|
470
|
+
.replace(/&/g, "&")
|
|
471
|
+
.replace(/</g, "<")
|
|
472
|
+
.replace(/>/g, ">")
|
|
473
|
+
.replace(/"/g, """)
|
|
474
|
+
.replace(/'/g, "'");
|
|
475
|
+
};
|
|
476
|
+
const tokens = [];
|
|
477
|
+
let lastIndex = 0;
|
|
478
|
+
let match;
|
|
479
|
+
urlRegex.lastIndex = 0;
|
|
480
|
+
while ((match = urlRegex.exec(pastedText)) !== null) {
|
|
481
|
+
if (match.index > lastIndex) {
|
|
482
|
+
tokens.push(escapeHtml(pastedText.substring(lastIndex, match.index)));
|
|
483
|
+
}
|
|
484
|
+
const url = match[0];
|
|
485
|
+
let href = url;
|
|
486
|
+
if (url.toLowerCase().startsWith("www.")) {
|
|
487
|
+
href = "https://" + url;
|
|
488
|
+
}
|
|
489
|
+
const escapedHref = escapeHtml(href);
|
|
490
|
+
const escapedUrl = escapeHtml(url);
|
|
491
|
+
tokens.push(`<a href="${escapedHref}" class="text-primary underline" data-link="${escapedHref}" target="_blank" rel="noopener">${escapedUrl}</a>`);
|
|
492
|
+
lastIndex = urlRegex.lastIndex;
|
|
493
|
+
}
|
|
494
|
+
if (lastIndex < pastedText.length) {
|
|
495
|
+
tokens.push(escapeHtml(pastedText.substring(lastIndex)));
|
|
496
|
+
}
|
|
497
|
+
const htmlContent = tokens.join("").replace(/\n/g, "<br>");
|
|
498
|
+
const sel = window.getSelection();
|
|
499
|
+
if (!sel || sel.rangeCount === 0)
|
|
500
|
+
return;
|
|
501
|
+
const range = sel.getRangeAt(0);
|
|
502
|
+
range.deleteContents();
|
|
503
|
+
const tempDiv = document.createElement("div");
|
|
504
|
+
tempDiv.innerHTML = htmlContent;
|
|
505
|
+
const fragment = document.createDocumentFragment();
|
|
506
|
+
let child;
|
|
507
|
+
while ((child = tempDiv.firstChild)) {
|
|
508
|
+
fragment.appendChild(child);
|
|
509
|
+
}
|
|
510
|
+
const lastNode = fragment.lastChild;
|
|
511
|
+
range.insertNode(fragment);
|
|
512
|
+
if (lastNode) {
|
|
513
|
+
const newRange = document.createRange();
|
|
514
|
+
newRange.setStartAfter(lastNode);
|
|
515
|
+
newRange.collapse(true);
|
|
516
|
+
sel.removeAllRanges();
|
|
517
|
+
sel.addRange(newRange);
|
|
518
|
+
}
|
|
519
|
+
saveSelection();
|
|
520
|
+
handleEditorChange();
|
|
521
|
+
}
|
|
522
|
+
}, [saveSelection, handleEditorChange]);
|
|
461
523
|
const renderControlItem = (0, react_1.useCallback)((item, index) => {
|
|
462
524
|
if (typeof item !== "string") {
|
|
463
525
|
return (0, jsx_runtime_1.jsx)("div", { children: item }, index);
|
|
@@ -555,7 +617,7 @@ function InputContentComponent({ label, tip, className = "", value, invalid, val
|
|
|
555
617
|
saveSelection();
|
|
556
618
|
handleEditorChange();
|
|
557
619
|
updateActiveStates();
|
|
558
|
-
}, onKeyDown: handleKeyDown, onMouseUp: () => {
|
|
620
|
+
}, onKeyDown: handleKeyDown, onPaste: handlePaste, onMouseUp: () => {
|
|
559
621
|
saveSelection();
|
|
560
622
|
updateActiveStates();
|
|
561
623
|
}, onKeyUp: () => {
|
|
@@ -9,7 +9,7 @@ const _utils_1 = require("@utils");
|
|
|
9
9
|
const BottomSheet_component_1 = require("../modal/BottomSheet.component");
|
|
10
10
|
const Button_component_1 = require("../button/Button.component");
|
|
11
11
|
const Modal_component_1 = require("../modal/Modal.component");
|
|
12
|
-
const InputImageComponent = ({ name, label, tip, value, disabled, aspect
|
|
12
|
+
const InputImageComponent = ({ name, label, tip, value, disabled, aspect, invalid, validations, onChange, register, unregister, className = "", }) => {
|
|
13
13
|
const { isSm } = (0, _utils_1.useResponsive)();
|
|
14
14
|
const randomId = (0, _utils_1.useInputRandomId)();
|
|
15
15
|
const inputRef = (0, react_1.useRef)(null);
|
|
@@ -19,6 +19,9 @@ const InputImageComponent = ({ name, label, tip, value, disabled, aspect = "1/1"
|
|
|
19
19
|
const [openCrop, setOpenCrop] = (0, react_1.useState)(false);
|
|
20
20
|
const inputHandler = (0, _utils_1.useInputHandler)(name, value, validations, register, true, unregister);
|
|
21
21
|
const [invalidMessage, setInvalidMessage] = (0, _utils_1.useValidation)(inputHandler.value, validations, invalid, inputHandler.idle);
|
|
22
|
+
const isCroppingEnabled = !!aspect;
|
|
23
|
+
const currentAspect = aspect || "1/1";
|
|
24
|
+
const containerAspect = (preview && !isCroppingEnabled) ? undefined : currentAspect;
|
|
22
25
|
(0, react_1.useEffect)(() => {
|
|
23
26
|
if (value) {
|
|
24
27
|
const url = typeof value === "object" ? URL.createObjectURL(value) : value;
|
|
@@ -48,7 +51,14 @@ const InputImageComponent = ({ name, label, tip, value, disabled, aspect = "1/1"
|
|
|
48
51
|
setInvalidMessage("Format gambar tidak diperbolehkan (JPG/PNG/WebP)");
|
|
49
52
|
return;
|
|
50
53
|
}
|
|
51
|
-
|
|
54
|
+
if (isCroppingEnabled) {
|
|
55
|
+
openCropper(file);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const url = URL.createObjectURL(file);
|
|
59
|
+
setPreview(url);
|
|
60
|
+
onChange?.(file);
|
|
61
|
+
}
|
|
52
62
|
};
|
|
53
63
|
const onCropDone = (file) => {
|
|
54
64
|
const url = URL.createObjectURL(file);
|
|
@@ -69,17 +79,27 @@ const InputImageComponent = ({ name, label, tip, value, disabled, aspect = "1/1"
|
|
|
69
79
|
e.preventDefault();
|
|
70
80
|
setDrag(false);
|
|
71
81
|
const file = e.dataTransfer.files?.[0];
|
|
72
|
-
|
|
82
|
+
if (!file)
|
|
83
|
+
return;
|
|
84
|
+
if (isCroppingEnabled) {
|
|
85
|
+
openCropper(file);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
const url = URL.createObjectURL(file);
|
|
89
|
+
setPreview(url);
|
|
90
|
+
onChange?.(file);
|
|
91
|
+
}
|
|
73
92
|
};
|
|
74
93
|
return ((0, jsx_runtime_1.jsxs)("div", { className: "input-container w-full", children: [label && ((0, jsx_runtime_1.jsxs)("label", { htmlFor: randomId, className: (0, _utils_1.cn)("input-label", disabled && "input-label-disabled", inputHandler.focus && "input-label-focus", !!invalidMessage && "input-label-error", (0, _utils_1.pcn)(className, "label")), 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", disabled && "input-tip-disabled", (0, _utils_1.pcn)(className, "tip")), children: tip })), (0, jsx_runtime_1.jsx)("label", { htmlFor: randomId, children: (0, jsx_runtime_1.jsxs)("div", { className: (0, _utils_1.cn)("input-image-dropzone", drag ? "input-image-dropzone-drag" : "", invalidMessage && "input-image-dropzone-error", (0, _utils_1.pcn)(className, "input")), style: {
|
|
75
|
-
|
|
76
|
-
|
|
94
|
+
position: "relative",
|
|
95
|
+
aspectRatio: containerAspect,
|
|
96
|
+
backgroundImage: preview && containerAspect ? `url(${preview})` : undefined,
|
|
77
97
|
backgroundSize: "cover",
|
|
78
98
|
backgroundPosition: "center",
|
|
79
99
|
}, onDragOver: (e) => e.preventDefault(), onDragEnter: (e) => {
|
|
80
100
|
e.preventDefault();
|
|
81
101
|
setDrag(true);
|
|
82
|
-
}, onDragLeave: () => setDrag(false), onDrop: onDropFile, children: [(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-max max-w-[350px]", children: cropSrc && ((0, jsx_runtime_1.jsx)("div", { className: "p-4", children: (0, jsx_runtime_1.jsx)(exports.CanvasCropper, { src: cropSrc, aspect: eval(
|
|
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-max max-w-[350px]", 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 }) })) }))] }));
|
|
83
103
|
};
|
|
84
104
|
exports.InputImageComponent = InputImageComponent;
|
|
85
105
|
const CanvasCropper = ({ src, aspect, onDone, }) => {
|
|
@@ -58,7 +58,7 @@ export type TableSupervisionProps = {
|
|
|
58
58
|
description: string;
|
|
59
59
|
};
|
|
60
60
|
on?: (item: Record<string, any>) => boolean;
|
|
61
|
-
} | ((row:
|
|
61
|
+
} | ((row: Record<string, any>, setModal?: (type: "EDIT" | "DELETE") => void, setDataSelected?: () => void, reset?: () => void, size?: string) => ReactNode))[];
|
|
62
62
|
block?: boolean;
|
|
63
63
|
noIndex?: boolean;
|
|
64
64
|
actionBulkingControl?: TableProps["actionBulking"];
|
package/package.json
CHANGED
|
@@ -574,6 +574,83 @@ export function InputContentComponent({
|
|
|
574
574
|
}
|
|
575
575
|
}, [handleBold, handleItalic, handleUnderline, handleStrikethrough, handleLink, handleEditorChange, updateActiveStates]);
|
|
576
576
|
|
|
577
|
+
const handlePaste = useCallback((e: React.ClipboardEvent<HTMLDivElement>) => {
|
|
578
|
+
const pastedText = e.clipboardData.getData("text/plain");
|
|
579
|
+
if (!pastedText) return;
|
|
580
|
+
|
|
581
|
+
const urlRegex = /(\b(?:https?:\/\/|www\.)[^\s()<>]+(?:\([\w\d]+\)|[^.,?;:"')\s]))/gi;
|
|
582
|
+
|
|
583
|
+
if (urlRegex.test(pastedText)) {
|
|
584
|
+
e.preventDefault();
|
|
585
|
+
|
|
586
|
+
const escapeHtml = (text: string) => {
|
|
587
|
+
return text
|
|
588
|
+
.replace(/&/g, "&")
|
|
589
|
+
.replace(/</g, "<")
|
|
590
|
+
.replace(/>/g, ">")
|
|
591
|
+
.replace(/"/g, """)
|
|
592
|
+
.replace(/'/g, "'");
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
const tokens: string[] = [];
|
|
596
|
+
let lastIndex = 0;
|
|
597
|
+
let match;
|
|
598
|
+
urlRegex.lastIndex = 0;
|
|
599
|
+
|
|
600
|
+
while ((match = urlRegex.exec(pastedText)) !== null) {
|
|
601
|
+
if (match.index > lastIndex) {
|
|
602
|
+
tokens.push(escapeHtml(pastedText.substring(lastIndex, match.index)));
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
const url = match[0];
|
|
606
|
+
let href = url;
|
|
607
|
+
if (url.toLowerCase().startsWith("www.")) {
|
|
608
|
+
href = "https://" + url;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const escapedHref = escapeHtml(href);
|
|
612
|
+
const escapedUrl = escapeHtml(url);
|
|
613
|
+
tokens.push(`<a href="${escapedHref}" class="text-primary underline" data-link="${escapedHref}" target="_blank" rel="noopener">${escapedUrl}</a>`);
|
|
614
|
+
|
|
615
|
+
lastIndex = urlRegex.lastIndex;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (lastIndex < pastedText.length) {
|
|
619
|
+
tokens.push(escapeHtml(pastedText.substring(lastIndex)));
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const htmlContent = tokens.join("").replace(/\n/g, "<br>");
|
|
623
|
+
|
|
624
|
+
const sel = window.getSelection();
|
|
625
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
626
|
+
const range = sel.getRangeAt(0);
|
|
627
|
+
range.deleteContents();
|
|
628
|
+
|
|
629
|
+
const tempDiv = document.createElement("div");
|
|
630
|
+
tempDiv.innerHTML = htmlContent;
|
|
631
|
+
|
|
632
|
+
const fragment = document.createDocumentFragment();
|
|
633
|
+
let child: Node | null;
|
|
634
|
+
while ((child = tempDiv.firstChild)) {
|
|
635
|
+
fragment.appendChild(child);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const lastNode = fragment.lastChild;
|
|
639
|
+
range.insertNode(fragment);
|
|
640
|
+
|
|
641
|
+
if (lastNode) {
|
|
642
|
+
const newRange = document.createRange();
|
|
643
|
+
newRange.setStartAfter(lastNode);
|
|
644
|
+
newRange.collapse(true);
|
|
645
|
+
sel.removeAllRanges();
|
|
646
|
+
sel.addRange(newRange);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
saveSelection();
|
|
650
|
+
handleEditorChange();
|
|
651
|
+
}
|
|
652
|
+
}, [saveSelection, handleEditorChange]);
|
|
653
|
+
|
|
577
654
|
const renderControlItem = useCallback((item: ToolbarControlItem, index: number) => {
|
|
578
655
|
if (typeof item !== "string") {
|
|
579
656
|
return <div key={index}>{item}</div>;
|
|
@@ -945,6 +1022,7 @@ export function InputContentComponent({
|
|
|
945
1022
|
updateActiveStates();
|
|
946
1023
|
}}
|
|
947
1024
|
onKeyDown={handleKeyDown}
|
|
1025
|
+
onPaste={handlePaste}
|
|
948
1026
|
onMouseUp={() => {
|
|
949
1027
|
saveSelection();
|
|
950
1028
|
updateActiveStates();
|
|
@@ -37,7 +37,7 @@ export const InputImageComponent: React.FC<InputImageProps> = ({
|
|
|
37
37
|
|
|
38
38
|
value,
|
|
39
39
|
disabled,
|
|
40
|
-
aspect
|
|
40
|
+
aspect,
|
|
41
41
|
invalid,
|
|
42
42
|
validations,
|
|
43
43
|
|
|
@@ -59,6 +59,10 @@ export const InputImageComponent: React.FC<InputImageProps> = ({
|
|
|
59
59
|
const inputHandler = useInputHandler(name, value, validations, register, true, unregister);
|
|
60
60
|
const [invalidMessage, setInvalidMessage] = useValidation(inputHandler.value, validations, invalid, inputHandler.idle);
|
|
61
61
|
|
|
62
|
+
const isCroppingEnabled = !!aspect;
|
|
63
|
+
const currentAspect = aspect || "1/1";
|
|
64
|
+
const containerAspect = (preview && !isCroppingEnabled) ? undefined : currentAspect;
|
|
65
|
+
|
|
62
66
|
useEffect(() => {
|
|
63
67
|
if (value) {
|
|
64
68
|
const url = typeof value === "object" ? URL.createObjectURL(value) : value;
|
|
@@ -91,7 +95,13 @@ export const InputImageComponent: React.FC<InputImageProps> = ({
|
|
|
91
95
|
return;
|
|
92
96
|
}
|
|
93
97
|
|
|
94
|
-
|
|
98
|
+
if (isCroppingEnabled) {
|
|
99
|
+
openCropper(file);
|
|
100
|
+
} else {
|
|
101
|
+
const url = URL.createObjectURL(file);
|
|
102
|
+
setPreview(url);
|
|
103
|
+
onChange?.(file);
|
|
104
|
+
}
|
|
95
105
|
};
|
|
96
106
|
|
|
97
107
|
const onCropDone = (file: File) => {
|
|
@@ -117,7 +127,15 @@ export const InputImageComponent: React.FC<InputImageProps> = ({
|
|
|
117
127
|
setDrag(false);
|
|
118
128
|
|
|
119
129
|
const file = e.dataTransfer.files?.[0];
|
|
120
|
-
|
|
130
|
+
if (!file) return;
|
|
131
|
+
|
|
132
|
+
if (isCroppingEnabled) {
|
|
133
|
+
openCropper(file);
|
|
134
|
+
} else {
|
|
135
|
+
const url = URL.createObjectURL(file);
|
|
136
|
+
setPreview(url);
|
|
137
|
+
onChange?.(file);
|
|
138
|
+
}
|
|
121
139
|
};
|
|
122
140
|
|
|
123
141
|
return (
|
|
@@ -157,8 +175,9 @@ export const InputImageComponent: React.FC<InputImageProps> = ({
|
|
|
157
175
|
pcn<CT>(className, "input")
|
|
158
176
|
)}
|
|
159
177
|
style={{
|
|
160
|
-
|
|
161
|
-
|
|
178
|
+
position: "relative",
|
|
179
|
+
aspectRatio: containerAspect,
|
|
180
|
+
backgroundImage: preview && containerAspect ? `url(${preview})` : undefined,
|
|
162
181
|
backgroundSize: "cover",
|
|
163
182
|
backgroundPosition: "center",
|
|
164
183
|
}}
|
|
@@ -170,10 +189,24 @@ export const InputImageComponent: React.FC<InputImageProps> = ({
|
|
|
170
189
|
onDragLeave={() => setDrag(false)}
|
|
171
190
|
onDrop={onDropFile}
|
|
172
191
|
>
|
|
173
|
-
|
|
174
|
-
<
|
|
175
|
-
|
|
176
|
-
|
|
192
|
+
{preview && !containerAspect ? (
|
|
193
|
+
<div className="relative group w-full h-full flex items-center justify-center">
|
|
194
|
+
<img
|
|
195
|
+
src={preview}
|
|
196
|
+
alt="Preview"
|
|
197
|
+
className="w-full h-auto max-h-[400px] object-contain rounded-lg"
|
|
198
|
+
/>
|
|
199
|
+
<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">
|
|
200
|
+
<Icon icon="solid/images" className="text-3xl mb-2" />
|
|
201
|
+
<p className="text-xs text-center">Klik atau seret untuk ganti Gambar</p>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
) : (
|
|
205
|
+
<div className="input-image-dropzone-content">
|
|
206
|
+
<Icon icon={drag ? "solid/hand-holding" : "solid/images"} className="text-3xl" />
|
|
207
|
+
<p className="input-image-dropzone-text">{drag ? "Letakkan di sini" : preview ? "Klik atau seret untuk ganti Gambar" : "Klik atau seret gambar"}</p>
|
|
208
|
+
</div>
|
|
209
|
+
)}
|
|
177
210
|
|
|
178
211
|
<input
|
|
179
212
|
id={randomId}
|
|
@@ -209,7 +242,7 @@ export const InputImageComponent: React.FC<InputImageProps> = ({
|
|
|
209
242
|
<div className="p-4">
|
|
210
243
|
<CanvasCropper
|
|
211
244
|
src={cropSrc}
|
|
212
|
-
aspect={eval(
|
|
245
|
+
aspect={eval(currentAspect.replace(":", "/"))}
|
|
213
246
|
onDone={onCropDone}
|
|
214
247
|
/>
|
|
215
248
|
</div>
|
|
@@ -221,7 +254,7 @@ export const InputImageComponent: React.FC<InputImageProps> = ({
|
|
|
221
254
|
<div className="p-4">
|
|
222
255
|
<CanvasCropper
|
|
223
256
|
src={cropSrc}
|
|
224
|
-
aspect={eval(
|
|
257
|
+
aspect={eval(currentAspect.replace(":", "/"))}
|
|
225
258
|
onDone={onCropDone}
|
|
226
259
|
/>
|
|
227
260
|
</div>
|
|
@@ -64,7 +64,7 @@ export type TableSupervisionProps = {
|
|
|
64
64
|
shortcut ?: { key: string, description: string },
|
|
65
65
|
on ?: (item: Record<string, any>) => boolean,
|
|
66
66
|
} | ((
|
|
67
|
-
row :
|
|
67
|
+
row : Record<string, any>,
|
|
68
68
|
setModal ?: (type: "EDIT" | "DELETE") => void,
|
|
69
69
|
setDataSelected ?: () => void,
|
|
70
70
|
reset ?: () => void,
|