-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7d1f6fc
commit fd342e2
Showing
14 changed files
with
872 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@keystone-next/example-custom-field-view': major | ||
--- | ||
|
||
Initial version of the custom-field-view example |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# @keystone-next/example-custom-field-view |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
## Feature Example - Custom Field View | ||
|
||
This project demonstrates how to create a custom field view for a JSON field. This custom field view allows users to add, edit and remove navigation items from a list | ||
|
||
## Instructions | ||
|
||
To run this project, clone the Keystone repository locally then navigate to this directory and run: | ||
|
||
```shell | ||
yarn dev | ||
``` | ||
|
||
This will start the Admin UI at [localhost:3000](http://localhost:3000). | ||
|
||
You can use the Admin UI to create items in your database. | ||
|
||
You can also access a GraphQL Playground at [localhost:3000/api/graphql](http://localhost:3000/api/graphql), which allows you to directly run GraphQL queries and mutations. | ||
|
||
## Features | ||
|
||
In this project we add a new JSON field to the `Task` list: | ||
|
||
```typescript | ||
relatedLinks: json({ | ||
ui: { | ||
views: require.resolve('./fields/related-links/components.tsx'), | ||
createView: { fieldMode: 'edit' }, | ||
listView: { fieldMode: 'hidden' }, | ||
itemView: { fieldMode: 'edit' }, | ||
}, | ||
}), | ||
``` | ||
|
||
This field defines `ui.views` and provides a custom editor component which allows users to view and edit this field in a more intuitive way than just using raw JSON. | ||
|
||
The stored JSON data: | ||
|
||
```json | ||
{ | ||
"data": { | ||
"allTasks": [ | ||
{ | ||
"relatedLinks": [ | ||
{ | ||
"label": "Keystone website", | ||
"href": "keystonejs.com" | ||
}, | ||
{ | ||
"label": "Prisma website", | ||
"href": "prisma.io" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
|
||
will be rendered as. | ||
|
||
<div align="center"> | ||
<img src="./custom-field-ui.png" width="445"> | ||
</div> |
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
193 changes: 193 additions & 0 deletions
193
examples/custom-field-view/fields/related-links/components.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
import React from 'react'; | ||
import { FieldProps } from '@keystone-next/types'; | ||
import { css } from '@emotion/css'; | ||
import { Button } from '@keystone-ui/button'; | ||
import { FieldContainer, FieldLabel, TextInput } from '@keystone-ui/fields'; | ||
import { MinusCircleIcon, EditIcon } from '@keystone-ui/icons'; | ||
import { controller } from '@keystone-next/fields/types/json/views'; | ||
import { Fragment, useState } from 'react'; | ||
|
||
interface RelatedLink { | ||
label: string; | ||
href: string; | ||
} | ||
|
||
const styles = { | ||
form: { | ||
field: css` | ||
display: flex; | ||
flex-wrap: nowrap; | ||
align-items: center; | ||
width: 100%; | ||
margin: 1rem 0 0 0; | ||
`, | ||
label: css` | ||
width: 10%; | ||
`, | ||
input: css` | ||
width: 90%; | ||
`, | ||
button: css` | ||
margin: 1rem 0.5rem 0 0; | ||
`, | ||
}, | ||
list: { | ||
ul: css` | ||
list-style: none; | ||
margin: 1rem 0 0 0; | ||
padding: 0; | ||
`, | ||
li: css` | ||
display: flex; | ||
align-items: center; | ||
flex-wrap: nowrap; | ||
width: 100%; | ||
&:nth-of-type(2n) > div:nth-of-type(1) { | ||
background-color: white; | ||
} | ||
`, | ||
data: css` | ||
background-color: #eff3f6; | ||
padding: 0.5rem; | ||
flex: auto; | ||
display: flex; | ||
align-items: flex-start; | ||
flex-wrap: nowrap; | ||
`, | ||
dataLabel: css` | ||
width: 40%; | ||
`, | ||
dataHref: css` | ||
width: 60%; | ||
`, | ||
optionButton: css` | ||
margin: 0 0 0 0.5rem; | ||
`, | ||
}, | ||
}; | ||
|
||
export const Field = ({ field, value, onChange, autoFocus }: FieldProps<typeof controller>) => { | ||
const [labelValue, setLabelValue] = useState(''); | ||
const [hrefValue, setHrefValue] = useState(''); | ||
const [index, setIndex] = useState<number | null>(null); | ||
|
||
const relatedLinks: RelatedLink[] = value ? JSON.parse(value) : []; | ||
|
||
const onSubmitNewRelatedLink = () => { | ||
if (onChange) { | ||
const relatedLinksCopy = [...relatedLinks, { label: labelValue, href: hrefValue }]; | ||
onChange(JSON.stringify(relatedLinksCopy)); | ||
onCancelRelatedLink(); | ||
} | ||
}; | ||
|
||
const onDeleteRelatedLink = (index: number) => { | ||
if (onChange) { | ||
const relatedLinksCopy = [...relatedLinks]; | ||
relatedLinksCopy.splice(index, 1); | ||
onChange(JSON.stringify(relatedLinksCopy)); | ||
onCancelRelatedLink(); | ||
} | ||
}; | ||
|
||
const onEditRelatedLink = (index: number) => { | ||
if (onChange) { | ||
setIndex(index); | ||
setLabelValue(relatedLinks[index].label); | ||
setHrefValue(relatedLinks[index].href); | ||
} | ||
}; | ||
|
||
const onUpdateRelatedLink = () => { | ||
if (onChange && index !== null) { | ||
const relatedLinksCopy = [...relatedLinks]; | ||
relatedLinksCopy[index] = { label: labelValue, href: hrefValue }; | ||
onChange(JSON.stringify(relatedLinksCopy)); | ||
onCancelRelatedLink(); | ||
} | ||
}; | ||
|
||
const onCancelRelatedLink = () => { | ||
setIndex(null); | ||
setLabelValue(''); | ||
setHrefValue(''); | ||
}; | ||
|
||
return ( | ||
<FieldContainer> | ||
<FieldLabel>{field.label}</FieldLabel> | ||
{onChange && ( | ||
<Fragment> | ||
<div className={styles.form.field}> | ||
<FieldLabel className={styles.form.label}>Label</FieldLabel> | ||
<TextInput | ||
autoFocus={autoFocus} | ||
onChange={event => setLabelValue(event.target.value)} | ||
value={labelValue} | ||
className={styles.form.input} | ||
/> | ||
</div> | ||
<div className={styles.form.field}> | ||
<FieldLabel className={styles.form.label}>Href</FieldLabel> | ||
<TextInput | ||
autoFocus={autoFocus} | ||
onChange={event => setHrefValue(event.target.value)} | ||
value={hrefValue} | ||
className={styles.form.input} | ||
/> | ||
</div> | ||
|
||
{index !== null ? ( | ||
<Fragment> | ||
<Button onClick={onUpdateRelatedLink} className={styles.form.button}> | ||
Update | ||
</Button> | ||
<Button onClick={onCancelRelatedLink} className={styles.form.button}> | ||
Cancel | ||
</Button> | ||
</Fragment> | ||
) : ( | ||
<Button onClick={onSubmitNewRelatedLink} className={styles.form.button}> | ||
Add | ||
</Button> | ||
)} | ||
</Fragment> | ||
)} | ||
<ul className={styles.list.ul}> | ||
{relatedLinks.map((relatedLink: RelatedLink, i: number) => { | ||
return ( | ||
<li key={`related-link-${i}`} className={styles.list.li}> | ||
<div className={styles.list.data}> | ||
<div className={styles.list.dataLabel}>{relatedLink.label}</div> | ||
<div className={styles.list.dataHref}> | ||
<a href={relatedLink.href} target="_blank"> | ||
{relatedLink.href} | ||
</a> | ||
</div> | ||
</div> | ||
{onChange && ( | ||
<div> | ||
<Button | ||
size="small" | ||
onClick={() => onEditRelatedLink(i)} | ||
className={styles.list.optionButton} | ||
> | ||
<EditIcon size="small" color="blue" /> | ||
</Button> | ||
<Button size="small" className={styles.list.optionButton}> | ||
<MinusCircleIcon | ||
size="small" | ||
color="red" | ||
onClick={() => onDeleteRelatedLink(i)} | ||
/> | ||
</Button> | ||
</div> | ||
)} | ||
</li> | ||
); | ||
})} | ||
</ul> | ||
</FieldContainer> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { config } from '@keystone-next/keystone/schema'; | ||
import { lists } from './schema'; | ||
|
||
export default config({ | ||
db: { | ||
provider: 'sqlite', | ||
url: process.env.DATABASE_URL || 'file:./keystone-example.db', | ||
}, | ||
lists, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{ | ||
"name": "@keystone-next/example-custom-field-view", | ||
"version": "0.0.0", | ||
"private": true, | ||
"license": "MIT", | ||
"scripts": { | ||
"dev": "keystone-next dev", | ||
"start": "keystone-next start", | ||
"build": "keystone-next build" | ||
}, | ||
"dependencies": { | ||
"@emotion/css": "^11.1.3", | ||
"@keystone-next/fields": "^11.0.2", | ||
"@keystone-next/keystone": "^21.0.0", | ||
"@keystone-next/types": "^21.0.1", | ||
"@keystone-ui/button": "^5.0.0", | ||
"@keystone-ui/core": "^3.1.0", | ||
"@keystone-ui/fields": "^4.1.2", | ||
"@keystone-ui/icons": "^4.0.0", | ||
"react": "^17.0.2" | ||
}, | ||
"devDependencies": { | ||
"typescript": "^4.3.5" | ||
}, | ||
"engines": { | ||
"node": "^12.20 || >= 14.13" | ||
}, | ||
"repository": "https://github.com/keystonejs/keystone/tree/master/examples/custom-field-view" | ||
} |
Oops, something went wrong.
fd342e2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs: