procon_bypass_man-web 0.1.1 → 0.1.2
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 +4 -4
- data/.node-version +1 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile.lock +1 -1
- data/README.md +4 -1
- data/bin/pbm_web +5 -0
- data/lib/procon_bypass_man/web/configuration.rb +40 -0
- data/lib/procon_bypass_man/web/db.rb +4 -4
- data/lib/procon_bypass_man/web/migration/002_insert_default_settings_rows.sql +3 -0
- data/lib/procon_bypass_man/web/models/setting.rb +1 -1
- data/lib/procon_bypass_man/web/public/bundle.js +1 -1
- data/lib/procon_bypass_man/web/server.rb +12 -2
- data/lib/procon_bypass_man/web/setting_parser/layer.rb +72 -0
- data/lib/procon_bypass_man/web/setting_parser/top_level_layer.rb +109 -0
- data/lib/procon_bypass_man/web/setting_parser.rb +4 -152
- data/lib/procon_bypass_man/web/storage.rb +4 -4
- data/lib/procon_bypass_man/web/version.rb +1 -1
- data/lib/procon_bypass_man/web.rb +14 -4
- data/src/components/button_setting.tsx +17 -34
- data/src/components/buttons_modal.tsx +15 -25
- data/src/components/macro_settings.tsx +6 -12
- data/src/hooks/useModal.ts +37 -0
- data/src/pages/buttons_setting_page.tsx +8 -18
- metadata +8 -2
@@ -15,6 +15,10 @@ module ProconBypassMan
|
|
15
15
|
class App < Sinatra::Base
|
16
16
|
require "yaml"
|
17
17
|
|
18
|
+
before do
|
19
|
+
env["rack.logger"] = ProconBypassMan::Web.logger
|
20
|
+
end
|
21
|
+
|
18
22
|
register Sinatra::Reloader if defined?(Sinatra::Reloader)
|
19
23
|
set :bind, '0.0.0.0'
|
20
24
|
|
@@ -119,13 +123,19 @@ module ProconBypassMan
|
|
119
123
|
end
|
120
124
|
end
|
121
125
|
|
126
|
+
# PBMから受け取って、emmitする
|
127
|
+
post '/api/pressed_buttons' do
|
128
|
+
status 200
|
129
|
+
body ''
|
130
|
+
end
|
131
|
+
|
122
132
|
get '/' do
|
123
|
-
send_file File.join(ProconBypassMan::Web.
|
133
|
+
send_file File.join(ProconBypassMan::Web.gem_root, 'lib/procon_bypass_man/web', 'public', 'index.html')
|
124
134
|
end
|
125
135
|
|
126
136
|
# サーバでパスとして解釈されないように、全部 `/`として受け付けるため
|
127
137
|
get '/:none' do
|
128
|
-
send_file File.join(ProconBypassMan::Web.
|
138
|
+
send_file File.join(ProconBypassMan::Web.gem_root, 'lib/procon_bypass_man/web', 'public', 'index.html')
|
129
139
|
end
|
130
140
|
end
|
131
141
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module ProconBypassMan
|
2
|
+
module Web
|
3
|
+
class SettingParser
|
4
|
+
class Layer
|
5
|
+
module Syntax
|
6
|
+
def initialize(mode: )
|
7
|
+
@table = {
|
8
|
+
mode: mode&.to_s,
|
9
|
+
}.compact
|
10
|
+
end
|
11
|
+
|
12
|
+
def flip(button, if_pressed: nil, force_neutral: nil)
|
13
|
+
@table[:flip] ||= {}
|
14
|
+
if if_pressed.nil? && force_neutral.nil?
|
15
|
+
@table[:flip][button] = nil
|
16
|
+
else
|
17
|
+
if if_pressed
|
18
|
+
if if_pressed.is_a?(Array)
|
19
|
+
ifp = if_pressed
|
20
|
+
else
|
21
|
+
ifp = [if_pressed]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
if force_neutral
|
25
|
+
if force_neutral.is_a?(Array)
|
26
|
+
fn = force_neutral
|
27
|
+
else
|
28
|
+
fn = [force_neutral]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@table[:flip][button] = { if_pressed: ifp, force_neutral: fn, enable: true }
|
32
|
+
end
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def remap(button, to: nil)
|
37
|
+
case to
|
38
|
+
when Array
|
39
|
+
@table[:remap] ||= {}
|
40
|
+
@table[:remap][button] = { to: to }
|
41
|
+
when String, Symbol
|
42
|
+
@table[:remap] ||= {}
|
43
|
+
@table[:remap][button] = { to: [to] }
|
44
|
+
end
|
45
|
+
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def macro(name, if_pressed: nil)
|
50
|
+
@table[:macro] ||= {}
|
51
|
+
if if_pressed.nil?
|
52
|
+
@table[:macro][name.to_s] = { if_pressed: [] }
|
53
|
+
else
|
54
|
+
@table[:macro][name.to_s] = { if_pressed: if_pressed }
|
55
|
+
end
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def method_missing(name, *_args)
|
60
|
+
ProconBypassMan::Web.logger.info("unknown layer DSL #{name}")
|
61
|
+
self
|
62
|
+
end
|
63
|
+
end
|
64
|
+
include Syntax
|
65
|
+
|
66
|
+
def to_hash
|
67
|
+
@table
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module ProconBypassMan
|
2
|
+
module Web
|
3
|
+
class SettingParser
|
4
|
+
class TopLevelLayer
|
5
|
+
module Serializer
|
6
|
+
def to_hash
|
7
|
+
h = { prefix_keys_for_changing_layer: prefix_keys_for_changing_layer || [] }
|
8
|
+
h[:layers] ||= {}
|
9
|
+
@layers.each do |key, layer|
|
10
|
+
h[:layers][key] = layer&.to_hash
|
11
|
+
end
|
12
|
+
h
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_hash_group_by_button
|
16
|
+
h = { prefix_keys_for_changing_layer: prefix_keys_for_changing_layer || [] }
|
17
|
+
h[:layers] ||= {}
|
18
|
+
@layers.each do |key, layer|
|
19
|
+
h[:layers][key] ||= {}
|
20
|
+
next if layer.nil?
|
21
|
+
|
22
|
+
if !@installed_plugin[:macros].empty?
|
23
|
+
h[:installed_macros] = @installed_plugin[:macros]
|
24
|
+
end
|
25
|
+
if !@installed_plugin[:modes].empty?
|
26
|
+
h[:installed_modes] = @installed_plugin[:modes]
|
27
|
+
end
|
28
|
+
|
29
|
+
layer.to_hash.dig(:flip)&.each do |button, value|
|
30
|
+
h[:layers][key][button] ||= {}
|
31
|
+
h[:layers][key][button][:open] = true
|
32
|
+
h[:layers][key][button][:flip] ||= {}
|
33
|
+
h[:layers][key][button][:flip][:enable] = true
|
34
|
+
if value
|
35
|
+
h[:layers][key][button][:flip].merge!(value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
if layer.to_hash.dig(:macro)
|
40
|
+
h[:layers][key][:macro] = []
|
41
|
+
if(macros = layer.to_hash.dig(:macro))
|
42
|
+
macros.transform_values! { |x| x[:if_pressed] }
|
43
|
+
h[:layers][key][:macro] = macros
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
if layer.to_hash.dig(:mode)
|
48
|
+
h[:layers][key][:mode] = { layer.to_hash.dig(:mode) => true }
|
49
|
+
end
|
50
|
+
|
51
|
+
layer.to_hash.dig(:remap)&.each do |button, value|
|
52
|
+
h[:layers][key][button] ||= {}
|
53
|
+
h[:layers][key][button][:remap] ||= {}
|
54
|
+
h[:layers][key][button][:open] = true
|
55
|
+
if value
|
56
|
+
h[:layers][key][button][:remap].merge!(value)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
h
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module Syntax
|
65
|
+
def initialize
|
66
|
+
@installed_plugin = { macros: [], modes: [] }
|
67
|
+
@layers = {}
|
68
|
+
end
|
69
|
+
|
70
|
+
def install_macro_plugin(name)
|
71
|
+
@installed_plugin[:macros] << name.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
def install_mode_plugin(name)
|
75
|
+
@installed_plugin[:modes] << name.to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
def prefix_keys_for_changing_layer(value=nil)
|
79
|
+
if value
|
80
|
+
@prefix_keys_for_changing_layer = value
|
81
|
+
else
|
82
|
+
@prefix_keys_for_changing_layer
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def layer(dir, mode: nil, &block)
|
87
|
+
if(mode == :manual || mode == 'manual')
|
88
|
+
mode = nil
|
89
|
+
end
|
90
|
+
|
91
|
+
if block_given?
|
92
|
+
@layers[dir] = Layer.new(mode: mode).instance_eval(&block) || Layer.new(mode: mode)
|
93
|
+
else
|
94
|
+
@layers[dir] = Layer.new(mode: mode)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def method_missing(name, *_args)
|
99
|
+
ProconBypassMan::Web.logger.info("unknown toplevel DSL #{name}")
|
100
|
+
self
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
include Syntax
|
105
|
+
include Serializer
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require "json"
|
2
|
+
require "procon_bypass_man/web/setting_parser/top_level_layer"
|
3
|
+
require "procon_bypass_man/web/setting_parser/layer"
|
2
4
|
|
3
5
|
# pluginの定数を握りつぶす
|
4
6
|
class Module
|
@@ -9,160 +11,10 @@ class Module
|
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
14
|
+
# PBM 0.1.8バージョンの構文に対応
|
12
15
|
module ProconBypassMan
|
13
16
|
module Web
|
14
17
|
class SettingParser
|
15
|
-
class Core
|
16
|
-
class Layer
|
17
|
-
def initialize(mode: )
|
18
|
-
@table = {
|
19
|
-
mode: mode&.to_s,
|
20
|
-
}.compact
|
21
|
-
end
|
22
|
-
|
23
|
-
def flip(button, if_pressed: nil, force_neutral: nil)
|
24
|
-
@table[:flip] ||= {}
|
25
|
-
if if_pressed.nil? && force_neutral.nil?
|
26
|
-
@table[:flip][button] = nil
|
27
|
-
else
|
28
|
-
if if_pressed
|
29
|
-
if if_pressed.is_a?(Array)
|
30
|
-
ifp = if_pressed
|
31
|
-
else
|
32
|
-
ifp = [if_pressed]
|
33
|
-
end
|
34
|
-
end
|
35
|
-
if force_neutral
|
36
|
-
if force_neutral.is_a?(Array)
|
37
|
-
fn = force_neutral
|
38
|
-
else
|
39
|
-
fn = [force_neutral]
|
40
|
-
end
|
41
|
-
end
|
42
|
-
@table[:flip][button] = { if_pressed: ifp, force_neutral: fn, enable: true }
|
43
|
-
end
|
44
|
-
self
|
45
|
-
end
|
46
|
-
|
47
|
-
def remap(button, to: nil)
|
48
|
-
case to
|
49
|
-
when Array
|
50
|
-
@table[:remap] ||= {}
|
51
|
-
@table[:remap][button] = { to: to }
|
52
|
-
when String, Symbol
|
53
|
-
@table[:remap] ||= {}
|
54
|
-
@table[:remap][button] = { to: [to] }
|
55
|
-
end
|
56
|
-
|
57
|
-
self
|
58
|
-
end
|
59
|
-
|
60
|
-
def macro(name, if_pressed: nil)
|
61
|
-
@table[:macro] ||= {}
|
62
|
-
if if_pressed.nil?
|
63
|
-
@table[:macro][name.to_s] = { if_pressed: [] }
|
64
|
-
else
|
65
|
-
@table[:macro][name.to_s] = { if_pressed: if_pressed }
|
66
|
-
end
|
67
|
-
self
|
68
|
-
end
|
69
|
-
|
70
|
-
def to_hash
|
71
|
-
@table
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def install_macro_plugin(name)
|
76
|
-
@installed_plugin[:macros] << name.to_s
|
77
|
-
end
|
78
|
-
|
79
|
-
def install_mode_plugin(name)
|
80
|
-
@installed_plugin[:modes] << name.to_s
|
81
|
-
end
|
82
|
-
|
83
|
-
def initialize
|
84
|
-
@installed_plugin = { macros: [], modes: [] }
|
85
|
-
@layers = {}
|
86
|
-
end
|
87
|
-
|
88
|
-
def prefix_keys_for_changing_layer(value=nil)
|
89
|
-
if value
|
90
|
-
@prefix_keys_for_changing_layer = value
|
91
|
-
else
|
92
|
-
@prefix_keys_for_changing_layer
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def layer(dir, mode: nil, &block)
|
97
|
-
if(mode == :manual || mode == 'manual')
|
98
|
-
mode = nil
|
99
|
-
end
|
100
|
-
|
101
|
-
if block_given?
|
102
|
-
@layers[dir] = Layer.new(mode: mode).instance_eval(&block) || Layer.new(mode: mode)
|
103
|
-
else
|
104
|
-
@layers[dir] = Layer.new(mode: mode)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def to_hash
|
109
|
-
h = { prefix_keys_for_changing_layer: prefix_keys_for_changing_layer || [] }
|
110
|
-
h[:layers] ||= {}
|
111
|
-
@layers.each do |key, layer|
|
112
|
-
h[:layers][key] = layer&.to_hash
|
113
|
-
end
|
114
|
-
h
|
115
|
-
end
|
116
|
-
|
117
|
-
def to_hash_group_by_button
|
118
|
-
h = { prefix_keys_for_changing_layer: prefix_keys_for_changing_layer || [] }
|
119
|
-
h[:layers] ||= {}
|
120
|
-
@layers.each do |key, layer|
|
121
|
-
h[:layers][key] ||= {}
|
122
|
-
next if layer.nil?
|
123
|
-
|
124
|
-
if !@installed_plugin[:macros].empty?
|
125
|
-
h[:installed_macros] = @installed_plugin[:macros]
|
126
|
-
end
|
127
|
-
if !@installed_plugin[:modes].empty?
|
128
|
-
h[:installed_modes] = @installed_plugin[:modes]
|
129
|
-
end
|
130
|
-
|
131
|
-
layer.to_hash.dig(:flip)&.each do |button, value|
|
132
|
-
h[:layers][key][button] ||= {}
|
133
|
-
h[:layers][key][button][:open] = true
|
134
|
-
h[:layers][key][button][:flip] ||= {}
|
135
|
-
h[:layers][key][button][:flip][:enable] = true
|
136
|
-
if value
|
137
|
-
h[:layers][key][button][:flip].merge!(value)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
if layer.to_hash.dig(:macro)
|
142
|
-
h[:layers][key][:macro] = []
|
143
|
-
if(macros = layer.to_hash.dig(:macro))
|
144
|
-
macros.transform_values! { |x| x[:if_pressed] }
|
145
|
-
h[:layers][key][:macro] = macros
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
if layer.to_hash.dig(:mode)
|
150
|
-
h[:layers][key][:mode] = { layer.to_hash.dig(:mode) => true }
|
151
|
-
end
|
152
|
-
|
153
|
-
layer.to_hash.dig(:remap)&.each do |button, value|
|
154
|
-
h[:layers][key][button] ||= {}
|
155
|
-
h[:layers][key][button][:remap] ||= {}
|
156
|
-
h[:layers][key][button][:open] = true
|
157
|
-
if value
|
158
|
-
h[:layers][key][button][:remap].merge!(value)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
h
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
18
|
def self.parse(text)
|
167
19
|
new(text)
|
168
20
|
end
|
@@ -182,7 +34,7 @@ module ProconBypassMan
|
|
182
34
|
end
|
183
35
|
|
184
36
|
def initialize(text)
|
185
|
-
@parser =
|
37
|
+
@parser = TopLevelLayer.new
|
186
38
|
@parser.instance_eval(text)
|
187
39
|
end
|
188
40
|
end
|
@@ -6,19 +6,19 @@ module ProconBypassMan
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def root_path
|
9
|
-
ProconBypassMan::Web::Setting.
|
9
|
+
ProconBypassMan::Web::Setting.find_or_create&.root_path
|
10
10
|
end
|
11
11
|
|
12
12
|
def root_path=(value)
|
13
|
-
ProconBypassMan::Web::Setting.
|
13
|
+
ProconBypassMan::Web::Setting.find_or_create&.update!(root_path: value)
|
14
14
|
end
|
15
15
|
|
16
16
|
def setting_path
|
17
|
-
ProconBypassMan::Web::Setting.
|
17
|
+
ProconBypassMan::Web::Setting.find_or_create&.setting_path
|
18
18
|
end
|
19
19
|
|
20
20
|
def setting_path=(value)
|
21
|
-
ProconBypassMan::Web::Setting.
|
21
|
+
ProconBypassMan::Web::Setting.find_or_create&.update!(setting_path: value)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require_relative "web/version"
|
2
|
+
require "logger"
|
3
|
+
require "procon_bypass_man/web/configuration"
|
2
4
|
require "procon_bypass_man/web/server"
|
3
5
|
require "procon_bypass_man/web/db"
|
4
6
|
require "procon_bypass_man/web/models/setting"
|
@@ -8,13 +10,21 @@ module ProconBypassMan
|
|
8
10
|
module Web
|
9
11
|
class Error < StandardError; end
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
+
extend ProconBypassMan::Web::Configuration::ClassAttributes
|
14
|
+
|
15
|
+
def self.configure(&block)
|
16
|
+
@@configuration = ProconBypassMan::Web::Configuration.new
|
17
|
+
@@configuration.instance_eval(&block)
|
18
|
+
@@configuration
|
13
19
|
end
|
14
20
|
|
15
21
|
def self.config
|
16
|
-
|
17
|
-
|
22
|
+
@@configuration ||= ProconBypassMan::Web::Configuration.new
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [String]
|
26
|
+
def self.gem_root
|
27
|
+
File.expand_path('../..', __dir__).freeze
|
18
28
|
end
|
19
29
|
end
|
20
30
|
end
|
@@ -9,6 +9,8 @@ import { ButtonsSettingContext } from "./../contexts/buttons_setting";
|
|
9
9
|
import { ButtonsSettingType, ButtonsInLayer, ButtonInLayer, Layers, Flip } from "../types/buttons_setting_type";
|
10
10
|
import { LayerKey } from "../types/layer_key";
|
11
11
|
import { disableFlipType, alwaysFlipType, flipIfPressedSelfType, flipIfPressedSomeButtonsType, ignoreButtonsInFlipingType, remapType, openMenuType, closeMenuType } from "../reducers/layer_reducer";
|
12
|
+
import { useModal, ModalSetting } from "../hooks/useModal";
|
13
|
+
import { ModalProps } from "../components/buttons_modal";
|
12
14
|
|
13
15
|
type ButtonMenuProp = {
|
14
16
|
name: Button;
|
@@ -19,27 +21,20 @@ type ButtonMenuProp = {
|
|
19
21
|
|
20
22
|
const ButtonMenu = ({ name, layerKey, buttonValue, layersDispatch }: ButtonMenuProp) => {
|
21
23
|
const buttonState = new ButtonState(name, buttonValue.flip, buttonValue.remap);
|
22
|
-
|
23
|
-
// for modal
|
24
|
-
const [isOpenModal, toggleModal] = useReducer((m) => { return !m; }, false);
|
25
|
-
|
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>>([])
|
24
|
+
const [modalProps, openModal] = useModal();
|
30
25
|
|
31
26
|
// 無効
|
32
|
-
const handleNullFlipValue = (e: React.
|
27
|
+
const handleNullFlipValue = (e: React.MouseEvent<HTMLInputElement>) => {
|
33
28
|
layersDispatch({ type: disableFlipType, payload: { layerKey: layerKey, button: name }});
|
34
29
|
};
|
35
30
|
|
36
31
|
// 常に連打
|
37
|
-
const handleFlipValue = (e: React.
|
32
|
+
const handleFlipValue = (e: React.MouseEvent<HTMLInputElement>) => {
|
38
33
|
layersDispatch({ type: alwaysFlipType, payload: { layerKey: layerKey, button: name }});
|
39
34
|
};
|
40
35
|
|
41
36
|
// 自分自身への条件付き連打
|
42
|
-
const openIfPressedRadioboxModal = (e: React.
|
37
|
+
const openIfPressedRadioboxModal = (e: React.MouseEvent<HTMLInputElement>) => {
|
43
38
|
layersDispatch({ type: flipIfPressedSelfType, payload: { layerKey: layerKey, button: name }});
|
44
39
|
};
|
45
40
|
|
@@ -48,12 +43,8 @@ const ButtonMenu = ({ name, layerKey, buttonValue, layersDispatch }: ButtonMenuP
|
|
48
43
|
const setFlipIfPressedSomeButtonsWithPersistence = (bs: Array<Button>) => {
|
49
44
|
layersDispatch({ type: flipIfPressedSomeButtonsType, payload: { layerKey: layerKey, button: name, targetButtons: bs }});
|
50
45
|
}
|
51
|
-
const openIfPressedSomeButtonsModal = (e: React.
|
52
|
-
|
53
|
-
setModalTitle("特定のキーを押したときだけ")
|
54
|
-
setModalPrefillButtons(flipIfPressedSomeButtons);
|
55
|
-
setModalCallbackOnSubmit(() => setFlipIfPressedSomeButtonsWithPersistence);
|
56
|
-
setModalCloseCallback(() => toggleModal);
|
46
|
+
const openIfPressedSomeButtonsModal = (e: React.MouseEvent<HTMLInputElement>) => {
|
47
|
+
openModal({ title: "特定のキーを押したときだけ", prefill: flipIfPressedSomeButtons, callbackOnSubmit: setFlipIfPressedSomeButtonsWithPersistence });
|
57
48
|
}
|
58
49
|
|
59
50
|
// 無視
|
@@ -62,11 +53,7 @@ const ButtonMenu = ({ name, layerKey, buttonValue, layersDispatch }: ButtonMenuP
|
|
62
53
|
layersDispatch({ type: ignoreButtonsInFlipingType, payload: { layerKey: layerKey, button: name, targetButtons: bs }});
|
63
54
|
}
|
64
55
|
const handleIgnoreButton = (e: React.ChangeEvent<HTMLInputElement>) => {
|
65
|
-
|
66
|
-
setModalTitle("連打中は特定のボタンの入力を無視する")
|
67
|
-
setModalPrefillButtons(buttonValue.flip?.force_neutral || [] as Array<Button>);
|
68
|
-
setModalCallbackOnSubmit(() => setIgnoreButtonsOnFlipingWithPersistence);
|
69
|
-
setModalCloseCallback(() => toggleModal);
|
56
|
+
openModal({ title: "連打中は特定のボタンの入力を無視する", prefill: buttonValue.flip?.force_neutral || [] as Array<Button>, callbackOnSubmit: setIgnoreButtonsOnFlipingWithPersistence });
|
70
57
|
};
|
71
58
|
|
72
59
|
// リマップ
|
@@ -74,21 +61,20 @@ const ButtonMenu = ({ name, layerKey, buttonValue, layersDispatch }: ButtonMenuP
|
|
74
61
|
layersDispatch({ type: remapType, payload: { layerKey: layerKey, button: name, targetButtons: bs }});
|
75
62
|
}
|
76
63
|
const handleRemapButton = (e: React.ChangeEvent<HTMLInputElement>) => {
|
77
|
-
|
78
|
-
setModalTitle("リマップ")
|
79
|
-
setModalPrefillButtons(buttonValue.remap?.to || []);
|
80
|
-
setModalCallbackOnSubmit(() => setRemapButtonsWithPersistence);
|
81
|
-
setModalCloseCallback(() => toggleModal);
|
64
|
+
openModal({ title: "リマップ", prefill: buttonValue.remap?.to || [], callbackOnSubmit: setRemapButtonsWithPersistence });
|
82
65
|
};
|
83
66
|
|
84
67
|
return(
|
85
68
|
<>
|
69
|
+
<div css={css`position: relative;`}>
|
70
|
+
{<ButtonsModal {...modalProps as ModalProps} />}
|
71
|
+
</div>
|
86
72
|
<fieldset><legend><strong>連打設定</strong></legend>
|
87
|
-
<label><input type="radio"
|
88
|
-
<label><input type="radio"
|
89
|
-
<label><input type="radio"
|
73
|
+
<label><input type="radio" onClick={handleNullFlipValue} checked={buttonState.isDisabledFlip()} readOnly={true} />無効</label><br />
|
74
|
+
<label><input type="radio" onClick={handleFlipValue} checked={buttonState.isAlwaysFlip()} readOnly={true} />常に連打する</label><br />
|
75
|
+
<label><input type="radio" onClick={openIfPressedRadioboxModal} checked={buttonState.isFlipIfPressedSelf()} readOnly={true} />このボタンを押している時だけ連打する({name})</label><br />
|
90
76
|
<label>
|
91
|
-
<input type="radio"
|
77
|
+
<input type="radio" onClick={openIfPressedSomeButtonsModal} checked={buttonState.isFlipIfPressedSomeButtons()} readOnly={true} />
|
92
78
|
特定のキーを押したときだけ連打する{flipIfPressedSomeButtons.length > 0 && `(${flipIfPressedSomeButtons.join(", ")})`}
|
93
79
|
</label>
|
94
80
|
|
@@ -106,9 +92,6 @@ const ButtonMenu = ({ name, layerKey, buttonValue, layersDispatch }: ButtonMenuP
|
|
106
92
|
別のボタンに置き換える{buttonState.isRemap() && `(${buttonValue.remap?.to?.join(", ")})`}
|
107
93
|
</label>
|
108
94
|
</fieldset>
|
109
|
-
<div css={css`position: relative;`}>
|
110
|
-
{isOpenModal && <ButtonsModal callbackOnSubmit={modalCallbackOnSubmit} callbackOnClose={modalCloseCallback} title={modalTitle} prefill={modalPrefillButtons} positionOnShown={"relative"} />}
|
111
|
-
</div>
|
112
95
|
</>
|
113
96
|
)
|
114
97
|
}
|
@@ -4,19 +4,21 @@ import { jsx, css } from '@emotion/react'
|
|
4
4
|
import React, { useState } from "react";
|
5
5
|
import { Button, buttons } from "../types/button";
|
6
6
|
|
7
|
-
type
|
7
|
+
export type ModalProps = {
|
8
8
|
callbackOnSubmit: any;
|
9
9
|
callbackOnClose: any;
|
10
10
|
prefill: Array<Button>;
|
11
11
|
title: string;
|
12
|
-
|
12
|
+
visible: boolean;
|
13
13
|
};
|
14
14
|
|
15
15
|
type CheckedButtons = {
|
16
16
|
[key in Button] : boolean
|
17
17
|
}
|
18
18
|
|
19
|
-
export const ButtonsModal = ({ callbackOnSubmit, callbackOnClose, title, prefill,
|
19
|
+
export const ButtonsModal = ({ callbackOnSubmit, callbackOnClose, title, prefill, visible }: ModalProps) => {
|
20
|
+
if(!visible) { return null };
|
21
|
+
|
20
22
|
const [checkedButtonMap, setCheckedButtonMap] = useState(
|
21
23
|
prefill.reduce((a, b) => { a[b] = true; return a },
|
22
24
|
buttons.reduce((a, b) => { a[b] = false; return a }, {} as CheckedButtons)
|
@@ -45,28 +47,16 @@ export const ButtonsModal = ({ callbackOnSubmit, callbackOnClose, title, prefill
|
|
45
47
|
font-weight: bold;
|
46
48
|
`)
|
47
49
|
const style = () => {
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
}
|
50
|
+
return css(`
|
51
|
+
position: absolute;
|
52
|
+
align: left;
|
53
|
+
top: 0px;
|
54
|
+
left: 20px;
|
55
|
+
width: 400px;
|
56
|
+
height: 400px;
|
57
|
+
border: solid;
|
58
|
+
background-color: white;
|
59
|
+
`);
|
70
60
|
}
|
71
61
|
const aStyle = css`
|
72
62
|
background-color: #4669ff;
|
@@ -10,29 +10,23 @@ import { Plugin, PluginBody, AvailablePlugins, MacroNameMap } from "../types/plu
|
|
10
10
|
import { ButtonsModal } from "./buttons_modal";
|
11
11
|
import { applyMacroType } from "../reducers/layer_reducer";
|
12
12
|
|
13
|
+
import { useModal, ModalSetting } from "../hooks/useModal";
|
14
|
+
import { ModalProps } from "../components/buttons_modal";
|
15
|
+
|
13
16
|
type MacroSettingProps = {
|
14
17
|
layerKey: LayerKey;
|
15
18
|
macro: StructMacro;
|
16
19
|
};
|
17
20
|
const MacroSetting = ({ macro, layerKey }: MacroSettingProps) => {
|
18
21
|
const { layersDispatch } = useContext(ButtonsSettingContext);
|
19
|
-
|
20
|
-
const [isOpenModal, toggleModal] = useReducer((m) => { return !m; }, false);
|
21
|
-
const [modalCallbackOnSubmit, setModalCallbackOnSubmit] = useState(undefined as any)
|
22
|
-
const [modalCloseCallback, setModalCloseCallback] = useState(undefined as any)
|
23
|
-
const [modalTitle, setModalTitle] = useState("")
|
24
|
-
const [modalPrefillButtons, setModalPrefillButtons] = useState<Array<Button>>([])
|
22
|
+
const [modalProps, openModal] = useModal();
|
25
23
|
|
26
24
|
const setButtonsForModal = (bs: Array<Button>) => {
|
27
25
|
macro.if_pressed = bs;
|
28
26
|
layersDispatch({ type: applyMacroType, payload: { layerKey: layerKey, macro: macro }});
|
29
27
|
}
|
30
28
|
const handleClick = (e: React.ChangeEvent<HTMLInputElement>) => {
|
31
|
-
|
32
|
-
setModalTitle("発動キーの設定")
|
33
|
-
setModalPrefillButtons(macro.if_pressed);
|
34
|
-
setModalCallbackOnSubmit(() => setButtonsForModal);
|
35
|
-
setModalCloseCallback(() => toggleModal);
|
29
|
+
openModal({ title: "キープレフィックスの変更", prefill: macro.if_pressed, callbackOnSubmit: setButtonsForModal });
|
36
30
|
}
|
37
31
|
const isEnable = macro.if_pressed.length > 0;
|
38
32
|
|
@@ -47,7 +41,7 @@ const MacroSetting = ({ macro, layerKey }: MacroSettingProps) => {
|
|
47
41
|
{isEnable && `${macro.if_pressed.join(", ")}で発動`}
|
48
42
|
</li>
|
49
43
|
<div css={css`position: relative;`}>
|
50
|
-
{
|
44
|
+
{<ButtonsModal {...modalProps as ModalProps} />}
|
51
45
|
</div>
|
52
46
|
</>
|
53
47
|
)
|