From 6fa5866f994e3c214fe3d57e0bfb7e792ba56cc0 Mon Sep 17 00:00:00 2001 From: Jarrod Flesch <30633324+JarrodMFlesch@users.noreply.github.com> Date: Fri, 29 Sep 2023 09:05:02 -0400 Subject: [PATCH] feat: 2.0 popover style updates (#3404) --- .../elements/ArrayAction/index.scss | 17 -- .../components/elements/ArrayAction/index.tsx | 33 ++- .../elements/DocumentControls/index.scss | 90 ++----- .../elements/DocumentControls/index.tsx | 45 ++-- .../components/elements/Localizer/index.scss | 27 +-- .../components/elements/Localizer/index.tsx | 29 +-- .../components/elements/PerPage/index.scss | 6 - .../components/elements/PerPage/index.tsx | 67 +++--- .../elements/Popup/PopupButton/index.scss | 5 - .../elements/Popup/PopupButtonList/index.scss | 47 ++++ .../elements/Popup/PopupButtonList/index.tsx | 75 ++++++ .../elements/Popup/PopupTrigger/index.scss | 14 ++ .../{PopupButton => PopupTrigger}/index.tsx | 4 +- .../{PopupButton => PopupTrigger}/types.ts | 0 .../components/elements/Popup/index.scss | 225 ++++++------------ .../admin/components/elements/Popup/index.tsx | 44 ++-- .../admin/components/elements/Popup/types.ts | 4 +- .../Relationship/AddNew/index.scss | 25 +- .../field-types/Relationship/AddNew/index.tsx | 34 +-- packages/payload/src/admin/scss/vars.scss | 4 +- .../field/elements/link/Element/index.scss | 33 ++- .../src/field/elements/link/Element/index.tsx | 4 +- test/fields/e2e.spec.ts | 28 +-- 23 files changed, 400 insertions(+), 460 deletions(-) delete mode 100644 packages/payload/src/admin/components/elements/Popup/PopupButton/index.scss create mode 100644 packages/payload/src/admin/components/elements/Popup/PopupButtonList/index.scss create mode 100644 packages/payload/src/admin/components/elements/Popup/PopupButtonList/index.tsx create mode 100644 packages/payload/src/admin/components/elements/Popup/PopupTrigger/index.scss rename packages/payload/src/admin/components/elements/Popup/{PopupButton => PopupTrigger}/index.tsx (91%) rename packages/payload/src/admin/components/elements/Popup/{PopupButton => PopupTrigger}/types.ts (100%) diff --git a/packages/payload/src/admin/components/elements/ArrayAction/index.scss b/packages/payload/src/admin/components/elements/ArrayAction/index.scss index ef539d654..1efda12c7 100644 --- a/packages/payload/src/admin/components/elements/ArrayAction/index.scss +++ b/packages/payload/src/admin/components/elements/ArrayAction/index.scss @@ -11,16 +11,6 @@ } } - &.popup--active .array-actions__button { - background: var(--theme-elevation-0); - } - - &__button, - &__action { - @extend %btn-reset; - cursor: pointer; - } - &__actions { list-style: none; margin: 0; @@ -28,9 +18,6 @@ } &__action { - @extend %btn-reset; - display: block; - svg { position: relative; top: -1px; @@ -40,10 +27,6 @@ stroke-width: 1px; } } - - &:hover { - opacity: 0.7; - } } &__move-up { diff --git a/packages/payload/src/admin/components/elements/ArrayAction/index.tsx b/packages/payload/src/admin/components/elements/ArrayAction/index.tsx index 19af7631e..590848ea8 100644 --- a/packages/payload/src/admin/components/elements/ArrayAction/index.tsx +++ b/packages/payload/src/admin/components/elements/ArrayAction/index.tsx @@ -9,6 +9,7 @@ import More from '../../icons/More' import Plus from '../../icons/Plus' import X from '../../icons/X' import Popup from '../Popup' +import * as PopupList from '../Popup/PopupButtonList' import './index.scss' const baseClass = 'array-actions' @@ -31,73 +32,69 @@ export const ArrayAction: React.FC = ({ horizontalAlign="center" render={({ close }) => { return ( - + {index !== 0 && ( - + )} {index < rowCount - 1 && ( - + )} {!hasMaxRows && ( - - + )} - - + + ) }} + size="medium" /> ) } diff --git a/packages/payload/src/admin/components/elements/DocumentControls/index.scss b/packages/payload/src/admin/components/elements/DocumentControls/index.scss index 5fc0a102c..f7c8aafd4 100644 --- a/packages/payload/src/admin/components/elements/DocumentControls/index.scss +++ b/packages/payload/src/admin/components/elements/DocumentControls/index.scss @@ -73,13 +73,14 @@ } &__controls-wrapper { + --controls-gap: calc(var(--base) / 2); + --dot-button-width: calc(var(--base) * 2); display: flex; align-items: center; margin: 0; - // move to the right to account for the padding on the dots - // this will make sure the alignment is correct - // while still keeping a large button hitbox - transform: translate3d(var(--base), 0, 0); + gap: var(--controls-gap); + padding-right: calc(var(--controls-gap) + var(--dot-button-width)); + position: relative; } &__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 { - display: flex; - gap: 2px; - background-color: transparent; - padding: 0; + height: 100%; + margin: 0; - > div { + .btn__label { + height: 100%; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + gap: 2px; + } + + > span > span > div { width: 3px; height: 3px; border-radius: 100%; @@ -122,49 +116,15 @@ } } - &__popup-actions { - list-style: none; - padding: 0; - margin: 0; - display: flex; - flex-direction: column; - gap: calc(var(--base) / 4); + &__popup { + position: absolute; + right: 0; + top: 0; + bottom: 0; + width: var(--dot-button-width); - a { - text-decoration: none; - } - - li { - position: relative; - - &::before { - content: ''; - display: block; - position: absolute; - height: 100%; - width: 100%; - border-radius: 1px; - background: var(--theme-elevation-100); - width: calc(100% + (var(--base) / 2)); - left: calc(var(--base) / -4); - top: 0; - opacity: 0; - transition: opacity 50ms linear; - } - - &:hover::before { - opacity: 1; - } - - > * { - position: relative; - width: 100%; - height: 100%; - text-align: left; - font-size: inherit; - line-height: inherit; - font-family: inherit; - } + .popup__trigger-wrap { + height: 100%; } } diff --git a/packages/payload/src/admin/components/elements/DocumentControls/index.tsx b/packages/payload/src/admin/components/elements/DocumentControls/index.tsx index 3c6fcd3b8..0e27942e9 100644 --- a/packages/payload/src/admin/components/elements/DocumentControls/index.tsx +++ b/packages/payload/src/admin/components/elements/DocumentControls/index.tsx @@ -1,6 +1,5 @@ import React, { Fragment } from 'react' import { useTranslation } from 'react-i18next' -import { Link } from 'react-router-dom' import type { CollectionPermission, GlobalPermission } from '../../../../auth' import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from '../../../../exports/types' @@ -9,10 +8,12 @@ import { formatDate } from '../../../utilities/formatDate' import { useConfig } from '../../utilities/Config' import { useDocumentInfo } from '../../utilities/DocumentInfo' import Autosave from '../Autosave' +import Button from '../Button' import DeleteDocument from '../DeleteDocument' import DuplicateDocument from '../DuplicateDocument' import { Gutter } from '../Gutter' import Popup from '../Popup' +import * as PopupList from '../Popup/PopupButtonList' import PreviewButton from '../PreviewButton' import { Publish } from '../Publish' import { Save } from '../Save' @@ -30,9 +31,9 @@ export const DocumentControls: React.FC<{ global?: SanitizedGlobalConfig hasSavePermission?: boolean id?: string + isAccountView?: boolean isEditing?: boolean permissions?: CollectionPermission | GlobalPermission - isAccountView?: boolean }> = (props) => { const { id, @@ -41,9 +42,9 @@ export const DocumentControls: React.FC<{ disableActions, global, hasSavePermission, + isAccountView, isEditing, permissions, - isAccountView, } = props const { publishedDoc } = useDocumentInfo() @@ -73,6 +74,8 @@ export const DocumentControls: React.FC<{ !global?.versions?.drafts?.autosave } + const showDotMenu = Boolean(collection && !disableActions) + return (
@@ -187,49 +190,47 @@ export const DocumentControls: React.FC<{ )}
- {Boolean(collection && !disableActions) && ( + {showDotMenu && ( + } - caret={false} className={`${baseClass}__popup`} - horizontalAlign="center" + horizontalAlign="right" size="large" verticalAlign="bottom" > -
    + {'create' in permissions && permissions?.create?.permission && ( -
  • - - {t('createNew')} - -
  • + + {t('createNew')} + + {!collection?.admin?.disableDuplicate && isEditing && ( -
  • + -
  • + )}
    )} {'delete' in permissions && permissions?.delete?.permission && id && ( -
  • + -
  • + )} -
+
)} diff --git a/packages/payload/src/admin/components/elements/Localizer/index.scss b/packages/payload/src/admin/components/elements/Localizer/index.scss index c5b370ab8..5736d81c8 100644 --- a/packages/payload/src/admin/components/elements/Localizer/index.scss +++ b/packages/payload/src/admin/components/elements/Localizer/index.scss @@ -18,7 +18,7 @@ button { color: currentColor; - padding: base(0.25) 0; + padding: 0; font-size: 1rem; line-height: base(1); background: transparent; @@ -39,33 +39,10 @@ &__button { white-space: nowrap; + display: flex; } span { 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)); - } - } } diff --git a/packages/payload/src/admin/components/elements/Localizer/index.tsx b/packages/payload/src/admin/components/elements/Localizer/index.tsx index db8dde9ea..23b5626b5 100644 --- a/packages/payload/src/admin/components/elements/Localizer/index.tsx +++ b/packages/payload/src/admin/components/elements/Localizer/index.tsx @@ -1,13 +1,13 @@ import qs from 'qs' import React from 'react' import { useTranslation } from 'react-i18next' -import { Link } from 'react-router-dom' import { Chevron } from '../..' import { useConfig } from '../../utilities/Config' import { useLocale } from '../../utilities/Locale' import { useSearchParams } from '../../utilities/SearchParams' import Popup from '../Popup' +import * as PopupList from '../Popup/PopupButtonList' import './index.scss' const baseClass = 'localizer' @@ -37,20 +37,10 @@ const Localizer: React.FC<{ } - caret={false} - horizontalAlign="left" + horizontalAlign="right" render={({ close }) => ( -
    + {locales.map((localeOption) => { - const baseLocaleClass = `${baseClass}__locale` - - const localeClasses = [ - baseLocaleClass, - locale.code === localeOption.code && `${baseLocaleClass}--active`, - ] - .filter(Boolean) - .join('') - const newParams = { ...searchParams, locale: localeOption.code, @@ -60,20 +50,19 @@ const Localizer: React.FC<{ if (localeOption.code !== locale.code) { return ( -
  • - - {localeOption.label} - {localeOption.label !== localeOption.code && ` (${localeOption.code})`} - -
  • + + {localeOption.label} + {localeOption.label !== localeOption.code && ` (${localeOption.code})`} + ) } return null })} -
+ )} showScrollbar + size="large" /> ) diff --git a/packages/payload/src/admin/components/elements/PerPage/index.scss b/packages/payload/src/admin/components/elements/PerPage/index.scss index 11b595aa4..5d76a482e 100644 --- a/packages/payload/src/admin/components/elements/PerPage/index.scss +++ b/packages/payload/src/admin/components/elements/PerPage/index.scss @@ -7,12 +7,6 @@ margin: 0; } - .popup-button--default { - @extend %btn-reset; - position: relative; - cursor: pointer; - } - &__button { @extend %btn-reset; cursor: pointer; diff --git a/packages/payload/src/admin/components/elements/PerPage/index.tsx b/packages/payload/src/admin/components/elements/PerPage/index.tsx index 7c0fbd4f0..7197b1e66 100644 --- a/packages/payload/src/admin/components/elements/PerPage/index.tsx +++ b/packages/payload/src/admin/components/elements/PerPage/index.tsx @@ -7,6 +7,7 @@ import { defaults } from '../../../../collections/config/defaults' import Chevron from '../../icons/Chevron' import { useSearchParams } from '../../utilities/SearchParams' import Popup from '../Popup' +import * as PopupList from '../Popup/PopupButtonList' import './index.scss' const baseClass = 'per-page' @@ -43,43 +44,37 @@ const PerPage: React.FC = ({ } horizontalAlign="right" render={({ close }) => ( -
-
    - {limits.map((limitNumber, i) => ( -
  • - -
  • - ))} -
-
+ + {limits.map((limitNumber, i) => ( + { + close() + if (handleChange) handleChange(limitNumber) + if (modifySearchParams) { + history.replace({ + search: qs.stringify( + { + ...params, + limit: limitNumber, + page: resetPage ? 1 : params.page, + }, + { addQueryPrefix: true }, + ), + }) + } + }} + > + {limitNumber === Number(limit) && } + {limitNumber} + + ))} + )} + size="small" /> ) diff --git a/packages/payload/src/admin/components/elements/Popup/PopupButton/index.scss b/packages/payload/src/admin/components/elements/Popup/PopupButton/index.scss deleted file mode 100644 index 27c47e294..000000000 --- a/packages/payload/src/admin/components/elements/Popup/PopupButton/index.scss +++ /dev/null @@ -1,5 +0,0 @@ -@import '../../../../scss/styles.scss'; - -.popup-button { - display: inline-flex; -} diff --git a/packages/payload/src/admin/components/elements/Popup/PopupButtonList/index.scss b/packages/payload/src/admin/components/elements/Popup/PopupButtonList/index.scss new file mode 100644 index 000000000..7ff5ee278 --- /dev/null +++ b/packages/payload/src/admin/components/elements/Popup/PopupButtonList/index.scss @@ -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); + } + } +} diff --git a/packages/payload/src/admin/components/elements/Popup/PopupButtonList/index.tsx b/packages/payload/src/admin/components/elements/Popup/PopupButtonList/index.tsx new file mode 100644 index 000000000..f4f11a0f1 --- /dev/null +++ b/packages/payload/src/admin/components/elements/Popup/PopupButtonList/index.tsx @@ -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
{children}
+} + +type MenuButtonProps = { + children: React.ReactNode + className?: string + id?: string + onClick?: () => void + to?: LinkProps['to'] +} +export const Button: React.FC = ({ id, children, className, onClick, to }) => { + const classes = [`${baseClass}__button`, className].filter(Boolean).join(' ') + + if (to) { + return ( + { + if (onClick) { + onClick() + } + }} + to={to} + > + {children} + + ) + } + + if (onClick) { + return ( + + ) + } + + return ( +
+ {children} +
+ ) +} diff --git a/packages/payload/src/admin/components/elements/Popup/PopupTrigger/index.scss b/packages/payload/src/admin/components/elements/Popup/PopupTrigger/index.scss new file mode 100644 index 000000000..dbe5ab2e8 --- /dev/null +++ b/packages/payload/src/admin/components/elements/Popup/PopupTrigger/index.scss @@ -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; +} diff --git a/packages/payload/src/admin/components/elements/Popup/PopupButton/index.tsx b/packages/payload/src/admin/components/elements/Popup/PopupTrigger/index.tsx similarity index 91% rename from packages/payload/src/admin/components/elements/Popup/PopupButton/index.tsx rename to packages/payload/src/admin/components/elements/Popup/PopupTrigger/index.tsx index b51a87983..6266538b7 100644 --- a/packages/payload/src/admin/components/elements/Popup/PopupButton/index.tsx +++ b/packages/payload/src/admin/components/elements/Popup/PopupTrigger/index.tsx @@ -6,7 +6,7 @@ import './index.scss' const baseClass = 'popup-button' -const PopupButton: React.FC = (props) => { +export const PopupTrigger: React.FC = (props) => { const { active, button, buttonType, className, setActive } = props const classes = [baseClass, className, `${baseClass}--${buttonType}`].filter(Boolean).join(' ') @@ -41,5 +41,3 @@ const PopupButton: React.FC = (props) => { ) } - -export default PopupButton diff --git a/packages/payload/src/admin/components/elements/Popup/PopupButton/types.ts b/packages/payload/src/admin/components/elements/Popup/PopupTrigger/types.ts similarity index 100% rename from packages/payload/src/admin/components/elements/Popup/PopupButton/types.ts rename to packages/payload/src/admin/components/elements/Popup/PopupTrigger/types.ts diff --git a/packages/payload/src/admin/components/elements/Popup/index.scss b/packages/payload/src/admin/components/elements/Popup/index.scss index e4031b793..31fd2f6a0 100644 --- a/packages/payload/src/admin/components/elements/Popup/index.scss +++ b/packages/payload/src/admin/components/elements/Popup/index.scss @@ -1,43 +1,48 @@ @import '../../../scss/styles.scss'; .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; &__content { position: absolute; - background: var(--theme-input-bg); + background: var(--popup-bg); opacity: 0; visibility: hidden; pointer-events: none; z-index: var(--z-popup); max-width: calc(100vw - #{$baseline}); - - &--caret { - &:after { - content: ' '; - position: absolute; - top: calc(100% - 1px); - border: 12px solid transparent; - border-top-color: var(--theme-input-bg); - } - } + color: var(--popup-text); + border-radius: 4px; + padding-left: var(--popup-padding); + padding-right: var(--popup-padding); + min-width: var(--popup-width, auto); } - &__wrap { + &__hide-scrollbar { overflow: hidden; } - .popup__scroll { - padding: $baseline; + &__scroll-container { overflow-y: auto; white-space: nowrap; - padding-right: calc(var(--scrollbar-width) + #{$baseline}); 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 { - .popup__scroll { - padding-right: 0; + .popup__scroll-container, + .popup__scroll-content { width: 100%; } } @@ -52,63 +57,24 @@ //////////////////////////////// &--size-small { - .popup__scroll { - [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-width: 100px; .popup__content { @include shadow-m; } + } - &.popup--h-align-left { - .popup__content { - left: - base(0.5); - - &:after { - left: base(0.425); - } - } + &--size-medium { + --popup-width: 150px; + .popup__content { + @include shadow-lg; } } &--size-large { + --popup-width: 200px; .popup__content { @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 { - .popup__content { - left: - base(1.75); - - &:after { - left: base(1.75); - } + .popup__caret { + left: var(--popup-padding); } } @@ -129,26 +91,21 @@ .popup__content { left: 50%; transform: translateX(-50%); + } - &:after { - left: 50%; - transform: translateX(-50%); - } + .popup__caret { + left: 50%; + transform: translateX(-50%); } } &--h-align-right { .popup__content { - right: - base(1.75); - [dir='rtl'] & { - right: - base(0.75); - } - &:after { - right: base(1.75); - [dir='rtl'] & { - right: base(0.75); - } - } + right: 0; + } + + .popup__caret { + right: var(--popup-padding); } } @@ -156,46 +113,32 @@ // VERTICAL ALIGNMENT //////////////////////////////// + &__caret { + position: absolute; + border: var(--popup-caret-size) solid transparent; + } + &--v-align-top { .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 { .popup__content { @include shadow-lg-top; - top: calc(100% + #{base(0.5)}); - - &:after { - top: unset; - bottom: calc(100% - 1px); - border-top-color: transparent !important; - border-bottom-color: var(--theme-input-bg); - } + top: calc(100% + var(--popup-caret-size)); } - &.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); - } + .popup__caret { + bottom: calc(100% - 1px); + border-bottom-color: var(--popup-bg); } } @@ -212,69 +155,55 @@ } @include mid-break { - &__scroll, - &--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); - } - } - } + --popup-padding: calc(var(--base) * 0.25); &--h-align-center { .popup__content { left: 50%; transform: translateX(-0%); + } - &:after { - left: 50%; - transform: translateX(-0%); - } + .popup__caret { + left: 50%; + transform: translateX(-0%); } } &--h-align-right { .popup__content { - right: - base(0.5); + right: 0; + } - &:after { - right: base(0.5); - } + .popup__caret { + right: var(--popup-padding); } } &--force-h-align-left { .popup__content { - left: - base(0.5); + left: 0; right: unset; transform: unset; + } - &:after { - left: base(0.5); - right: unset; - transform: unset; - } + .popup__caret { + left: var(--popup-padding); + right: unset; + transform: unset; } } &--force-h-align-right { .popup__content { - right: - base(0.5); + right: 0; left: unset; transform: unset; + } - &:after { - right: base(0.5); - left: unset; - transform: unset; - } + .popup__caret { + right: var(--popup-padding); + left: unset; + transform: unset; } } } diff --git a/packages/payload/src/admin/components/elements/Popup/index.tsx b/packages/payload/src/admin/components/elements/Popup/index.tsx index 96bc8465d..4e5777589 100644 --- a/packages/payload/src/admin/components/elements/Popup/index.tsx +++ b/packages/payload/src/admin/components/elements/Popup/index.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react' import type { Props } from './types' import useIntersect from '../../../hooks/useIntersect' -import PopupButton from './PopupButton' +import { PopupTrigger } from './PopupTrigger' import './index.scss' const baseClass = 'popup' @@ -18,16 +18,14 @@ const Popup: React.FC = (props) => { caret = true, children, className, - color = 'light', forceOpen, horizontalAlign: horizontalAlignFromProps = 'left', initActive = false, onToggleOpen, - padding, render, showOnHover = false, showScrollbar = false, - size = 'small', + size = 'medium', verticalAlign: verticalAlignFromProps = 'top', } = props @@ -38,7 +36,6 @@ const Popup: React.FC = (props) => { threshold: 1, }) - const buttonRef = useRef(null) const contentRef = useRef(null) const [active, setActive] = useState(initActive) const [verticalAlign, setVerticalAlign] = useState(verticalAlignFromProps) @@ -57,8 +54,8 @@ const Popup: React.FC = (props) => { } = bounds let boundingTopPos = 100 - let boundingRightPos = window.innerWidth - let boundingBottomPos = window.innerHeight + let boundingRightPos = document.documentElement.clientWidth + let boundingBottomPos = document.documentElement.clientHeight let boundingLeftPos = 0 if (boundingRef?.current) { @@ -131,7 +128,6 @@ const Popup: React.FC = (props) => { baseClass, className, `${baseClass}--size-${size}`, - `${baseClass}--color-${color}`, `${baseClass}--v-align-${verticalAlign}`, `${baseClass}--h-align-${horizontalAlign}`, active && `${baseClass}--active`, @@ -142,39 +138,35 @@ const Popup: React.FC = (props) => { return (
-
+
{showOnHover ? (
setActive(true)} onMouseLeave={() => setActive(false)} > -
) : ( - + )}
-
-
-
- {render && render({ close: () => setActive(false) })} - {children && children} +
+
+
+
+ {render && render({ close: () => setActive(false) })} + {children && children} +
+ + {caret &&
}
) diff --git a/packages/payload/src/admin/components/elements/Popup/types.ts b/packages/payload/src/admin/components/elements/Popup/types.ts index 87827e5c9..f80e4eafd 100644 --- a/packages/payload/src/admin/components/elements/Popup/types.ts +++ b/packages/payload/src/admin/components/elements/Popup/types.ts @@ -9,15 +9,13 @@ export type Props = { caret?: boolean children?: React.ReactNode className?: string - color?: 'dark' | 'light' forceOpen?: boolean horizontalAlign?: 'center' | 'left' | 'right' initActive?: boolean onToggleOpen?: (active: boolean) => void - padding?: CSSProperties['padding'] render?: (any) => React.ReactNode showOnHover?: boolean showScrollbar?: boolean - size?: 'large' | 'small' | 'wide' + size?: 'fit-content' | 'large' | 'medium' | 'small' verticalAlign?: 'bottom' | 'top' } diff --git a/packages/payload/src/admin/components/forms/field-types/Relationship/AddNew/index.scss b/packages/payload/src/admin/components/forms/field-types/Relationship/AddNew/index.scss index 4d5b149fc..193ebd9be 100644 --- a/packages/payload/src/admin/components/forms/field-types/Relationship/AddNew/index.scss +++ b/packages/payload/src/admin/components/forms/field-types/Relationship/AddNew/index.scss @@ -4,7 +4,7 @@ display: flex; align-items: stretch; - .popup__wrapper { + .popup__trigger-wrap { display: flex; align-items: stretch; height: 100%; @@ -22,27 +22,4 @@ display: flex; 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; - } - } } diff --git a/packages/payload/src/admin/components/forms/field-types/Relationship/AddNew/index.tsx b/packages/payload/src/admin/components/forms/field-types/Relationship/AddNew/index.tsx index a34efa17b..1619704fc 100644 --- a/packages/payload/src/admin/components/forms/field-types/Relationship/AddNew/index.tsx +++ b/packages/payload/src/admin/components/forms/field-types/Relationship/AddNew/index.tsx @@ -2,6 +2,7 @@ import React, { Fragment, useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import type { SanitizedCollectionConfig } from '../../../../../../collections/config/types' +import type { EditViewProps } from '../../../../views/types' import type { Value } from '../types' import type { Props } from './types' @@ -9,13 +10,13 @@ import { getTranslation } from '../../../../../../utilities/getTranslation' import Button from '../../../../elements/Button' import { useDocumentDrawer } from '../../../../elements/DocumentDrawer' import Popup from '../../../../elements/Popup' +import * as PopupList from '../../../../elements/Popup/PopupButtonList' import Tooltip from '../../../../elements/Tooltip' import Plus from '../../../../icons/Plus' import { useAuth } from '../../../../utilities/Auth' import { useConfig } from '../../../../utilities/Config' import './index.scss' import { useRelatedCollections } from './useRelatedCollections' -import { EditViewProps } from '../../../../views/types' const baseClass = 'relationship-add-new' @@ -85,7 +86,7 @@ export const AddNewRelation: React.FC = ({ [relationTo, collectionConfig, dispatchOptions, i18n, hasMany, setValue, value, config], ) - const onPopopToggle = useCallback((state) => { + const onPopupToggle = useCallback((state) => { setPopupOpen(state) }, []) @@ -161,31 +162,30 @@ export const AddNewRelation: React.FC = ({ } buttonType="custom" horizontalAlign="center" - onToggleOpen={onPopopToggle} + onToggleOpen={onPopupToggle} render={({ close: closePopup }) => ( -
    + {relatedCollections.map((relatedCollection) => { if (permissions.collections[relatedCollection.slug].create.permission) { return ( -
  • - -
  • + { + closePopup() + setSelectedCollection(relatedCollection.slug) + }} + > + {getTranslation(relatedCollection.labels.singular, i18n)} + ) } return null })} -
+ )} + size="medium" /> {collectionConfig && permissions.collections[collectionConfig.slug].create.permission && ( diff --git a/packages/payload/src/admin/scss/vars.scss b/packages/payload/src/admin/scss/vars.scss index 041d975a6..9b7a6ab29 100644 --- a/packages/payload/src/admin/scss/vars.scss +++ b/packages/payload/src/admin/scss/vars.scss @@ -85,8 +85,8 @@ $focus-box-shadow: 0 0 0 $style-stroke-width-m var(--theme-success-500); @mixin shadow-lg-top { box-shadow: - 0 -2px 20px 7px rgba(0, 2, 4, 0.1), - 0 6px 4px -4px rgba(0, 2, 4, 0.02); + 0 -20px 35px -10px rgba(0, 2, 4, 0.2), + 0 -6px 4px -4px rgba(0, 2, 4, 0.02); } @mixin shadow { diff --git a/packages/richtext-slate/src/field/elements/link/Element/index.scss b/packages/richtext-slate/src/field/elements/link/Element/index.scss index 83df44107..0cb98f357 100644 --- a/packages/richtext-slate/src/field/elements/link/Element/index.scss +++ b/packages/richtext-slate/src/field/elements/link/Element/index.scss @@ -11,14 +11,13 @@ bottom: 0; left: 0; - .popup__scroll, - .popup__wrap { + .popup__hide-scrollbar, + .popup__scroll-container { overflow: visible; } - .popup__scroll { + .popup__scroll-content { white-space: pre; - padding-right: base(0.5); } } @@ -35,7 +34,7 @@ @extend %btn-reset; font-weight: 600; cursor: pointer; - margin: 0 0 0 base(0.25); + margin: 0; &:hover, &:focus-visible { @@ -48,7 +47,29 @@ max-width: base(8); overflow: hidden; 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); + } + } + } } } diff --git a/packages/richtext-slate/src/field/elements/link/Element/index.tsx b/packages/richtext-slate/src/field/elements/link/Element/index.tsx index 26be4d3d9..008f065dc 100644 --- a/packages/richtext-slate/src/field/elements/link/Element/index.tsx +++ b/packages/richtext-slate/src/field/elements/link/Element/index.tsx @@ -159,6 +159,7 @@ export const LinkElement: React.FC<{ href={`${config.routes.admin}/collections/${element.doc.relationTo}/${element.doc.value}`} rel="noreferrer" target="_blank" + title={`${config.routes.admin}/collections/${element.doc.relationTo}/${element.doc.value}`} > label @@ -170,6 +171,7 @@ export const LinkElement: React.FC<{ href={element.url} rel="noreferrer" target="_blank" + title={element.url} > {element.url} @@ -200,7 +202,7 @@ export const LinkElement: React.FC<{ />
)} - size="small" + size="fit-content" verticalAlign="bottom" /> diff --git a/test/fields/e2e.spec.ts b/test/fields/e2e.spec.ts index e661b4028..4299e1b22 100644 --- a/test/fields/e2e.spec.ts +++ b/test/fields/e2e.spec.ts @@ -321,13 +321,13 @@ describe('fields', () => { const firstBlockSelector = blocksDrawer .locator('.blocks-drawer__blocks .blocks-drawer__block') .first() - await expect(firstBlockSelector).toContainText('Text') + await expect(firstBlockSelector).toContainText('Content') await firstBlockSelector.click() // ensure the block was appended to the rows const addedRow = page.locator('#field-blocks .blocks-field__row').last() 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 () => { @@ -348,13 +348,13 @@ describe('fields', () => { const firstBlockSelector = blocksDrawer .locator('.blocks-drawer__blocks .blocks-drawer__block') .first() - await expect(firstBlockSelector).toContainText('Text') + await expect(firstBlockSelector).toContainText('Content') await firstBlockSelector.click() // ensure the block was inserted beneath the first in the rows const addedRow = page.locator('#field-blocks #blocks-row-1') 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 () => { @@ -488,18 +488,16 @@ describe('fields', () => { await page.locator('#potentiallyEmptyArray-row-1 .array-actions__button').click() await page - .locator('#potentiallyEmptyArray-row-1 .popup__scroll .array-actions__remove') + .locator('#potentiallyEmptyArray-row-1 .popup__scroll-container .array-actions__remove') .click() await page.locator('#potentiallyEmptyArray-row-0 .array-actions__button').click() await page - .locator('#potentiallyEmptyArray-row-0 .popup__scroll .array-actions__remove') + .locator('#potentiallyEmptyArray-row-0 .popup__scroll-container .array-actions__remove') .click() - const rows = await page.locator( - '#field-potentiallyEmptyArray > .array-field__draggable-rows', - ) + const rows = page.locator('#field-potentiallyEmptyArray > .array-field__draggable-rows') - expect(rows).not.toBeVisible() + await expect(rows).toBeHidden() }) 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 .popup__scroll .array-actions__action.array-actions__remove', + '#potentiallyEmptyArray-row-0 .popup__scroll-container .array-actions__action.array-actions__remove', ) .click() - const rows = await page.locator( - '#field-potentiallyEmptyArray > .array-field__draggable-rows', - ) + const rows = page.locator('#field-potentiallyEmptyArray > .array-field__draggable-rows') - expect(rows).not.toBeVisible() + await expect(rows).toBeHidden() }) 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 .popup__scroll .array-actions__action.array-actions__remove', + '#potentiallyEmptyArray-row-1 .popup__scroll-container .array-actions__action.array-actions__remove', ) .click() await page.locator('#field-potentiallyEmptyArray > .array-field__add-row').click()