Skip to content

Commit

Permalink
Merge pull request #5 from syarul/rc
Browse files Browse the repository at this point in the history
cleanup
  • Loading branch information
syarul authored Jan 7, 2024
2 parents 87806bd + 58fc647 commit b26b163
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 33 deletions.
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
Build with GO, TEMPL, HTMX & _HYPERSCRIPT
[![Go](https://github.com/syarul/todomvc-go-templ-htmx-_hyperscript/actions/workflows/go.yml/badge.svg)](https://github.com/syarul/todomvc-go-templ-htmx-_hyperscript/actions/workflows/go.yml)

### Testing
As evidence of HTMX's capabilities in emulating the functionalities of modern frameworks, I have incorporated [unit test](https://github.com/syarul/todomvc-go-templ-htmx-_hyperscript/actions/runs/7412273948/job/20168687544) from https://github.com/cypress-io/cypress-example-todomvc. This demonstration serves to showcase that HTMX, when paired with _hyperscript, can replicate all the behaviors typically associated with most modern client frameworks.
### E2E Testing
As evidence of HTMX's capabilities in emulating the functionalities of modern frameworks, I have incorporated [cypress test](https://github.com/syarul/todomvc-go-templ-htmx-_hyperscript/actions/runs/7412273948/job/20168687544) from https://github.com/cypress-io/cypress-example-todomvc. This demonstration serves to showcase that HTMX, when paired with _hyperscript, can replicate if not all the behaviors typically associated with most modern client framework with minimum needs to write javascript.

### Security
Check on [this link](https://templ.guide/security/) when using `templ` as HTML template engine. At anytime as developer `Do not blame the farmer if you cook the rice til it burns.`
Check on [this link](https://templ.guide/security/) when using `templ` as HTML template engine.

> `Do not blame the farmer if you cook the rice til it burns.`
### Usage
- install go if you don't have
Expand All @@ -24,10 +26,16 @@ Check on [this link](https://templ.guide/security/) when using `templ` as HTML t
- finally run `go run .`
- visit `http://localhost:8888`
- alternatively you can compile into executable with `go build .`
- if you need to run the e2e testing make sure to have `nodejs` installed
- run in the root folder `git clone https://github.com/cypress-io/cypress-example-todomvc`
- `cd cypress-example-todomvc`
- `npm install`
- if you need to see the test in browser run `npm run cypress:open`
- for headless test `npm run cypress:run`

### HTMX
Visit [https://github.com/rajasegar/awesome-htmx](https://github.com/rajasegar/awesome-htmx) to look for HTMX curated infos

###
Todo
- Use behavior to modular the _hyperscript scripts
- Perf test (consolidate with other langs rust, zig, odin, ocaml, etc+)
2 changes: 0 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ func main() {
t := &todos{}

// Register the routes.
// http.Handle("/get-hash", http.HandlerFunc(t.getHash))
http.Handle("/set-hash", http.HandlerFunc(setHash))
http.Handle("/learn.json", http.HandlerFunc(learnHandler))

Expand Down Expand Up @@ -398,7 +397,6 @@ func (t *todos) updateTodo(w http.ResponseWriter, r *http.Request) {
return
}
templRenderer(w, r, todoItem(todo, selectedFilter(filters)))
// }
}

func (t *todos) removeTodo(w http.ResponseWriter, r *http.Request) {
Expand Down
54 changes: 28 additions & 26 deletions todomvc.templ
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ templ filter(filters []Filter) {
for _, filter := range filters {
<li>
<a class={ templ.KV("selected", filter.selected) } href={ templ.SafeURL(filter.url) }
hx-boost="true"
_="on click add .selected to me"
>
{ filter.name }
Expand Down Expand Up @@ -51,7 +50,8 @@ templ editTodo(todo Todo) {
htmx.ajax('GET', `/update-todo?id=${@todo-id}&title=${my.value}`, {target: closest <li/>, swap:'outerHTML'})
end
send toggleMain to <section.todoapp/>
send toggleFooter to <section.todoapp/>"
send toggleFooter to <section.todoapp/>
"
/>
}

Expand All @@ -72,7 +72,8 @@ templ todoCheck(todo Todo) {
if $toggleAll.checked and my.checked === false
my.click()
else if $toggleAll.checked === false and my.checked
my.click()"
my.click()
"
/>
}

Expand All @@ -81,8 +82,7 @@ templ todoItem(todo Todo, filterName string) {
<li
id={ fmt.Sprintf(`todo-%s`, strconv.FormatUint(todo.Id, 10)) }
class={ "todo", templ.KV("completed", todo.Done), templ.KV("editing", todo.editing) }
_="
on destroy my.querySelector('button').click()"
_="on destroy my.querySelector('button').click()"
>
<div class="view">
@todoCheck(todo)
Expand All @@ -96,7 +96,8 @@ templ todoItem(todo Todo, filterName string) {
add .editing to the closest <li/>
on htmx:afterRequest
set $el to my.parentNode.nextSibling
set $el.selectionStart to $el.value.length"
set $el.selectionStart to $el.value.length
"
>
{ todo.title }
</label><button
Expand All @@ -110,7 +111,8 @@ templ todoItem(todo Todo, filterName string) {
send toggleMain to <section.todoapp/>
send toggleFooter to <section.todoapp/>
send focus to <input.new-todo/>
send toggleClearCompleted to <footer.footer/>"
send toggleClearCompleted to <footer.footer/>
"
/>
</div>
@editTodo(todo)
Expand All @@ -130,7 +132,7 @@ templ toggleMain(todos []Todo, checked bool) {
</section>
}
}

// indescriminatelyx
templ footer(todos []Todo, filters []Filter, hasCompleted bool) {
if len(todos) != 0 {
<footer class="footer"
Expand All @@ -140,6 +142,8 @@ templ footer(todos []Todo, filters []Filter, hasCompleted bool) {
if $clearCompleted === undefined
htmx.ajax('GET', '/completed', {target:'.filters', swap:'afterend'})
else
// need to first set to undefined in case the fetch may return empty which
// will indiscriminately leave it in incorrect state
set $clearCompleted to undefined
htmx.ajax('GET', '/completed', {target:'.clear-completed', swap:'outerHTML'})
send toggleFooter to <section.todoapp/>
Expand All @@ -151,7 +155,8 @@ templ footer(todos []Todo, filters []Filter, hasCompleted bool) {
_="
on load send todoCount to me
on todoCount debounced at 100ms
fetch /update-counts then put the result into me"
fetch /update-counts then put the result into me
"
/>
@filter(filters)
@tpl.ClearCompleted(hasCompleted)
Expand All @@ -163,8 +168,7 @@ templ todoList(todos []Todo, selectedFilter string) {
if len(todos) != 0 {
<ul
class="todo-list"
_="
on load set $todo to me"
_="on load set $todo to me"
>
for _, todo := range todos {
@todoItem(todo, selectedFilter)
Expand All @@ -186,24 +190,22 @@ templ Page(todos []Todo, filters []Filter, checked bool, hasCompleted bool, sele
class="todoapp"
_="
on toggleMain debounced at 1ms
// log 'do main'
if $sectionMain
set $sectionMain to undefined
htmx.ajax('GET', '/toggle-main', {target:'section.main', swap:'outerHTML'})
else
htmx.ajax('GET', '/toggle-main', {target:'.todo-list', swap:'beforebegin'})
end
on toggleFooter debounced at 1ms
// log 'do footer'
if $footerFooter
fetch /todo-json then
JSON.parse(it)
fetch /todo-json as json then
if $todo.hasChildNodes() === false and it.length === 0
remove $footerFooter
set $footerFooter to undefined
end
// set hash already update the hash on the server
// this only do representation of the filter class which one need selected
// set-hash already update the hash on the server
// this reassign the filter class selected base on user interaction
// or location hash changes
for filter in $filter.children
if filter.textContent === 'All' and `${$initial}${$after}` === ''
add .selected to filter.firstChild
Expand All @@ -217,8 +219,9 @@ templ Page(todos []Todo, filters []Filter, checked bool, hasCompleted bool, sele
htmx.ajax('GET', '/footer', {target:'.header', swap:'beforeend'})
end
on show wait 20ms
fetch /todo-json then
JSON.parse(it)
// this is the DOM tree diffing of the todo-list, fetch only the needed
// to render and remove accordingly base on route All/Active/Completed
fetch /todo-json as json then
if window.location.hash === '#/active'
for todo in it
if todo.done
Expand Down Expand Up @@ -246,15 +249,15 @@ templ Page(todos []Todo, filters []Filter, checked bool, hasCompleted bool, sele
for todo in it
// check if the element exist in the current DOM, add if none
// placement is decided according to order if there's an element
// with higher than the current todo do 'beforebegin'
// with higher than the current todo swap as 'beforebegin'
for el in $todo.children
if parseInt(el.id.slice(5)) > todo.id and document.getElementById(`todo-${todo.id}`) === null
htmx.ajax('GET', `/todo-item?id=${todo.id}`, {target: `#${el.id}`, swap:'beforebegin'})
end
end
// do reverse lookup for lower than the current todo do 'afterend'
// do reverse lookup for lower than the current todo swap as 'afterend'
for el in Array.from($todo.children).reverse()
if parseInt(el.id.slice(5)) < todo.id and document.getElementById(`todo-${todo.id}`) === null
htmx.ajax('GET', `/todo-item?id=${todo.id}`, {target: `#${el.id}`, swap:'afterend'})
Expand Down Expand Up @@ -292,7 +295,8 @@ templ Page(todos []Todo, filters []Filter, checked bool, hasCompleted bool, sele
set my value to ''
end
send toggleMain to <section.todoapp/>
send toggleFooter to <section.todoapp/>"
send toggleFooter to <section.todoapp/>
"
/>
</header>
@toggleMain(todos, checked)
Expand All @@ -301,11 +305,10 @@ templ Page(todos []Todo, filters []Filter, checked bool, hasCompleted bool, sele
</section>
<footer class="info"
_="
on beforeunload log me
on load debounced at 10ms
call startMeUp()
hashCache()
"
"
>
<p>Double-click to edit a todo</p>
<p>Created by <a href="http://github.com/syarul/">syarul</a></p>
Expand Down Expand Up @@ -337,15 +340,14 @@ templ Page(todos []Todo, filters []Filter, checked bool, hasCompleted bool, sele
by http://github.com/syarul/"
end
def hashCache()
// log 'called hashCache!'
// this is done to get current location hash then update todo-list and footer
set $initial to window.location.hash.slice(2).charAt(0).toUpperCase()
set $after to window.location.hash.slice(3)
fetch `/set-hash?name=${$initial}${$after}` then
send show to <section.todoapp/>
send toggleFooter to <section.todoapp/>
end
// this to handle popstate event such as back/foward button
// this to handle popstate event such as back/forward button
// where it will automatically calling hashCache _hyperscript function
js
window.addEventListener('popstate', function(){
Expand Down
3 changes: 2 additions & 1 deletion tpl/clear_completed.templ
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ templ ClearCompleted (hasCompleted bool) {
<button class="clear-completed"
_="
on load set $clearCompleted to me
on click send destroy to <li.completed/>"
on click send destroy to <li.completed/>
"
>Clear completed</button>
}
}

0 comments on commit b26b163

Please sign in to comment.