-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
feat: Display directive #14795
base: main
Are you sure you want to change the base?
feat: Display directive #14795
Conversation
|
preview: https://svelte-dev-git-preview-svelte-14795-svelte.vercel.app/ this is an automated message |
|
Wouldn't it be possible for this to be a block? Kinda like a vue v-show directive equivalent? {#show visible}
<p transition:slide>hello world</p>
{/show} |
It could only work by setting the style And yes it's an equivalent to Vue |
I would prefer a block too, it works basically like an
Also I don't think this feature is important enough to deserve its own syntax as we can already do this: <div style:display={visible}></div> Pair that with a getter, a setter and factory pattern to turn it into something like: <script>
const handler = visibility_handler("flex", false); //Create an object with a setter and a getter that switches state between "flex" and "none!important" with an initial value of "none!important" as indicated by the second argument
</script>
`<div style:display={handler.visible}></div>` Which allows you to do things like: handler.toggle();
handler.visible = false; //assignment is captured via its setter. handler.visible getter now returns "none!important"
handler.visible = true; //assignment is captured via its setter. handler.visible now returns "flex" as it was memoized
handler.visible = "inline" //assignment is captured via its setter. The initial "flex" value is overridden
handler.hide();
handler.show(); |
But in order to hide the content, we need an element on which to set => How do you hide that : {#display visible}
Hello World !
{/display}
I known that |
This is really sweet! Hope in some shape or form this can be incorporated. I also wonder if this could be a block, this way it would be intuitive and basically only way to do it, vs if someone used and if block and the directive.
I think it can also play well with the transition I think it would make sense to use the Please read to the end as this has been more like a discovery journey. <script>
import { fade } from 'svelte/transition';
let signal = $state(true);
</script>
<!-- toggle #if condition -->
<button onclick={() => signal = !signal}>Fade in / out</button>
<!-- short form with functions predefined -->
{#if:transition=[in, out] signal}
<!-- every top element will run through in and out callbacks, including inside components and snippets -->
<div transition:fade>fades in and out</div>
{/if}
<!-- shortest form with a function that returns a tuple with for in and out -->
{#if:transition=display() signal}
<!-- every top element will run through in and out callbacks, including inside components and snippets -->
<div transition:fade>fades in and out</div>
{/if}
<!-- long form inlined functions, in out functions run for every top element -->
{#if:transition=[node => node.removeAttr('style'), node => node.style.display = 'none')] signal}
<!-- every top element will run through in and out callbacks, , including inside components and snippets -->
<div transition:fade>fades in and out</div>
{/if} The syntax for {#if:on:transition=[in, out] signal} Svelte could provide a super basic, like above, with svelte provided actions: <script>
import { fade } from 'svelte/transition'
import { display } from 'svelte/transition/actions';
let signal = $state(true);
</script>
{#if:transition=display() signal}
<!-- every top element will run through in and out callbacks, , including inside components and snippets -->
<div transition:fade>fades in and out</div>
{/if} SimplificationBut, maybe even simpler, {#if:action=[truthy, falsy]}
{/if} a more fleshed out example, and actually, it probably just makes sense to use the same syntax as for the <script>
import { fade } from 'svelte/transition'
import { display } from 'svelte/transition/actions';
// display or any custom function that has to return a tuple of functions for truthy and falsy
// display() => [(node: HTMLElement) => void, (node: HTMLElement) => void]
// example of what `display` could return: [node => node.removeAttr('style'), node => node.style.display = 'none')]
let signal = $state(true);
</script>
{#if:action:display signal}
<div transition:fade>fades in and out</div>
{/if}
<!-- or `action:` can be `use:` as it's shorter and already used in a similar fashion -->
{#if:use:display signal}
<div transition:fade>fades in and out</div>
{/if} And to reiterate:
NOTE: when a block mounts, the behavior would be the same as for the current block behavior: the transitions don't run and thus the |
A block will have more constraints : no component at top-level, but also no text content, If <div transition:slide style:display={"block", visible}>
...
</div> |
Perhaps something like |
I mean, you can't apply svelte transitions to text node or to Actually, my bad,
I thought about it initially but it seemed too confusing with the regular usage. |
We can't hide them either. |
These are really weird edge cases, and they can be either wrapped in elements , just not placed in the same block or wrapped by another if block. I think Anyway, I’ll be happy with whichever way ends up being supported. |
I'm failing to see how this proposal is better than: <div style:display={visible ? 'block' : 'none'}> What am I missing? |
Probably this part
|
Transitions ! But maybe this
|
Ah! I see. Yes, that part in userland would be a No No. Ok. Two things:
|
Yes it's a new syntax, and I'm not sure this is the best choice...
There is no "standard" way to hide a component... |
As far as avoiding a wrapper for components, it would be possible with a block directive (again I don't care which way ends up being supported). Any element with a defined transition, at any nested level regardless whether within a component or snippet, will be animated. And this way, elements only define transitions (vs transitions and directives), and as a developer you decide on the behavior at the block level vs at every single element, including 3rd party svelte components. |
How did you hide an unknown component, a text node, or any stuff generated by It's work with |
Since unmount would not happen, nor elements would be removed from DOM, |
This ⬆️ (and the comments above) basically only define the behavior / specification. As far as implementation, I don't know. I think it's doable to figure this out with DOM. E.g. a comment for start and end of a block can be inserted and then the top children figured out with something like As far as transitions, I'm assuming they're registered somewhere if the top block waits for them to finish, no matter the nesting level. So, I'm assuming, it can do the same before making the dom elements invisible. And for triggering the For SSR, the block will behave the same way as now either render or not render anything depending on the condition. |
Setting Ex : how do you handle this ? {#display visible}
<Comp />
{/display} |
Wrapping it in a |
{#display visible}
<Comp />
{/display} How about having Svelte enumerate all top-level elements at the block's position and adding setting the display on each of them? |
so, as I mentioned, one way is to find the elements inside the components and set the ones at the top as Otherwise, whatever is inside the block could be wrapped in one div with |
Wouldn't it be more performant to just change the div's style instead of removing it? |
if we're talking about a wrapper div, it's really fast to just move the elements after the last sibling just above the block. I'm not sure performance is a concern here with animations and all, especially compared to the usual destruction and recreation of nodes / components. Otherwise, we introduce another html element that could disrupt the css while the elements are visible (while they're not invisible, it should be fine with reactivity and css doesn't matter). And I think in this case we might as well have devs create their own wrapper. |
Also, if the actions are customizable as I suggested, if a default behavior is not satisfactory, a custom one can provided. As long as the |
This one is the most promising one, given the current state of AST nodes in Svelte. I've observed that Svelte core maintainers try to use ESTree specification (for JavaScript syntax) as much as possible, so the newcomers don't need to be confused by new things that doesn't exist neither in HTML or JavaScript. <div transition:slide style:display={["block", visible]}>
<!-- ... -->
</div> |
WIP : Another possible solution for #9976
This add a new
#display
directive for DOM elements.This directive will allow to show/hide an element using the Svelte transition API, without destroying it.
=> When visible is "falsy", the element will be hidden using a
display: none !important
.Otherwise it will be show either by removing the display style, or setting the value of
style:display
.Note : I known that
#display={...]
is not usual for directive but I think it's more expressive...In any case we could replace it with something like
svelte:display={...}
It's still a work in progress, and need some works that I can do if the syntax of this PR is accepted...
Before submitting the PR, please make sure you do the following
feat:
,fix:
,chore:
, ordocs:
.packages/svelte/src
, add a changeset (npx changeset
).Tests and linting
pnpm test
and lint the project withpnpm lint