Terakhir diperbaharui: Jan 5, 2020
Update User Interface (Tailwind CSS) #2
Daftar Isi
Persiapan Step by Step - Page Layout - Shared Component - UI Component - Component Lainnya - Pages - Update App.js DemoPada bagian ini kita akan tambahkan Tailwind CSS ke aplikasi DinoTes.
Persiapan
Clone repository dari aplikasi DinoTes disini.
Install dependency dengan jalankan perintah yarn install
atau npm install
.
Step by step
Karena saat ini aplikasi DinoTes menggunakan styled-components, kita perlu tambahkan package twin.macro untuk bisa menggunakan tailwindcss di dalam styled-components.
Tanpa menggunakan twin.macro maka kita harus mengkonfigurasi keduanya secara manual dengan langkah konfigurasi yang cukup panjang.
Install tailwindcss + twin.macro
1yarn add tailwindcss twin.macro
Tambahkan global styles yang berfungsi sama dengan preflight yang dimiliki tailwindcss.
src/App.js
1import React from 'react';2import { Route, Switch } from 'react-router-dom';3import styled from 'styled-components';4import { GlobalStyles } from 'twin.macro';5import HomePage from './pages/Home';6import AddPage from './pages/Add';7import EditPage from './pages/Edit';89const Container = styled.div`1011`;1213function App() {14 return (15 <div>16 <GlobalStyles />17 <Container>18 <Switch>19 <Route path="/add">20 <AddPage />21 </Route>22 <Route path="/edit/:id">23 <EditPage />24 </Route>25 <Route path="/">26 <HomePage />27 </Route>28 </Switch>29 </Container>30 </div>31 );32}3334export default App;
Buat konfigurasi untuk twin.macro.
Update package.json
1...2 'babelMacros': {3 'twin': {4 'preset': 'styled-components'5 }6 }7..
Jalankan aplikasi dengan perintah yarn start
. Hasilnya:
Jika diamati, hasilnya menjadi sedikit berantakan.
Kita perlu tulis ulang semua css yang ada di dalam styled component menggunakan utility class dari tailwindcss, sekaligus melakukan sedikit perubahan pada tampilan atau UI aplikasi dinotes menjadi seperti ini:
Gambar
Page Layout
Yang pertama kita update adalah layout.
Kita tambahkan component PageContainer sebagai container untuk halaman.
src/layouts/PageLayout.js
1import React from 'react';2import tw from 'twin.macro';3import Header from '../components/shared/Header';4import Footer from '../components/shared/Footer';56const PageContainer = tw.div`max-w-7xl mx-auto px-4 sm:px-6`;78const PageLayout = (props) => {9 const { children } = props;1011 return (12 <PageContainer>13 <Header />14 {children}15 <Footer />16 </PageContainer>17 );18};1920export default PageLayout;
Utility class yang ditambahkan pada component PageContainer adalah:
- max-w-7xl, max-width dari container adalah 7xl atau 80rem
- mx-auto, margin top & bottom auto
- px-4, ukuran padding adalah 1rem
- sm:px-6, pada ukuran layar 640px keatas ukuran padding top dan bottom berubah menjadi 1.5rem
Shared Component
Selanjutnya adalah component Header & Footer.
Kita akan pindahkan button untuk menambah note baru ke Header, dan juga kita akan tambahkan FontAwesome Icon pada button agar membuatnya menjadi lebih menarik.
src/components/shared/Header.js
1import React from 'react';2import tw from 'twin.macro';3import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';4import { faFile } from '@fortawesome/free-solid-svg-icons';56import Button from '../ui/Button';7import logo from '../../assets/images/header-logo.png';8import { Link } from 'react-router-dom';910const Navigation = tw.div`11 flex12 justify-between items-center13 border-b-2 border-gray-10014 py-615 md:justify-start md:space-x-316 `;1718const Img = tw.img`h-14 w-auto sm:h-16`;1920const Heading = tw.h2`21 invisible22 text-xl font-bold text-gray-90023 md:visible`;2425const Menu = tw.div`md:flex items-center justify-end md:flex-1 lg:w-0`;2627const Header = () => {28 return (29 <Navigation>30 <Img src={logo} alt="logo" />31 <Heading>DinoTes</Heading>32 <Menu>33 <Link to="/add">34 <Button>35 <FontAwesomeIcon icon={faFile} />36 New Note37 </Button>38 </Link>39 </Menu>40 </Navigation>41 );42};4344export default Header;
Kemudian update footer.js
src/components/shared/Footer.js
1import React from 'react';2import tw from 'twin.macro';34const Container = tw.div`m-4 p-2`;56const Footer = () => {7 return (8 <Container>9 <p>10 by <a href="https://devsaurus.com">devsaurus</a> © 202011 </p>12 </Container>13 );14};1516export default Footer;
UI Component
UI component disini meliputi Button, Container, Form dan Message.
src/components/ui/Button.js
Update code Button.js dari sebelumnya:
1import styled from 'styled-components';23const Button = styled.button`4567891011`;1213export default Button;
menjadi:
1import tw, { styled } from 'twin.macro';23const Button = styled.button(({ danger }) => [4 danger ? tw`bg-red-500` : tw`bg-purple-700`,5 tw`text-white text-base m-2 p-3 border rounded-md`6]);78export default Button;
Code menjadi sangat simple!
Lanjutkan dengan ui component yang lain:
src/components/ui/Container.js
1import tw from 'twin.macro';23const Container = tw.div`flex flex-col items-center justify-center m-4`;45export default Container;
src/components/ui/Form.js
1import tw from 'twin.macro';23const Form = tw.form`flex flex-col w-4/5`;45const FormGroup = tw.div`flex flex-col`;67const FormButtonGroup = tw.div`flex justify-end`;89const Input = tw.input`10 my-8 p-211 text-xl font-bold w-full12 focus:outline-none focus:ring focus:border-blue-300`;1314const TextArea = tw.textarea`15 resize-y16 mb-8 p-217 focus:outline-none focus:ring focus:border-blue-300`;1819export { Form, FormGroup, FormButtonGroup, Input, TextArea };
src/components/ui/Message.js
1import React from 'react';2import tw, { styled } from 'twin.macro';34const MessageContainer = styled.div(({ danger }) => [5 danger ? tw`border-red-500` : tw`border-green-500`,6 tw`flex flex-col items-center justify-center m-4 p-4 border-2 rounded`7]);89const Message = (props) => {10 const { text, type } = props;1112 return (13 <>14 {type === 'error' ? (15 <MessageContainer danger>16 <p>❌ {text}</p>17 </MessageContainer>18 ) : (19 <MessageContainer>20 <p>✅ {text}</p>21 </MessageContainer>22 )}23 </>24 );25};2627export default Message;
Component lainnya
src/components/AddNoteForm.js
1...2import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';3import { faSave } from '@fortawesome/free-solid-svg-icons';4...5 return (6 <>7 <InfoWrapper status={isSuccess} />8 <Form onSubmit={handleSubmit}>9 <FormGroup>10 <Input11 type='text'12 name='title'13 placeholder='Title'14 value={title}15 onChange={handleTitleChange}16 />17 </FormGroup>18 <FormGroup>19 <TextArea20 name='note'21 rows='12'22 placeholder='Your content goes here..'23 value={note}24 onChange={handleNoteChange}25 />26 </FormGroup>27 <FormButtonGroup>28 <Button type='submit'>29 <FontAwesomeIcon icon={faSave} /> Save30 </Button>31 </FormButtonGroup>32 </Form>33 </>34 );35...
Hasilnya:
src/components/EditNoteForm.js
1...2import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';3import { faSave, faTrashAlt } from '@fortawesome/free-solid-svg-icons';4import { Form, FormGroup, FormButtonGroup, Input, TextArea } from './ui/Form';5...6 return (7 <>8 <InfoWrapper status={isSuccess} />9 <Form onSubmit={handleSubmit}>10 <FormGroup>11 <Input12 type='text'13 name='title'14 value={title}15 onChange={handleTitleChange}16 />17 </FormGroup>18 <FormGroup>19 <TextArea20 name='note'21 rows='12'22 value={note}23 onChange={handleNoteChange}24 />25 </FormGroup>26 <FormButtonGroup>27 <Button type='submit'>28 <FontAwesomeIcon icon={faSave} /> Save29 </Button>30 <Button danger onClick={handleDeleteNote}>31 <FontAwesomeIcon icon={faTrashAlt} /> Delete32 </Button>33 </FormButtonGroup>34 </Form>35 </>36 );37...
Hasilnya:
Untuk component yang bertugas menampilkan semua notes, kita akan ganti tampilan dari list ke grid.
src/components/NotesList.js
1import React, { useEffect } from 'react';2import tw from 'twin.macro';3import { Link } from 'react-router-dom';4import { useSelector, useDispatch } from 'react-redux';56import { getAllNotes, fetchNotes } from '../features/notes/notesSlice';78const NotesListContainer = tw.div`grid grid-cols-1 md:grid-cols-3 gap-4 my-8`;9const Card = tw.div`text-left p-4 border rounded-md`;10const Title = tw.h4`text-lg font-semibold text-purple-900`;1112const NotesList = () => {13 const dispatch = useDispatch();14 const notes = useSelector(getAllNotes);15 const notesStatus = useSelector((state) => state.notes.status);16 const error = useSelector((state) => state.notes.error);1718 useEffect(() => {19 if (notesStatus === 'idle') {20 dispatch(fetchNotes());21 }22 }, [notesStatus, dispatch]);2324 let content;2526 if (notesStatus === 'loading') {27 content = <div>Loading...</div>;28 } else if (notesStatus === 'succeeded') {29 content = notes.map((note) => {30 return (31 <Card key={note._id}>32 <Title>33 <Link to={`/edit/${note._id}`}>{note.title}</Link>34 </Title>35 <p>{note.note.slice(0, 101)}</p>36 </Card>37 );38 });39 } else if (notesStatus === 'failed') {40 content = <div>{error}</div>;41 }4243 return <NotesListContainer>{content}</NotesListContainer>;44};4546export default NotesList;
Pages
Ada 3 halaman atau page yang harus diupdate, yaitu halaman untuk menambah note, halaman untuk mengedit note dan halaman untuk menampilkan semua note.
Karena pada setiap page terdapat sebuah link sebagai navigasi kembali halaman Home, kita akan buat ui component baru dengan nama HomeLink.js
src/ui/HomeLink.js
1import tw from 'twin.macro';23const HomeLink = tw.div`flex w-full`;45const Title = tw.h4`text-lg font-semibold text-blue-500`;67export { HomeLink, Title };
Update halaman untuk menambah note baru:
src/pages/Add.js
1import React from 'react';2import { Link } from 'react-router-dom';3import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';4import { faArrowLeft } from '@fortawesome/free-solid-svg-icons';5import PageLayout from '../layouts/PageLayout';6import AddNoteForm from '../components/AddNoteForm';7import Container from '../components/ui/Container';8import { HomeLink, Title } from '../components/ui/HomeLink';910const AddPage = () => {11 return (12 <PageLayout>13 <Container>14 <HomeLink>15 <Title>16 <FontAwesomeIcon icon={faArrowLeft} /> <Link to="/">Back</Link>17 </Title>18 </HomeLink>19 <AddNoteForm />20 </Container>21 </PageLayout>22 );23};2425export default AddPage;
Update halaman untuk mengedit note:
src/pages/Edit.js
1import React from 'react';2import { Link } from 'react-router-dom';3import PageLayout from '../layouts/PageLayout';4import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";5import { faArrowLeft } from "@fortawesome/free-solid-svg-icons";6import EditNoteForm from '../components/EditNoteForm';7import Container from '../components/ui/Container';8import { HomeLink, Title } from '../components/ui/HomeLink';910const EditPage = () => {11 return (12 <PageLayout>13 <Container>14 <HomeLink>15 <Title>16 <FontAwesomeIcon icon={faArrowLeft} /> <Link to="/">Back</Link>17 </Title>18 </HomeLink>19 <EditNoteForm />20 </Container>21 </PageLayout>22 );23};2425export default EditPage;
Update halaman utama dari aplikasi DinoTes.
Karena button untuk menambah note baru dipindah ke header maka kita bisa hapus button Add New Note.
1import React from 'react';2import PageLayout from '../layouts/PageLayout';3import NotesList from '../components/NotesList';4import Container from '../components/ui/Container';56const HomePage = () => {7 return (8 <PageLayout>9 <Container>10 <NotesList>Notes List</NotesList>11 </Container>12 </PageLayout>13 );14};1516export default HomePage;
Update App.js
Langkah terakhir adalah update App.js
src/App.js
1import React from 'react';2import { Route, Switch } from 'react-router-dom';3import tw, { GlobalStyles } from 'twin.macro';4import HomePage from './pages/Home';5import AddPage from './pages/Add';6import EditPage from './pages/Edit';78const Container = tw.div`text-center`;910function App() {11 return (12 <div>13 <GlobalStyles />14 <Container>15 <Switch>16 <Route path='/add'>17 <AddPage />18 </Route>19 <Route path='/edit/:id'>20 <EditPage />21 </Route>22 <Route path='/'>23 <HomePage />24 </Route>25 </Switch>26 </Container>27 </div>28 );29}3031export default App;
Demo
Hasil akhir setelah update UI dengan mengganti CSS dengan utility class dari tailwindcss.
Final code dapat ditemukan di github devsaurus.