@atlaskit/editor-plugin-media
Version:
Media plugin for @atlaskit/editor-core
213 lines (209 loc) • 11.6 kB
JavaScript
import { activateVideoControls, bindKeymapWithCommand, decreaseMediaSize, enter, increaseMediaSize, insertNewLine, moveDown, moveLeft, moveRight, tab, undo } from '@atlaskit/editor-common/keymaps';
import { mediaResizeAnnouncerMessMessages as mediaResizeAnnouncerMess } from '@atlaskit/editor-common/media';
import { calcMediaSingleMaxWidth, MEDIA_SINGLE_DEFAULT_MIN_PIXEL_WIDTH } from '@atlaskit/editor-common/media-single';
import { GapCursorSelection, Side } from '@atlaskit/editor-common/selection';
import { keymap } from '@atlaskit/editor-prosemirror/keymap';
import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
import { akEditorDefaultLayoutWidth } from '@atlaskit/editor-shared-styles';
import { insertAndSelectCaptionFromMediaSinglePos, selectCaptionFromMediaSinglePos } from '../pm-plugins/commands/captions';
import { stateKey } from '../pm-plugins/plugin-key';
import { updateMediaSingleWidth } from '../ui/toolbar/commands';
import { calcNewLayout, getSelectedMediaSingle } from '../ui/toolbar/utils';
function keymapPlugin(options, editorAnalyticsAPI, editorSelectionAPI, widthPlugin, getIntl) {
const list = {};
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
bindKeymapWithCommand(undo.common, ignoreLinksInSteps, list);
if (options !== null && options !== void 0 && options.allowCaptions) {
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
bindKeymapWithCommand(moveDown.common, insertAndSelectCaption(editorAnalyticsAPI), list);
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
bindKeymapWithCommand(tab.common, insertAndSelectCaption(editorAnalyticsAPI), list);
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
bindKeymapWithCommand(moveLeft.common, arrowLeftFromMediaSingle(editorSelectionAPI), list);
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
bindKeymapWithCommand(moveRight.common, arrowRightFromMediaSingle(editorSelectionAPI), list);
}
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
bindKeymapWithCommand(insertNewLine.common, splitMediaGroup, list);
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
bindKeymapWithCommand(enter.common, splitMediaGroup, list);
if (options !== null && options !== void 0 && options.allowPixelResizing) {
bindKeymapWithCommand(
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
increaseMediaSize.common, handleMediaIncrease(editorAnalyticsAPI, widthPlugin, options, getIntl), list);
bindKeymapWithCommand(
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
decreaseMediaSize.common, handleMediaDecrease(editorAnalyticsAPI, widthPlugin, options, getIntl), list);
}
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
bindKeymapWithCommand(activateVideoControls.common, focusPlayButton, list);
return keymap(list);
}
const ignoreLinksInSteps = state => {
const mediaPluginState = stateKey.getState(state);
mediaPluginState.ignoreLinks = true;
return false;
};
const splitMediaGroup = state => {
const mediaPluginState = stateKey.getState(state);
return mediaPluginState.splitMediaGroup();
};
const focusPlayButton = state => {
var _stateKey$getState;
const videoControlsWrapperRef = (_stateKey$getState = stateKey.getState(state)) === null || _stateKey$getState === void 0 ? void 0 : _stateKey$getState.element;
if (videoControlsWrapperRef) {
const firstButton = videoControlsWrapperRef === null || videoControlsWrapperRef === void 0 ? void 0 : videoControlsWrapperRef.querySelector('button, [tabindex]:not([tabindex="-1"])');
firstButton === null || firstButton === void 0 ? void 0 : firstButton.focus();
}
return true;
};
const validationMaxMin = (newWidth, maxWidth, minWidth, validation) => {
let newWidthValidated;
if (newWidth > maxWidth) {
newWidthValidated = maxWidth;
validation = 'greater-than-max';
} else if (newWidth < minWidth) {
newWidthValidated = minWidth;
validation = 'less-than-min';
} else {
newWidthValidated = newWidth;
validation = 'valid';
}
return {
newWidthValidated,
validation
};
};
const createAnnouncer = (action, mediaWidth, changeAmount, validation, getIntl) => {
const announcerContainer = document.getElementById('media-announcer') || document.createElement('div');
const intl = getIntl();
if (!announcerContainer.id) {
announcerContainer.id = 'media-announcer';
announcerContainer.setAttribute('role', 'status');
announcerContainer.setAttribute('aria-live', 'polite');
announcerContainer.setAttribute('aria-atomic', 'true');
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const style = announcerContainer.style;
style.position = 'absolute';
style.width = '1px';
style.height = '1px';
style.marginTop = '-1px';
style.opacity = '0';
style.overflow = 'hidden';
document.body.appendChild(announcerContainer);
} else {
const newMediaWidth = mediaWidth + changeAmount;
if (validation === 'greater-than-max') {
announcerContainer.textContent = intl.formatMessage(mediaResizeAnnouncerMess.MediaWidthIsMax);
} else if (validation === 'less-than-min') {
announcerContainer.textContent = intl.formatMessage(mediaResizeAnnouncerMess.MediaWidthIsMin);
} else {
announcerContainer.textContent = intl.formatMessage(action === 'increased' ? mediaResizeAnnouncerMess.DefaultMediaWidthIncreased : mediaResizeAnnouncerMess.DefaultMediaWidthDecreased, {
newMediaWidth
});
}
}
};
const handleMediaSizeChange = (editorAnalyticsAPI, widthPlugin, options, changeAmount, action, getIntl) => (state, dispatch) => {
var _getSelectedMediaSing, _getSelectedMediaSing2, _getSelectedMediaSing3, _widthPlugin$sharedSt, _widthPlugin$sharedSt2, _getSelectedMediaSing4, _getSelectedMediaSing5, _getSelectedMediaSing6;
const {
selection
} = state;
if (!(selection instanceof NodeSelection && selection.node.type.name === 'mediaSingle')) {
return false;
}
const mediaWidth = (_getSelectedMediaSing = getSelectedMediaSingle(state)) === null || _getSelectedMediaSing === void 0 ? void 0 : (_getSelectedMediaSing2 = _getSelectedMediaSing.node) === null || _getSelectedMediaSing2 === void 0 ? void 0 : (_getSelectedMediaSing3 = _getSelectedMediaSing2.attrs) === null || _getSelectedMediaSing3 === void 0 ? void 0 : _getSelectedMediaSing3.width;
const contentWidth = (widthPlugin === null || widthPlugin === void 0 ? void 0 : (_widthPlugin$sharedSt = widthPlugin.sharedState.currentState()) === null || _widthPlugin$sharedSt === void 0 ? void 0 : _widthPlugin$sharedSt.lineLength) || akEditorDefaultLayoutWidth;
const mediaPluginState = stateKey.getState(state);
const maxWidthForNestedNode = mediaPluginState.currentMaxWidth;
const minWidth = MEDIA_SINGLE_DEFAULT_MIN_PIXEL_WIDTH;
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let maxWidth = maxWidthForNestedNode;
const currentMaxWidth = (widthPlugin === null || widthPlugin === void 0 ? void 0 : (_widthPlugin$sharedSt2 = widthPlugin.sharedState.currentState()) === null || _widthPlugin$sharedSt2 === void 0 ? void 0 : _widthPlugin$sharedSt2.width) || maxWidth;
if (maxWidth === undefined && maxWidthForNestedNode === undefined) {
var _widthPlugin$sharedSt3;
maxWidth = options !== null && options !== void 0 && options.fullWidthEnabled ? widthPlugin === null || widthPlugin === void 0 ? void 0 : (_widthPlugin$sharedSt3 = widthPlugin.sharedState.currentState()) === null || _widthPlugin$sharedSt3 === void 0 ? void 0 : _widthPlugin$sharedSt3.lineLength : calcMediaSingleMaxWidth(currentMaxWidth, options === null || options === void 0 ? void 0 : options.editorAppearance);
}
const validation = 'valid';
const newWidth = mediaWidth + changeAmount;
if (options !== null && options !== void 0 && options.fullWidthEnabled) {
var _widthPlugin$sharedSt4;
maxWidth = widthPlugin === null || widthPlugin === void 0 ? void 0 : (_widthPlugin$sharedSt4 = widthPlugin.sharedState.currentState()) === null || _widthPlugin$sharedSt4 === void 0 ? void 0 : _widthPlugin$sharedSt4.lineLength;
} else if (maxWidthForNestedNode === undefined) {
maxWidth = calcMediaSingleMaxWidth(currentMaxWidth, options === null || options === void 0 ? void 0 : options.editorAppearance);
}
const {
newWidthValidated,
validation: validationResult
} = validationMaxMin(newWidth, maxWidth, minWidth, validation);
const newLayout = calcNewLayout(newWidthValidated, (_getSelectedMediaSing4 = getSelectedMediaSingle(state)) === null || _getSelectedMediaSing4 === void 0 ? void 0 : (_getSelectedMediaSing5 = _getSelectedMediaSing4.node) === null || _getSelectedMediaSing5 === void 0 ? void 0 : (_getSelectedMediaSing6 = _getSelectedMediaSing5.attrs) === null || _getSelectedMediaSing6 === void 0 ? void 0 : _getSelectedMediaSing6.layout, contentWidth, options === null || options === void 0 ? void 0 : options.fullWidthEnabled);
updateMediaSingleWidth(editorAnalyticsAPI)(newWidthValidated, validationResult, 'keyboard', newLayout)(state, dispatch);
createAnnouncer(action, mediaWidth, changeAmount, validationResult, getIntl);
return true;
};
const handleMediaIncrease = (editorAnalyticsAPI, widthPlugin, options, getIntl) => handleMediaSizeChange(editorAnalyticsAPI, widthPlugin, options, 1, 'increased', getIntl);
const handleMediaDecrease = (editorAnalyticsAPI, widthPlugin, options, getIntl) => handleMediaSizeChange(editorAnalyticsAPI, widthPlugin, options, -1, 'decreased', getIntl);
const insertAndSelectCaption = editorAnalyticsAPI => (state, dispatch) => {
const {
selection,
schema
} = state;
if (selection instanceof NodeSelection && selection.node.type === schema.nodes.mediaSingle && schema.nodes.caption) {
if (dispatch) {
const {
from,
node
} = selection;
if (!insertAndSelectCaptionFromMediaSinglePos(editorAnalyticsAPI)(from, node)(state, dispatch)) {
selectCaptionFromMediaSinglePos(from, node)(state, dispatch);
}
}
return true;
}
return false;
};
const arrowLeftFromMediaSingle = editorSelectionAPI => (state, dispatch) => {
const {
selection
} = state;
if (editorSelectionAPI && selection instanceof NodeSelection && selection.node.type.name === 'mediaSingle') {
const tr = editorSelectionAPI.selectNearNode({
selectionRelativeToNode: undefined,
selection: new GapCursorSelection(state.doc.resolve(selection.from), Side.LEFT)
})(state);
if (dispatch) {
dispatch(tr);
}
return true;
}
return false;
};
const arrowRightFromMediaSingle = editorSelectionAPI => (state, dispatch) => {
const {
selection
} = state;
if (editorSelectionAPI && selection instanceof NodeSelection && selection.node.type.name === 'mediaSingle') {
const tr = editorSelectionAPI.selectNearNode({
selectionRelativeToNode: undefined,
selection: new GapCursorSelection(state.doc.resolve(selection.to), Side.RIGHT)
})(state);
if (dispatch) {
dispatch(tr);
}
return true;
}
return false;
};
export default keymapPlugin;