Skip to content

Commit

Permalink
fix ID fields and field rendering generally
Browse files Browse the repository at this point in the history
  • Loading branch information
dcousens committed Dec 13, 2024
1 parent 2dc8740 commit 5d8b574
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 189 deletions.
5 changes: 5 additions & 0 deletions .changeset/maybe-item.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@keystone-6/core": major
---

Changes `item` type in `field.*View.field*` functions to `ListTypeInfo['item'] | null`
Original file line number Diff line number Diff line change
@@ -1,26 +1,87 @@
import React from 'react'
import React, {
useCallback,
useState,
} from 'react'
import copyToClipboard from 'clipboard-copy'

import { ActionButton } from '@keystar/ui/button'
import { Icon } from '@keystar/ui/icon'
import { clipboardIcon } from '@keystar/ui/icon/icons/clipboardIcon'
import { Grid, } from '@keystar/ui/layout'
import { TooltipTrigger, Tooltip } from '@keystar/ui/tooltip'
import { TextField } from '@keystar/ui/text-field'

import type {
FieldController,
FieldControllerConfig,
FieldProps,
IdFieldConfig,
} from '../../types'

export function Field () {
return null
const COPY_TOOLTIP_CONTENT = {
neutral: 'Copy ID',
positive: 'Copied to clipboard',
critical: 'Unable to copy',
}
type TooltipState = { isOpen?: boolean, tone: keyof typeof COPY_TOOLTIP_CONTENT }

export function Field ({
field,
value,
onChange,
autoFocus,
forceValidation,
}: FieldProps<typeof controller>) {
const [tooltipState, setTooltipState] = useState<TooltipState>({ tone:'neutral' })

const onCopy = useCallback(async () => {
try {
if (value) await copyToClipboard(value)
setTooltipState({ isOpen: true, tone: 'positive' })
} catch (err: any) {
setTooltipState({ isOpen: true, tone: 'critical' })
}

// close, then reset the tooltip state after a delay
setTimeout(() => {
setTooltipState(state => ({ ...state, isOpen: false }))
}, 2000)
setTimeout(() => {
setTooltipState({ isOpen: undefined, tone: 'neutral' })
}, 2300)
}, [value])

return (
<Grid gap="regular" columns="1fr auto" alignItems="end">
<TextField
label="Item ID"
value={value ?? ''}
isReadOnly
onFocus={({ target }) => {
if (target instanceof HTMLInputElement) target.select()
}}
/>
<TooltipTrigger isOpen={tooltipState.isOpen} placement='top end'>
<ActionButton aria-label="copy id" onPress={onCopy}>
<Icon src={clipboardIcon} />
</ActionButton>
<Tooltip tone={tooltipState.tone}>
{COPY_TOOLTIP_CONTENT[tooltipState.tone]}
</Tooltip>
</TooltipTrigger>
</Grid>
)
}

export function controller (
config: FieldControllerConfig<IdFieldConfig>
): FieldController<void, string> {
): FieldController<string | null, string> {
return {
path: config.path,
label: config.label,
description: config.description,
graphqlSelection: config.path,
defaultValue: undefined,
defaultValue: null,
deserialize: data => data[config.path],
serialize: value => ({ [config.path]: value }),
filter: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,16 @@ import React, {
useRef,
useState,
} from 'react'
import copyToClipboard from 'clipboard-copy'
import { useRouter } from 'next/router'

import { ActionButton, Button } from '@keystar/ui/button'
import { Button } from '@keystar/ui/button'
import { Icon } from '@keystar/ui/icon'
import { fileWarningIcon } from '@keystar/ui/icon/icons/fileWarningIcon'
import { clipboardIcon } from '@keystar/ui/icon/icons/clipboardIcon'
import { AlertDialog, DialogContainer, DialogTrigger } from '@keystar/ui/dialog'
import { Box, Grid, VStack } from '@keystar/ui/layout'
import { Box, VStack } from '@keystar/ui/layout'
import { ProgressCircle } from '@keystar/ui/progress'
import { SlotProvider } from '@keystar/ui/slots'
import { TextField } from '@keystar/ui/text-field'
import { toastQueue } from '@keystar/ui/toast'
import { TooltipTrigger, Tooltip } from '@keystar/ui/tooltip'
import { Heading, Text } from '@keystar/ui/typography'

import type { ListMeta } from '../../../../types'
Expand Down Expand Up @@ -86,7 +82,6 @@ function ItemForm ({
return deserializeItemToValue(list.fields, item)
})

console.log('ip', { item, value })
const invalidFields = useInvalidFields(list.fields, value)
const [forceValidation, setForceValidation] = useState(false)
const onSave = useEventCallback(async (e) => {
Expand Down Expand Up @@ -142,26 +137,26 @@ function ItemForm ({
]}
/>
<Fields
groups={list.groups}
view='itemView'
position="form"
fields={list.fields}
groups={list.groups}
forceValidation={forceValidation}
invalidFields={invalidFields}
position="form"
onChange={useCallback(value => setValue(value), [setValue])}
value={value}
/>
</VStack>

<StickySidebar>
<IdField itemId={itemId.toString()} />

<Box marginTop="xlarge">
<Fields
groups={list.groups}
view='itemView'
position="sidebar"
fields={list.fields}
groups={list.groups}
forceValidation={forceValidation}
invalidFields={invalidFields}
position="sidebar"
onChange={useCallback(value => setValue(value), [setValue])}
value={value}
/>
Expand Down Expand Up @@ -201,54 +196,6 @@ function ItemForm ({
)
}

const COPY_TOOLTIP_CONTENT = {
neutral: 'Copy ID',
positive: 'Copied to clipboard',
critical: 'Unable to copy',
}
type TooltipState = { isOpen?: boolean, tone: keyof typeof COPY_TOOLTIP_CONTENT }
function IdField ({ itemId }: { itemId: string }) {
const [tooltipState, setTooltipState] = useState<TooltipState>({ tone:'neutral' })

const onCopy = useCallback(async () => {
try {
await copyToClipboard(itemId)
setTooltipState({ isOpen: true, tone: 'positive' })
} catch (err: any) {
setTooltipState({ isOpen: true, tone: 'critical' })
}

// close, then reset the tooltip state after a delay
setTimeout(() => {
setTooltipState(state => ({ ...state, isOpen: false }))
}, 2000)
setTimeout(() => {
setTooltipState({ isOpen: undefined, tone: 'neutral' })
}, 2300)
}, [itemId])

return (
<Grid gap="regular" columns="1fr auto" alignItems="end">
<TextField
label="Item ID"
value={itemId}
isReadOnly
onFocus={({ target }) => {
if (target instanceof HTMLInputElement) target.select()
}}
/>
<TooltipTrigger isOpen={tooltipState.isOpen} placement='top end'>
<ActionButton aria-label="copy id" onPress={onCopy}>
<Icon src={clipboardIcon} />
</ActionButton>
<Tooltip tone={tooltipState.tone}>
{COPY_TOOLTIP_CONTENT[tooltipState.tone]}
</Tooltip>
</TooltipTrigger>
</Grid>
)
}

function DeleteButton ({
itemLabel,
itemId,
Expand Down
Loading

0 comments on commit 5d8b574

Please sign in to comment.