UNPKG

@atlaskit/editor-plugin-synced-block

Version:

SyncedBlock plugin for @atlaskit/editor-core

345 lines (343 loc) 13.4 kB
/* SyncedLocationDropdown.tsx generated by @compiled/babel-plugin v0.39.1 */ import _extends from "@babel/runtime/helpers/extends"; import "./SyncedLocationDropdown.compiled.css"; import * as React from 'react'; import { ax, ix } from "@compiled/react/runtime"; import { useState, useEffect } from 'react'; import { cx } from '@compiled/react'; import DropdownMenu, { DropdownItem, DropdownItemGroup } from '@atlaskit/dropdown-menu'; import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics'; import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages'; import { SYNCED_BLOCKS_DOCUMENTATION_URL } from '@atlaskit/editor-common/sync-block'; import { FloatingToolbarButton as Button } from '@atlaskit/editor-common/ui'; import { getPageIdAndTypeFromConfluencePageAri } from '@atlaskit/editor-synced-block-provider'; import { IconTile } from '@atlaskit/icon'; import PageLiveDocIcon from '@atlaskit/icon-lab/core/page-live-doc'; import ChevronDownIcon from '@atlaskit/icon/core/chevron-down'; import PageIcon from '@atlaskit/icon/core/page'; import QuotationMarkIcon from '@atlaskit/icon/core/quotation-mark'; import StatusErrorIcon from '@atlaskit/icon/core/status-error'; import { ConfluenceIcon, JiraIcon, AtlassianIcon } from '@atlaskit/logo'; import Lozenge from '@atlaskit/lozenge'; import { fg } from '@atlaskit/platform-feature-flags'; import { Box, Text, Inline, Anchor, Stack } from '@atlaskit/primitives/compiled'; import Spinner from '@atlaskit/spinner'; import Tooltip from '@atlaskit/tooltip'; const fadeIn = null; const headingStyles = null; const dropdownItemStyles = null; // logo icon does not fit in ADS IconTile, hence we need custom styles to match with other icons const logoTileStyles = null; const styles = { title: "_1reo15vq _18m915vq _syazazsu _1bto1l2s _o5721q9c", note: "_syaz1rpy _o5721q9c", lozenge: "_ahbq12x7 _1ul91wqb", noResultsContainer: "_1bsbo8uj _y3gn1h6o", dropdownContent: "_1rjcv77o _1bsbsmdz _c71lko4j _1e0c1txw _1bah1h6o _4cvr1h6o", containerWithMinHeight: "_1tkeqkoa", contentContainer: "_y44vfmxe _1bsb1osq _1wpz1fhb _18m91wug", errorContainer: "_1bsbo8uj _1e0c1txw", errorIcon: "_1mour5cr", learnMoreLink: "_4bfu1r31 _1hmsglyw _ajmmnqa1", requestAccess: "_1bsb19n7 _o5721q9c _ahbq12x7 _syaz1rpy" }; const shouldApplyMinHeight = (fetchStatus, itemCount) => { // When there are 1/2 items, dropdown height is less than minHeight 144px return !(fetchStatus === 'success' && itemCount > 0); }; const ItemTitle = ({ title, formatMessage, onSameDocument, isSource, hasAccess, productType }) => { return /*#__PURE__*/React.createElement(Inline, null, /*#__PURE__*/React.createElement(Box, { as: "span", xcss: styles.title }, title), onSameDocument && /*#__PURE__*/React.createElement(Box, { as: "span", xcss: styles.note }, "\xA0-", ' ', formatMessage(productType === 'confluence-page' ? messages.syncedLocationDropdownTitleNoteForConfluencePage : messages.syncedLocationDropdownTitleNoteForJiraWorkItem)), isSource && /*#__PURE__*/React.createElement(Box, { as: "span", xcss: styles.lozenge }, /*#__PURE__*/React.createElement(Lozenge, null, formatMessage(messages.syncedLocationDropdownSourceLozenge))), !hasAccess && /*#__PURE__*/React.createElement(Box, { as: "span", xcss: styles.requestAccess }, formatMessage(messages.syncedLocationDropdownRequestAccess))); }; const productIconMap = { 'confluence-page': ConfluenceIcon, 'jira-work-item': JiraIcon }; const subTypeIconMap = { live: PageLiveDocIcon, page: PageIcon }; const getConfluenceSubTypeIcon = (sourceAri, subType) => { try { const { type: pageType } = getPageIdAndTypeFromConfluencePageAri({ ari: sourceAri }); if (pageType === 'blogpost') { return QuotationMarkIcon; } else { return subType && subType in subTypeIconMap ? subTypeIconMap[subType] : PageIcon; } } catch { return PageIcon; } }; const ProductIcon = ({ product }) => { var _productIconMap$produ; const ProductIcon = product ? (_productIconMap$produ = productIconMap[product]) !== null && _productIconMap$produ !== void 0 ? _productIconMap$produ : AtlassianIcon : AtlassianIcon; return /*#__PURE__*/React.createElement("span", { className: ax(["_2rkol0p1 _bfhki8nm _1bsbgktf _4t3igktf _1e0c1txw _4cvr1h6o _1bah1h6o"]) }, /*#__PURE__*/React.createElement(ProductIcon, { size: "xxsmall", appearance: "neutral" })); }; const ItemIcon = ({ reference }) => { const { hasAccess, subType, productType, sourceAri } = reference; if (productType === 'confluence-page' && hasAccess) { return /*#__PURE__*/React.createElement(IconTile, { icon: getConfluenceSubTypeIcon(sourceAri, subType), label: "", appearance: 'gray', size: "xsmall" }); } // For `jira-work-item` (and any future product), we fall back to the generic product logo icon. // Jira issues don't have an equivalent page/blog subtype concept, so no rich IconTile is shown. // Future enhancement: if Jira issue type icons are needed, add an `issueType` field to // `SyncBlockSourceInfo` and fetch it in `fetchJiraWorkItemInfo` via the GraphQL query. return /*#__PURE__*/React.createElement(ProductIcon, { product: productType }); }; export const processReferenceData = (referenceData, intl) => { const { formatMessage } = intl; const sourceInfoMap = new Map(); referenceData === null || referenceData === void 0 ? void 0 : referenceData.forEach(reference => { if (!reference) { return; } if (sourceInfoMap.has(reference.sourceAri)) { var _sourceInfoMap$get; (_sourceInfoMap$get = sourceInfoMap.get(reference.sourceAri)) === null || _sourceInfoMap$get === void 0 ? void 0 : _sourceInfoMap$get.push(reference); } else { sourceInfoMap.set(reference.sourceAri, [reference]); } }); for (const references of sourceInfoMap.values()) { if (references.length > 1) { references.forEach((reference, index) => reference.title = `${reference.title === '' && reference.hasAccess && fg('platform_synced_block_patch_8') ? formatMessage(messages.syncedLocationDropdownUntitledPage) : reference.title}: ${formatMessage(messages.syncedLocationDropdownTitleBlockIndex, { index: index + 1 })}`); } } const sortedReferences = Array.from(sourceInfoMap.values()).flat().sort((a, b) => { if (a.isSource !== b.isSource) { return b.isSource ? 1 : -1; } if (a.hasAccess !== b.hasAccess) { return a.hasAccess ? -1 : 1; } return (a.title || '').localeCompare(b.title || ''); }); return sortedReferences; }; export const SyncedLocationDropdown = ({ syncBlockStore, resourceId, intl, isSource, localId, api }) => { const { formatMessage } = intl; const triggerTitle = formatMessage(messages.syncedLocationDropdownTitle); const [isOpen, setIsOpen] = useState(false); return /*#__PURE__*/React.createElement(DropdownMenu, { isOpen: isOpen // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , onOpenChange: ({ isOpen }) => setIsOpen(isOpen), testId: "synced-block-synced-locations-dropdown" // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , trigger: ({ triggerRef, ...triggerProps }) => /*#__PURE__*/React.createElement(Button, _extends({ ref: triggerRef, areAnyNewToolbarFlagsEnabled: true, selected: isOpen, iconAfter: /*#__PURE__*/React.createElement(ChevronDownIcon, { color: "currentColor", spacing: "spacious", label: "", size: "small" }) // eslint-disable-next-line react/jsx-props-no-spreading }, triggerProps), triggerTitle) }, isOpen && /*#__PURE__*/React.createElement(DropdownContent, { syncBlockStore: syncBlockStore, resourceId: resourceId, intl: intl, isSource: isSource, localId: localId, api: api })); }; const DropdownContent = ({ syncBlockStore, resourceId, intl, isSource, localId, api }) => { const { formatMessage } = intl; const [fetchStatus, setFetchStatus] = useState('none'); const [referenceData, setReferenceData] = useState([]); useEffect(() => { setFetchStatus('loading'); const getReferenceData = async () => { const response = await syncBlockStore.fetchReferencesSourceInfo(resourceId, localId, isSource); if (response.error) { setFetchStatus('error'); return; } setReferenceData(processReferenceData(response.references, intl)); setFetchStatus('success'); }; getReferenceData(); }, [syncBlockStore, intl, isSource, localId, resourceId]); const handleLocationClick = () => { var _api$analytics, _api$analytics$action; 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$action.fireAnalyticsEvent({ eventType: EVENT_TYPE.OPERATIONAL, action: ACTION.CLICKED, actionSubject: ACTION_SUBJECT.SYNCED_BLOCK, actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_CLICK_SYNCED_LOCATION, attributes: { resourceId } }); }; const content = () => { switch (fetchStatus) { case 'loading': return /*#__PURE__*/React.createElement(LoadingScreen, null); case 'error': return /*#__PURE__*/React.createElement(ErrorScreen, { formatMessage: formatMessage }); case 'success': if (referenceData.length > 0) { return /*#__PURE__*/React.createElement("div", { "data-testid": "synced-locations-dropdown-content", className: ax([styles.contentContainer, "_ku9g126e _18bk1rpy"]) }, /*#__PURE__*/React.createElement(DropdownItemGroup, { title: formatMessage(messages.syncedLocationDropdownHeading, { count: `${referenceData.length > 99 ? '99+' : referenceData.length}` }) }, referenceData.map(reference => { const title = reference.title === '' && reference.hasAccess && fg('platform_synced_block_patch_8') ? formatMessage(messages.syncedLocationDropdownUntitledPage) : reference.title || reference.url || ''; return /*#__PURE__*/React.createElement("div", { key: reference.title, className: ax(["_2ll012x7"]) }, /*#__PURE__*/React.createElement(Tooltip, { content: title }, /*#__PURE__*/React.createElement(DropdownItem, { elemBefore: /*#__PURE__*/React.createElement(ItemIcon, { reference: reference }), href: reference.url, target: "_blank", key: reference.title, rel: "noopener noreferrer" // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , onClick: () => handleLocationClick() }, /*#__PURE__*/React.createElement(ItemTitle, { title: title, formatMessage: formatMessage, onSameDocument: reference.onSameDocument, isSource: reference.isSource, hasAccess: reference.hasAccess, productType: reference.productType })))); }))); } else { return /*#__PURE__*/React.createElement(NoResultScreen, { formatMessage: formatMessage }); } } }; return /*#__PURE__*/React.createElement(Box, { xcss: cx(styles.dropdownContent, shouldApplyMinHeight(fetchStatus, referenceData.length) && styles.containerWithMinHeight) }, content()); }; const LoadingScreen = () => { return /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Spinner, null)); }; const ErrorScreen = ({ formatMessage }) => { return /*#__PURE__*/React.createElement(Box, { xcss: styles.errorContainer, testId: "synced-locations-dropdown-content-error" }, /*#__PURE__*/React.createElement(Box, { xcss: styles.errorIcon }, /*#__PURE__*/React.createElement(StatusErrorIcon, { color: "var(--ds-icon-danger, #C9372C)", spacing: "spacious", label: "", size: "small" })), /*#__PURE__*/React.createElement(Text, { as: "p", size: "medium" }, formatMessage(messages.syncedLocationDropdownError))); }; const NoResultScreen = ({ formatMessage }) => { return /*#__PURE__*/React.createElement(Stack, { xcss: styles.noResultsContainer, space: "space.100", testId: "synced-locations-dropdown-content-no-results" }, /*#__PURE__*/React.createElement(Text, { as: "p" }, formatMessage(messages.syncedLocationDropdownNoResults)), /*#__PURE__*/React.createElement(Text, { as: "p" }, /*#__PURE__*/React.createElement(Anchor, { href: SYNCED_BLOCKS_DOCUMENTATION_URL, target: "_blank", rel: "noopener noreferrer", xcss: styles.learnMoreLink }, formatMessage(messages.syncedLocationDropdownLearnMoreLink)))); };