react-pure-modal 2.2.7 → 3.0.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 CHANGED
@@ -4,9 +4,18 @@
4
4
 
5
5
  React pure modal is a simplest way to create dialog on your site.
6
6
 
7
- - Very small (less than 4Kb)
8
- - Mobile friendly
9
- - Without dependencies
7
+ ## Features
8
+
9
+ - [x] Lightweight, no external dependencies, 4.1Kb GZip including CSS
10
+ - [x] Easy-to-swap modal components; independent but designed to work together
11
+ - [x] Close only the current modal on ESC or backdrop
12
+ - [x] Easy change modal proportions
13
+ - [x] Easy change modal appearance with variables
14
+ - [x] Dynamic width based on content
15
+ - [x] Stop background scrolling when focused in modal
16
+ - [x] Mobile-friendly safe areas
17
+ - [x] Smooth animations
18
+ - [ ] Mobile-friendly gestures
10
19
 
11
20
  ## Demo
12
21
 
@@ -19,103 +28,110 @@ https://memcrab.github.io/react-pure-modal/
19
28
  ## Usage
20
29
 
21
30
  ```jsx
22
- import PureModal from 'react-pure-modal';
23
- import 'react-pure-modal/dist/react-pure-modal.min.css';
24
-
25
- const [modal, setModal] = useState(false);
26
-
27
- <PureModal
28
- header="Your header"
29
- footer={
30
- <div>
31
- <button>Cancel</button>
32
- <button>Save</button>
33
- </div>
34
- }
35
- isOpen={modal}
36
- closeButton="close"
37
- closeButtonPosition="bottom"
38
- onClose={() => {
39
- setModal(false);
40
- return true;
41
- }}
31
+ <Modal
32
+ isOpen={isOpen}
33
+ onClose={onClose}
34
+ closeOnBackdropClick
42
35
  >
43
- <p>Your content</p>
44
- </PureModal>;
36
+ <Modal.Close />
37
+ <Modal.Header>
38
+ <h2>Second Modal</h2>
39
+ </Modal.Header>
40
+ <Modal.Content>
41
+ <h1>some main content</h1>
42
+ <p>some content here</p>
43
+ </Modal.Content>
44
+ <Modal.Footer>footer content</Modal.Footer>
45
+ </Modal>
45
46
  ```
46
47
 
47
- And open with
48
-
49
- `<button className="button" onClick={() => setModal(true)}>Open simple modal</button>`
50
-
51
48
  ## Options
52
49
 
53
- #### replace `boolean` (default: false)
54
-
55
- Replace all inner markup with Component children
56
-
57
- #### isOpen: `boolean`
58
-
59
- Control modal state from parent component
60
-
61
- #### scrollable: `boolean` (default: true)
62
-
63
- You can disable scroll in modal body
64
-
65
- #### draggable: `boolean` (default: false)
66
-
67
- You can drag a modal window
50
+ - `isOpen` (boolean, required) - controls whether the modal is rendered.
51
+ - `onClose` (() => boolean | void) - called when the user clicks the close button, presses ESC, or (optionally) clicks the backdrop; set `isOpen` to `false` inside it.
52
+ - `closeOnBackdropClick` (boolean) - if `true`, clicking the backdrop calls `onClose` (default is `false`).
53
+ - `style` (React.CSSProperties) - inline styles applied to the backdrop element; use this to set CSS custom properties (listed below) or to tweak things like `zIndex`.
54
+ - `children` - compose the modal from the provided compounds: `Modal.Close`, `Modal.Header`, `Modal.Content`, and `Modal.Footer`.
55
+
56
+ ## CSS Variables
57
+
58
+ All variables can be provided through the `style` prop (e.g. `style={{ "--radius": "16px" }}`).
59
+
60
+ - `--radius` - border radius of the modal container (mobile uses `12px 12px 0 0`).
61
+ - `--aspect-ratio` - forced aspect ratio for the modal grid.
62
+ - `--backdrop-blur` - available for custom use if you want to reference a blur amount.
63
+ - `--backdrop-filter` - value for the backdrop `backdrop-filter` property.
64
+ - `--backdrop-color` - background color of the overlay.
65
+ - `--box-shadow` - shadow applied to the modal panel.
66
+ - `--max-width` - maximum width of the modal.
67
+ - `--max-height` - maximum height of the modal.
68
+ - `--min-width` - minimum width of the modal.
69
+ - `--background` - modal surface background.
70
+ - `--background-panels` - background for header and footer panels.
71
+ - `--contrast-color` - high-contrast color derived from `--background-panels` (used to build borders and hover states); override to steer the automatic mix.
72
+ - `--z-index` - base stacking level for the backdrop (panel uses `+1`).
73
+ - `--top-content-padding` / `--bottom-content-padding` - vertical padding for the content area.
74
+ - `--top-header-padding` / `--bottom-header-padding` - vertical padding for the header.
75
+ - `--top-footer-padding` / `--bottom-footer-padding` - vertical padding for the footer.
76
+ - `--left-padding` / `--right-padding` - horizontal padding shared across sections.
77
+ - `--dividers-color` - border tone mixed from `--background-panels` and `--contrast-color` by default; override to force a specific divider color.
78
+ - `--border` - border applied to header, footer, and close button; defaults to `1px solid var(--dividers-color)`.
68
79
 
