parent
37a6547d57
commit
3db98ac6cb
|
@ -562,11 +562,23 @@ a[data-toggle='tooltip'] {
|
|||
max-width: 400px;
|
||||
box-sizing: border-box;
|
||||
background: var(--black-500);
|
||||
|
||||
.tooltipContent {
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
|
||||
& * {
|
||||
color: var(--default-color-invert);
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
|
|
|
@ -138,7 +138,12 @@ export const FullArticle = (props: Props) => {
|
|||
tooltipElements.forEach((element) => {
|
||||
const tooltip = document.createElement('div')
|
||||
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)
|
||||
if (element.tagName === 'a') {
|
||||
element.setAttribute('href', 'javascript: void(0);')
|
||||
|
|
|
@ -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 uniqolor from 'uniqolor'
|
||||
import * as Y from 'yjs'
|
||||
|
@ -63,6 +63,7 @@ export const Editor = (props: Props) => {
|
|||
const { user } = useSession()
|
||||
|
||||
const [isCommonMarkup, setIsCommonMarkup] = createSignal(false)
|
||||
const [shouldShowTextBubbleMenu, setShouldShowTextBubbleMenu] = createSignal(false)
|
||||
|
||||
const docName = `shout-${props.shoutId}`
|
||||
|
||||
|
@ -185,7 +186,11 @@ export const Editor = (props: Props) => {
|
|||
const { empty } = selection
|
||||
const isEmptyTextBlock = doc.textBetween(from, to).length === 0 && isTextSelection(selection)
|
||||
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: {
|
||||
sticky: true
|
||||
|
@ -246,6 +251,10 @@ export const Editor = (props: Props) => {
|
|||
}
|
||||
})
|
||||
|
||||
onCleanup(() => {
|
||||
editor().destroy()
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="row">
|
||||
|
@ -256,6 +265,7 @@ export const Editor = (props: Props) => {
|
|||
</div>
|
||||
|
||||
<TextBubbleMenu
|
||||
shouldShow={shouldShowTextBubbleMenu()}
|
||||
isCommonMarkup={isCommonMarkup()}
|
||||
editor={editor()}
|
||||
ref={(el) => (textBubbleMenuRef.current = el)}
|
||||
|
|
|
@ -263,6 +263,9 @@ footnote {
|
|||
display: inline-flex;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
width: 0.8rem;
|
||||
height: 1em;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
|
|
@ -55,6 +55,11 @@
|
|||
bottom: -1rem;
|
||||
transition: 0.3s ease-in-out;
|
||||
|
||||
&.alwaysVisible {
|
||||
opacity: unset;
|
||||
bottom: unset;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
@ -52,9 +52,11 @@ type Props = {
|
|||
submitByEnter?: boolean
|
||||
submitByShiftEnter?: boolean
|
||||
onlyBubbleControls?: boolean
|
||||
controlsAlwaysVisible?: boolean
|
||||
}
|
||||
|
||||
export const MAX_DESCRIPTION_LIMIT = 400
|
||||
|
||||
const SimplifiedEditor = (props: Props) => {
|
||||
const { t } = useLocalize()
|
||||
const [counter, setCounter] = createSignal<number>()
|
||||
|
@ -217,6 +219,7 @@ const SimplifiedEditor = (props: Props) => {
|
|||
})
|
||||
|
||||
onCleanup(() => {
|
||||
editor().destroy()
|
||||
window.removeEventListener('keydown', handleKeyDown)
|
||||
})
|
||||
|
||||
|
@ -233,6 +236,7 @@ const SimplifiedEditor = (props: Props) => {
|
|||
setCounter(editor().storage.characterCount.characters())
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={(el) => (wrapperEditorElRef.current = el)}
|
||||
|
@ -252,7 +256,7 @@ const SimplifiedEditor = (props: Props) => {
|
|||
</Show>
|
||||
<div ref={(el) => (editorElRef.current = el)} />
|
||||
<Show when={!props.onlyBubbleControls}>
|
||||
<div class={styles.controls}>
|
||||
<div class={clsx(styles.controls, { [styles.alwaysVisible]: props.controlsAlwaysVisible })}>
|
||||
<div class={styles.actions}>
|
||||
<Popover content={t('Bold')}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
|
@ -350,6 +354,7 @@ const SimplifiedEditor = (props: Props) => {
|
|||
</Show>
|
||||
<Show when={props.onlyBubbleControls}>
|
||||
<TextBubbleMenu
|
||||
shouldShow={true}
|
||||
isCommonMarkup={true}
|
||||
editor={editor()}
|
||||
ref={(el) => (textBubbleMenuRef.current = el)}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
.TextBubbleMenu {
|
||||
background: var(--editor-bubble-menu-background);
|
||||
box-shadow: 0 4px 10px rgba(#000, 0.25);
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
|
||||
&.growWidth {
|
||||
min-width: 460px;
|
||||
|
|
|
@ -8,13 +8,12 @@ import { useLocalize } from '../../../context/localize'
|
|||
import { Popover } from '../../_shared/Popover'
|
||||
import { InsertLinkForm } from '../InsertLinkForm'
|
||||
import SimplifiedEditor from '../SimplifiedEditor'
|
||||
import { Button } from '../../_shared/Button'
|
||||
import { showModal } from '../../../stores/ui'
|
||||
|
||||
type BubbleMenuProps = {
|
||||
editor: Editor
|
||||
isCommonMarkup: boolean
|
||||
ref: (el: HTMLDivElement) => void
|
||||
shouldShow: boolean
|
||||
}
|
||||
|
||||
export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
||||
|
@ -32,6 +31,13 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
const [footnoteEditorOpen, setFootnoteEditorOpen] = createSignal(false)
|
||||
const [footNote, setFootNote] = createSignal<string>()
|
||||
|
||||
createEffect(() => {
|
||||
if (!props.shouldShow) {
|
||||
setFootNote()
|
||||
setFootnoteEditorOpen(false)
|
||||
}
|
||||
})
|
||||
|
||||
const isBold = isActive('bold')
|
||||
const isItalic = isActive('italic')
|
||||
const isH1 = isActive('heading', { level: 2 })
|
||||
|
@ -65,9 +71,15 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
}
|
||||
}
|
||||
|
||||
const currentFootnoteValue = createEditorTransaction(
|
||||
const updateCurrentFootnoteValue = createEditorTransaction(
|
||||
() => props.editor,
|
||||
(ed) => (ed && ed.getAttributes('footnote').value) || ''
|
||||
(ed) => {
|
||||
if (!isFootnote()) {
|
||||
return
|
||||
}
|
||||
const value = ed.getAttributes('footnote').value
|
||||
setFootNote(value)
|
||||
}
|
||||
)
|
||||
|
||||
const handleAddFootnote = (footnote) => {
|
||||
|
@ -81,7 +93,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
}
|
||||
|
||||
const handleOpenFootnoteEditor = () => {
|
||||
setFootNote(currentFootnoteValue())
|
||||
updateCurrentFootnoteValue()
|
||||
setFootnoteEditorOpen(true)
|
||||
}
|
||||
|
||||
|
@ -100,13 +112,13 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
<InsertLinkForm editor={props.editor} onClose={() => setLinkEditorOpen(false)} />
|
||||
</Match>
|
||||
<Match when={footnoteEditorOpen()}>
|
||||
<Button size={'S'} onClick={() => showModal('uploadImage')} value={'img'} />
|
||||
<SimplifiedEditor
|
||||
controlsAlwaysVisible={true}
|
||||
imageEnabled={true}
|
||||
placeholder={t('Enter footnote text')}
|
||||
onSubmit={(value) => handleAddFootnote(value)}
|
||||
variant={'bordered'}
|
||||
initialContent={currentFootnoteValue().value ?? null}
|
||||
initialContent={footNote()}
|
||||
onCancel={() => {
|
||||
setFootnoteEditorOpen(false)
|
||||
}}
|
||||
|
|
|
@ -26,11 +26,7 @@ export const Footnote = Node.create({
|
|||
return {
|
||||
value: {
|
||||
default: null,
|
||||
parseHTML: (element) => {
|
||||
return {
|
||||
value: element.dataset.value
|
||||
}
|
||||
},
|
||||
parseHTML: (element) => element.dataset.value || null,
|
||||
renderHTML: (attributes) => {
|
||||
return {
|
||||
'data-value': attributes.value
|
||||
|
@ -59,11 +55,8 @@ export const Footnote = Node.create({
|
|||
({ tr, state }) => {
|
||||
const { selection } = state
|
||||
const position = selection.$to.pos
|
||||
|
||||
console.log('!!! attributes:', attributes)
|
||||
const node = this.type.create(attributes)
|
||||
tr.insert(position, node)
|
||||
tr.insertText('\u00A0', position + 1) // it's make selection visible
|
||||
return true
|
||||
},
|
||||
updateFootnote:
|
||||
|
|
Loading…
Reference in New Issue
Block a user