Add button to sheet and shift selector
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
let sortColumn = $state<keyof RowData | null>(null);
|
let sortColumn = $state<keyof RowData | null>(null);
|
||||||
let sortDirection = $state<'asc' | 'desc'>('asc');
|
let sortDirection = $state<'asc' | 'desc'>('asc');
|
||||||
|
let lastCheckedId: string | null = $state(null);
|
||||||
|
|
||||||
const ROW_LIMIT = 200;
|
const ROW_LIMIT = 200;
|
||||||
|
|
||||||
@@ -85,20 +86,52 @@
|
|||||||
} finally {
|
} finally {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
}
|
}
|
||||||
} // Run on component mount
|
}
|
||||||
|
|
||||||
|
function handleRowClick(event: MouseEvent, clickedId: string) {
|
||||||
|
const clickedRow = rows.find((r) => r.id === clickedId);
|
||||||
|
if (!clickedRow || !clickedRow._valid) return;
|
||||||
|
|
||||||
|
// Handle shift-clicking for range selection
|
||||||
|
if (event.shiftKey && lastCheckedId) {
|
||||||
|
const lastIndex = displayData.findIndex((r) => r.id === lastCheckedId);
|
||||||
|
const currentIndex = displayData.findIndex((r) => r.id === clickedId);
|
||||||
|
|
||||||
|
if (lastIndex !== -1 && currentIndex !== -1) {
|
||||||
|
const start = Math.min(lastIndex, currentIndex);
|
||||||
|
const end = Math.max(lastIndex, currentIndex);
|
||||||
|
const isChecked = !clickedRow._checked; // The state to apply to the range
|
||||||
|
|
||||||
|
for (let i = start; i <= end; i++) {
|
||||||
|
const rowToSelect = displayData[i];
|
||||||
|
if (rowToSelect && rowToSelect._valid) {
|
||||||
|
// Prevent checking more than the limit
|
||||||
|
if (isChecked && selectedCount >= ROW_LIMIT && !rowToSelect._checked) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rowToSelect._checked = isChecked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Normal click, just toggle the state
|
||||||
|
if (!clickedRow._checked && selectedCount >= ROW_LIMIT) {
|
||||||
|
// Do not allow checking more than the limit
|
||||||
|
} else {
|
||||||
|
clickedRow._checked = !clickedRow._checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the last checked ID for the next shift-click
|
||||||
|
lastCheckedId = clickedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run on component mount
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
ensureToken();
|
ensureToken();
|
||||||
fetchAndProcessData();
|
fetchAndProcessData();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Function to toggle a single row's checked state
|
|
||||||
function toggleRow(id: string) {
|
|
||||||
const row = rows.find((r) => r.id === id);
|
|
||||||
if (row && row._valid) {
|
|
||||||
row._checked = !row._checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to toggle select-all: selects first 200 eligible items in current view
|
// Function to toggle select-all: selects first 200 eligible items in current view
|
||||||
function toggleSelectAll(event: Event) {
|
function toggleSelectAll(event: Event) {
|
||||||
const target = event.target as HTMLInputElement;
|
const target = event.target as HTMLInputElement;
|
||||||
@@ -169,28 +202,33 @@
|
|||||||
<div>
|
<div>
|
||||||
<h2 class="mb-2 text-xl font-semibold text-gray-900">Filter and Select Rows</h2>
|
<h2 class="mb-2 text-xl font-semibold text-gray-900">Filter and Select Rows</h2>
|
||||||
<p class="text-sm text-gray-700">
|
<p class="text-sm text-gray-700">
|
||||||
Review your data and select which rows to include. Select a batch of max 200 items by using the top checkbox.
|
Review your data and select which rows to include. Select a batch of max 200 items by using
|
||||||
|
the top checkbox.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-gray-700">
|
<p class="mt-1 text-sm text-gray-700">
|
||||||
|
Tip: Hold <kbd
|
||||||
|
class="rounded-md border border-gray-400 bg-gray-200 px-1.5 py-0.5 text-xs font-semibold"
|
||||||
|
>Shift</kbd
|
||||||
|
> and click two checkboxes to select a range of rows.
|
||||||
|
</p>
|
||||||
|
<p class="mt-1 text-sm text-gray-700">
|
||||||
Already printed or invalid data is marked in the status column.
|
Already printed or invalid data is marked in the status column.
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col space-y-2">
|
||||||
{#if $selectedSheet?.id}
|
{#if $selectedSheet?.id}
|
||||||
<p class="mt-1 text-sm text-gray-500">
|
|
||||||
Need to make changes?
|
|
||||||
<a
|
<a
|
||||||
href={`https://docs.google.com/spreadsheets/d/${$selectedSheet.id}/edit`}
|
href={`https://docs.google.com/spreadsheets/d/${$selectedSheet.id}/edit`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="text-blue-600 underline hover:text-blue-800"
|
class="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
||||||
>
|
>
|
||||||
Open the spreadsheet
|
Open Sheet
|
||||||
</a>
|
</a>
|
||||||
</p>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
|
||||||
<button
|
<button
|
||||||
onclick={fetchAndProcessData}
|
onclick={fetchAndProcessData}
|
||||||
class="inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:cursor-wait disabled:opacity-50"
|
class="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:cursor-wait disabled:opacity-50"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
{#if isLoading}
|
{#if isLoading}
|
||||||
@@ -220,6 +258,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if isLoading}
|
{#if isLoading}
|
||||||
<div class="py-12 text-center">
|
<div class="py-12 text-center">
|
||||||
@@ -313,7 +352,7 @@
|
|||||||
class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500 disabled:cursor-not-allowed disabled:bg-gray-200"
|
class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500 disabled:cursor-not-allowed disabled:bg-gray-200"
|
||||||
checked={row._checked}
|
checked={row._checked}
|
||||||
disabled={!row._valid || (selectedCount >= ROW_LIMIT && !row._checked)}
|
disabled={!row._valid || (selectedCount >= ROW_LIMIT && !row._checked)}
|
||||||
onchange={() => toggleRow(row.id)}
|
onclick={(e) => handleRowClick(e, row.id)}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td class="whitespace-nowrap px-4 py-3 text-sm">{row._rowIndex}</td>
|
<td class="whitespace-nowrap px-4 py-3 text-sm">{row._rowIndex}</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user