You are here

class GridDialog in CKEditor Bootstrap Grid 2.0.x

Creates a grid dialog form for use in CKEditor.

@package Drupal\ckeditor_bs_grid\Form

Hierarchy

Expanded class hierarchy of GridDialog

1 string reference to 'GridDialog'
ckeditor_bs_grid.routing.yml in ./ckeditor_bs_grid.routing.yml
ckeditor_bs_grid.routing.yml

File

src/Form/GridDialog.php, line 23

Namespace

Drupal\ckeditor_bs_grid\Form
View source
class GridDialog extends FormBase {

  /**
   * The form builder.
   *
   * @var \Drupal\Core\Form\FormBuilderInterface
   */
  protected $formBuilder;

  /**
   * GridDialog class initialize..
   *
   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
   *   The form builder.
   */
  public function __construct(FormBuilderInterface $form_builder) {
    $this->formBuilder = $form_builder;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static($container
      ->get('form_builder'));
  }

  /**
   * {@inheritDoc}
   */
  public function getFormId() {
    return 'ckeditor_bs_grid_dialog';
  }

  /**
   * {@inheritDoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, Editor $editor = NULL) {
    $form['#attached']['library'][] = 'editor/drupal.editor.dialog';
    $form['#attached']['library'][] = 'ckeditor_bs_grid/dialog';

    // Opening the dialog passes the value as editor object, but is only once.
    $values = $form_state
      ->getValues();
    $input = $form_state
      ->getUserInput();

    // Initialize entity element with form attributes, if present.
    $settings = empty($values['bs_grid_settings']) ? [] : Json::decode($values['bs_grid_settings']);
    $settings += empty($input['bs_grid_settings']) ? [] : Json::decode($input['bs_grid_settings']);

    // The default values are set directly from \Drupal::request()->request,
    // provided by the editor plugin opening the dialog.
    if (!$form_state
      ->get('bs_grid_settings')) {
      $form_state
        ->set('bs_grid_settings', isset($input['editor_object']) ? $input['editor_object'] : []);
    }
    $settings += $form_state
      ->get('bs_grid_settings');

    // Save the editor settings.
    if ($editor instanceof EditorInterface) {
      $editor_settings = $editor
        ->getSettings();
      if (isset($editor_settings['plugins']['bs_grid'])) {
        $settings['editor_settings'] = $editor_settings['plugins']['bs_grid'];
      }
    }
    $form_state
      ->set('bs_grid_settings', $settings);
    if (!$form_state
      ->get('step')) {
      $form_state
        ->set('step', 'select');
    }
    $form['#tree'] = TRUE;
    $form['#prefix'] = '<div id="bs_grid-dialog-form">';
    $form['#suffix'] = '</div>';
    $form['#attributes']['class'][] = 'bs_grid-dialog-step--' . $form_state
      ->get('step');
    if ($form_state
      ->get('step') == 'select') {
      $form = $this
        ->buildSelectStep($form, $form_state);
    }
    elseif ($form_state
      ->get('step') == 'layout') {
      $form = $this
        ->buildLayoutStep($form, $form_state);
    }
    elseif ($form_state
      ->get('step') == 'advanced') {
      $form = $this
        ->buildAdvancedStep($form, $form_state);
    }
    return $form;
  }

  /**
   * Builds the column selection step.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return array
   *   The form part.
   */
  public function buildSelectStep(array $form, FormStateInterface $form_state) {
    $settings = $form_state
      ->get('bs_grid_settings');
    $form['#title'] = $this
      ->t("Select columns");
    $columns = [];
    $available_cols = array_filter($settings['editor_settings']['available_columns']);
    foreach ($available_cols as $column) {
      $title = $this
        ->t('Column @num', [
        '@num' => $column,
      ]);
      $img = drupal_get_path('module', 'ckeditor_bs_grid') . '/images/ui/col_' . $column . '.png';
      $img_src = '<img src="/' . $img . '" title="' . $title . '" />';
      $columns[$column] = $img_src . '<p>' . $this
        ->t('Column @num', [
        '@num' => $column,
      ]) . '</p>';
    }

    // @todo Need to find a better way around this.
    if (!empty($settings['saved'])) {
      $form['bs_grid_settings'] = [
        '#type' => 'hidden',
        '#value' => Json::encode($settings),
      ];
    }

    // @todo Make this configurable from text format.
    $form['num_columns'] = [
      '#title' => $this
        ->t('Select Number of Columns'),
      '#type' => 'radios',
      '#options' => $columns,
      '#default_value' => $settings['num_columns'] ?? 1,
      '#attributes' => [
        'disabled' => $settings['saved'] ?? FALSE,
      ],
      '#prefix' => $this
        ->t('Read-only on existing elements.'),
    ];
    $form['actions'] = [
      '#type' => 'actions',
    ];
    $form['actions']['next'] = [
      '#type' => 'submit',
      '#value' => $this
        ->t('Next'),
      '#button_type' => 'primary',
      // No regular submit-handler. This form only works via JavaScript.
      '#submit' => [],
      '#ajax' => [
        'callback' => '::submitStep',
        'event' => 'click',
      ],
      '#attributes' => [
        'class' => [
          'js-button-next',
        ],
      ],
    ];
    return $form;
  }

