Skip to content

Commit

Permalink
Create as page
Browse files Browse the repository at this point in the history
  • Loading branch information
emmatown committed Jun 3, 2022
1 parent 56b4567 commit 399795b
Show file tree
Hide file tree
Showing 10 changed files with 369 additions and 296 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"main": "dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-CreateItemPage.cjs.js",
"module": "dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-admin-ui-pages-CreateItemPage.esm.js"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/** @jsxRuntime classic */
/** @jsx jsx */

import { Box, jsx } from '@keystone-ui/core';
import { LoadingDots } from '@keystone-ui/loading';
import { Button } from '@keystone-ui/button';
import { useRouter } from 'next/router';
import { Fields } from '../../../../admin-ui/utils';
import { PageContainer } from '../../../../admin-ui/components/PageContainer';
import { useKeystone, useList } from '../../../../admin-ui';
import { GraphQLErrorNotice } from '../../../../../admin-ui/components/dist/keystone-6-core-admin-ui-components.cjs';
import { ListMeta } from '../../../../types';
import { useCreateItem } from '../../../../admin-ui/utils/useCreateItem';
import { BaseToolbar, ColumnLayout, ItemPageHeader } from '../ItemPage/common';

function CreatePageForm(props: { list: ListMeta }) {
const createItem = useCreateItem(props.list);
const router = useRouter();
return (
<Box paddingTop="xlarge">
{createItem.error && (
<GraphQLErrorNotice
networkError={createItem.error?.networkError}
errors={createItem.error?.graphQLErrors}
/>
)}

<form
onSubmit={async event => {
event.preventDefault();
const item = await createItem.create();
if (item) {
router.push(`/${props.list.path}/${item.id}`);
}
}}
>
<Fields {...createItem.props} />
<BaseToolbar>
<Button
isLoading={createItem.state === 'loading'}
type="submit"
weight="bold"
tone="active"
>
Create {props.list.singular}
</Button>
</BaseToolbar>
</form>
</Box>
);
}

type CreateItemPageProps = { listKey: string };

export const getCreateItemPage = (props: CreateItemPageProps) => () =>
<CreateItemPage {...props} />;

