Skip to content

Commit 91720fb

Browse files
committed
Merge branch 'main' of github.com:backstagephp/fields into feat/form-layout-components
2 parents bcd799f + d97885f commit 91720fb

39 files changed

+2460
-201
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
build
66
composer.lock
77
coverage
8-
docs
98
node_modules
109
phpunit.xml
1110
phpstan.neon

README.md

Lines changed: 86 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,31 @@ This package aims to help you add dynamic, configurable fields to your Filament
1515

1616
## Features
1717

18-
- 🎯 **Easy Integration**: Seamlessly integrates with your Filament resources
19-
- 🔧 **Configurable Fields**: Add and manage custom fields for your models
20-
- 🎨 **Built-in Field Types**: Includes common Filament form fields like:
21-
- Text
22-
- Textarea
23-
- Rich Text Editor
24-
- Select
25-
- Checkbox
26-
- Checkbox List
27-
- Key-Value
28-
- Radio
29-
- Toggle
30-
- Color Picker
31-
- DateTime
32-
- Tags
33-
-**Extensible**: Create your own custom field types
34-
- 🔄 **Data Mutation**: Hooks to modify field data before filling forms or saving
35-
- 🏢 **Multi-tenant Support**: Built-in support for multi-tenant applications
18+
- 🎯 **Easy Integration**: Seamlessly integrates with your Filament resources
19+
- 🔧 **Configurable Fields**: Add and manage custom fields for your models
20+
- 🎨 **Built-in Field Types**: Includes common Filament form fields like:
21+
- Text
22+
- Textarea
23+
- Rich Text Editor (with Jump Anchor plugin)
24+
- Select
25+
- Checkbox
26+
- Checkbox List
27+
- Key-Value
28+
- Radio
29+
- Toggle
30+
- Color Picker
31+
- DateTime
32+
- Tags
33+
- **Extensible**: Create your own custom field types
34+
- 🔄 **Data Mutation**: Hooks to modify field data before filling forms or saving
35+
- 🏢 **Multi-tenant Support**: Built-in support for multi-tenant applications
3636

3737
This package is perfect for scenarios where you need to:
38-
- Add dynamic custom fields to your models
39-
- Allow users to configure form fields through the admin panel
40-
- Build flexible content management systems
41-
- Create customizable settings pages
4238

39+
- Add dynamic custom fields to your models
40+
- Allow users to configure form fields through the admin panel
41+
- Build flexible content management systems
42+
- Create customizable settings pages
4343

4444
## Installation
4545

@@ -63,7 +63,7 @@ The content of the `fields.php` file is as follows:
6363
<?php
6464

