with one row per field. * Hidden inputs and datalist elements are emitted before the table; * notices before, submit button after. * * Usage: * echo RsvFormBuilder::create() * ->text('name', 'Name', required: true) * ->email('email', 'Email') * ->submit('Save'); */ class RsvFormBuilder { private string $form_id = ""; /** @var string[] Rendered before the table (hidden inputs, datalists). */ private array $before = []; /** @var string[] WP admin notice banners rendered before the table. */ private array $notices = []; /** @var string[] elements inside the table. */ private array $rows = []; /** @var string[] Rendered after the table (submit button). */ private array $after = []; private function __construct() {} public static function create(string $id): static { return new static(); } // ------------------------------------------------------------------------- // Input fields — each becomes a in the table // ------------------------------------------------------------------------- public function text( string $id, string $label, string $desc = '', bool $required = false, string $value = '' ): static { $req = $required ? 'required' : ''; $ctrl = ''; return $this->row($id, $label, $ctrl, $desc); } public function email( string $id, string $label, string $desc = '', bool $required = false, string $value = '', ?string $list_id = null ): static { $req = $required ? 'required' : ''; $list = $list_id !== null ? 'list="' . esc_attr($list_id) . '"' : ''; $ctrl = ''; return $this->row($id, $label, $ctrl, $desc); } public function password( string $id, string $label, string $desc = '', bool $required = false, string $placeholder = '' ): static { $req = $required ? 'required' : ''; $ph = $placeholder !== '' ? 'placeholder="' . esc_attr($placeholder) . '"' : ''; $ctrl = ''; return $this->row($id, $label, $ctrl, $desc); } public function number( string $id, string $label, string $desc = '', bool $required = false, string|int $value = '', ?int $min = null, ?int $max = null ): static { $req = $required ? 'required' : ''; $min_attr = $min !== null ? 'min="' . $min . '"' : ''; $max_attr = $max !== null ? 'max="' . $max . '"' : ''; $ctrl = ''; return $this->row($id, $label, $ctrl, $desc); } public function date( string $id, string $label, string $desc = '', bool $required = false, string $value = '' ): static { $req = $required ? 'required' : ''; $ctrl = ''; return $this->row($id, $label, $ctrl, $desc); } public function time( string $id, string $label, string $desc = '', bool $required = false, string $value = '' ): static { $req = $required ? 'required' : ''; $ctrl = ''; return $this->row($id, $label, $ctrl, $desc); } public function checkbox( string $id, string $label, string $desc = '', bool $checked = false ): static { $c = $checked ? 'checked' : ''; $ctrl = ''; return $this->row($id, $label, $ctrl, $desc); } /** * @param array $options Associative: value => display text. */ public function select( string $id, string $label, array $options, string $desc = '', bool $required = false, string $selected = '' ): static { $req = $required ? 'required' : ''; $opts = $this->build_options($options, $selected); $ctrl = ''; return $this->row($id, $label, $ctrl, $desc); } public function textarea( string $id, string $label, string $desc = '', bool $required = false, string $value = '', int $rows = 5 ): static { $req = $required ? 'required' : ''; $ctrl = ''; return $this->row($id, $label, $ctrl, $desc); } public function custom(string $label, callable $fn) : static { $this->rows[] = '' . '' . esc_html($label) . '' . '' . $fn() . '' . ''; return $this; } /** * Groups multiple inputs into a single row with a shared label. * * The callable receives an RsvFormGroup instance; inputs added to it * are laid out as a flex row inside the row's . * * Example: * ->group('Availability Range', fn($g) => $g * ->time('start_time', 'Start') * ->time('end_time', 'End') * ) */ public function group(string $label, callable $fn): static { $group = RsvFormGroup::create(); $fn($group); $this->rows[] = '' . '' . esc_html($label) . '' . '' . $group->render() . '' . ''; return $this; } // ------------------------------------------------------------------------- // Non-field outputs // ------------------------------------------------------------------------- /** Hidden input — no row, emitted before the table. */ public function hidden(string $id, string|int $value): static { $this->before[] = ''; return $this; } /** * element for email/text suggestions — emitted before the table. * * @param string[] $values */ public function datalist(string $id, array $values): static { $options = ''; foreach ($values as $v) { $options .= '