insert link enter fix (#94)
* floating plus fix, esc fix * a href="#" -> span
This commit is contained in:
parent
47d14b0a5d
commit
bd6e014cdf
|
@ -19,6 +19,7 @@ import { Title } from '@solidjs/meta'
|
||||||
import { useLocalize } from '../../context/localize'
|
import { useLocalize } from '../../context/localize'
|
||||||
import stylesHeader from '../Nav/Header.module.scss'
|
import stylesHeader from '../Nav/Header.module.scss'
|
||||||
import styles from './Article.module.scss'
|
import styles from './Article.module.scss'
|
||||||
|
import { imageProxy } from '../../utils/imageProxy'
|
||||||
|
|
||||||
interface ArticleProps {
|
interface ArticleProps {
|
||||||
article: Shout
|
article: Shout
|
||||||
|
@ -152,7 +153,7 @@ export const FullArticle = (props: ArticleProps) => {
|
||||||
<Show when={props.article.cover}>
|
<Show when={props.article.cover}>
|
||||||
<div
|
<div
|
||||||
class={styles.shoutCover}
|
class={styles.shoutCover}
|
||||||
style={{ 'background-image': `url('${props.article.cover}')` }}
|
style={{ 'background-image': `url('${imageProxy(props.article.cover)}')` }}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
background: #fff;
|
background: #fff;
|
||||||
left: 24px;
|
left: 24px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -2px;
|
top: -4px;
|
||||||
min-width: 64vw;
|
min-width: 64vw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => {
|
||||||
const [selectedMenuItem, setSelectedMenuItem] = createSignal<MenuItem | undefined>()
|
const [selectedMenuItem, setSelectedMenuItem] = createSignal<MenuItem | undefined>()
|
||||||
const [menuOpen, setMenuOpen] = createSignal<boolean>(false)
|
const [menuOpen, setMenuOpen] = createSignal<boolean>(false)
|
||||||
const menuRef: { current: HTMLDivElement } = { current: null }
|
const menuRef: { current: HTMLDivElement } = { current: null }
|
||||||
|
const plusButtonRef: { current: HTMLButtonElement } = { current: null }
|
||||||
const handleEmbedFormSubmit = async (value: string) => {
|
const handleEmbedFormSubmit = async (value: string) => {
|
||||||
// TODO: add support instagram embed (blockquote)
|
// TODO: add support instagram embed (blockquote)
|
||||||
const emb = await embedData(value)
|
const emb = await embedData(value)
|
||||||
|
@ -62,7 +63,11 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => {
|
||||||
|
|
||||||
useOutsideClickHandler({
|
useOutsideClickHandler({
|
||||||
containerRef: menuRef,
|
containerRef: menuRef,
|
||||||
handler: () => {
|
handler: (e) => {
|
||||||
|
if (plusButtonRef.current.contains(e.target)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (menuOpen()) {
|
if (menuOpen()) {
|
||||||
setMenuOpen(false)
|
setMenuOpen(false)
|
||||||
}
|
}
|
||||||
|
@ -82,6 +87,7 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => {
|
||||||
<>
|
<>
|
||||||
<div ref={props.ref} class={styles.editorFloatingMenu}>
|
<div ref={props.ref} class={styles.editorFloatingMenu}>
|
||||||
<button
|
<button
|
||||||
|
ref={(el) => (plusButtonRef.current = el)}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMenuOpen(!menuOpen())
|
setMenuOpen(!menuOpen())
|
||||||
|
|
|
@ -18,7 +18,8 @@ export const InlineForm = (props: Props) => {
|
||||||
const [formValue, setFormValue] = createSignal(props.initialValue || '')
|
const [formValue, setFormValue] = createSignal(props.initialValue || '')
|
||||||
const [formValueError, setFormValueError] = createSignal<string | undefined>()
|
const [formValueError, setFormValueError] = createSignal<string | undefined>()
|
||||||
|
|
||||||
const handleFormInput = (value) => {
|
const handleFormInput = (e) => {
|
||||||
|
const value = e.currentTarget.value
|
||||||
setFormValueError()
|
setFormValueError()
|
||||||
setFormValue(value)
|
setFormValue(value)
|
||||||
}
|
}
|
||||||
|
@ -36,16 +37,16 @@ export const InlineForm = (props: Props) => {
|
||||||
props.onClose()
|
props.onClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleKeyPress = async (event) => {
|
const handleKeyDown = async (e) => {
|
||||||
setFormValueError('')
|
setFormValueError('')
|
||||||
const key = event.key
|
|
||||||
|
|
||||||
if (key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault()
|
||||||
await handleSaveButtonClick()
|
await handleSaveButtonClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'Esc') {
|
if (e.key === 'Escape' && props.onClear) {
|
||||||
props.onClear
|
props.onClear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,8 +58,8 @@ export const InlineForm = (props: Props) => {
|
||||||
type="text"
|
type="text"
|
||||||
value={props.initialValue ?? ''}
|
value={props.initialValue ?? ''}
|
||||||
placeholder={props.placeholder}
|
placeholder={props.placeholder}
|
||||||
onKeyPress={(e) => handleKeyPress(e)}
|
onKeyDown={handleKeyDown}
|
||||||
onInput={(e) => handleFormInput(e.currentTarget.value)}
|
onInput={handleFormInput}
|
||||||
/>
|
/>
|
||||||
<button type="button" onClick={handleSaveButtonClick} disabled={Boolean(formValueError())}>
|
<button type="button" onClick={handleSaveButtonClick} disabled={Boolean(formValueError())}>
|
||||||
<Icon name="status-done" />
|
<Icon name="status-done" />
|
||||||
|
|
|
@ -56,7 +56,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
.link {
|
||||||
|
cursor: pointer;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
color: rgb(255 255 255 / 35%);
|
color: rgb(255 255 255 / 35%);
|
||||||
|
|
|
@ -42,18 +42,15 @@ export const Panel = (props: Props) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleSaveLinkClick = (e) => {
|
const handleSaveClick = () => {
|
||||||
e.preventDefault()
|
|
||||||
saveShout()
|
saveShout()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePublishLinkClick = (e) => {
|
const handlePublishClick = () => {
|
||||||
e.preventDefault()
|
|
||||||
publishShout()
|
publishShout()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleFixTypographyLinkClick = (e) => {
|
const handleFixTypographyClick = () => {
|
||||||
e.preventDefault()
|
|
||||||
const html = useEditorHTML(() => editorRef.current())
|
const html = useEditorHTML(() => editorRef.current())
|
||||||
editorRef.current().commands.setContent(typograf.execute(html()))
|
editorRef.current().commands.setContent(typograf.execute(html()))
|
||||||
}
|
}
|
||||||
|
@ -74,27 +71,27 @@ export const Panel = (props: Props) => {
|
||||||
<div class={clsx(styles.actionsHolder, styles.scrolled)}>
|
<div class={clsx(styles.actionsHolder, styles.scrolled)}>
|
||||||
<section>
|
<section>
|
||||||
<p>
|
<p>
|
||||||
<a href="#" onClick={handlePublishLinkClick}>
|
<span class={styles.link} onClick={handlePublishClick}>
|
||||||
{t('Publish')}
|
{t('Publish')}
|
||||||
</a>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="#" onClick={handleSaveLinkClick}>
|
<span class={styles.link} onClick={handleSaveClick}>
|
||||||
{t('Save draft')}
|
{t('Save draft')}
|
||||||
</a>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<p>
|
<p>
|
||||||
<a class={styles.linkWithIcon}>
|
<a class={clsx(styles.link, styles.linkWithIcon)}>
|
||||||
<Icon name="eye" class={styles.icon} />
|
<Icon name="eye" class={styles.icon} />
|
||||||
{t('Preview')}
|
{t('Preview')}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a
|
<a
|
||||||
class={styles.linkWithIcon}
|
class={clsx(styles.link, styles.linkWithIcon)}
|
||||||
onClick={() => toggleEditorPanel()}
|
onClick={() => toggleEditorPanel()}
|
||||||
href={getPagePath(router, 'edit', { shoutId: props.shoutId.toString() })}
|
href={getPagePath(router, 'edit', { shoutId: props.shoutId.toString() })}
|
||||||
>
|
>
|
||||||
|
@ -103,7 +100,7 @@ export const Panel = (props: Props) => {
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a class={styles.linkWithIcon}>
|
<a class={clsx(styles.link, styles.linkWithIcon)}>
|
||||||
<Icon name="feed-discussion" class={styles.icon} />
|
<Icon name="feed-discussion" class={styles.icon} />
|
||||||
{t('FAQ')}
|
{t('FAQ')}
|
||||||
</a>
|
</a>
|
||||||
|
@ -112,10 +109,11 @@ export const Panel = (props: Props) => {
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<p>
|
<p>
|
||||||
<a>{t('Invite co-authors')}</a>
|
<a class={styles.link}>{t('Invite co-authors')}</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a
|
<a
|
||||||
|
class={styles.link}
|
||||||
onClick={() => toggleEditorPanel()}
|
onClick={() => toggleEditorPanel()}
|
||||||
href={getPagePath(router, 'editSettings', { shoutId: props.shoutId.toString() })}
|
href={getPagePath(router, 'editSettings', { shoutId: props.shoutId.toString() })}
|
||||||
>
|
>
|
||||||
|
@ -123,24 +121,30 @@ export const Panel = (props: Props) => {
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a onClick={handleFixTypographyLinkClick} href="#">
|
<span class={styles.link} onClick={handleFixTypographyClick}>
|
||||||
{t('Fix typography')}
|
{t('Fix typography')}
|
||||||
</a>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a>{t('Corrections history')}</a>
|
<a class={styles.link}>{t('Corrections history')}</a>
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<p>
|
<p>
|
||||||
<a href="/how-to-write-a-good-article">{t('How to write a good article')}</a>
|
<a class={styles.link} href="/how-to-write-a-good-article">
|
||||||
|
{t('How to write a good article')}
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="#">{t('Hotkeys')}</a>
|
<a class={styles.link} href="#">
|
||||||
|
{t('Hotkeys')}
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="#">{t('Help')}</a>
|
<a class={styles.link} href="#">
|
||||||
|
{t('Help')}
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Show, createMemo } from 'solid-js'
|
||||||
import './DialogCard.module.scss'
|
import './DialogCard.module.scss'
|
||||||
import styles from './DialogAvatar.module.scss'
|
import styles from './DialogAvatar.module.scss'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
|
import { imageProxy } from '../../utils/imageProxy'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
name: string
|
name: string
|
||||||
|
@ -46,7 +47,7 @@ const DialogAvatar = (props: Props) => {
|
||||||
style={{ 'background-color': `${randomBg()}` }}
|
style={{ 'background-color': `${randomBg()}` }}
|
||||||
>
|
>
|
||||||
<Show when={Boolean(props.url)} fallback={<div class={styles.letter}>{nameFirstLetter()}</div>}>
|
<Show when={Boolean(props.url)} fallback={<div class={styles.letter}>{nameFirstLetter()}</div>}>
|
||||||
<div class={styles.imageHolder} style={{ 'background-image': `url(${props.url})` }} />
|
<div class={styles.imageHolder} style={{ 'background-image': `url(${imageProxy(props.url)})` }} />
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,9 +16,11 @@ export const Modal = (props: ModalProps) => {
|
||||||
const { modal } = useModalStore()
|
const { modal } = useModalStore()
|
||||||
|
|
||||||
const handleHide = () => {
|
const handleHide = () => {
|
||||||
|
if (modal()) {
|
||||||
hideModal()
|
hideModal()
|
||||||
props.onClose && props.onClose()
|
props.onClose && props.onClose()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEscKeyDownHandler(handleHide)
|
useEscKeyDownHandler(handleHide)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,9 @@ import { onCleanup, onMount } from 'solid-js'
|
||||||
|
|
||||||
export const useEscKeyDownHandler = (onEscKeyDown: () => void) => {
|
export const useEscKeyDownHandler = (onEscKeyDown: () => void) => {
|
||||||
const keydownHandler = (e: KeyboardEvent) => {
|
const keydownHandler = (e: KeyboardEvent) => {
|
||||||
if (e.key === 'Escape') onEscKeyDown()
|
if (e.key === 'Escape') {
|
||||||
|
onEscKeyDown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
|
|
@ -2,24 +2,24 @@ import { onCleanup, onMount } from 'solid-js'
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
containerRef: { current: HTMLElement }
|
containerRef: { current: HTMLElement }
|
||||||
handler: () => void
|
handler: (e: MouseEvent & { target: Element }) => void
|
||||||
// if predicate is present
|
// if predicate is present
|
||||||
// handler is called only if predicate function returns true
|
// handler is called only if predicate function returns true
|
||||||
predicate?: () => boolean
|
predicate?: (e: MouseEvent & { target: Element }) => boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useOutsideClickHandler = (options: Options) => {
|
export const useOutsideClickHandler = (options: Options) => {
|
||||||
const { predicate, containerRef, handler } = options
|
const { predicate, containerRef, handler } = options
|
||||||
const handleClickOutside = (event: MouseEvent & { target: Element }) => {
|
const handleClickOutside = (e: MouseEvent & { target: Element }) => {
|
||||||
if (predicate && !predicate()) {
|
if (predicate && !predicate(e)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.target === containerRef.current || containerRef.current?.contains(event.target)) {
|
if (e.target === containerRef.current || containerRef.current?.contains(e.target)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
handler()
|
handler(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user