Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion demo/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
wysiwygToolbarConfigs,
} from '../src';
import type {ToolbarActionData} from '../src/bundle/Editor';
import {FoldingHeading} from '../src/extensions/yfm/FoldingHeading';
import {Math} from '../src/extensions/yfm/Math';
import {Mermaid} from '../src/extensions/yfm/Mermaid';
import {YfmHtmlBlock} from '../src/extensions/yfm/YfmHtmlBlock';
Expand Down Expand Up @@ -163,7 +164,8 @@ export const Playground = React.memo<PlaygroundProps>((props) => {
.use(YfmHtmlBlock, {
useConfig: useYfmHtmlBlockStyles,
sanitize,
}),
})
.use(FoldingHeading),
});

useEffect(() => {
Expand Down
40 changes: 40 additions & 0 deletions demo/YFM.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,41 @@ sequenceDiagram
Alice->>Bob: Hi Bob
Bob->>Alice: Hi Alice
\`\`\`
`.trim(),

foldingHeadings: `
#+ Heading 1

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum euismod, nulla sit amet sodales porttitor, ligula arcu consectetur justo, sit amet varius orci lorem a augue.

##+ Heading 2

Aenean lobortis rutrum eleifend. Aenean pulvinar orci eros, vitae porta justo interdum at. Proin metus nulla, porta tincidunt tempus eget, faucibus quis nisi.

###+ Heading 3

Praesent ut scelerisque tellus, condimentum iaculis massa. Integer a ante eu eros luctus vestibulum. Phasellus non laoreet lacus, non bibendum dui.

####+ Heading 4

Nunc pellentesque mollis tortor, ut dictum lectus consequat id. Aenean aliquet enim ac facilisis ornare. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.


#####+ Heading 5

Maecenas nec nisl eu dui lacinia consequat. Nulla non lacus varius risus lacinia vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus.

######+ Heading 6

Nulla facilisi. Pellentesque eu neque tincidunt odio viverra bibendum. Morbi consequat ac nibh id sagittis. Cras fermentum molestie urna vitae viverra.

## Heading 2

Mauris sed sem lorem. Maecenas vitae augue dui. In tempus vitae sem sed ultrices. Sed hendrerit mauris a ultrices rhoncus. Sed eget nibh nec turpis dignissim hendrerit non nec dolor.

# Heading 1

Duis id risus sit amet nunc ornare lobortis sed ut ipsum. Cras tempus ultricies nisl in auctor. Sed nec dui eget odio laoreet commodo at nec libero.
`.trim(),
};

Expand Down Expand Up @@ -333,6 +368,10 @@ export const Tasklist: StoryFn<PlaygroundStoryProps> = (props) => (
<PlaygroundComponent {...props} initial={markup.tasklist} />
);

export const FoldingHeadings: StoryFn<PlaygroundProps> = (props) => (
<PlaygroundComponent {...props} initial={markup.foldingHeadings} />
);

export const YfmNote: StoryFn<PlaygroundStoryProps> = (props) => (
<PlaygroundComponent {...props} initial={markup.yfmNotes} />
);
Expand Down Expand Up @@ -367,6 +406,7 @@ export const MermaidDiagram: StoryFn<PlaygroundStoryProps> = (props) => (

TextMarks.storyName = 'Text';
TextMarks.args = args;
FoldingHeadings.args = args;
YfmNote.storyName = 'YFM Note';
YfmNote.args = args;
YfmCut.storyName = 'YFM Cut';
Expand Down
3 changes: 3 additions & 0 deletions demo/md-plugins.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/* eslint-disable import/no-extraneous-dependencies */
import {transform as foldingHeadings} from '@diplodoc/folding-headings-extension';
import '@diplodoc/folding-headings-extension/runtime';
import {transform as yfmHtmlBlock} from '@diplodoc/html-extension';
import {transform as latex} from '@diplodoc/latex-extension';
import {transform as mermaid} from '@diplodoc/mermaid-extension';
Expand Down Expand Up @@ -54,6 +56,7 @@ const extendedPlugins = defaultPlugins.concat(
mermaid({bundle: false, runtime: MERMAID_RUNTIME}),
sub,
yfmHtmlBlock({bundle: false, runtimeJsPath: YFM_HTML_BLOCK_RUNTIME}),
foldingHeadings({bundle: false}),
);

export {extendedPlugins as plugins};
19 changes: 15 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
"@codemirror/state": "6.4.1",
"@codemirror/view": "6.26.3",
"@gravity-ui/i18n": "^1.1.0",
"@gravity-ui/icons": "^2.0.0",
"@gravity-ui/icons": "^2.10.0",
"@lezer/highlight": "1.2.0",
"@lezer/markdown": "1.3.0",
"@types/is-number": "^7.0.1",
Expand Down Expand Up @@ -199,6 +199,7 @@
"tslib": "^2.3.1"
},
"devDependencies": {
"@diplodoc/folding-headings-extension": "0.1.0",
"@diplodoc/html-extension": "1.2.7",
"@diplodoc/latex-extension": "1.0.3",
"@diplodoc/mermaid-extension": "1.2.1",
Expand Down Expand Up @@ -252,13 +253,16 @@
"typescript": "^4.5.2"
},
"peerDependenciesMeta": {
"@diplodoc/latex-extension": {
"@diplodoc/folding-headings-extension": {
"optional": true
},
"@diplodoc/mermaid-extension": {
"@diplodoc/html-extension": {
"optional": true
},
"@diplodoc/html-extension": {
"@diplodoc/latex-extension": {
"optional": true
},
"@diplodoc/mermaid-extension": {
"optional": true
},
"highlight.js": {
Expand All @@ -269,6 +273,7 @@
}
},
"peerDependencies": {
"@diplodoc/folding-headings-extension": "^0.1.0",
"@diplodoc/html-extension": "^1.2.7",
"@diplodoc/latex-extension": "^1.0.3",
"@diplodoc/mermaid-extension": "^1.0.0",
Expand Down
6 changes: 5 additions & 1 deletion src/bundle/config/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
CutIcon,
EmojiIcon,
FileIcon,
FoldingHeadingIcon,
FunctionBlockIcon,
FunctionInlineIcon,
HRuleIcon,
Expand Down Expand Up @@ -78,7 +79,8 @@ type Icon =
| 'emoji'
| 'tabs'
| 'mermaid'
| 'html';
| 'html'
| 'foldingHeading';

type Icons = Record<Icon, ToolbarIconData>;

Expand Down Expand Up @@ -135,4 +137,6 @@ export const icons: Icons = {

tabs: {data: TabsIcon},
mermaid: {data: MermaidIcon},

foldingHeading: {data: FoldingHeadingIcon},
};
14 changes: 13 additions & 1 deletion src/bundle/config/wysiwyg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,18 @@ export const wToolbarConfig: WToolbarData = [
[wImageItemData, wFileItemData, wTableItemData, wCheckboxItemData],
];

export const wToggleHeadingFoldingItemData: SelectionContextItemData = {
id: 'folding-heading',
type: ToolbarDataType.SingleButton,
icon: icons.foldingHeading,
title: () => i18n('folding-heading'),
hint: () => i18n('folding-heading_hint'),
isActive: (editor) => editor.actions.toggleHeadingFolding?.isActive() ?? false,
isEnable: (editor) => editor.actions.toggleHeadingFolding?.isEnable() ?? false,
exec: (editor) => editor.actions.toggleHeadingFolding.run(),
condition: 'enabled',
};

const textContextItemData: SelectionContextItemData = {
id: 'text',
type: ToolbarDataType.ReactComponent,
Expand All @@ -534,7 +546,7 @@ const textContextItemData: SelectionContextItemData = {
};

export const wSelectionMenuConfig: SelectionContextConfig = [
[textContextItemData],
[wToggleHeadingFoldingItemData, textContextItemData],
[...wBiusGroupConfig, wCodeItemData],
[
{
Expand Down
38 changes: 38 additions & 0 deletions src/extensions/yfm/FoldingHeading/FoldingHeading.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type {Action, ExtensionAuto} from '../../../core';

import {FoldingHeadingSpecs} from './FoldingHeadingSpec';
import {toggleHeadingFoldingAction} from './actions';
import {
openHeadingAndCreateParagraphAfterIfCursorAtEndOfHeading,
removeFoldingIfCursorAtStartOfHeading,
} from './commands';
import {foldingHeadingRule} from './input-rules';
import {foldingPlugin} from './plugins/Folding';
import {headingType} from './utils';

import '@diplodoc/folding-headings-extension/runtime/styles.css';

const action = 'toggleHeadingFolding';

export const FoldingHeading: ExtensionAuto = (builder) => {
builder.use(FoldingHeadingSpecs);

builder.addAction(action, () => toggleHeadingFoldingAction);
builder.addInputRules(({schema}) => ({rules: [foldingHeadingRule(headingType(schema), 6)]}));
builder.addKeymap(
() => ({
Enter: openHeadingAndCreateParagraphAfterIfCursorAtEndOfHeading,
Backspace: removeFoldingIfCursorAtStartOfHeading,
}),
builder.Priority.High,
);
builder.addPlugin(foldingPlugin);
};

declare global {
namespace WysiwygEditor {
interface Actions {
[action]: Action;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import {builders} from 'prosemirror-test-builder';

import {createMarkupChecker} from '../../../../../tests/sameMarkup';
import {ExtensionsManager} from '../../../../core';
import {BaseNode, BaseSchemaSpecs} from '../../../base/specs';
import {ItalicSpecs, headingNodeName, italicMarkName} from '../../../markdown/specs';
import {YfmHeadingAttr, YfmHeadingSpecs} from '../../../yfm/specs';

import {FoldingHeadingSpecs} from './FoldingHeadingSpecs';

const {schema, markupParser, serializer} = new ExtensionsManager({
extensions: (builder) =>
builder
.use(BaseSchemaSpecs, {})
.use(ItalicSpecs)
.use(YfmHeadingSpecs, {})
.use(FoldingHeadingSpecs),
}).buildDeps();

const {doc, h1, h2, h3, h4, h5, h6, i} = builders<
'doc' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6',
'i'
>(schema, {
doc: {nodeType: BaseNode.Doc},
h1: {nodeType: headingNodeName, [YfmHeadingAttr.Level]: 1},
h2: {nodeType: headingNodeName, [YfmHeadingAttr.Level]: 2},
h3: {nodeType: headingNodeName, [YfmHeadingAttr.Level]: 3},
h4: {nodeType: headingNodeName, [YfmHeadingAttr.Level]: 4},
h5: {nodeType: headingNodeName, [YfmHeadingAttr.Level]: 5},
h6: {nodeType: headingNodeName, [YfmHeadingAttr.Level]: 6},
i: {markType: italicMarkName},
});

const {same} = createMarkupChecker({parser: markupParser, serializer});

describe('Folding Headings', () => {
it('should parse folding headings', () => {
const markup = `
#+ heading 1

##+ heading 2

###+ heading 3

####+ heading 4

#####+ heading 5

######+ heading 6
`.trim();

return same(
markup,
doc(
h1({[YfmHeadingAttr.Folding]: true}, 'heading 1'),
h2({[YfmHeadingAttr.Folding]: true}, 'heading 2'),
h3({[YfmHeadingAttr.Folding]: true}, 'heading 3'),
h4({[YfmHeadingAttr.Folding]: true}, 'heading 4'),
h5({[YfmHeadingAttr.Folding]: true}, 'heading 5'),
h6({[YfmHeadingAttr.Folding]: true}, 'heading 6'),
),
);
});

it('should parse common headings', () => {
const markup = `
# heading 1

## heading 2

### heading 3

#### heading 4

##### heading 5

###### heading 6
`.trim();

return same(
markup,
doc(
h1('heading 1'),
h2('heading 2'),
h3('heading 3'),
h4('heading 4'),
h5('heading 5'),
h6('heading 6'),
),
);
});

it('should parse folding heading with inline markup', () => {
const markup = `
##+ *heading* 2
`.trim();

return same(markup, doc(h2({[YfmHeadingAttr.Folding]: true}, i('heading'), ' 2')));
});
});
Loading