Executive summary: A new FAPI special field type of hselect.
During the last session of of the Google Drupal Usability Survey, "This is crazy in here" was uttered when opening up the Parent item select input of the Menu Settings. By default, it was including every single possible menu item in one giant select list which can be a little crazy.
Which menus are included in that list is configurable so that things like the Navigation menu do not end up in there, but even without that, a giant select field full of every menu item is confronting.
One possible solution to this was to include something like hierarchial select into the core. I thought I'd try to use the existing #ajax infrastructure since I have no chance of putting all of HS into core ;) If someone is so brave (Wim?), they can give it go!
After looking at how it worked on the inside, it seemed that the form_alter approach that HS took because it is a contrib module is great but it doesn't seem like the right way to attack the issue when it's in core and we're adding the item in the first place.
For that, I'm proposing a new field type be added to FAPI - 'hselect' (feel free to give a better machine name .. I'm not sold on it).
This would allow any module to use this method of getting a single value out of a large tree of options where only the available options at a particular level are actioned at a time. It does this by breaking up the levels into multiple select options based on the given #options array in the #process function. There is a @todo in there to generate the whole list with a #process function, but that seems difficult to deal with in other hook_form_alters .. I know it drove me up the wall the last time I ran into a form that did that. No way to slightly change the options, just completely replace them.
eg an example of what it would look like in the declaration
// This would normally be generated, but this is for an example
$default = 'main-menu:0';
$options = array(
'main-menu:0' => array(
'#title' => 'Main menu',
'main-menu:137' => 'Home',
'main-menu:138' => 'Contact',
),
'management:0' => array(
'#title' => 'Management',
'management:1' => array(
'#title' => 'Administration',
/* etc. skipping the full array for now */
'management:8' => 'Content',
'management:6' => 'Appearance',
),
),
);
$form['my_hselect'] = array(
'#type' => 'hselect',
'#title' => t('Parent item'),
'#default_value' => $default,
'#options' => $options,
'#attributes' => array('class' => array('menu-parent-hselect')),
);
The present #process function of form_process_hselect would then process it into the following
$form['my_hselect'] = array(
'#type' => 'hselect',
'#title' => t('Parent item'),
'#default_value' => $default,
'#options' => $options, /* needs to be kept as a local reference to build the sub-selects */
'#attributes' => array('class' => array('menu-parent-hselect')),
'hselect' => array(
'#tree' => TRUE,
0 => array(
'#type' => 'select',
'#options' => array(
'main-menu:0' => 'Main menu',
'management:0' => 'Management',
),
'#default_value' => 'main-menu:0',
'#attributes' => array('class' => array('menu-parent-hselect-level-0')),
),
1 => array(
'#type' => 'select',
'#options' => array(
'_none' => ' - select - ',
'main-menu:137' => 'Home',
'main-menu:138' => 'Contact',
),
'#attributes' => array('class' => array('menu-parent-hselect-level-1')),
),
),
);
The returned value would be in $form_state['values']['my_hselect'] from the example above, and all other sub-select would go bye bye, just like with radios. At present, this aspect is borked.
I've made a start but I'd rather get the conversation going before putting this entirely into code only to have someone point out a much better way to do it (or what the @todo about putting the whole creation of #options into a #process .. maybe #options_callback would be a better way to go if we're already screwing with the api). It would be a neat feature to have available in core though as it could break down any large single leaf tree selects.
I'm fairly near the limit of what I know about making a new select type in there. I can get the ajax bit working with #ajax, but at present it needs POST processing of some kind to get rid of the invalid input values as I don't think I've done all the functions required. I'm also pretty much out of time for a bit now.