Teact: Fix empty fragment mount position (#5918)
This commit is contained in:
parent
b67b8ba533
commit
f21ca52337
@ -69,7 +69,7 @@ const AccountMenuItems = ({
|
|||||||
}, [accounts, currentCount, totalLimit]);
|
}, [accounts, currentCount, totalLimit]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
{Object.entries(accounts || {})
|
{Object.entries(accounts || {})
|
||||||
.sort(([, account]) => (account.userId === currentUser.id ? -1 : 1))
|
.sort(([, account]) => (account.userId === currentUser.id ? -1 : 1))
|
||||||
.map(([slot, account], index, arr) => {
|
.map(([slot, account], index, arr) => {
|
||||||
@ -116,7 +116,7 @@ const AccountMenuItems = ({
|
|||||||
{lang('MenuAddAccount')}
|
{lang('MenuAddAccount')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -11,12 +11,7 @@ export function App() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<h2>Click to update</h2>
|
<h2>Click to update</h2>
|
||||||
{true && (
|
<FragmentContainer items={trigger ? [1, 2, 3] : []} />
|
||||||
<>
|
|
||||||
{trigger && <span>fragment</span>}
|
|
||||||
{trigger && <span>content</span>}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<div>
|
<div>
|
||||||
This should always go last.
|
This should always go last.
|
||||||
</div>
|
</div>
|
||||||
@ -24,4 +19,12 @@ export function App() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function FragmentContainer({ items }: { items: number[] }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{items.map((n) => <div>{n}</div>)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
@ -303,6 +303,15 @@ function mountChildren(
|
|||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const { children } = $element;
|
const { children } = $element;
|
||||||
|
|
||||||
|
// Add a placeholder comment node for empty fragments to maintain position
|
||||||
|
if ($element.type === VirtualType.Fragment && children.length === 0) {
|
||||||
|
const fragmentEl = $element as VirtualElementFragment;
|
||||||
|
fragmentEl.placeholderTarget = document.createComment('empty-fragment');
|
||||||
|
insertBefore(options.fragment || parentEl, fragmentEl.placeholderTarget, options.nextSibling);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0, l = children.length; i < l; i++) {
|
for (let i = 0, l = children.length; i < l; i++) {
|
||||||
const $child = children[i];
|
const $child = children[i];
|
||||||
const $renderedChild = renderWithVirtual(parentEl, undefined, $child, $element, currentContext, i, options);
|
const $renderedChild = renderWithVirtual(parentEl, undefined, $child, $element, currentContext, i, options);
|
||||||
@ -390,7 +399,14 @@ function remount(
|
|||||||
function unmountRealTree($element: VirtualElement) {
|
function unmountRealTree($element: VirtualElement) {
|
||||||
if ($element.type === VirtualType.Component) {
|
if ($element.type === VirtualType.Component) {
|
||||||
unmountComponent($element.componentInstance);
|
unmountComponent($element.componentInstance);
|
||||||
} else if ($element.type !== VirtualType.Fragment) {
|
} else if ($element.type === VirtualType.Fragment) {
|
||||||
|
// Remove placeholder for empty fragments
|
||||||
|
const fragment = $element as VirtualElementFragment;
|
||||||
|
if (fragment.placeholderTarget && fragment.children.length === 0) {
|
||||||
|
fragment.placeholderTarget.parentNode?.removeChild(fragment.placeholderTarget);
|
||||||
|
fragment.placeholderTarget = undefined;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if ($element.type === VirtualType.Tag) {
|
if ($element.type === VirtualType.Tag) {
|
||||||
extraClasses.delete($element.target!);
|
extraClasses.delete($element.target!);
|
||||||
setElementRef($element, undefined);
|
setElementRef($element, undefined);
|
||||||
@ -419,6 +435,15 @@ function insertBefore(parentEl: DOMElement | DocumentFragment, node: Node, nextS
|
|||||||
|
|
||||||
function getNextSibling($current: VirtualElement): ChildNode | undefined {
|
function getNextSibling($current: VirtualElement): ChildNode | undefined {
|
||||||
if ($current.type === VirtualType.Component || $current.type === VirtualType.Fragment) {
|
if ($current.type === VirtualType.Component || $current.type === VirtualType.Fragment) {
|
||||||
|
if ($current.children.length === 0) {
|
||||||
|
// For empty fragments, use the placeholder node to track position
|
||||||
|
const fragment = $current as VirtualElementFragment;
|
||||||
|
if (fragment.placeholderTarget) {
|
||||||
|
return fragment.placeholderTarget.nextSibling || undefined;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const lastChild = $current.children[$current.children.length - 1];
|
const lastChild = $current.children[$current.children.length - 1];
|
||||||
return getNextSibling(lastChild);
|
return getNextSibling(lastChild);
|
||||||
}
|
}
|
||||||
@ -444,6 +469,28 @@ function renderChildren(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle transitions between empty and non-empty fragments
|
||||||
|
if ($current.type === VirtualType.Fragment && $new.type === VirtualType.Fragment) {
|
||||||
|
const currentFragment = $current as VirtualElementFragment;
|
||||||
|
const newFragment = $new as VirtualElementFragment;
|
||||||
|
|
||||||
|
// If transitioning from empty to non-empty, use the placeholder's position
|
||||||
|
if (currentFragment.children.length === 0 && newFragment.children.length > 0 && currentFragment.placeholderTarget) {
|
||||||
|
nextSibling = currentFragment.placeholderTarget.nextSibling || undefined;
|
||||||
|
// Remove the placeholder as we're adding real content
|
||||||
|
currentFragment.placeholderTarget.parentNode?.removeChild(currentFragment.placeholderTarget);
|
||||||
|
currentFragment.placeholderTarget = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If transitioning from non-empty to empty, add a placeholder
|
||||||
|
if (currentFragment.children.length > 0 && newFragment.children.length === 0) {
|
||||||
|
const lastCurrentChild = currentFragment.children[currentFragment.children.length - 1];
|
||||||
|
const siblingAfterFragment = getNextSibling(lastCurrentChild);
|
||||||
|
newFragment.placeholderTarget = document.createComment('empty-fragment');
|
||||||
|
insertBefore(currentEl, newFragment.placeholderTarget, siblingAfterFragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const currentChildren = $current.children;
|
const currentChildren = $current.children;
|
||||||
const newChildren = $new.children;
|
const newChildren = $new.children;
|
||||||
|
|
||||||
|
|||||||
@ -56,6 +56,7 @@ export interface VirtualElementComponent {
|
|||||||
export interface VirtualElementFragment {
|
export interface VirtualElementFragment {
|
||||||
type: VirtualType.Fragment;
|
type: VirtualType.Fragment;
|
||||||
children: VirtualElementChildren;
|
children: VirtualElementChildren;
|
||||||
|
placeholderTarget?: Comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type StateHookSetter<T> = (newValue: ((current: T) => T) | T) => void;
|
export type StateHookSetter<T> = (newValue: ((current: T) => T) | T) => void;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user