chore: misc css (#3391)

This commit is contained in:
Jacob Fletcher
2023-09-27 13:18:29 -04:00
committed by GitHub
parent d61eef23d1
commit aa94c00bc6
120 changed files with 1317 additions and 903 deletions

View File

@@ -10,10 +10,6 @@
position: relative;
}
&--nested {
margin-bottom: $baseline !important;
}
&__drag {
opacity: 0.5;
position: absolute;
@@ -111,7 +107,7 @@
background-color: var(--theme-elevation-0);
border-bottom-left-radius: $style-radius-s;
border-bottom-right-radius: $style-radius-s;
padding: $baseline $baseline 0 $baseline;
padding: var(--base);
}
@include small-break {

View File

@@ -16,4 +16,8 @@
background-color: var(--theme-elevation-150);
}
}
@include small-break {
padding: calc(var(--base) / 2) calc(var(--base) / 2) 0;
}
}

View File

@@ -8,7 +8,6 @@
z-index: 1;
display: flex;
align-items: center;
--controlsHeight: calc(var(--base) * 3);
&::after {
content: '';
@@ -32,7 +31,7 @@
}
&__content {
height: var(--controlsHeight);
height: var(--doc-controls-height);
display: flex;
align-items: center;
flex-grow: 1;
@@ -174,8 +173,7 @@
// The timestamps and meta can scroll past
// The same container needs to the sticky, though
// So we use a static height with a negative top
--controlsHeight: calc(var(--base) * 1.5);
top: calc(var(--controlsHeight) * -1);
top: calc(var(--doc-controls-height) * -1);
padding-right: 0;
padding-left: 0;

View File

@@ -32,6 +32,7 @@ export const DocumentControls: React.FC<{
id?: string
isEditing?: boolean
permissions?: CollectionPermission | GlobalPermission
isAccountView?: boolean
}> = (props) => {
const {
id,
@@ -42,6 +43,7 @@ export const DocumentControls: React.FC<{
hasSavePermission,
isEditing,
permissions,
isAccountView,
} = props
const { publishedDoc } = useDocumentInfo()
@@ -76,7 +78,7 @@ export const DocumentControls: React.FC<{
<div className={`${baseClass}__wrapper`}>
<div className={`${baseClass}__content`}>
<ul className={`${baseClass}__meta`}>
{collection && !isEditing && (
{collection && !isEditing && !isAccountView && (
<li className={`${baseClass}__list-item`}>
<p className={`${baseClass}__value`}>
{t('creatingNewLabel', {
@@ -113,7 +115,7 @@ export const DocumentControls: React.FC<{
)}
</Fragment>
)}
{collection?.timestamps && isEditing && (
{collection?.timestamps && (isEditing || isAccountView) && (
<Fragment>
<li
className={[`${baseClass}__list-item`, `${baseClass}__value-wrap`]

View File

@@ -4,18 +4,17 @@
&__header {
width: 100%;
margin-top: base(2.5);
margin-bottom: base(1);
@include mid-break {
margin-top: base(1.5);
}
display: flex;
flex-direction: column;
gap: base(0.5);
align-items: flex-start;
}
&__header-content {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: base(1);
width: 100%;
}
&__header-text {
@@ -62,4 +61,13 @@
}
}
}
@include mid-break {
&__header {
margin-top: base(1.5);
margin-bottom: base(0.5);
padding-left: var(--gutter-h);
padding-right: var(--gutter-h);
}
}
}

View File

@@ -7,6 +7,7 @@
list-style: none;
align-items: center;
margin: 0;
padding-left: 0;
}
&__tab {
@@ -21,7 +22,6 @@
&__tabs {
padding: 0;
margin-left: var(--gutter-h);
margin-bottom: calc(var(--base) / 2);
}
&__tab {

View File

@@ -7,6 +7,8 @@
display: flex;
align-items: center;
position: relative;
display: flex;
gap: calc(var(--base) / 2);
&::after {
content: '';
@@ -34,6 +36,7 @@
padding-bottom: calc(var(--base) / 1.5);
flex-direction: column;
gap: calc(var(--base) / 2);
padding-bottom: calc(var(--base) / 2);
&__title {
width: 100%;
@@ -44,6 +47,5 @@
@include small-break {
margin-top: 0;
padding-bottom: 0;
}
}

View File

@@ -1,5 +1,7 @@
@import '../../../scss/styles.scss';
$transTime: 200ms;
.drawer {
display: flex;
overflow: hidden;
@@ -15,18 +17,17 @@
bottom: 0;
left: 0;
opacity: 0;
transition: all 300ms ease-out;
transition: all $transTime linear;
}
&__content {
@include blur-bg();
opacity: 0;
transform: translateX(#{base(4)});
transform: translateX(calc(var(--base) * 4));
position: relative;
z-index: 2;
width: 100%;
transition: all 300ms ease-out;
width: calc(100% - var(--gutter-h));
overflow: hidden;
transition: all $transTime linear;
}
&__content-children {
@@ -37,21 +38,23 @@
}
&--is-open {
.drawer__content,
.drawer__blur-bg,
.drawer__close {
.drawer {
&__content,
&__blur-bg,
&__close {
opacity: 1;
}
.drawer__close {
transition: opacity 300ms ease-in-out;
transition-delay: 100ms;
&__close {
transition: opacity $transTime linear;
transition-delay: $transTime;
}
.drawer__content {
&__content {
transform: translateX(0);
}
}
}
&__close {
@extend %btn-reset;
@@ -59,12 +62,13 @@
z-index: 2;
flex-shrink: 0;
text-indent: -9999px;
background: rgba(0, 0, 0, 0.08);
cursor: pointer;
opacity: 0;
will-change: opacity;
transition: none;
transition-delay: 0ms;
flex-grow: 1;
background: transparent;
&:active,
&:focus {
@@ -112,10 +116,6 @@
&__header {
margin-top: base(1.5);
}
&__close {
width: base(1);
}
}
}

View File

@@ -85,12 +85,10 @@ export const Drawer: React.FC<Props> = ({
className={`${baseClass}__close`}
id={`close-drawer__${slug}`}
onClick={() => closeModal(slug)}
style={{
width: 'var(--gutter-h)',
}}
type="button"
/>
<div className={`${baseClass}__content`}>
<div className={`${baseClass}__blur-bg`} />
<Gutter className={`${baseClass}__content-children`} left={gutter} right={gutter}>
<EditDepthContext.Provider value={drawerDepth + 1}>
{header && header}

View File

@@ -1,8 +1,6 @@
@import '../../../scss/styles';
.list-controls {
margin-bottom: $baseline;
&__wrap {
display: flex;
align-items: center;
@@ -66,12 +64,15 @@
}
&__buttons-wrap {
margin-left: - base(0.25);
margin-right: - base(0.25);
width: calc(100% + #{base(0.5)});
[dir='ltr'] & {
margin-right: 0;
}
[dir='rtl'] & {
margin-left: 0;
}
.pill {
margin: 0 base(0.25);
padding: base(0.5) base(1);
svg {
@@ -85,10 +86,16 @@
width: 100%;
}
.column-selector,
.where-builder,
.sort-complex {
margin-top: calc(var(--base) / 2);
}
&__toggle-columns,
&__toggle-where,
&__toggle-sort {
flex-grow: 1;
flex: 1;
}
}
}

View File

@@ -12,6 +12,7 @@
&__header-wrap {
display: flex;
gap: base(1);
}
&__header-content {
@@ -22,8 +23,9 @@
button .pill {
pointer-events: none;
margin-top: base(0.25);
margin-right: base(0.5);
margin: 0;
top: 4px;
margin-left: base(0.5);
}
}
@@ -31,6 +33,10 @@
margin: 0;
}
&__header-close {
flex-shrink: 0;
}
&__toggler {
background: transparent;
border: 0;
@@ -90,5 +96,15 @@
.collection-list__header {
margin-bottom: base(0.5);
}
&__select-collection-wrap {
margin-top: calc(var(--base) / 2);
}
&__header-content {
button .pill {
top: 2px;
}
}
}
}

View File

@@ -4,12 +4,14 @@ $transTime: 200ms;
.main-menu {
display: flex;
overflow: hidden;
position: fixed;
height: 100vh;
&__blur-bg {
@include blur-bg();
position: absolute;
z-index: 1;
top: 0;
right: 0;
bottom: 0;
@@ -19,54 +21,35 @@ $transTime: 200ms;
}
&__content {
padding: base(1) 0 base(2);
position: relative;
opacity: 0;
transform: translateX(calc(var(--base) * -1));
transition: transform $transTime linear;
overflow: auto;
width: 50%;
}
&__content-children {
position: relative;
display: flex;
flex-direction: column;
gap: var(--base);
padding-left: calc(var(--base) * 3);
}
&__close {
@extend %btn-reset;
transform: translateX(calc(var(--base) * -2));
position: relative;
z-index: 2;
flex-shrink: 0;
text-indent: -9999px;
background: rgba(0, 0, 0, 0.2);
cursor: pointer;
opacity: 0;
will-change: opacity;
transition: none;
transition-delay: 0ms;
flex-grow: 1;
flex-shrink: 1;
&:active,
&:focus {
outline: 0;
}
width: 50%;
&::before {
position: absolute;
top: 0;
left: 0;
left: 100%;
height: 100%;
width: calc(var(--base) * 4);
content: ' ';
background: linear-gradient(to right, rgba(0, 0, 0, 0.25) 0%, rgba(0, 0, 0, 0) 100%);
pointer-events: none;
}
}
&__content-children {
position: relative;
z-index: 1;
overflow: auto;
height: 100%;
display: flex;
flex-direction: column;
gap: var(--base);
padding: var(--base) 0 calc(var(--base) * 3) calc(var(--base) * 3);
}
&--is-open {
.main-menu {
&__content,
@@ -76,16 +59,38 @@ $transTime: 200ms;
}
&__close {
transition: opacity $transTime ease-in-out;
transition: opacity $transTime linear;
transition-delay: $transTime;
}
&__content {
transform: translateX(0);
transition: all $transTime linear;
}
}
}
&__close {
@extend %btn-reset;
position: relative;
z-index: 2;
flex-shrink: 0;
text-indent: -9999px;
cursor: pointer;
opacity: 0;
will-change: opacity;
transition: none;
transition-delay: 0ms;
flex-grow: 1;
flex-shrink: 1;
background: rgba(0, 0, 0, 0.2);
&:active,
&:focus {
outline: 0;
}
}
&__link {
margin: 0;
@@ -104,10 +109,6 @@ $transTime: 200ms;
gap: calc(var(--base) / 2);
}
&.payload__modal-item--exitActive {
transition: none;
}
@include large-break {
&__content-children {
padding-left: var(--gutter-h);
@@ -115,19 +116,27 @@ $transTime: 200ms;
}
@include mid-break {
.main-menu {
&__close {
display: none;
}
&__content {
width: 100%;
padding-top: calc(var(--base) * 2);
&::after {
display: none;
}
}
&__content-children {
padding-top: calc(var(--base) * 0.5);
}
}
@include small-break {
&__content-children {
padding-top: calc(var(--base) * 2);
padding-bottom: calc(var(--base) * 2);
}
}
}

View File

@@ -85,6 +85,7 @@ export const MainMenu: React.FC = () => {
>
<div className={`${baseClass}__blur-bg`} />
<nav className={`${baseClass}__content`}>
<div className={`${baseClass}__blur-bg`} />
<Gutter className={`${baseClass}__content-children`}>
{Array.isArray(beforeNavLinks) &&
beforeNavLinks.map((Component, i) => <Component key={i} />)}

View File

@@ -96,10 +96,6 @@
&__content {
padding: 0 calc(var(--gutter-h) / 2);
}
&__nav-wrapper {
margin-left: calc(var(--base) / 1.125);
}
}
@include mid-break {

View File

@@ -32,6 +32,16 @@
outline-offset: var(--accessibility-outline-offset);
}
.icon {
flex-shrink: 0;
}
&__label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&--has-action {
cursor: pointer;
text-decoration: none;

View File

@@ -83,7 +83,7 @@ const StaticPill: React.FC<Props> = (props) => {
type={Element === 'button' ? 'button' : undefined}
>
{icon && alignIcon === 'left' && <React.Fragment>{icon}</React.Fragment>}
{children}
<span className={`${baseClass}__label`}>{children}</span>
{icon && alignIcon === 'right' && <React.Fragment>{icon}</React.Fragment>}
</Element>
)

View File

@@ -4,47 +4,42 @@
&__wrap {
display: flex;
align-items: center;
gap: var(--base);
}
&__inputs {
display: flex;
flex-grow: 1;
align-items: center;
gap: var(--base);
> div {
flex-basis: 100%;
}
}
[dir='rtl'] &__field,
&__operator {
margin-left: $baseline;
margin-right: 0;
}
&__field,
&__operator {
margin-right: $baseline;
}
&__actions {
flex-shrink: 0;
display: flex;
gap: calc(var(--base) / 2);
padding: calc(var(--base) / 2) 0;
}
.btn {
vertical-align: middle;
margin: 0 0 0 $baseline;
[dir='rtl'] & {
margin: 0 $baseline 0 0;
}
margin: 0;
}
@include mid-break {
&__wrap {
align-items: initial;
gap: calc(var(--base) / 2);
}
&__inputs {
display: block;
flex-direction: column;
gap: calc(var(--base) / 2);
align-items: stretch;
}
&__actions {
@@ -52,10 +47,5 @@
flex-direction: column;
justify-content: space-between;
}
&__field,
&__operator {
margin: 0 0 base(0.5);
}
}
}

View File

@@ -2,10 +2,20 @@
.where-builder {
background: var(--theme-elevation-50);
padding: base(0.5) $baseline $baseline;
padding: var(--base);
display: flex;
flex-direction: column;
gap: calc(var(--base) / 2);
&__label {
margin: base(0.5) 0;
.btn {
margin: 0;
align-self: flex-start;
}
&__no-filters {
display: flex;
flex-direction: column;
gap: calc(var(--base) / 2);
}
&__or-filters,
@@ -13,18 +23,18 @@
list-style: none;
margin: 0;
padding: 0;
}
display: flex;
flex-direction: column;
gap: calc(var(--base) / 2);
&__add-or,
&__add-first-filter {
&.btn {
margin-bottom: 0;
li {
display: flex;
flex-direction: column;
gap: calc(var(--base) / 2);
}
}
&__add-first-filter {
&.btn {
margin-top: 0;
}
@include small-break {
padding: calc(var(--base) / 2);
}
}

View File

@@ -2,7 +2,11 @@
.field-description {
display: flex;
padding-top: base(0.25);
padding-bottom: base(0.25);
color: var(--theme-elevation-400);
margin-top: calc(var(--base) / 4);
&--margin-bottom {
margin-top: 0;
margin-bottom: calc(var(--base) / 2);
}
}

View File

@@ -10,7 +10,7 @@ import { isComponent } from './types'
const baseClass = 'field-description'
const FieldDescription: React.FC<Props> = (props) => {
const { className, description, value } = props
const { className, description, value, marginPlacement } = props
const { i18n } = useTranslation()
@@ -21,7 +21,15 @@ const FieldDescription: React.FC<Props> = (props) => {
if (description) {
return (
<div className={[baseClass, className].filter(Boolean).join(' ')}>
<div
className={[
baseClass,
className,
marginPlacement && `${baseClass}--margin-${marginPlacement}`,
]
.filter(Boolean)
.join(' ')}
>
{typeof description === 'function'
? description({ value })
: getTranslation(description, i18n)}

View File

@@ -14,6 +14,7 @@ export type Props = {
className?: string
description?: Description
value?: unknown
marginPlacement?: 'top' | 'bottom'
}
export function isComponent(description: Description): description is DescriptionComponent {

View File

@@ -0,0 +1,55 @@
@import '../../../scss/styles.scss';
.render-fields {
--spacing-field: var(--base);
&--margins-small {
--spacing-field: var(--base);
}
&--margins-none {
--spacing-field: 0;
}
& > .field-type {
margin-bottom: var(--spacing-field);
&[type='hidden'] {
margin-bottom: 0;
}
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}
// at the top-level, add extra margins for the following field types
&:not(.render-fields--margins-small) {
& > .field-type {
&.group-field,
&.blocks-field,
&.array-field,
&.collapsible-field,
&.rich-text {
margin-top: calc(var(--spacing-field) * 2);
margin-bottom: calc(var(--spacing-field) * 2);
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}
}
}
@include small-break {
--spacing-field: calc(var(--base) / 2);
}
}

View File

@@ -9,6 +9,7 @@ import useIntersect from '../../../hooks/useIntersect'
import { useOperation } from '../../utilities/OperationProvider'
import RenderCustomComponent from '../../utilities/RenderCustomComponent'
import { filterFields } from './filterFields'
import './index.scss'
const baseClass = 'render-fields'
@@ -21,7 +22,7 @@ const intersectionObserverOptions = {
// This is so that we can conditionally render fields before reducing them, if desired
// See the sidebar in '../collections/Edit/Default/index.tsx' for an example
const RenderFields: React.FC<Props> = (props) => {
const { className, fieldTypes, forceRender } = props
const { className, fieldTypes, forceRender, margins } = props
const { i18n, t } = useTranslation('general')
const [hasRendered, setHasRendered] = useState(Boolean(forceRender))
@@ -38,8 +39,6 @@ const RenderFields: React.FC<Props> = (props) => {
}
}, [shouldRender, hasRendered])
const classes = [baseClass, className].filter(Boolean).join(' ')
let fieldsToRender = 'fields' in props ? props?.fields : null
if (!fieldsToRender && 'fieldSchema' in props) {
@@ -57,7 +56,17 @@ const RenderFields: React.FC<Props> = (props) => {
if (fieldsToRender) {
return (
<div className={classes} ref={intersectionRef}>
<div
className={[
baseClass,
className,
margins && `${baseClass}--margins-${margins}`,
margins === false && `${baseClass}--margins-none`,
]
.filter(Boolean)
.join(' ')}
ref={intersectionRef}
>
{hasRendered &&
fieldsToRender.map((reducedField, fieldIndex) => {
const {

View File

@@ -6,6 +6,7 @@ import type { ReducedField } from './filterFields'
export type Props = {
className?: string
fieldTypes: FieldTypes
margins?: 'small' | false
forceRender?: boolean
} & (
| {

View File

@@ -129,6 +129,7 @@ export const ArrayRow: React.FC<ArrayRowProps> = ({
indexPath={indexPath}
permissions={permissions?.fields}
readOnly={readOnly}
margins="small"
/>
</Collapsible>
</div>

View File

@@ -1,10 +1,20 @@
@import '../../../../scss/styles.scss';
.array-field {
margin: 0 0 base(2);
display: flex;
flex-direction: column;
gap: calc(var(--base) / 2);
&:last-child {
margin-bottom: base(1.5);
&__header {
display: flex;
flex-direction: column;
gap: calc(var(--base) / 2);
&__header-content {
display: flex;
flex-direction: column;
gap: calc(var(--base) / 4);
}
}
&--has-no-error {
@@ -19,14 +29,6 @@
gap: base(0.5);
}
&__header {
h3 {
margin-bottom: 0;
}
margin-bottom: base(1);
}
&__header-wrap {
display: flex;
align-items: flex-end;
@@ -52,10 +54,6 @@
}
}
&__row {
margin-bottom: base(0.5);
}
&__error-wrap {
position: relative;
}
@@ -67,10 +65,18 @@
pointer-events: none;
}
&__add-button-wrap {
margin-top: base(1);
&__draggable-rows {
display: flex;
flex-direction: column;
gap: calc(var(--base) / 2);
}
.btn {
&__title {
margin-bottom: 0;
}
&__add-row {
align-self: flex-start;
color: var(--theme-elevation-400);
margin: 0;
@@ -78,7 +84,6 @@
color: var(--theme-elevation-800);
}
}
}
}
html[data-theme='light'] {

View File

@@ -22,6 +22,7 @@ import useField from '../../useField'
import withCondition from '../../withCondition'
import { ArrayRow } from './ArrayRow'
import './index.scss'
import { fieldBaseClass } from '../shared'
const baseClass = 'array-field'
@@ -152,33 +153,41 @@ const ArrayFieldType: React.FC<Props> = (props) => {
)
const hasMaxRows = maxRows && rows.length >= maxRows
const fieldErrorCount =
rows.reduce((total, row) => total + (row?.childErrorPaths?.size || 0), 0) + (valid ? 0 : 1)
const fieldHasErrors = submitted && fieldErrorCount > 0
const classes = [
'field-type',
const showRequired = readOnly && rows.length === 0
const showMinRows = rows.length < minRows || (required && rows.length === 0)
return (
<div
className={[
fieldBaseClass,
baseClass,
className,
fieldHasErrors ? `${baseClass}--has-error` : `${baseClass}--has-no-error`,
]
.filter(Boolean)
.join(' ')
return (
<div className={classes} id={`field-${path.replace(/\./g, '__')}`}>
.join(' ')}
id={`field-${path.replace(/\./g, '__')}`}
>
{showError && (
<div className={`${baseClass}__error-wrap`}>
<Error message={errorMessage} showError={showError} />
</div>
)}
<header className={`${baseClass}__header`}>
<div className={`${baseClass}__header-wrap`}>
<div className={`${baseClass}__header-content`}>
<h3>{getTranslation(label || name, i18n)}</h3>
<h3 className={`${baseClass}__title`}>{getTranslation(label || name, i18n)}</h3>
{fieldHasErrors && fieldErrorCount > 0 && (
<ErrorPill count={fieldErrorCount} withMessage />
)}
</div>
{rows.length > 0 && (
<ul className={`${baseClass}__header-actions`}>
<li>
<button
@@ -189,7 +198,6 @@ const ArrayFieldType: React.FC<Props> = (props) => {
{t('collapseAll')}
</button>
</li>
<li>
<button
className={`${baseClass}__header-action`}
@@ -200,6 +208,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
</button>
</li>
</ul>
)}
</div>
<FieldDescription
className={`field-description-${path.replace(/\./g, '__')}`}
@@ -209,14 +218,13 @@ const ArrayFieldType: React.FC<Props> = (props) => {
</header>
<NullifyLocaleField fieldValue={value} localized={localized} path={path} />
{(rows.length > 0 || (!valid && (showRequired || showMinRows))) && (
<DraggableSortable
className={`${baseClass}__draggable-rows`}
ids={rows.map((row) => row.id)}
onDragEnd={({ moveFromIndex, moveToIndex }) => moveRow(moveFromIndex, moveToIndex)}
>
{rows.length > 0 &&
rows.map((row, i) => (
{rows.map((row, i) => (
<DraggableSortableItem disabled={readOnly} id={row.id} key={row.id}>
{(draggableSortableItemProps) => (
<ArrayRow
@@ -242,16 +250,14 @@ const ArrayFieldType: React.FC<Props> = (props) => {
)}
</DraggableSortableItem>
))}
{!valid && (
<React.Fragment>
{readOnly && rows.length === 0 && (
{showRequired && (
<Banner>
{t('validation:fieldHasNo', { label: getTranslation(labels.plural, i18n) })}
</Banner>
)}
{(rows.length < minRows || (required && rows.length === 0)) && (
{showMinRows && (
<Banner type="error">
{t('validation:requiresAtLeast', {
count: minRows,
@@ -264,19 +270,18 @@ const ArrayFieldType: React.FC<Props> = (props) => {
</React.Fragment>
)}
</DraggableSortable>
)}
{!readOnly && !hasMaxRows && (
<div className={`${baseClass}__add-button-wrap`}>
<Button
buttonStyle="icon-label"
icon="plus"
iconPosition="left"
iconStyle="with-border"
onClick={() => addRow(value)}
className={`${baseClass}__add-row`}
>
{t('addLabel', { label: getTranslation(labels.singular, i18n) })}
</Button>
</div>
)}
</div>
)

View File

@@ -133,6 +133,7 @@ export const BlockRow: React.FC<BlockFieldProps> = ({
indexPath={indexPath}
permissions={permissions?.blocks?.[row.blockType]?.fields}
readOnly={readOnly}
margins="small"
/>
</Collapsible>
</div>

View File

@@ -1,14 +1,14 @@
@import '../../../../scss/styles.scss';
.blocks-field {
margin: 0 0 base(2);
display: flex;
flex-direction: column;
gap: calc(var(--base) / 2);
&__header {
h3 {
margin-bottom: 0;
margin: 0;
}
margin-bottom: base(1);
}
&__header-wrap {
@@ -75,19 +75,22 @@
line-height: unset;
}
&__row {
margin-bottom: base(0.5);
}
&__error-wrap {
position: relative;
}
&__rows {
display: flex;
flex-direction: column;
gap: calc(var(--base) / 2);
}
&__drawer-toggler {
background-color: transparent;
margin: 0;
padding: 0;
border: none;
align-self: flex-start;
.btn {
color: var(--theme-elevation-400);

View File

@@ -25,6 +25,7 @@ import withCondition from '../../withCondition'
import { BlockRow } from './BlockRow'
import { BlocksDrawer } from './BlocksDrawer'
import './index.scss'
import { fieldBaseClass } from '../shared'
const baseClass = 'blocks-field'
@@ -161,20 +162,26 @@ const BlocksField: React.FC<Props> = (props) => {
const fieldErrorCount = rows.reduce((total, row) => total + (row?.childErrorPaths?.size || 0), 0)
const fieldHasErrors = submitted && fieldErrorCount + (valid ? 0 : 1) > 0
const classes = [
'field-type',
const showMinRows = rows.length < minRows || (required && rows.length === 0)
const showRequired = readOnly && rows.length === 0
return (
<div
className={[
fieldBaseClass,
baseClass,
className,
fieldHasErrors ? `${baseClass}--has-error` : `${baseClass}--has-no-error`,
]
.filter(Boolean)
.join(' ')
return (
<div className={classes} id={`field-${path.replace(/\./g, '__')}`}>
.join(' ')}
id={`field-${path.replace(/\./g, '__')}`}
>
{showError && (
<div className={`${baseClass}__error-wrap`}>
<Error message={errorMessage} showError={showError} />
</div>
)}
<header className={`${baseClass}__header`}>
<div className={`${baseClass}__header-wrap`}>
<div className={`${baseClass}__heading-with-error`}>
@@ -184,6 +191,7 @@ const BlocksField: React.FC<Props> = (props) => {
<ErrorPill count={fieldErrorCount} withMessage />
)}
</div>
{rows.length > 0 && (
<ul className={`${baseClass}__header-actions`}>
<li>
<button
@@ -204,18 +212,18 @@ const BlocksField: React.FC<Props> = (props) => {
</button>
</li>
</ul>
)}
</div>
<FieldDescription description={description} value={value} />
</header>
<NullifyLocaleField fieldValue={value} localized={localized} path={path} />
{(rows.length > 0 || (!valid && (showRequired || showMinRows))) && (
<DraggableSortable
className={`${baseClass}__rows`}
ids={rows.map((row) => row.id)}
onDragEnd={({ moveFromIndex, moveToIndex }) => moveRow(moveFromIndex, moveToIndex)}
>
{rows.length > 0 &&
rows.map((row, i) => {
{rows.map((row, i) => {
const { blockType } = row
const blockToRender = blocks.find((block) => block.slug === blockType)
@@ -252,7 +260,7 @@ const BlocksField: React.FC<Props> = (props) => {
})}
{!editingDefaultLocale && (
<React.Fragment>
{(rows.length < minRows || (required && rows.length === 0)) && (
{showMinRows && (
<Banner type="error">
{t('validation:requiresAtLeast', {
count: minRows,
@@ -265,7 +273,7 @@ const BlocksField: React.FC<Props> = (props) => {
})}
</Banner>
)}
{rows.length === 0 && readOnly && (
{showRequired && (
<Banner>
{t('validation:fieldHasNo', { label: getTranslation(labels.plural, i18n) })}
</Banner>
@@ -273,6 +281,7 @@ const BlocksField: React.FC<Props> = (props) => {
</React.Fragment>
)}
</DraggableSortable>
)}
{!readOnly && !hasMaxRows && (
<Fragment>
<DrawerToggler className={`${baseClass}__drawer-toggler`} slug={drawerSlug}>

View File

@@ -5,7 +5,7 @@ import Line from '../../../icons/Line'
import Label from '../../Label'
import './index.scss'
const baseClass = 'custom-checkbox'
const baseClass = 'checkbox-input'
type CheckboxInputProps = {
'aria-label'?: string
@@ -18,6 +18,7 @@ type CheckboxInputProps = {
partialChecked?: boolean
readOnly?: boolean
required?: boolean
className?: string
}
export const CheckboxInput: React.FC<CheckboxInputProps> = (props) => {
@@ -32,12 +33,14 @@ export const CheckboxInput: React.FC<CheckboxInputProps> = (props) => {
partialChecked,
readOnly,
required,
className,
} = props
return (
<div
className={[
baseClass,
className,
(checked || partialChecked) && `${baseClass}--checked`,
readOnly && `${baseClass}--read-only`,
]

View File

@@ -16,13 +16,14 @@
}
}
.custom-checkbox {
.checkbox-input {
display: inline-flex;
label {
padding-bottom: 0;
padding-left: base(0.5);
}
[dir='rtl'] &__input {
margin-right: 0;
margin-left: base(0.5);
@@ -65,7 +66,7 @@
&:active,
&:focus-within,
&:focus {
.custom-checkbox__input,
.checkbox-input__input,
& input[type='checkbox'] {
@include inputShadowActive;
@@ -76,7 +77,7 @@
}
&:hover {
.custom-checkbox__input,
.checkbox-input__input,
& input[type='checkbox'] {
border-color: var(--theme-elevation-250);
}
@@ -94,7 +95,7 @@
}
&--checked {
.custom-checkbox__icon {
.checkbox-input__icon {
svg {
opacity: 1;
}
@@ -102,7 +103,7 @@
}
&--read-only {
.custom-checkbox__input {
.checkbox-input__input {
background-color: var(--theme-elevation-100);
}
@@ -115,7 +116,7 @@
html[data-theme='light'] {
.checkbox {
&.error {
.custom-checkbox__input {
.checkbox-input__input {
@include lightInputError;
}
}
@@ -125,7 +126,7 @@ html[data-theme='light'] {
html[data-theme='dark'] {
.checkbox {
&.error {
.custom-checkbox__input {
.checkbox-input__input {
@include darkInputError;
}
}

View File

@@ -11,6 +11,7 @@ import useField from '../../useField'
import withCondition from '../../withCondition'
import { CheckboxInput } from './Input'
import './index.scss'
import { fieldBaseClass } from '../shared'
const baseClass = 'checkbox'
@@ -56,7 +57,7 @@ const Checkbox: React.FC<Props> = (props) => {
return (
<div
className={[
'field-type',
fieldBaseClass,
baseClass,
showError && 'error',
className,

View File

@@ -2,7 +2,6 @@
.code-field {
position: relative;
margin-bottom: $baseline;
&.error {
textarea {

View File

@@ -10,6 +10,7 @@ import Label from '../../Label'
import useField from '../../useField'
import withCondition from '../../withCondition'
import './index.scss'
import { fieldBaseClass } from '../shared'
const prismToMonacoLanguageMap = {
js: 'javascript',
@@ -52,19 +53,17 @@ const Code: React.FC<Props> = (props) => {
validate: memoizedValidate,
})
const classes = [
return (
<div
className={[
fieldBaseClass,
baseClass,
'field-type',
className,
showError && 'error',
readOnly && 'read-only',
]
.filter(Boolean)
.join(' ')
return (
<div
className={classes}
.join(' ')}
style={{
...style,
width,

View File

@@ -1,8 +1,6 @@
@import '../../../../scss/styles.scss';
.collapsible-field {
margin: 0 0 base(2);
&__row-label-wrap {
pointer-events: none;
display: flex;

View File

@@ -15,6 +15,7 @@ import { RowLabel } from '../../RowLabel'
import { WatchChildErrors } from '../../WatchChildErrors'
import withCondition from '../../withCondition'
import './index.scss'
import { fieldBaseClass } from '../shared'
const baseClass = 'collapsible-field'
@@ -86,20 +87,21 @@ const CollapsibleField: React.FC<Props> = (props) => {
const fieldHasErrors = submitted && errorCount > 0
const classes = [
'field-type',
return (
<div
id={`field-${fieldPreferencesKey}${path ? `-${path.replace(/\./g, '__')}` : ''}`}
className={[
fieldBaseClass,
baseClass,
className,
fieldHasErrors ? `${baseClass}--has-error` : `${baseClass}--has-no-error`,
]
.filter(Boolean)
.join(' ')
return (
<div id={`field-${fieldPreferencesKey}${path ? `-${path.replace(/\./g, '__')}` : ''}`}>
.join(' ')}
>
<WatchChildErrors fieldSchema={fields} path={path} setErrorCount={setErrorCount} />
<Collapsible
className={classes}
className={`${baseClass}__collapsible`}
collapsibleStyle={errorCount > 0 ? 'error' : 'default'}
header={
<div className={`${baseClass}__row-label-wrap`}>
@@ -118,6 +120,7 @@ const CollapsibleField: React.FC<Props> = (props) => {
fieldTypes={fieldTypes}
forceRender
indexPath={indexPath}
margins="small"
permissions={permissions}
readOnly={readOnly}
/>

View File

@@ -2,7 +2,6 @@
.field-type.confirm-password {
position: relative;
margin-bottom: $baseline;
input {
@include formInput;

View File

@@ -9,6 +9,7 @@ import { useFormFields } from '../../Form/context'
import Label from '../../Label'
import useField from '../../useField'
import './index.scss'
import { fieldBaseClass } from '../shared'
const ConfirmPassword: React.FC<Props> = (props) => {
const { disabled } = props
@@ -37,10 +38,12 @@ const ConfirmPassword: React.FC<Props> = (props) => {
validate,
})
const classes = ['field-type', 'confirm-password', showError && 'error'].filter(Boolean).join(' ')
return (
<div className={classes}>
<div
className={[fieldBaseClass, 'confirm-password', showError && 'error']
.filter(Boolean)
.join(' ')}
>
<Error message={errorMessage} showError={showError} />
<Label
htmlFor="field-confirm-password"

View File

@@ -1,8 +1,6 @@
@import '../../../../scss/styles';
.date-time-field {
margin-bottom: $baseline;
&__error-wrap {
position: relative;
}

View File

@@ -12,6 +12,7 @@ import Label from '../../Label'
import useField from '../../useField'
import withCondition from '../../withCondition'
import './index.scss'
import { fieldBaseClass } from '../shared'
const baseClass = 'date-time-field'
@@ -42,19 +43,17 @@ const DateTime: React.FC<Props> = (props) => {
validate: memoizedValidate,
})
const classes = [
'field-type',
return (
<div
className={[
fieldBaseClass,
baseClass,
className,
showError && `${baseClass}--has-error`,
readOnly && 'read-only',
]
.filter(Boolean)
.join(' ')
return (
<div
className={classes}
.join(' ')}
style={{
...style,
width,

View File

@@ -1,7 +1,6 @@
@import '../../../../scss/styles.scss';
.field-type.email {
margin-bottom: $baseline;
position: relative;
input {

View File

@@ -11,6 +11,7 @@ import Label from '../../Label'
import useField from '../../useField'
import withCondition from '../../withCondition'
import './index.scss'
import { fieldBaseClass } from '../shared'
const Email: React.FC<Props> = (props) => {
const {
@@ -50,13 +51,11 @@ const Email: React.FC<Props> = (props) => {
const { errorMessage, setValue, showError, value } = fieldType
const classes = ['field-type', 'email', className, showError && 'error', readOnly && 'read-only']
.filter(Boolean)
.join(' ')
return (
<div
className={classes}
className={[fieldBaseClass, 'email', className, showError && 'error', readOnly && 'read-only']
.filter(Boolean)
.join(' ')}
style={{
...style,
width,

View File

@@ -1,14 +1,24 @@
@import '../../../../scss/styles.scss';
.group-field {
margin: base(2) calc(var(--gutter-h) * -1);
padding: base(2) var(--gutter-h);
border-top: 1px solid var(--theme-elevation-100);
margin-left: calc(var(--gutter-h) * -1);
margin-right: calc(var(--gutter-h) * -1);
border-bottom: 1px solid var(--theme-elevation-100);
border-top: 1px solid var(--theme-elevation-100);
&--top-level {
padding: base(2) var(--gutter-h);
&:first-child {
padding-top: 0;
border-top: 0;
}
}
&--within-collapsible {
margin: $baseline calc(#{$baseline} * -1);
padding: $baseline;
margin-left: calc(var(--base) * -1);
margin-right: calc(var(--base) * -1);
padding: var(--base);
&:first-child {
border-top: 0;
@@ -54,10 +64,16 @@
}
&__header {
margin-bottom: base(0.75);
margin-bottom: calc(var(--base) / 2);
display: flex;
align-items: center;
gap: base(0.5);
> header {
display: flex;
flex-direction: column;
gap: calc(var(--base) / 4);
}
}
&__title {
@@ -65,8 +81,18 @@
}
@include small-break {
padding-top: $baseline;
padding-bottom: $baseline;
&--top-level {
padding: var(--base) var(--gutter-h);
&:first-child {
padding-top: 0;
border-top: 0;
}
}
&__header {
margin-bottom: calc(var(--base) / 2);
}
&--within-collapsible {
margin-left: calc(var(--gutter-h) * -1);
@@ -84,12 +110,8 @@
}
.group-field + .group-field {
margin-top: base(-2);
border-top: 0;
}
.group-field--within-collapsible + .group-field--within-collapsible {
margin-top: base(-1);
padding-top: 0;
}
.group-field--within-row + .group-field--within-row {

View File

@@ -16,6 +16,7 @@ import { useRow } from '../Row/provider'
import { useTabs } from '../Tabs/provider'
import './index.scss'
import { GroupProvider, useGroup } from './provider'
import { fieldBaseClass } from '../shared'
const baseClass = 'group-field'
@@ -41,12 +42,14 @@ const Group: React.FC<Props> = (props) => {
const groupHasErrors = submitted && errorCount > 0
const path = pathFromProps || name
const isTopLevel = !(isWithinCollapsible || isWithinGroup || isWithinRow)
return (
<div
className={[
'field-type',
fieldBaseClass,
baseClass,
isTopLevel && `${baseClass}--top-level`,
isWithinCollapsible && `${baseClass}--within-collapsible`,
isWithinGroup && `${baseClass}--within-group`,
isWithinRow && `${baseClass}--within-row`,
@@ -86,6 +89,7 @@ const Group: React.FC<Props> = (props) => {
}))}
fieldTypes={fieldTypes}
indexPath={indexPath}
margins="small"
permissions={permissions?.fields}
readOnly={readOnly}
/>

View File

@@ -2,7 +2,6 @@
.json-field {
position: relative;
margin-bottom: $baseline;
}
html[data-theme='light'] {

View File

@@ -10,6 +10,7 @@ import Label from '../../Label'
import useField from '../../useField'
import withCondition from '../../withCondition'
import './index.scss'
import { fieldBaseClass } from '../shared'
const baseClass = 'json-field'
@@ -59,19 +60,17 @@ const JSONField: React.FC<Props> = (props) => {
setStringValue(JSON.stringify(initialValue, null, 2))
}, [initialValue])
const classes = [
return (
<div
className={[
fieldBaseClass,
baseClass,
'field-type',
className,
showError && 'error',
readOnly && 'read-only',
]
.filter(Boolean)
.join(' ')
return (
<div
className={classes}
.join(' ')}
style={{
...style,
width,

View File

@@ -2,7 +2,6 @@
.field-type.number {
position: relative;
margin-bottom: $baseline;
&:not(.has-many) {
input {

View File

@@ -14,6 +14,7 @@ import Label from '../../Label'
import useField from '../../useField'
import withCondition from '../../withCondition'
import './index.scss'
import { fieldBaseClass } from '../shared'
const NumberField: React.FC<Props> = (props) => {
const {
@@ -60,17 +61,6 @@ const NumberField: React.FC<Props> = (props) => {
[setValue],
)
const classes = [
'field-type',
'number',
className,
showError && 'error',
readOnly && 'read-only',
hasMany && 'has-many',
]
.filter(Boolean)
.join(' ')
const [valueToRender, setValueToRender] = useState<
{ id: string; label: string; value: { value: number } }[]
>([]) // Only for hasMany
@@ -113,7 +103,16 @@ const NumberField: React.FC<Props> = (props) => {
return (
<div
className={classes}
className={[
fieldBaseClass,
'number',
className,
showError && 'error',
readOnly && 'read-only',
hasMany && 'has-many',
]
.filter(Boolean)
.join(' ')}
style={{
...style,
width,

View File

@@ -2,7 +2,6 @@
.field-type.password {
position: relative;
margin-bottom: $baseline;
input {
@include formInput;

View File

@@ -8,6 +8,7 @@ import Label from '../../Label'
import useField from '../../useField'
import withCondition from '../../withCondition'
import './index.scss'
import { fieldBaseClass } from '../shared'
const Password: React.FC<Props> = (props) => {
const {
@@ -38,13 +39,11 @@ const Password: React.FC<Props> = (props) => {
validate: memoizedValidate,
})
const classes = ['field-type', 'password', className, showError && 'error']
.filter(Boolean)
.join(' ')
return (
<div
className={classes}
className={[fieldBaseClass, 'password', className, showError && 'error']
.filter(Boolean)
.join(' ')}
style={{
...style,
width,

View File

@@ -2,11 +2,11 @@
.point {
position: relative;
margin-bottom: $baseline;
&__wrap {
display: flex;
width: calc(100% + #{base(1)});
margin: 0;
margin-left: base(-0.5);
margin-right: base(-0.5);
list-style: none;

View File

@@ -11,6 +11,7 @@ import Label from '../../Label'
import useField from '../../useField'
import withCondition from '../../withCondition'
import './index.scss'
import { fieldBaseClass } from '../shared'
const baseClass = 'point'
@@ -59,19 +60,17 @@ const PointField: React.FC<Props> = (props) => {
[setValue, value],
)
const classes = [
'field-type',
return (
<div
className={[
fieldBaseClass,
baseClass,
className,
showError && 'error',
readOnly && 'read-only',
]
.filter(Boolean)
.join(' ')
return (
<div
className={classes}
.join(' ')}
style={{
...style,
width,

View File

@@ -10,6 +10,7 @@ import FieldDescription from '../../FieldDescription'
import Label from '../../Label'
import RadioInput from './RadioInput'
import './index.scss'
import { fieldBaseClass } from '../shared'
const baseClass = 'radio-group'
@@ -50,8 +51,10 @@ const RadioGroupInput: React.FC<RadioGroupInputProps> = (props) => {
const path = pathFromProps || name
const classes = [
'field-type',
return (
<div
className={[
fieldBaseClass,
baseClass,
className,
`${baseClass}--layout-${layout}`,
@@ -59,11 +62,7 @@ const RadioGroupInput: React.FC<RadioGroupInputProps> = (props) => {
readOnly && `${baseClass}--read-only`,
]
.filter(Boolean)
.join(' ')
return (
<div
className={classes}
.join(' ')}
style={{
...style,
width,

View File

@@ -1,8 +1,6 @@
@import '../../../../scss/styles.scss';
.radio-group {
margin-bottom: $baseline;
&__error-wrap {
position: relative;
}

View File

@@ -2,7 +2,6 @@
.field-type.relationship {
position: relative;
margin-bottom: $baseline;
}
.relationship {

View File

@@ -28,6 +28,7 @@ import './index.scss'
import optionsReducer from './optionsReducer'
import { MultiValueLabel } from './select-components/MultiValueLabel'
import { SingleValue } from './select-components/SingleValue'
import { fieldBaseClass } from '../shared'
const maxResultsPerRequest = 10
@@ -389,8 +390,13 @@ const Relationship: React.FC<Props> = (props) => {
return r.test(string.slice(-breakApartThreshold))
}, [])
const classes = [
'field-type',
const valueToRender = findOptionsByValue({ options, value })
if (!Array.isArray(valueToRender) && valueToRender?.value === 'null') valueToRender.value = null
return (
<div
className={[
fieldBaseClass,
baseClass,
className,
showError && 'error',
@@ -398,14 +404,7 @@ const Relationship: React.FC<Props> = (props) => {
readOnly && `${baseClass}--read-only`,
]
.filter(Boolean)
.join(' ')
const valueToRender = findOptionsByValue({ options, value })
if (!Array.isArray(valueToRender) && valueToRender?.value === 'null') valueToRender.value = null
return (
<div
className={classes}
.join(' ')}
id={`field-${pathOrName.replace(/\./g, '__')}`}
style={{
...style,

View File

@@ -3,29 +3,18 @@
.field-type.row {
display: flex;
flex-wrap: wrap;
margin-left: - base(0.5);
margin-right: - base(0.5);
width: calc(100% + #{$baseline});
margin-bottom: 0;
width: 100%;
gap: var(--base);
> * {
padding-left: base(0.5);
padding-right: base(0.5);
flex-grow: 1;
}
@include mid-break {
display: block;
margin-left: 0;
margin-right: 0;
width: 100%;
flex-direction: column;
> * {
margin-left: 0;
margin-right: 0;
width: 100% !important;
padding-left: 0;
padding-right: 0;
}
}
}

View File

@@ -7,6 +7,7 @@ import RenderFields from '../../RenderFields'
import withCondition from '../../withCondition'
import './index.scss'
import { RowProvider } from './provider'
import { fieldBaseClass } from '../shared'
const Row: React.FC<Props> = (props) => {
const {
@@ -18,18 +19,17 @@ const Row: React.FC<Props> = (props) => {
permissions,
} = props
const classes = ['field-type', 'row', className].filter(Boolean).join(' ')
return (
<RowProvider>
<RenderFields
className={classes}
className={[fieldBaseClass, 'row', className].filter(Boolean).join(' ')}
fieldSchema={fields.map((field) => ({
...field,
path: createNestedFieldPath(path, field),
}))}
fieldTypes={fieldTypes}
indexPath={indexPath}
margins={false}
permissions={permissions}
readOnly={readOnly}
/>

View File

@@ -11,6 +11,7 @@ import Error from '../../Error'
import FieldDescription from '../../FieldDescription'
import Label from '../../Label'
import './index.scss'
import { fieldBaseClass } from '../shared'
export type SelectInputProps = Omit<SelectField, 'options' | 'type' | 'value'> & {
className?: string
@@ -52,10 +53,6 @@ const SelectInput: React.FC<SelectInputProps> = (props) => {
const { i18n } = useTranslation()
const classes = ['field-type', 'select', className, showError && 'error', readOnly && 'read-only']
.filter(Boolean)
.join(' ')
let valueToRender
if (hasMany && Array.isArray(value)) {
@@ -76,7 +73,15 @@ const SelectInput: React.FC<SelectInputProps> = (props) => {
return (
<div
className={classes}
className={[
fieldBaseClass,
'select',
className,
showError && 'error',
readOnly && 'read-only',
]
.filter(Boolean)
.join(' ')}
id={`field-${path.replace(/\./g, '__')}`}
style={{
...style,

View File

@@ -2,7 +2,6 @@
.field-type.select {
position: relative;
margin-bottom: $baseline;
}
html[data-theme='light'] {

View File

@@ -4,7 +4,6 @@
margin-top: base(2);
margin-left: calc(var(--gutter-h) * -1);
margin-right: calc(var(--gutter-h) * -1);
margin-bottom: base(2);
&__content-wrap {
padding-left: var(--gutter-h);
@@ -53,7 +52,7 @@
@extend %btn-reset;
@extend %h4;
display: flex;
padding-bottom: base(0.75);
padding-bottom: base(1);
margin: 0 $baseline 0 0;
cursor: pointer;
opacity: 0.5;
@@ -96,7 +95,7 @@
}
&__description {
margin-bottom: $baseline;
margin-bottom: calc(var(--base) / 2);
}
@include small-break {
@@ -107,6 +106,7 @@
&__tab-button {
margin: 0 base(0.75) 0 0;
padding-bottom: base(0.5);
&:last-child {
margin: 0;

View File

@@ -20,6 +20,7 @@ import { WatchChildErrors } from '../../WatchChildErrors'
import withCondition from '../../withCondition'
import './index.scss'
import { TabsProvider } from './provider'
import { fieldBaseClass } from '../shared'
const baseClass = 'tabs-field'
@@ -132,7 +133,12 @@ const TabsField: React.FC<Props> = (props) => {
return (
<div
className={[className, baseClass, isWithinCollapsible && `${baseClass}--within-collapsible`]
className={[
fieldBaseClass,
className,
baseClass,
isWithinCollapsible && `${baseClass}--within-collapsible`,
]
.filter(Boolean)
.join(' ')}
>
@@ -167,6 +173,7 @@ const TabsField: React.FC<Props> = (props) => {
<FieldDescription
className={`${baseClass}__description`}
description={activeTabConfig.description}
marginPlacement="bottom"
/>
<RenderFields
fieldSchema={activeTabConfig.fields.map((field) => {
@@ -184,6 +191,7 @@ const TabsField: React.FC<Props> = (props) => {
forceRender
indexPath={indexPath}
key={String(activeTabConfig.label)}
margins="small"
permissions={
tabHasName(activeTabConfig)
? permissions[activeTabConfig.name].fields

View File

@@ -11,6 +11,7 @@ import Error from '../../Error'
import FieldDescription from '../../FieldDescription'
import Label from '../../Label'
import './index.scss'
import { fieldBaseClass } from '../shared'
export type TextInputProps = Omit<TextField, 'type'> & {
className?: string
@@ -52,13 +53,11 @@ const TextInput: React.FC<TextInputProps> = (props) => {
const { i18n } = useTranslation()
const classes = ['field-type', 'text', className, showError && 'error', readOnly && 'read-only']
.filter(Boolean)
.join(' ')
return (
<div
className={classes}
className={[fieldBaseClass, 'text', className, showError && 'error', readOnly && 'read-only']
.filter(Boolean)
.join(' ')}
style={{
...style,
width,

View File

@@ -2,7 +2,6 @@
.field-type.text {
position: relative;
margin-bottom: $baseline;
input {
@include formInput;

View File

@@ -11,6 +11,7 @@ import Error from '../../Error'
import FieldDescription from '../../FieldDescription'
import Label from '../../Label'
import './index.scss'
import { fieldBaseClass } from '../shared'
export type TextAreaInputProps = Omit<TextareaField, 'type'> & {
className?: string
@@ -50,19 +51,17 @@ const TextareaInput: React.FC<TextAreaInputProps> = (props) => {
const { i18n } = useTranslation()
const classes = [
'field-type',
return (
<div
className={[
fieldBaseClass,
'textarea',
className,
showError && 'error',
readOnly && 'read-only',
]
.filter(Boolean)
.join(' ')
return (
<div
className={classes}
.join(' ')}
style={{
...style,
width,

View File

@@ -2,7 +2,6 @@
.field-type.textarea {
position: relative;
margin-bottom: $baseline;
display: flex;
flex-direction: column;

View File

@@ -19,6 +19,7 @@ import Error from '../../Error'
import FieldDescription from '../../FieldDescription'
import Label from '../../Label'
import './index.scss'
import { fieldBaseClass } from '../shared'
const baseClass = 'upload'
@@ -79,16 +80,6 @@ const UploadInput: React.FC<UploadInputProps> = (props) => {
filterOptions: filterOptionsResult,
})
const classes = [
'field-type',
baseClass,
className,
showError && 'error',
readOnly && 'read-only',
]
.filter(Boolean)
.join(' ')
useEffect(() => {
if (typeof value === 'string' && value !== '') {
const fetchFile = async () => {
@@ -135,7 +126,15 @@ const UploadInput: React.FC<UploadInputProps> = (props) => {
return (
<div
className={classes}
className={[
fieldBaseClass,
baseClass,
className,
showError && 'error',
readOnly && 'read-only',
]
.filter(Boolean)
.join(' ')}
style={{
...style,
width,

View File

@@ -2,7 +2,6 @@
.upload {
position: relative;
margin-bottom: $baseline;
&__wrap {
background: var(--theme-elevation-50);

View File

@@ -1,5 +1,7 @@
import type { Locale, SanitizedLocalizationConfig } from '../../../../config/types'
export const fieldBaseClass = 'field-type'
/**
* Determines whether a field should be displayed as right-to-left (RTL) based on its configuration, payload's localization configuration and the adming user's currently enabled locale.

View File

@@ -19,9 +19,5 @@
[dir='rtl'] & {
margin-right: 0;
}
&__wrap {
padding: 0 0 $baseline;
}
}
}

View File

@@ -77,6 +77,7 @@ const DefaultAccount: React.FC<EditViewProps> = (props) => {
data={data}
hasSavePermission={hasSavePermission}
permissions={permissions}
isAccountView
/>
<div className={`${baseClass}__main`}>
<Meta
@@ -95,6 +96,7 @@ const DefaultAccount: React.FC<EditViewProps> = (props) => {
operation="update"
readOnly={!hasSavePermission}
useAPIKey={auth.useAPIKey}
className={`${baseClass}__auth`}
/>
<RenderFields
fieldSchema={fields}

View File

@@ -2,7 +2,7 @@
.account {
width: 100%;
padding-bottom: calc(var(--base) * 4);
padding-bottom: var(--spacing-view-bottom);
&__form {
height: 100%;
@@ -12,10 +12,13 @@
margin-top: calc(var(--base) * 3);
}
&__auth {
margin-bottom: var(--base);
}
&__header {
display: flex;
flex-direction: column;
gap: var(--base);
}
&__payload-settings {

View File

@@ -74,7 +74,7 @@ const Dashboard: React.FC<Props> = (props) => {
beforeDashboard.map((Component, i) => <Component key={i} />)}
{groups.map(({ entities, label }, groupIndex) => {
return (
<React.Fragment key={groupIndex}>
<div className={`${baseClass}__group`} key={groupIndex}>
<h2 className={`${baseClass}__label`}>{label}</h2>
<ul className={`${baseClass}__card-list`}>
{entities.map(({ entity, type }, entityIndex) => {
@@ -127,7 +127,7 @@ const Dashboard: React.FC<Props> = (props) => {
)
})}
</ul>
</React.Fragment>
</div>
)
})}
{Array.isArray(afterDashboard) &&

View File

@@ -2,59 +2,57 @@
.dashboard {
width: 100%;
--gap: var(--base);
--cols: 5;
&__wrap {
padding-bottom: base(2);
padding-bottom: var(--spacing-view-bottom);
display: flex;
flex-direction: column;
gap: var(--base);
}
&__group {
display: flex;
flex-direction: column;
gap: var(--gap);
}
&__label {
margin: 0;
}
&__card-list {
width: calc(100% + #{$baseline});
margin: 0 - base(0.5);
padding: 0;
margin: 0;
list-style: none;
display: flex;
gap: var(--gap);
flex-wrap: wrap;
li {
width: 20%;
margin-bottom: $baseline;
width: calc(100% / var(--cols) - var(--gap) / var(--cols) * (var(--cols) - 1));
}
.card {
height: 100%;
margin: 0 base(0.5);
}
}
@include large-break {
li {
width: 25%;
}
--cols: 4;
}
@include mid-break {
&__wrap {
padding-top: 0;
padding-bottom: 0;
}
li {
width: 33.33%;
}
--gap: var(--base);
--cols: 2;
}
@include small-break {
&__card-list {
width: calc(100% + #{base(0.5)});
margin: 0 - base(0.25);
}
--cols: 1;
li {
width: 50%;
}
.card {
margin: 0 base(0.25) base(0.5);
&__wrap {
gap: var(--base);
}
}
}

View File

@@ -1,51 +1,50 @@
@import '../../../../scss/styles.scss';
.global-edit {
.global-default-edit {
width: 100%;
&__wrapper {
display: flex;
&--has-sidebar {
.global-edit {
// &__main {
// width: calc(100% - #{base(15)});
// }
.global-default-edit {
&__main {
width: 66.66%;
}
&__edit {
[dir='ltr'] & {
top: 0;
right: 0;
border-right: 1px solid var(--theme-elevation-100);
padding-right: calc(var(--base) * 2);
}
[dir='rtl'] & {
top: 0;
left: 0;
border-left: 1px solid var(--theme-elevation-100);
}
padding-left: calc(var(--base) * 2);
}
}
}
}
&__main {
width: calc(100% - #{base(15)});
width: 100%;
display: flex;
flex-direction: column;
min-height: 100%;
}
&__edit {
padding-top: base(1);
padding-bottom: base(4);
padding-top: calc(var(--base) * 1.5);
padding-bottom: var(--spacing-view-bottom);
flex-grow: 1;
}
&__sidebar-wrap {
position: sticky;
top: 0;
width: base(15);
flex-grow: 1;
}
&__sidebar {
@@ -64,20 +63,47 @@
display: flex;
flex-direction: column;
gap: var(--base);
padding: var(--base);
.render-fields {
& > *:last-child {
margin-bottom: 0;
}
}
padding: calc(var(--base) * 1.5) var(--gutter-h) var(--spacing-view-bottom)
calc(var(--base) * 2);
}
&__label {
color: var(--theme-elevation-400);
}
@include large-break {
&--no-sidebar {
.global-default-edit {
&__main {
width: 100%;
}
}
}
}
@include mid-break {
display: block;
&--has-sidebar {
.global-default-edit {
&__main {
width: 100%;
}
&__edit {
[dir='ltr'] & {
border-right: 0;
padding-right: var(--gutter-h);
}
[dir='rtl'] & {
border-left: 0;
padding-left: var(--gutter-h);
}
}
}
}
&__main {
width: 100%;
min-height: initial;
@@ -88,17 +114,13 @@
width: 100%;
height: initial;
border-left: 0;
margin-top: calc(var(--base) / 2);
}
&__form {
display: block;
}
&__edit {
padding-top: var(--base);
padding-bottom: 0;
}
&__sidebar-fields {
padding-top: 0;
padding-left: var(--gutter-h);

View File

@@ -13,7 +13,7 @@ import Meta from '../../../utilities/Meta'
import './index.scss'
import { EditViewProps } from '../../types'
const baseClass = 'global-edit'
const baseClass = 'global-default-edit'
export const DefaultGlobalEdit: React.FC<EditViewProps> = (props) => {
if ('global' in props) {
@@ -47,7 +47,10 @@ export const DefaultGlobalEdit: React.FC<EditViewProps> = (props) => {
permissions={permissions}
/>
<div
className={[`${baseClass}__wrapper`, hasSidebar && `${baseClass}__wrapper--has-sidebar`]
className={[
baseClass,
hasSidebar ? `${baseClass}--has-sidebar` : `${baseClass}--no-sidebar`,
]
.filter(Boolean)
.join(' ')}
>

View File

@@ -7,4 +7,18 @@
width: 100%;
margin-bottom: base(2);
}
&__wrap {
& > *:first-child {
margin-top: 0;
}
& > *:last-child {
margin-bottom: 0;
}
.btn {
margin: 0;
}
}
}

View File

@@ -57,7 +57,6 @@ const Login: React.FC = () => {
<Link to={`${admin}${logoutRoute}`}>{t('logOut')}</Link>
</Trans>
</p>
<br />
<Button buttonStyle="secondary" el="link" to={admin}>
{t('general:backToDashboard')}
</Button>
@@ -74,6 +73,7 @@ const Login: React.FC = () => {
{!collection.auth.disableLocalStrategy && (
<Form
action={`${serverURL}${api}/${userSlug}/login`}
className={`${baseClass}__form`}
disableSuccessStatus
initialData={{
email: autoLogin && autoLogin.prefillOnly ? autoLogin.email : undefined,

View File

@@ -7,6 +7,16 @@
min-height: 100vh;
&__wrap {
margin: 0 auto base(1);
& > *:first-child {
margin-top: 0;
}
& > *:last-child {
margin-bottom: 0;
}
.btn {
margin: 0;
}
}
}

View File

@@ -34,7 +34,6 @@ const Logout: React.FC<{ inactivity?: boolean }> = (props) => {
<div className={`${baseClass}__wrap`}>
{inactivity && <h2>{t('loggedOutInactivity')}</h2>}
{!inactivity && <h2>{t('loggedOutSuccessfully')}</h2>}
<br />
<Button
buttonStyle="secondary"
el="anchor"

View File

@@ -11,6 +11,7 @@ import ReactSelect from '../../../elements/ReactSelect'
import { useConfig } from '../../../utilities/Config'
import { mostRecentVersionOption, publishedVersionOption } from '../shared'
import './index.scss'
import { fieldBaseClass } from '../../../forms/field-types/shared'
const baseClass = 'compare-version'
@@ -85,10 +86,6 @@ const CompareVersion: React.FC<Props> = (props) => {
[dateFormat, baseURL, parentID, versionID, t, i18n],
)
const classes = ['field-type', baseClass, errorLoading && 'error-loading']
.filter(Boolean)
.join(' ')
useEffect(() => {
getResults({ lastLoadedPage: 1 })
}, [getResults])
@@ -99,7 +96,11 @@ const CompareVersion: React.FC<Props> = (props) => {
}, [publishedDoc])
return (
<div className={classes}>
<div
className={[fieldBaseClass, baseClass, errorLoading && 'error-loading']
.filter(Boolean)
.join(' ')}
>
<div className={`${baseClass}__label`}>{t('compareVersion')}</div>
{!errorLoading && (
<ReactSelect

View File

@@ -1,12 +1,18 @@
@import '../../../../scss/styles.scss';
.render-field-diffs {
display: flex;
flex-direction: column;
gap: var(--base);
&__field {
margin-bottom: $baseline;
overflow-wrap: anywhere;
display: flex;
flex-direction: column;
gap: var(--base);
}
&__locale {
margin-bottom: base(0.5);
@include small-break {
gap: calc(var(--base) / 2);
}
}

View File

@@ -202,9 +202,12 @@ const VersionView: React.FC<Props> = ({ collection, global }) => {
<div className={baseClass}>
<Meta description={metaDesc} title={metaTitle} />
<Gutter className={`${baseClass}__wrap`}>
<div className={`${baseClass}__intro`}>
{t('versionCreatedOn', { version: t(doc?.autosave ? 'autosavedVersion' : 'version') })}
</div>
<div className={`${baseClass}__header-wrap`}>
<p className={`${baseClass}__created-at`}>
{t('versionCreatedOn', {
version: t(doc?.autosave ? 'autosavedVersion' : 'version'),
})}
</p>
<header className={`${baseClass}__header`}>
<h2>{formattedCreatedAt}</h2>
{canUpdate && (
@@ -218,6 +221,7 @@ const VersionView: React.FC<Props> = ({ collection, global }) => {
/>
)}
</header>
</div>
<div className={`${baseClass}__controls`}>
<CompareVersion
baseURL={compareBaseURL}

View File

@@ -2,18 +2,22 @@
.view-version {
width: 100%;
margin-bottom: calc(var(--base) * 2);
padding-bottom: var(--spacing-view-bottom);
&__wrap {
padding-top: var(--base);
padding-top: calc(var(--base) * 1.5);
display: flex;
flex-direction: column;
gap: var(--base);
}
&__intro {
margin-bottom: calc(var(--base) / 2);
&__header-wrap {
display: flex;
flex-direction: column;
gap: calc(var(--base) / 4);
}
&__header {
margin-bottom: var(--base);
display: flex;
align-items: center;
flex-wrap: wrap;
@@ -23,9 +27,13 @@
}
}
&__created-at {
margin: 0;
color: var(--theme-elevation-500);
}
&__controls {
display: flex;
margin-bottom: var(--base);
gap: var(--base);
> * {
@@ -44,10 +52,8 @@
}
&__controls {
display: block;
> * {
margin-bottom: base(0.5);
}
flex-direction: column;
gap: calc(var(--base) / 4);
}
&__restore {
@@ -58,6 +64,7 @@
@include small-break {
&__wrap {
padding-top: calc(var(--base) / 2);
gap: calc(var(--base) / 2);
}
}
}

View File

@@ -6,15 +6,16 @@
&__wrap {
padding-top: 0;
padding-bottom: base(4);
padding-bottom: var(--spacing-view-bottom);
margin-top: calc(var(--base) * 0.75);
}
&__header {
margin-bottom: var(--base);
}
&__no-version {
margin-top: calc(var(--base) * 2);
&__no-versions {
margin-top: calc(var(--base) * 1.5);
}
&__parent-doc {
@@ -27,6 +28,7 @@
[dir='ltr'] & {
margin-left: auto;
}
[dir='rtl'] & {
margin-right: auto;
}
@@ -54,6 +56,7 @@
margin-right: base(1);
margin-left: auto;
}
[dir='rtl'] & {
margin-left: base(1);
margin-right: auto;
@@ -63,6 +66,24 @@
@include mid-break {
&__wrap {
padding-top: 0;
margin-top: 0;
}
// on mobile, extend the table all the way to the viewport edges
// this is to visually indicate overflowing content
.table {
display: flex;
width: calc(100% + calc(var(--gutter-h) * 2));
max-width: unset;
left: calc(var(--gutter-h) * -1);
position: relative;
padding-left: var(--gutter-h);
&::after {
content: '';
height: 1px;
padding-right: var(--gutter-h);
}
}
&__page-controls {
@@ -73,6 +94,7 @@
[dir='ltr'] & {
margin-left: 0;
}
[dir='rtl'] & {
margin-right: 0;
}

View File

@@ -8,6 +8,7 @@ import GenerateConfirmation from '../../../../elements/GenerateConfirmation'
import { useFormFields } from '../../../../forms/Form/context'
import Label from '../../../../forms/Label'
import useField from '../../../../forms/useField'
import { fieldBaseClass } from '../../../../forms/field-types/shared'
const path = 'apiKey'
const baseClass = 'api-key'
@@ -67,11 +68,9 @@ const APIKey: React.FC<{ readOnly?: boolean }> = ({ readOnly }) => {
}
}, [highlightedField])
const classes = ['field-type', 'api-key', 'read-only'].filter(Boolean).join(' ')
return (
<React.Fragment>
<div className={classes}>
<div className={[fieldBaseClass, 'api-key', 'read-only'].filter(Boolean).join(' ')}>
<Label htmlFor={path} label={APIKeyLabel} />
<input
className={highlightedField ? 'highlight' : undefined}

View File

@@ -1,22 +1,41 @@
@import '../../../../../scss/styles.scss';
.auth-fields {
padding: base(2) base(2) base(1.5);
padding: base(2);
background: var(--theme-elevation-50);
display: flex;
flex-direction: column;
gap: var(--base);
&__controls {
display: flex;
align-items: center;
gap: calc(var(--base) / 2);
flex-wrap: wrap;
}
&__changing-password {
display: flex;
flex-direction: column;
gap: var(--base);
}
.btn {
margin-top: 0;
[dir='ltr'] & {
margin-right: base(0.5);
}
[dir='rtl'] & {
margin-left: base(0.5);
}
margin: 0;
}
&__api-key-label {
position: relative;
}
@include mid-break {
padding: var(--base);
gap: calc(var(--base) / 2);
&__changing-password {
gap: calc(var(--base) / 2);
}
}
}
.field-type.api-key {

View File

@@ -18,6 +18,7 @@ const baseClass = 'auth-fields'
const Auth: React.FC<Props> = (props) => {
const {
className,
collection,
collection: { slug },
email,
@@ -27,6 +28,7 @@ const Auth: React.FC<Props> = (props) => {
useAPIKey,
verify,
} = props
const [changingPassword, setChangingPassword] = useState(requirePassword)
const enableAPIKey = useFormFields(([fields]) => fields.enableAPIKey)
const dispatchFields = useFormFields((reducer) => reducer[1])
@@ -82,7 +84,7 @@ const Auth: React.FC<Props> = (props) => {
}
return (
<div className={baseClass}>
<div className={[baseClass, className].filter(Boolean).join(' ')}>
{!collection.auth.disableLocalStrategy && (
<React.Fragment>
<Email
@@ -113,6 +115,8 @@ const Auth: React.FC<Props> = (props) => {
)}
</div>
)}
{((!changingPassword && !requirePassword) || operation === 'update') && (
<div className={`${baseClass}__controls`}>
{!changingPassword && !requirePassword && (
<Button
buttonStyle="secondary"
@@ -134,6 +138,8 @@ const Auth: React.FC<Props> = (props) => {
{t('forceUnlock')}
</Button>
)}
</div>
)}
</React.Fragment>
)}
{useAPIKey && (

View File

@@ -2,6 +2,7 @@ import type { VerifyConfig } from '../../../../../../auth/types'
import type { SanitizedCollectionConfig } from '../../../../../../collections/config/types'
export type Props = {
className?: string
collection: SanitizedCollectionConfig
email: string
operation: 'create' | 'update'

View File

@@ -1,36 +1,35 @@
@import '../../../../../scss/styles.scss';
.collection-edit {
.collection-default-edit {
width: 100%;
&__wrapper {
display: flex;
&--has-sidebar {
.collection-edit {
// &__main {
// width: calc(100% - #{base(15)});
// }
.collection-default-edit {
&__main {
width: 66.66%;
}
&__edit {
[dir='ltr'] & {
top: 0;
right: 0;
border-right: 1px solid var(--theme-elevation-100);
padding-right: calc(var(--base) * 2);
}
[dir='rtl'] & {
top: 0;
left: 0;
border-left: 1px solid var(--theme-elevation-100);
}
padding-left: calc(var(--base) * 2);
}
}
}
}
&__main {
width: calc(100% - #{base(15)});
width: 100%;
display: flex;
flex-direction: column;
min-height: 100%;
@@ -38,14 +37,18 @@
&__edit {
padding-top: calc(var(--base) * 1.5);
padding-bottom: base(4);
padding-bottom: var(--spacing-view-bottom);
flex-grow: 1;
}
&__auth {
margin-bottom: var(--base);
}
&__sidebar-wrap {
position: sticky;
top: 0;
width: base(15);
flex-grow: 1;
}
&__sidebar {
@@ -64,13 +67,10 @@
display: flex;
flex-direction: column;
gap: var(--base);
padding: var(--base);
.render-fields {
& > *:last-child {
margin-bottom: 0;
}
}
padding-top: calc(var(--base) * 1.5);
padding-left: calc(var(--base) * 2);
padding-right: var(--gutter-h);
padding-bottom: var(--spacing-view-bottom);
}
&__label {
@@ -78,6 +78,28 @@
}
@include mid-break {
display: block;
&--has-sidebar {
.collection-default-edit {
&__main {
width: 100%;
}
&__edit {
[dir='ltr'] & {
border-right: 0;
padding-right: var(--gutter-h);
}
[dir='rtl'] & {
border-left: 0;
padding-left: var(--gutter-h);
}
}
}
}
&__main {
width: 100%;
min-height: initial;
@@ -88,17 +110,13 @@
width: 100%;
height: initial;
border-left: 0;
margin-top: calc(var(--base) / 2);
}
&__form {
display: block;
}
&__edit {
padding-top: var(--base);
padding-bottom: 0;
}
&__sidebar-fields {
padding-top: 0;
padding-left: var(--gutter-h);

View File

@@ -15,7 +15,7 @@ import Upload from '../Upload'
import './index.scss'
import { EditViewProps } from '../../../types'
const baseClass = 'collection-edit'
const baseClass = 'collection-default-edit'
export const DefaultCollectionEdit: React.FC<
EditViewProps & {
@@ -23,9 +23,9 @@ export const DefaultCollectionEdit: React.FC<
disableActions?: boolean
}
> = (props) => {
if ('collection' in props) {
const { i18n, t } = useTranslation('general')
if ('collection' in props) {
const {
id,
apiURL,
@@ -67,7 +67,10 @@ export const DefaultCollectionEdit: React.FC<
permissions={permissions}
/>
<div
className={[`${baseClass}__wrapper`, hasSidebar && `${baseClass}__wrapper--has-sidebar`]
className={[
baseClass,
hasSidebar ? `${baseClass}--has-sidebar` : `${baseClass}--no-sidebar`,
]
.filter(Boolean)
.join(' ')}
>
@@ -88,6 +91,7 @@ export const DefaultCollectionEdit: React.FC<
<Gutter className={`${baseClass}__edit`}>
{auth && (
<Auth
className={`${baseClass}__auth`}
collection={collection}
email={data?.email}
operation={operation}
@@ -106,6 +110,7 @@ export const DefaultCollectionEdit: React.FC<
filter={(field) => !field?.admin?.position || field?.admin?.position !== 'sidebar'}
permissions={permissions.fields}
readOnly={!hasSavePermission}
className={`${baseClass}__fields`}
/>
</Gutter>
</div>
@@ -124,6 +129,4 @@ export const DefaultCollectionEdit: React.FC<
</Fragment>
)
}
return null
}

View File

@@ -2,7 +2,7 @@
.file-field {
position: relative;
margin: base(1.5) 0 base(2);
margin-bottom: var(--base);
background: var(--theme-elevation-50);
.tooltip.error-message {

View File

@@ -11,6 +11,7 @@ import Label from '../../../../forms/Label'
import useField from '../../../../forms/useField'
import { useDocumentInfo } from '../../../../utilities/DocumentInfo'
import './index.scss'
import { fieldBaseClass } from '../../../../forms/field-types/shared'
const baseClass = 'file-field'
@@ -133,17 +134,17 @@ const Upload: React.FC<Props> = (props) => {
return () => null
}, [handleDragIn, handleDragOut, handleDrop, value])
const classes = [baseClass, dragging && `${baseClass}--dragging`, 'field-type']
.filter(Boolean)
.join(' ')
const canRemoveUpload =
docPermissions?.update?.permission &&
'delete' in docPermissions &&
docPermissions?.delete?.permission
return (
<div className={classes}>
<div
className={[fieldBaseClass, baseClass, dragging && `${baseClass}--dragging`]
.filter(Boolean)
.join(' ')}
>
<Error message={errorMessage} showError={showError} />
{doc.filename && !replacingFile && (
<FileDetails

View File

@@ -129,7 +129,7 @@ const DefaultList: React.FC<Props> = (props) => {
)}
{Array.isArray(AfterListTable) &&
AfterListTable.map((Component, i) => <Component key={i} {...props} />)}
{data.docs && data.docs.length > 0 && (
<div className={`${baseClass}__page-controls`}>
<Paginator
disableHistoryChange={modifySearchParams === false}
@@ -159,8 +159,8 @@ const DefaultList: React.FC<Props> = (props) => {
modifySearchParams={modifySearchParams}
resetPage={data.totalDocs <= data.pagingCounter}
/>
<div className={`${baseClass}__list-selection`}>
{smallBreak && (
<div className={`${baseClass}__list-selection`}>
<Fragment>
<ListSelection label={getTranslation(collection.labels.plural, i18n)} />
<div className={`${baseClass}__list-selection-actions`}>
@@ -170,11 +170,12 @@ const DefaultList: React.FC<Props> = (props) => {
<DeleteMany collection={collection} resetParams={resetParams} />
</div>
</Fragment>
)}
</div>
)}
</Fragment>
)}
</div>
)}
</Gutter>
</SelectionProvider>
{Array.isArray(AfterList) &&

View File

@@ -0,0 +1,7 @@
@import '../../../../../scss/styles.scss';
.select-all {
&__checkbox {
display: block;
}
}

View File

@@ -3,6 +3,9 @@ import { useTranslation } from 'react-i18next'
import { CheckboxInput } from '../../../../forms/field-types/Checkbox/Input'
import { SelectAllStatus, useSelection } from '../SelectionProvider'
import './index.scss'
const baseClass = 'select-all'
const SelectAll: React.FC = () => {
const { t } = useTranslation('general')
@@ -17,6 +20,7 @@ const SelectAll: React.FC = () => {
id="select-all"
onToggle={() => toggleAll()}
partialChecked={selectAll === SelectAllStatus.Some}
className={`${baseClass}__checkbox`}
/>
)
}

View File

@@ -1,49 +1,7 @@
@import '../../../../../scss/styles.scss';
.select-row {
button {
@extend %btn-reset;
display: flex;
align-items: center;
cursor: pointer;
&:focus:not(:focus-visible),
&:active {
outline: none;
}
&:hover {
svg {
opacity: 0.2;
}
}
&:focus-visible {
outline: var(--accessibility-outline);
outline-offset: var(--accessibility-outline-offset);
}
}
&__input {
@include formInput;
padding: 0;
line-height: 0;
position: relative;
width: $baseline;
height: $baseline;
svg {
opacity: 0;
}
}
&--checked {
button {
.select-row__input {
svg {
opacity: 1;
}
}
}
&__checkbox {
display: block;
}
}

View File

@@ -4,10 +4,18 @@ import { CheckboxInput } from '../../../../forms/field-types/Checkbox/Input'
import { useSelection } from '../SelectionProvider'
import './index.scss'
const baseClass = 'select-row'
const SelectRow: React.FC<{ id: number | string }> = ({ id }) => {
const { selected, setSelection } = useSelection()
return <CheckboxInput checked={selected[id]} onToggle={() => setSelection(id)} />
return (
<CheckboxInput
checked={selected[id]}
onToggle={() => setSelection(id)}
className={`${baseClass}__checkbox`}
/>
)
}
export default SelectRow

View File

@@ -3,14 +3,16 @@
.collection-list {
width: 100%;
margin-top: base(0.5);
margin-bottom: base(2);
&__wrap {
padding-bottom: base(4);
padding-bottom: var(--spacing-view-bottom);
& > *:not(:last-child) {
margin-bottom: var(--base);
}
}
&__header {
margin-bottom: $baseline;
display: flex;
align-items: flex-end;
flex-wrap: wrap;
@@ -26,7 +28,7 @@
.pill {
position: relative;
top: -8px;
top: -14px;
margin: 0;
}
}
@@ -49,6 +51,17 @@
}
}
&__no-results {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: var(--base);
& > * {
margin: 0;
}
}
&__page-controls {
width: 100%;
display: flex;
@@ -127,6 +140,23 @@
margin: 0;
}
// on mobile, extend the table all the way to the viewport edges
// this is to visually indicate overflowing content
.table {
display: flex;
width: calc(100% + calc(var(--gutter-h) * 2));
max-width: unset;
left: calc(var(--gutter-h) * -1);
position: relative;
padding-left: var(--gutter-h);
&::after {
content: '';
height: 1px;
padding-right: var(--gutter-h);
}
}
&__page-controls {
flex-wrap: wrap;
}
@@ -147,7 +177,7 @@
&__header {
.pill {
top: -2px;
top: -6px;
}
}
}

Some files were not shown because too many files have changed in this diff Show More