io-8.x-1.x-dev/modules/io_browser/templates/io_browser.theme.inc
modules/io_browser/templates/io_browser.theme.inc
<?php
/**
* @file
* Hooks and preprocess functions for the IO Browser module.
*/
use Drupal\Core\Render\Element;
use Drupal\Core\StringTranslation\ByteSizeMarkup;
use Drupal\Component\Utility\DeprecationHelper;
use Drupal\io_browser\IoBrowserDefault as Defaults;
use Drupal\io_browser\IoBrowserUtil;
/**
* Prepares variables for io-browser.html.twig templates.
*
* @todo Core image, Focal Point, Crop integration outside EB.
*/
function template_preprocess_io_browser(&$variables) {
$element = $variables['element'];
// Pre 2.10 only used variables were added, now many in case needed.
foreach (Defaults::themeProperties() as $key => $default) {
$k = $key == 'items' ? 'items' : "#$key";
$variables[$key] = $element[$k] ?? $default;
}
$widgets = $build = $draggables = $thumbs = [];
$content = &$variables['content'];
$settings = &$variables['settings'];
$settings += Defaults::htmlSettings();
$formatter = io_browser()->formatter();
$blazies = $formatter->verifySafely($settings);
$ib = $settings['iobrowsers'];
// Get our list of widgets in order (needed when the form comes back after
// preview or failed validation).
// Image is stored as indexed children, media as items.
$items = empty($variables['items']) ? $element : $variables['items'];
foreach (Element::children($items) as $key) {
$widgets[] = &$items[$key];
}
// Sort the items if required.
if ($ib->get('widget.plugin_id') != 'entity_browser_entity_reference') {
usort($widgets, '_field_multiple_value_form_sort_helper');
}
// Provides basic settings.
$count = count($widgets);
$uploading = isset($element['#file_upload_delta']);
$element_id = $element['#id'];
$settings['count'] = $count;
$blazies->set('count', $count)
->set('css.element_id', $element_id);
$ib->set('is.uploading', $uploading);
// Massage settings.
_io_browser_massage_settings($variables, $settings);
foreach ($widgets as $delta => &$widget) {
$sets = $settings;
$sets['delta'] = $delta;
// EB uses 'display' while core Image 'preview', get them consistent here.
if (isset($widget['display'])) {
$widget['preview'] = $widget['display'];
unset($widget['display']);
}
if ($blazies->is('eb')) {
$widget_classes = $widget['#attributes']['class'] ?? [];
if ($widget_classes and is_array($widget_classes)) {
$classes = ['item-container', 'draggable'];
$widget['#attributes']['class'] = array_diff($widget_classes, $classes);
}
}
else {
// Save the uploading row for last, for image widget, not entity browser.
if (isset($widget['#files']) && empty($widget['#files'])) {
if ($title = ($element['#file_upload_title'] ?? NULL)) {
$widget['#title'] = $title;
$desc = $element['#file_upload_description'];
// @todo use directly renderInIsolation() post blazy:2.28|3.0.6.
if (method_exists($formatter, 'renderInIsolation')) {
$widget['#description'] = $formatter->renderInIsolation($desc);
}
else {
/* @phpstan-ignore-next-line */
$widget['#description'] = $formatter->renderer()->renderPlain($desc);
}
}
foreach (Element::children($widget) as $sub_key) {
if (!in_array($sub_key, ['display', 'fids', 'upload', 'upload_button'])) {
unset($widget[$sub_key]);
}
}
continue;
}
}
// Masages widgets.
_io_browser_prepare_widget_item($widget, $sets, $delta);
_io_browser_build_widget_item($widget, $sets, $delta);
// Build the widget slides.
$build['items'][] = $widget;
// We don't do early rendering, hide em all now.
foreach (Element::children($widget) as $sub_key) {
hide($widget[$sub_key]);
}
// Place _weight and thumb into sortable element.
if ($ib->is('sortable')) {
$draggables[] = _io_browser_get_draggable($widget, $sets);
}
}
// Pass variables to template.
$content['header'] = $content['main'] = [];
// Build buttons.
$buttons = _io_browser_get_buttons($settings);
// Build draggable.
$content['draggable'] = _io_browser_build_draggables($buttons, $draggables);
$build['#settings'] = $settings;
if ($items = ($build['items'] ?? [])) {
// Provide Blazy attributes for the containing element.
$first = $items[0]['preview'] ?? [];
if ($_uri = ($first['#uri'] ?? NULL)) {
$blazies->set('first.uri', $_uri);
}
$variables['attributes']['data-blazy'] = '';
$style = $settings['style'] ?? '';
if ($style) {
$attrs['class'][] = 'ib__grid';
$item_attrs['class'][] = 'ib__sortitem';
$item_attrs['class'][] = 'grid--ib';
if ($style != 'single') {
$attrs['class'][] = 'ib__sortable';
}
$blazies->set('grid.attributes', $attrs);
$blazies->set('grid.item_attributes', $item_attrs);
}
$content['main'] = $formatter->build($build);
$content['header'] = _io_browser_build_buttons($buttons);
}
unset($element['#theme_wrappers']);
$variables['blazies'] = $settings['blazies']->storage();
$variables['ib'] = $settings['iobrowsers']->storage();
}
/**
* Massages settings.
*/
function _io_browser_massage_settings(array &$variables, array &$settings) {
$blazies = $settings['blazies'];
$ib = $settings['iobrowsers'];
// This is because empty file upload is counted one.
$count = $blazies->get('count') ?: 0;
$cardinality = $blazies->get('field.cardinality') ?: 0;
$field_name = $blazies->get('field.name');
$multiple = $cardinality != 1;
if ($ib->is('uploading')) {
$count = $count > 1 ? ($count - 1) : $count;
}
if (!$multiple) {
$count = 1;
}
$nav = !empty($settings['optionset_thumbnail']) && $count > 1 && $multiple;
$field_name_css = str_replace('_', '-', $field_name);
// Vanilla is on, rebuild own logic to support asnavfor as needed.
$settings['id'] = $id = 'ib-' . $field_name_css;
$is_text = !empty($settings['alt_field']) || !empty($settings['title_field']);
$grids = ['column', 'grid', 'flex', 'nativegrid'];
$blazies->set('css.id', $id)
->set('is.blazy', TRUE)
->set('is.vanilla', TRUE)
->set('is.grid', FALSE)
->set('is.noscript', FALSE)
->set('is.nav', $nav)
->set('item.id', 'ib')
->set('lazy.id', 'blazy')
->set('count', $count);
$ib->set('is.text', $is_text)
->set('is.widget', TRUE);
if (in_array($settings['style'], $grids)) {
$blazies->set('is.grid', TRUE);
// Provides sensible defaults for the ignorant who doesn't provide Grid as
// otherwise confusingly invisible items.
if (empty($settings['grid'])) {
$settings['grid'] = 3;
$settings['grid_medium'] = 2;
$settings['grid_small'] = 1;
}
}
// Pass the settings to template.
$element_id = $blazies->get('css.element_id');
$weight_class = $element_id . '-weight';
$sortable = empty($settings['grid']) && $count > 1 && $multiple;
$ib->set('is.sortable', $sortable);
$ib->set('weight_class', $weight_class);
// Update attributes.
$ibend = $ib->is('uploading') ? ($count - 2) : ($count - 1);
if ($count > 1) {
$variables['attributes']['data-end'] = $ibend;
}
$ib->set('end', $ibend);
// Defines ID if not provided.
$variables['attributes']['id'] = $element_id;
$variables['attributes']['data-drupal-selector'] = $element_id;
}
/**
* Massages widget value.
*/
function _io_browser_prepare_widget_item(array &$widget, array $settings, $delta) {
$blazies = $settings['blazies'];
$ib = $settings['iobrowsers'];
$target_type = $blazies->get('field.target_type');
$attrs = &$widget['#attributes'] ?? [];
// Self-closed elements cannot be iconized, add the wrappers.
foreach (Defaults::widgetButtons() as $button) {
if (isset($widget[$button])) {
IoBrowserUtil::wrapButton($widget[$button], $button);
}
}
// Delay rendering of the weight selector, so that can be rendered later.
if (isset($widget['_weight'])) {
if (empty($settings['grid'])) {
hide($widget['_weight']);
}
$classes = [$ib->get('weight_class'), 'ib__weight'];
$widget['_weight']['#attributes']['class'] = $classes;
$widget['_weight']['#attributes']['data-ib-index'] = $delta;
$widget['_weight']['#wrapper_attributes']['class'][] = 'visually-hidden';
}
// Arrange the row without the normal form_element wrappers.
unset($widget['#theme'], $widget['#theme_wrappers']);
// Makes grids draggable.
if (!empty($settings['grid'])) {
$attrs['class'][] = 'js-form-managed-file';
$attrs['class'][] = 'form-managed-file';
// If using Blazy::grid.
if ($delta >= -1) {
$attrs['class'][] = 'grid--' . $delta;
}
if ($target_type) {
$attrs['class'][] = 'grid--' . str_replace('_', '-', $target_type);
}
}
}
/**
* Prepares variables for slick-vanilla--browser.html.twig templates.
*/
function _io_browser_build_widget_item(array &$widget, array &$settings, $delta) {
$ib = $settings['iobrowsers'];
$group = [];
foreach (Element::children($widget) as $child) {
$fid = 0;
if (in_array($child, ['upload', 'upload_button'])) {
unset($widget[$child]);
}
}
if (isset($widget['meta'])) {
// Must not use show() to avoid dup.
$group['meta'] = $widget['meta'];
unset($widget['meta']);
}
$fid = '';
if (isset($widget['#files'])) {
$file = reset($widget['#files']);
$fid = $file->id();
if (class_exists(ByteSizeMarkup::class)) {
$size = ByteSizeMarkup::create($file->getSize());
}
elseif (class_exists(DeprecationHelper::class)) {
/* @phpstan-ignore-next-line */
$size = DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.2.0', fn() => ByteSizeMarkup::create($file->getSize()), fn() => format_size($file->getSize()));
}
else {
$size = 'N/A';
}
$group['meta']['file_' . $fid]['filename']['#suffix'] = '<span class="file-size"> [' . $size . ']</span> ';
}
$meta = [
'fids',
'focal_point',
'width',
'height',
'alt',
'title',
'file_' . $fid,
'filename',
];
$label = $widget['filename'] ?? '';
foreach ($meta as $key) {
if ($key == 'filename') {
unset($widget[$key]);
}
if (isset($widget[$key])) {
$group['meta'][$key] = $widget[$key];
unset($widget[$key]);
}
}
if (!empty($settings['grid']) && isset($widget['_weight'])) {
show($widget['_weight']);
$group['action']['_weight'] = $widget['_weight'];
unset($widget['_weight']);
}
foreach (Defaults::widgetButtons()
+ ['display_field'] as $key) {
if (isset($widget[$key])) {
$group['action'][$key] = $widget[$key];
unset($widget[$key]);
}
}
if (isset($group['meta'])) {
$meta = $group['meta'];
unset($group['meta']);
$group['meta']['#theme'] = 'container';
$group['meta']['#attributes']['class'][] = 'ib__caption';
$group['meta']['#children'] = $meta;
}
if (isset($group['action'])) {
$actions = $group['action'];
$group['actions']['#theme'] = 'container';
$group['actions']['#attributes']['class'] = [
'ib__action',
'button-group',
'button-group--icon',
];
$group['actions']['#children'] = $actions;
unset($group['action']);
}
if (isset($widget['preview'])) {
$display = $widget['preview'];
$preview = io_browser()->toBlazy($display, $settings, $delta, $label);
$group['preview']['#theme'] = 'container';
$classes = &$group['preview']['#attributes'];
$classes['class'][] = 'ib__preview';
if ($settings['style'] == 'nativegrid') {
$classes['class'][] = 'b-cover';
}
if ($ib->get('widget.plugin_id') == 'media_library_widget') {
$classes['class'][] = 'media-library-item__preview';
$classes['class'][] = 'js-media-library-item-preview';
}
$group['preview']['#children'] = $preview;
unset($widget['preview']);
}
$widget['item'] = $group;
}
/**
* Prepares buttons if any.
*/
function _io_browser_get_buttons(array $settings) {
$blazies = $settings['blazies'];
$ib = $settings['iobrowsers'];
$buttons = [];
if (isset($settings['crop_list'])) {
$buttons['crop'] = 'Crop';
}
if ($blazies->get('field.cardinality') != 1) {
if ($ib->is('text')) {
$buttons['caption'] = 'Text';
}
$buttons['removeall'] = 'Remove all';
}
return $buttons;
}
/**
* Builds buttons if any.
*/
function _io_browser_build_buttons(array $buttons) {
$actions = [];
if ($buttons) {
foreach ($buttons as $key => $title) {
$actions[$key . '_button'] = [
'#type' => 'button',
'#value' => t('@title', ['@title' => $title]),
'#submit' => [],
'#attributes' => [
'class' => ['button--ib', 'button--js', 'button--' . $key],
'data-target' => $key,
],
];
}
}
return $actions;
}
/**
* Returns a draggable item.
*/
function _io_browser_get_draggable(array &$widget, array $settings) {
$blazies = $settings['blazies'];
$draggable = [];
if (isset($widget['_weight']) && empty($settings['grid'])) {
show($widget['_weight']);
$draggable[] = $widget['_weight'];
unset($widget['_weight']);
}
$attributes = &$widget['#attributes'] ?? [];
if (!isset($attributes['data-row-id'])) {
$attributes['data-row-id'] = $blazies->get('delta');
}
return $draggable ? [
'#markup' => io_browser()->formatter()->renderer()->render($draggable),
'#wrapper_attributes' => $attributes,
] : [];
}
/**
* Provides draggable items if any.
*/
function _io_browser_build_draggables(array &$buttons, array $draggables = []) {
$content = [];
if ($draggables) {
$content = [
'#theme' => 'item_list',
'#items' => $draggables,
'#wrapper_attributes' => [
'class' => ['ib__sortlist'],
],
'#attributes' => [
'class' => ['ib__sortable', 'clearfix'],
],
];
$buttons += [
'sort' => 'Sort',
'done' => 'Done',
];
}
return $content;
}
