ui: Refactor data-attrs constants, enum for bool strings (#27002)

* refactor: Data-attribute constants + boolean string enum

* refactor: Use CSS class string constants

* refactor: Address review comments
This commit is contained in:
Aleksander Grygier
2026-08-13 20:01:12 +02:00
committed by GitHub
parent fa4ec4590c
commit bdffafa5df
40 changed files with 280 additions and 189 deletions
@@ -9,6 +9,7 @@
* matches what the user sees on screen.
*/
import { UI_DATA_ATTRS } from '$lib/constants';
import { SvelteSet } from 'svelte/reactivity';
interface UseMarqueeSelectionOptions {
@@ -18,8 +19,8 @@ interface UseMarqueeSelectionOptions {
orderedIds: () => string[];
/** Document listeners attach only while the getter returns true. */
enabled: () => boolean;
/** DOM attribute key (after the `data-` prefix) that marks selectable rows. */
attributeName?: () => string;
/** Full `data-*` attribute that marks selectable rows. */
dataAttr?: () => string;
/** Minimum pixel distance before a press becomes a marquee drag. */
dragThresholdPx?: number;
}
@@ -36,16 +37,8 @@ export function useMarqueeSelection(options: UseMarqueeSelectionOptions) {
let dragMode: 'add' | 'remove' | null = null;
let suppressNextClick = false;
function resolveAttributeName(): string {
return options.attributeName?.() ?? 'conversation-row';
}
/**
* `dataset` keys are camelCased. `data-conversation-row` -> `conversationRow`.
* We resolve the attribute name once per call and read via the camelCase key.
*/
function datasetKey(key: string = resolveAttributeName()): string {
return key.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
function resolveDataAttr(): string {
return options.dataAttr?.() ?? UI_DATA_ATTRS.CONVERSATION_ROW;
}
function decideDragMode(startingRowId: string | null, currentlySelected: ReadonlySet<string>) {
@@ -78,9 +71,8 @@ export function useMarqueeSelection(options: UseMarqueeSelectionOptions) {
}
function findRowAtPoint(x: number, y: number): string | null {
const attr = resolveAttributeName();
const selector = `[data-${attr}]`;
const key = datasetKey(attr);
const attr = resolveDataAttr();
const selector = `[${attr}]`;
let bestMatch: HTMLElement | null = null;
let bestCenterDistance = Infinity;
@@ -89,7 +81,7 @@ export function useMarqueeSelection(options: UseMarqueeSelectionOptions) {
const rect = row.getBoundingClientRect();
if (y >= rect.top && y <= rect.bottom && x >= rect.left && x <= rect.right) {
return row.dataset[key] ?? null;
return row.getAttribute(attr);
}
if (x >= rect.left && x <= rect.right) {
@@ -102,13 +94,12 @@ export function useMarqueeSelection(options: UseMarqueeSelectionOptions) {
}
}
return bestMatch ? (bestMatch.dataset[key] ?? null) : null;
return bestMatch ? bestMatch.getAttribute(attr) : null;
}
function updateMarqueeRect(currentX: number, currentY: number) {
const attr = resolveAttributeName();
const selector = `[data-${attr}]`;
const key = datasetKey(attr);
const attr = resolveDataAttr();
const selector = `[${attr}]`;
const selected = options.selectedIds();
const left = Math.min(dragStartX, currentX);
const top = Math.min(dragStartY, currentY);
@@ -117,7 +108,7 @@ export function useMarqueeSelection(options: UseMarqueeSelectionOptions) {
const visibleIds = new SvelteSet(options.orderedIds());
for (const row of document.querySelectorAll<HTMLElement>(selector)) {
const id = row.dataset[key];
const id = row.getAttribute(attr);
if (!id || !visibleIds.has(id)) continue;
@@ -11,8 +11,8 @@ export interface UseScrollActiveRowOptions {
getContainer: () => HTMLDivElement | null;
getIndex: () => number;
getCount: () => number;
/** Attribute prefix, e.g. 'picker' for `[data-picker-index="0"]`. */
dataIndex: string;
/** Full data attribute marking the row, e.g. `data-picker-index`. */
dataAttr: string;
}
export function useScrollActiveRow(opts: UseScrollActiveRowOptions) {
@@ -41,9 +41,7 @@ export function useScrollActiveRow(opts: UseScrollActiveRowOptions) {
if (!container || index < 0 || index >= opts.getCount()) return;
const row = container.querySelector(
`[data-${opts.dataIndex}-index="${index}"]`
) as HTMLElement | null;
const row = container.querySelector(`[${opts.dataAttr}="${index}"]`) as HTMLElement | null;
row?.scrollIntoView({ block: 'nearest', inline: 'nearest' });
});