feat: 2.0 popover style updates (#3404)
This commit is contained in:
@@ -11,16 +11,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.popup--active .array-actions__button {
|
|
||||||
background: var(--theme-elevation-0);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__button,
|
|
||||||
&__action {
|
|
||||||
@extend %btn-reset;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__actions {
|
&__actions {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -28,9 +18,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__action {
|
&__action {
|
||||||
@extend %btn-reset;
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -1px;
|
top: -1px;
|
||||||
@@ -40,10 +27,6 @@
|
|||||||
stroke-width: 1px;
|
stroke-width: 1px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__move-up {
|
&__move-up {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import More from '../../icons/More'
|
|||||||
import Plus from '../../icons/Plus'
|
import Plus from '../../icons/Plus'
|
||||||
import X from '../../icons/X'
|
import X from '../../icons/X'
|
||||||
import Popup from '../Popup'
|
import Popup from '../Popup'
|
||||||
|
import * as PopupList from '../Popup/PopupButtonList'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
const baseClass = 'array-actions'
|
const baseClass = 'array-actions'
|
||||||
@@ -31,73 +32,69 @@ export const ArrayAction: React.FC<Props> = ({
|
|||||||
horizontalAlign="center"
|
horizontalAlign="center"
|
||||||
render={({ close }) => {
|
render={({ close }) => {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<PopupList.ButtonGroup buttonSize="small">
|
||||||
{index !== 0 && (
|
{index !== 0 && (
|
||||||
<button
|
<PopupList.Button
|
||||||
className={`${baseClass}__action ${baseClass}__move-up`}
|
className={`${baseClass}__action ${baseClass}__move-up`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
moveRow(index, index - 1)
|
moveRow(index, index - 1)
|
||||||
close()
|
close()
|
||||||
}}
|
}}
|
||||||
type="button"
|
|
||||||
>
|
>
|
||||||
<Chevron />
|
<Chevron />
|
||||||
{t('moveUp')}
|
{t('moveUp')}
|
||||||
</button>
|
</PopupList.Button>
|
||||||
)}
|
)}
|
||||||
{index < rowCount - 1 && (
|
{index < rowCount - 1 && (
|
||||||
<button
|
<PopupList.Button
|
||||||
className={`${baseClass}__action ${baseClass}__move-down`}
|
className={`${baseClass}__action`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
moveRow(index, index + 1)
|
moveRow(index, index + 1)
|
||||||
close()
|
close()
|
||||||
}}
|
}}
|
||||||
type="button"
|
|
||||||
>
|
>
|
||||||
<Chevron />
|
<Chevron />
|
||||||
{t('moveDown')}
|
{t('moveDown')}
|
||||||
</button>
|
</PopupList.Button>
|
||||||
)}
|
)}
|
||||||
{!hasMaxRows && (
|
{!hasMaxRows && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<button
|
<PopupList.Button
|
||||||
className={`${baseClass}__action ${baseClass}__add`}
|
className={`${baseClass}__action ${baseClass}__add`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
addRow(index + 1)
|
addRow(index + 1)
|
||||||
close()
|
close()
|
||||||
}}
|
}}
|
||||||
type="button"
|
|
||||||
>
|
>
|
||||||
<Plus />
|
<Plus />
|
||||||
{t('addBelow')}
|
{t('addBelow')}
|
||||||
</button>
|
</PopupList.Button>
|
||||||
<button
|
<PopupList.Button
|
||||||
className={`${baseClass}__action ${baseClass}__duplicate`}
|
className={`${baseClass}__action ${baseClass}__duplicate`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
duplicateRow(index)
|
duplicateRow(index)
|
||||||
close()
|
close()
|
||||||
}}
|
}}
|
||||||
type="button"
|
|
||||||
>
|
>
|
||||||
<Copy />
|
<Copy />
|
||||||
{t('duplicate')}
|
{t('duplicate')}
|
||||||
</button>
|
</PopupList.Button>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
<button
|
<PopupList.Button
|
||||||
className={`${baseClass}__action ${baseClass}__remove`}
|
className={`${baseClass}__action ${baseClass}__remove`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
removeRow(index)
|
removeRow(index)
|
||||||
close()
|
close()
|
||||||
}}
|
}}
|
||||||
type="button"
|
|
||||||
>
|
>
|
||||||
<X />
|
<X />
|
||||||
{t('remove')}
|
{t('remove')}
|
||||||
</button>
|
</PopupList.Button>
|
||||||
</React.Fragment>
|
</PopupList.ButtonGroup>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
size="medium"
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,13 +73,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__controls-wrapper {
|
&__controls-wrapper {
|
||||||
|
--controls-gap: calc(var(--base) / 2);
|
||||||
|
--dot-button-width: calc(var(--base) * 2);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
// move to the right to account for the padding on the dots
|
gap: var(--controls-gap);
|
||||||
// this will make sure the alignment is correct
|
padding-right: calc(var(--controls-gap) + var(--dot-button-width));
|
||||||
// while still keeping a large button hitbox
|
position: relative;
|
||||||
transform: translate3d(var(--base), 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__controls {
|
&__controls {
|
||||||
@@ -94,27 +95,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__popup {
|
|
||||||
.popup-button {
|
|
||||||
padding: var(--base);
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--theme-elevation-500);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--theme-text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__dots {
|
&__dots {
|
||||||
display: flex;
|
height: 100%;
|
||||||
gap: 2px;
|
margin: 0;
|
||||||
background-color: transparent;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
> div {
|
.btn__label {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> span > span > div {
|
||||||
width: 3px;
|
width: 3px;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
@@ -122,49 +116,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__popup-actions {
|
&__popup {
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: calc(var(--base) / 4);
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
right: 0;
|
||||||
width: 100%;
|
|
||||||
border-radius: 1px;
|
|
||||||
background: var(--theme-elevation-100);
|
|
||||||
width: calc(100% + (var(--base) / 2));
|
|
||||||
left: calc(var(--base) / -4);
|
|
||||||
top: 0;
|
top: 0;
|
||||||
opacity: 0;
|
bottom: 0;
|
||||||
transition: opacity 50ms linear;
|
width: var(--dot-button-width);
|
||||||
}
|
|
||||||
|
|
||||||
&:hover::before {
|
.popup__trigger-wrap {
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
> * {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
text-align: left;
|
|
||||||
font-size: inherit;
|
|
||||||
line-height: inherit;
|
|
||||||
font-family: inherit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { Fragment } from 'react'
|
import React, { Fragment } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Link } from 'react-router-dom'
|
|
||||||
|
|
||||||
import type { CollectionPermission, GlobalPermission } from '../../../../auth'
|
import type { CollectionPermission, GlobalPermission } from '../../../../auth'
|
||||||
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from '../../../../exports/types'
|
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from '../../../../exports/types'
|
||||||
@@ -9,10 +8,12 @@ import { formatDate } from '../../../utilities/formatDate'
|
|||||||
import { useConfig } from '../../utilities/Config'
|
import { useConfig } from '../../utilities/Config'
|
||||||
import { useDocumentInfo } from '../../utilities/DocumentInfo'
|
import { useDocumentInfo } from '../../utilities/DocumentInfo'
|
||||||
import Autosave from '../Autosave'
|
import Autosave from '../Autosave'
|
||||||
|
import Button from '../Button'
|
||||||
import DeleteDocument from '../DeleteDocument'
|
import DeleteDocument from '../DeleteDocument'
|
||||||
import DuplicateDocument from '../DuplicateDocument'
|
import DuplicateDocument from '../DuplicateDocument'
|
||||||
import { Gutter } from '../Gutter'
|
import { Gutter } from '../Gutter'
|
||||||
import Popup from '../Popup'
|
import Popup from '../Popup'
|
||||||
|
import * as PopupList from '../Popup/PopupButtonList'
|
||||||
import PreviewButton from '../PreviewButton'
|
import PreviewButton from '../PreviewButton'
|
||||||
import { Publish } from '../Publish'
|
import { Publish } from '../Publish'
|
||||||
import { Save } from '../Save'
|
import { Save } from '../Save'
|
||||||
@@ -30,9 +31,9 @@ export const DocumentControls: React.FC<{
|
|||||||
global?: SanitizedGlobalConfig
|
global?: SanitizedGlobalConfig
|
||||||
hasSavePermission?: boolean
|
hasSavePermission?: boolean
|
||||||
id?: string
|
id?: string
|
||||||
|
isAccountView?: boolean
|
||||||
isEditing?: boolean
|
isEditing?: boolean
|
||||||
permissions?: CollectionPermission | GlobalPermission
|
permissions?: CollectionPermission | GlobalPermission
|
||||||
isAccountView?: boolean
|
|
||||||
}> = (props) => {
|
}> = (props) => {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
@@ -41,9 +42,9 @@ export const DocumentControls: React.FC<{
|
|||||||
disableActions,
|
disableActions,
|
||||||
global,
|
global,
|
||||||
hasSavePermission,
|
hasSavePermission,
|
||||||
|
isAccountView,
|
||||||
isEditing,
|
isEditing,
|
||||||
permissions,
|
permissions,
|
||||||
isAccountView,
|
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const { publishedDoc } = useDocumentInfo()
|
const { publishedDoc } = useDocumentInfo()
|
||||||
@@ -73,6 +74,8 @@ export const DocumentControls: React.FC<{
|
|||||||
!global?.versions?.drafts?.autosave
|
!global?.versions?.drafts?.autosave
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showDotMenu = Boolean(collection && !disableActions)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Gutter className={baseClass}>
|
<Gutter className={baseClass}>
|
||||||
<div className={`${baseClass}__wrapper`}>
|
<div className={`${baseClass}__wrapper`}>
|
||||||
@@ -187,49 +190,47 @@ export const DocumentControls: React.FC<{
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{Boolean(collection && !disableActions) && (
|
{showDotMenu && (
|
||||||
<Popup
|
<Popup
|
||||||
button={
|
button={
|
||||||
<div className={`${baseClass}__dots`}>
|
<Button buttonStyle="secondary" className={`${baseClass}__dots`} el="div">
|
||||||
<div />
|
<div />
|
||||||
<div />
|
<div />
|
||||||
<div />
|
<div />
|
||||||
</div>
|
</Button>
|
||||||
}
|
}
|
||||||
caret={false}
|
|
||||||
className={`${baseClass}__popup`}
|
className={`${baseClass}__popup`}
|
||||||
horizontalAlign="center"
|
horizontalAlign="right"
|
||||||
size="large"
|
size="large"
|
||||||
verticalAlign="bottom"
|
verticalAlign="bottom"
|
||||||
>
|
>
|
||||||
<ul className={`${baseClass}__popup-actions`}>
|
<PopupList.ButtonGroup>
|
||||||
{'create' in permissions && permissions?.create?.permission && (
|
{'create' in permissions && permissions?.create?.permission && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<li>
|
<PopupList.Button
|
||||||
<Link
|
|
||||||
id="action-create"
|
id="action-create"
|
||||||
to={`${adminRoute}/collections/${collection?.slug}/create`}
|
to={`${adminRoute}/collections/${collection?.slug}/create`}
|
||||||
>
|
>
|
||||||
{t('createNew')}
|
{t('createNew')}
|
||||||
</Link>
|
</PopupList.Button>
|
||||||
</li>
|
|
||||||
{!collection?.admin?.disableDuplicate && isEditing && (
|
{!collection?.admin?.disableDuplicate && isEditing && (
|
||||||
<li>
|
<PopupList.Button>
|
||||||
<DuplicateDocument
|
<DuplicateDocument
|
||||||
collection={collection}
|
collection={collection}
|
||||||
id={id}
|
id={id}
|
||||||
slug={collection?.slug}
|
slug={collection?.slug}
|
||||||
/>
|
/>
|
||||||
</li>
|
</PopupList.Button>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
{'delete' in permissions && permissions?.delete?.permission && id && (
|
{'delete' in permissions && permissions?.delete?.permission && id && (
|
||||||
<li>
|
<PopupList.Button>
|
||||||
<DeleteDocument buttonId="action-delete" collection={collection} id={id} />
|
<DeleteDocument buttonId="action-delete" collection={collection} id={id} />
|
||||||
</li>
|
</PopupList.Button>
|
||||||
)}
|
)}
|
||||||
</ul>
|
</PopupList.ButtonGroup>
|
||||||
</Popup>
|
</Popup>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
button {
|
button {
|
||||||
color: currentColor;
|
color: currentColor;
|
||||||
padding: base(0.25) 0;
|
padding: 0;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
line-height: base(1);
|
line-height: base(1);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@@ -39,33 +39,10 @@
|
|||||||
|
|
||||||
&__button {
|
&__button {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
color: var(--theme-elevation-400);
|
color: var(--theme-elevation-400);
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
max-height: base(8);
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
li a {
|
|
||||||
all: unset;
|
|
||||||
cursor: pointer;
|
|
||||||
padding-right: 0;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus-visible {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include mid-break {
|
|
||||||
.popup__content {
|
|
||||||
width: calc(100vw - calc(var(--gutter-h) * 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import qs from 'qs'
|
import qs from 'qs'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Link } from 'react-router-dom'
|
|
||||||
|
|
||||||
import { Chevron } from '../..'
|
import { Chevron } from '../..'
|
||||||
import { useConfig } from '../../utilities/Config'
|
import { useConfig } from '../../utilities/Config'
|
||||||
import { useLocale } from '../../utilities/Locale'
|
import { useLocale } from '../../utilities/Locale'
|
||||||
import { useSearchParams } from '../../utilities/SearchParams'
|
import { useSearchParams } from '../../utilities/SearchParams'
|
||||||
import Popup from '../Popup'
|
import Popup from '../Popup'
|
||||||
|
import * as PopupList from '../Popup/PopupButtonList'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
const baseClass = 'localizer'
|
const baseClass = 'localizer'
|
||||||
@@ -37,20 +37,10 @@ const Localizer: React.FC<{
|
|||||||
<Chevron className={`${baseClass}__chevron`} />
|
<Chevron className={`${baseClass}__chevron`} />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
caret={false}
|
horizontalAlign="right"
|
||||||
horizontalAlign="left"
|
|
||||||
render={({ close }) => (
|
render={({ close }) => (
|
||||||
<ul>
|
<PopupList.ButtonGroup>
|
||||||
{locales.map((localeOption) => {
|
{locales.map((localeOption) => {
|
||||||
const baseLocaleClass = `${baseClass}__locale`
|
|
||||||
|
|
||||||
const localeClasses = [
|
|
||||||
baseLocaleClass,
|
|
||||||
locale.code === localeOption.code && `${baseLocaleClass}--active`,
|
|
||||||
]
|
|
||||||
.filter(Boolean)
|
|
||||||
.join('')
|
|
||||||
|
|
||||||
const newParams = {
|
const newParams = {
|
||||||
...searchParams,
|
...searchParams,
|
||||||
locale: localeOption.code,
|
locale: localeOption.code,
|
||||||
@@ -60,20 +50,19 @@ const Localizer: React.FC<{
|
|||||||
|
|
||||||
if (localeOption.code !== locale.code) {
|
if (localeOption.code !== locale.code) {
|
||||||
return (
|
return (
|
||||||
<li className={localeClasses} key={localeOption.code}>
|
<PopupList.Button key={localeOption.code} onClick={close} to={{ search }}>
|
||||||
<Link onClick={close} to={{ search }}>
|
|
||||||
{localeOption.label}
|
{localeOption.label}
|
||||||
{localeOption.label !== localeOption.code && ` (${localeOption.code})`}
|
{localeOption.label !== localeOption.code && ` (${localeOption.code})`}
|
||||||
</Link>
|
</PopupList.Button>
|
||||||
</li>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
})}
|
})}
|
||||||
</ul>
|
</PopupList.ButtonGroup>
|
||||||
)}
|
)}
|
||||||
showScrollbar
|
showScrollbar
|
||||||
|
size="large"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,12 +7,6 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-button--default {
|
|
||||||
@extend %btn-reset;
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__button {
|
&__button {
|
||||||
@extend %btn-reset;
|
@extend %btn-reset;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { defaults } from '../../../../collections/config/defaults'
|
|||||||
import Chevron from '../../icons/Chevron'
|
import Chevron from '../../icons/Chevron'
|
||||||
import { useSearchParams } from '../../utilities/SearchParams'
|
import { useSearchParams } from '../../utilities/SearchParams'
|
||||||
import Popup from '../Popup'
|
import Popup from '../Popup'
|
||||||
|
import * as PopupList from '../Popup/PopupButtonList'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
const baseClass = 'per-page'
|
const baseClass = 'per-page'
|
||||||
@@ -43,17 +44,13 @@ const PerPage: React.FC<Props> = ({
|
|||||||
}
|
}
|
||||||
horizontalAlign="right"
|
horizontalAlign="right"
|
||||||
render={({ close }) => (
|
render={({ close }) => (
|
||||||
<div>
|
<PopupList.ButtonGroup>
|
||||||
<ul>
|
|
||||||
{limits.map((limitNumber, i) => (
|
{limits.map((limitNumber, i) => (
|
||||||
<li className={`${baseClass}-item`} key={i}>
|
<PopupList.Button
|
||||||
<button
|
className={[limitNumber === Number(limit) && `${baseClass}__button-active`]
|
||||||
className={[
|
|
||||||
`${baseClass}__button`,
|
|
||||||
limitNumber === Number(limit) && `${baseClass}__button-active`,
|
|
||||||
]
|
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')}
|
.join(' ')}
|
||||||
|
key={i}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
close()
|
close()
|
||||||
if (handleChange) handleChange(limitNumber)
|
if (handleChange) handleChange(limitNumber)
|
||||||
@@ -70,16 +67,14 @@ const PerPage: React.FC<Props> = ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
type="button"
|
|
||||||
>
|
>
|
||||||
{limitNumber === Number(limit) && <Chevron />}
|
{limitNumber === Number(limit) && <Chevron />}
|
||||||
{limitNumber}
|
{limitNumber}
|
||||||
</button>
|
</PopupList.Button>
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</ul>
|
</PopupList.ButtonGroup>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
size="small"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
@import '../../../../scss/styles.scss';
|
|
||||||
|
|
||||||
.popup-button {
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
@import '../../../../scss/styles.scss';
|
||||||
|
|
||||||
|
.popup-button-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: left;
|
||||||
|
--list-button-padding: calc(var(--base) * 0.5);
|
||||||
|
|
||||||
|
&__text-align--left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text-align--center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text-align--right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
@extend %btn-reset;
|
||||||
|
padding-left: var(--list-button-padding);
|
||||||
|
padding-right: var(--list-button-padding);
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: inherit;
|
||||||
|
line-height: var(--base);
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
@extend %btn-reset;
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus-visible,
|
||||||
|
&:focus-within {
|
||||||
|
background-color: var(--popup-button-highlight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import type { LinkProps } from 'react-router-dom'
|
||||||
|
|
||||||
|
import * as React from 'react'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
const baseClass = 'popup-button-list'
|
||||||
|
export const ButtonGroup: React.FC<{
|
||||||
|
buttonSize?: 'default' | 'small'
|
||||||
|
children: React.ReactNode
|
||||||
|
className?: string
|
||||||
|
textAlign?: 'center' | 'left' | 'right'
|
||||||
|
}> = ({ buttonSize = 'default', children, className, textAlign = 'left' }) => {
|
||||||
|
const classes = [
|
||||||
|
baseClass,
|
||||||
|
className,
|
||||||
|
`${baseClass}__text-align--${textAlign}`,
|
||||||
|
`${baseClass}__button-size--${buttonSize}`,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')
|
||||||
|
return <div className={classes}>{children}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
type MenuButtonProps = {
|
||||||
|
children: React.ReactNode
|
||||||
|
className?: string
|
||||||
|
id?: string
|
||||||
|
onClick?: () => void
|
||||||
|
to?: LinkProps['to']
|
||||||
|
}
|
||||||
|
export const Button: React.FC<MenuButtonProps> = ({ id, children, className, onClick, to }) => {
|
||||||
|
const classes = [`${baseClass}__button`, className].filter(Boolean).join(' ')
|
||||||
|
|
||||||
|
if (to) {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
className={classes}
|
||||||
|
id={id}
|
||||||
|
onClick={() => {
|
||||||
|
if (onClick) {
|
||||||
|
onClick()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
to={to}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onClick) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={classes}
|
||||||
|
id={id}
|
||||||
|
onClick={() => {
|
||||||
|
if (onClick) {
|
||||||
|
onClick()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes} id={id}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
@import '../../../../scss/styles.scss';
|
||||||
|
|
||||||
|
.popup-button {
|
||||||
|
height: 100%;
|
||||||
|
color: currentColor;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: base(1);
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import './index.scss'
|
|||||||
|
|
||||||
const baseClass = 'popup-button'
|
const baseClass = 'popup-button'
|
||||||
|
|
||||||
const PopupButton: React.FC<Props> = (props) => {
|
export const PopupTrigger: React.FC<Props> = (props) => {
|
||||||
const { active, button, buttonType, className, setActive } = props
|
const { active, button, buttonType, className, setActive } = props
|
||||||
|
|
||||||
const classes = [baseClass, className, `${baseClass}--${buttonType}`].filter(Boolean).join(' ')
|
const classes = [baseClass, className, `${baseClass}--${buttonType}`].filter(Boolean).join(' ')
|
||||||
@@ -41,5 +41,3 @@ const PopupButton: React.FC<Props> = (props) => {
|
|||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PopupButton
|
|
||||||
@@ -1,43 +1,48 @@
|
|||||||
@import '../../../scss/styles.scss';
|
@import '../../../scss/styles.scss';
|
||||||
|
|
||||||
.popup {
|
.popup {
|
||||||
|
--popup-button-highlight: var(--theme-elevation-200);
|
||||||
|
--popup-bg: var(--theme-input-bg);
|
||||||
|
--popup-text: var(--theme-text);
|
||||||
|
--popup-caret-size: 10px;
|
||||||
|
--popup-x-padding: calc(var(--base) * 0.33);
|
||||||
|
--popup-padding: calc(var(--base) * 0.5);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: var(--theme-input-bg);
|
background: var(--popup-bg);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: var(--z-popup);
|
z-index: var(--z-popup);
|
||||||
max-width: calc(100vw - #{$baseline});
|
max-width: calc(100vw - #{$baseline});
|
||||||
|
color: var(--popup-text);
|
||||||
&--caret {
|
border-radius: 4px;
|
||||||
&:after {
|
padding-left: var(--popup-padding);
|
||||||
content: ' ';
|
padding-right: var(--popup-padding);
|
||||||
position: absolute;
|
min-width: var(--popup-width, auto);
|
||||||
top: calc(100% - 1px);
|
|
||||||
border: 12px solid transparent;
|
|
||||||
border-top-color: var(--theme-input-bg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__wrap {
|
&__hide-scrollbar {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup__scroll {
|
&__scroll-container {
|
||||||
padding: $baseline;
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
padding-right: calc(var(--scrollbar-width) + #{$baseline});
|
|
||||||
width: calc(100% + var(--scrollbar-width));
|
width: calc(100% + var(--scrollbar-width));
|
||||||
|
padding-top: var(--popup-padding);
|
||||||
|
padding-bottom: var(--popup-padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__scroll-content {
|
||||||
|
width: calc(100% - var(--scrollbar-width));
|
||||||
}
|
}
|
||||||
|
|
||||||
&--show-scrollbar {
|
&--show-scrollbar {
|
||||||
.popup__scroll {
|
.popup__scroll-container,
|
||||||
padding-right: 0;
|
.popup__scroll-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,63 +57,24 @@
|
|||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
||||||
&--size-small {
|
&--size-small {
|
||||||
.popup__scroll {
|
--popup-width: 100px;
|
||||||
[dir='ltr'] & {
|
|
||||||
padding: base(0.75) calc(var(--scrollbar-width) + #{base(0.75)}) base(0.75) base(0.75);
|
|
||||||
}
|
|
||||||
[dir='rtl'] & {
|
|
||||||
padding: base(0.75) base(0.75) base(0.75) calc(var(--scrollbar-width) + #{base(0.75)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup__content {
|
.popup__content {
|
||||||
@include shadow-m;
|
@include shadow-m;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.popup--h-align-left {
|
&--size-medium {
|
||||||
|
--popup-width: 150px;
|
||||||
.popup__content {
|
.popup__content {
|
||||||
left: - base(0.5);
|
@include shadow-lg;
|
||||||
|
|
||||||
&:after {
|
|
||||||
left: base(0.425);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&--size-large {
|
&--size-large {
|
||||||
|
--popup-width: 200px;
|
||||||
.popup__content {
|
.popup__content {
|
||||||
@include shadow-lg;
|
@include shadow-lg;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup__scroll {
|
|
||||||
padding: base(1) calc(var(--scrollbar-width) + #{base(1.5)}) base(1) base(1.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&--size-wide {
|
|
||||||
.popup__content {
|
|
||||||
@include shadow-m;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
border: 12px solid transparent;
|
|
||||||
border-top-color: var(--theme-input-bg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup__scroll {
|
|
||||||
padding: base(0.25) base(0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.popup--align-left {
|
|
||||||
.popup__content {
|
|
||||||
left: - base(0.5);
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
left: base(0.425);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
@@ -116,12 +82,8 @@
|
|||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
||||||
&--h-align-left {
|
&--h-align-left {
|
||||||
.popup__content {
|
.popup__caret {
|
||||||
left: - base(1.75);
|
left: var(--popup-padding);
|
||||||
|
|
||||||
&:after {
|
|
||||||
left: base(1.75);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,26 +91,21 @@
|
|||||||
.popup__content {
|
.popup__content {
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
&:after {
|
.popup__caret {
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&--h-align-right {
|
&--h-align-right {
|
||||||
.popup__content {
|
.popup__content {
|
||||||
right: - base(1.75);
|
right: 0;
|
||||||
[dir='rtl'] & {
|
|
||||||
right: - base(0.75);
|
|
||||||
}
|
|
||||||
&:after {
|
|
||||||
right: base(1.75);
|
|
||||||
[dir='rtl'] & {
|
|
||||||
right: base(0.75);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popup__caret {
|
||||||
|
right: var(--popup-padding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,46 +113,32 @@
|
|||||||
// VERTICAL ALIGNMENT
|
// VERTICAL ALIGNMENT
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
||||||
|
&__caret {
|
||||||
|
position: absolute;
|
||||||
|
border: var(--popup-caret-size) solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
&--v-align-top {
|
&--v-align-top {
|
||||||
.popup__content {
|
.popup__content {
|
||||||
bottom: calc(100% + #{$baseline});
|
@include shadow-lg;
|
||||||
|
bottom: calc(100% + var(--popup-caret-size));
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup__caret {
|
||||||
|
top: calc(100% - 1px);
|
||||||
|
border-top-color: var(--popup-bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&--v-align-bottom {
|
&--v-align-bottom {
|
||||||
.popup__content {
|
.popup__content {
|
||||||
@include shadow-lg-top;
|
@include shadow-lg-top;
|
||||||
top: calc(100% + #{base(0.5)});
|
top: calc(100% + var(--popup-caret-size));
|
||||||
|
}
|
||||||
|
|
||||||
&:after {
|
.popup__caret {
|
||||||
top: unset;
|
|
||||||
bottom: calc(100% - 1px);
|
bottom: calc(100% - 1px);
|
||||||
border-top-color: transparent !important;
|
border-bottom-color: var(--popup-bg);
|
||||||
border-bottom-color: var(--theme-input-bg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.popup--color-dark {
|
|
||||||
.popup__content {
|
|
||||||
&:after {
|
|
||||||
border-bottom-color: var(--theme-elevation-800);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////
|
|
||||||
// COLOR
|
|
||||||
////////////////////////////////
|
|
||||||
|
|
||||||
&--color-dark {
|
|
||||||
.popup__content {
|
|
||||||
background: var(--theme-elevation-800);
|
|
||||||
color: var(--theme-input-bg);
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
border-top-color: var(--theme-elevation-800);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,70 +155,56 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@include mid-break {
|
@include mid-break {
|
||||||
&__scroll,
|
--popup-padding: calc(var(--base) * 0.25);
|
||||||
&--size-large .popup__scroll {
|
|
||||||
padding: base(0.75);
|
|
||||||
padding-right: calc(var(--scrollbar-width) + #{base(0.75)});
|
|
||||||
}
|
|
||||||
|
|
||||||
&--h-align-left {
|
|
||||||
.popup__content {
|
|
||||||
left: - base(0.5);
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
left: base(0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&--h-align-center {
|
&--h-align-center {
|
||||||
.popup__content {
|
.popup__content {
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-0%);
|
transform: translateX(-0%);
|
||||||
|
}
|
||||||
|
|
||||||
&:after {
|
.popup__caret {
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-0%);
|
transform: translateX(-0%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&--h-align-right {
|
&--h-align-right {
|
||||||
.popup__content {
|
.popup__content {
|
||||||
right: - base(0.5);
|
right: 0;
|
||||||
|
|
||||||
&:after {
|
|
||||||
right: base(0.5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popup__caret {
|
||||||
|
right: var(--popup-padding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&--force-h-align-left {
|
&--force-h-align-left {
|
||||||
.popup__content {
|
.popup__content {
|
||||||
left: - base(0.5);
|
left: 0;
|
||||||
right: unset;
|
|
||||||
transform: unset;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
left: base(0.5);
|
|
||||||
right: unset;
|
right: unset;
|
||||||
transform: unset;
|
transform: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popup__caret {
|
||||||
|
left: var(--popup-padding);
|
||||||
|
right: unset;
|
||||||
|
transform: unset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&--force-h-align-right {
|
&--force-h-align-right {
|
||||||
.popup__content {
|
.popup__content {
|
||||||
right: - base(0.5);
|
right: 0;
|
||||||
left: unset;
|
left: unset;
|
||||||
transform: unset;
|
transform: unset;
|
||||||
|
}
|
||||||
|
|
||||||
&:after {
|
.popup__caret {
|
||||||
right: base(0.5);
|
right: var(--popup-padding);
|
||||||
left: unset;
|
left: unset;
|
||||||
transform: unset;
|
transform: unset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'
|
|||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
import useIntersect from '../../../hooks/useIntersect'
|
import useIntersect from '../../../hooks/useIntersect'
|
||||||
import PopupButton from './PopupButton'
|
import { PopupTrigger } from './PopupTrigger'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
const baseClass = 'popup'
|
const baseClass = 'popup'
|
||||||
@@ -18,16 +18,14 @@ const Popup: React.FC<Props> = (props) => {
|
|||||||
caret = true,
|
caret = true,
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
color = 'light',
|
|
||||||
forceOpen,
|
forceOpen,
|
||||||
horizontalAlign: horizontalAlignFromProps = 'left',
|
horizontalAlign: horizontalAlignFromProps = 'left',
|
||||||
initActive = false,
|
initActive = false,
|
||||||
onToggleOpen,
|
onToggleOpen,
|
||||||
padding,
|
|
||||||
render,
|
render,
|
||||||
showOnHover = false,
|
showOnHover = false,
|
||||||
showScrollbar = false,
|
showScrollbar = false,
|
||||||
size = 'small',
|
size = 'medium',
|
||||||
verticalAlign: verticalAlignFromProps = 'top',
|
verticalAlign: verticalAlignFromProps = 'top',
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
@@ -38,7 +36,6 @@ const Popup: React.FC<Props> = (props) => {
|
|||||||
threshold: 1,
|
threshold: 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
const buttonRef = useRef(null)
|
|
||||||
const contentRef = useRef(null)
|
const contentRef = useRef(null)
|
||||||
const [active, setActive] = useState(initActive)
|
const [active, setActive] = useState(initActive)
|
||||||
const [verticalAlign, setVerticalAlign] = useState(verticalAlignFromProps)
|
const [verticalAlign, setVerticalAlign] = useState(verticalAlignFromProps)
|
||||||
@@ -57,8 +54,8 @@ const Popup: React.FC<Props> = (props) => {
|
|||||||
} = bounds
|
} = bounds
|
||||||
|
|
||||||
let boundingTopPos = 100
|
let boundingTopPos = 100
|
||||||
let boundingRightPos = window.innerWidth
|
let boundingRightPos = document.documentElement.clientWidth
|
||||||
let boundingBottomPos = window.innerHeight
|
let boundingBottomPos = document.documentElement.clientHeight
|
||||||
let boundingLeftPos = 0
|
let boundingLeftPos = 0
|
||||||
|
|
||||||
if (boundingRef?.current) {
|
if (boundingRef?.current) {
|
||||||
@@ -131,7 +128,6 @@ const Popup: React.FC<Props> = (props) => {
|
|||||||
baseClass,
|
baseClass,
|
||||||
className,
|
className,
|
||||||
`${baseClass}--size-${size}`,
|
`${baseClass}--size-${size}`,
|
||||||
`${baseClass}--color-${color}`,
|
|
||||||
`${baseClass}--v-align-${verticalAlign}`,
|
`${baseClass}--v-align-${verticalAlign}`,
|
||||||
`${baseClass}--h-align-${horizontalAlign}`,
|
`${baseClass}--h-align-${horizontalAlign}`,
|
||||||
active && `${baseClass}--active`,
|
active && `${baseClass}--active`,
|
||||||
@@ -142,40 +138,36 @@ const Popup: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes}>
|
<div className={classes}>
|
||||||
<div className={`${baseClass}__wrapper`} ref={buttonRef}>
|
<div className={`${baseClass}__trigger-wrap`}>
|
||||||
{showOnHover ? (
|
{showOnHover ? (
|
||||||
<div
|
<div
|
||||||
className={`${baseClass}__on-hover-watch`}
|
className={`${baseClass}__on-hover-watch`}
|
||||||
onMouseEnter={() => setActive(true)}
|
onMouseEnter={() => setActive(true)}
|
||||||
onMouseLeave={() => setActive(false)}
|
onMouseLeave={() => setActive(false)}
|
||||||
>
|
>
|
||||||
<PopupButton
|
<PopupTrigger
|
||||||
{...{ active, button, buttonType, className: buttonClassName, setActive }}
|
{...{ active, button, buttonType, className: buttonClassName, setActive }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<PopupButton {...{ active, button, buttonType, className: buttonClassName, setActive }} />
|
<PopupTrigger
|
||||||
|
{...{ active, button, buttonType, className: buttonClassName, setActive }}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div className={`${baseClass}__content`} ref={contentRef}>
|
||||||
className={[`${baseClass}__content`, caret && `${baseClass}__content--caret`]
|
<div className={`${baseClass}__hide-scrollbar`} ref={intersectionRef}>
|
||||||
.filter(Boolean)
|
<div className={`${baseClass}__scroll-container`}>
|
||||||
.join(' ')}
|
<div className={`${baseClass}__scroll-content`}>
|
||||||
ref={contentRef}
|
|
||||||
>
|
|
||||||
<div className={`${baseClass}__wrap`} ref={intersectionRef}>
|
|
||||||
<div
|
|
||||||
className={`${baseClass}__scroll`}
|
|
||||||
style={{
|
|
||||||
padding,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{render && render({ close: () => setActive(false) })}
|
{render && render({ close: () => setActive(false) })}
|
||||||
{children && children}
|
{children && children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{caret && <div className={`${baseClass}__caret`} />}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,13 @@ export type Props = {
|
|||||||
caret?: boolean
|
caret?: boolean
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
className?: string
|
className?: string
|
||||||
color?: 'dark' | 'light'
|
|
||||||
forceOpen?: boolean
|
forceOpen?: boolean
|
||||||
horizontalAlign?: 'center' | 'left' | 'right'
|
horizontalAlign?: 'center' | 'left' | 'right'
|
||||||
initActive?: boolean
|
initActive?: boolean
|
||||||
onToggleOpen?: (active: boolean) => void
|
onToggleOpen?: (active: boolean) => void
|
||||||
padding?: CSSProperties['padding']
|
|
||||||
render?: (any) => React.ReactNode
|
render?: (any) => React.ReactNode
|
||||||
showOnHover?: boolean
|
showOnHover?: boolean
|
||||||
showScrollbar?: boolean
|
showScrollbar?: boolean
|
||||||
size?: 'large' | 'small' | 'wide'
|
size?: 'fit-content' | 'large' | 'medium' | 'small'
|
||||||
verticalAlign?: 'bottom' | 'top'
|
verticalAlign?: 'bottom' | 'top'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
|
||||||
.popup__wrapper {
|
.popup__trigger-wrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -22,27 +22,4 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__relations {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
li:not(:last-child) {
|
|
||||||
margin-bottom: base(0.375);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__relation-button {
|
|
||||||
@extend %btn-reset;
|
|
||||||
cursor: pointer;
|
|
||||||
display: block;
|
|
||||||
padding: base(0.125) 0;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { Fragment, useCallback, useEffect, useState } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import type { SanitizedCollectionConfig } from '../../../../../../collections/config/types'
|
import type { SanitizedCollectionConfig } from '../../../../../../collections/config/types'
|
||||||
|
import type { EditViewProps } from '../../../../views/types'
|
||||||
import type { Value } from '../types'
|
import type { Value } from '../types'
|
||||||
import type { Props } from './types'
|
import type { Props } from './types'
|
||||||
|
|
||||||
@@ -9,13 +10,13 @@ import { getTranslation } from '../../../../../../utilities/getTranslation'
|
|||||||
import Button from '../../../../elements/Button'
|
import Button from '../../../../elements/Button'
|
||||||
import { useDocumentDrawer } from '../../../../elements/DocumentDrawer'
|
import { useDocumentDrawer } from '../../../../elements/DocumentDrawer'
|
||||||
import Popup from '../../../../elements/Popup'
|
import Popup from '../../../../elements/Popup'
|
||||||
|
import * as PopupList from '../../../../elements/Popup/PopupButtonList'
|
||||||
import Tooltip from '../../../../elements/Tooltip'
|
import Tooltip from '../../../../elements/Tooltip'
|
||||||
import Plus from '../../../../icons/Plus'
|
import Plus from '../../../../icons/Plus'
|
||||||
import { useAuth } from '../../../../utilities/Auth'
|
import { useAuth } from '../../../../utilities/Auth'
|
||||||
import { useConfig } from '../../../../utilities/Config'
|
import { useConfig } from '../../../../utilities/Config'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
import { useRelatedCollections } from './useRelatedCollections'
|
import { useRelatedCollections } from './useRelatedCollections'
|
||||||
import { EditViewProps } from '../../../../views/types'
|
|
||||||
|
|
||||||
const baseClass = 'relationship-add-new'
|
const baseClass = 'relationship-add-new'
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ export const AddNewRelation: React.FC<Props> = ({
|
|||||||
[relationTo, collectionConfig, dispatchOptions, i18n, hasMany, setValue, value, config],
|
[relationTo, collectionConfig, dispatchOptions, i18n, hasMany, setValue, value, config],
|
||||||
)
|
)
|
||||||
|
|
||||||
const onPopopToggle = useCallback((state) => {
|
const onPopupToggle = useCallback((state) => {
|
||||||
setPopupOpen(state)
|
setPopupOpen(state)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@@ -161,31 +162,30 @@ export const AddNewRelation: React.FC<Props> = ({
|
|||||||
}
|
}
|
||||||
buttonType="custom"
|
buttonType="custom"
|
||||||
horizontalAlign="center"
|
horizontalAlign="center"
|
||||||
onToggleOpen={onPopopToggle}
|
onToggleOpen={onPopupToggle}
|
||||||
render={({ close: closePopup }) => (
|
render={({ close: closePopup }) => (
|
||||||
<ul className={`${baseClass}__relations`}>
|
<PopupList.ButtonGroup>
|
||||||
{relatedCollections.map((relatedCollection) => {
|
{relatedCollections.map((relatedCollection) => {
|
||||||
if (permissions.collections[relatedCollection.slug].create.permission) {
|
if (permissions.collections[relatedCollection.slug].create.permission) {
|
||||||
return (
|
return (
|
||||||
<li key={relatedCollection.slug}>
|
<PopupList.Button
|
||||||
<button
|
className={`${baseClass}__relation-button--${relatedCollection.slug}`}
|
||||||
className={`${baseClass}__relation-button ${baseClass}__relation-button--${relatedCollection.slug}`}
|
key={relatedCollection.slug}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
closePopup()
|
closePopup()
|
||||||
setSelectedCollection(relatedCollection.slug)
|
setSelectedCollection(relatedCollection.slug)
|
||||||
}}
|
}}
|
||||||
type="button"
|
|
||||||
>
|
>
|
||||||
{getTranslation(relatedCollection.labels.singular, i18n)}
|
{getTranslation(relatedCollection.labels.singular, i18n)}
|
||||||
</button>
|
</PopupList.Button>
|
||||||
</li>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
})}
|
})}
|
||||||
</ul>
|
</PopupList.ButtonGroup>
|
||||||
)}
|
)}
|
||||||
|
size="medium"
|
||||||
/>
|
/>
|
||||||
{collectionConfig &&
|
{collectionConfig &&
|
||||||
permissions.collections[collectionConfig.slug].create.permission && (
|
permissions.collections[collectionConfig.slug].create.permission && (
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ $focus-box-shadow: 0 0 0 $style-stroke-width-m var(--theme-success-500);
|
|||||||
|
|
||||||
@mixin shadow-lg-top {
|
@mixin shadow-lg-top {
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 -2px 20px 7px rgba(0, 2, 4, 0.1),
|
0 -20px 35px -10px rgba(0, 2, 4, 0.2),
|
||||||
0 6px 4px -4px rgba(0, 2, 4, 0.02);
|
0 -6px 4px -4px rgba(0, 2, 4, 0.02);
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin shadow {
|
@mixin shadow {
|
||||||
|
|||||||
@@ -11,14 +11,13 @@
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|
||||||
.popup__scroll,
|
.popup__hide-scrollbar,
|
||||||
.popup__wrap {
|
.popup__scroll-container {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup__scroll {
|
.popup__scroll-content {
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
padding-right: base(0.5);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +34,7 @@
|
|||||||
@extend %btn-reset;
|
@extend %btn-reset;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 0 0 0 base(0.25);
|
margin: 0;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
@@ -48,7 +47,29 @@
|
|||||||
max-width: base(8);
|
max-width: base(8);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
margin-right: base(0.25);
|
border-radius: 2px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--popup-button-highlight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-link__popup {
|
||||||
|
display: flex;
|
||||||
|
gap: calc(var(--base) * 0.25);
|
||||||
|
button {
|
||||||
|
&:hover {
|
||||||
|
.btn__icon {
|
||||||
|
background-color: var(--popup-button-highlight);
|
||||||
|
.fill {
|
||||||
|
fill: var(--theme-text);
|
||||||
|
}
|
||||||
|
.stroke {
|
||||||
|
stroke: var(--theme-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ export const LinkElement: React.FC<{
|
|||||||
href={`${config.routes.admin}/collections/${element.doc.relationTo}/${element.doc.value}`}
|
href={`${config.routes.admin}/collections/${element.doc.relationTo}/${element.doc.value}`}
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
title={`${config.routes.admin}/collections/${element.doc.relationTo}/${element.doc.value}`}
|
||||||
>
|
>
|
||||||
label
|
label
|
||||||
</a>
|
</a>
|
||||||
@@ -170,6 +171,7 @@ export const LinkElement: React.FC<{
|
|||||||
href={element.url}
|
href={element.url}
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
title={element.url}
|
||||||
>
|
>
|
||||||
{element.url}
|
{element.url}
|
||||||
</a>
|
</a>
|
||||||
@@ -200,7 +202,7 @@ export const LinkElement: React.FC<{
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
size="small"
|
size="fit-content"
|
||||||
verticalAlign="bottom"
|
verticalAlign="bottom"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -321,13 +321,13 @@ describe('fields', () => {
|
|||||||
const firstBlockSelector = blocksDrawer
|
const firstBlockSelector = blocksDrawer
|
||||||
.locator('.blocks-drawer__blocks .blocks-drawer__block')
|
.locator('.blocks-drawer__blocks .blocks-drawer__block')
|
||||||
.first()
|
.first()
|
||||||
await expect(firstBlockSelector).toContainText('Text')
|
await expect(firstBlockSelector).toContainText('Content')
|
||||||
await firstBlockSelector.click()
|
await firstBlockSelector.click()
|
||||||
|
|
||||||
// ensure the block was appended to the rows
|
// ensure the block was appended to the rows
|
||||||
const addedRow = page.locator('#field-blocks .blocks-field__row').last()
|
const addedRow = page.locator('#field-blocks .blocks-field__row').last()
|
||||||
await expect(addedRow).toBeVisible()
|
await expect(addedRow).toBeVisible()
|
||||||
await expect(addedRow.locator('.blocks-field__block-pill-text')).toContainText('Text')
|
await expect(addedRow.locator('.blocks-field__block-pill-content')).toContainText('Content')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should open blocks drawer from block row and add below', async () => {
|
test('should open blocks drawer from block row and add below', async () => {
|
||||||
@@ -348,13 +348,13 @@ describe('fields', () => {
|
|||||||
const firstBlockSelector = blocksDrawer
|
const firstBlockSelector = blocksDrawer
|
||||||
.locator('.blocks-drawer__blocks .blocks-drawer__block')
|
.locator('.blocks-drawer__blocks .blocks-drawer__block')
|
||||||
.first()
|
.first()
|
||||||
await expect(firstBlockSelector).toContainText('Text')
|
await expect(firstBlockSelector).toContainText('Content')
|
||||||
await firstBlockSelector.click()
|
await firstBlockSelector.click()
|
||||||
|
|
||||||
// ensure the block was inserted beneath the first in the rows
|
// ensure the block was inserted beneath the first in the rows
|
||||||
const addedRow = page.locator('#field-blocks #blocks-row-1')
|
const addedRow = page.locator('#field-blocks #blocks-row-1')
|
||||||
await expect(addedRow).toBeVisible()
|
await expect(addedRow).toBeVisible()
|
||||||
await expect(addedRow.locator('.blocks-field__block-pill-text')).toContainText('Text') // went from `Number` to `Text`
|
await expect(addedRow.locator('.blocks-field__block-pill-content')).toContainText('Content') // went from `Number` to `Content`
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should use i18n block labels', async () => {
|
test('should use i18n block labels', async () => {
|
||||||
@@ -488,18 +488,16 @@ describe('fields', () => {
|
|||||||
|
|
||||||
await page.locator('#potentiallyEmptyArray-row-1 .array-actions__button').click()
|
await page.locator('#potentiallyEmptyArray-row-1 .array-actions__button').click()
|
||||||
await page
|
await page
|
||||||
.locator('#potentiallyEmptyArray-row-1 .popup__scroll .array-actions__remove')
|
.locator('#potentiallyEmptyArray-row-1 .popup__scroll-container .array-actions__remove')
|
||||||
.click()
|
.click()
|
||||||
await page.locator('#potentiallyEmptyArray-row-0 .array-actions__button').click()
|
await page.locator('#potentiallyEmptyArray-row-0 .array-actions__button').click()
|
||||||
await page
|
await page
|
||||||
.locator('#potentiallyEmptyArray-row-0 .popup__scroll .array-actions__remove')
|
.locator('#potentiallyEmptyArray-row-0 .popup__scroll-container .array-actions__remove')
|
||||||
.click()
|
.click()
|
||||||
|
|
||||||
const rows = await page.locator(
|
const rows = page.locator('#field-potentiallyEmptyArray > .array-field__draggable-rows')
|
||||||
'#field-potentiallyEmptyArray > .array-field__draggable-rows',
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(rows).not.toBeVisible()
|
await expect(rows).toBeHidden()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should remove existing row', async () => {
|
test('should remove existing row', async () => {
|
||||||
@@ -513,15 +511,13 @@ describe('fields', () => {
|
|||||||
await page.locator('#potentiallyEmptyArray-row-0 .array-actions__button').click()
|
await page.locator('#potentiallyEmptyArray-row-0 .array-actions__button').click()
|
||||||
await page
|
await page
|
||||||
.locator(
|
.locator(
|
||||||
'#potentiallyEmptyArray-row-0 .popup__scroll .array-actions__action.array-actions__remove',
|
'#potentiallyEmptyArray-row-0 .popup__scroll-container .array-actions__action.array-actions__remove',
|
||||||
)
|
)
|
||||||
.click()
|
.click()
|
||||||
|
|
||||||
const rows = await page.locator(
|
const rows = page.locator('#field-potentiallyEmptyArray > .array-field__draggable-rows')
|
||||||
'#field-potentiallyEmptyArray > .array-field__draggable-rows',
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(rows).not.toBeVisible()
|
await expect(rows).toBeHidden()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should add row after removing existing row', async () => {
|
test('should add row after removing existing row', async () => {
|
||||||
@@ -537,7 +533,7 @@ describe('fields', () => {
|
|||||||
await page.locator('#potentiallyEmptyArray-row-1 .array-actions__button').click()
|
await page.locator('#potentiallyEmptyArray-row-1 .array-actions__button').click()
|
||||||
await page
|
await page
|
||||||
.locator(
|
.locator(
|
||||||
'#potentiallyEmptyArray-row-1 .popup__scroll .array-actions__action.array-actions__remove',
|
'#potentiallyEmptyArray-row-1 .popup__scroll-container .array-actions__action.array-actions__remove',
|
||||||
)
|
)
|
||||||
.click()
|
.click()
|
||||||
await page.locator('#field-potentiallyEmptyArray > .array-field__add-row').click()
|
await page.locator('#field-potentiallyEmptyArray > .array-field__add-row').click()
|
||||||
|
|||||||
Reference in New Issue
Block a user