mirror of
https://github.com/docmost/docmost.git
synced 2025-11-15 12:51:08 +10:00
Compare commits
7 Commits
feat/new-i
...
upgrade/ex
| Author | SHA1 | Date | |
|---|---|---|---|
| 527a6a650f | |||
| ef261565aa | |||
| 698cef55ea | |||
| 50b3f9ddd9 | |||
| 0029f84d50 | |||
| 6d024fc3de | |||
| 30dabd9fc1 |
@ -15,7 +15,7 @@
|
||||
"@docmost/editor-ext": "workspace:*",
|
||||
"@emoji-mart/data": "^1.2.1",
|
||||
"@emoji-mart/react": "^1.1.1",
|
||||
"@excalidraw/excalidraw": "^0.17.6",
|
||||
"@excalidraw/excalidraw": "0.18.0-864353b",
|
||||
"@mantine/core": "^7.17.0",
|
||||
"@mantine/form": "^7.17.0",
|
||||
"@mantine/hooks": "^7.17.0",
|
||||
@ -42,7 +42,7 @@
|
||||
"mitt": "^3.0.1",
|
||||
"react": "^18.3.1",
|
||||
"react-arborist": "3.4.0",
|
||||
"react-clear-modal": "^2.0.11",
|
||||
"react-clear-modal": "^2.0.15",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-drawio": "^1.0.1",
|
||||
"react-error-boundary": "^4.1.2",
|
||||
@ -77,6 +77,6 @@
|
||||
"prettier": "^3.4.1",
|
||||
"typescript": "^5.7.2",
|
||||
"typescript-eslint": "^8.17.0",
|
||||
"vite": "^6.3.2"
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,10 @@ import { useForm, zodResolver } from "@mantine/form";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import i18n from "i18next";
|
||||
import { getEmbedProviderById, getEmbedUrlAndProvider } from '@docmost/editor-ext';
|
||||
import {
|
||||
getEmbedProviderById,
|
||||
getEmbedUrlAndProvider,
|
||||
} from "@docmost/editor-ext";
|
||||
|
||||
const schema = z.object({
|
||||
url: z
|
||||
@ -49,6 +52,10 @@ export default function EmbedView(props: NodeViewProps) {
|
||||
async function onSubmit(data: { url: string }) {
|
||||
if (provider) {
|
||||
const embedProvider = getEmbedProviderById(provider);
|
||||
if (embedProvider.id === "iframe") {
|
||||
updateAttributes({ src: data.url });
|
||||
return;
|
||||
}
|
||||
if (embedProvider.regex.test(data.url)) {
|
||||
updateAttributes({ src: data.url });
|
||||
} else {
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
type LibraryItems = any;
|
||||
|
||||
type LibraryPersistedData = {
|
||||
libraryItems: LibraryItems;
|
||||
};
|
||||
|
||||
export interface LibraryPersistenceAdapter {
|
||||
load(metadata: { source: "load" | "save" }):
|
||||
| Promise<{ libraryItems: LibraryItems } | null>
|
||||
| {
|
||||
libraryItems: LibraryItems;
|
||||
}
|
||||
| null;
|
||||
|
||||
save(libraryData: LibraryPersistedData): Promise<void> | void;
|
||||
}
|
||||
|
||||
const LOCAL_STORAGE_KEY = "excalidrawLibrary";
|
||||
|
||||
export const localStorageLibraryAdapter: LibraryPersistenceAdapter = {
|
||||
async load() {
|
||||
try {
|
||||
const data = localStorage.getItem(LOCAL_STORAGE_KEY);
|
||||
if (data) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error downloading Excalidraw library from localStorage", e);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
async save(libraryData) {
|
||||
try {
|
||||
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(libraryData));
|
||||
} catch (e) {
|
||||
console.error(
|
||||
"Error while saving library from Excalidraw to localStorage",
|
||||
e,
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -13,7 +13,8 @@ import { uploadFile } from "@/features/page/services/page-service.ts";
|
||||
import { svgStringToFile } from "@/lib";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { getFileUrl } from "@/lib/config.ts";
|
||||
import { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types/types";
|
||||
import "@excalidraw/excalidraw/index.css";
|
||||
import type { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types";
|
||||
import { IAttachment } from "@/lib/types";
|
||||
import ReactClearModal from "react-clear-modal";
|
||||
import clsx from "clsx";
|
||||
@ -21,6 +22,8 @@ import { IconEdit } from "@tabler/icons-react";
|
||||
import { lazy } from "react";
|
||||
import { Suspense } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useHandleLibrary } from "@excalidraw/excalidraw";
|
||||
import { localStorageLibraryAdapter } from "@/features/editor/components/excalidraw/excalidraw-utils.ts";
|
||||
|
||||
const Excalidraw = lazy(() =>
|
||||
import("@excalidraw/excalidraw").then((module) => ({
|
||||
@ -35,6 +38,10 @@ export default function ExcalidrawView(props: NodeViewProps) {
|
||||
|
||||
const [excalidrawAPI, setExcalidrawAPI] =
|
||||
useState<ExcalidrawImperativeAPI>(null);
|
||||
useHandleLibrary({
|
||||
excalidrawAPI,
|
||||
adapter: localStorageLibraryAdapter,
|
||||
});
|
||||
const [excalidrawData, setExcalidrawData] = useState<any>(null);
|
||||
const [opened, { open, close }] = useDisclosure(false);
|
||||
const computedColorScheme = useComputedColorScheme();
|
||||
|
||||
@ -17,8 +17,8 @@ import {
|
||||
IconTable,
|
||||
IconTypography,
|
||||
IconMenu4,
|
||||
IconCalendar,
|
||||
} from "@tabler/icons-react";
|
||||
IconCalendar, IconAppWindow,
|
||||
} from '@tabler/icons-react';
|
||||
import {
|
||||
CommandProps,
|
||||
SlashMenuGroupedItemsType,
|
||||
@ -357,6 +357,20 @@ const CommandGroups: SlashMenuGroupedItemsType = {
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Iframe embed",
|
||||
description: "Embed any Iframe",
|
||||
searchTerms: ["iframe"],
|
||||
icon: IconAppWindow,
|
||||
command: ({ editor, range }: CommandProps) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange(range)
|
||||
.setEmbed({ provider: "iframe" })
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Airtable",
|
||||
description: "Embed Airtable",
|
||||
|
||||
@ -17,9 +17,9 @@ import {
|
||||
IconColumnRemove,
|
||||
IconRowInsertBottom,
|
||||
IconRowInsertTop,
|
||||
IconRowRemove,
|
||||
IconRowRemove, IconTableColumn, IconTableRow,
|
||||
IconTrashX,
|
||||
} from "@tabler/icons-react";
|
||||
} from '@tabler/icons-react';
|
||||
import { isCellSelection } from "@docmost/editor-ext";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@ -50,6 +50,14 @@ export const TableMenu = React.memo(
|
||||
return posToDOMRect(editor.view, selection.from, selection.to);
|
||||
}, [editor]);
|
||||
|
||||
const toggleHeaderColumn = useCallback(() => {
|
||||
editor.chain().focus().toggleHeaderColumn().run();
|
||||
}, [editor]);
|
||||
|
||||
const toggleHeaderRow = useCallback(() => {
|
||||
editor.chain().focus().toggleHeaderRow().run();
|
||||
}, [editor]);
|
||||
|
||||
const addColumnLeft = useCallback(() => {
|
||||
editor.chain().focus().addColumnBefore().run();
|
||||
}, [editor]);
|
||||
@ -180,6 +188,30 @@ export const TableMenu = React.memo(
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip position="top" label={t("Toggle header row")}
|
||||
>
|
||||
<ActionIcon
|
||||
onClick={toggleHeaderRow}
|
||||
variant="default"
|
||||
size="lg"
|
||||
aria-label={t("Toggle header row")}
|
||||
>
|
||||
<IconTableRow size={18} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip position="top" label={t("Toggle header column")}
|
||||
>
|
||||
<ActionIcon
|
||||
onClick={toggleHeaderColumn}
|
||||
variant="default"
|
||||
size="lg"
|
||||
aria-label={t("Toggle header column")}
|
||||
>
|
||||
<IconTableColumn size={18} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip position="top" label={t("Delete table")}>
|
||||
<ActionIcon
|
||||
onClick={deleteTable}
|
||||
|
||||
@ -104,6 +104,14 @@ export const embedProviders: IEmbedProvider[] = [
|
||||
return url;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "iframe",
|
||||
name: "Iframe",
|
||||
regex: /any-iframe/,
|
||||
getEmbedUrl: (match, url) => {
|
||||
return url;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export function getEmbedProviderById(id: string) {
|
||||
|
||||
1220
pnpm-lock.yaml
generated
1220
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user