Zach Goethel

This Site

My portfolio site is written in my own reactive declarative markup language, serving as a testbed for new source generation features.

Some demos:

Count: 0
state {
	int count
}

interface {
	Increment(),
	Decrement()
}

div(| class = {"d-flex flex-row"} |
	div(| class = {"my-auto"} |
		{"Count: " + count}
	)
	button(|
		class = {"btn btn-danger ms-2"},
		onclick = {"dispatch(this, 'Decrement');"}
		|
		<">-</">
	)
	button(|
		class = {"btn btn-success ms-2"},
		onclick = {"dispatch(this, 'Increment');"}
		|
		<">+</">
	)
)
namespace TestApp.Views;

using Generated;

public class UpDownDemo : UpDownDemoBase
{
    public UpDownDemo(IServiceProvider sp) : base(sp)
    {
    }

    public override void Decrement()
    {
        count--;
    }

    public override void Increment()
    {
        count++;
    }
}

Serverside code is generated to render HTML for the component. Controller endpoints are also generated corresponding to any actions within the component interface.

Some minimal JS swaps out DOM elements to reactively update the page to reflect its latest state.

ValueActions
Row 1
Row 2
state {
	List<string> rows = {new() { "Row 1", "Row 2" }}
}

interface {
	Add(string row),
	Remove(int index),
	MoveUp(int index),
	MoveDown(int index)
}

table(| class = {"table table-striped table-bordered"} |
	colgroup(
		col(| style = {"width: 70%;"} |)
		col(| style = {"width: 30%;"} |)
	)
	thead(
		tr(
			th(<">Value</">)
			th(<">Actions</">)
		)
	)
	tbody(
		tr(
			td(
				input(|
					type = {"text"},
					id = {"table-add"},
					class = {"form-control"},
					value = {rows.Count >= 5 ? "Max 5 rows" : ""},
					disabled = {rows.Count >= 5}
					|)
			)
			td(
				button(|
					type = {"button"},
					class = {"btn btn-outline-primary"},
					onclick = {"dispatch(this, 'Add', { row: $('#table-add').val() });"},
					disabled = {rows.Count >= 5}
					|
					<">Add</">
				)
			)
		)
		foreach({var (row, i) in rows.Select((it, i) => (it, i))}

			tr(
				td({string.IsNullOrEmpty(row) ? "Blank!" : row})
				td(
					i(|
						class = {"fa fa-arrow-up me-2"},
						style = {"cursor: pointer;"},
						onclick = {"dispatch(this, 'MoveUp', { index: " + i + " });"}
						|)
					i(|
						class = {"fa fa-arrow-down me-2"},
						style = {"cursor: pointer;"},
						onclick = {"dispatch(this, 'MoveDown', { index: " + i + " });"}
						|)
					i(|
						class = {"fa fa-trash me-2"},
						style = {"cursor: pointer;"},
						onclick = {"dispatch(this, 'Remove', { index: " + i + " });"}
						|)
				)
			)
		)
	)
)
namespace TestApp.Views;

using Generated;

public class TableRowDemo : TableRowDemoBase
{
    public TableRowDemo(IServiceProvider sp) : base(sp)
    {
    }

    public override void Add(string row)
    {
        if (rows.Count < 5)
        {
            rows.Add(row);
        }
    }

    public override void MoveDown(int index)
    {
        if (index >= rows.Count - 1)
        {
            return;
        }
        var temp = rows[index];
        rows[index] = rows[index + 1];
        rows[index + 1] = temp;
    }

    public override void MoveUp(int index)
    {
        if (index <= 0)
        {
            return;
        }
        var temp = rows[index];
        rows[index] = rows[index - 1];
        rows[index - 1] = temp;
    }

    public override void Remove(int index)
    {
        rows.RemoveAt(index);
    }
}
Created by Zach Goethel. Have a wonderful day!