diff --git a/package-lock.json b/package-lock.json index 56d1e30..e2d7c97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@plait/layouts": "^0.62.0-next.7", "@plait/mind": "^0.62.0-next.7", "@plait/text-plugins": "^0.62.0-next.7", + "@radix-ui/react-popover": "^1.0.3", "ahooks": "^3.8.0", "classnames": "^2.5.1", "open-color": "^1.9.1", @@ -2644,6 +2645,32 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz", + "integrity": "sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==" + }, + "node_modules/@floating-ui/dom": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz", + "integrity": "sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==", + "dependencies": { + "@floating-ui/core": "^0.7.3" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-0.7.2.tgz", + "integrity": "sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==", + "dependencies": { + "@floating-ui/dom": "^0.5.3", + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -3931,6 +3958,283 @@ "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", "dev": true }, + "node_modules/@radix-ui/primitive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.0.tgz", + "integrity": "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.1.tgz", + "integrity": "sha512-1yientwXqXcErDHEv8av9ZVNEBldH8L9scVR3is20lL+jOCfcJyMFZFEY5cgIrgexsq1qggSXqiEL/d/4f+QXA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.0.tgz", + "integrity": "sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.2.tgz", + "integrity": "sha512-WjJzMrTWROozDqLB0uRWYvj4UuXsM/2L19EmQ3Au+IJWqwvwq9Bwd+P8ivo0Deg9JDPArR1I6MbWNi1CmXsskg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-primitive": "1.0.1", + "@radix-ui/react-use-callback-ref": "1.0.0", + "@radix-ui/react-use-escape-keydown": "1.0.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.0.tgz", + "integrity": "sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.1.tgz", + "integrity": "sha512-Ej2MQTit8IWJiS2uuujGUmxXjF/y5xZptIIQnyd2JHLwtV0R2j9NRVoRj/1j/gJ7e3REdaBw4Hjf4a1ImhkZcQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-primitive": "1.0.1", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.0.tgz", + "integrity": "sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.3.tgz", + "integrity": "sha512-YwedSukfWsyJs3/yP3yXUq44k4/JBe3jqU63Z8v2i19qZZ3dsx32oma17ztgclWPNuqp3A+Xa9UiDlZHyVX8Vg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-dismissable-layer": "1.0.2", + "@radix-ui/react-focus-guards": "1.0.0", + "@radix-ui/react-focus-scope": "1.0.1", + "@radix-ui/react-id": "1.0.0", + "@radix-ui/react-popper": "1.1.0", + "@radix-ui/react-portal": "1.0.1", + "@radix-ui/react-presence": "1.0.0", + "@radix-ui/react-primitive": "1.0.1", + "@radix-ui/react-slot": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.0.tgz", + "integrity": "sha512-07U7jpI0dZcLRAxT7L9qs6HecSoPhDSJybF7mEGHJDBDv+ZoGCvIlva0s+WxMXwJEav+ckX3hAlXBtnHmuvlCQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "0.7.2", + "@radix-ui/react-arrow": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-primitive": "1.0.1", + "@radix-ui/react-use-callback-ref": "1.0.0", + "@radix-ui/react-use-layout-effect": "1.0.0", + "@radix-ui/react-use-rect": "1.0.0", + "@radix-ui/react-use-size": "1.0.0", + "@radix-ui/rect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.1.tgz", + "integrity": "sha512-NY2vUWI5WENgAT1nfC6JS7RU5xRYBfjZVLq0HmgEN1Ezy3rk/UruMV4+Rd0F40PEaFC5SrLS1ixYvcYIQrb4Ig==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.0.tgz", + "integrity": "sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.1.tgz", + "integrity": "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz", + "integrity": "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz", + "integrity": "sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.2.tgz", + "integrity": "sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz", + "integrity": "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.0.tgz", + "integrity": "sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/rect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.0.tgz", + "integrity": "sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.0.tgz", + "integrity": "sha512-d0O68AYy/9oeEy1DdC07bz1/ZXX+DqCskRd3i4JzLSTXwefzaepQrKjXC7aNM8lTHjFLDO0pDgaEiQ7jEk+HVg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, "node_modules/@rollup/pluginutils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", @@ -5279,13 +5583,13 @@ "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "dev": true + "devOptional": true }, "node_modules/@types/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", - "dev": true, + "devOptional": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -6231,6 +6535,17 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", @@ -7536,7 +7851,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "devOptional": true }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -7802,6 +8117,11 @@ "node": ">=8" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + }, "node_modules/detect-port": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", @@ -9548,6 +9868,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -10158,6 +10486,14 @@ "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.12.2.tgz", "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==" }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -13078,6 +13414,73 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", + "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -14883,6 +15286,60 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", + "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index fa7552f..319bd52 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@plait/layouts": "^0.62.0-next.7", "@plait/mind": "^0.62.0-next.7", "@plait/text-plugins": "^0.62.0-next.7", + "@radix-ui/react-popover": "^1.0.3", "ahooks": "^3.8.0", "classnames": "^2.5.1", "open-color": "^1.9.1", diff --git a/packages/drawnix/src/components/arrow-popup.tsx b/packages/drawnix/src/components/arrow-popup.tsx new file mode 100644 index 0000000..03566c3 --- /dev/null +++ b/packages/drawnix/src/components/arrow-popup.tsx @@ -0,0 +1,78 @@ +import classNames from 'classnames'; +import { Island } from './island'; +import Stack from './stack'; +import { ToolButton } from './tool-button'; +import { StraightArrowIcon, ElbowArrowIcon, CurveArrowIcon } from './icons'; +import { useBoard } from '@plait/react-board'; +import { BoardTransforms, PlaitBoard } from '@plait/core'; +import * as Popover from '@radix-ui/react-popover'; +import React from 'react'; +import { BoardCreationMode, setCreationMode } from '@plait/common'; +import { ArrowLineShape, DrawPointerType } from '@plait/draw'; + +export interface ArrowProps { + icon: React.ReactNode; + title: string; + pointer: ArrowLineShape; +} + +const ARROWS: ArrowProps[] = [ + { + icon: StraightArrowIcon, + title: 'Straight Arrow Line', + pointer: ArrowLineShape.straight, + }, + { + icon: ElbowArrowIcon, + title: 'Elbow Arrow Line', + pointer: ArrowLineShape.elbow, + }, + { + icon: CurveArrowIcon, + title: 'Curve Arrow Line', + pointer: ArrowLineShape.curve, + }, +]; + +export type ArrowPopupProps = { + onPointerUp: (pointer: DrawPointerType) => void; +}; + +export const ArrowPopupContent: React.FC = ({ + onPointerUp, +}) => { + const board = useBoard(); + const container = PlaitBoard.getBoardContainer(board); + + return ( + + + + + {ARROWS.map((arrow, index) => { + return ( + { + setCreationMode(board, BoardCreationMode.drawing); + BoardTransforms.updatePointerType(board, arrow.pointer); + }} + onPointerUp={() => { + onPointerUp(arrow.pointer); + }} + /> + ); + })} + + + + + ); +}; diff --git a/packages/drawnix/src/components/draw-toolbar.tsx b/packages/drawnix/src/components/draw-toolbar.tsx index 71febcb..88a01a7 100644 --- a/packages/drawnix/src/components/draw-toolbar.tsx +++ b/packages/drawnix/src/components/draw-toolbar.tsx @@ -8,20 +8,31 @@ import { SelectionIcon, ShapeIcon, TextIcon, - StraightArrowLineIcon + StraightArrowLineIcon, } from './icons'; import { useBoard } from '@plait/react-board'; import { BoardTransforms, PlaitBoard, PlaitPointerType } from '@plait/core'; import { MindPointerType } from '@plait/mind'; import { DrawnixPointerType } from '../drawnix'; import { BoardCreationMode, setCreationMode } from '@plait/common'; -import { BasicShapes } from '@plait/draw'; +import { ArrowLineShape, BasicShapes, DrawPointerType } from '@plait/draw'; +import * as Popover from '@radix-ui/react-popover'; +import { useSetState } from 'ahooks'; +import { ShapePopupContent } from './shape-popup'; +import { ArrowPopupContent } from './arrow-popup'; +import { useState } from 'react'; + +export enum PopupKey { + 'shape' = 'shape', + 'arrow' = 'arrow', +} type AppToolButtonProps = { title?: string; name?: string; icon: React.ReactNode; pointer?: DrawnixPointerType; + popupKey?: PopupKey; }; const isBasicPointer = (pointer: string) => { @@ -34,40 +45,57 @@ export const BUTTONS: AppToolButtonProps[] = [ { icon: HandIcon, pointer: PlaitPointerType.hand, - title: 'Hand' + title: 'Hand', }, { icon: SelectionIcon, pointer: PlaitPointerType.selection, - title: 'Selection' + title: 'Selection', }, { icon: MindIcon, pointer: MindPointerType.mind, - title: 'Mind' + title: 'Mind', }, { icon: TextIcon, pointer: BasicShapes.text, - title: 'Text' + title: 'Text', }, { - icon: ShapeIcon, - title: 'Shape' + icon: StraightArrowLineIcon, + title: 'Arrow Line', + popupKey: PopupKey.arrow, + pointer: ArrowLineShape.straight, }, { - icon: StraightArrowLineIcon, - title: 'Arrow Line' - } + icon: ShapeIcon, + title: 'Shape', + popupKey: PopupKey.shape, + pointer: BasicShapes.rectangle, + }, ]; export type DrawToolbarProps = { setPointer: (pointer: DrawnixPointerType) => void; }; +// TODO provider by plait/draw +export const isArrowLinePointer = (board: PlaitBoard) => { + return Object.keys(ArrowLineShape).includes(board.pointer); +}; + +export const isShapePointer = (board: PlaitBoard) => { + return Object.keys(BasicShapes).includes(board.pointer); +}; + export const DrawToolbar: React.FC = ({ setPointer }) => { const board = useBoard(); + const [arrowOpen, setArrowOpen] = useState(false); + + const [shapeOpen, setShapeOpen] = useState(false); + const onChange = (pointer: DrawnixPointerType) => { BoardTransforms.updatePointerType(board, pointer); setPointer(pointer); @@ -82,17 +110,104 @@ export const DrawToolbar: React.FC = ({ setPointer }) => { const onPointerUp = () => { setCreationMode(board, BoardCreationMode.drawing); }; + + const isChecked = (button: AppToolButtonProps) => { + return ( + PlaitBoard.isPointer(board, button.pointer) && !arrowOpen && !shapeOpen + ); + }; + return ( {BUTTONS.map((button, index) => { + if (button.popupKey === PopupKey.shape) { + return ( + { + setShapeOpen(open); + }} + > + + { + BoardTransforms.updatePointerType( + board, + BasicShapes.rectangle + ); + setPointer(BasicShapes.rectangle); + setCreationMode(board, BoardCreationMode.drawing); + }} + /> + + { + setShapeOpen(false); + setPointer(pointer); + }} + > + + ); + } + if (button.popupKey === PopupKey.arrow) { + return ( + { + setArrowOpen(open); + }} + > + + { + BoardTransforms.updatePointerType( + board, + ArrowLineShape.straight + ); + setPointer(ArrowLineShape.straight); + setCreationMode(board, BoardCreationMode.drawing); + }} + /> + + { + { + setArrowOpen(false); + setPointer(pointer); + }} + > + } + + ); + } return ( { diff --git a/packages/drawnix/src/components/icons.tsx b/packages/drawnix/src/components/icons.tsx index d33548c..e1dc889 100644 --- a/packages/drawnix/src/components/icons.tsx +++ b/packages/drawnix/src/components/icons.tsx @@ -53,3 +53,84 @@ export const StraightArrowLineIcon = createIcon( ); + +export const RectangleIcon = createIcon( + + + +); + +export const EllipseIcon = createIcon( + + + + + +); + +export const TriangleIcon = createIcon( + + + + + +); + +export const DiamondIcon = createIcon( + + + + + +); + +export const ParallelogramIcon = createIcon( + + + + + +); + +export const RoundRectangleIcon = createIcon( + + + + + +); + +export const StraightArrowIcon = createIcon( + + + + + +); + +export const ElbowArrowIcon = createIcon( + + + + + +); + +export const CurveArrowIcon = createIcon( + + + + + +); diff --git a/packages/drawnix/src/components/shape-popup.tsx b/packages/drawnix/src/components/shape-popup.tsx new file mode 100644 index 0000000..05aebe3 --- /dev/null +++ b/packages/drawnix/src/components/shape-popup.tsx @@ -0,0 +1,120 @@ +import classNames from 'classnames'; +import { Island } from './island'; +import Stack from './stack'; +import { ToolButton } from './tool-button'; +import { + RectangleIcon, + EllipseIcon, + TriangleIcon, + DiamondIcon, + ParallelogramIcon, + RoundRectangleIcon, +} from './icons'; +import { useBoard } from '@plait/react-board'; +import { BoardTransforms, PlaitBoard } from '@plait/core'; +import * as Popover from '@radix-ui/react-popover'; +import React from 'react'; +import { DrawnixPointerType } from '../drawnix'; +import { BoardCreationMode, setCreationMode } from '@plait/common'; +import { BasicShapes, DrawPointerType } from '@plait/draw'; + +export interface ShapeProps { + icon: React.ReactNode; + title: string; + pointer: DrawPointerType; +} + +const splitRows = (shapes: ShapeProps[], cols: number) => { + let result = []; + for (let i = 0; i < shapes.length; i += cols) { + result.push(shapes.slice(i, i + cols)); + } + return result; +}; + +const SHAPES: ShapeProps[] = [ + { + icon: RectangleIcon, + title: 'Rectangle', + pointer: BasicShapes.rectangle, + }, + { + icon: EllipseIcon, + title: 'Ellipse', + pointer: BasicShapes.ellipse, + }, + { + icon: TriangleIcon, + title: 'Triangle', + pointer: BasicShapes.triangle, + }, + { + icon: DiamondIcon, + title: 'Diamond', + pointer: BasicShapes.diamond, + }, + { + icon: ParallelogramIcon, + title: 'Parallelogram', + pointer: BasicShapes.parallelogram, + }, + { + icon: RoundRectangleIcon, + title: 'RoundRectangle', + pointer: BasicShapes.roundRectangle, + }, +]; + +const ROW_SHAPES = splitRows(SHAPES, 5); + +export type ShapePopupProps = { + onPointerUp: (pointer: DrawPointerType) => void; +}; + +export const ShapePopupContent: React.FC = ({ + onPointerUp, +}) => { + const board = useBoard(); + const container = PlaitBoard.getBoardContainer(board); + return ( + + + + + {ROW_SHAPES.map((rowShapes, rowIndex) => { + return ( + + {rowShapes.map((shape, index) => { + return ( + { + setCreationMode(board, BoardCreationMode.dnd); + BoardTransforms.updatePointerType( + board, + shape.pointer + ); + }} + onPointerUp={() => { + setCreationMode(board, BoardCreationMode.drawing); + onPointerUp(shape.pointer); + }} + /> + ); + })} + + ); + })} + + + + + ); +}; diff --git a/packages/drawnix/src/components/tool-button.tsx b/packages/drawnix/src/components/tool-button.tsx index 6068b58..862b72d 100644 --- a/packages/drawnix/src/components/tool-button.tsx +++ b/packages/drawnix/src/components/tool-button.tsx @@ -1,20 +1,20 @@ // Credits to excalidraw -import "./tool-icon.scss"; +import './tool-icon.scss'; -import type { CSSProperties } from "react"; -import React, { useEffect, useRef, useState } from "react"; -import { AbortError } from "../errors"; -import { isPromiseLike } from "../utils"; -import classNames from "classnames"; -import { EventPointerType } from "../types"; +import type { CSSProperties } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; +import { AbortError } from '../errors'; +import { isPromiseLike } from '../utils'; +import classNames from 'classnames'; +import { EventPointerType } from '../types'; -export type ToolButtonSize = "small" | "medium"; +export type ToolButtonSize = 'small' | 'medium'; type ToolButtonBaseProps = { icon?: React.ReactNode; - "aria-label": string; - "aria-keyshortcuts"?: string; - "data-testid"?: string; + 'aria-label': string; + 'aria-keyshortcuts'?: string; + 'data-testid'?: string; label?: string; title?: string; name?: string; @@ -28,44 +28,44 @@ type ToolButtonBaseProps = { disabled?: boolean; className?: string; style?: CSSProperties; + onPointerDown?(data: { pointerType: EventPointerType }): void; + onPointerUp?(data: { pointerType: EventPointerType }): void; }; type ToolButtonProps = | (ToolButtonBaseProps & { - type: "button"; + type: 'button'; children?: React.ReactNode; onClick?(event: React.MouseEvent): void; }) | (ToolButtonBaseProps & { - type: "submit"; + type: 'submit'; children?: React.ReactNode; onClick?(event: React.MouseEvent): void; }) | (ToolButtonBaseProps & { - type: "icon"; + type: 'icon'; children?: React.ReactNode; onClick?(): void; }) | (ToolButtonBaseProps & { - type: "radio"; + type: 'radio'; checked: boolean; onChange?(data: { pointerType: EventPointerType | null }): void; - onPointerDown?(data: { pointerType: EventPointerType }): void; - onPointerUp?(data: { pointerType: EventPointerType }): void; }); export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => { const { id: drawnixId } = { id: 'drawnix' }; const innerRef = React.useRef(null); React.useImperativeHandle(ref, () => innerRef.current); - const sizeCn = `tool-icon_size_${props.size}`; + const sizeCn = `tool-icon_size_${props.size || 'medium'}`; const [isLoading, setIsLoading] = useState(false); const isMountedRef = useRef(true); const onClick = async (event: React.MouseEvent) => { - const ret = "onClick" in props && props.onClick?.(event); + const ret = 'onClick' in props && props.onClick?.(event); if (isPromiseLike(ret)) { try { @@ -95,35 +95,40 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => { const lastPointerTypeRef = useRef(null); if ( - props.type === "button" || - props.type === "icon" || - props.type === "submit" + props.type === 'button' || + props.type === 'icon' || + props.type === 'submit' ) { - const type = (props.type === "icon" ? "button" : props.type) as - | "button" - | "submit"; + const type = (props.type === 'icon' ? 'button' : props.type) as + | 'button' + | 'submit'; return ( @@ -153,7 +156,7 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => { return (