69
- #### onClose: `Function`
70
80
 
71
- Handle modal closing. Should change isOpen to false
81
+ ## Changelog (latest on top)
72
82
 
73
- #### className: `string`
83
+ - Migration to compound components after 7 years with previous API
84
+ - Removed double calling onClose on popup closing and unmount. onClose will be called only on: close button, backdrop, esc click
85
+ - Drag and drop
86
+ - fix bug in firefox and safari with modal position
87
+ - set width as atribute
88
+ - new default aligning to the screen center!
89
+ - prevent of modal closing if ESC pressed in editable element
90
+ - now with minified css!
91
+ - styles are more impressive now, good mobile support
92
+ - now scrollable can be false
93
+ - remove dependencies, rewrite open and close logic, fix linting
94
+ - new header logic and breaking classes changes
74
95
 
75
- ClassName for modal DOM element, can be used for set modal width or change behaviour on mobile devices
76
96
 
77
- #### width: `string` (example '200px')
97
+ ## Development
78
98
 
79
- Width in pixels, em's, vw etc
99
+ Install the dependencies:
80
100
 
81
- #### header: `JSX.Element | string`
101
+ ```bash
102
+ npm install
103
+ ```
82
104
 
83
- Modal heading, doesn't disabled close button
84
105
 
85
- #### footer: `JSX.Element | string`
106
+ ## Approach Details
86
107
 
87
- Place here your actions
108
+ You can't use css `fixed` positioning because it stops using any nested modals. Modal nesting is well known bad UX pattern, but still sometimes in very rare cases you really need that.
88
109
 
89
- #### closeButton: `(JSX.Element | string)`
110
+ We can't use sticky, because scrollbar appears over the sticky elements, which looks weird
90
111
 
91
- Content of your closing button
112
+ HTML dialog tag can't be used due to lack of nesting and bad imperative interface. Like backdrop will be shown only when you open it with JS API `openModal()`, so no SSR support.
92
113
 
93
- #### closeButtonPosition: `string`
114
+ HTML popover can't be used due to low support accross browsers.
94
115
 
95
- Place closing button under your modal or inside header. Allowed options: 'header' | 'bottom'
116
+ ## Get started
96
117
 
97
- #### portal: `boolean` (default: false)
118
+ Build the library:
98
119
 
99
- Creates React.Portal
120
+ ```bash
121
+ pnpm build
122
+ ```
100
123
 
101
- ## Changelog (latest on top)
124
+ Build the library in watch mode:
102
125
 
103
- - Removed double calling onClose on popup closing and unmount. onClose will be called only on: close button, backdrop, esc click
104
- - Drag and drop
105
- - fix bug in firefox and safari with modal position
106
- - set width as atribute
107
- - new default aligning to the screen center!
108
- - prevent of modal closing if ESC pressed in editable element
109
- - now with minified css!
110
- - styles are more impressive now, good mobile support
111
- - now scrollable can be false
112
- - remove dependencies, rewrite open and close logic, fix linting
113
- - new header logic and breaking classes changes
126
+ ```bash
127
+ pnpm dev
128
+ ```
114
129
 
115
- ## Developing
130
+ ## Publishing a new version
116
131
 
117
- - `npm install`
118
- - `npm run webpack:dev -- --watch`
119
- - `npm run webpack:prod -- --watch`
120
- - `npm run test:dev`
121
- - Open `index.html` from examples
132
+ 1. Bump the package version and rebuild: `npm version <patch|minor|major>` then `npm run build`; commit the updated files (including `dist`) and push to `master`/`main`.
133
+ 2. Label the merged PRs to drive the next draft version: use `breaking`/`major` for a major bump (e.g. 3.0.0), `feature`/`enhancement` for minor, and `fix`/`bug`/`chore` for patch; Release Drafter uses these labels to compute the `vX.Y.Z` it suggests.
134
+ 3. Wait for the Release Drafter workflow to refresh the draft release on GitHub (it runs on pushes to the default branch).
135
+ 4. Open the draft release in the GitHub Releases page, review the generated notes, set the tag name to match the new version (e.g. `v3.0.1`), and publish the release.
136
+ 5. Publishing the release triggers the `Node.js Package` workflow to publish to npm using the `NPM_TOKEN` repository secret; verify that secret is configured.
137
+ 6. If you ever need to publish locally instead, run `npm run build` followed by `npm publish --access public` with `NPM_TOKEN` available in your environment.
package/package.json CHANGED
@@ -1,73 +1,48 @@
1
1
  {
2
2
  "name": "react-pure-modal",
3
- "version": "2.2.7",
4
- "description": "React modal",
5
- "main": "dist/react-pure-modal.min.js",
6
- "types": "dist/react-pure-modal.d.ts",
7
- "scripts": {
8
- "clean": "rimraf dist",
9
- "build": "npm run clean && tsc && NODE_ENV=production npm run webpack:prod && npm run webpack:dev",
10
- "webpack:dev": "webpack --config webpack.config.dev.js",
11
- "webpack:prod": "webpack --config webpack.config.js",
12
- "test": "jest --json",
13
- "test-ci": "jest --ci",
14
- "test:dev": "jest --watch"
15
- },
16
- "repository": {
17
- "type": "git",
18
- "url": "git+https://github.com/max-mykhailenko/react-pure-modal.git"
3
+ "version": "3.0.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.js"
9
+ }
19
10
  },
