insert link enter fix (#94)

* floating plus fix, esc fix

* a href="#" -> span
This commit is contained in:
Igor Lobanov 2023-05-12 15:45:31 +02:00 committed by GitHub
parent 47d14b0a5d
commit bd6e014cdf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 60 additions and 42 deletions

View File

@ -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>

View File

@ -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;
} }
} }

View File

@ -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())

View File

@ -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" />

View File

@ -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%);

View File

@ -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>

View File

@ -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>
) )

View File

@ -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)

View File

@ -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(() => {

View File

@ -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(() => {