たくさんの自由帳

夜勤明け (のなんでも出来そうな気分の中) Kotlin/JSを初めて触ったら面白かったので記事にする。

本題

残りの日数を数えるWebサイトをKotlin/JSで作ります。
これなら簡単に作れそう。

Kotlin/JS #とは

KotlinコードをJSにトランスパイルする。
JSみたいにDOM操作とかも出来る。

IDEAを開き新規プロジェクトを作成します

Gradleを押して、Kotlin DSL build scriptにチェックを入れ、Kotlin/JS for browserを選択します。

Imgur

名前とかは適当に入れてください。

Imgur

生成後しばらく待ちます。

起動する

IDEA右下のTerminalをおして、以下のコマンドを入れます。

./gradlew run --continuous

これはホットリロード付きで開発サーバーを起動するコマンドです。勝手にブラウザが起動するはずです。

初回起動時は、ファイアウォールが許可するか聞いてくるので許可してあげてください。

無事ブラウザにHello Worldが出てれば成功です。

Imgur

index.html を書き換える

src/main/resources/index.htmlindex.htmlになります。

scriptタグを移動

<script>タグ<body>タグの下に移動させます。
これをしないと多分DOM操作時にぬるぽ吐きます。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CountdownKotlinJS</title>
</head>
<body>

</body>

<script src="CountdownKotlinJS.js"></script>

</html>

適当にUIを作る

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>CountdownKotlinJS</title>
</head>

<style>
    .title {
        font-size: 30px;
    }

    .subtitle {
        font-size: 20px;
    }

    .card {
        width: 50%;
        margin-left: auto;
        margin-right: auto;
        margin-bottom: 10px;
        border-radius: 5px;
        padding: 10px;
        box-shadow: 0 2px 5px #ccc;
    }

    .divider {
        border-top: 1px solid #ccc
    }
</style>

<body>

    <div class="card">
        <p class="title">カウントダウン</p>
        <div class="divider"></div>
        <p class="subtitle">日付の選択</p>
        <p align="center">
            <input type="date" id="date_input">
            <input type="button" id="calc_button" value="残り日数計算">
        </p>
        <div class="divider"></div>
        <p class="subtitle">残り</p>
        <p align="center" class="title" id="countdown_text"></p>
    </div>
</body>

<script src="CountdownKotlinJS.js"></script>

</html>

Kotlin

とりあえず日付を取得するまで

import kotlinx.browser.document
import kotlinx.browser.window
import org.w3c.dom.HTMLInputElement

fun main() {

    // 残り日数表示要素
    val countdownTextElement = document.getElementById("countdown_text")!!
    // ボタン
    val calcButton = document.getElementById("calc_button")!!
    // 日付入力要素
    val dateInputElement = document.getElementById("date_input")!! as HTMLInputElement

    // ボタン押したとき
    calcButton.addEventListener("click", {
        // 日付取得
        val dateString = dateInputElement.value
        window.alert(dateString)
    })
}

日付を入れてボタンを押したらアラートが出ると思います。

Imgur

日付計算ライブラリ

別にJS標準のDateを使うって手もある。

今回はdayjsを入れようと思います。
Kotlin/JSでもnpmからライブラリを入れることが出来ます。

build.gradle.ktsに書き足します。
日付ライブラリといえばmoment.jsですが、今回はdayjsを使います。軽いらしい?

plugins {
    id("org.jetbrains.kotlin.js") version "1.5.10"
}

group = "io.github.takusan23"
version = "1.0.0"

repositories {
    mavenCentral()
}

dependencies {
    implementation(kotlin("stdlib-js"))

    // 日付計算
    implementation(npm("dayjs", "1.10.6"))

}

JSのライブラリをKotlinで使う

JSは動的に型をつけますが、Kotlinでは静的に型をつけます。
普通に考えるとJSライブラリが使えないように見えますが、Kotlin/JSにはdynamic型が用意されています。

これを使うとJSみたいに書くことが出来ます。なおこれを使うとKotlinNull安全等の恩恵が受けれなくなるのでKotlinで書く意味・・・?

/** dayjs読み込み */
@JsModule("dayjs")
@JsNonModule
external fun dayjs(): dynamic

残り日数計算

を含めた完全版です。

import kotlinx.browser.document
import org.w3c.dom.HTMLInputElement

/** dayjs(コンストラクタあり)読み込み */
@JsModule("dayjs")
@JsNonModule
external fun dayjs(d: dynamic): dynamic

/** dayjs読み込み */
@JsModule("dayjs")
@JsNonModule
external fun dayjs(): dynamic

fun main() {

    // 残り日数表示要素
    val countdownTextElement = document.getElementById("countdown_text")!!
    // ボタン
    val calcButton = document.getElementById("calc_button")!!
    // 日付入力要素
    val dateInputElement = document.getElementById("date_input")!! as HTMLInputElement

    // ボタン押したとき
    calcButton.addEventListener("click", {
        // 日付取得
        val dateString = dateInputElement.value
        val countdown = dayjs(dateString).diff(dayjs(), "day") as Int
        countdownTextElement.textContent = "残り $countdown 日"
    })
}

これで残りの日数を表示できます。やったね。

Imgur

書き出す(ビルド)

ターミナルに以下のコマンドを入力すことでHTMLを書き出すことが出来ます。

./gradlew browserWebpack

build/distributionsに書き出されます。

Imgur

ホスティング

今回はNetlifyにホスティングして公開します。

とりあえずGitHubに公開する

ここから出来ます。

Imgur

Netlifyで公開

ビルドコマンドのところを空白にします。
おま環だろうけど、Netlifyでビルド出来なかったのでGitHub Actionsでビルドして、結果だけNetlifyに公開するようにする。

ビルドしないように設定を変更します。Site settingsからBuild & deployをおし、Stop buildsします。

Imgur

GitHub Actions の前に

リポジトリの設定を開いて、Secretsを開きます。
この中に、必要な値を保存しておきます。

NETLIFY_AUTH_TOKEN

Netlifyのアカウント設定へ進んで、Applicationsの中のPersonal access tokensまでスクロールして、New access tokenを押して払い出してもらいます。

NETLIFY_SITE_ID

これはさっき公開したサイトの設定へすすんで、Site informationの中のAPI ID:の値です

こんなふう

Imgur

GitHub Actions を組む

リポジトリのActionsを選択して、set up a workflow yourselfを押して作成します。

Imgur

そしたら以下コピペ

# 参考にした。thx!:https://qiita.com/nwtgck/items/e9a355c2ccb03d8e8eb0

name: Netlify

on:
  push:
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      
      # 権限の変更
      - name: Grant permission
        run: chmod +x ./gradlew
      
      # HTML書き出し(ビルド)
      - name: Making html
        run: ./gradlew browserWebpack 
      
      # Netlifyにデプロイする
      - name: Upload netlify
        run: npx netlify-cli deploy --dir=./build/distributions --prod
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

できたらCommitしてActionsへ移動して見てみます。

作業進行中

Imgur

終わったらNetlifyで公開したサイトのURLを開いてます。無事開けたら成功です。

終わりに

完成品です:https://countdown-kotlinjs.netlify.app/
ソースコードです:https://github.com/takusan23/CountdownKotlinJS