프론트엔드/React 연습과제 해답

리액트 기초이론 5 - 컴포넌트 스타일링 2 - SASS 해답

syleemomo 2022. 3. 2. 13:15
728x90

 

* 연습과제 1

import React from 'react'
import './Button.scss'

function Button({ children, size, color, width, handleClick, disabled }){
    return <button 
                className={`Button ${size} ${color} ${width}`} 
                onClick={handleClick} disabled={disabled}
            >{children}</button>
}

export default Button;

Button.defaultProps = {
    size: 'medium',
    color: 'tomato',
    disabled: false
}
@use "sass:list";

$colors: blue, tomato, grey;
$hover-colors: skyblue, lightsalmon, lightgray; 
$sizes: large, medium, small;

$default-height: 50px;
$default-padding-left: 10px;
$default-padding-right: 10px;
$default-font-size: 1rem;

@mixin setBtnSize($size, $h, $pl, $pr, $fs){
  .#{$size}{
    height: $h;
    padding-left: $pl;
    padding-right: $pr;
    font-size: $fs;
  }
}

.Button {
  all: unset;
  color: white;
  cursor: pointer;
  border-radius: 5px;
  font-weight: bold;
  margin-left: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;

  &:hover{
    opacity: 0.7;
  }
}
/* 버튼의 크기 설정 */
@include setBtnSize(list.nth($sizes, 1), $default-height + 20px , $default-padding-left + 5px, $default-padding-right + 5px, $default-font-size + 0.2rem);
@include setBtnSize(list.nth($sizes, 2), $default-height , $default-padding-left, $default-padding-right, $default-font-size);
@include setBtnSize(list.nth($sizes, 3), $default-height - 20px , $default-padding-left - 5px, $default-padding-right - 5px, $default-font-size - 0.2rem);

/* 버튼의 배경색 설정 */
@each $color in $colors{
  .#{$color}{
    background: $color;

    &:hover{
      background: list.nth($hover-colors, list.index($colors, $color));
    }
  }
}

/* 전체 너비를 차지하는 버튼 */
.fullWidth{
  width: 100%;
  margin-left: 0px;
  margin-top: 10px;
  margin-bottom: 10px;
}

 

* 연습과제 2

import React from 'react'
import './Modal.scss'

function Modal({ open, children }){
    return <div className={`Modal ${open? "open": "close"}`}>{children}</div>
}

export default Modal;

Modal.defaultProps = {
    open: false
}
$modal-func: (
  open: block,
  close: none
);

$modal-style: (
  header: (
    font-size: 1.2rem,
    font-weight: bold
  ),
  body: (
    font-size: 0.9rem,
    margin: 20px
  ),
  'body input': (
    width: 60%,
    height: 30px,
    border: 1px solid lightgray,
    border-radius: 5px,
    margin-bottom: 10px
  ),
  footer: (
    display: flex,
    justify-content: flex-end,
    align-items: center
  )
);

.Modal {
  width: 500px;
  margin: 100px auto;
  border: 1px solid tan;
  padding: 10px;
  text-align: center;
}

// 모달창 열고 닫기
@each $key, $value in $modal-func{
  .#{$key}{
    display: $value;
  }
}
// 모달창 스타일링
@each $classname, $attributes in $modal-style{
  .#{$classname}{
    @if($classname != 'body input'){ padding: 5px; }

    @each $key, $value in $attributes{
      #{$key}: $value;
    }
  }
}

 

* 연습과제 3

import './App.scss';
import React, { Component } from 'react'

class App extends Component{
  state = {
    isSorted: false,
    products: null,
    sortedProducts: null
  }
  componentDidMount(){
    const API_URL = 'http://makeup-api.herokuapp.com/api/v1/products.json?brand=maybelline' 
    // 서버에서 데이터 가져오기
    fetch(API_URL)
    .then((res) => {
        return res.json()
    })
    .then((products) => {
      const sortedProducts = [...products] //  원본복사 
      sortedProducts.sort( (p1, p2) => parseFloat(p1.price) - parseFloat(p2.price) ) 
      this.setState({products, sortedProducts})
    })
  }
  toggleState = () => {
    this.setState({ isSorted: !this.state.isSorted})
  }
  
  render(){
    const { isSorted, products, sortedProducts } = this.state

    const displayProducts = (product) => {
      const item = (
        <div key={product.id} className='product'>
          <div className='product-img'><img src={product.image_link} alt={product.name}/></div>
          <div className='product-name'>{product.name} (${product.price})</div>
          <div className='product-description'>{product.description}</div>
          <div className='product-type'>{product.product_type}</div>
        </div>
      )
      return item
    }
    
    return (
      <>
        <div className="header">
          <button className="sort-btns" onClick={this.toggleState}>Price</button><br/>
        </div>
        
        <div className='root'>
          {isSorted ? 
              sortedProducts && sortedProducts.map(displayProducts) 
            : products && products.map(displayProducts)}
        </div>
      </>
    )
  }
}

export default App;
$primary-bg-color: peru;
$primary-color: white;

@mixin setPosition($jc, $ai, $fw){
  display: flex;
  flex-wrap: $fw;
  justify-content: $jc;
  align-items: $ai;
}
@mixin setFont($size, $weight){
  font-size: $size;
  font-weight: $weight;
  text-align: center;
}
@mixin setSize($width, $height){
  @if($width) { width: $width; }
  @if($height) { height: $height; }
}
@mixin setColor($bgc, $c){
  @if($bgc) { background: $bgc; }
  @if($c) { color: $c; }
}

.header{
  @include setSize(100%, 70px);
  @include setPosition(flex-end, center, nowrap);
  @include setColor($primary-color, null);

  position: fixed;
  left: 0;
  top: 0;
  right: 0;
  box-shadow: 1px 1px 5px 5px darkgray;
  z-index: 1;

  .sort-btns{
    all: unset;
    @include setSize(100px, 50px);
    @include setFont(1rem, bold);
    @include setColor($primary-bg-color, $primary-color);

    border-radius: 10px;
    cursor: pointer;
    margin-right: 10px;
  
    &:hover{
      opacity: 0.8;
    }
  }
}

.root{
  @include setSize(60%, null);
  @include setPosition(center, center, wrap);
  margin: 100px auto;

  .product{
    @include setSize(null, 500px);
    @include setColor($primary-bg-color, $primary-color);
  
    flex: 200px;
    box-shadow: 1px 1px 5px 5px $primary-bg-color;
    margin: 10px;
    overflow: hidden;

    .product-img{
      @include setSize(100%, 180px);
      overflow: hidden;

      img{
        @include setSize(100%, 100%);
      }
    }
    .product-name{
      @include setFont(1.1rem, bold);
    }
    .product-description{
      @include setFont(0.9rem, 400);
      margin-top: 15px;
    }
  }
}
728x90