'show', 'label' => 'Show', 'apply' => fn( $state, $action ) => self::with_field( $state, $action['target'], [ 'visible' => true ] ) ] ); Action_Registry::register( [ 'id' => 'hide', 'label' => 'Hide', 'apply' => fn( $state, $action ) => self::with_field( $state, $action['target'], [ 'visible' => false ] ) ] ); Action_Registry::register( [ 'id' => 'enable', 'label' => 'Enable', 'apply' => fn( $state, $action ) => self::with_field( $state, $action['target'], [ 'enabled' => true ] ) ] ); Action_Registry::register( [ 'id' => 'disable', 'label' => 'Disable', 'apply' => fn( $state, $action ) => self::with_field( $state, $action['target'], [ 'enabled' => false ] ) ] ); Action_Registry::register( [ 'id' => 'require', 'label' => 'Require', 'apply' => fn( $state, $action ) => self::with_field( $state, $action['target'], [ 'required' => true ] ) ] ); Action_Registry::register( [ 'id' => 'unrequire', 'label' => 'Make optional', 'apply' => fn( $state, $action ) => self::with_field( $state, $action['target'], [ 'required' => false ] ) ] ); Action_Registry::register( [ 'id' => 'readonly', 'label' => 'Make read-only', 'apply' => fn( $state, $action ) => self::with_field( $state, $action['target'], [ 'readonly' => true ] ) ] ); Action_Registry::register( [ 'id' => 'editable', 'label' => 'Make editable', 'apply' => fn( $state, $action ) => self::with_field( $state, $action['target'], [ 'readonly' => false ] ) ] ); } private static function with_field( array $state, $field_id, array $patch ) { $current = $state[ $field_id ] ?? []; $state[ $field_id ] = array_merge( $current, $patch ); return $state; } /** * Same baseline every consumer (evaluator, a future PHP-side inspector) * starts from — mirrors baseFieldState() in actions/builtins.js, * including the "a field targeted only by 'show' actions starts * hidden" fix documented there. `$fields` is a flat array here * (server-side callers already flatten containers before calling, see * Evaluator::evaluate_rules()). */ public static function base_field_state( array $fields, array $rules = [] ) { $starts_hidden = []; $has_hide_action = []; foreach ( $rules as $rule ) { if ( false === ( $rule['enabled'] ?? true ) ) { continue; } foreach ( $rule['actions'] ?? [] as $action ) { if ( 'show' === ( $action['type'] ?? null ) ) { $starts_hidden[ $action['target'] ] = true; } if ( 'hide' === ( $action['type'] ?? null ) ) { $has_hide_action[ $action['target'] ] = true; } } } foreach ( $has_hide_action as $field_id => $_ ) { unset( $starts_hidden[ $field_id ] ); } $state = []; foreach ( $fields as $field ) { $state[ $field['id'] ] = [ 'visible' => empty( $starts_hidden[ $field['id'] ] ), 'enabled' => true, 'required' => ! empty( $field['required'] ), 'readonly' => false, ]; } return $state; } }