0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

俺からReact, Vueを学んでみないか?クレカ入力画面の作成

Last updated at Posted at 2023-12-12

俺からReact, Vueを学んでみないかシリーズ

無茶苦茶調子に乗ったタイトルですが、インプレッションを集めるために煽っているだけです。すみません。

誰も使ってくれないだろう無駄なもの(HTML/CSSで作る超リアルな電卓など)をどんどんアウトプットしていこうという企画です。

第1回: 俺からReact, Vueを学んでみないか?リアルな電卓を作ってみよう!

クレカ入力画面はお好きですか?

今回はクレカ入力画面を作成しました

作成したといっても、NextjsにHTML/CSSを反映させただけですけど、、、
とはいえ、reactjsで動くように無茶苦茶修正しましたし、JavaScriptも修正が大変でした。


完成物

下記から確認できます!
https://dashboard-next-ui.vercel.app/subscription

スクリーンショット 2023-12-12 9.51.21.png

githubリポジトリ
https://github.com/kouhei-github/dashboard-next-ui


Reactの工夫したところ

管理画面のデザインも自分で作成したのですが、ここの解説はめんどくさいのでやりません。
layout.tsxをうまく活用してください。

ホバーしたらクレジットカードを回転させる

Reactjsでクレジットカードを回転させているわけではなく、CSSで回転させてます。
ただ、Hover時のonFocusイベントonBlurイベントなど工夫しました

ロジック

const [highlight, setHighlight] = useState('');
const [card, setCard] = useState<string>("card")
const handleNumberFocus = (className: string, flip=true) => {
   setCard(flip ? card.replaceAll("flip", "") : "flip card")
   setHighlight(className)
}

HTML

<label htmlFor="cvv">CVV</label>
<input
  id="cvv"
  type="number"
  value={cvv}
  onFocus={() => handleNumberFocus("highlight__cvv", false)}
  onBlur={() => handleNumberFocus("hidden")}
  onChange={(event) => handleCvvChange(event)}/>

コード全ては下記です

"use client"

import "../styles/_credit_card.css"
import {ChangeEvent, useState} from 'react'

const currentYear = new Date().getFullYear()

const initCardHolder: {class: string, innerHtml: string}[] = []
for (let i = 0; i < 16; i++) {
  initCardHolder.push({class: "", innerHtml: "#"},)
}

export default function Subscription() {
  const [number, setNumber] = useState('');
  const [holder, setHolder] = useState('Name on card');
  const [month, setMonth] = useState('MM');
  const [year, setYear] = useState('YY');
  const [cvv, setCvv] = useState('');
  const [cvvFocus, setCvvFocus] = useState(false);
  const [highlight, setHighlight] = useState('');

  const handleHolderChange = (event: ChangeEvent<HTMLInputElement>) => {
    setHolder(event.target.value);
    setHighlight('highlight__holder');
  };

  const handleCvvChange = (event: ChangeEvent<HTMLInputElement>) => {
    setCvv(event.target.value);
  };

  const [card, setCard] = useState<string>("card")
  const handleNumberFocus = (className: string, flip=true) => {
    setCard(flip ? card.replaceAll("flip", "") : "flip card")
    setHighlight(className)
  }

  const [cardNumberClass, setCardNumberClass] = useState<{class: string, innerHtml: string}[]>(initCardHolder)
  const [enteredCardNumbers, setEnteredCardNumbers] = useState<number>(0)
  const handleNumberChange = (event: ChangeEvent<HTMLInputElement>) => {
    const value = event.target.value

    if(enteredCardNumbers > value.length) {
      let newArr = [...cardNumberClass];
      newArr[15 - (15 - value.length)] = {class: "", innerHtml: "#"}
      setCardNumberClass(newArr)
    } else {
      if(value.length > 4 && value.length < 13) {
        let newArr = [...cardNumberClass];
        newArr[value.length - 1] = {class: "filed", innerHtml: "*"}
        setCardNumberClass(newArr)
      } else {
        let newArr = [...cardNumberClass];
        newArr[value.length - 1] = {class: "filed", innerHtml: value.slice(-1)}
        setCardNumberClass(newArr)
      }
    }

    setEnteredCardNumbers(value.length)
    setNumber(value)
  };

  return (
      <>
        <main>
          <section id="card" className={card}>
            <div id="highlight" className={highlight}></div>
            <section className="card__front">
              <div className="card__header">
                <div>CreditCard</div>
                <svg xmlns="http://www.w3.org/2000/svg" height="40" width="60" id="svg895" version="1.1"
                     viewBox="-96 -98.908 832 593.448">
                  <defs id="defs879">
                  </defs>
                  <path id="rect887" display="inline" fill="#ff5f00" strokeWidth="5.494" d="M224.833 42.298h190.416v311.005H224.833z"/>
                  <path id="path889" d="M244.446 197.828a197.448 197.448 0 0175.54-155.475 197.777 197.777 0 100 311.004 197.448 197.448 0 01-75.54-155.53z" fill="#eb001b" strokeWidth="5.494"/>
                  <path id="path891" d="M621.101 320.394v-6.372h2.747v-1.319h-6.537v1.319h2.582v6.373zm12.691 0v-7.69h-1.978l-2.307 5.493-2.308-5.494h-1.977v7.691h1.428v-5.823l2.143 5h1.483l2.143-5v5.823z" className="e" fill="#f79e1b" strokeWidth="5.494"/>
                  <path id="path893" d="M640 197.828a197.777 197.777 0 01-320.015 155.474 197.777 197.777 0 000-311.004A197.777 197.777 0 01640 197.773z" className="e" fill="#f79e1b" strokeWidth="5.494"/>
                </svg>
              </div>
              <div id="card_number" className="card__number">
                {Array.from( {length: 16}, (_, index) => (
                    <span key={index} className={cardNumberClass[index].class}>{cardNumberClass[index].innerHtml}<br/></span>
                ))}
              </div>
              <div className="card__footer">
                <div className="card__holder">
                  <div className="card__section__title">Card Holder</div>
                  <div id="card_holder">{holder}</div>
                </div>
                <div className="card__expires">
                  <div className="card__section__title">Expires</div>
                  <span id="card_expires_month">{month}</span>/<span id="card_expires_year">{year.slice( -2 )}</span>
                </div>
              </div>
            </section>
            <section className="card__back">
              <div className="card__hide_line"></div>
              <div className="card_cvv">
                <span>CVV</span>
                <div className={`card_cvv_field ${cvvFocus ? 'flip' : ''}`}>{Array( cvv.length + 1 ).join( "*" )}
              </div>
            </div>
          </section>
        </section>

        <form className="form">
          <div>
            <label htmlFor="number">Card Number</label>
            <input id="number" type="number" value={number} onFocus={() => handleNumberFocus("highlight__number")} onChange={(event) => handleNumberChange(event)}/>
          </div>
          <div>
            <label htmlFor="holder">Card Holder</label>
            <input id="holder" type="text" value={holder} onFocus={() => handleNumberFocus("highlight__holder")} onChange={(event) => handleHolderChange(event)} />
          </div>
          <div className="filed__group">
            <div>
              <label htmlFor="expiration_month">Expiration Date</label>
                <div className="filed__date">
                  <select id="expiration_month" value={month} onFocus={() => handleNumberFocus("highlight__expire")} onChange={(event) => setMonth(event.target.value)}>
                    <option disabled>Month</option>
                    {Array.from({length: 12}, (_, index) => (
                        <option key={index}>{String(index+1).length === 1 ? `0${index+1}` : index+1}</option>
                    ) )}
                  </select>
                  <select id="expiration_year" value={year} onFocus={() => handleNumberFocus("highlight__expire")} onChange={(event) => setYear(event.target.value)}>
                    <option disabled>Year</option>
                    {Array.from({length: currentYear+9 - currentYear+1}, (_, index) => (
                        <option key={index}>{index + currentYear}</option>
                    ))}
                  </select>
                </div>
              </div>
            <div>
              <label htmlFor="cvv">CVV</label>
              <input
                  id="cvv"
                  type="number"
                  value={cvv}
                  onFocus={() => handleNumberFocus("highlight__cvv", false)}
                  onBlur={() => handleNumberFocus("hidden")}
                  onChange={(event) => handleCvvChange(event)}/>
            </div>
          </div>
        </form>
        </main>
      </>
  )
}

所感

クレジット決済画面は案外簡単だった。
次はもう少し難しいものを作成してみる。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?