diff --git a/core/modules/grid/config/grid.fluid_12.yml b/core/modules/grid/config/grid.fluid_12.yml new file mode 100644 index 0000000..774b630 --- /dev/null +++ b/core/modules/grid/config/grid.fluid_12.yml @@ -0,0 +1,9 @@ +id: fluid_12 +label: Twelve column fluid +type: equal_column +options: + units: '%' + width: 100 + columns: 12 + padding_width: 1.5 + gutter_width: 2 diff --git a/core/modules/grid/config/grid.fluid_3.yml b/core/modules/grid/config/grid.fluid_3.yml new file mode 100644 index 0000000..7e6a9a6 --- /dev/null +++ b/core/modules/grid/config/grid.fluid_3.yml @@ -0,0 +1,9 @@ +id: fluid_3 +label: Three column fluid +type: equal_column +options: + units: '%' + width: 100 + columns: 3 + padding_width: 1.5 + gutter_width: 2 diff --git a/core/modules/grid/config/grid.fluid_6.yml b/core/modules/grid/config/grid.fluid_6.yml new file mode 100644 index 0000000..1d7fbcb --- /dev/null +++ b/core/modules/grid/config/grid.fluid_6.yml @@ -0,0 +1,9 @@ +id: fluid_6 +label: Six column fluid +type: equal_column +options: + units: '%' + width: 100 + columns: 6 + padding_width: 1.5 + gutter_width: 2 diff --git a/core/modules/grid/config/grid.ninesixty_12.yml b/core/modules/grid/config/grid.ninesixty_12.yml new file mode 100644 index 0000000..5409789 --- /dev/null +++ b/core/modules/grid/config/grid.ninesixty_12.yml @@ -0,0 +1,9 @@ +id: ninesixty_12 +label: '960px wide, 12 column grid' +type: equal_column +options: + units: 'px' + width: 960 + columns: 12 + padding_width: 20 + gutter_width: 10 diff --git a/core/modules/grid/config/grid.ninesixty_16.yml b/core/modules/grid/config/grid.ninesixty_16.yml new file mode 100644 index 0000000..2cd9c65 --- /dev/null +++ b/core/modules/grid/config/grid.ninesixty_16.yml @@ -0,0 +1,9 @@ +id: ninesixty_16 +label: '960px wide, 16 column grid' +type: equal_column +options: + units: 'px' + width: 960 + columns: 16 + padding_width: 20 + gutter_width: 10 diff --git a/core/modules/grid/grid.admin.inc b/core/modules/grid/grid.admin.inc new file mode 100644 index 0000000..b71c511 --- /dev/null +++ b/core/modules/grid/grid.admin.inc @@ -0,0 +1,67 @@ +render(); +} + +/** + * Page callback: Presents the grid editing form. + * + * @see grid_menu() + */ +function grid_page_edit(Grid $grid) { + drupal_set_title(t('Edit grid @label', array('@label' => $grid->label())), PASS_THROUGH); + return entity_get_form($grid); +} + +/** + * Page callback: Provides the new grid addition form. + * + * @see grid_menu() + */ +function grid_page_add() { + $grid = entity_create('grid', array()); + return entity_get_form($grid); +} + +/** + * Page callback: Form constructor for grid deletion confirmation form. + * + * @see grid_menu() + */ +function grid_confirm_delete($form, &$form_state, Grid $grid) { + // Always provide entity id in the same form key as in the entity edit form. + $form['id'] = array('#type' => 'value', '#value' => $grid->id()); + $form_state['grid'] = $grid; + return confirm_form($form, + t('Are you sure you want to remove the grid %title?', array('%title' => $grid->label())), + 'admin/structure/grids', + t('This action cannot be undone.'), + t('Delete'), + t('Cancel') + ); +} + +/** + * Form submission handler for grid_confirm_delete(). + */ +function grid_confirm_delete_submit($form, &$form_state) { + $grid = $form_state['grid']; + $grid->delete(); + drupal_set_message(t('Grid %label has been deleted.', array('%label' => $grid->label()))); + watchdog('grid', 'Grid %label has been deleted.', array('%label' => $grid->label()), WATCHDOG_NOTICE); + $form_state['redirect'] = 'admin/structure/grids'; +} diff --git a/core/modules/grid/grid.info b/core/modules/grid/grid.info new file mode 100644 index 0000000..9dd6db4 --- /dev/null +++ b/core/modules/grid/grid.info @@ -0,0 +1,8 @@ +name = Grid +description = Pluggable grid system manager. +package = Core +version = VERSION +core = 8.x +dependencies[] = config +configure = admin/structure/grids + diff --git a/core/modules/grid/grid.module b/core/modules/grid/grid.module new file mode 100644 index 0000000..4ec1e0b --- /dev/null +++ b/core/modules/grid/grid.module @@ -0,0 +1,101 @@ +' . t('Grids provide useful guides to place content on your pages. The grid module provides the ability to edit any type of grid and supports fluid and fixed equal column grids by itself. Extend with contributed modules for more grid types.') . '

'; + } +} + +/** + * Implements hook_menu(). + */ +function grid_menu() { + $items['admin/structure/grids'] = array( + 'title' => 'Grids', + 'description' => 'Manage list of grids which can be used with layouts.', + 'page callback' => 'grid_page_list', + 'access callback' => 'user_access', + 'access arguments' => array('administer grids'), + 'file' => 'grid.admin.inc', + ); + $items['admin/structure/grids/add'] = array( + 'title' => 'Add grid', + 'page callback' => 'grid_page_add', + 'access callback' => 'user_access', + 'access arguments' => array('administer grids'), + 'type' => MENU_LOCAL_ACTION, + 'file' => 'grid.admin.inc', + ); + $items['admin/structure/grids/manage/%grid'] = array( + 'title' => 'Edit grid', + 'page callback' => 'grid_page_edit', + 'page arguments' => array(4), + 'access callback' => 'user_access', + 'access arguments' => array('administer grids'), + 'type' => MENU_CALLBACK, + 'file' => 'grid.admin.inc', + ); + $items['admin/structure/grids/manage/%grid/edit'] = array( + 'title' => 'Edit', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + $items['admin/structure/grids/grid/%grid/delete'] = array( + 'title' => 'Delete', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('grid_confirm_delete', 4), + 'access callback' => 'user_access', + 'access arguments' => array('administer grids'), + 'type' => MENU_LOCAL_TASK, + 'file' => 'grid.admin.inc', + ); + return $items; +} + +/** + * Implements hook_permission(). + */ +function grid_permission() { + return array( + 'administer grids' => array( + 'title' => t('Administer grids'), + 'description' => t('Administer grids created with the grid module.'), + ), + ); +} + +/** + * Entity URI callback. + * + * @param Drupal\grid\Grid $grid + * Grid configuration entity instance. + * + * @return array + * Entity URI information. + */ +function grid_uri(Grid $grid) { + return array( + 'path' => 'admin/structure/grids/manage/' . $grid->id(), + ); +} + +/** + * Look up one grid setup based on machine name. + * + * @return Drupal\grid\Grid + * Grid configuration entity instance. + */ +function grid_load($id) { + return entity_load('grid', $id); +} diff --git a/core/modules/grid/lib/Drupal/grid/GridBundle.php b/core/modules/grid/lib/Drupal/grid/GridBundle.php new file mode 100644 index 0000000..eb5b927 --- /dev/null +++ b/core/modules/grid/lib/Drupal/grid/GridBundle.php @@ -0,0 +1,25 @@ +register('plugin.manager.grid', 'Drupal\grid\Plugin\GridManager'); + } +} diff --git a/core/modules/grid/lib/Drupal/grid/GridFormController.php b/core/modules/grid/lib/Drupal/grid/GridFormController.php new file mode 100644 index 0000000..e8b4f9c --- /dev/null +++ b/core/modules/grid/lib/Drupal/grid/GridFormController.php @@ -0,0 +1,105 @@ +type)) { + $grid->type = 'equal_column'; + $grid->options = array(); + } + $grid->options = $grid->getPlugin()->prepareOptions($grid->options); + } + + /** + * Overrides Drupal\Core\Entity\EntityFormController::form(). + */ + public function form(array $form, array &$form_state, EntityInterface $grid) { + $form['label'] = array( + '#type' => 'textfield', + '#title' => t('Label'), + '#maxlength' => 255, + '#default_value' => $grid->label(), + '#required' => TRUE, + ); + $form['id'] = array( + '#type' => 'machine_name', + '#default_value' => $grid->id(), + '#machine_name' => array( + 'exists' => 'grid_load', + 'source' => array('label'), + ), + '#disabled' => !$grid->isNew(), + ); + $form['type'] = array( + '#type' => 'value', + '#value' => $grid->type, + ); + $form['grid'] = array( + '#type' => 'value', + '#value' => $grid, + ); + $form['options'] = $grid->getPlugin()->form($form, $form_state, $grid->options); + + return parent::form($form, $form_state, $grid); + } + + /** + * Overrides Drupal\Core\Entity\EntityFormController::validate(). + */ + public function validate(array $form, array &$form_state) { + $form_state['values']['grid']->getPlugin()->validate($form, $form_state['values']['options']); + } + + /** + * Overrides Drupal\Core\Entity\EntityFormController::actions(). + */ + protected function actions(array $form, array &$form_state) { + // Only includes a Save action for the entity, no direct Delete button. + return array( + 'submit' => array( + '#value' => t('Save'), + '#validate' => array( + array($this, 'validate'), + ), + '#submit' => array( + array($this, 'submit'), + array($this, 'save'), + ), + ), + ); + } + + /** + * Overrides Drupal\Core\Entity\EntityFormController::save(). + */ + public function save(array $form, array &$form_state) { + $grid = $this->getEntity($form_state); + $grid->save(); + + watchdog('grid', 'Grid @label saved.', array('@label' => $grid->label()), WATCHDOG_NOTICE); + drupal_set_message(t('Grid %label saved.', array('%label' => $grid->label()))); + + $form_state['redirect'] = 'admin/structure/grids'; + } + +} diff --git a/core/modules/grid/lib/Drupal/grid/Plugin/Core/Entity/Grid.php b/core/modules/grid/lib/Drupal/grid/Plugin/Core/Entity/Grid.php new file mode 100644 index 0000000..78b1774 --- /dev/null +++ b/core/modules/grid/lib/Drupal/grid/Plugin/Core/Entity/Grid.php @@ -0,0 +1,101 @@ +plugin)) { + // Pass on Grid ID as part of the configuration array. + $this->options['id'] = $this->id; + $this->plugin = drupal_container()->get('plugin.manager.grid')->createInstance($this->type, $this->options); + } + return $this->plugin; + } + + /** + * Implements Drupal\grid\Plugin\GridInterface::getGridCss(). + */ + public function getGridCss($wrapper_selector = NULL, $col_selector_prefix = NULL, $skip_spacing = FALSE) { + return $this->getPlugin()->getGridCss($wrapper_selector, $col_selector_prefix, $skip_spacing); + } + +} diff --git a/core/modules/grid/lib/Drupal/grid/Plugin/GridInterface.php b/core/modules/grid/lib/Drupal/grid/Plugin/GridInterface.php new file mode 100644 index 0000000..ca73fd8 --- /dev/null +++ b/core/modules/grid/lib/Drupal/grid/Plugin/GridInterface.php @@ -0,0 +1,30 @@ +discovery = new AnnotatedClassDiscovery('grid', 'grid'); + $this->factory = new ReflectionFactory($this); + } +} diff --git a/core/modules/grid/lib/Drupal/grid/Plugin/grid/grid/EqualColumn.php b/core/modules/grid/lib/Drupal/grid/Plugin/grid/grid/EqualColumn.php new file mode 100644 index 0000000..94b631e --- /dev/null +++ b/core/modules/grid/lib/Drupal/grid/Plugin/grid/grid/EqualColumn.php @@ -0,0 +1,250 @@ +id = $id; + $this->columns = $columns; + $this->padding = $padding_width; + $this->gutter = $gutter_width; + $this->width = $width; + $this->units = $units; + } + + /** + * Prepare grid entity. + */ + public function prepareOptions(array $options) { + if (empty($options['width'])) { + // Set some defaults for the user if this is a new grid. + $options['units'] = '%'; + $options['width'] = 100; + $options['columns'] = 12; + $options['padding_width'] = 1.5; + $options['gutter_width'] = 2; + } + return $options; + } + + /** + * Form elements for grid editing. + */ + public function form(array $full_form, array &$form_state, array $options) { + // Master grid configuration. + $form['units'] = array( + '#type' => 'radios', + '#title' => t('Grid units'), + '#options' => array('%' => t('Percentages'), 'em' => t('Em-based'), 'px' => t('Pixel based')), + '#default_value' => $options['units'], + ); + $form['width'] = array( + '#type' => 'textfield', + '#title' => t('Grid width'), + '#description' => t('Width of the grid in unit set above. For example 960 (pixels) or 100 (percent).'), + '#default_value' => $options['width'], + ); + + // Grid detail configuration. + $form['columns'] = array( + '#type' => 'textfield', + '#title' => t('Number of grid columns'), + '#default_value' => $options['columns'], + ); + $form['padding_width'] = array( + '#type' => 'textfield', + '#title' => t('Column padding'), + '#description' => t('Column padding in unit set above. For example 10 (pixels) or 1.5 (percent). Enter 0 for no padding.'), + '#default_value' => $options['padding_width'], + ); + + $form['gutter_width'] = array( + '#type' => 'textfield', + '#title' => t('Gutter width'), + '#description' => t('Gutter width in unit set above. For example 20 (pixels) or 1.5 (percent). Enter 0 for no padding.'), + '#default_value' => $options['gutter_width'], + ); + return $form; + } + + /** + * Form validation callback. + */ + public function validate(array $form, array &$options_state) { + if ((intval($options_state['width']) != $options_state['width']) || $options_state['width'] == 0) { + // Width should be a positive integer. + form_set_error('columns', t('The width should be a positive number.')); + } + if ((intval($options_state['columns']) != $options_state['columns']) || $options_state['columns'] == 0) { + // Columns should be a positive integer. + form_set_error('columns', t('The number of columns should be a positive number.')); + } + if (!is_numeric($options_state['padding_width'])) { + // Padding can be float as well (eg. 1.5 for 1.5% for fluid grids). + form_set_error('padding_width', t('The column padding should be a number. Enter 0 (zero) for no padding.' . $options_state['padding_width'])); + } + if (!is_numeric($options_state['gutter_width'])) { + // Gutter can be float too (eg. 1.5 for 1.5% for fluid grids). + form_set_error('gutter_width', t('The gutter width should be a number. Enter 0 (zero) for no gutter.')); + } + } + + /** + * Implements Drupal\grid\Plugin\GridInterface::getGridCss(). + */ + public function getGridCss($wrapper_selector = NULL, $col_selector_prefix = NULL, $skip_spacing = FALSE) { + + // If the wrapper selector was not provided, generate one. This is useful for + // specific administration use cases when we scope the classes by grids. + if (empty($wrapper_selector)) { + $wrapper_selector = '.grid-' . $this->id; + } + + // If the col span selector was not provided, generate one. This is useful + // for the front end to apply varying span widths under different names. + if (empty($col_selector_prefix)) { + $col_selector_prefix = '.grid-col-'; + } + + // If spacing is to be skipped, use 0 instead of the configured values. + if ($skip_spacing) { + $padding = 0; + $gutter = 0; + } + else { + $padding = $this->padding; + $gutter = $this->gutter; + } + + // Because we use the border-box box model, we only need to substract the + // size of margins from the full width and divide the rest by number of + // columns to get a value for column size. + $colwidth = ($this->width - (($this->columns - 1) * $gutter)) / $this->columns; + + $css = array(); + $css[$wrapper_selector . ' .grid-col'] = array( + 'border' => '0px solid rgba(0,0,0,0)', + 'float' => 'left', + '-webkit-box-sizing' => 'border-box', + '-moz-box-sizing' => 'border-box', + 'box-sizing' => 'border-box', + '-moz-background-clip' => 'padding-box', + '-webkit-background-clip' => 'padding-box', + 'background-clip' => 'padding-box', + 'margin-left' => $gutter . $this->units, + 'padding' => '0 ' . $padding . $this->units, + ); + $css[$wrapper_selector . ' .grid-col' . $col_selector_prefix . 'first'] = array( + 'margin-left' => '0', + 'clear' => 'both', + ); + for ($i = 1; $i <= $this->columns; $i++) { + $selector = $wrapper_selector . ' .grid-col' . $col_selector_prefix . $i; + if ($i == 1) { + // The first column does not yet have any margins. + $css[$selector] = array( + 'width' => ($colwidth * $i) . $this->units, + ); + } + elseif ($i == $this->columns) { + // The last column always spans 100%. + $css[$selector] = array( + 'width' => $this->width . $this->units, + 'margin-left' => 0, + ); + } + else { + // Other columns absorb all columns that they need to include and one + // less margin before them. + $css[$selector] = array( + 'width' => (($colwidth * $i) + ($gutter * ($i - 1))) . $this->units, + ); + } + } + + return $this->formatCss($css); + } + + /** + * Returns a CSS string. + * + * @param (array) $css + * A two dimensional array, keyed first on selector, then on property name. + */ + protected function formatCss($css) { + $output = ''; + foreach ($css as $selector => $rules) { + $output .= $selector . "{\n"; + foreach ($rules as $property => $value) { + $output .= ' ' . $property . ': ' . $value . ";\n"; + } + $output .= "}\n"; + } + return $output; + } +}