diff --git elements.module elements.module
index ed66a30..5527997 100644
--- elements.module
+++ elements.module
@@ -51,11 +51,45 @@ function elements_element_info() {
'#theme' => 'rangefield',
'#theme_wrappers' => array('form_element'),
);
+ $types['horizontal_tabs'] = array(
+ '#theme_wrappers' => array('horizontal_tabs'),
+ '#default_tab' => '',
+ '#process' => array('form_process_horizontal_tabs'),
+ '#attached' => array(
+ 'library' => array(
+ array('elements', 'horizontal-tabs'),
+ array('system', 'drupal.form'),
+ ),
+ ),
+ );
return $types;
}
/**
+ * Implements hook_library().
+ */
+function elements_library() {
+
+ $path = drupal_get_path('module', 'elements');
+
+ // Horizontal Tabs.
+ $libraries['horizontal-tabs'] = array(
+ 'title' => 'Horizontal Tabs',
+ 'website' => 'http://drupal.org/node/323112',
+ 'version' => '1.0',
+ 'js' => array(
+ $path . '/horizontal-tabs/horizontal-tabs.js' => array(),
+ ),
+ 'css' => array(
+ $path . '/horizontal-tabs/horizontal-tabs.css' => array(),
+ ),
+ );
+
+ return $libraries;
+}
+
+/**
* Implements hook_element_info_alter().
*/
function elements_element_info_alter(&$types) {
@@ -105,6 +139,11 @@ function elements_theme() {
'render element' => 'element',
'file' => 'elements.theme.inc',
),
+ 'horizontal_tabs' => array(
+ 'arguments' => array('element' => NULL),
+ 'render element' => 'element',
+ 'file' => 'elements.theme.inc',
+ ),
);
}
@@ -154,3 +193,40 @@ function form_process_placeholder($element) {
}
return $element;
}
+
+/**
+ * Creates a group formatted as horizontal tabs.
+ *
+ * @param $element
+ * An associative array containing the properties and children of the
+ * fieldset.
+ * @param $form_state
+ * The $form_state array for the form this horizontal tab widget belongs to.
+ * @return
+ * The processed element.
+ */
+function form_process_horizontal_tabs($element, &$form_state) {
+ // Inject a new fieldset as child, so that form_process_fieldset() processes
+ // this fieldset like any other fieldset.
+ $element['group'] = array(
+ '#type' => 'fieldset',
+ '#theme_wrappers' => array(),
+ '#parents' => $element['#parents'],
+ );
+
+ // The JavaScript stores the currently selected tab in this hidden
+ // field so that the active tab can be restored the next time the
+ // form is rendered, e.g. on preview pages or when form validation
+ // fails.
+ $name = implode('__', $element['#parents']);
+ if (isset($form_state['values'][$name . '__active_tab'])) {
+ $element['#default_tab'] = $form_state['values'][$name . '__active_tab'];
+ }
+ $element[$name . '__active_tab'] = array(
+ '#type' => 'hidden',
+ '#default_value' => $element['#default_tab'],
+ '#attributes' => array('class' => array('horizontal-tabs-active-tab')),
+ );
+
+ return $element;
+}
diff --git elements.theme.inc elements.theme.inc
index 12b1706..b049a36 100644
--- elements.theme.inc
+++ elements.theme.inc
@@ -142,3 +142,22 @@ function theme_rangefield($variables) {
return $output;
}
+
+/**
+ * Returns HTML for an element's children fieldsets as horizontal tabs.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - element: An associative array containing the properties and children of the
+ * fieldset. Properties used: #children.
+ *
+ * @ingroup themeable
+ */
+function theme_horizontal_tabs($variables) {
+ $element = $variables['element'];
+
+ $output = '
' . (!empty($element['#title']) ? $element['#title'] : t('Horizontal Tabs')) . '
';
+ $output .= '' . $element['#children'] . '
';
+
+ return $output;
+}
diff --git horizontal-tabs/horizontal-tabs.css horizontal-tabs/horizontal-tabs.css
new file mode 100644
index 0000000..2399893
--- /dev/null
+++ horizontal-tabs/horizontal-tabs.css
@@ -0,0 +1,92 @@
+@CHARSET "UTF-8";
+
+div.horizontal-tabs {
+ margin: 0 0 1em 0; /* LTR */
+ padding: 0;
+ border: 1px solid #ccc;
+ position: relative; /* IE6/7 */
+}
+
+.horizontal-tabs ul.horizontal-tabs-list {
+ display: inline-block;
+ margin: 0;
+ border: 0;
+ padding: 0px;
+ position: relative; /* IE6 */
+ list-style: none;
+ list-style-image: none; /* IE6 */
+ background-color: #dedede;
+ border-right: 1px solid #dedede;
+ width: 100%;
+ height: auto;
+ clear: both;
+}
+
+.horizontal-tabs fieldset.horizontal-tabs-pane {
+ padding: 0 1em;
+ border: 0;
+}
+
+.horizontal-tabs-pane>legend {
+ display: none;
+}
+
+/* Layout of each tab */
+.horizontal-tabs ul.horizontal-tabs-list li {
+ background: #eee;
+ border-right: 1px solid #ccc;
+ padding: 1px;
+ padding-top: 0;
+ margin: 0;
+ min-width: 5em; /* IE7 */
+ float: left;
+}
+.horizontal-tabs ul.horizontal-tabs-list li.selected {
+ background-color: #fff;
+ padding: 0 0 1px 0;
+}
+.horizontal-tabs ul.horizontal-tabs-list li a {
+ display: block;
+ text-decoration: none;
+ padding: 0.5em 0.6em;
+}
+.horizontal-tabs ul.horizontal-tabs-list li a:hover {
+ outline: none;
+ background-color: #ededdd;
+}
+.horizontal-tabs ul.horizontal-tabs-list li:hover,
+.horizontal-tabs ul.horizontal-tabs-list li:focus {
+ background-color: #ddd;
+}
+.horizontal-tabs ul.horizontal-tabs-list li a:focus strong,
+.horizontal-tabs ul.horizontal-tabs-list li a:active strong,
+.horizontal-tabs ul.horizontal-tabs-list li a:hover strong {
+ text-decoration: none;
+ outline: none;
+}
+.horizontal-tabs ul.horizontal-tabs-list li a,
+.horizontal-tabs ul.horizontal-tabs-list li.selected a {
+ display: block;
+ text-decoration: none;
+ padding: 0.5em 0.6em 0.3em 0.6em;
+ position:relative;
+ top: 0px;
+}
+.horizontal-tabs ul.horizontal-tabs-list .selected strong {
+ color: #000;
+}
+.horizontal-tabs ul.horizontal-tabs-list .summary {
+ display: block;
+}
+.horizontal-tabs ul.horizontal-tabs ul.horizontal-tabs-list .summary {
+ line-height: normal;
+ margin-bottom: 0;
+}
+
+/**
+ * tab content
+ */
+div.field-group-htabs-wrapper .field-group-format-wrapper {
+ clear: both;
+ padding: 0 0 0.6em;
+}
\ No newline at end of file
diff --git horizontal-tabs/horizontal-tabs.js horizontal-tabs/horizontal-tabs.js
new file mode 100644
index 0000000..30da15a
--- /dev/null
+++ horizontal-tabs/horizontal-tabs.js
@@ -0,0 +1,204 @@
+(function ($) {
+
+/**
+ * This script transforms a set of fieldsets into a stack of horizontal
+ * tabs. Another tab pane can be selected by clicking on the respective
+ * tab.
+ *
+ * Each tab may have a summary which can be updated by another
+ * script. For that to work, each fieldset has an associated
+ * 'horizontalTabCallback' (with jQuery.data() attached to the fieldset),
+ * which is called every time the user performs an update to a form
+ * element inside the tab pane.
+ */
+Drupal.behaviors.horizontalTabs = {
+ attach: function (context) {
+ $('.horizontal-tabs-panes', context).once('horizontal-tabs', function () {
+ var focusID = $(':hidden.horizontal-tabs-active-tab', this).val();
+ var tab_focus;
+
+ // Check if there are some fieldsets that can be converted to horizontal-tabs
+ var $fieldsets = $('> fieldset', this);
+ if ($fieldsets.length == 0) {
+ return;
+ };
+
+ // Create the tab column.
+ var tab_list = $('');
+ $(this).wrap('').before(tab_list);
+
+ // Transform each fieldset into a tab.
+ $fieldsets.each(function () {
+ var horizontal_tab = new Drupal.horizontalTab({
+ title: $('> legend', this).text(),
+ fieldset: $(this)
+ });
+ tab_list.append(horizontal_tab.item);
+ $(this)
+ .removeClass('collapsible collapsed')
+ .addClass('horizontal-tabs-pane')
+ .data('horizontalTab', horizontal_tab);
+ if (this.id == focusID) {
+ tab_focus = $(this);
+ }
+ });
+
+ $('> li:first', tab_list).addClass('first');
+ $('> li:last', tab_list).addClass('last');
+
+ if (!tab_focus) {
+ // If the current URL has a fragment and one of the tabs contains an
+ // element that matches the URL fragment, activate that tab.
+ if (window.location.hash && $(window.location.hash, this).length) {
+ tab_focus = $(window.location.hash, this).closest('.horizontal-tabs-pane');
+ }
+ else {
+ tab_focus = $('> .horizontal-tabs-pane:first', this);
+ }
+ }
+ if (tab_focus.length) {
+ tab_focus.data('horizontalTab').focus();
+ }
+ });
+ }
+};
+
+/**
+ * The horizontal tab object represents a single tab within a tab group.
+ *
+ * @param settings
+ * An object with the following keys:
+ * - title: The name of the tab.
+ * - fieldset: The jQuery object of the fieldset that is the tab pane.
+ */
+Drupal.horizontalTab = function (settings) {
+ var self = this;
+ $.extend(this, settings, Drupal.theme('horizontalTab', settings));
+
+ this.link.click(function () {
+ self.focus();
+ return false;
+ });
+
+ // Keyboard events added:
+ // Pressing the Enter key will open the tab pane.
+ this.link.keydown(function(event) {
+ if (event.keyCode == 13) {
+ self.focus();
+ // Set focus on the first input field of the visible fieldset/tab pane.
+ $("fieldset.horizontal-tabs-pane :input:visible:enabled:first").focus();
+ return false;
+ }
+ });
+
+ // Pressing the Enter key lets you leave the tab again.
+ this.fieldset.keydown(function(event) {
+ // Enter key should not trigger inside