UNPKG

@atlaskit/editor-plugin-media

Version:

Media plugin for @atlaskit/editor-core

370 lines (364 loc) 13.3 kB
import _extends from "@babel/runtime/helpers/extends"; /* eslint-disable jsdoc/check-tag-names */ /** * @jsxRuntime classic * @jsx jsx */ import { useCallback, useEffect, useMemo, useState, useRef, Fragment } from 'react'; // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports import { jsx, css } from '@emotion/react'; import Button from '@atlaskit/button'; import { IconButton } from '@atlaskit/button/new'; import { pixelEntryMessages as messages } from '@atlaskit/editor-common/media'; import Form, { Field } from '@atlaskit/form'; import CrossIcon from '@atlaskit/icon/core/cross'; // eslint-disable-next-line @atlaskit/design-system/no-emotion-primitives, @atlaskit/design-system/no-emotion-primitives -- to be migrated to @atlaskit/primitives/compiled – go/akcss import { Inline, Box, Text, xcss } from '@atlaskit/primitives'; import Textfield from '@atlaskit/textfield'; import Tooltip from '@atlaskit/tooltip'; import { PIXEL_RESIZING_TOOLBAR_WIDTH, PIXEL_VIEW_MODE_TOOLBAR_WIDTH, PIXELENTRY_MIGRATION_BUTTON_TESTID } from './constants'; import { pixelEntryForm, pixelEntryHiddenSubmit, pixelSizingHeightInput, pixelSizingInput, pixelSizingWidthInput, pixelSizingWrapper } from './styles'; const pixelSizingLabel = xcss({ gridArea: 'label', lineHeight: "var(--ds-space-300, 24px)" }); export const PixelEntryComponent = ({ width, mediaWidth, mediaHeight, onSubmit, minWidth, maxWidth, onChange, intl: { formatMessage }, showMigration, onMigrate, onCloseAndSave, isViewMode, areAnyNewToolbarFlagsEnabled }) => { const ratioWidth = useMemo(() => { return mediaHeight / mediaWidth; }, [mediaHeight, mediaWidth]); const ratioHeight = useMemo(() => { return mediaWidth / mediaHeight; }, [mediaHeight, mediaWidth]); const [computedWidth, setComputedWidth] = useState(width); const [computedHeight, setComputedHeight] = useState(Math.round(ratioWidth * width)); // Handle update to width from outside component useEffect(() => { setComputedWidth(width); setComputedHeight(Math.round(ratioWidth * width)); }, [width, ratioWidth]); // Handle submit when user presses enter in form const handleOnSubmit = data => { if (data.inputWidth === '' || data.inputHeight === '') { return; } if (onSubmit) { let widthToBeSumitted = data.inputWidth; let validation = 'valid'; if (data.inputWidth < minWidth) { widthToBeSumitted = minWidth; validation = 'less-than-min'; } if (data.inputWidth > maxWidth) { widthToBeSumitted = maxWidth; validation = 'greater-than-max'; } // If user keeps submitting an invalid input, node width attribute will be updated with the same value // and won't upadte the state in useEffect (since width is the same) // Thus, we set the state here to always display the correct dimension if (validation !== 'valid') { setComputedWidth(widthToBeSumitted); setComputedHeight(Math.round(ratioWidth * widthToBeSumitted)); } onSubmit({ width: widthToBeSumitted, validation }); } }; // Handle submit when user presses enter or click close button in PixelEntryComponentNext const handleCloseAndSave = useCallback((data, setFocus) => { if (data.inputWidth === '' || data.inputHeight === '') { return; } if (onCloseAndSave) { let widthToBeSubmitted = data.inputWidth; let validation = 'valid'; if (data.inputWidth < minWidth) { widthToBeSubmitted = minWidth; validation = 'less-than-min'; } if (data.inputWidth > maxWidth) { widthToBeSubmitted = maxWidth; validation = 'greater-than-max'; } // If user keeps submitting an invalid input, node width attribute will be updated with the same value // and won't upadte the state in useEffect (since width is the same) // Thus, we set the state here to always display the correct dimension if (validation !== 'valid') { setComputedWidth(widthToBeSubmitted); setComputedHeight(Math.round(ratioWidth * widthToBeSubmitted)); } onCloseAndSave({ width: widthToBeSubmitted, validation }, setFocus); } }, [maxWidth, minWidth, onCloseAndSave, ratioWidth]); // Handle updating computed fields based on const handleOnChange = useCallback(type => event => { const value = parseInt(event.currentTarget.value); const newInputValue = isNaN(value) ? '' : value; let newWidth = ''; switch (type) { case 'inputWidth': { newWidth = newInputValue; setComputedWidth(newInputValue); const newHeight = newInputValue !== '' ? Math.round(ratioWidth * newInputValue) : ''; setComputedHeight(newHeight); break; } case 'inputHeight': { setComputedHeight(newInputValue); newWidth = newInputValue !== '' ? Math.round(ratioHeight * newInputValue) : ''; setComputedWidth(newWidth); break; } } const isInvalidInputValid = newWidth !== '' && (newWidth < minWidth || newWidth > maxWidth); onChange && onChange(isInvalidInputValid); }, [minWidth, maxWidth, onChange, ratioWidth, ratioHeight]); if (showMigration) { return jsx(Tooltip, { content: formatMessage(messages.migrationButtonTooltip) }, jsx(Button, { appearance: "warning", spacing: "compact", onClick: onMigrate, testId: PIXELENTRY_MIGRATION_BUTTON_TESTID }, formatMessage(messages.migrationButtonText))); } if (areAnyNewToolbarFlagsEnabled) { return jsx(PixelEntryComponentNext, { maxWidth: maxWidth, formatMessage: formatMessage, handleFieldChange: handleOnChange, computedWidth: computedWidth, computedHeight: computedHeight, handleCloseAndSave: handleCloseAndSave, isViewMode: isViewMode }); } return ( // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/design-system/consistent-css-prop-usage -- Ignored via go/DSP-18766 jsx("div", { css: pixelEntryForm }, jsx(Form, { onSubmit: handleOnSubmit }, ({ formProps }) => { return ( // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading jsx("form", formProps, jsx("div", { css: pixelSizingWrapper }, jsx(Field, { key: "inputWidth", name: "inputWidth", defaultValue: computedWidth }, ({ fieldProps }) => jsx(Tooltip, { hideTooltipOnMouseDown: true, content: formatMessage(messages.inputWidthTooltip, { maxWidth }), position: "top" }, jsx(Textfield // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading , _extends({}, fieldProps, { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/design-system/no-unsafe-style-overrides, @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) css: [pixelSizingWidthInput, pixelSizingInput], appearance: "none", isCompact: true, onChange: handleOnChange('inputWidth'), pattern: "\\d*", "aria-label": formatMessage(messages.inputWidthAriaLabel, { maxWidth }) })))), jsx(Box, { as: "span", xcss: pixelSizingLabel }, "\xD7"), jsx(Field, { key: "inputHeight", name: "inputHeight", defaultValue: computedHeight }, ({ fieldProps }) => jsx(Tooltip, { hideTooltipOnMouseDown: true, content: formatMessage(messages.inputHeightTooltip), position: "top" }, jsx(Textfield // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading , _extends({}, fieldProps, { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/design-system/no-unsafe-style-overrides, @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) css: [pixelSizingHeightInput, pixelSizingInput], appearance: "none", isCompact: true, onChange: handleOnChange('inputHeight'), pattern: "\\d*", "aria-label": formatMessage(messages.inputHeightAriaLabel) })))), jsx(Button, { css: pixelEntryHiddenSubmit, type: "submit" }, formatMessage(messages.submitButtonText)))) ); })) ); }; const pixelEntryWrapperStyles = xcss({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values width: `${PIXEL_RESIZING_TOOLBAR_WIDTH}px` }); const pixelEntryWrapperViewModeStyles = xcss({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values width: `${PIXEL_VIEW_MODE_TOOLBAR_WIDTH}px` }); const fieldStyles = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors '[data-ds--text-field--input]': { paddingBlock: "var(--ds-space-0, 0px)", paddingInline: "var(--ds-space-100, 8px)" }, flex: '1' }); const dividerStyles = xcss({ width: '1px', height: "var(--ds-space-500, 40px)", background: "var(--ds-border, #0B120E24)", marginInlineEnd: 'space.050' }); export const PixelEntryComponentNext = ({ maxWidth, formatMessage, handleFieldChange, computedWidth, computedHeight, handleCloseAndSave, isViewMode }) => { const widthInputRef = useRef(null); useEffect(() => { if (widthInputRef.current) { if (isViewMode) { widthInputRef.current.blur(); } else { widthInputRef.current.focus(); } } }, [isViewMode, widthInputRef]); const handleKeyDown = useCallback(event => { if (event.key === 'Enter') { const shouldSetFocus = true; handleCloseAndSave({ inputWidth: computedWidth, inputHeight: computedHeight }, shouldSetFocus); } }, [computedWidth, computedHeight, handleCloseAndSave]); const handleCloseButtonKeyDown = useCallback(event => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); const shouldSetFocus = true; handleCloseAndSave({ inputWidth: computedWidth, inputHeight: computedHeight }, shouldSetFocus); } }, [computedWidth, computedHeight, handleCloseAndSave]); return ( // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) jsx(Box, { xcss: [pixelEntryWrapperStyles, isViewMode && pixelEntryWrapperViewModeStyles] }, jsx(Inline, { alignBlock: "center", spread: "space-between" }, jsx(Box, { paddingInlineStart: "space.100" }, jsx(Text, { color: "color.text.subtlest" }, formatMessage(messages.inputWidthLabel))), jsx("div", { css: fieldStyles }, jsx(Tooltip, { hideTooltipOnMouseDown: true, hideTooltipOnClick: true, position: "top", content: formatMessage(messages.inputWidthTooltip, { maxWidth }) }, jsx(Textfield, { name: "inputWidth", value: computedWidth, ref: widthInputRef, height: "var(--ds-space-250, 20px)", appearance: "none", pattern: "\\d*", "aria-label": formatMessage(messages.inputWidthAriaLabel, { maxWidth }), onChange: handleFieldChange('inputWidth'), onKeyDown: handleKeyDown }))), jsx(Box, { paddingInlineStart: "space.100" }, jsx(Text, { color: "color.text.subtlest" }, formatMessage(messages.inputHeightTooltip))), jsx("div", { css: fieldStyles }, jsx(Tooltip, { hideTooltipOnMouseDown: true, hideTooltipOnClick: true, content: formatMessage(messages.inputHeightTooltip), position: "top" }, jsx(Textfield, { name: "inputHeight", value: computedHeight, height: "var(--ds-space-250, 20px)", appearance: "none", pattern: "\\d*", "aria-label": formatMessage(messages.inputHeightAriaLabel), onChange: handleFieldChange('inputHeight'), onKeyDown: handleKeyDown }))), !isViewMode && jsx(Fragment, null, jsx(Box, { xcss: dividerStyles }), jsx(IconButton // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , { icon: () => jsx(CrossIcon, { label: "", color: "var(--ds-icon-subtlest, #6B6E76)" }), label: formatMessage(messages.closePixelEntry), appearance: "subtle" // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , onClick: () => { handleCloseAndSave({ inputWidth: computedWidth, inputHeight: computedHeight }); }, onKeyDown: handleCloseButtonKeyDown })))) ); };