procon_bypass_man-web 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.babelrc +6 -0
- data/.circleci/config.yml +73 -0
- data/.gitignore +12 -0
- data/.rspec +1 -0
- data/.rubocop.yml +26 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +97 -0
- data/LICENSE.txt +21 -0
- data/Procfile +2 -0
- data/README.md +43 -0
- data/Rakefile +4 -0
- data/bin/console +15 -0
- data/bin/pbm_web +7 -0
- data/bin/setup +8 -0
- data/jest.config.ts +194 -0
- data/lib/procon_bypass_man/web.rb +20 -0
- data/lib/procon_bypass_man/web/db.rb +33 -0
- data/lib/procon_bypass_man/web/migration/001_create_settings_table.sql +4 -0
- data/lib/procon_bypass_man/web/models/base_model.rb +47 -0
- data/lib/procon_bypass_man/web/models/setting.rb +22 -0
- data/lib/procon_bypass_man/web/public/bundle.js +2 -0
- data/lib/procon_bypass_man/web/public/bundle.js.LICENSE.txt +57 -0
- data/lib/procon_bypass_man/web/public/index.html +1 -0
- data/lib/procon_bypass_man/web/server.rb +139 -0
- data/lib/procon_bypass_man/web/setting_parser.rb +190 -0
- data/lib/procon_bypass_man/web/storage.rb +25 -0
- data/lib/procon_bypass_man/web/version.rb +7 -0
- data/package.json +48 -0
- data/procon_bypass_man-web.gemspec +36 -0
- data/src/app.tsx +5 -0
- data/src/components/button_setting.tsx +142 -0
- data/src/components/buttons_modal.tsx +110 -0
- data/src/components/buttons_setting.tsx +67 -0
- data/src/components/installable_macros.tsx +58 -0
- data/src/components/installable_modes.tsx +57 -0
- data/src/components/macro_settings.tsx +85 -0
- data/src/components/mode_settings.tsx +62 -0
- data/src/contexts/buttons_setting.ts +2 -0
- data/src/index.html +11 -0
- data/src/lib/button_state.test.ts +110 -0
- data/src/lib/button_state.ts +52 -0
- data/src/lib/button_state_diff.test.ts +123 -0
- data/src/lib/button_state_diff.ts +63 -0
- data/src/lib/buttons_setting_converter.test.ts +185 -0
- data/src/lib/buttons_setting_converter.ts +107 -0
- data/src/lib/http_client.ts +93 -0
- data/src/pages/bpm_page.tsx +92 -0
- data/src/pages/buttons_setting_page.tsx +281 -0
- data/src/pages/global_setting_page.tsx +83 -0
- data/src/pages/home.tsx +17 -0
- data/src/pages/recoding_mode_page.tsx +15 -0
- data/src/pages/top.tsx +107 -0
- data/src/reducers/layer_reducer.ts +120 -0
- data/src/types/button.ts +2 -0
- data/src/types/buttons_setting_type.ts +63 -0
- data/src/types/layer_key.ts +2 -0
- data/src/types/pbm_stats.ts +1 -0
- data/src/types/plugin.ts +43 -0
- data/tmp/.keep +0 -0
- data/tsconfig.json +75 -0
- data/webpack.config.js +56 -0
- data/yarn.lock +6815 -0
- metadata +150 -0
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            module ProconBypassMan
         | 
| 2 | 
            +
              module Web
         | 
| 3 | 
            +
                class Storage
         | 
| 4 | 
            +
                  def self.instance
         | 
| 5 | 
            +
                    new
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def root_path
         | 
| 9 | 
            +
                    ProconBypassMan::Web::Setting.find_or_create_by&.root_path
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def root_path=(value)
         | 
| 13 | 
            +
                    ProconBypassMan::Web::Setting.find_or_create_by&.update!(root_path: value)
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def setting_path
         | 
| 17 | 
            +
                    ProconBypassMan::Web::Setting.find_or_create_by&.setting_path
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def setting_path=(value)
         | 
