diff --git a/.prettierignore b/.prettierignore index 7e108c091..535bb89f7 100644 --- a/.prettierignore +++ b/.prettierignore @@ -31,3 +31,6 @@ package.json package-lock.json tsconfig.json vite.config.ts + +# Ignore autogenerated storybook files +storybook-static diff --git a/package-lock.json b/package-lock.json index 1d975f33c..0bbb5c74c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6060,6 +6060,11 @@ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" } }, + "node_modules/@remirror/core-constants": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz", + "integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==" + }, "node_modules/@remix-run/router": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz", @@ -15854,6 +15859,411 @@ "@testing-library/dom": ">=7.21.4" } }, + "node_modules/@tiptap/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.9.0.tgz", + "integrity": "sha512-aXWZXoeNYxvQ7xExqxB2KJ7eJxazpB6p+hlWRA7gluppSewiGWTPpBJeWoax6kDWJxceklO6dWa8UmZWSclMiQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.9.0.tgz", + "integrity": "sha512-hMONFoby2ZtPVCawkYX47TvRbd/QAYNYk3nre7ldtGtfKvzGuMpmzFs2UwT9ubKtEhf0kE15ghXJz5bEBoj/1w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.9.0.tgz", + "integrity": "sha512-lZzIbJxgsau4dYE8q8Ax75flSFrOUEL4Bk402SmIrbAfM/No5XW5t1thSedczhUKPx286U1ZaQbBFcmm/ZiV0g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.9.0.tgz", + "integrity": "sha512-Dg6cB0L/bjEHHi6x2w8KMs1NIJ90SaAekImb0GSIKaijErzFIRQSZEZQWiaGMJ8qoA6i6w6ey04qITby0KosCA==", + "dependencies": { + "tippy.js": "^6.3.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.9.0.tgz", + "integrity": "sha512-26Hl0TDy94RUOMqYt1k+cT/4E84Nq+MyG6fmMic+PYb5/D60hR/wbscLw+Op4gGAtc3XHIZS9tW1wv6QHno0iQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/extension-list-item": "^2.7.0", + "@tiptap/extension-text-style": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.9.0.tgz", + "integrity": "sha512-8C96NeSpCg41dBncGtJFaxUMs60Lhz3QxHDm+1asS3FvmwdwrlpM9/oszGmw70lQKR3XtsW8Bqmm0tWIZhosWw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.9.0.tgz", + "integrity": "sha512-LhkX2KvdK6h6FZfvKXOY6YJ87IDx3rngfdABghJOk54R53kb8N4zdfzbkXE9VMr/lKj8EKs/A9jTWM3jaYwvDA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.9.0.tgz", + "integrity": "sha512-D4VrM472wNXd96vausXOaWKtZClZcGPNbwUICaz3NR/pWu19LqrglTfUuDs4JK8v930BQnoA+io4PljFC/qfXA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.9.0.tgz", + "integrity": "sha512-O4uQC54rTGiBB2gD6KnJ3Fe05SGS/WEf9yMs/uT0Vdsqf3TcG4gVbkQg6ilhEj8R9H8ZnKQUqEaLUzlOYhJd7A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-floating-menu": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.9.0.tgz", + "integrity": "sha512-sB9bc6TmB8u5QkuU163CcvY5jR4muruQ2ZJ8TE4uRitOH/vLbZJm0pGSVV0D92SZn8Oes5GZODXCEjTjoXZUUw==", + "dependencies": { + "tippy.js": "^6.3.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.9.0.tgz", + "integrity": "sha512-wW1aC+u9z3h1HPN+cI2XSopqdo70AkBZb93hYn1AsAn9ecQ8P15kr+O7Xc5LS65mai0M31yG27AzZJU/oo5suA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.9.0.tgz", + "integrity": "sha512-tXdirjVwydO5wxrqLGMMhTMqN+EDXRuXIX/OaiHcl799KG2aYBnjAsVCVdnGTzRIwd8auWrCnA7N15deJ/nZTA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.9.0.tgz", + "integrity": "sha512-DNSHHMbYbnutO1pbtixZFGWIxvMCFxol07r7cERjTW5OOY8/9y4Q3EilKrrjhePTpVINgoBkJK+cTxepEaCH6w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-history": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.9.0.tgz", + "integrity": "sha512-3zQXtycp+iNJhfLz4HEGrRRFUFlIqpHsjGIbv9veHAwO+zz+Skh1IC04IPcCbvsLXMie64tj6HDRt+OdzUCJSg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.9.0.tgz", + "integrity": "sha512-afKcStszoDX8kAOkPBD5GkupiAUtCmNMbtcMIU9HAKLKl3ll2RYvbVQT7haYEU+b5qBRFKiJO+7Sk31oEIbq2w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.9.0.tgz", + "integrity": "sha512-8n7QLGucxZwHE9tDCPA6ugV0d/ONNnqLNc0LHvTKmv/ULmbGgayLQGhqluMEuK29rfkaAxPBQiAbDOyjFZj8qQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.9.0.tgz", + "integrity": "sha512-HbJcHDd575r9JzkndxBZ7Ouw5DvoJRUL93vEIQybBJ5J7qa3SaOvGp9XKeiEVMvTCsphIU2Ucod8F+HNsAxCUA==", + "dependencies": { + "linkifyjs": "^4.1.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.9.0.tgz", + "integrity": "sha512-5Ifc0eip1nuNuxNF/FjhFYOdrmql/hnTQ6hRZpn+BW6yi4bLFGWThDt/gpK2ZIOKvGDQ+JFwRNzTPwbi2KWp0Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.9.0.tgz", + "integrity": "sha512-ULkvF8g4fypL/1p5UoeF3TJqYJ1UR//9PDNKt0mqZ2Vp9RhlclMpUZWntih7bR7ah15xW0bgqpXgcUn0oNQ/gg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/extension-list-item": "^2.7.0", + "@tiptap/extension-text-style": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.9.0.tgz", + "integrity": "sha512-VxLeE181KnJfvMCw09lOk9gwz8sg7eIVxpChHvRZXLdCELNv6dp/kl9w0wW46nOQ6qh0Ui8/G7Mruly9vSlVsQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-placeholder": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.9.0.tgz", + "integrity": "sha512-iJ3d0GgbAmQoHjy6+xuhyvX6feqOwFc5fLEUrVFvmtQGODmHA73bJaLYcyCFEtsB0L4de14pBFObY0BTuNkl/w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.9.0.tgz", + "integrity": "sha512-y1vj8hlUy3uEuo7awH2/RFpj1q9//pKB+Sp38HXpXOvhaKsJaSzSaDRlJEyybApQ99ng5O283I+WmdmMqAc3xA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.9.0.tgz", + "integrity": "sha512-wdnauKHSiXxGGMztPoUuHsfhiF+1NZ7gNRlP8KySCeYyBeu0yrN0eKzGLPABH6aZi5pWx/crRV+AJsUooRvh4Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-text-style": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.9.0.tgz", + "integrity": "sha512-rPR5wx8Onlse6+Bf0VNtOM3eBFV/ynW6tCKnJIsrtRJKRsiYpzbVA9QAP6mOv/bmzy6oRCqDPXTstPIf9eqW6Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-underline": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.9.0.tgz", + "integrity": "sha512-EgmVHsXrO+wLHjT9SstTLHhfNXuqP7V2PFKTkG/OsooFwStio6E88PfytBJH9f+5v3iryn+QfCqBxq1Qd4lfxQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/pm": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.9.0.tgz", + "integrity": "sha512-O7gj73B8/8LkaCICEQ1jlKPYA9cpmEyTleSDjpv4bA/yJSVEbVth6e3pS8OWVKbDgOzWUvUervB7i68e26Zweg==", + "dependencies": { + "prosemirror-changeset": "^2.2.1", + "prosemirror-collab": "^1.3.1", + "prosemirror-commands": "^1.6.0", + "prosemirror-dropcursor": "^1.8.1", + "prosemirror-gapcursor": "^1.3.2", + "prosemirror-history": "^1.4.1", + "prosemirror-inputrules": "^1.4.0", + "prosemirror-keymap": "^1.2.2", + "prosemirror-markdown": "^1.13.0", + "prosemirror-menu": "^1.2.4", + "prosemirror-model": "^1.22.3", + "prosemirror-schema-basic": "^1.2.3", + "prosemirror-schema-list": "^1.4.1", + "prosemirror-state": "^1.4.3", + "prosemirror-tables": "^1.4.0", + "prosemirror-trailing-node": "^3.0.0", + "prosemirror-transform": "^1.10.0", + "prosemirror-view": "^1.34.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@tiptap/react": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.9.0.tgz", + "integrity": "sha512-Q+I/clY2D/ZfZ9Sg3+Fyq5ZTONa7R2GsU9FWY2AH1F5fjUfV0ljLTCVKbEEOJqmERU4CMtUgjSII9Svfk7b/Qg==", + "dependencies": { + "@tiptap/extension-bubble-menu": "^2.9.0", + "@tiptap/extension-floating-menu": "^2.9.0", + "@types/use-sync-external-store": "^0.0.6", + "fast-deep-equal": "^3", + "use-sync-external-store": "^1.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + } + }, + "node_modules/@tiptap/starter-kit": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.9.0.tgz", + "integrity": "sha512-xj44XsQlHZlAAVDgiZFt4DQWuSKCys/HtaInO9JYLHGmfQ1k/HkV9BFMCTMFPf+Ofqsemx/C2RXuuWLaBW08bw==", + "dependencies": { + "@tiptap/core": "^2.9.0", + "@tiptap/extension-blockquote": "^2.9.0", + "@tiptap/extension-bold": "^2.9.0", + "@tiptap/extension-bullet-list": "^2.9.0", + "@tiptap/extension-code": "^2.9.0", + "@tiptap/extension-code-block": "^2.9.0", + "@tiptap/extension-document": "^2.9.0", + "@tiptap/extension-dropcursor": "^2.9.0", + "@tiptap/extension-gapcursor": "^2.9.0", + "@tiptap/extension-hard-break": "^2.9.0", + "@tiptap/extension-heading": "^2.9.0", + "@tiptap/extension-history": "^2.9.0", + "@tiptap/extension-horizontal-rule": "^2.9.0", + "@tiptap/extension-italic": "^2.9.0", + "@tiptap/extension-list-item": "^2.9.0", + "@tiptap/extension-ordered-list": "^2.9.0", + "@tiptap/extension-paragraph": "^2.9.0", + "@tiptap/extension-strike": "^2.9.0", + "@tiptap/extension-text": "^2.9.0", + "@tiptap/extension-text-style": "^2.9.0", + "@tiptap/pm": "^2.9.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -16287,12 +16697,26 @@ "@types/node": "*" } }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==" + }, "node_modules/@types/lodash": { "version": "4.14.199", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.199.tgz", "integrity": "sha512-Vrjz5N5Ia4SEzWWgIVwnHNEnb1UE1XMkvY5DGXrAeOGE9imk0hgTHh5GyDjLDJi9OTCn9oo9dXH1uToK1VRfrg==", "dev": true }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, "node_modules/@types/mdast": { "version": "3.0.12", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.12.tgz", @@ -16301,6 +16725,11 @@ "@types/unist": "^2" } }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==" + }, "node_modules/@types/mdx": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.7.tgz", @@ -16524,6 +16953,11 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.8.tgz", "integrity": "sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw==" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==" + }, "node_modules/@types/vfile": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/vfile/-/vfile-3.0.2.tgz", @@ -17514,8 +17948,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/aria-query": { "version": "5.1.3", @@ -20570,6 +21003,11 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, "node_modules/cross-fetch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", @@ -22084,7 +22522,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -23148,8 +23585,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-diff": { "version": "1.3.0", @@ -30488,6 +30924,19 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/linkifyjs": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.3.tgz", + "integrity": "sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg==" + }, "node_modules/listr2": { "version": "3.14.0", "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", @@ -31064,6 +31513,22 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, "node_modules/markdown-table": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz", @@ -31255,6 +31720,11 @@ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==" + }, "node_modules/meant": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/meant/-/meant-1.0.3.tgz", @@ -33657,6 +34127,11 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/orderedmap": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", + "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==" + }, "node_modules/os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -35261,6 +35736,183 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/prosemirror-changeset": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.2.1.tgz", + "integrity": "sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==", + "dependencies": { + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-collab": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz", + "integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==", + "dependencies": { + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.6.1.tgz", + "integrity": "sha512-tNy4uaGWzvuUYXDke7B28krndIrdQJhSh0OLpubtwtEwFbjItOj/eoAfPvstBJyyV0S2+b5t4G+4XPXdxar6pg==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.10.2" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.1.tgz", + "integrity": "sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz", + "integrity": "sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==", + "dependencies": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "node_modules/prosemirror-history": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.4.1.tgz", + "integrity": "sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-inputrules": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.4.0.tgz", + "integrity": "sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.2.tgz", + "integrity": "sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-markdown": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.1.tgz", + "integrity": "sha512-Sl+oMfMtAjWtlcZoj/5L/Q39MpEnVZ840Xo330WJWUvgyhNmLBLN7MsHn07s53nG/KImevWHSE6fEj4q/GihHw==", + "dependencies": { + "@types/markdown-it": "^14.0.0", + "markdown-it": "^14.0.0", + "prosemirror-model": "^1.20.0" + } + }, + "node_modules/prosemirror-menu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.4.tgz", + "integrity": "sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==", + "dependencies": { + "crelt": "^1.0.0", + "prosemirror-commands": "^1.0.0", + "prosemirror-history": "^1.0.0", + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-model": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.23.0.tgz", + "integrity": "sha512-Q/fgsgl/dlOAW9ILu4OOhYWQbc7TQd4BwKH/RwmUjyVf8682Be4zj3rOYdLnYEcGzyg8LL9Q5IWYKD8tdToreQ==", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-basic": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.3.tgz", + "integrity": "sha512-h+H0OQwZVqMon1PNn0AG9cTfx513zgIG2DY00eJ00Yvgb3UD+GQ/VlWW5rcaxacpCGT1Yx8nuhwXk4+QbXUfJA==", + "dependencies": { + "prosemirror-model": "^1.19.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.4.1.tgz", + "integrity": "sha512-jbDyaP/6AFfDfu70VzySsD75Om2t3sXTOdl5+31Wlxlg62td1haUpty/ybajSfJ1pkGadlOfwQq9kgW5IMo1Rg==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.7.3" + } + }, + "node_modules/prosemirror-state": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz", + "integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.27.0" + } + }, + "node_modules/prosemirror-tables": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.5.0.tgz", + "integrity": "sha512-VMx4zlYWm7aBlZ5xtfJHpqa3Xgu3b7srV54fXYnXgsAcIGRqKSrhiK3f89omzzgaAgAtDOV4ImXnLKhVfheVNQ==", + "dependencies": { + "prosemirror-keymap": "^1.1.2", + "prosemirror-model": "^1.8.1", + "prosemirror-state": "^1.3.1", + "prosemirror-transform": "^1.2.1", + "prosemirror-view": "^1.13.3" + } + }, + "node_modules/prosemirror-trailing-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz", + "integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==", + "dependencies": { + "@remirror/core-constants": "3.0.0", + "escape-string-regexp": "^4.0.0" + }, + "peerDependencies": { + "prosemirror-model": "^1.22.1", + "prosemirror-state": "^1.4.2", + "prosemirror-view": "^1.33.8" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.2.tgz", + "integrity": "sha512-2iUq0wv2iRoJO/zj5mv8uDUriOHWzXRnOTVgCzSXnktS/2iQRa3UUQwVlkBlYZFtygw6Nh1+X4mGqoYBINn5KQ==", + "dependencies": { + "prosemirror-model": "^1.21.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.34.3", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.34.3.tgz", + "integrity": "sha512-mKZ54PrX19sSaQye+sef+YjBbNu2voNwLS1ivb6aD2IRmxRGW64HU9B644+7OfJStGLyxvOreKqEgfvXa91WIA==", + "dependencies": { + "prosemirror-model": "^1.20.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, "node_modules/protocols": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", @@ -35338,6 +35990,14 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "engines": { + "node": ">=6" + } + }, "node_modules/puppeteer-core": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-2.1.1.tgz", @@ -37171,6 +37831,11 @@ "fsevents": "~2.3.2" } }, + "node_modules/rope-sequence": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", + "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==" + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -40840,6 +41505,14 @@ "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", "dev": true }, + "node_modules/tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "dependencies": { + "@popperjs/core": "^2.9.0" + } + }, "node_modules/tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -41412,6 +42085,11 @@ "node": ">=8" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" + }, "node_modules/uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", @@ -41971,6 +42649,14 @@ "react-dom": "16.8.0 - 18" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", + "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/user-home": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", @@ -42834,6 +43520,11 @@ "node": ">=0.10.0" } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, "node_modules/w3c-xmlserializer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", @@ -43705,6 +44396,18 @@ "@dnd-kit/modifiers": "^7.0.0", "@dnd-kit/sortable": "8.0.0", "@dnd-kit/utilities": "3.2.2", + "@tiptap/extension-blockquote": "2.9.0", + "@tiptap/extension-bullet-list": "2.9.0", + "@tiptap/extension-code-block": "2.9.0", + "@tiptap/extension-heading": "2.9.0", + "@tiptap/extension-link": "2.9.0", + "@tiptap/extension-list-item": "2.9.0", + "@tiptap/extension-ordered-list": "2.9.0", + "@tiptap/extension-placeholder": "2.9.0", + "@tiptap/extension-underline": "2.9.0", + "@tiptap/pm": "2.9.0", + "@tiptap/react": "2.9.0", + "@tiptap/starter-kit": "2.9.0", "@types/react": "18.0.27", "bootstrap": "5.2.3", "react-bootstrap": "2.7.2", diff --git a/packages/design-system/CHANGELOG.md b/packages/design-system/CHANGELOG.md index fa66761af..330ffddb8 100644 --- a/packages/design-system/CHANGELOG.md +++ b/packages/design-system/CHANGELOG.md @@ -53,6 +53,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - **Tab:** extend Props Interface to accept `disabled` prop to disable the tab. - **Offcanvas:** NEW Offcanvas component. - **FormCheckbox:** modify Props Interface to allow any react node as `label` prop. +- **RichTextEditor:** NEW Rich Text Editor component. # [1.1.0](https://github.com/IQSS/dataverse-frontend/compare/@iqss/dataverse-design-system@1.0.1...@iqss/dataverse-design-system@1.1.0) (2024-03-12) diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 4d0fe2e08..1eb3708cd 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -35,6 +35,18 @@ "@dnd-kit/modifiers": "^7.0.0", "@dnd-kit/sortable": "8.0.0", "@dnd-kit/utilities": "3.2.2", + "@tiptap/extension-blockquote": "2.9.0", + "@tiptap/extension-bullet-list": "2.9.0", + "@tiptap/extension-code-block": "2.9.0", + "@tiptap/extension-heading": "2.9.0", + "@tiptap/extension-link": "2.9.0", + "@tiptap/extension-list-item": "2.9.0", + "@tiptap/extension-ordered-list": "2.9.0", + "@tiptap/extension-placeholder": "2.9.0", + "@tiptap/extension-underline": "2.9.0", + "@tiptap/pm": "2.9.0", + "@tiptap/react": "2.9.0", + "@tiptap/starter-kit": "2.9.0", "@types/react": "18.0.27", "bootstrap": "5.2.3", "react-bootstrap": "2.7.2", diff --git a/packages/design-system/src/lib/components/rich-text-editor/EditorActions.module.scss b/packages/design-system/src/lib/components/rich-text-editor/EditorActions.module.scss new file mode 100644 index 000000000..dc277c80f --- /dev/null +++ b/packages/design-system/src/lib/components/rich-text-editor/EditorActions.module.scss @@ -0,0 +1,19 @@ +@use 'sass:color'; +@import 'src/lib/assets/styles/design-tokens/colors.module'; + +.editor-actions-wrapper { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + align-items: center; + padding-inline: 0.5rem; + + .editor-actions-button { + display: grid; + place-items: center; + + &.selected { + background-color: color.adjust($dv-secondary-color, $blackness: 30%); + } + } +} diff --git a/packages/design-system/src/lib/components/rich-text-editor/EditorActions.tsx b/packages/design-system/src/lib/components/rich-text-editor/EditorActions.tsx new file mode 100644 index 000000000..458bfd169 --- /dev/null +++ b/packages/design-system/src/lib/components/rich-text-editor/EditorActions.tsx @@ -0,0 +1,334 @@ +import { useRef, useState } from 'react' +import { Editor } from '@tiptap/react' +import { ButtonGroup } from '../button-group/ButtonGroup' +import { Button } from '../button/Button' +import { + ArrowLeft, + ArrowRight, + BlockquoteRight, + Code, + CodeSquare, + Link, + ListOl, + ListUl, + TypeBold, + TypeH1, + TypeH2, + TypeH3, + TypeItalic, + TypeStrikethrough, + TypeUnderline +} from 'react-bootstrap-icons' +import { Modal } from '../modal/Modal' +import { Form } from '../form/Form' +import { Col } from '../grid/Col' +import { richTextEditorDefaultLocales, RichTextEditorLocales } from './defaultLocales' +import styles from './EditorActions.module.scss' + +const EDITOR_FORMATS = { + heading: 'heading', + bold: 'bold', + italic: 'italic', + underline: 'underline', + strike: 'strike', + code: 'code', + codeBlock: 'codeBlock', + link: 'link', + blockquote: 'blockquote', + bulletList: 'bulletList', + orderedList: 'orderedList' +} as const + +interface EditorActionsProps { + editor: Editor | null + disabled?: boolean + locales?: RichTextEditorLocales +} + +export const EditorActions = ({ editor, disabled, locales }: EditorActionsProps) => { + const [linkDialogOpen, setLinkDialogOpen] = useState(false) + const linkTextfieldRef = useRef(null) + + const handleOpenLinkDialog = () => setLinkDialogOpen(true) + const handleCloseLinkDialog = () => setLinkDialogOpen(false) + + const handleOKLinkDialog = () => { + const url = linkTextfieldRef.current?.value + + if (url) { + editor?.chain().focus().extendMarkRange(EDITOR_FORMATS.link).setLink({ href: url }).run() + } else { + editor?.chain().focus().extendMarkRange(EDITOR_FORMATS.link).unsetLink().run() + } + setLinkDialogOpen(false) + } + + const handleToggleH1 = () => editor?.chain().focus().toggleHeading({ level: 1 }).run() + const isActiveH1 = !disabled && editor?.isActive(EDITOR_FORMATS.heading, { level: 1 }) + + const handleToggleH2 = () => editor?.chain().focus().toggleHeading({ level: 2 }).run() + const isActiveH2 = !disabled && editor?.isActive(EDITOR_FORMATS.heading, { level: 2 }) + + const handleToggleH3 = () => editor?.chain().focus().toggleHeading({ level: 3 }).run() + const isActiveH3 = !disabled && editor?.isActive(EDITOR_FORMATS.heading, { level: 3 }) + + const handleToggleBold = () => editor?.chain().focus().toggleBold().run() + const isActiveBold = !disabled && editor?.isActive(EDITOR_FORMATS.bold) + + const handleToggleItalic = () => editor?.chain().focus().toggleItalic().run() + const isActiveItalic = !disabled && editor?.isActive(EDITOR_FORMATS.italic) + + const handleToggleUnderline = () => editor?.chain().focus().toggleUnderline().run() + const isActiveUnderline = !disabled && editor?.isActive(EDITOR_FORMATS.underline) + + const handleToggleStrike = () => editor?.chain().focus().toggleStrike().run() + const isActiveStrike = !disabled && editor?.isActive(EDITOR_FORMATS.strike) + + const handleToggleCode = () => editor?.chain().focus().toggleCode().run() + const isActiveCode = !disabled && editor?.isActive(EDITOR_FORMATS.code) + + const handleToogleCodeBlock = () => editor?.chain().focus().toggleCodeBlock().run() + const isActiveCodeBlock = !disabled && editor?.isActive(EDITOR_FORMATS.codeBlock) + + const handleToggleBlockquote = () => editor?.chain().focus().toggleBlockquote().run() + const isActiveBlockquote = !disabled && editor?.isActive(EDITOR_FORMATS.blockquote) + + const handleToggleBulletList = () => editor?.chain().focus().toggleBulletList().run() + const isActiveBulletList = !disabled && editor?.isActive(EDITOR_FORMATS.bulletList) + + const handleToggleOrderedList = () => editor?.chain().focus().toggleOrderedList().run() + const isActiveOrderedList = !disabled && editor?.isActive(EDITOR_FORMATS.orderedList) + + const handleUndo = () => editor?.chain().focus().undo().run() + const handleRedo = () => editor?.chain().focus().redo().run() + + return ( + <> +
+ {/* Headings */} + +
+ + {/* Dialog for pasting a url to the link */} + + + + {locales?.linkDialog?.title ?? richTextEditorDefaultLocales.linkDialog?.title} + + + + + + {locales?.linkDialog?.label ?? richTextEditorDefaultLocales.linkDialog?.label} + + + + + + + + + + + + + ) +} diff --git a/packages/design-system/src/lib/components/rich-text-editor/RichTextEditor.scss b/packages/design-system/src/lib/components/rich-text-editor/RichTextEditor.scss new file mode 100644 index 000000000..0031c1c3a --- /dev/null +++ b/packages/design-system/src/lib/components/rich-text-editor/RichTextEditor.scss @@ -0,0 +1,92 @@ +@import 'src/lib/assets/styles/design-tokens/colors.module'; + +.rich-text-editor-wrapper { + display: flex; + flex-direction: column; + padding-top: 0.5rem; + border: solid 1px $dv-border-color; + border-radius: 6px; +} + +.editor-content-wrapper { + display: flex; + flex: 1; + flex-direction: column; + + .rich-text-editor-content { + flex: 1; + min-height: 200px; + max-height: 350px; + margin: 0.5rem; + padding: 6px 12px; + overflow-y: auto; + border: solid 1px $dv-border-color; + border-radius: 4px; + resize: vertical; + + &[style*='height'] { + max-height: unset; + } + + &.ProseMirror-focused { + border-color: #99bddb; + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(51 122 183 / 25%); + } + + p.is-editor-empty:first-child::before { + float: left; + height: 0; + color: #adb5bd; + content: attr(data-placeholder); + pointer-events: none; + } + + // Styles for the editor html content + + blockquote { + padding: 6px 10px; + background-color: rgba(0 0 0 / 15%); + border-left: solid 5px gray; + border-radius: 4px; + + p { + margin-bottom: 0.5rem; + } + } + + pre { + padding: 1rem; + overflow: auto; + font-size: 85%; + line-height: 1.45; + word-wrap: normal; + background-color: rgba(0 0 0 / 10%); + border-radius: 6px; + } + + code { + margin: 0; + padding: 0.2em 0.4em; + font-size: 85%; + white-space: break-spaces; + background-color: rgba(0 0 0 / 10%); + border-radius: 0.375rem; + } + + pre code { + display: inline; + max-width: auto; + margin: 0; + padding: 0; + overflow: visible; + font-size: 100%; + line-height: inherit; + white-space: pre; + word-wrap: normal; + word-break: normal; + background-color: transparent; + border: 0; + } + } +} diff --git a/packages/design-system/src/lib/components/rich-text-editor/RichTextEditor.tsx b/packages/design-system/src/lib/components/rich-text-editor/RichTextEditor.tsx new file mode 100644 index 000000000..8ca454036 --- /dev/null +++ b/packages/design-system/src/lib/components/rich-text-editor/RichTextEditor.tsx @@ -0,0 +1,71 @@ +import { useEffect } from 'react' +import { useEditor, EditorContent } from '@tiptap/react' +import StarterKit from '@tiptap/starter-kit' +import Underline from '@tiptap/extension-underline' +import Link from '@tiptap/extension-link' +import CodeBlock from '@tiptap/extension-code-block' +import Placeholder from '@tiptap/extension-placeholder' +import { EditorActions } from './EditorActions' +import { richTextEditorDefaultLocales, RichTextEditorLocales } from './defaultLocales' +import './RichTextEditor.scss' + +export interface RichTextEditorProps { + initialValue?: string | undefined + onChange?: (value: string) => void + error?: string + disabled?: boolean + locales?: RichTextEditorLocales + editorContentId?: string + editorContentAriaLabelledBy?: string +} + +export const RichTextEditor = ({ + initialValue, + onChange, + disabled, + locales, + editorContentId, + editorContentAriaLabelledBy +}: RichTextEditorProps) => { + const editor = useEditor({ + extensions: [ + StarterKit.configure({ + heading: { + levels: [1, 2, 3] + } + }), + Underline, + Link.configure({ + openOnClick: false, + autolink: true, + linkOnPaste: true + }), + CodeBlock, + Placeholder.configure({ + placeholder: locales?.placeholder ?? richTextEditorDefaultLocales.placeholder + }) + ], + content: initialValue, + editorProps: { + attributes: { + class: 'rich-text-editor-content', + ...(editorContentId && { id: editorContentId }), + ...(editorContentAriaLabelledBy && { 'aria-labelledby': editorContentAriaLabelledBy }) + } + }, + onUpdate: ({ editor }) => onChange && onChange(editor.getHTML()) + }) + + useEffect(() => { + if (editor) editor.setEditable(disabled ? false : true, false) + }, [disabled, editor]) + + return ( +
+ +
+ +
+
+ ) +} diff --git a/packages/design-system/src/lib/components/rich-text-editor/defaultLocales.ts b/packages/design-system/src/lib/components/rich-text-editor/defaultLocales.ts new file mode 100644 index 000000000..1ae189487 --- /dev/null +++ b/packages/design-system/src/lib/components/rich-text-editor/defaultLocales.ts @@ -0,0 +1,19 @@ +export interface RichTextEditorLocales { + placeholder?: string + linkDialog?: { + title?: string + label?: string + ok?: string + cancel?: string + } +} + +export const richTextEditorDefaultLocales: RichTextEditorLocales = { + placeholder: 'Write something …', + linkDialog: { + title: 'Add link', + label: 'Link', + ok: 'OK', + cancel: 'Cancel' + } +} diff --git a/packages/design-system/src/lib/index.ts b/packages/design-system/src/lib/index.ts index 5d6de754d..414b248f4 100644 --- a/packages/design-system/src/lib/index.ts +++ b/packages/design-system/src/lib/index.ts @@ -32,3 +32,4 @@ export { Spinner } from './components/spinner/Spinner' export { TransferList, type TransferListItem } from './components/transfer-list/TransferList' export { CloseButton } from './components/close-button/CloseButton' export { Offcanvas } from './components/offcanvas/Offcanvas' +export { RichTextEditor } from './components/rich-text-editor/RichTextEditor' diff --git a/packages/design-system/src/lib/stories/rich-text-editor/RichTextEditor.stories.tsx b/packages/design-system/src/lib/stories/rich-text-editor/RichTextEditor.stories.tsx new file mode 100644 index 000000000..ede6960d2 --- /dev/null +++ b/packages/design-system/src/lib/stories/rich-text-editor/RichTextEditor.stories.tsx @@ -0,0 +1,78 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { RichTextEditor } from '../../components/rich-text-editor/RichTextEditor' +import { Form } from '../../components/form/Form' +import { Col } from '../../components/grid/Col' + +/** + * ## Description + * The rich text editor component is a user interface element that allows users to input and format text content using + * a variety of tools and options. + + * ## Usage guidelines + * The rich text editor component should be used when there is a need to allow users to input and format text content in + * a more flexible and customizable way. + * + * You can keep track of the content changes by using the `onChange` prop. It will return the HTML content of the editor. + * + * You can also set an initial value using the `initialValue` prop. + * + * You can disable the editor by using the `disabled` prop. + * + * You can customize the locales of the editor by using the `locales` prop. + */ + +const meta: Meta = { + title: 'Rich Text Editor', + component: RichTextEditor, + tags: ['autodocs'] +} + +export default meta +type Story = StoryObj + +const handleChange = (value: string) => { + console.log({ value }) +} + +export const Default: Story = { + render: () => +} + +export const WithInitialValue: Story = { + render: () => ( + + ) +} + +export const WithNotDefaultLocales: Story = { + render: () => ( + + ) +} + +export const Disabled: Story = { + render: () => +} + +export const WithLabel: Story = { + render: () => ( + + Dataset Description + + + + + ) +} diff --git a/packages/design-system/tests/component/rich-text-editor/RichTextEditor.spec.tsx b/packages/design-system/tests/component/rich-text-editor/RichTextEditor.spec.tsx new file mode 100644 index 000000000..ca6732030 --- /dev/null +++ b/packages/design-system/tests/component/rich-text-editor/RichTextEditor.spec.tsx @@ -0,0 +1,312 @@ +import { RichTextEditor } from '../../../src/lib/components/rich-text-editor/RichTextEditor' + +const editorContentId = 'test-editor-content-id' + +const textToType = 'Hello Dataverse!' + +describe('RichTextEditor', () => { + it('should render the component', () => { + cy.mount() + + cy.findByTestId('rich-text-editor-wrapper').should('exist').should('be.visible') + }) + + it('should render the component with an id in the editor content', () => { + cy.mount() + + cy.get(`#${editorContentId}`).should('exist') + }) + + it('should render the component with an aria-labelledby attribute in the editor content', () => { + const ariaLabelledBy = 'test-aria-labelledby' + + cy.mount( + + ) + + cy.get(`#${editorContentId}`).should('have.attr', 'aria-labelledby', ariaLabelledBy) + }) + + it('should render the component with initial value', () => { + const initialValue = '

Hello Dataverse!

' + + cy.mount() + + cy.get(`#${editorContentId}`).should('contain.html', initialValue) + }) + + it('should render the component with disabled state', () => { + cy.mount() + + cy.get(`#${editorContentId}`).should('have.attr', 'contenteditable', 'false') + }) + + it('should call onChange when the content changes', () => { + const onChange = cy.spy().as('onChange') + + cy.mount() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.get('@onChange').should('have.been.calledWith', `

${textToType}

`) + }) + + describe('Editor Actions', () => { + it('should apply the heading 1', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.findByLabelText('Heading 1').click() + + cy.get(`#${editorContentId}`).should('contain.html', `

${textToType}

`) + + cy.findByLabelText('Heading 1').should('have.attr', 'aria-pressed', 'true') + }) + + it('should apply the heading 2', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.findByLabelText('Heading 2').click() + + cy.get(`#${editorContentId}`).should('contain.html', `

${textToType}

`) + + cy.findByLabelText('Heading 2').should('have.attr', 'aria-pressed', 'true') + }) + + it('should apply the heading 3', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.findByLabelText('Heading 3').click() + + cy.get(`#${editorContentId}`).should('contain.html', `

${textToType}

`) + + cy.findByLabelText('Heading 3').should('have.attr', 'aria-pressed', 'true') + }) + + it('should apply the Bold', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.get(`#${editorContentId}`).type('{selectall}') + + cy.findByLabelText('Bold').click() + + cy.get(`#${editorContentId}`).should('contain.html', `

${textToType}

`) + + cy.findByLabelText('Bold').should('have.attr', 'aria-pressed', 'true') + }) + + it('should apply the Italic', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.get(`#${editorContentId}`).type('{selectall}') + + cy.findByLabelText('Italic').click() + + cy.get(`#${editorContentId}`).should('contain.html', `

${textToType}

`) + + cy.findByLabelText('Italic').should('have.attr', 'aria-pressed', 'true') + }) + + it('should apply the Underline', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.get(`#${editorContentId}`).type('{selectall}') + + cy.findByLabelText('Underline').click() + + cy.get(`#${editorContentId}`).should('contain.html', `

${textToType}

`) + + cy.findByLabelText('Underline').should('have.attr', 'aria-pressed', 'true') + }) + + it('should apply the Striketrough', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.get(`#${editorContentId}`).type('{selectall}') + + cy.findByLabelText('Strikethrough').click() + + cy.get(`#${editorContentId}`).should('contain.html', `

${textToType}

`) + + cy.findByLabelText('Strikethrough').should('have.attr', 'aria-pressed', 'true') + }) + + it('should apply the Unordered list', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.get(`#${editorContentId}`).type('{selectall}') + + cy.findByLabelText('Unordered list').click() + + cy.get(`#${editorContentId}`).should('contain.html', `
  • ${textToType}

`) + + cy.findByLabelText('Unordered list').should('have.attr', 'aria-pressed', 'true') + }) + + it('should apply the Ordered list', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.get(`#${editorContentId}`).type('{selectall}') + + cy.findByLabelText('Ordered list').click() + + cy.get(`#${editorContentId}`).should('contain.html', `
  1. ${textToType}

`) + + cy.findByLabelText('Ordered list').should('have.attr', 'aria-pressed', 'true') + }) + + it('should apply the Code', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.get(`#${editorContentId}`).type('{selectall}') + + cy.findByLabelText('Code').click() + + cy.get(`#${editorContentId}`).should('contain.html', `

${textToType}

`) + + cy.findByLabelText('Code').should('have.attr', 'aria-pressed', 'true') + }) + + it('should apply the Code block', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.get(`#${editorContentId}`).type('{selectall}') + + cy.findByLabelText('Code block').click() + + cy.get(`#${editorContentId}`).should('contain.html', `
${textToType}
`) + + cy.findByLabelText('Code block').should('have.attr', 'aria-pressed', 'true') + }) + + it('should apply the blockquote', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.get(`#${editorContentId}`).type('{selectall}') + + cy.findByLabelText('Blockquote').click() + + cy.get(`#${editorContentId}`).should( + 'contain.html', + `

${textToType}

` + ) + + cy.findByLabelText('Blockquote').should('have.attr', 'aria-pressed', 'true') + }) + + describe('Link functionalities', () => { + it('should apply the link', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type('Here goes a link') + + cy.get(`#${editorContentId}`).type('{selectall}') + + cy.findByLabelText('Add link').click() + + cy.findByLabelText('Link').type('https://www.dataverse.com') + + cy.findByRole('button', { name: 'OK' }).click() + + cy.get(`#${editorContentId}`).should( + 'contain.html', + '

Here goes a link

' + ) + }) + + it('close the link dialog', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type('Here goes a link') + + cy.get(`#${editorContentId}`).type('{selectall}') + + cy.findByLabelText('Add link').click() + + cy.findByRole('button', { name: 'Cancel' }).click() + + cy.findByLabelText('Link').should('not.exist') + }) + + it('should not apply the link when the URL is empty', () => { + cy.mount() + + cy.get(`#${editorContentId}`).type('Here goes a link') + + cy.get(`#${editorContentId}`).type('{selectall}') + + cy.findByLabelText('Add link').click() + + cy.findByRole('button', { name: 'OK' }).click() + + cy.get(`#${editorContentId}`).should('contain.html', '

Here goes a link

') + }) + }) + + describe('Undo and Redo functionalities', () => { + it('should undo the last action', () => { + cy.mount() + + cy.clock() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.tick(500) + + cy.get(`#${editorContentId}`).type(' added text') + + cy.findByLabelText('Undo').click() + + cy.get(`#${editorContentId}`).should('contain.html', `

${textToType}

`) + + cy.clock().then((clock) => clock.restore()) + }) + + it('should redo the last action', () => { + cy.mount() + + cy.clock() + + cy.get(`#${editorContentId}`).type(textToType) + + cy.tick(500) + + cy.get(`#${editorContentId}`).type(' added text') + + cy.findByLabelText('Undo').click() + + cy.findByLabelText('Redo').click() + + cy.get(`#${editorContentId}`).should('contain.html', `

${textToType} added text

`) + + cy.clock().then((clock) => clock.restore()) + }) + }) + }) +})