add htmx-alpine hybrid optimistic ui
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
{{ $data := .Data }} {{ $depth := .Depth }} {{ $mapa := $data | toMap }} {{
|
||||
$depth | indent }} {{ if $mapa }} {{ $nextDepth := add $depth 1 }}
|
||||
|
||||
<ul style="list-style: none">
|
||||
{{ range $key, $value := $mapa }}
|
||||
<li>
|
||||
{{ $nextDepth | indent }}
|
||||
<strong>{{ $key }}:</strong>
|
||||
{{ template "fragments/inspector" (dict "Data" $value "Depth" $nextDepth) }}
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
|
||||
{{ else }} {{ $data }} {{ end }}
|
||||
@@ -0,0 +1,43 @@
|
||||
<form
|
||||
id="create-todo-form"
|
||||
class="flex gap-2 items-start w-full"
|
||||
hx-post="/todo"
|
||||
hx-swap="afterbegin"
|
||||
hx-target="#todo-list-body"
|
||||
hx-target-error="this"
|
||||
x-data="{
|
||||
hasError: {{ if .Error }}true{{ else }}false{{ end }}
|
||||
}"
|
||||
@htmx:after-request="
|
||||
if($event.detail.successful && $event.detail.target.id === 'todo-list-body') {
|
||||
$el.reset();
|
||||
hasError = false;
|
||||
creating = false;
|
||||
}"
|
||||
>
|
||||
<div class="flex-grow form-control space-y-1.5">
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
class="input input-sm input-bordered w-full"
|
||||
:class="{ 'input-error': hasError }"
|
||||
value="{{ .Name }}"
|
||||
placeholder="Escribe tu tarea..."
|
||||
x-init="$el.focus()"
|
||||
@input="hasError = false"
|
||||
/>
|
||||
{{ if .Error }}
|
||||
<p class="text-error text-xs" x-show="hasError">{{ .Error }}</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-ghost"
|
||||
@click="creating = false; hasError = false; $el.form.reset();"
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
|
||||
<button type="submit" class="btn btn-sm btn-primary">Confirmar</button>
|
||||
</form>
|
||||
@@ -0,0 +1,72 @@
|
||||
<li
|
||||
id="todo-{{.ID}}"
|
||||
class="card bg-base-200 shadow-sm"
|
||||
x-data="{
|
||||
isEditing: false,
|
||||
form: {
|
||||
name: '{{ .Name }}'
|
||||
},
|
||||
cancelEdit() {
|
||||
this.isEditing = false;
|
||||
this.form.name = '{{ .Name }}';
|
||||
}
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="card-body p-4 flex-row items-center justify-between gap-4"
|
||||
x-show="!isEditing"
|
||||
>
|
||||
<div class="flex items-center gap-2 flex-grow">
|
||||
<span class="cursor-pointer"> {{ .Name }} </span>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
{{if not .Completed}}
|
||||
<button class="btn btn-sm btn-ghost" @click="isEditing = true">
|
||||
Editar
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-secondary"
|
||||
hx-patch="/todo/{{.ID}}/completed"
|
||||
hx-target="closest li"
|
||||
hx-swap="outerHTML"
|
||||
>
|
||||
Completar
|
||||
</button>
|
||||
{{else}}
|
||||
<span
|
||||
class="badge badge-success badge-sm text-white cursor-pointer"
|
||||
hx-patch="/todo/{{.ID}}/completed"
|
||||
hx-target="closest li"
|
||||
hx-swap="outerHTML"
|
||||
hx-trigger="dblclick"
|
||||
>Completado</span
|
||||
>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="card-body p-4 flex-row items-center justify-between gap-4"
|
||||
x-show="isEditing"
|
||||
style="display: none"
|
||||
>
|
||||
<form
|
||||
class="flex w-full gap-2"
|
||||
hx-put="/todo/{{.ID}}"
|
||||
hx-target="closest li"
|
||||
hx-swap="outerHTML"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
class="input input-sm input-bordered flex-grow"
|
||||
x-model="form.name"
|
||||
/>
|
||||
<button type="button" class="btn btn-sm btn-ghost" @click="cancelEdit()">
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="submit" class="btn btn-sm btn-primary">Guardar</button>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
@@ -0,0 +1,26 @@
|
||||
<div
|
||||
class="card bg-base-100 shadow-xl w-1/3"
|
||||
x-data="{
|
||||
creating: false,
|
||||
}"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="card-title text-2xl font-medium">Listado de tareas</h2>
|
||||
|
||||
<button type="button" class="btn btn-primary" @click="creating = true">
|
||||
Nueva tarea
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div x-show="creating" class="w-full pb-4">
|
||||
{{ template "fragments/todo-row-form" .}}
|
||||
</div>
|
||||
|
||||
<ol id="todo-list-body" class="space-y-3">
|
||||
{{ range .Todo }} {{ template "fragments/todo-row" . }} {{ end }}
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ template "fragments/inspector" (dict "Data" . "Depth" 0) }}
|
||||
@@ -0,0 +1,28 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Lista de tareas</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2/dist/htmx.min.js"></script>
|
||||
|
||||
<script
|
||||
defer
|
||||
src="https://cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js"
|
||||
></script>
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/daisyui@5"
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
/>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/daisyui@5/themes.css"
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
/>
|
||||
</head>
|
||||
<body data-theme="bumblebee">
|
||||
{{ embed }}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="p-4">
|
||||
<div hx-get="/todo" hx-trigger="load" hx-swap="outerHTML">Cargando...</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user