| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416 | <?php/** * @file * Functions to support theming in the Claro theme. */use Drupal\claro\ClaroPreRender;use Drupal\Component\Utility\Html;use Drupal\Component\Utility\NestedArray;use Drupal\Component\Utility\UrlHelper;use Drupal\Core\Entity\EntityForm;use Drupal\Core\Form\FormStateInterface;use Drupal\Core\GeneratedLink;use Drupal\Core\Link;use Drupal\Core\Render\Element;use Drupal\Core\Template\Attribute;use Drupal\Core\Url;use Drupal\media\MediaForm;use Drupal\file\FileInterface;use Drupal\views\ViewExecutable;/** * Implements hook_theme_suggestions_HOOK_alter() for form_element. */function claro_theme_suggestions_form_element_alter(&$suggestions, $variables) {  if (!empty($variables['element']['#type'])) {    $suggestions[] = 'form_element__' . $variables['element']['#type'];  }}/** * Implements hook_theme_suggestions_HOOK_alter() for details. */function claro_theme_suggestions_details_alter(&$suggestions, $variables) {  if (!empty($variables['element']['#vertical_tab_item'])) {    $suggestions[] = 'details__vertical_tabs';  }}/** * Implements hook_preprocess_HOOK() for menu-local-tasks templates. * * Use preprocess hook to set #attached to child elements * because they will be processed by Twig and drupal_render will * be invoked. */function claro_preprocess_menu_local_tasks(&$variables) {  if (!empty($variables['primary'])) {    $variables['primary']['#attached'] = [      'library' => [        'claro/drupal.nav-tabs',      ],    ];  }  elseif (!empty($variables['secondary'])) {    $variables['secondary']['#attached'] = [      'library' => [        'claro/drupal.nav-tabs',      ],    ];  }  foreach (Element::children($variables['primary']) as $key) {    $variables['primary'][$key]['#level'] = 'primary';  }  foreach (Element::children($variables['secondary']) as $key) {    $variables['secondary'][$key]['#level'] = 'secondary';  }}/** * Implements hook_preprocess_HOOK() for menu-local-task templates. */function claro_preprocess_menu_local_task(&$variables) {  $variables['link']['#options']['attributes']['class'][] = 'tabs__link';  $variables['link']['#options']['attributes']['class'][] = 'js-tabs-link';  // Ensure is-active class is set when the tab is active. The generic active  // link handler applies stricter comparison rules than what is necessary for  // tabs.  if (isset($variables['is_active']) && $variables['is_active'] === TRUE) {    $variables['link']['#options']['attributes']['class'][] = 'is-active';  }  if (isset($variables['element']['#level'])) {    $variables['level'] = $variables['element']['#level'];  }}/** * Implements hook_preprocess_HOOK() for menu-local-task Views UI templates. */function claro_preprocess_menu_local_task__views_ui(&$variables) {  // Remove 'tabs__link' without adding a new class because it couldn't be used  // reliably.  // @see https://www.drupal.org/node/3051605  $link_class_index = array_search('tabs__link', $variables['link']['#options']['attributes']['class']);  if ($link_class_index !== FALSE) {    unset($variables['link']['#options']['attributes']['class'][$link_class_index]);  }}/** * Implements template_preprocess_HOOK() for node_add_list. * * Makes node_add_list variables compatible with entity_add_list. */function claro_preprocess_node_add_list(&$variables) {  if (!empty($variables['content'])) {    /** @var \Drupal\node\NodeTypeInterface $type */    foreach ($variables['content'] as $type) {      $label = $type->label();      $description = $type->getDescription();      $type_id = $type->id();      $add_url = Url::fromRoute('node.add', ['node_type' => $type_id]);      $variables['bundles'][$type_id] = [        'label' => $label,        'add_link' => Link::fromTextAndUrl($label, $add_url),        'description' => [],      ];      if (!empty($description)) {        $variables['bundles'][$type_id]['description'] = [          '#markup' => $description,        ];      }    }    $variables['attributes']['class'][] = 'node-type-list';  }}/** * Implements template_preprocess_HOOK() for block_content_add_list. * * Makes block_content_add_list variables compatible with entity_add_list. */function claro_preprocess_block_content_add_list(&$variables) {  if (!empty($variables['content'])) {    $query = \Drupal::request()->query->all();    /** @var \Drupal\block_content\BlockContentTypeInterface $type */    foreach ($variables['content'] as $type) {      $label = $type->label();      $description = $type->getDescription();      $type_id = $type->id();      $add_url = Url::fromRoute('block_content.add_form', [        'block_content_type' => $type_id,      ], [        'query' => $query,      ]);      $variables['bundles'][$type_id] = [        'label' => $label,        'add_link' => Link::fromTextAndUrl($label, $add_url),        'description' => [],      ];      if (!empty($description)) {        $variables['bundles'][$type_id]['description'] = [          '#markup' => $description,        ];      }    }  }}/** * Implements template_preprocess_HOOK() for entity_add_list. */function claro_preprocess_entity_add_list(&$variables) {  // Remove description if empty.  foreach ($variables['bundles'] as $type_id => $values) {    if (isset($values['description']['#markup']) && empty($values['description']['#markup'])) {      $variables['bundles'][$type_id]['description'] = [];    }  }}/** * Implements hook_preprocess_block() for block content. * * Disables contextual links for all blocks except for layout builder blocks. */function claro_preprocess_block(&$variables) {  if (isset($variables['title_suffix']['contextual_links']) && !isset($variables['elements']['#contextual_links']['layout_builder_block'])) {    unset($variables['title_suffix']['contextual_links']);    unset($variables['elements']['#contextual_links']);    $variables['attributes']['class'] = array_diff($variables['attributes']['class'], ['contextual-region']);  }}/** * Implements template_preprocess_HOOK() for admin_block. */function claro_preprocess_admin_block(&$variables) {  if (!empty($variables['block']['content'])) {    $variables['block']['content']['#attributes']['class'][] = 'admin-list--panel';  }}/** * Implements template_preprocess_HOOK() for admin_block. */function claro_preprocess_admin_block_content(&$variables) {  foreach ($variables['content'] as &$item) {    $link_attributes = $item['url']->getOption('attributes') ?: [];    $link_attributes['class'][] = 'admin-item__link';    $item['url']->setOption('attributes', $link_attributes);    $item['link'] = Link::fromTextAndUrl($item['title'], $item['url']);    if (empty($item['description']) || empty($item['description']['#markup'])) {      unset($item['description']);    }  }}/** * Implements hook_preprocess_HOOK() for menu-local-action templates. */function claro_preprocess_menu_local_action(array &$variables) {  $variables['link']['#options']['attributes']['class'][] = 'button--primary';  $variables['attributes']['class'][] = 'local-actions__item';  $legacy_class_key = array_search('button-action', $variables['link']['#options']['attributes']['class']);  if ($legacy_class_key !== FALSE) {    $variables['link']['#options']['attributes']['class'][$legacy_class_key] = 'button--action';  }}/** * Implements hook_element_info_alter(). */function claro_element_info_alter(&$type) {  // Add a pre-render function that handles the sidebar of the node form.  // @todo Refactor when https://www.drupal.org/node/3056089 is in.  if (isset($type['container'])) {    $container_pre_renders = !empty($type['container']['#pre_render']) ? $type['container']['#pre_render'] : [];    array_unshift($container_pre_renders, [ClaroPreRender::class, 'container']);    $type['container']['#pre_render'] = $container_pre_renders;  }  // @todo Refactor when https://www.drupal.org/node/3016343 is fixed.  if (isset($type['text_format'])) {    $type['text_format']['#pre_render'][] = [ClaroPreRender::class, 'textFormat'];  }  // Add a pre-render function that handles dropbutton variants.  if (isset($type['dropbutton'])) {    $type['dropbutton']['#pre_render'][] = [ClaroPreRender::class, 'dropButton'];  }  if (isset($type['vertical_tabs'])) {    $type['vertical_tabs']['#pre_render'][] = [ClaroPreRender::class, 'verticalTabs'];  }  // Add a pre-render to managed_file.  if (isset($type['managed_file'])) {    $type['managed_file']['#pre_render'][] = [ClaroPreRender::class, 'managedFile'];  }  // Add a pre-render to status_messages to alter the placeholder markup.  if (isset($type['status_messages'])) {    $type['status_messages']['#pre_render'][] = [ClaroPreRender::class, 'messagePlaceholder'];  }}/** * Implements template_preprocess_filter_guidelines(). */function claro_preprocess_filter_guidelines(&$variables) {  // Fix filter guidelines selector issue of 'filter/drupal.filter'.  // @todo Remove when https://www.drupal.org/node/2881212 is fixed.  $variables['attributes']['class'][] = 'filter-guidelines-item';  $variables['attributes']['class'][] = 'filter-guidelines-' . $variables['format']->id();}/** * Implements template_preprocess_text_format_wrapper(). * * @todo Remove when https://www.drupal.org/node/3016343 is fixed. */function claro_preprocess_text_format_wrapper(&$variables) {  $description_attributes = [];  if (!empty($variables['attributes']['id'])) {    $description_attributes['id'] = $variables['attributes']['aria-describedby'] = $variables['attributes']['id'];    unset($variables['attributes']['id']);  }  $variables['description_attributes'] = new Attribute($description_attributes);}/** * Implements hook_theme_registry_alter(). */function claro_theme_registry_alter(&$theme_registry) {  if (!empty($theme_registry['admin_block_content'])) {    $theme_registry['admin_block_content']['variables']['attributes'] = [];  }  // @todo Remove when https://www.drupal.org/node/3016346 is fixed.  if (!empty($theme_registry['text_format_wrapper'])) {    $theme_registry['text_format_wrapper']['variables']['disabled'] = FALSE;  }}/** * Implements hook_preprocess_install_page(). */function claro_preprocess_install_page(&$variables) {  // Claro has custom styling for the install page.  $variables['#attached']['library'][] = 'claro/install-page';}/** * Implements hook_preprocess_maintenance_page(). */function claro_preprocess_maintenance_page(&$variables) {  // Claro has custom styling for the maintenance page.  $variables['#attached']['library'][] = 'claro/maintenance-page';}/** * Implements hook_preprocess_HOOK() for details. * * @todo Revisit when https://www.drupal.org/node/3056089 is in. */function claro_preprocess_details(&$variables) {  $element = $variables['element'];  if (!empty($element['#accordion_item'])) {    // Details should appear as an accordion item.    $variables['accordion_item'] = TRUE;  }  if (!empty($element['#accordion'])) {    // Details should appear as a standalone accordion.    $variables['accordion'] = TRUE;  }  if (!empty($element['#theme']) &&  $element['#theme'] === 'file_widget_multiple') {    // Mark the details required if needed. If the file widget allows uploading    // multiple files, the required state is checked by checking the state of    // the first child.    $variables['required'] = isset($element[0]['#required']) ?      $element[0]['#required'] :      !empty($element['#required']);    // If the error is the same as the one in the multiple field widget element,    // we have to avoid displaying it twice. Seven and Stark have this issue    // as well.    // @todo revisit when https://www.drupal.org/node/3084906 is fixed.    if (isset($element['#errors']) && isset($variables['errors']) && $element['#errors'] === $variables['errors']) {      unset($variables['errors']);    }  }  $variables['disabled'] = !empty($element['#disabled']);}/** * Implements hook_form_alter(). */function claro_form_alter(array &$form, FormStateInterface $form_state, $form_id) {  $build_info = $form_state->getBuildInfo();  $form_object = $form_state->getFormObject();  // Make entity forms delete link use the action-link component.  if (isset($form['actions']['delete']['#type']) && $form['actions']['delete']['#type'] === 'link' && !empty($build_info['callback_object']) && $build_info['callback_object'] instanceof EntityForm) {    $form['actions']['delete'] = _claro_convert_link_to_action_link($form['actions']['delete'], 'trash', 'default', 'danger');  }  if ($form_object instanceof ViewsForm && strpos($form_object->getBaseFormId(), 'views_form_media_library') === 0) {    if (isset($form['header'])) {      $form['header']['#attributes']['class'][] = 'media-library-views-form__header';      $form['header']['media_bulk_form']['#attributes']['class'][] = 'media-library-views-form__bulk_form';    }    $form['actions']['submit']['#attributes']['class'] = ['media-library-select'];  }}/** * Implements hook_preprocess_HOOK() for links. */function claro_preprocess_links(&$variables) {  foreach ($variables['links'] as $links_item) {    if (!empty($links_item['link']) && !empty($links_item['link']['#url']) && $links_item['link']['#url'] instanceof Url) {      if ($links_item['link']['#url']->isRouted()) {        switch ($links_item['link']['#url']->getRouteName()) {          case 'system.theme_settings_theme':            $links_item['link'] = _claro_convert_link_to_action_link($links_item['link'], 'cog', 'small');            break;          case 'system.theme_uninstall':            $links_item['link'] = _claro_convert_link_to_action_link($links_item['link'], 'ex', 'small');            break;          case 'system.theme_set_default':          case 'system.theme_install':            $links_item['link'] = _claro_convert_link_to_action_link($links_item['link'], 'checkmark', 'small');            break;        }      }    }  }}/** * Converts a link render element to an action link. * * This helper merges every attributes from $link['#attributes'], from * $link['#options']['attributes'] and from the Url object's. * * @param array $link *   Link renderable array. * @param string|null $icon_name *   The name of the needed icon. When specified, a CSS class will be added with *   the following pattern: 'action-link--icon-[icon_name]'. If the needed icon *   is not implemented in CSS, no icon will be added. *   Currently available icons are: *    - checkmark, *    - cog, *    - ex, *    - plus, *    - trash. * @param string $size *   Name of the small action link variant. Defaults to 'default'. *   Supported sizes are: *    - default, *    - small, *    - extrasmall. * @param string $variant *   Variant of the action link. Supported variants are 'default' and 'danger'. *   Defaults to 'default'. * * @return array *   The link renderable converted to action link. */function _claro_convert_link_to_action_link(array $link, $icon_name = NULL, $size = 'default', $variant = 'default') {  // Early opt-out if we cannot do anything.  if (empty($link['#type']) || $link['#type'] !== 'link' || empty($link['#url'])) {    return $link;  }  // \Drupal\Core\Render\Element\Link::preRenderLink adds $link['#attributes']  // to $link[#options]['attributes'] if it is not empty, but it does not merges  // the 'class' subkey deeply.  // Because of this, when $link[#options]['attributes']['class'] is set, the  // classes defined in $link['#attributes']['class'] are ignored.  //  // To keep this behavior we repeat this for action-link, which means that this  // conversion happens a bit earlier. We unset $link['#attributes'] to prevent  // Link::preRenderLink() doing the same, because for action-links, that would  // be needless.  $link += ['#options' => []];  if (isset($link['#attributes'])) {    $link['#options'] += [      'attributes' => [],    ];    $link['#options']['attributes'] += $link['#attributes'];    unset($link['#attributes']);  }  $link['#options'] += ['attributes' => []];  $link['#options']['attributes'] += ['class' => []];  // Determine the needed (type) variant.  $variants_supported = ['default', 'danger'];  $variant = is_string($variant) && in_array($variant, $variants_supported) ? $variant : reset($variants_supported);  // Remove button, button modifier CSS classes and other unwanted ones.  $link['#options']['attributes']['class'] = array_diff($link['#options']['attributes']['class'], [    'button',    'button--action',    'button--primary',    'button--danger',    'button--small',    'button--extrasmall',    'link',  ]);  // Adding the needed CSS classes.  $link['#options']['attributes']['class'][] = 'action-link';  // Add the variant-modifier CSS class only if the variant is not the default.  if ($variant !== reset($variants_supported)) {    $link['#options']['attributes']['class'][] = Html::getClass("action-link--$variant");  }  // Add the icon modifier CSS class.  if (!empty($icon_name)) {    $link['#options']['attributes']['class'][] = Html::getClass("action-link--icon-$icon_name");  }  if ($size && in_array($size, ['small', 'extrasmall'])) {    $link['#options']['attributes']['class'][] = Html::getClass("action-link--$size");  }  // If the provided $link is an item of the 'links' theme function, then only  // the attributes of the Url object are processed during rendering.  $url_attributes = $link['#url']->getOption('attributes') ?: [];  $url_attributes = NestedArray::mergeDeep($url_attributes, $link['#options']['attributes']);  $link['#url']->setOption('attributes', $url_attributes);  return $link;}/** * Implements hook_form_BASE_FORM_ID_alter() for \Drupal\node\NodeForm. * * Changes vertical tabs to container. */function claro_form_node_form_alter(&$form, FormStateInterface $form_state) {  $form['#theme'] = ['node_edit_form'];  $form['#attached']['library'][] = 'claro/node-form';  $form['advanced']['#type'] = 'container';  $form['advanced']['#accordion'] = TRUE;  $form['meta']['#type'] = 'container';  $form['meta']['#access'] = TRUE;  $form['revision_information']['#type'] = 'container';  $form['revision_information']['#group'] = 'meta';  $form['revision_information']['#attributes']['class'][] = 'entity-meta__revision';}/** * Implements hook_form_BASE_FORM_ID_alter() for \Drupal\media\MediaForm. */function claro_form_media_form_alter(&$form, FormStateInterface $form_state) {  // Only attach CSS from core if this form comes from Media core, and not from  // the contrib Media Entity 1.x branch.  if (\Drupal::moduleHandler()->moduleExists('media') && $form_state->getFormObject() instanceof MediaForm) {    // @todo Revisit after https://www.drupal.org/node/2892304 is in. It    // introduces a footer region to these forms which will allow for us to    // display a top border over the published checkbox by defining a    // media-edit-form.html.twig template the same way node does.    $form['#attached']['library'][] = 'claro/media-form';  }}/** * Implements hook_views_ui_display_top_alter(). */function claro_views_ui_display_top_alter(&$element) {  // @todo remove this after https://www.drupal.org/node/3051605 has been  //   solved.  $element['tabs']['#prefix'] = preg_replace('/(class="(.+\s)?)tabs(\s.+"|")/', '$1views-tabs$3', $element['tabs']['#prefix']);  $element['tabs']['#prefix'] = preg_replace('/(class="(.+\s)?)secondary(\s.+"|")/', '$1views-tabs--secondary$3', $element['tabs']['#prefix']);  foreach (Element::children($element['tabs']) as $tab) {    $element['tabs'][$tab]['#theme'] = 'menu_local_task__views_ui';  }  // Change top extra actions to use the small dropbutton variant.  // @todo Revisit after https://www.drupal.org/node/3057581 is added.  if (!empty($element['extra_actions'])) {    $element['extra_actions']['#dropbutton_type'] = 'small';  }}/** * Implements hook_views_ui_display_tab_alter(). */function claro_views_ui_display_tab_alter(&$element) {  // We process the dropbutton-like element on views edit form's  // display settings top section.  //  // That element should be a regular Dropbutton.  //  // After that the reported issue is fixed and the element is rendered with  // the Dropbutton type, we just have to set it's '#dropbutton_type' to  // 'extrasmall'.  //  // @todo: revisit after https://www.drupal.org/node/3057577 is fixed.  $dummy_dropbutton = &$element['details']['top']['actions'];  if ($dummy_dropbutton) {    $child_keys = Element::children($dummy_dropbutton);    $prefix_regex = '/(<.*class\s*= *["\']?)([^"\']*)(.*)/i';    $child_count = 0;    foreach ($child_keys as $key) {      if (in_array($key, ['prefix', 'suffix'])) {        continue;      }      $nested_child_keys = Element::children($dummy_dropbutton[$key], TRUE);      if (!empty($nested_child_keys)) {        foreach ($nested_child_keys as $nested_key) {          $child_count++;          $prefix = $dummy_dropbutton[$key][$nested_key]['#prefix'];          $dummy_dropbutton[$key][$nested_key]['#prefix'] = preg_replace($prefix_regex, '$1$2 dropbutton__item dropbutton__item--extrasmall$3', $prefix);        }      }      else {        $child_count++;        $prefix = $dummy_dropbutton[$key]['#prefix'];        $dummy_dropbutton[$key]['#prefix'] = preg_replace($prefix_regex, '$1$2 dropbutton__item dropbutton__item--extrasmall$3', $prefix);      }    }    if (!empty($dummy_dropbutton['prefix']) && !empty($dummy_dropbutton['prefix']['#markup'])) {      $classes = 'dropbutton--extrasmall ';      $classes .= ($child_count > 1) ? 'dropbutton--multiple' : 'dropbutton--single';      $prefix = $dummy_dropbutton['prefix']['#markup'];      $dummy_dropbutton['prefix']['#markup'] = preg_replace($prefix_regex, '$1$2 ' . $classes . '$3', $prefix);    }  }}/** * Implements hook_preprocess_HOOK for views_exposed_form. */function claro_preprocess_views_exposed_form(&$variables) {  $form = &$variables['form'];  // Add BEM classes for items in the form.  // Sorted keys.  $child_keys = Element::children($form, TRUE);  $last_key = NULL;  $child_before_actions_key = NULL;  foreach ($child_keys as $child_key) {    if (!empty($form[$child_key]['#type'])) {      if ($form[$child_key]['#type'] === 'actions') {        // We need the key of the element that precedes the actions element.        $child_before_actions_key = $last_key;        $form[$child_key]['#attributes']['class'][] = 'views-exposed-form__item';        $form[$child_key]['#attributes']['class'][] = 'views-exposed-form__item--actions';      }      if (!in_array($form[$child_key]['#type'], ['hidden', 'actions'])) {        $form[$child_key]['#wrapper_attributes']['class'][] = 'views-exposed-form__item';        $last_key = $child_key;      }    }  }  if ($child_before_actions_key) {    // Add a modifier class to the item that precedes the form actions.    $form[$child_before_actions_key]['#wrapper_attributes']['class'][] = 'views-exposed-form__item--preceding-actions';  }}/** * Implements hook_form_FORM_ID_alter() for views_exposed_form. */function claro_form_views_exposed_form_alter(&$form, FormStateInterface $form_state) {  $view = $form_state->getStorage()['view'];  $view_title = $view->getTitle();  // Add a label so screenreaders can identify the purpose of the exposed form  // without having to scan content that appears further down the page.  $form['#attributes']['aria-label'] = t('Filter the contents of the %view_title view', ['%view_title' => $view_title]);}/** * Implements hook_preprocess_form_element(). */function claro_preprocess_form_element(&$variables) {  if (!empty($variables['element']['#errors'])) {    $variables['label']['#attributes']['class'][] = 'has-error';  }  if ($variables['disabled']) {    $variables['label']['#attributes']['class'][] = 'is-disabled';    if (!empty($variables['description']['attributes'])) {      $variables['description']['attributes']->addClass('is-disabled');    }  }}/** * Implements template_preprocess_HOOK() for input. */function claro_preprocess_input(&$variables) {  if (    !empty($variables['element']['#title_display']) &&    $variables['element']['#title_display'] === 'attribute' &&    !empty((string) $variables['element']['#title'])  ) {    $variables['attributes']['title'] = (string) $variables['element']['#title'];  }  $type_api = $variables['element']['#type'];  $type_html = $variables['attributes']['type'];  $text_types_html = [    'text',    'email',    'tel',    'number',    'search',    'password',    'date',    'time',    'file',    'color',    'datetime-local',    'url',    'month',    'week',  ];  if (in_array($type_html, $text_types_html)) {    $variables['attributes']['class'][] = 'form-element';    $variables['attributes']['class'][] = Html::getClass('form-element--type-' . $type_html);    $variables['attributes']['class'][] = Html::getClass('form-element--api-' . $type_api);    if (!empty($variables['element']['#autocomplete_route_name'])) {      $variables['autocomplete_message'] = t('Loading…');    }  }  if (in_array($type_html, ['checkbox', 'radio'])) {    $variables['attributes']['class'][] = 'form-boolean';    $variables['attributes']['class'][] = Html::getClass('form-boolean--type-' . $type_html);  }}/** * Implements template_preprocess_HOOK() for textarea. */function claro_preprocess_textarea(&$variables) {  $variables['attributes']['class'][] = 'form-element';  $variables['attributes']['class'][] = 'form-element--type-textarea';  $variables['attributes']['class'][] = 'form-element--api-textarea';}/** * Implements template_preprocess_HOOK() for select. */function claro_preprocess_select(&$variables) {  if (!empty($variables['element']['#title_display']) && $variables['element']['#title_display'] === 'attribute' && !empty((string) $variables['element']['#title'])) {    $variables['attributes']['title'] = (string) $variables['element']['#title'];  }  $variables['attributes']['class'][] = 'form-element';  $variables['attributes']['class'][] = $variables['element']['#multiple'] ?    'form-element--type-select-multiple' :    'form-element--type-select';  if (in_array('block-region-select', $variables['attributes']['class'])) {    $variables['attributes']['class'][] = 'form-element--extrasmall';  }}/** * Implements template_preprocess_HOOK() for datetime_wrapper. */function claro_preprocess_datetime_wrapper(&$variables) {  if (!empty($variables['element']['#errors'])) {    $variables['title_attributes']['class'][] = 'has-error';  }  if (!empty($variables['element']['#disabled'])) {    $variables['title_attributes']['class'][] = 'is-disabled';    if (!empty($variables['description_attributes'])) {      $variables['description_attributes']->addClass('is-disabled');    }  }}/** * Implements template_preprocess_HOOK() for fieldset. */function claro_preprocess_fieldset(&$variables) {  $element = $variables['element'];  $composite_types = ['checkboxes', 'radios'];  if (!empty($element['#type']) && in_array($element['#type'], $composite_types) && !empty($variables['element']['#children_errors'])) {    $variables['legend_span']['attributes']->addClass('has-error');  }  if (!empty($element['#disabled'])) {    $variables['legend_span']['attributes']->addClass('is-disabled');    if (!empty($variables['description']) && !empty($variables['description']['attributes'])) {      $variables['description']['attributes']->addClass('is-disabled');    }  }  // Remove 'container-inline' class from the main attributes and add a flag  // instead.  // @todo remove this after https://www.drupal.org/node/3059593 has been  //   resolved.  if (!empty($variables['attributes']['class'])) {    $container_inline_key = array_search('container-inline', $variables['attributes']['class']);    if ($container_inline_key !== FALSE) {      unset($variables['attributes']['class'][$container_inline_key]);      $variables['inline_items'] = TRUE;    }  }}/** * Implements hook_preprocess_HOOK() for field_multiple_value_form. */function claro_preprocess_field_multiple_value_form(&$variables) {  // Make disabled available for the template.  $variables['disabled'] = !empty($variables['element']['#disabled']);  if ($variables['multiple']) {    // Add an additional CSS class for the field label table cell.    // This repeats the logic of template_preprocess_field_multiple_value_form()    // without using '#prefix' and '#suffix' for the wrapper element.    //    // If the field is multiple, we don't have to check the existence of the    // table header cell.    //    // @see template_preprocess_field_multiple_value_form().    $header_attributes = ['class' => ['form-item__label', 'form-item__label--multiple-value-form']];    if (!empty($variables['element']['#required'])) {      $header_attributes['class'][] = 'js-form-required';      $header_attributes['class'][] = 'form-required';    }    // Using array_key_first() for addressing the first header cell would be    // more elegant here, but we can rely on the related theme.inc preprocess.    $variables['table']['#header'][0]['data'] = [      '#type' => 'html_tag',      '#tag' => 'h4',      '#value' => $variables['element']['#title'],      '#attributes' => $header_attributes,    ];    if ($variables['disabled']) {      $variables['table']['#attributes']['class'][] = 'tabledrag-disabled';      $variables['table']['#attributes']['class'][] = 'js-tabledrag-disabled';      // We will add the 'is-disabled' CSS class to the disabled table header      // cells.      $header_attributes['class'][] = 'is-disabled';      foreach ($variables['table']['#header'] as &$cell) {        if (is_array($cell) && isset($cell['data'])) {          $cell = $cell + ['class' => []];          $cell['class'][] = 'is-disabled';        }        else {          // We have to modify the structure of this header cell.          $cell = [            'data' => $cell,            'class' => ['is-disabled'],          ];        }      }    }    // Make add-more button smaller.    if (!empty($variables['button'])) {      $variables['button']['#attributes']['class'][] = 'button--small';    }  }}/** * Implements hook_preprocess_HOOK() for form_element__password_confirm. */function claro_preprocess_form_element__password_confirm(&$variables) {  // Add CSS classes needed for theming the password confirm widget.  $variables['attributes']['class'][] = 'password-confirm';  $variables['attributes']['class'][] = 'is-initial';  $variables['attributes']['class'][] = 'is-password-empty';  $variables['attributes']['class'][] = 'is-confirm-empty';}/** * Implements hook_preprocess_HOOK() for form_element__password. */function claro_preprocess_form_element__password(&$variables) {  if (!empty($variables['element']['#array_parents']) && in_array('pass1', $variables['element']['#array_parents'])) {    // This is the main password form element.    $variables['attributes']['class'][] = 'password-confirm__password';  }  if (!empty($variables['element']['#array_parents']) && in_array('pass2', $variables['element']['#array_parents'])) {    // This is the password confirm form element.    $variables['attributes']['class'][] = 'password-confirm__confirm';  }}/** * Implements template_preprocess_HOOK() for filter_tips. */function claro_preprocess_filter_tips(&$variables) {  $variables['#attached']['library'][] = 'filter/drupal.filter';}/** * Implements template_preprocess_HOOK() for table. */function claro_preprocess_table(&$variables) {  // Adding table sort indicator CSS class for inactive sort link.  // @todo Revisit after https://www.drupal.org/node/3025726 or  // https://www.drupal.org/node/1973418 is in.  if (!empty($variables['header'])) {    foreach ($variables['header'] as &$header_cell) {      // For 8.6.x and below.      // @todo Remove this after 8.6.x is out of support.      if ($header_cell['content'] instanceof GeneratedLink) {        $dom_doc = Html::load($header_cell['content']->getGeneratedLink());        $anchors = $dom_doc->getElementsByTagName('a');        if (!empty($anchors)) {          foreach ($anchors as $anchor) {            $anchor_href = $anchor->getAttribute('href');            $parsed_url = UrlHelper::parse($anchor_href);            $query = !empty($parsed_url['query']) ? $parsed_url['query'] : [];            if (isset($query['order']) && isset($query['sort'])) {              $header_cell['attributes']->addClass('sortable-heading');            }          }        }      }      // For 8.7.x and above.      if ($header_cell['content'] instanceof Link) {        $query = $header_cell['content']->getUrl()->getOption('query') ?: [];        if (isset($query['order']) && isset($query['sort'])) {          $header_cell['attributes']->addClass('sortable-heading');        }      }    }  }  // Mark the whole table and the first cells if rows are draggable.  if (!empty($variables['rows'])) {    $draggable_row_found = FALSE;    foreach ($variables['rows'] as &$row) {      /** @var \Drupal\Core\Template\Attribute $row['attributes'] */      if (!empty($row['attributes']) && $row['attributes']->hasClass('draggable')) {        if (!$draggable_row_found) {          $variables['attributes']['class'][] = 'draggable-table';          $draggable_row_found = TRUE;        }        reset($row['cells']);        $first_cell_key = key($row['cells']);        // The 'attributes' key is always here and it is an        // \Drupal\Core\Template\Attribute.        // @see template_preprocess_table();        $row['cells'][$first_cell_key]['attributes']->addClass('tabledrag-cell');        // Check that the first cell is empty or not.        if (empty($row['cells'][$first_cell_key]) || empty($row['cells'][$first_cell_key]['content'])) {          $row['cells'][$first_cell_key]['attributes']->addClass('tabledrag-cell--only-drag');        }      }    }  }}/** * Implements template_preprocess_HOOK() for field_ui_table. */function claro_preprocess_field_ui_table(&$variables) {  claro_preprocess_table($variables);}/** * Implements template_preprocess_HOOK() for views_view_table. * * @todo Revisit after https://www.drupal.org/node/3025726 or * https://www.drupal.org/node/1973418 is in. */function claro_preprocess_views_view_table(&$variables) {  if (!empty($variables['header'])) {    foreach ($variables['header'] as &$header_cell) {      if (!empty($header_cell['url'])) {        $parsed_url = UrlHelper::parse($header_cell['url']);        $query = !empty($parsed_url['query']) ? $parsed_url['query'] : [];        if (isset($query['order']) && isset($query['sort'])) {          $header_cell['attributes']->addClass('sortable-heading');        }      }    }  }}/** * Implements hook_preprocess_HOOK() for links__dropbutton__operations. * * Operations always use the extra small dropbutton variant. */function claro_preprocess_links__dropbutton__operations(&$variables) {  $item_classes = ['dropbutton__item', 'dropbutton__item--extrasmall'];  $variables['attributes']['class'][] = 'dropbutton--extrasmall';  foreach ($variables['links'] as &$link_data) {    $link_data['attributes']->addClass($item_classes);  }}/** * Implements hook_preprocess_HOOK() for links__dropbutton. */function claro_preprocess_links__dropbutton(&$variables) {  // Add the right CSS class for the dropbutton list that helps reducing FOUC.  if (!empty($variables['links'])) {    $variables['attributes']['class'][] = count($variables['links']) > 1      ? 'dropbutton--multiple'      : 'dropbutton--single';  }  $item_classes = ['dropbutton__item'];  // Check that the dropbutton has a supported variant class.  // @todo Revisit after https://www.drupal.org/node/3057581 is added.  if (!empty($variables['attributes']['class'])) {    if (array_search('dropbutton--small', $variables['attributes']['class'])) {      $item_classes[] = 'dropbutton__item--small';    }    elseif (array_search('dropbutton--extrasmall', $variables['attributes']['class'])) {      $item_classes[] = 'dropbutton__item--extrasmall';    }  }  foreach ($variables['links'] as &$link_data) {    $link_data['attributes']->addClass($item_classes);  }}/** * Implements hook_preprocess_HOOK() for views_ui_display_tab_bucket. */function claro_preprocess_views_ui_display_tab_bucket(&$variables) {  // Instead of re-styling Views UI dropbuttons with module-specific CSS styles,  // change dropbutton variants to the extra small version.  // @todo Revisit after https://www.drupal.org/node/3057581 is added.  if (!empty($variables['actions']) && $variables['actions']['#type'] === 'dropbutton') {    $variables['actions']['#dropbutton_type'] = 'extrasmall';  }}/** * Implements hook_preprocess_HOOK() for status_messages. */function claro_preprocess_status_messages(&$variables) {  $variables['title_ids'] = [];  foreach ($variables['message_list'] as $message_type => $messages) {    $variables['title_ids'][$message_type] = Html::getUniqueId("message-$message_type-title");  }}/** * Implements hook_preprocess_HOOK() for system_themes_page. */function claro_preprocess_system_themes_page(&$variables) {  if (!empty($variables['theme_groups'])) {    foreach ($variables['theme_groups'] as &$theme_group) {      if (!empty($theme_group['themes'])) {        foreach ($theme_group['themes'] as &$theme_card) {          /**           * @todo Remove dependency on attributes after           *   https://www.drupal.org/project/drupal/issues/2511548 has been           *   resolved.           */          if (isset($theme_card['screenshot']['#attributes']) && $theme_card['screenshot']['#attributes'] instanceof Attribute && $theme_card['screenshot']['#attributes']->hasClass('no-screenshot')) {            unset($theme_card['screenshot']);          }          $theme_card['title_id'] = Html::getUniqueId($theme_card['name'] . '-label');          $description_is_empty = empty((string) $theme_card['description']);          // Set description_id only if the description is not empty.          if (!$description_is_empty) {            $theme_card['description_id'] = Html::getUniqueId($theme_card['name'] . '-description');          }          if (!empty($theme_card['operations']) && !empty($theme_card['operations']['#theme']) && $theme_card['operations']['#theme'] === 'links') {            $theme_card['operations']['#theme'] = 'links__action_links';          }        }      }    }  }}/** * Implements hook_preprocess_HOOK() for links__action_links. */function claro_preprocess_links__action_links(&$variables) {  $variables['attributes']['class'][] = 'action-links';  foreach ($variables['links'] as $delta => $link_item) {    $variables['links'][$delta]['attributes']->addClass('action-links__item');  }}/** * Implements hook_preprocess_HOOK() for file_managed_file. */function claro_preprocess_file_managed_file(&$variables) {  // Produce the same renderable element structure as image widget has.  $child_keys = Element::children($variables['element']);  foreach ($child_keys as $child_key) {    $variables['data'][$child_key] = $variables['element'][$child_key];  }  _claro_preprocess_file_and_image_widget($variables);}/** * Implements hook_preprocess_HOOK() for file_widget_multiple. */function claro_preprocess_file_widget_multiple(&$variables) {  $has_upload = FALSE;  if (isset($variables['table']['#type']) && $variables['table']['#type'] === 'table') {    // Add a variant class for the table.    $variables['table']['#attributes']['class'][] = 'table-file-multiple-widget';    // Mark table disabled if the field widget is disabled.    if (isset($variables['element']['#disabled']) && $variables['element']['#disabled']) {      $variables['table']['#attributes']['class'][] = 'tabledrag-disabled';      $variables['table']['#attributes']['class'][] = 'js-tabledrag-disabled';      // We will add the 'is-disabled' CSS class to the disabled table header      // cells.      foreach ($variables['table']['#header'] as &$cell) {        if (is_array($cell) && isset($cell['data'])) {          $cell = $cell + ['class' => []];          $cell['class'][] = 'is-disabled';        }        else {          // We have to modify the structure of this header cell.          $cell = [            'data' => $cell,            'class' => ['is-disabled'],          ];        }      }    }    // Mark operations column cells with a CSS class.    if (isset($variables['table']['#rows']) && is_array($variables['table']['#rows'])) {      foreach ($variables['table']['#rows'] as $row_key => $row) {        if (isset($row['data']) && is_array($row['data'])) {          $last_cell = end($row['data']);          $last_cell_key = key($row['data']);          if (is_array($last_cell['data'])) {            foreach ($last_cell['data'] as $last_cell_item) {              if (isset($last_cell_item['#attributes']['class']) && is_array($last_cell_item['#attributes']['class']) && in_array('remove-button', $last_cell_item['#attributes']['class'])) {                $variables['table']['#rows'][$row_key]['data'][$last_cell_key] += ['class' => []];                $variables['table']['#rows'][$row_key]['data'][$last_cell_key]['class'][] = 'file-operations-cell';                break 1;              }            }          }        }      }    }    // Add a CSS class to the table if an upload widget is present.    // This is required for removing the border of the last table row.    if (!empty($variables['element'])) {      $element_keys = Element::children($variables['element']);      foreach ($element_keys as $delta) {        if (!isset($variables['element'][$delta]['upload']['#access']) || $variables['element'][$delta]['upload']['#access'] !== FALSE) {          $has_upload = TRUE;          break 1;        }      }    }    $variables['table']['#attributes']['class'][] = $has_upload ? 'table-file-multiple-widget--has-upload' : 'table-file-multiple-widget--no-upload';  }  $table_is_not_empty = isset($variables['table']['#rows']) && !empty($variables['table']['#rows']);  $table_is_accessible = !isset($variables['table']['#access']) || (isset($variables['table']['#access']) && $variables['table']['#access'] !== FALSE);  $variables['has_table'] = $table_is_not_empty && $table_is_accessible;}/** * Implements hook_preprocess_HOOK() for image_widget. */function claro_preprocess_image_widget(&$variables) {  // Stable adds the file size as #suffix for image file_link renderable array.  // We have to remove that because we will render it in our file_link template  // for every kind of files, and not just for images.  if (!empty($variables['element']['fids']['#value'])) {    $file = reset($variables['element']['#files']);    unset($variables['data']['file_' . $file->id()]['filename']['#suffix']);  }  _claro_preprocess_file_and_image_widget($variables);}/** * Helper pre-process callback for file_managed_file and image_widget. * * @param array $variables *   The renderable array of image and file widgets, with 'element' and 'data' *   keys. */function _claro_preprocess_file_and_image_widget(array &$variables) {  $element = $variables['element'];  $main_item_keys = [    'upload',    'upload_button',    'remove_button',  ];  // Calculate helper values for the template.  $upload_is_accessible = !isset($element['upload']['#access']) || $element['upload']['#access'] !== FALSE;  $is_multiple = !empty($element['#cardinality']) && $element['#cardinality'] !== 1;  $has_value = isset($element['#value']['fids']) && !empty($element['#value']['fids']);  // File widget properties.  $display_can_be_displayed = !empty($element['#display_field']);  // Display is rendered in a separate table cell for multiple value widgets.  $display_is_visible = $display_can_be_displayed && !$is_multiple && isset($element['display']['#type']) && $element['display']['#type'] !== 'hidden';  $description_can_be_displayed = !empty($element['#description_field']);  $description_is_visible = $description_can_be_displayed && isset($element['description']);  // Image widget properties.  $alt_can_be_displayed = !empty($element['#alt_field']);  $alt_is_visible = $alt_can_be_displayed && (!isset($element['alt']['#access']) || $element['alt']['#access'] !== FALSE);  $title_can_be_displayed = !empty($element['#title_field']);  $title_is_visible = $title_can_be_displayed && (!isset($element['title']['#access']) || $element['title']['#access'] !== FALSE);  $variables['multiple'] = $is_multiple;  $variables['upload'] = $upload_is_accessible;  $variables['has_value'] = $has_value;  $variables['has_meta'] = $alt_is_visible || $title_is_visible || $display_is_visible || $description_is_visible;  $variables['display'] = $display_is_visible;  // Render file upload input and upload button (or file name and remove button,  // if the field is not empty) in an emphasized div.  foreach ($variables['data'] as $key => $item) {    $item_is_filename = isset($item['filename']['#file']) && $item['filename']['#file'] instanceof FileInterface;    // Move filename to main items.    if ($item_is_filename) {      $variables['main_items']['filename'] = $item;      unset($variables['data'][$key]);      continue;    }    // Move buttons, upload input and hidden items to main items.    if (in_array($key, $main_item_keys)) {      $variables['main_items'][$key] = $item;      unset($variables['data'][$key]);    }  }}/** * Implements hook_preprocess_views_view_fields(). * * This targets each rendered media item in the grid display of the media * library's modal dialog. */function claro_preprocess_views_view_fields__media_library(array &$variables) {  // Add classes to media rendered entity field so it can be targeted for  // styling. Adding this class in a template is very difficult to do.  if (isset($variables['fields']['rendered_entity']->wrapper_attributes)) {    $variables['fields']['rendered_entity']->wrapper_attributes->addClass('media-library-item__click-to-select-trigger');  }}/** * Implements hook_form_BASE_FORM_ID_alter(). */function claro_form_media_library_add_form_alter(array &$form, FormStateInterface $form_state) {  $form['#attributes']['class'][] = 'media-library-add-form';  $form['#attached']['library'][] = 'claro/media_library.theme';  // If there are unsaved media items, apply styling classes to various parts  // of the form.  if (isset($form['media'])) {    $form['#attributes']['class'][] = 'media-library-add-form--with-input';    // Put a wrapper around the informational message above the unsaved media    // items.    $form['description']['#template'] = '<p class="media-library-add-form__description">{{ text }}</p>';  }  else {    $form['#attributes']['class'][] = 'media-library-add-form--without-input';  }}/** * Implements hook_form_FORM_ID_alter(). */function claro_form_media_library_add_form_upload_alter(array &$form, FormStateInterface $form_state) {  $form['#attributes']['class'][] = 'media-library-add-form--upload';  if (isset($form['container'])) {    $form['container']['#attributes']['class'][] = 'media-library-add-form__input-wrapper';  }}/** * Implements hook_form_FORM_ID_alter(). */function claro_form_media_library_add_form_oembed_alter(array &$form, FormStateInterface $form_state) {  $form['#attributes']['class'][] = 'media-library-add-form--oembed';  // If no media items have been added yet, add a couple of styling classes  // to the initial URL form.  if (isset($form['container'])) {    $form['container']['#attributes']['class'][] = 'media-library-add-form__input-wrapper';    $form['container']['url']['#attributes']['class'][] = 'media-library-add-form-oembed-url';    $form['container']['submit']['#attributes']['class'][] = 'media-library-add-form-oembed-submit';  }}/** * Implements hook_preprocess_item_list__media_library_add_form_media_list(). * * This targets each new, unsaved media item added to the media library, before * they are saved. */function claro_preprocess_item_list__media_library_add_form_media_list(array &$variables) {  foreach ($variables['items'] as &$item) {    $item['value']['preview']['#attributes']['class'][] = 'media-library-add-form__preview';    $item['value']['fields']['#attributes']['class'][] = 'media-library-add-form__fields';    $item['value']['remove_button']['#attributes']['class'][] = 'media-library-add-form__remove-button';    // #source_field_name is set by AddFormBase::buildEntityFormElement()    // to help themes and form_alter hooks identify the source field.    $fields = &$item['value']['fields'];    $source_field_name = $fields['#source_field_name'];    if (isset($fields[$source_field_name])) {      $fields[$source_field_name]['#attributes']['class'][] = 'media-library-add-form__source-field';    }  }}/** * Implements hook_preprocess_media_library_item__widget(). * * This targets each media item selected in an entity reference field. */function claro_preprocess_media_library_item__widget(array &$variables) {  $variables['content']['remove_button']['#attributes']['class'][] = 'media-library-item__remove';}/** * Implements hook_preprocess_media_library_item__small(). * * This targets each pre-selected media item selected when adding new media in * the modal media library dialog. */function claro_preprocess_media_library_item__small(array &$variables) {  $variables['content']['select']['#attributes']['class'][] = 'media-library-item__click-to-select-checkbox';}/** * @todo Remove this when https://www.drupal.org/project/drupal/issues/2999549 * lands. * * @see \Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget::formElement() */function claro_preprocess_fieldset__media_library_widget(array &$variables) {  if (isset($variables['prefix']['weight_toggle'])) {    $variables['prefix']['weight_toggle']['#attributes']['class'][] = 'media-library-widget__toggle-weight';  }  if (isset($variables['suffix']['open_button'])) {    $variables['suffix']['open_button']['#attributes']['class'][] = 'media-library-open-button';  }}/** * Implements hook_views_pre_render(). */function claro_views_pre_render(ViewExecutable $view) {  $add_classes = function (&$option, array $classes_to_add) {    $classes = preg_split('/\s+/', $option);    $classes = array_filter($classes);    $classes = array_merge($classes, $classes_to_add);    $option = implode(' ', array_unique($classes));  };  if ($view->id() === 'media_library') {    if ($view->display_handler->options['defaults']['css_class']) {      $add_classes($view->displayHandlers->get('default')->options['css_class'], ['media-library-view']);    }    else {      $add_classes($view->display_handler->options['css_class'], ['media-library-view']);    }    if ($view->current_display === 'page') {      if (array_key_exists('media_bulk_form', $view->field)) {        $add_classes($view->field['media_bulk_form']->options['element_class'], ['media-library-item__click-to-select-checkbox']);      }      if (array_key_exists('rendered_entity', $view->field)) {        $add_classes($view->field['rendered_entity']->options['element_class'], ['media-library-item__content']);      }      if (array_key_exists('edit_media', $view->field)) {        $add_classes($view->field['edit_media']->options['alter']['link_class'], ['media-library-item__edit']);      }      if (array_key_exists('delete_media', $view->field)) {        $add_classes($view->field['delete_media']->options['alter']['link_class'], ['media-library-item__remove']);      }    }    elseif (strpos($view->current_display, 'widget') === 0) {      if (array_key_exists('rendered_entity', $view->field)) {        $add_classes($view->field['rendered_entity']->options['element_class'], ['media-library-item__content']);      }      if (array_key_exists('media_library_select_form', $view->field)) {        $add_classes($view->field['media_library_select_form']->options['element_wrapper_class'], ['media-library-item__click-to-select-checkbox']);      }      if ($view->display_handler->options['defaults']['css_class']) {        $add_classes($view->displayHandlers->get('default')->options['css_class'], ['media-library-view--widget']);      }      else {        $add_classes($view->display_handler->options['css_class'], ['media-library-view--widget']);      }    }  }}
 |