🐬
精神と時の部屋日記8 | aws amplify入門
aws amplifyにやっと取り組む
バージョンによって機能しないコマンドが多々あったため、aws amplifyのドキュメントと、Amplify UIと呼ばれるオープンソースのUIライブラリのドキュメントを主に用いて学んでいこうと考えています!
Amplify UI docs
Amplify UIとCreate React App
Reactのdocs
Amplify UIのdocs
基本的なReactアプリを作成。amplify-ui-demo
npx create-react-app amplify-ui-demo && cd amplify-ui-demo
インストールします
npm install @aws-amplify/ui-react aws-amplify
src/App.jsを開き下記のコードに置き換えます。
import { Button, Flex, Heading, Image, Text } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
function App() {
return (
<Flex
direction={{ base: 'column', large: 'row' }}
maxWidth="32rem"
padding="1rem"
width="100%"
>
<Image
alt="Abstract art"
height="21rem"
src="https://images.unsplash.com/photo-1500462918059-b1a0cb512f1d?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987"
width="100%"
/>
<Flex justifyContent="space-between" direction="column">
<Heading level={3}>Abstract art</Heading>
<Text>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat
sed cras ornare arcu dui. Duis aute irure dolor in reprehenderit in
voluptate velit esse.
</Text>
<Button
variation="primary"
onClick={() => alert('Added item to cart!')}
>
Add to Cart
</Button>
</Flex>
</Flex>
);
}
export default App;
Reactアプリを起動しましょう!yarnでも可
npm start!
このような画面が表示されます。
ちょっと難しいですが以下のコードも試してみましょう!
src配下にpaintings.jsファイルを作って以下のコードをコピーして貼り付けてください
export const PAINTINGS = [
{
title: 'Hallway',
artist: 'Efe Kurnaz',
src: 'https://images.unsplash.com/photo-1500462918059-b1a0cb512f1d?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat sed cras ornare arcu dui. Ac feugiat sed lectus vestibulum.',
price: '$899.99',
avgRating: 4.8,
reviews: 445,
inStock: true,
readyForPickup: true,
bestSeller: true,
isNew: false,
limitedSupply: false,
},
{
title: 'Fire and Ice',
artist: 'Pawel Czerwinski',
src: 'https://images.unsplash.com/photo-1604871000636-074fa5117945?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
description:
'Lorem ipsum dolor sit amet, ubique patrioque at qui, modo hinc ne duo, ad consul animal volumus est. Ea quo etiam deleniti, amet singulis in sed. Omnesque lobortis vis ex. Wisi latine splendide vis ei, libris commodo no has.',
price: '$699.99',
avgRating: 4.1,
reviews: 222,
inStock: true,
readyForPickup: true,
bestSeller: false,
isNew: false,
limitedSupply: false,
},
{
title: 'Orange, pink, yellow',
artist: 'Kseniya Lapteva',
src: 'https://images.unsplash.com/photo-1629196914375-f7e48f477b6d?ixlib=rb-1.2.1&raw_url=true&q=80&fm=jpg&crop=entropy&cs=tinysrgb&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1306',
description:
'Lorem ipsum dolor sit amet, cu porro vivendum ius. Ad mei sint homero, cum an soluta epicurei. At pri minimum corrumpit. Minim percipitur eu mei, erant habemus deserunt qui et.',
price: '$139.99',
avgRating: 3.5,
reviews: 142,
inStock: true,
readyForPickup: false,
bestSeller: false,
isNew: true,
limitedSupply: false,
},
{
title: 'Melted Purple',
artist: 'Maria Orlova',
src: 'https://images.unsplash.com/photo-1549490349-8643362247b5?ixlib=rb-1.2.1&raw_url=true&q=80&fm=jpg&crop=entropy&cs=tinysrgb&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
description:
'Lorem ipsum dolor sit amet, facer nemore ei sea, mea facilis eloquentiam at. Et modus pertinax tincidunt est. Propriae argumentum necessitatibus eos ad.',
price: '$499.99',
avgRating: 4.5,
reviews: 301,
inStock: true,
readyForPickup: true,
bestSeller: true,
isNew: false,
limitedSupply: false,
},
{
title: 'Experimental',
artist: 'Bruno Thethe',
src: 'https://images.unsplash.com/photo-1550275994-cdc89cd1948f?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
description:
'Lorem ipsum dolor sit amet, ea probo choro tollit pri, ad pro justo intellegam repudiandae, labores civibus eu quo. Cum latine instructior at, est no odio tibique epicuri.',
price: '$159.99',
avgRating: 3.1,
reviews: 56,
inStock: true,
readyForPickup: true,
bestSeller: false,
isNew: true,
limitedSupply: false,
},
{
title: 'Rainbow',
artist: 'Felix Spiske',
src: 'https://images.unsplash.com/photo-1543857778-c4a1a3e0b2eb?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1310',
description:
'Lorem ipsum dolor sit amet, sea tritani indoctum cu, facilis praesent at qui. Cu cetero veritus vel, et prima erant perfecto vix. Tollit delectus scaevola duo et, inermis sensibus voluptatum cu ius.',
price: '$799.99',
avgRating: 4.9,
reviews: 550,
inStock: true,
readyForPickup: false,
bestSeller: true,
isNew: false,
limitedSupply: true,
},
{
title: 'Fearless Hue',
artist: 'Radienta',
src: 'https://images.unsplash.com/photo-1579547621113-e4bb2a19bdd6?ixlib=rb-1.2.1&raw_url=true&q=80&fm=jpg&crop=entropy&cs=tinysrgb&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=939',
description:
'Lorem ipsum dolor sit amet, te labore lucilius pro, te his consul singulis, cu vel unum impedit complectitur. In usu erat dicta doctus, purto aeterno vis te. Facete deterruisset nec id. At omittam antiopam pri.',
price: '$249.99',
avgRating: 3.3,
reviews: 294,
inStock: true,
readyForPickup: true,
bestSeller: false,
isNew: false,
limitedSupply: false,
},
{
title: 'Liquid',
artist: 'Joel Filipe',
src: 'https://images.unsplash.com/photo-1485163819542-13adeb5e0068?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987',
description:
'Lorem ipsum dolor sit amet, cum cu meliore tacimates, vel no sale maiorum. His cu autem placerat. Homero urbanitas vituperata ad sit, ex mel convenire elaboraret.',
price: '$549.99',
avgRating: 4.5,
reviews: 440,
inStock: false,
readyForPickup: false,
bestSeller: false,
isNew: false,
limitedSupply: true,
},
];
そして先ほど扱ったApp.jsを下のコードで置き換えてください
import * as React from 'react';
import {
Alert,
Badge,
Button,
Card,
Collection,
Divider,
Flex,
Heading,
Image,
Rating,
SelectField,
StepperField,
SwitchField,
Text,
View,
} from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
import { PAINTINGS } from './paintings';
function App() {
const [currentPainting, setCurrentPainting] = React.useState(PAINTINGS[0]);
const [image, setImage] = React.useState(PAINTINGS[0].src);
const [frame, setFrame] = React.useState(true);
const [quantity, setQuantity] = React.useState(1);
const [size, setSize] = React.useState('');
const [error, setError] = React.useState(false);
const handleAddToCart = () => {
if (size === '') {
setError(true);
return;
}
alert(
`Added to cart!\n${quantity} ${size} "${currentPainting.title}" by ${
currentPainting.artist
} with ${frame ? 'a' : 'no'} frame`
);
};
return (
<View width="100%" maxWidth="50rem" padding={{ base: 0, large: '2rem' }}>
<Card variation="outlined">
<Flex
direction={{ base: 'column', large: 'row' }}
justifyContent="space-evenly"
>
<Flex direction="column" gap="5rem" alignItems="center">
<View width="15rem" height="19rem">
<Image
src={image}
alt={`${currentPainting.title} abstract painting`}
width="100%"
height="21rem"
border={frame ? '3px solid black' : ''}
/>
</View>
<Collection
type="grid"
items={PAINTINGS}
templateColumns="1fr 1fr 1fr 1fr"
templateRows="1fr 1fr"
width="14rem"
>
{(item, index) => (
<Flex
width="100%"
onMouseOver={() => setImage(item.src)}
onMouseLeave={() => setImage(currentPainting.src)}
key={index}
justifyContent="center"
>
<Image
src={item.src}
alt={`${item.title} abstract painting`}
width="2rem"
height="2.5rem"
onClick={() => setCurrentPainting(item)}
borderRadius="5px"
padding="3px"
marginBottom="1rem"
style={{
cursor: 'pointer',
...(currentPainting.src === item.src && {
border: '1px solid #e77600',
boxShadow: 'rgba(0, 0, 0, 0.35) 0px 3px 8px',
}),
}}
/>
</Flex>
)}
</Collection>
</Flex>
<Flex direction="column" justifyContent="space-between">
<Flex direction="column" gap="0.7rem">
<Flex justifyContent="space-between" alignItems="center">
<Heading level={3}>{currentPainting.title}</Heading>
<Flex height="1.8rem">
{currentPainting.bestSeller ? (
<Badge variation="success">Bestseller</Badge>
) : null}
{currentPainting.isNew ? (
<Badge variation="info">New</Badge>
) : null}
{currentPainting.limitedSupply ? (
<Badge variation="warning">Limited supply</Badge>
) : null}
</Flex>
</Flex>
<Text fontWeight="bold">{currentPainting.artist}</Text>
<Flex
direction={{ base: 'column', large: 'row' }}
alignItems="baseline"
>
<Rating
value={currentPainting.avgRating}
fillColor="#f4a41d"
></Rating>
<Text fontSize="small" fontWeight="lighter">
{currentPainting.reviews} reviews
</Text>
</Flex>
<Divider />
<Flex alignItems="baseline">
<Text fontSize="medium" fontWeight="bold">
Price:
</Text>
<Text fontSize="large" color="#B12704" fontWeight="bold">
{currentPainting.price}
</Text>
</Flex>
<Text fontSize="small" paddingBottom="1rem">
{currentPainting.description}
</Text>
{currentPainting.readyForPickup ? (
<Text>
<Text variation="success" as="span">
Ready within 2 hours
</Text>{' '}
for pickup inside the store
</Text>
) : null}
<SwitchField
label={frame ? 'Frame' : 'No frame'}
labelPosition="end"
isChecked={frame}
onChange={(e) => {
setFrame(e.target.checked);
}}
isDisabled={!currentPainting.inStock}
/>
<SelectField
label="Size"
labelHidden
variation="quiet"
placeholder="Select your size"
value={size}
onChange={(e) => {
e.target.value !== '' && setError(false);
setSize(e.target.value);
}}
hasError={error}
errorMessage="Please select a size."
isDisabled={!currentPainting.inStock}
>
<option value="Small" label='Small (12x16")' />
<option value="Medium" label='Medium (18x24")' />
<option value="Large" label='Large (24x36")' />
<option value="X-Large" label='X-Large (30x40")' disabled />
</SelectField>
{!currentPainting.inStock ? (
<Alert variation="error">Out of stock!</Alert>
) : null}
</Flex>
<Flex
justifyContent="space-between"
direction={{ base: 'column', large: 'row' }}
>
<Flex alignItems="center" gap="5px">
<Text>Qty:</Text>
<StepperField
label="Quantity"
value={quantity}
onStepChange={setQuantity}
min={0}
max={10}
step={1}
labelHidden
width="10rem"
isDisabled={!currentPainting.inStock}
/>
</Flex>
<Button
variation="primary"
onClick={handleAddToCart}
disabled={!currentPainting.inStock || !quantity}
>
Add to Cart
</Button>
</Flex>
</Flex>
</Flex>
</Card>
</View>
);
}
export default App;
このような画面が表示されるはずです。こんな簡単に綺麗なUIが作れるのはマジですごいですね...
Discussion