svelte
svelte copied to clipboard
feat: svelte-display directive #6336
Currently, Svelte transitions are processed when an element is added/removed from the DOM. In many cases this is undesirable (for example to show/hide a complex menu)
More info on issue #6336
Proposal
A new directive called svelte:display (or another name), which would modify the visibility of an element (using a display: none !important), after the execution of any transition.
Usage
<script>
let visible = false;
</script>
<button on:click={() => visible = !visible}>toggle</button>
<p transition:fly="{{ y: 200, duration: 2000 }}" svelte:display={visible}>
Flies in and out
</p>
When visible change, the <p> tag will be show/hidden using a fly transition.
Implementation state
This current implementation is functional, and partly based on <svelte:element> for creating a frament, allowing to execute transition easily.
It has some basic checks (it produces an error if svelte:display and style:display are used on the same element) and it's compatible with SSR.
However the svelte:display directive is marked as an error on Visual Studio Code, and I don't know how to fix it:
Argument of type '{ "svelte:display": boolean; }' is not assignable to parameter of type 'HTMLProps<"div", HTMLAttributes
>'. Object literal may only specify known properties, and '"svelte:display"' does not exist in type 'HTMLProps<"div", HTMLAttributes >'.js(2345)
Missing features / TODO list
- It would be nice, to have an alternative using CSS classes instead of
display:none, but I'm not sure of the best syntax. Any advice ?svelte:display={{state=visible, visible:'is-visible', hidden:'is-hidden'}}(plain object)svelte:display={[visible,'visible','hidden']}(array-style)svelte:display={visible} svelte:visible="is-visible" svelte-hidden="is-hidden"(separate directive)
- Another feature that could be useful would be to implement a 'lazy' mode, allowing to create the element only when it is displayed for the first time. Ex :
svelte:display|lazy={visible} - I didn't write any unit test, but i could do it later.
- No docs either, but I'm afraid I'm not the best to do that (not an english speaker).
Before submitting the PR, please make sure you do the following
- [X] It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
- [X] Prefix your PR title with
[feat],[fix],[chore], or[docs]. - [X] This message body should clearly illustrate what problems it solves.
- [ ] Ideally, include a test that fails without this PR but passes with it.
Tests
- [X] Run the tests with
npm testand lint the project withnpm run lint
4915 passing (2m) 31 pending
Thanks for reading
I didn't see the code but we need to slove conflicts.
I like the idea as it would finally solve the problem that we cant have transitions on style changes, but I dont like the syntax.
Inconsistent. Svelte uses the moustache syntax for templating which cant be overlooked. An #if statement immediately makes clear thats its content is displayed based on state. The fact that it gets removed and re-build on state change is secondary. Its about the semantics.
So I propose extending the existing #if statement.
{#if @hidden state}
<div />
{/if}
Is just syntactic sugar for
<div hidden={!state} />
Or
<div style:display={state ? 'none' : 'initial'} />
It may seem odd at the first look, but it becomes very practical considering we have multiple sibling elements:
<div svelte:display={visible}>1</div>
<div svelte:display={visible}>2</div>
<div svelte:display={visible}>3</div>
becomes:
{#if @hidden state}
<div>1</div>
<div>2</div>
<div>3</div>
{/if}
Also, we can extend it for the #each block.
With the @hidden extension a developer will immediately see that the elements render based on state. an attribute somewhere in the element is easily overlooked.
Alternative:
{#if use @hidden state} // uses the hidden attribute
<div />
{/if}
{#if use @display state} // uses the display property
<div />
{/if}
This would syntax would be extensible, we could it for all different kind of things.
Yes it can also have some other advantage, like this :
{#if @hidden state}
<div>1</div>
<div>2</div>
<div>3</div>
{:else}
<div>4</div>
<div>5</div>
<div>6</div>
{/if}
=> All div are on the page, and hidden/visible according to the if condition...
Instead of :
<div svelte:display={visible}>1</div>
<div svelte:display={visible}>2</div>
<div svelte:display={visible}>3</div>
<div svelte:display={!visible}>4</div>
<div svelte:display={!visible}>5</div>
<div svelte:display={!visible}>6</div>
With hidden I see 3 possibilities :
- Hidding using the hidden attribute
- Hidding using the display:none CSS property
- Hidding using user class-names
I have made a partially working prototype.
@display to show/hide elements by using display:none on them :
{#if @display state}
<div>Content to show/hide</div>
{/if}
@show to show/hide elements by using the hidden attribute on them :
{#if @show state}
<div>Content to show/hide</div>
{/if}
@class to show/hide elements by using the specified class names :
{#if @class=is-visible|is-hidden state}
<div>Content to show/hide</div>
{/if}
Bonus : a @lazy option to create the elements only when they are visible for the first time :
{#if @lazy @display state}
<div>Content to show/hide</div>
{/if}
My prototype is working but unfinished (no SSR for now), but I'm not sure of the syntax. Do we really need so much feature ? Why not using a single {#display} block instead ?
I think we should really consider a {#display} block. Would make it all easier, also documentation/tutorial wise. A #if block destroys and rebuild the dom, while a #display block toggles the hidden attribute and animations/transitions work on both.
Why don't you use style directive? @adiguba
https://svelte.dev/docs#template-syntax-element-directives-style-property
The style directive can not be used with svelte's transition, so the element will be visible/hidden immediately. I know it's possible to use transitions in CSS, but it's less convenient than svelte's transition...
At the opposite, {#if} are sometimes problematic because elements are created/destroyed in addition to being visible/hidden.
It might be useful to have a non-destructive {#if}, but I'm not really happy with the syntax... But on reflection I think this is all too complicated...
Maybe we should impose a single way to hide the elements.
{#if @display state}
<div transition:slide>Content to show or hide using display:none</div>
{/if}
I think we should really consider a {#display} block. Would make it all easier, also documentation/tutorial wise. A
#ifblock destroys and rebuild the dom, while a#displayblock toggles the hidden attribute and animations/transitions work on both.
Agreed! This also reminds me of vuejs. with v-show being basically the same as v-if. I think it makes much more sense as a block because that's how svelte does things. If you don't like blocks for control flow then I guess you don't like svelte (:
The style directive can not be used with svelte's transition, so the element will be visible/hidden immediately. I know it's possible to use transitions in CSS, but it's less convenient than svelte's transition...
At the opposite, {#if} are sometimes problematic because elements are created/destroyed in addition to being visible/hidden.
It might be useful to have a non-destructive {#if}, but I'm not really happy with the syntax... But on reflection I think this is all too complicated...
Maybe we should impose a single way to hide the elements.
{#if @display state} <div transition:slide>Content to show or hide using display:none</div> {/if}
Yes this looks pretty nice as well! I'm not an ecmascript fine point expert, but could this potentially have parsing issues due to decorator syntax looking quite similar?
Yes this looks pretty nice as well! I'm not an ecmascript fine point expert, but could this potentially have parsing issues due to decorator syntax looking quite similar?
Isn't decorator syntax only typescript? Typescript doesnt work in markup anyway. But I think the {#display} syntax would fit better anyway!
Yes this looks pretty nice as well! I'm not an ecmascript fine point expert, but could this potentially have parsing issues due to decorator syntax looking quite similar?
Isn't decorator syntax only typescript? Typescript doesnt work in markup anyway. But I think the
{#display}syntax would fit better anyway!
https://github.com/tc39/proposal-decorators
I don't think it conflicts with the current iteration of the proposal though
I think we should hold off from doing this until we have thought more deeply about how the whole transition/animation API might change for v5
@adiguba is attempting to deploy a commit to the Svelte Team on Vercel.
A member of the Team first needs to authorize it.
there also an 'hidden' attribute which work exactly as setting 'display:none', but currently svelte didn't offer its binding.
here is my example of them. https://svelte.dev/repl/9e6b13a6876f423eaf191078bdbc0a2f?version=3.59.1
which show we can also do optimization if component is related to fetching
⚠️ No Changeset found
Latest commit: 3173fdde8961ba79e238b9de98799064ff88117f
Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.
This PR includes no changesets
When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types
Click here to learn what changesets are, and how to add one.
Click here if you're a maintainer who wants to add a changeset to this PR