From bd8c2e759a5bc8441863c44613b54af987ea367d Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Tue, 9 Apr 2024 20:04:18 +0200 Subject: [PATCH 01/34] Cleaned up component context API --- packages/ariakit/src/input/Form.tsx | 4 +- packages/ariakit/src/menu/Menu.tsx | 43 ++--- packages/mantine/src/menu/Menu.tsx | 13 +- .../mantine/src/toolbar/ToolbarSelect.tsx | 19 +- .../mantine/src/toolbar/ToolbarSelectItem.tsx | 24 --- .../DefaultButtons/ImageCaptionButton.tsx | 24 +-- .../DefaultSelects/BlockTypeSelect.tsx | 4 +- .../DefaultItems/BlockColorsItem.tsx | 6 +- .../implementation/SuggestionMenuItem.tsx | 4 +- .../react/src/editor/ComponentsContext.ts | 169 +++++++++++------- packages/shadcn/src/menu/Menu.tsx | 2 +- 11 files changed, 163 insertions(+), 149 deletions(-) delete mode 100644 packages/mantine/src/toolbar/ToolbarSelectItem.tsx diff --git a/packages/ariakit/src/input/Form.tsx b/packages/ariakit/src/input/Form.tsx index 6a64c589a..1d3b3ab47 100644 --- a/packages/ariakit/src/input/Form.tsx +++ b/packages/ariakit/src/input/Form.tsx @@ -1,8 +1,8 @@ import * as Ariakit from "@ariakit/react"; -import { ReactNode } from "react"; +import { FormProps } from "@ariakit/react"; -export const Form = (props: { children: ReactNode }) => { +export const Form = (props: FormProps) => { const { ...rest } = props; return ; diff --git a/packages/ariakit/src/menu/Menu.tsx b/packages/ariakit/src/menu/Menu.tsx index 476d29fae..307c7a6d5 100644 --- a/packages/ariakit/src/menu/Menu.tsx +++ b/packages/ariakit/src/menu/Menu.tsx @@ -1,7 +1,7 @@ import * as Ariakit from "@ariakit/react"; import { mergeCSSClasses } from "@blocknote/core"; -import { MenuItemProps, MenuProps } from "@blocknote/react"; +import { MenuItemProps, MenuProps, MenuLabelProps } from "@blocknote/react"; import { HTMLAttributes, forwardRef } from "react"; export const Menu = (props: MenuProps) => { @@ -36,14 +36,11 @@ export const MenuDropdown = forwardRef< export const MenuItem = forwardRef( (props, ref) => { - const { className, children, icon, checked, expandArrow, ...rest } = props; + const { children, icon, checked, subTrigger, onClick } = props; - if (expandArrow) { + if (subTrigger) { return ( - + {icon} {children} @@ -52,10 +49,7 @@ export const MenuItem = forwardRef( ); } return ( - + {icon} {children} {checked !== undefined && } @@ -64,21 +58,20 @@ export const MenuItem = forwardRef( } ); -export const MenuLabel = forwardRef< - HTMLDivElement, - HTMLAttributes ->((props, ref) => { - const { className, children, ...rest } = props; +export const MenuLabel = forwardRef( + (props, ref) => { + const { className, children, ...rest } = props; - return ( - - {children} - - ); -}); + return ( + + {children} + + ); + } +); export const MenuTrigger = forwardRef< HTMLDivElement, diff --git a/packages/mantine/src/menu/Menu.tsx b/packages/mantine/src/menu/Menu.tsx index d92cf4201..b2cb2baff 100644 --- a/packages/mantine/src/menu/Menu.tsx +++ b/packages/mantine/src/menu/Menu.tsx @@ -15,6 +15,7 @@ import { useState, } from "react"; import { HiChevronRight } from "react-icons/hi"; +import { MenuDividerProps } from "@mantine/core"; const SubMenuContext = createContext< | { @@ -27,7 +28,7 @@ const SubMenuContext = createContext< >(undefined); const SubMenu = (props: MenuProps) => { - const { sub, onOpenChange, open, defaultOpen, ...rest } = props; + const { sub, onOpenChange, ...rest } = props; const [opened, setOpened] = useState(false); @@ -64,7 +65,6 @@ const SubMenu = (props: MenuProps) => { middlewares={{ flip: true, shift: true, inline: false, size: true }} onClose={() => onOpenChange?.(false)} onOpen={() => onOpenChange?.(true)} - defaultOpened={defaultOpen} closeOnItemClick={false} {...rest} position="right" @@ -74,7 +74,7 @@ const SubMenu = (props: MenuProps) => { }; export const Menu = (props: MenuProps) => { - const { sub, onOpenChange, open, defaultOpen, ...rest } = props; + const { sub, onOpenChange, ...rest } = props; if (sub) { return ; @@ -86,7 +86,6 @@ export const Menu = (props: MenuProps) => { middlewares={{ flip: true, shift: true, inline: false, size: true }} onClose={() => onOpenChange?.(false)} onOpen={() => onOpenChange?.(true)} - defaultOpened={defaultOpen} closeOnItemClick={false} {...rest} position="right" @@ -96,7 +95,7 @@ export const Menu = (props: MenuProps) => { export const MenuItem = forwardRef( (props, ref) => { - const { icon, checked, expandArrow, subTrigger, ...rest } = props; + const { icon, checked, subTrigger, ...rest } = props; const ctx = useContext(SubMenuContext); const onMouseLeave = subTrigger ? ctx!.onTriggerMouseLeave : undefined; @@ -113,7 +112,7 @@ export const MenuItem = forwardRef( ) : checked === false ? (
) : undefined} - {expandArrow && } + {subTrigger && } } onMouseOver={onMouseOver} @@ -153,7 +152,7 @@ export const MenuDropdown = forwardRef( } ); -export const MenuDivider = forwardRef>( +export const MenuDivider = forwardRef( (props, ref) => { return ; } diff --git a/packages/mantine/src/toolbar/ToolbarSelect.tsx b/packages/mantine/src/toolbar/ToolbarSelect.tsx index 8488a4157..ff7268518 100644 --- a/packages/mantine/src/toolbar/ToolbarSelect.tsx +++ b/packages/mantine/src/toolbar/ToolbarSelect.tsx @@ -3,8 +3,7 @@ import * as Mantine from "@mantine/core"; import { isSafari } from "@blocknote/core"; import { ToolbarSelectProps } from "@blocknote/react"; import { HiChevronDown } from "react-icons/hi"; - -import { ToolbarSelectItem } from "./ToolbarSelectItem"; +import { TiTick } from "react-icons/ti"; // TODO: turn into select export function ToolbarSelect(props: ToolbarSelectProps) { @@ -41,7 +40,21 @@ export function ToolbarSelect(props: ToolbarSelectProps) { {props.items.map((item) => ( - + + ) : ( + // Ensures space for tick even if item isn't currently selected. +
+ ) + } + disabled={item.isDisabled}> + {item.text} + ))} diff --git a/packages/mantine/src/toolbar/ToolbarSelectItem.tsx b/packages/mantine/src/toolbar/ToolbarSelectItem.tsx deleted file mode 100644 index c9e6c162f..000000000 --- a/packages/mantine/src/toolbar/ToolbarSelectItem.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as Mantine from "@mantine/core"; - -import { ToolbarSelectItemProps } from "@blocknote/react"; -import { TiTick } from "react-icons/ti"; - -export function ToolbarSelectItem(props: ToolbarSelectItemProps) { - return ( - - ) : ( - // Ensures space for tick even if item isn't currently selected. -
- ) - } - disabled={props.isDisabled}> - {props.text} - - ); -} diff --git a/packages/react/src/components/FormattingToolbar/DefaultButtons/ImageCaptionButton.tsx b/packages/react/src/components/FormattingToolbar/DefaultButtons/ImageCaptionButton.tsx index c4ae361a0..1f79acaea 100644 --- a/packages/react/src/components/FormattingToolbar/DefaultButtons/ImageCaptionButton.tsx +++ b/packages/react/src/components/FormattingToolbar/DefaultButtons/ImageCaptionButton.tsx @@ -76,18 +76,18 @@ export const ImageCaptionButton = () => { isSelected={imageBlock.props.caption !== ""} /> - - {/*TODO*/} - {/**/} - + {/**/} + {/* TODO*/} + {/* */} + {/**/} ); }; diff --git a/packages/react/src/components/FormattingToolbar/DefaultSelects/BlockTypeSelect.tsx b/packages/react/src/components/FormattingToolbar/DefaultSelects/BlockTypeSelect.tsx index 8a6f5b990..37eb9e22b 100644 --- a/packages/react/src/components/FormattingToolbar/DefaultSelects/BlockTypeSelect.tsx +++ b/packages/react/src/components/FormattingToolbar/DefaultSelects/BlockTypeSelect.tsx @@ -18,7 +18,7 @@ import { } from "react-icons/ri"; import { - ToolbarSelectItemProps, + ToolbarSelectItem, useComponentsContext, } from "../../../editor/ComponentsContext"; import { useBlockNoteEditor } from "../../../hooks/useBlockNoteEditor"; @@ -112,7 +112,7 @@ export const BlockTypeSelect = (props: { items?: BlockTypeSelectItem[] }) => { [block.type, filteredItems] ); - const fullItems: ToolbarSelectItemProps[] = useMemo(() => { + const fullItems: ToolbarSelectItem[] = useMemo(() => { const onClick = (item: BlockTypeSelectItem) => { editor.focus(); diff --git a/packages/react/src/components/SideMenu/DragHandleMenu/DefaultItems/BlockColorsItem.tsx b/packages/react/src/components/SideMenu/DragHandleMenu/DefaultItems/BlockColorsItem.tsx index 629ed6f7e..864d696b7 100644 --- a/packages/react/src/components/SideMenu/DragHandleMenu/DefaultItems/BlockColorsItem.tsx +++ b/packages/react/src/components/SideMenu/DragHandleMenu/DefaultItems/BlockColorsItem.tsx @@ -35,14 +35,14 @@ export const BlockColorsItem = < } return ( - + - + {props.children} - + (null); const handleMouseLeave = useCallback(() => { - setSelected(false); + setSelected?.(false); }, [setSelected]); const handleMouseEnter = useCallback(() => { - setSelected(true); + setSelected?.(true); }, [setSelected]); // TODO: remove mantine classnames and clean up styles diff --git a/packages/react/src/editor/ComponentsContext.ts b/packages/react/src/editor/ComponentsContext.ts index b11aaf905..b7919f6f0 100644 --- a/packages/react/src/editor/ComponentsContext.ts +++ b/packages/react/src/editor/ComponentsContext.ts @@ -1,51 +1,52 @@ import { - ComponentProps, - ComponentPropsWithoutRef, ComponentType, createContext, + ReactNode, useContext, + KeyboardEvent, + MouseEvent, + ChangeEvent, + ElementType, } from "react"; import { SuggestionMenuEmptyItem } from "../components/SuggestionMenu/implementation/SuggestionMenuEmptyItem"; import { SuggestionMenuItem } from "../components/SuggestionMenu/implementation/SuggestionMenuItem"; import { SuggestionMenuLabel } from "../components/SuggestionMenu/implementation/SuggestionMenuLabel"; import { SuggestionMenuLoader } from "../components/SuggestionMenu/implementation/SuggestionMenuLoader"; +export type FormProps = { + children: ReactNode; +}; + export type MenuProps = { - children: React.ReactNode; - defaultOpen?: boolean; - open?: boolean; + children: ReactNode; onOpenChange?: (open: boolean) => void; position?: "top" | "right" | "bottom" | "left"; sub?: boolean; }; -export type TextInputProps = { - name: string; - label?: string; - icon?: React.ReactNode; - autoFocus?: boolean; - placeholder?: string; - value?: string; - onKeyDown?: (event: React.KeyboardEvent) => void; - onChange?: (event: React.ChangeEvent) => void; - onSubmit?: () => void; +export type MenuDividerProps = Record; + +export type MenuDropdownProps = { + className?: string; + sub?: boolean; + children: ReactNode; }; export type MenuItemProps = { - icon?: React.ReactNode; + icon?: ReactNode; checked?: boolean; - expandArrow?: boolean; subTrigger?: boolean; -} & ComponentProps<"div">; + onClick?: () => void; + children: ReactNode; +}; -export type MenuDropdownProps = { - sub?: boolean; - position?: "top" | "right" | "bottom" | "left"; -} & ComponentProps<"div">; +export type MenuLabelProps = { + children: ReactNode; +}; export type MenuTriggerProps = { - children: React.ReactNode; sub?: boolean; + children: ReactNode; }; export type PanelProps = { @@ -54,41 +55,88 @@ export type PanelProps = { setOpenTab: (name: string) => void; tabs: { name: string; - tabPanel: React.ReactNode; + tabPanel: ReactNode; }[]; loading: boolean; setLoading: (loading: boolean) => void; }; -export type PanelButtonProps = Omit, "size">; +export type PanelButtonProps = { + className: string; + onClick: () => void; + children: ReactNode; +}; export type PanelFileInputProps = { placeholder?: string; value?: File | null; - defaultValue?: File | null; onChange?: (payload: File | null) => void; }; -export type PanelTabProps = ComponentPropsWithoutRef<"div">; +export type PanelTabProps = { + children: ReactNode; +}; -export type PanelTextInputProps = Omit< - ComponentPropsWithoutRef<"input">, - "size" ->; +export type PanelTextInputProps = { + placeholder: string; + value: string; + onChange: (event: ChangeEvent) => void; + onKeyDown: (event: KeyboardEvent) => void; +}; + +export type PopoverProps = { + opened?: boolean; + position?: "top" | "right" | "bottom" | "left"; + children: ReactNode; +}; + +export type PopoverTriggerProps = { + children: ReactNode; +}; + +export type PopoverContentProps = { + children: ReactNode; +}; + +export type SuggestionMenuEmptyItemProps = { + children: ReactNode; +}; export type SuggestionMenuItemProps = { title: string; - onClick: () => void; subtext?: string; - icon?: JSX.Element; - badge?: string; + icon?: ReactNode; + badge?: ReactNode; + onClick?: () => void; isSelected?: boolean; - setSelected: (selected: boolean) => void; + setSelected?: (selected: boolean) => void; +}; + +export type SuggestionMenuLabelProps = { + children: ReactNode; +}; + +export type TextInputProps = { + name: string; + label?: string; + icon?: ReactNode; + autoFocus?: boolean; + placeholder?: string; + value?: string; + onKeyDown?: (event: KeyboardEvent) => void; + onChange?: (event: ChangeEvent) => void; + onSubmit?: () => void; +}; + +export type ToolbarProps = { + onMouseEnter?: () => void; + onMouseLeave?: () => void; + children: ReactNode; }; export type ToolbarButtonProps = { - onClick?: (e: React.MouseEvent) => void; - icon?: React.ReactNode; + onClick?: (e: MouseEvent) => void; + icon?: ReactNode; mainTooltip: string; secondaryTooltip?: string; isSelected?: boolean; @@ -96,42 +144,27 @@ export type ToolbarButtonProps = { isDisabled?: boolean; }; -export type ToolbarSelectItemProps = { +export type ToolbarSelectItem = { text: string; - icon?: React.ReactNode; + icon?: ReactNode; onClick?: () => void; isSelected?: boolean; isDisabled?: boolean; }; export type ToolbarSelectProps = { - // TODO: maybe don't use items array, but elements instead - items: ToolbarSelectItemProps[]; + items: ToolbarSelectItem[]; isDisabled?: boolean; }; export type ComponentsContextValue = { - FileInput: any; - - Form: ComponentType<{ - children: React.ReactNode; - }>; - - Toolbar: ComponentType< - ComponentPropsWithoutRef<"div"> & { - children: React.ReactNode; - } - >; - ToolbarSelect: ComponentType; - ToolbarButton: ComponentType; + Form: ComponentType; Menu: ComponentType; MenuTrigger: ComponentType; MenuDropdown: ComponentType; - MenuDivider: ComponentType>; - MenuLabel: ComponentType<{ - children: React.ReactNode; - }>; + MenuDivider: ComponentType; + MenuLabel: ComponentType; MenuItem: ComponentType; Panel: ComponentType; @@ -140,20 +173,20 @@ export type ComponentsContextValue = { PanelTab: ComponentType; PanelTextInput: ComponentType; - Popover: any; - PopoverTrigger: any; - PopoverContent: any; + Popover: ComponentType; + PopoverTrigger: ComponentType; + PopoverContent: ComponentType; - SuggestionMenuLabel?: ComponentType<{ - children: React.ReactNode; - }>; - SuggestionMenuLoader?: React.ElementType; + SuggestionMenuLabel?: ComponentType; + SuggestionMenuLoader?: ElementType; SuggestionMenuItem?: ComponentType; - SuggestionMenuEmptyItem?: ComponentType<{ - children: React.ReactNode; - }>; + SuggestionMenuEmptyItem?: ComponentType; TextInput: ComponentType; + + Toolbar: ComponentType; + ToolbarSelect: ComponentType; + ToolbarButton: ComponentType; }; export const ComponentsContext = createContext< diff --git a/packages/shadcn/src/menu/Menu.tsx b/packages/shadcn/src/menu/Menu.tsx index 7b17c2fb3..7110ff899 100644 --- a/packages/shadcn/src/menu/Menu.tsx +++ b/packages/shadcn/src/menu/Menu.tsx @@ -84,7 +84,7 @@ export const MenuItem = React.forwardRef((props: MenuItemProps, ref) => { {props.icon} {props.children} - {props.expandArrow && } + {props.subTrigger && } ); }); From 67709b96759ace9dbe90921812f7b70d39ac63d5 Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Thu, 11 Apr 2024 04:53:09 +0200 Subject: [PATCH 02/34] WIP --- examples/01-basic/01-minimal/App.tsx | 18 +- packages/ariakit/src/index.tsx | 61 +- packages/ariakit/src/input/Form.tsx | 4 +- packages/ariakit/src/input/TextInput.tsx | 6 +- packages/ariakit/src/menu/Menu.tsx | 98 +-- packages/ariakit/src/panel/Panel.tsx | 12 +- packages/ariakit/src/panel/PanelButton.tsx | 11 +- packages/ariakit/src/panel/PanelFileInput.tsx | 8 +- packages/ariakit/src/panel/PanelTab.tsx | 15 +- packages/ariakit/src/panel/PanelTextInput.tsx | 9 +- packages/ariakit/src/popover/Popover.tsx | 28 +- packages/ariakit/src/style.css | 660 ++++++++++++------ packages/ariakit/src/toolbar/Toolbar.tsx | 25 +- .../ariakit/src/toolbar/ToolbarButton.tsx | 80 ++- .../ariakit/src/toolbar/ToolbarSelect.tsx | 14 +- packages/mantine/src/index.tsx | 67 +- packages/mantine/src/input/TextInput.tsx | 6 +- packages/mantine/src/mantineStyles.css | 138 ++++ packages/mantine/src/menu/Menu.tsx | 141 ++-- packages/mantine/src/panel/Panel.tsx | 40 +- packages/mantine/src/panel/PanelButton.tsx | 12 +- packages/mantine/src/panel/PanelFileInput.tsx | 8 +- packages/mantine/src/panel/PanelTab.tsx | 16 +- packages/mantine/src/panel/PanelTextInput.tsx | 10 +- packages/mantine/src/popover/Popover.tsx | 14 +- packages/mantine/src/styles.css | 540 ++++++++++++++ packages/mantine/src/toolbar/Toolbar.tsx | 24 +- .../mantine/src/toolbar/ToolbarButton.tsx | 113 +-- .../src/toolbar/ToolbarInputsMenuItem.tsx | 1 + .../mantine/src/toolbar/ToolbarSelect.tsx | 8 +- .../components/ColorPicker/ColorPicker.tsx | 16 +- .../DefaultButtons/BasicTextStyleButton.tsx | 5 +- .../DefaultButtons/ColorStyleButton.tsx | 18 +- .../DefaultButtons/CreateLinkButton.tsx | 18 +- .../DefaultButtons/ImageCaptionButton.tsx | 14 +- .../DefaultButtons/NestBlockButtons.tsx | 11 +- .../DefaultButtons/ReplaceImageButton.tsx | 19 +- .../DefaultButtons/TextAlignButton.tsx | 6 +- .../DefaultSelects/BlockTypeSelect.tsx | 52 +- .../FormattingToolbar/FormattingToolbar.tsx | 4 +- .../ImagePanel/DefaultTabs/EmbedTab.tsx | 15 +- .../ImagePanel/DefaultTabs/UploadTab.tsx | 9 +- .../src/components/ImagePanel/ImagePanel.tsx | 8 +- .../DefaultButtons/DeleteLinkButton.tsx | 5 +- .../DefaultButtons/EditLinkButton.tsx | 21 +- .../DefaultButtons/OpenLinkButton.tsx | 5 +- .../LinkToolbar/EditLinkMenuItems.tsx | 11 +- .../components/LinkToolbar/LinkToolbar.tsx | 12 +- .../DefaultButtons/DragHandleButton.tsx | 11 +- .../DefaultItems/BlockColorsItem.tsx | 19 +- .../DefaultItems/RemoveBlockItem.tsx | 8 +- .../DragHandleMenu/DragHandleMenu.tsx | 7 +- .../components/TableHandles/TableHandle.tsx | 11 +- .../DefaultButtons/AddButton.tsx | 14 +- .../DefaultButtons/DeleteButton.tsx | 14 +- .../TableHandleMenu/TableHandleMenu.tsx | 7 +- .../react/src/editor/BlockNoteDefaultUI.tsx | 42 +- .../react/src/editor/ComponentsContext.ts | 211 ------ .../react/src/editor/ComponentsContext.tsx | 597 ++++++++++++++++ packages/react/src/editor/mantineStyles.css | 138 ---- packages/react/src/editor/styles.css | 544 +-------------- packages/react/src/index.ts | 2 - packages/shadcn/src/form/Form.tsx | 4 +- packages/shadcn/src/index.tsx | 65 +- packages/shadcn/src/input/TextInput.tsx | 6 +- packages/shadcn/src/menu/Menu.tsx | 71 +- packages/shadcn/src/panel/Panel.tsx | 5 +- packages/shadcn/src/panel/PanelButton.tsx | 4 +- packages/shadcn/src/panel/PanelFileInput.tsx | 7 +- packages/shadcn/src/panel/PanelTab.tsx | 5 +- packages/shadcn/src/panel/PanelTextInput.tsx | 14 +- packages/shadcn/src/style.css | 3 + packages/shadcn/src/toolbar/Toolbar.tsx | 82 ++- 73 files changed, 2483 insertions(+), 1844 deletions(-) create mode 100644 packages/mantine/src/mantineStyles.css create mode 100644 packages/mantine/src/styles.css delete mode 100644 packages/react/src/editor/ComponentsContext.ts create mode 100644 packages/react/src/editor/ComponentsContext.tsx delete mode 100644 packages/react/src/editor/mantineStyles.css diff --git a/examples/01-basic/01-minimal/App.tsx b/examples/01-basic/01-minimal/App.tsx index a32fe2fb7..fe9d2e6f6 100644 --- a/examples/01-basic/01-minimal/App.tsx +++ b/examples/01-basic/01-minimal/App.tsx @@ -1,16 +1,26 @@ -import "@blocknote/core/fonts/inter.css"; import { useCreateBlockNote } from "@blocknote/react"; -import "@blocknote/react/style.css"; import { uploadToTmpFilesDotOrg_DEV_ONLY } from "@blocknote/core"; -import { BlockNoteView } from "@blocknote/mantine"; -// import { BlockNoteView } from "@blocknote/ariakit"; +// import { BlockNoteView } from "@blocknote/mantine"; +// import "@blocknote/mantine/styles.css"; +import { BlockNoteView } from "@blocknote/ariakit"; +import "@blocknote/ariakit/style.css"; // import { BlockNoteView } from "@blocknote/shadcn"; +// import "@blocknote/shadcn/style.css"; export default function App() { // Creates a new editor instance. const editor = useCreateBlockNote({ uploadFile: uploadToTmpFilesDotOrg_DEV_ONLY, + initialContent: [ + { + type: "image", + }, + { + type: "paragraph", + content: "Hello, world!", + }, + ], }); // Renders the editor instance using a React component. diff --git a/packages/ariakit/src/index.tsx b/packages/ariakit/src/index.tsx index df7e6204e..e63477c6f 100644 --- a/packages/ariakit/src/index.tsx +++ b/packages/ariakit/src/index.tsx @@ -1,7 +1,8 @@ import { BlockNoteViewRaw, + Components, ComponentsContext, - ComponentsContextValue, + createComponentsContext, } from "@blocknote/react"; import "./style.css"; import { Form } from "./input/Form"; @@ -26,27 +27,43 @@ import { PanelTab } from "./panel/PanelTab"; import { PanelTextInput } from "./panel/PanelTextInput"; import { ComponentProps } from "react"; -export const components: ComponentsContextValue = { - Form, - TextInput, - Toolbar: Toolbar, - ToolbarSelect, - ToolbarButton, - Menu, - MenuTrigger, - MenuDropdown, - MenuDivider, - MenuLabel, - MenuItem, - Panel, - PanelButton, - PanelFileInput, - PanelTab, - PanelTextInput, - Popover, - PopoverContent, - PopoverTrigger, -}; +export const components: Components = createComponentsContext({ + FormattingToolbar: { + Root: Toolbar, + Button: ToolbarButton, + Select: ToolbarSelect, + }, + ImagePanel: { + Root: Panel, + Button: PanelButton, + FileInput: PanelFileInput, + TabPanel: PanelTab, + TextInput: PanelTextInput, + }, + LinkToolbar: { + Root: Toolbar, + Button: ToolbarButton, + }, + Generic: { + Form: { + Root: Form, + TextInput: TextInput, + }, + Menu: { + Root: Menu, + Trigger: MenuTrigger, + Dropdown: MenuDropdown, + Divider: MenuDivider, + Label: MenuLabel, + Item: MenuItem, + }, + Popover: { + Root: Popover, + Trigger: PopoverTrigger, + Content: PopoverContent, + }, + }, +}); export const BlockNoteView = ( props: ComponentProps diff --git a/packages/ariakit/src/input/Form.tsx b/packages/ariakit/src/input/Form.tsx index 1d3b3ab47..509382849 100644 --- a/packages/ariakit/src/input/Form.tsx +++ b/packages/ariakit/src/input/Form.tsx @@ -1,8 +1,8 @@ import * as Ariakit from "@ariakit/react"; -import { FormProps } from "@ariakit/react"; +import { ComponentProps } from "@blocknote/react"; -export const Form = (props: FormProps) => { +export const Form = (props: ComponentProps["Generic"]["Form"]["Root"]) => { const { ...rest } = props; return ; diff --git a/packages/ariakit/src/input/TextInput.tsx b/packages/ariakit/src/input/TextInput.tsx index dff48310d..4d90e1adf 100644 --- a/packages/ariakit/src/input/TextInput.tsx +++ b/packages/ariakit/src/input/TextInput.tsx @@ -1,8 +1,10 @@ import * as Ariakit from "@ariakit/react"; -import { TextInputProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; -export const TextInput = (props: TextInputProps) => { +export const TextInput = ( + props: ComponentProps["Generic"]["Form"]["TextInput"] +) => { const { icon, ...rest } = props; // TODO: support icon diff --git a/packages/ariakit/src/menu/Menu.tsx b/packages/ariakit/src/menu/Menu.tsx index 307c7a6d5..065781333 100644 --- a/packages/ariakit/src/menu/Menu.tsx +++ b/packages/ariakit/src/menu/Menu.tsx @@ -1,10 +1,8 @@ import * as Ariakit from "@ariakit/react"; -import { mergeCSSClasses } from "@blocknote/core"; -import { MenuItemProps, MenuProps, MenuLabelProps } from "@blocknote/react"; -import { HTMLAttributes, forwardRef } from "react"; +import { ComponentProps } from "@blocknote/react"; -export const Menu = (props: MenuProps) => { +export const Menu = (props: ComponentProps["Generic"]["Menu"]["Root"]) => { const { onOpenChange, position, ...rest } = props; return ( @@ -12,85 +10,65 @@ export const Menu = (props: MenuProps) => { placement={position} setOpen={onOpenChange} virtualFocus={true} - // activeId={} {...rest} /> ); }; -export const MenuDropdown = forwardRef< - HTMLDivElement, - HTMLAttributes ->((props, ref) => { +export const MenuDropdown = ( + props: ComponentProps["Generic"]["Menu"]["Dropdown"] +) => { const { className, children, ...rest } = props; return ( - + {children} ); -}); +}; -export const MenuItem = forwardRef( - (props, ref) => { - const { children, icon, checked, subTrigger, onClick } = props; +export const MenuItem = (props: ComponentProps["Generic"]["Menu"]["Item"]) => { + const { className, children, icon, checked, subTrigger, onClick } = props; - if (subTrigger) { - return ( - - {icon} - {children} - - {checked !== undefined && } - - ); - } + if (subTrigger) { return ( - + {icon} {children} + {checked !== undefined && } - + ); } -); + return ( + + {icon} + {children} + {checked !== undefined && } + + ); +}; -export const MenuLabel = forwardRef( - (props, ref) => { - const { className, children, ...rest } = props; +export const MenuLabel = ( + props: ComponentProps["Generic"]["Menu"]["Label"] +) => { + const { children, ...rest } = props; - return ( - - {children} - - ); - } -); + return {children}; +}; -export const MenuTrigger = forwardRef< - HTMLDivElement, - HTMLAttributes ->((props, ref) => { - const { className, children, ...rest } = props; +export const MenuTrigger = ( + props: ComponentProps["Generic"]["Menu"]["Trigger"] +) => { + const { children, ...rest } = props; return ( - + ); -}); +}; -export const MenuDivider = forwardRef< - HTMLHRElement, - Ariakit.MenuSeparatorProps ->((props, ref) => { - return ; -}); +export const MenuDivider = ( + props: ComponentProps["Generic"]["Menu"]["Divider"] +) => { + return ; +}; diff --git a/packages/ariakit/src/panel/Panel.tsx b/packages/ariakit/src/panel/Panel.tsx index 62e73c59b..622f98d8d 100644 --- a/packages/ariakit/src/panel/Panel.tsx +++ b/packages/ariakit/src/panel/Panel.tsx @@ -1,10 +1,10 @@ import * as Ariakit from "@ariakit/react"; -import { PanelProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; -export const Panel = (props: PanelProps) => { +export const Panel = (props: ComponentProps["ImagePanel"]["Root"]) => { return ( -
+
{ @@ -14,16 +14,16 @@ export const Panel = (props: PanelProps) => { }}> {/*{props.loading && }*/} - + {props.tabs.map((tab) => ( - + {tab.name} ))} {props.tabs.map((tab) => ( - + {tab.tabPanel} ))} diff --git a/packages/ariakit/src/panel/PanelButton.tsx b/packages/ariakit/src/panel/PanelButton.tsx index b9c04f511..eacfbe9ca 100644 --- a/packages/ariakit/src/panel/PanelButton.tsx +++ b/packages/ariakit/src/panel/PanelButton.tsx @@ -1,15 +1,12 @@ import * as Ariakit from "@ariakit/react"; -import { mergeCSSClasses } from "@blocknote/core"; -import { PanelButtonProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; -export const PanelButton = (props: PanelButtonProps) => { - const { children, className, ...rest } = props; +export const PanelButton = (props: ComponentProps["ImagePanel"]["Button"]) => { + const { className, children, ...rest } = props; return ( - + {children} ); diff --git a/packages/ariakit/src/panel/PanelFileInput.tsx b/packages/ariakit/src/panel/PanelFileInput.tsx index 34cc47a7a..68320550f 100644 --- a/packages/ariakit/src/panel/PanelFileInput.tsx +++ b/packages/ariakit/src/panel/PanelFileInput.tsx @@ -1,13 +1,15 @@ import * as Ariakit from "@ariakit/react"; -import { PanelFileInputProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; -export const PanelFileInput = (props: PanelFileInputProps) => ( +export const PanelFileInput = ( + props: ComponentProps["ImagePanel"]["FileInput"] +) => ( props.onChange?.(e.target.files![0])} placeholder={props.placeholder} diff --git a/packages/ariakit/src/panel/PanelTab.tsx b/packages/ariakit/src/panel/PanelTab.tsx index 8a689d075..efb205fb3 100644 --- a/packages/ariakit/src/panel/PanelTab.tsx +++ b/packages/ariakit/src/panel/PanelTab.tsx @@ -1,14 +1,7 @@ -import { mergeCSSClasses } from "@blocknote/core"; -import { PanelTabProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; -export const PanelTab = (props: PanelTabProps) => { - const { className, children, ...rest } = props; +export const PanelTab = (props: ComponentProps["ImagePanel"]["TabPanel"]) => { + const { children, ...rest } = props; - return ( -
- {children} -
- ); + return
{children}
; }; diff --git a/packages/ariakit/src/panel/PanelTextInput.tsx b/packages/ariakit/src/panel/PanelTextInput.tsx index a0e5163cf..f412f02e2 100644 --- a/packages/ariakit/src/panel/PanelTextInput.tsx +++ b/packages/ariakit/src/panel/PanelTextInput.tsx @@ -1,15 +1,16 @@ import * as Ariakit from "@ariakit/react"; -import { PanelTextInputProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; -export const PanelTextInput = (props: PanelTextInputProps) => { - const { children, ...rest } = props; +export const PanelTextInput = ( + props: ComponentProps["ImagePanel"]["TextInput"] +) => { + const { ...rest } = props; return ( diff --git a/packages/ariakit/src/popover/Popover.tsx b/packages/ariakit/src/popover/Popover.tsx index b853cc94e..281586562 100644 --- a/packages/ariakit/src/popover/Popover.tsx +++ b/packages/ariakit/src/popover/Popover.tsx @@ -1,27 +1,23 @@ import * as Ariakit from "@ariakit/react"; -import { mergeCSSClasses } from "@blocknote/core"; -import { forwardRef, HTMLAttributes } from "react"; +import { ComponentProps } from "@blocknote/react"; -export const PopoverTrigger = forwardRef< - HTMLButtonElement, - HTMLAttributes ->((props, ref) => { - const { className, children, ...rest } = props; +export const PopoverTrigger = ( + props: ComponentProps["Generic"]["Popover"]["Trigger"] +) => { + const { children, ...rest } = props; return ( ); -}); +}; -export const PopoverContent = forwardRef( - (props, ref) => -); +export const PopoverContent = ( + props: ComponentProps["Generic"]["Popover"]["Content"] +) => ; -export const Popover = (props: Ariakit.PopoverProviderProps) => ( - -); +export const Popover = ( + props: ComponentProps["Generic"]["Popover"]["Root"] +) => ; diff --git a/packages/ariakit/src/style.css b/packages/ariakit/src/style.css index 61493d7e0..33c53b9c1 100644 --- a/packages/ariakit/src/style.css +++ b/packages/ariakit/src/style.css @@ -1,136 +1,219 @@ -.button { +/* Formatting/Link Toolbar Root */ +.bn-formatting-toolbar-root, +.bn-link-toolbar-root { display: flex; - height: 2.5rem; - user-select: none; + max-width: 100%; align-items: center; + gap: 0.25rem; + overflow-x: auto; + border-radius: 0.5rem; + background-color: hsl(204 20% 100%); + padding: 0.25rem; + box-shadow: + 0 1px 3px 0 rgb(0 0 0 / 0.1), + 0 1px 2px -1px rgb(0 0 0 / 0.1); +} + +.bn-formatting-toolbar-root:where(.dark, .dark *), +.bn-link-toolbar-root:where(.dark, .dark *) { + 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); +} + +/* Formatting/Link Toolbar Button */ +.bn-formatting-toolbar-button, +.bn-link-toolbar-button { + --outline-offset: 0; + --outline: hsl(204 100% 40%); + --depth: 2px; + --padding-block: 0px; + --text: hsl(204 4% 0%); + --background: hsl(204 20% 100%); + --border: hsl(204 4% 0% / 13%); + --light: hsl(204 20% 100% / 20%); + --shadow: hsl(204 4% 0% / 10%); + outline-width: 2px; + outline-offset: var(--outline-offset); + outline-color: var(--outline); + user-select: none; white-space: nowrap; + display: flex; + height: 2.5rem; + align-items: center; + justify-content: center; + gap: 0.5rem; + border-radius: 0.5rem; border-style: none; - background-color: hsl(204 20% 100%); + background-color: var(--background); padding-left: 1rem; padding-right: 1rem; font-size: 1rem; line-height: 1.5rem; - color: hsl(204 4% 0%); + color: var(--text); text-decoration-line: none; - outline-width: 2px; - outline-offset: 2px; - outline-color: hsl(204 100% 40%); - --border: rgba(0, 0, 0, 0.1); - --highlight: rgba(255, 255, 255, 0.2); - --shadow: rgba(0, 0, 0, 0.1); - box-shadow: inset 0 0 0 1px var(--border), inset 0 2px 0 var(--highlight), - inset 0 -1px 0 var(--shadow), 0 1px 1px var(--shadow); - scroll-margin-left: 0.25rem; - scroll-margin-right: 0.25rem; - gap: 0.5rem; - border-radius: 0.25rem; - justify-content: space-between; - width: auto; + box-shadow: + inset 0 0 0 1px var(--border), + inset 0 2px 0 var(--light), + inset 0 -1px 0 var(--shadow), + 0 1px 1px var(--shadow); } -.button:hover { - background-color: hsl(204 20% 96%); +.bn-formatting-toolbar-button:not(:active):hover, +.bn-link-toolbar-button:not(:active):hover { + --border: hsl(204 4% 0% / 33%); } -.button[aria-disabled="true"] { +.bn-formatting-toolbar-button[aria-disabled="true"], +.bn-link-toolbar-button[aria-disabled="true"] { opacity: 0.5; } -.button[aria-expanded="true"] { - background-color: hsl(204 20% 96%); -} - -.button[data-focus-visible] { +.bn-formatting-toolbar-button[data-focus-visible], +.bn-link-toolbar-button[data-focus-visible] { outline-style: solid; } -.button:active, -.button[data-active] { - padding-top: 0.125rem; - box-shadow: inset 0 0 0 1px var(--border), inset 0 2px 0 var(--border); -} - -:is(.dark .button) { - background-color: hsl(204 20% 100% / 0.05); - color: hsl(204 20% 100%); - --shadow: rgba(0, 0, 0, 0.25); - --border: rgba(255, 255, 255, 0.1); - --highlight: rgba(255, 255, 255, 0.05); - box-shadow: inset 0 0 0 1px var(--border), inset 0 -1px 0 1px var(--shadow), - inset 0 1px 0 var(--highlight); +.bn-formatting-toolbar-button:active, +.bn-link-toolbar-button:active, +.bn-formatting-toolbar-button[data-active], +.bn-link-toolbar-button[data-active], +.bn-formatting-toolbar-button[data-selected="true"], +.bn-link-toolbar-button[data-selected="true"] { + padding-top: calc(var(--padding-block) + var(--depth)); + padding-bottom: calc(var(--padding-block) - var(--depth)); + box-shadow: + inset 0 0 0 1px var(--border), + inset 0 2px 0 var(--border); } -:is(.dark .button:hover) { - background-color: hsl(204 20% 100% / 0.1); +.bn-formatting-toolbar-button:where(.dark, .dark *), +.bn-link-toolbar-button:where(.dark, .dark *) { + --text: hsl(204 20% 100%); + --background: hsl(204 20% 100% / 5%); + --border: hsl(204 20% 100% / 10%); + --light: hsl(204 20% 100% / 5%); + --shadow: hsl(204 4% 0% / 25%); + box-shadow: + inset 0 0 0 1px var(--border), + inset 0 -1px 0 1px var(--shadow), + inset 0 1px 0 var(--light); } -:is(.dark .button)[aria-expanded="true"] { - background-color: hsl(204 20% 100% / 0.1); +.bn-formatting-toolbar-button:where(.dark, .dark *):not(:active):hover, +.bn-link-toolbar-button:where(.dark, .dark *):not(:active):hover { + --border: hsl(204 20% 100% / 25%); } -:is(.dark .button:active), -:is(.dark .button[data-active]) { - box-shadow: inset 0 0 0 1px var(--border), inset 0 1px 1px 1px var(--shadow); +.bn-formatting-toolbar-button:where(.dark, .dark *):active, +.bn-link-toolbar-button:where(.dark, .dark *):active, +.bn-formatting-toolbar-button:where(.dark, .dark *)[data-active], +.bn-link-toolbar-button:where(.dark, .dark *)[data-active], +.bn-formatting-toolbar-button:where(.dark, .dark *)[data-selected="true"], +.bn-link-toolbar-button:where(.dark, .dark *)[data-selected="true"] { + box-shadow: + inset 0 0 0 1px var(--border), + inset 0 1px 1px 1px var(--shadow); } -@media (min-width: 640px) { - .button { - gap: 0.5rem; - } +/* Formatting Toolbar Select */ +.bn-formatting-toolbar-root button[role="combobox"], +.bn-link-toolbar-root button[role="combobox"] { + --border: rgb(0 0 0/13%); + --highlight: rgb(255 255 255/20%); + --shadow: rgb(0 0 0/10%); + display: flex; + height: 2.5rem; + user-select: none; + align-items: center; + gap: 0.25rem; + white-space: nowrap; + border-radius: 0.5rem; + border-style: none; + background-color: hsl(204 20% 100%); + padding-left: 1rem; + padding-right: 1rem; + font-size: 1rem; + line-height: 1.5rem; + color: hsl(204 4% 0%); + text-decoration-line: none; + outline-width: 2px; + outline-offset: 2px; + outline-color: hsl(204 100% 40%); + box-shadow: + inset 0 0 0 1px var(--border), + inset 0 2px 0 var(--highlight), + inset 0 -1px 0 var(--shadow), + 0 1px 1px var(--shadow); + width: 200px; + justify-content: space-between; } -.secondary { - background-color: transparent; - color: currentColor; - box-shadow: none; -} -.secondary:hover { - background-color: hsl(204 4% 0% / 0.05); +.bn-formatting-toolbar-root button[role="combobox"]:where(.dark, .dark *), +.bn-link-toolbar-root button[role="combobox"]:where(.dark, .dark *) { + --border: rgb(255 255 255/10%); + --highlight: rgb(255 255 255/5%); + --shadow: rgb(0 0 0/25%); + background-color: hsl(204 20% 100% / 0.05); + color: hsl(204 20% 100%); + box-shadow: + inset 0 0 0 1px var(--border), + inset 0 -1px 0 1px var(--shadow), + inset 0 1px 0 var(--highlight); } -:is(.dark .secondary) { - background-color: transparent; - box-shadow: none; +.bn-formatting-toolbar-root button[role="combobox"]:not(:active):hover, +.bn-link-toolbar-root button[role="combobox"]:not(:active):hover { + --border: rgb(0 0 0/33%); } -:is(.dark .secondary:hover) { - background-color: hsl(204 20% 100% / 0.05); +.bn-formatting-toolbar-root button[role="combobox"]:where(.dark, .dark *):not(:active):hover, +.bn-link-toolbar-root button[role="combobox"]:where(.dark, .dark *):not(:active):hover { + --border: rgb(255 255 255/25%); } -:is(.dark .secondary:active), -:is(.dark .secondary[data-active]) { - box-shadow: none; +.bn-formatting-toolbar-root button[role="combobox"][aria-disabled="true"], +.bn-link-toolbar-root button[role="combobox"][aria-disabled="true"] { + opacity: 0.5; } -.bn-toolbar { - display: flex; - max-width: 100%; - align-items: center; - gap: 0.25rem; - overflow-x: auto; - border-radius: 0.5rem; - background-color: hsl(204 20% 100%); - padding: 0.25rem; - box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); +.bn-formatting-toolbar-root button[role="combobox"][data-focus-visible], +.bn-link-toolbar-root button[role="combobox"][data-focus-visible] { + outline-style: solid; } -:is(.dark .bn-toolbar) { - 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); +.bn-formatting-toolbar-root button:active, +.bn-link-toolbar-root button:active, +.bn-formatting-toolbar-root button[data-active], +.bn-link-toolbar-root button[data-active]{ + padding-top: 0.125rem; + box-shadow: + inset 0 0 0 1px var(--border), + inset 0 2px 0 var(--border); } -.separator { - height: 2rem; - border-right-width: 1px; - border-color: hsl(204 20% 88%); +@media (min-width: 640px) { + .bn-formatting-toolbar-root button, + .bn-link-toolbar-root button { + gap: 0.5rem; + } } -:is(.dark .separator) { - border-color: hsl(204 4% 28%); +.bn-formatting-toolbar-root button:active:where(.dark, .dark *), +.bn-link-toolbar-root button:active:where(.dark, .dark *), +.bn-formatting-toolbar-root button[data-active]:where(.dark, .dark *), +.bn-link-toolbar-root button[data-active]:where(.dark, .dark *){ + box-shadow: + inset 0 0 0 1px var(--border), + inset 0 1px 1px 1px var(--shadow); } -.popover { +/* Select, Menu, and Popover Dropdown */ +.bn-formatting-toolbar-select, +.bn-generic-menu-dropdown, +.bn-generic-popover-content { z-index: 50; display: flex; max-height: min(var(--popover-available-height, 300px), 300px); @@ -144,24 +227,49 @@ background-color: hsl(204 20% 100%); padding: 0.5rem; color: hsl(204 4% 0%); - box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + box-shadow: + 0 10px 15px -3px rgb(0 0 0 / 0.1), + 0 4px 6px -4px rgb(0 0 0 / 0.1); } -.popover:focus-visible, -.popover[data-focus-visible] { +.bn-formatting-toolbar-select:focus-visible, +.bn-generic-menu-dropdown:focus-visible, +.bn-generic-popover-content:focus-visible, +.bn-formatting-toolbar-select[data-focus-visible], +.bn-generic-menu-dropdown[data-focus-visible], +.bn-generic-popover-content[data-focus-visible] { outline: 2px solid hsl(204 100% 40%); outline-offset: -1px; } -:is(.dark .popover) { +.bn-formatting-toolbar-select:where(.dark, .dark *), +.bn-generic-menu-dropdown:where(.dark, .dark *), +.bn-generic-popover-content:where(.dark, .dark *) { border-color: hsl(204 4% 24%); background-color: hsl(204 4% 16%); color: hsl(204 20% 100%); - box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.25), - 0 4px 6px -4px rgb(0 0 0 / 0.1); + box-shadow: + 0 10px 15px -3px rgb(0 0 0 / 0.25), + 0 4px 6px -4px rgb(0 0 0 / 0.1); } -.select-item { +/* Menu Label */ +.bn-generic-menu-label { + cursor: default; + padding: 0.5rem; + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 500; + opacity: 0.6; +} + +.bn-generic-menu-label + * { + scroll-margin-top: 2.5rem; +} + +/* Select and Menu Item */ +.bn-formatting-toolbar-select > div, +.bn-generic-menu-item { display: flex; cursor: default; scroll-margin: 0.5rem; @@ -170,78 +278,45 @@ border-radius: 0.25rem; padding: 0.5rem; outline: none !important; - padding-right: 2rem; } -.select-item[aria-disabled="true"] { +.bn-formatting-toolbar-select > div[aria-disabled="true"], +.bn-generic-menu-item[aria-disabled="true"] { opacity: 0.5; } -.select-item[data-active-item] { +.bn-formatting-toolbar-select > div[data-active-item], +.bn-generic-menu-item[data-active-item] { background-color: hsl(204 100% 40%); 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%); +/* Image Panel Root */ +.bn-image-panel-root { + display: flex; + flex-direction: column; + gap: 0.5rem; + border-radius: 0.5rem; + background-color: hsl(204 20% 100%); + padding: 0.5rem; 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%); + 0 1px 3px 0 rgb(0 0 0 / 0.1), + 0 1px 2px -1px rgb(0 0 0 / 0.1); } -:is(.dark .bn-image-panel) { +.bn-image-panel-root:where(.dark, .dark *) { 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 { +.bn-image-panel-root > div { display: flex; gap: 0.5rem; } -.tab { +.bn-image-panel-root > div > button { display: flex; height: 2.5rem; user-select: none; @@ -261,139 +336,280 @@ outline-color: hsl(204 100% 40%); } -.tab:hover { +.bn-image-panel-root > div > button:hover { background-color: hsl(204 4% 0% / 7.5%); } -.tab[aria-disabled="true"] { +.bn-image-panel-root > div > button[aria-disabled="true"] { opacity: 0.5; } -.tab[aria-selected="true"] { +.bn-image-panel-root > div > button[aria-selected="true"] { background-color: hsl(204 100% 40%); color: hsl(204 20% 100%); } -.tab:hover[aria-selected="true"] { +.bn-image-panel-root > div > button:hover[aria-selected="true"] { background-color: hsl(204 100% 32%); } -.tab[data-focus-visible] { +.bn-image-panel-root > div > button[data-focus-visible] { outline-style: solid; } -.tab:active, -.tab[data-active] { +.bn-image-panel-root > div > button:active, +.bn-image-panel-root > div > button[data-active] { padding-top: 0.125rem; } -:is(.dark .tab:hover) { +.bn-image-panel-root > div > button:hover:where(.dark, .dark *) { background-color: hsl(204 20% 100% / 0.1); } -:is(.dark .tab[aria-selected="true"]) { +.bn-image-panel-root > div > button[aria-selected="true"]:where(.dark, .dark *) { background-color: hsl(204 100% 40%); color: hsl(204 20% 100%); } -:is(.dark .tab:hover[aria-selected="true"]) { +.bn-image-panel-root > div > button:hover[aria-selected="true"]:where(.dark, .dark *) { background-color: hsl(204 100% 32%); } -.panel { - padding: 0.5rem; -} - -.bn-menu { - position: relative; - z-index: 50; +.bn-image-panel-tab-panel { + align-items: center; display: flex; - max-height: var(--popover-available-height); - min-width: 180px; + gap: 0.5rem; + width: 500px; flex-direction: column; - overscroll-behavior: contain; border-radius: 0.5rem; - border-width: 1px; - border-style: solid; - border-color: hsl(204 20% 88%); background-color: hsl(204 20% 100%); - padding: 0.5rem; +} + +.bn-image-panel-text-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: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); - outline: none !important; - overflow: visible; + box-shadow: + inset 0 0 0 1px rgba(0 0 0 / 0.1), + inset 0 2px 5px 0 rgba(0 0 0 / 0.05); } -:is(.dark .bn-menu) { - border-color: hsl(204 4% 24%); - background-color: hsl(204 4% 16%); - color: hsl(204 20% 100%); - box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.25), - 0 4px 6px -4px rgb(0 0 0 / 0.1); +.bn-image-panel-text-input::placeholder { + color: hsl(204 4% 0% / 0.6); } -.bn-menu-item { - display: flex; - cursor: default; - scroll-margin: 0.5rem; - align-items: center; - gap: 0.5rem; - border-radius: 0.25rem; - padding: 0.5rem; - outline: none !important; +.bn-image-panel-text-input:hover { + background-color: hsl(204 20% 94%); } -.bn-menu-item[aria-disabled="true"] { - opacity: 0.25; +.bn-image-panel-text-input:focus-visible, +.bn-image-panel-text-input[data-focus-visible] { + outline: 2px solid hsl(204 100% 40%); + outline-offset: -1px; } -.bn-menu-item[data-active-item] { - background-color: hsl(204 100% 40%); +.bn-image-panel-text-input:where(.dark, .dark *) { + 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); } -.bn-menu-item:active, -.bn-menu-item[data-active] { - background-color: hsl(204 100% 32%); - padding-top: 9px; - padding-bottom: 7px; +.bn-image-panel-text-input:where(.dark, .dark *)::placeholder { + color: hsl(204 20% 100% / 46%); } -.bn-menu:not(:focus) .bn-menu-item:not(:focus)[aria-expanded="true"] { - background-color: hsl(204 4% 0% / 7.5%); - color: currentColor; +.bn-image-panel-text-input:hover:where(.dark, .dark *) { + background-color: hsl(204 4% 8%); } -:is( - .dark .bn-menu:not(:focus) .bn-menu-item:not(:focus)[aria-expanded="true"] - ) { - background-color: hsl(204 20% 100% / 0.1); +.bn-image-panel-file-input { + border-radius: 0.375rem; + border-style: none; + box-shadow: + inset 0 0 0 1px rgba(0 0 0 / 0.1), + inset 0 2px 5px 0 rgba(0 0 0 / 0.05); + padding: 0.5rem; + width: 100%; } -.bn-menu-item .label { - flex: 1 1 0%; +.bn-image-panel-button { + --border: rgb(0 0 0/13%); + --highlight: rgb(255 255 255/20%); + --shadow: rgb(0 0 0/10%); + display: flex; + height: 2.5rem; + user-select: none; + align-items: center; + justify-content: center; + gap: 0.25rem; + white-space: nowrap; + border-style: none; + background-color: hsl(204 20% 100%); + padding-left: 1rem; + padding-right: 1rem; + font-size: 1rem; + line-height: 1.5rem; + color: hsl(204 4% 0%); + text-decoration-line: none; + outline-width: 2px; + outline-offset: 2px; + outline-color: hsl(204 100% 40%); + box-shadow: + inset 0 0 0 1px var(--border), + inset 0 2px 0 var(--highlight), + inset 0 -1px 0 var(--shadow), + 0 1px 1px var(--shadow); + width: 60%; + border-radius: 0.375rem; } -.bn-menu-label { - cursor: default; - padding: 0.5rem; - padding-left: 0.75rem; - padding-right: 0.75rem; - font-size: 0.875rem; - line-height: 1.25rem; - font-weight: 500; +.bn-image-panel-button:where(.dark, .dark *) { + --border: rgb(255 255 255/10%); + --highlight: rgb(255 255 255/5%); + --shadow: rgb(0 0 0/25%); + background-color: hsl(204 20% 100% / 0.05); + color: hsl(204 20% 100%); + box-shadow: + inset 0 0 0 1px var(--border), + inset 0 -1px 0 1px var(--shadow), + inset 0 1px 0 var(--highlight); +} + +.bn-image-panel-button:not(:active):hover { + --border: rgb(0 0 0/33%); +} + +.bn-image-panel-button:where(.dark, .dark *):not(:active):hover { + --border: rgb(255 255 255/25%); +} + +.bn-image-panel-button[aria-disabled="true"] { 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); +.bn-image-panel-button[data-focus-visible] { + outline-style: solid; } +.bn-image-panel-button:active, +.bn-image-panel-button[data-active] { + padding-top: 0.125rem; + box-shadow: + inset 0 0 0 1px var(--border), + inset 0 2px 0 var(--border); +} + + +/* Mantine copied styles */ +/*.bn-formatting-toolbar-root,*/ +/*.bn-link-toolbar-root {*/ +/* background-color: var(--bn-colors-menu-background);*/ +/* border: var(--bn-border);*/ +/* border-radius: var(--bn-border-radius-medium);*/ +/* box-shadow: var(--bn-shadow-medium);*/ +/* display: flex;*/ +/* flex-wrap: nowrap;*/ +/* font-size: 0.75rem;*/ +/* font-weight: 600;*/ +/* gap: 2px;*/ +/* padding: 2px;*/ +/* width: fit-content;*/ +/*}*/ + +/*.bn-formatting-toolbar-root:empty,*/ +/*.bn-link-toolbar-root:empty {*/ +/* display: none;*/ +/*}*/ + +/*.bn-formatting-toolbar-root button,*/ +/*.bn-formatting-link-root button {*/ +/* align-items: center;*/ +/* background-color: var(--bn-colors-menu-background);*/ +/* border: none;*/ +/* border-radius: var(--bn-border-radius-small);*/ +/* color: var(--bn-colors-menu-text);*/ +/* display: flex;*/ +/* height: 1.875rem;*/ +/* justify-content: center;*/ +/* width: 1.875rem;*/ +/*}*/ + +/*.bn-formatting-toolbar-root svg,*/ +/*.bn-formatting-link-root svg {*/ +/* height: 1rem;*/ +/* width: 1rem;*/ +/*}*/ + +/*.bn-formatting-toolbar-root button:hover,*/ +/*.bn-formatting-link-root button:hover {*/ +/* background-color: var(--bn-colors-hovered-background);*/ +/* border: none;*/ +/* color: var(--bn-colors-hovered-text);*/ +/*}*/ + +/*.bn-formatting-toolbar-root button[data-selected],*/ +/*.bn-formatting-link-root button[data-selected] {*/ +/* background-color: var(--bn-colors-selected-background);*/ +/* border: none;*/ +/* color: var(--bn-colors-selected-text);*/ +/*}*/ + +/*.bn-formatting-toolbar-root button[disabled],*/ +/*.bn-formatting-link-root button[disabled] {*/ +/* background-color: var(--bn-colors-disabled-background);*/ +/* border: none;*/ +/* color: var(--bn-colors-disabled-text);*/ +/*}*/ + + +/*.bn-formatting-toolbar-root button[role="combobox"],*/ +/*.bn-formatting-link-root button[role="combobox"] {*/ +/* gap: 0.625rem;*/ +/* padding-inline: calc(0.875rem / 1.5);*/ +/* width: fit-content;*/ +/*}*/ + +/*.bn-formatting-toolbar-root button[role="combobox"] span svg,*/ +/*.bn-formatting-link-root button[role="combobox"] span svg {*/ +/* height: 0.75rem;*/ +/* width: 0.75rem;*/ +/*}*/ + +/*.bn-formatting-toolbar-root [data-dialog],*/ +/*.bn-formatting-link-root [data-dialog] {*/ +/* background-color: var(--bn-colors-menu-background);*/ +/* border: var(--bn-border);*/ +/* border-radius: var(--bn-border-radius-medium);*/ +/* box-shadow: var(--bn-shadow-medium);*/ +/* color: var(--bn-colors-menu-text);*/ +/* gap: 4px;*/ +/* !*min-width: 145px;*!*/ +/* padding: 2px;*/ +/* width: max-content;*/ +/*}*/ + +/*.bn-formatting-toolbar-root [data-dialog] > div,*/ +/*.bn-formatting-link-root [data-dialog] > div {*/ +/* align-items: center;*/ +/* border-radius: var(--bn-border-radius-small);*/ +/* cursor: pointer;*/ +/* display: flex;*/ +/* font-weight: normal;*/ +/* height: 30px;*/ +/* justify-content: start;*/ +/*}*/ + +/*.bn-formatting-toolbar-root [data-dialog] > div:hover,*/ +/*.bn-formatting-link-root [data-dialog] > div:hover {*/ +/* background-color: var(--bn-colors-hovered-background);*/ +/*}*/ \ No newline at end of file diff --git a/packages/ariakit/src/toolbar/Toolbar.tsx b/packages/ariakit/src/toolbar/Toolbar.tsx index 203d8ae3f..821be0369 100644 --- a/packages/ariakit/src/toolbar/Toolbar.tsx +++ b/packages/ariakit/src/toolbar/Toolbar.tsx @@ -1,23 +1,12 @@ import * as Ariakit from "@ariakit/react"; -import { mergeCSSClasses } from "@blocknote/core"; -import { forwardRef, HTMLAttributes } from "react"; +import { ComponentProps } from "@blocknote/react"; -export const Toolbar = forwardRef< - HTMLDivElement, - HTMLAttributes ->((props, ref) => { - const { className, children, ...rest } = props; +type ToolbarProps = ComponentProps["FormattingToolbar"]["Root"] & + ComponentProps["LinkToolbar"]["Root"]; - return ( - - {children} - - ); -}); +export const Toolbar = (props: ToolbarProps) => { + const { children, ...rest } = props; -// export const ToolbarButton = forwardRef( -// + return {children}; +}; diff --git a/packages/ariakit/src/toolbar/ToolbarButton.tsx b/packages/ariakit/src/toolbar/ToolbarButton.tsx index c5405d5e7..ccc7439e5 100644 --- a/packages/ariakit/src/toolbar/ToolbarButton.tsx +++ b/packages/ariakit/src/toolbar/ToolbarButton.tsx @@ -1,8 +1,12 @@ import * as Ariakit from "@ariakit/react"; import { isSafari } from "@blocknote/core"; -import { ToolbarButtonProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; import { forwardRef } from "react"; +import { useToolbarContext } from "@ariakit/react"; + +type ToolbarButtonProps = ComponentProps["FormattingToolbar"]["Button"] & + ComponentProps["LinkToolbar"]["Button"]; /** * Helper for basic buttons that show in the formatting toolbar. @@ -10,9 +14,15 @@ import { forwardRef } from "react"; // TODO: implement tooltip export const ToolbarButton = forwardRef( (props, ref) => { + const toolbar = useToolbarContext(); + + if (!toolbar) { + return ; + } + return ( { @@ -34,38 +44,38 @@ export const ToolbarButton = forwardRef( ); - return ( - - { - if (isSafari()) { - (e.currentTarget as HTMLButtonElement).focus(); - } - }} - onClick={props.onClick} - data-selected={props.isSelected ? "true" : undefined} - data-test={ - props.mainTooltip.slice(0, 1).toLowerCase() + - props.mainTooltip.replace(/\s+/g, "").slice(1) - } - // size={"xs"} - disabled={props.isDisabled || false} - ref={ref}> - }> - {props.icon} - {props.children} - - -
{props.mainTooltip}
- {props.secondaryTooltip &&
{props.secondaryTooltip}
} -
-
- ); + // return ( + // + // { + // if (isSafari()) { + // (e.currentTarget as HTMLButtonElement).focus(); + // } + // }} + // onClick={props.onClick} + // data-selected={props.isSelected ? "true" : undefined} + // data-test={ + // props.mainTooltip.slice(0, 1).toLowerCase() + + // props.mainTooltip.replace(/\s+/g, "").slice(1) + // } + // // size={"xs"} + // disabled={props.isDisabled || false} + // ref={ref}> + // }> + // {props.icon} + // {props.children} + // + // + //
{props.mainTooltip}
+ // {props.secondaryTooltip &&
{props.secondaryTooltip}
} + //
+ //
+ // ); } ); diff --git a/packages/ariakit/src/toolbar/ToolbarSelect.tsx b/packages/ariakit/src/toolbar/ToolbarSelect.tsx index 41223ad86..194ccbdc9 100644 --- a/packages/ariakit/src/toolbar/ToolbarSelect.tsx +++ b/packages/ariakit/src/toolbar/ToolbarSelect.tsx @@ -1,8 +1,10 @@ import * as Ariakit from "@ariakit/react"; -import { ToolbarSelectProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; -export function ToolbarSelect(props: ToolbarSelectProps) { +export function ToolbarSelect( + props: ComponentProps["FormattingToolbar"]["Select"] +) { const selectedItem = props.items.filter((p) => p.isSelected)[0]; const setValue = (value: string) => { @@ -13,16 +15,12 @@ export function ToolbarSelect(props: ToolbarSelectProps) { }> {selectedItem.icon} {selectedItem.text} - + {props.items.map((option) => ( - + {option.icon} {option.text} diff --git a/packages/mantine/src/index.tsx b/packages/mantine/src/index.tsx index 76a4bd4ab..b6deebaa3 100644 --- a/packages/mantine/src/index.tsx +++ b/packages/mantine/src/index.tsx @@ -1,8 +1,7 @@ -import { Loader, PopoverDropdown, PopoverTarget } from "@mantine/core"; import { BlockNoteViewRaw, + Components, ComponentsContext, - ComponentsContextValue, } from "@blocknote/react"; import { ComponentProps } from "react"; import { TextInput } from "./input/TextInput"; @@ -14,7 +13,7 @@ import { MenuLabel, MenuTrigger, } from "./menu/Menu"; -import { Popover } from "./popover/Popover"; +import { Popover, PopoverContent, PopoverTrigger } from "./popover/Popover"; import { Toolbar } from "./toolbar/Toolbar"; import { ToolbarButton } from "./toolbar/ToolbarButton"; import { ToolbarSelect } from "./toolbar/ToolbarSelect"; @@ -23,31 +22,45 @@ import { PanelButton } from "./panel/PanelButton"; import { PanelFileInput } from "./panel/PanelFileInput"; import { PanelTab } from "./panel/PanelTab"; import { PanelTextInput } from "./panel/PanelTextInput"; +import { createComponentsContext } from "../../react/src"; -export const components: ComponentsContextValue = { - Toolbar, - ToolbarButton, - ToolbarSelect, - Menu, - MenuTrigger, - MenuDropdown, - MenuDivider, - MenuLabel, - MenuItem, - Panel, - PanelButton, - PanelFileInput, - PanelTab, - PanelTextInput, - Popover: Popover, - PopoverContent: PopoverDropdown, - PopoverTrigger: PopoverTarget, - TextInput, - Form: (props) =>
, - SuggestionMenuLoader: () => ( - - ), -}; +export const components: Components = createComponentsContext({ + FormattingToolbar: { + Root: Toolbar, + Button: ToolbarButton, + Select: ToolbarSelect, + }, + ImagePanel: { + Root: Panel, + Button: PanelButton, + FileInput: PanelFileInput, + TabPanel: PanelTab, + TextInput: PanelTextInput, + }, + LinkToolbar: { + Root: Toolbar, + Button: ToolbarButton, + }, + Generic: { + Form: { + Root: (props) =>
{props.children}
, + TextInput: TextInput, + }, + Menu: { + Root: Menu, + Trigger: MenuTrigger, + Dropdown: MenuDropdown, + Divider: MenuDivider, + Label: MenuLabel, + Item: MenuItem, + }, + Popover: { + Root: Popover, + Trigger: PopoverTrigger, + Content: PopoverContent, + }, + }, +}); export const BlockNoteView = ( props: ComponentProps diff --git a/packages/mantine/src/input/TextInput.tsx b/packages/mantine/src/input/TextInput.tsx index 22501d210..047436f9c 100644 --- a/packages/mantine/src/input/TextInput.tsx +++ b/packages/mantine/src/input/TextInput.tsx @@ -1,8 +1,10 @@ import * as Mantine from "@mantine/core"; -import { TextInputProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; -export const TextInput = (props: TextInputProps) => { +export const TextInput = ( + props: ComponentProps["Generic"]["Form"]["TextInput"] +) => { const { icon, ...rest } = props; return ; }; diff --git a/packages/mantine/src/mantineStyles.css b/packages/mantine/src/mantineStyles.css new file mode 100644 index 000000000..aa511155b --- /dev/null +++ b/packages/mantine/src/mantineStyles.css @@ -0,0 +1,138 @@ +/* Based on https://github.com/orgs/mantinedev/discussions/5685 */ + +/* We need all the Mantine styles except the global styles, so unfortunately our + only option is to import all the component styles separately. Could consider + importing only styles for components used in BlockNote in the future. */ +/* Files list: https://mantine.dev/styles/css-files-list/ */ +@import url("../../../node_modules/@mantine/core/styles/ScrollArea.css"); +@import url("../../../node_modules/@mantine/core/styles/UnstyledButton.css"); +@import url("../../../node_modules/@mantine/core/styles/VisuallyHidden.css"); +@import url("../../../node_modules/@mantine/core/styles/Paper.css"); +@import url("../../../node_modules/@mantine/core/styles/Popover.css"); +@import url("../../../node_modules/@mantine/core/styles/CloseButton.css"); +@import url("../../../node_modules/@mantine/core/styles/Group.css"); +@import url("../../../node_modules/@mantine/core/styles/Loader.css"); +@import url("../../../node_modules/@mantine/core/styles/Overlay.css"); +@import url("../../../node_modules/@mantine/core/styles/ModalBase.css"); +@import url("../../../node_modules/@mantine/core/styles/Input.css"); +@import url("../../../node_modules/@mantine/core/styles/Flex.css"); + +@import url("../../../node_modules/@mantine/core/styles/Accordion.css"); +@import url("../../../node_modules/@mantine/core/styles/ActionIcon.css"); +@import url("../../../node_modules/@mantine/core/styles/Affix.css"); +@import url("../../../node_modules/@mantine/core/styles/Alert.css"); +@import url("../../../node_modules/@mantine/core/styles/Anchor.css"); +@import url("../../../node_modules/@mantine/core/styles/AspectRatio.css"); +@import url("../../../node_modules/@mantine/core/styles/AppShell.css"); +@import url("../../../node_modules/@mantine/core/styles/Avatar.css"); +@import url("../../../node_modules/@mantine/core/styles/Badge.css"); +@import url("../../../node_modules/@mantine/core/styles/BackgroundImage.css"); +@import url("../../../node_modules/@mantine/core/styles/Blockquote.css"); +@import url("../../../node_modules/@mantine/core/styles/Breadcrumbs.css"); +@import url("../../../node_modules/@mantine/core/styles/Button.css"); +@import url("../../../node_modules/@mantine/core/styles/Burger.css"); +@import url("../../../node_modules/@mantine/core/styles/Card.css"); +@import url("../../../node_modules/@mantine/core/styles/Center.css"); +@import url("../../../node_modules/@mantine/core/styles/Checkbox.css"); +@import url("../../../node_modules/@mantine/core/styles/Chip.css"); +@import url("../../../node_modules/@mantine/core/styles/Code.css"); +@import url("../../../node_modules/@mantine/core/styles/ColorInput.css"); +@import url("../../../node_modules/@mantine/core/styles/ColorPicker.css"); +@import url("../../../node_modules/@mantine/core/styles/ColorSwatch.css"); +@import url("../../../node_modules/@mantine/core/styles/Combobox.css"); +@import url("../../../node_modules/@mantine/core/styles/Container.css"); +@import url("../../../node_modules/@mantine/core/styles/Dialog.css"); +@import url("../../../node_modules/@mantine/core/styles/Divider.css"); +@import url("../../../node_modules/@mantine/core/styles/Drawer.css"); +@import url("../../../node_modules/@mantine/core/styles/Fieldset.css"); +@import url("../../../node_modules/@mantine/core/styles/Grid.css"); +@import url("../../../node_modules/@mantine/core/styles/Image.css"); +@import url("../../../node_modules/@mantine/core/styles/Indicator.css"); +@import url("../../../node_modules/@mantine/core/styles/InlineInput.css"); +@import url("../../../node_modules/@mantine/core/styles/Kbd.css"); +@import url("../../../node_modules/@mantine/core/styles/List.css"); +@import url("../../../node_modules/@mantine/core/styles/LoadingOverlay.css"); +@import url("../../../node_modules/@mantine/core/styles/Mark.css"); +@import url("../../../node_modules/@mantine/core/styles/Menu.css"); +@import url("../../../node_modules/@mantine/core/styles/Modal.css"); +@import url("../../../node_modules/@mantine/core/styles/NavLink.css"); +@import url("../../../node_modules/@mantine/core/styles/Notification.css"); +@import url("../../../node_modules/@mantine/core/styles/NumberInput.css"); +@import url("../../../node_modules/@mantine/core/styles/Pagination.css"); +@import url("../../../node_modules/@mantine/core/styles/Pill.css"); +@import url("../../../node_modules/@mantine/core/styles/PasswordInput.css"); +@import url("../../../node_modules/@mantine/core/styles/PillsInput.css"); +@import url("../../../node_modules/@mantine/core/styles/PinInput.css"); +@import url("../../../node_modules/@mantine/core/styles/Progress.css"); +@import url("../../../node_modules/@mantine/core/styles/Radio.css"); +@import url("../../../node_modules/@mantine/core/styles/Rating.css"); +@import url("../../../node_modules/@mantine/core/styles/RingProgress.css"); +@import url("../../../node_modules/@mantine/core/styles/SegmentedControl.css"); +@import url("../../../node_modules/@mantine/core/styles/SimpleGrid.css"); +@import url("../../../node_modules/@mantine/core/styles/Skeleton.css"); +@import url("../../../node_modules/@mantine/core/styles/Slider.css"); +@import url("../../../node_modules/@mantine/core/styles/Spoiler.css"); +@import url("../../../node_modules/@mantine/core/styles/Stack.css"); +@import url("../../../node_modules/@mantine/core/styles/Stepper.css"); +@import url("../../../node_modules/@mantine/core/styles/Switch.css"); +@import url("../../../node_modules/@mantine/core/styles/Table.css"); +@import url("../../../node_modules/@mantine/core/styles/Tabs.css"); +@import url("../../../node_modules/@mantine/core/styles/Text.css"); +@import url("../../../node_modules/@mantine/core/styles/ThemeIcon.css"); +@import url("../../../node_modules/@mantine/core/styles/Timeline.css"); +@import url("../../../node_modules/@mantine/core/styles/Title.css"); +@import url("../../../node_modules/@mantine/core/styles/Tooltip.css"); +@import url("../../../node_modules/@mantine/core/styles/TypographyStylesProvider.css"); + +/* Mantine global styles, scoped to bn-container */ +/* Based on @mantine/core/styles/global.css + (src: https://github.com/mantinedev/mantine/blob/master/packages/%40mantine/core/src/core/MantineProvider/global.css) + but with styles set on `body` and `html` instead set on `bn-container`, as + well as other minor changes. */ +.bn-container *, .bn-container :after, .bn-container :before { + box-sizing: border-box +} + +.bn-container button, +.bn-container select { + text-transform: none +} + +@media screen and (max-device-width: 500px) { + .bn-container { + -webkit-text-size-adjust: 100% + } +} + +@media (prefers-reduced-motion: reduce) { + .bn-container [data-respect-reduced-motion] [data-reduce-motion] { + animation: none; + transition: none + } +} + +.bn-container [data-mantine-color-scheme=dark] .mantine-dark-hidden, .bn-container [data-mantine-color-scheme=light] .mantine-light-hidden { + display: none +} + +.bn-container .mantine-focus-auto:focus-visible { + outline: calc(.125rem * var(--mantine-scale)) solid var(--mantine-primary-color-filled); + outline-offset: calc(.125rem * var(--mantine-scale)) +} + +.bn-container .mantine-focus-always:focus { + outline: calc(.125rem * var(--mantine-scale)) solid var(--mantine-primary-color-filled); + outline-offset: calc(.125rem * var(--mantine-scale)) +} + +.bn-container .mantine-focus-never:focus { + outline: none +} + +.bn-container .mantine-active:active { + transform: translateY(calc(.0625rem * var(--mantine-scale))) +} + +.bn-container[dir=rtl] .mantine-rotate-rtl { + transform: rotate(180deg) +} \ No newline at end of file diff --git a/packages/mantine/src/menu/Menu.tsx b/packages/mantine/src/menu/Menu.tsx index b2cb2baff..0c7bfb6d0 100644 --- a/packages/mantine/src/menu/Menu.tsx +++ b/packages/mantine/src/menu/Menu.tsx @@ -1,21 +1,14 @@ import * as Mantine from "@mantine/core"; -import { - MenuDropdownProps, - MenuItemProps, - MenuProps, - MenuTriggerProps, -} from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; import { createContext, - forwardRef, useCallback, useContext, useRef, useState, } from "react"; import { HiChevronRight } from "react-icons/hi"; -import { MenuDividerProps } from "@mantine/core"; const SubMenuContext = createContext< | { @@ -27,9 +20,7 @@ const SubMenuContext = createContext< | undefined >(undefined); -const SubMenu = (props: MenuProps) => { - const { sub, onOpenChange, ...rest } = props; - +const SubMenu = (props: ComponentProps["Generic"]["Menu"]["Root"]) => { const [opened, setOpened] = useState(false); const menuCloseTimer = useRef | undefined>(); @@ -63,17 +54,16 @@ const SubMenu = (props: MenuProps) => { opened={opened} withinPortal={false} middlewares={{ flip: true, shift: true, inline: false, size: true }} - onClose={() => onOpenChange?.(false)} - onOpen={() => onOpenChange?.(true)} + onClose={() => props.onOpenChange?.(false)} + onOpen={() => props.onOpenChange?.(true)} closeOnItemClick={false} - {...rest} position="right" /> ); }; -export const Menu = (props: MenuProps) => { +export const Menu = (props: ComponentProps["Generic"]["Menu"]["Root"]) => { const { sub, onOpenChange, ...rest } = props; if (sub) { @@ -83,7 +73,7 @@ export const Menu = (props: MenuProps) => { return ( onOpenChange?.(false)} onOpen={() => onOpenChange?.(true)} closeOnItemClick={false} @@ -93,73 +83,56 @@ export const Menu = (props: MenuProps) => { ); }; -export const MenuItem = forwardRef( - (props, ref) => { - const { icon, checked, subTrigger, ...rest } = props; - const ctx = useContext(SubMenuContext); - - const onMouseLeave = subTrigger ? ctx!.onTriggerMouseLeave : undefined; - const onMouseOver = subTrigger ? ctx!.onTriggerMouseOver : undefined; - - return ( - - {checked ? ( - - ) : checked === false ? ( -
- ) : undefined} - {subTrigger && } - - } - onMouseOver={onMouseOver} - onMouseLeave={onMouseLeave} - {...rest} - ref={ref} - /> - ); - } -); - -export const MenuTrigger = forwardRef( - (props, ref) => { - const { sub, children, ...rest } = props; - return ( - - {children} - - ); - } -); - -export const MenuDropdown = forwardRef( - (props, ref) => { - const { sub, ...rest } = props; - const ctx = useContext(SubMenuContext); - - return ( - - ); - } -); +export const MenuItem = (props: ComponentProps["Generic"]["Menu"]["Item"]) => { + const ctx = useContext(SubMenuContext); -export const MenuDivider = forwardRef( - (props, ref) => { - return ; - } -); + const onMouseLeave = props.subTrigger ? ctx!.onTriggerMouseLeave : undefined; + const onMouseOver = props.subTrigger ? ctx!.onTriggerMouseOver : undefined; -export const MenuLabel = forwardRef>( - (props, ref) => { - return ; - } -); + return ( + + {props.checked ? ( + + ) : !props.checked ? ( +
+ ) : undefined} + {props.subTrigger && } + + } + onMouseOver={onMouseOver} + onMouseLeave={onMouseLeave} + /> + ); +}; + +export const MenuTrigger = ( + props: ComponentProps["Generic"]["Menu"]["Trigger"] +) => {props.children}; + +export const MenuDropdown = ( + props: ComponentProps["Generic"]["Menu"]["Dropdown"] +) => { + const ctx = useContext(SubMenuContext); + + return ( + + ); +}; + +export const MenuDivider = ( + props: ComponentProps["Generic"]["Menu"]["Divider"] +) => ; + +export const MenuLabel = ( + props: ComponentProps["Generic"]["Menu"]["Label"] +) => ; diff --git a/packages/mantine/src/panel/Panel.tsx b/packages/mantine/src/panel/Panel.tsx index e99234fa9..266933a16 100644 --- a/packages/mantine/src/panel/Panel.tsx +++ b/packages/mantine/src/panel/Panel.tsx @@ -1,27 +1,25 @@ import * as Mantine from "@mantine/core"; -import { PanelProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; -export const Panel = (props: PanelProps) => { - return ( - - - {props.loading && } - - - {props.tabs.map((tab) => ( - - {tab.name} - - ))} - +export const Panel = (props: ComponentProps["ImagePanel"]["Root"]) => ( + + + {props.loading && } + {props.tabs.map((tab) => ( - - {tab.tabPanel} - + + {tab.name} + ))} - - - ); -}; + + + {props.tabs.map((tab) => ( + + {tab.tabPanel} + + ))} + + +); diff --git a/packages/mantine/src/panel/PanelButton.tsx b/packages/mantine/src/panel/PanelButton.tsx index b811820da..01f1ab0f1 100644 --- a/packages/mantine/src/panel/PanelButton.tsx +++ b/packages/mantine/src/panel/PanelButton.tsx @@ -1,13 +1,17 @@ import * as Mantine from "@mantine/core"; -import { PanelButtonProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; +import { forwardRef } from "react"; -export const PanelButton = (props: PanelButtonProps) => { +export const PanelButton = forwardRef< + HTMLButtonElement, + ComponentProps["ImagePanel"]["Button"] +>((props, ref) => { const { children, ...rest } = props; return ( - + {children} ); -}; +}); diff --git a/packages/mantine/src/panel/PanelFileInput.tsx b/packages/mantine/src/panel/PanelFileInput.tsx index 8efdddd8f..962c3c066 100644 --- a/packages/mantine/src/panel/PanelFileInput.tsx +++ b/packages/mantine/src/panel/PanelFileInput.tsx @@ -1,7 +1,7 @@ import * as Mantine from "@mantine/core"; -import { PanelFileInputProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; -export const PanelFileInput = (props: PanelFileInputProps) => ( - -); +export const PanelFileInput = ( + props: ComponentProps["ImagePanel"]["FileInput"] +) => ; diff --git a/packages/mantine/src/panel/PanelTab.tsx b/packages/mantine/src/panel/PanelTab.tsx index 6d2dc6414..efb205fb3 100644 --- a/packages/mantine/src/panel/PanelTab.tsx +++ b/packages/mantine/src/panel/PanelTab.tsx @@ -1,15 +1,7 @@ -import { mergeCSSClasses } from "@blocknote/core"; +import { ComponentProps } from "@blocknote/react"; -import { PanelTabProps } from "@blocknote/react"; +export const PanelTab = (props: ComponentProps["ImagePanel"]["TabPanel"]) => { + const { children, ...rest } = props; -export const PanelTab = (props: PanelTabProps) => { - const { className, children, ...rest } = props; - - return ( -
- {children} -
- ); + return
{children}
; }; diff --git a/packages/mantine/src/panel/PanelTextInput.tsx b/packages/mantine/src/panel/PanelTextInput.tsx index 8436db9c3..4dc6697b8 100644 --- a/packages/mantine/src/panel/PanelTextInput.tsx +++ b/packages/mantine/src/panel/PanelTextInput.tsx @@ -1,9 +1,9 @@ import * as Mantine from "@mantine/core"; -import { PanelTextInputProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; -export const PanelTextInput = (props: PanelTextInputProps) => { - const { children, ...rest } = props; - - return ; +export const PanelTextInput = ( + props: ComponentProps["ImagePanel"]["TextInput"] +) => { + return ; }; diff --git a/packages/mantine/src/popover/Popover.tsx b/packages/mantine/src/popover/Popover.tsx index 078f11509..29295926f 100644 --- a/packages/mantine/src/popover/Popover.tsx +++ b/packages/mantine/src/popover/Popover.tsx @@ -1,6 +1,10 @@ import * as Mantine from "@mantine/core"; -export const Popover = (props: Mantine.PopoverProps) => { +import { ComponentProps } from "@blocknote/react"; + +export const Popover = ( + props: ComponentProps["Generic"]["Popover"]["Root"] +) => { const { children, ...rest } = props; return ( @@ -9,3 +13,11 @@ export const Popover = (props: Mantine.PopoverProps) => { ); }; + +export const PopoverTrigger = ( + props: ComponentProps["Generic"]["Popover"]["Trigger"] +) => {props.children}; + +export const PopoverContent = ( + props: ComponentProps["Generic"]["Popover"]["Content"] +) => {props.children}; diff --git a/packages/mantine/src/styles.css b/packages/mantine/src/styles.css new file mode 100644 index 000000000..07c399477 --- /dev/null +++ b/packages/mantine/src/styles.css @@ -0,0 +1,540 @@ +@import url("@blocknote/core/fonts/inter.css"); +@import url("@blocknote/react/style.css"); +@import url("./mantineStyles.css"); + +/* Mantine base styles*/ + +/* Mantine FileInput component base styles */ +.bn-container .mantine-FileInput-input { + align-items: center; + background-color: var(--bn-colors-menu-background); + border: none; + border-radius: 4px; + color: var(--bn-colors-menu-text); + display: flex; + flex-direction: row; + justify-content: center; +} + +.bn-container .mantine-FileInput-input:hover { + background-color: var(--bn-colors-hovered-background); +} + +.bn-container .mantine-FileInput-wrapper { + border: solid var(--bn-colors-border) 1px; + border-radius: 4px; +} + +.bn-container .mantine-InputPlaceholder-placeholder { + color: var(--bn-colors-menu-text); + font-weight: 600; +} + +/* Mantine Menu component base styles */ +.bn-container .mantine-Menu-dropdown { + background-color: var(--bn-colors-menu-background); + border: var(--bn-border); + border-radius: var(--bn-border-radius-medium); + box-shadow: var(--bn-shadow-medium); + box-sizing: border-box; + color: var(--bn-colors-menu-text); + overflow-y: auto; +} + +.bn-container .mantine-Menu-label { + background-color: var(--bn-colors-menu-background); + color: var(--bn-colors-menu-text); +} + +.bn-container .mantine-Menu-item { + background-color: var(--bn-colors-menu-background); + border: none; + border-radius: var(--bn-border-radius-small); + color: var(--bn-colors-menu-text); +} + +.bn-container .mantine-Menu-item[data-hovered] { + background-color: var(--bn-colors-hovered-background); + border: none; + color: var(--bn-colors-hovered-text); +} + +/* Mantine Popover component base styles */ +.bn-container .mantine-Popover-dropdown { + background-color: transparent; + border: none; + border-radius: 0; + box-shadow: none; + padding: 0; +} + +/* Mantine Tabs component base styles */ +.bn-container .mantine-Tabs-root { + width: 100%; + background-color: var(--bn-colors-menu-background); +} + +.bn-container .mantine-Tabs-list:before { + border-color: var(--bn-colors-hovered-background); +} + +.bn-container .mantine-Tabs-tab { + color: var(--bn-colors-menu-text); + border-color: var(--bn-colors-hovered-background); +} + +.bn-container .mantine-Tabs-tab:hover { + background-color: var(--bn-colors-hovered-background); + border-color: var(--bn-colors-hovered-background); + color: var(--bn-colors-hovered-text); +} + +.bn-container .mantine-Tabs-tab[data-active], +.bn-container .mantine-Tabs-tab[data-active]:hover { + border-color: var(--bn-colors-menu-text); + color: var(--bn-colors-menu-text); +} + +.bn-container .mantine-Tabs-panel { + padding: 8px; +} + +/* Mantine TextInput component base styles */ +.bn-container .mantine-TextInput-input { + background-color: var(--bn-colors-menu-background); + border: solid var(--bn-colors-border) 1px; + border-radius: 4px; + color: var(--bn-colors-menu-text); + height: 32px; +} + +/* Mantine Tooltip component base styles */ +.bn-container .mantine-Tooltip-tooltip { + background-color: transparent; + border: none; + border-radius: 0; + box-shadow: none; + padding: 0; +} + +/* UI element styling */ + +/* Toolbar styling */ +.bn-container .bn-formatting-toolbar-root, +.bn-container .bn-link-toolbar-root { + background-color: var(--bn-colors-menu-background); + border: var(--bn-border); + border-radius: var(--bn-border-radius-medium); + box-shadow: var(--bn-shadow-medium); + flex-wrap: nowrap; + gap: 2px; + padding: 2px; + width: fit-content; +} + +.bn-container .bn-formatting-toolbar-root:empty, +.bn-container .bn-link-toolbar-root:empty { + display: none; +} + +.bn-container .bn-formatting-toolbar-root .mantine-Button-root, +.bn-container .bn-formatting-toolbar-root .mantine-ActionIcon-root, +.bn-container .bn-formatting-link-root .mantine-Button-root, +.bn-container .bn-formatting-link-root .mantine-ActionIcon-root { + background-color: var(--bn-colors-menu-background); + border: none; + border-radius: var(--bn-border-radius-small); + color: var(--bn-colors-menu-text); +} + +.bn-container .bn-formatting-toolbar-root .mantine-Button-root:hover, +.bn-container .bn-formatting-toolbar-root .mantine-ActionIcon-root:hover, +.bn-container .bn-formatting-link-root .mantine-Button-root:hover, +.bn-container .bn-formatting-link-root .mantine-ActionIcon-root:hover { + background-color: var(--bn-colors-hovered-background); + border: none; + color: var(--bn-colors-hovered-text); +} + +.bn-container .bn-formatting-toolbar-root .mantine-Button-root[data-selected], +.bn-container .bn-formatting-toolbar-root .mantine-ActionIcon-root[data-selected], +.bn-container .bn-formatting-link-root .mantine-Button-root[data-selected], +.bn-container .bn-formatting-link-root .mantine-ActionIcon-root[data-selected] { + background-color: var(--bn-colors-selected-background); + border: none; + color: var(--bn-colors-selected-text); +} + +.bn-container .bn-formatting-toolbar-root .mantine-Button-root[data-disabled], +.bn-container .bn-formatting-toolbar-root .mantine-ActionIcon-root[data-disabled], +.bn-container .bn-formatting-link-root .mantine-Button-root[data-disabled], +.bn-container .bn-formatting-link-root .mantine-ActionIcon-root[data-disabled] { + background-color: var(--bn-colors-disabled-background); + border: none; + color: var(--bn-colors-disabled-text); +} + +.bn-container .bn-formatting-toolbar-select .mantine-Menu-item { + font-size: 12px; + height: 30px; +} + +.bn-container .bn-formatting-toolbar-select .mantine-Menu-item:hover { + background-color: var(--bn-colors-hovered-background); +} + +.bn-container .bn-toolbar-input-dropdown { + background-color: var(--bn-colors-menu-background); + border: var(--bn-border); + border-radius: var(--bn-border-radius-medium); + box-shadow: var(--bn-shadow-medium); + color: var(--bn-colors-menu-text); + gap: 4px; + min-width: 145px; + padding: 2px; +} + +.bn-container .bn-toolbar-input-dropdown .mantine-Group-root { + flex-wrap: nowrap; +} + +.bn-container .bn-toolbar-input-dropdown .mantine-TextInput-root, +.bn-container .bn-toolbar-input-dropdown .mantine-FileInput-root { + width: 300px; +} + +.bn-container .bn-toolbar-input-dropdown .mantine-TextInput-wrapper, +.bn-container .bn-toolbar-input-dropdown .mantine-FileInput-wrapper { + padding: 0; + border-radius: 4px; +} + +.bn-container .bn-toolbar-input-dropdown .mantine-TextInput-wrapper:hover { + background-color: var(--bn-colors-hovered-background); +} + +.bn-container .bn-toolbar-input-dropdown .mantine-TextInput-input, +.bn-container .bn-toolbar-input-dropdown .mantine-FileInput-input { + border: none; + font-size: 12px; +} + +.bn-container .bn-toolbar-input-dropdown .mantine-FileInput-input:hover { + background-color: var(--bn-colors-hovered-background); +} + +.bn-container +.bn-toolbar-input-dropdown +.mantine-FileInput-section[data-position="left"] { + color: var(--bn-colors-menu-text); +} + +.bn-container .bn-toolbar-input-dropdown .mantine-FileInput-placeholder { + color: var(--bn-colors-menu-text); +} + +/* Slash Menu styling*/ +.bn-container .bn-slash-menu { + max-height: 100%; + position: relative; + box-shadow: var(--mantine-shadow-md); + border: calc(0.0625rem * var(--mantine-scale)) solid + var(--mantine-color-gray-2); + border-radius: var(--mantine-radius-default); + padding: 4px; +} + +.bn-container .bn-slash-menu .mantine-Menu-item { + height: 52px; +} + +.bn-container .bn-slash-menu .mantine-Menu-itemSection { + color: var(--bn-colors-tooltip-text); +} + +.bn-container .bn-slash-menu .mantine-Menu-itemSection[data-position="left"] { + background-color: var(--bn-colors-tooltip-background); + border-radius: var(--bn-border-radius-small); + padding: 8px; +} + +.bn-container .bn-slash-menu .mantine-Menu-itemLabel { + padding-right: 16px; +} + +.bn-container .bn-slash-menu .mantine-Menu-itemLabel .mantine-Stack-root { + gap: 0; +} + +.bn-container .bn-slash-menu .bn-slash-menu-loader { + height: 20px; + width: 100%; +} + +.bn-container .bn-slash-menu .bn-slash-menu-loader span { + background-color: var(--bn-colors-side-menu); +} + +/* Side Menu & Drag Handle styling */ +.bn-container .bn-side-menu { + background-color: transparent; +} + +.bn-container .bn-drag-handle { + height: 24px; + width: 24px; +} + +/* Image Panel styling*/ +.bn-container .bn-image-panel-root { + background-color: var(--bn-colors-menu-background); + border: var(--bn-border); + border-radius: var(--bn-border-radius-medium); + box-shadow: var(--bn-shadow-medium); + padding: 2px; + width: 500px; +} + +.bn-container .bn-image-panel-root .bn-image-panel-tab-panel { + align-items: center; + display: flex; + flex-direction: column; + gap: 8px; + width: 100%; +} + +.bn-container .bn-image-panel-root .mantine-TextInput-root, +.bn-container .bn-image-panel-root .mantine-FileInput-root { + width: 100%; +} + +.bn-container .bn-image-panel-root .mantine-Button-root { + background-color: var(--bn-colors-menu-background); + border: solid var(--bn-colors-border) 1px; + border-radius: var(--bn-border-radius-small); + color: var(--bn-colors-menu-text); + height: 32px; + width: 60%; +} + +.bn-container .bn-image-panel-root .mantine-Button-root:hover { + background-color: var(--bn-colors-hovered-background); +} + +.bn-container .bn-image-panel-root .mantine-Text-root { + text-align: center; +} + +/* Table Handle styling */ +.bn-container .bn-table-handle { + align-items: center; + background-color: var(--bn-colors-menu-background); + border: var(--bn-border); + border-radius: var(--bn-border-radius-small); + box-shadow: var(--bn-shadow-light); + color: var(--bn-colors-side-menu); + cursor: pointer; + display: flex; + justify-content: center; + overflow: visible; +} + +.bn-container .bn-table-handle > svg { + margin-inline: -4px; +} + +.bn-container .bn-table-handle:hover, +.bn-container .bn-table-handle-dragging { + background-color: var(--bn-colors-hovered-background); +} + +/* Drag Handle & Table Handle Menu styling */ +.bn-container .bn-drag-handle-menu { + width: 100px; +} + +.bn-container .bn-drag-handle-menu, +.bn-container .bn-table-handle-menu { + overflow: visible; +} + +.bn-container .bn-drag-handle-menu .mantine-Menu-item, +.bn-container .bn-table-handle-menu .mantine-Menu-item { + color: var(--bn-colors-menu-text); + font-size: 12px; + height: 30px; +} + +/* Tooltip styling */ +.bn-container .bn-tooltip { + background-color: var(--bn-colors-tooltip-background); + border: var(--bn-border); + border-radius: var(--bn-border-radius-medium); + box-shadow: var(--bn-shadow-medium); + color: var(--bn-colors-tooltip-text); + padding: 4px 10px; + text-align: center; +} + +/* Color Icon styling */ +.bn-container .bn-tick-space { + padding: 0; + width: 20px; +} + +/* +copied from mantine menu +https://github.com/mantinedev/mantine/blob/e3e3bb834de1f2f75a27dbc757dc0a2fc6a6cba8/packages/%40mantine/core/src/components/Menu/Menu.module.css +*/ + +.bn-container .bn-slash-menu .mantine-Menu-label { + color: var(--mantine-color-dimmed); + font-weight: 500; + font-size: var(--mantine-font-size-xs); + padding: calc(var(--mantine-spacing-xs) / 2) var(--mantine-spacing-sm); + cursor: default; +} + +.bn-container .bn-slash-menu-item { + font-size: var(--mantine-font-size-sm); + width: 100%; + padding: calc(var(--mantine-spacing-xs) / 1.5) var(--mantine-spacing-sm); + border-radius: var(--popover-radius, var(--mantine-radius-default)); + color: var(--menu-item-color, var(--mantine-color-text)); + display: flex; + align-items: center; + user-select: none; + + &:where([data-disabled], :disabled) { + color: var(--mantine-color-dimmed); + opacity: 0.6; + pointer-events: none; + } + + &:where([data-hovered]) { + } +} + +/* copied from mantine menu */ +.bn-container .bn-slash-menu-item .mantine-Menu-itemLabel { + flex: 1; +} + +/* copied from mantine menu */ +.bn-container .bn-slash-menu-item .mantine-Menu-itemSection { + display: flex; + justify-content: center; + align-items: center; + + &:where([data-position="left"]) { + margin-inline-end: var(--mantine-spacing-xs); + } + + &:where([data-position="right"]) { + margin-inline-start: var(--mantine-spacing-xs); + } +} + +/* copied from mantine badge */ + +.bn-badge-root { + --badge-height: calc(1rem * var(--mantine-scale)); + --badge-fz: calc(0.5625rem * var(--mantine-scale)); + --badge-padding-x: calc(0.375rem * var(--mantine-scale)); + --badge-radius: calc(62.5rem * var(--mantine-scale)); + --badge-bg: var(--bn-colors-tooltip-background); + --badge-color: var(--bn-colors-tooltip-text); + align-items: center; + background: var(--badge-bg); + border: var(--badge-bd); + border-radius: var(--badge-radius); + color: var(--badge-color); + cursor: inherit; + display: inline-flex; + font-size: var(--badge-fz); + font-weight: 700; + height: var(--badge-height); + justify-content: center; + letter-spacing: calc(0.01563rem * var(--mantine-scale)); + line-height: var(--badge-lh); + overflow: hidden; + padding: 0 var(--badge-padding-x); + text-decoration: none; + text-overflow: ellipsis; + text-transform: uppercase; + width: fit-content; +} + +.bn-badge-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* from https://github.com/mantinedev/mantine/blob/master/packages/%40mantine/core/src/components/UnstyledButton/UnstyledButton.module.css */ +.bn-sidemenu-button { + background-color: transparent; + cursor: pointer; + border: 0; + padding: 0; + appearance: none; + font-size: var(--mantine-font-size-md); + text-align: left; + text-decoration: none; + color: inherit; + touch-action: manipulation; + -webkit-tap-highlight-color: transparent; +} + +/* from https://github.com/mantinedev/mantine/blob/master/packages/%40mantine/core/src/components/ActionIcon/ActionIcon.module.css */ +.bn-sidemenu-button { + --ai-size: 24px; + --ai-color: var(--mantine-color-white); + + line-height: 1; + display: inline-flex; + align-items: center; + justify-content: center; + position: relative; + user-select: none; + overflow: hidden; + + width: var(--ai-size); + height: var(--ai-size); + min-width: var(--ai-size); + min-height: var(--ai-size); + border-radius: var(--ai-radius, var(--mantine-radius-default)); + background: var(--ai-bg, var(--mantine-primary-color-filled)); + color: var(--ai-color, var(--mantine-color-white)); + border: 1px solid transparent; + cursor: pointer; + + /* @mixin hover { + &:where(:not([data-loading], :disabled, [data-disabled])) { + background-color: var( + --ai-hover, + var(--mantine-primary-color-filled-hover) + ); + color: var(--ai-hover-color, var(--ai-color)); + } + } */ +} + +.bn-container .bn-sidemenu-button { + background-color: transparent; + color: var(--bn-colors-side-menu); +} + +.bn-container .bn-sidemenu-button:hover { + background-color: var(--bn-colors-hovered-background); +} + +.bn-sidemenu-button-icon { + display: flex; + align-items: center; + justify-content: center; + transition: transform 150ms ease, opacity 100ms ease; + width: 100%; + height: 100%; +} + diff --git a/packages/mantine/src/toolbar/Toolbar.tsx b/packages/mantine/src/toolbar/Toolbar.tsx index bfacaf6fd..2285f33f9 100644 --- a/packages/mantine/src/toolbar/Toolbar.tsx +++ b/packages/mantine/src/toolbar/Toolbar.tsx @@ -1,20 +1,12 @@ import * as Mantine from "@mantine/core"; -import { mergeCSSClasses } from "@blocknote/core"; -import { forwardRef, HTMLAttributes } from "react"; +import { ComponentProps } from "@blocknote/react"; -export const Toolbar = forwardRef< - HTMLDivElement, - HTMLAttributes ->((props, ref) => { - const { className, children, ...rest } = props; +type ToolbarProps = ComponentProps["FormattingToolbar"]["Root"] & + ComponentProps["LinkToolbar"]["Root"]; - return ( - - {children} - - ); -}); +export const Toolbar = (props: ToolbarProps) => { + const { children, ...rest } = props; + + return {children}; +}; diff --git a/packages/mantine/src/toolbar/ToolbarButton.tsx b/packages/mantine/src/toolbar/ToolbarButton.tsx index 9ac1aa02d..40ff92503 100644 --- a/packages/mantine/src/toolbar/ToolbarButton.tsx +++ b/packages/mantine/src/toolbar/ToolbarButton.tsx @@ -1,7 +1,7 @@ import * as Mantine from "@mantine/core"; import { isSafari } from "@blocknote/core"; -import { ToolbarButtonProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; import { forwardRef } from "react"; export const TooltipContent = (props: { @@ -16,64 +16,67 @@ export const TooltipContent = (props: { ); +type ToolbarButtonProps = ComponentProps["FormattingToolbar"]["Button"] & + ComponentProps["LinkToolbar"]["Button"]; + /** * Helper for basic buttons that show in the formatting toolbar. */ export const ToolbarButton = forwardRef( - (props, ref) => { - return ( - - }> - {/*Creates an ActionIcon instead of a Button if only an icon is provided as content.*/} - {props.children ? ( - { - if (isSafari()) { - (e.currentTarget as HTMLButtonElement).focus(); - } - }} - onClick={props.onClick} - data-selected={props.isSelected ? "true" : undefined} - data-test={ - props.mainTooltip.slice(0, 1).toLowerCase() + - props.mainTooltip.replace(/\s+/g, "").slice(1) + (props, ref) => ( + + }> + {/*Creates an ActionIcon instead of a Button if only an icon is provided as content.*/} + {props.children ? ( + { + if (isSafari()) { + (e.currentTarget as HTMLButtonElement).focus(); } - size={"xs"} - disabled={props.isDisabled || false} - ref={ref}> - {props.icon} - {props.children} - - ) : ( - { - if (isSafari()) { - (e.currentTarget as HTMLButtonElement).focus(); - } - }} - onClick={props.onClick} - data-selected={props.isSelected ? "true" : undefined} - data-test={ - props.mainTooltip.slice(0, 1).toLowerCase() + - props.mainTooltip.replace(/\s+/g, "").slice(1) + }} + onClick={props.onClick} + data-selected={props.isSelected ? "true" : undefined} + data-test={ + props.mainTooltip.slice(0, 1).toLowerCase() + + props.mainTooltip.replace(/\s+/g, "").slice(1) + } + size={"xs"} + disabled={props.isDisabled || false} + ref={ref}> + {props.icon} + {props.children} + + ) : ( + { + if (isSafari()) { + (e.currentTarget as HTMLButtonElement).focus(); } - size={30} - disabled={props.isDisabled || false} - ref={ref}> - {props.icon} - - )} - - ); - } + }} + onClick={props.onClick} + data-selected={props.isSelected ? "true" : undefined} + data-test={ + props.mainTooltip.slice(0, 1).toLowerCase() + + props.mainTooltip.replace(/\s+/g, "").slice(1) + } + size={30} + disabled={props.isDisabled || false} + ref={ref}> + {props.icon} + + )} + + ) ); diff --git a/packages/mantine/src/toolbar/ToolbarInputsMenuItem.tsx b/packages/mantine/src/toolbar/ToolbarInputsMenuItem.tsx index 4a157b510..cfc26e8b4 100644 --- a/packages/mantine/src/toolbar/ToolbarInputsMenuItem.tsx +++ b/packages/mantine/src/toolbar/ToolbarInputsMenuItem.tsx @@ -2,6 +2,7 @@ import * as Mantine from "@mantine/core"; import type { IconType } from "react-icons"; +// TODO export type ToolbarInputsMenuItemProps = { icon: IconType; } & Mantine.TextInputProps; diff --git a/packages/mantine/src/toolbar/ToolbarSelect.tsx b/packages/mantine/src/toolbar/ToolbarSelect.tsx index ff7268518..bea41e199 100644 --- a/packages/mantine/src/toolbar/ToolbarSelect.tsx +++ b/packages/mantine/src/toolbar/ToolbarSelect.tsx @@ -1,12 +1,14 @@ import * as Mantine from "@mantine/core"; import { isSafari } from "@blocknote/core"; -import { ToolbarSelectProps } from "@blocknote/react"; +import { ComponentProps } from "@blocknote/react"; import { HiChevronDown } from "react-icons/hi"; import { TiTick } from "react-icons/ti"; // TODO: turn into select -export function ToolbarSelect(props: ToolbarSelectProps) { +export function ToolbarSelect( + props: ComponentProps["FormattingToolbar"]["Select"] +) { const selectedItem = props.items.filter((p) => p.isSelected)[0]; if (!selectedItem) { @@ -38,7 +40,7 @@ export function ToolbarSelect(props: ToolbarSelectProps) { {selectedItem.text}
- + {props.items.map((item) => ( void; }; }) => { - const components = useComponentsContext()!; + const Components = useComponentsContext()!; const TextColorSection = () => props.text ? ( <> - Text + Text {[ "default", "gray", @@ -31,7 +31,7 @@ export const ColorPicker = (props: { "purple", "pink", ].map((color) => ( - { props.onClick && props.onClick(); props.text!.setColor(color); @@ -41,7 +41,7 @@ export const ColorPicker = (props: { checked={props.text!.color === color} key={"text-color-" + color}> {color.charAt(0).toUpperCase() + color.slice(1)} - + ))} ) : null; @@ -49,7 +49,9 @@ export const ColorPicker = (props: { const BackgroundColorSection = () => props.background ? ( <> - Background + + Background + {[ "default", "gray", @@ -62,7 +64,7 @@ export const ColorPicker = (props: { "purple", "pink", ].map((color) => ( - { props.onClick && props.onClick(); props.background!.setColor(color); @@ -72,7 +74,7 @@ export const ColorPicker = (props: { key={"background-color-" + color} checked={props.background!.color === color}> {color.charAt(0).toUpperCase() + color.slice(1)} - + ))} ) : null; diff --git a/packages/react/src/components/FormattingToolbar/DefaultButtons/BasicTextStyleButton.tsx b/packages/react/src/components/FormattingToolbar/DefaultButtons/BasicTextStyleButton.tsx index eccc62fec..ca2cf26c5 100644 --- a/packages/react/src/components/FormattingToolbar/DefaultButtons/BasicTextStyleButton.tsx +++ b/packages/react/src/components/FormattingToolbar/DefaultButtons/BasicTextStyleButton.tsx @@ -61,7 +61,7 @@ function checkBasicTextStyleInSchema