Skip to content

Commit 53201cb

Browse files
authored
Merge pull request #822 from streamich/peritext-inline-formatting
Peritext basic inline formatting
2 parents 8c4569c + 853ec06 commit 53201cb

File tree

19 files changed

+308
-61
lines changed

19 files changed

+308
-61
lines changed

src/json-crdt-extensions/peritext/block/Inline.ts

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {stringify} from '../../../json-text/stringify';
33
import {SliceBehavior, SliceTypeName} from '../slice/constants';
44
import {Range} from '../rga/Range';
55
import {ChunkSlice} from '../util/ChunkSlice';
6-
import {MarkerOverlayPoint} from '../overlay/MarkerOverlayPoint';
76
import {Cursor} from '../editor/Cursor';
87
import {hashId} from '../../../json-crdt/hash';
98
import {formatType} from '../slice/util';
@@ -15,34 +14,70 @@ import type {Peritext} from '../Peritext';
1514
import type {Slice} from '../slice/types';
1615
import type {PeritextMlAttributes, PeritextMlNode} from './types';
1716

18-
/** The attribute started before this inline and ends after this inline. */
19-
export class InlineAttrPassing {
17+
export abstract class AbstractInlineAttr {
2018
constructor(public slice: Slice) {}
19+
20+
/** @returns Whether the attribute starts at the start of the inline. */
21+
isStart(): boolean {
22+
return false;
23+
}
24+
25+
/** @returns Whether the attribute ends at the end of the inline. */
26+
isEnd(): boolean {
27+
return false;
28+
}
29+
30+
/** @returns Whether the attribute is collapsed to a point. */
31+
isCollapsed(): boolean {
32+
return false;
33+
}
2134
}
2235

36+
/** The attribute started before this inline and ends after this inline. */
37+
export class InlineAttrPassing extends AbstractInlineAttr {}
38+
2339
/** The attribute starts at the beginning of this inline. */
24-
export class InlineAttrStart {
25-
constructor(public slice: Slice) {}
40+
export class InlineAttrStart extends AbstractInlineAttr {
41+
isStart(): boolean {
42+
return true;
43+
}
2644
}
2745

2846
/** The attribute ends at the end of this inline. */
29-
export class InlineAttrEnd {
30-
constructor(public slice: Slice) {}
47+
export class InlineAttrEnd extends AbstractInlineAttr {
48+
isEnd(): boolean {
49+
return true;
50+
}
3151
}
3252

3353
/** The attribute starts and ends in this inline, exactly contains it. */
34-
export class InlineAttrContained {
35-
constructor(public slice: Slice) {}
54+
export class InlineAttrContained extends AbstractInlineAttr {
55+
isStart(): boolean {
56+
return true;
57+
}
58+
isEnd(): boolean {
59+
return true;
60+
}
3661
}
3762

3863
/** The attribute is collapsed at start of this inline. */
39-
export class InlineAttrStartPoint {
40-
constructor(public slice: Slice) {}
64+
export class InlineAttrStartPoint extends AbstractInlineAttr {
65+
isStart(): boolean {
66+
return true;
67+
}
68+
isCollapsed(): boolean {
69+
return true;
70+
}
4171
}
4272

4373
/** The attribute is collapsed at end of this inline. */
44-
export class InlineAttrEndPoint {
45-
constructor(public slice: Slice) {}
74+
export class InlineAttrEndPoint extends AbstractInlineAttr {
75+
isEnd(): boolean {
76+
return true;
77+
}
78+
isCollapsed(): boolean {
79+
return true;
80+
}
4681
}
4782

4883
export type InlineAttr =
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export {Block, IBlock} from './Block';
22
export {LeafBlock} from './LeafBlock';
3-
export {Inline} from './Inline';
3+
export * from './Inline';

src/json-crdt-extensions/peritext/slice/constants.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const enum SliceTypeCon {
4040
collapselist = 26, // Collapsible list - > List item
4141
collapse = 27, // Collapsible block
4242
note = 28, // Note block
43+
mathblock = 29, // <math> block
4344

4445
// ------------------------------------------------ inline slices (-64 to -1)
4546
Cursor = -1,
@@ -56,12 +57,12 @@ export const enum SliceTypeCon {
5657
ins = -12, // <ins>
5758
sup = -13, // <sup>
5859
sub = -14, // <sub>
59-
math = -15, // <math>
60+
math = -15, // <math> inline
6061
font = -16, // <span style="font-family: ...">
6162
col = -17, // <span style="color: ...">
6263
bg = -18, // <span style="background: ...">
6364
kbd = -19, // <kbd>
64-
hidden = -20, // <span style="color: transparent; background: black">
65+
spoiler = -20, // <span style="color: transparent; background: black">
6566
q = -21, // <q> (inline quote)
6667
cite = -22, // <cite> (inline citation)
6768
footnote = -23, // <sup> or <a> with href="#footnote-..." and title="Footnote ..."
@@ -106,6 +107,7 @@ export enum SliceTypeName {
106107
collapselist = SliceTypeCon.collapselist,
107108
collapse = SliceTypeCon.collapse,
108109
note = SliceTypeCon.note,
110+
mathblock = SliceTypeCon.mathblock,
109111

110112
Cursor = SliceTypeCon.Cursor,
111113
RemoteCursor = SliceTypeCon.RemoteCursor,
@@ -126,7 +128,7 @@ export enum SliceTypeName {
126128
col = SliceTypeCon.col,
127129
bg = SliceTypeCon.bg,
128130
kbd = SliceTypeCon.kbd,
129-
hidden = SliceTypeCon.hidden,
131+
spoiler = SliceTypeCon.spoiler,
130132
footnote = SliceTypeCon.footnote,
131133
ref = SliceTypeCon.ref,
132134
iaside = SliceTypeCon.iaside,

src/json-crdt-peritext-ui/plugins/cursor/RenderCaret.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ const innerClass = rule({
4444
an: moveAnimation + ' .25s ease-out forwards',
4545
});
4646

47+
const innerClass2 = rule({
48+
'mix-blend-mode': 'hard-light',
49+
});
50+
4751
export interface RenderCaretProps extends CaretViewProps {
4852
children: React.ReactNode;
4953
}
@@ -84,6 +88,9 @@ export const RenderCaret: React.FC<RenderCaretProps> = ({italic, children}) => {
8488
}}
8589
/>
8690
)}
91+
92+
{/* Two carets overlay, so that they look good, both, on white and black backgrounds. */}
93+
<span className={innerClass + innerClass2} style={style} />
8794
<span className={innerClass} style={style} />
8895
</span>
8996
);
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
export enum DefaultRendererColors {
22
ActiveCursor = '#07f',
33
InactiveCursor = 'rgba(127,127,127,.7)',
4-
ActiveSelection = '#d7e9fd',
4+
5+
/**
6+
* Derived from #d7e9fd. 80% opacity used so that
7+
* any inline formatting underneath the selection
8+
* is still visible.
9+
*/
10+
ActiveSelection = 'rgba(196,223,253,.8)',
11+
512
InactiveSelection = 'rgba(127,127,127,.2)',
613
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// biome-ignore lint: React is used for JSX
2+
import * as React from 'react';
3+
import {rule} from 'nano-theme';
4+
5+
const blockClass = rule({
6+
bg: '#222',
7+
col: 'transparent',
8+
bdrad: '2px',
9+
'&:hover': {
10+
bg: '#222',
11+
col: 'rgba(255, 255, 255, 0.2)',
12+
},
13+
});
14+
15+
export interface SpoilerProps {
16+
children: React.ReactNode;
17+
}
18+
19+
export const Spoiler: React.FC<SpoilerProps> = (props) => {
20+
const {children} = props;
21+
22+
return <span className={blockClass}>{children}</span>;
23+
};

src/json-crdt-peritext-ui/plugins/minimal/RenderInline.tsx renamed to src/json-crdt-peritext-ui/plugins/minimal/RenderInline/index.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
// biome-ignore lint: React is used for JSX
22
import * as React from 'react';
3-
import {usePeritext} from '../../react';
4-
import {useSyncStoreOpt} from '../../react/hooks';
5-
import {DefaultRendererColors} from './constants';
6-
import type {InlineViewProps} from '../../react/InlineView';
7-
import {CommonSliceType} from '../../../json-crdt-extensions';
3+
import {usePeritext} from '../../../react';
4+
import {useSyncStoreOpt} from '../../../react/hooks';
5+
import {DefaultRendererColors} from '../constants';
6+
import {CommonSliceType} from '../../../../json-crdt-extensions';
7+
import {Spoiler} from './Spoiler';
8+
import type {InlineViewProps} from '../../../react/InlineView';
89

910
interface RenderInlineSelectionProps extends RenderInlineProps {
1011
selection: [left: 'anchor' | 'focus' | '', right: 'anchor' | 'focus' | ''];
@@ -44,8 +45,7 @@ export const RenderInline: React.FC<RenderInlineProps> = (props) => {
4445
if (attr[CommonSliceType.sub]) element = <sub>{element}</sub>;
4546
if (attr[CommonSliceType.math]) element = <code>{element}</code>;
4647
if (attr[CommonSliceType.kbd]) element = <kbd>{element}</kbd>;
47-
if (attr[CommonSliceType.hidden])
48-
element = <span style={{color: 'transparent', background: 'black'}}>{element}</span>;
48+
if (attr[CommonSliceType.spoiler]) element = <Spoiler>{element}</Spoiler>;
4949

5050
if (selection) {
5151
element = (

src/json-crdt-peritext-ui/plugins/minimal/TopToolbar/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export const TopToolbar: React.FC<TopToolbarProps> = ({ctx}) => {
5151
{inlineGroupButton(CommonSliceType.b, 'Bold')}
5252
{inlineGroupButton(CommonSliceType.i, 'Italic')}
5353
{inlineGroupButton(CommonSliceType.u, 'Underline')}
54+
{inlineGroupButton(CommonSliceType.overline, 'Overline')}
5455
{inlineGroupButton(CommonSliceType.s, 'Strikethrough')}
5556
{inlineGroupButton(CommonSliceType.code, 'Code')}
5657
{inlineGroupButton(CommonSliceType.mark, 'Mark')}
@@ -60,7 +61,7 @@ export const TopToolbar: React.FC<TopToolbarProps> = ({ctx}) => {
6061
{inlineGroupButton(CommonSliceType.sub, 'Subscript')}
6162
{inlineGroupButton(CommonSliceType.math, 'Math')}
6263
{inlineGroupButton(CommonSliceType.kbd, 'Key')}
63-
{inlineGroupButton(CommonSliceType.hidden, 'Spoiler')}
64+
{inlineGroupButton(CommonSliceType.spoiler, 'Spoiler')}
6465
{inlineGroupButton(CommonSliceType.bookmark, 'Bookmark')}
6566
<ButtonSeparator />
6667
{button('Blue', () => {

src/json-crdt-peritext-ui/plugins/minimal/text.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const text: PeritextPlugin['text'] = (props, inline) => {
1313
if (attrs[CommonSliceType.b]) style.fontWeight = 'bold';
1414
if (attrs[CommonSliceType.i]) style.fontStyle = 'italic';
1515
if (attrs[CommonSliceType.u]) textDecoration = 'underline';
16+
if (attrs[CommonSliceType.overline]) textDecoration = textDecoration ? textDecoration + ' overline' : 'overline';
1617
if (attrs[CommonSliceType.s]) textDecoration = textDecoration ? textDecoration + ' line-through' : 'line-through';
1718
if ((attr = attrs[CommonSliceType.col])) style.color = attr[0].slice.data() + '';
1819

src/json-crdt-peritext-ui/plugins/toolbar/RenderBlock.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// biome-ignore lint: React is used for JSX
22
import * as React from 'react';
3-
import type {BlockViewProps} from '../../react/BlockView';
43
import {CommonSliceType} from '../../../json-crdt-extensions';
4+
import type {BlockViewProps} from '../../react/BlockView';
55

66
export interface RenderBlockProps extends BlockViewProps {
77
children: React.ReactNode;

0 commit comments

Comments
 (0)