Skip to content

Commit

Permalink
initial button block implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
iskaktoltay committed Oct 22, 2024
1 parent 010f08d commit 2bb551c
Show file tree
Hide file tree
Showing 9 changed files with 336 additions and 10 deletions.
191 changes: 191 additions & 0 deletions frontend/apps/desktop/src/editor/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import {useOpenUrl} from '@/open-url'
import {Button, Input, SizableText, XStack} from '@shm/ui'
import {MousePointerClick} from '@tamagui/lucide-icons'
import {SetStateAction} from 'react'
import {
Block,
BlockNoteEditor,
createReactBlockSpec,
defaultProps,
} from './blocknote'
import {MediaContainer} from './media-container'
import {DisplayComponentProps, MediaRender, MediaType} from './media-render'
import {HMBlockSchema} from './schema'
import {isValidUrl} from './utils'

export const ButtonBlock = createReactBlockSpec({
type: 'button',
propSchema: {
...defaultProps,

url: {
default: '',
},
name: {
default: '',
},
defaultOpen: {
values: ['false', 'true'],
default: 'false',
},
},
containsInlineContent: true,
// @ts-ignore
render: ({
block,
editor,
}: {
block: Block<HMBlockSchema>
editor: BlockNoteEditor<HMBlockSchema>
}) => Render(block, editor),
})

const Render = (
block: Block<HMBlockSchema>,
editor: BlockNoteEditor<HMBlockSchema>,
) => {
const submitButton = (url: string, assign: any, setFileName: any) => {
if (isValidUrl(url)) {
assign({props: {url: url}} as MediaType)
} else setFileName({name: 'The provided URL is invalid.', color: 'red'})
const cursorPosition = editor.getTextCursorPosition()
editor.focus()
if (cursorPosition.block.id === block.id) {
if (cursorPosition.nextBlock)
editor.setTextCursorPosition(cursorPosition.nextBlock, 'start')
else {
editor.insertBlocks(
[{type: 'paragraph', content: ''}],
block.id,
'after',
)
editor.setTextCursorPosition(
editor.getTextCursorPosition().nextBlock!,
'start',
)
}
}
}
return (
<MediaRender
block={block}
editor={editor}
mediaType="button"
submit={submitButton}
DisplayComponent={display}
icon={<MousePointerClick />}
CustomInput={ButtonInput}
/>
)
}

const ButtonInput = ({
assign,
setUrl,
fileName,
setFileName,
}: {
assign: any
setUrl: any
fileName: any
setFileName: any
}) => {
return (
<XStack flex={1} gap="$2">
<Input
unstyled
borderColor="$color8"
borderWidth="$1"
borderRadius="$2"
paddingLeft="$3"
height="$3"
width="100%"
placeholder={'Input Button Label here...'}
hoverStyle={{
borderColor: '$color11',
}}
focusStyle={{
borderColor: '$color11',
}}
onChange={(e: {nativeEvent: {text: SetStateAction<string>}}) => {
assign({props: {name: e.nativeEvent.text}})
if (fileName.color)
setFileName({
name: 'Upload File',
color: undefined,
})
}}
autoFocus={true}
/>
<Input
unstyled
borderColor="$color8"
borderWidth="$1"
borderRadius="$2"
paddingLeft="$3"
height="$3"
width="100%"
placeholder={'Input Button URL here...'}
hoverStyle={{
borderColor: '$color11',
}}
focusStyle={{
borderColor: '$color11',
}}
onChange={(e: {nativeEvent: {text: SetStateAction<string>}}) => {
setUrl(e.nativeEvent.text)
if (fileName.color)
setFileName({
name: 'Upload File',
color: undefined,
})
}}
autoFocus={true}
/>
</XStack>
)
}