  /**
   * Builds the layout selection step.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return array
   *   The form part.
   */
  public function buildLayoutStep(array $form, FormStateInterface $form_state) {
    $config = $this
      ->config('ckeditor_bs_grid.settings');
    $settings = $form_state
      ->get('bs_grid_settings');
    $form['#title'] = $this
      ->t("Choose a layout");
    $form['add_container'] = [
      '#title' => $this
        ->t('Add Container'),
      '#type' => 'checkbox',
      '#default_value' => $settings['add_container'] ?? FALSE,
      '#attributes' => [
        'class' => [
          'bs_grid-add-container',
        ],
      ],
    ];
    $form['container_type'] = [
      '#title' => $this
        ->t('Container Type'),
      '#type' => 'radios',
      '#options' => [
        'default' => $this
          ->t('Default'),
        'fluid' => $this
          ->t('Fluid'),
        'wrapper' => $this
          ->t('Wrapper'),
      ],
      '#default_value' => $settings['container_type'] ?? 'default',
      '#states' => [
        'visible' => [
          '.bs_grid-add-container' => [
            'checked' => TRUE,
          ],
        ],
      ],
    ];

    // @todo detect row class override.
    $form['no_gutter'] = [
      '#title' => $this
        ->t('No Gutters'),
      '#type' => 'checkbox',
      '#default_value' => $settings['no_gutter'] ?? FALSE,
    ];

    // Default.
    $num_cols = (int) $settings['num_columns'];
    $options = [
      'none' => $this
        ->t('None (advanced)'),
    ];
    $available_breakpoints = array_filter($settings['editor_settings']['available_breakpoints']);
    foreach ($config
      ->get('breakpoints') as $class => $breakpoint) {
      if (!isset($available_breakpoints[$class])) {
        continue;
      }
      $prefix = $breakpoint['prefix'];
      $form['breakpoints'][$prefix] = [
        '#title' => $breakpoint['label'],
        '#type' => 'details',
        '#open' => FALSE,
      ];
      foreach ($breakpoint['columns'][$num_cols]['layouts'] as $layout) {
        $options[implode('_', $layout['settings'])] = $layout['label'];
      }
      $form['breakpoints'][$prefix]['layout'] = [
        '#type' => 'radios',
        '#options' => $options,
        '#default_value' => $settings['breakpoints'][$prefix]['layout'] ?? 'none',
        '#attributes' => [
          'data-bs-grid-option' => TRUE,
        ],
      ];
    }

    // @todo Refactor for repetition here.
    $form['actions'] = [
      '#type' => 'actions',
    ];
    $form['actions']['back'] = [
      '#type' => 'submit',
      '#value' => $this
        ->t('Back'),
      '#button_type' => 'primary',
      // No regular submit-handler. This form only works via JavaScript.
      '#submit' => [],
      '#ajax' => [
        'callback' => '::submitBackStep',
        'event' => 'click',
      ],
      '#attributes' => [
        'class' => [
          'js-button-back',
        ],
      ],
    ];
    $form['actions']['save_modal'] = [
      '#type' => 'submit',
      '#value' => $this
        ->t('Save'),
      '#button_type' => 'primary',
      // No regular submit-handler. This form only works via JavaScript.
      '#submit' => [],
      '#ajax' => [
        'callback' => '::submitDialog',
        'event' => 'click',
      ],
      '#attributes' => [
        'class' => [
          'js-button-next',
        ],
      ],
    ];
    $form['actions']['advanced'] = [
      '#type' => 'submit',
      '#value' => $this
        ->t('Advanced Settings'),
      '#button_type' => 'primary',
      // No regular submit-handler. This form only works via JavaScript.
      '#submit' => [],
      '#ajax' => [
        'callback' => '::submitStep',
        'event' => 'click',
      ],
      '#attributes' => [
        'class' => [
          'js-button-next',
        ],
      ],
    ];
    return $form;
  }

