# 各種組み込み例

# Vanilla JS

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script defer src="https://cdn.jsdelivr.net/npm/shogi-player@1.1.17"></script>
    <script type="module">
      const el = document.querySelector("shogi-player-wc")
      el.addEventListener("ev_play_mode_move", e => {
        alert(e.detail[0].last_move_info.to_kif)
      })
    </script>
  </head>
  <body>
    <shogi-player-wc
      sp_mode="play"
      sp_body="position startpos moves 7g7f 3c3d 8h2b+ 3a2b"
      sp_controller="true"
      ></shogi-player-wc>
  </body>
</html>
単体で開く (opens new window)

Also see: 使い方

# Ruby

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script defer src="https://cdn.jsdelivr.net/npm/shogi-player@1.1.17"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@latest/dist/browser.script.iife.js"></script>
    <script type="text/ruby">
      require "js"
      document = JS.global[:document]
      el = document.querySelector("shogi-player-wc")
      el.setAttribute("sp_body", "position startpos moves 7g7f 3c3d 8h2b+ 3a2b")
      el.addEventListener("ev_play_mode_move") do |e|
        JS.global.alert(e[:detail][0][:last_move_info][:to_kif])
      end
    </script>
  </head>
  <body>
    <shogi-player-wc sp_mode="play"></shogi-player-wc>
  </body>
</html>
単体で開く (opens new window)

# React

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script defer src="https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js"></script>
    <script defer src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/shogi-player@1.1.17"></script>
    <script type="text/babel">
      class App extends React.Component {
        constructor(props) {
          super(props)
          this.spRef = React.createRef()
        }
        componentDidMount() {
          this.spRef.current.addEventListener("ev_play_mode_move", e => {
            alert(e.detail[0].last_move_info.to_kif)
          })
        }
        render() {
          return (<shogi-player-wc
                  ref={this.spRef}
                  sp_mode="play"
                  sp_body="position startpos moves 7g7f 3c3d 8h2b+ 3a2b"
                  sp_controller="true"
                  ></shogi-player-wc>)
        }
      }
      ReactDOM.render(<App/>, document.getElementById("root"))
    </script>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
単体で開く (opens new window)

# Solid.js

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script defer src="https://cdn.jsdelivr.net/npm/shogi-player@1.1.17"></script>
    <script type="module">
      import { render } from "https://cdn.skypack.dev/solid-js/web"
      import html from "https://cdn.skypack.dev/solid-js/html"

      const App = () => {
        return html`
          <shogi-player-wc
            sp_mode="play"
            sp_body="position startpos moves 7g7f 3c3d 8h2b+ 3a2b"
            sp_controller="true"
            ></shogi-player-wc>`
      }
      render(App, document.body)

      const el = document.querySelector("shogi-player-wc")
      el.addEventListener("ev_play_mode_move", e => alert(e.detail[0].last_move_info.to_kif))
      </script>
  </head>
  <body>
  </body>
</html>
単体で開く (opens new window)

# Web Components

# Basic

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script defer src="https://cdn.jsdelivr.net/npm/shogi-player@1.1.17"></script>
    <script type="module">
      class ShogiBoard extends HTMLElement {
        constructor() {
          super()
          this.attachShadow({mode: "open"})
        }
        connectedCallback() {
          this.shadowRoot.innerHTML = `
          <shogi-player-wc
            sp_mode="play"
            sp_body="position startpos moves 7g7f 3c3d 8h2b+ 3a2b"
            sp_controller="true"
          ></shogi-player-wc>`
          const el = this.shadowRoot.querySelector("shogi-player-wc")
          el.addEventListener("ev_play_mode_move", e => alert(e.detail[0].last_move_info.to_kif))
        }
      }
      customElements.define("shogi-board", ShogiBoard)
      </script>
  </head>
  <body>
    <shogi-board></shogi-board>
  </body>
</html>
単体で開く (opens new window)

# Full Component

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script defer src="https://cdn.jsdelivr.net/npm/shogi-player@1.1.17"></script>
    <script type="module">
      class ShogiBoard extends HTMLElement {
        constructor() {
          super()
          this.attachShadow({mode: "open"})
        }
        static get observedAttributes() {
          return ["source"]
        }
        attributeChangedCallback(name, oldValue, newValue) {
          if (name === "source") {
            this.source = newValue
          }
          if (this.isConnected) {
            this.render()
          }
        }
        render() {
          this.shadowRoot.innerHTML = `
          <style>
            :host {
              display: block;
              background-color: hsl(0 0% 0% / 0.1);
              padding: 1rem;
            }
            .container {
              display: flex;
              justify-content: center;
            }
            shogi-player-wc {
              flex-basis: 640px;
            }
            shogi-player-wc::part(root) {
              --sp_board_color: LightSkyBlue;
            }
          </style>
          <div class="container">
            <shogi-player-wc
              sp_mode="play"
              sp_body="${this.source}"
              sp_controller="true"
            ></shogi-player-wc>
          </div>
        `
          const el = this.shadowRoot.querySelector("shogi-player-wc")
          el.addEventListener("ev_play_mode_move", e => alert(e.detail[0].last_move_info.to_kif))
        }
      }
      customElements.define("shogi-board", ShogiBoard)
      // Reactive Test
      // setTimeout(() => document.querySelector('shogi-board').setAttribute("source", "position startpos moves 7g7f"), 1000 * 1)
      </script>
  </head>
  <body>
    <shogi-board source="position startpos moves 7g7f 3c3d 8h2b+ 3a2b"></shogi-board>
  </body>
</html>
単体で開く (opens new window)

# Lit

# Basic

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script defer src="https://cdn.jsdelivr.net/npm/shogi-player@1.1.17"></script>
    <script type="module">
      import { LitElement, html } from "https://cdn.jsdelivr.net/gh/lit/dist@2/core/lit-core.min.js"
      export class MyApp extends LitElement {
        render() {
          return html`
            <shogi-player-wc
              sp_mode="play"
              sp_body="position startpos moves 7g7f 3c3d 8h2b+ 3a2b"
              sp_controller="true"
              @ev_play_mode_move="${e => alert(e.detail[0].last_move_info.to_kif)}"
            ></shogi-player-wc>`
        }
      }
      customElements.define("my-app", MyApp)
    </script>
  </head>
  <body>
    <my-app></my-app>
  </body>
</html>
単体で開く (opens new window)

# Full Component

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script defer src="https://cdn.jsdelivr.net/npm/shogi-player@1.1.17"></script>
    <script type="module">
      import { LitElement, css, html } from "https://cdn.jsdelivr.net/gh/lit/dist@2/core/lit-core.min.js"
      export class ShogiBoard extends LitElement {
        static styles = css`
          :host {
            display: block;
            background-color: hsl(0 0% 0% / 0.1);
            padding: 1rem;
          }
          .container {
            display: flex;
            justify-content: center;
          }
          shogi-player-wc {
            flex-basis: 640px;
          }
          shogi-player-wc::part(root) {
            --sp_board_color: LightSkyBlue;
          }
        `

        static properties = {
          source: { type: String },
        }

        constructor() {
          super()
          this.source ??= "position sfen 4k4/9/9/9/9/9/9/9/9 b 2r2b4g4s4n4l18p 1"
        }

        render() {
          return html`
            <div class="container">
              <shogi-player-wc
                sp_mode="play"
                sp_body="${this.source}"
                sp_controller="true"
                @ev_play_mode_move="${e => alert(e.detail[0].last_move_info.to_kif)}"
              ></shogi-player-wc>
            </div>
          `
        }
      }
      customElements.define("shogi-board", ShogiBoard)
    </script>
  </head>
  <body>
    <shogi-board source="position startpos moves 7g7f 3c3d 8h2b+ 3a2b"></shogi-board>
  </body>
</html>
単体で開く (opens new window)

# Vue.js 2

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script defer src="https://cdn.jsdelivr.net/npm/vue@2"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/shogi-player@1.1.17"></script>
    <script type="module">
      Vue.config.ignoredElements = ["shogi-player-wc"]
      new Vue({el: "#app"})
    </script>
  </head>
  <body>
    <div id="app">
      <shogi-player-wc
        sp_mode="play"
        sp_body="position startpos moves 7g7f 3c3d 8h2b+ 3a2b"
        sp_controller="true"
        @ev_play_mode_move="e => alert(e.detail[0].last_move_info.to_kif)"
        ></shogi-player-wc>
    </div>
  </body>
</html>
単体で開く (opens new window)

# Vue.js 2 + UMD

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script defer src="https://cdn.jsdelivr.net/npm/vue@2"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/shogi-player@1.1.17/dist/lib/production/shogi-player.umd.min.js"></script>
    <script type="module">
      new Vue({el: "#app", components: { ShogiPlayer }})
    </script>
  </head>
  <body>
    <div id="app">
      <shogi-player
        sp_mode="play"
        sp_body="position startpos moves 7g7f 3c3d 8h2b+ 3a2b"
        sp_controller
        @ev_play_mode_move="e => alert(e.last_move_info.to_kif)"
        ></shogi-player>
    </div>
  </body>
</html>
単体で開く (opens new window)

# Vue 3

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script defer src="https://cdn.jsdelivr.net/npm/shogi-player@1.1.17"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/vue@3"></script>
    <script type="module">
      const app = Vue.createApp({
        methods: {
          ev_play_mode_move(e) {
            alert(e.detail[0].last_move_info.to_kif)
          },
        },
      })
      app.config.compilerOptions.isCustomElement = tag => tag === "shogi-player-wc"
      app.mount("#app")
    </script>
  </head>
  <body>
    <div id="app">
      <shogi-player-wc
        sp-pass-props='{
          sp_mode: "play",
          sp_body: "position startpos moves 7g7f 3c3d 8h2b+ 3a2b",
          sp_controller: true,
          sp_pass_style: "{\"--sp_controller_width\": 1.0}",
        }'
        @ev_play_mode_move="ev_play_mode_move"
        ></shogi-player-wc>
    </div>
  </body>
</html>
単体で開く (opens new window)

引数が渡せない問題と回避方法

  • Vue.js 2 で作成した Web Components を Vue 3 と組み合わせたときに限り snake_case なパラメータ名を持つ値が渡せない問題がある
    • 簡単に言えば _ を含むパラメータが無視される
  • そこでその嫌がらせのような制約を回避するために仕方なく sp-pass-props を用意した
  • これは v-bind に似ているが Vue はただの文字列として解釈するため確実に内容を渡すことができる
  • sp-pass-props の内容は JSON5 形式の文字列としてパースする
  • 型変換は JSON5 のパーサーに任せているので Boolean 型の真は "true" ではなく true と書く
  • 最終的に sp-pass-props の内容は $props 相当として扱う

# Vue.js 2 (vue/cli) + UMD

  • 手動で組み込んだ例を shogi-player-vue2-sample-umd (opens new window) に置いている
  • すでにビルドしているため vue.config.js に何も書かなくても動く
  • CSSも js に含んでいるため読み込む必要がない

# Vue.js 2 (vue/cli) + ShogiPlayer.vue

# Nuxt.js + ShogiPlayer.vue

手動で組み込んだ例を shogi-player-nuxt-sample (opens new window) に置いている

# Svelte

手動で組み込んだ例を shogi-player-svelte-sample (opens new window) に置いている

そこから該当箇所の抜粋:

<script>
  function ev_play_mode_move(e) {
    alert(e.detail[0].last_move_info.to_kif)
  }
</script>

<main>
  <shogi-player-wc
    sp-pass-props={'{sp_mode: "play", sp_body: "position startpos moves 7g7f 3c3d 8h2b+ 3a2b", sp_controller: true, sp_pass_style: {\"--sp_controller_width\": 1.0}}'}
    on:ev_play_mode_move={ev_play_mode_move}
    ></shogi-player-wc>
</main>
  • Vue 3 の場合と同じパラメータを渡す例が上になる
  • パラメータの渡し方が Vue 3 よりも難しい
  • sp-pass-props は途中で改行すると動かなくなる

# 自力ビルドの要点

  • transpile 問題
    • node_modules/shogi-player 以下を babel のビルド対象に含める
    • これをやらないとビルドできない
      • クラス定数や ?? 演算子が解釈されない
    • @vue/cli であれば vue.config.js の transpileDependencies に指定する
    • Nuxt.js であれば nuxt.config.js の build.transpile に含める
  • webpack-dev-server で ResizeObserver loop limit exceeded が出る場合
    • devServer.client.overlay.runtimeErrorsfalse にする
  • jest
    • Babel の設定は .babelrc ではなく babel.config.js に書く
    • そうしないと node_modules/* に適用されず jest が動かない
Last Updated: 6/22/2023, 12:46:05 PM