diff --git a/packages/host/app/components/card-chooser/mini/index.gts b/packages/host/app/components/card-chooser/mini/index.gts new file mode 100644 index 0000000000..f40aae5e95 --- /dev/null +++ b/packages/host/app/components/card-chooser/mini/index.gts @@ -0,0 +1,105 @@ +import { action } from '@ember/object'; +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; + +import type { Filter } from '@cardstack/runtime-common'; + +import SearchPanel from '@cardstack/host/components/card-search/panel'; +import { + removeFileExtension, + type NewCardArgs, +} from '@cardstack/host/utils/card-search/types'; + +interface Signature { + Element: HTMLDivElement; + Args: { + initialSearchKey?: string; + baseFilter?: Filter; + onSelect: (url: string) => void; + // URL of the currently selected card — the matching row gets the teal + // selection treatment + checkmark. Omit for a chooser that surfaces + // matches but doesn't persist a pinned selection. + selected?: string; + }; +} + +export default class MiniCardChooser extends Component { + @tracked private searchKey: string = this.args.initialSearchKey ?? ''; + + // SearchContent expects an array (it's the multi-select API plumbing). + // Wrap the single-select `@selected` so the existing isCardSelected check + // in SearchResultSection picks it up without any other changes. + private get selectedCards(): string[] | undefined { + return this.args.selected ? [this.args.selected] : undefined; + } + + @action + private setSearchKey(value: string) { + this.searchKey = value; + } + + @action + private handleSelect(selection: string | NewCardArgs) { + if (typeof selection !== 'string') { + return; + } + let normalized = removeFileExtension(selection); + if (normalized) { + this.args.onSelect(normalized); + } + } + + +} diff --git a/packages/host/app/components/card-chooser/mini/usage.gts b/packages/host/app/components/card-chooser/mini/usage.gts new file mode 100644 index 0000000000..87258f1879 --- /dev/null +++ b/packages/host/app/components/card-chooser/mini/usage.gts @@ -0,0 +1,71 @@ +import { action } from '@ember/object'; +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; + +import FreestyleUsage from 'ember-freestyle/components/freestyle/usage'; + +import MiniCardChooser from './index'; + +export default class MiniCardChooserUsage extends Component { + @tracked selectedUrl: string | undefined; + + @action onSelect(url: string) { + this.selectedUrl = url; + } + + +} diff --git a/packages/host/app/components/card-search/constants.ts b/packages/host/app/components/card-search/constants.ts index ec99388ebb..6b50dafcdd 100644 --- a/packages/host/app/components/card-search/constants.ts +++ b/packages/host/app/components/card-search/constants.ts @@ -24,6 +24,11 @@ export const VIEW_OPTIONS: ViewOption[] = [ { id: 'strip', icon: StripViewIcon }, ]; +// 'mini' is an internal-only view id that opt-in consumers (e.g. +// MiniCardChooser) request via @variant='mini'. It is deliberately +// not in VIEW_OPTIONS so the user-facing view picker stays grid/strip. +export type SectionViewOption = 'grid' | 'strip' | 'mini'; + /** Initial display limit for sections when not focused */ export const SECTION_DISPLAY_LIMIT_UNFOCUSED = 5; diff --git a/packages/host/app/components/card-search/item-button.gts b/packages/host/app/components/card-search/item-button.gts index b84e17c8e8..59105ab726 100644 --- a/packages/host/app/components/card-search/item-button.gts +++ b/packages/host/app/components/card-search/item-button.gts @@ -9,7 +9,7 @@ import { modifier } from 'ember-modifier'; import { Button } from '@cardstack/boxel-ui/components'; import { and, cn, not } from '@cardstack/boxel-ui/helpers'; -import { IconPlus } from '@cardstack/boxel-ui/icons'; +import { CheckMark, IconPlus } from '@cardstack/boxel-ui/icons'; import { cardTypeDisplayName, @@ -124,6 +124,11 @@ interface Signature { Element: HTMLElement; Args: { Positional: [cardEl: HTMLElement | undefined] }; }>; + // When true, render a right-aligned inside the row whenever + // @isSelected is set and the row is not in multi-select mode. Used by + // the mini-chooser variant whose selection treatment is a teal fill + + // checkmark rather than the legacy border-color shift. + showSelectedCheckmark?: boolean; }; } @@ -364,6 +369,22 @@ export default class ItemButton extends Component { {{/if}} {{/if}} + {{#if + (and + @showSelectedCheckmark + @isSelected + (not @multiSelect) + (not this.isNewCard) + ) + }} +