Skip to content

Commit

Permalink
added utility buttons to action card footer
Browse files Browse the repository at this point in the history
  • Loading branch information
NoahSaso committed Jan 23, 2025
1 parent 0687e37 commit a4a7128
Showing 3 changed files with 153 additions and 31 deletions.
3 changes: 3 additions & 0 deletions packages/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
@@ -62,6 +62,7 @@
"addImage": "Add image",
"addMember": "Add a member",
"addMembers": "Add members",
"addNewAction": "Add new {{action}} action",
"addNewOption": "Add a new option",
"addPayment": "Add payment",
"addRegisterAction": "Add register action",
@@ -189,6 +190,8 @@
"metadataUploaded": "Metadata uploaded",
"migrate": "Migrate",
"more": "More",
"moveDown": "Move down",
"moveUp": "Move up",
"new": "New",
"newCompensationCycle": "New Compensation Cycle",
"newPost": "New post",
139 changes: 110 additions & 29 deletions packages/stateless/components/actions/ActionCard.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,138 @@
import { Close } from '@mui/icons-material'
import {
Add,
ArrowDownward,
ArrowUpward,
Close,
CopyAll,
} from '@mui/icons-material'
import clsx from 'clsx'
import { ReactNode } from 'react'
import { useTranslation } from 'react-i18next'

import { Action } from '@dao-dao/types'

import { IconButton } from '../icon_buttons'
import { Tooltip } from '../tooltip'

export type ActionCardProps = {
action: Action<any>
onMoveUp?: () => void
onMoveDown?: () => void
onAddNew?: () => void
onDuplicate?: () => void
onRemove?: () => void
childrenContainerClassName?: string
children: ReactNode | ReactNode[]
}

