import React from 'react';
import { KeyboardEvent } from 'react';
import { EditorView } from 'prosemirror-view';
import styled from 'styled-components';
import { N100, N30, R400, N80 } from '@atlaskit/theme/colors';
import ChevronLeftLargeIcon from '@atlaskit/icon/glyph/chevron-left-large';
import CrossCircleIcon from '@atlaskit/icon/glyph/cross-circle';
import { messages } from '../messages';
import { injectIntl, InjectedIntlProps } from 'react-intl';
import Button from '../../../../floating-toolbar/ui/Button';
import PanelTextInput from '../../../../../ui/PanelTextInput';
import * as keymaps from '../../../../../keymaps';
import { ToolTipContent } from '../../../../../keymaps';
import { closeMediaAltTextMenu, updateAltText } from '../commands';
import {
withAnalyticsEvents,
WithAnalyticsEventsProps,
} from '@atlaskit/analytics-next';
import {
ACTION_SUBJECT,
ACTION_SUBJECT_ID,
EVENT_TYPE,
fireAnalyticsEvent,
ACTION,
MediaAltTextActionType,
FireAnalyticsCallback,
} from '../../../../analytics';
import { RECENT_SEARCH_WIDTH_IN_PX } from '../../../../../ui/LinkSearch/ToolbarComponents';
import { relativeFontSizeToBase16 } from '@atlaskit/editor-shared-styles';
import { ErrorMessage } from '@atlaskit/editor-common';
export const CONTAINER_WIDTH_IN_PX = RECENT_SEARCH_WIDTH_IN_PX;
export const MAX_ALT_TEXT_LENGTH = 510; // double tweet length
const SupportText = styled.p`
color: ${N100};
font-size: ${relativeFontSizeToBase16(12)};
padding: 12px 40px;
line-height: 20px;
border-top: 1px solid ${N30};
margin: 0;
`;
const Container = styled.div`
width: ${CONTAINER_WIDTH_IN_PX}px;
display: flex;
flex-direction: column;
overflow: auto;
line-height: 2;
`;
const InputWrapper = styled.section`
display: flex;
line-height: 0;
padding: 5px 0;
align-items: center;
`;
const ValidationWrapper = styled.section`
display: flex;
line-height: 0;
padding: 12px 24px 12px 0;
margin: 0 12px 0 40px;
border-top: 1px solid ${R400};
align-items: start;
display: flex;
flex-direction: column;
`;
const ButtonWrapper = styled.div`
display: flex;
padding: 4px 8px;
`;
const ClearText = styled.span`
color: ${N80};
`;
type Props = {
view: EditorView;
value?: string;
altTextValidator?: (value: string) => string[];
} & InjectedIntlProps &
WithAnalyticsEventsProps;
export type AltTextEditComponentState = {
showClearTextButton: boolean;
validationErrors: string[] | undefined;
lastValue: string | undefined;
};
export class AltTextEditComponent extends React.Component<
Props,
AltTextEditComponentState
> {
private fireCustomAnalytics?: FireAnalyticsCallback;
state = {
showClearTextButton: Boolean(this.props.value),
validationErrors: this.props.value
? this.getValidationErrors(this.props.value)
: [],
lastValue: this.props.value,
};
constructor(props: Props) {
super(props);
const { createAnalyticsEvent } = props;
this.fireCustomAnalytics = fireAnalyticsEvent(createAnalyticsEvent);
}
prevValue: string | undefined;
componentDidMount() {
this.prevValue = this.props.value;
}
componentWillUnmount() {
this.fireAnalytics(ACTION.CLOSED);
if (!this.prevValue && this.props.value) {
this.fireAnalytics(ACTION.ADDED);
}
if (this.prevValue && !this.props.value) {
this.fireAnalytics(ACTION.CLEARED);
}
if (this.prevValue && this.prevValue !== this.props.value) {
this.fireAnalytics(ACTION.EDITED);
}
}
private getValidationErrors(value: string): string[] {
const { altTextValidator } = this.props;
if (value && typeof altTextValidator === 'function') {
return altTextValidator(value) || [];
}
return [];
}
render() {
const {
intl: { formatMessage },
} = this.props;
const { showClearTextButton } = this.state;
const backButtonMessage = formatMessage(messages.back);
const backButtonMessageComponent = (
);
const errorsList = (this.state.validationErrors || []).map(function (
error,
index,
) {
return {error};
});
return (
}
tooltipContent={backButtonMessageComponent}
onClick={this.closeMediaAltTextMenu}
/>
{showClearTextButton && (
)}
{!!errorsList.length && (
{errorsList}
)}
{formatMessage(messages.supportText)}
);
}
private closeMediaAltTextMenu = () => {
const { view } = this.props;
closeMediaAltTextMenu(view.state, view.dispatch);
};
private fireAnalytics(actionType: MediaAltTextActionType) {
const { createAnalyticsEvent } = this.props;
if (createAnalyticsEvent && this.fireCustomAnalytics) {
this.fireCustomAnalytics({
payload: {
action: actionType,
actionSubject: ACTION_SUBJECT.MEDIA,
actionSubjectId: ACTION_SUBJECT_ID.ALT_TEXT,
eventType: EVENT_TYPE.TRACK,
},
});
}
}
private dispatchCancelEvent = (event: KeyboardEvent) => {
const { view } = this.props;
// We need to pass down the ESCAPE keymap
// because when we focus on the Toolbar, Prosemirror blur,
// making all keyboard shortcuts not working
view.someProp('handleKeyDown', (fn: any) => fn(view, event));
};
private updateAltText = (newAltText: string) => {
const { view } = this.props;
const newValue = newAltText.length === 0 ? null : newAltText;
updateAltText(newValue)(view.state, view.dispatch);
};
private handleOnChange = (newAltText: string) => {
const validationErrors = this.getValidationErrors(newAltText);
this.setState(
{
showClearTextButton: Boolean(newAltText),
validationErrors,
lastValue: newAltText,
},
() => {
if (!validationErrors || !validationErrors.length) {
this.updateAltText(newAltText);
}
},
);
};
private handleOnBlur = () => {
// Handling the trimming onBlur() because PanelTextInput doesn't sync
// defaultValue properly during unmount
const { value } = this.props;
const newValue = (this.state.lastValue || value || '').trim();
this.handleOnChange(newValue);
};
private handleClearText = () => {
this.handleOnChange('');
};
}
export default withAnalyticsEvents()(injectIntl(AltTextEditComponent));