From 6bace7d3117c5567fffce6b62b6e5f69ca76e286 Mon Sep 17 00:00:00 2001 From: Arkadzi Rakouski Date: Mon, 22 Jan 2024 13:44:56 +0300 Subject: [PATCH 1/5] fixes for lightbox (#372) * fixes for lightbox * fix run check --- .../_shared/Lightbox/Lightbox.module.scss | 2 +- src/components/_shared/Lightbox/Lightbox.tsx | 47 ++++++++++++++----- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/components/_shared/Lightbox/Lightbox.module.scss b/src/components/_shared/Lightbox/Lightbox.module.scss index 4958b65a..dd60812e 100644 --- a/src/components/_shared/Lightbox/Lightbox.module.scss +++ b/src/components/_shared/Lightbox/Lightbox.module.scss @@ -8,7 +8,7 @@ display: flex; align-items: center; justify-content: center; - z-index: 10000; + z-index: 99999; animation: 300ms fadeIn; animation-fill-mode: forwards; diff --git a/src/components/_shared/Lightbox/Lightbox.tsx b/src/components/_shared/Lightbox/Lightbox.tsx index e4ad4f5a..b4d399b8 100644 --- a/src/components/_shared/Lightbox/Lightbox.tsx +++ b/src/components/_shared/Lightbox/Lightbox.tsx @@ -30,6 +30,12 @@ export const Lightbox = (props: Props) => { current: null, } + const handleSmoothAction = (action: () => void) => { + setTransitionEnabled(true) + action() + setTimeout(() => setTransitionEnabled(false), TRANSITION_SPEED) + } + const closeLightbox = () => { lightboxRef.current?.classList.add(styles.fadeOut) @@ -40,34 +46,48 @@ export const Lightbox = (props: Props) => { const zoomIn = (event) => { event.stopPropagation() - setTransitionEnabled(true) - setZoomLevel(zoomLevel() * ZOOM_STEP) - setTimeout(() => setTransitionEnabled(false), TRANSITION_SPEED) + + handleSmoothAction(() => { + setZoomLevel(zoomLevel() * ZOOM_STEP) + }) } const zoomOut = (event) => { event.stopPropagation() - setTransitionEnabled(true) - setZoomLevel(zoomLevel() / ZOOM_STEP) - setTimeout(() => setTransitionEnabled(false), TRANSITION_SPEED) + + handleSmoothAction(() => { + setZoomLevel(zoomLevel() / ZOOM_STEP) + }) + } + + const positionReset = () => { + setTranslateX(0) + setTranslateY(0) } const zoomReset = (event) => { event.stopPropagation() - setZoomLevel(1) + + handleSmoothAction(() => { + setZoomLevel(1) + positionReset() + }) } - const handleWheelZoom = (event) => { + const handleMouseWheelZoom = (event) => { event.preventDefault() + event.stopPropagation() + + const isTrackpad = event.ctrlKey + if (isTrackpad) return let scale = zoomLevel() - scale += event.deltaY * -0.01 - scale = Math.min(Math.max(0.125, scale), 4) - setTransitionEnabled(true) - setZoomLevel(scale * ZOOM_STEP) + handleSmoothAction(() => { + setZoomLevel(scale * ZOOM_STEP) + }) } useEscKeyDownHandler(closeLightbox) @@ -130,6 +150,7 @@ export const Lightbox = (props: Props) => {
e.preventDefault()} ref={(el) => (lightboxRef.current = el)} > @@ -154,7 +175,7 @@ export const Lightbox = (props: Props) => { src={getImageUrl(props.image, { noSizeUrlPart: true })} alt={props.imageAlt || ''} onClick={(event) => event.stopPropagation()} - onWheel={handleWheelZoom} + onWheel={handleMouseWheelZoom} style={lightboxStyle()} onMouseDown={onMouseDown} /> From 8a8abd36529941fc3d5ceee6d610bfa1267612c2 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Mon, 22 Jan 2024 13:45:21 +0300 Subject: [PATCH 2/5] Add resize iframe listener (#369) Add resize container listener --- src/components/Article/FullArticle.tsx | 64 +++++++++++++++---- src/components/Editor/extensions/Iframe.ts | 2 + .../Feed/ArticleCard/ArticleCard.tsx | 2 - 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index b1c89325..db57a586 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -4,7 +4,7 @@ import { getPagePath } from '@nanostores/router' import { createPopper } from '@popperjs/core' import { Link, Meta } from '@solidjs/meta' import { clsx } from 'clsx' -import { createEffect, For, createMemo, onMount, Show, createSignal, onCleanup } from 'solid-js' +import { createEffect, For, createMemo, onMount, Show, createSignal, onCleanup, on } from 'solid-js' import { isServer } from 'solid-js/web' import { useLocalize } from '../../context/localize' @@ -42,6 +42,11 @@ type Props = { scrollToComments?: boolean } +type IframeSize = { + width: number + height: number +} + export type ArticlePageSearchParams = { scrollTo: 'comments' commentId: string @@ -173,18 +178,6 @@ export const FullArticle = (props: Props) => { actions: { loadReactionsBy }, } = useReactions() - onMount(async () => { - await loadReactionsBy({ - by: { shout: props.article.slug }, - }) - - setIsReactionsLoaded(true) - }) - - onMount(() => { - document.title = props.article.title - }) - const clickHandlers = [] const documentClickHandlers = [] @@ -286,8 +279,50 @@ export const FullArticle = (props: Props) => { } } - const cover = props.article.cover ?? 'production/image/logo_image.png' + // Check iframes size + const articleContainer: { current: HTMLElement } = { current: null } + const updateIframeSizes = () => { + if (!articleContainer?.current || !props.article.body) return + const iframes = articleContainer?.current?.querySelectorAll('iframe') + if (!iframes) return + const containerWidth = articleContainer.current?.offsetWidth + iframes.forEach((iframe) => { + const style = window.getComputedStyle(iframe) + const originalWidth = iframe.getAttribute('width') || style.width.replace('px', '') + const originalHeight = iframe.getAttribute('height') || style.height.replace('px', '') + const width = Number(originalWidth) + const height = Number(originalHeight) + + if (containerWidth < width) { + const aspectRatio = width / height + iframe.style.width = `${containerWidth}px` + iframe.style.height = `${Math.round(containerWidth / aspectRatio) + 40}px` + } + }) + } + + createEffect( + on( + () => props.article, + () => { + updateIframeSizes() + }, + ), + ) + + onMount(async () => { + await loadReactionsBy({ + by: { shout: props.article.slug }, + }) + setIsReactionsLoaded(true) + document.title = props.article.title + window?.addEventListener('resize', updateIframeSizes) + + onCleanup(() => window.removeEventListener('resize', updateIframeSizes)) + }) + + const cover = props.article.cover ?? 'production/image/logo_image.png' const ogImage = getOpenGraphImageUrl(cover, { title: props.article.title, topic: mainTopic().title, @@ -316,6 +351,7 @@ export const FullArticle = (props: Props) => {
(articleContainer.current = el)} class={clsx('col-md-16 col-lg-14 col-xl-12 offset-md-5', styles.articleContent)} onClick={handleArticleBodyClick} > diff --git a/src/components/Editor/extensions/Iframe.ts b/src/components/Editor/extensions/Iframe.ts index 63ebab2f..95cd0bd7 100644 --- a/src/components/Editor/extensions/Iframe.ts +++ b/src/components/Editor/extensions/Iframe.ts @@ -41,6 +41,8 @@ export const Iframe = Node.create({ default: this.options.allowFullscreen, parseHTML: () => this.options.allowFullscreen, }, + width: { default: null }, + height: { default: null }, } }, diff --git a/src/components/Feed/ArticleCard/ArticleCard.tsx b/src/components/Feed/ArticleCard/ArticleCard.tsx index ff275635..4770defb 100644 --- a/src/components/Feed/ArticleCard/ArticleCard.tsx +++ b/src/components/Feed/ArticleCard/ArticleCard.tsx @@ -7,12 +7,10 @@ import { createMemo, createSignal, For, Show } from 'solid-js' import { useLocalize } from '../../../context/localize' import { useSession } from '../../../context/session' import { router, useRouter } from '../../../stores/router' -import { showModal } from '../../../stores/ui' import { capitalize } from '../../../utils/capitalize' import { getDescription } from '../../../utils/meta' import { Icon } from '../../_shared/Icon' import { Image } from '../../_shared/Image' -import { InviteCoAuthorsModal } from '../../_shared/InviteCoAuthorsModal' import { Popover } from '../../_shared/Popover' import { CoverImage } from '../../Article/CoverImage' import { getShareUrl, SharePopup } from '../../Article/SharePopup' From df8ee62112f9e12473718655fd3074c15b703578 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Mon, 22 Jan 2024 13:45:46 +0300 Subject: [PATCH 3/5] Update tiptap link insert (#368) Update tiptap link insert --- src/components/Editor/Editor.tsx | 13 ++++- src/components/Editor/Prosemirror.scss | 7 +++ src/components/Editor/SimplifiedEditor.tsx | 5 +- .../Editor/TextBubbleMenu/TextBubbleMenu.tsx | 14 +++++- src/components/Editor/extensions/Span.ts | 31 ++++++++++++ .../Editor/extensions/ToggleTextWrap.ts | 50 +++++++++++++++++++ 6 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 src/components/Editor/extensions/Span.ts create mode 100644 src/components/Editor/extensions/ToggleTextWrap.ts diff --git a/src/components/Editor/Editor.tsx b/src/components/Editor/Editor.tsx index 04b2e984..6118d767 100644 --- a/src/components/Editor/Editor.tsx +++ b/src/components/Editor/Editor.tsx @@ -46,6 +46,8 @@ import { Figcaption } from './extensions/Figcaption' import { Figure } from './extensions/Figure' import { Footnote } from './extensions/Footnote' import { Iframe } from './extensions/Iframe' +import { Span } from './extensions/Span' +import { ToggleTextWrap } from './extensions/ToggleTextWrap' import { TrailingNode } from './extensions/TrailingNode' import { TextBubbleMenu } from './TextBubbleMenu' @@ -201,6 +203,8 @@ export const Editor = (props: Props) => { CustomBlockquote, Bold, Italic, + Span, + ToggleTextWrap, Strike, HorizontalRule.configure({ HTMLAttributes: { @@ -208,7 +212,10 @@ export const Editor = (props: Props) => { }, }), Underline, - Link.configure({ + Link.extend({ + inclusive: false, + }).configure({ + autolink: true, openOnClick: false, }), Heading.configure({ @@ -244,6 +251,7 @@ export const Editor = (props: Props) => { Figure, Figcaption, Footnote, + ToggleTextWrap, CharacterCount.configure(), // https://github.com/ueberdosis/tiptap/issues/2589#issuecomment-1093084689 BubbleMenu.configure({ pluginKey: 'textBubbleMenu', @@ -252,6 +260,9 @@ export const Editor = (props: Props) => { const { doc, selection } = state const { empty } = selection const isEmptyTextBlock = doc.textBetween(from, to).length === 0 && isTextSelection(selection) + if (isEmptyTextBlock) { + e.chain().focus().removeTextWrap({ class: 'highlight-fake-selection' }).run() + } setIsCommonMarkup(e.isActive('figcaption')) const result = (view.hasFocus() && diff --git a/src/components/Editor/Prosemirror.scss b/src/components/Editor/Prosemirror.scss index d84e01da..ca78c9f1 100644 --- a/src/components/Editor/Prosemirror.scss +++ b/src/components/Editor/Prosemirror.scss @@ -311,3 +311,10 @@ footnote { background-color: unset; } } + +.highlight-fake-selection { + background: var(--selection-background); + color: var(--selection-color); + border: solid var(--selection-background); + border-width: 5px 0; +} diff --git a/src/components/Editor/SimplifiedEditor.tsx b/src/components/Editor/SimplifiedEditor.tsx index a4aa4d7b..a549781e 100644 --- a/src/components/Editor/SimplifiedEditor.tsx +++ b/src/components/Editor/SimplifiedEditor.tsx @@ -117,7 +117,10 @@ const SimplifiedEditor = (props: Props) => { Paragraph, Bold, Italic, - Link.configure({ + Link.extend({ + inclusive: false, + }).configure({ + autolink: true, openOnClick: false, }), CharacterCount.configure({ diff --git a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx index c7bb2d32..7457b009 100644 --- a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx +++ b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx @@ -129,11 +129,21 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { }) }) + const handleOpenLinkForm = () => { + props.editor.chain().focus().addTextWrap({ class: 'highlight-fake-selection' }).run() + setLinkEditorOpen(true) + } + + const handleCloseLinkForm = () => { + setLinkEditorOpen(false) + props.editor.chain().focus().removeTextWrap({ class: 'highlight-fake-selection' }).run() + } + return (
- setLinkEditorOpen(false)} /> + {