diff --git a/package-lock.json b/package-lock.json index 11a6ece4f1..2a71ba0847 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5429,6 +5429,36 @@ } } }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.0.4.tgz", + "integrity": "sha512-egZfYY/+wRNCflXNHx+dePvnz9FbmssDTJBtgRfDY7e8SE5oIo3Py2eCB1ckAbh1Q7cQ/6yJZThJ++sgbxibog==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-use-controllable-state": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-toggle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz", @@ -23119,6 +23149,7 @@ "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-toggle": "^1.0.3", "@radix-ui/react-tooltip": "^1.0.7", "class-variance-authority": "^0.7.0", diff --git a/packages/react/src/ariakit/ariakit.css b/packages/react/src/ariakit/ariakit.css index 87d014744c..61493d7e06 100644 --- a/packages/react/src/ariakit/ariakit.css +++ b/packages/react/src/ariakit/ariakit.css @@ -182,6 +182,128 @@ color: hsl(204 20% 100%); } +.input { + height: 2.5rem; + width: 100%; + border-radius: 0.375rem; + border-style: none; + background-color: hsl(204 20% 94% / 0.4); + padding-left: 1rem; + padding-right: 1rem; + font-size: 1rem; + line-height: 1.5rem; + color: hsl(204 4% 0%); + box-shadow: + inset 0 0 0 1px rgba(0 0 0 / 0.1), + inset 0 2px 5px 0 rgba(0 0 0 / 0.05); +} + +.input::placeholder { + color: hsl(204 4% 0% / 0.6); +} + +.input:hover { + background-color: hsl(204 20% 94%); +} + +.input:focus-visible, +.input[data-focus-visible] { + outline: 2px solid hsl(204 100% 40%); + outline-offset: -1px; +} + +:is(.dark .input) { + background-color: hsl(204 4% 10%); + color: hsl(204 20% 100%); + box-shadow: + inset 0 0 0 1px rgba(255 255 255 / 0.12), + inset 0 -1px 0 0 rgba(255 255 255 / 0.05), + inset 0 2px 5px 0 rgba(0 0 0 / 0.15); +} + +:is(.dark .input)::placeholder { + color: hsl(204 20% 100% / 46%); +} + +:is(.dark .input:hover) { + background-color: hsl(204 4% 8%); +} + +:is(.dark .bn-image-panel) { + background-color: hsl(204 4% 16%); + box-shadow: + 0 1px 3px 0 rgb(0 0 0 / 0.25), + 0 1px 2px -1px rgb(0 0 0 / 0.1); +} + +.tab-list { + display: flex; + gap: 0.5rem; +} + +.tab { + display: flex; + height: 2.5rem; + user-select: none; + align-items: center; + justify-content: center; + gap: 0.5rem; + white-space: nowrap; + border-radius: 0.25rem; + border-style: none; + padding-left: 1rem; + padding-right: 1rem; + font-size: 1rem; + line-height: 1.5rem; + text-decoration-line: none; + outline-width: 2px; + outline-offset: 2px; + outline-color: hsl(204 100% 40%); +} + +.tab:hover { + background-color: hsl(204 4% 0% / 7.5%); +} + +.tab[aria-disabled="true"] { + opacity: 0.5; +} + +.tab[aria-selected="true"] { + background-color: hsl(204 100% 40%); + color: hsl(204 20% 100%); +} + +.tab:hover[aria-selected="true"] { + background-color: hsl(204 100% 32%); +} + +.tab[data-focus-visible] { + outline-style: solid; +} + +.tab:active, +.tab[data-active] { + padding-top: 0.125rem; +} + +:is(.dark .tab:hover) { + background-color: hsl(204 20% 100% / 0.1); +} + +:is(.dark .tab[aria-selected="true"]) { + background-color: hsl(204 100% 40%); + color: hsl(204 20% 100%); +} + +:is(.dark .tab:hover[aria-selected="true"]) { + background-color: hsl(204 100% 32%); +} + +.panel { + padding: 0.5rem; +} + .bn-menu { position: relative; z-index: 50; @@ -262,3 +384,16 @@ font-weight: 500; opacity: 0.5; } + +.bn-image-panel { + display: flex; + flex-direction: column; + gap: 0.5rem; + border-radius: 0.5rem; + background-color: hsl(204 20% 100%); + padding: 0.5rem; + box-shadow: + 0 1px 3px 0 rgb(0 0 0 / 0.1), + 0 1px 2px -1px rgb(0 0 0 / 0.1); +} + diff --git a/packages/react/src/ariakit/components.tsx b/packages/react/src/ariakit/components.tsx index b20eb8310c..634cc6ed5a 100644 --- a/packages/react/src/ariakit/components.tsx +++ b/packages/react/src/ariakit/components.tsx @@ -15,6 +15,12 @@ import { Toolbar } from "./toolbar/Toolbar"; import { ToolbarButton } from "./toolbar/ToolbarButton"; import { ToolbarSelect } from "./toolbar/ToolbarSelect"; +import { Panel } from "./panel/Panel"; +import { PanelButton } from "./panel/PanelButton"; +import { PanelFileInput } from "./panel/PanelFileInput"; +import { PanelTab } from "./panel/PanelTab"; +import { PanelTextInput } from "./panel/PanelTextInput"; + export const ariakitComponents: ComponentsContextValue = { Form, TextInput, @@ -27,6 +33,11 @@ export const ariakitComponents: ComponentsContextValue = { MenuDivider, MenuLabel, MenuItem, + Panel, + PanelButton, + PanelFileInput, + PanelTab, + PanelTextInput, Popover, PopoverContent, PopoverTrigger, diff --git a/packages/react/src/ariakit/panel/Panel.tsx b/packages/react/src/ariakit/panel/Panel.tsx new file mode 100644 index 0000000000..ded7c08241 --- /dev/null +++ b/packages/react/src/ariakit/panel/Panel.tsx @@ -0,0 +1,33 @@ +import * as Ariakit from "@ariakit/react"; + +import { PanelProps } from "../../editor/ComponentsContext"; + +export const Panel = (props: PanelProps) => { + return ( +
+ { + if (activeId) { + props.setOpenTab(activeId); + } + }}> + {/*{props.loading && }*/} + + + {props.tabs.map((tab) => ( + + {tab.name} + + ))} + + + {props.tabs.map((tab) => ( + + {tab.tabPanel} + + ))} + +
+ ); +}; diff --git a/packages/react/src/ariakit/panel/PanelButton.tsx b/packages/react/src/ariakit/panel/PanelButton.tsx new file mode 100644 index 0000000000..9c6a6345bc --- /dev/null +++ b/packages/react/src/ariakit/panel/PanelButton.tsx @@ -0,0 +1,16 @@ +import * as Ariakit from "@ariakit/react"; + +import { PanelButtonProps } from "../../editor/ComponentsContext"; +import { mergeCSSClasses } from "@blocknote/core"; + +export const PanelButton = (props: PanelButtonProps) => { + const { children, className, ...rest } = props; + + return ( + + {children} + + ); +}; diff --git a/packages/react/src/ariakit/panel/PanelFileInput.tsx b/packages/react/src/ariakit/panel/PanelFileInput.tsx new file mode 100644 index 0000000000..96d03c01f7 --- /dev/null +++ b/packages/react/src/ariakit/panel/PanelFileInput.tsx @@ -0,0 +1,16 @@ +import * as Ariakit from "@ariakit/react"; + +import { PanelFileInputProps } from "../../editor/ComponentsContext"; + +export const PanelFileInput = (props: PanelFileInputProps) => ( + + props.onChange?.(e.target.files![0])} + placeholder={props.placeholder} + /> + +); diff --git a/packages/react/src/components/ImagePanel/mantine/ImagePanelTab.tsx b/packages/react/src/ariakit/panel/PanelTab.tsx similarity index 51% rename from packages/react/src/components/ImagePanel/mantine/ImagePanelTab.tsx rename to packages/react/src/ariakit/panel/PanelTab.tsx index cda386b139..dfb92e3fa9 100644 --- a/packages/react/src/components/ImagePanel/mantine/ImagePanelTab.tsx +++ b/packages/react/src/ariakit/panel/PanelTab.tsx @@ -1,18 +1,15 @@ import { mergeCSSClasses } from "@blocknote/core"; -import { ComponentPropsWithoutRef, forwardRef } from "react"; -export const ImagePanelTab = forwardRef< - HTMLDivElement, - ComponentPropsWithoutRef<"div"> ->((props, ref) => { +import { PanelTabProps } from "../../editor/ComponentsContext"; + +export const PanelTab = (props: PanelTabProps) => { const { className, children, ...rest } = props; return (
+ {...rest}> {children}
); -}); +}; diff --git a/packages/react/src/ariakit/panel/PanelTextInput.tsx b/packages/react/src/ariakit/panel/PanelTextInput.tsx new file mode 100644 index 0000000000..3a63326490 --- /dev/null +++ b/packages/react/src/ariakit/panel/PanelTextInput.tsx @@ -0,0 +1,18 @@ +import * as Ariakit from "@ariakit/react"; + +import { PanelTextInputProps } from "../../editor/ComponentsContext"; + +export const PanelTextInput = (props: PanelTextInputProps) => { + const { children, ...rest } = props; + + return ( + + + + ); +}; diff --git a/packages/react/src/components/FormattingToolbar/DefaultButtons/ReplaceImageButton.tsx b/packages/react/src/components/FormattingToolbar/DefaultButtons/ReplaceImageButton.tsx index 7357733824..06bede1b04 100644 --- a/packages/react/src/components/FormattingToolbar/DefaultButtons/ReplaceImageButton.tsx +++ b/packages/react/src/components/FormattingToolbar/DefaultButtons/ReplaceImageButton.tsx @@ -10,7 +10,7 @@ import { RiImageEditFill } from "react-icons/ri"; import { useComponentsContext } from "../../../editor/ComponentsContext"; import { useBlockNoteEditor } from "../../../hooks/useBlockNoteEditor"; import { useSelectedBlocks } from "../../../hooks/useSelectedBlocks"; -import { ImagePanel } from "../../ImagePanel/mantine/ImagePanel"; +import { ImagePanel } from "../../ImagePanel/ImagePanel"; export const ReplaceImageButton = () => { const components = useComponentsContext()!; diff --git a/packages/react/src/components/ImagePanel/mantine/DefaultTabs/EmbedTab.tsx b/packages/react/src/components/ImagePanel/DefaultTabs/EmbedTab.tsx similarity index 79% rename from packages/react/src/components/ImagePanel/mantine/DefaultTabs/EmbedTab.tsx rename to packages/react/src/components/ImagePanel/DefaultTabs/EmbedTab.tsx index 542b9460aa..75eb1fb88a 100644 --- a/packages/react/src/components/ImagePanel/mantine/DefaultTabs/EmbedTab.tsx +++ b/packages/react/src/components/ImagePanel/DefaultTabs/EmbedTab.tsx @@ -6,12 +6,10 @@ import { StyleSchema, } from "@blocknote/core"; import { ChangeEvent, KeyboardEvent, useCallback, useState } from "react"; -import { useBlockNoteEditor } from "../../../../hooks/useBlockNoteEditor"; -import { ImagePanelProps } from "../../ImagePanelProps"; -import { ImagePanelButton } from "../ImagePanelButton"; -import { ImagePanelTab } from "../ImagePanelTab"; -import { ImagePanelTextInput } from "../ImagePanelTextInput"; +import { useComponentsContext } from "../../../editor/ComponentsContext"; +import { useBlockNoteEditor } from "../../../hooks/useBlockNoteEditor"; +import { ImagePanelProps } from "../ImagePanelProps"; export const EmbedTab = < I extends InlineContentSchema = DefaultInlineContentSchema, @@ -19,6 +17,8 @@ export const EmbedTab = < >( props: ImagePanelProps ) => { + const components = useComponentsContext()!; + const { block } = props; const editor = useBlockNoteEditor< @@ -61,20 +61,20 @@ export const EmbedTab = < }, [editor, block, currentURL]); return ( - - + - Embed Image - - + + ); }; diff --git a/packages/react/src/components/ImagePanel/mantine/DefaultTabs/UploadTab.tsx b/packages/react/src/components/ImagePanel/DefaultTabs/UploadTab.tsx similarity index 81% rename from packages/react/src/components/ImagePanel/mantine/DefaultTabs/UploadTab.tsx rename to packages/react/src/components/ImagePanel/DefaultTabs/UploadTab.tsx index cbc974eda7..875156bbf0 100644 --- a/packages/react/src/components/ImagePanel/mantine/DefaultTabs/UploadTab.tsx +++ b/packages/react/src/components/ImagePanel/DefaultTabs/UploadTab.tsx @@ -1,5 +1,4 @@ import { Text } from "@mantine/core"; -import { useBlockNoteEditor } from "../../../../hooks/useBlockNoteEditor"; import { DefaultBlockSchema, DefaultInlineContentSchema, @@ -9,9 +8,9 @@ import { } from "@blocknote/core"; import { useCallback, useEffect, useState } from "react"; -import { ImagePanelProps } from "../../ImagePanelProps"; -import { ImagePanelTab } from "../ImagePanelTab"; -import { ImagePanelFileInput } from "../ImagePanelFileInput"; +import { useComponentsContext } from "../../../editor/ComponentsContext"; +import { useBlockNoteEditor } from "../../../hooks/useBlockNoteEditor"; +import { ImagePanelProps } from "../ImagePanelProps"; export const UploadTab = < I extends InlineContentSchema = DefaultInlineContentSchema, @@ -21,6 +20,8 @@ export const UploadTab = < setLoading: (loading: boolean) => void; } ) => { + const components = useComponentsContext()!; + const { block, setLoading } = props; const editor = useBlockNoteEditor< @@ -70,18 +71,19 @@ export const UploadTab = < [block, editor, setLoading] ); - return editor.uploadFile !== undefined ? ( - - + {uploadFailed && ( Error: Upload failed )} - - ) : null; + + ); }; diff --git a/packages/react/src/components/ImagePanel/ImagePanel.tsx b/packages/react/src/components/ImagePanel/ImagePanel.tsx new file mode 100644 index 0000000000..3a5ca46cec --- /dev/null +++ b/packages/react/src/components/ImagePanel/ImagePanel.tsx @@ -0,0 +1,71 @@ +import { + DefaultBlockSchema, + DefaultInlineContentSchema, + DefaultStyleSchema, + InlineContentSchema, + StyleSchema, +} from "@blocknote/core"; +import { useState } from "react"; + +import { + PanelProps, + useComponentsContext, +} from "../../editor/ComponentsContext"; +import { useBlockNoteEditor } from "../../hooks/useBlockNoteEditor"; +import { ImagePanelProps } from "./ImagePanelProps"; +import { EmbedTab } from "./DefaultTabs/EmbedTab"; +import { UploadTab } from "./DefaultTabs/UploadTab"; + +/** + * By default, the ImageToolbar component will render with default tabs. + * However, you can override the tabs to render by passing the `tabs` prop. You + * can use the default tab panels in the `DefaultTabPanels` directory or make + * your own using the `ImageToolbarPanel` component. + */ +export const ImagePanel = < + I extends InlineContentSchema = DefaultInlineContentSchema, + S extends StyleSchema = DefaultStyleSchema +>( + props: ImagePanelProps & + Partial> +) => { + const components = useComponentsContext()!; + + const editor = useBlockNoteEditor< + { image: DefaultBlockSchema["image"] }, + I, + S + >(); + + const [loading, setLoading] = useState(false); + + const tabs: PanelProps["tabs"] = props.tabs ?? [ + ...(editor.uploadFile !== undefined + ? [ + { + name: "Upload", + tabPanel: , + }, + ] + : []), + { + name: "Embed", + tabPanel: , + }, + ]; + + const [openTab, setOpenTab] = useState( + props.defaultOpenTab || tabs[0].name + ); + + return ( + + ); +}; diff --git a/packages/react/src/components/ImagePanel/ImagePanelController.tsx b/packages/react/src/components/ImagePanel/ImagePanelController.tsx index adadd8a364..aee8039b7b 100644 --- a/packages/react/src/components/ImagePanel/ImagePanelController.tsx +++ b/packages/react/src/components/ImagePanel/ImagePanelController.tsx @@ -11,8 +11,8 @@ import { FC } from "react"; import { useBlockNoteEditor } from "../../hooks/useBlockNoteEditor"; import { useUIElementPositioning } from "../../hooks/useUIElementPositioning"; import { useUIPluginState } from "../../hooks/useUIPluginState"; +import { ImagePanel } from "./ImagePanel"; import { ImagePanelProps } from "./ImagePanelProps"; -import { ImagePanel } from "./mantine/ImagePanel"; export const ImagePanelController = < I extends InlineContentSchema = DefaultInlineContentSchema, diff --git a/packages/react/src/components/ImagePanel/mantine/ImagePanel.tsx b/packages/react/src/components/ImagePanel/mantine/ImagePanel.tsx deleted file mode 100644 index 24df5a4838..0000000000 --- a/packages/react/src/components/ImagePanel/mantine/ImagePanel.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { - DefaultBlockSchema, - DefaultInlineContentSchema, - DefaultStyleSchema, - InlineContentSchema, - StyleSchema, -} from "@blocknote/core"; -import { Group, LoadingOverlay, Tabs } from "@mantine/core"; -import { ReactNode, useState } from "react"; - -import { useBlockNoteEditor } from "../../../hooks/useBlockNoteEditor"; -import { ImagePanelProps } from "../ImagePanelProps"; -import { EmbedTab } from "./DefaultTabs/EmbedTab"; -import { UploadTab } from "./DefaultTabs/UploadTab"; - -/** - * By default, the ImageToolbar component will render with default tabs. - * However, you can override the tabs to render by passing the `tabs` prop. You - * can use the default tab panels in the `DefaultTabPanels` directory or make - * your own using the `ImageToolbarPanel` component. - */ -export const ImagePanel = < - I extends InlineContentSchema = DefaultInlineContentSchema, - S extends StyleSchema = DefaultStyleSchema ->( - props: ImagePanelProps & { - children?: ReactNode; - } -) => { - const editor = useBlockNoteEditor< - { image: DefaultBlockSchema["image"] }, - I, - S - >(); - - const [openTab, setOpenTab] = useState("default"); - const [loading, setLoading] = useState(false); - - return ( - - {props.children !== undefined ? ( - props.children - ) : ( - - {loading && } - - - {editor.uploadFile !== undefined && ( - - Upload - - )} - - Embed - - - - {editor.uploadFile !== undefined && ( - - - - )} - - - - - )} - - ); -}; diff --git a/packages/react/src/components/ImagePanel/mantine/ImagePanelButton.tsx b/packages/react/src/components/ImagePanel/mantine/ImagePanelButton.tsx deleted file mode 100644 index 56ea90afe6..0000000000 --- a/packages/react/src/components/ImagePanel/mantine/ImagePanelButton.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { ComponentPropsWithoutRef, forwardRef } from "react"; -import { Button } from "@mantine/core"; - -export const ImagePanelButton = forwardRef< - HTMLButtonElement, - Omit, "size"> ->((props, ref) => ( - -)); diff --git a/packages/react/src/components/ImagePanel/mantine/ImagePanelFileInput.tsx b/packages/react/src/components/ImagePanel/mantine/ImagePanelFileInput.tsx deleted file mode 100644 index 3ec61c5e72..0000000000 --- a/packages/react/src/components/ImagePanel/mantine/ImagePanelFileInput.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { FileInput } from "@mantine/core"; -import { ComponentPropsWithoutRef, forwardRef, ReactNode } from "react"; - -export const ImagePanelFileInput = forwardRef< - HTMLButtonElement, - Omit< - ComponentPropsWithoutRef<"button">, - "value" | "defaultValue" | "onChange" - > & { - placeholder?: ReactNode; - value?: File | null; - defaultValue?: File | null; - onChange?: (payload: File | null) => void; - } ->((props, ref) => ( - -)); diff --git a/packages/react/src/components/ImagePanel/mantine/ImagePanelTextInput.tsx b/packages/react/src/components/ImagePanel/mantine/ImagePanelTextInput.tsx deleted file mode 100644 index c008ef7f7f..0000000000 --- a/packages/react/src/components/ImagePanel/mantine/ImagePanelTextInput.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { TextInput } from "@mantine/core"; -import { ComponentPropsWithoutRef, forwardRef } from "react"; - -export const ImagePanelTextInput = forwardRef< - HTMLInputElement, - Omit, "size"> ->((props, ref) => ( - -)); diff --git a/packages/react/src/editor/BlockNoteView.tsx b/packages/react/src/editor/BlockNoteView.tsx index 483dc88ec3..029e42b1e7 100644 --- a/packages/react/src/editor/BlockNoteView.tsx +++ b/packages/react/src/editor/BlockNoteView.tsx @@ -35,6 +35,7 @@ import { import { ComponentsContext } from "./ComponentsContext"; import { EditorContent } from "./EditorContent"; import "./styles.css"; +import { ariakitComponents } from "../ariakit/components"; const mantineTheme = { // Removes button press effect @@ -232,7 +233,7 @@ export const BlockNoteViewAriakit = ( props: ComponentProps ) => { return ( - + ); diff --git a/packages/react/src/editor/ComponentsContext.ts b/packages/react/src/editor/ComponentsContext.ts index 9506a9cd11..6fa1fc08cc 100644 --- a/packages/react/src/editor/ComponentsContext.ts +++ b/packages/react/src/editor/ComponentsContext.ts @@ -1,5 +1,6 @@ import { ComponentProps, + ComponentPropsWithoutRef, ComponentType, createContext, useContext, @@ -47,6 +48,34 @@ export type MenuTriggerProps = { sub?: boolean; }; +export type PanelProps = { + defaultOpenTab: string; + openTab: string; + setOpenTab: (name: string) => void; + tabs: { + name: string; + tabPanel: React.ReactNode; + }[]; + loading: boolean; + setLoading: (loading: boolean) => void; +}; + +export type PanelButtonProps = Omit, "size">; + +export type PanelFileInputProps = { + placeholder?: string; + value?: File | null; + defaultValue?: File | null; + onChange?: (payload: File | null) => void; +}; + +export type PanelTabProps = ComponentPropsWithoutRef<"div">; + +export type PanelTextInputProps = Omit< + ComponentPropsWithoutRef<"input">, + "size" +>; + export type SuggestionMenuItemProps = { title: string; onClick: () => void; @@ -82,6 +111,7 @@ export type ToolbarSelectProps = { }; export type ComponentsContextValue = { + FileInput: any; Form: ComponentType<{ children: React.ReactNode; }>; @@ -99,6 +129,11 @@ export type ComponentsContextValue = { children: React.ReactNode; }>; MenuItem: ComponentType; + Panel: ComponentType; + PanelButton: ComponentType; + PanelFileInput: ComponentType; + PanelTab: ComponentType; + PanelTextInput: ComponentType; Popover: any; PopoverTrigger: any; PopoverContent: any; diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 5174fdb85c..13b5c1716d 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -50,13 +50,9 @@ export * from "./components/SuggestionMenu/types"; export * from "./components/ImagePanel/ImagePanelController"; export * from "./components/ImagePanel/ImagePanelProps"; -export * from "./components/ImagePanel/mantine/DefaultTabs/EmbedTab"; -export * from "./components/ImagePanel/mantine/DefaultTabs/UploadTab"; -export * from "./components/ImagePanel/mantine/ImagePanel"; -export * from "./components/ImagePanel/mantine/ImagePanelButton"; -export * from "./components/ImagePanel/mantine/ImagePanelFileInput"; -export * from "./components/ImagePanel/mantine/ImagePanelTab"; -export * from "./components/ImagePanel/mantine/ImagePanelTextInput"; +export * from "./components/ImagePanel/DefaultTabs/EmbedTab"; +export * from "./components/ImagePanel/DefaultTabs/UploadTab"; +export * from "./components/ImagePanel/ImagePanel"; export * from "./components/TableHandles/TableHandle"; export * from "./components/TableHandles/TableHandleProps"; diff --git a/packages/react/src/mantine/mantineComponents.tsx b/packages/react/src/mantine/mantineComponents.tsx index c8dfa49add..1f9e80504d 100644 --- a/packages/react/src/mantine/mantineComponents.tsx +++ b/packages/react/src/mantine/mantineComponents.tsx @@ -9,6 +9,11 @@ import { MenuLabel, MenuTrigger, } from "./menu/Menu"; +import { Panel } from "./panel/Panel"; +import { PanelButton } from "./panel/PanelButton"; +import { PanelFileInput } from "./panel/PanelFileInput"; +import { PanelTab } from "./panel/PanelTab"; +import { PanelTextInput } from "./panel/PanelTextInput"; import { Popover } from "./popover/Popover"; import { Toolbar } from "./toolbar/Toolbar"; import { ToolbarButton } from "./toolbar/ToolbarButton"; @@ -24,6 +29,11 @@ export const mantineComponents: ComponentsContextValue = { MenuDivider, MenuLabel, MenuItem, + Panel, + PanelButton, + PanelFileInput, + PanelTab, + PanelTextInput, Popover: Popover, PopoverContent: PopoverDropdown, PopoverTrigger: PopoverTarget, diff --git a/packages/react/src/mantine/panel/Panel.tsx b/packages/react/src/mantine/panel/Panel.tsx new file mode 100644 index 0000000000..7e2110dffc --- /dev/null +++ b/packages/react/src/mantine/panel/Panel.tsx @@ -0,0 +1,27 @@ +import { Group, LoadingOverlay, Tabs } from "@mantine/core"; + +import { PanelProps } from "../../editor/ComponentsContext"; + +export const Panel = (props: PanelProps) => { + return ( + + + {props.loading && } + + + {props.tabs.map((tab) => ( + + {tab.name} + + ))} + + + {props.tabs.map((tab) => ( + + {tab.tabPanel} + + ))} + + + ); +}; diff --git a/packages/react/src/mantine/panel/PanelButton.tsx b/packages/react/src/mantine/panel/PanelButton.tsx new file mode 100644 index 0000000000..0e82cf04c5 --- /dev/null +++ b/packages/react/src/mantine/panel/PanelButton.tsx @@ -0,0 +1,13 @@ +import { Button } from "@mantine/core"; + +import { PanelButtonProps } from "../../editor/ComponentsContext"; + +export const PanelButton = (props: PanelButtonProps) => { + const { children, ...rest } = props; + + return ( + + ); +}; diff --git a/packages/react/src/mantine/panel/PanelFileInput.tsx b/packages/react/src/mantine/panel/PanelFileInput.tsx new file mode 100644 index 0000000000..093ad6ee51 --- /dev/null +++ b/packages/react/src/mantine/panel/PanelFileInput.tsx @@ -0,0 +1,7 @@ +import { FileInput } from "@mantine/core"; + +import { PanelFileInputProps } from "../../editor/ComponentsContext"; + +export const PanelFileInput = (props: PanelFileInputProps) => ( + +); diff --git a/packages/react/src/mantine/panel/PanelTab.tsx b/packages/react/src/mantine/panel/PanelTab.tsx new file mode 100644 index 0000000000..dfb92e3fa9 --- /dev/null +++ b/packages/react/src/mantine/panel/PanelTab.tsx @@ -0,0 +1,15 @@ +import { mergeCSSClasses } from "@blocknote/core"; + +import { PanelTabProps } from "../../editor/ComponentsContext"; + +export const PanelTab = (props: PanelTabProps) => { + const { className, children, ...rest } = props; + + return ( +
+ {children} +
+ ); +}; diff --git a/packages/react/src/mantine/panel/PanelTextInput.tsx b/packages/react/src/mantine/panel/PanelTextInput.tsx new file mode 100644 index 0000000000..7b5ee14284 --- /dev/null +++ b/packages/react/src/mantine/panel/PanelTextInput.tsx @@ -0,0 +1,9 @@ +import { TextInput } from "@mantine/core"; + +import { PanelTextInputProps } from "../../editor/ComponentsContext"; + +export const PanelTextInput = (props: PanelTextInputProps) => { + const { children, ...rest } = props; + + return ; +}; diff --git a/packages/shadcn/package.json b/packages/shadcn/package.json index 21c9a0d739..f435c6a968 100644 --- a/packages/shadcn/package.json +++ b/packages/shadcn/package.json @@ -55,6 +55,7 @@ "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-toggle": "^1.0.3", "@radix-ui/react-tooltip": "^1.0.7", "class-variance-authority": "^0.7.0", diff --git a/packages/shadcn/src/components/ui/input.tsx b/packages/shadcn/src/components/ui/input.tsx new file mode 100644 index 0000000000..1afb83e15a --- /dev/null +++ b/packages/shadcn/src/components/ui/input.tsx @@ -0,0 +1,24 @@ +import * as React from "react"; + +import { cn } from "../../lib/utils"; + +export type InputProps = React.InputHTMLAttributes; + +const Input = React.forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ); + } +); +Input.displayName = "Input"; + +export { Input }; diff --git a/packages/shadcn/src/components/ui/tabs.tsx b/packages/shadcn/src/components/ui/tabs.tsx new file mode 100644 index 0000000000..b456cf653c --- /dev/null +++ b/packages/shadcn/src/components/ui/tabs.tsx @@ -0,0 +1,53 @@ +import * as React from "react"; +import * as TabsPrimitive from "@radix-ui/react-tabs"; + +import { cn } from "../../lib/utils"; + +const Tabs = TabsPrimitive.Root; + +const TabsList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsList.displayName = TabsPrimitive.List.displayName; + +const TabsTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; + +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsContent.displayName = TabsPrimitive.Content.displayName; + +export { Tabs, TabsList, TabsTrigger, TabsContent }; diff --git a/packages/shadcn/src/index.tsx b/packages/shadcn/src/index.tsx index 1c9054dd1f..c6cbfbd9aa 100644 --- a/packages/shadcn/src/index.tsx +++ b/packages/shadcn/src/index.tsx @@ -12,6 +12,11 @@ import { MenuLabel, MenuTrigger, } from "./menu/Menu"; +import { Panel } from "./panel/Panel"; +import { PanelButton } from "./panel/PanelButton"; +import { PanelFileInput } from "./panel/PanelFileInput"; +import { PanelTab } from "./panel/PanelTab"; +import { PanelTextInput } from "./panel/PanelTextInput"; import "./style.css"; import { Toolbar, ToolbarButton, ToolbarSelect } from "./toolbar/Toolbar"; @@ -25,6 +30,11 @@ export const components: ComponentsContextValue = { MenuLabel, MenuDivider, MenuItem, + Panel, + PanelButton, + PanelFileInput, + PanelTab, + PanelTextInput, Popover: () => null, PopoverContent: () => null, PopoverTrigger: () => null, diff --git a/packages/shadcn/src/panel/Panel.tsx b/packages/shadcn/src/panel/Panel.tsx new file mode 100644 index 0000000000..6f264459b5 --- /dev/null +++ b/packages/shadcn/src/panel/Panel.tsx @@ -0,0 +1,36 @@ +import { PanelProps } from "../../../react/src"; + +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "../components/ui/tabs"; + +export const Panel = (props: PanelProps) => { + return ( + + {/*{props.loading && }*/} + + + {props.tabs.map((tab) => ( + + {tab.name} + + ))} + + + {props.tabs.map((tab) => ( + + {tab.tabPanel} + + ))} + + ); +}; diff --git a/packages/shadcn/src/panel/PanelButton.tsx b/packages/shadcn/src/panel/PanelButton.tsx new file mode 100644 index 0000000000..2341b45f13 --- /dev/null +++ b/packages/shadcn/src/panel/PanelButton.tsx @@ -0,0 +1,13 @@ +import { PanelButtonProps } from "../../../react/src"; +import { Button } from "../components/ui/button"; +import { cn } from "../lib/utils"; + +export const PanelButton = (props: PanelButtonProps) => { + const { children, className, ...rest } = props; + + return ( + + ); +}; diff --git a/packages/shadcn/src/panel/PanelFileInput.tsx b/packages/shadcn/src/panel/PanelFileInput.tsx new file mode 100644 index 0000000000..18df11234e --- /dev/null +++ b/packages/shadcn/src/panel/PanelFileInput.tsx @@ -0,0 +1,14 @@ +import { PanelFileInputProps } from "../../../react/src"; +import { Input } from "../components/ui/input"; + +export const PanelFileInput = (props: PanelFileInputProps) => { + return ( + props.onChange?.(e.target.files![0])} + placeholder={props.placeholder} + /> + ); +}; diff --git a/packages/shadcn/src/panel/PanelTab.tsx b/packages/shadcn/src/panel/PanelTab.tsx new file mode 100644 index 0000000000..238ae28786 --- /dev/null +++ b/packages/shadcn/src/panel/PanelTab.tsx @@ -0,0 +1,15 @@ +import { mergeCSSClasses } from "@blocknote/core"; + +import { PanelTabProps } from "../../../react/src"; + +export const PanelTab = (props: PanelTabProps) => { + const { className, children, ...rest } = props; + + return ( +
+ {children} +
+ ); +}; diff --git a/packages/shadcn/src/panel/PanelTextInput.tsx b/packages/shadcn/src/panel/PanelTextInput.tsx new file mode 100644 index 0000000000..897333dcef --- /dev/null +++ b/packages/shadcn/src/panel/PanelTextInput.tsx @@ -0,0 +1,12 @@ +import { PanelTextInputProps } from "../../../react/src"; +import { Input } from "../components/ui/input"; + +export const PanelTextInput = (props: PanelTextInputProps) => { + const { children, ...rest } = props; + + return ( + + {children} + + ); +};