diff --git a/src/.vuepress/config.js b/src/.vuepress/config.js index 4ac2ae6..7ca73b3 100644 --- a/src/.vuepress/config.js +++ b/src/.vuepress/config.js @@ -133,6 +133,10 @@ module.exports = { text: 'BaseFieldManager Class', link: '/api/classes/base-field-manager/' }, + { + text: 'Extending Fields', + link: '/api/extending-fields/my-custom-element/' + }, { text: 'IntegrationManagerController', link: '/api/classes/integration-manager-controller/' diff --git a/src/.vuepress/sidebars/apiSidebar.js b/src/.vuepress/sidebars/apiSidebar.js index 48d2a20..198f246 100644 --- a/src/.vuepress/sidebars/apiSidebar.js +++ b/src/.vuepress/sidebars/apiSidebar.js @@ -34,4 +34,15 @@ module.exports = [ }, ] }, + { + title: 'Extend Fields', + collapsable: false, + sidebarDepth: 1, + children: [ + { + title: 'Custom Field with Custom Editor', + children: ['extending-fields/my-custom-element/'] + } + ] + }, ]; diff --git a/src/api/extending-fields/my-custom-element/index.md b/src/api/extending-fields/my-custom-element/index.md new file mode 100644 index 0000000..2265763 --- /dev/null +++ b/src/api/extending-fields/my-custom-element/index.md @@ -0,0 +1,463 @@ +# Custom Element With Custom Vue + +The Fluent Forms `BaseFieldManager` extension allows developers to create custom form fields with custom editor components. + +This guide demonstrates how to create a custom field called `MyCustomElement` by extending the [`BaseFieldManager`](/api/classes/base-field-manager/) class, along with implementing custom Vue components for the editor interface. + +## Implementation Overview + +To create a custom field, you'll need to: + +1. Create a PHP class that extends [`BaseFieldManager`](/api/classes/base-field-manager/) +2. Register custom Vue components for the editor interface +3. Implement the required methods for field rendering and configuration + +## PHP Class Implementation + +First, create a PHP class that extends [`BaseFieldManager`](/api/classes/base-field-manager/): + +```php +key, array($this, 'renderResponse'), 10, 4); + add_filter('fluentform/validate_input_item_' . $this->key, array($this, 'validateInput'), 10, 5); + + // Enqueue custom editor assets + add_action('fluentform/loading_editor_assets', function () { + wp_enqueue_script( + 'my-custom-element-editor', + MY_PLUGIN_URL . 'assets/js/custom-element.js', + [], + '1.0.0', + true + ); + }); + } + + function getComponent() + { + return [ + 'index' => 25, // Priority in the form editor + 'element' => 'my_custom_element', + 'attributes' => [ + 'name' => 'my_custom_element', + 'data-type' => 'my_custom_element' + ], + 'settings' => [ + 'label' => __('My Custom Element', 'my-plugin'), + 'admin_field_label' => '', + 'custom_option' => '', + 'conditional_logics' => [], + 'container_class' => '', + 'custom_settings' => [ + 'option_one' => '', + 'option_two' => '', + 'show_extra' => 'hide' + ], + 'validation_rules' => [ + 'required' => [ + 'value' => false, + 'message' => __('This field is required', 'my-plugin'), + ], + ], + ], + 'editor_options' => [ + 'title' => __('My Custom Element', 'my-plugin'), + 'icon_class' => 'el-icon-s-operation', + 'template' => 'CustomEditorField', // IMPORTANT: Must be 'CustomEditorField' to use custom Vue components + 'componentName' => 'MyCustomElementEditor', // IMPORTANT: Must match the Vue component name in JavaScript + ], + ]; + } + + public function getGeneralEditorElements() + { + return [ + 'label', + 'label_placement', + 'admin_field_label', + 'custom_option', + 'custom_settings', // This will use the custom settings component + 'validation_rules', + ]; + } + + public function getAdvancedEditorElements() + { + return [ + 'container_class', + 'name', + 'conditional_logics' + ]; + } + + public function getEditorCustomizationSettings() + { + return [ + 'custom_option' => [ + 'template' => 'selectGroup', + 'label' => __('Custom Option', 'my-plugin'), + 'options' => [ + [ + 'value' => 'option1', + 'label' => __('Option 1', 'my-plugin') + ], + [ + 'value' => 'option2', + 'label' => __('Option 2', 'my-plugin') + ], + [ + 'value' => 'option3', + 'label' => __('Option 3', 'my-plugin') + ] + ] + ], + 'custom_settings' => [ + 'template' => 'CustomSettingsField', // IMPORTANT: Must be 'CustomSettingsField' to use custom Vue components + 'label' => __('Custom Settings', 'my-plugin'), + 'componentName' => 'MyCustomSettingsComponent' // IMPORTANT: Must match the Vue component name in JavaScript + ], + ]; + } + + /** + * Render the element on the frontend + * @param array $data Element data + * @param object $form Form object + * @return void + */ + public function render($data, $form) + { + $elementName = $data['element']; + + // Add default class + $data['attributes']['class'] = trim('ff-el-form-control ' . Arr::get($data, 'attributes.class', '')); + $data['attributes']['id'] = $this->makeElementId($data, $form); + + // Add tab index if available + if ($tabIndex = \FluentForm\App\Helpers\Helper::getNextTabIndex()) { + $data['attributes']['tabindex'] = $tabIndex; + } + + // Set aria-required attribute + $ariaRequired = Arr::get($data, 'settings.validation_rules.required.value') ? 'true' : 'false'; + + // Get custom settings + $customOption = Arr::get($data, 'settings.custom_option'); + $showExtra = Arr::get($data, 'settings.custom_settings.show_extra', 'hide') === 'show'; + + // Enqueue frontend assets if needed + wp_enqueue_script( + 'my-custom-element-frontend', + MY_PLUGIN_URL . 'assets/js/custom-element-frontend.js', + ['jquery'], + '1.0.0', + true + ); + + // Create element markup + $elMarkup = '
'; + $elMarkup .= 'buildAttributes($data['attributes'], $form) . ' aria-required="' . $ariaRequired . '">'; + + if ($showExtra) { + $elMarkup .= '
Extra content is visible
'; + } + + $elMarkup .= '
'; + + // Build final HTML + $html = $this->buildElementMarkup($elMarkup, $data, $form); + + // Output the HTML + echo apply_filters('fluentform/rendering_field_html_' . $elementName, $html, $data, $form); + } + + /** + * Format the field value for display in entries, emails, etc. + * @param mixed $data Field value + * @param array $field Field settings + * @param int $form_id Form ID + * @param bool $isHtml Whether HTML output is expected + * @return string + */ + public function renderResponse($data, $field, $form_id, $isHtml) + { + if (is_array($data)) { + return implode(', ', $data); + } + + return $data; + } + + /** + * Validate the field input + * @param array $errorMessage Error message array + * @param array $field Field settings + * @param array $formData Form data + * @param array $fields All form fields + * @param object $form Form object + * @return array + */ + public function validateInput($errorMessage, $field, $formData, $fields, $form) + { + $fieldName = $field['name']; + if (empty($formData[$fieldName])) { + return $errorMessage; + } + $value = $formData[$fieldName]; // This is the user input value + + // Add your custom validation logic here + + return $errorMessage; + } +} +``` + +## Important Notes About Component Names + +### In PHP: + +1. In the `getComponent()` method, set `'template' => 'CustomEditorField'` to tell Fluent Forms to use a custom Vue component. +2. The `componentName` property in `editor_options` must match exactly the name you register in JavaScript. +3. Similarly, in `getEditorCustomizationSettings()`, use `'template' => 'CustomSettingsField'` and set `componentName` to match your settings component name. + +These connections are critical - if the names don't match exactly, Fluent Forms won't be able to find and render your custom components. + +## JavaScript Implementation + +Next, create a JavaScript file to register your custom Vue components: + +```javascript +// custom-element.js + +import MyCustomElementEditor from './components/MyCustomElementEditor.vue'; +import MyCustomSettingsComponent from './components/MyCustomSettingsComponent.vue'; + +// Register the components with EXACTLY the same names used in PHP +window.ffEditorOptionsCustomComponents = window.ffEditorOptionsCustomComponents || {}; + +// IMPORTANT: These keys must match the componentName values in PHP +window.ffEditorOptionsCustomComponents.MyCustomElementEditor = MyCustomElementEditor; +window.ffEditorOptionsCustomComponents.MyCustomSettingsComponent = MyCustomSettingsComponent; +``` + +## Vue Component for Editor + +Create a Vue component for the main editor interface: +- props + - `item` The field data + +```vue + + + + +``` + +## Vue Component for Custom Settings + +Create a Vue component for the custom settings: + +- props + - `listItem` Field settings options data - `object` + - `editItem` Field data - `object` + - `form_items` All fields data - `array` + +```vue + + + + + + +``` + +## Registering the Custom Field + +Finally, initialize your custom field in your plugin: + +```php +key, array($this, 'renderResponse'), 10, 4); + add_filter('fluentform/validate_input_item_' . $this->key, array($this, 'validateInput'), 10, 5); + + // Enqueue assets + add_action('fluentform/loading_editor_assets', array($this, 'enqueueEditorAssets')); + } + + // Implement required methods... + + public function enqueueEditorAssets() + { + wp_enqueue_script( + 'my-custom-element-editor', + MY_PLUGIN_URL . 'assets/js/custom-element.js', + [], + '1.0.0', + true + ); + } +} + +/* + * Initialize the class + */ +add_action('fluentform/loaded', function () { + new MyCustomElement(); +}); +``` + +By following this comprehensive guide, you can create powerful custom form fields that integrate perfectly with the Fluent Forms editor interface. \ No newline at end of file