This commit is contained in:
Stefan Pejcic
2024-11-07 19:03:37 +01:00
parent c6df945ed5
commit 09f9f9502d
2472 changed files with 620417 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
node_modules
.DS_Store
test
jest.config.js
**/*.spec.ts
**/*.spec.tsx
**/*.test.ts
**/*.test.tsx
tsup.config.ts
tsconfig.test.json
tsconfig.declarations.json

View File

@@ -0,0 +1,443 @@
# @refinedev/devtools
## 1.2.9
### Patch Changes
- Updated dependencies [[`da9da4ed1a9700c7a48db6520d683168c48b226e`](https://github.com/refinedev/refine/commit/da9da4ed1a9700c7a48db6520d683168c48b226e), [`451016a207d4dd6aecb4d56133efc1ad6229acff`](https://github.com/refinedev/refine/commit/451016a207d4dd6aecb4d56133efc1ad6229acff), [`da9da4ed1a9700c7a48db6520d683168c48b226e`](https://github.com/refinedev/refine/commit/da9da4ed1a9700c7a48db6520d683168c48b226e)]:
- @refinedev/devtools-server@1.1.37
- @refinedev/cli@2.16.39
## 1.2.8
### Patch Changes
- Updated dependencies [[`6963e591f8f307aee9362d5dfff99972eb64bf03`](https://github.com/refinedev/refine/commit/6963e591f8f307aee9362d5dfff99972eb64bf03), [`f5501f93a818d6e5811aa94cb354d77a2b1eb1ff`](https://github.com/refinedev/refine/commit/f5501f93a818d6e5811aa94cb354d77a2b1eb1ff)]:
- @refinedev/devtools-server@1.1.36
- @refinedev/cli@2.16.38
## 1.2.7
### Patch Changes
- [#6228](https://github.com/refinedev/refine/pull/6228) [`4e375902943c356f269e9f596103fd681ee9afb8`](https://github.com/refinedev/refine/commit/4e375902943c356f269e9f596103fd681ee9afb8) Thanks [@aliemir](https://github.com/aliemir)! - refactor(devtools): check both parent and child nodes for representation
Previously, Refine Devtools's X-Ray feature looked for the representation of the components by looking at the parent nodes until a proper `stateNode` was found. This was problematic when the parent node was not a proper HTML element. A lack of type checking caused the feature to break in runtime in some cases.
Adding only a type check for the `stateNode` is not enough since there may be cases where there are no proper HTML elements in the parent nodes. This change adds a check for the child nodes as well. This way, the feature will look for the representation in both the parent and child nodes.
First check for a representation node will be done in the child nodes. If a proper representation is not found, an element will be searched in the parent nodes. If a no proper representation is found in the parent nodes, `document.body` will be used as the representation.
[Resolves #6219](https://github.com/refinedev/refine/issues/6219)
- [#6228](https://github.com/refinedev/refine/pull/6228) [`4e375902943c356f269e9f596103fd681ee9afb8`](https://github.com/refinedev/refine/commit/4e375902943c356f269e9f596103fd681ee9afb8) Thanks [@aliemir](https://github.com/aliemir)! - fix(devtools): styling issues in the X-Ray feature
A minimum size was set for the X-Ray feature's overlay to prevent it from being too small.
- [#6185](https://github.com/refinedev/refine/pull/6185) [`603c73eb7d376fc2357a577f5921f844a8f444e4`](https://github.com/refinedev/refine/commit/603c73eb7d376fc2357a577f5921f844a8f444e4) Thanks [@aliemir](https://github.com/aliemir)! - feat(devtools): ability to change the port of the devtools server
Now users can change the port of the devtools server by setting the `REFINE_DEVTOOLS_PORT` environment variable. Previously, the port was hardcoded to "5001" and could not be changed.
If you're using `@refinedev/cli`'s runner commands to start your development server, `REFINE_DEVTOOLS_PORT` will be propagated to your app with appropriate prefix. E.g. if you're using Vite, the environment variable will be `VITE_REFINE_DEVTOOLS_PORT` and it will be used by the `@refinedev/devtools`'s `<DevtoolsProvider />` component to connect to the devtools server.
- In Next.js apps, it will be prefixed with `NEXT_PUBLIC_`
- In Craco and Create React App apps, it will be prefixed with `REACT_APP_`
- In Remix apps and other custom setups, the environment variable will be used as is.
In some scenarios where the environment variables are not passed to the browser, you may need to manually set the Refine Devtools URL in the `<DevtoolsProvider />` component via the `url` prop. Remix apps do not automatically pass environment variables to the browser, so you will need to set the URL manually. If not set, the default URL will be used.
While the port can be changed, this feature also allows users to host the devtools server on a different machine or domain and provide the `<DevtoolsProvider />` with the custom domain URL. This such case will be useful if you're dockerizing your app and devtools server separately.
**Enterprise Edition**: Refine Devtools running on ports other than "5001" is only available in the Enterprise Edition. If you're using the Community Edition, Refine Devtools will not work if the port is changed.
[Resolves #5111](https://github.com/refinedev/refine/issues/5111)
- Updated dependencies [[`d7fb07e59ddcbef49437c64d3a92b3d47d850225`](https://github.com/refinedev/refine/commit/d7fb07e59ddcbef49437c64d3a92b3d47d850225), [`cbf2fd70a6a0d54722b6541c948ce8cb3f682fb4`](https://github.com/refinedev/refine/commit/cbf2fd70a6a0d54722b6541c948ce8cb3f682fb4), [`c3a75139f82de022b54855e87e200ab38c803af5`](https://github.com/refinedev/refine/commit/c3a75139f82de022b54855e87e200ab38c803af5), [`9806a3629256d73bdc18ae808dce217f0108aad2`](https://github.com/refinedev/refine/commit/9806a3629256d73bdc18ae808dce217f0108aad2), [`e2b467528f6a799c3219e3a8fefd4834a0ca0431`](https://github.com/refinedev/refine/commit/e2b467528f6a799c3219e3a8fefd4834a0ca0431), [`603c73eb7d376fc2357a577f5921f844a8f444e4`](https://github.com/refinedev/refine/commit/603c73eb7d376fc2357a577f5921f844a8f444e4), [`603c73eb7d376fc2357a577f5921f844a8f444e4`](https://github.com/refinedev/refine/commit/603c73eb7d376fc2357a577f5921f844a8f444e4)]:
- @refinedev/cli@2.16.37
- @refinedev/devtools-server@1.1.35
- @refinedev/devtools-shared@1.1.12
## 1.2.6
### Patch Changes
- [#6098](https://github.com/refinedev/refine/pull/6098) [`8bc2c1c6790d1e098ce0d98e01f608e3310f7b4a`](https://github.com/refinedev/refine/commit/8bc2c1c6790d1e098ce0d98e01f608e3310f7b4a) Thanks [@aliemir](https://github.com/aliemir)! - chore(devtools): update devtools url fallback values
Updated fallback values for the Devtools URL and use single fallback value until its provided by the `@refinedev/devtools-server` when client is connected.
- Updated dependencies [[`8bc2c1c6790d1e098ce0d98e01f608e3310f7b4a`](https://github.com/refinedev/refine/commit/8bc2c1c6790d1e098ce0d98e01f608e3310f7b4a), [`8bc2c1c6790d1e098ce0d98e01f608e3310f7b4a`](https://github.com/refinedev/refine/commit/8bc2c1c6790d1e098ce0d98e01f608e3310f7b4a), [`8bc2c1c6790d1e098ce0d98e01f608e3310f7b4a`](https://github.com/refinedev/refine/commit/8bc2c1c6790d1e098ce0d98e01f608e3310f7b4a), [`8bc2c1c6790d1e098ce0d98e01f608e3310f7b4a`](https://github.com/refinedev/refine/commit/8bc2c1c6790d1e098ce0d98e01f608e3310f7b4a), [`24db047aea42e307a9662c46fde50ea69ca8c381`](https://github.com/refinedev/refine/commit/24db047aea42e307a9662c46fde50ea69ca8c381), [`8bc2c1c6790d1e098ce0d98e01f608e3310f7b4a`](https://github.com/refinedev/refine/commit/8bc2c1c6790d1e098ce0d98e01f608e3310f7b4a), [`8bc2c1c6790d1e098ce0d98e01f608e3310f7b4a`](https://github.com/refinedev/refine/commit/8bc2c1c6790d1e098ce0d98e01f608e3310f7b4a), [`50d21076928ca738ec54cc5bcd17fad2683653dd`](https://github.com/refinedev/refine/commit/50d21076928ca738ec54cc5bcd17fad2683653dd)]:
- @refinedev/devtools-server@1.1.34
- @refinedev/cli@2.16.36
- @refinedev/devtools-shared@1.1.11
## 1.2.4
### Patch Changes
- [#6059](https://github.com/refinedev/refine/pull/6059) [`ad42665ad9ccb07f6090da353377d016b67acdd0`](https://github.com/refinedev/refine/commit/ad42665ad9ccb07f6090da353377d016b67acdd0) Thanks [@aliemir](https://github.com/aliemir)! - fix(devtools): failing authentication checks
Devtools was failing on determining the auth status and always ended up redirecting to the login page or the onboarding step regardless of the actual authentication status.
Resolves [#6047](https://github.com/refinedev/refine/issues/6047)
- Updated dependencies [[`ad42665ad9ccb07f6090da353377d016b67acdd0`](https://github.com/refinedev/refine/commit/ad42665ad9ccb07f6090da353377d016b67acdd0), [`ad42665ad9ccb07f6090da353377d016b67acdd0`](https://github.com/refinedev/refine/commit/ad42665ad9ccb07f6090da353377d016b67acdd0)]:
- @refinedev/devtools-server@1.1.32
- @refinedev/cli@2.16.34
## 1.2.3
### Patch Changes
- [`6bd14228760d3e1e205ea9248e427f9afa2ec046`](https://github.com/refinedev/refine/commit/6bd14228760d3e1e205ea9248e427f9afa2ec046) Thanks [@BatuhanW](https://github.com/BatuhanW)! - chore: added `type` qualifier to imports used as type only.
```diff
- import { A } from "./example.ts";
+ import type { A } from "./example.ts";
```
- Updated dependencies [[`6bd14228760d3e1e205ea9248e427f9afa2ec046`](https://github.com/refinedev/refine/commit/6bd14228760d3e1e205ea9248e427f9afa2ec046), [`6bd14228760d3e1e205ea9248e427f9afa2ec046`](https://github.com/refinedev/refine/commit/6bd14228760d3e1e205ea9248e427f9afa2ec046), [`6bd14228760d3e1e205ea9248e427f9afa2ec046`](https://github.com/refinedev/refine/commit/6bd14228760d3e1e205ea9248e427f9afa2ec046), [`6bd14228760d3e1e205ea9248e427f9afa2ec046`](https://github.com/refinedev/refine/commit/6bd14228760d3e1e205ea9248e427f9afa2ec046), [`6bd14228760d3e1e205ea9248e427f9afa2ec046`](https://github.com/refinedev/refine/commit/6bd14228760d3e1e205ea9248e427f9afa2ec046), [`6bd14228760d3e1e205ea9248e427f9afa2ec046`](https://github.com/refinedev/refine/commit/6bd14228760d3e1e205ea9248e427f9afa2ec046), [`6bd14228760d3e1e205ea9248e427f9afa2ec046`](https://github.com/refinedev/refine/commit/6bd14228760d3e1e205ea9248e427f9afa2ec046), [`6bd14228760d3e1e205ea9248e427f9afa2ec046`](https://github.com/refinedev/refine/commit/6bd14228760d3e1e205ea9248e427f9afa2ec046), [`6bd14228760d3e1e205ea9248e427f9afa2ec046`](https://github.com/refinedev/refine/commit/6bd14228760d3e1e205ea9248e427f9afa2ec046)]:
- @refinedev/cli@2.16.33
- @refinedev/devtools-server@1.1.31
- @refinedev/devtools-shared@1.1.9
## 1.2.2
### Patch Changes
- [#5945](https://github.com/refinedev/refine/pull/5945) [`90930b381d8d369c63bc59beedf69c391875166d`](https://github.com/refinedev/refine/commit/90930b381d8d369c63bc59beedf69c391875166d) Thanks [@aliemir](https://github.com/aliemir)! - chore: added `type` qualifier to imports used as type only.
```diff
- import { A } from "./example.ts";
+ import type { A } from "./example.ts";
```
- Updated dependencies [[`45b68cc3450618468e938f9540dc52ff088b555a`](https://github.com/refinedev/refine/commit/45b68cc3450618468e938f9540dc52ff088b555a), [`429009db854653ab3ca00fbfb84561de38b3a255`](https://github.com/refinedev/refine/commit/429009db854653ab3ca00fbfb84561de38b3a255), [`6c22ece19f44ca2b99ad70543f9ee40b4b139863`](https://github.com/refinedev/refine/commit/6c22ece19f44ca2b99ad70543f9ee40b4b139863), [`429009db854653ab3ca00fbfb84561de38b3a255`](https://github.com/refinedev/refine/commit/429009db854653ab3ca00fbfb84561de38b3a255), [`bb89dc34bf6ef061d0bcdcf0cb3173fe7014ae5e`](https://github.com/refinedev/refine/commit/bb89dc34bf6ef061d0bcdcf0cb3173fe7014ae5e), [`6c22ece19f44ca2b99ad70543f9ee40b4b139863`](https://github.com/refinedev/refine/commit/6c22ece19f44ca2b99ad70543f9ee40b4b139863), [`6c22ece19f44ca2b99ad70543f9ee40b4b139863`](https://github.com/refinedev/refine/commit/6c22ece19f44ca2b99ad70543f9ee40b4b139863), [`90930b381d8d369c63bc59beedf69c391875166d`](https://github.com/refinedev/refine/commit/90930b381d8d369c63bc59beedf69c391875166d), [`bb89dc34bf6ef061d0bcdcf0cb3173fe7014ae5e`](https://github.com/refinedev/refine/commit/bb89dc34bf6ef061d0bcdcf0cb3173fe7014ae5e)]:
- @refinedev/cli@2.16.32
- @refinedev/devtools-server@1.1.30
- @refinedev/devtools-shared@1.1.8
## 1.2.1
### Patch Changes
- [#5928](https://github.com/refinedev/refine/pull/5928) [`db9756e7908`](https://github.com/refinedev/refine/commit/db9756e79086ff80774ee75d570d610bf0d5d76d) Thanks [@aliemir](https://github.com/aliemir)! - fix: type errors on typescript <5
Due to the changes in #5881, typescript users below version 5 are facing type errors. This PR fixes the type errors by updating the file extensions required by the `d.mts` declaration files to provide a compatible declarations for both typescript 4 and 5 users.
- Updated dependencies [[`db9756e7908`](https://github.com/refinedev/refine/commit/db9756e79086ff80774ee75d570d610bf0d5d76d)]:
- @refinedev/cli@2.16.31
- @refinedev/devtools-shared@1.1.7
- @refinedev/devtools-server@1.1.29
## 1.2.0
### Minor Changes
- [#5898](https://github.com/refinedev/refine/pull/5898) [`93c35d82a9c`](https://github.com/refinedev/refine/commit/93c35d82a9c412372d50d476f6e96ad5861181a3) Thanks [@aliemir](https://github.com/aliemir)! - feat: devtools selector with all selectables
Updated devtools selector to display all available elements instead of relying on the user's pointer to select the element.
### Patch Changes
- [#5881](https://github.com/refinedev/refine/pull/5881) [`ba719f6ea26`](https://github.com/refinedev/refine/commit/ba719f6ea264ee87226f42de900a754e81f1f22f) Thanks [@aliemir](https://github.com/aliemir)! - fix: declaration files in node10, node16 and nodenext module resolutions
- Updated dependencies [[`1c9a95f22ab`](https://github.com/refinedev/refine/commit/1c9a95f22ab8c3f1d1e48c7c889227ce1d9160cf), [`1c9a95f22ab`](https://github.com/refinedev/refine/commit/1c9a95f22ab8c3f1d1e48c7c889227ce1d9160cf), [`1c9a95f22ab`](https://github.com/refinedev/refine/commit/1c9a95f22ab8c3f1d1e48c7c889227ce1d9160cf), [`a9dbd808782`](https://github.com/refinedev/refine/commit/a9dbd808782212ed0bf6cf4401f85b675975a744), [`ba719f6ea26`](https://github.com/refinedev/refine/commit/ba719f6ea264ee87226f42de900a754e81f1f22f)]:
- @refinedev/devtools-server@1.1.28
- @refinedev/devtools-shared@1.1.6
- @refinedev/cli@2.16.30
## 1.1.37
### Patch Changes
- [#5823](https://github.com/refinedev/refine/pull/5823) [`aedc6a2961c`](https://github.com/refinedev/refine/commit/aedc6a2961cfe69309d4e14292147a858f94e3bf) Thanks [@aliemir](https://github.com/aliemir)! - fix: broken lodash imports in ESM builds
Fixed lodash imports in ESM builds which requires `lodash-es` imports to use `.js` extension to work properly unless the bundler is configured to handle non-fully-specified imports.
Resolves [#5822](https://github.com/refinedev/refine/issues/5822)
- Updated dependencies [[`aedc6a2961c`](https://github.com/refinedev/refine/commit/aedc6a2961cfe69309d4e14292147a858f94e3bf)]:
- @refinedev/devtools-server@1.1.27
- @refinedev/cli@2.16.29
## 1.1.36
### Patch Changes
- [#5765](https://github.com/refinedev/refine/pull/5765) [`0c197d82393`](https://github.com/refinedev/refine/commit/0c197d823939ae1fd4e0ee4b5a422322853b1e45) Thanks [@aliemir](https://github.com/aliemir)! - refactor: package bundles and package.json configuration for exports
Previously, Refine packages had exported ESM and CJS bundles with same `.js` extension and same types for both with `.d.ts` extensions. This was causing issues with bundlers and compilers to pick up the wrong files for the wrong environment. Now we're outputting ESM bundles with `.mjs` extension and CJS bundles with `.cjs` extension. Also types are now exported with both `.d.mts` and `.d.cts` extensions.
In older versions ESM and CJS outputs of some packages were using wrong imports/requires to dependencies causing errors in some environments. This will be fixed since now we're also enforcing the module type with extensions.
Above mentioned changes also supported with changes in `package.json` files of the packages to support the new extensions and types. All Refine packages now include `exports` fields in their configuration to make sure the correct bundle is picked up by the bundlers and compilers.
- [#5754](https://github.com/refinedev/refine/pull/5754) [`56ed144a0f5`](https://github.com/refinedev/refine/commit/56ed144a0f5af218fd9e6edbfd999ae433329927) Thanks [@alicanerdurmaz](https://github.com/alicanerdurmaz)! - chore: TypeScript upgraded to [v5.x.x](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-0.html). #5752
- Updated dependencies [[`b20a18e4dfc`](https://github.com/refinedev/refine/commit/b20a18e4dfc97481be865a2a012ea1c588bd76c6), [`0c197d82393`](https://github.com/refinedev/refine/commit/0c197d823939ae1fd4e0ee4b5a422322853b1e45), [`51f368eab1a`](https://github.com/refinedev/refine/commit/51f368eab1a72e2134981e999dc0b3e26e2b74e8), [`33a8a80d80f`](https://github.com/refinedev/refine/commit/33a8a80d80f160101907ad3a6e808b9d04b80107), [`56ed144a0f5`](https://github.com/refinedev/refine/commit/56ed144a0f5af218fd9e6edbfd999ae433329927), [`e9bbb1aa5af`](https://github.com/refinedev/refine/commit/e9bbb1aa5af94125cf0de562b3154302373a308f)]:
- @refinedev/cli@2.16.28
- @refinedev/devtools-server@1.1.26
- @refinedev/devtools-shared@1.1.5
## 1.1.35
### Patch Changes
- [#5695](https://github.com/refinedev/refine/pull/5695) [`79865affa1c`](https://github.com/refinedev/refine/commit/79865affa1c657e6b14ed34585caeec1f3d3da7f) Thanks [@BatuhanW](https://github.com/BatuhanW)! - chore: apply biome format and fix lint errors.
- Updated dependencies [[`79865affa1c`](https://github.com/refinedev/refine/commit/79865affa1c657e6b14ed34585caeec1f3d3da7f)]:
- @refinedev/cli@2.16.27
- @refinedev/devtools-server@1.1.25
- @refinedev/devtools-shared@1.1.4
## 1.1.34
### Patch Changes
- Updated dependencies [[`363fd4ed5f6`](https://github.com/refinedev/refine/commit/363fd4ed5f6dffbd70c2acf43ce310ab773589fb)]:
- @refinedev/cli@2.16.26
## 1.1.33
### Patch Changes
- Updated dependencies [[`e504c5b043c`](https://github.com/refinedev/refine/commit/e504c5b043c8cef7341356eeaa16df10e5e79a60)]:
- @refinedev/cli@2.16.25
## 1.1.32
### Patch Changes
- [#5573](https://github.com/refinedev/refine/pull/5573) [`546df06482`](https://github.com/refinedev/refine/commit/546df06482807e59a7f2a735361a8e9169bb2563) Thanks [@alicanerdurmaz](https://github.com/alicanerdurmaz)! - chore: add "use client" directive to exported files to work with nextjs app router
- Updated dependencies [[`546df06482`](https://github.com/refinedev/refine/commit/546df06482807e59a7f2a735361a8e9169bb2563)]:
- @refinedev/devtools-shared@1.1.3
- @refinedev/devtools-server@1.1.24
- @refinedev/cli@2.16.24
## 1.1.31
### Patch Changes
- Updated dependencies [[`ee0f7867c3`](https://github.com/refinedev/refine/commit/ee0f7867c3648dcbf1e2504f430a0b5814d91019)]:
- @refinedev/devtools-server@1.1.23
- @refinedev/cli@2.16.23
## 1.1.30
### Patch Changes
- Updated dependencies [[`fda3494215`](https://github.com/refinedev/refine/commit/fda349421509197b5a2be225bd3794adb2a7925c)]:
- @refinedev/cli@2.16.22
## 1.1.29
### Patch Changes
- [#5425](https://github.com/refinedev/refine/pull/5425) [`190af9fce2`](https://github.com/refinedev/refine/commit/190af9fce292bc46b169e3e121be6bf1c2a939a5) Thanks [@aliemir](https://github.com/aliemir)! - Updated `@refinedev/core` peer dependencies to latest (`^4.46.1`)
- Updated dependencies [[`190af9fce2`](https://github.com/refinedev/refine/commit/190af9fce292bc46b169e3e121be6bf1c2a939a5)]:
- @refinedev/cli@2.16.21
## 1.1.28
### Patch Changes
- Updated dependencies [[`1b031a2c19`](https://github.com/refinedev/refine/commit/1b031a2c19126ec1c01a85ecfbc794dc82480776), [`1b031a2c19`](https://github.com/refinedev/refine/commit/1b031a2c19126ec1c01a85ecfbc794dc82480776)]:
- @refinedev/devtools-server@1.1.22
- @refinedev/cli@2.16.20
## 1.1.27
### Patch Changes
- Updated dependencies [[`714841da4b24`](https://github.com/refinedev/refine/commit/714841da4b24ef0b392eb9cbb7320cb3be122292)]:
- @refinedev/cli@2.16.19
## 1.1.26
### Patch Changes
- Updated dependencies [[`404f16a947f3`](https://github.com/refinedev/refine/commit/404f16a947f330aad494f11545022684133bf2d0)]:
- @refinedev/cli@2.16.18
## 1.1.25
### Patch Changes
- Updated dependencies [[`97d5d9c98b28`](https://github.com/refinedev/refine/commit/97d5d9c98b28a1d69cada0a746202f82bec45622)]:
- @refinedev/cli@2.16.17
## 1.1.24
### Patch Changes
- Updated dependencies []:
- @refinedev/devtools-server@1.1.21
- @refinedev/cli@2.16.16
## 1.1.23
### Patch Changes
- Updated dependencies [[`72f9f608f42`](https://github.com/refinedev/refine/commit/72f9f608f4205cf4f3266068326d029546cd9f88), [`72f9f608f42`](https://github.com/refinedev/refine/commit/72f9f608f4205cf4f3266068326d029546cd9f88)]:
- @refinedev/devtools-server@1.1.20
- @refinedev/cli@2.16.15
## 1.1.22
### Patch Changes
- Updated dependencies []:
- @refinedev/devtools-server@1.1.19
- @refinedev/cli@2.16.14
## 1.1.21
### Patch Changes
- Updated dependencies []:
- @refinedev/devtools-server@1.1.18
- @refinedev/cli@2.16.13
## 1.1.20
### Patch Changes
- Updated dependencies [[`2bd813f62bf`](https://github.com/refinedev/refine/commit/2bd813f62bf55eb1be55ffe5b2c1c7079d7a93f0), [`b5f93f60f1d`](https://github.com/refinedev/refine/commit/b5f93f60f1d9d7ed105cf50512b090337a4dde2d), [`38f2a9b2e71`](https://github.com/refinedev/refine/commit/38f2a9b2e7149ad3d5e5c2780e05ddde0285ac3c)]:
- @refinedev/devtools-server@1.1.17
- @refinedev/cli@2.16.12
## 1.1.19
### Patch Changes
- Updated dependencies [[`2bd813f62bf`](https://github.com/refinedev/refine/commit/2bd813f62bf55eb1be55ffe5b2c1c7079d7a93f0), [`b5f93f60f1d`](https://github.com/refinedev/refine/commit/b5f93f60f1d9d7ed105cf50512b090337a4dde2d), [`38f2a9b2e71`](https://github.com/refinedev/refine/commit/38f2a9b2e7149ad3d5e5c2780e05ddde0285ac3c)]:
- @refinedev/devtools-server@1.1.16
- @refinedev/cli@2.16.11
## 1.1.18
### Patch Changes
- Updated dependencies [[`be419eb31bc`](https://github.com/refinedev/refine/commit/be419eb31bc7b7a3934f39bcfcbaaa0b9db60be8)]:
- @refinedev/devtools-server@1.1.15
- @refinedev/cli@2.16.10
## 1.1.17
### Patch Changes
- Updated dependencies [[`be419eb31bc`](https://github.com/refinedev/refine/commit/be419eb31bc7b7a3934f39bcfcbaaa0b9db60be8)]:
- @refinedev/devtools-server@1.1.14
- @refinedev/cli@2.16.9
## 1.1.16
### Patch Changes
- Updated dependencies [[`78117485899`](https://github.com/refinedev/refine/commit/781174858992bb1d077069d2858a37b44344879e)]:
- @refinedev/devtools-server@1.1.13
- @refinedev/cli@2.16.8
## 1.1.15
### Patch Changes
- Updated dependencies [[`78117485899`](https://github.com/refinedev/refine/commit/781174858992bb1d077069d2858a37b44344879e)]:
- @refinedev/devtools-server@1.1.12
- @refinedev/cli@2.16.7
## 1.1.14
### Patch Changes
- [#5127](https://github.com/refinedev/refine/pull/5127) [`4f89ca46ac4`](https://github.com/refinedev/refine/commit/4f89ca46ac4af5c1eddf842bddd3d981f0e47556) Thanks [@aliemir](https://github.com/aliemir)! - Update panel and pin positioning for rounded numbers to avoid subpixel blurry rendering
## 1.1.13
### Patch Changes
- [#5127](https://github.com/refinedev/refine/pull/5127) [`4f89ca46ac4`](https://github.com/refinedev/refine/commit/4f89ca46ac4af5c1eddf842bddd3d981f0e47556) Thanks [@aliemir](https://github.com/aliemir)! - Update panel and pin positioning for rounded numbers to avoid subpixel blurry rendering
## 1.1.12
### Patch Changes
- [#5082](https://github.com/refinedev/refine/pull/5082) [`61366ebd866`](https://github.com/refinedev/refine/commit/61366ebd86694328fe5a7f4dcf322db3c43bbc9d) Thanks [@aliemir](https://github.com/aliemir)! - Fixed the server/client mismatch error due to `<DevtoolsPanel />` component.
- Updated dependencies [[`61366ebd866`](https://github.com/refinedev/refine/commit/61366ebd86694328fe5a7f4dcf322db3c43bbc9d), [`6c40a720140`](https://github.com/refinedev/refine/commit/6c40a720140a8fe7141033a282500d354b1e621f)]:
- @refinedev/devtools-server@1.1.11
- @refinedev/cli@2.16.6
## 1.1.11
### Patch Changes
- [#5082](https://github.com/refinedev/refine/pull/5082) [`61366ebd866`](https://github.com/refinedev/refine/commit/61366ebd86694328fe5a7f4dcf322db3c43bbc9d) Thanks [@aliemir](https://github.com/aliemir)! - Fixed the server/client mismatch error due to `<DevtoolsPanel />` component.
- Updated dependencies [[`61366ebd866`](https://github.com/refinedev/refine/commit/61366ebd86694328fe5a7f4dcf322db3c43bbc9d), [`6c40a720140`](https://github.com/refinedev/refine/commit/6c40a720140a8fe7141033a282500d354b1e621f)]:
- @refinedev/devtools-server@1.1.10
- @refinedev/cli@2.16.5
## 1.1.10
### Patch Changes
- [#26](https://github.com/TheRakeshPurohit/refine/pull/26) [`7533e541739`](https://github.com/refinedev/refine/commit/7533e541739faadffb763feef8739ac46f62bd17) Thanks [@pull](https://github.com/apps/pull)! - Use the proper devtools url coming from the websocket handshake rather than using the hardcoded port.
- Updated dependencies [[`7533e541739`](https://github.com/refinedev/refine/commit/7533e541739faadffb763feef8739ac46f62bd17), [`7533e541739`](https://github.com/refinedev/refine/commit/7533e541739faadffb763feef8739ac46f62bd17)]:
- @refinedev/cli@2.16.4
- @refinedev/devtools-server@1.1.9
## 1.1.9
### Patch Changes
- [#5056](https://github.com/refinedev/refine/pull/5056) [`1fa531ebe89`](https://github.com/refinedev/refine/commit/1fa531ebe89bdab2af0dd57db121e2c0e72d44e8) Thanks [@aliemir](https://github.com/aliemir)! - Use the proper devtools url coming from the websocket handshake rather than using the hardcoded port.
- Updated dependencies [[`68f24d5b596`](https://github.com/refinedev/refine/commit/68f24d5b596eaf1b5b1690c7a57baf3e93fcf42b), [`1fa531ebe89`](https://github.com/refinedev/refine/commit/1fa531ebe89bdab2af0dd57db121e2c0e72d44e8)]:
- @refinedev/cli@2.16.3
- @refinedev/devtools-server@1.1.8
## 1.1.8
### Patch Changes
- [#5056](https://github.com/refinedev/refine/pull/5056) [`1fa531ebe89`](https://github.com/refinedev/refine/commit/1fa531ebe89bdab2af0dd57db121e2c0e72d44e8) Thanks [@aliemir](https://github.com/aliemir)! - Use the proper devtools url coming from the websocket handshake rather than using the hardcoded port.
- Updated dependencies [[`68f24d5b596`](https://github.com/refinedev/refine/commit/68f24d5b596eaf1b5b1690c7a57baf3e93fcf42b), [`1fa531ebe89`](https://github.com/refinedev/refine/commit/1fa531ebe89bdab2af0dd57db121e2c0e72d44e8)]:
- @refinedev/cli@2.16.2
- @refinedev/devtools-server@1.1.7
![refine devtools](https://github.com/refinedev/refine/assets/1110414/15ed6907-d0c8-4213-9024-2f6b0a09968f)
## 1.0.0
### Major Changes
- [#4960](https://github.com/refinedev/refine/pull/4960) [`d8e464fa2c4`](https://github.com/refinedev/refine/commit/d8e464fa2c461d0fd60050cf18247758ecdc42e3) Thanks [@aliemir](https://github.com/aliemir)! - Initial beta release of refine devtools.🎉
We're releasing refine devtools in beta. refine devtools is designed to help you debug and develop your refine apps. It will be a collection of features including monitoring queries and mutations, testing out inferencer generated codes, adding and updating refine packages from the UI and more. 🤯
## Usage
Install latest version of `@refinedev/cli`:
```bash
npm install @refinedev/cli@latest
```
> 🚨 If you don't have `@refinedev/cli` installed already, you can follow the [installation guide](https://refine.dev/docs/packages/documentation/cli/#how-to-add-to-an-existing-project) to add it to your project.
Install `@refinedev/devtools` with `@refinedev/cli`
```bash
npm run refine devtools init
```
![devtools-install](https://github.com/refinedev/refine/assets/23058882/7d7341cc-1edd-4cf3-b330-1796c6a8afc5)
Ta-da! 🎉 Everything is ready now, you can use the refine devtools in your project! 🕶
> Devtools only works in development mode and have no overhead on production builds. You don't need to do anything special to exclude DevTools from your bundle.

21
packages/devtools/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Refine Dev Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,39 @@
<div align="center" style="margin: 30px;">
<a href="https://refine.dev">
<img alt="refine logo" src="https://refine.ams3.cdn.digitaloceanspaces.com/readme/refine-readme-banner.png">
</a>
</div>
<br/>
<div align="center">refine is an open-source, headless React framework for developers building enterprise web applications.
It eliminates repetitive tasks in CRUD operations and provides industry-standard solutions for critical project components like **authentication**, **access control**, **routing**, **networking**, **state management**, and **i18n**.
</div>
---
![refine devtools](https://github.com/refinedev/refine/assets/1110414/15ed6907-d0c8-4213-9024-2f6b0a09968f)
We're releasing refine devtools in beta. refine devtools is designed to help you debug and develop your refine apps. It will be a collection of features including monitoring queries and mutations, testing out inferencer generated codes, adding and updating refine packages from the UI and more. 🤯
## Usage
Install latest version of `@refinedev/cli`:
```bash
npm install @refinedev/cli@latest
```
Install `@refinedev/devtools` with `@refinedev/cli`
```bash
npm run refine devtools init
```
> 🚨 If you don't have `@refinedev/cli` installed already, you can follow the [installation guide](https://refine.dev/docs/packages/documentation/cli/#how-to-add-to-an-existing-project) to add it to your project.
![devtools-install](https://github.com/refinedev/refine/assets/23058882/7d7341cc-1edd-4cf3-b330-1796c6a8afc5)
Ta-da! 🎉 Everything is ready now, you can use the refine devtools in your project! 🕶
> Devtools only works in development mode and have no overhead on production builds. You don't need to do anything special to exclude DevTools from your bundle.

View File

@@ -0,0 +1,15 @@
module.exports = {
preset: "ts-jest",
rootDir: "./",
displayName: "react-hook-form",
testPathIgnorePatterns: ["<rootDir>/node_modules/", "<rootDir>/dist/"],
testEnvironment: "jsdom",
transform: {
"^.+\\.tsx?$": [
"ts-jest",
{
tsconfig: "<rootDir>/tsconfig.test.json",
},
],
},
};

View File

@@ -0,0 +1,80 @@
{
"name": "@refinedev/devtools",
"version": "1.2.9",
"private": false,
"description": "refine devtools offers a set of features from monitoring to quickly prototyping a UI.",
"repository": {
"type": "git",
"url": "https://github.com/refinedev/refine.git",
"directory": "packages/devtools"
},
"license": "MIT",
"author": "refine",
"sideEffects": false,
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"typings": "dist/index.d.ts",
"files": [
"dist",
"src"
],
"scripts": {
"attw": "attw --pack .",
"build": "tsup && node ../shared/generate-declarations.js",
"dev": "tsup --watch",
"prepare": "pnpm build",
"publint": "publint --strict=true --level=suggestion",
"test": "jest --passWithNoTests --runInBand",
"types": "node ../shared/generate-declarations.js"
},
"dependencies": {
"@aliemir/dom-to-fiber-utils": "^0.4.0",
"@refinedev/devtools-shared": "1.1.12",
"error-stack-parser": "^2.1.4",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21"
},
"devDependencies": {
"@esbuild-plugins/node-resolve": "^0.1.4",
"@testing-library/jest-dom": "^5.16.4",
"@types/jest": "^29.2.4",
"@types/lodash": "^4.14.171",
"@types/node": "^18.16.2",
"@types/react-reconciler": "^0.28.8",
"@types/testing-library__jest-dom": "^5.14.3",
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.3.1",
"react-router-dom": "^6.8.1",
"ts-jest": "^29.1.2",
"tslib": "^2.6.2",
"tsup": "^6.7.0",
"typescript": "^5.4.2"
},
"peerDependencies": {
"@refinedev/cli": "2.16.39",
"@refinedev/core": "^4.46.1",
"@refinedev/devtools-server": "1.1.37",
"@types/react": "^17.0.0 || ^18.0.0",
"@types/react-dom": "^17.0.0 || ^18.0.0",
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0"
},
"engines": {
"node": ">=10"
},
"publishConfig": {
"access": "public"
}
}

View File

@@ -0,0 +1,19 @@
import React from "react";
type Props = {
children: string;
};
export const ApplyStyles = ({ children }: Props) => {
React.useEffect(() => {
const element = document.createElement("style");
element.innerHTML = children;
document.head.appendChild(element);
return () => {
document.head.removeChild(element);
};
}, [children]);
return null;
};

View File

@@ -0,0 +1,73 @@
import React from "react";
import { DevtoolsSelector } from "./devtools-selector";
import { DevtoolsIcon } from "./icons/devtools-icon";
import { SelectorButtonIcon } from "./icons/selector-button";
import { ApplyStyles } from "./apply-styles";
type Props = {
onClick?: () => void;
groupHover?: boolean;
onSelectorHighlight: (name: string) => void;
selectorActive: boolean;
setSelectorActive: React.Dispatch<React.SetStateAction<boolean>>;
};
export const DevtoolsPin = ({
onClick,
onSelectorHighlight,
selectorActive,
setSelectorActive,
}: Props) => {
return (
<div role="button" className="devtools-selector-pin-box" onClick={onClick}>
<DevtoolsIcon />
<DevtoolsSelector
style={{
position: "absolute",
top: 5,
right: 18,
width: "16px",
height: "16px",
}}
icon={
<SelectorButtonIcon
width={16}
height={16}
style={{ pointerEvents: "none" }}
/>
}
onHighlight={onSelectorHighlight}
active={selectorActive}
setActive={setSelectorActive}
/>
<ApplyStyles>
{
/* css */ `
.devtools-selector-pin-box {
z-index: 9999;
position: relative;
user-select: none;
-webkit-user-select: none;
background: none;
border: none;
padding: 0;
margin: 0;
appearance: none;
padding-right: 1px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: #6C7793;
transition: color 0.1s ease-in-out;
}
.devtools-selector-pin-box:hover {
color: #0FBDBD;
}
`
}
</ApplyStyles>
</div>
);
};

View File

@@ -0,0 +1,67 @@
import React from "react";
import { useSelector } from "src/utilities/use-selector";
import { ApplyStyles } from "./apply-styles";
import { SelectableElements } from "./selectable-elements";
type Props = {
active: boolean;
setActive: React.Dispatch<React.SetStateAction<boolean>>;
onHighlight: (name: string) => void;
icon?: React.ReactNode;
style?: React.CSSProperties;
};
export const DevtoolsSelector = ({
active,
setActive,
onHighlight,
icon,
style,
}: Props) => {
const { selectableElements } = useSelector(active);
const onSelect = (name: string) => {
onHighlight(name);
setActive(false);
};
return (
<div style={style}>
<div
role="button"
title="Element Selector"
className="refine-devtools-selector-button"
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
(document?.activeElement as HTMLElement)?.blur();
setActive((active) => !active);
}}
>
{icon}
</div>
{active && (
<SelectableElements elements={selectableElements} onSelect={onSelect} />
)}
<ApplyStyles>
{
/* css */ `
.refine-devtools-selector-button {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
transform: rotate(0deg);
transition: transform 0.2s ease-in-out;
line-height: 1;
}
.refine-devtools-selector-button:hover {
transform: rotate(180deg);
}
`
}
</ApplyStyles>
</div>
);
};

View File

@@ -0,0 +1,33 @@
import * as React from "react";
import type { SVGProps } from "react";
export const ArrowUnionIcon = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={16}
height={16}
viewBox="0 0 16 16"
fill="none"
{...props}
>
<path
fill="#303450"
stroke="url(#arrow-union-icon)"
d="M.5 8.495V15.5h15V8.495a4.5 4.5 0 0 1-3.816-2.483L9.341 1.33c-.553-1.105-2.13-1.105-2.683 0L4.317 6.012A4.5 4.5 0 0 1 .5 8.495Z"
/>
<defs>
<linearGradient
id="arrow-union-icon"
x1={8}
x2={8}
y1={0}
y2={10}
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#474E6B" />
<stop offset={0.9} stopColor="#474E6B" />
<stop offset={0.901} stopColor="#474E6B" stopOpacity={0} />
</linearGradient>
</defs>
</svg>
);

View File

@@ -0,0 +1,35 @@
import * as React from "react";
import type { SVGProps } from "react";
export const DevtoolsIcon = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={157}
height={25}
viewBox="0 0 157 25"
fill="none"
{...props}
>
<g>
<path fill="#1D1E30" d="M17 1h123v24H17z" />
<path
fill="#1D1E30"
d="M6.265 9.205A12 12 0 0 1 17.649 1H25v24H1L6.265 9.205ZM150.735 9.205A12 12 0 0 0 139.351 1H132v24h24l-5.265-15.795Z"
/>
<path
fill="currentColor"
d="M25 14.333A1.333 1.333 0 1 1 25 17a1.333 1.333 0 0 1 0-2.667Z"
/>
<path
fill="currentColor"
fillRule="evenodd"
d="M23.211 20.578a4 4 0 0 0 3.578 0l4-2A4 4 0 0 0 33 15v-4a4 4 0 0 0-2.211-3.578l-4-2a4 4 0 0 0-3.578 0l-4 2A4 4 0 0 0 17 11v4a4 4 0 0 0 2.211 3.578l4 2Zm-.878-4.911a2.667 2.667 0 0 0 5.334 0v-5.334a2.667 2.667 0 0 0-5.334 0v5.334Z"
clipRule="evenodd"
/>
<path
fill="#CFD7E2"
d="M42.152 17a.287.287 0 0 1-.192-.072.287.287 0 0 1-.072-.192V9.032c0-.072.024-.132.072-.18a.264.264 0 0 1 .192-.084h4.2c.288 0 .56.056.816.168a2.135 2.135 0 0 1 1.14 1.128c.112.256.168.532.168.828v3.984c0 .296-.056.572-.168.828a2.135 2.135 0 0 1-1.14 1.128 2.014 2.014 0 0 1-.816.168h-4.2Zm1.38-1.644h2.82a.455.455 0 0 0 .336-.132.497.497 0 0 0 .132-.348v-3.984a.455.455 0 0 0-.132-.336.436.436 0 0 0-.336-.144h-2.82v4.944Zm13.18-5.196a.244.244 0 0 1-.253.252h-4.44v1.656h4.02c.072 0 .132.024.18.072a.227.227 0 0 1 .084.18v1.128a.264.264 0 0 1-.084.192.244.244 0 0 1-.18.072h-4.02v1.644h4.44c.072 0 .132.028.18.084a.244.244 0 0 1 .072.18v1.116a.287.287 0 0 1-.072.192.244.244 0 0 1-.18.072h-5.832a.244.244 0 0 1-.18-.072.287.287 0 0 1-.072-.192V9.032c0-.072.024-.132.072-.18a.227.227 0 0 1 .18-.084h5.832c.072 0 .132.028.18.084a.244.244 0 0 1 .072.18v1.128ZM63.014 17h-2.232a.387.387 0 0 1-.216-.072.356.356 0 0 1-.144-.168l-1.716-4.296a.853.853 0 0 1-.072-.24 1.783 1.783 0 0 1-.024-.264V9.032c0-.072.024-.132.072-.18a.227.227 0 0 1 .18-.084h1.128c.072 0 .132.028.18.084a.227.227 0 0 1 .084.18v2.616c0 .072.008.156.024.252s.04.176.072.24l1.284 3.216h.528l1.284-3.216a.853.853 0 0 0 .072-.24c.016-.096.024-.18.024-.252V9.032c0-.072.024-.132.072-.18a.264.264 0 0 1 .192-.084h1.128c.072 0 .132.028.18.084a.244.244 0 0 1 .072.18v2.928c0 .072-.008.16-.024.264a.853.853 0 0 1-.072.24l-1.716 4.296a.356.356 0 0 1-.144.168.387.387 0 0 1-.216.072ZM73.29 8.768c.072 0 .132.028.18.084a.227.227 0 0 1 .084.18v1.128a.227.227 0 0 1-.084.18.244.244 0 0 1-.18.072h-2.208v6.324a.264.264 0 0 1-.084.192.244.244 0 0 1-.18.072H69.69a.244.244 0 0 1-.18-.072.287.287 0 0 1-.072-.192v-6.324H67.23a.287.287 0 0 1-.192-.072.244.244 0 0 1-.072-.18V9.032c0-.072.024-.132.072-.18a.264.264 0 0 1 .192-.084h6.06Zm6.507.012c.296 0 .572.056.828.168a2.171 2.171 0 0 1 1.128 1.128c.112.256.168.528.168.816v3.996c0 .288-.056.56-.168.816a2.171 2.171 0 0 1-1.128 1.128 2.043 2.043 0 0 1-.828.168h-2.34c-.296 0-.572-.056-.828-.168a2.171 2.171 0 0 1-1.128-1.128 2.014 2.014 0 0 1-.168-.816v-3.996c0-.288.056-.56.168-.816a2.171 2.171 0 0 1 1.128-1.128c.256-.112.532-.168.828-.168h2.34Zm.48 2.112a.436.436 0 0 0-.144-.336.455.455 0 0 0-.336-.132h-2.34a.497.497 0 0 0-.348.132.455.455 0 0 0-.132.336v3.996c0 .136.044.248.132.336a.497.497 0 0 0 .348.132h2.34a.455.455 0 0 0 .336-.132.436.436 0 0 0 .144-.336v-3.996Zm7.888-2.112c.295 0 .572.056.828.168a2.171 2.171 0 0 1 1.128 1.128c.112.256.168.528.168.816v3.996c0 .288-.056.56-.168.816a2.171 2.171 0 0 1-1.128 1.128 2.043 2.043 0 0 1-.828.168h-2.34c-.297 0-.573-.056-.829-.168a2.171 2.171 0 0 1-1.127-1.128 2.014 2.014 0 0 1-.168-.816v-3.996c0-.288.056-.56.168-.816a2.171 2.171 0 0 1 1.127-1.128c.257-.112.532-.168.829-.168h2.34Zm.48 2.112a.436.436 0 0 0-.144-.336.455.455 0 0 0-.337-.132h-2.34a.497.497 0 0 0-.347.132.455.455 0 0 0-.133.336v3.996c0 .136.044.248.133.336a.497.497 0 0 0 .347.132h2.34a.455.455 0 0 0 .337-.132.436.436 0 0 0 .143-.336v-3.996ZM98.294 17H92.68a.287.287 0 0 1-.192-.072.287.287 0 0 1-.072-.192V9.032c0-.072.024-.132.072-.18a.264.264 0 0 1 .192-.084h1.116c.072 0 .132.028.18.084a.227.227 0 0 1 .084.18v6.324h4.236c.072 0 .132.028.18.084a.244.244 0 0 1 .072.18v1.116a.287.287 0 0 1-.072.192.244.244 0 0 1-.18.072Zm7.336-5.76a.287.287 0 0 1-.192-.072.287.287 0 0 1-.072-.192v-.084a.455.455 0 0 0-.132-.336.436.436 0 0 0-.336-.144h-2.352a.46.46 0 0 0-.336.144.455.455 0 0 0-.132.336v.696c0 .136.044.252.132.348a.482.482 0 0 0 .336.132h2.352c.288 0 .56.056.816.168a2.171 2.171 0 0 1 1.128 1.128c.112.256.168.528.168.816v.696c0 .296-.056.572-.168.828a2.171 2.171 0 0 1-1.128 1.128 2.014 2.014 0 0 1-.816.168h-2.352c-.288 0-.56-.056-.816-.168a2.171 2.171 0 0 1-1.128-1.128 2.043 2.043 0 0 1-.168-.828v-.084c0-.072.024-.132.072-.18a.264.264 0 0 1 .192-.084h1.116c.072 0 .132.028.18.084a.227.227 0 0 1 .084.18v.084c0 .136.044.252.132.348a.482.482 0 0 0 .336.132h2.352a.455.455 0 0 0 .336-.132.497.497 0 0 0 .132-.348v-.696a.455.455 0 0 0-.132-.336.455.455 0 0 0-.336-.132h-2.352c-.288 0-.56-.056-.816-.168a2.171 2.171 0 0 1-1.128-1.128 2.099 2.099 0 0 1-.168-.828v-.696c0-.296.056-.572.168-.828a2.171 2.171 0 0 1 1.128-1.128c.256-.112.528-.168.816-.168h2.352c.288 0 .56.056.816.168a2.171 2.171 0 0 1 1.128 1.128c.112.256.168.532.168.828v.084a.287.287 0 0 1-.072.192.244.244 0 0 1-.18.072h-1.128Z"
/>
</g>
</svg>
);

View File

@@ -0,0 +1,27 @@
import React from "react";
export const ResizeHandleIcon = (props: React.SVGProps<SVGSVGElement>) => (
<svg
width={10}
height={26}
viewBox="0 0 10 26"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<rect x={0.5} y={0.5} width={9} height={25} rx={4.5} fill="#1D1E30" />
<path
d="M7 5C7 6.10457 6.10457 7 5 7C3.89543 7 3 6.10457 3 5C3 3.89543 3.89543 3 5 3C6.10457 3 7 3.89543 7 5Z"
fill="#303450"
/>
<path
d="M7 13C7 14.1046 6.10457 15 5 15C3.89543 15 3 14.1046 3 13C3 11.8954 3.89543 11 5 11C6.10457 11 7 11.8954 7 13Z"
fill="#303450"
/>
<path
d="M7 21C7 22.1046 6.10457 23 5 23C3.89543 23 3 22.1046 3 21C3 19.8954 3.89543 19 5 19C6.10457 19 7 19.8954 7 21Z"
fill="#303450"
/>
<rect x={0.5} y={0.5} width={9} height={25} rx={4.5} stroke="#303450" />
</svg>
);

View File

@@ -0,0 +1,37 @@
import React from "react";
export const SelectorButtonIcon = (props: React.SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={16}
height={16}
viewBox="0 0 16 16"
fill="none"
{...props}
>
<path
fill="#0FBDBD"
fillRule="evenodd"
d="M9 1a1 1 0 0 0-2 0v2.1A5.006 5.006 0 0 0 3.1 7H1a1 1 0 0 0 0 2h2.1A5.006 5.006 0 0 0 7 12.9V15a1 1 0 1 0 2 0v-2.1A5.006 5.006 0 0 0 12.9 9H15a1 1 0 1 0 0-2h-2.1A5.006 5.006 0 0 0 9 3.1V1Zm2 7a3 3 0 1 0-6 0 3 3 0 0 0 6 0Z"
clipRule="evenodd"
/>
</svg>
);
export const SelectorIcon = (props: React.SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={16}
height={16}
viewBox="0 0 16 16"
fill="none"
{...props}
>
<path
fill="#14141F"
fillRule="evenodd"
d="M9 1a1 1 0 0 0-2 0v2.1A5.006 5.006 0 0 0 3.1 7H1a1 1 0 0 0 0 2h2.1A5.006 5.006 0 0 0 7 12.9V15a1 1 0 1 0 2 0v-2.1A5.006 5.006 0 0 0 12.9 9H15a1 1 0 1 0 0-2h-2.1A5.006 5.006 0 0 0 9 3.1V1Zm2 7a3 3 0 1 0-6 0 3 3 0 0 0 6 0Z"
clipRule="evenodd"
/>
</svg>
);

View File

@@ -0,0 +1,283 @@
import React from "react";
import type { Placement } from "src/interfaces/placement";
import {
getDefaultPanelSize,
getMaxPanelHeight,
getMaxPanelWidth,
getPanelPosition,
getPanelToggleTransforms,
MIN_PANEL_HEIGHT,
MIN_PANEL_WIDTH,
roundToEven,
} from "src/utilities";
import { ResizeHandleIcon } from "./icons/resize-handle-icon";
type Props = {
placement: Placement;
defaultWidth?: number;
minWidth?: number;
maxWidth?: number;
defaultHeight?: number;
minHeight?: number;
maxHeight?: number;
children: ({ resizing }: { resizing: string | null }) => React.ReactNode;
onResize?: (width: number, height: number) => void;
visible?: boolean;
};
export const ResizablePane = ({ placement, visible, children }: Props) => {
const [hover, setHover] = React.useState(false);
const [resizing, setResizing] = React.useState<
"lx" | "rx" | "ty" | "by" | null
>(null);
const [resizePosition, setResizePosition] = React.useState<{
x: number;
y: number;
} | null>(null);
const [panelSize, setPanelSize] = React.useState<
Record<"width" | "height", number>
>(() => {
const defaultSize = getDefaultPanelSize(placement);
return {
width: roundToEven(defaultSize.width),
height: roundToEven(defaultSize.height),
};
});
React.useEffect(() => {
const handleResize = () => {
setPanelSize((p) => {
const defaultSize = getDefaultPanelSize(placement, p);
return {
width: roundToEven(defaultSize.width),
height: roundToEven(defaultSize.height),
};
});
};
handleResize();
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, [placement]);
React.useEffect(() => {
const handleMouseUp = () => {
setResizing(null);
};
if (resizing !== null) {
window.addEventListener("mouseup", handleMouseUp);
return () => {
window.removeEventListener("mouseup", handleMouseUp);
};
}
return;
}, [resizing]);
React.useEffect(() => {
const currentCursor = document.body.style.cursor;
if (resizing?.includes("x")) {
document.body.style.cursor = "col-resize";
} else if (resizing?.includes("y")) {
document.body.style.cursor = "row-resize";
}
return () => {
document.body.style.cursor = currentCursor;
};
}, [resizing]);
React.useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
if (resizing?.[1] === "x") {
const diff = e.clientX - (resizePosition?.x ?? e.clientX);
const newWidth =
panelSize.width + (resizing === "lx" ? -diff : diff) * 2;
setPanelSize((p) => ({
...p,
width: roundToEven(
Math.min(
getMaxPanelWidth(placement),
Math.max(MIN_PANEL_WIDTH, newWidth),
),
),
}));
} else if (resizing?.[1] === "y") {
const diff = e.clientY - (resizePosition?.y ?? e.clientY);
const newHeight =
panelSize.height + (resizing === "ty" ? -diff : diff) * 1;
setPanelSize((p) => ({
...p,
height: roundToEven(
Math.min(
getMaxPanelHeight(placement),
Math.max(MIN_PANEL_HEIGHT, newHeight),
),
),
}));
}
};
if (resizing !== null) {
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
};
}
return;
}, [resizing, placement]);
return (
<div
style={{
position: "absolute",
borderRadius: "8px",
boxShadow: "0 0 10px rgba(0, 0, 0, 0.5)",
border: "1px solid rgba(0, 0, 0, 0.5)",
transitionProperty: "transform, opacity",
transitionTimingFunction: "ease-in-out",
transitionDuration: "0.2s",
...getPanelPosition(placement),
opacity: visible ? 1 : 0,
transform: `${
getPanelPosition(placement).transform
} ${getPanelToggleTransforms(visible ?? false)}`,
...panelSize,
}}
onMouseEnter={() => {
setHover(true);
}}
onMouseLeave={() => {
setHover(false);
}}
>
{children({ resizing })}
{/* */}
<React.Fragment>
<div
style={{
position: "absolute",
left: 0,
top: "50%",
width: "10px",
height: "26px",
transform: "translateY(-13px) translateX(-5px)",
cursor: "col-resize",
transition: "opacity ease-in-out 0.2s",
pointerEvents: hover || resizing ? "auto" : "none",
opacity: hover || resizing ? 1 : 0,
}}
onMouseDown={(event) => {
setResizing("lx");
setResizePosition({
x: event.clientX,
y: event.clientY,
});
event.preventDefault();
}}
>
<ResizeHandleIcon />
</div>
<div
style={{
position: "absolute",
right: 0,
top: "50%",
width: "10px",
height: "26px",
transform: "translateY(-13px) translateX(5px)",
cursor: "col-resize",
transition: "opacity ease-in-out 0.2s",
pointerEvents: hover || resizing ? "auto" : "none",
opacity: hover || resizing ? 1 : 0,
}}
onMouseDown={(event) => {
setResizing("rx");
setResizePosition({
x: event.clientX,
y: event.clientY,
});
event.preventDefault();
}}
>
<ResizeHandleIcon />
</div>
<div
style={{
position: "absolute",
left: "50%",
top: 0,
width: "26px",
height: "10px",
transform: "translateY(-5px) translateX(-13px)",
cursor: "row-resize",
transition: "opacity ease-in-out 0.2s",
pointerEvents: hover || resizing ? "auto" : "none",
opacity: hover || resizing ? 1 : 0,
}}
onMouseDown={(event) => {
setResizing("ty");
setResizePosition({
x: event.clientX,
y: event.clientY,
});
event.preventDefault();
}}
>
<ResizeHandleIcon
style={{
transform: "rotate(90deg)",
transformOrigin: "13px 13px",
}}
/>
</div>
<div
style={{
position: "absolute",
left: "50%",
bottom: 0,
width: "26px",
height: "10px",
transform: "translateY(5px) translateX(-13px)",
cursor: "row-resize",
transition: "opacity ease-in-out 0.2s",
pointerEvents: hover || resizing ? "auto" : "none",
opacity: hover || resizing ? 1 : 0,
}}
onMouseDown={(event) => {
setResizing("by");
setResizePosition({
x: event.clientX,
y: event.clientY,
});
event.preventDefault();
}}
>
<ResizeHandleIcon
style={{
transform: "rotate(90deg)",
transformOrigin: "13px 13px",
}}
/>
</div>
</React.Fragment>
</div>
);
};

View File

@@ -0,0 +1,262 @@
import React from "react";
import debounce from "lodash/debounce";
import { createPortal } from "react-dom";
import { ApplyStyles } from "./apply-styles";
import { SelectorIcon } from "./icons/selector-button";
const MIN_SIZE = 22;
const getPosition = (element: HTMLElement, document: Document) => {
const { top, left, width, height } = element.getBoundingClientRect();
const { scrollLeft, scrollTop } = document.documentElement;
const positionLeft = left + scrollLeft - Math.max(0, MIN_SIZE - width) / 2;
const positionTop = top + scrollTop - Math.max(0, MIN_SIZE - height) / 2;
return {
left: positionLeft,
top: positionTop,
width: Math.max(MIN_SIZE, width),
height: Math.max(MIN_SIZE, height),
};
};
const SelectableElement = ({
element,
name,
onSelect,
}: {
element: HTMLElement;
name: string;
onSelect: (name: string) => void;
}) => {
const [position] = React.useState(() => getPosition(element, document));
const elementRef = React.useRef<HTMLButtonElement | null>(null);
React.useEffect(() => {
// use scroll event listener
const onScroll = debounce(
() => {
const nextPos = getPosition(element, document);
(["left", "top", "width", "height"] as const).forEach((prop) => {
elementRef.current?.style.setProperty(prop, `${nextPos[prop]}px`);
});
elementRef.current?.style.setProperty("opacity", "1");
},
200,
{
leading: false,
trailing: true,
},
);
const opacityOnScroll = debounce(
() => {
elementRef.current?.style.setProperty("opacity", "0");
},
200,
{
leading: true,
trailing: false,
},
);
document.addEventListener("scroll", onScroll);
document.addEventListener("scroll", opacityOnScroll);
return () => {
document.removeEventListener("scroll", onScroll);
document.removeEventListener("scroll", opacityOnScroll);
};
}, [element]);
const placement = React.useMemo(() => {
const tooltipBaseSize = { width: 22, height: 22 };
const nameWidth = name.length * 7.5;
const tooltipSize = {
width: tooltipBaseSize.width + nameWidth,
height: tooltipBaseSize.height,
};
const gap = 4;
// outside top start
if (
position.top - tooltipSize.height > 0 &&
position.left + tooltipSize.width < window.innerWidth &&
position.width > tooltipSize.width
) {
return { left: gap / 2, top: tooltipSize.height * -1 - gap };
}
// inside top start
if (
position.height >= tooltipSize.height * 1.5 &&
position.width >= tooltipSize.width * 1.5
) {
return { left: 0 + gap, top: 0 + gap };
}
// outside left start
if (position.left - tooltipSize.width > 0) {
return { right: position.width + gap, top: 0 - 1 };
}
// outside right start
if (
position.left + position.width + tooltipSize.width <
window.innerWidth
) {
return { left: position.width + gap, top: 0 - 1 };
}
// outside bottom start
if (
position.top + position.height + tooltipSize.height <
document.documentElement.scrollHeight
) {
return { left: 0 - 1, top: position.height + gap };
}
return { left: 0, top: 0 };
}, [position, name.length]);
return (
<button
type="button"
className="selector-xray-box"
onClick={(event) => {
event?.preventDefault();
event?.stopPropagation();
onSelect(name);
}}
ref={elementRef}
style={{
position: "absolute",
...position,
}}
>
<div
style={{
position: "absolute",
...placement,
}}
className="selector-xray-info"
>
<span className="selector-xray-info-icon">
<SelectorIcon
width={12}
height={12}
style={{ pointerEvents: "none" }}
/>
</span>
<span className="selector-xray-info-title">{` ${name}`}</span>
</div>
</button>
);
};
export const SelectableElements = ({
elements,
onSelect,
}: {
elements: Array<{ element: HTMLElement; name: string }>;
onSelect: (name: string) => void;
}) => {
const [selectorBoxRoot, setSelectorBoxRoot] =
React.useState<HTMLElement | null>(null);
React.useEffect(() => {
if (!selectorBoxRoot) {
const element = document.createElement("div");
element.id = "selector-box-root";
document.body.appendChild(element);
setSelectorBoxRoot(element);
return () => {
document.body.removeChild(element);
setSelectorBoxRoot(null);
};
}
return () => 0;
}, []);
if (!selectorBoxRoot) return null;
return (
<>
{createPortal(
elements.map((element, idx) => (
<SelectableElement
key={`selector-element-${idx}-${element.name}`}
{...element}
onSelect={onSelect}
/>
)),
selectorBoxRoot,
)}
<ApplyStyles>
{
/* css */ `
.selector-xray-box {
display: flex;
margin: 0;
padding: 0;
appearance: none;
z-index: 9999;
border: 2px dashed #47EBEB;
border-radius: 6px;
background: rgba(71, 235, 235, 0.01);
transition: opacity 0.2s ease-in-out;
cursor: crosshair;
}
.selector-xray-info {
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
padding: 3px 0;
min-width: 22px;
height: 22px;
color: #14141F;
background: #47EBEB;
font-size: 12px;
line-height: 16px;
font-family: monospace;
border-radius: 11px;
}
.selector-xray-info-icon {
display: flex;
min-width: 22px;
justify-content: center;
align-items: center;
flex-shrink: 0;
}
.selector-xray-info-title {
display: block;
max-width: 0;
overflow: hidden;
transition-property: max-width, padding-right;
transition-duration: 0.2s;
transition-timing-function: ease-in-out;
transition-delay: 0.1s;
}
.selector-xray-box:hover .selector-xray-info-title {
max-width: 200px;
padding-right: 8px;
}
.selector-xray-box:hover .selector-xray-info-title {
z-index: 90;
}
`
}
</ApplyStyles>
</>
);
};

9
packages/devtools/src/define.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
declare const __DEV_CONDITION__: string;
declare const __IMPORT_META_KEY__: any;
declare const __PROCESS_KEY__: any;
declare const __PROCESS_ENV_REFINE_DEVTOOLS_PORT_KEY__: any;
declare const __PROCESS_ENV_NEXT_PUBLIC_REFINE_DEVTOOLS_PORT_KEY__: any;
declare const __PROCESS_ENV_REACT_APP_REFINE_DEVTOOLS_PORT_KEY__: any;
declare const __IMPORT_META_ENV_REFINE_DEVTOOLS_PORT_KEY__: any;
declare const __IMPORT_META_ENV_VITE_REFINE_DEVTOOLS_PORT_KEY__: any;

View File

@@ -0,0 +1,2 @@
export { DevtoolsPanel } from "./panel.js";
export { DevtoolsProvider } from "./provider.js";

View File

@@ -0,0 +1 @@
export type Placement = "bottom" | "left" | "right" | "top";

View File

@@ -0,0 +1,172 @@
import React from "react";
import { DevtoolsPin } from "./components/devtools-pin";
import { ResizablePane } from "./components/resizable-pane";
import {
DevToolsContext,
DevtoolsEvent,
send,
} from "@refinedev/devtools-shared";
import type { Placement } from "./interfaces/placement";
const MAX_IFRAME_WAIT_TIME = 1500;
export const DevtoolsPanel =
__DEV_CONDITION__ !== "development"
? () => null
: () => {
const [browser, setBrowser] = React.useState<boolean>(false);
const [visible, setVisible] = React.useState(false);
const [placement] = React.useState<Placement>("bottom");
const { httpUrl, ws } = React.useContext(DevToolsContext);
const [width, setWidth] = React.useState<number>(0);
const [selectorActive, setSelectorActive] = React.useState(false);
const [iframeStatus, setIframeStatus] = React.useState<
"loading" | "loaded" | "failed"
>("loading");
const onSelectorHighlight = React.useCallback(
(name: string) => {
if (ws) {
send(ws, DevtoolsEvent.DEVTOOLS_HIGHLIGHT_IN_MONITOR, {
name,
});
}
setVisible(true);
},
[ws],
);
React.useEffect(() => {
if (selectorActive) {
setVisible(false);
}
}, [selectorActive]);
React.useEffect(() => {
if (typeof window !== "undefined") {
setBrowser(true);
}
}, []);
React.useEffect(() => {
if (browser) {
// set width by window size dynamically
setWidth(window.innerWidth);
const onResize = () => {
setWidth(window.innerWidth);
};
window.addEventListener("resize", onResize);
return () => {
window.removeEventListener("resize", onResize);
};
}
return () => undefined;
}, [browser]);
React.useEffect(() => {
if (iframeStatus !== "loaded") {
const onMessage = (event: MessageEvent) => {
if (event.data.type === "refine-devtools-iframe-loaded") {
setIframeStatus("loaded");
}
};
window.addEventListener("message", onMessage);
return () => {
window.removeEventListener("message", onMessage);
};
}
return () => 0;
}, []);
React.useEffect(() => {
let timeout: number;
if (iframeStatus === "loading") {
timeout = window.setTimeout(() => {
setIframeStatus("failed");
if (timeout) {
clearInterval(timeout);
}
}, MAX_IFRAME_WAIT_TIME);
}
return () => {
if (typeof timeout !== "undefined") {
clearInterval(timeout);
}
};
}, [iframeStatus]);
if (!browser) {
return null;
}
return (
<div
style={{
position: "fixed",
left: `${Math.round(width / 2)}px`,
transform: "translateX(-50%)",
bottom: 0,
zIndex: 99999,
}}
>
<DevtoolsPin
onClick={() => {
setVisible((v) => !v);
setSelectorActive(false);
}}
onSelectorHighlight={onSelectorHighlight}
selectorActive={selectorActive}
setSelectorActive={setSelectorActive}
/>
<ResizablePane visible={visible} placement={placement}>
{({ resizing }) => (
<iframe
allow="clipboard-write;"
src={httpUrl}
srcDoc={
httpUrl
? iframeStatus === "failed"
? failedConnectionContent
: undefined
: missingUrlContent
}
style={{
width: "100%",
height: "100%",
border: "none",
borderRadius: "7px",
pointerEvents: resizing ? "none" : "auto",
background: "#14141F",
}}
/>
)}
</ResizablePane>
</div>
);
};
const missingUrlContent = `
<html style="height:100%;padding:0;margin:0;background:#14141F;">
<body style="background:#14141F;display:flex;justify-content:center;height:100%;padding:24px;margin:0;align-items:center;box-sizing:border-box;">
<h1 style="font-family:ui-monospace,monospace;font-weight:400;color:#CFD7E2;text-align:center;font-size:24px;">Could not connect to the devtools server.</h1>
</body>
</html>
`;
const failedConnectionContent = `
<html style="height:100%;padding:0;margin:0;background:#14141F;">
<body style="background:#14141F;display:flex;flex-direction:column;justify-content:center;height:100%;padding:24px;margin:0;align-items:center;box-sizing:border-box;">
<h1 style="max-width:480px;min-width:480px;font-family:ui-monospace,monospace;font-weight:400;color:#CFD7E2;text-align:left;font-size:24px;margin-bottom:12px;line-height:24px;">Devtools Server is unreachable.</h1>
<p style="max-width:480px;font-family:ui-monospace,monospace;font-weight:400;color:#6C7793;text-align:left;font-size:16px;line-height:32px;">Please make sure Refine Devtools is running and <code style="background:#303450;color:#A3ADC2;padding:3px 6px;border-radius:4px;">&lt;DevtoolsProvider /&gt;</code> has valid <code style="background:#303450;color:#A3ADC2;padding:3px 6px;border-radius:4px;">url</code> prop. Environment variables may not always be available in browser depending on your project setup.</p>
</body>
</html>
`;

View File

@@ -0,0 +1,24 @@
import React from "react";
import { DevToolsContextProvider } from "@refinedev/devtools-shared";
import { getDevtoolsUrlFromEnv } from "./utilities/get-devtools-url-from-env";
type Props = React.PropsWithChildren<{
/**
* Devtools URL to connect to the server. This will also be used for the WebSocket connections and serving the Devtools UI.
* By default, it will use the `REFINE_DEVTOOLS_PORT` environment variable to construct to URL or use `5001` as the default port.
* If you're using `refine dev` command, it will try to automatically set the environment variable for you and use it.
* If environment variable is not working for you, you can manually set the URL as a string or a tuple of `[httpUrl: string, wsUrl: string]`.
*/
url?: string | [httpUrl: string, wsUrl: string];
}>;
export const DevtoolsProvider =
__DEV_CONDITION__ !== "development"
? ({ children }: Props) => children as any
: ({ children, url = getDevtoolsUrlFromEnv() }: Props) => {
return (
<DevToolsContextProvider url={url}>
{children}
</DevToolsContextProvider>
);
};

View File

@@ -0,0 +1,20 @@
const DEFAULT_DEVTOOLS_PORT = 5001;
export const getDevtoolsUrlFromEnv = () => {
const PORT_FROM_ENV =
typeof __PROCESS_KEY__ !== "undefined" && "env" in __PROCESS_KEY__
? __PROCESS_ENV_REFINE_DEVTOOLS_PORT_KEY__ ||
__PROCESS_ENV_NEXT_PUBLIC_REFINE_DEVTOOLS_PORT_KEY__ ||
__PROCESS_ENV_REACT_APP_REFINE_DEVTOOLS_PORT_KEY__
: typeof __IMPORT_META_KEY__ !== "undefined" && __IMPORT_META_KEY__.env
? __IMPORT_META_ENV_REFINE_DEVTOOLS_PORT_KEY__ ||
__IMPORT_META_ENV_VITE_REFINE_DEVTOOLS_PORT_KEY__
: null;
const port = PORT_FROM_ENV || DEFAULT_DEVTOOLS_PORT;
return [`http://localhost:${port}`, `ws://localhost:${port}`] as [
httpUrl: string,
wsUrl: string,
];
};

View File

@@ -0,0 +1,123 @@
import type { Placement } from "src/interfaces/placement";
export const getPanelToggleTransforms = (visible: boolean) => {
return visible ? "scaleX(1) translateY(0)" : "scaleX(0) translateY(25vw)";
};
export const SIZE = 50;
export const BUFFER = 10;
const PREFERRED_DEFAULT_WIDTH = () =>
typeof window !== "undefined" ? window.innerWidth * 0.7 : 1440 * 0.7; // 70% of window width
const PREFERRED_DEFAULT_HEIGHT = () =>
typeof window !== "undefined" ? window.innerHeight * 0.7 : 900 * 0.7; // 70% of window height
export const MIN_PANEL_WIDTH = 640;
export const MIN_PANEL_HEIGHT = 360;
export const getPinButtonTransform = (hover?: boolean) => {
return `translateY(${hover ? "0" : "50%"})`;
};
export const getPanelPosition = (placement: Placement) => {
switch (placement) {
case "left":
return {
left: `calc(${SIZE}px + ${BUFFER}px)`,
top: "50%",
transform: "translateY(-50%)",
};
case "right":
return {
right: `calc(${SIZE}px + ${BUFFER}px)`,
top: "50%",
transform: "translateY(-50%)",
};
case "top":
return {
left: "50%",
top: `calc(${SIZE}px + ${BUFFER}px)`,
transform: "translateX(-50%)",
};
case "bottom":
return {
left: "50%",
bottom: `calc(${SIZE}px + ${BUFFER}px)`,
transform: "translateX(-50%)",
};
}
};
export const getMaxPanelWidth = (placement: Placement) => {
switch (placement) {
case "left":
case "right":
return (
-BUFFER -
SIZE -
BUFFER +
(typeof window !== "undefined" ? window.innerWidth : 1440) -
BUFFER
);
case "top":
case "bottom":
return (
-BUFFER +
(typeof window !== "undefined" ? window.innerWidth : 1440) -
BUFFER
);
}
};
export const getMaxPanelHeight = (placement: Placement) => {
switch (placement) {
case "left":
case "right":
return (
-BUFFER +
(typeof window !== "undefined" ? window.innerHeight : 900) -
BUFFER
);
case "top":
case "bottom":
return (
-BUFFER -
SIZE -
BUFFER +
(typeof window !== "undefined" ? window.innerHeight : 900) -
BUFFER
);
}
};
export const getDefaultPanelSize = (
placement: Placement,
preferredSize?: { width: number; height: number },
): { width: number; height: number } => {
const defaultPreferred = {
width: PREFERRED_DEFAULT_WIDTH(),
height: PREFERRED_DEFAULT_HEIGHT(),
};
const maxPanelWidth = getMaxPanelWidth(placement);
const maxPanelHeight = getMaxPanelHeight(placement);
const width = Math.min(
maxPanelWidth,
(preferredSize ?? defaultPreferred).width,
);
const height = Math.min(
maxPanelHeight,
(preferredSize ?? defaultPreferred).height,
);
return {
width: width,
height: height,
};
};
export const roundToEven = (num: number) => {
const rounded = Math.round(num);
return rounded % 2 === 0 ? rounded : rounded + 1;
};

View File

@@ -0,0 +1,157 @@
import {
getElementFromFiber,
getFiberFromElement,
getFirstFiberHasName,
getFirstStateNodeFiber,
getNameFromFiber,
getParentOfFiber,
} from "@aliemir/dom-to-fiber-utils";
type Fiber = Exclude<ReturnType<typeof getFiberFromElement>, null>;
export type SelectableElement = {
element: HTMLElement;
name: string;
};
const getChildOfFiber = (fiber: Fiber | null) => {
if (!fiber) {
return null;
}
return fiber.child;
};
const getFirstHTMLElementFromFiberByChild = (fiber: Fiber | null) => {
let child = fiber;
while (child) {
const element = getElementFromFiber(child);
if (element && element instanceof HTMLElement) {
return element;
}
child = getChildOfFiber(child) as Fiber;
}
return null;
};
const getFirstHTMLElementFromFiberByParent = (fiber: Fiber | null) => {
let parent = fiber;
while (parent) {
const element = getElementFromFiber(parent);
if (element && element instanceof HTMLElement) {
return element;
}
parent = getParentOfFiber(parent) as Fiber;
}
return null;
};
const getFirstHTMLElementFromFiber = (
fiber: Fiber | null,
): [element: HTMLElement, "child" | "parent" | "body"] => {
let element = getFirstHTMLElementFromFiberByChild(fiber);
if (element) {
return [element, "child"];
}
element = getFirstHTMLElementFromFiberByParent(fiber);
if (element) {
return [element, "parent"];
}
return [document.body, "body"];
};
const selectFiber = (start: Fiber | null, activeTraceItems: string[]) => {
let fiber = start;
let firstParentOfNodeWithName: Fiber | null = null;
let fiberWithStateNode: Fiber | null = null;
let acceptedName = false;
while (!acceptedName && fiber) {
// Get the first fiber node that has a name (look up the tree)
firstParentOfNodeWithName = getFirstFiberHasName(fiber);
// Get the first fiber node that has a state node (look up the tree)
fiberWithStateNode = getFirstStateNodeFiber(firstParentOfNodeWithName);
acceptedName = activeTraceItems.includes(
getNameFromFiber(firstParentOfNodeWithName) ?? "",
);
if (!acceptedName) {
fiber = getParentOfFiber(fiber);
}
}
if (fiberWithStateNode && firstParentOfNodeWithName) {
return {
stateNode: fiberWithStateNode,
nameFiber: firstParentOfNodeWithName,
};
}
return {
stateNode: null,
nameFiber: null,
};
};
export const filterInvisibleNodes = (nodes: SelectableElement[]) => {
return nodes.filter(
(item) => item.element.offsetWidth > 0 && item.element.offsetHeight > 0,
);
};
export const getUniqueNodes = (nodes: SelectableElement[]) => {
const uniques: SelectableElement[] = [];
nodes.forEach((node) => {
const isElementExist = uniques.find(
(item) => item.element === node.element && item.name === node.name,
);
if (!isElementExist) {
uniques.push(node);
}
});
return uniques;
};
export const traverseDom = (
node: HTMLElement | null,
activeTraceItems: string[],
): SelectableElement[] => {
if (!node) {
return [];
}
const items: SelectableElement[] = [];
const fiber = getFiberFromElement(node);
const targetFiber = selectFiber(fiber, activeTraceItems);
if (targetFiber.nameFiber) {
const [element] = getFirstHTMLElementFromFiber(targetFiber.nameFiber);
const name = getNameFromFiber(targetFiber.nameFiber);
if (element && name) {
items.push({
element,
name,
});
}
}
for (let i = 0; i < node?.children?.length ?? 0; i++) {
items.push(
...traverseDom(node.children[i] as HTMLElement, activeTraceItems),
);
}
return items;
};

View File

@@ -0,0 +1,42 @@
import React from "react";
import { DevToolsContext } from "@refinedev/devtools-shared";
import {
filterInvisibleNodes,
getUniqueNodes,
traverseDom,
type SelectableElement,
} from "./selector-helpers";
export const useSelector = (active: boolean) => {
const { httpUrl } = React.useContext(DevToolsContext);
const [selectableElements, setSelectableElements] = React.useState<
SelectableElement[]
>([]);
const fetchTraceItems = React.useCallback(async () => {
const response = await fetch(`${httpUrl}/api/unique-trace-items`);
const data = await response.json();
return data.data as string[];
}, [httpUrl]);
const prepareSelector = React.useCallback(async () => {
const fetchedTraceItems = await fetchTraceItems();
const traversedNodes = traverseDom(document.body, fetchedTraceItems);
const filterInvisible = filterInvisibleNodes(traversedNodes);
const uniqueNodes = getUniqueNodes(filterInvisible);
setSelectableElements(uniqueNodes);
}, [fetchTraceItems]);
React.useEffect(() => {
if (active) {
prepareSelector();
}
}, [active, prepareSelector]);
return {
selectableElements,
};
};

View File

@@ -0,0 +1,22 @@
{
"extends": "./tsconfig.json",
"exclude": [
"node_modules",
"dist",
"test",
"../test/**/*",
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx"
],
"compilerOptions": {
"outDir": "dist",
"declarationDir": "dist",
"declaration": true,
"emitDeclarationOnly": true,
"noEmit": false,
"declarationMap": true,
"paths": {}
}
}

View File

@@ -0,0 +1,13 @@
{
"include": ["src", "types"],
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"types": ["node"],
"rootDir": "./src",
"baseUrl": ".",
"paths": {
"@test/*": ["test/*"],
"@test": ["test"]
}
}
}

View File

@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"include": ["test", "src"],
"compilerOptions": {
"rootDir": ".",
"types": ["node", "jest", "@testing-library/jest-dom"]
}
}

View File

@@ -0,0 +1,50 @@
import { defineConfig } from "tsup";
import { NodeResolvePlugin } from "@esbuild-plugins/node-resolve";
import { lodashReplacePlugin } from "../shared/lodash-replace-plugin";
export default defineConfig((options) => ({
entry: ["src/index.ts"],
splitting: false,
sourcemap: true,
clean: false,
minify: true,
format: ["cjs", "esm"],
outExtension: ({ format }) => ({ js: format === "cjs" ? ".cjs" : ".mjs" }),
platform: "browser",
esbuildOptions: (options) => {
options.define = {
...options.define,
__DEV_CONDITION__: "process.env.NODE_ENV",
__IMPORT_META_KEY__: "import.meta",
__PROCESS_KEY__: "process",
__PROCESS_ENV_REFINE_DEVTOOLS_PORT_KEY__:
"process.env.REFINE_DEVTOOLS_PORT",
__PROCESS_ENV_NEXT_PUBLIC_REFINE_DEVTOOLS_PORT_KEY__:
"process.env.NEXT_PUBLIC_REFINE_DEVTOOLS_PORT",
__PROCESS_ENV_REACT_APP_REFINE_DEVTOOLS_PORT_KEY__:
"process.env.REACT_APP_REFINE_DEVTOOLS_PORT",
__IMPORT_META_ENV_REFINE_DEVTOOLS_PORT_KEY__:
"import.meta.env.REFINE_DEVTOOLS_PORT",
__IMPORT_META_ENV_VITE_REFINE_DEVTOOLS_PORT_KEY__:
"import.meta.env.VITE_REFINE_DEVTOOLS_PORT",
};
options.banner = {
js: '"use client"',
};
},
esbuildPlugins: [
lodashReplacePlugin,
NodeResolvePlugin({
extensions: [".js", "ts", "tsx", "jsx"],
onResolved: (resolved) => {
if (resolved.includes("node_modules")) {
return {
external: true,
};
}
return resolved;
},
}),
],
onSuccess: options.watch ? "pnpm types" : undefined,
}));