5.4 KiB
Conditional Logic Design Document
Date: 2026-02-13 Status: Approved Plugin: obsidian-formfire Phase: 3 — Conditional Field Visibility
Problem
Forms with many fields become overwhelming when not all fields are relevant in every context. A "Meeting Notes" form shouldn't show "Book Author" fields. Currently all fields are always visible — users must skip irrelevant ones manually.
Solution
Add per-field conditional visibility rules with AND/OR logic. Fields can reference other fields' values to determine whether they should be shown or hidden. Hidden fields are excluded from submission.
Approach
Rule-per-Field — each FormField gets an optional conditions object containing rules and a logic combinator. This keeps conditions co-located with the field they control and avoids a separate rules data structure.
Data Model
New Types
type ConditionOperator =
| 'equals' | 'not_equals' // all field types
| 'contains' | 'not_contains' // text, textarea, tags
| 'is_empty' | 'is_not_empty' // all field types
| 'greater_than' | 'less_than'; // number, rating, slider
interface ConditionRule {
fieldId: string; // reference to another field
operator: ConditionOperator;
value?: unknown; // not needed for is_empty/is_not_empty
}
interface FieldConditions {
logic: 'and' | 'or'; // how rules are combined
rules: ConditionRule[]; // at least 1 rule
}
FormField Extension
interface FormField {
// ... existing fields ...
conditions?: FieldConditions; // optional, undefined = always visible
}
Operator Availability by Field Type
| Operator | text/textarea | number | toggle | date/time | dropdown | tags | note-link/folder | rating | slider | color |
|---|---|---|---|---|---|---|---|---|---|---|
| equals / not_equals | yes | yes | yes | yes | yes | — | yes | yes | yes | yes |
| contains / not_contains | yes | — | — | — | — | yes | — | — | — | — |
| is_empty / is_not_empty | yes | yes | — | yes | yes | yes | yes | yes | yes | yes |
| greater_than / less_than | — | yes | — | — | — | — | — | yes | yes | — |
Note: toggle doesn't support is_empty since it always has a value (true/false).
Evaluation Engine
New module: src/utils/condition-engine.ts
evaluateConditions(
conditions: FieldConditions,
values: Record<string, unknown>
): boolean
Logic
- Each
ConditionRuleis evaluated against the current value of the referenced field - Results are combined:
and= all must be true,or= at least one must be true - Return:
true= show field,false= hide field
Operator Semantics
equals/not_equals— string coercion for comparison, strict for booleanscontains/not_contains— substring check for strings, element check for arrays (tags)is_empty/is_not_empty—null,undefined,"",[]count as emptygreater_than/less_than— numeric comparison (parseFloat)
Cycle Prevention
Fields can only reference fields that appear before them in the field list. This prevents circular dependencies by design and is enforced in the builder UI (field dropdown only shows preceding fields).
Reactivity in Form Modal
- Change listeners on every field — value changes trigger
reevaluateVisibility() reevaluateVisibility()iterates all fields withconditions, evaluates them, togglesdisplay: none- Hidden fields excluded from submit — values not written to frontmatter
- Validation skips hidden fields — a required field that is hidden does not block submission
Builder UI
Per-Field Conditions Editor
Located below existing field settings, collapsible:
Collapsed: Shows summary like "Show if: type = Meeting" or "Always visible"
Expanded:
- Logic dropdown at top:
ALL conditions match(AND) /ANY condition matches(OR) - Rule rows, each containing:
- Field dropdown (only fields before current field)
- Operator dropdown (filtered by selected field's type)
- Value input (type-dependent: text input, dropdown with options, number input, toggle; hidden for is_empty/is_not_empty)
- Delete button (×)
- "+ Add condition" button
Live Preview Behavior
Fields with unmet conditions are shown grayed out with a "conditional" badge in the builder preview (not fully hidden, so the builder user can see all fields exist).
Files Changed
| File | Change |
|---|---|
src/types.ts |
New types: ConditionOperator, ConditionRule, FieldConditions; add conditions? to FormField |
src/utils/condition-engine.ts |
New — evaluateConditions(), operator logic, helpers |
src/utils/validators.ts |
Accept visibility map, skip hidden fields |
src/ui/form-modal.ts |
Change listeners, reevaluateVisibility(), exclude hidden fields from submit |
src/ui/form-builder.ts |
Conditions editor UI per field (collapsed/expanded, rule editor) |
src/ui/field-renderers.ts |
No changes — conditions operate one level above |
styles.css |
Condition editor styles, conditional badge in preview, show/hide transitions |
Out of Scope
- Nested condition groups (AND inside OR) — one level is sufficient
- Form-level conditions (hide entire form)
- Actions other than show/hide (e.g. set value, change required)
- Migration —
conditionsis optional, existing forms work unchanged