Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cro & HTMX Discussion #8

Open
librasteve opened this issue Aug 2, 2024 · 4 comments
Open

Cro & HTMX Discussion #8

librasteve opened this issue Aug 2, 2024 · 4 comments

Comments

@librasteve
Copy link
Owner

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
Copy link
Owner Author

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

  • we can put the html/htmx generator in a Cro routes file
  • this can be consumed as a sample project (ie fork the repo and build / run a Cro app)
  • we can generate / map routes to the htmx source in the content tree eg. on hx-get...
<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
Copy link
Owner Author

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": "[email protected]",
    }
]


@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
Copy link
Owner Author

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
Copy link
Owner Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant