Skip to content

Commit

Permalink
add response-targets and event-header
Browse files Browse the repository at this point in the history
  • Loading branch information
will-wow committed May 17, 2024
1 parent 470d6b8 commit 44b34da
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"go.lintTool": "golangci-lint",
"go.lintFlags": ["--fast"],
"cSpell.words": ["classtools", "gomponents"]
"cSpell.words": ["classtools", "eventheader", "templ", "gomponents"]
}
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ See [htmx/ext](./htmx/ext) for a full list of extensions.

- [`class-tools`](https://htmx.org/extensions/class-tools/)
- [`preload`](https://htmx.org/extensions/preload/)
- [`response-targets`](https://htmx.org/extensions/response-targets/)
- [`event-header`](https://htmx.org/extensions/event-header/)
- [`remove-me`](https://htmx.org/extensions/remove-me/)

## Examples
Expand Down
24 changes: 24 additions & 0 deletions htmx/ext/eventheader/eventheader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// package eventheader the Triggering-Event header to requests. The value of the header is a JSON serialized version of the event that triggered the request.
//
// # Install
//
// <script src="https://unpkg.com/[email protected]/dist/ext/event-header.js"></script>
//
// # Usage
//
// <button { hx.Ext(eventheader.Extension)... } >
// Click Me!
// </button>
//
// Sends something like this:
//
// Triggering-Event: '{ "isTrusted": false, "htmx-internal-data": { "handled": true }, "screenX": 0, "screenY": 0, "clientX": 0, "clientY": 0, "ctrlKey": false, "shiftKey": false, "altKey": false, "metaKey": false, "button": 0, "buttons": 0, "relatedTarget": null, "pageX": 0, "pageY": 0, "x": 0, "y": 0, "offsetX": 0, "offsetY": 0, "movementX": 0, "movementY": 0, "fromElement": null, "toElement": "button", "layerX": 0, "layerY": 0, "view": "Window", "detail": 0, "sourceCapabilities": null, "which": 1, "NONE": 0, "CAPTURING_PHASE": 1, "AT_TARGET": 2, "BUBBLING_PHASE": 3, "type": "click", "target": "button", "currentTarget": "button", "eventPhase": 2, "bubbles": true, "cancelable": true, "defaultPrevented": true, "composed": true, "timeStamp": 188.86999995447695, "srcElement": "button", "returnValue": false, "cancelBubble": false, "path": [ "button", "div#work-area", "body", "html", "Node", "Window" ] }'
//
// Extension: [event-header]
//
// [event-header]: https://htmx.org/extensions/event-header/
package eventheader

import "github.com/will-wow/typed-htmx-go/htmx"

const Extension htmx.Extension = "event-header"
102 changes: 102 additions & 0 deletions htmx/ext/responsetargets/responsetargets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// package responsetargets allows you to specify different target elements to be swapped when different HTTP response codes are received.
//
// # Install
//
// <script src="https://unpkg.com/[email protected]/dist/ext/response-targets.js"></script>
//
// Extension: [response-targets]
//
// [response-targets]: https://htmx.org/extensions/response-targets/
package responsetargets

import (
"fmt"
"strconv"
"strings"

"github.com/will-wow/typed-htmx-go/htmx"
)

// Extension allows you to specify different target elements to be swapped when different HTTP response codes are received.
//
// # Install
//
// <script src="https://unpkg.com/[email protected]/dist/ext/response-targets.js"></script>
//
// Extension: [response-targets]
//
// [response-targets]: https://htmx.org/extensions/response-targets/
const Extension htmx.Extension = "response-targets"

// A Code is a complete or partial HTTP response code.
type Code interface {
code() string
}

// A Status is a complete HTTP response code. You can wrap the http.Status* constants with [Status].
type Status int

var _ Code = Status(0)

func (s Status) code() string {
return strconv.Itoa(int(s))
}

// an errorCode is the string "error", used to cover all 4xx and 5xx HTTP response codes.
type errorCode string

var _ Code = errorCode("")

// Error is a status code that covers all 4xx and 5xx HTTP response codes.
const Error errorCode = "error"

func (e errorCode) code() string {
return string(e)
}

// A wildcard is a partial HTTP response code with a wildcard component.
type wildcard []int

var _ Code = (wildcard)(nil)

// Wildcard creates a wildcard code with the given digits.
// For example, Wildcard(4, 1) results in hx-target-41*, and matches all 41x HTTP response codes.
func Wildcard(digits ...int) wildcard {
return digits
}

func (w wildcard) code() string {
builder := strings.Builder{}
for _, digit := range w {
_, _ = builder.WriteString(strconv.Itoa(digit))
}
_ = builder.WriteByte('*')
return builder.String()
}

type wildcardX []int

// WildcardX creates a wildcard code with the given digits, and uses an 'x' instead of a '*' in the generated attribute.
// For example, WildcardX(4, 1) results in hx-target-41x, and matches all 41x HTTP response codes.
func WildcardX(digits ...int) wildcardX {
return digits
}

func (w wildcardX) code() string {
builder := strings.Builder{}
for _, digit := range w {
_, _ = builder.WriteString(strconv.Itoa(digit))
}
_ = builder.WriteByte('x')
return builder.String()
}

// Target specifies a target element to be swapped when specific HTTP response codes are received.
//
// Extension: [response-targets]
//
// [response-targets]: https://htmx.org/extensions/response-targets/
func Target[T any](hx htmx.HX[T], code Code, extendedSelector htmx.TargetSelector) T {
attr := fmt.Sprintf("hx-target-%s", code.code())
return hx.Attr(htmx.Attribute(attr), string(extendedSelector))
}
35 changes: 35 additions & 0 deletions htmx/ext/responsetargets/responsetargets_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package responsetargets_test

import (
"fmt"
"net/http"

"github.com/will-wow/typed-htmx-go/htmx"
"github.com/will-wow/typed-htmx-go/htmx/ext/responsetargets"
)

var hx = htmx.NewStringAttrs()

func ExampleTarget_code() {
attr := responsetargets.Target(hx, responsetargets.Code(http.StatusNotFound), htmx.TargetRelative(htmx.Next, "div"))
fmt.Println(attr)
// Output: hx-target-404='next div'
}

func ExampleTarget_error() {
attr := responsetargets.Target(hx, responsetargets.Error, htmx.TargetThis)
fmt.Println(attr)
// Output: hx-target-error='this'
}

func ExampleTarget_wildcard() {
attr := responsetargets.Target(hx, responsetargets.Wildcard(4, 0), htmx.TargetRelative(htmx.Next, "div"))
fmt.Println(attr)
// Output: hx-target-40*='next div'
}

func ExampleTarget_wildcardX() {
attr := responsetargets.Target(hx, responsetargets.WildcardX(4, 0), htmx.TargetRelative(htmx.Next, "div"))
fmt.Println(attr)
// Output: hx-target-40x='next div'
}
2 changes: 2 additions & 0 deletions htmx/htmx.go
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,8 @@ func (hx *HX[T]) Encoding(encoding EncodingContentType) T {
return hx.attr(Encoding, string(encoding))
}

// An Extension is the name of an htmx extension, to be passed to [HX.Ext()] to initialize the extension.
// Extensions are in packages under htmx/ext.
type Extension string

// Ext enables an htmx [extension] for an element and all its children.
Expand Down

0 comments on commit 44b34da

Please sign in to comment.