const display = ({
editor,
block,
selected,
setSelected,
assign,
}: DisplayComponentProps) => {
const openUrl = useOpenUrl()

return (
<MediaContainer
editor={editor}
block={block}
mediaType="button"
selected={selected}
setSelected={setSelected}
assign={assign}
>
<XStack height="$5" width="100%" jc="center" ai="center">
<Button
borderWidth={1}
borderRadius={1}
bc="$brand10"
size="$3"
minWidth="$10"
maxWidth="100%"
px="$2"
fontSize="$4"
justifyContent="center"
textAlign="center"
userSelect="none"
onPress={() => {
openUrl(block.props.url)
}}
>
<SizableText numberOfLines={1} ellipsizeMode="tail">
{block.props.name}
</SizableText>
</Button>
</XStack>
</MediaContainer>
)
}
7 changes: 7 additions & 0 deletions frontend/apps/desktop/src/editor/editor-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type EditorBlock =
| EditorImageBlock
| EditorVideoBlock
| EditorFileBlock
| EditorButtonBlock
| EditorEmbedBlock
| EditorWebEmbedBlock
| EditorMathBlock
Expand Down Expand Up @@ -76,6 +77,12 @@ export interface EditorFileBlock extends EditorBaseBlock {
content: Array<EditorInlineContent>
}

export interface EditorButtonBlock extends EditorBaseBlock {
type: 'button'
props: MediaBlockProps
content: Array<EditorInlineContent>
}

