diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..349b2b7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: Mobx React Form Devtools +on: + push: + branches: + - master +jobs: + CI: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - run: npm install --force + - run: npm run cover + - run: npm run coverage:check + - run: npm run build + - run: npm run coverage:report + - name: Semantic Release + uses: cycjimmy/semantic-release-action@v3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/README.md b/README.md index 1da60d3..c43515f 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ npm install --save mobx-react-form-devtools ```javascript import MobxReactFormDevTools from 'mobx-react-form-devtools'; -// register forms +// register forms (mobx-react-form Instances) MobxReactFormDevTools.register({ loginForm, registerForm, diff --git a/package-lock.json b/package-lock.json index 0dcf232..5d136e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,8 @@ "classnames": "^2.3.2", "lodash": "^4.17.21", "react-dock": "0.2.4", - "react-draggable": "^4.4.5", "react-icons": "^2.2.7", - "react-json-tree": "0.11.0", - "react-tooltip": "^5.8.3" + "react-json-tree": "0.11.0" }, "devDependencies": { "@types/react": "^18.0.28", @@ -53,7 +51,8 @@ "mobx-react": "*", "mobx-react-form": "*", "prop-types": "*", - "react": "*" + "react": "*", + "react-dom": "*" } }, "node_modules/@ampproject/remapping": { @@ -899,19 +898,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@floating-ui/core": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.2.tgz", - "integrity": "sha512-FaO9KVLFnxknZaGWGmNtjD2CVFuc0u4yeGEofoyXO2wgRA7fLtkngT6UB0vtWQWuhH3iMTZZ/Y89CMeyGfn8pA==" - }, - "node_modules/@floating-ui/dom": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.1.tgz", - "integrity": "sha512-TpIO93+DIujg3g7SykEAGZMDtbJRrmnYRCNYSjJlvIbGhBjRSNTLVbNeDQBrzy9qDgUbiWdc7KA0uZHZ2tJmiw==", - "dependencies": { - "@floating-ui/core": "^1.1.0" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -2907,14 +2893,6 @@ "node": ">=0.10.0" } }, - "node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "engines": { - "node": ">=6" - } - }, "node_modules/codecov": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.8.3.tgz", @@ -7054,78 +7032,6 @@ "node": ">= 6" } }, - "node_modules/mobx": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.9.0.tgz", - "integrity": "sha512-HdKewQEREEJgsWnErClfbFoVebze6rGazxFLU/XUyrII8dORfVszN1V0BMRnQSzcgsNNtkX8DHj3nC6cdWE9YQ==", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mobx" - } - }, - "node_modules/mobx-react": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-7.6.0.tgz", - "integrity": "sha512-+HQUNuh7AoQ9ZnU6c4rvbiVVl+wEkb9WqYsVDzGLng+Dqj1XntHu79PvEWKtSMoMj67vFp/ZPXcElosuJO8ckA==", - "peer": true, - "dependencies": { - "mobx-react-lite": "^3.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mobx" - }, - "peerDependencies": { - "mobx": "^6.1.0", - "react": "^16.8.0 || ^17 || ^18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, - "node_modules/mobx-react-form": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/mobx-react-form/-/mobx-react-form-6.3.1.tgz", - "integrity": "sha512-wbBZYI/tWopY+qpMg6YVQDLJNBFZgW664ocUEdjrtA8zLysGNszwzpeyqY/9M8ThZc3kVxTqAT1t95eIWjJPeg==", - "peer": true, - "dependencies": { - "lodash": "^4.17.21" - }, - "engines": { - "node": ">=8.0.0" - }, - "peerDependencies": { - "mobx": "^6.0.0" - } - }, - "node_modules/mobx-react-lite": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.4.3.tgz", - "integrity": "sha512-NkJREyFTSUXR772Qaai51BnE1voWx56LOL80xG7qkZr6vo8vEaLF3sz1JNUVh+rxmUzxYaqOhfuxTfqUh0FXUg==", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mobx" - }, - "peerDependencies": { - "mobx": "^6.1.0", - "react": "^16.8.0 || ^17 || ^18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, "node_modules/mocha": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", @@ -11202,6 +11108,7 @@ "version": "16.14.0", "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "dev": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -11239,6 +11146,7 @@ "version": "16.14.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "dev": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -11249,19 +11157,6 @@ "react": "^16.14.0" } }, - "node_modules/react-draggable": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.5.tgz", - "integrity": "sha512-OMHzJdyJbYTZo4uQE393fHcqqPYsEtkjfMgvCHr6rejT+Ezn4OZbNyGH50vv+SunC1RMvwOTSWkEODQLzw1M9g==", - "dependencies": { - "clsx": "^1.1.1", - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "react": ">= 16.3.0", - "react-dom": ">= 16.3.0" - } - }, "node_modules/react-icon-base": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/react-icon-base/-/react-icon-base-2.1.0.tgz", @@ -11303,19 +11198,6 @@ "react-dom": "^15.0.0 || ^16.0.0" } }, - "node_modules/react-tooltip": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.8.3.tgz", - "integrity": "sha512-h7maAlm2Xeymc14gWKhhrzsENeB83N65EzZ+AcQIGrOpNE0yefVRJIHhNcWHEJ0FEtf7VZXxtsj5glVXKxEtvA==", - "dependencies": { - "@floating-ui/dom": "1.1.1", - "classnames": "^2.3.2" - }, - "peerDependencies": { - "react": ">=16.14.0", - "react-dom": ">=16.14.0" - } - }, "node_modules/read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -11684,6 +11566,7 @@ "version": "0.19.1", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "dev": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" diff --git a/package.json b/package.json index 44f5749..e32a768 100644 --- a/package.json +++ b/package.json @@ -55,17 +55,16 @@ "classnames": "^2.3.2", "lodash": "^4.17.21", "react-dock": "0.2.4", - "react-draggable": "^4.4.5", "react-icons": "^2.2.7", - "react-json-tree": "0.11.0", - "react-tooltip": "^5.8.3" + "react-json-tree": "0.11.0" }, "peerDependencies": { "mobx": "*", "mobx-react": "*", "mobx-react-form": "*", "prop-types": "*", - "react": "*" + "react": "*", + "react-dom": "*" }, "devDependencies": { "@types/react": "^18.0.28", diff --git a/src/actions.ts b/src/actions.ts index 80849e2..fbab633 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -44,5 +44,17 @@ export default $store => ({ } }), + openInWindow: action(() => { + _.set($store, 'mode', 'windowed'); + _.set($store.dock, 'position', 'left'); + _.set($store.dock, 'fluid', true); + }), + + onCloseWindow: action(() => { + _.set($store, 'mode', 'docked'); + _.set($store.dock, 'position', 'right'); + _.set($store.dock, 'fluid', false); + }), + }); diff --git a/src/components/Draggable.tsx b/src/components/Draggable.tsx index bb44b2e..9cd3310 100644 --- a/src/components/Draggable.tsx +++ b/src/components/Draggable.tsx @@ -1,41 +1,55 @@ -import React from 'react'; +import React, { useRef } from 'react'; import { observer } from 'mobx-react'; import cx from 'classnames'; -import Draggable from 'react-draggable'; +// import Draggable from 'react-draggable'; import { FaBars, FaChevronLeft, FaBook, + FaWindows, } from '../icons'; import $U from '../styles/_.utils'; import style from '../styles/Draggable'; -export default observer(({ handlers }) => ( - -
-
-
- -
- - -
+export default observer(({ handlers }) => { + + const handleOnDragEnd = (e) => { + e.target.style.top = (e.clientY - 100) + "px"; + }; + + return ( + //
+
+ + + +
- -)); + // +)}); diff --git a/src/components/FormControls.tsx b/src/components/FormControls.tsx index b5d1e69..0df60c2 100644 --- a/src/components/FormControls.tsx +++ b/src/components/FormControls.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { observer } from 'mobx-react'; import cx from 'classnames'; -import { Tooltip } from 'react-tooltip'; import { FaDotCircleO, @@ -16,16 +15,11 @@ const { icon, iconOptionsActive } = style.controls; export default observer(({ store, handlers }) => (
- @@ -33,8 +27,7 @@ export default observer(({ store, handlers }) => ( type="button" className={cx($U.button, style.controls.button, 'tooltip')} onClick={handlers.handleFormOnClear} - data-tooltip-id="CLEAR" - data-tooltip-content="CLEAR" + title="Clear Form" > @@ -42,8 +35,7 @@ export default observer(({ store, handlers }) => ( type="button" className={cx($U.button, style.controls.button, 'tooltip')} onClick={handlers.handleFormOnReset} - data-tooltip-id="RESET" - data-tooltip-content="RESET" + title="Reset Form" > @@ -51,8 +43,7 @@ export default observer(({ store, handlers }) => ( type="button" className={cx($U.button, style.controls.button, 'tooltip')} onClick={handlers.handleToggleOptions} - data-tooltip-id="OPTIONS" - data-tooltip-content="OPTIONS" + title="Form Settings" > diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 78c0ce9..3884e50 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -5,12 +5,14 @@ import cx from 'classnames'; import { FaChevronCircleRight, FaBook, + FaWindows, } from '../icons'; + import $U from '../styles/_.utils'; import style from '../styles/Header'; export default observer(({ store, handlers }) => ( -
+
MOBX-REACT-FORM @@ -19,19 +21,26 @@ export default observer(({ store, handlers }) => ( DEVTOOLS
- + } + {!store.windowIsOpen && }
)); diff --git a/src/components/RenderFormData.tsx b/src/components/RenderFormData.tsx index ab82d1c..a89710a 100644 --- a/src/components/RenderFormData.tsx +++ b/src/components/RenderFormData.tsx @@ -19,10 +19,14 @@ import style from '../styles/RenderFormData'; export default observer(({ store, handlers }) => (

-
+
Form
-
+

diff --git a/src/components/Window.tsx b/src/components/Window.tsx new file mode 100755 index 0000000..87a1b56 --- /dev/null +++ b/src/components/Window.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { observer } from 'mobx-react'; +import cx from 'classnames'; + +import Header from './Header'; +import SelectInitialForm from './SelectInitialForm'; +import RenderFormData from './RenderFormData'; + +import style from '../styles/Dock'; + +export default observer(({ store, handlers }) => ( +
+ +
+ + {(store.selected.form && store.selected.key) + ? + : } + +
+)); diff --git a/src/components/WindowPortal.tsx b/src/components/WindowPortal.tsx new file mode 100644 index 0000000..bd84646 --- /dev/null +++ b/src/components/WindowPortal.tsx @@ -0,0 +1,26 @@ +import { useEffect, useRef } from "react"; +import ReactDOM from "react-dom"; +import { copyStyles } from "../utils"; + +export const WindowPortal = ({ children, closeWindowPortal }) => { + const externalWindow = useRef( + window.open("", "", "width=350,height=650,left=200,top=200") + ); + + const containerEl = document.createElement("div"); + + useEffect(() => { + const currentWindow = externalWindow.current; + return () => currentWindow.close(); + }, []); + + externalWindow.current.document.title = "mobx-react-form-devtools"; + externalWindow.current.document.body.appendChild(containerEl); + copyStyles(document, externalWindow.current.document); + + externalWindow.current.addEventListener("beforeunload", (e) => { + closeWindowPortal(e); + }); + + return ReactDOM.createPortal(children, containerEl); +}; diff --git a/src/handlers.ts b/src/handlers.ts index dd7451e..2f4ce1b 100644 --- a/src/handlers.ts +++ b/src/handlers.ts @@ -49,4 +49,14 @@ export default $actions => ({ $actions.handleFormControls('reset'); }, + handleOpenInWindow: (e) => { + e.preventDefault(); + $actions.openInWindow(); + }, + + handleOnCloseWindow: (e) => { + e.preventDefault(); + $actions.onCloseWindow(); + }, + }); diff --git a/src/icons.ts b/src/icons.ts index 263e139..45dd505 100644 --- a/src/icons.ts +++ b/src/icons.ts @@ -8,6 +8,7 @@ import FaCircleO from 'react-icons/lib/fa/circle-o'; import FaDotCircleO from 'react-icons/lib/fa/dot-circle-o'; import FaChevronLeft from 'react-icons/lib/fa/chevron-left'; import FaChevronCircleRight from 'react-icons/lib/fa/chevron-circle-right'; +import FaWindows from 'react-icons/lib/md/desktop-windows'; export { FaTh, @@ -20,4 +21,5 @@ export { FaDotCircleO, FaChevronLeft, FaChevronCircleRight, + FaWindows, }; diff --git a/src/index.tsx b/src/index.tsx index bff25f8..fabf178 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,12 +1,14 @@ -import 'react-tooltip/dist/react-tooltip.css' import React from 'react'; import { observe, action } from 'mobx'; import Dock from './components/Dock'; import FormOptions from './components/FormOptions'; +import { WindowPortal } from './components/WindowPortal'; import store from './store'; import actions from './actions'; import handlers from './handlers'; +import { observer } from 'mobx-react'; +import Window from './components/Window'; const $actions = actions(store); const $handlers = handlers($actions); @@ -27,8 +29,14 @@ export default { select: $actions.selectForm, - UI: () => (), + Options: () => , - Options: () => (), + UI: observer(() => + store.windowIsOpen + ? + + + : + ), }; diff --git a/src/store.ts b/src/store.ts index 7d33759..00578b6 100644 --- a/src/store.ts +++ b/src/store.ts @@ -3,6 +3,7 @@ import theme from './styles/_.theme'; import {makeFormOptions} from './form.options'; export default makeAutoObservable({ + mode: 'docked', theme, showOptions: false, open: false, @@ -26,4 +27,9 @@ export default makeAutoObservable({ get formOptions() { return makeFormOptions(this.selected.form); }, + + get windowIsOpen() { + return this.mode === 'windowed'; + }, + }); diff --git a/src/styles/Dock.ts b/src/styles/Dock.ts index d580f59..7b8a7af 100644 --- a/src/styles/Dock.ts +++ b/src/styles/Dock.ts @@ -1,6 +1,13 @@ import { css } from '@emotion/css' +import theme from './_.theme'; export default { + window: css({ + fontFamily: 'Helvetica Neue', + background: theme.base00, + marginTop: '-45px', + paddingBottom: '20px', + }), dock: css({ 'fontFamily': 'Helvetica Neue', '@media (min-width: 0px) and (max-width: 450px)': { @@ -12,4 +19,5 @@ export default { right: '0px', top: '100px', }), + }; diff --git a/src/styles/Draggable.ts b/src/styles/Draggable.ts index e5bf628..5e212cc 100644 --- a/src/styles/Draggable.ts +++ b/src/styles/Draggable.ts @@ -10,12 +10,17 @@ export default { color: theme.base04, }), draggable: css({ - cursor: 'ns-resize', + position: 'absolute', + right: 0, + top: 0, padding: '5px 5px 7px 5px', background: theme.base00, borderTopLeftRadius: '5px', borderBottomLeftRadius: '5px', }), + dragButton: css({ + cursor: 'ns-resize', + }), btn: css({ 'display': 'block', 'width': '20px', diff --git a/src/styles/RenderFormData.ts b/src/styles/RenderFormData.ts index d96e342..fa56f8b 100644 --- a/src/styles/RenderFormData.ts +++ b/src/styles/RenderFormData.ts @@ -16,6 +16,9 @@ export default { select: css({ textAlign: 'right', }), + windowed: css({ + marginTop: '45px', + }), icon: css({ marginTop: '-3px', }), diff --git a/src/utils.ts b/src/utils.ts index 65a366e..a3f0f51 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,17 @@ export const mapo = (object, callback) => Object.keys(object).map(key => - callback(key, object[key])); \ No newline at end of file + callback(key, object[key])); + +export function copyStyles(sourceDoc, targetDoc) { + Array.from(sourceDoc.styleSheets).forEach((styleSheet: any) => { + if (styleSheet.cssRules) { + const newStyleEl = sourceDoc.createElement("style"); + + Array.from(styleSheet.cssRules).forEach((cssRule: any) => { + newStyleEl.appendChild(sourceDoc.createTextNode(cssRule.cssText)); + }); + + targetDoc.head.appendChild(newStyleEl); + } + }); +}