【问题标题】:Svelte wrapping each slot细长的包裹每个插槽
【发布时间】:2020-09-21 05:27:01
【问题描述】:

我希望在 svelte 中创建一个 Spacer 元素。我已经完成了我所设定的,即允许用户将元素包装在<Space></Space> 标签中并传递道具以垂直/水平均匀地间隔它们,甚至将内部元素中心/开始/结束/基线对齐。我遇到的问题是我必须用<SpaceItem></SpaceItem> 将每个子元素包装到<Space></Space> 以获得所需的效果。下面是示例代码:

App.svelte

<script>
    import {Space, SpaceItem} from './components/Space';
</script>

<Space>
    <SpaceItem>
        <Button primary>Primary</Button>
    </SpaceItem>
    <SpaceItem>
        <Button primary icon="add">Primary</Button>
    </SpaceItem>
    <SpaceItem>
        <Button danger>Danger</Button>
    </SpaceItem>
</Space>

Space.svelte

<script>
    import { SPACE } from './SpaceContext.svelte';
    import { setContext } from 'svelte';
    import { readable } from 'svelte/store';
    export let vertical = false;
    export let horizontal = true;
    export let alignCenter = false;
    export let alignStart = false;
    export let alignEnd = false;
    export let alignBaseline = false;
    export let large = false;
    export let middle = false;
    export let size = null;

    horizontal = vertical ? false : true;
    size = size ? size : large ? 24 : middle ? 16 : 8;

    const gap = readable(size);
    const direction = readable(horizontal ? 'horizontal' : 'vertical');

    setContext(SPACE, {
        gap,
        direction,
    });
</script>

<style>
    .space {
        display: -webkit-inline-box;
        display: -ms-inline-flexbox;
        display: inline-flex;
    }
    .vertical {
        -webkit-box-orient: vertical;
        -webkit-box-direction: normal;
        -ms-flex-direction: column;
        flex-direction: column;
    }
    .align-center {
        -webkit-box-align: center;
        -ms-flex-align: center;
        align-items: center;
    }
    .align-start {
        -webkit-box-align: start;
        -ms-flex-align: start;
        align-items: flex-start;
    }
    .align-end {
        -webkit-box-align: end;
        -ms-flex-align: end;
        align-items: flex-end;
    }
    .align-baseline {
        -webkit-box-align: baseline;
        -ms-flex-align: baseline;
        align-items: baseline;
    }
</style>

<div
    class="space"
    class:horizontal
    class:vertical
    class:size
    class:align-center={alignCenter}
    class:align-start={alignStart}
    class:align-end={alignEnd}
    class:align-baseline={alignBaseline}>
    <slot />
</div>

空间项目

<script>
    import { SPACE } from './SpaceContext.svelte';
    import { getContext } from 'svelte';
    const { gap, direction } = getContext(SPACE);

    let style;
    if ($direction === 'horizontal') {
        style = `margin-right: ${$gap}px;`;
    } else {
        style = `margin-bottom: ${$gap}px;`;
    }
</script>

<style>

</style>

<div class="item" {style}>
    <slot />
</div>

有没有办法消除&lt;SpaceItem&gt;&lt;/SpaceItem&gt; 元素并让&lt;Space&gt;&lt;/Space&gt;&lt;SpaceItem&gt;&lt;/SpaceItem&gt; 包装传递给&lt;slot /&gt; 的每个元素?我只想将所有元素包装在一个 Space 标记中,而不是将每个单独的元素包装在 SpaceItem 标记中。

想要的效果是我能写:

App.svelte

<script>
    import {Space, SpaceItem} from './components/Space';
</script>

<Space>
    <Button primary>Primary</Button>
    <Button primary icon="add">Primary</Button>
    <Button danger>Danger</Button>
</Space>

并且仍然在 Space.svelte 文件中使用 &lt;SpaceItem&gt;&lt;/SpaceItem&gt; 包裹每个按钮。

【问题讨论】:

    标签: svelte


    【解决方案1】:

    这是我自己制定的一个解决方案,但我不确定这是否会在现实世界的应用程序中产生任何性能问题,因此欢迎使用 cmets。

    App.svelte

    <script>
      import Space from './Space.svelte';
      import Button from './Button.svelte';
    </script>
    
    <Space>
      <Button primary>Primary</Button>
      <Button primary icon="add">Primary</Button>
      <Button danger>Danger</Button>
    </Space>
    
    <Space vertical large alignCenter>
      <Button primary icon="add">Primary</Button>
      <Button danger>Danger</Button>
    </Space>
    

    Space.svelte

    <script>
      import { onMount } from 'svelte';
      import addWrapper from './utils/addWrapper';
      export let vertical = false;
      export let horizontal = true;
      export let alignCenter = false;
      export let alignStart = false;
      export let alignEnd = false;
      export let alignBaseline = false;
      export let large = false;
      export let middle = false;
      export let size = null;
    
      horizontal = vertical ? false : true;
      size = size ? size : large ? 24 : middle ? 16 : 8;
    
      const gap = size;
      const direction = horizontal ? 'horizontal' : 'vertical';
      let style;
    
      if (direction === 'horizontal') {
        style = `margin-right: ${gap}px;`;
      } else {
        style = `margin-bottom: ${gap}px;`;
      }
      let currentEl;
    
      onMount(async () => {
        addWrapper({
          node: currentEl,
          tag: 'div',
          style,
        });
      });
    </script>
    
    <style>
      .space {
        display: -webkit-inline-box;
        display: -ms-inline-flexbox;
        display: inline-flex;
      }
      .vertical {
        -webkit-box-orient: vertical;
        -webkit-box-direction: normal;
        -ms-flex-direction: column;
        flex-direction: column;
      }
      .align-center {
        -webkit-box-align: center;
        -ms-flex-align: center;
        align-items: center;
      }
      .align-start {
        -webkit-box-align: start;
        -ms-flex-align: start;
        align-items: flex-start;
      }
      .align-end {
        -webkit-box-align: end;
        -ms-flex-align: end;
        align-items: flex-end;
      }
      .align-baseline {
        -webkit-box-align: baseline;
        -ms-flex-align: baseline;
        align-items: baseline;
      }
    </style>
    
    <div
      bind:this={currentEl}
      class="space"
      class:horizontal
      class:vertical
      class:align-center={alignCenter}
      class:align-start={alignStart}
      class:align-end={alignEnd}
      class:align-baseline={alignBaseline}
    >
      <slot />
    </div>
    

    addWrapper.js

    export default async function addWrapper({ node, tag, style }) {
      const children = node.childNodes;
      [].forEach.call(children, function (child) {
        if (child.nodeName !== "#text" && child.nodeName !== "#comment") {
          let wrapper = document.createElement(tag);
          wrapper.setAttribute("style", style);
          child.parentNode.insertBefore(wrapper, child);
          wrapper.appendChild(child);
        }
      });
    }
    

    就像据说这行得通,并且无需为简单的情况创建单独的包装器,但更复杂的包装器需要重新工作。如果有人有更简单、更高效的更好建议,我会全力以赴。

    【讨论】:

      猜你喜欢
      • 2022-11-17
      • 2017-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-17
      • 1970-01-01
      • 1970-01-01
      • 2021-01-20
      相关资源
      最近更新 更多