egil / Htmxor

Supercharges Blazor static server side rendering (SSR) by seamlessly integrating the Htmx.org frontend library.
MIT License
109 stars 12 forks source link

feat: adds missing reswap modifier and adds reswap builder along with StopPolling #23

Closed tanczosm closed 2 months ago

tanczosm commented 2 months ago

This commit introduces several enhancements to the HtmxResponse class. It adds a new optional parameter modifier to the Reswap method, allowing for more flexible response swapping. A new overload of the Reswap method is also added that takes a SwapStyleBuilder object as an argument to allow for fluent modifiers.

See Reswap fluent modifiers docs: https://jalexsocial.github.io/rizzy.docs/docs/htmx/response/#applying-fluent-modifiers

A new method, StopPolling, has been introduced which sets the response code to stop polling.

Two new classes, HtmxStatusCodes and SwapStyleBuilder, have been created. The former provides status codes specific to HTMX responses while the latter aids in constructing reswap commands.

Additionally, a new enum called ScrollDirection has been added for specifying scroll direction values for reswaps.

tanczosm commented 2 months ago

I added the Reswap overload.

Some comments on the builder. It's patterned after a builder that is widely used with the go programming language: https://pkg.go.dev/github.com/angelofallars/htmx-go#readme-swap-strategy

The difference is that they have an additional swapstyle option called "Default" that is used in cases where there is no swap style used in the reswap. Since SwapStyle is used in configurations and AjaxRequest you'd need to write a custom serializer to include "Default" as a SwapStyle option, even if semantically probably a correct option.

I updated the SwapStyleBuilder and added a suite of tests to validate the builder. I also rewrote it to override existing modifiers if accidentilly calling the same modifier. It's worth reconsidering because on the server side for reswaps you have to construct all the modifiers in the correct format by hand which is more error prone than using the builder.

You can always consider it experimental for the time being and toss if you end up not liking it as it doesn't interfere with using the other Reswap methods as it has a pretty self-contained footprint.

egil commented 2 months ago

The difference is that they have an additional swapstyle option called "Default" that is used in cases where there is no swap style used in the reswap. Since SwapStyle is used in configurations and AjaxRequest you'd need to write a custom serializer to include "Default" as a SwapStyle option, even if semantically probably a correct option.

Can you elaborate more on what this means (remember I am an htmx newbie).

Could we add a Default option to the SwapStyle enum to achieve this?

egil commented 2 months ago

Ps. do also rebase your branch on main. Did a major clean up this evening.

tanczosm commented 2 months ago

The difference is that they have an additional swapstyle option called "Default" that is used in cases where there is no swap style used in the reswap. Since SwapStyle is used in configurations and AjaxRequest you'd need to write a custom serializer to include "Default" as a SwapStyle option, even if semantically probably a correct option.

Can you elaborate more on what this means (remember I am an htmx newbie).

Could we add a Default option to the SwapStyle enum to achieve this?

Having a "Default" option in SwapStyle could have meaning in several contexts but in many of them it is simply a no-op of sorts:

In some ways it's like setting a value to true/false/unset.

What it does also do is allows you to also build fluent chains off of SwapStyle.Default as well, so SwapStyle.Default.ShowNone() builds a reswap as "show:none" only. Likewise, SwapStyle.Default.After(TimeSpan.FromSeconds(1)).ScrollTop().ShowTransition().IgnoreTitle() builds modifiers only with no SwapStyle override.

tanczosm commented 2 months ago

Here is the full set of builder methods:

tanczosm commented 2 months ago

So remaining issues:

egil commented 2 months ago

So remaining issues:

  • [x] Finish Documentation (ready for review)
  • [ ] Can we add a Default SwapStyle which means effectively whatever htmx defaults to?

Would it work if SwapStyle.Default implies what the user specifies in HtmxConfig.DefaultSwapStyle, or if that is unset, is the htmx default to. During rendering, the renderer and/or the builder can look at the config and replace an empty string, as the default is either specified in the config or is just what htmx would do anyway?

  • [ ] Any changes to method names?

I will go over them and report back.

tanczosm commented 2 months ago

I refactored the code a bit and just added a nullable selector for Scroll and ShowOn when selectors are used. I updated the list of methods.

tanczosm commented 2 months ago

Would it work if SwapStyle.Default implies what the user specifies in HtmxConfig.DefaultSwapStyle, or if that is unset, is the htmx default to. During rendering, the renderer and/or the builder can look at the config and replace an empty string, as the default is either specified in the config or is just what htmx would do anyway?

The ordering of what swap style is used looks like this (in order of precedence):

  1. Swap style specified in hx-swap
  2. Swap style specified in DefaultSwapStyle configuration
  3. Swap style specified by htmx as the default (innerHTML)

For reswaps it is:

  1. Swap style specified in reswap (if exists)
  2. Swap style specified in hx-swap
  3. Swap style specified in DefaultSwapStyle configuration
  4. Swap style specified by htmx as the default (innerHTML)

The impact of adding SwapStyle.Default as an option would mean that it is possible that a developer could set HtmxConfig.DefaultSwapStyle to SwapStyle.Default. When emitting the configuration code, however, the value for DefaultSwapStyle cannot be 'default' as that is an invalid value. So one approach might be to override the setter on the configuration default swap style and ignore attempts to set it to SwapStyle.Default.

egil commented 2 months ago

The impact of adding SwapStyle.Default as an option would mean that it is possible that a developer could set HtmxConfig.DefaultSwapStyle to SwapStyle.Default. When emitting the configuration code, however, the value for DefaultSwapStyle cannot be 'default' as that is an invalid value. So one approach might be to override the setter on the configuration default swap style and ignore attempts to set it to SwapStyle.Default.

That makes sense. Lets do that.

Tried to pull the PR down and make some changes but it looks like I do not have permissions to push to your branch.

egil commented 2 months ago

Getting a ! [remote rejected] mat-response-additions -> refs/pull/23/head (deny updating a hidden ref) when I try to push to the PR. Here is a patch file instead.

Htmxor-c7a345e-fix tweaks to code docs, added default swap style.patch

tanczosm commented 2 months ago

As far as the swap style builder is concerned, the build method returns the style and modifiers as a tuple. Would it be better for the builder to create strings so it could alternatively be used to create swaps for hx-get as well?

Your example was:

<div hx-swap=@SwapStyle.InnerHTML.ScrollTop() hx-get="..."></div>

I think for this to work I would have to end the chain with Build() or ToString().

<div hx-swap=@SwapStyle.InnerHTML.ScrollTop().Build() hx-get="..."></div>
<div hx-swap=@SwapStyle.InnerHTML.ScrollTop().ToString() hx-get="..."></div>
egil commented 2 months ago

btw. excellent work with the code docs. Really nice that users wont have to go to the htmx docs, they have all they need in the editor!

egil commented 2 months ago

The builder should implement a ToString() method that returns the output for the attribute. Then, if users are in debug mode and look at the builder, they will also see that value, which is a very nice bonus.

tanczosm commented 2 months ago

The impact of adding SwapStyle.Default as an option would mean that it is possible that a developer could set HtmxConfig.DefaultSwapStyle to SwapStyle.Default. When emitting the configuration code, however, the value for DefaultSwapStyle cannot be 'default' as that is an invalid value. So one approach might be to override the setter on the configuration default swap style and ignore attempts to set it to SwapStyle.Default.

That makes sense. Lets do that.

Tried to pull the PR down and make some changes but it looks like I do not have permissions to push to your branch.

I added write access.

egil commented 2 months ago

Think you need to do this to allow me to push: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork

tanczosm commented 2 months ago

Think you need to do this to allow me to push: docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork

Looking into this. I don't have an "Allow edits and access to secrets by maintainers" option.

egil commented 2 months ago

ok, ill just merge this, create a PR on my own, and you can provide your suggestions :)