formfx 1.0.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.
- package/LICENSE +28 -0
- package/README.ja.md +171 -0
- package/README.md +171 -0
- package/dist/chunk-6E5EWQVW.js +62 -0
- package/dist/chunk-6E5EWQVW.js.map +1 -0
- package/dist/chunk-GXMPHKTF.cjs +62 -0
- package/dist/chunk-GXMPHKTF.cjs.map +1 -0
- package/dist/editor.cjs +2 -0
- package/dist/editor.cjs.map +1 -0
- package/dist/editor.d.cts +141 -0
- package/dist/editor.d.ts +141 -0
- package/dist/editor.js +2 -0
- package/dist/editor.js.map +1 -0
- package/dist/index.cjs +138 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +138 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, https://monou.jp/, Kato Masaya
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# FormFx
|
|
2
|
+
|
|
3
|
+
強力で軽量、かつ宣言的なフォーム制御ライブラリ。
|
|
4
|
+
|
|
5
|
+
FormFxを使用すると、複雑なJavaScriptを書いたり重いフレームワークに頼ったりすることなく、シンプルなデータ属性やJSONルールを使用して、動的なフォーム動作(表示/非表示、有効/無効、バリデーション)を管理できます。
|
|
6
|
+
|
|
7
|
+
## なぜ FormFx なのか?
|
|
8
|
+
|
|
9
|
+
- **メンテナンス性の向上**: jQueryやVanilla JSのスパゲッティコードを回避できます。
|
|
10
|
+
- **軽量**: フル機能のフォームエンジンに比べてオーバーヘッドが最小限です。
|
|
11
|
+
- **宣言的**: `data-fx-*` 属性を使用して、ロジックをHTML内に配置できます。
|
|
12
|
+
- **安全**: `eval()` や `new Function()` を使用しません。式は安全に評価されます。
|
|
13
|
+
- **フレームワークに依存しない**: あらゆる環境で動作します。
|
|
14
|
+
|
|
15
|
+
## インストール
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install formfx
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
または CDN 経由:
|
|
22
|
+
```html
|
|
23
|
+
<script src="https://unpkg.com/formfx/dist/index.js"></script>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## クイックスタート (属性ベース)
|
|
27
|
+
|
|
28
|
+
HTMLのデータ属性を使用してルールを定義するだけです。
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<form id="my-form">
|
|
32
|
+
<input type="checkbox" id="toggle" name="toggle">
|
|
33
|
+
|
|
34
|
+
<div data-fx-show="toggle == true">
|
|
35
|
+
<p>このセクションはチェックボックスがオンのときだけ表示されます。</p>
|
|
36
|
+
<input type="text" name="optional_field" data-fx-required="toggle == true">
|
|
37
|
+
</div>
|
|
38
|
+
</form>
|
|
39
|
+
|
|
40
|
+
<script type="module">
|
|
41
|
+
import { FormFx } from 'formfx';
|
|
42
|
+
const fx = new FormFx(document.getElementById('my-form'));
|
|
43
|
+
fx.mount();
|
|
44
|
+
</script>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## JSON ルール (高度な使い方)
|
|
48
|
+
|
|
49
|
+
より複雑なロジックや、HTMLをクリーンに保ちたい場合に使用します。JSONルールは属性ベースのルールよりも優先されます。
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
const fx = new FormFx(form, {
|
|
53
|
+
rules: [
|
|
54
|
+
{
|
|
55
|
+
if: "total > 1000",
|
|
56
|
+
then: [
|
|
57
|
+
{ show: "#discount-section" },
|
|
58
|
+
{ required: "#coupon-code" }
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## リピーター (Repeater)
|
|
66
|
+
|
|
67
|
+
動的な行の追加・削除を簡単に処理できます。
|
|
68
|
+
|
|
69
|
+
```html
|
|
70
|
+
<div data-fx-repeater="items" data-fx-min="1" data-fx-max="5">
|
|
71
|
+
<div data-fx-list>
|
|
72
|
+
<template>
|
|
73
|
+
<div data-fx-item>
|
|
74
|
+
<input type="text" data-fx-field="name">
|
|
75
|
+
<button type="button" data-fx-remove-btn>削除</button>
|
|
76
|
+
</div>
|
|
77
|
+
</template>
|
|
78
|
+
</div>
|
|
79
|
+
<button type="button" data-fx-add-btn>アイテムを追加</button>
|
|
80
|
+
</div>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 行コンテキスト (`@row.field`)
|
|
84
|
+
|
|
85
|
+
同じリピーター行内のフィールドを参照します。
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<input type="number" data-fx-field="price">
|
|
89
|
+
<div data-fx-show="@row.price > 100">高額アイテムです!</div>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 永続化 (Persistence)
|
|
93
|
+
|
|
94
|
+
フォームルールの状態を自動的に保存・復元します。
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
const fx = new FormFx(form, {
|
|
98
|
+
persist: {
|
|
99
|
+
key: 'my-form-settings',
|
|
100
|
+
storage: 'localStorage'
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## ルールエディタ (オプションのアドオン)
|
|
106
|
+
|
|
107
|
+
フォームルールのためのビジュアルエディタです。
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
import { FormFx } from 'formfx';
|
|
111
|
+
// メインバンドルを小さく保つため、エディタは個別にインポートします
|
|
112
|
+
import { RuleEditor } from 'formfx/editor';
|
|
113
|
+
import 'formfx/editor.css';
|
|
114
|
+
|
|
115
|
+
const fx = new FormFx(form);
|
|
116
|
+
fx.mount();
|
|
117
|
+
|
|
118
|
+
const editor = new RuleEditor(fx, {
|
|
119
|
+
mount: document.getElementById('editor-container'),
|
|
120
|
+
mode: 'json'
|
|
121
|
+
});
|
|
122
|
+
editor.mount();
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## デバッグパネル
|
|
126
|
+
|
|
127
|
+
開発用のビジュアルデバッグ情報。
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
const fx = new FormFx(form, { debug: true });
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## API リファレンス
|
|
134
|
+
|
|
135
|
+
### `FormFxOptions`
|
|
136
|
+
- `disableOnHide`: Boolean (デフォルト `true`)
|
|
137
|
+
- `clearOnHide`: Boolean (デフォルト `false`)
|
|
138
|
+
- `rules`: `JSONRule[]`
|
|
139
|
+
- `persist`: `PersistOptions`
|
|
140
|
+
- `debug`: Boolean
|
|
141
|
+
|
|
142
|
+
### `FormFx` メソッド
|
|
143
|
+
- `mount()`: リスナーとオブザーバーを初期化します。
|
|
144
|
+
- `destroy()`: すべてをクリーンアップします(リスナーとオブザーバーを削除)。
|
|
145
|
+
- `pause()` / `resume()`: ルールの評価を一時停止または再開します。
|
|
146
|
+
- `reEvaluate()`: 手動で評価をトリガーします。
|
|
147
|
+
- `exportRules()`: 現在のJSONルールを取得します。
|
|
148
|
+
- `importRules(rules)`: 新しいJSONルールを読み込みます。
|
|
149
|
+
- `enableRule(ruleId)` / `disableRule(ruleId)`: IDでJSONルールを制御します。
|
|
150
|
+
|
|
151
|
+
## 互換性
|
|
152
|
+
|
|
153
|
+
- モダンブラウザ (Chrome, Firefox, Safari, Edge)
|
|
154
|
+
- `eval()` を使用していません。厳格な CSP 環境でも安全です。
|
|
155
|
+
|
|
156
|
+
## セキュリティ
|
|
157
|
+
|
|
158
|
+
- 独自のトークナイザーとパーサーにより、式は特定の安全な操作のみ実行可能です。
|
|
159
|
+
- 式の中から `window` や `document` などのグローバルオブジェクトにはアクセスできません。
|
|
160
|
+
|
|
161
|
+
## ロードマップ
|
|
162
|
+
|
|
163
|
+
- **v1.1**: 式内での非同期関数のサポート。
|
|
164
|
+
- **v2.0**: カスタムエフェクト用のプラグインシステム。
|
|
165
|
+
|
|
166
|
+
## ライセンス
|
|
167
|
+
|
|
168
|
+
BSD 3-Clause License
|
|
169
|
+
|
|
170
|
+
### 貢献に関する同意
|
|
171
|
+
本リポジトリへの貢献(プルリクエスト等)を行う場合、寄与者ライセンス同意書(CLA)に同意したものとみなされます。これには著作者人格権の不行使や、オーナーによる将来のライセンス変更・商用製品への組み込みの許可が含まれます。詳細は [CONTRIBUTING.ja.md](CONTRIBUTING.ja.md) をご確認ください。
|
package/README.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# FormFx
|
|
2
|
+
|
|
3
|
+
Powerful, lightweight, and declarative form control library.
|
|
4
|
+
|
|
5
|
+
FormFx allows you to manage dynamic form behaviors (show/hide, enable/disable, validation) using simple data attributes or JSON rules, without writing complex JavaScript or relying on heavy frameworks.
|
|
6
|
+
|
|
7
|
+
## Why FormFx?
|
|
8
|
+
|
|
9
|
+
- **Better Maintenance**: Avoid jQuery/Vanilla JS spaghetti code.
|
|
10
|
+
- **Lightweight**: Minimal overhead compared to full-blown form engines.
|
|
11
|
+
- **Declarative**: Logic is co-located with HTML using `data-fx-*` attributes.
|
|
12
|
+
- **Safe**: No `eval()` or `new Function()`. Expressions are evaluated safely.
|
|
13
|
+
- **Framework Agnostic**: Works everywhere.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install formfx
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or via CDN:
|
|
22
|
+
```html
|
|
23
|
+
<script src="https://unpkg.com/formfx/dist/index.js"></script>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start (Attributes)
|
|
27
|
+
|
|
28
|
+
Just define rules in your HTML using data attributes.
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<form id="my-form">
|
|
32
|
+
<input type="checkbox" id="toggle" name="toggle">
|
|
33
|
+
|
|
34
|
+
<div data-fx-show="toggle == true">
|
|
35
|
+
<p>This is only visible when the checkbox is checked.</p>
|
|
36
|
+
<input type="text" name="optional_field" data-fx-required="toggle == true">
|
|
37
|
+
</div>
|
|
38
|
+
</form>
|
|
39
|
+
|
|
40
|
+
<script type="module">
|
|
41
|
+
import { FormFx } from 'formfx';
|
|
42
|
+
const fx = new FormFx(document.getElementById('my-form'));
|
|
43
|
+
fx.mount();
|
|
44
|
+
</script>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## JSON Rules (Advanced)
|
|
48
|
+
|
|
49
|
+
For more complex logic or when you want to keep HTML clean. JSON rules take precedence over attributes.
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
const fx = new FormFx(form, {
|
|
53
|
+
rules: [
|
|
54
|
+
{
|
|
55
|
+
if: "total > 1000",
|
|
56
|
+
then: [
|
|
57
|
+
{ show: "#discount-section" },
|
|
58
|
+
{ required: "#coupon-code" }
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Repeater
|
|
66
|
+
|
|
67
|
+
Handle dynamic rows with ease.
|
|
68
|
+
|
|
69
|
+
```html
|
|
70
|
+
<div data-fx-repeater="items" data-fx-min="1" data-fx-max="5">
|
|
71
|
+
<div data-fx-list>
|
|
72
|
+
<template>
|
|
73
|
+
<div data-fx-item>
|
|
74
|
+
<input type="text" data-fx-field="name">
|
|
75
|
+
<button type="button" data-fx-remove-btn>Remove</button>
|
|
76
|
+
</div>
|
|
77
|
+
</template>
|
|
78
|
+
</div>
|
|
79
|
+
<button type="button" data-fx-add-btn>Add Item</button>
|
|
80
|
+
</div>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Row Context (`@row.field`)
|
|
84
|
+
|
|
85
|
+
Refer to fields within the same repeater row.
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<input type="number" data-fx-field="price">
|
|
89
|
+
<div data-fx-show="@row.price > 100">Expensive item!</div>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Persistence
|
|
93
|
+
|
|
94
|
+
Automatically save and restore form rules state.
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
const fx = new FormFx(form, {
|
|
98
|
+
persist: {
|
|
99
|
+
key: 'my-form-settings',
|
|
100
|
+
storage: 'localStorage'
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Rule Editor (Optional Add-on)
|
|
106
|
+
|
|
107
|
+
A visual editor for your form rules.
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
import { FormFx } from 'formfx';
|
|
111
|
+
// Import editor separately to keep main bundle small
|
|
112
|
+
import { RuleEditor } from 'formfx/editor';
|
|
113
|
+
import 'formfx/editor.css';
|
|
114
|
+
|
|
115
|
+
const fx = new FormFx(form);
|
|
116
|
+
fx.mount();
|
|
117
|
+
|
|
118
|
+
const editor = new RuleEditor(fx, {
|
|
119
|
+
mount: document.getElementById('editor-container'),
|
|
120
|
+
mode: 'json'
|
|
121
|
+
});
|
|
122
|
+
editor.mount();
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Debug Panel
|
|
126
|
+
|
|
127
|
+
Visual debug information for development.
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
const fx = new FormFx(form, { debug: true });
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## API Reference
|
|
134
|
+
|
|
135
|
+
### `FormFxOptions`
|
|
136
|
+
- `disableOnHide`: Boolean (default `true`)
|
|
137
|
+
- `clearOnHide`: Boolean (default `false`)
|
|
138
|
+
- `rules`: `JSONRule[]`
|
|
139
|
+
- `persist`: `PersistOptions`
|
|
140
|
+
- `debug`: Boolean
|
|
141
|
+
|
|
142
|
+
### `FormFx` Methods
|
|
143
|
+
- `mount()`: Initialize listeners and observers.
|
|
144
|
+
- `destroy()`: Cleanup everything (removes listeners and observers).
|
|
145
|
+
- `pause()` / `resume()`: Pause or resume rule evaluation.
|
|
146
|
+
- `reEvaluate()`: Manually trigger evaluation.
|
|
147
|
+
- `exportRules()`: Get current JSON rules.
|
|
148
|
+
- `importRules(rules)`: Load new JSON rules.
|
|
149
|
+
- `enableRule(ruleId)` / `disableRule(ruleId)`: Control JSON rules by ID.
|
|
150
|
+
|
|
151
|
+
## Compatibility
|
|
152
|
+
|
|
153
|
+
- Modern browsers (Chrome, Firefox, Safari, Edge)
|
|
154
|
+
- No `eval()` used. Safe for strict CSP environments.
|
|
155
|
+
|
|
156
|
+
## Security
|
|
157
|
+
|
|
158
|
+
- Custom tokenizer and parser ensure expressions are only allowed to perform specific safe operations.
|
|
159
|
+
- No access to global objects like `window` or `document` from within expressions.
|
|
160
|
+
|
|
161
|
+
## Roadmap
|
|
162
|
+
|
|
163
|
+
- **v1.1**: Async function support in expressions.
|
|
164
|
+
- **v2.0**: Plugin system for custom effects.
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
This project is licensed under the BSD 3-Clause License - see the [LICENSE](LICENSE) file for details.
|
|
169
|
+
|
|
170
|
+
### Note for Contributors
|
|
171
|
+
By contributing to this repository, you agree to the terms of our Contributor License Agreement (CLA), which includes the waiver of moral rights and allows for future re-licensing or commercial use by the project owner.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
var f=class{constructor(t,e){this.fx=t;this.options=e;this.container=document.createElement("div"),this.container.className="formfx-editor";}container;activeRuleIndex=null;mount(){this.options.mount.appendChild(this.container),this.render();}render(){let t=this.fx.exportRules();this.container.innerHTML=`
|
|
2
|
+
<div class="formfx-editor-header">
|
|
3
|
+
<div class="formfx-editor-title">FormFx Rule Editor</div>
|
|
4
|
+
<div class="formfx-editor-actions">
|
|
5
|
+
<button class="formfx-btn formfx-btn-primary" id="fx-add-rule">+ Add Rule</button>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="formfx-editor-body">
|
|
9
|
+
<div class="formfx-rule-list" id="fx-rule-list"></div>
|
|
10
|
+
<div class="formfx-rule-form" id="fx-rule-form"></div>
|
|
11
|
+
</div>
|
|
12
|
+
`,this.renderRuleList(t),this.renderRuleForm(t),this.container.querySelector("#fx-add-rule")?.addEventListener("click",()=>{this.addRule();});}renderRuleList(t){let e=this.container.querySelector("#fx-rule-list");e&&(e.innerHTML=t.map((i,r)=>`
|
|
13
|
+
<div class="formfx-rule-item ${this.activeRuleIndex===r?"active":""}" data-index="${r}">
|
|
14
|
+
<div class="formfx-rule-item-id">${i.id||"(no id)"}</div>
|
|
15
|
+
<div class="formfx-rule-item-expr">if: ${i.if}</div>
|
|
16
|
+
</div>
|
|
17
|
+
`).join("")||'<div class="formfx-empty-state">No rules defined</div>',e.querySelectorAll(".formfx-rule-item").forEach(i=>{i.addEventListener("click",()=>{this.activeRuleIndex=parseInt(i.getAttribute("data-index")||"0"),this.render();});}));}renderRuleForm(t){let e=this.container.querySelector("#fx-rule-form");if(!e||this.activeRuleIndex===null||!t[this.activeRuleIndex]){e&&(e.innerHTML='<div class="formfx-empty-state">Select a rule to edit</div>');return}let i=t[this.activeRuleIndex];e.innerHTML=`
|
|
18
|
+
<div class="formfx-field-group">
|
|
19
|
+
<label class="formfx-label">Rule ID</label>
|
|
20
|
+
<input type="text" class="formfx-input" id="fx-edit-id" value="${i.id||""}">
|
|
21
|
+
</div>
|
|
22
|
+
<div class="formfx-field-group">
|
|
23
|
+
<label class="formfx-label">If Condition (DSL)</label>
|
|
24
|
+
<textarea class="formfx-textarea" id="fx-edit-if" rows="3">${i.if}</textarea>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="formfx-field-group">
|
|
28
|
+
<label class="formfx-label">Then Effects</label>
|
|
29
|
+
<div id="fx-then-list"></div>
|
|
30
|
+
<button class="formfx-btn" id="fx-add-then">+ Add Effect</button>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="formfx-field-group">
|
|
34
|
+
<label class="formfx-label">Else Effects (Optional)</label>
|
|
35
|
+
<div id="fx-else-list"></div>
|
|
36
|
+
<button class="formfx-btn" id="fx-add-else">+ Add Effect</button>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div style="margin-top: 40px; display: flex; justify-content: space-between;">
|
|
40
|
+
<button class="formfx-btn formfx-btn-primary" id="fx-save-rule">Save Changes</button>
|
|
41
|
+
<button class="formfx-btn formfx-btn-danger" id="fx-delete-rule">Delete Rule</button>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="formfx-json-preview">
|
|
45
|
+
<div class="formfx-label">JSON Preview (Readonly)</div>
|
|
46
|
+
<div class="formfx-json-content">${JSON.stringify(i,null,2)}</div>
|
|
47
|
+
</div>
|
|
48
|
+
`,this.renderEffectList("then",i.then),this.renderEffectList("else",i.else||[]),e.querySelector("#fx-save-rule")?.addEventListener("click",()=>this.saveRule()),e.querySelector("#fx-delete-rule")?.addEventListener("click",()=>this.deleteRule()),e.querySelector("#fx-add-then")?.addEventListener("click",()=>this.addEffect("then")),e.querySelector("#fx-add-else")?.addEventListener("click",()=>this.addEffect("else"));}renderEffectList(t,e){let i=this.container.querySelector(`#fx-${t}-list`);i&&(i.innerHTML=e.map((r,s)=>{let n=Object.entries(r)[0]||["show",""],l=n[0],o=n[1];return `
|
|
49
|
+
<div class="formfx-effect-row" data-type="${t}" data-index="${s}">
|
|
50
|
+
<select class="formfx-select fx-effect-type" style="width: 120px;">
|
|
51
|
+
<option value="show" ${l==="show"?"selected":""}>show</option>
|
|
52
|
+
<option value="hide" ${l==="hide"?"selected":""}>hide</option>
|
|
53
|
+
<option value="required" ${l==="required"?"selected":""}>require</option>
|
|
54
|
+
<option value="disabled" ${l==="disabled"?"selected":""}>disabled</option>
|
|
55
|
+
<option value="enable" ${l==="enable"?"selected":""}>enable</option>
|
|
56
|
+
<option value="clear" ${l==="clear"?"selected":""}>clear</option>
|
|
57
|
+
</select>
|
|
58
|
+
<input type="text" class="formfx-input fx-effect-selector" placeholder="#id" value="${o}">
|
|
59
|
+
<button class="formfx-btn formfx-btn-danger fx-remove-effect">\xD7</button>
|
|
60
|
+
</div>
|
|
61
|
+
`}).join(""),i.querySelectorAll(".fx-remove-effect").forEach((r,s)=>{r.addEventListener("click",()=>{e.splice(s,1),this.renderRuleForm(this.fx.exportRules());});}));}addRule(){let t=this.fx.exportRules(),e={id:`rule_${Date.now()}`,if:"true",then:[{show:""}]};t.push(e),this.activeRuleIndex=t.length-1,this.fx.importRules(t),this.render();}saveRule(){if(this.activeRuleIndex===null)return;this.fx.pause();let t=this.fx.exportRules(),e=t[this.activeRuleIndex];e.id=this.container.querySelector("#fx-edit-id").value,e.if=this.container.querySelector("#fx-edit-if").value;let i=r=>{let s=this.container.querySelectorAll(`.formfx-effect-row[data-type="${r}"]`),n=[];return s.forEach(l=>{let o=l.querySelector(".fx-effect-type").value,a=l.querySelector(".fx-effect-selector").value,d={};d[o]=a,n.push(d);}),n};e.then=i("then"),e.else=i("else"),e.else.length===0&&delete e.else,this.fx.importRules(t),this.fx.resume(),this.render();}deleteRule(){if(this.activeRuleIndex===null||!confirm("Are you sure you want to delete this rule?"))return;let t=this.fx.exportRules();t.splice(this.activeRuleIndex,1),this.activeRuleIndex=null,this.fx.importRules(t),this.render();}addEffect(t){if(this.activeRuleIndex===null)return;let e=this.fx.exportRules(),i=e[this.activeRuleIndex];t==="then"?i.then.push({show:""}):(i.else=i.else||[],i.else.push({show:""})),this.renderRuleForm(e);}};export{f as a};//# sourceMappingURL=chunk-6E5EWQVW.js.map
|
|
62
|
+
//# sourceMappingURL=chunk-6E5EWQVW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/editor/RuleEditor.ts"],"names":["RuleEditor","fx","options","rules","listEl","rule","index","item","formEl","type","effects","eff","i","entry","effectType","selector","btn","newRule","readEffects","rows","row","obj"],"mappings":"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","file":"chunk-6E5EWQVW.js","sourcesContent":["import { FormFx } from '../core/FormFx';\r\nimport { RuleEditorOptions, JSONRule } from '../types';\r\n\r\nexport class RuleEditor {\r\n private container: HTMLElement;\r\n private activeRuleIndex: number | null = null;\r\n\r\n constructor(\r\n private fx: FormFx,\r\n private options: RuleEditorOptions\r\n ) {\r\n this.container = document.createElement('div');\r\n this.container.className = 'formfx-editor';\r\n }\r\n\r\n mount(): void {\r\n this.options.mount.appendChild(this.container);\r\n this.render();\r\n }\r\n\r\n private render(): void {\r\n const rules = this.fx.exportRules();\r\n \r\n this.container.innerHTML = `\r\n <div class=\"formfx-editor-header\">\r\n <div class=\"formfx-editor-title\">FormFx Rule Editor</div>\r\n <div class=\"formfx-editor-actions\">\r\n <button class=\"formfx-btn formfx-btn-primary\" id=\"fx-add-rule\">+ Add Rule</button>\r\n </div>\r\n </div>\r\n <div class=\"formfx-editor-body\">\r\n <div class=\"formfx-rule-list\" id=\"fx-rule-list\"></div>\r\n <div class=\"formfx-rule-form\" id=\"fx-rule-form\"></div>\r\n </div>\r\n `;\r\n\r\n this.renderRuleList(rules);\r\n this.renderRuleForm(rules);\r\n\r\n this.container.querySelector('#fx-add-rule')?.addEventListener('click', () => {\r\n this.addRule();\r\n });\r\n }\r\n\r\n private renderRuleList(rules: JSONRule[]): void {\r\n const listEl = this.container.querySelector('#fx-rule-list');\r\n if (!listEl) return;\r\n\r\n listEl.innerHTML = rules.map((rule, index) => `\r\n <div class=\"formfx-rule-item ${this.activeRuleIndex === index ? 'active' : ''}\" data-index=\"${index}\">\r\n <div class=\"formfx-rule-item-id\">${rule.id || '(no id)'}</div>\r\n <div class=\"formfx-rule-item-expr\">if: ${rule.if}</div>\r\n </div>\r\n `).join('') || '<div class=\"formfx-empty-state\">No rules defined</div>';\r\n\r\n listEl.querySelectorAll('.formfx-rule-item').forEach(item => {\r\n item.addEventListener('click', () => {\r\n this.activeRuleIndex = parseInt(item.getAttribute('data-index') || '0');\r\n this.render();\r\n });\r\n });\r\n }\r\n\r\n private renderRuleForm(rules: JSONRule[]): void {\r\n const formEl = this.container.querySelector('#fx-rule-form');\r\n if (!formEl || this.activeRuleIndex === null || !rules[this.activeRuleIndex]) {\r\n if (formEl) formEl.innerHTML = '<div class=\"formfx-empty-state\">Select a rule to edit</div>';\r\n return;\r\n }\r\n\r\n const rule = rules[this.activeRuleIndex];\r\n formEl.innerHTML = `\r\n <div class=\"formfx-field-group\">\r\n <label class=\"formfx-label\">Rule ID</label>\r\n <input type=\"text\" class=\"formfx-input\" id=\"fx-edit-id\" value=\"${rule.id || ''}\">\r\n </div>\r\n <div class=\"formfx-field-group\">\r\n <label class=\"formfx-label\">If Condition (DSL)</label>\r\n <textarea class=\"formfx-textarea\" id=\"fx-edit-if\" rows=\"3\">${rule.if}</textarea>\r\n </div>\r\n \r\n <div class=\"formfx-field-group\">\r\n <label class=\"formfx-label\">Then Effects</label>\r\n <div id=\"fx-then-list\"></div>\r\n <button class=\"formfx-btn\" id=\"fx-add-then\">+ Add Effect</button>\r\n </div>\r\n\r\n <div class=\"formfx-field-group\">\r\n <label class=\"formfx-label\">Else Effects (Optional)</label>\r\n <div id=\"fx-else-list\"></div>\r\n <button class=\"formfx-btn\" id=\"fx-add-else\">+ Add Effect</button>\r\n </div>\r\n\r\n <div style=\"margin-top: 40px; display: flex; justify-content: space-between;\">\r\n <button class=\"formfx-btn formfx-btn-primary\" id=\"fx-save-rule\">Save Changes</button>\r\n <button class=\"formfx-btn formfx-btn-danger\" id=\"fx-delete-rule\">Delete Rule</button>\r\n </div>\r\n\r\n <div class=\"formfx-json-preview\">\r\n <div class=\"formfx-label\">JSON Preview (Readonly)</div>\r\n <div class=\"formfx-json-content\">${JSON.stringify(rule, null, 2)}</div>\r\n </div>\r\n `;\r\n\r\n this.renderEffectList('then', rule.then);\r\n this.renderEffectList('else', rule.else || []);\r\n\r\n // Events\r\n formEl.querySelector('#fx-save-rule')?.addEventListener('click', () => this.saveRule());\r\n formEl.querySelector('#fx-delete-rule')?.addEventListener('click', () => this.deleteRule());\r\n formEl.querySelector('#fx-add-then')?.addEventListener('click', () => this.addEffect('then'));\r\n formEl.querySelector('#fx-add-else')?.addEventListener('click', () => this.addEffect('else'));\r\n }\r\n\r\n private renderEffectList(type: 'then' | 'else', effects: any[]): void {\r\n const listEl = this.container.querySelector(`#fx-${type}-list`);\r\n if (!listEl) return;\r\n\r\n listEl.innerHTML = effects.map((eff, i) => {\r\n const entry = Object.entries(eff)[0] || ['show', ''];\r\n const effectType = entry[0];\r\n const selector = entry[1];\r\n\r\n return `\r\n <div class=\"formfx-effect-row\" data-type=\"${type}\" data-index=\"${i}\">\r\n <select class=\"formfx-select fx-effect-type\" style=\"width: 120px;\">\r\n <option value=\"show\" ${effectType === 'show' ? 'selected' : ''}>show</option>\r\n <option value=\"hide\" ${effectType === 'hide' ? 'selected' : ''}>hide</option>\r\n <option value=\"required\" ${effectType === 'required' ? 'selected' : ''}>require</option>\r\n <option value=\"disabled\" ${effectType === 'disabled' ? 'selected' : ''}>disabled</option>\r\n <option value=\"enable\" ${effectType === 'enable' ? 'selected' : ''}>enable</option>\r\n <option value=\"clear\" ${effectType === 'clear' ? 'selected' : ''}>clear</option>\r\n </select>\r\n <input type=\"text\" class=\"formfx-input fx-effect-selector\" placeholder=\"#id\" value=\"${selector}\">\r\n <button class=\"formfx-btn formfx-btn-danger fx-remove-effect\">×</button>\r\n </div>\r\n `;\r\n }).join('');\r\n\r\n listEl.querySelectorAll('.fx-remove-effect').forEach((btn, i) => {\r\n btn.addEventListener('click', () => {\r\n effects.splice(i, 1);\r\n this.renderRuleForm(this.fx.exportRules());\r\n });\r\n });\r\n }\r\n\r\n private addRule(): void {\r\n const rules = this.fx.exportRules();\r\n const newRule: JSONRule = {\r\n id: `rule_${Date.now()}`,\r\n if: 'true',\r\n then: [{ show: '' }]\r\n };\r\n rules.push(newRule);\r\n this.activeRuleIndex = rules.length - 1;\r\n this.fx.importRules(rules);\r\n this.render();\r\n }\r\n\r\n private saveRule(): void {\r\n if (this.activeRuleIndex === null) return;\r\n \r\n this.fx.pause(); // 編集適用中は pause\r\n\r\n const rules = this.fx.exportRules();\r\n const rule = rules[this.activeRuleIndex];\r\n\r\n rule.id = (this.container.querySelector('#fx-edit-id') as HTMLInputElement).value;\r\n rule.if = (this.container.querySelector('#fx-edit-if') as HTMLTextAreaElement).value;\r\n\r\n const readEffects = (type: 'then' | 'else') => {\r\n const rows = this.container.querySelectorAll(`.formfx-effect-row[data-type=\"${type}\"]`);\r\n const effects: any[] = [];\r\n rows.forEach(row => {\r\n const effectType = (row.querySelector('.fx-effect-type') as HTMLSelectElement).value;\r\n const selector = (row.querySelector('.fx-effect-selector') as HTMLInputElement).value;\r\n const obj: any = {};\r\n obj[effectType] = selector;\r\n effects.push(obj);\r\n });\r\n return effects;\r\n };\r\n\r\n rule.then = readEffects('then');\r\n rule.else = readEffects('else');\r\n if (rule.else.length === 0) delete rule.else;\r\n\r\n this.fx.importRules(rules);\r\n this.fx.resume();\r\n this.render();\r\n }\r\n\r\n private deleteRule(): void {\r\n if (this.activeRuleIndex === null || !confirm('Are you sure you want to delete this rule?')) return;\r\n const rules = this.fx.exportRules();\r\n rules.splice(this.activeRuleIndex, 1);\r\n this.activeRuleIndex = null;\r\n this.fx.importRules(rules);\r\n this.render();\r\n }\r\n\r\n private addEffect(type: 'then' | 'else'): void {\r\n if (this.activeRuleIndex === null) return;\r\n const rules = this.fx.exportRules();\r\n const rule = rules[this.activeRuleIndex];\r\n if (type === 'then') {\r\n rule.then.push({ show: '' });\r\n } else {\r\n rule.else = rule.else || [];\r\n rule.else.push({ show: '' });\r\n }\r\n this.renderRuleForm(rules);\r\n }\r\n}\r\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';var f=class{constructor(t,e){this.fx=t;this.options=e;this.container=document.createElement("div"),this.container.className="formfx-editor";}container;activeRuleIndex=null;mount(){this.options.mount.appendChild(this.container),this.render();}render(){let t=this.fx.exportRules();this.container.innerHTML=`
|
|
2
|
+
<div class="formfx-editor-header">
|
|
3
|
+
<div class="formfx-editor-title">FormFx Rule Editor</div>
|
|
4
|
+
<div class="formfx-editor-actions">
|
|
5
|
+
<button class="formfx-btn formfx-btn-primary" id="fx-add-rule">+ Add Rule</button>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="formfx-editor-body">
|
|
9
|
+
<div class="formfx-rule-list" id="fx-rule-list"></div>
|
|
10
|
+
<div class="formfx-rule-form" id="fx-rule-form"></div>
|
|
11
|
+
</div>
|
|
12
|
+
`,this.renderRuleList(t),this.renderRuleForm(t),this.container.querySelector("#fx-add-rule")?.addEventListener("click",()=>{this.addRule();});}renderRuleList(t){let e=this.container.querySelector("#fx-rule-list");e&&(e.innerHTML=t.map((i,r)=>`
|
|
13
|
+
<div class="formfx-rule-item ${this.activeRuleIndex===r?"active":""}" data-index="${r}">
|
|
14
|
+
<div class="formfx-rule-item-id">${i.id||"(no id)"}</div>
|
|
15
|
+
<div class="formfx-rule-item-expr">if: ${i.if}</div>
|
|
16
|
+
</div>
|
|
17
|
+
`).join("")||'<div class="formfx-empty-state">No rules defined</div>',e.querySelectorAll(".formfx-rule-item").forEach(i=>{i.addEventListener("click",()=>{this.activeRuleIndex=parseInt(i.getAttribute("data-index")||"0"),this.render();});}));}renderRuleForm(t){let e=this.container.querySelector("#fx-rule-form");if(!e||this.activeRuleIndex===null||!t[this.activeRuleIndex]){e&&(e.innerHTML='<div class="formfx-empty-state">Select a rule to edit</div>');return}let i=t[this.activeRuleIndex];e.innerHTML=`
|
|
18
|
+
<div class="formfx-field-group">
|
|
19
|
+
<label class="formfx-label">Rule ID</label>
|
|
20
|
+
<input type="text" class="formfx-input" id="fx-edit-id" value="${i.id||""}">
|
|
21
|
+
</div>
|
|
22
|
+
<div class="formfx-field-group">
|
|
23
|
+
<label class="formfx-label">If Condition (DSL)</label>
|
|
24
|
+
<textarea class="formfx-textarea" id="fx-edit-if" rows="3">${i.if}</textarea>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="formfx-field-group">
|
|
28
|
+
<label class="formfx-label">Then Effects</label>
|
|
29
|
+
<div id="fx-then-list"></div>
|
|
30
|
+
<button class="formfx-btn" id="fx-add-then">+ Add Effect</button>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="formfx-field-group">
|
|
34
|
+
<label class="formfx-label">Else Effects (Optional)</label>
|
|
35
|
+
<div id="fx-else-list"></div>
|
|
36
|
+
<button class="formfx-btn" id="fx-add-else">+ Add Effect</button>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div style="margin-top: 40px; display: flex; justify-content: space-between;">
|
|
40
|
+
<button class="formfx-btn formfx-btn-primary" id="fx-save-rule">Save Changes</button>
|
|
41
|
+
<button class="formfx-btn formfx-btn-danger" id="fx-delete-rule">Delete Rule</button>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="formfx-json-preview">
|
|
45
|
+
<div class="formfx-label">JSON Preview (Readonly)</div>
|
|
46
|
+
<div class="formfx-json-content">${JSON.stringify(i,null,2)}</div>
|
|
47
|
+
</div>
|
|
48
|
+
`,this.renderEffectList("then",i.then),this.renderEffectList("else",i.else||[]),e.querySelector("#fx-save-rule")?.addEventListener("click",()=>this.saveRule()),e.querySelector("#fx-delete-rule")?.addEventListener("click",()=>this.deleteRule()),e.querySelector("#fx-add-then")?.addEventListener("click",()=>this.addEffect("then")),e.querySelector("#fx-add-else")?.addEventListener("click",()=>this.addEffect("else"));}renderEffectList(t,e){let i=this.container.querySelector(`#fx-${t}-list`);i&&(i.innerHTML=e.map((r,s)=>{let n=Object.entries(r)[0]||["show",""],l=n[0],o=n[1];return `
|
|
49
|
+
<div class="formfx-effect-row" data-type="${t}" data-index="${s}">
|
|
50
|
+
<select class="formfx-select fx-effect-type" style="width: 120px;">
|
|
51
|
+
<option value="show" ${l==="show"?"selected":""}>show</option>
|
|
52
|
+
<option value="hide" ${l==="hide"?"selected":""}>hide</option>
|
|
53
|
+
<option value="required" ${l==="required"?"selected":""}>require</option>
|
|
54
|
+
<option value="disabled" ${l==="disabled"?"selected":""}>disabled</option>
|
|
55
|
+
<option value="enable" ${l==="enable"?"selected":""}>enable</option>
|
|
56
|
+
<option value="clear" ${l==="clear"?"selected":""}>clear</option>
|
|
57
|
+
</select>
|
|
58
|
+
<input type="text" class="formfx-input fx-effect-selector" placeholder="#id" value="${o}">
|
|
59
|
+
<button class="formfx-btn formfx-btn-danger fx-remove-effect">\xD7</button>
|
|
60
|
+
</div>
|
|
61
|
+
`}).join(""),i.querySelectorAll(".fx-remove-effect").forEach((r,s)=>{r.addEventListener("click",()=>{e.splice(s,1),this.renderRuleForm(this.fx.exportRules());});}));}addRule(){let t=this.fx.exportRules(),e={id:`rule_${Date.now()}`,if:"true",then:[{show:""}]};t.push(e),this.activeRuleIndex=t.length-1,this.fx.importRules(t),this.render();}saveRule(){if(this.activeRuleIndex===null)return;this.fx.pause();let t=this.fx.exportRules(),e=t[this.activeRuleIndex];e.id=this.container.querySelector("#fx-edit-id").value,e.if=this.container.querySelector("#fx-edit-if").value;let i=r=>{let s=this.container.querySelectorAll(`.formfx-effect-row[data-type="${r}"]`),n=[];return s.forEach(l=>{let o=l.querySelector(".fx-effect-type").value,a=l.querySelector(".fx-effect-selector").value,d={};d[o]=a,n.push(d);}),n};e.then=i("then"),e.else=i("else"),e.else.length===0&&delete e.else,this.fx.importRules(t),this.fx.resume(),this.render();}deleteRule(){if(this.activeRuleIndex===null||!confirm("Are you sure you want to delete this rule?"))return;let t=this.fx.exportRules();t.splice(this.activeRuleIndex,1),this.activeRuleIndex=null,this.fx.importRules(t),this.render();}addEffect(t){if(this.activeRuleIndex===null)return;let e=this.fx.exportRules(),i=e[this.activeRuleIndex];t==="then"?i.then.push({show:""}):(i.else=i.else||[],i.else.push({show:""})),this.renderRuleForm(e);}};exports.a=f;//# sourceMappingURL=chunk-GXMPHKTF.cjs.map
|
|
62
|
+
//# sourceMappingURL=chunk-GXMPHKTF.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/editor/RuleEditor.ts"],"names":["RuleEditor","fx","options","rules","listEl","rule","index","item","formEl","type","effects","eff","i","entry","effectType","selector","btn","newRule","readEffects","rows","row","obj"],"mappings":"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","file":"chunk-GXMPHKTF.cjs","sourcesContent":["import { FormFx } from '../core/FormFx';\r\nimport { RuleEditorOptions, JSONRule } from '../types';\r\n\r\nexport class RuleEditor {\r\n private container: HTMLElement;\r\n private activeRuleIndex: number | null = null;\r\n\r\n constructor(\r\n private fx: FormFx,\r\n private options: RuleEditorOptions\r\n ) {\r\n this.container = document.createElement('div');\r\n this.container.className = 'formfx-editor';\r\n }\r\n\r\n mount(): void {\r\n this.options.mount.appendChild(this.container);\r\n this.render();\r\n }\r\n\r\n private render(): void {\r\n const rules = this.fx.exportRules();\r\n \r\n this.container.innerHTML = `\r\n <div class=\"formfx-editor-header\">\r\n <div class=\"formfx-editor-title\">FormFx Rule Editor</div>\r\n <div class=\"formfx-editor-actions\">\r\n <button class=\"formfx-btn formfx-btn-primary\" id=\"fx-add-rule\">+ Add Rule</button>\r\n </div>\r\n </div>\r\n <div class=\"formfx-editor-body\">\r\n <div class=\"formfx-rule-list\" id=\"fx-rule-list\"></div>\r\n <div class=\"formfx-rule-form\" id=\"fx-rule-form\"></div>\r\n </div>\r\n `;\r\n\r\n this.renderRuleList(rules);\r\n this.renderRuleForm(rules);\r\n\r\n this.container.querySelector('#fx-add-rule')?.addEventListener('click', () => {\r\n this.addRule();\r\n });\r\n }\r\n\r\n private renderRuleList(rules: JSONRule[]): void {\r\n const listEl = this.container.querySelector('#fx-rule-list');\r\n if (!listEl) return;\r\n\r\n listEl.innerHTML = rules.map((rule, index) => `\r\n <div class=\"formfx-rule-item ${this.activeRuleIndex === index ? 'active' : ''}\" data-index=\"${index}\">\r\n <div class=\"formfx-rule-item-id\">${rule.id || '(no id)'}</div>\r\n <div class=\"formfx-rule-item-expr\">if: ${rule.if}</div>\r\n </div>\r\n `).join('') || '<div class=\"formfx-empty-state\">No rules defined</div>';\r\n\r\n listEl.querySelectorAll('.formfx-rule-item').forEach(item => {\r\n item.addEventListener('click', () => {\r\n this.activeRuleIndex = parseInt(item.getAttribute('data-index') || '0');\r\n this.render();\r\n });\r\n });\r\n }\r\n\r\n private renderRuleForm(rules: JSONRule[]): void {\r\n const formEl = this.container.querySelector('#fx-rule-form');\r\n if (!formEl || this.activeRuleIndex === null || !rules[this.activeRuleIndex]) {\r\n if (formEl) formEl.innerHTML = '<div class=\"formfx-empty-state\">Select a rule to edit</div>';\r\n return;\r\n }\r\n\r\n const rule = rules[this.activeRuleIndex];\r\n formEl.innerHTML = `\r\n <div class=\"formfx-field-group\">\r\n <label class=\"formfx-label\">Rule ID</label>\r\n <input type=\"text\" class=\"formfx-input\" id=\"fx-edit-id\" value=\"${rule.id || ''}\">\r\n </div>\r\n <div class=\"formfx-field-group\">\r\n <label class=\"formfx-label\">If Condition (DSL)</label>\r\n <textarea class=\"formfx-textarea\" id=\"fx-edit-if\" rows=\"3\">${rule.if}</textarea>\r\n </div>\r\n \r\n <div class=\"formfx-field-group\">\r\n <label class=\"formfx-label\">Then Effects</label>\r\n <div id=\"fx-then-list\"></div>\r\n <button class=\"formfx-btn\" id=\"fx-add-then\">+ Add Effect</button>\r\n </div>\r\n\r\n <div class=\"formfx-field-group\">\r\n <label class=\"formfx-label\">Else Effects (Optional)</label>\r\n <div id=\"fx-else-list\"></div>\r\n <button class=\"formfx-btn\" id=\"fx-add-else\">+ Add Effect</button>\r\n </div>\r\n\r\n <div style=\"margin-top: 40px; display: flex; justify-content: space-between;\">\r\n <button class=\"formfx-btn formfx-btn-primary\" id=\"fx-save-rule\">Save Changes</button>\r\n <button class=\"formfx-btn formfx-btn-danger\" id=\"fx-delete-rule\">Delete Rule</button>\r\n </div>\r\n\r\n <div class=\"formfx-json-preview\">\r\n <div class=\"formfx-label\">JSON Preview (Readonly)</div>\r\n <div class=\"formfx-json-content\">${JSON.stringify(rule, null, 2)}</div>\r\n </div>\r\n `;\r\n\r\n this.renderEffectList('then', rule.then);\r\n this.renderEffectList('else', rule.else || []);\r\n\r\n // Events\r\n formEl.querySelector('#fx-save-rule')?.addEventListener('click', () => this.saveRule());\r\n formEl.querySelector('#fx-delete-rule')?.addEventListener('click', () => this.deleteRule());\r\n formEl.querySelector('#fx-add-then')?.addEventListener('click', () => this.addEffect('then'));\r\n formEl.querySelector('#fx-add-else')?.addEventListener('click', () => this.addEffect('else'));\r\n }\r\n\r\n private renderEffectList(type: 'then' | 'else', effects: any[]): void {\r\n const listEl = this.container.querySelector(`#fx-${type}-list`);\r\n if (!listEl) return;\r\n\r\n listEl.innerHTML = effects.map((eff, i) => {\r\n const entry = Object.entries(eff)[0] || ['show', ''];\r\n const effectType = entry[0];\r\n const selector = entry[1];\r\n\r\n return `\r\n <div class=\"formfx-effect-row\" data-type=\"${type}\" data-index=\"${i}\">\r\n <select class=\"formfx-select fx-effect-type\" style=\"width: 120px;\">\r\n <option value=\"show\" ${effectType === 'show' ? 'selected' : ''}>show</option>\r\n <option value=\"hide\" ${effectType === 'hide' ? 'selected' : ''}>hide</option>\r\n <option value=\"required\" ${effectType === 'required' ? 'selected' : ''}>require</option>\r\n <option value=\"disabled\" ${effectType === 'disabled' ? 'selected' : ''}>disabled</option>\r\n <option value=\"enable\" ${effectType === 'enable' ? 'selected' : ''}>enable</option>\r\n <option value=\"clear\" ${effectType === 'clear' ? 'selected' : ''}>clear</option>\r\n </select>\r\n <input type=\"text\" class=\"formfx-input fx-effect-selector\" placeholder=\"#id\" value=\"${selector}\">\r\n <button class=\"formfx-btn formfx-btn-danger fx-remove-effect\">×</button>\r\n </div>\r\n `;\r\n }).join('');\r\n\r\n listEl.querySelectorAll('.fx-remove-effect').forEach((btn, i) => {\r\n btn.addEventListener('click', () => {\r\n effects.splice(i, 1);\r\n this.renderRuleForm(this.fx.exportRules());\r\n });\r\n });\r\n }\r\n\r\n private addRule(): void {\r\n const rules = this.fx.exportRules();\r\n const newRule: JSONRule = {\r\n id: `rule_${Date.now()}`,\r\n if: 'true',\r\n then: [{ show: '' }]\r\n };\r\n rules.push(newRule);\r\n this.activeRuleIndex = rules.length - 1;\r\n this.fx.importRules(rules);\r\n this.render();\r\n }\r\n\r\n private saveRule(): void {\r\n if (this.activeRuleIndex === null) return;\r\n \r\n this.fx.pause(); // 編集適用中は pause\r\n\r\n const rules = this.fx.exportRules();\r\n const rule = rules[this.activeRuleIndex];\r\n\r\n rule.id = (this.container.querySelector('#fx-edit-id') as HTMLInputElement).value;\r\n rule.if = (this.container.querySelector('#fx-edit-if') as HTMLTextAreaElement).value;\r\n\r\n const readEffects = (type: 'then' | 'else') => {\r\n const rows = this.container.querySelectorAll(`.formfx-effect-row[data-type=\"${type}\"]`);\r\n const effects: any[] = [];\r\n rows.forEach(row => {\r\n const effectType = (row.querySelector('.fx-effect-type') as HTMLSelectElement).value;\r\n const selector = (row.querySelector('.fx-effect-selector') as HTMLInputElement).value;\r\n const obj: any = {};\r\n obj[effectType] = selector;\r\n effects.push(obj);\r\n });\r\n return effects;\r\n };\r\n\r\n rule.then = readEffects('then');\r\n rule.else = readEffects('else');\r\n if (rule.else.length === 0) delete rule.else;\r\n\r\n this.fx.importRules(rules);\r\n this.fx.resume();\r\n this.render();\r\n }\r\n\r\n private deleteRule(): void {\r\n if (this.activeRuleIndex === null || !confirm('Are you sure you want to delete this rule?')) return;\r\n const rules = this.fx.exportRules();\r\n rules.splice(this.activeRuleIndex, 1);\r\n this.activeRuleIndex = null;\r\n this.fx.importRules(rules);\r\n this.render();\r\n }\r\n\r\n private addEffect(type: 'then' | 'else'): void {\r\n if (this.activeRuleIndex === null) return;\r\n const rules = this.fx.exportRules();\r\n const rule = rules[this.activeRuleIndex];\r\n if (type === 'then') {\r\n rule.then.push({ show: '' });\r\n } else {\r\n rule.else = rule.else || [];\r\n rule.else.push({ show: '' });\r\n }\r\n this.renderRuleForm(rules);\r\n }\r\n}\r\n"]}
|
package/dist/editor.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"editor.cjs"}
|