6565
return [
66-
66+
6767
'tenancy' => [
6868
'is_tenant_aware' => true,
6969

@@ -137,6 +137,41 @@ class ContentResource extends Resource
137137
}
138138
```
139139

140+
### Field Configuration
141+
142+
#### Validation Rules
143+
144+
Each field supports validation rules that can be configured through the admin interface. The package includes support for all standard Laravel and Filament validation rules:
145+
146+
- **Basic Rules**: Required, nullable, filled
147+
- **String Rules**: Min/max length, alpha, alphanumeric, email, URL
148+
- **Numeric Rules**: Min/max values, integer, decimal, numeric
149+
- **Date Rules**: Date format, before/after dates, date equals
150+
- **Comparison Rules**: Same as field, different from field, greater/less than field
151+
- **Conditional Rules**: Required if/unless, prohibited if/unless, required with/without
152+
- **Pattern Rules**: Regex, starts/ends with, in/not in list
153+
- **Database Rules**: Exists, unique
154+
155+
##### Field Dependencies
156+
157+
Validation rules can depend on other fields in the form:
158+
159+
- **Field Comparison**: Compare values with other fields (`same`, `different`, `greater_than`, etc.)
160+
- **Conditional Requirements**: Make fields required based on other field values (`required_if`, `required_unless`)
161+
- **Multi-field Dependencies**: Require fields based on multiple other fields (`required_with_all`, `required_without_all`)
162+
163+
When no other fields are available for dependency rules, the field selection will be disabled and show a helpful message.
164+
165+
#### Visibility Rules
166+
167+
Control when fields are shown or hidden based on conditions:
168+
169+
- **Conditional Display**: Show/hide fields based on other field values
170+
- **Dynamic Forms**: Create adaptive forms that change based on user input
171+
- **Complex Logic**: Support for multiple conditions and logical operators
172+
173+
The visibility system works seamlessly with validation rules to create intelligent, user-friendly forms.
174+
140175
### Making a resource page configurable
141176

142177
To make a resource page configurable, you need to add the `CanMapDynamicFields` trait to your page. For this example, we'll make a `EditContent` page configurable.
@@ -146,11 +181,7 @@ To make a resource page configurable, you need to add the `CanMapDynamicFields`
146181

147182
namespace Backstage\Resources\ContentResource\Pages;
148183

149-
use Filament\Forms\Components\Grid;
150-
use Filament\Forms\Components\Tabs;
151-
use Filament\Forms\Components\Tabs\Tab;
152-
use Filament\Forms\Form;
153-
use Filament\Resources\Pages\EditRecord;
184+
// ...
154185
use Backstage\Fields\Concerns\CanMapDynamicFields;
155186

156187
class EditContent extends EditRecord
@@ -168,7 +199,7 @@ class EditContent extends EditRecord
168199
public function mutateFormDataBeforeSave(array $data): array
169200
{
170201
$this->mutateBeforeSave($data);
171-
202+
172203
return $data;
173204
}
174205
}
@@ -223,7 +254,7 @@ When using select fields, you may want to populate the options with relations in
223254
```php
224255
return [
225256
// ...
226-
257+
227258
'selectable_resources' => [
228259
App\Filament\Resources\ContentResource::class,
229260
]
@@ -247,10 +278,10 @@ class CustomField extends Base
247278
public static function make(string $name, ?Field $field = null): TextInput
248279
{
249280
$input = self::applyDefaultSettings(TextInput::make($name), $field);
250-
281+
251282
// Add your custom field logic here
252283
$input->placeholder('Custom placeholder');
253-
284+
254285
return $input;
255286
}
256287

@@ -299,21 +330,22 @@ class RepeaterField extends Base
299330
```
300331

301332
Available base fields that can be excluded:
302-
- `required` - Required field toggle
303-
- `disabled` - Disabled field toggle
304-
- `hidden` - Hidden field toggle
305-
- `helperText` - Helper text input
306-
- `hint` - Hint text input
307-
- `hintColor` - Hint color picker
308-
- `hintIcon` - Hint icon input
309-
- `defaultValue` - Default value input
333+
334+
- `required` - Required field toggle
335+
- `disabled` - Disabled field toggle
336+
- `hidden` - Hidden field toggle
337+
- `helperText` - Helper text input
338+
- `hint` - Hint text input
339+
- `hintColor` - Hint color picker
340+
- `hintIcon` - Hint icon input
341+
- `defaultValue` - Default value input
310342

311343
#### Best practices for field exclusion
312344

313-
- **Only exclude what doesn't apply**: Don't exclude fields just because you don't use them - only exclude fields that conceptually don't make sense for your field type
314-
- **Document your exclusions**: Add comments explaining why certain fields are excluded
315-
- **Test thoroughly**: Make sure your field still works correctly after excluding base fields
316-
- **Consider inheritance**: If your field extends another custom field, make sure to call `parent::excludeFromBaseSchema()` if you need to add more exclusions
345+
- **Only exclude what doesn't apply**: Don't exclude fields just because you don't use them - only exclude fields that conceptually don't make sense for your field type
346+
- **Document your exclusions**: Add comments explaining why certain fields are excluded
347+
- **Test thoroughly**: Make sure your field still works correctly after excluding base fields
348+
- **Consider inheritance**: If your field extends another custom field, make sure to call `parent::excludeFromBaseSchema()` if you need to add more exclusions
317349

318350
Example of a field that excludes multiple base fields:
319351

@@ -342,6 +374,14 @@ To register your own fields, you can add them to the `fields.fields` config arra
342374
],
343375
```
344376

377+
## Documentation
378+
379+
### Rich Editor Plugins
380+
381+
The package includes a powerful Rich Editor with custom plugins:
382+
383+
- **[Jump Anchor Plugin](docs/jump-anchor-plugin.md)** - Add anchor links to selected text for navigation and jumping to specific sections
384+
345385
## Testing
346386

347387
```bash
@@ -362,8 +402,8 @@ Please review [our security policy](../../security/policy) on how to report secu
362402

363403
## Credits
364404

365-
- [Baspa](https://github.com/Backstage)
366-
- [All Contributors](../../contributors)
405+
- [Baspa](https://github.com/Backstage)
406+
- [All Contributors](../../contributors)
367407

368408
## License
369409

bin/build-rich-editor-plugins.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import * as esbuild from 'esbuild'
2+
import { readdir } from 'fs/promises'
3+
import { join } from 'path'
4+
5+
async function buildRichEditorPlugins() {
6+
try {
7+
// Find all JavaScript plugin files
8+
const pluginsDir = './resources/js/filament/rich-content-plugins'
9+
const files = await readdir(pluginsDir)
10+
const pluginFiles = files.filter(file => file.endsWith('.js'))
11+
12+
if (pluginFiles.length === 0) {
13+
console.log('No rich editor plugin files found to build.')
14+
return
15+
}
16+
17+
console.log(`Found ${pluginFiles.length} plugin file(s) to build:`)
18+
pluginFiles.forEach(file => console.log(` - ${file}`))
19+
20+
// Create entry points for each plugin
21+
const entryPoints = {}
22+
pluginFiles.forEach(file => {
23+
const fileName = file.replace('.js', '')
24+
entryPoints[`filament/rich-content-plugins/${fileName}`] = join(pluginsDir, file)
25+
})
26+
27+
const context = await esbuild.context({
28+
define: {
29+
'process.env.NODE_ENV': `'production'`,
30+
},
31+
bundle: true,
32+
mainFields: ['module', 'main'],
33+
platform: 'neutral',
34+
sourcemap: false,
35+
sourcesContent: false,
36+
treeShaking: true,
37+
target: ['es2020'],
38+
minify: true,
39+
entryPoints,
40+
outdir: './resources/js/dist',
41+
format: 'esm',
42+
})
43+
44+
await context.rebuild()
45+
await context.dispose()
46+
47+
console.log('✅ Rich editor plugins built successfully!')
48+
console.log('Built files:')
49+
Object.keys(entryPoints).forEach(key => {
50+
console.log(` - resources/js/dist/${key}.js`)
51+
})
52+
} catch (error) {
53+
console.error('❌ Error building rich editor plugins:', error)
54+
process.exit(1)
55+
}
56+
}
57+
58+
buildRichEditorPlugins()

docs/jump-anchor-plugin.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Jump Anchor Plugin
2+
3+
Add anchor links to rich editor content for navigation.
4+
5+
## Features
6+
7+
- Add anchor links to selected text
8+
- Uses standard HTML `id` attributes
9+
- Hashtag icon in the toolbar
10+
- Modal interface for configuration
11+
- Automatic ID generation
12+
13+
## Usage
14+
15+
1. Build the JavaScript extension:
16+
17+
```bash
18+
node bin/build-rich-editor-plugins.js
19+
```
20+
21+
2. Publish assets:
22+
23+
```bash
24+
php artisan filament:assets
25+
```
26+
27+
3. Use in rich editor:
28+
- Select text
29+
- Click hashtag button in toolbar
30+
- Enter anchor ID
31+
32+
## HTML Output
33+
34+
```html
35+
<span id="section-1">Selected Text</span>
36+
```
37+
38+
## Validation
39+
40+
Anchor IDs must contain only letters, numbers, hyphens, and underscores.
41+
42+
## Troubleshooting
43+
44+
- Build extension: `node bin/build-rich-editor-plugins.js`
45+
- Publish assets: `php artisan filament:assets`
46+
- Clear caches: `php artisan cache:clear`

0 commit comments

Comments
 (0)