mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-02-24 04:00:59 +01:00
editor: add export as csv to table
This commit is contained in:
66
packages/editor/package-lock.json
generated
66
packages/editor/package-lock.json
generated
@@ -35,8 +35,10 @@
|
||||
"@tiptap/pm": "^2.0.0-beta.218",
|
||||
"@tiptap/starter-kit": "^2.0.0-beta.218",
|
||||
"detect-indent": "^7.0.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"katex": "^0.16.2",
|
||||
"nanoid": "^4.0.1",
|
||||
"papaparse": "^5.4.0",
|
||||
"prism-themes": "^1.9.0",
|
||||
"prosemirror-codemark": "^0.4.1",
|
||||
"re-resizable": "^6.9.9",
|
||||
@@ -52,7 +54,9 @@
|
||||
"devDependencies": {
|
||||
"@mdi/js": "^6.9.96",
|
||||
"@mdi/react": "^1.6.0",
|
||||
"@types/file-saver": "^2.0.5",
|
||||
"@types/katex": "^0.14.0",
|
||||
"@types/papaparse": "^5.3.7",
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"@types/react": "17.0.2",
|
||||
"@types/react-color": "^3.0.6",
|
||||
@@ -160,9 +164,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.21.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz",
|
||||
"integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==",
|
||||
"version": "7.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz",
|
||||
"integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.19.4",
|
||||
"@babel/helper-validator-identifier": "^7.19.1",
|
||||
@@ -1492,6 +1496,12 @@
|
||||
"@types/chai": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/file-saver": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz",
|
||||
"integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/fs-extra": {
|
||||
"version": "9.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
|
||||
@@ -1537,6 +1547,15 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/object.pick/-/object.pick-1.3.2.tgz",
|
||||
"integrity": "sha512-sn7L+qQ6RLPdXRoiaE7bZ/Ek+o4uICma/lBFPyJEKDTPTBP1W8u0c4baj3EiS4DiqLs+Hk+KUGvMVJtAw3ePJg=="
|
||||
},
|
||||
"node_modules/@types/papaparse": {
|
||||
"version": "5.3.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.7.tgz",
|
||||
"integrity": "sha512-f2HKmlnPdCvS0WI33WtCs5GD7X1cxzzS/aduaxSu3I7TbhWlENjSPs6z5TaB9K0J+BH1jbmqTaM+ja5puis4wg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||
@@ -2312,6 +2331,11 @@
|
||||
"node": "^12.20 || >= 14.13"
|
||||
}
|
||||
},
|
||||
"node_modules/file-saver": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
|
||||
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
@@ -3047,6 +3071,11 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/papaparse": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.0.tgz",
|
||||
"integrity": "sha512-ZBQABWG09p+u8rFoJVl/GhgxZ5zy9Zh1Lu/LVc7VX5T4nljjC14/YTcpebYwqP218B9X307eBOP7Tuhoqv7v7w=="
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@@ -4504,9 +4533,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.21.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz",
|
||||
"integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==",
|
||||
"version": "7.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz",
|
||||
"integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==",
|
||||
"requires": {
|
||||
"@babel/helper-string-parser": "^7.19.4",
|
||||
"@babel/helper-validator-identifier": "^7.19.1",
|
||||
@@ -5329,6 +5358,12 @@
|
||||
"@types/chai": "*"
|
||||
}
|
||||
},
|
||||
"@types/file-saver": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz",
|
||||
"integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/fs-extra": {
|
||||
"version": "9.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
|
||||
@@ -5374,6 +5409,15 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/object.pick/-/object.pick-1.3.2.tgz",
|
||||
"integrity": "sha512-sn7L+qQ6RLPdXRoiaE7bZ/Ek+o4uICma/lBFPyJEKDTPTBP1W8u0c4baj3EiS4DiqLs+Hk+KUGvMVJtAw3ePJg=="
|
||||
},
|
||||
"@types/papaparse": {
|
||||
"version": "5.3.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.7.tgz",
|
||||
"integrity": "sha512-f2HKmlnPdCvS0WI33WtCs5GD7X1cxzzS/aduaxSu3I7TbhWlENjSPs6z5TaB9K0J+BH1jbmqTaM+ja5puis4wg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||
@@ -5993,6 +6037,11 @@
|
||||
"web-streams-polyfill": "^3.0.3"
|
||||
}
|
||||
},
|
||||
"file-saver": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
|
||||
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
@@ -6536,6 +6585,11 @@
|
||||
"yocto-queue": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"papaparse": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.0.tgz",
|
||||
"integrity": "sha512-ZBQABWG09p+u8rFoJVl/GhgxZ5zy9Zh1Lu/LVc7VX5T4nljjC14/YTcpebYwqP218B9X307eBOP7Tuhoqv7v7w=="
|
||||
},
|
||||
"parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
|
||||
@@ -31,8 +31,10 @@
|
||||
"@tiptap/pm": "^2.0.0-beta.218",
|
||||
"@tiptap/starter-kit": "^2.0.0-beta.218",
|
||||
"detect-indent": "^7.0.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"katex": "^0.16.2",
|
||||
"nanoid": "^4.0.1",
|
||||
"papaparse": "^5.4.0",
|
||||
"prism-themes": "^1.9.0",
|
||||
"prosemirror-codemark": "^0.4.1",
|
||||
"re-resizable": "^6.9.9",
|
||||
@@ -48,7 +50,9 @@
|
||||
"devDependencies": {
|
||||
"@mdi/js": "^6.9.96",
|
||||
"@mdi/react": "^1.6.0",
|
||||
"@types/file-saver": "^2.0.5",
|
||||
"@types/katex": "^0.14.0",
|
||||
"@types/papaparse": "^5.3.7",
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"@types/react": "17.0.2",
|
||||
"@types/react-color": "^3.0.6",
|
||||
|
||||
@@ -21,13 +21,17 @@ import { Editor } from "@tiptap/core";
|
||||
import { selectedRect, TableRect } from "@tiptap/pm/tables";
|
||||
import { Transaction } from "prosemirror-state";
|
||||
import { Node } from "prosemirror-model";
|
||||
import { unparse } from "papaparse";
|
||||
import { saveAs } from "file-saver";
|
||||
|
||||
type TableCell = {
|
||||
cell: Node;
|
||||
pos: number;
|
||||
};
|
||||
|
||||
function moveColumnRight(editor: Editor) {
|
||||
function moveColumnRight(editor?: Editor) {
|
||||
if (!editor) return;
|
||||
|
||||
const { tr } = editor.state;
|
||||
const rect = selectedRect(editor.state);
|
||||
if (rect.right === rect.map.width) return;
|
||||
@@ -38,7 +42,9 @@ function moveColumnRight(editor: Editor) {
|
||||
editor.view.dispatch(transaction);
|
||||
}
|
||||
|
||||
function moveColumnLeft(editor: Editor) {
|
||||
function moveColumnLeft(editor?: Editor) {
|
||||
if (!editor) return;
|
||||
|
||||
const { tr } = editor.state;
|
||||
const rect = selectedRect(editor.state);
|
||||
if (rect.left === 0) return;
|
||||
@@ -49,7 +55,9 @@ function moveColumnLeft(editor: Editor) {
|
||||
editor.view.dispatch(transaction);
|
||||
}
|
||||
|
||||
function moveRowDown(editor: Editor) {
|
||||
function moveRowDown(editor?: Editor) {
|
||||
if (!editor) return;
|
||||
|
||||
const { tr } = editor.state;
|
||||
const rect = selectedRect(editor.state);
|
||||
if (rect.top + 1 === rect.map.height) return;
|
||||
@@ -60,7 +68,9 @@ function moveRowDown(editor: Editor) {
|
||||
editor.view.dispatch(transaction);
|
||||
}
|
||||
|
||||
function moveRowUp(editor: Editor) {
|
||||
function moveRowUp(editor?: Editor) {
|
||||
if (!editor) return;
|
||||
|
||||
const { tr } = editor.state;
|
||||
const rect = selectedRect(editor.state);
|
||||
if (rect.top === 0) return;
|
||||
@@ -159,4 +169,19 @@ function moveCells(
|
||||
return tr;
|
||||
}
|
||||
|
||||
export { moveColumnLeft, moveColumnRight, moveRowDown, moveRowUp };
|
||||
function exportToCSV(editor?: Editor) {
|
||||
if (!editor) return;
|
||||
|
||||
const rect = selectedRect(editor.state);
|
||||
|
||||
const rows: string[][] = [];
|
||||
rect.table.forEach((node) => {
|
||||
const row: string[] = [];
|
||||
node.forEach((cell) => row.push(cell.textContent));
|
||||
rows.push(row);
|
||||
});
|
||||
|
||||
saveAs(new Blob([new TextEncoder().encode(unparse(rows))]), "table.csv");
|
||||
}
|
||||
|
||||
export { moveColumnLeft, moveColumnRight, moveRowDown, moveRowUp, exportToCSV };
|
||||
|
||||
@@ -17,18 +17,17 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { UnionCommands } from "@tiptap/core";
|
||||
import { useEffect } from "react";
|
||||
import { PermissionRequestEvent } from "../types";
|
||||
import { PermissionRequestEvent, Commands } from "../types";
|
||||
|
||||
export type Claims = "premium";
|
||||
export type PermissionHandlerOptions = {
|
||||
claims: Record<Claims, boolean>;
|
||||
onPermissionDenied: (claim: Claims, id: keyof UnionCommands) => void;
|
||||
onPermissionDenied: (claim: Claims, id: Commands) => void;
|
||||
};
|
||||
|
||||
const ClaimsMap: Record<Claims, (keyof UnionCommands)[]> = {
|
||||
premium: ["insertImage"]
|
||||
const ClaimsMap: Record<Claims, Commands[]> = {
|
||||
premium: ["insertImage", "exportToCSV"]
|
||||
};
|
||||
|
||||
export function usePermissionHandler(options: PermissionHandlerOptions) {
|
||||
|
||||
@@ -224,6 +224,7 @@ export const Icons = {
|
||||
heading: mdiFormatHeaderPound,
|
||||
indent: mdiFormatIndentIncrease,
|
||||
outdent: mdiFormatIndentDecrease,
|
||||
csv: "m22.28572 6-1.714289 11.142849L18.857151 6h-1.714302l2.156582 12h2.544L24 6Zm-8.57144 12H8.571428v-1.71428h5.142852v-3.428577h-3.42856a1.716 1.716 0 0 1-1.714292-1.714286V7.7142849A1.716 1.716 0 0 1 10.28572 6h5.142849v1.7142849H10.28572v3.4285721h3.42856a1.716 1.716 0 0 1 1.714289 1.714286v3.428577A1.716 1.716 0 0 1 13.71428 18Zm-6.8571369 0H1.7142849A1.716 1.716 0 0 1 0 16.28572V7.7142849A1.716 1.716 0 0 1 1.7142849 6h5.1428582v1.7142849H1.7142849V16.28572h5.1428582z",
|
||||
|
||||
plus: mdiPlus,
|
||||
minus: mdiMinus,
|
||||
|
||||
@@ -217,6 +217,11 @@ const tools: Record<ToolId, ToolDefinition> = {
|
||||
title: "Delete table",
|
||||
conditional: true
|
||||
},
|
||||
exportToCSV: {
|
||||
icon: "csv",
|
||||
title: "Export to CSV",
|
||||
conditional: true
|
||||
},
|
||||
cellBackgroundColor: {
|
||||
icon: "backgroundColor",
|
||||
title: "Cell background color",
|
||||
|
||||
@@ -59,7 +59,8 @@ import {
|
||||
CellBackgroundColor,
|
||||
CellBorderColor,
|
||||
CellTextColor,
|
||||
CellBorderWidth
|
||||
CellBorderWidth,
|
||||
ExportToCSV
|
||||
} from "./table";
|
||||
import {
|
||||
ImageSettings,
|
||||
@@ -160,6 +161,7 @@ const tools = {
|
||||
moveRowDown: MoveRowDown,
|
||||
deleteRow: DeleteRow,
|
||||
deleteTable: DeleteTable,
|
||||
exportToCSV: ExportToCSV,
|
||||
|
||||
outdent: Outdent,
|
||||
indent: Indent,
|
||||
|
||||
@@ -27,7 +27,8 @@ import {
|
||||
moveColumnLeft as moveColumnLeftAction,
|
||||
moveColumnRight as moveColumnRightAction,
|
||||
moveRowDown as moveRowDownAction,
|
||||
moveRowUp as moveRowUpAction
|
||||
moveRowUp as moveRowUpAction,
|
||||
exportToCSV as exportToCSVAction
|
||||
} from "../../extensions/table/actions";
|
||||
import { MoreTools } from "../components/more-tools";
|
||||
import { menuButtonToTool } from "./utils";
|
||||
@@ -166,6 +167,7 @@ export function TableProperties(props: ToolProps) {
|
||||
splitCells(editor),
|
||||
cellProperties(editor),
|
||||
{ type: "separator", key: "tableSeperator" },
|
||||
exportToCSV(editor),
|
||||
deleteTable(editor)
|
||||
],
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -334,14 +336,14 @@ const moveColumnLeft = (editor: Editor): MenuButton => ({
|
||||
...getToolDefinition("moveColumnLeft"),
|
||||
key: "moveColumnLeft",
|
||||
type: "button",
|
||||
onClick: () => moveColumnLeftAction(editor)
|
||||
onClick: () => moveColumnLeftAction(editor.current)
|
||||
});
|
||||
|
||||
const moveColumnRight = (editor: Editor): MenuButton => ({
|
||||
...getToolDefinition("moveColumnRight"),
|
||||
key: "moveColumnRight",
|
||||
type: "button",
|
||||
onClick: () => moveColumnRightAction(editor)
|
||||
onClick: () => moveColumnRightAction(editor.current)
|
||||
});
|
||||
|
||||
const deleteColumn = (editor: Editor): MenuButton => ({
|
||||
@@ -383,13 +385,13 @@ const moveRowUp = (editor: Editor): MenuButton => ({
|
||||
...getToolDefinition("moveRowUp"),
|
||||
key: "moveRowUp",
|
||||
type: "button",
|
||||
onClick: () => moveRowUpAction(editor)
|
||||
onClick: () => moveRowUpAction(editor.current)
|
||||
});
|
||||
const moveRowDown = (editor: Editor): MenuButton => ({
|
||||
...getToolDefinition("moveRowDown"),
|
||||
key: "moveRowDown",
|
||||
type: "button",
|
||||
onClick: () => moveRowDownAction(editor)
|
||||
onClick: () => moveRowDownAction(editor.current)
|
||||
});
|
||||
|
||||
const deleteRow = (editor: Editor): MenuButton => ({
|
||||
@@ -406,6 +408,13 @@ const deleteTable = (editor: Editor): MenuButton => ({
|
||||
onClick: () => editor.current?.chain().focus().deleteTable().run()
|
||||
});
|
||||
|
||||
const exportToCSV = (editor: Editor): MenuButton => ({
|
||||
...getToolDefinition("exportToCSV"),
|
||||
key: "exportToCSV",
|
||||
type: "button",
|
||||
onClick: () => exportToCSVAction(editor.requestPermission("exportToCSV"))
|
||||
});
|
||||
|
||||
const cellProperties = (editor: Editor): MenuButton => ({
|
||||
...getToolDefinition("cellProperties"),
|
||||
key: "cellProperties",
|
||||
@@ -430,3 +439,4 @@ export const MoveRowUp = menuButtonToTool(moveRowUp);
|
||||
export const MoveRowDown = menuButtonToTool(moveRowDown);
|
||||
export const DeleteRow = menuButtonToTool(deleteRow);
|
||||
export const DeleteTable = menuButtonToTool(deleteTable);
|
||||
export const ExportToCSV = menuButtonToTool(exportToCSV);
|
||||
|
||||
@@ -19,7 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { UnionCommands, Editor as TiptapEditor } from "@tiptap/core";
|
||||
|
||||
export type PermissionRequestEvent = CustomEvent<{ id: keyof UnionCommands }>;
|
||||
export type Commands = keyof UnionCommands | "exportToCSV";
|
||||
export type PermissionRequestEvent = CustomEvent<{ id: Commands }>;
|
||||
|
||||
export class Editor extends TiptapEditor {
|
||||
/**
|
||||
@@ -35,7 +36,7 @@ export class Editor extends TiptapEditor {
|
||||
* @param id the command id to get permission for
|
||||
* @returns latest editor instance
|
||||
*/
|
||||
requestPermission(id: keyof UnionCommands): TiptapEditor | undefined {
|
||||
requestPermission(id: Commands): TiptapEditor | undefined {
|
||||
const event = new CustomEvent("permissionrequest", {
|
||||
detail: { id },
|
||||
cancelable: true
|
||||
|
||||
Reference in New Issue
Block a user