| 21 | 
            +
                    ProconBypassMan::Web::Setting.find_or_create_by&.update!(setting_path: value)
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
    
        data/package.json
    ADDED
    
    | @@ -0,0 +1,48 @@ | |
| 1 | 
            +
            {
         | 
| 2 | 
            +
              "name": "procon_bypass_man-web",
         | 
| 3 | 
            +
              "version": "1.0.0",
         | 
| 4 | 
            +
              "main": "index.js",
         | 
| 5 | 
            +
              "license": "MIT",
         | 
| 6 | 
            +
              "devDependencies": {
         | 
| 7 | 
            +
                "@babel/core": "^7.14.8",
         | 
| 8 | 
            +
                "@babel/preset-env": "^7.14.9",
         | 
| 9 | 
            +
                "@babel/preset-react": "^7.14.5",
         | 
| 10 | 
            +
                "@babel/preset-typescript": "^7.14.5",
         | 
| 11 | 
            +
                "@emotion/react": "^11.4.0",
         | 
| 12 | 
            +
                "@types/jest": "^26.0.24",
         | 
| 13 | 
            +
                "@types/js-yaml": "^4.0.2",
         | 
| 14 | 
            +
                "@types/lodash": "^4.14.172",
         | 
| 15 | 
            +
                "@types/md5": "^2.3.1",
         | 
| 16 | 
            +
                "@types/react": "^17.0.11",
         | 
| 17 | 
            +
                "@types/react-dom": "^17.0.8",
         | 
| 18 | 
            +
                "babel-jest": "^27.0.6",
         | 
| 19 | 
            +
                "babel-loader": "^8.2.2",
         | 
| 20 | 
            +
                "deep-object-diff": "^1.1.0",
         | 
| 21 | 
            +
                "file-loader": "^6.2.0",
         | 
| 22 | 
            +
                "html-webpack-plugin": "^5.3.1",
         | 
| 23 | 
            +
                "jest": "^27.0.6",
         | 
| 24 | 
            +
                "js-yaml": "^4.1.0",
         | 
| 25 | 
            +
                "lodash": "^4.17.21",
         | 
| 26 | 
            +
                "md5": "^2.3.0",
         | 
| 27 | 
            +
                "ts-jest": "^27.0.4",
         | 
| 28 | 
            +
                "ts-loader": "^9.2.3",
         | 
| 29 | 
            +
                "ts-node": "^10.1.0",
         | 
| 30 | 
            +
                "typescript": "^4.3.4",
         | 
| 31 | 
            +
                "webpack": "^5.39.1",
         | 
| 32 | 
            +
                "webpack-cli": "^4.7.2",
         | 
| 33 | 
            +
                "webpack-dev-server": "^3.11.2"
         | 
| 34 | 
            +
              },
         | 
| 35 | 
            +
              "scripts": {
         | 
| 36 | 
            +
                "build": "webpack",
         | 
| 37 | 
            +
                "server": "webpack serve",
         | 
| 38 | 
            +
                "release-build": "NODE_ENV=production webpack",
         | 
| 39 | 
            +
                "test": "jest"
         | 
| 40 | 
            +
              },
         | 
| 41 | 
            +
              "dependencies": {
         | 
| 42 | 
            +
                "@types/react-router-dom": "^5.1.7",
         | 
| 43 | 
            +
                "axios": "^0.21.1",
         | 
| 44 | 
            +
                "react": "^17.0.2",
         | 
| 45 | 
            +
                "react-dom": "^17.0.2",
         | 
| 46 | 
            +
                "react-router-dom": "^5.2.0"
         | 
| 47 | 
            +
              }
         | 
| 48 | 
            +
            }
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative "lib/procon_bypass_man/web/version"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Gem::Specification.new do |spec|
         | 
| 6 | 
            +
              spec.name          = "procon_bypass_man-web"
         | 
| 7 | 
            +
              spec.version       = ProconBypassMan::Web::VERSION
         | 
| 8 | 
            +
              spec.authors       = ["jiikko"]
         | 
