Skip to content

Commit

Permalink
feat(tasks): start form edit work
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrobonamin committed Feb 27, 2024
1 parent f1dd4be commit ba1e200
Show file tree
Hide file tree
Showing 10 changed files with 246 additions and 122 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ export function TaskCreate(props: TaskCreateProps) {
: undefined,
}

return <TasksForm documentId={documentId} initialValue={initialValue} />
return <TasksForm documentId={documentId} initialValue={initialValue} mode="create" />
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ interface TaskEditProps {
export function TaskEdit(props: TaskEditProps) {
const {onDelete, selectedTask} = props

return <TasksForm documentId={selectedTask} />
return <TasksForm documentId={selectedTask} mode="edit" />
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import {

import {taskSchema} from './taskSchema'

export function AddOnWorkspaceProvider({children}: {children: React.ReactNode}) {
export function AddOnWorkspaceProvider({
children,
mode,
}: {
children: React.ReactNode
mode: 'edit' | 'create'
}) {
// Parent workspace source, we want to use the same project id
const source = useSource()
const basePath = undefined // TODO: Is basePath necessary here?
Expand All @@ -23,10 +29,10 @@ export function AddOnWorkspaceProvider({children}: {children: React.ReactNode})
// TODO: Get this host from the studio config.
apiHost: 'https://api.sanity.work',
schema: {
types: [taskSchema],
types: [taskSchema(mode)],
},
}),
[source.projectId],
[source.projectId, mode],
)

const {workspaces} = useMemo(
Expand Down
30 changes: 30 additions & 0 deletions packages/sanity/src/tasks/src/tasks/components/form/FormEdit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {type ObjectInputProps} from 'sanity'
import styled from 'styled-components'

import {type TaskDocument} from '../../types'
import {StatusSelector} from './StatusSelector'
import {Title} from './TitleField'

const FirstRow = styled.div`
display: flex;
margin-top: 7px;
`
export function FormEdit(props: ObjectInputProps<TaskDocument>) {
const statusField = props.schemaType.fields.find((f) => f.name === 'status')
if (!statusField) {
throw new Error('Status field not found')
}
return (
<div>
<Title onChange={props.onChange} value={props.value?.title} path={['title']} />
<FirstRow>
<StatusSelector
value={props.value?.status}
path={['status']}
onChange={props.onChange}
options={statusField.type.options.list}
/>
</FirstRow>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {CheckmarkCircleIcon, CheckmarkIcon, CircleIcon} from '@sanity/icons'
import {Menu} from '@sanity/ui'
import {type ForwardedRef, forwardRef, type ReactNode} from 'react'
import {
type FormPatch,
isString,
type PatchEvent,
type Path,
set,
type TitledListValue,
} from 'sanity'

import {Button, MenuButton, MenuItem} from '../../../../../ui-components'

// TODO: support customizing icons and options.
const OPTION_ICONS: Record<string, ReactNode> = {
closed: <CheckmarkCircleIcon />,
open: <CircleIcon />,
}

export const StatusMenuButton = forwardRef(function StatusMenuButton(
props: {value: string | undefined; options: TitledListValue<string>[]},
ref: ForwardedRef<HTMLButtonElement>,
) {
const {value, options} = props
const selectedOption = options.find((option) => option.value === value)
return (
<Button
{...props}
ref={ref}
tooltipProps={null}
icon={value && OPTION_ICONS[value]}
text={selectedOption?.title || value}
tone="default"
mode="ghost"
/>
)
})

interface StatusSelectorProps {
value: string | undefined
path: Path
options: TitledListValue<string>[]
onChange: (patch: FormPatch | PatchEvent | FormPatch[]) => void
}

export function StatusSelector(props: StatusSelectorProps) {
const {value, onChange, options, path} = props
return (
<MenuButton
button={<StatusMenuButton value={value} options={options} />}
id={`reference-menuButton`}
menu={
<Menu>
{options.map((option) => {
const isSelected = value === option.value
return (
<MenuItem
key={option.title}
icon={
isString(option.value) ? OPTION_ICONS[option.value] || CircleIcon : CircleIcon
}
text={option.title || option.value}
pressed={isSelected}
iconRight={isSelected && <CheckmarkIcon />}
// eslint-disable-next-line react/jsx-no-bind
onClick={() => onChange(set(option.value, path))}
/>
)
})}
</Menu>
}
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import styled from 'styled-components'

import {CommentsEnabledProvider} from '../../../../../structure/comments'
import {MentionUserProvider} from '../../context/mentionUser'
import {type TaskDocument} from '../../types'
import {type FormMode, type TaskDocument} from '../../types'
import {AddOnWorkspaceProvider} from './AddOnWorkspaceProvider'
import {useTasksFormBuilder} from './useTasksFormBuilder'

Expand All @@ -33,7 +33,6 @@ const TasksCreateFormInner = ({
documentType: 'tasks.task',
documentId,
initialValue,
actiob: 'create',
})

return (
Expand All @@ -57,9 +56,11 @@ const TasksCreateFormInner = ({
export function TasksForm({
documentId,
initialValue,
mode,
}: {
documentId: string
initialValue?: Partial<TaskDocument>
mode: FormMode
}) {
const currentUser = useCurrentUser()

Expand All @@ -68,7 +69,7 @@ export function TasksForm({
return (
// This provider needs to be mounted before the AddonWorkspaceProvider.
<MentionUserProvider>
<AddOnWorkspaceProvider>
<AddOnWorkspaceProvider mode={mode}>
<TasksCreateFormInner
documentId={documentId}
currentUser={currentUser}
Expand Down
25 changes: 18 additions & 7 deletions packages/sanity/src/tasks/src/tasks/components/form/TitleField.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// eslint-disable-next-line camelcase
import {getTheme_v2} from '@sanity/ui/theme'
import {type ChangeEvent, useCallback, useEffect, useRef} from 'react'
import {set, type StringFieldProps, unset} from 'sanity'
import {type FormPatch, type PatchEvent, type Path, set, type StringFieldProps, unset} from 'sanity'
import styled, {css} from 'styled-components'

const Root = styled.div`
Expand Down Expand Up @@ -51,9 +51,13 @@ const TitleInput = styled.textarea((props) => {
`
})

export function TitleField(props: StringFieldProps) {
const {value, inputProps} = props
const {onChange} = inputProps
export function Title(props: {
value: string | undefined
path?: Path
onChange: (patch: FormPatch | PatchEvent | FormPatch[]) => void
placeholder?: string
}) {
const {value, onChange, placeholder, path} = props
const ref = useRef<HTMLTextAreaElement | null>(null)

useEffect(() => {
Expand All @@ -66,9 +70,9 @@ export function TitleField(props: StringFieldProps) {
(event: ChangeEvent<HTMLTextAreaElement>) => {
const inputValue = event.currentTarget.value
if (!inputValue) onChange(unset())
return onChange(set(inputValue.replace(/\n/g, '')))
return onChange(set(inputValue.replace(/\n/g, ''), path))
},
[onChange],
[onChange, path],
)

return (
Expand All @@ -77,10 +81,17 @@ export function TitleField(props: StringFieldProps) {
ref={ref}
autoFocus={!value}
value={value}
placeholder={props.inputProps.schemaType.placeholder}
placeholder={placeholder}
onChange={handleChange}
rows={1}
/>
</Root>
)
}

export function TitleField(props: StringFieldProps) {
const {value, inputProps} = props
const {onChange, schemaType} = inputProps

return <Title value={value} onChange={onChange} placeholder={schemaType.placeholder} />
}
Loading

0 comments on commit ba1e200

Please sign in to comment.