English 中文(简体)
How to display as much as item fit in one line and if all don t fit in show the number of remaining item
原标题:

I have just started learning Svelte. I want to display as much as email fit in one line and show the remaining emails with a number like this: https://imgur.com/ro8eTPM

I want achieve this without modifying the code in the parent component. I just want to change the DisplayEmail component.

So far I have done this: https://imgur.com/qEeY1q7

<!-- Parent component -->
<script lang="ts">
  import DisplayEmail from  components/DisplayEmail.svelte 

const emailList = [
    [ abc@yahoo.com ,  abc@hotmail.com ,  abc@hotmail.com ,  abc@gmail.com ],
  ]
</script>

<style lang="scss">
  $border-style: solid 1px #777;
  table {
    table-layout: fixed;
    border: $border-style;
    width: 100%;
    text-align: left;
  }
  th,
  td {
    border: $border-style;
    padding: 5px 10px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    height: 34px;
    box-sizing: border-box;
  }
  th {
    &:nth-child(1) {
      width: 20%;
    }
    &:nth-child(2) {
      width: 30%;
    }
    &:nth-child(3) {
      width: 50%;
    }
    &:nth-child(4) {
      width: 90px;
    }
    &:nth-child(5) {
      width: 70px;
    }
  }
  tbody:nth-child(even) {
    background-color: #ddd;
  }
  .align-right {
    text-align: right;
  }
</style>

<table cellspacing="0">
  <thead>
    <tr>
      <th>Sender</th>
      <th>Recipients</th>
      <th>Subject</th>
      <th class="align-right">Date</th>
      <th class="align-right">Time</th>
    </tr>
  </thead>
  <tbody>
    {#each emailList as emails}
      <tr>
        <td>demo@gmail.com</td>
        <td>
          <DisplayEmail {emails} />
        </td>
        <td>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Enim, odio.
        </td>
        <td class="align-right">n/a</td>
        <td class="align-right">n/a</td>
      </tr>
    {/each}
  </tbody>
</table>
<!--DisplayEmail Component -->

<script lang="ts">
  export let emails
  let showEmail = emails.map((email: string) => email)
  let spanWidth
</script>

<span bind:offsetWidth={spanWidth}>{showEmail}</span>
最佳回答

This can be a bit complicated.

For one, showing the number takes away from the available space, which could cause one more item to be truncated, so trying to simply calculate sizes and trying to figure out how many would fit would be difficult.

The easiest method probably would be to:

  • Set up a wrapper element around everything whose size changes trigger a re-fitting of the items.
  • Set up a wrapper around the items text which can be used to detect an overflow
  • Reduce the amount of items incrementally until no more overflow occurs.

The structure would be something like this:

<span class="root" bind:this={root}>
    <span class="outer" bind:this={outer}>
        <span class="inner" bind:this={inner}>
            {emailsString}
        </span>
    </span>

    <span class="counter" bind:this={counter}
                class:show={overflow}>
            +{emails.length - displayedEmailCount}
    </span>
</span>

The code to update everything could use ResizeObservers like this:

onMount(() => {
    const containerObserver = new ResizeObserver(throttle(refit));
    containerObserver.observe(root);

    const overflowObserver = new ResizeObserver(throttle(updateDisplayCount));
    overflowObserver.observe(outer);
    overflowObserver.observe(inner);
    overflowObserver.observe(counter);
    
    return () => {
        containerObserver.disconnect();
        overflowObserver.disconnect();
    }
});

async function refit() {
    // Container size changed, reset display count because potentially
    // more items could fit now.
    displayedEmailCount = emails.length;
    await tick(); // Lets Svelte update the DOM
    await updateDisplayCount();
}
async function updateDisplayCount() {
    if (inner == null || outer == null)
        return;
    
    // Reduce count until items fit or no items are displayed
    while (inner.offsetWidth > outer.offsetWidth && displayedEmailCount > 0) {
        displayedEmailCount -= 1;
        await tick(); // Lets Svelte update the DOM
    }
}

$: displayedEmailCount = emails.length;
$: overflow = emails.length > displayedEmailCount;
$: emailsString = emails.slice(0, displayedEmailCount).join( ,  ) + overflowSuffix;
$: overflowSuffix =
    overflow == false ?    :
    displayedEmailCount > 0 ?  , ...  :  ... ;

(throttle is a function that ensures that code is only executed at most once per frame. Can be a performance optimization when an API calls a function/raises an event many times per frame.)

This could still be further improved, e.g. by only resetting the display count when the container grows, not when it shrinks.

Full REPL example - Resize the splitter between code and rendered result to cause trunctation.

问题回答

暂无回答




相关问题
CSS working only in Firefox

I am trying to create a search text-field like on the Apple website. The HTML looks like this: <div class="frm-search"> <div> <input class="btn" type="image" src="http://www....

image changed but appears the same in browser

I m writing a php script to crop an image. The script overwrites the old image with the new one, but when I reload the page (which is supposed to pickup the new image) I still see the old one. ...

Firefox background image horizontal centering oddity

I am building some basic HTML code for a CMS. One of the page-related options in the CMS is "background image" and "stretch page width / height to background image width / height." so that with large ...

Separator line in ASP.NET

I d like to add a simple separator line in an aspx web form. Does anyone know how? It sounds easy enough, but still I can t manage to find how to do it.. 10x!

热门标签