Add popover helpers in Editor (#105)

This commit is contained in:
Ilya Y 2023-05-29 20:14:58 +03:00 committed by GitHub
parent 5a1699aa87
commit bb45d206e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 386 additions and 238 deletions

View File

@ -5,7 +5,13 @@
"Add another image": "Add another image", "Add another image": "Add another image",
"Add comment": "Comment", "Add comment": "Comment",
"Add image": "Add image", "Add image": "Add image",
"Add link": "Add link",
"Add signature": "Add signature",
"Add url": "Add url",
"Address on Discourse": "Address on Discourse", "Address on Discourse": "Address on Discourse",
"Alignment center": "Alignment center",
"Alignment left": "Alignment left",
"Alignment right": "Alignment right",
"All": "All", "All": "All",
"All authors": "All authors", "All authors": "All authors",
"All posts": "All posts", "All posts": "All posts",
@ -18,8 +24,10 @@
"Autotypograph": "Autotypograph", "Autotypograph": "Autotypograph",
"Back to main page": "Back to main page", "Back to main page": "Back to main page",
"Become an author": "Become an author", "Become an author": "Become an author",
"Bold": "Bold",
"Bookmarked": "Saved", "Bookmarked": "Saved",
"Bookmarks": "Bookmarks", "Bookmarks": "Bookmarks",
"Bullet list": "Bullet list",
"By alphabet": "By alphabet", "By alphabet": "By alphabet",
"By authors": "By authors", "By authors": "By authors",
"By name": "By name", "By name": "By name",
@ -85,10 +93,14 @@
"Go to main page": "Go to main page", "Go to main page": "Go to main page",
"Group Chat": "Group Chat", "Group Chat": "Group Chat",
"Groups": "Groups", "Groups": "Groups",
"Header 1": "Header 1",
"Header 2": "Header 2",
"Header 3": "Header 3",
"Headers": "Headers", "Headers": "Headers",
"Help": "Помощь", "Help": "Помощь",
"Help to edit": "Help to edit", "Help to edit": "Help to edit",
"Here you can customize your profile the way you want.": "Here you can customize your profile the way you want.", "Here you can customize your profile the way you want.": "Here you can customize your profile the way you want.",
"Highlight": "Highlight",
"Hooray! Welcome!": "Hooray! Welcome!", "Hooray! Welcome!": "Hooray! Welcome!",
"Horizontal collaborative journalistic platform": "Horizontal collaborative journalism platform", "Horizontal collaborative journalistic platform": "Horizontal collaborative journalism platform",
"Hotkeys": "Горячие клавиши", "Hotkeys": "Горячие клавиши",
@ -100,7 +112,9 @@
"I have no account yet": "I don't have an account yet", "I have no account yet": "I don't have an account yet",
"I know the password": "I know the password", "I know the password": "I know the password",
"Image format not supported": "Image format not supported", "Image format not supported": "Image format not supported",
"Incut": "Incut",
"Independant magazine with an open horizontal cooperation about culture, science and society": "Independant magazine with an open horizontal cooperation about culture, science and society", "Independant magazine with an open horizontal cooperation about culture, science and society": "Independant magazine with an open horizontal cooperation about culture, science and society",
"Insert footnote": "Insert footnote",
"Introduce": "Introduction", "Introduce": "Introduction",
"Invalid email": "Check if your email is correct", "Invalid email": "Check if your email is correct",
"Invalid image URL": "Invalid image URL", "Invalid image URL": "Invalid image URL",
@ -108,6 +122,7 @@
"Invite co-authors": "Invite co-authors", "Invite co-authors": "Invite co-authors",
"Invite to collab": "Invite to Collab", "Invite to collab": "Invite to Collab",
"It does not look like url": "It doesn't look like a link", "It does not look like url": "It doesn't look like a link",
"Italic": "Italic",
"Join": "Join", "Join": "Join",
"Join our maillist": "To receive the best postings, just enter your email", "Join our maillist": "To receive the best postings, just enter your email",
"Join the community": "Join the community", "Join the community": "Join the community",
@ -139,6 +154,7 @@
"Nothing is here": "There is nothing here", "Nothing is here": "There is nothing here",
"Or continue with social network": "Or continue with social network", "Or continue with social network": "Or continue with social network",
"Or paste a link to an image": "Or paste a link to an image", "Or paste a link to an image": "Or paste a link to an image",
"Ordered list": "Ordered list",
"Our regular contributor": "Our regular contributor", "Our regular contributor": "Our regular contributor",
"Paragraphs": "Абзацев", "Paragraphs": "Абзацев",
"Participating": "Participating", "Participating": "Participating",
@ -166,7 +182,9 @@
"Profile settings": "Profile settings", "Profile settings": "Profile settings",
"Publications": "Publications", "Publications": "Publications",
"Publish Settings": "Publish Settings", "Publish Settings": "Publish Settings",
"Punchline": "Punchline",
"Quit": "Quit", "Quit": "Quit",
"Quote": "Quote",
"Quotes": "Quotes", "Quotes": "Quotes",
"Reason uknown": "Reason unknown", "Reason uknown": "Reason unknown",
"Recent": "Fresh", "Recent": "Fresh",
@ -202,6 +220,7 @@
"Subscribe who you like to tune your personal feed": "Subscribe to authors you're interested in to customize your personal feed and get instant updates on new posts and discussions", "Subscribe who you like to tune your personal feed": "Subscribe to authors you're interested in to customize your personal feed and get instant updates on new posts and discussions",
"Subscription": "Subscription", "Subscription": "Subscription",
"Subscriptions": "Subscriptions", "Subscriptions": "Subscriptions",
"Substrate": "Substrate",
"Successfully authorized": "Authorization successful", "Successfully authorized": "Authorization successful",
"Suggest an idea": "Suggest an idea", "Suggest an idea": "Suggest an idea",
"Support us": "Help the magazine", "Support us": "Help the magazine",
@ -255,13 +274,18 @@
"Your name will appear on your profile page and as your signature in publications, comments and responses.": "Your name will appear on your profile page and as your signature in publications, comments and responses", "Your name will appear on your profile page and as your signature in publications, comments and responses.": "Your name will appear on your profile page and as your signature in publications, comments and responses",
"accomplices": "accomplices", "accomplices": "accomplices",
"actions": "actions", "actions": "actions",
"add link": "add link",
"all topics": "all topics", "all topics": "all topics",
"author": "author", "author": "author",
"authors": "authors", "authors": "authors",
"back to menu": "back to menu",
"bold": "bold",
"bookmarks": "bookmarks", "bookmarks": "bookmarks",
"cancel": "Cancel", "cancel": "Cancel",
"cancel_low_caps": "cancel",
"collections": "collections", "collections": "collections",
"community": "community", "community": "community",
"delimiter": "delimiter",
"discussion": "discourse", "discussion": "discourse",
"discussions": "discussions", "discussions": "discussions",
"drafts": "drafts", "drafts": "drafts",
@ -270,12 +294,19 @@
"feed": "feed", "feed": "feed",
"follower": "follower", "follower": "follower",
"general feed": "general tape", "general feed": "general tape",
"header 1": "header 1",
"header 2": "header 2",
"header 3": "header 3",
"invalid password": "invalid password", "invalid password": "invalid password",
"italic": "italic",
"marker list": "marker list",
"my feed": "my ribbon", "my feed": "my ribbon",
"notifications": "notifications", "notifications": "notifications",
"number list": "number list",
"personal data usage and email notifications": "to process personal data and receive email notifications", "personal data usage and email notifications": "to process personal data and receive email notifications",
"post": "post", "post": "post",
"register": "register", "register": "register",
"repeat": "repeat",
"shout": "post", "shout": "post",
"sign up or sign in": "sign up or sign in", "sign up or sign in": "sign up or sign in",
"slug is used by another user": "Slug is already taken by another user", "slug is used by another user": "Slug is already taken by another user",
@ -283,19 +314,5 @@
"topics": "topics", "topics": "topics",
"user already exist": "user already exists", "user already exist": "user already exists",
"view": "view", "view": "view",
"zine": "zine", "zine": "zine"
"back to menu": "back to menu",
"bold": "bold",
"italic": "italic",
"add link": "add link",
"header 1": "header 1",
"header 2": "header 2",
"header 3": "header 3",
"marker list": "marker list",
"number list": "number list",
"delimiter": "delimiter",
"cancel_low_caps": "cancel",
"repeat": "repeat",
"Add signature": "Add signature",
"Substrate": "Substrate"
} }

View File

@ -6,8 +6,14 @@
"Add another image": "Добавить другое изображение", "Add another image": "Добавить другое изображение",
"Add comment": "Комментировать", "Add comment": "Комментировать",
"Add image": "Добавить изображение", "Add image": "Добавить изображение",
"Add link": "Добавить ссылку",
"Add signature": "Добавить подпись",
"Add to bookmarks": "Добавить в закладки", "Add to bookmarks": "Добавить в закладки",
"Add url": "Добавить ссылку",
"Address on Discourse": "Адрес на Дискурсе", "Address on Discourse": "Адрес на Дискурсе",
"Alignment center": "По центру",
"Alignment left": "По левому краю",
"Alignment right": "По правому краю",
"All": "Все", "All": "Все",
"All authors": "Все авторы", "All authors": "Все авторы",
"All posts": "Все публикации", "All posts": "Все публикации",
@ -20,8 +26,10 @@
"Autotypograph": "Автотипограф", "Autotypograph": "Автотипограф",
"Back to main page": "Вернуться на главную", "Back to main page": "Вернуться на главную",
"Become an author": "Стать автором", "Become an author": "Стать автором",
"Bold": "Жирный",
"Bookmarked": "Сохранено", "Bookmarked": "Сохранено",
"Bookmarks": "Закладки", "Bookmarks": "Закладки",
"Bullet list": "Маркированный список",
"By alphabet": "По алфавиту", "By alphabet": "По алфавиту",
"By authors": "По авторам", "By authors": "По авторам",
"By name": "По имени", "By name": "По имени",
@ -90,10 +98,14 @@
"Group Chat": "Общий чат", "Group Chat": "Общий чат",
"Groups": "Группы", "Groups": "Группы",
"Header": "Заголовок", "Header": "Заголовок",
"Header 1": "Заголовок 1",
"Header 2": "Заголовок 2",
"Header 3": "Заголовок 3",
"Headers": "Заголовки", "Headers": "Заголовки",
"Help": "Помощь", "Help": "Помощь",
"Help to edit": "Помочь редактировать", "Help to edit": "Помочь редактировать",
"Here you can customize your profile the way you want.": "Здесь можно настроить свой профиль так, как вы хотите.", "Here you can customize your profile the way you want.": "Здесь можно настроить свой профиль так, как вы хотите.",
"Highlight": "Подсветка",
"Hooray! Welcome!": "Ура! Добро пожаловать!", "Hooray! Welcome!": "Ура! Добро пожаловать!",
"Horizontal collaborative journalistic platform": "Горизонтальная платформа для коллаборативной журналистики", "Horizontal collaborative journalistic platform": "Горизонтальная платформа для коллаборативной журналистики",
"Hotkeys": "Горячие клавиши", "Hotkeys": "Горячие клавиши",
@ -105,7 +117,9 @@
"I have no account yet": "У меня еще нет аккаунта", "I have no account yet": "У меня еще нет аккаунта",
"I know the password": "Я знаю пароль!", "I know the password": "Я знаю пароль!",
"Image format not supported": "Тип изображения не поддерживается", "Image format not supported": "Тип изображения не поддерживается",
"Incut": "Подверстка",
"Independant magazine with an open horizontal cooperation about culture, science and society": "Независимый журнал с открытой горизонтальной редакцией о культуре, науке и обществе", "Independant magazine with an open horizontal cooperation about culture, science and society": "Независимый журнал с открытой горизонтальной редакцией о культуре, науке и обществе",
"Insert footnote": "Вставить сноску",
"Introduce": "Представление", "Introduce": "Представление",
"Invalid email": "Проверьте правильность ввода почты", "Invalid email": "Проверьте правильность ввода почты",
"Invalid image URL": "Некорректная ссылка на изображение", "Invalid image URL": "Некорректная ссылка на изображение",
@ -114,6 +128,7 @@
"Invite experts": "Пригласить экспертов", "Invite experts": "Пригласить экспертов",
"Invite to collab": "Пригласить к участию", "Invite to collab": "Пригласить к участию",
"It does not look like url": "Это не похоже на ссылку", "It does not look like url": "Это не похоже на ссылку",
"Italic": "Курсив",
"Join": "Присоединиться", "Join": "Присоединиться",
"Join our maillist": "Чтобы получать рассылку лучших публикаций, просто укажите свою почту", "Join our maillist": "Чтобы получать рассылку лучших публикаций, просто укажите свою почту",
"Join the community": "Присоединиться к сообществу", "Join the community": "Присоединиться к сообществу",
@ -146,6 +161,7 @@
"Nothing is here": "Здесь ничего нет", "Nothing is here": "Здесь ничего нет",
"Or continue with social network": "Или войдите через соцсеть", "Or continue with social network": "Или войдите через соцсеть",
"Or paste a link to an image": "Или вставьте ссылку на изображение", "Or paste a link to an image": "Или вставьте ссылку на изображение",
"Ordered list": "Нумерованный список",
"Our regular contributor": "Наш постоянный автор", "Our regular contributor": "Наш постоянный автор",
"Paragraphs": "Абзацев", "Paragraphs": "Абзацев",
"Participating": "Участвовать", "Participating": "Участвовать",
@ -177,7 +193,9 @@
"Publications": "Публикации", "Publications": "Публикации",
"Publish": "Опубликовать", "Publish": "Опубликовать",
"Publish Settings": "Настройки публикации", "Publish Settings": "Настройки публикации",
"Punchline": "Панчлайн",
"Quit": "Выйти", "Quit": "Выйти",
"Quote": "Цитата",
"Quotes": "Цитаты", "Quotes": "Цитаты",
"Reason uknown": "Причина неизвестна", "Reason uknown": "Причина неизвестна",
"Recent": "Свежее", "Recent": "Свежее",
@ -215,6 +233,7 @@
"Subscribe who you like to tune your personal feed": "Подпишитесь на интересующих вас авторов, чтобы настроить вашу персональную ленту и моментально узнавать о новых публикациях и обсуждениях", "Subscribe who you like to tune your personal feed": "Подпишитесь на интересующих вас авторов, чтобы настроить вашу персональную ленту и моментально узнавать о новых публикациях и обсуждениях",
"Subscription": "Подписка", "Subscription": "Подписка",
"Subscriptions": "Подписки", "Subscriptions": "Подписки",
"Substrate": "Подложка",
"Successfully authorized": "Авторизация успешна", "Successfully authorized": "Авторизация успешна",
"Suggest an idea": "Предложить идею", "Suggest an idea": "Предложить идею",
"Support us": "Помочь журналу", "Support us": "Помочь журналу",
@ -270,15 +289,20 @@
"Your name will appear on your profile page and as your signature in publications, comments and responses.": "Ваше имя появится на странице вашего профиля и как ваша подпись в публикациях, комментариях и откликах", "Your name will appear on your profile page and as your signature in publications, comments and responses.": "Ваше имя появится на странице вашего профиля и как ваша подпись в публикациях, комментариях и откликах",
"accomplices": "соучастники", "accomplices": "соучастники",
"actions": "действия", "actions": "действия",
"add link": "добавить ссылку",
"all topics": "все темы", "all topics": "все темы",
"author": "автор", "author": "автор",
"authors": "авторы", "authors": "авторы",
"back to menu": "назад в меню",
"bold": "жирный",
"bookmarks": "закладки", "bookmarks": "закладки",
"cancel": "Отмена", "cancel": "Отмена",
"cancel_low_caps": "отменить",
"collections": "коллекции", "collections": "коллекции",
"community": "сообщество", "community": "сообщество",
"create_chat": "Создать чат", "create_chat": "Создать чат",
"create_group": "Создать группу", "create_group": "Создать группу",
"delimiter": "разделитель",
"discourse_theme": "Тема дискурса", "discourse_theme": "Тема дискурса",
"discussion": "дискурс", "discussion": "дискурс",
"discussions": "дискуссии", "discussions": "дискуссии",
@ -288,13 +312,20 @@
"feed": "лента", "feed": "лента",
"follower": "подписчик", "follower": "подписчик",
"general feed": "общая лента", "general feed": "общая лента",
"header 1": "заголовок 1",
"header 2": "заголовок 2",
"header 3": "заголовок 3",
"invalid password": "некорректный пароль", "invalid password": "некорректный пароль",
"italic": "курсив",
"marker list": "маркир. список",
"my feed": "моя лента", "my feed": "моя лента",
"notifications": "уведомления", "notifications": "уведомления",
"number list": "нумер. список",
"or": "или", "or": "или",
"personal data usage and email notifications": "на обработку персональных данных и на получение почтовых уведомлений", "personal data usage and email notifications": "на обработку персональных данных и на получение почтовых уведомлений",
"post": "пост", "post": "пост",
"register": "зарегистрируйтесь", "register": "зарегистрируйтесь",
"repeat": "повторить",
"shout": "пост", "shout": "пост",
"sign in": "войти", "sign in": "войти",
"sign up": "зарегистрироваться", "sign up": "зарегистрироваться",
@ -305,19 +336,5 @@
"topics": "темы", "topics": "темы",
"user already exist": "пользователь уже существует", "user already exist": "пользователь уже существует",
"view": "просмотр", "view": "просмотр",
"zine": "журнал", "zine": "журнал"
"back to menu": "назад в меню",
"bold": "жирный",
"italic": "курсив",
"add link": "добавить ссылку",
"header 1": "заголовок 1",
"header 2": "заголовок 2",
"header 3": "заголовок 3",
"marker list": "маркир. список",
"number list": "нумер. список",
"delimiter": "разделитель",
"cancel_low_caps": "отменить",
"repeat": "повторить",
"Add signature": "Добавить подпись",
"Substrate": "Подложка"
} }

View File

@ -1,8 +1,9 @@
import type { Editor } from '@tiptap/core' import type { Editor } from '@tiptap/core'
import styles from './FigureBubbleMenu.module.scss' import styles from './BubbleMenu.module.scss'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { Icon } from '../../_shared/Icon' import { Icon } from '../../_shared/Icon'
import { useLocalize } from '../../../context/localize' import { useLocalize } from '../../../context/localize'
import { Popover } from '../../_shared/Popover'
type Props = { type Props = {
editor: Editor editor: Editor
@ -10,9 +11,13 @@ type Props = {
} }
export const BlockquoteBubbleMenu = (props: Props) => { export const BlockquoteBubbleMenu = (props: Props) => {
const { t } = useLocalize()
return ( return (
<div ref={props.ref} class={styles.FigureBubbleMenu}> <div ref={props.ref} class={styles.BubbleMenu}>
<Popover content={t('Alignment left')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton)} class={clsx(styles.bubbleMenuButton)}
onClick={() => { onClick={() => {
@ -21,20 +26,32 @@ export const BlockquoteBubbleMenu = (props: Props) => {
> >
<Icon name="editor-image-align-left" /> <Icon name="editor-image-align-left" />
</button> </button>
)}
</Popover>
<Popover content={t('Alignment center')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton)} class={clsx(styles.bubbleMenuButton)}
onClick={() => props.editor.chain().focus().setBlockQuoteFloat(null).run()} onClick={() => props.editor.chain().focus().setBlockQuoteFloat(null).run()}
> >
<Icon name="editor-image-align-center" /> <Icon name="editor-image-align-center" />
</button> </button>
)}
</Popover>
<Popover content={t('Alignment center')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton)} class={clsx(styles.bubbleMenuButton)}
onClick={() => props.editor.chain().focus().setBlockQuoteFloat('right').run()} onClick={() => props.editor.chain().focus().setBlockQuoteFloat('right').run()}
> >
<Icon name="editor-image-align-right" /> <Icon name="editor-image-align-right" />
</button> </button>
)}
</Popover>
</div> </div>
) )
} }

View File

@ -1,4 +1,4 @@
.FigureBubbleMenu { .BubbleMenu {
background: #000; background: #000;
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View File

@ -1,8 +1,9 @@
import type { Editor } from '@tiptap/core' import type { Editor } from '@tiptap/core'
import styles from './FigureBubbleMenu.module.scss' import styles from './BubbleMenu.module.scss'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { Icon } from '../../_shared/Icon' import { Icon } from '../../_shared/Icon'
import { useLocalize } from '../../../context/localize' import { useLocalize } from '../../../context/localize'
import { Popover } from '../../_shared/Popover'
type Props = { type Props = {
editor: Editor editor: Editor
@ -12,29 +13,43 @@ type Props = {
export const FigureBubbleMenu = (props: Props) => { export const FigureBubbleMenu = (props: Props) => {
const { t } = useLocalize() const { t } = useLocalize()
return ( return (
<div ref={props.ref} class={styles.FigureBubbleMenu}> <div ref={props.ref} class={styles.BubbleMenu}>
<Popover content={t('Alignment left')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton)} class={clsx(styles.bubbleMenuButton)}
onClick={() => props.editor.chain().focus().setImageFloat('left').run()} onClick={() => props.editor.chain().focus().setImageFloat('left').run()}
> >
<Icon name="editor-image-align-left" /> <Icon name="editor-image-align-left" />
</button> </button>
)}
</Popover>
<Popover content={t('Alignment center')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton)} class={clsx(styles.bubbleMenuButton)}
onClick={() => props.editor.chain().focus().setImageFloat(null).run()} onClick={() => props.editor.chain().focus().setImageFloat(null).run()}
> >
<Icon name="editor-image-align-center" /> <Icon name="editor-image-align-center" />
</button> </button>
)}
</Popover>
<Popover content={t('Alignment right')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton)} class={clsx(styles.bubbleMenuButton)}
onClick={() => props.editor.chain().focus().setImageFloat('right').run()} onClick={() => props.editor.chain().focus().setImageFloat('right').run()}
> >
<Icon name="editor-image-align-right" /> <Icon name="editor-image-align-right" />
</button> </button>
)}
</Popover>
<div class={styles.delimiter} /> <div class={styles.delimiter} />
<button <button
type="button" type="button"
@ -46,9 +61,13 @@ export const FigureBubbleMenu = (props: Props) => {
<span style={{ color: 'white' }}>{t('Add signature')}</span> <span style={{ color: 'white' }}>{t('Add signature')}</span>
</button> </button>
<div class={styles.delimiter} /> <div class={styles.delimiter} />
<button type="button" class={clsx(styles.bubbleMenuButton)}> <Popover content={t('Add image')}>
{(triggerRef: (el) => void) => (
<button type="button" ref={triggerRef} class={clsx(styles.bubbleMenuButton)}>
<Icon name="editor-image-add" /> <Icon name="editor-image-add" />
</button> </button>
)}
</Popover>
</div> </div>
) )
} }

View File

@ -1,6 +1,6 @@
import { createSignal, Show, For } from 'solid-js' import { createSignal, Show, For } from 'solid-js'
import type { Editor } from '@tiptap/core' import type { Editor } from '@tiptap/core'
import styles from './FigureBubbleMenu.module.scss' import styles from './BubbleMenu.module.scss'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { Icon } from '../../_shared/Icon' import { Icon } from '../../_shared/Icon'
import { useLocalize } from '../../../context/localize' import { useLocalize } from '../../../context/localize'
@ -16,7 +16,7 @@ export const IncutBubbleMenu = (props: Props) => {
const { t } = useLocalize() const { t } = useLocalize()
const [substratBubbleOpen, setSubstratBubbleOpen] = createSignal(false) const [substratBubbleOpen, setSubstratBubbleOpen] = createSignal(false)
return ( return (
<div ref={props.ref} class={styles.FigureBubbleMenu}> <div ref={props.ref} class={styles.BubbleMenu}>
<button <button
type="button" type="button"
class={clsx(styles.bubbleMenuButton)} class={clsx(styles.bubbleMenuButton)}

View File

@ -177,28 +177,25 @@ export const Editor = (props: EditorProps) => {
const isEmptyTextBlock = doc.textBetween(from, to).length === 0 && isTextSelection(selection) const isEmptyTextBlock = doc.textBetween(from, to).length === 0 && isTextSelection(selection)
setIsCommonMarkup(e.isActive('figure')) setIsCommonMarkup(e.isActive('figure'))
return ( return view.hasFocus() && !empty && !isEmptyTextBlock && !e.isActive('image')
view.hasFocus() &&
!empty &&
!isEmptyTextBlock &&
!e.isActive('image') &&
!e.isActive('blockquote') &&
!e.isActive('article')
)
} }
}), }),
BubbleMenu.configure({ BubbleMenu.configure({
pluginKey: 'blockquoteBubbleMenu', pluginKey: 'blockquoteBubbleMenu',
element: blockquoteBubbleMenuRef.current, element: blockquoteBubbleMenuRef.current,
shouldShow: ({ editor: e, view }) => { shouldShow: ({ editor: e, state }) => {
return view.hasFocus() && e.isActive('blockquote') const { selection } = state
const { empty } = selection
return empty && e.isActive('blockquote')
} }
}), }),
BubbleMenu.configure({ BubbleMenu.configure({
pluginKey: 'incutBubbleMenu', pluginKey: 'incutBubbleMenu',
element: incutBubbleMenuRef.current, element: incutBubbleMenuRef.current,
shouldShow: ({ editor: e, view }) => { shouldShow: ({ editor: e, state }) => {
return view.hasFocus() && e.isActive('article') const { selection } = state
const { empty } = selection
return empty && e.isActive('article')
} }
}), }),
BubbleMenu.configure({ BubbleMenu.configure({

View File

@ -2,6 +2,8 @@ import styles from './InlineForm.module.scss'
import { Icon } from '../../_shared/Icon' import { Icon } from '../../_shared/Icon'
import { createSignal } from 'solid-js' import { createSignal } from 'solid-js'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { Popover } from '../../_shared/Popover'
import { useLocalize } from '../../../context/localize'
type Props = { type Props = {
onClose: () => void onClose: () => void
@ -15,6 +17,7 @@ type Props = {
} }
export const InlineForm = (props: Props) => { export const InlineForm = (props: Props) => {
const { t } = useLocalize()
const [formValue, setFormValue] = createSignal(props.initialValue || '') const [formValue, setFormValue] = createSignal(props.initialValue || '')
const [formValueError, setFormValueError] = createSignal<string | undefined>() const [formValueError, setFormValueError] = createSignal<string | undefined>()
@ -61,12 +64,25 @@ export const InlineForm = (props: Props) => {
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
onInput={handleFormInput} onInput={handleFormInput}
/> />
<button type="button" onClick={handleSaveButtonClick} disabled={Boolean(formValueError())}> <Popover content={t('Add link')}>
{(triggerRef: (el) => void) => (
<button
ref={triggerRef}
type="button"
onClick={handleSaveButtonClick}
disabled={Boolean(formValueError())}
>
<Icon name="status-done" /> <Icon name="status-done" />
</button> </button>
<button type="button" onClick={props.onClear}> )}
</Popover>
<Popover content={props.initialValue ? t('Unlink') : t('Cancel')}>
{(triggerRef: (el) => void) => (
<button ref={triggerRef} type="button" onClick={props.onClear}>
{props.initialValue ? <Icon name="editor-unlink" /> : <Icon name="status-cancel" />} {props.initialValue ? <Icon name="editor-unlink" /> : <Icon name="status-cancel" />}
</button> </button>
)}
</Popover>
</div> </div>
<div class={clsx(styles.linkError, { [styles.visible]: Boolean(formValueError()) })}> <div class={clsx(styles.linkError, { [styles.visible]: Boolean(formValueError()) })}>

View File

@ -7,6 +7,7 @@ import { createEditorTransaction } from 'solid-tiptap'
import { useLocalize } from '../../../context/localize' import { useLocalize } from '../../../context/localize'
import { InlineForm } from '../InlineForm' import { InlineForm } from '../InlineForm'
import { validateUrl } from '../../../utils/validateUrl' import { validateUrl } from '../../../utils/validateUrl'
import { Popover } from '../../_shared/Popover'
type BubbleMenuProps = { type BubbleMenuProps = {
editor: Editor editor: Editor
@ -107,7 +108,10 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
<div class={styles.dropDown}> <div class={styles.dropDown}>
<header>{t('Headers')}</header> <header>{t('Headers')}</header>
<div class={styles.actions}> <div class={styles.actions}>
<Popover content={t('Header 1')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton, { class={clsx(styles.bubbleMenuButton, {
[styles.bubbleMenuButtonActive]: isH1() [styles.bubbleMenuButtonActive]: isH1()
@ -119,7 +123,12 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
> >
<Icon name="editor-h1" /> <Icon name="editor-h1" />
</button> </button>
)}
</Popover>
<Popover content={t('Header 2')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton, { class={clsx(styles.bubbleMenuButton, {
[styles.bubbleMenuButtonActive]: isH2() [styles.bubbleMenuButtonActive]: isH2()
@ -131,7 +140,12 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
> >
<Icon name="editor-h2" /> <Icon name="editor-h2" />
</button> </button>
)}
</Popover>
<Popover content={t('Header 3')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton, { class={clsx(styles.bubbleMenuButton, {
[styles.bubbleMenuButtonActive]: isH3() [styles.bubbleMenuButtonActive]: isH3()
@ -143,10 +157,15 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
> >
<Icon name="editor-h3" /> <Icon name="editor-h3" />
</button> </button>
)}
</Popover>
</div> </div>
<header>{t('Quotes')}</header> <header>{t('Quotes')}</header>
<div class={styles.actions}> <div class={styles.actions}>
<Popover content={t('Quote')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton, { class={clsx(styles.bubbleMenuButton, {
[styles.bubbleMenuButtonActive]: isBlockQuote() [styles.bubbleMenuButtonActive]: isBlockQuote()
@ -158,7 +177,12 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
> >
<Icon name="editor-blockquote" /> <Icon name="editor-blockquote" />
</button> </button>
)}
</Popover>
<Popover content={t('Punchline')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton, { class={clsx(styles.bubbleMenuButton, {
[styles.bubbleMenuButtonActive]: isBlockQuote() [styles.bubbleMenuButtonActive]: isBlockQuote()
@ -170,10 +194,15 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
> >
<Icon name="editor-quote" /> <Icon name="editor-quote" />
</button> </button>
)}
</Popover>
</div> </div>
<header>{t('squib')}</header> <header>{t('squib')}</header>
<div class={styles.actions}> <div class={styles.actions}>
<Popover content={t('Incut')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton, { class={clsx(styles.bubbleMenuButton, {
[styles.bubbleMenuButtonActive]: isBlockQuote() [styles.bubbleMenuButtonActive]: isBlockQuote()
@ -185,6 +214,8 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
> >
<Icon name="editor-squib" /> <Icon name="editor-squib" />
</button> </button>
)}
</Popover>
</div> </div>
</div> </div>
</Show> </Show>
@ -192,7 +223,10 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
<div class={styles.delimiter} /> <div class={styles.delimiter} />
</> </>
</Show> </Show>
<Popover content={t('Bold')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton, { class={clsx(styles.bubbleMenuButton, {
[styles.bubbleMenuButtonActive]: isBold() [styles.bubbleMenuButtonActive]: isBold()
@ -201,7 +235,12 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
> >
<Icon name="editor-bold" /> <Icon name="editor-bold" />
</button> </button>
)}
</Popover>
<Popover content={t('Italic')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton, { class={clsx(styles.bubbleMenuButton, {
[styles.bubbleMenuButtonActive]: isItalic() [styles.bubbleMenuButtonActive]: isItalic()
@ -210,9 +249,14 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
> >
<Icon name="editor-italic" /> <Icon name="editor-italic" />
</button> </button>
)}
</Popover>
<Show when={!props.isCommonMarkup}> <Show when={!props.isCommonMarkup}>
<Popover content={t('Highlight')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton, { class={clsx(styles.bubbleMenuButton, {
[styles.bubbleMenuButtonActive]: isHighlight() [styles.bubbleMenuButtonActive]: isHighlight()
@ -221,9 +265,14 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
> >
<div class={styles.toggleHighlight} /> <div class={styles.toggleHighlight} />
</button> </button>
)}
</Popover>
</Show> </Show>
<div class={styles.delimiter} /> <div class={styles.delimiter} />
<Popover content={t('Add url')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
onClick={toggleLinkForm} onClick={toggleLinkForm}
class={clsx(styles.bubbleMenuButton, { class={clsx(styles.bubbleMenuButton, {
@ -232,11 +281,17 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
> >
<Icon name="editor-link" /> <Icon name="editor-link" />
</button> </button>
)}
</Popover>
<Show when={!props.isCommonMarkup}> <Show when={!props.isCommonMarkup}>
<> <>
<button type="button" class={styles.bubbleMenuButton}> <Popover content={t('Insert footnote')}>
{(triggerRef: (el) => void) => (
<button ref={triggerRef} type="button" class={styles.bubbleMenuButton}>
<Icon name="editor-footnote" /> <Icon name="editor-footnote" />
</button> </button>
)}
</Popover>
<div class={styles.delimiter} /> <div class={styles.delimiter} />
<div class={styles.dropDownHolder}> <div class={styles.dropDownHolder}>
<button <button
@ -253,7 +308,10 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
<div class={styles.dropDown}> <div class={styles.dropDown}>
<header>{t('Lists')}</header> <header>{t('Lists')}</header>
<div class={styles.actions}> <div class={styles.actions}>
<Popover content={t('Bullet list')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton, { class={clsx(styles.bubbleMenuButton, {
[styles.bubbleMenuButtonActive]: isBulletList() [styles.bubbleMenuButtonActive]: isBulletList()
@ -265,7 +323,12 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
> >
<Icon name="editor-ul" /> <Icon name="editor-ul" />
</button> </button>
)}
</Popover>
<Popover content={t('Ordered list')}>
{(triggerRef: (el) => void) => (
<button <button
ref={triggerRef}
type="button" type="button"
class={clsx(styles.bubbleMenuButton, { class={clsx(styles.bubbleMenuButton, {
[styles.bubbleMenuButtonActive]: isOrderedList() [styles.bubbleMenuButtonActive]: isOrderedList()
@ -277,6 +340,8 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
> >
<Icon name="editor-ol" /> <Icon name="editor-ol" />
</button> </button>
)}
</Popover>
</div> </div>
</div> </div>
</Show> </Show>