@atlaskit/editor-plugin-text-formatting 0.1.0
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/.eslintrc.js +7 -0
- package/CHANGELOG.md +1 -0
- package/LICENSE.md +13 -0
- package/README.md +9 -0
- package/dist/cjs/actions.js +188 -0
- package/dist/cjs/commands/clear-formatting.js +111 -0
- package/dist/cjs/commands/text-formatting.js +143 -0
- package/dist/cjs/commands/transform-to-code.js +68 -0
- package/dist/cjs/index.js +12 -0
- package/dist/cjs/plugin.js +133 -0
- package/dist/cjs/pm-plugins/clear-formatting-keymap.js +21 -0
- package/dist/cjs/pm-plugins/clear-formatting.js +36 -0
- package/dist/cjs/pm-plugins/cursor.js +55 -0
- package/dist/cjs/pm-plugins/input-rule.js +274 -0
- package/dist/cjs/pm-plugins/keymap.js +52 -0
- package/dist/cjs/pm-plugins/main.js +113 -0
- package/dist/cjs/pm-plugins/plugin-key.js +9 -0
- package/dist/cjs/pm-plugins/smart-input-rule.js +176 -0
- package/dist/cjs/ui/Toolbar/constants.js +19 -0
- package/dist/cjs/ui/Toolbar/dropdown-menu.js +86 -0
- package/dist/cjs/ui/Toolbar/hooks/clear-formatting-icon.js +55 -0
- package/dist/cjs/ui/Toolbar/hooks/formatting-icons.js +227 -0
- package/dist/cjs/ui/Toolbar/hooks/menu-state.js +23 -0
- package/dist/cjs/ui/Toolbar/hooks/responsive-toolbar-buttons.js +60 -0
- package/dist/cjs/ui/Toolbar/index.js +183 -0
- package/dist/cjs/ui/Toolbar/more-button.js +42 -0
- package/dist/cjs/ui/Toolbar/single-toolbar-buttons.js +49 -0
- package/dist/cjs/ui/Toolbar/types.js +17 -0
- package/dist/cjs/utils/cell-selection.js +12 -0
- package/dist/cjs/utils.js +86 -0
- package/dist/cjs/version.json +5 -0
- package/dist/es2019/actions.js +161 -0
- package/dist/es2019/commands/clear-formatting.js +105 -0
- package/dist/es2019/commands/text-formatting.js +144 -0
- package/dist/es2019/commands/transform-to-code.js +71 -0
- package/dist/es2019/index.js +1 -0
- package/dist/es2019/plugin.js +124 -0
- package/dist/es2019/pm-plugins/clear-formatting-keymap.js +10 -0
- package/dist/es2019/pm-plugins/clear-formatting.js +26 -0
- package/dist/es2019/pm-plugins/cursor.js +52 -0
- package/dist/es2019/pm-plugins/input-rule.js +242 -0
- package/dist/es2019/pm-plugins/keymap.js +43 -0
- package/dist/es2019/pm-plugins/main.js +110 -0
- package/dist/es2019/pm-plugins/plugin-key.js +2 -0
- package/dist/es2019/pm-plugins/smart-input-rule.js +155 -0
- package/dist/es2019/ui/Toolbar/constants.js +20 -0
- package/dist/es2019/ui/Toolbar/dropdown-menu.js +66 -0
- package/dist/es2019/ui/Toolbar/hooks/clear-formatting-icon.js +44 -0
- package/dist/es2019/ui/Toolbar/hooks/formatting-icons.js +212 -0
- package/dist/es2019/ui/Toolbar/hooks/menu-state.js +11 -0
- package/dist/es2019/ui/Toolbar/hooks/responsive-toolbar-buttons.js +48 -0
- package/dist/es2019/ui/Toolbar/index.js +168 -0
- package/dist/es2019/ui/Toolbar/more-button.js +34 -0
- package/dist/es2019/ui/Toolbar/single-toolbar-buttons.js +39 -0
- package/dist/es2019/ui/Toolbar/types.js +10 -0
- package/dist/es2019/utils/cell-selection.js +5 -0
- package/dist/es2019/utils.js +74 -0
- package/dist/es2019/version.json +5 -0
- package/dist/esm/actions.js +168 -0
- package/dist/esm/commands/clear-formatting.js +101 -0
- package/dist/esm/commands/text-formatting.js +134 -0
- package/dist/esm/commands/transform-to-code.js +61 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/plugin.js +125 -0
- package/dist/esm/pm-plugins/clear-formatting-keymap.js +10 -0
- package/dist/esm/pm-plugins/clear-formatting.js +28 -0
- package/dist/esm/pm-plugins/cursor.js +48 -0
- package/dist/esm/pm-plugins/input-rule.js +257 -0
- package/dist/esm/pm-plugins/keymap.js +43 -0
- package/dist/esm/pm-plugins/main.js +99 -0
- package/dist/esm/pm-plugins/plugin-key.js +2 -0
- package/dist/esm/pm-plugins/smart-input-rule.js +169 -0
- package/dist/esm/ui/Toolbar/constants.js +8 -0
- package/dist/esm/ui/Toolbar/dropdown-menu.js +75 -0
- package/dist/esm/ui/Toolbar/hooks/clear-formatting-icon.js +47 -0
- package/dist/esm/ui/Toolbar/hooks/formatting-icons.js +215 -0
- package/dist/esm/ui/Toolbar/hooks/menu-state.js +15 -0
- package/dist/esm/ui/Toolbar/hooks/responsive-toolbar-buttons.js +50 -0
- package/dist/esm/ui/Toolbar/index.js +174 -0
- package/dist/esm/ui/Toolbar/more-button.js +33 -0
- package/dist/esm/ui/Toolbar/single-toolbar-buttons.js +38 -0
- package/dist/esm/ui/Toolbar/types.js +10 -0
- package/dist/esm/utils/cell-selection.js +5 -0
- package/dist/esm/utils.js +75 -0
- package/dist/esm/version.json +5 -0
- package/dist/types/actions.d.ts +22 -0
- package/dist/types/commands/clear-formatting.d.ts +6 -0
- package/dist/types/commands/text-formatting.d.ts +5 -0
- package/dist/types/commands/transform-to-code.d.ts +2 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/plugin.d.ts +17 -0
- package/dist/types/pm-plugins/clear-formatting-keymap.d.ts +4 -0
- package/dist/types/pm-plugins/clear-formatting.d.ts +8 -0
- package/dist/types/pm-plugins/cursor.d.ts +3 -0
- package/dist/types/pm-plugins/input-rule.d.ts +23 -0
- package/dist/types/pm-plugins/keymap.d.ts +4 -0
- package/dist/types/pm-plugins/main.d.ts +7 -0
- package/dist/types/pm-plugins/plugin-key.d.ts +3 -0
- package/dist/types/pm-plugins/smart-input-rule.d.ts +3 -0
- package/dist/types/ui/Toolbar/constants.d.ts +6 -0
- package/dist/types/ui/Toolbar/dropdown-menu.d.ts +15 -0
- package/dist/types/ui/Toolbar/hooks/clear-formatting-icon.d.ts +7 -0
- package/dist/types/ui/Toolbar/hooks/formatting-icons.d.ts +14 -0
- package/dist/types/ui/Toolbar/hooks/menu-state.d.ts +1 -0
- package/dist/types/ui/Toolbar/hooks/responsive-toolbar-buttons.d.ts +20 -0
- package/dist/types/ui/Toolbar/index.d.ts +25 -0
- package/dist/types/ui/Toolbar/more-button.d.ts +14 -0
- package/dist/types/ui/Toolbar/single-toolbar-buttons.d.ts +10 -0
- package/dist/types/ui/Toolbar/types.d.ts +32 -0
- package/dist/types/utils/cell-selection.d.ts +3 -0
- package/dist/types/utils.d.ts +11 -0
- package/dist/types-ts4.5/actions.d.ts +22 -0
- package/dist/types-ts4.5/commands/clear-formatting.d.ts +6 -0
- package/dist/types-ts4.5/commands/text-formatting.d.ts +5 -0
- package/dist/types-ts4.5/commands/transform-to-code.d.ts +2 -0
- package/dist/types-ts4.5/index.d.ts +3 -0
- package/dist/types-ts4.5/plugin.d.ts +19 -0
- package/dist/types-ts4.5/pm-plugins/clear-formatting-keymap.d.ts +4 -0
- package/dist/types-ts4.5/pm-plugins/clear-formatting.d.ts +8 -0
- package/dist/types-ts4.5/pm-plugins/cursor.d.ts +3 -0
- package/dist/types-ts4.5/pm-plugins/input-rule.d.ts +23 -0
- package/dist/types-ts4.5/pm-plugins/keymap.d.ts +4 -0
- package/dist/types-ts4.5/pm-plugins/main.d.ts +7 -0
- package/dist/types-ts4.5/pm-plugins/plugin-key.d.ts +3 -0
- package/dist/types-ts4.5/pm-plugins/smart-input-rule.d.ts +3 -0
- package/dist/types-ts4.5/ui/Toolbar/constants.d.ts +6 -0
- package/dist/types-ts4.5/ui/Toolbar/dropdown-menu.d.ts +15 -0
- package/dist/types-ts4.5/ui/Toolbar/hooks/clear-formatting-icon.d.ts +7 -0
- package/dist/types-ts4.5/ui/Toolbar/hooks/formatting-icons.d.ts +14 -0
- package/dist/types-ts4.5/ui/Toolbar/hooks/menu-state.d.ts +5 -0
- package/dist/types-ts4.5/ui/Toolbar/hooks/responsive-toolbar-buttons.d.ts +20 -0
- package/dist/types-ts4.5/ui/Toolbar/index.d.ts +25 -0
- package/dist/types-ts4.5/ui/Toolbar/more-button.d.ts +14 -0
- package/dist/types-ts4.5/ui/Toolbar/single-toolbar-buttons.d.ts +10 -0
- package/dist/types-ts4.5/ui/Toolbar/types.d.ts +32 -0
- package/dist/types-ts4.5/utils/cell-selection.d.ts +3 -0
- package/dist/types-ts4.5/utils.d.ts +11 -0
- package/package.json +93 -0
- package/report.api.md +66 -0
- package/tmp/api-report-tmp.d.ts +39 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, PUNC, SYMBOL } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
import { inputRuleWithAnalytics } from '@atlaskit/editor-common/utils';
|
|
3
|
+
import { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
4
|
+
import { createPlugin, createRule } from '@atlaskit/prosemirror-input-rules';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates an InputRuleHandler that will match on a regular expression of the
|
|
8
|
+
* form `(prefix, content, suffix?)`, and replace it with some given text,
|
|
9
|
+
* maintaining prefix and suffix around the replacement.
|
|
10
|
+
*
|
|
11
|
+
* @param text text to replace with
|
|
12
|
+
*/
|
|
13
|
+
function replaceTextUsingCaptureGroup(text) {
|
|
14
|
+
return (state, match, start, end) => {
|
|
15
|
+
const [, prefix,, suffix] = match;
|
|
16
|
+
const replacement = text + (suffix || '');
|
|
17
|
+
let {
|
|
18
|
+
tr,
|
|
19
|
+
selection: {
|
|
20
|
+
$to
|
|
21
|
+
}
|
|
22
|
+
} = state;
|
|
23
|
+
const startPos = start + (prefix || '').length;
|
|
24
|
+
const safePos = Math.min(
|
|
25
|
+
// I know it is almost impossible to have a negative value at this point.
|
|
26
|
+
// But, this is JS #trustnoone
|
|
27
|
+
Math.max(startPos, 0), end);
|
|
28
|
+
tr.replaceWith(safePos, end, state.schema.text(replacement, $to.marks()));
|
|
29
|
+
tr.setSelection(Selection.near(tr.doc.resolve(tr.selection.to)));
|
|
30
|
+
return tr;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function createReplacementRule(to, from) {
|
|
34
|
+
return createRule(from, replaceTextUsingCaptureGroup(to));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create replacement rules fiven a replacement map
|
|
39
|
+
* @param replMap - Replacement map
|
|
40
|
+
* @param trackingEventName - Analytics V2 tracking event name
|
|
41
|
+
* @param replacementRuleWithAnalytics - Analytics GAS V3 middleware for replacement and rules.
|
|
42
|
+
*/
|
|
43
|
+
function createReplacementRules(replMap, replacementRuleWithAnalytics) {
|
|
44
|
+
return Object.keys(replMap).map(replacement => {
|
|
45
|
+
const regex = replMap[replacement];
|
|
46
|
+
const rule = createReplacementRule(replacement, regex);
|
|
47
|
+
if (replacementRuleWithAnalytics) {
|
|
48
|
+
return replacementRuleWithAnalytics(replacement)(rule);
|
|
49
|
+
}
|
|
50
|
+
return rule;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// We don't agressively upgrade single quotes to smart quotes because
|
|
55
|
+
// they may clash with an emoji. Only do that when we have a matching
|
|
56
|
+
// single quote, or a contraction.
|
|
57
|
+
function createSingleQuotesRules() {
|
|
58
|
+
return [
|
|
59
|
+
// wrapped text
|
|
60
|
+
createRule(/(\s|^)'(\S+.*\S+)'$/, (state, match, start, end) => {
|
|
61
|
+
const OPEN_SMART_QUOTE_CHAR = '‘';
|
|
62
|
+
const CLOSED_SMART_QUOTE_CHAR = '’';
|
|
63
|
+
const [, spacing, innerContent] = match;
|
|
64
|
+
// Edge case where match begins with some spacing. We need to add
|
|
65
|
+
// it back to the document
|
|
66
|
+
const openQuoteReplacement = spacing + OPEN_SMART_QUOTE_CHAR;
|
|
67
|
+
// End is not always where the closed quote is, edge case exists
|
|
68
|
+
// when there is spacing after the closed quote. We need to
|
|
69
|
+
// determine position of closed quote from the start position.
|
|
70
|
+
const positionOfClosedQuote = start + openQuoteReplacement.length + innerContent.length;
|
|
71
|
+
return state.tr.insertText(CLOSED_SMART_QUOTE_CHAR, positionOfClosedQuote, end).insertText(openQuoteReplacement, start, start + openQuoteReplacement.length);
|
|
72
|
+
}),
|
|
73
|
+
// apostrophe
|
|
74
|
+
createReplacementRule('’', /(\w+)(')(\w+)$/)];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get replacement rules related to product
|
|
79
|
+
*/
|
|
80
|
+
function getProductRules(editorAnalyticsAPI) {
|
|
81
|
+
const productRuleWithAnalytics = product => inputRuleWithAnalytics((_, match) => ({
|
|
82
|
+
action: ACTION.SUBSTITUTED,
|
|
83
|
+
actionSubject: ACTION_SUBJECT.TEXT,
|
|
84
|
+
actionSubjectId: ACTION_SUBJECT_ID.PRODUCT_NAME,
|
|
85
|
+
eventType: EVENT_TYPE.TRACK,
|
|
86
|
+
attributes: {
|
|
87
|
+
product,
|
|
88
|
+
originalSpelling: match[2]
|
|
89
|
+
}
|
|
90
|
+
}), editorAnalyticsAPI);
|
|
91
|
+
return createReplacementRules({
|
|
92
|
+
Atlassian: /(\s+|^)(atlassian)(\s)$/,
|
|
93
|
+
Jira: /(\s+|^)(jira|JIRA)(\s)$/,
|
|
94
|
+
Bitbucket: /(\s+|^)(bitbucket|BitBucket)(\s)$/,
|
|
95
|
+
Hipchat: /(\s+|^)(hipchat|HipChat)(\s)$/,
|
|
96
|
+
Trello: /(\s+|^)(trello)(\s)$/
|
|
97
|
+
}, productRuleWithAnalytics);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get replacement rules related to symbol
|
|
102
|
+
*/
|
|
103
|
+
function getSymbolRules(editorAnalyticsAPI) {
|
|
104
|
+
const symbolToString = {
|
|
105
|
+
'→': SYMBOL.ARROW_RIGHT,
|
|
106
|
+
'←': SYMBOL.ARROW_LEFT,
|
|
107
|
+
'↔︎': SYMBOL.ARROW_DOUBLE
|
|
108
|
+
};
|
|
109
|
+
const symbolRuleWithAnalytics = symbol => inputRuleWithAnalytics({
|
|
110
|
+
action: ACTION.SUBSTITUTED,
|
|
111
|
+
actionSubject: ACTION_SUBJECT.TEXT,
|
|
112
|
+
actionSubjectId: ACTION_SUBJECT_ID.SYMBOL,
|
|
113
|
+
eventType: EVENT_TYPE.TRACK,
|
|
114
|
+
attributes: {
|
|
115
|
+
symbol: symbolToString[symbol]
|
|
116
|
+
}
|
|
117
|
+
}, editorAnalyticsAPI);
|
|
118
|
+
return createReplacementRules({
|
|
119
|
+
'→': /(\s+|^)(--?>)(\s)$/,
|
|
120
|
+
'←': /(\s+|^)(<--?)(\s)$/,
|
|
121
|
+
'↔︎': /(\s+|^)(<->?)(\s)$/
|
|
122
|
+
}, symbolRuleWithAnalytics);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get replacement rules related to punctuation
|
|
127
|
+
*/
|
|
128
|
+
function getPunctuationRules(editorAnalyticsAPI) {
|
|
129
|
+
const punctuationToString = {
|
|
130
|
+
'–': PUNC.DASH,
|
|
131
|
+
'…': PUNC.ELLIPSIS,
|
|
132
|
+
'”': PUNC.QUOTE_DOUBLE,
|
|
133
|
+
[PUNC.QUOTE_SINGLE]: PUNC.QUOTE_SINGLE
|
|
134
|
+
};
|
|
135
|
+
const punctuationRuleWithAnalytics = punctuation => inputRuleWithAnalytics({
|
|
136
|
+
action: ACTION.SUBSTITUTED,
|
|
137
|
+
actionSubject: ACTION_SUBJECT.TEXT,
|
|
138
|
+
actionSubjectId: ACTION_SUBJECT_ID.PUNC,
|
|
139
|
+
eventType: EVENT_TYPE.TRACK,
|
|
140
|
+
attributes: {
|
|
141
|
+
punctuation: punctuationToString[punctuation]
|
|
142
|
+
}
|
|
143
|
+
}, editorAnalyticsAPI);
|
|
144
|
+
const dashEllipsisRules = createReplacementRules({
|
|
145
|
+
'–': /(\s+|^)(--)(\s)$/,
|
|
146
|
+
'…': /()(\.\.\.)$/
|
|
147
|
+
}, punctuationRuleWithAnalytics);
|
|
148
|
+
const doubleQuoteRules = createReplacementRules({
|
|
149
|
+
'“': /((?:^|[\s\{\[\(\<'"\u2018\u201C]))(")$/,
|
|
150
|
+
'”': /"$/
|
|
151
|
+
}, punctuationRuleWithAnalytics);
|
|
152
|
+
const singleQuoteRules = createSingleQuotesRules();
|
|
153
|
+
return [...dashEllipsisRules, ...doubleQuoteRules, ...singleQuoteRules.map(rule => punctuationRuleWithAnalytics(PUNC.QUOTE_SINGLE)(rule))];
|
|
154
|
+
}
|
|
155
|
+
export default (editorAnalyticsAPI => createPlugin('text-formatting:smart-input', [...getProductRules(editorAnalyticsAPI), ...getSymbolRules(editorAnalyticsAPI), ...getPunctuationRules(editorAnalyticsAPI)]));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ToolbarSize } from '@atlaskit/editor-common/types';
|
|
2
|
+
import { IconTypes } from './types';
|
|
3
|
+
export const DefaultButtonsToolbar = [IconTypes.strong, IconTypes.em];
|
|
4
|
+
export const DefaultButtonsMenu = [IconTypes.underline, IconTypes.strike, IconTypes.code, IconTypes.subscript, IconTypes.superscript];
|
|
5
|
+
export const ResponsiveCustomButtonToolbar = {
|
|
6
|
+
[ToolbarSize.XXL]: DefaultButtonsToolbar,
|
|
7
|
+
[ToolbarSize.XL]: DefaultButtonsToolbar,
|
|
8
|
+
[ToolbarSize.L]: DefaultButtonsToolbar,
|
|
9
|
+
[ToolbarSize.M]: [],
|
|
10
|
+
[ToolbarSize.S]: [],
|
|
11
|
+
[ToolbarSize.XXXS]: []
|
|
12
|
+
};
|
|
13
|
+
export const ResponsiveCustomMenu = {
|
|
14
|
+
[ToolbarSize.XXL]: DefaultButtonsMenu,
|
|
15
|
+
[ToolbarSize.XL]: DefaultButtonsMenu,
|
|
16
|
+
[ToolbarSize.L]: DefaultButtonsMenu,
|
|
17
|
+
[ToolbarSize.M]: [IconTypes.strong, IconTypes.em, ...DefaultButtonsMenu],
|
|
18
|
+
[ToolbarSize.S]: [IconTypes.strong, IconTypes.em, ...DefaultButtonsMenu],
|
|
19
|
+
[ToolbarSize.XXXS]: [IconTypes.strong, IconTypes.em, ...DefaultButtonsMenu]
|
|
20
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import { DropdownMenuWithKeyboardNavigation as DropdownMenu } from '@atlaskit/editor-common/ui-menu';
|
|
3
|
+
import { akEditorMenuZIndex } from '@atlaskit/editor-shared-styles';
|
|
4
|
+
import { useMenuState } from './hooks/menu-state';
|
|
5
|
+
import { MoreButton } from './more-button';
|
|
6
|
+
export const FormattingTextDropdownMenu = /*#__PURE__*/React.memo(({
|
|
7
|
+
editorView,
|
|
8
|
+
moreButtonLabel,
|
|
9
|
+
isReducedSpacing,
|
|
10
|
+
items,
|
|
11
|
+
hasFormattingActive,
|
|
12
|
+
popupsBoundariesElement,
|
|
13
|
+
popupsMountPoint,
|
|
14
|
+
popupsScrollableElement
|
|
15
|
+
}) => {
|
|
16
|
+
const [isMenuOpen, toggleMenu, closeMenu] = useMenuState();
|
|
17
|
+
const [isOpenedByKeyboard, setIsOpenedByKeyboard] = useState(false);
|
|
18
|
+
const group = useMemo(() => [{
|
|
19
|
+
items
|
|
20
|
+
}], [items]);
|
|
21
|
+
const onItemActivated = useCallback(({
|
|
22
|
+
item,
|
|
23
|
+
shouldCloseMenu = true
|
|
24
|
+
}) => {
|
|
25
|
+
item.command(editorView.state, editorView.dispatch);
|
|
26
|
+
if (shouldCloseMenu) {
|
|
27
|
+
closeMenu();
|
|
28
|
+
}
|
|
29
|
+
}, [editorView.state, editorView.dispatch, closeMenu]);
|
|
30
|
+
return /*#__PURE__*/React.createElement(DropdownMenu, {
|
|
31
|
+
mountTo: popupsMountPoint,
|
|
32
|
+
onOpenChange: closeMenu,
|
|
33
|
+
boundariesElement: popupsBoundariesElement,
|
|
34
|
+
scrollableElement: popupsScrollableElement,
|
|
35
|
+
onItemActivated: onItemActivated,
|
|
36
|
+
isOpen: isMenuOpen,
|
|
37
|
+
items: group,
|
|
38
|
+
zIndex: akEditorMenuZIndex,
|
|
39
|
+
fitHeight: 188,
|
|
40
|
+
fitWidth: 136,
|
|
41
|
+
shouldUseDefaultRole: true,
|
|
42
|
+
shouldFocusFirstItem: () => {
|
|
43
|
+
if (isOpenedByKeyboard) {
|
|
44
|
+
setIsOpenedByKeyboard(false);
|
|
45
|
+
}
|
|
46
|
+
return isOpenedByKeyboard;
|
|
47
|
+
}
|
|
48
|
+
}, /*#__PURE__*/React.createElement(MoreButton, {
|
|
49
|
+
isSelected: isMenuOpen || hasFormattingActive,
|
|
50
|
+
label: moreButtonLabel,
|
|
51
|
+
isReducedSpacing: isReducedSpacing,
|
|
52
|
+
isDisabled: false,
|
|
53
|
+
onClick: () => {
|
|
54
|
+
toggleMenu();
|
|
55
|
+
setIsOpenedByKeyboard(false);
|
|
56
|
+
},
|
|
57
|
+
onKeyDown: event => {
|
|
58
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
59
|
+
event.preventDefault();
|
|
60
|
+
toggleMenu();
|
|
61
|
+
setIsOpenedByKeyboard(true);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"aria-expanded": isMenuOpen
|
|
65
|
+
}));
|
|
66
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** @jsx jsx */
|
|
2
|
+
import { useCallback, useMemo } from 'react';
|
|
3
|
+
import { jsx } from '@emotion/react';
|
|
4
|
+
import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
5
|
+
import { clearFormatting as clearFormattingKeymap, tooltip } from '@atlaskit/editor-common/keymaps';
|
|
6
|
+
import { toolbarMessages } from '@atlaskit/editor-common/messages';
|
|
7
|
+
import { shortcutStyle } from '@atlaskit/editor-shared-styles/shortcut';
|
|
8
|
+
import { clearFormattingWithAnalytics } from '../../../commands/clear-formatting';
|
|
9
|
+
import { pluginKey as clearFormattingPluginKey } from '../../../pm-plugins/clear-formatting';
|
|
10
|
+
const useClearFormattingPluginState = editorState => {
|
|
11
|
+
return useMemo(() => clearFormattingPluginKey.getState(editorState), [editorState]);
|
|
12
|
+
};
|
|
13
|
+
export const useClearIcon = ({
|
|
14
|
+
intl,
|
|
15
|
+
editorState,
|
|
16
|
+
editorAnalyticsAPI
|
|
17
|
+
}) => {
|
|
18
|
+
const pluginState = useClearFormattingPluginState(editorState);
|
|
19
|
+
const isPluginAvailable = Boolean(pluginState);
|
|
20
|
+
const formattingIsPresent = Boolean(pluginState === null || pluginState === void 0 ? void 0 : pluginState.formattingIsPresent);
|
|
21
|
+
const clearFormattingLabel = intl.formatMessage(toolbarMessages.clearFormatting);
|
|
22
|
+
const clearFormattingToolbar = useCallback((state, dispatch) => clearFormattingWithAnalytics(INPUT_METHOD.TOOLBAR, editorAnalyticsAPI)(state, dispatch), [editorAnalyticsAPI]);
|
|
23
|
+
return useMemo(() => {
|
|
24
|
+
if (!isPluginAvailable) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
key: 'clearFormatting',
|
|
29
|
+
command: clearFormattingToolbar,
|
|
30
|
+
content: clearFormattingLabel,
|
|
31
|
+
elemAfter:
|
|
32
|
+
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
|
|
33
|
+
jsx("div", {
|
|
34
|
+
css: shortcutStyle
|
|
35
|
+
}, tooltip(clearFormattingKeymap)),
|
|
36
|
+
value: {
|
|
37
|
+
name: 'clearFormatting'
|
|
38
|
+
},
|
|
39
|
+
isActive: false,
|
|
40
|
+
isDisabled: !formattingIsPresent,
|
|
41
|
+
'aria-label': clearFormattingKeymap ? tooltip(clearFormattingKeymap, String(clearFormattingLabel)) : String(clearFormattingLabel)
|
|
42
|
+
};
|
|
43
|
+
}, [isPluginAvailable, clearFormattingToolbar, clearFormattingLabel, formattingIsPresent]);
|
|
44
|
+
};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/** @jsx jsx */
|
|
2
|
+
import React, { useMemo } from 'react';
|
|
3
|
+
import { jsx } from '@emotion/react';
|
|
4
|
+
import { INPUT_METHOD, TOOLBAR_ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
|
|
5
|
+
import { getAriaKeyshortcuts, toggleBold, toggleCode, toggleItalic, toggleStrikethrough, toggleSubscript, toggleSuperscript, toggleUnderline, tooltip, ToolTipContent } from '@atlaskit/editor-common/keymaps';
|
|
6
|
+
import { toolbarMessages } from '@atlaskit/editor-common/messages';
|
|
7
|
+
import { shortcutStyle } from '@atlaskit/editor-shared-styles/shortcut';
|
|
8
|
+
import BoldIcon from '@atlaskit/icon/glyph/editor/bold';
|
|
9
|
+
import ItalicIcon from '@atlaskit/icon/glyph/editor/italic';
|
|
10
|
+
import { toggleCodeWithAnalytics, toggleEmWithAnalytics, toggleStrikeWithAnalytics, toggleStrongWithAnalytics, toggleSubscriptWithAnalytics, toggleSuperscriptWithAnalytics, toggleUnderlineWithAnalytics } from '../../../actions';
|
|
11
|
+
import { pluginKey as textFormattingPluginKey } from '../../../pm-plugins/plugin-key';
|
|
12
|
+
import { IconTypes } from '../types';
|
|
13
|
+
const withToolbarInputMethod = func => func({
|
|
14
|
+
inputMethod: INPUT_METHOD.TOOLBAR
|
|
15
|
+
});
|
|
16
|
+
const IconButtons = editorAnalyticsAPI => ({
|
|
17
|
+
strong: {
|
|
18
|
+
buttonId: TOOLBAR_ACTION_SUBJECT_ID.TEXT_FORMATTING_STRONG,
|
|
19
|
+
command: withToolbarInputMethod(toggleStrongWithAnalytics(editorAnalyticsAPI)),
|
|
20
|
+
message: toolbarMessages.bold,
|
|
21
|
+
tooltipKeymap: toggleBold,
|
|
22
|
+
component: () => jsx(BoldIcon, {
|
|
23
|
+
label: ""
|
|
24
|
+
})
|
|
25
|
+
},
|
|
26
|
+
em: {
|
|
27
|
+
buttonId: TOOLBAR_ACTION_SUBJECT_ID.TEXT_FORMATTING_ITALIC,
|
|
28
|
+
command: withToolbarInputMethod(toggleEmWithAnalytics(editorAnalyticsAPI)),
|
|
29
|
+
message: toolbarMessages.italic,
|
|
30
|
+
tooltipKeymap: toggleItalic,
|
|
31
|
+
component: () => jsx(ItalicIcon, {
|
|
32
|
+
label: ""
|
|
33
|
+
})
|
|
34
|
+
},
|
|
35
|
+
underline: {
|
|
36
|
+
command: withToolbarInputMethod(toggleUnderlineWithAnalytics(editorAnalyticsAPI)),
|
|
37
|
+
message: toolbarMessages.underline,
|
|
38
|
+
tooltipKeymap: toggleUnderline
|
|
39
|
+
},
|
|
40
|
+
strike: {
|
|
41
|
+
command: withToolbarInputMethod(toggleStrikeWithAnalytics(editorAnalyticsAPI)),
|
|
42
|
+
message: toolbarMessages.strike,
|
|
43
|
+
tooltipKeymap: toggleStrikethrough
|
|
44
|
+
},
|
|
45
|
+
code: {
|
|
46
|
+
command: withToolbarInputMethod(toggleCodeWithAnalytics(editorAnalyticsAPI)),
|
|
47
|
+
message: toolbarMessages.code,
|
|
48
|
+
tooltipKeymap: toggleCode
|
|
49
|
+
},
|
|
50
|
+
subscript: {
|
|
51
|
+
command: withToolbarInputMethod(toggleSubscriptWithAnalytics(editorAnalyticsAPI)),
|
|
52
|
+
message: toolbarMessages.subscript,
|
|
53
|
+
tooltipKeymap: toggleSubscript
|
|
54
|
+
},
|
|
55
|
+
superscript: {
|
|
56
|
+
command: withToolbarInputMethod(toggleSuperscriptWithAnalytics(editorAnalyticsAPI)),
|
|
57
|
+
message: toolbarMessages.superscript,
|
|
58
|
+
tooltipKeymap: toggleSuperscript
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
const getIcon = ({
|
|
62
|
+
iconType,
|
|
63
|
+
isDisabled,
|
|
64
|
+
isActive,
|
|
65
|
+
intl,
|
|
66
|
+
editorAnalyticsAPI
|
|
67
|
+
}) => {
|
|
68
|
+
const icon = IconButtons(editorAnalyticsAPI)[iconType];
|
|
69
|
+
const content = intl.formatMessage(icon.message);
|
|
70
|
+
const {
|
|
71
|
+
tooltipKeymap
|
|
72
|
+
} = icon;
|
|
73
|
+
return {
|
|
74
|
+
content,
|
|
75
|
+
buttonId: icon.buttonId,
|
|
76
|
+
iconMark: iconType,
|
|
77
|
+
key: iconType,
|
|
78
|
+
command: icon.command,
|
|
79
|
+
iconElement: icon.component ? icon.component() : undefined,
|
|
80
|
+
tooltipElement: tooltipKeymap ? jsx(ToolTipContent, {
|
|
81
|
+
description: content,
|
|
82
|
+
keymap: tooltipKeymap
|
|
83
|
+
}) : undefined,
|
|
84
|
+
elemAfter: tooltipKeymap ?
|
|
85
|
+
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
|
|
86
|
+
jsx("div", {
|
|
87
|
+
css: shortcutStyle
|
|
88
|
+
}, tooltip(tooltipKeymap)) : undefined,
|
|
89
|
+
value: {
|
|
90
|
+
name: iconType
|
|
91
|
+
},
|
|
92
|
+
isActive,
|
|
93
|
+
isDisabled,
|
|
94
|
+
'aria-label': tooltipKeymap ? tooltip(tooltipKeymap, String(content)) : String(content),
|
|
95
|
+
'aria-keyshortcuts': getAriaKeyshortcuts(tooltipKeymap)
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
const IconsMarkSchema = {
|
|
99
|
+
[IconTypes.strong]: 'strong',
|
|
100
|
+
[IconTypes.em]: 'em',
|
|
101
|
+
[IconTypes.strike]: 'strike',
|
|
102
|
+
[IconTypes.code]: 'code',
|
|
103
|
+
[IconTypes.underline]: 'underline',
|
|
104
|
+
[IconTypes.superscript]: 'subsup',
|
|
105
|
+
[IconTypes.subscript]: 'subsup'
|
|
106
|
+
};
|
|
107
|
+
const buildMenuIconState = iconMark => ({
|
|
108
|
+
schema,
|
|
109
|
+
textFormattingPluginState
|
|
110
|
+
}) => {
|
|
111
|
+
const hasPluginState = Boolean(Object.keys(textFormattingPluginState || {}).length);
|
|
112
|
+
const markSchema = IconsMarkSchema[iconMark];
|
|
113
|
+
const hasSchemaMark = Boolean(schema.marks[markSchema]);
|
|
114
|
+
if (!hasPluginState) {
|
|
115
|
+
return {
|
|
116
|
+
isActive: false,
|
|
117
|
+
isDisabled: true,
|
|
118
|
+
isHidden: false,
|
|
119
|
+
hasSchemaMark
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const isActive = textFormattingPluginState[`${iconMark}Active`];
|
|
123
|
+
const isDisabled = textFormattingPluginState[`${iconMark}Disabled`];
|
|
124
|
+
const isHidden = textFormattingPluginState[`${iconMark}Hidden`];
|
|
125
|
+
return {
|
|
126
|
+
isActive: Boolean(isActive),
|
|
127
|
+
isDisabled: Boolean(isDisabled),
|
|
128
|
+
isHidden: Boolean(isHidden),
|
|
129
|
+
hasSchemaMark
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
const buildIcon = (iconMark, editorAnalyticsAPI) => {
|
|
133
|
+
const getState = buildMenuIconState(iconMark);
|
|
134
|
+
return ({
|
|
135
|
+
schema,
|
|
136
|
+
textFormattingPluginState,
|
|
137
|
+
intl,
|
|
138
|
+
isToolbarDisabled
|
|
139
|
+
}) => {
|
|
140
|
+
const iconState = getState({
|
|
141
|
+
schema,
|
|
142
|
+
textFormattingPluginState
|
|
143
|
+
});
|
|
144
|
+
const {
|
|
145
|
+
isActive,
|
|
146
|
+
isDisabled,
|
|
147
|
+
isHidden,
|
|
148
|
+
hasSchemaMark
|
|
149
|
+
} = iconState;
|
|
150
|
+
const iconComponent = useMemo(() => getIcon({
|
|
151
|
+
iconType: IconTypes[iconMark],
|
|
152
|
+
isDisabled: isToolbarDisabled || isDisabled,
|
|
153
|
+
isActive,
|
|
154
|
+
intl,
|
|
155
|
+
editorAnalyticsAPI
|
|
156
|
+
}), [isToolbarDisabled, isDisabled, isActive, intl]);
|
|
157
|
+
const shouldRenderIcon = hasSchemaMark && !isHidden;
|
|
158
|
+
return useMemo(() => shouldRenderIcon ? iconComponent : null, [shouldRenderIcon, iconComponent]);
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
const useTextFormattingPluginState = editorState => useMemo(() => {
|
|
162
|
+
const pluginState = textFormattingPluginKey.getState(editorState);
|
|
163
|
+
|
|
164
|
+
// TODO: ED-13910 for reasons that goes beyond my knowledge. This is the only way to make the current unit tests happy. Even thought this was wrong before
|
|
165
|
+
return pluginState;
|
|
166
|
+
}, [editorState]);
|
|
167
|
+
export const useFormattingIcons = ({
|
|
168
|
+
isToolbarDisabled,
|
|
169
|
+
editorState,
|
|
170
|
+
intl,
|
|
171
|
+
editorAnalyticsAPI
|
|
172
|
+
}) => {
|
|
173
|
+
const textFormattingPluginState = useTextFormattingPluginState(editorState);
|
|
174
|
+
const {
|
|
175
|
+
schema
|
|
176
|
+
} = editorState;
|
|
177
|
+
const props = {
|
|
178
|
+
schema,
|
|
179
|
+
textFormattingPluginState,
|
|
180
|
+
intl,
|
|
181
|
+
isToolbarDisabled: Boolean(isToolbarDisabled)
|
|
182
|
+
};
|
|
183
|
+
const buildStrongIcon = buildIcon(IconTypes.strong, editorAnalyticsAPI);
|
|
184
|
+
const buildEmIcon = buildIcon(IconTypes.em, editorAnalyticsAPI);
|
|
185
|
+
const buildUnderlineIcon = buildIcon(IconTypes.underline, editorAnalyticsAPI);
|
|
186
|
+
const buildStrikeIcon = buildIcon(IconTypes.strike, editorAnalyticsAPI);
|
|
187
|
+
const buildCodeIcon = buildIcon(IconTypes.code, editorAnalyticsAPI);
|
|
188
|
+
const buildSubscriptIcon = buildIcon(IconTypes.subscript, editorAnalyticsAPI);
|
|
189
|
+
const buildSuperscriptIcon = buildIcon(IconTypes.superscript, editorAnalyticsAPI);
|
|
190
|
+
const strongIcon = buildStrongIcon(props);
|
|
191
|
+
const emIcon = buildEmIcon(props);
|
|
192
|
+
const underlineIcon = buildUnderlineIcon(props);
|
|
193
|
+
const strikeIcon = buildStrikeIcon(props);
|
|
194
|
+
const codeIcon = buildCodeIcon(props);
|
|
195
|
+
const subscriptIcon = buildSubscriptIcon(props);
|
|
196
|
+
const superscriptIcon = buildSuperscriptIcon(props);
|
|
197
|
+
const result = useMemo(() => [strongIcon, emIcon, underlineIcon, strikeIcon, codeIcon, subscriptIcon, superscriptIcon], [strongIcon, emIcon, underlineIcon, strikeIcon, codeIcon, subscriptIcon, superscriptIcon]);
|
|
198
|
+
return result;
|
|
199
|
+
};
|
|
200
|
+
export const useHasFormattingActived = ({
|
|
201
|
+
editorState,
|
|
202
|
+
iconTypeList
|
|
203
|
+
}) => {
|
|
204
|
+
const textFormattingPluginState = useTextFormattingPluginState(editorState);
|
|
205
|
+
const hasActiveFormatting = useMemo(() => {
|
|
206
|
+
if (!textFormattingPluginState) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
return iconTypeList.some(iconType => textFormattingPluginState[`${iconType}Active`]);
|
|
210
|
+
}, [textFormattingPluginState, iconTypeList]);
|
|
211
|
+
return hasActiveFormatting;
|
|
212
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
export const useMenuState = () => {
|
|
3
|
+
const [isMenuOpen, setIsMenuOpened] = useState(false);
|
|
4
|
+
const toggleMenu = useCallback(() => {
|
|
5
|
+
setIsMenuOpened(!isMenuOpen);
|
|
6
|
+
}, [isMenuOpen]);
|
|
7
|
+
const closeMenu = useCallback(() => {
|
|
8
|
+
setIsMenuOpened(false);
|
|
9
|
+
}, []);
|
|
10
|
+
return [isMenuOpen, toggleMenu, closeMenu];
|
|
11
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { DefaultButtonsMenu, DefaultButtonsToolbar, ResponsiveCustomButtonToolbar, ResponsiveCustomMenu } from '../constants';
|
|
3
|
+
export const useResponsiveIconTypeButtons = ({
|
|
4
|
+
toolbarSize,
|
|
5
|
+
responsivenessEnabled
|
|
6
|
+
}) => {
|
|
7
|
+
const iconTypeList = useMemo(() => ResponsiveCustomButtonToolbar[toolbarSize], [toolbarSize]);
|
|
8
|
+
return responsivenessEnabled ? iconTypeList : DefaultButtonsToolbar;
|
|
9
|
+
};
|
|
10
|
+
export const useResponsiveIconTypeMenu = ({
|
|
11
|
+
toolbarSize,
|
|
12
|
+
responsivenessEnabled
|
|
13
|
+
}) => {
|
|
14
|
+
const iconTypeList = useMemo(() => ResponsiveCustomMenu[toolbarSize], [toolbarSize]);
|
|
15
|
+
return responsivenessEnabled ? iconTypeList : DefaultButtonsMenu;
|
|
16
|
+
};
|
|
17
|
+
export const useResponsiveToolbarButtons = ({
|
|
18
|
+
icons,
|
|
19
|
+
toolbarSize,
|
|
20
|
+
responsivenessEnabled
|
|
21
|
+
}) => {
|
|
22
|
+
const iconTypeList = useResponsiveIconTypeButtons({
|
|
23
|
+
toolbarSize,
|
|
24
|
+
responsivenessEnabled
|
|
25
|
+
});
|
|
26
|
+
const iconsPosition = useMemo(() => {
|
|
27
|
+
return icons.reduce((acc, icon) => {
|
|
28
|
+
if (!icon || !icon.iconMark) {
|
|
29
|
+
return acc;
|
|
30
|
+
}
|
|
31
|
+
const isIconSingleButton = iconTypeList.includes(icon.iconMark);
|
|
32
|
+
if (isIconSingleButton) {
|
|
33
|
+
return {
|
|
34
|
+
dropdownItems: acc.dropdownItems,
|
|
35
|
+
singleItems: [...acc.singleItems, icon]
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
dropdownItems: [...acc.dropdownItems, icon],
|
|
40
|
+
singleItems: acc.singleItems
|
|
41
|
+
};
|
|
42
|
+
}, {
|
|
43
|
+
dropdownItems: [],
|
|
44
|
+
singleItems: []
|
|
45
|
+
});
|
|
46
|
+
}, [icons, iconTypeList]);
|
|
47
|
+
return iconsPosition;
|
|
48
|
+
};
|