Put the palette's kind filter in a menu on the search field
The chips took four lines of a narrow sidebar. A filter button at the end of the search field opens the kinds as checkboxes instead, and shows how many are picked; its tooltip says which. The menu lives in the overlay, so its font is set globally, where the palette's own styles do not reach. A storage's icon is 'storage': 'database' is not in the icon font, and showed nothing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
53439558b6
commit
1a7d139535
|
|
@ -185,50 +185,42 @@
|
|||
cursor: text;
|
||||
}
|
||||
|
||||
.blocks-list-kinds {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
padding: 0 2px 8px;
|
||||
}
|
||||
|
||||
.blocks-list-kind-chip {
|
||||
.blocks-list-filter-btn {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 2px 8px;
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 999px;
|
||||
background: #ffffff;
|
||||
color: #334155;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.blocks-list-kind-chip:hover {
|
||||
border-color: #94a3b8;
|
||||
}
|
||||
|
||||
.blocks-list-kind-chip--on {
|
||||
border-color: #2563eb;
|
||||
background: #eff6ff;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.blocks-list-kind-chip .blocks-list-card-kind {
|
||||
font-size: 13px;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
line-height: 13px;
|
||||
}
|
||||
|
||||
.blocks-list-kind-clear {
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 4px;
|
||||
border: none;
|
||||
background: none;
|
||||
border-radius: 8px;
|
||||
background: transparent;
|
||||
color: #64748b;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.blocks-list-filter-btn:hover {
|
||||
background: #f1f5f9;
|
||||
}
|
||||
|
||||
.blocks-list-filter-btn--on {
|
||||
color: #1d4ed8;
|
||||
background: #eff6ff;
|
||||
}
|
||||
|
||||
.blocks-list-filter-count {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
right: 1px;
|
||||
min-width: 14px;
|
||||
height: 14px;
|
||||
padding: 0 3px;
|
||||
border-radius: 999px;
|
||||
background: #2563eb;
|
||||
color: #ffffff;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
line-height: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,28 +15,44 @@
|
|||
<mat-label>Search blocks</mat-label>
|
||||
<mat-icon matPrefix fontIcon="search"></mat-icon>
|
||||
<input matInput placeholder="Search blocks..." #searchInput [value]="searchTerm()" (input)="searchTerm.set(searchInput.value)" />
|
||||
@if (availableKinds().length > 1) {
|
||||
<button
|
||||
matSuffix
|
||||
type="button"
|
||||
class="blocks-list-filter-btn"
|
||||
[class.blocks-list-filter-btn--on]="selectedKinds().size > 0"
|
||||
[matMenuTriggerFor]="kindMenu"
|
||||
[matTooltip]="selectedKinds().size ? 'Showing: ' + selectedKindLabels() : 'Filter by kind'"
|
||||
aria-label="Filter blocks by kind">
|
||||
<mat-icon fontIcon="filter_list"></mat-icon>
|
||||
@if (selectedKinds().size > 0) {
|
||||
<span class="blocks-list-filter-count">{{ selectedKinds().size }}</span>
|
||||
}
|
||||
</button>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-menu #kindMenu="matMenu" class="blocks-list-kind-menu">
|
||||
@for (kind of availableKinds(); track kind.label) {
|
||||
<button
|
||||
mat-menu-item
|
||||
type="button"
|
||||
role="menuitemcheckbox"
|
||||
[attr.aria-checked]="selectedKinds().has(kind.label)"
|
||||
(click)="$event.stopPropagation(); toggleKind(kind.label)">
|
||||
<mat-icon [fontIcon]="selectedKinds().has(kind.label) ? 'check_box' : 'check_box_outline_blank'"></mat-icon>
|
||||
<mat-icon [class]="'blocks-list-card-kind blocks-list-card-kind--' + kind.modifier" [fontIcon]="kind.icon"></mat-icon>
|
||||
<span>{{ kind.label }}</span>
|
||||
</button>
|
||||
}
|
||||
@if (selectedKinds().size > 0) {
|
||||
<button mat-menu-item type="button" (click)="clearKinds()">
|
||||
<mat-icon fontIcon="close"></mat-icon>
|
||||
<span>Show every kind</span>
|
||||
</button>
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
|
||||
@if (availableKinds().length > 1) {
|
||||
<div class="blocks-list-kinds" role="group" aria-label="Filter blocks by kind">
|
||||
@for (kind of availableKinds(); track kind.label) {
|
||||
<button
|
||||
type="button"
|
||||
class="blocks-list-kind-chip"
|
||||
[class.blocks-list-kind-chip--on]="selectedKinds().has(kind.label)"
|
||||
[attr.aria-pressed]="selectedKinds().has(kind.label)"
|
||||
(click)="toggleKind(kind.label)">
|
||||
<mat-icon [class]="'blocks-list-card-kind blocks-list-card-kind--' + kind.modifier" [fontIcon]="kind.icon"></mat-icon>
|
||||
<span>{{ kind.label }}</span>
|
||||
</button>
|
||||
}
|
||||
@if (selectedKinds().size > 0) {
|
||||
<button type="button" class="blocks-list-kind-clear" (click)="clearKinds()">Clear</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (filteredBlocks().length === 0) {
|
||||
<div class="blocks-list-empty">
|
||||
No blocks found
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { MatCardModule } from '@angular/material/card';
|
|||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { BLOCK_TYPE_DRAG_MIME } from './block-drag';
|
||||
|
|
@ -28,7 +29,7 @@ export type BlockKind = { icon: string; label: string; modifier: string };
|
|||
export function blockKind(block: Pick<BlockType, 'userInteractive' | 'capabilities'>): BlockKind {
|
||||
const capabilities = block.capabilities;
|
||||
const decides = capabilities?.visualRole === 'DECISION' || capabilities?.routesExclusively === true;
|
||||
if (capabilities?.visualRole === 'RESOURCE') return { icon: 'database', label: 'Resource', modifier: 'resource' };
|
||||
if (capabilities?.visualRole === 'RESOURCE') return { icon: 'storage', label: 'Resource', modifier: 'resource' };
|
||||
if (block.userInteractive) {
|
||||
return decides
|
||||
? { icon: 'touch_app', label: 'Human decision', modifier: 'interactive' }
|
||||
|
|
@ -42,7 +43,7 @@ export function blockKind(block: Pick<BlockType, 'userInteractive' | 'capabiliti
|
|||
|
||||
@Component({
|
||||
selector: 'app-blocks-list',
|
||||
imports: [MatCardModule, MatFormFieldModule, MatIconModule, MatInputModule, MatProgressSpinnerModule, MatTooltipModule],
|
||||
imports: [MatCardModule, MatFormFieldModule, MatIconModule, MatInputModule, MatMenuModule, MatProgressSpinnerModule, MatTooltipModule],
|
||||
templateUrl: './blocks-list.html',
|
||||
styleUrl: './blocks-list.css',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
|
@ -147,6 +148,12 @@ export class BlocksList extends ListStateViewHolder<BlockType> {
|
|||
this.selectedKinds.set(new Set());
|
||||
}
|
||||
|
||||
/** For the filter button's tooltip, in the order the menu lists them. */
|
||||
selectedKindLabels(): string {
|
||||
const selected = this.selectedKinds();
|
||||
return this.availableKinds().filter((kind) => selected.has(kind.label)).map((kind) => kind.label).join(', ');
|
||||
}
|
||||
|
||||
readonly showLoading = computed(() => this.loading() || this.serviceLoading());
|
||||
|
||||
kindOf(block: BlockType): BlockKind {
|
||||
|
|
|
|||
|
|
@ -654,3 +654,24 @@ body.node-focus-modal-open {
|
|||
.inner-nav {
|
||||
@apply flex flex-wrap justify-center gap-4 items-center max-w-7xl;
|
||||
}
|
||||
|
||||
/* The palette's kind filter: its menu lives in the overlay, outside the palette's own styles. */
|
||||
.blocks-list-kind-menu {
|
||||
--mat-menu-item-label-text-font: var(--font-body);
|
||||
--mat-menu-item-label-text-size: 13px;
|
||||
--mat-menu-item-label-text-weight: 500;
|
||||
}
|
||||
|
||||
.blocks-list-kind-menu .mat-mdc-menu-item {
|
||||
min-height: 36px;
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
|
||||
.blocks-list-kind-menu .mat-mdc-menu-item .mat-mdc-menu-item-text {
|
||||
font-family: var(--font-body);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.blocks-list-kind-menu .mat-mdc-menu-item .mat-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue