How GeoLibre's Attribute Table Integrates with Layer Selections: A Technical Deep Dive

GeoLibre's attribute table integrates with layer selections through a centralized Zustand store that synchronizes selected feature IDs bidirectionally between the map and table components, enabling real-time multi-selection with keyboard modifiers and automatic UI scrolling.

The GeoLibre desktop application implements a tightly coupled, store-driven architecture that keeps the attribute table synchronized with the current layer's selection state. When users select features on the map or click rows in the table, the system updates a shared global state managed by @geolibre/core, ensuring all panels reflect the same selection set instantly. This integration relies on pure selection logic, virtualized scrolling, and persistent anchor tracking to deliver a seamless geographic information system (GIS) experience.

Store-Driven Selection Architecture

The attribute table reads its selection state directly from the Zustand store via the useAppStore hook. According to the source code in apps/geolibre-desktop/src/components/panels/AttributeTable.tsx (lines 65-73), the component subscribes to three critical store values:

  • selectedLayerId – Identifies which layer's features to display
  • selectedFeatureId – The primary anchor feature for range selections
  • selectedFeatureIds – The complete set of currently selected feature IDs

When any of these values change, the table re-renders automatically to highlight the corresponding rows. This single source of truth pattern ensures that selections initiated from the map, layer panel, or attribute table itself remain perfectly synchronized across the entire application interface.

Row-Click Handling with Keyboard Modifiers

User interactions in the attribute table trigger a sophisticated selection algorithm that handles multiple keyboard modifiers. The handleRowClick function (lines 26-40 of AttributeTable.tsx) constructs a RowSelectionInput object and delegates the actual computation to the pure helper computeRowSelection defined in apps/geolibre-desktop/src/lib/attribute-selection.ts (lines 30-58).

The selection logic supports three distinct interaction modes:

  • Plain click – Replaces the current selection with a single feature ID
  • Ctrl/Cmd toggle – Adds or removes the clicked feature from the existing selection set
  • Shift range – Selects all features between the current anchor and the clicked row, with optional Ctrl merging to combine ranges

After computing the new selection set, the table calls selectFeatures(ids, anchor) to push the updated state back into the global store, completing the bidirectional data flow.

// Inside AttributeTable.tsx – click handler
const handleRowClick = (featureId: string, event: ReactMouseEvent) => {
  const additive = event.ctrlKey || event.metaKey; // Ctrl/Cmd toggles
  const range = event.shiftKey;                    // Shift selects range
  const { ids, anchor } = computeRowSelection({
    featureId,
    sortedIds: sorted.map(row => row.featureId),
    selectedIds: selectedFeatureIds,
    anchorId: selectedFeatureId,
    additive,
    range,
  });
  // Update global selection state
  selectFeatures(ids, anchor);
};
// Pure selection logic – attribute-selection.ts
export function computeRowSelection(input: RowSelectionInput): RowSelectionResult {
  const { featureId, sortedIds, selectedIds, anchorId, additive, range } = input;

  if (range) {
    // Shift‑range selection; optionally merge with existing selection
    const anchorIndex = anchorId != null ? sortedIds.indexOf(anchorId) : -1;
    const clickedIndex = sortedIds.indexOf(featureId);
    if (anchorIndex === -1 || clickedIndex === -1) return { ids: [featureId], anchor: featureId };
    const [from, to] = anchorIndex <= clickedIndex ? [anchorIndex, clickedIndex] : [clickedIndex, anchorIndex];
    const rangeIds = sortedIds.slice(from, to + 1);
    const merged = additive ? [...selectedIds, ...rangeIds] : rangeIds;
    return { ids: [...new Set(merged)], anchor: anchorId };
  }

  if (additive) {
    // Ctrl‑toggle: add or remove the clicked row
    const next = selectedIds.includes(featureId)
      ? selectedIds.filter(id => id !== featureId)
      : [...selectedIds, featureId];
    const anchor = next.includes(featureId) ? featureId
      : anchorId != null && next.includes(anchorId) ? anchorId
      : (next.at(-1) ?? null);
    return { ids: next, anchor };
  }

  // Plain click – single selection
  return { ids: [featureId], anchor: featureId };
}

Selection-Aware View Modes

The attribute table implements dual view modes controlled by local state featureView, which accepts either "all" or "selected" values. When set to "selected", the table filters its display to show only the currently selected features, enabling focused editing of specific subsets.

To prevent interface lock-in, the component automatically switches from "selected" back to "all" when the user performs a plain click on any row (lines 44-48 of AttributeTable.tsx). This ensures that narrowing the view to a specific selection does not trap the user in a single-row display when they attempt to select a different feature.

Automatic Scrolling Synchronization

To maintain visual continuity when selections originate from external sources like the map canvas, the table implements programmatic scroll synchronization. A useEffect hook monitors selectedFeatureId and invokes rowVirtualizer.scrollToIndex (lines 87-92 of AttributeTable.tsx) to bring the corresponding virtualized row into the viewport.

This mechanism guarantees that when users select a feature on the map, the attribute table automatically scrolls to highlight that specific record, eliminating manual searching through large datasets. The virtualized rendering ensures this scrolling remains performant even with thousands of features loaded.

Selection Set Persistence

The integration completes through the selectFeatures store action defined in @geolibre/core. This action updates both selectedFeatureId (the anchor for future range selections) and selectedFeatureIds (the complete multi-selection array) in the global state.

Because all UI components—including the map renderer, layer panel, and attribute table—subscribe to these same store values, the selection persists consistently across application state changes. The pure function design of computeRowSelection enables deterministic, testable selection logic that remains independent of React rendering cycles.

Summary

  • Centralized state management via Zustand ensures the attribute table, map, and layer panel share identical selection data through selectedFeatureId and selectedFeatureIds.
  • Modifier-aware selection logic in attribute-selection.ts handles Ctrl toggles, Shift ranges, and combined operations through the pure computeRowSelection function.
  • Bidirectional synchronization allows selections initiated in the table to update the map and vice versa, with automatic scrolling to keep selected rows visible.
  • Dual view modes ("all" vs "selected") provide flexible data inspection, with automatic mode switching to prevent interface navigation dead-ends.
  • Virtualized rendering with rowVirtualizer.scrollToIndex maintains performance and usability when synchronizing selections across large feature sets.

Frequently Asked Questions

How does GeoLibre handle multi-selection in the attribute table?

GeoLibre implements multi-selection through keyboard modifiers detected in the handleRowClick event handler. Ctrl or Cmd clicks toggle individual features in the selection set, while Shift clicks create ranges from the last anchor feature. The computeRowSelection pure function in attribute-selection.ts encapsulates this logic, returning a deduplicated array of selected IDs and updating the anchor point for subsequent range operations.

What happens when I select a feature on the map while the attribute table is open?

When a map selection occurs, the selectedFeatureId in the global Zustand store updates immediately. The attribute table detects this change through its useAppStore subscription and triggers a useEffect hook that calls rowVirtualizer.scrollToIndex to bring the corresponding row into view. This ensures the table automatically highlights and displays the map-selected feature without requiring manual scrolling.

Can the attribute table display only selected features?

Yes. The component maintains a local featureView state that toggles between "all" and "selected" modes. When set to "selected", the table filters its virtualized list to render only the feature IDs present in selectedFeatureIds. If the user clicks a row without keyboard modifiers while in this mode, the component automatically switches back to "all" view to prevent navigation limitations.

Where is the selection logic stored in the GeoLibre codebase?

The selection logic is separated into two locations for maintainability. The UI interaction handler resides in apps/geolibre-desktop/src/components/panels/AttributeTable.tsx, while the pure computational logic lives in apps/geolibre-desktop/src/lib/attribute-selection.ts. The global store definitions and selectFeatures action are located in packages/core/src/store.ts and packages/core/src/actions.ts respectively, following GeoLibre's monorepo architecture.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →