Version 1
[yaffs-website] / web / core / modules / breakpoint / src / BreakpointManager.php
diff --git a/web/core/modules/breakpoint/src/BreakpointManager.php b/web/core/modules/breakpoint/src/BreakpointManager.php
new file mode 100644 (file)
index 0000000..30caf27
--- /dev/null
@@ -0,0 +1,259 @@
+<?php
+
+namespace Drupal\breakpoint;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Extension\ThemeHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
+use Drupal\Core\Plugin\Discovery\YamlDiscovery;
+use Drupal\Core\Plugin\Factory\ContainerFactory;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+
+/**
+ * Defines a breakpoint plugin manager to deal with breakpoints.
+ *
+ * Extension can define breakpoints in a EXTENSION_NAME.breakpoints.yml file
+ * contained in the extension's base directory. Each breakpoint has the
+ * following structure:
+ * @code
+ *   MACHINE_NAME:
+ *     label: STRING
+ *     mediaQuery: STRING
+ *     weight: INTEGER
+ *     multipliers:
+ *       - STRING
+ * @endcode
+ * For example:
+ * @code
+ * bartik.mobile:
+ *   label: mobile
+ *   mediaQuery: '(min-width: 0px)'
+ *   weight: 0
+ *   multipliers:
+ *     - 1x
+ *     - 2x
+ * @endcode
+ * Optionally a breakpoint can provide a group key. By default an extensions
+ * breakpoints will be placed in a group labelled with the extension name.
+ *
+ * @see \Drupal\breakpoint\Breakpoint
+ * @see \Drupal\breakpoint\BreakpointInterface
+ * @see plugin_api
+ */
+class BreakpointManager extends DefaultPluginManager implements BreakpointManagerInterface {
+  use StringTranslationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaults = [
+    // Human readable label for breakpoint.
+    'label' => '',
+    // The media query for the breakpoint.
+    'mediaQuery' => '',
+    // Weight used for ordering breakpoints.
+    'weight' => 0,
+    // Breakpoint multipliers.
+    'multipliers' => [],
+    // The breakpoint group.
+    'group' => '',
+    // Default class for breakpoint implementations.
+    'class' => 'Drupal\breakpoint\Breakpoint',
+    // The plugin id. Set by the plugin system based on the top-level YAML key.
+    'id' => '',
+  ];
+
+  /**
+   * The theme handler.
+   *
+   * @var \Drupal\Core\Extension\ThemeHandlerInterface
+   */
+  protected $themeHandler;
+
+  /**
+   * Static cache of breakpoints keyed by group.
+   *
+   * @var array
+   */
+  protected $breakpointsByGroup;
+
+  /**
+   * The plugin instances.
+   *
+   * @var array
+   */
+  protected $instances = [];
+
+  /**
+   * Constructs a new BreakpointManager instance.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
+   *   The theme handler.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   The cache backend.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation service.
+   */
+  public function __construct(ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, CacheBackendInterface $cache_backend, TranslationInterface $string_translation) {
+    $this->factory = new ContainerFactory($this);
+    $this->moduleHandler = $module_handler;
+    $this->themeHandler = $theme_handler;
+    $this->setStringTranslation($string_translation);
+    $this->alterInfo('breakpoints');
+    $this->setCacheBackend($cache_backend, 'breakpoints', ['breakpoints']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getDiscovery() {
+    if (!isset($this->discovery)) {
+      $this->discovery = new YamlDiscovery('breakpoints', $this->moduleHandler->getModuleDirectories() + $this->themeHandler->getThemeDirectories());
+      $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
+    }
+    return $this->discovery;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processDefinition(&$definition, $plugin_id) {
+    parent::processDefinition($definition, $plugin_id);
+    // Allow custom groups and therefore more than one group per extension.
+    if (empty($definition['group'])) {
+      $definition['group'] = $definition['provider'];
+    }
+    // Ensure a 1x multiplier exists.
+    if (!in_array('1x', $definition['multipliers'])) {
+      $definition['multipliers'][] = '1x';
+    }
+    // Ensure that multipliers are sorted correctly.
+    sort($definition['multipliers']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function providerExists($provider) {
+    return $this->moduleHandler->moduleExists($provider) || $this->themeHandler->themeExists($provider);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBreakpointsByGroup($group) {
+    if (!isset($this->breakpointsByGroup[$group])) {
+      if ($cache = $this->cacheBackend->get($this->cacheKey . ':' . $group)) {
+        $this->breakpointsByGroup[$group] = $cache->data;
+      }
+      else {
+        $breakpoints = [];
+        foreach ($this->getDefinitions() as $plugin_id => $plugin_definition) {
+          if ($plugin_definition['group'] == $group) {
+            $breakpoints[$plugin_id] = $plugin_definition;
+          }
+        }
+        uasort($breakpoints, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);
+        $this->cacheBackend->set($this->cacheKey . ':' . $group, $breakpoints, Cache::PERMANENT, ['breakpoints']);
+        $this->breakpointsByGroup[$group] = $breakpoints;
+      }
+    }
+
+    $instances = [];
+    foreach ($this->breakpointsByGroup[$group] as $plugin_id => $definition) {
+      if (!isset($this->instances[$plugin_id])) {
+        $this->instances[$plugin_id] = $this->createInstance($plugin_id);
+      }
+      $instances[$plugin_id] = $this->instances[$plugin_id];
+    }
+    return $instances;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getGroups() {
+    // Use a double colon so as to not clash with the cache for each group.
+    if ($cache = $this->cacheBackend->get($this->cacheKey . '::groups')) {
+      $groups = $cache->data;
+    }
+    else {
+      $groups = [];
+      foreach ($this->getDefinitions() as $plugin_definition) {
+        if (!isset($groups[$plugin_definition['group']])) {
+          $groups[$plugin_definition['group']] = $plugin_definition['group'];
+        }
+      }
+      $this->cacheBackend->set($this->cacheKey . '::groups', $groups, Cache::PERMANENT, ['breakpoints']);
+    }
+    // Get the labels. This is not cacheable due to translation.
+    $group_labels = [];
+    foreach ($groups as $group) {
+      $group_labels[$group] = $this->getGroupLabel($group);
+    }
+    asort($group_labels);
+    return $group_labels;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getGroupProviders($group) {
+    $providers = [];
+    $breakpoints = $this->getBreakpointsByGroup($group);
+    foreach ($breakpoints as $breakpoint) {
+      $provider = $breakpoint->getProvider();
+      $extension = FALSE;
+      if ($this->moduleHandler->moduleExists($provider)) {
+        $extension = $this->moduleHandler->getModule($provider);
+      }
+      elseif ($this->themeHandler->themeExists($provider)) {
+        $extension = $this->themeHandler->getTheme($provider);
+      }
+      if ($extension) {
+        $providers[$extension->getName()] = $extension->getType();
+      }
+    }
+    return $providers;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function clearCachedDefinitions() {
+    parent::clearCachedDefinitions();
+    $this->breakpointsByGroup = NULL;
+    $this->instances = [];
+  }
+
+  /**
+   * Gets the label for a breakpoint group.
+   *
+   * @param string $group
+   *   The breakpoint group.
+   *
+   * @return string
+   *   The label.
+   */
+  protected function getGroupLabel($group) {
+    // Extension names are not translatable.
+    if ($this->moduleHandler->moduleExists($group)) {
+      $label = $this->moduleHandler->getName($group);
+    }
+    elseif ($this->themeHandler->themeExists($group)) {
+      $label = $this->themeHandler->getName($group);
+    }
+    else {
+      // Custom group label that should be translatable.
+      $label = $this->t($group, [], ['context' => 'breakpoint']);
+    }
+    return $label;
+  }
+
+}