LinkDesk/frontend/docs/shot-table-checkbox-fix.md

3.4 KiB

Shot Table Checkbox Selection Fix

Overview

Applied the same checkbox selection refactor to ShotsTableView component that was done for AssetBrowser, converting from array-based to object-based selection state.

Changes Made

1. Selection State Structure

Before:

const selectedShots = ref<number[]>([]);

After:

const selectedShots = ref<Record<number, boolean>>({});

2. Select All Checkbox

Before:

<Checkbox
  :checked="selectedShots.length === filteredShots.length && filteredShots.length > 0"
  :indeterminate="selectedShots.length > 0 && selectedShots.length < filteredShots.length"
  @update:checked="toggleSelectAll"
/>

After:

<Checkbox v-model="selectAllChecked" />

With computed property:

const selectAllChecked = computed({
  get: () => {
    return filteredShots.value.length > 0 && 
           filteredShots.value.every(shot => selectedShots.value[shot.id]);
  },
  set: (checked: boolean) => {
    filteredShots.value.forEach(shot => {
      selectedShots.value[shot.id] = checked;
    });
  }
});

3. Row Checkboxes

Before:

<Checkbox
  :checked="selectedShots.includes(shot.id)"
  @update:checked="(checked) => toggleShotSelection(shot.id, checked)"
  @click.stop
/>

After:

<Checkbox
  v-model="selectedShots[shot.id]"
  @click.stop
/>

4. Helper Method

Added helper to get selected IDs:

const getSelectedShotIds = () => {
  return Object.keys(selectedShots.value)
    .filter(id => selectedShots.value[Number(id)])
    .map(id => Number(id));
};

5. Row Selection Logic

Updated to work with object-based state:

const handleRowClick = (shot: Shot, event: MouseEvent) => {
  if (event.ctrlKey || event.metaKey) {
    // Multi-select with Ctrl/Cmd - toggle selection
    selectedShots.value[shot.id] = !selectedShots.value[shot.id];
  } else if (event.shiftKey && getSelectedShotIds().length > 0) {
    // Range select with Shift
    const selectedIds = getSelectedShotIds();
    const lastSelectedId = selectedIds[selectedIds.length - 1];
    // ... range selection logic
    selectedShots.value = {};
    for (let i = start; i <= end; i++) {
      selectedShots.value[filteredShots.value[i].id] = true;
    }
  } else {
    // Single select
    emit('select', shot);
  }
};

6. Watcher Update

Before:

watch(() => props.shots, () => {
  selectedShots.value = []
})

After:

watch(() => props.shots, () => {
  selectedShots.value = {}
})

Benefits

  1. Consistent with AssetBrowser: Both components now use the same pattern
  2. Direct v-model Binding: Uses reka-ui's native v-model support
  3. Better Performance: Object property access is faster than array operations
  4. Cleaner Code: No manual event handlers needed
  5. Fully Reactive: Vue's reactivity handles everything automatically

Testing

Test the following scenarios:

  1. Click individual checkboxes to select/deselect shots
  2. Click the "Select All" checkbox to select/deselect all shots
  3. Use Ctrl/Cmd+Click for multi-selection
  4. Use Shift+Click for range selection
  5. Verify the selection state persists correctly
  6. Verify the row highlighting reflects the selection state
  • frontend/src/components/shot/ShotsTableView.vue - Updated component
  • frontend/docs/checkbox-selection-refactor.md - Original AssetBrowser refactor documentation