Footnote fixpack (#208)

* Footnote hotfixies
This commit is contained in:
Ilya Y 2023-09-05 08:49:19 +03:00 committed by GitHub
parent 37a6547d57
commit 3db98ac6cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 69 additions and 22 deletions

View File

@ -562,10 +562,22 @@ a[data-toggle='tooltip'] {
max-width: 400px; max-width: 400px;
box-sizing: border-box; box-sizing: border-box;
background: var(--black-500); background: var(--black-500);
color: var(--default-color-invert);
p:last-child { .tooltipContent {
margin: 0; max-height: 300px;
overflow: auto;
& * {
color: var(--default-color-invert);
}
a {
text-decoration: underline;
}
p:last-child {
margin: 0;
}
} }
&::after { &::after {

View File

@ -138,7 +138,12 @@ export const FullArticle = (props: Props) => {
tooltipElements.forEach((element) => { tooltipElements.forEach((element) => {
const tooltip = document.createElement('div') const tooltip = document.createElement('div')
tooltip.classList.add(styles.tooltip) tooltip.classList.add(styles.tooltip)
tooltip.innerHTML = element.dataset.originalTitle || element.dataset.value const tooltipContent = document.createElement('div')
tooltipContent.classList.add(styles.tooltipContent)
tooltipContent.innerHTML = element.dataset.originalTitle || element.dataset.value
tooltip.appendChild(tooltipContent)
document.body.appendChild(tooltip) document.body.appendChild(tooltip)
if (element.tagName === 'a') { if (element.tagName === 'a') {
element.setAttribute('href', 'javascript: void(0);') element.setAttribute('href', 'javascript: void(0);')

View File

@ -1,4 +1,4 @@
import { createEffect, createSignal, Show } from 'solid-js' import { createEffect, createSignal, onCleanup, Show } from 'solid-js'
import { createTiptapEditor, useEditorHTML } from 'solid-tiptap' import { createTiptapEditor, useEditorHTML } from 'solid-tiptap'
import uniqolor from 'uniqolor' import uniqolor from 'uniqolor'
import * as Y from 'yjs' import * as Y from 'yjs'
@ -63,6 +63,7 @@ export const Editor = (props: Props) => {
const { user } = useSession() const { user } = useSession()
const [isCommonMarkup, setIsCommonMarkup] = createSignal(false) const [isCommonMarkup, setIsCommonMarkup] = createSignal(false)
const [shouldShowTextBubbleMenu, setShouldShowTextBubbleMenu] = createSignal(false)
const docName = `shout-${props.shoutId}` const docName = `shout-${props.shoutId}`
@ -185,7 +186,11 @@ export const Editor = (props: Props) => {
const { empty } = selection const { empty } = selection
const isEmptyTextBlock = doc.textBetween(from, to).length === 0 && isTextSelection(selection) const isEmptyTextBlock = doc.textBetween(from, to).length === 0 && isTextSelection(selection)
setIsCommonMarkup(e.isActive('figcaption')) setIsCommonMarkup(e.isActive('figcaption'))
return view.hasFocus() && !empty && !isEmptyTextBlock && !e.isActive('image') const result =
(view.hasFocus() && !empty && !isEmptyTextBlock && !e.isActive('image')) ||
e.isActive('footnote')
setShouldShowTextBubbleMenu(result)
return result
}, },
tippyOptions: { tippyOptions: {
sticky: true sticky: true
@ -246,6 +251,10 @@ export const Editor = (props: Props) => {
} }
}) })
onCleanup(() => {
editor().destroy()
})
return ( return (
<> <>
<div class="row"> <div class="row">
@ -256,6 +265,7 @@ export const Editor = (props: Props) => {
</div> </div>
<TextBubbleMenu <TextBubbleMenu
shouldShow={shouldShowTextBubbleMenu()}
isCommonMarkup={isCommonMarkup()} isCommonMarkup={isCommonMarkup()}
editor={editor()} editor={editor()}
ref={(el) => (textBubbleMenuRef.current = el)} ref={(el) => (textBubbleMenuRef.current = el)}

View File

@ -263,6 +263,9 @@ footnote {
display: inline-flex; display: inline-flex;
position: relative; position: relative;
cursor: pointer; cursor: pointer;
width: 0.8rem;
height: 1em;
&:before { &:before {
content: ''; content: '';
position: absolute; position: absolute;

View File

@ -55,6 +55,11 @@
bottom: -1rem; bottom: -1rem;
transition: 0.3s ease-in-out; transition: 0.3s ease-in-out;
&.alwaysVisible {
opacity: unset;
bottom: unset;
}
.actions { .actions {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -52,9 +52,11 @@ type Props = {
submitByEnter?: boolean submitByEnter?: boolean
submitByShiftEnter?: boolean submitByShiftEnter?: boolean
onlyBubbleControls?: boolean onlyBubbleControls?: boolean
controlsAlwaysVisible?: boolean
} }
export const MAX_DESCRIPTION_LIMIT = 400 export const MAX_DESCRIPTION_LIMIT = 400
const SimplifiedEditor = (props: Props) => { const SimplifiedEditor = (props: Props) => {
const { t } = useLocalize() const { t } = useLocalize()
const [counter, setCounter] = createSignal<number>() const [counter, setCounter] = createSignal<number>()
@ -217,6 +219,7 @@ const SimplifiedEditor = (props: Props) => {
}) })
onCleanup(() => { onCleanup(() => {
editor().destroy()
window.removeEventListener('keydown', handleKeyDown) window.removeEventListener('keydown', handleKeyDown)
}) })
@ -233,6 +236,7 @@ const SimplifiedEditor = (props: Props) => {
setCounter(editor().storage.characterCount.characters()) setCounter(editor().storage.characterCount.characters())
} }
}) })
return ( return (
<div <div
ref={(el) => (wrapperEditorElRef.current = el)} ref={(el) => (wrapperEditorElRef.current = el)}
@ -252,7 +256,7 @@ const SimplifiedEditor = (props: Props) => {
</Show> </Show>
<div ref={(el) => (editorElRef.current = el)} /> <div ref={(el) => (editorElRef.current = el)} />
<Show when={!props.onlyBubbleControls}> <Show when={!props.onlyBubbleControls}>
<div class={styles.controls}> <div class={clsx(styles.controls, { [styles.alwaysVisible]: props.controlsAlwaysVisible })}>
<div class={styles.actions}> <div class={styles.actions}>
<Popover content={t('Bold')}> <Popover content={t('Bold')}>
{(triggerRef: (el) => void) => ( {(triggerRef: (el) => void) => (
@ -350,6 +354,7 @@ const SimplifiedEditor = (props: Props) => {
</Show> </Show>
<Show when={props.onlyBubbleControls}> <Show when={props.onlyBubbleControls}>
<TextBubbleMenu <TextBubbleMenu
shouldShow={true}
isCommonMarkup={true} isCommonMarkup={true}
editor={editor()} editor={editor()}
ref={(el) => (textBubbleMenuRef.current = el)} ref={(el) => (textBubbleMenuRef.current = el)}

View File

@ -1,6 +1,8 @@
.TextBubbleMenu { .TextBubbleMenu {
background: var(--editor-bubble-menu-background); background: var(--editor-bubble-menu-background);
box-shadow: 0 4px 10px rgba(#000, 0.25); box-shadow: 0 4px 10px rgba(#000, 0.25);
max-height: 300px;
overflow: auto;
&.growWidth { &.growWidth {
min-width: 460px; min-width: 460px;

View File

@ -8,13 +8,12 @@ import { useLocalize } from '../../../context/localize'
import { Popover } from '../../_shared/Popover' import { Popover } from '../../_shared/Popover'
import { InsertLinkForm } from '../InsertLinkForm' import { InsertLinkForm } from '../InsertLinkForm'
import SimplifiedEditor from '../SimplifiedEditor' import SimplifiedEditor from '../SimplifiedEditor'
import { Button } from '../../_shared/Button'
import { showModal } from '../../../stores/ui'
type BubbleMenuProps = { type BubbleMenuProps = {
editor: Editor editor: Editor
isCommonMarkup: boolean isCommonMarkup: boolean
ref: (el: HTMLDivElement) => void ref: (el: HTMLDivElement) => void
shouldShow: boolean
} }
export const TextBubbleMenu = (props: BubbleMenuProps) => { export const TextBubbleMenu = (props: BubbleMenuProps) => {
@ -32,6 +31,13 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
const [footnoteEditorOpen, setFootnoteEditorOpen] = createSignal(false) const [footnoteEditorOpen, setFootnoteEditorOpen] = createSignal(false)
const [footNote, setFootNote] = createSignal<string>() const [footNote, setFootNote] = createSignal<string>()
createEffect(() => {
if (!props.shouldShow) {
setFootNote()
setFootnoteEditorOpen(false)
}
})
const isBold = isActive('bold') const isBold = isActive('bold')
const isItalic = isActive('italic') const isItalic = isActive('italic')
const isH1 = isActive('heading', { level: 2 }) const isH1 = isActive('heading', { level: 2 })
@ -65,9 +71,15 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
} }
} }
const currentFootnoteValue = createEditorTransaction( const updateCurrentFootnoteValue = createEditorTransaction(
() => props.editor, () => props.editor,
(ed) => (ed && ed.getAttributes('footnote').value) || '' (ed) => {
if (!isFootnote()) {
return
}
const value = ed.getAttributes('footnote').value
setFootNote(value)
}
) )
const handleAddFootnote = (footnote) => { const handleAddFootnote = (footnote) => {
@ -81,7 +93,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
} }
const handleOpenFootnoteEditor = () => { const handleOpenFootnoteEditor = () => {
setFootNote(currentFootnoteValue()) updateCurrentFootnoteValue()
setFootnoteEditorOpen(true) setFootnoteEditorOpen(true)
} }
@ -100,13 +112,13 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
<InsertLinkForm editor={props.editor} onClose={() => setLinkEditorOpen(false)} /> <InsertLinkForm editor={props.editor} onClose={() => setLinkEditorOpen(false)} />
</Match> </Match>
<Match when={footnoteEditorOpen()}> <Match when={footnoteEditorOpen()}>
<Button size={'S'} onClick={() => showModal('uploadImage')} value={'img'} />
<SimplifiedEditor <SimplifiedEditor
controlsAlwaysVisible={true}
imageEnabled={true} imageEnabled={true}
placeholder={t('Enter footnote text')} placeholder={t('Enter footnote text')}
onSubmit={(value) => handleAddFootnote(value)} onSubmit={(value) => handleAddFootnote(value)}
variant={'bordered'} variant={'bordered'}
initialContent={currentFootnoteValue().value ?? null} initialContent={footNote()}
onCancel={() => { onCancel={() => {
setFootnoteEditorOpen(false) setFootnoteEditorOpen(false)
}} }}

View File

@ -26,11 +26,7 @@ export const Footnote = Node.create({
return { return {
value: { value: {
default: null, default: null,
parseHTML: (element) => { parseHTML: (element) => element.dataset.value || null,
return {
value: element.dataset.value
}
},
renderHTML: (attributes) => { renderHTML: (attributes) => {
return { return {
'data-value': attributes.value 'data-value': attributes.value
@ -59,11 +55,8 @@ export const Footnote = Node.create({
({ tr, state }) => { ({ tr, state }) => {
const { selection } = state const { selection } = state
const position = selection.$to.pos const position = selection.$to.pos
console.log('!!! attributes:', attributes)
const node = this.type.create(attributes) const node = this.type.create(attributes)
tr.insert(position, node) tr.insert(position, node)
tr.insertText('\u00A0', position + 1) // it's make selection visible
return true return true
}, },
updateFootnote: updateFootnote: