@atlaskit/editor-plugin-date
Version:
Date plugin for @atlaskit/editor-core
303 lines (301 loc) • 13 kB
JavaScript
import React from 'react';
import Loadable from 'react-loadable';
import { date, dateWithLocalId } from '@atlaskit/adf-schema';
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
import { ToolTipContent } from '@atlaskit/editor-common/keymaps';
import { annotationMessages, toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
import { IconDate } from '@atlaskit/editor-common/quick-insert';
import { DateSharedCssClassName } from '@atlaskit/editor-common/styles';
import { calculateToolbarPositionAboveSelection } from '@atlaskit/editor-common/utils';
import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
import CommentIcon from '@atlaskit/icon/core/comment';
import { fg } from '@atlaskit/platform-feature-flags';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { closeDatePicker, closeDatePickerWithAnalytics, createDate } from './pm-plugins/actions';
import { deleteDateCommand, insertDateCommand } from './pm-plugins/commands';
import keymap from './pm-plugins/keymap';
import createDatePlugin from './pm-plugins/main';
import { pluginKey as datePluginKey } from './pm-plugins/plugin-key';
const DatePicker = Loadable({
loader: () => import( /* webpackChunkName: "@atlaskit-internal_editor-datepicker" */'./ui/DatePicker').then(mod => mod.default),
loading: () => null
});
function getDateNode(editorView, showDatePickerAt) {
if (!editorView) {
return null;
}
const domAtPos = editorView.domAtPos.bind(editorView);
// Ignored via go/ees005
// eslint-disable-next-line @atlaskit/editor/no-as-casting
const element = findDomRefAtPos(showDatePickerAt, domAtPos);
// Resolves ED-23702 for when the date is wrapped in an inline comment
const dateNode = element !== null && element !== void 0 && element.classList.contains(DateSharedCssClassName.DATE_CONTAINER) ? element : element === null || element === void 0 ? void 0 : element.querySelector(`.${DateSharedCssClassName.DATE_CONTAINER}`);
return dateNode || element;
}
function ContentComponent({
editorView,
dispatchAnalyticsEvent,
popupsMountPoint,
popupsBoundariesElement,
popupsScrollableElement,
dependencyApi,
weekStartDay
}) {
const {
editorDisabledState,
dateState
} = useSharedPluginState(dependencyApi, ['date', 'editorDisabled']);
if (!(dateState !== null && dateState !== void 0 && dateState.showDatePickerAt) || editorDisabledState !== null && editorDisabledState !== void 0 && editorDisabledState.editorDisabled) {
return null;
}
const {
showDatePickerAt,
isNew,
focusDateInput
} = dateState;
const dateNode = getDateNode(editorView, showDatePickerAt);
return /*#__PURE__*/React.createElement(DatePicker, {
mountTo: popupsMountPoint,
boundariesElement: popupsBoundariesElement,
scrollableElement: popupsScrollableElement,
key: showDatePickerAt,
element: dateNode,
isNew: isNew,
autoFocus: focusDateInput
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
,
onDelete: () => {
var _dependencyApi$core;
if (expValEquals('platform_editor_hydratable_ui', 'isEnabled', true) && !editorView) {
return;
}
dependencyApi === null || dependencyApi === void 0 ? void 0 : (_dependencyApi$core = dependencyApi.core) === null || _dependencyApi$core === void 0 ? void 0 : _dependencyApi$core.actions.execute(deleteDateCommand(dependencyApi));
editorView === null || editorView === void 0 ? void 0 : editorView.focus();
}
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
,
onSelect: (date, commitMethod) => {
var _dependencyApi$core2;
// Undefined means couldn't parse date, null means invalid (out of bounds) date
if (date === undefined || date === null) {
return;
}
if (expValEquals('platform_editor_hydratable_ui', 'isEnabled', true) && !editorView) {
return;
}
dependencyApi === null || dependencyApi === void 0 ? void 0 : (_dependencyApi$core2 = dependencyApi.core) === null || _dependencyApi$core2 === void 0 ? void 0 : _dependencyApi$core2.actions.execute(insertDateCommand(dependencyApi)({
date,
commitMethod
}));
editorView === null || editorView === void 0 ? void 0 : editorView.focus();
}
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
,
onTextChanged: date => {
var _dependencyApi$core3;
dependencyApi === null || dependencyApi === void 0 ? void 0 : (_dependencyApi$core3 = dependencyApi.core) === null || _dependencyApi$core3 === void 0 ? void 0 : _dependencyApi$core3.actions.execute(insertDateCommand(dependencyApi)({
date,
enterPressed: false
}));
}
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
,
closeDatePicker: () => {
if (!editorView) {
return;
}
closeDatePicker()(editorView.state, editorView.dispatch);
editorView === null || editorView === void 0 ? void 0 : editorView.focus();
}
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
,
closeDatePickerWithAnalytics: ({
date
}) => {
if (!editorView) {
return;
}
closeDatePickerWithAnalytics({
date
})(editorView.state, editorView.dispatch);
editorView.focus();
},
dispatchAnalyticsEvent: dispatchAnalyticsEvent,
weekStartDay: weekStartDay
});
}
const datePlugin = ({
config = {},
api
}) => ({
name: 'date',
getSharedState(editorState) {
if (!editorState) {
return {
showDatePickerAt: null,
isNew: false,
focusDateInput: false,
isInitialised: true
};
}
const {
showDatePickerAt,
isNew,
focusDateInput,
isInitialised
} = datePluginKey.getState(editorState) || {};
return {
showDatePickerAt,
isNew: !!isNew,
focusDateInput: !!focusDateInput,
isInitialised: !!isInitialised
};
},
commands: {
insertDate: insertDateCommand(api),
deleteDate: deleteDateCommand(api)
},
nodes() {
return [{
name: 'date',
node: fg('platform_editor_adf_with_localid') ? dateWithLocalId : date
}];
},
pmPlugins() {
return [{
name: 'date',
plugin: pmPluginFactoryParams => {
DatePicker.preload();
return createDatePlugin(pmPluginFactoryParams);
}
}, {
name: 'dateKeymap',
plugin: () => {
DatePicker.preload();
return keymap();
}
}];
},
contentComponent({
editorView,
dispatchAnalyticsEvent,
popupsMountPoint,
popupsBoundariesElement,
popupsScrollableElement
}) {
if (!editorView) {
return null;
}
return /*#__PURE__*/React.createElement(ContentComponent, {
dependencyApi: api,
editorView: editorView,
dispatchAnalyticsEvent: dispatchAnalyticsEvent,
popupsMountPoint: popupsMountPoint,
popupsBoundariesElement: popupsBoundariesElement,
popupsScrollableElement: popupsScrollableElement,
weekStartDay: config.weekStartDay
});
},
pluginsOptions: {
quickInsert: ({
formatMessage
}) => [{
id: 'date',
title: formatMessage(messages.date),
description: formatMessage(messages.dateDescription),
priority: 800,
keywords: ['calendar', 'day', 'time', 'today', '/'],
keyshortcut: '//',
icon: () => /*#__PURE__*/React.createElement(IconDate, null),
action(insert, state, source) {
var _api$analytics, _api$analytics$action, _api$analytics$action2;
const tr = createDate(true)(state);
api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : (_api$analytics$action = _api$analytics.actions) === null || _api$analytics$action === void 0 ? void 0 : (_api$analytics$action2 = _api$analytics$action.attachAnalyticsEvent) === null || _api$analytics$action2 === void 0 ? void 0 : _api$analytics$action2.call(_api$analytics$action, {
action: ACTION.INSERTED,
actionSubject: ACTION_SUBJECT.DOCUMENT,
actionSubjectId: ACTION_SUBJECT_ID.DATE,
eventType: EVENT_TYPE.TRACK,
attributes: {
inputMethod: source !== null && source !== void 0 ? source : INPUT_METHOD.QUICK_INSERT
}
})(tr);
return tr;
}
}],
floatingToolbar: (state, intl) => {
const isViewMode = () => {
var _api$editorViewMode, _api$editorViewMode$s;
return (api === null || api === void 0 ? void 0 : (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : (_api$editorViewMode$s = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode$s === void 0 ? void 0 : _api$editorViewMode$s.mode) === 'view';
};
if (!isViewMode()) {
return undefined;
}
const onClick = (stateFromClickEvent, dispatch) => {
var _api$analytics2, _api$annotation, _api$annotation$actio;
if (!(api !== null && api !== void 0 && api.annotation)) {
return true;
}
if (api !== null && api !== void 0 && (_api$analytics2 = api.analytics) !== null && _api$analytics2 !== void 0 && _api$analytics2.actions) {
var _api$analytics3, _api$analytics3$actio;
api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : (_api$analytics3$actio = _api$analytics3.actions) === null || _api$analytics3$actio === void 0 ? void 0 : _api$analytics3$actio.fireAnalyticsEvent({
action: ACTION.CLICKED,
actionSubject: ACTION_SUBJECT.BUTTON,
actionSubjectId: ACTION_SUBJECT_ID.CREATE_INLINE_COMMENT_FROM_HIGHLIGHT_ACTIONS_MENU,
eventType: EVENT_TYPE.UI,
attributes: {
source: 'highlightActionsMenu',
pageMode: 'edit',
sourceNode: 'date'
}
});
}
const command = (_api$annotation = api.annotation) === null || _api$annotation === void 0 ? void 0 : (_api$annotation$actio = _api$annotation.actions) === null || _api$annotation$actio === void 0 ? void 0 : _api$annotation$actio.setInlineCommentDraftState(true, INPUT_METHOD.TOOLBAR);
return command(stateFromClickEvent, dispatch);
};
return {
title: 'Date floating toolbar',
nodeType: [state.schema.nodes.date],
getDomRef: editorView => {
const dateState = datePluginKey.getState(state);
const datePosition = dateState === null || dateState === void 0 ? void 0 : dateState.showDatePickerAt;
if (!datePosition) {
return undefined;
}
const domAtPos = editorView.domAtPos.bind(editorView);
const domRef = findDomRefAtPos(datePosition, domAtPos);
const isHTMLElement = element => {
return element instanceof HTMLElement;
};
if (isHTMLElement(domRef)) {
return domRef;
}
return undefined;
},
onPositionCalculated: calculateToolbarPositionAboveSelection('Date floating toolbar'),
items: node => {
var _api$annotation2;
const annotationState = api === null || api === void 0 ? void 0 : (_api$annotation2 = api.annotation) === null || _api$annotation2 === void 0 ? void 0 : _api$annotation2.sharedState.currentState();
const activeCommentMark = node.marks.find(mark => mark.type.name === 'annotation' && (annotationState === null || annotationState === void 0 ? void 0 : annotationState.annotations[mark.attrs.id]) === false);
const showAnnotation = annotationState && annotationState.isVisible && isViewMode() && !annotationState.bookmark && !annotationState.mouseData.isSelecting && !activeCommentMark;
if (showAnnotation) {
return [{
type: 'button',
showTitle: true,
testId: 'add-comment-date-button',
icon: CommentIcon,
title: intl.formatMessage(annotationMessages.createComment),
onClick,
tooltipContent: /*#__PURE__*/React.createElement(ToolTipContent, {
description: intl.formatMessage(annotationMessages.createComment)
}),
supportsViewMode: true
}];
}
return [];
}
};
}
}
});
export default datePlugin;