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
154 lines (131 loc) • 5 kB
text/typescript
import {StackCompactIcon, StackIcon} from '@sanity/icons'
import {type SchemaType} from '@sanity/types'
import {startCase} from 'lodash'
import {structureLocaleNamespace} from '../i18n'
import {type DocumentListBuilder} from './DocumentList'
import {DocumentTypeListBuilder, type DocumentTypeListInput} from './DocumentTypeList'
import {defaultIntentChecker} from './Intent'
import {type List} from './List'
import {type ListItem, ListItemBuilder} from './ListItem'
import {getOrderingMenuItemsForSchemaType, MenuItemBuilder} from './MenuItem'
import {DEFAULT_SELECTED_ORDERING_OPTION} from './Sort'
import {type Collection} from './StructureNodes'
import {type StructureContext} from './types'
const BUNDLED_DOC_TYPES = ['sanity.imageAsset', 'sanity.fileAsset']
function isBundledDocType(typeName: string) {
return BUNDLED_DOC_TYPES.includes(typeName)
}
function isDocumentType(schemaType: SchemaType) {
return schemaType.type?.name === 'document'
}
function isList(collection: Collection): collection is List {
return collection.type === 'list'
}
export function getDocumentTypes({schema}: StructureContext): string[] {
return schema
.getTypeNames()
.filter((n) => {
const schemaType = schema.get(n)
return schemaType && isDocumentType(schemaType)
})
.filter((n) => !isBundledDocType(n))
}
export function getDocumentTypeListItems(context: StructureContext): ListItemBuilder[] {
const types = getDocumentTypes(context)
return types.map((typeName) => getDocumentTypeListItem(context, typeName))
}
export function getDocumentTypeListItem(
context: StructureContext,
typeName: string,
): ListItemBuilder {
const {schema} = context
const type = schema.get(typeName)
if (!type) {
throw new Error(`Schema type with name "${typeName}" not found`)
}
const title = type.title || startCase(typeName)
return new ListItemBuilder(context)
.id(typeName)
.title(title)
.schemaType(type)
.child((id, childContext) => {
const parent = childContext.parent as Collection
const parentItem = isList(parent)
? (parent.items.find((item) => item.id === id) as ListItem)
: null
let list = getDocumentTypeList(context, typeName)
if (parentItem && parentItem.title) {
list = list.title(parentItem.title)
}
return list
})
}
export function getDocumentTypeList(
context: StructureContext,
typeNameOrSpec: string | DocumentTypeListInput,
): DocumentListBuilder {
const {schema, resolveDocumentNode} = context
const schemaType = typeof typeNameOrSpec === 'string' ? typeNameOrSpec : typeNameOrSpec.schemaType
const typeName = typeof schemaType === 'string' ? schemaType : schemaType.name
const spec: DocumentTypeListInput =
typeof typeNameOrSpec === 'string' ? {schemaType} : typeNameOrSpec
const type = schema.get(typeName)
if (!type) {
throw new Error(`Schema type with name "${typeName}" not found`)
}
const title = type.title || startCase(typeName)
return new DocumentTypeListBuilder(context)
.id(spec.id || typeName)
.title(spec.title || title)
.filter('_type == $type')
.params({type: typeName})
.schemaType(type)
.defaultOrdering(DEFAULT_SELECTED_ORDERING_OPTION.by)
.menuItemGroups(
spec.menuItemGroups || [
{
id: 'sorting',
title: 'Sort',
i18n: {title: {key: 'menu-item-groups.actions-group', ns: structureLocaleNamespace}},
},
{
id: 'layout',
title: 'Layout',
i18n: {title: {key: 'menu-item-groups.layout-group', ns: structureLocaleNamespace}},
},
{
id: 'actions',
title: 'Actions',
i18n: {title: {key: 'menu-item-groups.sorting-group', ns: structureLocaleNamespace}},
},
],
)
.child(
spec.child ||
((documentId: string) => resolveDocumentNode({schemaType: typeName, documentId})),
)
.canHandleIntent(spec.canHandleIntent || defaultIntentChecker)
.menuItems(
spec.menuItems || [
// Create new (from action button) will be added in serialization step of GenericList
// Sort by <Y>
...getOrderingMenuItemsForSchemaType(context, type),
// Display as <Z>
new MenuItemBuilder(context)
.group('layout')
.i18n({title: {key: 'menu-items.layout.compact-view', ns: structureLocaleNamespace}})
.title('Compact view') // fallback title
.icon(StackCompactIcon)
.action('setLayout')
.params({layout: 'default'}),
new MenuItemBuilder(context)
.group('layout')
.i18n({title: {key: 'menu-items.layout.detailed-view', ns: structureLocaleNamespace}})
.title('Detailed view') // fallback title
.icon(StackIcon)
.action('setLayout')
.params({layout: 'detail'}),
// Create new (from menu) will be added in serialization step of GenericList
],
)
}