| 9 | 
            +
              spec.email         = ["n905i.1214@gmail.com"]
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              spec.summary       = "PBM for web"
         | 
| 12 | 
            +
              spec.description   = spec.summary
         | 
| 13 | 
            +
              spec.homepage      = "https://github.com/splaplapla/procon_bypass_man-web"
         | 
| 14 | 
            +
              spec.license       = "MIT"
         | 
| 15 | 
            +
              spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              spec.metadata["homepage_uri"] = spec.homepage
         | 
| 18 | 
            +
              spec.metadata["source_code_uri"] = spec.homepage
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              # Specify which files should be added to the gem when it is released.
         | 
| 21 | 
            +
              # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
         | 
| 22 | 
            +
              spec.files = Dir.chdir(File.expand_path(__dir__)) do
         | 
| 23 | 
            +
                `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
              spec.bindir        = "exe"
         | 
| 26 | 
            +
              spec.executables   = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
         | 
| 27 | 
            +
              spec.require_paths = ["lib"]
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              # Uncomment to register a new dependency of your gem
         | 
| 30 | 
            +
              spec.add_dependency "sinatra"
         | 
| 31 | 
            +
              spec.add_dependency "webrick"
         | 
| 32 | 
            +
              spec.add_dependency "sqlite3"
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              # For more information and examples about making a new gem, checkout our
         | 
| 35 | 
            +
              # guide at: https://bundler.io/guides/creating_gem.html
         | 
| 36 | 
            +
            end
         | 
    
        data/src/app.tsx
    ADDED
    
    
| @@ -0,0 +1,142 @@ | |
| 1 | 
            +
            /** @jsx jsx */
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            import { jsx, css } from '@emotion/react'
         | 
| 4 | 
            +
            import React, { useState, useContext } from "react";
         | 
| 5 | 
            +
            import { Button } from "../types/button";
         | 
| 6 | 
            +
            import { ButtonState } from "./../lib/button_state";
         | 
| 7 | 
            +
            import { ButtonsModal } from "./buttons_modal";
         | 
| 8 | 
            +
            import { ButtonsSettingContext } from "./../contexts/buttons_setting";
         | 
| 9 | 
            +
            import { ButtonsSettingType, ButtonsInLayer, ButtonInLayer, Layers, Flip } from "../types/buttons_setting_type";
         | 
| 10 | 
            +
            import { LayerKey } from "../types/layer_key";
         | 
| 11 | 
            +
            import { disableFlipType, alwaysFlipType, flipIfPressedSelfType, flipIfPressedSomeButtonsType, ignoreButtonsInFlipingType, remapType, openMenuType, closeMenuType } from "../reducers/layer_reducer";
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            type ButtonMenuProp = {
         | 
| 14 | 
            +
              name: Button;
         | 
| 15 | 
            +
              layerKey: LayerKey;
         | 
| 16 | 
            +
              buttonValue: ButtonInLayer;
         | 
| 17 | 
            +
              layersDispatch: any;
         | 
| 18 | 
            +
            };
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            const ButtonMenu = ({ name, layerKey, buttonValue, layersDispatch }: ButtonMenuProp) => {
         | 
| 21 | 
            +
              const flipRadioName = `${layerKey}_button_menu_${name}`;
         | 
| 22 | 
            +
              const buttonState = new ButtonState(name, buttonValue.flip, buttonValue.remap);
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              // for modal
         | 
| 25 | 
            +
              const [openModal, setOpenModal] = useState(false)
         | 
| 26 | 
            +
              const [modalCallbackOnSubmit, setModalCallbackOnSubmit] = useState(undefined as any)
         | 
| 27 | 
            +
              const [modalCloseCallback, setModalCloseCallback] = useState(undefined as any)
         | 
| 28 | 
            +
              const [modalTitle, setModalTitle] = useState("")
         | 
| 29 | 
            +
              const [modalPrefillButtons, setModalPrefillButtons] = useState<Array<Button>>([])
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              // 無効
         | 
| 32 | 
            +
              const handleNullFlipValue = (e: React.ChangeEvent<HTMLInputElement>) => {
         | 
| 33 | 
            +
                layersDispatch({ type: disableFlipType, payload: { layerKey: layerKey, button: name }});
         | 
| 34 | 
            +
              };
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              // 常に連打
         | 
| 37 | 
            +
              const handleFlipValue = (e: React.ChangeEvent<HTMLInputElement>) => {
         | 
| 38 | 
            +
                layersDispatch({ type: alwaysFlipType, payload: { layerKey: layerKey, button: name }});
         | 
| 39 | 
            +
              };
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              // 自分自身への条件付き連打
         | 
| 42 | 
            +
              const openIfPressedRadioboxModal = (e: React.ChangeEvent<HTMLInputElement>) => {
         | 
| 43 | 
            +
                layersDispatch({ type: flipIfPressedSelfType, payload: { layerKey: layerKey, button: name }});
         | 
| 44 | 
            +
              };
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              // 条件付き連打
         | 
| 47 | 
            +
              const flipIfPressedSomeButtons = buttonValue?.flip?.if_pressed || [] as Array<Button>;
         | 
| 48 | 
            +
              const setFlipIfPressedSomeButtonsWithPersistence = (bs: Array<Button>) => {
         | 
| 49 | 
            +
                layersDispatch({ type: flipIfPressedSomeButtonsType, payload: { layerKey: layerKey, button: name, targetButtons: bs }});
         | 
| 50 | 
            +
              }
         | 
| 51 | 
            +
              const openIfPressedSomeButtonsModal = (e: React.ChangeEvent<HTMLInputElement> | React.MouseEvent<HTMLInputElement>) => {
         | 
| 52 | 
            +
                setOpenModal(true)
         | 
| 53 | 
            +
                setModalTitle("特定のキーを押したときだけ")
         | 
| 54 | 
            +
                setModalPrefillButtons(flipIfPressedSomeButtons);
         | 
| 55 | 
            +
                setModalCallbackOnSubmit(() => setFlipIfPressedSomeButtonsWithPersistence);
         | 
| 56 | 
            +
                setModalCloseCallback(() => setOpenModal);
         | 
| 57 | 
            +
              }
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              // 無視
         | 
| 60 | 
            +
              const forceNeutralButtons = buttonValue.flip?.force_neutral || [] as Array<Button>
         | 
| 61 | 
            +
              const setIgnoreButtonsOnFlipingWithPersistence = (bs: Array<Button>) => {
         | 
| 62 | 
            +
                layersDispatch({ type: ignoreButtonsInFlipingType, payload: { layerKey: layerKey, button: name, targetButtons: bs }});
         | 
| 63 | 
            +
              }
         | 
| 64 | 
            +
              const handleIgnoreButton = (e: React.ChangeEvent<HTMLInputElement>) => {
         | 
| 65 | 
            +
                setOpenModal(true)
         | 
| 66 | 
            +
                setModalTitle("連打中は特定のボタンの入力を無視する")
         | 
| 67 | 
            +
                setModalPrefillButtons(buttonValue.flip?.force_neutral || [] as Array<Button>);
         | 
| 68 | 
            +
                setModalCallbackOnSubmit(() => setIgnoreButtonsOnFlipingWithPersistence);
         | 
| 69 | 
            +
                setModalCloseCallback(() => setOpenModal);
         | 
| 70 | 
            +
              };
         | 
| 71 | 
            +
             | 
| 72 | 
            +
              // リマップ
         | 
| 73 | 
            +
              const setRemapButtonsWithPersistence = (bs: Array<Button>) => {
         | 
| 74 | 
            +
                layersDispatch({ type: remapType, payload: { layerKey: layerKey, button: name, targetButtons: bs }});
         | 
| 75 | 
            +
              }
         | 
| 76 | 
            +
              const handleRemapButton = (e: React.ChangeEvent<HTMLInputElement>) => {
         | 
| 77 | 
            +
                setOpenModal(true)
         | 
| 78 | 
            +
                setModalTitle("リマップ")
         | 
| 79 | 
            +
                setModalPrefillButtons(buttonValue.remap?.to || []);
         | 
| 80 | 
            +
                setModalCallbackOnSubmit(() => setRemapButtonsWithPersistence);
         | 
| 81 | 
            +
                setModalCloseCallback(() => setOpenModal);
         | 
| 82 | 
            +
              };
         | 
| 83 | 
            +
             | 
| 84 | 
            +
              return(
         | 
| 85 | 
            +
                <>
         | 
| 86 | 
            +
                  <fieldset><legend><strong>連打設定</strong></legend>
         | 
| 87 | 
            +
                    <label><input type="radio" onChange={handleNullFlipValue} checked={buttonState.isDisabledFlip()}/>無効</label><br />
         | 
| 88 | 
            +
                    <label><input type="radio" onChange={handleFlipValue} checked={buttonState.isAlwaysFlip()}/>常に連打する</label><br />
         | 
| 89 | 
            +
                    <label><input type="radio" onChange={openIfPressedRadioboxModal} checked={buttonState.isFlipIfPressedSelf()}/>このボタンを押している時だけ連打する({name})</label><br />
         | 
| 90 | 
            +
                    <label>
         | 
| 91 | 
            +
                      <input type="radio" onChange={openIfPressedSomeButtonsModal} onClick={openIfPressedSomeButtonsModal} checked={buttonState.isFlipIfPressedSomeButtons()}/>
         | 
| 92 | 
            +
                      特定のキーを押したときだけ連打する{flipIfPressedSomeButtons.length > 0 && `(${flipIfPressedSomeButtons.join(", ")})`}
         | 
| 93 | 
            +
                    </label>
         | 
| 94 | 
            +
                  </fieldset>
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                  <fieldset><legend><strong>連打オプション</strong></legend>
         | 
| 97 | 
            +
                    <label>
         | 
| 98 | 
            +
                      <input type="checkbox" onChange={handleIgnoreButton} checked={forceNeutralButtons.length > 0} disabled={buttonState.isDisabledFlip()} />
         | 
| 99 | 
            +
                      連打中は特定のボタンの入力を無視する{forceNeutralButtons.length > 0 && `(${forceNeutralButtons.join(", ")})`}
         | 
| 100 | 
            +
                    </label>
         | 
| 101 | 
            +
                  </fieldset>
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                  <fieldset><legend><strong>リマップ設定</strong></legend>
         | 
| 104 | 
            +
                    <label>
         | 
| 105 | 
            +
                      <input type="checkbox" onChange={handleRemapButton} checked={buttonState.isRemap()} disabled={!buttonState.isDisabledFlip()} />
         | 
| 106 | 
            +
                        別のボタンに置き換える{buttonState.isRemap() && `(${buttonValue.remap?.to?.join(", ")})`}
         | 
| 107 | 
            +
                    </label>
         | 
| 108 | 
            +
                  </fieldset>
         | 
| 109 | 
            +
                  <div css={css`position: relative;`}>
         | 
| 110 | 
            +
                    {openModal && <ButtonsModal callbackOnSubmit={modalCallbackOnSubmit} callbackOnClose={modalCloseCallback} title={modalTitle} prefill={modalPrefillButtons} positionOnShown={"relative"} />}
         | 
| 111 | 
            +
                  </div>
         | 
| 112 | 
            +
                </>
         | 
| 113 | 
            +
              )
         | 
| 114 | 
            +
            }
         | 
| 115 | 
            +
             | 
| 116 | 
            +
            type Prop = {
         | 
| 117 | 
            +
              name: Button;
         | 
| 118 | 
            +
              layerKey: LayerKey;
         | 
| 119 | 
            +
            };
         | 
| 120 | 
            +
             | 
| 121 | 
            +
            export const ButtonSetting: React.FC<Prop> = ({ name, layerKey }) => {
         | 
| 122 | 
            +
              const settingContext = useContext(ButtonsSettingContext);
         | 
| 123 | 
            +
              const handleToggle = () => {
         | 
| 124 | 
            +
                if(isOpenMenu()) { // 閉じる
         | 
| 125 | 
            +
                  settingContext.layersDispatch({ type: closeMenuType, payload: { layerKey: layerKey, button: name }});
         | 
| 126 | 
            +
                } else { // 開く
         | 
| 127 | 
            +
                  settingContext.layersDispatch({ type: openMenuType, payload: { layerKey: layerKey, button: name }});
         | 
| 128 | 
            +
                }
         | 
| 129 | 
            +
              }
         | 
| 130 | 
            +
             | 
| 131 | 
            +
              const isOpenMenu = () => {
         | 
| 132 | 
            +
                return settingContext.layers[layerKey][name].open;
         | 
| 133 | 
            +
              }
         | 
| 134 | 
            +
              const buttonValue = settingContext.layers[layerKey][name] || {} as ButtonInLayer;
         | 
| 135 | 
            +
             | 
| 136 | 
            +
              return (
         | 
| 137 | 
            +
                <>
         | 
| 138 | 
            +
                  <label><input type="checkbox" checked={isOpenMenu()} onChange={handleToggle}/>{name}</label>
         | 
| 139 | 
            +
                  {isOpenMenu() && <ButtonMenu name={name} layerKey={layerKey} buttonValue={buttonValue} layersDispatch={settingContext.layersDispatch} />}
         | 
| 140 | 
            +
                </>
         | 
| 141 | 
            +
              );
         | 
| 142 | 
            +
            };
         | 
| @@ -0,0 +1,110 @@ | |
| 1 | 
            +
            /** @jsx jsx */
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            import { jsx, css } from '@emotion/react'
         | 
| 4 | 
            +
            import React, { useState } from "react";
         | 
| 5 | 
            +
            import { Button, buttons } from "../types/button";
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            type Props = {
         | 
| 8 | 
            +
              callbackOnSubmit: any;
         | 
| 9 | 
            +
              callbackOnClose: any;
         | 
| 10 | 
            +
              prefill: Array<Button>;
         | 
| 11 | 
            +
              title: string;
         | 
| 12 | 
            +
              positionOnShown: string;
         | 
| 13 | 
            +
            };
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            type CheckedButtons = {
         | 
| 16 | 
            +
              [key in Button] : boolean
         | 
| 17 | 
            +
            }
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            export const ButtonsModal = ({ callbackOnSubmit, callbackOnClose, title, prefill, positionOnShown }: Props) => {
         | 
| 20 | 
            +
              const [checkedButtonMap, setCheckedButtonMap] = useState(
         | 
| 21 | 
            +
                prefill.reduce((a, b) => { a[b] = true; return a },
         | 
| 22 | 
            +
                  buttons.reduce((a, b) => { a[b] = false; return a }, {} as CheckedButtons)
         | 
| 23 | 
            +
                )
         | 
| 24 | 
            +
              )
         | 
| 25 | 
            +
              const callback = callbackOnSubmit;
         | 
| 26 | 
            +
              const handleSubmit = (e: React.MouseEvent<HTMLAnchorElement>) => {
         | 
| 27 | 
            +
                e.preventDefault();
         | 
| 28 | 
            +
                const bs = Object.entries(checkedButtonMap).reduce((acc, item) => {
         | 
| 29 | 
            +
                  const checked: boolean = item[1];
         | 
| 30 | 
            +
                  const button = item[0] as Button;
         | 
| 31 | 
            +
                  checked && acc.push(button);
         | 
| 32 | 
            +
                  return acc;
         | 
| 33 | 
            +
                }, [] as Array<Button>).sort();
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                callbackOnSubmit(bs);
         | 
| 36 | 
            +
                callbackOnClose(false);
         | 
| 37 | 
            +
              };
         | 
| 38 | 
            +
              const handleCancel = (e: React.MouseEvent<HTMLAnchorElement>) => {
         | 
| 39 | 
            +
                e.preventDefault();
         | 
| 40 | 
            +
                callbackOnClose(false);
         | 
| 41 | 
            +
              }
         | 
| 42 | 
            +
              const titlestyle = css(`
         | 
