Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor vaTextarea autosize calculations to use scrollHeight instead of newline characters #4146

Merged
merged 6 commits into from Mar 14, 2024

Conversation

speedpro
Copy link
Contributor

Description

The old rows calculations used the number of new line characters \n to automatically calculate the number of rows for the textarea. This becomes a problem when long paragraphs on a single line wrap around but the textarea doesn't recognise the new rows. I've taken inspiration from Vuetify and refactored the autosize calculations to use scrollheight in an invisible textarea to calculate the rows instead.

Markup:

<template>
  //...
  <textarea
   //attrs...
  /> 
  <textarea
        ref="autosizer"
        class="va-textarea__autosizer va-textarea__textarea"
        v-if="autosize"
        v-model="valueComputed"
        aria-hidden="true"
        readonly
      /> 
  //...
</template>


<script setup>
//...
const autosizer = ref<HTMLTextAreaElement>()

const rows = ref(props.minRows)

function calculateInputHeight () {
  const minRows = parseFloat(String(props.minRows))
  const maxRows = parseFloat(String(props.maxRows))

  if (!props.autosize) {
    rows.value = Math.max(maxRows, Math.min(minRows, maxRows ?? 0))
    return
  }

  nextTick(() => {
    if (!autosizer.value) {
      return
    }

    const style = getComputedStyle(autosizer.value)

    const height = autosizer.value.scrollHeight
    const lineHeight = parseFloat(style.lineHeight)
    const minHeight = Math.max(
      minRows * lineHeight,
      minRows + Math.round(lineHeight),
    )

    const maxHeight = maxRows * lineHeight || Infinity
    const newHeight = Math.max(minHeight, Math.min(maxHeight, height ?? 0))
    rows.value = Math.floor(newHeight / lineHeight)
  })
}

onMounted(calculateInputHeight)
watch(valueComputed, calculateInputHeight)
watch(() => props.minRows, calculateInputHeight)
watch(() => props.maxRows, calculateInputHeight)
//...
</script>

<style>
//...
  &__autosizer {
    visibility: hidden;
    position: absolute;
    top: 0;
    left: 0;
    height: 0 !important;
    min-height: 0 !important;
    pointer-events: none;
  }
//...
</style>

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Improvement/refactoring (non-breaking change that doesn't add any feature but make things better)

@speedpro speedpro changed the title Refactor vaTextarea autosize calculations to use scrollHeight instead of newLine characters Refactor vaTextarea autosize calculations to use scrollHeight instead of \n Jan 25, 2024
@speedpro speedpro changed the title Refactor vaTextarea autosize calculations to use scrollHeight instead of \n Refactor vaTextarea autosize calculations to use scrollHeight instead of newline characters Jan 29, 2024
@m0ksem m0ksem self-requested a review January 29, 2024 04:02
@m0ksem
Copy link
Contributor

m0ksem commented Jan 29, 2024

Thanks for PR, @speedpro!

How about setting height: 0 on textarea, getting scrollHeight and setting height back to scrollHeight? Example. What do you think?

It doesn't look like there are some jumps or any other issues.

In current solution I have issue on Mac: scrollHeight is incorrect, because of scrollbar. overflow: hidden must be added. (I fixed it in my example)
image

@m0ksem m0ksem merged commit 293c1bc into epicmaxco:develop Mar 14, 2024
1 check passed
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.

None yet

2 participants