Skip to content
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

fix: #83、exit delay #84

Merged
merged 16 commits into from
Feb 27, 2025
Merged

fix: #83、exit delay #84

merged 16 commits into from
Feb 27, 2025

Conversation

rick-hup
Copy link
Collaborator

fix #83 Variant children dont work with v-show
fix #82 exit delay not working

@@ -11,4 +11,5 @@ export interface AnimatePresenceProps {
custom?: any

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The custom property is currently typed as any, which can lead to potential issues with type safety and maintainability. It's recommended to replace any with a more specific type or an interface that accurately describes the expected structure of custom. This change would leverage TypeScript's type checking to prevent runtime errors and improve code quality.

Comment on lines 26 to 31
const style = document.createElement('style')
if (config.value.nonce) {
style.nonce = config.value.nonce
}
styles.set(state, style)
document.head.appendChild(style)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating and appending a new <style> element for each component state can lead to performance degradation, especially in large applications. This approach increases the number of DOM elements significantly, which can slow down the page rendering and increase memory usage.

Recommendation: Consider using a single <style> element managed by usePopLayout or a similar context provider. This element could aggregate all dynamic styles, which would reduce the number of DOM manipulations and improve performance. Use class names or CSS variables to apply styles dynamically to the elements.

Comment on lines 33 to 41
style.sheet.insertRule(`
[data-motion-pop-id="${state.id}"] {
position: absolute !important;
width: ${size.width}px !important;
height: ${size.height}px !important;
top: ${size.top}px !important;
left: ${size.left}px !important;
}
${x}px !important;
}
`)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of insertRule with template literals that include dynamic content (e.g., state.id, size.width, etc.) poses a risk of CSS injection if the values are not properly sanitized. This can lead to security vulnerabilities where malicious content could be executed.

Recommendation: Ensure all dynamic values inserted into CSS rules are properly sanitized to prevent CSS injection. Alternatively, consider safer methods to apply styles, such as setting styles directly on elements via JavaScript, which avoids the complexity and risks associated with dynamic CSS rules.

Comment on lines 3 to 4
export * from './motion-config'
export * from './reorder'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using export * from modules (./motion-config and ./reorder) can lead to namespace pollution and makes it difficult to track where specific exports are coming from. This can complicate maintenance and debugging. Consider explicitly listing which members to export to improve code clarity and maintainability.

Recommended Change:

// Example for motion-config
export { specificConfig, anotherConfig } from './motion-config'
// Adjust for actual exported members

@@ -3,3 +3,4 @@
export * from './motion-config'
export * from './reorder'
export { default as RowValue } from './RowValue.vue'
export { mountedStates } from '@/state'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of an absolute path (@/state) for importing mountedStates can lead to issues with module resolution, especially if the project's configuration changes or in different environments. Consider using relative paths or ensuring that the module resolution configuration is robust across different environments.

Recommended Change:

// If possible, convert to a relative path
export { mountedStates } from '../../state'
// Adjust the path as necessary based on the actual structure

Comment on lines +197 to +198
if (shouldDelay) {
Promise.resolve().then(unmountState)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of Promise.resolve().then(unmountState) to delay the unmount operation introduces a potential issue where errors thrown in the unmountState function are not caught, leading to unhandled promise rejections. This can cause silent failures in the application.

Recommendation: Wrap the unmountState call within the promise chain with a .catch() block to handle possible errors gracefully. For example:

Promise.resolve().then(unmountState).catch(error => console.error('Failed to unmount state:', error));

Comment on lines 184 to 187
const unmountChild = (child: MotionState) => {
child.unmount(true)
child.children?.forEach(unmountChild)
}
Array.from(this.children).forEach(unmountChild)
Array.from(this.children).reverse().forEach(unmountChild)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a new function unmountChild inside the unmount function on each call can lead to increased memory usage, especially when unmounting a large number of child components. This is because each function creation results in a new closure environment.

Recommendation: Define unmountChild as a private method of the MotionState class. This way, the function is created once and reused, which is more memory efficient. For example:

private unmountChild(child: MotionState) {
  child.unmount(true);
}

Then, use it in the unmount method:

Array.from(this.children).reverse().forEach(this.unmountChild);

Comment on lines 51 to +52
export function getOptions(options: $Transition, key: string): $Transition {
return options[key as any] ? { ...options, ...options[key as any] } : { ...options }
return options[key as any] ? { ...options, ...options[key as any], [key]: undefined } : { ...options }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function getOptions uses object spreading in a potentially inefficient and unsafe manner. If options[key] is a large object, spreading it can be costly in terms of performance. Additionally, the type casting key as any could lead to runtime errors if key is not a valid property, potentially accessing undefined properties without checks.

Recommendation:

  • Consider checking if key is a valid property before accessing it to avoid runtime errors.
  • If performance is a concern and options[key] can be large, consider alternative methods to manipulate objects that do not involve spreading large objects.

@@ -49,7 +49,7 @@
}

export function getOptions(options: $Transition, key: string): $Transition {
return options[key as any] ? { ...options, ...options[key as any] } : { ...options }
return options[key as any] ? { ...options, ...options[key as any], [key]: undefined } : { ...options }
}

export function isCssVar(name: string) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function isCssVar uses optional chaining (name?.startsWith('--')) which is unnecessary since name is a required parameter of type string. This could lead to confusion about the function's expectations regarding its inputs and reduce code clarity.

Recommendation:

  • Remove the optional chaining to enforce the expectation that name should always be a string, enhancing the clarity and maintainability of the function.

Comment on lines +1 to +5
export function delay(fn: () => void) {
return Promise.resolve().then(() => {
fn()
})
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The delay function lacks error handling for the function fn that it executes. If fn throws an error, it will lead to an unhandled promise rejection, which can cause runtime issues.

Recommended Solution:
Wrap the function call fn() inside a try-catch block to handle any potential errors gracefully. For example:

export function delay(fn: () => void) {
  return Promise.resolve().then(() => {
    try {
      fn();
    } catch (error) {
      console.error('Error executing function:', error);
      // Handle error appropriately
    }
  });
}

@rick-hup rick-hup merged commit d402e2b into master Feb 27, 2025
Comment on lines 234 to 240
})
if (isAnimate) {
this.animateUpdates({
isFallback: !isActive,
isFallback: !isActive && name !== 'exit' && this.visualElement.isControllingVariants,
})
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The recursive call to setActive for child components in line 234 does not handle potential errors that might occur in the child's setActive method. This can lead to unhandled exceptions if a child component encounters an issue during its state update.

Recommendation: Consider adding error handling around the recursive call to ensure stability. For example:

this.visualElement.variantChildren?.forEach((child) => {
  try {
    ((child as any).state as MotionState).setActive(name, isActive, false);
  } catch (error) {
    console.error('Error updating active state for child:', error);
  }
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Variant children dont work with v-show exit delay
1 participant