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]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
{Object.entries(accounts || {})
|
||||
.sort(([, account]) => (account.userId === currentUser.id ? -1 : 1))
|
||||
.map(([slot, account], index, arr) => {
|
||||
@ -116,7 +116,7 @@ const AccountMenuItems = ({
|
||||
{lang('MenuAddAccount')}
|
||||
</MenuItem>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -11,12 +11,7 @@ export function App() {
|
||||
}}
|
||||
>
|
||||
<h2>Click to update</h2>
|
||||
{true && (
|
||||
<>
|
||||
{trigger && <span>fragment</span>}
|
||||
{trigger && <span>content</span>}
|
||||
</>
|
||||
)}
|
||||
<FragmentContainer items={trigger ? [1, 2, 3] : []} />
|
||||
<div>
|
||||
This should always go last.
|
||||
</div>
|
||||
@ -24,4 +19,12 @@ export function App() {
|
||||
);
|
||||
}
|
||||
|
||||
function FragmentContainer({ items }: { items: number[] }) {
|
||||
return (
|
||||
<>
|
||||
{items.map((n) => <div>{n}</div>)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
@ -303,6 +303,15 @@ function mountChildren(
|
||||
},
|
||||
) {
|
||||
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++) {
|
||||
const $child = children[i];
|
||||
const $renderedChild = renderWithVirtual(parentEl, undefined, $child, $element, currentContext, i, options);
|
||||
@ -390,7 +399,14 @@ function remount(
|
||||
function unmountRealTree($element: VirtualElement) {
|
||||
if ($element.type === VirtualType.Component) {
|
||||
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) {
|
||||
extraClasses.delete($element.target!);
|
||||
setElementRef($element, undefined);
|
||||
@ -419,6 +435,15 @@ function insertBefore(parentEl: DOMElement | DocumentFragment, node: Node, nextS
|
||||
|
||||
function getNextSibling($current: VirtualElement): ChildNode | undefined {
|
||||
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];
|
||||
return getNextSibling(lastChild);
|
||||
}
|
||||
@ -444,6 +469,28 @@ function renderChildren(
|
||||
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 newChildren = $new.children;
|
||||
|
||||
|
||||
@ -56,6 +56,7 @@ export interface VirtualElementComponent {
|
||||
export interface VirtualElementFragment {
|
||||
type: VirtualType.Fragment;
|
||||
children: VirtualElementChildren;
|
||||
placeholderTarget?: Comment;
|
||||
}
|
||||
|
||||
export type StateHookSetter<T> = (newValue: ((current: T) => T) | T) => void;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user