Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
78bf986
wip
duncanmcclean Apr 14, 2026
d38b23d
wip
duncanmcclean Apr 14, 2026
00c98db
Merge branch 'forms-2' into form-fields
duncanmcclean Apr 14, 2026
160dabe
wip
duncanmcclean Apr 14, 2026
292ce2a
wip
duncanmcclean Apr 14, 2026
30f2f26
wip
duncanmcclean Apr 14, 2026
4e0919a
wip
duncanmcclean Apr 14, 2026
d16e298
Merge branch 'forms-2' into form-fields
duncanmcclean Apr 14, 2026
dcc888c
blueprint -> form field conversion
duncanmcclean Apr 15, 2026
35c1bba
remove `BlueprintUndefinedException`
duncanmcclean Apr 15, 2026
5ca1ae4
remove `$blueprint` property
duncanmcclean Apr 15, 2026
6b45c1c
formatting
duncanmcclean Apr 15, 2026
36cdacf
add method to make form fieldtypes selectable/unselectable
duncanmcclean Apr 15, 2026
17e4b5c
add form fieldtype repo tests
duncanmcclean Apr 15, 2026
ef41de5
deprecate form selectable methods in `FieldtypeRepository`
duncanmcclean Apr 15, 2026
a70381b
formatting
duncanmcclean Apr 15, 2026
dbb809e
Merge branch 'forms-2' into form-fields
duncanmcclean Apr 15, 2026
0b49d41
Merge branch 'forms-2' into form-fields
duncanmcclean Apr 15, 2026
4fc3fdc
short and long answer tests
duncanmcclean Apr 15, 2026
92ea72e
support fieldsets
duncanmcclean Apr 15, 2026
df34391
wip
duncanmcclean Apr 15, 2026
03e9ff8
handle validation strings
duncanmcclean Apr 16, 2026
c8830a9
imports
duncanmcclean Apr 16, 2026
eef7df7
delete form blueprints
duncanmcclean Apr 16, 2026
5c3dcf9
Merge branch 'forms-2' into form-fields
duncanmcclean Apr 16, 2026
847b5e5
formatting
duncanmcclean Apr 16, 2026
2f5f813
update tests
duncanmcclean Apr 16, 2026
4ed2b78
remove `$selectableInForms` property from core fieldtypes
duncanmcclean Apr 16, 2026
4a46b02
wip
duncanmcclean Apr 16, 2026
ecc80a1
test with real fieldtype handles
duncanmcclean Apr 16, 2026
3ed8b07
Merge branch 'forms-2' into form-fields
duncanmcclean Apr 17, 2026
3ff1853
wip
duncanmcclean Apr 17, 2026
afa91c4
cache form blueprint to avoid unncessary work
duncanmcclean Apr 17, 2026
ba8db4c
add back the `$selectableInForms` property for now
duncanmcclean Apr 17, 2026
9ee76ba
make `FormFieldtype` arrayable
duncanmcclean Apr 17, 2026
a10880c
revert previous selectable changes
duncanmcclean Apr 17, 2026
70d6ee3
handle `$selectableInForms` a different way
duncanmcclean Apr 17, 2026
e52f3aa
wip
duncanmcclean Apr 17, 2026
5b4e245
toodeloo
duncanmcclean Apr 17, 2026
5373c83
Merge branch 'forms-2' into form-fields
duncanmcclean Apr 20, 2026
ba265e1
Merge remote-tracking branch 'origin/forms-2' into form-fields
duncanmcclean Apr 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions resources/js/components/blueprints/Builder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export default {
initialBlueprint: Object,
showTitle: Boolean,
useTabs: { type: Boolean, default: true },
isFormBlueprint: { type: Boolean, default: false },
},

data() {
Expand Down Expand Up @@ -84,15 +83,9 @@ export default {
this.$events.$on('root-form-save', () => {
this.$nextTick(() => this.save());
});

if (this.isFormBlueprint) {
Statamic.$config.set('isFormBlueprint', true);
}
},

beforeUnmount() {
Statamic.$config.set('isFormBlueprint', false);

this.$events.$off('root-form-save');

this.saveKeyBinding.destroy();
Expand Down
6 changes: 1 addition & 5 deletions resources/js/components/fields/FieldtypeSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,7 @@ export default {
created() {
if (this.fieldtypesLoaded) return;

let url = cp_url('fields/fieldtypes?selectable=true');

if (this.$config.get('isFormBlueprint')) url += '&forms=true';

this.$axios.get(url)
this.$axios.get(cp_url('fields/fieldtypes?selectable=true'))
.then((response) => (loadedFieldtypes.value = response.data))
.catch((e) => {
this.$toast.error(e.response?.data?.message || __('Something went wrong'));
Expand Down
2 changes: 0 additions & 2 deletions resources/js/pages/blueprints/Edit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ defineProps({
canDefineLocalizable: { type: Boolean, default: undefined },
resetRoute: String,
isResettable: Boolean,
isFormBlueprint: Boolean,
});
</script>

Expand All @@ -24,7 +23,6 @@ defineProps({
:initial-blueprint="blueprint"
:use-tabs="useTabs"
:can-define-localizable="canDefineLocalizable"
:is-form-blueprint="isFormBlueprint"
>
<template v-if="isResettable" #actions>
<ui-dropdown>
Expand Down
25 changes: 1 addition & 24 deletions resources/js/pages/blueprints/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Link } from '@inertiajs/vue3';
import Head from '@/pages/layout/Head.vue';
import { Header, Dropdown, DropdownMenu, DropdownLabel, DropdownItem, Button, Subheading, Panel, DocsCallout, Icon, StatusIndicator } from '@ui';

defineProps(['collections', 'taxonomies', 'navs', 'assetContainers', 'globals', 'forms', 'userBlueprint', 'groupBlueprint', 'additional']);
defineProps(['collections', 'taxonomies', 'navs', 'assetContainers', 'globals', 'userBlueprint', 'groupBlueprint', 'additional']);

const resetters = ref({});
</script>
Expand Down Expand Up @@ -179,29 +179,6 @@ const resetters = ref({});
</Panel>
</template>

<template v-if="forms.length">
<Subheading size="lg" class="mb-2" :text="__('Forms')" />
<Panel>
<table class="data-table">
<thead>
<tr>
<th class="text-start!">{{ __('Blueprint') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="form in forms" :key="form.handle">
<td>
<div class="flex items-center gap-2">
<Icon name="forms" class="text-gray-500 me-1" />
<Link :href="form.edit_url" v-text="__(form.title)" />
</div>
</td>
</tr>
</tbody>
</table>
</Panel>
</template>

<Subheading size="lg" class="mb-2" :text="__('Users')" />
<Panel>
<table class="data-table">
Expand Down
10 changes: 10 additions & 0 deletions resources/js/pages/forms/Connect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defineOptions({ layout: [Layout, FormsLayout] });

const props = defineProps({
form: Object,
fieldtypes: Array,
});

const formTitle = computed(() => props.form?.title || __('Untitled Form'));
Expand All @@ -33,4 +34,13 @@ const formTitle = computed(() => props.form?.title || __('Untitled Form'));
<Card class="min-h-32"></Card>
</Panel>
</div>

<div class="py-4">
<!-- TOOD: Remove from this component when wiring up the form builder. -->
<ul>
<li v-for="fieldtype in fieldtypes" :key="fieldtype.handle">
{{ fieldtype.title }}
</li>
</ul>
</div>
</template>
6 changes: 0 additions & 6 deletions resources/js/pages/forms/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,6 @@ const reloadPage = () => router.reload();
</template>
<template #prepended-row-actions="{ row: form }">
<DropdownItem v-if="form.can_edit" :text="__('Configure')" :href="form.edit_url" icon="cog" />
<DropdownItem
v-if="form.can_edit_blueprint"
icon="blueprint-edit"
:text="__('Edit Blueprint')"
:href="form.blueprint_url"
/>
</template>
</Listing>

Expand Down
13 changes: 0 additions & 13 deletions resources/js/pages/forms/Show.vue
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,6 @@ function exportSubmissions() {
<Dropdown v-if="form.canEdit || form.canDelete" placement="left-start" class="me-2">
<DropdownMenu>
<DropdownItem v-if="form.canEdit" :text="__('Configure Form')" icon="cog" :href="form.editUrl" />
<DropdownItem
v-if="form.canConfigureFields"
:text="__('Edit Blueprint')"
icon="blueprint-edit"
:href="form.blueprintUrl"
/>
<DropdownItem
v-if="form.canDelete"
:text="__('Delete Form')"
Expand All @@ -146,13 +140,6 @@ function exportSubmissions() {
:url="form.editUrl"
/>

<CommandPaletteItem
category="Actions"
:text="__('Edit Blueprint')"
icon="blueprint-edit"
:url="form.blueprintUrl"
/>

<CommandPaletteItem
category="Actions"
:text="__('Delete Form')"
Expand Down
4 changes: 2 additions & 2 deletions routes/cp.php
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,8 @@
Route::get('asset-containers/{asset_container}/edit', [AssetContainerBlueprintController::class, 'edit'])->name('asset-containers.edit');
Route::patch('asset-containers/{asset_container}', [AssetContainerBlueprintController::class, 'update'])->name('asset-containers.update');

Route::get('forms/{form}/edit', [FormBlueprintController::class, 'edit'])->name('forms.edit');
Route::patch('forms/{form}', [FormBlueprintController::class, 'update'])->name('forms.update');
Route::get('forms/{form}/edit', FormBlueprintController::class)->name('forms.edit');
Route::patch('forms/{form}', FormBlueprintController::class)->name('forms.update');

Route::get('globals/{global_set}/edit', [GlobalsBlueprintController::class, 'edit'])->name('globals.edit');
Route::patch('globals/{global_set}', [GlobalsBlueprintController::class, 'update'])->name('globals.update');
Expand Down
4 changes: 0 additions & 4 deletions src/CommandPalette/Palette.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,6 @@ protected function buildFields(): self
->map(fn ($set) => $set->blueprintCommandPaletteLink())
->each(fn (Link $link) => $this->addCommand($link));

Facades\Form::all()
->map(fn ($form) => $form->blueprintCommandPaletteLink())
->each(fn (Link $link) => $this->addCommand($link));

$this->addCommand(Facades\User::blueprintCommandPaletteLink());
$this->addCommand(Facades\UserGroup::blueprintCommandPaletteLink());

Expand Down
13 changes: 13 additions & 0 deletions src/Exceptions/FormFieldtypeNotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Statamic\Exceptions;

use Exception;

class FormFieldtypeNotFoundException extends Exception
{
public function __construct($fieldtype)
{
parent::__construct("Form Fieldtype [{$fieldtype}] not found");
}
}
12 changes: 12 additions & 0 deletions src/Fields/Fieldtype.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ abstract class Fieldtype implements Arrayable
protected $validatable = true;
protected $defaultable = true;
protected $selectable = true;
/**
* @deprecated Register a FormFieldtype instead.
*/
protected $selectableInForms = false;
protected $relationship = false;
protected $categories = [];
Expand Down Expand Up @@ -101,6 +104,9 @@ public function selectable(): bool
return $this->selectable;
}

/**
* @deprecated Use FormFieldtype::isSelectable() instead.
*/
public function selectableInForms(): bool
{
if (FieldtypeRepository::selectableInFormIsOverriden($this->handle())) {
Expand All @@ -110,11 +116,17 @@ public function selectableInForms(): bool
return $this->selectableInForms;
}

/**
* @deprecated Use FormFieldtype::makeSelectable() instead.
*/
public static function makeSelectableInForms()
{
FieldtypeRepository::makeSelectableInForms(self::handle());
}

/**
* @deprecated Use FormFieldtype::makeUnselectable() instead.
*/
public static function makeUnselectableInForms()
{
FieldtypeRepository::makeUnselectableInForms(self::handle());
Expand Down
12 changes: 12 additions & 0 deletions src/Fields/FieldtypeRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,33 @@ public function handles()
});
}

/**
* @deprecated Use FormFieldtype::makeSelectable() instead.
*/
public function makeSelectableInForms($handle)
{
$this->selectableInForms[$handle] = true;
}

/**
* @deprecated Use FormFieldtype::makeUnselectable() instead.
*/
public function makeUnselectableInForms($handle)
{
$this->selectableInForms[$handle] = false;
}

/**
* @deprecated Use FormFieldtype::isSelectable() instead.
*/
public function hasBeenMadeSelectableInForms($handle)
{
return $this->selectableInForms[$handle] ?? false;
}

/**
* @deprecated Use FormFieldtype::isSelectable() instead.
*/
public function selectableInFormIsOverriden($handle)
{
return array_key_exists($handle, $this->selectableInForms);
Expand Down
36 changes: 0 additions & 36 deletions src/Forms/Exceptions/BlueprintUndefinedException.php

This file was deleted.

34 changes: 34 additions & 0 deletions src/Forms/Fields/Email.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Statamic\Forms\Fields;

use Statamic\Support\Arr;

class Email extends FormFieldtype
{
protected static $fieldtype = 'text';
protected $icon = 'mail-sign-at';
protected $categories = ['Contact Info'];

public function configFieldItems(): array
{
return [
'placeholder' => [
'display' => __('Placeholder'),
'instructions' => __('statamic::fieldtypes.text.config.placeholder'),
'type' => 'text',
],
];
}

public function toFieldArray(): array
{
return [
'type' => 'text',
'input_type' => 'email',
'placeholder' => $this->config('placeholder'),
'validate' => [...((array) $this->config('validate', [])), 'email'],
...Arr::except($this->config(), ['type', 'input_type', 'placeholder', 'validate']),
];
}
}
38 changes: 38 additions & 0 deletions src/Forms/Fields/Fallback.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Statamic\Forms\Fields;

use Statamic\Fields\Fieldtype;

class Fallback extends FormFieldtype
{
protected ?Fieldtype $wrappedFieldtype = null;

public function wrapping(Fieldtype $fieldtype): static
{
$this->wrappedFieldtype = $fieldtype;

return $this;
}

public function toFieldArray(): array
{
return $this->config();
}

public function toArray(): array
{
if (! $this->wrappedFieldtype) {
return parent::toArray();
}

return [
'handle' => $this->wrappedFieldtype->handle(),
'title' => $this->wrappedFieldtype->title(),
'categories' => $this->wrappedFieldtype->categories(),
'keywords' => $this->wrappedFieldtype->keywords(),
'icon' => $this->wrappedFieldtype->icon(),
'config' => $this->wrappedFieldtype->configFields()->toPublishArray(),
];
}
}
Loading
Loading