Skip to content

Commit da2b395

Browse files
committed
feat(json-crdt-peritext-ui): 🎸 render fomatting preview text
1 parent c0c00af commit da2b395

File tree

3 files changed

+43
-8
lines changed

3 files changed

+43
-8
lines changed

src/json-crdt-peritext-ui/plugins/toolbar/cursor/caret/CaretBottomOverlay.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
import * as React from 'react';
22
import {ContextPane, ContextItem, ContextSep} from 'nice-ui/lib/4-card/ContextMenu';
33
import {useToolbarPlugin} from '../../context';
4+
import {SYMBOL} from 'nano-theme';
45
import type {Inline, Peritext, Slice} from '../../../../../json-crdt-extensions';
56
import type {CaretViewProps} from '../../../../web/react/cursor/CaretView';
6-
import type {ToolbarSlice, ToolBarSliceRegistryEntry} from '../../types';
7+
import type {Formatting, ToolBarSliceRegistryEntry} from '../../types';
78

8-
class ToolbarSliceImpl implements ToolbarSlice {
9+
class FormattingImpl implements Formatting {
910
public constructor(
1011
public readonly slice: Slice<string>,
1112
public readonly def: ToolBarSliceRegistryEntry,
1213
) {}
1314
}
1415

15-
const getConfigurableFormattingItems = (txt: Peritext, inline?: Inline): ToolbarSliceImpl[] => {
16+
const getConfigurableFormattingItems = (txt: Peritext, inline?: Inline): Formatting[] => {
1617
const slices = inline?.p1.layers;
17-
const res: ToolbarSlice[] = [];
18+
const res: Formatting[] = [];
1819
if (!slices) return res;
1920
const registry = txt.editor.getRegistry();
2021
for (const slice of slices) {
@@ -24,7 +25,7 @@ const getConfigurableFormattingItems = (txt: Peritext, inline?: Inline): Toolbar
2425
if (!def) continue;
2526
const isConfigurable = !!def.schema;
2627
if (!isConfigurable) continue;
27-
res.push(new ToolbarSliceImpl(slice, def));
28+
res.push(new FormattingImpl(slice, def));
2829
}
2930
return res;
3031
};
@@ -48,9 +49,20 @@ export const CaretBottomOverlay: React.FC<CaretBottomOverlayProps> = (props) =>
4849
const {def, slice} = formatting;
4950
const data = def.data();
5051
const menu = data.menu;
52+
const previewText = data.previewText?.(formatting) || '';
53+
const previewTextFormatted = previewText.length < 20 ? previewText : `${previewText.slice(0, 20)}${SYMBOL.ELLIPSIS}`;
5154
return (
52-
<ContextItem inset icon={menu?.icon?.()} right={data.renderIcon?.(formatting)} onClick={() => {}}>
55+
<ContextItem inset
56+
icon={menu?.icon?.()}
57+
right={data.renderIcon?.(formatting)}
58+
onClick={() => {}}
59+
>
5360
{menu?.name ?? def.name}
61+
{!!previewTextFormatted && (
62+
<span style={{opacity: 0.5}}>
63+
{previewTextFormatted}
64+
</span>
65+
)}
5466
</ContextItem>
5567
);
5668
})}

src/json-crdt-peritext-ui/plugins/toolbar/state/ToolbarState.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ export class ToolbarState implements UiLifeCycles {
108108
if (!data || typeof data !== 'object') return;
109109
return <Favicon url={data.href} />;
110110
};
111+
data.previewText = ({slice}) => {
112+
const data = slice.data() as {href: string};
113+
if (!data || typeof data !== 'object') return '';
114+
return (data.href || '').replace(/^(https?:\/\/)?(www\.)?/, '');
115+
};
111116
}
112117

113118
const changeUnsubscribe = et.subscribe('change', (ev) => {

src/json-crdt-peritext-ui/plugins/toolbar/types.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,25 @@ export type {MenuItem};
99

1010
export interface SliceRegistryEntryData extends Record<string, unknown> {
1111
menu?: MenuItem;
12-
renderIcon?: (formatting: ToolbarSlice) => React.ReactNode;
12+
13+
/**
14+
* A function that returns a React node to be used as an icon for the
15+
* formatting.
16+
*
17+
* @param formatting The formatting slice.
18+
* @returns A React node to be used as an icon for the formatting.
19+
*/
20+
renderIcon?: (formatting: Formatting) => React.ReactNode;
21+
22+
/**
23+
* Returns a short description of the formatting, for the user to easily
24+
* differentiate it from other formattings.
25+
*
26+
* @param formatting The formatting slice.
27+
* @returns A short description of the formatting. For example, if the
28+
* formatting is text color, this would be the color name.
29+
*/
30+
previewText?: (formatting: Formatting) => string;
1331
}
1432

1533
export type ToolBarSliceRegistryEntry<
@@ -18,7 +36,7 @@ export type ToolBarSliceRegistryEntry<
1836
Schema extends NodeBuilder = NodeBuilder,
1937
> = SliceRegistryEntry<Behavior, Tag, Schema, SliceRegistryEntryData>;
2038

21-
export interface ToolbarSlice {
39+
export interface Formatting {
2240
slice: Slice<string>;
2341
def: ToolBarSliceRegistryEntry;
2442
}

0 commit comments

Comments
 (0)