Skip to content

Commit

Permalink
FEATURE: Show selectable search results
Browse files Browse the repository at this point in the history
Resolves: #3
  • Loading branch information
Sebobo committed Jan 18, 2023
1 parent f21ede0 commit f379b7d
Show file tree
Hide file tree
Showing 18 changed files with 316 additions and 44 deletions.
4 changes: 2 additions & 2 deletions Classes/Domain/Dto/CommandDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

declare(strict_types=1);

namespace Shel\Neos\CommandBar\Domain\Dto;

/*
* This script belongs to the Neos CMS package "Shel.Neos.CommandBar".
*
Expand All @@ -10,8 +12,6 @@
* source code.
*/

namespace Shel\Neos\CommandBar\Domain\Dto;

use Neos\Flow\Annotations as Flow;

#[Flow\Proxy(false)]
Expand Down
35 changes: 35 additions & 0 deletions Classes/Helper/TranslationHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Shel\Neos\CommandBar\Helper;

/*
* This script belongs to the Neos CMS package "Shel.Neos.CommandBar".
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Neos\Flow\Annotations as Flow;
use Neos\Flow\I18n\EelHelper\TranslationParameterToken;

#[Flow\Proxy(false)]
class TranslationHelper
{

public static function translateByShortHandString(string $shortHandString): string
{
$shortHandStringParts = explode(':', $shortHandString);
if (count($shortHandStringParts) === 3) {
[$package, $source, $id] = $shortHandStringParts;
return (new TranslationParameterToken($id))
->package($package)
->source(str_replace('.', '/', $source))
->translate();
}

return $shortHandString;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@
use Neos\Flow\I18n\EelHelper\TranslationParameterToken;
use Neos\Neos\Controller\Backend\MenuHelper;
use Neos\Neos\Service\DataSource\AbstractDataSource;
use Shel\Neos\CommandBar\Domain\Dto\CommandDto;

class CommandBarDataSource extends AbstractDataSource
class CommandsDataSource extends AbstractDataSource
{

static protected $identifier = 'shel-neos-commandbar';
static protected $identifier = 'shel-neos-commandbar-commands';

public function __construct(private readonly MenuHelper $menuHelper)
{
Expand All @@ -46,27 +45,28 @@ static function (array $carry, array $site) {
}, []);

$modulesForMenu = array_reduce($this->menuHelper->buildModuleList($this->controllerContext),
static function (array $carry, array $module) {
function (array $carry, array $module) {
// Skip hidden or modules without submodules
if (!$module['submodules'] || $module['hideInMenu']) {
return $carry;
}
$carry[$module['group']] = [
'name' => self::translateByShortHandString($module['label']),
'description' => self::translateByShortHandString($module['description']),
'name' => $this->translateByShortHandString($module['label']),
'description' => $this->translateByShortHandString($module['description']),
'icon' => $module['icon'],
'subCommands' => array_reduce($module['submodules'], static function (array $carry, array $submodule) {
if ($submodule['hideInMenu']) {
'subCommands' => array_reduce($module['submodules'],
function (array $carry, array $submodule) {
if ($submodule['hideInMenu']) {
return $carry;
}
$carry[$submodule['module']] = [
'name' => $this->translateByShortHandString($submodule['label']),
'description' => $this->translateByShortHandString($submodule['description']),
'icon' => $submodule['icon'],
'action' => $submodule['modulePath'],
];
return $carry;
}
$carry[$submodule['module']] = [
'name' => self::translateByShortHandString($submodule['label']),
'description' => self::translateByShortHandString($submodule['description']),
'icon' => $submodule['icon'],
'action' => $submodule['modulePath'],
];
return $carry;
}, []),
}, []),
];
return $carry;
}, []);
Expand All @@ -87,7 +87,8 @@ static function (array $carry, array $module) {
];
}

protected static function translateByShortHandString(string $shortHandString): string
// FIXME: Using the TranslationHelper instead throws class not found error, why?
public function translateByShortHandString(string $shortHandString): string
{
$shortHandStringParts = explode(':', $shortHandString);
if (count($shortHandStringParts) === 3) {
Expand Down
80 changes: 80 additions & 0 deletions Classes/Service/DataSource/SearchNodesDataSource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

declare(strict_types=1);

namespace Shel\Neos\CommandBar\Service\DataSource;

/*
* This script belongs to the Neos CMS package "Shel.Neos.CommandBar".
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Flow\I18n\EelHelper\TranslationParameterToken;
use Neos\Neos\Domain\Service\NodeSearchServiceInterface;
use Neos\Neos\Service\DataSource\AbstractDataSource;
use Neos\Neos\Service\LinkingService;
use Shel\Neos\CommandBar\Helper\TranslationHelper;

class SearchNodesDataSource extends AbstractDataSource
{

static protected $identifier = 'shel-neos-commandbar-search-nodes';

public function __construct(
private readonly NodeSearchServiceInterface $nodeSearchService,
private readonly LinkingService $linkingService
) {
}

public function getData(NodeInterface $node = null, array $arguments = []): array
{
$query = $arguments['query'] ?? '';

if (!$node || !$query) {
return [];
}

$matchingNodes = $this->nodeSearchService->findByProperties($query, [
'Neos.Neos:Document',
'Neos.Neos:Shortcut',
], $node->getContext(), $node);

return array_map(function (NodeInterface $matchingNode) {
return [
'name' => $matchingNode->getLabel(),
'nodetype' => $this->translateByShortHandString($matchingNode->getNodeType()->getLabel()),
'contextPath' => $matchingNode->getContextPath(),
'icon' => $matchingNode->getNodeType()->getFullConfiguration()['ui']['icon'] ?? 'file',
'uri' => $this->getNodeUri($matchingNode),
];
}, array_values($matchingNodes));
}

protected function getNodeUri(NodeInterface $node): string
{
try {
return $this->linkingService->createNodeUri($this->controllerContext, $node, null, 'html', true);
} catch (\Exception $e) {
return '';
}
}

// FIXME: Using the TranslationHelper instead throws class not found error, why?
public function translateByShortHandString(string $shortHandString): string
{
$shortHandStringParts = explode(':', $shortHandString);
if (count($shortHandStringParts) === 3) {
[$package, $source, $id] = $shortHandStringParts;
return (new TranslationParameterToken($id))
->package($package)
->source(str_replace('.', '/', $source))
->translate();
}

return $shortHandString;
}
}
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
],
"autoload": {
"psr-4": {
"Shel\\Neos\\Commandbar\\": "Classes/"
"Shel\\Neos\\Commandbar\\": "Classes"
}
},
"extra": {
Expand Down
15 changes: 14 additions & 1 deletion packages/commandbar/src/CommandBar.module.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
:root {
--bar-min-width: 400px;
--bar-max-width: 600px;
--bar-max-width: 90vw;
--bar-width: 40vw;
--bar-width-expanded: 60vw;
--bar-background: rgba(0, 0, 0, 0.8);
--footer-background: rgba(0, 0, 0, 0.8);
--color-shadow: rgba(255, 255, 255, 0.7);
Expand Down Expand Up @@ -37,6 +38,11 @@
translate: -50% -50%;
width: var(--bar-width);
pointer-events: all;
transition: width 0.1s ease-out;
}

.commandBar.hasResults {
width: var(--bar-width-expanded);
}

.commandBar small {
Expand All @@ -51,6 +57,10 @@
transition: grid-template-rows 0.2s ease-in;
}

.resultsWrap > * {
opacity: 0;
}

.resultsWrap.expanded {
grid-template-rows: 1fr;
}
Expand All @@ -59,3 +69,6 @@
opacity: 1;
padding: 0.5rem;
}
.resultsWrap.split {
grid-template-columns: 1fr 1fr;
}
10 changes: 8 additions & 2 deletions packages/commandbar/src/CommandBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import CommandBarFooter from './CommandBarFooter/CommandBarFooter';
import CommandBarHeader from './CommandBarHeader/CommandBarHeader';
import CommandListing from './CommandList/CommandListing';
import { flattenCommands } from './helpers/flattenCommands';
import CommandResultsView from './CommandResultsView/CommandResultsView';

type CommandBarProps = {
commands: HierarchicalCommandList;
Expand All @@ -23,6 +24,7 @@ const initialState: CommandBarState = {
commands: {},
runningCommandId: null,
runningCommandMessage: null,
result: null,
};

const CommandBar: React.FC<CommandBarProps> = ({ commands, open, toggleOpen }) => {
Expand Down Expand Up @@ -99,6 +101,9 @@ const CommandBar: React.FC<CommandBarProps> = ({ commands, open, toggleOpen }) =
for await (const result of generator) {
console.debug('next value', result);
dispatch({ type: ACTIONS.RUNNING_COMMAND, commandId, argument: result.message });
if (result.options) {
dispatch({ type: ACTIONS.SET_RESULT, result });
}
}
dispatch({ type: ACTIONS.FINISHED_COMMAND });
}
Expand All @@ -118,21 +123,22 @@ const CommandBar: React.FC<CommandBarProps> = ({ commands, open, toggleOpen }) =
}

return (
<dialog className={styles.commandBar} open={open}>
<dialog className={[styles.commandBar, state.result && styles.hasResults].join(' ')} open={open}>
<CommandBarHeader
selectedCommandGroup={state.selectedCommandGroup}
searchWord={state.searchWord}
dispatch={dispatch}
handleSearch={handleSearch}
handleKeyEntered={handleKeyEntered}
/>
<div className={[styles.resultsWrap, state.expanded && styles.expanded].join(' ')}>
<div className={[styles.resultsWrap, state.expanded && styles.expanded, state.result && styles.split].join(' ')}>
<CommandListing
commands={state.commands}
availableCommandIds={state.availableCommandIds}
highlightedItem={state.highlightedItem}
handleSelectItem={handleSelectItem}
/>
{state.result && <CommandResultsView result={state.result} />}
</div>
{state.expanded && (
<CommandBarFooter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,10 @@
.highlighted {
background-color: var(--color-item-highlighted);
}

.label {
display: flex;
flex-wrap: wrap;
gap: 0 1em;
align-items: baseline;
}
6 changes: 4 additions & 2 deletions packages/commandbar/src/CommandList/CommandListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ const CommandListItem: React.FC<CommandListItemProps> = React.forwardRef(
ref={ref}
>
<Icon icon={icon} />
<span>{name}</span>
<small>{description}</small>
<span className={styles.label}>
<span>{name}</span>
<small>{description}</small>
</span>
</li>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
.results {
min-height: 0;
transition: opacity 0.3s ease-in;
opacity: 0;
overflow-y: auto;
max-height: 60vh;
}
Expand Down
4 changes: 3 additions & 1 deletion packages/commandbar/src/CommandList/CommandListing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ type CommandListingProps = {
availableCommandIds: CommandId[];
highlightedItem: number;
handleSelectItem: (commandId: CommandId) => void;
heading?: string;
};

const CommandListing: React.FC<CommandListingProps> = ({
commands,
availableCommandIds,
highlightedItem,
handleSelectItem,
heading = 'Commands',
}) => {
const selectedElementRef = React.useRef(null);

Expand All @@ -25,7 +27,7 @@ const CommandListing: React.FC<CommandListingProps> = ({

return (
<nav className={styles.results}>
<h6>Commands</h6>
{heading && <h6>{heading}</h6>}
{availableCommandIds.length > 0 ? (
<ul>
{availableCommandIds.map((commandId, index) => (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.commandResultsView {
max-width: 100%;
overflow: auto;
}
Loading

0 comments on commit f379b7d

Please sign in to comment.