Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@mindspank
Copy link
Contributor

@mindspank mindspank commented Oct 30, 2025

Opinionated nit: when editing a file name this will highlight just filename without extension so you can just type the new name without having to touch the mouse.

Checklist:

  • Covered by tests
  • Ran it and it works as intended
  • Reviewed the diff before requesting a review
  • Checked for unhandled edge cases
  • Linked the issues it closes
  • Checked if the docs need to be updated. If so, create a separate Linear DOCS issue
  • Intend to cherry-pick into the release branch
  • I'm proud of this work!

@mindspank mindspank requested a review from ericpgreen2 October 30, 2025 15:51
await tick();
inputElement.focus();
setTimeout(() => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to prevent the browser behaviour, needs a small timeout to wait for the browser to settle down

@ericpgreen2
Copy link
Contributor

The changes to Input.svelte have added quite a bit of nesting. Here are some strategies to improve readability and maintainability:

1. Early Returns / Guard Clauses

Convert nested if statements into early returns:

onMount(async () => {
  if (!claimFocusOnMount) return;
  
  if (selectElement) {
    selectElement.focus();
    return;
  }
  
  if (!inputElement) return;
  
  if (selectTextOnMount && inputElement instanceof HTMLInputElement) {
    await applyTextSelection(inputElement);
  } else {
    inputElement.focus();
  }
});

2. Extract Helper Functions

Break out the complex selection logic:

/**
 * Applies focus and text selection to an input element.
 * Uses a small delay to ensure browser completes focus before setting selection.
 */
async function applyTextSelection(element: HTMLInputElement) {
  await tick();
  element.focus();
  
  const SELECTION_DELAY_MS = 10; // Browser needs time to complete focus
  setTimeout(() => {
    if (!(element instanceof HTMLInputElement)) return;
    
    if (selectionStart !== undefined && selectionEnd !== undefined) {
      element.setSelectionRange(selectionStart, selectionEnd);
    } else {
      element.select();
    }
  }, SELECTION_DELAY_MS);
}

onMount(async () => {
  if (!claimFocusOnMount) return;
  
  if (selectElement) {
    selectElement.focus();
    return;
  }
  
  if (!inputElement) return;
  
  if (selectTextOnMount && inputElement instanceof HTMLInputElement) {
    await applyTextSelection(inputElement);
  } else {
    inputElement.focus();
  }
});

3. Complete Refactored Version (Recommended)

Here's a cleaner, more maintainable version:

// Constants
const SELECTION_DELAY_MS = 10; // Browser needs time to complete focus state

// Helper functions
function isTextInput(element: HTMLElement | undefined): element is HTMLInputElement {
  return element instanceof HTMLInputElement;
}

async function setTextSelection(
  element: HTMLInputElement,
  start: number | undefined,
  end: number | undefined
) {
  await tick();
  element.focus();
  
  setTimeout(() => {
    if (start !== undefined && end !== undefined) {
      element.setSelectionRange(start, end);
    } else {
      element.select();
    }
  }, SELECTION_DELAY_MS);
}

onMount(async () => {
  if (!claimFocusOnMount) return;
  
  // Priority 1: Select element (dropdowns)
  if (selectElement) {
    selectElement.focus();
    return;
  }
  
  // Priority 2: Input element (text inputs)
  if (!inputElement) return;
  
  // Simple focus for non-text inputs or when selection not requested
  if (!selectTextOnMount || !isTextInput(inputElement)) {
    inputElement.focus();
    return;
  }
  
  // Apply text selection for text inputs
  await setTextSelection(inputElement, selectionStart, selectionEnd);
});

Key Improvements

Reduced nesting: 1-2 levels max instead of 4-5
Clear flow: Guard clauses make the priority order obvious
Single responsibility: Each function does one thing
Named constants: SELECTION_DELAY_MS explains the magic number
Type guard: isTextInput() eliminates repeated instanceof checks
Documentation: Comments explain why, not just what
Testability: Helper functions can be unit tested separately

Bonus: Consider requestAnimationFrame

The setTimeout is a bit of a code smell. Consider using requestAnimationFrame for better timing:

async function setTextSelection(
  element: HTMLInputElement,
  start: number | undefined,
  end: number | undefined
) {
  await tick();
  element.focus();
  
  // Wait for next animation frame to ensure DOM is ready
  requestAnimationFrame(() => {
    if (start !== undefined && end !== undefined) {
      element.setSelectionRange(start, end);
    } else {
      element.select();
    }
  });
}

This is more reliable across browsers and doesn't use an arbitrary delay.

@rilldata rilldata deleted a comment from bito-code-review bot Nov 3, 2025
Comment on lines +38 to +44
if (!isDir && fileName) {
const lastDotIndex = fileName.lastIndexOf(".");
if (lastDotIndex > 0) {
selectionStart = 0;
selectionEnd = lastDotIndex;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like duplicated code to what was added in InputWithConfirm.svelte. Mind factoring out?

Comment on lines +22 to +33
let selectionStart: number | undefined = undefined;
let selectionEnd: number | undefined = undefined;
$: if (type === "File" && value) {
const lastDotIndex = value.lastIndexOf(".");
if (lastDotIndex > 0) {
selectionStart = 0;
selectionEnd = lastDotIndex;
} else {
selectionStart = undefined;
selectionEnd = undefined;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is business logic that should live outside of this generic form component. Mind keeping this component domain-agnostic?

@rilldata rilldata deleted a comment from bito-code-review bot Nov 3, 2025
@rilldata rilldata deleted a comment from bito-code-review bot Nov 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants