Skip to content

Commit 802b2b2

Browse files
committed
Add FormBuilder, FormHistory, FormPresets, AsyncForm, ConditionalForm
New features: fluent form builders, back-navigation history, preset forms (confirm/info/input), async data-loading forms, and conditional element visibility.
1 parent 71635ba commit 802b2b2

9 files changed

Lines changed: 956 additions & 0 deletions

File tree

src/imperazim/form/Form.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use pocketmine\player\Player;
88
use pocketmine\form\Form as PocketMineForm;
99
use imperazim\form\base\elements\Title;
10+
use imperazim\form\history\FormHistory;
1011

1112
/**
1213
* Abstract base class for all forms.
@@ -51,6 +52,20 @@ public function sendTo(Player $player): void {
5152
$player->sendForm($this);
5253
}
5354

55+
/**
56+
* Pushes the current form to history, then sends this form.
57+
* Allows the player to go back to the previous form later.
58+
*
59+
* @param Player $player Target player
60+
* @param Form|null $currentForm The form to push to history (if null, nothing is pushed)
61+
*/
62+
public function sendWithHistory(Player $player, ?Form $currentForm = null): void {
63+
if ($currentForm !== null) {
64+
FormHistory::push($player, $currentForm);
65+
}
66+
$this->sendTo($player);
67+
}
68+
5469
/**
5570
* Serializes form for JSON.
5671
*
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace imperazim\form\async;
6+
7+
use Closure;
8+
use pocketmine\player\Player;
9+
use pocketmine\plugin\PluginBase;
10+
use pocketmine\scheduler\ClosureTask;
11+
use imperazim\form\FormResult;
12+
use imperazim\form\custom\DynamicCustomForm;
13+
use imperazim\form\custom\elements\Dropdown;
14+
use imperazim\form\custom\elements\Label;
15+
use imperazim\form\long\DynamicLongForm;
16+
17+
/**
18+
* Populates form elements (dropdowns, buttons) via async callbacks.
19+
* Shows a "Loading..." form while data is being fetched, then re-sends with data.
20+
*
21+
* Usage:
22+
* AsyncFormData::customForm($plugin, $player, "Shop", function(Closure $resolve) {
23+
* // Async operation (DB query, HTTP, etc.)
24+
* $resolve([
25+
* new Dropdown("item", "Choose item", ["Sword", "Shield", "Potion"]),
26+
* ]);
27+
* }, fn(Player $p, $response) => ...);
28+
*
29+
* AsyncFormData::longForm($plugin, $player, "Warps", "Select warp:", function(Closure $resolve) {
30+
* // Simulated async
31+
* $resolve(["Spawn" => null, "PvP" => null, "Shop" => null]); // name => onClick (null = no-op)
32+
* }, fn(Player $p, string $buttonText) => ...);
33+
*/
34+
final class AsyncFormData {
35+
36+
/**
37+
* Creates a custom form with async-loaded elements.
38+
*
39+
* @param PluginBase $plugin Plugin reference
40+
* @param Player $player Target player
41+
* @param string $title Form title
42+
* @param Closure $loader fn(Closure $resolve): void — call $resolve(Element[]) when data is ready
43+
* @param Closure $onSubmit fn(Player, CustomResponse): FormResult
44+
* @param Closure|null $onClose fn(Player): FormResult
45+
* @param string $loadingMessage Message shown while loading
46+
*/
47+
public static function customForm(
48+
PluginBase $plugin,
49+
Player $player,
50+
string $title,
51+
Closure $loader,
52+
Closure $onSubmit,
53+
?Closure $onClose = null,
54+
string $loadingMessage = "§7Loading..."
55+
): void {
56+
// Show loading form
57+
DynamicCustomForm::build($title, function (DynamicCustomForm $form) use ($loadingMessage) {
58+
$form->addElement(new Label($loadingMessage));
59+
})->sendTo($player);
60+
61+
// Invoke loader with resolve callback
62+
$loader(function (array $elements) use ($plugin, $player, $title, $onSubmit, $onClose) {
63+
// Re-send form with loaded elements (must run on main thread)
64+
$plugin->getScheduler()->scheduleDelayedTask(
65+
new ClosureTask(function () use ($player, $title, $elements, $onSubmit, $onClose) {
66+
if (!$player->isConnected()) return;
67+
68+
$form = DynamicCustomForm::create($title);
69+
foreach ($elements as $element) {
70+
$form->addElement($element);
71+
}
72+
$form->setOnSubmit($onSubmit);
73+
if ($onClose !== null) {
74+
$form->setOnClose($onClose);
75+
}
76+
$form->sendTo($player);
77+
}),
78+
1 // Next tick
79+
);
80+
});
81+
}
82+
83+
/**
84+
* Creates a long form (button list) with async-loaded buttons.
85+
*
86+
* @param PluginBase $plugin Plugin reference
87+
* @param Player $player Target player
88+
* @param string $title Form title
89+
* @param string $content Body text
90+
* @param Closure $loader fn(Closure $resolve): void — call $resolve(array<string, ?Closure>) with buttonText => onClick
91+
* @param Closure $defaultHandler fn(Player, string $buttonText): FormResult — used when button onClick is null
92+
* @param string $loadingMessage Message shown while loading
93+
*/
94+
public static function longForm(
95+
PluginBase $plugin,
96+
Player $player,
97+
string $title,
98+
string $content,
99+
Closure $loader,
100+
Closure $defaultHandler,
101+
string $loadingMessage = "§7Loading..."
102+
): void {
103+
// Show loading form
104+
DynamicLongForm::build($title, function (DynamicLongForm $form) use ($loadingMessage) {
105+
$form->setContent($loadingMessage);
106+
})->sendTo($player);
107+
108+
// Invoke loader
109+
$loader(function (array $buttons) use ($plugin, $player, $title, $content, $defaultHandler) {
110+
$plugin->getScheduler()->scheduleDelayedTask(
111+
new ClosureTask(function () use ($player, $title, $content, $buttons, $defaultHandler) {
112+
if (!$player->isConnected()) return;
113+
114+
$form = DynamicLongForm::create($title);
115+
$form->setContent($content);
116+
117+
foreach ($buttons as $text => $onClick) {
118+
$buttonText = (string) $text;
119+
$form->addButton($buttonText, null, function (Player $p) use ($onClick, $defaultHandler, $buttonText) {
120+
if ($onClick !== null) {
121+
return $onClick($p);
122+
}
123+
return $defaultHandler($p, $buttonText);
124+
});
125+
}
126+
127+
$form->sendTo($player);
128+
}),
129+
1
130+
);
131+
});
132+
}
133+
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace imperazim\form\builder;
6+
7+
use Closure;
8+
use pocketmine\player\Player;
9+
use imperazim\form\FormResult;
10+
use imperazim\form\custom\DynamicCustomForm;
11+
use imperazim\form\custom\elements\Input;
12+
use imperazim\form\custom\elements\Toggle;
13+
use imperazim\form\custom\elements\Slider;
14+
use imperazim\form\custom\elements\Dropdown;
15+
use imperazim\form\custom\elements\Label;
16+
use imperazim\form\custom\elements\StepSlider;
17+
18+
/**
19+
* Fluent builder for custom forms.
20+
*
21+
* Usage:
22+
* FormBuilder::custom("Settings")
23+
* ->input("name", "Player Name", placeholder: "Enter name")
24+
* ->toggle("pvp", "Enable PvP", default: true)
25+
* ->slider("volume", "Volume", 0, 100, 1, 50)
26+
* ->dropdown("mode", "Game Mode", ["Survival", "Creative"])
27+
* ->onSubmit(fn(Player $p, $r) => FormResult::CLOSE)
28+
* ->send($player);
29+
*/
30+
final class CustomFormBuilder {
31+
private DynamicCustomForm $form;
32+
33+
public function __construct(string $title) {
34+
$this->form = DynamicCustomForm::create($title);
35+
}
36+
37+
/**
38+
* Adds a text input field.
39+
*
40+
* @param string $id Element identifier
41+
* @param string $label Display label
42+
* @param string $placeholder Placeholder text
43+
* @param string $default Default value
44+
* @return static
45+
*/
46+
public function input(string $id, string $label, string $placeholder = '', string $default = ''): static {
47+
$this->form->addElement(new Input($id, $label, $placeholder, $default));
48+
return $this;
49+
}
50+
51+
/**
52+
* Adds a toggle (boolean) switch.
53+
*
54+
* @param string $id Element identifier
55+
* @param string $text Display text
56+
* @param bool $default Default state
57+
* @return static
58+
*/
59+
public function toggle(string $id, string $text, bool $default = false): static {
60+
$this->form->addElement(new Toggle($id, $text, $default));
61+
return $this;
62+
}
63+
64+
/**
65+
* Adds a numeric slider.
66+
*
67+
* @param string $id Element identifier
68+
* @param string $text Display text
69+
* @param float $min Minimum value
70+
* @param float $max Maximum value
71+
* @param float $step Step increment
72+
* @param float $default Default value
73+
* @return static
74+
*/
75+
public function slider(string $id, string $text, float $min, float $max, float $step = 1.0, float $default = 0.0): static {
76+
$this->form->addElement(new Slider($id, $text, $min, $max, $step, $default));
77+
return $this;
78+
}
79+
80+
/**
81+
* Adds a dropdown selector.
82+
*
83+
* @param string $id Element identifier
84+
* @param string $text Display text
85+
* @param array $options Options list
86+
* @param string|int $default Default selection
87+
* @return static
88+
*/
89+
public function dropdown(string $id, string $text, array $options, string|int $default = 0): static {
90+
$this->form->addElement(new Dropdown($id, $text, $options, $default));
91+
return $this;
92+
}
93+
94+
/**
95+
* Adds a step slider.
96+
*
97+
* @param string $id Element identifier
98+
* @param string $text Display text
99+
* @param array $steps Step options
100+
* @param string|int $default Default step
101+
* @return static
102+
*/
103+
public function stepSlider(string $id, string $text, array $steps, string|int $default = 0): static {
104+
$this->form->addElement(new StepSlider($id, $text, $steps, $default));
105+
return $this;
106+
}
107+
108+
/**
109+
* Adds a text label.
110+
*
111+
* @param string $text Label text
112+
* @return static
113+
*/
114+
public function label(string $text): static {
115+
$this->form->addElement(new Label($text));
116+
return $this;
117+
}
118+
119+
/**
120+
* Sets the submit handler.
121+
*
122+
* @param Closure $handler fn(Player $player, CustomResponse $response): FormResult
123+
* @return static
124+
*/
125+
public function onSubmit(Closure $handler): static {
126+
$this->form->setOnSubmit($handler);
127+
return $this;
128+
}
129+
130+
/**
131+
* Sets the close handler.
132+
*
133+
* @param Closure $handler fn(Player $player): FormResult
134+
* @return static
135+
*/
136+
public function onClose(Closure $handler): static {
137+
$this->form->setOnClose($handler);
138+
return $this;
139+
}
140+
141+
/**
142+
* Builds and returns the underlying DynamicCustomForm.
143+
*
144+
* @return DynamicCustomForm
145+
*/
146+
public function build(): DynamicCustomForm {
147+
return $this->form;
148+
}
149+
150+
/**
151+
* Sends the form to the player.
152+
*
153+
* @param Player $player Target player
154+
*/
155+
public function send(Player $player): void {
156+
$this->form->sendTo($player);
157+
}
158+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace imperazim\form\builder;
6+
7+
/**
8+
* Fluent form builder entry point.
9+
*
10+
* Usage:
11+
* FormBuilder::custom("Config")->input("name", "Name")->toggle("vip", "VIP?")->onSubmit(fn($p, $r) => ...)->send($player);
12+
* FormBuilder::long("Menu")->content("Choose:")->button("Play", fn($p) => ...)->send($player);
13+
* FormBuilder::modal("Confirm")->content("Sure?")->accept("Yes", fn($p, $r) => ...)->deny("No", fn($p, $r) => ...)->send($player);
14+
*/
15+
final class FormBuilder {
16+
17+
/**
18+
* Creates a custom form builder.
19+
*
20+
* @param string $title Form title
21+
* @return CustomFormBuilder
22+
*/
23+
public static function custom(string $title): CustomFormBuilder {
24+
return new CustomFormBuilder($title);
25+
}
26+
27+
/**
28+
* Creates a long form (button list) builder.
29+
*
30+
* @param string $title Form title
31+
* @return LongFormBuilder
32+
*/
33+
public static function long(string $title): LongFormBuilder {
34+
return new LongFormBuilder($title);
35+
}
36+
37+
/**
38+
* Creates a modal form (yes/no dialog) builder.
39+
*
40+
* @param string $title Form title
41+
* @return ModalFormBuilder
42+
*/
43+
public static function modal(string $title): ModalFormBuilder {
44+
return new ModalFormBuilder($title);
45+
}
46+
}

0 commit comments

Comments
 (0)