From c4866bfc8581edb41e99079c45134b8cca5cff5a Mon Sep 17 00:00:00 2001 From: kudlajz Date: Wed, 30 Oct 2024 18:31:00 +0100 Subject: [PATCH 1/9] Tweaked text in coverage placeholder and added button to allow file upload --- .../components/InputPlaceholder.tsx | 3 ++ .../CoveragePlaceholderElement.module.scss | 9 ++++ .../elements/CoveragePlaceholderElement.tsx | 41 ++++++++++++++++--- packages/slate-editor/src/icons/Upload.svg | 3 ++ packages/slate-editor/src/icons/index.ts | 1 + 5 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.module.scss create mode 100644 packages/slate-editor/src/icons/Upload.svg diff --git a/packages/slate-editor/src/extensions/placeholders/components/InputPlaceholder.tsx b/packages/slate-editor/src/extensions/placeholders/components/InputPlaceholder.tsx index 187130e3f..a6c60c521 100644 --- a/packages/slate-editor/src/extensions/placeholders/components/InputPlaceholder.tsx +++ b/packages/slate-editor/src/extensions/placeholders/components/InputPlaceholder.tsx @@ -11,6 +11,7 @@ import styles from './InputPlaceholder.module.scss'; import type { ContentRenderProps } from './Placeholder'; export interface Props extends Omit { + children?: ReactNode; title: ReactNode | FunctionComponent; description: ReactNode | FunctionComponent; // Input properties @@ -28,6 +29,7 @@ export interface Props extends Omit { const isEsc = isHotkey('esc'); export function InputPlaceholder({ + children, className, action, title, @@ -135,6 +137,7 @@ export function InputPlaceholder({ value={value} /> + {children} ); } diff --git a/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.module.scss b/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.module.scss new file mode 100644 index 000000000..f75001263 --- /dev/null +++ b/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.module.scss @@ -0,0 +1,9 @@ +@import "styles/variables"; + +.Action { + margin: $spacing-2 0 0; +} + +.Button { + text-decoration: none; +} diff --git a/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.tsx b/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.tsx index eabfe62c8..425e7672b 100644 --- a/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.tsx +++ b/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.tsx @@ -7,12 +7,13 @@ import React, { type DragEvent, useEffect, useState } from 'react'; import { Transforms } from 'slate'; import { useSelected, useSlateStatic } from 'slate-react'; -import { SearchInput } from '#components'; -import { PlaceholderCoverage } from '#icons'; +import { Button, SearchInput } from '#components'; +import { PlaceholderCoverage, Upload } from '#icons'; import { URL_WITH_OPTIONAL_PROTOCOL_REGEXP, useFunction } from '#lib'; import { createCoverage } from '#extensions/coverage'; import { EventsEditor } from '#modules/events'; +import { UploadcareEditor } from '#modules/uploadcare'; import { InputPlaceholder } from '../components/InputPlaceholder'; import { withLoadingDots } from '../components/LoadingDots'; @@ -24,6 +25,8 @@ import { replacePlaceholder } from '../lib'; import type { PlaceholderNode } from '../PlaceholderNode'; import { PlaceholdersManager, usePlaceholderManagement } from '../PlaceholdersManager'; +import styles from './CoveragePlaceholderElement.module.scss'; + type Url = string; type CoverageRef = Pick; type Mode = 'search' | 'create'; @@ -49,6 +52,22 @@ export function CoveragePlaceholderElement({ PlaceholdersManager.activate(element); }); + const handleUpload = useFunction(async () => { + const files = await UploadcareEditor.upload(editor, { multiple: false }); + if (!files) { + return; + } + + setMode('search'); + const uploading = toProgressPromise(files[0]).then(async (fileInfo: PrezlyFileInfo) => { + const file = UploadcareFile.createFromUploadcareWidgetPayload(fileInfo); + const ref = await onCreateCoverage(file); + + return { coverage: { id: ref.coverage.id } }; + }); + PlaceholdersManager.register(element.type, element.uuid, uploading); + }); + const handleDrop = useFunction((event: DragEvent) => { event.preventDefault(); event.stopPropagation(); @@ -133,10 +152,11 @@ export function CoveragePlaceholderElement({ renderFrame={() => mode === 'create' ? ( PlaceholdersManager.deactivate(element)} onRemove={handleRemove} onSubmit={handleSubmitUrl} - /> + > +
+ +
+
) : undefined } inputTitle="Coverage" diff --git a/packages/slate-editor/src/icons/Upload.svg b/packages/slate-editor/src/icons/Upload.svg new file mode 100644 index 000000000..af359eee8 --- /dev/null +++ b/packages/slate-editor/src/icons/Upload.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/slate-editor/src/icons/index.ts b/packages/slate-editor/src/icons/index.ts index 12e1d465e..4529c7381 100644 --- a/packages/slate-editor/src/icons/index.ts +++ b/packages/slate-editor/src/icons/index.ts @@ -109,6 +109,7 @@ export { default as Resize } from './Resize.svg'; export { default as Search } from './Search.svg'; export { default as SocialFacebook } from './Social-Facebook.svg'; export { default as SocialTwitter } from './Social-Twitter.svg'; +export { default as Upload } from './Upload.svg'; export { default as User } from './User.svg'; export { default as Video } from './Video.svg'; export { default as WarningCircle } from './Warning-Circle.svg'; From 19398e905256b3f649dff255db74571ea55ea831 Mon Sep 17 00:00:00 2001 From: kudlajz Date: Wed, 30 Oct 2024 19:28:44 +0100 Subject: [PATCH 2/9] Use popper to position the suggestions panel --- .../components/SearchInput/SearchInput.tsx | 4 ++ .../SearchInput/Suggestions.module.scss | 1 + .../components/SearchInput/Suggestions.tsx | 50 ++++++++++++------- .../elements/ContactPlaceholderElement.tsx | 1 + .../elements/CoveragePlaceholderElement.tsx | 1 + .../GalleryBookmarkPlaceholderElement.tsx | 1 + .../InlineContactPlaceholderElement.tsx | 1 + .../StoryBookmarkPlaceholderElement.tsx | 1 + .../elements/StoryEmbedPlaceholderElement.tsx | 1 + 9 files changed, 42 insertions(+), 19 deletions(-) diff --git a/packages/slate-editor/src/components/SearchInput/SearchInput.tsx b/packages/slate-editor/src/components/SearchInput/SearchInput.tsx index 8eb339d3d..fbfc1fc6d 100644 --- a/packages/slate-editor/src/components/SearchInput/SearchInput.tsx +++ b/packages/slate-editor/src/components/SearchInput/SearchInput.tsx @@ -128,6 +128,7 @@ export function SearchInput({ suggestions, onClose: handleClose, onSelect, + origin: rootRef.current, children: suggestions.map((suggestion) => renderSuggestion({ suggestion, @@ -184,6 +185,7 @@ export namespace SearchInput { activeElement: HTMLElement | undefined; activeSuggestion: Suggestion | undefined; loading: boolean; + origin: HTMLElement | null; query: string; suggestions: Suggestion[]; onClose: () => void; @@ -227,6 +229,7 @@ function defaultRenderSuggestions({ activeElement, query, suggestions, + origin, children, }: SearchInput.Props.Suggestions) { return ( @@ -234,6 +237,7 @@ function defaultRenderSuggestions({ activeElement={activeElement} query={query} suggestions={suggestions} + origin={origin} > {children} diff --git a/packages/slate-editor/src/components/SearchInput/Suggestions.module.scss b/packages/slate-editor/src/components/SearchInput/Suggestions.module.scss index bd9a6693e..e2e7b614d 100644 --- a/packages/slate-editor/src/components/SearchInput/Suggestions.module.scss +++ b/packages/slate-editor/src/components/SearchInput/Suggestions.module.scss @@ -3,4 +3,5 @@ .Suggestions { display: flex; flex-direction: column; + width: 100%; } diff --git a/packages/slate-editor/src/components/SearchInput/Suggestions.tsx b/packages/slate-editor/src/components/SearchInput/Suggestions.tsx index de1310d52..de5dec144 100644 --- a/packages/slate-editor/src/components/SearchInput/Suggestions.tsx +++ b/packages/slate-editor/src/components/SearchInput/Suggestions.tsx @@ -1,6 +1,9 @@ +import type { FlipModifier } from '@popperjs/core/lib/modifiers/flip'; +import type { PreventOverflowModifier } from '@popperjs/core/lib/modifiers/preventOverflow'; import classNames from 'classnames'; import type { HTMLAttributes, ReactNode } from 'react'; import React, { useEffect, useRef, useState } from 'react'; +import { usePopper } from 'react-popper'; import { useFunction } from '#lib'; @@ -12,11 +15,12 @@ import type { Suggestion } from './types'; export interface Props extends HTMLAttributes { activeElement: HTMLElement | undefined; - minHeight?: number; + footer?: ReactNode; maxHeight?: number; + minHeight?: number; + origin: HTMLElement | null; query: string; suggestions: Suggestion[]; - footer?: ReactNode; } export function Suggestions({ @@ -24,28 +28,41 @@ export function Suggestions({ children, className, footer, - minHeight = 200, maxHeight = 500, + minHeight = 200, + origin, query, suggestions, ...attributes }: Props) { const [height, setHeight] = useState(); - const [calculatedMaxHeight, setMaxHeight] = useState(); const container = useRef(null); const childrenContainer = useRef(null); const [scrollarea, setScrollarea] = useState(null); + const popper = usePopper(origin, container.current, { + modifiers: [ + { + name: 'flip', + enabled: true, + options: { + fallbackPlacements: ['top'], + }, + } satisfies Partial, + { + name: 'preventOverflow', + enabled: true, + options: { + altAxis: true, + mainAxis: true, + }, + } satisfies Partial + ], + placement: 'bottom', + }); + const updatePanelSize = useFunction(() => { setHeight(childrenContainer.current?.getBoundingClientRect().height); - - if (container.current) { - const viewport = document.body.getBoundingClientRect(); - const rect = container.current.getBoundingClientRect(); - setMaxHeight(clamp(viewport.height - rect.top - 4, minHeight, maxHeight)); - } else { - setMaxHeight(undefined); - } }); useEffect(() => { @@ -71,7 +88,8 @@ export function Suggestions({ return ( ({ ); } - -function clamp(num: number, min: number, max: number) { - if (num < min) return min; - if (num > max) return max; - return num; -} diff --git a/packages/slate-editor/src/extensions/placeholders/elements/ContactPlaceholderElement.tsx b/packages/slate-editor/src/extensions/placeholders/elements/ContactPlaceholderElement.tsx index 8a8e683fd..e892f71f5 100644 --- a/packages/slate-editor/src/extensions/placeholders/elements/ContactPlaceholderElement.tsx +++ b/packages/slate-editor/src/extensions/placeholders/elements/ContactPlaceholderElement.tsx @@ -72,6 +72,7 @@ export function ContactPlaceholderElement({ query={props.query} suggestions={props.suggestions} footer={renderSuggestionsFooter?.(props)} + origin={props.origin} > {props.children} diff --git a/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.tsx b/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.tsx index 425e7672b..c3b070e21 100644 --- a/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.tsx +++ b/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.tsx @@ -145,6 +145,7 @@ export function CoveragePlaceholderElement({ query={props.query} suggestions={props.suggestions} footer={renderSuggestionsFooter?.({ ...props, onMode })} + origin={props.origin} > {props.children} diff --git a/packages/slate-editor/src/extensions/placeholders/elements/GalleryBookmarkPlaceholderElement.tsx b/packages/slate-editor/src/extensions/placeholders/elements/GalleryBookmarkPlaceholderElement.tsx index dce7dc62e..dddbe8ac6 100644 --- a/packages/slate-editor/src/extensions/placeholders/elements/GalleryBookmarkPlaceholderElement.tsx +++ b/packages/slate-editor/src/extensions/placeholders/elements/GalleryBookmarkPlaceholderElement.tsx @@ -95,6 +95,7 @@ export function GalleryBookmarkPlaceholderElement({ query={props.query} suggestions={props.suggestions} footer={renderSuggestionsFooter?.(props)} + origin={props.origin} > {props.children} diff --git a/packages/slate-editor/src/extensions/placeholders/elements/InlineContactPlaceholderElement/InlineContactPlaceholderElement.tsx b/packages/slate-editor/src/extensions/placeholders/elements/InlineContactPlaceholderElement/InlineContactPlaceholderElement.tsx index eb14aa0db..bdd2af456 100644 --- a/packages/slate-editor/src/extensions/placeholders/elements/InlineContactPlaceholderElement/InlineContactPlaceholderElement.tsx +++ b/packages/slate-editor/src/extensions/placeholders/elements/InlineContactPlaceholderElement/InlineContactPlaceholderElement.tsx @@ -101,6 +101,7 @@ export function InlineContactPlaceholderElement({ query={props.query} suggestions={props.suggestions} footer={renderSuggestionsFooter?.(props)} + origin={props.origin} > {props.children} diff --git a/packages/slate-editor/src/extensions/placeholders/elements/StoryBookmarkPlaceholderElement.tsx b/packages/slate-editor/src/extensions/placeholders/elements/StoryBookmarkPlaceholderElement.tsx index aef09964c..b28bc312e 100644 --- a/packages/slate-editor/src/extensions/placeholders/elements/StoryBookmarkPlaceholderElement.tsx +++ b/packages/slate-editor/src/extensions/placeholders/elements/StoryBookmarkPlaceholderElement.tsx @@ -71,6 +71,7 @@ export function StoryBookmarkPlaceholderElement({ query={props.query} suggestions={props.suggestions} footer={renderSuggestionsFooter?.(props)} + origin={props.origin} > {props.children} diff --git a/packages/slate-editor/src/extensions/placeholders/elements/StoryEmbedPlaceholderElement.tsx b/packages/slate-editor/src/extensions/placeholders/elements/StoryEmbedPlaceholderElement.tsx index c9d471ca1..d94d13ccf 100644 --- a/packages/slate-editor/src/extensions/placeholders/elements/StoryEmbedPlaceholderElement.tsx +++ b/packages/slate-editor/src/extensions/placeholders/elements/StoryEmbedPlaceholderElement.tsx @@ -71,6 +71,7 @@ export function StoryEmbedPlaceholderElement({ query={props.query} suggestions={props.suggestions} footer={renderSuggestionsFooter?.(props)} + origin={props.origin} > {props.children} From afe9856d16bed8b0d3be10d7ddcb6c8ee80b8073 Mon Sep 17 00:00:00 2001 From: kudlajz Date: Wed, 30 Oct 2024 19:30:22 +0100 Subject: [PATCH 3/9] Fixed prettier issues --- .../slate-editor/src/components/SearchInput/Suggestions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/slate-editor/src/components/SearchInput/Suggestions.tsx b/packages/slate-editor/src/components/SearchInput/Suggestions.tsx index de5dec144..07c42442c 100644 --- a/packages/slate-editor/src/components/SearchInput/Suggestions.tsx +++ b/packages/slate-editor/src/components/SearchInput/Suggestions.tsx @@ -56,7 +56,7 @@ export function Suggestions({ altAxis: true, mainAxis: true, }, - } satisfies Partial + } satisfies Partial, ], placement: 'bottom', }); From 824cd168dfd3de7248e5f20675f2634098fef22c Mon Sep 17 00:00:00 2001 From: kudlajz Date: Wed, 30 Oct 2024 19:32:38 +0100 Subject: [PATCH 4/9] Provide null origin for the stories --- .../src/components/SearchInput/SearchInput.stories.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/slate-editor/src/components/SearchInput/SearchInput.stories.tsx b/packages/slate-editor/src/components/SearchInput/SearchInput.stories.tsx index 184824686..e48ab1671 100644 --- a/packages/slate-editor/src/components/SearchInput/SearchInput.stories.tsx +++ b/packages/slate-editor/src/components/SearchInput/SearchInput.stories.tsx @@ -89,6 +89,7 @@ export function WithFooter() { renderSuggestions={({ activeElement, query, suggestions, children }) => ( activeElement={activeElement} + origin={null} query={query} suggestions={suggestions} footer={ From a32ffd6d8f74945872f669180366a192c5966979 Mon Sep 17 00:00:00 2001 From: kudlajz Date: Thu, 31 Oct 2024 11:58:39 +0100 Subject: [PATCH 5/9] Use camelCase for class names --- .../elements/CoveragePlaceholderElement.module.scss | 4 ++-- .../placeholders/elements/CoveragePlaceholderElement.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.module.scss b/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.module.scss index f75001263..20dd6420a 100644 --- a/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.module.scss +++ b/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.module.scss @@ -1,9 +1,9 @@ @import "styles/variables"; -.Action { +.action { margin: $spacing-2 0 0; } -.Button { +.button { text-decoration: none; } diff --git a/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.tsx b/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.tsx index c3b070e21..39402795f 100644 --- a/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.tsx +++ b/packages/slate-editor/src/extensions/placeholders/elements/CoveragePlaceholderElement.tsx @@ -167,9 +167,9 @@ export function CoveragePlaceholderElement({ onRemove={handleRemove} onSubmit={handleSubmitUrl} > -
+