diff --git a/src/admin/components/forms/FieldTypeGutter/context.tsx b/src/admin/components/forms/FieldTypeGutter/context.tsx deleted file mode 100644 index 6e6fd55bbf..0000000000 --- a/src/admin/components/forms/FieldTypeGutter/context.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React, { createContext, useContext } from 'react'; -import { useWindowInfo } from '@faceless-ui/window-info'; - -const context = createContext(false); -const { Provider } = context; - -export const NegativeFieldGutterProvider: React.FC<{allow?: boolean, children?: React.ReactNode}> = ({ children, allow }) => { - const { breakpoints: { m: midBreak } } = useWindowInfo(); - - return ( - - {children} - - ); -}; - -export const useNegativeFieldGutter = (): boolean => useContext(context); diff --git a/src/admin/components/forms/FieldTypeGutter/index.scss b/src/admin/components/forms/FieldTypeGutter/index.scss deleted file mode 100644 index 8038759767..0000000000 --- a/src/admin/components/forms/FieldTypeGutter/index.scss +++ /dev/null @@ -1,89 +0,0 @@ -@import '../../../scss/styles.scss'; - -$controls-top-adjustment: base(.1); - -@mixin nestedStickyOffsets ($loopCount, $currentCount: 0) { - .field-type { - @if $loopCount >$currentCount { - .field-type-gutter--v-align-sticky .field-type-gutter__content { - top: calc(#{$top-header-offset} + (#{base(2.75)} * #{$currentCount})); - } - - @include nestedStickyOffsets($loopCount, $currentCount + 1); - } - } -} - -@include nestedStickyOffsets(4); - -.field-type-gutter { - &--left { - margin-right: base(1.25); - } - - &--right { - padding-right: 0; - padding-left: base(1.25); - - .field-type-gutter__content { - margin-bottom: base(1); - } - - .field-type-gutter__content-container { - padding-right: 0; - box-shadow: none !important; - } - } - - &--v-align-top { - .field-type-gutter__content { - justify-content: flex-start; - } - } - - &--v-align-sticky { - .field-type-gutter__content { - position: sticky; - top: $top-header-offset; - height: unset; - } - } - - &__content-container { - padding-right: $style-stroke-width-s; - position: relative; - min-height: 100%; - box-shadow: #{$style-stroke-width-s} 0px 0px 0px var(--theme-elevation-150); - } - - &__content { - display: flex; - flex-direction: column; - justify-content: center; - height: 100%; - } - - &--negative-gutter { - &.field-type-gutter--left { - position: absolute; - top: 0; - right: 100%; - bottom: 0; - } - } - - @include mid-break { - &--left { - margin-right: base(1); - - .field-type-gutter__content-container { - padding-right: $style-stroke-width-m; - padding-left: 0; - } - } - - &--right { - padding-right: 0; - } - } -} diff --git a/src/admin/components/forms/FieldTypeGutter/index.tsx b/src/admin/components/forms/FieldTypeGutter/index.tsx deleted file mode 100644 index 7db0fdc04b..0000000000 --- a/src/admin/components/forms/FieldTypeGutter/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import { useNegativeFieldGutter } from './context'; -import { Props } from './types'; - -import './index.scss'; - -const baseClass = 'field-type-gutter'; - -const FieldTypeGutter: React.FC = ({ - children, - variant = 'left', - verticalAlignment = 'sticky', - className, - dragHandleProps = {}, -}) => { - const allowNegativeGutter = useNegativeFieldGutter(); - - const classes = [ - baseClass, - `${baseClass}--${variant}`, - `${baseClass}--v-align-${verticalAlignment}`, - allowNegativeGutter && `${baseClass}--negative-gutter`, - className && className, - ].filter(Boolean).join(' '); - - return ( -
-
-
- {children} -
-
-
- ); -}; - -export default FieldTypeGutter; diff --git a/src/admin/components/forms/FieldTypeGutter/types.ts b/src/admin/components/forms/FieldTypeGutter/types.ts deleted file mode 100644 index cccf9c2206..0000000000 --- a/src/admin/components/forms/FieldTypeGutter/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import { DraggableProvidedDragHandleProps } from 'react-beautiful-dnd'; - -export type Props = { - variant?: 'left' | 'right' - verticalAlignment?: 'top' | 'center' | 'sticky' - dragHandleProps?: DraggableProvidedDragHandleProps - className?: string - children?: React.ReactNode -} diff --git a/src/admin/components/forms/Form/fieldReducer.ts b/src/admin/components/forms/Form/fieldReducer.ts index cbd2546390..78311a4555 100644 --- a/src/admin/components/forms/Form/fieldReducer.ts +++ b/src/admin/components/forms/Form/fieldReducer.ts @@ -5,6 +5,7 @@ import flattenFilters from './flattenFilters'; import getSiblingData from './getSiblingData'; import reduceFieldsToValues from './reduceFieldsToValues'; import { Fields } from './types'; +import deepCopyObject from '../../../../utilities/deepCopyObject'; const unflattenRowsFromState = (state: Fields, path) => { // Take a copy of state @@ -117,10 +118,8 @@ function fieldReducer(state: Fields, action): Fields { const { unflattenedRows, remainingFlattenedState } = unflattenRowsFromState(state, path); - const duplicate = { - ...unflattenedRows[rowIndex], - id: new ObjectID().toHexString(), - }; + const duplicate = deepCopyObject(unflattenedRows[rowIndex]); + if (duplicate.id) delete duplicate.id; // If there are subfields if (Object.keys(duplicate).length > 0) { diff --git a/src/admin/components/forms/field-types/Array/Array.tsx b/src/admin/components/forms/field-types/Array/Array.tsx index c92d5c6d6c..a65340c6b1 100644 --- a/src/admin/components/forms/field-types/Array/Array.tsx +++ b/src/admin/components/forms/field-types/Array/Array.tsx @@ -20,6 +20,8 @@ import { fieldAffectsData } from '../../../../../fields/config/types'; import { Props } from './types'; import { usePreferences } from '../../../utilities/Preferences'; import { ArrayAction } from '../../../elements/ArrayAction'; +import { scrollToID } from '../../../../utilities/scrollToID'; +import HiddenInput from '../HiddenInput'; import './index.scss'; @@ -88,20 +90,13 @@ const ArrayFieldType: React.FC = (props) => { const addRow = useCallback(async (rowIndex: number) => { const subFieldState = await buildStateFromSchema({ fieldSchema: fields, operation, id, user, locale }); + console.log(subFieldState); dispatchFields({ type: 'ADD_ROW', rowIndex, subFieldState, path }); dispatchRows({ type: 'ADD', rowIndex }); setValue(value as number + 1); setTimeout(() => { - const newRow = document.getElementById(`${path}-row-${rowIndex + 1}`); - - if (newRow) { - const bounds = newRow.getBoundingClientRect(); - window.scrollBy({ - top: bounds.top - 100, - behavior: 'smooth', - }); - } + scrollToID(`${path}-row-${rowIndex + 1}`); }, 0); }, [dispatchRows, dispatchFields, fields, path, setValue, value, operation, id, user, locale]); @@ -109,6 +104,10 @@ const ArrayFieldType: React.FC = (props) => { dispatchFields({ type: 'DUPLICATE_ROW', rowIndex, path }); dispatchRows({ type: 'ADD', rowIndex }); setValue(value as number + 1); + + setTimeout(() => { + scrollToID(`${path}-row-${rowIndex + 1}`); + }, 0); }, [dispatchRows, dispatchFields, path, setValue, value]); const removeRow = useCallback((rowIndex: number) => { @@ -282,6 +281,10 @@ const ArrayFieldType: React.FC = (props) => { /> ) : undefined} > + = (props) => { setValue(value as number + 1); setTimeout(() => { - const newRow = document.getElementById(`${path}-row-${rowIndex + 1}`); - - if (newRow) { - const bounds = newRow.getBoundingClientRect(); - window.scrollBy({ - top: bounds.top - 100, - behavior: 'smooth', - }); - } + scrollToID(`${path}-row-${rowIndex + 1}`); }, 0); }, [path, setValue, value, blocks, dispatchFields, operation, id, user, locale]); @@ -118,6 +111,10 @@ const Blocks: React.FC = (props) => { dispatchFields({ type: 'DUPLICATE_ROW', rowIndex, path }); dispatchRows({ type: 'ADD', rowIndex, blockType }); setValue(value as number + 1); + + setTimeout(() => { + scrollToID(`${path}-row-${rowIndex + 1}`); + }, 0); }, [dispatchRows, dispatchFields, path, setValue, value]); const removeRow = useCallback((rowIndex: number) => { @@ -334,6 +331,10 @@ const Blocks: React.FC = (props) => { ) : undefined} > + = (props) => { const { @@ -23,12 +22,13 @@ const Group: React.FC = (props) => { style, className, width, - hideGutter, description, }, permissions, } = props; + const isWithinCollapsible = useCollapsible(); + const path = pathFromProps || name; return ( @@ -37,17 +37,15 @@ const Group: React.FC = (props) => { className={[ 'field-type', baseClass, + isWithinCollapsible && `${baseClass}--within-collapsible`, className, - !label && `${baseClass}--no-label`, ].filter(Boolean).join(' ')} style={{ ...style, width, }} > - {!hideGutter && ()} - -
+
{(label || description) && (
{label && ( @@ -59,19 +57,15 @@ const Group: React.FC = (props) => { />
)} -
- - ({ - ...subField, - path: `${path}${fieldAffectsData(subField) ? `.${subField.name}` : ''}`, - }))} - /> - -
+ ({ + ...subField, + path: `${path}${fieldAffectsData(subField) ? `.${subField.name}` : ''}`, + }))} + />
); diff --git a/src/admin/components/forms/field-types/Upload/Add/index.tsx b/src/admin/components/forms/field-types/Upload/Add/index.tsx index 580d8e2c2b..6807411b81 100644 --- a/src/admin/components/forms/field-types/Upload/Add/index.tsx +++ b/src/admin/components/forms/field-types/Upload/Add/index.tsx @@ -8,7 +8,6 @@ import Button from '../../../../elements/Button'; import RenderFields from '../../../RenderFields'; import FormSubmit from '../../../Submit'; import Upload from '../../../../views/collections/Edit/Upload'; -import { NegativeFieldGutterProvider } from '../../../FieldTypeGutter/context'; import ViewDescription from '../../../../elements/ViewDescription'; import { Props } from './types'; @@ -82,14 +81,12 @@ const AddUploadModal: React.FC = (props) => { - - - + diff --git a/src/admin/components/forms/field-types/rowReducer.tsx b/src/admin/components/forms/field-types/rowReducer.tsx index 584be19d07..448ac27f5c 100644 --- a/src/admin/components/forms/field-types/rowReducer.tsx +++ b/src/admin/components/forms/field-types/rowReducer.tsx @@ -85,8 +85,6 @@ const reducer = (currentState: Row[], action: Action): Row[] => { collapsed: collapse, })); - console.log(newState); - return newState; } diff --git a/src/admin/components/views/Account/index.tsx b/src/admin/components/views/Account/index.tsx index 3fca07099c..4a5de78355 100644 --- a/src/admin/components/views/Account/index.tsx +++ b/src/admin/components/views/Account/index.tsx @@ -9,7 +9,6 @@ import { useLocale } from '../../utilities/Locale'; import DefaultAccount from './Default'; import buildStateFromSchema from '../../forms/Form/buildStateFromSchema'; import RenderCustomComponent from '../../utilities/RenderCustomComponent'; -import { NegativeFieldGutterProvider } from '../../forms/FieldTypeGutter/context'; import { useDocumentInfo } from '../../utilities/DocumentInfo'; const AccountView: React.FC = () => { @@ -73,22 +72,20 @@ const AccountView: React.FC = () => { }, [dataToRender, fields, id, user, locale]); return ( - - - + ); }; diff --git a/src/admin/components/views/CreateFirstUser/index.tsx b/src/admin/components/views/CreateFirstUser/index.tsx index f85ba3c59c..8479f13947 100644 --- a/src/admin/components/views/CreateFirstUser/index.tsx +++ b/src/admin/components/views/CreateFirstUser/index.tsx @@ -10,8 +10,6 @@ import FormSubmit from '../../forms/Submit'; import { Props } from './types'; import { Field } from '../../../../fields/config/types'; -import { NegativeFieldGutterProvider } from '../../forms/FieldTypeGutter/context'; - import './index.scss'; const baseClass = 'create-first-user'; @@ -68,15 +66,13 @@ const CreateFirstUser: React.FC = (props) => { action={`${serverURL}${api}/${userSlug}/first-register`} validationOperation="create" > - - - + Create diff --git a/src/admin/components/views/Global/index.tsx b/src/admin/components/views/Global/index.tsx index d5aaba65b0..74af7a30ee 100644 --- a/src/admin/components/views/Global/index.tsx +++ b/src/admin/components/views/Global/index.tsx @@ -10,7 +10,6 @@ import { useLocale } from '../../utilities/Locale'; import RenderCustomComponent from '../../utilities/RenderCustomComponent'; import DefaultGlobal from './Default'; import buildStateFromSchema from '../../forms/Form/buildStateFromSchema'; -import { NegativeFieldGutterProvider } from '../../forms/FieldTypeGutter/context'; import { IndexProps } from './types'; import { useDocumentInfo } from '../../utilities/DocumentInfo'; @@ -77,22 +76,20 @@ const GlobalView: React.FC = (props) => { const globalPermissions = permissions?.globals?.[slug]; return ( - - - + ); }; export default GlobalView; diff --git a/src/admin/components/views/collections/Edit/index.tsx b/src/admin/components/views/collections/Edit/index.tsx index b3a008bc9d..e90e6ee221 100644 --- a/src/admin/components/views/collections/Edit/index.tsx +++ b/src/admin/components/views/collections/Edit/index.tsx @@ -9,7 +9,6 @@ import RenderCustomComponent from '../../../utilities/RenderCustomComponent'; import DefaultEdit from './Default'; import formatFields from './formatFields'; import buildStateFromSchema from '../../../forms/Form/buildStateFromSchema'; -import { NegativeFieldGutterProvider } from '../../../forms/FieldTypeGutter/context'; import { useLocale } from '../../../utilities/Locale'; import { IndexProps } from './types'; import { StepNavItem } from '../../../elements/StepNav/types'; @@ -120,24 +119,22 @@ const EditView: React.FC = (props) => { const hasSavePermission = (isEditing && collectionPermissions?.update?.permission) || (!isEditing && collectionPermissions?.create?.permission); return ( - - - + ); }; export default EditView; diff --git a/src/admin/utilities/scrollToID.ts b/src/admin/utilities/scrollToID.ts new file mode 100644 index 0000000000..da48290bbe --- /dev/null +++ b/src/admin/utilities/scrollToID.ts @@ -0,0 +1,11 @@ +export const scrollToID = (id: string): void => { + const element = document.getElementById(id); + + if (element) { + const bounds = element.getBoundingClientRect(); + window.scrollBy({ + top: bounds.top - 100, + behavior: 'smooth', + }); + } +}; diff --git a/src/fields/config/schema.ts b/src/fields/config/schema.ts index 37ef9de84f..870ef1152a 100644 --- a/src/fields/config/schema.ts +++ b/src/fields/config/schema.ts @@ -203,7 +203,6 @@ export const group = baseField.keys({ joi.func(), ), admin: baseAdminFields.keys({ - hideGutter: joi.boolean().default(false), description: joi.string(), }), }); diff --git a/src/fields/config/types.ts b/src/fields/config/types.ts index ecc7b5e301..6e73d027fe 100644 --- a/src/fields/config/types.ts +++ b/src/fields/config/types.ts @@ -160,9 +160,6 @@ export type DateField = FieldBase & { export type GroupField = FieldBase & { type: 'group'; fields: Field[]; - admin?: Admin & { - hideGutter?: boolean - } } export type RowAdmin = Omit; diff --git a/test/fields/collections/Collapsible/index.ts b/test/fields/collections/Collapsible/index.ts index 3c46c4c85e..e2cdb25110 100644 --- a/test/fields/collections/Collapsible/index.ts +++ b/test/fields/collections/Collapsible/index.ts @@ -16,6 +16,16 @@ const CollapsibleFields: CollectionConfig = { type: 'text', required: true, }, + { + name: 'group', + type: 'group', + fields: [ + { + name: 'textWithinGroup', + type: 'text', + }, + ], + }, ], }, ], @@ -23,6 +33,9 @@ const CollapsibleFields: CollectionConfig = { export const collapsibleDoc = { text: 'Seeded collapsible doc', + group: { + textWithinGroup: 'some text within a group', + }, }; export default CollapsibleFields; diff --git a/test/fields/collections/Tabs/index.ts b/test/fields/collections/Tabs/index.ts index dabf5578a6..a380360504 100644 --- a/test/fields/collections/Tabs/index.ts +++ b/test/fields/collections/Tabs/index.ts @@ -13,6 +13,10 @@ const TabsFields: CollectionConfig = { fields: [ { name: 'array', + labels: { + singular: 'Item', + plural: 'Items', + }, type: 'array', required: true, fields: [ diff --git a/test/fields/config.ts b/test/fields/config.ts index 80a57ee238..48f429de6e 100644 --- a/test/fields/config.ts +++ b/test/fields/config.ts @@ -16,11 +16,11 @@ export default buildConfig({ BlockFields, CollapsibleFields, ConditionalLogic, + PointFields, RichTextFields, SelectFields, TabsFields, TextFields, - PointFields, ], localization: { defaultLocale: 'en',