@keystatic/core

Search for an npm package
import { l as l10nMessages, G as useArrayFieldValidationMessage, H as ArrayFieldListView, I as FormValueContentFromPreviewProps, x as getInitialPropsValue, n as createGetPreviewProps, w as clientSideValidateProp, J as valueToUpdater, K as isValidURL, L as useIsInDocumentEditor, M as useObjectURL, N as getUploadedFile, O as getSrcPrefix, Q as ImageFieldInput, R as move, U as useFieldContext, V as document, W as mdx, X as markdoc } from './index-f0daff99.js';
export { $ as BlockWrapper, a0 as ToolbarSeparator, Z as collection, Y as config, _ as singleton } from './index-f0daff99.js';
export { c as component } from './api-06cf6298.js';
import { aX as basicFormFieldWithSimpleReaderParse, an as FieldDataError, aj as useImageLibraryURL, U as useEventCallback, z as useConfig, a_ as loadImageData, a$ as emptyImageData, b0 as UploadImageButton, b1 as ImageDimensionsInput, b2 as parseImageData, o as object, aY as text, b3 as assertRequired, aW as fixPath, _ as pluralize, a as useTree, b4 as SlugFieldContext, b5 as PathContext, b6 as validateText } from './index-041d8a0b.js';
export { aG as NotEditable } from './index-041d8a0b.js';
import { ActionButton, ButtonGroup, Button, ClearButton } from '@keystar/ui/button';
import { DialogContainer, Dialog, useDialogContainer } from '@keystar/ui/dialog';
import { FieldLabel, FieldMessage } from '@keystar/ui/field';
import { VStack, Flex, Box } from '@keystar/ui/layout';
import { MenuTrigger, Menu, Item } from '@keystar/ui/menu';
import { Content } from '@keystar/ui/slots';
import { Text, Heading } from '@keystar/ui/typography';
import { useLocalizedStringFormatter } from '@react-aria/i18n';
import { useField } from '@react-aria/label';
import { useState, useId, useMemo, useEffect, useReducer, useContext } from 'react';
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
import { Checkbox } from '@keystar/ui/checkbox';
import { NumberField } from '@keystar/ui/number-field';
import { TextField, TextArea } from '@keystar/ui/text-field';
import { Icon } from '@keystar/ui/icon';
import { imageIcon } from '@keystar/ui/icon/icons/imageIcon';
import { TextLink } from '@keystar/ui/link';
import { ProgressCircle } from '@keystar/ui/progress';
import { tokenSchema, css, containerQueries } from '@keystar/ui/style';
import '@braintree/sanitize-url';
import { isString } from 'emery';
import { Item as Item$1 } from '@react-stately/collections';
import { ActionBarContainer, ActionBar } from '@keystar/ui/action-bar';
import { Combobox, Item as Item$2 } from '@keystar/ui/combobox';
import { useDragAndDrop } from '@keystar/ui/drag-and-drop';
import { trash2Icon } from '@keystar/ui/icon/icons/trash2Icon';
import { ListView } from '@keystar/ui/list-view';
import Decimal from 'decimal.js';
import { filter } from 'minimatch';
import { Picker, Item as Item$3 } from '@keystar/ui/picker';
import slugify from '@sindresorhus/slugify';
import { refreshCwIcon } from '@keystar/ui/icon/icons/refreshCwIcon';
import { i as integer } from './index-6e2170a2.js';
import { u as useSlugsInCollection } from './useSlugsInCollection-dbe304b8.js';
import '@markdoc/markdoc';
import 'prosemirror-commands';
import 'prosemirror-state';
import 'prosemirror-transform';
import '@keystar/ui/editor';
import '@keystar/ui/icon/icons/boldIcon';
import '@keystar/ui/icon/icons/chevronDownIcon';
import '@keystar/ui/icon/icons/codeIcon';
import '@keystar/ui/icon/icons/italicIcon';
import '@keystar/ui/icon/icons/listIcon';
import '@keystar/ui/icon/icons/listOrderedIcon';
import '@keystar/ui/icon/icons/minusIcon';
import '@keystar/ui/icon/icons/plusIcon';
import '@keystar/ui/icon/icons/quoteIcon';
import '@keystar/ui/icon/icons/removeFormattingIcon';
import '@keystar/ui/icon/icons/strikethroughIcon';
import '@keystar/ui/icon/icons/tableIcon';
import '@keystar/ui/tooltip';
import '@keystar/ui/icon/icons/fileCodeIcon';
import '@keystar/ui/icon/icons/heading1Icon';
import '@keystar/ui/icon/icons/heading2Icon';
import '@keystar/ui/icon/icons/heading3Icon';
import '@keystar/ui/icon/icons/heading4Icon';
import '@keystar/ui/icon/icons/heading5Icon';
import '@keystar/ui/icon/icons/heading6Icon';
import '@keystar/ui/icon/icons/separatorHorizontalIcon';
import 'prosemirror-model';
import '@emotion/css';
import 'slate';
import 'slate-react';
import 'is-hotkey';
import '@keystar/ui/split-view';
import '@keystar/ui/icon/icons/panelLeftOpenIcon';
import '@keystar/ui/icon/icons/panelLeftCloseIcon';
import '@keystar/ui/icon/icons/panelRightOpenIcon';
import '@keystar/ui/icon/icons/panelRightCloseIcon';
import '@react-aria/overlays';
import '@react-aria/utils';
import '@react-stately/overlays';
import '@keystar/ui/badge';
import '@keystar/ui/nav-list';
import '@keystar/ui/overlays';
import '@keystar/ui/status-light';
import '@keystar/ui/utils';
import '@keystar/ui/core';
import '@ts-gql/tag/no-transform';
import 'urql';
import '@keystar/ui/avatar';
import '@keystar/ui/icon/icons/logOutIcon';
import '@keystar/ui/icon/icons/gitPullRequestIcon';
import '@keystar/ui/icon/icons/gitBranchPlusIcon';
import '@keystar/ui/icon/icons/githubIcon';
import '@keystar/ui/icon/icons/gitForkIcon';
import '@keystar/ui/icon/icons/monitorIcon';
import '@keystar/ui/icon/icons/moonIcon';
import '@keystar/ui/icon/icons/sunIcon';
import '@keystar/ui/icon/icons/userIcon';
import '@keystar/ui/icon/icons/gitBranchIcon';
import '@keystar/ui/radio';
import '@keystar/ui/toast';
import 'idb-keyval';
import '@keystar/ui/icon/icons/editIcon';
import '@keystar/ui/icon/icons/externalLinkIcon';
import '@keystar/ui/icon/icons/linkIcon';
import '@keystar/ui/icon/icons/unlinkIcon';
import '@keystar/ui/action-group';
import '@keystar/ui/icon/icons/maximizeIcon';
import '@keystar/ui/icon/icons/minimizeIcon';
import '@keystar/ui/icon/icons/subscriptIcon';
import '@keystar/ui/icon/icons/superscriptIcon';
import '@keystar/ui/icon/icons/typeIcon';
import '@keystar/ui/icon/icons/underlineIcon';
import '@keystar/ui/icon/icons/alignLeftIcon';
import '@keystar/ui/icon/icons/alignRightIcon';
import '@keystar/ui/icon/icons/alignCenterIcon';
import 'match-sorter';
import 'emery/assertions';
import 'yjs';
import '@keystar/ui/icon/icons/trashIcon';
import '@emotion/weak-memoize';
import '@keystar/ui/icon/icons/columnsIcon';
import '@keystar/ui/icon/icons/fileUpIcon';
import 'y-prosemirror';
import '@keystar/ui/icon/icons/sheetIcon';
import 'scroll-into-view-if-needed';
import '@react-stately/list';
import '@keystar/ui/listbox';
import 'slate-history';
import 'mdast-util-from-markdown';
import 'mdast-util-gfm-autolink-literal';
import 'micromark-extension-gfm-autolink-literal';
import 'mdast-util-gfm-strikethrough';
import 'micromark-extension-gfm-strikethrough';
import 'js-base64';
import 'prosemirror-view';
import '@react-aria/selection';
import 'prosemirror-tables';
import '@keystar/ui/icon/icons/pencilIcon';
import 'react-dom';
import 'prosemirror-history';
import 'prosemirror-keymap';
import 'escape-string-regexp';
import 'mdast-util-to-markdown';
import 'mdast-util-gfm';
import 'mdast-util-mdx';
import 'micromark-extension-gfm';
import 'micromark-extension-mdxjs';
import 'unist-util-visit';
import 'zod';
import '@keystar/ui/icon/icons/link2Icon';
import '@keystar/ui/icon/icons/link2OffIcon';
import '@keystar/ui/icon/icons/undo2Icon';
import 'y-protocols/awareness';
import '@toeverything/y-indexeddb';
import 'lib0/broadcastchannel';
import 'lib0/time';
import 'lib0/encoding';
import 'lib0/decoding';
import 'y-protocols/sync';
import 'y-protocols/auth';
import 'lib0/mutex';
import 'lib0/math';
import 'cookie';
import './hex-834c0b54.js';
import 'lru-cache';
import 'partysocket/ws';
import 'lib0/encoding.js';
function array(element, opts) {
var _opts$label;
return {
kind: 'array',
element,
label: (_opts$label = opts === null || opts === void 0 ? void 0 : opts.label) !== null && _opts$label !== void 0 ? _opts$label : 'Items',
description: opts === null || opts === void 0 ? void 0 : opts.description,
itemLabel: opts === null || opts === void 0 ? void 0 : opts.itemLabel,
asChildTag: opts === null || opts === void 0 ? void 0 : opts.asChildTag,
slugField: opts === null || opts === void 0 ? void 0 : opts.slugField,
validation: opts === null || opts === void 0 ? void 0 : opts.validation
};
}
function BlocksFieldInput(props) {
var _props$schema$validat, _props$schema$validat2;
const [modalState, setModalState] = useState({
kind: 'closed'
});
const dismiss = () => {
setModalState({
kind: 'closed'
});
};
const minLength = (_props$schema$validat = (_props$schema$validat2 = props.schema.validation) === null || _props$schema$validat2 === void 0 || (_props$schema$validat2 = _props$schema$validat2.length) === null || _props$schema$validat2 === void 0 ? void 0 : _props$schema$validat2.min) !== null && _props$schema$validat !== void 0 ? _props$schema$validat : 0;
const formId = useId();
const stringFormatter = useLocalizedStringFormatter(l10nMessages);
const errorMessage = useArrayFieldValidationMessage(props);
const {
descriptionProps,
errorMessageProps,
fieldProps: groupProps,
labelProps
} = useField({
description: props.schema.description,
errorMessage: errorMessage,
isInvalid: !!errorMessage,
label: props.schema.label,
labelElementType: 'span'
});
return /*#__PURE__*/jsxs(VStack, {
gap: "medium",
role: "group",
minWidth: 0,
...groupProps,
children: [/*#__PURE__*/jsx(FieldLabel, {
elementType: "span",
isRequired: minLength > 0,
supplementRequiredState: true,
...labelProps,
children: props.schema.label
}), props.schema.description && /*#__PURE__*/jsx(Text, {
size: "small",
color: "neutralSecondary",
...descriptionProps,
children: props.schema.description
}), /*#__PURE__*/jsxs(MenuTrigger, {
children: [/*#__PURE__*/jsx(ActionButton, {
alignSelf: "start",
children: "Add"
}), /*#__PURE__*/jsx(Menu, {
items: props.schema.element.discriminant.options,
onAction: discriminant => {
var _props$schema$element;
const val = (_props$schema$element = props.schema.element.discriminant.options.find(x => x.value.toString() === discriminant.toString())) === null || _props$schema$element === void 0 ? void 0 : _props$schema$element.value;
if (val === undefined) return;
setModalState({
kind: 'new',
discriminant: val
});
},
children: item => /*#__PURE__*/jsx(Item, {
children: item.label
}, item.value.toString())
})]
}), /*#__PURE__*/jsx(ArrayFieldListView, {
...props,
"aria-label": props.schema.label,
onOpenItem: idx => {
setModalState({
kind: 'edit',
idx
});
}
}), errorMessage && /*#__PURE__*/jsx(FieldMessage, {
...errorMessageProps,
children: errorMessage
}), /*#__PURE__*/jsx(DialogContainer, {
onDismiss: dismiss,
children: (_props$schema$element3 => {
if (modalState.kind === 'closed') {
return null;
}
if (modalState.kind === 'edit') {
var _props$schema$element2;
const idx = modalState.idx;
const previewProps = props.elements[idx].value;
const {
discriminant
} = props.elements[idx];
return /*#__PURE__*/jsxs(Dialog, {
children: [/*#__PURE__*/jsxs(Heading, {
children: ["Edit", ' ', (_props$schema$element2 = props.schema.element.discriminant.options.find(x => x.value === discriminant)) === null || _props$schema$element2 === void 0 ? void 0 : _props$schema$element2.label]
}), /*#__PURE__*/jsx(BlocksEditItemModalContent, {
formId: formId,
onClose: dismiss,
previewProps: previewProps,
modalStateIndex: idx
}), /*#__PURE__*/jsx(ButtonGroup, {
children: /*#__PURE__*/jsx(Button, {
form: formId,
prominence: "high",
type: "submit",
children: "Done"
})
})]
});
}
const discriminant = modalState.discriminant;
return /*#__PURE__*/jsxs(Dialog, {
children: [/*#__PURE__*/jsxs(Heading, {
children: ["Add", (_props$schema$element3 = props.schema.element.discriminant.options.find(x => x.value === discriminant)) === null || _props$schema$element3 === void 0 ? void 0 : _props$schema$element3.label]
}), /*#__PURE__*/jsx(Content, {
children: /*#__PURE__*/jsx(BlocksAddItemModalContent, {
discriminant: discriminant,
formId: formId,
previewProps: props
})
}), /*#__PURE__*/jsxs(ButtonGroup, {
children: [/*#__PURE__*/jsx(Button, {
onPress: dismiss,
children: stringFormatter.format('cancel')
}), /*#__PURE__*/jsx(Button, {
form: formId,
prominence: "high",
type: "submit",
children: stringFormatter.format('add')
})]
})]
});
})()
})]
});
}
function BlocksEditItemModalContent(props) {
return /*#__PURE__*/jsx(Content, {
children: /*#__PURE__*/jsx(VStack, {
id: props.formId,
elementType: "form",
onSubmit: event => {
if (event.target !== event.currentTarget) return;
event.preventDefault();
props.onClose();
},
gap: "xxlarge",
children: /*#__PURE__*/jsx(FormValueContentFromPreviewProps, {
autoFocus: true,
...props.previewProps
})
})
});
}
function BlocksAddItemModalContent(props) {
const schema = props.previewProps.schema.element.values[props.discriminant.toString()];
const [value, setValue] = useState(() => getInitialPropsValue(schema));
const [forceValidation, setForceValidation] = useState(false);
const previewProps = useMemo(() => createGetPreviewProps(schema, setValue, () => undefined), [schema, setValue])(value);
const {
dismiss
} = useDialogContainer();
return /*#__PURE__*/jsx(VStack, {
id: props.formId,
elementType: "form",
onSubmit: event => {
if (event.target !== event.currentTarget) return;
event.preventDefault();
if (!clientSideValidateProp(schema, value, undefined)) {
setForceValidation(true);
return;
}
props.previewProps.onChange([...props.previewProps.elements.map(x => ({
key: x.key
})), {
key: undefined,
value: valueToUpdater({
value,
discriminant: props.discriminant
}, props.previewProps.schema.element)
}]);
dismiss();
},
gap: "xxlarge",
children: /*#__PURE__*/jsx(FormValueContentFromPreviewProps, {
forceValidation: forceValidation,
autoFocus: true,
...previewProps
})
});
}
function blocks(blocks, opts) {
const entries = Object.entries(blocks);
if (!entries.length) {
throw new Error('fields.blocks must have at least one entry');
}
const select$1 = select({
label: 'Kind',
defaultValue: entries[0][0],
options: Object.entries(blocks).map(([key, {
label
}]) => ({
label,
value: key
}))
});
const element = conditional(select$1, Object.fromEntries(entries.map(([key, {
schema
}]) => [key, schema])));
return {
...array(element, {
label: opts.label,
description: opts.description,
validation: opts.validation,
itemLabel(props) {
const kind = props.discriminant;
const block = blocks[kind];
if (!block.itemLabel) return block.label;
return block.itemLabel(props.value);
}
}),
Input: BlocksFieldInput
};
}
function CheckboxFieldInput(props) {
return /*#__PURE__*/jsxs(Checkbox, {
isSelected: props.value,
onChange: props.onChange,
autoFocus: props.autoFocus,
children: [/*#__PURE__*/jsx(Text, {
children: props.label
}), props.description && /*#__PURE__*/jsx(Text, {
slot: "description",
children: props.description
})]
});
}
function checkbox({
label,
defaultValue = false,
description
}) {
return basicFormFieldWithSimpleReaderParse({
label,
Input(props) {
return /*#__PURE__*/jsx(CheckboxFieldInput, {
...props,
label: label,
description: description
});
},
defaultValue() {
return defaultValue;
},
parse(value) {
if (value === undefined) return defaultValue;
if (typeof value !== 'boolean') {
throw new FieldDataError('Must be a boolean');
}
return value;
},
validate(value) {
return value;
},
serialize(value) {
return {
value
};
}
});
}
function child(options) {
return {
kind: 'child',
options: options.kind === 'block' ? {
...options,
dividers: options.dividers,
formatting: options.formatting === 'inherit' ? {
blockTypes: 'inherit',
headingLevels: 'inherit',
inlineMarks: 'inherit',
listTypes: 'inherit',
alignment: 'inherit',
softBreaks: 'inherit'
} : options.formatting,
links: options.links,
images: options.images,
tables: options.tables,
componentBlocks: options.componentBlocks
} : {
kind: 'inline',
placeholder: options.placeholder,
formatting: options.formatting === 'inherit' ? {
inlineMarks: 'inherit',
softBreaks: 'inherit'
} : options.formatting,
links: options.links
}
};
}
function ImageField(props) {
const {
image,
onChange
} = props;
const [status, setStatus] = useState(image.src ? 'good' : '');
const imageLibraryURL = useImageLibraryURL();
const onPaste = event => {
event.preventDefault();
const text = event.clipboardData.getData('text/plain');
const parsed = parseImageData(text);
props.onChange(parsed);
};
const onLoad = useEventCallback(data => {
onChange(data);
setStatus('good');
});
const config = useConfig();
const hasSetFields = !!(props.image.alt || props.image.width || props.image.height);
useEffect(() => {
if (!props.image.src) {
setStatus('');
return;
}
if (!isValidURL(props.image.src)) {
return;
}
if (hasSetFields) {
setStatus('good');
return;
}
setStatus('loading');
loadImageData(props.image.src, config).then(newData => {
onLoad(newData);
}).catch(() => {
setStatus('error');
});
}, [config, hasSetFields, onLoad, props.image.src]);
const [blurred, setBlurred] = useState(false);
const errorMessage = (blurred || props.forceValidation) && props.isRequired && !image.src ? 'Image URL is required.' : undefined;
return /*#__PURE__*/jsxs(VStack, {
gap: "xlarge",
padding: "large",
children: [/*#__PURE__*/jsxs(VStack, {
gap: "medium",
children: [/*#__PURE__*/jsx(TextField, {
label: "Image URL",
isRequired: props.isRequired,
errorMessage: errorMessage,
autoFocus: props.autoFocus,
onPaste: onPaste,
onKeyDown: e => {
if (e.code === 'Backspace' || e.code === 'Delete') {
props.onChange(emptyImageData);
}
},
onBlur: () => setBlurred(true),
value: image.src,
description: /*#__PURE__*/jsxs(Text, {
children: ["Upload an image, or copy a URL from the", ' ', /*#__PURE__*/jsx(TextLink, {
prominence: "high",
href: imageLibraryURL,
target: "_blank",
rel: "noreferrer",
children: "Image\xA0Library"
}), ' ', "and paste it into this field."]
}),
endElement: status === 'loading' ? /*#__PURE__*/jsx(Flex, {
height: "element.regular",
width: "element.regular",
alignItems: "center",
justifyContent: "center",
children: /*#__PURE__*/jsx(ProgressCircle, {
size: "small",
"aria-label": "Checking\u2026",
isIndeterminate: true
})
}) : image.src ? /*#__PURE__*/jsx(ClearButton, {
onPress: () => {
props.onChange(emptyImageData);
setStatus('');
},
preventFocus: true
}) : null
}), /*#__PURE__*/jsx(UploadImageButton, {
alignSelf: "start",
onUploaded: data => {
onChange(data);
}
})]
}), status === 'good' ? /*#__PURE__*/jsxs(Fragment, {
children: [/*#__PURE__*/jsx(Box, {
alignSelf: "start",
backgroundColor: "canvas",
borderRadius: "regular",
border: "neutral",
padding: "regular",
children: /*#__PURE__*/jsx("img", {
alt: image.alt,
src: image.src,
style: {
display: 'block',
maxHeight: tokenSchema.size.alias.singleLineWidth,
maxWidth: '100%'
}
})
}), /*#__PURE__*/jsx(TextArea, {
label: "Alt text",
value: image.alt,
onChange: alt => props.onChange({
...image,
alt
})
}), /*#__PURE__*/jsx(ImageDimensionsInput, {
src: image.src,
image: image,
onChange: dimensions => {
onChange({
...props.image,
...dimensions
});
}
})]
}) : /*#__PURE__*/jsxs(VStack, {
"aria-hidden": true,
alignItems: "center",
backgroundColor: "surface",
borderRadius: "regular",
gap: "medium",
paddingX: "large",
paddingY: "xlarge",
children: [/*#__PURE__*/jsx(Icon, {
src: imageIcon,
color: "neutralSecondary",
size: "medium"
}), /*#__PURE__*/jsx(Text, {
align: "center",
color: "neutralSecondary",
size: "small",
children: "Awaiting URL to display image preview and information\u2026"
})]
})]
});
}
function CloudImageFieldInput(props) {
var _props$fields$width$v, _props$fields$height$;
const labelId = useId();
const descriptionId = useId();
return /*#__PURE__*/jsxs(VStack, {
"aria-labelledby": labelId,
"aria-describedby": props.schema.description ? descriptionId : undefined,
border: "muted",
borderRadius: "medium",
minWidth: 0,
role: "group",
children: [/*#__PURE__*/jsxs(VStack, {
backgroundColor: "surface",
borderBottom: "muted",
borderTopStartRadius: "medium",
borderTopEndRadius: "medium",
gap: "medium",
minWidth: 0,
padding: "large",
children: [/*#__PURE__*/jsx(Text, {
color: "neutralEmphasis",
size: "medium",
weight: "medium",
id: labelId,
children: props.schema.label
}), !!props.schema.description && /*#__PURE__*/jsx(Text, {
id: descriptionId,
size: "regular",
color: "neutralSecondary",
children: props.schema.description
})]
}), /*#__PURE__*/jsx(ImageField, {
image: {
src: props.fields.src.value,
alt: props.fields.alt.value,
width: (_props$fields$width$v = props.fields.width.value) !== null && _props$fields$width$v !== void 0 ? _props$fields$width$v : undefined,
height: (_props$fields$height$ = props.fields.height.value) !== null && _props$fields$height$ !== void 0 ? _props$fields$height$ : undefined
},
onChange: data => {
var _data$width, _data$height;
props.onChange({
src: data.src,
alt: data.alt,
width: (_data$width = data.width) !== null && _data$width !== void 0 ? _data$width : null,
height: (_data$height = data.height) !== null && _data$height !== void 0 ? _data$height : null
});
},
autoFocus: props.autoFocus,
isRequired: props.isRequired,
forceValidation: props.forceValidation
})]
});
}
function cloudImage({
label,
description,
validation
}) {
return {
...object({
src: text({
label: 'URL',
validation: {
length: {
min: validation !== null && validation !== void 0 && validation.isRequired ? 1 : 0
}
}
}),
alt: text({
label: 'Alt text'
}),
height: integer({
label: 'Height'
}),
width: integer({
label: 'Width'
})
}, {
label,
description
}),
Input(props) {
return /*#__PURE__*/jsx(CloudImageFieldInput, {
...props,
isRequired: validation === null || validation === void 0 ? void 0 : validation.isRequired
});
}
};
}
function conditional(discriminant, values) {
return {
kind: 'conditional',
discriminant,
values: values
};
}
function validateDate(validation, value, label) {
if (value !== null && !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
return `${label} is not a valid date`;
}
if (validation !== null && validation !== void 0 && validation.isRequired && value === null) {
return `${label} is required`;
}
if ((validation !== null && validation !== void 0 && validation.min || validation !== null && validation !== void 0 && validation.max) && value !== null) {
const date = new Date(value);
if ((validation === null || validation === void 0 ? void 0 : validation.min) !== undefined) {
const min = new Date(validation.min);
if (date < min) {
return `${label} must be after ${min.toLocaleDateString()}`;
}
}
if ((validation === null || validation === void 0 ? void 0 : validation.max) !== undefined) {
const max = new Date(validation.max);
if (date > max) {
return `${label} must be no later than ${max.toLocaleDateString()}`;
}
}
}
}
function DateFieldInput(props) {
var _props$validation;
const [blurred, onBlur] = useReducer(() => true, false);
return /*#__PURE__*/jsx(TextField, {
label: props.label,
description: props.description,
type: "date",
onChange: val => {
props.onChange(val === '' ? null : val);
},
autoFocus: props.autoFocus,
value: props.value === null ? '' : props.value,
onBlur: onBlur,
isRequired: (_props$validation = props.validation) === null || _props$validation === void 0 ? void 0 : _props$validation.isRequired,
errorMessage: blurred || props.forceValidation ? validateDate(props.validation, props.value, props.label) : undefined
});
}
function date({
label,
defaultValue,
validation,
description
}) {
return basicFormFieldWithSimpleReaderParse({
label,
Input(props) {
return /*#__PURE__*/jsx(DateFieldInput, {
validation: validation,
label: label,
description: description,
...props
});
},
defaultValue() {
if (defaultValue === undefined) {
return null;
}
if (typeof defaultValue === 'string') {
return defaultValue;
}
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, '0');
const day = String(today.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
},
parse(value) {
if (value === undefined) {
return null;
}
if (value instanceof Date) {
const year = value.getUTCFullYear();
const month = String(value.getUTCMonth() + 1).padStart(2, '0');
const day = String(value.getUTCDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
if (typeof value !== 'string') {
throw new FieldDataError('Must be a string');
}
return value;
},
serialize(value) {
if (value === null) return {
value: undefined
};
const date = new Date(value);
date.toISOString = () => value;
date.toString = () => value;
return {
value: date
};
},
validate(value) {
const message = validateDate(validation, value, label);
if (message !== undefined) {
throw new FieldDataError(message);
}
assertRequired(value, validation, label);
return value;
}
});
}
function validateDatetime(validation, value, label) {
if (value !== null && !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(value)) {
return `${label} is not a valid datetime`;
}
if (validation !== null && validation !== void 0 && validation.isRequired && value === null) {
return `${label} is required`;
}
if ((validation !== null && validation !== void 0 && validation.min || validation !== null && validation !== void 0 && validation.max) && value !== null) {
const datetime = new Date(value);
if ((validation === null || validation === void 0 ? void 0 : validation.min) !== undefined) {
const min = new Date(validation.min);
if (datetime < min) {
return `${label} must be after ${min.toISOString()}`;
}
}
if ((validation === null || validation === void 0 ? void 0 : validation.max) !== undefined) {
const max = new Date(validation.max);
if (datetime > max) {
return `${label} must be no later than ${max.toISOString()}`;
}
}
}
}
//ui.tsx
function DatetimeFieldInput(props) {
var _props$validation;
const [blurred, onBlur] = useReducer(() => true, false);
return /*#__PURE__*/jsx(TextField, {
label: props.label,
description: props.description,
type: "datetime-local",
onChange: val => {
props.onChange(val === '' ? null : val);
},
autoFocus: props.autoFocus,
value: props.value === null ? '' : props.value,
onBlur: onBlur,
isRequired: (_props$validation = props.validation) === null || _props$validation === void 0 ? void 0 : _props$validation.isRequired,
errorMessage: blurred || props.forceValidation ? validateDatetime(props.validation, props.value, props.label) : undefined
});
}
function datetime({
label,
defaultValue,
validation,
description
}) {
return basicFormFieldWithSimpleReaderParse({
label,
Input(props) {
return /*#__PURE__*/jsx(DatetimeFieldInput, {
validation: validation,
label: label,
description: description,
...props
});
},
defaultValue() {
if (defaultValue === undefined) {
return null;
}
if (typeof defaultValue === 'string') {
return defaultValue;
}
if (defaultValue.kind === 'now') {
const now = new Date();
return new Date(now.getTime() - now.getTimezoneOffset() * 60 * 1000).toISOString().slice(0, -8);
}
return null;
},
parse(value) {
if (value === undefined) {
return null;
}
if (value instanceof Date) {
return value.toISOString().slice(0, -8);
}
if (typeof value !== 'string') {
throw new FieldDataError('Must be a string or date');
}
return value;
},
serialize(value) {
if (value === null) return {
value: undefined
};
const date = new Date(value + 'Z');
date.toJSON = () => date.toISOString().slice(0, -8);
date.toString = () => date.toISOString().slice(0, -8);
return {
value: date
};
},
validate(value) {
const message = validateDatetime(validation, value, label);
if (message !== undefined) {
throw new FieldDataError(message);
}
assertRequired(value, validation, label);
return value;
}
});
}
function empty() {
return basicFormFieldWithSimpleReaderParse({
Input() {
return null;
},
defaultValue() {
return null;
},
parse() {
return null;
},
serialize() {
return {
value: undefined
};
},
validate(value) {
return value;
},
label: 'Empty'
});
}
function emptyDocument() {
return {
kind: 'form',
formKind: 'content',
Input() {
return null;
},
defaultValue() {
return null;
},
parse() {
return null;
},
contentExtension: '.mdoc',
serialize() {
return {
value: undefined,
content: new Uint8Array(),
external: new Map(),
other: new Map()
};
},
validate(value) {
return value;
},
reader: {
parse() {
return null;
}
}
};
}
function FileFieldInput(props) {
var _props$validation, _props$validation2;
const {
value
} = props;
const [blurred, onBlur] = useReducer(() => true, false);
const isInEditor = useIsInDocumentEditor();
const objectUrl = useObjectURL(value === null ? null : value.data, undefined);
const labelId = useId();
const descriptionId = useId();
return /*#__PURE__*/jsxs(Flex, {
"aria-describedby": props.description ? descriptionId : undefined,
"aria-labelledby": labelId,
direction: "column",
gap: "medium",
role: "group",
children: [/*#__PURE__*/jsx(FieldLabel, {
id: labelId,
elementType: "span",
isRequired: (_props$validation = props.validation) === null || _props$validation === void 0 ? void 0 : _props$validation.isRequired,
children: props.label
}), props.description && /*#__PURE__*/jsx(Text, {
size: "small",
color: "neutralSecondary",
id: descriptionId,
children: props.description
}), /*#__PURE__*/jsxs(ButtonGroup, {
children: [/*#__PURE__*/jsx(ActionButton, {
onPress: async () => {
const file = await getUploadedFile('');
if (file) {
var _file$filename$match$, _file$filename$match;
props.onChange({
data: file.content,
filename: file.filename,
extension: (_file$filename$match$ = (_file$filename$match = file.filename.match(/\.([^.]+$)/)) === null || _file$filename$match === void 0 ? void 0 : _file$filename$match[1]) !== null && _file$filename$match$ !== void 0 ? _file$filename$match$ : ''
});
}
},
children: "Choose file"
}), value !== null && /*#__PURE__*/jsxs(Fragment, {
children: [/*#__PURE__*/jsx(ActionButton, {
prominence: "low",
onPress: () => {
props.onChange(null);
onBlur();
},
children: "Remove"
}), objectUrl && /*#__PURE__*/jsx(Button, {
href: objectUrl,
download: value.filename,
prominence: "low",
children: "Download"
})]
})]
}), isInEditor && value !== null && /*#__PURE__*/jsx(TextField, {
label: "Filename",
onChange: filename => {
props.onChange({
...value,
filename
});
},
value: value.filename
}), (props.forceValidation || blurred) && ((_props$validation2 = props.validation) === null || _props$validation2 === void 0 ? void 0 : _props$validation2.isRequired) && value === null && /*#__PURE__*/jsxs(FieldMessage, {
children: [props.label, " is required"]
})]
});
}
function file({
label,
directory,
validation,
description,
publicPath
}) {
return {
kind: 'form',
formKind: 'asset',
label,
Input(props) {
return /*#__PURE__*/jsx(FileFieldInput, {
label: label,
description: description,
validation: validation,
...props
});
},
defaultValue() {
return null;
},
filename(value, args) {
if (typeof value === 'string') {
return value.slice(getSrcPrefix(publicPath, args.slug).length);
}
return undefined;
},
parse(value, args) {
var _value$match$, _value$match;
if (value === undefined) {
return null;
}
if (typeof value !== 'string') {
throw new FieldDataError('Must be a string');
}
if (args.asset === undefined) {
return null;
}
return {
data: args.asset,
filename: value.slice(getSrcPrefix(publicPath, args.slug).length),
extension: (_value$match$ = (_value$match = value.match(/\.([^.]+$)/)) === null || _value$match === void 0 ? void 0 : _value$match[1]) !== null && _value$match$ !== void 0 ? _value$match$ : ''
};
},
validate(value) {
assertRequired(value, validation, label);
return value;
},
serialize(value, args) {
if (value === null) {
return {
value: undefined,
asset: undefined
};
}
const filename = args.suggestedFilenamePrefix ? args.suggestedFilenamePrefix + '.' + value.extension : value.filename;
return {
value: `${getSrcPrefix(publicPath, args.slug)}${filename}`,
asset: {
filename,
content: value.data
}
};
},
directory: directory ? fixPath(directory) : undefined,
reader: {
parse(value) {
if (typeof value !== 'string' && value !== undefined) {
throw new FieldDataError('Must be a string');
}
const val = value === undefined ? null : value;
assertRequired(val, validation, label);
return val;
}
}
};
}
function image({
label,
directory,
validation,
description,
publicPath
}) {
return {
kind: 'form',
formKind: 'asset',
label,
Input(props) {
return /*#__PURE__*/jsx(ImageFieldInput, {
label: label,
description: description,
validation: validation,
...props
});
},
defaultValue() {
return null;
},
filename(value, args) {
if (typeof value === 'string') {
return value.slice(getSrcPrefix(publicPath, args.slug).length);
}
return undefined;
},
parse(value, args) {
var _value$match$, _value$match;
if (value === undefined) {
return null;
}
if (typeof value !== 'string') {
throw new FieldDataError('Must be a string');
}
if (args.asset === undefined) {
return null;
}
return {
data: args.asset,
filename: value.slice(getSrcPrefix(publicPath, args.slug).length),
extension: (_value$match$ = (_value$match = value.match(/\.([^.]+$)/)) === null || _value$match === void 0 ? void 0 : _value$match[1]) !== null && _value$match$ !== void 0 ? _value$match$ : ''
};
},
validate(value) {
assertRequired(value, validation, label);
return value;
},
serialize(value, args) {
if (value === null) {
return {
value: undefined,
asset: undefined
};
}
const filename = args.suggestedFilenamePrefix ? args.suggestedFilenamePrefix + '.' + value.extension : value.filename;
return {
value: `${getSrcPrefix(publicPath, args.slug)}${filename}`,
asset: {
filename,
content: value.data
}
};
},
directory: directory ? fixPath(directory) : undefined,
reader: {
parse(value) {
if (typeof value !== 'string' && value !== undefined) {
throw new FieldDataError('Must be a string');
}
const val = value === undefined ? null : value;
assertRequired(val, validation, label);
return val;
}
}
};
}
function validateMultiRelationshipLength(validation, value) {
var _validation$length$mi, _validation$length, _validation$length$ma, _validation$length2;
const minLength = (_validation$length$mi = validation === null || validation === void 0 || (_validation$length = validation.length) === null || _validation$length === void 0 ? void 0 : _validation$length.min) !== null && _validation$length$mi !== void 0 ? _validation$length$mi : 0;
if (value.length < minLength) {
return `Must have at least ${pluralize(minLength, {
singular: 'item'
})}.`;
}
const maxLength = (_validation$length$ma = validation === null || validation === void 0 || (_validation$length2 = validation.length) === null || _validation$length2 === void 0 ? void 0 : _validation$length2.max) !== null && _validation$length$ma !== void 0 ? _validation$length$ma : Infinity;
if (value.length > maxLength) {
return `Must have at most ${pluralize(maxLength, {
singular: 'item'
})}.`;
}
}
function MultiRelationshipInput(props) {
var _props$validation;
const valAsObjects = useMemo(() => {
return props.value.map(key => ({
key
}));
}, [props.value]);
const [blurred, onBlur] = useReducer(() => true, false);
const slugs = useSlugsInCollection(props.collection);
const options = useMemo(() => {
return slugs.map(slug => ({
slug
}));
}, [slugs]);
const _errorMessage = (props.forceValidation || blurred) && validateMultiRelationshipLength(props.validation, props.value);
// this state & effect shouldn't really exist
// it's here because react-aria/stately calls onSelectionChange with null
// after selecting an item if we immediately remove the error message
// so we delay it with an effect
const [errorMessage, setErrorMessage] = useState(_errorMessage);
useEffect(() => {
setErrorMessage(_errorMessage);
}, [_errorMessage]);
const items = useMemo(() => {
const elementSet = new Set(props.value);
return options.filter(option => !elementSet.has(option.slug));
}, [props.value, options]);
return /*#__PURE__*/jsxs(VStack, {
gap: "medium",
minWidth: 0,
children: [/*#__PURE__*/jsx(Combobox, {
label: props.label,
description: props.description,
selectedKey: null,
placeholder: items.length === 0 ? 'All selected' : undefined,
onSelectionChange: key => {
if (typeof key === 'string') {
props.onChange([...props.value, key]);
}
},
disabledKeys: ['No more items…'],
onBlur: onBlur,
autoFocus: props.autoFocus,
defaultItems: items.length ? items : [{
slug: 'No more items…'
}],
isReadOnly: items.length === 0,
isRequired: ((_props$validation = props.validation) === null || _props$validation === void 0 || (_props$validation = _props$validation.length) === null || _props$validation === void 0 ? void 0 : _props$validation.min) !== undefined && props.validation.length.min >= 1,
errorMessage: errorMessage,
width: "auto",
children: item => /*#__PURE__*/jsx(Item$1, {
children: item.slug
}, item.slug)
}), /*#__PURE__*/jsx(MultiRelationshipListView, {
autoFocus: props.autoFocus,
forceValidation: props.forceValidation,
onChange: value => {
props.onChange(value.map(x => x.key));
},
elements: valAsObjects,
"aria-label": props.label
}) ]
});
}
function MultiRelationshipListView(props) {
const [selectedKeys, setSelectedKeys] = useState(() => new Set([]));
let onMove = (keys, target) => {
const targetIndex = props.elements.findIndex(x => x.key === target.key);
if (targetIndex === -1) return;
const allKeys = props.elements.map(x => ({
key: x.key
}));
const indexToMoveTo = target.dropPosition === 'before' ? targetIndex : targetIndex + 1;
const indices = keys.map(key => allKeys.findIndex(x => x.key === key));
props.onChange(move(allKeys, indices, indexToMoveTo));
};
const dragType = useMemo(() => Math.random().toString(36), []);
let {
dragAndDropHooks
} = useDragAndDrop({
getItems(keys) {
// Use a drag type so the items can only be reordered within this list
// and not dragged elsewhere.
return [...keys].map(key => {
key = JSON.stringify(key);
return {
[dragType]: key,
'text/plain': key
};
});
},
getAllowedDropOperations() {
return ['move', 'cancel'];
},
async onDrop(e) {
if (e.target.type !== 'root' && e.target.dropPosition !== 'on') {
let keys = [];
for (let item of e.items) {
if (item.kind === 'text') {
let key;
if (item.types.has(dragType)) {
key = JSON.parse(await item.getText(dragType));
keys.push(key);
} else if (item.types.has('text/plain')) {
// Fallback for Chrome Android case: https://bugs.chromium.org/p/chromium/issues/detail?id=1293803
// Multiple drag items are contained in a single string so we need to split them out
key = await item.getText('text/plain');
keys = key.split('\n').map(val => val.replaceAll('"', ''));
}
}
}
onMove(keys, e.target);
}
},
getDropOperation(target) {
if (target.type === 'root' || target.dropPosition === 'on') {
return 'cancel';
}
return 'move';
}
});
return /*#__PURE__*/jsxs(ActionBarContainer, {
maxHeight: "scale.3400",
minHeight: "scale.1600",
children: [/*#__PURE__*/jsx(ListView, {
"aria-label": props['aria-label'],
items: props.elements,
dragAndDropHooks: dragAndDropHooks,
selectionMode: "multiple",
onSelectionChange: setSelectedKeys,
selectedKeys: selectedKeys,
renderEmptyState: arrayFieldEmptyState
// density="compact"
,
UNSAFE_className: css({
borderRadius: tokenSchema.size.radius.regular
}),
children: item => {
const label = item.key;
return /*#__PURE__*/jsx(Item$1, {
textValue: label,
children: label
}, item.key);
}
}), /*#__PURE__*/jsx(ActionBar, {
selectedItemCount: selectedKeys === 'all' ? 'all' : selectedKeys.size,
onClearSelection: () => setSelectedKeys(new Set()),
onAction: key => {
if (key === 'delete') {
let newItems = props.elements;
if (selectedKeys instanceof Set) {
newItems = props.elements.filter(item => !selectedKeys.has(item.key));
} else if (selectedKeys === 'all') {
newItems = [];
}
props.onChange(newItems);
setSelectedKeys(new Set());
}
},
children: /*#__PURE__*/jsxs(Item$1, {
textValue: "Remove",
children: [/*#__PURE__*/jsx(Icon, {
src: trash2Icon
}), /*#__PURE__*/jsx(Text, {
children: "Remove"
})]
}, "delete")
})]
});
}
function arrayFieldEmptyState() {
return /*#__PURE__*/jsx(VStack, {
gap: "large",
alignItems: "center",
justifyContent: "center",
height: "100%",
padding: "regular",
children: /*#__PURE__*/jsx(Text, {
align: "center",
color: "neutralTertiary",
children: "No items selected\u2026"
})
});
}
function multiRelationship({
label,
collection,
validation,
description
}) {
return basicFormFieldWithSimpleReaderParse({
label,
Input(props) {
return /*#__PURE__*/jsx(MultiRelationshipInput, {
label: label,
collection: collection,
description: description,
validation: validation,
...props
});
},
defaultValue() {
return [];
},
parse(value) {
if (value === undefined) {
return [];
}
if (!Array.isArray(value) || !value.every(isString)) {
throw new FieldDataError('Must be an array of strings');
}
return value;
},
validate(value) {
const error = validateMultiRelationshipLength(validation, value);
if (error) {
throw new FieldDataError(error);
}
return value;
},
serialize(value) {
return {
value
};
}
});
}
function MultiselectFieldInput(props) {
const labelId = useId();
const descriptionId = useId();
return /*#__PURE__*/jsxs(Flex, {
role: "group",
"aria-labelledby": labelId,
"aria-describedby": props.description ? descriptionId : undefined,
direction: "column",
gap: "medium",
children: [/*#__PURE__*/jsx(FieldLabel, {
elementType: "span",
id: labelId,
children: props.label
}), props.description && /*#__PURE__*/jsx(Text, {
id: descriptionId,
size: "small",
color: "neutralSecondary",
children: props.description
}), props.options.map(option => /*#__PURE__*/jsx(Checkbox, {
isSelected: props.value.includes(option.value),
onChange: () => {
if (props.value.includes(option.value)) {
props.onChange(props.value.filter(x => x !== option.value));
} else {
props.onChange([...props.value, option.value]);
}
},
children: option.label
}, option.value))]
});
}
function multiselect({
label,
options,
defaultValue = [],
description
}) {
const valuesToOption = new Map(options.map(x => [x.value, x]));
const field = basicFormFieldWithSimpleReaderParse({
label,
Input(props) {
return /*#__PURE__*/jsx(MultiselectFieldInput, {
label: label,
description: description,
options: options,
...props
});
},
defaultValue() {
return defaultValue;
},
parse(value) {
if (value === undefined) {
return [];
}
if (!Array.isArray(value)) {
throw new FieldDataError('Must be an array of options');
}
if (!value.every(x => typeof x === 'string' && valuesToOption.has(x))) {
throw new FieldDataError(`Must be an array with one of ${options.map(x => x.value).join(', ')}`);
}
return value;
},
validate(value) {
return value;
},
serialize(value) {
return {
value
};
}
});
return {
...field,
options
};
}
function validateNumber(validation, value, step, label) {
if (value !== null && typeof value !== 'number') {
return `${label} must be a number`;
}
if (validation !== null && validation !== void 0 && validation.isRequired && value === null) {
return `${label} is required`;
}
if (value !== null) {
if ((validation === null || validation === void 0 ? void 0 : validation.min) !== undefined && value < validation.min) {
return `${label} must be at least ${validation.min}`;
}
if ((validation === null || validation === void 0 ? void 0 : validation.max) !== undefined && value > validation.max) {
return `${label} must be at most ${validation.max}`;
}
if (step !== undefined && (validation === null || validation === void 0 ? void 0 : validation.validateStep) !== undefined && new Decimal(value).mod(new Decimal(step)).toNumber() !== 0) {
return `${label} must be a multiple of ${step}`;
}
}
}
function NumberFieldInput(props) {
var _props$validation;
const [blurred, onBlur] = useReducer(() => true, false);
return /*#__PURE__*/jsx(NumberField, {
label: props.label,
description: props.description,
isRequired: (_props$validation = props.validation) === null || _props$validation === void 0 ? void 0 : _props$validation.isRequired,
errorMessage: props.forceValidation || blurred ? validateNumber(props.validation, props.value, props.step, props.label) : undefined,
onBlur: onBlur,
autoFocus: props.autoFocus,
step: props.step,
value: props.value === null ? undefined : props.value,
onChange: val => {
props.onChange(val === undefined ? null : val);
}
});
}
function number({
label,
defaultValue,
step,
validation,
description
}) {
return basicFormFieldWithSimpleReaderParse({
label,
Input(props) {
return /*#__PURE__*/jsx(NumberFieldInput, {
label: label,
description: description,
validation: validation,
step: step,
...props
});
},
defaultValue() {
return defaultValue !== null && defaultValue !== void 0 ? defaultValue : null;
},
parse(value) {
if (value === undefined) {
return null;
}
if (typeof value === 'number') {
return value;
}
throw new FieldDataError('Must be a number');
},
validate(value) {
const message = validateNumber(validation, value, step, label);
if (message !== undefined) {
throw new FieldDataError(message);
}
assertRequired(value, validation, label);
return value;
},
serialize(value) {
return {
value: value === null ? undefined : value
};
}
});
}
function PathReferenceInput(props) {
var _props$validation, _props$validation2;
const match = useMemo(() => props.pattern ? filter(props.pattern) : () => true, [props.pattern]);
const [blurred, onBlur] = useReducer(() => true, false);
const tree = useTree().current;
const options = useMemo(() => {
const files = tree.kind === 'loaded' ? [...tree.data.entries.values()] : [];
return files.filter(val => match(val.path));
}, [tree, match]);
const _errorMessage = (props.forceValidation || blurred) && (_props$validation = props.validation) !== null && _props$validation !== void 0 && _props$validation.isRequired && props.value === null ? `${props.label} is required` : undefined;
// this state & effect shouldn't really exist
// it's here because react-aria/stately calls onSelectionChange with null
// after selecting an item if we immediately remove the error message
// so we delay it with an effect
const [errorMessage, setErrorMessage] = useState(_errorMessage);
useEffect(() => {
setErrorMessage(_errorMessage);
}, [_errorMessage]);
return /*#__PURE__*/jsx(Combobox, {
label: props.label,
description: props.description,
selectedKey: props.value,
onSelectionChange: key => {
if (typeof key === 'string' || key === null) {
props.onChange(key);
}
},
onBlur: onBlur,
isRequired: (_props$validation2 = props.validation) === null || _props$validation2 === void 0 ? void 0 : _props$validation2.isRequired,
errorMessage: errorMessage,
autoFocus: props.autoFocus,
defaultItems: options,
width: "auto",
children: item => /*#__PURE__*/jsx(Item$2, {
children: item.path
}, item.path)
});
}
function pathReference({
label,
pattern,
validation,
description
}) {
return basicFormFieldWithSimpleReaderParse({
label,
Input(props) {
return /*#__PURE__*/jsx(PathReferenceInput, {
label: label,
pattern: pattern,
description: description,
validation: validation,
...props
});
},
defaultValue() {
return null;
},
parse(value) {
if (value === undefined) {
return null;
}
if (typeof value !== 'string') {
throw new FieldDataError('Must be a string');
}
return value;
},
validate(value) {
assertRequired(value, validation, label);
return value;
},
serialize(value) {
return {
value: value === null ? undefined : value
};
}
});
}
function RelationshipInput(props) {
var _props$validation, _props$validation2;
const [blurred, onBlur] = useReducer(() => true, false);
const slugs = useSlugsInCollection(props.collection);
const options = useMemo(() => {
return slugs.map(slug => ({
slug
}));
}, [slugs]);
const _errorMessage = (props.forceValidation || blurred) && (_props$validation = props.validation) !== null && _props$validation !== void 0 && _props$validation.isRequired && props.value === null ? `${props.label} is required` : undefined;
// this state & effect shouldn't really exist
// it's here because react-aria/stately calls onSelectionChange with null
// after selecting an item if we immediately remove the error message
// so we delay it with an effect
const [errorMessage, setErrorMessage] = useState(_errorMessage);
useEffect(() => {
setErrorMessage(_errorMessage);
}, [_errorMessage]);
return /*#__PURE__*/jsx(Combobox, {
label: props.label,
description: props.description,
selectedKey: props.value,
onSelectionChange: key => {
if (typeof key === 'string' || key === null) {
props.onChange(key);
}
},
onBlur: onBlur,
autoFocus: props.autoFocus,
defaultItems: options,
isRequired: (_props$validation2 = props.validation) === null || _props$validation2 === void 0 ? void 0 : _props$validation2.isRequired,
errorMessage: errorMessage,
width: "auto",
children: item => /*#__PURE__*/jsx(Item$1, {
children: item.slug
}, item.slug)
});
}
function relationship({
label,
collection,
validation,
description
}) {
return basicFormFieldWithSimpleReaderParse({
label,
Input(props) {
return /*#__PURE__*/jsx(RelationshipInput, {
label: label,
collection: collection,
description: description,
validation: validation,
...props
});
},
defaultValue() {
return null;
},
parse(value) {
if (value === undefined) {
return null;
}
if (typeof value !== 'string') {
throw new FieldDataError('Must be a string');
}
return value;
},
validate(value) {
assertRequired(value, validation, label);
return value;
},
serialize(value) {
return {
value: value === null ? undefined : value
};
}
});
}
function SelectFieldInput(props) {
let fieldContext = useFieldContext();
return /*#__PURE__*/jsx(Picker, {
label: props.label,
description: props.description,
items: props.options,
selectedKey: props.value,
onSelectionChange: key => {
props.onChange(key);
},
autoFocus: props.autoFocus,
width: {
mobile: 'auto',
tablet: fieldContext.span === 12 ? 'alias.singleLineWidth' : 'auto'
},
children: item => /*#__PURE__*/jsx(Item$3, {
children: item.label
}, item.value)
});
}
function select({
label,
options,
defaultValue,
description
}) {
const optionValuesSet = new Set(options.map(x => x.value));
if (!optionValuesSet.has(defaultValue)) {
throw new Error(`A defaultValue of ${defaultValue} was provided to a select field but it does not match the value of one of the options provided`);
}
const field = basicFormFieldWithSimpleReaderParse({
label,
Input(props) {
return /*#__PURE__*/jsx(SelectFieldInput, {
label: label,
options: options,
description: description,
...props
});
},
defaultValue() {
return defaultValue;
},
parse(value) {
if (value === undefined) {
return defaultValue;
}
if (typeof value !== 'string') {
throw new FieldDataError('Must be a string');
}
if (!optionValuesSet.has(value)) {
throw new FieldDataError('Must be a valid option');
}
return value;
},
validate(value) {
return value;
},
serialize(value) {
return {
value
};
}
});
return {
...field,
options
};
}
const emptySet = new Set();
function SlugFieldInput(props) {
var _props$args$slug$labe, _props$args$slug, _props$args$slug$vali, _props$args$slug2, _props$args$slug$vali2, _props$args$slug3, _props$args$name$vali, _props$args$name$vali2, _props$args$name$vali3, _props$args$name$vali4, _props$args$name$vali5, _props$args$slug4;
const slugContext = useContext(SlugFieldContext);
const path = useContext(PathContext);
const slugInfo = path.length === 1 && path[0] === (slugContext === null || slugContext === void 0 ? void 0 : slugContext.field) ? slugContext : {
slugs: emptySet,
glob: '*'
};
const [blurredName, setBlurredName] = useState(false);
const [blurredSlug, setBlurredSlug] = useState(false);
const [shouldGenerateSlug, setShouldGenerateSlug] = useState(props.value === props.defaultValue);
const generateSlug = name => {
const generated = props.naiveGenerateSlug(name);
if (slugInfo.slugs.has(generated)) {
let i = 1;
while (slugInfo.slugs.has(`${generated}-${i}`)) {
i++;
}
return `${generated}-${i}`;
}
return generated;
};
const slugFieldLabel = (_props$args$slug$labe = (_props$args$slug = props.args.slug) === null || _props$args$slug === void 0 ? void 0 : _props$args$slug.label) !== null && _props$args$slug$labe !== void 0 ? _props$args$slug$labe : 'Slug';
const slugErrorMessage = props.forceValidation || blurredSlug ? validateText(props.value.slug, (_props$args$slug$vali = (_props$args$slug2 = props.args.slug) === null || _props$args$slug2 === void 0 || (_props$args$slug2 = _props$args$slug2.validation) === null || _props$args$slug2 === void 0 || (_props$args$slug2 = _props$args$slug2.length) === null || _props$args$slug2 === void 0 ? void 0 : _props$args$slug2.min) !== null && _props$args$slug$vali !== void 0 ? _props$args$slug$vali : 1, (_props$args$slug$vali2 = (_props$args$slug3 = props.args.slug) === null || _props$args$slug3 === void 0 || (_props$args$slug3 = _props$args$slug3.validation) === null || _props$args$slug3 === void 0 || (_props$args$slug3 = _props$args$slug3.length) === null || _props$args$slug3 === void 0 ? void 0 : _props$args$slug3.max) !== null && _props$args$slug$vali2 !== void 0 ? _props$args$slug$vali2 : Infinity, slugFieldLabel, slugInfo) : undefined;
return /*#__PURE__*/jsxs(Flex, {
gap: "xlarge",
direction: "column",
children: [/*#__PURE__*/jsx(TextField, {
label: props.args.name.label,
description: props.args.name.description,
autoFocus: props.autoFocus,
value: props.value.name,
isRequired: !!((_props$args$name$vali = props.args.name.validation) !== null && _props$args$name$vali !== void 0 && (_props$args$name$vali = _props$args$name$vali.length) !== null && _props$args$name$vali !== void 0 && _props$args$name$vali.min),
onChange: name => {
props.onChange({
name,
slug: shouldGenerateSlug ? generateSlug(name) : props.value.slug
});
},
onBlur: () => setBlurredName(true),
errorMessage: props.forceValidation || blurredName ? validateText(props.value.name, (_props$args$name$vali2 = (_props$args$name$vali3 = props.args.name.validation) === null || _props$args$name$vali3 === void 0 || (_props$args$name$vali3 = _props$args$name$vali3.length) === null || _props$args$name$vali3 === void 0 ? void 0 : _props$args$name$vali3.min) !== null && _props$args$name$vali2 !== void 0 ? _props$args$name$vali2 : 0, (_props$args$name$vali4 = (_props$args$name$vali5 = props.args.name.validation) === null || _props$args$name$vali5 === void 0 || (_props$args$name$vali5 = _props$args$name$vali5.length) === null || _props$args$name$vali5 === void 0 ? void 0 : _props$args$name$vali5.max) !== null && _props$args$name$vali4 !== void 0 ? _props$args$name$vali4 : Infinity, props.args.name.label, undefined) : undefined
}), /*#__PURE__*/jsxs(Flex, {
gap: "regular",
alignItems: "end",
children: [/*#__PURE__*/jsx(TextField, {
flex: 1,
label: slugFieldLabel,
description: (_props$args$slug4 = props.args.slug) === null || _props$args$slug4 === void 0 ? void 0 : _props$args$slug4.description,
value: props.value.slug,
onChange: slug => {
setShouldGenerateSlug(false);
props.onChange({
name: props.value.name,
slug
});
},
onBlur: () => setBlurredSlug(true),
errorMessage: slugErrorMessage,
isRequired: true
}), /*#__PURE__*/jsxs(Flex, {
gap: "regular",
direction: "column",
children: [/*#__PURE__*/jsxs(ActionButton, {
"aria-label": "regenerate",
onPress: () => {
props.onChange({
name: props.value.name,
slug: generateSlug(props.value.name)
});
},
children: [/*#__PURE__*/jsx(Icon, {
src: refreshCwIcon,
UNSAFE_className: css({
[containerQueries.above.mobile]: {
display: 'none'
}
})
}), /*#__PURE__*/jsx(Text, {
UNSAFE_className: css({
[containerQueries.below.tablet]: {
display: 'none'
}
}),
children: "Regenerate"
})]
}), slugErrorMessage !== undefined && /*#__PURE__*/jsx(Box, {
height: "element.xsmall"
})]
})]
})]
});
}
function parseSlugFieldAsNormalField(value) {
if (value === undefined) {
return {
name: '',
slug: ''
};
}
if (typeof value !== 'object') {
throw new FieldDataError('Must be an object');
}
if (Object.keys(value).length !== 2) {
throw new FieldDataError('Unexpected keys');
}
if (!('name' in value) || !('slug' in value)) {
throw new FieldDataError('Missing name or slug');
}
if (typeof value.name !== 'string') {
throw new FieldDataError('name must be a string');
}
if (typeof value.slug !== 'string') {
throw new FieldDataError('slug must be a string');
}
return {
name: value.name,
slug: value.slug
};
}
function parseAsSlugField(value, slug) {
if (value === undefined) {
return {
name: '',
slug
};
}
if (typeof value !== 'string') {
throw new FieldDataError('Must be a string');
}
return {
name: value,
slug
};
}
function slug(_args) {
var _args$name$validation, _args$name$validation2, _args$name$validation3, _args$name$validation4, _args$slug, _args$name$defaultVal, _args$name$defaultVal2;
const args = {
..._args,
name: {
..._args.name,
validation: {
length: {
min: Math.max((_args$name$validation = _args.name.validation) !== null && _args$name$validation !== void 0 && _args$name$validation.isRequired ? 1 : 0, (_args$name$validation2 = (_args$name$validation3 = _args.name.validation) === null || _args$name$validation3 === void 0 || (_args$name$validation3 = _args$name$validation3.length) === null || _args$name$validation3 === void 0 ? void 0 : _args$name$validation3.min) !== null && _args$name$validation2 !== void 0 ? _args$name$validation2 : 0),
max: (_args$name$validation4 = _args.name.validation) === null || _args$name$validation4 === void 0 || (_args$name$validation4 = _args$name$validation4.length) === null || _args$name$validation4 === void 0 ? void 0 : _args$name$validation4.max
}
}
}
};
const naiveGenerateSlug = ((_args$slug = args.slug) === null || _args$slug === void 0 ? void 0 : _args$slug.generate) || slugify;
const defaultValue = {
name: (_args$name$defaultVal = args.name.defaultValue) !== null && _args$name$defaultVal !== void 0 ? _args$name$defaultVal : '',
slug: naiveGenerateSlug((_args$name$defaultVal2 = args.name.defaultValue) !== null && _args$name$defaultVal2 !== void 0 ? _args$name$defaultVal2 : '')
};
function validate(value, {
slugField
} = {
slugField: undefined
}) {
var _args$name$validation5, _args$name$validation6, _args$name$validation7, _args$name$validation8, _args$slug$validation, _args$slug2, _args$slug$validation2, _args$slug3, _args$slug$label, _args$slug4;
const nameMessage = validateText(value.name, (_args$name$validation5 = (_args$name$validation6 = args.name.validation) === null || _args$name$validation6 === void 0 || (_args$name$validation6 = _args$name$validation6.length) === null || _args$name$validation6 === void 0 ? void 0 : _args$name$validation6.min) !== null && _args$name$validation5 !== void 0 ? _args$name$validation5 : 0, (_args$name$validation7 = (_args$name$validation8 = args.name.validation) === null || _args$name$validation8 === void 0 || (_args$name$validation8 = _args$name$validation8.length) === null || _args$name$validation8 === void 0 ? void 0 : _args$name$validation8.max) !== null && _args$name$validation7 !== void 0 ? _args$name$validation7 : Infinity, args.name.label, undefined);
if (nameMessage !== undefined) {
throw new FieldDataError(nameMessage);
}
const slugMessage = validateText(value.slug, (_args$slug$validation = (_args$slug2 = args.slug) === null || _args$slug2 === void 0 || (_args$slug2 = _args$slug2.validation) === null || _args$slug2 === void 0 || (_args$slug2 = _args$slug2.length) === null || _args$slug2 === void 0 ? void 0 : _args$slug2.min) !== null && _args$slug$validation !== void 0 ? _args$slug$validation : 1, (_args$slug$validation2 = (_args$slug3 = args.slug) === null || _args$slug3 === void 0 || (_args$slug3 = _args$slug3.validation) === null || _args$slug3 === void 0 || (_args$slug3 = _args$slug3.length) === null || _args$slug3 === void 0 ? void 0 : _args$slug3.max) !== null && _args$slug$validation2 !== void 0 ? _args$slug$validation2 : Infinity, (_args$slug$label = (_args$slug4 = args.slug) === null || _args$slug4 === void 0 ? void 0 : _args$slug4.label) !== null && _args$slug$label !== void 0 ? _args$slug$label : 'Slug', slugField ? slugField : {
slugs: emptySet,
glob: '*'
});
if (slugMessage !== undefined) {
throw new FieldDataError(slugMessage);
}
return value;
}
const emptySet = new Set();
return {
kind: 'form',
formKind: 'slug',
label: args.name.label,
Input(props) {
return /*#__PURE__*/jsx(SlugFieldInput, {
args: args,
naiveGenerateSlug: naiveGenerateSlug,
defaultValue: defaultValue,
...props
});
},
defaultValue() {
return defaultValue;
},
parse(value, args) {
if ((args === null || args === void 0 ? void 0 : args.slug) !== undefined) {
return parseAsSlugField(value, args.slug);
}
return parseSlugFieldAsNormalField(value);
},
validate,
serialize(value) {
return {
value
};
},
serializeWithSlug(value) {
return {
value: value.name,
slug: value.slug
};
},
reader: {
parse(value) {
const parsed = parseSlugFieldAsNormalField(value);
return validate(parsed);
},
parseWithSlug(value, args) {
return validate(parseAsSlugField(value, args.slug), {
slugField: {
glob: args.glob,
slugs: emptySet
}
}).name;
}
}
};
}
function validateUrl(validation, value, label) {
if (value !== null && (typeof value !== 'string' || !isValidURL(value))) {
return `${label} is not a valid URL`;
}
if (validation !== null && validation !== void 0 && validation.isRequired && value === null) {
return `${label} is required`;
}
}
function UrlFieldInput(props) {
var _props$validation;
const [blurred, onBlur] = useReducer(() => true, false);
return /*#__PURE__*/jsx(TextField, {
inputMode: "url",
width: "auto",
maxWidth: "scale.6000",
label: props.label,
description: props.description,
autoFocus: props.autoFocus,
value: props.value === null ? '' : props.value,
onChange: val => {
props.onChange(val === '' ? null : val);
},
onBlur: onBlur,
isRequired: (_props$validation = props.validation) === null || _props$validation === void 0 ? void 0 : _props$validation.isRequired,
errorMessage: props.forceValidation || blurred ? validateUrl(props.validation, props.value, props.label) : undefined
});
}
function url({
label,
defaultValue,
validation,
description
}) {
return basicFormFieldWithSimpleReaderParse({
label,
Input(props) {
return /*#__PURE__*/jsx(UrlFieldInput, {
label: label,
description: description,
validation: validation,
...props
});
},
defaultValue() {
return defaultValue || null;
},
parse(value) {
if (value === undefined) {
return null;
}
if (typeof value !== 'string') {
throw new FieldDataError('Must be a string');
}
return value === '' ? null : value;
},
validate(value) {
const message = validateUrl(validation, value, label);
if (message !== undefined) {
throw new FieldDataError(message);
}
assertRequired(value, validation, label);
return value;
},
serialize(value) {
return {
value: value === null ? undefined : value
};
}
});
}
var index = /*#__PURE__*/Object.freeze({
__proto__: null,
array: array,
blocks: blocks,
checkbox: checkbox,
child: child,
cloudImage: cloudImage,
conditional: conditional,
date: date,
datetime: datetime,
document: document,
empty: empty,
emptyDocument: emptyDocument,
file: file,
image: image,
integer: integer,
multiRelationship: multiRelationship,
multiselect: multiselect,
number: number,
object: object,
pathReference: pathReference,
relationship: relationship,
select: select,
slug: slug,
text: text,
url: url,
mdx: mdx,
markdoc: markdoc
});
export { index as fields };