Skip to content

Instantly share code, notes, and snippets.

@diegohaz
Last active August 18, 2022 12:39
Show Gist options
  • Save diegohaz/c48c82775c0ac36fda45987e73e9787c to your computer and use it in GitHub Desktop.
Save diegohaz/c48c82775c0ac36fda45987e73e9787c to your computer and use it in GitHub Desktop.
type LinkProps = AnchorHTMLAttributes<HTMLAnchorElement>;
const Link = createBlock<LinkProps>((props) => {
const { onSelectionChange, selectBlockContents } = useEditorState(props);
const popover = usePopoverState({ portal: true });
const [href, setHref] = useState(props.href);
useEffect(() => {
setHref(props.href);
}, [props.href]);
useEffect(() => {
return onSelectionChange((selection) => {
if (selection.startContainerIds.includes(props.id) && selection.endContainerIds.includes(props.id)) {
popover.show();
}
});
}, [onSelect, props.id, popover.show]);
return (
<>
<PopoverDisclosure
as="a"
state={popover}
{...props}
href={href}
onClick={(event) => {
props.onClick?.(event);
if (event.defaultPrevented) return;
event.preventDefault();
selectBlockConents(props.id);
}}
>
{(disclosureProps) => (
<Text as="a" {...disclosureProps} />
)}
</PopoverDisclosure>
<Popover autoFocusOnShow={false} state={popover}>
<a href={href} />
<button>Edit</button>
</Popover>
</>
);
});
const ListItem = createBlock((props) => {
const { value, insertBlock, removeBlock } = useEditorState(props);
useEnter((defaultAction) => {
if (value.length) {
insertBlock("ListItem");
} else {
removeBlock(props.id);
defaultAction();
}
}, [value, insertBlock, removeBlock, props.id]);
return <Text as="li" {...props} />;
});
const ListItem = createEditorComponent((props) => {
const editor = useEditorState(props);
useEditorEnter(() => {
editor.insertElement("ListItem");
}, [editor.insertElement]);
return <Text as="li" {...props} />;
});
type ListProps = {
inputs: {
type: "ordered" | "unordered";
};
};
const List = createEditorComponent<ListProps>((props) => {
const editor = useEditorState({
...props,
defaultValue: [{
type: "ListItem",
id: `${props.id}-1`,
}],
});
const type = useEditorInput(
ToggleGroup,
{
...props,
name: "type",
options: [
{ value: "ordered", label: "Ordered", icon: "Ordered" },
{ value: "unordered", label: "Unordered", icon: "Unordered" },
],
}
);
const component = type === "ordered" ? "ol" : "ul";
// if defaultValue doesn't work
useLayoutEffect(() => {
if (!editor.value.length) {
const selection = {
startContainerId: props.id,
startOffset: 0,
endContainerId: props.id,
endOffset: 0,
};
editor.insertElement("ListItem", null, selection);
}
}, [editor.insertElement, editor.value, props.id]);
return <EditorComponent as={component} {...props} />;
});
type HeadingProps = TextProps & {
inputs: {
level?: number;
};
};
const Heading = createEditorComponent<HeadingProps>((props) => {
const level = useEditorInput(HeadingLevel, { ...props, name: "level", label: "Heading level" });
const component = `h${level}`;
useEditorReplace("Paragraph");
return <Text as={component} {...props} />;
});
Heading.defaultProps = {
inputs: {
level: 1,
},
};
Heading.triggers = {
"#{1,6}": (text) => ({
inputs: {
level: text.length,
},
});
};
const HeadingLevel = createEditorInput<HeadingProps>((props) => {
useEditorInputValue(props, "value");
return <div />;
});
const defaultComponents = {
Paragraph,
Bold,
Heading,
...
};
function App() {
const editor = useEditorState({ defaultComponents });
editor.getSelection(); // { startContainerId: "id", startOffset: 0, endContainerId: "id", endOffset: 3, collapsed: false }
editor.insertElement("Paragraph", null, editor.getSelection());
editor.insertElement(Heading, { inputs: { level: 2 } });
editor.replaceElement(Paragraph);
return <Editor state={editor} />;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment