JavaScript
reactjs
VSCode

VSCodeで爆速コーディング環境を構築する(主にReactJS向け設定)

VSCodeで爆速コーディング環境を構築する

VSCode標準のコード補完とコードスニペットを併用することで爆速にコーディングすることが可能になります。
ReactJSのプロジェクトでコーディングする場合、自分はもっぱらVSCodeを使っていることが多いです。
いくつかのエディタでプラグインを入れることでJSXコード補完をサポートしてくれるのですが、
VSCodeの場合は標準でJSXコード補完をサポートしているのが強みだと思います。
ReactJSはJSXファイルにJavaScriptとHTMLタグを同ファイルに混在する形になります。

キー、マウスの速度を最速にする

VSCodeに限らず、キーボード速度、トラックパッド(マウス)速度はOSの設定で最速にできます。
システム設定→キーボードの設定からキーのリピートを速い、リピート入力認識までの時間を短いにします。

スクリーンショット 2018-11-05 9.25.09.png

トラックパッド→軌跡の速さを速いにします。
スクリーンショット 2018-11-05 9.27.29.png

スクロールの速さはアクセシビリティ→マウスとトラックパッドから変更できます。
スクリーンショット 2018-11-06 14.56.51.png

慣れないうちは酔いますが・・・

ショートカット

【Windows版】VS Code キーボードショートカット一覧 (オススメ付き)

Macの場合はCtrlをCommandに置き換え、
最低限覚えておきたいもの

  • Command + F ファイル内キーワード検索
  • Command + Shift + F プロジェクトフォルダ内キーワード検索
  • Command + Shift + E エクスプローラー表示
  • Command + P ファイル検索
  • Command + Shift + O ファイル内関数検索
  • Command + Enter 下に行追加
  • Command + Shift + Enter 上に行追加
  • Command + ↓ ファイル末尾
  • Command + ↑ ファイル先頭
  • Ctrl + A 行先頭
  • Ctrl + E 行末尾
  • Alt + → or ← 単語単位移動
  • Command + B サイドバー表示・非表示切り替え
  • Command + Shift + V Markdownファイル(.md)のプレビュー
  • Alt + ↑ or ↓ カーソル行の上下入れ替え
  • Command + N 新規ファイル作成
  • Command + Shift + N 新規ウィンドウを開く
  • Alt+Click 変数もしくは関数をAlt+クリックで定義に飛ぶ

拡張機能を使う

VSCodeでは拡張プラグインをいれることで作業効率が上がるものが多数あります。
こちらの記事が参考になります。
VSCodeのオススメ拡張機能 24選 (とTipsを少し)

Snippetを作成する

Snippetを作成することでテンプレ保存していたショートカットキーワードでコード展開することができます。
メニューから基本設定→ユーザスニペットを選択します。

スクリーンショット 2017-11-23 16.25.22.png

スニペットの言語を指定します。コーディング対象が.jsxファイルの場合はJavaScript React、.jsファイルの場合はJavaScriptを選択します。

スクリーンショット 2017-11-23 16.25.51.png

例えば、function関数を展開するスニペットは次のように記述します。
prefixがSnippetのショートカットキーとなります。
bodyが展開する内容となります。
${1}は展開したときの最初のカーソル位置になります。
Tabキーを押すことで${2}にカーソル位置が移ります。
${1:name}と記述することで展開時にデフォルトでnameという文字列が入ります。
descriptionはコードスニペット内容の説明を記述します。

javascript.json
{
    "function": {
        "prefix": "fu",
        "body": [
            "function ${1:name}(${2}) {",
            "  ${3:処理}",
            "}"
        ],
        "description": "function"
    }
}

Snippetとコード補完を併用する

Snippetとコード補完を併用するために、VSCodeの設定を上書きします。
基本設定→設定で設定を開きます。

スクリーンショット 2017-11-23 16.52.52.png

右の3点ボタンからsettings.jsonを編集します。
スクリーンショット 2018-11-02 20.48.52.png

editor.tabCompletionで検索し、鉛筆マークでユーザ定義(settings.json)を開き、上書きの設定を書きます。

スクリーンショット 2018-11-02 21.00.55.png

"editor.tabCompletion": onにします。候補が多くて出てくるまで遅いと思った場合はonlySnippetsにしてsnippetのみにするのも手です。
上記の設定をonにすることでTabキーでSnippetコードを展開できるようになります。
"editor.snippetSuggestions": "top"にすることで、snippetがサジェストの上位になります。
"editor.quickSuggestionsDelay": 500に設定します。
この設定は、入力後指定の時間後にコード補完の一覧が表示されます。
今回は500ミリ秒に指定しました。人によってタイピング速度が違うと思うので自分に合った時間を指定してください。
"editor.multiCursorModifier": "ctrlCmd"はMac環境専用の指定です。
cmdキーを押しながら、クリックすることでマルチカーソルで指定カーソル位置から同時編集できます。(同じ変数名などを同時に書き換えたいときに、たまに使えます)
Windowsの場合はこの指定無しの場合はデフォルトctrlキーでマルチカーソルできます。
"javascript.implicitProjectConfig.experimentalDecorators": trueはJavaScriptのdecoratorsの文法を使おうとするとVSCodeが「将来実装される機能です」という警告を出すのでtrueにすることで警告を無視できます。
search.excludeは検索時に除外するファイル、フォルダの設定です。
プロジェクト内検索が遅いなと思ったら余計なファイルまで検索している可能性が高いです。除外しておきましょう。
emmet.includeLanguagesは拡張子がファイルタイプがjsxでない場合でもjavascriptreact指定するとjsxファイルとしてコード展開が可能となります。(jsファイルにjsxを書いてる場合など)
emmet.syntaxProfilesはjsxのDOMの閉じタグを自動補完してくれます。
emmet.triggerExpansionOnTabはjsxのDOM記述時にタブ補完が効かない問題を解決してくれます。

settings.json
{
    "editor.tabCompletion": "on",
    "editor.snippetSuggestions": "top",
    "editor.quickSuggestions": {
        "other": true,
        "comments": false,
        "strings": false
    },
    "editor.quickSuggestionsDelay": 500,
    "editor.multiCursorModifier": "ctrlCmd",
    "javascript.implicitProjectConfig.experimentalDecorators": true,
    "search.exclude": {
        "**/.git": true,
        "**/node_modules": true,
        "**/bower_components": true
    }
    "emmet.includeLanguages": {
        "javascript": "javascriptreact"
    },
    "emmet.syntaxProfiles": {
        "jsx": {
            "self_closing_tag": true
        }
    },
    "emmet.triggerExpansionOnTab": true
}

Reactプロジェクト別の拡張設定

webpack.config.jsにて次のようなresolve指定がされている場合に

webpack.config.js
module.exports = {
  mode: 'development',

  resolve: {
    modules: ['src',  'node_modules'],
    extensions: ['.js', '.json'],
  },
}

src/components以下にSomeComponent.jsファイルからSomeComponentがdefault exportされているとして
次のような指定でimportできるようになると思います。
ただし、この場合、Alt+ClickでSomeComponentの定義(ファイル)に飛ぶことができません。

import SomeComponent from 'components/SomeComponent'

そこで次のようなjsconfig.json(プロジェクト別に適応される設定ファイル)をプロジェクトフォルダ直下に置くすることでAlt+Clickで飛ぶことができるようになります。
pathsにはresolveを解決したいパスを定義しておきます。

jsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "components/*": [
        "./src/components/*",
      ]
    },
    "jsx": "react"
  },
  "exclude": [
    "node_modules"
  ]
}

デモ

タイピングが遅いとコード補完が出てしまうのでSnippetを利用する場合はコード補完が出る前にショートカットキーを打ち切ってTabキーを押すのがコツです。
snippets.gif

Snippet例

私がよく使うJavaScriptコードをSnippetにまとめました。
(javascript.json,javascriptreact.json)

react.json
{
    // 変数
    "const": {
        "prefix": "c",
        "body": [
            "const $1"
        ],
        "description": "定数"
    },
    "let": {
        "prefix": "l",
        "body": [
            "let $1"
        ],
        "description": "変数"
    },
    "var": {
        "prefix": "v",
        "body": [
            "var $1"
        ],
        "description": "変数"
    },
    // デバッグ
    "console.log": {
        "prefix": "cl",
        "body": [
            "console.log(${1:val})"
        ],
        "description": "console.log"
    },
    "console.dir": {
        "prefix": "cd",
        "body": [
            "console.dir(${1:obj})"
        ],
        "description": "console.dir"
    },
    "console.log show all": {
        "prefix": "clj",
        "body": [
            "console.log(JSON.stringify(${1:obj}))"
        ],
        "description": "console.log show all"
    },
    "alert": {
        "prefix": "al",
        "body": [
            "alert(${1:message})"
        ],
        "description": "alert"
    },
    "debugger": {
        "prefix": "deb",
        "body": [
            "debugger"
        ],
        "description": "debugger"
    },
    // ループ
    "for": {
        "prefix": "fr",
        "body": [
            "for (let ${1:i} = 0; ${1:i} < ${2:count}; ++${1:i}) {",
            "  ${3}",
            "}"
        ],
        "description": "for in"
    },
    "for in": {
        "prefix": "fi",
        "body": [
            "for (let key in ${1:obj}s) {",
            "  const ${1:obj} = ${1:obj}s[key]",
            "}"
        ],
        "description": "for in"
    },
    "for of": {
        "prefix": "fo",
        "body": [
            "for (let val of ${1:arr}) {",
            "  ",
            "}"
        ],
        "description": "for of"
    },
    "forEach": {
        "prefix": "fe",
        "body": [
            "Object.keys(${1:obj}).forEach(key => {",
            "  ",
            "})"
        ],
        "description": "forEach"
    },
    "Object keys": {
        "prefix": "ok",
        "body": [
            "Object.keys(${1:obj})"
        ],
        "description": "Object keys"
    },
    "Object values": {
        "prefix": "ov",
        "body": [
            "Object.values(${1:obj})"
        ],
        "description": "Object values"
    },
    "Array from": {
        "prefix": "af",
        "body": [
            "Array.from(${1:arr})"
        ],
        "description": "array deepcopy"
    },
    "Object create": {
        "prefix": "oc",
        "body": [
            "Object.create(${1:obj})"
        ],
        "description": "object deepcopy"
    },
    "delete": {
        "prefix": "del",
        "body": [
            "delete ",
        ],
        "description": "delete"
    },
    "continue": {
        "prefix": "co",
        "body": [
            "continue",
        ],
        "description": "continue"
    },
    "break": {
        "prefix": "br",
        "body": [
            "break",
        ],
        "description": "break"
    },
    "switch": {
        "prefix": "sw",
        "body": [
            "switch (${1:val}) {",
            "  case ${2:match}:",
            "    break",
            "  default:",
            "}"
        ],
        "description": "switch"
    },
    "if": {
        "prefix": "if",
        "body": [
            "if (${1}) {",
            "  ",
            "}",
        ],
        "description": "while"
    },
    "while": {
        "prefix": "w",
        "body": [
            "while (${1}) {",
            "  ",
            "}",
        ],
        "description": "while"
    },
    "do while": {
        "prefix": "dw",
        "body": [
            "do {",
            "  ",
            "} while (${1})",
        ],
        "description": "do while"
    },
    "try catch": {
        "prefix": "tr",
        "body": [
            "try {",
            "  ",
            "} catch (e) {",
            "  ",
            "}",
        ],
        "description": "try catch"
    },
    // 配列操作
    "map": {
        "prefix": "ma",
        "body": [
            "${1:[]}.map((${2:val}) => ${2:val})"
        ],
        "description": "map"
    },
    "find": {
        "prefix": "fin",
        "body": [
            "${1:[]}.find((${2:val}) => ${2:val} === true)"
        ],
        "description": "find"
    },
    "filter": {
        "prefix": "fil",
        "body": [
            "${1:[]}.filter((${2:val}) => ${2:val} === true)"
        ],
        "description": "filter"
    },
    "concat": {
        "prefix": "con",
        "body": [
            "${1:[]}.concat(${2:[]})"
        ],
        "description": "concat"
    },
    "join": {
        "prefix": "jo",
        "body": [
            "${1:[]}.join('${2:,}')"
        ],
        "description": "join"
    },
    "split": {
        "prefix": "sp",
        "body": [
            "${1:''}.split('${2:,}')"
        ],
        "description": "split"
    },
    "reduce": {
        "prefix": "red",
        "body": [
            "${1:[]}.reduce((sum, current) => sum + ${2:current}, 0)"
        ],
        "description": "reduce"
    },
    "includes": {
        "prefix": "inc",
        "body": [
            "${1:[]}.includes(${2:val})"
        ],
        "description": "includes"
    },
    "sort": {
        "prefix": "so",
        "body": [
            "${1:[]}.sort((a, b) => ${2:a > b})"
        ],
        "description": "sort"
    },
    "slice": {
        "prefix": "sl",
        "body": [
            "${1:[]}.slice(${2:0, 5})"
        ],
        "description": "slice"
    },
    "push": {
        "prefix": "pu",
        "body": [
            "${1:[]}.push(${2:val})"
        ],
        "description": "push"
    },
    "pop": {
        "prefix": "po",
        "body": [
            "${1:[]}.pop()"
        ],
        "description": "pop"
    },
    "unshift": {
        "prefix": "ush",
        "body": [
            "${1:[]}.unshift(${2:val})"
        ],
        "description": "unshift"
    },
    "shift": {
        "prefix": "sh",
        "body": [
            "${1:[]}.shift()"
        ],
        "description": "shift"
    },
    // 関数   
    "return": {
        "prefix": "r",
        "body": [
            "return $1"
        ],
        "description": "return"
    },
    "async": {
        "prefix": "as",
        "body": [
            "async $1"
        ],
        "description": "非同期待ち"
    },
    "arrow function": {
        "prefix": "f",
        "body": [
            "($1) => $2"
        ],
        "description": "function"
    },
    "function": {
        "prefix": "fu",
        "body": [
            "function ${1:name}(${2}) {",
            "  $3",
            "}"
        ],
        "description": "function"
    },
    "await": {
        "prefix": "aw",
        "body": [
            "await ${1}"
        ],
        "description": "非同期待ち"
    },
    "Promise": {
        "prefix": "pr",
        "body": [
            "return new Promise((resolve, reject) => {",
            "  $1",
            "})"
        ],
        "description": "Promise"
    },
    "Promise parallel": {
        "prefix": "pra",
        "body": [
            "await Promise.all([${1:PromiseFunc}])",
            "  ${2:.then(([value1, value2]) => {\\}).catch((e) => {\\})}",
        ],
        "description": "Promise parallel"
    },
    "Promise sequence": {
        "prefix": "prs",
        "body": [
            "await [${1:{func: PromiseFunc1, param: param1\\},{func: PromiseFunc2, param: param2\\}}]",
        "  .reduce((promise, current) => {",
      "    return promise.then(async (prev) => {",
      "      return await current.func(current.param, prev)",
        "    })",
        "  }, Promise.resolve(null))",
        ],
        "description": "Promise sequence"
    },
    // JSON
    "JSON.parse": {
        "prefix": "jsp",
        "body": [
            "JSON.parse(${1:json})"
        ],
        "description": "JSON.parse"
    },
    "JSON.stringify": {
        "prefix": "jss",
        "body": [
            "JSON.stringify(${1:obj})"
        ],
        "description": "JSON.stringify"
    },
    // インポート・エクスポート
    "export": {
        "prefix": "ex",
        "body": [
            "export $1"
        ],
        "description": "export"
    },
    "import": {
        "prefix": "im",
        "body": [
            "import ${1:val} from '${2:module}'"
        ],
        "description": "import"
    },
    "module.export": {
        "prefix": "moe",
        "body": [
            "module.exports = $1"
        ],
        "description": "module.exports"
    },
    // クラス
    "class": {
        "prefix": "cla",
        "body": [
            "class ${1:name} {",
            "  constructor () {",
            "  }",
            "}"
        ],
        "description": "クラス"
    },
    // localStorage
    "localstorage.getItem": {
        "prefix": "lsg",
        "body": [
            "localStorage.getItem('${1}')",
        ],
        "description": "LocalStorage取得"
    },
    "localstorage.setItem": {
        "prefix": "lss",
        "body": [
            "localStorage.setItem('${1}', ${1})",
        ],
        "description": "LocalStorage保存"
    },
    // 数学
    "Number": {
        "prefix": "num",
        "body": [
            "Number(${1:num})"
        ],
        "description": "Number"
    },
    "Math floor": {
        "prefix": "mf",
        "body": [
            "Math.floor(${1:num})"
        ],
        "description": "Math floor"
    },
    "Math random": {
        "prefix": "ran",
        "body": [
            "Math.random()"
        ],
        "description": "Math random"
    },
    "Number toFixed": {
        "prefix": "tf",
        "body": [
            "${1:num}.toFixed(${2:3})"
        ],
        "description": "Number toFixed"
    },
    // 処理制御
    "setTimeout": {
        "prefix": "tm",
        "body": [
            "setTimeout(() => {",
            "  ",
            "}, ${1:1000})",
        ],
        "description": "遅延処理"
    },
    "gameloop": {
        "prefix": "ra",
        "body": [
            "function loop () {",
            "",
        "  requestAnimationFrame(loop)",
            "}",
            "loop()",
        ],
        "description": "gameloop"
    },
    "print": {
        "prefix": "pri",
        "body": [
            "window.print()",
        ],
        "description": "window.print"
    },

    // タグ
    "div": {
        "prefix": "d",
        "body": [
            "<div>$1</div>"
        ],
        "description": "division"
    },
    "a": {
        "prefix": "a",
        "body": [
            "<a href='${1:url}'>${2:link}</a>"
        ],
        "description": "anchor link"
    },
    "h1〜h6": {
        "prefix": "h",
        "body": [
            "<h${1:1}>${2}</h${1:1}>"
        ],
        "description": "h1〜h6"
    },
    "p": {
        "prefix": "p",
        "body": [
            "<p>$1</p>"
        ],
        "description": "paragraph"
    },
    "hr": {
        "prefix": "hr",
        "body": [
            "<hr/>"
        ],
        "description": "hr"
    },
    "ol": {
        "prefix": "ol",
        "body": [
            "<ol>",
            "  <li>${1:項目}</li>",
            "  <li>${1:項目}</li>",
            "  <li>${1:項目}</li>",
            "</ul>"
        ],
        "description": "順序リスト"
    },
    "ul": {
        "prefix": "ul",
        "body": [
            "<ul style='list-style:none'>",
            "  <li>${1:項目}</li>",
            "  <li>${1:項目}</li>",
            "  <li>${1:項目}</li>",
            "</ul>"
        ],
        "description": "順序なしリスト"
    },
    "details": {
        "prefix": "de",
        "body": [
            "<details>",
            "  <summary>${1:メニュー}</summary>",
            "  <dl>",
            "    <dt>${2:項目}</dt>",
            "    <dt>${2:項目}</dt>",
            "    <dt>${2:項目}</dt>",
            "  </dl>",
            "</details>",
        ],
        "description": "順序なしリスト"
    },
    // フォーム
    "form": {
        "prefix": "form",
        "body": [
            "<form action='${1:url}' method='post' enctype='multipart/form-data' accept-charset='UTF-8' >",
            "  $2",
            "  <input type='submit' value='送信' />",
            "</form>"
        ],
        "description": "form"
    },
    "label": {
        "prefix": "la",
        "body": [
            "<label for='${1:id}'>${2:ラベル}</label>"
        ],
        "description": "label"
    },
    "button": {
        "prefix": "bu",
        "body": [
            "<button name='${1:btn}' onclick='${2:alert(\"クリック\")}'>${3:Button}</button>"
        ],
        "description": "button"
    },
    "text": {
        "prefix": "txt",
        "body": [
            "<input type='text' name='${1:txt}' placeholder='${2:入力欄}' ${3:require} />"
        ],
        "description": "text入力"
    },
    "textarea": {
        "prefix": "txa",
        "body": [
            "<textarea name='${1:txta}'></textarea>"
        ],
        "description": "複数行text入力"
    },
    "password": {
        "prefix": "pa",
        "body": [
            "<input type='password' name='${1:pass}' placeholder='${2:パスワード}' require />"
        ],
        "description": "password"
    },
    "checkbox": {
        "prefix": "ch",
        "body": [
            "<label for='${1:chk}'><input id='${1:chk}' type='checkbox' name='${1:chk}' value='${2:value}' />${2:value}</label>",
        ],
        "description": "checkbox"
    },
    "radio": {
        "prefix": "ra",
        "body": [
        "<label for='${1:rad}1' class='radio-inline'><input id='${1:rad}1' type='radio' name='${1:rad}' value='${2:value}1' checked />${2:value}1</label>",
      "<label for='${1:rad}2' class='radio-inline'><input id='${1:rad}2' type='radio' name='${1:rad}' value='${2:value}2' />${2:value}2</label>",
      "<label for='${1:rad}3' class='radio-inline'><input id='${1:rad}3' type='radio' name='${1:rad}' value='${2:value}3' />${2:value}3</label>",
        ],
        "description": "checkbox"
    },
    "select": {
        "prefix": "sel",
        "body": [
      "<select name='${1:sel}'>",
      "  <option value='${2:value}1'>${3:項目}1</option>",
      "  <option value='${2:value}2'>${3:項目}2</option>",
        "  <option value='${2:value}3'>${3:項目}3</option>",
      "</select>",
        ],
        "description": "select"
    },
    "range": {
        "prefix": "ran",
        "body": [
            "<input type='range' name='${1:range}' min='0' max='100' step='1' />"
        ],
        "description": "range picker"
    },
    "file": {
        "prefix": "file",
        "body": [
            "<input type='file' name='${1:file}' accept='${2:image/*,video/*,audio/*}' />"
        ],
        "description": "file"
    },
    "color picker": {
        "prefix": "color",
        "body": [
            "<input type='color' name='${1:color}' />"
        ],
        "description": "color picker"
    },
    "date picker": {
        "prefix": "dp",
        "body": [
            "<input type='date' name='${1:date}' />"
        ],
        "description": "date picker"
    },
    // メディア
    "img": {
        "prefix": "img",
        "body": [
            "<img src='${1:https://picsum.photos/300/300/?random}' />"
        ],
        "description": "img"
    },
    "audio": {
        "prefix": "au",
        "body": [
            "<audio src='${2:url}' controls></audio>"
        ],
        "description": "audio"
    },
    "video": {
        "prefix": "vi",
        "body": [
            "<video src='${2:url}' width='${3:640}' height='${4:480}' controls></video>"
        ],
        "description": "video"
    },
    "canvas": {
        "prefix": "ca",
        "body": [
            "<canvas width='${2:640}' height='${3:480}'></canvas>"
        ],
        "description": "canvas"
    },
    "iframe": {
        "prefix": "ifr",
        "body": [
            "<iframe src='${1:url}' width='${2:640}' height='${3:480}' seamless sandbox='allow-same-origin allow-scripts' ></iframe>"
        ],
        "description": "iframe"
    },
    "fetch": {
        "prefix": "fet",
        "body": [
            "const data = await fetch('${1:/api}', {",
            "  method: 'POST',",
            "  headers: {'Content-Type': 'application/json'},",
            "  body: JSON.stringify(${2:{data: 'data'\\}}),",
            "}).then(async (response) => {",
            "  return await response.json()",
            "})",
        ],
        "description": "fetch"
    },

    // DOM操作
    "element create": {
        "prefix": "ece",
        "body": [
            "document.createElement('${1:div}')"
        ],
        "description": "DOM作成"
    },
    "element remove": {
        "prefix": "erc",
        "body": [
            "${1:elem}.parentNode.removeChild(${1:elem})"
        ],
        "description": "DOM削除"
    },
    "element copy": {
        "prefix": "eco",
        "body": [
            "${elem}.cloneNode()"
        ],
        "description": "DOMのDeepコピー"
    },
    "element class": {
        "prefix": "ecln",
        "body": [
            "${elem}.className"
        ],
        "description": "DOMのclass"
    },
    "element style": {
        "prefix": "es",
        "body": [
            "${1:elem}.style.${2:attr}"
        ],
        "description": "DOMのstyle"
    },
    "element query": {
        "prefix": "eqs",
        "body": [
            "document.querySelector('${1:[name=\"txt\"]}')"
        ],
        "description": "DOM取得"
    },
    "element queryAll": {
        "prefix": "eqsa",
        "body": [
            "document.querySelectorAll('${1:div,img}')"
        ],
        "description": "全DOM取得"
    },
    "element append child after": {
        "prefix": "eac",
        "body": [
            "${1:target}.appendChild(${2:elem})"
        ],
        "description": "DOM追加(子要素末尾)"
    },
    // イベント
    "element event": {
        "prefix": "eae",
        "body": [
            "${1:elem}.addEventListener('${2:click}', (e) => {",
            "  e.preventDefault()",
        "  let ${1:elem} = e.target",
            "",
            "})",
        ],
        "description": "DOM event"
    },
    "element event monitor": {
        "prefix": "eem",
        "body": [
            "monitorEvents(${1:div,'click'})"
        ],
        "description": "DOM event監視"
    },
    "device motion event": {
        "prefix": "edev",
        "body": [
            "window.addEventListener('devicemotion', (event) => {",
        "  const accel = {x:event.acceleration.x,y:event.acceleration.y,z:event.acceleration.z,alpha:event.rotationRate.alpha,beta:event.rotationRate.beta,gamma:event.rotationRate.gamma}",
            "  ",
            "})",
            "window.addEventListener('deviceorientation', (event) => {",
        "  const ori = { alpha:event.alpha,beta:event.beta,gamma:event.gamma}",
            "  ",
            "})",
        ],
        "description": "deviceのモーションイベント"
    },
    "touch event": {
        "prefix": "etou",
        "body": [
            "${1:document}.addEventListener('touchstart', (e) => {",
        "  let touches = e.touches",
            "  for (let i = 0; i < touches.length; ++i) {",
            "    const touch = touches[i]",
            "    const pos = {x:touch.clientX,y:touch.clientY}",
            "  }",
            "})",
            "${1:document}.addEventListener('touchmove', (e) => {",
        "  const touches = e.touches",
        "  for (let i = 0; i < touches.length; ++i) {",
        "    const touch = touches[i]",
      "    const pos = {x:touch.clientX,y:touch.clientY}",
        "  }",
            "})",
            "${1:document}.addEventListener('touchend', (e) => {",
        "  const touches = e.touches",
        "  for (let i = 0; i < touches.length; ++i) {",
        "    const touch = touches[i]",
      "    const pos = {x:touch.clientX,y:touch.clientY}",
        "  }",
            "})",
        ],
        "description": "touchイベント"
    },
    "mouse event": {
        "prefix": "emou",
        "body": [
            "${1:document}.addEventListener('mousedown', (e) => {",
        "  const pos = {x:e.clientX,y:e.clientY}",
            "  ",
            "})",
            "${1:document}.addEventListener('mousemove', (e) => {",
          "  const pos = {x:e.clientX,y:e.clientY}",
            "  ",
            "})",
            "${1:document}.addEventListener('mouseup', (e) => {",
        "  const pos = {x:e.clientX,y:e.clientY}",
            "  ",
            "})",
        ],
        "description": "mouseイベント"
    },
    "gamepad event": {
        "prefix": "epad",
        "body": [
            "const gamepads = {}",
            "",
            "function gamepadHandler(event, connecting) {",
          "  const gamepad = event.gamepad",
            "",
        "  if (connecting) {",
        "    gamepads[gamepad.index] = gamepad",
        "  } else {",
        "    delete gamepads[gamepad.index]",
        "  }",
            "}",
            "",
            "window.addEventListener('gamepadconnected', (e) => gamepadHandler(e, true), false)",
            "window.addEventListener('gamepaddisconnected', (e) => gamepadHandler(e, false), false)",
        ],
        "description": "gamepadイベント"
    },
    "key event": {
        "prefix": "ekey",
        "body": [
            "document.addEventListener('keyup', (e) => {",
      "  const keycode = e.keyCode",
      "  // Enter",
      "  if (keycode == 13) {}",
      "  // Space",
      "  if (keycode == 32) {}",
      "  // ←,A",
      "  if (keycode == 37 || keycode == 65) {}",
      "  // →,D",
      "  if (keycode == 39 || keycode == 68) {}",
      "  // ↑,W",
      "  if (keycode == 38 || keycode == 87) {}",
      "  // ↓,S",
      "  if (keycode == 40 || keycode == 83) {}",
      "})",
        ],
        "description": "keyイベント"
    },
    // DOM animate
    "DOM animate": {
        "prefix": "eani",
        "body": [
            "${1:elem}.animate([",
            "  ${2:{opacity: 0, color: '#fff'\\}, {opacity: 1, color: '#000'\\}},",
            "],",
            "{duration: ${3:1000}, iterations: Infinity})",
        ],
        "description": "DOM animate"
    },

    // NodeJS
    "require": {
        "prefix": "re",
        "body": [
            "const ${1:val} = require('${2:module}')"
        ],
        "description": "require"
    },
    "node": {
        "prefix": "node",
        "body": [
            "const express = require('express')",
            "const bodyParser = require('body-parser')",
            "const app = express()",
            "",
            "process.on('uncaughtException', (err) => console.error(err))",
            "process.on('unhandledRejection', (err) => console.error(err))",
            "process.on('SIGINT', () => process.exit(1))",
            "",
            "app.use(express.static('dist'))",
            "app.use(bodyParser.urlencoded({extended: true}))",
            "app.use(bodyParser.json())",
            "",
            "app.get('/', (req, res) => {",
            "  res.json('hello world!!')",
            "})",
            "",
            "app.listen(3000, () => {",
        "  console.log('Access to http://localhost:3000')",
            "})",
        ],
        "description": "style"
    },
    // api
    "api": {
        "prefix": "api",
        "body": [
            "const { ${1:path} } = require('./routes')",
            "",
            "app.use(",
        "  '${2:/api}',",
        "  ${3:authenticate,}",
        "  express.Router()",
        "    .get('/${1:path}', ${1:path}.index)",
        "    .get('/${1:path}/:id', ${1:path}.show)",
        "    .post('/${1:path}', ${1:path}.create)",
        "    .put('/${1:path}/:id', ${1:path}.update)",
      "    .delete('/${1:path}/:id', ${1:path}.remove)",
            ")",
        ],
        "description": "api"
    },
    "Authenticate": {
        "prefix": "auth",
        "body": [
            "const passport = require('passport')",
            "const BearerStrategy = require('passport-http-bearer')",
            "passport.use(new BearerStrategy(function(token, done) {",
        "  models.User.findOne({token, deactivate: {$ne: true}}, function(err, user) {",
        "    if (err) return done(err)",
        "    if (!user) return done(null, false)",
        "    return done(null, user)",
        "  })",
            "}))",
            "const AnonymousStrategy = require('passport-anonymous')",
            "passport.use(new AnonymousStrategy())",
            "const authenticate = passport.authenticate('bearer', {session: false})",
            "const partialAuth = passport.authenticate(['bearer', 'anonymous'], {session: false})",
            "",
            "const crypto = require('crypto')",
            "const generateToken = () => {",
        "  const length = 32",
        "  const chars = 'abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789_-'",
          "  const rnd = crypto.randomBytes(length)",
        "  const ret = []",
        "  for (let i = 0; i < length; i++) {",
        "    ret.push(chars[rnd[i] % chars.length])",
        "  }",
        "  return ret.join('')",
            "})",
        ],
        "description": "Authenticate"
    },
    "CRUD import": {
        "prefix": "crudi",
        "body": [
            "'use strict'",
            "",
            "require('fs').readdirSync(__dirname).forEach(e => {",
        "  const name = /^([a-z]+)\\.js$/i.test(e) && RegExp.\\$1",
        "  if (name && name !== 'index') {",
        "    const route = require('./' + name)",
        "    module.exports[name] = route",
        "  }",
            "})",
        ],
        "description": "crudi"
    },
    "CRUD": {
        "prefix": "crud",
        "body": [
            "const { ${1:Model} } = require('../models')",
            "",
            "module.exports = {",
            "  index,",
            "  create,",
            "  show,",
            "  update,",
            "  remove,",
            "}",
            "",
            "async function index(req, res) {",
            "  const ${2:model}s = await ${1:Model}.find()",
            "  res.json(${2:model}s)",
            "}",
            "",
            "async function create(req, res) {",
            "  const exist = await ${1:Model}.findOne({${3:name}: req.body.${3:name}})",
            "  if (exist) return res.status(400).json({message: 'already exist'})",
            "  const ${2:model} = await ${1:Model}.create(req.body)",
            "  res.json(${2:model})",
            "}",
            "",
            "async function show(req, res) {",
            "  const ${2:model} = await ${1:Model}.findOne({_id: req.params.id})",
            "  if (${2:model} === null) return res.status(404).json({message: 'not found'})",
            "  res.json(${2:model})",
            "}",
            "",
            "async function update(req, res) {",
            "  let ${2:model} = await ${1:Model}.findOne({_id: req.params.id})",
            "  if (${2:model} === null) return res.status(404).json({message: 'not found'})",
            "  ${2:model} = await ${1:Model}.findByIdAndUpdate(${2:model}.id, {\\$set: req.body})",
            "  res.json(${2:model})",
            "}",
            "",
            "async function remove(req, res) {",
            "  let ${2:model} = await ${1:Model}.findOne({_id: req.params.id})",
            "  if (${2:model} === null) return res.status(404).json({message: 'not found'})",
        "  ${2:model} = await ${1:Model}.findByIdAndRemove(${2:model}.id)",
            "  res.json(${2:model})",
            "}",
        ],
        "description": "Create Read Update Delete"
    },
    // WebSocket
    "WebSocket Server": {
        "prefix": "wss",
        "body": [
            "const io = require('socket.io')(9000)",
            "",
            "io.on('connection', (socket) => {",
          "  socket.on('event', (msg) => {",
            "    io.emit('event', msg)",
          "  })",
            "})",
        ],
        "description": "WebSocket Server"
    },
    "WebSocket Client script": {
        "prefix": "jswsc",
        "body": [
            "<script src=\"node_modules/socket.io-client/dist/socket.io.js\"></script>",
        ],
        "description": "WebSocket Client script"
    },
    "WebSocket Client import": {
        "prefix": "imwsc",
        "body": [
            "import io from 'socket.io-client'",
        ],
        "description": "WebSocket Client script"
    },
    "WebSocket Client": {
        "prefix": "wsc",
        "body": [
            "const socket = io('http://localhost:9000')",
      "socket.on('connect', () => {",
      "  socket.on('event', (data) => {",
      "  console.log(data)",
      " })",
      "  socket.emit('event', {msg: 'メッセージ'})",
      "})",
        ],
        "description": "WebSocket Client"
    },
    // axios
    "Axios script": {
        "prefix": "jsax",
        "body": [
            "<script src=\"https://unpkg.com/axios/dist/axios.min.js\"></script>",
        ],
        "description": "axios script"
    },
    "Axios import": {
        "prefix": "imax",
        "body": [
            "import axios from 'axios'",
        ],
        "description": "axios import"
    },
    "Axios": {
        "prefix": "ax",
        "body": [
            "axios.${1:get}('${2:https://randomuser.me/api/}')",
        ],
        "description": "axios"
    },
    "Axios interceptors": {
        "prefix": "axin",
        "body": [
            "axios.interceptors.request.use(req => {",
        "  const token = localStorage.getItem('token')",
            "",
          "  if (token) {",
        "    // ユーザ認証トークン付与",
          "    req.headers.Authorization = `Bearer ${token}`",
          "  }",
        "  return req",
            "}, err => Promise.reject(err))",
            "",
            "axios.interceptors.response.use(res => res, err => {",
        "  if (axios.isCancel(err)) {",
        "    return Promise.reject({code: 999, message: 'cancel'})",
        "  }",
        "  if (err.response.status && err.response.status === 401) {",
        "    localStorage.setItem('token', '')",
        "  }",
        "  return Promise.reject(err.response || {})",
            "})",
            "",
            "// ユーザ登録時認証トークン保存",
        "//localStorage.setItem('token', token)",
        ],
        "description": "axios interceptors"
    },
    // mongodb
    "MongoDB": {
        "prefix": "mongo",
        "body": [
            "const mongoose = require('mongoose')",
            "mongoose.Promise = global.Promise",
        "mongoose.connect('mongodb://localhost/${1:dbname}')",
        "const models = require('./models')",
        ],
        "description": "MongoDB"
    },
    // mongoose models
    "Mongoose Models": {
        "prefix": "mongoi",
        "body": [
            "'use strict'",
            "",
            "require('fs').readdirSync(__dirname).forEach(e => {",
        "  const name = /^([a-z]+)\\.js$/i.test(e) && RegExp.\\$1",
        "  if (name && name !== 'index') {",
        "    const model = require('./' + name)",
        "    module.exports[model.modelName] = model",
        "  }",
            "})",
        ],
        "description": "Mongoose Models"
    },
    // mongoose model
    "Mongoose Model": {
        "prefix": "model",
        "body": [
            "const mongoose = require('mongoose')",
            "const Schema = mongoose.Schema",
            "const mongooseLeanVirtuals = require('mongoose-lean-virtuals')",
            "",
            "const schema = new Schema({",
            "  ${1:releation: {type: Schema.Types.ObjectId, ref: 'OtherSchema'\\},}",
            "}, {",
            "  timestamps: true,",
            "  toObject: {",
        "    virtuals: true,",
        "  },",
        "  toJSON: {",
      "    virtuals: true,",
        "    transform: (doc, m) => {",
      "      delete m.__v",
      "      return m",
      "    },",
        "  },",
            "})",
            "",
            "schema.pre('update', async function(next) {",
          "  this.options.runValidators = true",
        "  return next()",
            "})",
            "schema.pre('findOneAndUpdate', async function(next) {",
            "  this.options.runValidators = true",
          "  this.options.new = true",
        "  return next()",
            "})",
            "",
            "schema.plugin(mongooseLeanVirtuals)",
            "",
            "module.exports = mongoose.model('${2:SchemaName}', schema)",
        ],
        "description": "Mongoose Model"
    },
    "Mongo model index": {
        "prefix": "mmi",
        "body": [
            "schema.index({${1:loc: '2dsphere'}})",
        ],
        "description": "Mongo model index"
    },
    "Mongo model method": {
        "prefix": "mmm",
        "body": [
            "schema.method('${1:name}', function(${2:arg}) {",
            "",
            "})",
        ],
        "description": "Mongo model method"
    },
    "Mongo model static method": {
        "prefix": "mms",
        "body": [
            "schema.static('${1:name}', function(${2:arg}) {",
            "",
            ")",
        ],
        "description": "Mongo model static method"
    },
    "Mongo model virtual": {
        "prefix": "mmv",
        "body": [
            "schema.virtual('${1:field}').get(function() {",
            "",
            "})",
        ],
        "description": "Mongo model virtual"
    },
    // mongoose find
    "Mongo find": {
        "prefix": "mfi",
        "body": [
            "${1:Model}.find({${2}})",
        ],
        "description": "Mongo find"
    },
    "Mongo findOne": {
        "prefix": "mfio",
        "body": [
            "${1:Model}.findOne({${2}})",
        ],
        "description": "Mongo findOne"
    },
    "Mongo find select": {
        "prefix": "mfs",
        "body": [
            ".select('${1}')",
        ],
        "description": "Mongo find select"
    },
    "Mongo find populate": {
        "prefix": "mfp",
        "body": [
            ".populate({",
            "  path: '${1:model}',",
            "  select: '${2}',",
            "})",
        ],
        "description": "Mongo find populate"
    },
    "Mongo find sort": {
        "prefix": "mfso",
        "body": [
            ".sort('-${1:field}')",
        ],
        "description": "Mongo find sort"
    },
    "Mongo find skip": {
        "prefix": "mfsk",
        "body": [
            ".skip(${1:num})",
        ],
        "description": "Mongo find skip"
    },
    "Mongo find lean": {
        "prefix": "mfl",
        "body": [
            ".lean(${1:{ virtuals: ['${field}'] \\}})",
        ],
        "description": "Mongo find lean"
    },
    // mongoose aggregate
    "Mongo aggregate": {
        "prefix": "ma",
        "body": [
            "${1:Model}.aggregate([",
            "  ${2}",
            "])",
        ],
        "description": "Mongo aggregate"
    },
    "Mongo aggregate match": {
        "prefix": "mam",
        "body": [
            "{ \\$match: { ${1} } },",
        ],
        "description": "Mongo aggregate match"
    },
    "Mongo aggregate project": {
        "prefix": "map",
        "body": [
            "{ \\$project: { ${1} } },",
        ],
        "description": "Mongo aggregate project"
    },
    "Mongo aggregate group": {
        "prefix": "mag",
        "body": [
            "{ \\$group: { _id: '\\$${1:field}', count: { \\$sum: 1 } } },",
        ],
        "description": "Mongo aggregate group"
    },
    "Mongo aggregate lookup": {
        "prefix": "mal",
        "body": [
            "{ \\$lookup: {",
            "  from: '${1:collection}',",
      "  localField: '${2:field}',",
      "  foreignField: '_id',",
      "  as: '${2:field}',",
            "}},",
        ],
        "description": "Mongo aggregate lookup"
    },
    "Mongo aggregate lookup pipeline": {
        "prefix": "malp",
        "body": [
            "{ \\$lookup: {",
            "  from: '${1:collection}',",
      "  let: { ${2:field}: '\\$${2:field}' },",
      "  pipeline: [",
      "    {",
      "      \\$match: {",
      "        \\$expr: {",
      "          \\$eq: ['\\$_id', '\\$\\$${2:field}'],",
      "        },",
      "      },",
      "    },",
      "  ],",
      "  as: '${2:field}',",
            "}},",
        ],
        "description": "Mongo aggregate lookup select"
    },
    "Mongo aggregate sort": {
        "prefix": "mas",
        "body": [
        "{\\$sort: { ${1:count}: -1 }},",
        ],
        "description": "Mongo aggregate sort"
    },
    "Mongo update": {
        "prefix": "mu",
        "body": [
            ".update(${1:findCondition}, ${2:updateData})",
        ],
        "description": "Mongo update"
    },
    "Mongo findOneAndUpdate": {
        "prefix": "mu",
        "body": [
            ".findOneAndUpdate(${1:findCondition}, ${2:updateData}, {upsert: true, new: true, runValidators: true})",
        ],
        "description": "Mongo update"
    },
    "Mongo findOneAndRemove": {
        "prefix": "mfr",
        "body": [
            ".findOneAndRemove(${1:findCondition})",
        ],
        "description": "Mongo remove"
    },

    // React
    "style": {
        "prefix": "st",
        "body": [
            "style={{$1}}",
        ],
        "description": "style"
    },
    "className": {
        "prefix": "cln",
        "body": [
            "className={$1}",
        ],
        "description": "className"
    },
    "ReactComponent HOC": {
        "prefix": "hoc",
        "body": [
            "import React from 'react'",
            "import { connect } from 'react-redux'",
            "import { reduxForm } from 'redux-form'",
            "",
            "export default () => {",
        "  return (WrappedComponent) => {",
        "    return connect(",
      "      // propsに受け取るreducerのstate",
      "      () => ({}),",
      "      // propsに付与するactions",
      "      {}",
      "    )(reduxForm({",
            "      form: 'formName',",
      "      validate: values => {",
      "        const errors = {}",
      "        return errors",
      "      },",
            "    })(class extends React.Component {",
            "",
            "      componentDidMount() {}",
            "",
            "      handleSubmit = (values) => {}",
            "",
      "      render () {",
      "        const { handleSubmit } = this.props",
        "        return <WrappedComponent {...this.props} onSubmit={handleSubmit(this.handleSubmit)} />",
      "      }",
        "    }))",
        "  }",
            "}",
        ],
        "description": "React HOCコンポーネント"
    },
    "ReactComponent": {
        "prefix": "rea",
        "body": [
            "import React from 'react'",
            "",
            "export default class ${1:name} extends React.Component {",
          "",
        "  componentDidMount () {}",
            "",
        "  render () {",
        "    return <div/>",
        "  }",
            "}",
        ],
        "description": "Reactコンポーネント"
    },
    "StatelessReactComponent": {
        "prefix": "srea",
        "body": [
            "import React from 'react'",
            "",
            "const ${1:name} = (${2:props}) => {",
          "  return ${3:null}",
            "}",
            "",
            "export default ${1:name}",
        ],
        "description": "Stateless Functinal Reactコンポーネント"
    },
    "ref": {
        "prefix": "ref",
        "body": [
            "ref={${1:node} => this.${1:node} = ${1:node} }",
        ],
        "description": "ref"
    },
    // Redux
    "Redux Reducer": {
        "prefix": "redux",
        "body": [
            "import { SubmissionError } from 'redux-form'",
            "",
            "const INDEX = '${1:model}/INDEX'",
            "const SHOW = '${1:model}/SHOW'",
            "const CREATE = '${1:model}/CREATE'",
            "const UPDATE = '${1:model}/UPDATE'",
            "",
            "const initialState = {",
            "  ${1:model}s: [],",
            "  ${1:model}: null,",
            "}",
            "",
            "export default function reducer(state = initialState, action = {}) {",
            "  switch (action.type) {",
            "    case INDEX:",
            "      return {",
            "        ...state,",
            "        ${1:model}s: action.${1:model}s || state.${1:model}s,",
            "      }",
            "    case SHOW:",
            "      return {",
            "        ...state,",
            "        ${1:model}: action.${1:model} || state.${1:model},",
            "      }",
            "    case CREATE:",
            "      return {",
            "        ...state,",
            "        ${1:model}: action.${1:model} || state.${1:model},",
            "      }",
            "    case UPDATE:",
            "      return {",
            "        ...state,",
            "        ${1:model}: action.${1:model} || state.${1:model},",
            "      }",
            "    default:",
            "      return state",
            "  }",
            "}",
            "",
            "export function index(page) {",
            "  return (dispatch, getState, client) => {",
            "    return client",
            "      .get(`/api/${1:model}${page ? '?page=' + page : ''}`)",
            "      .then(res => res.data)",
      "      .then(${1:model}s => {",
            "        dispatch({type: INDEX, ${1:model}s})",
            "        return ${1:model}s",
            "      })",
            "  }",
            "}",
            "",
            "export function show(id) {",
            "  return (dispatch, getState, client) => {",
            "    return client",
            "      .get(`/api/${1:model}/\\${id}`)",
            "      .then(res => res.data)",
      "      .then(${1:model} => {",
            "        dispatch({type: SHOW, ${1:model}})",
            "        return ${1:model}",
            "      })",
            "  }",
            "}",
            "",
            "export function create(data) {",
            "  return (dispatch, getState, client) => {",
            "    return client",
            "      .post('/api/${1:model}', data)",
            "      .then(res => res.data)",
      "      .then(${1:model} => {",
            "        dispatch({type: CREATE, ${1:model}})",
            "        return ${1:model}",
            "      })",
            "      .catch(err => {",
      "        throw new SubmissionError({",
      "          _error: err && err.data ? err.data.message : 'エラーが発生しました',",
      "          status: err && err.data ? err.data.status : null,",
      "        })",
      "      })",
            "  }",
            "}",
            "",
            "export function update(id, data) {",
            "  return (dispatch, getState, client) => {",
            "    return client",
            "      .put(`/api/${1:model}/\\${id}`, data)",
            "      .then(res => res.data)",
      "      .then(${1:model} => {",
            "        dispatch({type: UPDATE, ${1:model}})",
            "        return ${1:model}",
            "      })",
            "      .catch(err => {",
      "        throw new SubmissionError({",
      "          _error: err && err.data ? err.data.message : 'エラーが発生しました',",
      "          status: err && err.data ? err.data.status : null,",
      "        })",
      "      })",
            "  }",
            "}",
            "",
            "export function remove(id) {",
            "  return (dispatch, getState, client) => {",
            "    return client.delete(`/api/${1:model}/\\${id}`)",
            "  }",
            "}",
        ],
        "description": "Redux Reducer"
    },
    // connect
    "React Redux connect": {
        "prefix": "dco",
        "body": [
            "@connect(",
      "  state => ({}),",
      "  {}",
      ")"
        ],
        "description": "React Redux connect"
    },
    "React Redux connect import": {
        "prefix": "imco",
        "body": [
            "import { connect } from 'react-redux'",
        ],
        "description": "React Redux connect import"
    },
    // React Router
    "React Router import": {
        "prefix": "imrou",
        "body": [
            "import { withRouter } from 'react-router'",
            "",
            "@withRouter",
        ],
        "description": "React Router import"
    },
    "React Router DOM Link": {
        "prefix": "lin",
        "body": [
            "<Link to='${1:/}'>${2:リンク}</Link>",
        ],
        "description": "React Router DOM Link"
    },
    "React Router DOM Link import": {
        "prefix": "imlin",
        "body": [
            "import { Link } from 'react-router-dom'",
        ],
        "description": "React Router DOM Link import"
    },
    // Material-UI
    "classes": {
        "prefix": "cls",
        "body": [
            "classes={$1}",
        ],
        "description": "classes"
    },
    "withStyles": {
        "prefix": "wst",
        "body": [
            "withStyles(theme => ({",
            "  ",
            "}), {withTheme: true})(${1:props => <div/>})",
        ],
        "description": "withStyles"
    },
    "withStyles decorators": {
        "prefix": "dwst",
        "body": [
            "@withStyles(theme => ({",
            "  ",
            "}), {withTheme: true})",
        ],
        "description": "withStyles decorators"
    },
    "withStyles import": {
        "prefix": "imwst",
        "body": [
            "import { withStyles } from '@material-ui/core'",
        ],
        "description": "withStyles import"
    },
    "AppBar import": {
        "prefix": "imapp",
        "body": [
            "import AppBar from '@material-ui/core/AppBar'",
            "import Toolbar from '@material-ui/core/Toolbar'",
        ],
        "description": "AppBar iport"
    },
    "AppBar": {
        "prefix": "app",
        "body": [
            "<AppBar position='static'>",
            "  <Toolbar>",
            "    ${1:タイトル}",
            "  </Toolbar>",
            "</AppBar>"
        ],
        "description": "AppBar"
    },
    "Avatar import": {
        "prefix": "imav",
        "body": [
            "import Avatar from '@material-ui/core/Avatar'"
        ],
        "description": "Avatar import"
    },
    "Avatar": {
        "prefix": "av",
        "body": [
            "<Avatar src='${1:https://unsplash.it/48/48?random}' />"
        ],
        "description": "Avatar"
    },
    "Badge import": {
        "prefix": "imba",
        "body": [
            "import Badge from '@material-ui/core/Badge'"
        ],
        "description": "Badge import"
    },
    "Badge": {
        "prefix": "ba",
        "body": [
            "<Badge badgeContent={${1:1}} color='primary'>$2</Badge>"
        ],
        "description": "Badge"
    },
    "BottomNavigation import": {
        "prefix": "imbot",
        "body": [
            "import BottomNavigation from '@material-ui/core/BottomNavigation'",
            "import BottomNavigationAction from '@material-ui/core/BottomNavigationAction'",
            "import FavoriteIcon from '@material-ui/icons/Favorite'",
        ],
        "description": "BottomNavigation import"
    },
    "BottomNavigation": {
        "prefix": "bot",
        "body": [
            "<BottomNavigation",
      "  value={this.state.selected || 0}",
      "  onChange={(event, selected) => this.setState({selected})}",
      "  showLabels",
      ">",
      "  <BottomNavigationAction label='${1:Favorites}' icon={<FavoriteIcon />} />",
      "  <BottomNavigationAction label='${1:Favorites}' icon={<FavoriteIcon />} />",
      "  <BottomNavigationAction label='${1:Favorites}' icon={<FavoriteIcon />} />",
      "</BottomNavigation>",
        ],
        "description": "BottomNavigation"
    },
    "Button import": {
        "prefix": "imbu",
        "body": [
            "import Button from '@material-ui/core/Button'"
        ],
        "description": "Button import"
    },
    "Button": {
        "prefix": "bu",
        "body": [
            "<Button size='medium' variant='contained' color='primary' onClick={() => {}}>${1:Button}</Button>"
        ],
        "description": "Button"
    },
    "Card import": {
        "prefix": "imca",
        "body": [
            "import Card from '@material-ui/core/Card'",
            "import CardActions from '@material-ui/core/CardActions'",
            "import CardContent from '@material-ui/core/CardContent'",
            "import CardMedia from '@material-ui/core/CardMedia'",
        ],
        "description": "Card import"
    },
    "Card": {
        "prefix": "ca",
        "body": [
            "<Card style={{maxWidth: 240}}>",
            "  <CardMedia style={{height: 120}} image='https://unsplash.it/240/120?random' />",
            "  <CardContent></CardContent>",
            "  <CardActions><Button size='small' variant='contained' color='primary' onClick={() => {}}>${1:Button}</Button></CardActions>",
            "</Card>"
        ],
        "description": "Card"
    },
    "Chip import": {
        "prefix": "imchi",
        "body": [
            "import Chip from '@material-ui/core/Chip'"
        ],
        "description": "Chip"
    },
    "Chip": {
        "prefix": "chi",
        "body": [
            "<Chip label='${2:テキスト}' onClick={() => {}} onDelete={() => {}} />"
        ],
        "description": "Chip"
    },
    "Dialog import": {
        "prefix": "imdia",
        "body": [
            "import Dialog from '@material-ui/core/Dialog'",
            "import DialogTitle from '@material-ui/core/DialogTitle'",
            "import DialogContent from '@material-ui/core/DialogContent'",
            "import DialogActions from '@material-ui/core/DialogActions'",
        ],
        "description": "Dialog import"
    },
    "Dialog": {
        "prefix": "dia",
        "body": [
            "<Dialog open={!!this.state.${1:open}} onClose={() => this.setState({${1:open}: false})}>",
            "  <DialogTitle>${2:タイトル}</DialogTitle>",
            "  <DialogContent></DialogContent>",
            "  <DialogActions><Button size='small' variant='contained' color='primary' onClick={() => this.setState({${1:open}: false})}>${3:Button}</Button></DialogActions>",
            "</Dialog>"
        ],
        "description": "Dialog"
    },
    "Drawer import": {
        "prefix": "imdra",
        "body": [
            "import Drawer from '@material-ui/core/Drawer'",
        ],
        "description": "Drawer import"
    },
    "Drawer": {
        "prefix": "dra",
        "body": [
            "<Button size='small' onClick={() => this.setState({${1:open}: true})}>メニュー</Button>",
            "<Drawer anchor='bottom' open={this.state.${1:open}} onClose={() => this.setState({${1:open}: false})}>",
      "  <div",
      "    tabIndex={0}",
      "    role='button'",
      "    onClick={() => this.setState({${1:open}: false})}>",
            "    <List>",
            "      {",
            "        ${2:[1, 2, 3]}.map(v =>",
            "          <ListItem button divider key={v} onClick={() => this.setState({${1:open}: false})}>",
            "            <ListItemText primary={`項目\\${v}`} secondary={`項目\\${v} サブ`} />",
            "          </ListItem>",
            "        )",
            "      }",
            "    </List>",
      "  </div>",
      "</Drawer>",
        ],
        "description": "Drawer"
    },
    "GridList import": {
        "prefix": "imgr",
        "body": [
            "import GridList from '@material-ui/core/GridList'",
            "import GridListTile from '@material-ui/core/GridListTile'",
        ],
        "description": "GridList import"
    },
    "GridList": {
        "prefix": "gr",
        "body": [
            "<GridList cellHeight={160} cols={3}>",
      "  {[",
      "    {img: 'https://unsplash.it/320/160?random&1', cols: 2},",
      "    {img: 'https://unsplash.it/160/160?random&2', cols: 1},",
      "    {img: 'https://unsplash.it/160/160?random&3', cols: 1},",
      "    {img: 'https://unsplash.it/320/160?random&4', cols: 2},",
      "  ].map((tile, idx) => (",
      "    <GridListTile key={idx} cols={tile.cols || 1}>",
      "      <img src={tile.img} />",
      "    </GridListTile>",
      "  ))}",
      "</GridList>",
        ],
        "description": "GridList"
    },
    "ExpansionPanel import": {
        "prefix": "imexp",
        "body": [
            "import ExpansionPanel from '@material-ui/core/ExpansionPanel'",
            "import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary'",
            "import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails'",
            "import ExpandMoreIcon from '@material-ui/icons/ExpandMore'",
        ],
        "description": "ExpansionPanel import"
    },
    "ExpansionPanel": {
        "prefix": "exp",
        "body": [
            "<ExpansionPanel",
      "  classes={{expanded: {margin: 0}}}>",
      "  <ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}>${1:サマリ}</ExpansionPanelSummary>",
            "  <ExpansionPanelDetails>${2:詳細}</ExpansionPanelDetails>",
      "</ExpansionPanel>",
        ],
        "description": "ExpansionPanel"
    },
    "List import": {
        "prefix": "imli",
        "body": [
            "import List from '@material-ui/core/List'",
            "import ListItem from '@material-ui/core/ListItem'",
            "import ListItemText from '@material-ui/core/ListItemText'",
        ],
        "description": "List import"
    },
    "List": {
        "prefix": "li",
        "body": [
            "<List>",
            "  {",
            "    ${1:[1, 2, 3]}.map(v =>",
            "      <ListItem button divider key={v} onClick={() => {}}>",
            "        <ListItemText primary={`項目\\${v}`} secondary={`項目\\${v} サブ`} />",
            "      </ListItem>",
            "    )",
            "  }",
            "</List>",
        ],
        "description": "List"
    },
    "Menu import": {
        "prefix": "imme",
        "body": [
            "import Menu from '@material-ui/core/Menu'",
            "import MenuItem from '@material-ui/core/MenuItem'",
        ],
        "description": "Menu"
    },
    "Menu": {
        "prefix": "me",
        "body": [
            "<Button size='small' onClick={e => this.setState({anchor: e.currentTarget})}>${1:メニュー}</Button>",
            "<Menu",
            "  open={!!this.state.anchor}",
            "  anchorEl={this.state.anchor}",
            "  onClose={() => this.setState({anchor: null})}",
            "  MenuListProps={{style: {padding: 0}}}",
            ">",
            "  {",
            "    ${2:[1, 2, 3]}.map(v =>",
            "      <MenuItem key={v} onClick={() => {}}>{`項目\\${v}`}</MenuItem>",
            "    )",
            "  }",
            "</Menu>",
        ],
        "description": "Menu"
    },
    "Paper import": {
        "prefix": "impa",
        "body": [
            "import Paper from '@material-ui/core/Paper'"
        ],
        "description": "Paper import"
    },
    "Paper": {
        "prefix": "pa",
        "body": [
            "<Paper elevation={4}>$1</Paper>"
        ],
        "description": "Paper"
    },
    "Datetime Picker": {
        "prefix": "dtpi",
        "body": [
            "<TextField type='datetime-local' defaultValue={new Date().toISOString().slice(0, 16)} InputLabelProps={{shrink: true}}/>"
        ],
        "description": "Datetime Picker"
    },
    "CircularProgress import": {
        "prefix": "imcpg",
        "body": [
            "import CircularProgress from '@material-ui/core/CircularProgress'",
        ],
        "description": "CircularProgress import"
    },
    "CircularProgress": {
        "prefix": "cpg",
        "body": [
            "<CircularProgress size={${1:30}} thickness={${2:3}} />",
        ],
        "description": "CircularProgress"
    },
    "LinearProgress import": {
        "prefix": "imlpg",
        "body": [
            "import LinearProgress from '@material-ui/core/LinearProgress'",
        ],
        "description": "LinearProgress import"
    },
    "LinearProgress": {
        "prefix": "lpg",
        "body": [
            "<LinearProgress />",
        ],
        "description": "LinearProgress"
    },
    "Checkbox import": {
        "prefix": "imch",
        "body": [
            "import Checkbox from '@material-ui/core/Checkbox'",
            "import FormControlLabel from '@material-ui/core/FormControlLabel'",
        ],
        "description": "Checkbox"
    },
    "Checkbox": {
        "prefix": "ch",
        "body": [
            "<FormControlLabel",
      "  control={",
            "    <Checkbox",
            "      checked={this.state.${1:checked}}",
            "      onChange={(e) => this.setState({${1:checked}: e.target.checked})} />",
            "  }",
            "  label='${2:ラベル}'",
            "/>",
        ],
        "description": "Checkbox"
    },
    "Radio import": {
        "prefix": "imra",
        "body": [
            "import Radio from '@material-ui/core/Radio'",
            "import FormControlLabel from '@material-ui/core/FormControlLabel'",
        ],
        "description": "Radio"
    },
    "Radio": {
        "prefix": "ra",
        "body": [
            "<FormControlLabel value='${1:value}' control={<Radio />} label='${2:ラベル}' />",
        ],
        "description": "Radio"
    },
    "Switch import": {
        "prefix": "imswi",
        "body": [
            "import Switch from '@material-ui/core/Switch'",
            "import FormControlLabel from '@material-ui/core/FormControlLabel'",
        ],
        "description": "Switch"
    },
    "Switch": {
        "prefix": "swi",
        "body": [
            "<FormControlLabel",
            "  control={",
            "    <Switch",
            "      checked={this.state.${1:checked}}",
            "      onChange={(e) => this.setState({${1:checked}: e.target.checked})}",
            "    />",
      "  }",
      "  label='${2:ラベル}'",
      "/>",
        ],
        "description": "Switch"
    },
    "Select import": {
        "prefix": "imse",
        "body": [
            "import FormControl from '@material-ui/core/FormControl'",          
            "import InputLabel from '@material-ui/core/InputLabel'",
            "import MenuItem from '@material-ui/core/MenuItem'",
            "import Select from '@material-ui/core/Select'",
        ],
        "description": "Select"
    },
    "Select": {
        "prefix": "se",
        "body": [
            "<FormControl>",
            "  <InputLabel htmlFor='${1:inputLabel}'>選択項目</InputLabel>",
            "  <Select",
            "    value={this.state.${2:count} || ''}",
            "    onChange={(e) => this.setState({${2:count}: e.target.value})}",
            "    inputProps={{",
            "      id: '${1:inputLabel}',",
            "    }}",
            "  >",
      "    <MenuItem value={1}>選択肢1</MenuItem>",
      "    <MenuItem value={2}>選択肢2</MenuItem>",
      "    <MenuItem value={3}>選択肢3</MenuItem>",
      "  </Select>",
      "</FormControl>",
        ],
        "description": "Select"
    },
    "Snackbar import": {
        "prefix": "imsna",
        "body": [
            "import Snackbar from '@material-ui/core/Snackbar'",
        ],
        "description": "Snackbar import"
    },
    "Snackbar": {
        "prefix": "sna",
        "body": [
            "<Snackbar",
            "  open={!!this.state.${1:msg}}",
            "  anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}",
            "  onClose={() => this.setState({${1:msg}: null})}",
            "  autoHideDuration={2000}",
            "  message={this.state.${1:msg}} />",
        ],
        "description": "Snackbar"
    },
    "Stepper import": {
        "prefix": "imstep",
        "body": [
            "import Stepper from '@material-ui/core/Stepper'",
            "import Step from '@material-ui/core/Step'",
            "import StepLabel from '@material-ui/core/StepLabel'",
        ],
        "description": "Stepper import"
    },
    "Stepper": {
        "prefix": "step",
        "body": [
                "<div>",
        "  <Stepper activeStep={this.state.activeStep || 0} ${1:orientation='vertical'}>",
        "    { ${2:['項目1', '項目2', '項目3']}.map(label => (",
        "      <Step key={label}>",
        "        <StepLabel>{label}</StepLabel>",
        "      </Step>",
        "    ))}",
        "  </Stepper>",
        "  <Button size='medium' variant='raised' color='primary' onClick={() => this.setState({activeStep: (this.state.activeStep || 0) - 1})} disabled={(this.state.activeStep || 0) === 0}>前へ</Button>",
        "  <Button size='medium' variant='raised' color='primary' onClick={() => this.setState({activeStep: (this.state.activeStep || 0) + 1 })} disabled={(this.state.activeStep || 0) === ${2:['項目1', '項目2', '項目3']}.length - 1}>次へ</Button>",
                "</div>",
        ],
        "description": "Stepper"
    },
    "MobileStepper import": {
        "prefix": "immstep",
        "body": [
            "import MobileStepper from '@material-ui/core/MobileStepper'",
            "import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft'",
            "import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight'",
        ],
        "description": "MobileStepper import"
    },
    "MobileStepper": {
        "prefix": "mstep",
        "body": [
            "<MobileStepper",
      "  steps={${1:3}}",
      "  position='static'",
      "  activeStep={this.state.activeStep || 0}",
      "  nextButton={",
      "    <Button size='small' onClick={() => this.setState({activeStep: (this.state.activeStep || 0) + 1})} disabled={(this.state.activeStep || 0) === ${1:3} - 1}>",
      "      次へ",
      "      <KeyboardArrowRight />",
      "    </Button>",
      "  }",
      "  backButton={",
      "    <Button size='small' onClick={() => this.setState({activeStep: (this.state.activeStep || 0) - 1})} disabled={(this.state.activeStep || 0) === 0}>",
      "      <KeyboardArrowLeft />",
      "      前へ",
      "    </Button>",
      "  }",
      "/>",
        ],
        "description": "MobileStepper"
    },
    "Table import": {
        "prefix": "imta",
        "body": [
            "import Table from '@material-ui/core/Table'",
            "import TableBody from '@material-ui/core/TableBody'",
            "import TableCell from '@material-ui/core/TableCell'",
            "import TableHead from '@material-ui/core/TableHead'",
            "import TableRow from '@material-ui/core/TableRow'",
        ],
        "description": "Table import"
    },
    "Table": {
        "prefix": "ta",
        "body": [
            "<Table>",
      "  <TableHead>",
      "    <TableRow>",
      "      <TableCell>名前</TableCell>",
      "      <TableCell>年齢</TableCell>",
      "      <TableCell>身長</TableCell>",
      "    </TableRow>",
      "  </TableHead>",
      "  <TableBody>",
      "    {[",
      "      {name: '太郎', age: 20, height: '180cm'},",
      "      {name: '次郎', age: 18, height: '170cm'},",
      "      {name: '花子', age: 16, height: '160cm'},",
      "    ].map(((n, idx) => (",
      "      <TableRow key={idx}>",
      "        <TableCell component='th' scope='row'>{n.name}</TableCell>",
      "        <TableCell>{n.age}</TableCell>",
      "        <TableCell>{n.height}</TableCell>",
      "      </TableRow>",
      "    )",
      "    )}",
      "  </TableBody>",
      "</Table>",
        ],
        "description": "Table"
    },
    "TextField import": {
        "prefix": "imtx",
        "body": [
            "import TextField from '@material-ui/core/TextField'",
        ],
        "description": "TextField import"
    },
    "TextField": {
        "prefix": "tx",
        "body": [
            "<TextField label='${1:入力欄}' margin='normal' ${2:multiline rows='4'} ${3:error} />",
        ],
        "description": "TextField"
    },
    "Tab import": {
        "prefix": "imtab",
        "body": [
            "import Tabs from '@material-ui/core/Tabs'",
            "import Tab from '@material-ui/core/Tab'",
        ],
        "description": "Tab import"
    },
    "Tab": {
        "prefix": "tab",
        "body": [
            "<div>",
            "  <AppBar position='static'>",
            "    <Tabs value={this.state.${1:selected}} onChange={(e, ${1:selected}) => this.setState({${1:selected}})} fullWidth >",
            "      <Tab label='項目1' />",
            "      <Tab label='項目2' />",
            "      <Tab label='項目3' />",
            "    </Tabs>",
            "  </AppBar>",
            "  {this.state.${1:selected} === 0 && <div>項目1 コンテンツ</div>}",
            "  {this.state.${1:selected} === 1 && <div>項目2 コンテンツ</div>}",
            "  {this.state.${1:selected} === 2 && <div>項目3 コンテンツ</div>}",
            "</div>",
        ],
        "description": "Tab"
    },
    "Tooltip import": {
        "prefix": "imtip",
        "body": [
            "import Tooltip from '@material-ui/core/Tooltip'",
        ],
        "description": "Tooltip import"
    },
    "Tooltip": {
        "prefix": "tip",
        "body": [
            "<Tooltip title='${1:説明}'>",
            "  <Button size='small' onClick={() => {}}>ボタン</Button>",
            "</Tooltip>",
        ],
        "description": "Tooltip"
    },
    // Material-Icon
    "Material Icon import": {
        "prefix": "imic",
        "body": [
            "import ${1:Name} from '@material-ui/icons/${1:Name}'",
        ],
        "description": "Material Icon import"
    },
    // Redux Form
    "ReduxForm decorators": {
        "prefix": "drfo",
        "body": [
            "@reduxForm({",
            "  form: '${1:formName}',",
            "  validate: values => {",
      "    const errors = {}",
      "    return errors",
      "  },",
            "})",
        ],
        "description": "ReduxForm decorators"
    },
    "ReduxForm import": {
        "prefix": "imrfo",
        "body": [
            "import { reduxForm } from 'redux-form'",
        ],
        "description": "ReduxForm import"
    },
    "Redux form": {
        "prefix": "rfo",
        "body": [
            "<form onSubmit={this.props.handleSubmit(${1:(values) => null})}>",
            "</form>",
        ],
        "description": "Redux form"
    },
    "Field": {
        "prefix": "rfi",
        "body": [
            "<Field name='${1:name}' component={${2:component}} input meta />",
        ],
        "description": "Field"
    },
    "Field import": {
        "prefix": "imrfi",
        "body": [
            "import { Field } from 'redux-form'",
        ],
        "description": "Field import"
    },
    "FieldArray": {
        "prefix": "rfia",
        "body": [
            "<FieldArray name='${1:name}' component={${2:component}} />",
        ],
        "description": "FieldArray"
    },
    "FieldArray import": {
        "prefix": "rfia",
        "body": [
            "import { FieldArray } from 'redux-form'",
        ],
        "description": "FieldArray import"
    },
    // Styled Components
    "Styled Components import": {
        "prefix": "sc",
        "body": [
            "import styled from 'styled-components'",
        ],
        "description": "Styled Components import"
    },
    "Styled Components": {
        "prefix": "sc",
        "body": [
            "const Styled${1:MuiComponent} = styled(${1:MuiComponent})`",
        "  && {",
        "    ${1:style}",
        "  }",
            "`",
        ],
        "description": "Styled Components"
    },
    // レイアウト
    "flexbox": {
        "prefix": "fl",
        "body": [
            "display: flex;",
            "flex-direction: ${1:row,row-reverse,column,column-reverse};",
        "justify-content: ${2:flex-start,flex-end,space-between,space-around};",
            "align-items: ${3:stretch,flex-start,flex-end,center,base-line};",
            "flex-wrap: ${4:nowrap,wrap,wrap-reverse};",
        ],
        "description": "flexbox"
    },
    "grid": {
        "prefix": "gr",
        "body": [
            "display: inline-grid;",
            "grid-template-rows: ${1:100px 50px};",
        "grid-template-columns: ${2:50px 100px 50px};",
        ],
        "description": "grid"
    },
    "grid-item": {
        "prefix": "gri",
        "body": [
            "grid-row: ${1:1 / 2};",
        "grid-column: ${2:2 / 3};",
        ],
        "description": "grid-item"
    },
    // レスポンシブ
    "media query": {
        "prefix": "me",
        "body": [
            "@media screen and (max-width: 768px) {",
        "  $1",
            "}",
        ],
        "description": "レスポンシブレイアウト"
    },
    // スティッキー要素
    "sticky": {
        "prefix": "sti",
        "body": [
            "position: sticky;",
        ],
        "description": "スティッキー"
    },
    // マウスオーバー
    "hover": {
        "prefix": "ho",
        "body": [
            "&:hover {",
        "  $1",
            "}",
        ],
        "description": "マウスオーバー"
    },
    // 余白
    "padding": {
        "prefix": "p",
        "body": [
            "padding: ${1:1rem};",
        ],
        "description": "パディング"
    },
    "margin": {
        "prefix": "m",
        "body": [
            "margin: ${1:1rem};",
        ],
        "description": "マージン"
    },
    // 背景
    "background-color": {
        "prefix": "bgc",
        "body": [
            "background-color: ${1:#888};",
        ],
        "description": "背景色"
    },
    "background-image": {
        "prefix": "bgi",
        "body": [
            "background: url('${1:https://unsplash.it/300/300?random}');",
        "background-size: ${1:100}px ${2:50}px;",
          "width: ${1:100}px;",
        "height: ${2:50}px;",
        ],
        "description": "背景画像"
    },
    "background-blend-mode": {
        "prefix": "bgb",
        "body": [
            "background: url('${1:https://unsplash.it/300/300?random=1}'),url('${2:https://unsplash.it/300/300?random=2}');",
            "background-blend-mode: ${1:multiply,darken,lighten,screen,overlay,difference};",
        ],
        "description": "背景画像ブレンディング"
    },
    "linear-gradient": {
        "prefix": "grl",
        "body": [
            "background: linear-gradient(${1:45}deg,${2:red,orange,yellow,green,blue,purple});",
        ],
        "description": "線形グラデーション"
    },
    "radial-gradient": {
        "prefix": "grc",
        "body": [
            "background: radial-gradient(center,circle,${1:red,orange,yellow,green,blue,purple});",
        ],
        "description": "円形グラデーション"
    },
    // 位置、サイズ
    "left": {
        "prefix": "le",
        "body": [
            "left: ${1:4px,0.01vw};",
        ],
        "description": "横位置"
    },
    "top": {
        "prefix": "to",
        "body": [
            "top: ${1:4px,0.01vh};",
        ],
        "description": "縦位置"
    },
    "width": {
        "prefix": "w",
        "body": [
            "width: ${1:30%};",
        ],
        "description": "幅"
    },
    "height": {
        "prefix": "h",
        "body": [
            "height: ${1:30%};",
        ],
        "description": "高さ"
    },
    // 文字
    "color": {
        "prefix": "c",
        "body": [
            "color: ${1:#888};",
        ],
        "description": "文字色"
    },
    "font-size": {
        "prefix": "fs",
        "body": [
            "font-size: ${1:1.2rem};",
        ],
        "description": "文字サイズ"
    },
    "font-weight": {
        "prefix": "fw",
        "body": [
            "font-weight: ${1:normal, bold, 500};",
        ],
        "description": "文字サイズ"
    },
    "text-decoration": {
        "prefix": "td",
        "body": [
            "text-decoration: ${1:none,overline,underline,line-through};",
        ],
        "description": "文字装飾"
    },
    "WebFont": {
        "prefix": "wf",
        "body": [
            "@font-face {",
            "  src: url('${1:fontname}.ttf') format('truetype');",
            "  font-family: '${1:fontname}';",
            "}",
        ],
        "description": "WebFont"
    },
    // 影
    "text-shadow": {
        "prefix": "ts",
        "body": [
            "text-shadow: ${1:1rem 1rem 1rem rgba(0,0,0,1)};",
        ],
        "description": "テキスト影"
    },
    "box-shadow:": {
        "prefix": "bs",
        "body": [
            "text-shadow: ${1:1rem 1rem 1rem 1rem rgba(0,0,0,0.8)};",
        ],
        "description": "DOM影"
    },
    // 領域内表示方法
    "overflow": {
        "prefix": "o",
        "body": [
            "overflow: ${1:visible,hidden,scroll,auto};",
        ],
        "description": "領域外表示"
    },
    "overflow-wrap": {
        "prefix": "ow",
        "body": [
            "word-wrap: ${1:normal,break-word};",
            "overflow-wrap: ${1:normal,break-word};",
        ],
        "description": "領域外の折り返し表示"
    },
    "white-space": {
        "prefix": "ws",
        "body": [
            "white-space: ${1:normal,pre,no-wrap};",
        ],
        "description": "改行文字解釈"
    },
    // ボーダー
    "border": {
        "prefix": "b",
        "body": [
            "border: '${1:solid,dashed,groovy,ridge,inset,outset} ${2:0.1rem} ${3:black}';",
        ],
        "description": "ボーダー"
    },
    "border-radius": {
        "prefix": "br",
        "body": [
            "border-radius: ${1:1rem};",
        ],
        "description": "角丸ボーダー"
    },
    // クリッピング
    "clip-path": {
        "prefix": "cli",
        "body": [
            "clip-path: ${1:circle(50% at 50% 50%),polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%),polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%),polygon(0% 0%, 100% 0%, 100% 75%, 75% 75%, 75% 100%, 50% 75%, 0% 75%)};",
        ],
        "description": "クリッピング(円、星、六角形、吹き出し)"
    },
    // フィルター
    "css filter": {
        "prefix": "fil",
        "body": [
            "filter: ${1:grayscale(50%),blur(50%),,brightness(50%),contrast(50%),invert(50%),saturate(50%),sepia(50%)};",
        ],
        "description": "CSSフィルター"
    },
    // 幾何変換
    "transform": {
        "prefix": "trf",
        "body": [
            "transform: ${1:translate(0rem, 0rem) rotate(0deg) scale(1.0)};",
        ],
        "description": "幾何変換"
    },
    "transform3d": {
        "prefix": "trf",
        "body": [
            "transform: ${1:translate3d(0rem,0rem,0rem) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale3d(${1:1.0},${1:1.0},${1:1.0})};",
        ],
        "description": "3D幾何変換"
    },
    // 透明度
    "opacity": {
        "prefix": "op",
        "body": [
            "opacity:@val;",
        ],
        "description": "透明度"
    },
    // トランジション
    "transition": {
        "prefix": "tr",
        "body": [
            "transition: all ${1:linear,ease,ease-in,ease-out,ease-in-out,steps(5,start)} ${2:1}s;",
        ],
        "description": "CSS動的変化時のアニメーション"
    },
    // キーフレームアニメーション
    "keyframe animation": {
        "prefix": "ani",
        "body": [
            "@keyframes ${1:name} {",
            "  0% {}",
            "  100% {}",
            "}",
            "animation: ${1:name} ${2:1}s ${3:linear,ease,ease-in,ease-out,ease-in-out,steps(5,start)} ${4:infinite} ${5:alternative};",
        ],
        "description": "キーフレームアニメーション"
    },
}