@atlaskit/editor-plugin-block-type 4.0.14 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/cjs/blockTypePlugin.js +3 -0
- package/dist/cjs/pm-plugins/block-types.js +3 -1
- package/dist/cjs/pm-plugins/commands/block-type.js +58 -10
- package/dist/cjs/pm-plugins/commands/clear-formatting.js +58 -0
- package/dist/cjs/pm-plugins/main.js +6 -3
- package/dist/cjs/pm-plugins/ui/FloatingToolbarComponent.js +5 -0
- package/dist/cjs/pm-plugins/ui/PrimaryToolbarComponent.js +6 -1
- package/dist/cjs/pm-plugins/ui/ToolbarBlockType/index.js +82 -45
- package/dist/cjs/pm-plugins/ui/ToolbarBlockType/styled.js +15 -1
- package/dist/cjs/pm-plugins/utils.js +41 -2
- package/dist/es2019/blockTypePlugin.js +4 -1
- package/dist/es2019/pm-plugins/block-types.js +2 -0
- package/dist/es2019/pm-plugins/commands/block-type.js +49 -0
- package/dist/es2019/pm-plugins/commands/clear-formatting.js +50 -0
- package/dist/es2019/pm-plugins/main.js +7 -4
- package/dist/es2019/pm-plugins/ui/FloatingToolbarComponent.js +5 -0
- package/dist/es2019/pm-plugins/ui/PrimaryToolbarComponent.js +6 -1
- package/dist/es2019/pm-plugins/ui/ToolbarBlockType/index.js +83 -48
- package/dist/es2019/pm-plugins/ui/ToolbarBlockType/styled.js +14 -0
- package/dist/es2019/pm-plugins/utils.js +43 -2
- package/dist/esm/blockTypePlugin.js +4 -1
- package/dist/esm/pm-plugins/block-types.js +2 -0
- package/dist/esm/pm-plugins/commands/block-type.js +55 -9
- package/dist/esm/pm-plugins/commands/clear-formatting.js +50 -0
- package/dist/esm/pm-plugins/main.js +7 -4
- package/dist/esm/pm-plugins/ui/FloatingToolbarComponent.js +5 -0
- package/dist/esm/pm-plugins/ui/PrimaryToolbarComponent.js +6 -1
- package/dist/esm/pm-plugins/ui/ToolbarBlockType/index.js +84 -47
- package/dist/esm/pm-plugins/ui/ToolbarBlockType/styled.js +14 -0
- package/dist/esm/pm-plugins/utils.js +41 -2
- package/dist/types/blockTypePluginType.d.ts +1 -0
- package/dist/types/pm-plugins/block-types.d.ts +2 -0
- package/dist/types/pm-plugins/commands/block-type.d.ts +1 -0
- package/dist/types/pm-plugins/commands/clear-formatting.d.ts +8 -0
- package/dist/types/pm-plugins/main.d.ts +2 -7
- package/dist/types/pm-plugins/ui/ToolbarBlockType/index.d.ts +1 -0
- package/dist/types/pm-plugins/ui/ToolbarBlockType/styled.d.ts +1 -0
- package/dist/types/pm-plugins/utils.d.ts +3 -0
- package/dist/types-ts4.5/blockTypePluginType.d.ts +1 -0
- package/dist/types-ts4.5/pm-plugins/block-types.d.ts +2 -0
- package/dist/types-ts4.5/pm-plugins/commands/block-type.d.ts +1 -0
- package/dist/types-ts4.5/pm-plugins/commands/clear-formatting.d.ts +8 -0
- package/dist/types-ts4.5/pm-plugins/main.d.ts +2 -7
- package/dist/types-ts4.5/pm-plugins/ui/ToolbarBlockType/index.d.ts +1 -0
- package/dist/types-ts4.5/pm-plugins/ui/ToolbarBlockType/styled.d.ts +1 -0
- package/dist/types-ts4.5/pm-plugins/utils.d.ts +3 -0
- package/package.json +3 -3
@@ -4,6 +4,7 @@ import { filterChildrenBetween, wrapSelectionIn } from '@atlaskit/editor-common/
|
|
4
4
|
import { Slice, Fragment } from '@atlaskit/editor-prosemirror/model';
|
5
5
|
import { CellSelection } from '@atlaskit/editor-tables';
|
6
6
|
import { HEADINGS_BY_NAME, NORMAL_TEXT } from '../block-types';
|
7
|
+
import { FORMATTING_NODE_TYPES, FORMATTING_MARK_TYPES, cellSelectionNodesBetween, formatTypes, clearNodeFormattingOnSelection } from './clear-formatting';
|
7
8
|
import { wrapSelectionInBlockType } from './wrapSelectionIn';
|
8
9
|
export function setBlockType(name) {
|
9
10
|
return ({
|
@@ -117,6 +118,54 @@ export function setNormalText(fromBlockQuote) {
|
|
117
118
|
return tr;
|
118
119
|
};
|
119
120
|
}
|
121
|
+
export function clearFormatting() {
|
122
|
+
return function ({
|
123
|
+
tr
|
124
|
+
}) {
|
125
|
+
const formattingCleared = [];
|
126
|
+
const schema = tr.doc.type.schema;
|
127
|
+
FORMATTING_MARK_TYPES.forEach(mark => {
|
128
|
+
const {
|
129
|
+
from,
|
130
|
+
to
|
131
|
+
} = tr.selection;
|
132
|
+
const markType = schema.marks[mark];
|
133
|
+
if (!markType) {
|
134
|
+
return;
|
135
|
+
}
|
136
|
+
if (tr.selection instanceof CellSelection) {
|
137
|
+
cellSelectionNodesBetween(tr.selection, tr.doc, (node, pos) => {
|
138
|
+
const isTableCell = node.type === schema.nodes.tableCell || node.type === schema.nodes.tableHeader;
|
139
|
+
if (!isTableCell) {
|
140
|
+
return true;
|
141
|
+
}
|
142
|
+
if (tr.doc.rangeHasMark(pos, pos + node.nodeSize, markType)) {
|
143
|
+
formattingCleared.push(formatTypes[mark]);
|
144
|
+
tr.removeMark(pos, pos + node.nodeSize, markType);
|
145
|
+
}
|
146
|
+
return false;
|
147
|
+
});
|
148
|
+
} else if (tr.doc.rangeHasMark(from, to, markType)) {
|
149
|
+
formattingCleared.push(formatTypes[mark]);
|
150
|
+
tr.removeMark(from, to, markType);
|
151
|
+
}
|
152
|
+
});
|
153
|
+
FORMATTING_NODE_TYPES.forEach(nodeName => {
|
154
|
+
const formattedNodeType = schema.nodes[nodeName];
|
155
|
+
const {
|
156
|
+
$from,
|
157
|
+
$to
|
158
|
+
} = tr.selection;
|
159
|
+
if (tr.selection instanceof CellSelection) {
|
160
|
+
cellSelectionNodesBetween(tr.selection, tr.doc, clearNodeFormattingOnSelection(schema, tr, formattedNodeType, nodeName, formattingCleared));
|
161
|
+
} else {
|
162
|
+
tr.doc.nodesBetween($from.pos, $to.pos, clearNodeFormattingOnSelection(schema, tr, formattedNodeType, nodeName, formattingCleared));
|
163
|
+
}
|
164
|
+
});
|
165
|
+
tr.setStoredMarks([]);
|
166
|
+
return tr;
|
167
|
+
};
|
168
|
+
}
|
120
169
|
function withCurrentHeadingLevel(fn) {
|
121
170
|
return ({
|
122
171
|
tr
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import { ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
|
2
|
+
import { liftTarget } from '@atlaskit/editor-prosemirror/transform';
|
3
|
+
// Functions duplicated from platform/packages/editor/editor-plugin-text-formatting/src/editor-commands/clear-formatting.ts
|
4
|
+
// TODO: Refactor to avoid duplication if platform_editor_blockquote_in_text_formatting_menu experiment is productionalised
|
5
|
+
export const FORMATTING_NODE_TYPES = ['heading', 'blockquote'];
|
6
|
+
export const FORMATTING_MARK_TYPES = ['em', 'code', 'strike', 'strong', 'underline', 'textColor', 'subsup', 'backgroundColor'];
|
7
|
+
export const formatTypes = {
|
8
|
+
em: ACTION_SUBJECT_ID.FORMAT_ITALIC,
|
9
|
+
code: ACTION_SUBJECT_ID.FORMAT_CODE,
|
10
|
+
strike: ACTION_SUBJECT_ID.FORMAT_STRIKE,
|
11
|
+
strong: ACTION_SUBJECT_ID.FORMAT_STRONG,
|
12
|
+
underline: ACTION_SUBJECT_ID.FORMAT_UNDERLINE,
|
13
|
+
textColor: ACTION_SUBJECT_ID.FORMAT_COLOR,
|
14
|
+
subsup: 'subsup',
|
15
|
+
backgroundColor: ACTION_SUBJECT_ID.FORMAT_BACKGROUND_COLOR
|
16
|
+
};
|
17
|
+
export const cellSelectionNodesBetween = (selection, doc, f, startPos
|
18
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
19
|
+
) => {
|
20
|
+
selection.forEachCell((cell, cellPos) => {
|
21
|
+
doc.nodesBetween(cellPos, cellPos + cell.nodeSize, f, startPos);
|
22
|
+
});
|
23
|
+
};
|
24
|
+
|
25
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
26
|
+
export function clearNodeFormattingOnSelection(schema, tr, formattedNodeType, nodeName, formattingCleared) {
|
27
|
+
return function (node, pos) {
|
28
|
+
if (node.type === formattedNodeType) {
|
29
|
+
if (formattedNodeType.isTextblock) {
|
30
|
+
tr.setNodeMarkup(pos, schema.nodes.paragraph);
|
31
|
+
formattingCleared.push(nodeName);
|
32
|
+
return false;
|
33
|
+
} else {
|
34
|
+
// In case of panel or blockquote
|
35
|
+
const fromPos = tr.doc.resolve(pos + 1);
|
36
|
+
const toPos = tr.doc.resolve(pos + node.nodeSize - 1);
|
37
|
+
const nodeRange = fromPos.blockRange(toPos);
|
38
|
+
if (nodeRange) {
|
39
|
+
const targetLiftDepth = liftTarget(nodeRange);
|
40
|
+
if (targetLiftDepth || targetLiftDepth === 0) {
|
41
|
+
formattingCleared.push(nodeName);
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
43
|
+
tr.lift(nodeRange, targetLiftDepth);
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
return true;
|
49
|
+
};
|
50
|
+
}
|
@@ -6,7 +6,7 @@ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
6
6
|
import { BLOCK_QUOTE, CODE_BLOCK, HEADING_1, HEADING_2, HEADING_3, HEADING_4, HEADING_5, HEADING_6, HEADINGS_BY_LEVEL, NORMAL_TEXT, OTHER, PANEL, TEXT_BLOCK_TYPES, WRAPPER_BLOCK_TYPES, getBlockTypesInDropdown } from './block-types';
|
7
7
|
import { setHeadingWithAnalytics, setNormalTextWithAnalytics } from './commands/block-type';
|
8
8
|
import { HEADING_KEYS } from './consts';
|
9
|
-
import { areBlockTypesDisabled } from './utils';
|
9
|
+
import { areBlockTypesDisabled, checkFormattingIsPresent, hasBlockQuoteInOptions } from './utils';
|
10
10
|
const blockTypeForNode = (node, schema) => {
|
11
11
|
if (node.type === schema.nodes.heading) {
|
12
12
|
const maybeNode = HEADINGS_BY_LEVEL[node.attrs['level']];
|
@@ -96,12 +96,14 @@ export const createPlugin = (editorAPI, dispatch, lastNodeMustBeParagraph, inclu
|
|
96
96
|
const availableWrapperBlockTypes = WRAPPER_BLOCK_TYPES.filter(blockType => isBlockTypeSchemaSupported(blockType, state));
|
97
97
|
const BLOCK_TYPES_IN_DROPDOWN = getBlockTypesInDropdown(includeBlockQuoteAsTextstyleOption);
|
98
98
|
const availableBlockTypesInDropdown = BLOCK_TYPES_IN_DROPDOWN.filter(blockType => isBlockTypeSchemaSupported(blockType, state));
|
99
|
+
const formattingIsPresent = hasBlockQuoteInOptions(availableBlockTypesInDropdown) ? checkFormattingIsPresent(state) : undefined;
|
99
100
|
return {
|
100
101
|
currentBlockType: detectBlockType(availableBlockTypesInDropdown, state),
|
101
102
|
blockTypesDisabled: areBlockTypesDisabled(state),
|
102
103
|
availableBlockTypes,
|
103
104
|
availableWrapperBlockTypes,
|
104
|
-
availableBlockTypesInDropdown
|
105
|
+
availableBlockTypesInDropdown,
|
106
|
+
formattingIsPresent
|
105
107
|
};
|
106
108
|
},
|
107
109
|
// Ignored via go/ees005
|
@@ -110,9 +112,10 @@ export const createPlugin = (editorAPI, dispatch, lastNodeMustBeParagraph, inclu
|
|
110
112
|
const newPluginState = {
|
111
113
|
...oldPluginState,
|
112
114
|
currentBlockType: detectBlockType(oldPluginState.availableBlockTypesInDropdown, newState),
|
113
|
-
blockTypesDisabled: areBlockTypesDisabled(newState)
|
115
|
+
blockTypesDisabled: areBlockTypesDisabled(newState),
|
116
|
+
formattingIsPresent: hasBlockQuoteInOptions(oldPluginState.availableBlockTypesInDropdown) ? checkFormattingIsPresent(newState) : undefined
|
114
117
|
};
|
115
|
-
if (newPluginState.currentBlockType !== oldPluginState.currentBlockType || newPluginState.blockTypesDisabled !== oldPluginState.blockTypesDisabled) {
|
118
|
+
if (newPluginState.currentBlockType !== oldPluginState.currentBlockType || newPluginState.blockTypesDisabled !== oldPluginState.blockTypesDisabled || newPluginState.formattingIsPresent !== oldPluginState.formattingIsPresent) {
|
116
119
|
dispatch(pluginKey, newPluginState);
|
117
120
|
}
|
118
121
|
return newPluginState;
|
@@ -22,6 +22,10 @@ export function FloatingToolbarComponent({
|
|
22
22
|
var _api$core2, _api$blockType2, _api$blockType2$comma;
|
23
23
|
return api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockType2 = api.blockType) === null || _api$blockType2 === void 0 ? void 0 : (_api$blockType2$comma = _api$blockType2.commands) === null || _api$blockType2$comma === void 0 ? void 0 : _api$blockType2$comma.insertBlockQuote(INPUT_METHOD.TOOLBAR));
|
24
24
|
}, [api]);
|
25
|
+
const clearFormatting = useCallback(() => {
|
26
|
+
var _api$core3, _api$blockType3, _api$blockType3$comma;
|
27
|
+
return api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockType3 = api.blockType) === null || _api$blockType3 === void 0 ? void 0 : (_api$blockType3$comma = _api$blockType3.commands) === null || _api$blockType3$comma === void 0 ? void 0 : _api$blockType3$comma.clearFormatting());
|
28
|
+
}, [api]);
|
25
29
|
return /*#__PURE__*/React.createElement(ToolbarBlockType, {
|
26
30
|
isSmall: FloatingToolbarSettings.isSmall,
|
27
31
|
isDisabled: FloatingToolbarSettings.disabled,
|
@@ -32,6 +36,7 @@ export function FloatingToolbarComponent({
|
|
32
36
|
,
|
33
37
|
pluginState: blockTypeState,
|
34
38
|
wrapBlockQuote: wrapBlockQuote,
|
39
|
+
clearFormatting: clearFormatting,
|
35
40
|
shouldUseDefaultRole: FloatingToolbarSettings.shouldUseDefaultRole,
|
36
41
|
api: api
|
37
42
|
});
|
@@ -23,12 +23,17 @@ export function PrimaryToolbarComponent({
|
|
23
23
|
var _api$core2, _api$blockType2, _api$blockType2$comma;
|
24
24
|
return api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockType2 = api.blockType) === null || _api$blockType2 === void 0 ? void 0 : (_api$blockType2$comma = _api$blockType2.commands) === null || _api$blockType2$comma === void 0 ? void 0 : _api$blockType2$comma.insertBlockQuote(INPUT_METHOD.TOOLBAR));
|
25
25
|
};
|
26
|
+
const clearFormatting = () => {
|
27
|
+
var _api$core3, _api$blockType3, _api$blockType3$comma;
|
28
|
+
return api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockType3 = api.blockType) === null || _api$blockType3 === void 0 ? void 0 : (_api$blockType3$comma = _api$blockType3.commands) === null || _api$blockType3$comma === void 0 ? void 0 : _api$blockType3$comma.clearFormatting());
|
29
|
+
};
|
26
30
|
return /*#__PURE__*/React.createElement(ToolbarBlockType, {
|
27
31
|
isSmall: isSmall,
|
28
32
|
isDisabled: disabled,
|
29
33
|
isReducedSpacing: isToolbarReducedSpacing,
|
30
34
|
setTextLevel: boundSetBlockType,
|
31
|
-
wrapBlockQuote: wrapBlockQuote
|
35
|
+
wrapBlockQuote: wrapBlockQuote,
|
36
|
+
clearFormatting: clearFormatting
|
32
37
|
// Ignored via go/ees005
|
33
38
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
34
39
|
,
|
@@ -8,13 +8,16 @@ import React from 'react';
|
|
8
8
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
|
9
9
|
import { jsx } from '@emotion/react';
|
10
10
|
import { injectIntl } from 'react-intl-next';
|
11
|
-
import { findKeymapByDescription,
|
11
|
+
import { findKeymapByDescription, tooltip, clearFormatting } from '@atlaskit/editor-common/keymaps';
|
12
|
+
import { toolbarMessages } from '@atlaskit/editor-common/messages';
|
12
13
|
import { separatorStyles, wrapperStyle } from '@atlaskit/editor-common/styles';
|
13
14
|
import { DropdownMenuWithKeyboardNavigation as DropdownMenu } from '@atlaskit/editor-common/ui-menu';
|
14
15
|
import { akEditorMenuZIndex } from '@atlaskit/editor-shared-styles';
|
16
|
+
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
15
17
|
import { ThemeMutationObserver } from '@atlaskit/tokens';
|
18
|
+
import { NORMAL_TEXT } from '../../block-types';
|
16
19
|
import { BlockTypeButton } from './blocktype-button';
|
17
|
-
import { blockTypeMenuItemStyle, keyboardShortcut, keyboardShortcutSelect } from './styled';
|
20
|
+
import { blockTypeMenuItemStyle, keyboardShortcut, keyboardShortcutSelect, floatingToolbarWrapperStyle } from './styled';
|
18
21
|
// eslint-disable-next-line @repo/internal/react/no-class-components
|
19
22
|
class ToolbarBlockType extends React.PureComponent {
|
20
23
|
constructor(...args) {
|
@@ -56,23 +59,22 @@ class ToolbarBlockType extends React.PureComponent {
|
|
56
59
|
} = this.props;
|
57
60
|
const {
|
58
61
|
currentBlockType,
|
59
|
-
availableBlockTypesInDropdown
|
62
|
+
availableBlockTypesInDropdown,
|
63
|
+
formattingIsPresent
|
60
64
|
} = this.props.pluginState;
|
61
65
|
const items = availableBlockTypesInDropdown.map((blockType, index) => {
|
62
66
|
const isActive = currentBlockType === blockType;
|
63
67
|
const tagName = blockType.tagName || 'p';
|
64
68
|
const Tag = tagName;
|
65
69
|
const keyMap = findKeymapByDescription(blockType.title.defaultMessage);
|
66
|
-
|
70
|
+
const item = {
|
67
71
|
content:
|
68
72
|
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
|
69
73
|
jsx("div", {
|
70
74
|
css: blockTypeMenuItemStyle(tagName, isActive, this.state.typographyTheme)
|
71
75
|
}, jsx(Tag, null, formatMessage(blockType.title))),
|
72
76
|
value: blockType,
|
73
|
-
label: formatMessage(blockType.title),
|
74
77
|
'aria-label': tooltip(keyMap, formatMessage(blockType.title)),
|
75
|
-
keyShortcuts: getAriaKeyshortcuts(keyMap),
|
76
78
|
key: `${blockType.name}-${index}`,
|
77
79
|
elemAfter:
|
78
80
|
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
|
@@ -81,7 +83,30 @@ class ToolbarBlockType extends React.PureComponent {
|
|
81
83
|
}, tooltip(keyMap)),
|
82
84
|
isActive
|
83
85
|
};
|
86
|
+
return item;
|
84
87
|
});
|
88
|
+
if (availableBlockTypesInDropdown.map(blockType => blockType.name).includes('blockquote')) {
|
89
|
+
const clearFormattingItem = {
|
90
|
+
content: jsx("div", null, jsx("p", null, toolbarMessages.clearFormatting.defaultMessage)),
|
91
|
+
value: {
|
92
|
+
name: 'clearFormatting'
|
93
|
+
},
|
94
|
+
'aria-label': tooltip(clearFormatting, toolbarMessages.clearFormatting.defaultMessage),
|
95
|
+
key: 'clear-formatting',
|
96
|
+
elemAfter:
|
97
|
+
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
|
98
|
+
jsx("div", {
|
99
|
+
css: [keyboardShortcut]
|
100
|
+
}, tooltip(clearFormatting)),
|
101
|
+
isActive: false,
|
102
|
+
isDisabled: currentBlockType === NORMAL_TEXT && !formattingIsPresent
|
103
|
+
};
|
104
|
+
return [{
|
105
|
+
items
|
106
|
+
}, {
|
107
|
+
items: [clearFormattingItem]
|
108
|
+
}];
|
109
|
+
}
|
85
110
|
return [{
|
86
111
|
items
|
87
112
|
}];
|
@@ -94,8 +119,12 @@ class ToolbarBlockType extends React.PureComponent {
|
|
94
119
|
if (blockType.name === 'blockquote') {
|
95
120
|
this.props.wrapBlockQuote(blockType.name);
|
96
121
|
} else {
|
97
|
-
|
98
|
-
|
122
|
+
if (blockType.name === 'clearFormatting') {
|
123
|
+
this.props.clearFormatting();
|
124
|
+
} else {
|
125
|
+
const fromBlockQuote = this.props.pluginState.currentBlockType.name === 'blockquote';
|
126
|
+
this.props.setTextLevel(blockType.name, fromBlockQuote);
|
127
|
+
}
|
99
128
|
}
|
100
129
|
if (shouldCloseMenu) {
|
101
130
|
this.setState({
|
@@ -154,48 +183,54 @@ class ToolbarBlockType extends React.PureComponent {
|
|
154
183
|
const blockTypeTitles = availableBlockTypesInDropdown.filter(blockType => blockType.name === currentBlockType.name).map(blockType => blockType.title);
|
155
184
|
if (!this.props.isDisabled && !blockTypesDisabled) {
|
156
185
|
const items = this.createItems();
|
157
|
-
return (
|
186
|
+
return jsx("span", {
|
187
|
+
css: editorExperiment('platform_editor_blockquote_in_text_formatting_menu', true) ?
|
188
|
+
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
|
189
|
+
[wrapperStyle, floatingToolbarWrapperStyle] :
|
158
190
|
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
191
|
+
wrapperStyle
|
192
|
+
}, jsx(DropdownMenu, {
|
193
|
+
items: items,
|
194
|
+
onOpenChange: this.onOpenChange,
|
195
|
+
onItemActivated: this.handleSelectBlockType,
|
196
|
+
isOpen: active,
|
197
|
+
mountTo: popupsMountPoint,
|
198
|
+
boundariesElement: popupsBoundariesElement,
|
199
|
+
scrollableElement: popupsScrollableElement,
|
200
|
+
zIndex: akEditorMenuZIndex,
|
201
|
+
fitHeight: 360,
|
202
|
+
fitWidth: 106,
|
203
|
+
section: {
|
204
|
+
hasSeparator: true
|
205
|
+
},
|
206
|
+
shouldUseDefaultRole: shouldUseDefaultRole
|
207
|
+
// hasSeparator={true}
|
208
|
+
,
|
209
|
+
shouldFocusFirstItem: () => {
|
210
|
+
if (isOpenedByKeyboard) {
|
211
|
+
// eslint-disable-next-line @repo/internal/react/no-set-state-inside-render
|
212
|
+
this.setState({
|
213
|
+
...this.state,
|
214
|
+
isOpenedByKeyboard: false
|
215
|
+
});
|
182
216
|
}
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
217
|
+
return isOpenedByKeyboard;
|
218
|
+
}
|
219
|
+
}, jsx(BlockTypeButton, {
|
220
|
+
isSmall: isSmall,
|
221
|
+
isReducedSpacing: isReducedSpacing,
|
222
|
+
selected: active,
|
223
|
+
disabled: false,
|
224
|
+
title: blockTypeTitles[0],
|
225
|
+
onClick: this.handleTriggerClick,
|
226
|
+
onKeyDown: this.handleTriggerByKeyboard,
|
227
|
+
formatMessage: formatMessage,
|
228
|
+
"aria-expanded": active,
|
229
|
+
blockTypeName: currentBlockType.name
|
230
|
+
})), !(api !== null && api !== void 0 && api.primaryToolbar) && jsx("span", {
|
231
|
+
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
|
232
|
+
css: separatorStyles
|
233
|
+
}));
|
199
234
|
}
|
200
235
|
return (
|
201
236
|
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
|
@@ -18,6 +18,12 @@ export const blockTypeMenuItemStyle = (tagName, selected, typographyTheme) => {
|
|
18
18
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766
|
19
19
|
'h1, h2, h3, h4, h5, h6': {
|
20
20
|
marginTop: 0
|
21
|
+
},
|
22
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766
|
23
|
+
blockquote: {
|
24
|
+
paddingTop: 0,
|
25
|
+
paddingBottom: 0,
|
26
|
+
marginTop: 0
|
21
27
|
}
|
22
28
|
}
|
23
29
|
},
|
@@ -45,4 +51,12 @@ export const wrapperSmallStyle = css({
|
|
45
51
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-exported-styles -- Ignored via go/DSP-18766
|
46
52
|
export const expandIconWrapperStyle = css({
|
47
53
|
marginLeft: "var(--ds-space-negative-100, -8px)"
|
54
|
+
});
|
55
|
+
|
56
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-exported-styles
|
57
|
+
export const floatingToolbarWrapperStyle = css({
|
58
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors
|
59
|
+
"[data-role='droplistContent']": {
|
60
|
+
maxHeight: '90vh'
|
61
|
+
}
|
48
62
|
});
|
@@ -1,7 +1,8 @@
|
|
1
|
+
import { anyMarkActive } from '@atlaskit/editor-common/mark';
|
1
2
|
import { createRule, createWrappingJoinRule } from '@atlaskit/editor-common/utils';
|
2
3
|
import { fg } from '@atlaskit/platform-feature-flags';
|
3
4
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
4
|
-
import { WRAPPER_BLOCK_TYPES } from './block-types';
|
5
|
+
import { WRAPPER_BLOCK_TYPES, FORMATTING_NODE_TYPES, FORMATTING_MARK_TYPES } from './block-types';
|
5
6
|
export const isNodeAWrappingBlockNode = node => {
|
6
7
|
if (!node) {
|
7
8
|
return false;
|
@@ -107,4 +108,44 @@ export function areBlockTypesDisabled(state) {
|
|
107
108
|
return nodesTypes.filter(type => type !== panel).length > 0 && (!hasQuote || hasNestedListInQuote);
|
108
109
|
}
|
109
110
|
return nodesTypes.filter(type => type !== panel).length > 0;
|
110
|
-
}
|
111
|
+
}
|
112
|
+
const blockStylingIsPresent = state => {
|
113
|
+
const {
|
114
|
+
from,
|
115
|
+
to
|
116
|
+
} = state.selection;
|
117
|
+
let isBlockStyling = false;
|
118
|
+
state.doc.nodesBetween(from, to, node => {
|
119
|
+
if (FORMATTING_NODE_TYPES.indexOf(node.type.name) !== -1) {
|
120
|
+
isBlockStyling = true;
|
121
|
+
return false;
|
122
|
+
}
|
123
|
+
return true;
|
124
|
+
});
|
125
|
+
return isBlockStyling;
|
126
|
+
};
|
127
|
+
const marksArePresent = state => {
|
128
|
+
const activeMarkTypes = FORMATTING_MARK_TYPES.filter(mark => {
|
129
|
+
if (!!state.schema.marks[mark]) {
|
130
|
+
const {
|
131
|
+
$from,
|
132
|
+
empty
|
133
|
+
} = state.selection;
|
134
|
+
const {
|
135
|
+
marks
|
136
|
+
} = state.schema;
|
137
|
+
if (empty) {
|
138
|
+
return !!marks[mark].isInSet(state.storedMarks || $from.marks());
|
139
|
+
}
|
140
|
+
return anyMarkActive(state, marks[mark]);
|
141
|
+
}
|
142
|
+
return false;
|
143
|
+
});
|
144
|
+
return activeMarkTypes.length > 0;
|
145
|
+
};
|
146
|
+
export const checkFormattingIsPresent = state => {
|
147
|
+
return marksArePresent(state) || blockStylingIsPresent(state);
|
148
|
+
};
|
149
|
+
export const hasBlockQuoteInOptions = dropdownOptions => {
|
150
|
+
return !!dropdownOptions.find(blockType => blockType.name === 'blockquote');
|
151
|
+
};
|
@@ -8,7 +8,7 @@ import { IconHeading, IconQuote } from '@atlaskit/editor-common/quick-insert';
|
|
8
8
|
import { ToolbarSize } from '@atlaskit/editor-common/types';
|
9
9
|
import { fg } from '@atlaskit/platform-feature-flags';
|
10
10
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
11
|
-
import { setBlockTypeWithAnalytics, insertBlockQuoteWithAnalytics, insertBlockQuoteWithAnalyticsCommand } from './pm-plugins/commands/block-type';
|
11
|
+
import { setBlockTypeWithAnalytics, insertBlockQuoteWithAnalytics, insertBlockQuoteWithAnalyticsCommand, clearFormatting as _clearFormatting } from './pm-plugins/commands/block-type';
|
12
12
|
import inputRulePlugin from './pm-plugins/input-rule';
|
13
13
|
import keymapPlugin from './pm-plugins/keymap';
|
14
14
|
import { createPlugin, pluginKey } from './pm-plugins/main';
|
@@ -184,6 +184,9 @@ var blockTypePlugin = function blockTypePlugin(_ref3) {
|
|
184
184
|
insertBlockQuote: function insertBlockQuote(inputMethod) {
|
185
185
|
var _api$analytics5;
|
186
186
|
return insertBlockQuoteWithAnalyticsCommand(inputMethod, api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions);
|
187
|
+
},
|
188
|
+
clearFormatting: function clearFormatting() {
|
189
|
+
return _clearFormatting();
|
187
190
|
}
|
188
191
|
},
|
189
192
|
getSharedState: function getSharedState(editorState) {
|
@@ -70,6 +70,8 @@ export var OTHER = {
|
|
70
70
|
nodeName: ''
|
71
71
|
};
|
72
72
|
export var TEXT_BLOCK_TYPES = [NORMAL_TEXT, HEADING_1, HEADING_2, HEADING_3, HEADING_4, HEADING_5, HEADING_6];
|
73
|
+
export var FORMATTING_NODE_TYPES = ['heading', 'blockquote'];
|
74
|
+
export var FORMATTING_MARK_TYPES = ['em', 'code', 'strike', 'strong', 'underline', 'textColor', 'subsup', 'backgroundColor'];
|
73
75
|
export var WRAPPER_BLOCK_TYPES = [BLOCK_QUOTE, CODE_BLOCK, PANEL];
|
74
76
|
export var ALL_BLOCK_TYPES = TEXT_BLOCK_TYPES.concat(WRAPPER_BLOCK_TYPES);
|
75
77
|
export var getBlockTypesInDropdown = function getBlockTypesInDropdown(includeBlockQuoteAsTextstyleOption) {
|
@@ -7,6 +7,7 @@ import { filterChildrenBetween, wrapSelectionIn } from '@atlaskit/editor-common/
|
|
7
7
|
import { Slice, Fragment } from '@atlaskit/editor-prosemirror/model';
|
8
8
|
import { CellSelection } from '@atlaskit/editor-tables';
|
9
9
|
import { HEADINGS_BY_NAME, NORMAL_TEXT } from '../block-types';
|
10
|
+
import { FORMATTING_NODE_TYPES, FORMATTING_MARK_TYPES, cellSelectionNodesBetween, formatTypes, clearNodeFormattingOnSelection } from './clear-formatting';
|
10
11
|
import { wrapSelectionInBlockType } from './wrapSelectionIn';
|
11
12
|
export function setBlockType(name) {
|
12
13
|
return function (_ref) {
|
@@ -98,9 +99,54 @@ export function setNormalText(fromBlockQuote) {
|
|
98
99
|
return tr;
|
99
100
|
};
|
100
101
|
}
|
101
|
-
function
|
102
|
+
export function clearFormatting() {
|
102
103
|
return function (_ref7) {
|
103
104
|
var tr = _ref7.tr;
|
105
|
+
var formattingCleared = [];
|
106
|
+
var schema = tr.doc.type.schema;
|
107
|
+
FORMATTING_MARK_TYPES.forEach(function (mark) {
|
108
|
+
var _tr$selection = tr.selection,
|
109
|
+
from = _tr$selection.from,
|
110
|
+
to = _tr$selection.to;
|
111
|
+
var markType = schema.marks[mark];
|
112
|
+
if (!markType) {
|
113
|
+
return;
|
114
|
+
}
|
115
|
+
if (tr.selection instanceof CellSelection) {
|
116
|
+
cellSelectionNodesBetween(tr.selection, tr.doc, function (node, pos) {
|
117
|
+
var isTableCell = node.type === schema.nodes.tableCell || node.type === schema.nodes.tableHeader;
|
118
|
+
if (!isTableCell) {
|
119
|
+
return true;
|
120
|
+
}
|
121
|
+
if (tr.doc.rangeHasMark(pos, pos + node.nodeSize, markType)) {
|
122
|
+
formattingCleared.push(formatTypes[mark]);
|
123
|
+
tr.removeMark(pos, pos + node.nodeSize, markType);
|
124
|
+
}
|
125
|
+
return false;
|
126
|
+
});
|
127
|
+
} else if (tr.doc.rangeHasMark(from, to, markType)) {
|
128
|
+
formattingCleared.push(formatTypes[mark]);
|
129
|
+
tr.removeMark(from, to, markType);
|
130
|
+
}
|
131
|
+
});
|
132
|
+
FORMATTING_NODE_TYPES.forEach(function (nodeName) {
|
133
|
+
var formattedNodeType = schema.nodes[nodeName];
|
134
|
+
var _tr$selection2 = tr.selection,
|
135
|
+
$from = _tr$selection2.$from,
|
136
|
+
$to = _tr$selection2.$to;
|
137
|
+
if (tr.selection instanceof CellSelection) {
|
138
|
+
cellSelectionNodesBetween(tr.selection, tr.doc, clearNodeFormattingOnSelection(schema, tr, formattedNodeType, nodeName, formattingCleared));
|
139
|
+
} else {
|
140
|
+
tr.doc.nodesBetween($from.pos, $to.pos, clearNodeFormattingOnSelection(schema, tr, formattedNodeType, nodeName, formattingCleared));
|
141
|
+
}
|
142
|
+
});
|
143
|
+
tr.setStoredMarks([]);
|
144
|
+
return tr;
|
145
|
+
};
|
146
|
+
}
|
147
|
+
function withCurrentHeadingLevel(fn) {
|
148
|
+
return function (_ref8) {
|
149
|
+
var tr = _ref8.tr;
|
104
150
|
// Find all headings and paragraphs of text
|
105
151
|
var _tr$doc$type$schema$n = tr.doc.type.schema.nodes,
|
106
152
|
heading = _tr$doc$type$schema$n.heading,
|
@@ -137,8 +183,8 @@ function withCurrentHeadingLevel(fn) {
|
|
137
183
|
}
|
138
184
|
export function setNormalTextWithAnalytics(inputMethod, editorAnalyticsApi, fromBlockQuote) {
|
139
185
|
return withCurrentHeadingLevel(function (previousHeadingLevel) {
|
140
|
-
return function (
|
141
|
-
var tr =
|
186
|
+
return function (_ref9) {
|
187
|
+
var tr = _ref9.tr;
|
142
188
|
editorAnalyticsApi === null || editorAnalyticsApi === void 0 || editorAnalyticsApi.attachAnalyticsEvent({
|
143
189
|
action: ACTION.FORMATTED,
|
144
190
|
actionSubject: ACTION_SUBJECT.TEXT,
|
@@ -161,8 +207,8 @@ export var setHeadingWithAnalytics = function setHeadingWithAnalytics(newHeading
|
|
161
207
|
// eslint-disable-next-line @typescript-eslint/max-params
|
162
208
|
) {
|
163
209
|
return withCurrentHeadingLevel(function (previousHeadingLevel) {
|
164
|
-
return function (
|
165
|
-
var tr =
|
210
|
+
return function (_ref10) {
|
211
|
+
var tr = _ref10.tr;
|
166
212
|
editorAnalyticsApi === null || editorAnalyticsApi === void 0 || editorAnalyticsApi.attachAnalyticsEvent({
|
167
213
|
action: ACTION.FORMATTED,
|
168
214
|
actionSubject: ACTION_SUBJECT.TEXT,
|
@@ -211,8 +257,8 @@ export var insertBlockQuoteWithAnalytics = function insertBlockQuoteWithAnalytic
|
|
211
257
|
};
|
212
258
|
export function insertBlockQuoteWithAnalyticsCommand(inputMethod, editorAnalyticsApi) {
|
213
259
|
return withCurrentHeadingLevel(function (previousHeadingLevel) {
|
214
|
-
return function (
|
215
|
-
var tr =
|
260
|
+
return function (_ref11) {
|
261
|
+
var tr = _ref11.tr;
|
216
262
|
var nodes = tr.doc.type.schema.nodes;
|
217
263
|
|
218
264
|
// TODO: analytics event
|
@@ -234,8 +280,8 @@ export function insertBlockQuoteWithAnalyticsCommand(inputMethod, editorAnalytic
|
|
234
280
|
});
|
235
281
|
}
|
236
282
|
export var cleanUpAtTheStartOfDocument = function cleanUpAtTheStartOfDocument(state, dispatch) {
|
237
|
-
var
|
238
|
-
$cursor =
|
283
|
+
var _ref12 = state.selection,
|
284
|
+
$cursor = _ref12.$cursor;
|
239
285
|
if ($cursor && !$cursor.nodeBefore && !$cursor.nodeAfter && $cursor.pos === 1) {
|
240
286
|
var tr = state.tr,
|
241
287
|
schema = state.schema;
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import { ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
|
2
|
+
import { liftTarget } from '@atlaskit/editor-prosemirror/transform';
|
3
|
+
// Functions duplicated from platform/packages/editor/editor-plugin-text-formatting/src/editor-commands/clear-formatting.ts
|
4
|
+
// TODO: Refactor to avoid duplication if platform_editor_blockquote_in_text_formatting_menu experiment is productionalised
|
5
|
+
export var FORMATTING_NODE_TYPES = ['heading', 'blockquote'];
|
6
|
+
export var FORMATTING_MARK_TYPES = ['em', 'code', 'strike', 'strong', 'underline', 'textColor', 'subsup', 'backgroundColor'];
|
7
|
+
export var formatTypes = {
|
8
|
+
em: ACTION_SUBJECT_ID.FORMAT_ITALIC,
|
9
|
+
code: ACTION_SUBJECT_ID.FORMAT_CODE,
|
10
|
+
strike: ACTION_SUBJECT_ID.FORMAT_STRIKE,
|
11
|
+
strong: ACTION_SUBJECT_ID.FORMAT_STRONG,
|
12
|
+
underline: ACTION_SUBJECT_ID.FORMAT_UNDERLINE,
|
13
|
+
textColor: ACTION_SUBJECT_ID.FORMAT_COLOR,
|
14
|
+
subsup: 'subsup',
|
15
|
+
backgroundColor: ACTION_SUBJECT_ID.FORMAT_BACKGROUND_COLOR
|
16
|
+
};
|
17
|
+
export var cellSelectionNodesBetween = function cellSelectionNodesBetween(selection, doc, f, startPos
|
18
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
19
|
+
) {
|
20
|
+
selection.forEachCell(function (cell, cellPos) {
|
21
|
+
doc.nodesBetween(cellPos, cellPos + cell.nodeSize, f, startPos);
|
22
|
+
});
|
23
|
+
};
|
24
|
+
|
25
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
26
|
+
export function clearNodeFormattingOnSelection(schema, tr, formattedNodeType, nodeName, formattingCleared) {
|
27
|
+
return function (node, pos) {
|
28
|
+
if (node.type === formattedNodeType) {
|
29
|
+
if (formattedNodeType.isTextblock) {
|
30
|
+
tr.setNodeMarkup(pos, schema.nodes.paragraph);
|
31
|
+
formattingCleared.push(nodeName);
|
32
|
+
return false;
|
33
|
+
} else {
|
34
|
+
// In case of panel or blockquote
|
35
|
+
var fromPos = tr.doc.resolve(pos + 1);
|
36
|
+
var toPos = tr.doc.resolve(pos + node.nodeSize - 1);
|
37
|
+
var nodeRange = fromPos.blockRange(toPos);
|
38
|
+
if (nodeRange) {
|
39
|
+
var targetLiftDepth = liftTarget(nodeRange);
|
40
|
+
if (targetLiftDepth || targetLiftDepth === 0) {
|
41
|
+
formattingCleared.push(nodeName);
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
43
|
+
tr.lift(nodeRange, targetLiftDepth);
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
return true;
|
49
|
+
};
|
50
|
+
}
|