feat: add secondary actions to publish button

This commit is contained in:
Jessica Chowdhury
2023-12-01 17:25:45 +00:00
parent bea79feaea
commit 5caf100a13
7 changed files with 162 additions and 22 deletions

View File

@@ -24,6 +24,10 @@ a.btn {
@include color-svg(currentColor);
}
&__wrap {
display: flex;
}
&--has-tooltip {
position: relative;
}
@@ -57,6 +61,11 @@ a.btn {
padding: base(0.25) base(0.5);
}
&--has-secondary-actions {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&--style-primary {
background-color: var(--theme-elevation-800);
color: var(--theme-elevation-0);
@@ -214,4 +223,32 @@ a.btn {
outline: var(--accessibility-outline);
outline-offset: var(--accessibility-outline-offset);
}
&__chevron {
@include color-svg(currentColor);
border-radius: $style-radius-m;
border-left: 1px solid var(--theme-elevation-600);
border-top-left-radius: 0;
border-bottom-left-radius: 0;
cursor: pointer;
.icon {
vertical-align: middle;
transition: all 0.2s ease-in-out;
& path {
stroke-width: 1px;
}
}
&--open {
.icon {
transform: rotate(180deg);
}
}
&:focus:not(:focus-visible) {
box-shadow: none;
}
}
}

View File

@@ -4,11 +4,14 @@ import { Link } from 'react-router-dom'
import type { Props } from './types'
import chevron from '../../icons/Chevron'
import Chevron from '../../icons/Chevron'
import edit from '../../icons/Edit'
import linkIcon from '../../icons/Link'
import plus from '../../icons/Plus'
import swap from '../../icons/Swap'
import x from '../../icons/X'
import Popup from '../Popup'
import { ButtonGroup, Button as PopupButton } from '../Popup/PopupButtonList'
import Tooltip from '../Tooltip'
import './index.scss'
@@ -46,6 +49,41 @@ const ButtonContents = ({ children, icon, showTooltip, tooltip }) => {
)
}
const SecondaryActions = ({ className, secondaryActions }) => {
const [showSecondaryActions, setShowSecondaryActions] = React.useState<boolean>(false)
const multipleActions = secondaryActions.length > 1
return (
<Popup
button={
<div>
<Chevron />
</div>
}
buttonClassName={[
className && className,
`${baseClass}__chevron`,
showSecondaryActions && `${baseClass}__chevron--open`,
]
.filter(Boolean)
.join(' ')}
onToggleOpen={(active) => setShowSecondaryActions(active)}
>
<ButtonGroup>
{multipleActions ? (
secondaryActions.map((action, i) => (
<PopupButton key={i} onClick={action.onClick}>
{action.label}
</PopupButton>
))
) : (
<PopupButton onClick={secondaryActions.onClick}>{secondaryActions.label}</PopupButton>
)}
</ButtonGroup>
</Popup>
)
}
const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, Props>((props, ref) => {
const {
id,
@@ -61,6 +99,7 @@ const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, Props>((props,
newTab,
onClick,
round,
secondaryActions,
size = 'medium',
to,
tooltip,
@@ -82,6 +121,7 @@ const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, Props>((props,
size && `${baseClass}--size-${size}`,
iconPosition && `${baseClass}--icon-position-${iconPosition}`,
tooltip && `${baseClass}--has-tooltip`,
secondaryActions && `${baseClass}--has-secondary-actions`,
]
.filter(Boolean)
.join(' ')
@@ -106,36 +146,50 @@ const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, Props>((props,
type,
}
const ButtonContent = (
<ButtonContents icon={icon} showTooltip={showTooltip} tooltip={tooltip}>
{children}
</ButtonContents>
)
let buttonElement
switch (el) {
case 'link':
return (
buttonElement = (
<Link {...buttonProps} to={to || url}>
<ButtonContents icon={icon} showTooltip={showTooltip} tooltip={tooltip}>
{children}
</ButtonContents>
{ButtonContent}
</Link>
)
break
case 'anchor':
return (
buttonElement = (
<a {...buttonProps} href={url} ref={ref as React.LegacyRef<HTMLAnchorElement>}>
<ButtonContents icon={icon} showTooltip={showTooltip} tooltip={tooltip}>
{children}
</ButtonContents>
{ButtonContent}
</a>
)
break
default:
const Tag = el // eslint-disable-line no-case-declarations
return (
buttonElement = (
<Tag ref={ref} type="submit" {...buttonProps}>
<ButtonContents icon={icon} showTooltip={showTooltip} tooltip={tooltip}>
{children}
</ButtonContents>
{ButtonContent}
</Tag>
)
break
}
if (secondaryActions)
return (
<div className={`${baseClass}__wrap`}>
{buttonElement}
<SecondaryActions {...buttonProps} secondaryActions={secondaryActions} />
</div>
)
return buttonElement
})
export default Button

View File

@@ -1,6 +1,11 @@
import type { ElementType, MouseEvent } from 'react'
import type React from 'react'
type secondaryAction = {
label: string
onClick: (event: MouseEvent) => void
}
export type Props = {
'aria-label'?: string
buttonId?: string
@@ -16,6 +21,7 @@ export type Props = {
newTab?: boolean
onClick?: (event: MouseEvent) => void
round?: boolean
secondaryActions?: secondaryAction | secondaryAction[]
size?: 'medium' | 'small'
to?: string
tooltip?: string

View File

@@ -192,9 +192,19 @@
}
&__controls {
padding-left: var(--gutter-h);
padding: base(0.25) 0 base(0.25) var(--gutter-h);
overflow: auto;
& .popup__content {
position: fixed;
left: var(--gutter-h);
right: var(--gutter-h);
}
& .popup__caret {
display: none;
}
// do not show scrollbar because the parent container has a static height
// this container has a gradient overlay as visual indication of `overflow: scroll`
&::-webkit-scrollbar {

View File

@@ -36,7 +36,15 @@ export const PopupTrigger: React.FC<Props> = (props) => {
}
return (
<button className={classes} onClick={() => setActive(!active)} tabIndex={0} type="button">
<button
className={classes}
onClick={handleClick}
onKeyDown={(e) => {
if (e.key === 'Enter') handleClick()
}}
tabIndex={0}
type="button"
>
{button}
</button>
)

View File

@@ -1,6 +1,7 @@
import qs from 'qs'
import React, { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from 'react-toastify'
import { useForm, useFormModified } from '../../forms/Form/context'
import FormSubmit from '../../forms/Submit'
@@ -8,7 +9,6 @@ import { useConfig } from '../../utilities/Config'
import { useDocumentInfo } from '../../utilities/DocumentInfo'
import { useLocale } from '../../utilities/Locale'
import RenderCustomComponent from '../../utilities/RenderCustomComponent'
export type CustomPublishButtonProps = React.ComponentType<
DefaultPublishButtonProps & {
DefaultButton: React.ComponentType<DefaultPublishButtonProps>
@@ -30,8 +30,33 @@ const DefaultPublishButton: React.FC<DefaultPublishButtonProps> = ({
}) => {
if (!canPublish) return null
const testAction = () => {
console.log('test')
toast.success('Published to ___ locale(s)')
}
return (
<FormSubmit buttonId={id} disabled={disabled} onClick={publish} size="small" type="button">
<FormSubmit
buttonId={id}
disabled={disabled}
onClick={publish}
secondaryActions={[
{
label: 'Publish spanish only',
onClick: testAction,
},
{
label: 'Publish english only',
onClick: testAction,
},
{
label: 'Publish all locales',
onClick: testAction,
},
]}
size="small"
type="button"
>
{label}
</FormSubmit>
)

View File

@@ -31,11 +31,11 @@ const DraftPosts: CollectionConfig = {
readVersions: ({ req: { user } }) => Boolean(user),
},
admin: {
components: {
edit: {
PublishButton: CustomPublishButton,
},
},
// components: {
// edit: {
// PublishButton: CustomPublishButton,
// },
// },
defaultColumns: ['title', 'description', 'createdAt', '_status'],
preview: () => 'https://payloadcms.com',
useAsTitle: 'title',