chore: misc css (#3391)
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -16,4 +16,8 @@
|
||||
background-color: var(--theme-elevation-150);
|
||||
}
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
padding: calc(var(--base) / 2) calc(var(--base) / 2) 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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`]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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} />)}
|
||||
|
||||
@@ -96,10 +96,6 @@
|
||||
&__content {
|
||||
padding: 0 calc(var(--gutter-h) / 2);
|
||||
}
|
||||
|
||||
&__nav-wrapper {
|
||||
margin-left: calc(var(--base) / 1.125);
|
||||
}
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -14,6 +14,7 @@ export type Props = {
|
||||
className?: string
|
||||
description?: Description
|
||||
value?: unknown
|
||||
marginPlacement?: 'top' | 'bottom'
|
||||
}
|
||||
|
||||
export function isComponent(description: Description): description is DescriptionComponent {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { ReducedField } from './filterFields'
|
||||
export type Props = {
|
||||
className?: string
|
||||
fieldTypes: FieldTypes
|
||||
margins?: 'small' | false
|
||||
forceRender?: boolean
|
||||
} & (
|
||||
| {
|
||||
|
||||
@@ -129,6 +129,7 @@ export const ArrayRow: React.FC<ArrayRowProps> = ({
|
||||
indexPath={indexPath}
|
||||
permissions={permissions?.fields}
|
||||
readOnly={readOnly}
|
||||
margins="small"
|
||||
/>
|
||||
</Collapsible>
|
||||
</div>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -79,7 +85,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='light'] {
|
||||
.array-field {
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -133,6 +133,7 @@ export const BlockRow: React.FC<BlockFieldProps> = ({
|
||||
indexPath={indexPath}
|
||||
permissions={permissions?.blocks?.[row.blockType]?.fields}
|
||||
readOnly={readOnly}
|
||||
margins="small"
|
||||
/>
|
||||
</Collapsible>
|
||||
</div>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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`,
|
||||
]
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
.code-field {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
|
||||
&.error {
|
||||
textarea {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
@import '../../../../scss/styles.scss';
|
||||
|
||||
.collapsible-field {
|
||||
margin: 0 0 base(2);
|
||||
|
||||
&__row-label-wrap {
|
||||
pointer-events: none;
|
||||
display: flex;
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
.field-type.confirm-password {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
|
||||
input {
|
||||
@include formInput;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
@import '../../../../scss/styles';
|
||||
|
||||
.date-time-field {
|
||||
margin-bottom: $baseline;
|
||||
|
||||
&__error-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@import '../../../../scss/styles.scss';
|
||||
|
||||
.field-type.email {
|
||||
margin-bottom: $baseline;
|
||||
position: relative;
|
||||
|
||||
input {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
.json-field {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
}
|
||||
|
||||
html[data-theme='light'] {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
.field-type.number {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
|
||||
&:not(.has-many) {
|
||||
input {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
.field-type.password {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
|
||||
input {
|
||||
@include formInput;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
@import '../../../../scss/styles.scss';
|
||||
|
||||
.radio-group {
|
||||
margin-bottom: $baseline;
|
||||
|
||||
&__error-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
.field-type.relationship {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
}
|
||||
|
||||
.relationship {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
.field-type.select {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
}
|
||||
|
||||
html[data-theme='light'] {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
.field-type.text {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
|
||||
input {
|
||||
@include formInput;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
.field-type.textarea {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
.upload {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
|
||||
&__wrap {
|
||||
background: var(--theme-elevation-50);
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -19,9 +19,5 @@
|
||||
[dir='rtl'] & {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&__wrap {
|
||||
padding: 0 0 $baseline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) &&
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(' ')}
|
||||
>
|
||||
|
||||
@@ -7,4 +7,18 @@
|
||||
width: 100%;
|
||||
margin-bottom: base(2);
|
||||
}
|
||||
|
||||
&__wrap {
|
||||
& > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
& > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) &&
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
@import '../../../../../scss/styles.scss';
|
||||
|
||||
.select-all {
|
||||
&__checkbox {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -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`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user