diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 9be435156353..e0ad34f5bcb7 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -545,6 +545,8 @@ function build_element_attribute_update_assignment(element, node_id, attribute, update = b.stmt(b.call('$.set_value', node_id, value)); } else if (name === 'checked') { update = b.stmt(b.call('$.set_checked', node_id, value)); + } else if (name === 'hidden') { + update = b.stmt(b.call('$.set_hidden', node_id, value)); } else if (is_dom_property(name)) { update = b.stmt(b.assignment('=', b.member(node_id, name), value)); } else { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/TransitionDirective.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/TransitionDirective.js index e331f3647295..89dc073e8f39 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/TransitionDirective.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/TransitionDirective.js @@ -1,7 +1,12 @@ /** @import { Expression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import { TRANSITION_GLOBAL, TRANSITION_IN, TRANSITION_OUT } from '../../../../../constants.js'; +import { + TRANSITION_GLOBAL, + TRANSITION_THIS, + TRANSITION_IN, + TRANSITION_OUT +} from '../../../../../constants.js'; import * as b from '../../../../utils/builders.js'; import { parse_directive_name } from './shared/utils.js'; @@ -13,6 +18,9 @@ export function TransitionDirective(node, context) { let flags = node.modifiers.includes('global') ? TRANSITION_GLOBAL : 0; if (node.intro) flags |= TRANSITION_IN; if (node.outro) flags |= TRANSITION_OUT; + if (node.modifiers.includes('this')) { + flags |= TRANSITION_THIS; + } const args = [ b.literal(flags), diff --git a/packages/svelte/src/compiler/types/legacy-nodes.d.ts b/packages/svelte/src/compiler/types/legacy-nodes.d.ts index 2bd5fbbfa6d2..59ff38ad7278 100644 --- a/packages/svelte/src/compiler/types/legacy-nodes.d.ts +++ b/packages/svelte/src/compiler/types/legacy-nodes.d.ts @@ -187,7 +187,7 @@ export interface LegacyTransition extends BaseNode { name: string; /** The 'y' in `transition:x={y}` */ expression: null | Expression; - modifiers: Array<'local' | 'global'>; + modifiers: Array<'local' | 'global' | 'this'>; /** True if this is a `transition:` or `in:` directive */ intro: boolean; /** True if this is a `transition:` or `out:` directive */ diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index 9da0320432d4..413da995324a 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -251,7 +251,7 @@ export namespace AST { name: string; /** The 'y' in `transition:x={y}` */ expression: null | Expression; - modifiers: Array<'local' | 'global'>; + modifiers: Array<'local' | 'global' | 'this'>; /** True if this is a `transition:` or `in:` directive */ intro: boolean; /** True if this is a `transition:` or `out:` directive */ diff --git a/packages/svelte/src/constants.js b/packages/svelte/src/constants.js index 03fddc5ebd28..5c27c59730cd 100644 --- a/packages/svelte/src/constants.js +++ b/packages/svelte/src/constants.js @@ -14,6 +14,7 @@ export const PROPS_IS_LAZY_INITIAL = 1 << 4; export const TRANSITION_IN = 1; export const TRANSITION_OUT = 1 << 1; export const TRANSITION_GLOBAL = 1 << 2; +export const TRANSITION_THIS = 1 << 3; export const TEMPLATE_FRAGMENT = 1; export const TEMPLATE_USE_IMPORT_NODE = 1 << 1; diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index db3ac5fa4e7e..1f4140146e9c 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -74,6 +74,45 @@ export function set_checked(element, checked) { element.checked = checked; } +/** + * @param {HTMLElement} element + * @param {boolean} hidden + */ +export function set_hidden(element, hidden) { + // @ts-expect-error + var attributes = (element.__attributes ??= {}); + + if (attributes.hidden === (attributes.hidden = !!hidden)) return; + + /** @type {import('#client').TransitionManager[] | undefined} */ + // @ts-expect-error + const tm = element.__tm; + if (tm) { + if (hidden) { + var remaining = tm.length; + var check = () => { + if (--remaining == 0) { + // cleanup + for (var transition of tm) { + transition.stop(); + } + element.hidden = true; + } + }; + for (var transition of tm) { + transition.out(check); + } + } else { + element.hidden = false; + for (const transition of tm) { + transition.in(); + } + } + } else { + element.hidden = hidden; + } +} + /** * @param {Element} element * @param {string} attribute @@ -270,6 +309,8 @@ export function set_attributes( } else if (key === '__value' || (key === 'value' && value != null)) { // @ts-ignore element.value = element[key] = element.__value = value; + } else if (key === 'hidden') { + set_hidden(/** @type {HTMLElement} */ (element), value); } else { var name = key; if (!preserve_attribute_case) { diff --git a/packages/svelte/src/internal/client/dom/elements/transitions.js b/packages/svelte/src/internal/client/dom/elements/transitions.js index b473d0029d36..235181c4fa8d 100644 --- a/packages/svelte/src/internal/client/dom/elements/transitions.js +++ b/packages/svelte/src/internal/client/dom/elements/transitions.js @@ -5,7 +5,12 @@ import { active_effect, untrack } from '../../runtime.js'; import { loop } from '../../loop.js'; import { should_intro } from '../../render.js'; import { current_each_item } from '../blocks/each.js'; -import { TRANSITION_GLOBAL, TRANSITION_IN, TRANSITION_OUT } from '../../../../constants.js'; +import { + TRANSITION_GLOBAL, + TRANSITION_THIS, + TRANSITION_IN, + TRANSITION_OUT +} from '../../../../constants.js'; import { BLOCK_EFFECT, EFFECT_RAN, EFFECT_TRANSPARENT } from '../../constants.js'; import { queue_micro_task } from '../task.js'; @@ -169,6 +174,7 @@ export function transition(flags, element, get_fn, get_params) { var is_outro = (flags & TRANSITION_OUT) !== 0; var is_both = is_intro && is_outro; var is_global = (flags & TRANSITION_GLOBAL) !== 0; + var is_this = (flags & TRANSITION_THIS) !== 0; /** @type {'in' | 'out' | 'both'} */ var direction = is_both ? 'both' : is_intro ? 'in' : 'out'; @@ -243,6 +249,12 @@ export function transition(flags, element, get_fn, get_params) { } }; + if (is_this) { + // @ts-expect-error + (element.__tm ??= []).push(transition); + return; + } + var e = /** @type {Effect} */ (active_effect); (e.transitions ??= []).push(transition); diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 306fc69ca745..8eff6e88a6c8 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -33,7 +33,8 @@ export { set_xlink_attribute, handle_lazy_img, set_value, - set_checked + set_checked, + set_hidden } from './dom/elements/attributes.js'; export { set_class, set_svg_class, set_mathml_class, toggle_class } from './dom/elements/class.js'; export { apply, event, delegate, replay_events } from './dom/elements/events.js'; diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index e64abef0c5ca..34f6da270355 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1060,7 +1060,7 @@ declare module 'svelte/compiler' { name: string; /** The 'y' in `transition:x={y}` */ expression: null | Expression; - modifiers: Array<'local' | 'global'>; + modifiers: Array<'local' | 'global' | 'this'>; /** True if this is a `transition:` or `in:` directive */ intro: boolean; /** True if this is a `transition:` or `out:` directive */