php-school / cli-menu

🖥 Build beautiful PHP CLI menus. Simple yet Powerful. Expressive DSL.
http://www.phpschool.io
MIT License
1.94k stars 107 forks source link

Grouped items #217

Open jtreminio opened 4 years ago

jtreminio commented 4 years ago

SplitItem radio works well:

<?php

use PhpSchool\CliMenu\Builder\SplitItemBuilder;
use PhpSchool\CliMenu\CliMenu;
use PhpSchool\CliMenu\Builder\CliMenuBuilder;

require_once(__DIR__ . '/../vendor/autoload.php');

$menu = (new CliMenuBuilder)
    ->setTitle('Header 1')
    ->addSplitItem(function (SplitItemBuilder $b) use ($itemCallable) {
        $b->addRadioItem('Item 1-A', function() {})
            ->addRadioItem('Item 1-B', function() {})
            ->addRadioItem('Item 1-C', function() {})
        ;
    })
    ->build();

$menu->open();

But it's not possible to have multiple groups of RadioItem within the same menu level:

$menu = (new CliMenuBuilder)
    ->addStaticItem('Header 1')
    ->addRadioItem('Item 1-A', function() {})
    ->addRadioItem('Item 1-B', function() {})
    ->addRadioItem('Item 1-C', function() {})
    ->addLineBreak('---')
    ->addStaticItem('Header 2')
    ->addRadioItem('Item 2-A', function() {})
    ->addRadioItem('Item 2-B', function() {})
    ->addRadioItem('Item 2-C', function() {})
    ->build();

$menu->open();

Peek 2019-12-20 19-15

Similarly, I want to be able to group non-RadioItem items together.

I'm thinking a non-styled container item that can take any number of MenuItemInterface items.

It has the following API:

If it contains no items, the parent CliMenu displays nothing - it is only a container of items and has no styling of its own, nor does it affect the UI.

If it has items, it behaves as if those items were defined in-line with the parent menu.

Example:

$menu = (new CliMenuBuilder)
    ->addStaticItem('Header 1')
    ->addGroup(function (CliMenuBuilder $b) {
        $b->addRadioItem('Item 1-A', function() {})
            ->addRadioItem('Item 1-B', function() {})
            ->addRadioItem('Item 1-C', function() {})
        ;
    })
    ->addLineBreak('---')
    ->addStaticItem('Header 2')
    ->addGroup(function (CliMenuBuilder $b) {
        $b->addRadioItem('Item 2-A', function() {})
            ->addRadioItem('Item 2-B', function() {})
            ->addRadioItem('Item 2-C', function() {})
        ;
    })
    ->build();

$menu->open();

This would look identical to the GIF above.

If no items are defined,

$menu = (new CliMenuBuilder)
    ->addStaticItem('Header 1')
    ->addGroup(function (CliMenuBuilder $b) {
        $b->addRadioItem('Item 1-A', function() {})
            ->addRadioItem('Item 1-B', function() {})
            ->addRadioItem('Item 1-C', function() {})
        ;
    })
    ->addLineBreak('---')
    ->addStaticItem('Header 2')
    ->addGroup(function (CliMenuBuilder $b) {
    })
    ->build();

$menu->open();

then it acts as if the group isn't defined at all:

image

This will help group RadioItems in a vertical manner (without needing to use SplitItem), and allow grouping other item types for non-Menu work.

In addition, I would suggest changing RadioItem::getSelectAction() to use this group feature to disable other items within the same group, vs using SplitItem.

If the radio item does not belong to a group, behave as it already does and toggle off other radio items within the same CliMenu object.

edit:

Thinking about this, for initial PR it would be good to keep it simple and not give this class any styling at all. Simply a container, nothing else, but I could see it expending to keeping its own copy of styles and applying them to any children items.

So this would basically end up being a submenu that's visible on the current menu without needing to select an item to view the children items within.

jtreminio commented 4 years ago

Seems SplitItem handles item selection differently since it displays data horizontally, so it keeps a different selected index to the rest of the parent menu.

AydinHassan commented 4 years ago

It sounds like a logical next step. If was already thinking of this myself. It could have getChecked() method which returns the checked items. If we can implement this in a nice clean way without lots of special casing and touching lots of files I'd be happy to include it.