@stackbit/types
Version:
Types for Stackbit config and Content Source Interface
742 lines (662 loc) • 24.1 kB
text/typescript
/**
* Stackbit Field Types
*/
import { STYLE_PROPS } from './consts';
import type { DocumentWithSource } from './content-source-document';
import type { DocumentModelField, DocumentObjectField } from './content-source-document-fields';
import type { ConfigDelegate } from './config-delegate';
import type { CustomActionClientModel, CustomActionField, CustomActionObjectField } from './custom-actions';
import type { DistributiveOmit } from './utility-types';
import type { ModelWithSource } from './models';
import type { UpdateOperation } from './content-source-operation';
export type Field =
| FieldString
| FieldUrl
| FieldSlug
| FieldText
| FieldMarkdown
| FieldHtml
| FieldNumber
| FieldBoolean
| FieldDate
| FieldDatetime
| FieldColor
| FieldJson
| FieldRichText
| FieldFile
| FieldEnum
| FieldImage
| FieldObject
| FieldModel
| FieldReference
| FieldCrossReference
| FieldStyle
| FieldList;
export type ClientField<T extends Field = Field> = DistributiveOmit<T, 'default' | 'const' | 'actions'> & {
default?: unknown;
const?: unknown;
actions?: CustomActionClientModel<CustomActionField>[];
};
// Make all Field properties except the 'name' optional, including fields of nested objects and lists.
// prettier-ignore
export type DistributeFieldsExtension<Type extends Field> =
Type extends { type: 'object' } ?
Partial<Omit<Type, 'name' | 'fields'>> & { name: string; fields?: DistributeFieldsExtension<Field>[] } :
Type extends { type: 'list' } ?
Partial<Omit<Type, 'name' | 'items'>> & { name: string; items?: DistributePartialListItems<FieldListItems> } :
// do not make style fields partial
Type extends { type: 'style' } ? Type :
// do not make cross-reference fields partial
Type extends { type: 'cross-reference' } ? Type :
Partial<Omit<Type, 'name'>> & { name: string };
// prettier-ignore
export type DistributePartialListItems<Type extends FieldListItems> =
Type extends { type: 'object' } ?
Partial<Omit<Type, 'fields'>> & { fields?: DistributeFieldsExtension<Field>[] } :
// do not make cross-reference fields partial
Type extends { type: 'cross-reference' } ? Type :
Partial<Type>;
export type FieldExtension = DistributeFieldsExtension<Field>;
export type FieldType = Field['type'];
export type FieldSpecificProps =
| FieldStringProps
| FieldUrlProps
| FieldSlugProps
| FieldTextProps
| FieldMarkdownProps
| FieldHtmlProps
| FieldNumberProps
| FieldBooleanProps
| FieldDateProps
| FieldDatetimeProps
| FieldColorProps
| FieldJsonProps
| FieldRichTextProps
| FieldFileProps
| FieldEnumProps
| FieldImageProps
| FieldObjectProps
| FieldModelProps
| FieldReferenceProps
| FieldCrossReferenceProps
| FieldStyleProps
| FieldListProps;
type ExtractByType<Union extends { type: FieldType }, Type extends FieldType> = Union extends { type: Type }
? Union
: never;
export type FieldForType<Type extends FieldType> = ExtractByType<Field, Type>;
export type FieldSpecificPropsForType<Type extends FieldType> = ExtractByType<FieldSpecificProps, Type>;
export type FieldSpecificPropsForField<Type extends Field> = ExtractByType<FieldSpecificProps, Type['type']>;
export type FieldString = FieldCommonProps & FieldStringProps;
export type FieldUrl = FieldCommonProps & FieldUrlProps;
export type FieldSlug = FieldCommonProps & FieldSlugProps;
export type FieldText = FieldCommonProps & FieldTextProps;
export type FieldMarkdown = FieldCommonProps & FieldMarkdownProps;
export type FieldHtml = FieldCommonProps & FieldHtmlProps;
export type FieldNumber = FieldCommonProps & FieldNumberProps;
export type FieldBoolean = FieldCommonProps & FieldBooleanProps;
export type FieldDate = FieldCommonProps & FieldDateProps;
export type FieldDatetime = FieldCommonProps & FieldDatetimeProps;
export type FieldColor = FieldCommonProps & FieldColorProps;
export type FieldJson = FieldCommonProps & FieldJsonProps;
export type FieldRichText = FieldCommonProps & FieldRichTextProps;
export type FieldFile = FieldCommonProps & FieldFileProps;
export type FieldEnum = FieldCommonProps & FieldEnumProps;
export type FieldImage = FieldCommonProps & FieldImageProps;
export type FieldObject = Omit<FieldCommonProps, 'actions'> & FieldObjectProps;
export type FieldModel = FieldCommonProps & FieldModelProps;
export type FieldReference = FieldCommonProps & FieldReferenceProps;
export type FieldCrossReference = FieldCommonProps & FieldCrossReferenceProps;
export type FieldStyle = FieldCommonProps & FieldStyleProps;
export type FieldList = FieldCommonProps & FieldListProps;
export type FieldListObject = FieldCommonProps & FieldListItemsObjectProps;
export type FieldListModel = FieldCommonProps & FieldListItemsModelProps;
export type FieldListReference = FieldCommonProps & FieldListItemsReferenceProps;
export type FieldListCrossReference = FieldCommonProps & FieldListItemsCrossReferenceProps;
export interface FieldCommonProps {
/**
* The `name` property defines the name of the field key used to store its value in the content source.
*/
name: string;
/**
* Use the `label` property to provide the human-readable label for the field. If not specified, the
* `name` property will be start-cased and used instead.
*/
label?: string;
/**
* The `description` is presented as a tooltip when hovering over the info icon in Stackbit UI.
*/
description?: string;
/**
* Use the `required` property to mark a field as required. Visual editor
* will display an error message below the field if its value is not set,
* and publishing the document containing this field will not be possible
* until it has been filled in.
*/
required?: boolean;
/**
* Field Validations
* An object specifying validation rules that must be met when saving the field.
*/
validations?: FieldValidationsCustom;
/**
* Use the `default` property to specify an initial value for a field
* when no value provided when creating the parent object.
*/
default?: unknown | FieldInitialValueFunction;
/**
* Use the `group` field along with the `fieldGroups` model property,
* to organize related fields into logical groups presented as tabs within the Stackbit UI.
*/
group?: string;
/**
* Use the `const` property to specify a value for a field when creating
* the parent object. Setting this property implies that a field will be
* read-only and cannot be changed via Stackbit UI.
*/
const?: unknown | FieldInitialValueFunction;
/**
* The `hidden` property can be used to hide fields from the Stackbit UI and prevent content
* editors from modifying their values. This can be particularly useful for fields that are
* controlled by internal processes, or for fields that have constant values specified using
* the `const` property.
*/
hidden?: boolean;
/**
* You can use the `readOnly` property to restrict the editing of fields in the Stackbit UI.
*
* For primitive field types such as strings, numbers, and booleans, setting `readOnly`
* to `true` will prevent the field from being edited.
*
* For complex field types like `reference`, `model`, `cross-reference`, and `object`,
* setting `readOnly` to `true` will prevent editors from adding or removing the object
* or reference to the document. However, it will not prevent editing of fields within
* the nested or referenced object/document. To prevent editing of fields within an object,
* `readOnly` must be set on each field that should not be editable.
*
* For `list` fields, setting `readOnly` to `true` will prevent editors from adding, removing,
* or reordering items within the list.
*/
readOnly?: boolean;
/**
* To enable field localization, set the `localized` property to `true`. When a model field
* is marked as localized, the document fields returned from the content source must also be
* localized.
*/
localized?: boolean;
actions?: CustomActionField[];
}
export type FieldCustomControlType =
| 'custom-modal-html'
| 'custom-inline-html'
| 'custom-modal-script'
| 'custom-inline-script';
export interface FieldCustomControlTypeProps<ControlType = never> {
/**
* The `controlType` property can be utilized to define a custom UI control for the field.
* If you choose to define it, you must also specify either the `controlUrl` or the
* `controlFilePath` property.
*/
controlType?: FieldCustomControlType | ControlType;
/**
* Use `controlFilePath` to specify a path to the local HTML file containing
* the custom field UI. The path should be relative to the folder containing
* the stackbit.config.ts.
* When specifying this property, you must also specify the `controlType` property.
*/
controlFilePath?: string;
/**
* Use `controlUrl` to specify the remote URL to the HTML containing a custom
* field UI.
* When specifying this property, you must also specify the `controlType` property.
*/
controlUrl?: string;
}
export type FieldInitialValueFunction = (
options: {
data: Record<string, any>;
locale?: string;
} & ConfigDelegate
) => Promise<unknown>;
export type FieldBasicProps =
| FieldStringProps
| FieldUrlProps
| FieldSlugProps
| FieldTextProps
| FieldMarkdownProps
| FieldHtmlProps
| FieldBooleanProps
| FieldDateProps
| FieldDatetimeProps
| FieldColorProps
| FieldJsonProps
| FieldRichTextProps
| FieldFileProps;
export interface FieldStringProps extends FieldCustomControlTypeProps {
type: 'string';
validations?: FieldValidationsCustom &
FieldValidationsStringLength &
FieldValidationsRegExp &
FieldValidationsUnique;
}
export interface FieldUrlProps extends FieldCustomControlTypeProps {
type: 'url';
validations?: FieldValidationsCustom &
FieldValidationsStringLength &
FieldValidationsRegExp &
FieldValidationsUnique;
}
export interface FieldSlugProps extends FieldCustomControlTypeProps {
type: 'slug';
validations?: FieldValidationsCustom &
FieldValidationsStringLength &
FieldValidationsRegExp &
FieldValidationsUnique;
}
export interface FieldTextProps extends FieldCustomControlTypeProps {
type: 'text';
validations?: FieldValidationsCustom & FieldValidationsStringLength & FieldValidationsRegExp;
}
export interface FieldMarkdownProps extends FieldCustomControlTypeProps {
type: 'markdown';
validations?: FieldValidationsCustom & FieldValidationsStringLength & FieldValidationsRegExp;
}
export interface FieldHtmlProps extends FieldCustomControlTypeProps {
type: 'html';
validations?: FieldValidationsCustom & FieldValidationsStringLength & FieldValidationsRegExp;
}
export interface FieldBooleanProps extends FieldCustomControlTypeProps<'button-group' | 'checkbox'> {
type: 'boolean';
validations?: FieldValidationsCustom;
}
export interface FieldDateProps extends FieldCustomControlTypeProps {
type: 'date';
validations?: FieldValidationsCustom & FieldValidationsDateRange;
}
export interface FieldDatetimeProps extends FieldCustomControlTypeProps {
type: 'datetime';
validations?: FieldValidationsCustom & FieldValidationsDateRange;
}
export interface FieldColorProps extends FieldCustomControlTypeProps {
type: 'color';
validations?: FieldValidationsCustom;
}
export interface FieldJsonProps extends FieldCustomControlTypeProps {
type: 'json';
validations?: FieldValidationsCustom;
}
export interface FieldRichTextProps extends FieldCustomControlTypeProps {
type: 'richText';
validations?: FieldValidationsCustom;
}
export interface FieldFileProps extends FieldCustomControlTypeProps {
type: 'file';
validations?: FieldValidationsCustom & FieldValidationsFile;
}
export type FieldEnumProps =
| FieldEnumDropdownProps
| FieldEnumThumbnailsProps
| FieldEnumPaletteProps
| FieldEnumPaletteColorsProps;
export interface FieldEnumDropdownProps extends FieldCustomControlTypeProps<'dropdown' | 'button-group'> {
type: 'enum';
options: FieldEnumOptionValue[] | FieldEnumOptionObject[];
validations?: FieldValidationsCustom & FieldValidationsUnique;
}
export interface FieldEnumThumbnailsProps {
type: 'enum';
controlType: 'thumbnails';
options: FieldEnumOptionThumbnails[];
validations?: FieldValidationsCustom & FieldValidationsUnique;
}
export interface FieldEnumPaletteProps {
type: 'enum';
controlType: 'palette';
options: FieldEnumOptionPalette[];
validations?: FieldValidationsCustom & FieldValidationsUnique;
}
export interface FieldEnumPaletteColorsProps {
type: 'enum';
controlType: 'palette-colors';
options: FieldEnumOptionPaletteColors[];
validations?: FieldValidationsCustom & FieldValidationsUnique;
}
export type FieldEnumOptionValue = string | number;
export interface FieldEnumOptionObject {
label: string;
value: FieldEnumOptionValue;
}
export interface FieldEnumOptionThumbnails extends FieldEnumOptionObject {
thumbnail: string;
}
export interface FieldEnumOptionPalette extends FieldEnumOptionObject {
textColor?: string;
backgroundColor?: string;
borderColor?: string;
}
export interface FieldEnumOptionPaletteColors extends FieldEnumOptionObject {
colors: string[];
}
export interface FieldImageProps extends FieldCustomControlTypeProps {
type: 'image';
source?: string;
validations?: FieldValidationsCustom & FieldValidationsImage;
}
export interface FieldNumberProps extends FieldCustomControlTypeProps<'slider'> {
type: 'number';
subtype?: 'int' | 'float';
/** @deprecated Use [`validations.min`]{@link FieldValidationsNumberRange#min} property. */
min?: number;
/** @deprecated Use [`validations.min`]{@link FieldValidationsNumberRange#max} property. */
max?: number;
/** @deprecated Use [`validations.step`]{@link FieldValidationsNumberRange#step} property. */
step?: number;
unit?: string;
validations?: FieldValidationsCustom & FieldValidationsNumberRange & FieldValidationsUnique;
}
export interface FieldObjectProps extends FieldCustomControlTypeProps {
type: 'object';
labelField?: string;
preview?: DocumentFieldPreview;
thumbnail?: string;
variantField?: string;
validations?: FieldValidationsCustom;
actions?: (CustomActionField | CustomActionObjectField)[];
fieldGroups?: FieldGroupItem[];
fields: Field[];
}
export interface FieldGroupItem {
name: string;
label: string;
icon?: string;
}
export interface FieldModelProps extends FieldCustomControlTypeProps {
type: 'model';
models: string[];
groups?: string[];
validations?: FieldValidationsCustom;
}
export interface FieldReferenceProps extends FieldCustomControlTypeProps {
type: 'reference';
models: string[];
groups?: string[];
validations?: FieldValidationsCustom;
}
export interface FieldCrossReferenceProps extends FieldCustomControlTypeProps {
type: 'cross-reference';
models: FieldCrossReferenceModel[];
validations?: FieldValidationsCustom;
}
export interface FieldCrossReferenceModel {
modelName: string;
srcType?: string;
srcProjectId?: string;
}
export type StyleProps = (typeof STYLE_PROPS)[number];
export interface FieldStyleProps {
type: 'style';
styles: Record<string, Partial<Record<StyleProps, any>>>;
validations?: FieldValidationsCustom;
}
export interface FieldListProps extends FieldCustomControlTypeProps<'checkbox'> {
type: 'list';
items:
| FieldBasicProps
| FieldEnumProps
| FieldBooleanProps
| FieldImageProps
| FieldNumberProps
| FieldObjectProps
| FieldModelProps
| FieldReferenceProps
| FieldCrossReferenceProps;
validations?: FieldValidationsCustom & FieldValidationsListLength & FieldValidationsUnique;
}
export type FieldListItems = FieldListProps['items'];
export type FieldListItemsForType<Type extends FieldType> = ExtractByType<FieldListItems, Type>;
export type DistributeFieldListItemsBasicProps<Type extends FieldBasicProps> = Type extends FieldBasicProps
? {
type: 'list';
items: Type;
}
: never;
export type FieldListItemsBasicProps = DistributeFieldListItemsBasicProps<FieldBasicProps>;
export type DistributeFieldListItemsEnumProps<Type extends FieldEnumProps> = Type extends FieldEnumProps
? {
type: 'list';
items: Type;
} & FieldCustomControlTypeProps<'checkbox'>
: never;
export type FieldListItemsEnumProps = DistributeFieldListItemsEnumProps<FieldEnumProps>;
export interface FieldListItemsImageProps extends FieldCustomControlTypeProps {
type: 'list';
items: FieldImageProps;
}
export interface FieldListItemsNumberProps extends FieldCustomControlTypeProps {
type: 'list';
items: FieldNumberProps;
}
export interface FieldListItemsObjectProps extends FieldCustomControlTypeProps {
type: 'list';
items: FieldObjectProps;
}
export interface FieldListItemsModelProps extends FieldCustomControlTypeProps {
type: 'list';
items: FieldModelProps;
}
export interface FieldListItemsReferenceProps extends FieldCustomControlTypeProps {
type: 'list';
items: FieldReferenceProps;
}
export interface FieldListItemsCrossReferenceProps extends FieldCustomControlTypeProps {
type: 'list';
items: FieldCrossReferenceProps;
}
export type DocumentPreview = DocumentPreviewFunction | PreviewFields;
export type DocumentFieldPreview = ObjectPreviewFunction | PreviewFields;
export type DocumentPreviewFunction = (
options: {
document: DocumentWithSource;
currentLocale?: string;
} & ConfigDelegate
) => {
title?: string;
subtitle?: string;
image?: string;
};
export type ObjectPreviewFunction = (
options: {
parentDocument: DocumentWithSource;
documentField: DocumentModelField | DocumentObjectField;
currentLocale?: string;
} & ConfigDelegate
) => {
title?: string;
subtitle?: string;
image?: string;
};
export interface PreviewFields {
title?: string;
subtitle?: string;
image?: string;
}
export type FieldPath = (string | number)[];
// Field Validations
// =================
export type FieldValidationsCustom = {
/**
* The `validate` functions allows you to define custom validation logic for
* a field.
*/
validate?: FieldValidationsCustomFunction | FieldValidationsCustomFunction[];
};
export type FieldValidationsCustomFunction = (
options: {
updateOperation: UpdateOperation;
document?: DocumentWithSource;
model: ModelWithSource;
} & ConfigDelegate
) => Promise<true | string>;
export type FieldValidationsUnique = {
/**
* Use the `unique` property to make the field unique across all the
* documents of the same type.
*/
unique?: boolean;
errors?: {
unique?: string;
};
};
export type FieldValidationsRegExp = {
regexp?: string;
regexpNot?: string;
regexpPattern?: FieldValidationsRegExpPatterns;
errors?: {
regexp?: string;
regexpNot?: string;
regexpPattern?: string;
};
};
export type FieldValidationsRegExpPatterns =
| 'uppercase'
| 'lowercase'
| 'email'
| 'url'
| 'date-us'
| 'date-eu'
| 'time-12h'
| 'time-24h'
| 'phone-us'
| 'zip-code-us';
export type FieldValidationsStringLength = {
/** Minimum length of string (inclusive). */
min?: number;
/** Maximum length of string (inclusive). */
max?: number;
/** Exact length of string. */
exact?: number;
errors?: {
min?: string;
max?: string;
exact?: string;
};
};
export type FieldValidationsListLength = {
/** Minimum number of elements in array (inclusive). */
min?: number;
/** Maximum number of elements in array (inclusive). */
max?: number;
/** Exact number of elements in array. */
exact?: number;
errors?: {
min?: string;
max?: string;
exact?: string;
};
};
export type FieldValidationsDateRange = {
/** Minimum date (inclusive) */
min?: string;
/** Maximum date (inclusive). */
max?: string;
/** Minimum date (non-inclusive) */
after?: string;
/** Maximum date (non-inclusive). */
before?: string;
errors?: {
min?: string;
max?: string;
after?: string;
before?: string;
};
};
export type FieldValidationsNumberRange = {
/** Minimum value (inclusive). */
min?: number;
/** Maximum value (inclusive). */
max?: number;
/**
* Controls the step increments for fields with `controlType: "slider"`.
* When using the default control type, the value will be corrected upon save to the closest available step.
*/
step?: number;
/** Value must be less than the given limit. */
lessThan?: number;
/** Value must be greater than the given limit. */
greaterThan?: number;
errors?: {
min?: string;
max?: string;
step?: string;
lessThan?: string;
greaterThan?: string;
};
};
export type FieldValidationsImage = Omit<FieldValidationsFile, 'fileTypeGroups'> & {
/** Minimum image width in pixels (inclusive). */
minWidth?: number;
/** Maximum image width in pixels (inclusive). */
maxWidth?: number;
/** Minimum image height in pixels (inclusive). */
minHeight?: number;
/** Maximum image height in pixels (inclusive). */
maxHeight?: number;
/**
* The minimum ratio between image width and height i.e., min(width divided by height)
* Can be set to string specifying the ratio separated by a colon, or a fractional number.
* @example '4:3', 1.3333 => this will allow images with dimensions
* (w:600px, h:450px), (w:600px, h:300px), and will not allow images with
* dimensions (w:600px, h:600px), (w:300px, h:600px).
*/
minWidthToHeightRatio?: string | number;
/**
* The maximum ratio between image width and height i.e., max(width divided by height)
* Can be set to string specifying the ratio separated by a colon, or a fractional number.
* @example '2:1', 2 => this will allow images with dimensions
* (w:600px, h:300px), (w:400px, h:400px), and will not allow images with
* dimensions (w:600px, h:200px), (w:400px, h:100px).
*/
maxWidthToHeightRatio?: string | number;
errors?: {
minWidth?: string;
maxWidth?: string;
minHeight?: string;
maxHeight?: string;
minWidthToHeightRatio?: string;
maxWidthToHeightRatio?: string;
};
};
export type FieldValidationsFile = {
/** Minimum file size in bytes (inclusive). */
fileMinSize?: number;
/** Maximum file size in bytes (inclusive). */
fileMaxSize?: number;
/**
* Array of allowed file type extensions.
* @example ['gif', 'png', 'jpg', 'jpeg', 'webp', 'svg']
*/
fileTypes?: string[];
/**
* Array of allowed file type groups.
* The type groups contain predefined file types, and are added to the fileTypes array.
* @example ['image', 'video']
*/
fileTypeGroups?: FieldValidationsFileTypesGroup[];
errors?: {
fileMinSize?: string;
fileMaxSize?: string;
fileTypes?: string;
fileTypeGroups?: string;
};
};
export type FieldValidationsFileTypesGroup =
| 'image'
| 'video'
| 'audio'
| 'text'
| 'markup'
| 'code'
| 'document'
| 'presentation'
| 'spreadsheet'
| 'archive';