20
- "keywords": [
21
- "react",
22
- "modal",
23
- "dialog"
11
+ "sideEffects": [
12
+ "*.css",
13
+ "*.module.css",
14
+ "**/*.css"
24
15
  ],
25
- "author": "Max Mykhailenko",
26
- "license": "ISC",
27
- "bugs": {
28
- "url": "https://github.com/max-mykhailenko/react-pure-modal/issues"
16
+ "types": "./dist/index.d.ts",
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "build": "rslib build",
22
+ "dev": "rslib build --watch",
23
+ "storybook": "npx storybook dev",
24
+ "storybook:build": "storybook build -o storybook-static",
25
+ "storybook:deploy": "npm run storybook:build"
29
26
  },
30
- "homepage": "https://github.com/max-mykhailenko/react-pure-modal#readme",
31
27
  "devDependencies": {
32
- "@babel/core": "7.22.1",
33
- "@babel/plugin-proposal-class-properties": "7.10.4",
34
- "@babel/plugin-proposal-optional-chaining": "7.11.0",
35
- "@babel/preset-env": "7.11.0",
36
- "@babel/preset-react": "7.10.4",
37
- "@babel/preset-typescript": "7.10.4",
38
- "@types/node": "^15.14.1",
39
- "@types/react": "16.9.48",
40
- "@types/react-dom": "16.9.9",
41
- "autoprefixer": "9.8.6",
42
- "babel-eslint": "10.1.0",
43
- "babel-jest": "26.3.0",
44
- "babel-loader": "8.1.0",
45
- "css-loader": "4.2.2",
46
- "css-minimizer-webpack-plugin": "1.1.2",
47
- "eslint": "7.7.0",
48
- "eslint-config-airbnb": "18.2.0",
49
- "eslint-plugin-import": "2.22.1",
50
- "eslint-plugin-jsx-a11y": "6.3.1",
51
- "eslint-plugin-react": "7.20.6",
52
- "jest": "26.4.2",
53
- "mini-css-extract-plugin": "0.11.0",
54
- "postcss-csso": "4.0.0",
55
- "postcss-loader": "3.0.0",
56
- "prettier": "2.1.1",
57
- "react": "16.13.1",
58
- "react-dom": "16.13.1",
59
- "react-test-renderer": "16.13.1",
60
- "rimraf": "3.0.2",
61
- "style-loader": "1.2.1",
62
- "terser-webpack-plugin": "4.1.0",
63
- "typescript": "4.8.2",
64
- "webpack": "5.85.0",
65
- "webpack-cli": "5.1.3",
66
- "webpack-dev-server": "3.11.0",
67
- "yargs": "15.4.1"
28
+ "@biomejs/biome": "2.2.5",
29
+ "@rsbuild/core": "1.5.13",
30
+ "@rsbuild/plugin-react": "^1.4.1",
31
+ "@rslib/core": "^0.15.0",
32
+ "@storybook/addon-docs": "^10.1.0",
33
+ "@storybook/addon-onboarding": "^10.1.0",
34
+ "@storybook/react": "^10.1.0",
35
+ "@types/react": "^19.1.15",
36
+ "@types/react-dom": "19.2.0",
37
+ "react": "^19.1.1",
38
+ "storybook": "^10.1.0",
39
+ "storybook-addon-rslib": "^3.0.0",
40
+ "storybook-react-rsbuild": "^3.0.0",
41
+ "typescript": "^5.9.2",
42
+ "typescript-plugin-css-modules": "5.2.0"
68
43
  },
69
44
  "peerDependencies": {
70
- "react": "^15.3.0 || ^16.0 || ^17.0 || ^18.0",
71
- "react-dom": "^15.3.0 || ^16.0 || ^17.0 || ^18.0"
45
+ "react": "^18.0 || ^19.0",
46
+ "react-dom": "^18.0 || ^19.0"
72
47
  }
73
48
  }
