Skip to content

Commit 1365c2c

Browse files
committed
Merge branch 'main' into feat/form-layout-components
2 parents d5f6804 + 5a8c826 commit 1365c2c

File tree

8 files changed

+275
-11
lines changed

8 files changed

+275
-11
lines changed

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,31 @@
22

33
All notable changes to `fields` will be documented in this file.
44

5+
## v0.8.0 (Conditional fields) - 2025-09-26
6+
7+
### What's Changed
8+
9+
* fix: handling filament v4 RichEditor data structure by @Baspa in https://github.com/backstagephp/fields/pull/25
10+
* fix: disable array casting by @arduinomaster22 in https://github.com/backstagephp/fields/pull/26
11+
* fix: rich editor form fill issues by @Baspa in https://github.com/backstagephp/fields/pull/27
12+
* feat: auto assign workflow by @Baspa in https://github.com/backstagephp/fields/pull/28
13+
* feat: remove content cleaning by @Baspa in https://github.com/backstagephp/fields/pull/29
14+
* feat: remove live and set json to true by @Baspa in https://github.com/backstagephp/fields/pull/30
15+
* fix: disable indenting and drag n drop by @Baspa in https://github.com/backstagephp/fields/pull/31
16+
* Bump actions/github-script from 7 to 8 by @dependabot[bot] in https://github.com/backstagephp/fields/pull/32
17+
* fix: rich editor state by correcting return by @Baspa in https://github.com/backstagephp/fields/pull/33
18+
* build(deps-dev): bump form-data from 4.0.3 to 4.0.4 by @dependabot[bot] in https://github.com/backstagephp/fields/pull/35
19+
* build(deps-dev): bump esbuild from 0.19.12 to 0.25.0 by @dependabot[bot] in https://github.com/backstagephp/fields/pull/36
20+
* feat: rich editor jump anchor plugin by @Baspa in https://github.com/backstagephp/fields/pull/37
21+
* feat: let user change the relationKey by @Baspa in https://github.com/backstagephp/fields/pull/38
22+
* feat: validation and conditional fields by @Baspa in https://github.com/backstagephp/fields/pull/19
23+
24+
### New Contributors
25+
26+
* @arduinomaster22 made their first contribution in https://github.com/backstagephp/fields/pull/26
27+
28+
**Full Changelog**: https://github.com/backstagephp/fields/compare/v0.7.0...v0.8.0
29+
530
## v0.7.0 (Filament v4) - 2025-08-15
631

732
### What's Changed

composer.json

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"php": "^8.2",
2424
"baspa/laravel-timezones": "^1.2",
2525
"filament/filament": "^4.0",
26-
"saade/filament-adjacency-list": "dev-release/filament-v4",
26+
"saade/filament-adjacency-list": "^4.0",
2727
"spatie/laravel-package-tools": "^1.15.0",
2828
"staudenmeir/laravel-adjacency-list": "^1.0"
2929
},
@@ -75,10 +75,6 @@
7575
}
7676
},
7777
"repositories": {
78-
"saade/filament-adjacency-list": {
79-
"type": "git",
80-
"url": "https://github.com/backstagephp/filament-adjacency-list.git"
81-
}
8278
},
8379
"minimum-stability": "beta",
8480
"prefer-stable": true

src/Concerns/CanMapDynamicFields.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
namespace Backstage\Fields\Concerns;
44

55
use Backstage\Fields\Contracts\FieldInspector;
6-
use Backstage\Fields\Enums\Field;
76
use Backstage\Fields\Fields;
87
use Backstage\Fields\Fields\Checkbox;
98
use Backstage\Fields\Fields\CheckboxList;
109
use Backstage\Fields\Fields\Color;
1110
use Backstage\Fields\Fields\DateTime;
11+
use Backstage\Fields\Fields\FileUpload;
1212
use Backstage\Fields\Fields\KeyValue;
1313
use Backstage\Fields\Fields\MarkdownEditor;
1414
use Backstage\Fields\Fields\Radio;
@@ -47,6 +47,7 @@ trait CanMapDynamicFields
4747
'select' => Select::class,
4848
'checkbox' => Checkbox::class,
4949
'checkbox-list' => CheckboxList::class,
50+
'file-upload' => FileUpload::class,
5051
'key-value' => KeyValue::class,
5152
'radio' => Radio::class,
5253
'toggle' => Toggle::class,
@@ -343,6 +344,10 @@ protected function getFieldsFromBlocks(array $blocks): Collection
343344

344345
collect($blocks)->map(function ($block) use (&$processedFields) {
345346
foreach ($block as $key => $values) {
347+
if (! is_array($values) || ! isset($values['data'])) {
348+
continue;
349+
}
350+
346351
$fields = $values['data'];
347352
$fields = ModelsField::whereIn('ulid', array_keys($fields))->get();
348353

src/Enums/Field.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ enum Field: string
1212
case CheckboxList = 'checkbox-list';
1313
case Color = 'color';
1414
case DateTime = 'date-time';
15-
// case File = 'file-upload';
15+
case File = 'file-upload';
1616
// case Hidden = 'hidden';
1717
case KeyValue = 'key-value';
1818
// case Link = 'link';

src/Fields/FileUpload.php

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
<?php
2+
3+
namespace Backstage\Fields\Fields;
4+
5+
use Backstage\Fields\Contracts\FieldContract;
6+
use Backstage\Fields\Models\Field;
7+
use Filament\Forms\Components\FileUpload as FilamentFileUpload;
8+
use Filament\Forms\Components\Select;
9+
use Filament\Forms\Components\TextInput;
10+
use Filament\Forms\Components\Toggle;
11+
use Filament\Schemas\Components\Grid;
12+
use Filament\Schemas\Components\Tabs;
13+
use Filament\Schemas\Components\Tabs\Tab;
14+
use Filament\Schemas\Components\Utilities\Get;
15+
use Illuminate\Database\Eloquent\Model;
16+
17+
class FileUpload extends Base implements FieldContract
18+
{
19+
public static function getDefaultConfig(): array
20+
{
21+
return [
22+
...parent::getDefaultConfig(),
23+
'disk' => 'public',
24+
'directory' => 'uploads',
25+
'visibility' => 'public',
26+
'acceptedFileTypes' => null,
27+
'maxSize' => null,
28+
'maxFiles' => 1,
29+
'multiple' => false,
30+
'appendFiles' => false,
31+
'reorderable' => false,
32+
'openable' => true,
33+
'downloadable' => true,
34+
'previewable' => true,
35+
'deletable' => true,
36+
];
37+
}
38+
39+
public static function make(string $name, ?Field $field = null): FilamentFileUpload
40+
{
41+
$config = array_merge(self::getDefaultConfig(), $field->config ?? []);
42+
43+
$component = FilamentFileUpload::make($name)
44+
->label($field->name ?? null)
45+
->disk($config['disk'])
46+
->directory($config['directory'])
47+
->visibility($config['visibility'])
48+
->maxFiles($config['maxFiles'])
49+
->multiple($config['multiple'])
50+
->appendFiles($config['appendFiles'])
51+
->reorderable($config['reorderable'])
52+
->openable($config['openable'])
53+
->downloadable($config['downloadable'])
54+
->previewable($config['previewable'])
55+
->deletable($config['deletable']);
56+
57+
if ($config['acceptedFileTypes']) {
58+
$component->acceptedFileTypes(explode(',', $config['acceptedFileTypes']));
59+
}
60+
61+
if ($config['maxSize']) {
62+
$component->maxSize($config['maxSize']);
63+
}
64+
65+
return self::applyDefaultSettings($component, $field);
66+
}
67+
68+
public static function mutateFormDataCallback(Model $record, Field $field, array $data): array
69+
{
70+
if (! property_exists($record, 'valueColumn') || ! isset($record->values[$field->ulid])) {
71+
return $data;
72+
}
73+
74+
$data[$record->valueColumn][$field->ulid] = self::decodeFileValueForForm($record->values[$field->ulid]);
75+
76+
return $data;
77+
}
78+
79+
public static function mutateBeforeSaveCallback(Model $record, Field $field, array $data): array
80+
{
81+
if (! property_exists($record, 'valueColumn') || ! isset($data[$record->valueColumn][$field->ulid])) {
82+
return $data;
83+
}
84+
85+
$data[$record->valueColumn][$field->ulid] = self::normalizeFileValue($data[$record->valueColumn][$field->ulid]);
86+
87+
return $data;
88+
}
89+
90+
private static function decodeFileValueForForm(mixed $value): array
91+
{
92+
if (is_null($value) || $value === '') {
93+
return [];
94+
}
95+
96+
if (is_array($value)) {
97+
return $value;
98+
}
99+
100+
if (is_string($value) && json_validate($value)) {
101+
$decoded = json_decode($value, true);
102+
103+
return is_array($decoded) ? $decoded : [];
104+
}
105+
106+
if (is_string($value) && ! empty($value)) {
107+
return [$value];
108+
}
109+
110+
if (! empty($value)) {
111+
return [(string) $value];
112+
}
113+
114+
return [];
115+
}
116+
117+
private static function normalizeFileValue(mixed $value): ?string
118+
{
119+
if (is_null($value) || $value === '') {
120+
return null;
121+
}
122+
123+
if (is_array($value)) {
124+
return json_encode($value);
125+
}
126+
127+
if (is_string($value) && json_validate($value)) {
128+
return $value;
129+
}
130+
131+
if (is_string($value) && ! empty($value)) {
132+
return json_encode([$value]);
133+
}
134+
135+
if (! empty($value)) {
136+
return json_encode([(string) $value]);
137+
}
138+
139+
return null;
140+
}
141+
142+
public function getForm(): array
143+
{
144+
return [
145+
Tabs::make()
146+
->schema([
147+
Tab::make('General')
148+
->label(__('General'))
149+
->schema([
150+
...parent::getForm(),
151+
]),
152+
Tab::make('Field specific')
153+
->label(__('Field specific'))
154+
->schema([
155+
Grid::make(2)
156+
->schema([
157+
TextInput::make('config.disk')
158+
->label(__('Storage Disk'))
159+
->default('public')
160+
->required(),
161+
162+
TextInput::make('config.directory')
163+
->label(__('Upload Directory'))
164+
->default('uploads')
165+
->required(),
166+
167+
Select::make('config.visibility')
168+
->label(__('File Visibility'))
169+
->options([
170+
'public' => __('Public'),
171+
'private' => __('Private'),
172+
])
173+
->default('public')
174+
->required(),
175+
176+
TextInput::make('config.acceptedFileTypes')
177+
->label(__('Accepted File Types'))
178+
->placeholder('image/*,application/pdf')
179+
->helperText(__('Comma-separated list of MIME types or file extensions')),
180+
181+
TextInput::make('config.maxSize')
182+
->label(__('Max File Size (KB)'))
183+
->numeric()
184+
->minValue(1),
185+
186+
TextInput::make('config.maxFiles')
187+
->label(__('Max Files'))
188+
->numeric()
189+
->minValue(1)
190+
->default(1)
191+
->required(),
192+
]),
193+
194+
Grid::make(2)
195+
->schema([
196+
Toggle::make('config.multiple')
197+
->label(__('Multiple Files'))
198+
->helperText(__('Allow multiple file selection'))
199+
->live(),
200+
201+
Toggle::make('config.appendFiles')
202+
->label(__('Append Files'))
203+
->helperText(__('Append new files to existing ones'))
204+
->visible(fn (Get $get): bool => $get('config.multiple')),
205+
206+
Toggle::make('config.reorderable')
207+
->label(__('Reorderable'))
208+
->helperText(__('Allow reordering of files'))
209+
->visible(fn (Get $get): bool => $get('config.multiple')),
210+
211+
Toggle::make('config.openable')
212+
->label(__('Openable'))
213+
->helperText(__('Allow opening files in new tab')),
214+
215+
Toggle::make('config.downloadable')
216+
->label(__('Downloadable'))
217+
->helperText(__('Allow downloading files')),
218+
219+
Toggle::make('config.previewable')
220+
->label(__('Previewable'))
221+
->helperText(__('Allow previewing files')),
222+
223+
Toggle::make('config.deletable')
224+
->label(__('Deletable'))
225+
->helperText(__('Allow deleting files')),
226+
]),
227+
]),
228+
])->columnSpanFull(),
229+
];
230+
}
231+
}

src/Fields/Repeater.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ public function getForm(): array
157157
->disabled(fn (string $operation) => $operation === 'create')
158158
->hint(fn (string $operation) => $operation === 'create' ? __('Fields can be added once the field is created.') : '')
159159
->hintColor('primary')
160-
->form([
160+
->schema([
161161
Section::make('Field')
162162
->columns(3)
163163
->schema([

src/Fields/Select.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public static function mutateBeforeSaveCallback(Model $record, Field $field, arr
144144
* Normalize the select value to an array or a single value. This is needed because the select field can be
145145
* changed from single to multiple or vice versa.
146146
*/
147-
private static function normalizeSelectValue($value, Field $field): mixed
147+
protected static function normalizeSelectValue($value, Field $field): mixed
148148
{
149149
$isMultiple = $field->config['multiple'] ?? false;
150150

src/Filament/RelationManagers/FieldsRelationManager.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,19 @@ public function form(Schema $schema): Schema
106106
})
107107
->searchable()
108108
->preload()
109-
->options(function () {
110-
return Field::pluck('group')
109+
->options(function (Get $get) {
110+
$existingGroups = Field::pluck('group')
111111
->filter()
112112
->unique()
113113
->mapWithKeys(fn ($group) => [$group => $group])
114114
->toArray();
115+
116+
$currentValue = $get('group');
117+
if ($currentValue && ! array_key_exists($currentValue, $existingGroups)) {
118+
$existingGroups[$currentValue] = $currentValue;
119+
}
120+
121+
return $existingGroups;
115122
}),
116123

117124
Select::make('schema_id')

0 commit comments

Comments
 (0)