export const ActionCard = ({
action,
onMoveUp,
onMoveDown,
onAddNew,
onDuplicate,
onRemove,
childrenContainerClassName,
children,
}: ActionCardProps) => (
<div className="flex flex-col overflow-x-auto rounded-lg bg-background-tertiary">
<div className="primary-text flex flex-row items-center justify-between gap-8 border-b border-border-base p-3 xs:p-4 text-text-body">
<div className="flex flex-row items-center gap-2 pr-3 xs:pr-4">
<p className="text-xl">
<action.metadata.Icon />
</p>
}: ActionCardProps) => {
const { t } = useTranslation()

<p className="title-text">{action.metadata.label}</p>
const showRemove = !action?.metadata.programmaticOnly && !!onRemove
const showActions =
!action?.metadata.programmaticOnly &&
(onMoveUp || onMoveDown || onDuplicate || onAddNew)

return (
<div className="flex flex-col overflow-x-auto rounded-lg bg-background-tertiary">
<div className="primary-text flex flex-row items-center justify-between gap-8 p-3 xs:p-4 text-text-body">
<div className="flex flex-row items-center gap-2 pr-3 xs:pr-4">
<p className="text-xl">
<action.metadata.Icon />
</p>

<p className="title-text">{action.metadata.label}</p>
</div>

{showRemove && (
<div className="flex flex-row items-center gap-2">
<Tooltip title={t('button.remove')}>
<IconButton
Icon={Close}
onClick={onRemove}
size="sm"
variant="ghost"
/>
</Tooltip>
</div>
)}
</div>

{
// Don't allow removing programmatic actions.
onRemove && !action?.metadata.programmaticOnly && (
<IconButton
Icon={Close}
onClick={onRemove}
size="sm"
variant="ghost"
/>
)
}
</div>
<div
className={clsx(
'flex flex-col gap-4 p-3 xs:p-4 sm:p-6 border-y border-border-base',
childrenContainerClassName
)}
>
{children}
</div>

{showActions && (
<div className="flex flex-row items-center justify-end gap-2 p-3">
{onMoveUp && (
<Tooltip title={t('button.moveUp')}>
<IconButton
Icon={ArrowUpward}
className="hidden sm:block"
onClick={onMoveUp}
size="sm"
variant="ghost"
/>
</Tooltip>
)}

<div
className={clsx(
'flex flex-col gap-4 p-3 xs:p-4 sm:p-6',
childrenContainerClassName
{onMoveDown && (
<Tooltip title={t('button.moveDown')}>
<IconButton
Icon={ArrowDownward}
className="hidden sm:block"
onClick={onMoveDown}
size="sm"
variant="ghost"
/>
</Tooltip>
)}

{onAddNew && (
<Tooltip
title={t('button.addNewAction', {
action: action.metadata.label,
})}
>
<IconButton
Icon={Add}
onClick={onAddNew}
size="sm"
variant="ghost"
/>
</Tooltip>
)}

{onDuplicate && (
<Tooltip title={t('button.duplicate')}>
<IconButton
Icon={CopyAll}
className="hidden sm:block"
onClick={onDuplicate}
size="sm"
variant="ghost"
/>
</Tooltip>
)}
</div>
)}
>
{children}
</div>
</div>
)
)
}

export const ActionCardLoader = () => (
<div className="flex animate-pulse flex-col rounded-lg bg-background-tertiary">
42 changes: 40 additions & 2 deletions packages/stateless/components/actions/ActionsEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import clsx from 'clsx'
import cloneDeep from 'lodash.clonedeep'
import { nanoid } from 'nanoid'
import {
ComponentType,
MutableRefObject,
useCallback,
useEffect,
useRef,
useState,
} from 'react'
@@ -60,10 +62,24 @@ export const ActionsEditor = ({
// All actions from the form.
const actionData = watch(actionDataFieldName) || []

const { append, insert, remove } = useFieldArray({
const { append, insert, remove, move } = useFieldArray({
name: actionDataFieldName,
control,
})

// Remove actions with no valid action for the given key.
useEffect(() => {
actionData
.flatMap((action, index) =>
!(action.actionKey in actionMap) ? [index] : []
)
// Remove in descending order so indexes don't change.
.sort((a, b) => b - a)
.forEach((index) => remove(index))

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [actionData.length, actionMap, remove])

const addAction = useCallback(
async (data: ActionKeyAndDataNoId, insertIndex?: number) => {
const action = actionMap[data.actionKey]
@@ -121,6 +137,7 @@ export const ActionsEditor = ({
clearErrors={clearErrors}
idsSeenRef={idsSeenRef}
index={index}
moveTo={(to) => move(index, to)}
remove={remove}
scrollToNewActions={scrollToNewActions}
/>
@@ -166,6 +183,7 @@ type ActionEditorProps = {
}>
remove: (index: number) => void
addAction: Required<ActionComponentProps>['addAction']
moveTo: (index: number) => void
}

const ActionEditor = ({
@@ -180,6 +198,7 @@ const ActionEditor = ({
clearErrors,
remove,
addAction,
moveTo,
}: ActionEditorProps) => {
const { _id, actionKey, data } = actionData[index]
const action = actionMap[actionKey]
@@ -204,7 +223,26 @@ const ActionEditor = ({

return (
<div className="relative" id={`A${index + 1}`}>
<ActionCard action={action} onRemove={onRemove}>
<ActionCard
action={action}
onAddNew={
action.ready
? () =>
addAction(
{ actionKey, data: cloneDeep(action.defaults) },
index + 1
)
: undefined
}
onDuplicate={() =>
addAction({ actionKey, data: cloneDeep(data) }, index + 1)
}
onMoveDown={
index < actionData.length - 1 ? () => moveTo(index + 1) : undefined
}
onMoveUp={index > 0 ? () => moveTo(index - 1) : undefined}
onRemove={onRemove}
>
<div
className="animate-fade-in flex min-w-0 grow flex-col gap-4"
ref={(node) => {

0 comments on commit a4a7128

Please sign in to comment.