@atlaskit/editor-plugin-synced-block
Version:
SyncedBlock plugin for @atlaskit/editor-core
345 lines (343 loc) • 13.4 kB
JavaScript
/* 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))));
};