diff --git a/package-lock.json b/package-lock.json index 0aac2346..b3930fbf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,8 +22,11 @@ "@sentry/tracing": "^6.13.3", "@tailwindcss/forms": "^0.3.3", "@types/glob": "7.1.3", + "@types/invariant": "^2.2.35", "@types/js-yaml": "3.12.1", + "@types/lodash.omit": "^4.5.6", "@types/lodash.uniqby": "4.7.6", + "@types/lodash.xor": "^4.5.6", "@types/mocha": "^5.2.6", "@types/node": "10.17.48", "@types/node-fetch": "^2.5.12", @@ -47,8 +50,11 @@ "html-loader": "1.3.2", "html-webpack-plugin": "4.5.0", "image-size": "^1.0.0", + "invariant": "^2.2.4", "lodash-es": "^4.17.21", + "lodash.omit": "^4.5.0", "lodash.uniqby": "4.7.0", + "lodash.xor": "^4.5.0", "mdast-util-from-markdown": "1.0.0", "node-json-db": "^1.3.0", "npm-run-all": "^4.1.5", @@ -712,6 +718,12 @@ "@types/node": "*" } }, + "node_modules/@types/invariant": { + "version": "2.2.35", + "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.35.tgz", + "integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg==", + "dev": true + }, "node_modules/@types/js-yaml": { "version": "3.12.1", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.1.tgz", @@ -730,6 +742,15 @@ "integrity": "sha512-/BHF5HAx3em7/KkzVKm3LrsD6HZAXuXO1AJZQ3cRRBZj4oHZDviWPYu0aEplAqDFNHZPW6d3G7KN+ONcCCC7pw==", "dev": true }, + "node_modules/@types/lodash.omit": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@types/lodash.omit/-/lodash.omit-4.5.6.tgz", + "integrity": "sha512-KXPpOSNX2h0DAG2w7ajpk7TXvWF28ZHs5nJhOJyP0BQHkehgr948RVsToItMme6oi0XJkp19CbuNXkIX8FiBlQ==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/lodash.uniqby": { "version": "4.7.6", "resolved": "https://registry.npmjs.org/@types/lodash.uniqby/-/lodash.uniqby-4.7.6.tgz", @@ -739,6 +760,15 @@ "@types/lodash": "*" } }, + "node_modules/@types/lodash.xor": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@types/lodash.xor/-/lodash.xor-4.5.6.tgz", + "integrity": "sha512-rD3zHQuR04SMUWEn6CHLGYNRHEFgakV4K9QhFFP/Ly5xdN2MWsy+gWFA4MxhY+sKKSSKjktjPVyBTo/6rUdTjA==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/mdast": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.7.tgz", @@ -5182,6 +5212,12 @@ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", "dev": true }, + "node_modules/lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=", + "dev": true + }, "node_modules/lodash.topath": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", @@ -5194,6 +5230,12 @@ "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=", "dev": true }, + "node_modules/lodash.xor": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.xor/-/lodash.xor-4.5.0.tgz", + "integrity": "sha1-TUjtfpgJWwYyWCunFNP/iuj7HbY=", + "dev": true + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -9755,6 +9797,12 @@ "@types/node": "*" } }, + "@types/invariant": { + "version": "2.2.35", + "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.35.tgz", + "integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg==", + "dev": true + }, "@types/js-yaml": { "version": "3.12.1", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.1.tgz", @@ -9773,6 +9821,15 @@ "integrity": "sha512-/BHF5HAx3em7/KkzVKm3LrsD6HZAXuXO1AJZQ3cRRBZj4oHZDviWPYu0aEplAqDFNHZPW6d3G7KN+ONcCCC7pw==", "dev": true }, + "@types/lodash.omit": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@types/lodash.omit/-/lodash.omit-4.5.6.tgz", + "integrity": "sha512-KXPpOSNX2h0DAG2w7ajpk7TXvWF28ZHs5nJhOJyP0BQHkehgr948RVsToItMme6oi0XJkp19CbuNXkIX8FiBlQ==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, "@types/lodash.uniqby": { "version": "4.7.6", "resolved": "https://registry.npmjs.org/@types/lodash.uniqby/-/lodash.uniqby-4.7.6.tgz", @@ -9782,6 +9839,15 @@ "@types/lodash": "*" } }, + "@types/lodash.xor": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@types/lodash.xor/-/lodash.xor-4.5.6.tgz", + "integrity": "sha512-rD3zHQuR04SMUWEn6CHLGYNRHEFgakV4K9QhFFP/Ly5xdN2MWsy+gWFA4MxhY+sKKSSKjktjPVyBTo/6rUdTjA==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, "@types/mdast": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.7.tgz", @@ -13263,6 +13329,12 @@ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", "dev": true }, + "lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=", + "dev": true + }, "lodash.topath": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", @@ -13275,6 +13347,12 @@ "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=", "dev": true }, + "lodash.xor": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.xor/-/lodash.xor-4.5.0.tgz", + "integrity": "sha1-TUjtfpgJWwYyWCunFNP/iuj7HbY=", + "dev": true + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", diff --git a/package.json b/package.json index f247d802..387de0b3 100644 --- a/package.json +++ b/package.json @@ -632,7 +632,8 @@ "tags", "categories", "draft", - "fields" + "fields", + "data-collection" ], "description": "Define the type of field" }, @@ -698,6 +699,11 @@ }, "fields": { "$ref": "#contenttypefield" + }, + "dataType": { + "type": "string", + "default": "", + "description": "The ID of your data type defined in the `frontMatter.data.types` setting" } }, "additionalProperties": false, @@ -747,6 +753,20 @@ "fields" ] } + }, + { + "if": { + "properties": { + "type": { + "const": "data-collection" + } + } + }, + "then": { + "required": [ + "dataType" + ] + } } ] } @@ -1407,8 +1427,11 @@ "@sentry/tracing": "^6.13.3", "@tailwindcss/forms": "^0.3.3", "@types/glob": "7.1.3", + "@types/invariant": "^2.2.35", "@types/js-yaml": "3.12.1", + "@types/lodash.omit": "^4.5.6", "@types/lodash.uniqby": "4.7.6", + "@types/lodash.xor": "^4.5.6", "@types/mocha": "^5.2.6", "@types/node": "10.17.48", "@types/node-fetch": "^2.5.12", @@ -1432,8 +1455,11 @@ "html-loader": "1.3.2", "html-webpack-plugin": "4.5.0", "image-size": "^1.0.0", + "invariant": "^2.2.4", "lodash-es": "^4.17.21", + "lodash.omit": "^4.5.0", "lodash.uniqby": "4.7.0", + "lodash.xor": "^4.5.0", "mdast-util-from-markdown": "1.0.0", "node-json-db": "^1.3.0", "npm-run-all": "^4.1.5", diff --git a/src/components/uniforms-frontmatter/AutoField.tsx b/src/components/uniforms-frontmatter/AutoField.tsx new file mode 100644 index 00000000..2b5f132c --- /dev/null +++ b/src/components/uniforms-frontmatter/AutoField.tsx @@ -0,0 +1,39 @@ +import * as invariant from 'invariant'; +import { createAutoField } from 'uniforms'; +export { AutoFieldProps } from 'uniforms'; + +import BoolField from './BoolField'; +import DateField from './DateField'; +import ListField from './ListField'; +import NestField from './NestField'; +import NumField from './NumField'; +import RadioField from './RadioField'; +import SelectField from './SelectField'; +import TextField from './TextField'; + +const AutoField = createAutoField(props => { + if (props.allowedValues) { + return props.checkboxes && props.fieldType !== Array + ? RadioField + : SelectField; + } + + switch (props.fieldType) { + case Array: + return ListField; + case Boolean: + return BoolField; + case Date: + return DateField; + case Number: + return NumField; + case Object: + return NestField; + case String: + return TextField; + } + + return invariant(false, 'Unsupported field type: %s', props.fieldType); +}); + +export default AutoField; diff --git a/src/components/uniforms-frontmatter/AutoFields.tsx b/src/components/uniforms-frontmatter/AutoFields.tsx new file mode 100644 index 00000000..3e07ab3a --- /dev/null +++ b/src/components/uniforms-frontmatter/AutoFields.tsx @@ -0,0 +1,29 @@ +import { ComponentType, createElement, Fragment } from 'react'; +import { useForm } from 'uniforms'; + +import AutoField from './AutoField'; + +export type AutoFieldsProps = { + autoField?: ComponentType<{ name: string }>; + element?: ComponentType | string; + fields?: string[]; + omitFields?: string[]; +}; + +export default function AutoFields({ + autoField = AutoField, + element = Fragment, + fields, + omitFields = [], + ...props +}: AutoFieldsProps) { + const { schema } = useForm(); + + return createElement( + element, + props, + (fields ?? schema.getSubfields()) + .filter(field => !omitFields.includes(field)) + .map(field => createElement(autoField, { key: field, name: field })), + ); +} diff --git a/src/components/uniforms-frontmatter/AutoForm.tsx b/src/components/uniforms-frontmatter/AutoForm.tsx new file mode 100644 index 00000000..ea832171 --- /dev/null +++ b/src/components/uniforms-frontmatter/AutoForm.tsx @@ -0,0 +1,13 @@ +import { AutoForm } from 'uniforms'; + +import ValidatedQuickForm from './ValidatedQuickForm'; + +function Auto(parent: any) { + class _ extends AutoForm.Auto(parent) { + static Auto = Auto; + } + + return _ as unknown as AutoForm; +} + +export default Auto(ValidatedQuickForm); diff --git a/src/components/uniforms-frontmatter/BaseForm.tsx b/src/components/uniforms-frontmatter/BaseForm.tsx new file mode 100644 index 00000000..3f83574c --- /dev/null +++ b/src/components/uniforms-frontmatter/BaseForm.tsx @@ -0,0 +1,13 @@ +import { BaseForm } from 'uniforms'; + +function Unstyled(parent: any) { + class _ extends parent { + static Unstyled = Unstyled; + + static displayName = `Unstyled${parent.displayName}`; + } + + return _ as unknown as typeof BaseForm; +} + +export default Unstyled(BaseForm); diff --git a/src/components/uniforms-frontmatter/BoolField.css b/src/components/uniforms-frontmatter/BoolField.css new file mode 100644 index 00000000..9bdadbff --- /dev/null +++ b/src/components/uniforms-frontmatter/BoolField.css @@ -0,0 +1,52 @@ +.field__toggle { + position: relative; + display: inline-block; + width: 50px; + height: 24px; +} + +.field__toggle input { + opacity: 0; + width: 0; + height: 0; +} + +.field__toggle__slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: var(--vscode-button-secondaryBackground); + -webkit-transition: .4s; + transition: .4s; + border-radius: 34px; +} + +.field__toggle__slider:before { + position: absolute; + content: ""; + height: 16px; + width: 16px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; + border-radius: 50%; +} + +input:checked + .field__toggle__slider { + background-color: var(--vscode-button-background); +} + +input:focus + .field__toggle__slider { + box-shadow: 0 0 1px var(--vscode-button-background); +} + +input:checked + .field__toggle__slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} \ No newline at end of file diff --git a/src/components/uniforms-frontmatter/BoolField.tsx b/src/components/uniforms-frontmatter/BoolField.tsx new file mode 100644 index 00000000..85aab276 --- /dev/null +++ b/src/components/uniforms-frontmatter/BoolField.tsx @@ -0,0 +1,44 @@ +import * as React from 'react'; +import { Ref } from 'react'; +import { HTMLFieldProps, connectField, filterDOMProps } from 'uniforms'; +import { LabelStyles } from './component-styles'; +import './BoolField.css'; + +export type BoolFieldProps = HTMLFieldProps< + boolean, + HTMLDivElement, + { inputRef?: Ref } +>; + +function Bool({ + disabled, + id, + inputRef, + label, + name, + onChange, + readOnly, + value, + ...props +}: BoolFieldProps) { + return ( +
+ {label && } + + +
+ ); +} + +export default connectField(Bool, { kind: 'leaf' }); diff --git a/src/components/uniforms-frontmatter/DateField.tsx b/src/components/uniforms-frontmatter/DateField.tsx new file mode 100644 index 00000000..6b115d79 --- /dev/null +++ b/src/components/uniforms-frontmatter/DateField.tsx @@ -0,0 +1,57 @@ +import * as React from 'react'; +import { Ref } from 'react'; +import { HTMLFieldProps, connectField, filterDOMProps } from 'uniforms'; + +/* istanbul ignore next */ +const DateConstructor = (typeof global === 'object' ? global : window).Date; +const dateFormat = (value?: Date) => value?.toISOString().slice(0, -8); + +export type DateFieldProps = HTMLFieldProps< + Date, + HTMLDivElement, + { inputRef?: Ref; max?: Date; min?: Date } +>; + +function Date({ + disabled, + id, + inputRef, + label, + max, + min, + name, + onChange, + placeholder, + readOnly, + value, + ...props +}: DateFieldProps) { + return ( +
+ {label && } + + { + const date = new DateConstructor(event.target.valueAsNumber); + if (date.getFullYear() < 10000) { + onChange(date); + } else if (isNaN(event.target.valueAsNumber)) { + onChange(undefined); + } + }} + placeholder={placeholder} + readOnly={readOnly} + ref={inputRef} + type="datetime-local" + value={dateFormat(value) ?? ''} + /> +
+ ); +} + +export default connectField(Date, { kind: 'leaf' }); diff --git a/src/components/uniforms-frontmatter/ErrorField.tsx b/src/components/uniforms-frontmatter/ErrorField.tsx new file mode 100644 index 00000000..eba97bff --- /dev/null +++ b/src/components/uniforms-frontmatter/ErrorField.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { HTMLProps } from 'react'; +import { Override, connectField, filterDOMProps } from 'uniforms'; + +export type ErrorFieldProps = Override< + Omit, 'onChange'>, + { error?: any; errorMessage?: string } +>; + +function Error({ children, error, errorMessage, ...props }: ErrorFieldProps) { + return !error ? null : ( +
{children || errorMessage}
+ ); +} + +export default connectField(Error, { + initialValue: false, + kind: 'leaf', +}); diff --git a/src/components/uniforms-frontmatter/ErrorsField.css b/src/components/uniforms-frontmatter/ErrorsField.css new file mode 100644 index 00000000..04a81f16 --- /dev/null +++ b/src/components/uniforms-frontmatter/ErrorsField.css @@ -0,0 +1,14 @@ + + +.autoform-error { + background-color: var(--vscode-inputValidation-errorBackground); + border: 1px solid var(--vscode-inputValidation-errorBorder); + border-radius: 2px; + margin: 20px 0px; + padding: 10px; + color: var(--vscode-editor-foreground); + + ul { + margin-bottom: 0; + } +} \ No newline at end of file diff --git a/src/components/uniforms-frontmatter/ErrorsField.tsx b/src/components/uniforms-frontmatter/ErrorsField.tsx new file mode 100644 index 00000000..132d94a8 --- /dev/null +++ b/src/components/uniforms-frontmatter/ErrorsField.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import { HTMLProps } from 'react'; +import { filterDOMProps, useForm } from 'uniforms'; +import './ErrorsField.css'; + +export type ErrorsFieldProps = HTMLProps; + +export default function ErrorsField(props: ErrorsFieldProps) { + const { error, schema } = useForm(); + return !error && !props.children ? null : ( +
+
+ {props.children} + +
    + {schema.getErrorMessages(error).map((message, index) => ( +
  • {message}
  • + ))} +
+
+
+ ); +} diff --git a/src/components/uniforms-frontmatter/HiddenField.tsx b/src/components/uniforms-frontmatter/HiddenField.tsx new file mode 100644 index 00000000..d2a6d2fb --- /dev/null +++ b/src/components/uniforms-frontmatter/HiddenField.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import { HTMLProps, Ref, useEffect } from 'react'; +import { Override, filterDOMProps, useField } from 'uniforms'; + +export type HiddenFieldProps = Override< + HTMLProps, + { + inputRef?: Ref; + name: string; + noDOM?: boolean; + value?: any; + } +>; + +export default function HiddenField({ value, ...rawProps }: HiddenFieldProps) { + const props = useField(rawProps.name, rawProps, { initialValue: false })[0]; + + useEffect(() => { + if (value !== undefined && value !== props.value) { + props.onChange(value); + } + }); + + return props.noDOM ? null : ( + + ); +} diff --git a/src/components/uniforms-frontmatter/ListAddField.css b/src/components/uniforms-frontmatter/ListAddField.css new file mode 100644 index 00000000..195d3934 --- /dev/null +++ b/src/components/uniforms-frontmatter/ListAddField.css @@ -0,0 +1,21 @@ + + +.autoform__list_add_field { + display: flex; + padding: 5px; + border: 1px dashed var(--vscode-editor-foreground); + width: 100%; + justify-content: center; + margin-top: .5rem; + + &:hover { + border-color: var(--vscode-button-background); + color: var(--vscode-button-background); + cursor: pointer; + } + + svg { + height: 1rem; + width: 1rem; + } +} \ No newline at end of file diff --git a/src/components/uniforms-frontmatter/ListAddField.tsx b/src/components/uniforms-frontmatter/ListAddField.tsx new file mode 100644 index 00000000..2c41d9ff --- /dev/null +++ b/src/components/uniforms-frontmatter/ListAddField.tsx @@ -0,0 +1,63 @@ +import { PlusIcon } from '@heroicons/react/outline'; +import * as React from 'react'; +import { + HTMLFieldProps, + connectField, + filterDOMProps, + joinName, + useField, +} from 'uniforms'; +import './ListAddField.css'; + +export type ListAddFieldProps = HTMLFieldProps< + unknown, + HTMLSpanElement, + { initialCount?: number } +>; + +function ListAdd({ + disabled, + initialCount, + name, + readOnly, + value, + ...props +}: ListAddFieldProps) { + const nameParts = joinName(null, name); + const parentName = joinName(nameParts.slice(0, -1)); + const parent = useField< + { initialCount?: number; maxCount?: number }, + unknown[] + >(parentName, { initialCount }, { absoluteName: true })[0]; + + const limitNotReached = + !disabled && !(parent.maxCount! <= parent.value!.length); + + function onAction(event: React.KeyboardEvent | React.MouseEvent) { + if ( + limitNotReached && + !readOnly && + (!('key' in event) || event.key === 'Enter') + ) { + parent.onChange(parent.value!.concat([Object.assign({}, value)])); + } + } + + return ( + + + + ); +} + +export default connectField(ListAdd, { + initialValue: false, + kind: 'leaf', +}); diff --git a/src/components/uniforms-frontmatter/ListDelField.css b/src/components/uniforms-frontmatter/ListDelField.css new file mode 100644 index 00000000..3bb458a2 --- /dev/null +++ b/src/components/uniforms-frontmatter/ListDelField.css @@ -0,0 +1,27 @@ + + +.autoform__list_del_field { + display: flex; + width: 100%; + justify-content: center; + margin-top: .5rem; + + &:hover { + border-color: var(--vscode-button-background); + color: var(--vscode-button-background); + cursor: pointer; + } + + .line { + height: 1px; + background: var(--vscode-editor-foreground); + width: 100%; + margin-right: .5rem; + margin-top: .5rem; + } + + svg { + height: 1.25rem; + width: 1.25rem; + } +} \ No newline at end of file diff --git a/src/components/uniforms-frontmatter/ListDelField.tsx b/src/components/uniforms-frontmatter/ListDelField.tsx new file mode 100644 index 00000000..ba165527 --- /dev/null +++ b/src/components/uniforms-frontmatter/ListDelField.tsx @@ -0,0 +1,63 @@ +import { TrashIcon } from '@heroicons/react/outline'; +import * as React from 'react'; +import { + HTMLFieldProps, + connectField, + filterDOMProps, + joinName, + useField, +} from 'uniforms'; +import './ListDelField.css'; + +export type ListDelFieldProps = HTMLFieldProps; + +function ListDel({ disabled, name, readOnly, ...props }: ListDelFieldProps) { + const nameParts = joinName(null, name); + const nameIndex = +nameParts[nameParts.length - 1]; + const parentName = joinName(nameParts.slice(0, -1)); + const parent = useField<{ minCount?: number }, unknown[]>( + parentName, + {}, + { absoluteName: true }, + )[0]; + + const limitNotReached = + !disabled && !(parent.minCount! >= parent.value!.length); + + function onAction( + event: + | React.KeyboardEvent + | React.MouseEvent, + ) { + if ( + limitNotReached && + !readOnly && + (!('key' in event) || event.key === 'Enter') + ) { + const value = parent.value!.slice(); + value.splice(nameIndex, 1); + parent.onChange(value); + } + } + + return ( + +
+ +
+ + + ); +} + +export default connectField(ListDel, { + initialValue: false, + kind: 'leaf', +}); diff --git a/src/components/uniforms-frontmatter/ListField.css b/src/components/uniforms-frontmatter/ListField.css new file mode 100644 index 00000000..2942c151 --- /dev/null +++ b/src/components/uniforms-frontmatter/ListField.css @@ -0,0 +1,7 @@ + + +.autoform__list_field { + margin-bottom: 5px; + margin-top: 5px; + padding: 10px; +} \ No newline at end of file diff --git a/src/components/uniforms-frontmatter/ListField.tsx b/src/components/uniforms-frontmatter/ListField.tsx new file mode 100644 index 00000000..7323e400 --- /dev/null +++ b/src/components/uniforms-frontmatter/ListField.tsx @@ -0,0 +1,50 @@ +import * as React from 'react'; +import { Children, cloneElement, isValidElement } from 'react'; +import { HTMLFieldProps, connectField, filterDOMProps } from 'uniforms'; +import { LabelStyles } from './component-styles'; + +import ListAddField from './ListAddField'; +import ListItemField from './ListItemField'; + +import './ListField.css'; + +export type ListFieldProps = HTMLFieldProps< + unknown[], + HTMLDivElement, + { initialCount?: number; itemProps?: object } +>; + +function List({ + children = , + initialCount, + itemProps, + label, + value, + ...props +}: ListFieldProps) { + return ( +
+ {label && ( +
+ {label} +
+ )} + + {value?.map((item, itemIndex) => + Children.map(children, (child, childIndex) => + isValidElement(child) + ? cloneElement(child, { + key: `${itemIndex}-${childIndex}`, + name: child.props.name?.replace('$', '' + itemIndex), + ...itemProps, + }) + : child, + ), + )} + + +
+ ); +} + +export default connectField(List); diff --git a/src/components/uniforms-frontmatter/ListItemField.tsx b/src/components/uniforms-frontmatter/ListItemField.tsx new file mode 100644 index 00000000..ff2c3502 --- /dev/null +++ b/src/components/uniforms-frontmatter/ListItemField.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import { ReactNode } from 'react'; +import { connectField } from 'uniforms'; + +import AutoField from './AutoField'; +import ListDelField from './ListDelField'; + +export type ListItemFieldProps = { children?: ReactNode; value?: unknown }; + +function ListItem({ + children = , +}: ListItemFieldProps) { + return ( +
+ + {children} +
+ ); +} + +export default connectField(ListItem, { + initialValue: false, +}); diff --git a/src/components/uniforms-frontmatter/LongTextField.tsx b/src/components/uniforms-frontmatter/LongTextField.tsx new file mode 100644 index 00000000..69ce582e --- /dev/null +++ b/src/components/uniforms-frontmatter/LongTextField.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import { Ref } from 'react'; +import { HTMLFieldProps, connectField, filterDOMProps } from 'uniforms'; + +export type LongTextFieldProps = HTMLFieldProps< + string, + HTMLDivElement, + { inputRef?: Ref } +>; + +function LongText({ + disabled, + id, + inputRef, + label, + name, + onChange, + placeholder, + readOnly, + value, + ...props +}: LongTextFieldProps) { + return ( +
+ {label && } + +