web-componentarcgisesriarcgis-js-api

How to deselect an already-selected web component list item?


I've been trying to make use of list web components from ESRI Calcite Design System.

With the code snippet below I cannot manage to deselect an already-selected list item when it's clicked a second time.

I'm not stuck with using the Calcite Design System. I could use another web component library (e.g., Material Design) but I cannot find any examples.

"use strict";

const listNode = document.getElementById("point-list")

// Cannot use the event listener below because nothing's fired when already-selected item is clicked          
//listNode.addEventListener("calciteListChange", onListClickHandler)

listNode.addEventListener("click", onListClickHandler)

const currentSelectedListItem = (function(){
  let currentItemValue
  return {
    is_same: function(selectedItemValue) {
      if (currentItemValue != selectedItemValue) {
        currentItemValue = selectedItemValue
        return false
      }
      else {
        return true
      }
    },
    reset: function() {
      currentItemValue = null
    }
  }
})()

function onListClickHandler(event) {
  const target = event.target
  const targetId = target.value

  if (currentSelectedListItem.is_same(targetId)) {
    console.log("already selected")
  
    // Test 1 (FAILS)
    target.toggleSelected()

    // Test 2 (FAILS)
    // target.removeAttribute("selected")
 
    // Test 4 (FAILS)
    // Array.from(listNode.childNodes).forEach(item => item.selected = false)

    // Test 5 (FAILS)
    // const listItems = listNode.querySelectorAll("calcite-value-list-item")
    // listItems.forEach(item => item.selected = false)
  }
  else {
    console.log("new selection")
    // Do some stuff
  }
}
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no"/>

    <title>deselect already-selected value list item</title>

    <script src="https://js.arcgis.com/4.25/"></script>
    <link rel="stylesheet" href="https://js.arcgis.com/4.25/esri/themes/light/main.css"/>

    <script type="module" src="https://js.arcgis.com/calcite-components/1.0.0-beta.99/calcite.esm.js"></script>
    <link rel="stylesheet" type="text/css" href=https://js.arcgis.com/calcite-components/1.0.0-beta.99/calcite.css />
  </head>

  <body>
    <calcite-shell class="calcite-tutorial">
      <calcite-shell-panel slot="panel-end">
        <calcite-panel heading="Point list">
          <calcite-value-list id="point-list">
            <calcite-value-list-item label="point 1" value="point_1" description="some description"></calcite-value-list-item>
            <calcite-value-list-item label="point 2" value="point_2" description="some description"></calcite-value-list-item>
            <calcite-value-list-item label="point 3" value="point_3" description="some description"></calcite-value-list-item>
          </calcite-value-list>
        </calcite-panel>
      </calcite-shell-panel>
    </calcite-shell>
  </body>
</html>

Could you please help me?


Solution

  • I think the following example does what you want.

    Note:

    Here is my version:

    "use strict";
    
    const otherElement = document.getElementById("other-element");
    const listNode = document.getElementById("point-list")
    let lastSelectedItem;
    let lastSelectedValue;
    
    listNode.addEventListener("click", onListClickHandler)
    
    function onListClickHandler(event) {
      if (event.target.id === lastSelectedItem) {
        event.target.selected = !lastSelectedValue;
        
        // If you don't want to see the focus rectangle around the deselected item, then
        // selected another focusable element, and optionally call blur() on the new active element.
        if (event.target.selected == false) {
          otherElement.focus();
          document.activeElement.blur();
        }
      }
      lastSelectedItem = event.target.id;
      lastSelectedValue = event.target.selected
      
      // Just updating the descriptions in this example to show the selected state of each list item.
      for(let listItem of event.target.parentElement.children) {
        listItem.description = listItem.selected ? "Selected" : "NOT Selected";
      }
    }
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no"/>
    
        <title>deselect already-selected value list item</title>
    
        <script src="https://js.arcgis.com/4.25/"></script>
        <link rel="stylesheet" href="https://js.arcgis.com/4.25/esri/themes/light/main.css"/>
    
        <script type="module" src="https://js.arcgis.com/calcite-components/1.0.0-beta.99/calcite.esm.js"></script>
        <link rel="stylesheet" type="text/css" href=https://js.arcgis.com/calcite-components/1.0.0-beta.99/calcite.css />
      </head>
    
      <body>
        <calcite-shell class="calcite-tutorial">
          <!-- Just adding another focusable element to show how to remove focus rectangle on deselected elements-->
          <label tabIndex="1" id="other-element">Some other focusable element</label>
          <calcite-shell-panel slot="panel-end">
            <calcite-panel heading="Point list">
              <calcite-value-list id="point-list">
                <calcite-value-list-item id="item_1" label="point 1" value="point_1" description="some description"></calcite-value-list-item>
                <calcite-value-list-item id="item_2" label="point 2" value="point_2" description="some description"></calcite-value-list-item>
                <calcite-value-list-item id="item_3" label="point 3" value="point_3" description="some description"></calcite-value-list-item>
              </calcite-value-list>
            </calcite-panel>
          </calcite-shell-panel>
        </calcite-shell>
      </body>
    </html>