zikaari / aspen

Most advanced nested tree renderer for React apps
https://neeksandhu.github.io/react-aspen-demo/
MIT License
188 stars 8 forks source link

Aspen 2 release discussion #19

Open zikaari opened 2 years ago

zikaari commented 2 years ago

This thread will be used to track and discuss the next leap in super-performant tree rendering on planet Earth - Aspen 2.

The next version of Aspen has the following core goals:

The stepping-stone has already been set over at aspen-next branch in this repository. Everything you see there is subject to change as the project advances to the beta stage. Many features from current Aspen are yet to be ported over to Aspen 2, but the core framework has already been laid out. The breaking changes can be reviewed in the changelog, this will keep on evolving as the project advances.

You can subscribe to this issue by clicking Subscribe on the right to be notified on the progress of Aspen 2, including the beta release, and up to the final release.

cc @LoganDark @fintara @graup @rodgomesc @adamgruber @khushboovashi Since you folks use react-aspen in production, your feedback is very important in the direction Aspen will follow.

LoganDark commented 2 years ago

Overall, I really like the changes that are currently in the pipeline. Generalizing aspen to encompass more than just filesystem trees will allow for many unique experiences:

Functional components are very cool. Is it possible to raise the handle state up though? For example, could it be possible to declaratively specify that you want a certain prompt to exist?

Right now, aspen relies on a lot of imperative function calls that need to be kept around and cleaned up after by the caller, which isn't very fun. It would be nice if more of the library was declarative instead of imperative, since callback hell is the main not-very-fun part. Promises help somewhat (I use async functions heavily), but dealing with them in React is kind of annoying.

graup commented 2 years ago

