UNPKG

sanity

Version:

Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches

1 lines • 1.06 MB
{"version":3,"file":"pane.mjs","sources":["../../src/structure/components/DocTitle.tsx","../../src/structure/components/confirmDeleteDialog/ConfirmDeleteDialogBody.styles.tsx","../../src/structure/components/paneItem/PaneItemPreview.tsx","../../src/structure/components/paneRouter/BackLink.tsx","../../src/structure/components/pane/usePaneLayout.ts","../../src/structure/components/paneRouter/ChildLink.tsx","../../src/structure/components/paneRouter/ParameterizedLink.tsx","../../src/structure/components/paneRouter/ReferenceChildLink.tsx","../../src/structure/components/paneRouter/usePaneRouter.ts","../../src/structure/components/confirmDeleteDialog/ReferencePreviewLink.tsx","../../src/structure/components/confirmDeleteDialog/ConfirmDeleteDialogBody.tsx","../../src/structure/components/confirmDeleteDialog/useReferringDocuments.ts","../../src/structure/components/confirmDeleteDialog/ConfirmDeleteDialog.tsx","../../src/structure/components/confirmDeleteDialog/index.tsx","../../src/structure/components/Delay.tsx","../../src/structure/components/pane/constants.ts","../../src/structure/components/pane/PaneDivider.tsx","../../src/structure/components/pane/Pane.tsx","../../src/structure/components/pane/PaneContent.styles.tsx","../../src/structure/components/pane/usePane.ts","../../src/structure/components/pane/PaneContent.tsx","../../src/structure/util/toLowerCaseNoSpaces.tsx","../../src/structure/components/pane/PaneMenuButtonItem.tsx","../../src/structure/components/pane/PaneContextMenuButton.tsx","../../src/structure/components/pane/PaneFooter.styles.tsx","../../src/structure/components/pane/PaneFooter.tsx","../../src/structure/components/pane/PaneHeader.styles.tsx","../../src/structure/components/pane/PaneHeader.tsx","../../src/structure/components/pane/PaneHeaderActionButton.tsx","../../src/structure/components/pane/PaneLayout.styles.tsx","../../src/structure/components/pane/helpers/_calcPaneResize.ts","../../src/structure/components/pane/helpers/_getDOMPath.ts","../../src/structure/components/pane/helpers/_sortElements.ts","../../src/structure/components/pane/paneLayoutController.ts","../../src/structure/components/pane/PaneLayout.tsx","../../src/structure/menuNodes.ts","../../src/structure/components/IntentButton.tsx","../../src/structure/components/paneHeaderActions/InsufficientPermissionsMessageTooltip.tsx","../../src/structure/components/paneHeaderActions/PaneHeaderCreateButton.tsx","../../src/structure/components/paneHeaderActions/PaneHeaderActions.tsx","../../src/structure/components/MissingSchemaType.tsx","../../src/structure/components/paneItem/PaneItem.tsx","../../src/structure/components/RenderActionCollectionState.tsx","../../src/structure/components/RenderBadgeCollectionState.tsx","../../src/structure/diffView/hooks/useCreatePathSyncChannel.ts","../../src/structure/diffView/constants.ts","../../src/structure/diffView/hooks/useDiffViewRouter.ts","../../src/structure/diffView/types/diffViewMode.ts","../../src/structure/diffView/hooks/useDiffViewState.ts","../../src/structure/diffView/hooks/useScrollMirror.tsx","../../src/structure/diffView/versionMode/components/VersionModeHeader.tsx","../../src/structure/diffView/components/DialogLayout.ts","../../src/structure/panes/document/document-layout/pickDocumentLayoutComponent.ts","../../src/structure/diffView/hooks/usePathSyncChannel.ts","../../src/structure/diffView/components/Scroller.ts","../../src/structure/diffView/components/DiffViewPane.tsx","../../src/structure/diffView/components/EditReferenceLinkComponent.tsx","../../src/structure/diffView/components/DiffView.tsx","../../src/structure/diffView/plugin/DiffViewDocumentLayout.tsx","../../src/structure/panes/error/ErrorPane.tsx","../../src/structure/panes/loading/getWaitMessages.ts","../../src/structure/panes/loading/LoadingPane.tsx","../../src/structure/panes/document/useDocumentPane.tsx","../../src/structure/panes/document/comments/CommentsWrapper.tsx","../../src/structure/hooks/useFilteredReleases.ts","../../src/structure/hooks/useDocumentIdStack.ts","../../src/structure/useStructureTool.ts","../../src/structure/panes/document/constants.ts","../../src/structure/panes/document/DocumentInspectorMenuItemsResolver.tsx","../../src/structure/panes/document/useDocumentTitle.ts","../../src/structure/panes/document/DocumentOperationResults.tsx","../../src/structure/components/paneItem/helpers.tsx","../../src/structure/panes/document/documentInspector/DocumentInspectorHeader.tsx","../../src/structure/panes/document/documentInspector/DocumentInspectorPanel.tsx","../../src/structure/useStructureToolSetting.ts","../../src/structure/panes/document/inspectDialog/constants.ts","../../src/structure/panes/document/inspectDialog/helpers.ts","../../src/structure/panes/document/inspectDialog/InspectDialog.styles.tsx","../../src/structure/panes/document/inspectDialog/Search.tsx","../../src/structure/panes/document/inspectDialog/InspectDialog.tsx","../../src/structure/panes/document/documentPanel/banners/Banner.tsx","../../src/structure/panes/document/documentPanel/banners/DeletedDocumentBanners.tsx","../../src/structure/panes/document/documentPanel/banners/DeprecatedDocumentTypeBanner.tsx","../../src/structure/components/requestPermissionDialog/__telemetry__/RequestPermissionDialog.telemetry.ts","../../src/structure/components/requestPermissionDialog/RequestPermissionDialog.tsx","../../src/structure/components/requestPermissionDialog/useRoleRequestsStatus.tsx","../../src/structure/panes/document/documentPanel/banners/InsufficientPermissionBanner.tsx","../../src/structure/panes/document/documentPanel/banners/ReferenceChangedBanner.tsx","../../src/structure/panes/document/documentPanel/documentViews/useConditionalToast.ts","../../src/structure/panes/document/documentPanel/banners/AddToReleaseBanner.tsx","../../src/structure/panes/document/documentPanel/banners/ArchivedReleaseDocumentBanner.tsx","../../src/structure/panes/document/documentPanel/banners/CreateLinkedBanner.tsx","../../src/structure/panes/document/documentPanel/banners/__telemetry__/DraftLiveEditBanner.telemetry.ts","../../src/structure/panes/document/documentPanel/banners/DraftLiveEditBanner.tsx","../../src/structure/panes/document/documentPanel/banners/OpenReleaseToEditBanner.tsx","../../src/structure/panes/document/documentPanel/banners/ScheduledReleaseBanner.tsx","../../src/structure/panes/document/documentPanel/banners/UnpublishedDocumentBanner.tsx","../../src/structure/panes/document/documentPanel/documentViews/FormHeader.tsx","../../src/structure/panes/document/documentPanel/documentViews/FormView.tsx","../../src/structure/panes/document/documentPanel/header/DocumentHeaderTabs.tsx","../../src/structure/panes/document/documentPanel/header/DocumentHeaderTitle.tsx","../../src/structure/panes/document/documentPanel/header/DocumentPanelSubHeader.tsx","../../src/structure/panes/document/documentPanel/DocumentPanel.tsx","../../src/structure/constants.ts","../../src/structure/panes/document/statusBar/dialogs/constants.ts","../../src/structure/panes/document/statusBar/dialogs/ConfirmDialog.tsx","../../src/structure/panes/document/statusBar/dialogs/ModalDialog.tsx","../../src/structure/panes/document/statusBar/dialogs/PopoverDialog.tsx","../../src/structure/panes/document/statusBar/ActionStateDialog.tsx","../../src/structure/panes/document/statusBar/ActionMenuButton.tsx","../../src/structure/documentActions/DeleteAction.tsx","../../src/structure/documentActions/DiscardChangesAction.tsx","../../src/structure/documentActions/DuplicateAction.tsx","../../src/structure/documentActions/HistoryRestoreAction.tsx","../../src/structure/documentActions/__telemetry__/documentActions.telemetry.ts","../../src/structure/documentActions/PublishAction.tsx","../../src/structure/documentActions/UnpublishAction.tsx","../../src/structure/panes/document/statusBar/DocumentStatusBarActions.tsx","../../src/structure/panes/document/documentPanel/header/perspective/DocumentPerspectiveList.tsx","../../src/structure/panes/document/documentPanel/header/DocumentPanelHeader.tsx","../../src/structure/components/spacerButton/SpacerButton.tsx","../../src/structure/panes/document/statusBar/DocumentBadges.tsx","../../src/structure/panes/document/timeline/constants.ts","../../src/structure/panes/document/timeline/helpers.ts","../../src/structure/panes/document/timeline/expandableTimelineItemButton.tsx","../../src/structure/panes/document/timeline/timeline.styled.tsx","../../src/structure/panes/document/timeline/timelineI18n.ts","../../src/structure/panes/document/timeline/userAvatarStack.tsx","../../src/structure/panes/document/timeline/timelineItem.tsx","../../src/structure/panes/document/timeline/utils.ts","../../src/structure/panes/document/timeline/timeline.tsx","../../src/structure/panes/document/timeline/TimelineError.tsx","../../src/structure/panes/document/timeline/timelineMenu.tsx","../../src/structure/panes/document/statusBar/DocumentStatusPulse/AnimatedStatusIcon.tsx","../../src/structure/panes/document/statusBar/DocumentStatusPulse/DocumentStatusPulse.tsx","../../src/structure/panes/document/statusBar/DocumentStatusLine.tsx","../../src/structure/panes/document/statusBar/RevisionStatusLine.tsx","../../src/structure/panes/document/statusBar/useResizeObserver.ts","../../src/structure/panes/document/statusBar/DocumentStatusBar.tsx","../../src/structure/panes/document/keyboardShortcuts/DocumentActionShortcuts.tsx","../../src/structure/panes/document/menuItems.ts","../../src/structure/panes/document/document-layout/DocumentLayoutError.tsx","../../src/structure/panes/document/document-layout/DocumentLayoutFooter.tsx","../../src/structure/panes/document/document-layout/DocumentLayout.tsx","../../src/structure/panes/document/document-layout/useDocumentLayoutComponent.ts","../../src/structure/panes/document/__telemetry__/documentPanes.telemetry.ts","../../src/structure/panes/document/getInitialValueTemplateOpts.ts","../../src/structure/panes/document/useDocumentPaneInitialValue.ts","../../src/structure/panes/document/useDocumentPaneInspector.ts","../../src/structure/panes/document/usePreviewUrl.ts","../../src/structure/panes/document/DocumentPaneProvider.tsx","../../src/structure/panes/document/DocumentEventsPane.tsx","../../src/structure/panes/document/DocumentPaneLegacyTimeline.tsx","../../src/structure/panes/document/DocumentPaneProviderWrapper.tsx","../../src/structure/panes/document/useResetHistoryParams.ts","../../src/structure/panes/document/DocumentPane.tsx","../../src/structure/panes/documentList/constants.ts","../../src/structure/panes/documentList/DocumentListPaneContent.tsx","../../src/structure/panes/documentList/helpers.ts","../../src/structure/panes/documentList/listenSearchQuery.ts","../../src/structure/panes/documentList/useDocumentList.ts","../../src/structure/panes/documentList/DocumentListPane.tsx","../../src/structure/panes/documentList/PaneHeader.tsx","../../src/structure/panes/documentList/sheetList/DocumentSheetListSelect.tsx","../../src/structure/panes/documentList/sheetList/DocumentSheetListProvider.tsx","../../src/structure/panes/documentList/sheetList/SheetListCell.tsx","../../src/structure/panes/documentList/sheetList/useDocumentSheetColumns.tsx","../../src/structure/panes/documentList/sheetList/ColumnsControl.tsx","../../src/structure/panes/documentList/sheetList/DocumentSheetListFilter.tsx","../../src/structure/panes/documentList/sheetList/DocumentSheetListHeader.tsx","../../src/structure/panes/documentList/sheetList/DocumentSheetListPaginator.tsx","../../src/structure/panes/documentList/sheetList/useDocumentSheetListStore.ts","../../src/structure/panes/documentList/sheetList/useDocumentSheetList.ts","../../src/structure/panes/documentList/sheetList/DocumentSheetListPane.tsx","../../src/structure/panes/documentList/PaneContainer.tsx","../../src/structure/documentBadges/LiveEditBadge.ts","../../src/structure/getIntentState.ts","../../src/structure/panes/document/inspectors/changes/ChangesInspector.tsx","../../src/structure/panes/document/timeline/events/EventTimelineItem.tsx","../../src/structure/panes/document/timeline/events/PublishedEventMenu.tsx","../../src/structure/panes/document/timeline/events/EventsTimeline.tsx","../../src/structure/panes/document/timeline/events/EventsTimelineMenu.tsx","../../src/structure/panes/document/inspectors/changes/EventsInspector.tsx","../../src/structure/panes/document/inspectors/changes/EventsSelector.tsx","../../src/structure/panes/document/inspectors/changes/HistorySelector.tsx","../../src/structure/panes/document/inspectors/changes/ChangesTabs.tsx","../../src/structure/panes/document/inspectors/changes/index.ts","../../src/structure/panes/document/inspectors/validation/getPathTitles.ts","../../src/structure/panes/document/inspectors/validation/ValidationInspector.tsx","../../src/structure/panes/document/inspectors/validation/index.ts","../../src/structure/router.ts","../../src/structure/structureTool.ts"],"sourcesContent":["import {type SanityDocumentLike} from '@sanity/types'\nimport {unstable_useValuePreview as useValuePreview, useSchema, useTranslation} from 'sanity'\n\nimport {structureLocaleNamespace} from '../i18n'\n\nexport interface DocTitleProps {\n document: SanityDocumentLike\n}\n\nexport function DocTitle(props: DocTitleProps) {\n const {document: documentValue} = props\n const schema = useSchema()\n const schemaType = schema.get(documentValue._type)\n const {t} = useTranslation(structureLocaleNamespace)\n\n const {error, value} = useValuePreview({\n schemaType: schemaType!,\n value: documentValue,\n })\n\n if (!schemaType) {\n return <code>{t('doc-title.unknown-schema-type.text', {schemaType: documentValue._type})}</code>\n }\n\n if (error) {\n return <>{t('doc-title.error.text', {errorMessage: error.message})}</>\n }\n\n return (\n <>\n {value?.title || (\n <span style={{color: 'var(--card-muted-fg-color)'}}>{t('doc-title.fallback.text')}</span>\n )}\n </>\n )\n}\n","import {InfoOutlineIcon} from '@sanity/icons'\nimport {Box, Flex, Inline, rem, Text} from '@sanity/ui'\nimport {useTranslation} from 'sanity'\nimport {styled} from 'styled-components'\n\nimport {Tooltip} from '../../../ui-components'\nimport {structureLocaleNamespace} from '../../i18n'\n\nexport const ChevronWrapper = styled(Box)`\n margin-left: auto;\n`\n\nexport const CrossDatasetReferencesDetails = styled.details`\n flex: none;\n\n &[open] ${ChevronWrapper} {\n transform: rotate(180deg);\n }\n`\n\nexport const CrossDatasetReferencesSummary = styled.summary`\n list-style: none;\n\n &::-webkit-details-marker {\n display: none;\n }\n`\n\nexport const Table = styled.table`\n width: 100%;\n text-align: left;\n padding: 0 ${({theme}) => rem(theme.sanity.space[2])};\n border-collapse: collapse;\n\n th {\n padding: ${({theme}) => rem(theme.sanity.space[1])};\n }\n\n td {\n padding: 0 ${({theme}) => rem(theme.sanity.space[1])};\n }\n\n tr > *:last-child {\n text-align: right;\n }\n`\n\nexport const DocumentIdFlex = styled(Flex)`\n min-height: 33px;\n`\n\nexport const OtherReferenceCount = (props: {totalCount: number; references: unknown[]}) => {\n const {t} = useTranslation(structureLocaleNamespace)\n const difference = props.totalCount - props.references.length\n\n if (!difference) return null\n\n return (\n <Box padding={2}>\n <Inline space={2}>\n <Text size={1} muted>\n {t('confirm-delete-dialog.other-reference-count.title', {count: difference})}\n </Text>\n\n <Tooltip\n portal\n placement=\"top\"\n content={t('confirm-delete-dialog.other-reference-count.tooltip')}\n >\n <Text size={1} muted>\n <InfoOutlineIcon />\n </Text>\n </Tooltip>\n </Inline>\n </Box>\n )\n}\n","import {type SanityDocument, type SchemaType} from '@sanity/types'\nimport {Flex} from '@sanity/ui'\nimport {type ComponentType, useMemo} from 'react'\nimport {useObservable} from 'react-rx'\nimport {\n type DocumentPresence,\n DocumentPreviewPresence,\n type DocumentPreviewStore,\n DocumentStatus,\n DocumentStatusIndicator,\n type GeneralPreviewLayoutKey,\n getPreviewStateObservable,\n getPreviewValueWithFallback,\n SanityDefaultPreview,\n useDocumentVersionInfo,\n usePerspective,\n} from 'sanity'\n\nimport {TooltipDelayGroupProvider} from '../../../ui-components'\n\nexport interface PaneItemPreviewProps {\n documentPreviewStore: DocumentPreviewStore\n icon: ComponentType | false\n layout: GeneralPreviewLayoutKey\n presence?: DocumentPresence[]\n schemaType: SchemaType\n value: SanityDocument\n}\n\n/**\n * Preview component for _documents_ rendered in structure panes.\n *\n * Note that non-document previews are not handled by this component,\n * despite being pane items! Non-document previews bypass this entirely\n * and are rendered by `<SanityDefaultPreview>`.\n */\nexport function PaneItemPreview(props: PaneItemPreviewProps) {\n const {icon, layout, presence, schemaType, value} = props\n\n const versionsInfo = useDocumentVersionInfo(value._id)\n\n const {perspectiveStack} = usePerspective()\n const previewStateObservable = useMemo(() => {\n return getPreviewStateObservable(\n props.documentPreviewStore,\n schemaType,\n value._id,\n perspectiveStack,\n )\n }, [props.documentPreviewStore, schemaType, value._id, perspectiveStack])\n\n const {\n snapshot,\n original,\n isLoading: previewIsLoading,\n } = useObservable(previewStateObservable, {\n snapshot: null,\n isLoading: true,\n original: null,\n })\n\n const isLoading = previewIsLoading\n\n const status = isLoading ? null : (\n <TooltipDelayGroupProvider>\n <Flex align=\"center\" gap={3}>\n {presence && presence.length > 0 && <DocumentPreviewPresence presence={presence} />}\n <DocumentStatusIndicator\n draft={versionsInfo.draft}\n published={versionsInfo.published}\n versions={versionsInfo.versions}\n />\n </Flex>\n </TooltipDelayGroupProvider>\n )\n\n const tooltip = (\n <DocumentStatus\n draft={versionsInfo.draft}\n published={versionsInfo.published}\n versions={versionsInfo.versions}\n />\n )\n\n return (\n <SanityDefaultPreview\n {...getPreviewValueWithFallback({snapshot, original, fallback: value})}\n isPlaceholder={isLoading}\n icon={icon}\n layout={layout}\n status={status}\n tooltip={tooltip}\n />\n )\n}\n","import {type ComponentType, type ForwardedRef, forwardRef, useContext, useMemo} from 'react'\nimport {PaneRouterContext} from 'sanity/_singletons'\nimport {StateLink} from 'sanity/router'\n\nimport {type BackLinkProps} from './types'\n\n/**\n * @internal\n */\nexport const BackLink = forwardRef(function BackLink(\n props: BackLinkProps,\n ref: ForwardedRef<HTMLAnchorElement>,\n) {\n const {routerPanesState, groupIndex} = useContext(PaneRouterContext)\n const panes = useMemo(() => routerPanesState.slice(0, groupIndex), [groupIndex, routerPanesState])\n const state = useMemo(() => ({panes}), [panes])\n\n return <StateLink {...props} ref={ref} state={state} />\n}) as ComponentType<BackLinkProps>\n","import {useContext} from 'react'\nimport {PaneLayoutContext} from 'sanity/_singletons'\n\nimport {type PaneLayoutContextValue} from './types'\n\n/**\n *\n * @hidden\n * @beta This API will change. DO NOT USE IN PRODUCTION.\n */\nexport function usePaneLayout(): PaneLayoutContextValue {\n const pane = useContext(PaneLayoutContext)\n\n if (!pane) {\n throw new Error('PaneLayout: missing context value')\n }\n\n return pane\n}\n","import {type ForwardedRef, forwardRef, useContext} from 'react'\nimport {PaneRouterContext} from 'sanity/_singletons'\nimport {StateLink} from 'sanity/router'\n\nimport {type ChildLinkProps} from './types'\n\n/**\n * @internal\n */\nexport const ChildLink = forwardRef(function ChildLink(\n props: ChildLinkProps,\n ref: ForwardedRef<HTMLAnchorElement>,\n) {\n const {childId, childPayload, childParameters, ...rest} = props\n const {routerPanesState, groupIndex} = useContext(PaneRouterContext)\n\n return (\n <StateLink\n {...rest}\n ref={ref}\n state={{\n panes: [\n ...routerPanesState.slice(0, groupIndex + 1),\n [{id: childId, params: childParameters, payload: childPayload}],\n ],\n }}\n />\n )\n})\n","import {type ForwardedRef, forwardRef, type ReactNode, useContext, useMemo} from 'react'\nimport {useUnique} from 'sanity'\nimport {PaneRouterContext} from 'sanity/_singletons'\nimport {StateLink} from 'sanity/router'\n\ninterface ParameterizedLinkProps {\n params?: Record<string, string>\n payload?: unknown\n children?: ReactNode\n}\n\n/**\n * @internal\n */\nexport const ParameterizedLink = forwardRef(function ParameterizedLink(\n props: ParameterizedLinkProps,\n ref: ForwardedRef<HTMLAnchorElement>,\n) {\n const {routerPanesState: currentPanes, groupIndex, siblingIndex} = useContext(PaneRouterContext)\n const {params, payload, ...rest} = props\n const nextParams = useUnique(params)\n const nextPayload = useUnique(payload)\n\n const nextState = useMemo(() => {\n const currentGroup = currentPanes[groupIndex]\n const currentSibling = currentGroup[siblingIndex]\n\n const nextSibling = {\n ...currentSibling,\n params: nextParams ?? currentSibling.params,\n payload: nextPayload ?? currentSibling.payload,\n }\n\n const nextGroup = [\n ...currentGroup.slice(0, siblingIndex),\n nextSibling,\n ...currentGroup.slice(siblingIndex + 1),\n ]\n\n const nextPanes = [\n ...currentPanes.slice(0, groupIndex),\n nextGroup,\n ...currentPanes.slice(groupIndex + 1),\n ]\n\n return {panes: nextPanes}\n }, [currentPanes, groupIndex, nextParams, nextPayload, siblingIndex])\n\n return <StateLink ref={ref} {...rest} state={nextState} />\n})\n","import {toString as pathToString} from '@sanity/util/paths'\nimport {type ForwardedRef, forwardRef} from 'react'\n\nimport {ChildLink} from './ChildLink'\nimport {type ReferenceChildLinkProps} from './types'\n\nexport const ReferenceChildLink = forwardRef(function ReferenceChildLink(\n {documentId, documentType, parentRefPath, children, template, ...rest}: ReferenceChildLinkProps,\n ref: ForwardedRef<HTMLAnchorElement>,\n) {\n return (\n <ChildLink\n {...rest}\n ref={ref}\n childId={documentId}\n childPayload={template?.params}\n childParameters={{\n type: documentType,\n parentRefPath: pathToString(parentRefPath),\n ...(template && {template: template?.id}),\n }}\n >\n {children}\n </ChildLink>\n )\n})\n","import {useContext} from 'react'\nimport {PaneRouterContext} from 'sanity/_singletons'\n\nimport {type PaneRouterContextValue} from './types'\n\n/**\n *\n * @hidden\n * @beta\n */\nexport function usePaneRouter(): PaneRouterContextValue {\n return useContext(PaneRouterContext)\n}\n","import {type SanityDocument, type SchemaType} from '@sanity/types'\nimport {type ReactNode, useCallback} from 'react'\nimport {\n type FIXME,\n getPublishedId,\n PreviewCard,\n useDocumentPresence,\n useDocumentPreviewStore,\n} from 'sanity'\n\nimport {PaneItemPreview} from '../paneItem/PaneItemPreview'\nimport {usePaneRouter} from '../paneRouter'\n\nconst EMPTY_ARRAY: [] = []\n\ninterface ReferencePreviewLinkProps {\n onClick?: () => void\n type: SchemaType & {icon?: any}\n value: SanityDocument\n}\n\nexport function ReferencePreviewLink(props: ReferencePreviewLinkProps) {\n const {onClick, type, value} = props\n const publishedId = getPublishedId(value?._id)\n const documentPresence = useDocumentPresence(publishedId)\n const documentPreviewStore = useDocumentPreviewStore()\n const {ReferenceChildLink} = usePaneRouter()\n\n const Link = useCallback(\n function LinkComponent(linkProps: {children: ReactNode}) {\n return (\n <ReferenceChildLink\n documentId={value?._id}\n documentType={type?.name}\n parentRefPath={EMPTY_ARRAY}\n {...linkProps}\n />\n )\n },\n [ReferenceChildLink, type?.name, value?._id],\n )\n\n return (\n <PreviewCard __unstable_focusRing as={Link as FIXME} data-as=\"a\" onClick={onClick} radius={2}>\n <PaneItemPreview\n documentPreviewStore={documentPreviewStore}\n icon={type?.icon}\n layout=\"compact\"\n presence={documentPresence?.length > 0 ? documentPresence : EMPTY_ARRAY}\n schemaType={type}\n value={value}\n />\n </PreviewCard>\n )\n}\n","import {\n ChevronDownIcon,\n CopyIcon,\n DocumentsIcon,\n UnknownIcon,\n WarningOutlineIcon,\n} from '@sanity/icons'\nimport {Box, Card, Flex, Stack, Text, useToast} from '@sanity/ui'\nimport {useCallback} from 'react'\nimport {SanityDefaultPreview, Translate, useSchema, useTranslation} from 'sanity'\n\nimport {Button} from '../../../ui-components'\nimport {structureLocaleNamespace} from '../../i18n'\nimport {\n ChevronWrapper,\n CrossDatasetReferencesDetails,\n CrossDatasetReferencesSummary,\n DocumentIdFlex,\n OtherReferenceCount,\n Table,\n} from './ConfirmDeleteDialogBody.styles'\nimport {ReferencePreviewLink} from './ReferencePreviewLink'\nimport {type ReferringDocuments} from './useReferringDocuments'\n\ntype DeletionConfirmationDialogBodyProps = Required<ReferringDocuments> & {\n documentTitle: React.ReactNode\n action: 'unpublish' | 'delete'\n onReferenceLinkClick?: () => void\n}\n\n/**\n * The inner part of the `ConfirmDeleteDialog`. This is ran when both the\n * `crossDatasetReferences` and `internalReferences` are loaded.\n */\nexport function ConfirmDeleteDialogBody({\n crossDatasetReferences,\n internalReferences,\n documentTitle,\n totalCount,\n action,\n datasetNames,\n hasUnknownDatasetNames,\n onReferenceLinkClick,\n}: DeletionConfirmationDialogBodyProps) {\n const schema = useSchema()\n const toast = useToast()\n const {t} = useTranslation(structureLocaleNamespace)\n\n const renderPreviewItem = useCallback(\n (item: any) => {\n const type = schema.get(item._type)\n if (type) {\n return <ReferencePreviewLink type={type} value={item} onClick={onReferenceLinkClick} />\n }\n\n return (\n // Padding added to match the ReferencePreviewLink styling\n <Box padding={2}>\n <SanityDefaultPreview\n icon={UnknownIcon}\n title={t('confirm-delete-dialog.preview-item.preview-unavailable.title')}\n subtitle={t('confirm-delete-dialog.preview-item.preview-unavailable.subtitle', {\n documentId: item._id,\n })}\n layout=\"default\"\n />\n </Box>\n )\n },\n [schema, t, onReferenceLinkClick],\n )\n\n if (internalReferences?.totalCount === 0 && crossDatasetReferences?.totalCount === 0) {\n return (\n <Text as=\"p\" size={1}>\n <Translate\n t={t}\n i18nKey=\"confirm-delete-dialog.confirmation.text\"\n context={action}\n components={{DocumentTitle: () => <strong>{documentTitle}</strong>}}\n />\n </Text>\n )\n }\n\n // We do some extra checks to handle cases where you have unavailable dataset\n // name(s) due to permissions, both alone and in combination with known datasets\n\n // This normalizes one or more undefined dataset names to the catch-all `unavailable`\n const normalizedDatasetNames = [\n ...datasetNames,\n ...(hasUnknownDatasetNames ? ['unavailable'] : []),\n ]\n\n const datasetSubtitle = t('confirm-delete-dialog.cdr-summary.subtitle', {\n count: normalizedDatasetNames.length,\n datasets: normalizedDatasetNames.join(', '),\n context: hasUnknownDatasetNames && normalizedDatasetNames.length ? 'unavailable' : '',\n })\n\n return (\n <Flex direction=\"column\" gap={4}>\n <Card padding={3} radius={2} tone=\"caution\" flex=\"none\">\n <Flex>\n <Text aria-hidden=\"true\" size={1}>\n <WarningOutlineIcon />\n </Text>\n <Box flex={1} marginLeft={3}>\n <Text size={1}>\n <Translate\n i18nKey=\"confirm-delete-dialog.referring-document-count.text\"\n components={{DocumentTitle: () => documentTitle}}\n t={t}\n values={{count: totalCount}}\n />\n </Text>\n </Box>\n </Flex>\n </Card>\n <Box flex=\"none\">\n <Text size={1}>\n <Translate\n i18nKey=\"confirm-delete-dialog.referring-documents-descriptor.text\"\n t={t}\n context={action}\n components={{DocumentTitle: () => documentTitle}}\n />\n </Text>\n </Box>\n <Card radius={2} shadow={1} flex=\"auto\" padding={2}>\n <Flex direction=\"column\">\n {internalReferences.totalCount > 0 && (\n <Stack as=\"ul\" marginBottom={2} space={2} data-testid=\"internal-references\">\n {internalReferences?.references.map((item) => (\n <Box as=\"li\" key={item._id}>\n {renderPreviewItem(item)}\n </Box>\n ))}\n\n {internalReferences.totalCount > internalReferences.references.length && (\n <Box as=\"li\" padding={3}>\n <OtherReferenceCount {...internalReferences} />\n </Box>\n )}\n </Stack>\n )}\n\n {crossDatasetReferences.totalCount > 0 && (\n <CrossDatasetReferencesDetails\n data-testid=\"cross-dataset-references\"\n style={{\n // only add the border if needed\n borderTop:\n internalReferences.totalCount > 0\n ? '1px solid var(--card-shadow-outline-color)'\n : undefined,\n }}\n >\n <CrossDatasetReferencesSummary>\n <Card\n as=\"a\"\n marginTop={internalReferences.totalCount > 0 ? 2 : 0}\n radius={2}\n shadow={1}\n paddingY={1}\n >\n <Flex align=\"center\" gap={3} paddingX={3} paddingY={1}>\n <Text size={1}>\n <DocumentsIcon />\n </Text>\n <Stack space={2}>\n <Text textOverflow=\"ellipsis\" size={1}>\n {t('confirm-delete-dialog.cdr-summary.title', {\n count: normalizedDatasetNames.length,\n documentCount: t('confirm-delete-dialog.cdr-summary.document-count', {\n count: crossDatasetReferences.totalCount,\n }),\n })}\n </Text>\n <Text title={datasetSubtitle} textOverflow=\"ellipsis\" size={1} muted>\n {datasetSubtitle}\n </Text>\n </Stack>\n <ChevronWrapper>\n <Text muted size={1}>\n <ChevronDownIcon />\n </Text>\n </ChevronWrapper>\n </Flex>\n </Card>\n </CrossDatasetReferencesSummary>\n\n <Box overflow=\"auto\" paddingTop={2}>\n <Table>\n <thead>\n <tr>\n <th>\n <Text muted size={1} style={{minWidth: '5rem'}} weight=\"medium\">\n {t('confirm-delete-dialog.cdr-table.project-id.label')}\n </Text>\n </th>\n <th>\n <Text muted size={1} weight=\"medium\">\n {t('confirm-delete-dialog.cdr-table.dataset.label')}\n </Text>\n </th>\n <th>\n <Text muted size={1} weight=\"medium\">\n {t('confirm-delete-dialog.cdr-table.document-id.label')}\n </Text>\n </th>\n </tr>\n </thead>\n <tbody>\n {crossDatasetReferences.references\n .filter((reference): reference is Required<typeof reference> => {\n return 'projectId' in reference\n })\n .map(({projectId, datasetName, documentId}, index) => (\n // eslint-disable-next-line react/no-array-index-key\n <tr key={`${documentId}-${index}`}>\n <td>\n <Text size={1}>{projectId}</Text>\n </td>\n <td>\n <Text size={1}>{datasetName || 'unavailable'}</Text>\n </td>\n <td>\n <DocumentIdFlex align=\"center\" gap={2} justify=\"flex-end\">\n <Text textOverflow=\"ellipsis\" size={1}>\n {documentId || 'unavailable'}\n </Text>\n {documentId && (\n <Button\n mode=\"bleed\"\n icon={CopyIcon}\n tooltipProps={{\n content: t(\n 'confirm-delete-dialog.cdr-table.copy-id-button.tooltip',\n ),\n }}\n // eslint-disable-next-line react/jsx-no-bind\n onClick={() => {\n navigator.clipboard.writeText(documentId).catch(() => {\n toast.push({\n status: 'error',\n title: t(\n 'confirm-delete-dialog.cdr-table.id-copied-toast.title-failed',\n ),\n })\n })\n }}\n />\n )}\n </DocumentIdFlex>\n </td>\n </tr>\n ))}\n </tbody>\n </Table>\n\n <OtherReferenceCount {...crossDatasetReferences} />\n </Box>\n </CrossDatasetReferencesDetails>\n )}\n </Flex>\n </Card>\n <Box flex=\"none\">\n <Text size={1}>\n <Translate\n i18nKey=\"confirm-delete-dialog.referential-integrity-disclaimer.text\"\n t={t}\n context={action}\n components={{DocumentTitle: () => documentTitle}}\n />\n </Text>\n </Box>\n </Flex>\n )\n}\n","import {type ClientError, type SanityClient} from '@sanity/client'\nimport {useMemo} from 'react'\nimport {EMPTY, fromEvent, type Observable, of, timer} from 'rxjs'\nimport {\n catchError,\n distinctUntilChanged,\n map,\n shareReplay,\n startWith,\n switchMap,\n} from 'rxjs/operators'\nimport {\n type AvailabilityResponse,\n createHookFromObservableFactory,\n DEFAULT_STUDIO_CLIENT_OPTIONS,\n type DocumentStore,\n getDraftId,\n getPublishedId,\n useClient,\n useDocumentStore,\n} from 'sanity'\n\n// this is used in place of `instanceof` so the matching can be more robust and\n// won't have any issues with dual packages etc\n// https://nodejs.org/api/packages.html#dual-package-hazard\nfunction isClientError(e: unknown): e is ClientError {\n if (typeof e !== 'object') return false\n if (!e) return false\n return 'statusCode' in e && 'response' in e\n}\n\nconst POLL_INTERVAL = 5000\n\n// only fetches when the document is visible\nlet visiblePoll$: Observable<number>\nconst getVisiblePoll$ = () => {\n if (!visiblePoll$) {\n visiblePoll$ = fromEvent(document, 'visibilitychange').pipe(\n // add empty emission to have this fire on creation\n startWith(null),\n map(() => document.visibilityState === 'visible'),\n distinctUntilChanged(),\n switchMap((visible) =>\n visible\n ? // using timer instead of interval since timer will emit on creation\n timer(0, POLL_INTERVAL)\n : EMPTY,\n ),\n shareReplay({refCount: true, bufferSize: 1}),\n )\n }\n return visiblePoll$\n}\n\nexport type ReferringDocuments = {\n isLoading: boolean\n totalCount: number\n projectIds: string[]\n datasetNames: string[]\n hasUnknownDatasetNames: boolean\n internalReferences?: {\n totalCount: number\n references: Array<{_id: string; _type: string}>\n }\n crossDatasetReferences?: {\n totalCount: number\n references: Array<{\n /**\n * The project ID of the document that is currently referencing the subject\n * document. Unlike `documentId` and `datasetName`, this should always be\n * defined.\n */\n projectId: string\n /**\n * The ID of the document that is currently referencing the subject\n * document. This will be omitted if there is no access to the current\n * project and dataset pair (e.g. if no `sanity-project-token` were\n * configured)\n */\n documentId?: string\n /**\n * The dataset name that is currently referencing the subject document.\n * This will be omitted if there is no access to the current project and\n * dataset pair (e.g. if no `sanity-project-token` were configured)\n */\n datasetName?: string\n }>\n }\n}\n\nfunction getDocumentExistence(\n documentId: string,\n {versionedClient}: {versionedClient: SanityClient},\n): Observable<string | undefined> {\n const draftId = getDraftId(documentId)\n const publishedId = getPublishedId(documentId)\n const requestOptions = {\n uri: versionedClient.getDataUrl('doc', `${draftId},${publishedId}`),\n json: true,\n query: {excludeContent: 'true'},\n tag: 'use-referring-documents.document-existence',\n }\n return versionedClient.observable.request<AvailabilityResponse>(requestOptions).pipe(\n map(({omitted}) => {\n const nonExistant = omitted.filter((doc) => doc.reason === 'existence')\n if (nonExistant.length === 2) {\n // None of the documents exist\n return undefined\n }\n\n if (nonExistant.length === 0) {\n // Both exist, so use the published one\n return publishedId\n }\n\n // If the draft does not exist, use the published ID, and vice versa\n return nonExistant.some((doc) => doc.id === draftId) ? publishedId : draftId\n }),\n )\n}\n\n/**\n * fetches the cross-dataset references using the client observable.request\n * method (for that requests can be automatically cancelled)\n */\nfunction fetchCrossDatasetReferences(\n documentId: string,\n context: {versionedClient: SanityClient},\n): Observable<ReferringDocuments['crossDatasetReferences']> {\n const {versionedClient} = context\n\n return getVisiblePoll$().pipe(\n switchMap(() => getDocumentExistence(documentId, context)),\n switchMap((checkDocumentId) => {\n if (!checkDocumentId) {\n return of({totalCount: 0, references: []})\n }\n\n const currentDataset = versionedClient.config().dataset\n\n return versionedClient.observable\n .request({\n url: `/data/references/${currentDataset}/documents/${checkDocumentId}/to?excludeInternalReferences=true&excludePaths=true`,\n tag: 'use-referring-documents.external',\n })\n .pipe(\n catchError((e) => {\n // it's possible that referencing document doesn't exist yet so the\n // API will return a 404. In those cases, we want to catch and return\n // a response with no references\n if (isClientError(e) && e.statusCode === 404) {\n return of({totalCount: 0, references: []})\n }\n\n throw e\n }),\n )\n }),\n )\n}\n\nconst useInternalReferences = createHookFromObservableFactory(\n ([documentId, documentStore]: [string, DocumentStore]) => {\n const referencesClause = '*[references($documentId)][0...100]{_id,_type}'\n const totalClause = 'count(*[references($documentId)])'\n const fetchQuery = `{\"references\":${referencesClause},\"totalCount\":${totalClause}}`\n const listenQuery = '*[references($documentId)]'\n\n return documentStore.listenQuery(\n {fetch: fetchQuery, listen: listenQuery},\n {documentId},\n {tag: 'use-referring-documents', transitions: ['appear', 'disappear'], throttleTime: 5000},\n ) as Observable<ReferringDocuments['internalReferences']>\n },\n)\n\nconst useCrossDatasetReferences = createHookFromObservableFactory(\n ([documentId, versionedClient]: [string, SanityClient]) => {\n // (documentId: string, versionedClient: SanityClient) => {\n return getVisiblePoll$().pipe(\n switchMap(() =>\n fetchCrossDatasetReferences(documentId, {\n versionedClient,\n }),\n ),\n )\n },\n)\n\nexport function useReferringDocuments(documentId: string): ReferringDocuments {\n const versionedClient = useClient(DEFAULT_STUDIO_CLIENT_OPTIONS)\n\n const documentStore = useDocumentStore()\n const publishedId = getPublishedId(documentId)\n\n const [internalReferences, isInternalReferencesLoading] = useInternalReferences(\n useMemo(() => [publishedId, documentStore], [documentStore, publishedId]),\n )\n\n const [crossDatasetReferences, isCrossDatasetReferencesLoading] = useCrossDatasetReferences(\n useMemo(() => [publishedId, versionedClient], [publishedId, versionedClient]),\n )\n\n const projectIds = useMemo(() => {\n return Array.from(\n new Set(\n crossDatasetReferences?.references\n .map((crossDatasetReference) => crossDatasetReference.projectId)\n .filter(Boolean),\n ),\n ).sort()\n }, [crossDatasetReferences?.references])\n\n const datasetNames = useMemo(() => {\n return Array.from(\n new Set<string>(\n crossDatasetReferences?.references\n // .filter((name) => typeof name === 'string')\n .map((crossDatasetReference) => crossDatasetReference?.datasetName || '')\n .filter((datasetName) => Boolean(datasetName) && datasetName !== ''),\n ),\n ).sort()\n }, [crossDatasetReferences?.references])\n\n const hasUnknownDatasetNames = useMemo(() => {\n return Boolean(\n crossDatasetReferences?.references.some(\n (crossDatasetReference) => typeof crossDatasetReference.datasetName !== 'string',\n ),\n )\n }, [crossDatasetReferences?.references])\n\n return {\n totalCount: (internalReferences?.totalCount || 0) + (crossDatasetReferences?.totalCount || 0),\n projectIds,\n datasetNames,\n hasUnknownDatasetNames,\n internalReferences,\n crossDatasetReferences,\n isLoading: isInternalReferencesLoading || isCrossDatasetReferencesLoading,\n }\n}\n","import {Box, Flex} from '@sanity/ui'\nimport {useId, useMemo} from 'react'\nimport {LoadingBlock, useTranslation} from 'sanity'\nimport {styled} from 'styled-components'\n\nimport {Dialog} from '../../../ui-components'\nimport {structureLocaleNamespace} from '../../i18n'\nimport {DocTitle} from '../DocTitle'\nimport {ConfirmDeleteDialogBody} from './ConfirmDeleteDialogBody'\nimport {useReferringDocuments} from './useReferringDocuments'\n\n/** @internal */\nexport const DialogBody = styled(Box)`\n box-sizing: border-box;\n`\n\n/** @internal */\nexport const LoadingContainer = styled(Flex).attrs({\n align: 'center',\n direction: 'column',\n justify: 'center',\n})`\n height: 110px;\n`\n\n/** @internal */\nexport interface ConfirmDeleteDialogProps {\n /**\n * Incoming document ID used to find other referencing documents. This\n * field respects draft IDs (e.g. if you pass in a published ID when one\n * doesn't exist the document title may not show up).\n */\n id: string\n /**\n * The schema typename of the incoming document\n */\n type: string\n /**\n * The name of the action being done. (e.g. the `'unpublish'` action requires\n * the same document deletion confirmation).\n */\n action?: 'delete' | 'unpublish'\n onCancel: () => void\n onConfirm: () => void\n}\n\n/**\n * A confirmation dialog used to prevent unwanted document deletes. Loads all\n * the referencing internal and cross-data references prior to showing the\n * delete button.\n *\n * @internal\n */\nexport function ConfirmDeleteDialog({\n id,\n type,\n action = 'delete',\n onCancel,\n onConfirm,\n}: ConfirmDeleteDialogProps) {\n const {t} = useTranslation(structureLocaleNamespace)\n const dialogId = `deletion-confirmation-${useId()}`\n const {\n internalReferences,\n crossDatasetReferences,\n isLoading,\n totalCount,\n projectIds,\n datasetNames,\n hasUnknownDatasetNames,\n } = useReferringDocuments(id)\n const documentTitle = <DocTitle document={useMemo(() => ({_id: id, _type: type}), [id, type])} />\n const showConfirmButton = !isLoading\n\n return (\n <Dialog\n width={1}\n id={dialogId}\n header={t('confirm-delete-dialog.header.text', {context: action})}\n footer={{\n cancelButton: {\n onClick: onCancel,\n text: t('confirm-delete-dialog.cancel-button.text'),\n },\n confirmButton: showConfirmButton\n ? {\n text:\n totalCount > 0\n ? t('confirm-delete-dialog.confirm-anyway-button.text', {context: action})\n : t('confirm-delete-dialog.confirm-button.text', {context: action}),\n onClick: onConfirm,\n }\n : undefined,\n }}\n onClose={onCancel}\n onClickOutside={onCancel}\n >\n <DialogBody>\n {crossDatasetReferences && internalReferences && !isLoading ? (\n <ConfirmDeleteDialogBody\n crossDatasetReferences={crossDatasetReferences}\n internalReferences={internalReferences}\n documentTitle={documentTitle}\n isLoading={isLoading}\n totalCount={totalCount}\n action={action}\n projectIds={projectIds}\n datasetNames={datasetNames}\n hasUnknownDatasetNames={hasUnknownDatasetNames}\n onReferenceLinkClick={onCancel}\n />\n ) : (\n <LoadingContainer data-testid=\"loading-container\">\n <LoadingBlock showText title={t('confirm-delete-dialog.loading.text')} />\n </LoadingContainer>\n )}\n </DialogBody>\n </Dialog>\n )\n}\n","import {Box, Text} from '@sanity/ui'\nimport {type ComponentProps, useCallback, useId, useState} from 'react'\nimport {useTranslation} from 'sanity'\n\nimport {Dialog, ErrorBoundary} from '../../../ui-components'\nimport {structureLocaleNamespace} from '../../i18n'\nimport {ConfirmDeleteDialog, type ConfirmDeleteDialogProps} from './ConfirmDeleteDialog'\n\nexport type {ConfirmDeleteDialogProps}\n\ntype ArgType<T> = T extends (arg: infer U) => unknown ? U : never\ntype ErrorInfo = ArgType<ComponentProps<typeof ErrorBoundary>['onCatch']>\n\n/** @internal */\nfunction ConfirmDeleteDialogContainer(props: ConfirmDeleteDialogProps) {\n const {t} = useTranslation(structureLocaleNamespace)\n const id = useId()\n const [error, setError] = useState<ErrorInfo | null>(null)\n const handleRetry = useCallback(() => setError(null), [])\n\n return error ? (\n <Dialog\n id={`dialog-error-${id}`}\n data-testid=\"confirm-delete-error-dialog\"\n header={t('confirm-delete-dialog.error.title.text')}\n footer={{\n confirmButton: {\n text: t('confirm-delete-dialog.error.retry-button.text'),\n onClick: handleRetry,\n tone: 'default',\n },\n }}\n onClose={props.onCancel}\n >\n <Box padding={4}>\n <Text size={1}>{t('confirm-delete-dialog.error.message.text')}</Text>\n </Box>\n </Dialog>\n ) : (\n <ErrorBoundary onCatch={setError}>\n <ConfirmDeleteDialog {...props} />\n </ErrorBoundary>\n )\n}\n\nexport {ConfirmDeleteDialogContainer as ConfirmDeleteDialog}\n","import {useEffect, useState} from 'react'\n\nexport function Delay({\n children,\n ms = 0,\n}: {\n children?: React.JSX.Element | (() => React.JSX.Element)\n ms?: number\n}): React.JSX.Element {\n const [ready, setReady] = useState(ms <= 0)\n\n useEffect(() => {\n if (ms <= 0) {\n return undefined\n }\n\n const timeoutId = setTimeout(() => setReady(true), ms)\n\n return () => {\n clearTimeout(timeoutId)\n }\n }, [ms])\n\n if (!ready || !children) {\n return <></>\n }\n\n return typeof children === 'function' ? children() : children\n}\n","/**\n * @internal\n */\nexport const PANE_DEBUG = false\n\n/**\n * @internal\n */\nexport const PANE_COLLAPSED_WIDTH = 51\n\n/**\n * @internal\n */\nexport const PANE_DEFAULT_MIN_WIDTH = PANE_COLLAPSED_WIDTH * 4\n","import {Layer} from '@sanity/ui'\nimport {useCallback, useState} from 'react'\nimport {styled} from 'styled-components'\n\nimport {usePaneLayout} from './usePaneLayout'\n\nconst Root = styled(Layer)`\n position: relative;\n width: 1px;\n min-width: 1px;\n\n &:before {\n content: '';\n display: block;\n position: absolute;\n top: 0;\n