Skip to content

Toggleable item supportΒ #185

@jtreminio

Description

@jtreminio

edit: This comment out of date. This is now a PR ticket. See comment #185 (comment)


Currently item rows look like

{marker}  {text}                      {optional item extra}

The marker property seems to only be toggable on highlighting a row. It is not aware of state, nor can it be changed once the menu is displayed.

My goal is to add checkbox-style visuals to this library.

With the following code, you can display a "checkbox" when an item was previously selected:

    $itemCallable = function (CliMenu $menu) {
        $item = $menu->getSelectedItem();

        $key = array_search($item, $this->selected);
        if ($key !== false) {
            $item->hideItemExtra();
            unset($this->selected[$key]);
        } else {
            $item->showItemExtra();
            $this->selected[] = $item;
        }

        $menu->redraw();
    };

    $builder = (new CliMenuBuilder)
        ->setBackgroundColour('black')
        ->setForegroundColour('green');

    $menu = $builder->setTitle('Create a New Project')
        ->addLineBreak()
        ->addStaticItem('Select Language')
        ->addItem('PHP', $itemCallable, true)
        ->addItem('Javascript', $itemCallable, true)
        ->addItem('Ruby', $itemCallable, true)
        ->addItem('Python', $itemCallable, true)
        ->addItem('Go', $itemCallable, true)
        ->addLineBreak('-')
        ->build();

    // See https://github.com/php-school/cli-menu/issues/184
    foreach ($menu->getItems() as $item) {
        $item->hideItemExtra();
    }

    $menu->open();

    foreach ($this->selected as $item) {
        var_dump($item->getText());
    }

This produces the following:

Peek 2019-12-15 12-45

Unfortunately the item-extra string is pushed to the far right of the element. For one or two elements it might be fine, but for longer lists this could get a little confusing.

Instead, making it so the item-extra is to the left of the string, and hiding the marker makes it a nicer experience:

Peek 2019-12-15 12-47

This is achieved by modifying getRows(): https://github.com/php-school/cli-menu/blob/master/src/MenuItem/SelectableTrait.php#L31

public function getRows(MenuStyle $style, bool $selected = false) : array
{
    $marker = sprintf("%s", $style->getMarker($selected));

    $length = $style->getDisplaysExtra()
        ? $style->getContentWidth() - (mb_strlen($style->getItemExtra()) + 2)
        : $style->getContentWidth();

    $rows = explode(
        "\n",
        StringUtil::wordwrap(
            sprintf('%s', $this->text),
            $length,
            sprintf("\n%s", str_repeat(' ', mb_strlen($marker)))
        )
    );

    return array_map(function ($row, $key) use ($style, $length) {
        $text = $this->disabled ? $style->getDisabledItemText($row) : $row;

        if ($key === 0) {
            return $this->showItemExtra
                ? sprintf('%s  %s',
                    $style->getItemExtra(),
                    $text
                )
                : sprintf('%s  %s',
                    str_repeat(' ', mb_strlen($style->getItemExtra())),
                    $text
                );
        }

        return $text;
    }, $rows, array_keys($rows));
}

I think it would be better to show a different item-extra when not selected:

[ ]  Item not selected
[βœ”]  Item selected

From a technical standpoint, the marker item would make more sense to refactor, giving it state-awareness (am I toggled even if I'm not highlighted?), giving it a different value for "toggled but not highlighted", and a value for "toggled and highlighted".

However, it seems the marker is at a much more static state than item-extra, and thus adding ability to pass in a custom parser for getRows() to use makes more sense.

Since getRows() is not mutating data but returning a new array, all current properties can remain protected. Simply creating an interface for a parser to receive data, and return expected data would work.

Your thoughts?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions