parent
37a6547d57
commit
3db98ac6cb
|
@ -562,11 +562,23 @@ 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);
|
||||||
|
|
||||||
|
.tooltipContent {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
& * {
|
||||||
color: var(--default-color-invert);
|
color: var(--default-color-invert);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
p:last-child {
|
p:last-child {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: '';
|
||||||
|
|
|
@ -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);')
|
||||||
|
|
|
@ -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)}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user