xref: /openbmc/webui-vue/src/components/Composables/useTableSelection.js (revision d3b05033fe82aed6c53f4f2b52c23d3bd285d423)
1/**
2 * Composable for table selection utilities
3 * Extracted from BVTableSelectableMixin for use in Composition API
4 */
5
6import { ref, nextTick, watch } from 'vue';
7
8export function useTableSelection(currentPage = ref(1)) {
9  const selectedRows = ref([]);
10  const tableHeaderCheckboxModel = ref(false);
11  const tableHeaderCheckboxIndeterminate = ref(false);
12
13  // Watch for page changes and clear selections
14  // This prevents confusion with checkboxes appearing checked on the new page
15  watch(currentPage, (newPage, oldPage) => {
16    if (newPage !== oldPage) {
17      selectedRows.value = [];
18      tableHeaderCheckboxModel.value = false;
19      tableHeaderCheckboxIndeterminate.value = false;
20    }
21  });
22
23  const clearSelectedRows = (tableRef) => {
24    if (tableRef) {
25      tableRef.clearSelected();
26      selectedRows.value = [];
27      tableHeaderCheckboxModel.value = false;
28      tableHeaderCheckboxIndeterminate.value = false;
29    }
30  };
31
32  const toggleSelectRow = (tableRef, rowIndex) => {
33    if (tableRef && rowIndex !== undefined) {
34      const wasSelected = tableRef.isRowSelected(rowIndex);
35
36      if (wasSelected) {
37        tableRef.unselectRow(rowIndex);
38      } else {
39        tableRef.selectRow(rowIndex);
40      }
41
42      nextTick(() => {
43        onRowSelected(tableRef);
44      });
45    }
46  };
47
48  const onRowSelected = (tableRef) => {
49    if (!tableRef) return;
50
51    const allItems = tableRef.filteredItems || tableRef.items || [];
52    const selectedItems = allItems.filter((item, index) => {
53      return tableRef.isRowSelected(index);
54    });
55
56    selectedRows.value = selectedItems;
57
58    const currentPage = 1;
59    const perPage = allItems.length;
60    const startIndex = (currentPage - 1) * perPage;
61    const endIndex = Math.min(startIndex + perPage, allItems.length);
62    const pageItemsCount = endIndex - startIndex;
63
64    const selectedOnPageCount = selectedItems.filter((item) =>
65      allItems
66        .slice(startIndex, endIndex)
67        .some((pageItem) => pageItem === item),
68    ).length;
69
70    if (selectedOnPageCount === 0) {
71      tableHeaderCheckboxIndeterminate.value = false;
72      tableHeaderCheckboxModel.value = false;
73    } else if (selectedOnPageCount === pageItemsCount) {
74      tableHeaderCheckboxIndeterminate.value = false;
75      tableHeaderCheckboxModel.value = true;
76    } else {
77      tableHeaderCheckboxIndeterminate.value = true;
78      tableHeaderCheckboxModel.value = true;
79    }
80  };
81
82  const onChangeHeaderCheckbox = (tableRef, event) => {
83    /*
84     * Bootstrap Vue Next Migration:
85     * Handle header checkbox to select/deselect all rows on current page.
86     */
87    if (!tableRef) return;
88
89    const isChecked =
90      typeof event === 'boolean' ? event : event?.target?.checked;
91
92    if (isChecked) {
93      const allItems = tableRef.filteredItems || tableRef.items || [];
94      const endIndex = allItems.length;
95
96      for (let i = 0; i < endIndex; i++) {
97        tableRef.selectRow(i);
98      }
99    } else {
100      tableRef.clearSelected();
101      selectedRows.value = [];
102      tableHeaderCheckboxModel.value = false;
103      tableHeaderCheckboxIndeterminate.value = false;
104    }
105
106    nextTick(() => {
107      onRowSelected(tableRef);
108    });
109  };
110
111  return {
112    selectedRows,
113    tableHeaderCheckboxModel,
114    tableHeaderCheckboxIndeterminate,
115    clearSelectedRows,
116    toggleSelectRow,
117    onRowSelected,
118    onChangeHeaderCheckbox,
119  };
120}
121