Chris@0: '', Chris@0: // The media query for the breakpoint. Chris@0: 'mediaQuery' => '', Chris@0: // Weight used for ordering breakpoints. Chris@0: 'weight' => 0, Chris@0: // Breakpoint multipliers. Chris@0: 'multipliers' => [], Chris@0: // The breakpoint group. Chris@0: 'group' => '', Chris@0: // Default class for breakpoint implementations. Chris@0: 'class' => 'Drupal\breakpoint\Breakpoint', Chris@0: // The plugin id. Set by the plugin system based on the top-level YAML key. Chris@0: 'id' => '', Chris@0: ]; Chris@0: Chris@0: /** Chris@0: * The theme handler. Chris@0: * Chris@0: * @var \Drupal\Core\Extension\ThemeHandlerInterface Chris@0: */ Chris@0: protected $themeHandler; Chris@0: Chris@0: /** Chris@0: * Static cache of breakpoints keyed by group. Chris@0: * Chris@0: * @var array Chris@0: */ Chris@0: protected $breakpointsByGroup; Chris@0: Chris@0: /** Chris@0: * The plugin instances. Chris@0: * Chris@0: * @var array Chris@0: */ Chris@0: protected $instances = []; Chris@0: Chris@0: /** Chris@0: * Constructs a new BreakpointManager instance. Chris@0: * Chris@0: * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler Chris@0: * The module handler. Chris@0: * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler Chris@0: * The theme handler. Chris@0: * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend Chris@0: * The cache backend. Chris@0: * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation Chris@0: * The string translation service. Chris@0: */ Chris@0: public function __construct(ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, CacheBackendInterface $cache_backend, TranslationInterface $string_translation) { Chris@0: $this->factory = new ContainerFactory($this); Chris@0: $this->moduleHandler = $module_handler; Chris@0: $this->themeHandler = $theme_handler; Chris@0: $this->setStringTranslation($string_translation); Chris@0: $this->alterInfo('breakpoints'); Chris@0: $this->setCacheBackend($cache_backend, 'breakpoints', ['breakpoints']); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function getDiscovery() { Chris@0: if (!isset($this->discovery)) { Chris@0: $this->discovery = new YamlDiscovery('breakpoints', $this->moduleHandler->getModuleDirectories() + $this->themeHandler->getThemeDirectories()); Chris@0: $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery); Chris@0: } Chris@0: return $this->discovery; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function processDefinition(&$definition, $plugin_id) { Chris@0: parent::processDefinition($definition, $plugin_id); Chris@0: // Allow custom groups and therefore more than one group per extension. Chris@0: if (empty($definition['group'])) { Chris@0: $definition['group'] = $definition['provider']; Chris@0: } Chris@0: // Ensure a 1x multiplier exists. Chris@0: if (!in_array('1x', $definition['multipliers'])) { Chris@0: $definition['multipliers'][] = '1x'; Chris@0: } Chris@0: // Ensure that multipliers are sorted correctly. Chris@0: sort($definition['multipliers']); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function providerExists($provider) { Chris@0: return $this->moduleHandler->moduleExists($provider) || $this->themeHandler->themeExists($provider); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getBreakpointsByGroup($group) { Chris@0: if (!isset($this->breakpointsByGroup[$group])) { Chris@0: if ($cache = $this->cacheBackend->get($this->cacheKey . ':' . $group)) { Chris@0: $this->breakpointsByGroup[$group] = $cache->data; Chris@0: } Chris@0: else { Chris@0: $breakpoints = []; Chris@0: foreach ($this->getDefinitions() as $plugin_id => $plugin_definition) { Chris@0: if ($plugin_definition['group'] == $group) { Chris@0: $breakpoints[$plugin_id] = $plugin_definition; Chris@0: } Chris@0: } Chris@0: uasort($breakpoints, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']); Chris@0: $this->cacheBackend->set($this->cacheKey . ':' . $group, $breakpoints, Cache::PERMANENT, ['breakpoints']); Chris@0: $this->breakpointsByGroup[$group] = $breakpoints; Chris@0: } Chris@0: } Chris@0: Chris@0: $instances = []; Chris@0: foreach ($this->breakpointsByGroup[$group] as $plugin_id => $definition) { Chris@0: if (!isset($this->instances[$plugin_id])) { Chris@0: $this->instances[$plugin_id] = $this->createInstance($plugin_id); Chris@0: } Chris@0: $instances[$plugin_id] = $this->instances[$plugin_id]; Chris@0: } Chris@0: return $instances; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getGroups() { Chris@0: // Use a double colon so as to not clash with the cache for each group. Chris@0: if ($cache = $this->cacheBackend->get($this->cacheKey . '::groups')) { Chris@0: $groups = $cache->data; Chris@0: } Chris@0: else { Chris@0: $groups = []; Chris@0: foreach ($this->getDefinitions() as $plugin_definition) { Chris@0: if (!isset($groups[$plugin_definition['group']])) { Chris@0: $groups[$plugin_definition['group']] = $plugin_definition['group']; Chris@0: } Chris@0: } Chris@0: $this->cacheBackend->set($this->cacheKey . '::groups', $groups, Cache::PERMANENT, ['breakpoints']); Chris@0: } Chris@0: // Get the labels. This is not cacheable due to translation. Chris@0: $group_labels = []; Chris@0: foreach ($groups as $group) { Chris@0: $group_labels[$group] = $this->getGroupLabel($group); Chris@0: } Chris@0: asort($group_labels); Chris@0: return $group_labels; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getGroupProviders($group) { Chris@0: $providers = []; Chris@0: $breakpoints = $this->getBreakpointsByGroup($group); Chris@0: foreach ($breakpoints as $breakpoint) { Chris@0: $provider = $breakpoint->getProvider(); Chris@0: $extension = FALSE; Chris@0: if ($this->moduleHandler->moduleExists($provider)) { Chris@0: $extension = $this->moduleHandler->getModule($provider); Chris@0: } Chris@0: elseif ($this->themeHandler->themeExists($provider)) { Chris@0: $extension = $this->themeHandler->getTheme($provider); Chris@0: } Chris@0: if ($extension) { Chris@0: $providers[$extension->getName()] = $extension->getType(); Chris@0: } Chris@0: } Chris@0: return $providers; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function clearCachedDefinitions() { Chris@0: parent::clearCachedDefinitions(); Chris@0: $this->breakpointsByGroup = NULL; Chris@0: $this->instances = []; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the label for a breakpoint group. Chris@0: * Chris@0: * @param string $group Chris@0: * The breakpoint group. Chris@0: * Chris@0: * @return string Chris@0: * The label. Chris@0: */ Chris@0: protected function getGroupLabel($group) { Chris@0: // Extension names are not translatable. Chris@0: if ($this->moduleHandler->moduleExists($group)) { Chris@0: $label = $this->moduleHandler->getName($group); Chris@0: } Chris@0: elseif ($this->themeHandler->themeExists($group)) { Chris@0: $label = $this->themeHandler->getName($group); Chris@0: } Chris@0: else { Chris@0: // Custom group label that should be translatable. Chris@0: $label = $this->t($group, [], ['context' => 'breakpoint']); Chris@0: } Chris@0: return $label; Chris@0: } Chris@0: Chris@0: }