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

proposal: passing parent to the onAdd fn #469

Open
lawvs opened this issue Apr 11, 2024 · 5 comments
Open

proposal: passing parent to the onAdd fn #469

lawvs opened this issue Apr 11, 2024 · 5 comments

Comments

@lawvs
Copy link
Contributor

lawvs commented Apr 11, 2024

I suggest passing the parent value to the onAdd callback, this can reduce the workload of developers parsing the path.

/**
 * @param path path to the parent target where the value will be added
 */
type JsonViewerOnAdd = (path: Path) => void;

type JsonViewerProps<T = unknown> = {
  // ...
  onAdd?: JsonViewerOnAdd;
}
/**
 * @param path path to the parent target where the value will be added
+ * @param parent The value of the parent, a new value will be added to it
 */
- type JsonViewerOnAdd = (path: Path) => void;
+ type JsonViewerOnAdd = <U = unknown>(path: Path, parent: U) => void;

If you agree with this proposal, I would be pleased to submit a PR.

Related code

{enableAdd && (
<IconBox
onClick={event => {
event.preventDefault()
onAdd?.(path)
}}
>
<AddBoxIcon sx={{ fontSize: '.8rem' }} />
</IconBox>

@pionxzh
Copy link
Collaborator

pionxzh commented Apr 11, 2024

I agree that it would be easier for developers to process it in some cases, but this change would make the API inconsistent. Instead, how about we provide a utility function getPathValue(obj, paths) that takes an object and a path array and returns the value at that path. This way, you can use it in any API to get the value at the path.

@lawvs
Copy link
Contributor Author

lawvs commented Apr 11, 2024

I agree that it would be easier for developers to process it in some cases, but this change would make the API inconsistent. Instead, how about we provide a utility function getPathValue(obj, paths) that takes an object and a path array and returns the value at that path. This way, you can use it in any API to get the value at the path.

Good idea, additionally I suggest adding a getter to handle the user's custom objects.

// TODO fix types
function getPathValue<T = object, R = object>(
  obj: T,
  path: Path,
  getter: (obj: T, key: string | number) => unknown = (obj, key) =>
    (obj as any)[key],
) {
  return path.reduce(
    (acc, key) => getter(acc, key) as any,
    obj,
  ) as unknown as R;
}

@pionxzh
Copy link
Collaborator

pionxzh commented Apr 11, 2024

ah ye, it would be better to have an optional getter to handle special data types. Then, this getter should also accept cases like return null or something to indicate that nothing being handled... not sure what kind of API shape would be better.

@pionxzh
Copy link
Collaborator

pionxzh commented Apr 11, 2024

This is my draft api design.

interface CustomHandler {
  is: (value: unknown, path: Path) => boolean
  handler: (value: unknown, key: (string | number)) => unknown
}

function defaultHandler (value: unknown, key: (string | number)): unknown {
  ...
}

export function getPathValue<T = any> (
  obj: T,
  path: Path,
  customHandlers: CustomHandler[] = []
): unknown {
  try {
    return path.reduce((acc, key, index) => {
      if (acc === null || acc === undefined) {
        console.error('Invalid path or value encountered at path', path.slice(0, index))
        throw new Error('Invalid path or value encountered')
      }

      for (const handler of customHandlers) {
        if (handler.is(acc, key)) {
          return handler.handler(acc, key)
        }
      }
      return defaultHandler(acc, key)
    }, obj)
  } catch (error) {
    console.error(error)
    return null // or throw error?
  }
}

@pionxzh
Copy link
Collaborator

pionxzh commented Apr 16, 2024

I will try to work on this tmr. Let me know If you agree with the draft. You can also send a PR if you want 🙏

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

No branches or pull requests

2 participants