{
  "id": "TB03",
  "title": "Search and view only",
  "topic": "toolbar",
  "use": "A list that is only read has no actions in its toolbar — what remains is the search, the quick filter and the switch between views. The search leads at the left. The quick filter and the view switch keep their place at the right edge, where they stand on every other list of the module, even with nothing on the left but the search.",
  "decisions": [
    "A list that is only read has no actions in its toolbar: what remains is the search, the quick filter and the view switch.",
    "The quick filter and the view switch keep their place at the right edge, as on every other list of the module, even with nothing on the left but the search: a control that moves because a neighbor is missing breaks the promise its position carries.",
    "The quick filter and the view switch stand next to each other, with nothing between the two segmented controls, not even a filter button.",
    "Every segment keeps its text. An icon may join it but does not replace it: a switch of three symbols is guessed, not read.",
    "The quick filter covers everything — all, open, completed — so every voucher lies in one of its values and none is left out. A segmented control holds two or three short values; from four values on, a select."
  ],
  "try": [
    {
      "do": "Type “Hotel” in the search field",
      "then": "the list gets shorter, and the count below follows"
    },
    {
      "do": "Set the quick filter to Open",
      "then": "only the open vouchers are left; the others are under Completed"
    },
    {
      "do": "Search as well",
      "then": "both apply together, and the count says how many of how many"
    },
    {
      "do": "Click a column header",
      "then": "the list sorts by that column"
    },
    {
      "do": "Switch to Cards",
      "then": "the same vouchers with the same details; the quick filter stays where it is"
    },
    {
      "do": "Look to the right",
      "then": "the quick filter and the view switch stand at the right edge, as on every other list of the module"
    }
  ],
  "revision": 2,
  "changes": [
    {
      "revision": 2,
      "change": "The quick filter is a segmented control of two or three short values; from four values on, it is a select, since a longer row of segments reads as a view switch."
    }
  ],
  "reviewed": true,
  "source": "<style>\n  /* The cards scroll in a region of their own, as the table does. */\n  #tb03-cards {\n    overflow: auto;\n  }\n</style>\n\n<!-- Demo only, not part of the pattern. The table and the cards take the height the screen leaves them; here they are set to fit the frame. -->\n\n<div class=\"ed-stack ed-gap-4\">\n  <!-- Demo only, not part of the pattern. The screen's title comes from the screen around the pattern; SC01 places it. -->\n\n  <ed-toolbar id=\"tb03-toolbar\" label=\"Vouchers\">\n    <ed-input slot=\"search\" type=\"search\" placeholder=\"Voucher, posting text or type\" aria-label=\"Search vouchers\" style=\"inline-size: 18rem\"></ed-input>\n\n    <ed-radio-group id=\"tb03-state\" slot=\"view\" appearance=\"button\" aria-label=\"Processing state\" value=\"all\">\n      <ed-radio value=\"all\">All</ed-radio>\n      <ed-radio value=\"open\">Open</ed-radio>\n      <ed-radio value=\"completed\">Completed</ed-radio>\n    </ed-radio-group>\n    <ed-radio-group id=\"tb03-view\" slot=\"view\" appearance=\"button\" aria-label=\"View\" value=\"table\">\n      <ed-radio value=\"table\"><ed-icon slot=\"start\" name=\"table\"></ed-icon>Table</ed-radio>\n      <ed-radio value=\"cards\"><ed-icon slot=\"start\" name=\"grid\"></ed-icon>Cards</ed-radio>\n    </ed-radio-group>\n\n    <span id=\"tb03-count\" slot=\"meta\" role=\"status\" tabindex=\"-1\"></span>\n  </ed-toolbar>\n\n  <ed-data-grid id=\"tb03-grid\" label=\"Vouchers\" without-toolbar data-ed-preload=\"ed-format-date\">\n    <ed-empty-state slot=\"no-results\" size=\"small\" icon=\"search\">\n      <span id=\"tb03-none-label\" slot=\"label\"></span>\n      <span id=\"tb03-none-text\"></span>\n      <ed-button id=\"tb03-clear\" slot=\"button\" appearance=\"outlined\"></ed-button>\n    </ed-empty-state>\n  </ed-data-grid>\n\n  <div id=\"tb03-cards\" hidden>\n    <ul class=\"ed-grid ed-list-plain ed-m-0\" style=\"--ed-grid-min-column-size: 15rem\"></ul>\n  </div>\n</div>\n\n<!-- Demo only, not part of the pattern. The product supplies vouchers: the vouchers of the list, as { id, date, text, type, status }, with date as YYYY-MM-DD. -->\n\n<script type=\"module\">\n  const search = document.querySelector('#tb03-toolbar ed-input');\n  const state = document.getElementById('tb03-state');\n  const view = document.getElementById('tb03-view');\n  const count = document.getElementById('tb03-count');\n  const grid = document.getElementById('tb03-grid');\n  const cards = document.getElementById('tb03-cards');\n  const noneLabel = document.getElementById('tb03-none-label');\n  const noneText = document.getElementById('tb03-none-text');\n  const clearButton = document.getElementById('tb03-clear');\n\n  const statusVariant = { Open: 'neutral', Error: 'danger', Reviewed: 'info', Finalized: 'success' };\n\n  // Built from elements, not from an HTML string: a voucher's text is data and never becomes markup.\n  const element = (tag, properties) => Object.assign(document.createElement(tag), properties);\n  const day = value => element('ed-format-date', { date: value, month: 'short', day: 'numeric', year: 'numeric' });\n  const card = row => {\n    const badge = element('ed-badge', { slot: 'header-end', textContent: row.status });\n    badge.setAttribute('variant', statusVariant[row.status]);\n    const body = element('div', { className: 'ed-stack ed-gap-1' });\n    const caption = element('p', { className: 'ed-caption-m' });\n    caption.append(day(row.date), ` · ${row.type}`);\n    body.append(element('p', { textContent: row.text }), caption);\n    const box = element('ed-card');\n    box.append(element('h3', { slot: 'header', textContent: row.id }), badge, body);\n    const item = element('li');\n    item.append(box);\n    return item;\n  };\n\n  // The search covers what the empty state names: number, posting text and type.\n  grid.columns = [\n    { field: 'id', label: 'Voucher', width: 140, rowheader: true },\n    { field: 'date', label: 'Date', width: 130, formatter: day, searchable: false },\n    { field: 'text', label: 'Posting text', flex: 2, minWidth: 220 },\n    { field: 'type', label: 'Type', width: 170 },\n    {\n      field: 'status',\n      label: 'Status',\n      width: 130,\n      searchable: false,\n      formatter: value => {\n        const badge = document.createElement('ed-badge');\n        badge.setAttribute('variant', statusVariant[value]);\n        badge.textContent = value;\n        return badge;\n      }\n    }\n  ];\n\n  // The quick filter narrows the data itself rather than setting a column filter: it stands\n  // visibly in the toolbar, so it gets no badge in the row below. Its three values cover every\n  // voucher — completed is reviewed and finalized together.\n  const states = {\n    all: () => true,\n    open: voucher => voucher.status === 'Open',\n    completed: voucher => voucher.status === 'Reviewed' || voucher.status === 'Finalized'\n  };\n\n  const entries = number => (number === 1 ? '1 entry' : `${number} entries`);\n\n  const update = async () => {\n    await grid.updateComplete;\n    count.textContent = grid.filteredCount === vouchers.length ? entries(vouchers.length) : `${grid.filteredCount} of ${entries(vouchers.length)}`;\n\n    const term = grid.searchTerm.trim();\n    const quick = state.querySelector(`ed-radio[value=\"${state.value}\"]`).textContent.trim();\n    const [label, text, action] = state.value === 'all'\n      ? [`No match for “${term}”`, 'No voucher contains this text in its number, posting text or type.', 'Clear search']\n      : [`No match for “${term}”`, `Only the vouchers in “${quick}” are searched.`, 'Reset search and quick filter'];\n    noneLabel.textContent = label;\n    noneText.textContent = text;\n    clearButton.textContent = action;\n\n    const rows = grid.getProcessedRows();\n    const asCards = view.value === 'cards' && rows.length > 0;\n    grid.hidden = asCards;\n    cards.hidden = !asCards;\n    cards.firstElementChild.replaceChildren(...rows.map(card));\n  };\n\n  const showState = () => {\n    grid.data = vouchers.filter(states[state.value]);\n    return update();\n  };\n\n  search.addEventListener('input', () => {\n    grid.searchTerm = search.value;\n    update();\n  });\n  state.addEventListener('change', showState);\n  view.addEventListener('change', update);\n  // The empty state leaves with the rows its button brings back; the count takes the focus.\n  clearButton.addEventListener('click', () => {\n    grid.searchTerm = '';\n    search.value = '';\n    state.value = 'all';\n    showState().then(() => count.focus());\n  });\n  grid.addEventListener('ed-data-grid-sort-change', update);\n\n  await Promise.all(['ed-data-grid', 'ed-input', 'ed-radio-group'].map(tag => customElements.whenDefined(tag)));\n  showState();\n</script>",
  "page": "https://test-apps.eurodata.de/a/eurodata-ui/docs/patterns/toolbar/tb03-search-and-view/",
  "topicPage": "https://test-apps.eurodata.de/a/eurodata-ui/docs/patterns/toolbar/"
}
