Swiper update article & editor view (#145)
* Swiper update article & editor view * Add link by META+KeyK * Add isFocused check
This commit is contained in:
parent
40af94e101
commit
506b36af7f
|
@ -218,6 +218,7 @@
|
|||
"Quotes": "Quotes",
|
||||
"Reason uknown": "Reason unknown",
|
||||
"Recent": "Fresh",
|
||||
"Remove link": "Remove link",
|
||||
"Reply": "Reply",
|
||||
"Report": "Complain",
|
||||
"Required": "Required",
|
||||
|
@ -321,8 +322,8 @@
|
|||
"back to menu": "back to menu",
|
||||
"bold": "bold",
|
||||
"bookmarks": "bookmarks",
|
||||
"cancel": "Cancel",
|
||||
"cancel_low_caps": "cancel",
|
||||
"Cancel": "Cancel",
|
||||
"cancel": "cancel",
|
||||
"collections": "collections",
|
||||
"community": "community",
|
||||
"delimiter": "delimiter",
|
||||
|
|
|
@ -233,6 +233,7 @@
|
|||
"Reason uknown": "Причина неизвестна",
|
||||
"Recent": "Свежее",
|
||||
"Release date...": "Дата выхода...",
|
||||
"Remove link": "Убрать ссылку",
|
||||
"Reply": "Ответить",
|
||||
"Report": "Пожаловаться",
|
||||
"Required": "Поле обязательно для заполнения",
|
||||
|
@ -339,8 +340,8 @@
|
|||
"back to menu": "назад в меню",
|
||||
"bold": "жирный",
|
||||
"bookmarks": "закладки",
|
||||
"cancel": "Отмена",
|
||||
"cancel_low_caps": "отменить",
|
||||
"Cancel": "Отмена",
|
||||
"cancel": "отменить",
|
||||
"collections": "коллекции",
|
||||
"community": "сообщество",
|
||||
"create_chat": "Создать чат",
|
||||
|
|
|
@ -153,7 +153,13 @@ export const FullArticle = (props: ArticleProps) => {
|
|||
)}
|
||||
</For>
|
||||
</div>
|
||||
<Show when={props.article.cover && props.article.layout !== 'video'}>
|
||||
<Show
|
||||
when={
|
||||
props.article.cover &&
|
||||
props.article.layout !== 'video' &&
|
||||
props.article.layout !== 'image'
|
||||
}
|
||||
>
|
||||
<div
|
||||
class={styles.shoutCover}
|
||||
style={{ 'background-image': `url('${imageProxy(props.article.cover)}')` }}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { createEffect, createSignal, onMount, untrack } from 'solid-js'
|
||||
import { createEffect, createSignal } from 'solid-js'
|
||||
import { createTiptapEditor, useEditorHTML } from 'solid-tiptap'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { Bold } from '@tiptap/extension-bold'
|
||||
|
|
|
@ -53,6 +53,10 @@ export const InlineForm = (props: Props) => {
|
|||
}
|
||||
}
|
||||
|
||||
const handleClear = () => {
|
||||
props.initialValue ? props.onClear() : props.onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<div class={styles.InlineForm}>
|
||||
<div class={styles.form}>
|
||||
|
@ -76,9 +80,9 @@ export const InlineForm = (props: Props) => {
|
|||
</button>
|
||||
)}
|
||||
</Popover>
|
||||
<Popover content={props.initialValue ? t('Unlink') : t('Cancel')}>
|
||||
<Popover content={props.initialValue ? t('Remove link') : t('Cancel')}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
<button ref={triggerRef} type="button" onClick={props.onClear}>
|
||||
<button ref={triggerRef} type="button" onClick={handleClear}>
|
||||
{props.initialValue ? <Icon name="editor-unlink" /> : <Icon name="status-cancel" />}
|
||||
</button>
|
||||
)}
|
||||
|
|
|
@ -237,7 +237,7 @@ export const Panel = (props: Props) => {
|
|||
|
||||
<section class={styles.shortcutList}>
|
||||
<p>
|
||||
{t('cancel_low_caps')}
|
||||
{t('cancel')}
|
||||
<span class={styles.shortcut}>
|
||||
<span class={styles.shortcutButton}>Ctrl</span>
|
||||
<span class={styles.shortcutButton}>Z</span>
|
||||
|
|
|
@ -124,16 +124,21 @@ const SimplifiedEditor = (props: Props) => {
|
|||
})
|
||||
|
||||
const handleKeyDown = async (event) => {
|
||||
if (props.submitByEnter && event.keyCode === 13 && !event.shiftKey && !isEmpty()) {
|
||||
if (isEmpty() || !editor().isFocused) {
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
event.code === 'Enter' &&
|
||||
((props.submitByEnter && !event.shiftKey) || (props.submitByShiftEnter && event.shiftKey))
|
||||
) {
|
||||
event.preventDefault()
|
||||
props.onSubmit(html())
|
||||
handleClear()
|
||||
}
|
||||
|
||||
if (props.submitByShiftEnter && event.keyCode === 13 && event.shiftKey && !isEmpty()) {
|
||||
event.preventDefault()
|
||||
props.onSubmit(html())
|
||||
handleClear()
|
||||
if (event.code === 'KeyK' && (event.metaKey || event.ctrlKey) && !editor().state.selection.empty) {
|
||||
showModal('editorInsertLink')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,7 +149,8 @@ const SimplifiedEditor = (props: Props) => {
|
|||
onCleanup(() => {
|
||||
window.removeEventListener('keydown', handleKeyDown)
|
||||
})
|
||||
|
||||
const handleToggleBlockquote = () => editor().chain().focus().toggleBlockquote().run()
|
||||
const handleInsertLink = () => !editor().state.selection.empty && showModal('editorInsertLink')
|
||||
return (
|
||||
<div
|
||||
class={clsx(styles.SimplifiedEditor, {
|
||||
|
@ -184,7 +190,7 @@ const SimplifiedEditor = (props: Props) => {
|
|||
<button
|
||||
ref={triggerRef}
|
||||
type="button"
|
||||
onClick={() => showModal('editorInsertLink')}
|
||||
onClick={() => handleInsertLink}
|
||||
class={clsx(styles.actionButton, { [styles.active]: isLink() })}
|
||||
>
|
||||
<Icon name="editor-link" />
|
||||
|
@ -197,7 +203,7 @@ const SimplifiedEditor = (props: Props) => {
|
|||
<button
|
||||
ref={triggerRef}
|
||||
type="button"
|
||||
onClick={() => editor().chain().focus().toggleBlockquote().run()}
|
||||
onClick={handleToggleBlockquote}
|
||||
class={clsx(styles.actionButton, { [styles.active]: isBlockquote() })}
|
||||
>
|
||||
<Icon name="editor-quote" />
|
||||
|
@ -221,7 +227,7 @@ const SimplifiedEditor = (props: Props) => {
|
|||
</Show>
|
||||
</div>
|
||||
<div class={styles.buttons}>
|
||||
<Button value={t('cancel')} variant="secondary" disabled={isEmpty()} onClick={handleClear} />
|
||||
<Button value={t('Cancel')} variant="secondary" disabled={isEmpty()} onClick={handleClear} />
|
||||
<Button
|
||||
value={props.submitButtonText ?? t('Send')}
|
||||
variant="primary"
|
||||
|
|
|
@ -88,7 +88,7 @@ const CreateModalContent = (props: Props) => {
|
|||
|
||||
<div class={styles.footer}>
|
||||
<button type="button" class="btn btn-lg fs-3 btn-outline-danger" onClick={reset}>
|
||||
{t('cancel')}
|
||||
{t('Cancel')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
|
|
|
@ -5,7 +5,6 @@ import { Popover } from '../Popover'
|
|||
import { useLocalize } from '../../../context/localize'
|
||||
import { register } from 'swiper/element/bundle'
|
||||
import { DropArea } from '../DropArea'
|
||||
import MD from '../../Article/MD'
|
||||
import { createFileUploader } from '@solid-primitives/upload'
|
||||
import SwiperCore, { Manipulation, Navigation, Pagination } from 'swiper'
|
||||
import { SwiperRef } from './swiper'
|
||||
|
@ -103,9 +102,11 @@ export const SolidSwiper = (props: Props) => {
|
|||
} catch (error) {
|
||||
await showSnackbar({ type: 'error', body: t('Error') })
|
||||
console.error('[runUpload]', error)
|
||||
setLoading(false)
|
||||
}
|
||||
} else {
|
||||
await showSnackbar({ type: 'error', body: t('Invalid file type') })
|
||||
setLoading(false)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -179,52 +180,6 @@ export const SolidSwiper = (props: Props) => {
|
|||
</Popover>
|
||||
</Show>
|
||||
</div>
|
||||
<Switch>
|
||||
<Match when={props.editorMode}>
|
||||
<div class={styles.description}>
|
||||
<input
|
||||
type="text"
|
||||
class={clsx(styles.input, styles.title)}
|
||||
placeholder={t('Enter image title')}
|
||||
value={slide.title}
|
||||
onChange={(event) =>
|
||||
handleSlideDescriptionChange(index(), 'title', event.target.value)
|
||||
}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
class={styles.input}
|
||||
placeholder={t('Specify the source and the name of the author')}
|
||||
value={slide.source}
|
||||
onChange={(event) =>
|
||||
handleSlideDescriptionChange(index(), 'source', event.target.value)
|
||||
}
|
||||
/>
|
||||
<SimplifiedEditor
|
||||
initialContent={slide.body}
|
||||
smallHeight={true}
|
||||
placeholder={t('Enter image description')}
|
||||
onSubmit={(value) => handleSlideDescriptionChange(index(), 'body', value)}
|
||||
submitButtonText={t('Save')}
|
||||
/>
|
||||
</div>
|
||||
</Match>
|
||||
<Match when={!props.editorMode}>
|
||||
<div class={styles.slideDescription}>
|
||||
<Show when={slide?.title}>
|
||||
<div class={styles.articleTitle}>{slide.title}</div>
|
||||
</Show>
|
||||
<Show when={slide?.source}>
|
||||
<div class={styles.source}>{slide.source}</div>
|
||||
</Show>
|
||||
<Show when={slide?.body}>
|
||||
<div class={styles.body}>
|
||||
<MD body={slide.body} />
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</Match>
|
||||
</Switch>
|
||||
</swiper-slide>
|
||||
)}
|
||||
</For>
|
||||
|
@ -249,23 +204,19 @@ export const SolidSwiper = (props: Props) => {
|
|||
{slideIndex() + 1} / {props.images.length}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class={clsx(styles.holder, styles.thumbsHolder)}>
|
||||
<div class={styles.thumbs}>
|
||||
<swiper-container
|
||||
class={'thumbSwiper'}
|
||||
ref={(el) => (thumbSwipeRef.current = el)}
|
||||
slides-per-view={'auto'}
|
||||
free-mode={true}
|
||||
observer={true}
|
||||
space-between={20}
|
||||
auto-scroll-offset={1}
|
||||
watch-overflow={true}
|
||||
slide-to-clicked-slide={true}
|
||||
watch-slides-visibility={true}
|
||||
watch-slides-progress={true}
|
||||
direction={props.editorMode ? 'horizontal' : 'vertical'}
|
||||
slides-offset-after={props.editorMode && 140}
|
||||
slides-offset-after={props.editorMode && 160}
|
||||
slides-offset-before={props.editorMode && 30}
|
||||
>
|
||||
<For each={props.images}>
|
||||
{(slide, index) => (
|
||||
|
@ -337,6 +288,46 @@ export const SolidSwiper = (props: Props) => {
|
|||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
<Show
|
||||
when={props.editorMode}
|
||||
fallback={
|
||||
<div class={styles.slideDescription}>
|
||||
<Show when={props.images[slideIndex()]?.title}>
|
||||
<div class={styles.articleTitle}>{props.images[slideIndex()].title}</div>
|
||||
</Show>
|
||||
<Show when={props.images[slideIndex()]?.source}>
|
||||
<div class={styles.source}>{props.images[slideIndex()].source}</div>
|
||||
</Show>
|
||||
<Show when={props.images[slideIndex()]?.body}>
|
||||
<div class={styles.body} innerHTML={props.images[slideIndex()].body} />
|
||||
</Show>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div class={styles.description}>
|
||||
<input
|
||||
type="text"
|
||||
class={clsx(styles.input, styles.title)}
|
||||
placeholder={t('Enter image title')}
|
||||
value={props.images[slideIndex()].title}
|
||||
onChange={(event) => handleSlideDescriptionChange(slideIndex(), 'title', event.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
class={styles.input}
|
||||
placeholder={t('Specify the source and the name of the author')}
|
||||
value={props.images[slideIndex()].source}
|
||||
onChange={(event) => handleSlideDescriptionChange(slideIndex(), 'source', event.target.value)}
|
||||
/>
|
||||
<SimplifiedEditor
|
||||
initialContent={props.images[slideIndex()].body}
|
||||
smallHeight={true}
|
||||
placeholder={t('Enter image description')}
|
||||
onSubmit={(value) => handleSlideDescriptionChange(slideIndex(), 'body', value)}
|
||||
submitButtonText={t('Save')}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ $navigation-reserve: 32px;
|
|||
.Swiper {
|
||||
display: block;
|
||||
margin: 2rem 0;
|
||||
flex-direction: column;
|
||||
|
||||
&.articleMode {
|
||||
background: var(--background-color-invert);
|
||||
|
@ -137,6 +138,11 @@ $navigation-reserve: 32px;
|
|||
}
|
||||
}
|
||||
}
|
||||
&.editorMode {
|
||||
.holder {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation {
|
||||
background: rgb(0 0 0 / 40%);
|
||||
|
@ -183,6 +189,7 @@ $navigation-reserve: 32px;
|
|||
|
||||
.slideDescription {
|
||||
margin-top: 8px;
|
||||
align-self: flex-start;
|
||||
|
||||
.articleTitle {
|
||||
@include font-size(1.4rem);
|
||||
|
|
Loading…
Reference in New Issue
Block a user