  /**
   * Builds the advanced settings step.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return array
   *   The form part.
   */
  public function buildAdvancedStep(array $form, FormStateInterface $form_state) {
    $settings = $form_state
      ->get('bs_grid_settings');
    $form['#title'] = $this
      ->t("Advanced Settings");

    // Only show if container is selected.
    if (!empty($settings['add_container'])) {
      $form['container_wrapper_class'] = [
        '#title' => $this
          ->t('Container Wrapper Classes'),
        '#type' => 'textfield',
        '#description' => $this
          ->t('Add classes separated by space. Ex: bg-warning py-5'),
        '#default_value' => $settings['container_wrapper_class'] ?? '',
      ];
      $form['container_class'] = [
        '#title' => $this
          ->t('Container Classes'),
        '#type' => 'textfield',
        '#description' => $this
          ->t('Add classes separated by space. Ex: bg-warning py-5'),
        '#default_value' => $settings['container_class'] ?? '',
      ];
    }
    $form['row_class'] = [
      '#title' => $this
        ->t('Row Classes'),
      '#type' => 'textfield',
      '#description' => $this
        ->t('Add classes separated by space. Ex: bg-warning py-5'),
      '#default_value' => $settings['row_class'] ?? '',
    ];
    for ($i = 1; $i <= $settings['num_columns']; $i++) {
      $form['col_' . $i . '_classes'] = [
        '#title' => $this
          ->t('Col @num classes', [
          '@num' => $i,
        ]),
        '#type' => 'textfield',
        '#default_value' => $settings['col_' . $i . '_classes'] ?? '',
        '#description' => $this
          ->t('Add classes separated by space. Ex: bg-warning py-5'),
      ];
    }
    $form['actions'] = [
      '#type' => 'actions',
    ];
    $form['actions']['back'] = [
      '#type' => 'submit',
      '#value' => $this
        ->t('Back'),
      '#button_type' => 'primary',
      // No regular submit-handler. This form only works via JavaScript.
      '#submit' => [],
      '#ajax' => [
        'callback' => '::submitBackStep',
        'event' => 'click',
      ],
      '#attributes' => [
        'class' => [
          'js-button-back',
        ],
      ],
    ];
    $form['actions']['save_modal'] = [
      '#type' => 'submit',
      '#value' => $this
        ->t('Save'),
      '#button_type' => 'primary',
      // No regular submit-handler. This form only works via JavaScript.
      '#submit' => [],
      '#ajax' => [
        'callback' => '::submitDialog',
        'event' => 'click',
      ],
      '#attributes' => [
        'class' => [
          'js-button-next',
        ],
      ],
    ];
    return $form;
  }