| 43 | 
            +
                margin-top: 10px;
         | 
| 44 | 
            +
                font-size: 1.17em;
         | 
| 45 | 
            +
                font-weight: bold;
         | 
| 46 | 
            +
              `)
         | 
| 47 | 
            +
              const style = () => {
         | 
| 48 | 
            +
                if(positionOnShown === "relative") {
         | 
| 49 | 
            +
                  return css(`
         | 
| 50 | 
            +
                    position: absolute;
         | 
| 51 | 
            +
                    align: left;
         | 
| 52 | 
            +
                    top: -400px;
         | 
| 53 | 
            +
                    width: 400px;
         | 
| 54 | 
            +
                    height: 400px;
         | 
| 55 | 
            +
                    border: solid;
         | 
| 56 | 
            +
                    background-color: white;
         | 
| 57 | 
            +
                  `);
         | 
| 58 | 
            +
                } else {
         | 
| 59 | 
            +
                  return css(`
         | 
| 60 | 
            +
                    position: absolute;
         | 
| 61 | 
            +
                    align: left;
         | 
| 62 | 
            +
                    top: 0px;
         | 
| 63 | 
            +
                    left: 20px;
         | 
| 64 | 
            +
                    width: 400px;
         | 
| 65 | 
            +
                    height: 400px;
         | 
