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
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FormFx Public Types
|
|
3
|
+
* v1.0
|
|
4
|
+
*/
|
|
5
|
+
interface FormFxOptions {
|
|
6
|
+
/**
|
|
7
|
+
* 非表示になったとき、内部の入力値をdisabledにするかどうか
|
|
8
|
+
* @default true
|
|
9
|
+
*/
|
|
10
|
+
disableOnHide?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* 非表示になったとき、内部の入力値をクリアするかどうか
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
clearOnHide?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* デバッグモード
|
|
18
|
+
* @default false
|
|
19
|
+
*/
|
|
20
|
+
debug?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* JSON形式のルール定義
|
|
23
|
+
*/
|
|
24
|
+
rules?: JSONRule[];
|
|
25
|
+
/**
|
|
26
|
+
* ルールの永続化設定 (v0.4)
|
|
27
|
+
*/
|
|
28
|
+
persist?: PersistOptions;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 永続化オプション (v0.4)
|
|
32
|
+
*/
|
|
33
|
+
interface PersistOptions {
|
|
34
|
+
key: string;
|
|
35
|
+
storage: 'localStorage' | 'sessionStorage';
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Rule Editor 設定 (v0.4)
|
|
39
|
+
*/
|
|
40
|
+
interface RuleEditorOptions {
|
|
41
|
+
mount: HTMLElement;
|
|
42
|
+
mode: 'json' | 'attributes';
|
|
43
|
+
}
|
|
44
|
+
type FXValue = string | number | boolean | null | FXValue[];
|
|
45
|
+
type EffectType = 'show' | 'hide' | 'required' | 'disabled' | 'enable' | 'clear';
|
|
46
|
+
interface JSONEffect {
|
|
47
|
+
show?: string;
|
|
48
|
+
hide?: string;
|
|
49
|
+
require?: string;
|
|
50
|
+
required?: string;
|
|
51
|
+
disabled?: string;
|
|
52
|
+
enable?: string;
|
|
53
|
+
clear?: string;
|
|
54
|
+
}
|
|
55
|
+
interface JSONRule {
|
|
56
|
+
id?: string;
|
|
57
|
+
if: string;
|
|
58
|
+
then: JSONEffect[];
|
|
59
|
+
else?: JSONEffect[];
|
|
60
|
+
disabled?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 外部公開用のデバッグ情報
|
|
64
|
+
*/
|
|
65
|
+
interface FormFxDebugInfo {
|
|
66
|
+
ruleId?: string;
|
|
67
|
+
type: EffectType;
|
|
68
|
+
expression: string;
|
|
69
|
+
result: boolean | 'error';
|
|
70
|
+
error?: string;
|
|
71
|
+
element: HTMLElement;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
declare class FormFx {
|
|
75
|
+
private formElement;
|
|
76
|
+
private rules;
|
|
77
|
+
private options;
|
|
78
|
+
private cleanupEvents;
|
|
79
|
+
private cleanupRepeaters;
|
|
80
|
+
private observer;
|
|
81
|
+
private debugPanel;
|
|
82
|
+
private isPaused;
|
|
83
|
+
constructor(formElement: HTMLElement, options?: FormFxOptions);
|
|
84
|
+
mount(): void;
|
|
85
|
+
reEvaluate(): void;
|
|
86
|
+
private setupEvents;
|
|
87
|
+
private refreshRules;
|
|
88
|
+
private evaluate;
|
|
89
|
+
/**
|
|
90
|
+
* ルール評価を一時停止する (v0.4)
|
|
91
|
+
*/
|
|
92
|
+
pause(): void;
|
|
93
|
+
/**
|
|
94
|
+
* ルール評価を再開する (v0.4)
|
|
95
|
+
*/
|
|
96
|
+
resume(): void;
|
|
97
|
+
/**
|
|
98
|
+
* JSONルールをエクスポートする (v0.4)
|
|
99
|
+
*/
|
|
100
|
+
exportRules(): JSONRule[];
|
|
101
|
+
/**
|
|
102
|
+
* JSONルールをインポートする (v0.4)
|
|
103
|
+
*/
|
|
104
|
+
importRules(rules: JSONRule[]): void;
|
|
105
|
+
/**
|
|
106
|
+
* Rule Editor を有効化する (v0.4)
|
|
107
|
+
*/
|
|
108
|
+
enableRuleEditor(options: RuleEditorOptions): Promise<void>;
|
|
109
|
+
private loadPersistedRules;
|
|
110
|
+
private savePersistedRules;
|
|
111
|
+
/**
|
|
112
|
+
* JSONルールを有効化する
|
|
113
|
+
* @param ruleId
|
|
114
|
+
*/
|
|
115
|
+
enableRule(ruleId: string): void;
|
|
116
|
+
/**
|
|
117
|
+
* JSONルールを無効化する
|
|
118
|
+
* @param ruleId
|
|
119
|
+
*/
|
|
120
|
+
disableRule(ruleId: string): void;
|
|
121
|
+
destroy(): void;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
declare class RuleEditor {
|
|
125
|
+
private fx;
|
|
126
|
+
private options;
|
|
127
|
+
private container;
|
|
128
|
+
private activeRuleIndex;
|
|
129
|
+
constructor(fx: FormFx, options: RuleEditorOptions);
|
|
130
|
+
mount(): void;
|
|
131
|
+
private render;
|
|
132
|
+
private renderRuleList;
|
|
133
|
+
private renderRuleForm;
|
|
134
|
+
private renderEffectList;
|
|
135
|
+
private addRule;
|
|
136
|
+
private saveRule;
|
|
137
|
+
private deleteRule;
|
|
138
|
+
private addEffect;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export { type EffectType as E, FormFx as F, type JSONEffect as J, type PersistOptions as P, type RuleEditorOptions as R, RuleEditor, type FormFxOptions as a, type FXValue as b, type JSONRule as c, type FormFxDebugInfo as d };
|
package/dist/editor.d.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FormFx Public Types
|
|
3
|
+
* v1.0
|
|
4
|
+
*/
|
|
5
|
+
interface FormFxOptions {
|
|
6
|
+
/**
|
|
7
|
+
* 非表示になったとき、内部の入力値をdisabledにするかどうか
|
|
8
|
+
* @default true
|
|
9
|
+
*/
|
|
10
|
+
disableOnHide?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* 非表示になったとき、内部の入力値をクリアするかどうか
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
clearOnHide?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* デバッグモード
|
|
18
|
+
* @default false
|
|
19
|
+
*/
|
|
20
|
+
debug?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* JSON形式のルール定義
|
|
23
|
+
*/
|
|
24
|
+
rules?: JSONRule[];
|
|
25
|
+
/**
|
|
26
|
+
* ルールの永続化設定 (v0.4)
|
|
27
|
+
*/
|
|
28
|
+
persist?: PersistOptions;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 永続化オプション (v0.4)
|
|
32
|
+
*/
|
|
33
|
+
interface PersistOptions {
|
|
34
|
+
key: string;
|
|
35
|
+
storage: 'localStorage' | 'sessionStorage';
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Rule Editor 設定 (v0.4)
|
|
39
|
+
*/
|
|
40
|
+
interface RuleEditorOptions {
|
|
41
|
+
mount: HTMLElement;
|
|
42
|
+
mode: 'json' | 'attributes';
|
|
43
|
+
}
|
|
44
|
+
type FXValue = string | number | boolean | null | FXValue[];
|
|
45
|
+
type EffectType = 'show' | 'hide' | 'required' | 'disabled' | 'enable' | 'clear';
|
|
46
|
+
interface JSONEffect {
|
|
47
|
+
show?: string;
|
|
48
|
+
hide?: string;
|
|
49
|
+
require?: string;
|
|
50
|
+
required?: string;
|
|
51
|
+
disabled?: string;
|
|
52
|
+
enable?: string;
|
|
53
|
+
clear?: string;
|
|
54
|
+
}
|
|
55
|
+
interface JSONRule {
|
|
56
|
+
id?: string;
|
|
57
|
+
if: string;
|
|
58
|
+
then: JSONEffect[];
|
|
59
|
+
else?: JSONEffect[];
|
|
60
|
+
disabled?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 外部公開用のデバッグ情報
|
|
64
|
+
*/
|
|
65
|
+
interface FormFxDebugInfo {
|
|
66
|
+
ruleId?: string;
|
|
67
|
+
type: EffectType;
|
|
68
|
+
expression: string;
|
|
69
|
+
result: boolean | 'error';
|
|
70
|
+
error?: string;
|
|
71
|
+
element: HTMLElement;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
declare class FormFx {
|
|
75
|
+
private formElement;
|
|
76
|
+
private rules;
|
|
77
|
+
private options;
|
|
78
|
+
private cleanupEvents;
|
|
79
|
+
private cleanupRepeaters;
|
|
80
|
+
private observer;
|
|
81
|
+
private debugPanel;
|
|
82
|
+
private isPaused;
|
|
83
|
+
constructor(formElement: HTMLElement, options?: FormFxOptions);
|
|
84
|
+
mount(): void;
|
|
85
|
+
reEvaluate(): void;
|
|
86
|
+
private setupEvents;
|
|
87
|
+
private refreshRules;
|
|
88
|
+
private evaluate;
|
|
89
|
+
/**
|
|
90
|
+
* ルール評価を一時停止する (v0.4)
|
|
91
|
+
*/
|
|
92
|
+
pause(): void;
|
|
93
|
+
/**
|
|
94
|
+
* ルール評価を再開する (v0.4)
|
|
95
|
+
*/
|
|
96
|
+
resume(): void;
|
|
97
|
+
/**
|
|
98
|
+
* JSONルールをエクスポートする (v0.4)
|
|
99
|
+
*/
|
|
100
|
+
exportRules(): JSONRule[];
|
|
101
|
+
/**
|
|
102
|
+
* JSONルールをインポートする (v0.4)
|
|
103
|
+
*/
|
|
104
|
+
importRules(rules: JSONRule[]): void;
|
|
105
|
+
/**
|
|
106
|
+
* Rule Editor を有効化する (v0.4)
|
|
107
|
+
*/
|
|
108
|
+
enableRuleEditor(options: RuleEditorOptions): Promise<void>;
|
|
109
|
+
private loadPersistedRules;
|
|
110
|
+
private savePersistedRules;
|
|
111
|
+
/**
|
|
112
|
+
* JSONルールを有効化する
|
|
113
|
+
* @param ruleId
|
|
114
|
+
*/
|
|
115
|
+
enableRule(ruleId: string): void;
|
|
116
|
+
/**
|
|
117
|
+
* JSONルールを無効化する
|
|
118
|
+
* @param ruleId
|
|
119
|
+
*/
|
|
120
|
+
disableRule(ruleId: string): void;
|
|
121
|
+
destroy(): void;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
declare class RuleEditor {
|
|
125
|
+
private fx;
|
|
126
|
+
private options;
|
|
127
|
+
private container;
|
|
128
|
+
private activeRuleIndex;
|
|
129
|
+
constructor(fx: FormFx, options: RuleEditorOptions);
|
|
130
|
+
mount(): void;
|
|
131
|
+
private render;
|
|
132
|
+
private renderRuleList;
|
|
133
|
+
private renderRuleForm;
|
|
134
|
+
private renderEffectList;
|
|
135
|
+
private addRule;
|
|
136
|
+
private saveRule;
|
|
137
|
+
private deleteRule;
|
|
138
|
+
private addEffect;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export { type EffectType as E, FormFx as F, type JSONEffect as J, type PersistOptions as P, type RuleEditorOptions as R, RuleEditor, type FormFxOptions as a, type FXValue as b, type JSONRule as c, type FormFxDebugInfo as d };
|
package/dist/editor.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"editor.js"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
'use strict';var chunkGXMPHKTF_cjs=require('./chunk-GXMPHKTF.cjs');function w(e){if(e instanceof HTMLInputElement){if(e.type==="checkbox")return e.checked;if(e.type==="radio"){if(e.name){let r=(e.closest("form")||document).querySelector(`input[name="${e.name}"]:checked`);return r?r.value:null}return e.checked?e.value:null}return e.type==="number"||e.type==="range"?e.value===""?null:Number(e.value):e.value}return e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement?e.value:null}function y(e){e instanceof HTMLInputElement?e.type==="checkbox"||e.type==="radio"?e.checked=false:e.value="":e instanceof HTMLSelectElement?(e.selectedIndex=-1,e.value=""):e instanceof HTMLTextAreaElement?e.value="":e.querySelectorAll("input, select, textarea").forEach(r=>y(r));}function E(e,t){"disabled"in e?e.disabled=t:e.querySelectorAll("input, select, textarea").forEach(s=>{s.disabled=t;});}function O(e,t,r){let s=e.style.display!=="none";t?(e.style.display=e.dataset.fxOriginalDisplay||"",r.disableOnHide&&!s&&E(e,false)):(s&&!e.dataset.fxOriginalDisplay&&(e.dataset.fxOriginalDisplay=e.style.display),e.style.display="none",s&&((e.dataset.fxClearOnHide==="true"||r.clearOnHide)&&y(e),r.disableOnHide&&E(e,true)));}function N(e,t){e instanceof HTMLInputElement||e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement?e.required=t:e.querySelectorAll("input, select, textarea").forEach(s=>{s.required=t;});}function S(e,t){E(e,t);}function H(e){y(e);}function I(e,t){let r=e.querySelector(`#${t.replace(/([#;?%&,.+*~\':"!^$[\]()=>|/@])/g,"\\$1")}`);if(r)return r;let s=e.querySelector(`[name="${t}"]`);return s||e.querySelector(`[data-fx-id="${t}"]`)}function h(e,t){switch(e.type){case "Literal":return e.value;case "Identifier":{if(e.name.startsWith("@row.")){let r=e.name.substring(5);return t.row&&t.row[r]!==void 0?t.row[r]:null}return t.global[e.name]!==void 0?t.global[e.name]:null}case "UnaryExpression":if(e.operator==="!")return !h(e.argument,t);throw new Error(`Unknown unary operator: ${e.operator}`);case "BinaryExpression":{let r=h(e.left,t),s=h(e.right,t);if(e.operator==="==")return r==s;if(e.operator==="!=")return r!=s;if(e.operator===">")return r>s;if(e.operator===">=")return r>=s;if(e.operator==="<")return r<s;if(e.operator==="<=")return r<=s;if(e.operator==="+")return r+s;if(e.operator==="-")return r-s;if(e.operator==="*")return r*s;if(e.operator==="/")return r/s;if(e.operator==="%")return r%s;throw new Error(`Unknown binary operator: ${e.operator}`)}case "LogicalExpression":{let r=h(e.left,t);if(e.operator==="&&")return r&&h(e.right,t);if(e.operator==="||")return r||h(e.right,t);throw new Error(`Unknown logical operator: ${e.operator}`)}case "CallExpression":{let r=t.functions[e.callee];if(!r)throw new Error(`Unknown function: ${e.callee}`);let s=e.arguments.map(i=>h(i,t));return r(...s)}case "ArrayExpression":return e.elements.map(r=>h(r,t));default:throw new Error(`Unknown AST node type: ${e.type}`)}}function R(e){let t=0;function r(){return s()}function s(){let o=i();for(;t<e.length&&e[t].value==="||";){let c=e[t].value;t++;let f=i();o={type:"LogicalExpression",operator:c,left:o,right:f};}return o}function i(){let o=n();for(;t<e.length&&e[t].value==="&&";){let c=e[t].value;t++;let f=n();o={type:"LogicalExpression",operator:c,left:o,right:f};}return o}function n(){let o=u(),c=["==","!=",">",">=","<","<="];for(;t<e.length&&c.includes(e[t].value);){let f=e[t].value;t++;let d=u();o={type:"BinaryExpression",operator:f,left:o,right:d};}return o}function u(){let o=p();for(;t<e.length&&(e[t].value==="+"||e[t].value==="-");){let c=e[t].value;t++;let f=p();o={type:"BinaryExpression",operator:c,left:o,right:f};}return o}function p(){let o=a();for(;t<e.length&&(e[t].value==="*"||e[t].value==="/"||e[t].value==="%");){let c=e[t].value;t++;let f=a();o={type:"BinaryExpression",operator:c,left:o,right:f};}return o}function a(){return t<e.length&&e[t].value==="!"?(t++,{type:"UnaryExpression",operator:"!",argument:a()}):l()}function l(){let o=e[t];if(o.type==="PAREN"&&o.value==="("){t++;let c=r();if(e[t]?.value!==")")throw new Error("Expected )");return t++,c}if(o.type==="IDENTIFIER"){if(t++,t<e.length&&e[t].type==="PAREN"&&e[t].value==="("){t++;let c=[];if(!(e[t].type==="PAREN"&&e[t].value===")"))for(;;){if(c.push(r()),e[t].type==="COMMA"){t++;continue}if(e[t].type==="PAREN"&&e[t].value===")")break;throw new Error(`Expected , or ) in function call "${o.value}"`)}return t++,{type:"CallExpression",callee:o.value,arguments:c}}return {type:"Identifier",name:o.value}}if(o.type==="BRACKET"&&o.value==="["){t++;let c=[];if(!(e[t].type==="BRACKET"&&e[t].value==="]"))for(;;){if(c.push(r()),e[t].type==="COMMA"){t++;continue}if(e[t].type==="BRACKET"&&e[t].value==="]")break;throw new Error("Expected , or ] in array literal")}return t++,{type:"ArrayExpression",elements:c}}if(o.type==="NUMBER")return t++,{type:"Literal",value:parseFloat(o.value)};if(o.type==="STRING")return t++,{type:"Literal",value:o.value};if(o.type==="BOOLEAN")return t++,{type:"Literal",value:o.value==="true"};throw new Error(`Unexpected token: ${o.value}`)}return r()}function A(e){let t=[],r=0;for(;r<e.length;){let s=e[r];if(/\s/.test(s)){r++;continue}if(s==="("||s===")"){t.push({type:"PAREN",value:s}),r++;continue}if(s==="["||s==="]"){t.push({type:"BRACKET",value:s}),r++;continue}if(s===","){t.push({type:"COMMA",value:s}),r++;continue}if(s==="#"||s==="@"){let n=s==="@",u=n?"@":"";if(r++,n){let p="";for(;r<e.length&&/[a-zA-Z0-9_-]/.test(e[r]);)p+=e[r],r++;if(u+=p,e[r]===".")for(u+=".",r++;r<e.length&&/[a-zA-Z0-9_-]/.test(e[r]);)u+=e[r],r++;}else for(;r<e.length&&/[a-zA-Z0-9_-]/.test(e[r]);)u+=e[r],r++;t.push({type:"IDENTIFIER",value:u});continue}if(/[0-9]/.test(s)){let n="";for(;r<e.length&&/[0-9.]/.test(e[r]);)n+=e[r],r++;t.push({type:"NUMBER",value:n});continue}if(s==="'"){let n="";for(r++;r<e.length&&e[r]!=="'";)n+=e[r],r++;r++,t.push({type:"STRING",value:n});continue}if(s==="="&&e[r+1]==="="){t.push({type:"OPERATOR",value:"=="}),r+=2;continue}if(s==="!"&&e[r+1]==="="){t.push({type:"OPERATOR",value:"!="}),r+=2;continue}if(s===">"&&e[r+1]==="="){t.push({type:"OPERATOR",value:">="}),r+=2;continue}if(s==="<"&&e[r+1]==="="){t.push({type:"OPERATOR",value:"<="}),r+=2;continue}if(s===">"){t.push({type:"OPERATOR",value:">"}),r++;continue}if(s==="<"){t.push({type:"OPERATOR",value:"<"}),r++;continue}if(s==="&"&&e[r+1]==="&"){t.push({type:"OPERATOR",value:"&&"}),r+=2;continue}if(s==="|"&&e[r+1]==="|"){t.push({type:"OPERATOR",value:"||"}),r+=2;continue}if(s==="!"){t.push({type:"OPERATOR",value:"!"}),r++;continue}if(s==="+"||s==="-"||s==="*"||s==="/"||s==="%"){t.push({type:"OPERATOR",value:s}),r++;continue}let i=e.slice(r).match(/^[a-zA-Z][a-zA-Z0-9_]*/);if(i){let n=i[0];n==="true"||n==="false"?(t.push({type:"BOOLEAN",value:n}),r+=n.length):(t.push({type:"IDENTIFIER",value:n}),r+=n.length);continue}throw new Error(`Unexpected character: ${s} at position ${r}`)}return t}function F(e,t={}){let r=[];return ["show","required","disabled","enable","clear"].forEach(i=>{let n=`data-fx-${i}`;e.querySelectorAll(`[${n}]`).forEach(p=>{let a=p.getAttribute(n)||"";try{let l=A(a),o=R(l);r.push({element:p,type:i,expression:a,ast:o,source:"attribute"});}catch(l){console.warn(`[FormFx] Failed to parse expression "${a}" for ${n}:`,l);}});}),t.rules&&t.rules.forEach(i=>{try{let n=A(i.if),u=R(n),p=(a,l)=>{a.forEach(o=>{Object.entries(o).forEach(([c,f])=>{if(typeof f!="string"||!f)return;let d=c==="require"?"required":c,g=i.if,m=u;d==="hide"&&(d="show",m={type:"UnaryExpression",operator:"!",argument:u},g=`!(${i.if})`),l&&(m={type:"UnaryExpression",operator:"!",argument:m},g=`!(${g})`),e.querySelectorAll(f).forEach(v=>{let q=v;r.push({id:i.id,element:q,type:d,expression:g,ast:m,source:"json",rawRule:i,disabled:i.disabled});});});});};p(i.then,!1),i.else&&p(i.else,!0);}catch(n){console.warn("[FormFx] Failed to parse JSON rule:",i,n);}}),r}function x(e){let t=new Set,r=new Set;function s(i){i.type==="Identifier"?i.name.startsWith("@row.")?r.add(i.name.substring(5)):t.add(i.name):i.type==="BinaryExpression"||i.type==="LogicalExpression"?(s(i.left),s(i.right)):i.type==="UnaryExpression"?s(i.argument):i.type==="CallExpression"?i.arguments.forEach(s):i.type==="ArrayExpression"&&i.elements.forEach(s);}return s(e),{global:Array.from(t),row:Array.from(r)}}var z={in:(e,t)=>Array.isArray(t)?t.includes(e):false,contains:(e,t)=>typeof e!="string"?false:e.includes(t)};function $(e,t,r){let s=[],i={},n=new Set;t.forEach(a=>{x(a.ast).global.forEach(l=>n.add(l));}),n.forEach(a=>{let l=I(e,a);l?i[a]=w(l):i[a]=null;});let u={global:i,functions:z},p=new Map;return t.forEach(a=>{p.has(a.element)||p.set(a.element,new Map),p.get(a.element).set(a.type,a.source);}),t.forEach(a=>{if(!a.disabled&&!(a.source==="attribute"&&p.get(a.element)?.get(a.type)==="json"))try{let l,o=a.element.closest("[data-fx-item]");o&&(l={},x(a.ast).row.forEach(m=>{let T=o.querySelector(`[data-fx-field="${m}"]`);if(T){let v=T.querySelector("input, select, textarea");l[m]=v?w(v):null;}else l[m]=null;}));let c={...u,row:l},d=!!h(a.ast,c);switch(r.debug&&(s.push({rule:a,result:d,elements:[a.element]}),console.debug(`[FormFx Debug] Rule: ${a.type}="${a.expression}" (source: ${a.source}, result: ${d})`,{element:a.element,result:d,context:c})),a.type){case "show":O(a.element,d,r);break;case "required":N(a.element,d);break;case "disabled":S(a.element,d);break;case "enable":S(a.element,!d);break;case "clear":d&&a.lastResult===!1&&H(a.element);break}a.lastResult=d;}catch(l){let o=l instanceof Error?l.message:String(l);r.debug?(s.push({rule:a,result:"error",error:o,elements:[a.element]}),console.warn(`[FormFx Debug] Evaluation failed: "${a.expression}"`,l)):console.warn(`[FormFx] Failed to evaluate expression "${a.expression}":`,l);}}),s}function P(e,t,r){let s=()=>{t();};return e.addEventListener("input",s),e.addEventListener("change",s),()=>{e.removeEventListener("input",s),e.removeEventListener("change",s);}}function C(e,t){let r=new MutationObserver(s=>{let i=false;for(let n of s)if(n.type==="childList"&&(n.addedNodes.length>0||n.removedNodes.length>0)){i=true;break}i&&t();});return r.observe(e,{childList:true,subtree:true}),r}function D(e,t){let r=e.querySelectorAll("[data-fx-repeater]"),s=[];return r.forEach(i=>{let n={name:i.getAttribute("data-fx-name")||"",max:parseInt(i.getAttribute("data-fx-max")||"10",10),addButton:i.querySelector("[data-fx-add]"),listContainer:i.querySelector("[data-fx-list]")||i,template:i.querySelector("template[data-fx-template]")};if(!n.template){console.warn("[FormFx] Repeater template not found",i);return}let u=()=>{n.listContainer.querySelectorAll("[data-fx-item]").length<n.max&&(k(n),L(n),t());};n.addButton&&(n.addButton.addEventListener("click",u),s.push(()=>n.addButton?.removeEventListener("click",u)));let p=a=>{let l=a.target;if(l.closest("[data-fx-remove]")){let o=l.closest("[data-fx-item]");o&&(o.remove(),L(n),t());}};n.listContainer.addEventListener("click",p),s.push(()=>n.listContainer.removeEventListener("click",p)),L(n);}),()=>s.forEach(i=>i())}function k(e){let r=e.template.content.cloneNode(true).querySelector("[data-fx-item]");r&&e.listContainer.appendChild(r);}function L(e){let t=e.listContainer.querySelectorAll("[data-fx-item]");t.forEach((r,s)=>{r.setAttribute("data-fx-index",s.toString()),r.querySelectorAll("[data-fx-field]").forEach(n=>{let u=n.getAttribute("data-fx-field");u&&n.querySelectorAll("input, select, textarea").forEach(a=>{a.name=`${e.name}[${s}].${u}`;});});}),e.addButton&&(t.length>=e.max?e.addButton.style.display="none":e.addButton.style.display="");}var b=class{container=null;shadow=null;persistInfo=null;isMinimized=false;pos={x:10,y:10};isDragging=false;dragStart={x:0,y:0};constructor(){this.loadState(),this.container=document.createElement("div"),this.container.id="formfx-debug-panel",this.shadow=this.container.attachShadow({mode:"open"}),this.renderInitial(),document.body.appendChild(this.container),this.setupDragging();}loadState(){try{let t=localStorage.getItem("formfx-debug-state");if(t){let r=JSON.parse(t);this.isMinimized=!!r.isMinimized,r.pos&&(this.pos=r.pos);}}catch{}}saveState(){try{localStorage.setItem("formfx-debug-state",JSON.stringify({isMinimized:this.isMinimized,pos:this.pos}));}catch{}}setupDragging(){if(!this.shadow||!this.container)return;let t=this.shadow.querySelector(".header");if(!t)return;t.style.cursor="move",t.addEventListener("mousedown",s=>{this.isDragging=true,this.dragStart={x:s.clientX+this.pos.x,y:s.clientY+this.pos.y},s.preventDefault();}),window.addEventListener("mousemove",s=>{this.isDragging&&(this.pos.x=this.dragStart.x-s.clientX,this.pos.y=this.dragStart.y-s.clientY,this.updatePosition());}),window.addEventListener("mouseup",()=>{this.isDragging&&(this.isDragging=false,this.saveState());}),this.shadow.getElementById("toggle-minimize")?.addEventListener("click",s=>{s.stopPropagation(),this.isMinimized=!this.isMinimized,this.renderInitial(),this.updatePosition(),this.saveState();});}updatePosition(){this.container&&(this.container.style.bottom=`${this.pos.y}px`,this.container.style.right=`${this.pos.x}px`);}setPersistInfo(t){this.persistInfo=t;}renderInitial(){this.shadow&&(this.shadow.innerHTML=`
|
|
2
|
+
<style>
|
|
3
|
+
:host {
|
|
4
|
+
position: fixed;
|
|
5
|
+
bottom: ${this.pos.y}px;
|
|
6
|
+
right: ${this.pos.x}px;
|
|
7
|
+
width: 350px;
|
|
8
|
+
max-height: 80vh;
|
|
9
|
+
background: rgba(0, 0, 0, 0.85);
|
|
10
|
+
color: white;
|
|
11
|
+
font-family: monospace;
|
|
12
|
+
font-size: 12px;
|
|
13
|
+
border-radius: 8px;
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
z-index: 10000;
|
|
16
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
|
17
|
+
border: 1px solid #444;
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
}
|
|
21
|
+
.header {
|
|
22
|
+
padding: 8px 12px;
|
|
23
|
+
background: #222;
|
|
24
|
+
border-bottom: 1px solid #444;
|
|
25
|
+
font-weight: bold;
|
|
26
|
+
display: flex;
|
|
27
|
+
justify-content: space-between;
|
|
28
|
+
align-items: center;
|
|
29
|
+
user-select: none;
|
|
30
|
+
}
|
|
31
|
+
.content {
|
|
32
|
+
padding: 8px;
|
|
33
|
+
overflow-y: auto;
|
|
34
|
+
display: ${this.isMinimized?"none":"block"};
|
|
35
|
+
}
|
|
36
|
+
.header-controls {
|
|
37
|
+
display: flex;
|
|
38
|
+
gap: 8px;
|
|
39
|
+
}
|
|
40
|
+
.btn-icon {
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
padding: 2px 6px;
|
|
43
|
+
border-radius: 4px;
|
|
44
|
+
background: #444;
|
|
45
|
+
border: none;
|
|
46
|
+
color: white;
|
|
47
|
+
font-size: 10px;
|
|
48
|
+
}
|
|
49
|
+
.btn-icon:hover {
|
|
50
|
+
background: #666;
|
|
51
|
+
}
|
|
52
|
+
.rule {
|
|
53
|
+
margin-bottom: 8px;
|
|
54
|
+
padding: 6px;
|
|
55
|
+
border-radius: 4px;
|
|
56
|
+
background: #333;
|
|
57
|
+
}
|
|
58
|
+
.rule-header {
|
|
59
|
+
display: flex;
|
|
60
|
+
justify-content: space-between;
|
|
61
|
+
margin-bottom: 4px;
|
|
62
|
+
}
|
|
63
|
+
.rule-id {
|
|
64
|
+
color: #aaa;
|
|
65
|
+
font-size: 10px;
|
|
66
|
+
}
|
|
67
|
+
.rule-expr {
|
|
68
|
+
word-break: break-all;
|
|
69
|
+
color: #88ccff;
|
|
70
|
+
}
|
|
71
|
+
.status {
|
|
72
|
+
font-weight: bold;
|
|
73
|
+
padding: 2px 4px;
|
|
74
|
+
border-radius: 2px;
|
|
75
|
+
font-size: 10px;
|
|
76
|
+
}
|
|
77
|
+
.status-true { background: #228b22; }
|
|
78
|
+
.status-false { background: #b22222; }
|
|
79
|
+
.status-error { background: #ff4500; }
|
|
80
|
+
.status-disabled { background: #666; color: #ccc; }
|
|
81
|
+
.persist-badge {
|
|
82
|
+
font-size: 9px;
|
|
83
|
+
background: #444;
|
|
84
|
+
padding: 1px 4px;
|
|
85
|
+
border-radius: 4px;
|
|
86
|
+
color: #00ff00;
|
|
87
|
+
border: 1px solid #00ff00;
|
|
88
|
+
}
|
|
89
|
+
.header-top {
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
gap: 8px;
|
|
93
|
+
}
|
|
94
|
+
.effects {
|
|
95
|
+
margin-top: 4px;
|
|
96
|
+
padding-left: 12px;
|
|
97
|
+
border-left: 2px solid #555;
|
|
98
|
+
font-size: 11px;
|
|
99
|
+
}
|
|
100
|
+
.effect-item {
|
|
101
|
+
margin-top: 2px;
|
|
102
|
+
}
|
|
103
|
+
.effect-skipped {
|
|
104
|
+
color: #888;
|
|
105
|
+
text-decoration: line-through;
|
|
106
|
+
}
|
|
107
|
+
.error-msg {
|
|
108
|
+
color: #ff6347;
|
|
109
|
+
font-size: 10px;
|
|
110
|
+
margin-top: 4px;
|
|
111
|
+
}
|
|
112
|
+
</style>
|
|
113
|
+
<div class="header">
|
|
114
|
+
<div class="header-top">
|
|
115
|
+
<span>FormFx Debug</span>
|
|
116
|
+
${this.persistInfo?'<span class="persist-badge">PERSIST</span>':""}
|
|
117
|
+
</div>
|
|
118
|
+
<div class="header-controls">
|
|
119
|
+
<button id="toggle-minimize" class="btn-icon">${this.isMinimized?"\u25A1":"_"} </button>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
<div id="rule-list" class="content">
|
|
123
|
+
Waiting for evaluation...
|
|
124
|
+
</div>
|
|
125
|
+
`,this.setupDragging());}update(t){if(!this.shadow)return;let r=this.shadow.getElementById("rule-list");r&&(r.innerHTML=t.map(s=>{let{rule:i,result:n,error:u}=s,p=n==="error"?"status-error":n?"status-true":"status-false",a=n==="error"?"ERROR":n.toString().toUpperCase();i.disabled&&(p="status-disabled",a="DISABLED");let l=`${i.type} ${this.getSelector(i.element)}`;return n!==true&&n!=="error"&&!i.disabled&&(l=`<span class="effect-skipped">${l} (skipped)</span>`),i.disabled&&(l=`<span class="effect-skipped">${l}</span>`),`
|
|
126
|
+
<div class="rule">
|
|
127
|
+
<div class="rule-header">
|
|
128
|
+
<span class="rule-id">${i.id||(i.source==="attribute"?"Attr":"JSON")}</span>
|
|
129
|
+
<span class="status ${p}">${a}</span>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="rule-expr">${i.expression}</div>
|
|
132
|
+
<div class="effects">
|
|
133
|
+
<div class="effect-item">${l}</div>
|
|
134
|
+
</div>
|
|
135
|
+
${u?`<div class="error-msg">Error: ${u}</div>`:""}
|
|
136
|
+
</div>
|
|
137
|
+
`}).join(""));}getSelector(t){return t.id?`#${t.id}`:t.className?`.${t.className.split(" ").join(".")}`:t.tagName.toLowerCase()}destroy(){this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container);}};var M=class{constructor(t,r={}){this.formElement=t;this.options={disableOnHide:true,clearOnHide:false,...r},this.loadPersistedRules();}rules=[];options;cleanupEvents=null;cleanupRepeaters=null;observer=null;debugPanel=null;isPaused=false;mount(){this.options.debug&&(this.debugPanel=new b,this.options.persist&&this.debugPanel.setPersistInfo(this.options.persist)),this.refreshRules(),this.cleanupRepeaters=D(this.formElement,()=>{this.refreshRules(),this.setupEvents(),this.evaluate();}),this.setupEvents(),this.observer=C(this.formElement,()=>{this.refreshRules(),this.setupEvents(),this.evaluate();}),this.evaluate();}reEvaluate(){this.evaluate();}setupEvents(){this.cleanupEvents&&this.cleanupEvents();let t=new Set;this.rules.forEach(r=>{x(r.ast).global.forEach(s=>{t.add(s);});}),this.cleanupEvents=P(this.formElement,()=>{this.evaluate();},Array.from(t));}refreshRules(){this.rules=F(this.formElement,this.options);}evaluate(){if(this.isPaused)return;let t=$(this.formElement,this.rules,this.options);this.debugPanel&&this.debugPanel.update(t);}pause(){this.isPaused=true;}resume(){this.isPaused=false,this.evaluate();}exportRules(){return this.options.rules||[]}importRules(t){this.options.rules=t,this.savePersistedRules(),this.refreshRules(),this.setupEvents(),this.evaluate();}async enableRuleEditor(t){let{RuleEditor:r}=await import('./editor.cjs');new r(this,t).mount();}loadPersistedRules(){let{persist:t}=this.options;if(t)try{let s=(t.storage==="localStorage"?localStorage:sessionStorage).getItem(t.key);s&&(this.options.rules=JSON.parse(s));}catch(r){console.error("[FormFx] Failed to load persisted rules:",r);}}savePersistedRules(){let{persist:t,rules:r}=this.options;if(!(!t||!r))try{(t.storage==="localStorage"?localStorage:sessionStorage).setItem(t.key,JSON.stringify(r));}catch(s){console.error("[FormFx] Failed to save rules:",s);}}enableRule(t){this.rules.forEach(r=>{r.id===t&&r.source==="json"&&(r.disabled=false);}),this.options.rules&&(this.options.rules.forEach(r=>{r.id===t&&(r.disabled=false);}),this.savePersistedRules()),this.evaluate();}disableRule(t){this.rules.forEach(r=>{r.id===t&&r.source==="json"&&(r.disabled=true);}),this.options.rules&&(this.options.rules.forEach(r=>{r.id===t&&(r.disabled=true);}),this.savePersistedRules()),this.evaluate();}destroy(){this.cleanupEvents&&this.cleanupEvents(),this.cleanupRepeaters&&this.cleanupRepeaters(),this.observer&&this.observer.disconnect(),this.debugPanel&&this.debugPanel.destroy(),this.cleanupEvents=null,this.cleanupRepeaters=null,this.observer=null,this.debugPanel=null,this.rules=[];}};Object.defineProperty(exports,"RuleEditor",{enumerable:true,get:function(){return chunkGXMPHKTF_cjs.a}});exports.FormFx=M;//# sourceMappingURL=index.cjs.map
|
|
138
|
+
//# sourceMappingURL=index.cjs.map
|