  /**
   * {@inheritDoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {

    // Load current settings.
    $settings = $form_state
      ->get('bs_grid_settings');
    if ($form_state
      ->get('step') == 'select') {
      $values_to_save = [
        'num_columns',
      ];
    }
    elseif ($form_state
      ->get('step') == 'layout') {
      $values_to_save = [
        'add_container',
        'container_type',
        'no_gutter',
        'breakpoints',
      ];
    }
    elseif ($form_state
      ->get('step') == 'advanced') {
      $values_to_save = [
        'container_wrapper_class',
        'container_class',
        'row_class',
      ];

      // Detect changes and set parent to "none".
      for ($i = 1; $i <= $settings['num_columns']; $i++) {
        $key = 'col_' . $i . '_classes';
        $settings[$key] = $form_state
          ->getValue($key, '');
      }
    }
    foreach ($values_to_save as $save) {
      $settings[$save] = $form_state
        ->getValue($save);
    }
    $form_state
      ->set('bs_grid_settings', $settings);
  }

  /**
   * Submits a step to move on.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   An ajax response.
   */
  public function submitStep(array &$form, FormStateInterface $form_state) {
    $response = new AjaxResponse();

    // Display errors in form, if any.
    if ($form_state
      ->hasAnyErrors()) {
      unset($form['#prefix'], $form['#suffix']);
      $form['status_messages'] = [
        '#type' => 'status_messages',
        '#weight' => -10,
      ];
      $response
        ->addCommand(new HtmlCommand('#bs_grid-dialog-form', $form));
    }
    else {
      $form_state
        ->set('step', $form_state
        ->get('step') === 'select' ? 'layout' : 'advanced');
      $form_state
        ->setRebuild(TRUE);
      $rebuild_form = $this->formBuilder
        ->rebuildForm('ckeditor_bs_grid_dialog', $form_state, $form);
      unset($rebuild_form['#prefix'], $rebuild_form['#suffix']);
      $response
        ->addCommand(new HtmlCommand('#bs_grid-dialog-form', $rebuild_form));
      $response
        ->addCommand(new SetDialogTitleCommand('', $rebuild_form['#title']));
    }
    return $response;
  }

  /**
   * Submits a backstep. @todo this should probably just go above.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   An ajax response.
   */
  public function submitBackStep(array &$form, FormStateInterface $form_state) {
    $response = new AjaxResponse();
    $form_state
      ->set('step', $form_state
      ->get('step') === 'advanced' ? 'layout' : 'select');
    $form_state
      ->setRebuild(TRUE);
    $rebuild_form = $this->formBuilder
      ->rebuildForm('ckeditor_bs_grid_dialog', $form_state, $form);
    unset($rebuild_form['#prefix'], $rebuild_form['#suffix']);
    $response
      ->addCommand(new HtmlCommand('#bs_grid-dialog-form', $rebuild_form));
    $response
      ->addCommand(new SetDialogTitleCommand('', $rebuild_form['#title']));
    return $response;
  }

  /**
   * Commit the changes and close the dialog window.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   An ajax response.
   */
  public function submitDialog(array &$form, FormStateInterface $form_state) {
    $response = new AjaxResponse();
    $settings = $form_state
      ->get('bs_grid_settings');

    // Set container classes.
    if (isset($settings['add_container'])) {
      $settings['container_class'] = $settings['container_class'] ?? '';
      if ($settings['container_type'] === 'default') {
        $settings['container_class'] = 'container ' . $settings['container_class'];
      }
      else {
        $settings['container_class'] = 'container-fluid ' . $settings['container_class'];
      }
    }
    else {
      $settings['container_class'] = '';
    }

    // Set row classes.
    if (isset($settings['row_class'])) {
      if (strpos($settings['row_class'], 'row') === FALSE) {
        $settings['row_class'] = 'row ' . $settings['row_class'];
      }
      if ($settings['no_gutter'] && strpos($settings['row_class'], 'no-gutters') === FALSE) {
        $settings['row_class'] = 'no-gutters ' . $settings['row_class'];
      }
    }
    elseif ($settings['no_gutter']) {
      $settings['row_class'] = 'row no-gutters';
    }
    else {
      $settings['row_class'] = 'row';
    }

    // Parse out the column classes.
    for ($i = 1; $i <= $settings['num_columns']; $i++) {
      $keys = [];
      $col = 'col_' . $i . '_classes';
      foreach ($settings['breakpoints'] as $prefix => $selection) {

        // Advanced.
        if ($selection['layout'] === 'none') {
          continue;
        }
        else {
          $vals = explode('_', $selection['layout']);
          $col_value = $vals[$i - 1];
          if (!empty($col_value)) {
            $suffix = $col_value === 'equal' ? '' : '-' . $col_value;
            if ($prefix === 'none') {
              $keys['col' . $suffix] = TRUE;
            }
            else {
              $keys['col-' . $prefix . $suffix] = TRUE;
            }
          }
        }
      }
      $current = implode(' ', array_keys($keys));
      if (!empty($settings[$col])) {
        $settings[$col] = $current . ' ' . $settings[$col];
      }
      else {
        $settings[$col] = $current;
      }
    }

    // Track that we're committed.
    $settings['saved'] = TRUE;
    $response
      ->addCommand(new EditorDialogSave($settings));
    $response
      ->addCommand(new CloseModalDialogCommand());
    return $response;
  }

  /**
   * {@inheritDoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {

    // Nothing needed here.
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DependencySerializationTrait::$_entityStorages protected property
DependencySerializationTrait::$_serviceIds protected property
DependencySerializationTrait::__sleep public function 2
DependencySerializationTrait::__wakeup public function 2
FormBase::$configFactory protected property The config factory. 3
FormBase::$requestStack protected property The request stack. 1
FormBase::$routeMatch protected property The route match.
FormBase::config protected function Retrieves a configuration object.
FormBase::configFactory protected function Gets the config factory for this form. 3
FormBase::container private function Returns the service container.
FormBase::currentUser protected function Gets the current user.
FormBase::getRequest protected function Gets the request object.
FormBase::getRouteMatch protected function Gets the route match.
FormBase::logger protected function Gets the logger for a specific channel.
FormBase::redirect protected function Returns a redirect response object for the specified route.
FormBase::resetConfigFactory public function Resets the configuration factory.
FormBase::setConfigFactory public function Sets the config factory for this form.
FormBase::setRequestStack public function Sets the request stack object to use.
GridDialog::$formBuilder protected property The form builder.
GridDialog::buildAdvancedStep public function Builds the advanced settings step.
GridDialog::buildForm public function Form constructor. Overrides FormInterface::buildForm
GridDialog::buildLayoutStep public function Builds the layout selection step.
GridDialog::buildSelectStep public function Builds the column selection step.
GridDialog::create public static function Instantiates a new instance of this class. Overrides FormBase::create
GridDialog::getFormId public function Returns a unique string identifying the form. Overrides FormInterface::getFormId
GridDialog::submitBackStep public function Submits a backstep. @todo this should probably just go above.
GridDialog::submitDialog public function Commit the changes and close the dialog window.
GridDialog::submitForm public function Form submission handler. Overrides FormInterface::submitForm
GridDialog::submitStep public function Submits a step to move on.
GridDialog::validateForm public function Form validation handler. Overrides FormBase::validateForm
GridDialog::__construct public function GridDialog class initialize..
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 27
MessengerTrait::messenger public function Gets the messenger. 27
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
StringTranslationTrait::$stringTranslation protected property The string translation service. 4
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.