feat: updates block field to use new collapsible
This commit is contained in:
@@ -24,38 +24,26 @@ const config = buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Overriding SCSS variables
|
||||
### Overriding built-in styles
|
||||
|
||||
You can specify your own SCSS variable stylesheet that will allow for the override of Payload's base theme. This unlocks a ton of powerful theming and design options such as:
|
||||
To make it as easy as possible for you to override our styles, Payload uses [BEM naming conventions](http://getbem.com/) for all CSS within the Admin UI. If you provide your own CSS, you can override any built-in styles easily.
|
||||
|
||||
- Changing dashboard font families
|
||||
- Modifying color palette
|
||||
- Creating a dark theme
|
||||
- Etc.
|
||||
In addition to adding your own style definitions, you can also override Payload's built-in CSS variables. We use as much as possible behind the scenes, and you can override any of them that you'd like to.
|
||||
|
||||
To do so, provide your base Payload config with a path to your own SCSS variable sheet.
|
||||
You can find the built-in Payload CSS variables within [`./src/admin/scss/app.scss`](https://github.com/payloadcms/payload/blob/master/src/admin/scss/app.scss). The following variables are defined and can be overridden:
|
||||
|
||||
**Example in payload.config.js:**
|
||||
```js
|
||||
import { buildConfig } from 'payload/config';
|
||||
import path from 'path';
|
||||
- Breakpoints
|
||||
- Base color shades (white to black by default)
|
||||
- Success / warning / error color shades
|
||||
- Theme-specific colors (background, input background, text color, etc.)
|
||||
- Elevation colors (used to determine how "bright" something should be when compared to the background)
|
||||
- Fonts
|
||||
- Horizontal gutter
|
||||
|
||||
const config = buildConfig({
|
||||
admin: {
|
||||
scss: path.resolve(__dirname, 'relative/path/to/vars.scss'),
|
||||
},
|
||||
})
|
||||
```
|
||||
#### Dark mode
|
||||
|
||||
**Example stylesheet override:**
|
||||
```scss
|
||||
$font-body: 'Papyrus';
|
||||
$style-radius-m: 10px;
|
||||
```
|
||||
|
||||
To reference all Sass variables that you can override, look at the default [SCSS variable stylesheet](https://github.com/payloadcms/payload/blob/master/src/admin/scss/vars.scss) within the Payload source code.
|
||||
|
||||
<Banner type="error">
|
||||
<strong>Warning:</strong><br />
|
||||
Only SCSS variables, mixins, functions, and extends are allowed in <strong>your SCSS overrides</strong>. Do not attempt to add any CSS declarations to this file, as this variable stylesheet is imported by many components throughout the Payload Admin panel and will result in your CSS definition(s) being duplicated many times. If you need to add real CSS definitions, see "Adding your own CSS / SCSS" the top of this page.
|
||||
<Banner type="warning">
|
||||
If you're overriding colors or theme elevations, make sure to consider how your changes will affect dark mode.
|
||||
</Banner>
|
||||
|
||||
By default, Payload automatically overrides all `--theme-elevation`s and inverts all success / warning / error shades to suit dark mode. We also update some base theme variables like `--theme-bg`, `--theme-text`, etc.
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
@import '../dist/admin/scss/vars';
|
||||
@import '../dist/admin/scss/z-index';
|
||||
|
||||
//////////////////////////////
|
||||
// IMPORT OVERRIDES
|
||||
//////////////////////////////
|
||||
|
||||
@import '~payload-scss-overrides';
|
||||
|
||||
@import '../dist/admin/scss/type';
|
||||
@import '../dist/admin/scss/queries';
|
||||
@import '../dist/admin/scss/resets';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export type Props = {
|
||||
addRow: (current: number) => void
|
||||
addRow: (current: number, blockType?: string) => void
|
||||
duplicateRow: (current: number) => void
|
||||
removeRow: (index: number) => void
|
||||
moveRow: (from: number, to: number) => void
|
||||
|
||||
@@ -87,5 +87,9 @@
|
||||
header {
|
||||
padding: base(.75) var(--gutter-h);
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding: var(--gutter-h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ $cal-icon-width: 18px;
|
||||
background: var(--theme-input-bg);
|
||||
display: inline-flex;
|
||||
border: none;
|
||||
font-family: $font-body;
|
||||
font-family: var(--font-body);
|
||||
font-weight: 100;
|
||||
border-radius: 0;
|
||||
color: var(--theme-elevation-800);
|
||||
|
||||
@@ -42,6 +42,10 @@
|
||||
////////////////////////////////
|
||||
|
||||
&--size-small {
|
||||
.popup__scroll {
|
||||
padding: base(.75);
|
||||
}
|
||||
|
||||
.popup__content {
|
||||
@include shadow-m;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useWindowInfo } from '@faceless-ui/window-info';
|
||||
import { useScrollInfo } from '@faceless-ui/scroll-info';
|
||||
import { Props } from './types';
|
||||
@@ -40,13 +40,13 @@ const Popup: React.FC<Props> = (props) => {
|
||||
const { y: scrollY } = useScrollInfo();
|
||||
const { height: windowHeight, width: windowWidth } = useWindowInfo();
|
||||
|
||||
const handleClickOutside = (e) => {
|
||||
const handleClickOutside = useCallback((e) => {
|
||||
if (contentRef.current.contains(e.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setActive(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useThrottledEffect(() => {
|
||||
if (contentRef.current && buttonRef.current) {
|
||||
@@ -99,7 +99,7 @@ const Popup: React.FC<Props> = (props) => {
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
};
|
||||
}, [active, onToggleOpen]);
|
||||
}, [active, handleClickOutside, onToggleOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
setActive(forceOpen);
|
||||
|
||||
@@ -46,7 +46,7 @@ div.react-select {
|
||||
color: var(--theme-elevation-1000);
|
||||
|
||||
input {
|
||||
font-family: $font-body;
|
||||
font-family: var(--font-body);
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ div.react-select {
|
||||
}
|
||||
|
||||
.rs__option {
|
||||
font-family: $font-body;
|
||||
font-family: var(--font-body);
|
||||
font-size: $baseline-body-size;
|
||||
padding: base(.375) base(.75);
|
||||
color: var(--theme-elevation-800);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
flex-grow: 1;
|
||||
color: var(--theme-elevation-800);
|
||||
background-color: transparent;
|
||||
font-family: $font-body;
|
||||
font-family: var(--font-body);
|
||||
font-weight: 600;
|
||||
font-size: base(.75);
|
||||
padding: base(.1) base(.2);
|
||||
|
||||
@@ -44,6 +44,8 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
},
|
||||
} = props;
|
||||
|
||||
const path = pathFromProps || name;
|
||||
|
||||
// Handle labeling for Arrays, Global Arrays, and Blocks
|
||||
const getLabels = (p: Props) => {
|
||||
if (p?.labels) return p.labels;
|
||||
@@ -66,8 +68,6 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
|
||||
const { dispatchFields } = formContext;
|
||||
|
||||
const path = pathFromProps || name;
|
||||
|
||||
const memoizedValidate = useCallback((value, options) => {
|
||||
return validate(value, { ...options, minRows, maxRows, required });
|
||||
}, [maxRows, minRows, required, validate]);
|
||||
@@ -91,6 +91,18 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
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',
|
||||
});
|
||||
}
|
||||
}, 0);
|
||||
}, [dispatchRows, dispatchFields, fields, path, setValue, value, operation, id, user, locale]);
|
||||
|
||||
const duplicateRow = useCallback(async (rowIndex: number) => {
|
||||
@@ -233,52 +245,57 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
ref={provided.innerRef}
|
||||
{...provided.droppableProps}
|
||||
>
|
||||
{rows.length > 0 && rows.map((row, i) => (
|
||||
<Draggable
|
||||
key={row.id}
|
||||
draggableId={row.id}
|
||||
index={i}
|
||||
isDragDisabled={readOnly}
|
||||
>
|
||||
{(providedDrag) => (
|
||||
<div
|
||||
ref={providedDrag.innerRef}
|
||||
{...providedDrag.draggableProps}
|
||||
>
|
||||
<Collapsible
|
||||
collapsed={row.collapsed}
|
||||
onToggle={(collapsed) => setCollapse(row.id, collapsed)}
|
||||
className={`${baseClass}__row`}
|
||||
key={row.id}
|
||||
dragHandleProps={providedDrag.dragHandleProps}
|
||||
header={`${labels.singular} ${i + 1}`}
|
||||
actions={!readOnly ? (
|
||||
<ArrayAction
|
||||
rowCount={rows.length}
|
||||
duplicateRow={duplicateRow}
|
||||
addRow={addRow}
|
||||
moveRow={moveRow}
|
||||
removeRow={removeRow}
|
||||
index={i}
|
||||
/>
|
||||
) : undefined}
|
||||
>
|
||||
<RenderFields
|
||||
forceRender
|
||||
readOnly={readOnly}
|
||||
fieldTypes={fieldTypes}
|
||||
permissions={permissions.fields}
|
||||
fieldSchema={fields.map((field) => ({
|
||||
...field,
|
||||
path: `${path}.${i}${fieldAffectsData(field) ? `.${field.name}` : ''}`,
|
||||
}))}
|
||||
/>
|
||||
{rows.length > 0 && rows.map((row, i) => {
|
||||
const rowNumber = i + 1;
|
||||
|
||||
</Collapsible>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
))}
|
||||
return (
|
||||
<Draggable
|
||||
key={row.id}
|
||||
draggableId={row.id}
|
||||
index={i}
|
||||
isDragDisabled={readOnly}
|
||||
>
|
||||
{(providedDrag) => (
|
||||
<div
|
||||
id={`${path}-row-${i}`}
|
||||
ref={providedDrag.innerRef}
|
||||
{...providedDrag.draggableProps}
|
||||
>
|
||||
<Collapsible
|
||||
collapsed={row.collapsed}
|
||||
onToggle={(collapsed) => setCollapse(row.id, collapsed)}
|
||||
className={`${baseClass}__row`}
|
||||
key={row.id}
|
||||
dragHandleProps={providedDrag.dragHandleProps}
|
||||
header={`${labels.singular} ${rowNumber >= 10 ? rowNumber : `0${rowNumber}`}`}
|
||||
actions={!readOnly ? (
|
||||
<ArrayAction
|
||||
rowCount={rows.length}
|
||||
duplicateRow={duplicateRow}
|
||||
addRow={addRow}
|
||||
moveRow={moveRow}
|
||||
removeRow={removeRow}
|
||||
index={i}
|
||||
/>
|
||||
) : undefined}
|
||||
>
|
||||
<RenderFields
|
||||
forceRender
|
||||
readOnly={readOnly}
|
||||
fieldTypes={fieldTypes}
|
||||
permissions={permissions.fields}
|
||||
fieldSchema={fields.map((field) => ({
|
||||
...field,
|
||||
path: `${path}.${i}${fieldAffectsData(field) ? `.${field.name}` : ''}`,
|
||||
}))}
|
||||
/>
|
||||
|
||||
</Collapsible>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
{(rows.length < minRows || (required && rows.length === 0)) && (
|
||||
<Banner type="error">
|
||||
This field requires at least
|
||||
@@ -300,7 +317,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
{(!readOnly && (!hasMaxRows)) && (
|
||||
{(!readOnly && !hasMaxRows) && (
|
||||
<div className={`${baseClass}__add-button-wrap`}>
|
||||
<Button
|
||||
onClick={() => addRow(value as number)}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import React, { useCallback, useEffect, useReducer, useState } from 'react';
|
||||
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
|
||||
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
||||
|
||||
import { useAuth } from '../../../utilities/Auth';
|
||||
import { usePreferences } from '../../../utilities/Preferences';
|
||||
import { useLocale } from '../../../utilities/Locale';
|
||||
import withCondition from '../../withCondition';
|
||||
import Button from '../../../elements/Button';
|
||||
import reducer from '../rowReducer';
|
||||
import reducer, { Row } from '../rowReducer';
|
||||
import { useDocumentInfo } from '../../../utilities/DocumentInfo';
|
||||
import { useForm } from '../../Form/context';
|
||||
import buildStateFromSchema from '../../Form/buildStateFromSchema';
|
||||
import DraggableSection from '../../DraggableSection';
|
||||
import Error from '../../Error';
|
||||
import useField from '../../useField';
|
||||
import Popup from '../../../elements/Popup';
|
||||
@@ -20,10 +19,15 @@ import Banner from '../../../elements/Banner';
|
||||
import FieldDescription from '../../FieldDescription';
|
||||
import { Props } from './types';
|
||||
import { useOperation } from '../../../utilities/OperationProvider';
|
||||
import { Collapsible } from '../../../elements/Collapsible';
|
||||
import { ArrayAction } from '../../../elements/ArrayAction';
|
||||
import RenderFields from '../../RenderFields';
|
||||
import { fieldAffectsData } from '../../../../../fields/config/types';
|
||||
|
||||
import './index.scss';
|
||||
import Pill from '../../../elements/Pill';
|
||||
|
||||
const baseClass = 'field-type blocks';
|
||||
const baseClass = 'blocks-field';
|
||||
|
||||
const labelDefaults = {
|
||||
singular: 'Block',
|
||||
@@ -68,6 +72,7 @@ const Blocks: React.FC<Props> = (props) => {
|
||||
}, [maxRows, minRows, required, validate]);
|
||||
|
||||
const [disableFormData, setDisableFormData] = useState(false);
|
||||
const [selectorIndexOpen, setSelectorIndexOpen] = useState<number>();
|
||||
|
||||
const {
|
||||
showError,
|
||||
@@ -81,27 +86,56 @@ const Blocks: React.FC<Props> = (props) => {
|
||||
condition,
|
||||
});
|
||||
|
||||
const addRow = useCallback(async (rowIndex, blockType) => {
|
||||
const onAddPopupToggle = useCallback((open) => {
|
||||
if (!open) {
|
||||
setSelectorIndexOpen(undefined);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const addRow = useCallback(async (rowIndex: number, blockType: string) => {
|
||||
const block = blocks.find((potentialBlock) => potentialBlock.slug === blockType);
|
||||
|
||||
const subFieldState = await buildStateFromSchema({ fieldSchema: block.fields, operation, id, user, locale });
|
||||
|
||||
dispatchFields({ type: 'ADD_ROW', rowIndex, subFieldState, path, blockType });
|
||||
dispatchRows({ type: 'ADD', rowIndex, blockType });
|
||||
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',
|
||||
});
|
||||
}
|
||||
}, 0);
|
||||
}, [path, setValue, value, blocks, dispatchFields, operation, id, user, locale]);
|
||||
|
||||
const removeRow = useCallback((rowIndex) => {
|
||||
const duplicateRow = useCallback(async (rowIndex: number, blockType: string) => {
|
||||
dispatchFields({ type: 'DUPLICATE_ROW', rowIndex, path });
|
||||
dispatchRows({ type: 'ADD', rowIndex, blockType });
|
||||
setValue(value as number + 1);
|
||||
}, [dispatchRows, dispatchFields, path, setValue, value]);
|
||||
|
||||
const removeRow = useCallback((rowIndex: number) => {
|
||||
dispatchRows({ type: 'REMOVE', rowIndex });
|
||||
dispatchFields({ type: 'REMOVE_ROW', rowIndex, path });
|
||||
setValue(value as number - 1);
|
||||
}, [path, setValue, value, dispatchFields]);
|
||||
|
||||
const moveRow = useCallback((moveFromIndex, moveToIndex) => {
|
||||
const moveRow = useCallback((moveFromIndex: number, moveToIndex: number) => {
|
||||
dispatchRows({ type: 'MOVE', moveFromIndex, moveToIndex });
|
||||
dispatchFields({ type: 'MOVE_ROW', moveFromIndex, moveToIndex, path });
|
||||
}, [dispatchRows, dispatchFields, path]);
|
||||
|
||||
const onDragEnd = useCallback((result) => {
|
||||
if (!result.destination) return;
|
||||
const sourceIndex = result.source.index;
|
||||
const destinationIndex = result.destination.index;
|
||||
moveRow(sourceIndex, destinationIndex);
|
||||
}, [moveRow]);
|
||||
|
||||
const setCollapse = useCallback(async (rowID: string, collapsed: boolean) => {
|
||||
dispatchRows({ type: 'SET_COLLAPSE', id: rowID, collapsed });
|
||||
|
||||
@@ -130,26 +164,28 @@ const Blocks: React.FC<Props> = (props) => {
|
||||
}
|
||||
}, [preferencesKey, preferences, path, setPreference, rows]);
|
||||
|
||||
const onDragEnd = useCallback((result) => {
|
||||
if (!result.destination) return;
|
||||
const sourceIndex = result.source.index;
|
||||
const destinationIndex = result.destination.index;
|
||||
moveRow(sourceIndex, destinationIndex);
|
||||
}, [moveRow]);
|
||||
const toggleCollapseAll = useCallback(async (collapse: boolean) => {
|
||||
dispatchRows({ type: 'SET_ALL_COLLAPSED', collapse });
|
||||
|
||||
// Get preferences, and once retrieved,
|
||||
// Reset rows with preferences included
|
||||
useEffect(() => {
|
||||
const data = formContext.getDataByPath(path);
|
||||
if (preferencesKey) {
|
||||
const preferencesToSet = preferences || { fields: {} };
|
||||
|
||||
if (Array.isArray(data) && preferences) {
|
||||
dispatchRows({ type: 'SET_ALL', data: data || [], collapsedState: preferences?.fields?.[path]?.collapsed });
|
||||
setPreference(preferencesKey, {
|
||||
...preferencesToSet,
|
||||
fields: {
|
||||
...preferencesToSet?.fields || {},
|
||||
[path]: {
|
||||
...preferencesToSet?.fields?.[path],
|
||||
collapsed: collapse ? rows.map(({ id: rowID }) => rowID) : [],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [formContext, path, preferencesKey, preferences]);
|
||||
}, [path, preferences, preferencesKey, rows, setPreference]);
|
||||
|
||||
// Set row count on mount and when form context is reset
|
||||
useEffect(() => {
|
||||
const data = formContext.getDataByPath(path);
|
||||
const data = formContext.getDataByPath<Row[]>(path);
|
||||
dispatchRows({ type: 'SET_ALL', data: data || [] });
|
||||
}, [formContext, path]);
|
||||
|
||||
@@ -166,6 +202,7 @@ const Blocks: React.FC<Props> = (props) => {
|
||||
const hasMaxRows = maxRows && rows.length >= maxRows;
|
||||
|
||||
const classes = [
|
||||
'field-type',
|
||||
baseClass,
|
||||
className,
|
||||
].filter(Boolean).join(' ');
|
||||
@@ -182,7 +219,29 @@ const Blocks: React.FC<Props> = (props) => {
|
||||
/>
|
||||
</div>
|
||||
<header className={`${baseClass}__header`}>
|
||||
<h3>{label}</h3>
|
||||
<div className={`${baseClass}__header-wrap`}>
|
||||
<h3>{label}</h3>
|
||||
<ul className={`${baseClass}__header-actions`}>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleCollapseAll(true)}
|
||||
className={`${baseClass}__header-action`}
|
||||
>
|
||||
Collapse All
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleCollapseAll(false)}
|
||||
className={`${baseClass}__header-action`}
|
||||
>
|
||||
Show All
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<FieldDescription
|
||||
value={value}
|
||||
description={description}
|
||||
@@ -202,30 +261,82 @@ const Blocks: React.FC<Props> = (props) => {
|
||||
const { blockType } = row;
|
||||
const blockToRender = blocks.find((block) => block.slug === blockType);
|
||||
|
||||
const rowNumber = i + 1;
|
||||
|
||||
if (blockToRender) {
|
||||
return (
|
||||
<DraggableSection
|
||||
readOnly={readOnly}
|
||||
<Draggable
|
||||
key={row.id}
|
||||
id={row.id}
|
||||
blockType="blocks"
|
||||
blocks={blocks}
|
||||
label={blockToRender?.labels?.singular}
|
||||
isCollapsed={row.collapsed}
|
||||
rowCount={rows.length}
|
||||
rowIndex={i}
|
||||
addRow={addRow}
|
||||
removeRow={removeRow}
|
||||
moveRow={moveRow}
|
||||
setRowCollapse={setCollapse}
|
||||
parentPath={path}
|
||||
fieldTypes={fieldTypes}
|
||||
permissions={permissions}
|
||||
hasMaxRows={hasMaxRows}
|
||||
fieldSchema={[
|
||||
...blockToRender.fields,
|
||||
]}
|
||||
/>
|
||||
draggableId={row.id}
|
||||
index={i}
|
||||
isDragDisabled={readOnly}
|
||||
>
|
||||
{(providedDrag) => (
|
||||
<div
|
||||
id={`${path}-row-${i}`}
|
||||
ref={providedDrag.innerRef}
|
||||
{...providedDrag.draggableProps}
|
||||
>
|
||||
<Collapsible
|
||||
collapsed={row.collapsed}
|
||||
onToggle={(collapsed) => setCollapse(row.id, collapsed)}
|
||||
className={`${baseClass}__row`}
|
||||
key={row.id}
|
||||
dragHandleProps={providedDrag.dragHandleProps}
|
||||
header={(
|
||||
<div className={`${baseClass}__block-header`}>
|
||||
<span className={`${baseClass}__block-number`}>
|
||||
{rowNumber >= 10 ? rowNumber : `0${rowNumber}`}
|
||||
</span>
|
||||
<Pill className={`${baseClass}__block-pill ${baseClass}__block-pill-${blockType}`}>
|
||||
{blockToRender.labels.singular}
|
||||
</Pill>
|
||||
</div>
|
||||
)}
|
||||
actions={!readOnly ? (
|
||||
<React.Fragment>
|
||||
<Popup
|
||||
key={`${blockType}-${i}`}
|
||||
forceOpen={selectorIndexOpen === i}
|
||||
onToggleOpen={onAddPopupToggle}
|
||||
buttonType="none"
|
||||
size="large"
|
||||
horizontalAlign="right"
|
||||
render={({ close }) => (
|
||||
<BlockSelector
|
||||
blocks={blocks}
|
||||
addRow={addRow}
|
||||
addRowIndex={i}
|
||||
close={close}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<ArrayAction
|
||||
rowCount={rows.length}
|
||||
duplicateRow={() => duplicateRow(i, blockType)}
|
||||
addRow={() => setSelectorIndexOpen(i)}
|
||||
moveRow={moveRow}
|
||||
removeRow={removeRow}
|
||||
index={i}
|
||||
/>
|
||||
</React.Fragment>
|
||||
) : undefined}
|
||||
>
|
||||
<RenderFields
|
||||
forceRender
|
||||
readOnly={readOnly}
|
||||
fieldTypes={fieldTypes}
|
||||
permissions={permissions.fields}
|
||||
fieldSchema={blockToRender.fields.map((field) => ({
|
||||
...field,
|
||||
path: `${path}.${i}${fieldAffectsData(field) ? `.${field.name}` : ''}`,
|
||||
}))}
|
||||
/>
|
||||
|
||||
</Collapsible>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -251,7 +362,7 @@ const Blocks: React.FC<Props> = (props) => {
|
||||
)}
|
||||
</Droppable>
|
||||
|
||||
{(!readOnly && (rows.length < maxRows || maxRows === undefined)) && (
|
||||
{(!readOnly && !hasMaxRows) && (
|
||||
<div className={`${baseClass}__add-button-wrap`}>
|
||||
<Popup
|
||||
buttonType="custom"
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
@import '../../../../scss/styles.scss';
|
||||
|
||||
.field-type.blocks {
|
||||
.blocks-field {
|
||||
margin: base(2) 0;
|
||||
min-width: base(15);
|
||||
|
||||
&__header {
|
||||
h3 {
|
||||
@@ -12,6 +11,42 @@
|
||||
margin-bottom: base(1);
|
||||
}
|
||||
|
||||
&__header-wrap {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__header-actions {
|
||||
list-style: none;
|
||||
margin: 0 0 0 auto;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__header-action {
|
||||
@extend %btn-reset;
|
||||
cursor: pointer;
|
||||
margin-left: base(.5);
|
||||
font-weight: 600;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
&__block-header {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__block-number {
|
||||
margin-right: base(.375)
|
||||
}
|
||||
|
||||
&__row {
|
||||
margin-bottom: base(.5);
|
||||
}
|
||||
|
||||
&__error-wrap {
|
||||
position: relative;
|
||||
}
|
||||
@@ -28,27 +63,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.section .section {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.section__content {
|
||||
>div>div {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
min-width: calc(100vw - #{base(2)});
|
||||
}
|
||||
}
|
||||
|
||||
.field-type.group,
|
||||
.field-type.array,
|
||||
.field-type.blocks {
|
||||
.field-type.blocks {
|
||||
.field-type.blocks__add-button-wrap {
|
||||
margin-left: base(3);
|
||||
}
|
||||
.field-type:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
--gutter-h: #{base(.75)};
|
||||
--gutter-h: #{base(.5)};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,14 +250,8 @@ html {
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: $font-body;
|
||||
font-family: var(--font-body);
|
||||
font-weight: 400;
|
||||
color: var(--theme-text);
|
||||
margin: 0;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/* Used as a placeholder for when the Payload app does not have custom SCSS */
|
||||
@@ -5,8 +5,6 @@
|
||||
// IMPORT OVERRIDES
|
||||
//////////////////////////////
|
||||
|
||||
@import '~payload-scss-overrides';
|
||||
|
||||
@import 'type';
|
||||
@import 'queries';
|
||||
@import 'resets';
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
%h4,
|
||||
%h5,
|
||||
%h6 {
|
||||
font-family: $font-body;
|
||||
font-family: var(--font-body);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
@use 'sass:math';
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// All variables and mixins within this file are able to be overridden by
|
||||
// developers using Payload. No CSS definitions should be placed in this file.
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////
|
||||
// BREAKPOINTS
|
||||
/////////////////////////////
|
||||
@@ -26,26 +21,6 @@ $baseline : math.div($baseline-px, $baseline-body-size)+rem;
|
||||
@return math.div($baseline-px, $baseline-body-size) * $multiplier +rem;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// FONTS
|
||||
//////////////////////////////
|
||||
|
||||
$font-body : 'Suisse Intl' !default;
|
||||
$font-mono : monospace !default;
|
||||
|
||||
//////////////////////////////
|
||||
// COLORS (LEGACY - DO NOT USE. PREFER CSS VARIABLES)
|
||||
//////////////////////////////
|
||||
|
||||
$color-dark-gray : #333333 !default;
|
||||
$color-gray : #9A9A9A !default;
|
||||
$color-light-gray : #DADADA !default;
|
||||
$color-background-gray : #F3F3F3 !default;
|
||||
$color-red : #ff6f76 !default;
|
||||
$color-yellow : #FDFFA4 !default;
|
||||
$color-green : #B2FFD6 !default;
|
||||
$color-purple : #F3DDF3 !default;
|
||||
|
||||
//////////////////////////////
|
||||
// STYLES
|
||||
//////////////////////////////
|
||||
@@ -144,7 +119,7 @@ $focus-box-shadow: 0 0 0 $style-stroke-width-m var(--color-success-500);
|
||||
|
||||
@mixin formInput () {
|
||||
@include inputShadow;
|
||||
font-family: $font-body;
|
||||
font-family: var(--font-body);
|
||||
width: 100%;
|
||||
border: 1px solid var(--theme-elevation-150);
|
||||
background: var(--theme-input-bg);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default (element: HTMLElement): number => {
|
||||
export const getOffsetTop = (element: HTMLElement): number => {
|
||||
let el = element;
|
||||
// Set our distance placeholder
|
||||
let distance = 0;
|
||||
|
||||
@@ -18,7 +18,6 @@ export const defaults: Config = {
|
||||
indexHTML: path.resolve(__dirname, '../admin/index.html'),
|
||||
components: {},
|
||||
css: path.resolve(__dirname, '../admin/scss/custom.css'),
|
||||
scss: path.resolve(__dirname, '../admin/scss/overrides.scss'),
|
||||
dateFormat: 'MMMM do yyyy, h:mm a',
|
||||
},
|
||||
typescript: {
|
||||
|
||||
@@ -45,7 +45,6 @@ export default joi.object({
|
||||
disable: joi.bool(),
|
||||
indexHTML: joi.string(),
|
||||
css: joi.string(),
|
||||
scss: joi.string(),
|
||||
dateFormat: joi.string(),
|
||||
components: joi.object()
|
||||
.keys({
|
||||
|
||||
@@ -112,7 +112,6 @@ export type Config = {
|
||||
disable?: boolean;
|
||||
indexHTML?: string;
|
||||
css?: string
|
||||
scss?: string
|
||||
dateFormat?: string
|
||||
components?: {
|
||||
routes?: AdminRoute[]
|
||||
|
||||
@@ -54,7 +54,6 @@ export default (config: SanitizedConfig): Configuration => ({
|
||||
'payload-config': config.paths.config,
|
||||
payload$: mockModulePath,
|
||||
'payload-user-css': config.admin.css,
|
||||
'payload-scss-overrides': config.admin.scss,
|
||||
dotenv: mockDotENVPath,
|
||||
},
|
||||
extensions: ['.ts', '.tsx', '.js', '.json'],
|
||||
|
||||
Reference in New Issue
Block a user