PalettePanel.svelte 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. <script lang="ts">
  2. /**
  3. * The results panel under the search box (design spec §3.7).
  4. *
  5. * It renders whatever `palette.view` is: the entry points when the box is
  6. * empty, the ranked kind groups when it is not. The keyboard lives in
  7. * `TopBar` (the keys are pressed in the input, not here) and arrives as the
  8. * `selected` index; this component's only job beyond drawing is keeping that
  9. * row in view when the selection moves past the panel's edge.
  10. */
  11. import PaletteRows from './PaletteRows.svelte';
  12. import { palette } from '../lib/palette.svelte';
  13. import type { PaletteItem } from '../lib/search-model';
  14. interface Props {
  15. onpick: (item: PaletteItem) => void;
  16. }
  17. let { onpick }: Props = $props();
  18. let panel: HTMLDivElement | null = $state(null);
  19. let view = $derived(palette.view);
  20. $effect(() => {
  21. const index = palette.selected;
  22. if (!panel) return;
  23. const row = panel.querySelector(`[data-palette-row="${index}"]`);
  24. row?.scrollIntoView({ block: 'nearest' });
  25. });
  26. </script>
  27. <div class="panel" bind:this={panel} id="palette-panel" role="listbox" aria-label="Search results">
  28. {#if view.hint}
  29. <p class="hint">{view.hint}</p>
  30. {/if}
  31. <PaletteRows
  32. palette={view}
  33. selected={palette.selected}
  34. rowRole="option"
  35. {onpick}
  36. onhover={(index) => palette.select(index)}
  37. />
  38. {#if palette.failure}
  39. <p class="note">{palette.failure}</p>
  40. {:else if palette.pending && view.items.length === 0}
  41. <p class="note">Searching…</p>
  42. {:else if view.empty}
  43. <p class="note">{view.empty}</p>
  44. {/if}
  45. </div>
  46. <style>
  47. .panel {
  48. position: absolute;
  49. z-index: 40;
  50. top: 32px;
  51. right: 0;
  52. left: 0;
  53. max-height: 420px;
  54. overflow: auto;
  55. background: var(--paper);
  56. border: 1px solid var(--ink);
  57. }
  58. .hint {
  59. margin: 0;
  60. padding: 8px 10px;
  61. border-bottom: 1px solid var(--rule-faint);
  62. background: var(--paper-2);
  63. color: var(--ink-2);
  64. font-size: 12px;
  65. }
  66. .note {
  67. margin: 0;
  68. padding: 8px 10px;
  69. color: var(--ink-3);
  70. font-size: 12px;
  71. }
  72. </style>