Great to hear. My two main issues have also been:

  1. Generalizing to other kinds of trees
    • Instead of path and name, many trees just have id, children and data.
    • Type (File or Directory) is often implicit (depending on if children?.length > 0).
    • I had to re-create a directory-like structure with paths just to translate this tree to a file system tree and also specifically turn off some of the path handling (like OS-style separators and sorting, those are things I don't want to think about.)
  2. Translating between declarative state (React hooks) and imperative style
    • I found myself writing wrapper React components that take selectedItems, visibleItems, onChangeSelection(selectedItems) props and translate it to treeHandle calls. Ended up being a strange mix between controlled and uncontrolled components. Ideally, react-aspen would offer a controlled component that works just like standard inputs or selects, i.e. taking props like mentioned above.

/edit: I just read the 2.0.0 changelog and it seems it's addressing my first point perfectly. :+1:

Another thing that would be nice is having some extra functions built-in or at least one-line examples of how to achieve them with the API. Examples:

  1. Multiple selection
  2. Expanding/collapsing all or a list of items
  3. Search

On the other hand, I don't care so much about context menus and reordering, although those might certainly be useful in some cases.

Here are two different trees I built for our app showing two very different use cases:

https://user-images.githubusercontent.com/898549/142715200-4361acae-abe3-420a-93dc-13981af779e6.mov

https://user-images.githubusercontent.com/898549/142715202-5aa39099-c31a-481a-a00f-c6f630eedf4c.mov

LoganDark commented 2 years ago

@graup I've had moderate success implementing multiple select and keyboard bindings in my implementation of Aspen. You can just apply the decoration to multiple items at the same time, and as long as you account for that functionality, everything will Just Work.

Although the imperative style does still bother me - and of course, you have to reimplement all the FileTreeX stuff from scratch (I never did use FileTreeX, anyway - it looked far too simple for my use case).

I'd love to show it off but it's all proprietary for at least the next month.

khushboovashi commented 2 years ago

Hi,

I really appreciate your efforts for creating such a wonderful high performance tree. I liked the road map and would like to have below things to be considered as well.

  1. There should be an option to choose between fixed or dynamic sized tree
  2. The important tree item events (like add/update/delete/load/select) should be triggered, so those events can be listened to outside component
  3. Search Tree Item
  4. Refresh the tree items
  5. Item path should be id based

Thanks, Khushboo

On Thu, Nov 18, 2021 at 7:27 PM Neek Sandhu @.***> wrote:

This thread will be used to track and discuss the next leap in super-performant tree rendering on planet Earth - Aspen 2.

The next version of Aspen has the following core goals:

  • Even better performance
  • More versatility, i.e not being limited to just "file trees"
  • More lightweight, i.e removing as many dependencies as possible
  • Codebase enhancements, i.e using functional components, and switching to a monorepo

The stepping-stone has already been set over at aspen-next branch in this repository. Everything you see there is subject to change as the project advances to the beta stage. Many features from current Aspen are yet to be ported over to Aspen 2, but the core framework has already been laid out. The breaking changes can be reviewed in the changelog https://github.com/NeekSandhu/aspen/blob/aspen-next/CHANGELOG.md, this will keep on evolving as the project advances.

You can subscribe to this issue by clicking Subscribe on the right to be notified on the progress of Aspen 2, including the beta release, and up to the final release.

cc @LoganDark https://github.com/LoganDark @fintara https://github.com/fintara @graup https://github.com/graup @rodgomesc https://github.com/rodgomesc @adamgruber https://github.com/adamgruber @khushboovashi https://github.com/khushboovashi Since you folks use react-aspen in production, your feedback is very important in the direction Aspen will follow.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/NeekSandhu/aspen/issues/19, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADNOKSKPA3YOW7CFECJFSO3UMUA3JANCNFSM5IJVWTFA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

LoganDark commented 2 years ago

Hi, I really appreciate your efforts for creating such a wonderful high performance tree. I liked the road map and would like to have below things to be considered as well.

  1. There should be an option to choose between fixed or dynamic sized tree

You mean abandoning the virtual list? Since right now react-aspen uses a virtual list (window list or something) that requires you to specify a width/height.

  1. The important tree item events (like add/update/delete/load/select) should be triggered, so those events can be listened to outside component

You mean echoing events that the watcher emits back up? Could be useful.

  1. Search Tree Item

This can be implemented without too much difficulty. Since Aspen 2 is going to be generic over the metadata you store (like name, path, etc.) this is impossible to it to implement directly.

  1. Refresh the tree items

This is a good one. It can already be implemented by doing reading and then telling Aspen to update stuff, but it would be good for convenience.

  1. Item path should be id based

What do you mean item path? Currently, as it's a list, items are indexed by their position in the list. Also, having a reference to an entry means you can access it forever.

zikaari commented 2 years ago

https://github.com/NeekSandhu/aspen/issues/19#issuecomment-974580487 - Is it possible to raise the handle state up though? For example, could it be possible to declaratively specify that you want a certain prompt to exist?

No. Aspen MUST have the authority to force terminate a prompt under certain circumstances, for example, when the node to which the prompt is bound is removed. In a scenario like that - code like this becomes contradicting -

<Tree ...>
  <RenamePrompt node={...} />
</Tree>

https://github.com/NeekSandhu/aspen/issues/19#issuecomment-974596605 - Type (File or Directory) is often implicit (depending on if children?.length > 0)

Aspen is async first library. The count of children is unknown upfront, and stays unknown until requested by calling Tree#expand(branch). Plus, a node could actually be a true directory but just not have any children, then a check like this will be unreliable.


https://github.com/NeekSandhu/aspen/issues/19#issuecomment-974596605 - I had to re-create a directory-like structure with paths just to translate this tree to a file system tree and also specifically turn off some of the path handling (like OS-style separators and sorting, those are things I don't want to think about.)

Paths are now gone, forever. It's just pure raw tree nodes, you get to choose what kind of data those nodes actually represent (through the data property on Leaf or Branch). Aspen 1 relied on path-fx library for it to work, it won't anymore.


https://github.com/NeekSandhu/aspen/issues/19#issuecomment-974596605 - Expanding/collapsing all or a list of items

It will be now accessible through a recursive: boolean argument to Tree.expand and Tree.collapse


https://github.com/NeekSandhu/aspen/issues/19#issuecomment-974596605 - I found myself writing wrapper React components that take selectedItems, visibleItems, onChangeSelection(selectedItems) props and translate it to treeHandle calls. Ended up being a strange mix between controlled and uncontrolled components. Ideally, react-aspen would offer a controlled component that works just like standard inputs or selects, i.e. taking props like mentioned above.

https://github.com/NeekSandhu/aspen/issues/19#issuecomment-974596605 and https://github.com/NeekSandhu/aspen/issues/19#issuecomment-975306767 - Search and item selection

The goal of Aspen 2 (when somebody does yarn add react-aspen) is to be super lightweight, minimal, and bloat-free. Not only in terms of bundle size, but also the API footprint. Features like this, and even more like keyboard navigation are best if they are made available through separate packages. This will allow users to opt-in as they deem fit. I welcome you all to contribute to the Aspen project and would love to see more packages under the packages/* directory. If not, you can publish it under your own account, and then have it featured in the readme of Aspen.

I can totally imagine a package like react-aspen-select, that exports hooks like useTreeSelect(tree: Tree). Or imagine react-aspen-search with useTreeSearch(tree: Tree, matchFn: ((node: TreeNode) => number))). And everything just works. Let me know if this sparks interest among one (or more) of you.


https://github.com/NeekSandhu/aspen/issues/19#issuecomment-975306767 - There should be an option to choose between fixed or dynamic sized tree

@LoganDark We can use the variable size list from react-window instead of FixedSizeList conditionally depending on the props supplied to the Tree component at runtime.


https://github.com/NeekSandhu/aspen/issues/19#issuecomment-975306767 - The important tree item events (like add/update/delete/load/select) should be triggered, so those events can be listened to outside component

Events for initial load, addition, and removal will be listenable on the Tree instance using a similar syntax that's already available with Aspen 1.


https://github.com/NeekSandhu/aspen/issues/19#issuecomment-975306767 - Refresh the tree items

Aspen 2 already has Tree#amend, which you can use to insert and re-sort the children of a branch. The reason Aspen never directly exposes a "hard-reload" method is that this would mean loss of expansion state of the child (and grandchildren). If this were to be exposed, what do you expect the call signature to be that also makes the implication clear that it'll cause a loss of expansion state for its children?

graup commented 2 years ago

I can totally imagine a package like react-aspen-select, that exports hooks like useTreeSelect(tree: Tree). Or imagine react-aspen-search with useTreeSearch(tree: Tree, matchFn: ((node: TreeNode) => number))). And everything just works. Let me know if this sparks interest among one (or more) of you.

Love it, totally up for contributing to both of those ideas.

LoganDark commented 2 years ago

I can totally imagine a package like react-aspen-select, that exports hooks like useTreeSelect(tree: Tree). Or imagine react-aspen-search with useTreeSearch(tree: Tree, matchFn: ((node: TreeNode) => number))). And everything just works. Let me know if this sparks interest among one (or more) of you.

I totally missed this when I first read your comment. This sounds really cool. I'd probably be able to contribute my select that supports click and ctrl+click and shift+click and ctrl+shift+click and...

zikaari commented 2 years ago

Awesome! While you guys are definitely encouraged to use your own creativity in the implementation, I think it's worth sharing a plausible approach to this -

React-ARIA by Adobe is a good example. For the sake of our use-case ignore the "aria" aspect of this package, and instead just look at how this package works in general.

Take for example the useSelect. This hook is agnostic of the UI, which has been the motto of Aspen from day zero. The user calls the hook, it returns some stuff, some of it contains the workable data, in our case the selectedItems, or searchResults, and some of it is props that need to be bound the some UI element, could be the entire item row, or something inside of it. And guess what, everything just works. Beautifully.

We can jump into this after the first beta release since the API and behaviour will be pretty much stable thereafter.

@LoganDark Will your implementation support "checkbox" driven selection/deselection as well?

@graup Would you like to collaborate with Logan on the selection package, or is there something else altogether that you'd like to kick-off (depending on what you deem important for Superb.ai).

Either way, it'll be nice to have an API proposal beforehand so we all can review and see if it can be one-size-fits-all.


I'll do an alpha release by next Monday (I'll post here the installation instructions). We'll talk discuss feature parity with Aspen 1, and in general, discuss any flaws before advancing on to the beta release soon after that.

LoganDark commented 2 years ago

@LoganDark Will your implementation support "checkbox" driven selection/deselection as well?

Unfortunately it does not at the moment. It's mostly implemented as a monstrous onClick handler. I can provide it but can't really help with extracting it into a separate package yet. I do also have a bunch of keybind handlers, mostly arrow keys and shift+arrow keys, for manipulating the selection.

zikaari commented 2 years ago

I can provide it but can't really help with extracting it into a separate package yet

No rush. Aspen itself hasn't reached beta yet. I'll ask you after the alpha release again to check if and when you can help by publishing it (just so everyone is in sync with status of things).


I have a feeling that checkbox selection might just be low-hanging fruit for us.

The following API might be exposed which people can then bind to checkbox event listeners -

const { addToSelection, removeFromSelection } = useTreeSelect(...)

Again, just a rough idea. Time will tell.

graup commented 2 years ago

How about generalizing the "selection" package? We can just make it a useCustomTreeState() and support all kinds of user defined states (aka decorations) like selected, disabled, ... plus addToState, removeFromState, setState.

Are you keeping the decorations concept? Actually it works fairly well, just the API is not so useful. We can just make a const { addToSelection, removeFromSelection, setSelection } = useDecoration('selection', CascadeMode). It can also handle more complicated logic (like de-selecting an item that was selected by cascade -> item should be negated).

I could draw up a proposal for this in a new issue.

zikaari commented 2 years ago

We can just make it a useCustomTreeState() and support all kinds of user defined states (aka decorations) like selected, disabled, ... plus addToState, removeFromState, setState.

The only problem I see with this is that the end-user will have to do the plumbing by themself defeating the original vision of plug-n-play lego bricks.

I could draw up a proposal for this in a new issue.

I have enabled GitHub Discussions for this repo. I'd love to see how well that space works, please use Discussions for this discussion by clicking the Discussion tab above.

zikaari commented 2 years ago

⚠Important announcement, please read - https://github.com/zikaari/aspen/issues/23