export interface EditorEmbedBlock extends EditorBaseBlock {
type: 'embed'
props: EditorBlockProps & {
Expand Down
2 changes: 1 addition & 1 deletion frontend/apps/desktop/src/editor/media-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,7 @@ function MediaForm({
</Button>
)}
</XStack>
{fileName.name !== 'Upload File' && (
{fileName.color && fileName.color === 'red' && (
<SizableText size="$2" color={fileName.color} paddingTop="$2">
{fileName.name}
</SizableText>
Expand Down
5 changes: 4 additions & 1 deletion frontend/apps/desktop/src/editor/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
defaultBlockSchema,
defaultProps,
} from './blocknote'
import {ButtonBlock} from './button'
import {EmbedBlock} from './embed-block'
import {FileBlock} from './file'
import {HMHeadingBlockContent} from './heading-component-plugin'
Expand Down Expand Up @@ -38,9 +39,11 @@ export const hmBlockSchema: BlockSchema = {
languageClassPrefix: 'language-',
}),
},
embed: EmbedBlock,

video: VideoBlock,
embed: EmbedBlock,
file: FileBlock,
button: ButtonBlock,
nostr: NostrBlock,
['web-embed']: WebEmbed,
math: MathBlock('math'),
Expand Down
58 changes: 50 additions & 8 deletions frontend/apps/desktop/src/slash-menu-items.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import {
RiHeading,
RiImage2Fill,
RiMessage2Fill,
RiRadioButtonFill,
RiText,
RiVideoAddFill,
} from 'react-icons/ri'
import {
BlockNoteEditor,
BlockSpec,
HMBlockSchema,
PartialBlock,
PropSchema,
insertOrUpdateBlock,
} from './editor'

Expand All @@ -22,7 +25,9 @@ export const slashMenuItems = [
aliases: ['h', 'heading1', 'subheading'],
group: 'Text blocks',
icon: <RiHeading size={18} />,
execute: (editor) => {
execute: (
editor: BlockNoteEditor<Record<string, BlockSpec<string, PropSchema>>>,
) => {
insertOrUpdateBlock(editor, {
type: 'heading',
props: {level: '2'},
Expand All @@ -36,7 +41,9 @@ export const slashMenuItems = [
aliases: ['p'],
group: 'Text blocks',
icon: <RiText size={18} />,
execute: (editor) => {
execute: (
editor: BlockNoteEditor<Record<string, BlockSpec<string, PropSchema>>>,
) => {
insertOrUpdateBlock(editor, {
type: 'paragraph',
} as PartialBlock<HMBlockSchema>)
Expand Down Expand Up @@ -67,7 +74,9 @@ export const slashMenuItems = [
group: 'Media blocks',
icon: <RiImage2Fill size={18} />,
hint: 'Insert an Image',
execute: (editor) => {
execute: (
editor: BlockNoteEditor<Record<string, BlockSpec<string, PropSchema>>>,
) => {
insertOrUpdateBlock(
editor,
{
Expand All @@ -89,7 +98,9 @@ export const slashMenuItems = [
group: 'Media blocks',
icon: <RiVideoAddFill size={18} />,
hint: 'Insert a Video',
execute: (editor) => {
execute: (
editor: BlockNoteEditor<Record<string, BlockSpec<string, PropSchema>>>,
) => {
insertOrUpdateBlock(
editor,
{
Expand All @@ -111,7 +122,9 @@ export const slashMenuItems = [
group: 'Media blocks',
icon: <RiFile2Fill size={18} />,
hint: 'Insert a File',
execute: (editor) => {
execute: (
editor: BlockNoteEditor<Record<string, BlockSpec<string, PropSchema>>>,
) => {
insertOrUpdateBlock(
editor,
{
Expand All @@ -133,7 +146,9 @@ export const slashMenuItems = [
group: 'Media blocks',
icon: <RiArticleFill size={18} />,
hint: 'Insert a Hypermedia Embed',
execute: (editor) => {
execute: (
editor: BlockNoteEditor<Record<string, BlockSpec<string, PropSchema>>>,
) => {
insertOrUpdateBlock(
editor,
{
Expand All @@ -154,7 +169,9 @@ export const slashMenuItems = [
group: 'Media blocks',
icon: <RiFunctions size={18} />,
hint: 'Insert an Math Block',
execute: (editor) => {
execute: (
editor: BlockNoteEditor<Record<string, BlockSpec<string, PropSchema>>>,
) => {
insertOrUpdateBlock(
editor,
{
Expand All @@ -166,13 +183,38 @@ export const slashMenuItems = [
view.dispatch(state.tr.scrollIntoView())
},
},
{
name: 'Button',
aliases: ['button', 'click', 'press'],
group: '',
icon: <RiRadioButtonFill size={18} />,
hint: 'Insert a button block',
execute: (
editor: BlockNoteEditor<Record<string, BlockSpec<string, PropSchema>>>,
) => {
insertOrUpdateBlock(
editor,
{
type: 'button',
props: {
ref: '',
},
} as PartialBlock<HMBlockSchema>,
true,
)
const {state, view} = editor._tiptapEditor
view.dispatch(state.tr.scrollIntoView())
},
},
{
name: 'Nostr',
aliases: ['nostr', 'note', 'event'],
group: 'Web embeds',
icon: <RiMessage2Fill size={18} />,
hint: 'Insert a nostr note',
execute: (editor) => {
execute: (
editor: BlockNoteEditor<Record<string, BlockSpec<string, PropSchema>>>,
) => {
insertOrUpdateBlock(
editor,
{
Expand Down
8 changes: 8 additions & 0 deletions frontend/packages/shared/src/client/editorblock-to-hmblock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ function toHMBlockType(
if (editorBlockType === 'image') return 'Image'
if (editorBlockType === 'video') return 'Video'
if (editorBlockType === 'file') return 'File'
if (editorBlockType === 'button') return 'Button'
if (editorBlockType === 'embed') return 'Embed'
if (editorBlockType === 'web-embed') return 'WebEmbed'
return undefined
Expand Down Expand Up @@ -128,6 +129,13 @@ export function editorBlockToHMBlock(editorBlock: EditorBlock): HMBlock {
blockFile.attributes.size = String(editorBlock.props.size)
}

const blockButton = block.type === 'Button' ? block : undefined
if (blockButton && editorBlock.type == 'button') {
if (editorBlock.props.url) blockButton.link = editorBlock.props.url
if (editorBlock.props.name)
blockButton.attributes.name = editorBlock.props.name
}

const blockWebEmbed = block.type === 'WebEmbed' ? block : undefined
if (
blockWebEmbed &&
Expand Down
Loading

0 comments on commit 2bb551c

Please sign in to comment.