dry-ux 1.87.0 → 1.89.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/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
## dry-ux
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/dry-ux)
|
|
4
|
+
|
|
5
|
+
**dry-ux** is a comprehensive utility library designed for React applications. It offers a collection of reusable components, hooks, and utilities aimed at enhancing the development experience. The library includes features such as deferred rendering, error boundaries, loaders, storage utilities, and validation helpers. By promoting a DRY (Don't Repeat Yourself) approach, **dry-ux** helps developers avoid redundancy and streamline their codebase.
|
|
6
|
+
|
|
7
|
+
### Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install dry-ux
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Quick Start
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { DryUXProvider, useDryUxContext } from "dry-ux";
|
|
17
|
+
|
|
18
|
+
const App = () => (
|
|
19
|
+
<DryUXProvider>
|
|
20
|
+
<MyApp />
|
|
21
|
+
</DryUXProvider>
|
|
22
|
+
);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Key Features
|
|
26
|
+
|
|
27
|
+
- **Modal System** - Programmatic modals with alerts, confirms, actions, overlays, and draggable support
|
|
28
|
+
- **Deferred Rendering** - Optimize performance by rendering components only when visible
|
|
29
|
+
- **Error Boundaries** - Customizable error boundaries with fallback UI
|
|
30
|
+
- **Form Validation** - Declarative validation for inputs, selects, and textareas
|
|
31
|
+
- **Loaders** - Fullscreen and custom loader management
|
|
32
|
+
- **Storage Utilities** - Simplified local/session storage interactions
|
|
33
|
+
- **Currency Formatting** - Money display and currency input components
|
|
34
|
+
- **Viewport Detection** - Responsive breakpoint detection via context
|
|
35
|
+
|
|
36
|
+
### Usage
|
|
37
|
+
|
|
38
|
+
Access UI utilities anywhere via the `useDryUxContext` hook:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
const MyComponent = () => {
|
|
42
|
+
const { modal, loader } = useDryUxContext();
|
|
43
|
+
|
|
44
|
+
const openModal = () =>
|
|
45
|
+
modal.show({
|
|
46
|
+
title: "Hello",
|
|
47
|
+
content: <div>Modal content here</div>,
|
|
48
|
+
width: 400,
|
|
49
|
+
draggable: true,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return <button onClick={openModal}>Open Modal</button>;
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Documentation
|
|
57
|
+
|
|
58
|
+
For a detailed overview of all available classes, interfaces, type aliases, variables, and functions, refer to the [API Documentation](https://navedr.github.io/dry-ux/).
|
|
59
|
+
|
|
60
|
+
Check out the [demo](https://navedr.github.io/dry-ux/demo) for live examples.
|
|
61
|
+
|
|
62
|
+
### Requirements
|
|
63
|
+
|
|
64
|
+
- React >= 16.8.0
|
|
65
|
+
- react-dom >= 16.8.0
|
package/dist/styles/modal.css
CHANGED
|
@@ -72,6 +72,35 @@
|
|
|
72
72
|
cursor: move;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
.dry-ux-modal .modal-header .dry-ux-modal-header-right {
|
|
76
|
+
float: right;
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
gap: 4px;
|
|
80
|
+
margin-top: -2px;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.dry-ux-modal .modal-header .dry-ux-modal-header-right .close {
|
|
84
|
+
float: none;
|
|
85
|
+
margin-top: 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.dry-ux-modal .modal-header .dry-ux-modal-header-action {
|
|
89
|
+
background: none;
|
|
90
|
+
border: none;
|
|
91
|
+
padding: 0 3px;
|
|
92
|
+
cursor: pointer;
|
|
93
|
+
line-height: 1;
|
|
94
|
+
color: inherit;
|
|
95
|
+
opacity: 0.4;
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.dry-ux-modal .modal-header .dry-ux-modal-header-action:hover {
|
|
101
|
+
opacity: 1;
|
|
102
|
+
}
|
|
103
|
+
|
|
75
104
|
.dry-ux-modal-backdrop {
|
|
76
105
|
position: fixed!important;
|
|
77
106
|
top: 0!important;
|
package/dist/ui-utils/Modal.js
CHANGED
|
@@ -12,7 +12,7 @@ const useDraggable_1 = require("./useDraggable");
|
|
|
12
12
|
* @param {ModalProps} props - The props for the Modal component.
|
|
13
13
|
* @returns {JSX.Element} The Modal component.
|
|
14
14
|
*/
|
|
15
|
-
const Modal = ({ id, instance: { handleClose, toggleOverlay, shown, overlay, key: instanceKey, options: { content, footerContent, cssClass = "", closeBtn, title, width, onClose, titleCloseBtn = true, centered, trackingId, draggable, actions = [], }, }, config: { defaultModalStyles = false, styles = {}, centered: globalCentered, draggable: globalDraggable, onOpen, onClose: globalOnClose, }, providerId, debug, }) => {
|
|
15
|
+
const Modal = ({ id, instance: { handleClose, toggleOverlay, shown, overlay, key: instanceKey, options: { content, footerContent, cssClass = "", closeBtn, title, width, onClose, titleCloseBtn = true, centered, trackingId, draggable, actions = [], headerActions = [], }, }, config: { defaultModalStyles = false, styles = {}, centered: globalCentered, draggable: globalDraggable, onOpen, onClose: globalOnClose, }, providerId, debug, }) => {
|
|
16
16
|
const isCentered = centered !== null && centered !== void 0 ? centered : globalCentered;
|
|
17
17
|
const isDraggable = !!(draggable !== null && draggable !== void 0 ? draggable : globalDraggable) && !!title;
|
|
18
18
|
const draggableClass = isDraggable ? `dry-ux-draggable-${id}` : "";
|
|
@@ -78,8 +78,15 @@ const Modal = ({ id, instance: { handleClose, toggleOverlay, shown, overlay, key
|
|
|
78
78
|
return (React.createElement(react_bootstrap_1.Modal, { onHide: onHide, show: shown, animation: true, autoFocus: true, keyboard: false, className: modalCssClass, backdropClassName: "dry-ux-modal-backdrop", backdrop: "static" },
|
|
79
79
|
overlay && (React.createElement("div", { className: "dry-ux-overlay" },
|
|
80
80
|
React.createElement("div", { className: "dry-ux-overlay-content" }, overlay))),
|
|
81
|
-
!!title && (React.createElement(react_bootstrap_1.Modal.Header, { closeButton: titleCloseBtn, onHide: onHide },
|
|
82
|
-
React.createElement(
|
|
81
|
+
(!!title || !!(headerActions === null || headerActions === void 0 ? void 0 : headerActions.length)) && (React.createElement(react_bootstrap_1.Modal.Header, { closeButton: !(headerActions === null || headerActions === void 0 ? void 0 : headerActions.length) && titleCloseBtn, onHide: onHide },
|
|
82
|
+
!!(headerActions === null || headerActions === void 0 ? void 0 : headerActions.length) && (React.createElement("div", { className: "dry-ux-modal-header-right" },
|
|
83
|
+
headerActions.map(({ content: actionContent, onClick, title: actionTitle, className = "" }, index) => (React.createElement("button", { key: index, className: (0, classSet_1.classSet)({
|
|
84
|
+
"dry-ux-modal-header-action": true,
|
|
85
|
+
[className]: !!className,
|
|
86
|
+
}), onClick: onClick, title: actionTitle, type: "button" }, actionContent))),
|
|
87
|
+
titleCloseBtn && (React.createElement("button", { type: "button", className: "close", onClick: onHide },
|
|
88
|
+
React.createElement("span", { "aria-hidden": "true" }, "\u00D7"))))),
|
|
89
|
+
!!title && React.createElement(react_bootstrap_1.Modal.Title, null, title))),
|
|
83
90
|
React.createElement(react_bootstrap_1.Modal.Body, null, contentToRender),
|
|
84
91
|
showFooter && (React.createElement(react_bootstrap_1.Modal.Footer, null,
|
|
85
92
|
footerContent,
|
|
@@ -91,6 +91,27 @@ export type PopUpInstance = {
|
|
|
91
91
|
*/
|
|
92
92
|
toggleOverlay: (id: string, content?: Content) => void;
|
|
93
93
|
};
|
|
94
|
+
/**
|
|
95
|
+
* Represents an action displayed in the modal header (next to the close button).
|
|
96
|
+
*/
|
|
97
|
+
export type PopUpHeaderAction = {
|
|
98
|
+
/**
|
|
99
|
+
* The content to display in the header action button.
|
|
100
|
+
*/
|
|
101
|
+
content: Content;
|
|
102
|
+
/**
|
|
103
|
+
* Function to call when the header action is clicked.
|
|
104
|
+
*/
|
|
105
|
+
onClick?: () => void;
|
|
106
|
+
/**
|
|
107
|
+
* Tooltip text for the header action button.
|
|
108
|
+
*/
|
|
109
|
+
title?: string;
|
|
110
|
+
/**
|
|
111
|
+
* Additional CSS class for the header action button.
|
|
112
|
+
*/
|
|
113
|
+
className?: string;
|
|
114
|
+
};
|
|
94
115
|
/**
|
|
95
116
|
* Represents an action for a PopUp.
|
|
96
117
|
*/
|
|
@@ -172,6 +193,10 @@ export type PopUpOptions = {
|
|
|
172
193
|
* If true, the modal will be shown.
|
|
173
194
|
*/
|
|
174
195
|
actions?: PopUpAction[];
|
|
196
|
+
/**
|
|
197
|
+
* Actions rendered in the modal header, next to the close button.
|
|
198
|
+
*/
|
|
199
|
+
headerActions?: PopUpHeaderAction[];
|
|
175
200
|
};
|
|
176
201
|
/**
|
|
177
202
|
* Represents a utility for managing UI modals.
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import "../styles/viewport.css";
|
|
3
2
|
/**
|
|
4
3
|
* Enum representing different viewport sizes.
|
|
5
4
|
*/
|
|
@@ -50,10 +49,10 @@ export declare class CurrentViewport {
|
|
|
50
49
|
get current(): Viewport;
|
|
51
50
|
}
|
|
52
51
|
/**
|
|
53
|
-
* Component that detects the current viewport and triggers a callback on change.
|
|
52
|
+
* Component that detects the current viewport using matchMedia and triggers a callback on change.
|
|
54
53
|
* @param {Object} props - The component props.
|
|
55
54
|
* @param {function(CurrentViewport): void} props.onChange - Callback function to handle viewport change.
|
|
56
|
-
* @returns {
|
|
55
|
+
* @returns {null} This component renders nothing.
|
|
57
56
|
*/
|
|
58
57
|
export declare const ViewportDetect: React.FC<{
|
|
59
58
|
onChange: (current: CurrentViewport) => void;
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ViewportDetect = exports.CurrentViewport = exports.Viewport = void 0;
|
|
4
4
|
const React = require("react");
|
|
5
|
-
require("../styles/viewport.css");
|
|
6
|
-
const utilities_1 = require("../helpers/utilities");
|
|
7
5
|
/**
|
|
8
6
|
* Enum representing different viewport sizes.
|
|
9
7
|
*/
|
|
@@ -66,34 +64,39 @@ class CurrentViewport {
|
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
66
|
exports.CurrentViewport = CurrentViewport;
|
|
67
|
+
const breakpoints = [
|
|
68
|
+
{ viewport: Viewport.XS, query: "(max-width: 767px)" },
|
|
69
|
+
{ viewport: Viewport.SM, query: "(min-width: 768px) and (max-width: 991px)" },
|
|
70
|
+
{ viewport: Viewport.MD, query: "(min-width: 992px) and (max-width: 1199px)" },
|
|
71
|
+
{ viewport: Viewport.LG, query: "(min-width: 1200px)" },
|
|
72
|
+
];
|
|
69
73
|
/**
|
|
70
|
-
* Component that detects the
|
|
71
|
-
* @param {Object} props - The component props.
|
|
72
|
-
* @param {Viewport} props.viewport - The viewport size.
|
|
73
|
-
* @param {function(boolean): void} props.onVisibilityChange - Callback function to handle visibility change.
|
|
74
|
-
* @returns {JSX.Element} The detection element.
|
|
75
|
-
*/
|
|
76
|
-
const DetectionElement = ({ viewport, onVisibilityChange, }) => {
|
|
77
|
-
const ref = React.useRef(null);
|
|
78
|
-
const isVisible = (0, utilities_1.useIsVisible)(ref);
|
|
79
|
-
React.useEffect(() => {
|
|
80
|
-
onVisibilityChange(isVisible);
|
|
81
|
-
}, [isVisible]);
|
|
82
|
-
return React.createElement("div", { ref: ref, className: `dry-visible-${viewport}` });
|
|
83
|
-
};
|
|
84
|
-
/**
|
|
85
|
-
* Component that detects the current viewport and triggers a callback on change.
|
|
74
|
+
* Component that detects the current viewport using matchMedia and triggers a callback on change.
|
|
86
75
|
* @param {Object} props - The component props.
|
|
87
76
|
* @param {function(CurrentViewport): void} props.onChange - Callback function to handle viewport change.
|
|
88
|
-
* @returns {
|
|
77
|
+
* @returns {null} This component renders nothing.
|
|
89
78
|
*/
|
|
90
79
|
exports.ViewportDetect = React.memo(({ onChange }) => {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
80
|
+
const onChangeRef = React.useRef(onChange);
|
|
81
|
+
onChangeRef.current = onChange;
|
|
82
|
+
React.useEffect(() => {
|
|
83
|
+
const listeners = [];
|
|
84
|
+
breakpoints.forEach(({ viewport, query }) => {
|
|
85
|
+
const mql = window.matchMedia(query);
|
|
86
|
+
const handler = (e) => {
|
|
87
|
+
if (e.matches) {
|
|
88
|
+
onChangeRef.current(new CurrentViewport(viewport));
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
handler(mql);
|
|
92
|
+
mql.addEventListener("change", handler);
|
|
93
|
+
listeners.push({ mql, handler });
|
|
94
|
+
});
|
|
95
|
+
return () => {
|
|
96
|
+
listeners.forEach(({ mql, handler }) => {
|
|
97
|
+
mql.removeEventListener("change", handler);
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
}, []);
|
|
101
|
+
return null;
|
|
99
102
|
});
|