Skip to content

Commit

Permalink
Merge pull request #326 from prezly/feature/dev-8821-coverage-placeho…
Browse files Browse the repository at this point in the history
…lder

[DEV-8821] Feature - Coverage placeholder
  • Loading branch information
e1himself authored Sep 30, 2022
2 parents a0a92af + c44f722 commit ee894a7
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function Suggestions<T>({
className,
footer,
minHeight = 200,
maxHeight = 1000,
maxHeight = 500,
query,
suggestions,
...attributes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export namespace PlaceholderNode {
export enum Type {
ATTACHMENT = 'placeholder:attachment',
CONTACT = 'placeholder:contact',
COVERAGE = 'placeholder:coverage',
EMBED = 'placeholder:embed',
GALLERY = 'placeholder:gallery',
IMAGE = 'placeholder:image',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { NewsroomRef } from '@prezly/sdk';
import type { Extension } from '@prezly/slate-commons';
import React from 'react';

import { CoveragePlaceholderElement } from './elements';
import {
AttachmentPlaceholderElement,
ContactPlaceholderElement,
Expand All @@ -28,6 +29,12 @@ export interface Parameters {
ContactPlaceholderElement.Props,
'getSuggestions' | 'renderEmpty' | 'renderSuggestion' | 'renderSuggestionsFooter'
>;
withCoveragePlaceholders?:
| false
| Pick<
CoveragePlaceholderElement.Props,
'getSuggestions' | 'renderEmpty' | 'renderSuggestion' | 'renderSuggestionsFooter'
>;
withEmbedPlaceholders?: false | { fetchOembed: FetchOEmbedFn };
withGalleryPlaceholders?: boolean | { newsroom?: NewsroomRef };
withImagePlaceholders?: boolean | { withCaptions: boolean; newsroom?: NewsroomRef };
Expand All @@ -39,6 +46,7 @@ export interface Parameters {
export function PlaceholdersExtension({
withAttachmentPlaceholders = false,
withContactPlaceholders = false,
withCoveragePlaceholders = false,
withEmbedPlaceholders = false,
withGalleryPlaceholders = false,
withImagePlaceholders = false,
Expand All @@ -55,6 +63,7 @@ export function PlaceholdersExtension({
removeDisabledPlaceholders({
withAttachmentPlaceholders: Boolean(withAttachmentPlaceholders),
withContactPlaceholders: Boolean(withContactPlaceholders),
withCoveragePlaceholders: Boolean(withCoveragePlaceholders),
withEmbedPlaceholders: Boolean(withEmbedPlaceholders),
withGalleryPlaceholders: Boolean(withGalleryPlaceholders),
withImagePlaceholders: Boolean(withImagePlaceholders),
Expand Down Expand Up @@ -88,6 +97,20 @@ export function PlaceholdersExtension({
</ContactPlaceholderElement>
);
}
if (
withCoveragePlaceholders &&
isPlaceholderNode(element, PlaceholderNode.Type.COVERAGE)
) {
return (
<CoveragePlaceholderElement
{...withCoveragePlaceholders}
attributes={attributes}
element={element}
>
{children}
</CoveragePlaceholderElement>
);
}
if (withEmbedPlaceholders && isPlaceholderNode(element, PlaceholderNode.Type.EMBED)) {
return (
<EmbedPlaceholderElement
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import type { ProgressPromise } from '@prezly/progress-promise';
import type { OEmbedInfo } from '@prezly/sdk';
import type { AttachmentNode, ContactNode, GalleryNode, ImageNode } from '@prezly/slate-types';
import type {
AttachmentNode,
ContactNode,
CoverageNode,
GalleryNode,
ImageNode,
} from '@prezly/slate-types';
import { noop } from 'lodash-es';
import { useEffect, useState } from 'react';

Expand All @@ -18,6 +24,9 @@ interface Data {
[Type.CONTACT]: {
contact: ContactNode['contact'];
};
[Type.COVERAGE]: {
coverage: CoverageNode['coverage'];
};
[Type.EMBED]: {
url: string;
oembed?: OEmbedInfo; // `oembed` is undefined if an error occurred
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import type { CoverageNode } from '@prezly/slate-types';
import React from 'react';
import { useSlateStatic } from 'slate-react';

import { SearchInput } from '#components';
import { PlaceholderCoverage } from '#icons';
import { useFunction } from '#lib';

import { createCoverage } from '#extensions/coverage';
import { EventsEditor } from '#modules/events';

import {
type Props as BaseProps,
SearchInputPlaceholderElement,
} from '../components/SearchInputPlaceholderElement';
import { replacePlaceholder } from '../lib';
import type { PlaceholderNode } from '../PlaceholderNode';
import { PlaceholdersManager, usePlaceholderManagement } from '../PlaceholdersManager';

type CoverageRef = Pick<CoverageNode, 'coverage'>;

export function CoveragePlaceholderElement({
children,
element,
getSuggestions,
renderEmpty,
renderSuggestion,
renderSuggestionsFooter,
...props
}: CoveragePlaceholderElement.Props) {
const editor = useSlateStatic();

const handleTrigger = useFunction(() => {
PlaceholdersManager.activate(element);
});

const handleSelect = useFunction((data: CoverageRef) => {
EventsEditor.dispatchEvent(editor, 'coverage-dialog-submitted', {
coverage_id: data.coverage.id,
});

replacePlaceholder(editor, element, createCoverage(data.coverage.id));
});

usePlaceholderManagement(element.type, element.uuid, {
onTrigger: handleTrigger,
});

return (
<SearchInputPlaceholderElement<CoverageRef>
{...props}
element={element}
// Core
format="card"
icon={PlaceholderCoverage}
title="Click to insert coverage"
description="Add your Prezly coverage"
// Input
getSuggestions={getSuggestions}
renderEmpty={renderEmpty}
renderSuggestion={renderSuggestion}
renderSuggestions={(props) => (
<SearchInput.Suggestions
activeElement={props.activeElement}
query={props.query}
suggestions={props.suggestions}
footer={renderSuggestionsFooter?.(props)}
>
{props.children}
</SearchInput.Suggestions>
)}
inputTitle="Coverage"
inputDescription="Select coverage to insert"
inputPlaceholder="Search for coverage"
onSelect={handleSelect}
>
{children}
</SearchInputPlaceholderElement>
);
}

export namespace CoveragePlaceholderElement {
export interface Props
extends Omit<
BaseProps<CoverageRef>,
| 'onSelect'
| 'icon'
| 'title'
| 'description'
| 'inputTitle'
| 'inputDescription'
| 'inputPlaceholder'
| 'renderSuggestions'
> {
element: PlaceholderNode<PlaceholderNode.Type.COVERAGE>;
renderSuggestionsFooter?: BaseProps<CoverageRef>['renderSuggestions'];
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { AttachmentPlaceholderElement } from './AttachmentPlaceholderElement';
export { ContactPlaceholderElement } from './ContactPlaceholderElement';
export { CoveragePlaceholderElement } from './CoveragePlaceholderElement';
export { EmbedPlaceholderElement } from './EmbedPlaceholderElement';
export { GalleryPlaceholderElement } from './GalleryPlaceholderElement';
export { ImagePlaceholderElement } from './ImagePlaceholderElement';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const normalizations = [
removeDisabledPlaceholders({
withAttachmentPlaceholders: Boolean('ENABLED'),
withContactPlaceholders: false,
withCoveragePlaceholders: false,
withEmbedPlaceholders: false,
withGalleryPlaceholders: Boolean('ENABLED'),
withImagePlaceholders: Boolean('ENABLED'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { PlaceholderNode } from '../PlaceholderNode';
interface Parameters {
withAttachmentPlaceholders: boolean;
withContactPlaceholders: boolean;
withCoveragePlaceholders: boolean;
withImagePlaceholders: boolean;
withGalleryPlaceholders: boolean;
withEmbedPlaceholders: boolean;
Expand All @@ -17,6 +18,7 @@ interface Parameters {
export function removeDisabledPlaceholders({
withAttachmentPlaceholders,
withContactPlaceholders,
withCoveragePlaceholders,
withImagePlaceholders,
withGalleryPlaceholders,
withEmbedPlaceholders,
Expand All @@ -27,6 +29,7 @@ export function removeDisabledPlaceholders({
const config = {
[PlaceholderNode.Type.ATTACHMENT]: withAttachmentPlaceholders,
[PlaceholderNode.Type.CONTACT]: withContactPlaceholders,
[PlaceholderNode.Type.COVERAGE]: withCoveragePlaceholders,
[PlaceholderNode.Type.IMAGE]: withImagePlaceholders,
[PlaceholderNode.Type.GALLERY]: withGalleryPlaceholders,
[PlaceholderNode.Type.EMBED]: withEmbedPlaceholders,
Expand Down
5 changes: 5 additions & 0 deletions packages/slate-editor/src/icons/Placeholder-Coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion packages/slate-editor/src/icons/Placeholder-Video.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion packages/slate-editor/src/icons/Placeholder-WebBookmark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions packages/slate-editor/src/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export { default as ItemsLayoutVertical } from './Items-Layout-Vertical.svg';
export { default as Link } from './Link.svg';
export { default as PlaceholderAttachment } from './Placeholder-Attachment.svg';
export { default as PlaceholderContact } from './Placeholder-Contact.svg';
export { default as PlaceholderCoverage } from './Placeholder-Coverage.svg';
export { default as PlaceholderEmbed } from './Placeholder-Embed.svg';
export { default as PlaceholderImage } from './Placeholder-Image.svg';
export { default as PlaceholderGallery } from './Placeholder-Gallery.svg';
Expand Down
10 changes: 10 additions & 0 deletions packages/slate-editor/src/modules/editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,16 @@ export const Editor = forwardRef<EditorRef, EditorProps>((props, forwardedRef) =
return openFloatingPressContactsMenu();
}
if (action === MenuAction.ADD_COVERAGE) {
if (withPlaceholders) {
const placeholder = insertPlaceholder(
editor,
{ type: PlaceholderNode.Type.COVERAGE },
true,
);
PlaceholdersManager.trigger(placeholder);
EditorCommands.selectNode(editor, placeholder);
return;
}
return openFloatingCoverageMenu();
}
if (action === MenuAction.ADD_QUOTE) {
Expand Down

0 comments on commit ee894a7

Please sign in to comment.