function CreateItemPage(props: CreateItemPageProps) {
const list = useList(props.listKey);
const { createViewFieldModes } = useKeystone();

return (
<PageContainer
title={`Create ${list.singular}`}
header={<ItemPageHeader list={list} label="Create" />}
>
<ColumnLayout>
<Box>
{createViewFieldModes.state === 'error' && (
<GraphQLErrorNotice
networkError={
createViewFieldModes.error instanceof Error ? createViewFieldModes.error : undefined
}
errors={
createViewFieldModes.error instanceof Error ? undefined : createViewFieldModes.error
}
/>
)}
{createViewFieldModes.state === 'loading' && <LoadingDots label="Loading create form" />}
<CreatePageForm list={list} />
</Box>
</ColumnLayout>
</PageContainer>
);
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
/** @jsxRuntime classic */
/** @jsx jsx */

import { ButtonHTMLAttributes, useMemo, useState } from 'react';
import { useMemo } from 'react';

import { Center, Inline, Heading, VisuallyHidden, jsx, useTheme } from '@keystone-ui/core';
import { PlusIcon } from '@keystone-ui/icons/icons/PlusIcon';
import { DrawerController } from '@keystone-ui/modals';
import { LoadingDots } from '@keystone-ui/loading';

import { makeDataGetter } from '../../../../admin-ui/utils';
import { CreateItemDrawer } from '../../../../admin-ui/components/CreateItemDrawer';
import { PageContainer, HEADER_HEIGHT } from '../../../../admin-ui/components/PageContainer';
import { gql, useQuery } from '../../../../admin-ui/apollo';
import { useKeystone, useList } from '../../../../admin-ui/context';
import { useRouter, Link } from '../../../../admin-ui/router';
import { Link, LinkProps } from '../../../../admin-ui/router';

type ListCardProps = {
listKey: string;
Expand All @@ -28,8 +26,6 @@ type ListCardProps = {
const ListCard = ({ listKey, count, hideCreate }: ListCardProps) => {
const { colors, palette, radii, spacing } = useTheme();
const list = useList(listKey);
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const router = useRouter();
return (
<div css={{ position: 'relative' }}>
<Link
Expand Down Expand Up @@ -67,36 +63,19 @@ const ListCard = ({ listKey, count, hideCreate }: ListCardProps) => {
)}
</Link>
{hideCreate === false && (
<CreateButton
title={`Create ${list.singular}`}
disabled={isCreateModalOpen}
onClick={() => {
setIsCreateModalOpen(true);
}}
>
<CreateButton title={`Create ${list.singular}`} href={`/${list.path}/create`}>
<PlusIcon size="large" />
<VisuallyHidden>Create {list.singular}</VisuallyHidden>
</CreateButton>
)}
<DrawerController isOpen={isCreateModalOpen}>
<CreateItemDrawer
listKey={list.key}
onCreate={({ id }) => {
router.push(`/${list.path}/${id}`);
}}
onClose={() => {
setIsCreateModalOpen(false);
}}
/>
</DrawerController>
</div>
);
};

const CreateButton = (props: ButtonHTMLAttributes<HTMLButtonElement>) => {
const CreateButton = (props: LinkProps) => {
const theme = useTheme();
return (
<button
<Link
css={{
alignItems: 'center',
backgroundColor: theme.palette.neutral400,
Expand All @@ -113,8 +92,8 @@ const CreateButton = (props: ButtonHTMLAttributes<HTMLButtonElement>) => {
top: theme.spacing.large,
transition: 'background-color 80ms linear',
width: 32,

'&:hover, &:focus': {
color: 'white',
backgroundColor: theme.tones.positive.fill[0],
},
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/** @jsxRuntime classic */
/** @jsx jsx */

import { Heading, jsx, useTheme } from '@keystone-ui/core';
import { ChevronRightIcon } from '@keystone-ui/icons/icons/ChevronRightIcon';
import { HTMLAttributes, ReactNode } from 'react';
import { Container } from '../../../../admin-ui/components/Container';
import { Link } from '../../../../admin-ui/router';
import { ListMeta } from '../../../../types';

export function ItemPageHeader(props: { list: ListMeta; label: string }) {
const { palette, spacing } = useTheme();

return (
<Container
css={{
alignItems: 'center',
display: 'flex',
flex: 1,
justifyContent: 'space-between',
}}
>
<div
css={{
alignItems: 'center',
display: 'flex',
flex: 1,
minWidth: 0,
}}
>
<Heading type="h3">
<Link href={`/${props.list.path}`} css={{ textDecoration: 'none' }}>
{props.list.label}
</Link>
</Heading>
<div
css={{
color: palette.neutral500,
marginLeft: spacing.xsmall,
marginRight: spacing.xsmall,
}}
>
<ChevronRightIcon />
</div>
<Heading
as="h1"
type="h3"
css={{
minWidth: 0,
maxWidth: '100%',
overflow: 'hidden',
flex: 1,
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}}
>
{props.label}
</Heading>
</div>
</Container>
);
}

export function ColumnLayout(props: HTMLAttributes<HTMLDivElement>) {
const { spacing } = useTheme();

return (
// this container must be relative to catch absolute children
// particularly the "expanded" document-field, which needs a height of 100%
<Container css={{ position: 'relative', height: '100%' }}>
<div
css={{
alignItems: 'start',
display: 'grid',
gap: spacing.xlarge,
gridTemplateColumns: `2fr 1fr`,
}}
{...props}
/>
</Container>
);
}

export function BaseToolbar(props: { children: ReactNode }) {
const { colors, spacing } = useTheme();

return (
<div
css={{
background: colors.background,
borderTop: `1px solid ${colors.border}`,
bottom: 0,
display: 'flex',
justifyContent: 'space-between',
marginTop: spacing.xlarge,
paddingBottom: spacing.xlarge,
paddingTop: spacing.xlarge,
position: 'sticky',
zIndex: 20,
}}
>
{props.children}
</div>
);
}
Loading

0 comments on commit 399795b

Please sign in to comment.