Skip to content

Commit

Permalink
Merge pull request #574 from prezly/feature/dev-13852-refactor-galler…
Browse files Browse the repository at this point in the history
…ybookmarkplaceholderelement

[DEV-13852] Refactoring - Allow custom rendering of placeholders
  • Loading branch information
kudlajz authored Nov 8, 2024
2 parents 5f22370 + 0df51e0 commit ffea45c
Show file tree
Hide file tree
Showing 14 changed files with 313 additions and 1,093 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,80 +35,30 @@ export interface Parameters {
format?: FrameProps['format'];
removable?: RemovableFlagConfig;
withAttachmentPlaceholders?: boolean;
withContactPlaceholders?:
| false
| Pick<
ContactPlaceholderElement.Props,
| 'getSuggestions'
| 'invalidateSuggestions'
| 'renderAddon'
| 'renderEmpty'
| 'renderSuggestion'
| 'renderSuggestionsFooter'
>;
withContactPlaceholders?: false | Pick<ContactPlaceholderElement.Props, 'renderPlaceholder'>;
withCoveragePlaceholders?:
| false
| Pick<
CoveragePlaceholderElement.Props,
| 'getSuggestions'
| 'invalidateSuggestions'
| 'renderEmpty'
| 'renderSuggestion'
| 'renderSuggestionsFooter'
| 'onCreateCoverage'
>;
| Pick<CoveragePlaceholderElement.Props, 'onCreateCoverage' | 'renderPlaceholder'>;
withEmbedPlaceholders?: false | { fetchOembed: FetchOEmbedFn };
withGalleryPlaceholders?: boolean | { withMediaGalleryTab: WithMediaGalleryTab };
withGalleryBookmarkPlaceholders?:
| false
| Pick<
GalleryBookmarkPlaceholderElement.Props,
| 'fetchOembed'
| 'getSuggestions'
| 'invalidateSuggestions'
| 'renderAddon'
| 'renderEmpty'
| 'renderSuggestion'
| 'renderSuggestionsFooter'
>;
| Pick<GalleryBookmarkPlaceholderElement.Props, 'renderPlaceholder'>;
withImagePlaceholders?:
| boolean
| { withCaptions: boolean; withMediaGalleryTab: WithMediaGalleryTab };
withInlineContactPlaceholders?:
| false
| Pick<
InlineContactPlaceholderElement.Props,
| 'getSuggestions'
| 'invalidateSuggestions'
| 'renderEmpty'
| 'renderSuggestion'
| 'renderSuggestionsFooter'
>;
| Pick<InlineContactPlaceholderElement.Props, 'renderPlaceholder'>;
withMediaPlaceholders?:
| boolean
| { withCaptions: boolean; withMediaGalleryTab: WithMediaGalleryTab };
withStoryBookmarkPlaceholders?:
| false
| Pick<
StoryBookmarkPlaceholderElement.Props,
| 'getSuggestions'
| 'invalidateSuggestions'
| 'renderAddon'
| 'renderEmpty'
| 'renderSuggestion'
| 'renderSuggestionsFooter'
>;
| Pick<StoryBookmarkPlaceholderElement.Props, 'renderPlaceholder'>;
withStoryEmbedPlaceholders?:
| false
| Pick<
StoryEmbedPlaceholderElement.Props,
| 'getSuggestions'
| 'invalidateSuggestions'
| 'renderAddon'
| 'renderEmpty'
| 'renderSuggestion'
| 'renderSuggestionsFooter'
>;
| Pick<StoryEmbedPlaceholderElement.Props, 'renderPlaceholder'>;
withSocialPostPlaceholders?: false | { fetchOembed: FetchOEmbedFn };
withVideoPlaceholders?: false | { fetchOembed: FetchOEmbedFn };
withWebBookmarkPlaceholders?: false | { fetchOembed: FetchOEmbedFn };
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ReactNode } from 'react';
import React, { type MouseEvent, useState } from 'react';
import { Transforms } from 'slate';
import { ReactEditor, type RenderElementProps, useSlateStatic } from 'slate-react';
Expand All @@ -14,7 +15,9 @@ import { type Props as BaseProps, Placeholder } from './Placeholder';
export type Props = RenderElementProps &
Pick<BaseProps, 'icon' | 'title' | 'description' | 'format' | 'onClick' | 'onDrop'> & {
element: PlaceholderNode;
overflow?: 'visible' | 'hidden' | 'auto';
removable: RemovableFlagConfig;
renderFrame?: () => ReactNode;
};

export function PlaceholderElement({
Expand All @@ -27,7 +30,9 @@ export function PlaceholderElement({
icon,
title,
description,
overflow,
removable,
renderFrame,
// Callbacks
onClick,
onDrop,
Expand Down Expand Up @@ -63,29 +68,34 @@ export function PlaceholderElement({
<EditorBlock
{...attributes}
element={element}
overflow={overflow}
renderAboveFrame={children}
renderReadOnlyFrame={({ isSelected }) => (
<Placeholder
// Core
active={isActive}
format={format}
icon={icon}
title={title}
description={description}
// Variations
dragOver={onDrop ? dragOver : false}
dropZone={Boolean(onDrop)}
selected={isSelected}
progress={progress ?? isLoading}
// Callbacks
onClick={isLoading ? undefined : onClick}
onRemove={isRemovable ? handleRemove : undefined}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={onDrop}
onMouseOver={handleMouseOver}
/>
)}
renderReadOnlyFrame={({ isSelected }) =>
renderFrame ? (
renderFrame()
) : (
<Placeholder
// Core
active={isActive}
format={format}
icon={icon}
title={title}
description={description}
// Variations
dragOver={onDrop ? dragOver : false}
dropZone={Boolean(onDrop)}
selected={isSelected}
progress={progress ?? isLoading}
// Callbacks
onClick={isLoading ? undefined : onClick}
onRemove={isRemovable ? handleRemove : undefined}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={onDrop}
onMouseOver={handleMouseOver}
/>
)
}
rounded
void
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import type { ContactInfo } from '@prezly/slate-types';
import * as React from 'react';
import { createEditor as createSlateEditor } from 'slate';
import { type RenderElementProps, Slate } from 'slate-react';

import { SearchInput } from '#components';

import { PlaceholdersExtension } from '#extensions/placeholders';
import { createEditor } from '#modules/editor';

import type { Suggestion } from '../../../components/SearchInput/types';
import { PlaceholderNode } from '../PlaceholderNode';

import { ContactPlaceholderElement } from './ContactPlaceholderElement';
Expand All @@ -28,42 +24,6 @@ const attributes: RenderElementProps['attributes'] = {
ref: () => null,
};

function contact(props: Partial<ContactInfo> & Pick<ContactInfo, 'name'>): ContactInfo {
return {
avatar_url: null,
company: '',
description: '',
address: '',
email: '',
facebook: '',
mobile: '',
phone: '',
twitter: '',
website: '',
...props,
};
}

const suggestions: Suggestion<ContactInfo>[] = [
{ id: '00000000-00000000-00000000-00000001', value: contact({ name: 'Frodo Baggins' }) },
{ id: '00000000-00000000-00000000-00000002', value: contact({ name: 'Aragorn' }) },
{ id: '00000000-00000000-00000000-00000003', value: contact({ name: 'Samwise Gamgee' }) },
{ id: '00000000-00000000-00000000-00000004', value: contact({ name: 'Merry Brandybuck' }) },
{ id: '00000000-00000000-00000000-00000005', value: contact({ name: 'Legolas' }) },
{ id: '00000000-00000000-00000000-00000006', value: contact({ name: 'Pippin Took' }) },
{ id: '00000000-00000000-00000000-00000007', value: contact({ name: 'Gandalf' }) },
{ id: '00000000-00000000-00000000-00000008', value: contact({ name: 'Boromir' }) },
{ id: '00000000-00000000-00000000-00000009', value: contact({ name: 'Arwen' }) },
{ id: '00000000-00000000-00000000-00000010', value: contact({ name: 'Galadriel' }) },
];

async function getSuggestions(query: string) {
await delay(500 + Math.random() * 500);
return suggestions.filter(({ value }) =>
value.name.toLowerCase().includes(query.toLowerCase()),
);
}

export default {
title: 'Extensions/Placeholders/elements',
decorators: [
Expand All @@ -82,55 +42,10 @@ export function ContactPlaceholder() {
<ContactPlaceholderElement
attributes={attributes}
element={placeholder}
getSuggestions={getSuggestions}
renderSuggestion={({ ref, active, disabled, suggestion, onSelect }) => (
<SearchInput.Option
active={active}
disabled={disabled}
onClick={onSelect}
forwardRef={ref}
>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
<div
style={{
width: 40,
height: 40,
textAlign: 'center',
color: 'white',
lineHeight: '40px',
borderRadius: 6,
backgroundColor: '#2FA4F9',
marginRight: 16,
flexGrow: 0,
}}
>
{suggestion.value.name
.split(/\s/g)
.slice(0, 2)
.map((word) => word.substring(0, 1))
.join('')}
</div>
<div style={{ flexGrow: 1, fontWeight: 600, fontSize: 14 }}>
{suggestion.value.name}
</div>
</div>
</SearchInput.Option>
)}
renderSuggestionsFooter={() => (
<div>
<a href="#">+ Create a Site Contact</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="#">Edit Site Contacts</a>
</div>
)}
removable
renderPlaceholder={() => null}
>
{''}
</ContactPlaceholderElement>
);
}

function delay(ms: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
Loading

0 comments on commit ffea45c

Please sign in to comment.