librasteve / raku-HTMX-Examples

htmx done in raku
Artistic License 2.0
4 stars 1 forks source link

Cro & HTMX Discussion #8

Open librasteve opened 1 month ago

librasteve commented 1 month ago

I have been looking at the cro documentation website and I really like the way that Cro uses markdown for those static pages...

The magic is done with the routes.pm6 file https://github.com/Raku/cro-website/blob/main/lib/Routes.pm6

use Cro::HTTP::Router;
use Cro::WebApp::Template;
use Text::Markdown;

sub routes() is export {
    my %lookup;
    my @files = 'docs'.IO;
    while @files {
        for @files.pop.dir {
            if .d {
                @files.push($_);
            } else {
                my $module = .basename.Str.subst('-', '::', :g).subst('.md', '');
                my $link = .Str.subst('docs/', '').subst('.md', '');
                %lookup{$module} = $link
            }
        }
    }

    template-location 'templates';

    route {
        get -> {
            template 'index.crotmp';
        }
        get -> 'training-support' {
            template 'support.crotmp';
        }
        get -> 'roadmap' {
            template 'roadmap.crotmp';
        }

        get -> 'css', *@path {
            static 'static-content/css', @path
        }
        get -> 'js', *@path {
            static 'static-content/js', @path
        }
        get -> 'images', *@path {
            static 'static-content/images', @path
        }
        get -> 'fonts', *@path {
            static 'static-content/fonts', @path
        }
        get -> 'favicon.ico' {
            static 'static-content/favicon.ico'
        }

        subset DocName of Str where /^<[A..Za..z0..9-]>+$/;

        get -> 'docs' {
            doc-markdown 'index.md'
        }
        get -> 'docs', DocName $page {
            doc-markdown "$page.md";
        }
        get -> 'docs', DocName $path, DocName $page {
            doc-markdown "$path/$page.md";
        }

        sub wrap-header($h, $anchor) {
            "<a name=\"$anchor\">" ~ '<h' ~ $h.level ~
                ' style="padding-top: 150px; margin-top: -150px;">' ~ $h.text ~
                "<a class=\"title-anchor\" href=\"#$anchor\">§</a>" ~
                '</h' ~ $h.level ~ '></a>';
        }

        sub link-code($item) {
            for $item.items.kv -> $idx, $val {
                when $val ~~ Text::Markdown::Code {
                    my $file = %lookup{$val.text.lc};
                    if $file {
                        $item.items[$idx] = "<a href=\"/docs/$file\">$val\</a>".subst('`', '', :g);
                    }
                }
            }
        }

        sub doc-markdown($path) {
            with slurp("docs/$path") -> $markdown {
                my $parsed = parse-markdown($markdown);
                my $title = 'documentation';
                my @index;
                for $parsed.document.items -> $item is rw {
                    if $item ~~ Text::Markdown::Paragraph {
                        link-code($item);
                    }
                    elsif $item ~~ Text::Markdown::List {
                        for $item.items -> $list-item {
                            for $list-item.items -> $val {
                                if $val ~~ Text::Markdown::Paragraph {
                                    link-code($val);
                                }
                            }
                        }
                    }
                    elsif $item ~~ Text::Markdown::Heading {
                        my $level = $item.level;
                        if $level > 1 {
                            my $anchor = $item.text.subst(' ', '_', :g);
                            if $level == 2 {
                                push @index, %( :$anchor, :text($item.text) );;
                            }
                            $item = wrap-header($item, $anchor);
                        }
                        else {
                            $title = $item.text;
                        }
                    }
                }
                my $body = $parsed.to_html;
                template 'docs.crotmp', %( :$title, :$body, :@index );
            }
            else {
                not-found;
            }
        }
    }
}
librasteve commented 1 month ago

^^ I put the whole file here since I think it has a lot for this project:

<tr id="replaceMe">
  <td colspan="3">
    <button class='btn primary' hx-get="/contacts/?page=2"
                        hx-target="#replaceMe"
                        hx-swap="outerHTML">
         Load More Agents... <img class="htmx-indicator" src="/img/bars.svg">
    </button>
  </td>
</tr>

I think this needs an idea for how to represent the hx-get / hx-post sequences in our htmx source ... my initial shot would be to have something like:

class hx-get {
    has $.route;      #  <=== use this to set up the routes.raku 

    method next { sends the next htmx content }
} 

This would allow arbitrary programmatic patterns for the back end to service hx-get, hx-post and so on

any f/back welcome

librasteve commented 1 month ago

Here's the joe blow example done in flash / blueprint:

viz. https://github.com/Konfuzian/htmx-examples-with-flask/blob/main/examples/blueprints/click_to_edit.py

from flask import Blueprint, render_template, request

bp = Blueprint("click_to_edit", __name__, url_prefix="/click_to_edit")

data = [
    {
        "firstName": "Joe",
        "lastName": "Blow",
        "email": "joe@blow.com",
    }
]

@bp.route("/", defaults={"id": 0})
@bp.route("/contact/<int:id>", methods=["GET", "PUT"])
def contact(id):
    if request.method == "PUT":
        data[id] = {k: request.form[k] for k in ("firstName", "lastName", "email")}

    return render_template("click_to_edit/index.html.j2", contact=data[id])

@bp.route("/contact/<int:id>/edit")
def contact_edit(id):
    return render_template("click_to_edit/edit.html.j2", contact=data[id])
librasteve commented 3 weeks ago

OK - based on some work on implemented HTMX examples, I think that the initial emphasis on functional HTML can and should be moved out to a new raku module ... that said, there is an existing raku module HTML::Lazy that already implements this functionality, so current plan is to freeze devt on the functional aspects here (in the branch freezefunc) and to attempt to improve HTML::Lazy to do that part

librasteve commented 3 weeks ago

my current plan is to leave the main branch as a vanilla crotmp oriented set of all the HTMX examples

where we have more exotic code styles like OO or Fn, then these can be placed on long-lived branches of their own