package/.babelrc.js DELETED
@@ -1,12 +0,0 @@
1
- module.exports = {
2
- presets: [
3
- [
4
- '@babel/preset-env',
5
- {
6
- targets: { chrome: 70 },
7
- },
8
- ],
9
- '@babel/preset-react',
10
- '@babel/preset-typescript',
11
- ],
12
- };
package/.eslintrc DELETED
@@ -1,22 +0,0 @@
1
- {
2
- "extends": "airbnb",
3
- "parser": "babel-eslint",
4
- "env": {
5
- "browser": true,
6
- "node": true,
7
- "jasmine": true
8
- },
9
- "rules" : {
10
- "new-cap": 0,
11
- "no-case-declarations": 0,
12
- "no-nested-ternary": 0,
13
- "no-underscore-dangle": 0,
14
- "import/no-unresolved": 0,
15
- "import/extensions": 0,
16
- "react/jsx-filename-extension": 0,
17
- "react/forbid-prop-types": 1,
18
- "jsx-a11y/label-has-for": 1,
19
- "jsx-a11y/no-static-element-interactions": 1,
20
- "no-plusplus": 0
21
- }
22
- }
@@ -1,20 +0,0 @@
1
- name-template: 'v$RESOLVED_VERSION 🌈'
2
- tag-template: 'v$RESOLVED_VERSION'
3
- categories:
4
- - title: '🚀 Features'
5
- labels:
6
- - 'feature'
7
- - 'enhancement'
8
- - title: '🐛 Bug Fixes'
9
- labels:
10
- - 'fix'
11
- - 'bugfix'
12
- - 'bug'
13
- - title: '🧰 Maintenance'
14
- label: 'chore'
15
- change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
16
- change-title-escapes: '\<*_&'
17
- template: |
18
- ## Changes
19
-
20
- $CHANGES
@@ -1,24 +0,0 @@
1
- name: build dist and example
2
-
3
- on:
4
- push:
5
- branches:
6
- - master
7
-
8
- jobs:
9
- update_dist:
10
- runs-on: ubuntu-latest
11
- steps:
12
- - uses: actions/checkout@v2
13
- with:
14
- ref: ${{ github.head_ref }}
15
- - uses: actions/setup-node@v1
16
- with:
17
- node-version: 18
18
- - run: npm ci
19
- - run: npm run build
20
-
21
- - uses: stefanzweifel/git-auto-commit-action@v4
22
- with:
23
- commit_message: Apply automatic changes
24
- # file_pattern: src/*.js dist/*.js dist/*.css example/* index.html
@@ -1,22 +0,0 @@
1
- # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2
- # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3
-
4
-
5
- name: Node.js Package
6
-
7
- on:
8
- release:
9
- types: [released]
10
-
11
- jobs:
12
- publish-npm:
13
- runs-on: ubuntu-latest
14
- steps:
15
- - uses: actions/checkout@v2
16
- - uses: actions/setup-node@v1
17
- with:
18
- node-version: 12
19
- registry-url: https://registry.npmjs.org/
20
- - run: npm publish --access public
21
- env:
22
- NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
@@ -1,16 +0,0 @@
1
- name: Release Drafter
2
-
3
- on:
4
- push:
5
- branches:
6
- - master
7
-
8
- jobs:
9
- update_release_draft:
10
- runs-on: ubuntu-latest
11
- steps:
12
- - uses: release-drafter/release-drafter@v5
13
- with:
14
- config-name: release-drafter-config.yml
15
- env:
16
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
package/.prettierrc DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "trailingComma": "all",
3
- "tabWidth": 2,
4
- "semi": true,
5
- "singleQuote": true,
6
- "printWidth": 100,
7
- "useTabs": false,
8
- "bracketSpacing": true,
9
- "jsxBracketSameLine": false,
10
- "arrowParens": "avoid",
11
- "overrides": [
12
- {
13
- "files": ["*.ts", "*.tsx", "*.js"],
14
- "options": {
15
- "parser": "typescript"
16
- }
17
- }
18
- ]
19
- }
package/.travis.yml DELETED
@@ -1,16 +0,0 @@
1
- language: node_js
2
- node_js:
3
- - '12'
4
- before_script: npm run webpack:dev && npm run webpack:prod
5
- cache:
6
- directories:
7
- - node_modules
8
- script:
9
- - npm run test-ci
10
- deploy:
11
- provider: npm
12
- email: max.mykhailenko@gmail.com
13
- on:
14
- tags: true
15
- repo: memCrab/react-pure-modal
16
- branch: master
@@ -1,92 +0,0 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
2
-
3
- exports[`Should be closed 1`] = `null`;
4
-
5
- exports[`Should contain width attribute 1`] = `
6
- <div
7
- className="pure-modal-backdrop"
8
- onMouseDown={[Function]}
9
- >
10
- <div
11
- className="pure-modal 0.5"
12
- style={
13
- Object {
14
- "transform": "translate(0px, 0px)",
15
- "transition": "none",
16
- "width": "400px",
17
- }
18
- }
19
- >
20
- <span>
21
- Some content of modal
22
- </span>
23
- </div>
24
- </div>
25
- `;
26
-
27
- exports[`Should show content 1`] = `
28
- <div
29
- className="pure-modal-backdrop"
30
- onMouseDown={[Function]}
31
- >
32
- <div
33
- className="pure-modal 0.5"
34
- style={
35
- Object {
36
- "transform": "translate(0px, 0px)",
37
- "transition": "none",
38
- "width": undefined,
39
- }
40
- }
41
- >
42
- <div
43
- className="panel panel-default "
44
- >
45
- <div
46
- className="panel-heading"
47
- />
48
- <div
49
- className="panel-body scrollable"
50
- >
51
- <span>
52
- Some content of modal
53
- </span>
54
- </div>
55
- <div
56
- className="close"
57
- onClick={[Function]}
58
- style={
59
- Object {
60
- "margin": "",
61
- "position": "absolute",
62
- }
63
- }
64
- >
65
- ×
66
- </div>
67
- </div>
68
- </div>
69
- </div>
70
- `;
71
-
72
- exports[`Should show only content 1`] = `
73
- <div
74
- className="pure-modal-backdrop"
75
- onMouseDown={[Function]}
76
- >
77
- <div
78
- className="pure-modal 0.5"
79
- style={
80
- Object {
81
- "transform": "translate(0px, 0px)",
82
- "transition": "none",
83
- "width": undefined,
84
- }
85
- }
86
- >
87
- <span>
88
- Some content of modal
89
- </span>
90
- </div>
91
- </div>
92
- `;
@@ -1,44 +0,0 @@
1
- import React from 'react';
2
- import Modal from '../dist/react-pure-modal.min.js';
3
- import renderer from 'react-test-renderer';
4
-
5
- const mockMath = Object.create(global.Math);
6
- mockMath.random = () => 0.5;
7
- global.Math = mockMath;
8
-
9
- it('Should be null without props', () => {
10
- const component = renderer.create(<Modal />);
11
- expect(component.toJSON()).toBeNull();
12
- });
13
-
14
- it('Should be closed', () => {
15
- const component = renderer.create(<Modal isOpen={false} />);
16
- expect(component.toJSON()).toMatchSnapshot();
17
- });
18
-
19
- it('Should show content', () => {
20
- const component = renderer.create(
21
- <Modal isOpen>
22
- <span>Some content of modal</span>
23
- </Modal>
24
- );
25
- expect(component.toJSON()).toMatchSnapshot();
26
- });
27
-
28
- it('Should show only content', () => {
29
- const component = renderer.create(
30
- <Modal replace isOpen>
31
- <span>Some content of modal</span>
32
- </Modal>
33
- );
34
- expect(component.toJSON()).toMatchSnapshot();
35
- });
36
-
37
- it('Should contain width attribute', () => {
38
- const component = renderer.create(
39
- <Modal replace width="400px" isOpen>
40
- <span>Some content of modal</span>
41
- </Modal>
42
- );
43
- expect(component.toJSON()).toMatchSnapshot();
44
- });
@@ -1,25 +0,0 @@
1
- import React from 'react';
2
- import type { MouseOrTouch } from './types';
3
- declare type Props = {
4
- replace: boolean;
5
- children: JSX.Element;
6
- onDragStart?: (event: MouseOrTouch) => unknown;
7
- onDragEnd?: (event: MouseOrTouch) => unknown;
8
- onClose?: (event: React.MouseEvent<HTMLDivElement>) => void;
9
- bodyClass?: string;
10
- header?: JSX.Element | string;
11
- footer?: JSX.Element | string;
12
- closeButton: JSX.Element | string;
13
- closeButtonPosition: string;
14
- draggable: boolean;
15
- };
16
- declare function PureModalContent(props: Props): JSX.Element;
17
- declare namespace PureModalContent {
18
- var defaultProps: {
19
- closeButton: string;
20
- closeButtonPosition: string;
21
- replace: boolean;
22
- draggable: boolean;
23
- };
24
- }
25
- export default PureModalContent;
@@ -1,20 +0,0 @@
1
- import React from 'react';
2
- import './react-pure-modal.css';
3
- declare type Props = {
4
- children: JSX.Element;
5
- replace?: boolean;
6
- className?: string;
7
- header?: JSX.Element | string;
8
- footer?: JSX.Element | string;
9
- scrollable?: boolean;
10
- draggable?: boolean;
11
- width?: string;
12
- isOpen?: boolean;
13
- onClose?: Function;
14
- closeButton?: JSX.Element | string;
15
- closeButtonPosition?: string;
16
- portal?: boolean;
17
- };
18
- declare function PureModal(props: Props): JSX.Element | null;
19
- declare const _default: React.MemoExoticComponent<typeof PureModal>;
20
- export default _default;