| 66 | 
            +
                    border: solid;
         | 
| 67 | 
            +
                    background-color: white;
         | 
| 68 | 
            +
                  `);
         | 
| 69 | 
            +
                }
         | 
| 70 | 
            +
              }
         | 
| 71 | 
            +
              const aStyle = css`
         | 
| 72 | 
            +
                background-color: #4669ff;
         | 
| 73 | 
            +
                border-bottom: solid 2px #003aff;
         | 
| 74 | 
            +
                border-right: solid 2px #003aff;
         | 
| 75 | 
            +
                border-radius: 20px;
         | 
| 76 | 
            +
                font-weight: bold;
         | 
| 77 | 
            +
                color: #FFF;
         | 
| 78 | 
            +
                text-decoration: none;
         | 
| 79 | 
            +
                padding: 10px;
         | 
| 80 | 
            +
                display: inline-block;
         | 
| 81 | 
            +
                margin-left: 10px;
         | 
| 82 | 
            +
              `;
         | 
| 83 | 
            +
             | 
| 84 | 
            +
              const handleClick = (e: React.ChangeEvent<HTMLInputElement>) => {
         | 
| 85 | 
            +
                setCheckedButtonMap((previousButtonStats) => {
         | 
| 86 | 
            +
                  previousButtonStats[e.target.value as Button] = e.target.checked;
         | 
| 87 | 
            +
                  return previousButtonStats;
         | 
| 88 | 
            +
                })
         | 
| 89 | 
            +
              }
         | 
| 90 | 
            +
             | 
| 91 | 
            +
              return (
         | 
| 92 | 
            +
                <>
         | 
| 93 | 
            +
                  <div css={style()}>
         | 
| 94 | 
            +
                    <div css={titlestyle}>{title}</div>
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                    {buttons.map((b, index) => (
         | 
| 97 | 
            +
                      <div key={index}>
         | 
| 98 | 
            +
                        <label><input type="checkbox" value={b} defaultChecked={checkedButtonMap[b]} onChange={handleClick} />{b}</label>
         | 
| 99 | 
            +
                      </div>
         | 
| 100 | 
            +
                    ))}
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                    <hr />
         | 
| 103 | 
            +
                    <div css={css`display: flex`}>
         | 
| 104 | 
            +
                      <a href={"#"} onClick={handleCancel} css={aStyle}>変更せず閉じる</a>
         | 
| 105 | 
            +
                      <a href={"#"} onClick={handleSubmit} css={aStyle}>決定する</a>
         | 
| 106 | 
            +
                    </div>
         | 
| 107 | 
            +
                  </div>
         | 
| 108 | 
            +
                </>
         | 
| 109 | 
            +
              )
         | 
| 110 | 
            +
            }
         | 
| @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            /** @jsx jsx */
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            import { jsx, css } from '@emotion/react'
         | 
| 4 | 
            +
            import React, { useState, useContext } from "react";
         | 
| 5 | 
            +
            import { ButtonSetting } from "./button_setting";
         | 
| 6 | 
            +
            import { MacroSettings } from "./macro_settings";
         | 
| 7 | 
            +
            import { ModeSettings } from "./mode_settings";
         | 
| 8 | 
            +
            import { Button, buttons } from "../types/button";
         | 
| 9 | 
            +
            import { LayerKey } from "../types/layer_key";
         | 
| 10 | 
            +
            import { ButtonsSettingContext } from "./../contexts/buttons_setting";
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            type Props = {
         | 
| 13 | 
            +
              layerKey: LayerKey;
         | 
| 14 | 
            +
              layerRef: any;
         | 
| 15 | 
            +
            };
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            export const ButtonsSetting = ({ layerKey, layerRef }:Props) => {
         | 
| 18 | 
            +
              const [visibility, setVisibility] = useState("hidden");
         | 
| 19 | 
            +
              const visibilityStyle = () => {
         | 
| 20 | 
            +
                if(visibility === "hidden") {
         | 
| 21 | 
            +
                  return css`display: none;`;
         | 
| 22 | 
            +
                }
         | 
| 23 | 
            +
              }
         | 
| 24 | 
            +
              const ulStyle = css`
         | 
