dry-ux 1.86.0 → 1.88.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
+ [![npm version](https://img.shields.io/npm/v/dry-ux.svg)](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
@@ -12,11 +12,11 @@ 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, 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 = [], }, }, 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}` : "";
19
- (0, useDraggable_1.useDraggable)(isDraggable, shown, `dry-ux-draggable-${id}`);
19
+ (0, useDraggable_1.useDraggable)(isDraggable, shown, `dry-ux-draggable-${id}`, instanceKey);
20
20
  const applyStyles = React.useCallback(() => {
21
21
  document.querySelectorAll(".modal-dialog").forEach((el) => {
22
22
  el.style.width = typeof width == "number" ? `${width}px` : width;
@@ -69,6 +69,10 @@ export type PopUpInstance = {
69
69
  * Indicates whether the PopUp is shown.
70
70
  */
71
71
  shown: boolean;
72
+ /**
73
+ * @internal Unique key that changes each time the instance is created/replaced.
74
+ */
75
+ key?: number;
72
76
  /**
73
77
  * The overlay content for the PopUp.
74
78
  */
@@ -248,6 +248,7 @@ class UIUtilProvider extends React.PureComponent {
248
248
  instances[id] = {
249
249
  options,
250
250
  shown: false,
251
+ key: Date.now(),
251
252
  handleClose: this.toggleModalInstance.bind(this),
252
253
  toggleOverlay: this.toggleModalOverlay.bind(this),
253
254
  };
@@ -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 {JSX.Element} The viewport detection component.
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 visibility of a viewport element.
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 {JSX.Element} The viewport detection component.
77
+ * @returns {null} This component renders nothing.
89
78
  */
90
79
  exports.ViewportDetect = React.memo(({ onChange }) => {
91
- const onVisibilityChange = (viewport, isVisible) => {
92
- if (isVisible) {
93
- onChange(new CurrentViewport(viewport));
94
- }
95
- };
96
- return (React.createElement(React.Fragment, null, Object.keys(Viewport)
97
- .map(v => Viewport[v])
98
- .map((viewport) => (React.createElement(DetectionElement, { key: viewport, viewport: viewport, onVisibilityChange: isVisible => onVisibilityChange(viewport, isVisible) })))));
80
+ React.useEffect(() => {
81
+ const listeners = [];
82
+ breakpoints.forEach(({ viewport, query }) => {
83
+ const mql = window.matchMedia(query);
84
+ const handler = (e) => {
85
+ if (e.matches) {
86
+ onChange(new CurrentViewport(viewport));
87
+ }
88
+ };
89
+ // Check initial state
90
+ handler(mql);
91
+ // Listen for changes
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
+ }, [onChange]);
101
+ return null;
99
102
  });
@@ -6,5 +6,6 @@
6
6
  * @param enabled - Whether dragging is enabled.
7
7
  * @param shown - Current shown state; position resets when modal reopens.
8
8
  * @param modalClass - Unique CSS class on the modal to identify it.
9
+ * @param modalKey - When this value changes, drag position resets (e.g. when one modal replaces another).
9
10
  */
10
- export declare const useDraggable: (enabled: boolean, shown: boolean, modalClass: string) => void;
11
+ export declare const useDraggable: (enabled: boolean, shown: boolean, modalClass: string, modalKey: number) => void;
@@ -10,8 +10,9 @@ const React = require("react");
10
10
  * @param enabled - Whether dragging is enabled.
11
11
  * @param shown - Current shown state; position resets when modal reopens.
12
12
  * @param modalClass - Unique CSS class on the modal to identify it.
13
+ * @param modalKey - When this value changes, drag position resets (e.g. when one modal replaces another).
13
14
  */
14
- const useDraggable = (enabled, shown, modalClass) => {
15
+ const useDraggable = (enabled, shown, modalClass, modalKey) => {
15
16
  const cleanupRef = React.useRef(null);
16
17
  React.useEffect(() => {
17
18
  if (cleanupRef.current) {
@@ -69,6 +70,6 @@ const useDraggable = (enabled, shown, modalClass) => {
69
70
  cleanupRef.current = null;
70
71
  }
71
72
  };
72
- }, [enabled, shown, modalClass]);
73
+ }, [enabled, shown, modalClass, modalKey]);
73
74
  };
74
75
  exports.useDraggable = useDraggable;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dry-ux",
3
- "version": "1.86.0",
3
+ "version": "1.88.0",
4
4
  "description": "",
5
5
  "main": "dist/index",
6
6
  "scripts": {
@@ -9,7 +9,7 @@
9
9
  "watch": "rimraf dist/ && tsc --watch",
10
10
  "format": "prettier --write \"src/**/*.ts\" \"src/**/*.tsx\"",
11
11
  "update:version": "npm version minor --no-git-tag-version",
12
- "prepub:npm": "npm run format && npm run build && npm run update:version && npm run docs",
12
+ "prepub:npm": "npm run format && npm run build && npm run update:version",
13
13
  "publish:npm": "npm run prepub:npm && npm publish",
14
14
  "pack": "npm run build && npm pack --pack-destination pack/",
15
15
  "docs": "typedoc --options typedoc.json && cd test && npm run build:test && cp -r proddist ../htmldocs/demo",