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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/editor/RuleEditor.ts","../src/index.ts","../src/internal/dom/value.ts","../src/internal/dom/effects.ts","../src/internal/dom/select.ts","../src/internal/expr/evaluator.ts","../src/internal/expr/parser.ts","../src/internal/expr/tokenizer.ts","../src/internal/engine.ts","../src/internal/observe.ts","../src/plugins/repeater/index.ts","../src/internal/debug/panel.ts","../src/core/FormFx.ts"],"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","import { FormFx } from './core/FormFx';\r\n\r\n/**\r\n * Browser Global Support\r\n * <script>タグで読み込まれた際、自動的に window.FormFx に登録します\r\n */\r\nif (typeof window !== 'undefined') {\r\n // tsupのiife(globalName)を使っている場合、\r\n // エクスポート全体が window.FormFx に代入される。\r\n // その中の FormFx クラスを window.FormFx に直接上書きすることで\r\n // ユーザーが new FormFx() として直感的に使えるようにする。\r\n if ((window as any).FormFx && (window as any).FormFx.FormFx) {\r\n // すでに代入されている場合は即座に。\r\n (window as any).FormFx = (window as any).FormFx.FormFx;\r\n } else {\r\n // まだの場合はタイミングを待つ(tsupのIIFEの戻り値が代入されるタイミングに合わせる)\r\n setTimeout(() => {\r\n if ((window as any).FormFx && (window as any).FormFx.FormFx) {\r\n (window as any).FormFx = (window as any).FormFx.FormFx;\r\n }\r\n }, 0);\r\n }\r\n}\r\n\r\nexport { FormFx };\r\nexport * from './public-types';","import { FXValue } from '../../types';\r\n\r\nexport function getInputValue(el: HTMLElement): FXValue {\r\n if (el instanceof HTMLInputElement) {\r\n if (el.type === 'checkbox') {\r\n return el.checked;\r\n }\r\n if (el.type === 'radio') {\r\n // ラジオボタンの場合は、同じnameを持つチェックされている要素を探す\r\n if (el.name) {\r\n const form = el.closest('form') || document;\r\n const checked = form.querySelector(`input[name=\"${el.name}\"]:checked`) as HTMLInputElement;\r\n return checked ? checked.value : null;\r\n }\r\n return el.checked ? el.value : null;\r\n }\r\n if (el.type === 'number' || el.type === 'range') {\r\n return el.value === '' ? null : Number(el.value);\r\n }\r\n return el.value;\r\n }\r\n \r\n if (el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement) {\r\n return el.value;\r\n }\r\n\r\n return null;\r\n}\r\n\r\nexport function clearValue(el: HTMLElement): void {\r\n if (el instanceof HTMLInputElement) {\r\n if (el.type === 'checkbox' || el.type === 'radio') {\r\n el.checked = false;\r\n } else {\r\n el.value = '';\r\n }\r\n } else if (el instanceof HTMLSelectElement) {\r\n el.selectedIndex = -1;\r\n el.value = '';\r\n } else if (el instanceof HTMLTextAreaElement) {\r\n el.value = '';\r\n } else {\r\n // コンテナの場合は配下の要素をクリア\r\n const inputs = el.querySelectorAll('input, select, textarea');\r\n inputs.forEach(input => clearValue(input as HTMLElement));\r\n }\r\n}\r\n\r\nexport function setDisabled(el: HTMLElement, disabled: boolean): void {\r\n if ('disabled' in el) {\r\n (el as any).disabled = disabled;\r\n } else {\r\n // コンテナの場合は配下の要素を制御\r\n const inputs = el.querySelectorAll('input, select, textarea');\r\n inputs.forEach(input => {\r\n (input as HTMLInputElement).disabled = disabled;\r\n });\r\n }\r\n}\r\n","import { FormFxOptions } from '../../types';\r\nimport { clearValue, setDisabled } from './value';\r\n\r\nexport function applyShow(el: HTMLElement, show: boolean, options: FormFxOptions): void {\r\n const isCurrentlyVisible = el.style.display !== 'none';\r\n \r\n if (show) {\r\n el.style.display = el.dataset.fxOriginalDisplay || '';\r\n // 再表示されたときに disabled を解除するかどうかは要件にないが、\r\n // disableOnHide=true の場合は対称性を持たせるのが一般的\r\n if (options.disableOnHide && !isCurrentlyVisible) {\r\n setDisabled(el, false);\r\n }\r\n } else {\r\n if (isCurrentlyVisible && !el.dataset.fxOriginalDisplay) {\r\n el.dataset.fxOriginalDisplay = el.style.display;\r\n }\r\n el.style.display = 'none';\r\n \r\n // 非表示になったときの追加処理\r\n if (isCurrentlyVisible) {\r\n if (el.dataset.fxClearOnHide === 'true' || options.clearOnHide) {\r\n clearValue(el);\r\n }\r\n if (options.disableOnHide) {\r\n setDisabled(el, true);\r\n }\r\n }\r\n }\r\n}\r\n\r\nexport function applyRequired(el: HTMLElement, required: boolean): void {\r\n if (el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement) {\r\n el.required = required;\r\n } else {\r\n // コンテナの場合、配下の全ての入力要素に適用\r\n const inputs = el.querySelectorAll('input, select, textarea');\r\n inputs.forEach(input => {\r\n (input as HTMLInputElement).required = required;\r\n });\r\n }\r\n}\r\n\r\nexport function applyDisabled(el: HTMLElement, disabled: boolean): void {\r\n setDisabled(el, disabled);\r\n}\r\n\r\nexport function applyClear(el: HTMLElement): void {\r\n clearValue(el);\r\n}\r\n","export function findById(root: HTMLElement, id: string): HTMLElement | null {\r\n // 1. Try to find by ID\r\n const el = root.querySelector(`#${id.replace(/([#;?%&,.+*~\\':\"!^$[\\]()=>|/@])/g, '\\\\$1')}`);\r\n if (el) return el as HTMLElement;\r\n\r\n // 2. Try to find by name attribute (v1.0 enhancement)\r\n const byName = root.querySelector(`[name=\"${id}\"]`);\r\n if (byName) return byName as HTMLElement;\r\n \r\n // 3. Try to find by data-fx-id\r\n return root.querySelector(`[data-fx-id=\"${id}\"]`) as HTMLElement | null;\r\n}\r\n\r\nexport function findByFxAttr(root: HTMLElement, attr: string): HTMLElement[] {\r\n return Array.from(root.querySelectorAll(`[${attr}]`));\r\n}\r\n","import { ASTNode, FXValue, EvaluationContext } from '../../types';\r\n\r\nexport function evaluate(node: ASTNode, context: EvaluationContext): FXValue {\r\n switch (node.type) {\r\n case 'Literal':\r\n return node.value;\r\n\r\n case 'Identifier': {\r\n if (node.name.startsWith('@row.')) {\r\n const fieldName = node.name.substring(5);\r\n return context.row && context.row[fieldName] !== undefined ? context.row[fieldName] : null;\r\n }\r\n return context.global[node.name] !== undefined ? context.global[node.name] : null;\r\n }\r\n\r\n case 'UnaryExpression':\r\n if (node.operator === '!') {\r\n return !evaluate(node.argument, context);\r\n }\r\n throw new Error(`Unknown unary operator: ${(node as any).operator}`);\r\n\r\n case 'BinaryExpression': {\r\n const left = evaluate(node.left, context);\r\n const right = evaluate(node.right, context);\r\n if (node.operator === '==') return left == right;\r\n if (node.operator === '!=') return left != right;\r\n if (node.operator === '>') return (left as any) > (right as any);\r\n if (node.operator === '>=') return (left as any) >= (right as any);\r\n if (node.operator === '<') return (left as any) < (right as any);\r\n if (node.operator === '<=') return (left as any) <= (right as any);\r\n if (node.operator === '+') return (left as any) + (right as any);\r\n if (node.operator === '-') return (left as any) - (right as any);\r\n if (node.operator === '*') return (left as any) * (right as any);\r\n if (node.operator === '/') return (left as any) / (right as any);\r\n if (node.operator === '%') return (left as any) % (right as any);\r\n throw new Error(`Unknown binary operator: ${node.operator}`);\r\n }\r\n\r\n case 'LogicalExpression': {\r\n const left = evaluate(node.left, context);\r\n if (node.operator === '&&') {\r\n return left && evaluate(node.right, context);\r\n }\r\n if (node.operator === '||') {\r\n return left || evaluate(node.right, context);\r\n }\r\n throw new Error(`Unknown logical operator: ${node.operator}`);\r\n }\r\n\r\n case 'CallExpression': {\r\n const func = context.functions[node.callee];\r\n if (!func) {\r\n throw new Error(`Unknown function: ${node.callee}`);\r\n }\r\n const args = node.arguments.map((arg: ASTNode) => evaluate(arg, context));\r\n return func(...args);\r\n }\r\n\r\n case 'ArrayExpression': {\r\n return node.elements.map((el: ASTNode) => evaluate(el, context));\r\n }\r\n\r\n default:\r\n throw new Error(`Unknown AST node type: ${(node as any).type}`);\r\n }\r\n}\r\n","import { ASTNode, Token } from '../../types';\r\n\r\n/**\r\n * 簡易的な再帰下降構文解析器\r\n * 優先順位:\r\n * 1. Primary (Identifier, Literal, Parentheses)\r\n * 2. Unary (!)\r\n * 3. Comparison (==, !=)\r\n * 4. Logical AND (&&)\r\n * 5. Logical OR (||)\r\n */\r\nexport function parse(tokens: Token[]): ASTNode {\r\n let current = 0;\r\n\r\n function walk(): ASTNode {\r\n return parseLogicalOr();\r\n }\r\n\r\n function parseLogicalOr(): ASTNode {\r\n let node = parseLogicalAnd();\r\n while (current < tokens.length && tokens[current].value === '||') {\r\n const operator = tokens[current].value as '||';\r\n current++;\r\n const right = parseLogicalAnd();\r\n node = { type: 'LogicalExpression', operator, left: node, right };\r\n }\r\n return node;\r\n }\r\n\r\n function parseLogicalAnd(): ASTNode {\r\n let node = parseComparison();\r\n while (current < tokens.length && tokens[current].value === '&&') {\r\n const operator = tokens[current].value as '&&';\r\n current++;\r\n const right = parseComparison();\r\n node = { type: 'LogicalExpression', operator, left: node, right };\r\n }\r\n return node;\r\n }\r\n\r\n function parseComparison(): ASTNode {\r\n let node = parseAdditive();\r\n const comparisonOps = ['==', '!=', '>', '>=', '<', '<='];\r\n while (current < tokens.length && comparisonOps.includes(tokens[current].value)) {\r\n const operator = tokens[current].value;\r\n current++;\r\n const right = parseAdditive();\r\n node = { type: 'BinaryExpression', operator, left: node, right };\r\n }\r\n return node;\r\n }\r\n\r\n function parseAdditive(): ASTNode {\r\n let node = parseMultiplicative();\r\n while (current < tokens.length && (tokens[current].value === '+' || tokens[current].value === '-')) {\r\n const operator = tokens[current].value;\r\n current++;\r\n const right = parseMultiplicative();\r\n node = { type: 'BinaryExpression', operator, left: node, right };\r\n }\r\n return node;\r\n }\r\n\r\n function parseMultiplicative(): ASTNode {\r\n let node = parseUnary();\r\n while (current < tokens.length && (tokens[current].value === '*' || tokens[current].value === '/' || tokens[current].value === '%')) {\r\n const operator = tokens[current].value;\r\n current++;\r\n const right = parseUnary();\r\n node = { type: 'BinaryExpression', operator, left: node, right };\r\n }\r\n return node;\r\n }\r\n\r\n function parseUnary(): ASTNode {\r\n if (current < tokens.length && tokens[current].value === '!') {\r\n current++;\r\n return { type: 'UnaryExpression', operator: '!', argument: parseUnary() };\r\n }\r\n return parsePrimary();\r\n }\r\n\r\n function parsePrimary(): ASTNode {\r\n const token = tokens[current];\r\n\r\n if (token.type === 'PAREN' && token.value === '(') {\r\n current++;\r\n const node = walk();\r\n if (tokens[current]?.value !== ')') {\r\n throw new Error('Expected )');\r\n }\r\n current++;\r\n return node;\r\n }\r\n\r\n if (token.type === 'IDENTIFIER') {\r\n current++;\r\n // 関数呼び出しのチェック: id(arg1, arg2)\r\n if (current < tokens.length && tokens[current].type === 'PAREN' && tokens[current].value === '(') {\r\n current++; // skip (\r\n const args: ASTNode[] = [];\r\n if (!(tokens[current].type === 'PAREN' && tokens[current].value === ')')) {\r\n while (true) {\r\n args.push(walk());\r\n if (tokens[current].type === 'COMMA') {\r\n current++;\r\n continue;\r\n }\r\n if (tokens[current].type === 'PAREN' && tokens[current].value === ')') {\r\n break;\r\n }\r\n throw new Error(`Expected , or ) in function call \"${token.value}\"`);\r\n }\r\n }\r\n current++; // skip )\r\n return { type: 'CallExpression', callee: token.value, arguments: args };\r\n }\r\n return { type: 'Identifier', name: token.value };\r\n }\r\n\r\n if (token.type === 'BRACKET' && token.value === '[') {\r\n current++; // skip [\r\n const elements: ASTNode[] = [];\r\n if (!(tokens[current].type === 'BRACKET' && tokens[current].value === ']')) {\r\n while (true) {\r\n elements.push(walk());\r\n if (tokens[current].type === 'COMMA') {\r\n current++;\r\n continue;\r\n }\r\n if (tokens[current].type === 'BRACKET' && tokens[current].value === ']') {\r\n break;\r\n }\r\n throw new Error('Expected , or ] in array literal');\r\n }\r\n }\r\n current++; // skip ]\r\n return { type: 'ArrayExpression', elements };\r\n }\r\n\r\n if (token.type === 'NUMBER') {\r\n current++;\r\n return { type: 'Literal', value: parseFloat(token.value) };\r\n }\r\n\r\n if (token.type === 'STRING') {\r\n current++;\r\n return { type: 'Literal', value: token.value };\r\n }\r\n\r\n if (token.type === 'BOOLEAN') {\r\n current++;\r\n return { type: 'Literal', value: token.value === 'true' };\r\n }\r\n\r\n throw new Error(`Unexpected token: ${token.value}`);\r\n }\r\n\r\n return walk();\r\n}\r\n","import { Token } from '../../types';\r\n\r\nexport function tokenize(input: string): Token[] {\r\n const tokens: Token[] = [];\r\n let current = 0;\r\n\r\n while (current < input.length) {\r\n let char = input[current];\r\n\r\n if (/\\s/.test(char)) {\r\n current++;\r\n continue;\r\n }\r\n\r\n if (char === '(' || char === ')') {\r\n tokens.push({ type: 'PAREN', value: char });\r\n current++;\r\n continue;\r\n }\r\n\r\n if (char === '[' || char === ']') {\r\n tokens.push({ type: 'BRACKET', value: char });\r\n current++;\r\n continue;\r\n }\r\n\r\n if (char === ',') {\r\n tokens.push({ type: 'COMMA', value: char });\r\n current++;\r\n continue;\r\n }\r\n\r\n if (char === '#' || char === '@') {\r\n const isRow = char === '@';\r\n let value = isRow ? '@' : '';\r\n current++; // skip # or @\r\n \r\n // @row.fieldName 対応\r\n if (isRow) {\r\n let rowPrefix = '';\r\n while (current < input.length && /[a-zA-Z0-9_-]/.test(input[current])) {\r\n rowPrefix += input[current];\r\n current++;\r\n }\r\n value += rowPrefix;\r\n if (input[current] === '.') {\r\n value += '.';\r\n current++;\r\n while (current < input.length && /[a-zA-Z0-9_-]/.test(input[current])) {\r\n value += input[current];\r\n current++;\r\n }\r\n }\r\n } else {\r\n while (current < input.length && /[a-zA-Z0-9_-]/.test(input[current])) {\r\n value += input[current];\r\n current++;\r\n }\r\n }\r\n tokens.push({ type: 'IDENTIFIER', value });\r\n continue;\r\n }\r\n\r\n if (/[0-9]/.test(char)) {\r\n let value = '';\r\n while (current < input.length && /[0-9.]/.test(input[current])) {\r\n value += input[current];\r\n current++;\r\n }\r\n tokens.push({ type: 'NUMBER', value });\r\n continue;\r\n }\r\n\r\n if (char === \"'\") {\r\n let value = '';\r\n current++; // skip '\r\n while (current < input.length && input[current] !== \"'\") {\r\n value += input[current];\r\n current++;\r\n }\r\n current++; // skip '\r\n tokens.push({ type: 'STRING', value });\r\n continue;\r\n }\r\n\r\n // Operators: ==, !=, &&, ||, !, >=, <=, >, <\r\n if (char === '=' && input[current + 1] === '=') {\r\n tokens.push({ type: 'OPERATOR', value: '==' });\r\n current += 2;\r\n continue;\r\n }\r\n if (char === '!' && input[current + 1] === '=') {\r\n tokens.push({ type: 'OPERATOR', value: '!=' });\r\n current += 2;\r\n continue;\r\n }\r\n if (char === '>' && input[current + 1] === '=') {\r\n tokens.push({ type: 'OPERATOR', value: '>=' });\r\n current += 2;\r\n continue;\r\n }\r\n if (char === '<' && input[current + 1] === '=') {\r\n tokens.push({ type: 'OPERATOR', value: '<=' });\r\n current += 2;\r\n continue;\r\n }\r\n if (char === '>') {\r\n tokens.push({ type: 'OPERATOR', value: '>' });\r\n current++;\r\n continue;\r\n }\r\n if (char === '<') {\r\n tokens.push({ type: 'OPERATOR', value: '<' });\r\n current++;\r\n continue;\r\n }\r\n if (char === '&' && input[current + 1] === '&') {\r\n tokens.push({ type: 'OPERATOR', value: '&&' });\r\n current += 2;\r\n continue;\r\n }\r\n if (char === '|' && input[current + 1] === '|') {\r\n tokens.push({ type: 'OPERATOR', value: '||' });\r\n current += 2;\r\n continue;\r\n }\r\n if (char === '!') {\r\n tokens.push({ type: 'OPERATOR', value: '!' });\r\n current++;\r\n continue;\r\n }\r\n\r\n if (char === '+' || char === '-' || char === '*' || char === '/' || char === '%') {\r\n tokens.push({ type: 'OPERATOR', value: char });\r\n current++;\r\n continue;\r\n }\r\n\r\n // Identifiers (for functions like in, contains)\r\n const idMatch = input.slice(current).match(/^[a-zA-Z][a-zA-Z0-9_]*/);\r\n if (idMatch) {\r\n // boolean check already exists below, so we handle it here or let it fall through\r\n const word = idMatch[0];\r\n if (word === 'true' || word === 'false') {\r\n tokens.push({ type: 'BOOLEAN', value: word });\r\n current += word.length;\r\n } else {\r\n tokens.push({ type: 'IDENTIFIER', value: word });\r\n current += word.length;\r\n }\r\n continue;\r\n }\r\n\r\n throw new Error(`Unexpected character: ${char} at position ${current}`);\r\n }\r\n\r\n return tokens;\r\n}\r\n","import { ASTNode, DebugInfo, EffectType, EvaluationContext, FormFxOptions, FXValue, JSONEffect, JSONRule, Rule } from '../types';\r\nimport { applyClear, applyDisabled, applyRequired, applyShow } from './dom/effects';\r\nimport { findById } from './dom/select';\r\nimport { getInputValue } from './dom/value';\r\nimport { evaluate } from './expr/evaluator';\r\nimport { parse } from './expr/parser';\r\nimport { tokenize } from './expr/tokenizer';\r\n\r\nexport function createRules(formElement: HTMLElement, options: FormFxOptions = {}): Rule[] {\r\n const rules: Rule[] = [];\r\n \r\n // 1. attributes ルール\r\n const attributeTypes: EffectType[] = ['show', 'required', 'disabled', 'enable', 'clear'];\r\n\r\n attributeTypes.forEach(type => {\r\n const attrName = `data-fx-${type}`;\r\n const elements = formElement.querySelectorAll<HTMLElement>(`[${attrName}]`);\r\n \r\n elements.forEach(el => {\r\n const expression = el.getAttribute(attrName) || '';\r\n try {\r\n const tokens = tokenize(expression);\r\n const ast = parse(tokens);\r\n rules.push({\r\n element: el,\r\n type,\r\n expression,\r\n ast,\r\n source: 'attribute'\r\n });\r\n } catch (e) {\r\n console.warn(`[FormFx] Failed to parse expression \"${expression}\" for ${attrName}:`, e);\r\n }\r\n });\r\n });\r\n\r\n // 2. JSON rules\r\n if (options.rules) {\r\n options.rules.forEach(jsonRule => {\r\n try {\r\n const tokens = tokenize(jsonRule.if);\r\n const ast = parse(tokens);\r\n \r\n const addEffectRules = (effects: JSONEffect[], isElse: boolean) => {\r\n effects.forEach(effect => {\r\n Object.entries(effect).forEach(([key, selector]) => {\r\n if (typeof selector !== 'string' || !selector) return;\r\n let type = (key === 'require' ? 'required' : key) as EffectType;\r\n let expression = jsonRule.if;\r\n let finalAst = ast;\r\n\r\n if (type === 'hide') {\r\n type = 'show';\r\n finalAst = { type: 'UnaryExpression', operator: '!', argument: ast };\r\n expression = `!(${jsonRule.if})`;\r\n }\r\n\r\n if (isElse) {\r\n finalAst = { type: 'UnaryExpression', operator: '!', argument: finalAst };\r\n expression = `!(${expression})`;\r\n }\r\n\r\n const elements = formElement.querySelectorAll(selector);\r\n elements.forEach(node => {\r\n const el = node as HTMLElement;\r\n rules.push({\r\n id: jsonRule.id,\r\n element: el,\r\n type,\r\n expression,\r\n ast: finalAst,\r\n source: 'json',\r\n rawRule: jsonRule,\r\n disabled: jsonRule.disabled // v0.4\r\n });\r\n });\r\n });\r\n });\r\n };\r\n\r\n addEffectRules(jsonRule.then, false);\r\n if (jsonRule.else) {\r\n addEffectRules(jsonRule.else, true);\r\n }\r\n } catch (e) {\r\n console.warn(`[FormFx] Failed to parse JSON rule:`, jsonRule, e);\r\n }\r\n });\r\n }\r\n\r\n return rules;\r\n}\r\n\r\n/**\r\n * 依存しているフィールド(#id または @row.field)をASTから抽出する\r\n */\r\nexport function getDependencies(ast: ASTNode): { global: string[], row: string[] } {\r\n const global = new Set<string>();\r\n const row = new Set<string>();\r\n \r\n function traverse(node: ASTNode) {\r\n if (node.type === 'Identifier') {\r\n if (node.name.startsWith('@row.')) {\r\n row.add(node.name.substring(5));\r\n } else {\r\n global.add(node.name);\r\n }\r\n } else if (node.type === 'BinaryExpression' || node.type === 'LogicalExpression') {\r\n traverse(node.left);\r\n traverse(node.right);\r\n } else if (node.type === 'UnaryExpression') {\r\n traverse(node.argument);\r\n } else if (node.type === 'CallExpression') {\r\n node.arguments.forEach(traverse);\r\n } else if (node.type === 'ArrayExpression') {\r\n node.elements.forEach(traverse);\r\n }\r\n }\r\n \r\n traverse(ast);\r\n return {\r\n global: Array.from(global),\r\n row: Array.from(row)\r\n };\r\n}\r\n\r\nconst BUILTIN_FUNCTIONS: Record<string, (...args: any[]) => FXValue> = {\r\n in: (val, list) => {\r\n if (!Array.isArray(list)) return false;\r\n return list.includes(val);\r\n },\r\n contains: (val, sub) => {\r\n if (typeof val !== 'string') return false;\r\n return val.includes(sub);\r\n }\r\n};\r\n\r\nexport function runRules(formElement: HTMLElement, rules: Rule[], options: FormFxOptions): DebugInfo[] {\r\n const debugInfos: DebugInfo[] = [];\r\n // コンテキスト(値の辞書)を作成\r\n const globalContext: Record<string, FXValue> = {};\r\n \r\n // キャッシュして効率化することも考えられるが、まずは全ルール評価\r\n const allGlobalDeps = new Set<string>();\r\n rules.forEach(rule => {\r\n getDependencies(rule.ast).global.forEach(id => allGlobalDeps.add(id));\r\n });\r\n\r\n allGlobalDeps.forEach(id => {\r\n const el = findById(formElement, id);\r\n if (el) {\r\n globalContext[id] = getInputValue(el);\r\n } else {\r\n globalContext[id] = null;\r\n }\r\n });\r\n\r\n const baseEvalContext: EvaluationContext = {\r\n global: globalContext,\r\n functions: BUILTIN_FUNCTIONS\r\n };\r\n\r\n // 各ルールを評価して適用\r\n const elementToTypeToSource = new Map<HTMLElement, Map<EffectType, 'attribute' | 'json'>>();\r\n rules.forEach(rule => {\r\n if (!elementToTypeToSource.has(rule.element)) {\r\n elementToTypeToSource.set(rule.element, new Map());\r\n }\r\n const types = elementToTypeToSource.get(rule.element)!;\r\n // JSONが後にあるので、JSONがあれば上書きされる\r\n types.set(rule.type, rule.source);\r\n });\r\n\r\n rules.forEach(rule => {\r\n if (rule.disabled) return;\r\n\r\n // Determine if this rule should be skipped because a JSON rule for the same element and type exists.\r\n // JSON rules (added later in createRules) take precedence over attribute rules.\r\n if (rule.source === 'attribute') {\r\n const winner = elementToTypeToSource.get(rule.element)?.get(rule.type);\r\n if (winner === 'json') return;\r\n }\r\n\r\n try {\r\n // row context の構築\r\n let rowContext: Record<string, FXValue> | undefined = undefined;\r\n const rowItem = rule.element.closest('[data-fx-item]');\r\n if (rowItem) {\r\n rowContext = {};\r\n const deps = getDependencies(rule.ast);\r\n deps.row.forEach(fieldName => {\r\n const fieldEl = rowItem.querySelector(`[data-fx-field=\"${fieldName}\"]`);\r\n if (fieldEl) {\r\n // フィールド内の実際の入力要素を取得\r\n const input = fieldEl.querySelector('input, select, textarea') as HTMLElement;\r\n rowContext![fieldName] = input ? getInputValue(input) : null;\r\n } else {\r\n rowContext![fieldName] = null;\r\n }\r\n });\r\n }\r\n\r\n const evalContext: EvaluationContext = {\r\n ...baseEvalContext,\r\n row: rowContext\r\n };\r\n\r\n const result = evaluate(rule.ast, evalContext);\r\n const boolResult = !!result;\r\n\r\n if (options.debug) {\r\n debugInfos.push({\r\n rule,\r\n result: boolResult,\r\n elements: [rule.element]\r\n });\r\n console.debug(`[FormFx Debug] Rule: ${rule.type}=\"${rule.expression}\" (source: ${rule.source}, result: ${boolResult})`, {\r\n element: rule.element,\r\n result: boolResult,\r\n context: evalContext\r\n });\r\n }\r\n\r\n switch (rule.type) {\r\n case 'show':\r\n applyShow(rule.element, boolResult, options);\r\n break;\r\n case 'required':\r\n applyRequired(rule.element, boolResult);\r\n break;\r\n case 'disabled':\r\n applyDisabled(rule.element, boolResult);\r\n break;\r\n case 'enable':\r\n applyDisabled(rule.element, !boolResult);\r\n break;\r\n case 'clear':\r\n // 変化した瞬間(false -> true)のみクリア\r\n if (boolResult && rule.lastResult === false) {\r\n applyClear(rule.element);\r\n }\r\n break;\r\n }\r\n \r\n rule.lastResult = boolResult;\r\n\r\n } catch (e) {\r\n const errorMsg = e instanceof Error ? e.message : String(e);\r\n if (options.debug) {\r\n debugInfos.push({\r\n rule,\r\n result: 'error',\r\n error: errorMsg,\r\n elements: [rule.element]\r\n });\r\n console.warn(`[FormFx Debug] Evaluation failed: \"${rule.expression}\"`, e);\r\n } else {\r\n console.warn(`[FormFx] Failed to evaluate expression \"${rule.expression}\":`, e);\r\n }\r\n }\r\n });\r\n\r\n return debugInfos;\r\n}\r\n","export function setupEventListener(\r\n formElement: HTMLElement,\r\n callback: () => void,\r\n dependentIds: string[]\r\n): () => void {\r\n const handler = () => {\r\n callback();\r\n };\r\n\r\n // 依存フィールド単位での監視\r\n // ※ 動的に要素が追加される場合は MutationObserver がカバーする\r\n const cleanups: (() => void)[] = [];\r\n\r\n // 全体を監視しつつ、特定要素へのリスナーも検討可能だが、\r\n // シンプルさと確実性のために formElement への委譲を維持しつつ、\r\n // 将来的にはここで絞り込みを行う。\r\n // v0.2では「参照されているフィールドのみ」を意識した設計にする。\r\n \r\n formElement.addEventListener('input', handler);\r\n formElement.addEventListener('change', handler);\r\n\r\n return () => {\r\n formElement.removeEventListener('input', handler);\r\n formElement.removeEventListener('change', handler);\r\n };\r\n}\r\n\r\nexport function setupMutationObserver(\r\n formElement: HTMLElement,\r\n callback: () => void\r\n): MutationObserver {\r\n const observer = new MutationObserver((mutations) => {\r\n let shouldUpdate = false;\r\n for (const mutation of mutations) {\r\n if (mutation.type === 'childList' && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)) {\r\n shouldUpdate = true;\r\n break;\r\n }\r\n }\r\n \r\n if (shouldUpdate) {\r\n callback();\r\n }\r\n });\r\n\r\n observer.observe(formElement, {\r\n childList: true,\r\n subtree: true\r\n });\r\n\r\n return observer;\r\n}\r\n","import { RepeaterConfig } from '../../types';\r\n\r\nexport function setupRepeaters(\r\n formElement: HTMLElement,\r\n onUpdate: () => void\r\n): () => void {\r\n const repeaterElements = formElement.querySelectorAll<HTMLElement>('[data-fx-repeater]');\r\n const cleanups: (() => void)[] = [];\r\n\r\n repeaterElements.forEach(container => {\r\n const config: RepeaterConfig = {\r\n container,\r\n name: container.getAttribute('data-fx-name') || '',\r\n max: parseInt(container.getAttribute('data-fx-max') || '10', 10),\r\n addButton: container.querySelector<HTMLElement>('[data-fx-add]'),\r\n listContainer: container.querySelector<HTMLElement>('[data-fx-list]') || container,\r\n template: container.querySelector<HTMLTemplateElement>('template[data-fx-template]')!\r\n };\r\n\r\n if (!config.template) {\r\n console.warn('[FormFx] Repeater template not found', container);\r\n return;\r\n }\r\n\r\n const handleAdd = () => {\r\n const items = config.listContainer.querySelectorAll('[data-fx-item]');\r\n if (items.length < config.max) {\r\n addItem(config);\r\n reindex(config);\r\n onUpdate();\r\n }\r\n };\r\n\r\n if (config.addButton) {\r\n config.addButton.addEventListener('click', handleAdd);\r\n cleanups.push(() => config.addButton?.removeEventListener('click', handleAdd));\r\n }\r\n\r\n // 既存のアイテムに対してもイベント設定とリインデックスが必要\r\n const handleListClick = (e: Event) => {\r\n const target = e.target as HTMLElement;\r\n if (target.closest('[data-fx-remove]')) {\r\n const item = target.closest('[data-fx-item]');\r\n if (item) {\r\n item.remove();\r\n reindex(config);\r\n onUpdate();\r\n }\r\n }\r\n };\r\n\r\n config.listContainer.addEventListener('click', handleListClick);\r\n cleanups.push(() => config.listContainer.removeEventListener('click', handleListClick));\r\n\r\n // 初期化時に一度リインデックス\r\n reindex(config);\r\n });\r\n\r\n return () => cleanups.forEach(c => c());\r\n}\r\n\r\nfunction addItem(config: RepeaterConfig) {\r\n const content = config.template.content.cloneNode(true) as DocumentFragment;\r\n const item = content.querySelector('[data-fx-item]');\r\n if (item) {\r\n config.listContainer.appendChild(item);\r\n }\r\n}\r\n\r\nfunction reindex(config: RepeaterConfig) {\r\n const items = config.listContainer.querySelectorAll<HTMLElement>('[data-fx-item]');\r\n items.forEach((item, index) => {\r\n // data-fx-index 属性を付与(デバッグや特定用途向け)\r\n item.setAttribute('data-fx-index', index.toString());\r\n\r\n const fields = item.querySelectorAll<HTMLElement>('[data-fx-field]');\r\n fields.forEach(field => {\r\n const fieldName = field.getAttribute('data-fx-field');\r\n if (fieldName) {\r\n const inputs = field.querySelectorAll<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>('input, select, textarea');\r\n inputs.forEach(input => {\r\n input.name = `${config.name}[${index}].${fieldName}`;\r\n // 行内参照を容易にするため、input自体にも field 名を保持させることがあるが、\r\n // 今回は closest('[data-fx-field]') で解決する設計とした。\r\n });\r\n }\r\n });\r\n });\r\n\r\n // 追加ボタンの表示/非表示制御(任意だが親切)\r\n if (config.addButton) {\r\n if (items.length >= config.max) {\r\n config.addButton.style.display = 'none';\r\n } else {\r\n config.addButton.style.display = '';\r\n }\r\n }\r\n}\r\n","import { DebugInfo, PersistOptions } from '../../types';\r\n\r\nexport class DebugPanel {\r\n private container: HTMLElement | null = null;\r\n private shadow: ShadowRoot | null = null;\r\n private persistInfo: PersistOptions | null = null;\r\n private isMinimized = false;\r\n private pos = { x: 10, y: 10 };\r\n private isDragging = false;\r\n private dragStart = { x: 0, y: 0 };\r\n\r\n constructor() {\r\n this.loadState();\r\n this.container = document.createElement('div');\r\n this.container.id = 'formfx-debug-panel';\r\n this.shadow = this.container.attachShadow({ mode: 'open' });\r\n this.renderInitial();\r\n document.body.appendChild(this.container);\r\n this.setupDragging();\r\n }\r\n\r\n private loadState() {\r\n try {\r\n const saved = localStorage.getItem('formfx-debug-state');\r\n if (saved) {\r\n const state = JSON.parse(saved);\r\n this.isMinimized = !!state.isMinimized;\r\n if (state.pos) this.pos = state.pos;\r\n }\r\n } catch (e) {\r\n // Ignore\r\n }\r\n }\r\n\r\n private saveState() {\r\n try {\r\n localStorage.setItem('formfx-debug-state', JSON.stringify({\r\n isMinimized: this.isMinimized,\r\n pos: this.pos\r\n }));\r\n } catch (e) {\r\n // Ignore\r\n }\r\n }\r\n\r\n private setupDragging() {\r\n if (!this.shadow || !this.container) return;\r\n const header = this.shadow.querySelector('.header') as HTMLElement;\r\n if (!header) return;\r\n\r\n header.style.cursor = 'move';\r\n header.addEventListener('mousedown', (e) => {\r\n this.isDragging = true;\r\n this.dragStart = {\r\n x: e.clientX + this.pos.x,\r\n y: e.clientY + this.pos.y\r\n };\r\n e.preventDefault();\r\n });\r\n\r\n window.addEventListener('mousemove', (e) => {\r\n if (!this.isDragging) return;\r\n this.pos.x = this.dragStart.x - e.clientX;\r\n this.pos.y = this.dragStart.y - e.clientY;\r\n this.updatePosition();\r\n });\r\n\r\n window.addEventListener('mouseup', () => {\r\n if (this.isDragging) {\r\n this.isDragging = false;\r\n this.saveState();\r\n }\r\n });\r\n\r\n const toggleBtn = this.shadow.getElementById('toggle-minimize');\r\n toggleBtn?.addEventListener('click', (e) => {\r\n e.stopPropagation();\r\n this.isMinimized = !this.isMinimized;\r\n this.renderInitial();\r\n this.updatePosition();\r\n this.saveState();\r\n });\r\n }\r\n\r\n private updatePosition() {\r\n if (!this.container) return;\r\n this.container.style.bottom = `${this.pos.y}px`;\r\n this.container.style.right = `${this.pos.x}px`;\r\n }\r\n\r\n setPersistInfo(info: PersistOptions) {\r\n this.persistInfo = info;\r\n }\r\n\r\n private renderInitial() {\r\n if (!this.shadow) return;\r\n this.shadow.innerHTML = `\r\n <style>\r\n :host {\r\n position: fixed;\r\n bottom: ${this.pos.y}px;\r\n right: ${this.pos.x}px;\r\n width: 350px;\r\n max-height: 80vh;\r\n background: rgba(0, 0, 0, 0.85);\r\n color: white;\r\n font-family: monospace;\r\n font-size: 12px;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n z-index: 10000;\r\n box-shadow: 0 4px 12px rgba(0,0,0,0.3);\r\n border: 1px solid #444;\r\n display: flex;\r\n flex-direction: column;\r\n }\r\n .header {\r\n padding: 8px 12px;\r\n background: #222;\r\n border-bottom: 1px solid #444;\r\n font-weight: bold;\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n user-select: none;\r\n }\r\n .content {\r\n padding: 8px;\r\n overflow-y: auto;\r\n display: ${this.isMinimized ? 'none' : 'block'};\r\n }\r\n .header-controls {\r\n display: flex;\r\n gap: 8px;\r\n }\r\n .btn-icon {\r\n cursor: pointer;\r\n padding: 2px 6px;\r\n border-radius: 4px;\r\n background: #444;\r\n border: none;\r\n color: white;\r\n font-size: 10px;\r\n }\r\n .btn-icon:hover {\r\n background: #666;\r\n }\r\n .rule {\r\n margin-bottom: 8px;\r\n padding: 6px;\r\n border-radius: 4px;\r\n background: #333;\r\n }\r\n .rule-header {\r\n display: flex;\r\n justify-content: space-between;\r\n margin-bottom: 4px;\r\n }\r\n .rule-id {\r\n color: #aaa;\r\n font-size: 10px;\r\n }\r\n .rule-expr {\r\n word-break: break-all;\r\n color: #88ccff;\r\n }\r\n .status {\r\n font-weight: bold;\r\n padding: 2px 4px;\r\n border-radius: 2px;\r\n font-size: 10px;\r\n }\r\n .status-true { background: #228b22; }\r\n .status-false { background: #b22222; }\r\n .status-error { background: #ff4500; }\r\n .status-disabled { background: #666; color: #ccc; }\r\n .persist-badge {\r\n font-size: 9px;\r\n background: #444;\r\n padding: 1px 4px;\r\n border-radius: 4px;\r\n color: #00ff00;\r\n border: 1px solid #00ff00;\r\n }\r\n .header-top {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n }\r\n .effects {\r\n margin-top: 4px;\r\n padding-left: 12px;\r\n border-left: 2px solid #555;\r\n font-size: 11px;\r\n }\r\n .effect-item {\r\n margin-top: 2px;\r\n }\r\n .effect-skipped {\r\n color: #888;\r\n text-decoration: line-through;\r\n }\r\n .error-msg {\r\n color: #ff6347;\r\n font-size: 10px;\r\n margin-top: 4px;\r\n }\r\n </style>\r\n <div class=\"header\">\r\n <div class=\"header-top\">\r\n <span>FormFx Debug</span>\r\n ${this.persistInfo ? `<span class=\"persist-badge\">PERSIST</span>` : ''}\r\n </div>\r\n <div class=\"header-controls\">\r\n <button id=\"toggle-minimize\" class=\"btn-icon\">${this.isMinimized ? '□' : '_'} </button>\r\n </div>\r\n </div>\r\n <div id=\"rule-list\" class=\"content\">\r\n Waiting for evaluation...\r\n </div>\r\n `;\r\n this.setupDragging();\r\n }\r\n\r\n update(debugInfos: DebugInfo[]) {\r\n if (!this.shadow) return;\r\n const listEl = this.shadow.getElementById('rule-list');\r\n if (!listEl) return;\r\n\r\n listEl.innerHTML = debugInfos.map(info => {\r\n const { rule, result, error } = info;\r\n \r\n let statusClass = result === 'error' ? 'status-error' : (result ? 'status-true' : 'status-false');\r\n let statusText = result === 'error' ? 'ERROR' : result.toString().toUpperCase();\r\n\r\n if (rule.disabled) {\r\n statusClass = 'status-disabled';\r\n statusText = 'DISABLED';\r\n }\r\n \r\n let effectInfo = `${rule.type} ${this.getSelector(rule.element)}`;\r\n if (result !== true && result !== 'error' && !rule.disabled) {\r\n effectInfo = `<span class=\"effect-skipped\">${effectInfo} (skipped)</span>`;\r\n }\r\n if (rule.disabled) {\r\n effectInfo = `<span class=\"effect-skipped\">${effectInfo}</span>`;\r\n }\r\n\r\n return `\r\n <div class=\"rule\">\r\n <div class=\"rule-header\">\r\n <span class=\"rule-id\">${rule.id || (rule.source === 'attribute' ? 'Attr' : 'JSON')}</span>\r\n <span class=\"status ${statusClass}\">${statusText}</span>\r\n </div>\r\n <div class=\"rule-expr\">${rule.expression}</div>\r\n <div class=\"effects\">\r\n <div class=\"effect-item\">${effectInfo}</div>\r\n </div>\r\n ${error ? `<div class=\"error-msg\">Error: ${error}</div>` : ''}\r\n </div>\r\n `;\r\n }).join('');\r\n }\r\n\r\n private getSelector(el: HTMLElement): string {\r\n if (el.id) return `#${el.id}`;\r\n if (el.className) return `.${el.className.split(' ').join('.')}`;\r\n return el.tagName.toLowerCase();\r\n }\r\n\r\n destroy() {\r\n if (this.container && this.container.parentNode) {\r\n this.container.parentNode.removeChild(this.container);\r\n }\r\n }\r\n\r\n show() {\r\n if (this.container) {\r\n this.container.style.display = 'block';\r\n }\r\n }\r\n\r\n hide() {\r\n if (this.container) {\r\n this.container.style.display = 'none';\r\n }\r\n }\r\n}\r\n","import { FormFxOptions, Rule, JSONRule, RuleEditorOptions } from '../types';\r\nimport { createRules, runRules, getDependencies } from '../internal/engine';\r\nimport { setupEventListener, setupMutationObserver } from '../internal/observe';\r\nimport { setupRepeaters } from '../plugins/repeater';\r\nimport { DebugPanel } from '../internal/debug/panel';\r\n\r\nexport class FormFx {\r\n private rules: Rule[] = [];\r\n private options: FormFxOptions;\r\n private cleanupEvents: (() => void) | null = null;\r\n private cleanupRepeaters: (() => void) | null = null;\r\n private observer: MutationObserver | null = null;\r\n private debugPanel: DebugPanel | null = null;\r\n private isPaused = false;\r\n\r\n constructor(private formElement: HTMLElement, options: FormFxOptions = {}) {\r\n this.options = {\r\n disableOnHide: true,\r\n clearOnHide: false,\r\n ...options\r\n };\r\n\r\n // 永続化データの読み込み (JSON rulesのみ)\r\n this.loadPersistedRules();\r\n }\r\n\r\n mount(): void {\r\n // 0. Debug Panel の初期化\r\n if (this.options.debug) {\r\n this.debugPanel = new DebugPanel();\r\n if (this.options.persist) {\r\n this.debugPanel.setPersistInfo(this.options.persist);\r\n }\r\n }\r\n\r\n // 1. ルールの初回収集\r\n this.refreshRules();\r\n\r\n // 2. リピーターの初期化\r\n this.cleanupRepeaters = setupRepeaters(this.formElement, () => {\r\n // DOMが変化した後にルールを再収集して評価\r\n this.refreshRules();\r\n this.setupEvents(); // 依存フィールドが変わる可能性があるため再設定\r\n this.evaluate();\r\n });\r\n\r\n // 3. イベントリスナーの設定\r\n this.setupEvents();\r\n\r\n // 4. MutationObserverの設定(動的な属性追加などに対応)\r\n this.observer = setupMutationObserver(this.formElement, () => {\r\n this.refreshRules();\r\n this.setupEvents();\r\n this.evaluate();\r\n });\r\n\r\n // 5. 初回評価\r\n this.evaluate();\r\n }\r\n\r\n reEvaluate(): void {\r\n this.evaluate();\r\n }\r\n\r\n private setupEvents(): void {\r\n if (this.cleanupEvents) this.cleanupEvents();\r\n\r\n const dependentIds = new Set<string>();\r\n this.rules.forEach(rule => {\r\n getDependencies(rule.ast).global.forEach(id => {\r\n // IDだけでなくnameでも参照される可能性があるため、基本的には広めに監視する\r\n dependentIds.add(id);\r\n });\r\n });\r\n\r\n this.cleanupEvents = setupEventListener(this.formElement, () => {\r\n this.evaluate();\r\n }, Array.from(dependentIds));\r\n }\r\n\r\n private refreshRules(): void {\r\n // attributes ルールと JSON ルールの両方を収集\r\n // JSON rules が優先されるように後でマージするが、\r\n // createRules 内で JSON rules は後に追加されるように実装済み\r\n this.rules = createRules(this.formElement, this.options);\r\n }\r\n\r\n private evaluate(): void {\r\n if (this.isPaused) return;\r\n\r\n const debugInfos = runRules(this.formElement, this.rules, this.options);\r\n if (this.debugPanel) {\r\n this.debugPanel.update(debugInfos);\r\n }\r\n }\r\n\r\n /**\r\n * ルール評価を一時停止する (v0.4)\r\n */\r\n pause(): void {\r\n this.isPaused = true;\r\n }\r\n\r\n /**\r\n * ルール評価を再開する (v0.4)\r\n */\r\n resume(): void {\r\n this.isPaused = false;\r\n this.evaluate();\r\n }\r\n\r\n /**\r\n * JSONルールをエクスポートする (v0.4)\r\n */\r\n exportRules(): JSONRule[] {\r\n return this.options.rules || [];\r\n }\r\n\r\n /**\r\n * JSONルールをインポートする (v0.4)\r\n */\r\n importRules(rules: JSONRule[]): void {\r\n this.options.rules = rules;\r\n this.savePersistedRules();\r\n this.refreshRules();\r\n this.setupEvents();\r\n this.evaluate();\r\n }\r\n\r\n /**\r\n * デバッグパネルの表示/非表示を切り替える (v0.4.1)\r\n */\r\n setDebug(enabled: boolean): void {\r\n this.options.debug = enabled;\r\n if (enabled) {\r\n if (!this.debugPanel) {\r\n this.debugPanel = new DebugPanel();\r\n if (this.options.persist) {\r\n this.debugPanel.setPersistInfo(this.options.persist);\r\n }\r\n // 初回評価を反映させる\r\n this.evaluate();\r\n } else {\r\n this.debugPanel.show();\r\n }\r\n } else {\r\n if (this.debugPanel) {\r\n this.debugPanel.hide();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Rule Editor を有効化する (v0.4)\r\n */\r\n async enableRuleEditor(options: RuleEditorOptions): Promise<void> {\r\n const { RuleEditor } = await import('../editor/RuleEditor');\r\n const editor = new RuleEditor(this, options);\r\n editor.mount();\r\n }\r\n\r\n private loadPersistedRules(): void {\r\n const { persist } = this.options;\r\n if (!persist) return;\r\n\r\n try {\r\n const storage = persist.storage === 'localStorage' ? localStorage : sessionStorage;\r\n const data = storage.getItem(persist.key);\r\n if (data) {\r\n this.options.rules = JSON.parse(data);\r\n }\r\n } catch (e) {\r\n console.error('[FormFx] Failed to load persisted rules:', e);\r\n }\r\n }\r\n\r\n private savePersistedRules(): void {\r\n const { persist, rules } = this.options;\r\n if (!persist || !rules) return;\r\n\r\n try {\r\n const storage = persist.storage === 'localStorage' ? localStorage : sessionStorage;\r\n storage.setItem(persist.key, JSON.stringify(rules));\r\n } catch (e) {\r\n console.error('[FormFx] Failed to save rules:', e);\r\n }\r\n }\r\n\r\n /**\r\n * JSONルールを有効化する\r\n * @param ruleId \r\n */\r\n enableRule(ruleId: string): void {\r\n this.rules.forEach(r => {\r\n if (r.id === ruleId && r.source === 'json') {\r\n r.disabled = false;\r\n }\r\n });\r\n if (this.options.rules) {\r\n this.options.rules.forEach(r => {\r\n if (r.id === ruleId) r.disabled = false;\r\n });\r\n this.savePersistedRules();\r\n }\r\n this.evaluate();\r\n }\r\n\r\n /**\r\n * JSONルールを無効化する\r\n * @param ruleId \r\n */\r\n disableRule(ruleId: string): void {\r\n this.rules.forEach(r => {\r\n if (r.id === ruleId && r.source === 'json') {\r\n r.disabled = true;\r\n }\r\n });\r\n if (this.options.rules) {\r\n this.options.rules.forEach(r => {\r\n if (r.id === ruleId) r.disabled = true;\r\n });\r\n this.savePersistedRules();\r\n }\r\n this.evaluate();\r\n }\r\n\r\n destroy(): void {\r\n if (this.cleanupEvents) this.cleanupEvents();\r\n if (this.cleanupRepeaters) this.cleanupRepeaters();\r\n if (this.observer) this.observer.disconnect();\r\n if (this.debugPanel) this.debugPanel.destroy();\r\n \r\n // イベントリスナーの再解除を確実にするため\r\n this.cleanupEvents = null;\r\n this.cleanupRepeaters = null;\r\n this.observer = null;\r\n this.debugPanel = null;\r\n\r\n // 適用されたスタイルや属性をリセットしたい場合があるが、\r\n // 基本的には「監視を止める」のが destroy の責務\r\n this.rules = [];\r\n }\r\n}\r\n"],"mappings":"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","names":["RuleEditor_exports","__export","RuleEditor","init_RuleEditor","__esmMin","fx","options","rules","listEl","rule","index","item","formEl","type","effects","eff","i","entry","effectType","selector","btn","newRule","readEffects","rows","row","obj","src_exports","__export","FormFx","getInputValue","el","checked","clearValue","input","setDisabled","disabled","applyShow","el","show","options","isCurrentlyVisible","setDisabled","clearValue","applyRequired","required","input","applyDisabled","disabled","applyClear","findById","root","id","el","byName","evaluate","node","context","fieldName","left","right","func","args","arg","el","parse","tokens","current","walk","parseLogicalOr","node","parseLogicalAnd","operator","right","parseComparison","parseAdditive","comparisonOps","parseMultiplicative","parseUnary","parsePrimary","token","args","elements","tokenize","input","tokens","current","char","isRow","value","rowPrefix","idMatch","word","createRules","formElement","options","rules","type","attrName","el","expression","tokens","tokenize","ast","parse","e","jsonRule","addEffectRules","effects","isElse","effect","key","selector","finalAst","node","getDependencies","global","row","traverse","BUILTIN_FUNCTIONS","val","list","sub","runRules","debugInfos","globalContext","allGlobalDeps","rule","id","findById","getInputValue","baseEvalContext","elementToTypeToSource","rowContext","rowItem","fieldName","fieldEl","input","evalContext","boolResult","evaluate","applyShow","applyRequired","applyDisabled","applyClear","errorMsg","setupEventListener","formElement","callback","dependentIds","handler","cleanups","setupMutationObserver","observer","mutations","shouldUpdate","mutation","setupRepeaters","formElement","onUpdate","repeaterElements","cleanups","container","config","handleAdd","addItem","reindex","handleListClick","e","target","item","c","items","index","field","fieldName","input","DebugPanel","saved","state","header","e","info","debugInfos","listEl","rule","result","error","statusClass","statusText","effectInfo","el","FormFx","formElement","options","DebugPanel","setupRepeaters","setupMutationObserver","dependentIds","rule","getDependencies","id","setupEventListener","createRules","debugInfos","runRules","rules","enabled","RuleEditor","persist","data","e","ruleId"]}
package/dist/index.js CHANGED
@@ -1,4 +1,64 @@
1
- export{a as RuleEditor}from'./chunk-6E5EWQVW.js';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=`
1
+ var J=Object.defineProperty;var U=(t,e)=>()=>(t&&(e=t(t=0)),e);var V=(t,e)=>{for(var r in e)J(t,r,{get:e[r],enumerable:!0})};var k={};V(k,{RuleEditor:()=>M});var M,B=U(()=>{"use strict";M=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)}}});function R(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 O(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 I(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 T(t,e){E(t,e)}function $(t){g(t)}function H(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 S(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 L(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 N(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=L(a),o=S(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=L(i.if),d=S(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 z=y;r.push({id:i.id,element:z,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 X={in:(t,e)=>Array.isArray(e)?e.includes(t):!1,contains:(t,e)=>typeof t!="string"?!1:t.includes(e)};function P(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=H(t,a);l?i[a]=R(l):i[a]=null});let d={global:i,functions:X},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 w=o.querySelector(`[data-fx-field="${m}"]`);if(w){let y=w.querySelector("input, select, textarea");l[m]=y?R(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":O(a.element,f,r);break;case"required":I(a.element,f);break;case"disabled":T(a.element,f);break;case"enable":T(a.element,!f);break;case"clear":f&&a.lastResult===!1&&$(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 q(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 C(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 D(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&&(j(n),A(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(),A(n),e())}};n.listContainer.addEventListener("click",u),s.push(()=>n.listContainer.removeEventListener("click",u)),A(n)}),()=>s.forEach(i=>i())}function j(t){let r=t.template.content.cloneNode(!0).querySelector("[data-fx-item]");r&&t.listContainer.appendChild(r)}function A(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=`
2
62
  <style>
3
63
  :host {
4
64
  position: fixed;
@@ -122,17 +182,17 @@ export{a as RuleEditor}from'./chunk-6E5EWQVW.js';function w(e){if(e instanceof H
122
182
  <div id="rule-list" class="content">
123
183
  Waiting for evaluation...
124
184
  </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>`),`
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>`),`
126
186
  <div class="rule">
127
187
  <div class="rule-header">
128
188
  <span class="rule-id">${i.id||(i.source==="attribute"?"Attr":"JSON")}</span>
129
- <span class="status ${p}">${a}</span>
189
+ <span class="status ${u}">${a}</span>
130
190
  </div>
131
191
  <div class="rule-expr">${i.expression}</div>
132
192
  <div class="effects">
133
193
  <div class="effect-item">${l}</div>
134
194
  </div>
135
- ${u?`<div class="error-msg">Error: ${u}</div>`:""}
195
+ ${d?`<div class="error-msg">Error: ${d}</div>`:""}
136
196
  </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.js');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=[];}};export{M as FormFx};//# sourceMappingURL=index.js.map
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 F=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=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 e=new Set;this.rules.forEach(r=>{b(r.ast).global.forEach(s=>{e.add(s)})}),this.cleanupEvents=q(this.formElement,()=>{this.evaluate()},Array.from(e))}refreshRules(){this.rules=N(this.formElement,this.options)}evaluate(){if(this.isPaused)return;let e=P(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(()=>(B(),k));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));export{F as FormFx};
138
198
  //# sourceMappingURL=index.js.map