| 25 | 
            +
                border: 1px solid #666;
         | 
| 26 | 
            +
                display: flex;
         | 
| 27 | 
            +
                flex-wrap: wrap;
         | 
| 28 | 
            +
                justify-content: center;
         | 
| 29 | 
            +
                list-style-type: none;
         | 
| 30 | 
            +
                margin: 0 0 0 1em;
         | 
| 31 | 
            +
                padding: 0;
         | 
| 32 | 
            +
                width: 900px;
         | 
| 33 | 
            +
              `;
         | 
| 34 | 
            +
              const liStyle = css`
         | 
| 35 | 
            +
                border: 1px solid #aaa;
         | 
| 36 | 
            +
                margin: 0.2em;
         | 
| 37 | 
            +
                padding: 0.5em;
         | 
| 38 | 
            +
                width: 200px;
         | 
| 39 | 
            +
              `;
         | 
| 40 | 
            +
              layerRef.setVisibility = setVisibility;
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              const { layers } = useContext(ButtonsSettingContext);
         | 
| 43 | 
            +
              const isEnableMode = !layers[layerKey].mode.disable;
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              return(
         | 
| 46 | 
            +
                <div css={visibilityStyle()}>
         | 
| 47 | 
            +
                  <h4>モード</h4>
         | 
| 48 | 
            +
                  <ModeSettings layerKey={layerKey} />
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  <h4>マクロ</h4>
         | 
| 51 | 
            +
                  {isEnableMode && `モードが有効なので選択できません`}
         | 
| 52 | 
            +
                  {!isEnableMode && <MacroSettings layerKey={layerKey} />}
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  <h4>各ボタンの設定</h4>
         | 
| 55 | 
            +
                  {isEnableMode && `モードが有効なので選択できません`}
         | 
| 56 | 
            +
                  {!isEnableMode &&
         | 
| 57 | 
            +
                    <div css={ulStyle}>
         | 
| 58 | 
            +
                      {buttons.map((b, i) => (
         | 
| 59 | 
            +
                        <div key={i} css={liStyle}>
         | 
| 60 | 
            +
                          <ButtonSetting layerKey={layerKey} name={b} />
         | 
| 61 | 
            +
                        </div>
         | 
| 62 | 
            +
                      ))}
         | 
| 63 | 
            +
                    </div>
         | 
| 64 | 
            +
                  }
         | 
| 65 | 
            +
                </div>
         | 
| 66 | 
            +
              )
         | 
| 67 | 
            +
            }
         |