dry-ux 1.84.0 → 1.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/enhanced-inputs/CurrencyInput.d.ts +2 -0
- package/dist/enhanced-inputs/CurrencyInput.js +33 -3
- package/dist/styles/modal.css +13 -0
- package/dist/ui-utils/Modal.js +7 -1
- package/dist/ui-utils/UIUtil.interface.d.ts +4 -0
- package/dist/ui-utils/useDraggable.d.ts +10 -0
- package/dist/ui-utils/useDraggable.js +74 -0
- package/package.json +1 -1
|
@@ -6,6 +6,8 @@ interface CurrencyInputProps extends InputHTMLAttributes<HTMLInputElement>, IEnh
|
|
|
6
6
|
decimal_places?: boolean;
|
|
7
7
|
/** Currency symbol (default: "$") */
|
|
8
8
|
currency?: string;
|
|
9
|
+
/** Show a clear button when the field has a value (default: false) */
|
|
10
|
+
clearable?: boolean;
|
|
9
11
|
}
|
|
10
12
|
export declare const CurrencyInput: React.ForwardRefExoticComponent<CurrencyInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
11
13
|
export {};
|
|
@@ -30,11 +30,12 @@ const formatDisplay = (raw, currency, decimal_places) => {
|
|
|
30
30
|
};
|
|
31
31
|
exports.CurrencyInput = React.forwardRef((_a, ref) => {
|
|
32
32
|
var _b;
|
|
33
|
-
var { decimal_places = true, currency = "$", name, value, defaultValue, onChange, onFocus, onBlur } = _a, rest = __rest(_a, ["decimal_places", "currency", "name", "value", "defaultValue", "onChange", "onFocus", "onBlur"]);
|
|
33
|
+
var { decimal_places = true, currency = "$", clearable = false, name, value, defaultValue, onChange, onFocus, onBlur, disabled, readOnly } = _a, rest = __rest(_a, ["decimal_places", "currency", "clearable", "name", "value", "defaultValue", "onChange", "onFocus", "onBlur", "disabled", "readOnly"]);
|
|
34
34
|
const initialRaw = toRaw((_b = value !== null && value !== void 0 ? value : defaultValue) !== null && _b !== void 0 ? _b : "");
|
|
35
35
|
const [rawValue, setRawValue] = React.useState(initialRaw);
|
|
36
36
|
const [isFocused, setIsFocused] = React.useState(false);
|
|
37
37
|
const hiddenRef = React.useRef(null);
|
|
38
|
+
const inputRef = React.useRef(null);
|
|
38
39
|
const isControlled = value !== undefined;
|
|
39
40
|
// Sync rawValue when controlled value changes
|
|
40
41
|
React.useEffect(() => {
|
|
@@ -63,7 +64,36 @@ exports.CurrencyInput = React.forwardRef((_a, ref) => {
|
|
|
63
64
|
onChange(syntheticEvent);
|
|
64
65
|
}
|
|
65
66
|
};
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
const handleClear = () => {
|
|
68
|
+
const input = inputRef.current;
|
|
69
|
+
if (input) {
|
|
70
|
+
const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value").set;
|
|
71
|
+
setter.call(input, "");
|
|
72
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
73
|
+
input.focus();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const showClear = clearable && rawValue !== "" && !disabled && !readOnly;
|
|
77
|
+
return (React.createElement("span", { className: "dry-ux-currency-input-wrapper", style: { position: "relative", display: "inline-block" } },
|
|
78
|
+
React.createElement(HTMLInputs_1.Input, Object.assign({ ref: (el) => {
|
|
79
|
+
inputRef.current = el;
|
|
80
|
+
if (typeof ref === "function")
|
|
81
|
+
ref(el);
|
|
82
|
+
else if (ref)
|
|
83
|
+
ref.current = el;
|
|
84
|
+
} }, rest, { type: "text", disabled: disabled, readOnly: readOnly, value: displayValue, onFocus: handleFocus, onBlur: handleBlur, onChange: handleChange, style: clearable ? Object.assign(Object.assign({}, (rest.style || {})), { paddingRight: 28 }) : rest.style })),
|
|
85
|
+
showClear && (React.createElement("button", { type: "button", className: "dry-ux-currency-clear-btn", tabIndex: -1, onClick: handleClear, "aria-label": "Clear", style: {
|
|
86
|
+
position: "absolute",
|
|
87
|
+
right: 6,
|
|
88
|
+
top: "50%",
|
|
89
|
+
transform: "translateY(-50%)",
|
|
90
|
+
border: "none",
|
|
91
|
+
background: "transparent",
|
|
92
|
+
cursor: "pointer",
|
|
93
|
+
padding: "0 4px",
|
|
94
|
+
lineHeight: 1,
|
|
95
|
+
fontSize: 14,
|
|
96
|
+
color: "#999",
|
|
97
|
+
} }, "\u2715")),
|
|
68
98
|
React.createElement("input", { ref: hiddenRef, type: "hidden", name: name, value: rawValue, className: "validate validate-hidden" })));
|
|
69
99
|
});
|
package/dist/styles/modal.css
CHANGED
|
@@ -59,6 +59,19 @@
|
|
|
59
59
|
left: 50%!important;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
.dry-ux-modal.draggable .modal-dialog {
|
|
63
|
+
transform: none!important;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.dry-ux-modal.draggable .modal-header {
|
|
67
|
+
cursor: move;
|
|
68
|
+
user-select: none;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.dry-ux-modal.draggable .modal-header.dragging {
|
|
72
|
+
cursor: move;
|
|
73
|
+
}
|
|
74
|
+
|
|
62
75
|
.dry-ux-modal-backdrop {
|
|
63
76
|
position: fixed!important;
|
|
64
77
|
top: 0!important;
|
package/dist/ui-utils/Modal.js
CHANGED
|
@@ -5,14 +5,18 @@ const react_bootstrap_1 = require("react-bootstrap");
|
|
|
5
5
|
require("../styles/modal.css");
|
|
6
6
|
const classSet_1 = require("../helpers/classSet");
|
|
7
7
|
const ActionsOverlay_1 = require("./ActionsOverlay");
|
|
8
|
+
const useDraggable_1 = require("./useDraggable");
|
|
8
9
|
/**
|
|
9
10
|
* Modal component that renders a Bootstrap modal with custom configurations.
|
|
10
11
|
*
|
|
11
12
|
* @param {ModalProps} props - The props for the Modal component.
|
|
12
13
|
* @returns {JSX.Element} The Modal component.
|
|
13
14
|
*/
|
|
14
|
-
const Modal = ({ id, instance: { handleClose, toggleOverlay, shown, overlay, options: { content, footerContent, cssClass = "", closeBtn, title, width, onClose, titleCloseBtn = true, centered, trackingId, actions = [], }, }, config: { defaultModalStyles = false, styles = {}, centered: globalCentered, onOpen, onClose: globalOnClose }, providerId, debug, }) => {
|
|
15
|
+
const Modal = ({ id, instance: { handleClose, toggleOverlay, shown, overlay, options: { content, footerContent, cssClass = "", closeBtn, title, width, onClose, titleCloseBtn = true, centered, trackingId, draggable, actions = [], }, }, config: { defaultModalStyles = false, styles = {}, centered: globalCentered, onOpen, onClose: globalOnClose }, providerId, debug, }) => {
|
|
15
16
|
const isCentered = centered !== null && centered !== void 0 ? centered : globalCentered;
|
|
17
|
+
const isDraggable = !!draggable && !!title;
|
|
18
|
+
const draggableClass = isDraggable ? `dry-ux-draggable-${id}` : "";
|
|
19
|
+
(0, useDraggable_1.useDraggable)(isDraggable, shown, `dry-ux-draggable-${id}`);
|
|
16
20
|
const applyStyles = React.useCallback(() => {
|
|
17
21
|
document.querySelectorAll(".modal-dialog").forEach((el) => {
|
|
18
22
|
el.style.width = typeof width == "number" ? `${width}px` : width;
|
|
@@ -45,6 +49,8 @@ const Modal = ({ id, instance: { handleClose, toggleOverlay, shown, overlay, opt
|
|
|
45
49
|
const modalCssClass = (0, classSet_1.classSet)({
|
|
46
50
|
"dry-ux-modal": true,
|
|
47
51
|
centered: isCentered,
|
|
52
|
+
draggable: isDraggable,
|
|
53
|
+
[draggableClass]: isDraggable,
|
|
48
54
|
[cssClass]: true,
|
|
49
55
|
"default-styles": defaultModalStyles,
|
|
50
56
|
});
|
|
@@ -160,6 +160,10 @@ export type PopUpOptions = {
|
|
|
160
160
|
* The tracking ID for the modal.
|
|
161
161
|
*/
|
|
162
162
|
trackingId?: string;
|
|
163
|
+
/**
|
|
164
|
+
* If true, the modal can be dragged by the title bar.
|
|
165
|
+
*/
|
|
166
|
+
draggable?: boolean;
|
|
163
167
|
/**
|
|
164
168
|
* If true, the modal will be shown.
|
|
165
169
|
*/
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook that makes a modal dialog draggable by its header.
|
|
3
|
+
* Finds the modal via the .draggable CSS class, attaches mousedown to .modal-header,
|
|
4
|
+
* and applies transform: translate() to the .modal-dialog element.
|
|
5
|
+
*
|
|
6
|
+
* @param enabled - Whether dragging is enabled.
|
|
7
|
+
* @param shown - Current shown state; position resets when modal reopens.
|
|
8
|
+
* @param modalClass - Unique CSS class on the modal to identify it.
|
|
9
|
+
*/
|
|
10
|
+
export declare const useDraggable: (enabled: boolean, shown: boolean, modalClass: string) => void;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useDraggable = void 0;
|
|
4
|
+
const React = require("react");
|
|
5
|
+
/**
|
|
6
|
+
* Hook that makes a modal dialog draggable by its header.
|
|
7
|
+
* Finds the modal via the .draggable CSS class, attaches mousedown to .modal-header,
|
|
8
|
+
* and applies transform: translate() to the .modal-dialog element.
|
|
9
|
+
*
|
|
10
|
+
* @param enabled - Whether dragging is enabled.
|
|
11
|
+
* @param shown - Current shown state; position resets when modal reopens.
|
|
12
|
+
* @param modalClass - Unique CSS class on the modal to identify it.
|
|
13
|
+
*/
|
|
14
|
+
const useDraggable = (enabled, shown, modalClass) => {
|
|
15
|
+
const cleanupRef = React.useRef(null);
|
|
16
|
+
React.useEffect(() => {
|
|
17
|
+
if (cleanupRef.current) {
|
|
18
|
+
cleanupRef.current();
|
|
19
|
+
cleanupRef.current = null;
|
|
20
|
+
}
|
|
21
|
+
if (!enabled || !shown)
|
|
22
|
+
return;
|
|
23
|
+
// Defer to next frame to ensure portal DOM is committed
|
|
24
|
+
const raf = requestAnimationFrame(() => {
|
|
25
|
+
const modal = document.querySelector(`.${modalClass}`);
|
|
26
|
+
const dialog = modal === null || modal === void 0 ? void 0 : modal.querySelector(".modal-dialog");
|
|
27
|
+
const header = dialog === null || dialog === void 0 ? void 0 : dialog.querySelector(".modal-header");
|
|
28
|
+
if (!dialog || !header)
|
|
29
|
+
return;
|
|
30
|
+
const offset = { x: 0, y: 0 };
|
|
31
|
+
const startPos = { x: 0, y: 0 };
|
|
32
|
+
let dragging = false;
|
|
33
|
+
dialog.style.setProperty("transform", "none", "important");
|
|
34
|
+
const onMouseDown = (e) => {
|
|
35
|
+
if (e.target.closest(".close"))
|
|
36
|
+
return;
|
|
37
|
+
dragging = true;
|
|
38
|
+
startPos.x = e.clientX - offset.x;
|
|
39
|
+
startPos.y = e.clientY - offset.y;
|
|
40
|
+
header.classList.add("dragging");
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
};
|
|
43
|
+
const onMouseMove = (e) => {
|
|
44
|
+
if (!dragging)
|
|
45
|
+
return;
|
|
46
|
+
offset.x = e.clientX - startPos.x;
|
|
47
|
+
offset.y = e.clientY - startPos.y;
|
|
48
|
+
dialog.style.setProperty("transform", `translate(${offset.x}px, ${offset.y}px)`, "important");
|
|
49
|
+
};
|
|
50
|
+
const onMouseUp = () => {
|
|
51
|
+
if (!dragging)
|
|
52
|
+
return;
|
|
53
|
+
dragging = false;
|
|
54
|
+
header.classList.remove("dragging");
|
|
55
|
+
};
|
|
56
|
+
header.addEventListener("mousedown", onMouseDown);
|
|
57
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
58
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
59
|
+
cleanupRef.current = () => {
|
|
60
|
+
header.removeEventListener("mousedown", onMouseDown);
|
|
61
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
62
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
return () => {
|
|
66
|
+
cancelAnimationFrame(raf);
|
|
67
|
+
if (cleanupRef.current) {
|
|
68
|
+
cleanupRef.current();
|
|
69
|
+
cleanupRef.current = null;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}, [enabled, shown, modalClass]);
|
|
73
|
+
};
|
|
74
|
+
exports.useDraggable = useDraggable;
|