-
Notifications
You must be signed in to change notification settings - Fork 3
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
Better add blocks #413
Better add blocks #413
Conversation
@nbenn This is good for review. Other than that, the decision to drop the JavaScript from PR #325 is to have all the logic into one place for easier customisation, like we already discussed. If for instance, people are note happy with |
@JohnCoene, @karmatarap and @nbenn preview of how it works: How it looks for a single stack with 2 block packages loaded Screen.Recording.2024-08-27.at.08.48.14.movFor a workspace (display of the result block) Screen.Recording.2024-08-27.at.09.59.52.mov |
Is there any way to make the |
@JohnCoene Added a default "uncategorized" for |
On an empty stack I see Where I cannot add the |
Either we have to explicitly pass Can you try: blockr::register_block(
new_codelist_block,
"Dummy data",
"Create dummy data",
input = NA_character_,
output = "list",
category = "data",
package = pkgname,
classes = c("codelist_block", "data_block")
) |
But how does the logic for what block can be added work? The block is valid since the input is |
@DivadNojnarg Thank you, seems to work fine now. I will test some more. One thought is whether we need the "+" button, it feels like a needless click, shouldn't we just have to select the block we want (rather than select block then click "+")? You may also want to test it with a lot of blocks to see performances (should be OK) and see how they display, it needs |
Thanks for the feedback. I've been experimenting a minimalistic approach:
Screen.Recording.2024-09-03.at.15.47.12.movAdvantages:
Drawbacks:
What do you think @JohnCoene, @nbenn and @karmatarap ? |
@nbenn Good for final review ;) |
I honestly prefer how it was before the search was added. We don't use any of the space we have to display blocks: the select input is a small box and scrolling through is rather unwieldy. Is there a way to make that fit the width and height of the offcanvas? |
@JohnCoene. We can add custom renderers for the search items, to make them a bit nicer to look. This can also be useful to render thumbnails later, or any other registry information. Even extra tooltips and popovers. |
Should we automatically close the offcanvas after having selected a block? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lgtm. Some minor comments added.
Not for this PR, but something to start thinking about: How can we make it possible to add arbitrary further attributes to the registry? High level thoughts:
register_block()
,new_block_descr()
, ... would take...
register_blockr_blocks()
would run conditionally (on someblockr.auto_register_blocks
option- the wrapping package can then provide its own
.onLoad()
hook to pass the desired attributes toregister_block()
et al.
What am I missing? @DivadNojnarg is there anything you might want to do differently here in anticipation of such an extension?
R/registry.R
Outdated
constructor, | ||
name, | ||
description, | ||
category = "uncategorized", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason for not moving "category" further down the arg list (given that we provide a default)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was more a semantic reason, I thought category might be more important than the pkg name but than can move at the end.
R/registry.R
Outdated
constructor, | ||
name, | ||
description, | ||
category = "uncategorized", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as above.
attrs <- attributes(blk) | ||
data.frame( | ||
name = attrs[["name"]], | ||
ctor = names(available_blocks())[[i]], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need to expose a name for the constructor here? The names here are just the names used for constructor bindings in the block_registry
environment. If a result_block
is called result_block
in this environment, this is to be treated more as a coincidence than a "rule".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's an artifact I needed for the accotdions and pills but we don't need anymore with the new virtual select. To cleanup.
tmp <- registry[is.na(registry$input), ] | ||
# Drop result block if there are no other stacks | ||
if (length(get_workspace_stacks()) <= 1) { | ||
tmp <- tmp[tmp$ctor != "result_block", ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not look super robust to me: the name for this binding (within the block_registry
env) is not part of any API really.
Also, we're hard-coding here some implicit assumption that has to be satisfied for some block class. Maybe this is beyond the scope for this PR, but what if a 3rd-party block implementation also would want such kind of special treatment? What kind of API could we offer to enable this? And if that sounds too complicated, is it worthwhile to do something (hacky) like this for the result block only?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need a separate discussion on the registry. That goes beyond this PR.
R/stack.R
Outdated
# Otherwise we compare the output of the last block | ||
# and propose any of the block that have compatible input | ||
ctor <- class(stack[[length(stack)]])[1] | ||
last_blk_output <- registry[registry$ctor == ctor, "output"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, many implicit assumptions here: it is not guaranteed in any way that the (first entry) of a block class attribute is identical to the binding name of the block constructor in this lookup environment.
Why not compare the (full) class attribute to the classes
registry "attribute"? That should be way more stable, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would work. However, I find not optimal that we have to define the block classes within the constructor:
new_block(
fields = fields,
expr = quote(.(stack)),
...,
class = c("result_block", "data_block")
)
and that we have to do the same for the registry definition. But, again, we need to discuss about the registry separately.
I depends if you want to be able to add multiple blocks quickly by chaining the selection, which is possible right now. In that case, closing the offcanvas each time would be annoying. |
@nbenn Thanks for the comments. I fixed some issues, and also made the add_block stuff generic in case we want to support different options. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lgtm.
This is similar to #325 but with 0 JavaScript (for easier customisation):
category
field to the registry: allows to sort blocks on the UI side. Default touncategorized
to avoid breaking change.get_registry
: renders as a dataframe for easier manipulation thanavailable_blocks()
.add_block_server
is now a real module: it usesget_compatible_blocks
that return a list of block compatible with the last stack block. This helper func can also be used without shiny. Block choices are rendered server side as a consequence of runningget_compatible_blocks
that listens to any block change and update the list of available blocks. The search/select input leverages shinyWidgets's virtual select.add_block_ui
is now a real UI module function.remove_stack_server
(code cleanup).