formfx 1.0.0 → 1.0.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.
package/dist/index.d.cts CHANGED
@@ -1 +1,128 @@
1
- export { E as EffectType, b as FXValue, F as FormFx, d as FormFxDebugInfo, a as FormFxOptions, J as JSONEffect, c as JSONRule, P as PersistOptions, RuleEditor, R as RuleEditorOptions } from './editor.cjs';
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
+ * デバッグパネルの表示/非表示を切り替える (v0.4.1)
107
+ */
108
+ setDebug(enabled: boolean): void;
109
+ /**
110
+ * Rule Editor を有効化する (v0.4)
111
+ */
112
+ enableRuleEditor(options: RuleEditorOptions): Promise<void>;
113
+ private loadPersistedRules;
114
+ private savePersistedRules;
115
+ /**
116
+ * JSONルールを有効化する
117
+ * @param ruleId
118
+ */
119
+ enableRule(ruleId: string): void;
120
+ /**
121
+ * JSONルールを無効化する
122
+ * @param ruleId
123
+ */
124
+ disableRule(ruleId: string): void;
125
+ destroy(): void;
126
+ }
127
+
128
+ export { type EffectType, type FXValue, FormFx, type FormFxDebugInfo, type FormFxOptions, type JSONEffect, type JSONRule, type PersistOptions, type RuleEditorOptions };
package/dist/index.d.ts CHANGED
@@ -1 +1,128 @@
1
- export { E as EffectType, b as FXValue, F as FormFx, d as FormFxDebugInfo, a as FormFxOptions, J as JSONEffect, c as JSONRule, P as PersistOptions, RuleEditor, R as RuleEditorOptions } from './editor.js';
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
+ * デバッグパネルの表示/非表示を切り替える (v0.4.1)
107
+ */
108
+ setDebug(enabled: boolean): void;
109
+ /**
110
+ * Rule Editor を有効化する (v0.4)
111
+ */
112
+ enableRuleEditor(options: RuleEditorOptions): Promise<void>;
113
+ private loadPersistedRules;
114
+ private savePersistedRules;
115
+ /**
116
+ * JSONルールを有効化する
117
+ * @param ruleId
118
+ */
119
+ enableRule(ruleId: string): void;
120
+ /**
121
+ * JSONルールを無効化する
122
+ * @param ruleId
123
+ */
124
+ disableRule(ruleId: string): void;
125
+ destroy(): void;
126
+ }
127
+
128
+ export { type EffectType, type FXValue, FormFx, type FormFxDebugInfo, type FormFxOptions, type JSONEffect, type JSONRule, type PersistOptions, type RuleEditorOptions };
@@ -0,0 +1,198 @@
1
+ "use strict";var FormFx=(()=>{var T=Object.defineProperty;var V=Object.getOwnPropertyDescriptor;var X=Object.getOwnPropertyNames;var j=Object.prototype.hasOwnProperty;var _=(t,e)=>()=>(t&&(e=t(t=0)),e);var I=(t,e)=>{for(var r in e)T(t,r,{get:e[r],enumerable:!0})},Z=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of X(e))!j.call(t,i)&&i!==r&&T(t,i,{get:()=>e[i],enumerable:!(s=V(e,i))||s.enumerable});return t};var K=t=>Z(T({},"__esModule",{value:!0}),t);var z={};I(z,{RuleEditor:()=>O});var O,J=_(()=>{"use strict";O=class{constructor(e,r){this.fx=e;this.options=r;this.container=document.createElement("div"),this.container.className="formfx-editor"}container;activeRuleIndex=null;mount(){this.options.mount.appendChild(this.container),this.render()}render(){let e=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(e),this.renderRuleForm(e),this.container.querySelector("#fx-add-rule")?.addEventListener("click",()=>{this.addRule()})}renderRuleList(e){let r=this.container.querySelector("#fx-rule-list");r&&(r.innerHTML=e.map((s,i)=>`
13
+ <div class="formfx-rule-item ${this.activeRuleIndex===i?"active":""}" data-index="${i}">
14
+ <div class="formfx-rule-item-id">${s.id||"(no id)"}</div>
15
+ <div class="formfx-rule-item-expr">if: ${s.if}</div>
16
+ </div>
17
+ `).join("")||'<div class="formfx-empty-state">No rules defined</div>',r.querySelectorAll(".formfx-rule-item").forEach(s=>{s.addEventListener("click",()=>{this.activeRuleIndex=parseInt(s.getAttribute("data-index")||"0"),this.render()})}))}renderRuleForm(e){let r=this.container.querySelector("#fx-rule-form");if(!r||this.activeRuleIndex===null||!e[this.activeRuleIndex]){r&&(r.innerHTML='<div class="formfx-empty-state">Select a rule to edit</div>');return}let s=e[this.activeRuleIndex];r.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="${s.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">${s.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(s,null,2)}</div>
47
+ </div>
48
+ `,this.renderEffectList("then",s.then),this.renderEffectList("else",s.else||[]),r.querySelector("#fx-save-rule")?.addEventListener("click",()=>this.saveRule()),r.querySelector("#fx-delete-rule")?.addEventListener("click",()=>this.deleteRule()),r.querySelector("#fx-add-then")?.addEventListener("click",()=>this.addEffect("then")),r.querySelector("#fx-add-else")?.addEventListener("click",()=>this.addEffect("else"))}renderEffectList(e,r){let s=this.container.querySelector(`#fx-${e}-list`);s&&(s.innerHTML=r.map((i,n)=>{let d=Object.entries(i)[0]||["show",""],u=d[0],a=d[1];return`
49
+ <div class="formfx-effect-row" data-type="${e}" data-index="${n}">
50
+ <select class="formfx-select fx-effect-type" style="width: 120px;">
51
+ <option value="show" ${u==="show"?"selected":""}>show</option>
52
+ <option value="hide" ${u==="hide"?"selected":""}>hide</option>
53
+ <option value="required" ${u==="required"?"selected":""}>require</option>
54
+ <option value="disabled" ${u==="disabled"?"selected":""}>disabled</option>
55
+ <option value="enable" ${u==="enable"?"selected":""}>enable</option>
56
+ <option value="clear" ${u==="clear"?"selected":""}>clear</option>
57
+ </select>
58
+ <input type="text" class="formfx-input fx-effect-selector" placeholder="#id" value="${a}">
59
+ <button class="formfx-btn formfx-btn-danger fx-remove-effect">\xD7</button>
60
+ </div>
61
+ `}).join(""),s.querySelectorAll(".fx-remove-effect").forEach((i,n)=>{i.addEventListener("click",()=>{r.splice(n,1),this.renderRuleForm(this.fx.exportRules())})}))}addRule(){let e=this.fx.exportRules(),r={id:`rule_${Date.now()}`,if:"true",then:[{show:""}]};e.push(r),this.activeRuleIndex=e.length-1,this.fx.importRules(e),this.render()}saveRule(){if(this.activeRuleIndex===null)return;this.fx.pause();let e=this.fx.exportRules(),r=e[this.activeRuleIndex];r.id=this.container.querySelector("#fx-edit-id").value,r.if=this.container.querySelector("#fx-edit-if").value;let s=i=>{let n=this.container.querySelectorAll(`.formfx-effect-row[data-type="${i}"]`),d=[];return n.forEach(u=>{let a=u.querySelector(".fx-effect-type").value,l=u.querySelector(".fx-effect-selector").value,o={};o[a]=l,d.push(o)}),d};r.then=s("then"),r.else=s("else"),r.else.length===0&&delete r.else,this.fx.importRules(e),this.fx.resume(),this.render()}deleteRule(){if(this.activeRuleIndex===null||!confirm("Are you sure you want to delete this rule?"))return;let e=this.fx.exportRules();e.splice(this.activeRuleIndex,1),this.activeRuleIndex=null,this.fx.importRules(e),this.render()}addEffect(e){if(this.activeRuleIndex===null)return;let r=this.fx.exportRules(),s=r[this.activeRuleIndex];e==="then"?s.then.push({show:""}):(s.else=s.else||[],s.else.push({show:""})),this.renderRuleForm(r)}}});var Y={};I(Y,{FormFx:()=>w});function S(t){if(t instanceof HTMLInputElement){if(t.type==="checkbox")return t.checked;if(t.type==="radio"){if(t.name){let r=(t.closest("form")||document).querySelector(`input[name="${t.name}"]:checked`);return r?r.value:null}return t.checked?t.value:null}return t.type==="number"||t.type==="range"?t.value===""?null:Number(t.value):t.value}return t instanceof HTMLSelectElement||t instanceof HTMLTextAreaElement?t.value:null}function g(t){t instanceof HTMLInputElement?t.type==="checkbox"||t.type==="radio"?t.checked=!1:t.value="":t instanceof HTMLSelectElement?(t.selectedIndex=-1,t.value=""):t instanceof HTMLTextAreaElement?t.value="":t.querySelectorAll("input, select, textarea").forEach(r=>g(r))}function E(t,e){"disabled"in t?t.disabled=e:t.querySelectorAll("input, select, textarea").forEach(s=>{s.disabled=e})}function $(t,e,r){let s=t.style.display!=="none";e?(t.style.display=t.dataset.fxOriginalDisplay||"",r.disableOnHide&&!s&&E(t,!1)):(s&&!t.dataset.fxOriginalDisplay&&(t.dataset.fxOriginalDisplay=t.style.display),t.style.display="none",s&&((t.dataset.fxClearOnHide==="true"||r.clearOnHide)&&g(t),r.disableOnHide&&E(t,!0)))}function H(t,e){t instanceof HTMLInputElement||t instanceof HTMLSelectElement||t instanceof HTMLTextAreaElement?t.required=e:t.querySelectorAll("input, select, textarea").forEach(s=>{s.required=e})}function L(t,e){E(t,e)}function N(t){g(t)}function P(t,e){let r=t.querySelector(`#${e.replace(/([#;?%&,.+*~\':"!^$[\]()=>|/@])/g,"\\$1")}`);if(r)return r;let s=t.querySelector(`[name="${e}"]`);return s||t.querySelector(`[data-fx-id="${e}"]`)}function h(t,e){switch(t.type){case"Literal":return t.value;case"Identifier":{if(t.name.startsWith("@row.")){let r=t.name.substring(5);return e.row&&e.row[r]!==void 0?e.row[r]:null}return e.global[t.name]!==void 0?e.global[t.name]:null}case"UnaryExpression":if(t.operator==="!")return!h(t.argument,e);throw new Error(`Unknown unary operator: ${t.operator}`);case"BinaryExpression":{let r=h(t.left,e),s=h(t.right,e);if(t.operator==="==")return r==s;if(t.operator==="!=")return r!=s;if(t.operator===">")return r>s;if(t.operator===">=")return r>=s;if(t.operator==="<")return r<s;if(t.operator==="<=")return r<=s;if(t.operator==="+")return r+s;if(t.operator==="-")return r-s;if(t.operator==="*")return r*s;if(t.operator==="/")return r/s;if(t.operator==="%")return r%s;throw new Error(`Unknown binary operator: ${t.operator}`)}case"LogicalExpression":{let r=h(t.left,e);if(t.operator==="&&")return r&&h(t.right,e);if(t.operator==="||")return r||h(t.right,e);throw new Error(`Unknown logical operator: ${t.operator}`)}case"CallExpression":{let r=e.functions[t.callee];if(!r)throw new Error(`Unknown function: ${t.callee}`);let s=t.arguments.map(i=>h(i,e));return r(...s)}case"ArrayExpression":return t.elements.map(r=>h(r,e));default:throw new Error(`Unknown AST node type: ${t.type}`)}}function A(t){let e=0;function r(){return s()}function s(){let o=i();for(;e<t.length&&t[e].value==="||";){let c=t[e].value;e++;let p=i();o={type:"LogicalExpression",operator:c,left:o,right:p}}return o}function i(){let o=n();for(;e<t.length&&t[e].value==="&&";){let c=t[e].value;e++;let p=n();o={type:"LogicalExpression",operator:c,left:o,right:p}}return o}function n(){let o=d(),c=["==","!=",">",">=","<","<="];for(;e<t.length&&c.includes(t[e].value);){let p=t[e].value;e++;let f=d();o={type:"BinaryExpression",operator:p,left:o,right:f}}return o}function d(){let o=u();for(;e<t.length&&(t[e].value==="+"||t[e].value==="-");){let c=t[e].value;e++;let p=u();o={type:"BinaryExpression",operator:c,left:o,right:p}}return o}function u(){let o=a();for(;e<t.length&&(t[e].value==="*"||t[e].value==="/"||t[e].value==="%");){let c=t[e].value;e++;let p=a();o={type:"BinaryExpression",operator:c,left:o,right:p}}return o}function a(){return e<t.length&&t[e].value==="!"?(e++,{type:"UnaryExpression",operator:"!",argument:a()}):l()}function l(){let o=t[e];if(o.type==="PAREN"&&o.value==="("){e++;let c=r();if(t[e]?.value!==")")throw new Error("Expected )");return e++,c}if(o.type==="IDENTIFIER"){if(e++,e<t.length&&t[e].type==="PAREN"&&t[e].value==="("){e++;let c=[];if(!(t[e].type==="PAREN"&&t[e].value===")"))for(;;){if(c.push(r()),t[e].type==="COMMA"){e++;continue}if(t[e].type==="PAREN"&&t[e].value===")")break;throw new Error(`Expected , or ) in function call "${o.value}"`)}return e++,{type:"CallExpression",callee:o.value,arguments:c}}return{type:"Identifier",name:o.value}}if(o.type==="BRACKET"&&o.value==="["){e++;let c=[];if(!(t[e].type==="BRACKET"&&t[e].value==="]"))for(;;){if(c.push(r()),t[e].type==="COMMA"){e++;continue}if(t[e].type==="BRACKET"&&t[e].value==="]")break;throw new Error("Expected , or ] in array literal")}return e++,{type:"ArrayExpression",elements:c}}if(o.type==="NUMBER")return e++,{type:"Literal",value:parseFloat(o.value)};if(o.type==="STRING")return e++,{type:"Literal",value:o.value};if(o.type==="BOOLEAN")return e++,{type:"Literal",value:o.value==="true"};throw new Error(`Unexpected token: ${o.value}`)}return r()}function M(t){let e=[],r=0;for(;r<t.length;){let s=t[r];if(/\s/.test(s)){r++;continue}if(s==="("||s===")"){e.push({type:"PAREN",value:s}),r++;continue}if(s==="["||s==="]"){e.push({type:"BRACKET",value:s}),r++;continue}if(s===","){e.push({type:"COMMA",value:s}),r++;continue}if(s==="#"||s==="@"){let n=s==="@",d=n?"@":"";if(r++,n){let u="";for(;r<t.length&&/[a-zA-Z0-9_-]/.test(t[r]);)u+=t[r],r++;if(d+=u,t[r]===".")for(d+=".",r++;r<t.length&&/[a-zA-Z0-9_-]/.test(t[r]);)d+=t[r],r++}else for(;r<t.length&&/[a-zA-Z0-9_-]/.test(t[r]);)d+=t[r],r++;e.push({type:"IDENTIFIER",value:d});continue}if(/[0-9]/.test(s)){let n="";for(;r<t.length&&/[0-9.]/.test(t[r]);)n+=t[r],r++;e.push({type:"NUMBER",value:n});continue}if(s==="'"){let n="";for(r++;r<t.length&&t[r]!=="'";)n+=t[r],r++;r++,e.push({type:"STRING",value:n});continue}if(s==="="&&t[r+1]==="="){e.push({type:"OPERATOR",value:"=="}),r+=2;continue}if(s==="!"&&t[r+1]==="="){e.push({type:"OPERATOR",value:"!="}),r+=2;continue}if(s===">"&&t[r+1]==="="){e.push({type:"OPERATOR",value:">="}),r+=2;continue}if(s==="<"&&t[r+1]==="="){e.push({type:"OPERATOR",value:"<="}),r+=2;continue}if(s===">"){e.push({type:"OPERATOR",value:">"}),r++;continue}if(s==="<"){e.push({type:"OPERATOR",value:"<"}),r++;continue}if(s==="&"&&t[r+1]==="&"){e.push({type:"OPERATOR",value:"&&"}),r+=2;continue}if(s==="|"&&t[r+1]==="|"){e.push({type:"OPERATOR",value:"||"}),r+=2;continue}if(s==="!"){e.push({type:"OPERATOR",value:"!"}),r++;continue}if(s==="+"||s==="-"||s==="*"||s==="/"||s==="%"){e.push({type:"OPERATOR",value:s}),r++;continue}let i=t.slice(r).match(/^[a-zA-Z][a-zA-Z0-9_]*/);if(i){let n=i[0];n==="true"||n==="false"?(e.push({type:"BOOLEAN",value:n}),r+=n.length):(e.push({type:"IDENTIFIER",value:n}),r+=n.length);continue}throw new Error(`Unexpected character: ${s} at position ${r}`)}return e}function q(t,e={}){let r=[];return["show","required","disabled","enable","clear"].forEach(i=>{let n=`data-fx-${i}`;t.querySelectorAll(`[${n}]`).forEach(u=>{let a=u.getAttribute(n)||"";try{let l=M(a),o=A(l);r.push({element:u,type:i,expression:a,ast:o,source:"attribute"})}catch(l){console.warn(`[FormFx] Failed to parse expression "${a}" for ${n}:`,l)}})}),e.rules&&e.rules.forEach(i=>{try{let n=M(i.if),d=A(n),u=(a,l)=>{a.forEach(o=>{Object.entries(o).forEach(([c,p])=>{if(typeof p!="string"||!p)return;let f=c==="require"?"required":c,v=i.if,m=d;f==="hide"&&(f="show",m={type:"UnaryExpression",operator:"!",argument:d},v=`!(${i.if})`),l&&(m={type:"UnaryExpression",operator:"!",argument:m},v=`!(${v})`),t.querySelectorAll(p).forEach(y=>{let U=y;r.push({id:i.id,element:U,type:f,expression:v,ast:m,source:"json",rawRule:i,disabled:i.disabled})})})})};u(i.then,!1),i.else&&u(i.else,!0)}catch(n){console.warn("[FormFx] Failed to parse JSON rule:",i,n)}}),r}function b(t){let e=new Set,r=new Set;function s(i){i.type==="Identifier"?i.name.startsWith("@row.")?r.add(i.name.substring(5)):e.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(t),{global:Array.from(e),row:Array.from(r)}}var G={in:(t,e)=>Array.isArray(e)?e.includes(t):!1,contains:(t,e)=>typeof t!="string"?!1:t.includes(e)};function C(t,e,r){let s=[],i={},n=new Set;e.forEach(a=>{b(a.ast).global.forEach(l=>n.add(l))}),n.forEach(a=>{let l=P(t,a);l?i[a]=S(l):i[a]=null});let d={global:i,functions:G},u=new Map;return e.forEach(a=>{u.has(a.element)||u.set(a.element,new Map),u.get(a.element).set(a.type,a.source)}),e.forEach(a=>{if(!a.disabled&&!(a.source==="attribute"&&u.get(a.element)?.get(a.type)==="json"))try{let l,o=a.element.closest("[data-fx-item]");o&&(l={},b(a.ast).row.forEach(m=>{let R=o.querySelector(`[data-fx-field="${m}"]`);if(R){let y=R.querySelector("input, select, textarea");l[m]=y?S(y):null}else l[m]=null}));let c={...d,row:l},f=!!h(a.ast,c);switch(r.debug&&(s.push({rule:a,result:f,elements:[a.element]}),console.debug(`[FormFx Debug] Rule: ${a.type}="${a.expression}" (source: ${a.source}, result: ${f})`,{element:a.element,result:f,context:c})),a.type){case"show":$(a.element,f,r);break;case"required":H(a.element,f);break;case"disabled":L(a.element,f);break;case"enable":L(a.element,!f);break;case"clear":f&&a.lastResult===!1&&N(a.element);break}a.lastResult=f}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 D(t,e,r){let s=()=>{e()},i=[];return t.addEventListener("input",s),t.addEventListener("change",s),()=>{t.removeEventListener("input",s),t.removeEventListener("change",s)}}function k(t,e){let r=new MutationObserver(s=>{let i=!1;for(let n of s)if(n.type==="childList"&&(n.addedNodes.length>0||n.removedNodes.length>0)){i=!0;break}i&&e()});return r.observe(t,{childList:!0,subtree:!0}),r}function B(t,e){let r=t.querySelectorAll("[data-fx-repeater]"),s=[];return r.forEach(i=>{let n={container:i,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 d=()=>{n.listContainer.querySelectorAll("[data-fx-item]").length<n.max&&(W(n),F(n),e())};n.addButton&&(n.addButton.addEventListener("click",d),s.push(()=>n.addButton?.removeEventListener("click",d)));let u=a=>{let l=a.target;if(l.closest("[data-fx-remove]")){let o=l.closest("[data-fx-item]");o&&(o.remove(),F(n),e())}};n.listContainer.addEventListener("click",u),s.push(()=>n.listContainer.removeEventListener("click",u)),F(n)}),()=>s.forEach(i=>i())}function W(t){let r=t.template.content.cloneNode(!0).querySelector("[data-fx-item]");r&&t.listContainer.appendChild(r)}function F(t){let e=t.listContainer.querySelectorAll("[data-fx-item]");e.forEach((r,s)=>{r.setAttribute("data-fx-index",s.toString()),r.querySelectorAll("[data-fx-field]").forEach(n=>{let d=n.getAttribute("data-fx-field");d&&n.querySelectorAll("input, select, textarea").forEach(a=>{a.name=`${t.name}[${s}].${d}`})})}),t.addButton&&(e.length>=t.max?t.addButton.style.display="none":t.addButton.style.display="")}var x=class{container=null;shadow=null;persistInfo=null;isMinimized=!1;pos={x:10,y:10};isDragging=!1;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 e=localStorage.getItem("formfx-debug-state");if(e){let r=JSON.parse(e);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 e=this.shadow.querySelector(".header");if(!e)return;e.style.cursor="move",e.addEventListener("mousedown",s=>{this.isDragging=!0,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=!1,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(e){this.persistInfo=e}renderInitial(){this.shadow&&(this.shadow.innerHTML=`
62
+ <style>
63
+ :host {
64
+ position: fixed;
65
+ bottom: ${this.pos.y}px;
66
+ right: ${this.pos.x}px;
67
+ width: 350px;
68
+ max-height: 80vh;
69
+ background: rgba(0, 0, 0, 0.85);
70
+ color: white;
71
+ font-family: monospace;
72
+ font-size: 12px;
73
+ border-radius: 8px;
74
+ overflow: hidden;
75
+ z-index: 10000;
76
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
77
+ border: 1px solid #444;
78
+ display: flex;
79
+ flex-direction: column;
80
+ }
81
+ .header {
82
+ padding: 8px 12px;
83
+ background: #222;
84
+ border-bottom: 1px solid #444;
85
+ font-weight: bold;
86
+ display: flex;
87
+ justify-content: space-between;
88
+ align-items: center;
89
+ user-select: none;
90
+ }
91
+ .content {
92
+ padding: 8px;
93
+ overflow-y: auto;
94
+ display: ${this.isMinimized?"none":"block"};
95
+ }
96
+ .header-controls {
97
+ display: flex;
98
+ gap: 8px;
99
+ }
100
+ .btn-icon {
101
+ cursor: pointer;
102
+ padding: 2px 6px;
103
+ border-radius: 4px;
104
+ background: #444;
105
+ border: none;
106
+ color: white;
107
+ font-size: 10px;
108
+ }
109
+ .btn-icon:hover {
110
+ background: #666;
111
+ }
112
+ .rule {
113
+ margin-bottom: 8px;
114
+ padding: 6px;
115
+ border-radius: 4px;
116
+ background: #333;
117
+ }
118
+ .rule-header {
119
+ display: flex;
120
+ justify-content: space-between;
121
+ margin-bottom: 4px;
122
+ }
123
+ .rule-id {
124
+ color: #aaa;
125
+ font-size: 10px;
126
+ }
127
+ .rule-expr {
128
+ word-break: break-all;
129
+ color: #88ccff;
130
+ }
131
+ .status {
132
+ font-weight: bold;
133
+ padding: 2px 4px;
134
+ border-radius: 2px;
135
+ font-size: 10px;
136
+ }
137
+ .status-true { background: #228b22; }
138
+ .status-false { background: #b22222; }
139
+ .status-error { background: #ff4500; }
140
+ .status-disabled { background: #666; color: #ccc; }
141
+ .persist-badge {
142
+ font-size: 9px;
143
+ background: #444;
144
+ padding: 1px 4px;
145
+ border-radius: 4px;
146
+ color: #00ff00;
147
+ border: 1px solid #00ff00;
148
+ }
149
+ .header-top {
150
+ display: flex;
151
+ align-items: center;
152
+ gap: 8px;
153
+ }
154
+ .effects {
155
+ margin-top: 4px;
156
+ padding-left: 12px;
157
+ border-left: 2px solid #555;
158
+ font-size: 11px;
159
+ }
160
+ .effect-item {
161
+ margin-top: 2px;
162
+ }
163
+ .effect-skipped {
164
+ color: #888;
165
+ text-decoration: line-through;
166
+ }
167
+ .error-msg {
168
+ color: #ff6347;
169
+ font-size: 10px;
170
+ margin-top: 4px;
171
+ }
172
+ </style>
173
+ <div class="header">
174
+ <div class="header-top">
175
+ <span>FormFx Debug</span>
176
+ ${this.persistInfo?'<span class="persist-badge">PERSIST</span>':""}
177
+ </div>
178
+ <div class="header-controls">
179
+ <button id="toggle-minimize" class="btn-icon">${this.isMinimized?"\u25A1":"_"} </button>
180
+ </div>
181
+ </div>
182
+ <div id="rule-list" class="content">
183
+ Waiting for evaluation...
184
+ </div>
185
+ `,this.setupDragging())}update(e){if(!this.shadow)return;let r=this.shadow.getElementById("rule-list");r&&(r.innerHTML=e.map(s=>{let{rule:i,result:n,error:d}=s,u=n==="error"?"status-error":n?"status-true":"status-false",a=n==="error"?"ERROR":n.toString().toUpperCase();i.disabled&&(u="status-disabled",a="DISABLED");let l=`${i.type} ${this.getSelector(i.element)}`;return n!==!0&&n!=="error"&&!i.disabled&&(l=`<span class="effect-skipped">${l} (skipped)</span>`),i.disabled&&(l=`<span class="effect-skipped">${l}</span>`),`
186
+ <div class="rule">
187
+ <div class="rule-header">
188
+ <span class="rule-id">${i.id||(i.source==="attribute"?"Attr":"JSON")}</span>
189
+ <span class="status ${u}">${a}</span>
190
+ </div>
191
+ <div class="rule-expr">${i.expression}</div>
192
+ <div class="effects">
193
+ <div class="effect-item">${l}</div>
194
+ </div>
195
+ ${d?`<div class="error-msg">Error: ${d}</div>`:""}
196
+ </div>
197
+ `}).join(""))}getSelector(e){return e.id?`#${e.id}`:e.className?`.${e.className.split(" ").join(".")}`:e.tagName.toLowerCase()}destroy(){this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container)}show(){this.container&&(this.container.style.display="block")}hide(){this.container&&(this.container.style.display="none")}};var w=class{constructor(e,r={}){this.formElement=e;this.options={disableOnHide:!0,clearOnHide:!1,...r},this.loadPersistedRules()}rules=[];options;cleanupEvents=null;cleanupRepeaters=null;observer=null;debugPanel=null;isPaused=!1;mount(){this.options.debug&&(this.debugPanel=new x,this.options.persist&&this.debugPanel.setPersistInfo(this.options.persist)),this.refreshRules(),this.cleanupRepeaters=B(this.formElement,()=>{this.refreshRules(),this.setupEvents(),this.evaluate()}),this.setupEvents(),this.observer=k(this.formElement,()=>{this.refreshRules(),this.setupEvents(),this.evaluate()}),this.evaluate()}reEvaluate(){this.evaluate()}setupEvents(){this.cleanupEvents&&this.cleanupEvents();let e=new Set;this.rules.forEach(r=>{b(r.ast).global.forEach(s=>{e.add(s)})}),this.cleanupEvents=D(this.formElement,()=>{this.evaluate()},Array.from(e))}refreshRules(){this.rules=q(this.formElement,this.options)}evaluate(){if(this.isPaused)return;let e=C(this.formElement,this.rules,this.options);this.debugPanel&&this.debugPanel.update(e)}pause(){this.isPaused=!0}resume(){this.isPaused=!1,this.evaluate()}exportRules(){return this.options.rules||[]}importRules(e){this.options.rules=e,this.savePersistedRules(),this.refreshRules(),this.setupEvents(),this.evaluate()}setDebug(e){this.options.debug=e,e?this.debugPanel?this.debugPanel.show():(this.debugPanel=new x,this.options.persist&&this.debugPanel.setPersistInfo(this.options.persist),this.evaluate()):this.debugPanel&&this.debugPanel.hide()}async enableRuleEditor(e){let{RuleEditor:r}=await Promise.resolve().then(()=>(J(),z));new r(this,e).mount()}loadPersistedRules(){let{persist:e}=this.options;if(e)try{let s=(e.storage==="localStorage"?localStorage:sessionStorage).getItem(e.key);s&&(this.options.rules=JSON.parse(s))}catch(r){console.error("[FormFx] Failed to load persisted rules:",r)}}savePersistedRules(){let{persist:e,rules:r}=this.options;if(!(!e||!r))try{(e.storage==="localStorage"?localStorage:sessionStorage).setItem(e.key,JSON.stringify(r))}catch(s){console.error("[FormFx] Failed to save rules:",s)}}enableRule(e){this.rules.forEach(r=>{r.id===e&&r.source==="json"&&(r.disabled=!1)}),this.options.rules&&(this.options.rules.forEach(r=>{r.id===e&&(r.disabled=!1)}),this.savePersistedRules()),this.evaluate()}disableRule(e){this.rules.forEach(r=>{r.id===e&&r.source==="json"&&(r.disabled=!0)}),this.options.rules&&(this.options.rules.forEach(r=>{r.id===e&&(r.disabled=!0)}),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=[]}};typeof window<"u"&&(window.FormFx&&window.FormFx.FormFx?window.FormFx=window.FormFx.FormFx:setTimeout(()=>{window.FormFx&&window.FormFx.FormFx&&(window.FormFx=window.FormFx.FormFx)},0));return K(Y);})();
198
+ //# sourceMappingURL=index.global.js.map