feat: add secondary actions to publish button
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user